amicus 4.9.7 → 4.10.0

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.
Files changed (66) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/CHANGELOG.md +125 -0
  3. package/README.md +2 -1
  4. package/bin/amicus.js +5 -0
  5. package/docs/ROADMAP.md +33 -5
  6. package/docs/architecture-map.md +41 -6
  7. package/docs/configuration.md +14 -8
  8. package/docs/council.md +140 -3
  9. package/docs/usage.md +29 -6
  10. package/electron/ipc-setup.js +6 -9
  11. package/electron/setup-ui-alias-groups.js +29 -124
  12. package/package.json +1 -1
  13. package/schemas/council-verdict.schema.json +3 -1
  14. package/skills/second-opinion/SEAT-BRIEFS.md +6 -0
  15. package/src/cli-council-run-tools.js +168 -0
  16. package/src/cli-handlers-council-run.js +6 -6
  17. package/src/cli-handlers.js +8 -1
  18. package/src/cli.js +34 -1
  19. package/src/council/briefings-chair.js +1 -1
  20. package/src/council/briefings-task.js +11 -5
  21. package/src/council/briefings.js +25 -7
  22. package/src/council/report-lost-rows.js +89 -0
  23. package/src/council/report-md.js +3 -1
  24. package/src/council/report.js +3 -2
  25. package/src/council/run-degrade.js +22 -1
  26. package/src/council/run-finish.js +23 -1
  27. package/src/council/run-launch.js +33 -4
  28. package/src/council/run-retry-launch.js +9 -4
  29. package/src/council/run-retry.js +3 -0
  30. package/src/council/run-seat-tools-verify.js +296 -0
  31. package/src/council/run-seat-tools.js +274 -0
  32. package/src/council/run-server.js +41 -6
  33. package/src/council/run-stage1-launch.js +8 -3
  34. package/src/council/run.js +21 -21
  35. package/src/council/seat-tools.js +299 -0
  36. package/src/council/verdict-seats-reviewed.js +76 -6
  37. package/src/headless.js +136 -6
  38. package/src/mcp-council-pack-map.js +24 -0
  39. package/src/mcp-council-run.js +17 -15
  40. package/src/mcp-server.js +2 -2
  41. package/src/mcp-tools.js +15 -4
  42. package/src/opencode-client.js +26 -0
  43. package/src/pack/pack-validate.js +3 -1
  44. package/src/prompt-builder.js +2 -2
  45. package/src/sidecar/aliases-review-gate.js +65 -0
  46. package/src/sidecar/aliases-review-prompt.js +91 -0
  47. package/src/sidecar/aliases-review-render.js +116 -0
  48. package/src/sidecar/aliases-review.js +298 -0
  49. package/src/sidecar/aliases.js +279 -0
  50. package/src/sidecar/fanout.js +7 -1
  51. package/src/sidecar/heartbeat.js +46 -0
  52. package/src/sidecar/models.js +20 -7
  53. package/src/sidecar/session-utils.js +7 -34
  54. package/src/sidecar/setup.js +20 -18
  55. package/src/utils/agent-mapping.js +1 -1
  56. package/src/utils/alias-groups.js +128 -0
  57. package/src/utils/alias-proposals.js +151 -0
  58. package/src/utils/alias-resolver.js +1 -1
  59. package/src/utils/alias-state.js +88 -0
  60. package/src/utils/alias-store.js +65 -0
  61. package/src/utils/config.js +10 -5
  62. package/src/utils/degrade.js +8 -0
  63. package/src/utils/model-id-siblings.js +106 -0
  64. package/src/utils/model-validator.js +1 -1
  65. package/src/utils/quick-picks.js +13 -32
  66. package/src/utils/text-sanitize.js +27 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amicus",
3
- "version": "4.9.7",
3
+ "version": "4.10.0",
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
@@ -3,6 +3,131 @@
3
3
  All notable changes to Amicus are documented here. Format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow semver.
5
5
 
