triss-coworker 0.37.2 → 0.38.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,76 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.38.0] — 2026-08-22
11
+
12
+ ### Added
13
+
14
+ - **Canonical OpenCode provider routing** — both `opencode` engines now resolve
15
+ Triss worker, Z.AI, OpenCode Zen, OpenCode Go, Moonshot, and Kimi for Coding
16
+ through one provider registry. Protected runs pin a transient provider in
17
+ `OPENCODE_CONFIG_CONTENT`, including model-specific Chat Completions,
18
+ Responses, and Anthropic transport metadata for audited Zen/Go models.
19
+ - **Explicit raw best-effort credential mode** — literal
20
+ `TRISS_CODER_ALLOW_BEST_EFFORT_ISOLATION=1` allows either OpenCode engine to
21
+ run every canonical provider, including ordinary plugins, agents, custom
22
+ tools, and shell policies, while reporting credential isolation as
23
+ unavailable and warning that the selected raw credential is visible to the
24
+ same-UID child.
25
+
26
+ ### Changed
27
+
28
+ - OpenCode 2 now installs from `@opencode-ai/cli@beta` and accepts compatible
29
+ releases at or above `0.0.0-beta-17793` when the required CLI capability
30
+ probe passes. `TRISS_CODER_OPENCODE2_VERSION` is a minimum-version override,
31
+ replacing the obsolete exact `next-*` pin.
32
+ - `triss coder init`, `coder run`, `coder models`, `triss status`, and the MCP
33
+ coder path use the same provider ownership, credential, endpoint, package,
34
+ header, and transport rules. One-shot main/small model routing is validated
35
+ as one provider pair; OpenCode 2 continues to report its small role as
36
+ unavailable.
37
+ - `triss-dsh-provider-bundle` is version-aligned and republished unchanged;
38
+ the routing and credential-mode behavior above belongs to the Triss runtime,
39
+ while the companion still exposes its existing OpenCode Zen, OpenCode Go,
40
+ and Z.AI catalogue routes.
41
+
42
+ ### Fixed
43
+
44
+ - OpenCode 1 no longer sends Triss's run-scoped proxy token to the real
45
+ upstream because `OPENCODE_BASE_URL` was ignored. The transient provider
46
+ overlay now pins the credential proxy endpoint that the engine actually
47
+ uses, including the unprefixed `/responses` route.
48
+ - The bounded effective-config audit now mirrors the real launch mode:
49
+ one-shot runs probe and launch with `--pure`; ordinary runs do neither, so
50
+ managed, account, MDM, and disk policy layers are audited without disabling
51
+ the deny-first project policy.
52
+ - Protected credential-store checks now use the canonical local and global
53
+ Triss env paths (including `~/.config/triss/.env`), allow non-secret/control
54
+ entries, and reject readable stores containing provider credentials before
55
+ the engine starts.
56
+ - Worker routes verify the configured base URL through the final effective
57
+ provider projection, and Zen/Go protected runs fail closed for unaudited or
58
+ unsupported model transports instead of guessing Chat Completions.
59
+
60
+ ### Security
61
+
62
+ - Protected mode rejects persistent collisions with Triss's transient provider
63
+ alias and verifies the final merged provider/model projection before any
64
+ credential-bearing child is spawned. Raw best-effort mode remains an
65
+ explicit risk acknowledgement and never claims proxy isolation.
66
+
67
+ ### Artifact integrity (0.38.0)
68
+
69
+ - `triss-dsh-provider-bundle-0.38.0.tgz` — sha256
70
+ `bd9efaf1a692f82c15e49333c9a23071d9e30eee49e8e4576535dd91176fc71e`,
71
+ integrity
72
+ `sha512-sunelgV+I7TsL0OfkjDiv2XcnRQc+nQ4o3KjgfVZq390t8g2sVBTQTxPtNp+LU5IE41zm+fTqpXusAZzVQRuUg==`
73
+ (computed with the pinned release npm 11.6.2 via `npm pack`; `npm pack`
74
+ output is byte-deterministic).
75
+ - Root `triss-coworker-0.38.0.tgz` sha256 is reproducible via `npm pack` at
76
+ tag `v0.38.0` (the root tarball ships `CHANGELOG.md`, so its hash cannot be
77
+ recorded inside this file); registry verification compares the packed
78
+ artifact against the published tarball byte-for-byte.
79
+
10
80
  ## [0.37.2] — 2026-08-20
11
81
 
12
82
  ### Added
