@tangle-network/agent-integrations 0.14.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";
@@ -1290,6 +1290,869 @@ function unique(values) {
1290
1290
  return [...new Set(values)];
1291
1291
  }
1292
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.` };
2119
+ }
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." };
2130
+ }
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";
2154
+ }
2155
+
1293
2156
  // src/connectors/types.ts
1294
2157
  var ResourceContention = class extends Error {
1295
2158
  constructor(message, alternatives = [], currentState) {
@@ -1344,7 +2207,7 @@ function assertValidConnectorManifest(manifest) {
1344
2207
  }
1345
2208
 
1346
2209
  // src/connectors/oauth.ts
1347
- import { createHash, randomBytes } from "crypto";
2210
+ import { createHash as createHash3, randomBytes } from "crypto";
1348
2211
  var PENDING_TTL_MS = 10 * 60 * 1e3;
1349
2212
  var InMemoryOAuthFlowStore = class {
1350
2213
  pendingFlows = /* @__PURE__ */ new Map();
@@ -1372,7 +2235,7 @@ function startOAuthFlow(input) {
1372
2235
  const now = input.now ?? Date.now();
1373
2236
  store.sweep?.(now);
1374
2237
  const codeVerifier = base64Url(randomBytes(48));
1375
- const codeChallenge = base64Url(createHash("sha256").update(codeVerifier).digest());
2238
+ const codeChallenge = base64Url(createHash3("sha256").update(codeVerifier).digest());
1376
2239
  const state = base64Url(randomBytes(24));
1377
2240
  store.put(state, {
1378
2241
  codeVerifier,
@@ -1837,7 +2700,7 @@ function readMetaString(meta, key) {
1837
2700
  }
1838
2701
 
1839
2702
  // src/connectors/adapters/google-sheets.ts
1840
- import { createHash as createHash2 } from "crypto";
2703
+ import { createHash as createHash4 } from "crypto";
1841
2704
  var SCOPES2 = ["https://www.googleapis.com/auth/spreadsheets"];
1842
2705
  var AUTH_URL2 = "https://accounts.google.com/o/oauth2/v2/auth";
1843
2706
  var TOKEN_URL2 = "https://oauth2.googleapis.com/token";
@@ -2111,7 +2974,7 @@ async function fetchAllRows(accessToken, meta) {
2111
2974
  function rowFingerprint(values) {
2112
2975
  const keys = Object.keys(values).sort();
2113
2976
  const blob = keys.map((k) => `${k}=${values[k]}`).join("");
2114
- return createHash2("sha256").update(blob).digest("hex").slice(0, 16);
2977
+ return createHash4("sha256").update(blob).digest("hex").slice(0, 16);
2115
2978
  }
2116
2979
  function matchesPredicate(row, predicate) {
2117
2980
  for (const [k, v] of Object.entries(predicate)) {
@@ -4597,7 +5460,7 @@ function unique2(values) {
4597
5460
  }
4598
5461
 
4599
5462
  // src/policy.ts
4600
- import { randomUUID } from "crypto";
5463
+ import { randomUUID as randomUUID2 } from "crypto";
4601
5464
  var StaticIntegrationPolicyEngine = class {
4602
5465
  rules;
4603
5466
  defaultReadEffect;
@@ -4640,7 +5503,7 @@ function buildApprovalRequest(ctx, reason, requestedAt) {
4640
5503
  throw new Error("Cannot build approval request without an action descriptor.");
4641
5504
  }
4642
5505
  return {
4643
- id: `approval_${randomUUID()}`,
5506
+ id: `approval_${randomUUID2()}`,
4644
5507
  connectionId: ctx.connection.id,
4645
5508
  providerId: ctx.connection.providerId,
4646
5509
  connectorId: ctx.connection.connectorId,
@@ -4656,7 +5519,7 @@ function buildApprovalRequest(ctx, reason, requestedAt) {
4656
5519
  function redactApprovalRequest(request) {
4657
5520
  return {
4658
5521
  ...request,
4659
- inputPreview: redactUnknown(request.inputPreview)
5522
+ inputPreview: redactUnknown2(request.inputPreview)
4660
5523
  };
4661
5524
  }
4662
5525
  function ruleMatches(rule, ctx) {
@@ -4680,17 +5543,17 @@ function defaultReason(effect, risk) {
4680
5543
  return `${risk} integration action requires approval by default policy.`;
4681
5544
  }
4682
5545
  function previewInput(input) {
4683
- return redactUnknown(input);
5546
+ return redactUnknown2(input);
4684
5547
  }
4685
- function redactUnknown(value) {
4686
- if (Array.isArray(value)) return value.map(redactUnknown);
5548
+ function redactUnknown2(value) {
5549
+ if (Array.isArray(value)) return value.map(redactUnknown2);
4687
5550
  if (!value || typeof value !== "object") return value;
4688
5551
  const out = {};
4689
5552
  for (const [key, child] of Object.entries(value)) {
4690
5553
  if (/token|secret|password|authorization|api[_-]?key|credential/i.test(key)) {
4691
5554
  out[key] = "[REDACTED]";
4692
5555
  } else {
4693
- out[key] = redactUnknown(child);
5556
+ out[key] = redactUnknown2(child);
4694
5557
  }
4695
5558
  }
4696
5559
  return out;
@@ -4754,13 +5617,13 @@ function redactInvocationEnvelope(envelope) {
4754
5617
  return {
4755
5618
  ...envelope,
4756
5619
  capabilityToken: "[REDACTED]",
4757
- input: redactUnknown2(envelope.input)
5620
+ input: redactUnknown3(envelope.input)
4758
5621
  };
4759
5622
  }
4760
5623
  function redactCapability(capability) {
4761
5624
  return {
4762
5625
  ...capability,
4763
- metadata: redactUnknown2(capability.metadata)
5626
+ metadata: redactUnknown3(capability.metadata)
4764
5627
  };
4765
5628
  }
4766
5629
  function normalizeIntegrationResult(result) {
@@ -4788,15 +5651,40 @@ function normalizeIntegrationResult(result) {
4788
5651
  metadata: result.metadata
4789
5652
  };
4790
5653
  }
4791
- function redactUnknown2(value) {
4792
- 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);
4793
5681
  if (!value || typeof value !== "object") return value;
4794
5682
  const out = {};
4795
5683
  for (const [key, child] of Object.entries(value)) {
4796
5684
  if (/token|secret|password|authorization|api[_-]?key|credential/i.test(key)) {
4797
5685
  out[key] = "[REDACTED]";
4798
5686
  } else {
4799
- out[key] = redactUnknown2(child);
5687
+ out[key] = redactUnknown3(child);
4800
5688
  }
4801
5689
  }
4802
5690
  return out;
@@ -4808,152 +5696,6 @@ function isPlainRecord(value) {
4808
5696
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4809
5697
  }
4810
5698
 
4811
- // src/adapter-provider.ts
4812
- function createConnectorAdapterProvider(options) {
4813
- const providerId = options.id ?? "first-party";
4814
- const now = options.now ?? (() => /* @__PURE__ */ new Date());
4815
- const adapters = /* @__PURE__ */ new Map();
4816
- for (const adapter of options.adapters) {
4817
- adapters.set(adapter.manifest.kind, adapter);
4818
- }
4819
- return {
4820
- id: providerId,
4821
- kind: options.kind ?? "first_party",
4822
- listConnectors: () => [...adapters.values()].map((adapter) => manifestToConnector(providerId, adapter)),
4823
- async invokeAction(connection, request) {
4824
- const adapter = adapters.get(connection.connectorId);
4825
- if (!adapter) {
4826
- throw new IntegrationError(`Connector adapter ${connection.connectorId} not found.`, "connector_not_found");
4827
- }
4828
- const capability = adapter.manifest.capabilities.find((candidate) => candidate.name === request.action);
4829
- if (!capability) {
4830
- throw new IntegrationError(`Capability ${request.action} is not defined by ${connection.connectorId}.`, "action_not_found");
4831
- }
4832
- const source = await options.resolveDataSource(connection);
4833
- const invocation = {
4834
- source,
4835
- capabilityName: request.action,
4836
- args: toRecord(request.input),
4837
- idempotencyKey: request.idempotencyKey ?? `idem_${connection.id}_${request.action}_${now().getTime()}`,
4838
- expectedEtag: typeof request.metadata?.expectedEtag === "string" ? request.metadata.expectedEtag : void 0,
4839
- callSessionId: typeof request.metadata?.callSessionId === "string" ? request.metadata.callSessionId : void 0
4840
- };
4841
- if (capability.class === "read") {
4842
- if (!adapter.executeRead) {
4843
- throw new IntegrationError(`Connector ${connection.connectorId} does not implement reads.`, "action_not_found");
4844
- }
4845
- const result = await adapter.executeRead(invocation);
4846
- return readResultToAction(request, result);
4847
- }
4848
- if (capability.class === "mutation") {
4849
- if (!adapter.executeMutation) {
4850
- throw new IntegrationError(`Connector ${connection.connectorId} does not implement mutations.`, "action_not_found");
4851
- }
4852
- const result = await adapter.executeMutation(invocation);
4853
- return mutationResultToAction(request, result);
4854
- }
4855
- throw new IntegrationError(`Capability ${request.action} is not invokable as an action.`, "action_not_found");
4856
- }
4857
- };
4858
- }
4859
- function manifestToConnector(providerId, adapter) {
4860
- const manifest = adapter.manifest;
4861
- return {
4862
- id: manifest.kind,
4863
- providerId,
4864
- title: manifest.displayName,
4865
- category: mapCategory(manifest.category),
4866
- auth: mapAuth(manifest.auth.kind),
4867
- scopes: manifest.auth.kind === "oauth2" ? manifest.auth.scopes : [],
4868
- actions: manifest.capabilities.filter((capability) => capability.class === "read" || capability.class === "mutation").map((capability) => ({
4869
- id: capability.name,
4870
- title: titleFromName(capability.name),
4871
- risk: capability.class === "read" ? "read" : capability.externalEffect ? "destructive" : "write",
4872
- requiredScopes: capability.requiredScopes ?? [],
4873
- dataClass: inferDataClass(manifest.category),
4874
- description: capability.description,
4875
- approvalRequired: capability.class === "mutation",
4876
- inputSchema: capability.parameters
4877
- })),
4878
- metadata: {
4879
- source: "first-party-adapter",
4880
- supportTier: "firstPartyExecutable",
4881
- executable: true
4882
- }
4883
- };
4884
- }
4885
- function readResultToAction(request, result) {
4886
- return {
4887
- ok: true,
4888
- action: request.action,
4889
- output: result.data,
4890
- metadata: {
4891
- etag: result.etag,
4892
- fetchedAt: result.fetchedAt
4893
- }
4894
- };
4895
- }
4896
- function mutationResultToAction(request, result) {
4897
- if (result.status === "committed") {
4898
- return {
4899
- ok: true,
4900
- action: request.action,
4901
- output: result.data,
4902
- metadata: {
4903
- etagAfter: result.etagAfter,
4904
- committedAt: result.committedAt,
4905
- idempotentReplay: result.idempotentReplay
4906
- }
4907
- };
4908
- }
4909
- if (result.status === "conflict") {
4910
- return {
4911
- ok: false,
4912
- action: request.action,
4913
- output: {
4914
- conflict: true,
4915
- message: result.message,
4916
- alternatives: result.alternatives,
4917
- currentState: result.currentState
4918
- }
4919
- };
4920
- }
4921
- return {
4922
- ok: false,
4923
- action: request.action,
4924
- output: {
4925
- rateLimited: true,
4926
- retryAfterMs: result.retryAfterMs,
4927
- message: result.message
4928
- }
4929
- };
4930
- }
4931
- function mapAuth(kind) {
4932
- if (kind === "oauth2") return "oauth2";
4933
- if (kind === "api-key") return "api_key";
4934
- if (kind === "none") return "none";
4935
- return "custom";
4936
- }
4937
- function mapCategory(category) {
4938
- if (category === "comms") return "chat";
4939
- if (category === "spreadsheet") return "database";
4940
- if (category === "doc") return "docs";
4941
- if (category === "commerce") return "workflow";
4942
- return category === "other" ? "other" : category;
4943
- }
4944
- function inferDataClass(category) {
4945
- if (category === "commerce") return "sensitive";
4946
- if (category === "webhook") return "internal";
4947
- return "private";
4948
- }
4949
- function titleFromName(name) {
4950
- return name.split(/[._-]/g).filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ");
4951
- }
4952
- function toRecord(input) {
4953
- if (input && typeof input === "object" && !Array.isArray(input)) return input;
4954
- return {};
4955
- }
4956
-
4957
5699
  // src/importers.ts
4958
5700
  var HTTP_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete"]);
4959
5701
  function importOpenApiConnector(document, options) {
@@ -5740,7 +6482,7 @@ var IntegrationHub = class {
5740
6482
  assertScopes(connection, request.scopes);
5741
6483
  const now = this.now();
5742
6484
  const capability = {
5743
- id: `cap_${randomUUID2()}`,
6485
+ id: `cap_${randomUUID3()}`,
5744
6486
  subject: request.subject,
5745
6487
  connectionId: request.connectionId,
5746
6488
  scopes: unique6(request.scopes),
@@ -5995,16 +6737,26 @@ function unique6(values) {
5995
6737
  }
5996
6738
  export {
5997
6739
  ACTIVEPIECES_OVERRIDES,
6740
+ ApprovalBackedPolicyEngine,
5998
6741
  CredentialsExpired,
6742
+ DEFAULT_INTEGRATION_BRIDGE_ENV,
5999
6743
  DEFAULT_SIGNATURE_TOLERANCE_SECONDS,
6744
+ DefaultIntegrationActionGuard,
6000
6745
  INTEGRATION_FAMILIES,
6001
6746
  InMemoryConnectionStore,
6747
+ InMemoryIntegrationApprovalStore,
6748
+ InMemoryIntegrationAuditStore,
6749
+ InMemoryIntegrationEventStore,
6002
6750
  InMemoryIntegrationGrantStore,
6751
+ InMemoryIntegrationHealthcheckStore,
6752
+ InMemoryIntegrationIdempotencyStore,
6753
+ InMemoryIntegrationSecretStore,
6003
6754
  InMemoryIntegrationWorkflowStore,
6004
6755
  InMemoryOAuthFlowStore,
6005
6756
  IntegrationError,
6006
6757
  IntegrationHub,
6007
6758
  IntegrationRuntime,
6759
+ IntegrationSandboxHost,
6008
6760
  IntegrationWorkflowRuntime,
6009
6761
  ResourceContention,
6010
6762
  StaticIntegrationPolicyEngine,
@@ -6017,6 +6769,8 @@ export {
6017
6769
  buildApprovalRequest,
6018
6770
  buildDefaultIntegrationRegistry,
6019
6771
  buildHealthcheckPlan,
6772
+ buildIntegrationBridgeEnvironment,
6773
+ buildIntegrationBridgePayload,
6020
6774
  buildIntegrationCoverageConnectors,
6021
6775
  buildIntegrationInvocationEnvelope,
6022
6776
  buildIntegrationToolCatalog,
@@ -6024,14 +6778,23 @@ export {
6024
6778
  composeIntegrationRegistry,
6025
6779
  consoleStepsToText,
6026
6780
  consumePendingFlow,
6781
+ createApprovalBackedPolicyEngine,
6782
+ createAuditingActionGuard,
6783
+ createConnectionCredentialResolver,
6027
6784
  createConnectorAdapterProvider,
6785
+ createCredentialBackedAdapterProvider,
6786
+ createDefaultIntegrationActionGuard,
6028
6787
  createDefaultIntegrationPolicyEngine,
6029
6788
  createGatewayCatalogProvider,
6030
6789
  createHttpIntegrationProvider,
6790
+ createIntegrationAuditEvent,
6031
6791
  createIntegrationRuntime,
6032
6792
  createIntegrationWorkflowRuntime,
6033
6793
  createMockIntegrationProvider,
6034
6794
  declarativeRestConnector,
6795
+ decodeIntegrationBridgePayload,
6796
+ dispatchIntegrationInvocation,
6797
+ encodeIntegrationBridgePayload,
6035
6798
  exchangeAuthorizationCode,
6036
6799
  firstHeader,
6037
6800
  getActivepiecesOverride,
@@ -6041,6 +6804,7 @@ export {
6041
6804
  gitlabConnector,
6042
6805
  googleCalendar,
6043
6806
  googleSheets,
6807
+ healthcheckRequest,
6044
6808
  hubspot,
6045
6809
  importGraphqlConnector,
6046
6810
  importMcpConnector,
@@ -6059,16 +6823,25 @@ export {
6059
6823
  normalizeGatewayCatalog,
6060
6824
  normalizeIntegrationResult,
6061
6825
  notionDatabase,
6826
+ parseIntegrationBridgeEnvironment,
6062
6827
  parseIntegrationToolName,
6063
6828
  parseStripeSignatureHeader,
6829
+ receiveIntegrationWebhook,
6064
6830
  redactApprovalRequest,
6065
6831
  redactCapability,
6832
+ redactIntegrationBridgePayload,
6066
6833
  redactInvocationEnvelope,
6067
6834
  refreshAccessToken,
6068
6835
  renderAgentToolDescription,
6069
6836
  renderConsoleSteps,
6070
6837
  renderRunbookMarkdown,
6838
+ resolveConnectionCredentials,
6839
+ resolveIntegrationApproval,
6840
+ revokeConnection,
6841
+ runIntegrationHealthcheck,
6842
+ runIntegrationHealthchecks,
6071
6843
  salesforceConnector,
6844
+ sanitizeAuditConnection,
6072
6845
  sanitizeConnection,
6073
6846
  searchIntegrationTools,
6074
6847
  signCapability,
@@ -6076,6 +6849,7 @@ export {
6076
6849
  slackEventsConnector,
6077
6850
  specAuthToConnectorAuth,
6078
6851
  startOAuthFlow,
6852
+ storedEventToTriggerEvent,
6079
6853
  stripePackConnector,
6080
6854
  stripeWebhookReceiverConnector,
6081
6855
  summarizeIntegrationRegistry,