feat: add seq cursor, work_items, provider fallback, and delivery reliability
Major reliability improvements to the message processing pipeline: - Add messages.seq monotonic cursor replacing timestamp-based cursors, preventing message loss from timestamp collisions with LIMIT queries - Add work_items table separating agent production from delivery, enabling delivery retry without re-running the agent - Propagate Discord send failures instead of silently swallowing them - Add Claude 429 → Kimi K2.5 automatic provider fallback with cooldown - Fix follow-up turn state reset in live index.ts path (not just message-runtime.ts) to prevent final output from being lost - Add restart context tracking for graceful restart announcements - Lazy migration from timestamp cursors to seq cursors for existing data Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
83
src/index.test.ts
Normal file
83
src/index.test.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import {
|
||||
editFormattedTrackedChannelMessage,
|
||||
sendFormattedChannelMessage,
|
||||
sendFormattedTrackedChannelMessage,
|
||||
} from './index.js';
|
||||
import { Channel } from './types.js';
|
||||
|
||||
function makeChannel(overrides: Partial<Channel> = {}): Channel {
|
||||
return {
|
||||
name: 'test',
|
||||
connect: async () => {},
|
||||
sendMessage: async () => {},
|
||||
isConnected: () => true,
|
||||
ownsJid: (jid: string) => jid === 'dc:test',
|
||||
disconnect: async () => {},
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe('index scheduler messaging helpers', () => {
|
||||
it('sends formatted tracked messages through sendAndTrack', async () => {
|
||||
const sendAndTrack = vi.fn(async () => 'msg-123');
|
||||
const channel = makeChannel({ sendAndTrack });
|
||||
|
||||
const messageId = await sendFormattedTrackedChannelMessage(
|
||||
[channel],
|
||||
'dc:test',
|
||||
'<internal>hidden</internal>watching ci',
|
||||
);
|
||||
|
||||
expect(messageId).toBe('msg-123');
|
||||
expect(sendAndTrack).toHaveBeenCalledWith('dc:test', 'watching ci');
|
||||
});
|
||||
|
||||
it('edits formatted tracked messages through editMessage', async () => {
|
||||
const editMessage = vi.fn(async () => {});
|
||||
const channel = makeChannel({ editMessage });
|
||||
|
||||
await editFormattedTrackedChannelMessage(
|
||||
[channel],
|
||||
'dc:test',
|
||||
'msg-123',
|
||||
'<internal>hidden</internal>still watching',
|
||||
);
|
||||
|
||||
expect(editMessage).toHaveBeenCalledWith(
|
||||
'dc:test',
|
||||
'msg-123',
|
||||
'still watching',
|
||||
);
|
||||
});
|
||||
|
||||
it('skips empty messages after formatting', async () => {
|
||||
const sendMessage = vi.fn(async () => {});
|
||||
const sendAndTrack = vi.fn(async () => 'msg-123');
|
||||
const editMessage = vi.fn(async () => {});
|
||||
const channel = makeChannel({ sendMessage, sendAndTrack, editMessage });
|
||||
|
||||
const trackedResult = await sendFormattedTrackedChannelMessage(
|
||||
[channel],
|
||||
'dc:test',
|
||||
'<internal>only hidden</internal>',
|
||||
);
|
||||
await editFormattedTrackedChannelMessage(
|
||||
[channel],
|
||||
'dc:test',
|
||||
'msg-123',
|
||||
'<internal>only hidden</internal>',
|
||||
);
|
||||
await sendFormattedChannelMessage(
|
||||
[channel],
|
||||
'dc:test',
|
||||
'<internal>only hidden</internal>',
|
||||
);
|
||||
|
||||
expect(trackedResult).toBeNull();
|
||||
expect(sendAndTrack).not.toHaveBeenCalled();
|
||||
expect(editMessage).not.toHaveBeenCalled();
|
||||
expect(sendMessage).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user