package/README.md CHANGED
@@ -11,6 +11,9 @@
11
11
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
12
12
  [![Changelog](https://img.shields.io/badge/changelog-Keep%20a%20Changelog-orange.svg)](CHANGELOG.md)
13
13
 
14
+ **Website:** [triss.work](https://triss.work/) · **Quickstart:**
15
+ [triss.work/docs/getting-started](https://triss.work/docs/getting-started/)
16
+
14
17
  Triss is a small CLI (`triss`) that hands token-heavy I/O off to a cheap
15
18
  DeepSeek model so your expensive primary agent (Claude Code, Codex, or
16
19
  anything else that can run a shell/MCP tool) stays focused on reasoning and
@@ -48,7 +51,7 @@ tokens of tracker chatter into its own context. Adding a new provider
48
51
 
49
52
  ## Requirements
50
53
 
51
- - **Node.js ≥ 22** (LTS). Check with `node --version`.
54
+ - **Node.js ≥ 22.12** (LTS). Check with `node --version`.
52
55
  - Don't have it? Install via [nvm](https://github.com/nvm-sh/nvm), [fnm](https://github.com/Schniz/fnm), Homebrew (`brew install node`), or [nodejs.org](https://nodejs.org/).
53
56
  - **npm** (ships with Node.js) — used only for the package-manager install path.
54
57
  `pnpm` and `yarn` also work.
@@ -148,9 +151,9 @@ triss config wizard
148
151
 
149
152
  The wizard first asks **Standard** vs **Advanced**:
150
153
 
151
- - **Standard** — for most users. Just two prompts: API key + worker model
152
- name. Triss writes the model into both `flash` and `pro` presets so
153
- `--model pro` works the same as `--model flash`. No questions about
154
+ - **Standard** — for most users. Two prompts for API key + worker model
155
+ (writes the model into both `flash` and `pro` so `--model pro` works), then
156
+ asks which agent to wire up: **Claude / Codex / Both**. No questions about
154
157
  Jira/Linear/base URL/etc. *Recommended starting point.*
155
158
  - **Advanced** — full control: separate `flash`/`pro` presets, custom
156
159
  base URL, integrations (Jira, Linear, …), default-preset choice.
@@ -206,7 +209,7 @@ top-level commands; `triss config <Tab>`, `triss jira <Tab>`,
206
209
 
207
210
  | Mode in `triss config wizard` | What it does about your agent |
208
211
  | ----------------------------- | ------------------------------ |
209
- | **Standard** (default) | Installs **both** paths automatically — MCP server *and* global agent rules. No question asked. |
212
+ | **Standard** (default) | After the two prompts, asks **Claude / Codex / Both** and installs MCP + global rules accordingly. |
210
213
  | **Advanced** (`--advanced`) | Asks at the end: Both / MCP only / rules only / Skip. Default = Both. |
211
214
 
212
215
  The two paths cooperate: MCP is primary, `CLAUDE.md` / `AGENTS.md` rules are
@@ -747,10 +750,12 @@ implicit persistent conversation), `result_retention`
747
750
  / `result_id` (`retained` only for isolated changed runs with enforced
748
751
  result-store quota and a successful reservation), and `execution_capabilities`
749
752
  (eight honest `enforced|best_effort|unavailable` values plus
750
- `effective_isolation`). The `opencode2` beta engine still returns the older
751
- envelope shape (`session_id`, `exit_reason`, `final_text`, `files_changed`,
752
- `diff_stat`, `worktree`, `usage`, `warnings`) without these fields — use its
753
- `files_changed` / `diff_stat` / `worktree` as evidence there. Non-isolated `files_changed` is `null`; the only
753
+ `effective_isolation`). OpenCode 2 emits the same envelope v2 fields,
754
+ including `envelope_version`, lifecycle fields, activity, and
755
+ OpenCode best-effort raw runs explicitly report
756
+ `credential_isolation: "unavailable"` plus the stable downgrade warning;
757
+ Crush remains proxy-backed and reports the proxy's `best_effort` capability.
758
+ Non-isolated `files_changed` is `null`; the only
754
759
  changes-expectation evidence is `run_files_changed`. Process completion and a
755
760
  non-empty final text are not task satisfaction — use `--isolate`, check
756
761
  `run_files_changed` in the envelope, and verify the retained worktree/diff
@@ -889,6 +894,14 @@ external plugins. Unreadable/JSONC/unknown layers fail closed. Concurrent
889
894
  same-user config mutation between preflight and spawn remains outside this
890
895
  guard's threat model.
891
896
 
897
+ Every OpenCode 1 run performs this final effective-config check before the
898
+ credential-bearing engine spawn, using a bounded `opencode debug config`
899
+ subprocess and a disposable probe credential. The probe mirrors the actual
900
+ run's plugin mode: explicit one-shot provider runs use `--pure` in both
901
+ processes; ordinary runs omit it in both, so their disk-backed deny-first bash
902
+ policy and later account/org, managed-directory, and macOS MDM layers remain
903
+ visible to the audit.
904
+
892
905
  ```bash
893
906
  triss coder init --provider opencode-zen # guided: key + opencode.json
894
907
  triss coder models --provider opencode-zen # see live Zen ids
@@ -959,7 +972,7 @@ Not every engine speaks every provider:
959
972
  | Engine | Providers served |
960
973
  | ---------- | ------------------------------------------------- |
961
974
  | `opencode` | Triss worker, Z.AI GLM, OpenCode Zen, OpenCode Go, Moonshot, Kimi for Coding |
962
- | `opencode2` | Same provider routing as V1 (shares `opencode.json`; one-shot `--provider` / `--small-model` overlays rejected — see [docs/engines/opencode2.md](docs/engines/opencode2.md)) |
975
+ | `opencode2` | Same canonical provider routing as V1 (transient `OPENCODE_CONFIG_CONTENT` overlay; protected proxy by default; explicit literal-1 `TRISS_CODER_ALLOW_BEST_EFFORT_ISOLATION` enables raw best-effort with an honest downgrade warning; see [docs/engines/opencode2.md](docs/engines/opencode2.md)) |
963
976
  | `crush` | Z.AI GLM (coding-plan only) |
964
977
 
965
978
  `triss coder init` drives setup in that order — **engine, then provider,
@@ -1228,8 +1241,7 @@ OpenCode stores (quota usage on subscriptions, not necessarily cash paid).
1228
1241
  ### An earlier example week (the Triss worker)
1229
1242
 
1230
1243
  One full week of real usage on this codebase, captured from the DeepSeek
1231
- dashboard (May 6–13, 2026, all at list price — DeepSeek's off-peak window
1232
- would add another ~75% discount we did not use):
1244
+ dashboard (May 6–13, 2026, all at the list price then in effect):
1233
1245
 
1234
1246
  | Metric | Pro | Flash | **Total** |
1235
1247
  | -------------- | --------- | --------- | ---------- |
@@ -1257,9 +1269,9 @@ raw 5.8M input. That compounds across turns.
1257
1269
  For reference, here is a single measured call of the kind that drives
1258
1270
  the weekly numbers above:
1259
1271
 
1260
- | Task | Source bytes | DeepSeek (pro, -75%) | DeepSeek (pro, list price) | Same job in Opus 4.x |
1261
- | ---- | ------------ | -------------------- | -------------------------- | -------------------- |
1262
- | `triss ask --urls --paths` over the original `claude-coworker-model` README + 12 of our source files (18.3K in / 2.4K out, structured 4-section report) | ≈ 65 KB | **\$0.010** | \$0.040 | ≈ \$0.45 |
1272
+ | Task | Source bytes | DeepSeek (pro, list price) | Same job in Opus 4.x |
1273
+ | ---- | ------------ | -------------------------- | -------------------- |
1274
+ | `triss ask --urls --paths` over the original `claude-coworker-model` README + 12 of our source files (18.3K in / 2.4K out, structured 4-section report) | ≈ 65 KB | **\$0.040** | ≈ \$0.45 |
1263
1275
 
1264
1276
  Real savings depend on which operations you delegate (bulk reads win
1265
1277
  the most; tiny lookups break even); see `templates/claude.md` for the
package/bin/triss.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- const [nodeMajor] = process.versions.node.split('.').map(Number);
3
- if (nodeMajor < 22) {
2
+ const [nodeMajor, nodeMinor] = process.versions.node.split('.').map(Number);
3
+ if (nodeMajor < 22 || (nodeMajor === 22 && nodeMinor < 12)) {
4
4
  process.stderr.write(
5
- `triss requires Node.js >= 22 (you are on ${process.versions.node}).\n` +
5
+ `triss requires Node.js >= 22.12.0 (you are on ${process.versions.node}).\n` +
6
6
  `Upgrade via nvm/fnm or https://nodejs.org/.\n`,
7
7
  );
8
8
  process.exit(1);
@@ -272,7 +272,8 @@ self-hosted endpoints).
272
272
  | `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) |
273
273
  | `TRISS_CODER_OPENCODE_VERSION` | no | `1.18.7` | Pin override for the `opencode-ai` npm install |
274
274
  | `TRISS_CODER_ENGINE` | no | `opencode` | Coding engine: `opencode` (default), `opencode2` (beta — see [opencode2.md](engines/opencode2.md)), or `crush` |
275
- | `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) |
275
+ | `TRISS_CODER_OPENCODE2_VERSION` | no | `0.0.0-beta-17793` | Minimum accepted OpenCode 2 version; install from `@opencode-ai/cli@beta` (unsupported `next/dev/tui-v2` overrides fail closed) |
276
+ | `TRISS_CODER_ALLOW_BEST_EFFORT_ISOLATION` | no | unset | Only literal `1` enables OpenCode raw-credential best-effort mode; OpenCode reports credential isolation as unavailable and allows same-UID tools/plugins/shell to read the selected key. Crush continues to require its credential proxy. |
276
277
  | `TRISS_CODER_CRUSH_VERSION` | no | `0.1.6` | Pin override for the `@phpcraftdream/crush` npm install (crush engine) |
