surface: envelope inbound messages for agent

This commit is contained in:
Peter Steinberger
2025-12-09 18:43:21 +00:00
parent 55bffeba4a
commit ffc930b871
8 changed files with 149 additions and 28 deletions

View File

@@ -680,8 +680,8 @@ describe("web auto-reply", () => {
expect(resolver).toHaveBeenCalledTimes(1);
const args = resolver.mock.calls[0][0];
expect(args.Body).toContain("[Jan 1 00:00] [clawdis] first");
expect(args.Body).toContain("[Jan 1 01:00] [clawdis] second");
expect(args.Body).toContain("[WhatsApp +1 2025-01-01 00:00] [clawdis] first");
expect(args.Body).toContain("[WhatsApp +1 2025-01-01 01:00] [clawdis] second");
// Max listeners bumped to avoid warnings in multi-instance test runs
expect(process.getMaxListeners?.()).toBeGreaterThanOrEqual(50);
@@ -1292,7 +1292,8 @@ describe("web auto-reply", () => {
// The resolver should receive a prefixed body with the configured marker
const callArg = resolver.mock.calls[0]?.[0] as { Body?: string };
expect(callArg?.Body).toBeDefined();
expect(callArg?.Body).toBe("[same-phone] hello");
expect(callArg?.Body).toContain("[WhatsApp +1555");
expect(callArg?.Body).toContain("[same-phone] hello");
resetLoadConfigMock();
});
@@ -1324,9 +1325,10 @@ describe("web auto-reply", () => {
sendMedia: vi.fn(),
});
// Body should NOT be prefixed
// Body should include envelope but not the same-phone prefix
const callArg = resolver.mock.calls[0]?.[0] as { Body?: string };
expect(callArg?.Body).toBe("hello");
expect(callArg?.Body).toContain("[WhatsApp +1555");
expect(callArg?.Body).toContain("hello");
});
it("applies responsePrefix to regular replies", async () => {

View File

@@ -31,6 +31,8 @@ import {
sleepWithAbort,
} from "./reconnect.js";
import { formatError, getWebAuthAgeMs, readWebSelfId } from "./session.js";
import { formatAgentEnvelope } from "../auto-reply/envelope.js";
import { formatAgentEnvelope } from "../auto-reply/envelope.js";
const WEB_TEXT_LIMIT = 4000;
const DEFAULT_GROUP_HISTORY_LIMIT = 50;
@@ -759,19 +761,6 @@ export async function monitorWebProvider(
type PendingBatch = { messages: WebInboundMsg[]; timer?: NodeJS.Timeout };
const pendingBatches = new Map<string, PendingBatch>();
const formatTimestamp = (ts?: number) => {
const tsCfg = cfg.inbound?.timestampPrefix;
const tsEnabled = tsCfg !== false; // default true
if (!tsEnabled) return "";
const tz = typeof tsCfg === "string" ? tsCfg : "UTC";
const date = ts ? new Date(ts) : new Date();
try {
return `[${date.toLocaleDateString("en-US", { month: "short", day: "numeric", timeZone: tz })} ${date.toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", hour12: false, timeZone: tz })}] `;
} catch {
return `[${date.toISOString().slice(5, 16).replace("T", " ")}] `;
}
};
const buildLine = (msg: WebInboundMsg) => {
// Build message prefix: explicit config > default based on allowFrom
let messagePrefix = cfg.inbound?.messagePrefix;
@@ -784,7 +773,18 @@ export async function monitorWebProvider(
msg.chatType === "group"
? `${msg.senderName ?? msg.senderE164 ?? "Someone"}: `
: "";
return `${formatTimestamp(msg.timestamp)}${prefixStr}${senderLabel}${msg.body}`;
const baseLine = `${prefixStr}${senderLabel}${msg.body}`;
// Wrap with standardized envelope for the agent.
return formatAgentEnvelope({
surface: "WhatsApp",
from:
msg.chatType === "group"
? msg.from
: msg.from?.replace(/^whatsapp:/, ""),
timestamp: msg.timestamp,
body: baseLine,
});
};
const processBatch = async (conversationId: string) => {
@@ -806,9 +806,13 @@ export async function monitorWebProvider(
history.length > 0 ? history.slice(0, -1) : [];
if (historyWithoutCurrent.length > 0) {
const historyText = historyWithoutCurrent
.map(
(m) =>
`${m.sender}: ${m.body}${m.timestamp ? ` [${new Date(m.timestamp).toISOString()}]` : ""}`,
.map((m) =>
formatAgentEnvelope({
surface: "WhatsApp",
from: conversationId,
timestamp: m.timestamp,
body: `${m.sender}: ${m.body}`,
}),
)
.join("\\n");
combinedBody = `[Chat messages since your last reply - for context]\\n${historyText}\\n\\n[Current message - respond to this]\\n${buildLine(latest)}`;