@te-river/opencode-team-mode 1.5.11 → 1.5.12

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 (89) hide show
  1. package/README.md +129 -53
  2. package/README.zh-CN.md +103 -45
  3. package/dist/agents.d.ts.map +1 -1
  4. package/dist/agents.js +17 -11
  5. package/dist/agents.js.map +1 -1
  6. package/dist/approval-gate.d.ts +54 -16
  7. package/dist/approval-gate.d.ts.map +1 -1
  8. package/dist/approval-gate.js +163 -42
  9. package/dist/approval-gate.js.map +1 -1
  10. package/dist/prompts/lead.d.ts +1 -1
  11. package/dist/prompts/lead.d.ts.map +1 -1
  12. package/dist/prompts/lead.js +22 -3
  13. package/dist/prompts/lead.js.map +1 -1
  14. package/dist/prompts/shared.d.ts +1 -1
  15. package/dist/prompts/shared.d.ts.map +1 -1
  16. package/dist/prompts/shared.js +154 -119
  17. package/dist/prompts/shared.js.map +1 -1
  18. package/dist/prompts/specialists.d.ts +2 -2
  19. package/dist/prompts/specialists.d.ts.map +1 -1
  20. package/dist/prompts/specialists.js +40 -11
  21. package/dist/prompts/specialists.js.map +1 -1
  22. package/dist/tm/args-schema.d.ts +25 -1
  23. package/dist/tm/args-schema.d.ts.map +1 -1
  24. package/dist/tm/args-schema.js +149 -39
  25. package/dist/tm/args-schema.js.map +1 -1
  26. package/dist/tm/browser.d.ts +188 -19
  27. package/dist/tm/browser.d.ts.map +1 -1
  28. package/dist/tm/browser.js +781 -111
  29. package/dist/tm/browser.js.map +1 -1
  30. package/dist/tm/config.d.ts +61 -0
  31. package/dist/tm/config.d.ts.map +1 -1
  32. package/dist/tm/config.js +55 -1
  33. package/dist/tm/config.js.map +1 -1
  34. package/dist/tm/index.d.ts +5 -3
  35. package/dist/tm/index.d.ts.map +1 -1
  36. package/dist/tm/index.js +36 -2
  37. package/dist/tm/index.js.map +1 -1
  38. package/dist/tm/memory.d.ts +88 -10
  39. package/dist/tm/memory.d.ts.map +1 -1
  40. package/dist/tm/memory.js +881 -99
  41. package/dist/tm/memory.js.map +1 -1
  42. package/dist/tm/pipelines.d.ts +26 -3
  43. package/dist/tm/pipelines.d.ts.map +1 -1
  44. package/dist/tm/pipelines.js +167 -11
  45. package/dist/tm/pipelines.js.map +1 -1
  46. package/dist/tm/ptc/budgets.d.ts +22 -1
  47. package/dist/tm/ptc/budgets.d.ts.map +1 -1
  48. package/dist/tm/ptc/budgets.js +87 -8
  49. package/dist/tm/ptc/budgets.js.map +1 -1
  50. package/dist/tm/ptc/contract.d.ts +49 -9
  51. package/dist/tm/ptc/contract.d.ts.map +1 -1
  52. package/dist/tm/ptc/contract.js +41 -3
  53. package/dist/tm/ptc/contract.js.map +1 -1
  54. package/dist/tm/ptc/driver.d.ts +5 -2
  55. package/dist/tm/ptc/driver.d.ts.map +1 -1
  56. package/dist/tm/ptc/driver.js +47 -15
  57. package/dist/tm/ptc/driver.js.map +1 -1
  58. package/dist/tm/ptc/engines.d.ts +15 -4
  59. package/dist/tm/ptc/engines.d.ts.map +1 -1
  60. package/dist/tm/ptc/engines.js +281 -28
  61. package/dist/tm/ptc/engines.js.map +1 -1
  62. package/dist/tm/ptc/index.d.ts +4 -4
  63. package/dist/tm/ptc/index.d.ts.map +1 -1
  64. package/dist/tm/ptc/index.js +2 -2
  65. package/dist/tm/ptc/index.js.map +1 -1
  66. package/dist/tm/ptc/pscan.d.ts +20 -1
  67. package/dist/tm/ptc/pscan.d.ts.map +1 -1
  68. package/dist/tm/ptc/pscan.js +328 -14
  69. package/dist/tm/ptc/pscan.js.map +1 -1
  70. package/dist/tm/ptc/summary.d.ts.map +1 -1
  71. package/dist/tm/ptc/summary.js +23 -1
  72. package/dist/tm/ptc/summary.js.map +1 -1
  73. package/dist/tm/ptc/tool.d.ts +34 -3
  74. package/dist/tm/ptc/tool.d.ts.map +1 -1
  75. package/dist/tm/ptc/tool.js +117 -20
  76. package/dist/tm/ptc/tool.js.map +1 -1
  77. package/dist/tm/search.d.ts +83 -14
  78. package/dist/tm/search.d.ts.map +1 -1
  79. package/dist/tm/search.js +440 -45
  80. package/dist/tm/search.js.map +1 -1
  81. package/dist/tm/shell-bridge.d.ts +14 -1
  82. package/dist/tm/shell-bridge.d.ts.map +1 -1
  83. package/dist/tm/shell-bridge.js +57 -10
  84. package/dist/tm/shell-bridge.js.map +1 -1
  85. package/dist/tm/webfetch.d.ts +25 -7
  86. package/dist/tm/webfetch.d.ts.map +1 -1
  87. package/dist/tm/webfetch.js +88 -14
  88. package/dist/tm/webfetch.js.map +1 -1
  89. package/package.json +5 -2
package/README.md CHANGED
@@ -44,7 +44,7 @@ TeamMode's answer to each:
44
44
 
45
45
  | Pain | TeamMode's answer |
46
46
  |---|---|
47
- | 🔥 **Context flooding** | Every governed tool output over 2000 tokens is offloaded to a local run store and replaced by an 80-token preview + an HMAC handle. The agent pages through what it needs — the window never drowns. |
47
+ | 🔥 **Context flooding** | Every governed tool output over its content-class offload threshold (prose 4000 / data 2000 tokens, CJK-aware) is offloaded to a local run store and replaced by an 80-token preview + an HMAC handle. The agent pages through what it needs — the window never drowns. |
48
48
  | 🐌 **Round-trip overhead** | `tm_ptc_run`: the agent writes ONE program that makes N governed calls in a single turn. Zero LLM round-trips during the run. |
49
49
  | 🕳️ **Silent side effects** | R6/R2 approval gate: env-var reads and dangerous ops route through OpenCode's official confirmation dialog, auto-rejected after 10 unanswered minutes. The plugin never approves on its own — it only ever rejects. |
50
50
  | 🌫️ **Hallucinated research** | Web access is a two-role grant with an allowlisted, governed tool chain. A fact that couldn't be fetched is reported as a gap — never fabricated. |
@@ -201,19 +201,22 @@ as a structured skeleton — nothing landed in your repo, nothing was guessed.
201
201
  ## 🧰 Governed tools & security
202
202
 
203
203
  Every tool TeamMode adds runs under ONE governance pipeline: outputs above
204
- `TM_OFFLOAD_THRESHOLD` tokens never enter the context window — they are
205
- offloaded to a run store and replaced by a content-aware preview plus an
206
- HMAC-signed handle that the agent pages through with `tm_fetch` when it
207
- genuinely needs the payload.
204
+ the offload threshold never enter the context window — the boundary is
205
+ content-class aware (`TM_OFFLOAD_THRESHOLD_TEXT` = 4000 for prose:
206
+ text/log/markdown; `TM_OFFLOAD_THRESHOLD_DATA` = 2000 for json/csv/code/
207
+ binary; an unknown class falls back to the global `TM_OFFLOAD_THRESHOLD`) —
208
+ and oversized results are offloaded to a run store and replaced by a
209
+ content-aware preview plus an HMAC-signed handle that the agent pages
210
+ through with `tm_fetch` when it genuinely needs the payload.
208
211
 
