Skip to content

Commit 8338412

Browse files
committed
fix: cover per-peer LINE cron recovery (#81704)
1 parent f3f2c78 commit 8338412

3 files changed

Lines changed: 24 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Docs: https://docs.openclaw.ai
1515
- Agents: strip Gemini/Gemma `<final>` tags with attributes or self-closing syntax from delivered replies, including strict final-tag streaming enforcement. Fixes #65867.
1616
- macOS/update: disarm legacy `ai.openclaw.update.*` LaunchAgents when `openclaw update` starts from one, preventing KeepAlive relaunch loops that repeatedly restart the Gateway and replay update continuations. Fixes #82167.
1717
- LINE: acknowledge signed webhook events before agent processing so slow model replies do not cause LINE `request_timeout` delivery failures. Fixes #65375. Thanks @myericho.
18+
- LINE: stop cron recovery from inferring lowercased LINE recipients from canonical session keys, so long-running task replies do not silently retry undeliverable push targets. Fixes #81628. (#81704) Thanks @edenfunf.
1819
- TTS: preserve channel-derived voice-note delivery for `/tts audio` replies even when the provider output is not natively voice-compatible. (#82174) Thanks @xuruiray.
1920
- Codex/Lossless: keep Codex explicit compaction on native app-server threads while allowing Lossless through the context-engine slot; `openclaw doctor --fix` now migrates legacy `compaction.provider: "lossless-claw"` config to `plugins.slots.contextEngine`.
2021
- Cron/doctor: report scheduled jobs with explicit `payload.model` overrides, including provider namespace counts and default-model mismatches, so stale cron model pins are visible during auth or billing investigations. Fixes #82151. Thanks @mgonto.

src/agents/tools/cron-tool.test.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -859,9 +859,7 @@ describe("cron tool", () => {
859859
dmScope: "per-account-channel-peer",
860860
peerId: "Uabcdef0123456789abcdef0123456789",
861861
});
862-
expect(sessionKey).toBe(
863-
"agent:main:line:primary:direct:uabcdef0123456789abcdef0123456789",
864-
);
862+
expect(sessionKey).toBe("agent:main:line:primary:direct:uabcdef0123456789abcdef0123456789");
865863

866864
const delivery = await executeAddAndReadDelivery({
867865
callId: "call-line-direct-no-context-81628",
@@ -871,6 +869,24 @@ describe("cron tool", () => {
871869
expect(delivery?.to).toBeUndefined();
872870
});
873871

872+
it("does not surface lowercased LINE DM recipients with per-peer scope (#81628)", async () => {
873+
const sessionKey = buildAgentPeerSessionKey({
874+
agentId: "main",
875+
channel: "line",
876+
peerKind: "direct",
877+
dmScope: "per-peer",
878+
peerId: "Uabcdef0123456789abcdef0123456789",
879+
});
880+
expect(sessionKey).toBe("agent:main:direct:uabcdef0123456789abcdef0123456789");
881+
882+
const delivery = await executeAddAndReadDelivery({
883+
callId: "call-line-per-peer-no-context-81628",
884+
agentSessionKey: sessionKey,
885+
});
886+
887+
expect(delivery?.to).toBeUndefined();
888+
});
889+
874890
it("does not let current delivery context override explicit delivery targets", async () => {
875891
expect(
876892
await executeAddAndReadDelivery({

src/agents/tools/cron-tool.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,7 @@ function inferDeliveryFromSessionKey(agentSessionKey?: string): CronDelivery | n
575575
if (!peerId) {
576576
return null;
577577
}
578+
const marker = parts[markerIndex];
578579

579580
let channel: CronMessageChannel | undefined;
580581
if (markerIndex >= 1) {
@@ -587,11 +588,12 @@ function inferDeliveryFromSessionKey(agentSessionKey?: string): CronDelivery | n
587588
// HTTP 400, so refuse the fallback for LINE and let the caller surface the
588589
// missing target instead of silently scheduling an undeliverable job.
589590
// openclaw/openclaw#81628
590-
if (channel === "line") {
591+
const isChannellessLineDirectId =
592+
!channel && (marker === "direct" || marker === "dm") && /^[ucr][a-f0-9]{32}$/.test(peerId);
593+
if (channel === "line" || isChannellessLineDirectId) {
591594
return null;
592595
}
593596

594-
const marker = parts[markerIndex];
595597
const delivery: CronDelivery = { mode: "announce", to: peerId };
596598
if (channel) {
597599
delivery.channel = channel;

0 commit comments

Comments
 (0)