@te-river/opencode-team-mode 1.5.0 → 1.5.2

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 (54) hide show
  1. package/README.md +157 -53
  2. package/README.zh-CN.md +137 -53
  3. package/dist/agents.d.ts.map +1 -1
  4. package/dist/agents.js +128 -26
  5. package/dist/agents.js.map +1 -1
  6. package/dist/approval-gate.d.ts +147 -0
  7. package/dist/approval-gate.d.ts.map +1 -0
  8. package/dist/approval-gate.js +471 -0
  9. package/dist/approval-gate.js.map +1 -0
  10. package/dist/commands.js +3 -3
  11. package/dist/commands.js.map +1 -1
  12. package/dist/envprotect.d.ts +234 -0
  13. package/dist/envprotect.d.ts.map +1 -0
  14. package/dist/envprotect.js +820 -0
  15. package/dist/envprotect.js.map +1 -0
  16. package/dist/index.d.ts +5 -1
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +112 -1
  19. package/dist/index.js.map +1 -1
  20. package/dist/tm/config.d.ts +105 -0
  21. package/dist/tm/config.d.ts.map +1 -0
  22. package/dist/tm/config.js +118 -0
  23. package/dist/tm/config.js.map +1 -0
  24. package/dist/tm/guard.d.ts +56 -0
  25. package/dist/tm/guard.d.ts.map +1 -0
  26. package/dist/tm/guard.js +249 -0
  27. package/dist/tm/guard.js.map +1 -0
  28. package/dist/tm/index.d.ts +41 -0
  29. package/dist/tm/index.d.ts.map +1 -0
  30. package/dist/tm/index.js +93 -0
  31. package/dist/tm/index.js.map +1 -0
  32. package/dist/tm/preview.d.ts +46 -0
  33. package/dist/tm/preview.d.ts.map +1 -0
  34. package/dist/tm/preview.js +354 -0
  35. package/dist/tm/preview.js.map +1 -0
  36. package/dist/tm/ptc.d.ts +279 -0
  37. package/dist/tm/ptc.d.ts.map +1 -0
  38. package/dist/tm/ptc.js +839 -0
  39. package/dist/tm/ptc.js.map +1 -0
  40. package/dist/tm/refs.d.ts +37 -0
  41. package/dist/tm/refs.d.ts.map +1 -0
  42. package/dist/tm/refs.js +58 -0
  43. package/dist/tm/refs.js.map +1 -0
  44. package/dist/tm/store.d.ts +95 -0
  45. package/dist/tm/store.d.ts.map +1 -0
  46. package/dist/tm/store.js +222 -0
  47. package/dist/tm/store.js.map +1 -0
  48. package/dist/tm/tools.d.ts +84 -0
  49. package/dist/tm/tools.d.ts.map +1 -0
  50. package/dist/tm/tools.js +710 -0
  51. package/dist/tm/tools.js.map +1 -0
  52. package/dist/types.d.ts +155 -0
  53. package/dist/types.d.ts.map +1 -1
  54. package/package.json +3 -2
package/dist/agents.js CHANGED
@@ -23,6 +23,75 @@
23
23
  * - All agents run at temperature 0.2 for format discipline.
24
24
  */
25
25
  /* ------------------------------------------------------------------ */
