@tangle-network/agent-integrations 0.14.0 → 0.16.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";
@@ -129,7 +129,7 @@ function buildActivepiecesConnectors(options = {}) {
129
129
  const category = override?.category ?? entry.category;
130
130
  const scopes = [`${entry.id}.read`, `${entry.id}.write`];
131
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
+ const catalogTriggers = entry.triggers.map((trigger2) => toTrigger(trigger2, scopes, dataClassFor(category)));
133
133
  return {
134
134
  id: entry.id,
135
135
  providerId,
@@ -172,10 +172,10 @@ function toAction(action, scopes, dataClass) {
172
172
  inputSchema: { type: "object", additionalProperties: true, properties: {} }
173
173
  };
174
174
  }
175
- function toTrigger(trigger, scopes, dataClass) {
175
+ function toTrigger(trigger2, scopes, dataClass) {
176
176
  return {
177
- id: trigger.id,
178
- title: trigger.title,
177
+ id: trigger2.id,
178
+ title: trigger2.title,
179
179
  requiredScopes: [scopes[0]],
180
180
  dataClass,
181
181
  payloadSchema: { type: "object", additionalProperties: true, properties: {} }
@@ -1247,8 +1247,8 @@ function mergeActions(candidates) {
1247
1247
  function mergeTriggers(candidates) {
1248
1248
  const out = /* @__PURE__ */ new Map();
1249
1249
  for (const candidate of toolBindableCandidates(candidates)) {
1250
- for (const trigger of candidate.connector.triggers ?? []) {
1251
- if (!out.has(trigger.id)) out.set(trigger.id, trigger);
1250
+ for (const trigger2 of candidate.connector.triggers ?? []) {
1251
+ if (!out.has(trigger2.id)) out.set(trigger2.id, trigger2);
1252
1252
  }
1253
1253
  }
1254
1254
  return out.size > 0 ? [...out.values()] : void 0;
@@ -1290,6 +1290,1554 @@ 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/actions.ts
1525
+ var CANONICAL_INTEGRATION_ACTIONS = {
1526
+ googleCalendarEventsList: "google-calendar.events.list",
1527
+ googleCalendarEventsCreate: "google-calendar.events.create",
1528
+ gmailMessagesSearch: "gmail.messages.search",
1529
+ gmailMessagesSend: "gmail.messages.send",
1530
+ googleDriveFilesSearch: "google-drive.files.search",
1531
+ googleDriveFilesRead: "google-drive.files.read",
1532
+ githubRepositoriesGet: "github.repositories.get",
1533
+ githubIssuesSearch: "github.issues.search",
1534
+ githubIssuesCreate: "github.issues.create",
1535
+ githubPullRequestsComment: "github.pull-requests.comment",
1536
+ slackChannelsList: "slack.channels.list",
1537
+ slackMessagesSearch: "slack.messages.search",
1538
+ slackMessagesPost: "slack.messages.post",
1539
+ providerHttpRequest: "provider.http.request"
1540
+ };
1541
+ function buildCanonicalLaunchConnectors(options = {}) {
1542
+ const providerId = options.providerId ?? "tangle-platform";
1543
+ const connectors = [
1544
+ googleCalendarConnector(providerId),
1545
+ gmailConnector(providerId),
1546
+ googleDriveConnector(providerId),
1547
+ githubConnector(providerId),
1548
+ slackConnector(providerId)
1549
+ ];
1550
+ if (!options.includeProviderPassthrough) return connectors;
1551
+ return connectors.map((connector) => ({
1552
+ ...connector,
1553
+ actions: [...connector.actions, providerPassthroughAction(connector.id)]
1554
+ }));
1555
+ }
1556
+ function canonicalActionConnectorId(actionId) {
1557
+ if (actionId.startsWith("google-calendar.")) return "google-calendar";
1558
+ if (actionId.startsWith("gmail.")) return "gmail";
1559
+ if (actionId.startsWith("google-drive.")) return "google-drive";
1560
+ if (actionId.startsWith("github.")) return "github";
1561
+ if (actionId.startsWith("slack.")) return "slack";
1562
+ if (actionId === CANONICAL_INTEGRATION_ACTIONS.providerHttpRequest) return void 0;
1563
+ return actionId.split(".")[0];
1564
+ }
1565
+ function googleCalendarConnector(providerId) {
1566
+ return {
1567
+ id: "google-calendar",
1568
+ providerId,
1569
+ title: "Google Calendar",
1570
+ category: "calendar",
1571
+ auth: "oauth2",
1572
+ scopes: ["https://www.googleapis.com/auth/calendar.readonly", "https://www.googleapis.com/auth/calendar.events"],
1573
+ actions: [
1574
+ {
1575
+ id: CANONICAL_INTEGRATION_ACTIONS.googleCalendarEventsList,
1576
+ title: "List calendar events",
1577
+ risk: "read",
1578
+ requiredScopes: ["https://www.googleapis.com/auth/calendar.readonly"],
1579
+ dataClass: "private",
1580
+ description: "Read events from a Google Calendar over a bounded time range.",
1581
+ inputSchema: objectSchema2({
1582
+ calendarId: { type: "string", default: "primary" },
1583
+ timeMin: { type: "string", description: "RFC3339 lower bound." },
1584
+ timeMax: { type: "string", description: "RFC3339 upper bound." }
1585
+ }, ["timeMin", "timeMax"])
1586
+ },
1587
+ {
1588
+ id: CANONICAL_INTEGRATION_ACTIONS.googleCalendarEventsCreate,
1589
+ title: "Create calendar event",
1590
+ risk: "write",
1591
+ requiredScopes: ["https://www.googleapis.com/auth/calendar.events"],
1592
+ dataClass: "private",
1593
+ approvalRequired: true,
1594
+ description: "Create an event on a Google Calendar after user approval.",
1595
+ inputSchema: objectSchema2({
1596
+ calendarId: { type: "string", default: "primary" },
1597
+ start: { type: "string", description: "RFC3339 start time." },
1598
+ end: { type: "string", description: "RFC3339 end time." },
1599
+ summary: { type: "string" },
1600
+ description: { type: "string" },
1601
+ attendees: { type: "array", items: { type: "string" } }
1602
+ }, ["start", "end", "summary"])
1603
+ }
1604
+ ],
1605
+ metadata: { source: "canonical-launch", supportTier: "setupReady" }
1606
+ };
1607
+ }
1608
+ function gmailConnector(providerId) {
1609
+ return {
1610
+ id: "gmail",
1611
+ providerId,
1612
+ title: "Gmail",
1613
+ category: "email",
1614
+ auth: "oauth2",
1615
+ scopes: ["https://www.googleapis.com/auth/gmail.readonly", "https://www.googleapis.com/auth/gmail.send"],
1616
+ actions: [
1617
+ {
1618
+ id: CANONICAL_INTEGRATION_ACTIONS.gmailMessagesSearch,
1619
+ title: "Search Gmail messages",
1620
+ risk: "read",
1621
+ requiredScopes: ["https://www.googleapis.com/auth/gmail.readonly"],
1622
+ dataClass: "private",
1623
+ description: "Search user Gmail messages and return bounded message metadata/snippets.",
1624
+ inputSchema: objectSchema2({ query: { type: "string" }, maxResults: { type: "integer", minimum: 1, maximum: 50 } }, ["query"])
1625
+ },
1626
+ {
1627
+ id: CANONICAL_INTEGRATION_ACTIONS.gmailMessagesSend,
1628
+ title: "Send Gmail message",
1629
+ risk: "write",
1630
+ requiredScopes: ["https://www.googleapis.com/auth/gmail.send"],
1631
+ dataClass: "private",
1632
+ approvalRequired: true,
1633
+ description: "Send an email from the user account after approval.",
1634
+ inputSchema: objectSchema2({
1635
+ to: { type: "array", items: { type: "string" } },
1636
+ subject: { type: "string" },
1637
+ body: { type: "string" }
1638
+ }, ["to", "subject", "body"])
1639
+ }
1640
+ ],
1641
+ triggers: [{
1642
+ id: "gmail.messages.received",
1643
+ title: "Gmail message received",
1644
+ requiredScopes: ["https://www.googleapis.com/auth/gmail.readonly"],
1645
+ dataClass: "private",
1646
+ description: "Triggered when a new matching Gmail message is received."
1647
+ }],
1648
+ metadata: { source: "canonical-launch", supportTier: "setupReady" }
1649
+ };
1650
+ }
1651
+ function googleDriveConnector(providerId) {
1652
+ return {
1653
+ id: "google-drive",
1654
+ providerId,
1655
+ title: "Google Drive",
1656
+ category: "storage",
1657
+ auth: "oauth2",
1658
+ scopes: ["https://www.googleapis.com/auth/drive.readonly", "https://www.googleapis.com/auth/drive.file"],
1659
+ actions: [
1660
+ {
1661
+ id: CANONICAL_INTEGRATION_ACTIONS.googleDriveFilesSearch,
1662
+ title: "Search Drive files",
1663
+ risk: "read",
1664
+ requiredScopes: ["https://www.googleapis.com/auth/drive.readonly"],
1665
+ dataClass: "private",
1666
+ description: "Search user-visible Google Drive files.",
1667
+ inputSchema: objectSchema2({ query: { type: "string" }, maxResults: { type: "integer", minimum: 1, maximum: 50 } }, ["query"])
1668
+ },
1669
+ {
1670
+ id: CANONICAL_INTEGRATION_ACTIONS.googleDriveFilesRead,
1671
+ title: "Read Drive file",
1672
+ risk: "read",
1673
+ requiredScopes: ["https://www.googleapis.com/auth/drive.readonly"],
1674
+ dataClass: "private",
1675
+ description: "Read metadata and content for an authorized Drive file.",
1676
+ inputSchema: objectSchema2({ fileId: { type: "string" } }, ["fileId"])
1677
+ }
1678
+ ],
1679
+ metadata: { source: "canonical-launch", supportTier: "setupReady" }
1680
+ };
1681
+ }
1682
+ function githubConnector(providerId) {
1683
+ return {
1684
+ id: "github",
1685
+ providerId,
1686
+ title: "GitHub",
1687
+ category: "workflow",
1688
+ auth: "oauth2",
1689
+ scopes: ["repo", "read:user"],
1690
+ actions: [
1691
+ readAction(CANONICAL_INTEGRATION_ACTIONS.githubRepositoriesGet, "Read repository metadata", ["repo"], objectSchema2({ owner: { type: "string" }, repo: { type: "string" } }, ["owner", "repo"])),
1692
+ readAction(CANONICAL_INTEGRATION_ACTIONS.githubIssuesSearch, "Search issues and pull requests", ["repo"], objectSchema2({ query: { type: "string" }, limit: { type: "integer", minimum: 1, maximum: 50 } }, ["query"])),
1693
+ writeAction(CANONICAL_INTEGRATION_ACTIONS.githubIssuesCreate, "Create issue", ["repo"], objectSchema2({ owner: { type: "string" }, repo: { type: "string" }, title: { type: "string" }, body: { type: "string" } }, ["owner", "repo", "title"])),
1694
+ writeAction(CANONICAL_INTEGRATION_ACTIONS.githubPullRequestsComment, "Comment on pull request", ["repo"], objectSchema2({ owner: { type: "string" }, repo: { type: "string" }, pullNumber: { type: "integer" }, body: { type: "string" } }, ["owner", "repo", "pullNumber", "body"]))
1695
+ ],
1696
+ metadata: { source: "canonical-launch", supportTier: "setupReady" }
1697
+ };
1698
+ }
1699
+ function slackConnector(providerId) {
1700
+ return {
1701
+ id: "slack",
1702
+ providerId,
1703
+ title: "Slack",
1704
+ category: "chat",
1705
+ auth: "oauth2",
1706
+ scopes: ["channels:read", "search:read", "chat:write"],
1707
+ actions: [
1708
+ readAction(CANONICAL_INTEGRATION_ACTIONS.slackChannelsList, "List Slack channels", ["channels:read"], objectSchema2({ limit: { type: "integer", minimum: 1, maximum: 200 } })),
1709
+ readAction(CANONICAL_INTEGRATION_ACTIONS.slackMessagesSearch, "Search Slack messages", ["search:read"], objectSchema2({ query: { type: "string" }, count: { type: "integer", minimum: 1, maximum: 50 } }, ["query"])),
1710
+ writeAction(CANONICAL_INTEGRATION_ACTIONS.slackMessagesPost, "Post Slack message", ["chat:write"], objectSchema2({ channel: { type: "string" }, text: { type: "string" }, blocks: { type: "array" } }, ["channel", "text"]))
1711
+ ],
1712
+ triggers: [trigger("slack.message.posted", "Slack message posted", ["channels:read"])],
1713
+ metadata: { source: "canonical-launch", supportTier: "setupReady" }
1714
+ };
1715
+ }
1716
+ function readAction(id, title, scopes, inputSchema) {
1717
+ return { id, title, risk: "read", requiredScopes: scopes, dataClass: "private", inputSchema };
1718
+ }
1719
+ function writeAction(id, title, scopes, inputSchema) {
1720
+ return { id, title, risk: "write", requiredScopes: scopes, dataClass: "private", approvalRequired: true, inputSchema };
1721
+ }
1722
+ function trigger(id, title, scopes) {
1723
+ return { id, title, requiredScopes: scopes, dataClass: "private" };
1724
+ }
1725
+ function providerPassthroughAction(connectorId) {
1726
+ return {
1727
+ id: CANONICAL_INTEGRATION_ACTIONS.providerHttpRequest,
1728
+ title: "Provider HTTP request",
1729
+ risk: "write",
1730
+ requiredScopes: [],
1731
+ dataClass: "sensitive",
1732
+ approvalRequired: true,
1733
+ description: `Controlled provider-native passthrough for ${connectorId}. Disabled by default by platform policy.`,
1734
+ inputSchema: objectSchema2({
1735
+ method: { type: "string", enum: ["GET", "POST", "PUT", "PATCH", "DELETE"] },
1736
+ path: { type: "string" },
1737
+ query: { type: "object" },
1738
+ body: { type: "object" }
1739
+ }, ["method", "path"])
1740
+ };
1741
+ }
1742
+ function objectSchema2(properties, required = []) {
1743
+ return { type: "object", additionalProperties: false, properties, required };
1744
+ }
1745
+
1746
+ // src/bridge.ts
1747
+ var DEFAULT_INTEGRATION_BRIDGE_ENV = "TANGLE_INTEGRATION_BUNDLE";
1748
+ function buildIntegrationBridgePayload(bundle) {
1749
+ return {
1750
+ version: 1,
1751
+ manifestId: bundle.manifestId,
1752
+ subject: bundle.subject,
1753
+ expiresAt: bundle.expiresAt,
1754
+ tools: bundle.tools.flatMap((tool) => {
1755
+ const binding = bundle.capabilities.find(
1756
+ (candidate) => candidate.connectorId === tool.connectorId && candidate.connectionId && candidate.allowedActions.includes(tool.action.id)
1757
+ );
1758
+ if (!binding) return [];
1759
+ return [{
1760
+ name: tool.name,
1761
+ title: tool.title,
1762
+ connectorId: tool.connectorId,
1763
+ connectionId: binding.connectionId,
1764
+ action: tool.action.id,
1765
+ risk: tool.risk,
1766
+ dataClass: tool.dataClass,
1767
+ requiredScopes: tool.requiredScopes,
1768
+ capabilityToken: binding.capability.token
1769
+ }];
1770
+ })
1771
+ };
1772
+ }
1773
+ function encodeIntegrationBridgePayload(payload) {
1774
+ return Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
1775
+ }
1776
+ function decodeIntegrationBridgePayload(encoded) {
1777
+ const parsed = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
1778
+ assertBridgePayload(parsed);
1779
+ return parsed;
1780
+ }
1781
+ function buildIntegrationBridgeEnvironment(bundle, options = {}) {
1782
+ const envVar = options.envVar ?? DEFAULT_INTEGRATION_BRIDGE_ENV;
1783
+ return {
1784
+ [envVar]: encodeIntegrationBridgePayload(buildIntegrationBridgePayload(bundle))
1785
+ };
1786
+ }
1787
+ function parseIntegrationBridgeEnvironment(env, options = {}) {
1788
+ const envVar = options.envVar ?? DEFAULT_INTEGRATION_BRIDGE_ENV;
1789
+ const encoded = env[envVar];
1790
+ if (!encoded) throw new Error(`Missing ${envVar}.`);
1791
+ return decodeIntegrationBridgePayload(encoded);
1792
+ }
1793
+ function redactIntegrationBridgePayload(payload) {
1794
+ return {
1795
+ ...payload,
1796
+ tools: payload.tools.map((tool) => ({
1797
+ ...tool,
1798
+ capabilityToken: "[REDACTED]"
1799
+ }))
1800
+ };
1801
+ }
1802
+ function assertBridgePayload(value) {
1803
+ if (!value || typeof value !== "object") throw new Error("Invalid integration bridge payload.");
1804
+ const payload = value;
1805
+ if (payload.version !== 1) throw new Error("Unsupported integration bridge payload version.");
1806
+ if (typeof payload.manifestId !== "string") throw new Error("Invalid integration bridge manifestId.");
1807
+ if (typeof payload.expiresAt !== "string") throw new Error("Invalid integration bridge expiresAt.");
1808
+ if (!Array.isArray(payload.tools)) throw new Error("Invalid integration bridge tools.");
1809
+ }
1810
+
1811
+ // src/errors.ts
1812
+ var IntegrationRuntimeError = class extends Error {
1813
+ code;
1814
+ status;
1815
+ userAction;
1816
+ metadata;
1817
+ constructor(input) {
1818
+ super(input.message);
1819
+ this.name = "IntegrationRuntimeError";
1820
+ this.code = input.code;
1821
+ this.status = input.status ?? statusForCode(input.code);
1822
+ this.userAction = input.userAction;
1823
+ this.metadata = input.metadata;
1824
+ }
1825
+ };
1826
+ function normalizeIntegrationError(error) {
1827
+ if (error instanceof IntegrationRuntimeError) {
1828
+ return {
1829
+ ok: false,
1830
+ code: error.code,
1831
+ message: error.message,
1832
+ status: error.status,
1833
+ userAction: error.userAction,
1834
+ metadata: redactUnknown2(error.metadata)
1835
+ };
1836
+ }
1837
+ const message = error instanceof Error ? error.message : String(error ?? "Unknown integration error.");
1838
+ return {
1839
+ ok: false,
1840
+ code: inferCode(message),
1841
+ message,
1842
+ status: 500
1843
+ };
1844
+ }
1845
+ function statusForCode(code) {
1846
+ if (code === "missing_connection" || code === "missing_grant") return 409;
1847
+ if (code === "approval_required") return 202;
1848
+ if (code === "approval_denied") return 403;
1849
+ if (code === "connection_revoked" || code === "connection_expired" || code === "provider_auth_failed") return 401;
1850
+ if (code === "scope_missing" || code === "action_denied" || code === "passthrough_disabled") return 403;
1851
+ if (code === "action_not_found" || code === "manifest_invalid" || code === "input_invalid") return 400;
1852
+ if (code === "provider_rate_limited") return 429;
1853
+ if (code === "provider_unavailable") return 503;
1854
+ if (code === "capability_expired" || code === "capability_invalid") return 401;
1855
+ return 500;
1856
+ }
1857
+ function inferCode(message) {
1858
+ if (/approval/i.test(message)) return "approval_required";
1859
+ if (/scope/i.test(message)) return "scope_missing";
1860
+ if (/expired/i.test(message)) return "connection_expired";
1861
+ if (/revoked/i.test(message)) return "connection_revoked";
1862
+ if (/rate.?limit|429/i.test(message)) return "provider_rate_limited";
1863
+ if (/unauth|forbidden|401|403/i.test(message)) return "provider_auth_failed";
1864
+ return "unknown";
1865
+ }
1866
+ function redactUnknown2(value) {
1867
+ if (Array.isArray(value)) return value.map(redactUnknown2);
1868
+ if (!value || typeof value !== "object") return value;
1869
+ const out = {};
1870
+ for (const [key, child] of Object.entries(value)) {
1871
+ if (/token|secret|password|authorization|api[_-]?key|credential|refresh/i.test(key)) {
1872
+ out[key] = "[REDACTED]";
1873
+ } else {
1874
+ out[key] = redactUnknown2(child);
1875
+ }
1876
+ }
1877
+ return out;
1878
+ }
1879
+
1880
+ // src/client.ts
1881
+ var TangleIntegrationsClient = class {
1882
+ endpoint;
1883
+ bridge;
1884
+ fetchImpl;
1885
+ getCapabilityToken;
1886
+ constructor(options) {
1887
+ this.endpoint = options.endpoint.replace(/\/$/, "");
1888
+ this.bridge = options.bridge ?? parseIntegrationBridgeEnvironment(
1889
+ options.env ?? readProcessEnv(),
1890
+ { envVar: options.envVar ?? DEFAULT_INTEGRATION_BRIDGE_ENV }
1891
+ );
1892
+ this.fetchImpl = options.fetchImpl ?? fetch;
1893
+ this.getCapabilityToken = options.getCapabilityToken ?? ((tool) => tool.capabilityToken);
1894
+ }
1895
+ tools() {
1896
+ return [...this.bridge.tools];
1897
+ }
1898
+ findTool(toolOrAction) {
1899
+ const found = this.bridge.tools.find(
1900
+ (tool) => tool.name === toolOrAction || tool.action === toolOrAction || `${tool.connectorId}.${tool.action}` === toolOrAction
1901
+ );
1902
+ if (!found) {
1903
+ throw new IntegrationRuntimeError({
1904
+ code: "action_not_found",
1905
+ message: `Integration tool ${toolOrAction} is not available in this runtime.`,
1906
+ metadata: { available: this.bridge.tools.map((tool) => ({ name: tool.name, action: tool.action, connectorId: tool.connectorId })) }
1907
+ });
1908
+ }
1909
+ return found;
1910
+ }
1911
+ async invoke(input) {
1912
+ try {
1913
+ const tool = this.findTool(input.tool);
1914
+ const token = await this.getCapabilityToken(tool);
1915
+ const response = await this.fetchImpl(`${this.endpoint}/v1/integrations/invoke`, {
1916
+ method: "POST",
1917
+ headers: {
1918
+ "content-type": "application/json",
1919
+ authorization: `Bearer ${token}`
1920
+ },
1921
+ body: JSON.stringify({
1922
+ action: tool.action,
1923
+ input: input.input,
1924
+ idempotencyKey: input.idempotencyKey ?? defaultIdempotencyKey(tool.action),
1925
+ dryRun: input.dryRun,
1926
+ metadata: input.metadata
1927
+ })
1928
+ });
1929
+ const json = await response.json().catch(() => void 0);
1930
+ if (!response.ok && !json) {
1931
+ return { status: "failed", action: tool.action, error: `Integration invoke failed with HTTP ${response.status}` };
1932
+ }
1933
+ return json ?? { status: "failed", action: tool.action, error: "Integration invoke returned an empty response." };
1934
+ } catch (error) {
1935
+ const normalized = normalizeIntegrationError(error);
1936
+ return { status: "failed", action: input.tool, error: normalized.message, metadata: { code: normalized.code, userAction: normalized.userAction } };
1937
+ }
1938
+ }
1939
+ };
1940
+ function createTangleIntegrationsClient(options) {
1941
+ return new TangleIntegrationsClient(options);
1942
+ }
1943
+ function defaultIdempotencyKey(action) {
1944
+ return `${action}:${Date.now()}:${Math.random().toString(36).slice(2)}`;
1945
+ }
1946
+ function readProcessEnv() {
1947
+ if (typeof process !== "undefined" && process.env) return process.env;
1948
+ return {};
1949
+ }
1950
+
1951
+ // src/consent.ts
1952
+ function renderConsentSummary(manifestOrResolution, options = {}) {
1953
+ const manifest = "manifest" in manifestOrResolution ? manifestOrResolution.manifest : manifestOrResolution;
1954
+ const appName = options.appName ?? manifest.title ?? manifest.id;
1955
+ const requirements = manifest.requirements;
1956
+ const risk = aggregateRisk(requirements, options.connectors);
1957
+ const connectorIds = unique2(requirements.map((requirement) => requirement.connectorId));
1958
+ const first = requirements[0];
1959
+ const body = first ? sentenceForRequirement(appName, first) : `${appName} does not request integrations.`;
1960
+ return {
1961
+ title: `${appName} wants to use ${humanList(connectorIds.map(titleize))}`,
1962
+ body,
1963
+ bullets: requirements.map((requirement) => bulletForRequirement(requirement, options.connectors)),
1964
+ primaryAction: risk === "read" ? "Allow access" : risk === "write" ? "Review and allow" : "Review destructive access",
1965
+ risk,
1966
+ connectorIds
1967
+ };
1968
+ }
1969
+ function renderApprovalCopy(input) {
1970
+ return {
1971
+ title: `${input.appName} wants to ${input.action.title.toLowerCase()}`,
1972
+ body: `${input.appName} is requesting permission to run "${input.action.title}" on ${input.connectorTitle}.`,
1973
+ bullets: [
1974
+ `Risk: ${input.action.risk}`,
1975
+ `Data: ${input.action.dataClass}`,
1976
+ ...input.approvalId ? [`Approval id: ${input.approvalId}`] : []
1977
+ ],
1978
+ primaryAction: input.action.risk === "read" ? "Allow" : "Approve action",
1979
+ risk: input.action.risk,
1980
+ connectorIds: []
1981
+ };
1982
+ }
1983
+ function sentenceForRequirement(appName, requirement) {
1984
+ if (requirement.connectorId === "google-calendar" && requirement.mode === "read") {
1985
+ return `${appName} wants to read your Google Calendar to find schedule-aware recommendations.`;
1986
+ }
1987
+ if (requirement.connectorId === "google-calendar" && requirement.mode === "write") {
1988
+ return `${appName} wants to create or update Google Calendar events after your approval.`;
1989
+ }
1990
+ if (requirement.mode === "read") return `${appName} wants to read ${titleize(requirement.connectorId)} data.`;
1991
+ if (requirement.mode === "write") return `${appName} wants to write ${titleize(requirement.connectorId)} data after approval.`;
1992
+ return `${appName} wants to subscribe to ${titleize(requirement.connectorId)} events.`;
1993
+ }
1994
+ function bulletForRequirement(requirement, connectors = []) {
1995
+ const connector = connectors.find((candidate) => candidate.id === requirement.connectorId);
1996
+ const actions = requirement.requiredActions?.length ? requirement.requiredActions.map((id) => connector?.actions.find((action) => action.id === id)?.title ?? id) : requirement.requiredTriggers ?? [];
1997
+ return `${titleize(requirement.connectorId)}: ${requirement.reason}${actions.length ? ` (${actions.join(", ")})` : ""}`;
1998
+ }
1999
+ function aggregateRisk(requirements, connectors = []) {
2000
+ let rank = 0;
2001
+ for (const requirement of requirements) {
2002
+ if (requirement.mode === "write") rank = Math.max(rank, 1);
2003
+ const connector = connectors.find((candidate) => candidate.id === requirement.connectorId);
2004
+ for (const actionId of requirement.requiredActions ?? []) {
2005
+ const risk = connector?.actions.find((action) => action.id === actionId)?.risk;
2006
+ if (risk === "write") rank = Math.max(rank, 1);
2007
+ if (risk === "destructive") rank = Math.max(rank, 2);
2008
+ }
2009
+ }
2010
+ return rank === 2 ? "destructive" : rank === 1 ? "write" : "read";
2011
+ }
2012
+ function humanList(values) {
2013
+ if (values.length <= 1) return values[0] ?? "integrations";
2014
+ if (values.length === 2) return `${values[0]} and ${values[1]}`;
2015
+ return `${values.slice(0, -1).join(", ")}, and ${values.at(-1)}`;
2016
+ }
2017
+ function titleize(value) {
2018
+ return value.split(/[-_.]/g).filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join(" ");
2019
+ }
2020
+ function unique2(values) {
2021
+ return [...new Set(values)];
2022
+ }
2023
+
2024
+ // src/adapter-provider.ts
2025
+ function createConnectorAdapterProvider(options) {
2026
+ const providerId = options.id ?? "first-party";
2027
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
2028
+ const adapters = /* @__PURE__ */ new Map();
2029
+ for (const adapter of options.adapters) {
2030
+ adapters.set(adapter.manifest.kind, adapter);
2031
+ }
2032
+ return {
2033
+ id: providerId,
2034
+ kind: options.kind ?? "first_party",
2035
+ listConnectors: () => [...adapters.values()].map((adapter) => manifestToConnector(providerId, adapter)),
2036
+ async invokeAction(connection, request) {
2037
+ const adapter = adapters.get(connection.connectorId);
2038
+ if (!adapter) {
2039
+ throw new IntegrationError(`Connector adapter ${connection.connectorId} not found.`, "connector_not_found");
2040
+ }
2041
+ const capability = adapter.manifest.capabilities.find((candidate) => candidate.name === request.action);
2042
+ if (!capability) {
2043
+ throw new IntegrationError(`Capability ${request.action} is not defined by ${connection.connectorId}.`, "action_not_found");
2044
+ }
2045
+ const source = await options.resolveDataSource(connection);
2046
+ const invocation = {
2047
+ source,
2048
+ capabilityName: request.action,
2049
+ args: toRecord(request.input),
2050
+ idempotencyKey: request.idempotencyKey ?? `idem_${connection.id}_${request.action}_${now().getTime()}`,
2051
+ expectedEtag: typeof request.metadata?.expectedEtag === "string" ? request.metadata.expectedEtag : void 0,
2052
+ callSessionId: typeof request.metadata?.callSessionId === "string" ? request.metadata.callSessionId : void 0
2053
+ };
2054
+ if (capability.class === "read") {
2055
+ if (!adapter.executeRead) {
2056
+ throw new IntegrationError(`Connector ${connection.connectorId} does not implement reads.`, "action_not_found");
2057
+ }
2058
+ const result = await adapter.executeRead(invocation);
2059
+ return readResultToAction(request, result);
2060
+ }
2061
+ if (capability.class === "mutation") {
2062
+ if (!adapter.executeMutation) {
2063
+ throw new IntegrationError(`Connector ${connection.connectorId} does not implement mutations.`, "action_not_found");
2064
+ }
2065
+ const result = await adapter.executeMutation(invocation);
2066
+ return mutationResultToAction(request, result);
2067
+ }
2068
+ throw new IntegrationError(`Capability ${request.action} is not invokable as an action.`, "action_not_found");
2069
+ }
2070
+ };
2071
+ }
2072
+ function manifestToConnector(providerId, adapter) {
2073
+ const manifest = adapter.manifest;
2074
+ return {
2075
+ id: manifest.kind,
2076
+ providerId,
2077
+ title: manifest.displayName,
2078
+ category: mapCategory(manifest.category),
2079
+ auth: mapAuth(manifest.auth.kind),
2080
+ scopes: manifest.auth.kind === "oauth2" ? manifest.auth.scopes : [],
2081
+ actions: manifest.capabilities.filter((capability) => capability.class === "read" || capability.class === "mutation").map((capability) => ({
2082
+ id: capability.name,
2083
+ title: titleFromName(capability.name),
2084
+ risk: capability.class === "read" ? "read" : capability.externalEffect ? "destructive" : "write",
2085
+ requiredScopes: capability.requiredScopes ?? [],
2086
+ dataClass: inferDataClass(manifest.category),
2087
+ description: capability.description,
2088
+ approvalRequired: capability.class === "mutation",
2089
+ inputSchema: capability.parameters
2090
+ })),
2091
+ metadata: {
2092
+ source: "first-party-adapter",
2093
+ supportTier: "firstPartyExecutable",
2094
+ executable: true
2095
+ }
2096
+ };
2097
+ }
2098
+ function readResultToAction(request, result) {
2099
+ return {
2100
+ ok: true,
2101
+ action: request.action,
2102
+ output: result.data,
2103
+ metadata: {
2104
+ etag: result.etag,
2105
+ fetchedAt: result.fetchedAt
2106
+ }
2107
+ };
2108
+ }
2109
+ function mutationResultToAction(request, result) {
2110
+ if (result.status === "committed") {
2111
+ return {
2112
+ ok: true,
2113
+ action: request.action,
2114
+ output: result.data,
2115
+ metadata: {
2116
+ etagAfter: result.etagAfter,
2117
+ committedAt: result.committedAt,
2118
+ idempotentReplay: result.idempotentReplay
2119
+ }
2120
+ };
2121
+ }
2122
+ if (result.status === "conflict") {
2123
+ return {
2124
+ ok: false,
2125
+ action: request.action,
2126
+ output: {
2127
+ conflict: true,
2128
+ message: result.message,
2129
+ alternatives: result.alternatives,
2130
+ currentState: result.currentState
2131
+ }
2132
+ };
2133
+ }
2134
+ return {
2135
+ ok: false,
2136
+ action: request.action,
2137
+ output: {
2138
+ rateLimited: true,
2139
+ retryAfterMs: result.retryAfterMs,
2140
+ message: result.message
2141
+ }
2142
+ };
2143
+ }
2144
+ function mapAuth(kind) {
2145
+ if (kind === "oauth2") return "oauth2";
2146
+ if (kind === "api-key") return "api_key";
2147
+ if (kind === "none") return "none";
2148
+ return "custom";
2149
+ }
2150
+ function mapCategory(category) {
2151
+ if (category === "comms") return "chat";
2152
+ if (category === "spreadsheet") return "database";
2153
+ if (category === "doc") return "docs";
2154
+ if (category === "commerce") return "workflow";
2155
+ return category === "other" ? "other" : category;
2156
+ }
2157
+ function inferDataClass(category) {
2158
+ if (category === "commerce") return "sensitive";
2159
+ if (category === "webhook") return "internal";
2160
+ return "private";
2161
+ }
2162
+ function titleFromName(name) {
2163
+ return name.split(/[._-]/g).filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ");
2164
+ }
2165
+ function toRecord(input) {
2166
+ if (input && typeof input === "object" && !Array.isArray(input)) return input;
2167
+ return {};
2168
+ }
2169
+
2170
+ // src/credentials.ts
2171
+ var InMemoryIntegrationSecretStore = class {
2172
+ secrets = /* @__PURE__ */ new Map();
2173
+ get(ref) {
2174
+ return this.secrets.get(secretKey(ref));
2175
+ }
2176
+ put(ref, credentials) {
2177
+ this.secrets.set(secretKey(ref), credentials);
2178
+ }
2179
+ delete(ref) {
2180
+ this.secrets.delete(secretKey(ref));
2181
+ }
2182
+ };
2183
+ function createConnectionCredentialResolver(options) {
2184
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
2185
+ return async function resolveDataSource(connection) {
2186
+ const credentials = await resolveConnectionCredentials(connection, {
2187
+ secrets: options.secrets,
2188
+ connections: options.connections,
2189
+ adapters: options.adapters,
2190
+ now,
2191
+ markConnectionError: options.markConnectionError
2192
+ });
2193
+ return {
2194
+ id: connection.id,
2195
+ projectId: String(connection.metadata?.projectId ?? connection.owner.id),
2196
+ publishedAgentId: typeof connection.metadata?.publishedAgentId === "string" ? connection.metadata.publishedAgentId : null,
2197
+ kind: connection.connectorId,
2198
+ label: connection.account?.displayName ?? connection.account?.email ?? connection.connectorId,
2199
+ consistencyModel: typeof connection.metadata?.consistencyModel === "string" ? connection.metadata.consistencyModel : "authoritative",
2200
+ scopes: connection.grantedScopes,
2201
+ metadata: connection.metadata ?? {},
2202
+ credentials,
2203
+ status: connection.status === "active" ? "active" : connection.status === "revoked" ? "revoked" : "error"
2204
+ };
2205
+ };
2206
+ }
2207
+ async function resolveConnectionCredentials(input, options) {
2208
+ if (input.status !== "active") throw new Error(`Connection ${input.id} is ${input.status}.`);
2209
+ if (!input.secretRef) return { kind: "none" };
2210
+ const current = await options.secrets.get(input.secretRef);
2211
+ if (!current) throw new Error(`Secret ${input.secretRef.provider}/${input.secretRef.id} not found.`);
2212
+ if (!isExpiredOauth(current, options.now ?? (() => /* @__PURE__ */ new Date()))) return current;
2213
+ const adapter = options.adapters?.find((candidate) => candidate.manifest.kind === input.connectorId);
2214
+ if (!adapter?.refreshToken) return current;
2215
+ try {
2216
+ const refreshed = await adapter.refreshToken(current);
2217
+ await options.secrets.put(input.secretRef, refreshed);
2218
+ if (options.connections) {
2219
+ await options.connections.put({
2220
+ ...input,
2221
+ status: "active",
2222
+ updatedAt: (options.now?.() ?? /* @__PURE__ */ new Date()).toISOString(),
2223
+ expiresAt: refreshed.kind === "oauth2" && refreshed.expiresAt ? new Date(refreshed.expiresAt).toISOString() : input.expiresAt
2224
+ });
2225
+ }
2226
+ return refreshed;
2227
+ } catch (error) {
2228
+ const err = error instanceof Error ? error : new Error("Credential refresh failed.");
2229
+ await options.markConnectionError?.(input, err);
2230
+ if (options.connections) {
2231
+ await options.connections.put({
2232
+ ...input,
2233
+ status: "expired",
2234
+ updatedAt: (options.now?.() ?? /* @__PURE__ */ new Date()).toISOString()
2235
+ });
2236
+ }
2237
+ throw err;
2238
+ }
2239
+ }
2240
+ function createCredentialBackedAdapterProvider(options) {
2241
+ return createConnectorAdapterProvider({
2242
+ ...options,
2243
+ resolveDataSource: createConnectionCredentialResolver(options)
2244
+ });
2245
+ }
2246
+ async function revokeConnection(input) {
2247
+ if (input.connection.secretRef) await input.secrets?.delete?.(input.connection.secretRef);
2248
+ const revoked = {
2249
+ ...input.connection,
2250
+ status: "revoked",
2251
+ updatedAt: (input.now?.() ?? /* @__PURE__ */ new Date()).toISOString()
2252
+ };
2253
+ await input.connections?.put(revoked);
2254
+ return revoked;
2255
+ }
2256
+ function isExpiredOauth(credentials, now) {
2257
+ return credentials.kind === "oauth2" && typeof credentials.expiresAt === "number" && credentials.expiresAt <= now().getTime() && Boolean(credentials.refreshToken);
2258
+ }
2259
+ function secretKey(ref) {
2260
+ return `${ref.provider}:${ref.id}`;
2261
+ }
2262
+
2263
+ // src/events.ts
2264
+ var InMemoryIntegrationEventStore = class {
2265
+ events = /* @__PURE__ */ new Map();
2266
+ providerIds = /* @__PURE__ */ new Set();
2267
+ put(event) {
2268
+ this.events.set(event.id, event);
2269
+ if (event.providerEventId) this.providerIds.add(providerKey(event.sourceId, event.providerEventId));
2270
+ }
2271
+ hasProviderEvent(sourceId, providerEventId) {
2272
+ return this.providerIds.has(providerKey(sourceId, providerEventId));
2273
+ }
2274
+ list() {
2275
+ return [...this.events.values()];
2276
+ }
2277
+ };
2278
+ async function receiveIntegrationWebhook(input) {
2279
+ if (!input.adapter.handleInboundEvent) {
2280
+ return { status: 405, body: { ok: false, error: "Connector does not support inbound webhooks." }, received: [], duplicates: [] };
2281
+ }
2282
+ const signature = input.adapter.verifySignature?.({
2283
+ rawBody: input.rawBody,
2284
+ headers: input.headers,
2285
+ source: input.source
2286
+ });
2287
+ if (signature && !signature.valid) {
2288
+ return { status: 401, body: { ok: false, error: signature.reason ?? "Invalid webhook signature." }, received: [], duplicates: [] };
2289
+ }
2290
+ const handled = await input.adapter.handleInboundEvent({
2291
+ source: input.source,
2292
+ rawBody: input.rawBody,
2293
+ headers: input.headers
2294
+ });
2295
+ const received = [];
2296
+ const duplicates = [];
2297
+ for (const inbound of handled.events) {
2298
+ const event = storedEvent(input.source, inbound, input.now ?? (() => /* @__PURE__ */ new Date()));
2299
+ if (event.providerEventId && await input.store.hasProviderEvent(event.sourceId, event.providerEventId)) {
2300
+ duplicates.push(event);
2301
+ continue;
2302
+ }
2303
+ await input.store.put(event);
2304
+ received.push(event);
2305
+ await dispatchStoredEvent(event, input.source, input.workflowRuntime);
2306
+ }
2307
+ return {
2308
+ status: handled.response?.status ?? 200,
2309
+ body: handled.response?.body ?? { received: true, count: received.length, duplicateCount: duplicates.length },
2310
+ headers: handled.response?.headers,
2311
+ received,
2312
+ duplicates
2313
+ };
2314
+ }
2315
+ function storedEventToTriggerEvent(event, source) {
2316
+ return {
2317
+ id: event.id,
2318
+ providerId: String(source.metadata.providerId ?? "first-party"),
2319
+ connectorId: event.connectorId,
2320
+ connectionId: source.id,
2321
+ trigger: event.eventType,
2322
+ occurredAt: event.receivedAt,
2323
+ payload: event.payload,
2324
+ metadata: {
2325
+ providerEventId: event.providerEventId,
2326
+ sourceId: event.sourceId,
2327
+ ...event.metadata
2328
+ }
2329
+ };
2330
+ }
2331
+ async function dispatchStoredEvent(event, source, workflowRuntime) {
2332
+ if (!workflowRuntime) return;
2333
+ await workflowRuntime.dispatchEvent(storedEventToTriggerEvent(event, source), () => void 0);
2334
+ }
2335
+ function storedEvent(source, event, now) {
2336
+ return {
2337
+ id: `evt_${source.id}_${event.providerEventId ?? `${event.eventType}_${now().getTime()}`}`,
2338
+ sourceId: source.id,
2339
+ connectorId: source.kind,
2340
+ eventType: event.eventType,
2341
+ providerEventId: event.providerEventId,
2342
+ receivedAt: now().toISOString(),
2343
+ payload: event.payload
2344
+ };
2345
+ }
2346
+ function providerKey(sourceId, providerEventId) {
2347
+ return `${sourceId}:${providerEventId}`;
2348
+ }
2349
+
2350
+ // src/guard.ts
2351
+ import { createHash as createHash2 } from "crypto";
2352
+ var InMemoryIntegrationIdempotencyStore = class {
2353
+ records = /* @__PURE__ */ new Map();
2354
+ get(key) {
2355
+ return this.records.get(key);
2356
+ }
2357
+ put(record) {
2358
+ this.records.set(record.key, record);
2359
+ }
2360
+ };
2361
+ var DefaultIntegrationActionGuard = class {
2362
+ idempotency;
2363
+ audit;
2364
+ rateLimiter;
2365
+ now;
2366
+ constructor(options = {}) {
2367
+ this.idempotency = options.idempotency;
2368
+ this.audit = options.audit;
2369
+ this.rateLimiter = options.rateLimiter;
2370
+ this.now = options.now ?? (() => /* @__PURE__ */ new Date());
2371
+ }
2372
+ async invokeAction(ctx, proceed) {
2373
+ const idempotencyKey = ctx.request.idempotencyKey;
2374
+ const requestHash = hashRequest(ctx);
2375
+ if (idempotencyKey && this.idempotency) {
2376
+ const existing = await this.idempotency.get(idempotencyKey);
2377
+ if (existing) {
2378
+ if (existing.requestHash !== requestHash) {
2379
+ return {
2380
+ ok: false,
2381
+ action: ctx.request.action,
2382
+ output: { idempotencyConflict: true, message: "Idempotency key was reused with different integration input." }
2383
+ };
2384
+ }
2385
+ return {
2386
+ ...existing.result,
2387
+ metadata: { ...existing.result.metadata ?? {}, idempotentReplay: true }
2388
+ };
2389
+ }
2390
+ }
2391
+ if (ctx.request.dryRun && ctx.action?.risk !== "read") {
2392
+ const result = {
2393
+ ok: true,
2394
+ action: ctx.request.action,
2395
+ output: { dryRun: true },
2396
+ metadata: { dryRun: true }
2397
+ };
2398
+ await this.writeIdempotency(idempotencyKey, requestHash, result);
2399
+ return result;
2400
+ }
2401
+ const rateLimit = await this.rateLimiter?.check(ctx);
2402
+ if (rateLimit && !rateLimit.allowed) {
2403
+ return {
2404
+ ok: false,
2405
+ action: ctx.request.action,
2406
+ output: { rateLimited: true, retryAfterMs: rateLimit.retryAfterMs, message: rateLimit.reason ?? "Integration rate limit exceeded." }
2407
+ };
2408
+ }
2409
+ try {
2410
+ const result = await proceed();
2411
+ await this.writeIdempotency(idempotencyKey, requestHash, result);
2412
+ await this.audit?.record(createIntegrationAuditEvent({
2413
+ type: result.ok ? "action.invoked" : "action.failed",
2414
+ actor: ctx.connection.owner,
2415
+ connectionId: ctx.connection.id,
2416
+ providerId: ctx.connection.providerId,
2417
+ connectorId: ctx.connection.connectorId,
2418
+ action: ctx.request.action,
2419
+ risk: ctx.action?.risk,
2420
+ dataClass: ctx.action?.dataClass,
2421
+ ok: result.ok,
2422
+ metadata: { idempotencyKey, externalId: result.externalId, warnings: result.warnings },
2423
+ now: this.now
2424
+ }));
2425
+ return result;
2426
+ } catch (error) {
2427
+ await this.audit?.record(createIntegrationAuditEvent({
2428
+ type: "action.failed",
2429
+ actor: ctx.connection.owner,
2430
+ connectionId: ctx.connection.id,
2431
+ providerId: ctx.connection.providerId,
2432
+ connectorId: ctx.connection.connectorId,
2433
+ action: ctx.request.action,
2434
+ risk: ctx.action?.risk,
2435
+ dataClass: ctx.action?.dataClass,
2436
+ ok: false,
2437
+ message: error instanceof Error ? error.message : "Integration action failed.",
2438
+ metadata: { idempotencyKey },
2439
+ now: this.now
2440
+ }));
2441
+ throw error;
2442
+ }
2443
+ }
2444
+ async writeIdempotency(key, requestHash, result) {
2445
+ if (!key || !this.idempotency) return;
2446
+ await this.idempotency.put({
2447
+ key,
2448
+ requestHash,
2449
+ result,
2450
+ createdAt: this.now().toISOString()
2451
+ });
2452
+ }
2453
+ };
2454
+ function createDefaultIntegrationActionGuard(options = {}) {
2455
+ return new DefaultIntegrationActionGuard(options);
2456
+ }
2457
+ function hashRequest(ctx) {
2458
+ return createHash2("sha256").update(JSON.stringify({
2459
+ connectionId: ctx.connection.id,
2460
+ action: ctx.request.action,
2461
+ input: ctx.request.input ?? null,
2462
+ dryRun: ctx.request.dryRun ?? false
2463
+ })).digest("base64url");
2464
+ }
2465
+
2466
+ // src/healthcheck.ts
2467
+ var InMemoryIntegrationHealthcheckStore = class {
2468
+ results = /* @__PURE__ */ new Map();
2469
+ put(result) {
2470
+ this.results.set(result.connectionId, result);
2471
+ }
2472
+ get(connectionId) {
2473
+ return this.results.get(connectionId);
2474
+ }
2475
+ list() {
2476
+ return [...this.results.values()];
2477
+ }
2478
+ };
2479
+ async function runIntegrationHealthcheck(input) {
2480
+ const now = input.now ?? (() => /* @__PURE__ */ new Date());
2481
+ const checkedAt = now().toISOString();
2482
+ const connector = input.connector ?? input.registry?.byId.get(input.connection.connectorId)?.connector;
2483
+ const checks = [];
2484
+ checks.push(connectionStatusCheck(input.connection, now));
2485
+ if (!connector) {
2486
+ checks.push({ id: "connector-known", status: "unknown", message: `Connector ${input.connection.connectorId} is not in the registry.` });
2487
+ } else {
2488
+ checks.push(connectorExecutableCheck(connector));
2489
+ checks.push(scopeShapeCheck(input.connection, connector));
2490
+ if (input.test && input.connection.status === "active") {
2491
+ checks.push(await liveHealthcheck(input.connection, connector, input.test));
2492
+ }
2493
+ }
2494
+ const result = {
2495
+ connectionId: input.connection.id,
2496
+ providerId: input.connection.providerId,
2497
+ connectorId: input.connection.connectorId,
2498
+ status: rollupHealthStatus(checks),
2499
+ checkedAt,
2500
+ checks
2501
+ };
2502
+ await input.audit?.record(createIntegrationAuditEvent({
2503
+ type: "healthcheck.completed",
2504
+ actor: input.connection.owner,
2505
+ connectionId: input.connection.id,
2506
+ providerId: input.connection.providerId,
2507
+ connectorId: input.connection.connectorId,
2508
+ ok: result.status === "healthy",
2509
+ message: result.status,
2510
+ metadata: { checks: checks.map((check) => ({ id: check.id, status: check.status, message: check.message })) },
2511
+ occurredAt: checkedAt
2512
+ }));
2513
+ return result;
2514
+ }
2515
+ async function runIntegrationHealthchecks(input) {
2516
+ const results = [];
2517
+ for (const connection of input.connections) {
2518
+ const result = await runIntegrationHealthcheck({
2519
+ connection,
2520
+ registry: input.registry,
2521
+ test: input.test,
2522
+ audit: input.audit,
2523
+ now: input.now
2524
+ });
2525
+ await input.store?.put(result);
2526
+ results.push(result);
2527
+ }
2528
+ return results;
2529
+ }
2530
+ function healthcheckRequest(action = "healthcheck") {
2531
+ return {
2532
+ connectionId: "__healthcheck__",
2533
+ action,
2534
+ input: {},
2535
+ dryRun: true,
2536
+ metadata: { healthcheck: true }
2537
+ };
2538
+ }
2539
+ function connectionStatusCheck(connection, now) {
2540
+ if (connection.status !== "active") {
2541
+ return { id: "connection-active", status: "unhealthy", message: `Connection is ${connection.status}.` };
2542
+ }
2543
+ if (connection.expiresAt && Date.parse(connection.expiresAt) <= now().getTime()) {
2544
+ return { id: "connection-active", status: "unhealthy", message: "Connection credentials are expired." };
2545
+ }
2546
+ return { id: "connection-active", status: "healthy", message: "Connection is active." };
2547
+ }
2548
+ function connectorExecutableCheck(connector) {
2549
+ const executable = connector.actions.length > 0 || (connector.triggers?.length ?? 0) > 0;
2550
+ if (!executable) {
2551
+ return { id: "connector-executable", status: "degraded", message: `${connector.title} is catalog-only.` };
2552
+ }
2553
+ return { id: "connector-executable", status: "healthy", message: `${connector.title} has executable actions or triggers.` };
2554
+ }
2555
+ function scopeShapeCheck(connection, connector) {
2556
+ const declaredScopes = new Set(connector.scopes);
2557
+ const undeclared = connection.grantedScopes.filter((scope) => !declaredScopes.has(scope));
2558
+ if (connector.scopes.length === 0 && connection.grantedScopes.length > 0) {
2559
+ return { id: "scope-shape", status: "unknown", message: "Connector does not declare a scope catalog.", metadata: { grantedScopes: connection.grantedScopes } };
2560
+ }
2561
+ if (undeclared.length > 0) {
2562
+ return { id: "scope-shape", status: "degraded", message: "Connection has scopes not declared by the connector.", metadata: { undeclared } };
2563
+ }
2564
+ return { id: "scope-shape", status: "healthy", message: "Granted scopes match the connector shape." };
2565
+ }
2566
+ async function liveHealthcheck(connection, connector, test) {
2567
+ try {
2568
+ const result = await test(connection, connector);
2569
+ const ok = typeof result === "boolean" ? result : result.ok;
2570
+ return {
2571
+ id: "provider-live-test",
2572
+ status: ok ? "healthy" : "unhealthy",
2573
+ message: ok ? "Provider live test passed." : "Provider live test failed.",
2574
+ metadata: typeof result === "boolean" ? void 0 : { action: result.action, warnings: result.warnings }
2575
+ };
2576
+ } catch (error) {
2577
+ return {
2578
+ id: "provider-live-test",
2579
+ status: "unhealthy",
2580
+ message: error instanceof Error ? error.message : "Provider live test failed."
2581
+ };
2582
+ }
2583
+ }
2584
+ function rollupHealthStatus(checks) {
2585
+ if (checks.some((check) => check.status === "unhealthy")) return "unhealthy";
2586
+ if (checks.some((check) => check.status === "degraded")) return "degraded";
2587
+ if (checks.some((check) => check.status === "unknown")) return "unknown";
2588
+ return "healthy";
2589
+ }
2590
+
2591
+ // src/manifest.ts
2592
+ function validateIntegrationManifest(manifest) {
2593
+ const issues = [];
2594
+ if (!manifest.id?.trim()) issues.push({ path: "id", message: "Manifest id is required." });
2595
+ if (!Array.isArray(manifest.requirements)) issues.push({ path: "requirements", message: "Requirements must be an array." });
2596
+ const ids = /* @__PURE__ */ new Set();
2597
+ for (const [index, requirement] of (manifest.requirements ?? []).entries()) {
2598
+ const path = `requirements[${index}]`;
2599
+ if (!requirement.id?.trim()) issues.push({ path: `${path}.id`, message: "Requirement id is required." });
2600
+ if (ids.has(requirement.id)) issues.push({ path: `${path}.id`, message: `Duplicate requirement id ${requirement.id}.` });
2601
+ ids.add(requirement.id);
2602
+ if (!requirement.connectorId?.trim()) issues.push({ path: `${path}.connectorId`, message: "Connector id is required." });
2603
+ if (!["read", "write", "trigger"].includes(requirement.mode)) issues.push({ path: `${path}.mode`, message: "Mode must be read, write, or trigger." });
2604
+ if (!requirement.reason?.trim()) issues.push({ path: `${path}.reason`, message: "Human-readable reason is required." });
2605
+ if (requirement.mode !== "trigger" && !requirement.requiredActions?.length) {
2606
+ issues.push({ path: `${path}.requiredActions`, message: "Non-trigger requirements should list required actions." });
2607
+ }
2608
+ if (requirement.mode === "trigger" && !requirement.requiredTriggers?.length) {
2609
+ issues.push({ path: `${path}.requiredTriggers`, message: "Trigger requirements should list required triggers." });
2610
+ }
2611
+ }
2612
+ return { ok: issues.length === 0, issues };
2613
+ }
2614
+ function assertValidIntegrationManifest(manifest) {
2615
+ const result = validateIntegrationManifest(manifest);
2616
+ if (!result.ok) {
2617
+ throw new Error(`Invalid integration manifest: ${result.issues.map((issue) => `${issue.path}: ${issue.message}`).join("; ")}`);
2618
+ }
2619
+ }
2620
+ function inferIntegrationManifestFromTools(options) {
2621
+ const byConnector = /* @__PURE__ */ new Map();
2622
+ for (const item of options.tools) {
2623
+ const action = typeof item === "string" ? item : item.action;
2624
+ const connectorId = typeof item === "string" ? canonicalActionConnectorId(action) : item.connectorId ?? canonicalActionConnectorId(action);
2625
+ if (!connectorId) continue;
2626
+ const mode = typeof item === "string" ? inferMode(action) : item.mode ?? inferMode(action);
2627
+ const id = `${connectorId}-${mode}`;
2628
+ const existing = byConnector.get(id);
2629
+ const reason = typeof item === "string" ? defaultReason(connectorId, mode) : item.reason ?? defaultReason(connectorId, mode);
2630
+ if (existing) {
2631
+ byConnector.set(id, {
2632
+ ...existing,
2633
+ requiredActions: unique3([...existing.requiredActions ?? [], action]),
2634
+ requiredScopes: unique3([...existing.requiredScopes ?? [], ...typeof item === "string" ? [] : item.scopes ?? []])
2635
+ });
2636
+ } else {
2637
+ byConnector.set(id, {
2638
+ id,
2639
+ connectorId,
2640
+ mode,
2641
+ reason,
2642
+ requiredActions: mode === "trigger" ? void 0 : [action],
2643
+ requiredScopes: typeof item === "string" ? void 0 : item.scopes
2644
+ });
2645
+ }
2646
+ }
2647
+ const manifest = {
2648
+ id: options.manifestId,
2649
+ title: options.title,
2650
+ requirements: [...byConnector.values()],
2651
+ metadata: options.metadata
2652
+ };
2653
+ assertValidIntegrationManifest(manifest);
2654
+ return manifest;
2655
+ }
2656
+ function explainMissingRequirements(resolution) {
2657
+ return [...resolution.missing, ...resolution.optionalMissing].map((item) => ({
2658
+ requirementId: item.requirement.id,
2659
+ connectorId: item.requirement.connectorId,
2660
+ status: item.status,
2661
+ message: item.message,
2662
+ userAction: item.requirement.optional ? "ignore_optional" : item.status === "not_executable" ? "enable" : "connect"
2663
+ }));
2664
+ }
2665
+ function calendarExercisePlannerManifest(id = "exercise-calendar-planner") {
2666
+ return {
2667
+ id,
2668
+ title: "Exercise Calendar Planner",
2669
+ requirements: [{
2670
+ id: "calendar-read",
2671
+ connectorId: "google-calendar",
2672
+ mode: "read",
2673
+ reason: "Read busy and free calendar windows to recommend exercise sessions.",
2674
+ requiredActions: [CANONICAL_INTEGRATION_ACTIONS.googleCalendarEventsList],
2675
+ requiredScopes: ["https://www.googleapis.com/auth/calendar.readonly"]
2676
+ }]
2677
+ };
2678
+ }
2679
+ function inferMode(action) {
2680
+ if (/(create|send|post|update|delete|write|comment|request)$/i.test(action)) return "write";
2681
+ return "read";
2682
+ }
2683
+ function defaultReason(connectorId, mode) {
2684
+ if (connectorId === "google-calendar" && mode === "read") return "Read calendar availability for the generated app.";
2685
+ if (connectorId === "google-calendar" && mode === "write") return "Create or update calendar events after user approval.";
2686
+ return `${mode === "read" ? "Read from" : mode === "write" ? "Write to" : "Subscribe to"} ${connectorId} for this app.`;
2687
+ }
2688
+ function unique3(values) {
2689
+ return [...new Set(values)];
2690
+ }
2691
+
2692
+ // src/passthrough.ts
2693
+ var PROVIDER_PASSTHROUGH_ACTION = CANONICAL_INTEGRATION_ACTIONS.providerHttpRequest;
2694
+ function validateProviderPassthroughRequest(input, policy) {
2695
+ if (!policy.enabled) {
2696
+ throw new IntegrationRuntimeError({
2697
+ code: "passthrough_disabled",
2698
+ message: "Provider-native passthrough is disabled for this connector."
2699
+ });
2700
+ }
2701
+ if (!input.path.startsWith("/")) {
2702
+ throw new IntegrationRuntimeError({ code: "input_invalid", message: "Provider passthrough path must start with /." });
2703
+ }
2704
+ if (policy.allowedMethods?.length && !policy.allowedMethods.includes(input.method)) {
2705
+ throw new IntegrationRuntimeError({ code: "action_denied", message: `Provider passthrough method ${input.method} is not allowed.` });
2706
+ }
2707
+ if (policy.allowedPathPrefixes?.length && !policy.allowedPathPrefixes.some((prefix) => input.path.startsWith(prefix))) {
2708
+ throw new IntegrationRuntimeError({ code: "action_denied", message: `Provider passthrough path ${input.path} is not allowed.` });
2709
+ }
2710
+ const maxBodyBytes = policy.maxBodyBytes ?? 64 * 1024;
2711
+ const bodyBytes = Buffer.byteLength(JSON.stringify(input.body ?? null), "utf8");
2712
+ if (bodyBytes > maxBodyBytes) {
2713
+ throw new IntegrationRuntimeError({ code: "input_invalid", message: `Provider passthrough body exceeds ${maxBodyBytes} bytes.` });
2714
+ }
2715
+ for (const key of Object.keys(input.headers ?? {})) {
2716
+ if (/authorization|cookie|token|secret|api[_-]?key/i.test(key)) {
2717
+ throw new IntegrationRuntimeError({ code: "input_invalid", message: `Provider passthrough header ${key} is not caller-settable.` });
2718
+ }
2719
+ }
2720
+ }
2721
+
2722
+ // src/policy.ts
2723
+ import { randomUUID as randomUUID2 } from "crypto";
2724
+ var StaticIntegrationPolicyEngine = class {
2725
+ rules;
2726
+ defaultReadEffect;
2727
+ defaultWriteEffect;
2728
+ defaultDestructiveEffect;
2729
+ now;
2730
+ constructor(options = {}) {
2731
+ this.rules = options.rules ?? [];
2732
+ this.defaultReadEffect = options.defaultReadEffect ?? "allow";
2733
+ this.defaultWriteEffect = options.defaultWriteEffect ?? "require_approval";
2734
+ this.defaultDestructiveEffect = options.defaultDestructiveEffect ?? "deny";
2735
+ this.now = options.now ?? (() => /* @__PURE__ */ new Date());
2736
+ }
2737
+ decide(ctx) {
2738
+ const action = ctx.action;
2739
+ if (!action) return { decision: "deny", reason: "Integration action is missing from connector catalog." };
2740
+ const matched = this.rules.find((rule) => ruleMatches(rule, ctx));
2741
+ const effect = matched?.effect ?? this.defaultEffect(action.risk);
2742
+ const reason = matched?.reason ?? defaultReason2(effect, action.risk);
2743
+ if (effect === "allow") return { decision: "allow", reason, metadata: matched ? { ruleId: matched.id } : void 0 };
2744
+ if (effect === "deny") return { decision: "deny", reason, metadata: matched ? { ruleId: matched.id } : void 0 };
2745
+ return {
2746
+ decision: "require_approval",
2747
+ reason,
2748
+ approval: buildApprovalRequest(ctx, reason, this.now()),
2749
+ metadata: matched ? { ruleId: matched.id } : void 0
2750
+ };
2751
+ }
2752
+ defaultEffect(risk) {
2753
+ if (risk === "read") return this.defaultReadEffect;
2754
+ if (risk === "write") return this.defaultWriteEffect;
2755
+ return this.defaultDestructiveEffect;
2756
+ }
2757
+ };
2758
+ function createDefaultIntegrationPolicyEngine(options = {}) {
2759
+ return new StaticIntegrationPolicyEngine(options);
2760
+ }
2761
+ function buildApprovalRequest(ctx, reason, requestedAt) {
2762
+ if (!ctx.action) {
2763
+ throw new Error("Cannot build approval request without an action descriptor.");
2764
+ }
2765
+ return {
2766
+ id: `approval_${randomUUID2()}`,
2767
+ connectionId: ctx.connection.id,
2768
+ providerId: ctx.connection.providerId,
2769
+ connectorId: ctx.connection.connectorId,
2770
+ action: ctx.request.action,
2771
+ actor: { type: ctx.subject.type, id: ctx.subject.id },
2772
+ risk: ctx.action.risk,
2773
+ dataClass: ctx.action.dataClass,
2774
+ reason,
2775
+ requestedAt: requestedAt.toISOString(),
2776
+ inputPreview: previewInput(ctx.request.input)
2777
+ };
2778
+ }
2779
+ function redactApprovalRequest(request) {
2780
+ return {
2781
+ ...request,
2782
+ inputPreview: redactUnknown3(request.inputPreview)
2783
+ };
2784
+ }
2785
+ function ruleMatches(rule, ctx) {
2786
+ if (!ctx.action) return false;
2787
+ if (rule.providerId && rule.providerId !== ctx.connection.providerId) return false;
2788
+ if (rule.connectorId && rule.connectorId !== ctx.connection.connectorId) return false;
2789
+ if (rule.action && rule.action !== ctx.request.action) return false;
2790
+ if (rule.risk && rule.risk !== ctx.action.risk) return false;
2791
+ if (rule.maxRisk && riskRank(ctx.action.risk) > riskRank(rule.maxRisk)) return false;
2792
+ if (rule.dataClass && rule.dataClass !== ctx.action.dataClass) return false;
2793
+ return true;
2794
+ }
2795
+ function riskRank(risk) {
2796
+ if (risk === "read") return 0;
2797
+ if (risk === "write") return 1;
2798
+ return 2;
2799
+ }
2800
+ function defaultReason2(effect, risk) {
2801
+ if (effect === "allow") return `${risk} integration action allowed by default policy.`;
2802
+ if (effect === "deny") return `${risk} integration action denied by default policy.`;
2803
+ return `${risk} integration action requires approval by default policy.`;
2804
+ }
2805
+ function previewInput(input) {
2806
+ return redactUnknown3(input);
2807
+ }
2808
+ function redactUnknown3(value) {
2809
+ if (Array.isArray(value)) return value.map(redactUnknown3);
2810
+ if (!value || typeof value !== "object") return value;
2811
+ const out = {};
2812
+ for (const [key, child] of Object.entries(value)) {
2813
+ if (/token|secret|password|authorization|api[_-]?key|credential/i.test(key)) {
2814
+ out[key] = "[REDACTED]";
2815
+ } else {
2816
+ out[key] = redactUnknown3(child);
2817
+ }
2818
+ }
2819
+ return out;
2820
+ }
2821
+
2822
+ // src/presets.ts
2823
+ function createPlatformIntegrationPolicyPreset(options = {}) {
2824
+ return new StaticIntegrationPolicyEngine({
2825
+ ...options,
2826
+ defaultReadEffect: "allow",
2827
+ defaultWriteEffect: options.allowWritesWithoutApproval ? "allow" : "require_approval",
2828
+ defaultDestructiveEffect: options.allowDestructiveActions ? "require_approval" : "deny",
2829
+ rules: [
2830
+ ...options.allowProviderPassthrough ? [] : [{
2831
+ id: "deny-provider-native-passthrough",
2832
+ action: "provider.http.request",
2833
+ effect: "deny",
2834
+ reason: "Provider-native passthrough is disabled by default. Promote the connector action or enable passthrough explicitly."
2835
+ }],
2836
+ ...options.rules ?? []
2837
+ ]
2838
+ });
2839
+ }
2840
+
1293
2841
  // src/connectors/types.ts
1294
2842
  var ResourceContention = class extends Error {
1295
2843
  constructor(message, alternatives = [], currentState) {
@@ -1344,7 +2892,7 @@ function assertValidConnectorManifest(manifest) {
1344
2892
  }
1345
2893
 
1346
2894
  // src/connectors/oauth.ts
1347
- import { createHash, randomBytes } from "crypto";
2895
+ import { createHash as createHash3, randomBytes } from "crypto";
1348
2896
  var PENDING_TTL_MS = 10 * 60 * 1e3;
1349
2897
  var InMemoryOAuthFlowStore = class {
1350
2898
  pendingFlows = /* @__PURE__ */ new Map();
@@ -1372,7 +2920,7 @@ function startOAuthFlow(input) {
1372
2920
  const now = input.now ?? Date.now();
1373
2921
  store.sweep?.(now);
1374
2922
  const codeVerifier = base64Url(randomBytes(48));
1375
- const codeChallenge = base64Url(createHash("sha256").update(codeVerifier).digest());
2923
+ const codeChallenge = base64Url(createHash3("sha256").update(codeVerifier).digest());
1376
2924
  const state = base64Url(randomBytes(24));
1377
2925
  store.put(state, {
1378
2926
  codeVerifier,
@@ -1837,7 +3385,7 @@ function readMetaString(meta, key) {
1837
3385
  }
1838
3386
 
1839
3387
  // src/connectors/adapters/google-sheets.ts
1840
- import { createHash as createHash2 } from "crypto";
3388
+ import { createHash as createHash4 } from "crypto";
1841
3389
  var SCOPES2 = ["https://www.googleapis.com/auth/spreadsheets"];
1842
3390
  var AUTH_URL2 = "https://accounts.google.com/o/oauth2/v2/auth";
1843
3391
  var TOKEN_URL2 = "https://oauth2.googleapis.com/token";
@@ -2111,7 +3659,7 @@ async function fetchAllRows(accessToken, meta) {
2111
3659
  function rowFingerprint(values) {
2112
3660
  const keys = Object.keys(values).sort();
2113
3661
  const blob = keys.map((k) => `${k}=${values[k]}`).join("");
2114
- return createHash2("sha256").update(blob).digest("hex").slice(0, 16);
3662
+ return createHash4("sha256").update(blob).digest("hex").slice(0, 16);
2115
3663
  }
2116
3664
  function matchesPredicate(row, predicate) {
2117
3665
  for (const [k, v] of Object.entries(predicate)) {
@@ -4150,7 +5698,7 @@ var repoParams = {
4150
5698
  },
4151
5699
  required: ["owner", "repo"]
4152
5700
  };
4153
- var githubConnector = declarativeRestConnector({
5701
+ var githubConnector2 = declarativeRestConnector({
4154
5702
  kind: "github",
4155
5703
  displayName: "GitHub",
4156
5704
  description: "Search repositories/issues and create or update GitHub issues through a user-scoped token.",
@@ -4485,7 +6033,7 @@ var salesforceConnector = declarativeRestConnector({
4485
6033
  });
4486
6034
 
4487
6035
  // src/catalog.ts
4488
- var riskRank = {
6036
+ var riskRank2 = {
4489
6037
  read: 0,
4490
6038
  write: 1,
4491
6039
  destructive: 2
@@ -4508,7 +6056,7 @@ function buildIntegrationToolCatalog(connectors) {
4508
6056
  const tools = [];
4509
6057
  for (const connector of connectors) {
4510
6058
  for (const action of connector.actions) {
4511
- const tags = unique2([
6059
+ const tags = unique4([
4512
6060
  connector.id,
4513
6061
  connector.providerId,
4514
6062
  connector.title,
@@ -4527,173 +6075,73 @@ function buildIntegrationToolCatalog(connectors) {
4527
6075
  providerId: connector.providerId,
4528
6076
  connectorId: connector.id,
4529
6077
  connectorTitle: connector.title,
4530
- category: connector.category,
4531
- action,
4532
- risk: action.risk,
4533
- dataClass: action.dataClass,
4534
- requiredScopes: action.requiredScopes,
4535
- inputSchema: action.inputSchema,
4536
- outputSchema: action.outputSchema,
4537
- tags
4538
- });
4539
- }
4540
- }
4541
- return tools;
4542
- }
4543
- function searchIntegrationTools(catalog, query, filters = {}) {
4544
- const terms = tokenize(query);
4545
- const filtered = catalog.filter((tool) => {
4546
- if (filters.providerId && tool.providerId !== filters.providerId) return false;
4547
- if (filters.connectorId && tool.connectorId !== filters.connectorId) return false;
4548
- if (filters.category && tool.category !== filters.category) return false;
4549
- if (filters.dataClass && tool.dataClass !== filters.dataClass) return false;
4550
- if (filters.maxRisk && riskRank[tool.risk] > riskRank[filters.maxRisk]) return false;
4551
- return true;
4552
- });
4553
- const scored = filtered.map((tool) => scoreTool(tool, terms));
4554
- return scored.filter((result) => terms.length === 0 || result.score > 0).sort((a, b) => b.score - a.score || a.tool.name.localeCompare(b.tool.name)).slice(0, filters.limit ?? 20);
4555
- }
4556
- function toMcpTools(tools) {
4557
- return tools.map((tool) => ({
4558
- name: tool.name,
4559
- description: `${tool.title}. ${tool.description}`,
4560
- inputSchema: tool.inputSchema ?? {
4561
- type: "object",
4562
- additionalProperties: true,
4563
- properties: {}
4564
- }
4565
- }));
4566
- }
4567
- function scoreTool(tool, terms) {
4568
- if (terms.length === 0) return { tool, score: 1, matched: [] };
4569
- const haystack = new Set(tool.tags);
4570
- const matched = [];
4571
- let score = 0;
4572
- for (const term of terms) {
4573
- if (haystack.has(term)) {
4574
- matched.push(term);
4575
- score += 4;
4576
- continue;
4577
- }
4578
- if (tool.tags.some((tag) => tag.includes(term))) {
4579
- matched.push(term);
4580
- score += 1;
4581
- }
4582
- }
4583
- if (tool.risk === "read") score += 0.25;
4584
- return { tool, score, matched: unique2(matched) };
4585
- }
4586
- function tokenize(value) {
4587
- return value.toLowerCase().split(/[^a-z0-9]+/g).map((part) => part.trim()).filter(Boolean);
4588
- }
4589
- function encodeToolPart(value) {
4590
- return Buffer.from(value, "utf8").toString("base64url").replace(/_/g, ".");
4591
- }
4592
- function decodeToolPart(value) {
4593
- return Buffer.from(value.replace(/\./g, "_"), "base64url").toString("utf8");
4594
- }
4595
- function unique2(values) {
4596
- return [...new Set(values)];
4597
- }
4598
-
4599
- // src/policy.ts
4600
- import { randomUUID } from "crypto";
4601
- var StaticIntegrationPolicyEngine = class {
4602
- rules;
4603
- defaultReadEffect;
4604
- defaultWriteEffect;
4605
- defaultDestructiveEffect;
4606
- now;
4607
- constructor(options = {}) {
4608
- this.rules = options.rules ?? [];
4609
- this.defaultReadEffect = options.defaultReadEffect ?? "allow";
4610
- this.defaultWriteEffect = options.defaultWriteEffect ?? "require_approval";
4611
- this.defaultDestructiveEffect = options.defaultDestructiveEffect ?? "deny";
4612
- this.now = options.now ?? (() => /* @__PURE__ */ new Date());
4613
- }
4614
- decide(ctx) {
4615
- const action = ctx.action;
4616
- if (!action) return { decision: "deny", reason: "Integration action is missing from connector catalog." };
4617
- const matched = this.rules.find((rule) => ruleMatches(rule, ctx));
4618
- const effect = matched?.effect ?? this.defaultEffect(action.risk);
4619
- const reason = matched?.reason ?? defaultReason(effect, action.risk);
4620
- if (effect === "allow") return { decision: "allow", reason, metadata: matched ? { ruleId: matched.id } : void 0 };
4621
- if (effect === "deny") return { decision: "deny", reason, metadata: matched ? { ruleId: matched.id } : void 0 };
4622
- return {
4623
- decision: "require_approval",
4624
- reason,
4625
- approval: buildApprovalRequest(ctx, reason, this.now()),
4626
- metadata: matched ? { ruleId: matched.id } : void 0
4627
- };
4628
- }
4629
- defaultEffect(risk) {
4630
- if (risk === "read") return this.defaultReadEffect;
4631
- if (risk === "write") return this.defaultWriteEffect;
4632
- return this.defaultDestructiveEffect;
4633
- }
4634
- };
4635
- function createDefaultIntegrationPolicyEngine(options = {}) {
4636
- return new StaticIntegrationPolicyEngine(options);
4637
- }
4638
- function buildApprovalRequest(ctx, reason, requestedAt) {
4639
- if (!ctx.action) {
4640
- throw new Error("Cannot build approval request without an action descriptor.");
4641
- }
4642
- return {
4643
- id: `approval_${randomUUID()}`,
4644
- connectionId: ctx.connection.id,
4645
- providerId: ctx.connection.providerId,
4646
- connectorId: ctx.connection.connectorId,
4647
- action: ctx.request.action,
4648
- actor: { type: ctx.subject.type, id: ctx.subject.id },
4649
- risk: ctx.action.risk,
4650
- dataClass: ctx.action.dataClass,
4651
- reason,
4652
- requestedAt: requestedAt.toISOString(),
4653
- inputPreview: previewInput(ctx.request.input)
4654
- };
6078
+ category: connector.category,
6079
+ action,
6080
+ risk: action.risk,
6081
+ dataClass: action.dataClass,
6082
+ requiredScopes: action.requiredScopes,
6083
+ inputSchema: action.inputSchema,
6084
+ outputSchema: action.outputSchema,
6085
+ tags
6086
+ });
6087
+ }
6088
+ }
6089
+ return tools;
4655
6090
  }
4656
- function redactApprovalRequest(request) {
4657
- return {
4658
- ...request,
4659
- inputPreview: redactUnknown(request.inputPreview)
4660
- };
6091
+ function searchIntegrationTools(catalog, query, filters = {}) {
6092
+ const terms = tokenize(query);
6093
+ const filtered = catalog.filter((tool) => {
6094
+ if (filters.providerId && tool.providerId !== filters.providerId) return false;
6095
+ if (filters.connectorId && tool.connectorId !== filters.connectorId) return false;
6096
+ if (filters.category && tool.category !== filters.category) return false;
6097
+ if (filters.dataClass && tool.dataClass !== filters.dataClass) return false;
6098
+ if (filters.maxRisk && riskRank2[tool.risk] > riskRank2[filters.maxRisk]) return false;
6099
+ return true;
6100
+ });
6101
+ const scored = filtered.map((tool) => scoreTool(tool, terms));
6102
+ return scored.filter((result) => terms.length === 0 || result.score > 0).sort((a, b) => b.score - a.score || a.tool.name.localeCompare(b.tool.name)).slice(0, filters.limit ?? 20);
4661
6103
  }
4662
- function ruleMatches(rule, ctx) {
4663
- if (!ctx.action) return false;
4664
- if (rule.providerId && rule.providerId !== ctx.connection.providerId) return false;
4665
- if (rule.connectorId && rule.connectorId !== ctx.connection.connectorId) return false;
4666
- if (rule.action && rule.action !== ctx.request.action) return false;
4667
- if (rule.risk && rule.risk !== ctx.action.risk) return false;
4668
- if (rule.maxRisk && riskRank2(ctx.action.risk) > riskRank2(rule.maxRisk)) return false;
4669
- if (rule.dataClass && rule.dataClass !== ctx.action.dataClass) return false;
4670
- return true;
6104
+ function toMcpTools(tools) {
6105
+ return tools.map((tool) => ({
6106
+ name: tool.name,
6107
+ description: `${tool.title}. ${tool.description}`,
6108
+ inputSchema: tool.inputSchema ?? {
6109
+ type: "object",
6110
+ additionalProperties: true,
6111
+ properties: {}
6112
+ }
6113
+ }));
4671
6114
  }
4672
- function riskRank2(risk) {
4673
- if (risk === "read") return 0;
4674
- if (risk === "write") return 1;
4675
- return 2;
6115
+ function scoreTool(tool, terms) {
6116
+ if (terms.length === 0) return { tool, score: 1, matched: [] };
6117
+ const haystack = new Set(tool.tags);
6118
+ const matched = [];
6119
+ let score = 0;
6120
+ for (const term of terms) {
6121
+ if (haystack.has(term)) {
6122
+ matched.push(term);
6123
+ score += 4;
6124
+ continue;
6125
+ }
6126
+ if (tool.tags.some((tag) => tag.includes(term))) {
6127
+ matched.push(term);
6128
+ score += 1;
6129
+ }
6130
+ }
6131
+ if (tool.risk === "read") score += 0.25;
6132
+ return { tool, score, matched: unique4(matched) };
4676
6133
  }
4677
- function defaultReason(effect, risk) {
4678
- if (effect === "allow") return `${risk} integration action allowed by default policy.`;
4679
- if (effect === "deny") return `${risk} integration action denied by default policy.`;
4680
- return `${risk} integration action requires approval by default policy.`;
6134
+ function tokenize(value) {
6135
+ return value.toLowerCase().split(/[^a-z0-9]+/g).map((part) => part.trim()).filter(Boolean);
4681
6136
  }
4682
- function previewInput(input) {
4683
- return redactUnknown(input);
6137
+ function encodeToolPart(value) {
6138
+ return Buffer.from(value, "utf8").toString("base64url").replace(/_/g, ".");
4684
6139
  }
4685
- function redactUnknown(value) {
4686
- if (Array.isArray(value)) return value.map(redactUnknown);
4687
- if (!value || typeof value !== "object") return value;
4688
- const out = {};
4689
- for (const [key, child] of Object.entries(value)) {
4690
- if (/token|secret|password|authorization|api[_-]?key|credential/i.test(key)) {
4691
- out[key] = "[REDACTED]";
4692
- } else {
4693
- out[key] = redactUnknown(child);
4694
- }
4695
- }
4696
- return out;
6140
+ function decodeToolPart(value) {
6141
+ return Buffer.from(value.replace(/\./g, "_"), "base64url").toString("utf8");
6142
+ }
6143
+ function unique4(values) {
6144
+ return [...new Set(values)];
4697
6145
  }
4698
6146
 
4699
6147
  // src/sandbox.ts
@@ -4754,13 +6202,13 @@ function redactInvocationEnvelope(envelope) {
4754
6202
  return {
4755
6203
  ...envelope,
4756
6204
  capabilityToken: "[REDACTED]",
4757
- input: redactUnknown2(envelope.input)
6205
+ input: redactUnknown4(envelope.input)
4758
6206
  };
4759
6207
  }
4760
6208
  function redactCapability(capability) {
4761
6209
  return {
4762
6210
  ...capability,
4763
- metadata: redactUnknown2(capability.metadata)
6211
+ metadata: redactUnknown4(capability.metadata)
4764
6212
  };
4765
6213
  }
4766
6214
  function normalizeIntegrationResult(result) {
@@ -4788,15 +6236,40 @@ function normalizeIntegrationResult(result) {
4788
6236
  metadata: result.metadata
4789
6237
  };
4790
6238
  }
4791
- function redactUnknown2(value) {
4792
- if (Array.isArray(value)) return value.map(redactUnknown2);
6239
+ async function dispatchIntegrationInvocation(envelope, options) {
6240
+ try {
6241
+ validateIntegrationInvocationEnvelope(envelope, options);
6242
+ const result = await options.hub.invokeWithCapability(
6243
+ envelope.capabilityToken,
6244
+ invocationRequestFromEnvelope(envelope)
6245
+ );
6246
+ return normalizeIntegrationResult(result);
6247
+ } catch (error) {
6248
+ return {
6249
+ status: "failed",
6250
+ action: typeof envelope?.action === "string" ? envelope.action : "unknown",
6251
+ error: error instanceof Error ? error.message : "Integration invocation failed."
6252
+ };
6253
+ }
6254
+ }
6255
+ var IntegrationSandboxHost = class {
6256
+ options;
6257
+ constructor(options) {
6258
+ this.options = options;
6259
+ }
6260
+ dispatch(envelope) {
6261
+ return dispatchIntegrationInvocation(envelope, this.options);
6262
+ }
6263
+ };
6264
+ function redactUnknown4(value) {
6265
+ if (Array.isArray(value)) return value.map(redactUnknown4);
4793
6266
  if (!value || typeof value !== "object") return value;
4794
6267
  const out = {};
4795
6268
  for (const [key, child] of Object.entries(value)) {
4796
6269
  if (/token|secret|password|authorization|api[_-]?key|credential/i.test(key)) {
4797
6270
  out[key] = "[REDACTED]";
4798
6271
  } else {
4799
- out[key] = redactUnknown2(child);
6272
+ out[key] = redactUnknown4(child);
4800
6273
  }
4801
6274
  }
4802
6275
  return out;
@@ -4808,152 +6281,6 @@ function isPlainRecord(value) {
4808
6281
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
4809
6282
  }
4810
6283
 
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
6284
  // src/importers.ts
4958
6285
  var HTTP_METHODS = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete"]);
4959
6286
  function importOpenApiConnector(document, options) {
@@ -5008,7 +6335,7 @@ function importMcpConnector(catalog, options) {
5008
6335
  }));
5009
6336
  }
5010
6337
  function connectorFromActions(options, actions) {
5011
- const scopes = unique3([
6338
+ const scopes = unique5([
5012
6339
  ...options.scopes ?? [],
5013
6340
  ...actions.flatMap((action) => action.requiredScopes)
5014
6341
  ]);
@@ -5040,7 +6367,7 @@ function riskFromMcpTool(tool, fallback) {
5040
6367
  }
5041
6368
  function scopesFromOpenApiOperation(operation, fallback) {
5042
6369
  const scopes = (operation.security ?? []).flatMap((entry) => Object.values(entry).flat());
5043
- return unique3(scopes.length > 0 ? scopes : fallback);
6370
+ return unique5(scopes.length > 0 ? scopes : fallback);
5044
6371
  }
5045
6372
  function openApiInputSchema(path, method, operation) {
5046
6373
  return {
@@ -5060,7 +6387,7 @@ function titleFromId(id) {
5060
6387
  function isObject(value) {
5061
6388
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
5062
6389
  }
5063
- function unique3(values) {
6390
+ function unique5(values) {
5064
6391
  return [...new Set(values)];
5065
6392
  }
5066
6393
 
@@ -5111,7 +6438,7 @@ function normalizeGatewayCatalog(entries, options) {
5111
6438
  title,
5112
6439
  category: normalizeCategory(entry.category),
5113
6440
  auth: normalizeAuth(entry.auth),
5114
- scopes: unique4([
6441
+ scopes: unique6([
5115
6442
  ...entry.scopes ?? [],
5116
6443
  ...actions.flatMap((action) => action.requiredScopes)
5117
6444
  ]),
@@ -5141,7 +6468,7 @@ function normalizeActions(actions, fallbackScopes) {
5141
6468
  id,
5142
6469
  title: action.title ?? action.name ?? titleFromId2(id),
5143
6470
  risk: normalizeRisk(action.risk),
5144
- requiredScopes: unique4([
6471
+ requiredScopes: unique6([
5145
6472
  ...action.requiredScopes ?? [],
5146
6473
  ...action.scopes ?? [],
5147
6474
  ...action.requiredScopes?.length || action.scopes?.length ? [] : fallbackScopes
@@ -5155,21 +6482,21 @@ function normalizeActions(actions, fallbackScopes) {
5155
6482
  }).filter((action) => action.id);
5156
6483
  }
5157
6484
  function normalizeTriggers(triggers, fallbackScopes) {
5158
- const normalized = triggers.map((trigger) => {
5159
- const id = slug2(trigger.id ?? trigger.key ?? trigger.name ?? trigger.title ?? "");
6485
+ const normalized = triggers.map((trigger2) => {
6486
+ const id = slug2(trigger2.id ?? trigger2.key ?? trigger2.name ?? trigger2.title ?? "");
5160
6487
  return {
5161
6488
  id,
5162
- title: trigger.title ?? trigger.name ?? titleFromId2(id),
5163
- requiredScopes: unique4([
5164
- ...trigger.requiredScopes ?? [],
5165
- ...trigger.scopes ?? [],
5166
- ...trigger.requiredScopes?.length || trigger.scopes?.length ? [] : fallbackScopes
6489
+ title: trigger2.title ?? trigger2.name ?? titleFromId2(id),
6490
+ requiredScopes: unique6([
6491
+ ...trigger2.requiredScopes ?? [],
6492
+ ...trigger2.scopes ?? [],
6493
+ ...trigger2.requiredScopes?.length || trigger2.scopes?.length ? [] : fallbackScopes
5167
6494
  ]),
5168
- dataClass: normalizeDataClass(trigger.dataClass),
5169
- description: trigger.description,
5170
- payloadSchema: trigger.payloadSchema
6495
+ dataClass: normalizeDataClass(trigger2.dataClass),
6496
+ description: trigger2.description,
6497
+ payloadSchema: trigger2.payloadSchema
5171
6498
  };
5172
- }).filter((trigger) => trigger.id);
6499
+ }).filter((trigger2) => trigger2.id);
5173
6500
  return normalized.length > 0 ? normalized : void 0;
5174
6501
  }
5175
6502
  function defaultActionsFor(category, scopes) {
@@ -5249,7 +6576,7 @@ function slug2(value) {
5249
6576
  function titleFromId2(id) {
5250
6577
  return id.split("-").filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ");
5251
6578
  }
5252
- function unique4(values) {
6579
+ function unique6(values) {
5253
6580
  return [...new Set(values)];
5254
6581
  }
5255
6582
 
@@ -5337,7 +6664,7 @@ var IntegrationRuntime = class {
5337
6664
  const connector = {
5338
6665
  ...entry.connector,
5339
6666
  actions: entry.connector.actions.filter((action) => grant.allowedActions.includes(action.id)),
5340
- triggers: entry.connector.triggers?.filter((trigger) => grant.allowedTriggers.includes(trigger.id)),
6667
+ triggers: entry.connector.triggers?.filter((trigger2) => grant.allowedTriggers.includes(trigger2.id)),
5341
6668
  scopes: entry.connector.scopes.filter((scope) => grant.scopes.includes(scope))
5342
6669
  };
5343
6670
  const capability = await this.hub.issueCapability({
@@ -5431,32 +6758,32 @@ function missing(requirement, status, message, connector, registryEntry2) {
5431
6758
  }
5432
6759
  function requiredActions(requirement, connector) {
5433
6760
  if (requirement.mode === "trigger") return [];
5434
- if (requirement.requiredActions?.length) return unique5(requirement.requiredActions);
6761
+ if (requirement.requiredActions?.length) return unique7(requirement.requiredActions);
5435
6762
  const actions = connector.actions.filter((action) => {
5436
6763
  if (requirement.mode === "read") return action.risk === "read";
5437
6764
  if (requirement.mode === "write") return action.risk !== "read";
5438
6765
  return false;
5439
6766
  });
5440
- return unique5(actions.map((action) => action.id));
6767
+ return unique7(actions.map((action) => action.id));
5441
6768
  }
5442
6769
  function requiredTriggers(requirement, connector) {
5443
- if (requirement.requiredTriggers?.length) return unique5(requirement.requiredTriggers);
6770
+ if (requirement.requiredTriggers?.length) return unique7(requirement.requiredTriggers);
5444
6771
  if (requirement.mode !== "trigger") return [];
5445
- return unique5((connector.triggers ?? []).map((trigger) => trigger.id));
6772
+ return unique7((connector.triggers ?? []).map((trigger2) => trigger2.id));
5446
6773
  }
5447
6774
  function requiredScopes(requirement, connector) {
5448
- if (requirement.requiredScopes?.length) return unique5(requirement.requiredScopes);
6775
+ if (requirement.requiredScopes?.length) return unique7(requirement.requiredScopes);
5449
6776
  const actionIds = new Set(requiredActions(requirement, connector));
5450
6777
  const triggerIds = new Set(requiredTriggers(requirement, connector));
5451
- return unique5([
6778
+ return unique7([
5452
6779
  ...connector.actions.filter((action) => actionIds.has(action.id)).flatMap((action) => action.requiredScopes),
5453
- ...(connector.triggers ?? []).filter((trigger) => triggerIds.has(trigger.id)).flatMap((trigger) => trigger.requiredScopes)
6780
+ ...(connector.triggers ?? []).filter((trigger2) => triggerIds.has(trigger2.id)).flatMap((trigger2) => trigger2.requiredScopes)
5454
6781
  ]);
5455
6782
  }
5456
6783
  function sameActor(a, b) {
5457
6784
  return a.type === b.type && a.id === b.id;
5458
6785
  }
5459
- function unique5(values) {
6786
+ function unique7(values) {
5460
6787
  return [...new Set(values)];
5461
6788
  }
5462
6789
 
@@ -5740,11 +7067,11 @@ var IntegrationHub = class {
5740
7067
  assertScopes(connection, request.scopes);
5741
7068
  const now = this.now();
5742
7069
  const capability = {
5743
- id: `cap_${randomUUID2()}`,
7070
+ id: `cap_${randomUUID3()}`,
5744
7071
  subject: request.subject,
5745
7072
  connectionId: request.connectionId,
5746
- scopes: unique6(request.scopes),
5747
- allowedActions: unique6(request.allowedActions),
7073
+ scopes: unique8(request.scopes),
7074
+ allowedActions: unique8(request.allowedActions),
5748
7075
  issuedAt: now.toISOString(),
5749
7076
  expiresAt: new Date(now.getTime() + request.ttlMs).toISOString(),
5750
7077
  metadata: request.metadata
@@ -5797,18 +7124,18 @@ var IntegrationHub = class {
5797
7124
  }
5798
7125
  return proceed();
5799
7126
  }
5800
- async subscribeTrigger(connectionId, trigger, targetUrl) {
7127
+ async subscribeTrigger(connectionId, trigger2, targetUrl) {
5801
7128
  const connection = await this.requireConnection(connectionId);
5802
7129
  this.assertConnectionActive(connection);
5803
7130
  const provider = this.requireProvider(connection.providerId);
5804
7131
  const connector = await this.requireConnector(provider, connection.connectorId);
5805
- const spec = connector.triggers?.find((candidate) => candidate.id === trigger);
5806
- if (!spec) throw new IntegrationError(`Trigger ${trigger} is not defined by connector ${connector.id}.`, "action_not_found");
7132
+ const spec = connector.triggers?.find((candidate) => candidate.id === trigger2);
7133
+ if (!spec) throw new IntegrationError(`Trigger ${trigger2} is not defined by connector ${connector.id}.`, "action_not_found");
5807
7134
  assertScopes(connection, spec.requiredScopes);
5808
7135
  if (!provider.subscribeTrigger) {
5809
7136
  throw new IntegrationError(`Provider ${provider.id} does not support triggers.`, "auth_not_supported");
5810
7137
  }
5811
- return provider.subscribeTrigger(connection, trigger, targetUrl);
7138
+ return provider.subscribeTrigger(connection, trigger2, targetUrl);
5812
7139
  }
5813
7140
  requireProvider(providerId) {
5814
7141
  const provider = this.providers.get(providerId);
@@ -5893,10 +7220,10 @@ function createMockIntegrationProvider(options = {}) {
5893
7220
  action: request.action,
5894
7221
  output: { echo: request.input ?? null }
5895
7222
  },
5896
- subscribeTrigger: (connection, trigger, targetUrl) => ({
5897
- id: `sub_${connection.id}_${trigger}`,
7223
+ subscribeTrigger: (connection, trigger2, targetUrl) => ({
7224
+ id: `sub_${connection.id}_${trigger2}`,
5898
7225
  connectionId: connection.id,
5899
- trigger,
7226
+ trigger: trigger2,
5900
7227
  targetUrl,
5901
7228
  status: "active",
5902
7229
  createdAt: (/* @__PURE__ */ new Date(0)).toISOString()
@@ -5924,10 +7251,10 @@ function createHttpIntegrationProvider(options) {
5924
7251
  request
5925
7252
  }, options.bearer);
5926
7253
  },
5927
- async subscribeTrigger(connection, trigger, targetUrl) {
7254
+ async subscribeTrigger(connection, trigger2, targetUrl) {
5928
7255
  return postJson(fetcher, `${baseUrl}/triggers/subscribe`, {
5929
7256
  connection,
5930
- trigger,
7257
+ trigger: trigger2,
5931
7258
  targetUrl
5932
7259
  }, options.bearer);
5933
7260
  },
@@ -5990,61 +7317,95 @@ function base64UrlEncode(value) {
5990
7317
  function base64UrlDecode(value) {
5991
7318
  return Buffer.from(value, "base64url").toString("utf8");
5992
7319
  }
5993
- function unique6(values) {
7320
+ function unique8(values) {
5994
7321
  return [...new Set(values)];
5995
7322
  }
5996
7323
  export {
5997
7324
  ACTIVEPIECES_OVERRIDES,
7325
+ ApprovalBackedPolicyEngine,
7326
+ CANONICAL_INTEGRATION_ACTIONS,
5998
7327
  CredentialsExpired,
7328
+ DEFAULT_INTEGRATION_BRIDGE_ENV,
5999
7329
  DEFAULT_SIGNATURE_TOLERANCE_SECONDS,
7330
+ DefaultIntegrationActionGuard,
6000
7331
  INTEGRATION_FAMILIES,
6001
7332
  InMemoryConnectionStore,
7333
+ InMemoryIntegrationApprovalStore,
7334
+ InMemoryIntegrationAuditStore,
7335
+ InMemoryIntegrationEventStore,
6002
7336
  InMemoryIntegrationGrantStore,
7337
+ InMemoryIntegrationHealthcheckStore,
7338
+ InMemoryIntegrationIdempotencyStore,
7339
+ InMemoryIntegrationSecretStore,
6003
7340
  InMemoryIntegrationWorkflowStore,
6004
7341
  InMemoryOAuthFlowStore,
6005
7342
  IntegrationError,
6006
7343
  IntegrationHub,
6007
7344
  IntegrationRuntime,
7345
+ IntegrationRuntimeError,
7346
+ IntegrationSandboxHost,
6008
7347
  IntegrationWorkflowRuntime,
7348
+ PROVIDER_PASSTHROUGH_ACTION,
6009
7349
  ResourceContention,
6010
7350
  StaticIntegrationPolicyEngine,
7351
+ TangleIntegrationsClient,
6011
7352
  _resetPendingFlowsForTests,
6012
7353
  airtableConnector,
6013
7354
  asanaConnector,
6014
7355
  assertValidConnectorManifest,
7356
+ assertValidIntegrationManifest,
6015
7357
  assertValidIntegrationSpec,
6016
7358
  buildActivepiecesConnectors,
6017
7359
  buildApprovalRequest,
7360
+ buildCanonicalLaunchConnectors,
6018
7361
  buildDefaultIntegrationRegistry,
6019
7362
  buildHealthcheckPlan,
7363
+ buildIntegrationBridgeEnvironment,
7364
+ buildIntegrationBridgePayload,
6020
7365
  buildIntegrationCoverageConnectors,
6021
7366
  buildIntegrationInvocationEnvelope,
6022
7367
  buildIntegrationToolCatalog,
7368
+ calendarExercisePlannerManifest,
7369
+ canonicalActionConnectorId,
6023
7370
  canonicalConnectorId,
6024
7371
  composeIntegrationRegistry,
6025
7372
  consoleStepsToText,
6026
7373
  consumePendingFlow,
7374
+ createApprovalBackedPolicyEngine,
7375
+ createAuditingActionGuard,
7376
+ createConnectionCredentialResolver,
6027
7377
  createConnectorAdapterProvider,
7378
+ createCredentialBackedAdapterProvider,
7379
+ createDefaultIntegrationActionGuard,
6028
7380
  createDefaultIntegrationPolicyEngine,
6029
7381
  createGatewayCatalogProvider,
6030
7382
  createHttpIntegrationProvider,
7383
+ createIntegrationAuditEvent,
6031
7384
  createIntegrationRuntime,
6032
7385
  createIntegrationWorkflowRuntime,
6033
7386
  createMockIntegrationProvider,
7387
+ createPlatformIntegrationPolicyPreset,
7388
+ createTangleIntegrationsClient,
6034
7389
  declarativeRestConnector,
7390
+ decodeIntegrationBridgePayload,
7391
+ dispatchIntegrationInvocation,
7392
+ encodeIntegrationBridgePayload,
6035
7393
  exchangeAuthorizationCode,
7394
+ explainMissingRequirements,
6036
7395
  firstHeader,
6037
7396
  getActivepiecesOverride,
6038
7397
  getIntegrationFamily,
6039
7398
  getIntegrationSpec,
6040
- githubConnector,
7399
+ githubConnector2 as githubConnector,
6041
7400
  gitlabConnector,
6042
7401
  googleCalendar,
6043
7402
  googleSheets,
7403
+ healthcheckRequest,
6044
7404
  hubspot,
6045
7405
  importGraphqlConnector,
6046
7406
  importMcpConnector,
6047
7407
  importOpenApiConnector,
7408
+ inferIntegrationManifestFromTools,
6048
7409
  inferIntegrationSupportTier,
6049
7410
  integrationCoverageChecklistMarkdown,
6050
7411
  integrationSpecToConnector,
@@ -6057,18 +7418,30 @@ export {
6057
7418
  manifestToConnector,
6058
7419
  microsoftCalendar,
6059
7420
  normalizeGatewayCatalog,
7421
+ normalizeIntegrationError,
6060
7422
  normalizeIntegrationResult,
6061
7423
  notionDatabase,
7424
+ parseIntegrationBridgeEnvironment,
6062
7425
  parseIntegrationToolName,
6063
7426
  parseStripeSignatureHeader,
7427
+ receiveIntegrationWebhook,
6064
7428
  redactApprovalRequest,
6065
7429
  redactCapability,
7430
+ redactIntegrationBridgePayload,
6066
7431
  redactInvocationEnvelope,
6067
7432
  refreshAccessToken,
6068
7433
  renderAgentToolDescription,
7434
+ renderApprovalCopy,
7435
+ renderConsentSummary,
6069
7436
  renderConsoleSteps,
6070
7437
  renderRunbookMarkdown,
7438
+ resolveConnectionCredentials,
7439
+ resolveIntegrationApproval,
7440
+ revokeConnection,
7441
+ runIntegrationHealthcheck,
7442
+ runIntegrationHealthchecks,
6071
7443
  salesforceConnector,
7444
+ sanitizeAuditConnection,
6072
7445
  sanitizeConnection,
6073
7446
  searchIntegrationTools,
6074
7447
  signCapability,
@@ -6076,6 +7449,8 @@ export {
6076
7449
  slackEventsConnector,
6077
7450
  specAuthToConnectorAuth,
6078
7451
  startOAuthFlow,
7452
+ statusForCode,
7453
+ storedEventToTriggerEvent,
6079
7454
  stripePackConnector,
6080
7455
  stripeWebhookReceiverConnector,
6081
7456
  summarizeIntegrationRegistry,
@@ -6085,7 +7460,9 @@ export {
6085
7460
  validateCredentialFormat,
6086
7461
  validateCredentialSet,
6087
7462
  validateIntegrationInvocationEnvelope,
7463
+ validateIntegrationManifest,
6088
7464
  validateIntegrationSpec,
7465
+ validateProviderPassthroughRequest,
6089
7466
  verifyCapabilityToken,
6090
7467
  verifyHmacSignature,
6091
7468
  verifySlackSignature,