triss-coworker 0.35.0 → 0.36.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,102 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.36.0] — 2026-08-18
11
+
12
+ ### Added
13
+
14
+ - **OpenCode 2 coder engine (beta)**: `triss coder run --engine opencode2`
15
+ and `triss coder init --engine opencode2`, pinned to the exact verified
16
+ build `@opencode-ai/cli@0.0.0-next-17430` (`TRISS_CODER_OPENCODE2_VERSION`
17
+ to override). The beta runs every managed invocation `--standalone` with
18
+ auto-update disabled, isolates V2 state under `<project>/.triss/opencode2/`
19
+ (0700), folds the V2 event stream into the shared usage envelope
20
+ (`usage_source: "opencode2"`, terminal-error precedence, no fabricated
21
+ zeros), namespaces sessions per engine, and rides the shared opencode-v1
22
+ configuration backend for `coder model set` / rollback. See
23
+ docs/opencode2.md and docs/opencode2-engine-plan.md.
24
+
25
+ ### Security (opencode2 engine beta)
26
+
27
+ - Fail-closed effective-configuration preflight for `coder run --engine
28
+ opencode2`: the managed provider projection (exact package, settings keys,
29
+ credential placeholder, and the worker `baseURL` compared against the
30
+ configured Triss worker endpoint), `provider.api` and model-level
31
+ transport overrides are rejected before any credential is forwarded, and
32
+ the permission gate requires deny-everything shell policy (no live
33
+ allow/ask rule — the credential sits in the child environment), proven
34
+ with a real last-match-wins evaluator.
35
+ - `opencode2` runs resolve the engine binary to an absolute path
36
+ (`which` + `realpath`) and spawn exactly that path, re-verifying the same
37
+ path and version after the run.
38
+ - Session store reads fail closed on malformed namespaces (no silent data
39
+ loss on rewrite); the lookup happens inside the isolation cleanup guard so
40
+ a malformed store never leaks a worktree/branch.
41
+ - XDG runtime roots reject symlinked path components anywhere in the chain
42
+ below the project root.
43
+ - `opencode init --engine opencode2` owns its flow: credential prompt and
44
+ `--local`/`--global` scope are honored, an exact-pin mismatch is terminal
45
+ before any config mutation, and the post-setup audit is the full provider
46
+ + permission preflight (not just the plugin/agent scan).
47
+ - The full run preflight parses config layers through the JSONC-aware
48
+ canonical parser (comments and trailing commas no longer abort the run),
49
+ and `parseOpenCodeDocument` drops trailing commas followed by comments.
50
+
51
+ ### Fixed (review rounds 4–6)
52
+
53
+ - Dual legacy/native config forms (`provider`+`providers`,
54
+ `plugin`+`plugins`, `permission`+`permissions`) reject — the pinned build
55
+ prefers the native value while a legacy-first projection audits the other
56
+ one; the worker key/endpoint provenance gate resolves each field's
57
+ EFFECTIVE source from the pre-dotenv snapshot (a decoy key in the project
58
+ `.triss.env` cannot mask a shell key); unrelated provider definitions
59
+ (in-process npm code) reject; the audit walks the canonical
60
+ (`realpathSync.native`) runtime directory; audited sources are re-audited
61
+ immediately before the credential-bearing spawn (TOCTOU); the top-level
62
+ key table is captured from the official schema (benign keys no longer
63
+ false-reject; object `lsp`/`formatter` and `experimental` reject).
64
+ - A held session-store lock no longer discards a finished run:
65
+ `persistSessionMapping` retries with backoff and degrades to the lock-free
66
+ protocol (mapping kept, warning emitted, foreign lock untouched).
67
+ - A corrupted `sessions.json` on the V1 `--isolate` path no longer strands
68
+ the `.triss/wt/<slug>` worktree and `coder/<slug>` branch.
69
+ - `TRISS_CODER_ENGINE=opencode2` from a `.env` file now routes `coder init`
70
+ to the V2 flow (the engine is resolved after the env files load); the
71
+ pre-dotenv snapshots are threaded through so the provenance and
72
+ pin-shadow checks stay exact.
73
+ - `coder init --engine opencode2` on a tree carrying the V1 bash allowlist
74
+ rejects BEFORE any credential/config write with actionable guidance; a
75
+ fresh V2 init warns that the shared deny-everything policy removes the
76
+ allowlisted commands from plain V1 runs (documented in
77
+ docs/opencode2.md, including two new troubleshooting rows).
78
+ - Model inspection tolerates unrelated hostile config shapes (a non-string
79
+ plugin reference, unreadable directories) — the post-commit audit of a
80
+ successful `coder model set` no longer rolls back over them; `coder
81
+ models` warnings use the OpenCode branch for `opencode2` (no false
82
+ `configured-model-unavailable` for a shell-exported foreign model);
83
+ rollback of an `opencode2` record reports engine `opencode2`.
84
+ - `opencode-go/` routes price as unknown (the Go reseller's tariffs are
85
+ unmodeled); the prefixed `TRISS_PRICE_OPENCODE_GO_<MODEL>` override is
86
+ the documented way to price them.
87
+ - Unknown `engines.*` namespaces in the session store fail closed;
88
+ `ensureOpenCode2RuntimeDirs` reports the directories it created;
89
+ `.env.example` documents `TRISS_CODER_OPENCODE2_VERSION`.
90
+
91
+ ### Artifact integrity (0.36.0)
92
+
93
+ - `triss-dsh-provider-bundle-0.36.0.tgz` — sha256
94
+ `0e3100362fc02d242deac114ec6e0c4c966bbe5edde4f156a2ffdb76ef2eb329`,
95
+ integrity `sha512-P2QoA6Ahu/J9swIJhk9IkuP1OLR+bzn4zJdUKyuskutbis2GUSt5m3n+QmqP7B0rB1JAqi0WGIMkUrTBcmf9cQ==`
96
+ (computed with the pinned release npm 11.6.2 via
97
+ `scripts/publish-gate.js pack-inspect`; `npm pack` output is
98
+ byte-deterministic — tar entries carry the fixed npm epoch mtime — so a
99
+ test pins this value against every future pack of the same content).
100
+ - Root `triss-coworker-0.36.0.tgz` sha256 is reproducible via
101
+ `npm pack` at tag `v0.36.0` (the root tarball ships `CHANGELOG.md`, so its
102
+ hash cannot be recorded inside this file); registry verification compares
103
+ the packed artifact against the published tarball byte-for-byte
104
+ (`scripts/publish-gate.js pack-inspect`).
105
+
10
106
  ## [0.35.0] — 2026-08-14
