Skip to content

Add continuous compaction with contextual hand summaries#948

Open
pbranchu wants to merge 8 commits intoRightNow-AI:mainfrom
pbranchu:feat/continuous-compaction
Open

Add continuous compaction with contextual hand summaries#948
pbranchu wants to merge 8 commits intoRightNow-AI:mainfrom
pbranchu:feat/continuous-compaction

Conversation

@pbranchu
Copy link
Copy Markdown
Contributor

@pbranchu pbranchu commented Apr 1, 2026

Closes #896

Summary

Addresses the core problem in #896: conversation history grows unboundedly, old topics bleed into new sessions, and the agent has no situational awareness of what happened between exchanges.

What this adds

Continuous compaction — every N exchanges (configurable, default 5), when the session exceeds keep_recent messages:

  1. Summarizes older turns into a compact paragraph
  2. Queries configured hands (calendar, mail, etc.) for a time-bounded context summary
  3. Injects the context into the live session as a message — visible on the next LLM turn without requiring memory_recall

Session gap detection — when a user returns after a configurable gap (default 15 min), the bridge triggers compaction + context refresh before dispatching the first message. The context preamble is prepended so the agent is situationally aware on the first response.

Key design points

  • Config-driven context sources: only hands explicitly listed under [[compaction.context_sources]] are queried — no unexpected calls to arbitrary hands
  • Bounded time window: queries use from_ts (last compaction) → to_ts (now), tracked per-agent; max_lookback_secs caps how far back a gap query reaches
  • Parallel queries: all context sources run concurrently via tokio::spawn, latency is the slowest source not the sum
  • Session injection: context appears as a [Context refresh — timestamp] user message appended after compaction, in the keep_recent window
  • Streaming and non-streaming paths: both paths share the same query_context_sources_parallel() and inject_context_into_session() helpers

Configuration

[compaction]
continuous_interval = 5     # compact every N exchanges (0 = disabled)
keep_recent = 6             # verbatim turns to keep
session_gap_secs = 900      # gap that triggers pre-dispatch refresh (0 = disabled)
max_lookback_secs = 86400   # cap on gap query lookback

[[compaction.context_sources]]
hand = "calendar-hand"
prompt = "Summarize events from the last few hours and any upcoming in the next 24 hours. Be concise."

[[compaction.context_sources]]
hand = "mail-hand"
prompt = "Summarize unread or notable emails. Exclude newsletters and advertisements."

Also fixes

  • StreamableHttpClientTransportConfig struct expression broken by rmcp 1.3.0 marking it #[non_exhaustive] — switched to the with_uri().custom_headers() builder API
  • Two pre-existing clippy warnings: is_multiple_of() and redundant closure in LazyLock

Test plan

  • cargo clippy -- -D warnings passes
  • cargo test passes
  • With continuous_interval = 5: after 5 exchanges, compaction triggers and context appears in session
  • Context source hands receive from {ts} to {ts} time window in query
  • Sources queried in parallel (check logs show concurrent responses)
  • Session gap: return after >15 min, first response reflects calendar/mail context
  • max_lookback_secs caps gap query (3-day absence doesn't dump 3 days of events)
  • Empty context_sources list: compaction runs normally, no hand queries
  • Hand timeout (30s): skipped gracefully, other sources still collected

🤖 Generated with Claude Code

Philippe Branchu and others added 8 commits April 1, 2026 13:52
Add periodic compaction that triggers every N exchanges and queries
configured hands (e.g. calendar, mail) for contextual summaries that
get stored in shared memory for long-term recall.

- CompactionTomlConfig + CompactionContextSource types in openfang-types
- needs_continuous_compaction() helper + continuous_interval field in compactor
- Exchange counter + spawned continuous compaction task in kernel post-loop
- Queries context_sources hands with timeout, stores results in memory

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a user returns after >15 minutes (configurable session_gap_secs),
the bridge triggers compaction + context source queries BEFORE
dispatching the first message. This gives the agent situational
awareness on the first response.

The lookback for context sources is bounded by max_lookback_secs
(default 24h) — so a 3-day absence queries only the last 24 hours,
not 3 days of events/emails.

The context preamble is prepended to the user's message as
[Session context] so the agent sees it naturally.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Exchange counter was only wired in the streaming send path.
Add the same counter + compaction trigger to send_message_inner
so API and agent_send calls also trigger continuous compaction.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The continuous compaction in the non-streaming send_message_inner
path only ran standard compaction without querying context sources
(calendar-hand, mail-hand). Now both streaming and non-streaming
paths have identical compaction behavior: compact + query hands +
store context in memory.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously calendar-hand and mail-hand were queried sequentially,
adding 14.5s to the first response after a session gap. Now both
run concurrently via tokio::spawn, reducing latency to the slower
of the two (~11s).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The compaction summarizer now receives the current timestamp and is
instructed to:
- Drop past events that are no longer actionable (completed meetings,
  delivered orders, resolved questions)
- Keep future events and pending tasks
- Use absolute dates/times, never relative
- Focus on what's still relevant

This prevents stale event data ("hike today" from yesterday's session)
from persisting across compaction cycles.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ies, proper time window

- Context summaries now appended to the live session as a synthetic user
  message after compaction, so the agent sees them on the next turn without
  needing to call memory_recall (previously they were only written to memory)
- Context source queries now run in parallel (tokio::spawn per source) instead
  of sequentially — matches session gap detection behaviour
- Time window is now bounded on both ends: from_ts (last compaction time) to
  to_ts (now), tracked per-agent in last_compaction_at DashMap; first
  compaction uses max_lookback_secs as the lower bound
- Extracted query_context_sources_parallel() and inject_context_into_session()
  as module-level helpers, eliminating duplication between streaming and
  non-streaming paths
- Fix clippy: use .is_multiple_of() for modulo-zero checks, redundant closure
  in tool_runner LazyLock

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use StreamableHttpClientTransportConfig::with_uri().custom_headers() builder
instead of struct expression syntax, which broke when rmcp marked the struct
#[non_exhaustive] in 1.3.0. Also removes the now-unused Arc import.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Continuous compaction with contextual hand summaries

1 participant