@slates/provider 1.0.0-rc.27 → 1.0.0-rc.28

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
@@ -86,6 +86,7 @@ __export(index_exports, {
86
86
  isApiErrorRecord: () => isApiErrorRecord,
87
87
  isAxiosErrorLike: () => isAxiosErrorLike,
88
88
  isServiceErrorData: () => isServiceErrorData,
89
+ isUrlEmbeddedCredential: () => isUrlEmbeddedCredential,
89
90
  mapServiceErrorCode: () => mapServiceErrorCode,
90
91
  mapServiceErrorToSlateErrorInput: () => mapServiceErrorToSlateErrorInput,
91
92
  mergeScopes: () => mergeScopes,
@@ -1074,10 +1075,14 @@ var AuthConfigSecretRedactor = class {
1074
1075
  return this.secretToPlaceholder.get(stringValue) ?? stringValue;
1075
1076
  });
1076
1077
  }
1077
- redactEmbedded(value) {
1078
- if (this.secretToPlaceholder.size === 0) return value;
1078
+ /** `embeddable` limits which secrets are also replaced inside longer strings. */
1079
+ redactEmbedded(value, embeddable = () => true) {
1080
+ let secrets = [...this.secretToPlaceholder.entries()].filter(
1081
+ ([secret, placeholder]) => embeddable(secret, placeholder.slice(AUTH_CONFIG_SECRET_PLACEHOLDER_PREFIX.length))
1082
+ ).map(([secret]) => secret);
1083
+ if (secrets.length === 0) return this.redact(value);
1079
1084
  let pattern = new RegExp(
1080
- [...this.secretToPlaceholder.keys()].sort((a, b) => b.length - a.length).map((secret) => secret.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"),
1085
+ secrets.sort((a, b) => b.length - a.length).map((secret) => secret.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"),
1081
1086
  "g"
1082
1087
  );
1083
1088
  return this.transform(value, (stringValue) => {
@@ -1130,18 +1135,21 @@ var AuthConfigSecretRedactor = class {
1130
1135
  return typeof value === "object" && value !== null && !Array.isArray(value);
1131
1136
  }
1132
1137
  };
1138
+ var URL_CREDENTIAL_KEY = /^(?:.*[-_])?(?:token|secret|password|api[-_]?key)$/i;
1139
+ var isUrlEmbeddedCredential = (secret, path) => secret.length >= 8 && URL_CREDENTIAL_KEY.test(path.split(".").pop() ?? "");
1133
1140
  var redactUrlAttachmentSecrets = (attachments, authConfig) => {
1134
1141
  if (!authConfig || !attachments) return attachments;
1135
1142
  let redactor = new AuthConfigSecretRedactor(authConfig);
1136
1143
  return attachments.map((attachment) => {
1137
1144
  if (attachment.content.type !== "url") return attachment;
1138
- if (!attachment.content.headers && !attachment.content.query) return attachment;
1145
+ let { url, headers, query } = attachment.content;
1139
1146
  return {
1140
1147
  ...attachment,
1141
1148
  content: {
1142
1149
  ...attachment.content,
1143
- headers: attachment.content.headers ? redactor.redactEmbedded(attachment.content.headers) : void 0,
1144
- query: attachment.content.query ? redactor.redactEmbedded(attachment.content.query) : void 0
1150
+ url: redactor.redactEmbedded(url, isUrlEmbeddedCredential),
1151
+ ...headers ? { headers: redactor.redactEmbedded(headers) } : {},
1152
+ ...query ? { query: redactor.redactEmbedded(query) } : {}
1145
1153
  }
1146
1154
  };
1147
1155
  });
@@ -1191,6 +1199,7 @@ var getFileUrlTool = (spec2, handleGetFileUrl) => tool(spec2, {
1191
1199
  return {
1192
1200
  output: {
1193
1201
  ...output,
1202
+ url: redactor.redactEmbedded(output.url, isUrlEmbeddedCredential),
1194
1203
  ...output.headers ? { headers: redactor.redactEmbedded(output.headers) } : {},
1195
1204
  ...output.query ? { query: redactor.redactEmbedded(output.query) } : {}
1196
1205
  },
@@ -1288,6 +1297,9 @@ var SlateTriggerGroup = class _SlateTriggerGroup {
1288
1297
  get webhook() {
1289
1298
  return this._params.webhook;
1290
1299
  }
1300
+ get gateway() {
1301
+ return this._params.gateway;
1302
+ }
1291
1303
  get routingMatchers() {
1292
1304
  return this._params.routingMatchers;
1293
1305
  }
@@ -1304,6 +1316,7 @@ var SlateTriggerGroupBuilder = class {
1304
1316
  #source = null;
1305
1317
  #polling = null;
1306
1318
  #webhook = null;
1319
+ #gateway = null;
1307
1320
  #routingMatchers = null;
1308
1321
  polling(props) {
1309
1322
  if (this.#source) {
@@ -1333,6 +1346,15 @@ var SlateTriggerGroupBuilder = class {
1333
1346
  };
1334
1347
  return this;
1335
1348
  }
1349
+ /** Receive events over a platform-managed persistent connection (e.g. a WebSocket gateway). */
1350
+ gateway(props) {
1351
+ if (this.#source) {
1352
+ throw new SlateDeclarationError("Trigger group invocation is already defined");
1353
+ }
1354
+ this.#source = "gateway";
1355
+ this.#gateway = { connect: props.connect, receive: props.receive };
1356
+ return this;
1357
+ }
1336
1358
  routingMatchers(handler) {
1337
1359
  this.#routingMatchers = handler;
1338
1360
  return this;
@@ -1340,7 +1362,7 @@ var SlateTriggerGroupBuilder = class {
1340
1362
  build() {
1341
1363
  if (!this.#source) {
1342
1364
  throw new SlateDeclarationError(
1343
- "Trigger group invocation (polling or webhook) is not defined"
1365
+ "Trigger group invocation (polling, webhook, or gateway) is not defined"
1344
1366
  );
1345
1367
  }
1346
1368
  if (!this.#routingMatchers) {
@@ -1351,6 +1373,7 @@ var SlateTriggerGroupBuilder = class {
1351
1373
  source: this.#source,
1352
1374
  polling: this.#polling ?? void 0,
1353
1375
  webhook: this.#webhook ?? void 0,
1376
+ gateway: this.#gateway ?? void 0,
1354
1377
  routingMatchers: this.#routingMatchers
1355
1378
  });
1356
1379
  }
@@ -2140,6 +2163,7 @@ var redactHashFragment = (hash, redactor) => {
2140
2163
  );
2141
2164
  return redacted ? `#${redacted}` : "";
2142
2165
  };
2166
+ var isEmbeddedPathSecret = (secret, path) => secret.length >= 8 && isSecretKeyName(path.split(".").pop() ?? "");
2143
2167
  var sanitizeUrl = (value, redactor) => {
2144
2168
  if (!value) return value;
2145
2169
  try {
@@ -2148,6 +2172,7 @@ var sanitizeUrl = (value, redactor) => {
2148
2172
  url.username = "";
2149
2173
  url.password = "";
2150
2174
  }
2175
+ if (redactor) url.pathname = redactor.redactEmbedded(url.pathname, isEmbeddedPathSecret);
2151
2176
  for (let key of Array.from(url.searchParams.keys())) {
2152
2177
  if (isUrlFormSecretKeyName(key)) {
2153
2178
  url.searchParams.set(key, REDACTED_VALUE);
@@ -2160,7 +2185,7 @@ var sanitizeUrl = (value, redactor) => {
2160
2185
  url.hash = redactHashFragment(url.hash, redactor);
2161
2186
  return decodeRedactedMarker(url.toString());
2162
2187
  } catch {
2163
- let text = redactor ? redactor.redact(value) : value;
2188
+ let text = redactor ? redactor.redactEmbedded(value, isEmbeddedPathSecret) : value;
2164
2189
  return text.replace(
2165
2190
  /([?&]([^=&#]+)=)([^&#]+)/g,
2166
2191
  (_match, prefix, key, rawValue) => isUrlFormSecretKeyName(key) ? `${prefix}${REDACTED_VALUE}` : `${prefix}${rawValue}`
@@ -3012,6 +3037,7 @@ var skipWebhook = (reason, response) => ({
3012
3037
  isApiErrorRecord,
3013
3038
  isAxiosErrorLike,
3014
3039
  isServiceErrorData,
3040
+ isUrlEmbeddedCredential,
3015
3041
  mapServiceErrorCode,
3016
3042
  mapServiceErrorToSlateErrorInput,
3017
3043
  mergeScopes,
package/dist/index.d.cts CHANGED
@@ -50,15 +50,17 @@ declare class AuthConfigSecretRedactor {
50
50
  private readonly secretToPlaceholder;
51
51
  constructor(authConfig: Record<string, unknown> | undefined);
52
52
  redact<T>(value: T): T;
53
- redactEmbedded<T>(value: T): T;
53
+ /** `embeddable` limits which secrets are also replaced inside longer strings. */
54
+ redactEmbedded<T>(value: T, embeddable?: (secret: string, path: string) => boolean): T;
54
55
  private buildSecretMap;
55
56
  private transform;
56
57
  private isPlainObject;
57
58
  }
59
+ declare let isUrlEmbeddedCredential: (secret: string, path: string) => boolean;
58
60
  /**
59
61
  * Applied to every finalized attachment list right before it's sent over
60
62
  * `slates/action.tool.invoke` (see @slates/provider-handler). Only `type: 'url'` attachments
61
- * carry headers/query -- content/upload_reference attachments pass through untouched.
63
+ * carry credentials -- content/upload_reference attachments pass through untouched.
62
64
  */
63
65
  declare let redactUrlAttachmentSecrets: (attachments: SlateAttachment[] | undefined, authConfig: Record<string, unknown> | undefined) => SlateAttachment[] | undefined;
64
66
 
@@ -523,6 +525,41 @@ type SlateTriggerGroupPollHandler<ConfigType extends {}, AuthType extends {}> =
523
525
  }[];
524
526
  updatedState?: any;
525
527
  }>;
528
+ interface SlateGatewayEvent {
529
+ payload: any;
530
+ idempotencyKey?: string;
531
+ }
532
+ type SlateTriggerGroupGatewayConnectHandler<ConfigType extends {}, AuthType extends {}> = (context: SlateContext<ConfigType, AuthType, {
533
+ state: any | null;
534
+ }>) => Promise<{
535
+ url: string;
536
+ state?: any;
537
+ }>;
538
+ /** `closed` is set, with no frames, when the socket closed unexpectedly. */
539
+ type SlateTriggerGroupGatewayReceiveHandler<ConfigType extends {}, AuthType extends {}> = (context: SlateContext<ConfigType, AuthType, {
540
+ state: any | null;
541
+ frames: string[];
542
+ closed?: {
543
+ code: number;
544
+ reason: string;
545
+ } | null;
546
+ }>) => Promise<{
547
+ state?: any;
548
+ send?: string[];
549
+ events?: SlateGatewayEvent[];
550
+ /** With `expectsAck`, the hub reconnects when no `heartbeatAcked` arrives before the next beat. */
551
+ heartbeat?: {
552
+ intervalMs: number;
553
+ frame: string;
554
+ expectsAck?: boolean;
555
+ } | null;
556
+ /** Set when `frames` included the provider's heartbeat acknowledgement. */
557
+ heartbeatAcked?: boolean;
558
+ close?: {
559
+ reconnect: boolean;
560
+ reason?: string;
561
+ } | null;
562
+ }>;
526
563
  type SlateWebhookTargetListHandler<ConfigType extends {}, AuthType extends {}> = (context: SlateContext<ConfigType, AuthType, {
527
564
  pageToken: any | null;
528
565
  }>) => Promise<{
@@ -574,6 +611,10 @@ interface SlateTriggerGroupPollingParameters<ConfigType extends {}, AuthType ext
574
611
  intervalSeconds: number;
575
612
  pollEvents: SlateTriggerGroupPollHandler<ConfigType, AuthType>;
576
613
  }
614
+ interface SlateTriggerGroupGatewayParameters<ConfigType extends {}, AuthType extends {}> {
615
+ connect: SlateTriggerGroupGatewayConnectHandler<ConfigType, AuthType>;
616
+ receive: SlateTriggerGroupGatewayReceiveHandler<ConfigType, AuthType>;
617
+ }
577
618
  interface SlateTriggerGroupWebhookAutoRegistrationParameters<ConfigType extends {}, AuthType extends {}> {
578
619
  webhookTargetList: SlateWebhookTargetListHandler<ConfigType, AuthType>;
579
620
  webhookRegister: SlateWebhookRegisterHandler<ConfigType, AuthType>;
@@ -598,9 +639,10 @@ interface SlateTriggerGroupCreateParameters<InputType extends {} = Record<string
598
639
  eventSchema?: z$1.ZodType<InputType>;
599
640
  }
600
641
  interface SlateTriggerGroupParameters<ConfigType extends {}, AuthType extends {}, InputType extends {} = Record<string, unknown>> extends SlateTriggerGroupCreateParameters<InputType> {
601
- source: 'polling' | 'webhook';
642
+ source: 'polling' | 'webhook' | 'gateway';
602
643
  polling?: SlateTriggerGroupPollingParameters<ConfigType, AuthType>;
603
644
  webhook?: SlateTriggerGroupWebhookParameters<ConfigType, AuthType>;
645
+ gateway?: SlateTriggerGroupGatewayParameters<ConfigType, AuthType>;
604
646
  routingMatchers: SlateTriggerGroupRoutingMatchersHandler<ConfigType, AuthType>;
605
647
  }
606
648
  declare class SlateTriggerGroup<ConfigType extends {}, AuthType extends {}, InputType extends {} = Record<string, unknown>> {
@@ -614,9 +656,10 @@ declare class SlateTriggerGroup<ConfigType extends {}, AuthType extends {}, Inpu
614
656
  get description(): string | undefined;
615
657
  get metadata(): Record<string, any> | undefined;
616
658
  get eventSchema(): z$1.ZodType<InputType, unknown, z$1.core.$ZodTypeInternals<InputType, unknown>>;
617
- get source(): "polling" | "webhook";
659
+ get source(): "polling" | "webhook" | "gateway";
618
660
  get polling(): SlateTriggerGroupPollingParameters<ConfigType, AuthType> | undefined;
619
661
  get webhook(): SlateTriggerGroupWebhookParameters<ConfigType, AuthType> | undefined;
662
+ get gateway(): SlateTriggerGroupGatewayParameters<ConfigType, AuthType> | undefined;
620
663
  get routingMatchers(): SlateTriggerGroupRoutingMatchersHandler<ConfigType, AuthType>;
621
664
  }
622
665
  declare class SlateTriggerGroupBuilder<ConfigType extends {}, AuthType extends {}, InputType extends {} = Record<string, unknown>> {
@@ -634,6 +677,8 @@ declare class SlateTriggerGroupBuilder<ConfigType extends {}, AuthType extends {
634
677
  manualRegistration?: SlateTriggerGroupWebhookManualRegistrationParameters<UserConfigSchema, FullConfigSchema>;
635
678
  process: SlateWebhookProcessHandler;
636
679
  }): SlateTriggerGroupBuilder<ConfigType, AuthType, InputType>;
680
+ /** Receive events over a platform-managed persistent connection (e.g. a WebSocket gateway). */
681
+ gateway(props: SlateTriggerGroupGatewayParameters<ConfigType, AuthType>): SlateTriggerGroupBuilder<ConfigType, AuthType, InputType>;
637
682
  routingMatchers(handler: SlateTriggerGroupRoutingMatchersHandler<ConfigType, AuthType>): SlateTriggerGroupBuilder<ConfigType, AuthType, InputType>;
638
683
  build(): SlateTriggerGroup<ConfigType, AuthType, InputType>;
639
684
  }
@@ -1094,4 +1139,4 @@ declare let skipWebhook: (reason: string, response?: Response | SlateWebhookHttp
1094
1139
  response: Response | SlateWebhookHttpResponseInit;
1095
1140
  };
1096
1141
 
1097
- export { AUTH_CONFIG_SECRET_PLACEHOLDER_PREFIX, type ApiErrorBuildHelpers, type ApiErrorDetailOptions, type ApiErrorResponse, type ApiErrorStatus, type ApiServiceError, AuthConfigSecretRedactor, type BuildApiServiceErrorOptions, type CreateHmacSignatureOptions, GET_FILE_URL_TOOL_ID, type GetFileUrlHandler, type GetFileUrlToolInput, type GetFileUrlToolOutput, type NormalizeOAuthTokenResponseOptions, type OAuthExpiresAtOptions, type OAuthRefreshTokenFallbackMode, type OAuthTokenResponse, Slate, SlateAction, SlateActionBuilder, type SlateActionCreateParameters, type SlateActionDocsReference, type SlateActionParameters, type SlateActionParametersAny, type SlateActionParametersTool, type SlateActionParametersTrigger, type SlateActionScopeClause, type SlateActionScopeClauseInput, type SlateActionScopeValue, type SlateActionScopes, type SlateActionScopesInput, type SlateActionType, SlateAdapter, type SlateAdapterCapability, SlateAdapterSpec, type SlateAdapterSpecParameters, type SlateAddAttachmentContent, type SlateAddAttachmentInput, type SlateAttachment, type SlateAttachmentInlineContent, type SlateAttachmentUploadReferenceContent, type SlateAttachmentUrlContent, SlateAuth, type SlateAuthDocsReference, type SlateAuthType, type SlateAuthWithCustomData, type SlateAuthWithNone, type SlateAuthWithOauth, type SlateAuthWithServiceAccount, type SlateAuthWithToken, type SlateAuthenticatedAxiosDefaults, type SlateAxiosDefaults, type SlateAxiosErrorAdapter, type SlateAxiosErrorOptions, SlateConfig, type SlateConfigDocsReference, SlateContext, SlateDeclarationError, SlateDefaultPollingIntervalSeconds, type SlateDirectUploadInput, type SlateDocsReference, SlateError, type SlateErrorInput, type SlateErrorIssue, type SlateErrorKind, type SlateErrorProviderInfo, type SlateErrorResponse, type SlateErrorUpstreamInfo, type SlateLiveInvocationInfo, type SlateLogEntry, type SlateLogFields, type SlateLogListener, type SlateLogMessageInput, type SlateLogType, SlateLogger, type SlateProviderDocsReference, SlatePublicContext, SlatePublicTool, type SlatePublicToolInvocationHandler, type SlatePublicToolParameters, type SlateServiceErrorData, SlateSpecification, type SlateSpecificationCreateParameters, type SlateSpecificationParameters, SlateTool, type SlateToolInvocationHandler, type SlateToolParameters, SlateTrigger, SlateTriggerGroup, SlateTriggerGroupBuilder, type SlateTriggerGroupCreateParameters, type SlateTriggerGroupParameters, type SlateTriggerGroupPollHandler, type SlateTriggerGroupPollingParameters, type SlateTriggerGroupRoutingMatchersHandler, type SlateTriggerGroupWebhookAutoRegistrationParameters, type SlateTriggerGroupWebhookManualRegistrationParameters, type SlateTriggerGroupWebhookParameters, type SlateTriggerMappingHandler, type SlateTriggerMatchesHandler, type SlateTriggerParameters, type SlateTriggerRoutingMatcher, type SlateTypedDocsReference, type SlateWebhookHttpResponseInit, type SlateWebhookManualFinishHandler, type SlateWebhookManualSetupHandler, type SlateWebhookProcessHandler, type SlateWebhookRegisterHandler, type SlateWebhookSkipped, type SlateWebhookTarget, type SlateWebhookTargetListHandler, type SlateWebhookTargetOwnership, type SlateWebhookUnregisterHandler, type VerifyHmacSignatureOptions, adapterSpec, allOf, anyOf, auth, axios, buildApiServiceError, collectApiErrorDetails, config, createApiServiceError, createAuthenticatedAxios, createAxios, createBase64Attachment, createHmacSignature, createSlateError, createTextAttachment, createUrlAttachment, extractApiErrorMessage, getApiErrorResponse, getApiErrorStatus, getBase64ByteLength, getCurrentContext, getFileUrlTool, getFileUrlToolInputSchema, getFileUrlToolOutputSchema, getGraphWebhookValidationResponse, getMetaWebhookVerificationResponse, getOAuthExpiresAtFromExpiresIn, getResponseHeaderValue, getServiceErrorData, graphWebhookHttp, inferSlateErrorFromAxios, isApiErrorRecord, isAxiosErrorLike, isServiceErrorData, mapServiceErrorCode, mapServiceErrorToSlateErrorInput, mergeScopes, metaWebhookHttp, normalizeOAuthTokenResponse, pickDefined, publicTool, redactUrlAttachmentSecrets, requestAxios, requestAxiosData, runWithContext, scopeClause, setIfDefined, skipWebhook, spec, statusLabelForApiError, tool, trigger, triggerGroup, uploadAttachmentDirect, validateScopes, verifyHmacSignature };
1142
+ export { AUTH_CONFIG_SECRET_PLACEHOLDER_PREFIX, type ApiErrorBuildHelpers, type ApiErrorDetailOptions, type ApiErrorResponse, type ApiErrorStatus, type ApiServiceError, AuthConfigSecretRedactor, type BuildApiServiceErrorOptions, type CreateHmacSignatureOptions, GET_FILE_URL_TOOL_ID, type GetFileUrlHandler, type GetFileUrlToolInput, type GetFileUrlToolOutput, type NormalizeOAuthTokenResponseOptions, type OAuthExpiresAtOptions, type OAuthRefreshTokenFallbackMode, type OAuthTokenResponse, Slate, SlateAction, SlateActionBuilder, type SlateActionCreateParameters, type SlateActionDocsReference, type SlateActionParameters, type SlateActionParametersAny, type SlateActionParametersTool, type SlateActionParametersTrigger, type SlateActionScopeClause, type SlateActionScopeClauseInput, type SlateActionScopeValue, type SlateActionScopes, type SlateActionScopesInput, type SlateActionType, SlateAdapter, type SlateAdapterCapability, SlateAdapterSpec, type SlateAdapterSpecParameters, type SlateAddAttachmentContent, type SlateAddAttachmentInput, type SlateAttachment, type SlateAttachmentInlineContent, type SlateAttachmentUploadReferenceContent, type SlateAttachmentUrlContent, SlateAuth, type SlateAuthDocsReference, type SlateAuthType, type SlateAuthWithCustomData, type SlateAuthWithNone, type SlateAuthWithOauth, type SlateAuthWithServiceAccount, type SlateAuthWithToken, type SlateAuthenticatedAxiosDefaults, type SlateAxiosDefaults, type SlateAxiosErrorAdapter, type SlateAxiosErrorOptions, SlateConfig, type SlateConfigDocsReference, SlateContext, SlateDeclarationError, SlateDefaultPollingIntervalSeconds, type SlateDirectUploadInput, type SlateDocsReference, SlateError, type SlateErrorInput, type SlateErrorIssue, type SlateErrorKind, type SlateErrorProviderInfo, type SlateErrorResponse, type SlateErrorUpstreamInfo, type SlateGatewayEvent, type SlateLiveInvocationInfo, type SlateLogEntry, type SlateLogFields, type SlateLogListener, type SlateLogMessageInput, type SlateLogType, SlateLogger, type SlateProviderDocsReference, SlatePublicContext, SlatePublicTool, type SlatePublicToolInvocationHandler, type SlatePublicToolParameters, type SlateServiceErrorData, SlateSpecification, type SlateSpecificationCreateParameters, type SlateSpecificationParameters, SlateTool, type SlateToolInvocationHandler, type SlateToolParameters, SlateTrigger, SlateTriggerGroup, SlateTriggerGroupBuilder, type SlateTriggerGroupCreateParameters, type SlateTriggerGroupGatewayConnectHandler, type SlateTriggerGroupGatewayParameters, type SlateTriggerGroupGatewayReceiveHandler, type SlateTriggerGroupParameters, type SlateTriggerGroupPollHandler, type SlateTriggerGroupPollingParameters, type SlateTriggerGroupRoutingMatchersHandler, type SlateTriggerGroupWebhookAutoRegistrationParameters, type SlateTriggerGroupWebhookManualRegistrationParameters, type SlateTriggerGroupWebhookParameters, type SlateTriggerMappingHandler, type SlateTriggerMatchesHandler, type SlateTriggerParameters, type SlateTriggerRoutingMatcher, type SlateTypedDocsReference, type SlateWebhookHttpResponseInit, type SlateWebhookManualFinishHandler, type SlateWebhookManualSetupHandler, type SlateWebhookProcessHandler, type SlateWebhookRegisterHandler, type SlateWebhookSkipped, type SlateWebhookTarget, type SlateWebhookTargetListHandler, type SlateWebhookTargetOwnership, type SlateWebhookUnregisterHandler, type VerifyHmacSignatureOptions, adapterSpec, allOf, anyOf, auth, axios, buildApiServiceError, collectApiErrorDetails, config, createApiServiceError, createAuthenticatedAxios, createAxios, createBase64Attachment, createHmacSignature, createSlateError, createTextAttachment, createUrlAttachment, extractApiErrorMessage, getApiErrorResponse, getApiErrorStatus, getBase64ByteLength, getCurrentContext, getFileUrlTool, getFileUrlToolInputSchema, getFileUrlToolOutputSchema, getGraphWebhookValidationResponse, getMetaWebhookVerificationResponse, getOAuthExpiresAtFromExpiresIn, getResponseHeaderValue, getServiceErrorData, graphWebhookHttp, inferSlateErrorFromAxios, isApiErrorRecord, isAxiosErrorLike, isServiceErrorData, isUrlEmbeddedCredential, mapServiceErrorCode, mapServiceErrorToSlateErrorInput, mergeScopes, metaWebhookHttp, normalizeOAuthTokenResponse, pickDefined, publicTool, redactUrlAttachmentSecrets, requestAxios, requestAxiosData, runWithContext, scopeClause, setIfDefined, skipWebhook, spec, statusLabelForApiError, tool, trigger, triggerGroup, uploadAttachmentDirect, validateScopes, verifyHmacSignature };
package/dist/index.d.ts CHANGED
@@ -50,15 +50,17 @@ declare class AuthConfigSecretRedactor {
50
50
  private readonly secretToPlaceholder;
51
51
  constructor(authConfig: Record<string, unknown> | undefined);
52
52
  redact<T>(value: T): T;
53
- redactEmbedded<T>(value: T): T;
53
+ /** `embeddable` limits which secrets are also replaced inside longer strings. */
54
+ redactEmbedded<T>(value: T, embeddable?: (secret: string, path: string) => boolean): T;
54
55
  private buildSecretMap;
55
56
  private transform;
56
57
  private isPlainObject;
57
58
  }
59
+ declare let isUrlEmbeddedCredential: (secret: string, path: string) => boolean;
58
60
  /**
59
61
  * Applied to every finalized attachment list right before it's sent over
60
62
  * `slates/action.tool.invoke` (see @slates/provider-handler). Only `type: 'url'` attachments
61
- * carry headers/query -- content/upload_reference attachments pass through untouched.
63
+ * carry credentials -- content/upload_reference attachments pass through untouched.
62
64
  */
63
65
  declare let redactUrlAttachmentSecrets: (attachments: SlateAttachment[] | undefined, authConfig: Record<string, unknown> | undefined) => SlateAttachment[] | undefined;
64
66
 
@@ -523,6 +525,41 @@ type SlateTriggerGroupPollHandler<ConfigType extends {}, AuthType extends {}> =
523
525
  }[];
524
526
  updatedState?: any;
525
527
  }>;
528
+ interface SlateGatewayEvent {
529
+ payload: any;
530
+ idempotencyKey?: string;
531
+ }
532
+ type SlateTriggerGroupGatewayConnectHandler<ConfigType extends {}, AuthType extends {}> = (context: SlateContext<ConfigType, AuthType, {
533
+ state: any | null;
534
+ }>) => Promise<{
535
+ url: string;
536
+ state?: any;
537
+ }>;
538
+ /** `closed` is set, with no frames, when the socket closed unexpectedly. */
539
+ type SlateTriggerGroupGatewayReceiveHandler<ConfigType extends {}, AuthType extends {}> = (context: SlateContext<ConfigType, AuthType, {
540
+ state: any | null;
541
+ frames: string[];
542
+ closed?: {
543
+ code: number;
544
+ reason: string;
545
+ } | null;
546
+ }>) => Promise<{
547
+ state?: any;
548
+ send?: string[];
549
+ events?: SlateGatewayEvent[];
550
+ /** With `expectsAck`, the hub reconnects when no `heartbeatAcked` arrives before the next beat. */
551
+ heartbeat?: {
552
+ intervalMs: number;
553
+ frame: string;
554
+ expectsAck?: boolean;
555
+ } | null;
556
+ /** Set when `frames` included the provider's heartbeat acknowledgement. */
557
+ heartbeatAcked?: boolean;
558
+ close?: {
559
+ reconnect: boolean;
560
+ reason?: string;
561
+ } | null;
562
+ }>;
526
563
  type SlateWebhookTargetListHandler<ConfigType extends {}, AuthType extends {}> = (context: SlateContext<ConfigType, AuthType, {
527
564
  pageToken: any | null;
528
565
  }>) => Promise<{
@@ -574,6 +611,10 @@ interface SlateTriggerGroupPollingParameters<ConfigType extends {}, AuthType ext
574
611
  intervalSeconds: number;
575
612
  pollEvents: SlateTriggerGroupPollHandler<ConfigType, AuthType>;
576
613
  }
614
+ interface SlateTriggerGroupGatewayParameters<ConfigType extends {}, AuthType extends {}> {
615
+ connect: SlateTriggerGroupGatewayConnectHandler<ConfigType, AuthType>;
616
+ receive: SlateTriggerGroupGatewayReceiveHandler<ConfigType, AuthType>;
617
+ }
577
618
  interface SlateTriggerGroupWebhookAutoRegistrationParameters<ConfigType extends {}, AuthType extends {}> {
578
619
  webhookTargetList: SlateWebhookTargetListHandler<ConfigType, AuthType>;
579
620
  webhookRegister: SlateWebhookRegisterHandler<ConfigType, AuthType>;
@@ -598,9 +639,10 @@ interface SlateTriggerGroupCreateParameters<InputType extends {} = Record<string
598
639
  eventSchema?: z$1.ZodType<InputType>;
599
640
  }
600
641
  interface SlateTriggerGroupParameters<ConfigType extends {}, AuthType extends {}, InputType extends {} = Record<string, unknown>> extends SlateTriggerGroupCreateParameters<InputType> {
601
- source: 'polling' | 'webhook';
642
+ source: 'polling' | 'webhook' | 'gateway';
602
643
  polling?: SlateTriggerGroupPollingParameters<ConfigType, AuthType>;
603
644
  webhook?: SlateTriggerGroupWebhookParameters<ConfigType, AuthType>;
645
+ gateway?: SlateTriggerGroupGatewayParameters<ConfigType, AuthType>;
604
646
  routingMatchers: SlateTriggerGroupRoutingMatchersHandler<ConfigType, AuthType>;
605
647
  }
606
648
  declare class SlateTriggerGroup<ConfigType extends {}, AuthType extends {}, InputType extends {} = Record<string, unknown>> {
@@ -614,9 +656,10 @@ declare class SlateTriggerGroup<ConfigType extends {}, AuthType extends {}, Inpu
614
656
  get description(): string | undefined;
615
657
  get metadata(): Record<string, any> | undefined;
616
658
  get eventSchema(): z$1.ZodType<InputType, unknown, z$1.core.$ZodTypeInternals<InputType, unknown>>;
617
- get source(): "polling" | "webhook";
659
+ get source(): "polling" | "webhook" | "gateway";
618
660
  get polling(): SlateTriggerGroupPollingParameters<ConfigType, AuthType> | undefined;
619
661
  get webhook(): SlateTriggerGroupWebhookParameters<ConfigType, AuthType> | undefined;
662
+ get gateway(): SlateTriggerGroupGatewayParameters<ConfigType, AuthType> | undefined;
620
663
  get routingMatchers(): SlateTriggerGroupRoutingMatchersHandler<ConfigType, AuthType>;
621
664
  }
622
665
  declare class SlateTriggerGroupBuilder<ConfigType extends {}, AuthType extends {}, InputType extends {} = Record<string, unknown>> {
@@ -634,6 +677,8 @@ declare class SlateTriggerGroupBuilder<ConfigType extends {}, AuthType extends {
634
677
  manualRegistration?: SlateTriggerGroupWebhookManualRegistrationParameters<UserConfigSchema, FullConfigSchema>;
635
678
  process: SlateWebhookProcessHandler;
636
679
  }): SlateTriggerGroupBuilder<ConfigType, AuthType, InputType>;
680
+ /** Receive events over a platform-managed persistent connection (e.g. a WebSocket gateway). */
681
+ gateway(props: SlateTriggerGroupGatewayParameters<ConfigType, AuthType>): SlateTriggerGroupBuilder<ConfigType, AuthType, InputType>;
637
682
  routingMatchers(handler: SlateTriggerGroupRoutingMatchersHandler<ConfigType, AuthType>): SlateTriggerGroupBuilder<ConfigType, AuthType, InputType>;
638
683
  build(): SlateTriggerGroup<ConfigType, AuthType, InputType>;
639
684
  }
@@ -1094,4 +1139,4 @@ declare let skipWebhook: (reason: string, response?: Response | SlateWebhookHttp
1094
1139
  response: Response | SlateWebhookHttpResponseInit;
1095
1140
  };
1096
1141
 
1097
- export { AUTH_CONFIG_SECRET_PLACEHOLDER_PREFIX, type ApiErrorBuildHelpers, type ApiErrorDetailOptions, type ApiErrorResponse, type ApiErrorStatus, type ApiServiceError, AuthConfigSecretRedactor, type BuildApiServiceErrorOptions, type CreateHmacSignatureOptions, GET_FILE_URL_TOOL_ID, type GetFileUrlHandler, type GetFileUrlToolInput, type GetFileUrlToolOutput, type NormalizeOAuthTokenResponseOptions, type OAuthExpiresAtOptions, type OAuthRefreshTokenFallbackMode, type OAuthTokenResponse, Slate, SlateAction, SlateActionBuilder, type SlateActionCreateParameters, type SlateActionDocsReference, type SlateActionParameters, type SlateActionParametersAny, type SlateActionParametersTool, type SlateActionParametersTrigger, type SlateActionScopeClause, type SlateActionScopeClauseInput, type SlateActionScopeValue, type SlateActionScopes, type SlateActionScopesInput, type SlateActionType, SlateAdapter, type SlateAdapterCapability, SlateAdapterSpec, type SlateAdapterSpecParameters, type SlateAddAttachmentContent, type SlateAddAttachmentInput, type SlateAttachment, type SlateAttachmentInlineContent, type SlateAttachmentUploadReferenceContent, type SlateAttachmentUrlContent, SlateAuth, type SlateAuthDocsReference, type SlateAuthType, type SlateAuthWithCustomData, type SlateAuthWithNone, type SlateAuthWithOauth, type SlateAuthWithServiceAccount, type SlateAuthWithToken, type SlateAuthenticatedAxiosDefaults, type SlateAxiosDefaults, type SlateAxiosErrorAdapter, type SlateAxiosErrorOptions, SlateConfig, type SlateConfigDocsReference, SlateContext, SlateDeclarationError, SlateDefaultPollingIntervalSeconds, type SlateDirectUploadInput, type SlateDocsReference, SlateError, type SlateErrorInput, type SlateErrorIssue, type SlateErrorKind, type SlateErrorProviderInfo, type SlateErrorResponse, type SlateErrorUpstreamInfo, type SlateLiveInvocationInfo, type SlateLogEntry, type SlateLogFields, type SlateLogListener, type SlateLogMessageInput, type SlateLogType, SlateLogger, type SlateProviderDocsReference, SlatePublicContext, SlatePublicTool, type SlatePublicToolInvocationHandler, type SlatePublicToolParameters, type SlateServiceErrorData, SlateSpecification, type SlateSpecificationCreateParameters, type SlateSpecificationParameters, SlateTool, type SlateToolInvocationHandler, type SlateToolParameters, SlateTrigger, SlateTriggerGroup, SlateTriggerGroupBuilder, type SlateTriggerGroupCreateParameters, type SlateTriggerGroupParameters, type SlateTriggerGroupPollHandler, type SlateTriggerGroupPollingParameters, type SlateTriggerGroupRoutingMatchersHandler, type SlateTriggerGroupWebhookAutoRegistrationParameters, type SlateTriggerGroupWebhookManualRegistrationParameters, type SlateTriggerGroupWebhookParameters, type SlateTriggerMappingHandler, type SlateTriggerMatchesHandler, type SlateTriggerParameters, type SlateTriggerRoutingMatcher, type SlateTypedDocsReference, type SlateWebhookHttpResponseInit, type SlateWebhookManualFinishHandler, type SlateWebhookManualSetupHandler, type SlateWebhookProcessHandler, type SlateWebhookRegisterHandler, type SlateWebhookSkipped, type SlateWebhookTarget, type SlateWebhookTargetListHandler, type SlateWebhookTargetOwnership, type SlateWebhookUnregisterHandler, type VerifyHmacSignatureOptions, adapterSpec, allOf, anyOf, auth, axios, buildApiServiceError, collectApiErrorDetails, config, createApiServiceError, createAuthenticatedAxios, createAxios, createBase64Attachment, createHmacSignature, createSlateError, createTextAttachment, createUrlAttachment, extractApiErrorMessage, getApiErrorResponse, getApiErrorStatus, getBase64ByteLength, getCurrentContext, getFileUrlTool, getFileUrlToolInputSchema, getFileUrlToolOutputSchema, getGraphWebhookValidationResponse, getMetaWebhookVerificationResponse, getOAuthExpiresAtFromExpiresIn, getResponseHeaderValue, getServiceErrorData, graphWebhookHttp, inferSlateErrorFromAxios, isApiErrorRecord, isAxiosErrorLike, isServiceErrorData, mapServiceErrorCode, mapServiceErrorToSlateErrorInput, mergeScopes, metaWebhookHttp, normalizeOAuthTokenResponse, pickDefined, publicTool, redactUrlAttachmentSecrets, requestAxios, requestAxiosData, runWithContext, scopeClause, setIfDefined, skipWebhook, spec, statusLabelForApiError, tool, trigger, triggerGroup, uploadAttachmentDirect, validateScopes, verifyHmacSignature };
1142
+ export { AUTH_CONFIG_SECRET_PLACEHOLDER_PREFIX, type ApiErrorBuildHelpers, type ApiErrorDetailOptions, type ApiErrorResponse, type ApiErrorStatus, type ApiServiceError, AuthConfigSecretRedactor, type BuildApiServiceErrorOptions, type CreateHmacSignatureOptions, GET_FILE_URL_TOOL_ID, type GetFileUrlHandler, type GetFileUrlToolInput, type GetFileUrlToolOutput, type NormalizeOAuthTokenResponseOptions, type OAuthExpiresAtOptions, type OAuthRefreshTokenFallbackMode, type OAuthTokenResponse, Slate, SlateAction, SlateActionBuilder, type SlateActionCreateParameters, type SlateActionDocsReference, type SlateActionParameters, type SlateActionParametersAny, type SlateActionParametersTool, type SlateActionParametersTrigger, type SlateActionScopeClause, type SlateActionScopeClauseInput, type SlateActionScopeValue, type SlateActionScopes, type SlateActionScopesInput, type SlateActionType, SlateAdapter, type SlateAdapterCapability, SlateAdapterSpec, type SlateAdapterSpecParameters, type SlateAddAttachmentContent, type SlateAddAttachmentInput, type SlateAttachment, type SlateAttachmentInlineContent, type SlateAttachmentUploadReferenceContent, type SlateAttachmentUrlContent, SlateAuth, type SlateAuthDocsReference, type SlateAuthType, type SlateAuthWithCustomData, type SlateAuthWithNone, type SlateAuthWithOauth, type SlateAuthWithServiceAccount, type SlateAuthWithToken, type SlateAuthenticatedAxiosDefaults, type SlateAxiosDefaults, type SlateAxiosErrorAdapter, type SlateAxiosErrorOptions, SlateConfig, type SlateConfigDocsReference, SlateContext, SlateDeclarationError, SlateDefaultPollingIntervalSeconds, type SlateDirectUploadInput, type SlateDocsReference, SlateError, type SlateErrorInput, type SlateErrorIssue, type SlateErrorKind, type SlateErrorProviderInfo, type SlateErrorResponse, type SlateErrorUpstreamInfo, type SlateGatewayEvent, type SlateLiveInvocationInfo, type SlateLogEntry, type SlateLogFields, type SlateLogListener, type SlateLogMessageInput, type SlateLogType, SlateLogger, type SlateProviderDocsReference, SlatePublicContext, SlatePublicTool, type SlatePublicToolInvocationHandler, type SlatePublicToolParameters, type SlateServiceErrorData, SlateSpecification, type SlateSpecificationCreateParameters, type SlateSpecificationParameters, SlateTool, type SlateToolInvocationHandler, type SlateToolParameters, SlateTrigger, SlateTriggerGroup, SlateTriggerGroupBuilder, type SlateTriggerGroupCreateParameters, type SlateTriggerGroupGatewayConnectHandler, type SlateTriggerGroupGatewayParameters, type SlateTriggerGroupGatewayReceiveHandler, type SlateTriggerGroupParameters, type SlateTriggerGroupPollHandler, type SlateTriggerGroupPollingParameters, type SlateTriggerGroupRoutingMatchersHandler, type SlateTriggerGroupWebhookAutoRegistrationParameters, type SlateTriggerGroupWebhookManualRegistrationParameters, type SlateTriggerGroupWebhookParameters, type SlateTriggerMappingHandler, type SlateTriggerMatchesHandler, type SlateTriggerParameters, type SlateTriggerRoutingMatcher, type SlateTypedDocsReference, type SlateWebhookHttpResponseInit, type SlateWebhookManualFinishHandler, type SlateWebhookManualSetupHandler, type SlateWebhookProcessHandler, type SlateWebhookRegisterHandler, type SlateWebhookSkipped, type SlateWebhookTarget, type SlateWebhookTargetListHandler, type SlateWebhookTargetOwnership, type SlateWebhookUnregisterHandler, type VerifyHmacSignatureOptions, adapterSpec, allOf, anyOf, auth, axios, buildApiServiceError, collectApiErrorDetails, config, createApiServiceError, createAuthenticatedAxios, createAxios, createBase64Attachment, createHmacSignature, createSlateError, createTextAttachment, createUrlAttachment, extractApiErrorMessage, getApiErrorResponse, getApiErrorStatus, getBase64ByteLength, getCurrentContext, getFileUrlTool, getFileUrlToolInputSchema, getFileUrlToolOutputSchema, getGraphWebhookValidationResponse, getMetaWebhookVerificationResponse, getOAuthExpiresAtFromExpiresIn, getResponseHeaderValue, getServiceErrorData, graphWebhookHttp, inferSlateErrorFromAxios, isApiErrorRecord, isAxiosErrorLike, isServiceErrorData, isUrlEmbeddedCredential, mapServiceErrorCode, mapServiceErrorToSlateErrorInput, mergeScopes, metaWebhookHttp, normalizeOAuthTokenResponse, pickDefined, publicTool, redactUrlAttachmentSecrets, requestAxios, requestAxiosData, runWithContext, scopeClause, setIfDefined, skipWebhook, spec, statusLabelForApiError, tool, trigger, triggerGroup, uploadAttachmentDirect, validateScopes, verifyHmacSignature };
@@ -961,10 +961,14 @@ var AuthConfigSecretRedactor = class {
961
961
  return this.secretToPlaceholder.get(stringValue) ?? stringValue;
962
962
  });
963
963
  }
964
- redactEmbedded(value) {
965
- if (this.secretToPlaceholder.size === 0) return value;
964
+ /** `embeddable` limits which secrets are also replaced inside longer strings. */
965
+ redactEmbedded(value, embeddable = () => true) {
966
+ let secrets = [...this.secretToPlaceholder.entries()].filter(
967
+ ([secret, placeholder]) => embeddable(secret, placeholder.slice(AUTH_CONFIG_SECRET_PLACEHOLDER_PREFIX.length))
968
+ ).map(([secret]) => secret);
969
+ if (secrets.length === 0) return this.redact(value);
966
970
  let pattern = new RegExp(
967
- [...this.secretToPlaceholder.keys()].sort((a, b) => b.length - a.length).map((secret) => secret.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"),
971
+ secrets.sort((a, b) => b.length - a.length).map((secret) => secret.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|"),
968
972
  "g"
969
973
  );
970
974
  return this.transform(value, (stringValue) => {
@@ -1017,18 +1021,21 @@ var AuthConfigSecretRedactor = class {
1017
1021
  return typeof value === "object" && value !== null && !Array.isArray(value);
1018
1022
  }
1019
1023
  };
1024
+ var URL_CREDENTIAL_KEY = /^(?:.*[-_])?(?:token|secret|password|api[-_]?key)$/i;
1025
+ var isUrlEmbeddedCredential = (secret, path) => secret.length >= 8 && URL_CREDENTIAL_KEY.test(path.split(".").pop() ?? "");
1020
1026
  var redactUrlAttachmentSecrets = (attachments, authConfig) => {
1021
1027
  if (!authConfig || !attachments) return attachments;
1022
1028
  let redactor = new AuthConfigSecretRedactor(authConfig);
1023
1029
  return attachments.map((attachment) => {
1024
1030
  if (attachment.content.type !== "url") return attachment;
1025
- if (!attachment.content.headers && !attachment.content.query) return attachment;
1031
+ let { url, headers, query } = attachment.content;
1026
1032
  return {
1027
1033
  ...attachment,
1028
1034
  content: {
1029
1035
  ...attachment.content,
1030
- headers: attachment.content.headers ? redactor.redactEmbedded(attachment.content.headers) : void 0,
1031
- query: attachment.content.query ? redactor.redactEmbedded(attachment.content.query) : void 0
1036
+ url: redactor.redactEmbedded(url, isUrlEmbeddedCredential),
1037
+ ...headers ? { headers: redactor.redactEmbedded(headers) } : {},
1038
+ ...query ? { query: redactor.redactEmbedded(query) } : {}
1032
1039
  }
1033
1040
  };
1034
1041
  });
@@ -1078,6 +1085,7 @@ var getFileUrlTool = (spec2, handleGetFileUrl) => tool(spec2, {
1078
1085
  return {
1079
1086
  output: {
1080
1087
  ...output,
1088
+ url: redactor.redactEmbedded(output.url, isUrlEmbeddedCredential),
1081
1089
  ...output.headers ? { headers: redactor.redactEmbedded(output.headers) } : {},
1082
1090
  ...output.query ? { query: redactor.redactEmbedded(output.query) } : {}
1083
1091
  },
@@ -1175,6 +1183,9 @@ var SlateTriggerGroup = class _SlateTriggerGroup {
1175
1183
  get webhook() {
1176
1184
  return this._params.webhook;
1177
1185
  }
1186
+ get gateway() {
1187
+ return this._params.gateway;
1188
+ }
1178
1189
  get routingMatchers() {
1179
1190
  return this._params.routingMatchers;
1180
1191
  }
@@ -1191,6 +1202,7 @@ var SlateTriggerGroupBuilder = class {
1191
1202
  #source = null;
1192
1203
  #polling = null;
1193
1204
  #webhook = null;
1205
+ #gateway = null;
1194
1206
  #routingMatchers = null;
1195
1207
  polling(props) {
1196
1208
  if (this.#source) {
@@ -1220,6 +1232,15 @@ var SlateTriggerGroupBuilder = class {
1220
1232
  };
1221
1233
  return this;
1222
1234
  }
1235
+ /** Receive events over a platform-managed persistent connection (e.g. a WebSocket gateway). */
1236
+ gateway(props) {
1237
+ if (this.#source) {
1238
+ throw new SlateDeclarationError("Trigger group invocation is already defined");
1239
+ }
1240
+ this.#source = "gateway";
1241
+ this.#gateway = { connect: props.connect, receive: props.receive };
1242
+ return this;
1243
+ }
1223
1244
  routingMatchers(handler) {
1224
1245
  this.#routingMatchers = handler;
1225
1246
  return this;
@@ -1227,7 +1248,7 @@ var SlateTriggerGroupBuilder = class {
1227
1248
  build() {
1228
1249
  if (!this.#source) {
1229
1250
  throw new SlateDeclarationError(
1230
- "Trigger group invocation (polling or webhook) is not defined"
1251
+ "Trigger group invocation (polling, webhook, or gateway) is not defined"
1231
1252
  );
1232
1253
  }
1233
1254
  if (!this.#routingMatchers) {
@@ -1238,6 +1259,7 @@ var SlateTriggerGroupBuilder = class {
1238
1259
  source: this.#source,
1239
1260
  polling: this.#polling ?? void 0,
1240
1261
  webhook: this.#webhook ?? void 0,
1262
+ gateway: this.#gateway ?? void 0,
1241
1263
  routingMatchers: this.#routingMatchers
1242
1264
  });
1243
1265
  }
@@ -2027,6 +2049,7 @@ var redactHashFragment = (hash, redactor) => {
2027
2049
  );
2028
2050
  return redacted ? `#${redacted}` : "";
2029
2051
  };
2052
+ var isEmbeddedPathSecret = (secret, path) => secret.length >= 8 && isSecretKeyName(path.split(".").pop() ?? "");
2030
2053
  var sanitizeUrl = (value, redactor) => {
2031
2054
  if (!value) return value;
2032
2055
  try {
@@ -2035,6 +2058,7 @@ var sanitizeUrl = (value, redactor) => {
2035
2058
  url.username = "";
2036
2059
  url.password = "";
2037
2060
  }
2061
+ if (redactor) url.pathname = redactor.redactEmbedded(url.pathname, isEmbeddedPathSecret);
2038
2062
  for (let key of Array.from(url.searchParams.keys())) {
2039
2063
  if (isUrlFormSecretKeyName(key)) {
2040
2064
  url.searchParams.set(key, REDACTED_VALUE);
@@ -2047,7 +2071,7 @@ var sanitizeUrl = (value, redactor) => {
2047
2071
  url.hash = redactHashFragment(url.hash, redactor);
2048
2072
  return decodeRedactedMarker(url.toString());
2049
2073
  } catch {
2050
- let text = redactor ? redactor.redact(value) : value;
2074
+ let text = redactor ? redactor.redactEmbedded(value, isEmbeddedPathSecret) : value;
2051
2075
  return text.replace(
2052
2076
  /([?&]([^=&#]+)=)([^&#]+)/g,
2053
2077
  (_match, prefix, key, rawValue) => isUrlFormSecretKeyName(key) ? `${prefix}${REDACTED_VALUE}` : `${prefix}${rawValue}`
@@ -2898,6 +2922,7 @@ export {
2898
2922
  isApiErrorRecord,
2899
2923
  isAxiosErrorLike,
2900
2924
  isServiceErrorData,
2925
+ isUrlEmbeddedCredential,
2901
2926
  mapServiceErrorCode,
2902
2927
  mapServiceErrorToSlateErrorInput,
2903
2928
  mergeScopes,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slates/provider",
3
- "version": "1.0.0-rc.27",
3
+ "version": "1.0.0-rc.28",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { AuthConfigSecretRedactor } from '../auth/redact';
2
+ import { AuthConfigSecretRedactor, isUrlEmbeddedCredential } from '../auth/redact';
3
3
  import type { SlateContext } from '../context/context';
4
4
  import type { SlateSpecification } from '../specification/specification';
5
5
  import { tool } from './tool';
@@ -42,6 +42,7 @@ export let getFileUrlTool = <ConfigType extends {}, AuthType extends {}>(
42
42
  return {
43
43
  output: {
44
44
  ...output,
45
+ url: redactor.redactEmbedded(output.url, isUrlEmbeddedCredential),
45
46
  ...(output.headers ? { headers: redactor.redactEmbedded(output.headers) } : {}),
46
47
  ...(output.query ? { query: redactor.redactEmbedded(output.query) } : {})
47
48
  },
@@ -31,6 +31,39 @@ export type SlateTriggerGroupPollHandler<ConfigType extends {}, AuthType extends
31
31
  updatedState?: any;
32
32
  }>;
33
33
 
34
+ export interface SlateGatewayEvent {
35
+ payload: any;
36
+ idempotencyKey?: string;
37
+ }
38
+
39
+ export type SlateTriggerGroupGatewayConnectHandler<
40
+ ConfigType extends {},
41
+ AuthType extends {}
42
+ > = (
43
+ context: SlateContext<ConfigType, AuthType, { state: any | null }>
44
+ ) => Promise<{ url: string; state?: any }>;
45
+
46
+ /** `closed` is set, with no frames, when the socket closed unexpectedly. */
47
+ export type SlateTriggerGroupGatewayReceiveHandler<
48
+ ConfigType extends {},
49
+ AuthType extends {}
50
+ > = (
51
+ context: SlateContext<
52
+ ConfigType,
53
+ AuthType,
54
+ { state: any | null; frames: string[]; closed?: { code: number; reason: string } | null }
55
+ >
56
+ ) => Promise<{
57
+ state?: any;
58
+ send?: string[];
59
+ events?: SlateGatewayEvent[];
60
+ /** With `expectsAck`, the hub reconnects when no `heartbeatAcked` arrives before the next beat. */
61
+ heartbeat?: { intervalMs: number; frame: string; expectsAck?: boolean } | null;
62
+ /** Set when `frames` included the provider's heartbeat acknowledgement. */
63
+ heartbeatAcked?: boolean;
64
+ close?: { reconnect: boolean; reason?: string } | null;
65
+ }>;
66
+
34
67
  export type SlateWebhookTargetListHandler<ConfigType extends {}, AuthType extends {}> = (
35
68
  context: SlateContext<ConfigType, AuthType, { pageToken: any | null }>
36
69
  ) => Promise<{
@@ -121,6 +154,14 @@ export interface SlateTriggerGroupPollingParameters<
121
154
  pollEvents: SlateTriggerGroupPollHandler<ConfigType, AuthType>;
122
155
  }
123
156
 
157
+ export interface SlateTriggerGroupGatewayParameters<
158
+ ConfigType extends {},
159
+ AuthType extends {}
160
+ > {
161
+ connect: SlateTriggerGroupGatewayConnectHandler<ConfigType, AuthType>;
162
+ receive: SlateTriggerGroupGatewayReceiveHandler<ConfigType, AuthType>;
163
+ }
164
+
124
165
  export interface SlateTriggerGroupWebhookAutoRegistrationParameters<
125
166
  ConfigType extends {},
126
167
  AuthType extends {}
@@ -164,9 +205,10 @@ export interface SlateTriggerGroupParameters<
164
205
  AuthType extends {},
165
206
  InputType extends {} = Record<string, unknown>
166
207
  > extends SlateTriggerGroupCreateParameters<InputType> {
167
- source: 'polling' | 'webhook';
208
+ source: 'polling' | 'webhook' | 'gateway';
168
209
  polling?: SlateTriggerGroupPollingParameters<ConfigType, AuthType>;
169
210
  webhook?: SlateTriggerGroupWebhookParameters<ConfigType, AuthType>;
211
+ gateway?: SlateTriggerGroupGatewayParameters<ConfigType, AuthType>;
170
212
  routingMatchers: SlateTriggerGroupRoutingMatchersHandler<ConfigType, AuthType>;
171
213
  }
172
214
 
@@ -227,6 +269,10 @@ export class SlateTriggerGroup<
227
269
  return this._params.webhook;
228
270
  }
229
271
 
272
+ get gateway() {
273
+ return this._params.gateway;
274
+ }
275
+
230
276
  get routingMatchers() {
231
277
  return this._params.routingMatchers;
232
278
  }
@@ -237,9 +283,10 @@ export class SlateTriggerGroupBuilder<
237
283
  AuthType extends {},
238
284
  InputType extends {} = Record<string, unknown>
239
285
  > {
240
- #source: 'polling' | 'webhook' | null = null;
286
+ #source: 'polling' | 'webhook' | 'gateway' | null = null;
241
287
  #polling: SlateTriggerGroupPollingParameters<ConfigType, AuthType> | null = null;
242
288
  #webhook: SlateTriggerGroupWebhookParameters<ConfigType, AuthType> | null = null;
289
+ #gateway: SlateTriggerGroupGatewayParameters<ConfigType, AuthType> | null = null;
243
290
  #routingMatchers: SlateTriggerGroupRoutingMatchersHandler<ConfigType, AuthType> | null =
244
291
  null;
245
292
 
@@ -302,6 +349,20 @@ export class SlateTriggerGroupBuilder<
302
349
  return this;
303
350
  }
304
351
 
352
+ /** Receive events over a platform-managed persistent connection (e.g. a WebSocket gateway). */
353
+ gateway(
354
+ props: SlateTriggerGroupGatewayParameters<ConfigType, AuthType>
355
+ ): SlateTriggerGroupBuilder<ConfigType, AuthType, InputType> {
356
+ if (this.#source) {
357
+ throw new SlateDeclarationError('Trigger group invocation is already defined');
358
+ }
359
+
360
+ this.#source = 'gateway';
361
+ this.#gateway = { connect: props.connect, receive: props.receive };
362
+
363
+ return this;
364
+ }
365
+
305
366
  routingMatchers(
306
367
  handler: SlateTriggerGroupRoutingMatchersHandler<ConfigType, AuthType>
307
368
  ): SlateTriggerGroupBuilder<ConfigType, AuthType, InputType> {
@@ -312,7 +373,7 @@ export class SlateTriggerGroupBuilder<
312
373
  build(): SlateTriggerGroup<ConfigType, AuthType, InputType> {
313
374
  if (!this.#source) {
314
375
  throw new SlateDeclarationError(
315
- 'Trigger group invocation (polling or webhook) is not defined'
376
+ 'Trigger group invocation (polling, webhook, or gateway) is not defined'
316
377
  );
317
378
  }
318
379
  if (!this.#routingMatchers) {
@@ -324,6 +385,7 @@ export class SlateTriggerGroupBuilder<
324
385
  source: this.#source,
325
386
  polling: this.#polling ?? undefined,
326
387
  webhook: this.#webhook ?? undefined,
388
+ gateway: this.#gateway ?? undefined,
327
389
  routingMatchers: this.#routingMatchers
328
390
  });
329
391
  }
@@ -167,7 +167,7 @@ describe('redactUrlAttachmentSecrets', () => {
167
167
  ]);
168
168
  });
169
169
 
170
- it('only changes header and query values, preserving metadata and the original attachment', () => {
170
+ it('only changes credential values, preserving metadata and the original attachment', () => {
171
171
  let attachment: SlateAttachment = {
172
172
  mimeType: 'application/pdf',
173
173
  attachmentHash: 'file-hash',
@@ -187,6 +187,7 @@ describe('redactUrlAttachmentSecrets', () => {
187
187
  ...original,
188
188
  content: {
189
189
  ...original.content,
190
+ url: 'https://example.com/$$MT$secret$authConfig$token$$/file.pdf',
190
191
  headers: { Authorization: 'Bearer $$MT$secret$authConfig$token$$' },
191
192
  query: { key: '$$MT$secret$authConfig$nested.token2$$-suffix' }
192
193
  }
@@ -195,7 +196,35 @@ describe('redactUrlAttachmentSecrets', () => {
195
196
  expect(attachment).toEqual(original);
196
197
  });
197
198
 
198
- it('leaves url attachments with no headers/query untouched', () => {
199
+ it('redacts a bot token embedded in the url path', () => {
200
+ let attachments = [
201
+ urlAttachment({ url: 'https://api.telegram.org/file/bot123:ABCDEFGH/photos/a.jpg' })
202
+ ];
203
+
204
+ expect(
205
+ redactUrlAttachmentSecrets(attachments, { token: '123:ABCDEFGH', botId: '12345678' })
206
+ ).toEqual([
207
+ urlAttachment({
208
+ url: 'https://api.telegram.org/file/bot$$MT$secret$authConfig$token$$/photos/a.jpg'
209
+ })
210
+ ]);
211
+ });
212
+
213
+ it('keeps non-credential and short auth values in the url', () => {
214
+ let attachments = [
215
+ urlAttachment({ url: 'https://example.com/AC12345678/12345678/abc/file.pdf' })
216
+ ];
217
+
218
+ expect(
219
+ redactUrlAttachmentSecrets(attachments, {
220
+ account_sid: 'AC12345678',
221
+ botId: '12345678',
222
+ token: 'abc'
223
+ })
224
+ ).toEqual(attachments);
225
+ });
226
+
227
+ it('leaves url attachments with no credentials untouched', () => {
199
228
  let attachments = [urlAttachment()];
200
229
  expect(redactUrlAttachmentSecrets(attachments, authConfig)).toEqual(attachments);
201
230
  });
@@ -25,11 +25,20 @@ export class AuthConfigSecretRedactor {
25
25
  });
26
26
  }
27
27
 
28
- public redactEmbedded<T>(value: T): T {
29
- if (this.secretToPlaceholder.size === 0) return value;
28
+ /** `embeddable` limits which secrets are also replaced inside longer strings. */
29
+ public redactEmbedded<T>(
30
+ value: T,
31
+ embeddable: (secret: string, path: string) => boolean = () => true
32
+ ): T {
33
+ let secrets = [...this.secretToPlaceholder.entries()]
34
+ .filter(([secret, placeholder]) =>
35
+ embeddable(secret, placeholder.slice(AUTH_CONFIG_SECRET_PLACEHOLDER_PREFIX.length))
36
+ )
37
+ .map(([secret]) => secret);
38
+ if (secrets.length === 0) return this.redact(value);
30
39
 
31
40
  let pattern = new RegExp(
32
- [...this.secretToPlaceholder.keys()]
41
+ secrets
33
42
  .sort((a, b) => b.length - a.length)
34
43
  // Escape regex metacharacters so values match literally; $& inserts the matched character.
35
44
  .map(secret => secret.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
@@ -102,10 +111,17 @@ export class AuthConfigSecretRedactor {
102
111
  }
103
112
  }
104
113
 
114
+ // Credentials replaced inside URLs (e.g. Telegram's `/file/bot<token>/` path). Kept narrow:
115
+ // a placeholder in a URL makes the hub proxy the download instead of handing out the link.
116
+ let URL_CREDENTIAL_KEY = /^(?:.*[-_])?(?:token|secret|password|api[-_]?key)$/i;
117
+
118
+ export let isUrlEmbeddedCredential = (secret: string, path: string) =>
119
+ secret.length >= 8 && URL_CREDENTIAL_KEY.test(path.split('.').pop() ?? '');
120
+
105
121
  /**
106
122
  * Applied to every finalized attachment list right before it's sent over
107
123
  * `slates/action.tool.invoke` (see @slates/provider-handler). Only `type: 'url'` attachments
108
- * carry headers/query -- content/upload_reference attachments pass through untouched.
124
+ * carry credentials -- content/upload_reference attachments pass through untouched.
109
125
  */
110
126
  export let redactUrlAttachmentSecrets = (
111
127
  attachments: SlateAttachment[] | undefined,
@@ -117,18 +133,15 @@ export let redactUrlAttachmentSecrets = (
117
133
 
118
134
  return attachments.map(attachment => {
119
135
  if (attachment.content.type !== 'url') return attachment;
120
- if (!attachment.content.headers && !attachment.content.query) return attachment;
121
136
 
137
+ let { url, headers, query } = attachment.content;
122
138
  return {
123
139
  ...attachment,
124
140
  content: {
125
141
  ...attachment.content,
126
- headers: attachment.content.headers
127
- ? redactor.redactEmbedded(attachment.content.headers)
128
- : undefined,
129
- query: attachment.content.query
130
- ? redactor.redactEmbedded(attachment.content.query)
131
- : undefined
142
+ url: redactor.redactEmbedded(url, isUrlEmbeddedCredential),
143
+ ...(headers ? { headers: redactor.redactEmbedded(headers) } : {}),
144
+ ...(query ? { query: redactor.redactEmbedded(query) } : {})
132
145
  }
133
146
  };
134
147
  });
@@ -1,4 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest';
2
+ import { AuthConfigSecretRedactor } from '../auth/redact';
2
3
  import { __traceInternals } from './trace';
3
4
 
4
5
  let {
@@ -272,6 +273,26 @@ describe('sanitizeUrl', () => {
272
273
  expect(out).toContain('expires_in=3600');
273
274
  });
274
275
 
276
+ it('redacts long auth-config secrets embedded in the path', () => {
277
+ let redactor = new AuthConfigSecretRedactor({
278
+ token: '123456:ABCdefGHIjklMNOpqrSTU',
279
+ botId: '123456',
280
+ applicationId: '1503442063478362305'
281
+ });
282
+ expect(
283
+ sanitizeUrl('https://api.telegram.org/bot123456:ABCdefGHIjklMNOpqrSTU/getMe', redactor)
284
+ ).toBe('https://api.telegram.org/bot$$MT$secret$authConfig$token$$/getMe');
285
+ expect(sanitizeUrl('https://example.com/bots/123456/info', redactor)).toBe(
286
+ 'https://example.com/bots/123456/info'
287
+ );
288
+ expect(
289
+ sanitizeUrl(
290
+ 'https://discord.com/api/applications/1503442063478362305/commands',
291
+ redactor
292
+ )
293
+ ).toBe('https://discord.com/api/applications/1503442063478362305/commands');
294
+ });
295
+
275
296
  it('leaves innocuous URLs unchanged', () => {
276
297
  expect(sanitizeUrl('https://example.com/api?page=2&limit=10')).toBe(
277
298
  'https://example.com/api?page=2&limit=10'
@@ -563,6 +563,10 @@ let redactHashFragment = (hash: string, redactor?: AuthConfigSecretRedactor) =>
563
563
  return redacted ? `#${redacted}` : '';
564
564
  };
565
565
 
566
+ // Redacts secret-keyed auth values embedded in URL paths (e.g. Telegram's `/bot<token>/...`).
567
+ let isEmbeddedPathSecret = (secret: string, path: string) =>
568
+ secret.length >= 8 && isSecretKeyName(path.split('.').pop() ?? '');
569
+
566
570
  let sanitizeUrl = (value: string, redactor?: AuthConfigSecretRedactor) => {
567
571
  if (!value) return value;
568
572
 
@@ -574,6 +578,8 @@ let sanitizeUrl = (value: string, redactor?: AuthConfigSecretRedactor) => {
574
578
  url.password = '';
575
579
  }
576
580
 
581
+ if (redactor) url.pathname = redactor.redactEmbedded(url.pathname, isEmbeddedPathSecret);
582
+
577
583
  for (let key of Array.from(url.searchParams.keys())) {
578
584
  if (isUrlFormSecretKeyName(key)) {
579
585
  url.searchParams.set(key, REDACTED_VALUE);
@@ -590,7 +596,7 @@ let sanitizeUrl = (value: string, redactor?: AuthConfigSecretRedactor) => {
590
596
 
591
597
  return decodeRedactedMarker(url.toString());
592
598
  } catch {
593
- let text = redactor ? redactor.redact(value) : value;
599
+ let text = redactor ? redactor.redactEmbedded(value, isEmbeddedPathSecret) : value;
594
600
  return text.replace(
595
601
  /([?&]([^=&#]+)=)([^&#]+)/g,
596
602
  (_match, prefix: string, key: string, rawValue: string) =>