11
107
 
12
108
  ### Added
package/README.md CHANGED
@@ -279,7 +279,8 @@ The exposed tool set is **filtered by configured credentials**:
279
279
  `ZHIPU_API_KEY` (Z.AI GLM), `OPENCODE_API_KEY` (OpenCode Zen or Go),
280
280
  `MOONSHOT_API_KEY` (Moonshot Kimi), or `KIMI_API_KEY` (Kimi for Coding)
281
281
  (setup: `triss coder init`). `triss_coder_run` takes an optional `engine`
282
- (`opencode` default, or `crush`); its timeout defaults to
282
+ (`opencode` V1 default; `--engine opencode2` beta — see
283
+ [opencode2.md](docs/opencode2.md) — or `crush`); its timeout defaults to
283
284
  1500s (25 min) over MCP, above the CLI's 900s, since coding runs are
284
285
  expected to be long; override per call via the `timeout` arg. For
285
286
  runs that may exceed it, use `triss coder run` on the CLI instead.
@@ -311,8 +312,8 @@ and writing**.
311
312
  | `triss review` | Code review on a branch, PR, or explicitly piped diff (linked ticket only for branch/PR) | The agent reading the whole diff |
312
313
  | `triss commit-msg` | Generates a commit message from staged diff | Hand-writing or copy-pasting from web LLMs |
313
314
  | `triss usage` | Cumulative cost / token usage with per-project breakdown | Squinting at stderr after each call |
