triss-coworker 0.26.0 → 0.27.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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.27.0] — 2026-08-05
11
+
12
+ ### Added
13
+
14
+ - **Coder model management.** `triss coder models` discovers the effective
15
+ model configuration and live provider catalogue; `triss coder model set`
16
+ safely switches persistent main/small model pairs with wizard diagnostics,
17
+ transactional writes, and retained-record rollback recovery.
18
+
19
+ ### Changed
20
+
21
+ - OpenCode Zen's preferred/offline-fallback pair now starts with
22
+ `opencode/deepseek-v4-flash-free` instead of the retired
23
+ `opencode/hy3-free`; the live catalogue remains authoritative. Existing
24
+ provider/model mismatches produce actionable recovery choices without
25
+ silently switching providers.
26
+
27
+ ### Fixed
28
+
29
+ - One-shot `ask` and `review` responses now preserve providers' top-level
30
+ `final_text` instead of reporting an empty result after a successful call.
31
+ - GLM model switching no longer requires `--allow-unverified` when the provider
32
+ has no catalogue API; Crush accepts the documented Z.AI aliases, omitted
33
+ small-model values are preserved, and locks owned by dead processes can be
34
+ reclaimed safely.
35
+
10
36
  ## [0.26.0] — 2026-07-27
11
37
 
12
38
  ### Added
@@ -780,7 +806,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
780
806
 
781
807
  - Initial release of `triss-coworker`.
782
808
 
783
- [Unreleased]: https://github.com/ayleen/triss-coworker/compare/v0.26.0...HEAD
809
+ [Unreleased]: https://github.com/ayleen/triss-coworker/compare/v0.27.0...HEAD
810
+ [0.27.0]: https://github.com/ayleen/triss-coworker/compare/v0.26.0...v0.27.0
784
811
  [0.26.0]: https://github.com/ayleen/triss-coworker/compare/v0.25.0...v0.26.0
785
812
  [0.25.0]: https://github.com/ayleen/triss-coworker/compare/v0.24.2...v0.25.0
786
813
  [0.24.2]: https://github.com/ayleen/triss-coworker/compare/v0.24.1...v0.24.2
package/README.md CHANGED
@@ -271,7 +271,7 @@ and writing**.
271
271
  | `triss review` | Code review on current branch or a PR (diff + linked ticket) | The agent reading the whole diff |
272
272
  | `triss commit-msg` | Generates a commit message from staged diff | Hand-writing or copy-pasting from web LLMs |
273
273
  | `triss usage` | Cumulative cost / token usage with per-project breakdown | Squinting at stderr after each call |
274
- | `triss coder init` | Sets up a coding agent (default `opencode` engine; `--engine crush` for crush): provider key (Z.AI GLM default; `--provider opencode-zen` for free OpenCode Zen models like `hy3`; `--provider moonshot` for Kimi pay-as-you-go; `--provider kimi-for-coding` for the Kimi subscription), `opencode.json` / crush models + `permissions.run` policy, agent templates. Blocks (non-zero) on an unsafe existing `opencode.json` — missing deny-first bash policy (override with `--allow-unsafe-bash`) or a stale/cross-provider `small_model` | Manually installing/configuring opencode |
274
+ | `triss coder init` | Sets up a coding agent (default `opencode` engine; `--engine crush` for crush): provider key (Z.AI GLM default; `--provider opencode-zen` for free rotating OpenCode Zen models — discover the current id with `triss coder models`; `--provider moonshot` for Kimi pay-as-you-go; `--provider kimi-for-coding` for the Kimi subscription), `opencode.json` / crush models + `permissions.run` policy, agent templates. Blocks (non-zero) on an unsafe existing `opencode.json` — missing deny-first bash policy (override with `--allow-unsafe-bash`) or a stale/cross-provider `small_model` | Manually installing/configuring opencode |
275
275
  | `triss coder run` | Spawns the coding agent (GLM, Kimi, or Zen models) and prints one JSON envelope (`--engine opencode\|crush`; `--isolate` for a disposable worktree — opencode defaults to isolate-OFF, crush defaults to isolate-ON; crush adds opt-in `--restrict`/`--no-restrict` for its CLI allowlist). **POSIX only** (macOS/Linux) — refuses to run on Windows. | Manually driving `opencode run` and parsing its ndjson stream |
