@open-everywhere/provider-discord 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent/approvals/discord-card.ts +16 -36
- package/agent/channels/discord-channel-info.ts +7 -2
- package/agent/channels/discord-dispatch-target.ts +62 -1
- package/agent/register.ts +7 -2
- package/agent/surface-parity.ts +4 -0
- package/dist/agent/approvals/discord-card.d.ts +2 -3
- package/dist/agent/approvals/discord-card.d.ts.map +1 -1
- package/dist/agent/approvals/discord-card.js +14 -35
- package/dist/agent/approvals/discord-card.js.map +1 -1
- package/dist/agent/channels/discord-channel-info.d.ts +4 -0
- package/dist/agent/channels/discord-channel-info.d.ts.map +1 -1
- package/dist/agent/channels/discord-channel-info.js +2 -1
- package/dist/agent/channels/discord-channel-info.js.map +1 -1
- package/dist/agent/channels/discord-dispatch-target.d.ts +23 -1
- package/dist/agent/channels/discord-dispatch-target.d.ts.map +1 -1
- package/dist/agent/channels/discord-dispatch-target.js +46 -0
- package/dist/agent/channels/discord-dispatch-target.js.map +1 -1
- package/dist/agent/register.d.ts.map +1 -1
- package/dist/agent/register.js +7 -2
- package/dist/agent/register.js.map +1 -1
- package/dist/agent/surface-parity.d.ts.map +1 -1
- package/dist/agent/surface-parity.js +3 -0
- package/dist/agent/surface-parity.js.map +1 -1
- package/package.json +2 -2
|
@@ -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 {
|
|
21
|
+
import type { CardOptionTone, PendingActionRow } from "@open-everywhere/core/agent/adapter-api.js";
|
|
22
|
+
import { approvalLabel, optionsWithoutConfirm, 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,27 @@ 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:
|
|
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 [
|
|
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
|
|
122
|
-
*
|
|
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. OPE-2456's rule option needs a confirmation before the click, which a Discord button cannot show, so it is left out.
|
|
124
124
|
*/
|
|
125
125
|
export function pendingDiscordCard(row: PendingActionRow, summaryLines: readonly string[]): DiscordCardMessage {
|
|
126
|
-
const
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
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
|
+
// A Discord button sends its click with no confirmation first, so the rule option (OPE-2456) is left out here.
|
|
129
|
+
const buttons: DiscordComponent[] = optionsWithoutConfirm(layout.options).map((o) => ({
|
|
150
130
|
type: COMPONENT_BUTTON,
|
|
151
|
-
style:
|
|
152
|
-
label:
|
|
153
|
-
custom_id: buildDiscordApprovalCustomId(row,
|
|
154
|
-
});
|
|
131
|
+
style: OPTION_STYLE[o.tone],
|
|
132
|
+
label: o.label,
|
|
133
|
+
custom_id: buildDiscordApprovalCustomId(row, o.verb),
|
|
134
|
+
}));
|
|
155
135
|
return { content, components: [{ type: COMPONENT_ACTION_ROW, components: buttons }] };
|
|
156
136
|
}
|
|
157
137
|
|
|
@@ -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
|
-
|
|
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 {
|
|
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", {
|
|
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);
|
package/agent/surface-parity.ts
CHANGED
|
@@ -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
|
|
55
|
-
*
|
|
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. OPE-2456's rule option needs a confirmation before the click, which a Discord button cannot show, so it is left out.
|
|
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,
|
|
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,CAW7G;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 {
|
|
20
|
+
import { approvalLabel, optionsWithoutConfirm, 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,25 @@ export function parseDiscordApprovalCustomId(customId) {
|
|
|
83
83
|
return null;
|
|
84
84
|
return { id, decision: decision, org };
|
|
85
85
|
}
|
|
86
|
-
/** The shared card body:
|
|
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 [
|
|
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
|
|
93
|
-
*
|
|
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. OPE-2456's rule option needs a confirmation before the click, which a Discord button cannot show, so it is left out.
|
|
95
94
|
*/
|
|
96
95
|
export function pendingDiscordCard(row, summaryLines) {
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
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
|
+
// A Discord button sends its click with no confirmation first, so the rule option (OPE-2456) is left out here.
|
|
99
|
+
const buttons = optionsWithoutConfirm(layout.options).map((o) => ({
|
|
121
100
|
type: COMPONENT_BUTTON,
|
|
122
|
-
style:
|
|
123
|
-
label:
|
|
124
|
-
custom_id: buildDiscordApprovalCustomId(row,
|
|
125
|
-
});
|
|
101
|
+
style: OPTION_STYLE[o.tone],
|
|
102
|
+
label: o.label,
|
|
103
|
+
custom_id: buildDiscordApprovalCustomId(row, o.verb),
|
|
104
|
+
}));
|
|
126
105
|
return { content, components: [{ type: COMPONENT_ACTION_ROW, components: buttons }] };
|
|
127
106
|
}
|
|
128
107
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discord-card.js","sourceRoot":"","sources":["../../../agent/approvals/discord-card.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,uFAAuF;AACvF,yFAAyF;AACzF,yFAAyF;AACzF,4EAA4E;AAC5E,EAAE;AACF,0FAA0F;AAC1F,2FAA2F;AAC3F,kFAAkF;AAClF,8CAA8C;AAC9C,2FAA2F;AAC3F,2FAA2F;AAC3F,wFAAwF;AACxF,yFAAyF;AACzF,+EAA+E;AAC/E,EAAE;AACF,qGAAqG;AACrG,mGAAmG;AACnG,iGAAiG;AAGjG,OAAO,EAAE,
|
|
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;
|
|
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
|
-
|
|
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;
|
|
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;AAoBlF,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,CAAgF,CAAC;gBAC/G,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,MAAM,OAAO,GAAG,IAAI,KAAK,UAAU,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;gBACtH,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;YAC1F,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,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,
|
|
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":"AAAA,0FAA0F;AAC1F,iGAAiG;AACjG,mGAAmG;AACnG,wFAAwF;AACxF,iGAAiG;AACjG,uCAAuC;AACvC,EAAE;AACF,+FAA+F;AAC/F,8FAA8F;AAC9F,mDAAmD;
|
|
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,
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../agent/register.ts"],"names":[],"mappings":"AAsEA;;8DAE8D;AAC9D,wBAAgB,uBAAuB,IAAI,IAAI,CA8E9C"}
|
package/dist/agent/register.js
CHANGED
|
@@ -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", {
|
|
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":"AAAA,sGAAsG;AACtG,kGAAkG;AAClG,gGAAgG;AAChG,mGAAmG;AACnG,oGAAoG;AACpG,gGAAgG;AAChG,iGAAiG;AACjG,EAAE;AACF,0BAA0B;AAC1B,qGAAqG;AACrG,mGAAmG;AACnG,gFAAgF;AAChF,wGAAwG;AACxG,sEAAsE;AACtE,8FAA8F;AAC9F,+FAA+F;AAC/F,uFAAuF;AACvF,kFAAkF;AAClF,4FAA4F;AAC5F,yFAAyF;AACzF,4GAA4G;AAC5G,mGAAmG;AACnG,8FAA8F;AAC9F,qFAAqF;AACrF,sGAAsG;AACtG,iGAAiG;AACjG,yGAAyG;AACzG,sGAAsG;AACtG,oGAAoG;AAEpG,OAAO,EACL,sBAAsB,EACtB,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,EACtB,eAAe,EACf,uBAAuB,EACvB,mBAAmB,EACnB,mBAAmB,EACnB,uBAAuB,EACvB,yBAAyB,EACzB,kBAAkB,EAClB,6BAA6B,EAC7B,uBAAuB,EACvB,wBAAwB,EACxB,yBAAyB,EACzB,0BAA0B,EAC1B,sBAAsB,EACtB,wBAAwB,EACxB,oBAAoB,EACpB,gCAAgC,GACjC,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,uCAAuC,CAAC;
|
|
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,
|
|
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;
|
|
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.
|
|
3
|
+
"version": "0.6.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.
|
|
171
|
+
"@open-everywhere/core": "0.6.0",
|
|
172
172
|
"zod": "4.5.4"
|
|
173
173
|
},
|
|
174
174
|
"peerDependencies": {
|