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
fix(ai): declare append-only history for session-cached backends
Trailing volatile placement only pays off for backends that cache by
request prefix. Meridian's proxy matches the incoming message array
against the history it already holds, and its lineage boundary tolerates
a last-message append only for new tool_result blocks, so a payload-only
volatile text block classified as modified-history and forced a full
replay every turn.
Model.appendOnlyHistory now declares that constraint. When set, the API
registry routes volatileContext into the system prompt and adds no
payload-only message block, so the request history stays byte-identical
to the persisted conversation. Default placement is unchanged. The flag
is settable from registerProvider model entries, models.json model
definitions, and modelOverrides.
refs #26
upstream_support: Prime through c382f09856d4 reduced the system-prompt date to a day stamp for cache stability, but it still serializes mutable continual-harness state and that date into the cached system prompt, has no provider-neutral volatile-content channel, and does not pin active tool order behind the `cache_control` marker on the last tool definition.
334
+
upstream_support: Prime through c382f09856d4 reduced the system-prompt date to a day stamp for cache stability, but it still serializes mutable continual-harness state and that date into the cached system prompt, has no provider-neutral volatile-content channel, no per-model placement contract for session-cached proxy backends, and does not pin active tool order behind the `cache_control` marker on the last tool definition.
335
335
revisit_when:
336
-
- Prime upstream provides an equivalent provider-neutral channel that keeps mutable state and clock-derived content out of the cached tools/system/history prefix.
337
-
- Pylon can drop the fork behavior without regressing measured cache-hit rates through a Claude-Max proxy.
336
+
- Prime upstream provides an equivalent provider-neutral channel that keeps mutable state and clock-derived content out of the cached tools/system/history prefix, including a declared constraint for append-only-history backends.
337
+
- Pylon can drop the fork behavior without regressing measured cache-hit rates through a Claude-Max proxy or Meridian lineage continuation rates.
Copy file name to clipboardExpand all lines: .pylon/upstream-review.md
+3-1Lines changed: 3 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -157,6 +157,8 @@ This ledger records Prime upstream evidence and the decision taken for each over
157
157
- Design: `Context.volatileContext` carries continual-harness state and the current date. The Anthropic provider appends it as the final content block after the `cache_control` breakpoint on the last history block, so the cached tools -> system -> history prefix is unchanged by a harness write or a date flip. The OpenAI-completions provider appends it after its Anthropic-style markers and after the automatically cached prefix of providers without markers. The API registry folds the content into the end of the message list for every other provider, so no provider can silently drop it. `AgentSession` pins each tool to the slot it first occupied, so a registry refresh or an extension reload serializes an unchanged tool set to identical bytes.
158
158
- Behavior preservation: the harness-state section and the `Current date` line are byte-identical to what the system prompt previously inlined, including the empty-state rendering and the custom-prompt-only scope of the date. Only their position changed. The subagent guidance still precedes the harness-state roster in the assembled request.
159
159
- Compatibility classification: **backward-compatible and additive**. `Context.volatileContext`, `AgentState.volatileContext`, `AgentLoopConfig.getVolatileContext`, and `ApiProvider.handlesVolatileContext` are all optional; a caller that sets none behaves exactly as before. No daemon command, event, protocol version, or schema revision changes.
160
-
- Validation: 10 new `packages/ai` payload tests assert a byte-identical cached prefix across volatile changes for both the Anthropic and OpenAI-completions payload builders, correct marker placement, the trailing-user-message case, blank-input handling, and the registry fallback. 4 new `packages/coding-agent/test/suite/regressions/26-stable-cache-prefix.test.ts` cases assert an unchanged prefix across a mid-session harness memory write, an unchanged prefix across a mocked two-day clock advance, identical tool bytes across repeated registry refreshes, and first-activation tool-order pinning across reordering and removal. `test/system-prompt.test.ts` (25), the compaction, compact-skill, refine, runtime, queue, and prompt suites (357), `packages/agent` (60), and 13 focused `packages/ai` provider files (93 passed, 9 gated skips) are green. `npm run check` passes. `test/extensions-runner.test.ts` fails 21 cases identically on unmodified `origin/pylon`, so it is pre-existing and unrelated.
160
+
- Validation: 15 `packages/ai` payload tests assert a byte-identical cached prefix across volatile changes for both the Anthropic and OpenAI-completions payload builders, correct marker placement, the trailing-user-message case, blank-input handling, and the registry fallback. 5 `packages/coding-agent/test/suite/regressions/26-stable-cache-prefix.test.ts` cases assert an unchanged prefix across a mid-session harness memory write, an unchanged prefix across a mocked two-day clock advance, identical tool bytes across repeated registry refreshes, first-activation tool-order pinning across reordering and removal, and — for an `appendOnlyHistory` model registered through `registerProvider` — an append-only history with the volatile content in the system prompt. `test/suite` (79 files, 960), `test/system-prompt.test.ts` (25), `test/model-registry.test.ts`, `test/agent-session-recursion.test.ts` (112), `packages/agent` (60), and 13 focused `packages/ai` provider files (93 passed, 9 gated skips) are green. `npm run check` passes. `test/extensions-runner.test.ts` fails 21 cases identically on unmodified `origin/pylon`, so it is pre-existing and unrelated.
161
+
- Review correction — two placements, declared per model. The first candidate always used trailing placement, which regressed the Meridian proxy path from "cold cache when harness state or the date changes" to a full fresh replay every turn. Meridian's lineage matcher (`meridian/src/proxy/session/lineage.ts`) resumes an SDK session only when the incoming history matches the stored history; its boundary branch at `cached.messageCount - 1` tolerates a last-message block append only when every stored block hash is preserved and every appended block is a new `tool_result` (`hasOnlyNewToolResults`, deliberately narrow per Meridian #689/#692). A payload-only volatile text block fails that on the turn it appears, and on the next turn the stored boundary message holds more blocks than the incoming replay because the block was never persisted, so `incomingBlockHashes.length > storedBlocks.length` fails too. Classification falls through to `diverged: modified-history` and forces a full-history replay on the exact deployment #22 targets.
162
+
- The fix is a declared per-model constraint, not a workaround. `Model.appendOnlyHistory` marks a backend that caches by session rather than by request prefix and therefore requires the request message array to stay byte-identical to the persisted history. When set, the API registry routes `volatileContext` into the system prompt and adds no payload-only message block; the backend's own session cache absorbs the system-prompt change. Default stays trailing placement, which is correct for direct prefix-cached APIs. The flag is settable from extension `registerProvider` model entries and from `models.json` model definitions and `modelOverrides`, and it is documented for extension authors in `packages/coding-agent/docs/extensions.md`.
161
163
- Deferred: the optional debug-level prefix-drift check from issue #26 is not implemented. The regression tests assert the invariant directly, and a cross-turn byte comparison would need new mutable state inside otherwise pure prompt assembly.
162
164
- Revisit when Prime upstream offers an equivalent provider-neutral channel that keeps mutable state and clock-derived content out of the cached prefix, and Pylon can drop the fork behavior without regressing measured cache-hit rates through a Claude-Max proxy.
- Added `Context.volatileContext` for content that must stay out of every cached prompt prefix; Anthropic and OpenAI-completions place it after their cache breakpoints and other providers receive it appended to the message list ([#26](https://github.com/pylon-code/prime-agent/issues/26)).
2
+
- Added the `Model.appendOnlyHistory` flag for session-cached backends, which routes volatile content into the system prompt so the request message array stays byte-identical to the persisted history ([#26](https://github.com/pylon-code/prime-agent/issues/26)).
Copy file name to clipboardExpand all lines: packages/ai/README.md
+10Lines changed: 10 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -997,6 +997,16 @@ The model still sees it, but never inside a cached region:
997
997
- Anthropic and OpenAI-completions append it after their `cache_control` breakpoints, so the cached tools, system prompt, and conversation history are unaffected when it changes.
998
998
- Every other provider receives it appended to the end of `Context.messages`, which is after the automatically cached prefix. Set `handlesVolatileContext: true` when registering a custom provider that positions the content itself.
999
999
1000
+
### Session-cached backends
1001
+
1002
+
Trailing placement assumes the backend caches by request prefix. A proxy in front of an agent SDK does not: it matches the incoming message array against the history it already holds and resumes that session, so a payload-only trailing block reads as a modified history and forces a full replay.
1003
+
1004
+
Set `appendOnlyHistory: true` on those models. The volatile content then goes into the system prompt and the message array stays byte-identical to the caller's history:
The `Context` object can be easily serialized and deserialized using standard JSON methods, making it simple to persist conversations, implement chat history, or transfer contexts between services:
- Fixed prompt-cache churn by sending continual harness state and the current date outside the cached prompt prefix, so a harness refinement or a midnight date flip no longer re-caches a long-running session ([#26](https://github.com/pylon-code/prime-agent/issues/26)).
2
2
- Pinned each tool to the slot it first occupied so a tool-registry refresh serializes an unchanged tool set to identical bytes ([#26](https://github.com/pylon-code/prime-agent/issues/26)).
3
+
- Added the `appendOnlyHistory` model flag so proxy-backed providers registered by extensions keep an append-only request history and receive volatile prompt content in the system prompt ([#26](https://github.com/pylon-code/prime-agent/issues/26)).
-`oauth` - OAuth provider config for `/login` support. When provided, the provider appears in the login menu.
1649
1649
-`streamSimple` - Custom streaming implementation for non-standard APIs.
1650
1650
1651
+
**Model definitions: `appendOnlyHistory`**
1652
+
1653
+
Set `appendOnlyHistory: true` on a model when the backend keeps its own session cache keyed on the request message array, rather than caching by request prefix. Proxies in front of an agent SDK work this way: they match the incoming history against the history they already hold and resume that session, so they only tolerate an append.
1654
+
1655
+
The flag controls where Prime Agent puts volatile prompt content — continual harness state and the current date. By default that content is appended after the message list so a change cannot invalidate a prefix cache. A session-cached backend reads the same append as a modified history and replays the whole conversation instead, so the flag routes the content into the system prompt and leaves the message array byte-identical to the persisted conversation.
Leave it unset for direct provider APIs, including Anthropic and OpenAI-compatible endpoints that cache by prefix. `models.json` accepts the same field on a model definition or a `modelOverrides` entry.
1678
+
1651
1679
See [custom-provider.md](custom-provider.md) for advanced topics: custom streaming APIs, OAuth details, model definition reference.
0 commit comments