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
Session compaction is intentionally lossy, and the information that tends to hurt most when lost is not generic conversation detail but decision state:
the latest plan that the user accepted / asked the agent to execute;
explicit user product/architecture decisions and constraints;
rationale/trade-offs that explain why a decision was made;
rejected alternatives that must not be proposed again;
current plan progress / remaining steps;
unresolved questions or blockers.
In long coding sessions this causes a familiar failure mode: after compaction the agent still remembers the broad goal, but silently drops part of an agreed plan or a user decision, then re-plans from incomplete state, reopens already-settled choices, or implements something inconsistent with the pre-compaction agreement.
This gets worse over multiple compactions: even a good first summary may be summarized again later, causing progressive information decay unless critical decision state is treated as cumulative state rather than ordinary prose.
Research / precedent
This problem is already visible in several coding-agent implementations, and the better implementations point toward preserving structured continuation state rather than relying on an unconstrained generic summary:
The recently merged compaction-event work in #312 is currently a specification, not a runtime implementation on main yet.
More importantly, that design intentionally makes CompactionObservermetadata-only and non-mutating. It exposes lifecycle/evidence/correlation data but no raw prompt/response body, and observers return no replacement/decision. That is the correct contract for detection and should not be weakened for this feature.
Therefore this feature should reuse the same compaction recognition/rule authority, but it needs a separate content-preservation/mutation path. Detection, extraction, and compaction-summary augmentation are different responsibilities.
There is also a timing issue: the planned started event is emitted only after an upstream B-leg successfully opens. If we want to augment the current compaction request before the compaction model generates its summary, a post-Open observer is already too late. The shared compaction matcher/classifier therefore needs to be reusable at the pre-open canonical request seam without duplicating the signature rules.
Proposed model: a versioned Continuity Capsule
Do not treat this primarily as "generate another summary". Maintain a small, structured, versioned piece of state containing only information that must survive lossy compaction.
The exact schema can differ, but the important property is that the proxy stores/merges typed continuation facts, not an ever-growing block of natural-language history.
Semantics must be explicit
Latest explicit user intent wins. If the user changes a decision later, the old one becomes superseded rather than both surviving as active facts.
Assistant brainstorming/proposals are not user decisions merely because they appeared in the transcript.
An assistant plan becomes accepted only when there is actual evidence of user acceptance / instruction to proceed, or a harness exposes an authoritative current plan state. Otherwise it remains provisional and should not be fossilized as settled product direction.
Explicit user rejections should be preserved when they constrain future work, so the agent does not repeatedly propose a rejected architecture.
Preserve rationale/trade-offs when explicitly provided and useful; this is often what prevents a later model from re-litigating a settled choice.
Keep the capsule bounded. It is continuity state, not a second transcript.
Extraction pipeline
1. Deterministic extraction first
Before paying for another LLM call, harvest information that coding harnesses already expose structurally.
Examples include versioned signatures for:
Codex update_plan state;
OpenCode todowrite / todo state;
Cline-style task_progress checklists where applicable;
known plan-mode / proposed-plan carriers;
other harness-specific structured planning state already covered by the compaction signature survey.
This should share the same versioned agent-signature/rule authority as compaction detection. We should not create a second unrelated matrix of hard-coded provider/agent checks.
A structured current plan should be copied/normalized mechanically. Do not ask an LLM to infer data the proxy already has in machine-readable form.
2. Cheap local eligibility heuristics
Run the semantic extractor only when there is evidence it may add information, for example:
a planning/plan-mode marker was observed;
a structured plan/TODO was created or revised;
an assistant emitted a substantial plan followed by an affirmative/corrective user response;
user turns contain candidate constraints/choices/corrections not already represented in the capsule;
the previous capsule is absent/stale and this compaction covers a substantial unseen history range.
These heuristics should optimize unnecessary calls, not decide semantic truth. Avoid a fragile regex system that declares arbitrary assistant prose to be an "accepted plan".
3. Optional auxiliary-model extraction for unstructured decisions
Where deterministic extraction is insufficient, use the existing auxiliary request mechanism (Aux.Collect) with an operator-selected route/model rather than inventing another provider client path.
The extraction model is deliberately independent from the model used by the primary coding session. The configured extractor route may point to a completely different model and provider. A session using an expensive frontier coding model may, for example, use a small/fast/cheap model for continuity extraction.
The auxiliary call should:
run with this preservation plugin disabled to prevent recursion;
have a strict timeout and token budget;
have tools disabled / no side effects;
receive the previous capsule plus only the relevant new pre-compaction context;
produce strict schema-constrained JSON which is validated before use;
treat transcript text as untrusted data, not as instructions to the extractor.
One extractor/normalizer call should be enough. There should not normally be a second LLM round trip merely to "improve" the ordinary compaction summary.
This requirement is important and should not be implemented as an extra visible assistant turn inside the user's main agent conversation.
The semantic extractor MUST execute as a proxy-created auxiliary/internal LLM invocation outside the primary agent session context:
it is not appended to the user's main conversation as a user/assistant turn;
its prompt/context is the dedicated extraction input described above, not the live main-agent prompt stack;
its response is consumed by the proxy and converted into validated continuity state, not surfaced as an assistant message;
it has independent request/B-leg execution and model routing while retaining parent session/A-leg/principal correlation for accounting and diagnostics.
The actual model call SHOULD execute through an independent asynchronous worker boundary — for example a goroutine/worker pool in-process or an external worker process — rather than running the extractor model inline inside the main session's normal B-leg/stream-processing stack. The exact scheduling mechanism is an implementation choice; the semantic contract is that extraction is separate work with its own lifecycle and resource budget.
This does not mean fire-and-forget. When continuity state is required before a compaction/continuation can safely proceed, the main flow may wait at a narrow, explicit preservation barrier for the relevant capsule revision. The main session must never depend on the extractor sharing its execution thread, model, provider connection, or conversational context.
Preferred optimization: do not wait until the compaction request itself if the relevant decision state was observable earlier. Candidate plan/decision-bearing turns may enqueue background extraction proactively. Then a later compaction usually consumes an already-current capsule; the compaction boundary only needs to flush/wait when the worker is behind the required transcript revision.
Conceptual worker identity/correlation should be explicit, e.g.:
A dedicated worker must remain bounded: no unbounded goroutine-per-event spawning, no unbounded queue, and no extraction job should outlive its useful session/revision indefinitely.
Billing and usage attribution
The auxiliary extractor is real additional model usage and is billable. Although it is not a conversational turn in the user's visible main-agent transcript, for usage/accounting purposes it is an additional proxy-initiated LLM inference/agent turn caused by that user's session.
Default accounting semantics:
all extractor B-leg usage/cost is attributed to the same authenticated principal/account that owns the originating session;
the call participates in the normal applicable usage-authority, credit/admission, metering, billing, and provider-cost accounting paths instead of bypassing them;
retries/failover, if permitted for the extractor route, are accounted exactly as other submitted B-legs are accounted;
user/account totals MUST include extractor input/output/cache/cost usage;
operator/user-facing accounting SHOULD distinguish primary inference from auxiliary continuity extraction so the additional cost is visible rather than silently blended or hidden;
the auxiliary call must not be charged to an unrelated system/operator account merely because the proxy initiated it.
If the user's applicable billing/admission policy rejects the extractor call, the default preservation behavior should remain fail-open: skip semantic extraction (while retaining any deterministic continuity state) and allow native compaction/session processing to continue. The proxy must not bypass billing authorization simply because the call is internally generated.
A future explicitly configured operator-funded/system-funded extractor may be useful, but that would be a separate opt-in accounting policy. Originating-user attribution is the default.
This cost implication should be documented for operators/users because enabling the feature can generate additional billable inference calls beyond those visibly initiated by the main agent.
Transcript/input preparation
Do not blindly resend the entire raw session transcript to the extractor on every compaction.
Preferred source order:
the exact canonical pre-compaction context already visible to the proxy / carried by the compaction request;
existing secure-session transcript data when transcript capture is enabled and additional history is required;
otherwise a small bounded feature-owned ephemeral window if needed.
Go-LIP already has secure-session transcript recording when TranscriptEnabled is active. Reuse it rather than building a second durable transcript database.
For extractor input:
preserve user messages with high priority;
preserve assistant plan/proposal/clarification messages relevant to decisions;
preserve structured plan/TODO tool calls and their small arguments/results;
strip or heavily truncate ordinary tool outputs, shell logs, file dumps, compiler output, large code blocks, images/binary material, etc.;
by default exclude untrusted external/tool-result text from semantic decision extraction unless explicitly needed, both for token cost and prompt-injection resistance;
include the previous continuity capsule so extraction is incremental rather than repeatedly reconstructing the entire history.
This should make cost roughly proportional to the delta since the prior preservation boundary, not total session age.
How to get the capsule through compaction
The reliable path should be designed explicitly instead of doing fire-and-forget work from a completion observer.
Preferred path: pre-compaction augmentation
When the shared classifier identifies a compaction request before upstream Open:
determine the required transcript/capsule revision;
consume an already-completed background extraction result when available, or request/flush the missing extraction work;
wait only at a bounded preservation barrier if the required capsule revision is still in flight;
inject a bounded, clearly delimited continuity block into the compaction-generation context/instructions;
require the compaction model to preserve these facts in its continuation summary;
if a provider-neutral result-augmentation seam is available, mechanically merge/append the validated capsule into the generated compaction summary as the final guarantee.
Current Go-LIP already has canonical request mutation surfaces (request.Transform / request-part hooks), and response-part hooks can mutate canonical response events. We should reuse an existing safe seam where possible, but a dedicated typed CompactionAugmenter / BeforeCompaction surface may be cleaner than forcing stream-fragment manipulation into a generic hook.
The important distinction is: the extractor model executes independently/background; synchronization happens only at the preservation boundary when its result is actually required.
Fallback: post-compaction reinjection
Some provider-native/opaque compaction mechanisms may not expose a safely mutable summary. For those, persist the capsule and inject it as proxy-owned continuation context on the first eligible post-compaction turn (or according to a well-defined bounded reinjection policy).
The timing invariant is important: the first model turn after compaction must not race ahead of required preservation state.
A completely unsynchronized fire-and-forget extractor is therefore insufficient: the next request can arrive before the worker finishes. The session/A-leg needs a revision-aware barrier/state transition ensuring the required capsule revision is ready before the first post-compaction B-leg. That barrier does not turn extraction into a main-session model call; it only joins independently executed background work when necessary.
Repeated compactions / incremental merge
This is a first-class requirement.
For compaction N+1, do not merely summarize whatever survived compaction N.
Use:
previous continuity capsule
+ new conversation/plan delta since previous capsule revision
-> validated merged continuity capsule
-> compaction N+1
Required properties:
active decisions survive indefinitely until explicitly superseded/removed;
superseded decisions do not reappear as active after a later compaction;
completed plan steps can be dropped/condensed according to policy while pending steps remain;
capsule size does not grow monotonically because of duplicate facts;
duplicate/replayed compaction lifecycle events are idempotent;
store a revision / source compaction transaction ID / digest so retry and failover cannot double-apply an update;
stale worker results are rejected if a newer capsule/session revision already exists.
Scope and ownership
State should be scoped to the authoritative continuity identity, not arbitrary client hints.
At minimum define behavior for:
SessionID + A-leg continuity;
new session;
A-leg reset;
fork/clone/branch;
resumed durable session;
concurrent turns.
A fork should not accidentally receive decisions from an unrelated branch. If inheritance on fork is desired, make that an explicit copy-on-fork rule rather than a consequence of using a broad session key.
Concurrent updates need latest-revision / compare-and-merge semantics so an older extractor result cannot overwrite a newer user decision.
Worker jobs must use the same authoritative parent identity and cannot derive ownership from client-supplied session hints.
Privacy / security boundary
Calling an auxiliary remote LLM with session history is a new data-egress path and must be treated as such.
Requirements:
feature disabled unless explicitly configured;
operator explicitly chooses extractor route/model (or a clearly documented safe default if we ever add one);
extractor route/model is independent from the main-session route and may use a different provider;
session-level enable/disable is trusted policy metadata, not an unauthenticated client-controlled header;
existing transcript/redaction policy must be respected;
if transcript capture is disabled, this feature must not silently enable durable full-transcript capture;
never persist or log extractor input/output verbatim by default;
do not send secrets, full tool outputs, raw file dumps, credentials, or unrelated external content merely because they appeared earlier in the session;
auxiliary request must suppress the preservation plugin itself and remain subject to auxiliary-depth protections;
multi-user deployments must retain the same principal/session isolation as the parent request;
background worker queues/state must not become a cross-session data leakage surface.
The capsule itself may be session state, but its retention/TTL and audit visibility must be explicit.
Configuration
Configurable globally at the feature/proxy level and overridable per trusted session policy.
Conceptual options (exact YAML nesting can follow the feature-plugin config conventions):
workers / max_pending_jobs are conceptual controls rather than a commitment to an in-process worker-pool implementation; an external worker implementation needs equivalent bounded concurrency/backpressure semantics.
Session override precedence should be explicit, e.g. trusted session policy > global default. A session must not be allowed to self-enable external transcript export or change billing attribution unless the configured authorization model permits it.
Changing the main session model/route MUST NOT implicitly change the extractor model/route. Extractor routing changes only through its own effective configuration/session policy.
Failure behavior
Preservation improves UX; it must not make model traffic fragile.
no candidate plan/decisions -> no auxiliary call;
extractor queue saturation -> skip/defer according to bounded policy; never unbounded enqueue;
extractor billing/admission rejection -> do not bypass account policy; retain deterministic state and continue according to preservation failure policy;
malformed/schema-invalid extractor output -> discard it, do not inject arbitrary text;
ambiguous inferred decision -> omit/mark provisional rather than converting it into authoritative user intent;
duplicate compaction event -> idempotent no-op;
stale worker result -> reject rather than overwrite a newer capsule revision;
capsule over budget -> deterministic compaction/drop priority, never unbounded growth;
persistence unavailable -> explicit metric/log and native compaction behavior, unless a future operator policy deliberately chooses fail-closed.
Observability
Add content-free metrics/diagnostics for:
compaction candidates detected;
preservation skipped by heuristic;
deterministic structured-plan hits;
auxiliary extraction jobs queued/started/completed/dropped;
Normal logs should expose IDs/revisions/counts, not capsule contents.
Acceptance criteria
An explicitly accepted multi-step plan is still present and actionable after compaction.
Explicit user architecture/product decisions and constraints survive compaction even if they were made many turns earlier.
A later user correction supersedes the earlier decision; both are not presented as simultaneously active.
Assistant-only brainstorming is not promoted to an accepted user decision.
A user-rejected alternative does not reappear as the active plan after compaction.
Structured plan state (e.g. Codex update_plan, OpenCode todos, supported versioned equivalents) can be preserved without an extractor LLM round trip when sufficient.
A session with no relevant planning/decision state performs zero unnecessary extractor calls.
Three or more successive compactions preserve the active decision set without duplication or progressive loss.
Ordinary large tool outputs / shell logs / file dumps are not sent to the extractor by default.
With secure-session transcript capture disabled, enabling this feature does not silently create a durable full transcript.
Auxiliary extraction cannot recurse into itself.
Extractor timeout/error/malformed output leaves normal compaction functional.
Semantic extraction executes as a separate auxiliary/internal invocation and is never added as a visible turn to the primary agent conversation.
The extractor model/route is independently configurable and may use a completely different model/provider from the primary session without changing the main session route.
Extractor inference runs behind an independent bounded worker boundary rather than sharing the main session's model-call/stream execution lifecycle.
When a required extractor result is still in flight, the main session joins it only at an explicit bounded revision-aware preservation barrier; there is no unsynchronized post-compaction race.
An extractor invocation is accounted as additional billable LLM usage attributable to the originating authenticated user/account by default.
User/account usage and cost totals include auxiliary extraction, while diagnostics/accounting can distinguish continuity-extraction cost from primary inference cost.
Billing/admission rejection for the auxiliary call is not bypassed and does not fall back implicitly to an unrelated system account.
Duplicate/retried compaction lifecycle signals cannot duplicate the capsule or injected block.
Fork/reset/new-A-leg behavior is deterministic and tested; state cannot leak into an unrelated branch.
Concurrent turns cannot let an older extraction result overwrite a newer explicit user decision.
Capsule/token/worker-queue bounds are enforced and repeated compaction does not cause monotonic prompt or background-work growth.
replacing the coding agent's own compaction implementation;
making the compaction detector itself content-bearing or mutating;
universal semantic "agent identity" inference;
storing full raw transcripts solely to support this feature;
treating arbitrary assistant suggestions or tool output as authoritative user decisions;
treating auxiliary extractor inference as free/non-billable usage;
forcing the extractor to use the same model/provider/route as the primary session;
exposing the extractor response as a user-visible assistant turn.
Suggested implementation direction
My preference is therefore not original option (a) "run another model to improve the generated summary" and not plain option (b) "append whatever text the extractor returns".
The higher-reliability/minimal-round-trip shape is:
This makes the LLM extractor responsible only for the genuinely semantic part — unstructured decisions/acceptance/rationale — while deterministic code handles plan state, lifecycle, dedupe, precedence, limits, reinjection, worker synchronization, and billing attribution.
Problem
Session compaction is intentionally lossy, and the information that tends to hurt most when lost is not generic conversation detail but decision state:
In long coding sessions this causes a familiar failure mode: after compaction the agent still remembers the broad goal, but silently drops part of an agreed plan or a user decision, then re-plans from incomplete state, reopens already-settled choices, or implements something inconsistent with the pre-compaction agreement.
This gets worse over multiple compactions: even a good first summary may be summarized again later, causing progressive information decay unless critical decision state is treated as cumulative state rather than ordinary prose.
Research / precedent
This problem is already visible in several coding-agent implementations, and the better implementations point toward preserving structured continuation state rather than relying on an unconstrained generic summary:
Goal,Constraints & Preferences,Progress,Key Decisions,Next Steps, andCritical Context: https://github.com/earendil-works/pi/blob/main/packages/coding-agent/src/core/compaction/compaction.tssession_before_compacthook which can cancel compaction or return a custom replacement compaction summary. Its example custom compactor also demonstrates using a cheaper/faster secondary model: https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/extensions.md and https://github.com/earendil-works/pi/blob/main/packages/coding-agent/examples/extensions/custom-compaction.tsupdate_plan, so at least some planning information does not need to be rediscovered semantically from prose: https://github.com/openai/codex/blob/main/codex-rs/core/src/tools/handlers/plan.rsexperimental.session.compactinghook for injecting compaction context, and its V2 session design uses a structured rolling compaction checkpoint while retaining the full transcript durably: https://github.com/anomalyco/opencode/blob/dev/packages/web/src/content/docs/plugins.mdx and https://github.com/anomalyco/opencode/blob/dev/specs/v2/session.mdtodowrite/Todo) and its generatedCompactionPartincludestodos, which is another useful precedent for carrying planning state structurally rather than trusting free-form summarization alone: https://github.com/anomalyco/opencode/blob/dev/packages/sdk/js/src/gen/types.gen.tsImportant Go-LIP architecture constraint
The recently merged compaction-event work in #312 is currently a specification, not a runtime implementation on
mainyet.More importantly, that design intentionally makes
CompactionObservermetadata-only and non-mutating. It exposes lifecycle/evidence/correlation data but no raw prompt/response body, and observers return no replacement/decision. That is the correct contract for detection and should not be weakened for this feature.Therefore this feature should reuse the same compaction recognition/rule authority, but it needs a separate content-preservation/mutation path. Detection, extraction, and compaction-summary augmentation are different responsibilities.
There is also a timing issue: the planned
startedevent is emitted only after an upstream B-leg successfully opens. If we want to augment the current compaction request before the compaction model generates its summary, a post-Openobserver is already too late. The shared compaction matcher/classifier therefore needs to be reusable at the pre-open canonical request seam without duplicating the signature rules.Proposed model: a versioned
Continuity CapsuleDo not treat this primarily as "generate another summary". Maintain a small, structured, versioned piece of state containing only information that must survive lossy compaction.
Conceptually:
{ "schema_version": 1, "revision": 7, "latest_plan": { "status": "accepted", "source": "structured_tool|plan_mode|conversation", "steps": [ {"text": "...", "status": "pending|in_progress|completed"} ] }, "user_decisions": [ { "statement": "...", "status": "active|superseded|rejected", "rationale": "...", "source_turn": "..." } ], "constraints": ["..."], "rejected_alternatives": ["..."], "open_questions": ["..."] }The exact schema can differ, but the important property is that the proxy stores/merges typed continuation facts, not an ever-growing block of natural-language history.
Semantics must be explicit
acceptedonly when there is actual evidence of user acceptance / instruction to proceed, or a harness exposes an authoritative current plan state. Otherwise it remains provisional and should not be fossilized as settled product direction.Extraction pipeline
1. Deterministic extraction first
Before paying for another LLM call, harvest information that coding harnesses already expose structurally.
Examples include versioned signatures for:
update_planstate;todowrite/ todo state;task_progresschecklists where applicable;This should share the same versioned agent-signature/rule authority as compaction detection. We should not create a second unrelated matrix of hard-coded provider/agent checks.
A structured current plan should be copied/normalized mechanically. Do not ask an LLM to infer data the proxy already has in machine-readable form.
2. Cheap local eligibility heuristics
Run the semantic extractor only when there is evidence it may add information, for example:
These heuristics should optimize unnecessary calls, not decide semantic truth. Avoid a fragile regex system that declares arbitrary assistant prose to be an "accepted plan".
3. Optional auxiliary-model extraction for unstructured decisions
Where deterministic extraction is insufficient, use the existing auxiliary request mechanism (
Aux.Collect) with an operator-selected route/model rather than inventing another provider client path.The extraction model is deliberately independent from the model used by the primary coding session. The configured extractor route may point to a completely different model and provider. A session using an expensive frontier coding model may, for example, use a small/fast/cheap model for continuity extraction.
The auxiliary call should:
One extractor/normalizer call should be enough. There should not normally be a second LLM round trip merely to "improve" the ordinary compaction summary.
Auxiliary execution: off-session, background, independently routed
This requirement is important and should not be implemented as an extra visible assistant turn inside the user's main agent conversation.
The semantic extractor MUST execute as a proxy-created auxiliary/internal LLM invocation outside the primary agent session context:
The actual model call SHOULD execute through an independent asynchronous worker boundary — for example a goroutine/worker pool in-process or an external worker process — rather than running the extractor model inline inside the main session's normal B-leg/stream-processing stack. The exact scheduling mechanism is an implementation choice; the semantic contract is that extraction is separate work with its own lifecycle and resource budget.
This does not mean fire-and-forget. When continuity state is required before a compaction/continuation can safely proceed, the main flow may wait at a narrow, explicit preservation barrier for the relevant capsule revision. The main session must never depend on the extractor sharing its execution thread, model, provider connection, or conversational context.
Preferred optimization: do not wait until the compaction request itself if the relevant decision state was observable earlier. Candidate plan/decision-bearing turns may enqueue background extraction proactively. Then a later compaction usually consumes an already-current capsule; the compaction boundary only needs to flush/wait when the worker is behind the required transcript revision.
Conceptual worker identity/correlation should be explicit, e.g.:
A dedicated worker must remain bounded: no unbounded goroutine-per-event spawning, no unbounded queue, and no extraction job should outlive its useful session/revision indefinitely.
Billing and usage attribution
The auxiliary extractor is real additional model usage and is billable. Although it is not a conversational turn in the user's visible main-agent transcript, for usage/accounting purposes it is an additional proxy-initiated LLM inference/agent turn caused by that user's session.
Default accounting semantics:
If the user's applicable billing/admission policy rejects the extractor call, the default preservation behavior should remain fail-open: skip semantic extraction (while retaining any deterministic continuity state) and allow native compaction/session processing to continue. The proxy must not bypass billing authorization simply because the call is internally generated.
A future explicitly configured operator-funded/system-funded extractor may be useful, but that would be a separate opt-in accounting policy. Originating-user attribution is the default.
This cost implication should be documented for operators/users because enabling the feature can generate additional billable inference calls beyond those visibly initiated by the main agent.
Transcript/input preparation
Do not blindly resend the entire raw session transcript to the extractor on every compaction.
Preferred source order:
Go-LIP already has secure-session transcript recording when
TranscriptEnabledis active. Reuse it rather than building a second durable transcript database.For extractor input:
This should make cost roughly proportional to the delta since the prior preservation boundary, not total session age.
How to get the capsule through compaction
The reliable path should be designed explicitly instead of doing fire-and-forget work from a completion observer.
Preferred path: pre-compaction augmentation
When the shared classifier identifies a compaction request before upstream
Open:Current Go-LIP already has canonical request mutation surfaces (
request.Transform/ request-part hooks), and response-part hooks can mutate canonical response events. We should reuse an existing safe seam where possible, but a dedicated typedCompactionAugmenter/BeforeCompactionsurface may be cleaner than forcing stream-fragment manipulation into a generic hook.The important distinction is: the extractor model executes independently/background; synchronization happens only at the preservation boundary when its result is actually required.
Fallback: post-compaction reinjection
Some provider-native/opaque compaction mechanisms may not expose a safely mutable summary. For those, persist the capsule and inject it as proxy-owned continuation context on the first eligible post-compaction turn (or according to a well-defined bounded reinjection policy).
The timing invariant is important: the first model turn after compaction must not race ahead of required preservation state.
A completely unsynchronized fire-and-forget extractor is therefore insufficient: the next request can arrive before the worker finishes. The session/A-leg needs a revision-aware barrier/state transition ensuring the required capsule revision is ready before the first post-compaction B-leg. That barrier does not turn extraction into a main-session model call; it only joins independently executed background work when necessary.
Repeated compactions / incremental merge
This is a first-class requirement.
For compaction
N+1, do not merely summarize whatever survived compactionN.Use:
Required properties:
Scope and ownership
State should be scoped to the authoritative continuity identity, not arbitrary client hints.
At minimum define behavior for:
SessionID + A-legcontinuity;A fork should not accidentally receive decisions from an unrelated branch. If inheritance on fork is desired, make that an explicit copy-on-fork rule rather than a consequence of using a broad session key.
Concurrent updates need latest-revision / compare-and-merge semantics so an older extractor result cannot overwrite a newer user decision.
Worker jobs must use the same authoritative parent identity and cannot derive ownership from client-supplied session hints.
Privacy / security boundary
Calling an auxiliary remote LLM with session history is a new data-egress path and must be treated as such.
Requirements:
The capsule itself may be session state, but its retention/TTL and audit visibility must be explicit.
Configuration
Configurable globally at the feature/proxy level and overridable per trusted session policy.
Conceptual options (exact YAML nesting can follow the feature-plugin config conventions):
workers/max_pending_jobsare conceptual controls rather than a commitment to an in-process worker-pool implementation; an external worker implementation needs equivalent bounded concurrency/backpressure semantics.Session override precedence should be explicit, e.g. trusted session policy > global default. A session must not be allowed to self-enable external transcript export or change billing attribution unless the configured authorization model permits it.
Changing the main session model/route MUST NOT implicitly change the extractor model/route. Extractor routing changes only through its own effective configuration/session policy.
Failure behavior
Preservation improves UX; it must not make model traffic fragile.
fail_openby default);Observability
Add content-free metrics/diagnostics for:
Normal logs should expose IDs/revisions/counts, not capsule contents.
Acceptance criteria
update_plan, OpenCode todos, supported versioned equivalents) can be preserved without an extractor LLM round trip when sufficient.Non-goals
Suggested implementation direction
My preference is therefore not original option (a) "run another model to improve the generated summary" and not plain option (b) "append whatever text the extractor returns".
The higher-reliability/minimal-round-trip shape is:
This makes the LLM extractor responsible only for the genuinely semantic part — unstructured decisions/acceptance/rationale — while deterministic code handles plan state, lifecycle, dedupe, precedence, limits, reinjection, worker synchronization, and billing attribution.