feat: add is_bot_message column and support dedicated phone numbers (#235)
* feat: add is_bot_message column and support dedicated phone numbers Replace fragile content-prefix bot detection with an explicit is_bot_message database column. The old prefix check (content NOT LIKE 'Andy:%') is kept as a backstop for pre-migration messages. - Add is_bot_message column with automatic backfill migration - Add ASSISTANT_HAS_OWN_NUMBER env var to skip name prefix when the assistant has its own WhatsApp number - Move prefix logic into WhatsApp channel (no longer a router concern) - Remove prefixAssistantName from Channel interface - Load .env via dotenv so launchd-managed processes pick up config - WhatsApp bot detection: fromMe for own number, prefix match for shared Based on #160 and #173. Co-Authored-By: Stefan Gasser <stefan@stefangasser.com> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: extract shared .env parser and remove dotenv dependency Extract .env parsing into src/env.ts, used by both config.ts and container-runner.ts. Reads only requested keys without loading secrets into process.env, avoiding leaking API keys to child processes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Stefan Gasser <stefan@stefangasser.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
import { ASSISTANT_NAME, TRIGGER_PATTERN } from './config.js';
|
||||
import { TRIGGER_PATTERN } from './config.js';
|
||||
import {
|
||||
escapeXml,
|
||||
formatMessages,
|
||||
formatOutbound,
|
||||
stripInternalTags,
|
||||
} from './router.js';
|
||||
import { Channel, NewMessage } from './types.js';
|
||||
import { NewMessage } from './types.js';
|
||||
|
||||
function makeMsg(overrides: Partial<NewMessage> = {}): NewMessage {
|
||||
return {
|
||||
@@ -162,34 +162,18 @@ describe('stripInternalTags', () => {
|
||||
});
|
||||
|
||||
describe('formatOutbound', () => {
|
||||
const waChannel = { prefixAssistantName: true } as Channel;
|
||||
const noPrefixChannel = { prefixAssistantName: false } as Channel;
|
||||
const defaultChannel = {} as Channel;
|
||||
|
||||
it('prefixes with assistant name when channel wants it', () => {
|
||||
expect(formatOutbound(waChannel, 'hello world')).toBe(
|
||||
`${ASSISTANT_NAME}: hello world`,
|
||||
);
|
||||
});
|
||||
|
||||
it('does not prefix when channel opts out', () => {
|
||||
expect(formatOutbound(noPrefixChannel, 'hello world')).toBe('hello world');
|
||||
});
|
||||
|
||||
it('defaults to prefixing when prefixAssistantName is undefined', () => {
|
||||
expect(formatOutbound(defaultChannel, 'hello world')).toBe(
|
||||
`${ASSISTANT_NAME}: hello world`,
|
||||
);
|
||||
it('returns text with internal tags stripped', () => {
|
||||
expect(formatOutbound('hello world')).toBe('hello world');
|
||||
});
|
||||
|
||||
it('returns empty string when all text is internal', () => {
|
||||
expect(formatOutbound(waChannel, '<internal>hidden</internal>')).toBe('');
|
||||
expect(formatOutbound('<internal>hidden</internal>')).toBe('');
|
||||
});
|
||||
|
||||
it('strips internal tags and prefixes remaining text', () => {
|
||||
it('strips internal tags from remaining text', () => {
|
||||
expect(
|
||||
formatOutbound(waChannel, '<internal>thinking</internal>The answer is 42'),
|
||||
).toBe(`${ASSISTANT_NAME}: The answer is 42`);
|
||||
formatOutbound('<internal>thinking</internal>The answer is 42'),
|
||||
).toBe('The answer is 42');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user