
























@@ -412,6 +412,94 @@ describe("runMessageAction plugin dispatch", () => {
412412});
413413});
414414415+it("routes gateway-executed plugin sends through gateway RPC instead of local dispatch", async () => {
416+const handleAction = vi.fn(async () => jsonResult({ ok: true, local: true }));
417+const gatewayPlugin: ChannelPlugin = {
418+id: "gatewaychat",
419+meta: {
420+id: "gatewaychat",
421+label: "Gateway Chat",
422+selectionLabel: "Gateway Chat",
423+docsPath: "/channels/gatewaychat",
424+blurb: "Gateway Chat send test plugin.",
425+},
426+capabilities: { chatTypes: ["direct"] },
427+config: createAlwaysConfiguredPluginConfig(),
428+messaging: {
429+targetResolver: {
430+looksLikeId: () => true,
431+},
432+},
433+actions: {
434+describeMessageTool: () => ({ actions: ["send"] }),
435+supportsAction: ({ action }) => action === "send",
436+resolveExecutionMode: ({ action }) => (action === "send" ? "gateway" : "local"),
437+ handleAction,
438+},
439+};
440+setActivePluginRegistry(
441+createTestRegistry([
442+{
443+pluginId: "gatewaychat",
444+source: "test",
445+plugin: gatewayPlugin,
446+},
447+]),
448+);
449+mocks.callGatewayLeastPrivilege.mockResolvedValue({
450+ok: true,
451+messageId: "gw-send-1",
452+});
453+454+const result = await runMessageAction({
455+cfg: {
456+channels: {
457+gatewaychat: {
458+enabled: true,
459+},
460+},
461+} as OpenClawConfig,
462+action: "send",
463+params: {
464+channel: "gatewaychat",
465+target: "user-123",
466+message: "hello from cli",
467+},
468+gateway: {
469+clientName: "cli",
470+mode: "cli",
471+},
472+dryRun: false,
473+});
474+475+expect(mocks.callGatewayLeastPrivilege).toHaveBeenCalledWith(
476+expect.objectContaining({
477+method: "message.action",
478+params: expect.objectContaining({
479+channel: "gatewaychat",
480+action: "send",
481+params: expect.objectContaining({
482+to: "user-123",
483+message: "hello from cli",
484+}),
485+idempotencyKey: "idem-gateway-action",
486+}),
487+}),
488+);
489+expect(mocks.executeSendAction).not.toHaveBeenCalled();
490+expect(handleAction).not.toHaveBeenCalled();
491+expect(result).toMatchObject({
492+kind: "send",
493+channel: "gatewaychat",
494+action: "send",
495+handledBy: "plugin",
496+payload: {
497+ok: true,
498+messageId: "gw-send-1",
499+},
500+});
501+});
502+415503it("uses requester session channel policy for host-media reads", async () => {
416504const handlePolicyCheckedAction = vi.fn(async ({ mediaAccess }) =>
417505jsonResult({
@@ -912,6 +1000,86 @@ describe("runMessageAction plugin dispatch", () => {
9121000},
9131001});
9141002});
1003+1004+it("routes gateway-executed plugin polls through gateway RPC instead of local dispatch", async () => {
1005+const handleAction = vi.fn(async () => jsonResult({ ok: true, local: true }));
1006+const pollGatewayPlugin = createPollForwardingPlugin({
1007+pluginId: "pollchat",
1008+label: "Poll Chat",
1009+blurb: "Poll chat gateway forwarding test plugin.",
1010+ handleAction,
1011+});
1012+const baseActions = pollGatewayPlugin.actions!;
1013+pollGatewayPlugin.actions = {
1014+describeMessageTool: baseActions.describeMessageTool,
1015+supportsAction: baseActions.supportsAction,
1016+handleAction: baseActions.handleAction,
1017+resolveExecutionMode: ({ action }) => (action === "poll" ? "gateway" : "local"),
1018+};
1019+setActivePluginRegistry(
1020+createTestRegistry([
1021+{
1022+pluginId: "pollchat",
1023+source: "test",
1024+plugin: pollGatewayPlugin,
1025+},
1026+]),
1027+);
1028+mocks.callGatewayLeastPrivilege.mockResolvedValue({
1029+ok: true,
1030+pollId: "gw-poll-1",
1031+});
1032+1033+const result = await runMessageAction({
1034+cfg: {
1035+channels: {
1036+pollchat: {
1037+botToken: "tok",
1038+},
1039+},
1040+} as OpenClawConfig,
1041+action: "poll",
1042+params: {
1043+channel: "pollchat",
1044+target: "pollchat:123",
1045+pollQuestion: "Lunch?",
1046+pollOption: ["Pizza", "Sushi"],
1047+},
1048+gateway: {
1049+clientName: "cli",
1050+mode: "cli",
1051+},
1052+dryRun: false,
1053+});
1054+1055+expect(mocks.callGatewayLeastPrivilege).toHaveBeenCalledWith(
1056+expect.objectContaining({
1057+method: "message.action",
1058+params: expect.objectContaining({
1059+channel: "pollchat",
1060+action: "poll",
1061+params: expect.objectContaining({
1062+to: "pollchat:123",
1063+pollQuestion: "Lunch?",
1064+pollOption: ["Pizza", "Sushi"],
1065+}),
1066+idempotencyKey: "idem-gateway-action",
1067+}),
1068+}),
1069+);
1070+expect(mocks.executePollAction).not.toHaveBeenCalled();
1071+expect(handleAction).not.toHaveBeenCalled();
1072+expect(result).toMatchObject({
1073+kind: "poll",
1074+channel: "pollchat",
1075+action: "poll",
1076+handledBy: "plugin",
1077+payload: {
1078+ok: true,
1079+pollId: "gw-poll-1",
1080+},
1081+});
1082+});
9151083});
91610849171085describe("plugin-owned poll semantics", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。