feat: unify provider history context

This commit is contained in:
Peter Steinberger
2026-01-10 18:53:33 +01:00
parent 8c1d39064d
commit d41372b9d9
19 changed files with 718 additions and 80 deletions

View File

@@ -1225,6 +1225,34 @@ describe("legacy config detection", () => {
}
});
it("accepts historyLimit overrides per provider and account", async () => {
vi.resetModules();
const { validateConfigObject } = await import("./config.js");
const res = validateConfigObject({
messages: { groupChat: { historyLimit: 12 } },
whatsapp: { historyLimit: 9, accounts: { work: { historyLimit: 4 } } },
telegram: { historyLimit: 8, accounts: { ops: { historyLimit: 3 } } },
slack: { historyLimit: 7, accounts: { ops: { historyLimit: 2 } } },
signal: { historyLimit: 6 },
imessage: { historyLimit: 5 },
msteams: { historyLimit: 4 },
discord: { historyLimit: 3 },
});
expect(res.ok).toBe(true);
if (res.ok) {
expect(res.config.whatsapp?.historyLimit).toBe(9);
expect(res.config.whatsapp?.accounts?.work?.historyLimit).toBe(4);
expect(res.config.telegram?.historyLimit).toBe(8);
expect(res.config.telegram?.accounts?.ops?.historyLimit).toBe(3);
expect(res.config.slack?.historyLimit).toBe(7);
expect(res.config.slack?.accounts?.ops?.historyLimit).toBe(2);
expect(res.config.signal?.historyLimit).toBe(6);
expect(res.config.imessage?.historyLimit).toBe(5);
expect(res.config.msteams?.historyLimit).toBe(4);
expect(res.config.discord?.historyLimit).toBe(3);
}
});
it('rejects imessage.dmPolicy="open" without allowFrom "*"', async () => {
vi.resetModules();
const { validateConfigObject } = await import("./config.js");

View File

@@ -151,6 +151,8 @@ export type WhatsAppConfig = {
* - "allowlist": only allow group messages from senders in groupAllowFrom/allowFrom
*/
groupPolicy?: GroupPolicy;
/** Max group messages to keep as history context (0 disables). */
historyLimit?: number;
/** Outbound text chunk size (chars). Default: 4000. */
textChunkLimit?: number;
/** Maximum media file size in MB. Default: 50. */
@@ -187,6 +189,8 @@ export type WhatsAppAccountConfig = {
allowFrom?: string[];
groupAllowFrom?: string[];
groupPolicy?: GroupPolicy;
/** Max group messages to keep as history context (0 disables). */
historyLimit?: number;
textChunkLimit?: number;
mediaMaxMb?: number;
blockStreaming?: boolean;
@@ -347,6 +351,8 @@ export type TelegramAccountConfig = {
* - "allowlist": only allow group messages from senders in groupAllowFrom/allowFrom
*/
groupPolicy?: GroupPolicy;
/** Max group messages to keep as history context (0 disables). */
historyLimit?: number;
/** Outbound text chunk size (chars). Default: 4000. */
textChunkLimit?: number;
/** Disable block streaming for this account. */
@@ -584,6 +590,8 @@ export type SlackAccountConfig = {
* - "allowlist": only allow channels present in slack.channels
*/
groupPolicy?: GroupPolicy;
/** Max channel messages to keep as history context (0 disables). */
historyLimit?: number;
textChunkLimit?: number;
blockStreaming?: boolean;
/** Merge streamed block replies before sending. */
@@ -641,6 +649,8 @@ export type SignalAccountConfig = {
* - "allowlist": only allow group messages from senders in groupAllowFrom/allowFrom
*/
groupPolicy?: GroupPolicy;
/** Max group messages to keep as history context (0 disables). */
historyLimit?: number;
/** Outbound text chunk size (chars). Default: 4000. */
textChunkLimit?: number;
blockStreaming?: boolean;
@@ -714,6 +724,8 @@ export type MSTeamsConfig = {
mediaAllowHosts?: Array<string>;
/** Default: require @mention to respond in channels/groups. */
requireMention?: boolean;
/** Max group/channel messages to keep as history context (0 disables). */
historyLimit?: number;
/** Default reply style: "thread" replies to the message, "top-level" posts a new message. */
replyStyle?: MSTeamsReplyStyle;
/** Per-team config. Key is team ID (from the /team/ URL path segment). */
@@ -748,6 +760,8 @@ export type IMessageAccountConfig = {
* - "allowlist": only allow group messages from senders in groupAllowFrom/allowFrom
*/
groupPolicy?: GroupPolicy;
/** Max group messages to keep as history context (0 disables). */
historyLimit?: number;
/** Include attachments + reactions in watch payloads. */
includeAttachments?: boolean;
/** Max outbound media size in MB. */

View File

@@ -217,6 +217,7 @@ const TelegramAccountSchemaBase = z.object({
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
groupPolicy: GroupPolicySchema.optional().default("open"),
historyLimit: z.number().int().min(0).optional(),
textChunkLimit: z.number().int().positive().optional(),
blockStreaming: z.boolean().optional(),
draftChunk: BlockStreamingChunkSchema.optional(),
@@ -305,12 +306,12 @@ const DiscordAccountSchema = z.object({
enabled: z.boolean().optional(),
token: z.string().optional(),
groupPolicy: GroupPolicySchema.optional().default("open"),
historyLimit: z.number().int().min(0).optional(),
textChunkLimit: z.number().int().positive().optional(),
blockStreaming: z.boolean().optional(),
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
maxLinesPerMessage: z.number().int().positive().optional(),
mediaMaxMb: z.number().positive().optional(),
historyLimit: z.number().int().min(0).optional(),
retry: RetryConfigSchema,
actions: z
.object({
@@ -377,6 +378,7 @@ const SlackAccountSchema = z.object({
appToken: z.string().optional(),
allowBots: z.boolean().optional(),
groupPolicy: GroupPolicySchema.optional().default("open"),
historyLimit: z.number().int().min(0).optional(),
textChunkLimit: z.number().int().positive().optional(),
blockStreaming: z.boolean().optional(),
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
@@ -430,6 +432,7 @@ const SignalAccountSchemaBase = z.object({
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
groupPolicy: GroupPolicySchema.optional().default("open"),
historyLimit: z.number().int().min(0).optional(),
textChunkLimit: z.number().int().positive().optional(),
blockStreaming: z.boolean().optional(),
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
@@ -477,6 +480,7 @@ const IMessageAccountSchemaBase = z.object({
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
groupAllowFrom: z.array(z.union([z.string(), z.number()])).optional(),
groupPolicy: GroupPolicySchema.optional().default("open"),
historyLimit: z.number().int().min(0).optional(),
includeAttachments: z.boolean().optional(),
mediaMaxMb: z.number().int().positive().optional(),
textChunkLimit: z.number().int().positive().optional(),
@@ -550,6 +554,7 @@ const MSTeamsConfigSchema = z
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
mediaAllowHosts: z.array(z.string()).optional(),
requireMention: z.boolean().optional(),
historyLimit: z.number().int().min(0).optional(),
replyStyle: MSTeamsReplyStyleSchema.optional(),
teams: z.record(z.string(), MSTeamsTeamSchema.optional()).optional(),
})
@@ -1286,6 +1291,7 @@ export const ClawdbotSchema = z
allowFrom: z.array(z.string()).optional(),
groupAllowFrom: z.array(z.string()).optional(),
groupPolicy: GroupPolicySchema.optional().default("open"),
historyLimit: z.number().int().min(0).optional(),
textChunkLimit: z.number().int().positive().optional(),
mediaMaxMb: z.number().int().positive().optional(),
blockStreaming: z.boolean().optional(),
@@ -1324,6 +1330,7 @@ export const ClawdbotSchema = z
allowFrom: z.array(z.string()).optional(),
groupAllowFrom: z.array(z.string()).optional(),
groupPolicy: GroupPolicySchema.optional().default("open"),
historyLimit: z.number().int().min(0).optional(),
textChunkLimit: z.number().int().positive().optional(),
mediaMaxMb: z.number().int().positive().optional().default(50),
blockStreaming: z.boolean().optional(),