@open-everywhere/provider-slack 0.1.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.
Files changed (151) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +21 -0
  3. package/README.md +47 -0
  4. package/agent/access/slack-attachment-fetch.ts +168 -0
  5. package/agent/app-manifest.ts +149 -0
  6. package/agent/channels/slack-approval-card.ts +275 -0
  7. package/agent/channels/slack-attachments.ts +195 -0
  8. package/agent/channels/slack-commands.ts +227 -0
  9. package/agent/channels/slack-context.ts +185 -0
  10. package/agent/channels/slack-events.ts +5406 -0
  11. package/agent/channels/slack-front.ts +2349 -0
  12. package/agent/channels/slack-identity.ts +520 -0
  13. package/agent/channels/slack-input-response.ts +162 -0
  14. package/agent/channels/slack-mrkdwn.ts +61 -0
  15. package/agent/channels/slack-origin-codec.ts +62 -0
  16. package/agent/channels/slack-post-arm.ts +314 -0
  17. package/agent/channels/slack-read.ts +248 -0
  18. package/agent/channels/slack-route-hold.ts +107 -0
  19. package/agent/channels/slack-shortcut.ts +336 -0
  20. package/agent/channels/slack-signature.ts +53 -0
  21. package/agent/channels/slack-spend-gate.ts +62 -0
  22. package/agent/channels/slack-tenant-fence.ts +99 -0
  23. package/agent/channels/slack-wake-backscroll.ts +604 -0
  24. package/agent/channels/slack-web-api.ts +178 -0
  25. package/agent/channels/slack-webhook-verifier.ts +116 -0
  26. package/agent/channels/slack.ts +1266 -0
  27. package/agent/contributions.ts +32 -0
  28. package/agent/docs-generated.ts +15 -0
  29. package/agent/ops.ts +50 -0
  30. package/agent/provider-events.ts +91 -0
  31. package/agent/register.ts +144 -0
  32. package/agent/surface-parity.ts +99 -0
  33. package/dist/agent/access/slack-attachment-fetch.d.ts +15 -0
  34. package/dist/agent/access/slack-attachment-fetch.d.ts.map +1 -0
  35. package/dist/agent/access/slack-attachment-fetch.js +151 -0
  36. package/dist/agent/access/slack-attachment-fetch.js.map +1 -0
  37. package/dist/agent/app-manifest.d.ts +15 -0
  38. package/dist/agent/app-manifest.d.ts.map +1 -0
  39. package/dist/agent/app-manifest.js +141 -0
  40. package/dist/agent/app-manifest.js.map +1 -0
  41. package/dist/agent/channels/slack-approval-card.d.ts +71 -0
  42. package/dist/agent/channels/slack-approval-card.d.ts.map +1 -0
  43. package/dist/agent/channels/slack-approval-card.js +232 -0
  44. package/dist/agent/channels/slack-approval-card.js.map +1 -0
  45. package/dist/agent/channels/slack-attachments.d.ts +66 -0
  46. package/dist/agent/channels/slack-attachments.d.ts.map +1 -0
  47. package/dist/agent/channels/slack-attachments.js +174 -0
  48. package/dist/agent/channels/slack-attachments.js.map +1 -0
  49. package/dist/agent/channels/slack-commands.d.ts +27 -0
  50. package/dist/agent/channels/slack-commands.d.ts.map +1 -0
  51. package/dist/agent/channels/slack-commands.js +208 -0
  52. package/dist/agent/channels/slack-commands.js.map +1 -0
  53. package/dist/agent/channels/slack-context.d.ts +26 -0
  54. package/dist/agent/channels/slack-context.d.ts.map +1 -0
  55. package/dist/agent/channels/slack-context.js +178 -0
  56. package/dist/agent/channels/slack-context.js.map +1 -0
  57. package/dist/agent/channels/slack-events.d.ts +1654 -0
  58. package/dist/agent/channels/slack-events.d.ts.map +1 -0
  59. package/dist/agent/channels/slack-events.js +4240 -0
  60. package/dist/agent/channels/slack-events.js.map +1 -0
  61. package/dist/agent/channels/slack-front.d.ts +387 -0
  62. package/dist/agent/channels/slack-front.d.ts.map +1 -0
  63. package/dist/agent/channels/slack-front.js +1958 -0
  64. package/dist/agent/channels/slack-front.js.map +1 -0
  65. package/dist/agent/channels/slack-identity.d.ts +245 -0
  66. package/dist/agent/channels/slack-identity.d.ts.map +1 -0
  67. package/dist/agent/channels/slack-identity.js +405 -0
  68. package/dist/agent/channels/slack-identity.js.map +1 -0
  69. package/dist/agent/channels/slack-input-response.d.ts +26 -0
  70. package/dist/agent/channels/slack-input-response.d.ts.map +1 -0
  71. package/dist/agent/channels/slack-input-response.js +127 -0
  72. package/dist/agent/channels/slack-input-response.js.map +1 -0
  73. package/dist/agent/channels/slack-mrkdwn.d.ts +3 -0
  74. package/dist/agent/channels/slack-mrkdwn.d.ts.map +1 -0
  75. package/dist/agent/channels/slack-mrkdwn.js +53 -0
  76. package/dist/agent/channels/slack-mrkdwn.js.map +1 -0
  77. package/dist/agent/channels/slack-origin-codec.d.ts +7 -0
  78. package/dist/agent/channels/slack-origin-codec.d.ts.map +1 -0
  79. package/dist/agent/channels/slack-origin-codec.js +55 -0
  80. package/dist/agent/channels/slack-origin-codec.js.map +1 -0
  81. package/dist/agent/channels/slack-post-arm.d.ts +54 -0
  82. package/dist/agent/channels/slack-post-arm.d.ts.map +1 -0
  83. package/dist/agent/channels/slack-post-arm.js +268 -0
  84. package/dist/agent/channels/slack-post-arm.js.map +1 -0
  85. package/dist/agent/channels/slack-read.d.ts +84 -0
  86. package/dist/agent/channels/slack-read.d.ts.map +1 -0
  87. package/dist/agent/channels/slack-read.js +205 -0
  88. package/dist/agent/channels/slack-read.js.map +1 -0
  89. package/dist/agent/channels/slack-route-hold.d.ts +34 -0
  90. package/dist/agent/channels/slack-route-hold.d.ts.map +1 -0
  91. package/dist/agent/channels/slack-route-hold.js +97 -0
  92. package/dist/agent/channels/slack-route-hold.js.map +1 -0
  93. package/dist/agent/channels/slack-shortcut.d.ts +162 -0
  94. package/dist/agent/channels/slack-shortcut.d.ts.map +1 -0
  95. package/dist/agent/channels/slack-shortcut.js +262 -0
  96. package/dist/agent/channels/slack-shortcut.js.map +1 -0
  97. package/dist/agent/channels/slack-signature.d.ts +19 -0
  98. package/dist/agent/channels/slack-signature.d.ts.map +1 -0
  99. package/dist/agent/channels/slack-signature.js +50 -0
  100. package/dist/agent/channels/slack-signature.js.map +1 -0
  101. package/dist/agent/channels/slack-spend-gate.d.ts +22 -0
  102. package/dist/agent/channels/slack-spend-gate.d.ts.map +1 -0
  103. package/dist/agent/channels/slack-spend-gate.js +48 -0
  104. package/dist/agent/channels/slack-spend-gate.js.map +1 -0
  105. package/dist/agent/channels/slack-tenant-fence.d.ts +61 -0
  106. package/dist/agent/channels/slack-tenant-fence.d.ts.map +1 -0
  107. package/dist/agent/channels/slack-tenant-fence.js +88 -0
  108. package/dist/agent/channels/slack-tenant-fence.js.map +1 -0
  109. package/dist/agent/channels/slack-wake-backscroll.d.ts +188 -0
  110. package/dist/agent/channels/slack-wake-backscroll.d.ts.map +1 -0
  111. package/dist/agent/channels/slack-wake-backscroll.js +473 -0
  112. package/dist/agent/channels/slack-wake-backscroll.js.map +1 -0
  113. package/dist/agent/channels/slack-web-api.d.ts +46 -0
  114. package/dist/agent/channels/slack-web-api.d.ts.map +1 -0
  115. package/dist/agent/channels/slack-web-api.js +147 -0
  116. package/dist/agent/channels/slack-web-api.js.map +1 -0
  117. package/dist/agent/channels/slack-webhook-verifier.d.ts +5 -0
  118. package/dist/agent/channels/slack-webhook-verifier.d.ts.map +1 -0
  119. package/dist/agent/channels/slack-webhook-verifier.js +112 -0
  120. package/dist/agent/channels/slack-webhook-verifier.js.map +1 -0
  121. package/dist/agent/channels/slack.d.ts +37 -0
  122. package/dist/agent/channels/slack.d.ts.map +1 -0
  123. package/dist/agent/channels/slack.js +1169 -0
  124. package/dist/agent/channels/slack.js.map +1 -0
  125. package/dist/agent/contributions.d.ts +17 -0
  126. package/dist/agent/contributions.d.ts.map +1 -0
  127. package/dist/agent/contributions.js +32 -0
  128. package/dist/agent/contributions.js.map +1 -0
  129. package/dist/agent/docs-generated.d.ts +3 -0
  130. package/dist/agent/docs-generated.d.ts.map +1 -0
  131. package/dist/agent/docs-generated.js +13 -0
  132. package/dist/agent/docs-generated.js.map +1 -0
  133. package/dist/agent/ops.d.ts +3 -0
  134. package/dist/agent/ops.d.ts.map +1 -0
  135. package/dist/agent/ops.js +38 -0
  136. package/dist/agent/ops.js.map +1 -0
  137. package/dist/agent/provider-events.d.ts +3 -0
  138. package/dist/agent/provider-events.d.ts.map +1 -0
  139. package/dist/agent/provider-events.js +84 -0
  140. package/dist/agent/provider-events.js.map +1 -0
  141. package/dist/agent/register.d.ts +5 -0
  142. package/dist/agent/register.d.ts.map +1 -0
  143. package/dist/agent/register.js +118 -0
  144. package/dist/agent/register.js.map +1 -0
  145. package/dist/agent/surface-parity.d.ts +3 -0
  146. package/dist/agent/surface-parity.d.ts.map +1 -0
  147. package/dist/agent/surface-parity.js +95 -0
  148. package/dist/agent/surface-parity.js.map +1 -0
  149. package/package.json +209 -0
  150. package/tsconfig.build.json +22 -0
  151. package/tsconfig.json +15 -0
