@open-everywhere/provider-discord 0.4.0 → 0.5.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.
@@ -18,8 +18,8 @@
18
18
  // DECIDED card (no buttons), and the JUDGE-LIFT NOTICE (OPE-1103) — a review surface for an action
19
19
  // that already ran, carrying the one "This should not have run" button and never a decision one.
20
20
 
21
- import type { PendingActionRow } from "@open-everywhere/core/agent/adapter-api.js";
22
- import { getSettings, grantButtonLabel, pendingCardExpiryClause, sessionGrantKind } from "@open-everywhere/core/agent/adapter-api.js";
21
+ import type { CardOptionTone, PendingActionRow } from "@open-everywhere/core/agent/adapter-api.js";
22
+ import { approvalLabel, pendingCardLayout, plainSummaryForRow } from "@open-everywhere/core/agent/adapter-api.js";
23
23
 
24
24
  /**
25
25
  * Every approval button custom_id this feature owns starts with this. Deliberately distinct
@@ -111,47 +111,26 @@ export function parseDiscordApprovalCustomId(customId: string): { id: string; de
111
111
  return { id, decision: decision as DiscordApprovalDecision, org };
112
112
  }
113
113
 
114
- /** The shared card body: header + summary + provenance. `<@id>` is Discord's user-mention
115
- * syntax too, so the requester line reads identically to the Slack card. */
114
+ /** The shared card body: the plain sentence first (OPE-2434), then the `Approval requested: <kind>` label, the summary and the provenance. `<@id>` is Discord's user-mention syntax too, so the requester line reads identically to the Slack card. The sentence went through core's `plainValue`, so it carries no markdown of its own. */
116
115
  function cardBody(row: PendingActionRow, summaryLines: readonly string[], footer: string): string {
117
- return [`**Approval requested: ${row.kind}**`, ...summaryLines, footer].join("\n");
116
+ return [`**${plainSummaryForRow(row)}**`, `-# ${approvalLabel(row.kind)}`, ...summaryLines, footer].join("\n");
118
117
  }
119
118
 
119
+ const OPTION_STYLE: Record<CardOptionTone, number> = { primary: BUTTON_STYLE_SUCCESS, danger: BUTTON_STYLE_DANGER, neutral: BUTTON_STYLE_SECONDARY };
120
+
120
121
  /**
121
- * The card for a still-pending action: summary + "requested by / expires" + Approve/Reject
122
- * buttons carrying the pending id + org in their custom_ids (see the file header for why
123
- * the org rides the custom_id on Discord). Mirrors card.ts's pendingCardBlocks.
122
+ * The card for a still-pending action: the plain sentence, the summary, "requested by / expires", and one button per layout option, each carrying the pending id + org in its custom_id (see the file header for why the org rides the custom_id on Discord). Mirrors card.ts's pendingCardBlocks.
123
+ * The OPTIONS come from core's card layout (OPE-53, OPE-2363: only a ROUTINE's card gets the middle grant button), so Slack, Teams and Discord can never disagree, and OPE-2456's extra option lands here with no change.
124
124
  */
125
125
  export function pendingDiscordCard(row: PendingActionRow, summaryLines: readonly string[]): DiscordCardMessage {
126
- const content = cardBody(row, summaryLines, `-# Requested by <@${row.requestedBy}> · ${pendingCardExpiryClause(row.expiresAt)}`);
127
- const buttons: DiscordComponent[] = [
128
- {
129
- type: COMPONENT_BUTTON,
130
- style: BUTTON_STYLE_SUCCESS,
131
- label: "Approve",
132
- custom_id: buildDiscordApprovalCustomId(row, "approve"),
133
- },
134
- ];
135
- // OPE-53, OPE-2363: only a ROUTINE's card gets the middle button — publish this one AND skip the card
136
- // for the same kind on later fires of THIS routine. sessionGrantKind() (core approvals/session-grant.ts)
137
- // decides the KIND (`pr.open` alone), and grantButtonLabel() decides the reach: undefined for every
138
- // card in a conversation, which offers Approve and Reject only. The label and the presence both come
139
- // from core, so Slack and Discord can never disagree.
140
- const grantLabel = sessionGrantKind(row) !== null ? grantButtonLabel(row, getSettings().approvals.routineGrantTtlHours) : undefined;
141
- if (grantLabel !== undefined) {
142
- buttons.push({
143
- type: COMPONENT_BUTTON,
144
- style: BUTTON_STYLE_SECONDARY,
145
- label: grantLabel,
146
- custom_id: buildDiscordApprovalCustomId(row, "approve_session"),
147
- });
148
- }
149
- buttons.push({
126
+ const layout = pendingCardLayout(row, summaryLines);
127
+ const content = cardBody(row, summaryLines, `-# Requested by <@${layout.requestedBy}> · ${layout.expiry}`);
128
+ const buttons: DiscordComponent[] = layout.options.map((o) => ({
150
129
  type: COMPONENT_BUTTON,
151
- style: BUTTON_STYLE_DANGER,
152
- label: "Reject",
153
- custom_id: buildDiscordApprovalCustomId(row, "reject"),
154
- });
130
+ style: OPTION_STYLE[o.tone],
131
+ label: o.label,
132
+ custom_id: buildDiscordApprovalCustomId(row, o.verb),
133
+ }));
155
134
  return { content, components: [{ type: COMPONENT_ACTION_ROW, components: buttons }] };
156
135
  }
157
136
 
@@ -18,6 +18,10 @@ export interface DiscordChannelInfo {
18
18
  /** Present (with a thread type) when the channel is a THREAD — its parent channel id. */
19
19
  readonly parentId?: string;
20
20
  readonly isThread: boolean;
21
+ /** The guild the channel belongs to (`guild_id`), or undefined for a DM or a guild lookup.
22
+ * Read by the declared-target resolver (OPE-2419) to confirm a configured `discord/<guild>/<channel>`
23
+ * names a channel that really is in that guild. */
24
+ readonly guildId?: string;
21
25
  }
22
26
 
23
27
  /** Injectable seams (tests swap both; live callers pass nothing). */
