refactor(extensions): burn down Discord and Slack dead exports (#106017)

* refactor(discord): trim dead internal exports

* refactor(slack): trim dead internal exports

* test(discord): cover inherited streaming migration

* chore(tooling): ratchet reduced extension LOC

* refactor(discord): remove stale approval helper import

* chore(deadcode): remove reintroduced transcript baseline
This commit is contained in:
Peter Steinberger
2026-07-12 22:58:22 -07:00
committed by GitHub
parent ce990a3ad9
commit 4349687b9d
66 changed files with 97 additions and 440 deletions
@@ -8,8 +8,6 @@ import {
createDiscordMessagingActionContext,
type DiscordMessagingActionOptions,
} from "./runtime.messaging.shared.js";
export { discordMessagingActionRuntime } from "./runtime.messaging.runtime.js";
export async function handleDiscordMessagingAction(
action: string,
params: Record<string, unknown>,
@@ -8,10 +8,8 @@ import { clearPresences, setPresence } from "../monitor/presence-cache.js";
import { DiscordThreadInitialMessageError } from "../send.js";
import { discordGuildActionRuntime, handleDiscordGuildAction } from "./runtime.guild.js";
import { handleDiscordAction } from "./runtime.js";
import {
discordMessagingActionRuntime,
handleDiscordMessagingAction,
} from "./runtime.messaging.js";
import { handleDiscordMessagingAction } from "./runtime.messaging.js";
import { discordMessagingActionRuntime } from "./runtime.messaging.runtime.js";
import {
discordModerationActionRuntime,
handleDiscordModerationAction,
+1 -1
View File
@@ -3,7 +3,7 @@ import { buildApprovalResolutionRef } from "openclaw/plugin-sdk/approval-referen
import type { MessagePresentationAction } from "openclaw/plugin-sdk/interactive-runtime";
import type { ComponentData } from "./internal/discord.js";
export type DiscordApprovalAction = Extract<MessagePresentationAction, { type: "approval" }>;
type DiscordApprovalAction = Extract<MessagePresentationAction, { type: "approval" }>;
const DISCORD_APPROVAL_CUSTOM_ID_MAX_CHARS = 100;
@@ -7,8 +7,8 @@ import { describe, expect, it } from "vitest";
import {
createDiscordNativeApprovalAdapter,
getDiscordApprovalCapability,
shouldHandleDiscordApprovalRequest,
} from "./approval-native.js";
import { shouldHandleDiscordApprovalRequest } from "./approval-shared.js";
const STORE_PATH = path.join(os.tmpdir(), "openclaw-discord-approval-native-test.json");
const NATIVE_APPROVAL_CFG = {
-11
View File
@@ -8,7 +8,6 @@ import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalString,
} from "openclaw/plugin-sdk/string-coerce-runtime";
export { shouldHandleDiscordApprovalRequest } from "./approval-shared.js";
import { listDiscordAccountIds, resolveDiscordAccount } from "./accounts.js";
import {
createChannelApproverDmTargetResolver,
@@ -23,16 +22,6 @@ import {
isDiscordExecApprovalClientEnabled,
} from "./exec-approvals.js";
// Legacy export kept for monitor test/support surfaces; native routing now uses
// the shared session-conversation fallback helper instead.
export function extractDiscordChannelId(sessionKey?: string | null): string | null {
if (!sessionKey) {
return null;
}
const match = sessionKey.match(/discord:(?:channel|group):(\d+)/);
return match?.[1] ?? null;
}
function extractDiscordSessionKind(sessionKey?: string | null): "channel" | "group" | "dm" | null {
if (!sessionKey) {
return null;
+8
View File
@@ -64,11 +64,18 @@ describe("discord doctor", () => {
work: {
streaming: {
mode: "off",
chunkMode: "newline",
block: {
enabled: true,
coalesce: {
idleMs: 250,
},
},
preview: {
chunk: {
minChars: 120,
},
},
},
},
},
@@ -80,6 +87,7 @@ describe("discord doctor", () => {
"Moved channels.discord.draftChunk → channels.discord.streaming.preview.chunk.",
"Moved channels.discord.accounts.work.streaming (boolean) → channels.discord.accounts.work.streaming.mode (off).",
"Moved channels.discord.accounts.work.blockStreamingCoalesce → channels.discord.accounts.work.streaming.block.coalesce.",
"Copied flat channels.discord delivery keys into channels.discord.accounts.work.streaming to keep inherited settings while migrating flat streaming keys.",
]);
});
@@ -25,15 +25,6 @@ export async function editWebhookMessage(
: await rest.patch(Routes.webhookMessage(applicationId, token, messageId), data);
}
export async function deleteWebhookMessage(
rest: RequestClient,
applicationId: string,
token: string,
messageId: string,
): Promise<unknown> {
return await rest.delete(Routes.webhookMessage(applicationId, token, messageId));
}
export async function getWebhookMessage(
rest: RequestClient,
applicationId: string,
+1 -11
View File
@@ -13,7 +13,6 @@ import {
createUserDmChannel,
deleteChannelMessage,
deleteOwnMessageReaction,
deleteWebhookMessage,
editApplicationCommand,
editWebhookMessage,
getCurrentUser,
@@ -207,13 +206,7 @@ describe("Discord REST API helpers", () => {
});
it("routes interaction webhook helpers through the typed REST client", async () => {
const rest = createFakeRestClient([
{ ok: true },
{ id: "m1" },
{ id: "m2" },
{ id: "m3" },
undefined,
]);
const rest = createFakeRestClient([{ ok: true }, { id: "m1" }, { id: "m2" }, { id: "m3" }]);
const query = { wait: "true" };
await expect(createInteractionCallback(rest, "i1", "itoken", { type: 5 })).resolves.toEqual({
@@ -226,8 +219,6 @@ describe("Discord REST API helpers", () => {
await expect(
editWebhookMessage(rest, "app1", "wtoken", "m1", { body: { content: "updated" } }),
).resolves.toEqual({ id: "m3" });
await deleteWebhookMessage(rest, "app1", "wtoken", "m1");
expect(rest.calls).toEqual([
{
method: "POST",
@@ -246,7 +237,6 @@ describe("Discord REST API helpers", () => {
path: Routes.webhookMessage("app1", "wtoken", "m1"),
data: { body: { content: "updated" } },
},
{ method: "DELETE", path: Routes.webhookMessage("app1", "wtoken", "m1") },
]);
});
-1
View File
@@ -32,7 +32,6 @@ export {
export {
createInteractionCallback,
createWebhookMessage,
deleteWebhookMessage,
editWebhookMessage,
getWebhookMessage,
} from "./api.interactions.js";
+2 -2
View File
@@ -10,7 +10,7 @@ import { dispatchInteraction } from "./interaction-dispatch.js";
import { RequestClient, type RequestClientOptions } from "./rest.js";
import type { Guild, GuildMember, Message, User } from "./structures.js";
export interface Route {
interface Route {
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
path: `/${string}`;
handler(req: Request, ctx?: Context): Response | Promise<Response>;
@@ -18,7 +18,7 @@ export interface Route {
disabled?: boolean;
}
export interface Context {
interface Context {
waitUntil?(promise: Promise<unknown>): void;
env?: unknown;
}
@@ -451,4 +451,3 @@ function stableCommandSetHash(commands: SerializedCommand[]): string {
);
return createHash("sha256").update(JSON.stringify(stable)).digest("hex");
}
export { testing as __testing };
+1 -1
View File
@@ -9,7 +9,7 @@ import type { BaseMessageInteractiveComponent } from "./components.js";
import type { AutocompleteInteraction, CommandInteraction } from "./interactions.js";
type ConditionalCommandOption = (interaction: unknown) => boolean;
export type CommandOption = Record<string, unknown> & {
type CommandOption = Record<string, unknown> & {
name: string;
description?: string;
type: ApplicationCommandOptionType;
@@ -11,7 +11,7 @@ export type ComponentData<
> = {
[K in T]: ComponentParserResult["data"][K];
};
export type ConditionalComponentOption = (interaction: BaseComponentInteraction) => boolean;
type ConditionalComponentOption = (interaction: BaseComponentInteraction) => boolean;
export function parseCustomId(id: string): ComponentParserResult {
const [rawKeyValue, ...parts] = id.split(";");
@@ -1,17 +1,11 @@
// Discord plugin module implements components behavior.
export {
BaseComponent,
BaseMessageInteractiveComponent,
BaseModalComponent,
clean,
colorToNumber,
parseCustomId,
type ComponentData,
type ComponentParserResult,
type ConditionalComponentOption,
} from "./components.base.js";
export {
AnySelectMenu,
Button,
ChannelSelectMenu,
Container,
@@ -37,9 +37,6 @@ import {
type StructureClient,
} from "./structures.js";
export { OptionsHandler } from "./interaction-options.js";
export { ModalFields } from "./modal-fields.js";
type InteractionClient = StructureClient & {
options: { clientId: string };
componentHandler: {
+1 -1
View File
@@ -26,7 +26,7 @@ import { isDiscordRateLimitBody } from "./schemas.js";
export { DiscordError, isUnknownDiscordVoiceStateError, RateLimitError } from "./rest-errors.js";
type RuntimeProfile = "serverless" | "persistent";
export type RequestPriority = RestRequestPriority;
type RequestPriority = RestRequestPriority;
type RequestSchedulerOptions = {
lanes?: Partial<
Record<RequestPriority, { maxQueueSize?: number; staleAfterMs?: number; weight?: number }>
@@ -2,12 +2,7 @@
export const AGENT_BUTTON_KEY = "agent";
export const AGENT_SELECT_KEY = "agentsel";
export {
ackComponentInteraction,
resolveAgentComponentRoute,
resolveComponentInteractionContext,
resolveDiscordChannelContext,
} from "./agent-components-context.js";
export { ackComponentInteraction, resolveAgentComponentRoute } from "./agent-components-context.js";
export {
ensureAgentComponentInteractionAllowed,
ensureComponentUserAllowed,
@@ -114,7 +114,7 @@ async function runAgentSystemControlInteraction(params: AgentSystemControlParams
});
}
export class AgentComponentButton extends Button {
class AgentComponentButton extends Button {
override label = AGENT_BUTTON_KEY;
customId = `${AGENT_BUTTON_KEY}:seed=1`;
override style = ButtonStyle.Primary;
@@ -142,7 +142,7 @@ export class AgentComponentButton extends Button {
}
}
export class AgentSelectMenu extends StringSelectMenu {
class AgentSelectMenu extends StringSelectMenu {
customId = `${AGENT_SELECT_KEY}:seed=1`;
options: APIStringSelectComponent["options"] = [];
private ctx: AgentComponentContext;
@@ -17,14 +17,6 @@ import {
type DiscordComponentControlHandlers,
} from "./agent-components.wildcard-controls.js";
export { resolveDiscordComponentOriginatingTo } from "./agent-components.dispatch.js";
export {
AgentComponentButton,
AgentSelectMenu,
createAgentComponentButton,
createAgentSelectMenu,
} from "./agent-components.system-controls.js";
type ComponentFactory = (ctx: AgentComponentContext) => BaseMessageInteractiveComponent;
function bindDiscordComponentControl<T extends BaseMessageInteractiveComponent>(
@@ -2,6 +2,7 @@
import type { ApprovalResolveResult } from "openclaw/plugin-sdk/approval-gateway-runtime";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { buildExecApprovalCustomId, parseExecApprovalData } from "../approval-custom-id.js";
import { parseCustomId, type ButtonInteraction, type ComponentData } from "../internal/discord.js";
const resolveApprovalOverGatewayMock = vi.hoisted(() => vi.fn());
@@ -15,13 +16,7 @@ vi.mock("openclaw/plugin-sdk/approval-gateway-runtime", async (importOriginal) =
};
});
import {
ExecApprovalButton,
buildExecApprovalCustomId,
createDiscordExecApprovalButtonContext,
extractDiscordChannelId,
parseExecApprovalData,
} from "./exec-approvals.js";
import { ExecApprovalButton, createDiscordExecApprovalButtonContext } from "./exec-approvals.js";
function buildConfig(
execApprovals?: NonNullable<NonNullable<OpenClawConfig["channels"]>["discord"]>["execApprovals"],
@@ -93,13 +88,6 @@ describe("discord exec approval monitor helpers", () => {
expect(parseExecApprovalData({ kind: "plugin", action: "deny" } as ComponentData)).toBeNull();
});
it("extracts discord channel ids from session keys", () => {
expect(extractDiscordChannelId("agent:main:discord:channel:123456789")).toBe("123456789");
expect(extractDiscordChannelId("agent:main:discord:group:222333444")).toBe("222333444");
expect(extractDiscordChannelId("agent:main:telegram:channel:123456789")).toBeNull();
expect(extractDiscordChannelId("")).toBeNull();
});
it("rejects invalid approval button payloads", async () => {
const interaction = createInteraction();
const button = new ExecApprovalButton({
@@ -10,7 +10,7 @@ import type {
DiscordExecApprovalConfig,
OpenClawConfig,
} from "openclaw/plugin-sdk/config-contracts";
import { buildExecApprovalCustomId, parseExecApprovalData } from "../approval-custom-id.js";
import { parseExecApprovalData } from "../approval-custom-id.js";
import {
DISCORD_APPROVAL_ALLOWED_MENTIONS,
formatDiscordApprovalDisplayValue,
@@ -26,9 +26,6 @@ import {
type MessagePayloadObject,
} from "../internal/discord.js";
export { buildExecApprovalCustomId, parseExecApprovalData };
export { extractDiscordChannelId } from "../approval-native.js";
type ExecApprovalButtonContext = {
getApprovers: () => string[];
resolveApproval: (
@@ -2,6 +2,10 @@
import { EventEmitter } from "node:events";
import { beforeAll, describe, expect, it, vi } from "vitest";
import { DISCORD_GATEWAY_TRANSPORT_ACTIVITY_EVENT } from "./gateway-handle.js";
import {
parseDiscordGatewayInfoBody,
resolveDiscordGatewayInfoTimeoutMs,
} from "./gateway-metadata.js";
const { GatewayIntents, GatewayPlugin } = vi.hoisted(() => {
const GatewayIntentsLocal = {
@@ -73,17 +77,11 @@ vi.mock("openclaw/plugin-sdk/runtime-env", () => ({
describe("createDiscordGatewayPlugin", () => {
let createDiscordGatewayPlugin: typeof import("./gateway-plugin.js").createDiscordGatewayPlugin;
let parseDiscordGatewayInfoBody: typeof import("./gateway-plugin.js").parseDiscordGatewayInfoBody;
let resolveDiscordGatewayIntents: typeof import("./gateway-plugin.js").resolveDiscordGatewayIntents;
let resolveDiscordGatewayInfoTimeoutMs: typeof import("./gateway-plugin.js").resolveDiscordGatewayInfoTimeoutMs;
beforeAll(async () => {
({
createDiscordGatewayPlugin,
parseDiscordGatewayInfoBody,
resolveDiscordGatewayIntents,
resolveDiscordGatewayInfoTimeoutMs,
} = await import("./gateway-plugin.js"));
({ createDiscordGatewayPlugin, resolveDiscordGatewayIntents } =
await import("./gateway-plugin.js"));
});
function createPlugin(
@@ -27,11 +27,6 @@ import {
type DiscordGatewayFetchInit,
} from "./gateway-metadata.js";
export {
parseDiscordGatewayInfoBody,
resolveDiscordGatewayInfoTimeoutMs,
} from "./gateway-metadata.js";
const DISCORD_GATEWAY_HANDSHAKE_TIMEOUT_MS = 30_000;
const DISCORD_GATEWAY_POLICY_VIOLATION_CLOSE_CODE = 1008;
const DISCORD_GATEWAY_WS_RECEIVER_LIMIT_CODE = "WS_ERR_TOO_MANY_BUFFERED_PARTS";
@@ -3,7 +3,7 @@ import type { APIAttachment, APIStickerItem } from "discord-api-types/v10";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import type { Message } from "../internal/discord.js";
export type DiscordSnapshotAuthor = {
type DiscordSnapshotAuthor = {
id?: string | null;
username?: string | null;
discriminator?: string | null;
@@ -21,13 +21,13 @@ export type DiscordSnapshotMessage = {
author?: DiscordSnapshotAuthor | null;
};
export type DiscordMessageSnapshot = {
type DiscordMessageSnapshot = {
message?: DiscordSnapshotMessage | null;
};
const FORWARD_MESSAGE_REFERENCE_TYPE = 1;
export function normalizeDiscordStickerItems(value: unknown): APIStickerItem[] {
function normalizeDiscordStickerItems(value: unknown): APIStickerItem[] {
if (!Array.isArray(value)) {
return [];
}
@@ -52,7 +52,7 @@ export type DiscordMediaInfo = {
placeholder: string;
};
export type DiscordMediaResolveOptions = {
type DiscordMediaResolveOptions = {
fetchImpl?: FetchLike;
ssrfPolicy?: SsrFPolicy;
readIdleTimeoutMs?: number;
@@ -7,6 +7,7 @@ import {
} from "discord-api-types/v10";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { ChannelType, type Client, type Message } from "../internal/discord.js";
import { resetDiscordChannelInfoCacheForTest } from "./message-channel-info.js";
const readRemoteMediaBuffer = vi.fn();
const saveMediaBuffer = vi.fn();
@@ -46,7 +47,6 @@ vi.mock("openclaw/plugin-sdk/runtime-env", async () => {
};
});
let resetDiscordChannelInfoCacheForTest: typeof import("./message-utils.js").resetDiscordChannelInfoCacheForTest;
let resolveDiscordChannelInfo: typeof import("./message-utils.js").resolveDiscordChannelInfo;
let resolveDiscordMessageChannelId: typeof import("./message-utils.js").resolveDiscordMessageChannelId;
let resolveDiscordMessageText: typeof import("./message-utils.js").resolveDiscordMessageText;
@@ -56,7 +56,6 @@ let resolveReferencedReplyMediaList: typeof import("./message-utils.js").resolve
beforeAll(async () => {
({
resetDiscordChannelInfoCacheForTest,
resolveDiscordChannelInfo,
resolveDiscordMessageChannelId,
resolveDiscordMessageText,
@@ -1,32 +1,17 @@
// Discord helper module supports message utils behavior.
export {
resetDiscordChannelInfoCacheForTest,
resolveDiscordChannelInfo,
resolveDiscordMessageChannelId,
type DiscordChannelInfo,
type DiscordChannelInfoClient,
} from "./message-channel-info.js";
export {
hasDiscordMessageStickers,
normalizeDiscordMessageSnapshots,
normalizeDiscordStickerItems,
resolveDiscordMessageSnapshots,
resolveDiscordMessageStickers,
resolveDiscordReferencedForwardMessage,
resolveDiscordReferencedReplyMessage,
resolveDiscordSnapshotStickers,
type DiscordMessageSnapshot,
type DiscordSnapshotAuthor,
type DiscordSnapshotMessage,
} from "./message-forwarded.js";
export { hasDiscordMessageStickers } from "./message-forwarded.js";
export {
buildDiscordMediaPayload,
buildDiscordMediaPlaceholder,
resolveForwardedMediaList,
resolveMediaList,
resolveReferencedReplyMediaList,
type DiscordMediaInfo,
type DiscordMediaResolveOptions,
} from "./message-media.js";
export {
resolveDiscordEmbedText,
@@ -73,7 +73,7 @@ export type DiscordModelPickerState = {
* select cap. Below this threshold the user gets the existing flat list +
* prev/next behavior unchanged.
*/
export const DISCORD_MODEL_PICKER_BUCKET_THRESHOLD = DISCORD_COMPONENT_MAX_SELECT_OPTIONS;
const DISCORD_MODEL_PICKER_BUCKET_THRESHOLD = DISCORD_COMPONENT_MAX_SELECT_OPTIONS;
/** Target items per alpha bucket. Discord caps selects at 25 options. */
export const DISCORD_MODEL_PICKER_BUCKET_TARGET_SIZE = 20;
@@ -63,13 +63,13 @@ type DiscordModelPickerRenderShellParams = {
trailingRows?: DiscordModelPickerRow[];
};
export type DiscordModelPickerRenderedView = {
type DiscordModelPickerRenderedView = {
layout: DiscordModelPickerLayout;
content?: string;
components: TopLevelComponents[];
};
export type DiscordModelPickerProviderViewParams = {
type DiscordModelPickerProviderViewParams = {
command: DiscordModelPickerCommandContext;
userId: string;
data: ModelsProviderData;
@@ -79,7 +79,7 @@ export type DiscordModelPickerProviderViewParams = {
layout?: DiscordModelPickerLayout;
};
export type DiscordModelPickerModelViewParams = {
type DiscordModelPickerModelViewParams = {
command: DiscordModelPickerCommandContext;
userId: string;
data: ModelsProviderData;
@@ -877,7 +877,7 @@ export function renderDiscordModelPickerModelsView(
});
}
export type DiscordModelPickerRecentsViewParams = {
type DiscordModelPickerRecentsViewParams = {
command: DiscordModelPickerCommandContext;
userId: string;
data: ModelsProviderData;
@@ -16,12 +16,12 @@ import {
resetDiscordComponentRuntimeMocks,
upsertPairingRequestMock,
} from "../test-support/component-runtime.js";
import { resolveComponentInteractionContext } from "./agent-components-helpers.js";
import { resolveComponentInteractionContext } from "./agent-components-context.js";
import { resolveDiscordComponentOriginatingTo } from "./agent-components.dispatch.js";
import {
createAgentComponentButton,
createAgentSelectMenu,
resolveDiscordComponentOriginatingTo,
} from "./agent-components.js";
} from "./agent-components.system-controls.js";
describe("agent components", () => {
const defaultDmSessionKey = buildAgentSessionKey({
@@ -34,7 +34,7 @@ function createCommandArgsWithValue(params: { argName: string; value: string }):
return { values };
}
export function buildDiscordCommandArgCustomId(params: {
function buildDiscordCommandArgCustomId(params: {
command: string;
arg: string;
value: string;
@@ -71,7 +71,7 @@ function parseDiscordCommandArgData(
};
}
export async function handleDiscordCommandArgInteraction(params: {
async function handleDiscordCommandArgInteraction(params: {
interaction: ButtonInteraction;
data: ComponentData;
ctx: DiscordCommandArgContext;
@@ -11,7 +11,7 @@ import type { ThreadBindingManager } from "./thread-bindings.js";
type DiscordConfig = NonNullable<OpenClawConfig["channels"]>["discord"];
export type DispatchDiscordCommandInteractionParams = {
type DispatchDiscordCommandInteractionParams = {
interaction: CommandInteraction | ButtonInteraction | StringSelectMenuInteraction;
prompt: string;
command: ChatCommandDefinition;
@@ -302,7 +302,7 @@ function resolveDiscordModelPickerSubmissionRuntime(params: {
);
}
export async function handleDiscordModelPickerInteraction(params: {
async function handleDiscordModelPickerInteraction(params: {
interaction: ButtonInteraction | StringSelectMenuInteraction;
data: ComponentData;
ctx: DiscordModelPickerContext;
@@ -1,27 +1,19 @@
// Discord plugin module implements native command ui behavior.
export {
buildDiscordCommandArgCustomId,
buildDiscordCommandArgMenu,
createDiscordCommandArgFallbackButton,
handleDiscordCommandArgInteraction,
} from "./native-command-arg-ui.js";
export {
createDiscordModelPickerFallbackButton,
createDiscordModelPickerFallbackSelect,
handleDiscordModelPickerInteraction,
} from "./native-command-model-picker-interaction.js";
export {
replyWithDiscordModelPickerProviders,
resolveDiscordNativeChoiceContext,
shouldOpenDiscordModelPickerFromCommand,
} from "./native-command-model-picker-ui.js";
export type {
DispatchDiscordCommandInteraction,
DispatchDiscordCommandInteractionParams,
DispatchDiscordCommandInteractionResult,
} from "./native-command-dispatch.js";
export type { DispatchDiscordCommandInteraction } from "./native-command-dispatch.js";
export type {
DiscordCommandArgContext,
DiscordModelPickerContext,
SafeDiscordInteractionCall,
} from "./native-command-ui.types.js";
@@ -21,7 +21,7 @@ import {
import * as commandTextModule from "openclaw/plugin-sdk/text-utility-runtime";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { defineThrowingDiscordChannelGetter } from "../test-support/partial-channel.js";
import { resolveDiscordChannelContext } from "./agent-components-helpers.js";
import { resolveDiscordChannelContext } from "./agent-components-context.js";
import * as modelPickerPreferencesModule from "./model-picker-preferences.js";
import * as modelPickerModule from "./model-picker.state.js";
import { createModelsProviderData as createBaseModelsProviderData } from "./model-picker.test-utils.js";
@@ -56,4 +56,3 @@ export const testing = {
return previous;
},
};
export { testing as __testing };
@@ -85,7 +85,7 @@ import { resolveDiscordSenderIdentity } from "./sender-identity.js";
import type { ThreadBindingManager } from "./thread-bindings.js";
const log = createSubsystemLogger("discord/native-command");
export { testing, testing as __testing } from "./native-command.runtime.js";
export { testing } from "./native-command.runtime.js";
export function createDiscordNativeCommand(params: {
command: NativeCommandSpec;
@@ -688,4 +688,3 @@ export const testing = {
};
export const resolveDiscordRuntimeGroupPolicy = resolveOpenProviderRuntimeGroupPolicy;
export { testing as __testing };
@@ -2,7 +2,7 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { ChannelType } from "../internal/discord.js";
import { createPartialDiscordChannelWithThrowingGetters } from "../test-support/partial-channel.js";
import { resetDiscordChannelInfoCacheForTest } from "./message-utils.js";
import { resetDiscordChannelInfoCacheForTest } from "./message-channel-info.js";
import { resolveDiscordThreadParentInfo } from "./threading.js";
describe("resolveDiscordThreadParentInfo", () => {
@@ -13,8 +13,7 @@ const DISCORD_MESSAGE_COMPONENT_LIMIT = 40;
const DISCORD_TEXT_DISPLAY_LIMIT = 2000;
const DISCORD_CONTEXT_PREFIX_LENGTH = Array.from("-# ").length;
export const DISCORD_PRESENTATION_TEXT_LIMIT =
DISCORD_TEXT_DISPLAY_LIMIT - DISCORD_CONTEXT_PREFIX_LENGTH;
const DISCORD_PRESENTATION_TEXT_LIMIT = DISCORD_TEXT_DISPLAY_LIMIT - DISCORD_CONTEXT_PREFIX_LENGTH;
export const DISCORD_PRESENTATION_CAPABILITIES = {
supported: true,
+1 -1
View File
@@ -47,7 +47,7 @@ const DISCORD_PRECONNECT_ERROR_CODES = new Set([
]);
const log = createSubsystemLogger("discord/retry");
export type DiscordRetrySafety = "idempotent" | "nonce-protected-create" | "non-idempotent-create";
type DiscordRetrySafety = "idempotent" | "nonce-protected-create" | "non-idempotent-create";
export type DiscordRetryRunner = <T>(
fn: () => Promise<T>,
@@ -9,10 +9,10 @@ import {
type TopLevelComponents,
} from "./internal/discord.js";
export const SUPPRESS_EMBEDS_FLAG = MessageFlags.SuppressEmbeds;
const SUPPRESS_EMBEDS_FLAG = MessageFlags.SuppressEmbeds;
export const SUPPRESS_NOTIFICATIONS_FLAG = MessageFlags.SuppressNotifications;
export type DiscordSendComponentFactory = (text: string) => TopLevelComponents[];
type DiscordSendComponentFactory = (text: string) => TopLevelComponents[];
export type DiscordSendComponents = TopLevelComponents[] | DiscordSendComponentFactory;
export type DiscordSendEmbeds = Array<APIEmbed | Embed>;
export type DiscordAllowedMentions = APIAllowedMentions;
@@ -51,7 +51,7 @@ export function resolveDiscordSendEmbeds(params: {
return normalizeDiscordEmbeds(params.embeds);
}
export function buildDiscordMessagePayload(params: {
function buildDiscordMessagePayload(params: {
text: string;
components?: TopLevelComponents[];
embeds?: Embed[];
-3
View File
@@ -45,16 +45,13 @@ const DISCORD_UPLOAD_TOO_LARGE_NOTICE =
type DiscordRequest = DiscordRetryRunner;
export {
buildDiscordMessagePayload,
buildDiscordMessageRequest,
createDiscordMessageNonce,
resolveDiscordMessageFlags,
resolveDiscordSendComponents,
resolveDiscordSendEmbeds,
stripUndefinedFields,
SUPPRESS_EMBEDS_FLAG,
SUPPRESS_NOTIFICATIONS_FLAG,
type DiscordSendComponentFactory,
type DiscordAllowedMentions,
type DiscordSendComponents,
type DiscordSendEmbeds,
+9 -77
View File
@@ -1,8 +1,8 @@
// Slack tests cover approval auth plugin behavior.
import { describe, expect, it } from "vitest";
import { isSlackApprovalAuthorizedSender, slackApprovalAuth } from "./approval-auth.js";
import { isSlackApprovalAuthorizedSender } from "./approval-auth.js";
describe("slackApprovalAuth", () => {
describe("isSlackApprovalAuthorizedSender", () => {
it("authorizes general Slack approvers from allowFrom and defaultTo", () => {
const cfg = {
channels: {
@@ -15,65 +15,12 @@ describe("slackApprovalAuth", () => {
},
};
expect(
slackApprovalAuth.authorizeActorAction({
cfg,
senderId: "U123OWNER",
action: "approve",
approvalKind: "exec",
}),
).toEqual({ authorized: true });
expect(
slackApprovalAuth.authorizeActorAction({
cfg,
senderId: "u123owner",
action: "approve",
approvalKind: "plugin",
}),
).toEqual({ authorized: true });
expect(
slackApprovalAuth.authorizeActorAction({
cfg,
senderId: "U345DEFAULT",
action: "approve",
approvalKind: "plugin",
}),
).toEqual({ authorized: true });
expect(
slackApprovalAuth.authorizeActorAction({
cfg,
senderId: "u345default",
action: "approve",
approvalKind: "plugin",
}),
).toEqual({ authorized: true });
expect(
slackApprovalAuth.authorizeActorAction({
cfg,
senderId: "U999EXEC",
action: "approve",
approvalKind: "plugin",
}),
).toEqual({
authorized: false,
reason: "❌ You are not authorized to approve plugin requests on Slack.",
});
expect(
slackApprovalAuth.authorizeActorAction({
cfg,
senderId: "U999ATTACKER",
action: "approve",
approvalKind: "exec",
}),
).toEqual({
authorized: false,
reason: "❌ You are not authorized to approve exec requests on Slack.",
});
for (const senderId of ["U123OWNER", "u123owner", "U345DEFAULT", "u345default"]) {
expect(isSlackApprovalAuthorizedSender({ cfg, senderId })).toBe(true);
}
for (const senderId of ["U999EXEC", "U999ATTACKER"]) {
expect(isSlackApprovalAuthorizedSender({ cfg, senderId })).toBe(false);
}
});
it("canonicalizes configured plugin approver ids before matching uppercase senders", () => {
@@ -87,14 +34,7 @@ describe("slackApprovalAuth", () => {
};
for (const senderId of ["U123OWNER", "U345DEFAULT"]) {
expect(
slackApprovalAuth.authorizeActorAction({
cfg,
senderId,
action: "approve",
approvalKind: "plugin",
}),
).toEqual({ authorized: true });
expect(isSlackApprovalAuthorizedSender({ cfg, senderId })).toBe(true);
}
});
@@ -107,14 +47,6 @@ describe("slackApprovalAuth", () => {
},
};
expect(
slackApprovalAuth.authorizeActorAction({
cfg,
senderId: "U123OWNER",
action: "approve",
approvalKind: "plugin",
}),
).toEqual({ authorized: true });
expect(
isSlackApprovalAuthorizedSender({
cfg,
+1 -10
View File
@@ -1,8 +1,5 @@
// Slack plugin module implements approval auth behavior.
import {
createResolvedApproverActionAuthAdapter,
resolveApprovalApprovers,
} from "openclaw/plugin-sdk/approval-auth-runtime";
import { resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import { resolveSlackAccount, resolveSlackAccountAllowFrom } from "./accounts.js";
import { normalizeSlackApproverId } from "./exec-approvals.js";
@@ -35,9 +32,3 @@ export function isSlackApprovalAuthorizedSender(params: {
}
return (resolveSlackAccountAllowFrom(params) ?? []).some((entry) => entry.trim() === "*");
}
export const slackApprovalAuth = createResolvedApproverActionAuthAdapter({
channelLabel: "Slack",
resolveApprovers: ({ cfg, accountId }) => getSlackApprovalApprovers({ cfg, accountId }),
normalizeSenderId: (value) => normalizeSlackApproverId(value),
});
+1 -1
View File
@@ -9,7 +9,7 @@ import {
} from "./data-visualization.js";
import { escapeSlackMrkdwn } from "./monitor/mrkdwn.js";
export type SlackNativeDataFallbackFormat = "plain" | "mrkdwn-safe";
type SlackNativeDataFallbackFormat = "plain" | "mrkdwn-safe";
type RenderSlackBlockFallbackOptions = {
nativeDataFormat?: SlackNativeDataFallbackFormat;
+1 -33
View File
@@ -2,10 +2,7 @@
import { describe, expect, it } from "vitest";
import { buildSlackBlocksFallbackText, renderSlackBlockFallbackText } from "./blocks-fallback.js";
import { parseSlackBlocksInput } from "./blocks-input.js";
import {
encodeSlackModalPrivateMetadata,
parseSlackModalPrivateMetadata,
} from "./modal-metadata.js";
import { parseSlackModalPrivateMetadata } from "./modal-metadata.js";
describe("buildSlackBlocksFallbackText", () => {
it("prefers header text", () => {
@@ -280,32 +277,3 @@ describe("parseSlackModalPrivateMetadata", () => {
});
});
});
describe("encodeSlackModalPrivateMetadata", () => {
it("encodes only known non-empty fields", () => {
expect(
JSON.parse(
encodeSlackModalPrivateMetadata({
sessionKey: "agent:main:slack:channel:C1",
channelId: "",
channelType: "im",
userId: "U123",
pluginInteractiveData: "dean.contract:confirm",
}),
),
).toEqual({
sessionKey: "agent:main:slack:channel:C1",
channelType: "im",
userId: "U123",
pluginInteractiveData: "dean.contract:confirm",
});
});
it("throws when encoded payload exceeds Slack metadata limit", () => {
expect(() =>
encodeSlackModalPrivateMetadata({
sessionKey: `agent:main:${"x".repeat(4000)}`,
}),
).toThrow(/cannot exceed 3000 chars/i);
});
});
+4 -4
View File
@@ -7,8 +7,8 @@ import {
import { escapeSlackMrkdwn } from "./monitor/mrkdwn.js";
import { renderSlackMessagePresentationTableFallbackText } from "./presentation-fallback.js";
export const SLACK_DATA_TABLE_COLUMNS_MAX = 20;
export const SLACK_DATA_TABLE_ROWS_MAX = 100;
const SLACK_DATA_TABLE_COLUMNS_MAX = 20;
const SLACK_DATA_TABLE_ROWS_MAX = 100;
export const SLACK_DATA_TABLE_CELL_CHARACTERS_MAX = 10_000;
type SlackDataTableRawTextCell = {
@@ -22,9 +22,9 @@ type SlackDataTableRawNumberCell = {
text: string;
};
export type SlackDataTableCell = SlackDataTableRawTextCell | SlackDataTableRawNumberCell;
type SlackDataTableCell = SlackDataTableRawTextCell | SlackDataTableRawNumberCell;
export type SlackDataTableBlock = Block & {
type SlackDataTableBlock = Block & {
type: "data_table";
caption: string;
rows: SlackDataTableCell[][];
+6 -6
View File
@@ -8,11 +8,11 @@ import {
import { escapeSlackMrkdwn } from "./monitor/mrkdwn.js";
import { renderSlackMessagePresentationChartFallbackText } from "./presentation-fallback.js";
export const SLACK_CHART_TITLE_MAX = 50;
export const SLACK_CHART_LABEL_MAX = 20;
export const SLACK_CHART_AXIS_LABEL_MAX = 50;
export const SLACK_CHART_SERIES_MAX = 12;
export const SLACK_CHART_DATA_POINTS_MAX = 20;
const SLACK_CHART_TITLE_MAX = 50;
const SLACK_CHART_LABEL_MAX = 20;
const SLACK_CHART_AXIS_LABEL_MAX = 50;
const SLACK_CHART_SERIES_MAX = 12;
const SLACK_CHART_DATA_POINTS_MAX = 20;
// Slack's API rejects a third data_visualization block even though its public
// reference does not currently document this per-message subtype limit.
export const SLACK_DATA_VISUALIZATION_BLOCKS_MAX = 2;
@@ -34,7 +34,7 @@ type SlackSeriesChart = {
};
};
export type SlackDataVisualizationBlock = Block & {
type SlackDataVisualizationBlock = Block & {
type: "data_visualization";
title: string;
chart: SlackPieChart | SlackSeriesChart;
-19
View File
@@ -9,8 +9,6 @@ type SlackModalPrivateMetadata = {
pluginInteractiveData?: string;
};
const SLACK_PRIVATE_METADATA_MAX = 3000;
export function parseSlackModalPrivateMetadata(raw: unknown): SlackModalPrivateMetadata {
if (typeof raw !== "string" || raw.trim().length === 0) {
return {};
@@ -28,20 +26,3 @@ export function parseSlackModalPrivateMetadata(raw: unknown): SlackModalPrivateM
return {};
}
}
export function encodeSlackModalPrivateMetadata(input: SlackModalPrivateMetadata): string {
const payload: SlackModalPrivateMetadata = {
...(input.sessionKey ? { sessionKey: input.sessionKey } : {}),
...(input.channelId ? { channelId: input.channelId } : {}),
...(input.channelType ? { channelType: input.channelType } : {}),
...(input.userId ? { userId: input.userId } : {}),
...(input.pluginInteractiveData ? { pluginInteractiveData: input.pluginInteractiveData } : {}),
};
const encoded = JSON.stringify(payload);
if (encoded.length > SLACK_PRIVATE_METADATA_MAX) {
throw new Error(
`Slack modal private_metadata cannot exceed ${SLACK_PRIVATE_METADATA_MAX} chars`,
);
}
return encoded;
}
+1 -1
View File
@@ -16,7 +16,7 @@ export type SlackEventScope = {
uploadCompletionClient?: WebClient;
};
export type SlackEventScopeResolution =
type SlackEventScopeResolution =
| { ok: true; scope?: SlackEventScope }
| {
ok: false;
@@ -7,8 +7,8 @@ import type { SlackMessageEvent } from "../../types.js";
import type { SlackMediaResult } from "../media-types.js";
import {
discardSlackPreflightMedia,
findCaptionlessSlackAudioFile,
formatSlackAudioTranscriptForAgent,
hasCaptionlessSlackAudio,
resolveSlackPreflightAudioTranscript,
sendSlackPreflightAudioTranscriptEcho,
} from "./preflight-audio.js";
@@ -68,15 +68,15 @@ describe("Slack captionless audio preflight", () => {
],
});
expect(hasCaptionlessSlackAudio(voiceClip)).toBe(true);
expect(hasCaptionlessSlackAudio({ ...voiceClip, text: "typed caption" })).toBe(false);
expect(findCaptionlessSlackAudioFile(voiceClip)).toEqual(voiceClip.files?.[0]);
expect(findCaptionlessSlackAudioFile({ ...voiceClip, text: "typed caption" })).toBeUndefined();
expect(
hasCaptionlessSlackAudio(
findCaptionlessSlackAudioFile(
createSlackMessage({
files: [{ id: "F2", name: "screen.mp4", mimetype: "video/mp4" }],
}),
),
).toBe(false);
).toBeUndefined();
});
it("frames machine transcripts as untrusted input without replacing the file placeholder", () => {
@@ -31,10 +31,6 @@ export function findCaptionlessSlackAudioFile(message: SlackMessageEvent): Slack
return message.files?.slice(0, MAX_SLACK_MEDIA_FILES).find(isSlackAudioFile);
}
export function hasCaptionlessSlackAudio(message: SlackMessageEvent): boolean {
return Boolean(findCaptionlessSlackAudioFile(message));
}
export function formatSlackAudioTranscriptForAgent(params: {
transcript: string;
rawBody: string;
@@ -337,4 +337,3 @@ export function resolveSlackRoutingContext(params: {
export const testing = {
normalizeSlackRouteBindingConfig,
};
export { testing as __testing };
@@ -202,4 +202,3 @@ export const testing = {
didSlackPreviewEditApplyAfterError,
readSlackMessageAfterEditError,
};
export { testing as __testing };
-3
View File
@@ -778,8 +778,6 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
}
}
export { isNonRecoverableSlackAuthError } from "./reconnect-policy.js";
export const resolveSlackRuntimeGroupPolicy = resolveOpenProviderRuntimeGroupPolicy;
export const testing = {
@@ -798,4 +796,3 @@ export const testing = {
getSocketEmitter,
waitForSlackSocketDisconnect,
};
export { testing as __testing };
+2 -2
View File
@@ -276,14 +276,14 @@ export async function deliverReplies(params: {
return latestResult;
}
export type SlackRespondFn = (payload: {
type SlackRespondFn = (payload: {
text: string;
blocks?: (Block | KnownBlock)[];
mrkdwn?: false;
response_type?: "ephemeral" | "in_channel";
}) => Promise<unknown>;
export type SlackResponseUrlBudget = ResponseUrlBudget<Parameters<SlackRespondFn>[0]>;
type SlackResponseUrlBudget = ResponseUrlBudget<Parameters<SlackRespondFn>[0]>;
/**
* Compute effective threadTs for a Slack reply based on replyToMode.
@@ -4,7 +4,7 @@ export type SlackResponseUrlBudget<TPayload> = {
remaining: () => number | undefined;
};
export const SLACK_RESPONSE_URL_MAX_CALLS = 5;
const SLACK_RESPONSE_URL_MAX_CALLS = 5;
export class SlackResponseAlreadyReportedError extends Error {}
+1 -1
View File
@@ -21,7 +21,7 @@ export type SlackFormattingDisabledMessage = {
mrkdwn: false;
};
export type SlackNativeDataDeliveryPlan = {
type SlackNativeDataDeliveryPlan = {
accessibilityText: string;
fallbackMessages: SlackFormattingDisabledMessage[];
skipOriginalBlocks: boolean;
+2 -1
View File
@@ -1,7 +1,8 @@
// Slack tests cover shared plugin behavior.
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
import { describe, expect, it } from "vitest";
import { createSlackPluginBase, setSlackChannelAllowlist, slackConfigAdapter } from "./shared.js";
import { setSlackChannelAllowlist } from "./setup-shared.js";
import { createSlackPluginBase, slackConfigAdapter } from "./shared.js";
describe("createSlackPluginBase", () => {
it("owns Slack native command name overrides", () => {
+1 -1
View File
@@ -23,7 +23,7 @@ import { collectRuntimeConfigAssignments, secretTargetRegistryEntries } from "./
import { slackSecurityAdapter } from "./security.js";
import { SLACK_CHANNEL } from "./setup-shared.js";
export { setSlackChannelAllowlist, SLACK_CHANNEL } from "./setup-shared.js";
export { SLACK_CHANNEL } from "./setup-shared.js";
export { isSlackPluginAccountConfigured };
+1 -1
View File
@@ -233,7 +233,7 @@ export async function appendSlackStream(params: AppendSlackStreamParams): Promis
}
/** Result of {@link stopSlackStream}. */
export type StopSlackStreamResult = {
type StopSlackStreamResult = {
/**
* The Slack `ts` of the finalized streamed message, when `chat.stopStream`
* reports it. Used to populate `MessageSentEvent.messageId` for the
+1 -2
View File
@@ -1,12 +1,11 @@
// Slack tests cover targets plugin behavior.
import { describe, expect, it } from "vitest";
import { canonicalizeSlackApiTargetId } from "./target-parsing.js";
import { canonicalizeSlackApiTargetId, slackTargetsMatch } from "./target-parsing.js";
import {
normalizeSlackMessagingTarget,
parseSlackTarget,
resolveSlackChannelId,
slackContextTargetsMatch,
slackTargetsMatch,
} from "./targets.js";
describe("parseSlackTarget", () => {
-1
View File
@@ -34,5 +34,4 @@ export {
parseSlackTarget,
resolveSlackChannelId,
} from "./target-parsing.js";
export { slackTargetsMatch };
export type { SlackTarget, SlackTargetKind, SlackTargetParseOptions } from "./target-parsing.js";
-103
View File
@@ -53,136 +53,59 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [
"extensions/diffs/src/viewer-assets.ts: resolveViewerRuntimeFileUrl",
"extensions/diffs/src/viewer-assets.ts: VIEWER_LOADER_PATH",
"extensions/discord/src/actions/runtime.guild.ts: discordGuildActionRuntime",
"extensions/discord/src/actions/runtime.messaging.ts: discordMessagingActionRuntime",
"extensions/discord/src/actions/runtime.moderation.ts: discordModerationActionRuntime",
"extensions/discord/src/approval-custom-id.ts: DiscordApprovalAction",
"extensions/discord/src/approval-native.ts: createDiscordNativeApprovalAdapter",
"extensions/discord/src/approval-native.ts: extractDiscordChannelId",
"extensions/discord/src/approval-native.ts: shouldHandleDiscordApprovalRequest",
"extensions/discord/src/audit-core.ts: collectDiscordAuditChannelIdsForGuilds",
"extensions/discord/src/chunk.ts: chunkDiscordText",
"extensions/discord/src/components-registry.ts: clearDiscordComponentEntries",
"extensions/discord/src/components-registry.ts: resolveDiscordComponentEntry",
"extensions/discord/src/components-registry.ts: resolveDiscordModalEntry",
"extensions/discord/src/directory-cache.ts: resetDiscordDirectoryCacheForTest",
"extensions/discord/src/internal/api.interactions.ts: deleteWebhookMessage",
"extensions/discord/src/internal/api.ts: deleteWebhookMessage",
"extensions/discord/src/internal/client.ts: AnyListener",
"extensions/discord/src/internal/client.ts: ClientOptions",
"extensions/discord/src/internal/client.ts: ComponentRegistry",
"extensions/discord/src/internal/client.ts: Context",
"extensions/discord/src/internal/client.ts: Route",
"extensions/discord/src/internal/command-deploy.ts: __testing",
"extensions/discord/src/internal/command-deploy.ts: testing",
"extensions/discord/src/internal/commands.ts: CommandOption",
"extensions/discord/src/internal/components.base.ts: ConditionalComponentOption",
"extensions/discord/src/internal/components.ts: AnySelectMenu",
"extensions/discord/src/internal/components.ts: BaseComponent",
"extensions/discord/src/internal/components.ts: BaseModalComponent",
"extensions/discord/src/internal/components.ts: clean",
"extensions/discord/src/internal/components.ts: colorToNumber",
"extensions/discord/src/internal/components.ts: ConditionalComponentOption",
"extensions/discord/src/internal/interactions.ts: BaseInteraction",
"extensions/discord/src/internal/interactions.ts: ModalFields",
"extensions/discord/src/internal/interactions.ts: OptionsHandler",
"extensions/discord/src/internal/rest-scheduler.ts: RestSchedulerOptions",
"extensions/discord/src/internal/rest.ts: QueuedRequest",
"extensions/discord/src/internal/rest.ts: RequestPriority",
"extensions/discord/src/mentions.ts: discordTextHasTargetedMention",
"extensions/discord/src/monitor.gateway.ts: WaitForDiscordGatewayStopParams",
"extensions/discord/src/monitor/agent-components-helpers.ts: resolveComponentInteractionContext",
"extensions/discord/src/monitor/agent-components-helpers.ts: resolveDiscordChannelContext",
"extensions/discord/src/monitor/agent-components.dispatch.ts: resolveDiscordComponentOriginatingTo",
"extensions/discord/src/monitor/agent-components.system-controls.ts: AgentComponentButton",
"extensions/discord/src/monitor/agent-components.system-controls.ts: AgentSelectMenu",
"extensions/discord/src/monitor/agent-components.ts: AgentComponentButton",
"extensions/discord/src/monitor/agent-components.ts: AgentSelectMenu",
"extensions/discord/src/monitor/agent-components.ts: createAgentComponentButton",
"extensions/discord/src/monitor/agent-components.ts: createAgentSelectMenu",
"extensions/discord/src/monitor/agent-components.ts: createDiscordComponentButton",
"extensions/discord/src/monitor/agent-components.ts: createDiscordComponentChannelSelect",
"extensions/discord/src/monitor/agent-components.ts: createDiscordComponentMentionableSelect",
"extensions/discord/src/monitor/agent-components.ts: createDiscordComponentRoleSelect",
"extensions/discord/src/monitor/agent-components.ts: createDiscordComponentStringSelect",
"extensions/discord/src/monitor/agent-components.ts: createDiscordComponentUserSelect",
"extensions/discord/src/monitor/agent-components.ts: resolveDiscordComponentOriginatingTo",
"extensions/discord/src/monitor/allow-list.ts: resolveDiscordRoleAllowed",
"extensions/discord/src/monitor/auto-presence.ts: resolveDiscordAutoPresenceDecision",
"extensions/discord/src/monitor/channel-access.ts: resolveDiscordChannelOwnerIdSafe",
"extensions/discord/src/monitor/exec-approvals.ts: buildExecApprovalCustomId",
"extensions/discord/src/monitor/exec-approvals.ts: ExecApprovalButton",
"extensions/discord/src/monitor/exec-approvals.ts: extractDiscordChannelId",
"extensions/discord/src/monitor/exec-approvals.ts: parseExecApprovalData",
"extensions/discord/src/monitor/gateway-metadata.ts: fetchDiscordGatewayInfo",
"extensions/discord/src/monitor/gateway-metadata.ts: parseDiscordGatewayInfoBody",
"extensions/discord/src/monitor/gateway-plugin.ts: parseDiscordGatewayInfoBody",
"extensions/discord/src/monitor/gateway-plugin.ts: resolveDiscordGatewayInfoTimeoutMs",
"extensions/discord/src/monitor/gateway-supervisor.ts: classifyDiscordGatewayEvent",
"extensions/discord/src/monitor/inbound-context.ts: buildDiscordUntrustedContext",
"extensions/discord/src/monitor/message-channel-info.ts: resetDiscordChannelInfoCacheForTest",
"extensions/discord/src/monitor/message-forwarded.ts: DiscordMessageSnapshot",
"extensions/discord/src/monitor/message-forwarded.ts: DiscordSnapshotAuthor",
"extensions/discord/src/monitor/message-forwarded.ts: normalizeDiscordStickerItems",
"extensions/discord/src/monitor/message-media.ts: DiscordMediaResolveOptions",
"extensions/discord/src/monitor/message-utils.ts: buildDiscordMediaPlaceholder",
"extensions/discord/src/monitor/message-utils.ts: DiscordMediaResolveOptions",
"extensions/discord/src/monitor/message-utils.ts: DiscordMessageSnapshot",
"extensions/discord/src/monitor/message-utils.ts: DiscordSnapshotAuthor",
"extensions/discord/src/monitor/message-utils.ts: DiscordSnapshotMessage",
"extensions/discord/src/monitor/message-utils.ts: normalizeDiscordMessageSnapshots",
"extensions/discord/src/monitor/message-utils.ts: normalizeDiscordStickerItems",
"extensions/discord/src/monitor/message-utils.ts: resetDiscordChannelInfoCacheForTest",
"extensions/discord/src/monitor/message-utils.ts: resolveDiscordMessageSnapshots",
"extensions/discord/src/monitor/message-utils.ts: resolveDiscordMessageStickers",
"extensions/discord/src/monitor/message-utils.ts: resolveDiscordReferencedForwardMessage",
"extensions/discord/src/monitor/message-utils.ts: resolveDiscordReferencedReplyMessage",
"extensions/discord/src/monitor/message-utils.ts: resolveDiscordSnapshotStickers",
"extensions/discord/src/monitor/model-picker-preferences.ts: buildDiscordModelPickerPreferenceKey",
"extensions/discord/src/monitor/model-picker.state.ts: computeAlphaBuckets",
"extensions/discord/src/monitor/model-picker.state.ts: DISCORD_CUSTOM_ID_MAX_CHARS",
"extensions/discord/src/monitor/model-picker.state.ts: DISCORD_MODEL_PICKER_BUCKET_TARGET_SIZE",
"extensions/discord/src/monitor/model-picker.state.ts: DISCORD_MODEL_PICKER_BUCKET_THRESHOLD",
"extensions/discord/src/monitor/model-picker.state.ts: DISCORD_MODEL_PICKER_MODEL_PAGE_SIZE",
"extensions/discord/src/monitor/model-picker.state.ts: DISCORD_MODEL_PICKER_PROVIDER_PAGE_SIZE",
"extensions/discord/src/monitor/model-picker.state.ts: DISCORD_MODEL_PICKER_PROVIDER_SINGLE_PAGE_MAX",
"extensions/discord/src/monitor/model-picker.state.ts: parseDiscordModelPickerCustomId",
"extensions/discord/src/monitor/model-picker.view.ts: DiscordModelPickerModelViewParams",
"extensions/discord/src/monitor/model-picker.view.ts: DiscordModelPickerProviderViewParams",
"extensions/discord/src/monitor/model-picker.view.ts: DiscordModelPickerRecentsViewParams",
"extensions/discord/src/monitor/model-picker.view.ts: DiscordModelPickerRenderedView",
"extensions/discord/src/monitor/native-command-arg-ui.ts: buildDiscordCommandArgCustomId",
"extensions/discord/src/monitor/native-command-arg-ui.ts: handleDiscordCommandArgInteraction",
"extensions/discord/src/monitor/native-command-dispatch.ts: DispatchDiscordCommandInteractionParams",
"extensions/discord/src/monitor/native-command-model-picker-interaction.ts: handleDiscordModelPickerInteraction",
"extensions/discord/src/monitor/native-command-ui.ts: buildDiscordCommandArgCustomId",
"extensions/discord/src/monitor/native-command-ui.ts: DispatchDiscordCommandInteraction",
"extensions/discord/src/monitor/native-command-ui.ts: DispatchDiscordCommandInteractionParams",
"extensions/discord/src/monitor/native-command-ui.ts: DispatchDiscordCommandInteractionResult",
"extensions/discord/src/monitor/native-command-ui.ts: handleDiscordCommandArgInteraction",
"extensions/discord/src/monitor/native-command-ui.ts: handleDiscordModelPickerInteraction",
"extensions/discord/src/monitor/native-command-ui.ts: SafeDiscordInteractionCall",
"extensions/discord/src/monitor/native-command.runtime.ts: __testing",
"extensions/discord/src/monitor/native-command.runtime.ts: testing",
"extensions/discord/src/monitor/native-command.ts: __testing",
"extensions/discord/src/monitor/native-command.ts: testing",
"extensions/discord/src/monitor/provider.lifecycle.ts: resolveDiscordGatewayReadyTimeoutMs",
"extensions/discord/src/monitor/provider.lifecycle.ts: resolveDiscordGatewayRuntimeReadyTimeoutMs",
"extensions/discord/src/monitor/provider.ts: __testing",
"extensions/discord/src/monitor/provider.ts: resolveDiscordRuntimeGroupPolicy",
"extensions/discord/src/monitor/provider.ts: testing",
"extensions/discord/src/monitor/thread-title.ts: normalizeGeneratedThreadTitle",
"extensions/discord/src/outbound-components.ts: DISCORD_PRESENTATION_TEXT_LIMIT",
"extensions/discord/src/resolve-allowlist-common.ts: findDiscordGuildByName",
"extensions/discord/src/retry.ts: DiscordRetrySafety",
"extensions/discord/src/retry.ts: isRetryableDiscordPreConnectError",
"extensions/discord/src/retry.ts: isRetryableDiscordTransientError",
"extensions/discord/src/runtime.ts: DiscordRuntime",
"extensions/discord/src/send.message-request.ts: buildDiscordMessagePayload",
"extensions/discord/src/send.message-request.ts: DiscordSendComponentFactory",
"extensions/discord/src/send.message-request.ts: SUPPRESS_EMBEDS_FLAG",
"extensions/discord/src/send.shared.ts: buildDiscordMessagePayload",
"extensions/discord/src/send.shared.ts: DiscordSendComponentFactory",
"extensions/discord/src/send.shared.ts: SUPPRESS_EMBEDS_FLAG",
"extensions/discord/src/voice-message.ts: VoiceMessageMetadata",
"extensions/discord/src/voice/prompt.ts: DISCORD_VOICE_SPOKEN_OUTPUT_CONTRACT",
"extensions/duckduckgo/src/config.ts: DEFAULT_DDG_SAFE_SEARCH",
@@ -938,29 +861,15 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [
"extensions/signal/src/runtime.ts: clearSignalRuntime",
"extensions/signal/src/runtime.ts: getSignalRuntime",
"extensions/signal/src/setup-core.ts: parseSignalAllowFromEntries",
"extensions/slack/src/approval-auth.ts: slackApprovalAuth",
"extensions/slack/src/approval-native-gates.ts: resolveSlackApprovalKind",
"extensions/slack/src/approval-native.ts: testing",
"extensions/slack/src/blocks-fallback.ts: SlackNativeDataFallbackFormat",
"extensions/slack/src/channel-migration.ts: migrateSlackChannelsInPlace",
"extensions/slack/src/data-table.ts: canRenderSlackDataTable",
"extensions/slack/src/data-table.ts: SLACK_DATA_TABLE_COLUMNS_MAX",
"extensions/slack/src/data-table.ts: SLACK_DATA_TABLE_ROWS_MAX",
"extensions/slack/src/data-table.ts: SlackDataTableBlock",
"extensions/slack/src/data-table.ts: SlackDataTableCell",
"extensions/slack/src/data-visualization.ts: SLACK_CHART_AXIS_LABEL_MAX",
"extensions/slack/src/data-visualization.ts: SLACK_CHART_DATA_POINTS_MAX",
"extensions/slack/src/data-visualization.ts: SLACK_CHART_LABEL_MAX",
"extensions/slack/src/data-visualization.ts: SLACK_CHART_SERIES_MAX",
"extensions/slack/src/data-visualization.ts: SLACK_CHART_TITLE_MAX",
"extensions/slack/src/data-visualization.ts: SlackDataVisualizationBlock",
"extensions/slack/src/exec-approvals.ts: isSlackExecApprovalApprover",
"extensions/slack/src/exec-approvals.ts: isSlackExecApprovalTargetRecipient",
"extensions/slack/src/exec-approvals.ts: shouldHandleSlackExecApprovalRequest",
"extensions/slack/src/format.ts: markdownToSlackMrkdwn",
"extensions/slack/src/modal-metadata.ts: encodeSlackModalPrivateMetadata",
"extensions/slack/src/monitor/auth.ts: clearSlackAllowFromCacheForTest",
"extensions/slack/src/monitor/event-scope.ts: SlackEventScopeResolution",
"extensions/slack/src/monitor/events/home.ts: buildSlackHomeView",
"extensions/slack/src/monitor/events/messages.ts: formatSlackInboundLogLine",
"extensions/slack/src/monitor/inbound-delivery-state.ts: clearSlackInboundDeliveryStateForTest",
@@ -977,39 +886,27 @@ export const KNIP_UNUSED_EXPORT_BASELINE = [
"extensions/slack/src/monitor/message-handler/dispatch.ts: resolveSlackStreamRecipientTeamId",
"extensions/slack/src/monitor/message-handler/dispatch.ts: shouldEnableSlackPreviewStreaming",
"extensions/slack/src/monitor/message-handler/dispatch.ts: shouldInitializeSlackDraftStream",
"extensions/slack/src/monitor/message-handler/preflight-audio.ts: hasCaptionlessSlackAudio",
"extensions/slack/src/monitor/message-handler/prepare-routing.ts: __testing",
"extensions/slack/src/monitor/message-handler/prepare-routing.ts: SlackRoutingContextDeps",
"extensions/slack/src/monitor/message-handler/prepare-routing.ts: testing",
"extensions/slack/src/monitor/message-handler/prepare-thread-context-root.ts: SlackThreadRootCandidate",
"extensions/slack/src/monitor/message-handler/preview-finalize.ts: __testing",
"extensions/slack/src/monitor/message-handler/preview-finalize.ts: testing",
"extensions/slack/src/monitor/message-handler/subteam-mentions.ts: clearSlackSubteamMentionCacheForTest",
"extensions/slack/src/monitor/message-handler/subteam-mentions.ts: extractSlackSubteamMentionIds",
"extensions/slack/src/monitor/provider-support.ts: createSlackSocketModeLogger",
"extensions/slack/src/monitor/provider-support.ts: shouldSkipOpenClawSlackSelfEvent",
"extensions/slack/src/monitor/provider.ts: __testing",
"extensions/slack/src/monitor/provider.ts: formatSlackSocketReconnectMessage",
"extensions/slack/src/monitor/provider.ts: formatSlackSocketStartRetryMessage",
"extensions/slack/src/monitor/provider.ts: isNonRecoverableSlackAuthError",
"extensions/slack/src/monitor/provider.ts: testing",
"extensions/slack/src/monitor/replies.ts: SlackRespondFn",
"extensions/slack/src/monitor/replies.ts: SlackResponseUrlBudget",
"extensions/slack/src/monitor/reply.runtime.ts: createReplyDispatcherWithTyping",
"extensions/slack/src/monitor/reply.runtime.ts: dispatchInboundMessage",
"extensions/slack/src/monitor/reply.runtime.ts: settleReplyDispatcher",
"extensions/slack/src/monitor/response-url-budget.ts: SLACK_RESPONSE_URL_MAX_CALLS",
"extensions/slack/src/native-data-blocks.ts: renderSlackNativeDataFallbackText",
"extensions/slack/src/native-data-fallback.ts: SlackNativeDataDeliveryPlan",
"extensions/slack/src/reply-blocks.ts: resolveSlackReplyText",
"extensions/slack/src/runtime.ts: clearSlackRuntime",
"extensions/slack/src/send.ts: clearSlackDefaultSendIdentitiesForTest",
"extensions/slack/src/send.ts: clearSlackDmChannelCache",
"extensions/slack/src/shared.ts: setSlackChannelAllowlist",
"extensions/slack/src/streaming.ts: extractSlackErrorCode",
"extensions/slack/src/streaming.ts: isBenignSlackFinalizeError",
"extensions/slack/src/streaming.ts: StopSlackStreamResult",
"extensions/slack/src/targets.ts: slackTargetsMatch",
"extensions/sms/src/channel.ts: resolveSmsTextChunkLimit",
"extensions/sms/src/config-schema.ts: SmsConfigSchema",
"extensions/sms/src/gateway.ts: registerSmsWebhookRoute",
+3 -3
View File
@@ -106,11 +106,11 @@
"extensions/discord/src/monitor/native-command-model-picker-interaction.ts": 782,
"extensions/discord/src/monitor/native-command.ts": 736,
"extensions/discord/src/monitor/provider.lifecycle.ts": 583,
"extensions/discord/src/monitor/provider.ts": 691,
"extensions/discord/src/monitor/provider.ts": 690,
"extensions/discord/src/monitor/thread-bindings.manager.ts": 556,
"extensions/discord/src/monitor/thread-bindings.state.ts": 563,
"extensions/discord/src/send.outbound.ts": 515,
"extensions/discord/src/send.shared.ts": 557,
"extensions/discord/src/send.shared.ts": 554,
"extensions/discord/src/voice/manager.ts": 1917,
"extensions/discord/src/voice/realtime.ts": 1815,
"extensions/elevenlabs/speech-provider.ts": 596,
@@ -337,7 +337,7 @@
"extensions/slack/src/monitor/message-handler/dispatch.ts": 2232,
"extensions/slack/src/monitor/message-handler/prepare.ts": 1715,
"extensions/slack/src/monitor/provider-support.ts": 510,
"extensions/slack/src/monitor/provider.ts": 801,
"extensions/slack/src/monitor/provider.ts": 798,
"extensions/slack/src/monitor/replies.ts": 623,
"extensions/slack/src/monitor/slash.ts": 1098,
"extensions/slack/src/reply-blocks.ts": 505,