Staging (Week of 06-07-22) - #235
Merged
Merged
Conversation
…self-update loop (ENG-655) (#240) * fix(cli): restore Anton CLI for MindsHub users — minds-cloud crash + self-update loop (ENG-655) Two verified failures that made the standalone `anton` CLI unusable for MindsHub users (desktop app + webapp are unaffected — they build the LLM client via cowork-server's own build_llm_client and never call this updater). 1. Crash: `ValueError: Unknown planning provider: minds-cloud`. The CLI's LLMClient registry only has anthropic/openai/openai-compatible; MindsHub speaks the OpenAI-compatible API but the desktop/consolidated config uses the first-class name "minds-cloud", which the CLI never mapped. Add an AntonSettings field-validator that normalizes minds-cloud (and the underscore spelling) → openai-compatible, so model_post_init derives the creds/base from the minds_* fields and the existing provider handles it. Reproduced the crash and the fix in a test. 2. Perpetual failed self-update that corrupts installs (reported on Windows). `__version__` was hardcoded in anton/__init__.py and drifted from the release tags (stuck at 2.26.6.30.1 across several releases) because the release workflow only tags (pyproject is hatch-vcs / version-from-tag). The updater read that stale constant as local_ver, so remote(tag) > local was always true → it force-reinstalled the running tool on every launch → on Windows that partial reinstall bricked the env (missing rich._emoji_codes, typer rich_utils ImportError, or "No module named 'anton'"). - Derive __version__ from installed package metadata (importlib.metadata) so it always matches what's installed — kills the drift class at the root. - Anti-thrash guard in updater.py: record a tag whose reinstall didn't take and don't force-reinstall that same tag again until a newer one appears. 3. Escape hatch + dep cap: add a discoverable `--no-update` CLI flag (surfacing the existing ANTON_DISABLE_AUTOUPDATES setting), and cap `rich<15` (rich 14 + typer 0.24 verified compatible; the reported errors were reinstall corruption, not a version range). Tests: 5 for minds-cloud normalization incl. the from_settings crash repro; 3 for the updater anti-thrash marker. Full suite passes (the 2 scratchpad failures are pre-existing/environmental — they fail on clean staging too; subprocess can't spawn in the sandbox). NOTE for reviewer: the updater changes (dynamic version + anti-thrash) resolve the loop deterministically in unit tests, but the uv-tool-install / hatch-vcs interaction should be sanity-checked with a real `uv tool install git+...@<tag>` on macOS + Windows before relying on it in the wild. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(updater): read anton-agent's version specifically, not a leftover legacy anton tool (ENG-655) Local uv-flow testing surfaced an additional root cause of the update loop. `_read_installed_anton_version()` matched `re.search(r"anton\s+(\S+)")`, but a real machine can have BOTH a leftover legacy `anton` tool (pre-rename) and the current `anton-agent` tool — `uv tool list` shows `anton` first, so the bare regex read the legacy tool's version (2.26.5.13.1) instead of anton-agent's (2.26.7.6.2). That made installed_ver != remote_ver → "Update skipped" forever, even after a correct install, and combined with the new anti-thrash marker would permanently block updates. Anchor the match to `anton-agent` (line-start, optional v prefix). Verified against the real `uv tool list` output. Two tests: dual-tool ordering, and the absent-anton-agent case (returns None → caller bails safely). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * review(cli): dual-dist version fallback + case-tolerant minds-cloud validator (ENG-655) Adversarial review of PR #240 surfaced two real gaps: 1. __version__ resolved only the "anton-agent" distribution → on a legacy install under the old "anton" dist name it raised PackageNotFoundError and fell back to "0.0.0.dev0", which makes the updater see local < remote and force-reinstall once per release — reintroducing the exact ENG-655 loop for that cohort. Now try "anton-agent" then "anton" before the dev fallback. Also wrap the whole block defensively: __init__ runs on every `import anton` (incl. the desktop's cowork-server), so it must never raise. 2. The minds-cloud → openai-compatible validator was case-sensitive; "MINDS-CLOUD" or padded values slipped through and re-hit the crash. Now lower()/strip() tolerant. Cross-surface review confirmed no app/webapp/instance regressions (validate_assignment is off so the harness setattr path is unaffected; cowork-server reads importlib.metadata directly, not anton.__version__; rich 14.3.3 satisfies <15; updater is CLI-only). Tests: added case/whitespace normalization case; suite 1148 passed (2 pre-existing scratchpad-subprocess failures unrelated, fail on clean staging too). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
[ENG-583] Langfuse scrubbing
) * feat(llm): shared status-error mapper + typed model-403s (ENG-598) A gateway 403 for a tier-locked/disabled model surfaced in chat as "An unexpected error occurred: Server returned 403 — the LLM endpoint may be temporarily unavailable. Try again in a moment.. Please try again or rephrase your request." — wrong cause, useless advice, double-wrapped. Two layers: 1) openai.py mapped every non-401/429 APIStatusError to the generic "temporarily unavailable" copy, in four copy-pasted blocks (complete / stream / _complete_via_responses / _stream_via_responses) that had already drifted in wording. 2) session.py's plan_stream fallback swallowed the exception and yielded it as assistant TEXT, so the turn "succeeded" and cowork-server's turn-error mapping (the error-card machinery) never ran. Fix: - One shared _raise_for_status_error(exc, model) used by all four call paths so the mapping can't drift. 401 and 429 behavior unchanged (same copy the downstream provider_auth / token_limit detections key on; the "or to top up" wording drift normalized). - New ModelUnavailableError(ConnectionError) in provider.py carrying {code, model} for the gateway's structured 403s: model_access_denied → plan copy with upgrade/switch guidance; model_disabled → hedged copy (can be tier lock or admin kill switch until ENG-596's config lands). Detection is code-exact on error.code — BYOK OpenAI 403s, Anthropic permission errors, and Cloudflare HTML 403s carry no such code and fall through to the generic message, never the plan copy. Subclassing ConnectionError keeps every legacy call site working unchanged. - session.py: TokenLimitExceeded / ModelUnavailableError now PROPAGATE from the plan_stream fallback instead of being wrapped into prose, so the turn fails and the server can render an actionable card. All other exceptions keep the existing text fallback. Tests: 11 new (401 copy pin, 429 quota + precedence over model-403, both gateway codes incl. attrs/copy, ConnectionError compatibility, BYOK/HTML/ codeless 403 fall-through, 500 generic). Suite: 1147 passed; the 2 test_chat_scratchpad failures pre-exist on clean staging (env-dependent). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * review: don't auto-retry ModelUnavailableError — deterministic like TokenLimitExceeded A model-gate 403 is deterministic: the recovery loop would re-send the same doomed request twice (burning the retry budget + user-visible delay) before the fallback path finally propagated it. Short-circuit at the outer catch, exactly like TokenLimitExceeded. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * review(598): steer CLI default to setup for ModelUnavailableError; mapper doc/type nits Addresses lucas-koontz's review on PR #236 (all non-blocking; PR approved): - CLI turn handler (chat.py) defaulted to "retry" for any ConnectionError, and ModelUnavailableError subclasses ConnectionError — so a deterministic plan gate highlighted "retry" as the default, re-sending the same doomed request (contradicting the session.py comment). Now defaults to "setup" (switch model/provider) for ModelUnavailableError; transient ConnectionErrors keep retry. - Fixed the grammatically-broken 429 docstring bullet in _raise_for_status_error. - _raise_for_status_error typed -> NoReturn (it always raises); documents the contract the four except-sites rely on. Mapper + session tests: 11 passed. (Pre-existing F541s elsewhere in chat.py left untouched — not in scope.) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
hamishfagg
enabled auto-merge
July 13, 2026 04:15
hamishfagg
disabled auto-merge
July 13, 2026 04:30
hamishfagg
enabled auto-merge
July 13, 2026 04:30
hamishfagg
approved these changes
Jul 13, 2026
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
No description provided.