release: v0.6.3 (#423–#425, #460–#462) - #463
Merged
Merged
Conversation
Atomic Chat's desktop app exposes an OpenAI-compatible Local API Server on 127.0.0.1:1337 under /v1, but the wizard had no preset for it, so users added it by hand as a custom/cloud endpoint. Add a keyless local preset (ATOMIC_CHAT_API_KEY for servers where a key was set in the app), land a :1337 External llama.cpp URL on it the way :11434 lands on Ollama, and name the preset in the refusal line for a local :1337 URL.
A wandering escalation ends the turn through the breaker path, but formatForcedLoopReply never received the detector, so a turn of eleven distinct, successful fetches closed with "stuck in a no-progress loop on `os.web.fetch` after 12 blocked attempts" and "the repeated tool call". Only one call was blocked and nothing was repeated; the veto text on the same event already said "12 different attempts". The agent loop now passes breaker.detector through, and a wandering stop says the tool reached N different arguments this turn, the per-turn cap, and that the last call was not run. Every other detector keeps its existing wording. The detector itself is unchanged. Refs #458
…a repeat Review of the first commit found the new reply could still be false: - A parallel batch is gated against the same recorded history, so its calls can carry the spread past the cap before anything is refused. The next stop then quoted, say, 14 as "the cap". The reply no longer presents the count as the cap: "hit the limit on different arguments within recent tool calls — 14, counting the last call". - The spread covers the tracker's history window, not the whole turn, so "this turn" is gone from the wording. - Past that overshoot, a verbatim repeat is still vetoed by the escalation, but the breaker signal took the repeat verdict (count 0), so the turn ended on "a no-progress loop after 0 blocked attempts". When the escalation alone forces the breaker, the signal now carries detector "wandering" and the spread (new ToolLoopTracker.wanderingSpread). The veto body keeps its repeat wording, as the existing test requires. The breaker log line also records the detector. Refs #458
…ead as its answer
A message sent while a turn is running is folded into that turn at the
next step boundary (mid-turn steering). The loop recorded it as a plain
`user` turn and the TUI drew it with the same `YOU` bubble as a prompt
that opens a turn. The model keeps working on the turn's opening
request, so the transcript showed that request's tool steps and reply
directly under the steered text, as if they answered it.
A Discord report ("the model's response to Test is strange") matches
this frame for frame when reproduced: a slow first local turn asking for
the weather in Tokyo, `Test` sent before step 0, then the thread reopened
from another thread with a running turn. The stored transcript renders
as YOU Test, reasoning (3 blocks), os.web.search 0ms, os.web.fetch 0ms,
AGENT <Tokyo weather>, then the detached-turn notice.
The steered user turn now carries `steered: true` (agent loop), the flag
reaches the chat message from both the live `steer_applied` event and a
stored session, and the bubble label reads
"YOU · steered into the running turn".
Once first-run setup was skipped, finished, or any backend got configured, decideOnboarding() never opens the flow again, and nothing in the app led back to it. - /onboarding (alias /setup) and a Setup menu row "Run first-time setup again…" clear every tui.onboarding stamp and open the flow on its splash. The once-only offers (second backend, local setup seen, import) are cleared too, so a requested re-run shows the screens a skipped first run missed. - Only the stamps change: providers, keys, sessions and memory are left alone. - Refused with a notice while a turn is running or waiting on an approval, because the flow covers the whole app, prompts included. - `atomic-agent tui --onboarding` does the same from the shell; it cannot be combined with --skip-llama-setup.
buildPrompt passed userMessage and contextualKeywordGate to clipProfileSection but never the vote threshold, so selectProfileFacts always ran with profileFilterThreshold = 0 (filter disabled) and downvoted profile facts, pinned ones included, kept rendering into every prompt. Default the threshold from config.memory.voting.profileFilterThreshold, with an explicit BuildPromptInput override; 0 still disables the filter.
- Browsing the local branch no longer writes config. Enter on "Local models" (and accepting the local offer) used to persist localModels.mode = "managed" before any model was chosen; on a re-run that turned a working external endpoint into managed-with-no-model just by pressing Enter and backing out. The write now happens when a curated or Hugging Face model is picked, still before the pull, so a Ctrl+C mid-download keeps the choice. - Re-runs stay out of the first-run funnel. The flow state carries `rerun` (true when decideOnboarding would not have opened setup), and useOnboardingLifecycle skips onboarding_step reports for it. - `--onboarding` clears the stamps just before the first render instead of at the top of tuiCommand, so a launch that dies while the runtime boots leaves them alone. The flag beats ATOMIC_AGENT_TUI_SKIP_LLAMA_SETUP on purpose; only the two flags together are rejected.
…gain] on the steer The newest user message in the chat was taken as the turn's request. After a steer that is the steer. So stopping a turn that `Test` was steered into put `retryText: "Test"` on "Agent stopped by user.", and the run-history entry recorded `Test` as the run's message. Clicking that notice's [try again] re-sent only `Test`, as a new plain turn, without the request it had corrected. `lastUserMessage` is now `lastTurnRequest` and skips steered messages. It has two callers, and both name the turn: the stopped notice's `retryText` and `withRunHistoryEntry` (completed, cancelled and failed runs). Neither wants the latest text. The composer's up-arrow history is fed separately by `appendUserMessage` and still gets the steer. A steered bubble no longer has its own [try again]. Re-sending a steer alone recreates the confusion the label removes: a new turn on a correction without its request. Resending the opening request instead would copy the button right above it, on the message that opened the turn, and a button that sends different text from its bubble would be a surprise. Removing it is the smallest change. [copy] stays, and the footer is still one row, so the height estimate is unchanged. Left as is: live, a steer applied after tool steps is drawn above those steps. The cards stay in the streaming tail until the reply. To split them at the steer, the steer would have to finalise the tail into its own message. That would clear the reasoning ring the Reasoning tab reads mid-turn and rescope the `● N tool steps` count. Too much to change for an ordering gap the label already explains.
When the running turn's opening message is not in the chat list (a turn opened over Telegram or HTTP, or a switch-back replay that dropped its oldest events), lastTurnRequest walked past the steer to a request from a turn that had already finished, and the stopped notice offered to re-run it. Past a steer, a final assistant reply now ends the walk and no retry is offered. Interim progress notes carry `progressNote` on the chat message so they do not count as that boundary.
…nload A parallel download waited on its worst connection. The stall watchdog only fires on silence and is re-armed by every chunk, so a connection the CDN slows to a few hundred bytes a second never trips it, and the plan is made once per attempt, so connections that finish their slice go home. A 2.7 GB GGUF sat at 96% for hours with a 116 MB remainder on one ~400 B/s connection while a fresh connection to the same URL ran at 1.9 MB/s. - Pace check: every 30 s a connection among several is compared with the best per-connection rate this attempt has seen (clock-measured windows plus the average of every connection that stops). Below 1/8 of it and under 256 KiB/s it is dropped and its remainder re-requested on a new connection, without reporting a retry — the download chip reads a retry as a network outage. Silent windows stay the stall watchdog's call. A segment stops being judged after three reconnects in a row that did not help, so a link slow everywhere costs at most three requests per segment while a CDN that throttles each connection after a fast start is worked around for as long as it does that. - Idle connections take work: a connection that finishes with the queue empty cuts the back half off the running slice with the most bytes left (both halves at least the 8 MiB floor). The donor's end moves to the cut, which its stream already re-reads on every chunk; a chunk crossing the cut is clamped out of the donor's count so progress and the sidecar count every byte once. Single-stream downloads, the retry and give-up policy and the sidecar format are unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Release integration for v0.6.3: the open maintainer PRs merged into one branch on top of
main(which already carries the harness stack #426–#439, #452, #453, #459), so the release is built from exactly what was tested.### profilehonoursmemory.voting.profileFilterThreshold— a downvoted fact, pinned or not, leaves the prompt:1337opens the wizard on it/onboarding(/setup), a Setup menu row andtui --onboardingre-run first-time setup without touching providers, keys, models, sessions or memorywanderingloop stop is worded by its detector (refs #458)YOU · steered into the running turn;[try again]re-sends the turn's request, not the steer#423 and #425 were rebased onto
mainafter the harness stack; neither changes harness behaviour (#423 only filters which facts render in the profile section, #425 touches TUI files only, no config bump). All six merge into this branch without conflicts.How it was verified
npm run lintclean;npm run buildclean.src/agent src/session src/tui351 files / 4 089 tests pass;src/local-llm src/tui/local-models src/tui/onboarding68 / 820;src/tui src/prompt src/memory src/agent src/providers src/llm src/config src/runtime src/analytics546 / 6 791. One uncaughtspawn … llama-server EACCESfromlocal-models-orchestrator-auto-update.test.tsreproduces identically onmainon the release Mac (all 8 of its tests pass) and is not introduced here.workflow_dispatch,publish=false) on this branch before fix(tui): mark a steered message so the running turn's reply is not read as its answer #461 succeeded on all four targets; the signed darwin-arm64 binary boots, migrates config to v69, and opens first-run setup on a fresh state directory.x-linked-etag.