Discord: restore plugin bindings after restart

This commit is contained in:
huntharo
2026-03-15 09:04:40 -04:00
committed by Vincent Koc
parent eb4e96573a
commit 7ef75b8779
6 changed files with 144 additions and 46 deletions

View File

@@ -1879,7 +1879,7 @@ describe("dispatchReplyFromConfig", () => {
);
});
it("lets a plugin claim inbound traffic before core commands and agent dispatch", async () => {
it("does not broadcast inbound claims without a core-owned plugin binding", async () => {
setNoAbort();
hookMocks.runner.hasHooks.mockImplementation(
((hookName?: string) =>
@@ -1906,31 +1906,12 @@ describe("dispatchReplyFromConfig", () => {
MessageSid: "msg-claim-1",
SessionKey: "agent:main:telegram:group:-10099:77",
});
const replyResolver = vi.fn(async () => ({ text: "should not run" }) satisfies ReplyPayload);
const replyResolver = vi.fn(async () => ({ text: "core reply" }) satisfies ReplyPayload);
const result = await dispatchReplyFromConfig({ ctx, cfg, dispatcher, replyResolver });
expect(result).toEqual({ queuedFinal: false, counts: { tool: 0, block: 0, final: 0 } });
expect(hookMocks.runner.runInboundClaim).toHaveBeenCalledWith(
expect.objectContaining({
content: "who are you",
channel: "telegram",
accountId: "default",
conversationId: "-10099:topic:77",
parentConversationId: "-10099",
senderId: "user-9",
commandAuthorized: true,
wasMentioned: true,
}),
expect.objectContaining({
channelId: "telegram",
accountId: "default",
conversationId: "-10099:topic:77",
parentConversationId: "-10099",
senderId: "user-9",
messageId: "msg-claim-1",
}),
);
expect(result).toEqual({ queuedFinal: true, counts: { tool: 0, block: 0, final: 0 } });
expect(hookMocks.runner.runInboundClaim).not.toHaveBeenCalled();
expect(hookMocks.runner.runMessageReceived).toHaveBeenCalledWith(
expect.objectContaining({
from: ctx.From,
@@ -1957,8 +1938,10 @@ describe("dispatchReplyFromConfig", () => {
sessionKey: "agent:main:telegram:group:-10099:77",
}),
);
expect(replyResolver).not.toHaveBeenCalled();
expect(dispatcher.sendFinalReply).not.toHaveBeenCalled();
expect(replyResolver).toHaveBeenCalledTimes(1);
expect(dispatcher.sendFinalReply).toHaveBeenCalledWith(
expect.objectContaining({ text: "core reply" }),
);
});
it("emits internal message:received hook when a session key is available", async () => {

View File

@@ -233,19 +233,6 @@ export async function dispatchReplyFromConfig(params: {
return { queuedFinal: false, counts: dispatcher.getQueuedCounts() };
}
let pluginClaimedInbound = false;
if (hookRunner?.hasHooks("inbound_claim")) {
const inboundClaim = await hookRunner.runInboundClaim(
toPluginInboundClaimEvent(hookContext, {
commandAuthorized:
typeof ctx.CommandAuthorized === "boolean" ? ctx.CommandAuthorized : undefined,
wasMentioned: typeof ctx.WasMentioned === "boolean" ? ctx.WasMentioned : undefined,
}),
inboundClaimContext,
);
pluginClaimedInbound = inboundClaim?.handled === true;
}
// Trigger plugin hooks (fire-and-forget)
if (hookRunner?.hasHooks("message_received")) {
fireAndForgetHook(
@@ -270,12 +257,6 @@ export async function dispatchReplyFromConfig(params: {
);
}
if (pluginClaimedInbound) {
markIdle("plugin_claim");
recordProcessed("completed", { reason: "plugin-claimed" });
return { queuedFinal: false, counts: dispatcher.getQueuedCounts() };
}
// Check if we should route replies to originating channel instead of dispatcher.
// Only route when the originating channel is DIFFERENT from the current surface.
// This handles cross-provider routing (e.g., message from Telegram being processed