@lmctl-ai/lmctl 0.2.19 → 0.2.21

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.
@@ -1,381 +0,0 @@
1
- # Model Selection
2
-
3
- `model=` on a `_MEMBER_` line is a provider-specific model request.
4
-
5
- For `.lmctl` teamfile compatibility mode:
6
-
7
- - `lmctl lint <teamfile>` resolves `model=` for `claude`, `gemini`, `qwen`,
8
- and `hermes` against the lmprice model catalog (see
9
- `## lmprice Model Catalog` below) and warns when the model is not found.
10
- This REPLACED the static tested-model lists that used to live in this
11
- document (2026-09-24) — hand-copied lists contradicted the catalog.
12
-
13
- **Best-effort contract (operator, 2026-09-25, hardened 2026-09-29): lint is
14
- advisory and is NOT claimed to be 100% up to date.** Checks can lag provider
15
- reality in BOTH directions — a model lint warns about may have just been
16
- added upstream, and a model lint accepts may have just been retired. Every
17
- model check is a warning, never an error, full stop: lint has exactly two
18
- outcomes (ok / warning) and always exits 0 on warnings-only. There used to be
19
- a `--strict` flag that could promote warnings to a failing exit code; the
20
- operator removed it entirely (2026-09-29: "no need to give users the choice
21
- with no obvious value") — lmctl decides the scoping, not the caller. Exit 1
22
- is reserved for genuine validation errors (a malformed teamfile, an
23
- unparseable member line), never for a warning. The runtime stance for
24
- anything unrecognized or out of date is always: "not recognized — could
25
- still be valid; no cost information," never a failure. Cost is equally
26
- best-effort (n/a with a reason, never a guessed price).
27
- - `codex` and `agy` model validation queries each provider's own live
28
- catalog command (`codex debug models`, `agy models`) instead of a
29
- hand-copied list — see their `## Codex` / `## Antigravity` sections
30
- below.
31
- - `opencode` model validation reads configured models from
32
- `~/.config/opencode/opencode.json` and WARNS (never injects) when a
33
- `model=` isn't declared there and isn't a config-free github-copilot
34
- GPT/Gemini/o-series id. When `model=` is present, `lmctl lint` also
35
- opens a short-lived ACP session and asks opencode itself to validate
36
- `model=` and `effort=`. It never sends a prompt. See the `## OpenCode`
37
- section below.
38
- - `kimi` model validation reads configured aliases from
39
- `$KIMI_CODE_HOME/config.toml` (default `~/.kimi-code/config.toml`) and
40
- WARNS (never injects) when `model=` isn't one of the declared
41
- `[models.<alias>]` table names, or when the config file itself is
42
- missing/unreadable. Same rationale as opencode: kimi model aliases are
43
- user-defined, not a fixed vendor catalog, so a hardcoded list in this
44
- doc would be wrong for every user whose aliases differ. See the
45
- `## Kimi` section below.
46
- - `hermes` model ids are the ENCODED `<provider>:<model>` form Hermes's
47
- ACP server advertises (`acp_adapter/model_catalog.py`). lmctl sends the
48
- value through ACP `session/set_model` and does NOT maintain a Hermes
49
- model list to validate against: Hermes resolves models at runtime from
50
- its own provider config + auth, so any hardcoded list here would be
51
- wrong for most users. Lint warns only when `model=` has no `provider:`
52
- prefix. Hermes only treats a leading `<x>:` as a provider delimiter when
53
- `x` is a known provider name (`hermes_cli/models.py:parse_model_input`);
54
- a bare id stays the model name and Hermes then has to infer the provider
55
- by detection, which fails for models it cannot place. After the shape
56
- check, lint ALSO resolves the id against the lmprice catalog (the hermes
57
- adapter splits the `provider:` prefix) and warns on a catalog miss —
58
- warning-only, since Hermes may still resolve it from user config. See the
59
- `## Hermes` section below.
60
- - `copilot` accepts `model=` syntactically, but model selection is not
61
- supported and lint warns that the value is ignored.
62
- - `lmctl seed <teamfile>` passes `model=` through to the provider seed
63
- call. It does not contact provider model catalogs.
64
-
65
- The `claude`/`gemini`/`qwen` tables in the sections below are HISTORICAL
66
- evidence records of the retired tested-model lists (pre-2026-09-24); lint no
67
- longer consults them — it resolves against the lmprice catalog.
68
-
69
- ## lmprice Model Catalog
70
-
71
- Since 2026-09-24 (0.2.1), model validation and costing share one catalog
72
- layer fed by the public lmprice dataset
73
- (`https://lmctl.com/dataset/model_list.json` + `aliases.json`):
74
-
75
- - **Loading**: ETag-conditional refresh into `~/.lmctl/cache/lmprice/` with a
76
- 6h TTL (`LMCTL_PRICE_TTL=0` forces; `LMCTL_PRICE_DATA=<dir>` pins a
77
- snapshot and disables network); bundled snapshot from build time is the
78
- offline fallback; the loader never throws.
79
- - **Resolution order** (provider-aware): provider-qualified alias hit
80
- (evidence `established`/`asserted` → standards chain → pinned row;
81
- `unresolved` is terminal, never guessed) → provider adapter family match →
82
- miss. States: priced / estimated / known-unpriced / unresolved / unknown /
83
- no-catalog.
84
- - **Invariant (operator, 2026-09-25): if we can lint, then we can cost.**
85
- lmprice build validation rejects half-bindings (asserted alias ending in a
86
- null record pin); lmctl surfaces any legacy known-unpriced as the lint
87
- warning "recognized but no price information."
88
- - **Freshness**: the catalog is only as current as the last lmprice publish
89
- plus the client TTL — see the best-effort contract at the top of this
90
- document. `cost --debug` shows the dataset's `generated_at`.
91
-
92
- ## Claude
93
-
94
- | Configured `model=` | Evidence |
95
- | --- | --- |
96
- | `claude-opus-4-6` | Ported from lmctl0 tested-model catalog. |
97
- | `claude-opus-4-7` | Ported from lmctl0 tested-model catalog. |
98
- | `claude-opus-4-8` | Added from operator-confirmed availability on 2026-06-13; `lmctl lint` compatibility smoke accepts it. |
99
- | `claude-opus-5` | Operator-confirmed valid 2026-07-25; next-gen Opus release following the `-5` naming (cf. `claude-sonnet-5`/`claude-fable-5`, dropping the `-4-` infix). Added to the lint tested-model catalog. |
100
- | `claude-sonnet-4-6` | Ported from lmctl0 tested-model catalog. |
101
- | `claude-sonnet-5` | Operator-confirmed 2026-07-05 (via GitHub Copilot: `opencode --model github-copilot/claude-sonnet-5 run …` → "build · claude-sonnet-5"). Also a valid native Claude Code model id (`claude --model claude-sonnet-5`). |
102
- | `claude-haiku-4-5` | Ported from lmctl0 tested-model catalog. |
103
- | `claude-fable-5` | Session observed locally via `lmctl0 ls` on 2026-06-11; `lmctl lint` compatibility smoke accepts it. Anthropic announced on 2026-06-12 that access to Fable 5 was suspended for customers; keep the id valid for existing teamfiles, but do not use it for new work while unavailable. |
104
- | `claude-fable-5-1` | Fable 5.1, math.lmctl's designated review model as of 2026-09-02; requested addition to the lint tested-model catalog to silence the "not in the tested model list" warning on seed. |
105
-
106
- ## Codex
107
-
108
- **2026-09-15: this section is no longer read by `lmctl lint`.** It used to
109
- be a hand-copied list that went stale in BOTH directions within the same
110
- week: it kept `gpt-5.4-mini` listed as valid after OpenAI withdrew it from
111
- the subscription (194 codex members with no `model=` pinned were silently
112
- falling through to it as the ambient default), and separately omitted
113
- `gpt-5.6-terra`/`gpt-5.6-luna` after an operator quota downshift moved 260
114
- members onto them — both real, working models (`lmctl prompt` confirmed
115
- live), producing 45 fleet-wide false-positive warnings. `codex debug
116
- models` ("Render the raw model catalog as JSON") shipped since the first
117
- incident and gives codex the same live-catalog treatment `agy` already
118
- has — lint now queries it directly (`model-selection-lint.ts`'s
119
- `loadCodexModels`), filtering to entries with `"visibility": "list"` (the
120
- catalog's own "offered to users" flag — `gpt-reserve`/`codex-auto-review`
121
- come back `"hide"` and are excluded the same way a UI picker would skip
122
- them). Left as historical reference only, not maintained going forward.
123
-
124
- | Configured `model=` | Evidence |
125
- | --- | --- |
126
- | `gpt-5.5` | Observed from `codex debug models` on 2026-09-15. |
127
- | `gpt-5.6-sol` | Observed from `codex debug models` on 2026-09-15. |
128
- | `gpt-5.6-terra` | Observed from `codex debug models` on 2026-09-15. |
129
- | `gpt-5.6-luna` | Observed from `codex debug models` on 2026-09-15. |
130
- | `gpt-6-astra` | Observed from `codex debug models` on 2026-09-15. |
131
-
132
- **Banned as a build model (operator, 2026-09-06):** `gpt-5.3-codex-spark`
133
- — plenty of regular-GPT quota headroom now, so there's no cost reason left
134
- to reach for the cheap/fast classification model on build work. `lmctl
135
- lint` still warns on explicit `model=gpt-5.3-codex-spark` for this reason
136
- regardless of live-catalog membership (it has since also dropped out of
137
- the live catalog entirely).
138
-
139
- **Retired:** `gpt-5.4-mini` -- removed from the OpenAI subscription
140
- 2026-09-15 while 194 codex members across ~90 teamfiles had no `model=`
141
- pinned and were falling through to it as the ambient default. Unlike
142
- `claude-fable-5` above (suspended, may return, kept valid), this id was
143
- withdrawn outright and is not expected back.
144
-
145
- ## Gemini
146
-
147
- **2026-09-24: lint no longer reads a tested-model table here** — gemini
148
- models resolve against the lmprice catalog (chat rows only; embedding /
149
- image / tts / realtime rows are excluded by the adapter). Note the
150
- `gemini` provider requires an API/enterprise Google account; personal
151
- subscriptions should use `agy`.
152
-
153
- ## Qwen
154
-
155
- **2026-09-24: lint no longer reads a tested-model table here** — qwen
156
- models resolve against the lmprice catalog (bare, `dashscope/*`, and
157
- `qwen/` rows). `qwen/qwen3.5-plus` is an established identity whose
158
- current catalog price is null (known-unpriced — not a warning);
159
- `qwen/qwen3.6-plus` is deliberately unresolved in the alias table.
160
-
161
- ## Antigravity
162
-
163
- Antigravity (`agy`) is a distinct provider from Gemini even though it stores
164
- state under `~/.gemini/antigravity-cli`. `lmctl` supports it through native
165
- terminal resume plus one-shot `agy -p` turns.
166
-
167
- **2026-09-05: this section is no longer read by `lmctl lint`.** It used to
168
- be a hand-copied snapshot of `agy models`' output (literally captioned
169
- "Observed from `agy models` on <date>" below) — which went stale on every
170
- new model release (fable-5.1 once, then Gemini 3.6/3.7/3.8 all landing the
171
- same day). `agy` is the one provider with its own live model-catalog
172
- command (`agy models`, tab-separated `<id>\t<Display Name>` lines), so lint
173
- now queries it directly (`model-selection-lint.ts`'s `loadAgyModels`)
174
- instead of maintaining a copy of it here. Left as historical reference only,
175
- not maintained going forward.
176
-
177
- | Configured `model=` | Evidence |
178
- | --- | --- |
179
- | `Gemini 3.5 Flash (Medium)` | Observed from `agy models` on 2026-07-20. |
180
- | `Gemini 3.5 Flash (High)` | Observed from `agy models` on 2026-07-20. |
181
- | `Gemini 3.5 Flash (Low)` | Observed from `agy models` on 2026-07-20. |
182
- | `Gemini 3.1 Pro (Low)` | Observed from `agy models` on 2026-07-20. |
183
- | `Gemini 3.1 Pro (High)` | Observed from `agy models` on 2026-07-20. |
184
- | `Claude Sonnet 4.6 (Thinking)` | Observed from `agy models` on 2026-07-20. |
185
- | `Claude Opus 4.6 (Thinking)` | Observed from `agy models` on 2026-07-20. |
186
- | `GPT-OSS 120B (Medium)` | Observed from `agy models` on 2026-07-20. |
187
-
188
- **Pricing (2026-09-29, operator decision, math.lmctl findings 002/003):** separate from the lint
189
- question above ("is this a real agy model," answered live) is "does this resolve in the lmprice
190
- catalog for cost purposes" — the fleet's largest unpriced population (86 members, 4 base models,
191
- 11 effort-tier declared strings). Root cause: agy exposes Google's Gemini models under its own
192
- display-label strings with a reasoning-effort tier baked into the name (`"Gemini 3.8 Flash (High)"`),
193
- and no upstream catalog source (LiteLLM or OpenRouter) will ever carry that exact string —
194
- normalizing punctuation can't fix a naming SYSTEM difference, only an explicit, evidenced row can
195
- (see `backlog/`'s finding-002/003 threads for the full reasoning).
196
-
197
- **Two layers, discovered while fixing this, not designed up front:** lmprice's own `aliases.json`
198
- already independently covers 4 of the 11 strings (established evidence, sourced from
199
- antigravity.google/docs/models, added 2026-09-24 — predates this fix): `gemini 3.8 flash (high)`,
200
- `gemini 3.8 flash (medium)`, `gemini 3.1 pro (high)`. Those resolve via lmctl's existing alias-table
201
- step and never touch new code. `src/providers/model-catalog.ts`'s `agyAdapter`/
202
- `AGY_DISPLAY_TO_IDENTITY` (added here) covers the remaining 7 lmprice hasn't published yet — all of
203
- 3.7-flash and 3.6-flash had zero coverage from either mechanism before this. Confirmed live via
204
- `resolveOfferForCost`'s `provenance` field: `"alias-pin"` for the 4 lmprice already has,
205
- `"route-preference"` (this new table) for the other 7 — no conflict, no double-resolution. Raw
206
- `agy models` output this table was built from (verified live, 2026-09-29, agy binary on this
207
- machine):
208
-
209
- ```
210
- gemini-3.8-flash-high Gemini 3.8 Flash (High)
211
- gemini-3.8-flash-medium Gemini 3.8 Flash (Medium)
212
- gemini-3.8-flash-low Gemini 3.8 Flash (Low)
213
- gemini-3.7-flash-high Gemini 3.7 Flash (High)
214
- gemini-3.7-flash-medium Gemini 3.7 Flash (Medium)
215
- gemini-3.7-flash-low Gemini 3.7 Flash (Low)
216
- gemini-3.6-flash-high Gemini 3.6 Flash (High)
217
- gemini-3.6-flash-medium Gemini 3.6 Flash (Medium)
218
- gemini-3.6-flash-low Gemini 3.6 Flash (Low)
219
- gemini-3.1-pro-high Gemini 3.1 Pro (High)
220
- gemini-3.1-pro-low Gemini 3.1 Pro (Low)
221
- claude-sonnet-4-6 Claude Sonnet 4.6 (Thinking)
222
- claude-opus-4-6-thinking Claude Opus 4.6 (Thinking)
223
- gpt-oss-120b-medium GPT-OSS 120B (Medium)
224
- ```
225
-
226
- Every effort-tier variant of one base model maps to that base model's own catalog identity —
227
- reasoning effort/thinking budget changes how many tokens a turn generates, not the per-token rate,
228
- true of every major vendor's reasoning-effort parameter (OpenAI, Claude, Gemini alike).
229
-
230
- **agy fronts Claude too, not just Gemini (math.lmctl, 2026-09-29):** 3 members declare `Claude Opus
231
- 4.6 (Thinking)` on `provider=agy`. `coversRecord`/`AGY_FIRST_PARTY_PROVIDERS` were Gemini-only when
232
- this was first written -- a real gap for Claude-on-agy, caught before it silently priced at zero.
233
- Verified per-string, live:
234
-
235
- - `claude opus 4.6 (thinking)` already had an established display-label alias in lmprice's table
236
- (like the 4 Gemini ones above, added 2026-09-24) -- resolves via `alias-pin`, never touches
237
- `AGY_MODEL_TO_IDENTITY` at all. No code change needed for this one specifically.
238
- - `claude sonnet 4.6 (thinking)` had NO such alias and was a genuine `unknown` -- fixed by adding
239
- `"claude sonnet 4.6": "claude-sonnet-4-6"` to `AGY_MODEL_TO_IDENTITY`, broadening `coversRecord` to
240
- `/gemini|claude/`, and adding `"anthropic"` to `AGY_FIRST_PARTY_PROVIDERS` so the bare
241
- `litellm:claude-sonnet-4-6` (provider `anthropic`) row wins the tie-break over routed variants
242
- (`azure_ai/`, `deepinfra/`, `vertex_ai/`, `snowflake/`, `perplexity/`, `openrouter/`).
243
- - `gpt-oss 120b (medium)` remains **deliberately unmapped**: unlike Claude/Gemini, OpenAI does not
244
- host this open-weight model itself, so there is no single first-party row to prefer among a dozen
245
- equally-plausible third-party inference providers (groq, fireworks_ai, cerebras, deepinfra, ...).
246
- Picking one without evidence of which route agy actually uses would be exactly the kind of guess
247
- this whole explicit-mapping design exists to avoid -- stays `unknown` until there's real evidence
248
- (e.g. an antigravity.google/docs/models entry, same provenance as the existing aliases).
249
-
250
- ## OpenCode
251
-
252
- opencode model ids use `<provider>/<model>` shape. **lmctl does NOT manage opencode
253
- model config.** When lmctl spawns an opencode member it writes only
254
- `permission: "allow"` (the YOLO contract — opencode ACP has no
255
- `--dangerously-skip-permissions` flag) plus its own `mcp.lmctl` bridge entry into
256
- the member's `.opencode/opencode.json`. It never injects a provider/model catalog.
257
- Models resolve from **opencode's own default models.dev catalog + your
258
- `~/.config/opencode/opencode.json`** — you own the model config.
259
-
260
- What needs declaring:
261
- - **github-copilot GPT / Gemini / o-series** → served **config-free** from opencode's
262
- default catalog. Just `opencode auth login github-copilot`, then
263
- `model=github-copilot/gpt-5.5`, etc.
264
- - **github-copilot Claude** (`claude-opus-4.8`, `claude-sonnet-5`, …) → **must be
265
- declared** in your `opencode.json`. Copilot proxies Claude through its own API and
266
- opencode's default catalog does not route them (→ "404 page not found"); declaring
267
- them under a `github-copilot` provider (`@ai-sdk/openai-compatible`,
268
- `baseURL https://api.githubcopilot.com`) fixes it.
269
- - **Custom providers** (`deepseek/*`, `z-ai-coding/*`, `openrouter/*`,
270
- `ollama-local/*`, …) → **must be declared** (not in models.dev).
271
-
272
- Copy the published sample and adjust for your account:
273
-
274
- ```sh
275
- mkdir -p ~/.config/opencode
276
- cp examples/opencode.json ~/.config/opencode/opencode.json # or: curl -sL https://lmctl.com/examples/opencode.json -o ~/.config/opencode/opencode.json
277
- ```
278
-
279
- **Lint = detect + warn (never inject).** `lmctl lint`/`seed` warns when a
280
- `provider=opencode model=` is **not declared** in your `opencode.json` and isn't a
281
- config-free github-copilot GPT/Gemini/o id — pointing you at the sample. It also
282
- warns when `model=` is absent (opencode would silently pick its own default). It
283
- does not edit your config.
284
-
285
- `effort=`/variants: an opencode member's `effort=` maps to opencode's `--variant`
286
- (reasoning effort). Declare the variants on the model in your `opencode.json` (see
287
- the sample's Claude/GPT entries with `variants: { high, xhigh, max }`); on the ACP
288
- prompt path lmctl sends `session/set_config_option` with `configId: "model"` and
289
- `configId: "effort"` before `session/prompt`.
290
-
291
- For each opencode member with `model=`, `lmctl lint` runs the same ACP
292
- initialization and config-option calls, then exits before `session/prompt`. A
293
- provider rejection text is preserved inside an `ACP RPC error:` diagnostic so
294
- lint reflects opencode's live catalog and validation rules. If the local binary
295
- cannot start, complete its handshake, or respond before the probe timeout, lint
296
- skips this live check without treating the environment problem as a teamfile
297
- error.
298
-
299
- Example:
300
-
301
- ```md
302
- _MEMBER_ alias=Reviewer provider=opencode model=github-copilot/gpt-5.4 effort=xhigh sessionid=<seeded-id>
303
- ```
304
-
305
- ## Kimi
306
-
307
- Kimi (Moonshot AI's Kimi Code CLI) speaks ACP, same transport family as opencode/
308
- gemini/copilot. `model=` values are **user-defined aliases** declared in
309
- `$KIMI_CODE_HOME/config.toml` under `[models.<alias>]` table headers (e.g.
310
- `[models."kimi-code/k3"]`), not a fixed vendor catalog — same shape as opencode,
311
- not claude/codex. `lmctl` does not manage this file; it only reads it to validate
312
- `model=` at lint/seed time and warns (never injects) when the config is missing
313
- or the alias isn't declared.
314
-
315
- Kimi's ACP mode defaults to permission-**prompting** (unlike every argv-YOLO or
316
- config-file-YOLO provider). lmctl forces it into full-auto via an ACP protocol
317
- call (`session/set_config_option`, `configId: "mode"`, `value: "auto"`) sent
318
- after `session/new`/`session/load` and before the first `session/prompt` —
319
- fail-closed: if that call is rejected, lmctl throws rather than proceeding, on
320
- the one-shot transport (`src/providers/transports/acp.ts`). See
321
- `src/providers/yolo-contract.ts`'s `PROTOCOL_LEVEL_YOLO` set for the contract.
322
-
323
- Kimi 0.28.x exposes `thinking` through ACP only as an `on`/`off` toggle.
324
- Turning it on selects the current model alias's `default_effort` from
325
- `config.toml`; it does not accept a session-level effort value. Choose or
326
- define a different model alias when a different default effort is required.
327
- Accordingly, lmctl warns that `_MEMBER_ effort=` is ignored for Kimi.
328
-
329
- Example:
330
-
331
- ```md
332
- _MEMBER_ alias=Reviewer provider=kimi model="kimi-code/k3" sessionid=<seeded-id>
333
- ```
334
-
335
- `effort=` support by provider:
336
-
337
- | Provider | Status |
338
- | --- | --- |
339
- | `opencode` | Supported (via the model's declared `variants` in opencode.json). |
340
- | `kimi` | Not supported on the Kimi 0.28.x baseline; effort is fixed by the selected model alias's `default_effort`. |
341
- | `claude` | Claude Code has native `--effort`, but lmctl has not wired `_MEMBER_ effort=` to Claude yet. |
342
- | `codex` | Native effort surfaces exist; lmctl has not wired `_MEMBER_ effort=` to Codex yet. |
343
- | `agy` | No verified effort flag; only `--model` supported. |
344
- | `hermes` | Not supported. Hermes exposes reasoning effort through its own config (`--reasoning`) but not through its ACP surface, so lmctl ignores `effort=` for a Hermes member and warns. |
345
-
346
- ## Hermes
347
-
348
- Hermes is driven over ACP (`hermes acp`), and its ACP model ids are the
349
- encoded `provider:model` strings it advertises in `session/new`
350
- (`acp_adapter/model_catalog.py`, `encode_model_choice`). lmctl applies
351
- `model=` with ACP `session/set_model` when it differs from the loaded model.
352
-
353
- ```md
354
- _MEMBER_ alias=Reviewer provider=hermes model="deepseek:deepseek-v4.1-flash" sessionid=<uuid>
355
- ```
356
-
357
- There is deliberately **no** tested-model table here. Hermes resolves its
358
- model inventory at runtime from the user's own provider configuration and
359
- credentials, so a list in this repository would be wrong for most users —
360
- the same reasoning as `kimi` and `opencode`. Lint checks the SHAPE only:
361
- the value should carry a `provider:` prefix, because without one Hermes
362
- has to infer the provider by model-name detection and fails for models it
363
- cannot place.
364
-
365
- V1 supports ACP seeding and structured prompt/dispatch against one effective
366
- `HERMES_HOME`. Existing session IDs default to strict resume. Yolo mode enables
367
- both the shell approval bypass and the per-process ACP edit mode; plan mode is
368
- rejected. Native deletion, interactive resume, fork, and persistent daemon
369
- sessions are not supported by this integration. The reader is read-only and
370
- shows active transcript rows; token totals do not establish last-turn usage.
371
-
372
- Release prerequisites: Hermes must return JSON-RPC errors for missing load/resume
373
- IDs. Task 140 also uncovered an ACP compression-head restore defect: restart-safe
374
- resume after compression requires a separate Hermes fix before this provider lands.
375
-
376
- The source fixes are reviewed and committed locally in the Hermes checkout as
377
- `989fd87461e23493fe62250b1671f9fc368e9117`. This is not an upstream release or an
378
- installed-runtime update: the current GitHub identity has read-only access to
379
- NousResearch/hermes-agent, and validation used the fixed checkout explicitly.
380
- Provider publication remains gated on an approved, available patched Hermes
381
- runtime and final owner landing approval (task 141 notes 548-549).