ADR-0024: Context compaction is kaged-owned, layered, observable, and operator-tunable
- Status: Accepted
- Date: 2026-05-27
- Deciders: @karasu
- Supersedes: —
- Superseded by: —
Context
Long-running agent sessions accumulate context faster than any model's context window can hold. When the window fills, something must give. Today, kaged does not own this transition — Mastra (the agentic substrate per ADR-0012) trims the message list internally when a provider call fails on context length. The operator does not see what was trimmed. The audit log does not record it. This is incompatible with kaged's manifesto commitment to operator visibility and is the silent operational hazard most likely to bite long sessions.
RFC-0004 explored the design space honestly and settled on a layered strategy model. The motivating consumer is the pre_compact and post_compact plugin hooks contracted in ADR-0023 (project-plugin lifecycle hooks): without a kaged-controlled compaction firing point, plugins that need them (notably memory plugins like a hindsight backend) are partially functional. Beyond that, the design has to be honest that compaction is itself a load-bearing operational concept, which operators in long sessions need to see, tune, and override.
Three converging pressures make this load-bearing now:
- Sessions are getting long. With ADR-0022 (recursive agents), ADR-0019 (workflows), ADR-0020 (issues), a single session can span hours.
- RFC-0003 / ADR-0023 (memory) depends on the
pre_compactandpost_compacthooks having a firing point. - The Mastra-opaque path violates the manifesto. Operator visibility is non-negotiable.
This ADR commits to the model. The mechanism details (token estimation algorithm, exact harness boundary work) are spec-level and follow.
Decision
kaged owns context compaction at the harness boundary. Compaction is a layered, configurable, observable, operator-tunable event with four built-in strategies (
drop,summarize,delegate,checkpoint), per-agent configuration, plugin participation via thepre_compactandpost_compacthooks, a plugin "compactor" role for full message-list transformation, a dedicated UI surface with operator-attached feedback per event, and reversibility via the existingsupersededmessage flag.
Specifics
kaged owns the compaction point. Mastra's internal trimming is neutralized. The harness's
reconstructMessages()path becomes the compaction point: messages are compacted before they reach Mastra'sagent.stream()call.Pre-call trigger with reactive fallback. Before each LLM call, the harness estimates token usage. If the estimate crosses the upper threshold (default 0.85 of the model's context window), compaction fires before the call. If the estimate is wrong and the provider returns a context-length error, the harness compacts reactively and retries the call.
Hard trigger with hysteresis. Crossing the upper threshold triggers compaction; the configured strategy compacts until the message list is below a lower threshold (default 0.60) so we don't oscillate. Both thresholds are operator-configurable per agent.
Four built-in strategies. Configurable per-agent in the DSL:
drop— naïve oldest-first drop until below the lower threshold. The fallback / floor strategy. Default if no compaction config is declared.summarize— summarize a window of older messages via a configured model (alias fromlocal-config.md). Operator-authored summarizer prompt referenced viaproject:/.delegate— call a designated compactor plugin (see § Plugin roles below). Plugin returns the full transformed message list.checkpoint— create a checkpoint withreason: compaction_pending, pause the session, let the operator inspect and approve.
Per-agent compaction config. Each
AgentSpecmay declare its owncompaction:block. Subagents inherit defaults from their parent unless overridden. Compaction events are per-agent because each agent has its own context window.Plugin roles. The plugin manifest gains a
role:field with values"observer" | "compactor"(or both). Memory plugins (per ADR-0023) are observers by default. A plugin that declares thecompactorrole may also return a transformed message list, replacing the strategy step:type ObserverResult = { retain?: string[]; inject?: string } | null; type CompactorResult = { messages: Message[]; // the new (compacted) message list superseded: MessageId[]; // which original messages to mark superseded summary?: string; // optional human-readable summary for the audit log };The plugin host enforces at most one compactor role per agent. A single plugin may declare both roles; the hindsight reference integration is the canonical such case.
Compactor plugin failure → fall back to
drop. If the designated compactor plugin errors, throws, or times out, the harness falls back to thedropstrategy and logs the full failure chain to the audit log as a single event. Compaction must complete; a session never stalls on a broken plugin.Streaming behavior. Compaction operates between LLM calls, not during one. If a stream is in flight and compaction is needed, the next call is delayed until the current stream completes. The compaction check happens at the start of each call.
Atomicity of tool-call/tool-result pairs. The compaction unit is the tool-call/tool-result pair, not the individual message. Strategies operate on pairs; summarization receives the full pair as one unit; drop drops the pair together. The pair is never split across the compaction boundary.
Always-keep set. Kaged has a default always-keep set: the system prompt and the first operator message. Plugins can mark additional messages as always-keep via the existing message-metadata mechanism (per ADR-0022 / agent-tooling metadata machinery). Operators can configure additional always-keep predicates per project. Always-keep messages are never compaction candidates regardless of strategy.
Reversibility via
supersededflag. Compaction marks original messagessuperseded = trueand creates a new summary message (forsummarize/delegate). Original messages remain in storage. Rolling back past a compaction point unsupersedes the affected range — samesupersededflag, same rollback machinery as ADR-0022 / session-manager. No new flag, no new mechanism.Operator/model asymmetric history access. The operator can always see superseded messages via the audit log and the message-history API with an explicit
include_superseded=trueflag. The model only sees the reconstructed compacted list. The asymmetry is the point.First-class Compactor UI. A dedicated UI surface (
docs/specs/ui/compactor.md, new) provides:- Live stats per session: events fired, tokens before/after, summary length distribution, model cost per event, plugin hook timings, fallback occurrences.
- Tunable knobs for every compaction parameter — thresholds, window sizes, preserve-recent count, model selection, strategy, etc. Rendered as sliders (range with step), enums, free-text, boolean toggles, and model-alias pickers.
- Plugin-defined knobs. Compactor and observer plugins declare their configurable parameters in the plugin manifest using a kaged-defined knob schema (range with step, enum, text, boolean, model-alias picker). The UI renders the controls automatically from the manifest. Operators do not edit plugin source to tune behavior.
- Feedback loop. Every compaction event has an operator-attachable flag (
good/bad/neutral) and a free-text notes field. Stored on theCompactionRecord. Invaluable during plugin development; a compactor-plugin author can iterate on a signal tied to operator judgment, not just cost numbers. - Manual trigger. The operator can fire a compaction at any time, with any configured strategy, from this view. Same machinery as automatic compaction; trigger source recorded in the audit log.
- Strategy preview / dry-run. The operator can see what a compaction would do against the current message list before committing.
The view is reachable from the session pane (per-session) and from project status (aggregated).
Cost surfacing. Summarizer model calls are tracked as a separate line item in the session cost breakdown (extending the existing
stats.costsurface fromagent.md§ Stats). The UI's compactor view surfaces this prominently; the session cost panel showsprimary: $X, compactor: $Yas two line items.Audit log. New audit event types:
compaction.triggered,compaction.completed,compaction.failed,compaction.flagged(operator feedback). Each event captures the trigger reason, strategy, before/after message IDs, model used (if any), plugin hooks fired, durations, and operator annotations.API parity. Every UI action has an API endpoint. Operators can drive compaction programmatically: manual-compact, compaction-history, compaction-flag, knob-schema (per plugin).
Consequences
What this commits us to
- A
CompactionRecordrow in@kaged/storageanalogous toCheckpointRecord, includingflag(good/bad/neutral) andnotesfields. Schema migration in@kaged/storage. - Token estimation in
@kaged/llm, pre-call. Existing model-metadata catalog is already in place; this extends it with an estimator. - Harness ownership of the message-reconstruction-to-compaction-to-LLM path. Mastra's internal trimming neutralized.
- A
compaction:block inAgentSpec(@kaged/dslschema work). - A
role: "observer" | "compactor"field on the plugin manifest (@kaged/plugin-types+@kaged/plugin-host). - A compactor-return-shape contract in the plugin-host spec.
- A plugin knob schema in the plugin manifest (range/enum/text/bool/model-alias picker). The UI renders from this schema; operators tune plugin behavior via the rendered controls.
- A new UI spec at
docs/specs/ui/compactor.mdand a new UI surface implementing it. - New API endpoints: manual-compact, compaction-history, compaction-flag, knob-schema-for-plugin.
- New audit event types (4 above).
- Documentation in
docs/specs/agent.mdexplaining how Mastra's internal trimming is neutralized.
What this forecloses
- A Mastra-opaque compaction path. Mastra trimming context-length-on-failure without kaged visibility is no longer an acceptable production mode.
- A "one strategy fits all" model. The strategy is per-agent and configurable; no global default beyond
drop. - Silent compaction events. Every event is in the audit log, the UI, and the cost breakdown.
- Compactor plugins that can stall a session. Plugin failures fall back to
drop; the contract is "compaction must complete." - Free-form plugin tuning UI. Plugins that want operator-visible tuning declare their knobs in the manifest using the kaged schema; ad-hoc plugin-rendered settings UIs are not part of the model.
What becomes easier
- Long sessions. Operators can see what's happening to their context and intervene before things go sideways.
- Memory plugin development. The hindsight reference integration's compaction hook now has a real firing point. Plugin authors can iterate against operator-attached feedback.
- Cost reasoning. The compactor cost is a line item; operators see the price of compaction directly.
- Reversibility. Compaction joins rollback under the same
supersededmechanism — one mental model for "things kaged can undo." - Building custom compactor plugins. The knob schema, the feedback loop, and the dry-run all support fast iteration.
What becomes harder
- Implementation. The harness boundary work is non-trivial — token estimation, Mastra coordination, message-list transformation, plugin orchestration with failure handling.
- Spec surface. Six spec amendments plus a new UI spec.
- Plugin-author UX. Plugin authors who want operator-visible tuning have to declare knobs in their manifest using the kaged schema. The reward is automatic UI rendering and consistent operator experience; the cost is one more spec to read.
- Operator cognitive load on the compactor view. Tunable knobs invite tuning; tuning invites tinkering. Documented; the default strategy (
drop) is supposed to be boring enough that operators only touch the view when they have a reason.
Spec amendments required
Each lands in its own PR per ADR-0003, citing this ADR.
| # | File | Change |
|---|---|---|
| 1 | docs/specs/agent.md |
Add § Compaction, which defines reconstructMessages()-as-compaction-point, the pre-call estimation flow, Mastra-trimming neutralization, the hook firing order (pre_compact fires before strategy execution, post_compact fires after), the dry-run path, the always-keep set. |
| 2 | docs/specs/session-manager.md |
Extend the checkpoint protocol with reason: compaction_pending. Document the CompactionRecord shape (including flag and notes). Document the rollback-past-compaction unsuperseding behavior. |
| 3 | docs/specs/plugin-host.md |
Add role: "observer" | "compactor" to the manifest. Document the CompactorResult return contract. Document the at-most-one-compactor-per-agent rule. Define the plugin knob schema (range with step / enum / text / boolean / model-alias picker). Document the compactor-failure-fallback path. |
| 4 | docs/specs/http-api.md |
Add manual-compact endpoint (POST /api/v1/sessions/:id/compact), compaction-history endpoint, compaction-flag endpoint (PATCH /api/v1/sessions/:id/compactions/:cid), knob-schema-for-plugin endpoint. |
| 5 | docs/specs/llm.md |
Document pre-call token estimation API. Document how the existing model-metadata catalog feeds the estimator. |
| 6 | docs/specs/project-dsl.md |
Add AgentSpec.compaction block — schema, defaults, inheritance from parent. Update Appendix A. |
| 7 | docs/specs/ui/compactor.md (new) |
The compactor view spec — per-session and aggregated views, knob rendering from plugin manifest, history table, flagging UI, dry-run mode, manual trigger flow. |
| 8 | STATUS.md |
New row(s) for compaction + knob-schema work; updates to existing harness / plugin-host / storage / ui rows. |
After spec PRs land:
- Storage migration.
CompactionRecordtable created. - Tests. Estimator tests in
@kaged/llm, harness compaction tests in@kaged/harness, host role-discrimination tests in@kaged/plugin-host, UI knob-rendering tests in@kaged/ui. End-to-end test from operator-triggers-compact → harness → strategy → plugin hooks → persist → audit → UI render. - Implementation. Per ADR-0003, tests first, code second.
- Reference compactor plugin.
@kaged/memory-hindsight(or community equivalent) declares bothobserverandcompactorroles. The markdown plugin (@kaged/memory-markdown) declares onlyobserver.
Open questions
All twelve open questions from RFC-0004 were resolved during conversation. They are recorded here for cross-reference; the resolutions in this ADR are the load-bearing record:
- Pre-call vs post-failure trigger → pre-call primary, reactive fallback (decision item 2).
- Threshold semantics → hard trigger with hysteresis (decision item 3).
- Per-agent vs primary-only compaction → per-agent (decision item 5).
- Compactor plugin failure → fall back to
drop, log loudly (decision item 7). - Streaming during compaction → wait; compaction between calls (decision item 8).
- Manual compaction interface → full Compactor UI with knobs, stats, feedback, dry-run (decision item 13).
- Tool-call/tool-result pair coupling → atomic; never split (decision item 9).
- Always-keep set → kaged default + plugin metadata + operator predicates (decision item 10).
- Cost surfacing → separate line item (decision item 14).
- Model access to superseded messages → no by default; operator can inspect (decision item 12).
- Rollback interaction → same
supersededflag, same machinery (decision item 11). - Single plugin in both roles → allowed; hindsight is the canonical case (decision item 6).
No questions remain unresolved at ADR acceptance.
Alternatives considered
Alternative A — Status quo (Mastra-opaque trimming)
Don't take ownership; let Mastra handle context-length pressure internally.
Why tempting: zero implementation cost. No coordination problem.
Why rejected: silently violates the manifesto. RFC-0003's pre_compact and post_compact hooks have no firing point and the hindsight integration is permanently degraded. Long sessions become unauditable at the moment they most need auditing.
Alternative B — Naïve drop as the only strategy
Drop oldest non-superseded messages until below threshold. No summarization, no plugin delegation, no checkpoint integration.
Why tempting: simplest possible implementation. Predictable, fast, observable.
Why rejected: loses information. The oldest messages are often the highest-context-value (task definition, initial constraints, operator's original prompt). The pre_compact and post_compact hooks can mitigate but only for projects with memory plugins; projects without become silently worse over time. Drop is the right fallback / floor strategy; it is the wrong default ceiling.
Alternative C — Summarization-only
Always summarize via a configured model. No drop, no plugin delegation, no checkpoint.
Why tempting: preserves information; one consistent path.
Why rejected: summarization is expensive and the prompt is operator-authored — one more thing to maintain. Cross-tool-call dependencies break when summarization removes a referenced message. A bad summary destroys context as effectively as a drop. Should be one strategy among several, not the only one.
Alternative D — Always checkpoint on compaction
Every compaction event creates a checkpoint and pauses the session.
Why tempting: maximum operator control; nothing lost without explicit consent.
Why rejected: unattended runs cannot proceed. High-friction for sessions that should be background-runnable. Some operators want this; many don't. Should be a configurable strategy, not the default.
Alternative E — No plugin involvement; strategies are kaged-internal
Compaction strategies are built into kaged; plugins cannot participate.
Why tempting: simpler. Fewer moving parts. No plugin-failure-mode question.
Why rejected: the hindsight integration is the motivating consumer of pre_compact and post_compact. Without plugin participation, memory backends cannot survive context compaction, which is exactly the problem this ADR exists to solve. Plugin involvement is load-bearing for RFC-0003 / ADR-0023.
Alternative F — Plugin knobs as free-form UI rendered by the plugin itself
The plugin owns its own configuration UI; kaged renders it in a <iframe> or sandbox.
Why tempting: maximum plugin flexibility. No knob-schema constraint.
Why rejected: breaks the consistent operator UX. Different plugins would have different look-and-feel for configuration. The kaged-defined knob schema (range/enum/text/bool/model-alias picker) covers the realistic configuration surface for almost every plugin; the constraint is the feature.
Amendments
2026-05-30 — Context size overrides via model metadata override system (ADR-0026)
ADR-0026 introduces a model metadata override system that stores per-provider+model overrides in the DB, merged with LiteLLM defaults at read time.
Impact on compaction:
maxInputTokensis overridable. The context window used for compaction threshold calculation (decision item 3:fraction >= upper_threshold) is the effective context window — LiteLLM default + operator override. When an operator overridesmaxInputTokensfor a model, the compaction system uses the overridden value.Self-hosted models get compaction support. Models not in the LiteLLM catalog (Ollama, vLLM, fine-tunes) previously fell back to a 32k conservative default with a per-call warning. Operators can now set
maxInputTokensexplicitly, making compaction thresholds accurate for self-hosted models.No DSL changes. The
compaction:block inAgentSpeccontinues to configure thresholds (0.85/0.60 defaults), strategies, and rules. The context window those thresholds are calculated against now comes from the override-awareresolveModelMetafunction. Compaction thresholds are ratios; the override changes the denominator, not the ratio.resolveModelMetareplaceslookupModelMetain the compaction path. The harness and the token estimator callresolveModelMeta(provider, modelId, overrides)instead oflookupModelMeta(provider, modelId). The merge semantics (override wins, missing fields from defaults) are defined in ADR-0026.
No new decision items. This amendment extends the data source for the existing context window calculation. The compaction behavior (triggers, strategies, atomicity, reversibility) is unchanged.
2026-05-31, Hook rename to pre_compact and post_compact
The single compaction hook is split into two distinct hooks to clarify roles and execution order:
pre_compactfires before strategy execution. It is mutable for compactor plugins and readable for observer plugins.post_compactfires after strategy execution. It is readable for observer plugins.
This rename resolves ambiguity and ensures clean separation of concerns.
2026-06-06 — Implementation hardening: pruning pre-pass, fallback context window, no-op guard, reactive retry cap, summarize-at-threshold upgrade
Implementation of the compaction pipeline revealed several gaps between the ADR design and operational reality. This amendment documents the fixes and additions, all modeled after the reference implementation in oh-my-pi.
1. Tool output pruning pre-pass
A new lightweight context-reduction step runs before the compaction pipeline. Large tool-result messages (file reads, search results, etc.) accumulate quickly and often represent stale context that does not warrant full compaction. The pruning pre-pass:
- Walks tool-result messages from newest to oldest.
- Protects the most recent 40,000 tokens of tool output (configurable via
PruneConfig.protectTokens). - Replaces older large tool results with a short
[Pruned — N tokens]notice. - Never prunes protected tool names (
read,skillby default — configurable viaPruneConfig.protectedTools). - Skips results where the pruned notice would be as large as the original (no net savings).
- Only applies if total savings exceed a minimum threshold (default 20,000 tokens via
PruneConfig.minimumSavings).
Pruning is not compaction — it does not mark messages superseded, does not produce a CompactionRecord, and does not fire plugin hooks. It is a pre-pass that reduces context pressure so that full compaction fires less often. The pruned message list feeds into runCompactionPipeline() as the input.
Implementation: @kaged/harness pruning.ts (pruneToolOutputs), wired in primary-runner.ts before runCompactionPipeline().
The pipeline diagram in agent.md is amended to include the pruning step between reconstructMessages() and token estimation.
2. Fallback context window
When ModelMeta is unavailable (model not in the LiteLLM catalog, no operator override via ADR-0026), the token estimator previously returned fraction: 0 — making proactive compaction impossible and causing the context to grow until the provider rejected it.
The estimator now uses a fallback context window of 128,000 tokens when modelMeta.maxInputTokens is null or absent. This ensures fraction is always positive and proactive compaction can trigger. The contextWindow field in the estimation result remains null to signal that the value is a fallback, not authoritative.
128,000 was chosen as a conservative lower bound: it is below current frontier models (200k–1M+) so compaction triggers early rather than late, and above older models where the operator should have configured an override via ADR-0026.
Implementation: FALLBACK_CONTEXT_WINDOW constant in @kaged/llm estimate-tokens.ts.
3. No-op guard
If a compaction strategy executes but supersedes zero messages (e.g., all messages are in the always-keep set, or the message list is already below the lower threshold), the pipeline now returns { compacted: false } instead of producing an empty CompactionRecord. This prevents:
- Persisting meaningless compaction records.
- Reactive retry loops where compaction "succeeds" but context is unchanged, causing the same provider error on retry.
The no-op guard emits a compaction.noop audit event with the reason "strategy superseded zero messages" and the attempted strategy.
Exception: dry-run mode skips the no-op guard — dry-runs always produce a record for operator inspection, even when the strategy would be a no-op.
Implementation: post-strategy check in runCompactionPipeline() (compaction.ts).
4. Reactive retry cap
The reactive fallback path (§ Reactive fallback retry in the spec) now enforces a single retry via a reactiveRetryAttempted flag in dispatchPrimary. Once a reactive compaction + retry has been attempted (whether it succeeded or not), no further reactive compaction is attempted for that dispatch cycle. This prevents infinite loops when:
- The model returns a context-length error.
- Compaction runs but cannot reduce context enough (no-op guard fires).
- The retry would hit the same error.
Additionally, when the reactive path's no-op guard fires (compaction didn't reduce context), the retry is skipped entirely — the original error propagates. Context-overflow error messages from failed provider calls are not persisted to the message history, preventing them from consuming context on subsequent runs.
The spec already documented "at most one retry" (agent.md § Reactive fallback retry); this amendment confirms the implementation mechanism.
Implementation: reactiveRetryAttempted flag in primary-runner.ts dispatchPrimary().
5. Summarize-at-threshold upgrade for drop strategy
When the drop strategy is configured (the default), the pipeline now attempts summarization first if both conditions are met:
- A
summarizeFnis available (the harness can call a model). - The agent's compaction config includes a
summarizeblock (agentCompaction.summarize).
If summarization succeeds, the result is used (better context preservation than pure drop). If summarization throws, the pipeline falls back to pure drop silently — the contract that "compaction must complete" is preserved.
This means operators who configure a compaction.summarize block get automatic summarization even with strategy: "drop", without changing their strategy. The drop strategy becomes "summarize if possible, drop if not" — a strictly better default that loses no information when a summarizer is available.
Implementation: case "drop" branch in runCompactionPipeline() (compaction.ts).
6. Provider prefix mapping fix (copilot)
The KAGED_TO_LITELLM_PREFIX map in model-meta.ts was missing the copilot provider entry. This caused lookupModelMeta("copilot", ...) to return null, which cascaded through the fallback context window gap (item 2 above) to make compaction non-functional for Copilot-backed sessions. The fix adds copilot → "openai/" to the prefix map.
Implementation: model-meta.ts in @kaged/llm.
No new decision items. These are implementation-level fixes and enhancements within the existing compaction design. The ADR's behavioral commitments (triggers, strategies, atomicity, reversibility, plugin participation, operator visibility) are unchanged. The pruning pre-pass is a new mechanism but operates below the compaction boundary — it is a context-reduction optimization, not a compaction strategy.
Amendment: 2026-06-06 — Default compaction summary prompt, summarize defaults, UI manual-compact overrides
Three changes extend the compaction surface to be usable out of the box without any operator configuration.
7. Default compaction summary prompt
Every kaged project now ships a default compaction summary prompt at config:/prompts/compaction-summary.md. The prompt produces a structured context checkpoint handoff (Goal, Constraints & Preferences, Progress, Key Decisions, Next Steps, Critical Context, Additional Notes) adapted from the oh-my-pi reference implementation's compaction-summary.md.
- Auto-creation.
kaged project initscaffolds the file into.kaged/prompts/compaction-summary.mdalongside the existingdefault.mdsystem prompt. If the file is missing when a project is loaded, the daemon writes the default content at project-load time. - Operator-owned. The file is config, not a managed artifact. Operators are free to edit it after creation.
- Override.
compaction.summarize.promptcan point to anyproject:/orconfig:/path.
8. summarize.model and summarize.prompt defaults
The summarize block within compaction: now has full defaults:
modelis optional (was required). When omitted, the summarizer uses the agent's own model alias.promptdefaults toconfig:/prompts/compaction-summary.md(was required).- The no-config fallback now includes a
summarizeblock (window_messages: 20,preserve_recent: 10,max_summary_tokens: 1500,prompt: config:/prompts/compaction-summary.md).
This means the drop strategy's summarize-at-threshold upgrade (item 5 above) works without any operator configuration — the default fallback config includes everything needed.
9. UI manual-compact override controls
The Compactor view's manual-compact modal is extended from a two-field confirmation (agent + strategy) to a full per-invocation override panel:
- Strategy override — segmented control defaulting to the agent's synthesized
compaction.strategy. - Summarize toggle — checkbox visible for
drop/summarizestrategies, controlling whether summarize-at-threshold is attempted. - Threshold sliders —
upper_thresholdandlower_thresholdpre-filled from synthesized DSL. - Prompt path override — pre-filled from
compaction.summarize.prompt.
All overrides are per-invocation — they apply to the single compaction and do not modify the project DSL. The existing Knobs panel remains the surface for persistent config changes.
The POST /sessions/:id/compact endpoint accepts the override fields alongside the existing agent_path, strategy, and dry_run. Omitted fields default to the agent's synthesized DSL config.
No new decision items. These are extensions within the existing compaction design. The ADR's behavioral commitments are unchanged.
References
- RFC-0004 — Context compaction (the design exploration backing this ADR)
- RFC-0003, Agent memory (the motivating consumer of
pre_compactandpost_compact) - ADR-0003 — Doc-first, then TDD (the process this ADR feeds into)
- ADR-0012 — Mastra substrate (the trimming this ADR neutralizes)
- ADR-0016 — Streaming-first UI (the foundation the compactor view builds on)
- ADR-0022 — Per-agent config (the posture per-agent compaction extends)
- ADR-0023, Project-plugin lifecycle hooks (the hook contract
pre_compactandpost_compactride on) docs/specs/agent.md— Harness spec (the load-bearing amendment)docs/specs/session-manager.md— Checkpoint protocol (extended for compaction_pending)docs/specs/plugin-host.md— Plugin host spec (role + knob-schema amendments)- ADR-0026 — Cost management, model metadata overrides (context size override mechanism)