mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-29 21:17:05 +02:00
test(cron): cover delivery context edge cases
This commit is contained in:
@@ -140,7 +140,7 @@ forever.
|
||||
| `webhook` | POST finished event payload to a URL |
|
||||
| `none` | No runner fallback delivery |
|
||||
|
||||
Use `--announce --channel telegram --to "-1001234567890"` for channel delivery. For Telegram forum topics, use `-1001234567890:topic:123`. Slack/Discord/Mattermost targets should use explicit prefixes (`channel:<id>`, `user:<id>`).
|
||||
Use `--announce --channel telegram --to "-1001234567890"` for channel delivery. For Telegram forum topics, use `-1001234567890:topic:123`. Slack/Discord/Mattermost targets should use explicit prefixes (`channel:<id>`, `user:<id>`). Matrix room IDs are case-sensitive; use the exact room ID or `room:!room:server` form from Matrix.
|
||||
|
||||
For isolated jobs, chat delivery is shared. If a chat route is available, the
|
||||
agent can use the `message` tool even when the job uses `--no-deliver`. If the
|
||||
@@ -148,6 +148,11 @@ agent sends to the configured/current target, OpenClaw skips the fallback
|
||||
announce. Otherwise `announce`, `webhook`, and `none` only control what the
|
||||
runner does with the final reply after the agent turn.
|
||||
|
||||
When an agent creates an isolated reminder from an active chat, OpenClaw stores
|
||||
the preserved live delivery target for the fallback announce route. Internal
|
||||
session keys may be lowercase; provider delivery targets are not reconstructed
|
||||
from those keys when current chat context is available.
|
||||
|
||||
Failure notifications follow a separate destination path:
|
||||
|
||||
- `cron.failureDestination` sets a global default for failure notifications.
|
||||
@@ -418,6 +423,9 @@ openclaw doctor
|
||||
- Delivery mode `none` means no runner fallback send is expected. The agent can
|
||||
still send directly with the `message` tool when a chat route is available.
|
||||
- Delivery target missing/invalid (`channel`/`to`) means outbound was skipped.
|
||||
- For Matrix, copied or legacy jobs with lowercased `delivery.to` room IDs can
|
||||
fail because Matrix room IDs are case-sensitive. Edit the job to the exact
|
||||
`!room:server` or `room:!room:server` value from Matrix.
|
||||
- Channel auth errors (`unauthorized`, `Forbidden`) mean delivery was blocked by credentials.
|
||||
- If the isolated run returns only the silent token (`NO_REPLY` / `no_reply`),
|
||||
OpenClaw suppresses direct outbound delivery and also suppresses the fallback
|
||||
|
||||
@@ -883,6 +883,11 @@ Matrix accepts these target forms anywhere OpenClaw asks you for a room or user
|
||||
- Rooms: `!room:server`, `room:!room:server`, or `matrix:room:!room:server`
|
||||
- Aliases: `#alias:server`, `channel:#alias:server`, or `matrix:channel:#alias:server`
|
||||
|
||||
Matrix room IDs are case-sensitive. Use the exact room ID casing from Matrix
|
||||
when configuring explicit delivery targets, cron jobs, bindings, or allowlists.
|
||||
OpenClaw keeps internal session keys canonical for storage, so those lowercase
|
||||
keys are not a reliable source for Matrix delivery IDs.
|
||||
|
||||
Live directory lookup uses the logged-in Matrix account:
|
||||
|
||||
- User lookups query the Matrix user directory on that homeserver.
|
||||
|
||||
@@ -138,6 +138,10 @@ Delivery ownership note:
|
||||
- `announce` fallback-delivers the final reply only when the agent did not send
|
||||
directly to the resolved target. `webhook` posts the finished payload to a URL.
|
||||
`none` disables runner fallback delivery.
|
||||
- Reminders created from an active chat preserve the live chat delivery target
|
||||
for fallback announce delivery. Internal session keys may be lowercase; do not
|
||||
use them as a source of truth for case-sensitive provider IDs such as Matrix
|
||||
room IDs.
|
||||
|
||||
## Common admin commands
|
||||
|
||||
|
||||
@@ -199,4 +199,28 @@ describe("createOpenClawTools cron context wiring", () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("uses agent route context when auto-threading context is unavailable", async () => {
|
||||
const { createOpenClawTools } = await import("./openclaw-tools.js");
|
||||
|
||||
createOpenClawTools({
|
||||
agentSessionKey: "agent:main:matrix:channel:!abcdef1234567890:example.org",
|
||||
agentChannel: "matrix",
|
||||
agentAccountId: "bot-a",
|
||||
agentTo: "room:!FallbackRoom:Example.Org",
|
||||
agentThreadId: "$FallbackThread:Example.Org",
|
||||
disableMessageTool: true,
|
||||
disablePluginTools: true,
|
||||
});
|
||||
|
||||
expect(mocks.createCronToolOptions).toHaveBeenCalledWith({
|
||||
agentSessionKey: "agent:main:matrix:channel:!abcdef1234567890:example.org",
|
||||
currentDeliveryContext: {
|
||||
channel: "matrix",
|
||||
to: "room:!FallbackRoom:Example.Org",
|
||||
accountId: "bot-a",
|
||||
threadId: "$FallbackThread:Example.Org",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,6 +15,14 @@ vi.mock("../agent-scope.js", async () => {
|
||||
import { createCronTool } from "./cron-tool.js";
|
||||
|
||||
describe("cron tool", () => {
|
||||
type TestDelivery = {
|
||||
mode?: string;
|
||||
channel?: string;
|
||||
to?: string;
|
||||
accountId?: string;
|
||||
threadId?: string | number;
|
||||
};
|
||||
|
||||
function createTestCronTool(
|
||||
opts?: Parameters<typeof createCronTool>[0],
|
||||
): ReturnType<typeof createCronTool> {
|
||||
@@ -64,7 +72,7 @@ describe("cron tool", () => {
|
||||
currentDeliveryContext?: NonNullable<
|
||||
Parameters<typeof createCronTool>[0]
|
||||
>["currentDeliveryContext"];
|
||||
delivery?: { mode?: string; channel?: string; to?: string } | null;
|
||||
delivery?: TestDelivery | null;
|
||||
}) {
|
||||
const tool = createTestCronTool({
|
||||
agentSessionKey: params.agentSessionKey,
|
||||
@@ -79,7 +87,7 @@ describe("cron tool", () => {
|
||||
});
|
||||
|
||||
const call = callGatewayMock.mock.calls[0]?.[0] as {
|
||||
params?: { delivery?: { mode?: string; channel?: string; to?: string } };
|
||||
params?: { delivery?: TestDelivery };
|
||||
};
|
||||
return call?.params?.delivery;
|
||||
}
|
||||
@@ -464,6 +472,53 @@ describe("cron tool", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps explicit delivery account and thread while filling target from context", async () => {
|
||||
expect(
|
||||
await executeAddAndReadDelivery({
|
||||
callId: "call-explicit-delivery-fields-win",
|
||||
agentSessionKey: "agent:main:matrix:channel:!abcdef1234567890:example.org",
|
||||
currentDeliveryContext: {
|
||||
channel: "matrix",
|
||||
to: "!AbCdEf1234567890:example.org",
|
||||
accountId: "context-bot",
|
||||
threadId: "$ContextThread:Example.Org",
|
||||
},
|
||||
delivery: {
|
||||
mode: "announce",
|
||||
accountId: "explicit-bot",
|
||||
threadId: "$ExplicitThread:Example.Org",
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
mode: "announce",
|
||||
channel: "matrix",
|
||||
to: "!AbCdEf1234567890:example.org",
|
||||
accountId: "explicit-bot",
|
||||
threadId: "$ExplicitThread:Example.Org",
|
||||
});
|
||||
});
|
||||
|
||||
it("trims current context fields without changing provider target casing", async () => {
|
||||
expect(
|
||||
await executeAddAndReadDelivery({
|
||||
callId: "call-trim-current-context",
|
||||
agentSessionKey: "agent:main:matrix:channel:!abcdef1234567890:example.org",
|
||||
currentDeliveryContext: {
|
||||
channel: " Matrix ",
|
||||
to: " !AbCdEf1234567890:Example.Org ",
|
||||
accountId: " Bot-A ",
|
||||
threadId: " $RootEvent:Example.Org ",
|
||||
},
|
||||
}),
|
||||
).toEqual({
|
||||
mode: "announce",
|
||||
channel: "matrix",
|
||||
to: "!AbCdEf1234567890:Example.Org",
|
||||
accountId: "bot-a",
|
||||
threadId: "$RootEvent:Example.Org",
|
||||
});
|
||||
});
|
||||
|
||||
it("infers delivery from current context even when no session key is available", async () => {
|
||||
expect(
|
||||
await executeAddAndReadDelivery({
|
||||
|
||||
@@ -644,8 +644,8 @@ Use jobId as the canonical identifier; id is accepted for compatibility. Use con
|
||||
inferDeliveryFromSessionKey(opts.agentSessionKey);
|
||||
if (inferred) {
|
||||
(job as { delivery?: unknown }).delivery = {
|
||||
...delivery,
|
||||
...inferred,
|
||||
...delivery,
|
||||
} satisfies CronDelivery;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user