@te-river/opencode-team-mode 1.6.1 → 1.7.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.
Files changed (201) hide show
  1. package/README.md +117 -118
  2. package/README.zh-CN.md +98 -100
  3. package/dist/agents.d.ts.map +1 -1
  4. package/dist/agents.js +89 -79
  5. package/dist/agents.js.map +1 -1
  6. package/dist/approval-gate.d.ts +8 -9
  7. package/dist/approval-gate.d.ts.map +1 -1
  8. package/dist/approval-gate.js +13 -14
  9. package/dist/approval-gate.js.map +1 -1
  10. package/dist/capabilities.d.ts +11 -37
  11. package/dist/capabilities.d.ts.map +1 -1
  12. package/dist/capabilities.js +11 -141
  13. package/dist/capabilities.js.map +1 -1
  14. package/dist/commands.js +1 -1
  15. package/dist/envprotect/hook.d.ts +12 -31
  16. package/dist/envprotect/hook.d.ts.map +1 -1
  17. package/dist/envprotect/hook.js +13 -91
  18. package/dist/envprotect/hook.js.map +1 -1
  19. package/dist/envprotect/patterns.d.ts +6 -4
  20. package/dist/envprotect/patterns.d.ts.map +1 -1
  21. package/dist/envprotect/patterns.js +6 -4
  22. package/dist/envprotect/patterns.js.map +1 -1
  23. package/dist/envprotect.d.ts +1 -1
  24. package/dist/envprotect.d.ts.map +1 -1
  25. package/dist/envprotect.js +1 -1
  26. package/dist/envprotect.js.map +1 -1
  27. package/dist/host/note.d.ts.map +1 -1
  28. package/dist/host/note.js +8 -5
  29. package/dist/host/note.js.map +1 -1
  30. package/dist/host/v2-browser-gate.d.ts +19 -1
  31. package/dist/host/v2-browser-gate.d.ts.map +1 -1
  32. package/dist/host/v2-browser-gate.js +47 -6
  33. package/dist/host/v2-browser-gate.js.map +1 -1
  34. package/dist/host/v2-capabilities.d.ts +43 -0
  35. package/dist/host/v2-capabilities.d.ts.map +1 -1
  36. package/dist/host/v2-capabilities.js +77 -1
  37. package/dist/host/v2-capabilities.js.map +1 -1
  38. package/dist/host/v2-compaction.d.ts +129 -0
  39. package/dist/host/v2-compaction.d.ts.map +1 -0
  40. package/dist/host/v2-compaction.js +346 -0
  41. package/dist/host/v2-compaction.js.map +1 -0
  42. package/dist/host/v2-default-agent.d.ts +37 -0
  43. package/dist/host/v2-default-agent.d.ts.map +1 -0
  44. package/dist/host/v2-default-agent.js +128 -0
  45. package/dist/host/v2-default-agent.js.map +1 -0
  46. package/dist/host/v2-events.d.ts +2 -0
  47. package/dist/host/v2-events.d.ts.map +1 -1
  48. package/dist/host/v2-events.js +27 -0
  49. package/dist/host/v2-events.js.map +1 -1
  50. package/dist/host/v2-guard.d.ts +178 -0
  51. package/dist/host/v2-guard.d.ts.map +1 -1
  52. package/dist/host/v2-guard.js +324 -8
  53. package/dist/host/v2-guard.js.map +1 -1
  54. package/dist/host/v2-offload.d.ts +1 -1
  55. package/dist/host/v2-offload.js +2 -2
  56. package/dist/host/v2-permissions.d.ts +45 -44
  57. package/dist/host/v2-permissions.d.ts.map +1 -1
  58. package/dist/host/v2-permissions.js +48 -49
  59. package/dist/host/v2-permissions.js.map +1 -1
  60. package/dist/host/v2-probe-chain.d.ts +93 -0
  61. package/dist/host/v2-probe-chain.d.ts.map +1 -0
  62. package/dist/host/v2-probe-chain.js +146 -0
  63. package/dist/host/v2-probe-chain.js.map +1 -0
  64. package/dist/host/v2-prune.d.ts +157 -0
  65. package/dist/host/v2-prune.d.ts.map +1 -0
  66. package/dist/host/v2-prune.js +507 -0
  67. package/dist/host/v2-prune.js.map +1 -0
  68. package/dist/host/v2-retry.d.ts +143 -0
  69. package/dist/host/v2-retry.d.ts.map +1 -0
  70. package/dist/host/v2-retry.js +315 -0
  71. package/dist/host/v2-retry.js.map +1 -0
  72. package/dist/host/v2-scope.d.ts.map +1 -1
  73. package/dist/host/v2-scope.js +14 -7
  74. package/dist/host/v2-scope.js.map +1 -1
  75. package/dist/host/v2-session-client.d.ts +62 -7
  76. package/dist/host/v2-session-client.d.ts.map +1 -1
  77. package/dist/host/v2-session-client.js +485 -26
  78. package/dist/host/v2-session-client.js.map +1 -1
  79. package/dist/host/v2-session.d.ts +72 -1
  80. package/dist/host/v2-session.d.ts.map +1 -1
  81. package/dist/host/v2-session.js +154 -10
  82. package/dist/host/v2-session.js.map +1 -1
  83. package/dist/host/v2-tool.d.ts +2 -3
  84. package/dist/host/v2-tool.d.ts.map +1 -1
  85. package/dist/host/v2-tool.js +2 -3
  86. package/dist/host/v2-tool.js.map +1 -1
  87. package/dist/host/v2-types.d.ts +25 -0
  88. package/dist/host/v2-types.d.ts.map +1 -1
  89. package/dist/host/v2.d.ts +14 -12
  90. package/dist/host/v2.d.ts.map +1 -1
  91. package/dist/host/v2.js +648 -74
  92. package/dist/host/v2.js.map +1 -1
  93. package/dist/identity.d.ts +27 -0
  94. package/dist/identity.d.ts.map +1 -0
  95. package/dist/identity.js +34 -0
  96. package/dist/identity.js.map +1 -0
  97. package/dist/index.d.ts +9 -24
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +17 -27
  100. package/dist/index.js.map +1 -1
  101. package/dist/prompts/lead.d.ts +1 -1
  102. package/dist/prompts/lead.d.ts.map +1 -1
  103. package/dist/prompts/lead.js +44 -27
  104. package/dist/prompts/lead.js.map +1 -1
  105. package/dist/prompts/shared.d.ts +2 -2
  106. package/dist/prompts/shared.d.ts.map +1 -1
  107. package/dist/prompts/shared.js +74 -57
  108. package/dist/prompts/shared.js.map +1 -1
  109. package/dist/prompts/specialists.d.ts +2 -2
  110. package/dist/prompts/specialists.d.ts.map +1 -1
  111. package/dist/prompts/specialists.js +53 -83
  112. package/dist/prompts/specialists.js.map +1 -1
  113. package/dist/tm/args-schema.d.ts +7 -32
  114. package/dist/tm/args-schema.d.ts.map +1 -1
  115. package/dist/tm/args-schema.js +12 -153
  116. package/dist/tm/args-schema.js.map +1 -1
  117. package/dist/tm/board.d.ts +10 -9
  118. package/dist/tm/board.d.ts.map +1 -1
  119. package/dist/tm/board.js +11 -10
  120. package/dist/tm/board.js.map +1 -1
  121. package/dist/tm/config-files.d.ts +81 -0
  122. package/dist/tm/config-files.d.ts.map +1 -0
  123. package/dist/tm/config-files.js +209 -0
  124. package/dist/tm/config-files.js.map +1 -0
  125. package/dist/tm/config-layers.d.ts +117 -0
  126. package/dist/tm/config-layers.d.ts.map +1 -0
  127. package/dist/tm/config-layers.js +320 -0
  128. package/dist/tm/config-layers.js.map +1 -0
  129. package/dist/tm/config.d.ts +10 -101
  130. package/dist/tm/config.d.ts.map +1 -1
  131. package/dist/tm/config.js +17 -71
  132. package/dist/tm/config.js.map +1 -1
  133. package/dist/tm/dispatch.d.ts +154 -22
  134. package/dist/tm/dispatch.d.ts.map +1 -1
  135. package/dist/tm/dispatch.js +559 -80
  136. package/dist/tm/dispatch.js.map +1 -1
  137. package/dist/tm/egress.d.ts +19 -7
  138. package/dist/tm/egress.d.ts.map +1 -1
  139. package/dist/tm/egress.js +21 -9
  140. package/dist/tm/egress.js.map +1 -1
  141. package/dist/tm/index.d.ts +23 -20
  142. package/dist/tm/index.d.ts.map +1 -1
  143. package/dist/tm/index.js +117 -116
  144. package/dist/tm/index.js.map +1 -1
  145. package/dist/tm/ledger.d.ts +9 -2
  146. package/dist/tm/ledger.d.ts.map +1 -1
  147. package/dist/tm/ledger.js +4 -1
  148. package/dist/tm/ledger.js.map +1 -1
  149. package/dist/tm/perm-ask.d.ts +3 -3
  150. package/dist/tm/perm-ask.d.ts.map +1 -1
  151. package/dist/tm/perm-ask.js +3 -3
  152. package/dist/tm/perm-ask.js.map +1 -1
  153. package/dist/tm/pipelines.d.ts +12 -11
  154. package/dist/tm/pipelines.d.ts.map +1 -1
  155. package/dist/tm/pipelines.js +40 -207
  156. package/dist/tm/pipelines.js.map +1 -1
  157. package/dist/tm/preview.d.ts +1 -1
  158. package/dist/tm/preview.js +3 -3
  159. package/dist/tm/preview.js.map +1 -1
  160. package/dist/tm/refs.d.ts +31 -4
  161. package/dist/tm/refs.d.ts.map +1 -1
  162. package/dist/tm/refs.js +48 -4
  163. package/dist/tm/refs.js.map +1 -1
  164. package/dist/tm/result.d.ts +1 -1
  165. package/dist/tm/result.d.ts.map +1 -1
  166. package/dist/tm/result.js +4 -3
  167. package/dist/tm/result.js.map +1 -1
  168. package/dist/tm/search.d.ts +2 -2
  169. package/dist/tm/search.js +7 -7
  170. package/dist/tm/search.js.map +1 -1
  171. package/dist/tm/serp-loop.d.ts +19 -7
  172. package/dist/tm/serp-loop.d.ts.map +1 -1
  173. package/dist/tm/serp-loop.js +24 -9
  174. package/dist/tm/serp-loop.js.map +1 -1
  175. package/dist/tm/stats.d.ts +31 -27
  176. package/dist/tm/stats.d.ts.map +1 -1
  177. package/dist/tm/stats.js +170 -58
  178. package/dist/tm/stats.js.map +1 -1
  179. package/dist/tm/store.d.ts +18 -5
  180. package/dist/tm/store.d.ts.map +1 -1
  181. package/dist/tm/store.js +39 -9
  182. package/dist/tm/store.js.map +1 -1
  183. package/dist/tm/tools.d.ts +7 -5
  184. package/dist/tm/tools.d.ts.map +1 -1
  185. package/dist/tm/tools.js +10 -41
  186. package/dist/tm/tools.js.map +1 -1
  187. package/dist/tm/webfetch.d.ts +1 -1
  188. package/dist/tm/webfetch.d.ts.map +1 -1
  189. package/dist/tm/webfetch.js +38 -23
  190. package/dist/tm/webfetch.js.map +1 -1
  191. package/dist/types.d.ts +40 -123
  192. package/dist/types.d.ts.map +1 -1
  193. package/dist/types.js +7 -21
  194. package/dist/types.js.map +1 -1
  195. package/docs/installation-v2.md +124 -39
  196. package/docs/installation.md +9 -0
  197. package/docs/research/browser-pane.md +5 -0
  198. package/docs/v2-port-notes.md +500 -315
  199. package/package.json +1 -1
  200. package/scripts/gen-v2-config.mjs +246 -94
  201. package/scripts/lib/fake-ctx.mjs +244 -146
