diff options
| author | Adam Malczewski <[email protected]> | 2026-06-22 14:07:30 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-22 14:07:30 +0900 |
| commit | 4e1d041c0ee34fa1b74b0d5ecbd432cdacf696e9 (patch) | |
| tree | 1d4b482b2193631d96be3eb107974c1c6133220d /src/core/chunks/reducer.test.ts | |
| parent | 06185717c61343e732002d782294f7de54c183b0 (diff) | |
| download | dispatch-web-4e1d041c0ee34fa1b74b0d5ecbd432cdacf696e9.tar.gz dispatch-web-4e1d041c0ee34fa1b74b0d5ecbd432cdacf696e9.zip | |
feat: trim chunks during generation via step-complete syncTail (CR-6)
The backend now persists chunks at step boundaries during generation
(CR-6). The FE calls syncTail on each step-complete event to fetch
the newly committed chunks. applyHistory clears the provisional array
when new committed chunks arrive mid-generation (they're duplicates
of what was folded from live events). The accumulating chunk (current
in-progress step) is kept.
This means trimTranscript can now drop oldest committed chunks
uniformly during a long turn — no unbounded provisional growth. The
browser never holds more than chatLimit chunks, even mid-generation.
3 new tests: clears provisional on new committed during generation,
keeps provisional when no new chunks, keeps accumulating when clearing.
689 tests green.
Diffstat (limited to 'src/core/chunks/reducer.test.ts')
| -rw-r--r-- | src/core/chunks/reducer.test.ts | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/src/core/chunks/reducer.test.ts b/src/core/chunks/reducer.test.ts index a346545..c479488 100644 --- a/src/core/chunks/reducer.test.ts +++ b/src/core/chunks/reducer.test.ts @@ -551,6 +551,86 @@ describe("applyHistory", () => { expect(s.committed).toHaveLength(2); expect(s.committed.map((c) => c.seq)).toEqual([1, 2]); }); + + it("clears provisional when new committed chunks arrive during generation (CR-6)", () => { + let s = initialState(); + s = foldEvent(s, turnStart("t1")); + s = foldEvent(s, textDelta("t1", "hello")); + s = foldEvent(s, toolCall("t1", "tc1", "run_shell", {})); + s = foldEvent(s, toolResult("t1", "tc1", "run_shell", "output")); + expect(s.generating).toBe(true); + expect(s.provisional.length).toBeGreaterThan(0); + + s = applyHistory(s, [ + storedChunk(1, "assistant", { type: "text", text: "hello" }), + storedChunk(2, "assistant", { + type: "tool-call", + toolCallId: "tc1", + toolName: "run_shell", + input: {}, + stepId: "s0" as StepId, + }), + storedChunk(3, "tool", { + type: "tool-result", + toolCallId: "tc1", + toolName: "run_shell", + content: "output", + isError: false, + stepId: "s0" as StepId, + }), + ]); + + expect(s.provisional).toEqual([]); + expect(s.generating).toBe(true); + expect(s.committed).toHaveLength(3); + }); + + it("keeps provisional when no new committed chunks arrive during generation", () => { + let s = initialState(); + s = applyHistory(s, [storedChunk(1, "user", { type: "text", text: "q" })]); + s = foldEvent(s, turnStart("t1")); + s = foldEvent(s, textDelta("t1", "hello")); + s = foldEvent(s, toolCall("t1", "tc1", "run_shell", {})); + // At this point: provisional has [text "hello", tool-call] + expect(s.provisional.length).toBeGreaterThan(0); + + // applyHistory with chunks already in committed — no new additions + s = applyHistory(s, [storedChunk(1, "user", { type: "text", text: "q" })]); + + expect(s.provisional.length).toBeGreaterThan(0); + }); + + it("keeps accumulating when clearing provisional during generation (CR-6)", () => { + let s = initialState(); + s = applyHistory(s, [storedChunk(1, "user", { type: "text", text: "q" })]); + s = foldEvent(s, turnStart("t1")); + s = foldEvent(s, toolCall("t1", "tc1", "run_shell", {})); + s = foldEvent(s, toolResult("t1", "tc1", "run_shell", "output")); + // Start accumulating text for the NEXT step + s = foldEvent(s, textDelta("t1", "streaming...")); + expect(s.accumulating).not.toBeNull(); + + s = applyHistory(s, [ + storedChunk(2, "assistant", { + type: "tool-call", + toolCallId: "tc1", + toolName: "run_shell", + input: {}, + stepId: "s0" as StepId, + }), + storedChunk(3, "tool", { + type: "tool-result", + toolCallId: "tc1", + toolName: "run_shell", + content: "output", + isError: false, + stepId: "s0" as StepId, + }), + ]); + + expect(s.provisional).toEqual([]); + expect(s.accumulating).not.toBeNull(); + }); }); describe("selectChunks", () => { |
