diff --git a/CHANGELOG.md b/CHANGELOG.md index acf8e02626d..d3666a0a4b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Hooks/Cron: suppress duplicate main-session events for delivered hook turns and mark `SILENT_REPLY_TOKEN` (`NO_REPLY`) early exits as delivered to prevent hook context pollution. (#20678) Thanks @JonathanWorks. - Providers/OpenRouter: inject `cache_control` on system prompts for OpenRouter Anthropic models to improve prompt-cache reuse. (#17473) Thanks @rrenamed. - Providers/OpenRouter: allow pass-through OpenRouter and Opencode model IDs in live model filtering so custom routed model IDs are treated as modern refs. (#14312) Thanks @Joly0. - Providers/OpenRouter: default reasoning to enabled when the selected model advertises `reasoning: true` and no session/directive override is set. (#22513) Thanks @zwffff. diff --git a/src/cron/isolated-agent/run.ts b/src/cron/isolated-agent/run.ts index e4e4e798c76..fce4174da4d 100644 --- a/src/cron/isolated-agent/run.ts +++ b/src/cron/isolated-agent/run.ts @@ -753,7 +753,7 @@ export async function runCronIsolatedAgentTurn(params: { return withRunSession({ status: "ok", summary, outputText, ...telemetry }); } if (synthesizedText.toUpperCase() === SILENT_REPLY_TOKEN.toUpperCase()) { - return withRunSession({ status: "ok", summary, outputText, ...telemetry }); + return withRunSession({ status: "ok", summary, outputText, delivered: true, ...telemetry }); } try { const didAnnounce = await runSubagentAnnounceFlow({ diff --git a/src/gateway/server/hooks.ts b/src/gateway/server/hooks.ts index a20a748ef5e..2065bacd894 100644 --- a/src/gateway/server/hooks.ts +++ b/src/gateway/server/hooks.ts @@ -86,11 +86,13 @@ export function createGatewayHooksRequestHandler(params: { const summary = result.summary?.trim() || result.error?.trim() || result.status; const prefix = result.status === "ok" ? `Hook ${value.name}` : `Hook ${value.name} (${result.status})`; - enqueueSystemEvent(`${prefix}: ${summary}`.trim(), { - sessionKey: mainSessionKey, - }); - if (value.wakeMode === "now") { - requestHeartbeatNow({ reason: `hook:${jobId}` }); + if (!result.delivered) { + enqueueSystemEvent(`${prefix}: ${summary}`.trim(), { + sessionKey: mainSessionKey, + }); + if (value.wakeMode === "now") { + requestHeartbeatNow({ reason: `hook:${jobId}` }); + } } } catch (err) { logHooks.warn(`hook agent failed: ${String(err)}`);