@tangle-network/agent-integrations 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/index.ts
2
- import { createHmac as createHmac3, randomUUID as randomUUID2, timingSafeEqual as timingSafeEqual2 } from "crypto";
2
+ import { createHmac as createHmac3, randomUUID as randomUUID3, timingSafeEqual as timingSafeEqual2 } from "crypto";
3
3
 
4
4
  // src/activepieces-catalog.ts
5
5
  import { readFileSync } from "fs";
@@ -128,21 +128,24 @@ function buildActivepiecesConnectors(options = {}) {
128
128
  const override = getActivepiecesOverride(entry.id);
129
129
  const category = override?.category ?? entry.category;
130
130
  const scopes = [`${entry.id}.read`, `${entry.id}.write`];
131
- const actions = entry.actions.length > 0 ? entry.actions.map((action) => toAction(applyActionOverride(action, override), scopes, dataClassFor(category))) : defaultActions(entry.id, scopes, dataClassFor(category));
131
+ const catalogActions = entry.actions.length > 0 ? entry.actions.map((action) => toAction(applyActionOverride(action, override), scopes, dataClassFor(category))) : defaultActions(entry.id, scopes, dataClassFor(category));
132
+ const catalogTriggers = entry.triggers.map((trigger) => toTrigger(trigger, scopes, dataClassFor(category)));
132
133
  return {
133
134
  id: entry.id,
134
135
  providerId,
135
136
  title: entry.title,
136
137
  category,
137
138
  auth: entry.auth,
138
- scopes,
139
- actions,
140
- triggers: entry.triggers.map((trigger) => toTrigger(trigger, scopes, dataClassFor(category))),
139
+ scopes: options.includeCatalogActions ? scopes : [],
140
+ actions: options.includeCatalogActions ? catalogActions : [],
141
+ triggers: options.includeCatalogActions ? catalogTriggers : void 0,
141
142
  metadata: {
142
143
  source: "activepieces-community",
143
144
  executable: false,
144
145
  runtime: "activepieces-piece",
145
146
  catalogOnly: true,
147
+ catalogActionCount: catalogActions.length,
148
+ catalogTriggerCount: catalogTriggers.length,
146
149
  npmPackage: entry.npmPackage,
147
150
  version: entry.version,
148
151
  license: entry.source.license,
@@ -490,15 +493,15 @@ function actionPack(pack, scopes) {
490
493
  }
491
494
  function triggersFor(pack, scopes) {
492
495
  const readScope = scopes.find((scope) => scope.endsWith(".read")) ?? scopes[0];
493
- const requiredScopes = readScope ? [readScope] : [];
494
- if (pack === "email") return [{ id: "message.received", title: "Message received", requiredScopes, dataClass: "private" }];
495
- if (pack === "calendar") return [{ id: "event.changed", title: "Event changed", requiredScopes, dataClass: "private" }];
496
- if (pack === "chat") return [{ id: "message.posted", title: "Message posted", requiredScopes, dataClass: "private" }];
497
- if (pack === "crm") return [{ id: "record.changed", title: "Record changed", requiredScopes, dataClass: "private" }];
498
- if (pack === "support") return [{ id: "ticket.changed", title: "Ticket changed", requiredScopes, dataClass: "private" }];
499
- if (pack === "commerce") return [{ id: "order.changed", title: "Order changed", requiredScopes, dataClass: "sensitive" }];
500
- if (pack === "finance") return [{ id: "transaction.changed", title: "Transaction changed", requiredScopes, dataClass: "sensitive" }];
501
- if (pack === "workflow" || pack === "webhook") return [{ id: "event.received", title: "Event received", requiredScopes, dataClass: "internal" }];
496
+ const requiredScopes2 = readScope ? [readScope] : [];
497
+ if (pack === "email") return [{ id: "message.received", title: "Message received", requiredScopes: requiredScopes2, dataClass: "private" }];
498
+ if (pack === "calendar") return [{ id: "event.changed", title: "Event changed", requiredScopes: requiredScopes2, dataClass: "private" }];
499
+ if (pack === "chat") return [{ id: "message.posted", title: "Message posted", requiredScopes: requiredScopes2, dataClass: "private" }];
500
+ if (pack === "crm") return [{ id: "record.changed", title: "Record changed", requiredScopes: requiredScopes2, dataClass: "private" }];
501
+ if (pack === "support") return [{ id: "ticket.changed", title: "Ticket changed", requiredScopes: requiredScopes2, dataClass: "private" }];
502
+ if (pack === "commerce") return [{ id: "order.changed", title: "Order changed", requiredScopes: requiredScopes2, dataClass: "sensitive" }];
503
+ if (pack === "finance") return [{ id: "transaction.changed", title: "Transaction changed", requiredScopes: requiredScopes2, dataClass: "sensitive" }];
504
+ if (pack === "workflow" || pack === "webhook") return [{ id: "event.received", title: "Event received", requiredScopes: requiredScopes2, dataClass: "internal" }];
502
505
  return void 0;
503
506
  }
504
507
  function scopesFor(id, pack) {
@@ -1175,6 +1178,8 @@ function inferIntegrationSupportTier(connector) {
1175
1178
  if (metadata.source === "first-party-adapter" || connector.providerId === "first-party") return "firstPartyExecutable";
1176
1179
  if (metadata.source === "gateway-catalog" && metadata.executable === true) return "gatewayExecutable";
1177
1180
  if (metadata.source === "integration-spec") return "setupReady";
1181
+ if (metadata.source === "coverage-catalog" || metadata.source === "activepieces-community" || metadata.catalogOnly === true) return "catalogOnly";
1182
+ if (connector.actions.length > 0) return "gatewayExecutable";
1178
1183
  return "catalogOnly";
1179
1184
  }
1180
1185
  function registryEntry(canonicalId, candidates, precedence, aliases) {
@@ -1221,7 +1226,7 @@ function registryEntry(canonicalId, candidates, precedence, aliases) {
1221
1226
  sources,
1222
1227
  conflicts,
1223
1228
  toolBindable: actions.length > 0,
1224
- catalogOnlyActionCount: ordered.filter((candidate) => candidate.supportTier === "catalogOnly").reduce((sum, candidate) => sum + candidate.connector.actions.length, 0)
1229
+ catalogOnlyActionCount: ordered.filter((candidate) => candidate.supportTier === "catalogOnly").reduce((sum, candidate) => sum + catalogActionCount(candidate.connector), 0)
1225
1230
  }
1226
1231
  }
1227
1232
  }
@@ -1252,6 +1257,10 @@ function toolBindableCandidates(candidates) {
1252
1257
  const bindable = candidates.filter((candidate) => candidate.supportTier !== "catalogOnly");
1253
1258
  return bindable.length > 0 ? bindable : [];
1254
1259
  }
1260
+ function catalogActionCount(connector) {
1261
+ const value = connector.metadata?.catalogActionCount;
1262
+ return typeof value === "number" ? value : connector.actions.length;
1263
+ }
1255
1264
  function conflictDiagnostics(candidates) {
1256
1265
  return [
1257
1266
  conflictFor("auth", candidates.map((candidate) => ({
@@ -1271,14 +1280,877 @@ function conflictFor(field, values) {
1271
1280
  if (uniqueValues.size <= 1) return void 0;
1272
1281
  return { field, values };
1273
1282
  }
1274
- function isSupportTier(value) {
1275
- return value === "catalogOnly" || value === "setupReady" || value === "gatewayExecutable" || value === "firstPartyExecutable" || value === "sandboxExecutable";
1283
+ function isSupportTier(value) {
1284
+ return value === "catalogOnly" || value === "setupReady" || value === "gatewayExecutable" || value === "firstPartyExecutable" || value === "sandboxExecutable";
1285
+ }
1286
+ function slug(value) {
1287
+ return value.trim().toLowerCase().replace(/&/g, "and").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
1288
+ }
1289
+ function unique(values) {
1290
+ return [...new Set(values)];
1291
+ }
1292
+
1293
+ // src/audit.ts
1294
+ import { randomUUID } from "crypto";
1295
+ var InMemoryIntegrationAuditStore = class {
1296
+ events = [];
1297
+ record(event) {
1298
+ this.events.push(event);
1299
+ }
1300
+ list(filter = {}) {
1301
+ return this.events.filter((event) => matchesFilter(event, filter));
1302
+ }
1303
+ };
1304
+ function createIntegrationAuditEvent(input) {
1305
+ const occurredAt = input.occurredAt instanceof Date ? input.occurredAt.toISOString() : input.occurredAt ?? (input.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
1306
+ return {
1307
+ ...input,
1308
+ id: input.id ?? `audit_${randomUUID()}`,
1309
+ occurredAt,
1310
+ metadata: input.metadata ? redactUnknown(input.metadata) : void 0
1311
+ };
1312
+ }
1313
+ function createAuditingActionGuard(options) {
1314
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
1315
+ return {
1316
+ async invokeAction(ctx, proceed) {
1317
+ const startedAt = now();
1318
+ try {
1319
+ const result = await proceed();
1320
+ await options.sink.record(actionEvent({
1321
+ ctx,
1322
+ request: ctx.request,
1323
+ result,
1324
+ type: result.ok ? "action.invoked" : "action.failed",
1325
+ subject: options.subject,
1326
+ occurredAt: startedAt,
1327
+ includeInputPreview: options.includeInputPreview
1328
+ }));
1329
+ return result;
1330
+ } catch (error) {
1331
+ await options.sink.record(actionEvent({
1332
+ ctx,
1333
+ request: ctx.request,
1334
+ type: "action.failed",
1335
+ subject: options.subject,
1336
+ occurredAt: startedAt,
1337
+ includeInputPreview: options.includeInputPreview,
1338
+ message: error instanceof Error ? error.message : "Integration action failed."
1339
+ }));
1340
+ throw error;
1341
+ }
1342
+ }
1343
+ };
1344
+ }
1345
+ function sanitizeAuditConnection(connection) {
1346
+ return {
1347
+ id: connection.id,
1348
+ owner: connection.owner,
1349
+ providerId: connection.providerId,
1350
+ connectorId: connection.connectorId,
1351
+ status: connection.status,
1352
+ grantedScopes: connection.grantedScopes,
1353
+ account: connection.account,
1354
+ hasSecretRef: Boolean(connection.secretRef),
1355
+ createdAt: connection.createdAt,
1356
+ updatedAt: connection.updatedAt,
1357
+ expiresAt: connection.expiresAt,
1358
+ lastUsedAt: connection.lastUsedAt
1359
+ };
1360
+ }
1361
+ function actionEvent(input) {
1362
+ return createIntegrationAuditEvent({
1363
+ type: input.type,
1364
+ occurredAt: input.occurredAt,
1365
+ actor: input.subject ?? input.ctx.connection.owner,
1366
+ connectionId: input.ctx.connection.id,
1367
+ providerId: input.ctx.connection.providerId,
1368
+ connectorId: input.ctx.connection.connectorId,
1369
+ action: input.request.action,
1370
+ risk: input.ctx.action?.risk,
1371
+ dataClass: input.ctx.action?.dataClass,
1372
+ ok: input.result?.ok ?? false,
1373
+ message: input.message,
1374
+ metadata: {
1375
+ idempotencyKey: input.request.idempotencyKey,
1376
+ dryRun: input.request.dryRun,
1377
+ externalId: input.result?.externalId,
1378
+ warnings: input.result?.warnings,
1379
+ inputPreview: input.includeInputPreview ? redactUnknown(input.request.input) : void 0
1380
+ }
1381
+ });
1382
+ }
1383
+ function matchesFilter(event, filter) {
1384
+ if (filter.type && event.type !== filter.type) return false;
1385
+ if (filter.actor && (!event.actor || event.actor.type !== filter.actor.type || event.actor.id !== filter.actor.id)) return false;
1386
+ if (filter.connectionId && event.connectionId !== filter.connectionId) return false;
1387
+ if (filter.providerId && event.providerId !== filter.providerId) return false;
1388
+ if (filter.connectorId && event.connectorId !== filter.connectorId) return false;
1389
+ if (filter.action && event.action !== filter.action) return false;
1390
+ return true;
1391
+ }
1392
+ function redactUnknown(value) {
1393
+ if (Array.isArray(value)) return value.map(redactUnknown);
1394
+ if (!value || typeof value !== "object") return value;
1395
+ const out = {};
1396
+ for (const [key, child] of Object.entries(value)) {
1397
+ if (/token|secret|password|authorization|api[_-]?key|credential|refresh/i.test(key)) {
1398
+ out[key] = "[REDACTED]";
1399
+ } else {
1400
+ out[key] = redactUnknown(child);
1401
+ }
1402
+ }
1403
+ return out;
1404
+ }
1405
+
1406
+ // src/approval.ts
1407
+ import { createHash } from "crypto";
1408
+ var InMemoryIntegrationApprovalStore = class {
1409
+ records = /* @__PURE__ */ new Map();
1410
+ get(approvalId) {
1411
+ return this.records.get(approvalId);
1412
+ }
1413
+ put(record) {
1414
+ this.records.set(record.id, record);
1415
+ }
1416
+ list(filter = {}) {
1417
+ return [...this.records.values()].filter((record) => matchesFilter2(record, filter));
1418
+ }
1419
+ };
1420
+ var ApprovalBackedPolicyEngine = class {
1421
+ base;
1422
+ store;
1423
+ audit;
1424
+ now;
1425
+ approvalTtlMs;
1426
+ constructor(options) {
1427
+ this.base = options.base;
1428
+ this.store = options.store;
1429
+ this.audit = options.audit;
1430
+ this.now = options.now ?? (() => /* @__PURE__ */ new Date());
1431
+ this.approvalTtlMs = options.approvalTtlMs;
1432
+ }
1433
+ async decide(ctx) {
1434
+ const approved = await this.findApprovedRecord(ctx);
1435
+ if (approved) return { decision: "allow", reason: `Approved by ${approved.resolvedBy?.type ?? "actor"} ${approved.resolvedBy?.id ?? "unknown"}.`, metadata: { approvalId: approved.id } };
1436
+ const decision = await this.base.decide(ctx);
1437
+ if (decision.decision !== "require_approval") return decision;
1438
+ const requestedAt = decision.approval.requestedAt;
1439
+ const expiresAt = this.approvalTtlMs ? new Date(Date.parse(requestedAt) + this.approvalTtlMs).toISOString() : void 0;
1440
+ const record = {
1441
+ id: decision.approval.id,
1442
+ request: decision.approval,
1443
+ status: "pending",
1444
+ requestedAt,
1445
+ expiresAt,
1446
+ metadata: { ...decision.metadata ?? {}, inputHash: approvalInputHash(ctx.request.input) }
1447
+ };
1448
+ await this.store.put(record);
1449
+ await this.audit?.record(createIntegrationAuditEvent({
1450
+ type: "approval.requested",
1451
+ actor: ctx.subject,
1452
+ connectionId: ctx.connection.id,
1453
+ providerId: ctx.connection.providerId,
1454
+ connectorId: ctx.connection.connectorId,
1455
+ action: ctx.request.action,
1456
+ risk: ctx.action?.risk,
1457
+ dataClass: ctx.action?.dataClass,
1458
+ message: decision.reason,
1459
+ metadata: { approvalId: record.id },
1460
+ now: this.now
1461
+ }));
1462
+ return decision;
1463
+ }
1464
+ async findApprovedRecord(ctx) {
1465
+ const approvalId = typeof ctx.request.metadata?.approvalId === "string" ? ctx.request.metadata.approvalId : void 0;
1466
+ if (!approvalId) return void 0;
1467
+ const record = await this.store.get(approvalId);
1468
+ if (!record || record.status !== "approved") return void 0;
1469
+ if (record.expiresAt && Date.parse(record.expiresAt) <= this.now().getTime()) {
1470
+ await this.store.put({ ...record, status: "expired" });
1471
+ return void 0;
1472
+ }
1473
+ if (!approvalMatches(record, ctx)) return void 0;
1474
+ return record;
1475
+ }
1476
+ };
1477
+ function createApprovalBackedPolicyEngine(options) {
1478
+ return new ApprovalBackedPolicyEngine(options);
1479
+ }
1480
+ async function resolveIntegrationApproval(input) {
1481
+ const record = await input.store.get(input.approvalId);
1482
+ if (!record) throw new Error(`Approval ${input.approvalId} not found.`);
1483
+ const now = input.now ?? (() => /* @__PURE__ */ new Date());
1484
+ const next = {
1485
+ ...record,
1486
+ status: input.approved ? "approved" : "denied",
1487
+ resolvedAt: now().toISOString(),
1488
+ resolvedBy: input.resolvedBy,
1489
+ reason: input.reason,
1490
+ metadata: { ...record.metadata ?? {}, ...input.metadata ?? {} }
1491
+ };
1492
+ await input.store.put(next);
1493
+ await input.audit?.record(createIntegrationAuditEvent({
1494
+ type: "approval.resolved",
1495
+ actor: input.resolvedBy,
1496
+ connectionId: record.request.connectionId,
1497
+ providerId: record.request.providerId,
1498
+ connectorId: record.request.connectorId,
1499
+ action: record.request.action,
1500
+ risk: record.request.risk,
1501
+ dataClass: record.request.dataClass,
1502
+ ok: input.approved,
1503
+ message: input.reason,
1504
+ metadata: { approvalId: record.id, status: next.status },
1505
+ now
1506
+ }));
1507
+ return next;
1508
+ }
1509
+ function approvalMatches(record, ctx) {
1510
+ return record.request.connectionId === ctx.connection.id && record.request.providerId === ctx.connection.providerId && record.request.connectorId === ctx.connection.connectorId && record.request.action === ctx.request.action && record.request.actor.type === ctx.subject.type && record.request.actor.id === ctx.subject.id && record.metadata?.inputHash === approvalInputHash(ctx.request.input);
1511
+ }
1512
+ function matchesFilter2(record, filter) {
1513
+ if (filter.status && record.status !== filter.status) return false;
1514
+ if (filter.connectionId && record.request.connectionId !== filter.connectionId) return false;
1515
+ if (filter.connectorId && record.request.connectorId !== filter.connectorId) return false;
1516
+ if (filter.action && record.request.action !== filter.action) return false;
1517
+ if (filter.actor && (record.request.actor.type !== filter.actor.type || record.request.actor.id !== filter.actor.id)) return false;
1518
+ return true;
1519
+ }
1520
+ function approvalInputHash(input) {
1521
+ return createHash("sha256").update(JSON.stringify(input ?? null)).digest("base64url");
1522
+ }
1523
+
1524
+ // src/bridge.ts
1525
+ var DEFAULT_INTEGRATION_BRIDGE_ENV = "TANGLE_INTEGRATION_BUNDLE";
1526
+ function buildIntegrationBridgePayload(bundle) {
1527
+ return {
1528
+ version: 1,
1529
+ manifestId: bundle.manifestId,
1530
+ subject: bundle.subject,
1531
+ expiresAt: bundle.expiresAt,
1532
+ tools: bundle.tools.flatMap((tool) => {
1533
+ const binding = bundle.capabilities.find(
1534
+ (candidate) => candidate.connectorId === tool.connectorId && candidate.connectionId && candidate.allowedActions.includes(tool.action.id)
1535
+ );
1536
+ if (!binding) return [];
1537
+ return [{
1538
+ name: tool.name,
1539
+ title: tool.title,
1540
+ connectorId: tool.connectorId,
1541
+ connectionId: binding.connectionId,
1542
+ action: tool.action.id,
1543
+ risk: tool.risk,
1544
+ dataClass: tool.dataClass,
1545
+ requiredScopes: tool.requiredScopes,
1546
+ capabilityToken: binding.capability.token
1547
+ }];
1548
+ })
1549
+ };
1550
+ }
1551
+ function encodeIntegrationBridgePayload(payload) {
1552
+ return Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
1553
+ }
1554
+ function decodeIntegrationBridgePayload(encoded) {
1555
+ const parsed = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
1556
+ assertBridgePayload(parsed);
1557
+ return parsed;
1558
+ }
1559
+ function buildIntegrationBridgeEnvironment(bundle, options = {}) {
1560
+ const envVar = options.envVar ?? DEFAULT_INTEGRATION_BRIDGE_ENV;
1561
+ return {
1562
+ [envVar]: encodeIntegrationBridgePayload(buildIntegrationBridgePayload(bundle))
1563
+ };
1564
+ }
1565
+ function parseIntegrationBridgeEnvironment(env, options = {}) {
1566
+ const envVar = options.envVar ?? DEFAULT_INTEGRATION_BRIDGE_ENV;
1567
+ const encoded = env[envVar];
1568
+ if (!encoded) throw new Error(`Missing ${envVar}.`);
1569
+ return decodeIntegrationBridgePayload(encoded);
1570
+ }
1571
+ function redactIntegrationBridgePayload(payload) {
1572
+ return {
1573
+ ...payload,
1574
+ tools: payload.tools.map((tool) => ({
1575
+ ...tool,
1576
+ capabilityToken: "[REDACTED]"
1577
+ }))
1578
+ };
1579
+ }
1580
+ function assertBridgePayload(value) {
1581
+ if (!value || typeof value !== "object") throw new Error("Invalid integration bridge payload.");
1582
+ const payload = value;
1583
+ if (payload.version !== 1) throw new Error("Unsupported integration bridge payload version.");
1584
+ if (typeof payload.manifestId !== "string") throw new Error("Invalid integration bridge manifestId.");
1585
+ if (typeof payload.expiresAt !== "string") throw new Error("Invalid integration bridge expiresAt.");
1586
+ if (!Array.isArray(payload.tools)) throw new Error("Invalid integration bridge tools.");
1587
+ }
1588
+
1589
+ // src/adapter-provider.ts
1590
+ function createConnectorAdapterProvider(options) {
1591
+ const providerId = options.id ?? "first-party";
1592
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
1593
+ const adapters = /* @__PURE__ */ new Map();
1594
+ for (const adapter of options.adapters) {
1595
+ adapters.set(adapter.manifest.kind, adapter);
1596
+ }
1597
+ return {
1598
+ id: providerId,
1599
+ kind: options.kind ?? "first_party",
1600
+ listConnectors: () => [...adapters.values()].map((adapter) => manifestToConnector(providerId, adapter)),
1601
+ async invokeAction(connection, request) {
1602
+ const adapter = adapters.get(connection.connectorId);
1603
+ if (!adapter) {
1604
+ throw new IntegrationError(`Connector adapter ${connection.connectorId} not found.`, "connector_not_found");
1605
+ }
1606
+ const capability = adapter.manifest.capabilities.find((candidate) => candidate.name === request.action);
1607
+ if (!capability) {
1608
+ throw new IntegrationError(`Capability ${request.action} is not defined by ${connection.connectorId}.`, "action_not_found");
1609
+ }
1610
+ const source = await options.resolveDataSource(connection);
1611
+ const invocation = {
1612
+ source,
1613
+ capabilityName: request.action,
1614
+ args: toRecord(request.input),
1615
+ idempotencyKey: request.idempotencyKey ?? `idem_${connection.id}_${request.action}_${now().getTime()}`,
1616
+ expectedEtag: typeof request.metadata?.expectedEtag === "string" ? request.metadata.expectedEtag : void 0,
1617
+ callSessionId: typeof request.metadata?.callSessionId === "string" ? request.metadata.callSessionId : void 0
1618
+ };
1619
+ if (capability.class === "read") {
1620
+ if (!adapter.executeRead) {
1621
+ throw new IntegrationError(`Connector ${connection.connectorId} does not implement reads.`, "action_not_found");
1622
+ }
1623
+ const result = await adapter.executeRead(invocation);
1624
+ return readResultToAction(request, result);
1625
+ }
1626
+ if (capability.class === "mutation") {
1627
+ if (!adapter.executeMutation) {
1628
+ throw new IntegrationError(`Connector ${connection.connectorId} does not implement mutations.`, "action_not_found");
1629
+ }
1630
+ const result = await adapter.executeMutation(invocation);
1631
+ return mutationResultToAction(request, result);
1632
+ }
1633
+ throw new IntegrationError(`Capability ${request.action} is not invokable as an action.`, "action_not_found");
1634
+ }
1635
+ };
1636
+ }
1637
+ function manifestToConnector(providerId, adapter) {
1638
+ const manifest = adapter.manifest;
1639
+ return {
1640
+ id: manifest.kind,
1641
+ providerId,
1642
+ title: manifest.displayName,
1643
+ category: mapCategory(manifest.category),
1644
+ auth: mapAuth(manifest.auth.kind),
1645
+ scopes: manifest.auth.kind === "oauth2" ? manifest.auth.scopes : [],
1646
+ actions: manifest.capabilities.filter((capability) => capability.class === "read" || capability.class === "mutation").map((capability) => ({
1647
+ id: capability.name,
1648
+ title: titleFromName(capability.name),
1649
+ risk: capability.class === "read" ? "read" : capability.externalEffect ? "destructive" : "write",
1650
+ requiredScopes: capability.requiredScopes ?? [],
1651
+ dataClass: inferDataClass(manifest.category),
1652
+ description: capability.description,
1653
+ approvalRequired: capability.class === "mutation",
1654
+ inputSchema: capability.parameters
1655
+ })),
1656
+ metadata: {
1657
+ source: "first-party-adapter",
1658
+ supportTier: "firstPartyExecutable",
1659
+ executable: true
1660
+ }
1661
+ };
1662
+ }
1663
+ function readResultToAction(request, result) {
1664
+ return {
1665
+ ok: true,
1666
+ action: request.action,
1667
+ output: result.data,
1668
+ metadata: {
1669
+ etag: result.etag,
1670
+ fetchedAt: result.fetchedAt
1671
+ }
1672
+ };
1673
+ }
1674
+ function mutationResultToAction(request, result) {
1675
+ if (result.status === "committed") {
1676
+ return {
1677
+ ok: true,
1678
+ action: request.action,
1679
+ output: result.data,
1680
+ metadata: {
1681
+ etagAfter: result.etagAfter,
1682
+ committedAt: result.committedAt,
1683
+ idempotentReplay: result.idempotentReplay
1684
+ }
1685
+ };
1686
+ }
1687
+ if (result.status === "conflict") {
1688
+ return {
1689
+ ok: false,
1690
+ action: request.action,
1691
+ output: {
1692
+ conflict: true,
1693
+ message: result.message,
1694
+ alternatives: result.alternatives,
1695
+ currentState: result.currentState
1696
+ }
1697
+ };
1698
+ }
1699
+ return {
1700
+ ok: false,
1701
+ action: request.action,
1702
+ output: {
1703
+ rateLimited: true,
1704
+ retryAfterMs: result.retryAfterMs,
1705
+ message: result.message
1706
+ }
1707
+ };
1708
+ }
1709
+ function mapAuth(kind) {
1710
+ if (kind === "oauth2") return "oauth2";
1711
+ if (kind === "api-key") return "api_key";
1712
+ if (kind === "none") return "none";
1713
+ return "custom";
1714
+ }
1715
+ function mapCategory(category) {
1716
+ if (category === "comms") return "chat";
1717
+ if (category === "spreadsheet") return "database";
1718
+ if (category === "doc") return "docs";
1719
+ if (category === "commerce") return "workflow";
1720
+ return category === "other" ? "other" : category;
1721
+ }
1722
+ function inferDataClass(category) {
1723
+ if (category === "commerce") return "sensitive";
1724
+ if (category === "webhook") return "internal";
1725
+ return "private";
1726
+ }
1727
+ function titleFromName(name) {
1728
+ return name.split(/[._-]/g).filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ");
1729
+ }
1730
+ function toRecord(input) {
1731
+ if (input && typeof input === "object" && !Array.isArray(input)) return input;
1732
+ return {};
1733
+ }
1734
+
1735
+ // src/credentials.ts
1736
+ var InMemoryIntegrationSecretStore = class {
1737
+ secrets = /* @__PURE__ */ new Map();
1738
+ get(ref) {
1739
+ return this.secrets.get(secretKey(ref));
1740
+ }
1741
+ put(ref, credentials) {
1742
+ this.secrets.set(secretKey(ref), credentials);
1743
+ }
1744
+ delete(ref) {
1745
+ this.secrets.delete(secretKey(ref));
1746
+ }
1747
+ };
1748
+ function createConnectionCredentialResolver(options) {
1749
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
1750
+ return async function resolveDataSource(connection) {
1751
+ const credentials = await resolveConnectionCredentials(connection, {
1752
+ secrets: options.secrets,
1753
+ connections: options.connections,
1754
+ adapters: options.adapters,
1755
+ now,
1756
+ markConnectionError: options.markConnectionError
1757
+ });
1758
+ return {
1759
+ id: connection.id,
1760
+ projectId: String(connection.metadata?.projectId ?? connection.owner.id),
1761
+ publishedAgentId: typeof connection.metadata?.publishedAgentId === "string" ? connection.metadata.publishedAgentId : null,
1762
+ kind: connection.connectorId,
1763
+ label: connection.account?.displayName ?? connection.account?.email ?? connection.connectorId,
1764
+ consistencyModel: typeof connection.metadata?.consistencyModel === "string" ? connection.metadata.consistencyModel : "authoritative",
1765
+ scopes: connection.grantedScopes,
1766
+ metadata: connection.metadata ?? {},
1767
+ credentials,
1768
+ status: connection.status === "active" ? "active" : connection.status === "revoked" ? "revoked" : "error"
1769
+ };
1770
+ };
1771
+ }
1772
+ async function resolveConnectionCredentials(input, options) {
1773
+ if (input.status !== "active") throw new Error(`Connection ${input.id} is ${input.status}.`);
1774
+ if (!input.secretRef) return { kind: "none" };
1775
+ const current = await options.secrets.get(input.secretRef);
1776
+ if (!current) throw new Error(`Secret ${input.secretRef.provider}/${input.secretRef.id} not found.`);
1777
+ if (!isExpiredOauth(current, options.now ?? (() => /* @__PURE__ */ new Date()))) return current;
1778
+ const adapter = options.adapters?.find((candidate) => candidate.manifest.kind === input.connectorId);
1779
+ if (!adapter?.refreshToken) return current;
1780
+ try {
1781
+ const refreshed = await adapter.refreshToken(current);
1782
+ await options.secrets.put(input.secretRef, refreshed);
1783
+ if (options.connections) {
1784
+ await options.connections.put({
1785
+ ...input,
1786
+ status: "active",
1787
+ updatedAt: (options.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
1788
+ expiresAt: refreshed.kind === "oauth2" && refreshed.expiresAt ? new Date(refreshed.expiresAt).toISOString() : input.expiresAt
1789
+ });
1790
+ }
1791
+ return refreshed;
1792
+ } catch (error) {
1793
+ const err = error instanceof Error ? error : new Error("Credential refresh failed.");
1794
+ await options.markConnectionError?.(input, err);
1795
+ if (options.connections) {
1796
+ await options.connections.put({
1797
+ ...input,
1798
+ status: "expired",
1799
+ updatedAt: (options.now?.() ?? /* @__PURE__ */ new Date()).toISOString()
1800
+ });
1801
+ }
1802
+ throw err;
1803
+ }
1804
+ }
1805
+ function createCredentialBackedAdapterProvider(options) {
1806
+ return createConnectorAdapterProvider({
1807
+ ...options,
1808
+ resolveDataSource: createConnectionCredentialResolver(options)
1809
+ });
1810
+ }
1811
+ async function revokeConnection(input) {
1812
+ if (input.connection.secretRef) await input.secrets?.delete?.(input.connection.secretRef);
1813
+ const revoked = {
1814
+ ...input.connection,
1815
+ status: "revoked",
1816
+ updatedAt: (input.now?.() ?? /* @__PURE__ */ new Date()).toISOString()
1817
+ };
1818
+ await input.connections?.put(revoked);
1819
+ return revoked;
1820
+ }
1821
+ function isExpiredOauth(credentials, now) {
1822
+ return credentials.kind === "oauth2" && typeof credentials.expiresAt === "number" && credentials.expiresAt <= now().getTime() && Boolean(credentials.refreshToken);
1823
+ }
1824
+ function secretKey(ref) {
1825
+ return `${ref.provider}:${ref.id}`;
1826
+ }
1827
+
1828
+ // src/events.ts
1829
+ var InMemoryIntegrationEventStore = class {
1830
+ events = /* @__PURE__ */ new Map();
1831
+ providerIds = /* @__PURE__ */ new Set();
1832
+ put(event) {
1833
+ this.events.set(event.id, event);
1834
+ if (event.providerEventId) this.providerIds.add(providerKey(event.sourceId, event.providerEventId));
1835
+ }
1836
+ hasProviderEvent(sourceId, providerEventId) {
1837
+ return this.providerIds.has(providerKey(sourceId, providerEventId));
1838
+ }
1839
+ list() {
1840
+ return [...this.events.values()];
1841
+ }
1842
+ };
1843
+ async function receiveIntegrationWebhook(input) {
1844
+ if (!input.adapter.handleInboundEvent) {
1845
+ return { status: 405, body: { ok: false, error: "Connector does not support inbound webhooks." }, received: [], duplicates: [] };
1846
+ }
1847
+ const signature = input.adapter.verifySignature?.({
1848
+ rawBody: input.rawBody,
1849
+ headers: input.headers,
1850
+ source: input.source
1851
+ });
1852
+ if (signature && !signature.valid) {
1853
+ return { status: 401, body: { ok: false, error: signature.reason ?? "Invalid webhook signature." }, received: [], duplicates: [] };
1854
+ }
1855
+ const handled = await input.adapter.handleInboundEvent({
1856
+ source: input.source,
1857
+ rawBody: input.rawBody,
1858
+ headers: input.headers
1859
+ });
1860
+ const received = [];
1861
+ const duplicates = [];
1862
+ for (const inbound of handled.events) {
1863
+ const event = storedEvent(input.source, inbound, input.now ?? (() => /* @__PURE__ */ new Date()));
1864
+ if (event.providerEventId && await input.store.hasProviderEvent(event.sourceId, event.providerEventId)) {
1865
+ duplicates.push(event);
1866
+ continue;
1867
+ }
1868
+ await input.store.put(event);
1869
+ received.push(event);
1870
+ await dispatchStoredEvent(event, input.source, input.workflowRuntime);
1871
+ }
1872
+ return {
1873
+ status: handled.response?.status ?? 200,
1874
+ body: handled.response?.body ?? { received: true, count: received.length, duplicateCount: duplicates.length },
1875
+ headers: handled.response?.headers,
1876
+ received,
1877
+ duplicates
1878
+ };
1879
+ }
1880
+ function storedEventToTriggerEvent(event, source) {
1881
+ return {
1882
+ id: event.id,
1883
+ providerId: String(source.metadata.providerId ?? "first-party"),
1884
+ connectorId: event.connectorId,
1885
+ connectionId: source.id,
1886
+ trigger: event.eventType,
1887
+ occurredAt: event.receivedAt,
1888
+ payload: event.payload,
1889
+ metadata: {
1890
+ providerEventId: event.providerEventId,
1891
+ sourceId: event.sourceId,
1892
+ ...event.metadata
1893
+ }
1894
+ };
1895
+ }
1896
+ async function dispatchStoredEvent(event, source, workflowRuntime) {
1897
+ if (!workflowRuntime) return;
1898
+ await workflowRuntime.dispatchEvent(storedEventToTriggerEvent(event, source), () => void 0);
1899
+ }
1900
+ function storedEvent(source, event, now) {
1901
+ return {
1902
+ id: `evt_${source.id}_${event.providerEventId ?? `${event.eventType}_${now().getTime()}`}`,
1903
+ sourceId: source.id,
1904
+ connectorId: source.kind,
1905
+ eventType: event.eventType,
1906
+ providerEventId: event.providerEventId,
1907
+ receivedAt: now().toISOString(),
1908
+ payload: event.payload
1909
+ };
1910
+ }
1911
+ function providerKey(sourceId, providerEventId) {
1912
+ return `${sourceId}:${providerEventId}`;
1913
+ }
1914
+
1915
+ // src/guard.ts
1916
+ import { createHash as createHash2 } from "crypto";
1917
+ var InMemoryIntegrationIdempotencyStore = class {
1918
+ records = /* @__PURE__ */ new Map();
1919
+ get(key) {
1920
+ return this.records.get(key);
1921
+ }
1922
+ put(record) {
1923
+ this.records.set(record.key, record);
1924
+ }
1925
+ };
1926
+ var DefaultIntegrationActionGuard = class {
1927
+ idempotency;
1928
+ audit;
1929
+ rateLimiter;
1930
+ now;
1931
+ constructor(options = {}) {
1932
+ this.idempotency = options.idempotency;
1933
+ this.audit = options.audit;
1934
+ this.rateLimiter = options.rateLimiter;
1935
+ this.now = options.now ?? (() => /* @__PURE__ */ new Date());
1936
+ }
1937
+ async invokeAction(ctx, proceed) {
1938
+ const idempotencyKey = ctx.request.idempotencyKey;
1939
+ const requestHash = hashRequest(ctx);
1940
+ if (idempotencyKey && this.idempotency) {
1941
+ const existing = await this.idempotency.get(idempotencyKey);
1942
+ if (existing) {
1943
+ if (existing.requestHash !== requestHash) {
1944
+ return {
1945
+ ok: false,
1946
+ action: ctx.request.action,
1947
+ output: { idempotencyConflict: true, message: "Idempotency key was reused with different integration input." }
1948
+ };
1949
+ }
1950
+ return {
1951
+ ...existing.result,
1952
+ metadata: { ...existing.result.metadata ?? {}, idempotentReplay: true }
1953
+ };
1954
+ }
1955
+ }
1956
+ if (ctx.request.dryRun && ctx.action?.risk !== "read") {
1957
+ const result = {
1958
+ ok: true,
1959
+ action: ctx.request.action,
1960
+ output: { dryRun: true },
1961
+ metadata: { dryRun: true }
1962
+ };
1963
+ await this.writeIdempotency(idempotencyKey, requestHash, result);
1964
+ return result;
1965
+ }
1966
+ const rateLimit = await this.rateLimiter?.check(ctx);
1967
+ if (rateLimit && !rateLimit.allowed) {
1968
+ return {
1969
+ ok: false,
1970
+ action: ctx.request.action,
1971
+ output: { rateLimited: true, retryAfterMs: rateLimit.retryAfterMs, message: rateLimit.reason ?? "Integration rate limit exceeded." }
1972
+ };
1973
+ }
1974
+ try {
1975
+ const result = await proceed();
1976
+ await this.writeIdempotency(idempotencyKey, requestHash, result);
1977
+ await this.audit?.record(createIntegrationAuditEvent({
1978
+ type: result.ok ? "action.invoked" : "action.failed",
1979
+ actor: ctx.connection.owner,
1980
+ connectionId: ctx.connection.id,
1981
+ providerId: ctx.connection.providerId,
1982
+ connectorId: ctx.connection.connectorId,
1983
+ action: ctx.request.action,
1984
+ risk: ctx.action?.risk,
1985
+ dataClass: ctx.action?.dataClass,
1986
+ ok: result.ok,
1987
+ metadata: { idempotencyKey, externalId: result.externalId, warnings: result.warnings },
1988
+ now: this.now
1989
+ }));
1990
+ return result;
1991
+ } catch (error) {
1992
+ await this.audit?.record(createIntegrationAuditEvent({
1993
+ type: "action.failed",
1994
+ actor: ctx.connection.owner,
1995
+ connectionId: ctx.connection.id,
1996
+ providerId: ctx.connection.providerId,
1997
+ connectorId: ctx.connection.connectorId,
1998
+ action: ctx.request.action,
1999
+ risk: ctx.action?.risk,
2000
+ dataClass: ctx.action?.dataClass,
2001
+ ok: false,
2002
+ message: error instanceof Error ? error.message : "Integration action failed.",
2003
+ metadata: { idempotencyKey },
2004
+ now: this.now
2005
+ }));
2006
+ throw error;
2007
+ }
2008
+ }
2009
+ async writeIdempotency(key, requestHash, result) {
2010
+ if (!key || !this.idempotency) return;
2011
+ await this.idempotency.put({
2012
+ key,
2013
+ requestHash,
2014
+ result,
2015
+ createdAt: this.now().toISOString()
2016
+ });
2017
+ }
2018
+ };
2019
+ function createDefaultIntegrationActionGuard(options = {}) {
2020
+ return new DefaultIntegrationActionGuard(options);
2021
+ }
2022
+ function hashRequest(ctx) {
2023
+ return createHash2("sha256").update(JSON.stringify({
2024
+ connectionId: ctx.connection.id,
2025
+ action: ctx.request.action,
2026
+ input: ctx.request.input ?? null,
2027
+ dryRun: ctx.request.dryRun ?? false
2028
+ })).digest("base64url");
2029
+ }
2030
+
2031
+ // src/healthcheck.ts
2032
+ var InMemoryIntegrationHealthcheckStore = class {
2033
+ results = /* @__PURE__ */ new Map();
2034
+ put(result) {
2035
+ this.results.set(result.connectionId, result);
2036
+ }
2037
+ get(connectionId) {
2038
+ return this.results.get(connectionId);
2039
+ }
2040
+ list() {
2041
+ return [...this.results.values()];
2042
+ }
2043
+ };
2044
+ async function runIntegrationHealthcheck(input) {
2045
+ const now = input.now ?? (() => /* @__PURE__ */ new Date());
2046
+ const checkedAt = now().toISOString();
2047
+ const connector = input.connector ?? input.registry?.byId.get(input.connection.connectorId)?.connector;
2048
+ const checks = [];
2049
+ checks.push(connectionStatusCheck(input.connection, now));
2050
+ if (!connector) {
2051
+ checks.push({ id: "connector-known", status: "unknown", message: `Connector ${input.connection.connectorId} is not in the registry.` });
2052
+ } else {
2053
+ checks.push(connectorExecutableCheck(connector));
2054
+ checks.push(scopeShapeCheck(input.connection, connector));
2055
+ if (input.test && input.connection.status === "active") {
2056
+ checks.push(await liveHealthcheck(input.connection, connector, input.test));
2057
+ }
2058
+ }
2059
+ const result = {
2060
+ connectionId: input.connection.id,
2061
+ providerId: input.connection.providerId,
2062
+ connectorId: input.connection.connectorId,
2063
+ status: rollupHealthStatus(checks),
2064
+ checkedAt,
2065
+ checks
2066
+ };
2067
+ await input.audit?.record(createIntegrationAuditEvent({
2068
+ type: "healthcheck.completed",
2069
+ actor: input.connection.owner,
2070
+ connectionId: input.connection.id,
2071
+ providerId: input.connection.providerId,
2072
+ connectorId: input.connection.connectorId,
2073
+ ok: result.status === "healthy",
2074
+ message: result.status,
2075
+ metadata: { checks: checks.map((check) => ({ id: check.id, status: check.status, message: check.message })) },
2076
+ occurredAt: checkedAt
2077
+ }));
2078
+ return result;
2079
+ }
2080
+ async function runIntegrationHealthchecks(input) {
2081
+ const results = [];
2082
+ for (const connection of input.connections) {
2083
+ const result = await runIntegrationHealthcheck({
2084
+ connection,
2085
+ registry: input.registry,
2086
+ test: input.test,
2087
+ audit: input.audit,
2088
+ now: input.now
2089
+ });
2090
+ await input.store?.put(result);
2091
+ results.push(result);
2092
+ }
2093
+ return results;
2094
+ }
2095
+ function healthcheckRequest(action = "healthcheck") {
2096
+ return {
2097
+ connectionId: "__healthcheck__",
2098
+ action,
2099
+ input: {},
2100
+ dryRun: true,
2101
+ metadata: { healthcheck: true }
2102
+ };
2103
+ }
2104
+ function connectionStatusCheck(connection, now) {
2105
+ if (connection.status !== "active") {
2106
+ return { id: "connection-active", status: "unhealthy", message: `Connection is ${connection.status}.` };
2107
+ }
2108
+ if (connection.expiresAt && Date.parse(connection.expiresAt) <= now().getTime()) {
2109
+ return { id: "connection-active", status: "unhealthy", message: "Connection credentials are expired." };
2110
+ }
2111
+ return { id: "connection-active", status: "healthy", message: "Connection is active." };
2112
+ }
2113
+ function connectorExecutableCheck(connector) {
2114
+ const executable = connector.actions.length > 0 || (connector.triggers?.length ?? 0) > 0;
2115
+ if (!executable) {
2116
+ return { id: "connector-executable", status: "degraded", message: `${connector.title} is catalog-only.` };
2117
+ }
2118
+ return { id: "connector-executable", status: "healthy", message: `${connector.title} has executable actions or triggers.` };
1276
2119
  }
1277
- function slug(value) {
1278
- return value.trim().toLowerCase().replace(/&/g, "and").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
2120
+ function scopeShapeCheck(connection, connector) {
2121
+ const declaredScopes = new Set(connector.scopes);
2122
+ const undeclared = connection.grantedScopes.filter((scope) => !declaredScopes.has(scope));
2123
+ if (connector.scopes.length === 0 && connection.grantedScopes.length > 0) {
2124
+ return { id: "scope-shape", status: "unknown", message: "Connector does not declare a scope catalog.", metadata: { grantedScopes: connection.grantedScopes } };
2125
+ }
2126
+ if (undeclared.length > 0) {
2127
+ return { id: "scope-shape", status: "degraded", message: "Connection has scopes not declared by the connector.", metadata: { undeclared } };
2128
+ }
2129
+ return { id: "scope-shape", status: "healthy", message: "Granted scopes match the connector shape." };
1279
2130
  }
1280
- function unique(values) {
1281
- return [...new Set(values)];
2131
+ async function liveHealthcheck(connection, connector, test) {
2132
+ try {
2133
+ const result = await test(connection, connector);
2134
+ const ok = typeof result === "boolean" ? result : result.ok;
2135
+ return {
2136
+ id: "provider-live-test",
2137
+ status: ok ? "healthy" : "unhealthy",
2138
+ message: ok ? "Provider live test passed." : "Provider live test failed.",
2139
+ metadata: typeof result === "boolean" ? void 0 : { action: result.action, warnings: result.warnings }
2140
+ };
2141
+ } catch (error) {
2142
+ return {
2143
+ id: "provider-live-test",
2144
+ status: "unhealthy",
2145
+ message: error instanceof Error ? error.message : "Provider live test failed."
2146
+ };
2147
+ }
2148
+ }
2149
+ function rollupHealthStatus(checks) {
2150
+ if (checks.some((check) => check.status === "unhealthy")) return "unhealthy";
2151
+ if (checks.some((check) => check.status === "degraded")) return "degraded";
2152
+ if (checks.some((check) => check.status === "unknown")) return "unknown";
2153
+ return "healthy";
1282
2154
  }
1283
2155
 
1284
2156
  // src/connectors/types.ts
@@ -1335,7 +2207,7 @@ function assertValidConnectorManifest(manifest) {
1335
2207
  }
1336
2208
 
1337
2209
  // src/connectors/oauth.ts
1338
- import { createHash, randomBytes } from "crypto";
2210
+ import { createHash as createHash3, randomBytes } from "crypto";
1339
2211
  var PENDING_TTL_MS = 10 * 60 * 1e3;
1340
2212
  var InMemoryOAuthFlowStore = class {
1341
2213
  pendingFlows = /* @__PURE__ */ new Map();
@@ -1363,7 +2235,7 @@ function startOAuthFlow(input) {
1363
2235
  const now = input.now ?? Date.now();
1364
2236
  store.sweep?.(now);
1365
2237
  const codeVerifier = base64Url(randomBytes(48));
1366
- const codeChallenge = base64Url(createHash("sha256").update(codeVerifier).digest());
2238
+ const codeChallenge = base64Url(createHash3("sha256").update(codeVerifier).digest());
1367
2239
  const state = base64Url(randomBytes(24));
1368
2240
  store.put(state, {
1369
2241
  codeVerifier,
@@ -1828,7 +2700,7 @@ function readMetaString(meta, key) {
1828
2700
  }
1829
2701
 
1830
2702
  // src/connectors/adapters/google-sheets.ts
1831
- import { createHash as createHash2 } from "crypto";
2703
+ import { createHash as createHash4 } from "crypto";
1832
2704
  var SCOPES2 = ["https://www.googleapis.com/auth/spreadsheets"];
1833
2705
  var AUTH_URL2 = "https://accounts.google.com/o/oauth2/v2/auth";
1834
2706
  var TOKEN_URL2 = "https://oauth2.googleapis.com/token";
@@ -2102,7 +2974,7 @@ async function fetchAllRows(accessToken, meta) {
2102
2974
  function rowFingerprint(values) {
2103
2975
  const keys = Object.keys(values).sort();
2104
2976
  const blob = keys.map((k) => `${k}=${values[k]}`).join("");
2105
- return createHash2("sha256").update(blob).digest("hex").slice(0, 16);
2977
+ return createHash4("sha256").update(blob).digest("hex").slice(0, 16);
2106
2978
  }
2107
2979
  function matchesPredicate(row, predicate) {
2108
2980
  for (const [k, v] of Object.entries(predicate)) {
@@ -4588,7 +5460,7 @@ function unique2(values) {
4588
5460
  }
4589
5461
 
4590
5462
  // src/policy.ts
4591
- import { randomUUID } from "crypto";
5463
+ import { randomUUID as randomUUID2 } from "crypto";
4592
5464
  var StaticIntegrationPolicyEngine = class {
4593
5465
  rules;
4594
5466
  defaultReadEffect;
@@ -4631,7 +5503,7 @@ function buildApprovalRequest(ctx, reason, requestedAt) {
4631
5503
  throw new Error("Cannot build approval request without an action descriptor.");
4632
5504
  }
4633
5505
  return {
4634
- id: `approval_${randomUUID()}`,
5506
+ id: `approval_${randomUUID2()}`,
4635
5507
  connectionId: ctx.connection.id,
4636
5508
  providerId: ctx.connection.providerId,
4637
5509
  connectorId: ctx.connection.connectorId,
@@ -4647,7 +5519,7 @@ function buildApprovalRequest(ctx, reason, requestedAt) {
4647
5519
  function redactApprovalRequest(request) {
4648
5520
  return {
4649
5521
  ...request,
4650
- inputPreview: redactUnknown(request.inputPreview)
5522
+ inputPreview: redactUnknown2(request.inputPreview)
4651
5523
  };
4652
5524
  }
4653
5525
  function ruleMatches(rule, ctx) {
@@ -4671,17 +5543,17 @@ function defaultReason(effect, risk) {
4671
5543
  return `${risk} integration action requires approval by default policy.`;
4672
5544
  }
4673
5545
  function previewInput(input) {
4674
- return redactUnknown(input);
5546
+ return redactUnknown2(input);
4675
5547
  }
4676
- function redactUnknown(value) {
4677
- if (Array.isArray(value)) return value.map(redactUnknown);
5548
+ function redactUnknown2(value) {
5549
+ if (Array.isArray(value)) return value.map(redactUnknown2);
4678
5550
  if (!value || typeof value !== "object") return value;
4679
5551
  const out = {};
4680
5552
  for (const [key, child] of Object.entries(value)) {
4681
5553
  if (/token|secret|password|authorization|api[_-]?key|credential/i.test(key)) {
4682
5554
  out[key] = "[REDACTED]";
4683
5555
  } else {
4684
- out[key] = redactUnknown(child);
5556
+ out[key] = redactUnknown2(child);
4685
5557
  }
4686
5558
  }
4687
5559
  return out;
@@ -4745,13 +5617,13 @@ function redactInvocationEnvelope(envelope) {
4745
5617
  return {
4746
5618
  ...envelope,
4747
5619
  capabilityToken: "[REDACTED]",
4748
- input: redactUnknown2(envelope.input)
5620
+ input: redactUnknown3(envelope.input)
4749
5621
  };
4750
5622
  }
4751
5623
  function redactCapability(capability) {
4752
5624
  return {
4753
5625
  ...capability,
4754
- metadata: redactUnknown2(capability.metadata)
5626
+ metadata: redactUnknown3(capability.metadata)
4755
5627
  };
4756
5628
  }
4757
5629
  function normalizeIntegrationResult(result) {
@@ -4779,15 +5651,40 @@ function normalizeIntegrationResult(result) {
4779
5651
  metadata: result.metadata
4780
5652
  };
4781
5653
  }
4782
- function redactUnknown2(value) {
4783
- if (Array.isArray(value)) return value.map(redactUnknown2);
5654
+ async function dispatchIntegrationInvocation(envelope, options) {
5655
+ try {
5656
+ validateIntegrationInvocationEnvelope(envelope, options);
5657
+ const result = await options.hub.invokeWithCapability(
5658
+ envelope.capabilityToken,
5659
+ invocationRequestFromEnvelope(envelope)
5660
+ );
5661
+ return normalizeIntegrationResult(result);
5662
+ } catch (error) {
5663
+ return {
5664
+ status: "failed",
5665
+ action: typeof envelope?.action === "string" ? envelope.action : "unknown",
5666
+ error: error instanceof Error ? error.message : "Integration invocation failed."
5667
+ };
5668
+ }
5669
+ }
5670
+ var IntegrationSandboxHost = class {
5671
+ options;
5672
+ constructor(options) {
5673
+ this.options = options;
5674
+ }
5675
+ dispatch(envelope) {
5676
+ return dispatchIntegrationInvocation(envelope, this.options);
5677
+ }
5678
+ };
5679
+ function redactUnknown3(value) {
5680
+ if (Array.isArray(value)) return value.map(redactUnknown3);
4784
5681
  if (!value || typeof value !== "object") return value;
4785
5682
  const out = {};
4786
5683
  for (const [key, child] of Object.entries(value)) {
4787
5684
  if (/token|secret|password|authorization|api[_-]?key|credential/i.test(key)) {
4788
5685
  out[key] = "[REDACTED]";
4789
5686
  } else {
4790
- out[key] = redactUnknown2(child);
5687
+ out[key] = redactUnknown3(child);
4791
5688
  }
4792
5689
  }
4793
5690
  return out;
@@ -4799,152 +5696,6 @@ function isPlainRecord(value) {
4799
5696
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4800
5697
  }
4801
5698
 
4802
- // src/adapter-provider.ts
4803
- function createConnectorAdapterProvider(options) {
4804
- const providerId = options.id ?? "first-party";
4805
- const now = options.now ?? (() => /* @__PURE__ */ new Date());
4806
- const adapters = /* @__PURE__ */ new Map();
4807
- for (const adapter of options.adapters) {
4808
- adapters.set(adapter.manifest.kind, adapter);
4809
- }
4810
- return {
4811
- id: providerId,
4812
- kind: options.kind ?? "first_party",
4813
- listConnectors: () => [...adapters.values()].map((adapter) => manifestToConnector(providerId, adapter)),
4814
- async invokeAction(connection, request) {
4815
- const adapter = adapters.get(connection.connectorId);
4816
- if (!adapter) {
4817
- throw new IntegrationError(`Connector adapter ${connection.connectorId} not found.`, "connector_not_found");
4818
- }
4819
- const capability = adapter.manifest.capabilities.find((candidate) => candidate.name === request.action);
4820
- if (!capability) {
4821
- throw new IntegrationError(`Capability ${request.action} is not defined by ${connection.connectorId}.`, "action_not_found");
4822
- }
4823
- const source = await options.resolveDataSource(connection);
4824
- const invocation = {
4825
- source,
4826
- capabilityName: request.action,
4827
- args: toRecord(request.input),
4828
- idempotencyKey: request.idempotencyKey ?? `idem_${connection.id}_${request.action}_${now().getTime()}`,
4829
- expectedEtag: typeof request.metadata?.expectedEtag === "string" ? request.metadata.expectedEtag : void 0,
4830
- callSessionId: typeof request.metadata?.callSessionId === "string" ? request.metadata.callSessionId : void 0
4831
- };
4832
- if (capability.class === "read") {
4833
- if (!adapter.executeRead) {
4834
- throw new IntegrationError(`Connector ${connection.connectorId} does not implement reads.`, "action_not_found");
4835
- }
4836
- const result = await adapter.executeRead(invocation);
4837
- return readResultToAction(request, result);
4838
- }
4839
- if (capability.class === "mutation") {
4840
- if (!adapter.executeMutation) {
4841
- throw new IntegrationError(`Connector ${connection.connectorId} does not implement mutations.`, "action_not_found");
4842
- }
4843
- const result = await adapter.executeMutation(invocation);
4844
- return mutationResultToAction(request, result);
4845
- }
4846
- throw new IntegrationError(`Capability ${request.action} is not invokable as an action.`, "action_not_found");
4847
- }
4848
- };
4849
- }
4850
- function manifestToConnector(providerId, adapter) {
4851
- const manifest = adapter.manifest;
4852
- return {
4853
- id: manifest.kind,
4854
- providerId,
4855
- title: manifest.displayName,
4856
- category: mapCategory(manifest.category),
4857
- auth: mapAuth(manifest.auth.kind),
4858
- scopes: manifest.auth.kind === "oauth2" ? manifest.auth.scopes : [],
4859
- actions: manifest.capabilities.filter((capability) => capability.class === "read" || capability.class === "mutation").map((capability) => ({
4860
- id: capability.name,
4861
- title: titleFromName(capability.name),
4862
- risk: capability.class === "read" ? "read" : capability.externalEffect ? "destructive" : "write",
4863
- requiredScopes: capability.requiredScopes ?? [],
4864
- dataClass: inferDataClass(manifest.category),
4865
- description: capability.description,
4866
- approvalRequired: capability.class === "mutation",
4867
- inputSchema: capability.parameters
4868
- })),
4869
- metadata: {
4870
- source: "first-party-adapter",
4871
- supportTier: "firstPartyExecutable",
4872
- executable: true
4873
- }
4874
- };
4875
- }
4876
- function readResultToAction(request, result) {
4877
- return {
4878
- ok: true,
4879
- action: request.action,
4880
- output: result.data,
4881
- metadata: {
4882
- etag: result.etag,
4883
- fetchedAt: result.fetchedAt
4884
- }
4885
- };
4886
- }
4887
- function mutationResultToAction(request, result) {
4888
- if (result.status === "committed") {
4889
- return {
4890
- ok: true,
4891
- action: request.action,
4892
- output: result.data,
4893
- metadata: {
4894
- etagAfter: result.etagAfter,
4895
- committedAt: result.committedAt,
4896
- idempotentReplay: result.idempotentReplay
4897
- }
4898
- };
4899
- }
4900
- if (result.status === "conflict") {
4901
- return {
4902
- ok: false,
4903
- action: request.action,
4904
- output: {
4905
- conflict: true,
4906
- message: result.message,
4907
- alternatives: result.alternatives,
4908
- currentState: result.currentState
4909
- }
4910
- };
4911
- }
4912
- return {
4913
- ok: false,
4914
- action: request.action,
4915
- output: {
4916
- rateLimited: true,
4917
- retryAfterMs: result.retryAfterMs,
4918
- message: result.message
4919
- }
4920
- };
4921
- }
4922
- function mapAuth(kind) {
4923
- if (kind === "oauth2") return "oauth2";
4924
- if (kind === "api-key") return "api_key";
4925
- if (kind === "none") return "none";
4926
- return "custom";
4927
- }
4928
- function mapCategory(category) {
4929
- if (category === "comms") return "chat";
4930
- if (category === "spreadsheet") return "database";
4931
- if (category === "doc") return "docs";
4932
- if (category === "commerce") return "workflow";
4933
- return category === "other" ? "other" : category;
4934
- }
4935
- function inferDataClass(category) {
4936
- if (category === "commerce") return "sensitive";
4937
- if (category === "webhook") return "internal";
4938
- return "private";
4939
- }
4940
- function titleFromName(name) {
4941
- return name.split(/[._-]/g).filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ");
4942
- }
4943
- function toRecord(input) {
4944
- if (input && typeof input === "object" && !Array.isArray(input)) return input;
4945
- return {};
4946
- }
4947
-
4948
5699
  // src/importers.ts
4949
5700
  var HTTP_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete"]);
4950
5701
  function importOpenApiConnector(document, options) {
@@ -5244,6 +5995,295 @@ function unique4(values) {
5244
5995
  return [...new Set(values)];
5245
5996
  }
5246
5997
 
5998
+ // src/runtime.ts
5999
+ var InMemoryIntegrationGrantStore = class {
6000
+ grants = /* @__PURE__ */ new Map();
6001
+ get(grantId) {
6002
+ return this.grants.get(grantId);
6003
+ }
6004
+ put(grant) {
6005
+ this.grants.set(grant.id, grant);
6006
+ }
6007
+ listByManifest(manifestId, grantee) {
6008
+ return [...this.grants.values()].filter(
6009
+ (grant) => grant.manifestId === manifestId && (!grantee || sameActor(grant.grantee, grantee))
6010
+ );
6011
+ }
6012
+ listByGrantee(grantee) {
6013
+ return [...this.grants.values()].filter((grant) => sameActor(grant.grantee, grantee));
6014
+ }
6015
+ delete(grantId) {
6016
+ this.grants.delete(grantId);
6017
+ }
6018
+ };
6019
+ var IntegrationRuntime = class {
6020
+ hub;
6021
+ grants;
6022
+ now;
6023
+ constructor(options) {
6024
+ this.hub = options.hub;
6025
+ this.grants = options.grants ?? new InMemoryIntegrationGrantStore();
6026
+ this.now = options.now ?? (() => /* @__PURE__ */ new Date());
6027
+ }
6028
+ async registry() {
6029
+ return this.hub.listRegistry();
6030
+ }
6031
+ async resolveManifest(manifest, owner) {
6032
+ const registry = await this.registry();
6033
+ const connections = await this.hub.listConnections(owner);
6034
+ const resolutions = manifest.requirements.map(
6035
+ (requirement) => resolveRequirement(requirement, owner, registry, connections)
6036
+ );
6037
+ return {
6038
+ manifest,
6039
+ owner,
6040
+ ready: resolutions.filter((resolution) => resolution.status === "ready"),
6041
+ missing: resolutions.filter((resolution) => resolution.status !== "ready" && !resolution.requirement.optional),
6042
+ optionalMissing: resolutions.filter((resolution) => resolution.status !== "ready" && resolution.requirement.optional === true)
6043
+ };
6044
+ }
6045
+ async createGrants(input) {
6046
+ const resolution = await this.resolveManifest(input.manifest, input.owner);
6047
+ if (resolution.missing.length > 0) {
6048
+ throw new Error(`Cannot create integration grants; missing requirements: ${resolution.missing.map((r) => r.requirement.id).join(", ")}`);
6049
+ }
6050
+ const now = this.now().toISOString();
6051
+ const grants = resolution.ready.map((ready) => ({
6052
+ id: `grant_${input.manifest.id}_${ready.requirement.id}_${ready.connection.id}`,
6053
+ manifestId: input.manifest.id,
6054
+ requirementId: ready.requirement.id,
6055
+ owner: input.owner,
6056
+ grantee: input.grantee,
6057
+ connectionId: ready.connection.id,
6058
+ connectorId: ready.connector.id,
6059
+ scopes: requiredScopes(ready.requirement, ready.connector),
6060
+ allowedActions: requiredActions(ready.requirement, ready.connector),
6061
+ allowedTriggers: requiredTriggers(ready.requirement, ready.connector),
6062
+ status: "active",
6063
+ createdAt: now,
6064
+ updatedAt: now,
6065
+ metadata: input.metadata
6066
+ }));
6067
+ for (const grant of grants) await this.grants.put(grant);
6068
+ return grants;
6069
+ }
6070
+ async buildSandboxBundle(input) {
6071
+ const grants = (await this.grants.listByManifest(input.manifestId, input.grantee)).filter((grant) => grant.status === "active");
6072
+ const registry = await this.registry();
6073
+ const bindings = [];
6074
+ const connectors = [];
6075
+ let expiresAt = "";
6076
+ for (const grant of grants) {
6077
+ const entry = registry.byId.get(grant.connectorId);
6078
+ if (!entry) continue;
6079
+ const connector = {
6080
+ ...entry.connector,
6081
+ actions: entry.connector.actions.filter((action) => grant.allowedActions.includes(action.id)),
6082
+ triggers: entry.connector.triggers?.filter((trigger) => grant.allowedTriggers.includes(trigger.id)),
6083
+ scopes: entry.connector.scopes.filter((scope) => grant.scopes.includes(scope))
6084
+ };
6085
+ const capability = await this.hub.issueCapability({
6086
+ subject: input.subject,
6087
+ connectionId: grant.connectionId,
6088
+ scopes: grant.scopes,
6089
+ allowedActions: grant.allowedActions,
6090
+ ttlMs: input.ttlMs,
6091
+ metadata: {
6092
+ manifestId: grant.manifestId,
6093
+ grantId: grant.id,
6094
+ requirementId: grant.requirementId
6095
+ }
6096
+ });
6097
+ bindings.push({
6098
+ requirementId: grant.requirementId,
6099
+ connectorId: grant.connectorId,
6100
+ connectionId: grant.connectionId,
6101
+ grantId: grant.id,
6102
+ scopes: grant.scopes,
6103
+ allowedActions: grant.allowedActions,
6104
+ allowedTriggers: grant.allowedTriggers,
6105
+ capability
6106
+ });
6107
+ connectors.push(connector);
6108
+ expiresAt = capability.capability.expiresAt;
6109
+ }
6110
+ return {
6111
+ manifestId: input.manifestId,
6112
+ subject: input.subject,
6113
+ capabilities: bindings,
6114
+ connectors,
6115
+ tools: buildIntegrationToolCatalog(connectors),
6116
+ expiresAt
6117
+ };
6118
+ }
6119
+ };
6120
+ function createIntegrationRuntime(options) {
6121
+ return new IntegrationRuntime(options);
6122
+ }
6123
+ function resolveRequirement(requirement, owner, registry, connections) {
6124
+ const entry = registry.byId.get(requirement.connectorId);
6125
+ if (!entry) {
6126
+ return missing(requirement, "unknown_connector", `Unknown connector ${requirement.connectorId}.`);
6127
+ }
6128
+ const connector = entry.connector;
6129
+ if (connector.actions.length === 0 && (connector.triggers?.length ?? 0) === 0) {
6130
+ return missing(requirement, "not_executable", `${connector.title} is catalog-only and cannot be invoked yet.`, connector, entry);
6131
+ }
6132
+ const scopes = requiredScopes(requirement, connector);
6133
+ const actions = requiredActions(requirement, connector);
6134
+ const triggers = requiredTriggers(requirement, connector);
6135
+ const connection = connections.find(
6136
+ (candidate) => sameActor(candidate.owner, owner) && candidate.status === "active" && (candidate.connectorId === connector.id || entry.aliases.includes(candidate.connectorId)) && scopes.every((scope) => candidate.grantedScopes.includes(scope))
6137
+ );
6138
+ if (!connection) {
6139
+ return {
6140
+ requirement,
6141
+ status: "missing_connection",
6142
+ connector,
6143
+ registryEntry: entry,
6144
+ missingScopes: scopes,
6145
+ missingActions: actions,
6146
+ missingTriggers: triggers,
6147
+ message: `${connector.title} needs an active user connection with the required scopes.`
6148
+ };
6149
+ }
6150
+ return {
6151
+ requirement,
6152
+ status: "ready",
6153
+ connector,
6154
+ registryEntry: entry,
6155
+ connection,
6156
+ missingScopes: [],
6157
+ missingActions: [],
6158
+ missingTriggers: [],
6159
+ message: `${connector.title} is ready.`
6160
+ };
6161
+ }
6162
+ function missing(requirement, status, message, connector, registryEntry2) {
6163
+ return {
6164
+ requirement,
6165
+ status,
6166
+ connector,
6167
+ registryEntry: registryEntry2,
6168
+ missingScopes: [],
6169
+ missingActions: [],
6170
+ missingTriggers: [],
6171
+ message
6172
+ };
6173
+ }
6174
+ function requiredActions(requirement, connector) {
6175
+ if (requirement.mode === "trigger") return [];
6176
+ if (requirement.requiredActions?.length) return unique5(requirement.requiredActions);
6177
+ const actions = connector.actions.filter((action) => {
6178
+ if (requirement.mode === "read") return action.risk === "read";
6179
+ if (requirement.mode === "write") return action.risk !== "read";
6180
+ return false;
6181
+ });
6182
+ return unique5(actions.map((action) => action.id));
6183
+ }
6184
+ function requiredTriggers(requirement, connector) {
6185
+ if (requirement.requiredTriggers?.length) return unique5(requirement.requiredTriggers);
6186
+ if (requirement.mode !== "trigger") return [];
6187
+ return unique5((connector.triggers ?? []).map((trigger) => trigger.id));
6188
+ }
6189
+ function requiredScopes(requirement, connector) {
6190
+ if (requirement.requiredScopes?.length) return unique5(requirement.requiredScopes);
6191
+ const actionIds = new Set(requiredActions(requirement, connector));
6192
+ const triggerIds = new Set(requiredTriggers(requirement, connector));
6193
+ return unique5([
6194
+ ...connector.actions.filter((action) => actionIds.has(action.id)).flatMap((action) => action.requiredScopes),
6195
+ ...(connector.triggers ?? []).filter((trigger) => triggerIds.has(trigger.id)).flatMap((trigger) => trigger.requiredScopes)
6196
+ ]);
6197
+ }
6198
+ function sameActor(a, b) {
6199
+ return a.type === b.type && a.id === b.id;
6200
+ }
6201
+ function unique5(values) {
6202
+ return [...new Set(values)];
6203
+ }
6204
+
6205
+ // src/workflow.ts
6206
+ var InMemoryIntegrationWorkflowStore = class {
6207
+ workflows = /* @__PURE__ */ new Map();
6208
+ put(workflow) {
6209
+ this.workflows.set(workflow.id, workflow);
6210
+ }
6211
+ get(id) {
6212
+ return this.workflows.get(id);
6213
+ }
6214
+ list() {
6215
+ return [...this.workflows.values()];
6216
+ }
6217
+ listByWorkflow(workflowId) {
6218
+ return [...this.workflows.values()].filter((workflow) => workflow.workflowId === workflowId);
6219
+ }
6220
+ listByOwner(owner) {
6221
+ return [...this.workflows.values()].filter((workflow) => sameActor2(workflow.owner, owner));
6222
+ }
6223
+ };
6224
+ var IntegrationWorkflowRuntime = class {
6225
+ runtime;
6226
+ hub;
6227
+ grants;
6228
+ store;
6229
+ now;
6230
+ constructor(options) {
6231
+ this.runtime = options.runtime;
6232
+ this.hub = options.hub;
6233
+ this.grants = options.grants;
6234
+ this.store = options.store ?? new InMemoryIntegrationWorkflowStore();
6235
+ this.now = options.now ?? (() => /* @__PURE__ */ new Date());
6236
+ }
6237
+ async install(input) {
6238
+ const grants = await this.runtime.createGrants({
6239
+ manifest: input.workflow.manifest,
6240
+ owner: input.owner,
6241
+ grantee: input.grantee,
6242
+ metadata: { workflowId: input.workflow.id }
6243
+ });
6244
+ const triggerGrant = findTriggerGrant(grants, input.workflow.trigger.requirementId, input.workflow.trigger.triggerId);
6245
+ const subscription = await this.hub.subscribeTrigger(
6246
+ triggerGrant.connectionId,
6247
+ input.workflow.trigger.triggerId,
6248
+ input.workflow.trigger.targetUrl
6249
+ );
6250
+ const installed = {
6251
+ id: `workflow_${input.workflow.id}_${triggerGrant.id}`,
6252
+ workflowId: input.workflow.id,
6253
+ manifestId: input.workflow.manifest.id,
6254
+ owner: input.owner,
6255
+ grantee: input.grantee,
6256
+ triggerGrantId: triggerGrant.id,
6257
+ subscription,
6258
+ status: "active",
6259
+ createdAt: this.now().toISOString(),
6260
+ metadata: input.workflow.metadata
6261
+ };
6262
+ await this.store.put(installed);
6263
+ return installed;
6264
+ }
6265
+ async dispatchEvent(event, handler) {
6266
+ const workflows = (await this.store.list()).filter(
6267
+ (workflow) => workflow.status === "active" && workflow.subscription.connectionId === event.connectionId && workflow.subscription.trigger === event.trigger
6268
+ );
6269
+ await handler({ event, workflows });
6270
+ return { matched: workflows };
6271
+ }
6272
+ };
6273
+ function createIntegrationWorkflowRuntime(options) {
6274
+ return new IntegrationWorkflowRuntime(options);
6275
+ }
6276
+ function findTriggerGrant(grants, requirementId, triggerId) {
6277
+ const grant = grants.find(
6278
+ (candidate) => candidate.requirementId === requirementId && candidate.allowedTriggers.includes(triggerId)
6279
+ );
6280
+ if (!grant) throw new Error(`Missing trigger grant ${requirementId}/${triggerId}.`);
6281
+ return grant;
6282
+ }
6283
+ function sameActor2(a, b) {
6284
+ return a.type === b.type && a.id === b.id;
6285
+ }
6286
+
5247
6287
  // src/specs/types.ts
5248
6288
  function specAuthToConnectorAuth(auth) {
5249
6289
  if (auth.mode === "api_key") return "api_key";
@@ -5433,17 +6473,20 @@ var IntegrationHub = class {
5433
6473
  await this.store.put(connection);
5434
6474
  return connection;
5435
6475
  }
6476
+ async listConnections(owner) {
6477
+ return this.store.listByOwner(owner);
6478
+ }
5436
6479
  async issueCapability(request) {
5437
6480
  const connection = await this.requireConnection(request.connectionId);
5438
6481
  this.assertConnectionActive(connection);
5439
6482
  assertScopes(connection, request.scopes);
5440
6483
  const now = this.now();
5441
6484
  const capability = {
5442
- id: `cap_${randomUUID2()}`,
6485
+ id: `cap_${randomUUID3()}`,
5443
6486
  subject: request.subject,
5444
6487
  connectionId: request.connectionId,
5445
- scopes: unique5(request.scopes),
5446
- allowedActions: unique5(request.allowedActions),
6488
+ scopes: unique6(request.scopes),
6489
+ allowedActions: unique6(request.allowedActions),
5447
6490
  issuedAt: now.toISOString(),
5448
6491
  expiresAt: new Date(now.getTime() + request.ttlMs).toISOString(),
5449
6492
  metadata: request.metadata
@@ -5671,9 +6714,9 @@ async function postJson(fetcher, url, body, bearer) {
5671
6714
  if (!response.ok) throw new IntegrationError(`Integration provider returned HTTP ${response.status}.`, "provider_not_found");
5672
6715
  return response.json();
5673
6716
  }
5674
- function assertScopes(connection, requiredScopes) {
5675
- const missing = requiredScopes.filter((scope) => !connection.grantedScopes.includes(scope));
5676
- if (missing.length > 0) throw new IntegrationError(`Missing integration scopes: ${missing.join(", ")}`, "scope_denied");
6717
+ function assertScopes(connection, requiredScopes2) {
6718
+ const missing2 = requiredScopes2.filter((scope) => !connection.grantedScopes.includes(scope));
6719
+ if (missing2.length > 0) throw new IntegrationError(`Missing integration scopes: ${missing2.join(", ")}`, "scope_denied");
5677
6720
  }
5678
6721
  function hmac(payload, secret) {
5679
6722
  return createHmac3("sha256", secret).update(payload).digest("base64url");
@@ -5689,18 +6732,32 @@ function base64UrlEncode(value) {
5689
6732
  function base64UrlDecode(value) {
5690
6733
  return Buffer.from(value, "base64url").toString("utf8");
5691
6734
  }
5692
- function unique5(values) {
6735
+ function unique6(values) {
5693
6736
  return [...new Set(values)];
5694
6737
  }
5695
6738
  export {
5696
6739
  ACTIVEPIECES_OVERRIDES,
6740
+ ApprovalBackedPolicyEngine,
5697
6741
  CredentialsExpired,
6742
+ DEFAULT_INTEGRATION_BRIDGE_ENV,
5698
6743
  DEFAULT_SIGNATURE_TOLERANCE_SECONDS,
6744
+ DefaultIntegrationActionGuard,
5699
6745
  INTEGRATION_FAMILIES,
5700
6746
  InMemoryConnectionStore,
6747
+ InMemoryIntegrationApprovalStore,
6748
+ InMemoryIntegrationAuditStore,
6749
+ InMemoryIntegrationEventStore,
6750
+ InMemoryIntegrationGrantStore,
6751
+ InMemoryIntegrationHealthcheckStore,
6752
+ InMemoryIntegrationIdempotencyStore,
6753
+ InMemoryIntegrationSecretStore,
6754
+ InMemoryIntegrationWorkflowStore,
5701
6755
  InMemoryOAuthFlowStore,
5702
6756
  IntegrationError,
5703
6757
  IntegrationHub,
6758
+ IntegrationRuntime,
6759
+ IntegrationSandboxHost,
6760
+ IntegrationWorkflowRuntime,
5704
6761
  ResourceContention,
5705
6762
  StaticIntegrationPolicyEngine,
5706
6763
  _resetPendingFlowsForTests,
@@ -5712,6 +6769,8 @@ export {
5712
6769
  buildApprovalRequest,
5713
6770
  buildDefaultIntegrationRegistry,
5714
6771
  buildHealthcheckPlan,
6772
+ buildIntegrationBridgeEnvironment,
6773
+ buildIntegrationBridgePayload,
5715
6774
  buildIntegrationCoverageConnectors,
5716
6775
  buildIntegrationInvocationEnvelope,
5717
6776
  buildIntegrationToolCatalog,
@@ -5719,12 +6778,23 @@ export {
5719
6778
  composeIntegrationRegistry,
5720
6779
  consoleStepsToText,
5721
6780
  consumePendingFlow,
6781
+ createApprovalBackedPolicyEngine,
6782
+ createAuditingActionGuard,
6783
+ createConnectionCredentialResolver,
5722
6784
  createConnectorAdapterProvider,
6785
+ createCredentialBackedAdapterProvider,
6786
+ createDefaultIntegrationActionGuard,
5723
6787
  createDefaultIntegrationPolicyEngine,
5724
6788
  createGatewayCatalogProvider,
5725
6789
  createHttpIntegrationProvider,
6790
+ createIntegrationAuditEvent,
6791
+ createIntegrationRuntime,
6792
+ createIntegrationWorkflowRuntime,
5726
6793
  createMockIntegrationProvider,
5727
6794
  declarativeRestConnector,
6795
+ decodeIntegrationBridgePayload,
6796
+ dispatchIntegrationInvocation,
6797
+ encodeIntegrationBridgePayload,
5728
6798
  exchangeAuthorizationCode,
5729
6799
  firstHeader,
5730
6800
  getActivepiecesOverride,
@@ -5734,6 +6804,7 @@ export {
5734
6804
  gitlabConnector,
5735
6805
  googleCalendar,
5736
6806
  googleSheets,
6807
+ healthcheckRequest,
5737
6808
  hubspot,
5738
6809
  importGraphqlConnector,
5739
6810
  importMcpConnector,
@@ -5752,16 +6823,25 @@ export {
5752
6823
  normalizeGatewayCatalog,
5753
6824
  normalizeIntegrationResult,
5754
6825
  notionDatabase,
6826
+ parseIntegrationBridgeEnvironment,
5755
6827
  parseIntegrationToolName,
5756
6828
  parseStripeSignatureHeader,
6829
+ receiveIntegrationWebhook,
5757
6830
  redactApprovalRequest,
5758
6831
  redactCapability,
6832
+ redactIntegrationBridgePayload,
5759
6833
  redactInvocationEnvelope,
5760
6834
  refreshAccessToken,
5761
6835
  renderAgentToolDescription,
5762
6836
  renderConsoleSteps,
5763
6837
  renderRunbookMarkdown,
6838
+ resolveConnectionCredentials,
6839
+ resolveIntegrationApproval,
6840
+ revokeConnection,
6841
+ runIntegrationHealthcheck,
6842
+ runIntegrationHealthchecks,
5764
6843
  salesforceConnector,
6844
+ sanitizeAuditConnection,
5765
6845
  sanitizeConnection,
5766
6846
  searchIntegrationTools,
5767
6847
  signCapability,
@@ -5769,6 +6849,7 @@ export {
5769
6849
  slackEventsConnector,
5770
6850
  specAuthToConnectorAuth,
5771
6851
  startOAuthFlow,
6852
+ storedEventToTriggerEvent,
5772
6853
  stripePackConnector,
5773
6854
  stripeWebhookReceiverConnector,
5774
6855
  summarizeIntegrationRegistry,