277
278
  | `TRISS_CODER_SESSION_CAP` | no | `4` | Persistent v2 session inventory cap per engine (fail closed) |
278
279
 
@@ -389,13 +390,23 @@ provider key, Triss audits the pinned version's full file graph: global
389
390
  configs from the actual runtime directory to the Git root (or `/` for non-Git
390
391
  directories). JSONC and unreadable layers fail closed. Because account/org,
391
392
  managed-directory, and macOS MDM settings load after the in-memory overlay,
392
- Triss also runs `opencode debug config --pure` under the exact sanitized child
393
- environment with a random canary instead of the real credential. The final
393
+ Triss also validates the final merged config before every OpenCode 1 run under
394
+ the exact sanitized child environment, using a bounded `opencode debug config`
395
+ subprocess and a random canary instead of the real credential. The probe mirrors
396
+ the actual run: an explicit one-shot provider pair uses `--pure` for both probe
397
+ and run, while an ordinary run omits `--pure` for both so its disk-backed
398
+ deny-first bash policy and late managed layers remain visible. The final
394
399
  main/small pair and selected provider must still match before the real key is
395
- injected; the actual one-shot run also uses `--pure`. This includes inherited
396
- cwd and created or reused isolation worktrees. Concurrent same-user mutation
397
- between preflight and spawn remains outside the guard's threat model;
398
- unverified OpenCode versions fail closed.
400
+ injected. This includes inherited cwd and created or reused isolation worktrees.
401
+ Concurrent same-user mutation between preflight and spawn remains outside the
402
+ guard's threat model; unverified OpenCode versions fail closed.
403
+
404
+ For OpenCode 1, main and small models from the same provider may use different
405
+ audited transports. Triss then creates two transient provider aliases and two
406
+ model-scoped loopback proxy routes that share only the same one-run proxy token;
407
+ each route remains pinned to its own protocol, package, path, and model. OpenCode
408
+ 2 validates an explicit small model as belonging to the selected provider but
409
+ does not configure or route it because that beta has no small-model role.
399
410
 