209
212
  | Tool | What it does | Roles |
210
213
  |---|---|---|
211
- | `tm_read` / `tm_grep` / `tm_bash` / `tm_fetch` | Governed file read / regex search / read-only shell (allowlist) / paged handle retrieval | all six agents |
212
- | `tm_memory` | Project + global memory store (Markdown + frontmatter): add / search / list / forget | all six agents |
213
- | `tm_ptc_run` | Batch orchestration: one program, N governed calls, zero LLM round-trips | all six agents |
214
- | `tm_search` | Multi-engine web search with extracted, deduplicated hit lists | Lead + Researcher |
214
+ | `tm_read` / `tm_grep` / `tm_bash` / `tm_fetch` | Governed file read / regex search / read-only shell (allowlist) / paged handle retrieval (JSON handles take a `fields` dot-path projection — a deliberately small jq subset like `items[].name`) | all six agents |
215
+ | `tm_memory` | Session + project + global memory store (Markdown + frontmatter): add / search / list / forget / compact | all six agents |
216
+ | `tm_ptc_run` | Batch orchestration: one program, N governed calls, zero LLM round-trips; web roles also get `tm.search` / `tm.webfetch` inside the program | all six agents |
217
+ | `tm_search` | Multi-engine web search with extracted, deduplicated, RRF-fused hit lists | Lead + Researcher |
215
218
  | `tm_webfetch` | Single governed GET of an allowlisted page (search pages auto-extracted) | Lead + Researcher |
216
- | `tm_browser` | Interactive browser session (CDP, **your default browser**): open / navigate / read / screenshot / close | Lead + Researcher + Tester (UI verification) |
219
+ | `tm_browser` | Interactive browser session (**your default browser**): 16 Playwright verbs (snapshot-first `take_snapshot` → uid-addressed `click`/`fill`/`drag`/…) + 5 legacy compat verbs (open/navigate/read/screenshot/close); Playwright engine needs Node ≥ 20, below that (or on any import failure) it auto-degrades to the legacy CDP engine | Lead + Researcher + Tester (UI verification) |
217
220
 
218
221
  > **Fixed tool priority ladder (every task): ① TeamMode governed tools
219
222
  > (`tm_*`) → ② user MCP/plugin tools → ③ the model's own reasoning.**
@@ -228,26 +231,48 @@ its own step id and its own payload, and nothing cross-contaminates
228
231
  ### Context governance: offload, handles, previews
229
232
 
230
233
  Large tool outputs are context cost's main driver — every step re-sends the
231
- whole window. So results above `TM_OFFLOAD_THRESHOLD` tokens are written to a
232
- local run store (`<repo>/.git/opencode-team/`, never your working tree) and
233
- replaced by a handle with a content-aware preview: JSON keys / CSV header +
234
- shape / log ERROR×N stats / code signatures / binary metadata, hard-capped at
235
- 80 tokens. When the agent actually needs the payload, it pages through with
236
- `tm_fetch` using an HMAC-signed, run-scoped, expiring handle. `tm_bash` only
237
- allows read-only commands (allowlist), and failures come back as structured
238
- errors instead of raw dumps.
239
-
240
- ### Project + global memory (tm_memory)
234
+ whole window. So results above the content-class offload boundary (prose →
235
+ `TM_OFFLOAD_THRESHOLD_TEXT`, structured data → `TM_OFFLOAD_THRESHOLD_DATA`,
236
+ unknown → global `TM_OFFLOAD_THRESHOLD`) are written to a local run store
237
+ (`<repo>/.git/opencode-team/`, never your working tree) and replaced by a
238
+ handle with a content-aware preview: JSON keys / CSV header + shape / log
239
+ ERROR×N stats / code signatures / binary metadata, hard-capped at 80 tokens.
240
+ When the agent actually needs the payload, it pages through with `tm_fetch`
241
+ using an HMAC-signed, run-scoped, expiring handle — and on a JSON handle it
242
+ can ask for a `fields` dot-path projection instead (a deliberately small jq
243
+ subset: `items[].name`, `[].stargazers_count`), so a big API dump narrows to
244
+ just the values needed without the raw body ever entering the window.
245
+ `tm_bash` only allows read-only commands (allowlist), and failures come back
246
+ as structured errors instead of raw dumps.
247
+
248
+ ### Session + project + global memory (tm_memory)
241
249
 
242
250
  Durable facts — build commands, environment quirks, architecture decisions,
