refactor!: rename chat providers to channels

This commit is contained in:
Peter Steinberger
2026-01-13 06:16:43 +00:00
parent 0cd632ba84
commit 90342a4f3a
393 changed files with 8004 additions and 6737 deletions

View File

@@ -1,5 +1,5 @@
import { randomUUID } from "node:crypto";
import { DEFAULT_CHAT_CHANNEL } from "../../channels/registry.js";
import { agentCommand } from "../../commands/agent.js";
import { loadConfig } from "../../config/config.js";
import {
@@ -10,15 +10,14 @@ import {
} from "../../config/sessions.js";
import { registerAgentRunContext } from "../../infra/agent-events.js";
import { resolveOutboundTarget } from "../../infra/outbound/targets.js";
import { DEFAULT_CHAT_PROVIDER } from "../../providers/registry.js";
import { defaultRuntime } from "../../runtime.js";
import { resolveSendPolicy } from "../../sessions/send-policy.js";
import {
INTERNAL_MESSAGE_PROVIDER,
isDeliverableMessageProvider,
isGatewayMessageProvider,
normalizeMessageProvider,
} from "../../utils/message-provider.js";
INTERNAL_MESSAGE_CHANNEL,
isDeliverableMessageChannel,
isGatewayMessageChannel,
normalizeMessageChannel,
} from "../../utils/message-channel.js";
import { parseMessageWithAttachments } from "../chat-attachments.js";
import {
type AgentWaitParams,
@@ -60,7 +59,7 @@ export const agentHandlers: GatewayRequestHandlers = {
fileName?: string;
content?: unknown;
}>;
provider?: string;
channel?: string;
lane?: string;
extraSystemPrompt?: string;
idempotencyKey: string;
@@ -115,21 +114,21 @@ export const agentHandlers: GatewayRequestHandlers = {
return;
}
}
const rawProvider =
typeof request.provider === "string" ? request.provider.trim() : "";
if (rawProvider) {
const normalized = normalizeMessageProvider(rawProvider);
const rawChannel =
typeof request.channel === "string" ? request.channel.trim() : "";
if (rawChannel) {
const normalized = normalizeMessageChannel(rawChannel);
if (
normalized &&
normalized !== "last" &&
!isGatewayMessageProvider(normalized)
!isGatewayMessageChannel(normalized)
) {
respond(
false,
undefined,
errorShape(
ErrorCodes.INVALID_REQUEST,
`invalid agent params: unknown provider: ${normalized}`,
`invalid agent params: unknown channel: ${normalized}`,
),
);
return;
@@ -162,7 +161,7 @@ export const agentHandlers: GatewayRequestHandlers = {
systemSent: entry?.systemSent,
sendPolicy: entry?.sendPolicy,
skillsSnapshot: entry?.skillsSnapshot,
lastProvider: entry?.lastProvider,
lastChannel: entry?.lastChannel,
lastTo: entry?.lastTo,
modelOverride: entry?.modelOverride,
providerOverride: entry?.providerOverride,
@@ -174,7 +173,7 @@ export const agentHandlers: GatewayRequestHandlers = {
cfg,
entry,
sessionKey: requestedSessionKey,
provider: entry?.provider,
channel: entry?.channel,
chatType: entry?.chatType,
});
if (sendPolicy === "deny") {
@@ -213,10 +212,9 @@ export const agentHandlers: GatewayRequestHandlers = {
const runId = idem;
const requestedProvider =
normalizeMessageProvider(request.provider) ?? "last";
const requestedChannel = normalizeMessageChannel(request.channel) ?? "last";
const lastProvider = sessionEntry?.lastProvider;
const lastChannel = sessionEntry?.lastChannel;
const lastTo =
typeof sessionEntry?.lastTo === "string"
? sessionEntry.lastTo.trim()
@@ -224,24 +222,22 @@ export const agentHandlers: GatewayRequestHandlers = {
const wantsDelivery = request.deliver === true;
const resolvedProvider = (() => {
if (requestedProvider === "last") {
const resolvedChannel = (() => {
if (requestedChannel === "last") {
// WebChat is not a deliverable surface. Treat it as "unset" for routing,
// so VoiceWake and CLI callers don't get stuck with deliver=false.
if (lastProvider && lastProvider !== INTERNAL_MESSAGE_PROVIDER) {
return lastProvider;
if (lastChannel && lastChannel !== INTERNAL_MESSAGE_CHANNEL) {
return lastChannel;
}
return wantsDelivery
? DEFAULT_CHAT_PROVIDER
: INTERNAL_MESSAGE_PROVIDER;
return wantsDelivery ? DEFAULT_CHAT_CHANNEL : INTERNAL_MESSAGE_CHANNEL;
}
if (isGatewayMessageProvider(requestedProvider)) return requestedProvider;
if (isGatewayMessageChannel(requestedChannel)) return requestedChannel;
if (lastProvider && lastProvider !== INTERNAL_MESSAGE_PROVIDER) {
return lastProvider;
if (lastChannel && lastChannel !== INTERNAL_MESSAGE_CHANNEL) {
return lastChannel;
}
return wantsDelivery ? DEFAULT_CHAT_PROVIDER : INTERNAL_MESSAGE_PROVIDER;
return wantsDelivery ? DEFAULT_CHAT_CHANNEL : INTERNAL_MESSAGE_CHANNEL;
})();
const explicitTo =
@@ -250,18 +246,18 @@ export const agentHandlers: GatewayRequestHandlers = {
: undefined;
const deliveryTargetMode = explicitTo
? "explicit"
: isDeliverableMessageProvider(resolvedProvider)
: isDeliverableMessageChannel(resolvedChannel)
? "implicit"
: undefined;
let resolvedTo =
explicitTo ||
(isDeliverableMessageProvider(resolvedProvider)
(isDeliverableMessageChannel(resolvedChannel)
? lastTo || undefined
: undefined);
if (!resolvedTo && isDeliverableMessageProvider(resolvedProvider)) {
if (!resolvedTo && isDeliverableMessageChannel(resolvedChannel)) {
const cfg = cfgForAgent ?? loadConfig();
const fallback = resolveOutboundTarget({
provider: resolvedProvider,
channel: resolvedChannel,
cfg,
accountId: sessionEntry?.lastAccountId ?? undefined,
mode: "implicit",
@@ -272,8 +268,7 @@ export const agentHandlers: GatewayRequestHandlers = {
}
const deliver =
request.deliver === true &&
resolvedProvider !== INTERNAL_MESSAGE_PROVIDER;
request.deliver === true && resolvedChannel !== INTERNAL_MESSAGE_CHANNEL;
const accepted = {
runId,
@@ -298,10 +293,10 @@ export const agentHandlers: GatewayRequestHandlers = {
thinking: request.thinking,
deliver,
deliveryTargetMode,
provider: resolvedProvider,
channel: resolvedChannel,
timeout: request.timeout?.toString(),
bestEffortDeliver,
messageProvider: resolvedProvider,
messageChannel: resolvedChannel,
runId,
lane: request.lane,
extraSystemPrompt: request.extraSystemPrompt,

View File

@@ -1,44 +1,44 @@
import { resolveChannelDefaultAccountId } from "../../channels/plugins/helpers.js";
import {
type ChannelId,
getChannelPlugin,
listChannelPlugins,
normalizeChannelId,
} from "../../channels/plugins/index.js";
import { buildChannelAccountSnapshot } from "../../channels/plugins/status.js";
import type {
ChannelAccountSnapshot,
ChannelPlugin,
} from "../../channels/plugins/types.js";
import type { ClawdbotConfig } from "../../config/config.js";
import { loadConfig, readConfigFileSnapshot } from "../../config/config.js";
import { getProviderActivity } from "../../infra/provider-activity.js";
import { resolveProviderDefaultAccountId } from "../../providers/plugins/helpers.js";
import {
getProviderPlugin,
listProviderPlugins,
normalizeProviderId,
type ProviderId,
} from "../../providers/plugins/index.js";
import { buildProviderAccountSnapshot } from "../../providers/plugins/status.js";
import type {
ProviderAccountSnapshot,
ProviderPlugin,
} from "../../providers/plugins/types.js";
import { getChannelActivity } from "../../infra/channel-activity.js";
import { DEFAULT_ACCOUNT_ID } from "../../routing/session-key.js";
import { defaultRuntime } from "../../runtime.js";
import {
ErrorCodes,
errorShape,
formatValidationErrors,
validateProvidersLogoutParams,
validateProvidersStatusParams,
validateChannelsLogoutParams,
validateChannelsStatusParams,
} from "../protocol/index.js";
import { formatForLog } from "../ws-log.js";
import type { GatewayRequestContext, GatewayRequestHandlers } from "./types.js";
type ProviderLogoutPayload = {
provider: ProviderId;
type ChannelLogoutPayload = {
channel: ChannelId;
accountId: string;
cleared: boolean;
[key: string]: unknown;
};
export async function logoutProviderAccount(params: {
providerId: ProviderId;
export async function logoutChannelAccount(params: {
channelId: ChannelId;
accountId?: string | null;
cfg: ClawdbotConfig;
context: GatewayRequestContext;
plugin: ProviderPlugin;
}): Promise<ProviderLogoutPayload> {
plugin: ChannelPlugin;
}): Promise<ChannelLogoutPayload> {
const resolvedAccountId =
params.accountId?.trim() ||
params.plugin.config.defaultAccountId?.(params.cfg) ||
@@ -48,7 +48,7 @@ export async function logoutProviderAccount(params: {
params.cfg,
resolvedAccountId,
);
await params.context.stopProvider(params.providerId, resolvedAccountId);
await params.context.stopChannel(params.channelId, resolvedAccountId);
const result = await params.plugin.gateway?.logoutAccount?.({
cfg: params.cfg,
accountId: resolvedAccountId,
@@ -56,35 +56,35 @@ export async function logoutProviderAccount(params: {
runtime: defaultRuntime,
});
if (!result) {
throw new Error(`Provider ${params.providerId} does not support logout`);
throw new Error(`Channel ${params.channelId} does not support logout`);
}
const cleared = Boolean(result.cleared);
const loggedOut =
typeof result.loggedOut === "boolean" ? result.loggedOut : cleared;
if (loggedOut) {
params.context.markProviderLoggedOut(
params.providerId,
params.context.markChannelLoggedOut(
params.channelId,
true,
resolvedAccountId,
);
}
return {
provider: params.providerId,
channel: params.channelId,
accountId: resolvedAccountId,
...result,
cleared,
};
}
export const providersHandlers: GatewayRequestHandlers = {
"providers.status": async ({ params, respond, context }) => {
if (!validateProvidersStatusParams(params)) {
export const channelsHandlers: GatewayRequestHandlers = {
"channels.status": async ({ params, respond, context }) => {
if (!validateChannelsStatusParams(params)) {
respond(
false,
undefined,
errorShape(
ErrorCodes.INVALID_REQUEST,
`invalid providers.status params: ${formatValidationErrors(validateProvidersStatusParams.errors)}`,
`invalid channels.status params: ${formatValidationErrors(validateChannelsStatusParams.errors)}`,
),
);
return;
@@ -95,18 +95,18 @@ export const providersHandlers: GatewayRequestHandlers = {
typeof timeoutMsRaw === "number" ? Math.max(1000, timeoutMsRaw) : 10_000;
const cfg = loadConfig();
const runtime = context.getRuntimeSnapshot();
const plugins = listProviderPlugins();
const pluginMap = new Map<ProviderId, ProviderPlugin>(
const plugins = listChannelPlugins();
const pluginMap = new Map<ChannelId, ChannelPlugin>(
plugins.map((plugin) => [plugin.id, plugin]),
);
const resolveRuntimeSnapshot = (
providerId: ProviderId,
channelId: ChannelId,
accountId: string,
defaultAccountId: string,
): ProviderAccountSnapshot | undefined => {
const accounts = runtime.providerAccounts[providerId];
const defaultRuntime = runtime.providers[providerId];
): ChannelAccountSnapshot | undefined => {
const accounts = runtime.channelAccounts[channelId];
const defaultRuntime = runtime.channels[channelId];
const raw =
accounts?.[accountId] ??
(accountId === defaultAccountId ? defaultRuntime : undefined);
@@ -114,30 +114,30 @@ export const providersHandlers: GatewayRequestHandlers = {
return raw;
};
const isAccountEnabled = (plugin: ProviderPlugin, account: unknown) =>
const isAccountEnabled = (plugin: ChannelPlugin, account: unknown) =>
plugin.config.isEnabled
? plugin.config.isEnabled(account, cfg)
: !account ||
typeof account !== "object" ||
(account as { enabled?: boolean }).enabled !== false;
const buildProviderAccounts = async (providerId: ProviderId) => {
const plugin = pluginMap.get(providerId);
const buildChannelAccounts = async (channelId: ChannelId) => {
const plugin = pluginMap.get(channelId);
if (!plugin) {
return {
accounts: [] as ProviderAccountSnapshot[],
accounts: [] as ChannelAccountSnapshot[],
defaultAccountId: DEFAULT_ACCOUNT_ID,
defaultAccount: undefined as ProviderAccountSnapshot | undefined,
defaultAccount: undefined as ChannelAccountSnapshot | undefined,
resolvedAccounts: {} as Record<string, unknown>,
};
}
const accountIds = plugin.config.listAccountIds(cfg);
const defaultAccountId = resolveProviderDefaultAccountId({
const defaultAccountId = resolveChannelDefaultAccountId({
plugin,
cfg,
accountIds,
});
const accounts: ProviderAccountSnapshot[] = [];
const accounts: ChannelAccountSnapshot[] = [];
const resolvedAccounts: Record<string, unknown> = {};
for (const accountId of accountIds) {
const account = plugin.config.resolveAccount(cfg, accountId);
@@ -175,11 +175,11 @@ export const providersHandlers: GatewayRequestHandlers = {
}
}
const runtimeSnapshot = resolveRuntimeSnapshot(
providerId,
channelId,
accountId,
defaultAccountId,
);
const snapshot = await buildProviderAccountSnapshot({
const snapshot = await buildChannelAccountSnapshot({
plugin,
cfg,
accountId,
@@ -188,8 +188,8 @@ export const providersHandlers: GatewayRequestHandlers = {
audit: auditResult,
});
if (lastProbeAt) snapshot.lastProbeAt = lastProbeAt;
const activity = getProviderActivity({
provider: providerId as never,
const activity = getChannelActivity({
channel: channelId as never,
accountId,
});
if (snapshot.lastInboundAt == null) {
@@ -208,28 +208,28 @@ export const providersHandlers: GatewayRequestHandlers = {
const payload: Record<string, unknown> = {
ts: Date.now(),
providerOrder: plugins.map((plugin) => plugin.id),
providerLabels: Object.fromEntries(
channelOrder: plugins.map((plugin) => plugin.id),
channelLabels: Object.fromEntries(
plugins.map((plugin) => [plugin.id, plugin.meta.label]),
),
providers: {} as Record<string, unknown>,
providerAccounts: {} as Record<string, unknown>,
providerDefaultAccountId: {} as Record<string, unknown>,
channels: {} as Record<string, unknown>,
channelAccounts: {} as Record<string, unknown>,
channelDefaultAccountId: {} as Record<string, unknown>,
};
const providersMap = payload.providers as Record<string, unknown>;
const accountsMap = payload.providerAccounts as Record<string, unknown>;
const defaultAccountIdMap = payload.providerDefaultAccountId as Record<
const channelsMap = payload.channels as Record<string, unknown>;
const accountsMap = payload.channelAccounts as Record<string, unknown>;
const defaultAccountIdMap = payload.channelDefaultAccountId as Record<
string,
unknown
>;
for (const plugin of plugins) {
const { accounts, defaultAccountId, defaultAccount, resolvedAccounts } =
await buildProviderAccounts(plugin.id);
await buildChannelAccounts(plugin.id);
const fallbackAccount =
resolvedAccounts[defaultAccountId] ??
plugin.config.resolveAccount(cfg, defaultAccountId);
const summary = plugin.status?.buildProviderSummary
? await plugin.status.buildProviderSummary({
const summary = plugin.status?.buildChannelSummary
? await plugin.status.buildChannelSummary({
account: fallbackAccount,
cfg,
defaultAccountId,
@@ -237,52 +237,52 @@ export const providersHandlers: GatewayRequestHandlers = {
defaultAccount ??
({
accountId: defaultAccountId,
} as ProviderAccountSnapshot),
} as ChannelAccountSnapshot),
})
: {
configured: defaultAccount?.configured ?? false,
};
providersMap[plugin.id] = summary;
channelsMap[plugin.id] = summary;
accountsMap[plugin.id] = accounts;
defaultAccountIdMap[plugin.id] = defaultAccountId;
}
respond(true, payload, undefined);
},
"providers.logout": async ({ params, respond, context }) => {
if (!validateProvidersLogoutParams(params)) {
"channels.logout": async ({ params, respond, context }) => {
if (!validateChannelsLogoutParams(params)) {
respond(
false,
undefined,
errorShape(
ErrorCodes.INVALID_REQUEST,
`invalid providers.logout params: ${formatValidationErrors(validateProvidersLogoutParams.errors)}`,
`invalid channels.logout params: ${formatValidationErrors(validateChannelsLogoutParams.errors)}`,
),
);
return;
}
const rawProvider = (params as { provider?: unknown }).provider;
const providerId =
typeof rawProvider === "string" ? normalizeProviderId(rawProvider) : null;
if (!providerId) {
const rawChannel = (params as { channel?: unknown }).channel;
const channelId =
typeof rawChannel === "string" ? normalizeChannelId(rawChannel) : null;
if (!channelId) {
respond(
false,
undefined,
errorShape(
ErrorCodes.INVALID_REQUEST,
"invalid providers.logout provider",
"invalid channels.logout channel",
),
);
return;
}
const plugin = getProviderPlugin(providerId);
const plugin = getChannelPlugin(channelId);
if (!plugin?.gateway?.logoutAccount) {
respond(
false,
undefined,
errorShape(
ErrorCodes.INVALID_REQUEST,
`provider ${providerId} does not support logout`,
`channel ${channelId} does not support logout`,
),
);
return;
@@ -303,8 +303,8 @@ export const providersHandlers: GatewayRequestHandlers = {
return;
}
try {
const payload = await logoutProviderAccount({
providerId,
const payload = await logoutChannelAccount({
channelId,
accountId,
cfg: snapshot.config ?? {},
context,

View File

@@ -7,7 +7,7 @@ import { mergeSessionEntry, saveSessionStore } from "../../config/sessions.js";
import { registerAgentRunContext } from "../../infra/agent-events.js";
import { defaultRuntime } from "../../runtime.js";
import { resolveSendPolicy } from "../../sessions/send-policy.js";
import { INTERNAL_MESSAGE_PROVIDER } from "../../utils/message-provider.js";
import { INTERNAL_MESSAGE_CHANNEL } from "../../utils/message-channel.js";
import {
abortChatRunById,
abortChatRunsForSessionKey,
@@ -242,7 +242,7 @@ export const chatHandlers: GatewayRequestHandlers = {
cfg,
entry,
sessionKey: p.sessionKey,
provider: entry?.provider,
channel: entry?.channel,
chatType: entry?.chatType,
});
if (sendPolicy === "deny") {
@@ -331,7 +331,7 @@ export const chatHandlers: GatewayRequestHandlers = {
thinking: p.thinking,
deliver: p.deliver,
timeout: Math.ceil(timeoutMs / 1000).toString(),
messageProvider: INTERNAL_MESSAGE_PROVIDER,
messageChannel: INTERNAL_MESSAGE_CHANNEL,
abortSignal: abortController.signal,
},
defaultRuntime,

View File

@@ -1,14 +1,14 @@
import {
getChannelPlugin,
normalizeChannelId,
} from "../../channels/plugins/index.js";
import type { ChannelId } from "../../channels/plugins/types.js";
import { DEFAULT_CHAT_CHANNEL } from "../../channels/registry.js";
import { loadConfig } from "../../config/config.js";
import { deliverOutboundPayloads } from "../../infra/outbound/deliver.js";
import type { OutboundProvider } from "../../infra/outbound/targets.js";
import type { OutboundChannel } from "../../infra/outbound/targets.js";
import { resolveOutboundTarget } from "../../infra/outbound/targets.js";
import { normalizePollInput } from "../../polls.js";
import {
getProviderPlugin,
normalizeProviderId,
} from "../../providers/plugins/index.js";
import type { ProviderId } from "../../providers/plugins/types.js";
import { DEFAULT_CHAT_PROVIDER } from "../../providers/registry.js";
import {
ErrorCodes,
errorShape,
@@ -38,7 +38,7 @@ export const sendHandlers: GatewayRequestHandlers = {
message: string;
mediaUrl?: string;
gifPlayback?: boolean;
provider?: string;
channel?: string;
accountId?: string;
idempotencyKey: string;
};
@@ -52,44 +52,44 @@ export const sendHandlers: GatewayRequestHandlers = {
}
const to = request.to.trim();
const message = request.message.trim();
const providerInput =
typeof request.provider === "string" ? request.provider : undefined;
const normalizedProvider = providerInput
? normalizeProviderId(providerInput)
const channelInput =
typeof request.channel === "string" ? request.channel : undefined;
const normalizedChannel = channelInput
? normalizeChannelId(channelInput)
: null;
if (providerInput && !normalizedProvider) {
if (channelInput && !normalizedChannel) {
respond(
false,
undefined,
errorShape(
ErrorCodes.INVALID_REQUEST,
`unsupported provider: ${providerInput}`,
`unsupported channel: ${channelInput}`,
),
);
return;
}
const provider = normalizedProvider ?? DEFAULT_CHAT_PROVIDER;
const channel = normalizedChannel ?? DEFAULT_CHAT_CHANNEL;
const accountId =
typeof request.accountId === "string" && request.accountId.trim().length
? request.accountId.trim()
: undefined;
try {
const outboundProvider = provider as Exclude<OutboundProvider, "none">;
const plugin = getProviderPlugin(provider as ProviderId);
const outboundChannel = channel as Exclude<OutboundChannel, "none">;
const plugin = getChannelPlugin(channel as ChannelId);
if (!plugin) {
respond(
false,
undefined,
errorShape(
ErrorCodes.INVALID_REQUEST,
`unsupported provider: ${provider}`,
`unsupported channel: ${channel}`,
),
);
return;
}
const cfg = loadConfig();
const resolved = resolveOutboundTarget({
provider: outboundProvider,
channel: outboundChannel,
to,
cfg,
accountId,
@@ -105,7 +105,7 @@ export const sendHandlers: GatewayRequestHandlers = {
}
const results = await deliverOutboundPayloads({
cfg,
provider: outboundProvider,
channel: outboundChannel,
to: resolved.to,
accountId,
payloads: [{ text: message, mediaUrl: request.mediaUrl }],
@@ -118,7 +118,7 @@ export const sendHandlers: GatewayRequestHandlers = {
const payload: Record<string, unknown> = {
runId: idem,
messageId: result.messageId,
provider,
channel,
};
if ("chatId" in result) payload.chatId = result.chatId;
if ("channelId" in result) payload.channelId = result.channelId;
@@ -131,7 +131,7 @@ export const sendHandlers: GatewayRequestHandlers = {
ok: true,
payload,
});
respond(true, payload, undefined, { provider });
respond(true, payload, undefined, { channel });
} catch (err) {
const error = errorShape(ErrorCodes.UNAVAILABLE, String(err));
context.dedupe.set(`send:${idem}`, {
@@ -139,10 +139,7 @@ export const sendHandlers: GatewayRequestHandlers = {
ok: false,
error,
});
respond(false, undefined, error, {
provider,
error: formatForLog(err),
});
respond(false, undefined, error, { channel, error: formatForLog(err) });
}
},
poll: async ({ params, respond, context }) => {
@@ -164,7 +161,7 @@ export const sendHandlers: GatewayRequestHandlers = {
options: string[];
maxSelections?: number;
durationHours?: number;
provider?: string;
channel?: string;
accountId?: string;
idempotencyKey: string;
};
@@ -177,23 +174,23 @@ export const sendHandlers: GatewayRequestHandlers = {
return;
}
const to = request.to.trim();
const providerInput =
typeof request.provider === "string" ? request.provider : undefined;
const normalizedProvider = providerInput
? normalizeProviderId(providerInput)
const channelInput =
typeof request.channel === "string" ? request.channel : undefined;
const normalizedChannel = channelInput
? normalizeChannelId(channelInput)
: null;
if (providerInput && !normalizedProvider) {
if (channelInput && !normalizedChannel) {
respond(
false,
undefined,
errorShape(
ErrorCodes.INVALID_REQUEST,
`unsupported poll provider: ${providerInput}`,
`unsupported poll channel: ${channelInput}`,
),
);
return;
}
const provider = normalizedProvider ?? DEFAULT_CHAT_PROVIDER;
const channel = normalizedChannel ?? DEFAULT_CHAT_CHANNEL;
const poll = {
question: request.question,
options: request.options,
@@ -205,7 +202,7 @@ export const sendHandlers: GatewayRequestHandlers = {
? request.accountId.trim()
: undefined;
try {
const plugin = getProviderPlugin(provider as ProviderId);
const plugin = getChannelPlugin(channel as ChannelId);
const outbound = plugin?.outbound;
if (!outbound?.sendPoll) {
respond(
@@ -213,14 +210,14 @@ export const sendHandlers: GatewayRequestHandlers = {
undefined,
errorShape(
ErrorCodes.INVALID_REQUEST,
`unsupported poll provider: ${provider}`,
`unsupported poll channel: ${channel}`,
),
);
return;
}
const cfg = loadConfig();
const resolved = resolveOutboundTarget({
provider: provider as Exclude<OutboundProvider, "none">,
channel: channel as Exclude<OutboundChannel, "none">,
to,
cfg,
accountId,
@@ -246,7 +243,7 @@ export const sendHandlers: GatewayRequestHandlers = {
const payload: Record<string, unknown> = {
runId: idem,
messageId: result.messageId,
provider,
channel,
};
if (result.toJid) payload.toJid = result.toJid;
if (result.channelId) payload.channelId = result.channelId;
@@ -257,7 +254,7 @@ export const sendHandlers: GatewayRequestHandlers = {
ok: true,
payload,
});
respond(true, payload, undefined, { provider });
respond(true, payload, undefined, { channel });
} catch (err) {
const error = errorShape(ErrorCodes.UNAVAILABLE, String(err));
context.dedupe.set(`poll:${idem}`, {
@@ -266,7 +263,7 @@ export const sendHandlers: GatewayRequestHandlers = {
error,
});
respond(false, undefined, error, {
provider,
channel,
error: formatForLog(err),
});
}

View File

@@ -186,7 +186,7 @@ export const sessionsHandlers: GatewayRequestHandlers = {
contextTokens: entry?.contextTokens,
sendPolicy: entry?.sendPolicy,
label: entry?.label,
lastProvider: entry?.lastProvider,
lastChannel: entry?.lastChannel,
lastTo: entry?.lastTo,
skillsSnapshot: entry?.skillsSnapshot,
};

View File

@@ -10,7 +10,7 @@ import type {
ErrorShape,
RequestFrame,
} from "../protocol/index.js";
import type { ProviderRuntimeSnapshot } from "../server-providers.js";
import type { ChannelRuntimeSnapshot } from "../server-channels.js";
import type { DedupeEntry } from "../server-shared.js";
export type GatewayClient = {
@@ -68,17 +68,17 @@ export type GatewayRequestContext = {
wizardSessions: Map<string, WizardSession>;
findRunningWizard: () => string | null;
purgeWizardSession: (id: string) => void;
getRuntimeSnapshot: () => ProviderRuntimeSnapshot;
startProvider: (
provider: import("../../providers/plugins/types.js").ProviderId,
getRuntimeSnapshot: () => ChannelRuntimeSnapshot;
startChannel: (
channel: import("../../channels/plugins/types.js").ChannelId,
accountId?: string,
) => Promise<void>;
stopProvider: (
provider: import("../../providers/plugins/types.js").ProviderId,
stopChannel: (
channel: import("../../channels/plugins/types.js").ChannelId,
accountId?: string,
) => Promise<void>;
markProviderLoggedOut: (
providerId: import("../../providers/plugins/types.js").ProviderId,
markChannelLoggedOut: (
channelId: import("../../channels/plugins/types.js").ChannelId,
cleared: boolean,
accountId?: string,
) => void;

View File

@@ -1,4 +1,4 @@
import { listProviderPlugins } from "../../providers/plugins/index.js";
import { listChannelPlugins } from "../../channels/plugins/index.js";
import {
ErrorCodes,
errorShape,
@@ -12,7 +12,7 @@ import type { GatewayRequestHandlers } from "./types.js";
const WEB_LOGIN_METHODS = new Set(["web.login.start", "web.login.wait"]);
const resolveWebLoginProvider = () =>
listProviderPlugins().find((plugin) =>
listChannelPlugins().find((plugin) =>
(plugin.gatewayMethods ?? []).some((method) =>
WEB_LOGIN_METHODS.has(method),
),
@@ -48,7 +48,7 @@ export const webHandlers: GatewayRequestHandlers = {
);
return;
}
await context.stopProvider(provider.id, accountId);
await context.stopChannel(provider.id, accountId);
if (!provider.gateway?.loginWithQrStart) {
respond(
false,
@@ -126,7 +126,7 @@ export const webHandlers: GatewayRequestHandlers = {
accountId,
});
if (result.connected) {
await context.startProvider(provider.id, accountId);
await context.startChannel(provider.id, accountId);
}
respond(true, result, undefined);
} catch (err) {