fix: unify inbound dispatch pipeline

This commit is contained in:
Peter Steinberger
2026-01-23 22:51:37 +00:00
parent da26954dd0
commit 2e0a835e07
29 changed files with 543 additions and 297 deletions

View File

@@ -12,21 +12,21 @@ describe("signal event handler sender prefix", () => {
beforeEach(() => {
dispatchMock.mockReset().mockImplementation(async ({ dispatcher, ctx }) => {
dispatcher.sendFinalReply({ text: "ok" });
return { queuedFinal: true, counts: { final: 1 }, ctx };
return { queuedFinal: true, counts: { tool: 0, block: 0, final: 1 }, ctx };
});
readAllowFromMock.mockReset().mockResolvedValue([]);
});
it("prefixes group bodies with sender label", async () => {
let capturedBody = "";
const dispatchModule = await import("../auto-reply/reply/dispatch-from-config.js");
vi.spyOn(dispatchModule, "dispatchReplyFromConfig").mockImplementation(
const dispatchModule = await import("../auto-reply/dispatch.js");
vi.spyOn(dispatchModule, "dispatchInboundMessage").mockImplementation(
async (...args: unknown[]) => dispatchMock(...args),
);
dispatchMock.mockImplementationOnce(async ({ dispatcher, ctx }) => {
capturedBody = ctx.Body ?? "";
dispatcher.sendFinalReply({ text: "ok" });
return { queuedFinal: true, counts: { final: 1 } };
return { queuedFinal: true, counts: { tool: 0, block: 0, final: 1 } };
});
const { createSignalEventHandler } = await import("./monitor/event-handler.js");

View File

@@ -9,14 +9,21 @@ vi.mock("./send.js", () => ({
sendReadReceiptSignal: (...args: unknown[]) => sendReadReceiptMock(...args),
}));
vi.mock("../auto-reply/reply/dispatch-from-config.js", () => ({
dispatchReplyFromConfig: vi.fn(
vi.mock("../auto-reply/dispatch.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../auto-reply/dispatch.js")>();
const dispatchInboundMessage = vi.fn(
async (params: { replyOptions?: { onReplyStart?: () => void } }) => {
await Promise.resolve(params.replyOptions?.onReplyStart?.());
return { queuedFinal: false, counts: { tool: 0, block: 0, final: 0 } };
},
),
}));
);
return {
...actual,
dispatchInboundMessage,
dispatchInboundMessageWithDispatcher: dispatchInboundMessage,
dispatchInboundMessageWithBufferedDispatcher: dispatchInboundMessage,
};
});
vi.mock("../pairing/pairing-store.js", () => ({
readChannelAllowFromStore: vi.fn().mockResolvedValue([]),
@@ -25,11 +32,13 @@ vi.mock("../pairing/pairing-store.js", () => ({
describe("signal event handler typing + read receipts", () => {
beforeEach(() => {
vi.useRealTimers();
sendTypingMock.mockReset().mockResolvedValue(true);
sendReadReceiptMock.mockReset().mockResolvedValue(true);
});
it("sends typing + read receipt for allowed DMs", async () => {
vi.resetModules();
const { createSignalEventHandler } = await import("./monitor/event-handler.js");
const handler = createSignalEventHandler({
runtime: { log: () => {}, error: () => {} } as any,

View File

@@ -5,17 +5,24 @@ import { expectInboundContextContract } from "../../../test/helpers/inbound-cont
let capturedCtx: MsgContext | undefined;
vi.mock("../../auto-reply/reply/dispatch-from-config.js", () => ({
dispatchReplyFromConfig: vi.fn(async (params: { ctx: MsgContext }) => {
vi.mock("../../auto-reply/dispatch.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../auto-reply/dispatch.js")>();
const dispatchInboundMessage = vi.fn(async (params: { ctx: MsgContext }) => {
capturedCtx = params.ctx;
return { queuedFinal: false, counts: { tool: 0, block: 0 } };
}),
}));
return { queuedFinal: false, counts: { tool: 0, block: 0, final: 0 } };
});
return {
...actual,
dispatchInboundMessage,
dispatchInboundMessageWithDispatcher: dispatchInboundMessage,
dispatchInboundMessageWithBufferedDispatcher: dispatchInboundMessage,
};
});
import { createSignalEventHandler } from "./event-handler.js";
describe("signal createSignalEventHandler inbound contract", () => {
it("passes a finalized MsgContext to dispatchReplyFromConfig", async () => {
it("passes a finalized MsgContext to dispatchInboundMessage", async () => {
capturedCtx = undefined;
const handler = createSignalEventHandler({

View File

@@ -17,7 +17,7 @@ import {
createInboundDebouncer,
resolveInboundDebounceMs,
} from "../../auto-reply/inbound-debounce.js";
import { dispatchReplyFromConfig } from "../../auto-reply/reply/dispatch-from-config.js";
import { dispatchInboundMessage } from "../../auto-reply/dispatch.js";
import {
buildPendingHistoryContextFromMap,
clearHistoryEntries,
@@ -225,7 +225,7 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) {
onReplyStart,
});
const { queuedFinal } = await dispatchReplyFromConfig({
const { queuedFinal } = await dispatchInboundMessage({
ctx: ctxPayload,
cfg: deps.cfg,
dispatcher,