276
276
  | `triss coder clean` | Removes finished `.triss/wt` isolation worktrees (`--all` forces all) | Manually finding and deleting stale git worktrees |
277
277
  | `triss init` | Drops a tiny (~15 line) delegation block into `CLAUDE.md` / `AGENTS.md` | Hand-writing routing rules |
@@ -464,8 +464,8 @@ triss coder clean # remove finished isolation wo
464
464
  ```
465
465
 
466
466
  `triss coder init` first asks which provider to configure — **Z.AI GLM**
467
- (default), **OpenCode Zen** (`--provider opencode-zen`, free models incl.
468
- Hunyuan `hy3`), **Moonshot Kimi** (`--provider moonshot`, pay-as-you-go
467
+ (default), **OpenCode Zen** (`--provider opencode-zen`, free rotating
468
+ models), **Moonshot Kimi** (`--provider moonshot`, pay-as-you-go
469
469
  `moonshotai/*` models like `kimi-k2.7-code`), or **Kimi for Coding**
470
470
  (`--provider kimi-for-coding`, the flat-rate subscription serving Kimi K3).
471
471
  For Z.AI it probes which plan your key works with (subscription vs.
@@ -509,23 +509,17 @@ natively; triss also forwards it as `ZAI_API_KEY` for older binaries; see
509
509
  **Providers** — the `opencode` engine isn't limited to Z.AI. The required
510
510
  API key follows the model's `<provider>/` prefix: `zai-coding-plan/*` and
511
511
  `zai/*` (GLM) use `ZHIPU_API_KEY`; `opencode/*` models — served free by
512
- [OpenCode Zen](https://opencode.ai/docs/zen/), e.g. **`opencode/hy3-free`**
513
- (Tencent Hunyuan 3) — use `OPENCODE_API_KEY`; `moonshotai/*` (and the
514
- China-mainland `moonshotai-cn/*`) Kimi models use `MOONSHOT_API_KEY`; and
515
- `kimi-for-coding/*` models — the flat-rate
512
+ [OpenCode Zen](https://opencode.ai/docs/zen/) — use `OPENCODE_API_KEY`;
513
+ `moonshotai/*` (and the China-mainland `moonshotai-cn/*`) Kimi models use
514
+ `MOONSHOT_API_KEY`; and `kimi-for-coding/*` models — the flat-rate
516
515
  [Kimi for Coding](https://www.kimi.com/code/docs/en/) subscription, e.g.
517
- **`kimi-for-coding/k3`** (Kimi K3) — use `KIMI_API_KEY`. To point coder at
518
- a Zen model:
516
+ **`kimi-for-coding/k3`** (Kimi K3) — use `KIMI_API_KEY`. Zen ids rotate
517
+ freely, so discover the current id and pass it rather than hard-coding one:
519
518
 
520
519
  ```bash
521
- triss coder init --provider opencode-zen # guided: key + opencode.json
522
- # …or by hand:
523
- triss config set OPENCODE_API_KEY <key> # or add it to .triss.env
524
- export TRISS_CODER_MODEL=opencode/hy3-free # default it for every run
525
- export TRISS_CODER_SMALL_MODEL=opencode/hy3-free
526
- triss coder run "add input validation to /signup"
527
- # …or per-run, without changing the default:
528
- triss coder run "..." --model opencode/hy3-free
520
+ triss coder init --provider opencode-zen # guided: key + opencode.json
521
+ triss coder models --provider opencode-zen # see live Zen ids
522
+ triss coder run "..." --model opencode/<current-id> # per-run override
529
523
  ```
530
524
 
531
525
  Kimi works the same way:
@@ -539,13 +533,16 @@ triss coder run "..." --model kimi-for-coding/k3
539
533
  ```
540
534
 
541
535
  `triss coder run` passes the resolved model to opencode with `--model` and
542
- forwards only the key that model needs, so a Zen run works with
543
- `OPENCODE_API_KEY` alone and a Kimi run with `MOONSHOT_API_KEY` /
544
- `KIMI_API_KEY` alone — no Z.AI key required. The deny-first `opencode.json`
545
- bash policy applies to every provider. Any Zen model id works via
546
- `TRISS_CODER_MODEL=opencode/<id>` / `--model`, not just the free ones the
547
- picker lists. (The `crush` engine speaks Z.AI only.) Full details, the model
548
- catalogue, and every configuration path are in
536
+ forwards only the key that model needs — no Z.AI key is required for Zen or
537
+ Kimi runs. A shell-exported `TRISS_CODER_MODEL` is a runtime override of the
538
+ MAIN model only (it sits in the OpenCode-main precedence chain: one-run `--model`
539
+ → shell `TRISS_CODER_MODEL` → project `.triss.env` → global `.env` → built-in
540
+ default), so it changes the main model for every run until unset.
541
+ `TRISS_CODER_SMALL_MODEL` is setup intent consumed by `triss coder init` /
542
+ `triss coder model` — it does not swap the small model mid-run (opencode reads
543
+ `small_model` from `opencode.json`); use `--model` for a one-run main-model
544
+ override. The deny-first `opencode.json` bash policy applies to every provider.
545
+ Full details, the model catalogue, and every configuration path are in
549
546
  [docs/opencode-zen.md](docs/opencode-zen.md).
550
547
 
551
548
  If your Z.AI plan hits its usage limit, `triss coder run` fails fast with
@@ -555,6 +552,73 @@ opencode retries the throttled call silently, so the reset time is read
555
552
  from the engine log and the run is killed within a few seconds of the
556
553
  limit.
557
554
 
555
+ **Model management — engine vs provider.** Two independent axes; most
556
+ confusion is conflating them:
557
+
558
+ - **Engine** = *how* the agent is launched: `opencode` (default) or
559
+ `crush`. Set at `triss coder init --engine …` or per run with
560
+ `triss coder run --engine …`.
561
+ - **Provider** = *which* API serves the model: Z.AI GLM, OpenCode Zen,
562
+ Moonshot Kimi, Kimi for Coding. Set at `triss coder init --provider …`.
563
+
564
+ Not every engine speaks every provider:
565
+
566
+ | Engine | Providers served |
567
+ | ---------- | ------------------------------------------------- |
568
+ | `opencode` | Z.AI GLM, OpenCode Zen, Moonshot, Kimi for Coding |
569
+ | `crush` | Z.AI GLM (coding-plan only) |
570
+
571
+ `triss coder init` drives setup in that order — **engine, then provider,
572
+ then it asks for only that provider's credential** and writes the matching
573
+ model prefix (`opencode/<id>`, `zai-coding-plan/*`, `moonshotai/*`, or
574
+ `kimi-for-coding/*`).
575
+
576
+ **Choosing a model.** `--model` on `triss coder run` is a
577
+ **single-run override of the main model only** — it does not persist
578
+ and does not change the small/summarization model. To persist a
579
+ choice, use `triss coder model set` — positional main plus `--small` —
580
+ which requires you to name the engine and the scope explicitly:
581
+
582
+ ```bash
583
+ # global, opencode engine: positional main + --small; --yes for noninteractive use
584
+ triss coder model set --engine opencode --global \
585
+ opencode/<id> --small opencode/<id> --yes
586
+ # local, crush engine: canonical coding-plan GLM pair
587
+ triss coder model set --engine crush --local \
588
+ zai-coding-plan/glm-5.2 --small zai-coding-plan/glm-5-turbo --yes
589
+ ```
590
+
591
+ **Discovering models.** `triss coder models` lists what the resolved
592
+ engine/provider offers for that invocation, with filters and a
593
+ machine-readable form:
594
+
595
+ ```bash
596
+ triss coder models # current effective engine/provider
597
+ triss coder models --engine opencode # resolved provider for the OpenCode engine
598
+ triss coder models --provider opencode-zen # just OpenCode Zen
599
+ triss coder models --json # for scripting
600
+ ```
601
+
602
+ **When a Zen model goes stale.** Free OpenCode Zen models rotate — no
603
+ specific id is guaranteed to stay (the once-free `opencode/hy3-free`
604
+ Hunyuan model is the canonical stale-model incident). If a model is
605
+ removed upstream, recovery stays **inside Zen**, not a silent jump to
606
+ GLM. See what is live and re-pick explicitly:
607
+
608
+ ```bash
609
+ triss coder models --provider opencode-zen # current Zen ids
610
+ triss coder model set --engine opencode --global \
611
+ opencode/<current-id> --small opencode/<current-id> --yes
612
+ ```
613
+
614
+ Triss surfaces that replacement command rather than auto-switching you
615
+ to GLM.
616
+
617
+ Full flag reference, the model catalogues, key/endpoint routing, and
618
+ the stale-model recovery flow are in [docs/glm-clients.md](docs/glm-clients.md),
619
+ [docs/opencode-zen.md](docs/opencode-zen.md), and
620
+ [docs/configuration.md](docs/configuration.md).
621
+
558
622
  ## Integrations
559
623
 
560
624
  External-service plugins live under `src/integrations/<name>/`. They are
@@ -636,6 +700,12 @@ triss review --provider kimi # pro preset → kimi-k3
636
700
  triss ask --paths ... --question "..." --provider kimi --model flash # kimi-k2.6
637
701
  ```
638
702
 
703
+ One-shot `ask` / `review` output is provider-shape tolerant: Triss emits the
704
+ assistant text whether the successful response carries OpenAI-style
705
+ `choices[0].message.content` or a top-level `final_text`. A present
706
+ `final_text` must never be treated as an empty response or dropped by the CLI
707
+ or MCP renderer.
708
+
639
709
  For GLM, `pro` maps to `glm-5.2` on both endpoints, while `flash` — the cheap
640
710
  bulk-read tier — maps to `glm-4.7` on the subscription endpoint and to
641
711
  `glm-4.5-air` ($0.20/$1.10 per 1M) on pay-as-you-go. The subscription endpoint
package/bin/triss.js CHANGED
@@ -39,6 +39,7 @@ import {
39
39
  runUnset,
40
40
  } from '../src/commands/config.js';
41
41
  import { runCoderInit, runCoderRun, runCoderClean } from '../src/commands/coder.js';
42
+ import { runCoderModels, runCoderModelSet, runCoderModelRollback } from '../src/commands/coder-models.js';
42
43
  import { loadIntegrations } from '../src/integrations/_registry.js';
43
44
  import { withCall } from '../src/call-context.js';
44
45
 
@@ -181,6 +182,8 @@ config
181
182
  .option('-f, --force', 're-prompt for keys that are already set')
182
183
  .option('--standard', 'API key + one model only — skip the standard/advanced prompt')
183
184
  .option('--advanced', 'full wizard with presets, base URL, integrations — skip the prompt')
185
+ .option('--coder-engine <name>', 'coder target only: coding engine to configure (opencode default, or crush). `coder init` uses --engine')
186
+ .option('--coder-provider <name>', 'coder target only: opencode engine provider (zai, opencode-zen, moonshot, kimi-for-coding). `coder init` uses --provider')
184
187
  .action(wrap(runWizard));
185
188
 
186
189
  config
@@ -227,7 +230,7 @@ config
227
230
 
228
231
  const coder = program
229
232
  .command('coder')
230
- .description('Run a GLM coding agent (opencode or crush engine)');
233
+ .description('Run a coding agent (OpenCode or Crush engine)');
231
234
 
232
235
  coder
233
236
  .command('init')
@@ -235,7 +238,7 @@ coder
235
238
  .option('-g, --global', 'save to the global scope (~/.config/triss/.env, ~/.config/opencode/)')
236
239
  .option('-l, --local', 'save to the project scope (./.triss.env, ./opencode.json)')
237
240
  .option('--engine <name>', 'coding engine to configure: opencode (default) or crush')
238
- .option('--provider <name>', 'opencode engine model provider: zai (Z.AI GLM, default), opencode-zen (free models incl. hy3), moonshot (Kimi pay-as-you-go), or kimi-for-coding (subscription)')
241
+ .option('--provider <name>', 'opencode engine model provider: zai (Z.AI GLM, default), opencode-zen (free rotating models — discover with `triss coder models`), moonshot (Kimi pay-as-you-go), or kimi-for-coding (subscription)')
239
242
  .option('--allow-unsafe-bash', 'proceed even if an existing opencode.json has no deny-first bash policy (the agent runs with --auto)')
240
243
  .action(wrap(runCoderInit));
241
244
 
@@ -246,7 +249,7 @@ coder
246
249
  .option('--session <id>', 'triss-side session slug, mapped to a real opencode session id in .triss/sessions.json')
247
250
  .option('--continue', 'continue the most recent opencode session (maps to opencode --continue)')
248
251
  .option('--agent <name>', 'opencode agent template to use', 'coder')
249
- .option('--model <p/m>', 'override the model for this run only')
252
+ .option('--model <p/m>', 'override the MAIN model for this one run only (does not change small_model or repair persistent config; use `triss coder model set` for that)')
250
253
  .option('--isolate', 'run in a disposable git worktree under .triss/wt/<slug>')
251
254
  .option('--no-isolate', 'disable worktree isolation (opencode defaults to OFF; crush defaults to ON)')
252
255
  .option('--restrict', 'crush only: enforce the allowlist via CLI --allow-bash/--allow-tool flags (--restrict-run). Opt-in (default OFF)')
@@ -263,6 +266,51 @@ coder
263
266
  .option('--all', 'force-remove every worktree under .triss/wt, regardless of diff state')
264
267
  .action(wrap(runCoderClean));
265
268
 
269
+ coder
270
+ .command('models')
271
+ .description('List current + live coder models, provider compatibility, and credential readiness (read-only)\n\n' +
272
+ 'Configuration sources:\n' +
273
+ ' • opencode: project opencode.json (local) or ~/.config/opencode/opencode.json (global)\n' +
274
+ ' • crush: ./.crush/crush.json (local) or ~/.local/share/crush/crush.json (global)')
275
+ .option('--engine <name>', 'coding engine: opencode (default) or crush')
276
+ .option('--provider <name>', 'provider kind: zai, opencode-zen, moonshot, or kimi-for-coding')
277
+ .option('--json', 'print the stable machine-readable state object (no secrets)')
278
+ .action(wrap(runCoderModels));
279
+
280
+ // `coder model` is a command GROUP whose only leaf today is `set`. Registered
281
+ // as a nested group so `triss coder model set --help` prints its OWN usage line
282
+ // (`Usage: triss coder model set ...`) rather than the parent `coder` usage.
283
+ const coderModel = coder
284
+ .command('model')
285
+ .description('Inspect or change persistent coder models (e.g. `triss coder model set`)');
286
+
287
+ coderModel
288
+ .command('set [main-model]')
289
+ .description(
290
+ 'Persistently switch the coder main/small models for one engine+scope (non-interactive; needs --yes to write). ' +
291
+ 'A one-run main override is `triss coder run --model` (main-only, not a persistent repair).\n\n' +
292
+ 'Engines and configuration targets:\n' +
293
+ ' • opencode: project opencode.json (local) or ~/.config/opencode/opencode.json (global); runtime main follows TRISS_CODER_MODEL env precedence, config main is opencode.json.model\n' +
294
+ ' • crush: project .crush/crush.json (local) or ~/.local/share/crush/crush.json (global)'
295
+ )
296
+ .option('--small <model>', 'small/fast model id (omit to keep the current compatible value)')
297
+ .option('--engine <name>', 'coding engine: opencode (default) or crush')
298
+ .option('--provider <name>', 'provider kind: zai, opencode-zen, moonshot, or kimi-for-coding')
299
+ .option('-g, --global', 'write to the global scope (OpenCode: ~/.config/opencode/opencode.json + global .env; Crush: ~/.local/share/crush/crush.json)')
300
+ .option('-l, --local', 'write to the project scope (OpenCode: ./opencode.json + ./.triss.env; Crush: ./.crush/crush.json)')
301
+ .option('--allow-unverified', 'proceed past a not-verified catalogue (timeout/http-error/parse-error) ONLY — never auth or an authoritative unavailable result')
302
+ .option('--allow-unsafe-bash', 'proceed even if the existing opencode.json lacks the deny-first bash policy')
303
+ .option('--yes', 'non-interactive confirmation: apply the planned switch (required to write; without it the plan is printed and nothing is changed)')
304
+ .action((mainModel, opts) => wrap(runCoderModelSet)(mainModel, opts));
305
+
306
+ coderModel
307
+ .command('rollback')
308
+ .description('Restore retained transaction record (opencode.json / crush.json + env pins)')
309
+ .requiredOption('--from <absolute-record-dir>', 'absolute path to the retained transaction record directory')
310
+ .option('-g, --global', 'restore to the global scope (~/.config/opencode/ or ~/.local/share/crush/)')
311
+ .option('-l, --local', 'restore to the project scope (./opencode.json or ./.crush/crush.json)')
312
+ .action((opts) => wrap(runCoderModelRollback)(opts.from, opts));
313
+
266
314
  function wrap(fn) {
267
315
  return async (...args) => {
268
316
  try {