@@ -55,14 +59,15 @@ export function fetchDiscordInfo(
55
59
  headers: { authorization: `Bot ${token}` },
56
60
  });
57
61
  if (!res.ok) return undefined;
58
- const body = (await res.json()) as { name?: unknown; parent_id?: unknown; type?: unknown };
62
+ const body = (await res.json()) as { name?: unknown; parent_id?: unknown; type?: unknown; guild_id?: unknown };
59
63
  const name = typeof body.name === "string" && body.name.trim() ? body.name.trim() : undefined;
60
64
  const isThread = body.type === 10 || body.type === 11 || body.type === 12;
61
65
  const parentId = isThread && typeof body.parent_id === "string" && body.parent_id ? body.parent_id : undefined;
62
66
  // A nameless channel resolves undefined wholesale — kept from the original
63
67
  // scope-label impl (its callers require the name), and harmless for the grain
64
68
  // consumer: every real guild channel/thread carries a name.
65
- return name ? { name, parentId, isThread } : undefined;
69
+ const guildId = kind === "channels" && typeof body.guild_id === "string" && body.guild_id ? body.guild_id : undefined;
70
+ return name ? { name, parentId, isThread, ...(guildId ? { guildId } : {}) } : undefined;
66
71
  } catch {
67
72
  return undefined;
68
73
  }
@@ -9,8 +9,17 @@
9
9
  // discordChannel().receive owns the send; NO Gateway websocket is involved (that transport is
10
10
  // inbound-ambient only). Target = `{ channelId }`.
11
11
 
12
- import type { Routine, DispatchTarget, SessionMode, UnifiedWakeOrigin } from "@open-everywhere/core/agent/adapter-api.js";
12
+ import type {
13
+ Routine,
14
+ DispatchTarget,
15
+ DeclaredTargetResolution,
16
+ DeclaredTargetSpec,
17
+ SessionMode,
18
+ UnifiedWakeOrigin,
19
+ } from "@open-everywhere/core/agent/adapter-api.js";
13
20
  import discord from "./discord.js";
21
+ import { discordOpenDmChannel } from "./discord-post.js";
22
+ import { fetchDiscordInfo } from "./discord-channel-info.js";
14
23
 
15
24
  /** One stored-target field as a non-empty string, or undefined (never a coerced non-string — the
16
25
  * store row is data, validated on the way out, not trusted). Local copy of core dispatch.ts's
@@ -101,3 +110,55 @@ export function discordReportTargetFor(surfaceKey: string): Readonly<Record<stri
101
110
  const channelId = segs.length === 3 && segs[0] === "discord" ? segs[2] : undefined;
102
111
  return channelId && channelId.length > 0 ? { channelId } : null;
103
112
  }
113
+
114
+ /**
115
+ * Resolve a target that config NAMES (OPE-2419 — a webhook's `target`) into the facts
116
+ * {@link resolveDiscordTarget} reads.
117
+ *
118
+ * • A ROOM, `discord/<guildId>/<channelId>`: the third segment IS the channel id (the same
119
+ * round-trip {@link discordReportTargetFor} relies on). The guild id is stored too, which is the
120
+ * GUILD EVIDENCE resolveDiscordTarget needs before it mints a guild-shaped wake origin.
121
+ * 🔴 THE GUILD IS CHECKED, NOT TRUSTED. The bot token posts to a channel id in ANY guild, while the
122
+ * fire's partition, domain and memory come from the guild segment. A target that names guild A
123
+ * and a channel in guild B would bind guild A's domain in guild B's room. So the resolver reads
124
+ * the channel's own `guild_id` and refuses on a mismatch, and also when it cannot read it.
125
+ * • A PERSON, `dm:discord/<userId>`: `POST /users/@me/channels` opens (or reuses) the 1:1 DM, and
126
+ * the target is stored with channel type "1", so the fire binds as a DM. The surface key's
127
+ * workspace segment is the person's user id — what bindDiscord derives for a DM (`guildId ?? userId`).
128
+ *
129
+ * A DM Discord refuses (the person shares no server with the bot, or turned off DMs from server
130
+ * members) comes back as a refusal that says so. It is never a silent drop.
131
+ */
132
+ export async function resolveDiscordDeclaredTarget(
133
+ spec: DeclaredTargetSpec,
134
+ deps: {
135
+ readonly openDm?: (userId: string) => Promise<string>;
136
+ readonly channelGuild?: (channelId: string) => Promise<string | undefined>;
137
+ } = {},
138
+ ): Promise<DeclaredTargetResolution> {
139
+ if (spec.kind === "room") {
140
+ const guildId = await (deps.channelGuild ?? (async (id: string) => (await fetchDiscordInfo("channels", id))?.guildId))(spec.surface).catch(() => undefined);
141
+ if (guildId !== spec.workspace) {
142
+ return {
143
+ ok: false,
144
+ reason:
145
+ guildId === undefined
146
+ ? `Discord did not confirm that channel ${spec.surface} is in guild ${spec.workspace} (the channel lookup failed, or the bot cannot see the channel). Check DISCORD_BOT_TOKEN and that the bot is in that guild.`
147
+ : `channel ${spec.surface} is in guild ${guildId}, not guild ${spec.workspace}. Correct the target to "discord/${guildId}/${spec.surface}".`,
148
+ };
149
+ }
150
+ return { ok: true, facts: { surfaceKey: `discord/${spec.workspace}/${spec.surface}`, target: { channelId: spec.surface, guildId: spec.workspace } } };
151
+ }
152
+ let channelId: string;
153
+ try {
154
+ channelId = await (deps.openDm ?? discordOpenDmChannel)(spec.principalId);
155
+ } catch (err) {
156
+ return {
157
+ ok: false,
158
+ reason:
159
+ `Discord could not open a DM with user ${spec.principalId} (${err instanceof Error ? err.message : String(err)}). ` +
160
+ `The person must share a server with the bot and allow direct messages from server members.`,
161
+ };
162
+ }
163
+ return { ok: true, facts: { surfaceKey: `discord/${spec.principalId}/${channelId}`, target: { channelId, channelType: "1" } } };
164
+ }
package/agent/register.ts CHANGED
@@ -54,7 +54,7 @@ import { PROVIDER_DOCS } from "./docs-generated.js";
54
54
  import { DISCORD_SURFACE_PARITY } from "./surface-parity.js";
