summaryrefslogtreecommitdiffhomepage
AgeCommit message (Collapse)Author
2026-06-27fix(concurrency): silent background refresh — no loading flickerAdam Malczewski
The 2s status poll (and post-mutation limit reloads) toggled a visible loading state, but since the refresh is near-instant the spinner flashed for <1 frame — a visible flicker/jerk every poll. The polling is now SILENT (mirrors the heartbeat runs list): - refreshLimits/refreshStatus: re-entrancy guard (limitsInFlight/statusInFlight, no UI), no loading-state toggle. Error cleared only on success so it stays visible + stable during an in-flight retry (no vanish-mid-poll). - Removed limitsLoading/statusLoading: the Refresh buttons are plain-text (no spinner, not disabled); the empty-state guards use hasLoaded* only (no && !loading), so the list area never reflows mid-refresh. +1 regression-guard test (Refresh buttons never surface a spinner). typecheck 0/0, 926 tests green (x2), biome clean, build OK.
2026-06-27feat(concurrency): loading-ring for queued chats (CR-13 consumed)Adam Malczewski
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.
2026-06-27docs(handoff): CR-13 — per-conversation 'queued' status for the ↵Adam Malczewski
concurrency queue A small UX ask (queued chat -> loading ring, generating -> loading dots, in the tab + composer corner) needs a backend change: there is no FE-derivable per- conversation 'queued' signal (turn-start sets generating before the queue wait; concurrency status is per-provider aggregate; background tabs only get the conversation.statusChanged broadcast). Add 'queued' to ConversationStatus (@dispatch/wire) + emit it from the concurrency extension when acquire() blocks. Sent to backend agent e1d7; FE side fully designed, blocked on the wire bump. Header notes the dev merge (e81df4c, 921 tests green).
2026-06-27Merge branch 'dev' into feature/provider-concurrencyAdam Malczewski
2026-06-27feat(concurrency): provider dropdown instead of free-text inputAdam Malczewski
Replace the Add-form provider text input with a <select> dropdown populated from the available models' provider prefixes (<provider>/<model>), unioned with providers already carrying a configured limit (so a limit set out-of-band but whose model list is empty still appears). The selection auto-defaults to the first option and falls back when an option disappears. - logic/view-model.ts: pure providerFromModel + providerOptions(models, limits) (distinct provider ids, first-seen order) + 7 tests. - ConcurrencyView.svelte: models prop + <select> bound to newProviderId; disabled + 'No providers available' when there are no models. - App.svelte: pass models={store.models}; component test updated to the dropdown (selectOptions) + a no-models case (6 component tests). Verified: typecheck 0/0, 922 tests green, biome clean, build OK.
2026-06-27feat(sidebar-tabs): add dashboard home button + same-tab workspace openAdam Malczewski
2026-06-27feat(sidebar-tabs): prefix build hash with "build: "Adam Malczewski
2026-06-27fix(sidebar-tabs): align chat scrollbar with composer instead of flush ↵Adam Malczewski
against sidebar
2026-06-27feat(sidebar-tabs): use daisyui loading-dots for chat generating spinnersAdam Malczewski
2026-06-27fix(sidebar-tabs): use text-selection highlight as copy indicator instead of ↵Adam Malczewski
swapping text
2026-06-27feat(sidebar-tabs): click tab ID badge to copy conversation idAdam Malczewski
2026-06-27fix(sidebar-tabs): drop sidebar left padding so it shares the chat gutterAdam Malczewski
2026-06-27feat(sidebar-tabs): remove sidebar left borderAdam Malczewski
2026-06-27feat(sidebar-tabs): replace hamburger with directional double-chevron toggleAdam Malczewski
2026-06-27fix(sidebar-tabs): inset top-bar title 8px left to match parent px-2 spacingAdam Malczewski
2026-06-27feat(sidebar-tabs): add 2px horizontal spacing to top-bar titleAdam Malczewski
2026-06-27feat(sidebar-tabs): add active tab title to top bar (New Tab for unstarted ↵Adam Malczewski
chats)
2026-06-27docs(concurrency): expand backend handoff §2j + drop unused importAdam Malczewski
- backend-handoff.md: full §2j slice (API surface, contract note, FE summary, verification, the single-provider GET note, worktree symlink note) + bump the pinned transport-contract version 0.22.0 → 0.23.0 in the header/packages/ mirror/endpoints sections. - ConcurrencyView.svelte: remove an unused ConcurrencyLimitView type import (the type flows via viewConcurrencyLimits' return type; svelte-check clean).
2026-06-27feat(concurrency): add provider concurrency limits UI — settings + live statusAdam Malczewski
2026-06-27feat(sidebar-tabs): move tab bar from top into sidebar as vertical listAdam Malczewski
2026-06-27Merge branch 'dev' into feature/heartbeatAdam Malczewski
# Conflicts: # src/app/App.svelte # src/app/store.svelte.ts # src/app/store.test.ts # src/features/workspaces/ui/WorkspaceCard.test.ts
2026-06-27Merge branch 'feature/indent-change' into devAdam Malczewski
2026-06-27chore: update path references for directory rename (dispatch-backend → ↵Adam Malczewski
backend, dispatch-web → frontend)
2026-06-27feat(heartbeat): show next heartbeat timer + backend handoff for timestamp ↵Adam Malczewski
endpoint
2026-06-26fix(heartbeat): hide runs loading state to prevent sidebar flickerAdam Malczewski
2026-06-26fix(heartbeat): PromptEditor save not persisting — flickers and revertsAdam Malczewski
2026-06-26style: switch from tabs to 2-space indentation (incl. svelte)Adam Malczewski
2026-06-26feat(heartbeat): default system prompt to workspace default + reset buttonAdam Malczewski
2026-06-26fix(heartbeat): make PromptEditor modal truly fullscreenAdam Malczewski
2026-06-26feat(heartbeat): prompt editor modal + hours/minutes timer + backend handoffAdam Malczewski
2026-06-26feat(heartbeat): heartbeat view with config, run list, live chat modal, and ↵Adam Malczewski
sidebar wiring
2026-06-25Merge branch 'feature/ssh-support' into devAdam Malczewski
# Conflicts: # src/features/workspaces/ui/WorkspaceCard.svelte # src/features/workspaces/ui/WorkspaceCard.test.ts # src/features/workspaces/ui/WorkspacesHome.svelte
2026-06-25fix(computer): show ✓ check after a successful Test (not a stuck spinner)Adam Malczewski
When the user clicks Test, the button now transitions spinner → ✓ (green) on success / ✗ (red) on failure (persisting until the next test or computer change), instead of reverting to plain 'Test' text with no success indicator. Also refresh the connection-status badge immediately after a test completes, so a stale 'connecting' spinner caught by the 4s poll mid-test is cleared and the badge reflects the real post-test state. Clear the test result when the selected computer changes so a stale ✓ from alias A doesn't persist for alias B.
2026-06-25docs(handoff): mark §2d provider-retry divergence RESOLVED (backend merge ↵Adam Malczewski
de022ce) Backend merged dev into feature/ssh-support (de022ce): TurnProviderRetryEvent is now present alongside the SSH types. FE re-synced both file: deps; typecheck is GREEN (0/0) with ZERO FE code changes (the consumer was already complete). Full suite: 795/795 tests, biome clean, build OK. §2e verified post-merge.
2026-06-25feat(computer): SSH computer selection + status + workspace default (handoff #2)Adam Malczewski
Mirrors the cwd/workspace UI for the SSH-computer feature: - New feature library src/features/computer/: - logic/view-model.ts (pure): viewComputer/viewComputerStatus/viewTestResult/ summarizeComputers/formatHost/knownHostLabel + state->badge for the 4 ComputerStatusResponse states + SaveComputer/LoadComputerStatus/ TestComputer/LoadComputers ports. 20 view-model tests. - ui/ComputerField.svelte: per-conversation selector (dropdown + connection-status badge + Test-connection, polling the selected alias). - ui/ComputerSelect.svelte: reusable Local/computers dropdown, shared with the workspace default-computer control. - AppStore: computerId state + refreshComputer (at every focus site, parallel to refreshCwd) + setComputer (PUT /conversations/:id/computer, null=clear) + global computers catalog (GET /computers on boot, like models) + computerStatus(alias) + testComputer(alias). chat.send UNCHANGED (resolved server-side like cwd). - App.svelte: ComputerField in the Model sidebar view next to CwdField; adapted ports wrap the store. - workspaces: setDefaultComputer on WorkspaceHttp+WorkspaceStore (PUT /workspaces/:id/default-computer); default-computer selector in WorkspaceCard (reuses ComputerSelect); router passes store.computers through. - Re-mirrored .dispatch/transport-contract.reference.md (Computers section + ChatRequest.computerId); updated .dispatch/wire.reference.md (Computer/ ComputerEntry/defaultComputerId + the provider-retry divergence note from handoff #1); GLOSSARY + backend-handoff.md (handoff #2, §2e). Transparency invariant: the computer is USER-facing only (a tool-execution target, never part of the model prompt); the agent never sees it. Verify: 795/795 tests green; biome clean; vite build succeeds; 0 typecheck errors from the computer feature. (11 pre-existing svelte-check errors remain from the open §2d provider-retry divergence — backend feature/ssh-support still lacks TurnProviderRetryEvent; not from this feature.)
2026-06-25feat(workspaces): open workspace in a new browser tabAdam Malczewski
The 'Open' button on a workspace card did client-side navigation (onNavigate -> pushState), opening the workspace in the SAME tab. Replace the JS click handler with a native anchor using target="_blank" (+ rel=noopener noreferrer) so clicking Open opens the workspace in a NEW browser tab. The new tab does a full load and routes to /<id> via the existing parsePath -> App wiring. The card no longer needs the onNavigate prop (its only navigation was the Open button), so drop it from WorkspaceCard + WorkspacesHome and update the card tests to assert the new-tab link attributes instead of the onNavigate call.
2026-06-25feat(tabs): show full title in tab hover tooltipAdam Malczewski
The conversation tab's title span was truncated with a static 'Double-click to rename' tooltip. Replace the static title with the full tab.title so hovering reveals the complete conversation title (the truncate class still clips the visible label).
2026-06-25feat: workspaces shell + cwd-lsp rename + mcp/settings/system-prompt ↵Adam Malczewski
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.
2026-06-25feat(core): chunks retry-banner, wire conformance, ws logic, history adapterAdam Malczewski
- core/chunks: add retry-banner view-model (+test), update reducer/selectors/types - core/wire: update conformance checks (+test) - adapters/ws: update reconnect logic (+tests) - adapters/history: new client-side routing adapter (history + popstate wrapper)
2026-06-25chore: regenerate contract mirrors, bump deps, update docs/notes/scriptsAdam Malczewski
- Regenerate .dispatch/{wire,transport-contract}.reference.md mirrors - Bump deps (package.json, bun.lock) - Update AGENTS.md, GLOSSARY.md, README.md, ROADMAP.md - Add workspaces backend-handoff exchange + notes/assumptions-log.md - Add scripts/fix-dist-perms.sh (root-owned dist/ from Docker build) - Add scripts/live-probe-provider-retry.ts
2026-06-22fix: duplicate user message on first send in a new tabAdam Malczewski
When a draft is promoted to a real conversation, send() appends the user message as a provisional chunk (optimistic echo). But load() also fires syncTail, which fetches the CR-6 persisted user message as a committed chunk — showing the message twice until turn seal. Fix: applyHistory now removes provisional chunks that duplicate the last committed chunk (matching role + text content) when new committed chunks arrive during generation. The optimistic echo is dropped once the authoritative committed version arrives. 684 tests green.
2026-06-22feat: double-click tab to rename (PUT /conversations/:id/title)Adam Malczewski
Double-click a tab's title to enter inline edit mode. Enter or click away (blur) saves; Escape cancels. The rename is optimistic — the local tab updates immediately and PUT /title fires in the background. 683 tests green.
2026-06-22feat: switch theme from dracula to monokai (custom)Adam Malczewski
Replace dracula with a custom monokai theme (from dachinat/daisyui-themes, with modifications: all -content colors set to black, base-300 changed to #3b3a3a). Dracula is unloaded; monokai is the sole bundled theme and default. Also adds .env.development (dev ports 24203/24205) and .env.production (arch ports 24991/24990) so Vite uses the correct backend per mode. Excludes src/themes/ from biome (third-party CSS). 683 tests green.
2026-06-22feat: consume context window + percentage-based compact handoffAdam Malczewski
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.
2026-06-22feat: persist sidebar layout + open/closed state between refreshesAdam Malczewski
Sidebar panel layout (which views are open and their order) and the sidebar open/closed toggle are now persisted to localStorage. Default layout is just the Model view at the top. - ViewSidebar accepts an onChange callback that reports panel kinds - App.svelte creates two createLocalStore instances (dispatch.sidebar.views + dispatch.sidebar.open) using the store's storage adapter - AppStore exposes its storage instance so the shell persists via the same adapter (test-injectable, not globalThis.localStorage) - Tests pre-populate fake storage with ["extensions"] for the 4 tests that need the Extensions view visible 686 tests green. 0 svelte-check warnings (2 pre-existing errors from missing transport-contract exports, unchanged).
2026-06-22feat: collapsible tool output (collapsed by default like thinking)Adam Malczewski
Tool calls and results now use the same DaisyUI collapse pattern as thinking blocks — collapsed by default, click to expand. Each card shows the tool name + a wrench icon in the title; expanding reveals the input/output with overflow-x-auto for long lines and max-h-96 overflow-y-auto for very long output. Batched tool calls: each entry is its own collapse card (was a DaisyUI list). Pending results show a spinner in the title. Errors show a red badge. 686 tests green.
2026-06-22fix: tool output scrolls within its container instead of bleeding outAdam Malczewski
Add overflow-hidden to tool card containers and overflow-x-auto to <pre> elements so long tool output (file contents, JSON, etc.) gets its own scrollbar instead of expanding the chat width and creating a horizontal scrollbar on the entire transcript. 686 tests green.
2026-06-22fix: trim provisional chunks during long turns (browser stays responsive)Adam Malczewski
trimTranscript now drops oldest provisional chunks (the in-flight turn) when committed chunks are exhausted. Previously it bailed with drop=0 when committed was empty, allowing unbounded provisional growth during long generating turns (300+ chunks → browser crawls). Root cause of the syncTail approach failing: the kernel emits step-complete (line 360) BEFORE calling onStepComplete (line 542) — chunks are persisted only after tool results come back, not when step-complete fires. So syncTail on step-complete found nothing. Reverted the applyHistory + syncTail-on-step-complete changes from 4e1d041. The new approach is simpler: trim provisional directly in trimTranscript. Dropped chunks are lost temporarily (no Show Earlier) but come back as committed when the turn seals and syncTail fetches everything from the server. 686 tests green.
2026-06-22feat: trim chunks during generation via step-complete syncTail (CR-6)Adam Malczewski
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.
2026-06-22docs: update backend-handoff — CR-6 resolved by backend, trim for relevanceAdam Malczewski
Backend shipped incremental seq assignment at step boundaries during generation. Marked CR-6 as RESOLVED with the FE adoption plan (option c: fold events for current step only, syncTail for sealed steps). Trimmed the verbose resolved-CR history to a compact table — the code is the source of truth now. 686 tests green.