Repository navigation
v0.5.1
What's Changed
Maintenance release: no new features, no breaking changes. Existing
settings and chat history are read as-is.
Changed
-
Codebase reorganised into explicit layers —
core/,domain/,
application/,infrastructure/,presentation/— with the dependency
direction enforced by an architecture test. Domain contracts now live
next to their consumers; the OpenAI wire format is confined to one codec;
ChatSessionis split into an accumulator, a persister and the session
itself; every conversation action (create / fork / rename / delete) goes
through one controller; MCP runtime state (connection, tools, prompts)
is no longer stored in the persisted server config. No user-facing
behaviour change beyond the fixes below. -
Stricter static analysis.
strict-casts,strict-inference,
strict-raw-typesand ~50 additional lint rules; the whole tree is
dart formatted. -
Test suite grew from 164 to 254 tests, now covering the streaming
pipeline end to end (ChatSession,ChatSessionManager,
ToolExecutor), providers and the main widgets. -
Outbound HTTP requests now identify the client. Every request to the
LLM API and to MCP servers carries
User-Agent: SpecterChat/<version> (<os>) Dart/<runtime>instead of
Dart's defaultDart/3.x (dart:io), so server-side logs can tell
SpecterChat apart from other clients and which version is talking. A
User-Agentset in a server's custom headers still takes precedence. -
The MCP
clientInforeports the real app version. It was pinned to
0.1.0regardless of the build; it now followspubspec.yaml, like the
About section.
Fixed
- Stop really stops. Pressing Stop while the model had already emitted a
complete tool call used to execute that tool and start another model
turn: the transport reported a cancelled request the same way as a
finished one. Cancellation is now its own stream event; a stopped turn
keeps its partial text and never runs tools or retries. - Deleting a conversation tears down its streaming session first, so an
in-flight stream can no longer keep writing to rows that are gone. - Message ids are strictly monotonic.
ORDER BY idis the canonical
message order, but UUIDv7 ids minted within the same millisecond (parallel
tool results, a fast local model) carried random low bits and could sort
out of creation order. The generator now uses the 12-bitrand_afield
as an intra-millisecond counter. - A conversation created while the session cap was reached could be
handed to the UI already disposed — the LRU pass no longer evicts the
session it is creating. - Conversations created within the same second now keep a stable order
in the sidebar (updated_athas one-second precision; ties break on id). - Per-conversation settings edits are no longer lost when switching
conversations during the 500 ms save debounce. - A tool call no longer fails for good once the server has dropped the MCP
session. Streamable HTTP servers evict idle sessions (rmcp does after
5 minutes without a request) and answer every later call carrying the old
mcp-session-idwith404 Session not found. The client used to surface
that as a genericError POSTing to endpoint (HTTP 404)to the model,
retry the dead id on its SSE stream, and stay marked connected; the only
way out was a manual reconnect. The transport now treats that 404 for what
the spec says it is — the session is gone — forgets the id and stops the
reconnection loop, andMcpServiceopens a new session and replays the
call once, so a pause in the conversation is invisible to the model.
Full Changelog: v0.5.0...v0.5.1