@@ -32,11 +32,15 @@ implied:
32
32
  then the bundled `opencode-cli.exe --version`), because the desktop install does not
33
33
  put `opencode` on `PATH` — on such a machine a PATH-only probe answers "not found"
34
34
  while 2.0.16 is running;
35
- - this branch has **not been exercised end-to-end on a live 2.x host yet** (both
36
- scripts parse clean and the probe is verified against the installed app's version
37
- file, which is not the same claim). So the manual steps below remain the verified
38
- path; if the installer's v2 branch misbehaves, follow them and report which step said
39
- what.
35
+ - this v2 branch **has been exercised end to end on a live 2.0.18 host** with `HOME` and
36
+ `USERPROFILE` redirected (both front-ends): exactly one `loading plugin` line for Team, no load
37
+ failure, `v2-boot tools_registered:9`, a foreign plugin entry left intact in each config file,
38
+ and the legacy `.json` still strict-parseable afterwards. Running it — not reading it — found
39
+ and fixed two defects: `ours()` did not recognise a working-tree path spelling (so the next run
40
+ would have added a SECOND entry, the double load this function exists to prevent), and
41
+ PowerShell's `Copy-Item <src>\* <dst>` threw mid-copy when the destination did not exist yet,
42
+ leaving a half-copied package. The manual steps below are still the documented fallback: if the
43
+ installer's v2 branch misbehaves on YOUR host, follow them and report which step said what.
40
44
 
41
45
  ### What is different on 2.x (read this before the steps)
42
46
 
@@ -44,15 +48,15 @@ implied:
44
48
  |---|---|---|
45
49
  | The six roles | injected by the plugin at boot | `~/.config/opencode/agents/*.md`, written by the generator below — a plugin cannot add an agent |
46
50
  | The six `/team-*` commands | injected by the plugin | `~/.config/opencode/commands/*.md`, same generator |