400
411
  **Direct OpenCode `main` and `small`** (you run `opencode` yourself, not via
401
412
  `triss coder run`): `opencode.json` is the source of truth — project
@@ -65,7 +65,8 @@ safety layer.
65
65
  The Zen catalogue is **live and authoritative whenever it is reachable**.
66
66
  Triss reads `GET https://opencode.ai/zen/v1/models` and treats whatever that
67
67
  endpoint returns (authenticated) as the truth; the hardcoded priority list is
68
- used only as the explicitly unverified offline fallback described below.
68
+ used only as the offline availability fallback described below. Its ids still
69
+ need audited transport metadata before secure init can write them.
69
70
  Discover the live catalogue on demand:
70
71
 
71
72
  ```bash
@@ -106,12 +107,18 @@ configuration are untouched, and GLM stays reachable through either engine (see
106
107
  model. The priority list below is an **offline fallback only** — used when the
107
108
  catalogue can't be fetched, and always labelled **not verified**:
108
109
 
109
- | Role | Offline priority (first available wins; the live list may differ) |
110
+ | Role | Offline priority (first available wins; transport-audited ids only) |
110
111
  |---|---|
111
- | main | `deepseek-v4-flash-free` → `north-mini-code-free` → `nemotron-3-ultra-free` → `mimo-v2.5-free` |
112
- | small | `deepseek-v4-flash-free` → `north-mini-code-free` → `mimo-v2.5-free` |
112
+ | main | `deepseek-v4-flash-free` → `nemotron-3-ultra-free` → `mimo-v2.5-free` |
113
+ | small | `deepseek-v4-flash-free` → `mimo-v2.5-free` |
113
114
 
114
- The interactive picker only offers models the live catalogue actually lists.
115
+ The offline path filters this list through Triss's audited transport registry;
116
+ catalogue-only ids such as `north-mini-code-free` are not written by secure
117
+ init when the live catalogue cannot be verified.
118
+
119
+ The secure interactive picker only offers live catalogue models with audited
120
+ transport metadata. Explicit unaudited ids remain available only through the
121
+ best-effort raw path described above.
115
122
  For backward compatibility, Zen init keeps usable ids from a mixed response
116
123
  even when unrelated entries are malformed; a response with no usable ids still
117
124
  falls back as not verified. OpenCode Go intentionally uses a stricter all-entry
@@ -10,14 +10,23 @@ engine remains the default and is unchanged by installing or configuring V2.
10
10
  ## Installation
11
11
 
12
12
  ```bash
