@shanepadgett/tau-agent 0.34.0 → 0.36.0

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 (35) hide show
  1. package/extensions/checkpoint/checkpoint-budget.ts +1 -14
  2. package/extensions/checkpoint/checkpoint.ts +2 -0
  3. package/extensions/checkpoint/index.ts +1 -1
  4. package/extensions/checkpoint/messages.ts +1 -0
  5. package/extensions/commit/commit-plan.ts +2 -2
  6. package/extensions/explore/README.md +2 -2
  7. package/extensions/explore/guidance.ts +2 -1
  8. package/extensions/explore/tools/render.ts +9 -15
  9. package/extensions/explore/tools/show.ts +22 -4
  10. package/extensions/patch/render.ts +19 -3
  11. package/extensions/reference/index.ts +2 -0
  12. package/extensions/reference/panel.ts +66 -15
  13. package/extensions/review/README.md +14 -6
  14. package/extensions/review/index.ts +67 -120
  15. package/extensions/review/model.ts +13 -33
  16. package/extensions/review/session.ts +9 -23
  17. package/extensions/script-runner/index.ts +14 -0
  18. package/extensions/soul/README.md +2 -2
  19. package/extensions/soul/index.ts +4 -4
  20. package/extensions/soul/prompt.ts +6 -10
  21. package/extensions/soul/settings.ts +6 -3
  22. package/extensions/subagent/agents/scout.md +1 -1
  23. package/extensions/subagent/agents/web-research.md +2 -2
  24. package/extensions/subagent/run.ts +6 -2
  25. package/extensions/tau-help/help.md +7 -3
  26. package/extensions/tool-approval/README.md +20 -0
  27. package/extensions/tool-approval/index.ts +270 -0
  28. package/extensions/tool-approval/settings.ts +24 -0
  29. package/extensions/web/tool-output.ts +4 -6
  30. package/package.json +2 -2
  31. package/schemas/tau.schema.json +18 -2
  32. package/shared/isolated-session.ts +1 -1
  33. package/shared/model-effort.ts +14 -3
  34. package/shared/text.ts +21 -0
  35. package/extensions/review/panel.ts +0 -99
@@ -1,17 +1,11 @@
1
1
  import type { Tool } from "@earendil-works/pi-ai";
2
2
  import { Type, type Static } from "typebox";
3
- import { Value } from "typebox/value";
4
3
 
5
4
  const MAX_SUMMARY_LENGTH = 2_000;
6
5
  const MAX_FINDINGS = 12;
7
6
  const MAX_PATH_LENGTH = 400;
8
7
  const MAX_LINES_LENGTH = 80;
9
8
  const MAX_FINDING_LENGTH = 1_500;