6
+ ## [4.10.0] - 2026-09-14
7
+
8
+ ### Added
9
+
10
+ - **`amicus aliases`** — your model aliases as a standing command: `following` (the shipped
11
+ pin, moves with releases) vs `pinned` (yours), grouped by vendor; `--review` walks every
12
+ proposal in a numbered picker (accept a newer same-tier sibling, follow the shipped pin,
13
+ choose another catalog id, skip, or never ask again — no copy-paste); `--json` for scripts.
14
+ Without a terminal `--review` prints the list, says it is interactive, and exits 1;
15
+ `--unpin <name>` removes a pin (a curated name goes back to following, a custom name is
16
+ deleted); refuses instead when `name` is also your (non-curated) `config.default`, so a
17
+ delete can never leave the default dangling on a key that no longer resolves — pick a new
18
+ default first. The sibling comparator behind "newer same-tier sibling" (and shared with the
19
+ CI alias-pin drift gate, `scripts/check-ci-alias-pins.js`) never reads a size/variant token
20
+ glued to a number (`20b`, `8x22b`) as a version, so a differently-sized variant is never
21
+ offered as a sibling of another. (#238, #249)
22
+
23
+ ### Changed
24
+
25
+ - **A curated alias follows the shipped pin unless you pin it.** A name absent from
26
+ `config.aliases` resolves to the pin amicus ships (this is how the merge always worked);
27
+ a present key is a pin. On save, a key equal to the shipped pin is dropped with a Notice —
28
+ every alias still resolves to the same id it did before. The setup wizard no longer seeds
29
+ the 21 curated ids; it pins only the default you chose, and only when its live flagship
30
+ differs from the shipped pin, and says so. (#238 D1/D6/Q9)
31
+ - **The drift report cannot propose a downgrade.** `models --check`'s family fallback-drift
32
+ line ignores non-authoritative catalog rows and is silent when the OpenRouter namespace was
33
+ rejected this run. (#238 §5)
34
+ - The `--add-alias` copy-paste hints in `models --check`, model validation and alias repair now
35
+ point at `amicus aliases --review` where the alias is reviewable there. (#238 D4)
36
+
37
+ ## [4.9.8] - 2026-09-13
38
+
39
+ ### Changed
40
+
41
+ - **Council legs run as two per-run agents with an explicit tool allowlist.** Stage-1 seats and
42
+ their retries run as `council-seat` — task mode defaults to `webfetch`, review mode to no tools,
43
+ and `--tools <a,b,c>` (MCP: `tools`) opts more in, validated against the engine's own declared
44
+ ids before any leg launches; repair, judge, debate and chair legs run as `council-support` with
45
+ no tools at all. `task` and `skill` are refused (they spawn or escape the session), as are
46
+ `edit`/`write`/`apply_patch`/`question`/`invalid`; `--agent Plan|Build` is the escape hatch
47
+ (`Plan` = the pre-4.9.8 behaviour: edits denied, reads/searches/shell allowed). A local tool
48
+ needs `--out-dir` outside the project tree and scopes the seats to the tree with
49
+ `external_directory: deny`. With `read` opted in the seat is denied `.env`, `.env.*` and
50
+ `.envrc` files at the engine (the match is case-sensitive on Linux; the deny rules are
51
+ measured to render after the seat's own read allow; the refusal itself is exercised by the
52
+ release ritual's live run); `grep` and `bash` have no per-file fence. `bash` is outside
53
+ every fence (run directory, home, network) and the CLI says so when it is opted in; the CLI
54
+ also prints a Notice for `grep`/`glob` (no per-file fence) and for `--agent Plan` (every leg
55
+ can run commands); `tools: null` is treated as unset like `agent: null`; an `--agent` run's
56
+ directory stays inside the project; `--tools` and `--agent` cannot be combined; `todowrite`
57
+ counts as a tool that never touches the tree. After registration, the run also reads back
58
+ what the engine actually rendered for each council agent — over the run directory, its
59
+ `_scratch` support-leg directory, and the project tree when local — and refuses before
60
+ launch if an `opencode.json`/`.opencode/agent` file the engine loads for that directory (the
61
+ tree's, or your global config) widened it, or if the engine cannot be asked at all
62
+ once verification can run — including when the run's own shared OpenCode server fails to
63
+ start: a default (non-`--agent`) run now refuses before any launch instead of falling back,
64
+ and the per-wave fallback servers that used to absorb that failure for every run now serve
65
+ only an `--agent` run, whose verification is skipped by design; a reviewed tree's own config
66
+ was measured to merge into the registered agent by key order (ruling P2-R33; the
67
+ unconditional refusal on an unverifiable engine, a failed shared-server start included, is
68
+ P2-R38/P2-R43, `_scratch` coverage is P2-R39, and the external_directory exemption for the
69
+ engine's own tool-output cache — resolved XDG-first, matching `auth-json.js`/`engine-log.js`
70
+ — is narrowed by P2-R40/P2-R42). The tripwire also verifies the council agents' non-permission
71
+ surface (prompt, model, sampling, options, mode) as rendered, since a tree can set those too
72
+ (P2-R53); a council agent is registered by replacing any same-name entry; the local-tool
73
+ placement rule compares physical paths, so a run directory reached through a symlink into the
74
+ project is refused (P2-R54); the CLI prints a Notice for `read` naming its exact fence;
75
+ `tools: null` over MCP is unset (P2-R55).
76
+ Two of the three leg-loss classes in the 2026-09-11 study shared one
77
+ precondition — a seat reached for a tool it did not need (gemini `grep`/`glob` over the global
78
+ install, cohere `task {}`) — and this closes that door. Previously every leg ran as the engine's
79
+ `Plan` agent, which denies file edits but leaves the rest of the tool set available (the
80
+ study saw seats reach for `grep`, `glob` and `task` under it). (`docs/council.md` § Tool
81
+ access; spec §4; PR 2 of 3.)
82
+
83
+ ### Fixed
84
+
85
+ - **Headless legs are no longer declared complete while the engine is still answering.**
86
+ The stable-idle heuristic (`headless.js`, the v4.4 B4 "measured defect site") now
87
+ defers to `session.status`: while the engine reports `busy` (or `retry`, provider
88
+ backoff) and no tool call is live, flat output is treated as activity rather than
89
+ silence, and the leg waits for its message to finalize. In-flight reasoning and text
90
+ are invisible to the poller, so a seat that narrated between tool calls and then
91
+ answered for longer than 60 s was harvested mid-answer — three complete council
92
+ deliverables were discarded 39–107 s before they finished in the 2026-09-11
93
+ study (run D0), and the run reported `complete` with no degrades. The B4 bounded
94
+ tool-settle ceiling is unchanged and still governs whenever a tool call is live. When
95
+ `session.status` is unavailable the heuristic runs as the fallback it was always meant
96
+ to be. The trade is explicit: a busy-but-wedged (or permanently retrying) leg that
97
+ already produced output now runs to its `--timeout` (15 min default) and is named
98
+ `timeout`, instead of being declared complete at 60 s on a stub; the debug trace
99
+ names the veto once per flat stretch (and again whenever a non-zero count is reset,
100
+ e.g. a status flip-flop) and the exit line records the last engine status. When the
101
+ fallback heuristic ends an unfinalized message the leg logs a warning naming the
102
+ status it saw, and a retry whose next attempt lies beyond the leg deadline ends the
103
+ leg at once as `RETRY_BEYOND_DEADLINE` instead of holding the seat (never once the
104
+ last message has finalized — that leg completes normally — and never while a tool
105
+ call is live, where the tool-settle ceiling governs).
106
+ (`docs/superpowers/specs/2026-09-11-council-leg-completion-design.md` §3; PR 1 of 3.)
107
+ - **A seat whose findings nothing could verify is now visible in the report and in the verdict
108
+ census.** When a Stage-1 response carried no parseable findings block and the bounded repair
109
+ supplied one, the seat's `runStats` row has carried `findingsUnverified: true` since the repair
110
+ contract landed (LC-11) — through `tally.json` and `verdict.json` — and died at the renderer:
111
+ `report.html`'s **What was lost** table held only the degrade sink's records (dead legs and the
112
+ like), and `seatsReviewed` counted the seat as a full reviewer. Now the report (Markdown and
113
+ HTML) gains one **What was lost** row per such seat (channel `unverified-repair`: *seat X's
114
+ findings came from a repair of a response with no findings block — nothing verified them*) and
115
+ one per refused repair (channel `repair-refused`, naming the code), derived from `runStats` when
116
+ the report is built — so `amicus council report` on an older `verdict.json` shows them too, and
117
+ nothing in `run.json`, the exit code or the degrade sink changes. `verdict.json`'s census gains
118
+ two numbers, `seatsReviewed: {reviewed, unverified, refused, of}` (always written, never more
119
+ than `reviewed`; older documents still validate) — `refused` (council #248 round 2) counts
120
+ seats whose repair was refused: they tallied no findings and would otherwise read as full
121
+ reviewers on every gate surface, and the council-review check title and sticky comment print
122
+ `(N unverified)` and `(N refused)` when non-zero. A run also prints one `Notice:` line on
123
+ stderr at the end naming its unverified and refused seats, so a local run is no longer silent
124
+ about them; the exit code and every artifact are unchanged. The wording never says "stub":
125
+ the flag also fires on a real review whose trailing JSON block was malformed. Study run D0
126
+ (three narration stubs, three repairs) now reads `{reviewed: 3, unverified: 3, refused: 0,
127
+ of: 3}` in `verdict.json` — `seats reviewed: 3 of 3 (3 unverified)` in the sticky comment
128
+ — with three rows in the report, where it read `{reviewed: 3, of: 3}` and nothing. (#242;
129
+ `docs/superpowers/specs/2026-09-11-council-leg-completion-design.md` §5; PR 3 of 3.)
130
+
6
131
  ## [4.9.7] - 2026-09-09
7
132
 
8
133
  Three findings deferred from the v4.9.6 cut, and the two open questions filed beside them. The
package/README.md CHANGED
@@ -419,6 +419,7 @@ When you don't need a full council — just one other model's take — fork a co
419
419
  | `amicus status <id>` | One-shot status for a session or fan-out wave (human or `--json`; `--wave <id>` alternative spelling). |
420
420
  | `amicus watch <id>` | Live-render a fan-out wave, council run, or session from any terminal until it finishes — in-place table on a TTY, `--plain` milestone lines, or `--json` NDJSON; exit code maps the terminal state. `--ui` opens the [Council Workspace](./docs/council.md#council-workspace-gui) window instead (interactive-only, no `--json`). |
421
421
  | `amicus models` | List, search, refresh the catalog, or audit aliases. |
422
+ | `amicus aliases` | Your model aliases — following / pinned; `--review` walks the update proposals, `--unpin <name>` removes a pin, `--json` for scripts. |
422
423
  | `amicus doctor` | Diagnose your setup — keys, default model, catalog, aliases, OpenCode binary, Electron, skills, MCP registration, OpenRouter credit (`--json`; `--fix` self-heals what it can). |
423
424
  | `amicus spend` | Cross-run cost rollup from the spend ledger, with per-run attribution — total + per-model spend, tokens, and source mix, most-expensive first (`--wave`/`--council`/`--project`/`--model`/`--op`/`--failed` filter it, `--group-by` buckets it, `--since 7d` windows it; `--json` for a versioned doc; shows remaining OpenRouter credit when a key is configured). |
424
425
  | `amicus key` | Manage API keys non-interactively: `amicus key <provider> <key>` saves after live validation; `--remove`; bare `amicus key` lists providers. |
@@ -455,7 +456,7 @@ $ amicus status demo123 --json
455
456
  "taskId": "demo123",
456
457
  "status": "complete",
457
458
  "elapsed": "5m 0s",
458
- "version": "4.9.7",
459
+ "version": "4.10.0",
459
460
  "model": "google/gemini-2.5-flash",
460
461
  "phase": "terminal"
461
462
  }
package/bin/amicus.js CHANGED
@@ -165,6 +165,11 @@ async function main() {
165
165
  exitCode = await handleModels(args);
166
166
  break;
167
167
  }
168
+ case 'aliases': {
169
+ const { handleAliases } = require('../src/sidecar/aliases');
170
+ exitCode = await handleAliases(args);
171
+ break;
172
+ }
168
173
  case 'council': {
169
174
  const { handleCouncil } = require('../src/cli-handlers-council');
170
175
  exitCode = await handleCouncil(args);
package/docs/ROADMAP.md CHANGED
@@ -13,13 +13,14 @@ lives under **Backlog (tracked, not scheduled)** with everything else that is re
13
13
  Nothing about the content changed and no judgment about its value is implied; only its status. When
14
14
  an org buyer and the org to support them exist, it earns a number then.
15
15
 
16
- Amicus is at **v4.9.7** (2026-09-09). Each 4.x rev below leads with the benefit, not the
17
- plumbing; the patch releases since v4.9.0 carry no section of their own, because each corrected a
16
+ Amicus is at **v4.10.0** (2026-09-14). Each 4.x rev below leads with the benefit, not the
17
+ plumbing; the v4.9.x patch releases carry no section of their own, because each corrected a
18
18
  defect rather than adding scope — where one added a surface (v4.9.4's `--thinking` refusals and
19
- `output-budget` doctor row, v4.9.5's Electron digest gate, v4.9.6's artifact custody, v4.9.7's dual name-table rescue boundary) it did so to
20
- make an existing promise true, not to widen it. See `CHANGELOG.md` for what each one contained.
19
+ `output-budget` doctor row, v4.9.5's Electron digest gate, v4.9.6's artifact custody, v4.9.7's dual name-table rescue boundary, v4.9.8's per-run seat tool allowlist and its unverified/refused seat census) it did so to
20
+ make an existing promise true, not to widen it. v4.10.0 adds a surface (`amicus aliases`) and so
21
+ gets a section. See `CHANGELOG.md` for what each one contained.
21
22
 
22
- **Status:** v4.0 through **v4.9.0** have **shipped**, plus the v4.9.1–v4.9.7 patch releases —
23
+ **Status:** v4.0 through **v4.10.0** have **shipped**, plus the v4.9.1–v4.9.8 patch releases —
23
24
  everything on this page is a record of what landed, not a plan. Composition — the scope that
24
25
  carried the number v4.6 here until the degrade-announcement-invariant milestone took the v4.6.0
25
26
  release (2026-08-02) — is now an unscheduled candidate for the next rev, tabled in its own section
@@ -447,6 +448,33 @@ a verdict about a review that never happened.
447
448
  > to do* — announcing losses, counting money, attributing seats. v4.9 changes what a council can be
448
449
  > asked for in the first place, which is a different kind of rev and is scoped as one.
449
450
 
451
+ ## v4.10 — "Your aliases follow the pins" *(follow-or-pin aliases — #238 Phase 1)* — ✅ SHIPPED v4.10.0, 2026-09-14
452
+ **Benefit:** a curated alias tracks the pin amicus ships until you deliberately pin it, and
453
+ `amicus aliases` shows which is which and walks every worthwhile change in a numbered picker —
454
+ no more copy-pasting `--add-alias` lines out of `models --check`.
455
+
456
+ - **★ `amicus aliases`** — every alias as `following` (the shipped pin, moves with releases) or
457
+ `pinned` (yours), grouped by vendor; `--review` proposes one change per pinned alias (a strictly
458
+ newer same-tier sibling, the shipped pin, a same-vendor replacement for an id gone from the
459
+ catalog) and accepts, skips or dismisses it in place; `--json` for scripts; `--unpin <name>`
460
+ removes a pin (a curated name goes back to following, a custom name is deleted, the alias your
461
+ default points at is refused). **Closes the user half of #238** *(L)*
462
+ - **Absence is follow** — a name absent from `config.aliases` resolves to the shipped pin, a
463
+ present key is a pin, and a key equal to the shipped pin is dropped on save with a Notice; the
464
+ setup wizard stops seeding the 21 curated ids and pins only the default you chose, and only when
465
+ its live flagship differs from the shipped one *(M)*
466
+ - **The drift report cannot propose a downgrade** — `models --check`'s fallback-drift line ignores
467
+ non-authoritative catalog rows and is silent when the OpenRouter namespace was rejected *(S)*
468
+ - **The sibling comparator is tier-safe** — lifted out of the CI alias-pin drift gate and shared
469
+ with it; a size or variant token glued to a number (`20b`, `8x22b`, `4o`) is never read as a
470
+ version, so a differently-sized model is never offered as a "newer" sibling *(S)*
471
+ > Why here: the two council rounds on #249 (16 + 13 findings) shaped the release — the typed
472
+ > shipped id follows without a catalog, the comparator's size rule, the `--unpin` default guard
473
+ > and the terminal-escape sanitizer on every alias surface all came out of them. Phases 2–4 of
474
+ > the #238 design (owner mode with `curated-pins.json`, the Electron "Needs review" section, the
475
+ > quiet update notice) are unscheduled; the spec that scopes them ships in the repo under
476
+ > `docs/superpowers/specs/`.
477
+
450
478
  ## Backlog (tracked, not scheduled)
451
479
 
452
480
  ### Enterprise-readiness *(unscheduled — gated on funding / cofounder)*
@@ -38,6 +38,7 @@ src/
38
38
  │ ├── presets-cli.js
39
39
  │ ├── report-cost.js
40
40
  │ ├── report-html.js
41
+ │ ├── report-lost-rows.js
41
42
  │ ├── report-md.js
42
43
  │ ├── report.js
43
44
  │ ├── run-assemble.js
@@ -56,6 +57,8 @@ src/
56
57
  │ ├── run-retry-notes.js
57
58
  │ ├── run-retry-window.js # The Stage-1 retry's no-output window: how long a RELAUNCHED leg may stay
58
59
  │ ├── run-retry.js
60
+ │ ├── run-seat-tools-verify.js # The engine-rendering tripwire's pure pieces, plus `verificationDirectories`'
61
+ │ ├── run-seat-tools.js
59
62
  │ ├── run-server.js
60
63
  │ ├── run-stage1-launch.js # Stage-1 launch pass for the council engine.
61
64
  │ ├── run-stage1-rows.js
@@ -66,6 +69,7 @@ src/
66
69
  │ ├── run-stats-entry.js
67
70
  │ ├── run-verdict-files.js
68
71
  │ ├── run.js
72
+ │ ├── seat-tools.js
69
73
  │ ├── seats.js
70
74
  │ ├── stage1-bind.js
71
75
  │ ├── street-cred.js
@@ -102,6 +106,11 @@ src/
102
106
  ├── prompts/
103
107
  │ └── cowork-agent-prompt.js # Cowork Agent Prompt
104
108
  ├── sidecar/
109
+ │ ├── aliases-review-gate.js # Pure §5-gate helpers for `amicus aliases --review` (#249 r1 R2/R3), split
110
+ │ ├── aliases-review-prompt.js # The real-readline prompt for `amicus aliases --review`, split out of
111
+ │ ├── aliases-review-render.js # Pure, side-effect-free screen text for `amicus aliases --review` (#238 §4),
112
+ │ ├── aliases-review.js # `amicus aliases --review` (#238 §4): a numbered readline picker over the
113
+ │ ├── aliases.js # `amicus aliases` (#238 D4) — the user's alias map as a standing command.
105
114
  │ ├── budget.js
106
115
  │ ├── child-sessions.js
107
116
  │ ├── context-builder.js # Context Builder Module
@@ -135,6 +144,7 @@ src/
135
144
  │ ├── fanout-validate.js
136
145
  │ ├── fanout-wave-io.js
137
146
  │ ├── fanout.js
147
+ │ ├── heartbeat.js
138
148
  │ ├── interactive-abort.js
139
149
  │ ├── interactive-mirror.js
140
150
  │ ├── interactive-process.js # Sidecar Interactive Process Helpers - Electron probe/env/process-exit plumbing
@@ -180,9 +190,13 @@ src/
180
190
  │ ├── activity-poller.js
181
191
  │ ├── agent-mapping.js # Agent Mapping Module
182
192
  │ ├── alias-audit.js # Alias Audit (F5) — report + suggest for most classes; doctor --fix auto-repairs one narrow, mechanically-unambiguous class (B3).
193
+ │ ├── alias-groups.js # Vendor-derived alias grouping (issue 213). Moved VERBATIM out of
194
+ │ ├── alias-proposals.js # The alias review ENGINE (#238 §2): pure, no I/O, no prompts. Turns
183
195
  │ ├── alias-resolver.js # Alias Resolver Utilities
184
196
  │ ├── alias-shadow-writer.js # The alias-shadow notice's WRITE half: say it without ever sinking the run.
185
197
  │ ├── alias-shadow.js # Alias-shadow self-diagnosis — name a local alias that repoints a curated one.
198
+ │ ├── alias-state.js # Following-vs-pinned state for model aliases (#238 D1) and the normalization
199
+ │ ├── alias-store.js # The write sinks the alias review flow needs beyond `setup.js :: addAlias`
186
200
  │ ├── api-key-store.js # API Key Store — reading, saving, and validating API keys.
187
201
  │ ├── api-key-validation.js # API Key Validation — test API keys against provider endpoints.
188
202
  │ ├── atomic-write.js # Atomic file write helper.
@@ -247,6 +261,7 @@ src/
247
261
  │ ├── model-classification.js # Tri-state catalog classification (#61).
248
262
  │ ├── model-descriptor.js # Model-descriptor grammar + RouteResult factories (#61).
249
263
  │ ├── model-fetcher.js # Model Fetcher
264
+ │ ├── model-id-siblings.js # The tier-safe sibling comparator (#238 D7). A sibling shares a pin's vendor
250
265
  │ ├── model-input-default.js
251
266
  │ ├── model-output-limit.js # Issue #218 — the per-model `limit` descriptor amicus hands opencode.
252
267
  │ ├── model-shortlist.js # Vendor model shortlist (#138) -- the family -> model second level.
@@ -308,6 +323,7 @@ src/
308
323
  │ └── seat-space.js # Council Workspace — the seat-space PREDICATES (v4.8 PR5b).
309
324
  ├── cli-council-run-bench.js # Bench and input resolution for the council run command.
310
325
  ├── cli-council-run-render.js
326
+ ├── cli-council-run-tools.js # `--tools`/`--agent` validation and the v4.7 out-dir fence for `council run`.
311
327
  ├── cli-handlers-abort.js # CLI Abort Handler (B21-rest extraction)
312
328
  ├── cli-handlers-council-run.js
313
329
  ├── cli-handlers-council.js
@@ -336,6 +352,7 @@ src/
336
352
  ├── jsonl-parser.js # JSONL Parser
337
353
  ├── mcp-council-awareness.js
338
354
  ├── mcp-council-bench.js
355
+ ├── mcp-council-pack-map.js # COUNCIL_PACK_PARAM_MAP, split out of mcp-council-run.js for the 300-line size gate (P2-R16).
339
356
  ├── mcp-council-run.js
340
357
  ├── mcp-notify.js # Pure helpers + in-process registry for the MCP `onComplete: 'mcp-notify'`
341
358
  ├── mcp-server.js # @module mcp-server — Amicus MCP Server (stdio transport)
@@ -419,6 +436,7 @@ scripts/
419
436
  ├── integration-test.sh
420
437
  ├── mark-test-passed.js # Writes the current git HEAD SHA to .test-passed for the pre-push SHA cache
421
438
  ├── postinstall.js # Post-install script for amicus
439
+ ├── probe-council-agents.js # Spec 2026-09-11 §4 — the ENGINE side of the council agents (ruling P2-R21).
422
440
  ├── probe-darwin-extract.js
423
441
  ├── probe-max-tokens.js # Wire probe for issue #218: what max_tokens / reasoning / thinking does the
424
442
  ├── run-integration-keyless.js
@@ -452,6 +470,7 @@ evals/
452
470
  |--------|---------|-------------|
453
471
  | `cli-council-run-bench.js` | Bench and input resolution for the council run command. | `resolveBench()`, `resolveChair()`, `resolveCritic()`, `CHAIR_DEFAULT()`, `parseList()` |
454
472
  | `cli-council-run-render.js` | | `renderRunHuman()` |
473
+ | `cli-council-run-tools.js` | `--tools`/`--agent` validation and the v4.7 out-dir fence for `council run`. | `checkCouncilRunTools()` |
455
474
  | `cli-handlers-abort.js` | CLI Abort Handler (B21-rest extraction) | `handleAbort()` |
456
475
  | `cli-handlers-council-run.js` | | `handleCouncilRun()`, `renderRunHuman()`, `CHAIR_DEFAULT()` |
457
476
  | `cli-handlers-council.js` | | `handleCouncil()` |
@@ -480,6 +499,7 @@ evals/
480
499
  | `jsonl-parser.js` | JSONL Parser | `parseJSONLLine()`, `readJSONL()`, `extractTimestamp()`, `formatMessage()`, `formatContext()` |
481
500
  | `mcp-council-awareness.js` | | `subWaveIds()`, `countWaveLegs()`, `elapsedOf()`, `enginePid()`, `buildCouncilStatusPayload()` |
482
501
  | `mcp-council-bench.js` | | `resolveBenchInput()`, `auditBenchAliases()` |
502
+ | `mcp-council-pack-map.js` | COUNCIL_PACK_PARAM_MAP, split out of mcp-council-run.js for the 300-line size gate (P2-R16). | `COUNCIL_PACK_PARAM_MAP()` |
483
503
  | `mcp-council-run.js` | | `handleCouncilRunTool()`, `COUNCIL_PACK_PARAM_MAP()`, `buildCouncilStatusPayload()`, `listCouncilRuns()`, `abortCouncilRun()` |
484
504
  | `mcp-notify.js` | Pure helpers + in-process registry for the MCP `onComplete: 'mcp-notify'` | `validateOnComplete()`, `buildNotifyPayload()`, `requestMcpNotify()`, `consumeMcpNotify()` |
485
505
  | `mcp-server.js` | @module mcp-server — Amicus MCP Server (stdio transport) | `handlers()`, `startMcpServer()`, `getProjectDir()`, `resolveProjectDir()`, `getClientRoot()` |
@@ -494,7 +514,7 @@ evals/
494
514
  | `spend-query.js` | | `filterRows()`, `groupRows()`, `computeWasted()`, `emptyTokens()`, `addTokens()` |
495
515
  | `council/anonymize.js` | | `assignLabels()`, `toGlobalId()`, `toGlobalFindings()`, `rankingToOrder()`, `LETTERS()` |
496
516
  | `council/briefings-chair-task.js` | | `CHAIR_ANSWER_VALUES()`, `ANSWER_SCALE_ADDENDUM()`, `TASK_CHAIR_SYNTHESIS()`, `TASK_CHAIR_SYNTHESIS_NO_CLAIMS()`, `TASK_CONCURRENCE_CAVEAT()` |
497
- | `council/briefings-chair.js` | | `dateLine()`, `CHAIR_NO_TOOLS_PREAMBLE()`, `chairRepairPromptFor()`, `CHAIR_VERDICT_VALUES()`, `VERDICT_SCALE_ADDENDUM()` |
517
+ | `council/briefings-chair.js` | | `dateLine()`, `CHAIR_NO_TOOLS_LEAD()`, `CHAIR_NO_TOOLS_PREAMBLE()`, `chairRepairPromptFor()`, `CHAIR_VERDICT_VALUES()` |
498
518
  | `council/briefings-debate.js` | | `DEBATE_NO_TOOLS_PREAMBLE()`, `DEFENSE_CONTRACT()`, `REVOTE_CONTRACT()`, `buildDefenseBrief()`, `buildRevoteBundle()` |
499
519
  | `council/briefings-stage2-task.js` | | `TASK_JUDGE_FRAME()`, `TASK_JUDGE_A()`, `TASK_JUDGE_B()`, `TASK_JUDGE_B_NO_CLAIMS()`, `NO_CLAIMS_INDEX()` |
500
520
  | `council/briefings-stage2.js` | | `JUDGE_NO_TOOLS_PREAMBLE()`, `CHAIR_NO_TOOLS_PREAMBLE()`, `CHAIR_VERDICT_VALUES()`, `JUDGE_OUTPUT_CONTRACT()`, `VERDICT_SCALE_ADDENDUM()` |
@@ -511,6 +531,7 @@ evals/
511
531
  | `council/presets-cli.js` | | `runSave()`, `runList()`, `runShow()` |
512
532
  | `council/report-cost.js` | | `buildCostModel()` |
513
533
  | `council/report-html.js` | | `renderHtml()` |
534
+ | `council/report-lost-rows.js` | | `lostRowsOf()` |
514
535
  | `council/report-md.js` | | `renderMd()` |
515
536
  | `council/report.js` | | `buildReport()`, `toModel()`, `TIER_ORDER()`, `SYMBOL()`, `isSeatSpace()` |
516
537
  | `council/run-assemble.js` | | `buildRunStatsEntry()`, `worseConformance()`, `buildTallyInput()`, `writeTallyFiles()`, `writeVerdictFiles()` |
@@ -519,7 +540,7 @@ evals/
519
540
  | `council/run-debate-revote.js` | | `legOpts()`, `legRow()`, `runRevoteWave()` |
520
541
  | `council/run-debate-stage.js` | | `runDebateStage()` |
521
542
  | `council/run-debate.js` | | `runDebate()`, `nothingToDebate()`, `disputingJudges()`, `debateTargets()` |
522
- | `council/run-degrade.js` | | `createDegradeSink()` |
543
+ | `council/run-degrade.js` | | `createDegradeSink()`, `noteDroppedMembers()` |
523
544
  | `council/run-finalize.js` | | `statusForExit()`, `resolveTerminalExit()`, `writeRunTerminal()`, `SIGNAL_EXIT()` |
524
545
  | `council/run-finish.js` | | `finishRun()` |
525
546
  | `council/run-launch.js` | | `createLaunchers()`, `materializeReviews()`, `materializeDebate()`, `sanitizeName()`, `isAbortExit()` |
@@ -529,7 +550,9 @@ evals/
529
550
  | `council/run-retry-notes.js` | | `waveStillDeadNote()`, `skippedWaveNote()`, `srcLegStillDeadNote()`, `retryLegStillDeadNote()`, `missingLegStillDeadNote()` |
530
551
  | `council/run-retry-window.js` | The Stage-1 retry's no-output window: how long a RELAUNCHED leg may stay | `retryBackstopMs()` |
531
552
  | `council/run-retry.js` | | `groupStage1Losses()`, `retryStage1Losses()` |
532
- | `council/run-server.js` | | `acquireRunServer()`, `releaseRunServer()`, `resolveRunServerModels()`, `recordServerFate()` |
553
+ | `council/run-seat-tools-verify.js` | The engine-rendering tripwire's pure pieces, plus `verificationDirectories`' | `verificationDirectories()`, `listEngineAgents()`, `verifyAgentRendering()`, `verifyAgentFields()`, `resolvePhysicalPath()` |
554
+ | `council/run-seat-tools.js` | | `preflightSeatTools()`, `validateSeatToolsAgainstEngine()`, `listEngineAgents()`, `verifyAgentRendering()`, `verifyAgentFields()` |
555
+ | `council/run-server.js` | | `acquireRunServer()`, `releaseRunServer()`, `resolveRunServerModels()`, `recordServerFate()`, `listEngineToolIds()` |
533
556
  | `council/run-stage1-launch.js` | Stage-1 launch pass for the council engine. | `launchStage1()` |
534
557
  | `council/run-stage1-rows.js` | | `pushDeadSeatRows()`, `supersededRows()` |
535
558
  | `council/run-stage1-superseded.js` | | `supersededRows()` |
@@ -539,12 +562,13 @@ evals/
539
562
  | `council/run-stats-entry.js` | | `buildRunStatsEntry()` |
540
563
  | `council/run-verdict-files.js` | | `writeVerdictFiles()` |
541
564
  | `council/run.js` | | `runCouncil()`, `pickFallbackChair()`, `SIGNAL_EXIT()` |
565
+ | `council/seat-tools.js` | | `REFUSED_TOOL_IDS()`, `REMOTE_TOOL_IDS()`, `NON_LOCAL_TOOL_IDS()`, `SEAT_READ_DENY_PATTERNS()`, `defaultToolsFor()` |
542
566
  | `council/seats.js` | | `buildSeats()`, `roleAt()`, `bindSeats()`, `artifactName()`, `displayName()` |
543
567
  | `council/stage1-bind.js` | | `bindStage1Waves()`, `orphanLegNote()`, `missingSeatDeadWave()`, `bindPaddedWave()` |
544
568
  | `council/street-cred.js` | | `computeStreetCred()`, `rankPositions()`, `credSeats()` |
545
569
  | `council/tally.js` | | `assignTier()`, `computeStreetCred()`, `tally()`, `COUNCIL_SCHEMA_VERSION()` |
546
570
  | `council/verdict-seat-loss.js` | | `summarizeSeatLoss()`, `deriveSeatLoss()` |
547
- | `council/verdict-seats-reviewed.js` | #202: the bench-seat census for verdict.json, as a spreadable fragment. | `seatsReviewedOf()` |
571
+ | `council/verdict-seats-reviewed.js` | #202: the bench-seat census for verdict.json, as a spreadable fragment. | `seatsReviewedOf()`, `isBenchRole()`, `isUnverifiedSeat()`, `isRefusedSeat()`, `seatLabel()` |
548
572
  | `council/verdict.js` | | `buildVerdict()`, `summarizeSeatLoss()`, `deriveSeatLoss()`, `readOverallVerdict()`, `readPriorVerdictSurfaces()` |
549
573
  | `design/tokens.js` | | `tokenCss()`, `TOKENS()` |
550
574
  | `observe/council-legs.js` | | `buildLegRows()` |
@@ -559,6 +583,11 @@ evals/
559
583
  | `pack/pack-store.js` | | `packsDir()`, `canonicalHash()`, `resolvePackRef()`, `readPack()`, `writePack()` |
560
584
  | `pack/pack-validate.js` | | `validatePack()`, `KIND_OPTIONS()`, `KINDS()` |
561
585
  | `prompts/cowork-agent-prompt.js` | Cowork Agent Prompt | `buildCoworkAgentPrompt()` |
586
+ | `sidecar/aliases-review-gate.js` | Pure §5-gate helpers for `amicus aliases --review` (#249 r1 R2/R3), split | `classifyTypedId()`, `notInCatalogLine()`, `notVerifiedLine()`, `staleCatalogBanner()` |
587
+ | `sidecar/aliases-review-prompt.js` | The real-readline prompt for `amicus aliases --review`, split out of | `createPrompt()` |
588
+ | `sidecar/aliases-review-render.js` | Pure, side-effect-free screen text for `amicus aliases --review` (#238 §4), | `ageLabel()`, `menuFor()`, `menuLineText()`, `renderScreen()`, `refreshingCatalogLine()` |
589
+ | `sidecar/aliases-review.js` | `amicus aliases --review` (#238 §4): a numbered readline picker over the | `runReview()` |
590
+ | `sidecar/aliases.js` | `amicus aliases` (#238 D4) — the user's alias map as a standing command. | `handleAliases()`, `collectAliasView()`, `renderAliasList()`, `buildAliasesDoc()`, `loadDeps()` |
562
591
  | `sidecar/budget.js` | | `checkBudget()`, `formatBudgetError()`, `DEFAULT_MAX_COST_PER_MTOK()`, `ASSUMED_OUTPUT_TOKENS()` |
563
592
  | `sidecar/child-sessions.js` | | `collectSubtreeUsage()`, `subtreeIsUnknown()`, `SUBTREE_MAX_DEPTH()`, `SUBTREE_MAX_SESSIONS()` |
564
593
  | `sidecar/context-builder.js` | Context Builder Module | `buildContext()`, `parseDuration()`, `resolveSessionFile()`, `applyContextFilters()`, `findCoworkSession()` |
@@ -592,6 +621,7 @@ evals/
592
621
  | `sidecar/fanout-validate.js` | | `parseModelsList()`, `DEFAULT_MAX_LEGS()`, `validateFanoutModels()` |
593
622
  | `sidecar/fanout-wave-io.js` | | `writeWaveMetadata()`, `writeWaveDoc()`, `finishWave()`, `stampLegAttribution()` |
594
623
  | `sidecar/fanout.js` | | `parseModelsList()`, `deriveLegIds()`, `validateFanoutModels()`, `DEFAULT_MAX_LEGS()`, `runFanout()` |
624
+ | `sidecar/heartbeat.js` | | `HEARTBEAT_INTERVAL()`, `createHeartbeat()` |
595
625
  | `sidecar/interactive-abort.js` | | `startAbortWatch()`, `markResultAborted()`, `readAbortedMarker()`, `DEFAULT_INTERVAL_MS()` |
596
626
  | `sidecar/interactive-mirror.js` | | `startInteractiveMirror()` |
597
627
  | `sidecar/interactive-process.js` | Sidecar Interactive Process Helpers - Electron probe/env/process-exit plumbing | `getElectronPath()`, `checkElectronAvailable()`, `buildElectronEnv()`, `handleElectronProcess()` |
@@ -635,9 +665,13 @@ evals/
635
665
  | `utils/activity-poller.js` | | `createActivityPoller()`, `killIfAlive()` |
636
666
  | `utils/agent-mapping.js` | Agent Mapping Module | `PRIMARY_AGENTS()`, `OPENCODE_AGENTS()`, `HEADLESS_SAFE_AGENTS()`, `mapAgentToOpenCode()`, `isValidAgent()` |
637
667
  | `utils/alias-audit.js` | Alias Audit (F5) — report + suggest for most classes; doctor --fix auto-repairs one narrow, mechanically-unambiguous class (B3). | `collectAliasSources()`, `findStaleAliases()`, `findDriftedStoredAliases()`, `suggestReplacements()`, `findFabricatedAliasRepairs()` |
668
+ | `utils/alias-groups.js` | Vendor-derived alias grouping (issue 213). Moved VERBATIM out of | `groupAliases()`, `aliasVendorOf()`, `vendorLabel()`, `titleCaseVendor()`, `PREFERRED_VENDOR_ORDER()` |
669
+ | `utils/alias-proposals.js` | The alias review ENGINE (#238 §2): pure, no I/O, no prompts. Turns | `buildAliasProposals()`, `gatedCatalogIds()` |
638
670
  | `utils/alias-resolver.js` | Alias Resolver Utilities | `autoRepairAlias()` |
639
671
  | `utils/alias-shadow-writer.js` | The alias-shadow notice's WRITE half: say it without ever sinking the run. | `safeWrite()`, `armStream()`, `writeNoticeToStderr()` |
640
672
  | `utils/alias-shadow.js` | Alias-shadow self-diagnosis — name a local alias that repoints a curated one. | `findAliasShadows()`, `formatAliasShadow()`, `noteAliasShadows()`, `auditAliasShadows()` |
673
+ | `utils/alias-state.js` | Following-vs-pinned state for model aliases (#238 D1) and the normalization | `normalizeAliases()`, `listAliasRows()`, `isCurated()` |
674
+ | `utils/alias-store.js` | The write sinks the alias review flow needs beyond `setup.js :: addAlias` | `removeAlias()`, `readDismissals()`, `recordDismissal()` |
641
675
  | `utils/api-key-store.js` | API Key Store — reading, saving, and validating API keys. | `getEnvPath()`, `loadEnvEntries()`, `readApiKeys()`, `readApiKeyHints()`, `readApiKeyValues()` |
642
676
  | `utils/api-key-validation.js` | API Key Validation — test API keys against provider endpoints. | `validateApiKey()`, `redactSecret()`, `validateOpenRouterKey()`, `checkOpenRouterCredit()`, `OPENROUTER_NO_CREDIT_WARNING()` |
643
677
  | `utils/atomic-write.js` | Atomic file write helper. | `writeFileAtomic()` |
@@ -702,6 +736,7 @@ evals/
702
736
  | `utils/model-classification.js` | Tri-state catalog classification (#61). | `classifyModel()` |
703
737
  | `utils/model-descriptor.js` | Model-descriptor grammar + RouteResult factories (#61). | `GATEWAY_MODES()`, `parseDescriptor()`, `resolved()`, `selectionRequired()`, `routeError()` |
704
738
  | `utils/model-fetcher.js` | Model Fetcher | `fetchModelsFromProvider()`, `fetchAllModels()`, `fetchAllModelsDetailed()`, `fetchModelsFromProviderDetailed()`, `providersToFetch()` |
739
+ | `utils/model-id-siblings.js` | The tier-safe sibling comparator (#238 D7). A sibling shares a pin's vendor | `parsePin()`, `compareVersions()`, `newestSibling()` |
705
740
  | `utils/model-input-default.js` | | `resolveModelInputOrDefault()` |
706
741
  | `utils/model-output-limit.js` | Issue #218 — the per-model `limit` descriptor amicus hands opencode. | `normalizeOutputBudget()`, `buildLimitLookup()`, `computeModelLimit()`, `positiveCount()` |
707
742
  | `utils/model-shortlist.js` | Vendor model shortlist (#138) -- the family -> model second level. | `buildModelShortlist()`, `compareShortlistRows()`, `SHORTLIST_LIMIT()` |
@@ -721,7 +756,7 @@ evals/
721
756
  | `utils/provider-default-picker.js` | Provider-default picker core (Part 2, Task 4). | `buildProviderDefaultChoices()`, `applyProviderDefault()`, `pricePerMInputFrom()`, `directFormIfSafe()`, `directFormIfProven()` |
722
757
  | `utils/provider-default-prompt.js` | Provider-default prompt flow (Part 2, Task 6/7 shared helper). | `runProviderDefaultFlow()`, `formatPrice()`, `formatRow()` |
723
758
  | `utils/provider-registry.js` | Provider-capability registry — the single source of truth for provider | `PROVIDERS()`, `getProvider()`, `isDirectProvider()`, `listDirectProviders()`, `PROVIDER_ENV_MAP()` |
724
- | `utils/quick-picks.js` | Quick-pick resolution (wizard Step 2) — resolves each curated family to | `compareIdsDesc()`, `canonicalRoutesFor()`, `pickCurrent()`, `resolveQuickPicks()`, `toLiveSeedAliases()` |
759
+ | `utils/quick-picks.js` | Quick-pick resolution (wizard Step 2) — resolves each curated family to | `compareIdsDesc()`, `canonicalRoutesFor()`, `pickCurrent()`, `resolveQuickPicks()`, `toStorableRoute()` |
725
760
  | `utils/read-slice.js` | Byte-bounded slicing for amicus_read (15a.3 / B17). | `sliceForRead()`, `READ_CAP_BYTES()` |
726
761
  | `utils/remediation-hints.js` | | |
727
762
  | `utils/result-schema-rebuild.js` | | `buildRunResultFromSession()`, `buildWaveResultFromSession()` |
@@ -742,7 +777,7 @@ evals/
742
777
  | `utils/shared-server.js` | | `SharedServerManager()` |
743
778
  | `utils/spend-ledger.js` | | `appendSpend()`, `readSpendRows()`, `SPEND_LEDGER_FILE()`, `SPEND_LEDGER_SCHEMA_VERSION()` |
744
779
  | `utils/start-helpers.js` | Start Command Helpers | `resolveLaunchModel()`, `deriveAlias()`, `maybeOfferProviderDefaults()` |
745
- | `utils/text-sanitize.js` | One third-party string, safe to render: no escapes, no bidi, one short line. | `collapseExcerpt()`, `MAX_EXCERPT_CHARS()` |
780
+ | `utils/text-sanitize.js` | One third-party string, safe to render: no escapes, no bidi, one short line. | `collapseExcerpt()`, `MAX_EXCERPT_CHARS()`, `safeFragment()`, `MAX_FRAGMENT_CHARS()` |
746
781
  | `utils/thinking-validators.js` | Thinking Level Validators | `VARIANT_LEVELS()`, `validateThinkingLevel()` |
747
782
  | `utils/ttft.js` | The one honesty predicate for the time-to-first-token probe (v4.9 W13). | `isMeasuredTtft()` |
748
783
  | `utils/untrusted-fence.js` | Untrusted sidecar output fence. | `fenceSidecarOutput()`, `defangOutboundFenceTags()`, `OUTBOUND_FENCE_TAGS()` |
@@ -540,7 +540,7 @@ amicus models --check && echo "aliases ok"
540
540
 
541
541
  ## Model Aliases
542
542
 
543
- Aliases are short names that resolve to full provider-prefixed model IDs. `amicus setup` seeds a curated default set (e.g. `gemini`, `gpt`, `opus`, `deepseek`) to the bare canonical form for direct-capable vendors. You add or override aliases with:
543
+ Aliases are short names that resolve to full provider-prefixed model IDs. Amicus ships a curated set (`gemini`, `gpt`, `opus`, `deepseek`, `claude`, `glm`, …) that resolves to the pins the package ships — an alias you have not pinned FOLLOWS those pins and moves with each release. `amicus setup` no longer copies them into your config; it pins only the default alias you chose, and only when its live flagship differs from the shipped pin. See `amicus aliases` for what resolves on your machine. You add or override aliases with:
544
544
 
545
545
  ```bash
546
546
  amicus setup --add-alias fast=google/gemini-3.1-flash-lite-preview
@@ -671,14 +671,14 @@ level includes everything above it.
671
671
  // Default alias, resolved via `aliases` below when --model is omitted.
672
672
  "default": "gemini",
673
673
 
674
- // Short name -> full model id. Bare `provider/model` (canonical) routes direct-first;
675
- // `amicus setup` seeds direct-capable vendors this way automatically.
676
- // `amicus setup --add-alias name=provider/model` adds more.
674
+ // Short name -> full model id, for the aliases YOU pinned. A curated alias
675
+ // (gemini, gpt, opus, …) that is ABSENT here FOLLOWS the pin amicus ships and
676
+ // moves with each release; a present key is pinned and reviewable with
677
+ // `amicus aliases --review`. A key equal to the shipped pin is dropped on save
678
+ // (with a Notice) — it is the same as following.
677
679
  "aliases": {
678
- "gemini": "google/gemini-3-pro-preview",
679
- "gpt": "openai/gpt-5",
680
- "opus": "anthropic/claude-opus-4",
681
- "deepseek": "deepseek/deepseek-v3"
680
+ "glm": "openrouter/z-ai/glm-5.4",
681
+ "fast": "google/gemini-3.6-flash-lite"
682
682
  },
683
683
 
684
684
  // Named council member lists, e.g. seeded by the Free OpenRouter council
@@ -700,6 +700,12 @@ level includes everything above it.
700
700
  "tier_onboarded": true
701
701
  },
702
702
 
703
+ // Written by `amicus aliases --review`'s "never ask again" — keyed
704
+ // `alias@proposedId`; hand-delete a key here to be asked again.
705
+ "aliasReview": {
706
+ "dismissed": { "glm@openrouter/z-ai/glm-5.4": "2026-09-14T00:00:00.000Z" }
707
+ },
708
+
703
709
  // User-defined local / OpenAI-compatible providers (v4.2) — written by
704
710
  // `amicus provider add`, or hand-edited. id -> normalized entry; `pricing`
705
711
  // defaults to {prompt: 0, completion: 0} (the $0 tier) when omitted.