feat: make inbound envelopes configurable

Co-authored-by: Shiva Prasad <shiv19@users.noreply.github.com>
This commit is contained in:
Peter Steinberger
2026-01-18 18:42:34 +00:00
parent 42e6ff4611
commit 744d1329cb
32 changed files with 688 additions and 145 deletions

View File

@@ -328,9 +328,13 @@ describe("web auto-reply", () => {
expect(resolver).toHaveBeenCalledTimes(2);
const firstArgs = resolver.mock.calls[0][0];
const secondArgs = resolver.mock.calls[1][0];
expect(firstArgs.Body).toContain("[WhatsApp +1 2025-01-01T00:00Z] [clawdbot] first");
expect(firstArgs.Body).toMatch(
/\[WhatsApp \+1 (\+\d+[smhd] )?2025-01-01T00:00Z\] \[clawdbot\] first/,
);
expect(firstArgs.Body).not.toContain("second");
expect(secondArgs.Body).toContain("[WhatsApp +1 2025-01-01T01:00Z] [clawdbot] second");
expect(secondArgs.Body).toMatch(
/\[WhatsApp \+1 (\+\d+[smhd] )?2025-01-01T01:00Z\] \[clawdbot\] second/,
);
expect(secondArgs.Body).not.toContain("first");
// Max listeners bumped to avoid warnings in multi-instance test runs

View File

@@ -1,5 +1,8 @@
import { resolveMessagePrefix } from "../../../agents/identity.js";
import { formatInboundEnvelope } from "../../../auto-reply/envelope.js";
import {
formatInboundEnvelope,
type EnvelopeFormatOptions,
} from "../../../auto-reply/envelope.js";
import type { loadConfig } from "../../../config/config.js";
import type { WebInboundMsg } from "../types.js";
@@ -14,8 +17,10 @@ export function buildInboundLine(params: {
cfg: ReturnType<typeof loadConfig>;
msg: WebInboundMsg;
agentId: string;
previousTimestamp?: number;
envelope?: EnvelopeFormatOptions;
}) {
const { cfg, msg, agentId } = params;
const { cfg, msg, agentId, previousTimestamp, envelope } = params;
// WhatsApp inbound prefix: channels.whatsapp.messagePrefix > legacy messages.messagePrefix > identity/defaults
const messagePrefix = resolveMessagePrefix(cfg, agentId, {
configured: cfg.channels?.whatsapp?.messagePrefix,
@@ -37,5 +42,7 @@ export function buildInboundLine(params: {
e164: msg.senderE164,
id: msg.senderJid,
},
previousTimestamp,
envelope,
});
}

View File

@@ -8,7 +8,10 @@ import {
type ResponsePrefixContext,
} from "../../../auto-reply/reply/response-prefix-template.js";
import { resolveTextChunkLimit } from "../../../auto-reply/chunk.js";
import { formatInboundEnvelope } from "../../../auto-reply/envelope.js";
import {
formatInboundEnvelope,
resolveEnvelopeFormatOptions,
} from "../../../auto-reply/envelope.js";
import {
buildHistoryContextFromEntries,
type HistoryEntry,
@@ -20,7 +23,11 @@ import { shouldComputeCommandAuthorized } from "../../../auto-reply/command-dete
import { finalizeInboundContext } from "../../../auto-reply/reply/inbound-context.js";
import { toLocationContext } from "../../../channels/location.js";
import type { loadConfig } from "../../../config/config.js";
import { recordSessionMetaFromInbound, resolveStorePath } from "../../../config/sessions.js";
import {
readSessionUpdatedAt,
recordSessionMetaFromInbound,
resolveStorePath,
} from "../../../config/sessions.js";
import { logVerbose, shouldLogVerbose } from "../../../globals.js";
import type { getChildLogger } from "../../../logging.js";
import { readChannelAllowFromStore } from "../../../pairing/pairing-store.js";
@@ -121,10 +128,20 @@ export async function processMessage(params: {
suppressGroupHistoryClear?: boolean;
}) {
const conversationId = params.msg.conversationId ?? params.msg.from;
const storePath = resolveStorePath(params.cfg.session?.store, {
agentId: params.route.agentId,
});
const envelopeOptions = resolveEnvelopeFormatOptions(params.cfg);
const previousTimestamp = readSessionUpdatedAt({
storePath,
sessionKey: params.route.sessionKey,
});
let combinedBody = buildInboundLine({
cfg: params.cfg,
msg: params.msg,
agentId: params.route.agentId,
previousTimestamp,
envelope: envelopeOptions,
});
let shouldClearGroupHistory = false;
@@ -152,6 +169,7 @@ export async function processMessage(params: {
body: bodyWithId,
chatType: "group",
senderLabel: entry.sender,
envelope: envelopeOptions,
});
},
});
@@ -288,9 +306,6 @@ export async function processMessage(params: {
});
}
const storePath = resolveStorePath(params.cfg.session?.store, {
agentId: params.route.agentId,
});
const metaTask = recordSessionMetaFromInbound({
storePath,
sessionKey: params.route.sessionKey,