triss-coworker 0.28.0 → 0.30.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 +97 -1
- package/README.md +80 -18
- package/bin/triss.js +7 -5
- package/docs/coder-model-management-plan.md +2 -2
- package/docs/coder-one-shot-provider-plan.md +126 -0
- package/docs/coder-process-lifecycle-plan.md +62 -0
- package/docs/configuration.md +60 -9
- package/docs/glm-clients.md +28 -6
- package/docs/mcp.md +24 -6
- package/docs/openai-compatible-coder-plan.md +259 -0
- package/package.json +1 -1
- package/src/coder-lock.js +115 -0
- package/src/coder-models.js +107 -137
- package/src/coder-providers.js +11 -0
- package/src/commands/coder-models.js +2 -1
- package/src/commands/coder.js +1012 -90
- package/src/commands/status.js +8 -18
- package/src/config.js +32 -2
- package/src/mcp/handlers.js +11 -4
- package/src/mcp/server.js +37 -25
- package/src/mcp/tools.js +14 -10
- package/templates/claude-full.md +16 -2
- package/templates/claude.md +8 -1
- package/templates/codex-full.md +14 -2
- package/templates/codex.md +8 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,98 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.30.0] — 2026-08-06
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **OpenCode coder runs can switch provider and the complete main/small pair
|
|
15
|
+
for one invocation.** `triss coder run --provider <name> --model <p/m>
|
|
16
|
+
[--small-model <p/m>]` uses an in-memory OpenCode overlay and never rewrites
|
|
17
|
+
`.env` or `opencode.json`; omitted `--small-model` defaults to the one-shot
|
|
18
|
+
main. CLI help, MCP (`provider` / `model` / `small_model`), README, config
|
|
19
|
+
reference, and generated agent instructions document the same contract.
|
|
20
|
+
- Added docs-first implementation and process-lifecycle contracts with focused
|
|
21
|
+
RED/GREEN coverage for GLM ↔ Triss worker switching.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- The MCP server now advertises the package version instead of a stale
|
|
26
|
+
hard-coded protocol version.
|
|
27
|
+
- `--model` without `--provider` retains its existing main-only semantics.
|
|
28
|
+
An explicit provider requires a fully qualified model, both transient roles
|
|
29
|
+
must share its exact prefix, and Crush remains Z.AI-only. Worker credentials
|
|
30
|
+
and the managed provider can coexist with GLM while either pair is selected
|
|
31
|
+
per run.
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- `coder run` no longer resolves when the immediate OpenCode CLI exits but a
|
|
36
|
+
same-process-group tool descendant remains alive. Triss now terminates and
|
|
37
|
+
waits for residual processes before computing the final envelope, preventing
|
|
38
|
+
late edits and lingering database/WAL contention.
|
|
39
|
+
- MCP request cancellation/timeout signals now reach `runCoderRun()` and the
|
|
40
|
+
selected engine's detached process group instead of leaving OpenCode or
|
|
41
|
+
Crush running after the client regains control. Timeout, host SIGINT/SIGTERM,
|
|
42
|
+
caller cancellation, and normal-close cleanup preserve SIGKILL escalation
|
|
43
|
+
for descendants that survive SIGTERM.
|
|
44
|
+
- A successful child exit now immediately disarms its execution timeout and
|
|
45
|
+
host/AbortSignal listeners while bounded residual cleanup and stdio close
|
|
46
|
+
continue. A delayed `close` can no longer relabel a completed OpenCode or
|
|
47
|
+
Crush run as `timeout` or signal a recycled numeric process group.
|
|
48
|
+
|
|
49
|
+
### Security
|
|
50
|
+
|
|
51
|
+
- Detached OpenCode and Crush group signalling now rejects missing,
|
|
52
|
+
non-integer, zero, and `1` child PIDs before negation. This prevents a failed
|
|
53
|
+
or test-double spawn from issuing POSIX `kill(-1, SIGTERM)` to every process
|
|
54
|
+
the current user can signal, or `kill(0, SIGTERM)` to Triss's own group.
|
|
55
|
+
Custom spawn seams also receive no real group-signalling authority unless
|
|
56
|
+
they explicitly inject the matching process-group owner.
|
|
57
|
+
- One-shot runs overlay only model fields and never assume that overlay has
|
|
58
|
+
final precedence. Before any real selected credential reaches OpenCode,
|
|
59
|
+
Triss audits every file-backed source loaded by pinned OpenCode 1.18.7:
|
|
60
|
+
global `config.json`/`opencode.json(c)`, `~/.opencode/opencode.json(c)`, and
|
|
61
|
+
runtime-directory ancestors through the Git root or `/` outside Git. It then
|
|
62
|
+
resolves the final merged config with `debug config --pure` under the exact
|
|
63
|
+
sanitized child environment and a random canary instead of the real key.
|
|
64
|
+
Final main/small models and the selected provider must match, catching later
|
|
65
|
+
account/org, managed-directory, and macOS MDM overrides. The actual run also
|
|
66
|
+
uses `--pure`; unreadable, JSONC, unknown, or unverified config fails closed.
|
|
67
|
+
Concurrent same-user mutation between preflight and spawn remains outside
|
|
68
|
+
the guard's threat model.
|
|
69
|
+
|
|
70
|
+
## [0.29.0] — 2026-08-06
|
|
71
|
+
|
|
72
|
+
### Added
|
|
73
|
+
|
|
74
|
+
- **Existing OpenAI-compatible worker profiles are now available in coder
|
|
75
|
+
mode.** `triss coder init --provider worker` registers an env-backed
|
|
76
|
+
`triss-worker` provider through `@ai-sdk/openai-compatible`, reusing
|
|
77
|
+
`TRISS_WORKER_API_KEY`, `TRISS_WORKER_BASE_URL`, and the configured
|
|
78
|
+
flash/pro model ids instead of introducing another credential. Init, run,
|
|
79
|
+
model inspection/switching, status, MCP, wizard/help output, and generated
|
|
80
|
+
agent instructions all understand `triss-worker/<model-id>`. V1 supports one
|
|
81
|
+
custom profile and the Chat Completions protocol; Crush remains Z.AI-only.
|
|
82
|
+
|
|
83
|
+
### Changed
|
|
84
|
+
|
|
85
|
+
- Worker setup resolves credentials, endpoint, and model ids from the selected
|
|
86
|
+
local/global scope while preserving genuine parent-shell overrides. Worker
|
|
87
|
+
readiness now has one shared definition across `triss status` and MCP, and
|
|
88
|
+
the MCP coder-status description names `TRISS_WORKER_API_KEY` explicitly.
|
|
89
|
+
- OpenCode init, persistent model changes, and rollback now coordinate through
|
|
90
|
+
the same PID/token `(engine, scope)` filesystem lock with dead-PID recovery.
|
|
91
|
+
|
|
92
|
+
### Security
|
|
93
|
+
|
|
94
|
+
- Worker init and run fail closed before forwarding credentials when the
|
|
95
|
+
effective project/global `provider["triss-worker"]`, endpoint, env-backed key
|
|
96
|
+
binding, package, or complete flash/pro model allowlist is missing, stale, or
|
|
97
|
+
conflicting. A global init cannot persist project-local endpoint settings or
|
|
98
|
+
report success over a higher-precedence unsafe project provider.
|
|
99
|
+
- Jira issue output and its model-summarization corpus no longer include the
|
|
100
|
+
presence or absence of the worker credential.
|
|
101
|
+
|
|
10
102
|
## [0.28.0] — 2026-08-06
|
|
11
103
|
|
|
12
104
|
### Added
|
|
@@ -841,7 +933,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
841
933
|
|
|
842
934
|
- Initial release of `triss-coworker`.
|
|
843
935
|
|
|
844
|
-
[Unreleased]: https://github.com/ayleen/triss-coworker/compare/v0.
|
|
936
|
+
[Unreleased]: https://github.com/ayleen/triss-coworker/compare/v0.30.0...HEAD
|
|
937
|
+
[0.30.0]: https://github.com/ayleen/triss-coworker/compare/v0.29.0...v0.30.0
|
|
938
|
+
[0.29.0]: https://github.com/ayleen/triss-coworker/compare/v0.28.0...v0.29.0
|
|
939
|
+
[0.28.0]: https://github.com/ayleen/triss-coworker/compare/v0.27.1...v0.28.0
|
|
940
|
+
[0.27.1]: https://github.com/ayleen/triss-coworker/compare/v0.27.0...v0.27.1
|
|
845
941
|
[0.27.0]: https://github.com/ayleen/triss-coworker/compare/v0.26.0...v0.27.0
|
|
846
942
|
[0.26.0]: https://github.com/ayleen/triss-coworker/compare/v0.25.0...v0.26.0
|
|
847
943
|
[0.25.0]: https://github.com/ayleen/triss-coworker/compare/v0.24.2...v0.25.0
|
package/README.md
CHANGED
|
@@ -237,7 +237,8 @@ The exposed tool set is **filtered by configured credentials**:
|
|
|
237
237
|
- **`triss_github_*`** when `GITHUB_TOKEN` is set (or `gh` CLI logged in).
|
|
238
238
|
- **`triss_gitlab_*`** when `GITLAB_TOKEN` is set.
|
|
239
239
|
- **`triss_coder_run` + `triss_coder_status`** when any coder provider key is
|
|
240
|
-
set — `
|
|
240
|
+
set — `TRISS_WORKER_API_KEY` (the existing OpenAI-compatible worker),
|
|
241
|
+
`ZHIPU_API_KEY` (Z.AI GLM), `OPENCODE_API_KEY` (OpenCode Zen or Go),
|
|
241
242
|
`MOONSHOT_API_KEY` (Moonshot Kimi), or `KIMI_API_KEY` (Kimi for Coding)
|
|
242
243
|
(setup: `triss coder init`). `triss_coder_run` takes an optional `engine`
|
|
243
244
|
(`opencode` default, or `crush`); its timeout defaults to
|
|
@@ -271,8 +272,8 @@ and writing**.
|
|
|
271
272
|
| `triss review` | Code review on current branch or a PR (diff + linked ticket) | The agent reading the whole diff |
|
|
272
273
|
| `triss commit-msg` | Generates a commit message from staged diff | Hand-writing or copy-pasting from web LLMs |
|
|
273
274
|
| `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 (
|
|
275
|
-
| `triss coder run` | Spawns the coding agent (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 |
|
|
275
|
+
| `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 |
|
|
276
|
+
| `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 |
|
|
276
277
|
| `triss coder clean` | Removes finished `.triss/wt` isolation worktrees (`--all` forces all) | Manually finding and deleting stale git worktrees |
|
|
277
278
|
| `triss init` | Drops a tiny (~15 line) delegation block into `CLAUDE.md` / `AGENTS.md` | Hand-writing routing rules |
|
|
278
279
|
| `triss agent-help` | Prints the full delegation cookbook on demand (the nano block points here) | A 200-line CLAUDE.md that always loads |
|
|
@@ -453,7 +454,8 @@ default timeout, configurable via `--timeout <ms>`.
|
|
|
453
454
|
|
|
454
455
|
### `triss coder`
|
|
455
456
|
|
|
456
|
-
Delegates an implementation task to a cheap coding agent —
|
|
457
|
+
Delegates an implementation task to a cheap coding agent — the existing
|
|
458
|
+
OpenAI-compatible Triss worker, GLM, Kimi,
|
|
457
459
|
OpenCode Zen, or OpenCode Go models (the `opencode` engine by default; `crush` is an
|
|
458
460
|
alternative — see **Engines** below) instead of the primary model writing
|
|
459
461
|
the code itself.
|
|
@@ -467,7 +469,8 @@ triss coder clean # remove finished isolation wo
|
|
|
467
469
|
```
|
|
468
470
|
|
|
469
471
|
`triss coder init` first asks which provider to configure — **Z.AI GLM**
|
|
470
|
-
(default), **
|
|
472
|
+
(default), the existing **Triss worker** (`--provider worker`, aliases
|
|
473
|
+
`openai` / `openai-compatible`), **OpenCode Zen** (`--provider opencode-zen`, free rotating
|
|
471
474
|
models), **OpenCode Go** (`--provider opencode-go`, paid subscription),
|
|
472
475
|
**Moonshot Kimi** (`--provider moonshot`, pay-as-you-go
|
|
473
476
|
`moonshotai/*` models like `kimi-k2.7-code`), or **Kimi for Coding**
|
|
@@ -486,7 +489,10 @@ endpoint. Either way it lets you pick the model interactively. See the
|
|
|
486
489
|
Prints one JSON envelope to stdout — `files_changed`, `diff_stat`, and
|
|
487
490
|
`worktree` tell you what to review; `--isolate` runs the agent in a
|
|
488
491
|
disposable `.triss/wt/<slug>` git worktree so it never touches your
|
|
489
|
-
working tree directly.
|
|
492
|
+
working tree directly. Before returning the envelope, Triss terminates and
|
|
493
|
+
waits for any residual process in the selected engine's process group, so tests or DB
|
|
494
|
+
clients cannot keep locks or write files after completion. MCP cancellation is
|
|
495
|
+
forwarded to the same cleanup path. `--session <slug>` continues the same opencode
|
|
490
496
|
conversation across calls. **POSIX only** (macOS/Linux) for now. See
|
|
491
497
|
`docs/glm-clients.md` for the full picture of how Triss talks to GLM
|
|
492
498
|
(both engines, key/endpoint routing, models, and every usage mode),
|
|
@@ -513,7 +519,9 @@ natively; triss also forwards it as `ZAI_API_KEY` for older binaries; see
|
|
|
513
519
|
`docs/crush-restrict-issues.md` for the live-verified bug facts.
|
|
514
520
|
|
|
515
521
|
**Providers** — the `opencode` engine isn't limited to Z.AI. The required
|
|
516
|
-
API key follows the model's `<provider>/` prefix: `
|
|
522
|
+
API key follows the model's `<provider>/` prefix: `triss-worker/*` reuses
|
|
523
|
+
the existing `TRISS_WORKER_API_KEY` + `TRISS_WORKER_BASE_URL` profile;
|
|
524
|
+
`zai-coding-plan/*` and
|
|
517
525
|
`zai/*` (GLM) use `ZHIPU_API_KEY`; `opencode/*` models — served free by
|
|
518
526
|
[OpenCode Zen](https://opencode.ai/docs/zen/) — and paid `opencode-go/*`
|
|
519
527
|
models use the shared `OPENCODE_API_KEY`;
|
|
@@ -523,6 +531,55 @@ models use the shared `OPENCODE_API_KEY`;
|
|
|
523
531
|
**`kimi-for-coding/k3`** (Kimi K3) — use `KIMI_API_KEY`. Zen ids rotate
|
|
524
532
|
freely, so discover the current id and pass it rather than hard-coding one:
|
|
525
533
|
|
|
534
|
+
```bash
|
|
535
|
+
triss coder init --provider worker
|
|
536
|
+
triss coder run "mechanical task" \
|
|
537
|
+
--provider worker --model triss-worker/deepseek-v4-flash
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
The worker route uses Chat Completions through `@ai-sdk/openai-compatible`.
|
|
541
|
+
It creates no second API key and is supported by the OpenCode engine only.
|
|
542
|
+
After changing the worker base URL or flash/pro model ids, rerun
|
|
543
|
+
`triss coder init --provider worker`; `coder model set --provider worker`
|
|
544
|
+
fails closed until the matching env-backed provider block is present. Global
|
|
545
|
+
init reads only global worker settings (except genuine shell exports) and
|
|
546
|
+
rejects a conflicting higher-precedence project provider. Every worker run
|
|
547
|
+
revalidates the registered global/project endpoint and model allowlist before
|
|
548
|
+
forwarding the key, and prints an exact scope-specific init command when the
|
|
549
|
+
saved provider is missing or stale. The broader runtime-tree snapshot described
|
|
550
|
+
below applies to explicit one-shot `--provider worker` runs.
|
|
551
|
+
|
|
552
|
+
Worker init is a one-time provider registration, not an exclusive provider
|
|
553
|
+
choice: GLM and worker credentials/configuration can coexist. After the worker
|
|
554
|
+
provider has been registered, switch the complete main/small pair for one task
|
|
555
|
+
without changing the persistent default:
|
|
556
|
+
|
|
557
|
+
```bash
|
|
558
|
+
# Worker for this task; small defaults to the same model.
|
|
559
|
+
triss coder run "mechanical task" \
|
|
560
|
+
--provider worker --model triss-worker/deepseek-v4-flash
|
|
561
|
+
|
|
562
|
+
# GLM for this task, with an explicit fast model.
|
|
563
|
+
triss coder run "hard task" \
|
|
564
|
+
--provider zai --model zai-coding-plan/glm-5.2 \
|
|
565
|
+
--small-model zai-coding-plan/glm-5-turbo
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
`--provider` is OpenCode-only, requires a provider-qualified `--model`, and
|
|
569
|
+
never rewrites `.env` or `opencode.json`. `--small-model` is available only
|
|
570
|
+
with `--provider`; when omitted it equals the one-shot main model. The worker
|
|
571
|
+
run still validates the previously registered env-backed provider before the
|
|
572
|
+
key is forwarded. During a one-shot provider run, Triss first audits every
|
|
573
|
+
file-backed source loaded by the pinned OpenCode version: global `config.json`
|
|
574
|
+
and `opencode.json(c)`, `~/.opencode/opencode.json(c)`, and runtime-directory
|
|
575
|
+
ancestors up to the Git root (or `/` outside Git). It then asks OpenCode for the
|
|
576
|
+
final merged config under the exact sanitized child environment, with a random
|
|
577
|
+
canary instead of the real credential, and verifies the final main/small models
|
|
578
|
+
and selected provider. Both the preflight and actual run use `--pure` to disable
|
|
579
|
+
external plugins. Unreadable/JSONC/unknown layers fail closed. Concurrent
|
|
580
|
+
same-user config mutation between preflight and spawn remains outside this
|
|
581
|
+
guard's threat model.
|
|
582
|
+
|
|
526
583
|
```bash
|
|
527
584
|
triss coder init --provider opencode-zen # guided: key + opencode.json
|
|
528
585
|
triss coder models --provider opencode-zen # see live Zen ids
|
|
@@ -563,8 +620,10 @@ MAIN model only (it sits in the OpenCode-main precedence chain: one-run `--model
|
|
|
563
620
|
default), so it changes the main model for every run until unset.
|
|
564
621
|
`TRISS_CODER_SMALL_MODEL` is setup intent consumed by `triss coder init` /
|
|
565
622
|
`triss coder model` — it does not swap the small model mid-run (opencode reads
|
|
566
|
-
`small_model` from `opencode.json`)
|
|
567
|
-
override
|
|
623
|
+
`small_model` from `opencode.json`). Use `--model` alone for a one-run main-only
|
|
624
|
+
override within the current provider; use `--provider` + `--model` and optional
|
|
625
|
+
`--small-model` to switch the complete pair for one run. The deny-first
|
|
626
|
+
`opencode.json` bash policy applies to every provider.
|
|
568
627
|
Full details, the model catalogue, and every configuration path are in
|
|
569
628
|
[docs/opencode-zen.md](docs/opencode-zen.md) and [docs/opencode-go.md](docs/opencode-go.md).
|
|
570
629
|
|
|
@@ -581,26 +640,29 @@ confusion is conflating them:
|
|
|
581
640
|
- **Engine** = *how* the agent is launched: `opencode` (default) or
|
|
582
641
|
`crush`. Set at `triss coder init --engine …` or per run with
|
|
583
642
|
`triss coder run --engine …`.
|
|
584
|
-
- **Provider** = *which* API serves the model: Z.AI GLM, OpenCode Zen, OpenCode Go,
|
|
585
|
-
Moonshot Kimi, Kimi for Coding.
|
|
643
|
+
- **Provider** = *which* API serves the model: Triss worker, Z.AI GLM, OpenCode Zen, OpenCode Go,
|
|
644
|
+
Moonshot Kimi, Kimi for Coding. Register/set the persistent default with
|
|
645
|
+
`triss coder init --provider …`, or select a complete one-shot pair with
|
|
646
|
+
`triss coder run --provider … --model … [--small-model …]`.
|
|
586
647
|
|
|
587
648
|
Not every engine speaks every provider:
|
|
588
649
|
|
|
589
650
|
| Engine | Providers served |
|
|
590
651
|
| ---------- | ------------------------------------------------- |
|
|
591
|
-
| `opencode` | Z.AI GLM, OpenCode Zen, OpenCode Go, Moonshot, Kimi for Coding |
|
|
652
|
+
| `opencode` | Triss worker, Z.AI GLM, OpenCode Zen, OpenCode Go, Moonshot, Kimi for Coding |
|
|
592
653
|
| `crush` | Z.AI GLM (coding-plan only) |
|
|
593
654
|
|
|
594
655
|
`triss coder init` drives setup in that order — **engine, then provider,
|
|
595
656
|
then it asks for only that provider's credential** and writes the matching
|
|
596
|
-
model prefix (`opencode/<id>`, `opencode-go/<id>`, `zai-coding-plan/*`, `moonshotai/*`, or
|
|
657
|
+
model prefix (`triss-worker/<id>`, `opencode/<id>`, `opencode-go/<id>`, `zai-coding-plan/*`, `moonshotai/*`, or
|
|
597
658
|
`kimi-for-coding/*`).
|
|
598
659
|
|
|
599
|
-
**Choosing a model.** `--model` on `triss coder run`
|
|
600
|
-
**single-run override of the main model only
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
660
|
+
**Choosing a model.** `--model` by itself on `triss coder run` remains a
|
|
661
|
+
**single-run override of the main model only**. For a non-persistent provider
|
|
662
|
+
switch, pass `--provider` with a provider-qualified `--model`; add
|
|
663
|
+
`--small-model` when the small role should differ, otherwise it defaults to the
|
|
664
|
+
one-shot main. To persist a choice, use `triss coder model set` — positional
|
|
665
|
+
main plus `--small` — which requires the engine and scope explicitly:
|
|
604
666
|
|
|
605
667
|
```bash
|
|
606
668
|
# global, opencode engine: positional main + --small; --yes for noninteractive use
|
package/bin/triss.js
CHANGED
|
@@ -183,7 +183,7 @@ config
|
|
|
183
183
|
.option('--standard', 'API key + one model only — skip the standard/advanced prompt')
|
|
184
184
|
.option('--advanced', 'full wizard with presets, base URL, integrations — skip the prompt')
|
|
185
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, opencode-go, moonshot, kimi-for-coding). `coder init` uses --provider')
|
|
186
|
+
.option('--coder-provider <name>', 'coder target only: opencode engine provider (zai, worker, opencode-zen, opencode-go, moonshot, kimi-for-coding). `coder init` uses --provider')
|
|
187
187
|
.action(wrap(runWizard));
|
|
188
188
|
|
|
189
189
|
config
|
|
@@ -238,19 +238,21 @@ coder
|
|
|
238
238
|
.option('-g, --global', 'save to the global scope (~/.config/triss/.env, ~/.config/opencode/)')
|
|
239
239
|
.option('-l, --local', 'save to the project scope (./.triss.env, ./opencode.json)')
|
|
240
240
|
.option('--engine <name>', 'coding engine to configure: opencode (default) or crush')
|
|
241
|
-
.option('--provider <name>', 'opencode engine model provider: zai (
|
|
241
|
+
.option('--provider <name>', 'opencode engine model provider: zai, worker (existing OpenAI-compatible TRISS_WORKER_* profile), opencode-zen, opencode-go, moonshot, or kimi-for-coding')
|
|
242
242
|
.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)')
|
|
243
243
|
.option('--allow-unsafe-bash', 'proceed even if an existing opencode.json has no deny-first bash policy (the agent runs with --auto)')
|
|
244
244
|
.action(wrap(runCoderInit));
|
|
245
245
|
|
|
246
246
|
coder
|
|
247
247
|
.command('run [prompt]')
|
|
248
|
-
.description('Spawn a coding agent — GLM, Kimi, OpenCode Zen, or OpenCode Go
|
|
248
|
+
.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')
|
|
249
249
|
.option('--engine <name>', 'coding engine: opencode (default) or crush')
|
|
250
250
|
.option('--session <id>', 'triss-side session slug, mapped to a real opencode session id in .triss/sessions.json')
|
|
251
251
|
.option('--continue', 'continue the most recent opencode session (maps to opencode --continue)')
|
|
252
252
|
.option('--agent <name>', 'opencode agent template to use', 'coder')
|
|
253
|
+
.option('--provider <name>', 'OpenCode only: select a provider for one run; requires --model and does not modify .env or opencode.json')
|
|
253
254
|
.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)')
|
|
255
|
+
.option('--small-model <p/m>', 'with --provider, override small_model for one run (defaults to the one-shot main model)')
|
|
254
256
|
.option('--isolate', 'run in a disposable git worktree under .triss/wt/<slug>')
|
|
255
257
|
.option('--no-isolate', 'disable worktree isolation (opencode defaults to OFF; crush defaults to ON)')
|
|
256
258
|
.option('--restrict', 'crush only: enforce the allowlist via CLI --allow-bash/--allow-tool flags (--restrict-run). Opt-in (default OFF)')
|
|
@@ -274,7 +276,7 @@ coder
|
|
|
274
276
|
' • opencode: project opencode.json (local) or ~/.config/opencode/opencode.json (global)\n' +
|
|
275
277
|
' • crush: ./.crush/crush.json (local) or ~/.local/share/crush/crush.json (global)')
|
|
276
278
|
.option('--engine <name>', 'coding engine: opencode (default) or crush')
|
|
277
|
-
.option('--provider <name>', 'provider kind: zai, opencode-zen, opencode-go, moonshot, or kimi-for-coding')
|
|
279
|
+
.option('--provider <name>', 'provider kind: zai, worker, opencode-zen, opencode-go, moonshot, or kimi-for-coding')
|
|
278
280
|
.option('--json', 'print the stable machine-readable state object (no secrets)')
|
|
279
281
|
.action(wrap(runCoderModels));
|
|
280
282
|
|
|
@@ -296,7 +298,7 @@ coderModel
|
|
|
296
298
|
)
|
|
297
299
|
.option('--small <model>', 'small/fast model id (omit to keep the current compatible value)')
|
|
298
300
|
.option('--engine <name>', 'coding engine: opencode (default) or crush')
|
|
299
|
-
.option('--provider <name>', 'provider kind: zai, opencode-zen, opencode-go, moonshot, or kimi-for-coding')
|
|
301
|
+
.option('--provider <name>', 'provider kind: zai, worker, opencode-zen, opencode-go, moonshot, or kimi-for-coding')
|
|
300
302
|
.option('-g, --global', 'write to the global scope (OpenCode: ~/.config/opencode/opencode.json + global .env; Crush: ~/.local/share/crush/crush.json)')
|
|
301
303
|
.option('-l, --local', 'write to the project scope (OpenCode: ./opencode.json + ./.triss.env; Crush: ./.crush/crush.json)')
|
|
302
304
|
.option('--allow-unverified', 'with explicit main + --small, bypass only a provider-defined transient catalogue failure (Go: transport or HTTP 408/429/500/502/503/504; Zen: timeout/http-error/parse-error; never auth or authoritative failure)')
|
|
@@ -30,7 +30,7 @@ management and recovery workflow.
|
|
|
30
30
|
|
|
31
31
|
| Engine | Supported providers | Persistent model store |
|
|
32
32
|
| --- | --- | --- |
|
|
33
|
-
| `opencode` | Z.AI GLM, OpenCode Zen, Moonshot Kimi, Kimi for Coding | `opencode.json` plus `TRISS_CODER_MODEL` / `TRISS_CODER_SMALL_MODEL` |
|
|
33
|
+
| `opencode` | Triss worker (OpenAI-compatible), Z.AI GLM, OpenCode Zen, OpenCode Go, Moonshot Kimi, Kimi for Coding | `opencode.json` plus `TRISS_CODER_MODEL` / `TRISS_CODER_SMALL_MODEL` |
|
|
34
34
|
| `crush` | Z.AI GLM only | crush large/fast model roles in `crush.json` |
|
|
35
35
|
|
|
36
36
|
OpenCode Zen is not an engine and `hy3-free` was not a GLM model. It was one
|
|
@@ -648,7 +648,7 @@ verification contract as OpenCode applies (Corrective Blocker A):
|
|
|
648
648
|
released on every exit (success, failure, rollback-failed).
|
|
649
649
|
- `deps.lock` is an OVERRIDE seam for deterministic unit tests ONLY. Absence of
|
|
650
650
|
`deps.lock` MUST NEVER mean unlocked: when `deps.lock` is not supplied the
|
|
651
|
-
operation MUST use the
|
|
651
|
+
operation MUST use the shared filesystem lock via `acquireCoderMutationLock`.
|
|
652
652
|
- The lock seam is `deps.lock(engine, scope)` returning `{ release() }`. When
|
|
653
653
|
`deps.lock` is injected, the apply MUST call it; tests assert acquire happens
|
|
654
654
|
before the config read and release happens after verification completes.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# One-shot coder provider selection
|
|
2
|
+
|
|
3
|
+
Status: implementation contract for v0.30.0.
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`triss coder run --model` currently overrides only OpenCode's main model. The
|
|
8
|
+
persisted `small_model` remains active, so changing from GLM to the Triss worker
|
|
9
|
+
(or back) for one run can create a cross-provider pair and is rejected. Users
|
|
10
|
+
must persistently switch both roles even when they only want another provider
|
|
11
|
+
for one task.
|
|
12
|
+
|
|
13
|
+
## User contract
|
|
14
|
+
|
|
15
|
+
The OpenCode engine accepts an explicit one-shot provider pair:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
triss coder run "mechanical task" \
|
|
19
|
+
--provider worker \
|
|
20
|
+
--model triss-worker/deepseek-v4-flash
|
|
21
|
+
|
|
22
|
+
triss coder run "hard task" \
|
|
23
|
+
--provider zai \
|
|
24
|
+
--model zai-coding-plan/glm-5.2 \
|
|
25
|
+
--small-model zai-coding-plan/glm-5-turbo
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
- `--provider` is one-run only and never rewrites the project `.triss.env`,
|
|
29
|
+
the global Triss `.env`, or any `opencode.json`.
|
|
30
|
+
- `--provider` requires a provider-qualified `--model`. This keeps Z.AI
|
|
31
|
+
coding-plan versus pay-as-you-go selection explicit and avoids guessing a
|
|
32
|
+
model from whichever credentials happen to be present.
|
|
33
|
+
- `--small-model` is valid only with `--provider`. When omitted, the one-shot
|
|
34
|
+
small model equals the explicit main model.
|
|
35
|
+
- Main and small must resolve to the selected provider and must use the same
|
|
36
|
+
raw provider prefix. A regional/plan mix is rejected before credentials are
|
|
37
|
+
forwarded.
|
|
38
|
+
- The feature is OpenCode-only. Crush remains Z.AI-only and rejects
|
|
39
|
+
`--provider`/`--small-model` before spawning.
|
|
40
|
+
- Existing `--model` without `--provider` keeps its v0.29 main-only semantics.
|
|
41
|
+
|
|
42
|
+
The MCP `triss_coder_run` tool exposes the same operation through `provider`,
|
|
43
|
+
`model`, and `small_model` fields.
|
|
44
|
+
|
|
45
|
+
## Runtime design and security invariants
|
|
46
|
+
|
|
47
|
+
For an explicit provider run, Triss supplies an in-memory
|
|
48
|
+
`OPENCODE_CONFIG_CONTENT` overlay containing only the selected `model` and
|
|
49
|
+
`small_model`. No temporary or persistent config file is published. The
|
|
50
|
+
overlay is not assumed to have final precedence: OpenCode 1.18.7 can merge
|
|
51
|
+
account/organization, managed-directory, and macOS MDM configuration after it.
|
|
52
|
+
|
|
53
|
+
The overlay must not define or replace providers. OpenCode deep-merges provider
|
|
54
|
+
objects, which could retain untrusted lower-precedence options such as custom
|
|
55
|
+
headers. Before forwarding any selected credential, Triss audits the pinned
|
|
56
|
+
OpenCode version's global `config.json` and `opencode.json(c)`, the legacy
|
|
57
|
+
`~/.opencode/opencode.json(c)` source, and direct config ancestors from the
|
|
58
|
+
actual runtime directory to its Git root (or filesystem root outside Git).
|
|
59
|
+
Built-in providers reject any persistent block for the selected provider id;
|
|
60
|
+
managed worker blocks must match the complete expected definition in every
|
|
61
|
+
layer. JSONC is rejected because Triss cannot prove that commented config
|
|
62
|
+
contains no hidden endpoint/header override.
|
|
63
|
+
|
|
64
|
+
After this file audit, Triss runs `opencode debug config --pure` under the exact
|
|
65
|
+
sanitized child environment. The real selected credential is absent; a random
|
|
66
|
+
canary is supplied instead so the managed worker's env binding can still be
|
|
67
|
+
verified. The final merged main model, small model, and selected-provider block
|
|
68
|
+
must match. The actual one-shot `opencode run` also receives `--pure`, disabling
|
|
69
|
+
external plugins in both phases. Any probe failure or unknown final shape is
|
|
70
|
+
fail-closed. This remains a pre-spawn check; concurrent same-user mutation
|
|
71
|
+
between preflight and spawn is outside the guard's threat model. Unverified
|
|
72
|
+
OpenCode versions fail closed so changed discovery rules cannot silently bypass
|
|
73
|
+
the audit. The runtime directory is explicit `--cwd`, inherited `process.cwd()`,
|
|
74
|
+
or the created/reused isolation worktree. The Triss worker
|
|
75
|
+
therefore retains its existing setup prerequisite:
|
|
76
|
+
`triss coder init --engine opencode --provider worker --global|--local`
|
|
77
|
+
registers the env-backed provider once. Every worker run then verifies the
|
|
78
|
+
effective provider package, endpoint, credential binding, and complete model
|
|
79
|
+
allowlist before forwarding `TRISS_WORKER_API_KEY`. In one-shot mode the audit
|
|
80
|
+
checks the selected transient pair rather than requiring the persisted
|
|
81
|
+
main/small pair to be worker models.
|
|
82
|
+
|
|
83
|
+
Only the credential required by the explicit main model is forwarded. The
|
|
84
|
+
selected provider must equal the provider implied by both model prefixes, so a
|
|
85
|
+
flag cannot relabel a model to obtain a different credential.
|
|
86
|
+
|
|
87
|
+
## Failure contract
|
|
88
|
+
|
|
89
|
+
All usage failures happen before isolation. Provider-config auditing happens
|
|
90
|
+
after an isolation worktree is resolved but before engine spawn or credential
|
|
91
|
+
forwarding; a newly created clean worktree is removed if that audit fails, while
|
|
92
|
+
a reused worktree is preserved for inspection. Failure cases are:
|
|
93
|
+
|
|
94
|
+
- missing `--model` with `--provider`;
|
|
95
|
+
- `--small-model` without `--provider`;
|
|
96
|
+
- unknown provider or model/provider mismatch;
|
|
97
|
+
- empty or whitespace-containing provider/model id;
|
|
98
|
+
- different main/small raw prefixes;
|
|
99
|
+
- non-Z.AI provider flags on Crush;
|
|
100
|
+
- missing selected-provider credential;
|
|
101
|
+
- missing, stale, or conflicting managed worker provider;
|
|
102
|
+
- selected-provider overrides or unauditable JSONC in effective config layers;
|
|
103
|
+
- an installed OpenCode version other than the audited pin.
|
|
104
|
+
|
|
105
|
+
Errors name the invalid flags and include the exact worker init recovery
|
|
106
|
+
command when applicable. No failure writes model pins or OpenCode config.
|
|
107
|
+
|
|
108
|
+
## TDD and acceptance
|
|
109
|
+
|
|
110
|
+
RED tests must establish:
|
|
111
|
+
|
|
112
|
+
1. Commander help exposes `--provider` and `--small-model` with one-shot and
|
|
113
|
+
non-persistent wording.
|
|
114
|
+
2. GLM-persisted config can run a worker one-shot pair, and worker-persisted
|
|
115
|
+
config can run a GLM one-shot pair.
|
|
116
|
+
3. Spawn receives a matching in-memory main/small overlay and only the selected
|
|
117
|
+
provider credential.
|
|
118
|
+
4. The persistent env/config bytes are unchanged after success and failure.
|
|
119
|
+
5. Every failure case above occurs before spawn.
|
|
120
|
+
6. Legacy main-only `--model` behavior remains unchanged.
|
|
121
|
+
7. MCP forwards `provider` and `small_model` without changing isolation
|
|
122
|
+
defaults.
|
|
123
|
+
|
|
124
|
+
Acceptance requires focused tests, the full Node test/lint suite, package
|
|
125
|
+
smoke, a live synthetic worker run from a GLM-persisted temporary project, and
|
|
126
|
+
sequential GLM 5.2 plus independent agent reviews.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Coder process-group lifecycle
|
|
2
|
+
|
|
3
|
+
Status: remediation contract for v0.30.0.
|
|
4
|
+
|
|
5
|
+
## User report
|
|
6
|
+
|
|
7
|
+
An OpenCode Go user observed that Triss returned control while several
|
|
8
|
+
OpenCode-related processes remained alive. Repository edits sometimes appeared
|
|
9
|
+
after the JSON completion envelope, and lingering database users caused WAL
|
|
10
|
+
contention.
|
|
11
|
+
|
|
12
|
+
## Reproduction and root cause
|
|
13
|
+
|
|
14
|
+
A minimal live `opencode-go/deepseek-v4-flash` completion on OpenCode 1.18.7
|
|
15
|
+
left no process behind. A live synthetic `npm test` child was also cleaned up
|
|
16
|
+
by OpenCode itself. The report is nevertheless valid at Triss's boundary:
|
|
17
|
+
|
|
18
|
+
1. A deterministic process-group test demonstrated that the immediate
|
|
19
|
+
OpenCode CLI can close its stdio and exit while a same-group tool descendant
|
|
20
|
+
remains alive. `spawnEngine()` resolved on the immediate child's `close`
|
|
21
|
+
event and never checked the rest of the detached process group.
|
|
22
|
+
2. The MCP SDK supplies an `AbortSignal` when the client cancels or times out a
|
|
23
|
+
tool request. The Triss MCP server discarded that signal, so a client could
|
|
24
|
+
return control while `runCoderRun()` and OpenCode continued in the server.
|
|
25
|
+
3. On timeout, `close` cleared the pending SIGKILL escalation, which could also
|
|
26
|
+
leave a descendant that survived SIGTERM.
|
|
27
|
+
|
|
28
|
+
## Contract
|
|
29
|
+
|
|
30
|
+
- The coder completion envelope is emitted only after the selected engine's
|
|
31
|
+
detached process group can no longer execute or write files.
|
|
32
|
+
- On immediate-child close, Triss probes the group, sends SIGTERM to residual
|
|
33
|
+
members, waits a short grace period, escalates to SIGKILL, and waits for group
|
|
34
|
+
disappearance before resolving.
|
|
35
|
+
- Signal permission failures and a group that remains observable after SIGKILL
|
|
36
|
+
fail the coder run closed; Triss must not emit a successful completion
|
|
37
|
+
envelope for either condition.
|
|
38
|
+
- MCP request cancellation is forwarded server → tool handler →
|
|
39
|
+
`runCoderRun()` → `spawnEngine()` / `spawnCrush()`, which terminates the same
|
|
40
|
+
process group.
|
|
41
|
+
- Timeout, rate-limit, host-signal, normal completion, spawn error, and MCP
|
|
42
|
+
cancellation use the same cleanup boundary.
|
|
43
|
+
- Triss never kills processes outside the detached group it created for the
|
|
44
|
+
current engine call.
|
|
45
|
+
- A missing, non-integer, zero, or `1` child pid is never negated or passed to
|
|
46
|
+
`process.kill`; in particular, Triss can never issue POSIX `kill(-1, ...)`
|
|
47
|
+
against every signalable process in the user's login session.
|
|
48
|
+
- A custom/injected spawn function cannot signal an arbitrary real process
|
|
49
|
+
group unless its caller also injects the matching group-signalling owner.
|
|
50
|
+
|
|
51
|
+
## Verification
|
|
52
|
+
|
|
53
|
+
- A real-process regression spawns a fake OpenCode group leader that emits a
|
|
54
|
+
valid fixture, leaves a 30-second same-group descendant, and exits. The test
|
|
55
|
+
proves the descendant is gone before `runCoderRun()` returns.
|
|
56
|
+
- An abort regression proves cancellation sends SIGTERM to the negative process
|
|
57
|
+
group and produces a killed envelope when partial events exist.
|
|
58
|
+
- MCP unit tests prove the SDK signal reaches the coder lifecycle.
|
|
59
|
+
- Crush regressions prove cancellation, residual-child cleanup, and the
|
|
60
|
+
degenerate-pid guard use the same lifecycle boundary.
|
|
61
|
+
- Live OpenCode Go smoke is repeated after the fix, followed by a process-list
|
|
62
|
+
and delayed-file check.
|