fix(destinations): default to replying to the origin destination
When a multi-destination agent receives an inbound message, the model
had no explicit guidance about which destination to address by default
and would sometimes pick the wrong one — e.g. Casa replying to the
admin's group questions in Laura's DM instead of in the group itself.
The formatter already injects `from="<destname>"` on every inbound
<message> tag (formatter.ts:184), so the origin is right there in the
prompt — the system prompt just never told the agent to use it.
Added one line to buildDestinationsSection() that nudges the agent
toward replying via the same destination the message came from, with
an out for explicit cross-destination requests ("tell Laura that…").
Single-destination groups are unaffected (they take a separate
short-circuit path with a fallback that auto-replies to the origin).
Tests cover the multi-destination, single-destination, and
no-destination cases.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
52
container/agent-runner/src/destinations.test.ts
Normal file
52
container/agent-runner/src/destinations.test.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
|
||||
|
||||
import { closeSessionDb, getInboundDb, initTestSessionDb } from './db/connection.js';
|
||||
import { buildSystemPromptAddendum } from './destinations.js';
|
||||
|
||||
beforeEach(() => {
|
||||
initTestSessionDb();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
closeSessionDb();
|
||||
});
|
||||
|
||||
function seedDestination(name: string, displayName: string, channelType: string, platformId: string): void {
|
||||
getInboundDb()
|
||||
.prepare(
|
||||
`INSERT INTO destinations (name, display_name, type, channel_type, platform_id, agent_group_id)
|
||||
VALUES (?, ?, 'channel', ?, ?, NULL)`,
|
||||
)
|
||||
.run(name, displayName, channelType, platformId);
|
||||
}
|
||||
|
||||
describe('buildSystemPromptAddendum — multi-destination routing guidance', () => {
|
||||
it('includes default-routing nudge when there are >1 destinations', () => {
|
||||
seedDestination('casa', 'Casa', 'whatsapp', 'group-1@g.us');
|
||||
seedDestination('whatsapp-mg-17780', 'whatsapp-mg-17780', 'whatsapp', 'phone-2@s.whatsapp.net');
|
||||
|
||||
const prompt = buildSystemPromptAddendum('Casa');
|
||||
|
||||
expect(prompt).toContain('Default routing');
|
||||
expect(prompt).toContain('from="name"');
|
||||
expect(prompt).toContain('`casa`');
|
||||
expect(prompt).toContain('`whatsapp-mg-17780`');
|
||||
});
|
||||
|
||||
it('omits the default-routing nudge for a single destination (short-circuited)', () => {
|
||||
seedDestination('casa', 'Casa', 'whatsapp', 'group-1@g.us');
|
||||
|
||||
const prompt = buildSystemPromptAddendum('Casa');
|
||||
|
||||
// Single-destination path uses the simpler "no special wrapping needed" copy
|
||||
expect(prompt).toContain('no special wrapping needed');
|
||||
expect(prompt).not.toContain('Default routing');
|
||||
});
|
||||
|
||||
it('handles the no-destination case without crashing', () => {
|
||||
const prompt = buildSystemPromptAddendum('Casa');
|
||||
|
||||
expect(prompt).toContain('no configured destinations');
|
||||
expect(prompt).not.toContain('Default routing');
|
||||
});
|
||||
});
|
||||
@@ -128,6 +128,10 @@ function buildDestinationsSection(): string {
|
||||
lines.push('Text outside of `<message>` blocks is scratchpad — logged but not sent anywhere.');
|
||||
lines.push('Use `<internal>...</internal>` to make scratchpad intent explicit.');
|
||||
lines.push('');
|
||||
lines.push(
|
||||
'**Default routing**: when replying to an incoming message, address the same destination the message came `from` — every inbound `<message>` tag carries a `from="name"` attribute that names the origin destination. Only address a different destination when the request itself asks you to (e.g., "tell Laura that…").',
|
||||
);
|
||||
lines.push('');
|
||||
lines.push(
|
||||
'To send a message mid-response (e.g., an acknowledgment before a long task), call the `send_message` MCP tool with the `to` parameter set to a destination name.',
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user