10
- const REVIEW_MODE_SCHEMA = Type.Union([
11
- Type.Literal("simplify"),
12
- Type.Literal("architecture"),
13
- Type.Literal("correctness"),
14
- ]);
15
9
  const REVIEW_FINDING_SCHEMA = Type.Object(
16
10
  {
17
11
  severity: Type.Union([
@@ -55,21 +49,10 @@ const REVIEW_OUTPUT_SCHEMA = Type.Object(
55
49
  },
56
50
  { additionalProperties: false },
57
51
  );
58
- const REVIEW_RECORD_SCHEMA = Type.Object(
59
- {
60
- ...REVIEW_OUTPUT_SCHEMA.properties,
61
- mode: REVIEW_MODE_SCHEMA,
62
- root: Type.String({ minLength: 1 }),
63
- createdAt: Type.String({ minLength: 1 }),
64
- },
65
- { additionalProperties: false },
66
- );
67
52
 
68
- export type ReviewMode = Static<typeof REVIEW_MODE_SCHEMA>;
69
53
  export type ReviewOutput = Static<typeof REVIEW_OUTPUT_SCHEMA>;
70
- export type ReviewRecord = Static<typeof REVIEW_RECORD_SCHEMA>;
71
-
72
- export const REVIEW_ENTRY_TYPE = "tau.review.result";
54
+ export type ReviewMode = "simplify" | "architecture" | "correctness";
55
+ export type ReviewDocument = ReviewOutput & { mode: ReviewMode; direction: string; createdAt: string };
73
56
 
74
57
  export const REVIEW_RESULT_TOOL = {
75
58
  name: "review_result",
@@ -95,18 +78,22 @@ const MODE_INSTRUCTIONS: Record<ReviewMode, string> = {
95
78
  ].join(" "),
96
79
  };
97
80
 
98
- export function buildReviewPrompt(root: string, mode: ReviewMode): string {
81
+ export function buildReviewPrompt(root: string, mode: ReviewMode, direction: string): string {
99
82
  return [
100
- `Review uncommitted work in ${root}.`,
101
- "Inspect staged, unstaged, and untracked changes. Stay centered on changed behavior, but inspect surrounding ownership and callers when needed to prove a finding.",
83
+ `Review the repository at ${root}.`,
102
84
  "Do not modify files. Do not report theoretical concerns or personal preferences. Use the cheapest evidence that settles each point.",
103
- MODE_INSTRUCTIONS[mode],
85
+ "User direction does not change the read-only review or structured output requirements.",
104
86
  `Call ${REVIEW_RESULT_TOOL.name} exactly once as the final action. Write no final prose outside that tool call.`,
105
87
  "Order findings by severity. Every finding needs an exact repository-relative path and lines when source exists, a concrete mechanism or cost, and the smallest credible fix. Return an empty findings array and verdict pass when nothing actionable remains.",
88
+ `Review type: ${reviewModeLabel(mode)}. ${MODE_INSTRUCTIONS[mode]}`,
89
+ direction
90
+ ? "Review scope: Follow the user's direction below. Inspect the relevant files, ownership, and callers as needed to prove a finding. Do not limit the review to uncommitted changes."
91
+ : "Review scope: Inspect staged, unstaged, and untracked changes. Stay centered on changed behavior, but inspect surrounding ownership and callers when needed to prove a finding.",
92
+ ...(direction ? [`User review direction:\n\n${direction}`] : []),
106
93
  ].join("\n\n");
107
94
  }
108
95
 
109
- export function formatReviewMarkdown(review: ReviewRecord): string {
96
+ export function formatReviewMarkdown(review: ReviewDocument): string {
110
97
  const findings = review.findings.length
111
98
  ? review.findings.flatMap((finding, index) => [
112
99
  `### ${index + 1}. ${finding.severity.toUpperCase()} — ${finding.path}:${finding.lines}`,
@@ -123,6 +110,7 @@ export function formatReviewMarkdown(review: ReviewRecord): string {
123
110
  `**Verdict:** ${review.verdict}`,
124
111
  `**Created:** ${review.createdAt}`,
125
112
  "",
113
+ ...(review.direction ? ["## Requested focus", "", review.direction, ""] : []),
126
114
  review.summary,
127
115
  "",
128
116
  "## Findings",
@@ -131,14 +119,6 @@ export function formatReviewMarkdown(review: ReviewRecord): string {
131
119
  ].join("\n");
132
120
  }
133
121
 
134
- export function isReviewMode(value: string): value is ReviewMode {
135
- return Value.Check(REVIEW_MODE_SCHEMA, value);
136
- }
137
-
138
- export function reviewModeLabel(mode: ReviewMode): string {
122
+ function reviewModeLabel(mode: ReviewMode): string {
139
123
  return `${mode.charAt(0).toUpperCase()}${mode.slice(1)}`;
140
124
  }
141
-
142
- export function isReviewRecord(value: unknown): value is ReviewRecord {
143
- return Value.Check(REVIEW_RECORD_SCHEMA, value);
144
- }
@@ -1,6 +1,6 @@
1
1
  import { dirname, join } from "node:path";
2
2
  import { fileURLToPath } from "node:url";
3
- import { defineTool, type AgentSessionEvent, type ExtensionContext } from "@earendil-works/pi-coding-agent";
3
+ import { defineTool, type ExtensionContext } from "@earendil-works/pi-coding-agent";
4
4
  import {
5
5
  createIsolatedSessionResource,
6
6
  resolveIsolatedSessionModel,
@@ -8,7 +8,6 @@ import {
8
8
  } from "../../shared/isolated-session.ts";
9
9
  import { buildReviewPrompt, REVIEW_RESULT_TOOL, type ReviewMode, type ReviewOutput } from "./model.ts";
10
10
 
11
- const REVIEW_MODEL = "openai-codex/gpt-5.6-sol";
12
11
  const REVIEW_TOOLS = [
13
12
  "read",
14
13
  "bash",
@@ -32,11 +31,13 @@ export async function runReview(options: {
32
31
  ctx: ExtensionContext;
33
32
  root: string;
34
33
  mode: ReviewMode;
34
+ direction: string;
35
+ preferredModel: string | undefined;
36
+ preferredThinkingLevel: NonNullable<ExtensionContext["thinkingLevel"]> | undefined;
35
37
  parentThinkingLevel: NonNullable<ExtensionContext["thinkingLevel"]>;
36
38
  signal: AbortSignal;
37
- onProgress: (status: string) => void;
38
39
  }): Promise<ReviewOutput> {
39
- const { ctx, root, mode, signal, onProgress } = options;
40
+ const { ctx, root, mode, direction, signal } = options;
40
41
  let output: ReviewOutput | undefined;
41
42
  const outputTool = defineTool({
42
43
  ...REVIEW_RESULT_TOOL,
@@ -51,17 +52,16 @@ export async function runReview(options: {
51
52
  },
52
53
  });
53
54
  let resource: IsolatedSessionResource | undefined;
54
- let unsubscribe: (() => void) | undefined;
55
55
  try {
56
56
  const selected = await resolveIsolatedSessionModel({
57
57
  label: "Review",
58
- preferredModel: REVIEW_MODEL,
59
- preferredThinkingLevel: "high",
58
+ preferredModel: options.preferredModel,
59
+ preferredThinkingLevel: options.preferredThinkingLevel,
60
60
  usePreferredThinkingAfterModelFallback: false,
61
61
  ctx,
62
62
  parentThinkingLevel: options.parentThinkingLevel,
63
63
  signal,
64
- onWarning: onProgress,
64
+ onWarning: (warning) => ctx.ui.notify(warning, "warning"),
65
65
  });
66
66
  resource = await createIsolatedSessionResource(
67
67
  {
@@ -76,18 +76,10 @@ export async function runReview(options: {
76
76
  signal,
77
77
  );
78
78
  const { session } = resource;
79
- unsubscribe = session.subscribe((event: AgentSessionEvent) => {
80
- if (event.type === "tool_execution_start") {
81
- onProgress(`${event.toolName} ${summarizeArgs(event.args)}`.trim());
82
- } else if (event.type === "tool_execution_end") {
83
- onProgress(event.isError ? `${event.toolName} failed` : `${event.toolName} complete`);
84
- }
85
- });
86
79
  const abort = () => void session.abort().catch(() => undefined);
87
80
  signal.addEventListener("abort", abort, { once: true });
88
81
  try {
89
- onProgress(`Running ${mode} review`);
90
- await session.prompt(buildReviewPrompt(root, mode), { expandPromptTemplates: false });
82
+ await session.prompt(buildReviewPrompt(root, mode, direction), { expandPromptTemplates: false });
91
83
  } finally {
92
84
  signal.removeEventListener("abort", abort);
93
85
  }
@@ -95,12 +87,6 @@ export async function runReview(options: {
95
87
  if (!output) throw new Error("Review ended without structured output");
96
88
  return output;
97
89
  } finally {
98
- unsubscribe?.();
99
90
  await resource?.dispose();
100
91
  }
101
92
  }
102
-
103
- function summarizeArgs(args: unknown): string {
104
- const text = typeof args === "string" ? args : (JSON.stringify(args) ?? String(args));
105
- return text.length > 140 ? `${text.slice(0, 139)}…` : text;
106
- }
@@ -15,6 +15,7 @@ import {
15
15
  } from "@earendil-works/pi-coding-agent";
16
16
  import { Text } from "@earendil-works/pi-tui";
17
17
  import { Type } from "typebox";
18
+ import { renderToolOutputPreview } from "../../shared/text.ts";
18
19
 
19
20
  type Language = "python3" | "node" | "deno";
20
21
 
@@ -295,6 +296,19 @@ export default function scriptRunnerExtension(pi: ExtensionAPI): void {
295
296
  text.setText(body ? `${header}\n${body}` : header);
296
297
  return text;
297
298
  },
299
+ renderResult(result, options, theme, context) {
300
+ const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
301
+ if (options.isPartial) {
302
+ text.setText("");
303
+ return text;
304
+ }
305
+ const output = result.content
306
+ .filter((item): item is { type: "text"; text: string } => item.type === "text")
307
+ .map((item) => item.text)
308
+ .join("\n");
309
+ text.setText(renderToolOutputPreview(output, options.expanded || context.isError, theme));
310
+ return text;
311
+ },
298
312
  });
299
313
 
300
314
  pi.registerTool(tool);
@@ -3,7 +3,7 @@
3
3
  Soul shapes how Tau works and talks by adding two independent sections to Pi's native assistant prompt:
4
4
 
5
5
  - **ponytail** — a lazy-senior-dev build ethos: do the smallest correct thing, reuse before writing, YAGNI, fix bugs at the root, never cut validation, security, or accessibility.
6
- - **caveman** — a terse communication style: drop filler, keep technical substance exact, quote the shortest decisive line, spell out anything where brevity would risk safety.
6
+ - **simplified** — Simplified Technical English (ASD-STE100): use short sentences and paragraphs, explain jargon, and shape plans and conversations in small chunks.
7
7
 
8
8
  Pi continues to own tool guidance, project instructions, skills, documentation paths, custom prompts, and working-directory context.
9
9
 
@@ -12,7 +12,7 @@ Toggle each section in Tau settings (both on by default):
12
12
  ```json
13
13
  {
14
14
  "extensions": {
15
- "soul": { "ponytail": true, "caveman": false }
15
+ "soul": { "ponytail": true, "simplified": false }
16
16
  }
17
17
  }
18
18
  ```
@@ -1,22 +1,22 @@
1
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
2
  import { loadTauExtensionSettings } from "../../shared/settings/load.ts";
3
- import { CAVEMAN_STYLE, PONYTAIL_ETHOS } from "./prompt.ts";
3
+ import { PONYTAIL_ETHOS, SIMPLIFIED_TECHNICAL_ENGLISH } from "./prompt.ts";
4
4
  import soulSettings from "./settings.ts";
5
5
 
6
6
  export default function soulExtension(pi: ExtensionAPI): void {
7
7
  let ponytail = true;
8
- let caveman = true;
8
+ let simplified = true;
9
9
 
10
10
  pi.on("session_start", async (_event, ctx) => {
11
11
  const settings = await loadTauExtensionSettings(ctx, soulSettings);
12
12
  ponytail = settings.ponytail;
13
- caveman = settings.caveman;
13
+ simplified = settings.simplified;
14
14
  });
15
15
 
16
16
  pi.on("before_agent_start", (event) => {
17
17
  const sections: string[] = [];
18
18
  if (ponytail) sections.push(PONYTAIL_ETHOS);
19
- if (caveman) sections.push(CAVEMAN_STYLE);
19
+ if (simplified) sections.push(SIMPLIFIED_TECHNICAL_ENGLISH);
20
20
  if (sections.length === 0) return undefined;
21
21
  return { systemPrompt: [event.systemPrompt, ...sections].join("\n\n") };
22
22
  });
@@ -26,18 +26,14 @@ Non-trivial logic leaves one runnable check behind: the smallest thing that fail
26
26
 
27
27
  Mark a deliberate simplification that cuts a real corner with a named ceiling and its upgrade path.`;
28
28
 
29
- export const CAVEMAN_STYLE = `## Communication style
29
+ export const SIMPLIFIED_TECHNICAL_ENGLISH = `## Communication style
30
30
 
31
- Primary directive: simple, terse, human-like conversation. Talk to the human the way a person does, not the way a document reads. A wall of text does not get read. Outside a plan or a file you were asked to write, keep replies short. All technical substance stays. Only fluff dies.
31
+ Use Simplified Technical English (ASD-STE100) when you communicate with the user. Assume the user is tired and has limited capacity for jargon.
32
32
 
33
- Drop articles (a/an/the), filler (just/really/basically/actually/simply), pleasantries (sure/certainly/of course/happy to), and hedging. Fragments OK. Short synonyms (big not extensive, fix not "implement a solution for"). No emoji, no decorative tables, no tool-call narration. Do not dump long raw logs. Quote the shortest decisive line.
33
+ Use short sentences and short paragraphs. Explain one idea at a time. Prefer common words, active voice, and concrete explanations. Avoid idioms, vague language, filler, and unexplained abbreviations. Explain unavoidable jargon in plain words when you first use it.
34
34
 
35
- Standard well-known acronyms OK (DB, API, HTTP). Never invent abbreviations (cfg, impl, req, fn). The tokenizer splits them the same as the full word, so they save nothing and cost the reader clarity. No causal arrows. Technical terms, code blocks, and error strings stay exact and verbatim.
35
+ Keep technical content exact. Do not alter paths, commands, API names, code symbols, flags, or error messages. Explain what they mean around the exact text.
36
36
 
37
- Preserve the user's language. Compress the style, not the language.
37
+ Work in small chunks. Answer the immediate question first. For plans, start with the smallest useful outline and expand it only when needed. Expect plans to change after each decision. Do not write a novel before the plan has been checked.
38
38
 
39
- No self-reference. Never name or announce the style.
40
-
41
- No filler emphasis or manufactured insight. Skip "here's the thing", "that's the part that really matters", "and that's the key". Skip "not just X, it's Y" and "it's not X, it's Y" framing. Skip grand closers that restate the point as a lesson. State the fact, the mechanism, or the next step, then stop.
42
-
43
- Full clear sentences, terse style dropped, for security warnings, irreversible-action confirmations, multi-step sequences where order matters, and anywhere compression would create ambiguity. Resume terse after.`;
39
+ Keep responses short enough to scan, while giving enough explanation for the user to understand the reason and next step. Do not replace explanations with fragments just to be brief. Use full clear sentences for safety, irreversible actions, exact step order, and uncertainty.`;
@@ -3,14 +3,17 @@ import { defineTauExtensionSettings } from "../../shared/settings/define.ts";
3
3
 
4
4
  export default defineTauExtensionSettings({
5
5
  key: "soul",
6
- defaults: { ponytail: true as boolean, caveman: true as boolean },
6
+ defaults: { ponytail: true as boolean, simplified: true as boolean },
7
7
  schema: Type.Object(
8
8
  {
9
9
  ponytail: Type.Optional(
10
10
  Type.Boolean({ default: true, description: "Add the lazy-senior-dev build ethos to Tau's system prompt." }),
11
11
  ),
12
- caveman: Type.Optional(
13
- Type.Boolean({ default: true, description: "Add the terse communication style to Tau's system prompt." }),
12
+ simplified: Type.Optional(
13
+ Type.Boolean({
14
+ default: true,
15
+ description: "Add Simplified Technical English and small-chunk explanations to Tau's system prompt.",
16
+ }),
14
17
  ),
15
18
  },
16
19
  { additionalProperties: false },
@@ -48,7 +48,7 @@ Use cheapest source that proves each returned fact. Skip steps when task supplie
48
48
  1. **Supplied context** — Treat current line-numbered task files as authoritative this turn.
49
49
  2. **Paths and literals** — Use read-only `bash` (`ls`, `find`, `rg`/`grep`) for narrow path discovery, exact text, registrations, and unsupported formats. Use ranged `read` for formatting or source without structural support.
50
50
  3. **Structure** — Default to `outline` for known files/packages and unfamiliar supported subtrees. Use `discover` when requested declaration path or exact name is unknown. Use `ast_search` for source shapes.
51
- 4. **Exact declarations** — Use `show` with path + name (+ line when needed). Prefer `signature`; add docs, body, imports, or context lines only when explicitly required.
51
+ 4. **Exact declarations** — Use `show` with a top-level `targets` array containing path + name (+ line when needed), even for one declaration. Prefer `signature`; add docs, body, imports, or context lines only when explicitly required.
52
52
  5. **Direct relationships** — After resolving a declaration, use `callers`, `callees`, `references`, or `implementations` for one direct relationship lookup. Use `deps` and `reverse_deps` for file imports, not declaration calls.
53
53
 
54
54
  Structural results prove bounded syntax, not runtime dispatch. Preserve exact, inferred, and ambiguous labels emitted by tools. Never convert an ambiguous result into a fact.
@@ -11,8 +11,8 @@ names:
11
11
  - Crawler
12
12
  - Netscout
13
13
  - Wayfinder
14
- model: openai-codex/gpt-5.6-sol
15
- thinking: medium
14
+ model: openai-codex/gpt-5.6-luna
15
+ thinking: xhigh
16
16
  ---
17
17
 
18
18
  Stay inside delegated task. Answer exactly what was asked. No broader research, background collection, unrequested recommendations, or implementation work.
@@ -306,8 +306,12 @@ function subscribeTurnEvents(options: {
306
306
  const { session, details, usage, toolUsage, turnMessages, assistantMessageEndAt, publish } = options;
307
307
  const actionById = new Map<string, string>();
308
308
  return session.subscribe((event: AgentSessionEvent) => {
309
- if (event.type === "message_update" && event.message.role === "assistant") {
310
- details.response = cappedTail(textOf(event.message), PREVIEW_LIMIT);
309
+ if (event.type === "message_start" && event.message.role === "assistant") {
310
+ details.response = "";
311
+ return;
312
+ }
313
+ if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") {
314
+ details.response = cappedTail(details.response + event.assistantMessageEvent.delta, PREVIEW_LIMIT);
311
315
  publish();
312
316
  return;
313
317
  }
@@ -48,7 +48,7 @@ Adds `/effort [quick|standard|deep]` to select effort and a provider from curren
48
48
 
49
49
  ## explore
50
50
 
51
- Structural source tools on in-process tree-sitter (WASM), available on every Node-supported platform. Registers `outline`, `show`, `discover`, `ast_search`, `deps`, `reverse_deps`, `callers`, `callees`, `references`, `implementations`, `impact`, and `context`; symbol targets use path + name (+ line when needed). Pi keeps `ls` / `find` / `grep` / `read`. Large full `read`/autoread of registered source (including Markdown) returns outline by default (`explore.read.*`); ranged `read` or `show` for bodies. Disable with `explore.read.enabled: false`.
51
+ Structural source tools on in-process tree-sitter (WASM), available on every Node-supported platform. Registers `outline`, `show`, `discover`, `ast_search`, `deps`, `reverse_deps`, `callers`, `callees`, `references`, `implementations`, `impact`, and `context`; `show` takes a top-level `targets` array of path + name objects (+ line when needed). Pi keeps `ls` / `find` / `grep` / `read`. Large full `read`/autoread of registered source (including Markdown) returns outline by default (`explore.read.*`); ranged `read` or `show` for bodies. Disable with `explore.read.enabled: false`.
52
52
 
53
53
  ## footer
54
54
 
@@ -84,7 +84,7 @@ Adds `/ready` to scan agent-readiness rails (cold start, toolchain, verify, lint
84
84
 
85
85
  ## review
86
86
 
87
- Adds `/review` for explicit isolated review of current Git changes. Choose `simplify`, `architecture`, or `correctness`, or run a mode directly. Results stay outside agent context until you send them from result view, and can be exported under `.pi/tau/reviews/`. `/review show` reopens latest result on current session branch.
87
+ Adds `/review [direction]` for an isolated simplify, architecture, or correctness review. With no direction, it reviews current Git changes. Free-form direction reviews the requested part of the repository even when the working tree is clean. Choose a review type and logged-in provider, then Tau writes the result as Markdown under `.pi/tau/reviews/` without adding it to the parent agent context.
88
88
 
89
89
  ## reference
90
90
 
@@ -108,7 +108,7 @@ Runs configured commands while keeping their output out of agent context when th
108
108
 
109
109
  ## soul
110
110
 
111
- Adds two independently toggleable sections to Pi’s native assistant prompt: `ponytail` (a lazy-senior-dev build ethos) and `caveman` (a terse communication style). Both on by default.
111
+ Adds two independently toggleable sections to Pi’s native assistant prompt: `ponytail` (a lazy-senior-dev build ethos) and `simplified` (Simplified Technical English with short, explanatory responses and small planning chunks). Both on by default.
112
112
 
113
113
  ## stash
114
114
 
@@ -126,6 +126,10 @@ Adds `/tau-help` to show this guide as rendered Markdown in the chat.
126
126
 
127
127
  Adds `/tau`, `/tau init [--global|--project]`, and `/tau doctor` for Tau setup and diagnostics.
128
128
 
129
+ ## tool-approval
130
+
131
+ Reviews agent `bash` and `script_runner` requests with a quick-effort model before execution. Set `extensions.toolApproval.autoApprove` to run every reviewer-approved request without another confirmation. The reviewer approves routine local development work. Concrete destructive, system, production, privileged, or security-sensitive effects require human approval with one explanatory paragraph. Reviewer failures fall back to human approval and send an attention notification.
132
+
129
133
  ## tool-loader
130
134
 
131
135
  Progressively exposes registered specialist tool groups through `load_tools`. Tau registers `web`, `image`, and `appshot`; project or global package extensions can add groups with `registerDeferredToolGroup()` from `@shanepadgett/tau-agent`. Supported providers can preserve more prompt-cache reuse.
@@ -0,0 +1,20 @@
1
+ # Tool Approval
2
+
3
+ Reviews agent `bash` and `script_runner` requests with a quick-effort model before execution. The reviewer returns a validated decision and one concise paragraph that explains the request.
4
+
5
+ Trivially recognized read-only bash commands can run without a human prompt after a valid approval. With `autoApprove` enabled, every reviewer-approved request runs without another confirmation. Routine local development work should be approved, including requests that modify project files or run scripts. The reviewer asks for human approval only when it finds a concrete destructive, system, production, privileged, or security-sensitive effect.
6
+
7
+ When approval is required, Tau shows one paragraph that explains the effect and risk without repeating the request. If the reviewer fails or returns a malformed decision, Tau asks for direct human approval instead of running it automatically. Tau also sends an attention notification when the approval window opens.
8
+
9
+ Configure under `extensions.toolApproval`:
10
+
11
+ ```json
12
+ {
13
+ "extensions": {
14
+ "toolApproval": {
15
+ "enabled": true,
16
+ "autoApprove": true
17
+ }
18
+ }
19
+ }
20
+ ```