Regeneration-Prompt: |
During PR #27727 prepare, rebasing onto current main exposed a pnpm check failure in extensions/telegram/src/setup-surface.test.ts because telegramSetupWizard.prepare is optional and the test accessed prepared.cfg without proving the result exists. Keep runtime behavior unchanged. Add the smallest test-side assertion/helper needed to narrow the optional prepare result before reading cfg, then rerun the full prepare gates on the refreshed prep branch.
Regeneration-Prompt: |
Prepare PR #27727 for the unique compaction-safeguard budget fix only, leaving the overlapping post-compaction audit parser work out of scope. Fix the retry-fallback path in the compaction safeguard so when a quality-regeneration retry fails after one successful attempt, the final capped summary is rebuilt from the last successful history body plus the full reserved suffix, preserving split-turn context and preserved recent turns instead of only diagnostics and workspace rules. Replace the weak regression with one that forces capped fallback truncation and proves both suffix sections survive, then append the required changelog entry in Unreleased Fixes with (#27727) thanks @Pandadadadazxf.
The split-turn block is appended at the end of summaryWithoutPreservedTurns,
so truncation (prefix-preserving slice) drops it first. Move it into the
reserved suffix so preparation.isSplitTurn sessions retain turn context.
Made-with: Cursor
When body budget is tiny (suffix consumes almost entire 16k), budget <= 0
and we returned marker.slice(0, maxChars)—a useless fragment. Prefer
summary.slice(0, maxChars) to retain at least Decisions/Constraints prefix.
Made-with: Cursor
When suffix.length >= maxChars, slice(0, maxChars) kept the head (preserved
turns) and dropped the tail (workspace rules, diagnostics). Use slice(-maxChars)
to preserve the critical tail instead.
Made-with: Cursor
Include preservedTurnsSection in the reserved suffix so it survives truncation.
Truncation keeps the prefix (slice(0, budget)), so the tail is dropped first;
the 'preserved recent turns' block was in the body and could disappear on long
summaries. Move it into the suffix with diagnostics and workspace rules.
Made-with: Cursor
appendSummarySection('', section) strips leading newlines via trimStart.
When body does not end with newline (e.g. buildStructuredFallbackSummary),
concatenation could merge content like '...## Exact identifiers## Tool Failures'.
Ensure reserved suffix has leading separator so headings/tags stay distinct.
Made-with: Cursor
Cap main history body before appending tool failures and file ops so
these high-signal diagnostics survive truncation. Truncation keeps the
prefix (slice(0, budget)), so sections appended at the end would be
dropped first; moving them into the reserved suffix preserves them.
Made-with: Cursor
The context-usage banner in the web UI fell back to inputTokens when
totalTokens was missing. inputTokens is accumulated across all API
calls in a run (tool-use loops, compaction retries), so it overstates
actual context window utilization -- e.g. showing "100% context used
757.3k / 200k" when the real prompt snapshot is only 46k/200k (23%).
Drop the inputTokens fallback so the banner only fires when a genuine
prompt snapshot (totalTokens) is available.
Made-with: Cursor
* fix(matrix): pass agentId to buildMentionRegexes for agent-level mention patterns
* fix(matrix): resolve conflicts from main branch
* Retrigger CI
---------
Co-authored-by: Dinakar Sarbada <dinakars777@users.noreply.github.com>
* feat(gateway): persist webchat inbound images to disk
Images sent via the webchat control UI (chat.send RPC) were parsed into
content blocks but never written to disk, unlike WhatsApp and Telegram
handlers which call saveMediaBuffer(). This caused:
- Images lost after conversation compaction (only existed as ephemeral base64)
- Image editing/generation workflows failing for webchat-origin images
- Incomplete ~/.openclaw/media/inbound/ directory
After parseMessageWithAttachments extracts parsedImages, iterate and
persist each via saveMediaBuffer(buffer, mimeType, 'inbound'). Uses
fire-and-forget (.catch + warn log) so disk I/O never blocks the
chat.send response path.
Fixes#47930
* fix(gateway): address PR review comments on webchat image persistence
- Move saveMediaBuffer calls after sendPolicy/stop/dedupe checks so
rejected or retried requests don't write files to disk (Codex P1)
- Await all saves and collect SavedMedia results into persistedImages
so the persisted paths are available in scope (Greptile P1)
- Preserve Error stack trace in warn log instead of coercing to
toString() (Greptile P2)
- Switch to Promise.all for concurrent writes
* fix(gateway): address remaining review comments on webchat image persistence
- Revert to fire-and-forget pattern (no await) to eliminate race window
where retried requests miss the in-flight guard during image saves
- Remove unused SavedMedia import and persistedImages collection
- Use formatForLog for consistent error logging with stack traces
- Add NOTE comment about path propagation being a follow-up task
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(gateway): gate image persistence to webchat callers and defer base64 decode
* fix: drop unrelated format churn in lifecycle.test.ts
* gateway: clarify image persistence scope covers all chat.send callers
* fix(gateway): use generic chat.send log prefix for image persistence warnings
* fix(gateway): persist chat.send image refs in transcript
* fix(gateway): keep chat.send image refs off visible text
* fix(gateway): persist chat send media refs on dispatch
* fix(gateway): serialize chat send image persistence
* fix(gateway): persist chat send media after dispatch
* fix: persist chat.send inbound images across follow-ups (#51324) (thanks @fuller-stack-dev)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
* feat(telegram): auto-rename DM topics on first message
fix(telegram): use bot.api for topic rename to avoid SecretRef resolution
* fix(telegram): address security + test review feedback
- Fix test assertion: DEFAULT_PROMPT_SUBSTRING matches 'very short'
- Use RawBody instead of Body (no envelope metadata to LLM)
- Truncate user message to 500 chars for LLM prompt
- Remove user-derived content from verbose logs
- Remove redundant threadSpec.id null check
- Fix AutoTopicLabelParams type to match generateTopicLabel
* fix(telegram): use effective dm auto-topic config
* fix(telegram): detect direct auto-topic overrides
* fix: auto-rename Telegram DM topics on first message (#51502) (thanks @Lukavyi)
---------
Co-authored-by: Ayaan Zaidi <hi@obviy.us>