@tangle-network/agent-integrations 0.15.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
@@ -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;
@@ -1521,6 +1521,228 @@ function approvalInputHash(input) {
1521
1521
  return createHash("sha256").update(JSON.stringify(input ?? null)).digest("base64url");
1522
1522
  }
1523
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
+
1524
1746
  // src/bridge.ts
1525
1747
  var DEFAULT_INTEGRATION_BRIDGE_ENV = "TANGLE_INTEGRATION_BUNDLE";
1526
1748
  function buildIntegrationBridgePayload(bundle) {
@@ -1586,6 +1808,219 @@ function assertBridgePayload(value) {
1586
1808
  if (!Array.isArray(payload.tools)) throw new Error("Invalid integration bridge tools.");
1587
1809
  }
1588
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
+
1589
2024
  // src/adapter-provider.ts
1590
2025
  function createConnectorAdapterProvider(options) {
1591
2026
  const providerId = options.id ?? "first-party";
@@ -2153,6 +2588,256 @@ function rollupHealthStatus(checks) {
2153
2588
  return "healthy";
2154
2589
  }
2155
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
+
2156
2841
  // src/connectors/types.ts
2157
2842
  var ResourceContention = class extends Error {
2158
2843
  constructor(message, alternatives = [], currentState) {
@@ -5013,7 +5698,7 @@ var repoParams = {
5013
5698
  },
5014
5699
  required: ["owner", "repo"]
5015
5700
  };
5016
- var githubConnector = declarativeRestConnector({
5701
+ var githubConnector2 = declarativeRestConnector({
5017
5702
  kind: "github",
5018
5703
  displayName: "GitHub",
5019
5704
  description: "Search repositories/issues and create or update GitHub issues through a user-scoped token.",
@@ -5348,7 +6033,7 @@ var salesforceConnector = declarativeRestConnector({
5348
6033
  });
5349
6034
 
5350
6035
  // src/catalog.ts
5351
- var riskRank = {
6036
+ var riskRank2 = {
5352
6037
  read: 0,
5353
6038
  write: 1,
5354
6039
  destructive: 2
@@ -5371,7 +6056,7 @@ function buildIntegrationToolCatalog(connectors) {
5371
6056
  const tools = [];
5372
6057
  for (const connector of connectors) {
5373
6058
  for (const action of connector.actions) {
5374
- const tags = unique2([
6059
+ const tags = unique4([
5375
6060
  connector.id,
5376
6061
  connector.providerId,
5377
6062
  connector.title,
@@ -5410,7 +6095,7 @@ function searchIntegrationTools(catalog, query, filters = {}) {
5410
6095
  if (filters.connectorId && tool.connectorId !== filters.connectorId) return false;
5411
6096
  if (filters.category && tool.category !== filters.category) return false;
5412
6097
  if (filters.dataClass && tool.dataClass !== filters.dataClass) return false;
5413
- if (filters.maxRisk && riskRank[tool.risk] > riskRank[filters.maxRisk]) return false;
6098
+ if (filters.maxRisk && riskRank2[tool.risk] > riskRank2[filters.maxRisk]) return false;
5414
6099
  return true;
5415
6100
  });
5416
6101
  const scored = filtered.map((tool) => scoreTool(tool, terms));
@@ -5444,7 +6129,7 @@ function scoreTool(tool, terms) {
5444
6129
  }
5445
6130
  }
5446
6131
  if (tool.risk === "read") score += 0.25;
5447
- return { tool, score, matched: unique2(matched) };
6132
+ return { tool, score, matched: unique4(matched) };
5448
6133
  }
5449
6134
  function tokenize(value) {
5450
6135
  return value.toLowerCase().split(/[^a-z0-9]+/g).map((part) => part.trim()).filter(Boolean);
@@ -5455,110 +6140,10 @@ function encodeToolPart(value) {
5455
6140
  function decodeToolPart(value) {
5456
6141
  return Buffer.from(value.replace(/\./g, "_"), "base64url").toString("utf8");
5457
6142
  }
5458
- function unique2(values) {
6143
+ function unique4(values) {
5459
6144
  return [...new Set(values)];
5460
6145
  }
5461
6146
 
5462
- // src/policy.ts
5463
- import { randomUUID as randomUUID2 } from "crypto";
5464
- var StaticIntegrationPolicyEngine = class {
5465
- rules;
5466
- defaultReadEffect;
5467
- defaultWriteEffect;
5468
- defaultDestructiveEffect;
5469
- now;
5470
- constructor(options = {}) {
5471
- this.rules = options.rules ?? [];
5472
- this.defaultReadEffect = options.defaultReadEffect ?? "allow";
5473
- this.defaultWriteEffect = options.defaultWriteEffect ?? "require_approval";
5474
- this.defaultDestructiveEffect = options.defaultDestructiveEffect ?? "deny";
5475
- this.now = options.now ?? (() => /* @__PURE__ */ new Date());
5476
- }
5477
- decide(ctx) {
5478
- const action = ctx.action;
5479
- if (!action) return { decision: "deny", reason: "Integration action is missing from connector catalog." };
5480
- const matched = this.rules.find((rule) => ruleMatches(rule, ctx));
5481
- const effect = matched?.effect ?? this.defaultEffect(action.risk);
5482
- const reason = matched?.reason ?? defaultReason(effect, action.risk);
5483
- if (effect === "allow") return { decision: "allow", reason, metadata: matched ? { ruleId: matched.id } : void 0 };
5484
- if (effect === "deny") return { decision: "deny", reason, metadata: matched ? { ruleId: matched.id } : void 0 };
5485
- return {
5486
- decision: "require_approval",
5487
- reason,
5488
- approval: buildApprovalRequest(ctx, reason, this.now()),
5489
- metadata: matched ? { ruleId: matched.id } : void 0
5490
- };
5491
- }
5492
- defaultEffect(risk) {
5493
- if (risk === "read") return this.defaultReadEffect;
5494
- if (risk === "write") return this.defaultWriteEffect;
5495
- return this.defaultDestructiveEffect;
5496
- }
5497
- };
5498
- function createDefaultIntegrationPolicyEngine(options = {}) {
5499
- return new StaticIntegrationPolicyEngine(options);
5500
- }
5501
- function buildApprovalRequest(ctx, reason, requestedAt) {
5502
- if (!ctx.action) {
5503
- throw new Error("Cannot build approval request without an action descriptor.");
5504
- }
5505
- return {
5506
- id: `approval_${randomUUID2()}`,
5507
- connectionId: ctx.connection.id,
5508
- providerId: ctx.connection.providerId,
5509
- connectorId: ctx.connection.connectorId,
5510
- action: ctx.request.action,
5511
- actor: { type: ctx.subject.type, id: ctx.subject.id },
5512
- risk: ctx.action.risk,
5513
- dataClass: ctx.action.dataClass,
5514
- reason,
5515
- requestedAt: requestedAt.toISOString(),
5516
- inputPreview: previewInput(ctx.request.input)
5517
- };
5518
- }
5519
- function redactApprovalRequest(request) {
5520
- return {
5521
- ...request,
5522
- inputPreview: redactUnknown2(request.inputPreview)
5523
- };
5524
- }
5525
- function ruleMatches(rule, ctx) {
5526
- if (!ctx.action) return false;
5527
- if (rule.providerId && rule.providerId !== ctx.connection.providerId) return false;
5528
- if (rule.connectorId && rule.connectorId !== ctx.connection.connectorId) return false;
5529
- if (rule.action && rule.action !== ctx.request.action) return false;
5530
- if (rule.risk && rule.risk !== ctx.action.risk) return false;
5531
- if (rule.maxRisk && riskRank2(ctx.action.risk) > riskRank2(rule.maxRisk)) return false;
5532
- if (rule.dataClass && rule.dataClass !== ctx.action.dataClass) return false;
5533
- return true;
5534
- }
5535
- function riskRank2(risk) {
5536
- if (risk === "read") return 0;
5537
- if (risk === "write") return 1;
5538
- return 2;
5539
- }
5540
- function defaultReason(effect, risk) {
5541
- if (effect === "allow") return `${risk} integration action allowed by default policy.`;
5542
- if (effect === "deny") return `${risk} integration action denied by default policy.`;
5543
- return `${risk} integration action requires approval by default policy.`;
5544
- }
5545
- function previewInput(input) {
5546
- return redactUnknown2(input);
5547
- }
5548
- function redactUnknown2(value) {
5549
- if (Array.isArray(value)) return value.map(redactUnknown2);
5550
- if (!value || typeof value !== "object") return value;
5551
- const out = {};
5552
- for (const [key, child] of Object.entries(value)) {
5553
- if (/token|secret|password|authorization|api[_-]?key|credential/i.test(key)) {
5554
- out[key] = "[REDACTED]";
5555
- } else {
5556
- out[key] = redactUnknown2(child);
5557
- }
5558
- }
5559
- return out;
5560
- }
5561
-
5562
6147
  // src/sandbox.ts
5563
6148
  function buildIntegrationInvocationEnvelope(input) {
5564
6149
  const parsed = parseIntegrationToolName(input.toolName);
@@ -5617,13 +6202,13 @@ function redactInvocationEnvelope(envelope) {
5617
6202
  return {
5618
6203
  ...envelope,
5619
6204
  capabilityToken: "[REDACTED]",
5620
- input: redactUnknown3(envelope.input)
6205
+ input: redactUnknown4(envelope.input)
5621
6206
  };
5622
6207
  }
5623
6208
  function redactCapability(capability) {
5624
6209
  return {
5625
6210
  ...capability,
5626
- metadata: redactUnknown3(capability.metadata)
6211
+ metadata: redactUnknown4(capability.metadata)
5627
6212
  };
5628
6213
  }
5629
6214
  function normalizeIntegrationResult(result) {
@@ -5676,15 +6261,15 @@ var IntegrationSandboxHost = class {
5676
6261
  return dispatchIntegrationInvocation(envelope, this.options);
5677
6262
  }
5678
6263
  };
5679
- function redactUnknown3(value) {
5680
- if (Array.isArray(value)) return value.map(redactUnknown3);
6264
+ function redactUnknown4(value) {
6265
+ if (Array.isArray(value)) return value.map(redactUnknown4);
5681
6266
  if (!value || typeof value !== "object") return value;
5682
6267
  const out = {};
5683
6268
  for (const [key, child] of Object.entries(value)) {
5684
6269
  if (/token|secret|password|authorization|api[_-]?key|credential/i.test(key)) {
5685
6270
  out[key] = "[REDACTED]";
5686
6271
  } else {
5687
- out[key] = redactUnknown3(child);
6272
+ out[key] = redactUnknown4(child);
5688
6273
  }
5689
6274
  }
5690
6275
  return out;
@@ -5750,7 +6335,7 @@ function importMcpConnector(catalog, options) {
5750
6335
  }));
5751
6336
  }
5752
6337
  function connectorFromActions(options, actions) {
5753
- const scopes = unique3([
6338
+ const scopes = unique5([
5754
6339
  ...options.scopes ?? [],
5755
6340
  ...actions.flatMap((action) => action.requiredScopes)
5756
6341
  ]);
@@ -5782,7 +6367,7 @@ function riskFromMcpTool(tool, fallback) {
5782
6367
  }
5783
6368
  function scopesFromOpenApiOperation(operation, fallback) {
5784
6369
  const scopes = (operation.security ?? []).flatMap((entry) => Object.values(entry).flat());
5785
- return unique3(scopes.length > 0 ? scopes : fallback);
6370
+ return unique5(scopes.length > 0 ? scopes : fallback);
5786
6371
  }
5787
6372
  function openApiInputSchema(path, method, operation) {
5788
6373
  return {
@@ -5802,7 +6387,7 @@ function titleFromId(id) {
5802
6387
  function isObject(value) {
5803
6388
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
5804
6389
  }
5805
- function unique3(values) {
6390
+ function unique5(values) {
5806
6391
  return [...new Set(values)];
5807
6392
  }
5808
6393
 
@@ -5853,7 +6438,7 @@ function normalizeGatewayCatalog(entries, options) {
5853
6438
  title,
5854
6439
  category: normalizeCategory(entry.category),
5855
6440
  auth: normalizeAuth(entry.auth),
5856
- scopes: unique4([
6441
+ scopes: unique6([
5857
6442
  ...entry.scopes ?? [],
5858
6443
  ...actions.flatMap((action) => action.requiredScopes)
5859
6444
  ]),
@@ -5883,7 +6468,7 @@ function normalizeActions(actions, fallbackScopes) {
5883
6468
  id,
5884
6469
  title: action.title ?? action.name ?? titleFromId2(id),
5885
6470
  risk: normalizeRisk(action.risk),
5886
- requiredScopes: unique4([
6471
+ requiredScopes: unique6([
5887
6472
  ...action.requiredScopes ?? [],
5888
6473
  ...action.scopes ?? [],
5889
6474
  ...action.requiredScopes?.length || action.scopes?.length ? [] : fallbackScopes
@@ -5897,21 +6482,21 @@ function normalizeActions(actions, fallbackScopes) {
5897
6482
  }).filter((action) => action.id);
5898
6483
  }
5899
6484
  function normalizeTriggers(triggers, fallbackScopes) {
5900
- const normalized = triggers.map((trigger) => {
5901
- 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 ?? "");
5902
6487
  return {
5903
6488
  id,
5904
- title: trigger.title ?? trigger.name ?? titleFromId2(id),
5905
- requiredScopes: unique4([
5906
- ...trigger.requiredScopes ?? [],
5907
- ...trigger.scopes ?? [],
5908
- ...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
5909
6494
  ]),
5910
- dataClass: normalizeDataClass(trigger.dataClass),
5911
- description: trigger.description,
5912
- payloadSchema: trigger.payloadSchema
6495
+ dataClass: normalizeDataClass(trigger2.dataClass),
6496
+ description: trigger2.description,
6497
+ payloadSchema: trigger2.payloadSchema
5913
6498
  };
5914
- }).filter((trigger) => trigger.id);
6499
+ }).filter((trigger2) => trigger2.id);
5915
6500
  return normalized.length > 0 ? normalized : void 0;
5916
6501
  }
5917
6502
  function defaultActionsFor(category, scopes) {
@@ -5991,7 +6576,7 @@ function slug2(value) {
5991
6576
  function titleFromId2(id) {
5992
6577
  return id.split("-").filter(Boolean).map((part) => part.slice(0, 1).toUpperCase() + part.slice(1)).join(" ");
5993
6578
  }
5994
- function unique4(values) {
6579
+ function unique6(values) {
5995
6580
  return [...new Set(values)];
5996
6581
  }
5997
6582
 
@@ -6079,7 +6664,7 @@ var IntegrationRuntime = class {
6079
6664
  const connector = {
6080
6665
  ...entry.connector,
6081
6666
  actions: entry.connector.actions.filter((action) => grant.allowedActions.includes(action.id)),
6082
- triggers: entry.connector.triggers?.filter((trigger) => grant.allowedTriggers.includes(trigger.id)),
6667
+ triggers: entry.connector.triggers?.filter((trigger2) => grant.allowedTriggers.includes(trigger2.id)),
6083
6668
  scopes: entry.connector.scopes.filter((scope) => grant.scopes.includes(scope))
6084
6669
  };
6085
6670
  const capability = await this.hub.issueCapability({
@@ -6173,32 +6758,32 @@ function missing(requirement, status, message, connector, registryEntry2) {
6173
6758
  }
6174
6759
  function requiredActions(requirement, connector) {
6175
6760
  if (requirement.mode === "trigger") return [];
6176
- if (requirement.requiredActions?.length) return unique5(requirement.requiredActions);
6761
+ if (requirement.requiredActions?.length) return unique7(requirement.requiredActions);
6177
6762
  const actions = connector.actions.filter((action) => {
6178
6763
  if (requirement.mode === "read") return action.risk === "read";
6179
6764
  if (requirement.mode === "write") return action.risk !== "read";
6180
6765
  return false;
6181
6766
  });
6182
- return unique5(actions.map((action) => action.id));
6767
+ return unique7(actions.map((action) => action.id));
6183
6768
  }
6184
6769
  function requiredTriggers(requirement, connector) {
6185
- if (requirement.requiredTriggers?.length) return unique5(requirement.requiredTriggers);
6770
+ if (requirement.requiredTriggers?.length) return unique7(requirement.requiredTriggers);
6186
6771
  if (requirement.mode !== "trigger") return [];
6187
- return unique5((connector.triggers ?? []).map((trigger) => trigger.id));
6772
+ return unique7((connector.triggers ?? []).map((trigger2) => trigger2.id));
6188
6773
  }
6189
6774
  function requiredScopes(requirement, connector) {
6190
- if (requirement.requiredScopes?.length) return unique5(requirement.requiredScopes);
6775
+ if (requirement.requiredScopes?.length) return unique7(requirement.requiredScopes);
6191
6776
  const actionIds = new Set(requiredActions(requirement, connector));
6192
6777
  const triggerIds = new Set(requiredTriggers(requirement, connector));
6193
- return unique5([
6778
+ return unique7([
6194
6779
  ...connector.actions.filter((action) => actionIds.has(action.id)).flatMap((action) => action.requiredScopes),
6195
- ...(connector.triggers ?? []).filter((trigger) => triggerIds.has(trigger.id)).flatMap((trigger) => trigger.requiredScopes)
6780
+ ...(connector.triggers ?? []).filter((trigger2) => triggerIds.has(trigger2.id)).flatMap((trigger2) => trigger2.requiredScopes)
6196
6781
  ]);
6197
6782
  }
6198
6783
  function sameActor(a, b) {
6199
6784
  return a.type === b.type && a.id === b.id;
6200
6785
  }
6201
- function unique5(values) {
6786
+ function unique7(values) {
6202
6787
  return [...new Set(values)];
6203
6788
  }
6204
6789
 
@@ -6485,8 +7070,8 @@ var IntegrationHub = class {
6485
7070
  id: `cap_${randomUUID3()}`,
6486
7071
  subject: request.subject,
6487
7072
  connectionId: request.connectionId,
6488
- scopes: unique6(request.scopes),
6489
- allowedActions: unique6(request.allowedActions),
7073
+ scopes: unique8(request.scopes),
7074
+ allowedActions: unique8(request.allowedActions),
6490
7075
  issuedAt: now.toISOString(),
6491
7076
  expiresAt: new Date(now.getTime() + request.ttlMs).toISOString(),
6492
7077
  metadata: request.metadata
@@ -6539,18 +7124,18 @@ var IntegrationHub = class {
6539
7124
  }
6540
7125
  return proceed();
6541
7126
  }
6542
- async subscribeTrigger(connectionId, trigger, targetUrl) {
7127
+ async subscribeTrigger(connectionId, trigger2, targetUrl) {
6543
7128
  const connection = await this.requireConnection(connectionId);
6544
7129
  this.assertConnectionActive(connection);
6545
7130
  const provider = this.requireProvider(connection.providerId);
6546
7131
  const connector = await this.requireConnector(provider, connection.connectorId);
6547
- const spec = connector.triggers?.find((candidate) => candidate.id === trigger);
6548
- 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");
6549
7134
  assertScopes(connection, spec.requiredScopes);
6550
7135
  if (!provider.subscribeTrigger) {
6551
7136
  throw new IntegrationError(`Provider ${provider.id} does not support triggers.`, "auth_not_supported");
6552
7137
  }
6553
- return provider.subscribeTrigger(connection, trigger, targetUrl);
7138
+ return provider.subscribeTrigger(connection, trigger2, targetUrl);
6554
7139
  }
6555
7140
  requireProvider(providerId) {
6556
7141
  const provider = this.providers.get(providerId);
@@ -6635,10 +7220,10 @@ function createMockIntegrationProvider(options = {}) {
6635
7220
  action: request.action,
6636
7221
  output: { echo: request.input ?? null }
6637
7222
  },
6638
- subscribeTrigger: (connection, trigger, targetUrl) => ({
6639
- id: `sub_${connection.id}_${trigger}`,
7223
+ subscribeTrigger: (connection, trigger2, targetUrl) => ({
7224
+ id: `sub_${connection.id}_${trigger2}`,
6640
7225
  connectionId: connection.id,
6641
- trigger,
7226
+ trigger: trigger2,
6642
7227
  targetUrl,
6643
7228
  status: "active",
6644
7229
  createdAt: (/* @__PURE__ */ new Date(0)).toISOString()
@@ -6666,10 +7251,10 @@ function createHttpIntegrationProvider(options) {
6666
7251
  request
6667
7252
  }, options.bearer);
6668
7253
  },
6669
- async subscribeTrigger(connection, trigger, targetUrl) {
7254
+ async subscribeTrigger(connection, trigger2, targetUrl) {
6670
7255
  return postJson(fetcher, `${baseUrl}/triggers/subscribe`, {
6671
7256
  connection,
6672
- trigger,
7257
+ trigger: trigger2,
6673
7258
  targetUrl
6674
7259
  }, options.bearer);
6675
7260
  },
@@ -6732,12 +7317,13 @@ function base64UrlEncode(value) {
6732
7317
  function base64UrlDecode(value) {
6733
7318
  return Buffer.from(value, "base64url").toString("utf8");
6734
7319
  }
6735
- function unique6(values) {
7320
+ function unique8(values) {
6736
7321
  return [...new Set(values)];
6737
7322
  }
6738
7323
  export {
6739
7324
  ACTIVEPIECES_OVERRIDES,
6740
7325
  ApprovalBackedPolicyEngine,
7326
+ CANONICAL_INTEGRATION_ACTIONS,
6741
7327
  CredentialsExpired,
6742
7328
  DEFAULT_INTEGRATION_BRIDGE_ENV,
6743
7329
  DEFAULT_SIGNATURE_TOLERANCE_SECONDS,
@@ -6756,17 +7342,22 @@ export {
6756
7342
  IntegrationError,
6757
7343
  IntegrationHub,
6758
7344
  IntegrationRuntime,
7345
+ IntegrationRuntimeError,
6759
7346
  IntegrationSandboxHost,
6760
7347
  IntegrationWorkflowRuntime,
7348
+ PROVIDER_PASSTHROUGH_ACTION,
6761
7349
  ResourceContention,
6762
7350
  StaticIntegrationPolicyEngine,
7351
+ TangleIntegrationsClient,
6763
7352
  _resetPendingFlowsForTests,
6764
7353
  airtableConnector,
6765
7354
  asanaConnector,
6766
7355
  assertValidConnectorManifest,
7356
+ assertValidIntegrationManifest,
6767
7357
  assertValidIntegrationSpec,
6768
7358
  buildActivepiecesConnectors,
6769
7359
  buildApprovalRequest,
7360
+ buildCanonicalLaunchConnectors,
6770
7361
  buildDefaultIntegrationRegistry,
6771
7362
  buildHealthcheckPlan,
6772
7363
  buildIntegrationBridgeEnvironment,
@@ -6774,6 +7365,8 @@ export {
6774
7365
  buildIntegrationCoverageConnectors,
6775
7366
  buildIntegrationInvocationEnvelope,
6776
7367
  buildIntegrationToolCatalog,
7368
+ calendarExercisePlannerManifest,
7369
+ canonicalActionConnectorId,
6777
7370
  canonicalConnectorId,
6778
7371
  composeIntegrationRegistry,
6779
7372
  consoleStepsToText,
@@ -6791,16 +7384,19 @@ export {
6791
7384
  createIntegrationRuntime,
6792
7385
  createIntegrationWorkflowRuntime,
6793
7386
  createMockIntegrationProvider,
7387
+ createPlatformIntegrationPolicyPreset,
7388
+ createTangleIntegrationsClient,
6794
7389
  declarativeRestConnector,
6795
7390
  decodeIntegrationBridgePayload,
6796
7391
  dispatchIntegrationInvocation,
6797
7392
  encodeIntegrationBridgePayload,
6798
7393
  exchangeAuthorizationCode,
7394
+ explainMissingRequirements,
6799
7395
  firstHeader,
6800
7396
  getActivepiecesOverride,
6801
7397
  getIntegrationFamily,
6802
7398
  getIntegrationSpec,
6803
- githubConnector,
7399
+ githubConnector2 as githubConnector,
6804
7400
  gitlabConnector,
6805
7401
  googleCalendar,
6806
7402
  googleSheets,
@@ -6809,6 +7405,7 @@ export {
6809
7405
  importGraphqlConnector,
6810
7406
  importMcpConnector,
6811
7407
  importOpenApiConnector,
7408
+ inferIntegrationManifestFromTools,
6812
7409
  inferIntegrationSupportTier,
6813
7410
  integrationCoverageChecklistMarkdown,
6814
7411
  integrationSpecToConnector,
@@ -6821,6 +7418,7 @@ export {
6821
7418
  manifestToConnector,
6822
7419
  microsoftCalendar,
6823
7420
  normalizeGatewayCatalog,
7421
+ normalizeIntegrationError,
6824
7422
  normalizeIntegrationResult,
6825
7423
  notionDatabase,
6826
7424
  parseIntegrationBridgeEnvironment,
@@ -6833,6 +7431,8 @@ export {
6833
7431
  redactInvocationEnvelope,
6834
7432
  refreshAccessToken,
6835
7433
  renderAgentToolDescription,
7434
+ renderApprovalCopy,
7435
+ renderConsentSummary,
6836
7436
  renderConsoleSteps,
6837
7437
  renderRunbookMarkdown,
6838
7438
  resolveConnectionCredentials,
@@ -6849,6 +7449,7 @@ export {
6849
7449
  slackEventsConnector,
6850
7450
  specAuthToConnectorAuth,
6851
7451
  startOAuthFlow,
7452
+ statusForCode,
6852
7453
  storedEventToTriggerEvent,
6853
7454
  stripePackConnector,
6854
7455
  stripeWebhookReceiverConnector,
@@ -6859,7 +7460,9 @@ export {
6859
7460
  validateCredentialFormat,
6860
7461
  validateCredentialSet,
6861
7462
  validateIntegrationInvocationEnvelope,
7463
+ validateIntegrationManifest,
6862
7464
  validateIntegrationSpec,
7465
+ validateProviderPassthroughRequest,
6863
7466
  verifyCapabilityToken,
6864
7467
  verifyHmacSignature,
6865
7468
  verifySlackSignature,