| Age | Commit message (Collapse) | Author |
|
# Conflicts:
# .dispatch/transport-contract.reference.md
# backend-handoff.md
# src/app/App.svelte
# src/features/chat/ui/Composer.svelte
|
|
Images are now stored on disk under tmp (not SQLite) and served via
GET /images/:conversationId/:imageId. Persisted ImageChunk.url is a compact
relative HTTP path (/images/<conv>/<uuid>.png) instead of a base64 data URL.
No wire/transport-contract type change (behavior only) — re-mirrored the
delta notes.
- New pure resolveImageUrl(url, apiBase) helper (core/chunks/image-url.ts,
+8 tests): data/absolute URLs pass through; relative paths are prepended
with the API base (no double slash; empty base -> root-relative). Exported
from core/chunks + re-exported from features/chat.
- ChatView: new apiBaseUrl prop; <img src> uses resolveImageUrl. The
optimistic echo's data URL passes through; persisted relative paths
resolve against the base. +3 tests.
- AppStore exposes httpBase (getter); App.svelte passes apiBaseUrl into
ChatView and the heartbeat RunModal (also renders image chunks).
Verification: svelte-check 0/0; vitest 959/959 (run twice, +11); biome
clean; vite build OK. See backend-handoff.md §2j-update-2.
Not merged or pushed.
|
|
Backend vision update (additive to [email protected] / [email protected],
no version bump).
Contracts mirrored (.dispatch/transport-contract.reference.md):
- VisionSettingsResponse + SetVisionSettingsRequest (GET/PUT /settings/vision).
- Delta note: read_image -> consult_vision; numbered placeholders; compaction.
Tool rendering (ChatView):
- read_image test -> consult_vision (rendering is generic by toolName).
- +2 tests: numbered-placeholder text chunk + [Compacted image] text chunk
(both regular text chunks, render as-is — no special handling).
New vision feature library (src/features/vision/):
- logic/view-model.ts (32 tests): VisionSettings/VisionSettingsPatch types
(consumer-defines-port), LoadVisionSettings/SaveVisionSettings ports +
results, normalizeVisionSettings (network-seam coercion), parseImageLimit/
imageLimitChanged, compactionModelOptions (vision-capable models via chat's
public isVisionModel + Auto sentinel), round-trip helpers, imageLimitLabel.
- ui/VisionSettingsView.svelte (9 tests): imageLimit input + Save,
compactionModel dropdown (Auto + vision-capable models), load-on-mount,
save-on-change, error/saved feedback.
- index.ts.
Cross-unit seam: isVisionModel added to features/chat public index.ts
(additive); imported through the public surface, not internals.
Store wiring (src/app/store.svelte.ts):
- visionSettings state + refreshVisionSettings (GET /settings/vision,
normalized at the seam) + setVisionSettings (PUT, partial, returns merged) +
VisionSettingsResult; seeded on boot; exposed on AppStore. +4 store tests.
Mounted in App.svelte: new "Vision" sidebar view kind + VisionSettingsView
in viewContent (not conversation-scoped); load/save adapters; visionManifest.
Verification: svelte-check 0/0; vitest 948/948 (run twice, +47 since the
prior vision commit); biome clean; vite build OK. See backend-handoff.md §2j.
Not merged or pushed.
|
|
Backend shipped "queued" ConversationStatus (additive to [email protected]): when a
request blocks on a concurrency slot, conversation.statusChanged broadcasts
"queued" (broadcast-only, never persisted); "active" on slot grant.
FE consumes it:
- WS parser (adapters/ws/logic.ts): accepts "queued" in the status set.
- Store handler: "queued" updates the status map + opens a tab for a new
cross-device queued conversation (like "active").
- TabList: status === "queued" -> loading-ring (spinner, aria-label "Queued");
"active" -> loading-dots (unchanged).
- Composer: status type widened to ComposerStatus (idle|running|queued|error),
exported from features/chat. "queued" -> a loading-ring status icon +
placeholder "Queued for a slot…"; behaves like "running" for the send
button (steer/stop — the turn is in flight, just waiting for a slot).
- App.svelte: composerStatus derived (error > queued > running > idle) —
conversationStatus === "queued" wins over generating so the corner shows a
ring during the wait (turn-start fires before the slot is granted, so
generating is already true while status === "queued").
- Re-mirrored .dispatch/wire.reference.md (ConversationStatus widened + header).
Tests: WS parser accepts queued; store handler sets status + opens a cross-device
tab + transitions queued->active->idle; TabList renders a ring for queued + dots
for active. typecheck 0/0, 925 tests green (x2), biome clean, build OK.
backend-handoff.md CR-13 marked RESOLVED.
|
|
|
|
features + app wiring
- workspaces: URL-driven conversation grouping (home listing at /, routing,
store, http adapter, WorkspaceCard) wired into the App.svelte shell
- rename features/workspace -> features/cwd-lsp (the cwd/lsp status feature)
- new features: mcp (status view), settings (chat-limit field), system-prompt
(prompt builder), all rendered via the generic surface host
- chat: store + ChatView updates
- tabs: tabs-store updates
- app wiring: ErrorModal (full-screen error surface), app/App.svelte + store.svelte
This commit makes HEAD typecheck clean for the first time: the prior HEAD
(c95cc77) imported features/settings from app/App.svelte but never committed
the feature, so only the full working tree was green.
|
|
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.
|
|
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.
|
|
Restore the ergonomic composer from old Dispatch: an auto-resizing textarea
(1→7 lines) with a fixed-width Send button beside it, and a status bar BELOW
holding a status icon · context-window fill bar (escalating success/warning/
error color) · compact token count (current / limit · pct%).
The bar reuses the latest turn's contextSize as current usage and HARDCODES a
1,000,000-token window limit as a placeholder (real per-model limit is the next
backend ask). Absorbs the standalone ContextSizeBadge (removed). Pure helpers
computeContextUsage + formatCompactTokens added to core/metrics (tested).
540 tests green.
|
|
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.
|
|
+ tables
views (new feature):
- pure panel-stack reducer + thin generic ViewSidebar (dropdown picker + add/remove),
switches on view KIND, never a surface id
Extensions view (composition root):
- folds frontend modules + backend surfaces into one "Extensions" view
- frontend module list AGGREGATED from each feature's public `manifest` export
(can't drift); no per-module version (FE features are internal to dispatch-web)
- surfaces are AUTO-SUBSCRIBED on catalog + rendered expanded (no catalog buttons)
surface-host:
- consecutive `stat` fields coalesce into one aligned label/value table (StatTable)
- generic custom-field renderer: dispatch on rendererId === "table" → SurfaceTable
(pure parseTablePayload), so a backend `custom`/table field renders generically
- shared presentational components/Table.svelte (used by both, neither feature
depends on the other)
store:
- auto-subscribe every catalog entry, unsubscribe vanished ones, re-subscribe all
on reconnect; expose all received specs via `surfaces` (drops single-selection)
backend-handoff: CR-1 — emit Loaded Extensions as a custom/table field; notes
what's already covered FE-side (renderer shipped, stat-table fallback works).
|
|
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.
|
|
- features/tabs: pure tab-workspace reducer (create/select/close/setModel/
setTitle/deriveTitle, draft=null active) + injected-persistence runes store
- features/chat: mutable per-tab model (setModel) + delta routing guard
(ignore foreign conversationId) + ModelSelector.svelte + DaisyUI chat bubbles
/ composer (keeps streaming <details> keying fix)
- features/conversation-cache: surface delete(conversationId) on the wrapper
for tab-close local-forget
- adapters/local-storage: generic injected JSON localStore<T> (quota/corrupt-safe)
Verified: svelte-check 0/0, vitest 273, biome clean, build ok.
|
|
- adapters/idb: createIdbChunkStore implements the ConversationChunkStore port
over IndexedDB (compound [conversationId,seq] key, idempotent append, meta
store for lastAccess); 8 tests with fake-indexeddb
- features/chat: createChatStore (runes-thin over the core/chunks reducer, all
effects injected via ChatTransport/HistorySync/ConversationCache ports) +
ChatView/Composer svelte-thin UI; folds chat.delta, syncs on turn-sealed,
hydrates from cache then catches up; 25 tests
Verified green: svelte-check 0/0, vitest 202, biome clean, build ok.
|