@@ -0,0 +1,151 @@
1
+ // Trusted-provenance Slack attachment fetch — the ONE seam that attaches the Slack BOT TOKEN to
2
+ // a file download, and ONLY ever sends it to a validated Slack file host.
3
+ //
4
+ // WHY THIS EXISTS, DISTINCT FROM oeFetch (access/host-egress.ts). `oeFetch` is the guard for a
5
+ // MODEL-SUPPLIED URL: it sends NO credentials, precisely because a model-chosen destination must
6
+ // never receive one (P0-1, the EXFIL leg). But a Slack image sits behind `url_private`, which
7
+ // 302/403s without the bot token — so pasted Slack screenshots (the main describe_image use case)
8
+ // are unfetchable credential-less. This helper closes that gap under two hard constraints that
9
+ // oeFetch's blanket "send nothing" rule made moot there:
10
+ //
11
+ // 1. TRUSTED PROVENANCE — the URL is NOT a tool argument. It arrives as an already-resolved
12
+ // record whose `urlPrivate` came from a Slack event's `files[].url_private`
13
+ // (channels/slack-attachments.ts, populated only by parseMessageEvent). describe_image
14
+ // resolves a model-named FILE ID through that registry and passes the resulting record here.
15
+ // A model-chosen URL string can never reach this function — the signature takes a record, not
16
+ // a raw URL, and describe_image only ever builds that record from the registry.
17
+ //
18
+ // 2. HOST-PINNED CREDENTIAL — the bot token rides ONLY to a Slack file host (files.slack.com).
19
+ // Any other host is refused and the token is NEVER sent (nor is the host even resolved). So
20
+ // even if provenance were somehow subverted, the credential cannot leak to an attacker host.
21
+ //
22
+ // Plus the SAME SSRF posture as oeFetch: resolve-then-check-then-PIN (reusing
23
+ // `assertSafeFetchUrl`) so even files.slack.com gets the DNS-rebind check — a poisoned resolver
24
+ // pointing files.slack.com at an internal IP is refused — and `redirect:"error"` so a 3xx can't
25
+ // bounce the token to another host.
26
+ //
27
+ // ONE TIER-DEPENDENT CAVEAT (OPE-322). `assertSafeFetchUrl` now routes through the forward proxy
28
+ // when `*_PROXY` is set (the self-host/docker stack), because the app container has no WAN route at
29
+ // all — without it this download simply fails there. On that path the IP-pin cannot operate (the
30
+ // proxy resolves the CONNECT target itself) and anti-rebind is delegated to the proxy's default-deny
31
+ // ACL; the returned `antiRebind` says which held, and this seam records it on the allow row. The
32
+ // HOST-PIN above — the property that actually protects the token — is unaffected, and it is decided
33
+ // before any dispatch. The token does traverse the operator's own proxy: Tier A tunnels it
34
+ // undecrypted (CONNECT), Tier B decrypts with the operator's CA, which is what Tier B is for.
35
+ import { assertSafeFetchUrl, auditToolAction, hostCallAuditFields, } from "@open-everywhere/core/agent/adapter-api.js";
36
+ // THE CREDENTIAL'S IDENTITY, FOR THE AUDIT ROW (OPE-1547) — the ref NAME, never the value. This is
37
+ // the one host-side seam that attaches a credential, so before this ticket it was the one place
38
+ // where a plane both saw the call and knew whose credential rode it, and said neither on the row.
39
+ // The shared cross-plane row shape (core access/egress.ts) is what it fills now.
40
+ const SLACK_BOT_TOKEN_REF = "SLACK_BOT_TOKEN";
41
+ /** Every attachment download is a GET — Slack's `url_private` is a file read. */
42
+ const ATTACHMENT_VERB = "GET";
43
+ // Slack serves `url_private` downloads from `files.slack.com` (Enterprise Grid included — same
44
+ // host). The bot token is sent to NO other host. If Slack ever documents a second legitimate file
45
+ // host, add it here — the pin is a closed set on purpose (a `*.slack.com` wildcard would let the
46
+ // API host `slack.com` receive the token, which it must not).
47
+ const SLACK_FILE_HOSTS = new Set(["files.slack.com"]);
48
+ /** Is `hostname` a Slack file host the bot token may be sent to? Exact-match, case-insensitive. */
49
+ function isSlackFileHost(hostname) {
50
+ return SLACK_FILE_HOSTS.has(hostname.toLowerCase());
51
+ }
52
+ /**
53
+ * Download a Slack attachment with the bot token, host-pinned to a Slack file host and SSRF-guarded.
54
+ * Refuses (and never sends the token) if the URL is not HTTPS on a Slack file host. Returns the raw
55
+ * Response — the caller (describe_image) reads the bytes.
56
+ */
57
+ export async function fetchSlackAttachment(ctx, ref) {
58
+ let url;
59
+ try {
60
+ url = new URL(ref.urlPrivate);
61
+ }
62
+ catch {
63
+ throw new Error("describe_image: the Slack attachment URL is not a valid URL.");
64
+ }
65
+ // HTTPS-only: a bearer token must never ride cleartext.
66
+ if (url.protocol !== "https:") {
67
+ throw new Error(`describe_image: refusing to send the Slack bot token over a non-HTTPS URL ("${url.protocol}").`);
68
+ }
69
+ // HOST-PIN (before any DNS): the token may ONLY go to a Slack file host. A non-Slack host is
70
+ // refused HERE — the token is never sent and the host isn't even resolved.
71
+ if (!isSlackFileHost(url.hostname)) {
72
+ await auditToolAction(ctx, "slack.attachment.deny", {
73
+ // `credential: null` is the load-bearing claim on every deny here: the host-pin is decided
74
+ // BEFORE the token is read, so a refused download never had one attached.
75
+ ...hostCallAuditFields({ host: url.hostname, verb: ATTACHMENT_VERB, outcome: "denied" }),
76
+ reason: "not-slack-file-host",
77
+ id: ref.id,
78
+ });
79
+ throw new Error(`describe_image: refusing to send the Slack bot token to non-Slack host "${url.hostname}".`);
80
+ }
81
+ // SSRF resolve-then-check-then-PIN — even Slack's own host gets the rebind check, and the
82
+ // connection is pinned to the validated public IP so it can't be re-resolved between check and
83
+ // connect. `redirect:"error"` (below) stops a 3xx bouncing the token to another host.
84
+ let safe;
85
+ try {
86
+ safe = await assertSafeFetchUrl(ref.urlPrivate);
87
+ }
88
+ catch (err) {
89
+ const resolveFailed = err?.oeEgressCause === "resolve-failed";
90
+ await auditToolAction(ctx, "slack.attachment.deny", {
91
+ // `denied` rather than `failed` — the SSRF guard refuses on POLICY before any dispatch. A
92
+ // resolver that could not ANSWER is a different event and core tags it, so this seam reports
93
+ // it as `failed` under its own reason rather than as an SSRF refusal.
94
+ ...hostCallAuditFields({ host: url.hostname, verb: ATTACHMENT_VERB, outcome: resolveFailed ? "failed" : "denied" }),
95
+ reason: resolveFailed ? "resolve-failed" : "ssrf",
96
+ id: ref.id,
97
+ });
98
+ throw err;
99
+ }
100
+ // Defence-in-depth: assertSafeFetchUrl preserves the hostname, but re-assert it so the token can
101
+ // NEVER ride a host that isn't a Slack file host, whatever the parse produced.
102
+ if (!isSlackFileHost(safe.url.hostname)) {
103
+ await auditToolAction(ctx, "slack.attachment.deny", {
104
+ ...hostCallAuditFields({ host: safe.url.hostname, verb: ATTACHMENT_VERB, outcome: "denied" }),
105
+ reason: "not-slack-file-host",
106
+ id: ref.id,
107
+ });
108
+ throw new Error(`describe_image: refusing to send the Slack bot token to non-Slack host "${safe.url.hostname}".`);
109
+ }
110
+ const token = process.env.SLACK_BOT_TOKEN;
111
+ if (!token) {
112
+ throw new Error("describe_image: SLACK_BOT_TOKEN is not set — the bot token is required to download a Slack attachment.");
113
+ }
114
+ // ONE row per call, carrying the OUTCOME — so it is written after the dispatch settles, not
115
+ // before it starts (the same trade core's `oeFetch` states: the refusal paths above still record
116
+ // before anything leaves, and what is lost is a process death mid-flight).
117
+ //
118
+ // 🔴 `credential` NAMES THE REF, NEVER THE TOKEN. `token` is read one line above and is not
119
+ // referenced here; the row carries the ref name only, which is the identity an operator looks up.
120
+ // `route` is the dispatch route — it was called `via` until OPE-1547, the same field name the
121
+ // SANDBOX plane used for the stamping connection.
122
+ const row = (outcome, extra = {}) => ({
123
+ ...hostCallAuditFields({
124
+ host: safe.url.hostname,
125
+ verb: ATTACHMENT_VERB,
126
+ outcome,
127
+ credential: SLACK_BOT_TOKEN_REF,
128
+ }),
129
+ id: ref.id,
130
+ route: safe.via,
131
+ antiRebind: safe.antiRebind,
132
+ ...extra,
133
+ });
134
+ let response;
135
+ try {
136
+ // Bot token ONLY to the pinned Slack file host; NO redirect follow (a 3xx must not bounce the
137
+ // token elsewhere); connection pinned to the validated IP.
138
+ response = await fetch(safe.url, {
139
+ dispatcher: safe.dispatcher,
140
+ redirect: "error",
141
+ headers: { authorization: `Bearer ${token}` },
142
+ });
143
+ }
144
+ catch (err) {
145
+ await auditToolAction(ctx, "slack.attachment.allow", row("failed"));
146
+ throw err;
147
+ }
148
+ await auditToolAction(ctx, "slack.attachment.allow", row("allowed", { status: response.status }));
149
+ return response;
150
+ }
151
+ //# sourceMappingURL=slack-attachment-fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack-attachment-fetch.js","sourceRoot":"","sources":["../../../agent/access/slack-attachment-fetch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /** The token the request URLs carry where the deployment's own host belongs. The ops plane
2
+ * substitutes it (packages/core/agent/ops/surface-manifest.ts); the wizard leaves it for the
3
+ * operator, who has usually not deployed yet when they run it. */
4
+ export declare const SLACK_DEPLOY_HOST_PLACEHOLDER = "YOUR-DEPLOY-URL";
5
+ /** A ready-to-paste Slack app manifest (Slack's "create from manifest" flow) with the minimal
6
+ * scope/event set docs/deploy/surfaces/slack.md walks through by hand. */
7
+ export declare function slackAppManifestTemplate(): string;
8
+ /** Where the operator pastes the manifest. */
9
+ export declare const SLACK_APP_CONSOLE_URL = "https://api.slack.com/apps";
10
+ /** The ordered clicks. Slack reserves every one of them for a workspace admin. */
11
+ export declare const SLACK_APP_MANIFEST_STEPS: readonly string[];
12
+ /** What the operator brings back. Both are environment variables, not credential-store secrets:
13
+ * the bundle reads them from the environment (agent/ops.ts). */
14
+ export declare const SLACK_APP_MANIFEST_COLLECT: readonly string[];
15
+ //# sourceMappingURL=app-manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-manifest.d.ts","sourceRoot":"","sources":["../../agent/app-manifest.ts"],"names":[],"mappings":"AAaA;;mEAEmE;AACnE,eAAO,MAAM,6BAA6B,oBAAoB,CAAC;AAE/D;2EAC2E;AAC3E,wBAAgB,wBAAwB,IAAI,MAAM,CA6GjD;AAED,8CAA8C;AAC9C,eAAO,MAAM,qBAAqB,+BAA+B,CAAC;AAElE,kFAAkF;AAClF,eAAO,MAAM,wBAAwB,EAAE,SAAS,MAAM,EAMrD,CAAC;AAEF;iEACiE;AACjE,eAAO,MAAM,0BAA0B,EAAE,SAAS,MAAM,EAGvD,CAAC"}
@@ -0,0 +1,141 @@
1
+ // THE Slack app manifest — one definition, three readers (OPE-1603).
2
+ //
3
+ // This file is the only place the manifest exists. The install wizard's Slack step
4
+ // (packages/create/src/steps/slack.ts) prints it, the ops MCP tool `surface_manifest` serves it
5
+ // with this deployment's host substituted, and `scripts/slack-scope-audit-test.ts` parses the bot
6
+ // scope array below against the runbook's documented scopes. It used to live in the wizard, where
7
+ // the ops server could not reach it at all.
8
+ //
9
+ // 🔴 THIS MODULE IMPORTS NOTHING, DELIBERATELY. The install wizard depends on the bundle for this
10
+ // document alone, so a leaf module costs it no transitive weight — not the adapter-api barrel, not
11
+ // eve, not the Slack Web API client. `agent/ops.ts` does the typed registration; this file holds
12
+ // only the vendor's own knowledge.
13
+ /** The token the request URLs carry where the deployment's own host belongs. The ops plane
14
+ * substitutes it (packages/core/agent/ops/surface-manifest.ts); the wizard leaves it for the
15
+ * operator, who has usually not deployed yet when they run it. */
16
+ export const SLACK_DEPLOY_HOST_PLACEHOLDER = "YOUR-DEPLOY-URL";
17
+ /** A ready-to-paste Slack app manifest (Slack's "create from manifest" flow) with the minimal
18
+ * scope/event set docs/deploy/surfaces/slack.md walks through by hand. */
19
+ export function slackAppManifestTemplate() {
20
+ return JSON.stringify({
21
+ display_information: { name: "Open Everywhere" },
22
+ features: {
23
+ bot_user: { display_name: "Open Everywhere", always_online: true },
24
+ // The "/oe" ZERO-INFERENCE control command (agent/commands/core.ts). Declared here rather
25
+ // than left to a manual setup step because one of its subcommands is `/oe stop` (OPE-418),
26
+ // and stop is not a convenience: it is the affordance a person reaches for when the agent
27
+ // is doing the wrong thing. An install where stop has to be wired by hand is an install
28
+ // where stop is missing. NOTE the request URL is the SLASH-COMMAND endpoint, which Slack
29
+ // configures SEPARATELY from the Events request URL above.
30
+ slash_commands: [
31
+ {
32
+ command: "/oe",
33
+ url: `https://${SLACK_DEPLOY_HOST_PLACEHOLDER}/eve/v1/slack/commands`,
34
+ description: "Open Everywhere control commands — status, budget, stop.",
35
+ usage_hint: "stop | status | budget",
36
+ should_escape: false,
37
+ },
38
+ ],
39
+ // SHORTCUTS AS A WAKE (OPE-1376). Both kinds are declared, because they answer two
40
+ // different needs and neither substitutes for the other: the MESSAGE shortcut hands the
41
+ // agent one message somebody is looking at, and the GLOBAL shortcut starts a conversation
42
+ // from the composer with no room in scope (it wakes the invoker's own DM window). Both
43
+ // arrive on the interactivity Request URL declared under `settings` below, which is why
44
+ // that block is no longer optional in this manifest.
45
+ //
46
+ // The `callback_id`s are free-form: the front route owns EVERY shortcut payload the app
47
+ // receives, so renaming one here changes only the trigger name the model is told about.
48
+ shortcuts: [
49
+ {
50
+ name: "Send to Open Everywhere",
51
+ type: "message",
52
+ callback_id: "oe_message_shortcut",
53
+ description: "Wake the agent on this message.",
54
+ },
55
+ {
56
+ name: "Ask Open Everywhere",
57
+ type: "global",
58
+ callback_id: "oe_global_shortcut",
59
+ description: "Wake the agent in a DM.",
60
+ },
61
+ ],
62
+ },
63
+ oauth_config: {
64
+ scopes: {
65
+ bot: [
66
+ "app_mentions:read",
67
+ "chat:write",
68
+ "channels:history",
69
+ // Slack's own description: "Add shortcuts and slash commands that people can use". It
70
+ // grants no read access — it is what lets this manifest DECLARE the `/oe` command above
71
+ // and the two shortcuts beside it. Both entry points are wakes an install needs.
72
+ "commands",
73
+ // conversations.info/conversations.members (channel trust gate, join intros, the
74
+ // governance roster) — public and private channel halves. Without these, EVERY
75
+ // channel binds `external` (fail-closed) on a fresh install, silently (OPE-175).
76
+ "channels:read",
77
+ "groups:read",
78
+ "im:history",
79
+ // conversations.info/conversations.members on a `D…` DM — the `im` half of the SAME two
80
+ // calls channels:read/groups:read cover for channels. The governance roster
81
+ // (slackGovernanceRoster) is keyed on whatever room a click or a card lands in, and DMs
82
+ // are rooms: on a member-tier approve-click in a DM the roster resolves the `D…` id, so
83
+ // without this both calls return `missing_scope`, the roster comes back unresolved, and
84
+ // the clicker floor's beneficiary leg refuses the click citing the roster (OPE-332,
85
+ // live-proven 2026-08-09). channels:read/groups:read do NOT cover an `im`.
86
+ "im:read",
87
+ // conversations.open — the governance DM-delivery arm (OPE-44): org-scope admin output
88
+ // asked in an ordinary room is delivered to the requester's DM, never rendered there.
89
+ "im:write",
90
+ "groups:history",
91
+ // The group-DM (`mpim`) half of `im:read` — same two calls, same call sites. Code-derived
92
+ // rather than live-proven: the roster/disclosure paths are keyed on the room, and a
93
+ // member-tier click or a card can land in a group DM exactly as it can in a 1:1.
94
+ "mpim:read",
95
+ "reactions:write",
96
+ "reactions:read",
97
+ "pins:read",
98
+ "pins:write",
99
+ // users.info — the DM-partner trust check. Without this, EVERY DM binds `external`
100
+ // (fail-closed) on a fresh install, silently (OPE-175).
101
+ "users:read",
102
+ ],
103
+ },
104
+ },
105
+ settings: {
106
+ event_subscriptions: {
107
+ request_url: `https://${SLACK_DEPLOY_HOST_PLACEHOLDER}/eve/v1/slack`,
108
+ bot_events: ["app_mention", "message.im", "reaction_added", "reaction_removed"],
109
+ },
110
+ // The INTERACTIVITY Request URL — the SAME public route Event Subscriptions uses. Every
111
+ // button click and every shortcut lands here, and the front route classifies which handler
112
+ // owns it. Required by the `shortcuts` above; without it Slack refuses to deliver a
113
+ // shortcut at all, and the approval / connector / "Set API key" cards' buttons fail with
114
+ // "the app is not configured to handle interactive responses".
115
+ interactivity: {
116
+ is_enabled: true,
117
+ request_url: `https://${SLACK_DEPLOY_HOST_PLACEHOLDER}/eve/v1/slack`,
118
+ },
119
+ org_deploy_enabled: false,
120
+ socket_mode_enabled: false,
121
+ token_rotation_enabled: false,
122
+ },
123
+ }, null, 2);
124
+ }
125
+ /** Where the operator pastes the manifest. */
126
+ export const SLACK_APP_CONSOLE_URL = "https://api.slack.com/apps";
127
+ /** The ordered clicks. Slack reserves every one of them for a workspace admin. */
128
+ export const SLACK_APP_MANIFEST_STEPS = [
129
+ "Open https://api.slack.com/apps and sign in to the workspace that will host the agent.",
130
+ "Create New App → From an app manifest.",
131
+ "Pick the workspace, choose JSON, and paste the manifest below.",
132
+ "Next → Create.",
133
+ "Install to Workspace, and approve the scopes Slack lists.",
134
+ ];
135
+ /** What the operator brings back. Both are environment variables, not credential-store secrets:
136
+ * the bundle reads them from the environment (agent/ops.ts). */
137
+ export const SLACK_APP_MANIFEST_COLLECT = [
138
+ "OAuth & Permissions → Bot User OAuth Token (xoxb-…) = SLACK_BOT_TOKEN",
139
+ "Basic Information → App Credentials → Signing Secret = SLACK_SIGNING_SECRET",
140
+ ];
141
+ //# sourceMappingURL=app-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-manifest.js","sourceRoot":"","sources":["../../agent/app-manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ import { type ApprovalCardRespondFn } from "@open-everywhere/core/agent/adapter-api.js";
2
+ /** The `input_response.routed` sibling for a tap: an approval settled through a BUTTON rather than a
3
+ * typed reply. RE-EXPORTED FROM CORE, never re-spelled here — one action name for every surface, so
4
+ * a scorer that joins on the row cannot under-count one of them (OPE-1856). */
5
+ export declare const APPROVAL_CARD_SETTLED_AUDIT_ACTION = "input_response.card_settled";
6
+ /** A verified tap that settled nothing, carrying WHICH refusal it was. Core-owned, as above. */
7
+ export declare const APPROVAL_CARD_REFUSED_AUDIT_ACTION = "input_response.card_refused";
8
+ /** The settle delivery this leg needs. CORE-OWNED and bound by the front route through
9
+ * `approvalCardRespondVia(args.attachSession)` — re-exported here, never re-spelled, so the address
10
+ * the answer takes cannot drift per surface (OPE-1863). */
11
+ export type ApprovalRespondFn = ApprovalCardRespondFn;
12
+ /** What one verified tap did, for the front route's log line. Never thrown — this endpoint answers a
13
+ * person who is waiting, so every path returns. */
14
+ export interface ApprovalCardClickOutcome {
15
+ readonly handled: boolean;
16
+ readonly reason: string;
17
+ /** A line to show the tapper alone, or undefined when the outcome needs no note. */
18
+ readonly notice?: string;
19
+ }
20
+ /** The `block_actions` fields this leg reads, narrowed so nothing here needs Slack's own types. */
21
+ interface ParsedApprovalClick {
22
+ readonly actionId: string;
23
+ readonly channelId: string;
24
+ readonly userId: string;
25
+ readonly teamId?: string;
26
+ }
27
+ /**
28
+ * Read the one `oe_input:` action out of a verified `block_actions` payload. `undefined` when the
29
+ * payload is not one of ours or is missing a field the decision needs — the front route then leaves
30
+ * it alone rather than half-handling it.
31
+ *
32
+ * THE FIRST `oe_input:` ACTION WINS and the rest are ignored: Slack sends one action per click, and a
33
+ * payload carrying two of ours is not a shape this host mints.
34
+ */
35
+ export declare function parseSlackApprovalClick(payload: unknown): ParsedApprovalClick | undefined;
36
+ /**
37
+ * Handle one verified `oe_input:` click: resolve it, admit it, deliver the answer to eve, consume the
38
+ * rows. NEVER THROWS — the front route has already 200'd the interaction, and an unhandled throw here
39
+ * would only lose the log line.
40
+ *
41
+ * 🔴 THE ANSWER TEXT IS HOST COPY, and it must be. A tap carries no words, so a transcript that read
42
+ * the button as the person's own prose would be inventing their words. What rides is the host label
43
+ * this deployment put on that button (`Approve` / `Cancel`), which is what the person actually chose,
44
+ * and the `optionId` beside it is what eve settles on — `resolveApprovalOutcome` reads `optionId`
45
+ * ALONE, so the text is transcript only and never the decision.
46
+ *
47
+ * 🔴 CONSUME-ONCE, AND BOTH ROWS. The ticket goes so a second tap on the same card cannot answer a
48
+ * request eve has already settled; the outstanding-question row goes so the room's NEXT message is an
49
+ * ordinary wake again rather than a second answer to a settled request. Both happen only after eve
50
+ * accepted the delivery, exactly as the text leg orders them.
51
+ *
52
+ * THE LIMIT OF THAT, stated rather than implied: the read and the delete are not one atomic step, and
53
+ * the delete runs only AFTER `respond` returns, so anything arriving while the first tap is still in
54
+ * flight resolves against rows that are both still present. Two such deliveries each spend an
55
+ * admission, each call `respond`, and each can write a settled audit row — so one approval can read
56
+ * as decided by two taps. It is not a second DECISION: both had to be admitted and both had to name
57
+ * an option the request offered, and eve's own resolution refuses the second delivery for a request
58
+ * it already settled. A delivery that arrives after the first settle RETURNED is the covered case —
59
+ * the ticket is gone and it resolves `no-ticket`.
60
+ *
61
+ * 🔴 A SECOND DELIVERY IS MOST LIKELY TO BE SLACK'S OWN, NOT A SECOND PERSON, and this handler does
62
+ * not ack until the settle returns. Slack expects an interactivity endpoint to answer within about
63
+ * three seconds; what it does past that — show the tapper a connection error, redeliver, or both —
64
+ * is VENDOR BEHAVIOUR THIS TREE HAS NOT MEASURED, so do not read the bound above as covering it.
65
+ * Acking first and settling in the background would remove the delay and widen exactly the window
66
+ * this paragraph describes; the ordering here takes the narrower window instead. Whoever measures
67
+ * the real behaviour should revisit that trade, with the measurement written down.
68
+ */
69
+ export declare function handleSlackApprovalCardClick(payload: unknown, respond: ApprovalRespondFn): Promise<ApprovalCardClickOutcome>;
70
+ export {};
71
+ //# sourceMappingURL=slack-approval-card.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack-approval-card.d.ts","sourceRoot":"","sources":["../../../agent/channels/slack-approval-card.ts"],"names":[],"mappings":"AAsBA,OAAO,EAkBL,KAAK,qBAAqB,EAE3B,MAAM,4CAA4C,CAAC;AAKpD;;gFAEgF;AAChF,eAAO,MAAM,kCAAkC,gCAA0C,CAAC;AAC1F,gGAAgG;AAChG,eAAO,MAAM,kCAAkC,gCAA0C,CAAC;AAE1F;;4DAE4D;AAC5D,MAAM,MAAM,iBAAiB,GAAG,qBAAqB,CAAC;AAEtD;oDACoD;AACpD,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,oFAAoF;IACpF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,mGAAmG;AACnG,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB,GAAG,SAAS,CAmBzF;AAmCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAsB,4BAA4B,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAsGlI"}