47
- | Default agent | filled only if the user left it alone | **Team is the default on every boot** (the API has no getter), plus `default_agent: "team"` in config, which is the checkable one. Opt out with `"team-mode": { "defaultAgent": false }` |
48
- | Non-blocking commands | `tm_pty`, on the host's own terminal sessions | **not registered at all** — the v2 plugin context has no pty domain, so the tool could only ever report its own missing seam. Run a slow step as its own `shell` call (one per call, each with its own `timeout`) and tee the output to a log you can read back |
49
- | File access | `tm_read` / `tm_grep` / `tm_bash` | the host's own `read` / `grep` / `glob` / `shell` — **governed anyway**: oversized results are offloaded through `tool.execute.after`, and out-of-project paths go through the host's own `external_directory` permission (a dialog, where v1 had a hard refusal) |
50
- | Batch calls | `tm_ptc_run` | the host's own `execute` (Code Mode) |
51
- | Interactive browsing | `tm_browser` (our own playwright/CDP browser) | **the host's own `browser_*` tools first** — they are the only browser the desktop renders in its side panel (`docs/research/browser-pane.md`). `tm_browser` stays registered as the fallback for a host with no desktop browser (CLI / standalone), and the host catalog is policed by `src/host/v2-browser-gate.ts` (URL / address red line / env-file path at `execute.before`, with a leak-detected fallback that replaces the page with the refusal). Snapshots are capped, not offloaded, so every `[ref=…]` the next click needs stays in context |
51
+ | Default agent | filled only if the user left it alone | **Team is the default on every boot** (the API has no getter), plus `default_agent: "Team"` in config, which is the checkable one. Opt out with `{ "package": "@te-river/opencode-team-mode@latest", "options": { "defaultAgent": false } }` in `plugins` — the object form, not the 1.x tuple (measured 2026-10-06: a tuple is rejected with `path=$.plugins.1 kind=invalid`, the object form is delivered) |
52
+ | Non-blocking commands | `tm_pty` — **removed with v1 support** | **not registered at all** — the v2 plugin context has no pty domain, so the tool could only ever report its own missing seam. Run a slow step as its own `shell` call (one per call, each with its own `timeout`) and tee the output to a log you can read back |
53
+ | File access | `tm_read` / `tm_grep` / `tm_bash` — **removed with v1 support** | the host's own `read` / `grep` / `glob` / `shell` — **governed anyway**: oversized results are offloaded through `tool.execute.after`, and out-of-project paths go through the host's own `external_directory` permission (a dialog, where v1 had a hard refusal) |
54
+ | Batch calls | `tm_ptc_run` — **removed with v1 support** | the host's own `execute` (Code Mode) |
55
+ | Interactive browsing | `tm_browser` (our own playwright/CDP browser) — **removed in 1.7.0** | **the host's own `browser_*` tools are the only browser** — they are the one the desktop renders in its side panel (`docs/research/browser-pane.md`). The host catalog is policed by `src/host/v2-browser-gate.ts` (URL / address red line / env-file path at `execute.before`, with a leak-detected fallback that replaces the page with the refusal). Snapshots are capped, not offloaded, so every `[ref=…]` the next click needs stays in context. **Known cost:** on a host with no native browser catalog (CLI / standalone) there is now no browser at all — the agent reports that gap rather than simulating one. Calling convention: `evaluate` takes `{tabID, script}` where `script` is an expression and the return value must be stringified; snapshot tokens read `@e8 [link]` |
52
56
  | Sub-agent settlement | the host `event` hook | `ctx.event.subscribe()` — zero-dependency async iterable, filtered by event-type name (`src/host/v2-events.ts`). Without it `tm_join` cannot tell a settled child from a running one; `tm_stats` reports what the feed forwarded and which type names went unrecognised (names only, never payloads) |
53
57
  | The task ledger | the host's `todowrite` | **`tm_ledger`**, stored in the host's `ctx.storage` (v2 has no `todowrite`) |
54
58
  | Asking the user | the host's official per-request dialog | **a plugin cannot open a dialog on v2.** Governed calls that would have asked instead **fail closed** with a refusal that says why. The dialogs you do see are raised by the host itself (permission rules, out-of-project access) |
55
- | Web access | a 22-host allowlist + the confirm dialog | **the domain gate is off** (`TM_WEBFETCH_ALLOWED_DOMAINS` defaults to `"*"`): a 2.x plugin cannot raise a dialog, so "approve to proceed" was an instruction to wait for a window that never opens. Address classes are still policed — cloud-metadata / link-local / reserved ranges are refused under every setting, and private space (loopback, RFC1918, CGNAT, `.localhost`) is refused too, but the refusal now names both exits instead of promising a click: `TM_PRIVATE_SPACE=allow` (opens the class) or that one hostname in `TM_WEBFETCH_ALLOWED_DOMAINS` (opens exactly it) |
59
+ | Web access | a 22-host allowlist + the confirm dialog | **the domain gate is off** (`TM_WEBFETCH_ALLOWED_DOMAINS` defaults to `"*"`): a 2.x plugin cannot raise a dialog, so "approve to proceed" was an instruction to wait for a window that never opens. Address classes are still policed — cloud-metadata / link-local / reserved ranges are refused under every setting, and private space (RFC1918, CGNAT, ULA, IPv6 link-local, `.localhost`) is refused too, but the refusal now names both exits instead of promising a click: `TM_PRIVATE_SPACE=allow` (opens the class) or that one hostname in `TM_WEBFETCH_ALLOWED_DOMAINS` (opens exactly it). **Loopback (`127/8`, `::1`, `localhost`) is served by default** — it reaches only a service the user started on their own machine, and a gate whose only exit is a dialog the plugin cannot raise is a wall, not a gate |
56
60
  | Sub-agents | needs `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true` | native; the plugin forces `background: true` on every `subagent` call, so **do not set that env var for 2.x** (it is a v1 workaround and changes nothing here) |
57
61
  | Effect on your other modes | the plugin's hooks are global | **none.** Every hook checks the session's owner first, so `build`, `plan` and any agent you installed yourself stay exactly as a fresh OpenCode leaves them — no tool is deleted from their requests, no temperature is set, nothing is offloaded, no permission is tightened, no dispatch is converted to background |
58
62
 
@@ -115,20 +119,25 @@ returning cleanly is not evidence the plugin loaded.
115
119
  the next hand edit may not.
116
120
  3. Generate the roles and commands (Step 2). A plugin cannot create an agent on 2.x, so
117
121
  without these twelve files Team does not exist however well the plugin loaded.
118
- 4. Set `default_agent: "team"` **last** (Step 3) — a default naming agents that are not on
122
+ 4. Set `default_agent: "Team"` **last** (Step 3) — a default naming agents that are not on
119
123
  disk makes the host fall back to `build` silently.
120
124
  5. Verify, in this order, and report what you actually saw:
121
125
  - `opencode-cli … run --standalone --print-logs` (or restart the desktop and read the
122
126
  log) → exactly **one** line `msg="loading plugin" id=<our entry>
123
127
  entrypoint=file:///…/index.js`, and **no** `failed to load plugin` naming us.
124
128
  - a new Team session → `tm_stats`, section `启动与人格`: `tools_registered: 9`, and the
125
- `作用域` line. A row that says `agents_missing` named roles means Step 3 has not run.
129
+ `作用域` line. The role check is the visibility line, not a
130
+ missing-names field (the boot row's `agents_missing` is gone): it distinguishes 已装而看不见 —
131
+ `agents_editor_unseen` with `agents_resolved_in_request` non-empty, because the host really
132
+ resolved those ids in a request — from 本进程无法区分, since `ctx.agent.transform` is a
133
+ snapshot from BEFORE the config directory merges. "Step 3 has not run" shows up at the host
134
+ (`--agent Team` does not resolve), never as a row in this table.
126
135
  - If the log shows nothing at all about our id, the entry is under a key the host does
127
136
  not read (`plugin`, singular) or the directory has no root `index.js`. Both failures
128
137
  are silent by design; the absence of an error is not a success signal.
129
138
  6. Say which of the two spellings you left installed, and whether the package came from
130
139
  npm or from a local path — the Extensions panel cannot tell them apart (a loader file
131
- named `team-mode.js` and a directory whose plugin id is `team-mode` print identically).
140
+ named `team-mode.js` and a directory whose plugin id is `@te-river/opencode-team-mode` print identically).
132
141
 
133
142
  ### Step 2 — Generate the six roles and the six commands
134
143
 
