@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.
package/dist/index.cjs CHANGED
@@ -30,8 +30,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ AUTH_CONFIG_SECRET_PLACEHOLDER_PREFIX: () => AUTH_CONFIG_SECRET_PLACEHOLDER_PREFIX,
34
+ AuthConfigSecretRedactor: () => AuthConfigSecretRedactor,
35
+ GET_FILE_URL_TOOL_ID: () => GET_FILE_URL_TOOL_ID,
33
36
  Slate: () => Slate,
34
37
  SlateAction: () => SlateAction,
38
+ SlateActionBuilder: () => SlateActionBuilder,
39
+ SlateAdapter: () => SlateAdapter,
40
+ SlateAdapterSpec: () => SlateAdapterSpec,
35
41
  SlateAuth: () => SlateAuth,
36
42
  SlateConfig: () => SlateConfig,
37
43
  SlateContext: () => SlateContext,
@@ -39,9 +45,12 @@ __export(index_exports, {
39
45
  SlateDefaultPollingIntervalSeconds: () => SlateDefaultPollingIntervalSeconds,
40
46
  SlateError: () => SlateError,
41
47
  SlateLogger: () => SlateLogger,
48
+ SlatePublicContext: () => SlatePublicContext,
49
+ SlatePublicTool: () => SlatePublicTool,
42
50
  SlateSpecification: () => SlateSpecification,
43
51
  SlateTool: () => SlateTool,
44
52
  SlateTrigger: () => SlateTrigger,
53
+ adapterSpec: () => adapterSpec,
45
54
  allOf: () => allOf,
46
55
  anyOf: () => anyOf,
47
56
  auth: () => auth,
@@ -62,6 +71,9 @@ __export(index_exports, {
62
71
  getApiErrorStatus: () => getApiErrorStatus,
63
72
  getBase64ByteLength: () => getBase64ByteLength,
64
73
  getCurrentContext: () => getCurrentContext,
74
+ getFileUrlTool: () => getFileUrlTool,
75
+ getFileUrlToolInputSchema: () => getFileUrlToolInputSchema,
76
+ getFileUrlToolOutputSchema: () => getFileUrlToolOutputSchema,
65
77
  getGraphWebhookValidationResponse: () => getGraphWebhookValidationResponse,
66
78
  getMetaWebhookVerificationResponse: () => getMetaWebhookVerificationResponse,
67
79
  getOAuthExpiresAtFromExpiresIn: () => getOAuthExpiresAtFromExpiresIn,
@@ -78,6 +90,8 @@ __export(index_exports, {
78
90
  metaWebhookHttp: () => metaWebhookHttp,
79
91
  normalizeOAuthTokenResponse: () => normalizeOAuthTokenResponse,
80
92
  pickDefined: () => pickDefined,
93
+ publicTool: () => publicTool,
94
+ redactUrlAttachmentSecrets: () => redactUrlAttachmentSecrets,
81
95
  requestAxios: () => requestAxios,
82
96
  requestAxiosData: () => requestAxiosData,
83
97
  runWithContext: () => runWithContext,
@@ -87,6 +101,7 @@ __export(index_exports, {
87
101
  statusLabelForApiError: () => statusLabelForApiError,
88
102
  tool: () => tool,
89
103
  trigger: () => trigger,
104
+ uploadAttachmentDirect: () => uploadAttachmentDirect,
90
105
  validateScopes: () => validateScopes,
91
106
  verifyHmacSignature: () => verifyHmacSignature
92
107
  });
@@ -148,6 +163,12 @@ var SlateAction = class {
148
163
  get docs() {
149
164
  return this._params.docs;
150
165
  }
166
+ get adapter() {
167
+ return this._params.adapter ?? null;
168
+ }
169
+ get isPublic() {
170
+ return this._params.isPublic === true;
171
+ }
151
172
  };
152
173
 
153
174
  // src/error/api.ts
@@ -892,11 +913,12 @@ var isScopes = (value) => typeof value === "object" && value !== null && "AND" i
892
913
 
893
914
  // src/action/builder.ts
894
915
  var SlateActionBuilder = class {
895
- constructor(type, spec2, params, factory) {
916
+ constructor(type, spec2, params, factory, isPublicTool = false) {
896
917
  this.type = type;
897
918
  this.spec = spec2;
898
919
  this.params = params;
899
920
  this.factory = factory;
921
+ this.isPublicTool = isPublicTool;
900
922
  this.#configSchema = spec2.configSchema;
901
923
  this.#authSchema = spec2.authSchema;
902
924
  }
@@ -904,6 +926,7 @@ var SlateActionBuilder = class {
904
926
  spec;
905
927
  params;
906
928
  factory;
929
+ isPublicTool;
907
930
  #configSchema;
908
931
  #authSchema;
909
932
  #inputSchema = null;
@@ -912,20 +935,34 @@ var SlateActionBuilder = class {
912
935
  #authMethods;
913
936
  #toolParams = null;
914
937
  #triggerParams = null;
938
+ #interfaceLocked = false;
915
939
  input(schema) {
940
+ if (this.#interfaceLocked) {
941
+ throw new SlateDeclarationError("Adapter contract input schema cannot be changed");
942
+ }
916
943
  this.#inputSchema = schema;
917
944
  return this;
918
945
  }
919
946
  output(schema) {
947
+ if (this.#interfaceLocked) {
948
+ throw new SlateDeclarationError("Adapter contract output schema cannot be changed");
949
+ }
920
950
  this.#outputSchema = schema;
921
951
  return this;
922
952
  }
953
+ lockInterface() {
954
+ this.#interfaceLocked = true;
955
+ return this;
956
+ }
923
957
  scopes(scopes) {
924
958
  validateScopes(scopes);
925
959
  this.#scopes = scopes;
926
960
  return this;
927
961
  }
928
962
  authMethods(authMethods) {
963
+ if (this.isPublicTool) {
964
+ throw new SlateDeclarationError("Public tools cannot require authentication methods");
965
+ }
929
966
  this.#authMethods = this.validateAuthMethods(authMethods);
930
967
  return this;
931
968
  }
@@ -981,6 +1018,9 @@ var SlateActionBuilder = class {
981
1018
  validateScopes(scopes);
982
1019
  }
983
1020
  let authMethods = this.validateAuthMethods(this.#authMethods ?? this.params.authMethods);
1021
+ if (this.isPublicTool && authMethods) {
1022
+ throw new SlateDeclarationError("Public tools cannot require authentication methods");
1023
+ }
984
1024
  if (!this.#inputSchema) {
985
1025
  throw new SlateDeclarationError("Input schema is not defined");
986
1026
  }
@@ -997,6 +1037,7 @@ var SlateActionBuilder = class {
997
1037
  ...this.params,
998
1038
  scopes,
999
1039
  authMethods,
1040
+ isPublic: this.isPublicTool === true,
1000
1041
  configSchema: this.#configSchema,
1001
1042
  authSchema: this.#authSchema,
1002
1043
  inputSchema: this.#inputSchema,
@@ -1007,6 +1048,9 @@ var SlateActionBuilder = class {
1007
1048
  }
1008
1049
  };
1009
1050
 
1051
+ // src/action/getFileUrlTool.ts
1052
+ var import_zod = require("zod");
1053
+
1010
1054
  // src/action/tool.ts
1011
1055
  var SlateTool = class _SlateTool extends SlateAction {
1012
1056
  #handleInvocation;
@@ -1016,16 +1060,61 @@ var SlateTool = class _SlateTool extends SlateAction {
1016
1060
  }
1017
1061
  static create(spec2, params) {
1018
1062
  return new SlateActionBuilder("tool", spec2, params, (params2) => {
1019
- if (params2.type !== "tool") throw new Error("Invalid action type for tool");
1020
- return new _SlateTool(spec2, params2.inputSchema, params2.outputSchema, params2);
1063
+ return _SlateTool.fromCreateParameters(spec2, params2);
1021
1064
  });
1022
1065
  }
1066
+ static fromCreateParameters(spec2, params) {
1067
+ if (params.type !== "tool") throw new Error("Invalid action type for tool");
1068
+ return new _SlateTool(spec2, params.inputSchema, params.outputSchema, params);
1069
+ }
1023
1070
  get handleInvocation() {
1024
1071
  return this.#handleInvocation;
1025
1072
  }
1026
1073
  };
1027
1074
  var tool = (spec2, params) => SlateTool.create(spec2, params);
1028
1075
 
1076
+ // src/action/getFileUrlTool.ts
1077
+ var GET_FILE_URL_TOOL_ID = "metorial$getFileUrl";
1078
+ var getFileUrlToolInputSchema = import_zod.z.object({
1079
+ url: import_zod.z.string(),
1080
+ reference: import_zod.z.any().nullable()
1081
+ });
1082
+ var getFileUrlToolOutputSchema = import_zod.z.object({
1083
+ url: import_zod.z.string(),
1084
+ expiresAt: import_zod.z.string(),
1085
+ headers: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional(),
1086
+ query: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional()
1087
+ });
1088
+ var getFileUrlTool = (spec2, handleGetFileUrl) => tool(spec2, {
1089
+ key: GET_FILE_URL_TOOL_ID,
1090
+ name: "Get File URL",
1091
+ description: "Reserved: refreshes an expiring attachment download URL. Hub-internal only."
1092
+ }).input(getFileUrlToolInputSchema).output(getFileUrlToolOutputSchema).handleInvocation(async (ctx) => ({
1093
+ output: await handleGetFileUrl(ctx),
1094
+ message: "ok"
1095
+ })).build();
1096
+
1097
+ // src/action/publicTool.ts
1098
+ var SlatePublicTool = class {
1099
+ constructor() {
1100
+ }
1101
+ static create(spec2, params) {
1102
+ return new SlateActionBuilder(
1103
+ "tool",
1104
+ spec2,
1105
+ params,
1106
+ (params2) => {
1107
+ return SlateTool.fromCreateParameters(spec2, {
1108
+ ...params2,
1109
+ isPublic: true
1110
+ });
1111
+ },
1112
+ true
1113
+ );
1114
+ }
1115
+ };
1116
+ var publicTool = (spec2, params) => SlatePublicTool.create(spec2, params);
1117
+
1029
1118
  // src/action/trigger.ts
1030
1119
  var SlateDefaultPollingIntervalSeconds = 60 * 10;
1031
1120
  var SlateTrigger = class _SlateTrigger extends SlateAction {
@@ -1084,23 +1173,35 @@ var SlateTrigger = class _SlateTrigger extends SlateAction {
1084
1173
  var trigger = (spec2, params) => SlateTrigger.create(spec2, params);
1085
1174
 
1086
1175
  // src/action/attachment.ts
1087
- var createUrlAttachment = (url, mimeType) => ({
1088
- mimeType,
1089
- content: {
1090
- type: "url",
1091
- url
1176
+ var createUrlAttachment = (url, opts = {}) => {
1177
+ if (opts.refreshReference !== void 0 && !opts.refreshAt) {
1178
+ throw new Error("createUrlAttachment: refreshAt is required when refreshReference is set");
1092
1179
  }
1093
- });
1094
- var createBase64Attachment = (content, mimeType) => ({
1180
+ return {
1181
+ mimeType: opts.mimeType,
1182
+ attachmentHash: opts.attachmentHash,
1183
+ content: {
1184
+ type: "url",
1185
+ url,
1186
+ ...opts.headers ? { headers: opts.headers } : {},
1187
+ ...opts.query ? { query: opts.query } : {},
1188
+ ...opts.refreshReference !== void 0 ? { refreshReference: opts.refreshReference } : {},
1189
+ ...opts.refreshAt ? { refreshAt: opts.refreshAt } : {}
1190
+ }
1191
+ };
1192
+ };
1193
+ var createBase64Attachment = (content, mimeType, attachmentHash) => ({
1095
1194
  mimeType,
1195
+ attachmentHash,
1096
1196
  content: {
1097
1197
  type: "content",
1098
1198
  encoding: "base64",
1099
1199
  content
1100
1200
  }
1101
1201
  });
1102
- var createTextAttachment = (content, mimeType) => ({
1202
+ var createTextAttachment = (content, mimeType, attachmentHash) => ({
1103
1203
  mimeType,
1204
+ attachmentHash,
1104
1205
  content: {
1105
1206
  type: "content",
1106
1207
  encoding: "utf-8",
@@ -1108,6 +1209,173 @@ var createTextAttachment = (content, mimeType) => ({
1108
1209
  }
1109
1210
  });
1110
1211
 
1212
+ // src/action/directUpload.ts
1213
+ var uploadAttachmentDirect = async (d) => {
1214
+ let baseUrl = d.live.baseUrl.replace(/\/$/, "");
1215
+ let registerRes = await fetch(`${baseUrl}/slates-hub/live-invocation/attachments`, {
1216
+ method: "POST",
1217
+ headers: {
1218
+ "content-type": "application/json",
1219
+ authorization: `Bearer ${d.live.token}`
1220
+ },
1221
+ body: JSON.stringify({
1222
+ attachments: [{ mimeType: d.mimeType, filename: d.filename }]
1223
+ })
1224
+ });
1225
+ if (!registerRes.ok) {
1226
+ throw new Error(
1227
+ `Failed to register attachment for direct upload (status ${registerRes.status})`
1228
+ );
1229
+ }
1230
+ let { attachments } = await registerRes.json();
1231
+ let registration = attachments[0];
1232
+ if (!registration) {
1233
+ throw new Error("Live invocation API returned no attachment registrations");
1234
+ }
1235
+ let putRes = await fetch(registration.uploadUrl, {
1236
+ method: "PUT",
1237
+ ...d.body instanceof Uint8Array ? {} : { duplex: "half" },
1238
+ headers: d.mimeType ? { "content-type": d.mimeType } : void 0,
1239
+ body: d.body
1240
+ });
1241
+ if (!putRes.ok) {
1242
+ throw new Error(`Failed to upload attachment content (status ${putRes.status})`);
1243
+ }
1244
+ return {
1245
+ mimeType: d.mimeType,
1246
+ content: { type: "upload_reference", referenceId: registration.referenceId }
1247
+ };
1248
+ };
1249
+
1250
+ // src/adapter/adapter.ts
1251
+ var SlateAdapter = class {
1252
+ constructor(_spec, _tools, _triggers) {
1253
+ this._spec = _spec;
1254
+ this._tools = _tools;
1255
+ this._triggers = _triggers;
1256
+ }
1257
+ _spec;
1258
+ _tools;
1259
+ _triggers;
1260
+ get spec() {
1261
+ return this._spec;
1262
+ }
1263
+ get id() {
1264
+ return this._spec.id;
1265
+ }
1266
+ get name() {
1267
+ return this._spec.name;
1268
+ }
1269
+ get capabilities() {
1270
+ return this._spec.capabilities;
1271
+ }
1272
+ get tools() {
1273
+ return this._tools;
1274
+ }
1275
+ get triggers() {
1276
+ return this._triggers;
1277
+ }
1278
+ get actions() {
1279
+ return [...this._triggers, ...this._tools];
1280
+ }
1281
+ };
1282
+
1283
+ // src/adapter/spec.ts
1284
+ var SlateAdapterSpec = class _SlateAdapterSpec {
1285
+ constructor(_params) {
1286
+ this._params = _params;
1287
+ }
1288
+ _params;
1289
+ static create(params) {
1290
+ let id = params.id?.trim();
1291
+ let name = params.name?.trim();
1292
+ if (!id) {
1293
+ throw new SlateDeclarationError("Adapter id must be a non-empty string");
1294
+ }
1295
+ if (!name) {
1296
+ throw new SlateDeclarationError("Adapter name must be a non-empty string");
1297
+ }
1298
+ return new _SlateAdapterSpec({
1299
+ id,
1300
+ name,
1301
+ capabilities: validateCapabilities(params.capabilities ?? [])
1302
+ });
1303
+ }
1304
+ get id() {
1305
+ return this._params.id;
1306
+ }
1307
+ get name() {
1308
+ return this._params.name;
1309
+ }
1310
+ get capabilities() {
1311
+ return this._params.capabilities;
1312
+ }
1313
+ get parameters() {
1314
+ return this._params;
1315
+ }
1316
+ tool(spec2, params) {
1317
+ return SlateTool.create(spec2, this.linkAction(params));
1318
+ }
1319
+ publicTool(spec2, params) {
1320
+ return SlatePublicTool.create(spec2, this.linkAction(params));
1321
+ }
1322
+ trigger(spec2, params) {
1323
+ return SlateTrigger.create(spec2, this.linkAction(params));
1324
+ }
1325
+ register(params) {
1326
+ let actions = [...params.tools, ...params.triggers];
1327
+ let seenKeys = /* @__PURE__ */ new Set();
1328
+ for (let action of actions) {
1329
+ if (action.adapter && action.adapter !== this.id) {
1330
+ throw new SlateDeclarationError(
1331
+ `Action "${action.key}" is linked to adapter "${action.adapter}", not "${this.id}"`
1332
+ );
1333
+ }
1334
+ if (!action.adapter) {
1335
+ throw new SlateDeclarationError(
1336
+ `Action "${action.key}" must be created with this adapter's tool(), publicTool(), or trigger() helpers`
1337
+ );
1338
+ }
1339
+ if (seenKeys.has(action.key)) {
1340
+ throw new SlateDeclarationError(
1341
+ `Adapter "${this.id}" already has an action with key "${action.key}"`
1342
+ );
1343
+ }
1344
+ seenKeys.add(action.key);
1345
+ }
1346
+ return new SlateAdapter(this, params.tools, params.triggers);
1347
+ }
1348
+ linkAction(params) {
1349
+ if (params.adapter != null && params.adapter !== this.id) {
1350
+ throw new SlateDeclarationError(
1351
+ `Action "${params.key}" cannot be linked to adapter "${this.id}" because it is already linked to "${params.adapter}"`
1352
+ );
1353
+ }
1354
+ return {
1355
+ ...params,
1356
+ adapter: this.id
1357
+ };
1358
+ }
1359
+ };
1360
+ var adapterSpec = (params) => SlateAdapterSpec.create(params);
1361
+ var validateCapabilities = (capabilities) => {
1362
+ let seenIds = /* @__PURE__ */ new Set();
1363
+ return capabilities.map((capability) => {
1364
+ let id = capability.id?.trim();
1365
+ if (!id) {
1366
+ throw new SlateDeclarationError("Adapter capability ids must be non-empty strings");
1367
+ }
1368
+ if (seenIds.has(id)) {
1369
+ throw new SlateDeclarationError(`Adapter capability "${id}" is defined more than once`);
1370
+ }
1371
+ seenIds.add(id);
1372
+ return {
1373
+ id,
1374
+ value: capability.value
1375
+ };
1376
+ });
1377
+ };
1378
+
1111
1379
  // src/auth/auth.ts
1112
1380
  var SlateAuth = class _SlateAuth {
1113
1381
  #outputSchema = null;
@@ -1249,6 +1517,76 @@ var normalizeOAuthTokenResponse = (data, options = {}) => {
1249
1517
  };
1250
1518
  };
1251
1519
 
1520
+ // src/auth/redact.ts
1521
+ var AUTH_CONFIG_SECRET_PLACEHOLDER_PREFIX = "$$MT$secret$authConfig$";
1522
+ var AuthConfigSecretRedactor = class {
1523
+ secretToPlaceholder = /* @__PURE__ */ new Map();
1524
+ constructor(authConfig) {
1525
+ if (authConfig) this.buildSecretMap(authConfig);
1526
+ }
1527
+ redact(value) {
1528
+ return this.transform(value, (stringValue) => {
1529
+ return this.secretToPlaceholder.get(stringValue) ?? stringValue;
1530
+ });
1531
+ }
1532
+ buildSecretMap(value, path = []) {
1533
+ if (typeof value === "string") {
1534
+ if (value.length === 0) return;
1535
+ this.secretToPlaceholder.set(
1536
+ value,
1537
+ `${AUTH_CONFIG_SECRET_PLACEHOLDER_PREFIX}${path.join(".")}`
1538
+ );
1539
+ return;
1540
+ }
1541
+ if (Array.isArray(value)) {
1542
+ value.forEach((item, index) => {
1543
+ this.buildSecretMap(item, [...path, String(index)]);
1544
+ });
1545
+ return;
1546
+ }
1547
+ if (this.isPlainObject(value)) {
1548
+ Object.entries(value).forEach(([key, nestedValue]) => {
1549
+ this.buildSecretMap(nestedValue, [...path, key]);
1550
+ });
1551
+ }
1552
+ }
1553
+ transform(value, transformString) {
1554
+ if (typeof value === "string") {
1555
+ return transformString(value);
1556
+ }
1557
+ if (Array.isArray(value)) {
1558
+ return value.map((item) => this.transform(item, transformString));
1559
+ }
1560
+ if (this.isPlainObject(value)) {
1561
+ let result = {};
1562
+ Object.entries(value).forEach(([key, nestedValue]) => {
1563
+ result[key] = this.transform(nestedValue, transformString);
1564
+ });
1565
+ return result;
1566
+ }
1567
+ return value;
1568
+ }
1569
+ isPlainObject(value) {
1570
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1571
+ }
1572
+ };
1573
+ var redactUrlAttachmentSecrets = (attachments, authConfig) => {
1574
+ if (!authConfig || !attachments) return attachments;
1575
+ let redactor = new AuthConfigSecretRedactor(authConfig);
1576
+ return attachments.map((attachment) => {
1577
+ if (attachment.content.type !== "url") return attachment;
1578
+ if (!attachment.content.headers && !attachment.content.query) return attachment;
1579
+ return {
1580
+ ...attachment,
1581
+ content: {
1582
+ ...attachment.content,
1583
+ headers: attachment.content.headers ? redactor.redact(attachment.content.headers) : void 0,
1584
+ query: attachment.content.query ? redactor.redact(attachment.content.query) : void 0
1585
+ }
1586
+ };
1587
+ });
1588
+ };
1589
+
1252
1590
  // src/axios/index.ts
1253
1591
  var import_axios3 = __toESM(require("axios"), 1);
1254
1592
 
@@ -1527,9 +1865,9 @@ var truncateText = (text) => {
1527
1865
  truncated: true
1528
1866
  };
1529
1867
  };
1530
- var sanitizeFreeText = (text) => {
1868
+ var sanitizeFreeText = (text, redactor) => {
1531
1869
  if (!text) return text;
1532
- let result = text;
1870
+ let result = redactor ? redactor.redact(text) : text;
1533
1871
  result = result.replace(
1534
1872
  HTTP_AUTH_SCHEME_PATTERN,
1535
1873
  (_match, scheme) => `${scheme} ${REDACTED_VALUE}`
@@ -1547,21 +1885,21 @@ var sanitizeFreeText = (text) => {
1547
1885
  );
1548
1886
  return result;
1549
1887
  };
1550
- var sanitizeScalar = (value) => truncateText(sanitizeFreeText(value));
1551
- var redactStructuredValue = (value, depth = 0) => {
1888
+ var sanitizeScalar = (value, redactor) => truncateText(sanitizeFreeText(value, redactor));
1889
+ var redactStructuredValue = (value, depth = 0, redactor) => {
1552
1890
  if (value == null) return value;
1553
1891
  if (depth >= STRUCTURED_DEPTH_LIMIT) return "[truncated]";
1554
1892
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
1555
- return typeof value === "string" ? sanitizeFreeText(value) : value;
1893
+ return typeof value === "string" ? sanitizeFreeText(value, redactor) : value;
1556
1894
  }
1557
1895
  if (Array.isArray(value)) {
1558
- return value.slice(0, STRUCTURED_ENTRY_LIMIT).map((entry) => redactStructuredValue(entry, depth + 1));
1896
+ return value.slice(0, STRUCTURED_ENTRY_LIMIT).map((entry) => redactStructuredValue(entry, depth + 1, redactor));
1559
1897
  }
1560
1898
  if (isRecord2(value)) {
1561
1899
  return Object.fromEntries(
1562
1900
  Object.entries(value).slice(0, STRUCTURED_ENTRY_LIMIT).map(([key, entry]) => [
1563
1901
  key,
1564
- isSecretKeyName(key) ? REDACTED_VALUE : redactStructuredValue(entry, depth + 1)
1902
+ isSecretKeyName(key) ? REDACTED_VALUE : redactStructuredValue(entry, depth + 1, redactor)
1565
1903
  ])
1566
1904
  );
1567
1905
  }
@@ -1579,11 +1917,11 @@ var maybeParseJson = (text, contentType) => {
1579
1917
  return null;
1580
1918
  }
1581
1919
  };
1582
- var sanitizeTextBody = (text, contentType) => {
1920
+ var sanitizeTextBody = (text, contentType, redactor) => {
1583
1921
  if (!text) return void 0;
1584
1922
  let normalizedContentType = normalizeContentType(contentType);
1585
1923
  let parsedJson = maybeParseJson(text, normalizedContentType);
1586
- let normalizedText = parsedJson !== null ? JSON.stringify(redactStructuredValue(parsedJson)) : sanitizeFreeText(text);
1924
+ let normalizedText = parsedJson !== null ? JSON.stringify(redactStructuredValue(parsedJson, 0, redactor)) : sanitizeFreeText(text, redactor);
1587
1925
  let { text: truncatedText, truncated } = truncateText(normalizedText);
1588
1926
  return {
1589
1927
  ...normalizedContentType ? { contentType: normalizedContentType } : {},
@@ -1599,22 +1937,26 @@ var isBinaryLike = (value) => {
1599
1937
  if (typeof FormData !== "undefined" && value instanceof FormData) return true;
1600
1938
  return false;
1601
1939
  };
1602
- var serializeTextBody = (value, contentType) => {
1940
+ var serializeTextBody = (value, contentType, redactor) => {
1603
1941
  let normalizedContentType = normalizeContentType(contentType);
1604
1942
  if (value == null || isBinaryLike(value) || !isTextContentType(normalizedContentType)) {
1605
1943
  return void 0;
1606
1944
  }
1607
1945
  if (typeof value === "string") {
1608
- return sanitizeTextBody(value, normalizedContentType);
1946
+ return sanitizeTextBody(value, normalizedContentType, redactor);
1609
1947
  }
1610
1948
  if (value instanceof URLSearchParams) {
1611
- return sanitizeTextBody(value.toString(), normalizedContentType);
1949
+ return sanitizeTextBody(value.toString(), normalizedContentType, redactor);
1612
1950
  }
1613
1951
  if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
1614
- return sanitizeTextBody(String(value), normalizedContentType);
1952
+ return sanitizeTextBody(String(value), normalizedContentType, redactor);
1615
1953
  }
1616
1954
  if (Array.isArray(value) || isRecord2(value)) {
1617
- return sanitizeTextBody(JSON.stringify(redactStructuredValue(value)), "application/json");
1955
+ return sanitizeTextBody(
1956
+ JSON.stringify(redactStructuredValue(value, 0, redactor)),
1957
+ "application/json",
1958
+ redactor
1959
+ );
1618
1960
  }
1619
1961
  return void 0;
1620
1962
  };
@@ -1629,16 +1971,20 @@ var buildUrl = (baseURL, url) => {
1629
1971
  };
1630
1972
  var REDACTED_ENCODED = encodeURIComponent(REDACTED_VALUE);
1631
1973
  var decodeRedactedMarker = (value) => REDACTED_ENCODED === REDACTED_VALUE ? value : value.split(REDACTED_ENCODED).join(REDACTED_VALUE);
1632
- var redactHashFragment = (hash) => {
1974
+ var redactHashFragment = (hash, redactor) => {
1633
1975
  if (!hash?.includes("=")) return hash;
1634
1976
  let withoutHash = hash.startsWith("#") ? hash.slice(1) : hash;
1635
1977
  let redacted = withoutHash.replace(
1636
1978
  /(?<![A-Za-z0-9])([A-Za-z0-9][A-Za-z0-9_\-.]*)=([^&]*)/g,
1637
- (match, key, _rawValue) => isUrlFormSecretKeyName(key) ? `${key}=${REDACTED_VALUE}` : match
1979
+ (match, key, rawValue) => {
1980
+ if (isUrlFormSecretKeyName(key)) return `${key}=${REDACTED_VALUE}`;
1981
+ let literalRedacted = redactor ? redactor.redact(rawValue) : rawValue;
1982
+ return literalRedacted !== rawValue ? `${key}=${literalRedacted}` : match;
1983
+ }
1638
1984
  );
1639
1985
  return redacted ? `#${redacted}` : "";
1640
1986
  };
1641
- var sanitizeUrl = (value) => {
1987
+ var sanitizeUrl = (value, redactor) => {
1642
1988
  if (!value) return value;
1643
1989
  try {
1644
1990
  let url = new URL(value);
@@ -1649,12 +1995,17 @@ var sanitizeUrl = (value) => {
1649
1995
  for (let key of Array.from(url.searchParams.keys())) {
1650
1996
  if (isUrlFormSecretKeyName(key)) {
1651
1997
  url.searchParams.set(key, REDACTED_VALUE);
1998
+ continue;
1652
1999
  }
2000
+ let rawValue = url.searchParams.get(key) ?? "";
2001
+ let literalRedacted = redactor ? redactor.redact(rawValue) : rawValue;
2002
+ if (literalRedacted !== rawValue) url.searchParams.set(key, literalRedacted);
1653
2003
  }
1654
- url.hash = redactHashFragment(url.hash);
2004
+ url.hash = redactHashFragment(url.hash, redactor);
1655
2005
  return decodeRedactedMarker(url.toString());
1656
2006
  } catch {
1657
- return value.replace(
2007
+ let text = redactor ? redactor.redact(value) : value;
2008
+ return text.replace(
1658
2009
  /([?&]([^=&#]+)=)([^&#]+)/g,
1659
2010
  (_match, prefix, key, rawValue) => isUrlFormSecretKeyName(key) ? `${prefix}${REDACTED_VALUE}` : `${prefix}${rawValue}`
1660
2011
  );
@@ -1693,8 +2044,10 @@ var headerEntries = (headers) => {
1693
2044
  }
1694
2045
  return [];
1695
2046
  };
1696
- var sanitizeHeaders = (headers) => {
1697
- let entries = headerEntries(headers).map(([key, value]) => [key.toLowerCase(), sanitizeScalar(value).text]).filter(([key]) => isSafeHeaderName(key));
2047
+ var sanitizeHeaders = (headers, redactor) => {
2048
+ let entries = headerEntries(headers).map(
2049
+ ([key, value]) => [key.toLowerCase(), sanitizeScalar(value, redactor).text]
2050
+ ).filter(([key]) => isSafeHeaderName(key));
1698
2051
  if (entries.length === 0) return void 0;
1699
2052
  return Object.fromEntries(entries);
1700
2053
  };
@@ -1720,15 +2073,16 @@ var clearTraceDraft = (config2, draft) => {
1720
2073
  var attachHttpTraceDraft = (config2, context) => {
1721
2074
  let traceAwareConfig = config2;
1722
2075
  let contentType = getContentType(config2.headers);
2076
+ let redactor = new AuthConfigSecretRedactor(context._getAuthConfigForRedaction());
1723
2077
  traceAwareConfig.__slatesHttpTraceDraft = {
1724
2078
  context,
1725
2079
  startedAt: (/* @__PURE__ */ new Date()).toISOString(),
1726
2080
  startedAtMs: Date.now(),
1727
2081
  request: {
1728
2082
  method: (config2.method ?? "GET").toUpperCase(),
1729
- url: sanitizeUrl(buildUrl(config2.baseURL, config2.url)),
1730
- ...sanitizeHeaders(config2.headers) ? { headers: sanitizeHeaders(config2.headers) } : {},
1731
- ...serializeTextBody(config2.data, contentType) ? { body: serializeTextBody(config2.data, contentType) } : {}
2083
+ url: sanitizeUrl(buildUrl(config2.baseURL, config2.url), redactor),
2084
+ ...sanitizeHeaders(config2.headers, redactor) ? { headers: sanitizeHeaders(config2.headers, redactor) } : {},
2085
+ ...serializeTextBody(config2.data, contentType, redactor) ? { body: serializeTextBody(config2.data, contentType, redactor) } : {}
1732
2086
  }
1733
2087
  };
1734
2088
  if (isObjectLike(traceAwareConfig)) {
@@ -1747,6 +2101,7 @@ var recordHttpTraceFromResponse = (response) => {
1747
2101
  let draft = takeTraceDraft(config2);
1748
2102
  if (!draft) return response;
1749
2103
  clearTraceDraft(config2, draft);
2104
+ let redactor = new AuthConfigSecretRedactor(draft.context._getAuthConfigForRedaction());
1750
2105
  let contentType = getContentType(response.headers);
1751
2106
  draft.context.recordHttpTrace({
1752
2107
  startedAt: draft.startedAt,
@@ -1755,8 +2110,8 @@ var recordHttpTraceFromResponse = (response) => {
1755
2110
  response: {
1756
2111
  status: response.status,
1757
2112
  ...response.statusText ? { statusText: response.statusText } : {},
1758
- ...sanitizeHeaders(response.headers) ? { headers: sanitizeHeaders(response.headers) } : {},
1759
- ...serializeTextBody(response.data, contentType) ? { body: serializeTextBody(response.data, contentType) } : {}
2113
+ ...sanitizeHeaders(response.headers, redactor) ? { headers: sanitizeHeaders(response.headers, redactor) } : {},
2114
+ ...serializeTextBody(response.data, contentType, redactor) ? { body: serializeTextBody(response.data, contentType, redactor) } : {}
1760
2115
  }
1761
2116
  });
1762
2117
  return response;
@@ -1766,6 +2121,7 @@ var recordHttpTraceFromError = (error) => {
1766
2121
  let draft = takeTraceDraft(config2);
1767
2122
  if (!draft) return;
1768
2123
  clearTraceDraft(config2, draft);
2124
+ let redactor = new AuthConfigSecretRedactor(draft.context._getAuthConfigForRedaction());
1769
2125
  let responseContentType = getContentType(error.response?.headers);
1770
2126
  draft.context.recordHttpTrace({
1771
2127
  startedAt: draft.startedAt,
@@ -1775,8 +2131,8 @@ var recordHttpTraceFromError = (error) => {
1775
2131
  response: {
1776
2132
  status: error.response.status,
1777
2133
  ...error.response.statusText ? { statusText: error.response.statusText } : {},
1778
- ...sanitizeHeaders(error.response.headers) ? { headers: sanitizeHeaders(error.response.headers) } : {},
1779
- ...serializeTextBody(error.response.data, responseContentType) ? { body: serializeTextBody(error.response.data, responseContentType) } : {}
2134
+ ...sanitizeHeaders(error.response.headers, redactor) ? { headers: sanitizeHeaders(error.response.headers, redactor) } : {},
2135
+ ...serializeTextBody(error.response.data, responseContentType, redactor) ? { body: serializeTextBody(error.response.data, responseContentType, redactor) } : {}
1780
2136
  }
1781
2137
  } : {},
1782
2138
  error: {
@@ -1907,26 +2263,42 @@ var SlateConfig = class _SlateConfig {
1907
2263
  var config = (schema) => SlateConfig.create(schema);
1908
2264
 
1909
2265
  // src/context/context.ts
1910
- var SlateContext = class {
1911
- constructor(config2, input, auth2, spec2, logger) {
2266
+ var import_node_stream = require("stream");
2267
+ var import_p_queue = __toESM(require("p-queue"), 1);
2268
+ var isBufferLike = (value) => value instanceof Uint8Array || value instanceof ArrayBuffer;
2269
+ var toUint8Array = (value) => value instanceof ArrayBuffer ? new Uint8Array(value) : value;
2270
+ var isReadableStream = (value) => typeof ReadableStream !== "undefined" && value instanceof ReadableStream;
2271
+ var isNodeReadable = (value) => !!value && typeof value === "object" && typeof value.pipe === "function" && typeof value.on === "function";
2272
+ var isResponseLike = (value) => typeof Response !== "undefined" && value instanceof Response;
2273
+ var readStreamToBuffer = async (stream) => {
2274
+ let reader = stream.getReader();
2275
+ let chunks = [];
2276
+ while (true) {
2277
+ let { done, value } = await reader.read();
2278
+ if (done) break;
2279
+ if (value) chunks.push(value);
2280
+ }
2281
+ return Buffer.concat(chunks);
2282
+ };
2283
+ var SlatePublicContext = class {
2284
+ constructor(input, spec2, logger, liveInvocation = null) {
1912
2285
  this.spec = spec2;
1913
2286
  this.logger = logger;
1914
- this.#config = config2;
1915
2287
  this.#input = input;
1916
- this.#auth = auth2;
2288
+ this.#liveInvocation = liveInvocation;
1917
2289
  }
1918
2290
  spec;
1919
2291
  logger;
1920
- #config;
1921
2292
  #input;
1922
- #auth;
1923
2293
  #httpTraces = [];
2294
+ #liveInvocation;
2295
+ #attachmentsDisabled = false;
2296
+ #attachments = [];
2297
+ #pendingUploads = [];
2298
+ #uploadQueue = new import_p_queue.default({ concurrency: 10 });
1924
2299
  get specification() {
1925
2300
  return this.spec;
1926
2301
  }
1927
- get config() {
1928
- return Object.freeze(this.#config);
1929
- }
1930
2302
  get input() {
1931
2303
  return Object.freeze(this.#input);
1932
2304
  }
@@ -1944,9 +2316,6 @@ var SlateContext = class {
1944
2316
  this.#input.registrationDetails
1945
2317
  );
1946
2318
  }
1947
- get auth() {
1948
- return Object.freeze(this.#auth);
1949
- }
1950
2319
  recordHttpTrace(trace) {
1951
2320
  this.#httpTraces.push(trace);
1952
2321
  }
@@ -1980,6 +2349,107 @@ var SlateContext = class {
1980
2349
  progress(message) {
1981
2350
  this.logger.progress(message);
1982
2351
  }
2352
+ async addAttachment(input) {
2353
+ if (input.type === "url") {
2354
+ this.#attachments.push(
2355
+ createUrlAttachment(input.url.toString(), {
2356
+ mimeType: input.mimeType,
2357
+ headers: input.headers,
2358
+ query: input.query,
2359
+ refreshReference: input.refreshReference,
2360
+ refreshAt: input.refreshAt
2361
+ })
2362
+ );
2363
+ return;
2364
+ }
2365
+ let content = input.content;
2366
+ if (isBufferLike(content)) {
2367
+ let bytes = toUint8Array(content);
2368
+ this.#attachments.push(
2369
+ createBase64Attachment(Buffer.from(bytes).toString("base64"), input.mimeType)
2370
+ );
2371
+ return;
2372
+ }
2373
+ let normalized = await this.#normalizeStreamInput(content, {
2374
+ mimeType: input.mimeType,
2375
+ filename: input.filename
2376
+ });
2377
+ if (!normalized) return;
2378
+ if (!this.#liveInvocation || this.#attachmentsDisabled) {
2379
+ let buffer = await readStreamToBuffer(normalized.stream);
2380
+ this.#attachments.push(
2381
+ createBase64Attachment(buffer.toString("base64"), normalized.mimeType)
2382
+ );
2383
+ return;
2384
+ }
2385
+ let live = this.#liveInvocation;
2386
+ let task = this.#uploadQueue.add(
2387
+ () => uploadAttachmentDirect({
2388
+ live,
2389
+ mimeType: normalized.mimeType,
2390
+ filename: normalized.filename,
2391
+ body: normalized.stream
2392
+ })
2393
+ ).then((attachment) => {
2394
+ this.#attachments.push(attachment);
2395
+ }).catch((err) => {
2396
+ this.#attachmentsDisabled = true;
2397
+ this.warn({
2398
+ message: `Attachment dropped: direct upload failed (${err instanceof Error ? err.message : String(err)}).`
2399
+ });
2400
+ });
2401
+ this.#pendingUploads.push(task);
2402
+ }
2403
+ async #normalizeStreamInput(input, opts) {
2404
+ if (isResponseLike(input)) {
2405
+ if (!input.body) {
2406
+ this.warn({ message: "Attachment dropped: response has no body." });
2407
+ return null;
2408
+ }
2409
+ return {
2410
+ stream: input.body,
2411
+ mimeType: opts.mimeType ?? input.headers.get("content-type") ?? void 0,
2412
+ filename: opts.filename
2413
+ };
2414
+ }
2415
+ if (isReadableStream(input)) {
2416
+ return { stream: input, mimeType: opts.mimeType, filename: opts.filename };
2417
+ }
2418
+ if (isNodeReadable(input)) {
2419
+ return {
2420
+ stream: import_node_stream.Readable.toWeb(input),
2421
+ mimeType: opts.mimeType,
2422
+ filename: opts.filename
2423
+ };
2424
+ }
2425
+ this.warn({ message: "Attachment dropped: unsupported attachment input type." });
2426
+ return null;
2427
+ }
2428
+ async _finalizeAttachments() {
2429
+ await Promise.allSettled(this.#pendingUploads);
2430
+ return this.#attachments;
2431
+ }
2432
+ _getAuthConfigForRedaction() {
2433
+ return void 0;
2434
+ }
2435
+ };
2436
+ var SlateContext = class extends SlatePublicContext {
2437
+ #config;
2438
+ #auth;
2439
+ constructor(config2, input, auth2, spec2, logger, liveInvocation = null) {
2440
+ super(input, spec2, logger, liveInvocation);
2441
+ this.#config = config2;
2442
+ this.#auth = auth2;
2443
+ }
2444
+ get config() {
2445
+ return Object.freeze(this.#config);
2446
+ }
2447
+ get auth() {
2448
+ return Object.freeze(this.#auth);
2449
+ }
2450
+ _getAuthConfigForRedaction() {
2451
+ return this.#auth;
2452
+ }
1983
2453
  };
1984
2454
 
1985
2455
  // src/crypto/hmac.ts
@@ -2129,14 +2599,40 @@ var setIfDefined = (target, key, value) => {
2129
2599
 
2130
2600
  // src/specification/slate.ts
2131
2601
  var Slate = class _Slate {
2132
- constructor(_spec, _actions) {
2602
+ constructor(_spec, _actions, _adapters) {
2133
2603
  this._spec = _spec;
2134
2604
  this._actions = _actions;
2605
+ this._adapters = _adapters;
2135
2606
  }
2136
2607
  _spec;
2137
2608
  _actions;
2609
+ _adapters;
2138
2610
  static create(params) {
2139
- return new _Slate(params.spec, [...params.triggers, ...params.tools]);
2611
+ let adapters = params.adapters ?? [];
2612
+ let actions = [...params.triggers, ...params.tools];
2613
+ let seenAdapterIds = /* @__PURE__ */ new Set();
2614
+ let seenActionKeys = new Map(actions.map((action) => [action.key, action]));
2615
+ for (let adapter of adapters) {
2616
+ if (seenAdapterIds.has(adapter.id)) {
2617
+ throw new SlateDeclarationError(
2618
+ `Adapter "${adapter.id}" is registered more than once`
2619
+ );
2620
+ }
2621
+ seenAdapterIds.add(adapter.id);
2622
+ for (let action of adapter.actions) {
2623
+ let existing = seenActionKeys.get(action.key);
2624
+ if (existing && existing !== action) {
2625
+ throw new SlateDeclarationError(
2626
+ `Adapter "${adapter.id}" action "${action.key}" conflicts with another action`
2627
+ );
2628
+ }
2629
+ if (!existing) {
2630
+ actions.push(action);
2631
+ seenActionKeys.set(action.key, action);
2632
+ }
2633
+ }
2634
+ }
2635
+ return new _Slate(params.spec, actions, adapters);
2140
2636
  }
2141
2637
  get spec() {
2142
2638
  return this._spec;
@@ -2144,10 +2640,13 @@ var Slate = class _Slate {
2144
2640
  get actions() {
2145
2641
  return this._actions;
2146
2642
  }
2643
+ get adapters() {
2644
+ return this._adapters;
2645
+ }
2147
2646
  };
2148
2647
 
2149
2648
  // src/specification/specification.ts
2150
- var import_zod = __toESM(require("zod"), 1);
2649
+ var import_zod2 = __toESM(require("zod"), 1);
2151
2650
  var SlateSpecification = class _SlateSpecification {
2152
2651
  constructor(_config, _auth, _params) {
2153
2652
  this._config = _config;
@@ -2164,7 +2663,7 @@ var SlateSpecification = class _SlateSpecification {
2164
2663
  return this._config.configSchema;
2165
2664
  }
2166
2665
  get authSchema() {
2167
- return "outputSchema" in this._auth ? this._auth.outputSchema : import_zod.default.object({});
2666
+ return "outputSchema" in this._auth ? this._auth.outputSchema : import_zod2.default.object({});
2168
2667
  }
2169
2668
  get config() {
2170
2669
  return this._config;
@@ -2244,8 +2743,14 @@ var getMetaWebhookVerificationResponse = (request, expectedVerifyToken) => {
2244
2743
  };
2245
2744
  // Annotate the CommonJS export names for ESM import in node:
2246
2745
  0 && (module.exports = {
2746
+ AUTH_CONFIG_SECRET_PLACEHOLDER_PREFIX,
2747
+ AuthConfigSecretRedactor,
2748
+ GET_FILE_URL_TOOL_ID,
2247
2749
  Slate,
2248
2750
  SlateAction,
2751
+ SlateActionBuilder,
2752
+ SlateAdapter,
2753
+ SlateAdapterSpec,
2249
2754
  SlateAuth,
2250
2755
  SlateConfig,
2251
2756
  SlateContext,
@@ -2253,9 +2758,12 @@ var getMetaWebhookVerificationResponse = (request, expectedVerifyToken) => {
2253
2758
  SlateDefaultPollingIntervalSeconds,
2254
2759
  SlateError,
2255
2760
  SlateLogger,
2761
+ SlatePublicContext,
2762
+ SlatePublicTool,
2256
2763
  SlateSpecification,
2257
2764
  SlateTool,
2258
2765
  SlateTrigger,
2766
+ adapterSpec,
2259
2767
  allOf,
2260
2768
  anyOf,
2261
2769
  auth,
@@ -2276,6 +2784,9 @@ var getMetaWebhookVerificationResponse = (request, expectedVerifyToken) => {
2276
2784
  getApiErrorStatus,
2277
2785
  getBase64ByteLength,
2278
2786
  getCurrentContext,
2787
+ getFileUrlTool,
2788
+ getFileUrlToolInputSchema,
2789
+ getFileUrlToolOutputSchema,
2279
2790
  getGraphWebhookValidationResponse,
2280
2791
  getMetaWebhookVerificationResponse,
2281
2792
  getOAuthExpiresAtFromExpiresIn,
@@ -2292,6 +2803,8 @@ var getMetaWebhookVerificationResponse = (request, expectedVerifyToken) => {
2292
2803
  metaWebhookHttp,
2293
2804
  normalizeOAuthTokenResponse,
2294
2805
  pickDefined,
2806
+ publicTool,
2807
+ redactUrlAttachmentSecrets,
2295
2808
  requestAxios,
2296
2809
  requestAxiosData,
2297
2810
  runWithContext,
@@ -2301,6 +2814,7 @@ var getMetaWebhookVerificationResponse = (request, expectedVerifyToken) => {
2301
2814
  statusLabelForApiError,
2302
2815
  tool,
2303
2816
  trigger,
2817
+ uploadAttachmentDirect,
2304
2818
  validateScopes,
2305
2819
  verifyHmacSignature
2306
2820
  });