314
- | `triss coder init` | Sets up a coding agent (default `opencode` engine; `--engine crush` for crush): provider key/profile (`--provider worker` reuses `TRISS_WORKER_*`; Z.AI GLM default; Zen, Go, and Kimi are also supported), config, permission policy, and 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 |
315
- | `triss coder run` | Spawns the coding agent (the OpenAI-compatible Triss worker, GLM, Kimi, OpenCode Zen, or OpenCode Go) 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 |
315
+ | `triss coder init` | Sets up a coding agent (default `opencode` V1 engine; `--engine opencode2` for the V2 beta — shares the `opencode.json` config, see [docs/opencode2.md](docs/opencode2.md); `--engine crush` for crush): provider key/profile (`--provider worker` reuses `TRISS_WORKER_*`; Z.AI GLM default; Zen, Go, and Kimi are also supported), config, permission policy, and 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 |
316
+ | `triss coder run` | Spawns the coding agent (the OpenAI-compatible Triss worker, GLM, Kimi, OpenCode Zen, or OpenCode Go) and prints one JSON envelope (`--engine opencode|opencode2|crush` — opencode V1 default, opencode2 beta per [docs/opencode2.md](docs/opencode2.md); `--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 |
316
317
  | `triss coder clean` | Removes finished `.triss/wt` isolation worktrees (`--all` forces all) | Manually finding and deleting stale git worktrees |
317
318
  | `triss init` | Drops a tiny (~15 line) delegation block into `CLAUDE.md` / `AGENTS.md` | Hand-writing routing rules |
318
319
  | `triss agent-help` | Prints the full delegation cookbook on demand (the nano block points here) | A 200-line CLAUDE.md that always loads |
@@ -587,8 +588,9 @@ default timeout, configurable via `--timeout <ms>`.
587
588
 
588
589
  Delegates an implementation task to a cheap coding agent — the existing
589
590
  OpenAI-compatible Triss worker, GLM, Kimi,
590
- OpenCode Zen, or OpenCode Go models (the `opencode` engine by default; `crush` is an
591
- alternative — see **Engines** below) instead of the primary model writing
591
+ OpenCode Zen, or OpenCode Go models (the `opencode` V1 engine by default;
592
+ `opencode2` beta — see [docs/opencode2.md](docs/opencode2.md) — or `crush`; see
593
+ **Engines** below) instead of the primary model writing
592
594
  the code itself.
593
595
 
594
596
  ```bash
package/bin/triss.js CHANGED
@@ -276,7 +276,7 @@ config
276
276
  .option('-f, --force', 're-prompt for keys that are already set')
277
277
  .option('--standard', 'API key + one model only — skip the standard/advanced prompt')
278
278
  .option('--advanced', 'full wizard with presets, base URL, integrations — skip the prompt')
279
- .option('--coder-engine <name>', 'coder target only: coding engine to configure (opencode default, or crush). `coder init` uses --engine')
279
+ .option('--coder-engine <name>', 'coder target only: coding engine to configure (opencode default, opencode2 beta, or crush). `coder init` uses --engine')
280
280
  .option('--coder-provider <name>', 'coder target only: opencode engine provider (zai, worker, opencode-zen, opencode-go, moonshot, kimi-for-coding). `coder init` uses --provider')
281
281
  .action(wrap(runWizard));
282
282
 
@@ -324,14 +324,14 @@ config
324
324
 
325
325
  const coder = program
326
326
  .command('coder')
327
- .description('Run a coding agent (OpenCode or Crush engine)');
327
+ .description('Run a coding agent (OpenCode V1, OpenCode 2 beta, or Crush engine)');
328
328
 
329
329
  coder
330
330
  .command('init')
331
- .description('Install/configure a coding engine (opencode default, or crush), provider key, permission policy, and agent templates')
331
+ .description('Install/configure a coding engine (opencode default, opencode2 beta, or crush), provider key, permission policy, and agent templates')
332
332
  .option('-g, --global', 'save to the global scope (~/.config/triss/.env, ~/.config/opencode/)')
333
333
  .option('-l, --local', 'save to the project scope (./.triss.env, ./opencode.json)')
334
- .option('--engine <name>', 'coding engine to configure: opencode (default) or crush')
334
+ .option('--engine <name>', 'coding engine to configure: opencode (default), opencode2 (beta — shares the opencode.json config; see docs/opencode2.md), or crush')
335
335
  .option('--provider <name>', 'opencode engine model provider: zai, worker (existing OpenAI-compatible TRISS_WORKER_* profile), opencode-zen, opencode-go, moonshot, or kimi-for-coding')
336
336
  .option('--allow-unverified', 'requires explicit --provider opencode-go (alias: go): allow the built-in fallback only after a temporary network or HTTP 408/429/500/502/503/504 catalogue failure (never bypasses 401/403, empty, or invalid responses)')
337
337
  .option('--allow-unsafe-bash', 'proceed even if an existing opencode.json has no deny-first bash policy (the agent runs with --auto)')
@@ -339,11 +339,11 @@ coder
339
339
 
340
340
  coder
341
341
  .command('run [prompt]')
342
- .description('Spawn a coding agent — GLM, the OpenAI-compatible Triss worker, Kimi, OpenCode Zen, or OpenCode Go (opencode default, or --engine crush) — and print a JSON envelope to stdout')
343
- .option('--engine <name>', 'coding engine: opencode (default) or crush')
342
+ .description('Spawn a coding agent — GLM, the OpenAI-compatible Triss worker, Kimi, OpenCode Zen, or OpenCode Go (opencode default; opencode2 beta or --engine crush) — and print a JSON envelope to stdout')
343
+ .option('--engine <name>', 'coding engine: opencode (default), opencode2 (beta — see docs/opencode2.md), or crush')
344
344
  .option('--session <id>', 'triss-side session slug, mapped to a real opencode session id in .triss/sessions.json')
345
345
  .option('--continue', 'continue the most recent opencode session (maps to opencode --continue)')
346
- .option('--agent <name>', 'opencode agent template to use', 'coder')
346
+ .option('--agent <name>', 'agent template to use (V1 default: coder; opencode2 beta uses its built-in primary agent unless set)')
347
347
  .option('--provider <name>', 'OpenCode only: select a provider for one run; requires --model and does not modify .env or opencode.json')
348
348
  .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)')