55
55
  import { DISCORD_PROVIDER_EVENTS } from "./provider-events.js";
56
56
  import { registerDiscordOps } from "./ops.js";
57
- import { resolveDiscordTarget, discordReportTargetFor } from "./channels/discord-dispatch-target.js";
57
+ import { resolveDiscordTarget, discordReportTargetFor, resolveDiscordDeclaredTarget } from "./channels/discord-dispatch-target.js";
58
58
  import { bindDiscord } from "./channels/discord-identity.js";
59
59
  import { discordPostArm } from "./channels/discord-post-arm.js";
60
60
  import { discordWakeOriginCodec } from "./channels/discord-origin-codec.js";
@@ -103,7 +103,12 @@ export function registerDiscordProvider(): void {
103
103
  // has already run and its failure is swallowed. Without this registration a judge lift on a
104
104
  // Discord-bound domain leaves audit rows and nothing a person in the room can act on.
105
105
  registerJudgeLiftNoticePoster("discord", postDiscordJudgeLiftNotice);
106
- registerRoutineDispatch("discord", { resolveTarget: resolveDiscordTarget, reportTargetFor: discordReportTargetFor });
106
+ registerRoutineDispatch("discord", {
107
+ resolveTarget: resolveDiscordTarget,
108
+ reportTargetFor: discordReportTargetFor,
109
+ // OPE-2419 — a webhook target on Discord: a guild channel, or a person's DM opened at fire time.
110
+ resolveDeclaredTarget: (spec) => resolveDiscordDeclaredTarget(spec),
111
+ });
107
112
  // Native admin leg for the spend-cap bypass (spend/bypass.ts): a guild ADMINISTRATOR, REST-verified.
108
113
  registerNativeAdminCheck("discord", async (scopeKey, principalId) => {
109
114
  const guildId = guildIdFromScopeKey(scopeKey);
@@ -39,6 +39,10 @@ export const DISCORD_SURFACE_PARITY: SurfaceParityDeclaration = {
39
39
  // stamps `thread: true` at mint from the relay payload's channel type (OPE-1719), which is the
40
40
  // positive signal the guard needs. The DM leg keeps the raw divert: a DM has no threads.
41
41
  "owned-thread-ingress": { supported: "the guild leg only — a Discord DM carries no threads, so its divert never consults ownership" },
42
+ "thread-done-reaction": {
43
+ unsupported:
44
+ "a Discord thread is its own channel, and the message that started it lives in the PARENT channel, whose id the origin does not carry. Marking the thread's first message instead would put the mark on the wrong message",
45
+ },
42
46
 
43
47
  // wake legs and conversation shapes
44
48
  "addressed-leg": "supported",
@@ -51,9 +51,8 @@ export declare function parseDiscordApprovalCustomId(customId: string): {
51
51
  org: string;
52
52
  } | null;
53
53
  /**
54
- * The card for a still-pending action: summary + "requested by / expires" + Approve/Reject
55
- * buttons carrying the pending id + org in their custom_ids (see the file header for why
56
- * the org rides the custom_id on Discord). Mirrors card.ts's pendingCardBlocks.
54
+ * The card for a still-pending action: the plain sentence, the summary, "requested by / expires", and one button per layout option, each carrying the pending id + org in its custom_id (see the file header for why the org rides the custom_id on Discord). Mirrors card.ts's pendingCardBlocks.
55
+ * The OPTIONS come from core's card layout (OPE-53, OPE-2363: only a ROUTINE's card gets the middle grant button), so Slack, Teams and Discord can never disagree, and OPE-2456's extra option lands here with no change.
57
56
  */
58
57
  export declare function pendingDiscordCard(row: PendingActionRow, summaryLines: readonly string[]): DiscordCardMessage;
59
58
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"discord-card.d.ts","sourceRoot":"","sources":["../../../agent/approvals/discord-card.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAGnF;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,iBAAiB,CAAC;AAEzD,uHAAuH;AACvH,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAYzC;4FAC4F;AAC5F,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC;AAED,6FAA6F;AAC7F,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,SAAS,gBAAgB,EAAE,CAAC;CAClD;AAED;;;;;GAKG;AACH,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,QAAQ,GAAG,iBAAiB,GAAG,YAAY,CAAC;AAM9F;;;;;;;;;;GAUG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,gBAAgB,EAAE,QAAQ,EAAE,uBAAuB,GAAG,MAAM,CAQ7G;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,uBAAuB,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAcpI;AAQD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,kBAAkB,CA+B7G;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,kBAAkB,CAkB1I;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,kBAAkB,CAalI;AAKD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,OAAO,GAAG,OAAO,CAOlE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAGvF"}
1
+ {"version":3,"file":"discord-card.d.ts","sourceRoot":"","sources":["../../../agent/approvals/discord-card.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAkB,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAGnG;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,iBAAiB,CAAC;AAEzD,uHAAuH;AACvH,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAYzC;4FAC4F;AAC5F,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC;AAED,6FAA6F;AAC7F,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,SAAS,gBAAgB,EAAE,CAAC;CAClD;AAED;;;;;GAKG;AACH,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,QAAQ,GAAG,iBAAiB,GAAG,YAAY,CAAC;AAM9F;;;;;;;;;;GAUG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,gBAAgB,EAAE,QAAQ,EAAE,uBAAuB,GAAG,MAAM,CAQ7G;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,uBAAuB,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAcpI;AASD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,MAAM,EAAE,GAAG,kBAAkB,CAU7G;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,kBAAkB,CAkB1I;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,kBAAkB,CAalI;AAKD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,OAAO,GAAG,OAAO,CAOlE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAGvF"}
@@ -17,7 +17,7 @@
17
17
  // THREE renders live here, and only the first two are a card: the PENDING card (Approve/Reject), the
18
18
  // DECIDED card (no buttons), and the JUDGE-LIFT NOTICE (OPE-1103) — a review surface for an action
19
19
  // that already ran, carrying the one "This should not have run" button and never a decision one.
20
- import { getSettings, grantButtonLabel, pendingCardExpiryClause, sessionGrantKind } from "@open-everywhere/core/agent/adapter-api.js";
20
+ import { approvalLabel, pendingCardLayout, plainSummaryForRow } from "@open-everywhere/core/agent/adapter-api.js";
21
21
  /**
22
22
  * Every approval button custom_id this feature owns starts with this. Deliberately distinct
23
23
  * from BOTH the Slack action_id prefix ("oe_approval:" — a different wire format, keeping
@@ -83,46 +83,24 @@ export function parseDiscordApprovalCustomId(customId) {
83
83
  return null;
84
84
  return { id, decision: decision, org };
85
85
  }
86
- /** The shared card body: header + summary + provenance. `<@id>` is Discord's user-mention
87
- * syntax too, so the requester line reads identically to the Slack card. */
86
+ /** The shared card body: the plain sentence first (OPE-2434), then the `Approval requested: <kind>` label, the summary and the provenance. `<@id>` is Discord's user-mention syntax too, so the requester line reads identically to the Slack card. The sentence went through core's `plainValue`, so it carries no markdown of its own. */
88
87
  function cardBody(row, summaryLines, footer) {
89
- return [`**Approval requested: ${row.kind}**`, ...summaryLines, footer].join("\n");
88
+ return [`**${plainSummaryForRow(row)}**`, `-# ${approvalLabel(row.kind)}`, ...summaryLines, footer].join("\n");
90
89
  }
90
+ const OPTION_STYLE = { primary: BUTTON_STYLE_SUCCESS, danger: BUTTON_STYLE_DANGER, neutral: BUTTON_STYLE_SECONDARY };
91
91
  /**
92
- * The card for a still-pending action: summary + "requested by / expires" + Approve/Reject
93
- * buttons carrying the pending id + org in their custom_ids (see the file header for why
94
- * the org rides the custom_id on Discord). Mirrors card.ts's pendingCardBlocks.
92
+ * The card for a still-pending action: the plain sentence, the summary, "requested by / expires", and one button per layout option, each carrying the pending id + org in its custom_id (see the file header for why the org rides the custom_id on Discord). Mirrors card.ts's pendingCardBlocks.
93
+ * The OPTIONS come from core's card layout (OPE-53, OPE-2363: only a ROUTINE's card gets the middle grant button), so Slack, Teams and Discord can never disagree, and OPE-2456's extra option lands here with no change.
95
94
  */
96
95
  export function pendingDiscordCard(row, summaryLines) {
97
- const content = cardBody(row, summaryLines, `-# Requested by <@${row.requestedBy}> · ${pendingCardExpiryClause(row.expiresAt)}`);
98
- const buttons = [
99
- {
100
- type: COMPONENT_BUTTON,
101
- style: BUTTON_STYLE_SUCCESS,
102
- label: "Approve",
103
- custom_id: buildDiscordApprovalCustomId(row, "approve"),
104
- },
105
- ];
106
- // OPE-53, OPE-2363: only a ROUTINE's card gets the middle button — publish this one AND skip the card
107
- // for the same kind on later fires of THIS routine. sessionGrantKind() (core approvals/session-grant.ts)
108
- // decides the KIND (`pr.open` alone), and grantButtonLabel() decides the reach: undefined for every
109
- // card in a conversation, which offers Approve and Reject only. The label and the presence both come
110
- // from core, so Slack and Discord can never disagree.
111
- const grantLabel = sessionGrantKind(row) !== null ? grantButtonLabel(row, getSettings().approvals.routineGrantTtlHours) : undefined;
112
- if (grantLabel !== undefined) {
113
- buttons.push({
114
- type: COMPONENT_BUTTON,
115
- style: BUTTON_STYLE_SECONDARY,
116
- label: grantLabel,
117
- custom_id: buildDiscordApprovalCustomId(row, "approve_session"),
118
- });
119
- }
120
- buttons.push({
96
+ const layout = pendingCardLayout(row, summaryLines);
97
+ const content = cardBody(row, summaryLines, `-# Requested by <@${layout.requestedBy}> · ${layout.expiry}`);
98
+ const buttons = layout.options.map((o) => ({
121
99
  type: COMPONENT_BUTTON,
122
- style: BUTTON_STYLE_DANGER,
123
- label: "Reject",
124
- custom_id: buildDiscordApprovalCustomId(row, "reject"),
125
- });
100
+ style: OPTION_STYLE[o.tone],
101
+ label: o.label,
102
+ custom_id: buildDiscordApprovalCustomId(row, o.verb),
103
+ }));
126
104
  return { content, components: [{ type: COMPONENT_ACTION_ROW, components: buttons }] };
127
105
  }
128
106
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"discord-card.js","sourceRoot":"","sources":["../../../agent/approvals/discord-card.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"discord-card.js","sourceRoot":"","sources":["../../../agent/approvals/discord-card.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,10 @@ export interface DiscordChannelInfo {
4
4
  /** Present (with a thread type) when the channel is a THREAD — its parent channel id. */
5
5
  readonly parentId?: string;
6
6
  readonly isThread: boolean;
7
+ /** The guild the channel belongs to (`guild_id`), or undefined for a DM or a guild lookup.
8
+ * Read by the declared-target resolver (OPE-2419) to confirm a configured `discord/<guild>/<channel>`
9
+ * names a channel that really is in that guild. */
10
+ readonly guildId?: string;
7
11
  }
8
12
  /** Injectable seams (tests swap both; live callers pass nothing). */
9
13
  export interface DiscordInfoDeps {
@@ -1 +1 @@
1
- {"version":3,"file":"discord-channel-info.d.ts","sourceRoot":"","sources":["../../../agent/channels/discord-channel-info.ts"],"names":[],"mappings":"AAcA,iFAAiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,yFAAyF;IACzF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,qEAAqE;AACrE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AASD,0FAA0F;AAC1F,wBAAgB,4BAA4B,IAAI,IAAI,CAEnD;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,UAAU,GAAG,QAAQ,EAC3B,EAAE,EAAE,MAAM,EACV,IAAI,GAAE,eAAoB,GACzB,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CA4BzC;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,GAAE,eAAoB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAGtH"}
1
+ {"version":3,"file":"discord-channel-info.d.ts","sourceRoot":"","sources":["../../../agent/channels/discord-channel-info.ts"],"names":[],"mappings":"AAcA,iFAAiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,yFAAyF;IACzF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B;;wDAEoD;IACpD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,qEAAqE;AACrE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AASD,0FAA0F;AAC1F,wBAAgB,4BAA4B,IAAI,IAAI,CAEnD;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,UAAU,GAAG,QAAQ,EAC3B,EAAE,EAAE,MAAM,EACV,IAAI,GAAE,eAAoB,GACzB,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CA6BzC;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,GAAE,eAAoB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAGtH"}
@@ -43,7 +43,8 @@ export function fetchDiscordInfo(kind, id, deps = {}) {
43
43
  // A nameless channel resolves undefined wholesale — kept from the original
44
44
  // scope-label impl (its callers require the name), and harmless for the grain
45
45
  // consumer: every real guild channel/thread carries a name.
46
- return name ? { name, parentId, isThread } : undefined;
46
+ const guildId = kind === "channels" && typeof body.guild_id === "string" && body.guild_id ? body.guild_id : undefined;
47
+ return name ? { name, parentId, isThread, ...(guildId ? { guildId } : {}) } : undefined;
47
48
  }
48
49
  catch {
49
50
  return undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"discord-channel-info.js","sourceRoot":"","sources":["../../../agent/channels/discord-channel-info.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,oFAAoF;AACpF,uBAAuB;AACvB,kFAAkF;AAClF,sFAAsF;AACtF,qFAAqF;AACrF,qFAAqF;AACrF,EAAE;AACF,gFAAgF;AAChF,iFAAiF;AACjF,sFAAsF;AACtF,kFAAkF;AAClF,kFAAkF;AAgBlF,uFAAuF;AACvF,wFAAwF;AACxF,qFAAqF;AACrF,qFAAqF;AACrF,uBAAuB;AACvB,MAAM,SAAS,GAAG,IAAI,GAAG,EAAmD,CAAC;AAE7E,0FAA0F;AAC1F,MAAM,UAAU,4BAA4B;IAC1C,SAAS,CAAC,KAAK,EAAE,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,IAA2B,EAC3B,EAAU,EACV,IAAI,GAAoB,EAAE;IAE1B,MAAM,GAAG,GAAG,GAAG,IAAI,IAAI,EAAE,EAAE,CAAC;IAC5B,IAAI,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;YACd,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;YAC1D,IAAI,CAAC,KAAK;gBAAE,OAAO,SAAS,CAAC;YAC7B,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC;gBACtC,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,+BAA+B,IAAI,IAAI,EAAE,EAAE,EAAE;oBACrE,OAAO,EAAE,EAAE,aAAa,EAAE,OAAO,KAAK,EAAE,EAAE;iBAC3C,CAAC,CAAC;gBACH,IAAI,CAAC,GAAG,CAAC,EAAE;oBAAE,OAAO,SAAS,CAAC;gBAC9B,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA4D,CAAC;gBAC3F,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;gBAC9F,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,KAAK,EAAE,IAAI,IAAI,CAAC,IAAI,KAAK,EAAE,IAAI,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;gBAC1E,MAAM,QAAQ,GAAG,QAAQ,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;gBAC/G,2EAA2E;gBAC3E,8EAA8E;gBAC9E,4DAA4D;gBAC5D,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;YACzD,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,SAAS,CAAC;YACnB,CAAC;QACH,CAAC,CAAC,EAAE,CAAC;QACL,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,SAAiB,EAAE,IAAI,GAAoB,EAAE;IACvF,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,UAAU,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;IACjE,OAAO,IAAI,EAAE,QAAQ,CAAC;AACxB,CAAC"}
1
+ {"version":3,"file":"discord-channel-info.js","sourceRoot":"","sources":["../../../agent/channels/discord-channel-info.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { Routine, DispatchTarget } from "@open-everywhere/core/agent/adapter-api.js";
1
+ import type { Routine, DispatchTarget, DeclaredTargetResolution, DeclaredTargetSpec } from "@open-everywhere/core/agent/adapter-api.js";
2
2
  export declare function resolveDiscordTarget(r: Routine): DispatchTarget | null;
3
3
  /**
4
4
  * Derive a post-back target for a CONFIGURED Discord report channel ({@link Domain.reportChannel},
@@ -12,4 +12,26 @@ export declare function resolveDiscordTarget(r: Routine): DispatchTarget | null;
12
12
  * "channel" arm above — capped rather than spend-exempt, which is the safe direction.
13
13
  */
14
14
  export declare function discordReportTargetFor(surfaceKey: string): Readonly<Record<string, unknown>> | null;
15
+ /**
16
+ * Resolve a target that config NAMES (OPE-2419 — a webhook's `target`) into the facts
17
+ * {@link resolveDiscordTarget} reads.
18
+ *
19
+ * • A ROOM, `discord/<guildId>/<channelId>`: the third segment IS the channel id (the same
20
+ * round-trip {@link discordReportTargetFor} relies on). The guild id is stored too, which is the
21
+ * GUILD EVIDENCE resolveDiscordTarget needs before it mints a guild-shaped wake origin.
22
+ * 🔴 THE GUILD IS CHECKED, NOT TRUSTED. The bot token posts to a channel id in ANY guild, while the
23
+ * fire's partition, domain and memory come from the guild segment. A target that names guild A
24
+ * and a channel in guild B would bind guild A's domain in guild B's room. So the resolver reads
25
+ * the channel's own `guild_id` and refuses on a mismatch, and also when it cannot read it.
26
+ * • A PERSON, `dm:discord/<userId>`: `POST /users/@me/channels` opens (or reuses) the 1:1 DM, and
27
+ * the target is stored with channel type "1", so the fire binds as a DM. The surface key's
28
+ * workspace segment is the person's user id — what bindDiscord derives for a DM (`guildId ?? userId`).
29
+ *
30
+ * A DM Discord refuses (the person shares no server with the bot, or turned off DMs from server
31
+ * members) comes back as a refusal that says so. It is never a silent drop.
32
+ */
33
+ export declare function resolveDiscordDeclaredTarget(spec: DeclaredTargetSpec, deps?: {
34
+ readonly openDm?: (userId: string) => Promise<string>;
35
+ readonly channelGuild?: (channelId: string) => Promise<string | undefined>;
36
+ }): Promise<DeclaredTargetResolution>;
15
37
  //# sourceMappingURL=discord-dispatch-target.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"discord-dispatch-target.d.ts","sourceRoot":"","sources":["../../../agent/channels/discord-dispatch-target.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAkC,MAAM,4CAA4C,CAAC;AAW1H,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CA+DtE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAInG"}
1
+ {"version":3,"file":"discord-dispatch-target.d.ts","sourceRoot":"","sources":["../../../agent/channels/discord-dispatch-target.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,wBAAwB,EACxB,kBAAkB,EAGnB,MAAM,4CAA4C,CAAC;AAapD,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CA+DtE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,CAInG;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,kBAAkB,EACxB,IAAI,GAAE;IACJ,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACtD,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CACvE,GACL,OAAO,CAAC,wBAAwB,CAAC,CA0BnC"}
@@ -9,6 +9,8 @@
9
9
  // discordChannel().receive owns the send; NO Gateway websocket is involved (that transport is
10
10
  // inbound-ambient only). Target = `{ channelId }`.
11
11
  import discord from "./discord.js";
12
+ import { discordOpenDmChannel } from "./discord-post.js";
13
+ import { fetchDiscordInfo } from "./discord-channel-info.js";
12
14
  /** One stored-target field as a non-empty string, or undefined (never a coerced non-string — the
13
15
  * store row is data, validated on the way out, not trusted). Local copy of core dispatch.ts's
14
16
  * module-private `storedString`. */
@@ -96,4 +98,48 @@ export function discordReportTargetFor(surfaceKey) {
96
98
  const channelId = segs.length === 3 && segs[0] === "discord" ? segs[2] : undefined;
97
99
  return channelId && channelId.length > 0 ? { channelId } : null;
98
100
  }
101
+ /**
102
+ * Resolve a target that config NAMES (OPE-2419 — a webhook's `target`) into the facts
103
+ * {@link resolveDiscordTarget} reads.
104
+ *
105
+ * • A ROOM, `discord/<guildId>/<channelId>`: the third segment IS the channel id (the same
106
+ * round-trip {@link discordReportTargetFor} relies on). The guild id is stored too, which is the
107
+ * GUILD EVIDENCE resolveDiscordTarget needs before it mints a guild-shaped wake origin.
108
+ * 🔴 THE GUILD IS CHECKED, NOT TRUSTED. The bot token posts to a channel id in ANY guild, while the
109
+ * fire's partition, domain and memory come from the guild segment. A target that names guild A
110
+ * and a channel in guild B would bind guild A's domain in guild B's room. So the resolver reads
111
+ * the channel's own `guild_id` and refuses on a mismatch, and also when it cannot read it.
112
+ * • A PERSON, `dm:discord/<userId>`: `POST /users/@me/channels` opens (or reuses) the 1:1 DM, and
113
+ * the target is stored with channel type "1", so the fire binds as a DM. The surface key's
114
+ * workspace segment is the person's user id — what bindDiscord derives for a DM (`guildId ?? userId`).
115
+ *
116
+ * A DM Discord refuses (the person shares no server with the bot, or turned off DMs from server
117
+ * members) comes back as a refusal that says so. It is never a silent drop.
118
+ */
119
+ export async function resolveDiscordDeclaredTarget(spec, deps = {}) {
120
+ if (spec.kind === "room") {
121
+ const guildId = await (deps.channelGuild ?? (async (id) => (await fetchDiscordInfo("channels", id))?.guildId))(spec.surface).catch(() => undefined);
122
+ if (guildId !== spec.workspace) {
123
+ return {
124
+ ok: false,
125
+ reason: guildId === undefined
126
+ ? `Discord did not confirm that channel ${spec.surface} is in guild ${spec.workspace} (the channel lookup failed, or the bot cannot see the channel). Check DISCORD_BOT_TOKEN and that the bot is in that guild.`
127
+ : `channel ${spec.surface} is in guild ${guildId}, not guild ${spec.workspace}. Correct the target to "discord/${guildId}/${spec.surface}".`,
128
+ };
129
+ }
130
+ return { ok: true, facts: { surfaceKey: `discord/${spec.workspace}/${spec.surface}`, target: { channelId: spec.surface, guildId: spec.workspace } } };
131
+ }
132
+ let channelId;
133
+ try {
134
+ channelId = await (deps.openDm ?? discordOpenDmChannel)(spec.principalId);
135
+ }
136
+ catch (err) {
137
+ return {
138
+ ok: false,
139
+ reason: `Discord could not open a DM with user ${spec.principalId} (${err instanceof Error ? err.message : String(err)}). ` +
140
+ `The person must share a server with the bot and allow direct messages from server members.`,
141
+ };
142
+ }
143
+ return { ok: true, facts: { surfaceKey: `discord/${spec.principalId}/${channelId}`, target: { channelId, channelType: "1" } } };
144
+ }
99
145
  //# sourceMappingURL=discord-dispatch-target.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"discord-dispatch-target.js","sourceRoot":"","sources":["../../../agent/channels/discord-dispatch-target.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"discord-dispatch-target.js","sourceRoot":"","sources":["../../../agent/channels/discord-dispatch-target.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../agent/register.ts"],"names":[],"mappings":"AAsEA;;8DAE8D;AAC9D,wBAAgB,uBAAuB,IAAI,IAAI,CAyE9C"}
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../agent/register.ts"],"names":[],"mappings":"AAsEA;;8DAE8D;AAC9D,wBAAgB,uBAAuB,IAAI,IAAI,CA8E9C"}
@@ -32,7 +32,7 @@ import { PROVIDER_DOCS } from "./docs-generated.js";
32
32
  import { DISCORD_SURFACE_PARITY } from "./surface-parity.js";
33
33
  import { DISCORD_PROVIDER_EVENTS } from "./provider-events.js";
34
34
  import { registerDiscordOps } from "./ops.js";
35
- import { resolveDiscordTarget, discordReportTargetFor } from "./channels/discord-dispatch-target.js";
35
+ import { resolveDiscordTarget, discordReportTargetFor, resolveDiscordDeclaredTarget } from "./channels/discord-dispatch-target.js";
36
36
  import { bindDiscord } from "./channels/discord-identity.js";
37
37
  import { discordPostArm } from "./channels/discord-post-arm.js";
38
38
  import { discordWakeOriginCodec } from "./channels/discord-origin-codec.js";
@@ -80,7 +80,12 @@ export function registerDiscordProvider() {
80
80
  // has already run and its failure is swallowed. Without this registration a judge lift on a
81
81
  // Discord-bound domain leaves audit rows and nothing a person in the room can act on.
82
82
  registerJudgeLiftNoticePoster("discord", postDiscordJudgeLiftNotice);
83
- registerRoutineDispatch("discord", { resolveTarget: resolveDiscordTarget, reportTargetFor: discordReportTargetFor });
83
+ registerRoutineDispatch("discord", {
84
+ resolveTarget: resolveDiscordTarget,
85
+ reportTargetFor: discordReportTargetFor,
86
+ // OPE-2419 — a webhook target on Discord: a guild channel, or a person's DM opened at fire time.
87
+ resolveDeclaredTarget: (spec) => resolveDiscordDeclaredTarget(spec),
88
+ });
84
89
  // Native admin leg for the spend-cap bypass (spend/bypass.ts): a guild ADMINISTRATOR, REST-verified.
85
90
  registerNativeAdminCheck("discord", async (scopeKey, principalId) => {
86
91
  const guildId = guildIdFromScopeKey(scopeKey);
@@ -1 +1 @@
1
- {"version":3,"file":"register.js","sourceRoot":"","sources":["../../agent/register.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"register.js","sourceRoot":"","sources":["../../agent/register.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"surface-parity.d.ts","sourceRoot":"","sources":["../../agent/surface-parity.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4CAA4C,CAAC;AAE3F,eAAO,MAAM,sBAAsB,EAAE,wBA+EpC,CAAC"}
1
+ {"version":3,"file":"surface-parity.d.ts","sourceRoot":"","sources":["../../agent/surface-parity.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4CAA4C,CAAC;AAE3F,eAAO,MAAM,sBAAsB,EAAE,wBAmFpC,CAAC"}
@@ -35,6 +35,9 @@ export const DISCORD_SURFACE_PARITY = {
35
35
  // stamps `thread: true` at mint from the relay payload's channel type (OPE-1719), which is the
36
36
  // positive signal the guard needs. The DM leg keeps the raw divert: a DM has no threads.
37
37
  "owned-thread-ingress": { supported: "the guild leg only — a Discord DM carries no threads, so its divert never consults ownership" },
38
+ "thread-done-reaction": {
39
+ unsupported: "a Discord thread is its own channel, and the message that started it lives in the PARENT channel, whose id the origin does not carry. Marking the thread's first message instead would put the mark on the wrong message",
40
+ },
38
41
  // wake legs and conversation shapes
39
42
  "addressed-leg": "supported",
40
43
  "ambient-leg": "supported",
@@ -1 +1 @@
1
- {"version":3,"file":"surface-parity.js","sourceRoot":"","sources":["../../agent/surface-parity.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,wFAAwF;AACxF,EAAE;AACF,yFAAyF;AACzF,iGAAiG;AACjG,uFAAuF;AAIvF,MAAM,CAAC,MAAM,sBAAsB,GAA6B;IAC9D,4BAA4B;IAC5B,iBAAiB,EAAE,WAAW;IAC9B,qBAAqB,EAAE,WAAW;IAClC,UAAU,EAAE,WAAW;IACvB,mBAAmB,EAAE,WAAW;IAChC,gGAAgG;IAChG,iGAAiG;IACjG,iFAAiF;IACjF,wBAAwB,EAAE,WAAW;IACrC,cAAc,EAAE,WAAW;IAC3B,0BAA0B,EAAE,WAAW;IACvC,eAAe,EAAE,WAAW;IAC5B,+FAA+F;IAC/F,8FAA8F;IAC9F,+CAA+C;IAC/C,yBAAyB,EAAE,EAAE,SAAS,EAAE,sEAAsE,EAAE;IAChH,iGAAiG;IACjG,iGAAiG;IACjG,kGAAkG;IAClG,6EAA6E;IAC7E,iGAAiG;IACjG,uFAAuF;IACvF,uBAAuB,EAAE;QACvB,WAAW,EACT,yMAAyM;KAC5M;IACD,8FAA8F;IAC9F,4FAA4F;IAC5F,+FAA+F;IAC/F,yFAAyF;IACzF,sBAAsB,EAAE,EAAE,SAAS,EAAE,8FAA8F,EAAE;IAErI,oCAAoC;IACpC,eAAe,EAAE,WAAW;IAC5B,aAAa,EAAE,WAAW;IAC1B,iGAAiG;IACjG,mGAAmG;IACnG,oGAAoG;IACpG,eAAe,EAAE,WAAW;IAC5B,+FAA+F;IAC/F,qEAAqE;IACrE,wBAAwB,EAAE,WAAW;IACrC,iGAAiG;IACjG,wEAAwE;IACxE,eAAe,EAAE,WAAW;IAC5B,iGAAiG;IACjG,iGAAiG;IACjG,4BAA4B;IAC5B,mBAAmB,EAAE,WAAW;IAChC,mBAAmB,EAAE,WAAW;IAChC,qFAAqF;IACrF,iGAAiG;IACjG,iGAAiG;IACjG,gEAAgE;IAChE,mBAAmB,EAAE,EAAE,WAAW,EAAE,2GAA2G,EAAE;IACjJ,sFAAsF;IACtF,OAAO,EAAE,EAAE,SAAS,EAAE,8BAA8B,EAAE;IACtD,KAAK,EAAE,WAAW;IAClB,EAAE,EAAE,WAAW;IACf,IAAI,EAAE,WAAW;IAEjB,oBAAoB;IACpB,KAAK,EAAE,WAAW;IAClB,UAAU,EAAE,WAAW;IACvB,8FAA8F;IAC9F,KAAK,EAAE,WAAW;IAClB,QAAQ,EAAE,WAAW;IACrB,YAAY,EAAE,WAAW;IACzB,WAAW,EAAE,WAAW;IACxB,MAAM,EAAE,EAAE,WAAW,EAAE,8EAA8E,EAAE;IACvG,YAAY,EAAE,WAAW;IACzB,SAAS,EAAE,WAAW;IACtB,eAAe,EAAE,WAAW;IAC5B,IAAI,EAAE,EAAE,SAAS,EAAE,yCAAyC,EAAE;IAC9D,cAAc,EAAE,WAAW;IAC3B,eAAe,EAAE,WAAW;IAC5B,UAAU,EAAE,EAAE,SAAS,EAAE,yEAAyE,EAAE;IACpG,SAAS,EAAE,WAAW;CACvB,CAAC"}
1
+ {"version":3,"file":"surface-parity.js","sourceRoot":"","sources":["../../agent/surface-parity.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,wFAAwF;AACxF,EAAE;AACF,yFAAyF;AACzF,iGAAiG;AACjG,uFAAuF;AAIvF,MAAM,CAAC,MAAM,sBAAsB,GAA6B;IAC9D,4BAA4B;IAC5B,iBAAiB,EAAE,WAAW;IAC9B,qBAAqB,EAAE,WAAW;IAClC,UAAU,EAAE,WAAW;IACvB,mBAAmB,EAAE,WAAW;IAChC,gGAAgG;IAChG,iGAAiG;IACjG,iFAAiF;IACjF,wBAAwB,EAAE,WAAW;IACrC,cAAc,EAAE,WAAW;IAC3B,0BAA0B,EAAE,WAAW;IACvC,eAAe,EAAE,WAAW;IAC5B,+FAA+F;IAC/F,8FAA8F;IAC9F,+CAA+C;IAC/C,yBAAyB,EAAE,EAAE,SAAS,EAAE,sEAAsE,EAAE;IAChH,iGAAiG;IACjG,iGAAiG;IACjG,kGAAkG;IAClG,6EAA6E;IAC7E,iGAAiG;IACjG,uFAAuF;IACvF,uBAAuB,EAAE;QACvB,WAAW,EACT,yMAAyM;KAC5M;IACD,8FAA8F;IAC9F,4FAA4F;IAC5F,+FAA+F;IAC/F,yFAAyF;IACzF,sBAAsB,EAAE,EAAE,SAAS,EAAE,8FAA8F,EAAE;IACrI,sBAAsB,EAAE;QACtB,WAAW,EACT,0NAA0N;KAC7N;IAED,oCAAoC;IACpC,eAAe,EAAE,WAAW;IAC5B,aAAa,EAAE,WAAW;IAC1B,iGAAiG;IACjG,mGAAmG;IACnG,oGAAoG;IACpG,eAAe,EAAE,WAAW;IAC5B,+FAA+F;IAC/F,qEAAqE;IACrE,wBAAwB,EAAE,WAAW;IACrC,iGAAiG;IACjG,wEAAwE;IACxE,eAAe,EAAE,WAAW;IAC5B,iGAAiG;IACjG,iGAAiG;IACjG,4BAA4B;IAC5B,mBAAmB,EAAE,WAAW;IAChC,mBAAmB,EAAE,WAAW;IAChC,qFAAqF;IACrF,iGAAiG;IACjG,iGAAiG;IACjG,gEAAgE;IAChE,mBAAmB,EAAE,EAAE,WAAW,EAAE,2GAA2G,EAAE;IACjJ,sFAAsF;IACtF,OAAO,EAAE,EAAE,SAAS,EAAE,8BAA8B,EAAE;IACtD,KAAK,EAAE,WAAW;IAClB,EAAE,EAAE,WAAW;IACf,IAAI,EAAE,WAAW;IAEjB,oBAAoB;IACpB,KAAK,EAAE,WAAW;IAClB,UAAU,EAAE,WAAW;IACvB,8FAA8F;IAC9F,KAAK,EAAE,WAAW;IAClB,QAAQ,EAAE,WAAW;IACrB,YAAY,EAAE,WAAW;IACzB,WAAW,EAAE,WAAW;IACxB,MAAM,EAAE,EAAE,WAAW,EAAE,8EAA8E,EAAE;IACvG,YAAY,EAAE,WAAW;IACzB,SAAS,EAAE,WAAW;IACtB,eAAe,EAAE,WAAW;IAC5B,IAAI,EAAE,EAAE,SAAS,EAAE,yCAAyC,EAAE;IAC9D,cAAc,EAAE,WAAW;IAC3B,eAAe,EAAE,WAAW;IAC5B,UAAU,EAAE,EAAE,SAAS,EAAE,yEAAyE,EAAE;IACpG,SAAS,EAAE,WAAW;CACvB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-everywhere/provider-discord",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Open Everywhere Discord provider — Discord channel and DM surfaces, gateway wiring, identity mapping and approval cards.",
5
5
  "keywords": [
6
6
  "agent",
@@ -168,7 +168,7 @@
168
168
  "test": "echo 'provider-discord: its test suite runs at the APP TIER (root scripts/) because the discord surface composes deeply with core internals (a custom gateway, admin approval cards, guild-admin bypass, routine dispatch) — see the root test chain (test:discord*, test:scenarios*, test:oe-commands, and the read/dispatch/audit/wake integration tests). This keeps the core adapter-api barrel a small port surface rather than exporting test-only internals.'"
169
169
  },
170
170
  "dependencies": {
171
- "@open-everywhere/core": "0.4.0",
171
+ "@open-everywhere/core": "0.5.0",
172
172
  "zod": "4.5.4"
173
173
  },
174
174
  "peerDependencies": {