mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 12:40:43 +00:00
fix(gateway): preflight strict agent delivery
This commit is contained in:
@@ -31,6 +31,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Discord/voice: merge configured media-understanding providers such as Deepgram into partial active provider registries, so follow-up voice turns keep transcribing after another media plugin is already active. Fixes #65687. Thanks @OneMintJulep.
|
||||
- WhatsApp: stage `qrcode` through root mirrored runtime dependencies so packaged QR pairing can render from staged plugin-runtime-deps installs. Fixes #75394. Thanks @FelipeX2001.
|
||||
- Discord/voice: apply per-channel Discord `systemPrompt` overrides to voice transcript turns by forwarding the trusted channel prompt through the voice agent run. Fixes #47095. Thanks @qearlyao.
|
||||
- Gateway/agent: reject strict `openclaw agent --deliver` requests with missing delivery targets before starting the agent run, so users do not wait for a completed turn that cannot send anywhere. Thanks @vincentkoc.
|
||||
- Discord/voice: run voice-channel turns under a voice-output policy that hides the agent `tts` tool and asks for spoken reply text, so `/vc join` sessions synthesize and play agent replies instead of ending with `NO_REPLY`. Fixes #61536. Thanks @aounakram.
|
||||
- Plugins/runtime-deps: include packaged OpenClaw identity in bundled plugin loader cache keys, so same-path package upgrades stop reusing stale versioned runtime-deps mirrors. Fixes #75045. Thanks @sahilsatralkar.
|
||||
- Plugin SDK: restore reply-prefix and reply-pipeline helpers on the deprecated root/compat SDK surface so external plugins still using `openclaw/plugin-sdk` do not fail message dispatch after update. Fixes #75171. Thanks @zhangxiliang.
|
||||
|
||||
@@ -1110,6 +1110,38 @@ describe("gateway agent handler", () => {
|
||||
expect(callArgs.bestEffortDeliver).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects strict delivery with a missing target before dispatching the agent", async () => {
|
||||
mocks.agentCommand.mockClear();
|
||||
primeMainAgentRun();
|
||||
const respond = vi.fn();
|
||||
|
||||
await invokeAgent(
|
||||
{
|
||||
message: "strict missing delivery target",
|
||||
agentId: "main",
|
||||
sessionKey: "agent:main:main",
|
||||
deliver: true,
|
||||
replyChannel: "telegram",
|
||||
bestEffortDeliver: false,
|
||||
idempotencyKey: "test-strict-delivery-missing-target",
|
||||
},
|
||||
{
|
||||
reqId: "strict-delivery-missing-target",
|
||||
respond,
|
||||
flushDispatch: false,
|
||||
},
|
||||
);
|
||||
|
||||
expect(mocks.agentCommand).not.toHaveBeenCalled();
|
||||
expect(respond).toHaveBeenCalledWith(
|
||||
false,
|
||||
undefined,
|
||||
expect.objectContaining({
|
||||
message: expect.stringContaining("requires target"),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("downgrades to session-only when bestEffortDeliver=true and no external channel is configured", async () => {
|
||||
mocks.agentCommand.mockClear();
|
||||
primeMainAgentRun();
|
||||
|
||||
@@ -1128,6 +1128,7 @@ export const agentHandlers: GatewayRequestHandlers = {
|
||||
let resolvedTo = deliveryPlan.resolvedTo;
|
||||
let effectivePlan = deliveryPlan;
|
||||
let deliveryDowngradeReason: string | null = null;
|
||||
let deliveryTargetResolutionError: Error | undefined;
|
||||
|
||||
if (wantsDelivery && resolvedChannel === INTERNAL_MESSAGE_CHANNEL) {
|
||||
const cfgResolved = cfgForAgent ?? cfg;
|
||||
@@ -1165,9 +1166,32 @@ export const agentHandlers: GatewayRequestHandlers = {
|
||||
});
|
||||
if (fallback.resolvedTarget?.ok) {
|
||||
resolvedTo = fallback.resolvedTo;
|
||||
} else if (fallback.resolvedTarget && !fallback.resolvedTarget.ok) {
|
||||
deliveryTargetResolutionError = fallback.resolvedTarget.error;
|
||||
}
|
||||
}
|
||||
|
||||
if (wantsDelivery && isDeliverableMessageChannel(resolvedChannel) && !resolvedTo) {
|
||||
if (!bestEffortDeliver) {
|
||||
respond(
|
||||
false,
|
||||
undefined,
|
||||
errorShape(
|
||||
ErrorCodes.INVALID_REQUEST,
|
||||
deliveryTargetResolutionError
|
||||
? String(deliveryTargetResolutionError)
|
||||
: `delivery target is required for ${resolvedChannel}: pass --to/--reply-to or configure a default target`,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
context.logGateway.info(
|
||||
deliveryTargetResolutionError
|
||||
? `agent delivery target missing (bestEffortDeliver): ${String(deliveryTargetResolutionError)}`
|
||||
: "agent delivery target missing (bestEffortDeliver): no deliverable target",
|
||||
);
|
||||
}
|
||||
|
||||
if (wantsDelivery && resolvedChannel === INTERNAL_MESSAGE_CHANNEL) {
|
||||
const shouldDowngrade = shouldDowngradeDeliveryToSessionOnly({
|
||||
wantsDelivery,
|
||||
|
||||
Reference in New Issue
Block a user