349
349
  .option('--small-model <p/m>', 'with --provider, override small_model for one run (defaults to the one-shot main model)')
@@ -366,10 +366,10 @@ coder
366
366
  coder
367
367
  .command('models')
368
368
  .description('List current + live coder models, provider compatibility, and credential readiness (read-only)\n\n' +
369
- 'Configuration sources:\n' +
370
- ' • opencode: project opencode.json (local) or ~/.config/opencode/opencode.json (global)\n' +
369
+ 'Configuration sources (layered; later sources win):\n' +
370
+ ' • opencode & opencode2: JSONC-aware layered config — global ~/.config/opencode/opencode.json, then every opencode.json/.opencode/opencode.json up from the project root to the Git boundary (project-local wins)\n' +
371
371
  ' • crush: ./.crush/crush.json (local) or ~/.local/share/crush/crush.json (global)')
372
- .option('--engine <name>', 'coding engine: opencode (default) or crush')
372
+ .option('--engine <name>', 'coding engine: opencode (default), opencode2 (beta — layered JSONC config), or crush')
373
373
  .option('--provider <name>', 'provider kind: zai, worker, opencode-zen, opencode-go, moonshot, or kimi-for-coding')
374
374
  .option('--json', 'print the stable machine-readable state object (no secrets)')
375
375
  .action(wrap(runCoderModels));
@@ -391,7 +391,7 @@ coderModel
391
391
  ' • crush: project .crush/crush.json (local) or ~/.local/share/crush/crush.json (global)'
392
392
  )
393
393
  .option('--small <model>', 'small/fast model id (omit to keep the current compatible value)')
394
- .option('--engine <name>', 'coding engine: opencode (default) or crush')
394
+ .option('--engine <name>', 'coding engine: opencode (default), opencode2 (beta — layered JSONC config), or crush')
395
395
  .option('--provider <name>', 'provider kind: zai, worker, opencode-zen, opencode-go, moonshot, or kimi-for-coding')
396
396
  .option('-g, --global', 'write to the global scope (OpenCode: ~/.config/opencode/opencode.json + global .env; Crush: ~/.local/share/crush/crush.json)')
397
397
  .option('-l, --local', 'write to the project scope (OpenCode: ./opencode.json + ./.triss.env; Crush: ./.crush/crush.json)')
@@ -263,14 +263,15 @@ self-hosted endpoints).
263
263
  | Variable | Required | Default | Notes |
