@agent-native/core 0.98.5 → 0.98.7

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.
Files changed (135) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +18 -0
  3. package/corpus/core/docs/content/external-agents.mdx +66 -7
  4. package/corpus/core/docs/content/locales/ar-SA/external-agents.mdx +40 -7
  5. package/corpus/core/docs/content/locales/de-DE/external-agents.mdx +41 -7
  6. package/corpus/core/docs/content/locales/es-ES/external-agents.mdx +41 -7
  7. package/corpus/core/docs/content/locales/fr-FR/external-agents.mdx +41 -7
  8. package/corpus/core/docs/content/locales/hi-IN/external-agents.mdx +40 -7
  9. package/corpus/core/docs/content/locales/ja-JP/external-agents.mdx +40 -7
  10. package/corpus/core/docs/content/locales/ko-KR/external-agents.mdx +40 -7
  11. package/corpus/core/docs/content/locales/pt-BR/external-agents.mdx +41 -7
  12. package/corpus/core/docs/content/locales/zh-CN/external-agents.mdx +39 -7
  13. package/corpus/core/docs/content/locales/zh-TW/external-agents.mdx +39 -7
  14. package/corpus/core/package.json +1 -1
  15. package/corpus/core/src/a2a/handlers.ts +101 -26
  16. package/corpus/core/src/a2a/task-store.ts +128 -3
  17. package/corpus/core/src/client/session-replay.ts +67 -5
  18. package/corpus/core/src/integrations/adapters/slack.ts +207 -17
  19. package/corpus/core/src/integrations/identity-links-store.ts +210 -0
  20. package/corpus/core/src/integrations/identity.ts +196 -0
  21. package/corpus/core/src/integrations/index.ts +6 -0
  22. package/corpus/core/src/integrations/plugin.ts +130 -1
  23. package/corpus/core/src/integrations/types.ts +37 -0
  24. package/corpus/core/src/integrations/webhook-handler.ts +1 -0
  25. package/corpus/core/src/mcp/build-server.ts +127 -22
  26. package/corpus/core/src/mcp/builtin-tools.ts +5 -2
  27. package/corpus/core/src/mcp/external-agent-policy.ts +18 -0
  28. package/corpus/core/src/mcp/index.ts +1 -0
  29. package/corpus/core/src/server/agent-chat/plugin-options.ts +12 -1
  30. package/corpus/core/src/server/agent-chat/script-entries.ts +20 -3
  31. package/corpus/core/src/server/agent-chat-plugin.ts +3 -0
  32. package/corpus/core/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +34 -5
  33. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +4 -1
  34. package/corpus/templates/analytics/AGENTS.md +20 -5
  35. package/corpus/templates/analytics/actions/create-session-replay-agent-link.ts +0 -1
  36. package/corpus/templates/analytics/actions/get-session-replay-events.ts +0 -1
  37. package/corpus/templates/analytics/actions/get-session-replay-timeline.ts +40 -0
  38. package/corpus/templates/analytics/actions/list-error-issues.ts +12 -0
  39. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +4 -1
  40. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +48 -17
  41. package/corpus/templates/analytics/changelog/2026-07-12-analytics-uses-the-full-in-app-agent-for-multi-step-incident.md +6 -0
  42. package/corpus/templates/analytics/changelog/2026-07-12-connected-external-agents-can-now-look-up-sessions-error-iss.md +6 -0
  43. package/corpus/templates/analytics/changelog/2026-07-12-session-identities-stay-visible-when-demo-mode-is-off.md +6 -0
  44. package/corpus/templates/analytics/server/handlers/session-replay.ts +15 -9
  45. package/corpus/templates/analytics/server/lib/analytics-connector-catalog.ts +2 -2
  46. package/corpus/templates/analytics/server/lib/error-capture.ts +47 -5
  47. package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +72 -22
  48. package/corpus/templates/analytics/server/lib/session-replay.ts +41 -11
  49. package/corpus/templates/analytics/server/plugins/agent-chat.ts +7 -0
  50. package/corpus/templates/analytics/server/plugins/db.ts +15 -0
  51. package/corpus/templates/analytics/server/routes/api/session-replay/agent-diagnostics.json.get.ts +8 -5
  52. package/corpus/templates/analytics/server/routes/api/session-replay/agent-events.json.get.ts +8 -3
  53. package/corpus/templates/calendar/app/components/calendar/DeleteEventDialog.tsx +12 -2
  54. package/corpus/templates/forms/app/global.css +54 -8
  55. package/corpus/templates/forms/app/pages/AskPage.tsx +35 -13
  56. package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +11 -17
  57. package/corpus/templates/forms/app/pages/ResponsesPage.tsx +8 -5
  58. package/corpus/templates/forms/changelog/2026-07-12-ask-forms-suggestions-now-sit-beneath-the-composer-for-a-tig.md +6 -0
  59. package/corpus/templates/forms/changelog/2026-07-12-builder-response-tables-now-fill-the-view-without-a-redundan.md +6 -0
  60. package/corpus/templates/forms/changelog/2026-07-12-response-tables-now-fill-the-available-pane-with-a-flexible-.md +6 -0
  61. package/corpus/templates/mail/AGENTS.md +3 -1
  62. package/corpus/templates/mail/server/lib/mail-integrations.ts +3 -0
  63. package/dist/a2a/handlers.d.ts.map +1 -1
  64. package/dist/a2a/handlers.js +89 -20
  65. package/dist/a2a/handlers.js.map +1 -1
  66. package/dist/a2a/task-store.d.ts +32 -1
  67. package/dist/a2a/task-store.d.ts.map +1 -1
  68. package/dist/a2a/task-store.js +111 -4
  69. package/dist/a2a/task-store.js.map +1 -1
  70. package/dist/client/session-replay.d.ts.map +1 -1
  71. package/dist/client/session-replay.js +53 -8
  72. package/dist/client/session-replay.js.map +1 -1
  73. package/dist/collab/routes.d.ts +1 -1
  74. package/dist/integrations/adapters/slack.d.ts.map +1 -1
  75. package/dist/integrations/adapters/slack.js +176 -17
  76. package/dist/integrations/adapters/slack.js.map +1 -1
  77. package/dist/integrations/identity-links-store.d.ts +23 -0
  78. package/dist/integrations/identity-links-store.d.ts.map +1 -0
  79. package/dist/integrations/identity-links-store.js +158 -0
  80. package/dist/integrations/identity-links-store.js.map +1 -0
  81. package/dist/integrations/identity.d.ts +28 -0
  82. package/dist/integrations/identity.d.ts.map +1 -0
  83. package/dist/integrations/identity.js +142 -0
  84. package/dist/integrations/identity.js.map +1 -0
  85. package/dist/integrations/index.d.ts +2 -0
  86. package/dist/integrations/index.d.ts.map +1 -1
  87. package/dist/integrations/index.js +2 -0
  88. package/dist/integrations/index.js.map +1 -1
  89. package/dist/integrations/plugin.d.ts.map +1 -1
  90. package/dist/integrations/plugin.js +93 -1
  91. package/dist/integrations/plugin.js.map +1 -1
  92. package/dist/integrations/types.d.ts +31 -0
  93. package/dist/integrations/types.d.ts.map +1 -1
  94. package/dist/integrations/types.js.map +1 -1
  95. package/dist/integrations/webhook-handler.js +1 -0
  96. package/dist/integrations/webhook-handler.js.map +1 -1
  97. package/dist/mcp/build-server.d.ts +9 -2
  98. package/dist/mcp/build-server.d.ts.map +1 -1
  99. package/dist/mcp/build-server.js +97 -20
  100. package/dist/mcp/build-server.js.map +1 -1
  101. package/dist/mcp/builtin-tools.d.ts.map +1 -1
  102. package/dist/mcp/builtin-tools.js +5 -2
  103. package/dist/mcp/builtin-tools.js.map +1 -1
  104. package/dist/mcp/external-agent-policy.d.ts +19 -0
  105. package/dist/mcp/external-agent-policy.d.ts.map +1 -0
  106. package/dist/mcp/external-agent-policy.js +2 -0
  107. package/dist/mcp/external-agent-policy.js.map +1 -0
  108. package/dist/mcp/index.d.ts +1 -0
  109. package/dist/mcp/index.d.ts.map +1 -1
  110. package/dist/mcp/index.js.map +1 -1
  111. package/dist/notifications/routes.d.ts +1 -1
  112. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  113. package/dist/server/agent-chat/plugin-options.d.ts +11 -1
  114. package/dist/server/agent-chat/plugin-options.d.ts.map +1 -1
  115. package/dist/server/agent-chat/plugin-options.js.map +1 -1
  116. package/dist/server/agent-chat/script-entries.d.ts.map +1 -1
  117. package/dist/server/agent-chat/script-entries.js +16 -2
  118. package/dist/server/agent-chat/script-entries.js.map +1 -1
  119. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  120. package/dist/server/agent-chat-plugin.js +3 -0
  121. package/dist/server/agent-chat-plugin.js.map +1 -1
  122. package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +34 -5
  123. package/docs/content/external-agents.mdx +66 -7
  124. package/docs/content/locales/ar-SA/external-agents.mdx +40 -7
  125. package/docs/content/locales/de-DE/external-agents.mdx +41 -7
  126. package/docs/content/locales/es-ES/external-agents.mdx +41 -7
  127. package/docs/content/locales/fr-FR/external-agents.mdx +41 -7
  128. package/docs/content/locales/hi-IN/external-agents.mdx +40 -7
  129. package/docs/content/locales/ja-JP/external-agents.mdx +40 -7
  130. package/docs/content/locales/ko-KR/external-agents.mdx +40 -7
  131. package/docs/content/locales/pt-BR/external-agents.mdx +41 -7
  132. package/docs/content/locales/zh-CN/external-agents.mdx +39 -7
  133. package/docs/content/locales/zh-TW/external-agents.mdx +39 -7
  134. package/package.json +1 -1
  135. package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +34 -5
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Durable links between a verified provider identity and an Agent Native user.
3
+ * Provider credentials and raw provider payloads never belong in this table.
4
+ */
5
+ import { randomUUID } from "node:crypto";
6
+ import { getDbExec, intType, isPostgres, isUniqueViolation, retryOnDdlRace, } from "../db/client.js";
7
+ import { ensureIndexExists, ensureTableExists } from "../db/ddl-guard.js";
8
+ const TABLE = "integration_identity_links";
9
+ let _initPromise;
10
+ function createSql() {
11
+ const integer = intType();
12
+ return `CREATE TABLE IF NOT EXISTS ${TABLE} (
13
+ id TEXT PRIMARY KEY,
14
+ platform TEXT NOT NULL,
15
+ tenant_id TEXT NOT NULL,
16
+ external_user_id TEXT NOT NULL,
17
+ user_email TEXT NOT NULL,
18
+ org_id TEXT NOT NULL,
19
+ created_at ${integer} NOT NULL,
20
+ updated_at ${integer} NOT NULL
21
+ )`;
22
+ }
23
+ const INDEXES = [
24
+ [
25
+ "idx_integration_identity_links_external",
26
+ `CREATE UNIQUE INDEX IF NOT EXISTS idx_integration_identity_links_external
27
+ ON ${TABLE}(platform, tenant_id, external_user_id)`,
28
+ ],
29
+ [
30
+ "idx_integration_identity_links_account",
31
+ `CREATE INDEX IF NOT EXISTS idx_integration_identity_links_account
32
+ ON ${TABLE}(org_id, user_email)`,
33
+ ],
34
+ ];
35
+ async function ensureTable() {
36
+ if (!_initPromise) {
37
+ _initPromise = (async () => {
38
+ const client = getDbExec();
39
+ if (isPostgres()) {
40
+ await ensureTableExists(TABLE, createSql());
41
+ for (const [name, sql] of INDEXES) {
42
+ await ensureIndexExists(name, sql);
43
+ }
44
+ return;
45
+ }
46
+ await retryOnDdlRace(() => client.execute(createSql()));
47
+ for (const [, sql] of INDEXES) {
48
+ await retryOnDdlRace(() => client.execute(sql));
49
+ }
50
+ })().catch((error) => {
51
+ _initPromise = undefined;
52
+ throw error;
53
+ });
54
+ }
55
+ return _initPromise;
56
+ }
57
+ function required(value, name) {
58
+ const normalized = value.trim();
59
+ if (!normalized)
60
+ throw new Error(`${name} is required`);
61
+ if (normalized.length > 512)
62
+ throw new Error(`${name} is too long`);
63
+ return normalized;
64
+ }
65
+ function normalizePlatform(value) {
66
+ return required(value, "platform").toLowerCase();
67
+ }
68
+ function normalizeEmail(value) {
69
+ const normalized = required(value, "userEmail").toLowerCase();
70
+ if (!normalized.includes("@"))
71
+ throw new Error("userEmail must be an email");
72
+ return normalized;
73
+ }
74
+ function rowToLink(row) {
75
+ return {
76
+ id: String(row.id),
77
+ platform: String(row.platform),
78
+ tenantId: String(row.tenant_id),
79
+ externalUserId: String(row.external_user_id),
80
+ userEmail: String(row.user_email),
81
+ orgId: String(row.org_id),
82
+ createdAt: Number(row.created_at ?? 0),
83
+ updatedAt: Number(row.updated_at ?? 0),
84
+ };
85
+ }
86
+ async function selectByExternalIdentity(platform, tenantId, externalUserId) {
87
+ await ensureTable();
88
+ const { rows } = await getDbExec().execute({
89
+ sql: `SELECT * FROM ${TABLE}
90
+ WHERE platform = ? AND tenant_id = ? AND external_user_id = ?
91
+ LIMIT 1`,
92
+ args: [platform, tenantId, externalUserId],
93
+ });
94
+ return rows[0] ? rowToLink(rows[0]) : null;
95
+ }
96
+ function matchesVerifiedLink(row, input) {
97
+ return row.userEmail === input.userEmail && row.orgId === input.orgId;
98
+ }
99
+ /** Persist a verified mapping and fail closed if it changes identity later. */
100
+ export async function upsertVerifiedIntegrationIdentity(input) {
101
+ const platform = normalizePlatform(input.platform);
102
+ const tenantId = required(input.tenantId, "tenantId");
103
+ const externalUserId = required(input.externalUserId, "externalUserId");
104
+ const userEmail = normalizeEmail(input.userEmail);
105
+ const orgId = required(input.orgId, "orgId");
106
+ const existing = await selectByExternalIdentity(platform, tenantId, externalUserId);
107
+ if (existing) {
108
+ if (!matchesVerifiedLink(existing, { userEmail, orgId })) {
109
+ throw new Error("This provider identity is already linked to a different Agent Native account.");
110
+ }
111
+ const updatedAt = Date.now();
112
+ await getDbExec().execute({
113
+ sql: `UPDATE ${TABLE} SET updated_at = ? WHERE id = ?`,
114
+ args: [updatedAt, existing.id],
115
+ });
116
+ return { ...existing, updatedAt };
117
+ }
118
+ const id = randomUUID();
119
+ const now = Date.now();
120
+ try {
121
+ await getDbExec().execute({
122
+ sql: `INSERT INTO ${TABLE} (
123
+ id, platform, tenant_id, external_user_id, user_email, org_id,
124
+ created_at, updated_at
125
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
126
+ args: [
127
+ id,
128
+ platform,
129
+ tenantId,
130
+ externalUserId,
131
+ userEmail,
132
+ orgId,
133
+ now,
134
+ now,
135
+ ],
136
+ });
137
+ }
138
+ catch (error) {
139
+ if (!isUniqueViolation(error))
140
+ throw error;
141
+ const raced = await selectByExternalIdentity(platform, tenantId, externalUserId);
142
+ if (!raced || !matchesVerifiedLink(raced, { userEmail, orgId })) {
143
+ throw new Error("This provider identity is already linked to a different Agent Native account.");
144
+ }
145
+ return raced;
146
+ }
147
+ return {
148
+ id,
149
+ platform,
150
+ tenantId,
151
+ externalUserId,
152
+ userEmail,
153
+ orgId,
154
+ createdAt: now,
155
+ updatedAt: now,
156
+ };
157
+ }
158
+ //# sourceMappingURL=identity-links-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-links-store.js","sourceRoot":"","sources":["../../src/integrations/identity-links-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Durable links between a verified provider identity and an Agent Native user.\n * Provider credentials and raw provider payloads never belong in this table.\n */\n\nimport { randomUUID } from \"node:crypto\";\n\nimport {\n getDbExec,\n intType,\n isPostgres,\n isUniqueViolation,\n retryOnDdlRace,\n} from \"../db/client.js\";\nimport { ensureIndexExists, ensureTableExists } from \"../db/ddl-guard.js\";\n\nconst TABLE = \"integration_identity_links\";\nlet _initPromise: Promise<void> | undefined;\n\nexport interface IntegrationIdentityLink {\n id: string;\n platform: string;\n tenantId: string;\n externalUserId: string;\n userEmail: string;\n orgId: string;\n createdAt: number;\n updatedAt: number;\n}\n\nfunction createSql(): string {\n const integer = intType();\n return `CREATE TABLE IF NOT EXISTS ${TABLE} (\n id TEXT PRIMARY KEY,\n platform TEXT NOT NULL,\n tenant_id TEXT NOT NULL,\n external_user_id TEXT NOT NULL,\n user_email TEXT NOT NULL,\n org_id TEXT NOT NULL,\n created_at ${integer} NOT NULL,\n updated_at ${integer} NOT NULL\n )`;\n}\n\nconst INDEXES = [\n [\n \"idx_integration_identity_links_external\",\n `CREATE UNIQUE INDEX IF NOT EXISTS idx_integration_identity_links_external\n ON ${TABLE}(platform, tenant_id, external_user_id)`,\n ],\n [\n \"idx_integration_identity_links_account\",\n `CREATE INDEX IF NOT EXISTS idx_integration_identity_links_account\n ON ${TABLE}(org_id, user_email)`,\n ],\n] as const;\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n if (isPostgres()) {\n await ensureTableExists(TABLE, createSql());\n for (const [name, sql] of INDEXES) {\n await ensureIndexExists(name, sql);\n }\n return;\n }\n await retryOnDdlRace(() => client.execute(createSql()));\n for (const [, sql] of INDEXES) {\n await retryOnDdlRace(() => client.execute(sql));\n }\n })().catch((error) => {\n _initPromise = undefined;\n throw error;\n });\n }\n return _initPromise;\n}\n\nfunction required(value: string, name: string): string {\n const normalized = value.trim();\n if (!normalized) throw new Error(`${name} is required`);\n if (normalized.length > 512) throw new Error(`${name} is too long`);\n return normalized;\n}\n\nfunction normalizePlatform(value: string): string {\n return required(value, \"platform\").toLowerCase();\n}\n\nfunction normalizeEmail(value: string): string {\n const normalized = required(value, \"userEmail\").toLowerCase();\n if (!normalized.includes(\"@\")) throw new Error(\"userEmail must be an email\");\n return normalized;\n}\n\nfunction rowToLink(row: Record<string, unknown>): IntegrationIdentityLink {\n return {\n id: String(row.id),\n platform: String(row.platform),\n tenantId: String(row.tenant_id),\n externalUserId: String(row.external_user_id),\n userEmail: String(row.user_email),\n orgId: String(row.org_id),\n createdAt: Number(row.created_at ?? 0),\n updatedAt: Number(row.updated_at ?? 0),\n };\n}\n\nasync function selectByExternalIdentity(\n platform: string,\n tenantId: string,\n externalUserId: string,\n): Promise<IntegrationIdentityLink | null> {\n await ensureTable();\n const { rows } = await getDbExec().execute({\n sql: `SELECT * FROM ${TABLE}\n WHERE platform = ? AND tenant_id = ? AND external_user_id = ?\n LIMIT 1`,\n args: [platform, tenantId, externalUserId],\n });\n return rows[0] ? rowToLink(rows[0] as Record<string, unknown>) : null;\n}\n\nfunction matchesVerifiedLink(\n row: IntegrationIdentityLink,\n input: { userEmail: string; orgId: string },\n): boolean {\n return row.userEmail === input.userEmail && row.orgId === input.orgId;\n}\n\n/** Persist a verified mapping and fail closed if it changes identity later. */\nexport async function upsertVerifiedIntegrationIdentity(input: {\n platform: string;\n tenantId: string;\n externalUserId: string;\n userEmail: string;\n orgId: string;\n}): Promise<IntegrationIdentityLink> {\n const platform = normalizePlatform(input.platform);\n const tenantId = required(input.tenantId, \"tenantId\");\n const externalUserId = required(input.externalUserId, \"externalUserId\");\n const userEmail = normalizeEmail(input.userEmail);\n const orgId = required(input.orgId, \"orgId\");\n const existing = await selectByExternalIdentity(\n platform,\n tenantId,\n externalUserId,\n );\n\n if (existing) {\n if (!matchesVerifiedLink(existing, { userEmail, orgId })) {\n throw new Error(\n \"This provider identity is already linked to a different Agent Native account.\",\n );\n }\n const updatedAt = Date.now();\n await getDbExec().execute({\n sql: `UPDATE ${TABLE} SET updated_at = ? WHERE id = ?`,\n args: [updatedAt, existing.id],\n });\n return { ...existing, updatedAt };\n }\n\n const id = randomUUID();\n const now = Date.now();\n try {\n await getDbExec().execute({\n sql: `INSERT INTO ${TABLE} (\n id, platform, tenant_id, external_user_id, user_email, org_id,\n created_at, updated_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,\n args: [\n id,\n platform,\n tenantId,\n externalUserId,\n userEmail,\n orgId,\n now,\n now,\n ],\n });\n } catch (error) {\n if (!isUniqueViolation(error)) throw error;\n const raced = await selectByExternalIdentity(\n platform,\n tenantId,\n externalUserId,\n );\n if (!raced || !matchesVerifiedLink(raced, { userEmail, orgId })) {\n throw new Error(\n \"This provider identity is already linked to a different Agent Native account.\",\n );\n }\n return raced;\n }\n\n return {\n id,\n platform,\n tenantId,\n externalUserId,\n userEmail,\n orgId,\n createdAt: now,\n updatedAt: now,\n };\n}\n"]}
@@ -0,0 +1,28 @@
1
+ import type { IncomingMessage, IntegrationExecutionContext } from "./types.js";
2
+ export type IntegrationIdentityDeclineReason = "unverified" | "guest" | "unlinked-workspace" | "membership-check-failed";
3
+ /**
4
+ * Thrown when the default Slack DM identity ladder declines to run a message.
5
+ * `reason` is a stable machine-readable discriminator (used e.g. to dedupe
6
+ * decline replies); `userFacingMessage` is safe to send back to the sender as
7
+ * a polite reply; `message` stays log-only.
8
+ */
9
+ export declare class IntegrationIdentityDeclinedError extends Error {
10
+ readonly reason: IntegrationIdentityDeclineReason;
11
+ readonly userFacingMessage: string;
12
+ constructor(reason: IntegrationIdentityDeclineReason, message: string, userFacingMessage: string);
13
+ }
14
+ /**
15
+ * Resolve the default integration principal.
16
+ *
17
+ * Slack DMs become a user principal only after the adapter has verified the
18
+ * sender email and the email is already a member of the managed installation's
19
+ * Agent Native organization. Hydrated full workspace members whose email is
20
+ * missing, unverified, or not an organization member run as an anonymous
21
+ * org-scoped service principal instead — the same visibility shared channels
22
+ * get. Hydration failures, guests/external members, and workspaces without a
23
+ * connected organization are declined with a user-facing message. Shared
24
+ * channels deliberately stay service-scoped so a channel message cannot borrow
25
+ * one participant's private permissions.
26
+ */
27
+ export declare function resolveDefaultIntegrationExecutionContext(incoming: IncomingMessage): Promise<IntegrationExecutionContext>;
28
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/integrations/identity.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAE/E,MAAM,MAAM,gCAAgC,GACxC,YAAY,GACZ,OAAO,GACP,oBAAoB,GACpB,yBAAyB,CAAC;AAE9B;;;;;GAKG;AACH,qBAAa,gCAAiC,SAAQ,KAAK;IACzD,QAAQ,CAAC,MAAM,EAAE,gCAAgC,CAAC;IAClD,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IAEnC,YACE,MAAM,EAAE,gCAAgC,EACxC,OAAO,EAAE,MAAM,EACf,iBAAiB,EAAE,MAAM,EAM1B;CACF;AAmDD;;;;;;;;;;;;GAYG;AACH,wBAAsB,yCAAyC,CAC7D,QAAQ,EAAE,eAAe,GACxB,OAAO,CAAC,2BAA2B,CAAC,CA8FtC"}
@@ -0,0 +1,142 @@
1
+ import { getDbExec } from "../db/client.js";
2
+ import { upsertVerifiedIntegrationIdentity } from "./identity-links-store.js";
3
+ import { getActiveIntegrationInstallationByKey, getActiveIntegrationInstallationForTenant, } from "./installations-store.js";
4
+ import { slackInstallationKey } from "./slack-oauth.js";
5
+ /**
6
+ * Thrown when the default Slack DM identity ladder declines to run a message.
7
+ * `reason` is a stable machine-readable discriminator (used e.g. to dedupe
8
+ * decline replies); `userFacingMessage` is safe to send back to the sender as
9
+ * a polite reply; `message` stays log-only.
10
+ */
11
+ export class IntegrationIdentityDeclinedError extends Error {
12
+ reason;
13
+ userFacingMessage;
14
+ constructor(reason, message, userFacingMessage) {
15
+ super(message);
16
+ this.name = "IntegrationIdentityDeclinedError";
17
+ this.reason = reason;
18
+ this.userFacingMessage = userFacingMessage;
19
+ }
20
+ }
21
+ function serviceOwner(platform) {
22
+ return `integration@${platform}`;
23
+ }
24
+ function normalizedEmail(value) {
25
+ if (typeof value !== "string")
26
+ return null;
27
+ const email = value.trim().toLowerCase();
28
+ if (!email || email.length > 320 || !email.includes("@"))
29
+ return null;
30
+ return email;
31
+ }
32
+ async function resolveSlackInstallation(incoming) {
33
+ const teamId = typeof incoming.platformContext.teamId === "string"
34
+ ? incoming.platformContext.teamId
35
+ : incoming.tenantId;
36
+ const enterpriseId = typeof incoming.platformContext.enterpriseId === "string"
37
+ ? incoming.platformContext.enterpriseId
38
+ : undefined;
39
+ const apiAppId = typeof incoming.platformContext.apiAppId === "string"
40
+ ? incoming.platformContext.apiAppId
41
+ : undefined;
42
+ if (apiAppId && (teamId || enterpriseId)) {
43
+ const byKey = await getActiveIntegrationInstallationByKey("slack", slackInstallationKey({ teamId, enterpriseId, apiAppId }));
44
+ if (byKey)
45
+ return byKey;
46
+ }
47
+ return teamId || enterpriseId
48
+ ? getActiveIntegrationInstallationForTenant("slack", teamId ?? enterpriseId)
49
+ : null;
50
+ }
51
+ async function isMemberOfOrg(email, orgId) {
52
+ const { rows } = await getDbExec().execute({
53
+ sql: `SELECT 1 FROM org_members
54
+ WHERE org_id = ? AND LOWER(email) = ?
55
+ LIMIT 1`,
56
+ args: [orgId, email],
57
+ });
58
+ return rows.length > 0;
59
+ }
60
+ /**
61
+ * Resolve the default integration principal.
62
+ *
63
+ * Slack DMs become a user principal only after the adapter has verified the
64
+ * sender email and the email is already a member of the managed installation's
65
+ * Agent Native organization. Hydrated full workspace members whose email is
66
+ * missing, unverified, or not an organization member run as an anonymous
67
+ * org-scoped service principal instead — the same visibility shared channels
68
+ * get. Hydration failures, guests/external members, and workspaces without a
69
+ * connected organization are declined with a user-facing message. Shared
70
+ * channels deliberately stay service-scoped so a channel message cannot borrow
71
+ * one participant's private permissions.
72
+ */
73
+ export async function resolveDefaultIntegrationExecutionContext(incoming) {
74
+ const installation = incoming.platform === "slack"
75
+ ? await resolveSlackInstallation(incoming)
76
+ : null;
77
+ if (incoming.platform !== "slack" || incoming.conversationType !== "dm") {
78
+ return {
79
+ ownerEmail: serviceOwner(incoming.platform),
80
+ orgId: installation?.orgId ?? null,
81
+ principalType: "service",
82
+ ...(installation?.id ? { installationId: installation.id } : {}),
83
+ };
84
+ }
85
+ // Hydration check first: a transient users.info failure must land on the
86
+ // retry decline here, never on the anonymous org-scoped tier below.
87
+ if (incoming.actorTrust?.verified !== true) {
88
+ throw new IntegrationIdentityDeclinedError("unverified", "Slack DM sender identity could not be hydrated; declining instead of guessing a principal.", "I couldn't verify your Slack identity just now, so I can't run this request. Please try again in a moment.");
89
+ }
90
+ if (incoming.actorTrust.memberType === "guest" ||
91
+ incoming.actorTrust.memberType === "external") {
92
+ throw new IntegrationIdentityDeclinedError("guest", "External or guest Slack members cannot use this integration.", "This assistant is only available to members of this workspace's organization.");
93
+ }
94
+ if (!installation?.orgId) {
95
+ throw new IntegrationIdentityDeclinedError("unlinked-workspace", "Slack workspace is not connected to an Agent Native organization.", "This Slack workspace isn't connected to an organization yet.");
96
+ }
97
+ const email = normalizedEmail(incoming.senderEmail);
98
+ let isOrgMember = false;
99
+ if (email &&
100
+ incoming.senderVerified === true &&
101
+ incoming.senderId &&
102
+ incoming.tenantId) {
103
+ try {
104
+ isOrgMember = await isMemberOfOrg(email, installation.orgId);
105
+ }
106
+ catch {
107
+ // A membership-store outage is not evidence that the sender is merely
108
+ // unlinked. Fail closed instead of widening them to org-wide access.
109
+ throw new IntegrationIdentityDeclinedError("membership-check-failed", "Slack DM organization membership could not be verified.", "I couldn't verify your organization membership just now, so I can't run this request. Please try again in a moment.");
110
+ }
111
+ }
112
+ if (isOrgMember && email && incoming.senderId && incoming.tenantId) {
113
+ const link = await upsertVerifiedIntegrationIdentity({
114
+ platform: incoming.platform,
115
+ tenantId: incoming.tenantId,
116
+ externalUserId: incoming.senderId,
117
+ userEmail: email,
118
+ orgId: installation.orgId,
119
+ });
120
+ return {
121
+ ownerEmail: link.userEmail,
122
+ orgId: link.orgId,
123
+ principalType: "user",
124
+ installationId: installation.id,
125
+ };
126
+ }
127
+ // Hydrated full workspace member (member/admin/owner) whose email is
128
+ // unverified, missing (legacy install without users:read.email), or not an
129
+ // organization member: run with the anonymous org-scoped service principal —
130
+ // the same visibility shared channels get. Nothing user-private is
131
+ // accessible. One structured line keeps 100%-anonymous workspaces (legacy
132
+ // scope missing) visible in logs.
133
+ console.warn(`[integrations] anonymous org-scoped principal used: platform=${incoming.platform} teamId=${incoming.tenantId ?? "unknown"} emailPresent=${Boolean(email)} memberType=${incoming.actorTrust.memberType}`);
134
+ return {
135
+ ownerEmail: serviceOwner(incoming.platform),
136
+ orgId: installation.orgId,
137
+ principalType: "service",
138
+ installationId: installation.id,
139
+ anonymousMember: true,
140
+ };
141
+ }
142
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/integrations/identity.ts"],"names":[],"mappings":"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","sourcesContent":["import { getDbExec } from \"../db/client.js\";\nimport { upsertVerifiedIntegrationIdentity } from \"./identity-links-store.js\";\nimport {\n getActiveIntegrationInstallationByKey,\n getActiveIntegrationInstallationForTenant,\n} from \"./installations-store.js\";\nimport { slackInstallationKey } from \"./slack-oauth.js\";\nimport type { IncomingMessage, IntegrationExecutionContext } from \"./types.js\";\n\nexport type IntegrationIdentityDeclineReason =\n | \"unverified\"\n | \"guest\"\n | \"unlinked-workspace\"\n | \"membership-check-failed\";\n\n/**\n * Thrown when the default Slack DM identity ladder declines to run a message.\n * `reason` is a stable machine-readable discriminator (used e.g. to dedupe\n * decline replies); `userFacingMessage` is safe to send back to the sender as\n * a polite reply; `message` stays log-only.\n */\nexport class IntegrationIdentityDeclinedError extends Error {\n readonly reason: IntegrationIdentityDeclineReason;\n readonly userFacingMessage: string;\n\n constructor(\n reason: IntegrationIdentityDeclineReason,\n message: string,\n userFacingMessage: string,\n ) {\n super(message);\n this.name = \"IntegrationIdentityDeclinedError\";\n this.reason = reason;\n this.userFacingMessage = userFacingMessage;\n }\n}\n\nfunction serviceOwner(platform: string): string {\n return `integration@${platform}`;\n}\n\nfunction normalizedEmail(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n const email = value.trim().toLowerCase();\n if (!email || email.length > 320 || !email.includes(\"@\")) return null;\n return email;\n}\n\nasync function resolveSlackInstallation(incoming: IncomingMessage) {\n const teamId =\n typeof incoming.platformContext.teamId === \"string\"\n ? incoming.platformContext.teamId\n : incoming.tenantId;\n const enterpriseId =\n typeof incoming.platformContext.enterpriseId === \"string\"\n ? incoming.platformContext.enterpriseId\n : undefined;\n const apiAppId =\n typeof incoming.platformContext.apiAppId === \"string\"\n ? incoming.platformContext.apiAppId\n : undefined;\n if (apiAppId && (teamId || enterpriseId)) {\n const byKey = await getActiveIntegrationInstallationByKey(\n \"slack\",\n slackInstallationKey({ teamId, enterpriseId, apiAppId }),\n );\n if (byKey) return byKey;\n }\n return teamId || enterpriseId\n ? getActiveIntegrationInstallationForTenant(\n \"slack\",\n teamId ?? enterpriseId!,\n )\n : null;\n}\n\nasync function isMemberOfOrg(email: string, orgId: string): Promise<boolean> {\n const { rows } = await getDbExec().execute({\n sql: `SELECT 1 FROM org_members\n WHERE org_id = ? AND LOWER(email) = ?\n LIMIT 1`,\n args: [orgId, email],\n });\n return rows.length > 0;\n}\n\n/**\n * Resolve the default integration principal.\n *\n * Slack DMs become a user principal only after the adapter has verified the\n * sender email and the email is already a member of the managed installation's\n * Agent Native organization. Hydrated full workspace members whose email is\n * missing, unverified, or not an organization member run as an anonymous\n * org-scoped service principal instead — the same visibility shared channels\n * get. Hydration failures, guests/external members, and workspaces without a\n * connected organization are declined with a user-facing message. Shared\n * channels deliberately stay service-scoped so a channel message cannot borrow\n * one participant's private permissions.\n */\nexport async function resolveDefaultIntegrationExecutionContext(\n incoming: IncomingMessage,\n): Promise<IntegrationExecutionContext> {\n const installation =\n incoming.platform === \"slack\"\n ? await resolveSlackInstallation(incoming)\n : null;\n\n if (incoming.platform !== \"slack\" || incoming.conversationType !== \"dm\") {\n return {\n ownerEmail: serviceOwner(incoming.platform),\n orgId: installation?.orgId ?? null,\n principalType: \"service\",\n ...(installation?.id ? { installationId: installation.id } : {}),\n };\n }\n\n // Hydration check first: a transient users.info failure must land on the\n // retry decline here, never on the anonymous org-scoped tier below.\n if (incoming.actorTrust?.verified !== true) {\n throw new IntegrationIdentityDeclinedError(\n \"unverified\",\n \"Slack DM sender identity could not be hydrated; declining instead of guessing a principal.\",\n \"I couldn't verify your Slack identity just now, so I can't run this request. Please try again in a moment.\",\n );\n }\n if (\n incoming.actorTrust.memberType === \"guest\" ||\n incoming.actorTrust.memberType === \"external\"\n ) {\n throw new IntegrationIdentityDeclinedError(\n \"guest\",\n \"External or guest Slack members cannot use this integration.\",\n \"This assistant is only available to members of this workspace's organization.\",\n );\n }\n if (!installation?.orgId) {\n throw new IntegrationIdentityDeclinedError(\n \"unlinked-workspace\",\n \"Slack workspace is not connected to an Agent Native organization.\",\n \"This Slack workspace isn't connected to an organization yet.\",\n );\n }\n\n const email = normalizedEmail(incoming.senderEmail);\n let isOrgMember = false;\n if (\n email &&\n incoming.senderVerified === true &&\n incoming.senderId &&\n incoming.tenantId\n ) {\n try {\n isOrgMember = await isMemberOfOrg(email, installation.orgId);\n } catch {\n // A membership-store outage is not evidence that the sender is merely\n // unlinked. Fail closed instead of widening them to org-wide access.\n throw new IntegrationIdentityDeclinedError(\n \"membership-check-failed\",\n \"Slack DM organization membership could not be verified.\",\n \"I couldn't verify your organization membership just now, so I can't run this request. Please try again in a moment.\",\n );\n }\n }\n if (isOrgMember && email && incoming.senderId && incoming.tenantId) {\n const link = await upsertVerifiedIntegrationIdentity({\n platform: incoming.platform,\n tenantId: incoming.tenantId,\n externalUserId: incoming.senderId,\n userEmail: email,\n orgId: installation.orgId,\n });\n return {\n ownerEmail: link.userEmail,\n orgId: link.orgId,\n principalType: \"user\",\n installationId: installation.id,\n };\n }\n\n // Hydrated full workspace member (member/admin/owner) whose email is\n // unverified, missing (legacy install without users:read.email), or not an\n // organization member: run with the anonymous org-scoped service principal —\n // the same visibility shared channels get. Nothing user-private is\n // accessible. One structured line keeps 100%-anonymous workspaces (legacy\n // scope missing) visible in logs.\n console.warn(\n `[integrations] anonymous org-scoped principal used: platform=${incoming.platform} teamId=${incoming.tenantId ?? \"unknown\"} emailPresent=${Boolean(email)} memberType=${incoming.actorTrust.memberType}`,\n );\n return {\n ownerEmail: serviceOwner(incoming.platform),\n orgId: installation.orgId,\n principalType: \"service\",\n installationId: installation.id,\n anonymousMember: true,\n };\n}\n"]}
@@ -1,5 +1,7 @@
1
1
  export type { PlatformAdapter, IncomingMessage, OutgoingMessage, IntegrationStatus, IntegrationsPluginOptions, ImmediateWebhookResponse, PlatformAdapterCapabilities, UnsupportedPlatformCapabilityError, IntegrationExecutionContext, IntegrationActorTrust, IntegrationContextMessage, IntegrationFileReference, IntegrationConversationType, IntegrationTriggerKind, PlatformRunProgress, } from "./types.js";
2
2
  export { assertPlatformCapability } from "./types.js";
3
+ export { resolveDefaultIntegrationExecutionContext } from "./identity.js";
4
+ export { upsertVerifiedIntegrationIdentity, type IntegrationIdentityLink, } from "./identity-links-store.js";
3
5
  export { BUILT_IN_INTEGRATION_CATALOG, INTEGRATION_CATEGORIES, getIntegrationCatalogEntry, listBuiltInChannelIntegrations, listIntegrationCatalog, type BuiltInChannelId, type AutomationCapabilities, type ChannelCapabilities, type IntegrationAvailability, type IntegrationCatalogEntry, type IntegrationCategory, type IntegrationCredentialRequirement, type IntegrationIconKey, type IntegrationSupportMaturity, } from "./catalog.js";
4
6
  export { createIntegrationsPlugin, defaultIntegrationsPlugin, enqueueRemoteCommand, } from "./plugin.js";
5
7
  export { getRemoteComputerCapabilities, listRemoteDevicesForOwner, revokeRemoteDeviceForOwner, unregisterRemoteDevice, } from "./remote-devices-store.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/integrations/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/integrations/index.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,6 @@
1
1
  export { assertPlatformCapability } from "./types.js";
2
+ export { resolveDefaultIntegrationExecutionContext } from "./identity.js";
3
+ export { upsertVerifiedIntegrationIdentity, } from "./identity-links-store.js";
2
4
  export { BUILT_IN_INTEGRATION_CATALOG, INTEGRATION_CATEGORIES, getIntegrationCatalogEntry, listBuiltInChannelIntegrations, listIntegrationCatalog, } from "./catalog.js";
3
5
  // Plugin
4
6
  export { createIntegrationsPlugin, defaultIntegrationsPlugin, enqueueRemoteCommand, } from "./plugin.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/integrations/index.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAEtD,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,EACtB,0BAA0B,EAC1B,8BAA8B,EAC9B,sBAAsB,GAUvB,MAAM,cAAc,CAAC;AAEtB,SAAS;AACT,OAAO,EACL,wBAAwB,EACxB,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,6BAA6B,EAC7B,yBAAyB,EACzB,0BAA0B,EAC1B,sBAAsB,GACvB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,kCAAkC,EAClC,yBAAyB,EACzB,iCAAiC,EACjC,wBAAwB,GACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,0BAA0B,EAC1B,6BAA6B,EAC7B,sBAAsB,EACtB,2BAA2B,EAC3B,6BAA6B,GAG9B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EACL,mCAAmC,EACnC,mCAAmC,EACnC,4BAA4B,EAC5B,8BAA8B,EAC9B,wCAAwC,EACxC,4BAA4B,GAC7B,MAAM,wBAAwB,CAAC;AAkBhC,WAAW;AACX,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EACL,mCAAmC,EACnC,4BAA4B,EAC5B,qBAAqB,GACtB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,0BAA0B;AAC1B,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,SAAS;AACT,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,GAEvB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,iCAAiC,EACjC,qCAAqC,EACrC,yCAAyC,EACzC,0BAA0B,EAC1B,4BAA4B,EAC5B,6BAA6B,EAC7B,6BAA6B,EAC7B,6BAA6B,GAC9B,MAAM,0BAA0B,CAAC;AASlC,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACpB,gCAAgC,EAChC,aAAa,GACd,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,sBAAsB,EACtB,uBAAuB,GAExB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,gCAAgC,EAChC,sBAAsB,EACtB,8BAA8B,EAC9B,mBAAmB,EACnB,0BAA0B,EAC1B,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAQ1B,OAAO,EACL,4BAA4B,EAC5B,yBAAyB,EACzB,2BAA2B,EAC3B,oCAAoC,EACpC,6BAA6B,EAC7B,6BAA6B,EAC7B,0BAA0B,EAC1B,4BAA4B,GAC7B,MAAM,yBAAyB,CAAC;AAOjC,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,GAEnB,MAAM,2BAA2B,CAAC","sourcesContent":["// Types\nexport type {\n PlatformAdapter,\n IncomingMessage,\n OutgoingMessage,\n IntegrationStatus,\n IntegrationsPluginOptions,\n ImmediateWebhookResponse,\n PlatformAdapterCapabilities,\n UnsupportedPlatformCapabilityError,\n IntegrationExecutionContext,\n IntegrationActorTrust,\n IntegrationContextMessage,\n IntegrationFileReference,\n IntegrationConversationType,\n IntegrationTriggerKind,\n PlatformRunProgress,\n} from \"./types.js\";\nexport { assertPlatformCapability } from \"./types.js\";\n\nexport {\n BUILT_IN_INTEGRATION_CATALOG,\n INTEGRATION_CATEGORIES,\n getIntegrationCatalogEntry,\n listBuiltInChannelIntegrations,\n listIntegrationCatalog,\n type BuiltInChannelId,\n type AutomationCapabilities,\n type ChannelCapabilities,\n type IntegrationAvailability,\n type IntegrationCatalogEntry,\n type IntegrationCategory,\n type IntegrationCredentialRequirement,\n type IntegrationIconKey,\n type IntegrationSupportMaturity,\n} from \"./catalog.js\";\n\n// Plugin\nexport {\n createIntegrationsPlugin,\n defaultIntegrationsPlugin,\n enqueueRemoteCommand,\n} from \"./plugin.js\";\n\nexport {\n getRemoteComputerCapabilities,\n listRemoteDevicesForOwner,\n revokeRemoteDeviceForOwner,\n unregisterRemoteDevice,\n} from \"./remote-devices-store.js\";\nexport {\n claimNextComputerCommand,\n enqueueComputerCommand,\n listRemoteCommandsForOwner,\n} from \"./remote-commands-store.js\";\nexport {\n assertValidComputerCommandEnvelope,\n computeComputerActionHash,\n computerOperationRequiresApproval,\n ComputerSupervisionError,\n} from \"./computer-supervision.js\";\nexport {\n authorizeComputerOperation,\n createComputerApprovalRequest,\n decideComputerApproval,\n getComputerApprovalForOwner,\n listComputerApprovalsForOwner,\n type ComputerApprovalRecord,\n type ComputerApprovalStatus,\n} from \"./computer-supervision-store.js\";\nexport { insertRemoteLiveViewEvents } from \"./remote-run-events-store.js\";\nexport {\n listRemotePushNotificationsForOwner,\n listRemotePushRegistrationsForOwner,\n queueRemotePushNotifications,\n toPublicRemotePushRegistration,\n unregisterRemotePushRegistrationForOwner,\n upsertRemotePushRegistration,\n} from \"./remote-push-store.js\";\nexport type {\n ComputerApprovalScope,\n ComputerCommandAction,\n ComputerCommandEnvelope,\n ComputerOperationClass,\n PublicRemotePushRegistration,\n PublicRemoteDevice,\n RemoteComputerCapabilities,\n RemoteCommand,\n RemoteDevice,\n RemoteDeviceMetadata,\n RemoteLiveViewEvent,\n RemotePushNotification,\n RemotePushRegistration,\n RemoteRunEvent,\n} from \"./remote-types.js\";\n\n// Adapters\nexport { slackAdapter } from \"./adapters/slack.js\";\nexport { telegramAdapter } from \"./adapters/telegram.js\";\nexport { whatsappAdapter } from \"./adapters/whatsapp.js\";\nexport { discordAdapter } from \"./adapters/discord.js\";\nexport {\n clearMicrosoftTeamsAccessTokenCache,\n getMicrosoftTeamsAccessToken,\n microsoftTeamsAdapter,\n} from \"./adapters/microsoft-teams.js\";\nexport { googleDocsAdapter } from \"./adapters/google-docs.js\";\nexport { emailAdapter } from \"./adapters/email.js\";\n\n// Google Docs integration\nexport {\n startGoogleDocsPoller,\n stopGoogleDocsPoller,\n handlePushNotification,\n} from \"./google-docs-poller.js\";\n\n// Stores\nexport {\n getIntegrationConfig,\n saveIntegrationConfig,\n deleteIntegrationConfig,\n listIntegrationConfigs,\n type IntegrationConfig,\n} from \"./config-store.js\";\n\nexport {\n disconnectIntegrationInstallation,\n getActiveIntegrationInstallationByKey,\n getActiveIntegrationInstallationForTenant,\n getIntegrationInstallation,\n listIntegrationInstallations,\n resolveIntegrationTokenBundle,\n updateIntegrationInstallation,\n upsertIntegrationInstallation,\n} from \"./installations-store.js\";\nexport type {\n InstallationActor,\n IntegrationInstallation,\n IntegrationInstallationHealth,\n IntegrationInstallationStatus,\n IntegrationTokenBundle,\n} from \"./installations-store.js\";\n\nexport {\n assertSlackInstallAccess,\n buildSlackAuthorizeUrl,\n exchangeSlackOAuthCode,\n refreshSlackOAuthToken,\n slackInstallationKey,\n slackOAuthResponseToInstallation,\n testSlackAuth,\n} from \"./slack-oauth.js\";\n\nexport {\n SLACK_AGENT_BOT_EVENTS,\n buildSlackAgentManifest,\n type SlackAgentManifestUrls,\n} from \"./slack-manifest.js\";\n\nexport {\n DEFAULT_INTEGRATION_SCOPE_POLICY,\n deleteIntegrationScope,\n evaluateIntegrationScopePolicy,\n getIntegrationScope,\n integrationScopeSubjectKey,\n listIntegrationScopes,\n saveIntegrationScope,\n} from \"./scope-store.js\";\nexport type {\n IntegrationConversationTrust,\n IntegrationScope,\n IntegrationScopeAccess,\n IntegrationScopePolicy,\n} from \"./scope-store.js\";\n\nexport {\n getIntegrationBudgetSnapshot,\n getIntegrationUsageBudget,\n listIntegrationUsageBudgets,\n listIntegrationBudgetThresholdEvents,\n releaseIntegrationUsageBudget,\n reserveIntegrationUsageBudget,\n saveIntegrationUsageBudget,\n settleIntegrationUsageBudget,\n} from \"./usage-budget-store.js\";\nexport type {\n IntegrationBudgetPeriod,\n IntegrationBudgetSubject,\n IntegrationUsageBudget,\n} from \"./usage-budget-store.js\";\n\nexport {\n getThreadMapping,\n saveThreadMapping,\n deleteThreadMapping,\n listThreadMappings,\n type ThreadMapping,\n} from \"./thread-mapping-store.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/integrations/index.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAEtD,OAAO,EAAE,yCAAyC,EAAE,MAAM,eAAe,CAAC;AAC1E,OAAO,EACL,iCAAiC,GAElC,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,4BAA4B,EAC5B,sBAAsB,EACtB,0BAA0B,EAC1B,8BAA8B,EAC9B,sBAAsB,GAUvB,MAAM,cAAc,CAAC;AAEtB,SAAS;AACT,OAAO,EACL,wBAAwB,EACxB,yBAAyB,EACzB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,6BAA6B,EAC7B,yBAAyB,EACzB,0BAA0B,EAC1B,sBAAsB,GACvB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,kCAAkC,EAClC,yBAAyB,EACzB,iCAAiC,EACjC,wBAAwB,GACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,0BAA0B,EAC1B,6BAA6B,EAC7B,sBAAsB,EACtB,2BAA2B,EAC3B,6BAA6B,GAG9B,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EACL,mCAAmC,EACnC,mCAAmC,EACnC,4BAA4B,EAC5B,8BAA8B,EAC9B,wCAAwC,EACxC,4BAA4B,GAC7B,MAAM,wBAAwB,CAAC;AAkBhC,WAAW;AACX,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EACL,mCAAmC,EACnC,4BAA4B,EAC5B,qBAAqB,GACtB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,0BAA0B;AAC1B,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAEjC,SAAS;AACT,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,GAEvB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,iCAAiC,EACjC,qCAAqC,EACrC,yCAAyC,EACzC,0BAA0B,EAC1B,4BAA4B,EAC5B,6BAA6B,EAC7B,6BAA6B,EAC7B,6BAA6B,GAC9B,MAAM,0BAA0B,CAAC;AASlC,OAAO,EACL,wBAAwB,EACxB,sBAAsB,EACtB,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACpB,gCAAgC,EAChC,aAAa,GACd,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,sBAAsB,EACtB,uBAAuB,GAExB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,gCAAgC,EAChC,sBAAsB,EACtB,8BAA8B,EAC9B,mBAAmB,EACnB,0BAA0B,EAC1B,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAQ1B,OAAO,EACL,4BAA4B,EAC5B,yBAAyB,EACzB,2BAA2B,EAC3B,oCAAoC,EACpC,6BAA6B,EAC7B,6BAA6B,EAC7B,0BAA0B,EAC1B,4BAA4B,GAC7B,MAAM,yBAAyB,CAAC;AAOjC,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,GAEnB,MAAM,2BAA2B,CAAC","sourcesContent":["// Types\nexport type {\n PlatformAdapter,\n IncomingMessage,\n OutgoingMessage,\n IntegrationStatus,\n IntegrationsPluginOptions,\n ImmediateWebhookResponse,\n PlatformAdapterCapabilities,\n UnsupportedPlatformCapabilityError,\n IntegrationExecutionContext,\n IntegrationActorTrust,\n IntegrationContextMessage,\n IntegrationFileReference,\n IntegrationConversationType,\n IntegrationTriggerKind,\n PlatformRunProgress,\n} from \"./types.js\";\nexport { assertPlatformCapability } from \"./types.js\";\n\nexport { resolveDefaultIntegrationExecutionContext } from \"./identity.js\";\nexport {\n upsertVerifiedIntegrationIdentity,\n type IntegrationIdentityLink,\n} from \"./identity-links-store.js\";\n\nexport {\n BUILT_IN_INTEGRATION_CATALOG,\n INTEGRATION_CATEGORIES,\n getIntegrationCatalogEntry,\n listBuiltInChannelIntegrations,\n listIntegrationCatalog,\n type BuiltInChannelId,\n type AutomationCapabilities,\n type ChannelCapabilities,\n type IntegrationAvailability,\n type IntegrationCatalogEntry,\n type IntegrationCategory,\n type IntegrationCredentialRequirement,\n type IntegrationIconKey,\n type IntegrationSupportMaturity,\n} from \"./catalog.js\";\n\n// Plugin\nexport {\n createIntegrationsPlugin,\n defaultIntegrationsPlugin,\n enqueueRemoteCommand,\n} from \"./plugin.js\";\n\nexport {\n getRemoteComputerCapabilities,\n listRemoteDevicesForOwner,\n revokeRemoteDeviceForOwner,\n unregisterRemoteDevice,\n} from \"./remote-devices-store.js\";\nexport {\n claimNextComputerCommand,\n enqueueComputerCommand,\n listRemoteCommandsForOwner,\n} from \"./remote-commands-store.js\";\nexport {\n assertValidComputerCommandEnvelope,\n computeComputerActionHash,\n computerOperationRequiresApproval,\n ComputerSupervisionError,\n} from \"./computer-supervision.js\";\nexport {\n authorizeComputerOperation,\n createComputerApprovalRequest,\n decideComputerApproval,\n getComputerApprovalForOwner,\n listComputerApprovalsForOwner,\n type ComputerApprovalRecord,\n type ComputerApprovalStatus,\n} from \"./computer-supervision-store.js\";\nexport { insertRemoteLiveViewEvents } from \"./remote-run-events-store.js\";\nexport {\n listRemotePushNotificationsForOwner,\n listRemotePushRegistrationsForOwner,\n queueRemotePushNotifications,\n toPublicRemotePushRegistration,\n unregisterRemotePushRegistrationForOwner,\n upsertRemotePushRegistration,\n} from \"./remote-push-store.js\";\nexport type {\n ComputerApprovalScope,\n ComputerCommandAction,\n ComputerCommandEnvelope,\n ComputerOperationClass,\n PublicRemotePushRegistration,\n PublicRemoteDevice,\n RemoteComputerCapabilities,\n RemoteCommand,\n RemoteDevice,\n RemoteDeviceMetadata,\n RemoteLiveViewEvent,\n RemotePushNotification,\n RemotePushRegistration,\n RemoteRunEvent,\n} from \"./remote-types.js\";\n\n// Adapters\nexport { slackAdapter } from \"./adapters/slack.js\";\nexport { telegramAdapter } from \"./adapters/telegram.js\";\nexport { whatsappAdapter } from \"./adapters/whatsapp.js\";\nexport { discordAdapter } from \"./adapters/discord.js\";\nexport {\n clearMicrosoftTeamsAccessTokenCache,\n getMicrosoftTeamsAccessToken,\n microsoftTeamsAdapter,\n} from \"./adapters/microsoft-teams.js\";\nexport { googleDocsAdapter } from \"./adapters/google-docs.js\";\nexport { emailAdapter } from \"./adapters/email.js\";\n\n// Google Docs integration\nexport {\n startGoogleDocsPoller,\n stopGoogleDocsPoller,\n handlePushNotification,\n} from \"./google-docs-poller.js\";\n\n// Stores\nexport {\n getIntegrationConfig,\n saveIntegrationConfig,\n deleteIntegrationConfig,\n listIntegrationConfigs,\n type IntegrationConfig,\n} from \"./config-store.js\";\n\nexport {\n disconnectIntegrationInstallation,\n getActiveIntegrationInstallationByKey,\n getActiveIntegrationInstallationForTenant,\n getIntegrationInstallation,\n listIntegrationInstallations,\n resolveIntegrationTokenBundle,\n updateIntegrationInstallation,\n upsertIntegrationInstallation,\n} from \"./installations-store.js\";\nexport type {\n InstallationActor,\n IntegrationInstallation,\n IntegrationInstallationHealth,\n IntegrationInstallationStatus,\n IntegrationTokenBundle,\n} from \"./installations-store.js\";\n\nexport {\n assertSlackInstallAccess,\n buildSlackAuthorizeUrl,\n exchangeSlackOAuthCode,\n refreshSlackOAuthToken,\n slackInstallationKey,\n slackOAuthResponseToInstallation,\n testSlackAuth,\n} from \"./slack-oauth.js\";\n\nexport {\n SLACK_AGENT_BOT_EVENTS,\n buildSlackAgentManifest,\n type SlackAgentManifestUrls,\n} from \"./slack-manifest.js\";\n\nexport {\n DEFAULT_INTEGRATION_SCOPE_POLICY,\n deleteIntegrationScope,\n evaluateIntegrationScopePolicy,\n getIntegrationScope,\n integrationScopeSubjectKey,\n listIntegrationScopes,\n saveIntegrationScope,\n} from \"./scope-store.js\";\nexport type {\n IntegrationConversationTrust,\n IntegrationScope,\n IntegrationScopeAccess,\n IntegrationScopePolicy,\n} from \"./scope-store.js\";\n\nexport {\n getIntegrationBudgetSnapshot,\n getIntegrationUsageBudget,\n listIntegrationUsageBudgets,\n listIntegrationBudgetThresholdEvents,\n releaseIntegrationUsageBudget,\n reserveIntegrationUsageBudget,\n saveIntegrationUsageBudget,\n settleIntegrationUsageBudget,\n} from \"./usage-budget-store.js\";\nexport type {\n IntegrationBudgetPeriod,\n IntegrationBudgetSubject,\n IntegrationUsageBudget,\n} from \"./usage-budget-store.js\";\n\nexport {\n getThreadMapping,\n saveThreadMapping,\n deleteThreadMapping,\n listThreadMappings,\n type ThreadMapping,\n} from \"./thread-mapping-store.js\";\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/integrations/plugin.ts"],"names":[],"mappings":"AAqIA,OAAO,KAAK,EAEV,yBAAyB,EAG1B,MAAM,YAAY,CAAC;AAOpB,KAAK,cAAc,GAAG,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AA4F9D,KAAK,yBAAyB,GAAG;IAC/B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAUF,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,yBAAyB,GAClC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAsFlC;AA+ND;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,CAAC,EAAE,yBAAyB,GAClC,cAAc,CAq6DhB;AAED;;GAEG;AACH,eAAO,MAAM,yBAAyB,gBAA6B,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/integrations/plugin.ts"],"names":[],"mappings":"AAyIA,OAAO,KAAK,EAEV,yBAAyB,EAG1B,MAAM,YAAY,CAAC;AAOpB,KAAK,cAAc,GAAG,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AA4F9D,KAAK,yBAAyB,GAAG;IAC/B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAeF,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,yBAAyB,GAClC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAsFlC;AA+ND;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,CAAC,EAAE,yBAAyB,GAClC,cAAc,CA6hEhB;AAED;;GAEG;AACH,eAAO,MAAM,yBAAyB,gBAA6B,CAAC"}
