fix(whatsapp): use RawBody for command detection in group chats#639
Closed
mcinteerj wants to merge 1 commit intoopenclaw:mainfrom
Closed
fix(whatsapp): use RawBody for command detection in group chats#639mcinteerj wants to merge 1 commit intoopenclaw:mainfrom
mcinteerj wants to merge 1 commit intoopenclaw:mainfrom
Conversation
WhatsApp group messages include structural context (history, sender labels) in the Body field for LLM context. This caused command detection to fail when the formatted body did not match exact command strings like "/status". Changes: - Add RawBody field to MsgContext for clean message text - WhatsApp gateway sets RawBody to msg.body (original Baileys message) - Command detection, directive parsing, abort detection, and reset triggers now prefer RawBody over Body - BodyStripped falls back to RawBody before Body This separates concerns: Body carries formatted LLM context, RawBody carries clean text for command matching. Tests: - session.test.ts: RawBody extraction, reset triggers, fallback - abort.test.ts: /stop detection, bare word triggers - reply.raw-body.test.ts: /think, /model, /status directive parsing Related: openclaw#638 (RFC: Structured MsgContext and Decomposition of CombinedBody) Note: Discord has a similar combinedBody pattern that may need the same fix.
This was referenced Jan 10, 2026
Contributor
Contributor
andersoncollab
left a comment
There was a problem hiding this comment.
Approved as canonical BoardTableView. QA and security reviews pending.
ronan-dandelion-cult
pushed a commit
to karmaterminal/openclaw
that referenced
this pull request
Apr 19, 2026
…ompaction
Per swim-35 convergent finding (Elliott journal evidence + Cael+Ronan
source trace): request_compaction() always failed because the call sites
in agent-runner-execution.ts and followup-runner.ts dropped the session's
active provider/model on the floor. resolveEmbeddedCompactionTarget then
fell through to DEFAULT_PROVIDER/DEFAULT_MODEL ('openai'/'gpt-5.4') which
no prince has auth for, producing 'Unknown model: openai/gpt-5.4' that
classified as 'reason=unknown' in the journal and an instant <1s failure.
Three coupled defects, all fixed:
1. Defect 2 (root cause): pass run.provider + run.model into
compactEmbeddedPiSession at both call sites.
2. Defect 3 (caller lies to tool): both sites previously discarded the
compactEmbeddedPiSession return value and unconditionally returned
{ ok: true, compacted: true } even on real failure. This caused
incrementVolitionalCompactionCount to fire on phantom-successful
compactions, lying to /status telemetry. Now honor the real result.
3. Defect 1 (silent failure): add log.warn on the resolve-with-failure
branch in request-compaction-tool.ts so volitional failures are
visible. Add 'unknown model' classifier branch in compact-reasons.ts
so the journal stops saying 'reason=unknown' for this exact case.
Tests: 15/15 green on request-compaction-tool + compact-reasons.
tsc --noEmit clean across whole project.
Closes karmaterminal/openclaw-bootstrap#639
ronan-dandelion-cult
pushed a commit
to karmaterminal/openclaw
that referenced
this pull request
Apr 19, 2026
…del + skip-reason log level Three follow-up fixes from PR #191 review (copilot pass relayed by figs-as-ronan): 1. followup-runner.ts:283 — read inner-scope provider/model from the run: async (provider, model, ...) fallback dispatcher (line 207), not the outer run.provider/run.model (persisted primary). Otherwise a fallback-selected model still routes compaction through a primary that may be in cooldown — half-fix. 2. agent-runner-execution.ts:1020 — same shadowing fix; closure opens at line 805. Same failure mode. 3. request-compaction-tool.ts:169 — downgrade resolved-failure WARN to info-level when the reason matches a legitimate skip case (nothing to compact / below threshold / already compacted / no real conversation messages). Mirrors isCompactionSkipReason from commands-compact.ts:39 with a local copy to avoid cross-package coupling (agents/tools → auto-reply/reply). Real failure cases still WARN. Findings 1 & 2 are correctness; finding 3 is observability polish.
ronan-dandelion-cult
pushed a commit
to karmaterminal/openclaw
that referenced
this pull request
Apr 19, 2026
…alls Scribe self-review pass (Cael's claude_session_start /pr-review-toolkit on 37aae9c) caught the next failure mode after the original openclaw#639 bug and the Copilot fold: compactEmbeddedPiSession accepts authProfileId and uses it via resolveEmbeddedCompactionTarget to pick credentials. Both call sites (agent-runner-execution.ts:~1027, followup-runner.ts:~285) were not threading it — so a session on a non-default auth profile (per-user API key) would route compaction with the right provider/model but the wrong credentials. Conditional pattern mirrors agent-runner-execution.ts:841-844: thread the persisted authProfileId only when the inner-scope provider matches the persisted primary. On fallback to a different provider, the persisted profile is wrong (keyed to primary), so leave undefined and let resolveEmbeddedCompactionTarget pick the default profile for that provider. This is symmetric to findings 1+2 from the Copilot fold: same fallback- shadowing class, one parameter deeper. Pre-existing bug, but the lane is open and the fix is +2 conditional + 1 field per call site.
karmafeast
added a commit
to karmaterminal/openclaw
that referenced
this pull request
Apr 19, 2026
…ompaction-provider fix(openclaw#639): thread session provider/model through volitional compaction
ronan-dandelion-cult
pushed a commit
to karmaterminal/openclaw
that referenced
this pull request
Apr 19, 2026
…le + status restoration Updates RFC docs/design/continue-work-signal-v2.md to reflect the totality of changes since 107ca2b (the prior RFC edit) plus the two ship-gate PRs about to land: - §4.3: document session provider/model threading through volitional compaction (openclaw#191 / bootstrap#639). Three coupled defects: root cause, caller-honesty (phantom-counter), visibility (`unknown_model` classifier + `isLegitSkipReason` helper + `log.warn` on resolve-with-fallback + scope-aware `authProfileId`). - §6.1: add `[context-pressure:noop]` log anchor with reason taxonomy (window-zero / below-threshold / band-dedup); document the bootstrap#580 investigation cycle (`:reach`/`:skip` instrumentation, root cause = sentinel collision on band 0, fix = -1 sentinel). - §6.3: clarify Discord/agent path through src/auto-reply/status.ts was reconnected at openclaw#187 + tested at #188 (the line had been silently dropped in an earlier refactor); note `volitional: N` is honest only after #191. - §6.4: replace 'instrumentation is not currently in place' note with status of distinguishing-instrumentation work (openclaw#164/171/172/173). - Appendix C.1: add 'Closed failure modes' table — phantom-counter, hedge-timer ref leak, band-0 dedup, precondition-skip blindness, Copilot summarization headers, dist-bundle satellite chunks, subagent-announce runtime path mismatch. - Appendix D.2: add evidence-location rows for the new file paths (volitional threading sites; armHedgeTimer; status renderer; request-compaction-tool tests; context-pressure noop sites; agent-runner runtime promotion; subagent-announce co-location; F-NOISE scheduler test). - Header: bump test count (~180 across 13 files, was '172 across 8') to reflect additions in #165, #170, #188, #193. Skip-list (no RFC mention): #174 sessions/config raw-key sweep (internal hygiene); #173 Copilot log-enabled nits (micro-hygiene); 86134af removal of investigation breadcrumbs (cycle is folded into §6.1 narrative). Refs: openclaw#191 head fc3f415 (in-flight, MERGEABLE/UNSTABLE, APPROVED) openclaw#193 head 14483a6 (in-flight, MERGEABLE/UNSTABLE, APPROVED x2) openclaw#187, #188 (merged d787890) openclaw#160, #162, #164, #165, #169, #170, #171, #172, #173, #174 🍆 in 🩲: this is a docs PR; if either #191 or #193 changes shape pre-merge the affected paragraph here will need a one-line touch-up. Co-Authored-By: dandelion cult - ronan 🌊 <karmafeast@gmail.com>
This was referenced Apr 19, 2026
ronan-dandelion-cult
pushed a commit
to karmaterminal/openclaw
that referenced
this pull request
Apr 20, 2026
) The empty catch in agent-runner.ts post-compaction lifecycle silently swallowed spawnSubagentDirect rejections — staged TaskFlow records were released by consumeStagedPostCompactionDelegates, then any throw left zero trace in logs, journals, or operator events. Same bug class as the openclaw#639 ship-gate we just fought. Extract the per-delegate spawn loop into continuation/post-compaction-dispatch.ts so the failure path is testable without agent-runner integration fixtures. On failure, emit: - log.warn '[continuation:post-compaction-spawn-failed]' with error message, sessionKey, and task slice (mirrors delegate-dispatch.ts:223-232) - enqueueSystemEvent so the operator surface sees dropped work Failures are isolated per-delegate; one rejection does not abort the remaining staged delegates. Test coverage: 5 cases including success, named-anchor + system-event emission on rejection, per-delegate isolation, non-Error throwables, and empty-list short-circuit. Closes #203
cael-dandelion-cult
pushed a commit
to karmaterminal/openclaw
that referenced
this pull request
Apr 20, 2026
) The empty catch block in agent-runner.ts was silently swallowing spawn failures for post-compaction delegates. Since consumeStagedPostCompactionDelegates had already marked the TaskFlow as released, these failures resulted in lost work with zero trace. This fix extracts the post-compaction dispatch loop into a dedicated helper function (dispatchPostCompactionDelegates) with proper error handling that mirrors delegate-dispatch.ts:223-232: - Logs warn with anchor [continuation:post-compaction-spawn-failed] - Enqueues a system event so failures are observable Bug-class precedent: openclaw#639 (same silent-catch pattern in delegate-dispatch). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
cael-dandelion-cult
pushed a commit
to karmaterminal/openclaw
that referenced
this pull request
Apr 20, 2026
…th (#209) Adds behavioral test coverage for the openclaw#639 fix arc. These tests guard against regression when the next provider/model rename or fallback refactor happens. Coverage: - Counter truthfulness: getVolitionalCompactionCount increments ONLY on {ok:true, compacted:true}; legit-skip reasons and failures do not increment. - Log level correctness: legit-skip → log.info with [resolved-skip] anchor; real failures → log.warn with [resolved-failure] anchor. - Call-site threading: verifies triggerCompaction closure passes the session's actual provider/model/authProfileId to compactEmbeddedPiSession (not DEFAULT_PROVIDER/DEFAULT_MODEL). - authProfileId fallback-drop: when compaction provider ≠ persisted primary, authProfileId is dropped (set to undefined). Refs: openclaw#639 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
5 tasks
ronan-dandelion-cult
pushed a commit
to karmaterminal/openclaw
that referenced
this pull request
Apr 20, 2026
…/error paths (#205) - Route request-compaction-tool's resolved-failure warn log through classifyCompactionReason so journal lines carry the structured code (e.g. unknown_model, timeout, provider_error_4xx) alongside the raw reason string. Before: operators had to grep the freeform 'reason=...' field; after: they can pivot on a closed-taxonomy 'code=...' field. - Mirror the same classification on the background-error branch so transport-level rejects (AbortError / module-not-found / network) surface a structured code instead of only the raw throw message. - Add request-compaction-tool.classifier-emission.test.ts (4 cases): resolve-with-unknown_model warn includes code=unknown_model, generic 'Compaction cancelled' warn emits some code= field (pinning emission contract for future classifier extensions), promise-rejection error path includes code=<classified>, legit-skip takes info path (not warn). - Pre-existing isLegitSkipReason routes 'nothing to compact' / 'below threshold' / 'already compacted' / 'no real conversation messages' to the info path unchanged; the classifier only runs on real failures. This preserves the openclaw#639 legit-skip-quiet posture while closing the #205 structured-logging gap. - triggerCompaction catch sites in followup-runner.ts and agent-runner-execution.ts intentionally unchanged: their return flows through the warn sibling which now classifies, satisfying the #205 acceptance criterion without extra touch on the catch itself. pnpm tsgo clean. 14/14 request-compaction-tool + compact-reasons tests green. Closes #205. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
silas-dandelion-cult
added a commit
to karmaterminal/openclaw
that referenced
this pull request
Apr 20, 2026
…upstream 22 comment-edits across 13 files. All bare `#N` references that point to karmaterminal/openclaw or karmaterminal/openclaw-bootstrap fork issues now use the prefixed `karmaterminal/openclaw#N` (or `karmaterminal/openclaw-bootstrap#N`) form so they cannot be silently auto-linked to unrelated upstream openclaw/openclaw issues. Citations changed: - bug openclaw#639 -> karmaterminal/openclaw#639 (8 sites: compact-reasons.{ts,test.ts}, followup-runner.ts ×3, agent-runner-execution.ts ×3, request-compaction-tool.ts ×2) - (#580) / See #580 -> (#580) / See #580 (4 sites: context-pressure.{ts,test.ts}) - (#531) -> (#531) (2 sites: scheduler.test.ts) - issue #475 -> #475 (1 site: session-cost-usage.ts) - bootstrap#475 -> karmaterminal/openclaw-bootstrap#475 (1 site: discoverAllSessions.test.ts; the line above already used the prefixed form, this was the laggard) - Bug #473 fix / issue #473 -> #473 (3 sites: subagent-announce.continuation.runtime.{ts,test.ts}) - review gap on #188 -> review gap on #188 (1 site: status.test.ts; also dropped a named-prince prefix in the same test label, which separately satisfies part of #200) Left alone (out of scope per issue): - Pre-existing upstream openclaw/openclaw citations (openclaw#26905, openclaw#36142, openclaw#60552, openclaw#28491, openclaw#41981, openclaw#18264, openclaw#59428, openclaw#31311, openclaw#14869, openclaw#17971, openclaw#29683, openclaw#58409, openclaw#35481, openclaw#30115, openclaw#14396, openclaw#19537, openclaw#1205, openclaw#1435, openclaw#26881, openclaw#32228, openclaw#35074, openclaw#26881) -- these were present at v2026.4.14 and resolve correctly. - delegate-dispatch.ts:62 (`openclaw#189.`) -- already in the canonical prefixed form per the issue's exception note. Refs #199
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
WhatsApp group messages include structural context (history, sender labels) in the
Bodyfield for LLM context. This caused command detection to fail when the formatted body did not match exact command strings like/status.Problem
In WhatsApp group chats, the gateway constructs a
combinedBodywith:[Chat messages since your last reply - for context]\n...[WhatsApp ...] Sender: message\n[from: Name (phone)]Commands like
/status\n[from: Jake McInteer]failed exact matching against/status.Solution
Add
RawBodyfield toMsgContextcarrying the clean message text (original Baileys message). Command detection, directive parsing, abort detection, and reset triggers now preferRawBodyoverBody.This separates concerns:
Body: Formatted LLM context (history, sender labels)RawBody: Clean text for command matchingChanges
src/auto-reply/templating.ts: AddRawBodyfield toMsgContextsrc/web/auto-reply.ts: SetRawBody: msg.bodyin WhatsApp gatewaysrc/auto-reply/reply/session.ts: UseRawBodyfortriggerBodyNormalizedand reset triggerssrc/auto-reply/reply.ts: UseRawBodyfor directive parsingsrc/auto-reply/reply/abort.ts: UseRawBodyfor abort detectionsrc/auto-reply/reply/mentions.ts: Cleanup debug loggingTests
session.test.ts: RawBody extraction, reset triggers, fallback to Bodyabort.test.ts:/stopdetection, bare word triggersreply.raw-body.test.ts:/think,/model,/statusdirective parsing with structural wrappersRelated
Note
Discord has a similar
combinedBodypattern that may need the same fix. See #638 for broader architectural discussion.