amicus 4.6.0 → 4.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +128 -0
- package/README.md +2 -2
- package/docs/ROADMAP.md +45 -8
- package/docs/configuration.md +13 -9
- package/docs/council.md +7 -2
- package/docs/publishing.md +1 -1
- package/docs/troubleshooting.md +49 -20
- package/docs/usage.md +21 -1
- package/electron/setup-ui-aliases.js +2 -2
- package/electron/workspace-ui/index.html +3 -0
- package/electron/workspace-ui/live-model.js +71 -0
- package/electron/workspace-ui/workspace-app.js +2 -2
- package/electron/workspace-ui/workspace-panels.js +9 -10
- package/electron/workspace-ui/workspace-seats.js +117 -0
- package/electron/workspace-ui/workspace-verbs.js +1 -0
- package/electron/workspace-ui/workspace.css +6 -0
- package/package.json +1 -1
- package/schemas/alias-audit.schema.json +6 -1
- package/schemas/council-run.schema.json +14 -0
- package/skills/second-opinion/MODEL-NOTES.md +182 -35
- package/src/cli-handlers-doctor.js +16 -4
- package/src/cli.js +4 -0
- package/src/council/run-chair.js +49 -3
- package/src/council/run-launch.js +4 -0
- package/src/council/run-retry-notes.js +74 -0
- package/src/council/run-retry.js +280 -0
- package/src/council/run-stages.js +41 -11
- package/src/council/verdict.js +8 -1
- package/src/headless.js +119 -9
- package/src/mcp-council-awareness.js +1 -0
- package/src/mcp-server.js +17 -2
- package/src/mcp-tools.js +5 -1
- package/src/opencode-client.js +21 -0
- package/src/sidecar/fanout-leg.js +2 -2
- package/src/sidecar/fanout.js +1 -1
- package/src/sidecar/models-probe.js +119 -0
- package/src/sidecar/models.js +81 -6
- package/src/utils/alias-audit.js +52 -1
- package/src/utils/base-url-classify.js +74 -0
- package/src/utils/council-presets.js +6 -2
- package/src/utils/curated-models.js +29 -10
- package/src/utils/degrade.js +1 -0
- package/src/utils/doctor-base-url-check.js +41 -0
- package/src/utils/model-fetcher.js +1 -0
- package/src/utils/model-tiers.js +28 -7
- package/src/utils/no-output-backstop.js +48 -0
- package/src/utils/remediation-hints.js +15 -11
- package/src/utils/result-schema.js +29 -2
- package/src/utils/update-notice.js +171 -0
- package/src/workspace/live-normalize.js +1 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amicus",
|
|
3
|
-
"version": "4.6.
|
|
3
|
+
"version": "4.6.2",
|
|
4
4
|
"description": "Multi-model LLM Council + parallel AI window for Claude Code. Run structured council reviews across Gemini, GPT, DeepSeek and more — or fork a conversation to any model and fold the results back.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Christian Wagner"
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,134 @@ All notable changes to Amicus are documented here. Format follows
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [4.6.2] - 2026-08-05
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Headless legs now fail fast when a model produces nothing.** A leg that produces zero
|
|
13
|
+
output, reasoning, or tool calls for `AMICUS_NO_OUTPUT_BACKSTOP_MS` (120 s default,
|
|
14
|
+
env-tunable) is failed with `NO_OUTPUT_BACKSTOP: …` instead of burning the full `--timeout`
|
|
15
|
+
to learn nothing — the "accepted but not serving" class. Disarms permanently on the first
|
|
16
|
+
sign of activity, so slow cold-prefill local models are unaffected; `0` (or negative)
|
|
17
|
+
disables it.
|
|
18
|
+
- **`doctor` gains a new `anthropic-base-url` row.** Prints the exact `ANTHROPIC_BASE_URL` the
|
|
19
|
+
process sees and how it will be treated — the host-form value can live only in a parent
|
|
20
|
+
process's environment, so the seen value is the only diagnostic there is.
|
|
21
|
+
- **Host-form `ANTHROPIC_BASE_URL` is now carried into the engine as `<value>/v1` by default**
|
|
22
|
+
(a provider-config override — zero env vars written; announced once per process).
|
|
23
|
+
Host-form is the Anthropic-SDK convention (the SDK appends `/v1` itself), but OpenCode's
|
|
24
|
+
provider layer treats the value as the full prefix, so unnormalized host-form previously
|
|
25
|
+
404'd every direct-Anthropic leg. `AMICUS_BASE_URL_NORMALIZE=0` disables normalization
|
|
26
|
+
entirely.
|
|
27
|
+
- **`models --check` and the `doctor` aliases row now flag stored-alias drift**: a stored alias
|
|
28
|
+
that's still catalog-listed but no longer matches any route its family currently resolves to
|
|
29
|
+
(the v4.6.1 `gemini` release-gate class), with the exact `setup --add-alias` refresh command.
|
|
30
|
+
- **`models --check` gains an opt-in `--live` probe.** `--check --live` sends one real, tiny
|
|
31
|
+
request to every *stored* alias (curated defaults are out of scope) on a single quiet fan-out
|
|
32
|
+
wave and reports `SERVED` / `SILENT` (`accepted-but-silent`) / `ERROR` per alias — the
|
|
33
|
+
on-demand version of the check that would have caught the v4.6.1 `gemini` incident (a
|
|
34
|
+
catalog-listed model no longer actually served). Spends real money: one tiny leg per stored
|
|
35
|
+
alias. Non-served outcomes fold into the existing exit code; a stored-alias count above the
|
|
36
|
+
fan-out leg cap (`AMICUS_FANOUT_MAX_LEGS`) fails fast before anything is probed. When the
|
|
37
|
+
probe can't run (catalog unavailable, or `--refresh` also passed), Amicus announces the skip
|
|
38
|
+
instead of silently dropping the flag.
|
|
39
|
+
- **The Council Workspace seats panel now shows seats a run announced dead.** The **Seats**
|
|
40
|
+
table appends a row for every seat with zero usable legs — derived from `run.json`'s
|
|
41
|
+
`degrades[]` (dead-leg/dead-wave records) and the critic's `verdict.seatLoss` — with a
|
|
42
|
+
blind-maskable model name (a dead seat has no anonymity label, so blind mode renders
|
|
43
|
+
`(masked)` rather than leak the raw id), `did not review — retried once` (the degrade
|
|
44
|
+
recorded a `retryWaveId`) or plain `did not review`, no cost cell, and muted seat-dead
|
|
45
|
+
styling. Dead rows appear live, mid-poll: as soon as the run checkpoints the loss — always
|
|
46
|
+
post-retry, so a row never lands before the seat's one shot at recovery is spent — it paints
|
|
47
|
+
and stays through every tick after (immediately for a dead-wave seat; at that stage's
|
|
48
|
+
boundary for a dead-leg seat, since the panel keeps suppressing the row while the seat's own
|
|
49
|
+
errored-leg entry is still listed live); the run's terminal refresh then additionally unions
|
|
50
|
+
the critic's `seatLoss` on top, for any loss the live payload alone didn't carry.
|
|
51
|
+
- **The chair fallback walk now records every attempt on `run.json`.** Each leg it tries — `ch1`
|
|
52
|
+
(the requested chair), `ch2` (a same-chair retry), `ch3` (the ledger-promoted fallback) —
|
|
53
|
+
appends an entry to an additive `chairAttempts[]` array (`{waveId, model, outcome, reason}`,
|
|
54
|
+
`outcome ∈ completed|error|timeout|no-output`), checkpointed after every attempt so a mid-walk
|
|
55
|
+
kill never loses what already ran. When no chair leg completes at all, the `chair-failed`
|
|
56
|
+
degrade's "What was lost" `why` now names each attempt's cause instead of one flat sentence —
|
|
57
|
+
`ch1 <model>: <reason> · ch2 <model>: <reason> · ...` — while a chair that ran but produced no
|
|
58
|
+
parseable VERDICT line keeps its original flat why (the walk didn't fail). The `ch4`
|
|
59
|
+
VERDICT-line repair re-prompt is deliberately not counted as an attempt — its chair leg already
|
|
60
|
+
completed; only the verdict line gets re-prompted.
|
|
61
|
+
|
|
62
|
+
### Fixed
|
|
63
|
+
|
|
64
|
+
- **The `gpt` quick-pick family stopped resolving once OpenAI split 5.6 into
|
|
65
|
+
tiers.** `idPattern` only matched bare numeric ids (`gpt-5.5`), so it missed
|
|
66
|
+
5.6 entirely — `gpt-5.6-sol` (premium), `gpt-5.6-terra` (mid), `gpt-5.6-luna`
|
|
67
|
+
(economy), and their `-pro` siblings all fell outside it, leaving the family
|
|
68
|
+
pinned to the older `gpt-5.5` and the owner's stored `gpt` alias reported as
|
|
69
|
+
DRIFTED. Per owner ruling, `gpt` now tracks the TERRA (mid) tier: the
|
|
70
|
+
pattern additionally matches `-terra` while still excluding `-terra-pro`,
|
|
71
|
+
`-sol*`, `-luna*`, and the unrelated `-codex` family; the pinned fallback
|
|
72
|
+
moves to `openrouter/openai/gpt-5.6-terra`. Bare numeric ids stay matched as
|
|
73
|
+
a within-family fallback.
|
|
74
|
+
- **The curated `opus` pin fell behind the live catalog.** Both routes move from Claude
|
|
75
|
+
Opus 4.8 to Claude Opus 5: `openrouter/anthropic/claude-opus-5` plus the authored
|
|
76
|
+
direct `anthropic/claude-opus-5` (verified against the live catalog and Anthropic docs
|
|
77
|
+
2026-08-04 — authored, never derived; anthropic stays in `DIVERGENT_VENDORS`). Same
|
|
78
|
+
live price, so the `frontier` bench's pricing evidence is unchanged. The offline
|
|
79
|
+
`ANTHROPIC_MODELS` floor gains a matching `anthropic/claude-opus-5` row so keyless and
|
|
80
|
+
offline installs never report the shipped default stale. Opus 5's two gateway forms
|
|
81
|
+
coincide (no dotted version segment), so the dot-vs-dash regression guards now ride
|
|
82
|
+
`haiku`, the surviving divergent-form alias.
|
|
83
|
+
|
|
84
|
+
### Removed
|
|
85
|
+
|
|
86
|
+
- **The `devstral` alias** (owner ruling 2026-08-04). OpenRouter delisted the entire
|
|
87
|
+
devstral family — zero matches across the live catalog, any vendor — and the alias had
|
|
88
|
+
no other route. No served model is a devstral successor, so the alias was dropped
|
|
89
|
+
rather than retargeted ("no pinned guess is better than a wrong one"); `mistral`
|
|
90
|
+
remains the vendor's alias, and a stored `devstral` alias in user config is flagged
|
|
91
|
+
with replacement suggestions by `models --check` / `doctor`. This was the pin that made
|
|
92
|
+
`models --check` exit 1 — the v4.6.2 release-gate risk.
|
|
93
|
+
|
|
94
|
+
## [4.6.1] - 2026-08-03
|
|
95
|
+
|
|
96
|
+
### Added
|
|
97
|
+
|
|
98
|
+
- **The MCP channel finally hears about new versions** (spec 2026-08-03). The MCP server now
|
|
99
|
+
runs the update check at startup and appends one flavor-aware notice block to the first
|
|
100
|
+
successful tool result of each server process (once per session, latched); `amicus_guide`
|
|
101
|
+
carries an always-on update line, and one `[amicus] update available` line lands in the
|
|
102
|
+
client's MCP log on stderr. The instruction is chosen config-first (`npx -y amicus@latest`
|
|
103
|
+
registrations are told a restart suffices; cached/pinned npx copies get the re-point-or-
|
|
104
|
+
clear-cache hint in the unverified voice; global installs get `npm install -g amicus`, from
|
|
105
|
+
where #33's stale-version warning takes over). Words only — no auto-update over MCP, no
|
|
106
|
+
periodic re-check; `NO_UPDATE_NOTIFIER=1` still disables the check entirely.
|
|
107
|
+
- **A lost Stage-1 seat gets one more chance (SL-2).** A council sub-wave that dies before
|
|
108
|
+
its legs exist, or a leg that ends with no usable output, is relaunched exactly once —
|
|
109
|
+
serially, after the surviving launches settle. Recovery announces in the one voice
|
|
110
|
+
(`Recovered: seat X reviewed on retry — …`, a `stage1-retry` heal on
|
|
111
|
+
`run.json`/`verdict.json` `degrades[]`) and the run stays exit 0; a seat still dead after
|
|
112
|
+
its retry degrades exactly as before, with both attempts named in the why. Unconditional;
|
|
113
|
+
gated on the run's `--max-cost` position (an over-budget run skips the retry and records
|
|
114
|
+
the loss byte-identically to v4.6.0). Retry legs and their spend-ledger rows carry
|
|
115
|
+
`retryOfWaveId`. A healed critic counts as seated in `verdict.seatLoss`.
|
|
116
|
+
|
|
117
|
+
### Changed
|
|
118
|
+
|
|
119
|
+
- **The shipped second-opinion MODEL-NOTES seed was corrected and enriched** (owner-ruled
|
|
120
|
+
fold-back, PR #93): the haiku "hard-404" warning re-caused to the `ANTHROPIC_BASE_URL` `/v1`
|
|
121
|
+
convention split, GLM's stale reliability caution withdrawn on the v4.4.1 fence-extractor
|
|
122
|
+
replay evidence, pre-degrade-era claims re-grounded in the current announce/retry contract,
|
|
123
|
+
and three model sections plus the "peer consensus ≠ evidence on published numbers" rule added
|
|
124
|
+
from the field.
|
|
125
|
+
|
|
126
|
+
### Removed
|
|
127
|
+
|
|
128
|
+
- **The unused `rebuildElectron` remediation hint** (owner ruling 2026-08-03, closing Plan 3's
|
|
129
|
+
queued hint-voice question). It had no live call site — `doctor --fix`, the in-place Electron
|
|
130
|
+
self-heal, is its stated convergence target — and its prose asserted unverified causes ("after
|
|
131
|
+
an ABI mismatch or partial unpack"). Absence-pinned in `tests/remediation-hints.test.js`; a
|
|
132
|
+
reintroduction must adopt the unverified-cause voice. The same ruling keeps
|
|
133
|
+
`sweepSessionIndexTmp`'s confident voice: its cause is definitional (an atomic-write tmp orphan
|
|
134
|
+
has no other producer), not a guess.
|
|
135
|
+
|
|
8
136
|
## [4.6.0] - 2026-08-02
|
|
9
137
|
|
|
10
138
|
### Added (v4.6 milestone — the degrade announcement invariant, plans 1-4)
|
package/README.md
CHANGED
|
@@ -441,7 +441,7 @@ $ amicus status demo123 --json
|
|
|
441
441
|
"taskId": "demo123",
|
|
442
442
|
"status": "complete",
|
|
443
443
|
"elapsed": "5m 0s",
|
|
444
|
-
"version": "4.6.
|
|
444
|
+
"version": "4.6.2",
|
|
445
445
|
"model": "google/gemini-2.5-flash",
|
|
446
446
|
"phase": "terminal"
|
|
447
447
|
}
|
|
@@ -552,7 +552,7 @@ Run `amicus doctor` first — it checks keys, catalog, OpenCode binary, Electron
|
|
|
552
552
|
| Install fails partway, or `amicus doctor` reports the OpenCode binary "not found" | A **transient** error during the OpenCode engine's own postinstall (a spawn `ENOENT`, or an antivirus file-lock while it lays down its 11 per-platform binaries) can roll back the whole atomic install — retrying usually succeeds | Just re-run `npm install -g amicus`. If it still fails, clear the cache first: `npm cache clean --force && npm install -g amicus`. |
|
|
553
553
|
| `401` / auth error | No usable key for the model's vendor — bare `provider/model` ids fall back to `OPENROUTER_API_KEY` automatically, so this means neither the direct key nor an OpenRouter key is configured (or `--gateway direct`/`openrouter` forced a gateway whose key is missing) | Run `amicus setup`, or `amicus key <provider> <key>` to add the missing key; see [Routing](#routing). |
|
|
554
554
|
| `402` / "Payment Required" on first council review / `start` / `fanout` call | Your OpenRouter key is real but has no credit. Key save (`amicus key openrouter <key>` or the setup wizard's key step) only checks that the key **authenticates** — it doesn't check balance, so a zero-credit key saves cleanly and only fails later, on the first real model call. (The `amicus council` subcommand itself is deterministic math and never calls a model.) | Add credit at [openrouter.ai/credits](https://openrouter.ai/credits), **or** switch to a zero-cost council: `amicus setup` → option 2 (Free OpenRouter council) builds one from live `:free`-suffixed models and saves it as `councils.free` — then run `amicus fanout --council free …`. See [Free council (zero-cost)](#free-council-zero-cost) above. |
|
|
555
|
-
| Every direct `anthropic/…` model (`haiku`, `sonnet`, `opus`, `claude`) errors `Not Found` in ~2 s at zero tokens, but the same model works via `openrouter/anthropic/…` | An inherited `ANTHROPIC_BASE_URL` missing
|
|
555
|
+
| Every direct `anthropic/…` model (`haiku`, `sonnet`, `opus`, `claude`) errors `Not Found` in ~2 s at zero tokens, but the same model works via `openrouter/anthropic/…` | An inherited host-form `ANTHROPIC_BASE_URL` (missing `/v1`) — the Anthropic-SDK convention, not OpenCode's; a shell spawned by Claude Code sets it for you. **Since v4.6.2 amicus normalizes this to `<value>/v1` automatically** (a provider-config override, announced once on stderr) — the model id, alias, and key were never the problem, and now neither is the URL, unless `AMICUS_BASE_URL_NORMALIZE=0` is set or the path is nonstandard. | Should now self-heal — confirm with `amicus doctor`'s `anthropic-base-url` row (shows the value seen + its treatment). Still failing: unset `AMICUS_BASE_URL_NORMALIZE` if you set it, or `export ANTHROPIC_BASE_URL=https://api.anthropic.com/v1` / unset the var entirely / pass `--gateway openrouter`. In a council a dead seat **degrades the run instead of failing it** — smoke-test each seat with one throwaway `amicus start` before paying for a council. See [docs/troubleshooting.md](./docs/troubleshooting.md#every-direct-anthropic-model-fails-with-not-found). |
|
|
556
556
|
| `Model 'X' is unverified against the direct catalog; attempting anyway` for a model that plainly exists | Not a claim the model is wrong — amicus **couldn't check**. That vendor's direct catalog fetch failed (usually a stale or truncated stored key), leaving its namespace empty, and an empty namespace never blocks a launch. The engine may still run the model from its own credential store, so a working model warns forever. | `amicus models --refresh` and watch for a provider that stays empty; re-save the good key with `amicus key <provider> <apikey>`. See [docs/troubleshooting.md](./docs/troubleshooting.md#model-x-is-unverified-against-the-direct-catalog-attempting-anyway). |
|
|
557
557
|
| Session not found | No session matches the given ID | Run `amicus list`, or omit `--session-id` to use the most recent. |
|
|
558
558
|
| No conversation history found | Project-path encoding | Check `~/.claude/projects/`; `/` and `_` in the project path are encoded as `-` in the directory name. |
|
package/docs/ROADMAP.md
CHANGED
|
@@ -7,12 +7,14 @@ major jump, gated on funding/cofounder. The observability arc is split so the **
|
|
|
7
7
|
first (v4.3)** and the **Electron "Council Workspace" (v4.4)** rides on top of it. `--dry-run` cost
|
|
8
8
|
preview dropped to the backlog.
|
|
9
9
|
|
|
10
|
-
Amicus is at **v4.
|
|
10
|
+
Amicus is at **v4.6.1** (tagged 2026-08-03). Each 4.x rev below leads with the benefit, not the
|
|
11
11
|
plumbing.
|
|
12
12
|
|
|
13
|
-
**Status:** v4.0 through **v4.
|
|
14
|
-
record of what landed, not a plan. **v4.
|
|
15
|
-
rev
|
|
13
|
+
**Status:** v4.0 through **v4.6.1** have **shipped** — everything down to the v4.7 heading is a
|
|
14
|
+
record of what landed, not a plan. **v4.7 (composition + tagging + GUI ergonomics) is the next
|
|
15
|
+
planned rev** — that scope carried the number v4.6 here until the degrade-announcement-invariant
|
|
16
|
+
milestone took the v4.6.0 release (2026-08-02); renumbered, not descoped. v5.0 remains
|
|
17
|
+
forward-looking.
|
|
16
18
|
|
|
17
19
|
> 📁 **Reading this from an npm install?** Some references below point at working documents that
|
|
18
20
|
> live in the git repository and are deliberately **not** in the published package — anything under
|
|
@@ -95,7 +97,7 @@ stops hiding. Design: `docs/superpowers/specs/2026-07-27-v4.5-save-and-share-des
|
|
|
95
97
|
client best able to show it. *(S–M; the pieces exist — see the design notes below.)*
|
|
96
98
|
- **Council policy packs + full run-profiles** (bench + lenses + options + briefing template, invoke by name) — B7/F5 *(M)*
|
|
97
99
|
- **Briefing templates + library** (F9) *(S–M)* — the foundation packs reference; the `{{input}}`
|
|
98
|
-
chaining variable and the `critique`/`refine` built-ins arrive with v4.
|
|
100
|
+
chaining variable and the `critique`/`refine` built-ins arrive with the composition rev (now v4.7)
|
|
99
101
|
- **Ride-along fixes** — FR-1 (a failed council seat can render perpetually live), the FR-2 ruling,
|
|
100
102
|
RN-1/RN-5/RN-11 Workspace renderer fixes, TST-3 real-CDP abort pass *(S each; dispositions for
|
|
101
103
|
all 17 open items are tabled in the design doc's §8)*
|
|
@@ -103,7 +105,42 @@ stops hiding. Design: `docs/superpowers/specs/2026-07-27-v4.5-save-and-share-des
|
|
|
103
105
|
> Why here: save/share velocity multipliers that only pay off once councils are a command (v4.0)
|
|
104
106
|
> and observable (v4.3/v4.4); auto-open makes the v4.4 surface discoverable on its best client.
|
|
105
107
|
|
|
106
|
-
## v4.6 — "
|
|
108
|
+
## v4.6 — "A loss announces itself" *(the degrade announcement invariant)* — ✅ SHIPPED v4.6.0, 2026-08-02
|
|
109
|
+
**Benefit:** a council run can no longer degrade quietly — every loss states what was lost, why,
|
|
110
|
+
and what it does to the run, in one voice, on every surface (stderr, `run.json`, `verdict.json`,
|
|
111
|
+
the report, `doctor`). The north star made mechanical: a correct-but-silent degrade fails the bar
|
|
112
|
+
as hard as a crash.
|
|
113
|
+
- **The ten-channel degrade announcement contract** — every loss routes through one sink
|
|
114
|
+
(`src/council/run-degrade.js`, the only code allowed to flip `degraded.value`, enforced by a
|
|
115
|
+
source-scan invariant test) and lands with mandatory what/why/effect on every surface, including
|
|
116
|
+
the report's new **"What was lost"** section — #85 *(L)*
|
|
117
|
+
- **`verdict.seatLoss` derived from the degrade records** (#84 — a dead critic *leg* finally flips
|
|
118
|
+
`criticSeated`; the v4.5.2 seatLoss suites passed byte-unedited) + **Stage-2 judge legs get
|
|
119
|
+
`runStats` cost rows** (#83 — per-leg attribution for ~38% of a run's spend that had none) *(M)*
|
|
120
|
+
- **`doctor` speaks the vocabulary** — `doctor --json` gains additive `degrades[]`, `--fix` prints
|
|
121
|
+
`Recovered:` lines in the one voice, and the engine hints state causes as **unverified** instead
|
|
122
|
+
of asserting an antivirus guess *(M)*
|
|
123
|
+
- **Workspace discoverability from the CLI** — `watch` usage names `--ui` (#80), a CLI council run
|
|
124
|
+
with Electron present prints how to open the live Workspace (#81), `watch --ui` against an
|
|
125
|
+
`--out-dir` run names its cause (#82), and the Stage-5 verdict rebuild preserves
|
|
126
|
+
`seatLoss`/`degrades[]` (#87) *(S each)*
|
|
127
|
+
- **Deliberate behavior changes** — dropped preset members and shared-server acquisition failures
|
|
128
|
+
now exit degraded (2) on every transport; judge rows raise reported cost totals vs v4.5.x
|
|
129
|
+
(`runStats` consumers keying by model must exclude `role: 'judge'`).
|
|
130
|
+
- **Docs** — the full record is `CHANGELOG.md` §4.6.0; spec
|
|
131
|
+
`docs/superpowers/specs/2026-08-01-degrade-announcement-invariant-design.md`; plans 1–4 under
|
|
132
|
+
`docs/superpowers/plans/` (`2026-08-0*-v4.6-degrade-invariant-plan-*.md`).
|
|
133
|
+
> Why it jumped the queue (2026-08-01): the v4.5.x field reports showed the engine was not losing
|
|
134
|
+
> legs (11 four-seat council runs on v4.5.4, 10 clean) — but when a seat *was* lost, nothing told
|
|
135
|
+
> the user which one. That silent-degrade class was ruled a north-star violation and took the rev
|
|
136
|
+
> number; the composition scope below moved to v4.7.
|
|
137
|
+
|
|
138
|
+
**v4.6.1 (shipped 2026-08-03):** the follow-on point release — **SL-2** ("a lost seat gets one
|
|
139
|
+
more chance": the once-only Stage-1 retry with `Recovered:` heals, ruled heal-first off SL-3),
|
|
140
|
+
the **MCP update notice**, the `rebuildElectron` hint deletion, and the **fold-back-corrected
|
|
141
|
+
MODEL-NOTES seed** (PR #93). Ninth consecutive first-attempt publish.
|
|
142
|
+
|
|
143
|
+
## v4.7 — "Compose your councils" *(renumbered from v4.6, 2026-08-02 — spec + fresh plan at kickoff per the anti-rot rule)*
|
|
107
144
|
**Benefit:** councils chain — generate → critique → refine with no manual copy-paste — and history
|
|
108
145
|
becomes navigable.
|
|
109
146
|
- **Composable/chained waves** (`--input-from <id>` / `--prompt-file -` pipe + per-source digests) —
|
|
@@ -115,8 +152,8 @@ becomes navigable.
|
|
|
115
152
|
remainder of TST-7
|
|
116
153
|
- **README + docs update** *(S)*
|
|
117
154
|
> The 2026-07-19 combined spec (`2026-07-19-v4.5-policy-packs-composition-design.md`) holds the
|
|
118
|
-
> approved chaining/tagging/F10 design detail and is the primary input to the v4.
|
|
119
|
-
> is NOT executed as-written — v4.
|
|
155
|
+
> approved chaining/tagging/F10 design detail and is the primary input to the v4.7 brainstorm; it
|
|
156
|
+
> is NOT executed as-written — v4.7 gets its own spec + fresh plan at kickoff.
|
|
120
157
|
|
|
121
158
|
### Deferred out of v4.4.1 into v4.5 (2026-07-27)
|
|
122
159
|
|
package/docs/configuration.md
CHANGED
|
@@ -36,14 +36,16 @@ Per vendor, the bare form's direct key is: `google/...` → `GOOGLE_GENERATIVE_A
|
|
|
36
36
|
`DEEPSEEK_API_KEY`.
|
|
37
37
|
|
|
38
38
|
**Inherited provider base URLs.** Amicus does not define or read `*_BASE_URL` variables for the
|
|
39
|
-
hosted vendors above,
|
|
40
|
-
hands them to the underlying provider SDK. `ANTHROPIC_BASE_URL`
|
|
41
|
-
appends only `/messages` to it, so it must include the `/v1` path segment
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
`
|
|
46
|
-
|
|
39
|
+
hosted vendors above, and it still passes the whole environment through to the OpenCode engine,
|
|
40
|
+
which hands them to the underlying provider SDK. `ANTHROPIC_BASE_URL` gets one extra treatment:
|
|
41
|
+
the SDK appends only `/messages` to it, so it must include the `/v1` path segment, and some hosts
|
|
42
|
+
set the `/v1`-less host-form for you — a shell spawned by Claude Code inherits it. **Since v4.6.2,
|
|
43
|
+
amicus detects that host-form value and carries a normalized `<value>/v1` into the engine as a
|
|
44
|
+
provider-config override by default** (no env var is rewritten) — see `AMICUS_BASE_URL_NORMALIZE`
|
|
45
|
+
above to disable it, and `amicus doctor`'s `anthropic-base-url` row to see how your current value
|
|
46
|
+
is being treated. Any other path (including an already-correct `/v1`) passes through unchanged.
|
|
47
|
+
See [troubleshooting § Every Direct Anthropic Model Fails with `"Not Found"`](./troubleshooting.md#every-direct-anthropic-model-fails-with-not-found)
|
|
48
|
+
for the pre-normalization failure mode and the manual fix if you've disabled the knob.
|
|
47
49
|
|
|
48
50
|
---
|
|
49
51
|
|
|
@@ -88,6 +90,7 @@ through OpenRouter as before.
|
|
|
88
90
|
| `AMICUS_FANOUT_MAX_LEGS` | Cap the number of concurrent legs in a single fanout wave. Protects against accidental runaway costs when `--models` is a long list. Non-positive or non-integer values fall back to the default. | `10` |
|
|
89
91
|
| `AMICUS_MCP_CLIENT` | Force the MCP server's `--client` value (`code-local`, `code-web`, or `cowork`) instead of auto-detecting it from the caller's MCP `initialize` handshake (`clientInfo.name`). Invalid values are ignored (with a warning) and detection proceeds normally. Note: `code-web` requires an explicit `--session-dir` and is not usable for MCP-spawned sessions. | auto-detected |
|
|
90
92
|
| `AMICUS_MAX_SESSIONS` | Maximum number of concurrent sessions the shared OpenCode server (`src/utils/shared-server.js`) will track before rejecting new ones. Renamed from `SIDECAR_MAX_SESSIONS` in v2.0.0. | `20` |
|
|
93
|
+
| `AMICUS_BASE_URL_NORMALIZE` | Set `0` to stop amicus from carrying a host-form `ANTHROPIC_BASE_URL` into the engine as `<value>/v1`. Host-form is the Anthropic-SDK convention (the SDK appends `/v1`); OpenCode treats the value as a full prefix, so unnormalized host-form 404s every direct-Anthropic leg. | `1` |
|
|
91
94
|
|
|
92
95
|
---
|
|
93
96
|
|
|
@@ -95,7 +98,7 @@ through OpenRouter as before.
|
|
|
95
98
|
|
|
96
99
|
These variables control the polling loop that drives headless sessions. The defaults are conservative and work for almost all workloads. You only need them if you are running against unusually slow or fast model endpoints, or if you are building tooling on top of Amicus and need tighter completion detection.
|
|
97
100
|
|
|
98
|
-
**Which of these accept `0`, and which ignore it.**
|
|
101
|
+
**Which of these accept `0`, and which ignore it.** Five knobs in this table — the four `SETTLE` knobs (`AMICUS_USAGE_SETTLE_POLLS`, `AMICUS_USAGE_SETTLE_INTERVAL_MS`, `AMICUS_USAGE_SETTLE_CALL_TIMEOUT_MS`, `AMICUS_TOOL_SETTLE_GRACE_MS`) plus `AMICUS_NO_OUTPUT_BACKSTOP_MS` — read through `envNumber()` (`src/utils/env-num.js`), which honours an explicit, finite `0` — for those, `0` is a documented escape hatch and each row below says what it switches off (`AMICUS_NO_OUTPUT_BACKSTOP_MS` treats any negative value the same as `0`). **Every other variable in this table reads through `Number(env) || default`, so `0` is falsy and silently falls back to the default** — there is no way to set them to zero, and that is deliberate: a `0` poll interval would busy-loop and a `0` stall threshold would kill every leg on its first poll. In both families a blank, missing or non-finite value falls back to the default.
|
|
99
102
|
|
|
100
103
|
| Variable | Purpose | Default |
|
|
101
104
|
|----------|---------|---------|
|
|
@@ -105,6 +108,7 @@ These variables control the polling loop that drives headless sessions. The defa
|
|
|
105
108
|
| `AMICUS_STABLE_IDLE_POLLS` | Number of consecutive idle polls required when no explicit completion signal is received (approximately 60 s at the 2 s default). This is the fallback heuristic for models or SDK versions that don't emit a clean completion event. | `30` |
|
|
106
109
|
| `AMICUS_MAX_CONSECUTIVE_POLL_FAILURES` | Consecutive poll failures before the headless runner bails. At the 2 s interval this is approximately 30 s. Prevents a dead server from burning the full session timeout on futile polls. | `15` |
|
|
107
110
|
| `AMICUS_TOOL_CALL_STALL_MS` | How long a tool call may sit pending with **no** result and no output growth before the leg is failed with `Tool call stalled: <tool>` and its OpenCode session aborted. This is the wedge guard: it targets a leg producing nothing at all, and it is skipped while a tool-settle deferral is active (`AMICUS_TOOL_SETTLE_GRACE_MS` owns that decision instead, and ends in a completion rather than a failure). **`0` is ignored** — it falls back to the default rather than disabling the guard, because a `0` threshold would kill every leg on its first poll. There is no way to switch this off; raise it if you legitimately run very long single tool calls. | `180000` |
|
|
111
|
+
| `AMICUS_NO_OUTPUT_BACKSTOP_MS` | Fail a headless leg fast when the model has produced no output, reasoning, or tool calls for this long — the "accepted but not serving" class. Disarms permanently on the first sign of activity, so slow cold-prefill local models are unaffected. **Set `0` (or negative) to disable the backstop entirely** — silent legs then run to the ordinary timeout. | `120000` |
|
|
108
112
|
| `AMICUS_USAGE_SETTLE_POLLS` | How many extra `getMessages` reads run **after** a leg has already finished, to catch provider usage/cost that lands milliseconds after the completion signal (measured: real paid legs losing their cost by 29 ms and 155 ms). The loop breaks early as soon as every assistant message carries usage, so the common case is one extra read. **Set to `0` to disable the reconciliation entirely** — legs then report whatever usage was present at completion, which can be `$0` on a leg that really did cost money. | `3` |
|
|
109
113
|
| `AMICUS_USAGE_SETTLE_INTERVAL_MS` | Delay between those settle reads. **`0` is honoured and means no delay** — the reads run back to back. It does **not** disable the reconciliation (that is `AMICUS_USAGE_SETTLE_POLLS=0`); it only removes the gap between attempts. | `400` |
|
|
110
114
|
| `AMICUS_USAGE_SETTLE_CALL_TIMEOUT_MS` | Per-call deadline for a settle read and for the child-session (subagent) spend walk. Deliberately much tighter than `AMICUS_POLL_CALL_TIMEOUT_MS`: the leg is already finished, so a hung read must not add 30 s × 3 to a run's wall time. The effective value is the **smaller** of this and `AMICUS_POLL_CALL_TIMEOUT_MS`, so raising it above that has no effect. **`0` is honoured and means no timer is armed at all** — a hung settle read or subtree walk would then wait indefinitely. | `5000` |
|
package/docs/council.md
CHANGED
|
@@ -176,7 +176,9 @@ Key semantics:
|
|
|
176
176
|
same per-member reason) as a preview, before you spend anything.
|
|
177
177
|
- Chair failure recovery: one retry of the same chair → promote the highest peers-only
|
|
178
178
|
street-cred model (from `amicus council stats`) that is not a bench seat → give up and write
|
|
179
|
-
the verdict with `overallVerdict: null`.
|
|
179
|
+
the verdict with `overallVerdict: null`. Each attempt in that walk is additionally recorded on
|
|
180
|
+
`run.json` as `chairAttempts[]` (`{waveId, model, outcome, reason}`), checkpointed after every
|
|
181
|
+
attempt so a mid-walk kill preserves what already happened.
|
|
180
182
|
- SIGINT/SIGTERM abort the active wave/solo, finalize `run.json` as `aborted`, exit 130/143.
|
|
181
183
|
`amicus abort <councilRunId>` (and the MCP tools via the sessions-dir pointer file
|
|
182
184
|
`council-<runId>.json`) work on council runs; `status`/`list` resolve them the same way.
|
|
@@ -329,7 +331,10 @@ launch:
|
|
|
329
331
|
the Stage-2 contract carries no structured reason field, so rationale lives in prose; on a
|
|
330
332
|
`--debate` run, a re-voted cell instead opens `revote-<model>.md` and also surfaces the
|
|
331
333
|
structured `reason` `debate.json` records for that re-vote), chair verdict + street-cred +
|
|
332
|
-
Stage-4 decisions, and a cost-by-seat table with a `--max-cost` ceiling gauge.
|
|
334
|
+
Stage-4 decisions, and a cost-by-seat table with a `--max-cost` ceiling gauge. The Seats table
|
|
335
|
+
also lists any seat the run announced dead as a muted, no-cost row, live — as soon as the run
|
|
336
|
+
checkpoints the loss, no terminal wait required (a seat whose errored legs still occupy the
|
|
337
|
+
active stage's roster paints once that stage completes).
|
|
333
338
|
|
|
334
339
|
**Historical runs** render entirely from the run directory — open any old `council-<runId>` at any
|
|
335
340
|
time; nothing here requires the run to still be live.
|
package/docs/publishing.md
CHANGED
|
@@ -48,7 +48,7 @@ Provenance is implied under trusted publishing.
|
|
|
48
48
|
|
|
49
49
|
Run top-to-bottom before `npm version`:
|
|
50
50
|
|
|
51
|
-
1. **MODEL-NOTES fold-back:** diff the machine-local ledger (`~/.claude/skills/second-opinion/MODEL-NOTES.md`) against the shipped seed (`skills/second-opinion/MODEL-NOTES.md`);
|
|
51
|
+
1. **MODEL-NOTES fold-back:** diff the machine-local ledger (`~/.claude/skills/second-opinion/MODEL-NOTES.md`) against the shipped seed (`skills/second-opinion/MODEL-NOTES.md`); cherry-pick generalizable lessons from the local ledger into the shipped seed — **per-section judgment, both directions, never a bulk copy** (the divergence is two-directional: adopt seed-side corrections back into the local ledger too). Merge/prune, keep it tight — no run-ledger numbers, those live in `amicus council stats`. Standing practice per the owner's ruling, 2026-08-03: shipped = curated seed, local = lab notebook.
|
|
52
52
|
2. `npm test` green; `npm run lint` clean.
|
|
53
53
|
3. **Integration tier, both rails:**
|
|
54
54
|
- `npm run test:integration` (keyless) — must be **0 failures**. This is the same thing the `integration` job in `ci.yml` runs on every push, so it should already be green.
|
package/docs/troubleshooting.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
## First: run `amicus doctor`
|
|
7
7
|
|
|
8
|
-
Before working through any symptom below, run `amicus doctor` (plugin-only installs: `npx -y amicus@latest doctor`). It checks, in order: Node version, config directory, API keys, default model, catalog freshness, alias staleness, the OpenCode binary, the OpenCode engine's MCP launch path, Electron, installed skills, MCP registration, the legacy sidecar MCP entry, session index tmp files, OpenRouter credit, local providers, and the project root — and prints a targeted fix hint for every failing check. `amicus doctor --fix` self-heals what it can (e.g. re-installs a broken Electron in place); `--json` gives machine-readable output.
|
|
8
|
+
Before working through any symptom below, run `amicus doctor` (plugin-only installs: `npx -y amicus@latest doctor`). It checks, in order: Node version, config directory, API keys, default model, catalog freshness, alias staleness and drift, the ANTHROPIC_BASE_URL form, the OpenCode binary, the OpenCode engine's MCP launch path, Electron, installed skills, MCP registration, the legacy sidecar MCP entry, session index tmp files, OpenRouter credit, local providers, and the project root — and prints a targeted fix hint for every failing check. `amicus doctor --fix` self-heals what it can (e.g. re-installs a broken Electron in place); `--json` gives machine-readable output.
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -56,33 +56,50 @@ one.** The council degrades around the missing model — the chair silently fall
|
|
|
56
56
|
collapses from 3 seats to 2, and every finding comes back `confidence: "thin"` because it only ever
|
|
57
57
|
had one peer corroborator. Nothing in `verdict.json` records that the roster changed.
|
|
58
58
|
|
|
59
|
-
**Cause:** an `ANTHROPIC_BASE_URL` environment variable
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
59
|
+
**Cause:** an `ANTHROPIC_BASE_URL` environment variable in **host-form** — missing the `/v1` path
|
|
60
|
+
segment. That's the Anthropic-SDK convention (the SDK appends `/v1` itself), not OpenCode's — its
|
|
61
|
+
provider layer treats the value as the already-complete prefix and appends only `/messages`, so
|
|
62
|
+
`https://api.anthropic.com` (no `/v1`) posts to `https://api.anthropic.com/messages` instead of
|
|
63
63
|
`https://api.anthropic.com/v1/messages`. That URL returns HTTP **404 with an empty body**, so the AI
|
|
64
|
-
SDK has no error payload to report and surfaces the bare HTTP status text — `Not Found`.
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
SDK has no error payload to report and surfaces the bare HTTP status text — `Not Found`. Some hosts
|
|
65
|
+
set this variable for you: notably, a shell spawned by Claude Code inherits
|
|
66
|
+
`ANTHROPIC_BASE_URL=https://api.anthropic.com` (no `/v1`) from the host process. The model id, the
|
|
67
|
+
alias, and the API key are never the problem; only the URL is wrong.
|
|
68
|
+
|
|
69
|
+
**Since v4.6.2, this self-heals by default.** Amicus classifies `ANTHROPIC_BASE_URL` on every
|
|
70
|
+
engine start; when it's host-form, it carries a normalized `<value>/v1` into the engine as a
|
|
71
|
+
provider-config override (no env var is rewritten) and prints one
|
|
72
|
+
`Notice: ANTHROPIC_BASE_URL is host-form (…); passing …/v1 to the engine …` line to stderr, once per
|
|
73
|
+
process. `amicus doctor` also gained an `anthropic-base-url` row that always prints the exact value
|
|
74
|
+
it sees and how it's being treated. So on a current install, this failure should be rare — if
|
|
75
|
+
you're seeing it anyway, it's one of:
|
|
76
|
+
- **`AMICUS_BASE_URL_NORMALIZE=0` is set**, the deliberate escape hatch — it disables the fix
|
|
77
|
+
entirely. Easy to hit by accident if you set it while chasing something unrelated.
|
|
78
|
+
- **`ANTHROPIC_BASE_URL` carries a nonstandard path** — anything other than blank/`/` (host-form)
|
|
79
|
+
or an already-correct `/v1` — which amicus passes through untouched rather than guessing (an
|
|
80
|
+
exotic proxy serving `/messages` at a custom root stays possible).
|
|
81
|
+
- You're running a **pre-v4.6.2** amicus, where none of the above exists yet.
|
|
82
|
+
|
|
83
|
+
**Confirm it in one command** (no key needed for the first three lines):
|
|
72
84
|
|
|
73
85
|
```bash
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
86
|
+
amicus doctor # anthropic-base-url row: the value seen + how it's treated
|
|
87
|
+
echo "$ANTHROPIC_BASE_URL" # host-form (no /v1 suffix) is the underlying condition
|
|
88
|
+
echo "$AMICUS_BASE_URL_NORMALIZE" # "0" here is what disables the automatic fix
|
|
89
|
+
amicus start --model haiku --prompt hi --no-ui # should complete; stderr shows the Notice line
|
|
90
|
+
# the first time normalization actually fires
|
|
77
91
|
```
|
|
78
92
|
|
|
79
93
|
**Fix** — pick one:
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
|
|
94
|
+
- Unset `AMICUS_BASE_URL_NORMALIZE` (or set it to anything other than `0`) to restore the default
|
|
95
|
+
self-heal.
|
|
96
|
+
- Still failing with normalization on? Add the missing segment yourself:
|
|
97
|
+
`export ANTHROPIC_BASE_URL=https://api.anthropic.com/v1` (PowerShell:
|
|
98
|
+
`$env:ANTHROPIC_BASE_URL = 'https://api.anthropic.com/v1'`), or unset the variable entirely:
|
|
99
|
+
`unset ANTHROPIC_BASE_URL` (PowerShell: `Remove-Item Env:\ANTHROPIC_BASE_URL`).
|
|
83
100
|
- Or route Anthropic models through OpenRouter for the run: `--gateway openrouter`.
|
|
84
101
|
|
|
85
|
-
|
|
102
|
+
Any of these gets `amicus start --model haiku …` completing normally again. **Before spending
|
|
86
103
|
money on a council, run one throwaway `amicus start` against each configured seat** — a
|
|
87
104
|
`Not Found` there costs nothing, whereas discovering it mid-council costs a degraded verdict.
|
|
88
105
|
|
|
@@ -188,6 +205,18 @@ For full headless configuration, see [docs/configuration.md](./configuration.md)
|
|
|
188
205
|
|
|
189
206
|
---
|
|
190
207
|
|
|
208
|
+
## Headless Leg Fails with `NO_OUTPUT_BACKSTOP`
|
|
209
|
+
|
|
210
|
+
**Symptom:** A headless leg (`amicus start --no-ui`, or one leg of a `fanout`/council run) fails with an error starting `NO_OUTPUT_BACKSTOP: model produced no output, reasoning, or tool calls in Ns`.
|
|
211
|
+
|
|
212
|
+
**Cause:** The model endpoint accepted the request but produced nothing — no output, reasoning, or tool calls — for the entire backstop window (120 s by default). This is the "accepted but not serving" class: usually a dead or misconfigured endpoint, or a catalog-listed model that's no longer actually being served upstream.
|
|
213
|
+
|
|
214
|
+
**Confirm:** `amicus models --check` — audits your configured aliases against the live catalog and flags drift/staleness. A model that still resolves locally but has quietly been retired upstream is exactly this failure mode, but a static audit only compares against the catalog's *listing* — it can't tell you whether a still-listed model actually answers. For that, run `amicus models --check --live` on demand: it sends one tiny real request to every stored alias and reports `SERVED` / `SILENT` / `ERROR` per alias, where `SILENT` (`accepted-but-silent`) is this exact `NO_OUTPUT_BACKSTOP` failure, caught deliberately instead of by accident. It spends real money (one tiny leg per stored alias) — see [docs/usage.md § `amicus models`](./usage.md#amicus-models--the-model-catalog) for cost, scope, and exit-code details.
|
|
215
|
+
|
|
216
|
+
**Fix:** Check the alias's target (`amicus models --search <term>` to find the current id, then re-point the alias) — this is the common case. Raise `AMICUS_NO_OUTPUT_BACKSTOP_MS` only if a model legitimately needs more than 120 s to produce its first token; see [docs/configuration.md § Headless Poller Tuning](./configuration.md#headless-poller-tuning).
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
191
220
|
## Multiple Active Sessions / Wrong Session Picked Up
|
|
192
221
|
|
|
193
222
|
**Symptom:** Amicus resumes or reads from the wrong session.
|
package/docs/usage.md
CHANGED
|
@@ -376,10 +376,29 @@ amicus models # List the catalog
|
|
|
376
376
|
amicus models --search gemini # Filter by substring over id and name
|
|
377
377
|
amicus models --refresh # Force-refresh from provider APIs
|
|
378
378
|
amicus models --check # Audit your aliases against the catalog
|
|
379
|
+
amicus models --check --live # + probe every stored alias with a real leg (spends)
|
|
379
380
|
```
|
|
380
381
|
|
|
381
382
|
`amicus models --check` exits with the **number of stale aliases** (capped at 100) and prints same-vendor replacement suggestions for each, so it drops cleanly into CI.
|
|
382
383
|
|
|
384
|
+
**Drifted aliases.** `--check` (and the `doctor` aliases row) also flags **`DRIFTED:`** stored aliases — a stored alias whose target is still catalog-listed but no longer matches any route its family currently resolves to (the v4.6.1 `gemini` release-gate class, where `doctor` stayed green while the model behind it had moved on). Each drift line prints the exact `amicus setup --add-alias <alias>=<current>` refresh command. Drift is informational only — unlike stale aliases, it never changes the exit code.
|
|
385
|
+
|
|
386
|
+
**Live probe (`--check --live`).** Presence in the catalog is not proof of service — a stored alias can point at a model id the catalog still lists but the provider has quietly stopped serving (the v4.6.1 `gemini` incident). `--check` alone can't see that; `--live` can, by actually asking. Scope is **stored aliases only** (`amicus setup --add-alias`) — curated defaults follow the catalog by construction and have no "was it actually served" question for a live probe to answer. **This spends real money — one tiny leg per stored alias** — every probed alias gets one ordinary engine leg on a single quiet fan-out wave, with a real session dir and a real spend-ledger row, exactly as if you'd run it yourself.
|
|
387
|
+
|
|
388
|
+
Each stored alias resolves to one of three outcomes:
|
|
389
|
+
|
|
390
|
+
| Outcome | Example line | Meaning |
|
|
391
|
+
|---------|--------------|---------|
|
|
392
|
+
| `SERVED` | `SERVED: gemini -> openrouter/google/gemini-3.6-flash ($0.0004)` | The model answered; cost shown in parens. |
|
|
393
|
+
| `SILENT` (`accepted-but-silent`) | `SILENT: probetest -> anthropic/claude-opus-4-8 — NO_OUTPUT_BACKSTOP: … (accepted but not serving)` | The endpoint accepted the request but produced nothing for the probe's 30 s backstop window (shorter than the ordinary 120 s default, and not tunable) — the exact "listed but not actually serving" failure this check exists to catch. |
|
|
394
|
+
| `ERROR` | `ERROR: gpt -> openai/gpt-5.6-terra — 402 Payment Required` | Routing, auth, or provider failure; the raw error is printed. |
|
|
395
|
+
|
|
396
|
+
**Exit code.** The probe's non-served count folds into the same exit code as the static audit — `max(existing exit, min(nonServedCount, 100))` — so a single `SILENT` or `ERROR` fails the check even when every alias is otherwise catalog-fresh. No stored aliases prints `Live probe: no stored aliases to probe` and never affects the exit code. `--json` adds `probe` (the per-alias array) and `probeCount` (its length) to the `alias-audit` document — both additive, `[]`/`0` when `--live` wasn't passed.
|
|
397
|
+
|
|
398
|
+
**Cap.** The probe is one fan-out leg per stored alias, so it's bound by the same fan-out leg cap as everything else — 10 by default, raise it with `AMICUS_FANOUT_MAX_LEGS`. More stored aliases than the cap fails fast with a one-line error and probes nothing, so a doomed wave never spends a token.
|
|
399
|
+
|
|
400
|
+
**When it doesn't run.** `--live` requires `--check` (a bare `--live` errors immediately). If the catalog itself is unavailable, or `--refresh` is also on the command line (which returns before `--check` ever runs), the probe is skipped — Amicus says so instead of silently dropping the flag: `--live skipped: <reason> — nothing was probed`. The `--json` signal differs by path: for catalog-unavailable, the `alias-audit` document carries an additive `probeSkipped` field (a reason slug, e.g. `"catalog-unavailable"`; `null` once the probe actually ran or wasn't requested); for the `--refresh` case, `--json`'s stdout document is a `model-catalog` doc instead, which never carries `probeSkipped` — the announcement goes to stderr there so stdout stays valid JSON.
|
|
401
|
+
|
|
383
402
|
**Validation on launch.** `start` and `fanout` validate the model against the catalog before launching. For an explicit `--model` on `continue`/`resume` this is **blocking** (a typo'd model fails fast with suggestions); for a model *inherited* from a prior session it's **advisory**. Skip it any time with `--no-validate-model`, or fix the catalog with `amicus models --refresh`.
|
|
384
403
|
|
|
385
404
|
**Aliases are a curated seed, not a fixed list.** `amicus setup` seeds a curated set of short aliases (e.g. `gemini`, `gpt`, `opus`, `deepseek`), and you add or override them with `amicus setup --add-alias name=provider/model`. To see exactly what resolves on *your* machine, run `amicus models` — that is the source of truth.
|
|
@@ -443,7 +462,7 @@ $ amicus status demo123 --json
|
|
|
443
462
|
"taskId": "demo123",
|
|
444
463
|
"status": "complete",
|
|
445
464
|
"elapsed": "5m 0s",
|
|
446
|
-
"version": "4.6.
|
|
465
|
+
"version": "4.6.2",
|
|
447
466
|
"model": "google/gemini-2.5-flash",
|
|
448
467
|
"phase": "terminal"
|
|
449
468
|
}
|
|
@@ -501,6 +520,7 @@ Runs every check below, in order, and prints a ✓/⚠/✗ line for each plus a
|
|
|
501
520
|
| `default-model` | Your default model alias resolves | error |
|
|
502
521
|
| `catalog` | Model-catalog cache present and within the 24h TTL | warn |
|
|
503
522
|
| `aliases` | Your configured aliases still resolve against the catalog | warn |
|
|
523
|
+
| `anthropic-base-url` | `ANTHROPIC_BASE_URL` isn't host-form (host-form 404s every direct-Anthropic leg unless normalized) | warn |
|
|
504
524
|
| `opencode-bin` | The OpenCode engine binary is on `PATH` | error |
|
|
505
525
|
| `engine-mcp` | The engine copy `npx -y amicus@latest mcp` would actually launch (catches a broken npx-cache copy a healthy local install would hide) | warn (error only if there's exactly one npx-cache copy and it's broken) |
|
|
506
526
|
| `electron` | Electron (the interactive GUI) is installed | warn — headless still works |
|
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
* delete, and add functionality for the setup wizard Step 3.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
/** Grouping metadata for the
|
|
8
|
+
/** Grouping metadata for the 19 default aliases */
|
|
9
9
|
const ALIAS_GROUPS = [
|
|
10
10
|
{ name: 'Gemini', keys: ['gemini', 'gemini-pro'] },
|
|
11
11
|
{ name: 'GPT', keys: ['gpt', 'gpt-pro', 'codex'] },
|
|
12
12
|
{ name: 'Claude', keys: ['claude', 'sonnet', 'opus', 'haiku', 'fable'] },
|
|
13
13
|
{ name: 'DeepSeek', keys: ['deepseek'] },
|
|
14
14
|
{ name: 'Qwen', keys: ['qwen', 'qwen-coder', 'qwen-flash'] },
|
|
15
|
-
{ name: 'Mistral', keys: ['mistral'
|
|
15
|
+
{ name: 'Mistral', keys: ['mistral'] },
|
|
16
16
|
{ name: 'Other', keys: ['glm', 'minimax', 'grok', 'kimi', 'seed'] },
|
|
17
17
|
];
|
|
18
18
|
|
|
@@ -102,6 +102,9 @@
|
|
|
102
102
|
<script src="./live-model.js"></script>
|
|
103
103
|
<script src="./workspace-render.js"></script>
|
|
104
104
|
<script src="./workspace-matrix.js"></script>
|
|
105
|
+
<!-- ⚠️ D8 extraction (Task 1, v4.6.2 PR4): workspace-seats.js must load BEFORE
|
|
106
|
+
workspace-panels.js, whose renderSeatsPanel delegates to it. -->
|
|
107
|
+
<script src="./workspace-seats.js"></script>
|
|
105
108
|
<!-- ⚠️ DE-ROT (F05): panels + verbs are the Task 13 split of workspace-app.js.
|
|
106
109
|
Load order matters: workspace-app.js boots last and owns `state`. -->
|
|
107
110
|
<script src="./workspace-panels.js"></script>
|
|
@@ -104,8 +104,79 @@
|
|
|
104
104
|
});
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
/**
|
|
108
|
+
* D6 (v4.6.2 PR4 Task 2, "dead-seat rows"): announced-dead seats, unioned
|
|
109
|
+
* from the run's own `degrades[]` (dead-leg/dead-wave channels — the live
|
|
110
|
+
* announcement; shapes verified against src/council/run-retry-notes.js and
|
|
111
|
+
* the skipped-path notes at src/council/run-stages.js:155-174) and the
|
|
112
|
+
* verdict's derived `seatLoss` (verdict.js summarizeSeatLoss/deriveSeatLoss
|
|
113
|
+
* — the critic-loss backstop, kept for verdicts written before `degrades[]`
|
|
114
|
+
* existed, v4.5.2 precedent). De-duped by seat model: `degrades` is scanned
|
|
115
|
+
* FIRST, so a real record's retry marker always beats the backstop's
|
|
116
|
+
* no-data guess for the same seat.
|
|
117
|
+
*
|
|
118
|
+
* `retried` reads `data.retryWaveId` / `data.firstFailure`: every
|
|
119
|
+
* still-dead-after-retry note (run-retry-notes.js's four builders) carries
|
|
120
|
+
* `retryWaveId`; the two run-stages.js skipped-path notes (fired when the
|
|
121
|
+
* once-only retry pass never even attempted the seat — an unmappable lens
|
|
122
|
+
* loss, an out-of-range index, or a zero-model unit) carry neither, so they
|
|
123
|
+
* correctly fall back to the plain phrasing.
|
|
124
|
+
*
|
|
125
|
+
* D6 filter (zero usable legs ONLY, "no ghost when a retry succeeded"): a
|
|
126
|
+
* candidate already present in `liveSeats` (it has a cost row — SL-2 healed
|
|
127
|
+
* it) is dropped. This is the one thing standing between a recovered seat
|
|
128
|
+
* and a duplicate/ghost row — same failure family as the F37 debate-role
|
|
129
|
+
* collision and the RN-11 keyed-row lessons just above (seatsFromRunStats,
|
|
130
|
+
* seatCells): an identity that is not carefully matched silently
|
|
131
|
+
* duplicates or overwrites instead of failing loud.
|
|
132
|
+
*
|
|
133
|
+
* @param {Array<object>} degrades run.json's `degrades[]` (may be absent)
|
|
134
|
+
* @param {?object} seatLoss verdict.json's `seatLoss` (may be absent)
|
|
135
|
+
* @param {Array<{model: string}>} liveSeats seatsFromRunStats(...)'s output
|
|
136
|
+
* (or any seat list keyed the same way — the live seat map)
|
|
137
|
+
* @returns {Array<{model: string, statusText: string}>}
|
|
138
|
+
*/
|
|
139
|
+
function deadSeats(degrades, seatLoss, liveSeats) {
|
|
140
|
+
var seen = {};
|
|
141
|
+
var order = [];
|
|
142
|
+
function add(model, retried) {
|
|
143
|
+
if (!model || seen[model]) { return; }
|
|
144
|
+
seen[model] = true;
|
|
145
|
+
order.push({
|
|
146
|
+
model: model,
|
|
147
|
+
statusText: retried ? 'did not review — retried once' : 'did not review',
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
(degrades || []).forEach(function (d) {
|
|
151
|
+
if (!d || d.kind !== 'degrade') { return; }
|
|
152
|
+
if (d.channel !== 'dead-leg' && d.channel !== 'dead-wave') { return; }
|
|
153
|
+
var data = d.data || {};
|
|
154
|
+
var retried = !!(data.retryWaveId || data.firstFailure);
|
|
155
|
+
if (d.channel === 'dead-leg') {
|
|
156
|
+
add(data.seat, retried);
|
|
157
|
+
} else {
|
|
158
|
+
(data.models || []).forEach(function (m) { add(m, retried); });
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
if (seatLoss && seatLoss.criticRequested && !seatLoss.criticSeated) {
|
|
162
|
+
add(seatLoss.criticRequested, false);
|
|
163
|
+
}
|
|
164
|
+
var live = {};
|
|
165
|
+
// ⚠️ Fable review (PR4b fix wave): same F34/F36 alias-selection seatCells already uses
|
|
166
|
+
// (`seat.modelInput || seat.model`, above) — a LIVE payload seat's `model` is the RESOLVED
|
|
167
|
+
// executable id, not the alias a degrade record names; `modelInput` carries the alias.
|
|
168
|
+
// Keying this map on `s.model` alone meant a dead-leg seat whose errored roster row was
|
|
169
|
+
// still in the active stage's `liveSeats` never matched its alias-keyed degrade candidate,
|
|
170
|
+
// so D6 failed to suppress it — both rows rendered until the stage boundary dropped the
|
|
171
|
+
// errored row. Terminal-path cost rows (seatsFromRunStats) carry no `modelInput` at all and
|
|
172
|
+
// are already alias-only, so `|| s.model` leaves that path unchanged.
|
|
173
|
+
(liveSeats || []).forEach(function (s) { live[s.modelInput || s.model] = true; });
|
|
174
|
+
return order.filter(function (s) { return !live[s.model]; });
|
|
175
|
+
}
|
|
176
|
+
|
|
107
177
|
// ⚠️ DE-ROT (F41): STAGE_LABELS is exported so applyLive() can label post-open stages.
|
|
108
178
|
var api = { pollDelay: pollDelay, seatCells: seatCells, seatsFromRunStats: seatsFromRunStats,
|
|
179
|
+
deadSeats: deadSeats,
|
|
109
180
|
defaultBlind: defaultBlind, dash: dash, TERMINAL_STATUSES: TERMINAL_STATUSES, STAGE_LABELS: STAGE_LABELS };
|
|
110
181
|
if (typeof module !== 'undefined' && module.exports) { module.exports = api; }
|
|
111
182
|
if (typeof window !== 'undefined') { window.AmicusLive = api; }
|