@@ -148,7 +157,7 @@ node …/scripts/gen-v2-config.mjs --dir ~/.config/opencode
148
157
 
149
158
  Expected output: `共 12 个文件(角色 6 + 命令 6)`. It is idempotent — re-running writes
150
159
  nothing and prints `已是最新`. A file it did not generate (no marker) is **refused**, not
151
- overwritten, so your own `agents/team.md` survives; `--force` overrides that.
160
+ overwritten, so your own `agents/Team.md` survives; `--force` overrides that.
152
161
 
153
162
  On Windows the installed path is
154
163
  `%USERPROFILE%\.config\opencode\node_modules\@te-river\opencode-team-mode\scripts\gen-v2-config.mjs`.
@@ -165,7 +174,7 @@ The plugin also calls `default("team")` on every boot, but a 2.x plugin cannot r
165
174
  value back, so the config key is the one you can verify:
166
175
 
167
176
  ```jsonc
168
- { "default_agent": "team" }
177
+ { "default_agent": "Team" }
169
178
  ```
170
179
 
171
180
  **Order matters:** set it only after Step 2 wrote the role files. A default naming a
@@ -182,7 +191,7 @@ opencode reload # or just restart the desktop app
182
191
 
183
192
  Four checks, in this order:
184
193
 
185
- 1. `opencode agents` (or the picker) lists `team`, `architect`, `implementer`,
194
+ 1. `opencode agents` (or the picker) lists `Team`, `architect`, `implementer`,
186
195
  `reviewer`, `tester`, `researcher`.
187
196
  2. A new session starts as Team. Ask: `tm_stats` — the reply has a
188
197
  **启动与人格** section showing the v2 boot line: which tools were registered, whether
@@ -206,13 +215,55 @@ opencode reload
206
215
  The second command is not optional after an upgrade: the role files carry the prompts, and
207
216
  a new release may have changed them. Re-running is cheap — unchanged files are skipped.
208
217
 
