| Age | Commit message (Collapse) | Author |
|
Backend now persists concurrency limits across reboots (no API contract change
— same endpoints/shapes, so no re-pin/re-mirror needed). Taking the suggested
optional UX hint: after a successful Save the limit row shows a brief green
'Saved.' that clears on the next edit (mirrors the ChatLimitField pattern).
backend-handoff.md §2j: notes the persistence change + the two earlier
backend-only changes (200ms release cooldown; 'queued' status — already shipped
as CR-13). typecheck 0/0, 926 tests green, biome clean, build OK.
|
|
The 'Concurrency limits' and 'Live status' section headings are clear on their
own; the explanatory paragraphs under each were overbearing. Removed both.
|
|
The 'queued' (waiting-for-a-concurrency-slot) indicator on the tab + composer
corner used loading-spinner; switch to DaisyUI's loading-ring. The 'active'
(generating) indicator stays loading-dots. typecheck 0/0, 926 tests green,
biome clean, build OK.
|
|
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.
|
|
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.
|
|
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).
|
|
|
|
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.
|
|
|
|
|
|
against sidebar
|
|
|
|
swapping text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
chats)
|
|
- 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).
|
|
|
|
|
|
# Conflicts:
# src/app/App.svelte
# src/app/store.svelte.ts
# src/app/store.test.ts
# src/features/workspaces/ui/WorkspaceCard.test.ts
|
|
|
|
backend, dispatch-web → frontend)
|
|
endpoint
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sidebar wiring
|
|
# Conflicts:
# src/features/workspaces/ui/WorkspaceCard.svelte
# src/features/workspaces/ui/WorkspaceCard.test.ts
# src/features/workspaces/ui/WorkspacesHome.svelte
|
|
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.
|
|
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.
|
|
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.)
|
|
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.
|
|
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).
|
|
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.
|
|
- 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)
|
|
- 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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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).
|
|
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.
|
|
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.
|