Skip to content

fix(auto-reply): single canonical group history and deduped turn metadata#99256

Merged
obviyus merged 3 commits into
mainfrom
codex/clean-turn-payload
Jul 2, 2026
Merged

fix(auto-reply): single canonical group history and deduped turn metadata#99256
obviyus merged 3 commits into
mainfrom
codex/clean-turn-payload

Conversation

@obviyus

@obviyus obviyus commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

Fixes #99218. A payload audit found the per-turn input the agent model receives carries ~700-800 tokens of redundant overhead on a busy group turn:

  • Telegram group turns delivered history twice in two formats in the same user message: the core chat-window block (Conversation context (untrusted, chronological...)) plus the Telegram body fold ([Chat messages since your last reply - for context] with a full bracket envelope per line). Core's chatWindowCoversHistory dedupe could not see the channel-side fold.
  • Sender identity was stated up to 5 times per turn (Conversation info, a standalone Sender block recombining its own fields, envelope lines, chat-window lines, and a quote-turn seam that rendered #59 Pash: [Telegram Ops id:...] Pash: text).
  • The delivery/silence contract was stated 3-5 times on message-tool-only turns; room events carried visible_reply_contract: plus two copies of the delivery hint plus the closing directive.
  • The extra system prompt rendered under ## Group Chat Context even for direct chats, with a nested same-level ## Inbound Context heading.

Why This Change Was Made

One canonical statement per fact makes the payload cheaper and easier for the model to parse — duplicated near-identical history was the largest single source of model confusion on ambient/group turns (directly relevant to the over-reply behavior tracked in #99142).

  • One history surface: the Telegram body now carries only the current message; group-history entries (including the bot's own outbound replies) merge into the core chat-window block via mergeTelegramGroupHistoryPromptContext, deduped by message id with cache-derived rich rows winning, chronological, watermark semantics preserved (room events keep the full window, addressed turns post-self). Both legacy fold paths (formatTelegramGroupHistoryEntry, buildRecoveredTelegramBody) are deleted.
  • One sender statement: nested sender: { id, name, username, e164, is_bot } inside Conversation info; standalone Sender block deleted; quote-turn header no longer repeats the sender the envelope already names.
  • One delivery contract per layer: the per-turn Delivery: hint is removed (the identical contract is in the system prompt — see snapshot evidence); room events keep exactly one authoritative default-silent directive, preserving fix(auto-reply): default room events to silence #99144 behavior. The retired hint strings moved to LEGACY_MESSAGE_TOOL_DELIVERY_HINTS (recognition-only: strip-inbound-meta, heartbeat-filter, codex attempt-context); exported SDK names unchanged.
  • Headers: ## Conversation Context (core and the copilot sibling in extensions/copilot/src/attempt.ts), Inbound Context demoted to ###.
  • Old-transcript compat: strip-inbound-meta.ts keeps every previously-shipped sentinel (including the removed Sender block and both old/new chat-window headers) so shipped session history still strips cleanly; extractInboundSenderLabel reads both the old flat and new nested sender shapes.

User Impact

Every group turn on every channel gets a smaller, single-format context. Snapshot floor case (Telegram DM, zero history): userInputText 1129 → 1033 chars; group turns save far more since the duplicated history block dominated. No behavior change to reply gating: room-event default-silent (#99144) and group history coverage (#99143, including bot self-replies on ambient turns) are preserved and test-pinned.

Evidence

  • node scripts/run-vitest.mjs across all touched test files plus an affected-format sweep (22 shards) — green.
  • pnpm tsgo, pnpm check:test-types, pnpm prompt:snapshots:check — green.
  • node scripts/run-vitest.mjs test/extension-test-boundary.test.ts src/scripts/test-projects.test.ts — green.
  • Blacksmith Testbox check:changed (tbx_01kwjepxmm4byr12p6qd0xz1tk): all lanes green including Linux prompt-snapshot drift; the single typecheck core tests failure was a stale test call-site fixed in-branch, re-proven with pnpm check:test-types.
  • Autoreview (branch mode vs origin/main): 1 finding (the same stale call-site), fixed; fresh pass clean.
  • Pre-existing unrelated: extensions/mattermost/src/channel.test.ts fails identically on clean main (1/49); untouched by this branch.

@openclaw-barnacle openclaw-barnacle Bot added channel: telegram Channel integration: telegram agents Agent runtime and tooling extensions: copilot size: XL maintainer Maintainer-authored PR labels Jul 2, 2026
@obviyus obviyus self-assigned this Jul 2, 2026
@clawsweeper

clawsweeper Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed July 2, 2026, 6:59 PM ET / 22:59 UTC.

Summary
The PR moves Telegram group history into structured chat-window context, consolidates sender and delivery prompt metadata, updates prompt headers, and refreshes tests and prompt snapshots.

PR surface: Source +60, Tests +240. Total +300 across 22 files.

Reproducibility: yes. at source level: current main body-folds Telegram group history while core prompt assembly also renders structured chat-window/history and repeated sender/delivery metadata. I did not run a live Telegram transport capture in this read-only review.

Review metrics: 1 noteworthy metric.

  • Public SDK constant changed: 1 exported array broadened. The changed subpath is a public Plugin SDK export, so maintainers should notice the compatibility impact before merge.

Stored data model
Persistent data-model change detected: serialized state: extensions/telegram/src/bot-message-context.require-mention.test.ts, vector/embedding metadata: src/auto-reply/reply/inbound-meta.test.ts, vector/embedding metadata: src/auto-reply/reply/prompt-prelude.test.ts, vector/embedding metadata: test/fixtures/agents/prompt-snapshots/codex-runtime-happy-path/discord-group-codex-message-tool.md, vector/embedding metadata: test/fixtures/agents/prompt-snapshots/codex-runtime-happy-path/telegram-direct-codex-message-tool.md, vector/embedding metadata: test/fixtures/agents/prompt-snapshots/codex-runtime-happy-path/telegram-heartbeat-codex-tool.md. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #99218
Summary: This PR is the candidate fix for the open prompt-payload duplication issue; adjacent ambient-room PRs are related context but not the canonical remaining work.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🦐 gold shrimp
Proof: 🌊 off-meta tidepool
Patch quality: 🦐 gold shrimp
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Keep LEGACY_MESSAGE_TOOL_DELIVERY_HINTS limited to retired strings or get explicit maintainer acceptance for broadening the public export.
  • Have maintainers decide whether the included test/snapshot proof is enough for this Telegram prompt-context change or whether they want a redacted live prompt capture.

Risk before merge

  • [P1] The PR changes LEGACY_MESSAGE_TOOL_DELIVERY_HINTS from retired-only strings to include current hints, which can surprise third-party plugins importing that public SDK constant.
  • [P1] The PR changes model-visible Telegram context and delivery guidance, so green unit/snapshot checks do not by themselves settle live group reply or no-reply behavior.

Maintainer options:

  1. Keep the legacy export narrow (recommended)
    Before merge, keep LEGACY_MESSAGE_TOOL_DELIVERY_HINTS limited to retired strings and use MESSAGE_TOOL_DELIVERY_HINTS or a private recognition list for broad stripping.
  2. Accept the SDK semantic change
    Maintainers can intentionally accept the broader exported legacy list if they are comfortable treating that public constant as recognition-only compatibility data.

Next step before merge

  • [P1] Protected maintainer labeling plus public SDK compatibility and prompt message-delivery risk require human review rather than cleanup closure or automated repair routing.

Security
Cleared: The diff does not change dependencies, workflows, secrets handling, package resolution, or executable supply-chain surfaces; no concrete security regression was found.

Review findings

  • [P1] Preserve the legacy hint list semantics — src/plugin-sdk/message-tool-delivery-hints.ts:7-12
Review details

Best possible solution:

Land the prompt cleanup only after preserving the public legacy hint export semantics or explicitly accepting that SDK compatibility change, while keeping the Telegram rolling-history and old-transcript stripping behavior intact.

Do we have a high-confidence way to reproduce the issue?

Yes, at source level: current main body-folds Telegram group history while core prompt assembly also renders structured chat-window/history and repeated sender/delivery metadata. I did not run a live Telegram transport capture in this read-only review.

Is this the best way to solve the issue?

No, not quite: the central prompt cleanup is the right layer, but broadening the public LEGACY_MESSAGE_TOOL_DELIVERY_HINTS export is not the safest way to implement recognition-only stripping.

Full review comments:

  • [P1] Preserve the legacy hint list semantics — src/plugin-sdk/message-tool-delivery-hints.ts:7-12
    plugin-sdk/message-tool-delivery-hints is a documented public export, but this changes LEGACY_MESSAGE_TOOL_DELIVERY_HINTS from the two retired strings into the full current recognition list. Existing plugin code importing the legacy-only list would now match current prompt hints too; keep this export stable and use MESSAGE_TOOL_DELIVERY_HINTS or a private recognition list for stripping.
    Confidence: 0.86

Overall correctness: patch is incorrect
Overall confidence: 0.86

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 3a2baef0bfc6.

Label changes

Label justifications:

  • P2: The PR addresses a real prompt/session-context quality bug with limited blast radius, but it is not an outage or emergency.
  • merge-risk: 🚨 compatibility: The diff changes behavior of an exported Plugin SDK constant under plugin-sdk/message-tool-delivery-hints.
  • merge-risk: 🚨 message-delivery: The diff changes model-visible Telegram context and delivery guidance that can affect when group turns send or suppress visible replies.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🌊 off-meta tidepool and patch quality is 🦐 gold shrimp.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Not applicable: The protected maintainer-labeled PR is not subject to the external-contributor proof gate; the PR body provides local/Testbox validation rather than live Telegram proof.
Evidence reviewed

PR surface:

Source +60, Tests +240. Total +300 across 22 files.

View PR surface stats
Area Files Added Removed Net
Source 10 244 184 +60
Tests 12 419 179 +240
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 22 663 363 +300

What I checked:

Likely related people:

  • obviyus: Recent merged work on rolling Telegram group history and default-silent room-event prompts is directly adjacent to the PR's central history and prompt cleanup. (role: recent Telegram and prompt-context area contributor; confidence: high; commits: b07fd6f1b4c0, 59a6240dae13, dad7768da850; files: extensions/telegram/src/bot-message-context.session.ts, extensions/telegram/src/group-history-window.ts, src/auto-reply/reply/prompt-prelude.ts)
  • ragesaq: Recent message-tool delivery work shaped the delivery-mode behavior and hints that this PR trims from per-turn metadata. (role: message-tool delivery behavior contributor; confidence: medium; commits: a15427d605fe; files: src/auto-reply/reply/inbound-meta.ts, src/plugin-sdk/message-tool-delivery-hints.ts)
  • linhongkuan: Recent Telegram sender bot-status context work touched the sender metadata surface that this PR consolidates into the conversation-info block. (role: recent sender metadata contributor; confidence: medium; commits: 69b0604f313d; files: extensions/telegram/src/bot-message-context.session.ts, src/auto-reply/reply/inbound-meta.ts)
  • Mars: Earlier shipped work on stripping injected inbound metadata is relevant to the PR's old-transcript stripping compatibility path. (role: historical inbound metadata stripping contributor; confidence: medium; commits: a4e7e952e143; files: src/auto-reply/reply/strip-inbound-meta.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. labels Jul 2, 2026
obviyus added 3 commits July 2, 2026 15:54
Move Telegram group history out of the message body fold and into the structured chat-window context used by the inbound metadata renderer. Preserve outbound self entries, post-self watermarks, recovered forum-topic routing, and richer cached reply/media fields while dropping stale recovered-topic windows.\n\nFixes #99218
Merge sender identity into conversation info, remove duplicate per-turn delivery hints, demote inbound metadata heading, keep room-event default-silent guidance singular, strip rendered chat-window context on replay, and refresh prompt snapshots.
@obviyus
obviyus force-pushed the codex/clean-turn-payload branch from 74caaaf to 5b43f21 Compare July 2, 2026 22:55
@obviyus

obviyus commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

Maintainer decision on the two P1s:

LEGACY_MESSAGE_TOOL_DELIVERY_HINTS broadening — accepted (option 2). The export's contract is "hint strings current core no longer emits", and this PR is what retires the two moved hints (no prod builder emits them after this change; recognition-side consumers all use the combined MESSAGE_TOOL_DELIVERY_HINTS). Keeping the legacy list frozen while adding a private second recognition list would leave the public export semantically wrong and duplicate one concept across two lists. Repo-wide, LEGACY_MESSAGE_TOOL_DELIVERY_HINTS has no importers outside its defining module; the exported names and the combined array's membership are unchanged.

Proof — accepted as sufficient. Beyond the snapshot/Testbox lanes in the PR body, the change was validated by rendering a real production group turn (real message + 11 cached context messages) through both main's and this branch's actual prompt-assembly code: same content, 3152 → 1496 chars, one history block instead of two overlapping ones. Live transport capture would re-prove the same bytes.

@obviyus
obviyus merged commit 3380d3b into main Jul 2, 2026
100 checks passed
@obviyus
obviyus deleted the codex/clean-turn-payload branch July 2, 2026 23:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling channel: telegram Channel integration: telegram extensions: copilot maintainer Maintainer-authored PR merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. P2 Normal backlog priority with limited blast radius. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. size: XL status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Per-turn agent input payload duplicates group history and restates sender/delivery facts up to 5x

1 participant