feat(messages): also derive responsePrefix from identity.name

When identity.name is configured and responsePrefix is not explicitly set,
automatically default responsePrefix to [identity.name].

This means users only need to set their identity once:
  { identity: { name: "MyBot" } }

And outbound messages will automatically be prefixed with [MyBot].
This commit is contained in:
Richard Poelderl
2026-01-09 16:06:02 +01:00
committed by Peter Steinberger
parent 8112b276c0
commit 43848b7b43
3 changed files with 48 additions and 3 deletions

View File

@@ -891,8 +891,8 @@ export type AudioConfig = {
};
export type MessagesConfig = {
messagePrefix?: string; // Prefix added to all inbound messages (default: "[{identity.name}]" or "[clawdbot]" if no allowFrom, else "")
responsePrefix?: string; // Prefix auto-added to all outbound replies (e.g., "🦞")
messagePrefix?: string; // Prefix added to all inbound messages (default: "[{agents.list[].identity.name}]" or "[clawdbot]" if no allowFrom, else "")
responsePrefix?: string; // Prefix auto-added to all outbound replies (default: "[{agents.list[].identity.name}]" when set, else none)
groupChat?: GroupChatConfig;
queue?: QueueConfig;
/** Emoji reaction used to acknowledge inbound messages (empty disables). */

View File

@@ -2000,4 +2000,43 @@ describe("web auto-reply", () => {
expect(resolverArg.Body).not.toContain("[clawdbot]");
resetLoadConfigMock();
});
it("uses identity.name for responsePrefix when set", async () => {
setLoadConfigMock(() => ({
identity: { name: "Richbot", emoji: "🦁" },
whatsapp: { allowFrom: ["*"] },
}));
let capturedOnMessage:
| ((msg: import("./inbound.js").WebInboundMessage) => Promise<void>)
| undefined;
const reply = vi.fn();
const listenerFactory = async (opts: {
onMessage: (
msg: import("./inbound.js").WebInboundMessage,
) => Promise<void>;
}) => {
capturedOnMessage = opts.onMessage;
return { close: vi.fn() };
};
const resolver = vi.fn().mockResolvedValue({ text: "hello there" });
await monitorWebProvider(false, listenerFactory, false, resolver);
expect(capturedOnMessage).toBeDefined();
await capturedOnMessage?.({
body: "hi",
from: "+1555",
to: "+2666",
id: "msg1",
sendComposing: vi.fn(),
reply,
sendMedia: vi.fn(),
});
// Reply should have identity-based responsePrefix prepended
expect(reply).toHaveBeenCalledWith("[Richbot] hello there");
resetLoadConfigMock();
});
});

View File

@@ -1170,9 +1170,15 @@ export async function monitorWebProvider(
const textLimit = resolveTextChunkLimit(cfg, "whatsapp");
let didLogHeartbeatStrip = false;
let didSendReply = false;
// Derive responsePrefix from identity.name if not explicitly set
const responsePrefix =
cfg.messages?.responsePrefix ??
(cfg.identity?.name?.trim()
? `[${cfg.identity.name.trim()}]`
: undefined);
const { dispatcher, replyOptions, markDispatchIdle } =
createReplyDispatcherWithTyping({
responsePrefix: cfg.messages?.responsePrefix,
responsePrefix,
onHeartbeatStrip: () => {
if (!didLogHeartbeatStrip) {
didLogHeartbeatStrip = true;