13
- npm install -g @opencode-ai/cli@0.0.0-next-17430 # exact pin
13
+ npm install -g @opencode-ai/cli@beta # current V2 beta channel
14
14
  triss coder init --engine opencode2
15
15
  ```
16
16
 
17
- - The pin is an **exact match** (`TRISS_CODER_OPENCODE2_VERSION` to override).
18
- `triss status` reports the installed `opencode2` version against the pin.
19
- - `triss coder init --engine opencode2` never spawns a V2 service; it only
20
- probes `opencode2 --version`.
17
+ - The supported floor is `0.0.0-beta-17793`. `TRISS_CODER_OPENCODE2_VERSION`
18
+ overrides the minimum accepted version; it is not an exact pin. `next-*`,
19
+ `dev-*`, and `tui-v2-*` overrides are unsupported.
20
+ - `triss status` reports the installed version against the effective minimum
21
+ and the required CLI capabilities (`--standalone`, `--format`, `--auto`,
22
+ `--model`).
23
+ - `triss coder init --engine opencode2` never spawns a V2 service; it probes
24
+ `opencode2 --version` and `opencode2 run --help` under isolated runtime
25
+ roots.
26
+ - When the host lacks a compatible `ps -axo` process snapshot (for example a
27
+ minimal BusyBox container), the required CLI flags and version still qualify
28
+ the engine. Init, status, and runs report that resident-service verification
29
+ is best effort instead of misdiagnosing the installed beta as incompatible.
21
30
 
22
31
  ## Shared config implications
23
32
 
@@ -25,15 +34,19 @@ V2 **shares** the V1 `opencode.json` (global and/or project) and the same env
25
34
  pins — one config file, both engines. Consequences:
26
35
 
27
36
  - The **shell policies of the two engines are NOT one policy**: the opencode2
28
- beta requires deny-everything (`permission.bash = {"*":"deny"}`) because the
37
+ beta requires deny-everything (`permission.bash = {"*":"deny"}`) in protected
38
+ mode because the
29
39
  credential sits in the child environment, while V1 init writes its
30
40
  allowlist (`git status`, `git diff`, `npm test`, …) into the same shared
31
- file. A fresh `coder init --engine opencode2` writes the deny-everything
41
+ file. A fresh protected `coder init --engine opencode2` writes the deny-everything
32
42
  form (and warns that plain V1 `coder run` loses the allowlisted commands);
33
43
  a tree initialized by V1 init is **rejected by V2** until the allow rules
34
44
  are removed. Re-running plain `triss coder init` restores the V1 allowlist
35
45
  — and makes the tree V2-incompatible again. This tension is inherent to
36
46
  the shared-config beta and goes away only with real credential isolation.
47
+ Explicit best-effort mode (`TRISS_CODER_ALLOW_BEST_EFFORT_ISOLATION=1`)
48
+ accepts the raw-credential risk, so a fresh V2 config uses the normal V1
49
+ allowlist and an existing allow/ask policy remains byte-identical.
37
50
  - `triss coder model set` / `triss coder models` show a shared-config notice
38
51
  when the resolved engine is `opencode2`.
39
52
  - Model transactions (`model set`, rollback) ride the same
@@ -49,6 +62,15 @@ reports `small_role_effective: false` for `opencode2`; the `small_model` value
49
62
  in the shared config is an **OpenCode 1 compatibility value** and is shown as
50
63
  such. There is no per-V2 small override.
51
64
 
65
+ Zen and Go protected runs use Triss's versioned, model-specific transport
66
+ metadata. Audited models may use Chat Completions, Responses, or Anthropic
67
+ Messages as indicated by that metadata; an unknown or Google/Gemini model is
68
+ rejected before spawn instead of being guessed as Chat. In explicit
69
+ `best_effort_raw` mode, an unknown Zen/Go model uses OpenCode's built-in
70
+ provider metadata while persistent provider overrides are still rejected. An
71
+ explicit `--small-model` is validated for provider ownership but does not
72
+ participate in V2 transport resolution and is reported as `used: false`.
73
+
52
74
  ## Usage gaps
53
75
 
54
76
  Usage comes from per-step `step_finish` events (`usage_source: "opencode2"`).
@@ -59,12 +81,26 @@ maps `opencode2` into the same engine family as `opencode` (explicit
59
81
  engines — the Go reseller's tariffs are not modeled (a
60
82
  `TRISS_PRICE_OPENCODE_GO_<MODEL>` override prices it explicitly).
61
83
 
62
- ## Plugin, agent, and custom-tool gates (fail closed)
84
+ ## Credential modes and executable surfaces
85
+
86
+ Protected mode is the default. It passes only a run-scoped proxy token to the
87
+ engine and keeps the strict deny-first executable-surface policy. Any
88
+ configured or discovered plugin, agent, or custom-tool source rejects before
89
+ the protected child starts.
90
+
91
+ Explicit best-effort mode is selected only by the literal environment value
92
+ `TRISS_CODER_ALLOW_BEST_EFFORT_ISOLATION=1`. It passes only the selected raw
93
+ provider credential, does not start the credential proxy, allows normal shell
94
+ policy and discovered agents/plugins/tools after config parse/shape checks,
95
+ and emits `TRISS_CODER_CREDENTIAL_ISOLATION_DOWNGRADED`. The envelope reports
96
+ `execution_capabilities.credential_isolation: "unavailable"`; same-UID code,
97
+ plugins, tools, and shell commands may read the credential. Endpoint, model,
98
+ package, header, and credential-binding pinning still applies.
63
99
 
64
- No V2 plugin, subagent, or custom tool is verified to preserve the deny-first
65
- policy yet, so **any** configured or discovered plugin, agent, or custom-tool
66
- source rejects the run **before any process spawns or credential is
67
- forwarded** — in both `coder init` and `coder run`:
100
+ The structural/config-shape checks apply in both modes. The executable-source
101
+ gate is mode-dependent: protected mode rejects these sources, while
102
+ best-effort mode inspects them and permits them after the shape checks. In
103
+ particular:
68
104
 
69
105
  - `~/.config/opencode/plugins`, `.opencode/plugin`, configured plugin entries;
70
106
  - `~/.config/opencode/agents/*`, `.opencode/agent/*`, configured agent
@@ -73,8 +109,10 @@ forwarded** — in both `coder init` and `coder run`:
73
109
  `.opencode/tool`, and `.opencode/tools` custom-tool directories.
74
110
 
75
111
  The error names the offending source path (never secrets). Remove or disable
76
- the source, then re-run. One-shot `--provider`/`--small-model` overlays are
77
- likewise rejected for `opencode2` until per-route translation fixtures exist.
112
+ the source for protected mode, or explicitly acknowledge best-effort risk.
113
+ One-shot provider selection is supported by both OpenCode engines; OpenCode 2
114
+ validates a supplied small model but reports that it has no separate small
115
+ role.
78
116
 
79
117
  ## Rollback
80
118
 
@@ -99,14 +137,16 @@ To fully remove V2: `npm uninstall -g @opencode-ai/cli` and delete
99
137
 
100
138
  | Symptom | Cause / fix |
101
139
  |---|---|
102
- | `live-allow-rule (git status)` / `… not deny-everything` from `coder init --engine opencode2` | The existing (typically V1-authored) `opencode.json` carries live bash allow rules — the V2 beta cannot run while any exist. V2 init rejects **before writing anything**: remove the allow rules from `opencode.json` (V1 runs lose them too), or stay on `--engine opencode` until the V2 beta grows real credential isolation. |
103
- | V1 `coder run` lost `git status` / `npm test` after a V2 init | A fresh V2 init writes deny-everything into the SHARED `opencode.json` (init prints this warning). Export `TRISS_CODER_ENGINE=opencode2`, or re-run plain `triss coder init` to restore the V1 allowlist — which makes the tree V2-incompatible again. |
104
- | `unsupported plugin source "…"` / `unsupported agent source "…"` / `unsupported custom tool source "…"` | The static preflight found an unverified source. Remove or disable it (see the path in the error). **Note:** the standard `~/.config/opencode/agents/` templates written by a V1 `triss coder init` also trip this gate — V2 beta requires a machine without V1 agent templates (or with them temporarily moved) until subagents are fixture-verified. |
140
+ | `live-allow-rule (git status)` / `… not deny-everything` from protected `coder init --engine opencode2` | The existing (typically V1-authored) `opencode.json` carries live bash allow rules — protected V2 cannot run while any exist. Protected init rejects **before writing anything**: remove the allow rules from `opencode.json` (V1 runs lose them too), or use the explicit best-effort acknowledgement if the raw-credential risk is acceptable. |
141
+ | V1 `coder run` lost `git status` / `npm test` after a protected V2 init | A fresh **protected** V2 init writes deny-everything into the SHARED `opencode.json` (init prints this warning). Export `TRISS_CODER_ENGINE=opencode2`, or re-run plain `triss coder init` to restore the V1 allowlist. A fresh best-effort V2 init writes the normal V1 allowlist and emits no false degradation warning. |
142
+ | `unsupported plugin source "…"` / `unsupported agent source "…"` / `unsupported custom tool source "…"` | In protected mode, the static preflight found an unverified source. Remove or disable it (see the path in the error), or explicitly opt into `TRISS_CODER_ALLOW_BEST_EFFORT_ISOLATION=1`; best-effort mode permits normal plugins, agents, and custom tools after the structural/config-shape checks and warns that the selected raw credential is exposed. |
105
143
  | `Agent not found: "coder"` on an older Triss build | Older builds injected `--agent coder` into V2 runs; current builds use the engine's built-in primary agent when `--agent` is not passed. Update Triss. |
106
- | `opencode2 not found` | Install the exact pin: `npm install -g @opencode-ai/cli@0.0.0-next-17430`. |
107
- | `version mismatch (pin …)` | `TRISS_CODER_OPENCODE2_VERSION` override or reinstall the pinned build — V2 is exact-pin only. |
144
+ | `opencode2 not found` | Install the current beta channel: `npm install -g @opencode-ai/cli@beta`. |
145
+ | `below minimum` / `unsupported OpenCode 2 CLI contract` | Remove an obsolete `next-*` override or set a supported beta minimum at or above `0.0.0-beta-17793`; verify that `run --help` exposes the required flags. |
146
+ | `OPENCODE2_SERVICE_SNAPSHOT_UNAVAILABLE` | This host cannot run the optional `ps -axo` resident-service check. The verified `--standalone` CLI contract remains usable with best-effort service verification; use a host with compatible process inspection when a post-probe service proof is required. |
147
+ | `capability-probe-unavailable` | The isolated probe HOME/XDG root could not be created or inspected. Fix `TMPDIR`/filesystem permissions and retry; reinstalling OpenCode 2 does not repair this host error. |
108
148
  | `usage_status: "missing"` | The run emitted no `step_finish`; counters are null by contract, not a bug. |
109
149
  | `--session and --continue … ambiguous` | Pick one resume intent; they are mutually exclusive on V2. |
110
150
  | Session slug not found across engines | By design: V1 and V2 keep separate session maps; a slug never cross-resumes. |
111
- | `UnexpectedStatus` when resuming `--session ses_…` | Known upstream issue in pin `0.0.0-next-17430`: the V2 binary fails a resume by real session id (verified with a direct `opencode2 run --session` call outside Triss). Reported; not a Triss bug. |
151
+ | `UnexpectedStatus` when resuming `--session ses_…` | An upstream beta compatibility issue. Capture the installed beta version and CLI capability output when reporting it; Triss does not roll back or maintain a future-beta denylist. |
112
152
  | `provider.no-route: Model unavailable: opencode-go/…` | Transient provider-side catalogue failure — the same command succeeded minutes earlier. Retry; if it persists, check the Go subscription/status page. |
package/docs/mcp.md CHANGED
@@ -503,7 +503,11 @@ the default), `OPENCODE_API_KEY` (OpenCode Zen or paid OpenCode Go — see
503
503
  `debug config --pure` under the sanitized child environment and a random
504
504
  canary instead of the real key. Late provider/model overrides, unauditable
505
505
  JSONC, and unknown effective config fail closed; the actual run also uses
506
- `--pure`.
506
+ `--pure`. All OpenCode 1 MCP runs perform the bounded final-config probe
507
+ before the credential-bearing spawn. Calls without `provider` omit `--pure`
508
+ from both probe and run, preserving their disk-backed deny-first bash policy
509
+ while keeping later account/org, managed-directory, and macOS MDM layers in
510
+ the audited view.
507
511
  Without `provider`, `model` keeps its legacy main-only semantics. `model`
508
512
  takes a `<provider>/<id>`
509
513
  string — a Triss worker model (`triss-worker/deepseek-v4-flash`, reuses
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "triss-coworker",
3
- "version": "0.37.2",
3
+ "version": "0.38.0",
4
4
  "description": "Give your AI coding agent a cheap DeepSeek coworker. Delegate bulk reads, boilerplate, and doc updates to save 60-70% of your token budget.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -26,6 +26,7 @@
26
26
  "docs/troubleshooting.md",
27
27
  "docs/integrations/",
28
28
  "docs/engines/",
29
+ "!docs/website/",
29
30
  "README.md",
30
31
  "CHANGELOG.md",
31
32
  "LICENSE",
@@ -77,9 +78,9 @@
77
78
  "bugs": {
78
79
  "url": "https://github.com/ayleen/triss-coworker/issues"
79
80
  },
80
- "homepage": "https://github.com/ayleen/triss-coworker#readme",
81
+ "homepage": "https://triss.work",
81
82
  "engines": {
82
- "node": ">=22"
83
+ "node": ">=22.12.0"
83
84
  },
84
85
  "packageManager": "npm@11.6.2",
85
86
  "dependencies": {
@@ -79,7 +79,8 @@ function isValidOrigin(endpoint) {
79
79
  * @param {string} opts.endpoint canonical upstream ORIGIN (https, no path)
80
80
  * @param {string} opts.credential real provider credential (in-memory only)
81
81
  * @param {string} [opts.pathPrefix='/v1'] OpenAI-compatible scope prefix
82
- * @param {string} [opts.authStyle='bearer'] 'bearer' | 'anthropic'
82
+ * @param {string} [opts.protocol] 'openai_chat' | 'openai_responses' | 'anthropic_messages'
83
+ * @param {string} [opts.authStyle='bearer'] compatibility alias for protocol auth
83
84
  * @param {string} [opts.token] pre-generated single-run token (tests)
84
85
  * @param {number} [opts.maxRequests] request-count cap
85
86
  * @param {number} [opts.maxBodyBytes] per-request body-byte cap
@@ -103,7 +104,10 @@ export async function startCoderCredentialProxy(opts = {}) {
103
104
  const pathPrefix = typeof opts.pathPrefix === 'string' && opts.pathPrefix.startsWith('/')
104
105
  ? opts.pathPrefix.replace(/\/+$/, '') || '/'
105
106
  : '/v1';
106
- const authStyle = opts.authStyle === 'anthropic' ? 'anthropic' : 'bearer';
107
+ const protocol = ['openai_chat', 'openai_responses', 'anthropic_messages'].includes(opts.protocol)
108
+ ? opts.protocol
109
+ : opts.authStyle === 'anthropic' ? 'anthropic_messages' : 'openai_chat';
110
+ const authStyle = protocol === 'anthropic_messages' ? 'anthropic' : 'bearer';
107
111
  if (typeof provider !== 'string' || provider.length === 0) {
108
112
  throw new TypeError('startCoderCredentialProxy: provider is required');
109
113
  }
@@ -167,9 +171,9 @@ export async function startCoderCredentialProxy(opts = {}) {
167
171
  // for the pinned protocol is forwarded — the whole prefix subtree is NOT
168
172
  // open (other mutating or billed provider routes must be unreachable).
169
173
  const allowedPaths = new Set(
170
- authStyle === 'anthropic'
174
+ protocol === 'anthropic_messages'
171
175
  ? [`${pathPrefix}/messages`]
172
- : [`${pathPrefix}/chat/completions`],
176
+ : [protocol === 'openai_responses' ? `${pathPrefix}/responses` : `${pathPrefix}/chat/completions`],
173
177
  );
174
178
 
175
179
  function pathAllowed(url) {
@@ -218,6 +222,13 @@ export async function startCoderCredentialProxy(opts = {}) {
218
222
  res.end(JSON.stringify({ error: { message: 'absolute-URI forward-proxy route denied' } }));
219
223
  return;
220
224
  }
225
+ // The proxy is an inference-only surface. Do not let a GET/PUT/etc.
226
+ // reach the body parser and get forwarded as POST upstream.
227
+ if (req.method !== 'POST') {
228
+ res.writeHead(405, { 'content-type': 'application/json', allow: 'POST' });
229
+ res.end(JSON.stringify({ error: { message: 'only POST is supported' } }));
230
+ return;
231
+ }
221
232
  // Provider/model pinning: only the pinned OpenAI-compatible scope is
222
233
  // forwarded; everything else is denied. The prefix match is
223
234
  // boundary-exact — a `/v10` route must not pass a `/v1` pin.
@@ -437,6 +448,7 @@ export async function startCoderCredentialProxy(opts = {}) {
437
448
  scopedBaseUrl: `http://${LOOPBACK_HOST}:${port}${pathPrefix}`,
438
449
  provider,
439
450
  model,
451
+ protocol,
440
452
  revoke,
441
453
  closed,
442
454
  };