243
- your conventions — live as human-editable Markdown with frontmatter:
244
-
245
- - **`project`** (default): `<repo>/.git/opencode-team/memories/…` — per checkout, git-adjacent.
246
- - **`global`**: `~/.opencode-team/memories/global/` (override `TM_MEMORY_GLOBAL_DIR`) — **follows you across ALL projects**.
247
-
248
- Actions: `add` / `search` (deterministic keyword scoring) / `list` / `forget`;
249
- 4000 chars per memory. Agents are prompted to search before assuming
250
- conventions and to save hard-won facts for the next conversation.
251
+ your conventions — live as human-editable Markdown with frontmatter, in
252
+ THREE tiers:
253
+
254
+ - **`session`**: this conversation's transients only — in-process, TTL-swept
255
+ (`TM_MEMORY_SESSION_TTL_MIN`, default 240 min), invisible to other
256
+ sessions; ephemeral unless `TM_MEMORY_SESSION_PERSIST=1` writes them under
257
+ `memories/sessions/<sid>/`.
258
+ - **`project`** (default): `<repo>/.git/opencode-team/memories/…` — per checkout, git-adjacent. Facts about THIS repo: build commands, environment quirks, architecture decisions.
259
+ - **`global`**: `~/.opencode-team/memories/global/` (override `TM_MEMORY_GLOBAL_DIR`) — **follows you across ALL projects**. User-level conventions: preferred package manager, commit style, tooling habits.
260
+
261
+ Actions: `add` / `search` (deterministic keyword scoring) / `list` / `forget` /
262
+ `compact`; 4000 chars per memory. `search` walks ALL tiers with **session >
263
+ project > global precedence** — project entries get a +2 near-tie weight and a
264
+ same-title higher-layer twin shadows the lower one (it never surfaces).
265
+ Near-duplicates never pile up: an `add` that hits an existing entry in the same
266
+ tier and category (dedup key, or title+keywords Jaccard ≥ 0.6) **folds into
267
+ it** — new content wins, keywords union, the old slug moves to `supersedes:`,
268
+ and the answer says 已合并 (that is normal; don't re-add under a variant title).
269
+ When a tier reaches `TM_MEMORY_MAX_ENTRIES` (200 per scope) the add fails on
270
+ purpose: run `compact` first — it dry-runs the merge plan by default, and
271
+ re-running with `apply:true` performs it after copying every original to a
272
+ timestamped `.compact-backup` tree (the rollback path). Entries older than
273
+ `TM_MEMORY_STALE_DAYS` (30) surface tagged `[stale Nd]` in search. Agents
274
+ are prompted to search before assuming conventions and to save hard-won
275
+ facts for the next conversation.
251
276
 
252
277
  ### 🌐 Web search that actually works (in China)
253
278
 
@@ -258,18 +283,22 @@ sees raw SERP chrome.
258
283
 
259
284
  | Engine | Notes |
260
285
  |---|---|
261
- | `bing` (default) | cn.bing.com; `bing-int` forces international results (`ensearch=1`) |
262
- | `sogou` / `so` (360) | CN-native engines, good for CJK content |
263
- | `baidu` | flakiest (anti-bot) but sometimes the only CN-specific index; failures name alternatives |
286
+ | `auto` (default) | Classifies the query, fans out to 2–3 engines **in parallel**, dedupes by host+path and fuses with weighted RRF (stackoverflow/bing 0.4, others 0.2) into a top-10 list tagged with each hit's source engine(s). Routing: errors / camelCase APIs → `stackoverflow`+`github`+`bing`; dev-ecosystem (releases, frameworks, open source) → `hn`+`github`+`npm`; Chinese / general → `bing`. Pin another default via `TM_SEARCH_DEFAULT_ENGINE` |
287
+ | `bing` | cn.bing.com — the only live CN HTML SERP; multi-word CJK queries get their phrase boundary protected (quoted) so markup shuffle can't split the result list |
288
+ | `stackoverflow` | api.stackexchange.com question search (no key, 300/day/IP) → numbered questions with composite snippets; `auto` tracks the quota and swaps in `bing` once spent |
289
+ | `hn` | Hacker News via Algolia API (no key) → story titles + snippets with direct article URLs |
264
290
  | `bilibili` | video search |
265
291
  | `moegirl` | MediaWiki search API — entry titles + snippets, structured |
266
292
  | `npm` | registry search → name@version + description, structured |
267
- | `github` | repo search API → stars + description, structured |
293
+ | `github` | repo search API → stars + description, structured; `org:` / `user:` / `stars:` / `language:` qualifiers fold into the query (e.g. `vector db stars:>500 language:rust`) |
268
294
 
269
- All nine engines are reachable from mainland China **without API keys**, and
270
- every one of them sits on the seeded domain allowlist. On an empty result
271
- (an anti-bot shell), the error names the alternative engines instead of
272
- leaving the agent stuck. Two more channels complete the surface:
295
+ All seven engines are reachable from mainland China **without API keys**, and
296
+ every one of them sits on the seeded domain allowlist. The previous CN HTML
297
+ SERPs (`sogou` / `so` / `baidu` / `bing-int`) were **removed** — a live
298
+ benchmark (2026-09-14) showed them serving anti-bot shells or 100% empty
299
+ results; they are not even manually selectable. On an empty result,
300
+ the error names the alternative engines instead of leaving the agent stuck.
301
+ Two more channels complete the surface:
273
302
 
274
303
  - `tm_webfetch` — a known URL, one governed GET. Search-engine pages it
275
304
  fetches are auto-extracted to hit lists too. JSON endpoints like
@@ -277,9 +306,28 @@ leaving the agent stuck. Two more channels complete the surface:
277
306
  - `tm_browser` — JS-rendered pages: **your DEFAULT browser** (Windows
278
307
  registry / Linux `xdg-settings`; Chromium-family only — Firefox falls back
279
308
  to the Edge/Chrome probe order because CDP is Chromium-proprietary;
280
- `TM_BROWSER_PATH` overrides), headful via CDP pipe, isolated temp profile,
281
- **domain allowlist enforced at the network layer** per request
282
- (`Fetch.requestPaused` → non-allowlisted hosts get `BlockedByClient`).
309
+ `TM_BROWSER_PATH` overrides), headful by default, isolated temp profile,
310
+ **domain allowlist enforced at the network layer** per request and per
311
+ redirect hop. The action surface is chrome-devtools-mcp aligned: **16
312
+ Playwright verbs** (`navigate_page` · `take_snapshot` · `click` · `fill` ·
313
+ `hover` · `drag` · `press_key` · `select_page` · `upload_file` · `wait_for`
314
+ · `evaluate_script` · `list_console_messages` · `list_network_requests` ·
315
+ `list_pages` · `take_screenshot` · `handle_dialog`) plus 5 legacy compat
316
+ verbs (`open` / `navigate` / `read` / `screenshot` / `close`).
317
+ Snapshot-first: `take_snapshot` returns the aria snapshot with injected
318
+ `[uid=eN]` tokens, follow-up actions address nodes by uid instead of
319
+ guessed locators; snapshots are hard-capped by
320
+ `TM_BROWSER_SNAPSHOT_MAX_TOKENS` (default 1200).
321
+ **Engine split:** the primary engine is `playwright-core` (an optional
322
+ dependency — needs **Node ≥ 20**; on older Node, or if the import fails,
323
+ the session auto-degrades per instance to the zero-dep `cdp-legacy`
324
+ engine, which keeps the core verbs only; pin either via
325
+ `TM_BROWSER_ENGINE=playwright|cdp-legacy`). No browser download is ever
326
+ involved — Playwright launches YOUR installed browser by path, so
327
+ `npx playwright install` is not part of the user flow (the dependency
328
+ itself resolves at npm install/publish time). Isolated temp profile by
329
+ default: persistent logins only if you explicitly set
330
+ `TM_BROWSER_USER_DATA_DIR`.
283
331
 
284
332
  When a fetch still returns **403 after the real-Chrome headers**, the error
285
333
  is a DIRECTIVE: the gate is JS-challenge / TLS-fingerprint based and only a
@@ -289,16 +337,18 @@ filter known noise: engine-internal wrappers (`so.com/link?`, `ai.so.com`)
289
337
  and same-name-different-site domains (`maimai.cn` 脉脉 vs the maimai DX
290
338
  game) never ride along — extend the hit blacklist with `TM_HIT_BLACKLIST`.
291
339
 
292
- Seeded allowlist (both tools; 21 hosts — baidu/moegirl/bilibili are PARENT
293
- domains, so every sibling subdomain — baike.baidu.com, mzh.moegirl.org.cn,
294
- space.bilibili.com — is covered):
340
+ Seeded allowlist (all three web tools; 23 hosts — baidu/moegirl/bilibili are
341
+ PARENT domains, so every sibling subdomain — baike.baidu.com,
342
+ mzh.moegirl.org.cn, space.bilibili.com — is covered):
295
343
  `baidu.com`, `moegirl.org.cn`, `bilibili.com`, `www.sogou.com`, `www.so.com`,
296
344
  `cn.bing.com`, `www.bing.com`, `zhihu.com`, `juejin.cn`, `csdn.net`,
297
345
  `cnblogs.com`, `gitee.com`, `github.com`, `api.github.com`,
298
346
  `raw.githubusercontent.com`, `gist.githubusercontent.com`, `ghproxy.net`
299
- (mainland mirror for github raw), `stackoverflow.com`, `npmjs.org`,
300
- `pypi.org`, `learn.microsoft.com` — extend via
301
- `TM_WEBFETCH_ALLOWED_DOMAINS` (`"*"` opens every host). Architect /
347
+ (mainland mirror for github raw), `stackoverflow.com`, `api.stackexchange.com`
348
+ + `hn.algolia.com` (the two JSON search engines), `npmjs.org`, `pypi.org`,
349
+ `learn.microsoft.com` — extend via
350
+ `TM_WEBFETCH_ALLOWED_DOMAINS` (`"*"` opens every host; a custom list REPLACES
351
+ the seed, so keep the engine hosts or `tm_search` loses its targets). Architect /
302
352
  implementer / reviewer have NO network grant — web questions come back as a
303
353
  reported gap, never simulated. The tester carries `tm_browser` ONLY, for
304
354
  governed UI verification of the project (local dev servers, preview routes);
@@ -307,7 +357,7 @@ open web fetching stays with the two network roles.
307
357
  **Out-of-allowlist targets are a gate, not a wall.** When a fetch / search /
308
358
  browser-open points at a host outside the allowlist, the tool hands the URL
309
359
  to OpenCode's **official confirmation dialog** — you decide, once per