264
264
  | -------------------------------- | -------- | ------------------ | ------------------------------------------ |
265
265
  | `ZHIPU_API_KEY` | yes¹ | — | Z.AI API key for `ask`/`review --provider glm` and GLM coder models — <https://z.ai/manage-apikey/apikey-list> |
266
- | `OPENCODE_API_KEY` | no¹ | — | Shared OpenCode credential for Zen `opencode/*` and paid Go `opencode-go/*` models (opencode engine only). A key alone does not prove Go subscription, quota, or regional readiness. See [opencode-zen.md](opencode-zen.md) and [opencode-go.md](opencode-go.md). |
266
+ | `OPENCODE_API_KEY` | no¹ | — | Shared OpenCode credential for Zen `opencode/*` and paid Go `opencode-go/*` models (opencode engines). A key alone does not prove Go subscription, quota, or regional readiness. See [opencode-zen.md](opencode-zen.md) and [opencode-go.md](opencode-go.md). |
267
267
  | `MOONSHOT_API_KEY` | no¹ | — | Moonshot AI (Kimi) key for `ask`/`review --provider kimi` and `moonshotai/*` coder models — <https://platform.kimi.ai/console/api-keys> |
268
- | `KIMI_API_KEY` | no¹ | — | Kimi for Coding subscription key (opencode engine only) — unlocks `kimi-for-coding/*` models like `kimi-for-coding/k3` — <https://www.kimi.com/code/docs/en/> |
268
+ | `KIMI_API_KEY` | no¹ | — | Kimi for Coding subscription key (opencode engines) — unlocks `kimi-for-coding/*` models like `kimi-for-coding/k3` — <https://www.kimi.com/code/docs/en/> |
269
269
  | `TRISS_KIMI_BASE_URL` | no | `https://api.moonshot.ai/v1` | Endpoint for `--provider kimi` ask/review calls — set `https://api.moonshot.cn/v1` for a China-mainland key. Trailing slashes are stripped; a blank/degenerate value falls back to the default |
270
270
  | `TRISS_CODER_MODEL` | no | `zai-coding-plan/glm-5.2` | Resolved **main** model, passed to opencode via `--model`. Worker uses `triss-worker/<id>`, Go uses `opencode-go/<id>`, and Zen uses `opencode/<id>`. Main and small must stay within one provider prefix. |
271
271
  | `TRISS_CODER_SMALL_MODEL` | no | `zai-coding-plan/glm-5-turbo` | Small/fast **management/init intent** — written to `opencode.json` `small_model` by `init`/`triss coder model set`. **Not** a runtime override of an already-pinned small role (see precedence) |
272
272
  | `TRISS_CODER_OPENCODE_VERSION` | no | `1.18.7` | Pin override for the `opencode-ai` npm install |
273
- | `TRISS_CODER_ENGINE` | no | `opencode` | Coding engine: `opencode` (default) or `crush` |
273
+ | `TRISS_CODER_ENGINE` | no | `opencode` | Coding engine: `opencode` (default), `opencode2` (beta — see [opencode2.md](./opencode2.md)), or `crush` |
274
+ | `TRISS_CODER_OPENCODE2_VERSION` | no | `0.0.0-next-17430` | Exact pin override for the `@opencode-ai/cli` npm install (opencode2 engine — exact match required) |
274
275
  | `TRISS_CODER_CRUSH_VERSION` | no | `0.1.6` | Pin override for the `@phpcraftdream/crush` npm install (crush engine) |
275
276
  | `TRISS_CODER_CRUSH_RESTRICT` | no | unset (`0`) | crush only — `1` opts INTO the CLI allowlist (`--restrict-run` + `--allow-bash`/`--allow-tool`, the only enforcement path that works today — crush 0.1.3 ignores the `permissions.run` config). Unset leaves crush unrestricted; crush then defaults to isolate-ON. CLI `--restrict`/`--no-restrict` overrides; crush.json `permissions.run.restrict` is the next fallback (forward-compat — currently inert) |
276
277
 
