diff options
| author | Adam Malczewski <[email protected]> | 2026-06-22 01:31:29 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-22 01:31:29 +0900 |
| commit | 2772e0723cfc7898443320515e165a625de1db46 (patch) | |
| tree | c65e8a7c1a9ffb1ca6b44147cd3eb8629aa47830 /.dispatch | |
| parent | 54e88b71efd9a6fd9d880b6e90d844a875808662 (diff) | |
| download | dispatch-web-2772e0723cfc7898443320515e165a625de1db46.tar.gz dispatch-web-2772e0723cfc7898443320515e165a625de1db46.zip | |
feat(compaction): conversation compacting + auto-compact threshold
Consume the compaction handoff ([email protected], [email protected]).
Re-pinned file: deps + re-mirrored .dispatch/*.reference.md.
- New 'Compaction' sidebar view (CompactionView.svelte):
- 'Compact now' button → POST /conversations/:id/compact (loading indicator
+ result: 'N messages summarized, M kept')
- Auto-compact threshold number input → GET/PUT
/conversations/:id/compact-threshold (0 = disabled, default 350000)
- Re-mounts per conversation via {#key}
- App store: compactNow() + compactThreshold reactive state +
setCompactThreshold(), seeded on focus change (like reasoning-effort + cwd)
- conversation.compacted WS handler: reloads the SAME conversation's history
(ID unchanged — old history forked to an archive, not a tab switch)
- WS adapter parses newConversationId field on ConversationCompactedMessage
- conformance guards + tests cover the new type
686 tests green.
Diffstat (limited to '.dispatch')
| -rw-r--r-- | .dispatch/transport-contract.reference.md | 43 | ||||
| -rw-r--r-- | .dispatch/wire.reference.md | 23 |
2 files changed, 63 insertions, 3 deletions
diff --git a/.dispatch/transport-contract.reference.md b/.dispatch/transport-contract.reference.md index e599eb3..608211d 100644 --- a/.dispatch/transport-contract.reference.md +++ b/.dispatch/transport-contract.reference.md @@ -5,10 +5,18 @@ > hangs on a permission prompt). Your CODE still imports `@dispatch/transport-contract` normally — > this file is for READING only. > -> **Orchestrator:** SNAPSHOT of `[email protected]` (conversation lifecycle). -> Depends on `@dispatch/[email protected]` (see `wire.reference.md`) + `@dispatch/[email protected]` (see +> **Orchestrator:** SNAPSHOT of `[email protected]` (compaction). +> Depends on `@dispatch/[email protected]` (see `wire.reference.md`) + `@dispatch/[email protected]` (see > `ui-contract.reference.md`). > +> **2026-06-22 delta (compaction handoff — package bumped `0.14.0` → `0.15.0`, ADDITIVE):** +> adds conversation compaction — summarize old history + retain recent N messages. Manual: +> `POST /conversations/:id/compact` (optional `{ keepLastN, modelName }`) → `CompactResponse`. +> Automatic: after each turn settles, if the last turn's input tokens exceeded the per-conversation +> `compactThreshold`, compaction runs automatically. `GET`/`PUT /conversations/:id/compact-threshold` +> (`CompactThresholdResponse`/`SetCompactThresholdRequest`) — `threshold: 0` = disabled; default +> 350000 when not stored. Re-exports `CompactionResult` from `[email protected]`. +> > **2026-06-22 delta (conversation lifecycle handoff — package bumped `0.13.0` → `0.14.0`, ADDITIVE):** > adds conversation lifecycle **status** (`active`/`idle`/`closed`) for cross-device tab > persistence. `ConversationMeta` (re-exported from `[email protected]`) gains a `status` field. New @@ -712,6 +720,7 @@ export interface ConversationStatusChangedMessage { export interface ConversationCompactedMessage { readonly type: "conversation.compacted"; readonly conversationId: string; + readonly newConversationId: string; readonly messagesSummarized: number; readonly messagesKept: number; } @@ -760,4 +769,34 @@ export interface TitleResponse { readonly conversationId: string; readonly title: string; } + +// ─── Compaction ────────────────────────────────────────────────────────────── + +/** + * Response for `POST /conversations/:id/compact` — confirms the conversation + * history was compacted (old messages summarized, recent messages retained). + */ +export interface CompactResponse { + readonly conversationId: string; + readonly newConversationId: string; + readonly messagesSummarized: number; + readonly messagesKept: number; +} + +/** + * Response for `GET /conversations/:id/compact-threshold` — the token count + * at which automatic compaction triggers (0 = manual only; default 350000 + * when not stored). + */ +export interface CompactThresholdResponse { + readonly conversationId: string; + readonly threshold: number; +} + +/** + * Request body for `PUT /conversations/:id/compact-threshold`. + */ +export interface SetCompactThresholdRequest { + readonly threshold: number; +} ``` diff --git a/.dispatch/wire.reference.md b/.dispatch/wire.reference.md index ead4d9c..44b0fe7 100644 --- a/.dispatch/wire.reference.md +++ b/.dispatch/wire.reference.md @@ -4,9 +4,14 @@ > types WITHOUT following the `file:` dep symlink out of this repo (which hangs on a permission > prompt). Your CODE still imports `@dispatch/wire` normally — this file is for READING only. > -> **Orchestrator:** SNAPSHOT of `[email protected]` (conversation lifecycle status). Regenerate +> **Orchestrator:** SNAPSHOT of `[email protected]` (compaction). Regenerate > whenever `@dispatch/wire` changes. > +> **2026-06-22 delta (compaction handoff — package bumped `0.10.0` → `0.11.0`, ADDITIVE):** +> adds `CompactionResult` — the result of a compaction operation (`summary`, `messagesSummarized`, +> `messagesKept`). The summary text is the model's output; the FE doesn't render it directly (it +> becomes the conversation's first system message after compaction). +> > **2026-06-22 delta (conversation lifecycle handoff — package bumped `0.9.0` → `0.10.0`, ADDITIVE):** > adds `ConversationStatus` (`"active" | "idle" | "closed"`) — the per-conversation lifecycle > status. `ConversationMeta` gains a `status` field. `active` = a turn is generating; `idle` = @@ -612,5 +617,21 @@ export interface ConversationMeta { readonly lastActivityAt: number; readonly title: string; readonly status: ConversationStatus; + /** Points to the archive conversation with full pre-compaction history. */ + readonly compactedFrom?: string; +} + +// ─── Compaction ────────────────────────────────────────────────────────────── + +/** + * Result of a compaction operation. `summary` is the text the model produced; + * `messagesKept` is how many recent messages were retained after the summary; + * `messagesSummarized` is how many old messages were replaced by the summary. + */ +export interface CompactionResult { + readonly summary: string; + readonly newConversationId: string; + readonly messagesSummarized: number; + readonly messagesKept: number; } ``` |
