@te-river/opencode-team-mode 1.5.0 → 1.5.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.
@@ -0,0 +1,467 @@
1
+ /**
2
+ * R6 — code-level environment-variable read protection for Team mode.
3
+ *
4
+ * HUMAN-approved feature: in Team mode, EVERY path the model can use to
5
+ * read environment variables / env files is intercepted in code, before
6
+ * the tool runs, independent of prompt compliance. This hook throws inside
7
+ * `tool.execute.before`, which opencode (verified live on 1.18.29) turns
8
+ * into a failed tool call whose error text is returned to the model — so
9
+ * the block is hard, not advisory.
10
+ *
11
+ * Surfaces covered (bash + PowerShell dialects, matched against the bash
12
+ * tool's `args.command`, and against path-class args of the read / grep /
13
+ * glob / list tools):
14
+ * 1. explicit env commands: `env` — a dump whenever no outside command
15
+ * word follows: flags/assignments only (`env -0`, `env -u HOME`,
16
+ * `env FOO=bar`) or a command word that is itself `env`/`printenv`
17
+ * (`env printenv`); a real launcher (`env node app.js`, `env -i node
18
+ * app.js`) passes — `printenv`, bare `set` (sh context only —
19
+ * PowerShell `Set-*` cmdlets are never matched), `declare -p` and
20
+ * its dump synonyms `typeset -p` / `export -p` / `local -p` (flags
21
+ * may be split: `declare -x -p FOO`), and the PowerShell `env:`
22
+ * drive via Get-ChildItem / gci / dir / ls / Get-Item / gi /
23
+ * Get-Content / gc / cat / type (flags and optional quotes allowed
24
+ * between cmdlet and drive: `Get-Content 'env:HOME'`). The same
25
+ * statement heads are caught behind statement noise: subshells and
26
+ * groups (`(env)`, `{ env; }`), escaped commands (`\env`), the `time`
27
+ * keyword prefix, and command substitution (`$(printenv)`, backticks).
28
+ * 2. `$env:` / `${env:...}` expansion (PowerShell);
29
+ * 3. strict mode only: `${VAR}` and `$ALLCAPS_VAR` expansion;
30
+ * 4. env-file paths: `.env`, `.env.*`, `*.env`, `.bashrc`,
31
+ * `.bash_profile`, `.profile`, `.zshrc`, `.zprofile`, `.zshenv`
32
+ * (checked-in template copies like `.env.example` are exempt, and
33
+ * code identifiers like `process.env` are not files).
34
+ *
35
+ * TeamMode's own tm_* tools (tm_read / tm_grep / tm_bash) are aliased onto
36
+ * read/grep/bash in `inspectToolCall`, so this interception covers them
37
+ * identically — the plugin tools can never serve as an R6 bypass.
38
+ *
39
+ * Known limitation (accepted, documented): interpreter execution paths —
40
+ * `bash -c "env"`, `node -e 'process.env'`, `python -c "import os; ..."` —
41
+ * are NOT intercepted; the pattern surface stops at the command line
42
+ * itself. One decode pass covers URL-encoded names (`/%2Eenv`); a double
43
+ * encoding (`/%252Eenv`) and a quoted dump head behind `env`
44
+ * (`env 'printenv'`) stay ahead of the matcher by design.
45
+ *
46
+ * Configuration (read by the PLUGIN at startup — this is program-level
47
+ * configuration of the tool itself, the same category as the ttlDays
48
+ * option below; it is NOT what R6 defends against, which is the MODEL
49
+ * reading environment variables through tool calls at runtime):
50
+ * - `TM_ENV_PROTECT` = "strict" (default) | "standard" | "off";
51
+ * unknown values fail closed into strict.
52
+ * - `TM_ENV_PROTECT_EXTRA_DENY` = semicolon-separated user regexes,
53
+ * applied to every scanned string in any non-off mode.
54
+ *
55
+ * Audit red line (HUMAN privacy constraint): every interception is logged
56
+ * via `client.app.log()` (level "warn", service "team-mode-env-protect")
57
+ * recording ONLY the tool name and the pattern category. Command text,
58
+ * paths, variable names and values are NEVER logged — the audit trail must
59
+ * answer "what class of read was blocked", and must not become a place
60
+ * where secrets accumulate.
61
+ */
62
+ /** Audit log service identifier (also the query key for "what was blocked"). */
63
+ export const ENV_PROTECT_SERVICE = "team-mode-env-protect";
64
+ /**
65
+ * Fixed structured message returned to the model on every block. The
66
+ * category is appended as a machine-readable suffix; tests pin both.
67
+ */
68
+ export const ENV_PROTECT_MESSAGE = "TeamMode R6 env protection: 环境变量读取已被拦截。需要变量值请向 HUMAN 申请。";
69
+ /** Pattern categories surfaced in the block message and the audit log. */
70
+ export const CATEGORY_BASH_ENV_COMMAND = "bash-env-command";
71
+ export const CATEGORY_BASH_ENV_EXPANSION = "bash-env-expansion";
72
+ export const CATEGORY_ENV_FILE_PATH = "env-file-path";
73
+ export const CATEGORY_EXTRA_DENY = "extra-deny";
74
+ /**
75
+ * Resolve the protection mode. Only exact "standard" / "off" switch those
76
+ * modes on; everything else (unset, typos, wrong case) fails CLOSED into
77
+ * strict — a mistyped opt-out must never disable the red line.
78
+ */
79
+ export function resolveEnvProtectMode(raw) {
80
+ if (raw === "standard")
81
+ return "standard";
82
+ if (raw === "off")
83
+ return "off";
84
+ return "strict";
85
+ }
86
+ /**
87
+ * Parse `TM_ENV_PROTECT_EXTRA_DENY` (semicolon-separated regex bodies).
88
+ * Invalid regexes are skipped rather than crashing plugin startup; the
89
+ * remaining rules still apply. Empty/absent input yields no extra rules.
90
+ */
91
+ export function parseExtraDeny(raw) {
92
+ if (typeof raw !== "string")
93
+ return [];
94
+ const rules = [];
95
+ for (const part of raw.split(";")) {
96
+ const body = part.trim();
97
+ if (!body)
98
+ continue;
99
+ try {
100
+ rules.push(new RegExp(body));
101
+ }
102
+ catch {
103
+ /* user regex failed to compile — skip it, keep the rest */
104
+ }
105
+ }
106
+ return rules;
107
+ }
108
+ /** The block error handed back to the model (fixed text + category tag). */
109
+ export function envProtectError(category) {
110
+ return new Error(`${ENV_PROTECT_MESSAGE} [category=${category}]`);
111
+ }
112
+ // ---------- env-file path matching ----------
113
+ /** Shell/rc basenames whose content is effectively an env dump. */
114
+ const ENV_FILE_BASENAMES = new Set([
115
+ ".bashrc",
116
+ ".bash_profile",
117
+ ".profile",
118
+ ".zshrc",
119
+ ".zprofile",
120
+ ".zshenv",
121
+ ]);
122
+ /** Checked-in template suffixes — documentation copies, not real secrets. */
123
+ const ENV_TEMPLATE_SUFFIX = /\.(?:example|sample|template|dist)$/;
124
+ /**
125
+ * Code identifiers that merely end in ".env" and name an object, not a
126
+ * file (`process.env`, `import.meta.env`, ...). The escaped form is
127
+ * listed too because a grep pattern like `process\.env` survives
128
+ * tokenization with its backslash.
129
+ */
130
+ const ENV_IDENTIFIER = /^(?:process|deno|bun|os|import\.meta)\.env$/i;
131
+ /**
132
+ * True when the value points at an env file. Matches on the BASENAME with
133
+ * both separators handled; URL query/fragment tails are cut first so
134
+ * `https://x/.env?raw=1` matches the same basename as a plain path, and
135
+ * asterisks are stripped so glob patterns like "*.env" or a recursive deep
136
+ * form of ".env.local" are caught by the same matcher as real paths.
137
+ * (Note: glob spellings are written without backticks here — a double-star
138
+ * + slash inside JSDoc would close the comment block early.)
139
+ */
140
+ export function isEnvFilePath(raw) {
141
+ const value = String(raw ?? "")
142
+ // one percent-decode pass FIRST, so URL-encoded names like
143
+ // `https://x/%2Eenv` land on the same basename as the plain path
144
+ // (single pass only: `%252E` decodes to `%2E` and stays encoded)
145
+ .replace(/%([0-9A-Fa-f]{2})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)))
146
+ .replace(/[?#].*$/, "")
147
+ .replace(/\*/g, "")
148
+ .trim();
149
+ if (!value)
150
+ return false;
151
+ // A code identifier like `process.env` is not a file path, however it is
152
+ // segmented. The unescaped form matters because a grep pattern spelled
153
+ // `process\.env` would otherwise split at the backslash into path +
154
+ // basename `.env` and slip past the identifier check below.
155
+ if (ENV_IDENTIFIER.test(value) || ENV_IDENTIFIER.test(value.replace(/\\/g, ""))) {
156
+ return false;
157
+ }
158
+ const segments = value.split(/[\\/]/);
159
+ const base = (segments[segments.length - 1] || "").toLowerCase();
160
+ if (!base)
161
+ return false;
162
+ if (base === ".env")
163
+ return true;
164
+ if (base.startsWith(".env.")) {
165
+ // `.env.example` and friends are safe checked-in documentation
166
+ if (ENV_TEMPLATE_SUFFIX.test(base))
167
+ return false;
168
+ return true;
169
+ }
170
+ if (base.endsWith(".env")) {
171
+ if (ENV_IDENTIFIER.test(base) || ENV_IDENTIFIER.test(base.replace(/\\/g, ""))) {
172
+ return false;
173
+ }
174
+ return true;
175
+ }
176
+ return ENV_FILE_BASENAMES.has(base);
177
+ }
178
+ // ---------- bash command classification ----------
179
+ /**
180
+ * Split a command into shell segments so compound statements like
181
+ * `cd /x && printenv` are checked at every statement boundary.
182
+ */
183
+ function splitSegments(command) {
184
+ return command.split(/&&|\|\||[;\n|&]/);
185
+ }
186
+ /**
187
+ * First token + remainder of one segment. Statement noise is peeled off
188
+ * until stable so the real statement head surfaces: subshell/group
189
+ * openers and closers (`(env)`, `{ env; }`), escaped commands (`\env`),
190
+ * and the `time` keyword prefix (`time (env)` needs two passes). Leading
191
+ * `VAR=value` assignment prefixes are skipped so `FOO=1 env` is still
192
+ * recognized as an env dump.
193
+ */
194
+ function commandToken(segment) {
195
+ let s = segment.trim();
196
+ for (;;) {
197
+ for (;;) {
198
+ const next = s
199
+ .replace(/^[({\\]+/, "")
200
+ .replace(/[)}]+$/, "")
201
+ .replace(/^time\s+/, "")
202
+ .trim();
203
+ if (next === s)
204
+ break;
205
+ s = next;
206
+ }
207
+ const sp = s.search(/\s/);
208
+ const token = sp === -1 ? s : s.slice(0, sp);
209
+ const isAssignment = /^[A-Za-z_][A-Za-z0-9_]*=/.test(token);
210
+ if (!isAssignment)
211
+ return { token, rest: sp === -1 ? "" : s.slice(sp).trim() };
212
+ s = sp === -1 ? "" : s.slice(sp).trim();
213
+ if (!s)
214
+ return { token: "", rest: "" };
215
+ }
216
+ }
217
+ /**
218
+ * env options whose value is a SEPARATE token (GNU env: `-u NAME`,
219
+ * `-S STRING`, `-C DIR`, long spellings included) — without this table the
220
+ * value token would be mistaken for the launched command word and a dump
221
+ * like `env -u HOME` would pass as a launcher.
222
+ */
223
+ const ENV_VALUE_OPTS = new Set(["u", "s", "c", "--unset", "--split-string", "--chdir"]);
224
+ /**
225
+ * Classify the argument tail of an `env` invocation. True (dump) when no
226
+ * outside command word remains: a bare `env`, flags/assignments only
227
+ * (`env -0`, `env -i`, `env -u HOME`, `env FOO=bar`), or when the command
228
+ * word is itself `env`/`printenv` (`env printenv`, `env -i env`) — the
229
+ * single-token bypass of the bare-dump rule. A real launcher — the first
230
+ * non-flag, non-assignment token names any other program (`env node
231
+ * app.js`, `env -i node app.js`) — returns false and stays allowed.
232
+ */
233
+ function envRestIsDump(rest) {
234
+ const tokens = rest.split(/\s+/).filter(Boolean);
235
+ for (let i = 0; i < tokens.length; i++) {
236
+ const token = tokens[i];
237
+ if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(token))
238
+ continue;
239
+ if (token.startsWith("-")) {
240
+ const shortTakesValue = /^-[a-z0-9]+$/i.test(token) &&
241
+ [...token.slice(1)].some((ch) => ENV_VALUE_OPTS.has(ch.toLowerCase()));
242
+ if ((shortTakesValue || ENV_VALUE_OPTS.has(token.toLowerCase())) && i + 1 < tokens.length) {
243
+ i++;
244
+ }
245
+ continue;
246
+ }
247
+ const cmd = token.toLowerCase();
248
+ return cmd === "env" || cmd === "printenv";
249
+ }
250
+ return true;
251
+ }
252
+ /**
253
+ * Statement-head env dumps. Case matters for `set`: the exact lowercase
254
+ * bare `set` (no arguments) is the sh builtin that prints every variable,
255
+ * while PowerShell `Set-*` cmdlets and `set -euo pipefail` style flags are
256
+ * never variable dumps and must pass. `env`/`printenv` are matched
257
+ * case-insensitively. A pathed `/usr/bin/env` is a program launcher, not
258
+ * a dump, and stays allowed. `env` dumps when no outside command word
259
+ * follows (see envRestIsDump); `env <command>` is a launcher and passes.
260
+ * `declare -p` prints variable definitions in reusable form, and so do its
261
+ * synonyms `typeset -p` / `export -p` / `local -p` — with the flag allowed
262
+ * to be split from `-p` by one other option (`declare -x -p FOO`).
263
+ */
264
+ function isEnvDumpStatement(token, rest) {
265
+ if (!token)
266
+ return false;
267
+ const lower = token.toLowerCase();
268
+ if (lower === "env" && !token.includes("/")) {
269
+ return envRestIsDump(rest);
270
+ }
271
+ if (lower === "printenv")
272
+ return true;
273
+ if (lower === "declare" || lower === "typeset" || lower === "export" || lower === "local") {
274
+ return /^\s*(?:-[a-z]+\s+)?-[a-z]*p[a-z]*\b/.test(` ${rest}`);
275
+ }
276
+ if (token === "set")
277
+ return rest === "";
278
+ return false;
279
+ }
280
+ /** PowerShell `env:` drive access without the `$` prefix. Optional
281
+ * parameter flags and optional quotes between the cmdlet and the drive
282
+ * (`Get-Content -Path env:HOME`, `Get-Content 'env:HOME'`, `ls env:`) are
283
+ * part of the same read. */
284
+ const PS_ENV_DRIVE = /(?:^|[\s;&|("'`])(?:get-childitem|gci|dir|ls|get-item|gi|get-content|gc|cat|type)\s+(?:-[A-Za-z]+\s+)*['"]?env:/i;
285
+ /** `$env:` / `${env:...}` expansion (PowerShell), case-insensitive like
286
+ * the drive itself. */
287
+ const PS_ENV_EXPANSION = /\$\{?env:/i;
288
+ /**
289
+ * Command substitutions that run a command line of their own. Captured
290
+ * content excludes parens, so there is no nesting and the recursive
291
+ * classification in classifyBashCommand cannot loop.
292
+ */
293
+ const COMMAND_SUBSTITUTION = /\$\(([^()]*)\)|`([^`]*)`/g;
294
+ /** strict-mode brace expansion, e.g. `${HOME}`. */
295
+ const BRACE_EXPANSION = /\$\{[^}]*\}/;
296
+ /** strict-mode bare all-caps variable, e.g. `$API_KEY` (also `$F`, `$_`).
297
+ * The lookahead pins the END of the identifier so mixed-case names like
298
+ * `$Path` never match on their capitalized prefix alone. */
299
+ const ALLCAPS_EXPANSION = /\$[A-Z_][A-Z0-9_]*(?![A-Za-z0-9_])/;
300
+ /**
301
+ * Tokenize a bash command and report whether any token names an env file —
302
+ * catches `cat .env`, `head -50 .env.local`, `curl https://x/.env`,
303
+ * `X=.env ./run` (the token survives because `=`, `.`, `/`, `:` stay inside
304
+ * tokens). Quote/glob leftovers are stripped per token.
305
+ */
306
+ function bashEnvFileHit(command) {
307
+ const tokens = command.split(/[\s'"`|;&<>()]+/);
308
+ for (const rawToken of tokens) {
309
+ const token = rawToken.replace(/^[*'"`]+|[*'"`]+$/g, "");
310
+ if (token && isEnvFilePath(token))
311
+ return true;
312
+ }
313
+ return false;
314
+ }
315
+ /**
316
+ * Classify a bash/PowerShell command string. Returns the pattern category
317
+ * when the command must be blocked, null when it may pass. `off` is
318
+ * handled by the caller; user extra-deny rules take precedence.
319
+ */
320
+ export function classifyBashCommand(command, mode, extra = []) {
321
+ const text = String(command ?? "");
322
+ if (!text)
323
+ return null;
324
+ for (const rule of extra) {
325
+ if (rule.test(text))
326
+ return CATEGORY_EXTRA_DENY;
327
+ }
328
+ for (const segment of splitSegments(text)) {
329
+ const { token, rest } = commandToken(segment);
330
+ if (isEnvDumpStatement(token, rest))
331
+ return CATEGORY_BASH_ENV_COMMAND;
332
+ }
333
+ // command substitution runs its own command line: classify the inner
334
+ // text recursively (`$(printenv) PATH`, backtick `cat .env`)
335
+ for (const match of text.matchAll(COMMAND_SUBSTITUTION)) {
336
+ const inner = match[1] ?? match[2];
337
+ if (!inner)
338
+ continue;
339
+ const innerCategory = classifyBashCommand(inner, mode, extra);
340
+ if (innerCategory)
341
+ return innerCategory;
342
+ }
343
+ if (PS_ENV_DRIVE.test(text))
344
+ return CATEGORY_BASH_ENV_COMMAND;
345
+ if (PS_ENV_EXPANSION.test(text))
346
+ return CATEGORY_BASH_ENV_EXPANSION;
347
+ if (mode === "strict") {
348
+ if (BRACE_EXPANSION.test(text))
349
+ return CATEGORY_BASH_ENV_EXPANSION;
350
+ if (ALLCAPS_EXPANSION.test(text))
351
+ return CATEGORY_BASH_ENV_EXPANSION;
352
+ }
353
+ if (bashEnvFileHit(text))
354
+ return CATEGORY_ENV_FILE_PATH;
355
+ return null;
356
+ }
357
+ // ---------- file-tool path classification ----------
358
+ /**
359
+ * Path-class argument keys scanned defensively across read / grep / glob /
360
+ * list (read uses `filePath`; the others historically use `path`, and
361
+ * `pattern` / `include` select files for glob / grep). Keys outside this
362
+ * set (descriptions, notes) are never scanned.
363
+ */
364
+ const PATH_LIKE_KEYS = /^(filepath|path|file|dir|directory|pattern|include)$/i;
365
+ /**
366
+ * Classify the path-class arguments of a file tool. Returns the pattern
367
+ * category when the call must be blocked, null when it may pass.
368
+ */
369
+ export function classifyPathFields(args, mode, extra = []) {
370
+ if (!args || typeof args !== "object")
371
+ return null;
372
+ for (const [key, value] of Object.entries(args)) {
373
+ if (typeof value !== "string" || !value)
374
+ continue;
375
+ if (!PATH_LIKE_KEYS.test(key))
376
+ continue;
377
+ for (const rule of extra) {
378
+ if (rule.test(value))
379
+ return CATEGORY_EXTRA_DENY;
380
+ }
381
+ if (isEnvFilePath(value))
382
+ return CATEGORY_ENV_FILE_PATH;
383
+ }
384
+ return null;
385
+ }
386
+ // ---------- tool-call level routing ----------
387
+ /** File tools whose path-class arguments are scanned for env files. */
388
+ const PATH_SCAN_TOOLS = new Set(["read", "grep", "glob", "list"]);
389
+ /**
390
+ * TeamMode's own tm_* tools alias onto the built-in surface so the SAME
391
+ * interception applies to them (anti-backdoor: tm_read/tm_grep/tm_bash must
392
+ * never become an R6 bypass by virtue of a different tool name). The tools
393
+ * ALSO re-check with the same matchers inside their own pipelines — this
394
+ * hook-level alias is the outer defense layer.
395
+ */
396
+ const TM_TOOL_ALIASES = {
397
+ tm_read: "read",
398
+ tm_grep: "grep",
399
+ tm_bash: "bash",
400
+ };
401
+ /**
402
+ * Top routing: tool name + tool args -> category to block, or null to pass.
403
+ * `off` always passes (the hook stays installed but is a no-op).
404
+ */
405
+ export function inspectToolCall(tool, args, mode, extra = []) {
406
+ if (mode === "off")
407
+ return null;
408
+ const rawName = String(tool ?? "").trim().toLowerCase();
409
+ const name = TM_TOOL_ALIASES[rawName] ?? rawName;
410
+ if (name === "bash") {
411
+ const command = args?.command;
412
+ return classifyBashCommand(String(command ?? ""), mode, extra);
413
+ }
414
+ if (PATH_SCAN_TOOLS.has(name)) {
415
+ return classifyPathFields(args, mode, extra);
416
+ }
417
+ return null;
418
+ }
419
+ /**
420
+ * Best-effort audit of one interception. Records ONLY the tool name and
421
+ * the pattern category (privacy red line: never command text, paths,
422
+ * variable names or values); the message embeds the service id itself so
423
+ * file-backed log sinks that drop the `service` field stay greppable. A
424
+ * failing audit endpoint must never turn a block into a pass-through, so
425
+ * every failure is swallowed after the call attempt.
426
+ */
427
+ async function auditInterception(client, tool, category) {
428
+ try {
429
+ // Call the method THROUGH the app object. Destructuring `app.log`
430
+ // first would drop the SDK's `this` binding, so every real call died
431
+ // inside the SDK and was silently swallowed here — zero audit entries.
432
+ const app = client?.app;
433
+ if (!app || typeof app.log !== "function")
434
+ return;
435
+ await app.log({
436
+ body: {
437
+ level: "warn",
438
+ service: ENV_PROTECT_SERVICE,
439
+ message: `${ENV_PROTECT_SERVICE} :: ${tool} :: ${category}`,
440
+ },
441
+ });
442
+ }
443
+ catch {
444
+ /* audit is best-effort; the block below still takes effect */
445
+ }
446
+ }
447
+ /**
448
+ * Build the `tool.execute.before` hook. The hook is installed in every
449
+ * mode; with mode "off" it returns immediately and everything passes.
450
+ * When a block triggers, the audit entry is written first, then the fixed
451
+ * structured error is thrown so opencode surfaces it to the model as the
452
+ * failed tool result.
453
+ */
454
+ export function createEnvProtectHook(client, mode, extra = []) {
455
+ return async (input, output) => {
456
+ if (mode === "off")
457
+ return;
458
+ const tool = String(input?.tool ?? "");
459
+ const args = output?.args;
460
+ const category = inspectToolCall(tool, args, mode, extra);
461
+ if (!category)
462
+ return;
463
+ await auditInterception(client, tool, category);
464
+ throw envProtectError(category);
465
+ };
466
+ }
467
+ //# sourceMappingURL=envprotect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envprotect.js","sourceRoot":"","sources":["../src/envprotect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AAEH,gFAAgF;AAChF,MAAM,CAAC,MAAM,mBAAmB,GAAG,uBAAuB,CAAA;AAE1D;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAC9B,0DAA0D,CAAA;AAE5D,0EAA0E;AAC1E,MAAM,CAAC,MAAM,yBAAyB,GAAG,kBAAkB,CAAA;AAC3D,MAAM,CAAC,MAAM,2BAA2B,GAAG,oBAAoB,CAAA;AAC/D,MAAM,CAAC,MAAM,sBAAsB,GAAG,eAAe,CAAA;AACrD,MAAM,CAAC,MAAM,mBAAmB,GAAG,YAAY,CAAA;AAI/C;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAY;IAChD,IAAI,GAAG,KAAK,UAAU;QAAE,OAAO,UAAU,CAAA;IACzC,IAAI,GAAG,KAAK,KAAK;QAAE,OAAO,KAAK,CAAA;IAC/B,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,GAAY;IACzC,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAA;IACtC,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;QACxB,IAAI,CAAC,IAAI;YAAE,SAAQ;QACnB,IAAI,CAAC;YACH,KAAK,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,2DAA2D;QAC7D,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,OAAO,IAAI,KAAK,CAAC,GAAG,mBAAmB,cAAc,QAAQ,GAAG,CAAC,CAAA;AACnE,CAAC;AAED,+CAA+C;AAE/C,mEAAmE;AACnE,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC;IACjC,SAAS;IACT,eAAe;IACf,UAAU;IACV,QAAQ;IACR,WAAW;IACX,SAAS;CACV,CAAC,CAAA;AAEF,6EAA6E;AAC7E,MAAM,mBAAmB,GAAG,qCAAqC,CAAA;AAEjE;;;;;GAKG;AACH,MAAM,cAAc,GAAG,8CAA8C,CAAA;AAErE;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;QAC7B,2DAA2D;QAC3D,iEAAiE;QACjE,iEAAiE;SAChE,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;SACjF,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;SACtB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;SAClB,IAAI,EAAE,CAAA;IACT,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IACxB,yEAAyE;IACzE,wEAAwE;IACxE,oEAAoE;IACpE,4DAA4D;IAC5D,IAAI,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;QAChF,OAAO,KAAK,CAAA;IACd,CAAC;IACD,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACrC,MAAM,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAA;IAChE,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAA;IACvB,IAAI,IAAI,KAAK,MAAM;QAAE,OAAO,IAAI,CAAA;IAChC,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7B,+DAA+D;QAC/D,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAA;QAChD,OAAO,IAAI,CAAA;IACb,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;YAC9E,OAAO,KAAK,CAAA;QACd,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;AACrC,CAAC;AAED,oDAAoD;AAEpD;;;GAGG;AACH,SAAS,aAAa,CAAC,OAAe;IACpC,OAAO,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAA;AACzC,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,YAAY,CAAC,OAAe;IACnC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAA;IACtB,SAAS,CAAC;QACR,SAAS,CAAC;YACR,MAAM,IAAI,GAAG,CAAC;iBACX,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;iBACvB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;iBACrB,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;iBACvB,IAAI,EAAE,CAAA;YACT,IAAI,IAAI,KAAK,CAAC;gBAAE,MAAK;YACrB,CAAC,GAAG,IAAI,CAAA;QACV,CAAC;QACD,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QACzB,MAAM,KAAK,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;QAC5C,MAAM,YAAY,GAAG,0BAA0B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAC3D,IAAI,CAAC,YAAY;YAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,CAAA;QAC9E,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;QACvC,IAAI,CAAC,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAA;IACxC,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,gBAAgB,EAAE,SAAS,CAAC,CAAC,CAAA;AAEvF;;;;;;;;GAQG;AACH,SAAS,aAAa,CAAC,IAAY;IACjC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IAChD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;QACvB,IAAI,0BAA0B,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,SAAQ;QACpD,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,eAAe,GACnB,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC;gBAC3B,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAA;YACxE,IAAI,CAAC,eAAe,IAAI,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;gBAC1F,CAAC,EAAE,CAAA;YACL,CAAC;YACD,SAAQ;QACV,CAAC;QACD,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,EAAE,CAAA;QAC/B,OAAO,GAAG,KAAK,KAAK,IAAI,GAAG,KAAK,UAAU,CAAA;IAC5C,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAS,kBAAkB,CAAC,KAAa,EAAE,IAAY;IACrD,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IACxB,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAA;IACjC,IAAI,KAAK,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5C,OAAO,aAAa,CAAC,IAAI,CAAC,CAAA;IAC5B,CAAC;IACD,IAAI,KAAK,KAAK,UAAU;QAAE,OAAO,IAAI,CAAA;IACrC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QAC1F,OAAO,qCAAqC,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAA;IAC/D,CAAC;IACD,IAAI,KAAK,KAAK,KAAK;QAAE,OAAO,IAAI,KAAK,EAAE,CAAA;IACvC,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;6BAG6B;AAC7B,MAAM,YAAY,GAChB,kHAAkH,CAAA;AAEpH;wBACwB;AACxB,MAAM,gBAAgB,GAAG,YAAY,CAAA;AAErC;;;;GAIG;AACH,MAAM,oBAAoB,GAAG,2BAA2B,CAAA;AAExD,mDAAmD;AACnD,MAAM,eAAe,GAAG,aAAa,CAAA;AAErC;;6DAE6D;AAC7D,MAAM,iBAAiB,GAAG,oCAAoC,CAAA;AAE9D;;;;;GAKG;AACH,SAAS,cAAc,CAAC,OAAe;IACrC,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAA;IAC/C,KAAK,MAAM,QAAQ,IAAI,MAAM,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAA;QACxD,IAAI,KAAK,IAAI,aAAa,CAAC,KAAK,CAAC;YAAE,OAAO,IAAI,CAAA;IAChD,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAe,EACf,IAAoB,EACpB,QAAkB,EAAE;IAEpB,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAA;IAClC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAA;IAEtB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,mBAAmB,CAAA;IACjD,CAAC;IAED,KAAK,MAAM,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;QAC7C,IAAI,kBAAkB,CAAC,KAAK,EAAE,IAAI,CAAC;YAAE,OAAO,yBAAyB,CAAA;IACvE,CAAC;IAED,qEAAqE;IACrE,6DAA6D;IAC7D,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,CAAC;QACxD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAA;QAClC,IAAI,CAAC,KAAK;YAAE,SAAQ;QACpB,MAAM,aAAa,GAAG,mBAAmB,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;QAC7D,IAAI,aAAa;YAAE,OAAO,aAAa,CAAA;IACzC,CAAC;IAED,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,yBAAyB,CAAA;IAC7D,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,2BAA2B,CAAA;IAEnE,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,2BAA2B,CAAA;QAClE,IAAI,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,2BAA2B,CAAA;IACtE,CAAC;IAED,IAAI,cAAc,CAAC,IAAI,CAAC;QAAE,OAAO,sBAAsB,CAAA;IACvD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,sDAAsD;AAEtD;;;;;GAKG;AACH,MAAM,cAAc,GAAG,uDAAuD,CAAA;AAE9E;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,IAAyC,EACzC,IAAoB,EACpB,QAAkB,EAAE;IAEpB,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAA;IAClD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK;YAAE,SAAQ;QACjD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,SAAQ;QACvC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC;gBAAE,OAAO,mBAAmB,CAAA;QAClD,CAAC;QACD,IAAI,aAAa,CAAC,KAAK,CAAC;YAAE,OAAO,sBAAsB,CAAA;IACzD,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,gDAAgD;AAEhD,uEAAuE;AACvE,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;AAEjE;;;;;;GAMG;AACH,MAAM,eAAe,GAA2B;IAC9C,OAAO,EAAE,MAAM;IACf,OAAO,EAAE,MAAM;IACf,OAAO,EAAE,MAAM;CAChB,CAAA;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAY,EACZ,IAAyC,EACzC,IAAoB,EACpB,QAAkB,EAAE;IAEpB,IAAI,IAAI,KAAK,KAAK;QAAE,OAAO,IAAI,CAAA;IAC/B,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IACvD,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC,IAAI,OAAO,CAAA;IAChD,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,MAAM,OAAO,GAAI,IAA0C,EAAE,OAAO,CAAA;QACpE,OAAO,mBAAmB,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;IAChE,CAAC;IACD,IAAI,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,OAAO,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;IAC9C,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAWD;;;;;;;GAOG;AACH,KAAK,UAAU,iBAAiB,CAC9B,MAAe,EACf,IAAY,EACZ,QAAgB;IAEhB,IAAI,CAAC;QACH,mEAAmE;QACnE,qEAAqE;QACrE,uEAAuE;QACvE,MAAM,GAAG,GAAI,MAAyC,EAAE,GAAG,CAAA;QAC3D,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,CAAC,GAAG,KAAK,UAAU;YAAE,OAAM;QACjD,MAAM,GAAG,CAAC,GAAG,CAAC;YACZ,IAAI,EAAE;gBACJ,KAAK,EAAE,MAAM;gBACb,OAAO,EAAE,mBAAmB;gBAC5B,OAAO,EAAE,GAAG,mBAAmB,OAAO,IAAI,OAAO,QAAQ,EAAE;aAC5D;SACF,CAAC,CAAA;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,8DAA8D;IAChE,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,MAAe,EACf,IAAoB,EACpB,QAAkB,EAAE;IAEpB,OAAO,KAAK,EAAE,KAAc,EAAE,MAAe,EAAiB,EAAE;QAC9D,IAAI,IAAI,KAAK,KAAK;YAAE,OAAM;QAC1B,MAAM,IAAI,GAAG,MAAM,CAAE,KAAmC,EAAE,IAAI,IAAI,EAAE,CAAC,CAAA;QACrE,MAAM,IAAI,GAAI,MAAoD,EAAE,IAAI,CAAA;QACxE,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;QACzD,IAAI,CAAC,QAAQ;YAAE,OAAM;QACrB,MAAM,iBAAiB,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;QAC/C,MAAM,eAAe,CAAC,QAAQ,CAAC,CAAA;IACjC,CAAC,CAAA;AACH,CAAC"}
package/dist/index.d.ts CHANGED
@@ -6,7 +6,11 @@
6
6
  *
7
7
  * export default {
8
8
  * id: "team-mode", → Desktop plugin display name
9
- * server: async (input, options) => ({ config(cfg) { ...inject... } })
9
+ * server: async (input, options) => ({
10
+ * config(cfg) { ...inject agents/commands... },
11
+ * "tool.execute.before"(...) { ...R6 env protection... },
12
+ * tool: { tm_read, tm_grep, tm_bash, tm_fetch }, → JIT layer-2 tools
13
+ * })
10
14
  * }
11
15
  *
12
16
  * `server()` is the ONLY function the loader calls; a `setup` property is
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,YAAY,CAAA;AA6BhE,QAAA,MAAM,MAAM,EAAE,cAyCb,CAAA;AAED,eAAe,MAAM,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,YAAY,CAAA;AAmChE,QAAA,MAAM,MAAM,EAAE,cAuEb,CAAA;AAED,eAAe,MAAM,CAAA"}
package/dist/index.js CHANGED
@@ -6,7 +6,11 @@
6
6
  *
7
7
  * export default {
8
8
  * id: "team-mode", → Desktop plugin display name
9
- * server: async (input, options) => ({ config(cfg) { ...inject... } })
9
+ * server: async (input, options) => ({
10
+ * config(cfg) { ...inject agents/commands... },
11
+ * "tool.execute.before"(...) { ...R6 env protection... },
12
+ * tool: { tm_read, tm_grep, tm_bash, tm_fetch }, → JIT layer-2 tools
13
+ * })
10
14
  * }
11
15
  *
12
16
  * `server()` is the ONLY function the loader calls; a `setup` property is
@@ -27,6 +31,8 @@
27
31
  import { agents } from "./agents.js";
28
32
  import { commands } from "./commands.js";
29
33
  import { startBlackboardMaintenance, resolveTtlMs, DEFAULT_TTL_DAYS, } from "./blackboard.js";
34
+ import { createEnvProtectHook, parseExtraDeny, resolveEnvProtectMode, } from "./envprotect.js";
35
+ import { createTmTools } from "./tm/index.js";
30
36
  /** Runtime addendum to the team prompt: concrete board + TTL (hybrid mode). */
31
37
  function blackboardNote(root, ttlDays) {
32
38
  return [
@@ -57,6 +63,24 @@ const plugin = {
57
63
  const ttlDays = Math.round(ttlMs / (24 * 60 * 60 * 1000)) || DEFAULT_TTL_DAYS;
58
64
  const boardRoot = startBlackboardMaintenance(directory, ttlMs);
59
65
  const note = blackboardNote(boardRoot, ttlDays);
66
+ // ---------- R6 env protection (code-level interception) ----------
67
+ // Reading TM_ENV_PROTECT / TM_ENV_PROTECT_EXTRA_DENY here is the PLUGIN
68
+ // configuring ITSELF at startup — program-level configuration, the same
69
+ // category as the ttlDays option. R6 exists to stop the MODEL reading
70
+ // environment variables through tool calls; these two reads are not
71
+ // that and are out of scope of the protection.
72
+ const envProtectMode = resolveEnvProtectMode(process.env.TM_ENV_PROTECT);
73
+ const envProtectExtra = parseExtraDeny(process.env.TM_ENV_PROTECT_EXTRA_DENY);
74
+ const envProtectHook = createEnvProtectHook(input?.client, envProtectMode, envProtectExtra);
75
+ // ---------- JIT layer-2 tools (tm_read / tm_grep / tm_bash / tm_fetch) ----------
76
+ // T0.4-verified static registration: server() hooks gain a `tool` segment.
77
+ // The R6 mode/extra resolved above are passed in so the tm_* pipelines
78
+ // share the SAME interception source as the built-in tools (anti-backdoor:
79
+ // the global hook ALSO aliases tm_* onto read/grep/bash — see envprotect).
80
+ const tmRuntime = await createTmTools(input, {
81
+ mode: envProtectMode,
82
+ extra: envProtectExtra,
83
+ });
60
84
  return {
61
85
  // ---------- v1 config hook: inject agents & commands ----------
62
86
  config(cfg) {
@@ -84,6 +108,10 @@ const plugin = {
84
108
  cfg.default_agent = "team";
85
109
  }
86
110
  },
111
+ // ---------- R6: block the model's env-var read paths in code ----------
112
+ "tool.execute.before": envProtectHook,
113
+ // ---------- JIT layer-2: tm_read / tm_grep / tm_bash / tm_fetch ----------
114
+ tool: tmRuntime.tools,
87
115
  };
88
116
  },
89
117
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * JIT layer-2 tools (T1.2 + T1.3) — configuration surface.
3
+ *
4
+ * Every knob resolves from the process environment with a typed default;
5
+ * invalid values never crash plugin startup, they fall back to the default
6
+ * (same fail-soft philosophy as blackboard.resolveTtlMs and
7
+ * envprotect.resolveEnvProtectMode).
8
+ *
9
+ * Token 口径 (estimate basis): tokens ≈ chars/4, ceil. This is a rough
10
+ * ASCII/code calibration; CJK text is under-counted by it (one CJK char is
11
+ * closer to a full token than to 0.25). Two design choices keep that honest:
12
+ * the conservative boundary (estimate == threshold still offloads) and the
13
+ * hard preview cap — a mis-estimate can only make governance MORE aggressive,
14
+ * never less. This口径 is pinned by test-tm-tools.mjs.
15
+ *
16
+ * Naming note: TM_BLACKBOARD_DIR (this module, run-payload store under the
17
+ * project root, default `.blackboard/`) is a DIFFERENT artifact from the
18
+ * team blackboard in blackboard.ts (`.git/opencode-team/`, plugin options).
19
+ * They share a philosophy (TTL sweeper is the sole cleanup path), not code.
20
+ */
21
+ /** Default tm_bash read-only allowlist (P3, command-level). */
22
+ export declare const DEFAULT_BASH_READONLY_ALLOWED: readonly string[];
23
+ export interface TmConfig {
24
+ /** Offload boundary in estimated tokens (estimate == threshold offloads). */
25
+ offloadThreshold: number;
26
+ /** Raw lines a preview builder may scan before summarizing. */
27
+ previewLines: number;
28
+ /** Hard preview cap in estimated tokens — preview bloat is an R4 regression. */
29
+ previewMaxTokens: number;
30
+ /** Single tm_fetch segment cap in lines. */
31
+ fetchMaxLines: number;
32
+ /** Run-payload store dir (relative to project root, or absolute). */
33
+ blackboardDir: string;
34
+ /** Trajectory store dir (relative to project root, or absolute). */
35
+ trajectoryDir: string;
36
+ /** Handle TTL in days (expire_at + physical sweep of expired run dirs). */
37
+ blackboardTtlDays: number;
38
+ /** P3 read-only command allowlist for tm_bash. */
39
+ bashReadonlyAllowed: string[];
40
+ }
41
+ export declare const TM_CONFIG_DEFAULTS: {
42
+ readonly offloadThreshold: 2000;
43
+ readonly previewLines: 20;
44
+ readonly previewMaxTokens: 80;
45
+ readonly fetchMaxLines: 2000;
46
+ readonly blackboardDir: ".blackboard/";
47
+ readonly trajectoryDir: ".trajectory/";
48
+ readonly blackboardTtlDays: 7;
49
+ };
50
+ type EnvLike = Record<string, string | undefined>;
51
+ /**
52
+ * Parse `TM_BASH_READONLY_ALLOWED`. Unset/absent -> null (caller applies the
53
+ * default list). An explicitly EMPTY string parses to [] (deny-all bash) —
54
+ * an explicit user choice is respected; separator is comma or semicolon.
55
+ */
56
+ export declare function parseAllowlistEnv(raw: unknown): string[] | null;
57
+ /** Resolve the full tm-tools config from an env-like record (default: process.env). */
58
+ export declare function resolveTmConfig(env?: EnvLike): TmConfig;
59
+ /**
60
+ * Token estimate — chars/4, ceil (口径 documented above). Empty input = 0.
61
+ */
62
+ export declare function estimateTokens(text: string): number;
63
+ /**
64
+ * Offload decision. estimate == threshold ALSO offloads — the conservative
65
+ * boundary (HUMAN-pinned spec: "等于阈值也卸载").
66
+ */
67
+ export declare function shouldOffload(tokens: number, threshold: number): boolean;
68
+ /** Collapse whitespace and hard-cap a string for messages / clue lines. */
69
+ export declare function shorten(text: unknown, max: number): string;
70
+ export {};
71
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/tm/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,+DAA+D;AAC/D,eAAO,MAAM,6BAA6B,EAAE,SAAS,MAAM,EAI1D,CAAA;AAED,MAAM,WAAW,QAAQ;IACvB,6EAA6E;IAC7E,gBAAgB,EAAE,MAAM,CAAA;IACxB,+DAA+D;IAC/D,YAAY,EAAE,MAAM,CAAA;IACpB,gFAAgF;IAChF,gBAAgB,EAAE,MAAM,CAAA;IACxB,4CAA4C;IAC5C,aAAa,EAAE,MAAM,CAAA;IACrB,qEAAqE;IACrE,aAAa,EAAE,MAAM,CAAA;IACrB,oEAAoE;IACpE,aAAa,EAAE,MAAM,CAAA;IACrB,2EAA2E;IAC3E,iBAAiB,EAAE,MAAM,CAAA;IACzB,kDAAkD;IAClD,mBAAmB,EAAE,MAAM,EAAE,CAAA;CAC9B;AAED,eAAO,MAAM,kBAAkB;;;;;;;;CAQrB,CAAA;AAEV,KAAK,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;AAmBjD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,EAAE,GAAG,IAAI,CAG/D;AAED,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,GAAG,GAAE,OAAqB,GAAG,QAAQ,CAYpE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAExE;AAED,2EAA2E;AAC3E,wBAAgB,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAG1D"}