fix(llm): align replay state with assembled content and degrade unusable state
A max-tokens response that included a tool call persisted assembler-transformed content next to replay metadata projected from the untransformed native message, so the next request died in history reconstruction with INVALID_REPLAY_STATE and the session stayed permanently stuck. Write side: the finish chunk's replayState becomes a typed ReplayEnvelope — opaque response-level metadata plus optional per-block entries aligned with the emitted block sequence. BlockAssembler computes one keep/drop decision for blocks and entries together, so stored metadata always describes stored content and retained blocks keep their signatures. pi-ai splits its state into a version-2 response half and per-block signature entries. Read side: durable content is authoritative. toPiAssistant degrades any unusable state — foreign kind, other versions (including the flat v1 form already on disk), malformed metadata, or content/block mismatches — to the existing provider-neutral conversion with an onReplayDegrade diagnostic instead of failing the request, which un-bricks sessions poisoned before this change. Covered by assembler and replay unit tests, an agent-loop continuation regression, keyless real-composition continuation tests (native pruned-envelope replay and legacy flat-state degrade), and the authored keyless snapshot scenario max-tokens-continue through the assembled ACP app.
This commit is contained in:
@@ -351,6 +351,11 @@
|
||||
"symbol": "ToolProviderResult",
|
||||
"source": "packages/core/system-prompt/src/index.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/llm-streaming.md",
|
||||
"symbol": "ReplayEnvelope",
|
||||
"source": "packages/llm/llm/src/types.ts"
|
||||
},
|
||||
{
|
||||
"doc": "docs/subsystems/llm-streaming.md",
|
||||
"symbol": "StreamChunk",
|
||||
|
||||
Reference in New Issue
Block a user