@@ -26,6 +26,7 @@ import { ComputerSupervisionError } from "./computer-supervision.js";
26
26
  import { getIntegrationConfig, saveIntegrationConfig } from "./config-store.js";
27
27
  import { claimIntegrationControl } from "./controls-store.js";
28
28
  import { startGoogleDocsPoller, handlePushNotification, } from "./google-docs-poller.js";
29
+ import { IntegrationIdentityDeclinedError, resolveDefaultIntegrationExecutionContext, } from "./identity.js";
29
30
  import { disconnectIntegrationInstallation, listIntegrationInstallations, resolveIntegrationTokenBundle, updateIntegrationInstallation, upsertIntegrationInstallation, } from "./installations-store.js";
30
31
  import { forgetIntegrationMemory, integrationMemoryActions, listIntegrationMemory, rememberForIntegrationScope, } from "./integration-memory.js";
31
32
  import { extractBearerToken, signInternalToken, verifyInternalToken, } from "./internal-token.js";
@@ -123,6 +124,10 @@ Keep responses concise — messaging platforms have character limits and users e
123
124
 
124
125
  If a task requires many steps, summarize what you did rather than streaming every detail.`;
125
126
  const REMOTE_DEVICE_ONLINE_MS = 90_000;
127
+ // One decline reply per sender + decline reason per window: during a Slack
128
+ // API outage every message would otherwise get another identical "try again"
129
+ // reply. Short enough that a persistent condition still reminds the sender.
130
+ const DECLINE_NOTICE_DEDUPE_TTL_MS = 5 * 60 * 1_000;
126
131
  export async function enqueueRemoteCommand(envelope) {
127
132
  const ownerEmail = readString(envelope.ownerEmail);
128
133
  if (!ownerEmail)
@@ -1888,11 +1893,61 @@ export function createIntegrationsPlugin(options) {
1888
1893
  setResponseStatus(event, 404);
1889
1894
  return { error: `Integration ${platform} is not enabled` };
1890
1895
  }
1891
- const incoming = await withCredentialContext(credentialContext, () => adapter.parseIncomingMessage(event));
1896
+ let incoming = await withCredentialContext(credentialContext, () => adapter.parseIncomingMessage(event));
1892
1897
  if (!incoming) {
1893
1898
  setResponseStatus(event, 200);
1894
1899
  return "ok";
1895
1900
  }
1901
+ if (adapter.hydrateIncomingIdentity) {
1902
+ try {
1903
+ incoming = await withCredentialContext(credentialContext, () => adapter.hydrateIncomingIdentity(incoming));
1904
+ }
1905
+ catch (err) {
1906
+ // Identity hydration is best-effort for platforms that have an
1907
+ // app-specific resolver. Slack's default DM resolver below will
1908
+ // still fail closed when the identity is absent or unverified.
1909
+ console.warn(`[integrations] Could not hydrate ${platform} sender identity:`, err instanceof Error ? err.message : err);
1910
+ }
1911
+ }
1912
+ let defaultExecutionContext = null;
1913
+ if (incoming.platform === "slack" &&
1914
+ incoming.conversationType === "dm") {
1915
+ try {
1916
+ defaultExecutionContext = await withCredentialContext(credentialContext, () => resolveDefaultIntegrationExecutionContext(incoming));
1917
+ }
1918
+ catch (err) {
1919
+ // Only an explicit execution-context resolver may override the
1920
+ // default Slack identity ladder. The legacy owner-only resolver
1921
+ // predates org-bound identities and must not turn a rejected DM
1922
+ // into an authenticated owner run.
1923
+ if (!options?.resolveExecutionContext) {
1924
+ const declined = err instanceof IntegrationIdentityDeclinedError ? err : null;
1925
+ if (declined) {
1926
+ console.warn(`[integrations] default Slack DM identity declined message:`, declined.message);
1927
+ // Best-effort polite reply back to the DM, deduped per
1928
+ // sender + reason so an outage doesn't produce an identical
1929
+ // reply for every message. Do not await Slack's API: webhook
1930
+ // acknowledgement has a stricter deadline than notice
1931
+ // delivery and must not be delayed by a slow postMessage.
1932
+ void withCredentialContext(credentialContext, () => adapter.sendSystemNotice
1933
+ ? adapter.sendSystemNotice(incoming, declined.userFacingMessage, {
1934
+ dedupeKey: `decline:${incoming.tenantId ?? "unknown"}:${incoming.senderId ?? "unknown"}:${declined.reason}`,
1935
+ dedupeTtlMs: DECLINE_NOTICE_DEDUPE_TTL_MS,
1936
+ })
1937
+ : Promise.resolve()).catch((noticeErr) => {
1938
+ console.warn(`[integrations] decline notice failed:`, noticeErr instanceof Error
1939
+ ? noticeErr.message
1940
+ : noticeErr);
1941
+ });
1942
+ }
1943
+ else {
1944
+ console.error(`[integrations] default Slack DM identity denied message:`, err);
1945
+ }
1946
+ setResponseStatus(event, 200);
1947
+ return "ok";
1948
+ }
1949
+ }
1950
+ }
1896
1951
  let executionContext = {
1897
1952
  ownerEmail: `integration@${platform}`,
1898
1953
  orgId: null,
@@ -1908,6 +1963,33 @@ export function createIntegrationsPlugin(options) {
1908
1963
  return "ok";
1909
1964
  }
1910
1965
  }
1966
+ else if (defaultExecutionContext) {
1967
+ executionContext = defaultExecutionContext;
1968
+ if (defaultExecutionContext.anonymousMember) {
1969
+ // The anonymous tier must never be silent. (1) The agent run
1970
+ // can tell: the note rides the serialized `incoming` into the
1971
+ // queued task and surfaces via <integration-context>.
1972
+ incoming.identityNote =
1973
+ "Caller is an unlinked Slack workspace member running with organization-wide visibility only; personal or privately-shared data is not accessible. They can get personal access by having an admin add their Slack email to the organization (or by reconnecting Slack with the users:read.email scope).";
1974
+ // (2) The sender gets a one-time heads-up (adapter-throttled per
1975
+ // sender). Fire-and-forget so it never delays or replaces the
1976
+ // normal agent run.
1977
+ if (adapter.sendSystemNotice) {
1978
+ const senderEmail = typeof incoming.senderEmail === "string" &&
1979
+ incoming.senderEmail.trim()
1980
+ ? incoming.senderEmail.trim()
1981
+ : null;
1982
+ const noticeText = senderEmail
1983
+ ? `Heads up: I couldn't match your Slack account to an organization member, so I can only use org-wide data. Ask an admin to add ${senderEmail} to the organization for personal access.`
1984
+ : "Heads up: I couldn't verify your Slack account's email, so I can only use org-wide data. Ask an admin to update the Slack connection with the users:read.email scope for personal access.";
1985
+ void withCredentialContext(credentialContext, () => adapter.sendSystemNotice(incoming, noticeText, {
1986
+ dedupeKey: `anonymous-tier:${incoming.tenantId ?? "unknown"}:${incoming.senderId ?? "unknown"}`,
1987
+ })).catch((noticeErr) => {
1988
+ console.warn(`[integrations] anonymous-tier notice failed:`, noticeErr instanceof Error ? noticeErr.message : noticeErr);
1989
+ });
1990
+ }
1991
+ }
1992
+ }
1911
1993
  else if (options?.resolveOwner) {
1912
1994
  try {
1913
1995
  executionContext.ownerEmail = await withCredentialContext(credentialContext, () => Promise.resolve(options.resolveOwner(incoming)));
@@ -1916,6 +1998,16 @@ export function createIntegrationsPlugin(options) {
1916
1998
  console.error(`[integrations] resolveOwner failed, using default:`, err);
1917
1999
  }
1918
2000
  }
2001
+ else {
2002
+ try {
2003
+ executionContext = await withCredentialContext(credentialContext, () => resolveDefaultIntegrationExecutionContext(incoming));
2004
+ }
2005
+ catch (err) {
2006
+ console.error(`[integrations] default execution identity denied message:`, err);
2007
+ setResponseStatus(event, 200);
2008
+ return "ok";
2009
+ }
2010
+ }
1919
2011
  if (executionContext.scopeId) {
1920
2012
  incoming.integrationScopeId = executionContext.scopeId;
1921
2013
  }