| Age | Commit message (Collapse) | Author |
|
1. Real context window: GET /models now returns modelInfo[model].contextWindow.
The Composer uses this instead of the hardcoded MAX_CONTEXT = 1,000,000.
Falls back to 1M when modelInfo is absent or the model has no contextWindow.
2. Percentage-based auto-compact: the compact-threshold endpoint is renamed
to compact-percent. The CompactionView now shows a percent input (0-100,
default 85, 0 = manual) instead of a token count input. Types renamed:
CompactThresholdResponse → CompactPercentResponse,
SetCompactThresholdRequest → SetCompactPercentRequest.
Note: the field name in the backend types is still 'threshold' (not
'percent') — the FE maps between them.
Re-mirrored .dispatch/transport-contract.reference.md.
686 tests green. 0 svelte-check errors + warnings.
|
|
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.
|
|
Consume the conversation lifecycle handoff ([email protected], [email protected]).
Re-pinned file: deps + re-mirrored .dispatch/*.reference.md.
- fetchOpenConversations() on connect: GET /conversations?status=active,idle
restores the tab bar across devices (merges with localStorage — opens new
tabs, removes closed ones, updates titles from backend)
- conversation.statusChanged WS handler: closed → removeTabLocally (no
re-POST); active → open tab + spinner; idle → update status map
- conversation.compacted WS handler: dispose stale store + cache, reload
history from server
- TabBar shows a spinner on active conversations (statusFor prop)
- closeTab refactored to use removeTabLocally (extracted cleanup)
- conformance guards + WS adapter tests cover all 3 new WsServerMessage types
686 tests green.
|
|
Consume the conversation.open handoff ([email protected], [email protected]).
Re-pinned file: deps + re-mirrored .dispatch/*.reference.md.
- WS adapter (logic.ts + index.ts): parse + route the new top-level
"conversation.open" WsServerMessage to an onConversationOpen handler
- app store: openConversation(id) opens (or focuses) a tab — creates a chat
store, loads history, subscribes to live turns, creates+selects the tab
- conformance guard + WS adapter tests cover the new type
- backend also shipped conversation metadata endpoints (GET /conversations,
GET /conversations/:id/last, GET/PUT /conversations/:id/title) — mirrored
but not yet consumed by the FE
682 tests green.
|
|
boundaries
Consume the message-queue + steering handoff ([email protected], [email protected]).
Re-pinned file: deps + re-mirrored .dispatch/*.reference.md.
- fold steering AgentEvent into the transcript as a provisional user bubble
(after the tool-result it followed; no de-dup — the queue surface carried it)
- add rendererId: "message-queue" custom renderer (pure parser + MessageQueueList)
rendered as a compact panel above the Composer (hidden when queue is empty)
- add ChatStore.queueMessage / AppStore.queueMessage — sends chat.queue WS op
(trim/validate non-empty; auto-starts a turn if idle)
- Composer switches to chat.queue while generating (button → Queue, placeholder
→ Steer the conversation...)
- exhaustiveness guards updated for steering + chat.queue
- carry-to-new-turn needs no special handling (normal new turn)
664 tests green.
|
|
thinking-depth knob
Consume the backend's reasoning-effort handoff ([email protected] ReasoningEffort +
[email protected] GET/PUT /conversations/:id/reasoning-effort,
ChatRequest.reasoningEffort): a 5-level selector in the sidebar Model view,
under the provider + model dropdowns. null renders as 'high (default)' per
the server-owned resolution chain; PUT on change (effective next turn);
error + revert on 400; per-conversation re-mount incl. drafts (the draft id
survives promotion, so an effort set on a draft applies from turn 1).
Re-mirrored .dispatch references; GLOSSARY 'reasoning effort'; handoff
updated. 616 tests green; live curl probe passed.
|
|
show-earlier backfill
Re-pinned [email protected]>0.10.0 + [email protected]>0.6.1 (reply
frontend-history-windowing-handoff.md); re-mirrored both .dispatch references.
- HistorySync port gains optional { limit?, beforeSeq? } (CR-5 params); the
app's createHistorySync appends them to GET /conversations/:id.
- COLD-cache fresh load now fetches ?sinceSeq=0&limit=<floor(0.75xL)> — a huge
conversation no longer ships whole to show 192 chunks. A warm-cache tail sync
stays unwindowed (windowing a tail that outgrew the limit would leave a
silent seq gap behind the cache).
- hasEarlier now derives from the [email protected] CONTRACT (1-based gap-free seqs):
loaded window starting above seq 1 => older history exists — covering both
locally-trimmed AND server-windowed transcripts (the watermark stays as the
merge floor only).
- showEarlier(): local cache first; when the cache doesn't reach far enough
back, backfills the missing older run via ?beforeSeq=<oldestKnown>&limit=
and persists it (next page-in is local). latestSeq windowed-read caveat is
satisfied structurally (tail cursor derives from the cache's max seq).
- live-probe: +6 CR-5 checks (seq origin, newest-k ascending, short-chat
exactness, beforeSeq paging, 400 validation x2). NOT yet run live — backend
was down at commit time; run pending.
- backend-handoff.md: CR-5 RESOLVED, pins/mirrors current. 602 tests green x2.
|
|
scope-aware subscriptions
- closeTab now POSTs /conversations/:id/close (abort in-flight turn + stop/disable
warming server-side); disconnect still leaves both running ([email protected])
- syncSubscriptions honors catalog scope ([email protected]): global surfaces are
not re-subscribed on conversation switch
- fix(ws): the surface-message parser dropped the conversationId echo (CR-4d was
ours, not the backend's) — preserved + unit-tested
- secondsUntilNext: 3s stale guard — a past nextWarmAt renders as waiting, not 0s
- re-pinned + re-mirrored [email protected] / [email protected]
- scripts/probe-cache-warming.ts: live CR-4 probe (default-off, future nextWarmAt,
repeated warms, mid-turn close abort, idempotent re-close) — 17/17 against bin/up
|
|
One main agent does planning, contracts, and the feature writing — no summoning. Removes ORCHESTRATOR.md and the now-obsolete summoning artifacts (.dispatch/package-agent.md + .dispatch/rules/frontend-*.md, whose code rules are restated in AGENTS.md); keeps the contract mirrors + daisyui reference. Fixes dangling refs in README.md and backend-handoff.md.
|
|
on stream
- subscribe every open conversation on load + WS reconnect (resync), unsubscribe on tab close
- derive a stream-based 'generating' state for watchers (Composer running indicator)
- fold the user-message turn event so watchers render the prompt mid-turn (de-dup vs sender's optimistic echo)
- re-pin [email protected] / [email protected]; re-mirror contracts; add user-message to the exhaustiveness guard
|
|
Backend context-size handoff: re-pin [email protected] / [email protected]
(+ re-mirror .dispatch reference snapshots). Thread the optional contextSize
through core/metrics (done fold + durable + selectCurrentContextSize: latest
turn's defined value, undefined=>unknown never 0, durable-wins-over-live).
Chat store exposes currentContextSize; ContextSizeBadge renders
"N tokens in context" / "context size unknown" above the composer.
GLOSSARY: add context size / context window. 533 tests green.
|
|
workspace ([email protected]): a cwd field in the Model sidebar view (GET/PUT /conversations/:id/cwd) + a new 'Language Servers' view (GET /conversations/:id/lsp) with per-server connected/starting/error badges, spinner, error text, and refresh. Store-owned reactive cwd, re-seeded on focus change; works for DRAFTS too (targets the draft's client-minted id, which survives promotion, so turn 1 runs in the chosen cwd). Network seam normalizes the untyped LSP body.
smart-scroll: pure stick-to-bottom reducer + injected controller shell (scroll/scrollend + a ResizeObserver on the content so the view follows async height changes — markdown/highlight, images, collapses, viewport reflow), plus a floating scroll-to-bottom button. FIX: restore the transcript scrollbar — the refactor moved overflow-y-auto to an inner child, so the flex-1 container needed min-h-0 to constrain instead of growing to content.
harness: vitest-setup polyfills Element.scrollTo + ResizeObserver (jsdom implements neither), fixing App component tests. docs: backend-handoff pruned (CR-3 resolved/removed); added cwd/LSP verification courier (backend confirmed all 6 asks ✅); removed the resolved cache-warming-timer courier.
Verified: svelte-check 0 errors, biome clean, 523 tests pass, vite build OK.
|
|
cache warming + retention, markdown
Consumes the backend cache-warming + cache-rate handoffs end-to-end and adds supporting infra:
- protocol/transport: conversation-scoped surfaces (conversationId on subscribe/invoke/surface + staleness routing); store auto-subscribes the catalog with the focused conversation and re-scopes on switch.
- surface-host: generic Number field renderer + custom rendererId dispatch (graceful skip on unknown).
- cache-warming feature: enabled toggle, min+sec interval, AUTHORITATIVE countdown from the surface's cache-warming-timer nextWarmAt, manual Warm now (POST /chat/warm), lastWarmAt-keyed history, cache-retention stat, expectedCacheRate headline.
- metrics: cross-turn expected-cache (retention) derivation + bubble badge; cache-rate fix needs no code change (inputTokens now total).
- markdown feature: marked + marked-highlight + highlight.js + dompurify, rendered in ChatView.
- fixes (gemini review): {#key activeConversationId} remount of CacheWarmingView to stop history/feedback leaking across tabs; guard NaN interval inputs from committing 0.
- docs/contracts: regenerated transport/ui-contract mirrors; backend-handoff updated (CR-3 resolved).
Verified: svelte-check 0 errors, biome clean, 494 tests pass, vite build OK.
|
|
Consume [email protected] / [email protected] metrics: usage.stepId,
step-complete (ttft/decode/genTotal), done.durationMs/usage, and the
durable GET /conversations/:id/metrics endpoint.
- core/metrics: pure live-fold + durable-merge reducer; decode-rate TPS;
head-aligned, stable placement; progressive per-step rows (each shown as
its step ends) with the turn-total row gated on the done event.
- features/chat: store folds metric events + hydrates durable TurnMetrics;
ChatView renders inline step bubbles + a turn-total bubble.
- app: MetricsSync HTTP effect (tolerates 404) injected into chat stores.
- scripts/live-probe: drives the metrics path; live-verified 17/17 vs bin/up.
- docs: regenerate .dispatch wire/transport mirrors to 0.4.0; glossary terms
(turn/step metrics, TTFT, decode time, TPS, metrics bubble); trim handoff.
|
|
This reverts commit 48c6d85c3cc5a57a729f14068e2346b17ed62088.
|
|
Consume wire/transport-contract 0.3.0 (step-complete event + timing
fields on usage/tool-result/done). Pure core/telemetry module:
foldMetricEvent (reducer) + derived selectors (stepTps, turnTps, etc).
TelemetryState is pure data, no active-turn tracking — consumers pass
turnId to selectors. ChatStore wires foldMetricEvent into handleDelta
and exposes telemetry + currentTurnId. ChatView shows step-metrics
footer (time/TPS/tokens) on assistant text bubbles and durationMs badge
on tool cards. New TurnSummary component renders turn-level stats
(wall-clock, tokens, steps, TPS) in a DaisyUI stats block. Extended
live-probe to verify telemetry events against bin/up (pending backend
restart). 336 tests, typecheck 0, biome clean, build ok.
|
|
Consume the backend's new stepId grouping key (wire/transport-contract
0.1.0 -> 0.2.0). foldEvent copies event.stepId onto live tool chunks so
live and replay group identically. New pure selector groupRenderedChunks
(core/chunks) folds a step's 2+ tool calls into one tool-batch group,
pairing each call with its result by toolCallId; single/no-stepId calls
stay as cards. ChatView renders a batch as a DaisyUI list (list-row per
pair). Fixtures updated for the now-required event stepId.
|
|
- add .dispatch/rules/frontend-styling.md (DaisyUI v5 + dracula; thin components)
- package-agent: verify section explains whole-project svelte-check under
parallel builds (judge only YOUR files)
|
|
- pin @dispatch/wire + @dispatch/transport-contract (+ ui-contract) as file:
deps @0.1.0; overrides{} workaround for their workspace:* deps (bun can't
resolve workspace: from outside the monorepo)
- add fake-indexeddb (dev) for the upcoming IndexedDB adapter tests
- mirror wire + transport-contract into .dispatch/*.reference.md for headless
agents; point package-agent.md at all three references
- backend-handoff.md: convert to a living FE<->backend seam doc
Verified green: svelte-check 0/0, vitest 91, biome clean, build ok; contract
import smoke-test passes.
|
|
Pure-core feature libraries assembled at the composition root:
- core/protocol: pure reducer over surface catalog/spec/error messages
- features/surface-host: generic field-kind interpreter (toggle/progress/
selector/stat/button) + pure plan logic; no surface-id special-casing
- adapters/ws: injected WebSocket client (effects at the edge)
- app: composition root store (Svelte 5 runes over the pure reducer),
host-relative surface WS URL resolution (resolveWsUrl), root App.svelte
Verified green: svelte-check 0/0, vitest 84 passed, biome clean, vite build ok.
|