@slates/provider 1.0.0-rc.17 → 1.0.0-rc.19

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.
@@ -54,6 +54,12 @@ var SlateAction = class {
54
54
  get docs() {
55
55
  return this._params.docs;
56
56
  }
57
+ get adapter() {
58
+ return this._params.adapter ?? null;
59
+ }
60
+ get isPublic() {
61
+ return this._params.isPublic === true;
62
+ }
57
63
  };
58
64
 
59
65
  // src/error/api.ts
@@ -798,11 +804,12 @@ var isScopes = (value) => typeof value === "object" && value !== null && "AND" i
798
804
 
799
805
  // src/action/builder.ts
800
806
  var SlateActionBuilder = class {
801
- constructor(type, spec2, params, factory) {
807
+ constructor(type, spec2, params, factory, isPublicTool = false) {
802
808
  this.type = type;
803
809
  this.spec = spec2;
804
810
  this.params = params;
805
811
  this.factory = factory;
812
+ this.isPublicTool = isPublicTool;
806
813
  this.#configSchema = spec2.configSchema;
807
814
  this.#authSchema = spec2.authSchema;
808
815
  }
@@ -810,6 +817,7 @@ var SlateActionBuilder = class {
810
817
  spec;
811
818
  params;
812
819
  factory;
820
+ isPublicTool;
813
821
  #configSchema;
814
822
  #authSchema;
815
823
  #inputSchema = null;
@@ -818,20 +826,34 @@ var SlateActionBuilder = class {
818
826
  #authMethods;
819
827
  #toolParams = null;
820
828
  #triggerParams = null;
829
+ #interfaceLocked = false;
821
830
  input(schema) {
831
+ if (this.#interfaceLocked) {
832
+ throw new SlateDeclarationError("Adapter contract input schema cannot be changed");
833
+ }
822
834
  this.#inputSchema = schema;
823
835
  return this;
824
836
  }
825
837
  output(schema) {
838
+ if (this.#interfaceLocked) {
839
+ throw new SlateDeclarationError("Adapter contract output schema cannot be changed");
840
+ }
826
841
  this.#outputSchema = schema;
827
842
  return this;
828
843
  }
844
+ lockInterface() {
845
+ this.#interfaceLocked = true;
846
+ return this;
847
+ }
829
848
  scopes(scopes) {
830
849
  validateScopes(scopes);
831
850
  this.#scopes = scopes;
832
851
  return this;
833
852
  }
834
853
  authMethods(authMethods) {
854
+ if (this.isPublicTool) {
855
+ throw new SlateDeclarationError("Public tools cannot require authentication methods");
856
+ }
835
857
  this.#authMethods = this.validateAuthMethods(authMethods);
836
858
  return this;
837
859
  }
@@ -887,6 +909,9 @@ var SlateActionBuilder = class {
887
909
  validateScopes(scopes);
888
910
  }
889
911
  let authMethods = this.validateAuthMethods(this.#authMethods ?? this.params.authMethods);
912
+ if (this.isPublicTool && authMethods) {
913
+ throw new SlateDeclarationError("Public tools cannot require authentication methods");
914
+ }
890
915
  if (!this.#inputSchema) {
891
916
  throw new SlateDeclarationError("Input schema is not defined");
892
917
  }
@@ -903,6 +928,7 @@ var SlateActionBuilder = class {
903
928
  ...this.params,
904
929
  scopes,
905
930
  authMethods,
931
+ isPublic: this.isPublicTool === true,
906
932
  configSchema: this.#configSchema,
907
933
  authSchema: this.#authSchema,
908
934
  inputSchema: this.#inputSchema,
@@ -913,6 +939,9 @@ var SlateActionBuilder = class {
913
939
  }
914
940
  };
915
941
 
942
+ // src/action/getFileUrlTool.ts
943
+ import { z } from "zod";
944
+
916
945
  // src/action/tool.ts
917
946
  var SlateTool = class _SlateTool extends SlateAction {
918
947
  #handleInvocation;
@@ -922,16 +951,61 @@ var SlateTool = class _SlateTool extends SlateAction {
922
951
  }
923
952
  static create(spec2, params) {
924
953
  return new SlateActionBuilder("tool", spec2, params, (params2) => {
925
- if (params2.type !== "tool") throw new Error("Invalid action type for tool");
926
- return new _SlateTool(spec2, params2.inputSchema, params2.outputSchema, params2);
954
+ return _SlateTool.fromCreateParameters(spec2, params2);
927
955
  });
928
956
  }
957
+ static fromCreateParameters(spec2, params) {
958
+ if (params.type !== "tool") throw new Error("Invalid action type for tool");
959
+ return new _SlateTool(spec2, params.inputSchema, params.outputSchema, params);
960
+ }
929
961
  get handleInvocation() {
930
962
  return this.#handleInvocation;
931
963
  }
932
964
  };
933
965
  var tool = (spec2, params) => SlateTool.create(spec2, params);
934
966
 
967
+ // src/action/getFileUrlTool.ts
968
+ var GET_FILE_URL_TOOL_ID = "metorial$getFileUrl";
969
+ var getFileUrlToolInputSchema = z.object({
970
+ url: z.string(),
971
+ reference: z.any().nullable()
972
+ });
973
+ var getFileUrlToolOutputSchema = z.object({
974
+ url: z.string(),
975
+ expiresAt: z.string(),
976
+ headers: z.record(z.string(), z.string()).optional(),
977
+ query: z.record(z.string(), z.string()).optional()
978
+ });
979
+ var getFileUrlTool = (spec2, handleGetFileUrl) => tool(spec2, {
980
+ key: GET_FILE_URL_TOOL_ID,
981
+ name: "Get File URL",
982
+ description: "Reserved: refreshes an expiring attachment download URL. Hub-internal only."
983
+ }).input(getFileUrlToolInputSchema).output(getFileUrlToolOutputSchema).handleInvocation(async (ctx) => ({
984
+ output: await handleGetFileUrl(ctx),
985
+ message: "ok"
986
+ })).build();
987
+
988
+ // src/action/publicTool.ts
989
+ var SlatePublicTool = class {
990
+ constructor() {
991
+ }
992
+ static create(spec2, params) {
993
+ return new SlateActionBuilder(
994
+ "tool",
995
+ spec2,
996
+ params,
997
+ (params2) => {
998
+ return SlateTool.fromCreateParameters(spec2, {
999
+ ...params2,
1000
+ isPublic: true
1001
+ });
1002
+ },
1003
+ true
1004
+ );
1005
+ }
1006
+ };
1007
+ var publicTool = (spec2, params) => SlatePublicTool.create(spec2, params);
1008
+
935
1009
  // src/action/trigger.ts
936
1010
  var SlateDefaultPollingIntervalSeconds = 60 * 10;
937
1011
  var SlateTrigger = class _SlateTrigger extends SlateAction {
@@ -990,23 +1064,35 @@ var SlateTrigger = class _SlateTrigger extends SlateAction {
990
1064
  var trigger = (spec2, params) => SlateTrigger.create(spec2, params);
991
1065
 
992
1066
  // src/action/attachment.ts
993
- var createUrlAttachment = (url, mimeType) => ({
994
- mimeType,
995
- content: {
996
- type: "url",
997
- url
1067
+ var createUrlAttachment = (url, opts = {}) => {
1068
+ if (opts.refreshReference !== void 0 && !opts.refreshAt) {
1069
+ throw new Error("createUrlAttachment: refreshAt is required when refreshReference is set");
998
1070
  }
999
- });
1000
- var createBase64Attachment = (content, mimeType) => ({
1071
+ return {
1072
+ mimeType: opts.mimeType,
1073
+ attachmentHash: opts.attachmentHash,
1074
+ content: {
1075
+ type: "url",
1076
+ url,
1077
+ ...opts.headers ? { headers: opts.headers } : {},
1078
+ ...opts.query ? { query: opts.query } : {},
1079
+ ...opts.refreshReference !== void 0 ? { refreshReference: opts.refreshReference } : {},
1080
+ ...opts.refreshAt ? { refreshAt: opts.refreshAt } : {}
1081
+ }
1082
+ };
1083
+ };
1084
+ var createBase64Attachment = (content, mimeType, attachmentHash) => ({
1001
1085
  mimeType,
1086
+ attachmentHash,
1002
1087
  content: {
1003
1088
  type: "content",
1004
1089
  encoding: "base64",
1005
1090
  content
1006
1091
  }
1007
1092
  });
1008
- var createTextAttachment = (content, mimeType) => ({
1093
+ var createTextAttachment = (content, mimeType, attachmentHash) => ({
1009
1094
  mimeType,
1095
+ attachmentHash,
1010
1096
  content: {
1011
1097
  type: "content",
1012
1098
  encoding: "utf-8",
@@ -1014,6 +1100,173 @@ var createTextAttachment = (content, mimeType) => ({
1014
1100
  }
1015
1101
  });
1016
1102
 
1103
+ // src/action/directUpload.ts
1104
+ var uploadAttachmentDirect = async (d) => {
1105
+ let baseUrl = d.live.baseUrl.replace(/\/$/, "");
1106
+ let registerRes = await fetch(`${baseUrl}/slates-hub/live-invocation/attachments`, {
1107
+ method: "POST",
1108
+ headers: {
1109
+ "content-type": "application/json",
1110
+ authorization: `Bearer ${d.live.token}`
1111
+ },
1112
+ body: JSON.stringify({
1113
+ attachments: [{ mimeType: d.mimeType, filename: d.filename }]
1114
+ })
1115
+ });
1116
+ if (!registerRes.ok) {
1117
+ throw new Error(
1118
+ `Failed to register attachment for direct upload (status ${registerRes.status})`
1119
+ );
1120
+ }
1121
+ let { attachments } = await registerRes.json();
1122
+ let registration = attachments[0];
1123
+ if (!registration) {
1124
+ throw new Error("Live invocation API returned no attachment registrations");
1125
+ }
1126
+ let putRes = await fetch(registration.uploadUrl, {
1127
+ method: "PUT",
1128
+ ...d.body instanceof Uint8Array ? {} : { duplex: "half" },
1129
+ headers: d.mimeType ? { "content-type": d.mimeType } : void 0,
1130
+ body: d.body
1131
+ });
1132
+ if (!putRes.ok) {
1133
+ throw new Error(`Failed to upload attachment content (status ${putRes.status})`);
1134
+ }
1135
+ return {
1136
+ mimeType: d.mimeType,
1137
+ content: { type: "upload_reference", referenceId: registration.referenceId }
1138
+ };
1139
+ };
1140
+
1141
+ // src/adapter/adapter.ts
1142
+ var SlateAdapter = class {
1143
+ constructor(_spec, _tools, _triggers) {
1144
+ this._spec = _spec;
1145
+ this._tools = _tools;
1146
+ this._triggers = _triggers;
1147
+ }
1148
+ _spec;
1149
+ _tools;
1150
+ _triggers;
1151
+ get spec() {
1152
+ return this._spec;
1153
+ }
1154
+ get id() {
1155
+ return this._spec.id;
1156
+ }
1157
+ get name() {
1158
+ return this._spec.name;
1159
+ }
1160
+ get capabilities() {
1161
+ return this._spec.capabilities;
1162
+ }
1163
+ get tools() {
1164
+ return this._tools;
1165
+ }
1166
+ get triggers() {
1167
+ return this._triggers;
1168
+ }
1169
+ get actions() {
1170
+ return [...this._triggers, ...this._tools];
1171
+ }
1172
+ };
1173
+
1174
+ // src/adapter/spec.ts
1175
+ var SlateAdapterSpec = class _SlateAdapterSpec {
1176
+ constructor(_params) {
1177
+ this._params = _params;
1178
+ }
1179
+ _params;
1180
+ static create(params) {
1181
+ let id = params.id?.trim();
1182
+ let name = params.name?.trim();
1183
+ if (!id) {
1184
+ throw new SlateDeclarationError("Adapter id must be a non-empty string");
1185
+ }
1186
+ if (!name) {
1187
+ throw new SlateDeclarationError("Adapter name must be a non-empty string");
1188
+ }
1189
+ return new _SlateAdapterSpec({
1190
+ id,
1191
+ name,
1192
+ capabilities: validateCapabilities(params.capabilities ?? [])
1193
+ });
1194
+ }
1195
+ get id() {
1196
+ return this._params.id;
1197
+ }
1198
+ get name() {
1199
+ return this._params.name;
1200
+ }
1201
+ get capabilities() {
1202
+ return this._params.capabilities;
1203
+ }
1204
+ get parameters() {
1205
+ return this._params;
1206
+ }
1207
+ tool(spec2, params) {
1208
+ return SlateTool.create(spec2, this.linkAction(params));
1209
+ }
1210
+ publicTool(spec2, params) {
1211
+ return SlatePublicTool.create(spec2, this.linkAction(params));
1212
+ }
1213
+ trigger(spec2, params) {
1214
+ return SlateTrigger.create(spec2, this.linkAction(params));
1215
+ }
1216
+ register(params) {
1217
+ let actions = [...params.tools, ...params.triggers];
1218
+ let seenKeys = /* @__PURE__ */ new Set();
1219
+ for (let action of actions) {
1220
+ if (action.adapter && action.adapter !== this.id) {
1221
+ throw new SlateDeclarationError(
1222
+ `Action "${action.key}" is linked to adapter "${action.adapter}", not "${this.id}"`
1223
+ );
1224
+ }
1225
+ if (!action.adapter) {
1226
+ throw new SlateDeclarationError(
1227
+ `Action "${action.key}" must be created with this adapter's tool(), publicTool(), or trigger() helpers`
1228
+ );
1229
+ }
1230
+ if (seenKeys.has(action.key)) {
1231
+ throw new SlateDeclarationError(
1232
+ `Adapter "${this.id}" already has an action with key "${action.key}"`
1233
+ );
1234
+ }
1235
+ seenKeys.add(action.key);
1236
+ }
1237
+ return new SlateAdapter(this, params.tools, params.triggers);
1238
+ }
1239
+ linkAction(params) {
1240
+ if (params.adapter != null && params.adapter !== this.id) {
1241
+ throw new SlateDeclarationError(
1242
+ `Action "${params.key}" cannot be linked to adapter "${this.id}" because it is already linked to "${params.adapter}"`
1243
+ );
1244
+ }
1245
+ return {
1246
+ ...params,
1247
+ adapter: this.id
1248
+ };
1249
+ }
1250
+ };
1251
+ var adapterSpec = (params) => SlateAdapterSpec.create(params);
1252
+ var validateCapabilities = (capabilities) => {
1253
+ let seenIds = /* @__PURE__ */ new Set();
1254
+ return capabilities.map((capability) => {
1255
+ let id = capability.id?.trim();
1256
+ if (!id) {
1257
+ throw new SlateDeclarationError("Adapter capability ids must be non-empty strings");
1258
+ }
1259
+ if (seenIds.has(id)) {
1260
+ throw new SlateDeclarationError(`Adapter capability "${id}" is defined more than once`);
1261
+ }
1262
+ seenIds.add(id);
1263
+ return {
1264
+ id,
1265
+ value: capability.value
1266
+ };
1267
+ });
1268
+ };
1269
+
1017
1270
  // src/auth/auth.ts
1018
1271
  var SlateAuth = class _SlateAuth {
1019
1272
  #outputSchema = null;
@@ -1155,6 +1408,76 @@ var normalizeOAuthTokenResponse = (data, options = {}) => {
1155
1408
  };
1156
1409
  };
1157
1410
 
1411
+ // src/auth/redact.ts
1412
+ var AUTH_CONFIG_SECRET_PLACEHOLDER_PREFIX = "$$MT$secret$authConfig$";
1413
+ var AuthConfigSecretRedactor = class {
1414
+ secretToPlaceholder = /* @__PURE__ */ new Map();
1415
+ constructor(authConfig) {
1416
+ if (authConfig) this.buildSecretMap(authConfig);
1417
+ }
1418
+ redact(value) {
1419
+ return this.transform(value, (stringValue) => {
1420
+ return this.secretToPlaceholder.get(stringValue) ?? stringValue;
1421
+ });
1422
+ }
1423
+ buildSecretMap(value, path = []) {
1424
+ if (typeof value === "string") {
1425
+ if (value.length === 0) return;
1426
+ this.secretToPlaceholder.set(
1427
+ value,
1428
+ `${AUTH_CONFIG_SECRET_PLACEHOLDER_PREFIX}${path.join(".")}`
1429
+ );
1430
+ return;
1431
+ }
1432
+ if (Array.isArray(value)) {
1433
+ value.forEach((item, index) => {
1434
+ this.buildSecretMap(item, [...path, String(index)]);
1435
+ });
1436
+ return;
1437
+ }
1438
+ if (this.isPlainObject(value)) {
1439
+ Object.entries(value).forEach(([key, nestedValue]) => {
1440
+ this.buildSecretMap(nestedValue, [...path, key]);
1441
+ });
1442
+ }
1443
+ }
1444
+ transform(value, transformString) {
1445
+ if (typeof value === "string") {
1446
+ return transformString(value);
1447
+ }
1448
+ if (Array.isArray(value)) {
1449
+ return value.map((item) => this.transform(item, transformString));
1450
+ }
1451
+ if (this.isPlainObject(value)) {
1452
+ let result = {};
1453
+ Object.entries(value).forEach(([key, nestedValue]) => {
1454
+ result[key] = this.transform(nestedValue, transformString);
1455
+ });
1456
+ return result;
1457
+ }
1458
+ return value;
1459
+ }
1460
+ isPlainObject(value) {
1461
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1462
+ }
1463
+ };
1464
+ var redactUrlAttachmentSecrets = (attachments, authConfig) => {
1465
+ if (!authConfig || !attachments) return attachments;
1466
+ let redactor = new AuthConfigSecretRedactor(authConfig);
1467
+ return attachments.map((attachment) => {
1468
+ if (attachment.content.type !== "url") return attachment;
1469
+ if (!attachment.content.headers && !attachment.content.query) return attachment;
1470
+ return {
1471
+ ...attachment,
1472
+ content: {
1473
+ ...attachment.content,
1474
+ headers: attachment.content.headers ? redactor.redact(attachment.content.headers) : void 0,
1475
+ query: attachment.content.query ? redactor.redact(attachment.content.query) : void 0
1476
+ }
1477
+ };
1478
+ });
1479
+ };
1480
+
1158
1481
  // src/axios/index.ts
1159
1482
  import baseAxios, { getAdapter, isAxiosError as isAxiosError2 } from "axios";
1160
1483
 
@@ -1433,9 +1756,9 @@ var truncateText = (text) => {
1433
1756
  truncated: true
1434
1757
  };
1435
1758
  };
1436
- var sanitizeFreeText = (text) => {
1759
+ var sanitizeFreeText = (text, redactor) => {
1437
1760
  if (!text) return text;
1438
- let result = text;
1761
+ let result = redactor ? redactor.redact(text) : text;
1439
1762
  result = result.replace(
1440
1763
  HTTP_AUTH_SCHEME_PATTERN,
1441
1764
  (_match, scheme) => `${scheme} ${REDACTED_VALUE}`
@@ -1453,21 +1776,21 @@ var sanitizeFreeText = (text) => {
1453
1776
  );
1454
1777
  return result;
1455
1778
  };
1456
- var sanitizeScalar = (value) => truncateText(sanitizeFreeText(value));
1457
- var redactStructuredValue = (value, depth = 0) => {
1779
+ var sanitizeScalar = (value, redactor) => truncateText(sanitizeFreeText(value, redactor));
1780
+ var redactStructuredValue = (value, depth = 0, redactor) => {
1458
1781
  if (value == null) return value;
1459
1782
  if (depth >= STRUCTURED_DEPTH_LIMIT) return "[truncated]";
1460
1783
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
1461
- return typeof value === "string" ? sanitizeFreeText(value) : value;
1784
+ return typeof value === "string" ? sanitizeFreeText(value, redactor) : value;
1462
1785
  }
1463
1786
  if (Array.isArray(value)) {
1464
- return value.slice(0, STRUCTURED_ENTRY_LIMIT).map((entry) => redactStructuredValue(entry, depth + 1));
1787
+ return value.slice(0, STRUCTURED_ENTRY_LIMIT).map((entry) => redactStructuredValue(entry, depth + 1, redactor));
1465
1788
  }
1466
1789
  if (isRecord2(value)) {
1467
1790
  return Object.fromEntries(
1468
1791
  Object.entries(value).slice(0, STRUCTURED_ENTRY_LIMIT).map(([key, entry]) => [
1469
1792
  key,
1470
- isSecretKeyName(key) ? REDACTED_VALUE : redactStructuredValue(entry, depth + 1)
1793
+ isSecretKeyName(key) ? REDACTED_VALUE : redactStructuredValue(entry, depth + 1, redactor)
1471
1794
  ])
1472
1795
  );
1473
1796
  }
@@ -1485,11 +1808,11 @@ var maybeParseJson = (text, contentType) => {
1485
1808
  return null;
1486
1809
  }
1487
1810
  };
1488
- var sanitizeTextBody = (text, contentType) => {
1811
+ var sanitizeTextBody = (text, contentType, redactor) => {
1489
1812
  if (!text) return void 0;
1490
1813
  let normalizedContentType = normalizeContentType(contentType);
1491
1814
  let parsedJson = maybeParseJson(text, normalizedContentType);
1492
- let normalizedText = parsedJson !== null ? JSON.stringify(redactStructuredValue(parsedJson)) : sanitizeFreeText(text);
1815
+ let normalizedText = parsedJson !== null ? JSON.stringify(redactStructuredValue(parsedJson, 0, redactor)) : sanitizeFreeText(text, redactor);
1493
1816
  let { text: truncatedText, truncated } = truncateText(normalizedText);
1494
1817
  return {
1495
1818
  ...normalizedContentType ? { contentType: normalizedContentType } : {},
@@ -1505,22 +1828,26 @@ var isBinaryLike = (value) => {
1505
1828
  if (typeof FormData !== "undefined" && value instanceof FormData) return true;
1506
1829
  return false;
1507
1830
  };
1508
- var serializeTextBody = (value, contentType) => {
1831
+ var serializeTextBody = (value, contentType, redactor) => {
1509
1832
  let normalizedContentType = normalizeContentType(contentType);
1510
1833
  if (value == null || isBinaryLike(value) || !isTextContentType(normalizedContentType)) {
1511
1834
  return void 0;
1512
1835
  }
1513
1836
  if (typeof value === "string") {
1514
- return sanitizeTextBody(value, normalizedContentType);
1837
+ return sanitizeTextBody(value, normalizedContentType, redactor);
1515
1838
  }
1516
1839
  if (value instanceof URLSearchParams) {
1517
- return sanitizeTextBody(value.toString(), normalizedContentType);
1840
+ return sanitizeTextBody(value.toString(), normalizedContentType, redactor);
1518
1841
  }
1519
1842
  if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
1520
- return sanitizeTextBody(String(value), normalizedContentType);
1843
+ return sanitizeTextBody(String(value), normalizedContentType, redactor);
1521
1844
  }
1522
1845
  if (Array.isArray(value) || isRecord2(value)) {
1523
- return sanitizeTextBody(JSON.stringify(redactStructuredValue(value)), "application/json");
1846
+ return sanitizeTextBody(
1847
+ JSON.stringify(redactStructuredValue(value, 0, redactor)),
1848
+ "application/json",
1849
+ redactor
1850
+ );
1524
1851
  }
1525
1852
  return void 0;
1526
1853
  };
@@ -1535,16 +1862,20 @@ var buildUrl = (baseURL, url) => {
1535
1862
  };
1536
1863
  var REDACTED_ENCODED = encodeURIComponent(REDACTED_VALUE);
1537
1864
  var decodeRedactedMarker = (value) => REDACTED_ENCODED === REDACTED_VALUE ? value : value.split(REDACTED_ENCODED).join(REDACTED_VALUE);
1538
- var redactHashFragment = (hash) => {
1865
+ var redactHashFragment = (hash, redactor) => {
1539
1866
  if (!hash?.includes("=")) return hash;
1540
1867
  let withoutHash = hash.startsWith("#") ? hash.slice(1) : hash;
1541
1868
  let redacted = withoutHash.replace(
1542
1869
  /(?<![A-Za-z0-9])([A-Za-z0-9][A-Za-z0-9_\-.]*)=([^&]*)/g,
1543
- (match, key, _rawValue) => isUrlFormSecretKeyName(key) ? `${key}=${REDACTED_VALUE}` : match
1870
+ (match, key, rawValue) => {
1871
+ if (isUrlFormSecretKeyName(key)) return `${key}=${REDACTED_VALUE}`;
1872
+ let literalRedacted = redactor ? redactor.redact(rawValue) : rawValue;
1873
+ return literalRedacted !== rawValue ? `${key}=${literalRedacted}` : match;
1874
+ }
1544
1875
  );
1545
1876
  return redacted ? `#${redacted}` : "";
1546
1877
  };
1547
- var sanitizeUrl = (value) => {
1878
+ var sanitizeUrl = (value, redactor) => {
1548
1879
  if (!value) return value;
1549
1880
  try {
1550
1881
  let url = new URL(value);
@@ -1555,12 +1886,17 @@ var sanitizeUrl = (value) => {
1555
1886
  for (let key of Array.from(url.searchParams.keys())) {
1556
1887
  if (isUrlFormSecretKeyName(key)) {
1557
1888
  url.searchParams.set(key, REDACTED_VALUE);
1889
+ continue;
1558
1890
  }
1891
+ let rawValue = url.searchParams.get(key) ?? "";
1892
+ let literalRedacted = redactor ? redactor.redact(rawValue) : rawValue;
1893
+ if (literalRedacted !== rawValue) url.searchParams.set(key, literalRedacted);
1559
1894
  }
1560
- url.hash = redactHashFragment(url.hash);
1895
+ url.hash = redactHashFragment(url.hash, redactor);
1561
1896
  return decodeRedactedMarker(url.toString());
1562
1897
  } catch {
1563
- return value.replace(
1898
+ let text = redactor ? redactor.redact(value) : value;
1899
+ return text.replace(
1564
1900
  /([?&]([^=&#]+)=)([^&#]+)/g,
1565
1901
  (_match, prefix, key, rawValue) => isUrlFormSecretKeyName(key) ? `${prefix}${REDACTED_VALUE}` : `${prefix}${rawValue}`
1566
1902
  );
@@ -1599,8 +1935,10 @@ var headerEntries = (headers) => {
1599
1935
  }
1600
1936
  return [];
1601
1937
  };
1602
- var sanitizeHeaders = (headers) => {
1603
- let entries = headerEntries(headers).map(([key, value]) => [key.toLowerCase(), sanitizeScalar(value).text]).filter(([key]) => isSafeHeaderName(key));
1938
+ var sanitizeHeaders = (headers, redactor) => {
1939
+ let entries = headerEntries(headers).map(
1940
+ ([key, value]) => [key.toLowerCase(), sanitizeScalar(value, redactor).text]
1941
+ ).filter(([key]) => isSafeHeaderName(key));
1604
1942
  if (entries.length === 0) return void 0;
1605
1943
  return Object.fromEntries(entries);
1606
1944
  };
@@ -1626,15 +1964,16 @@ var clearTraceDraft = (config2, draft) => {
1626
1964
  var attachHttpTraceDraft = (config2, context) => {
1627
1965
  let traceAwareConfig = config2;
1628
1966
  let contentType = getContentType(config2.headers);
1967
+ let redactor = new AuthConfigSecretRedactor(context._getAuthConfigForRedaction());
1629
1968
  traceAwareConfig.__slatesHttpTraceDraft = {
1630
1969
  context,
1631
1970
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
1632
1971
  startedAtMs: Date.now(),
1633
1972
  request: {
1634
1973
  method: (config2.method ?? "GET").toUpperCase(),
1635
- url: sanitizeUrl(buildUrl(config2.baseURL, config2.url)),
1636
- ...sanitizeHeaders(config2.headers) ? { headers: sanitizeHeaders(config2.headers) } : {},
1637
- ...serializeTextBody(config2.data, contentType) ? { body: serializeTextBody(config2.data, contentType) } : {}
1974
+ url: sanitizeUrl(buildUrl(config2.baseURL, config2.url), redactor),
1975
+ ...sanitizeHeaders(config2.headers, redactor) ? { headers: sanitizeHeaders(config2.headers, redactor) } : {},
1976
+ ...serializeTextBody(config2.data, contentType, redactor) ? { body: serializeTextBody(config2.data, contentType, redactor) } : {}
1638
1977
  }
1639
1978
  };
1640
1979
  if (isObjectLike(traceAwareConfig)) {
@@ -1653,6 +1992,7 @@ var recordHttpTraceFromResponse = (response) => {
1653
1992
  let draft = takeTraceDraft(config2);
1654
1993
  if (!draft) return response;
1655
1994
  clearTraceDraft(config2, draft);
1995
+ let redactor = new AuthConfigSecretRedactor(draft.context._getAuthConfigForRedaction());
1656
1996
  let contentType = getContentType(response.headers);
1657
1997
  draft.context.recordHttpTrace({
1658
1998
  startedAt: draft.startedAt,
@@ -1661,8 +2001,8 @@ var recordHttpTraceFromResponse = (response) => {
1661
2001
  response: {
1662
2002
  status: response.status,
1663
2003
  ...response.statusText ? { statusText: response.statusText } : {},
1664
- ...sanitizeHeaders(response.headers) ? { headers: sanitizeHeaders(response.headers) } : {},
1665
- ...serializeTextBody(response.data, contentType) ? { body: serializeTextBody(response.data, contentType) } : {}
2004
+ ...sanitizeHeaders(response.headers, redactor) ? { headers: sanitizeHeaders(response.headers, redactor) } : {},
2005
+ ...serializeTextBody(response.data, contentType, redactor) ? { body: serializeTextBody(response.data, contentType, redactor) } : {}
1666
2006
  }
1667
2007
  });
1668
2008
  return response;
@@ -1672,6 +2012,7 @@ var recordHttpTraceFromError = (error) => {
1672
2012
  let draft = takeTraceDraft(config2);
1673
2013
  if (!draft) return;
1674
2014
  clearTraceDraft(config2, draft);
2015
+ let redactor = new AuthConfigSecretRedactor(draft.context._getAuthConfigForRedaction());
1675
2016
  let responseContentType = getContentType(error.response?.headers);
1676
2017
  draft.context.recordHttpTrace({
1677
2018
  startedAt: draft.startedAt,
@@ -1681,8 +2022,8 @@ var recordHttpTraceFromError = (error) => {
1681
2022
  response: {
1682
2023
  status: error.response.status,
1683
2024
  ...error.response.statusText ? { statusText: error.response.statusText } : {},
1684
- ...sanitizeHeaders(error.response.headers) ? { headers: sanitizeHeaders(error.response.headers) } : {},
1685
- ...serializeTextBody(error.response.data, responseContentType) ? { body: serializeTextBody(error.response.data, responseContentType) } : {}
2025
+ ...sanitizeHeaders(error.response.headers, redactor) ? { headers: sanitizeHeaders(error.response.headers, redactor) } : {},
2026
+ ...serializeTextBody(error.response.data, responseContentType, redactor) ? { body: serializeTextBody(error.response.data, responseContentType, redactor) } : {}
1686
2027
  }
1687
2028
  } : {},
1688
2029
  error: {
@@ -1813,26 +2154,42 @@ var SlateConfig = class _SlateConfig {
1813
2154
  var config = (schema) => SlateConfig.create(schema);
1814
2155
 
1815
2156
  // src/context/context.ts
1816
- var SlateContext = class {
1817
- constructor(config2, input, auth2, spec2, logger) {
2157
+ import { Readable } from "stream";
2158
+ import PQueue from "p-queue";
2159
+ var isBufferLike = (value) => value instanceof Uint8Array || value instanceof ArrayBuffer;
2160
+ var toUint8Array = (value) => value instanceof ArrayBuffer ? new Uint8Array(value) : value;
2161
+ var isReadableStream = (value) => typeof ReadableStream !== "undefined" && value instanceof ReadableStream;
2162
+ var isNodeReadable = (value) => !!value && typeof value === "object" && typeof value.pipe === "function" && typeof value.on === "function";
2163
+ var isResponseLike = (value) => typeof Response !== "undefined" && value instanceof Response;
2164
+ var readStreamToBuffer = async (stream) => {
2165
+ let reader = stream.getReader();
2166
+ let chunks = [];
2167
+ while (true) {
2168
+ let { done, value } = await reader.read();
2169
+ if (done) break;
2170
+ if (value) chunks.push(value);
2171
+ }
2172
+ return Buffer.concat(chunks);
2173
+ };
2174
+ var SlatePublicContext = class {
2175
+ constructor(input, spec2, logger, liveInvocation = null) {
1818
2176
  this.spec = spec2;
1819
2177
  this.logger = logger;
1820
- this.#config = config2;
1821
2178
  this.#input = input;
1822
- this.#auth = auth2;
2179
+ this.#liveInvocation = liveInvocation;
1823
2180
  }
1824
2181
  spec;
1825
2182
  logger;
1826
- #config;
1827
2183
  #input;
1828
- #auth;
1829
2184
  #httpTraces = [];
2185
+ #liveInvocation;
2186
+ #attachmentsDisabled = false;
2187
+ #attachments = [];
2188
+ #pendingUploads = [];
2189
+ #uploadQueue = new PQueue({ concurrency: 10 });
1830
2190
  get specification() {
1831
2191
  return this.spec;
1832
2192
  }
1833
- get config() {
1834
- return Object.freeze(this.#config);
1835
- }
1836
2193
  get input() {
1837
2194
  return Object.freeze(this.#input);
1838
2195
  }
@@ -1850,9 +2207,6 @@ var SlateContext = class {
1850
2207
  this.#input.registrationDetails
1851
2208
  );
1852
2209
  }
1853
- get auth() {
1854
- return Object.freeze(this.#auth);
1855
- }
1856
2210
  recordHttpTrace(trace) {
1857
2211
  this.#httpTraces.push(trace);
1858
2212
  }
@@ -1886,6 +2240,107 @@ var SlateContext = class {
1886
2240
  progress(message) {
1887
2241
  this.logger.progress(message);
1888
2242
  }
2243
+ async addAttachment(input) {
2244
+ if (input.type === "url") {
2245
+ this.#attachments.push(
2246
+ createUrlAttachment(input.url.toString(), {
2247
+ mimeType: input.mimeType,
2248
+ headers: input.headers,
2249
+ query: input.query,
2250
+ refreshReference: input.refreshReference,
2251
+ refreshAt: input.refreshAt
2252
+ })
2253
+ );
2254
+ return;
2255
+ }
2256
+ let content = input.content;
2257
+ if (isBufferLike(content)) {
2258
+ let bytes = toUint8Array(content);
2259
+ this.#attachments.push(
2260
+ createBase64Attachment(Buffer.from(bytes).toString("base64"), input.mimeType)
2261
+ );
2262
+ return;
2263
+ }
2264
+ let normalized = await this.#normalizeStreamInput(content, {
2265
+ mimeType: input.mimeType,
2266
+ filename: input.filename
2267
+ });
2268
+ if (!normalized) return;
2269
+ if (!this.#liveInvocation || this.#attachmentsDisabled) {
2270
+ let buffer = await readStreamToBuffer(normalized.stream);
2271
+ this.#attachments.push(
2272
+ createBase64Attachment(buffer.toString("base64"), normalized.mimeType)
2273
+ );
2274
+ return;
2275
+ }
2276
+ let live = this.#liveInvocation;
2277
+ let task = this.#uploadQueue.add(
2278
+ () => uploadAttachmentDirect({
2279
+ live,
2280
+ mimeType: normalized.mimeType,
2281
+ filename: normalized.filename,
2282
+ body: normalized.stream
2283
+ })
2284
+ ).then((attachment) => {
2285
+ this.#attachments.push(attachment);
2286
+ }).catch((err) => {
2287
+ this.#attachmentsDisabled = true;
2288
+ this.warn({
2289
+ message: `Attachment dropped: direct upload failed (${err instanceof Error ? err.message : String(err)}).`
2290
+ });
2291
+ });
2292
+ this.#pendingUploads.push(task);
2293
+ }
2294
+ async #normalizeStreamInput(input, opts) {
2295
+ if (isResponseLike(input)) {
2296
+ if (!input.body) {
2297
+ this.warn({ message: "Attachment dropped: response has no body." });
2298
+ return null;
2299
+ }
2300
+ return {
2301
+ stream: input.body,
2302
+ mimeType: opts.mimeType ?? input.headers.get("content-type") ?? void 0,
2303
+ filename: opts.filename
2304
+ };
2305
+ }
2306
+ if (isReadableStream(input)) {
2307
+ return { stream: input, mimeType: opts.mimeType, filename: opts.filename };
2308
+ }
2309
+ if (isNodeReadable(input)) {
2310
+ return {
2311
+ stream: Readable.toWeb(input),
2312
+ mimeType: opts.mimeType,
2313
+ filename: opts.filename
2314
+ };
2315
+ }
2316
+ this.warn({ message: "Attachment dropped: unsupported attachment input type." });
2317
+ return null;
2318
+ }
2319
+ async _finalizeAttachments() {
2320
+ await Promise.allSettled(this.#pendingUploads);
2321
+ return this.#attachments;
2322
+ }
2323
+ _getAuthConfigForRedaction() {
2324
+ return void 0;
2325
+ }
2326
+ };
2327
+ var SlateContext = class extends SlatePublicContext {
2328
+ #config;
2329
+ #auth;
2330
+ constructor(config2, input, auth2, spec2, logger, liveInvocation = null) {
2331
+ super(input, spec2, logger, liveInvocation);
2332
+ this.#config = config2;
2333
+ this.#auth = auth2;
2334
+ }
2335
+ get config() {
2336
+ return Object.freeze(this.#config);
2337
+ }
2338
+ get auth() {
2339
+ return Object.freeze(this.#auth);
2340
+ }
2341
+ _getAuthConfigForRedaction() {
2342
+ return this.#auth;
2343
+ }
1889
2344
  };
1890
2345
 
1891
2346
  // src/crypto/hmac.ts
@@ -2035,14 +2490,40 @@ var setIfDefined = (target, key, value) => {
2035
2490
 
2036
2491
  // src/specification/slate.ts
2037
2492
  var Slate = class _Slate {
2038
- constructor(_spec, _actions) {
2493
+ constructor(_spec, _actions, _adapters) {
2039
2494
  this._spec = _spec;
2040
2495
  this._actions = _actions;
2496
+ this._adapters = _adapters;
2041
2497
  }
2042
2498
  _spec;
2043
2499
  _actions;
2500
+ _adapters;
2044
2501
  static create(params) {
2045
- return new _Slate(params.spec, [...params.triggers, ...params.tools]);
2502
+ let adapters = params.adapters ?? [];
2503
+ let actions = [...params.triggers, ...params.tools];
2504
+ let seenAdapterIds = /* @__PURE__ */ new Set();
2505
+ let seenActionKeys = new Map(actions.map((action) => [action.key, action]));
2506
+ for (let adapter of adapters) {
2507
+ if (seenAdapterIds.has(adapter.id)) {
2508
+ throw new SlateDeclarationError(
2509
+ `Adapter "${adapter.id}" is registered more than once`
2510
+ );
2511
+ }
2512
+ seenAdapterIds.add(adapter.id);
2513
+ for (let action of adapter.actions) {
2514
+ let existing = seenActionKeys.get(action.key);
2515
+ if (existing && existing !== action) {
2516
+ throw new SlateDeclarationError(
2517
+ `Adapter "${adapter.id}" action "${action.key}" conflicts with another action`
2518
+ );
2519
+ }
2520
+ if (!existing) {
2521
+ actions.push(action);
2522
+ seenActionKeys.set(action.key, action);
2523
+ }
2524
+ }
2525
+ }
2526
+ return new _Slate(params.spec, actions, adapters);
2046
2527
  }
2047
2528
  get spec() {
2048
2529
  return this._spec;
@@ -2050,10 +2531,13 @@ var Slate = class _Slate {
2050
2531
  get actions() {
2051
2532
  return this._actions;
2052
2533
  }
2534
+ get adapters() {
2535
+ return this._adapters;
2536
+ }
2053
2537
  };
2054
2538
 
2055
2539
  // src/specification/specification.ts
2056
- import z from "zod";
2540
+ import z2 from "zod";
2057
2541
  var SlateSpecification = class _SlateSpecification {
2058
2542
  constructor(_config, _auth, _params) {
2059
2543
  this._config = _config;
@@ -2070,7 +2554,7 @@ var SlateSpecification = class _SlateSpecification {
2070
2554
  return this._config.configSchema;
2071
2555
  }
2072
2556
  get authSchema() {
2073
- return "outputSchema" in this._auth ? this._auth.outputSchema : z.object({});
2557
+ return "outputSchema" in this._auth ? this._auth.outputSchema : z2.object({});
2074
2558
  }
2075
2559
  get config() {
2076
2560
  return this._config;
@@ -2149,8 +2633,14 @@ var getMetaWebhookVerificationResponse = (request, expectedVerifyToken) => {
2149
2633
  };
2150
2634
  };
2151
2635
  export {
2636
+ AUTH_CONFIG_SECRET_PLACEHOLDER_PREFIX,
2637
+ AuthConfigSecretRedactor,
2638
+ GET_FILE_URL_TOOL_ID,
2152
2639
  Slate,
2153
2640
  SlateAction,
2641
+ SlateActionBuilder,
2642
+ SlateAdapter,
2643
+ SlateAdapterSpec,
2154
2644
  SlateAuth,
2155
2645
  SlateConfig,
2156
2646
  SlateContext,
@@ -2158,9 +2648,12 @@ export {
2158
2648
  SlateDefaultPollingIntervalSeconds,
2159
2649
  SlateError,
2160
2650
  SlateLogger,
2651
+ SlatePublicContext,
2652
+ SlatePublicTool,
2161
2653
  SlateSpecification,
2162
2654
  SlateTool,
2163
2655
  SlateTrigger,
2656
+ adapterSpec,
2164
2657
  allOf,
2165
2658
  anyOf,
2166
2659
  auth,
@@ -2181,6 +2674,9 @@ export {
2181
2674
  getApiErrorStatus,
2182
2675
  getBase64ByteLength,
2183
2676
  getCurrentContext,
2677
+ getFileUrlTool,
2678
+ getFileUrlToolInputSchema,
2679
+ getFileUrlToolOutputSchema,
2184
2680
  getGraphWebhookValidationResponse,
2185
2681
  getMetaWebhookVerificationResponse,
2186
2682
  getOAuthExpiresAtFromExpiresIn,
@@ -2197,6 +2693,8 @@ export {
2197
2693
  metaWebhookHttp,
2198
2694
  normalizeOAuthTokenResponse,
2199
2695
  pickDefined,
2696
+ publicTool,
2697
+ redactUrlAttachmentSecrets,
2200
2698
  requestAxios,
2201
2699
  requestAxiosData,
2202
2700
  runWithContext,
@@ -2206,6 +2704,7 @@ export {
2206
2704
  statusLabelForApiError,
2207
2705
  tool,
2208
2706
  trigger,
2707
+ uploadAttachmentDirect,
2209
2708
  validateScopes,
2210
2709
  verifyHmacSignature
2211
2710
  };