@open-everywhere/provider-discord 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 (166) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +21 -0
  3. package/README.md +47 -0
  4. package/agent/access/discord-grant-click.ts +237 -0
  5. package/agent/approvals/discord-admin.ts +227 -0
  6. package/agent/approvals/discord-card.ts +248 -0
  7. package/agent/approvals/discord-flow.ts +469 -0
  8. package/agent/approvals/discord-scope-label.ts +27 -0
  9. package/agent/channels/discord-approval-card.ts +373 -0
  10. package/agent/channels/discord-backscroll.ts +593 -0
  11. package/agent/channels/discord-bot-name.ts +113 -0
  12. package/agent/channels/discord-channel-info.ts +83 -0
  13. package/agent/channels/discord-directory.ts +179 -0
  14. package/agent/channels/discord-dispatch-target.ts +103 -0
  15. package/agent/channels/discord-events.ts +175 -0
  16. package/agent/channels/discord-front.ts +355 -0
  17. package/agent/channels/discord-gateway.ts +3256 -0
  18. package/agent/channels/discord-identity.ts +269 -0
  19. package/agent/channels/discord-interaction-defer.ts +123 -0
  20. package/agent/channels/discord-message-ops.ts +127 -0
  21. package/agent/channels/discord-origin-codec.ts +105 -0
  22. package/agent/channels/discord-post-arm.ts +230 -0
  23. package/agent/channels/discord-post.ts +463 -0
  24. package/agent/channels/discord-read.ts +140 -0
  25. package/agent/channels/discord-rest.ts +85 -0
  26. package/agent/channels/discord.ts +244 -0
  27. package/agent/commands/discord-command-sync.ts +285 -0
  28. package/agent/commands/discord-commands.ts +291 -0
  29. package/agent/contributions.ts +31 -0
  30. package/agent/docs-generated.ts +15 -0
  31. package/agent/identity/discord-link-consent.ts +124 -0
  32. package/agent/ops.ts +14 -0
  33. package/agent/provider-events.ts +86 -0
  34. package/agent/register.ts +147 -0
  35. package/agent/surface-parity.ts +95 -0
  36. package/dist/agent/access/discord-grant-click.d.ts +25 -0
  37. package/dist/agent/access/discord-grant-click.d.ts.map +1 -0
  38. package/dist/agent/access/discord-grant-click.js +210 -0
  39. package/dist/agent/access/discord-grant-click.js.map +1 -0
  40. package/dist/agent/approvals/discord-admin.d.ts +63 -0
  41. package/dist/agent/approvals/discord-admin.d.ts.map +1 -0
  42. package/dist/agent/approvals/discord-admin.js +219 -0
  43. package/dist/agent/approvals/discord-admin.js.map +1 -0
  44. package/dist/agent/approvals/discord-card.d.ts +102 -0
  45. package/dist/agent/approvals/discord-card.d.ts.map +1 -0
  46. package/dist/agent/approvals/discord-card.js +217 -0
  47. package/dist/agent/approvals/discord-card.js.map +1 -0
  48. package/dist/agent/approvals/discord-flow.d.ts +109 -0
  49. package/dist/agent/approvals/discord-flow.d.ts.map +1 -0
  50. package/dist/agent/approvals/discord-flow.js +375 -0
  51. package/dist/agent/approvals/discord-flow.js.map +1 -0
  52. package/dist/agent/approvals/discord-scope-label.d.ts +3 -0
  53. package/dist/agent/approvals/discord-scope-label.d.ts.map +1 -0
  54. package/dist/agent/approvals/discord-scope-label.js +27 -0
  55. package/dist/agent/approvals/discord-scope-label.js.map +1 -0
  56. package/dist/agent/channels/discord-approval-card.d.ts +81 -0
  57. package/dist/agent/channels/discord-approval-card.d.ts.map +1 -0
  58. package/dist/agent/channels/discord-approval-card.js +303 -0
  59. package/dist/agent/channels/discord-approval-card.js.map +1 -0
  60. package/dist/agent/channels/discord-backscroll.d.ts +209 -0
  61. package/dist/agent/channels/discord-backscroll.d.ts.map +1 -0
  62. package/dist/agent/channels/discord-backscroll.js +505 -0
  63. package/dist/agent/channels/discord-backscroll.js.map +1 -0
  64. package/dist/agent/channels/discord-bot-name.d.ts +19 -0
  65. package/dist/agent/channels/discord-bot-name.d.ts.map +1 -0
  66. package/dist/agent/channels/discord-bot-name.js +98 -0
  67. package/dist/agent/channels/discord-bot-name.js.map +1 -0
  68. package/dist/agent/channels/discord-channel-info.d.ts +22 -0
  69. package/dist/agent/channels/discord-channel-info.d.ts.map +1 -0
  70. package/dist/agent/channels/discord-channel-info.js +65 -0
  71. package/dist/agent/channels/discord-channel-info.js.map +1 -0
  72. package/dist/agent/channels/discord-directory.d.ts +11 -0
  73. package/dist/agent/channels/discord-directory.d.ts.map +1 -0
  74. package/dist/agent/channels/discord-directory.js +170 -0
  75. package/dist/agent/channels/discord-directory.js.map +1 -0
  76. package/dist/agent/channels/discord-dispatch-target.d.ts +15 -0
  77. package/dist/agent/channels/discord-dispatch-target.d.ts.map +1 -0
  78. package/dist/agent/channels/discord-dispatch-target.js +99 -0
  79. package/dist/agent/channels/discord-dispatch-target.js.map +1 -0
  80. package/dist/agent/channels/discord-events.d.ts +58 -0
  81. package/dist/agent/channels/discord-events.d.ts.map +1 -0
  82. package/dist/agent/channels/discord-events.js +150 -0
  83. package/dist/agent/channels/discord-events.js.map +1 -0
  84. package/dist/agent/channels/discord-front.d.ts +115 -0
  85. package/dist/agent/channels/discord-front.d.ts.map +1 -0
  86. package/dist/agent/channels/discord-front.js +279 -0
  87. package/dist/agent/channels/discord-front.js.map +1 -0
  88. package/dist/agent/channels/discord-gateway.d.ts +783 -0
  89. package/dist/agent/channels/discord-gateway.d.ts.map +1 -0
  90. package/dist/agent/channels/discord-gateway.js +2591 -0
  91. package/dist/agent/channels/discord-gateway.js.map +1 -0
  92. package/dist/agent/channels/discord-identity.d.ts +110 -0
  93. package/dist/agent/channels/discord-identity.d.ts.map +1 -0
  94. package/dist/agent/channels/discord-identity.js +201 -0
  95. package/dist/agent/channels/discord-identity.js.map +1 -0
  96. package/dist/agent/channels/discord-interaction-defer.d.ts +33 -0
  97. package/dist/agent/channels/discord-interaction-defer.d.ts.map +1 -0
  98. package/dist/agent/channels/discord-interaction-defer.js +102 -0
  99. package/dist/agent/channels/discord-interaction-defer.js.map +1 -0
  100. package/dist/agent/channels/discord-message-ops.d.ts +3 -0
  101. package/dist/agent/channels/discord-message-ops.d.ts.map +1 -0
  102. package/dist/agent/channels/discord-message-ops.js +123 -0
  103. package/dist/agent/channels/discord-message-ops.js.map +1 -0
  104. package/dist/agent/channels/discord-origin-codec.d.ts +7 -0
  105. package/dist/agent/channels/discord-origin-codec.d.ts.map +1 -0
  106. package/dist/agent/channels/discord-origin-codec.js +98 -0
  107. package/dist/agent/channels/discord-origin-codec.js.map +1 -0
  108. package/dist/agent/channels/discord-post-arm.d.ts +3 -0
  109. package/dist/agent/channels/discord-post-arm.d.ts.map +1 -0
  110. package/dist/agent/channels/discord-post-arm.js +195 -0
  111. package/dist/agent/channels/discord-post-arm.js.map +1 -0
  112. package/dist/agent/channels/discord-post.d.ts +120 -0
  113. package/dist/agent/channels/discord-post.d.ts.map +1 -0
  114. package/dist/agent/channels/discord-post.js +426 -0
  115. package/dist/agent/channels/discord-post.js.map +1 -0
  116. package/dist/agent/channels/discord-read.d.ts +40 -0
  117. package/dist/agent/channels/discord-read.d.ts.map +1 -0
  118. package/dist/agent/channels/discord-read.js +128 -0
  119. package/dist/agent/channels/discord-read.js.map +1 -0
  120. package/dist/agent/channels/discord-rest.d.ts +33 -0
  121. package/dist/agent/channels/discord-rest.d.ts.map +1 -0
  122. package/dist/agent/channels/discord-rest.js +75 -0
  123. package/dist/agent/channels/discord-rest.js.map +1 -0
  124. package/dist/agent/channels/discord.d.ts +3 -0
  125. package/dist/agent/channels/discord.d.ts.map +1 -0
  126. package/dist/agent/channels/discord.js +230 -0
  127. package/dist/agent/channels/discord.js.map +1 -0
  128. package/dist/agent/commands/discord-command-sync.d.ts +66 -0
  129. package/dist/agent/commands/discord-command-sync.d.ts.map +1 -0
  130. package/dist/agent/commands/discord-command-sync.js +224 -0
  131. package/dist/agent/commands/discord-command-sync.js.map +1 -0
  132. package/dist/agent/commands/discord-commands.d.ts +45 -0
  133. package/dist/agent/commands/discord-commands.d.ts.map +1 -0
  134. package/dist/agent/commands/discord-commands.js +266 -0
  135. package/dist/agent/commands/discord-commands.js.map +1 -0
  136. package/dist/agent/contributions.d.ts +17 -0
  137. package/dist/agent/contributions.d.ts.map +1 -0
  138. package/dist/agent/contributions.js +31 -0
  139. package/dist/agent/contributions.js.map +1 -0
  140. package/dist/agent/docs-generated.d.ts +3 -0
  141. package/dist/agent/docs-generated.d.ts.map +1 -0
  142. package/dist/agent/docs-generated.js +13 -0
  143. package/dist/agent/docs-generated.js.map +1 -0
  144. package/dist/agent/identity/discord-link-consent.d.ts +13 -0
  145. package/dist/agent/identity/discord-link-consent.d.ts.map +1 -0
  146. package/dist/agent/identity/discord-link-consent.js +111 -0
  147. package/dist/agent/identity/discord-link-consent.js.map +1 -0
  148. package/dist/agent/ops.d.ts +2 -0
  149. package/dist/agent/ops.d.ts.map +1 -0
  150. package/dist/agent/ops.js +13 -0
  151. package/dist/agent/ops.js.map +1 -0
  152. package/dist/agent/provider-events.d.ts +3 -0
  153. package/dist/agent/provider-events.d.ts.map +1 -0
  154. package/dist/agent/provider-events.js +78 -0
  155. package/dist/agent/provider-events.js.map +1 -0
  156. package/dist/agent/register.d.ts +5 -0
  157. package/dist/agent/register.d.ts.map +1 -0
  158. package/dist/agent/register.js +125 -0
  159. package/dist/agent/register.js.map +1 -0
  160. package/dist/agent/surface-parity.d.ts +3 -0
  161. package/dist/agent/surface-parity.d.ts.map +1 -0
  162. package/dist/agent/surface-parity.js +90 -0
  163. package/dist/agent/surface-parity.js.map +1 -0
  164. package/package.json +185 -0
  165. package/tsconfig.build.json +22 -0
  166. package/tsconfig.json +15 -0