26
+ /* Tool whitelist builder (Phase 2 / T2.1, G2 ruling "方案甲") */
27
+ /* ------------------------------------------------------------------ */
28
+ /**
29
+ * Builds the permission block that IS the agent's tool whitelist.
30
+ *
31
+ * Verified live (T0.4③): a "deny" permission removes the built-in tool
32
+ * from the model's tool surface entirely — zero bypass, zero hallucinated
33
+ * calls. So a whitelist = deny every excluded built-in + allow the kept
34
+ * set, exactly the shape the D6 probe validated
35
+ * (%TEMP%/opencode/tm-probe/workspace/opencode.json).
36
+ *
37
+ * G2 rulings baked in:
38
+ * - glob/list never enter the whitelist — file enumeration goes through
39
+ * tm_bash (ls / dir / Get-ChildItem);
40
+ * - webfetch/websearch excluded network-wide (P5 network zero);
41
+ * - "tm_*" covers exactly the four governed tools (tm_read / tm_grep /
42
+ * tm_bash / tm_fetch); the R6 env-protection hook aliases onto them
43
+ * unchanged, so narrowing the surface does not weaken the anti-backdoor
44
+ * chain.
45
+ *
46
+ * T2.1 review revision (Critical fix): the built-in bash RETURNS for the
47
+ * execution roles (team / implementer / reviewer / tester). G2 only ruled
48
+ * that glob/list enumeration goes through tm_bash — it never ruled away
49
+ * command execution, and tm_bash is a read-only allowlist that cannot run
50
+ * npm test / tsc / --help probes. R6 keeps governing bash's env surface
51
+ * through the envprotect hook, independently of this matrix.
52
+ */
53
+ const NEVER_ALLOWED = [
54
+ "read",
55
+ "grep",
56
+ "glob",
57
+ "list",
58
+ "apply_patch",
59
+ "webfetch",
60
+ "websearch",
61
+ "todowrite",
62
+ "lsp",
63
+ "skill",
64
+ "question",
65
+ ];
66
+ /** Built-ins granted per agent; every one NOT granted is denied. */
67
+ const PER_AGENT_TOOLS = ["edit", "write", "task", "bash"];
68
+ /** The governed tools, named explicitly next to the "tm_*" wildcard
69
+ * (belt-and-braces: the explicit allows survive even if a host ever
70
+ * stops expanding the wildcard). */
71
+ const TM_TOOLS = ["tm_read", "tm_grep", "tm_bash", "tm_fetch"];
72
+ /** M3: tm_ptc_run — explicit per-agent grant (five specialists = allow,
73
+ * team = deny to override the tm_* wildcard). Design §3: PTC is a
74
+ * governed, read-only bridge; the team lead orchestrates, it does not
75
+ * run batch programs itself. */
76
+ const PTC_TOOL = "tm_ptc_run";
77
+ const whitelist = (...granted) => {
78
+ const permission = {};
79
+ for (const tool of NEVER_ALLOWED)
80
+ permission[tool] = "deny";
81
+ for (const tool of PER_AGENT_TOOLS) {
82
+ permission[tool] = granted.includes(tool) ? "allow" : "deny";
83
+ }
84
+ for (const tool of TM_TOOLS)
85
+ permission[tool] = "allow";
86
+ permission["tm_*"] = "allow";
87
+ return permission;
88
+ };
89
+ /** Apply the M3 tm_ptc_run grant to an agent's permission block. The five
90
+ * specialists get `allow`; the team lead gets `deny` (overrides the wildcard). */
91
+ function applyPtcPermission(permission, isTeamLead) {
92
+ permission[PTC_TOOL] = isTeamLead ? "deny" : "allow";
93
+ }
94
+ /* ------------------------------------------------------------------ */
26
95
  /* Reply contract + hybrid blackboard (appended to every specialist) */
27
96
  /* ------------------------------------------------------------------ */
