From cb4f4f5f3a9abf3ceb8ca03777cdd3f375c6c067 Mon Sep 17 00:00:00 2001 From: simplyclever914 Date: Sun, 3 May 2026 15:34:35 +0300 Subject: [PATCH] Address compaction review feedback - gate the compaction continuation retry on existing replay side-effect metadata - type the bundled compaction notifier hook without explicit any - add an Unreleased changelog entry --- CHANGELOG.md | 2 +- src/agents/pi-embedded-runner/run.ts | 2 +- .../bundled/compaction-notifier/handler.ts | 27 ++++++++++++------- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d6d407b2de..cc4c6aff09a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,6 +71,7 @@ Docs: https://docs.openclaw.ai - Channels/secrets: resolve SecretRef-backed channel credentials through external plugin secret contracts after the plugin split, covering runtime startup, target discovery, webhook auth, disabled-account enumeration, and late-bound web_search config. Fixes #76371. (#76449) Thanks @joshavant and @neeravmakwana. - Docker/Gateway: pass Docker setup `.env` values into gateway and CLI containers and preserve exec SecretRef `passEnv` keys in managed service plans, so 1Password Connect-backed Discord tokens keep resolving after doctor or plugin repair. Thanks @vincentkoc. - Control UI/WebChat: explain compaction boundaries in chat history and link directly to session checkpoint controls so pre-compaction turns no longer look silently lost after refresh. Fixes #76415. Thanks @BunsDev. +- Agents/compaction: add an optional bundled compaction notifier hook and retry once from the compacted transcript when automatic compaction leaves a turn without a final visible reply. Thanks @simplyclever914. - Agents/incomplete-turn: detect and surface a warning when the agent's final text after a tool-call chain is silently dropped because the post-tool assistant response was never produced, instead of completing the turn with only the pre-tool analysis text. Fixes #76477. Thanks @amknight. - Channels/WhatsApp: attach native outbound mention metadata for group text and media captions by resolving `@+` and `@` tokens against WhatsApp participant data, including LID groups. Fixes #39879; carries forward #56863. Thanks @kengi1437, @joe2643, and @fridayck. - Channels/WhatsApp: require outbound mention tokens to end at a word boundary so phone-number prefixes inside longer strings no longer trigger hidden native mentions. @@ -79,7 +80,6 @@ Docs: https://docs.openclaw.ai - Plugins/install: require OpenClaw-owned install provenance before granting official npm plugin scanner trust, so direct npm package names no longer bypass launch-code scanning while catalog, onboarding, and doctor installs stay trusted. Thanks @fede-kamel and @vincentkoc. - Network proxy: preserve target TLS hostname validation for Node HTTPS requests routed through the managed HTTP proxy, so Discord-style CONNECT traffic no longer validates certificates against the local proxy host. Fixes #74809. (#76442) Thanks @jesse-merhi and @abnershang. - Gateway/sessions: keep async `sessions.list` title and preview hydration bounded to transcript head/tail reads so Control UI polling cannot full-scan large session transcripts every refresh. Thanks @vincentkoc. -- Gateway/performance: cache per-run verbose-level session reads, skip a redundant `lsof` scan in `gateway --force` when no listener was killed, and make the Gateway startup benchmark print usage for `--help`. - Gateway/sessions: keep agent runtime metadata on lightweight `sessions.list` rows so model-only session patches do not make Control UI lose runtime identity. Thanks @vincentkoc. - Gateway/sessions: keep bulk `sessions.list` rows lightweight by skipping per-row transcript usage fallback, display model inference, and plugin projection, avoiding event-loop stalls in large session stores. Thanks @Marvinthebored and @vincentkoc. - Gateway/models: keep read-only `models.list` fallbacks on persisted/current metadata and configured rows while using static auth checks, so missing `models.json` files no longer runtime-load provider discovery or stall gateway after restart. Fixes #76382; refs #76360 and #75707. Thanks @trojy13, @RayWoo, @AnathemaOfficial, and @vincentkoc. diff --git a/src/agents/pi-embedded-runner/run.ts b/src/agents/pi-embedded-runner/run.ts index f208c99c643..4a9de3cc096 100644 --- a/src/agents/pi-embedded-runner/run.ts +++ b/src/agents/pi-embedded-runner/run.ts @@ -2363,7 +2363,7 @@ export async function runEmbeddedPiAgent( !attempt.yieldDetected && !attempt.didSendDeterministicApprovalPrompt && !attempt.lastToolError && - !hasMessagingToolDeliveryEvidence(attempt) && + !resolveAttemptReplayMetadata(attempt).hadPotentialSideEffects && compactionContinuationRetryAttempts < 1 ) { compactionContinuationRetryAttempts += 1; diff --git a/src/hooks/bundled/compaction-notifier/handler.ts b/src/hooks/bundled/compaction-notifier/handler.ts index c47f356bb91..7fbf5e8837d 100644 --- a/src/hooks/bundled/compaction-notifier/handler.ts +++ b/src/hooks/bundled/compaction-notifier/handler.ts @@ -1,21 +1,28 @@ -const handler = async (event: any) => { +import type { HookHandler } from "../../hooks.js"; + +function readOptionalNumber(context: Record, key: string): number | undefined { + const value = context[key]; + return typeof value === "number" && Number.isFinite(value) ? value : undefined; +} + +const handler: HookHandler = async (event) => { try { - if (!event || !Array.isArray(event.messages)) return; - const context = event.context ?? {}; + const context = event.context; if (event.type === "session" && event.action === "compact:before") { - const messageCount = typeof context.messageCount === "number" && context.messageCount >= 0 - ? ` (${context.messageCount} messages)` + const messageCount = readOptionalNumber(context, "messageCount"); + const messageSuffix = messageCount !== undefined && messageCount >= 0 + ? ` (${messageCount} messages)` : ""; - event.messages.push(`🧹 Compacting context${messageCount} so I can continue without losing history…`); + event.messages.push(`🧹 Compacting context${messageSuffix} so I can continue without losing history…`); return; } if (event.type === "session" && event.action === "compact:after") { - const before = typeof context.tokensBefore === "number" ? context.tokensBefore : undefined; - const after = typeof context.tokensAfter === "number" ? context.tokensAfter : undefined; - const tokenDelta = before !== undefined && after !== undefined - ? ` (${before.toLocaleString()} → ${after.toLocaleString()} tokens)` + const tokensBefore = readOptionalNumber(context, "tokensBefore"); + const tokensAfter = readOptionalNumber(context, "tokensAfter"); + const tokenDelta = tokensBefore !== undefined && tokensAfter !== undefined + ? ` (${tokensBefore.toLocaleString()} → ${tokensAfter.toLocaleString()} tokens)` : ""; event.messages.push(`✅ Context compacted${tokenDelta}. Continuing from where I left off.`); }