You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When the conversation grows too long, docker-agent compacts older messages via pkg/compaction/ and pkg/runtime/compactor/. Today the memory subsystem has no awareness of this event, which means:
Facts about to be discarded are lost. Anything the model learned during the summarised portion of the conversation never makes it into the memory store, even if it would have been worth saving.
Session scoping breaks. Per-session state keyed by session_id is broken by compaction-driven session rotation; consumers need to be told about the switch.
Proposed design
1. OnPreCompress and OnSessionSwitch hooks on a memory coordinator
Define a new interface in pkg/memory/coordinator.go:
// pkg/memory/coordinator.gotypeCoordinatorinterface {
// … existing methods …// OnPreCompress is called before compaction discards messages.// It receives the messages about to be summarised/dropped and// returns a hint string for the compressor (may be empty).OnPreCompress(ctx context.Context, messages []Message) (hintstring, errerror)
// OnSessionSwitch is called whenever the active session ID changes.OnSessionSwitch(ctx context.Context, newSessionID, parentSessionIDstring, resetbool) error
}
Call site in the compactor (pkg/runtime/compactor/):
hint, _:=memCoordinator.OnPreCompress(ctx, messagesToCompress)
// pass hint to compression-summary prompt so the summariser preserves key factssummary:=compressor.Summarise(ctx, messagesToCompress, hint)
2. What OnPreCompress does
Inside the coordinator:
Scan the messages about to be discarded for memory-worthy content using a lightweight extraction heuristic (keyword density, question–answer pairs, explicit user corrections, preference statements).
For each candidate fact, call add_memory (agent-notes tier) or add_memory(target="user") (user-profile tier) as appropriate — subject to the same write-time security scan (sibling sub-issue B) and budget enforcement (sibling sub-issue A).
Return a short string summarising what was extracted (for the compressor prompt in pkg/compaction/prompts/).
Step 2 should be best-effort and non-blocking: if the extraction LLM call times out or fails, log and return an empty hint rather than blocking compaction.
3. OnSessionSwitch — snapshot refresh after session-ID rotation
Compaction typically rotates the session_id. After the new session is created:
Updates any cached session_id fields so subsequent writes land in the correct session.
When reset=true (e.g. /new, /reset), also clears any accumulated per-session turn buffers or counters.
4. Snapshot refresh on compaction
After OnSessionSwitch, the next call to inject_memories (#3015) finds a stale (generation-bumped) snapshot and rebuilds it from the DB, now including the facts extracted by OnPreCompress. The user sees the newly extracted memories injected in the very next turn — no session restart required.
5. Signal for compaction events
The compactor must emit a signal the memory coordinator can subscribe to, or the coordinator must be passed to the compactor as a dependency. Use the existing hook/event bus pattern in pkg/hooks/ if applicable; otherwise add a direct call from the compactor.
Implementation checklist
pkg/memory/coordinator.go — define Coordinator interface with OnPreCompress and OnSessionSwitch; implement on the concrete coordinator struct
OnPreCompress: lightweight extraction heuristic (no LLM call required for MVP — keyword / pattern matching is sufficient); call add_memory for each extracted fact; respect security scan and budget limits; return hint string
pkg/runtime/compactor/ call site — call OnPreCompress before discarding messages; call OnSessionSwitch after new session is created
pkg/runtime/hooks.go — propagate new session ID to memory coordinator on /reset, /new, /resume, /branch (same triggers as executeStopHooks session rotation)
Unit tests: mock OnPreCompress receives the correct subset of messages; facts are inserted via add_memory; OnSessionSwitch invalidates snapshot; reset=true clears per-session state
Integration test: run a session that accumulates memories, trigger compaction, assert extracted facts appear in the next turn's injected block
Acceptance criteria
OnPreCompress is called before any message is discarded during compaction
At least one fact from the compressed messages is extracted and persisted (when the heuristic finds candidates)
Extracted facts are subject to the same security scan as user-initiated writes (sub-issue B)
OnSessionSwitch is called after session-ID rotation; the next turn's injected block reflects the new state
reset=true clears per-session counters; reset=false preserves them
OnPreCompress failure never blocks or aborts compaction
No regression in compaction latency > 10% on the existing benchmark
Background
Sub-issue of #3011.
When the conversation grows too long, docker-agent compacts older messages via
pkg/compaction/andpkg/runtime/compactor/. Today the memory subsystem has no awareness of this event, which means:inject_memories(feat(memory): Frozen snapshot + cache invalidation for inject_memories #3017) still reflects pre-write state.session_idis broken by compaction-driven session rotation; consumers need to be told about the switch.Proposed design
1.
OnPreCompressandOnSessionSwitchhooks on a memory coordinatorDefine a new interface in
pkg/memory/coordinator.go:Call site in the compactor (
pkg/runtime/compactor/):2. What
OnPreCompressdoesInside the coordinator:
add_memory(agent-notes tier) oradd_memory(target="user")(user-profile tier) as appropriate — subject to the same write-time security scan (sibling sub-issue B) and budget enforcement (sibling sub-issue A).pkg/compaction/prompts/).Step 2 should be best-effort and non-blocking: if the extraction LLM call times out or fails, log and return an empty hint rather than blocking compaction.
3.
OnSessionSwitch— snapshot refresh after session-ID rotationCompaction typically rotates the
session_id. After the new session is created:Inside the coordinator,
OnSessionSwitch:session_idfields so subsequent writes land in the correct session.reset=true(e.g./new,/reset), also clears any accumulated per-session turn buffers or counters.4. Snapshot refresh on compaction
After
OnSessionSwitch, the next call toinject_memories(#3015) finds a stale (generation-bumped) snapshot and rebuilds it from the DB, now including the facts extracted byOnPreCompress. The user sees the newly extracted memories injected in the very next turn — no session restart required.5. Signal for compaction events
The compactor must emit a signal the memory coordinator can subscribe to, or the coordinator must be passed to the compactor as a dependency. Use the existing hook/event bus pattern in
pkg/hooks/if applicable; otherwise add a direct call from the compactor.Implementation checklist
pkg/memory/coordinator.go— defineCoordinatorinterface withOnPreCompressandOnSessionSwitch; implement on the concrete coordinator structOnPreCompress: lightweight extraction heuristic (no LLM call required for MVP — keyword / pattern matching is sufficient); calladd_memoryfor each extracted fact; respect security scan and budget limits; return hint stringOnSessionSwitch: invalidate snapshot generation counter; update cachedsession_id; reset per-session counters whenreset=truepkg/runtime/compactor/call site — callOnPreCompressbefore discarding messages; callOnSessionSwitchafter new session is createdpkg/runtime/hooks.go— propagate new session ID to memory coordinator on/reset,/new,/resume,/branch(same triggers asexecuteStopHookssession rotation)OnPreCompressreceives the correct subset of messages; facts are inserted viaadd_memory;OnSessionSwitchinvalidates snapshot;reset=trueclears per-session stateAcceptance criteria
OnPreCompressis called before any message is discarded during compactionOnSessionSwitchis called after session-ID rotation; the next turn's injected block reflects the new statereset=trueclears per-session counters;reset=falsepreserves themOnPreCompressfailure never blocks or aborts compaction