refactor(config): unify streaming config across channels

This commit is contained in:
Peter Steinberger
2026-02-21 19:53:23 +01:00
parent 747bb581b3
commit 2c14b0cf4c
26 changed files with 885 additions and 156 deletions

View File

@@ -1,6 +1,12 @@
import { z } from "zod";
import { isSafeScpRemoteHost } from "../infra/scp-host.js";
import { isValidInboundPathRootPattern } from "../media/inbound-path-policy.js";
import {
resolveDiscordPreviewStreamMode,
resolveSlackNativeStreaming,
resolveSlackStreamingMode,
resolveTelegramPreviewStreamMode,
} from "./discord-preview-streaming.js";
import {
normalizeTelegramCommandDescription,
normalizeTelegramCommandName,
@@ -99,25 +105,24 @@ const validateTelegramCustomCommands = (
}
};
function normalizeTelegramStreamingConfig(value: {
streaming?: boolean;
streamMode?: "off" | "partial" | "block";
function normalizeTelegramStreamingConfig(value: { streaming?: unknown; streamMode?: unknown }) {
value.streaming = resolveTelegramPreviewStreamMode(value);
delete value.streamMode;
}
function normalizeDiscordStreamingConfig(value: { streaming?: unknown; streamMode?: unknown }) {
value.streaming = resolveDiscordPreviewStreamMode(value);
delete value.streamMode;
}
function normalizeSlackStreamingConfig(value: {
streaming?: unknown;
nativeStreaming?: unknown;
streamMode?: unknown;
}) {
if (typeof value.streaming === "boolean") {
delete value.streamMode;
return;
}
if (value.streamMode === "off") {
value.streaming = false;
delete value.streamMode;
return;
}
if (value.streamMode === "partial" || value.streamMode === "block") {
value.streaming = true;
delete value.streamMode;
return;
}
value.streaming = false;
value.nativeStreaming = resolveSlackNativeStreaming(value);
value.streaming = resolveSlackStreamingMode(value);
delete value.streamMode;
}
export const TelegramAccountSchemaBase = z
@@ -143,7 +148,7 @@ export const TelegramAccountSchemaBase = z
dms: z.record(z.string(), DmConfigSchema.optional()).optional(),
textChunkLimit: z.number().int().positive().optional(),
chunkMode: z.enum(["length", "newline"]).optional(),
streaming: z.boolean().optional(),
streaming: z.union([z.boolean(), z.enum(["off", "partial", "block", "progress"])]).optional(),
blockStreaming: z.boolean().optional(),
draftChunk: BlockStreamingChunkSchema.optional(),
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
@@ -332,7 +337,9 @@ export const DiscordAccountSchema = z
chunkMode: z.enum(["length", "newline"]).optional(),
blockStreaming: z.boolean().optional(),
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
streamMode: z.enum(["partial", "block", "off"]).optional().default("off"),
// Canonical streaming mode. Legacy aliases (`streamMode`, boolean `streaming`) are auto-mapped.
streaming: z.union([z.boolean(), z.enum(["off", "partial", "block", "progress"])]).optional(),
streamMode: z.enum(["partial", "block", "off"]).optional(),
draftChunk: BlockStreamingChunkSchema.optional(),
maxLinesPerMessage: z.number().int().positive().optional(),
mediaMaxMb: z.number().positive().optional(),
@@ -422,6 +429,8 @@ export const DiscordAccountSchema = z
})
.strict()
.superRefine((value, ctx) => {
normalizeDiscordStreamingConfig(value);
const activityText = typeof value.activity === "string" ? value.activity.trim() : "";
const hasActivity = Boolean(activityText);
const hasActivityType = value.activityType !== undefined;
@@ -610,7 +619,9 @@ export const SlackAccountSchema = z
chunkMode: z.enum(["length", "newline"]).optional(),
blockStreaming: z.boolean().optional(),
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
streaming: z.boolean().optional(),
streaming: z.union([z.boolean(), z.enum(["off", "partial", "block", "progress"])]).optional(),
nativeStreaming: z.boolean().optional(),
streamMode: z.enum(["replace", "status_final", "append"]).optional(),
mediaMaxMb: z.number().positive().optional(),
reactionNotifications: z.enum(["off", "own", "all", "allowlist"]).optional(),
reactionAllowlist: z.array(z.union([z.string(), z.number()])).optional(),
@@ -652,6 +663,8 @@ export const SlackAccountSchema = z
})
.strict()
.superRefine((value, ctx) => {
normalizeSlackStreamingConfig(value);
const dmPolicy = value.dmPolicy ?? value.dm?.policy ?? "pairing";
const allowFrom = value.allowFrom ?? value.dm?.allowFrom;
const allowFromPath =