@@ -46,22 +46,23 @@ needs — `ZHIPU_API_KEY` for GLM, `OPENCODE_API_KEY` for OpenCode Zen or Go (§
46
46
 
47
47
  ---
48
48
 
49
- ## 2. The two clients (engines)
50
-
51
- | | **opencode** (engine #1, default) | **crush** (engine #2) |
52
- |---|---|---|
53
- | Select via | default, or `--engine opencode` | `--engine crush` / `TRISS_CODER_ENGINE=crush` |
54
- | npm package | `opencode-ai` (pinned `1.18.7`) | `@phpcraftdream/crush` (pinned `0.1.6`) |
55
- | Version pin env | `TRISS_CODER_OPENCODE_VERSION` | `TRISS_CODER_CRUSH_VERSION` |
56
- | Key it reads | `TRISS_WORKER_API_KEY` for `triss-worker/…`; `ZHIPU_API_KEY` for GLM; shared `OPENCODE_API_KEY` for `opencode/…` Zen and `opencode-go/…` Go models; `MOONSHOT_API_KEY` for `moonshotai/…`; `KIMI_API_KEY` for `kimi-for-coding/…` | `ZAI_API_KEY` (Triss bridges from `ZHIPU_API_KEY`; crush ≥0.1.1 also reads `ZHIPU_API_KEY` natively) |
57
- | Providers | Triss worker (`triss-worker/…`, OpenAI-compatible), Z.AI GLM, OpenCode Zen (`opencode/…`; [opencode-zen.md](opencode-zen.md)), OpenCode Go (`opencode-go/…`; [opencode-go.md](opencode-go.md)), Moonshot Kimi, and Kimi for Coding | Z.AI GLM only |
58
- | Provider config | `opencode.json` with a provider-qualified model prefix. Triss writes `provider["triss-worker"]` with `@ai-sdk/openai-compatible`; Zen/Kimi models resolve via OpenCode's built-in providers | `crush.json` `models` block (atoms `glm5_2` / `glm5_turbo`) |
59
- | Output | ndjson stream that Triss folds into one envelope | ONE JSON object at end-of-run — trivial last-line parse |
60
- | Sessions | slug → real `ses_…` id mapped in `.triss/sessions.json` | native get-or-create with the caller's arbitrary id — no map |
61
- | Safety model | **deny-first bash allowlist** in `opencode.json` (persistent, enforced) | config `permissions.run` seeded into `crush.json` for forward-compat, but **currently inert** — enforcement is opt-in via `--restrict`, which emits the allowlist as **CLI flags** (`--allow-bash`/`--allow-tool`). See §8 |
62
- | Isolation default | **OFF** (`opencode.json` policy is the safety layer) | **ON** (the disposable worktree is crush's reliable safety layer — the config allowlist is inert and a denied bash deadlocks to timeout) |
63
- | Per-call cost | engine-**calculated** from its own model catalogue, so a `0` is equally consistent with "coding plan" and "no rate in the catalogue"; Triss keeps it as `reported_total_usd` and only trusts a zero for a proven subscription/free call | real `delta_cost_usd` reported — a per-call charge Triss trusts, including an explicit `0` |
64
- | Sub-agents | opencode agent templates | `--agents single` (Triss forces this) |
49
+ ## 2. The engines
50
+
51
+ | | **opencode** (engine #1, default) | **opencode2** (engine #2, beta) | **crush** (engine #3) |
52
+ |---|---|---|---|
53
+ | Select via | default, or `--engine opencode` | `--engine opencode2` / `TRISS_CODER_ENGINE=opencode2` | `--engine crush` / `TRISS_CODER_ENGINE=crush` |
54
+ | Status | stable | beta — see [opencode2.md](./opencode2.md) | stable |
55
+ | npm package | `opencode-ai` (pinned `1.18.7`) | `@opencode-ai/cli` (exact pin `0.0.0-next-17430`) | `@phpcraftdream/crush` (pinned `0.1.6`) |
56
+ | Version pin env | `TRISS_CODER_OPENCODE_VERSION` | `TRISS_CODER_OPENCODE2_VERSION` (exact-match) | `TRISS_CODER_CRUSH_VERSION` |
57
+ | Key it reads | `TRISS_WORKER_API_KEY` for `triss-worker/…`; `ZHIPU_API_KEY` for GLM; shared `OPENCODE_API_KEY` for `opencode/…` Zen and `opencode-go/…` Go models; `MOONSHOT_API_KEY` for `moonshotai/…`; `KIMI_API_KEY` for `kimi-for-coding/…` | same keys as opencode (shared config surface) | `ZAI_API_KEY` (Triss bridges from `ZHIPU_API_KEY`; crush ≥0.1.1 also reads `ZHIPU_API_KEY` natively) |
58
+ | Providers | Triss worker (`triss-worker/…`, OpenAI-compatible), Z.AI GLM, OpenCode Zen (`opencode/…`; [opencode-zen.md](opencode-zen.md)), OpenCode Go (`opencode-go/…`; [opencode-go.md](opencode-go.md)), Moonshot Kimi, and Kimi for Coding | provider routing as resolved for V1 (fixture-gated per route; see [opencode2.md](./opencode2.md)) | Z.AI GLM only |
59
+ | Provider config | `opencode.json` with a provider-qualified model prefix. Triss writes `provider["triss-worker"]` with `@ai-sdk/openai-compatible`; Zen/Kimi models resolve via OpenCode's built-in providers | shares `opencode.json` with V1 — one config, both engines | `crush.json` `models` block (atoms `glm5_2` / `glm5_turbo`) |
60
+ | Output | ndjson stream that Triss folds into one envelope | ndjson event stream (V2 shape) folded by the same envelope contract | ONE JSON object at end-of-run — trivial last-line parse |
61
+ | Sessions | slug → real `ses_…` id mapped in `.triss/sessions.json` | versioned session map under `engines.opencode2` — V1/V2 slugs never cross-resume | native get-or-create with the caller's arbitrary id — no map |
62
+ | Safety model | **deny-first bash allowlist** in `opencode.json` (persistent, enforced) | deny-first policy shared from `opencode.json`, PLUS a static plugin/agent preflight that rejects any unverified source before spawn | config `permissions.run` seeded into `crush.json` for forward-compat, but **currently inert** — enforcement is opt-in via `--restrict`, which emits the allowlist as **CLI flags** (`--allow-bash`/`--allow-tool`). See §8 |
63
+ | Isolation default | **OFF** (`opencode.json` policy is the safety layer) | **OFF** (same policy reasoning as V1) | **ON** (the disposable worktree is crush's reliable safety layer — the config allowlist is inert and a denied bash deadlocks to timeout) |
64
+ | Per-call cost | engine-**calculated** from its own model catalogue, so a `0` is equally consistent with "coding plan" and "no rate in the catalogue"; Triss keeps it as `reported_total_usd` and only trusts a zero for a proven subscription/free call | same fold as V1 (`usage_source: opencode2`); per-step `step_finish` coverage — a run without it reports `usage_status: missing` with null counters, never zeros | real `delta_cost_usd` reported — a per-call charge Triss trusts, including an explicit `0` |
65
+ | Sub-agents | opencode agent templates | none — the static agent gate rejects any agent source until subagents are fixture-verified (see [opencode2.md](./opencode2.md)) | `--agents single` (Triss forces this) |
65
66
 
66
67
  **Rule of thumb:** prefer **opencode** for the persistent bash-policy safety
67
68
  layer (it actually enforces); reach for **crush** when you want the simpler
package/docs/mcp.md CHANGED
@@ -353,7 +353,9 @@ the default), `OPENCODE_API_KEY` (OpenCode Zen or paid OpenCode Go — see
353
353
  tool argument). Returns the JSON envelope — `engine`, `engine_version`,
354
354
  `session_id`, `exit_reason`, `final_text`, `files_changed`, `diff_stat`,
355
355
  `worktree`, `usage`, `warnings` — as the tool result. `engine` is the
356
- `opencode`/`crush` enum (default `opencode`, or `TRISS_CODER_ENGINE`);
356
+ `opencode`/`opencode2`/`crush` enum (default `opencode` V1, or
357
+ `TRISS_CODER_ENGINE`; `opencode2` is the V2 beta — see
358
+ [opencode2.md](opencode2.md));
357
359
  `isolate` is unset by default in the schema — opencode resolves unset to
358
360
  isolate-OFF, crush resolves unset to isolate-ON (crush 0.1.3's
359
361
  `permissions.run` config is inert and denied bash deadlocks, so the