fix: newline chunking across channels

This commit is contained in:
Peter Steinberger
2026-01-25 04:05:14 +00:00
parent ca78ccf74c
commit 458e731f8b
80 changed files with 580 additions and 91 deletions

View File

@@ -9,18 +9,21 @@ import {
} from "./messenger.js";
import { setMSTeamsRuntime } from "./runtime.js";
const chunkMarkdownText = (text: string, limit: number) => {
if (!text) return [];
if (limit <= 0 || text.length <= limit) return [text];
const chunks: string[] = [];
for (let index = 0; index < text.length; index += limit) {
chunks.push(text.slice(index, index + limit));
}
return chunks;
};
const runtimeStub = {
channel: {
text: {
chunkMarkdownText: (text: string, limit: number) => {
if (!text) return [];
if (limit <= 0 || text.length <= limit) return [text];
const chunks: string[] = [];
for (let index = 0; index < text.length; index += limit) {
chunks.push(text.slice(index, index + limit));
}
return chunks;
},
chunkMarkdownText,
chunkMarkdownTextWithMode: chunkMarkdownText,
resolveMarkdownTableMode: () => "code",
convertMarkdownTables: (text: string) => text,
},

View File

@@ -1,4 +1,5 @@
import {
type ChunkMode,
isSilentReplyText,
loadWebMedia,
type MarkdownTableMode,
@@ -63,6 +64,7 @@ export type MSTeamsReplyRenderOptions = {
chunkText?: boolean;
mediaMode?: "split" | "inline";
tableMode?: MarkdownTableMode;
chunkMode?: ChunkMode;
};
/**
@@ -129,11 +131,16 @@ function pushTextMessages(
opts: {
chunkText: boolean;
chunkLimit: number;
chunkMode: ChunkMode;
},
) {
if (!text) return;
if (opts.chunkText) {
for (const chunk of getMSTeamsRuntime().channel.text.chunkMarkdownText(text, opts.chunkLimit)) {
for (const chunk of getMSTeamsRuntime().channel.text.chunkMarkdownTextWithMode(
text,
opts.chunkLimit,
opts.chunkMode,
)) {
const trimmed = chunk.trim();
if (!trimmed || isSilentReplyText(trimmed, SILENT_REPLY_TOKEN)) continue;
out.push({ text: trimmed });
@@ -197,6 +204,7 @@ export function renderReplyPayloadsToMessages(
const out: MSTeamsRenderedMessage[] = [];
const chunkLimit = Math.min(options.textChunkLimit, 4000);
const chunkText = options.chunkText !== false;
const chunkMode = options.chunkMode ?? "length";
const mediaMode = options.mediaMode ?? "split";
const tableMode =
options.tableMode ??
@@ -215,7 +223,7 @@ export function renderReplyPayloadsToMessages(
if (!text && mediaList.length === 0) continue;
if (mediaList.length === 0) {
pushTextMessages(out, text, { chunkText, chunkLimit });
pushTextMessages(out, text, { chunkText, chunkLimit, chunkMode });
continue;
}
@@ -229,13 +237,13 @@ export function renderReplyPayloadsToMessages(
if (mediaList[i]) out.push({ mediaUrl: mediaList[i] });
}
} else {
pushTextMessages(out, text, { chunkText, chunkLimit });
pushTextMessages(out, text, { chunkText, chunkLimit, chunkMode });
}
continue;
}
// mediaMode === "split"
pushTextMessages(out, text, { chunkText, chunkLimit });
pushTextMessages(out, text, { chunkText, chunkLimit, chunkMode });
for (const mediaUrl of mediaList) {
if (!mediaUrl) continue;
out.push({ mediaUrl });

View File

@@ -7,6 +7,7 @@ import { sendMessageMSTeams, sendPollMSTeams } from "./send.js";
export const msteamsOutbound: ChannelOutboundAdapter = {
deliveryMode: "direct",
chunker: (text, limit) => getMSTeamsRuntime().channel.text.chunkMarkdownText(text, limit),
chunkerMode: "markdown",
textChunkLimit: 4000,
pollMaxOptions: 12,
sendText: async ({ cfg, to, text, deps }) => {

View File

@@ -59,6 +59,7 @@ export function createMSTeamsReplyDispatcher(params: {
cfg: params.cfg,
agentId: params.agentId,
});
const chunkMode = core.channel.text.resolveChunkMode(params.cfg, "msteams");
const { dispatcher, replyOptions, markDispatchIdle } =
core.channel.reply.createReplyDispatcherWithTyping({
@@ -75,6 +76,7 @@ export function createMSTeamsReplyDispatcher(params: {
chunkText: true,
mediaMode: "split",
tableMode,
chunkMode,
});
const mediaMaxBytes = resolveChannelMediaMaxBytes({
cfg: params.cfg,