28
97
  const REPLY_CONTRACT = `
@@ -74,8 +143,9 @@ process management every run.
74
143
  ## Triage — classify before acting (Step 0, always)
75
144
  - Question ≠ work order. When the user asks, analyzes, or consults
76
145
  ("why does X fail?", "how would we do Y?"), ANSWER it — read code if
77
- useful, change nothing. If answering needs external knowledge, dispatch
78
- \`researcher\`; don't grind through it yourself.
146
+ useful, change nothing. If answering needs a deeper dig through the
147
+ codebase than your context affords, dispatch \`researcher\`; don't
148
+ grind through it yourself.
79
149
  - Spotted an obvious defect while answering? Propose the fix and WAIT for
80
150
  the go-ahead — never fix-on-the-sly.
81
151
  - Explicit action request ("fix X", "add Y", "refactor Z") → route via the
@@ -96,7 +166,7 @@ files — NOT docs, comments, formatting, NOT *.test.* files).
96
166
  | Docs / comments / formatting only | implementer (or direct edit, see below) |
97
167
  | Product behavior change (bug fix, small feature) | implementer → tester → reviewer |
98
168
  | Multi-module / cross-interface feature | architect → implementer → tester → reviewer(s) |
99
- | Unknown external tech / dependency in play | researcher first, then the fitting row above |
169
+ | Unfamiliar tech / dependency in play | researcher first (local-repo evidence: call sites, installed/vendored packages, shipped docs), then the fitting row above |
100
170
 
101
171
  - FIXED MINIMUM PIPELINES: the reviewer may be skipped ONLY for
102
172
  non-product artifacts, with a one-line reason. A product change routed
@@ -123,8 +193,9 @@ Count the dispatches your routing row prescribes:
123
193
  ONLY when it is genuinely absent. These docs define the conventions
124
194
  the whole team must follow; distill the binding ones for your
125
195
  dispatches. Then read the relevant source yourself; dispatch
126
- \`researcher\` ONLY for genuinely unknown external tech. Done means
127
- you can state which files change, in what order, and the risks.
196
+ \`researcher\` ONLY for genuinely unfamiliar tech — its findings come
197
+ from the local repo (code, docs, installed packages), never the web.
198
+ Done means you can state which files change, in what order, and the risks.
128
199
  - Plan (≤30 lines): Goal / Root cause or scope (file:line evidence) /
129
200
  Change list (file → what) / Pipeline (routing row + agents) /
130
201
  Assumptions & risks / Open questions.
@@ -244,8 +315,8 @@ Findings that drive architecture or API usage must be verified before
244
315
  adoption:
245
316
  - The researcher tags each finding High / Medium / Low confidence.
246
317
  - Low/medium-confidence claims that affect the design get a second check
247
- (re-ask the researcher for another source, or sanity-check against the
248
- actual codebase).
318
+ (re-ask the researcher for a second local source, or sanity-check against
319
+ the actual codebase).
249
320
  - Never let an unverified claim silently become an implementation
250
321
  decision; list remaining assumptions explicitly in the final report.
251
322
 
@@ -272,12 +343,10 @@ If a target file does not exist, offer to create it; skip both when the
272
343
  user opted out.
273
344
  `,
274
345
  color: "#E879F9", // purple
275
- permission: {
276
- edit: "allow",
277
- bash: "allow",
278
- webfetch: "allow",
279
- task: "allow",
280
- },
346
+ // Whitelist (7 tools): tm_* x4 + task dispatch + edit (<=10-line
347
+ // non-product edits, see "When you may edit directly") + write (board
348
+ // files) + bash (discovery-gate probes: --help, installed versions).
349
+ permission: whitelist("task", "edit", "write", "bash"),
281
350
  temperature: 0.2,
282
351
  };
283
352
  /* ------------------------------------------------------------------ */
@@ -320,9 +389,12 @@ When the team lead sends back a design flaw found in review or testing:
320
389
  of guessing about the codebase.
321
390
  `,
322
391
  color: "#38BDF8", // sky blue
323
- // Read-only on PROJECT sources; a dispatched board artifact is explicitly
324
- // writable (see Blackboard rules).
325
- permission: { edit: "allow", bash: "deny", webfetch: "allow" },
392
+ // Whitelist (5 tools): tm_* x4 + task dispatch. Fully read-only by
393
+ // design (T2.1): output lives in the reply; if a board artifact is ever
394
+ // dispatched, the BLACKBOARD WRITE FAILED fallback hands the content to
395
+ // the lead inline (see Blackboard rules). No bash — unchanged from the
396
+ // pre-T2.1 architect bash:deny posture.
397
+ permission: whitelist("task"),
326
398
  temperature: 0.2,
327
399
  };
328
400
  /* ------------------------------------------------------------------ */
@@ -359,7 +431,9 @@ to you by the team lead.
359
431
  check, the previously failing test).
360
432
  `,
361
433
  color: "#4ADE80", // green
