




















@@ -6690,6 +6690,168 @@ describe("sendPolicy deny — suppress delivery, not processing (#53328)", () =>
66906690expect(dispatcher.sendFinalReply).not.toHaveBeenCalled();
66916691});
669266926693+it("routes plugin-owned bindings under message-tool-only source delivery", async () => {
6694+setNoAbort();
6695+hookMocks.runner.hasHooks.mockImplementation(
6696+((hookName?: string) =>
6697+hookName === "inbound_claim" || hookName === "message_received") as () => boolean,
6698+);
6699+hookMocks.registry.plugins = [{ id: "openclaw-codex-app-server", status: "loaded" }];
6700+hookMocks.runner.runInboundClaimForPluginOutcome.mockResolvedValue({
6701+status: "handled",
6702+result: { handled: true },
6703+});
6704+sessionBindingMocks.resolveByConversation.mockReturnValue({
6705+bindingId: "binding-message-tool-only",
6706+targetSessionKey: "plugin-binding:codex:abc123",
6707+targetKind: "session",
6708+conversation: {
6709+channel: "telegram",
6710+accountId: "default",
6711+conversationId: "-1001234567890:topic:11",
6712+parentConversationId: "-1001234567890",
6713+},
6714+status: "active",
6715+boundAt: 1710000000000,
6716+metadata: {
6717+pluginBindingOwner: "plugin",
6718+pluginId: "openclaw-codex-app-server",
6719+pluginRoot: "/tmp/plugin",
6720+},
6721+} satisfies SessionBindingRecord);
6722+sessionStoreMocks.currentEntry = {
6723+sessionId: "s1",
6724+updatedAt: 0,
6725+sendPolicy: "allow",
6726+};
6727+const dispatcher = createDispatcher();
6728+const replyResolver = vi.fn(async () => ({ text: "agent reply" }) satisfies ReplyPayload);
6729+const ctx = buildTestCtx({
6730+Provider: "telegram",
6731+Surface: "telegram",
6732+OriginatingChannel: "telegram",
6733+OriginatingTo: "telegram:-1001234567890",
6734+To: "telegram:-1001234567890",
6735+AccountId: "default",
6736+MessageThreadId: 11,
6737+SessionKey: "agent:main:telegram:group:-1001234567890:topic:11",
6738+ChatType: "group",
6739+GroupSubject: "Dev",
6740+Body: "observed message",
6741+});
6742+6743+const result = await dispatchReplyFromConfig({
6744+ ctx,
6745+cfg: emptyConfig,
6746+ dispatcher,
6747+ replyResolver,
6748+replyOptions: {
6749+sourceReplyDeliveryMode: "message_tool_only",
6750+},
6751+});
6752+6753+expect(result).toEqual({
6754+queuedFinal: false,
6755+counts: { tool: 0, block: 0, final: 0 },
6756+sourceReplyDeliveryMode: "message_tool_only",
6757+});
6758+expect(sessionBindingMocks.touch).toHaveBeenCalledWith("binding-message-tool-only");
6759+expect(hookMocks.runner.runInboundClaimForPluginOutcome).toHaveBeenCalledWith(
6760+"openclaw-codex-app-server",
6761+expect.objectContaining({
6762+channel: "telegram",
6763+content: "observed message",
6764+threadId: 11,
6765+}),
6766+expect.objectContaining({
6767+pluginBinding: expect.objectContaining({ bindingId: "binding-message-tool-only" }),
6768+}),
6769+);
6770+expect(replyResolver).not.toHaveBeenCalled();
6771+expect(dispatcher.sendFinalReply).not.toHaveBeenCalled();
6772+});
6773+6774+it("keeps unmentioned plugin-bound fallback from ordinary group agent dispatch", async () => {
6775+setNoAbort();
6776+hookMocks.runner.hasHooks.mockImplementation(
6777+((hookName?: string) =>
6778+hookName === "inbound_claim" || hookName === "message_received") as () => boolean,
6779+);
6780+hookMocks.registry.plugins = [{ id: "openclaw-codex-app-server", status: "loaded" }];
6781+hookMocks.runner.runInboundClaimForPluginOutcome.mockResolvedValue({
6782+status: "no_handler",
6783+});
6784+sessionBindingMocks.resolveByConversation.mockReturnValue({
6785+bindingId: "binding-message-tool-fallback",
6786+targetSessionKey: "plugin-binding:codex:abc123",
6787+targetKind: "session",
6788+conversation: {
6789+channel: "telegram",
6790+accountId: "default",
6791+conversationId: "-1001234567890:topic:11",
6792+parentConversationId: "-1001234567890",
6793+},
6794+status: "active",
6795+boundAt: 1710000000000,
6796+metadata: {
6797+pluginBindingOwner: "plugin",
6798+pluginId: "openclaw-codex-app-server",
6799+pluginRoot: "/tmp/plugin",
6800+},
6801+} satisfies SessionBindingRecord);
6802+sessionStoreMocks.currentEntry = {
6803+sessionId: "s1",
6804+updatedAt: 0,
6805+sendPolicy: "allow",
6806+};
6807+const dispatcher = createDispatcher();
6808+const replyResolver = vi.fn(async () => ({ text: "agent reply" }) satisfies ReplyPayload);
6809+const ctx = buildTestCtx({
6810+Provider: "telegram",
6811+Surface: "telegram",
6812+OriginatingChannel: "telegram",
6813+OriginatingTo: "telegram:-1001234567890",
6814+To: "telegram:-1001234567890",
6815+AccountId: "default",
6816+MessageThreadId: 11,
6817+SessionKey: "agent:main:telegram:group:-1001234567890:topic:11",
6818+ChatType: "group",
6819+GroupSubject: "Dev",
6820+Body: "observed message",
6821+WasMentioned: false,
6822+});
6823+6824+const result = await dispatchReplyFromConfig({
6825+ ctx,
6826+cfg: emptyConfig,
6827+ dispatcher,
6828+ replyResolver,
6829+replyOptions: {
6830+sourceReplyDeliveryMode: "message_tool_only",
6831+},
6832+});
6833+6834+expect(result).toEqual({
6835+queuedFinal: false,
6836+counts: { tool: 0, block: 0, final: 0 },
6837+sourceReplyDeliveryMode: "message_tool_only",
6838+});
6839+expect(hookMocks.runner.runInboundClaimForPluginOutcome).toHaveBeenCalledWith(
6840+"openclaw-codex-app-server",
6841+expect.objectContaining({
6842+channel: "telegram",
6843+content: "observed message",
6844+threadId: 11,
6845+}),
6846+expect.objectContaining({
6847+pluginBinding: expect.objectContaining({ bindingId: "binding-message-tool-fallback" }),
6848+}),
6849+);
6850+expect(replyResolver).not.toHaveBeenCalled();
6851+expect(dispatcher.sendToolResult).not.toHaveBeenCalled();
6852+expect(dispatcher.sendFinalReply).not.toHaveBeenCalled();
6853+});
6854+66936855it("keeps message-tool-only source delivery private while still processing the turn", async () => {
66946856setNoAbort();
66956857sessionStoreMocks.currentEntry = {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。