@te-river/opencode-team-mode 1.5.15 → 1.6.1

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 (196) hide show
  1. package/README.md +853 -690
  2. package/README.zh-CN.md +761 -631
  3. package/dist/agents.d.ts +5 -3
  4. package/dist/agents.d.ts.map +1 -1
  5. package/dist/agents.js +19 -8
  6. package/dist/agents.js.map +1 -1
  7. package/dist/capabilities.d.ts +1 -1
  8. package/dist/capabilities.js +2 -2
  9. package/dist/capabilities.js.map +1 -1
  10. package/dist/host/note.d.ts +7 -0
  11. package/dist/host/note.d.ts.map +1 -0
  12. package/dist/host/note.js +28 -0
  13. package/dist/host/note.js.map +1 -0
  14. package/dist/host/v1.d.ts +14 -0
  15. package/dist/host/v1.d.ts.map +1 -0
  16. package/dist/host/v1.js +340 -0
  17. package/dist/host/v1.js.map +1 -0
  18. package/dist/host/v2-browser-gate.d.ts +45 -0
  19. package/dist/host/v2-browser-gate.d.ts.map +1 -0
  20. package/dist/host/v2-browser-gate.js +319 -0
  21. package/dist/host/v2-browser-gate.js.map +1 -0
  22. package/dist/host/v2-capabilities.d.ts +68 -0
  23. package/dist/host/v2-capabilities.d.ts.map +1 -0
  24. package/dist/host/v2-capabilities.js +146 -0
  25. package/dist/host/v2-capabilities.js.map +1 -0
  26. package/dist/host/v2-client.d.ts +53 -0
  27. package/dist/host/v2-client.d.ts.map +1 -0
  28. package/dist/host/v2-client.js +193 -0
  29. package/dist/host/v2-client.js.map +1 -0
  30. package/dist/host/v2-events.d.ts +56 -0
  31. package/dist/host/v2-events.d.ts.map +1 -0
  32. package/dist/host/v2-events.js +158 -0
  33. package/dist/host/v2-events.js.map +1 -0
  34. package/dist/host/v2-guard.d.ts +101 -0
  35. package/dist/host/v2-guard.d.ts.map +1 -0
  36. package/dist/host/v2-guard.js +199 -0
  37. package/dist/host/v2-guard.js.map +1 -0
  38. package/dist/host/v2-offload.d.ts +148 -0
  39. package/dist/host/v2-offload.d.ts.map +1 -0
  40. package/dist/host/v2-offload.js +288 -0
  41. package/dist/host/v2-offload.js.map +1 -0
  42. package/dist/host/v2-permissions.d.ts +123 -0
  43. package/dist/host/v2-permissions.d.ts.map +1 -0
  44. package/dist/host/v2-permissions.js +190 -0
  45. package/dist/host/v2-permissions.js.map +1 -0
  46. package/dist/host/v2-probe.d.ts +85 -0
  47. package/dist/host/v2-probe.d.ts.map +1 -0
  48. package/dist/host/v2-probe.js +268 -0
  49. package/dist/host/v2-probe.js.map +1 -0
  50. package/dist/host/v2-scope.d.ts +51 -0
  51. package/dist/host/v2-scope.d.ts.map +1 -0
  52. package/dist/host/v2-scope.js +58 -0
  53. package/dist/host/v2-scope.js.map +1 -0
  54. package/dist/host/v2-session-client.d.ts +74 -0
  55. package/dist/host/v2-session-client.d.ts.map +1 -0
  56. package/dist/host/v2-session-client.js +171 -0
  57. package/dist/host/v2-session-client.js.map +1 -0
  58. package/dist/host/v2-session.d.ts +85 -0
  59. package/dist/host/v2-session.d.ts.map +1 -0
  60. package/dist/host/v2-session.js +171 -0
  61. package/dist/host/v2-session.js.map +1 -0
  62. package/dist/host/v2-subagent.d.ts +157 -0
  63. package/dist/host/v2-subagent.d.ts.map +1 -0
  64. package/dist/host/v2-subagent.js +286 -0
  65. package/dist/host/v2-subagent.js.map +1 -0
  66. package/dist/host/v2-tool.d.ts +67 -0
  67. package/dist/host/v2-tool.d.ts.map +1 -0
  68. package/dist/host/v2-tool.js +209 -0
  69. package/dist/host/v2-tool.js.map +1 -0
  70. package/dist/host/v2-transcript.d.ts +73 -0
  71. package/dist/host/v2-transcript.d.ts.map +1 -0
  72. package/dist/host/v2-transcript.js +151 -0
  73. package/dist/host/v2-transcript.js.map +1 -0
  74. package/dist/host/v2-types.d.ts +229 -0
  75. package/dist/host/v2-types.d.ts.map +1 -0
  76. package/dist/host/v2-types.js +17 -0
  77. package/dist/host/v2-types.js.map +1 -0
  78. package/dist/host/v2.d.ts +42 -0
  79. package/dist/host/v2.d.ts.map +1 -0
  80. package/dist/host/v2.js +706 -0
  81. package/dist/host/v2.js.map +1 -0
  82. package/dist/host-hooks.d.ts +13 -2
  83. package/dist/host-hooks.d.ts.map +1 -1
  84. package/dist/host-hooks.js +18 -5
  85. package/dist/host-hooks.js.map +1 -1
  86. package/dist/index.d.ts +23 -17
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js +29 -326
  89. package/dist/index.js.map +1 -1
  90. package/dist/prompts/lead.d.ts +1 -1
  91. package/dist/prompts/lead.d.ts.map +1 -1
  92. package/dist/prompts/lead.js +452 -359
  93. package/dist/prompts/lead.js.map +1 -1
  94. package/dist/prompts/shared.d.ts +2 -2
  95. package/dist/prompts/shared.d.ts.map +1 -1
  96. package/dist/prompts/shared.js +89 -17
  97. package/dist/prompts/shared.js.map +1 -1
  98. package/dist/prompts/specialists.d.ts +2 -2
  99. package/dist/prompts/specialists.d.ts.map +1 -1
  100. package/dist/prompts/specialists.js +44 -10
  101. package/dist/prompts/specialists.js.map +1 -1
  102. package/dist/task-offload.d.ts +83 -0
  103. package/dist/task-offload.d.ts.map +1 -0
  104. package/dist/task-offload.js +174 -0
  105. package/dist/task-offload.js.map +1 -0
  106. package/dist/tm/args-schema.d.ts +4 -0
  107. package/dist/tm/args-schema.d.ts.map +1 -1
  108. package/dist/tm/args-schema.js +36 -3
  109. package/dist/tm/args-schema.js.map +1 -1
  110. package/dist/tm/board.d.ts +113 -0
  111. package/dist/tm/board.d.ts.map +1 -0
  112. package/dist/tm/board.js +275 -0
  113. package/dist/tm/board.js.map +1 -0
  114. package/dist/tm/browser.d.ts +325 -6
  115. package/dist/tm/browser.d.ts.map +1 -1
  116. package/dist/tm/browser.js +1360 -135
  117. package/dist/tm/browser.js.map +1 -1
  118. package/dist/tm/config.d.ts +36 -25
  119. package/dist/tm/config.d.ts.map +1 -1
  120. package/dist/tm/config.js +29 -7
  121. package/dist/tm/config.js.map +1 -1
  122. package/dist/tm/dispatch.d.ts +133 -53
  123. package/dist/tm/dispatch.d.ts.map +1 -1
  124. package/dist/tm/dispatch.js +481 -279
  125. package/dist/tm/dispatch.js.map +1 -1
  126. package/dist/tm/dupe-guard.d.ts +16 -3
  127. package/dist/tm/dupe-guard.d.ts.map +1 -1
  128. package/dist/tm/dupe-guard.js +27 -7
  129. package/dist/tm/dupe-guard.js.map +1 -1
  130. package/dist/tm/egress.d.ts +44 -0
  131. package/dist/tm/egress.d.ts.map +1 -0
  132. package/dist/tm/egress.js +191 -0
  133. package/dist/tm/egress.js.map +1 -0
  134. package/dist/tm/guard.js +1 -1
  135. package/dist/tm/guard.js.map +1 -1
  136. package/dist/tm/index.d.ts +67 -2
  137. package/dist/tm/index.d.ts.map +1 -1
  138. package/dist/tm/index.js +228 -20
  139. package/dist/tm/index.js.map +1 -1
  140. package/dist/tm/ledger.d.ts +114 -0
  141. package/dist/tm/ledger.d.ts.map +1 -0
  142. package/dist/tm/ledger.js +308 -0
  143. package/dist/tm/ledger.js.map +1 -0
  144. package/dist/tm/perm-ask.d.ts +45 -5
  145. package/dist/tm/perm-ask.d.ts.map +1 -1
  146. package/dist/tm/perm-ask.js +107 -7
  147. package/dist/tm/perm-ask.js.map +1 -1
  148. package/dist/tm/pipelines.d.ts.map +1 -1
  149. package/dist/tm/pipelines.js +24 -2
  150. package/dist/tm/pipelines.js.map +1 -1
  151. package/dist/tm/preview.d.ts +55 -0
  152. package/dist/tm/preview.d.ts.map +1 -1
  153. package/dist/tm/preview.js +225 -0
  154. package/dist/tm/preview.js.map +1 -1
  155. package/dist/tm/ptc/tool.d.ts.map +1 -1
  156. package/dist/tm/ptc/tool.js +2 -4
  157. package/dist/tm/ptc/tool.js.map +1 -1
  158. package/dist/tm/pty.d.ts.map +1 -1
  159. package/dist/tm/pty.js +6 -3
  160. package/dist/tm/pty.js.map +1 -1
  161. package/dist/tm/search.d.ts +5 -0
  162. package/dist/tm/search.d.ts.map +1 -1
  163. package/dist/tm/search.js +40 -8
  164. package/dist/tm/search.js.map +1 -1
  165. package/dist/tm/serp-loop.d.ts +45 -0
  166. package/dist/tm/serp-loop.d.ts.map +1 -0
  167. package/dist/tm/serp-loop.js +80 -0
  168. package/dist/tm/serp-loop.js.map +1 -0
  169. package/dist/tm/stats.d.ts +26 -0
  170. package/dist/tm/stats.d.ts.map +1 -1
  171. package/dist/tm/stats.js +194 -4
  172. package/dist/tm/stats.js.map +1 -1
  173. package/dist/tm/webfetch.d.ts +56 -0
  174. package/dist/tm/webfetch.d.ts.map +1 -1
  175. package/dist/tm/webfetch.js +187 -19
  176. package/dist/tm/webfetch.js.map +1 -1
  177. package/dist/tool-coerce.d.ts +24 -0
  178. package/dist/tool-coerce.d.ts.map +1 -0
  179. package/dist/tool-coerce.js +63 -0
  180. package/dist/tool-coerce.js.map +1 -0
  181. package/dist/types.d.ts +7 -1
  182. package/dist/types.d.ts.map +1 -1
  183. package/docs/installation-v2.md +436 -0
  184. package/docs/installation.md +81 -2
  185. package/docs/research/agent-data-exchange.md +117 -0
  186. package/docs/research/browser-pane.md +89 -0
  187. package/docs/research/host-http-api.md +132 -0
  188. package/docs/research/host-subagent-injection.md +187 -0
  189. package/docs/research/plugin-loader-contract.md +261 -0
  190. package/docs/research/v2-builtin-tools.md +447 -0
  191. package/docs/v2-port-notes.md +315 -0
  192. package/index.js +7 -0
  193. package/package.json +4 -1
  194. package/scripts/gen-v2-config.mjs +304 -0
  195. package/scripts/lib/config-surgery.cjs +239 -0
  196. package/scripts/lib/fake-ctx.mjs +146 -0
