summaryrefslogtreecommitdiffhomepage
path: root/packages/core/src/chunks
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-05-30 23:14:55 +0900
committerAdam Malczewski <[email protected]>2026-05-30 23:14:55 +0900
commit624b808da0f2f8bbad8a4fbbcca3f82f24ecfc47 (patch)
tree869d34092345344ff13953398f876c8b38c8116a /packages/core/src/chunks
parentb19f1aafc43141a865ecd40a813ed3212e77d95e (diff)
downloaddispatch-624b808da0f2f8bbad8a4fbbcca3f82f24ecfc47.tar.gz
dispatch-624b808da0f2f8bbad8a4fbbcca3f82f24ecfc47.zip
feat(chunks): chunk-native frontend store with turn-sealed reconcile + per-chunk eviction
Replace the stored ChatMessage[] with a chunk-native model: tab.chunks (sealed ChunkRow[]) + tab.live (transient in-flight turn buffer) + derived tab.renderGroups. This enables per-chunk eviction (trimming WITHIN a large turn) and raw-chunk pagination (loadOlderChunks), removing the whole-message eviction limitation. Backend: - Emit turn-start/turn-sealed around each turn; expose currentTurnId in the status snapshot. turn-sealed fires after the durable write (status:idle fires before it). - New GET /tabs/:id/chunks raw paginated endpoint (limit/before). - Wrap appendChunks in a single SQLite transaction. Frontend: - turn-sealed drives a turn-aware reconcile that folds the sealed turn into chunks while preserving a concurrent newer in-flight turn and pending queued messages; deferred while the user is scrolled up. - Stable turn-scoped render keys (${turnId}:${role}:${n}) avoid remount/flash. Reconcile correctness (three review passes): - preserve a concurrent newer turn when an earlier deferred reconcile flushes; - keep optimistic queued user messages (no loss); - turn-start backfill skips pending queued rows and tags only the turn initiator; - bind consumed interrupt messages to the in-flight turn so they collapse on seal (no lingering/duplicated bubble). Tests: chat-store reconcile/eviction/pagination suite; api chunks endpoint + events.
Diffstat (limited to 'packages/core/src/chunks')
-rw-r--r--packages/core/src/chunks/append.ts13
1 files changed, 8 insertions, 5 deletions
diff --git a/packages/core/src/chunks/append.ts b/packages/core/src/chunks/append.ts
index baccd10..4bc6b5f 100644
--- a/packages/core/src/chunks/append.ts
+++ b/packages/core/src/chunks/append.ts
@@ -6,7 +6,7 @@
* backend (agent + agent-manager) and the frontend store call this helper
* so the wire format stays in lockstep across the boundary.
*
- * Open/close rules — see plan-chunk-refactor.md for the full table.
+ * Open/close rules — see notes/plan-chunk-refactor.md for the full table.
*
* | Chunk | Opens on | Coalesces |
* |---------------|-----------------------------------------------------------------|------------------------------------------------------------|
@@ -35,9 +35,10 @@
* (no unsealed thinking chunk) are dropped.
*
* Ignored events:
- * - `status`, `done`, `usage`, `task-list-update`, `tab-created`,
- * `message-queued`, `message-consumed`, `message-cancelled` — these are
- * control / lifecycle events, not message content.
+ * - `status`, `turn-start`, `turn-sealed`, `done`, `usage`,
+ * `task-list-update`, `tab-created`, `message-queued`, `message-consumed`,
+ * `message-cancelled` — these are control / lifecycle events, not message
+ * content.
*/
import type {
@@ -200,6 +201,8 @@ export function appendEventToChunks(chunks: Chunk[], event: AgentEvent): void {
// Lifecycle / control events — no chunk emitted.
case "status":
+ case "turn-start":
+ case "turn-sealed":
case "done":
case "usage":
case "task-list-update":
@@ -251,7 +254,7 @@ export interface SystemEventLike {
* in flight*. (When a turn IS in flight, the caller should instead use
* `appendEventToChunks` against the in-flight message's chunks directly.)
*
- * Routing rules (from plan-chunk-refactor.md):
+ * Routing rules (from notes/plan-chunk-refactor.md):
*
* 1. Most recent message is `role: "system"` → append a `system` chunk
* to it. (Note: a second consecutive system event creates a second