310
- target (an unanswered dialog is auto-rejected on the usual 10-minute timer,
360
+ target (an unanswered dialog is auto-rejected on the usual 1-minute timer,
311
361
  and the plugin still never self-allows). Every dialog also fires a
312
362
  **system toast notification**, so you know something is waiting even when
313
363
  you're not staring at the screen. Env-file URLs and non-http(s) schemes
@@ -319,11 +369,21 @@ remain hard-rejected with no dialog — R6 red lines are never consentable.
319
369
  environment variables silently. Env reads (`printenv`, `env`, `Get-ChildItem
320
370
  env:`, …) and env files (`.env`, shell rc) route through OpenCode's official
321
371
  confirmation dialog; unanswered prompts are **auto-rejected after
322
- `TM_ASK_TIMEOUT_MIN` (default 10 min)**. Env reads no wildcard can express
372
+ `TM_ASK_TIMEOUT_MIN` (default 1 min)**. Env reads no wildcard can express
323
373
  (embedded `$VAR` / `${VAR}` / `$env:` inside another command, command
324
374
  substitution) and the `tm_*` wrapper channel stay a **hard block** — no dialog
325
375
  to slip through. The audit log records only tool name + pattern category +
326
- verdict — never command text, paths, variable names or values.
376
+ verdict — never command text, paths, variable names or values. Verdicts now
377
+ also explain a dialog that cannot be answered anymore: a reply that hits an
378
+ **already-closed** dialog (host 404 — the timer already auto-rejected) audits
379
+ as `already-closed` without degrading the gate, a plugin-side bad reply shape
380
+ audits as `rejected-shape-bug`, and a user reply that arrives *after* the
381
+ auto-reject is recorded as `late-<verdict>` for observability only (the
382
+ rejection stands — the plugin still never self-allows). The 1-minute auto-reject
383
+ floor is tunable via `TM_ASK_TIMEOUT_FLOOR_MIN`; the short default is safe
384
+ because a reply that races the timer audits as benign `already-closed` (host
385
+ 404 on a closed dialog — no gate degrade), and the observed ~120 s is the
386
+ host-to-plugin event-bus *delivery* lag, not click-resolution latency.
327
387
 
328
388
  **R2 dangerous operations (same dialog).** Delete, git publish, network
329
389
  fetch, package install/publish, process/system, privilege changes — none are
@@ -390,24 +450,36 @@ for overrides, extra agents and disabling roles.
390
450
  | Env var | Default | Purpose |
391
451
  |---|---|---|
392
452
  | `TM_ENV_PROTECT` | `strict` | R6 mode: `strict` / `standard` / `off` (off also disarms the approval timer) |
393
- | `TM_ASK_TIMEOUT_MIN` | `10` | minutes before an unanswered dialog is auto-rejected (hard floor 3 — the host's reply event reaches the plugin ~120 s late) |
453
+ | `TM_ASK_TIMEOUT_MIN` | `1` | minutes before an unanswered dialog is auto-rejected (floored at 1 min — safe: a racing reply audits as benign `already-closed`; the host's reply event reaching the plugin ~120 s late is event-bus delivery lag, not a click delay) |
454
+ | `TM_ASK_TIMEOUT_FLOOR_MIN` | `1` | minimum enforced for the ask timeout above |
394
455
  | `TM_ENV_PROTECT_EXTRA_DENY` | — | extra block patterns (regex; always hard block, never dialog-governed) |
395
- | `TM_OFFLOAD_THRESHOLD` | `2000` | offload threshold (tokens, CJK-aware estimate) |
456
+ | `TM_OFFLOAD_THRESHOLD` | `2000` | global offload fallback (tokens, CJK-aware estimate) — used when the content class is unknown |
457
+ | `TM_OFFLOAD_THRESHOLD_TEXT` | `4000` | offload boundary for prose (text / log / markdown) |
458
+ | `TM_OFFLOAD_THRESHOLD_DATA` | `2000` | offload boundary for structured payloads (json / csv / code / binary) |
396
459
  | `TM_PREVIEW_MAX_TOKENS` | `80` | preview hard cap |
397
460
  | `TM_FETCH_MAX_LINES` | `2000` | tm_fetch page cap |
398
461
  | `TM_BLACKBOARD_DIR` / `TM_TRAJECTORY_DIR` | `<repo>/.git/opencode-team/…` | offload store / trajectory ledger (tmpdir fallback; explicit = absolute or project-relative) |
399
462
  | `TM_BLACKBOARD_TTL` | `7` | store retention (days) |
400
463
  | `TM_BASH_READONLY_ALLOWED` | built-in table | tm_bash allowlist |
401
- | `TM_WEBFETCH_ALLOWED_DOMAINS` | the 21 seeded hosts | tm_webfetch / tm_search / tm_browser allowlist (`"*"` opens all; empty = deny all) |
464
+ | `TM_SEARCH_DEFAULT_ENGINE` | `auto` | tm_search engine when no `engine` arg is given (`auto` = classify + parallel fan-out + RRF fusion; any table name also pins a manual default) |
465
+ | `TM_WEBFETCH_ALLOWED_DOMAINS` | the 23 seeded hosts | tm_webfetch / tm_search / tm_browser allowlist (`"*"` opens all; empty = deny all; a custom list REPLACES the seed — keep the engine hosts) |
402
466
  | `TM_BROWSER_PATH` | auto-detect | tm_browser executable override (default: your DEFAULT browser when Chromium-family, else Edge/Chrome probes) |
403
467
  | `TM_BROWSER_HEADLESS` | `auto` | `1` headless (CI) / `0` headful / `auto` (headless only on display-less Linux) |
404
- | `TM_MEMORY_GLOBAL_DIR` | `~/.opencode-team/memories/global/` | tm_memory GLOBAL scope store |
468
+ | `TM_BROWSER_ENGINE` | `playwright` | `playwright` (needs Node ≥ 20; any import failure auto-degrades) / `cdp-legacy` (zero-dep CDP pipe, core verbs only) |
469
+ | `TM_BROWSER_SNAPSHOT_MAX_TOKENS` | `1200` | hard cap on `take_snapshot` payloads |
470
+ | `TM_BROWSER_USER_DATA_DIR` | — (isolated temp profile) | explicit persistent profile dir — the ONLY way logins survive between sessions |
471
+ | `TM_MEMORY_GLOBAL_DIR` | `~/.opencode-team/memories/global/` | tm_memory GLOBAL tier store |
472
+ | `TM_MEMORY_SESSION_TTL_MIN` | `240` | session-tier entry TTL (lazy + boot sweep) |
473
+ | `TM_MEMORY_MAX_ENTRIES` | `200` | per-scope entry cap; over it `add` fails on purpose — run `compact` |
474
+ | `TM_MEMORY_STALE_DAYS` | `30` | age after which search hits are tagged `[stale Nd]` (`0` disables) |
475
+ | `TM_MEMORY_SESSION_PERSIST` | — (ephemeral) | `1` also writes session entries under `memories/sessions/<sid>/` |
405
476
  | `TM_HIT_BLACKLIST` | `maimai.cn` | extra domains never listed as search hits (comma/semicolon separated; same-name-different-site noise like 脉脉) |
406
477
  | `TM_PTC_MAX_PROGRAM_CHARS` | `4000` | PTC program source cap |
407
478
  | `TM_PTC_MAX_CALLS` | `20` | PTC per-run bridge-call budget (1–200) |
408
479
  | `TM_PTC_MAX_ERRORS` | `3` | PTC per-run error budget (1–50) |
409
480
  | `TM_PTC_TIMEOUT_MS` | `60000` | PTC per-run wall-clock timeout (5s–10min) |
410
481
  | `TM_PTC_ENGINE` | `auto` | `auto` (worker→inline degrade) / `worker` / `inline` |
482
+ | `TM_PTC_WEB_BRIDGE` | `on` | expose `tm.search` / `tm.webfetch` to PTC programs (`off` removes them from the bridge set) |
411
483
 
412
484
  ---
413
485
 
@@ -427,6 +499,10 @@ not the rule: a deliverable over ~50 lines goes to ONE named board file under
427
499
  routing bug.
428
500
  - **Approval gate (count-based):** ≥2 dispatches → plan (≤30 lines) → **your
429
501
  approval** → execute. Blocking questions are batched and asked immediately.
502
+ - **Concurrent dispatch:** independent dispatches batch into the SAME round
503
+ (parallel implementers with per-file ownership + verbatim contracts,
504
+ 3-dimension reviews, split test suites); only the Team Lead dispatches —
505
+ specialists no longer hold `task`, so no sub-agent spawns a sub-agent.
430
506
  - **Adaptive review:** one reviewer by default; three parallel dimensions
431
507
  only for high-risk profiles (auth/security, cross-module contracts, public APIs).
432
508
  - **Static verification:** build / typecheck / lint / tests. Improvised
@@ -506,7 +582,7 @@ the whole workflow are host-agnostic. On display-less Linux, `tm_browser`
506
582
  runs headless automatically.
507
583
 
508
584
  **What happens if I don't answer a confirmation dialog?**
509
- It auto-rejects after `TM_ASK_TIMEOUT_MIN` (default 10). The plugin never
585
+ It auto-rejects after `TM_ASK_TIMEOUT_MIN` (default 1). The plugin never
510
586
  self-approves — the only side it can take is yours or nobody's.
511
587
 
512
588
  ---
package/README.zh-CN.md CHANGED
@@ -43,9 +43,9 @@ TeamMode 对每一个的回应:
43
43
 
44
44
  | 痛点 | TeamMode 的回答 |
45
45
  |---|---|
46
- | 🔥 **上下文爆炸** | 所有受治理工具的输出超过 2000 token 就卸载到本地 run 存储,换成 80 token 的预览 + HMAC 句柄。agent 需要什么再分页取什么——窗口永远淹不了。 |
46
+ | 🔥 **上下文爆炸** | 所有受治理工具的输出超过内容分档阈值(散文 4000 / 数据 2000 token,CJK 感知)就卸载到本地 run 存储,换成 80 token 的预览 + HMAC 句柄。agent 需要什么再分页取什么——窗口永远淹不了。 |
47
47
  | 🐌 **回合开销** | `tm_ptc_run`:agent 写**一个程序**,单回合内发起 N 次受治理调用。运行期间零 LLM 回合。 |
48
- | 🕳️ **静默副作用** | R6/R2 审批门禁:环境变量读取和危险操作走 OpenCode 官方确认弹窗,10 分钟没人理自动拒绝。插件从不代替你批准——它只会拒绝。 |
48
+ | 🕳️ **静默副作用** | R6/R2 审批门禁:环境变量读取和危险操作走 OpenCode 官方确认弹窗,1 分钟没人理自动拒绝。插件从不代替你批准——它只会拒绝。 |
49
49
  | 🌫️ **幻觉式调研** | 联网是双角色的授权 + 白名单受治理工具链。抓不到的事实就报告为缺口——绝不编造。 |
50
50
 
51
51
  底下还有流程纪律:确定性路由表、≥2 次派工先出 ≤30 行计划等你批、agent
@@ -196,18 +196,21 @@ team 完成。rateLimit.ts (new) · app.ts (+3) · 14 测试全绿
196
196
 
197
197
  ## 🧰 受治理的工具与安全
198
198
 
199
- TeamMode 加的每个工具都跑在**同一条治理管线**下:超过 `TM_OFFLOAD_THRESHOLD`
200
- token 的输出永不进入上下文窗口——卸载到 run 存储,换成内容感知预览 +
199
+ TeamMode 加的每个工具都跑在**同一条治理管线**下:超过卸载阈值的输出永不
200
+ 进入上下文窗口——阈值按内容分档:散文(text/log/markdown)走
201
+ `TM_OFFLOAD_THRESHOLD_TEXT`(4000),结构化数据(json/csv/code/binary)走
202
+ `TM_OFFLOAD_THRESHOLD_DATA`(2000),内容类别未知时回退全局
203
+ `TM_OFFLOAD_THRESHOLD`。超限输出卸载到 run 存储,换成内容感知预览 +
201
204
  HMAC 句柄,agent 真需要 payload 时用 `tm_fetch` 分页取。
202
205
 
203
206
  | 工具 | 功能 | 角色 |
204
207
  |---|---|---|
205
- | `tm_read` / `tm_grep` / `tm_bash` / `tm_fetch` | 受治理的文件读 / 正则搜索 / 只读 shell(白名单)/ 句柄分页 | 全部六个 agent |
206
- | `tm_memory` | 项目 + 全局记忆库(Markdown + frontmatter):add / search / list / forget | 全部六个 agent |
207
- | `tm_ptc_run` | 批量编排:一个程序、N 次受治理调用、零 LLM 回合 | 全部六个 agent |
208
- | `tm_search` | 多引擎网络搜索,返回提取去重后的命中列表 | Lead + Researcher |
208
+ | `tm_read` / `tm_grep` / `tm_bash` / `tm_fetch` | 受治理的文件读 / 正则搜索 / 只读 shell(白名单)/ 句柄分页(JSON 句柄支持 `fields` 点路径投影——刻意小的 jq 子集,如 `items[].name`) | 全部六个 agent |
209
+ | `tm_memory` | 会话 + 项目 + 全局三层记忆库(Markdown + frontmatter):add / search / list / forget / compact | 全部六个 agent |
210
+ | `tm_ptc_run` | 批量编排:一个程序、N 次受治理调用、零 LLM 回合;联网角色还能在程序里调 `tm.search` / `tm.webfetch` | 全部六个 agent |
211
+ | `tm_search` | 多引擎网络搜索,返回提取、去重、RRF 融合后的命中列表 | Lead + Researcher |
209
212
  | `tm_webfetch` | 白名单页面的单次受治理 GET(搜索页自动提取) | Lead + Researcher |
210
- | `tm_browser` | 交互式浏览器会话(CDP,**驱动你的默认浏览器**):open / navigate / read / screenshot / close | Lead + Researcher + Tester(仅 UI 验证) |
213
+ | `tm_browser` | 交互式浏览器会话(**驱动你的默认浏览器**):16 个 Playwright 动词(快照优先:`take_snapshot` → 按 uid 寻址的 `click`/`fill`/`drag`…)+ 5 个旧版兼容动词(open/navigate/read/screenshot/close);Playwright 引擎需 Node ≥ 20,不满足或导入失败时自动降级到旧版 CDP 引擎 | Lead + Researcher + Tester(仅 UI 验证) |
211
214
 
212
215
  > **固定工具优先级阶梯(每个任务都适用):① TeamMode 受治理工具(`tm_*`)
213
216
  > → ② 用户 MCP/插件工具 → ③ 模型自己的推理。** 它同时是回退链:某个受治理
@@ -221,23 +224,37 @@ HMAC 句柄,agent 真需要 payload 时用 `tm_fetch` 分页取。
221
224
  ### 上下文治理:卸载、句柄、预览
222
225
 
223
226
  大的工具输出是上下文成本的主要来源——每一步都要重发整个窗口。所以超过
224
- `TM_OFFLOAD_THRESHOLD` 的结果写进本地 run 存储(`<repo>/.git/opencode-team/`,
225
- 永不污染工作树),换成带内容感知预览的句柄:JSON 键 / CSV 表头+形状 /
226
- 日志 ERROR×N 统计 / 代码签名 / 二进制元数据,硬顶 80 token。agent 真需要
227
- payload 时用 `tm_fetch`(HMAC 签名、run 域、带过期)分页读。`tm_bash`
228
- 只放行只读命令(白名单),失败以结构化错误返回,不糊原始转储。
229
-
230
- ### 项目 + 全局记忆(tm_memory)
227
+ 内容分档阈值的(散文 → `TM_OFFLOAD_THRESHOLD_TEXT`,结构化数据 →
228
+ `TM_OFFLOAD_THRESHOLD_DATA`,未知类别 → 全局 `TM_OFFLOAD_THRESHOLD`)
229
+ 结果写进本地 run 存储(`<repo>/.git/opencode-team/`,永不污染工作树),
230
+ 换成带内容感知预览的句柄:JSON 键 / CSV 表头+形状 / 日志 ERROR×N 统计 /
231
+ 代码签名 / 二进制元数据,硬顶 80 token。agent 真需要 payload 时用
232
+ `tm_fetch`(HMAC 签名、run 域、带过期)分页读;JSON 句柄还可以直接要
233
+ `fields` 点路径投影(刻意小的 jq 子集:`items[].name`、
234
+ `[].stargazers_count`),大 API 转储只留需要的字段、原文从不进窗口。
235
+ `tm_bash` 只放行只读命令(白名单),失败以结构化错误返回,不糊原始转储。
236
+
237
+ ### 会话 + 项目 + 全局三层记忆(tm_memory)
231
238
 
232
239
  耐久的事实——构建命令、环境怪癖、架构决策、你的约定——以人可编辑的
233
- Markdown + frontmatter 存放:
234
-
235
- - **`project`**(默认):`<repo>/.git/opencode-team/memories/…` —— 每 checkout 一份,贴近 git。
236
- - **`global`**:`~/.opencode-team/memories/global/`(可用 `TM_MEMORY_GLOBAL_DIR` 覆盖)——**跟着你走遍所有项目**。
237
-
238
- 动作:`add` / `search`(确定性关键词打分)/ `list` / `forget`;每条记忆
239
- 上限 4000 字符。agent 被要求先搜记忆再做项目假设,也把来之不易的事实
240
- 存下来留给下个会话。
240
+ Markdown + frontmatter 存放,共三层:
241
+
242
+ - **`session`**:仅本次会话的瞬时事实——进程内、按 TTL 清扫(`TM_MEMORY_SESSION_TTL_MIN`,默认 240 分钟),对其他会话不可见;除非 `TM_MEMORY_SESSION_PERSIST=1` 才落盘到 `memories/sessions/<sid>/`。
243
+ - **`project`**(默认):`<repo>/.git/opencode-team/memories/…` —— 每 checkout 一份,贴近 git。存放本仓库的事实:构建命令、环境怪癖、架构决策。
244
+ - **`global`**:`~/.opencode-team/memories/global/`(可用 `TM_MEMORY_GLOBAL_DIR` 覆盖)——**跟着你走遍所有项目**。存放用户级约定:偏好的包管理器、提交风格、工具习惯。
245
+
246
+ 动作:`add` / `search`(确定性关键词打分)/ `list` / `forget` / `compact`;
247
+ 每条记忆上限 4000 字符。`search` 走全部三层,**会话 > 项目 > 全局**优先:
248
+ 项目条目获得 +2 的近似同分加权,同名上层条目遮蔽下层(永不浮出)。
249
+ 近重复从不堆积:`add` 命中同层同分类的既有条目(去重键,或
250
+ title+keywords 的 Jaccard ≥ 0.6)时**并入既有条目**——新内容胜出、
251
+ keywords 取并集、旧 slug 进 `supersedes:`、答复标注"已合并"(这是正常
252
+ 现象,别再换个变体标题重复添加)。某层到达 `TM_MEMORY_MAX_ENTRIES`
253
+ (每作用域 200)时 add 故意失败:先跑 `compact`——默认 dry-run 只报合并
254
+ 计划,带 `apply:true` 重跑才执行,执行前所有原件先复制进带时间戳的
255
+ `.compact-backup` 树(回滚路径)。超过 `TM_MEMORY_STALE_DAYS`(30 天)的
256
+ 条目在搜索结果里标 `[stale Nd]`。agent 被要求先搜记忆再做项目假设,
257
+ 也把来之不易的事实存下来留给下个会话。
241
258
 
242
259
  ### 🌐 真正好用的网络搜索(中国可用)
243
260
 
@@ -247,17 +264,20 @@ agent 永远看不到原始搜索页的噪音。
247
264
 
248
265
  | 引擎 | 说明 |
249
266
  |---|---|
250
- | `bing`(默认) | cn.bing.com;`bing-int` 强制国际版结果(`ensearch=1`) |
251
- | `sogou` / `so`(360) | 国产引擎,CJK 内容友好 |
252
- | `baidu` | 反爬最凶,但有时是唯一的中文特化索引;失败时会点名替代引擎 |
267
+ | `auto`(默认) | 给查询分类,**并行**扇出 2–3 个引擎,按 host+path 去重后做加权 RRF 融合(stackoverflow/bing 0.4,其余 0.2),产出标有来源引擎的 top-10 列表。路由:报错/camelCase API → `stackoverflow`+`github`+`bing`;开发生态(发布、框架、开源)→ `hn`+`github`+`npm`;中文/通用 → `bing`。用 `TM_SEARCH_DEFAULT_ENGINE` 钉别的默认 |
268
+ | `bing` | cn.bing.com——唯一活着的中文 HTML SERP;多词 CJK 查询自动保护短语边界(加引号),markup 洗牌拆不散结果列表 |
269
+ | `stackoverflow` | api.stackexchange.com 问题搜索(免 Key,300 次/天/IP)→ 带复合摘要的编号问题列表;`auto` 跟踪配额,耗尽自动换 `bing` 顶上 |
270
+ | `hn` | Hacker News(Algolia API,免 Key)→ 帖子标题 + 摘要与原文链接 |
253
271
  | `bilibili` | 视频搜索 |
254
272
  | `moegirl` | MediaWiki 搜索 API——词条标题 + 摘要,结构化 |
255
273
  | `npm` | registry 搜索 → name@version + 描述,结构化 |
256
- | `github` | 仓库搜索 API → star 数 + 描述,结构化 |
274
+ | `github` | 仓库搜索 API → star 数 + 描述,结构化;`org:` / `user:` / `stars:` / `language:` 限定符透传折进查询(如 `vector db stars:>500 language:rust`) |
257
275
 
258
- 九个引擎在中国大陆**全部免 Key 可达**,且全部在种子域名白名单内。空结果
259
- (反爬拦截页)时错误信息会点名替代引擎,不让 agent 卡死。另两条通道补全
260
- 能力面:
276
+ 七个引擎在中国大陆**全部免 Key 可达**,且全部在种子域名白名单内。旧的
277
+ 中文 HTML SERP(`sogou` / `so` / `baidu` / `bing-int`)已被**移除**——
278
+ 实测定标(2026-09-14)显示它们只返回反爬壳或 100% 空结果,连手动选择
279
+ 都不再提供。空结果时错误信息会点名替代引擎,不让 agent 卡死。另两条
280
+ 通道补全能力面:
261
281
 
262
282
  - `tm_webfetch` —— 已知 URL,单次受治理 GET。它抓到的搜索引擎页面同样
263
283
  自动提取为命中列表。`registry.npmjs.org/<pkg>/latest` 这类 JSON 端点
@@ -265,9 +285,23 @@ agent 永远看不到原始搜索页的噪音。
265
285
  - `tm_browser` —— JS 渲染页:**驱动你的默认浏览器**(Windows 读注册表 /
266
286
  Linux 读 xdg-settings;仅限 Chromium 系——默认是 Firefox 时回退到
267
287
  Edge/Chrome 探测顺序,因为 CDP 是 Chromium 专有协议;`TM_BROWSER_PATH`
268
- 可强制指定),CDP pipe headful 运行,隔离临时 profile,**域名白名单在
269
- 网络层逐请求强制**(`Fetch.requestPaused` → 非白名单主机直接
270
- `BlockedByClient`)。
288
+ 可强制指定),默认有头运行,隔离临时 profile,**域名白名单在网络层
289
+ 逐请求、逐重定向跳强制**。动作面与 chrome-devtools-mcp 对齐:**16 个
290
+ Playwright 动词**(`navigate_page` · `take_snapshot` · `click` · `fill` ·
291
+ `hover` · `drag` · `press_key` · `select_page` · `upload_file` · `wait_for`
292
+ · `evaluate_script` · `list_console_messages` · `list_network_requests` ·
293
+ `list_pages` · `take_screenshot` · `handle_dialog`)+ 5 个旧版兼容动词
294
+ (`open` / `navigate` / `read` / `screenshot` / `close`)。快照优先:
295
+ `take_snapshot` 返回注入了 `[uid=eN]` 标记的 aria 快照,后续动作按 uid
296
+ 寻址节点而不是猜定位器;快照受
297
+ `TM_BROWSER_SNAPSHOT_MAX_TOKENS`(默认 1200)硬顶。
298
+ **引擎分工:** 主引擎是 `playwright-core`(optionalDependencies——
299
+ 需 **Node ≥ 20**;旧版 Node 或导入失败时,整个插件实例自动降级到零依赖
300
+ `cdp-legacy` 引擎,只保留核心动词;可用 `TM_BROWSER_ENGINE=playwright|cdp-legacy`
301
+ 钉死)。从不下载浏览器——Playwright 按路径启动**你自己装的**浏览器,
302
+ `npx playwright install` 不属于用户流程(依赖本体在 npm
303
+ install/发布时解析)。默认隔离临时 profile:登录态想跨会话保留,只有
304
+ 显式设置 `TM_BROWSER_USER_DATA_DIR` 这一条路。
271
305
 
272
306
  **伪装浏览器请求头后仍收到 403** 时,错误信息是一条指令:该站点的门槛是
273
307
  JS 挑战 / TLS 指纹级别,只有真实浏览器能过——会直接让 agent 调 `tm_browser`
@@ -276,22 +310,24 @@ JS 挑战 / TLS 指纹级别,只有真实浏览器能过——会直接让 age
276
310
  (`maimai.cn` 脉脉 ≠ maimai DX 游戏)不会混入命中列表——用
277
311
  `TM_HIT_BLACKLIST` 可扩展命中黑名单。
278
312
 
279
- 种子白名单(两个工具共用,21 个主机;baidu/moegirl/bilibili 用的是父域,
313
+ 种子白名单(三个联网工具共用,23 个主机;baidu/moegirl/bilibili 用的是父域,
280
314
  所有兄弟子域——baike.baidu.com、mzh.moegirl.org.cn、space.bilibili.com——
281
315
  一并覆盖):`baidu.com`、`moegirl.org.cn`、`bilibili.com`、`www.sogou.com`、
282
316
  `www.so.com`、`cn.bing.com`、`www.bing.com`、`zhihu.com`、`juejin.cn`、
283
317
  `csdn.net`、`cnblogs.com`、`gitee.com`、`github.com`、`api.github.com`、
284
318
  `raw.githubusercontent.com`、`gist.githubusercontent.com`、`ghproxy.net`
285
- (github raw 的大陆镜像)、`stackoverflow.com`、`npmjs.org`、`pypi.org`、
319
+ (github raw 的大陆镜像)、`stackoverflow.com`、`api.stackexchange.com`
320
+ + `hn.algolia.com`(两个 JSON 搜索引擎)、`npmjs.org`、`pypi.org`、
286
321
  `learn.microsoft.com`——用
287
- `TM_WEBFETCH_ALLOWED_DOMAINS` 扩展(`"*"` 放开全部主机)。architect /
322
+ `TM_WEBFETCH_ALLOWED_DOMAINS` 扩展(`"*"` 放开全部主机;自定义列表是
323
+ **替换**种子,保留引擎主机否则 `tm_search` 没了目标)。architect /
288
324
  implementer / reviewer **没有**联网授权——网络问题会报告为缺口,绝不编造。
289
325
  tester 仅持有 `tm_browser`,用于本项目的治理化 UI 验证(本地开发服务器、
290
326
  预览路由);开放网络抓取仍归两个联网角色。
291
327
 
292
328
  **白名单外是门,不是墙。** 当抓取 / 搜索 / 浏览器打开的目标主机不在白名单
293
329
  内时,工具会把 URL 交给 OpenCode 的**官方确认弹窗**——由你裁决,每次一个
294
- 目标(无人应答照常走 10 分钟自动拒绝,插件依旧绝不代你批准)。每个弹窗
330
+ 目标(无人应答照常走 1 分钟自动拒绝,插件依旧绝不代你批准)。每个弹窗
295
331
  同时触发一条**系统 toast 通知**,即使你没盯着屏幕也知道有待批准的操作。
296
332
  env 文件 URL 和非 http(s) 协议保持硬拦截、无弹窗——R6 红线不可被"同意"。
297
333
 
@@ -300,10 +336,17 @@ env 文件 URL 和非 http(s) 协议保持硬拦截、无弹窗——R6 红线
300
336
  **R6 环境变量保护。** TeamMode 激活时,模型不能悄悄读环境变量。env 读取
301
337
  (`printenv`、`env`、`Get-ChildItem env:` 等)和 env 文件(`.env`、shell rc)
302
338
  走 OpenCode **官方确认弹窗**;没人应答的弹窗在 `TM_ASK_TIMEOUT_MIN`(默认
303
- 10 分钟)后**自动拒绝**。通配符表达不了的 env 读取(命令内嵌 `$VAR` /
339
+ 1 分钟)后**自动拒绝**。通配符表达不了的 env 读取(命令内嵌 `$VAR` /
304
340
  `${VAR}` / `$env:`、命令替换)和 `tm_*` 包装通道保持**硬拦截**——没有弹窗
305
341
  可钻。审计日志只记录工具名 + 模式类别 + 裁决——绝不记录命令文本、路径、
306
- 变量名或值。
342
+ 变量名或值。裁决现在还解释"弹窗已无法应答"的情形:应答打到**已关闭**的
343
+ 弹窗(宿主 404——计时器早已自动拒绝)记为 `already-closed`、不触发降级;
344
+ 插件侧应答形状错误记为 `rejected-shape-bug`;用户在自动拒绝**之后**才到的
345
+ 回复记为 `late-<verdict>`(仅可观测——拒绝已成事实,插件依旧绝不代你批准)。
346
+ 自动拒绝的地板为 1 分钟,可用 `TM_ASK_TIMEOUT_FLOOR_MIN` 调节。短默认值是安全
347
+ 的:抢在计时器之后的应答会良性记为 `already-closed`(宿主对已关闭弹窗返回
348
+ 404——不触发门禁降级),实测约 120 秒的滞后是宿主→插件事件总线的**投递**
349
+ 延迟,并非点击解析延迟。
307
350
 
308
351
  **R2 危险操作(同一个弹窗)。** 删除、git 发布、网络抓取、包安装/发布、
309
352
  进程/系统、提权——统统不允许静默放行。日常验证栈(`npm test`、`tsc`、
@@ -363,24 +406,36 @@ Team Lead 自己从不删黑板,你可以随时审计任何一次运行。
363
406
  | 环境变量 | 默认 | 用途 |
364
407
  |---|---|---|
365
408
  | `TM_ENV_PROTECT` | `strict` | R6 模式:`strict` / `standard` / `off`(off 同时解除审批计时器) |
366
- | `TM_ASK_TIMEOUT_MIN` | `10` | 无人应答弹窗自动拒绝前等待的分钟数(硬下限 3——宿主的应答事件到插件晚约 120 秒) |
409
+ | `TM_ASK_TIMEOUT_MIN` | `1` | 无人应答弹窗自动拒绝前等待的分钟数(地板 1 分钟——安全:抢跑应答良性记为 `already-closed`;宿主应答事件到插件晚约 120 秒是事件总线投递延迟,非点击解析延迟) |
410
+ | `TM_ASK_TIMEOUT_FLOOR_MIN` | `1` | 上述自动拒绝的强制最小时长 |
367
411
  | `TM_ENV_PROTECT_EXTRA_DENY` | — | 额外拦截模式(正则;永远硬拦截,不走弹窗) |
368
- | `TM_OFFLOAD_THRESHOLD` | `2000` | 卸载阈值(token,CJK 感知估算) |
412
+ | `TM_OFFLOAD_THRESHOLD` | `2000` | 全局卸载兜底阈值(token,CJK 感知估算)——内容类别未知时使用 |
413
+ | `TM_OFFLOAD_THRESHOLD_TEXT` | `4000` | 散文类(text / log / markdown)卸载阈值 |
414
+ | `TM_OFFLOAD_THRESHOLD_DATA` | `2000` | 结构化载荷(json / csv / code / binary)卸载阈值 |
369
415
  | `TM_PREVIEW_MAX_TOKENS` | `80` | 预览硬顶 |
370
416
  | `TM_FETCH_MAX_LINES` | `2000` | tm_fetch 单页行数上限 |
371
417
  | `TM_BLACKBOARD_DIR` / `TM_TRAJECTORY_DIR` | `<repo>/.git/opencode-team/…` | 卸载存储 / 轨迹账本(tmpdir 回退;显式值 = 绝对或项目相对) |
372
418
  | `TM_BLACKBOARD_TTL` | `7` | 存储保留天数 |
373
419
  | `TM_BASH_READONLY_ALLOWED` | 内置表 | tm_bash 白名单 |
374
- | `TM_WEBFETCH_ALLOWED_DOMAINS` | 21 个种子主机 | tm_webfetch / tm_search / tm_browser 白名单(`"*"` 全开;空 = 全拒) |
420
+ | `TM_SEARCH_DEFAULT_ENGINE` | `auto` | tm_search 未显式给 `engine` 时的默认引擎(`auto` = 分类 + 并行扇出 + RRF 融合;也可钉表中任一引擎) |
421
+ | `TM_WEBFETCH_ALLOWED_DOMAINS` | 23 个种子主机 | tm_webfetch / tm_search / tm_browser 白名单(`"*"` 全开;空 = 全拒;自定义值**替换**种子——保留引擎主机) |
375
422
  | `TM_BROWSER_PATH` | 自动探测 | tm_browser 可执行文件覆盖(默认用你的默认浏览器——Chromium 系时;否则回退 Edge/Chrome 探测) |
376
423
  | `TM_BROWSER_HEADLESS` | `auto` | `1` 无头(CI)/ `0` 有头 / `auto`(仅无显示的 Linux 用无头) |
377
- | `TM_MEMORY_GLOBAL_DIR` | `~/.opencode-team/memories/global/` | tm_memory GLOBAL 作用域存储 |
424
+ | `TM_BROWSER_ENGINE` | `playwright` | `playwright`(需 Node ≥ 20;导入失败自动降级)/ `cdp-legacy`(零依赖 CDP pipe,仅核心动词) |
425
+ | `TM_BROWSER_SNAPSHOT_MAX_TOKENS` | `1200` | `take_snapshot` 载荷硬顶 |
426
+ | `TM_BROWSER_USER_DATA_DIR` | —(隔离临时 profile) | 显式持久 profile 目录——登录态跨会话保留的唯一途径 |
427
+ | `TM_MEMORY_GLOBAL_DIR` | `~/.opencode-team/memories/global/` | tm_memory GLOBAL 层存储 |
428
+ | `TM_MEMORY_SESSION_TTL_MIN` | `240` | session 层条目 TTL(惰性 + 启动清扫) |
429
+ | `TM_MEMORY_MAX_ENTRIES` | `200` | 每作用域条目上限;超限 add 故意失败——先跑 `compact` |
430
+ | `TM_MEMORY_STALE_DAYS` | `30` | 超过此天数的条目在搜索结果标 `[stale Nd]`(`0` 关闭) |
431
+ | `TM_MEMORY_SESSION_PERSIST` | —(瞬态) | `1` 时 session 条目同时落盘 `memories/sessions/<sid>/` |
378
432
  | `TM_HIT_BLACKLIST` | `maimai.cn` | 永不进入搜索命中列表的额外域名(逗号/分号分隔;过滤同名不同站噪音) |
379
433
  | `TM_PTC_MAX_PROGRAM_CHARS` | `4000` | PTC 程序源码上限 |
380
434
  | `TM_PTC_MAX_CALLS` | `20` | PTC 单次运行桥接调用预算(1–200) |
381
435
  | `TM_PTC_MAX_ERRORS` | `3` | PTC 单次运行错误预算(1–50) |
382
436
  | `TM_PTC_TIMEOUT_MS` | `60000` | PTC 单次运行墙钟超时(5s–10min) |
383
437
  | `TM_PTC_ENGINE` | `auto` | `auto`(worker→inline 降级)/ `worker` / `inline` |
438
+ | `TM_PTC_WEB_BRIDGE` | `on` | 向 PTC 程序暴露 `tm.search` / `tm.webfetch`(`off` 从桥接集移除) |
384
439
 
385
440
  ---
386
441
 
@@ -398,6 +453,9 @@ Team Lead 自己从不删黑板,你可以随时审计任何一次运行。
398
453
  路由低于 3 次派工就是路由 bug。
399
454
  - **审批门禁(计数式):** ≥2 次派工 → 计划(≤30 行)→ **等你批准** → 执行。
400
455
  阻塞性问题立刻批量问,不猜、不挤牙膏。
456
+ - **并发派工:** 互相独立的派工批进**同一轮**并行执行(多 implementer 各带
457
+ 文件所有权 + 原文数据契约、三维度评审、分包 tester 同时跑);派工权 Team
458
+ Lead 独有——专家角色已收回 `task`,子代理不再生子代理。
401
459
  - **自适应评审:** 默认一名评审;高风险画像(鉴权/安全面、跨模块契约、
402
460
  公共 API)才三维度并行。
403
461
  - **静态验证:** 构建 / 类型检查 / lint / 测试。禁止临时起意的浏览器自动化;
@@ -470,7 +528,7 @@ OpenCode 按 spec 字符串缓存插件,从不重新解析 `@latest`(上游
470
528
  宿主无关。无显示的 Linux 上 `tm_browser` 自动转无头。
471
529
 
472
530
  **确认弹窗我不理会会怎样?**
473
- `TM_ASK_TIMEOUT_MIN`(默认 10 分钟)后自动拒绝。插件从不自我批准——它
531
+ `TM_ASK_TIMEOUT_MIN`(默认 1 分钟)后自动拒绝。插件从不自我批准——它
474
532
  能站的队只有"你"或者"没人"。
475
533
 
476
534
  ---
@@ -1 +1 @@
1
- {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAmB,MAAM,YAAY,CAAA;AA+Q9D,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAO9C,CAAA"}
1
+ {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAmB,MAAM,YAAY,CAAA;AAqR9D,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAO9C,CAAA"}