auto-permission-system 3.9.0 → 3.9.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.
package/CHANGELOG.md CHANGED
@@ -3,15 +3,6 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  Entries below [1.0.0] are generated automatically by [semantic-release](https://semantic-release.gitbook.io/) from [Conventional Commits](https://www.conventionalcommits.org/) — do not edit by hand.
5
5
 
6
- # [3.9.0](https://github.com/Navin009/auto-permission-system/compare/v3.8.0...v3.9.0) (2026-10-13)
7
-
8
- ### Features
9
-
10
- * **demo:** crisp widescreen HD terminal recording (`demo.mp4`, `demo.gif`) demonstrating Cloud Auto Mode and Advanced Secure Mode
11
- * **branding:** official security shield logo and banner in documentation and package
12
- * **docs:** comprehensive FAQ, Cloud Auto Mode architecture, interactive prompt guides, and zero-trust documentation
13
- * **metadata:** package keywords, clean npm manifest, and remove legacy PyPI references
14
-
15
6
  # [2.0.0](https://github.com/Navin009/auto-permission-system/compare/v1.2.0...v2.0.0) (2026-10-02)
16
7
 
17
8
 
package/README.md CHANGED
@@ -11,11 +11,11 @@
11
11
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)
12
12
  [![Pi package](https://img.shields.io/badge/pi--package-gallery-blueviolet)](https://pi.dev/packages)
13
13
  [![CI](https://github.com/Navin009/auto-permission-system/actions/workflows/ci.yml/badge.svg)](https://github.com/Navin009/auto-permission-system/actions/workflows/ci.yml)
14
- [![Cloud Auto Mode for Pi](https://img.shields.io/badge/Pi-Cloud_Auto_Mode-blue.svg)](#secure-auto-mode--cloud-auto-mode)
14
+ [![Claude Auto Mode for Pi](https://img.shields.io/badge/Pi-Claude_Auto_Mode-blue.svg)](#secure-auto-mode--claude-auto-mode)
15
15
  [![Security for Pi](https://img.shields.io/badge/Security-for_Pi-green.svg)](#security-for-pi-how-it-works)
16
16
  [![Permission System for Pi](https://img.shields.io/badge/Permission_System-for_Pi-orange.svg)](#permission-system--modes)
17
17
 
18
- **Auto Permission System** is a zero-trust permission architecture and **Secure Auto Mode / Cloud Auto Mode for the [Pi coding agent](https://pi.dev)**. It brings the confidence of **Claude's auto mode** to Pi—enabling autonomous agent workflows while actively scanning files, shell commands, and tool content to **prevent data breaches and credential exfiltration**.
18
+ **Auto Permission System** is a zero-trust permission architecture and **Secure Auto Mode / Claude Auto Mode for the [Pi coding agent](https://pi.dev)**. It brings the confidence of **Claude's auto mode** to Pi—enabling autonomous agent workflows while actively scanning files, shell commands, and tool content to **prevent data breaches and credential exfiltration**.
19
19
 
20
20
  Whether running interactively with keyboard-driven ask prompts or running headlessly in CI/CD pipelines, this package enforces multi-layer boundaries across filesystem access, network egress, shell commands, and MCP tools.
21
21
 
@@ -25,14 +25,12 @@ Whether running interactively with keyboard-driven ask prompts or running headle
25
25
 
26
26
  ![Auto Permission System Demo](https://raw.githubusercontent.com/Navin009/auto-permission-system/main/assets/demo.gif)
27
27
 
28
- > **High-Definition Video:** Watch or download the full-resolution recording at [assets/demo.mp4](https://raw.githubusercontent.com/Navin009/auto-permission-system/main/assets/demo.mp4) (H.264, 60fps). Run `./demo/run-demo.sh` locally to reproduce this live simulation.
29
-
30
28
  ### Demonstrated Moments in the Recording:
31
29
  1. **Interactive `.env` Read Guard:** Intercepts `read(".env")` before the AI can see secrets. Displays a 10s countdown prompt defaulting to `No` (Block).
32
30
  2. **Kernel OS Sandbox Confinement:** Prevents bash commands from writing to system paths outside allowed roots (`EROFS: Read-only file system`).
33
31
  3. **Governed Network Egress:** Catches unknown outbound connections (`curl`) with an interactive domain ask prompt and a 15s countdown timer.
34
32
  4. **Real-Time API Key & Secret Redaction:** Automatically detects exposed API tokens (`sk-proj-...`, `ghp_...`) in command output and replaces them with `***REDACTED***` before reaching the LLM context.
35
- 5. **Autonomous Cloud Auto Mode:** Demonstrates headless, fail-closed CI execution (`pi -p`) with zero deadlocks and zero hangs.
33
+ 5. **Autonomous Claude Auto Mode:** Demonstrates headless, fail-closed CI execution (`pi -p`) with zero deadlocks and zero hangs.
36
34
 
37
35
  ---
38
36
 
@@ -43,7 +41,7 @@ Whether running interactively with keyboard-driven ask prompts or running headle
43
41
  - [Key Features at a Glance](#key-features-at-a-glance)
44
42
  - [Security for Pi: How It Works](#security-for-pi-how-it-works)
45
43
  - [Interactive Ask Prompts](#interactive-ask-prompts)
46
- - [Secure Auto Mode & Cloud Auto Mode](#secure-auto-mode--cloud-auto-mode)
44
+ - [Secure Auto Mode & Claude Auto Mode](#secure-auto-mode--claude-auto-mode)
47
45
  - [Real-Time Secret Detection & Content Scanning](#real-time-secret-detection--content-scanning)
48
46
  - [Permission System & Modes](#permission-system--modes)
49
47
  - [Configuration Reference](#configuration-reference)
@@ -100,7 +98,7 @@ Once installed, the extension automatically loads whenever you launch `pi`. Chec
100
98
  | Feature | Description | Protection Level |
101
99
  |---|---|---|
102
100
  | 🛡️ **3-Layer Defense** | OS Kernel sandbox (`bubblewrap`/`sandbox-exec`) + in-process tool guard + real-time content redactor. | Zero-Trust |
103
- | ⚡ **Secure Auto Mode** | Autonomous execution inspired by Claude's auto mode; scans files and output to prevent data breaches. | Autonomous / Cloud |
101
+ | ⚡ **Secure Auto Mode** | Autonomous execution inspired by Claude's auto mode; scans files and output to prevent data breaches. | Autonomous / Claude |
104
102
  | 🔑 **Interactive Ask Prompts** | Real-time terminal prompts with 10s countdowns (`Once`, `Session`, `Project`, `Global`). | Interactive Human-in-the-Loop |
105
103
  | 🔍 **AST & Regex Secret Scanner** | Scans tool stdout/stderr for API keys, private keys, and tokens, redacting them before model context. | Data Breach Prevention |
106
104
  | 🌐 **Network Egress Gate** | Intercepts unapproved network connections and prompts for confirmation with wildcards. | Data Leak Prevention |
@@ -162,7 +160,7 @@ When an unlisted domain, `.env` file, or directory write is requested during an
162
160
 
163
161
  > No
164
162
  Yes, just this once
165
- Yes, for this session
163
+ Yes, for this conversation
166
164
 
167
165
  Default: No (10s)
168
166
  ```
@@ -179,7 +177,7 @@ When an unlisted domain, `.env` file, or directory write is requested during an
179
177
 
180
178
  > No
181
179
  Yes, just this once
182
- Yes, all in group for this session
180
+ Yes, all in group for this conversation
183
181
  Yes, always…
184
182
 
185
183
  Default: Yes, just this once (15s)
@@ -191,33 +189,34 @@ When an unlisted domain, `.env` file, or directory write is requested during an
191
189
  ⚠ Sensitive information detected: API Secret Key
192
190
 
193
191
  Tool bash (node verify_service.js)
194
- Finding sk-(?!ant-)[A-Za-z0-9_-] (score: 90 / CRITICAL)
192
+ Finding 23: sk-(?!ant-)[A-Za-z0-9_-]
195
193
  Question Should the AI be allowed to see it?
196
194
 
197
195
  > No, keep private (recommended)
198
196
  Yes, allow
199
197
  ```
198
+ - `Finding` prints the **complete** detected line and its line number — one `Finding` row per match when several secrets appear, with no truncation.
200
199
  - Choosing **No, keep private** replaces the token with `***REDACTED***` in the model's context window, preventing credentials from leaking to LLM providers.
201
200
 
202
201
  ---
203
202
 
204
- ## Secure Auto Mode & Cloud Auto Mode
203
+ ## Secure Auto Mode & Claude Auto Mode
205
204
 
206
205
  In modern AI coding tools like Claude Code, developers love **auto mode** because the agent can execute tasks autonomously without constantly prompting for confirmation on trivial, harmless steps.
207
206
 
208
207
  However, unconstrained auto mode is a significant data breach risk. If an autonomous agent reads credentials, runs unvetted scripts, or sends code to third-party endpoints, the entire developer environment can be compromised.
209
208
 
210
- **Cloud Auto Mode for Pi** delivers **Claude-style auto mode with zero-trust security**:
209
+ **Claude Auto Mode for Pi** delivers **Claude-style auto mode with zero-trust security**:
211
210
  - **Autonomous Execution with Real-Time Guardrails:** The agent operates autonomously within approved boundaries, allowing rapid development while guarding sensitive resources.
212
211
  - **Advanced Secure Scanning:** Uses `advanced-secure` mode to scan files, commands, and content to stop data breaches before they can occur.
213
212
  - **Fail-Closed in Headless CI/CD:** When running unattended (`pi -p`, GitHub Actions, Docker, remote devboxes), unapproved operations fail closed immediately with a structured reason instead of hanging or deadlocking the pipeline.
214
213
 
215
- ### Activating Cloud Auto Mode in CI/CD:
214
+ ### Activating Claude Auto Mode in CI/CD:
216
215
 
217
216
  ```bash
218
- # 1. Copy the shipped Cloud Auto Mode template to your repository:
217
+ # 1. Copy the shipped Claude Auto Mode template to your repository:
219
218
  mkdir -p .pi
220
- cp node_modules/auto-permission-system/sandbox.cloud.json .pi/sandbox.json
219
+ cp node_modules/auto-permission-system/sandbox.claude.json .pi/sandbox.json
221
220
 
222
221
  # 2. Run Pi headlessly with complete automated protection:
223
222
  pi -p "Review pull request changes, verify dependencies, and execute test suite"
@@ -240,10 +239,10 @@ jobs:
240
239
  run: |
241
240
  npm install -g @earendil-works/pi-coding-agent
242
241
  pi install npm:auto-permission-system
243
- - name: Activate Cloud Auto Mode Preset
242
+ - name: Activate Claude Auto Mode Preset
244
243
  run: |
245
244
  mkdir -p .pi
246
- cp node_modules/auto-permission-system/sandbox.cloud.json .pi/sandbox.json
245
+ cp node_modules/auto-permission-system/sandbox.claude.json .pi/sandbox.json
247
246
  - name: Run Autonomous Review in Auto Mode
248
247
  run: |
249
248
  pi -p "Verify changes, run npm test, and flag security issues"
@@ -394,11 +393,11 @@ grep '"finding"' ~/.pi/agent/audit.log | tail -n 10
394
393
 
395
394
  A selection of common questions is below. For the complete, detailed guide, read **[docs/FAQ.md](docs/FAQ.md)**.
396
395
 
397
- ### How does Cloud Auto Mode secure unattended runs?
396
+ ### How does Claude Auto Mode secure unattended runs?
398
397
  In headless mode (`pi -p` or subagent executions), interactive ask prompts cannot be presented to a human. Auto Permission System operates in a fail-closed posture: any tool call or bash command targeting an unlisted domain, credential file, or outside-project path is automatically blocked, and the refusal reason is provided to the agent and recorded in the audit log.
399
398
 
400
- ### What is the relationship between Cloud Auto Mode and Claude's auto mode?
401
- Claude Code popularized autonomous "auto mode", allowing developers to let AI code without stopping for confirmation on harmless steps. Cloud Auto Mode brings this exact convenience to the Pi coding agent, but adds essential zero-trust boundaries: using Advanced Secure Mode to scan files, shell commands, and content to guarantee that autonomous execution never leads to data breaches or leaked API tokens.
399
+ ### What is the relationship between Claude Auto Mode and Claude's auto mode?
400
+ Claude Code popularized autonomous "auto mode", allowing developers to let AI code without stopping for confirmation on harmless steps. Claude Auto Mode brings this exact convenience to the Pi coding agent, but adds essential zero-trust boundaries: using Advanced Secure Mode to scan files, shell commands, and content to guarantee that autonomous execution never leads to data breaches or leaked API tokens.
402
401
 
403
402
  ### How do I allow the agent to reach a new domain?
404
403
  Add it to `network.allowedDomains` in `<cwd>/.pi/sandbox.json` or `~/.pi/agent/extensions/sandbox.json`, or run a curl command once and select **Yes, always… → All in group in this project**.
@@ -65,7 +65,7 @@ async function askDetection(
65
65
  if (choice === "session") {
66
66
  detectionGrants.add(key);
67
67
  audit({ layer: 2, tool: kind, subject, reason, decision: "session", note: "advanced-secure", cwd: ctx.cwd });
68
- ctx.ui.notify(`security-guard: allowed for this session (not saved) → ${subject}`, "info");
68
+ ctx.ui.notify(`security-guard: allowed for this conversation (not saved) → ${subject}`, "info");
69
69
  return null;
70
70
  }
71
71
  audit({ layer: 2, tool: kind, subject, reason, decision: "no", note: "advanced-secure", cwd: ctx.cwd });
@@ -288,7 +288,7 @@ export default function (pi: ExtensionAPI) {
288
288
  if (typeof rawPath === "string" && rawPath) return rawPath;
289
289
  if (event.toolName === "bash") {
290
290
  const cmd = (event.input as { command?: string } | undefined)?.command;
291
- return cmd ? `bash: ${cmd}` : "bash";
291
+ return cmd ? `bash (${cmd})` : "bash";
292
292
  }
293
293
  return event.toolName;
294
294
  })();
@@ -301,15 +301,15 @@ export default function (pi: ExtensionAPI) {
301
301
  if (choice === "program-session" && programId) {
302
302
  grantProgramOutputSession(programId);
303
303
  audit({ layer: 2, tool: event.toolName, decision: "session", grant: programId, note: "advanced-secure-output", findings, cwd: ctx.cwd });
304
- ctx.ui.notify(`🛡 Advanced Secure: you allowed all ${programId} output for the rest of this session.`, "info");
304
+ ctx.ui.notify(`🧠 Advanced Secure: you allowed all ${programId} output for the rest of this conversation.`, "info");
305
305
  } else {
306
306
  audit({ layer: 2, tool: event.toolName, decision: "yes", note: "advanced-secure-output", findings, cwd: ctx.cwd });
307
- ctx.ui.notify(`🛡 Advanced Secure: you allowed ${event.toolName} output to be sent.`, "warning");
307
+ ctx.ui.notify(`🧠 Advanced Secure: you allowed ${event.toolName} output to be sent.`, "warning");
308
308
  }
309
309
  return;
310
310
  }
311
311
  audit({ layer: 2, tool: event.toolName, decision: "no", note: "advanced-secure-output", findings, cwd: ctx.cwd });
312
- ctx.ui.notify(`🛡 Advanced Secure: blocked ${event.toolName} output — it may contain sensitive information. Nothing was sent to the model.`, "warning");
312
+ ctx.ui.notify(`🧠 Advanced Secure: blocked ${event.toolName} output — it may contain sensitive information. Nothing was sent to the model.`, "warning");
313
313
  return { content: [{ type: "text", text: withheldNotice(subject) }] };
314
314
  });
315
315
 
@@ -349,7 +349,7 @@ export default function (pi: ExtensionAPI) {
349
349
  }
350
350
  recordProjectTrust(file, TRUST_STORE);
351
351
  audit({ layer: 2, event: "project-trusted", file, applied: changes, cwd: ctx.cwd });
352
- ctx.ui.notify("Done. The file applies now. Start a new session so the bash sandbox uses it too.", "info");
352
+ ctx.ui.notify("Done. The file applies now. Start a new conversation so the bash sandbox uses it too.", "info");
353
353
  return;
354
354
  }
355
355
  if (!active) {
@@ -367,7 +367,7 @@ export default function (pi: ExtensionAPI) {
367
367
  ` cwd: ${ctx.cwd}`,
368
368
  ` hasUI: ${(ctx as { hasUI?: boolean }).hasUI !== false}`,
369
369
  ` subagent.network: ${policy.subagent?.network ?? "allow"}`,
370
- ` session grants: ${sessionGrantSummary() || "(none)"}`,
370
+ ` conversation grants: ${sessionGrantSummary() || "(none)"}`,
371
371
  ` project file: ${!existsSync(projectPolicyPath(ctx.cwd)) ? "none" : projectTrusted(ctx.cwd) ? "trusted" : untrustedProjectChanges(ctx.cwd).length ? `not trusted, its weaker settings are ignored (/security trust)` : "only block rules, they apply"}`,
372
372
  ` outside project: read ${policy.filesystem.outsideProject?.read ?? "allow"}${(policy.filesystem.outsideProject?.allowRead ?? []).length ? `, allowRead ${policy.filesystem.outsideProject?.allowRead?.join(", ")}` : ""}`,
373
373
  "",
@@ -46,7 +46,7 @@ const EXPLANATION = [
46
46
  " No file contents are inspected.",
47
47
  "",
48
48
  "YOLO",
49
- " Turns every layer off. Saved like the other modes, so new sessions",
49
+ " Turns every layer off. Saved like the other modes, so new conversations",
50
50
  " start with it too until you pick Default or Advanced Secure again.",
51
51
  ].join("\n");
52
52
 
@@ -72,8 +72,8 @@ function renderSandboxChip(ctx: ExtensionContext): void {
72
72
  }
73
73
 
74
74
  function modeNotice(mode: PermissionMode): { text: string; level: "info" | "warning" } {
75
- if (mode === "yolo") return { text: "⚠️ YOLO — all security layers disabled. Saved; new sessions start this way too.", level: "warning" };
76
- if (mode === "advanced-secure") return { text: "🛡 Advanced Secure on. File reads, MCP calls, and tool output are checked for secrets.", level: "info" };
75
+ if (mode === "yolo") return { text: "⚠️ YOLO — all security layers disabled. Saved; new conversations start this way too.", level: "warning" };
76
+ if (mode === "advanced-secure") return { text: "🧠 Advanced Secure on. File reads, MCP calls, and tool output are checked for secrets.", level: "info" };
77
77
  return { text: "🛡 Default mode. Rule-based policy only.", level: "info" };
78
78
  }
79
79
 
@@ -92,7 +92,7 @@ async function pickMode(pi: ExtensionAPI, ctx: ExtensionContext, yolo: { active:
92
92
  emitYolo(pi.events, true);
93
93
  audit({ layer: 0, event: "permission-mode", mode: "yolo", cwd: ctx.cwd, note: "runtime-sync" });
94
94
  renderSandboxChip(ctx);
95
- ctx.ui.notify("⚠️ YOLO — all security layers disabled for this session.", "warning");
95
+ ctx.ui.notify("⚠️ YOLO — all security layers disabled for this conversation.", "warning");
96
96
  return;
97
97
  }
98
98
  ctx.ui.notify(`Permission mode is already ${modeLabel(next)}.`, "info");
@@ -218,7 +218,7 @@ export default function (pi: ExtensionAPI) {
218
218
  ` Allow Write: ${config.filesystem?.allowWrite?.join(", ") || "(none)"}`,
219
219
  ` Deny Write: ${config.filesystem?.denyWrite?.join(", ") || "(none)"}`,
220
220
  ` Outside project reads: ${config.filesystem?.outsideProject?.read ?? "allow"}`,
221
- ` Session grants: ${filesystemSessionGrantSummary() || "(none)"}`,
221
+ ` Conversation grants: ${filesystemSessionGrantSummary() || "(none)"}`,
222
222
  ];
223
223
  ctx.ui.notify(lines.join("\n"), "info");
224
224
  },
package/package.json CHANGED
@@ -1,36 +1,34 @@
1
1
  {
2
2
  "name": "auto-permission-system",
3
- "version": "3.9.0",
4
- "description": "Secure auto mode & cloud auto mode for Pi. A zero-trust permission system for the Pi coding agent powered by Advanced Secure mode to scan files, content, and tools to prevent data breaches.",
3
+ "version": "3.9.1",
4
+ "description": "Claude auto mode for Pi & security for Pi. A zero-trust permission system for Pi coding agent powered by Advanced Secure mode to scan files, content, and tools to prevent data breaches.",
5
5
  "type": "module",
6
6
  "author": "Navin Kumar",
7
7
  "keywords": [
8
8
  "pi-package",
9
9
  "pi",
10
10
  "pi-coding-agent",
11
+ "claude-auto-mode-for-pi",
11
12
  "pi-extension",
12
13
  "pi-permission",
13
14
  "pi-permissions",
14
15
  "pi-permission-system",
15
16
  "pi-security",
16
- "cloud-auto-mode-for-pi",
17
17
  "security-for-pi",
18
18
  "permission-system-for-pi",
19
19
  "secure-pi",
20
- "cloud-auto-mode",
21
- "pi-cloud-auto-mode",
20
+ "claude-auto-mode",
21
+ "pi-claude-auto-mode",
22
22
  "auto-permission-system",
23
23
  "auto-secure",
24
24
  "secure",
25
25
  "secure-it",
26
26
  "auto-mode",
27
27
  "safe-auto-mode",
28
- "claude-auto-mode",
29
28
  "data-breach-prevention",
30
29
  "content-scan",
31
- "cloud",
32
- "cloud-security",
33
- "auto-mode",
30
+ "claude",
31
+ "claude-security",
34
32
  "permission",
35
33
  "permissions",
36
34
  "permission-system",
@@ -91,7 +89,7 @@
91
89
  "assets/",
92
90
  "sandbox.example.json",
93
91
  "sandbox.default.json",
94
- "sandbox.cloud.json",
92
+ "sandbox.claude.json",
95
93
  "banner.png",
96
94
  "README.md",
97
95
  "CHANGELOG.md",
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: auto-permission-system
3
- description: Use this skill when the user asks about auto-permission-system, security for Pi, permission system for Pi, how to secure Pi, Cloud auto mode for Pi, security policy configuration, why a tool call was blocked, how to add an allow-list entry, or how to configure sandbox behavior. Covers the two-layer sandbox (OS-level bash sandbox and in-process tool guard), policy file locations and fields, Cloud auto mode preset (sandbox.cloud.json), ask-tier prompt options, absolute-deny paths, escape hatches (--yolo, --no-sandbox, /permission-mode → YOLO), the audit log, and common tasks like allowing a new domain or write path.
3
+ description: Use this skill when the user asks about auto-permission-system, security for Pi, permission system for Pi, how to secure Pi, Claude auto mode for Pi, security policy configuration, why a tool call was blocked, how to add an allow-list entry, or how to configure sandbox behavior. Covers the two-layer sandbox (OS-level bash sandbox and in-process tool guard), policy file locations and fields, Claude auto mode preset (sandbox.claude.json), ask-tier prompt options, absolute-deny paths, escape hatches (--yolo, --no-sandbox, /permission-mode → YOLO), the audit log, and common tasks like allowing a new domain or write path.
4
4
  ---
5
5
 
6
6
  # Security for Pi (Pi Permission System) — Skill
7
7
 
8
- Use this skill when the user asks about the auto-permission-system extension, security for Pi, permission system for Pi, how to secure Pi, or configuring Cloud auto mode for Pi.
8
+ Use this skill when the user asks about the auto-permission-system extension, security for Pi, permission system for Pi, how to secure Pi, or configuring Claude auto mode for Pi.
9
9
 
10
10
  ## What this extension does
11
11
 
@@ -14,19 +14,19 @@ Wraps the `bash` tool with `sandbox-exec` (macOS) or `bubblewrap` (Linux). Block
14
14
  - Filesystem writes outside `allowWrite`
15
15
  - Filesystem reads of `denyRead` paths (defaults include `~/.ssh`, `~/.aws`, `~/.gnupg`, `~/.pi/agent`)
16
16
  - Filesystem reads outside the project when `outsideProject.read` is `"ask"` or `"deny"` — when set, the home dir (and the project's parent) is fenced with `denyRead` and the project plus `allowWrite`/`outsideProject.allowRead` are re-exposed (ADR-014). With `"ask"`, a bash command that *plainly* names an outside path is confirmed with you **before it runs** (ADR-015): allow (`yes — this once`, or a persistent `always` grant) → the real, complete output; deny → the command is not run at all (no silently trimmed output). `install`/`npm` under `~/.pi/agent` are pre-allowed via `outsideProject.allowRead`, so they never prompt. Obfuscated reads still hit the fence and stay masked.
17
- - Network egress to domains not in `allowedDomains`: a host in neither list asks before the proxy decides (ADR-023) — `yes — this once` covers that bash command's connections to the host, `yes — for this session` lasts until the session ends, and `always` writes `overrides.allowDomains` and applies live. A host in `deniedDomains`, or `network.strictAllowlist: true`, is a hard deny that is never ask-able; headless blocks.
17
+ - Network egress to domains not in `allowedDomains`: a host in neither list asks before the proxy decides (ADR-023) — `yes — this once` covers that bash command's connections to the host, `yes — for this conversation` lasts until the conversation ends, and `always` writes `overrides.allowDomains` and applies live. A host in `deniedDomains`, or `network.strictAllowlist: true`, is a hard deny that is never ask-able; headless blocks.
18
18
 
19
19
  **Layer 2 — In-process tool guard**
20
20
  Hooks `tool_call` for `read`, `grep`, `find`, `ls`, `write`, `edit`, `fetch_content`, `web_search`, `get_search_content`. Applies the same policy file as Layer 1. For `grep`, `find` and `ls` the search root is checked like a `read` path. A `tool_result` hook also removes `grep` output lines from denied files beneath an allowed root (for example a `.env` deep in the tree); approving the root does not approve those files. When a call is blocked, shows an interactive prompt with persistence options.
21
21
 
22
- **Layer 3 — Subagent posture & Cloud Auto Mode**
23
- When `ctx.hasUI === false` (subagents, `-p`, JSON mode, CI/CD runs), applies the `subagent.network` policy: `allow` (default) | `deny` | `research-only`. In Cloud Auto Mode, unapproved prompts fail closed.
22
+ **Layer 3 — Subagent posture & Claude Auto Mode**
23
+ When `ctx.hasUI === false` (subagents, `-p`, JSON mode, CI/CD runs), applies the `subagent.network` policy: `allow` (default) | `deny` | `research-only`. In Claude Auto Mode, unapproved prompts fail closed.
24
24
 
25
25
  ## Policy files
26
26
 
27
27
  Merged in order (later wins):
28
28
  1. `sandbox.default.json` — shipped baseline (`outsideProject.read: "ask"`, full `denyRead`/`denyWrite`, `modelDenyRead`)
29
- 2. `sandbox.cloud.json` — shipped Cloud Auto Mode preset for headless, Docker, and CI/CD runs (`strictAllowlist: true`, `outsideProject.read: "deny"`)
29
+ 2. `sandbox.claude.json` — shipped Claude Auto Mode preset for headless, Docker, and CI/CD runs (`strictAllowlist: true`, `outsideProject.read: "deny"`)
30
30
  3. `~/.pi/agent/extensions/sandbox.json` — global user policy
31
31
  4. `<cwd>/.pi/sandbox.json` — project-local overrides (also written by ask-tier prompts)
32
32
 
@@ -87,7 +87,7 @@ A project `.pi/sandbox.json` applies in full only after the user trusted its exa
87
87
  When a normal (non-absolute-deny) call is blocked:
88
88
  - **no — block** — hard deny (default, pre-selected)
89
89
  - **yes — this once** — allow just this call
90
- - **yes — for this session** — this file, its parent folder, or this domain until the session ends; not saved
90
+ - **yes — for this conversation** — this file, its parent folder, or this domain until the conversation ends; not saved
91
91
  - **always for CURRENT project** — whitelist this file in `<cwd>/.pi/sandbox.json`
92
92
  - **always for CURRENT project (folder)** — whitelist the parent directory
93
93
  - **always for ALL projects** — whitelist in `~/.pi/agent/extensions/sandbox.json`
@@ -99,7 +99,7 @@ For a Layer 1 bash **write** blocked after the command already ran, `yes — thi
99
99
 
100
100
  Every prompt waits **15 seconds** by default and shows it live as `Default: … (Ns)` under the body. For file reads/writes no answer means the safe default: **block / deny**, and Esc blocks. For a network ask, an unanswered countdown means **allow once** (Esc still denies) — ADR-024. In the TUI the `Default:` line disappears as soon as you press ↑/↓ and the timer restarts, so the hint never competes with an active choice (ADR-031). The prompt is keyboard-only (ADR-036): ↑/↓ move the highlight, Enter selects, Esc cancels — a mouse click or wheel over the prompt does nothing.
101
101
 
102
- The same options apply to a bash command whose network request reaches a host outside `allowedDomains` (ADR-023), with the countdown default and live footer from ADR-024/ADR-031: `No` / `Yes, just this once` / `Yes, all in group for this session` / `Yes, always…`, and an untouched countdown counts as allow-once. Because the sandbox proxy asks per connection, `Allow (default)` covers that bash command rather than one connection — redirects and parallel connections to the same host do not re-prompt. A host in `deniedDomains`, or `network.strictAllowlist: true`, is blocked outright: the prompt never appears. A remembered host is written to `overrides.allowDomains` and takes effect immediately, so the waiting request completes without a retry. Prompts from both layers are serialized through one queue (`src/ui/ask-flow.ts`), so two unknown hosts cannot replace each other's selector and hang the waiting connection.
102
+ The same options apply to a bash command whose network request reaches a host outside `allowedDomains` (ADR-023), with the countdown default and live footer from ADR-024/ADR-031: `No` / `Yes, just this once` / `Yes, all in group for this conversation` / `Yes, always…`, and an untouched countdown counts as allow-once. Because the sandbox proxy asks per connection, `Allow (default)` covers that bash command rather than one connection — redirects and parallel connections to the same host do not re-prompt. A host in `deniedDomains`, or `network.strictAllowlist: true`, is blocked outright: the prompt never appears. A remembered host is written to `overrides.allowDomains` and takes effect immediately, so the waiting request completes without a retry. Prompts from both layers are serialized through one queue (`src/ui/ask-flow.ts`), so two unknown hosts cannot replace each other's selector and hang the waiting connection.
103
103
 
104
104
  ## Sensitive reads ask
105
105
 
@@ -113,7 +113,7 @@ The same options apply to a bash command whose network request reaches a host ou
113
113
  - **`advanced-secure`** (recommended) — adds secret/credential detection, and **only adds asks or blocks** (never loosens):
114
114
  - file reads whose name looks like a credential store ask before the read;
115
115
  - risky `mcp__<server>__<tool>` calls ask before they run. The gate ignores the `mcp__<server>__` namespace, takes the operation from the head verb of the name (or the first verb of the description), and asks for destructive names, mutating names (`update_*`, `create_*`, `run_*`, `evaluate_*`), and destructive arguments (SQL `DELETE`/`UPDATE` without `WHERE`, `rm -rf`, `*` targets, HTTP `DELETE`, production targets). A documented tool with an unclassified name (`context`, `capture`, `logs`) does not ask; a tool with **no description** counts as opaque and does. Tune it under `mcp` in `sandbox.json` (ADR-032): `allowTools` / `allowPrefixes` skip asks, `askTools` forces them, `trustAnnotations` lets a server's `readOnlyHint` allow the call, `askThreshold` moves the score line (default 30). An untrusted project file may only tighten it (add `askTools`, lower `askThreshold`, turn `allowSimpleUpdates`/`trustAnnotations` off).
116
- - before any tool/command/file output reaches the model, a secret-like hit shows the file and each detected line as `lineNo: text`, with two choices — **No, keep private** or **Yes, allow**. No, keep private withholds the output and tells the model it was withheld because it may contain sensitive information (not a failure, not an empty result).
116
+ - before any tool/command/file output reaches the model, a secret-like hit shows the tool and each detected line as `Finding <lineNo>: <complete line>` — one `Finding` row per match, no truncation — plus `Question Should the AI be allowed to see it?`, with two choices — **No, keep private** or **Yes, allow**. No, keep private withholds the output and tells the model it was withheld because it may contain sensitive information (not a failure, not an empty result).
117
117
 
118
118
  Switch with `/permission-mode`; all three modes are saved to the global `sandbox.json` and reflected in the footer's sandbox chip (`Sandbox: 🧠 N domains, M paths` for advanced-secure, `Sandbox: 🔒 …` for default). An untrusted project may move the mode **up** (toward stricter), never down: it cannot turn on YOLO or turn off Advanced Secure. The third choice, **YOLO**, disables every layer; the chip reads `⚠️ YOLO — all security layers disabled` until you pick another mode (ADR-020).
119
119
 
package/src/core/trust.ts CHANGED
@@ -67,7 +67,7 @@ export function isProjectFileDeclined(projectPath: string, storePath: string): b
67
67
  * and share only the store file (ADR-018/ADR-020).
68
68
  */
69
69
  export interface ProjectTrust {
70
- /** Record pi's own project-trust answer for this session. Declined is never trusted. */
70
+ /** Record pi's own project-trust answer for this conversation. Declined is never trusted. */
71
71
  setDeclined(value: boolean): void;
72
72
  /** The project file applies in full only when its current hash is recorded and pi did not decline. */
73
73
  isTrusted(cwd: string): boolean;
@@ -40,7 +40,7 @@ function linkRow(absPath: string, spelled: string | undefined): string[] {
40
40
  const auditL1 = (entry: Record<string, unknown>) => audit({ layer: 1, tool: "bash", ...entry });
41
41
 
42
42
  /**
43
- * In-memory filesystem grants from "Yes, for this session" (ADR-010, ADR-012).
43
+ * In-memory filesystem grants from "Yes, for this conversation" (ADR-010, ADR-012).
44
44
  * A read grant is the absolute path; a write grant is the parent directory (a
45
45
  * whole path would not let a new file be created — ADR-021), falling back to the
46
46
  * exact path when the parent is an unsafe folder to grant (root, home, …).
@@ -48,7 +48,7 @@ const auditL1 = (entry: Record<string, unknown>) => audit({ layer: 1, tool: "bas
48
48
  */
49
49
  const sessionReadGrants: string[] = [];
50
50
  const sessionWriteGrants: string[] = [];
51
- /** askRead (.env) session grants, kept apart so they never leak into allowRead (ADR-019). */
51
+ /** askRead (.env) conversation grants, kept apart so they never leak into allowRead (ADR-019). */
52
52
  const sessionAskReadGrants: string[] = [];
53
53
 
54
54
  export function clearFilesystemSessionGrants(): void {
@@ -158,7 +158,7 @@ async function preflightOutsideReads(
158
158
  if (choice === MainChoice.Session) {
159
159
  sessionReadGrants.push(absPath);
160
160
  auditL1({ subject: absPath, decision: "session", scope: "session", reason: "outside-project-read", note: "preflight", cwd });
161
- opts.ctx.ui?.notify?.(`pi-sandbox: allowed read of ${absPath} for this session (not saved)`, "info");
161
+ opts.ctx.ui?.notify?.(`pi-sandbox: allowed read of ${absPath} for this conversation (not saved)`, "info");
162
162
  continue;
163
163
  }
164
164
  if (choice !== MainChoice.Remember || !grant) {
@@ -366,8 +366,8 @@ export function createSandboxedBashOps(opts?: SandboxedBashOpts): BashOperations
366
366
  if (choice === MainChoice.Once) readOnce.push(absPath);
367
367
  else sessionReadGrants.push(absPath);
368
368
  auditL1({ subject: absPath, decision: choice, scope: choice === MainChoice.Once ? "invocation" : "session", cwd: opts.ctx.cwd, note: "outside-project-read" });
369
- opts.ctx.ui?.notify?.(`pi-sandbox: allowed read of ${absPath} ${choice === MainChoice.Once ? "once" : "for this session"} — re-running the command`, "warning");
370
- onData(Buffer.from(`\n✅ pi-sandbox: read of ${absPath} allowed ${choice === MainChoice.Once ? "once" : "for this session"}. Command output with the new permission follows.\n`));
369
+ opts.ctx.ui?.notify?.(`pi-sandbox: allowed read of ${absPath} ${choice === MainChoice.Once ? "once" : "for this conversation"} — re-running the command`, "warning");
370
+ onData(Buffer.from(`\n✅ pi-sandbox: read of ${absPath} allowed ${choice === MainChoice.Once ? "once" : "for this conversation"}. Command output with the new permission follows.\n`));
371
371
  resolve(attempt([]));
372
372
  return;
373
373
  }
@@ -441,8 +441,8 @@ export function createSandboxedBashOps(opts?: SandboxedBashOpts): BashOperations
441
441
  sessionWriteGrants.push(value);
442
442
  (value === parentDir ? grantedWriteFolders : grantedWriteFiles).add(value);
443
443
  auditL1({ subject: value, granularity: value === parentDir ? "folder" : "file", original: absPath, decision: "session", scope: "session", cwd: opts.ctx.cwd, note: "write-session" });
444
- opts.ctx.ui?.notify?.(`pi-sandbox: allowed ${value} for this session (not saved) — re-running the command`, "info");
445
- onData(Buffer.from(`\n✅ pi-sandbox: write to ${value} allowed for this session. Command output with the new permission follows.\n`));
444
+ opts.ctx.ui?.notify?.(`pi-sandbox: allowed ${value} for this conversation (not saved) — re-running the command`, "info");
445
+ onData(Buffer.from(`\n✅ pi-sandbox: write to ${value} allowed for this conversation. Command output with the new permission follows.\n`));
446
446
  resolve(attempt([]));
447
447
  return;
448
448
  }
@@ -4,7 +4,7 @@
4
4
  * Uses the same two-screen ask-tier as Layer 2 (once / session / remember
5
5
  * project|global) and the same grant semantics:
6
6
  *
7
- * - session grants: in memory only, cleared at `session_start`, never saved;
7
+ * - conversation grants: in memory only, cleared at `session_start`, never saved;
8
8
  * - "once" grants: one bash command's connections to that host (the proxy
9
9
  * calls back per request, so a redirect or a parallel connection must not
10
10
  * re-prompt), scoped by `beginNetworkCommand`/`endNetworkCommand`;
@@ -14,7 +14,7 @@
14
14
  *
15
15
  * Headless (`hasUI === false`) denies, like every other gate.
16
16
  *
17
- * Session grants use the parent-domain wildcard when useful (covers all sibling
17
+ * Conversation grants use the parent-domain wildcard when useful (covers all sibling
18
18
  * subdomains). Screen 2 hides the "in this project" rows in untrusted
19
19
  * projects so the user can't quietly trust a widening file.
20
20
  */
@@ -22,7 +22,7 @@
22
22
  import { domainMatches, GrantScope, OverrideKind } from "../core/index";
23
23
  import { askMainWithRemember, askRememberHost, ASK_TIMEOUT_MS, MainChoice, parentDomainWildcard, type AskCtx, type HostGrant } from "../ui/ask-flow";
24
24
 
25
- /** In-memory session grants ("Yes, for this session"). Cleared at session_start. */
25
+ /** In-memory conversation grants ("Yes, for this conversation"). Cleared at session_start. */
26
26
  const sessionGrants: string[] = [];
27
27
  /** Hosts allowed for the current bash command ("Yes, just this once" covers its connections). */
28
28
  let commandGrants: Set<string> | null = null;
@@ -87,7 +87,7 @@ function firstWord(command: string | undefined): string | null {
87
87
 
88
88
  /**
89
89
  * Screen 1 (once / session / remember) plus, for remember, screen 2 (scope and
90
- * pattern). A session grant uses the parent-domain wildcard when the host has
90
+ * pattern). A conversation grant uses the parent-domain wildcard when the host has
91
91
  * a useful parent, so any sibling subdomain is covered for the rest of the
92
92
  * session; apex hosts (2 parts) fall back to the exact host because
93
93
  * `*.example.com` would not match `example.com`.
@@ -124,7 +124,7 @@ async function decide(deps: NetworkAskDeps, host: string, port: number | undefin
124
124
  bodyParts.join("\n"),
125
125
  {
126
126
  session: true,
127
- sessionLabel: "Yes, all in group for this session",
127
+ sessionLabel: "Yes, all in group for this conversation",
128
128
  allowFirst: true,
129
129
  icon: "net",
130
130
  timeoutMs: ASK_TIMEOUT_MS,
@@ -161,7 +161,7 @@ async function decide(deps: NetworkAskDeps, host: string, port: number | undefin
161
161
  if (mainChoice === MainChoice.Session) {
162
162
  sessionGrants.push(wildcard);
163
163
  deps.audit({ ...base, decision: "session", grant: wildcard, requested: host });
164
- ctx.ui.notify?.(`pi-sandbox: allowed for this session (not saved) → ${wildcard}`, "info");
164
+ ctx.ui.notify?.(`pi-sandbox: allowed for this conversation (not saved) → ${wildcard}`, "info");
165
165
  return true;
166
166
  }
167
167
 
@@ -5,15 +5,10 @@ import { scanToolOutput } from "../detect/index";
5
5
  export interface Exposure {
6
6
  /** Finding types, e.g. ["SECRET_ASSIGNMENT"]. Empty means clean. */
7
7
  types: string[];
8
- /** One block: `subject` then `lineNo: text` per detected line (max 8). */
8
+ /** One block: `Tool` header then `Finding <lineNo>: <line>` per detected line. */
9
9
  hits: string[];
10
10
  }
11
11
 
12
- const MAX_LINES = 8;
13
- const MAX_LINE_CHARS = 200;
14
-
15
- const clip = (s: string) => (s.length > MAX_LINE_CHARS ? `${s.slice(0, MAX_LINE_CHARS)}…` : s);
16
-
17
12
  export function collectExposure(content: ReadonlyArray<{ type: string; text?: string }>, subject: string): Exposure {
18
13
  const types = new Set<string>();
19
14
  const lines: string[] = [];
@@ -29,14 +24,9 @@ export function collectExposure(content: ReadonlyArray<{ type: string; text?: st
29
24
  if (after[i] !== before[i] && before[i].trim()) matchedIndices.push(i);
30
25
  }
31
26
  if (matchedIndices.length === 0) continue;
32
- const shown = matchedIndices.slice(0, MAX_LINES);
33
- const firstIdx = shown[0];
34
- const lastIdx = shown[shown.length - 1];
35
- const moreMatched = matchedIndices.length > MAX_LINES;
36
- const moreBelow = lastIdx < before.length - 1;
37
- if (firstIdx > 0) lines.push(" \u2026");
38
- for (const idx of shown) lines.push(`${idx + 1}: ${clip(before[idx].trim())}`);
39
- if (moreMatched || moreBelow) lines.push(" \u2026");
27
+ lines.push(`Tool ${subject}`);
28
+ // One `Finding` row per detected line, showing the complete line (no clipping).
29
+ for (const idx of matchedIndices) lines.push(`Finding ${idx + 1}: ${before[idx].trim()}`);
40
30
  }
41
- return { types: [...types], hits: lines.length ? [`From ${subject}\n${lines.join("\n")}`] : [] };
31
+ return { types: [...types], hits: lines.length ? [lines.join("\n")] : [] };
42
32
  }
@@ -1,11 +1,11 @@
1
1
  /**
2
- * In-memory session grants for the Advanced Secure output gate (ADR-018).
2
+ * In-memory conversation grants for the Advanced Secure output gate (ADR-018).
3
3
  *
4
4
  * When a CLI like `composio` returns output that matches a secret pattern
5
5
  * (JWT, OAuth refresh token, etc.), the user can pick "Yes, all <program>
6
- * output for this session" instead of approving every block. The program id
6
+ * output for this conversation" instead of approving every block. The program id
7
7
  * is added here, and the output gate short-circuits for the rest of the
8
- * session — same shape as path/host session grants, but for tool output.
8
+ * conversation — same shape as path/host conversation grants, but for tool output.
9
9
  *
10
10
  * Cleared at session_start (ADR-010), never written to disk. The credential
11
11
  * tier (denyRead / askRead) still wins on the file side; this only loosens
@@ -19,12 +19,12 @@ export function clearProgramOutputSessionGrants(): void {
19
19
  programOutputSessionGrants.clear();
20
20
  }
21
21
 
22
- /** Add a program to the session grant list. Idempotent. */
22
+ /** Add a program to the conversation grant list. Idempotent. */
23
23
  export function grantProgramOutputSession(programId: string): void {
24
24
  programOutputSessionGrants.add(programId.toLowerCase());
25
25
  }
26
26
 
27
- /** True when the program is already granted for the rest of the session. */
27
+ /** True when the program is already granted for the rest of the conversation. */
28
28
  export function isProgramOutputGranted(programId: string | undefined): boolean {
29
29
  if (!programId) return false;
30
30
  return programOutputSessionGrants.has(programId.toLowerCase());
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * Program-id detection for the Advanced Secure output gate.
3
3
  *
4
- * Used to label the "This output may contain a secret" prompt with the binary
4
+ * Used to label the Advanced Secure output prompt with the binary
5
5
  * that produced it (`composio`, `aws`, `gcloud`, …) and to key the per-program
6
- * session grant (`isProgramOutputGranted`). Pure functions, no pi / fs / audit.
6
+ * conversation grant (`isProgramOutputGranted`). Pure functions, no pi / fs / audit.
7
7
  */
8
8
 
9
9
  /** Strip env assignments (`FOO=bar`), common wrappers (sudo, nice, command, …), and the
@@ -27,7 +27,7 @@ export function bashBinary(command: string | undefined): string | undefined {
27
27
 
28
28
  /** The program id for a tool call. Bash binaries only — MCP and other tools return
29
29
  * `undefined` so the Advanced Secure output gate doesn't offer them a per-program
30
- * session grant (MCP tools handle their own secrets and don't share the
30
+ * conversation grant (MCP tools handle their own secrets and don't share the
31
31
  * composio-style "same binary, same surface" pattern). */
32
32
  export function programIdForToolCall(toolName: string | undefined, input: unknown): string | undefined {
33
33
  if (!toolName) return undefined;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Layer 2 ask-tier prompts, session grants, and persisted "always" overrides
2
+ * Layer 2 ask-tier prompts, conversation grants, and persisted "always" overrides
3
3
  * (ADR-009, ADR-010). Owns the interactive UI calls; policy decisions come from
4
4
  * matching.ts.
5
5
  */
@@ -32,7 +32,7 @@ export function persistOverride(scope: GrantScope, cwd: string, kind: OverrideKi
32
32
  }
33
33
 
34
34
  /**
35
- * Grants from "yes — for this session" (ADR-010). In memory only, cleared on
35
+ * Grants from "yes — for this conversation" (ADR-010). In memory only, cleared on
36
36
  * session_start, never written to sandbox.json, never consulted for the
37
37
  * absolute-deny tier.
38
38
  */
@@ -67,7 +67,7 @@ export type UICtx = {
67
67
  };
68
68
 
69
69
  /**
70
- * Ask the user, then apply the answer: session grants stay in memory, "always"
70
+ * Ask the user, then apply the answer: conversation grants stay in memory, "always"
71
71
  * grants are persisted and announced to the other layer via `onPersist`.
72
72
  * Headless asks deny. A `null` return means the call may proceed.
73
73
  */
@@ -102,7 +102,7 @@ export async function askOrBlock(
102
102
  const value = ask.overrideKind === OverrideKind.AllowDomains || isSafeFolderGrant(parent, homedir()) ? parent : ask.overrideValue;
103
103
  sessionGrants.push({ kind: ask.overrideKind, value });
104
104
  audit({ layer: 2, tool: ask.tool, subject: ask.subject, reason: ask.reason, decision, grant: value, cwd: ctx.cwd });
105
- ctx.ui.notify(`security-guard: allowed for this session (not saved) → ${value}`, "info");
105
+ ctx.ui.notify(`security-guard: allowed for this conversation (not saved) → ${value}`, "info");
106
106
  return null;
107
107
  }
108
108
  if (decision === Decision.AlwaysCwd || decision === Decision.AlwaysGlobal || decision === Decision.AlwaysCwdFolder || decision === Decision.AlwaysGlobalFolder) {
@@ -3,7 +3,7 @@
3
3
  * it only calls the caller's `ctx.ui.select`, so the contract tests can script it.
4
4
  *
5
5
  * Every prompt is a question. Answers are uniformly `No` / `Yes, just this once` /
6
- * `Yes, for this session` / `Yes, always…` so row 2 is always "once". Screen 2
6
+ * `Yes, for this conversation` / `Yes, always…` so row 2 is always "once". Screen 2
7
7
  * collapses to "All in folder · in this project" / "Only <name> · in this project" /
8
8
  * "All in folder · in all projects" / "Only <name> · in all projects" — folder /
9
9
  * wildcard is preselected (the wider grant is the right answer almost every time).
@@ -294,10 +294,10 @@ function withIcon(icon: string | undefined, header: string): string {
294
294
  export type MainOpts = {
295
295
  /** Offer `Yes, just this once` (default on). */
296
296
  once?: boolean;
297
- /** Offer `Yes, for this session` (opt-in). */
297
+ /** Offer `Yes, for this conversation` (opt-in). */
298
298
  session?: boolean;
299
- /** Override the session label so the caller can name what the session grant covers
300
- * (`Yes, for this session` for files, `Yes, all in group for this session` for networks). */
299
+ /** Override the session label so the caller can name what the conversation grant covers
300
+ * (`Yes, for this conversation` for files, `Yes, all in group for this conversation` for networks). */
301
301
  sessionLabel?: string;
302
302
  /** Offer `Yes, always…` (default on). */
303
303
  remember?: boolean;
@@ -322,7 +322,7 @@ export type MainOpts = {
322
322
  * highlighted.
323
323
  */
324
324
  export async function askMain(ctx: AskCtx, header: string, body: string, opts: MainOpts = {}): Promise<MainChoice> {
325
- const sessionLabel = opts.sessionLabel ?? "Yes, for this session";
325
+ const sessionLabel = opts.sessionLabel ?? "Yes, for this conversation";
326
326
  const choices: Array<{ label: string; value: MainChoice }> = [];
327
327
  choices.push({ label: "No", value: MainChoice.Block });
328
328
  if (opts.once !== false) choices.push({ label: "Yes, just this once", value: MainChoice.Once });
@@ -444,19 +444,21 @@ export type ExposureChoice = (typeof ExposureChoice)[keyof typeof ExposureChoice
444
444
 
445
445
  /** Advanced Secure output gate (ADR-018): show where it was found and ask before the output reaches the model.
446
446
  * When `programId` is set, an extra option lets the user grant the rest of the
447
- * session for that program — same pattern as path/host session grants, but for
447
+ * conversation for that program — same pattern as path/host conversation grants, but for
448
448
  * tool output (display, not access). */
449
449
  export async function askExposure(ctx: AskCtx, hits: string[], programId?: string): Promise<ExposureChoice> {
450
450
  const choices: Array<{ label: string; value: ExposureChoice }> = [
451
451
  { label: "No, keep it hidden (recommended)", value: ExposureChoice.Block },
452
452
  { label: "Yes, show it this once", value: ExposureChoice.Allow },
453
453
  ];
454
- if (programId) choices.push({ label: `Yes, all ${programId} output for this session`, value: ExposureChoice.ProgramSession });
454
+ if (programId) choices.push({ label: `Yes, all ${programId} output for this conversation`, value: ExposureChoice.ProgramSession });
455
455
  const titleParts = [
456
- `${ICON.cred} This output may contain a secret \u2014 show it to the AI?`,
456
+ `${ICON.warn} Sensitive information detected`,
457
457
  "",
458
458
  hits.join("\n\n"),
459
459
  "",
460
+ "Question Should the AI be allowed to see it?",
461
+ "",
460
462
  "Note you'll be asked again each time a secret shows up",
461
463
  ];
462
464
  const { picked } = await askSelect(ctx, titleParts.join("\n"), choices.map((c) => c.label), undefined, { footer: "Default: No" });
@@ -13,7 +13,7 @@
13
13
  *
14
14
  * → No
15
15
  * Yes, just this once
16
- * Yes, all in group for this session
16
+ * Yes, all in group for this conversation
17
17
  * Yes, always…
18
18
  *
19
19
  * Keyboard only (ADR-036): ↑/↓ move the highlight, Enter selects, Esc cancels.
package/src/ui/ask.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  * ADR-030.
5
5
  *
6
6
  * Every prompt is a question. Answers are uniformly `No` / `Yes, just this once` /
7
- * `Yes, for this session` / `Yes, always…`. Body uses the canonical field set
7
+ * `Yes, for this conversation` / `Yes, always…`. Body uses the canonical field set
8
8
  * File / Folder / Site / Group / Command / Why / Risk / Note. Absolute-deny
9
9
  * (credentials) collapses to ONE screen with `No` preselected. Untrusted
10
10
  * project: screen 2 hides the `in this project` rows and adds a Note.
@@ -192,7 +192,7 @@ export async function askDecision(ctx: AskCtx, ask: AskKind, absoluteDenyPattern
192
192
  }
193
193
 
194
194
  const header = headerFor(action, ask.subject, ask);
195
- const sessionLabel = action === AskAction.Network ? "Yes, all in group for this session" : "Yes, for this session";
195
+ const sessionLabel = action === AskAction.Network ? "Yes, all in group for this conversation" : "Yes, for this conversation";
196
196
  const untrusted = ask.projectTrusted === false;
197
197
  const mainOpts = {
198
198
  session: true,
File without changes