@@ -0,0 +1,436 @@
1
+ # TeamMode on OpenCode 2.x — Installation / 安装指南(v2 专用)
2
+
3
+ > **Read this page instead of `installation.md` if your host is OpenCode 2.x.**
4
+ > The two generations are not variants of one install: a 2.x plugin **cannot create
5
+ > an agent**, so the six Team roles and the six `/team-*` commands reach the host as
6
+ > **config files** this package generates, and several v1 mechanisms have no v2
7
+ > equivalent at all. `installation.md` describes the 1.18.x path and stays correct
8
+ > there.
9
+ >
10
+ > 如果你的宿主是 OpenCode 2.x,请看本页而不是 `installation.md`:2.x 的插件**不能创建
11
+ > agent**,六个角色和六条 `/team-*` 命令是以**配置文件**的形式由本包生成出去的;v1 的几处
12
+ > 机制在 v2 上根本没有对应物。`installation.md` 讲的是 1.18.x,那份仍然是对的。
13
+
14
+ Find out which one you are on / 先确认你在哪一代:
15
+
16
+ ```bash
17
+ opencode --version # 1.18.x → installation.md ; 2.x → this page
18
+ ```
19
+
20
+ ---
21
+
22
+ ## Part A — English
23
+
24
+ ### One command, or the steps below
25
+
26
+ `scripts/install.sh` / `scripts\install.ps1` now detect the host major version and run
27
+ all three steps of this page on 2.x (plugin entry → generate roles and commands →
28
+ `default_agent` last, with a read-back from disk). Two things are said rather than
29
+ implied:
30
+
31
+ - the version probe also asks the **desktop app** (`resources/opencode-cli.version`,
32
+ then the bundled `opencode-cli.exe --version`), because the desktop install does not
33
+ put `opencode` on `PATH` — on such a machine a PATH-only probe answers "not found"
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.
40
+
41
+ ### What is different on 2.x (read this before the steps)
42
+
43
+ | | OpenCode 1.18.x | OpenCode 2.x |
44
+ |---|---|---|
45
+ | 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
+ | 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 |
52
+ | 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
+ | The task ledger | the host's `todowrite` | **`tm_ledger`**, stored in the host's `ctx.storage` (v2 has no `todowrite`) |
54
+ | 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) |
56
+ | 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
+ | 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
+
59
+ ### Prerequisite
60
+
61
+ OpenCode 2.x (desktop or CLI) and Node.js on PATH (the plugin runs on the host's own
62
+ runtime; Node 20+ additionally enables the Playwright browser engine).
63
+
64
+ ### Step 1 — Install the plugin
65
+
66
+ The key is **`plugins`** (plural), and one line in it is the whole plugin install: the
67
+ host resolves the entry and **installs the package itself at startup**. There is nothing
68
+ to `npm install` first, no plugin cache to purge, and `opencode plugin add` is not a
69
+ prerequisite — it is a convenience that writes this same key (into `opencode.json`, which
70
+ is why you may see the entry appear there).
71
+
72
+ ```jsonc
73
+ // ~/.config/opencode/opencode.jsonc
74
+ { "plugins": ["@te-river/opencode-team-mode@latest"] }
75
+ ```
76
+
77
+ Two facts about that file, both measured on 2.0.16 / 2.0.18 and both easy to get wrong
78
+ from 1.18.x habits (`docs/research/plugin-loader-contract.md` has the host's own code):
79
+
80
+ - `opencode.json` and `opencode.jsonc` are **both parsed and merged**. The 1.18.x rule
81
+ "jsonc overrides json" is false on 2.x, and acting on it — copying the old file forward
82
+ — puts the same plugin in both, which **loads it twice** (two personalities, the same
83
+ tools and hooks bound twice) with no warning.
84
+ - The host's dedupe matches only an **identical string**. `@te-river/…@latest` in one file
85
+ and `./vendor/team-mode` in the other is two entries, not one plugin.
86
+
87
+ A **local directory** is the other legal spelling, and the one a development tree needs:
88
+
89
+ ```jsonc
90
+ { "plugins": ["./vendor/team-mode"] } // relative to this file, or an absolute dir
91
+ ```
92
+
93
+ The directory **must contain a root `index.js`**. The host resolves a directory as
94
+ `<dir>/index` and nothing else — a `package.json#exports` map is honoured for an installed
95
+ package but not for a path — and a directory whose entrypoint does not resolve is skipped
96
+ **silently**: no error, no log line, no plugin. This package ships that `index.js` for
97
+ exactly that reason.
98
+
99
+ `opencode plugin add` cannot do this: it refuses a path with "Plugin target must be an npm
100
+ registry package or Git package specifier".
101
+
102
+ #### Doing this as an agent, on someone's machine
103
+
104
+ The installers (`scripts/install.sh`, `scripts/install.ps1`) encode the same procedure; if
105
+ you are an agent working for a user who prefers not to run them, follow these steps and do
106
+ not skip the last one. Never claim an install without the arrival check — a config write
107
+ returning cleanly is not evidence the plugin loaded.
108
+
109
+ 1. Read both `~/.config/opencode/opencode.jsonc` and `opencode.json`. Count how many
110
+ entries anywhere in them name this package (`opencode-team-mode`, case and hyphens
111
+ vary). More than one → the plugin is loading twice; fix that before anything else.
112
+ 2. Write **one** entry, into the file that already holds the user's other keys (prefer the
113
+ `.jsonc`, since that is where previous installers wrote). Keep every entry that is not
114
+ ours, keep the comments, and leave no comma dangling before `]` — the host tolerates it,
115
+ the next hand edit may not.
116
+ 3. Generate the roles and commands (Step 2). A plugin cannot create an agent on 2.x, so
117
+ 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
119
+ disk makes the host fall back to `build` silently.
120
+ 5. Verify, in this order, and report what you actually saw:
121
+ - `opencode-cli … run --standalone --print-logs` (or restart the desktop and read the
122
+ log) → exactly **one** line `msg="loading plugin" id=<our entry>
123
+ entrypoint=file:///…/index.js`, and **no** `failed to load plugin` naming us.
124
+ - 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.
126
+ - If the log shows nothing at all about our id, the entry is under a key the host does
127
+ not read (`plugin`, singular) or the directory has no root `index.js`. Both failures
128
+ are silent by design; the absence of an error is not a success signal.
129
+ 6. Say which of the two spellings you left installed, and whether the package came from
130
+ 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).
132
+
133
+ ### Step 2 — Generate the six roles and the six commands
134
+
135
+ This is the step 2.x exists for and the v1 installer never needed. Run the generator
136
+ that ships **inside the installed package** — never copy the markdown by hand, or the
137
+ prompts drift away from what the plugin injects on v1:
138
+
139
+ ```bash
140
+ # the installed copy of this package (global config's node_modules):
141
+ node ~/.config/opencode/node_modules/@te-river/opencode-team-mode/scripts/gen-v2-config.mjs
142
+
143
+ # preview without writing:
144
+ node …/scripts/gen-v2-config.mjs --print
145
+ # write somewhere else (a project's .opencode/, a temp dir for a check):
146
+ node …/scripts/gen-v2-config.mjs --dir ~/.config/opencode
147
+ ```
148
+
149
+ Expected output: `共 12 个文件(角色 6 + 命令 6)`. It is idempotent — re-running writes
150
+ 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.
152
+
153
+ On Windows the installed path is
154
+ `%USERPROFILE%\.config\opencode\node_modules\@te-river\opencode-team-mode\scripts\gen-v2-config.mjs`.
155
+
156
+ The generated `agents/<role>.md` carries the role's `description` / `mode` / `color` /
157
+ permission triples in frontmatter and the **v2 variant** of the prompt in the body: the
158
+ generator rewrites every sentence that named a retired tool, and it **fails the run** if a
159
+ rewrite rule stops matching the source. That is deliberate — a v2 model told to call
160
+ `tm_read` costs the round the mandate exists to save.
161
+
162
+ ### Step 3 — Make Team the default, in config
163
+
164
+ The plugin also calls `default("team")` on every boot, but a 2.x plugin cannot read the
165
+ value back, so the config key is the one you can verify:
166
+
167
+ ```jsonc
168
+ { "default_agent": "team" }
169
+ ```
170
+
171
+ **Order matters:** set it only after Step 2 wrote the role files. A default naming a
172
+ missing agent makes the host fall back to `build` **silently** — you would see Team
173
+ working in a new session and nothing explaining why it stopped. Note also that
174
+ `default_agent` does not change the agent already stored on an existing conversation;
175
+ it applies to new sessions.
176
+
177
+ ### Step 4 — Restart and verify
178
+
179
+ ```bash
180
+ opencode reload # or just restart the desktop app
181
+ ```
182
+
183
+ Four checks, in this order:
184
+
185
+ 1. `opencode agents` (or the picker) lists `team`, `architect`, `implementer`,
186
+ `reviewer`, `tester`, `researcher`.
187
+ 2. A new session starts as Team. Ask: `tm_stats` — the reply has a
188
+ **启动与人格** section showing the v2 boot line: which tools were registered, whether
189
+ `tool.execute.after` / `permission.evaluate` / `session.context` attached, the probe's
190
+ counts, and `storage_probe` (the `ctx.storage` round-trip that `tm_ledger` depends on).
191
+ 3. **JIT actually governs the host's tools**: ask Team to run a command that prints a lot
192
+ (e.g. `dir /s` / `ls -R` on a large tree). The tool result should come back as a short
193
+ preview with an offload handle, not the whole dump. If it comes back verbatim, the
194
+ offload layer is not attached — `tm_stats` says so.
195
+ 4. `tm_ledger { action:"add", text:"smoke test" }` answers with an id and
196
+ `已写入 ctx.storage`. That is the v2 ledger being live.
197
+
198
+ ### Updating / 更新
199
+
200
+ ```bash
201
+ opencode plugin update @te-river/opencode-team-mode # or plugin add …@latest again
202
+ node ~/.config/opencode/node_modules/@te-river/opencode-team-mode/scripts/gen-v2-config.mjs
203
+ opencode reload
204
+ ```
205
+
206
+ The second command is not optional after an upgrade: the role files carry the prompts, and
207
+ a new release may have changed them. Re-running is cheap — unchanged files are skipped.
208
+
209
+ ### Uninstalling
210
+
211
+ 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`
213
+ and `~/.config/opencode/commands/team-*.md`. Only files carrying our marker were written
214
+ by the generator — anything else you wrote yourself is yours.
215
+ 3. Remove `"default_agent": "team"` if you set it.
216
+ 4. The plugin's own data lives under `<repo>/.git/opencode-team/` (git workspaces) or the
217
+ OS temp dir; deleting it costs nothing — the TTL sweeper does the same over time.
218
+
219
+ ### Troubleshooting
220
+
221
+ | Symptom | Cause and move |
222
+ |---|---|
223
+ | Roles are not in the picker | Step 2 never ran, or ran against a different `--dir`. `ls ~/.config/opencode/agents` |
224
+ | 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` |
226
+ | `/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
+ | `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
+ | `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
+ | 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 |
230
+ | 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
+ | 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
+ | 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 |
234
+ | 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
+
236
+ ---
237
+
238
+ ## Part B — 中文
239
+
240
+ ### 一条命令,或者按下面的步骤来
241
+
242
+ `scripts/install.sh` / `scripts\install.ps1` 现在会探测宿主主版本,并在 2.x 上把本页的三步全做完(插件条目 → 生成角色与命令 → 最后写 `default_agent`,并从磁盘读回校验)。两件事直说而不含糊:
243
+
244
+ - 版本探测也会去问**桌面端**(`resources/opencode-cli.version`,再试自带的 `opencode-cli.exe --version`),因为桌面安装不把 `opencode` 放进 `PATH`——在这种机器上,只探 PATH 会得到"没找到",而 2.0.16 正在跑;
245
+ - 这条 v2 分支**还没在活体 2.x 宿主上端到端跑过**(两份脚本解析通过、探测对已安装应用的版本文件验证过——这不等于同一个结论)。所以下面的手动步骤仍是已验证路径;安装脚本的 v2 分支若表现不对,请按手动步骤做,并回报是哪一步说了什么。
246
+
247
+ ### 2.x 上到底哪里不一样(先看这张表再动手)
248
+
249
+ | | OpenCode 1.18.x | OpenCode 2.x |
250
+ |---|---|---|
251
+ | 六个角色 | 插件启动时注入 | `~/.config/opencode/agents/*.md`,由下面的生成器写出——插件没有"造 agent"的接口 |
252
+ | 六条 `/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) |
256
+ | 入口文件(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=…]` 都留在上下文里 |
258
+ | 子代理结算检测 | 宿主的 `event` 钩子 | `ctx.event.subscribe()`——零依赖的 async iterable,按事件类型名过滤(`src/host/v2-events.ts`)。没有它 `tm_join` 分不清「已结算」和「仍在跑」;`tm_stats` 会给出转发了多少、哪些类型名没认出来(只记名字,绝不记负载) |
259
+ | 任务清单 | 宿主 `todowrite` | **`tm_ledger`**,存在宿主的 `ctx.storage` 里(v2 不给插件 `todowrite`) |
260
+ | 征求用户同意 | 宿主官方逐次弹窗 | **插件在 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 到日志好回读 |
263
+ | 子代理 | 需要 `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true` | 原生能力;插件对每次 `subagent` 强制 `background: true`,所以 **2.x 不要去设那个环境变量**(那是 v1 的补丁,在这里什么也不改变,只会让人误判) |
264
+ | 对你其它模式的影响 | 插件钩子是全局的 | **没有影响。** 每个钩子都先看会话归属,所以 `build`、`plan` 和你自己装的 agent 都保持刚装好 OpenCode 时的样子——不会从它们的请求里删工具、不会设温度、不会卸载结果、不会收紧权限、也不会把它们的派发改成后台 |
265
+
266
+ ### 前置条件
267
+
268
+ OpenCode 2.x(桌面版或 CLI),以及 PATH 上可用的 Node.js(插件跑在宿主的运行时里;
269
+ Node 20+ 还会额外启用 Playwright 浏览器引擎)。
270
+
271
+ ### 第 1 步 — 安装插件
272
+
273
+ 键名是 **`plugins`(复数)**,而且里面一行就是完整的插件安装:宿主会在**启动时自己解析并安装**
274
+ 这个条目指向的包。不需要先 `npm install`,没有缓存要清,`opencode plugin add` 也不是前置条件
275
+ ——它只是顺手帮你写了这同一个键(写进 `opencode.json`,所以你会看到条目出现在那儿)。
276
+
277
+ ```jsonc
278
+ // ~/.config/opencode/opencode.jsonc
279
+ { "plugins": ["@te-river/opencode-team-mode@latest"] }
280
+ ```
281
+
282
+ 关于这个文件有两件事是实测出来的(2.0.16 / 2.0.18),而且用 1.18.x 的直觉一定会搞错,
283
+ 宿主的原始代码见 `docs/research/plugin-loader-contract.md`:
284
+
285
+ - `opencode.json` 与 `opencode.jsonc` 是**两份都读、合并生效**。1.18.x 那句"`.jsonc` 覆盖
286
+ `.json`"在 2.x 上不成立;照它做(把旧文件向前复制一份)就等于把同一个插件写进两个文件,
287
+ 于是**加载两次**(两套人格、同样的工具和钩子绑两遍),而且没有任何警告。
288
+ - 宿主自己的去重只认**完全相同的字符串**。一个文件里写 `@te-river/…@latest`、另一个里写
289
+ `./vendor/team-mode`,是两个条目,不是一个插件。
290
+
291
+ **本地目录**是另一种合法写法,开发时必须要用它:
292
+
293
+ ```jsonc
294
+ { "plugins": ["./vendor/team-mode"] } // 相对本文件,或者绝对路径的目录
295
+ ```
296
+
297
+ 这个目录**必须有根 `index.js`**。宿主解析目录只试 `<dir>/index`,别的不试 ——
298
+ `package.json#exports` 只对已安装的包生效,对路径不生效 —— 而解析不出入口的目录会被
299
+ **静默跳过**:没有报错、没有日志、也没有插件。本包发布时带上那个 `index.js` 就是为了这件事。
300
+
301
+ `opencode plugin add` 干不了这件事:给它路径会被拒绝
302
+ ("Plugin target must be an npm registry package or Git package specifier")。
303
+
304
+ #### 由 Agent 代用户在机器上执行
305
+
306
+ 两个安装脚本(`scripts/install.sh`、`scripts/install.ps1`)走的就是下面这套;如果你是被派来
307
+ 装机器的 Agent 而用户不想跑脚本,就按顺序做完,**尤其不要跳过最后一步**——配置写成功了不等于
308
+ 插件加载了,没有到达性检查就没有资格说"装好了"。
309
+
310
+ 1. 同时读 `~/.config/opencode/opencode.jsonc` 和 `opencode.json`,数一下两个文件里指向本包的
311
+ 条目一共有几条(`opencode-team-mode`,大小写和连字符都可能不一样)。多于一条 → 插件正在
312
+ 被加载两次,先修这件事再谈其他。
313
+ 2. 只写**一条**条目,写进已经装着用户其他配置的那个文件(优先 `.jsonc`,因为以前的安装器
314
+ 就写在那里)。别人家的条目一条都不许动,注释保留,`)`/`]` 前不要留悬挂逗号——宿主容忍它,
315
+ 下一个手工编辑的人未必容忍。
316
+ 3. 生成角色与命令(第 2 步)。2.x 上插件不能创建 agent,所以这十二个文件不在,Team 就不存在,
317
+ 插件加载得再好也没用。
318
+ 4. 最后一步才写 `default_agent: "team"`(第 3 步)——默认值指向不存在的 agent 会让宿主
319
+ 静默退回 `build`。
320
+ 5. 验证,按顺序,并且把你真正看到的说出来:
321
+ - `opencode-cli … run --standalone --print-logs`(或重启桌面端后读日志)→
322
+ **恰好一条** `msg="loading plugin" id=<我们的条目> entrypoint=file:///…/index.js`,
323
+ 并且**没有**指名我们的 `failed to load plugin`。
324
+ - 新建一个 Team 会话 → `tm_stats` 的 `启动与人格`:`tools_registered: 9`,以及 `作用域`
325
+ 那一行。如果 `agents_missing` 列出了角色名,说明第 3 步没做。
326
+ - 日志里关于我们的 id 一个字都没有 → 条目写在了宿主不读的键下(单数 `plugin`),或者那个
327
+ 目录没有根 `index.js`。这两种失败都是设计上静默的,所以"没报错"不是成功信号。
328
+ 6. 明确说清你留下的是哪种写法、包来自 npm 还是本地路径 —— 扩展面板分不出来:一个叫
329
+ `team-mode.js` 的 loader 文件,和一个插件 id 恰好是 `team-mode` 的目录条目,显示出来一模一样。
330
+
331
+ ### 第 2 步 — 生成六个角色和六条命令
332
+
333
+ 这一步就是 2.x 存在、而 v1 安装脚本从不需要的那件事。请运行**已安装包里**自带的那个生成器
334
+ ——不要手抄 markdown,否则 v1 注入的提示词和 v2 配置文件里的会悄悄分叉:
335
+
336
+ ```bash
337
+ node ~/.config/opencode/node_modules/@te-river/opencode-team-mode/scripts/gen-v2-config.mjs
338
+
339
+ # 只预览不写盘:
340
+ node …/scripts/gen-v2-config.mjs --print
341
+ # 写到别处(某个项目的 .opencode/,或者先写到临时目录做检查):
342
+ node …/scripts/gen-v2-config.mjs --dir ~/.config/opencode
343
+ ```
344
+
345
+ 期望输出 `共 12 个文件(角色 6 + 命令 6)`。它是幂等的:再跑一次什么都不写,只报
346
+ `已是最新`。不是它生成的文件(没有标记)会被**拒绝覆盖**,所以你自己手写的
347
+ `agents/team.md` 安全;要强行覆盖才用 `--force`。
348
+
349
+ Windows 上的路径是
350
+ `%USERPROFILE%\.config\opencode\node_modules\@te-river\opencode-team-mode\scripts\gen-v2-config.mjs`。
351
+
352
+ 生成的 `agents/<角色>.md` 里,frontmatter 放 `description` / `mode` / `color` 和权限三元组,
353
+ 正文是提示词的 **v2 变体**:所有点名了已退役工具的句子都会被改写,而且**只要某条改写规则
354
+ 在源文里找不到,生成就会失败**。这是刻意的——告诉一个 v2 模型"用 `tm_read`",代价就是那条
355
+ 规则本来要省下的那一轮。
356
+
357
+ ### 第 3 步 — 在配置里把 Team 设为默认
358
+
359
+ 插件每次启动也会调 `default("team")`,但 v2 插件读不回这个值,所以能核对的是配置键:
360
+
361
+ ```jsonc
362
+ { "default_agent": "team" }
363
+ ```
364
+
365
+ **顺序不能颠倒**:必须在第 2 步已经把角色文件写出去之后再设。默认值指向一个不存在的 agent
366
+ 时,宿主会**静默回落到 `build`**——你会看到新会话不是 Team,而现场没有任何解释。另外
367
+ `default_agent` 不会改动已经存在的会话所用过的 agent,它只作用于新会话。
368
+
369
+ ### 第 4 步 — 重启并核对
370
+
371
+ ```bash
372
+ opencode reload # 或者直接重启桌面端
373
+ ```
374
+
375
+ 按顺序做四项核对:
376
+
377
+ 1. `opencode agents`(或选择器里)能看到 `team`、`architect`、`implementer`、`reviewer`、
378
+ `tester`、`researcher`。
379
+ 2. 新会话默认是 Team。在里面跑 `tm_stats` —— 回复里应有**启动与人格**一段,展示 v2 的启动
380
+ 快照:注册了哪些工具、`tool.execute.after` / `permission.evaluate` / `session.context`
381
+ 有没有挂上、探针计到的东西,以及 `storage_probe`(`tm_ledger` 依赖的那次
382
+ `ctx.storage` 写读回环)。
383
+ 3. **JIT 真的在治理宿主工具**:让 Team 跑一条输出很多的命令(大目录树的 `ls -R` /
384
+ `dir /s`)。结果应该以"短预览 + 卸载句柄"回来,而不是整坨灌进上下文。若原样回来了,
385
+ 说明卸载层没挂上——`tm_stats` 会直说。
386
+ 4. `tm_ledger { action:"add", text:"冒烟测试" }` 应该回你一个编号,并写明
387
+ `已写入 ctx.storage`。这就是 v2 清单活着的样子。
388
+
389
+ ### 更新
390
+
391
+ ```bash
392
+ opencode plugin update @te-river/opencode-team-mode # 或者再跑一次 plugin add …@latest
393
+ node ~/.config/opencode/node_modules/@te-river/opencode-team-mode/scripts/gen-v2-config.mjs
394
+ opencode reload
395
+ ```
396
+
397
+ 第二条命令在升级之后不是可选项:角色文件里装的是提示词,新版本可能改过。重跑很便宜——没变
398
+ 的文件会被跳过。
399
+
400
+ ### 卸载
401
+
402
+ 1. 去掉插件条目(`opencode plugin remove @te-river/opencode-team-mode`)。
403
+ 2. 删掉生成的文件:`~/.config/opencode/agents/{team,architect,implementer,reviewer,tester,researcher}.md`
404
+ 和 `~/.config/opencode/commands/team-*.md`。只有带我们标记的文件是生成器写的,你自己写的
405
+ 东西它从不碰。
406
+ 3. 如果设过 `"default_agent": "team"`,一并删掉。
407
+ 4. 插件自己的数据在 `<repo>/.git/opencode-team/`(git 工作区)或系统临时目录下;删掉没有代价
408
+ ——TTL 清理器本来就会定期做同样的事。
409
+
410
+ ### 故障排查
411
+
412
+ | 现象 | 原因与处置 |
413
+ |---|---|
414
+ | 选择器里没有那六个角色 | 第 2 步没跑,或者 `--dir` 指到了别处。`ls ~/.config/opencode/agents` |
415
+ | 默认 agent 悄悄变成 `build` | `default_agent` 指向了一个不存在的角色——重跑第 2 步,再做第 3 步 |
416
+ | 手写的 `agents/team.md` 没被更新 | 这是设计:生成器拒绝覆盖不是它生成的文件。把你的文件挪开,或显式加 `--force` |
417
+ | `/team-plan` 能跑但不是以那个专家身份跑 | 2.x 把 `mode: subagent` 文档化为"只在子会话里运行"。如果选定专家的命令行为不对,改法是角色定义里用 `mode: "all"`(请回报——这是本流程里唯一还没在活体宿主上验证过的一项) |
418
+ | `tm_webfetch` 说它"没问任何人就直接拒绝" | v2 的预期行为:插件弹不出对话框。换一个不需要这次访问的源,或让宿主自己的权限规则放行;地址红线(元数据 / 私网)在两代宿主上都没有授权路径 |
419
+ | `subagent` 好像把领队挡住了 | v2 上插件会强制 `background: true`;如果你顺手设了老的 `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS`,请取消它——那是 v1 的开关,在这里只会误导判断 |
420
+ | 某个工具的输出没被卸载 | `TM_NATIVE_OFFLOAD=off`(或者那个 2.x 构建没有 `tool.execute.after` 缝)——`tm_stats` 会告诉你是哪一种 |
421
+ | 插件行为怪:两次弹窗、钩子重复、工具像注册了两遍 | 它被加载了两次。`opencode.json` 与 `opencode.jsonc` 是合并读取的,而宿主去重只认完全相同的字符串,所以同一个包换两种写法(或者同时躺在两个文件里)就是两套人格。把两个文件里指向 Team 的条目一起数一遍;`msg="loading plugin"` 对同一个 id 出现两条就是证据 |
422
+ | 写了条目,但宿主日志里关于它一个字都没有 | 两个"设计上静默"的原因:键名写成了单数 `plugin`(2.x 读 `plugins`),或者目录条目缺根 `index.js`。宿主解析目录只试 `<dir>/index`,试不出来就直接丢掉、不留任何消息 —— 在这里"没报错"不是成功信号 |
423
+ | 后台子代理的回复太长 | 子会话现在会被登记、可被收集(`tm_join` 会列出它,行上标着「宿主 subagent 派发」),但它的**正文**仍然是宿主的注入消息,v2 不会在落盘前把这条消息交给插件;我们也不改写发出的消息(猜错那一层的形状等于静默删证据)。所以超限交付走黑板文件:正文进文件、回复里带路径,领队读那个文件 |
424
+ | `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 里验证过 |
426
+ | 想知道宿主到底给了什么 | `TM_V2_PROBE=<路径>.jsonl` 只记工具名与权限动作名**以及计数**,绝不记命令行、路径、URL 或环境变量值;`tm_stats` 会把能力矩阵渲染出来 |
427
+
428
+ ### v2 专属的开关
429
+
430
+ | 变量 / 配置 | 默认 | 作用 |
431
+ |---|---|---|
432
+ | `TM_WEBFETCH_ALLOWED_DOMAINS` | `*`(**仅 v2**) | 域名门禁默认关掉,只留地址红线;显式给了值就以它为准(v1 仍是 24 个种子域名) |
433
+ | `TM_NATIVE_OFFLOAD` | on | 用 `tool.execute.after` 治理宿主原生工具的结果;off 就退回宿主原样 |
434
+ | `TM_R6_FINE_ASK` | on(v2) | 由按命令分类器决定哪条 shell 要问;`off` 回到"每条 shell 都问",宿主没有 `permission.hook` 时也自动回到这一档 |
435
+ | `TM_V2_PROBE` | 未设 | 把宿主真实工具面/动作名记成 JSONL(只有名字和计数) |
436
+ | `"team-mode": { "defaultAgent": false }` | 不设置 = Team 永远默认 | 退出默认位抢占(只影响插件的每次启动行为,配置里的 `default_agent` 请自己改回来) |
@@ -1,5 +1,17 @@
1
1
  # TeamMode Installation Guide / 安装指南
2
2
 
3
+ > **This page is the OpenCode 1.18.x path.** On OpenCode 2.x a plugin cannot create an
4
+ > agent, so the six roles and the six `/team-*` commands have to be generated into
5
+ > `~/.config/opencode/agents/` and `commands/` — that flow is its own page:
6
+ > **[installation-v2.md](./installation-v2.md)**. Check `opencode --version` first;
7
+ > following the wrong one leaves Team half-installed (the plugin loads, the roles do not
8
+ > exist).
9
+ >
10
+ > **本页是 1.18.x 的装法。** OpenCode 2.x 上插件不能创建 agent,六个角色和六条 `/team-*`
11
+ > 命令必须生成到 `~/.config/opencode/agents/` 与 `commands/`——那一套单独写在
12
+ > **[installation-v2.md](./installation-v2.md)**。先跑 `opencode --version` 再选:走错页面会
13
+ > 留下"插件加载了、角色不存在"的半安装状态。
14
+
3
15
  > **For LLM agents:** your user asked you to install `@te-river/opencode-team-mode`. This document is the complete, self-contained procedure. Read it fully, then execute. Do not improvise package names or config locations — everything you need is here.
4
16
  >
5
17
  > **面向 LLM 智能体:** 用户要求你安装 `@te-river/opencode-team-mode`。本文档是完整、自包含的安装规程。通读后再执行。不要 improvised 包名或配置路径——所需信息全在这里。
@@ -58,10 +70,46 @@ After restart, verify in this order:
58
70
 
59
71
  1. The agent picker (`@` mentions) lists: **team, architect, implementer, reviewer, tester, researcher**.
60
72
  2. Slash commands `/team-plan`, `/team-implement`, `/team-review`, `/team-test`, `/team-research`, `/team-run` autocomplete.
61
- 3. Optional deeper check: `npm view @te-river/opencode-team-mode version` shows the latest **published** version — but it queries the npm registry, **not** what OpenCode actually loads, so it misleads after an update. To confirm the **running** version, either read the `version` field of the cached `package.json` (`~/.cache/opencode/packages/@te-river/opencode-team-mode@latest/package.json` in the scoped layout, or `.../packages/@te_river+opencode-team-mode@latest/package.json` flat), or probe a feature that exists only in the new version (e.g. ask an agent to run `tm_memory` with `action: compact` — old builds reject it). Finally, ask the user to start a chat and confirm the **team** agent responds.
73
+ 3. Definitive check for the **running** build: ask an agent to run `tm_stats` and look at which rows appear. `npm view … version` queries the registry, not what OpenCode loads, so it misleads after an update; reading a `package.json` proves only the file, not the loaded code. A feature row (e.g. `宿主后台 task 注入`) either renders or it does not — that is the running build. Finally, ask the user to start a chat and confirm the **team** agent responds.
74
+
75
+ > **Where the loaded copy actually lives.** Under `~/.cache/opencode/packages/` the
76
+ > scoped layout is a **wrapper directory**: `packages/@te-river/opencode-team-mode@latest/`
77
+ > holds only `package.json` (`{"dependencies": {"@te-river/opencode-team-mode": "<version>"}}`),
78
+ > a `package-lock.json` and `node_modules/` — **the real package is the nested
79
+ > `…@latest/node_modules/@te-river/opencode-team-mode/`**, and that is the copy
80
+ > whose `dist/` executes. Overwriting a `dist/` at the wrapper root, or the one
81
+ > under `~/.config/opencode/node_modules/`, does nothing. (Costly to learn: it
82
+ > was found by a `tm_stats` row that refused to appear.)
62
83
 
63
84
  Report which checks passed. If verification fails, see Troubleshooting.
64
85
 
86
+ ### Step 5 — the visible sub-agent (the installer enables this)
87
+
88
+ OpenCode's own `task { background: true }` is the only sub-agent its interface
89
+ can **show**: the card links to the live child session, it does not block the
90
+ lead, and the host wakes the parent with the result. TeamMode then keeps that
91
+ result inside the token budget (`TM_TASK_OFFLOAD`).
92
+
93
+ It rides an OpenCode **experimental flag** read from the host process
94
+ environment at startup, so a plugin cannot set it — the installer writes it for
95
+ you:
96
+
97
+ | Platform | What the installer does | Revert |
98
+ |---|---|---|
99
+ | Windows | `setx OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS true` (user scope, survives reboot — **every program you start afterwards sees it, not just OpenCode**) | `REG delete HKCU\Environment /v OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS /f` |
100
+ | macOS | `launchctl setenv …` — reaches GUI apps this login, **not** after a logout | `launchctl unsetenv OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS` |
101
+ | Linux | `systemctl --user set-environment …` when a user session exists, else prints the `export` line | `systemctl --user unset-environment OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS` |
102
+
103
+ Skip it entirely: run `install.ps1 -NoBackgroundSubagents`, or set
104
+ `TEAMMODE_SKIP_BACKGROUND_SUBAGENTS=1` for a piped install. Without the flag
105
+ nothing breaks — `task` simply blocks the lead, and `tm_join` still collects
106
+ whatever children exist (snapshot, cancel, named-id read-back, restart recovery).
107
+
108
+ Cost, stated plainly: each finished background task wakes the lead and costs a
109
+ turn, and the host injects the child's reply verbatim until TeamMode replaces
110
+ an oversized one with a preview + pointer.
111
+
112
+
65
113
  ### Updating
66
114
 
67
115
  > ⚠️ **OpenCode loads the plugin from this spec-string cache under
@@ -269,10 +317,41 @@ never re-resolved on its own).
269
317
 
270
318
  1. Agent 选择器(`@` 提及)里出现:**team、architect、implementer、reviewer、tester、researcher**。
271
319
  2. 斜杠命令 `/team-plan`、`/team-implement`、`/team-review`、`/team-test`、`/team-research`、`/team-run` 可以自动补全。
272
- 3. 可选的深度检查:`npm view @te-river/opencode-team-mode version` 显示**已发布**的最新版本——但它查的是 npm registry,**不是** OpenCode 实际加载的东西,升级后会造成误判。要确认**运行版本**:读缓存目录里 `package.json` 的 `version`(作用域式 `~/.cache/opencode/packages/@te-river/opencode-team-mode@latest/package.json`,扁平式 `.../packages/@te_river+opencode-team-mode@latest/package.json`),或用只存在于新版的特性做探针(例:让 agent 调用 `tm_memory` 的 `action: compact`——旧版没有该 action 会报错)。最后请用户开一个会话确认 **team** agent 能正常响应。
320
+ 3. 判定**运行版本**最可靠的办法:让 agent 跑一次 `tm_stats`,看表里出现了哪些行。`npm view … version` 查的是 registry,不是 OpenCode 实际加载的东西,升级后会误判;读 `package.json` 也只证明文件、不证明被执行的代码。而某一行特性(例如 `宿主后台 task 注入`)要么在要么不在——那就是正在运行的构建。最后请用户开一个会话确认 **team** agent 能正常响应。
321
+
322
+ > **被加载的那份到底在哪。** `~/.cache/opencode/packages/` 下的作用域式目录是一个
323
+ > **包装层**:`packages/@te-river/opencode-team-mode@latest/` 里只有 `package.json`
324
+ > (内容仅是 `{"dependencies": {"@te-river/opencode-team-mode": "<版本>"}}`)、
325
+ > `package-lock.json` 和 `node_modules/`——**真正的包是嵌套在里面的
326
+ > `…@latest/node_modules/@te-river/opencode-team-mode/`**,被执行的是它的 `dist/`。
327
+ > 覆盖包装层根上的 `dist/`、或覆盖 `~/.config/opencode/node_modules/` 里那份,都不生效。
328
+ > (这条是踩出来的:覆盖后 `tm_stats` 里新行死活不出现,才发现还有一层。)
273
329
 
274
330
  报告哪些检查通过。验证失败时看"故障排查"。
275
331
 
332
+ ### 第 5 步 — 可见的子代理(安装脚本已默认开启)
333
+
334
+ OpenCode 自带的 `task { background: true }` 是唯一能在它界面上**看见**的子代理:
335
+ 卡片直接链到那个子会话、不阻塞 lead、完成时宿主把父会话唤醒。TeamMode 再把
336
+ 回来的结果压在 token 预算内(`TM_TASK_OFFLOAD`)。
337
+
338
+ 它依赖一个 OpenCode 的**实验性开关**,由宿主进程启动时读环境变量决定,插件无法
339
+ 自己打开——所以安装脚本替你写:
340
+
341
+ | 平台 | 安装脚本做什么 | 撤销 |
342
+ |---|---|---|
343
+ | Windows | `setx OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS true`(用户级,重启仍在——**之后你启动的每个程序都看得到它,不只是 OpenCode**) | `REG delete HKCU\Environment /v OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS /f` |
344
+ | macOS | `launchctl setenv …`——本次登录对 GUI 生效,**注销后失效** | `launchctl unsetenv OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS` |
345
+ | Linux | 有用户级 systemd 就 `systemctl --user set-environment …`,否则打印 `export` 那行 | `systemctl --user unset-environment OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS` |
346
+
347
+ 不想开:`install.ps1 -NoBackgroundSubagents`,或管道式安装前设
348
+ `TEAMMODE_SKIP_BACKGROUND_SUBAGENTS=1`。**不开也不会坏**——`task` 只是会阻塞 lead,
349
+ `tm_join` 照样负责回收(状态快照、取消、按 id 取回整篇回复、重启后接管)。
350
+
351
+ 代价说明白:每个后台任务完成都会唤醒 lead 一次、花一轮;而且在 TeamMode 把超限
352
+ 正文换成"预览 + 取回指针"之前,宿主注入的是全文。
353
+
354
+
276
355
  ### 更新
277
356
 
278
357
  > ⚠️ **OpenCode 加载插件用的是这份按 spec 字符串缓存的副本——