Skip to content

Commit 1af8daf

Browse files
authored
feat(core): persist v2 session context epochs (anomalyco#30789)
1 parent c47cb28 commit 1af8daf

45 files changed

Lines changed: 4861 additions & 521 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CONTEXT.md

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@ OpenCode sessions preserve durable conversational history while assembling the r
88
The structured collection of contextual facts presented to the model as initial instructions and chronological updates.
99
_Avoid_: System prompt
1010

11-
**Context Component**:
12-
One independently loaded fact within the **System Context**, represented by a stable key and one effectfully loaded baseline/update rendering.
11+
**Context Source**:
12+
One independently observed typed value within the **System Context**, represented by a stable key, JSON codec, infallible loader, pure baseline/update renderers, and an optional removal renderer for dynamic sources.
1313
_Avoid_: Prompt fragment
1414

15+
**System Context Registry**:
16+
The Location-scoped registry of ordered, scoped producers that contribute to the current **System Context**.
17+
1518
**Mid-Conversation System Message**:
16-
A durable chronological instruction that tells the model the newly effective state of a changed **Context Component**.
19+
A durable chronological instruction that tells the model the newly effective state of a changed **Context Source**.
1720
_Avoid_: System update, system notification, raw text diff
1821

1922
**Context Epoch**:
@@ -23,48 +26,57 @@ The span during which one initially rendered **System Context** remains immutabl
2326
The full **System Context** rendered at the start of a **Context Epoch**.
2427
_Avoid_: Live system prompt
2528

26-
**Context Checkpoint**:
27-
The durable model-hidden comparison state used to detect which **Context Components** changed since context was last admitted to a provider turn.
29+
**Context Snapshot**:
30+
The overwriteable model-hidden JSON state used to compare each **Context Source** with the value last admitted to a provider turn.
2831

2932
**Unavailable Context**:
30-
An expected temporary inability to load a **Context Component** value; the runtime retains its prior effective state and emits no update, or omits it until first successfully loaded.
33+
An expected temporary inability to observe a **Context Source** value; the runtime retains its prior effective state and emits no update, or omits it until first successfully loaded.
3134

3235
**Safe Provider-Turn Boundary**:
3336
The point immediately before a provider call, after durable input promotion and any required tool settlement, where context changes may be admitted chronologically.
3437

3538
## Relationships
3639

37-
- A **System Context** contains one or more **Context Components**.
38-
- A changed **Context Component** may produce one **Mid-Conversation System Message** containing its newly effective state.
39-
- A **Mid-Conversation System Message** persists its originating **Context Component** key and the exact rendered text sent to the model.
40-
- A **Context Checkpoint** advances atomically with the corresponding durable **Mid-Conversation System Message**.
41-
- A **Context Checkpoint** stores one rendered-content hash per stable **Context Component** key so core and plugin-defined components can evolve independently.
42-
- Changes from multiple **Context Components** admitted at one safe boundary combine into one **Mid-Conversation System Message**.
40+
- A **System Context** is an opaque carrier composed from zero or more **Context Sources**.
41+
- The **System Context Registry** uses stable-keyed scoped contributions to assemble the current **System Context**; contributor removal naturally removes its sources at the next **Safe Provider-Turn Boundary**.
42+
- A changed **Context Source** may produce one **Mid-Conversation System Message** containing its newly effective state.
43+
- A **Mid-Conversation System Message** persists the exact combined rendered text sent to the model.
44+
- The current **Context Snapshot** advances atomically with the corresponding durable **Mid-Conversation System Message**.
45+
- A **Context Snapshot** stores one codec-encoded JSON value and, for removable dynamic sources, a pre-rendered removal message per stable **Context Source** key.
46+
- Changes from multiple **Context Sources** admitted at one safe boundary combine into one **Mid-Conversation System Message**.
4347
- Context changes are sampled and admitted lazily at a **Safe Provider-Turn Boundary**, never pushed asynchronously when their source changes.
4448
- At a **Safe Provider-Turn Boundary**, newly promoted user input or settled tool results precede any combined **Mid-Conversation System Message**.
45-
- The first provider turn renders the latest **Baseline System Context** and initializes its **Context Checkpoint** without emitting a redundant **Mid-Conversation System Message**.
46-
- Compaction starts a new **Context Epoch** with a freshly rendered **Baseline System Context** and **Context Checkpoint**; prior **Mid-Conversation System Messages** remain durable audit history but leave projected model history.
47-
- A **Context Checkpoint** is an evolvable component map; a newly registered core or plugin-defined **Context Component** absent from an existing checkpoint emits its current state once at the next **Safe Provider-Turn Boundary**.
48-
- **Context Component** keys are stable and namespaced; duplicate keys fail assembly. Built-in components preserve declaration order and plugin-defined components append in lexicographic key order so rendered context is deterministic.
49-
- Each **Context Component** loader returns its model-visible baseline string and absolute current-state update string from one coherent sample; the update string is hashed for change detection.
49+
- The first provider turn renders the latest complete **Baseline System Context** and initializes its **Context Snapshot** without emitting a redundant **Mid-Conversation System Message**; unavailable initial context blocks the turn instead of persisting an incomplete baseline.
50+
- Initial **System Context** preparation precedes the first durable input promotion so an unavailable baseline leaves that input pending and retryable; ordinary reconciliation remains after promotion.
51+
- Compaction starts a new **Context Epoch** with a freshly rendered **Baseline System Context** and **Context Snapshot**; prior **Mid-Conversation System Messages** remain durable audit history but leave projected model history.
52+
- A newly registered core or plugin-defined **Context Source** absent from the current snapshot emits its baseline rendering once at the next **Safe Provider-Turn Boundary**.
53+
- **Context Source** keys are stable and namespaced; duplicate keys fail composition. `SystemContext.combine(...)` preserves caller order; the **System Context Registry** evaluates producers concurrently and combines them in stable contribution-key order so rendered context remains deterministic.
54+
- Each **Context Source** loader returns one coherent typed value. `SystemContext.make(...)` hides that value type so differently typed sources compose uniformly. Its codec compares and stores that value; its pure renderers produce model-visible baseline, update, and removal text only when needed.
55+
- `SystemContext.initialize(...)` observes a composed **System Context** once and produces a fresh **Baseline System Context** with its **Context Snapshot**.
56+
- `SystemContext.reconcile(...)` observes a composed **System Context** once and returns exactly one next action: unchanged, updated, replacement ready, or replacement blocked.
57+
- `SystemContext.replace(...)` represents an explicit baseline-replacing transition such as compaction or model/provider switch; it either produces a fresh generation or reports that replacement is blocked by unavailable admitted context.
58+
- Context Epoch preparation retries until stable after optimistic revision mismatches so concurrent replacement requests cannot terminate an otherwise valid safe-boundary run.
5059
- **Unavailable Context** uses stale-while-revalidate semantics and is distinct from a successfully loaded absence, which may emit removal text.
51-
- Ordinary **Context Component** loaders return values directly; loaders that intentionally use stale-while-revalidate may explicitly return **Unavailable Context**.
52-
- Nested project instruction files discovered while reading join the effective instructions returned by the instruction service and are admitted durably at the next **Safe Provider-Turn Boundary**.
53-
- A discovered nested project instruction remains active for the session while it stays in the same location and is folded into later **Baseline System Contexts** after compaction.
60+
- Ordinary **Context Source** loaders return values directly; loaders that intentionally use stale-while-revalidate may explicitly return **Unavailable Context**.
61+
- Nested project instruction discovery after successful reads remains a follow-up; when implemented, discovered instructions must be admitted durably at the next **Safe Provider-Turn Boundary**.
5462
- Location-scoped services naturally re-resolve effective context when a moved session next runs in its destination location.
63+
- Moving a Session clears its active **Context Epoch**, so the destination must initialize a complete baseline before another prompt can promote.
64+
- Context Epoch initialization is fenced against the authoritative Session Location, so an old-Location runner cannot recreate source context after a concurrent move.
5565
- Instruction discovery, source identity, persistence, and file loading belong to the instruction service; the **System Context** abstraction only composes effectful producers and renders loaded values.
56-
- Plugin-defined **Context Components** register through a scoped replayable registry so plugin hot reload adds and removes components predictably.
66+
- The first instruction-service slice observes global and upward project `AGENTS.md` files as one ordered aggregate **Context Source** at each **Safe Provider-Turn Boundary**.
67+
- Built-in and instruction context producers register through the **System Context Registry** with stable contribution keys. Plugin-defined context registration and hot-reload lifecycle remain a follow-up built on the same scoped registry seam.
5768
- Context source changes never wake idle sessions; the next naturally scheduled **Safe Provider-Turn Boundary** loads and compares current values lazily.
5869
- Once admitted, a **Mid-Conversation System Message** remains durable even if the following provider attempt fails and is replayed unchanged on retry.
59-
- **Mid-Conversation System Messages** remain durable model-projection history but are hidden from normal user-facing transcript surfaces.
60-
- The date **Context Component** initially preserves host-local calendar-date behavior; a configured user timezone may replace that default later.
70+
- **Mid-Conversation System Messages** remain durable Session-message history; normal user-facing transcript surfaces may hide them.
71+
- The date **Context Source** initially preserves host-local calendar-date behavior; a configured user timezone may replace that default later.
6172
- A **Context Epoch** begins with one immutable **Baseline System Context**.
6273
- A **Baseline System Context** is stored durably and reused verbatim across process restarts within its **Context Epoch**.
63-
- A **Baseline System Context** durably preserves deterministic keyed top-level component strings rather than eagerly joining all text; request assembly lowers them into canonical LLM system parts.
74+
- A **Baseline System Context** durably preserves the exact joined text used for the active provider-cache prefix.
6475
- Compaction or a model/provider switch starts a new **Context Epoch** because the baseline can be replaced without preserving the prior provider cache.
6576
- A model/provider switch always starts a new **Context Epoch** while preserving chronological conversation history.
6677
- A **Mid-Conversation System Message** lowers to the provider's native chronological instruction role when supported and to a wrapped chronological fallback otherwise.
67-
- When an effective instruction file changes, its **Mid-Conversation System Message** includes the complete current contents and supersedes the prior version from that source; when it is removed, the message states that it no longer applies.
78+
- When the effective aggregate instruction set changes, its **Mid-Conversation System Message** includes the complete current ordered set and supersedes the prior aggregate value; when no ambient instructions remain, the message states that previously loaded instructions no longer apply.
79+
- Ambient project instruction discovery honors `OPENCODE_DISABLE_PROJECT_CONFIG`; global instructions remain eligible.
6880

6981
## Example dialogue
7082

@@ -73,5 +85,4 @@ The point immediately before a provider call, after durable input promotion and
7385
7486
## Flagged ambiguities
7587

76-
- Legacy `experimental.chat.system.transform` can mutate the assembled baseline system prompt arbitrarily, but V2 plugins do not yet expose an equivalent hook. Decide separately whether to port it, replace dynamic uses with plugin-defined **Context Components**, or narrow its semantics.
77-
- A location change likely starts a new **Context Epoch** so location-dependent instructions and discovery can be rebuilt cleanly, but implementation should verify whether an append-only update is sufficient and meaningfully preserves cache.
88+
- Legacy `experimental.chat.system.transform` can mutate the assembled baseline system prompt arbitrarily, but V2 plugins do not yet expose an equivalent hook. Decide separately whether to port it, replace dynamic uses with plugin-defined **Context Sources**, or narrow its semantics.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CREATE TABLE `session_context_epoch` (
2+
`session_id` text PRIMARY KEY,
3+
`baseline` text NOT NULL,
4+
`snapshot` text NOT NULL,
5+
`baseline_seq` integer NOT NULL,
6+
`replacement_seq` integer,
7+
`revision` integer DEFAULT 0 NOT NULL,
8+
CONSTRAINT `fk_session_context_epoch_session_id_session_id_fk` FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON DELETE CASCADE
9+
);

0 commit comments

Comments
 (0)