362
- permission: { edit: "allow", bash: "allow", webfetch: "allow" },
434
+ // Whitelist (7 tools): tm_* x4 + edit/write (code implementation) + bash
435
+ // (narrowest verification for the fix: build / typecheck / failing test).
436
+ permission: whitelist("edit", "write", "bash"),
363
437
  temperature: 0.2,
364
438
  };
365
439
  /* ------------------------------------------------------------------ */
@@ -417,9 +491,12 @@ the previously flagged scope plus regressions introduced by the fixes;
417
491
  confirm each prior finding item by item (fixed / not fixed / partial).
418
492
  `,
419
493
  color: "#FB923C", // orange
420
- // May edit ONLY a dispatched board artifact; never the reviewed code
421
- // (see Blackboard rules + role rules).
422
- permission: { edit: "allow", bash: "allow", webfetch: "allow" },
494
+ // Whitelist (6 tools): tm_* x4 + task dispatch + bash. No edit/write
495
+ // (T2.1): findings travel in the reply skeleton; a dispatched board
496
+ // artifact rides the BLACKBOARD WRITE FAILED inline fallback (see
497
+ // Blackboard rules). bash backs the evidence standard — the reviewer
498
+ // must be able to run npm test / tsc itself.
499
+ permission: whitelist("task", "bash"),
423
500
  temperature: 0.2,
424
501
  };
425
502
  /* ------------------------------------------------------------------ */
@@ -483,7 +560,9 @@ worse than admitting the gap.
483
560
  refactor instead of contorting the test.
484
561
  `,
485
562
  color: "#F472B6", // pink
486
- permission: { edit: "allow", bash: "allow", webfetch: "allow" },
563
+ // Whitelist (7 tools): tm_* x4 + edit/write (test files) + bash (the
564
+ // whole verification stack: build / typecheck / lint / test runs).
565
+ permission: whitelist("edit", "write", "bash"),
487
566
  temperature: 0.2,
488
567
  };
489
568
  /* ------------------------------------------------------------------ */
@@ -491,10 +570,11 @@ worse than admitting the gap.
491
570
  /* ------------------------------------------------------------------ */
492
571
  const researcher = {
493
572
  mode: "subagent",
494
- description: "Researcher — investigates libraries, APIs, best practices, and " +
495
- "documentation; every finding carries a source and confidence tag so " +
496
- "the team can decide what needs verification. Use for information that " +
497
- "must inform a technical decision.",
573
+ description: "Researcher — investigates the local repository: code, configs, " +
574
+ "installed/vendored packages, and shipped documentation; every finding " +
575
+ "carries a source (file:line) and confidence tag so the team can decide " +
576
+ "what needs verification. Use for information that must inform a " +
577
+ "technical decision. Web research is out of scope (no network tools).",
498
578
  prompt: `You are the **Researcher** on a multi-agent coding team.
499
579
 
500
580
  ## Role
@@ -534,7 +614,13 @@ by the team — flag prominently if that is the case.
534
614
  of re-deriving.
535
615
  `,
536
616
  color: "#A78BFA", // violet
537
- permission: { edit: "allow", bash: "allow", webfetch: "allow", websearch: "allow" },
617
+ // Whitelist (4 tools): tm_* x4 ONLY — intentionally below the 5-7 band
618
+ // (T2.1): pure local research. webfetch/websearch are excluded
619
+ // network-wide (P5), which also removes the dangling websearch:allow
620
+ // found in T0.3; file enumeration goes through tm_bash (G2 方案甲).
621
+ // No bash / no execution rights is intentional trimming — local
622
+ // research reads, it does not run.
623
+ permission: whitelist(),
538
624
  temperature: 0.2,
539
625
  };
540
626
  /* Shared rules appended to every specialist prompt (after the contract). */
@@ -545,6 +631,14 @@ Every "done / fixed / passed" claim in your reply must carry its
545
631
  evidence: command output, log lines, or a diff. No narrative-only
546
632
  completions.
547
633
 