218
+ **Prefer the installer for an upgrade: `scripts/install.ps1` / `install.sh`, re-run.** It is the
219
+ only path that carries migrations. The installer recognises our own entry whatever its spelling
220
+ (`@te-river/opencode-team-mode`, a pinned version, or a `vendor/team-mode` path — one regex,
221
+ `scripts/lib/config-surgery.cjs`), so it **replaces** it instead of appending a second one; two
222
+ entries load the plugin twice, and the host's dedupe matches only an identical string. It then
223
+ rewrites `default_agent` and **reads the value back from disk**. That matters because
224
+ `default_agent` naming a role that does not exist is not an error: the host falls back to
225
+ `build` silently, and the session simply stops being Team. If you upgrade by hand
226
+ (`plugin update` + the generator) and a release ever renames a role, re-run the installer
227
+ before concluding the install is broken.
228
+
229
+ **Before a release, run the real-host acceptance script:** `node scripts/acceptance.mjs` prints a
230
+ pass/fail table for the checks that found the last two defects (plugin load, the 2.x option shape,
231
+ agent visibility, the layered config, R6 armed by default, `grep`/`glob` not bypassing the
232
+ env-file line). It is zero-token by default; `--turns` adds the checks that need a model.
233
+
234
+ ### Layered configuration (`team-mode.jsonc`)
235
+
236
+ Settings can live in a file instead of the environment. Two layers, mirroring the host's own config:
237
+
238
+ | layer | file |
239
+ |---|---|
240
+ | global | `~/.config/opencode/team-mode.jsonc` |
241
+ | project | `<project>/team-mode.jsonc`, or `<project>/.opencode/team-mode.jsonc` (the `.opencode` one wins) |
242
+
243
+ Precedence is `env < global < project`, and **a file beats an environment variable** — a value you
244
+ put in a file wins over the matching `TM_*`. To get the environment back for one run, set
245
+ `TM_CONFIG_ENV_ONLY=1`: it is read before any file, and a file cannot switch it off.
246
+
247
+ Keys are the `TM_*` names in camelCase (`offloadThreshold`, `searchDefaultEngine`, …). Six keys are
248
+ **red lines a file may not change** — `envProtect`, `r6FineAsk`, `privateSpace`,
249
+ `webfetchAllowedDomains`, `browserAskEval`, `bashReadonlyAllowed`; a file value for one of them is
250
+ ignored and reported. A file that fails to parse is skipped **whole** (half a config is worse than
251
+ none), while a single bad key loses only itself. Unknown keys warn at boot and are never applied.
252
+ `tm_stats` shows which layer each key came from.
253
+
254
+ **R6 (env protection) is armed by default.** A Team role's native `read` of a `.env` is denied
255
+ outright, and a shell command that reads the environment goes to the host's own permission prompt.
256
+ Turn it off with the plugin option `envProtect: false` or with `TM_ENV_PROTECT=off`; on 2.x the
257
+ shell face is an `ask` (the host's dialog — a plugin cannot raise one) while the env-FILE face is
258
+ a hard `deny` with no consent path.
259
+
209
260
  ### Uninstalling
210
261
 
211
262
  1. Remove the plugin entry (`opencode plugin remove @te-river/opencode-team-mode`).
212
- 2. Delete the generated files: `~/.config/opencode/agents/{team,architect,implementer,reviewer,tester,researcher}.md`
263
+ 2. Delete the generated files: `~/.config/opencode/agents/{Team,architect,implementer,reviewer,tester,researcher}.md`
213
264
  and `~/.config/opencode/commands/team-*.md`. Only files carrying our marker were written
214
265
  by the generator — anything else you wrote yourself is yours.
215
- 3. Remove `"default_agent": "team"` if you set it.
266
+ 3. Remove `"default_agent": "Team"` if you set it.
216
267
  4. The plugin's own data lives under `<repo>/.git/opencode-team/` (git workspaces) or the
217
268
  OS temp dir; deleting it costs nothing — the TTL sweeper does the same over time.
218
269
 
@@ -222,15 +273,16 @@ a new release may have changed them. Re-running is cheap — unchanged files are
222
273
  |---|---|
223
274
  | Roles are not in the picker | Step 2 never ran, or ran against a different `--dir`. `ls ~/.config/opencode/agents` |
224
275
  | The default silently became `build` | `default_agent` names an agent that does not exist — re-run Step 2, then Step 3 |
225
- | A hand-written `agents/team.md` was not updated | By design: the generator refuses files it did not create. Move yours aside, or pass `--force` |
276
+ | A hand-written `agents/Team.md` was not updated | By design: the generator refuses files it did not create. Move yours aside, or pass `--force` |
226
277
  | `/team-plan` etc. run but not as the specialist | 2.x documents `mode: subagent` as "runs only in a child session"; if a command selecting a specialist does not behave, the fix is `mode: "all"` in the role definition (report it — this is the one item in this flow still unverified on a live host) |
227
278
  | `tm_webfetch` on an odd site says it refused **without asking anyone** | Expected on 2.x: a plugin cannot raise a dialog. Use a source that works, or let the host's own permission rule allow it; the address red line (metadata / private) has no consent path at all, on either generation |
228
279
  | `task`/`subagent` seems to block the lead | On 2.x the plugin forces `background: true`; if you also set the old `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS`, unset it — it is a v1 flag and only confuses the picture |
229
280
  | A tool's output arrives un-offloaded | `TM_NATIVE_OFFLOAD=off` (or a missing `tool.execute.after` seam on an older 2.x build) — `tm_stats` names which |
281
+ | Context compacts earlier than the host would on its own | By design (task #39): Team admits a compaction once the usage crosses **75% of the model's window**, through `ctx.session.compact` — the host's `compaction` block has no percentage key and a 2.x plugin has no `config` domain, so the threshold had to live in the package. `TM_COMPACT_AT_PERCENT=90` moves it, `TM_COMPACT_TRIGGER=off` hands the timing back to the host entirely. `tm_stats` prints `compact_checked / fired / confirmed / below / deduped / no_usage / no_limit`, and the `ctx.session.compact` capability row is `ok` only once the host has accepted an admission |
230
282
  | The plugin behaves oddly — two dialogs, doubled hooks, tools registered twice | It is loaded twice. `opencode.json` and `opencode.jsonc` are merged, and the host's dedupe matches only an identical string, so the same package under two spellings (or in both files) is two personalities. Count the Team entries across BOTH files; `msg="loading plugin"` appearing twice for one id is the proof |
231
283
  | You wrote the entry and the host says nothing at all about it | Two silent causes, both by design: the key is singular `plugin` (2.x reads `plugins`), or a directory entry without a root `index.js`. The loader resolves a directory as `<dir>/index` only and drops it with no message when that fails — absence of an error is not a success signal here |
232
284
  | A background sub-agent's reply is huge | The child is registered, collectable AND readable: `tm_join` lists it (marked 宿主 subagent 派发), reads its report back through `ctx.session.context`, and credits the seam it came from (`正文来源=ctx.session.context`) — so the reply arrives through the same JIT pipeline as everything else (offload + ≤80-token preview + `tm_fetch` handle when it is oversized). What v2 still never gives a plugin is the injected message BEFORE the host persists it, so we can read a report but cannot rewrite the injection — which is why a >~50-line DELIVERABLE still belongs on the blackboard file with its path in the reply |
233
- | You want OUR browser visible in the side panel | Not possible — the panel attaches to the server's own browser service (`docs/research/browser-pane.md`). What IS reachable is a still frame: hand the screenshot to the host's `browser_preview { path }`, which renders a server-local png/html/md/pdf into that panel. Not yet confirmed in a real GUI session |
285
+ | You want a plugin's own browser visible in the side panel | Not possible — the panel attaches to the server's own browser service (`docs/research/browser-pane.md`), and the self-built `tm_browser` was removed in 1.7.0. What IS reachable is a still frame: hand a screenshot to the host's `browser_preview { path }`, which renders a server-local png/html/md/pdf into that panel. Not yet confirmed in a real GUI session |
234
286
  | You need to know what the host actually exposes | `TM_V2_PROBE=<path>.jsonl` records tool and action **names and counts only** — never a command, path, URL or value — and `tm_stats` renders the capability matrix |
235
287
 
236
288
  ---
@@ -242,7 +294,7 @@ a new release may have changed them. Re-running is cheap — unchanged files are
242
294
  `scripts/install.sh` / `scripts\install.ps1` 现在会探测宿主主版本,并在 2.x 上把本页的三步全做完(插件条目 → 生成角色与命令 → 最后写 `default_agent`,并从磁盘读回校验)。两件事直说而不含糊:
243
295
 
244
296
  - 版本探测也会去问**桌面端**(`resources/opencode-cli.version`,再试自带的 `opencode-cli.exe --version`),因为桌面安装不把 `opencode` 放进 `PATH`——在这种机器上,只探 PATH 会得到"没找到",而 2.0.16 正在跑;
245
- - 这条 v2 分支**还没在活体 2.x 宿主上端到端跑过**(两份脚本解析通过、探测对已安装应用的版本文件验证过——这不等于同一个结论)。所以下面的手动步骤仍是已验证路径;安装脚本的 v2 分支若表现不对,请按手动步骤做,并回报是哪一步说了什么。
297
+ - 这条 v2 分支**已经在活体 2.0.18 宿主上端到端跑过**(两个前端都跑了,`HOME` 与 `USERPROFILE` 重定向):Team 只有**一条** `loading plugin`、零加载失败、`v2-boot tools_registered:9`、每个配置文件里别人家的条目原样保留、旧的 `.json` 之后仍能被严格 `JSON.parse`。是"跑"而不是"读"抓出并修好了两个缺陷:`ours()` 认不出工作树路径的写法(下一次运行就会加出**第二条**条目——恰好是这个函数存在的理由),以及 PowerShell 的 `Copy-Item <src>\* <dst>` 在目标目录尚不存在时会中途抛错,留下半份包。下面的手动步骤仍是文档化的退路:安装脚本在你那台宿主上表现不对,就按手动步骤做,并回报是哪一步说了什么。
246
298
 
247
299
  ### 2.x 上到底哪里不一样(先看这张表再动手)
248
300
 
@@ -250,16 +302,16 @@ a new release may have changed them. Re-running is cheap — unchanged files are
250
302
  |---|---|---|
251
303
  | 六个角色 | 插件启动时注入 | `~/.config/opencode/agents/*.md`,由下面的生成器写出——插件没有"造 agent"的接口 |
252
304
  | 六条 `/team-*` 命令 | 插件注入 | 同一个生成器写 `commands/*.md` |
253
- | 默认 agent | 只在用户没动过时补位 | **每次启动都把 Team 设成默认**(v2 没有读取接口),再加配置里的 `default_agent: "team"`(这一条才是你核对得动的)。退出方式:`"team-mode": { "defaultAgent": false }` |
254
- | 读文件 / 搜代码 / 跑命令 | `tm_read` / `tm_grep` / `tm_bash` | 宿主的 `read` / `grep` / `glob` / `shell`,**治理照旧**:超大结果照样在 `tool.execute.after` 被卸载成预览 + 句柄,跨出项目的路径走宿主自己的 `external_directory` 权限(v1 是硬拒,v2 是弹窗) |
255
- | 批量调用 | `tm_ptc_run` | 宿主自己的 `execute`(Code Mode) |
305
+ | 默认 agent | 只在用户没动过时补位 | **每次启动都把 Team 设成默认**(v2 没有读取接口),再加配置里的 `default_agent: "Team"`(这一条才是你核对得动的)。退出方式:`plugins` 里写 `{ "package": "@te-river/opencode-team-mode@latest", "options": { "defaultAgent": false } }`——必须是对象形状,不是 1.x 的元组(2026-10-06 实测:元组被拒 `path=$.plugins.1 kind=invalid`,对象形状能送达) |
306
+ | 读文件 / 搜代码 / 跑命令 | `tm_read` / `tm_grep` / `tm_bash`——**已随 v1 支持一起移除** | 宿主的 `read` / `grep` / `glob` / `shell`,**治理照旧**:超大结果照样在 `tool.execute.after` 被卸载成预览 + 句柄,跨出项目的路径走宿主自己的 `external_directory` 权限(v1 是硬拒,v2 是弹窗) |
307
+ | 批量调用 | `tm_ptc_run`——**已随 v1 支持一起移除** | 宿主自己的 `execute`(Code Mode) |
256
308
  | 入口文件(v2 主机的硬要求) | — | 宿主把插件**目录**解析成 `<目录>/index.js`;只写 `package.json#exports` 的目录会被**静默忽略**(连错误都没有)。本包根目录的 `index.js` 就是为这条存在的——`plugins: ["./vendor/team-mode"]` 这类官方写法能加载,靠的是它 |
257
- | 交互式浏览 | `tm_browser`(我们自己起的 playwright/CDP 浏览器) | **优先宿主的 `browser_*`**——只有它是桌面端侧边栏里那个浏览器(`docs/research/browser-pane.md`)。`tm_browser` 保留为"宿主没接桌面浏览器时"的退路(CLI / standalone);原生目录由 `src/host/v2-browser-gate.ts` 管(`execute.before` 上判 URL / 地址红线 / 环境文件路径,并且有"漏过去就把页面换成拒绝语"的兜底)。快照是**截断**而不是卸载,所以下一次点击要的 `[ref=…]` 都留在上下文里 |
309
+ | 交互式浏览 | `tm_browser`(我们自己起的 playwright/CDP 浏览器)——**已在 1.7.0 移除** | **宿主自己的 `browser_*` 是唯一的浏览器**——只有它是桌面端侧边栏里那个浏览器(`docs/research/browser-pane.md`)。原生目录由 `src/host/v2-browser-gate.ts` 管(`execute.before` 上判 URL / 地址红线 / 环境文件路径,并且有"漏过去就把页面换成拒绝语"的兜底)。快照是**截断**而不是卸载,所以下一次点击要的 `[ref=…]` 都留在上下文里。**已知代价:** 没有原生浏览器目录的宿主(CLI / standalone)从此没有浏览器可用——agent 如实报告这个缺口,绝不模拟。调用约定:`evaluate` 的参数是 `{tabID, script}`,`script` 是表达式,返回值要自己 stringify;快照记号形如 `@e8 [link]` |
258
310
  | 子代理结算检测 | 宿主的 `event` 钩子 | `ctx.event.subscribe()`——零依赖的 async iterable,按事件类型名过滤(`src/host/v2-events.ts`)。没有它 `tm_join` 分不清「已结算」和「仍在跑」;`tm_stats` 会给出转发了多少、哪些类型名没认出来(只记名字,绝不记负载) |
259
311
  | 任务清单 | 宿主 `todowrite` | **`tm_ledger`**,存在宿主的 `ctx.storage` 里(v2 不给插件 `todowrite`) |
260
312
  | 征求用户同意 | 宿主官方逐次弹窗 | **插件在 v2 弹不出对话框。** 原本该问的受治理调用一律**直接拒绝**,并说明是"没人可问"而不是"问了被拒"。你看到的弹窗都来自宿主自己(权限规则、越出项目目录) |
261
- | 联网 | 22 个域名白名单 + 确认弹窗 | **域名门禁关掉**(`TM_WEBFETCH_ALLOWED_DOMAINS` 默认 `"*"`):2.x 插件弹不出确认框,"批准后放行"等于让 agent 去等一个永远不会出现的窗口。地址类别照管——元数据 / 链路本地 / 保留网段在任何设置下都拒;私网(回环、RFC1918、CGNAT、`.localhost`)也拒,但拒绝语现在给出两条出口而不是承诺点击:`TM_PRIVATE_SPACE=allow`(放开整段),或把那一台主机名写进 `TM_WEBFETCH_ALLOWED_DOMAINS`(只放开它自己) |
262
- | 非阻塞命令 | `tm_pty`(跑在宿主终端会话上)| **完全不注册**——v2 插件上下文没有 pty 域,这个工具只能报自己缺缝。慢步骤请一条一个 `shell` 调用(各自带 `timeout`),并把输出 tee 到日志好回读 |
313
+ | 联网 | 22 个域名白名单 + 确认弹窗 | **域名门禁关掉**(`TM_WEBFETCH_ALLOWED_DOMAINS` 默认 `"*"`):2.x 插件弹不出确认框,"批准后放行"等于让 agent 去等一个永远不会出现的窗口。地址类别照管——元数据 / 链路本地 / 保留网段在任何设置下都拒;私网(RFC1918、CGNAT、ULA、IPv6 链路本地、`.localhost`)也拒,但拒绝语现在给出两条出口而不是承诺点击:`TM_PRIVATE_SPACE=allow`(放开整段),或把那一台主机名写进 `TM_WEBFETCH_ALLOWED_DOMAINS`(只放开它自己)。**回环(`127/8`、`::1`、`localhost`)默认放行**——它只打到用户自己机器上的服务,而一道唯一出口是"插件弹不出来的确认框"的门禁,是墙不是门 |
314
+ | 非阻塞命令 | `tm_pty`——**已随 v1 支持一起移除** | **完全不注册**——v2 插件上下文没有 pty 域,这个工具只能报自己缺缝。慢步骤请一条一个 `shell` 调用(各自带 `timeout`),并把输出 tee 到日志好回读 |
263
315
  | 子代理 | 需要 `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true` | 原生能力;插件对每次 `subagent` 强制 `background: true`,所以 **2.x 不要去设那个环境变量**(那是 v1 的补丁,在这里什么也不改变,只会让人误判) |
264
316
  | 对你其它模式的影响 | 插件钩子是全局的 | **没有影响。** 每个钩子都先看会话归属,所以 `build`、`plan` 和你自己装的 agent 都保持刚装好 OpenCode 时的样子——不会从它们的请求里删工具、不会设温度、不会卸载结果、不会收紧权限、也不会把它们的派发改成后台 |
265
317
 
@@ -315,18 +367,18 @@ Node 20+ 还会额外启用 Playwright 浏览器引擎)。
315
367
  下一个手工编辑的人未必容忍。
316
368
  3. 生成角色与命令(第 2 步)。2.x 上插件不能创建 agent,所以这十二个文件不在,Team 就不存在,
317
369
  插件加载得再好也没用。
318
- 4. 最后一步才写 `default_agent: "team"`(第 3 步)——默认值指向不存在的 agent 会让宿主
370
+ 4. 最后一步才写 `default_agent: "Team"`(第 3 步)——默认值指向不存在的 agent 会让宿主
319
371
  静默退回 `build`。
320
372
  5. 验证,按顺序,并且把你真正看到的说出来:
321
373
  - `opencode-cli … run --standalone --print-logs`(或重启桌面端后读日志)→
322
374
  **恰好一条** `msg="loading plugin" id=<我们的条目> entrypoint=file:///…/index.js`,
323
375
  并且**没有**指名我们的 `failed to load plugin`。
324
376
  - 新建一个 Team 会话 → `tm_stats` 的 `启动与人格`:`tools_registered: 9`,以及 `作用域`
325
- 那一行。如果 `agents_missing` 列出了角色名,说明第 3 步没做。
377
+ 那一行。角色这一项看的是可见性行,不再是缺名字段(boot 行的 `agents_missing` 已经去掉):它区分"已装而看不见"——`agents_editor_unseen` 有值,且 `agents_resolved_in_request` 也有值,说明宿主在真实请求里解析过这些 id——和"本进程无法区分",因为 `ctx.agent.transform` 拿到的是配置目录合并**之前**的角色集。"第 3 步没做"要到宿主那边才看得见(`--agent Team` 解析不到),不会在这张表里变成一行结论。
326
378
  - 日志里关于我们的 id 一个字都没有 → 条目写在了宿主不读的键下(单数 `plugin`),或者那个
327
379
  目录没有根 `index.js`。这两种失败都是设计上静默的,所以"没报错"不是成功信号。
328
380
  6. 明确说清你留下的是哪种写法、包来自 npm 还是本地路径 —— 扩展面板分不出来:一个叫
329
- `team-mode.js` 的 loader 文件,和一个插件 id 恰好是 `team-mode` 的目录条目,显示出来一模一样。
381
+ `team-mode.js` 的 loader 文件,和一个插件 id 恰好是 `@te-river/opencode-team-mode` 的目录条目,显示出来一模一样。
330
382
 
331
383
  ### 第 2 步 — 生成六个角色和六条命令
332
384
 
@@ -344,7 +396,7 @@ node …/scripts/gen-v2-config.mjs --dir ~/.config/opencode
344
396
 
345
397
  期望输出 `共 12 个文件(角色 6 + 命令 6)`。它是幂等的:再跑一次什么都不写,只报
346
398
  `已是最新`。不是它生成的文件(没有标记)会被**拒绝覆盖**,所以你自己手写的
347
- `agents/team.md` 安全;要强行覆盖才用 `--force`。
399
+ `agents/Team.md` 安全;要强行覆盖才用 `--force`。
348
400
 
349
401
  Windows 上的路径是
350
402
  `%USERPROFILE%\.config\opencode\node_modules\@te-river\opencode-team-mode\scripts\gen-v2-config.mjs`。
@@ -359,7 +411,7 @@ Windows 上的路径是
359
411
  插件每次启动也会调 `default("team")`,但 v2 插件读不回这个值,所以能核对的是配置键:
360
412
 
361
413
  ```jsonc
362
- { "default_agent": "team" }
414
+ { "default_agent": "Team" }
363
415
  ```
364
416
 
365
417
  **顺序不能颠倒**:必须在第 2 步已经把角色文件写出去之后再设。默认值指向一个不存在的 agent
@@ -374,7 +426,7 @@ opencode reload # 或者直接重启桌面端
374
426
 
375
427
  按顺序做四项核对:
376
428
 
377
- 1. `opencode agents`(或选择器里)能看到 `team`、`architect`、`implementer`、`reviewer`、
429
+ 1. `opencode agents`(或选择器里)能看到 `Team`、`architect`、`implementer`、`reviewer`、
378
430
  `tester`、`researcher`。
379
431
  2. 新会话默认是 Team。在里面跑 `tm_stats` —— 回复里应有**启动与人格**一段,展示 v2 的启动
380
432
  快照:注册了哪些工具、`tool.execute.after` / `permission.evaluate` / `session.context`
@@ -397,13 +449,45 @@ opencode reload
397
449
  第二条命令在升级之后不是可选项:角色文件里装的是提示词,新版本可能改过。重跑很便宜——没变
398
450
  的文件会被跳过。
399
451
 
452
+ **升级请优先走安装器:重跑 `scripts/install.ps1` / `install.sh`。** 它是唯一带迁移的路径。安装器
453
+ 认得出"我们自己的那一条"无论它写成什么样子(`@te-river/opencode-team-mode`、钉死的版本号、或
454
+ `vendor/team-mode` 路径——同一个正则,在 `scripts/lib/config-surgery.cjs` 里),所以它是**替换**而不是
455
+ 再追加一条;追加成两条会让插件被装载两次,而宿主的去重只认完全相同的字符串。之后它会重写
456
+ `default_agent` 并**从磁盘回读**。这条要紧是因为:`default_agent` 指向一个不存在的角色**不算错误**——
457
+ 宿主会静默退回 `build`,会话就此不再是 Team。如果你手工升级(`plugin update` + 生成器),而某个版本
458
+ 改过角色名,请先重跑安装器,再下结论说装坏了。
459
+
460
+ **发版前跑一次真机验收脚本:** `node scripts/acceptance.mjs` 会打印一张通过/不通过表,覆盖"抓出最近两个缺陷"的那些检查
461
+ (插件装载、2.x 选项形状、角色可见性、分层配置、R6 默认 armed、`grep`/`glob` 不绕 env-FILE)。默认零 token;`--turns` 加上需要真回合的检查。
462
+
463
+ ### 分层配置(`team-mode.jsonc`)
464
+
465
+ 设置可以写在文件里,而不是环境变量里。两层,与宿主自己的配置同构:
466
+
467
+ | 层 | 文件 |
468
+ |---|---|
469
+ | 全局 | `~/.config/opencode/team-mode.jsonc` |
470
+ | 项目 | `<project>/team-mode.jsonc`,或 `<project>/.opencode/team-mode.jsonc`(后者胜) |
471
+
472
+ 优先级 `env < 全局 < 项目`,并且**文件赢环境变量**——写进文件的值会盖过同名的 `TM_*`。想让某一次运行回到纯环境变量,设
473
+ `TM_CONFIG_ENV_ONLY=1`:它在任何文件被读取之前求值,文件关不掉它。
474
+
475
+ 键名就是 `TM_*` 的 camelCase 写法(`offloadThreshold`、`searchDefaultEngine` …)。有六个键是**文件不许改写的红线**——
476
+ `envProtect`、`r6FineAsk`、`privateSpace`、`webfetchAllowedDomains`、`browserAskEval`、`bashReadonlyAllowed`;文件里写了也会被忽略并报告。
477
+ 解析失败的那一层**整层跳过**(半份配置比没有配置更危险),单个坏键只丢它自己。未知键在启动时告警、**不应用**。
478
+ `tm_stats` 会显示每个键最终来自哪一层。
479
+
480
+ **R6(环境变量保护)默认开启。** Team 角色用原生 `read` 读 `.env` 会被直接拒;读环境变量的 shell 命令会走宿主自己的权限提示。
481
+ 关闭方式:插件选项 `envProtect: false`,或 `TM_ENV_PROTECT=off`。2.x 上 shell 面是 `ask`(宿主的对话框——插件弹不出来),
482
+ env-FILE 面是硬 `deny`、没有同意路径。
483
+
400
484
  ### 卸载
401
485
 
402
486
  1. 去掉插件条目(`opencode plugin remove @te-river/opencode-team-mode`)。
403
- 2. 删掉生成的文件:`~/.config/opencode/agents/{team,architect,implementer,reviewer,tester,researcher}.md`
487
+ 2. 删掉生成的文件:`~/.config/opencode/agents/{Team,architect,implementer,reviewer,tester,researcher}.md`
404
488
  和 `~/.config/opencode/commands/team-*.md`。只有带我们标记的文件是生成器写的,你自己写的
405
489
  东西它从不碰。
406
- 3. 如果设过 `"default_agent": "team"`,一并删掉。
490
+ 3. 如果设过 `"default_agent": "Team"`,一并删掉。
407
491
  4. 插件自己的数据在 `<repo>/.git/opencode-team/`(git 工作区)或系统临时目录下;删掉没有代价
408
492
  ——TTL 清理器本来就会定期做同样的事。
409
493
 
@@ -413,16 +497,17 @@ opencode reload
413
497
  |---|---|
414
498
  | 选择器里没有那六个角色 | 第 2 步没跑,或者 `--dir` 指到了别处。`ls ~/.config/opencode/agents` |
415
499
  | 默认 agent 悄悄变成 `build` | `default_agent` 指向了一个不存在的角色——重跑第 2 步,再做第 3 步 |
416
- | 手写的 `agents/team.md` 没被更新 | 这是设计:生成器拒绝覆盖不是它生成的文件。把你的文件挪开,或显式加 `--force` |
500
+ | 手写的 `agents/Team.md` 没被更新 | 这是设计:生成器拒绝覆盖不是它生成的文件。把你的文件挪开,或显式加 `--force` |
417
501
  | `/team-plan` 能跑但不是以那个专家身份跑 | 2.x 把 `mode: subagent` 文档化为"只在子会话里运行"。如果选定专家的命令行为不对,改法是角色定义里用 `mode: "all"`(请回报——这是本流程里唯一还没在活体宿主上验证过的一项) |
418
502
  | `tm_webfetch` 说它"没问任何人就直接拒绝" | v2 的预期行为:插件弹不出对话框。换一个不需要这次访问的源,或让宿主自己的权限规则放行;地址红线(元数据 / 私网)在两代宿主上都没有授权路径 |
419
503
  | `subagent` 好像把领队挡住了 | v2 上插件会强制 `background: true`;如果你顺手设了老的 `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS`,请取消它——那是 v1 的开关,在这里只会误导判断 |
420
504
  | 某个工具的输出没被卸载 | `TM_NATIVE_OFFLOAD=off`(或者那个 2.x 构建没有 `tool.execute.after` 缝)——`tm_stats` 会告诉你是哪一种 |
505
+ | 上下文比宿主自己更早压缩 | 这是设计(任务 #39):用量越过**模型窗口的 75%**,Team 就用 `ctx.session.compact` 提交一次压缩——宿主的 `compaction` 配置里没有百分比键,2.x 插件也没有 config 域,所以这个阈值只能长在包里。`TM_COMPACT_AT_PERCENT=90` 可以调,`TM_COMPACT_TRIGGER=off` 把时机整个交回宿主。`tm_stats` 会打印 `compact_checked / fired / confirmed / below / deduped / no_usage / no_limit`,而 `ctx.session.compact` 那行能力矩阵只有在宿主真的接受过一次之后才会写 `ok` |
421
506
  | 插件行为怪:两次弹窗、钩子重复、工具像注册了两遍 | 它被加载了两次。`opencode.json` 与 `opencode.jsonc` 是合并读取的,而宿主去重只认完全相同的字符串,所以同一个包换两种写法(或者同时躺在两个文件里)就是两套人格。把两个文件里指向 Team 的条目一起数一遍;`msg="loading plugin"` 对同一个 id 出现两条就是证据 |
422
507
  | 写了条目,但宿主日志里关于它一个字都没有 | 两个"设计上静默"的原因:键名写成了单数 `plugin`(2.x 读 `plugins`),或者目录条目缺根 `index.js`。宿主解析目录只试 `<dir>/index`,试不出来就直接丢掉、不留任何消息 —— 在这里"没报错"不是成功信号 |
423
508
  | 后台子代理的回复太长 | 子会话现在会被登记、可被收集(`tm_join` 会列出它,行上标着「宿主 subagent 派发」),但它的**正文**仍然是宿主的注入消息,v2 不会在落盘前把这条消息交给插件;我们也不改写发出的消息(猜错那一层的形状等于静默删证据)。所以超限交付走黑板文件:正文进文件、回复里带路径,领队读那个文件 |
424
509
  | `tm_join` 说"没有匹配的派发",可孩子明明跑完了 | 活体在 2.0.16 上复现过:领队用的是**宿主的** `subagent{background:true}`,插件不创建子会话所以登记里没有它,而收养/按 id 认领原本走 `client.session.children` / `client.session.get`——v2 的插件上下文里根本没有 client。所以回复是通过宿主自己的完成注入到达领队的,不是通过我们的收集通道。这条待补的桥是 `ctx.session.get` 做父子校验(事件流已经能给出候选 sessionID) |
425
- | 想在侧边栏看到我们 `tm_browser` 的页面 | 做不到(面板挂的是服务端自己的浏览器服务;见 `docs/research/browser-pane.md`)。能看到的是**静帧**:让 agent 把截图交给宿主的 `browser_preview { path }`,它会把服务端本地文件(png/html/md/pdf/mermaid)渲染进面板——这条尚未在你的 GUI 里验证过 |
510
+ | 想在侧边栏看到插件自己的浏览器页面 | 做不到(面板挂的是服务端自己的浏览器服务;见 `docs/research/browser-pane.md`,自建 `tm_browser` 已在 1.7.0 移除)。能看到的是**静帧**:让 agent 把截图交给宿主的 `browser_preview { path }`,它会把服务端本地文件(png/html/md/pdf/mermaid)渲染进面板——这条尚未在你的 GUI 里验证过 |
426
511
  | 想知道宿主到底给了什么 | `TM_V2_PROBE=<路径>.jsonl` 只记工具名与权限动作名**以及计数**,绝不记命令行、路径、URL 或环境变量值;`tm_stats` 会把能力矩阵渲染出来 |
427
512
 
428
513
  ### v2 专属的开关
@@ -433,4 +518,4 @@ opencode reload
433
518
  | `TM_NATIVE_OFFLOAD` | on | 用 `tool.execute.after` 治理宿主原生工具的结果;off 就退回宿主原样 |
434
519
  | `TM_R6_FINE_ASK` | on(v2) | 由按命令分类器决定哪条 shell 要问;`off` 回到"每条 shell 都问",宿主没有 `permission.hook` 时也自动回到这一档 |
435
520
  | `TM_V2_PROBE` | 未设 | 把宿主真实工具面/动作名记成 JSONL(只有名字和计数) |
436
- | `"team-mode": { "defaultAgent": false }` | 不设置 = Team 永远默认 | 退出默认位抢占(只影响插件的每次启动行为,配置里的 `default_agent` 请自己改回来) |
521
+ | `{ "package": "@te-river/opencode-team-mode@latest", "options": { "defaultAgent": false } }` | 不设置 = Team 永远默认 | 退出默认位抢占(2.x 必须用对象形状;1.x 的元组会被宿主拒:`path=$.plugins.1 kind=invalid`) |
@@ -1,5 +1,14 @@
1
1
  # TeamMode Installation Guide / 安装指南
2
2
 
3
+ > **⚠️ HISTORICAL — 1.18.x IS NO LONGER SUPPORTED.** As of the 1.7.0 line this package
4
+ > targets OpenCode 2.x only: the `server` (1.18.x) personality was removed, so the plugin
5
+ > exports `{id, setup}` and nothing else. This page is kept for reference only and is **not
6
+ > a working install path** — install from **[installation-v2.md](./installation-v2.md)**.
7
+ >
8
+ > **⚠️ 历史文档——1.18.x 已不再支持。** 自 1.7.0 线起本包只面向 OpenCode 2.x:`server`
9
+ > (1.18.x)人格已移除,插件只导出 `{id, setup}`。本页仅作历史记录,**不是可用的安装
10
+ > 路径**——请走 **[installation-v2.md](./installation-v2.md)**。
11
+
3
12
  > **This page is the OpenCode 1.18.x path.** On OpenCode 2.x a plugin cannot create an
4
13
  > agent, so the six roles and the six `/team-*` commands have to be generated into
5
14
  > `~/.config/opencode/agents/` and `commands/` — that flow is its own page:
@@ -1,5 +1,10 @@
1
1
  # 能不能把 tm_browser 显示进宿主侧边栏?(BrowserPane 取证)
2
2
 
3
+ > **历史记录(2026-10-06 补注):自建浏览器 `tm_browser` 已于 1.7.0 移除**(提交 `d668817`,
4
+ > 破坏性变更)。本页记录的是移除前的取证,保留是因为它解释了"为什么浏览器只能交给宿主原生
5
+ > `browser_*`"——结论(插件无法把自己的页面注册进侧边栏)至今成立,且正是移除自建浏览器的依据。
6
+ > 现在浏览器只剩宿主原生 `browser_*`,由 `src/host/v2-browser-gate.ts` 在 `execute.before` 上治理。
7
+
3
8
  日期 2026-09-25。取证对象是用户机器上的安装体,只读、未修改宿主:
4
9
 
5
10
  | 证据 | 位置 |