@@ -0,0 +1,505 @@
1
+ // Wake-time conversation backscroll for Discord — the parity twin of slack-wake-backscroll.ts.
2
+ //
3
+ // Discord's name-drop WAKE and @mention paths (discord-gateway.ts) used to hand the model ONLY
4
+ // the triggering message: a cold name-drop mid-thread arrived with no idea what's being
5
+ // discussed, and a re-wake after auto-disengage resumed the same session with a GAP for
6
+ // everything said while the bot was away. This module closes both by fetching recent history at
7
+ // wake time and attaching it as the SAME untrusted-wrapped `context` strings the Slack paths use
8
+ // (eve appends each as a user message before the delivery turn — SendPayload.context).
9
+ //
10
+ // SIMPLER THAN SLACK: Discord threads are their OWN channel ids, so ONE endpoint —
11
+ // `GET /channels/{channelId}/messages` — covers both a base channel and a thread (no
12
+ // history/replies split). We always read strictly OLDER than the trigger via `?before=<id>`
13
+ // (Discord returns newest-first; we reverse to oldest-first). For an in-THREAD wake we then slice
14
+ // to "since the agent last spoke" (shared sinceLastAgentReply) — a resumed thread session already
15
+ // holds everything up to its last reply; for a BASE channel we keep the recent tail (last N).
16
+ //
17
+ // AUTH: reuses the Discord bot token exactly as discord-gateway.ts's REST calls do (`Bot <token>`
18
+ // header) — no new auth path. The bot's OWN author id (msg.botId, forwarded by the relay) both
19
+ // labels the agent's prior posts as `sender_type=agent` AND anchors the since-last-agent slice;
20
+ // absent (older relay) it simply degrades (own posts read as a bot, no slice boundary) — never a
21
+ // crash. The requester is injected so the fetcher unit-tests offline with NO network.
22
+ //
23
+ // RENDER REUSE: the ContextLine shape, the render + UNTRUSTED wrap, the caps, and the
24
+ // since-last-agent slice all come from the shared context-render.ts — the SAME renderer Slack
25
+ // uses — so Discord's attribution + untrusted framing are byte-identical to Slack's without
26
+ // cross-importing a slack-named file. The untrusted wrap is a SECURITY invariant (channel content
27
+ // is DATA, never instructions), so it must never diverge between surfaces. NO read-more hint:
28
+ // Discord exposes no read_discord_* tools today, so naming a "fetch more" tool would be a lie —
29
+ // the bounded backscroll (the actual win) is attached regardless.
30
+ //
31
+ // BOOTSTRAP BACKSCROLL is fail-soft: any error yields no context and never blocks the first wake.
32
+ // SEAT CATCH-UP propagates the same read failure to core's shared fail-soft boundary, so the wake
33
+ // still continues but its `seat.catch_up` row says `failed` rather than `empty` (OPE-1842).
34
+ //
35
+ // TWO MECHANISMS LIVE IN THIS FILE (both wake-safe, both Discord-specific fetch adapters):
36
+ // 1. fetchDiscordBackscrollLines — the FLAT name-drop/@mention enrichment above: raw history →
37
+ // context-render.ts ContextLine. BOTH Discord wake dispatch paths call it directly and grade
38
+ // the RAW lines with gradeDiscordContextLines (OPE-475) before rendering: the gateway's NATIVE
39
+ // dispatch hands the graded lines to the shared renderWake as the NormalizedWake's `contextLines`
40
+ // (W3 — renderWake does the untrusted-wrap); the SINGLE-WINDOW divert path renders them itself
41
+ // via the shared renderContext (discord-gateway.ts `discordSeatContextForWindow`) — a
42
+ // pre-rendered `context` string, but sourced from the SAME graded lines the join reads via
43
+ // contextLineIngestion, so the join can never carry a line the render's own source set did
44
+ // not. 🔴 THAT IS NOT AN EQUALITY OF COUNTS: renderContext applies its own MAX_CHARS bound and
45
+ // skips a line whose text trims empty, so the render can keep FEWER lines than the join marks.
46
+ // The direction over-taints, which is the safe one.
47
+ // 🔴 BOTH PATHS TAKE THE DOMAIN'S ADMISSION POLICY, and the SEAT decides, never the road
48
+ // (OPE-1800, discord-gateway.ts `admitDiscordSeatLines`). The policy runs wherever
49
+ // `grainUsesUnifiedWindow` holds for the room — which is the predicate Slack resolves `seated`
50
+ // from — so a wake that falls through to the native arm because its seat STOOD DOWN (the guild
51
+ // vouch absent) is policed too. A room with no seat at all keeps every graded line.
52
+ // 2. fetchDiscordBackscrollWindow / fetchDiscordWakeEnvelopeBackscroll (added below) — the DROP-IN
53
+ // fetch adapter behind the platform-NEUTRAL gap-clustering/bounds core (backscroll-window.ts),
54
+ // the exact Slack shape (slack-wake-backscroll.ts fetchSlackBackscrollWindow /
55
+ // fetchWakeEnvelopeBackscroll): raw history → visibility-gated NormalizedMessage[] →
56
+ // fetchedBackscroll → session-tagged WakeMessageInput[]. This fulfils the neutral core's
57
+ // "Discord drop-in" seam, and it is LIVE (OPE-1726): `discordSeatCatchUpFetch` below registers it
58
+ // as this surface's SEAT CATCH-UP fetch, so every wake on an ongoing generation reads the channel
59
+ // since the seat's own last wake and renders the result as the wake envelope's `<session>`
60
+ // siblings (discord-gateway.ts `divertDiscordWakeToUnifiedWindow`). The BOOTSTRAP half — a fresh
61
+ // generation's first wake — still carries the recorded tail rather than a platform fetch.
62
+ // The flat path (1) continues to serve the mention/name-drop hydration on every Discord wake.
63
+ import { sinceLastAgentReply, WAKE_BACKSCROLL_MAX, fetchedBackscroll, resolveWakeTrustFromContext, armedAuthorTrustTenants, catchUpSender, } from "@open-everywhere/core/agent/adapter-api.js";
64
+ import { discordAuthorRoles } from "../approvals/discord-admin.js";
65
+ // Direct bot-token GET to the Discord REST API. Mirrors discord-gateway.ts's own fetch calls
66
+ // (`Bot <token>` header; the token is a SECRET — never logged). Fail-soft: no token / non-ok /
67
+ // any throw → undefined. Kept local rather than imported from discord-gateway.ts so the module
68
+ // graph stays a DAG (discord-gateway → here; importing back up would cycle).
69
+ const liveRequest = async (channelId, query) => {
70
+ const token = process.env.DISCORD_BOT_TOKEN;
71
+ if (!token || !channelId)
72
+ return undefined;
73
+ try {
74
+ const qs = new URLSearchParams(query).toString();
75
+ const res = await fetch(`https://discord.com/api/v10/channels/${channelId}/messages?${qs}`, {
76
+ headers: { authorization: `Bot ${token}` },
77
+ });
78
+ if (!res.ok)
79
+ return undefined;
80
+ return await res.json();
81
+ }
82
+ catch {
83
+ return undefined;
84
+ }
85
+ };
86
+ const liveDeps = { request: liveRequest };
87
+ // Discord message `type`s we keep as ordinary conversation: 0 = DEFAULT, 19 = REPLY. Everything
88
+ // else (7 member-join, 6 pin, channel-name-change, thread-created, …) is system noise, dropped
89
+ // like Slack's `subtype` filter. A message with no/other type is treated conservatively as noise.
90
+ function isConversationType(type) {
91
+ return type === 0 || type === 19;
92
+ }
93
+ /**
94
+ * Map raw Discord message objects (`GET /channels/{id}/messages` — raw REST JSON, probe
95
+ * defensively) into {@link ContextLine}s, dropping system-type + empty-content messages. `botId`
96
+ * is the agent's own author id: a message by it is labelled `agent` (Discord uses one `author.id`
97
+ * for everyone — bots included — so we compare ids, same idea as Slack comparing bot_id/user_id).
98
+ * Order is PRESERVED — the caller reverses to oldest-first before rendering. Pure. Exported for
99
+ * the test oracle. Kept HERE (not shared): Discord's message JSON is shaped differently from
100
+ * Slack's, so the mapping is genuinely platform-specific; only the ContextLine target + renderer
101
+ * are shared (context-render.ts).
102
+ */
103
+ export function linesFromDiscordMessages(raw, botId) {
104
+ const lines = [];
105
+ for (const m of raw) {
106
+ if (!isConversationType(m.type))
107
+ continue; // system noise (joins, pins, thread-created, …)
108
+ const text = typeof m.content === "string" ? m.content : "";
109
+ if (!text)
110
+ continue; // empty content (Message Content Intent off, or an embed-only post)
111
+ const author = typeof m.author === "object" && m.author !== null ? m.author : undefined;
112
+ const authorId = author && typeof author.id === "string" ? author.id : undefined;
113
+ const isBot = author?.bot === true;
114
+ const id = typeof m.id === "string" ? m.id : "";
115
+ if (botId !== undefined && authorId === botId)
116
+ lines.push({ senderType: "agent", ts: id, text });
117
+ else if (isBot && authorId)
118
+ lines.push({ senderType: "bot", senderId: authorId, ts: id, text });
119
+ else if (authorId)
120
+ lines.push({ senderType: "user", senderId: authorId, ts: id, text });
121
+ else
122
+ lines.push({ senderType: "unknown", ts: id, text });
123
+ }
124
+ return lines;
125
+ }
126
+ /**
127
+ * PER-LINE trust for a mention/name-drop backscroll's {@link ContextLine}s (OPE-475) — the Discord
128
+ * analogue of Slack's regrade step, converging both backscroll shapes on ONE trust story rather than
129
+ * a second mechanism. Two additive facts, mirroring exactly what the TRIGGER already carries
130
+ * (discord-gateway.ts `resolveWakeTrustFromContext` + `authorFacts`), resolved per SIBLING:
131
+ *
132
+ * `trust` — {@link resolveWakeTrustFromContext} off the SAME binding the trigger reads, with
133
+ * `principalFallback` OFF (a sibling has its own author — see that function's own
134
+ * per-message-resolution note). Read by the RENDER. Resolved for every `user` line,
135
+ * unconditionally — no I/O, no gate.
136
+ * `authorTrust` — the OPERATOR's author-trust register query (identity/author-trust.ts, OPE-516):
137
+ * Discord's ONLY per-author refutation reachable here. `member.pending` (the
138
+ * trigger's own signal) is NOT available: it rides the gateway relay's MESSAGE_CREATE
139
+ * member object, and Discord's documented Message object (the shape
140
+ * `GET /channels/{id}/messages` — what this backscroll fetch reads — returns) has
141
+ * no `member` field at all. Read ONLY by the taint join.
142
+ *
143
+ * 🔴 THE REGISTER QUERY IS BUILT FROM VERIFIED ROLE EVIDENCE, NEVER FROM AN UNCHECKED GUESS (fixed
144
+ * after a caught bug: an earlier revision hardcoded `memberships: []` on EVERY sibling — on a guild
145
+ * armed the documented-usual way, `{ membership: "discord/<g>/role:*" }`, that made EVERY sibling
146
+ * look role-less and therefore `external`, so a mention wake that hydrated ordinary colleagues'
147
+ * messages marked the taint bit on a role-armed guild — the always-on-bit failure this whole plane
148
+ * exists to avoid). So the role fetch is COST-GATED and FAIL-OPEN, in that order:
149
+ *
150
+ * 1. UNARMED tenant (the common case — `armedAuthorTrustTenants()` doesn't list `discord/<guild>`):
151
+ * no register query is even worth building, so NO role fetch runs at all. Zero new I/O on the
152
+ * default posture.
153
+ * 2. ARMED tenant: each DISTINCT candidate author (a `user` line with a `senderId`) gets ONE
154
+ * {@link DiscordRoleProbe} call (TTL-cached — {@link discordAuthorRoles}), mirroring exactly how
155
+ * {@link regradeSlackContextLineTrust}'s probe is one call per distinct author, not per message.
156
+ * 3. The probe's THREE outcomes are kept apart, never collapsed to a boolean: a real (possibly
157
+ * empty, on a verified 404) roles array builds the SAME group-rung `{tenantKey}/role:<id>` keys
158
+ * the trigger's own `authorFacts` builds from `msg.memberRoles`; `undefined` (no token, a
159
+ * network error, an unreadable body) means "cannot check", and that line gets NO `authorTrust`
160
+ * at all — SKIPPED, never an unverified `[]`. "Cannot check" is not "checked and empty".
161
+ *
162
+ * 🔴 THE REGISTER LEG NEVER DEMOTES `trust` — it matches the trigger, and access/taint-signal.ts's own
163
+ * design: the register is a JOIN-ONLY refutation, decoupled from the render (see `nonMemberChatTrust`).
164
+ * So a register-refuted Discord backscroll sibling marks the turn but does not render the reference-only
165
+ * note — an honest limit of what this endpoint can tell the model, not a shortfall against Slack's
166
+ * parity (Slack's refutation is a SURFACE fact and demotes the render; Discord has none here).
167
+ *
168
+ * `agent`/`bot`/`unknown` lines are left untouched — machine or unattributable authors are not this
169
+ * leg's question.
170
+ */
171
+ export async function gradeDiscordContextLines(lines, ctx, guildId, roleProbe = discordAuthorRoles) {
172
+ const withTrust = lines.map((l) => l.senderType !== "user" ? l : { ...l, trust: resolveWakeTrustFromContext(ctx, { id: l.senderId, isBot: false }) });
173
+ const tenantKey = guildId !== undefined ? `discord/${guildId}` : undefined;
174
+ // COST GATE: an unarmed tenant's register leg contributes nothing (identity/author-trust.ts —
175
+ // silence is not a declaration), so there is nothing worth a role fetch for. Zero new I/O here is
176
+ // the default posture, not an edge case.
177
+ if (tenantKey === undefined || !armedAuthorTrustTenants().includes(tenantKey))
178
+ return withTrust;
179
+ const candidates = new Set();
180
+ for (const l of withTrust) {
181
+ if (l.senderType === "user" && l.senderId !== undefined)
182
+ candidates.add(l.senderId);
183
+ }
184
+ if (candidates.size === 0)
185
+ return withTrust;
186
+ const ids = [...candidates];
187
+ const roleResults = await Promise.all(ids.map((id) => roleProbe(guildId, id)));
188
+ const rolesById = new Map(ids.map((id, i) => [id, roleResults[i]]));
189
+ return withTrust.map((l) => {
190
+ if (l.senderType !== "user" || l.senderId === undefined)
191
+ return l;
192
+ const roles = rolesById.get(l.senderId);
193
+ if (roles === undefined)
194
+ return l; // cannot check — SKIP the register query for this line entirely
195
+ const authorTrust = {
196
+ tenantKey,
197
+ authorId: l.senderId,
198
+ memberships: roles.map((r) => `${tenantKey}/role:${r}`),
199
+ };
200
+ return { ...l, authorTrust };
201
+ });
202
+ }
203
+ // Pull the message array out of a REST response, or undefined (fail-soft on junk / non-array).
204
+ function extractDiscordMessages(res) {
205
+ if (!Array.isArray(res))
206
+ return undefined;
207
+ return res;
208
+ }
209
+ /**
210
+ * Fetch recent conversation history for a Discord wake/mention as RAW, oldest-first
211
+ * {@link ContextLine}s, capped at {@link WAKE_BACKSCROLL_MAX}. `undefined` = nothing to add OR any
212
+ * failure — always non-fatal. Both Discord wake dispatch paths (discord-gateway.ts, native and
213
+ * single-window divert) call this directly, grade the lines with gradeDiscordContextLines
214
+ * (OPE-475), and render them via the shared renderContext THEMSELVES — no pre-rendered string is
215
+ * built inside this module.
216
+ *
217
+ * One endpoint (`GET /channels/{channelId}/messages?limit&before`) for both cases:
218
+ * - IN-THREAD (`isThread`): strictly-older messages, then sliced to "since the agent's last
219
+ * reply" (a resumed thread session already holds everything up to its last reply).
220
+ * - BASE CHANNEL: strictly-older messages, most recent N.
221
+ */
222
+ export async function fetchDiscordBackscrollLines(input, deps = liveDeps) {
223
+ try {
224
+ const res = await deps.request(input.channelId, {
225
+ limit: String(WAKE_BACKSCROLL_MAX),
226
+ // Strictly OLDER than the trigger — the trigger's text already rides on the wake message.
227
+ before: input.triggerMessageId,
228
+ });
229
+ const raw = extractDiscordMessages(res);
230
+ if (!raw)
231
+ return undefined;
232
+ // Discord returns newest-first → reverse to oldest-first for the slice + render.
233
+ const ordered = [...raw].reverse();
234
+ let lines = linesFromDiscordMessages(ordered, input.botId);
235
+ // In a thread, keep only what followed the agent's last reply (the disengage/re-wake gap, or
236
+ // "someone answered while I was away"); a base channel keeps the recent tail via the N-cap.
237
+ if (input.isThread)
238
+ lines = sinceLastAgentReply(lines);
239
+ const capped = lines.slice(-WAKE_BACKSCROLL_MAX);
240
+ if (capped.length === 0)
241
+ return undefined;
242
+ return { lines: capped, source: `discord ${input.isThread ? "thread" : "channel"} ${input.channelId}` };
243
+ }
244
+ catch {
245
+ return undefined; // fail-soft: a wake never depends on its backscroll
246
+ }
247
+ }
248
+ // --- neutral-core first-wake backscroll (supervisor context hydration) — DISCORD ADAPTER -----
249
+ //
250
+ // THIS IS THE DISCORD FETCH ADAPTER behind the platform-NEUTRAL clustering/bounds core
251
+ // (backscroll-window.ts), the mirror of slack-wake-backscroll.ts's fetchSlackBackscrollWindow.
252
+ // Its job: turn `GET /channels/{id}/messages` into a visibility-GATED {@link NormalizedMessage}[]
253
+ // window, then hand it to {@link fetchedBackscroll}, which caps it most-recent-first and
254
+ // spine/bolt + count/time/token bounds and emits session-tagged {@link WakeMessageInput}[] the wake
255
+ // envelope renders as `<session>` groups.
256
+ //
257
+ // SIMPLER THAN SLACK (again): a Discord thread is its own channel id, so the SAME single endpoint
258
+ // covers a base channel AND a thread — there is no conversations.history/replies split and thus no
259
+ // bounded thread-reply fan-out to mirror (a base-channel window here does NOT reach into the
260
+ // replies of threads hanging off it; that's the deliberate one-endpoint scope, matching the flat
261
+ // path). FAIL-SOFT ABSOLUTELY throughout, exactly like {@link fetchDiscordBackscrollLines}.
262
+ /** Reachback (hours) for the raw Discord window: how far back from the trigger we paginate. Shares
263
+ * the SAME env + 7d default as the Slack adapter (slack-wake-backscroll.ts RAW_FETCH_REACHBACK_HOURS)
264
+ * — it is one supervisor-backscroll concept, kept in sync by reading the same knob. Generous enough
265
+ * that the spine (the most-recent cluster, exempt from the 36h enrichment cap) still spans a
266
+ * weekend/overnight gap, yet a hard floor bounding the fetch. */
267
+ export const DISCORD_RAW_FETCH_REACHBACK_HOURS = Number(process.env.OE_SUPERVISOR_BACKSCROLL_REACHBACK_HOURS) || 168;
268
+ /** `GET /channels/{id}/messages` page size (Discord's max is 100). */
269
+ const DISCORD_RAW_FETCH_PAGE_LIMIT = 100;
270
+ /** Max history pages to paginate — bounds a busy channel's fetch at ≤300 messages so a burst can't
271
+ * fan out into an unbounded loop. Combined with the reachback floor (whichever binds first). */
272
+ const DISCORD_RAW_FETCH_MAX_PAGES = 3;
273
+ /** Discord's epoch (2015-01-01T00:00:00Z, ms) — the offset baked into every snowflake id. */
274
+ const DISCORD_EPOCH_MS = 1_420_070_400_000;
275
+ /**
276
+ * The creation time (epoch ms) encoded in a Discord snowflake id: the top 42 bits are
277
+ * `(unix_ms - DISCORD_EPOCH_MS)`. Robust — every message id carries it, so we can derive a time even
278
+ * when the raw `timestamp` field is absent/garbled. Returns undefined for a non-numeric / non-positive
279
+ * id (fail-soft; the caller drops that message from the window rather than guessing a time).
280
+ */
281
+ export function snowflakeToMs(id) {
282
+ try {
283
+ const n = BigInt(id);
284
+ if (n <= 0n)
285
+ return undefined;
286
+ return Number(n >> 22n) + DISCORD_EPOCH_MS;
287
+ }
288
+ catch {
289
+ return undefined; // not an integer id → no derivable time
290
+ }
291
+ }
292
+ /**
293
+ * The INVERSE of {@link snowflakeToMs}: the smallest snowflake whose encoded time is `ms`, for a
294
+ * `before=` anchor when the caller states a TIME and holds no message id (the catch-up fetch with no
295
+ * trigger). The low 22 bits (worker, process, sequence) are zero, which is exactly what "the
296
+ * beginning of this millisecond" means to Discord's own ordering. A time at or before Discord's epoch
297
+ * clamps to `1` — the smallest usable anchor — rather than producing a negative id.
298
+ */
299
+ function msToSnowflake(ms) {
300
+ const delta = Math.max(0, Math.floor(ms) - DISCORD_EPOCH_MS);
301
+ const id = BigInt(delta) << 22n;
302
+ return (id > 0n ? id : 1n).toString();
303
+ }
304
+ /** The message's epoch ms: prefer the canonical ISO `timestamp` field, else decode the snowflake
305
+ * `id` (both present on a REST message object). undefined ⇒ neither yielded a finite time. */
306
+ function messageMs(m, id) {
307
+ const iso = typeof m.timestamp === "string" ? Date.parse(m.timestamp) : Number.NaN;
308
+ if (Number.isFinite(iso))
309
+ return iso;
310
+ return snowflakeToMs(id);
311
+ }
312
+ /**
313
+ * The VISIBILITY / TRUST GATE (the load-bearing SECURITY filter) — the Discord parity of
314
+ * slack-wake-backscroll.ts isBackscrollVisible, kept LOCAL (not imported from a slack-named file, the
315
+ * same DAG discipline as linesFromDiscordMessages). Backscroll must never surface a message it can't
316
+ * ATTRIBUTE. KEEPS: the bot's OWN prior posts (`agent` → `self`); identifiable humans (`user` →
317
+ * `human`); ATTRIBUTABLE foreign bots (`bot` WITH a bot id → `bot`) as MARKED AWARENESS (clearly
318
+ * bot-flagged, CONTEXT-ONLY, never a wake trigger — dropping them would break conversational
319
+ * coherence when a human replies to a bot post). DROPS ONLY unattributable senders (`unknown`, or a
320
+ * `bot` with no id) — no auth'd author ⇒ fail closed. (System-subtype / empty-content noise is already
321
+ * dropped upstream by linesFromDiscordMessages.) The neutral core never sets `trigger` on backscroll,
322
+ * and the gateway's live wake gate still drops bot authors as triggers, so a bot post here is
323
+ * awareness, never a response obligation. */
324
+ export function isDiscordBackscrollVisible(line) {
325
+ return (line.senderType === "agent" ||
326
+ line.senderType === "user" ||
327
+ (line.senderType === "bot" && line.senderId !== undefined) // attributable foreign bot: marked awareness
328
+ );
329
+ }
330
+ /**
331
+ * Normalize ONE raw Discord message → {@link NormalizedMessage}, reusing {@link linesFromDiscordMessages}
332
+ * for the exact sender classification + system/empty drop, then applying the VISIBILITY GATE and
333
+ * resolving the epoch ms. Returns undefined for a dropped (invisible / system / empty / timeless)
334
+ * message. `type`/`threadId` describe the surface the window was read from (a Discord thread is its
335
+ * own channel id, so a thread window tags every message `thread` with the thread's id as the root).
336
+ */
337
+ function normalizeDiscordMessage(m, botId, type, threadId) {
338
+ const [line] = linesFromDiscordMessages([m], botId);
339
+ if (!line)
340
+ return undefined; // system type / empty content (dropped upstream)
341
+ if (!isDiscordBackscrollVisible(line))
342
+ return undefined; // VISIBILITY GATE (unattributable sender only)
343
+ const ms = messageMs(m, line.ts);
344
+ if (ms === undefined)
345
+ return undefined; // no derivable time → can't cluster it, drop
346
+ return {
347
+ id: line.ts,
348
+ ms,
349
+ // undefined for the bot's OWN posts (agent); the foreign bot's id for a `bot`; the user id for a
350
+ // human — kept raw for @-tagging / attribution.
351
+ authorId: line.senderId,
352
+ // THE SHARED SENDER RULE (OPE-1857): agent → self (our own post); bot → bot (attributable
353
+ // foreign bot, marked awareness); every other visible line is a `user` → human. `unknown` was
354
+ // already dropped by the gate above.
355
+ sender: catchUpSender({ self: line.senderType === "agent", bot: line.senderType === "bot" }),
356
+ type,
357
+ threadId,
358
+ text: line.text,
359
+ };
360
+ }
361
+ /** Paginate `GET /channels/{id}/messages` backward from `before` (exclusive), bounded by
362
+ * {@link DISCORD_RAW_FETCH_MAX_PAGES} AND the reachback floor (`oldestMs`, whichever binds first).
363
+ * Discord returns each page newest-first; the next page anchors on the page's OLDEST id. Returns the
364
+ * raw messages (newest-first across pages — the neutral core sorts by ms anyway). Fail-soft: a
365
+ * junk/empty page ends pagination with whatever's in hand. */
366
+ async function fetchDiscordWindow(request, channelId, triggerMessageId, oldestMs) {
367
+ const all = [];
368
+ let before = triggerMessageId;
369
+ for (let page = 0; page < DISCORD_RAW_FETCH_MAX_PAGES; page++) {
370
+ const res = await request(channelId, { limit: String(DISCORD_RAW_FETCH_PAGE_LIMIT), before });
371
+ const msgs = extractDiscordMessages(res);
372
+ if (!msgs || msgs.length === 0)
373
+ break;
374
+ all.push(...msgs);
375
+ const oldest = msgs[msgs.length - 1]; // Discord newest-first → last is the oldest in this page
376
+ const oldestId = typeof oldest.id === "string" ? oldest.id : undefined;
377
+ if (!oldestId)
378
+ break;
379
+ const oldestMsThisPage = messageMs(oldest, oldestId);
380
+ if (oldestMsThisPage !== undefined && oldestMsThisPage < oldestMs)
381
+ break; // paged past the floor
382
+ if (msgs.length < DISCORD_RAW_FETCH_PAGE_LIMIT)
383
+ break; // short page ⇒ no more history
384
+ before = oldestId; // next page: strictly older than this page's oldest
385
+ }
386
+ return all;
387
+ }
388
+ /**
389
+ * DISCORD FETCH SEAM: fetch a bounded, visibility-GATED, NORMALIZED backscroll window for a first
390
+ * wake. This is the platform boundary — the neutral core ({@link fetchedBackscroll}) consumes its
391
+ * output. FAIL-SOFT: any error/failure yields `[]`. Deduped by message id.
392
+ */
393
+ export async function fetchDiscordBackscrollWindow(input, deps = liveDeps, failureMode = "soft") {
394
+ try {
395
+ const triggerMs = snowflakeToMs(input.triggerMessageId);
396
+ const reachbackHours = input.reachbackHours ?? DISCORD_RAW_FETCH_REACHBACK_HOURS;
397
+ // Reachback origin = the trigger's own time when derivable, else now (fail-soft — a bad trigger
398
+ // id still yields a sane recent window rather than nothing).
399
+ const originMs = triggerMs ?? Date.now();
400
+ // The reachback floor, then the seat's own last wake where it is LATER — never earlier, so the
401
+ // catch-up can only narrow the fetch (see {@link DiscordBackscrollWindowInput.sinceMs}).
402
+ const floorMs = originMs - reachbackHours * 3_600_000;
403
+ const oldestMs = typeof input.sinceMs === "number" && Number.isFinite(input.sinceMs) ? Math.max(floorMs, input.sinceMs) : floorMs;
404
+ const raw = await fetchDiscordWindow(deps.request, input.channelId, input.triggerMessageId, oldestMs);
405
+ if (raw.length === 0)
406
+ return [];
407
+ const type = input.isThread ? "thread" : "channel";
408
+ const threadId = input.isThread ? input.channelId : undefined;
409
+ const seen = new Set();
410
+ const out = [];
411
+ for (const m of raw) {
412
+ const n = normalizeDiscordMessage(m, input.botId, type, threadId);
413
+ if (!n)
414
+ continue;
415
+ if (n.ms < oldestMs)
416
+ continue; // outside the reachback window
417
+ if (seen.has(n.id))
418
+ continue; // dedupe (paging overlap / repeats)
419
+ seen.add(n.id);
420
+ out.push(n);
421
+ }
422
+ return out;
423
+ }
424
+ catch (err) {
425
+ if (failureMode === "throw")
426
+ throw err;
427
+ return []; // fail-soft: a first wake never depends on its backscroll
428
+ }
429
+ }
430
+ /**
431
+ * Fetch + cluster a Discord first-wake backscroll into session-tagged {@link WakeMessageInput}
432
+ * entries (oldest-first, no `trigger`). Thin orchestration over the Discord fetch seam
433
+ * ({@link fetchDiscordBackscrollWindow}) + the neutral clustering/bounds core
434
+ * ({@link fetchedBackscroll}) — the exact parity of slack-wake-backscroll.ts
435
+ * fetchWakeEnvelopeBackscroll. FAIL-SOFT ABSOLUTELY.
436
+ *
437
+ * Per-message trust mirrors the Slack seam: pass a `trustFor` resolver (build it with
438
+ * identity/wake-trust.ts `backscrollTrustResolver`, off the confirmInternal verdict + domain-grain
439
+ * channelKey) so each backscroll author gets its real roster/gate-derived tier (F3). When omitted,
440
+ * fetchedBackscroll's fail-closed per-sender default applies (a human → `unauth`, self/bot →
441
+ * `relay`), never a blanket `peer`.
442
+ *
443
+ * NOTE: this is a proven DROP-IN with no live caller yet — Discord has no wake-envelope render
444
+ * surface to render the `<session>` output on a live wake (see the file header + the
445
+ * TODO(discord-supervisor-backscroll) at discord-gateway.ts's dispatch seam). The `trustFor` seam
446
+ * is wired here ahead of that surface so, when the Discord supervisor lands, it passes the resolver
447
+ * exactly as slack-events.ts's live supervisor deps do for fetchWakeEnvelopeBackscroll.
448
+ */
449
+ export async function fetchDiscordWakeEnvelopeBackscroll(input, deps = liveDeps, trustFor) {
450
+ try {
451
+ const window = await fetchDiscordBackscrollWindow(input, deps);
452
+ if (window.length === 0)
453
+ return [];
454
+ return fetchedBackscroll(window, undefined, trustFor);
455
+ }
456
+ catch {
457
+ return []; // fail-soft: a first wake never depends on its backscroll
458
+ }
459
+ }
460
+ /**
461
+ * DISCORD'S REGISTERED CATCH-UP FETCH (OPE-1726) — the one thing this surface contributes to the
462
+ * shared since-last-wake path (core channels/seat-catch-up.ts). It reuses the window fetch above,
463
+ * addressed by the neutral request shape: the seat's container, bounded below by its last wake.
464
+ * 🔴 THE UPPER BOUND IS A MESSAGE ID, not a time, because `GET /channels/:id/messages` pages with
465
+ * `before=<snowflake>` and takes no timestamp. `excludeId` IS that snowflake (the trigger's own id),
466
+ * and with none the read anchors on a snowflake synthesised from `untilMs` — Discord ids embed their
467
+ * own millisecond, so the synthesised anchor orders correctly even though it names no real message.
468
+ * The shared path re-applies both bounds on its own side, so a page that overshoots is trimmed there.
469
+ * `threadRoot` present ⇒ the seat is bound to a thread, and a Discord thread IS its own channel id,
470
+ * so the read addresses that id and tags every message `thread`.
471
+ * A transport/read failure propagates to core's shared boundary, which keeps the wake alive and
472
+ * records `failed` instead of reporting a quiet channel (OPE-1842). Bootstrap callers keep the
473
+ * window fetch's default fail-soft mode.
474
+ */
475
+ export async function fetchDiscordSeatCatchUp(req, deps = liveDeps) {
476
+ const isThread = req.threadRoot !== undefined;
477
+ const anchor = req.threadRoot ?? req.container;
478
+ const triggerMessageId = req.excludeId ?? msToSnowflake(req.untilMs ?? Date.now());
479
+ const strictDeps = {
480
+ request: async (channelId, query) => {
481
+ const res = await deps.request(channelId, query);
482
+ if (!Array.isArray(res))
483
+ throw new Error("Discord catch-up request failed");
484
+ return res;
485
+ },
486
+ };
487
+ return fetchDiscordBackscrollWindow({ channelId: anchor, triggerMessageId, isThread, sinceMs: req.sinceMs }, strictDeps, "throw");
488
+ }
489
+ /**
490
+ * A Discord snowflake encodes MILLISECONDS and the ISO `timestamp` field carries them too (OPE-1847,
491
+ * `core/agent/channels/seat-catch-up.ts` {@link SeatCatchUpDeclaration.timestampPrecisionMs}).
492
+ * 🔴 ONE MILLISECOND IS NOT ZERO WIDENING. The road widens the LOWER bound by nothing here and the
493
+ * UPPER bound by exactly one millisecond, so a message stamped at the trigger's own instant is
494
+ * INSIDE the window and `excludeId` is the only thing that removes the trigger. The paging call
495
+ * already anchors on `before=<the trigger snowflake>`, which is exclusive, and a message that did
496
+ * arrive would carry that snowflake as its id.
497
+ * 🔴 IT IS STILL DECLARED, and the parity test requires it. The declaration is what makes "this
498
+ * surface needs the finest unit" a CHECKED claim rather than an omission, and an omission is
499
+ * indistinguishable from the GitHub and Notion defect this whole mechanism exists to close.
500
+ */
501
+ export const DISCORD_TIMESTAMP_PRECISION_MS = 1;
502
+ /** The registered form — the transport seam is fixed to the live one. Tests drive
503
+ * {@link fetchDiscordSeatCatchUp} with a canned requester instead. */
504
+ export const discordSeatCatchUpFetch = (req) => fetchDiscordSeatCatchUp(req);
505
+ //# sourceMappingURL=discord-backscroll.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discord-backscroll.js","sourceRoot":"","sources":["../../../agent/channels/discord-backscroll.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { BotDisplayNameArm } from "@open-everywhere/core/agent/adapter-api.js";
2
+ /** `GET /users/@me` — injectable for offline tests, same DI shape as discord-read.ts's
3
+ * `DiscordReadRequester`. Returns the parsed JSON body; throws on a missing token, a non-ok
4
+ * response, or a transport failure — the caller catches. */
5
+ export type DiscordSelfRequester = () => Promise<unknown>;
6
+ /**
7
+ * The bot's own Discord handle — `global_name` when Discord reports one, else `username` — or
8
+ * `undefined` when neither resolves. Pure but for the injected `req`, so the resolution rule is
9
+ * unit-testable with a canned requester: no token mint and no network call. FAIL-SOFT on
10
+ * everything: a thrown request (no token / non-ok status / transport failure) and a response that
11
+ * is not an object both read `undefined`.
12
+ */
13
+ export declare function discordBotDisplayNameViaRest(req: DiscordSelfRequester): Promise<string | undefined>;
14
+ /** Live arm: {@link discordBotDisplayNameViaRest} through the real REST transport, resolved once
15
+ * per instance and registered into core's registry from register.ts. */
16
+ export declare const discordBotDisplayName: BotDisplayNameArm;
17
+ /** Test seam: drop the memoised handle so one test cannot inherit another's. */
18
+ export declare function _resetDiscordBotDisplayNameForTest(): void;
19
+ //# sourceMappingURL=discord-bot-name.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discord-bot-name.d.ts","sourceRoot":"","sources":["../../../agent/channels/discord-bot-name.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAC;AAIpF;;6DAE6D;AAC7D,MAAM,MAAM,oBAAoB,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;AAyB1D;;;;;;GAMG;AACH,wBAAsB,4BAA4B,CAAC,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAOzG;AAKD;yEACyE;AACzE,eAAO,MAAM,qBAAqB,EAAE,iBAmBnC,CAAC;AAEF,gFAAgF;AAChF,wBAAgB,kCAAkC,IAAI,IAAI,CAEzD"}