634
+ ## Tool surface (do not retry removed tools)
635
+ All file reads / searches / enumeration go through tm_read / tm_grep / tm_bash.
636
+ The built-in read/grep/glob/list tools are removed from the tool surface —
637
+ retrying them only wastes a turn. Built-in bash exists only where granted
638
+ (team / implementer / reviewer / tester run commands: build / test / git);
639
+ architect and researcher have no bash at all — one-off read-only commands
640
+ go through tm_bash or are reported as a gap.
641
+
548
642
  ## Project conventions
549
643
  If the project README (or AGENTS.md) is quoted in your dispatch, treat
550
644
  its conventions as binding — they outrank your defaults. Do not re-open
@@ -555,6 +649,14 @@ belongs to the work.`;
555
649
  for (const a of [architect, implementer, reviewer, tester, researcher]) {
556
650
  a.prompt = (a.prompt ?? "") + REPLY_CONTRACT + SHARED_RULES;
557
651
  }
652
+ /* M3: tm_ptc_run permission — five specialists get allow, team gets deny
653
+ * (overrides the tm_* wildcard). Applied once at module init. */
654
+ if (teamLead.permission)
655
+ applyPtcPermission(teamLead.permission, true);
656
+ for (const a of [architect, implementer, reviewer, tester, researcher]) {
657
+ if (a.permission)
658
+ applyPtcPermission(a.permission, false);
659
+ }
558
660
  /* ------------------------------------------------------------------ */
559
661
  /* Export all agents keyed by name */
560
662
  /* ------------------------------------------------------------------ */
@@ -1 +1 @@
1
- {"version":3,"file":"agents.js","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agents.js","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Unified approval gate — Layer 2 (timeout auto-reject) + Layer 3 (SDK
3
+ * fallback) of the R6 + R2 approval flow.
4
+ *
5
+ * The host only shows its official confirmation dialog for command patterns
6
+ * declared `ask` in an agent's permission config (Layer 1, envprotect.ts).
7
+ * When a dialog opens it SUSPENDS the tool call and waits for a human; on the
8
+ * OpenCode DESKTOP that human is present, but a request can also be left
9
+ * unanswered. This module owns ONE timer that watches the host permission
10
+ * events and closes abandoned requests on the safe side.
11
+ *
12
+ * REAL host contract (1.18.29, probed live via SSE + the plugin event hook —
13
+ * see gate-test report-p5):
14
+ * permission.asked → properties = { id, sessionID, permission: "bash",
15
+ * patterns: [<concrete command segment>, …], metadata:
16
+ * { command }, always: [<host's "always" generalization
17
+ * proposal>], tool: { messageID, callID } } — the props
18
+ * carry NO `type` field, and the tool name sits in
19
+ * `permission`. A props matching one of OUR injected
20
+ * bash ask sets means the dialog is ours: audit "ask",
21
+ * arm the timeout, and — for the R6 env face — mark
22
+ * the session deferrable (canDefer, see below).
23
+ * permission.replied → properties = { sessionID, requestID, reply } on the
24
+ * wire, but the plugin-side observer has also seen it
25
+ * with ONLY { sessionID }. The gate therefore cancels
26
+ * EVERY pending timer of that session on a reply: an
27
+ * already-approved command left on a live timer would
28
+ * get a second reject after the timeout, the SDK call
29
+ * would hit a dead id (4xx) and flip the gate
30
+ * permanently degraded.
31
+ *
32
+ * Older/typed spellings (`permission.updated`, `pattern`, `permissionID`,
33
+ * `response`) keep being accepted — the shipped SDK d.ts still names the
34
+ * events that way, and some builds omit the reply id entirely (a short
35
+ * ghost-suppression window per session then absorbs late/duplicate `asked`
36
+ * replays instead of re-arming a ghost timer).
37
+ *
38
+ * Hard rules (HUMAN-approved):
39
+ * - the plugin NEVER self-allows: `reply` is fixed to "reject". The only
40
+ * way a gated command runs is a human approving the dialog;
41
+ * - on `TM_ENV_PROTECT=off` the gate is not armed (index.ts decides);
42
+ * - deferral is SESSION-SCOPED (canDefer): only sessions registered as
43
+ * carrying our injected ask set may have env reads passed through —
44
+ * registration comes from an exec-role user prompt (the live-proven
45
+ * `message.updated` UserMessage{role,agent,sessionID} signal, plus the
46
+ * documented `chat.message` hook) or from an R6-env-classified asked
47
+ * event. Stock build/plan sessions never register, so the global R6
48
+ * hook keeps hard-throwing there — closing the R6 bypass the global
49
+ * `isArmed()` deferral allowed;
50
+ * - if the SDK reply FAILS (network/host — including the v1
51
+ * `throwOnError:false` envelope resolving with `{ error }`), we cannot
52
+ * close the dialog, so the gate flips itself permanently "degraded" —
53
+ * the R6 hook stops deferring env reads and hard-throws them again
54
+ * (fail-closed), and the event is audited "degraded".
55
+ *
56
+ * SDK surface (probed live on 1.18.x): the plugin's `input.client` is the v1
57
+ * `OpencodeClient`, which has NO `permission` namespace and NO list/create
58
+ * endpoint; the reply path is the top-level
59
+ * `postSessionIdPermissionsPermissionId({ path: { id: sessionID,
60
+ * permissionID }, body: { response } })`. `client.permission.reply` /
61
+ * `client.permission.list` are still tried first so a richer host build is
62
+ * supported, but on v1 they are simply absent and the poll fallback is a
63
+ * no-op (events are the sole pending signal there).
64
+ */
65
+ import type { PermissionEvent } from "./types.js";
66
+ /** Default timeout, in minutes, before an unanswered dialog is auto-rejected. */
67
+ export declare const DEFAULT_ASK_TIMEOUT_MIN = 10;
68
+ /**
69
+ * Hard floor (minutes) for the auto-reject timeout. Measured live on the
70
+ * OpenCode Desktop: a `permission.replied` (human answered "once") reaches
71
+ * the plugin through the host event bus ~120s LATE — a 1-minute timer
72
+ * therefore fires BEFORE the cancel arrives and auto-rejects the just-
73
+ * approved request on a dead id (4xx → permanent degraded, the D4
74
+ * double-reject race). 3 min = observed ~120s lag + margin; in-range
75
+ * values below the floor clamp UP to it (a longer timeout is always the
76
+ * safe direction — the dialog just stays open a bit longer).
77
+ */
78
+ export declare const MIN_ASK_TIMEOUT_MIN = 3;
79
+ /** The verdict vocabulary recorded in the audit trail (privacy: no command
80
+ * text, path, variable name or value is ever logged). */
81
+ export type ApprovalVerdict = "ask" | "allowed-once" | "allowed-always" | "rejected" | "timeout-rejected" | "degraded";
82
+ /**
83
+ * Resolve `TM_ASK_TIMEOUT_MIN`. Unset / blank / non-numeric / <1 / >1440
84
+ * (24h) fall back to the 10-minute default — a mistyped value can never
85
+ * disable the timeout or make it absurdly long. Valid values below the
86
+ * 3-minute bus-lag floor clamp UP to it (1→3, 2→3; see
87
+ * `MIN_ASK_TIMEOUT_MIN` for the measured ~120s replied-event lag).
88
+ */
89
+ export declare function resolveAskTimeoutMs(env?: Record<string, string | undefined>): number;
90
+ type TimerHandle = unknown;
91
+ export interface GateTimers {
92
+ setTimeoutFn: (cb: () => void, ms: number) => TimerHandle;
93
+ clearTimeoutFn: (handle: TimerHandle) => void;
94
+ }
95
+ export interface ApprovalGateDeps {
96
+ client: unknown;
97
+ timeoutMs: number;
98
+ /** Injectable timers (default: real global timers) for deterministic tests. */
99
+ timers?: GateTimers;
100
+ /** Injectable clock (default: Date.now) for deterministic window tests. */
101
+ now?: () => number;
102
+ }
103
+ export interface ApprovalGate {
104
+ /** Handle one host event (permission.asked/updated / permission.replied). */
105
+ handleEvent(event: PermissionEvent): void;
106
+ /** Start the poll fallback (no-op on v1 where no list endpoint exists). */
107
+ start(): void;
108
+ /** Stop the poll + every pending timer. */
109
+ dispose(): void;
110
+ /** Global gate health: armed, not degraded, and the client can reply. */
111
+ isArmed(): boolean;
112
+ /**
113
+ * The R6 hook's deferral query: TRUE only when this exact session is
114
+ * registered as carrying our injected ask set AND the gate is healthy.
115
+ * Replaces the old global `isArmed()` deferral (which let stock build/plan
116
+ * sessions pass env reads silently with no dialog in front of them).
117
+ */
118
+ canDefer(sessionID?: string): boolean;
119
+ /** True once a session registered (exec-role prompt seen on
120
+ * message.updated / chat.message by index.ts, or an R6-env-classified
121
+ * asked event). Introspection for tests/diagnostics. */
122
+ hasLiveAsk(sessionID?: string): boolean;
123
+ /** Mark a session as carrying the injected ask set (index.ts calls this
124
+ * when a user prompt routes to an agent we injected the escalated bash
125
+ * ask object into — proven live: `message.updated` UserMessage carries
126
+ * { sessionID, role:"user", agent } and fires BEFORE the session's first
127
+ * tool call, while permission.asked fires AFTER tool.execute.before). */
128
+ registerExecSession(sessionID?: string): void;
129
+ /** True after the user picked "always" on an env-related ask in this session
130
+ * — subsequent env reads pass silently (the approval event itself is audited). */
131
+ isEnvApproved(sessionID?: string): boolean;
132
+ /** Number of pending requests currently being timed (tests / introspection). */
133
+ pendingSize(): number;
134
+ }
135
+ /** True when the client exposes a permission-reply path at all. index.ts
136
+ * arms the gate ONLY for such a client — fail-closed otherwise (no armed
137
+ * auto-reject ⇒ the R6 hook keeps hard-throwing every env read). */
138
+ export declare function hasPermissionReplyCapability(client: unknown): boolean;
139
+ /**
140
+ * Create the approval gate. It is intentionally pure about timing +
141
+ * replying + session registration; the caller (index.ts) decides whether to
142
+ * arm it (mode != off AND the client is reply-capable), feeds events in, and
143
+ * wires `canDefer(sessionID)` into the R6 hook.
144
+ */
145
+ export declare function createApprovalGate(deps: ApprovalGateDeps): ApprovalGate;
146
+ export {};
147
+ //# sourceMappingURL=approval-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval-gate.d.ts","sourceRoot":"","sources":["../src/approval-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+DG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAGjD,iFAAiF;AACjF,eAAO,MAAM,uBAAuB,KAAK,CAAA;AAEzC;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB,IAAI,CAAA;AAiBpC;0DAC0D;AAC1D,MAAM,MAAM,eAAe,GACvB,KAAK,GACL,cAAc,GACd,gBAAgB,GAChB,UAAU,GACV,kBAAkB,GAClB,UAAU,CAAA;AAEd;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GAAG,MAAM,CAQjG;AAED,KAAK,WAAW,GAAG,OAAO,CAAA;AAE1B,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,KAAK,WAAW,CAAA;IACzD,cAAc,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAA;CAC9C;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,2EAA2E;IAC3E,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,WAAW,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAAA;IACzC,2EAA2E;IAC3E,KAAK,IAAI,IAAI,CAAA;IACb,2CAA2C;IAC3C,OAAO,IAAI,IAAI,CAAA;IACf,yEAAyE;IACzE,OAAO,IAAI,OAAO,CAAA;IAClB;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IACrC;;8DAE0D;IAC1D,UAAU,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IACvC;;;;8EAI0E;IAC1E,mBAAmB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7C;uFACmF;IACnF,aAAa,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;IAC1C,gFAAgF;IAChF,WAAW,IAAI,MAAM,CAAA;CACtB;AA6GD;;qEAEqE;AACrE,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAErE;AAgBD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,YAAY,CA6OvE"}