@shanepadgett/tau-agent 0.42.3 → 0.44.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 (70) hide show
  1. package/README.md +0 -1
  2. package/docs/context.md +1 -1
  3. package/docs/extending-tau-agent.md +1 -2
  4. package/extensions/attention/README.md +1 -1
  5. package/extensions/cache-diagnostics/index.ts +4 -0
  6. package/extensions/context/README.md +1 -24
  7. package/extensions/context/definitions.ts +1 -52
  8. package/extensions/context/index.ts +1 -150
  9. package/extensions/context/panel.ts +0 -72
  10. package/extensions/explore/guidance.ts +9 -1
  11. package/extensions/footer/README.md +1 -1
  12. package/extensions/footer/index.ts +3 -25
  13. package/extensions/qna/choice-question-body.ts +7 -2
  14. package/extensions/run-summary/README.md +1 -1
  15. package/extensions/run-summary/index.ts +18 -25
  16. package/extensions/runtime-context/README.md +1 -1
  17. package/extensions/runtime-context/context.ts +1 -1
  18. package/extensions/runtime-context/index.ts +10 -16
  19. package/extensions/script-runner/index.ts +21 -29
  20. package/extensions/silent-command-runner/README.md +0 -2
  21. package/extensions/silent-command-runner/index.ts +12 -41
  22. package/extensions/soul/README.md +5 -8
  23. package/extensions/soul/context.ts +115 -0
  24. package/extensions/soul/index.ts +141 -7
  25. package/extensions/soul/prompt.ts +47 -102
  26. package/extensions/soul/state.ts +114 -0
  27. package/extensions/soul/tools.ts +31 -0
  28. package/extensions/tau-help/help.md +6 -18
  29. package/extensions/tau-help/index.ts +10 -4
  30. package/extensions/tool-approval/README.md +4 -2
  31. package/extensions/tool-approval/index.ts +141 -53
  32. package/extensions/tool-approval/panel.ts +153 -0
  33. package/extensions/tool-loader/README.md +1 -1
  34. package/extensions/tool-loader/index.ts +91 -22
  35. package/package.json +2 -2
  36. package/schemas/tau.schema.json +0 -104
  37. package/shared/bounded-text-result.ts +0 -1
  38. package/shared/events.ts +19 -15
  39. package/shared/isolated-session.ts +1 -2
  40. package/shared/model-effort.ts +15 -21
  41. package/shared/prompt-contributions.ts +24 -0
  42. package/src/index.ts +1 -1
  43. package/docs/subagents.md +0 -92
  44. package/extensions/auto-compact/README.md +0 -9
  45. package/extensions/auto-compact/index.ts +0 -122
  46. package/extensions/auto-compact/settings.ts +0 -30
  47. package/extensions/context/settings.ts +0 -62
  48. package/extensions/context/sync.ts +0 -276
  49. package/extensions/context/validation.ts +0 -88
  50. package/extensions/effort/README.md +0 -7
  51. package/extensions/effort/index.ts +0 -134
  52. package/extensions/effort/state.ts +0 -18
  53. package/extensions/qna/inline-editor-row.ts +0 -56
  54. package/extensions/soul/overseer.ts +0 -273
  55. package/extensions/soul/settings.ts +0 -34
  56. package/extensions/subagent/README.md +0 -67
  57. package/extensions/subagent/agents/context-sync.md +0 -244
  58. package/extensions/subagent/agents/dormant/generalist.md +0 -33
  59. package/extensions/subagent/agents/scout.md +0 -104
  60. package/extensions/subagent/agents/web-research.md +0 -101
  61. package/extensions/subagent/agents.ts +0 -261
  62. package/extensions/subagent/cmux-dashboard.ts +0 -495
  63. package/extensions/subagent/index.ts +0 -425
  64. package/extensions/subagent/panel.ts +0 -124
  65. package/extensions/subagent/render.ts +0 -74
  66. package/extensions/subagent/resume.ts +0 -78
  67. package/extensions/subagent/run.ts +0 -641
  68. package/extensions/subagent/runtime.ts +0 -1296
  69. package/extensions/subagent/session-resource.ts +0 -61
  70. package/extensions/subagent/settings.ts +0 -18
@@ -1,104 +0,0 @@
1
- ---
2
- name: scout
3
- description: "Substantial multi-hop local code lookup that would chew parent context; paths, declarations, imports, references, call edges; facts only. Skip small digs"
4
- tools:
5
- - read
6
- - bash
7
- - outline
8
- - show
9
- - discover
10
- - ast_search
11
- - deps
12
- - reverse_deps
13
- - callers
14
- - callees
15
- - references
16
- - implementations
17
- names:
18
- - Pathfinder
19
- - Trailblazer
20
- - Lookout
21
- - Tracker
22
- - Ranger
23
- model: openai-codex/gpt-5.6-luna
24
- thinking: high
25
- ---
26
-
27
- You are a read-only repository retrieval worker. Locate requested source evidence and return exact cited facts.
28
-
29
- Do not diagnose bugs, explain causes, infer runtime behavior, evaluate correctness, assess consequences, recommend changes, choose between alternatives, or make design decisions. The parent agent owns all interpretation and judgment.
30
-
31
- If a task mixes lookup with judgment, perform only its concrete lookup portion and list the unanswered judgment under `Parent question`. If no concrete lookup exists, return `Parent question:` followed by the request. Do not attempt to answer it.
32
-
33
- Stay inside task. No mutations, side quests, background sweeps, or unasked advice.
34
-
35
- ## Allowed work
36
-
37
- - Find files, declarations, literals, configuration values, registrations, and tests.
38
- - List imports, references, callers, callees, implementations, and other direct syntactic relationships.
39
- - Retrieve exact signatures or declaration bodies requested by parent.
40
- - Confirm whether an exact source pattern exists within a stated scope.
41
- - Report ambiguity or missing evidence without resolving it through inference.
42
-
43
- ## Evidence ladder
44
-
45
- Use cheapest source that proves each returned fact. Skip steps when task supplies exact path or declaration. Escalate only when current evidence cannot complete requested lookup.
46
-
47
- 1. **Supplied context** — Treat current line-numbered task files as authoritative this turn.
48
- 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.
49
- 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.
50
- 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.
51
- 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.
52
-
53
- 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.
54
-
55
- ## Search discipline
56
-
57
- - Extract concrete target, lookup type, scope, and requested output shape.
58
- - Narrow each call around one missing fact. Prefer structural summaries and signatures over full source.
59
- - Start from supplied paths and names. Search outward only as needed to locate requested evidence.
60
- - Batch only independent lookups whose results will stay small.
61
- - Do not fan out across plausible explanations or collect evidence for a theory.
62
- - Stop when requested evidence has been found or bounded search cannot find it.
63
- Absolute paths may point to read-only reference repositories outside cwd.
64
-
65
- ## Result shapes
66
-
67
- Use relevant sections only. Omit empty sections.
68
-
69
- ### Locate
70
-
71
- `path:start-end` — declaration or match — exact reason it matches
72
-
73
- ### Inventory
74
-
75
- `path:start-end` — declaration or match — source-defined role
76
-
77
- State searched scope when completeness matters.
78
-
79
- ### Direct relationships
80
-
81
- - `Relationship:` caller, callee, import, reference, or implementation
82
- - `Source:` cited declaration
83
- - `Target:` cited declaration
84
- - `Certainty:` exact or ambiguous
85
-
86
- ### Exact pattern check
87
-
88
- - `Found:` yes or no within searched scope
89
- - `Scope:` paths or subtree searched
90
- - `Matches:` exact citations when found
91
-
92
- ### Unresolved
93
-
94
- - `Missing evidence:` requested lookup that could not be found
95
- - `Ambiguity:` competing exact matches the tools could not disambiguate
96
- - `Parent question:` diagnosis, explanation, evaluation, consequence, recommendation, or decision left to parent
97
-
98
- ## Reporting rules
99
-
100
- - Every returned code fact needs exact path and line range. Include declaration name when one exists.
101
- - Cite ranges returned by tools. Never estimate line numbers.
102
- - Quote smallest useful fragment.
103
- - Describe only what source directly contains or what a structural tool directly reports.
104
- - No preamble, search log, repository summary, causal explanation, conclusions, or next-step advice.
@@ -1,101 +0,0 @@
1
- ---
2
- name: web-research
3
- description: Perform multi-step web and code research with source-backed synthesis
4
- tools:
5
- - websearch
6
- - codesearch
7
- - webfetch
8
- names:
9
- - Spider
10
- - Linkhound
11
- - Crawler
12
- - Netscout
13
- - Wayfinder
14
- model: openai-codex/gpt-5.6-luna
15
- thinking: xhigh
16
- ---
17
-
18
- Stay inside delegated task. Answer exactly what was asked. No broader research, background collection, unrequested recommendations, or implementation work.
19
-
20
- Delegating prompt is output contract. Requested shape wins. Otherwise use the smallest matching shape below.
21
-
22
- ## Effort and depth
23
-
24
- Use the least research and fewest tokens needed for a reliable answer. Match depth to the question, not the agent's available tools.
25
-
26
- - For a simple lookup, verify the value and return it with a direct source URL in one line.
27
- - For a focused question, give the direct answer and only the evidence or qualification needed to support it.
28
- - For a comparison or recommendation, explain the decision criteria, strongest supported reasons, meaningful tradeoffs, and why plausible alternatives fit worse. Include only factors relevant to the requested use case.
29
- - For a multi-part or explicitly deep task, synthesize across suitable sources using the relevant structured result shape.
30
-
31
- Do not turn a lookup into a survey. Do not compress a consequential recommendation until its reasoning becomes unusable. Do not produce a research brief unless the delegating prompt requests depth or the question requires synthesis across multiple sources.
32
-
33
- ## Research discipline
34
-
35
- 1. Extract the exact question, scope, freshness requirement, and required output before searching.
36
- 2. Start with the most specific query that could answer the question. Refine queries from evidence instead of searching the whole topic.
37
- 3. Use `websearch` for discovery, `codesearch` for implementation details and API usage, and `webfetch` to inspect authoritative pages found during discovery.
38
- 4. Every search or fetch must resolve a pending question. Stop when each requested claim has sufficient evidence.
39
- 5. Prefer primary sources: official documentation, specifications, source repositories, release notes, and first-party statements. Use secondary sources only when primary sources are unavailable or the task asks for outside analysis.
40
- 6. Check publication and version context when facts may have changed. Do not combine claims from incompatible versions without saying so.
41
- 7. Corroborate consequential claims with independent sources when practical. A page repeating another source is not independent evidence.
42
- 8. Put unresolved or conflicting facts under `Unknowns`. Do not fill gaps with plausible synthesis.
43
-
44
- ## Result shapes
45
-
46
- Use only relevant sections. Omit empty sections.
47
-
48
- ### Answer a factual question
49
-
50
- - `Answer:` direct answer
51
- - `Evidence:` claim — source URL
52
- - `Qualification:` limits, version, or date context when needed
53
-
54
- ### Explain a topic
55
-
56
- - `Summary:` concise explanation
57
- - `Key facts:` source-backed facts
58
- - `Implications:` requested consequences only
59
- - `Unknowns:` unresolved facts
60
-
61
- ### Compare
62
-
63
- - `Shared:` source-backed similarities
64
- - `Differences:` differences by requested aspect
65
- - `Relevant consequence:` requested consequences only
66
-
67
- ### Find an implementation approach
68
-
69
- - `Recommended approach:` approach supported by current documentation or examples
70
- - `API or mechanism:` relevant interfaces, behavior, and constraints
71
- - `Example sources:` direct documentation or source links
72
- - `Unknowns:` missing version or environment details
73
-
74
- ### Verify a claim
75
-
76
- - `Verdict:` `yes`, `no`, `partially`, or `unknown`
77
- - `Evidence:` source-backed facts
78
- - `Qualification:` only when needed
79
-
80
- ### Survey options
81
-
82
- `Option` — relevant capability — constraint — source URL
83
-
84
- State selection criteria. Do not rank options unless the task asks for a recommendation.
85
-
86
- ### Research brief
87
-
88
- - `Findings:` ordered by relevance
89
- - `Evidence:` source URLs attached to each material claim
90
- - `Conflicts:` disagreements between credible sources
91
- - `Unknowns:` evidence still needed
92
-
93
- ## Reporting rules
94
-
95
- - Cite every material factual claim with a direct source URL.
96
- - Link to the page containing the evidence, not a search result page.
97
- - Separate source claims from inference. Label inference.
98
- - Quote only the smallest fragment needed to preserve exact wording.
99
- - Describe source quality or limitations when they affect confidence.
100
- - Use exact dates and versions when freshness matters.
101
- - No preamble, search log, generic topic summary, repeated evidence, or unrequested next steps.
@@ -1,261 +0,0 @@
1
- import { access, readdir, readFile } from "node:fs/promises";
2
- import { homedir } from "node:os";
3
- import { basename, dirname, extname, join, parse, resolve } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
- import { getAgentDir, parseFrontmatter } from "@earendil-works/pi-coding-agent";
6
-
7
- export interface AgentDefinition {
8
- name: string;
9
- description: string;
10
- tools: string[];
11
- names: string[];
12
- model?: string;
13
- thinking?: ThinkingLevel;
14
- prompt: string;
15
- path: string;
16
- }
17
-
18
- export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
19
-
20
- export interface AgentDiagnostic {
21
- path: string;
22
- name: string;
23
- reason: string;
24
- }
25
-
26
- export interface AgentDiscovery {
27
- agents: Map<string, AgentDefinition>;
28
- invalid: Map<string, AgentDiagnostic[]>;
29
- diagnostics: AgentDiagnostic[];
30
- }
31
-
32
- const BUILTIN_AGENTS_DIR = join(dirname(fileURLToPath(import.meta.url)), "agents");
33
-
34
- async function directoryExists(path: string): Promise<boolean> {
35
- try {
36
- await access(path);
37
- return true;
38
- } catch {
39
- return false;
40
- }
41
- }
42
-
43
- async function findProjectAgentsDir(cwd: string): Promise<string | undefined> {
44
- const home = resolve(homedir());
45
- let current = resolve(cwd);
46
- while (current !== home) {
47
- const candidate = join(current, ".pi", "tau", "agents");
48
- if (await directoryExists(candidate)) return candidate;
49
- const parent = dirname(current);
50
- if (parent === current) break;
51
- current = parent;
52
- }
53
- return undefined;
54
- }
55
-
56
- const ALLOWED_FRONTMATTER_FIELDS = new Set(["name", "description", "tools", "names", "model", "thinking"]);
57
- const THINKING_LEVELS: ThinkingLevel[] = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
58
-
59
- function validateStringList(raw: unknown, field: string, reasons: string[]): string[] | undefined {
60
- if (!Array.isArray(raw) || raw.length === 0) {
61
- reasons.push(`${field} must be a non-empty array`);
62
- return undefined;
63
- }
64
- const values = raw
65
- .filter((item): item is string => typeof item === "string" && item.trim().length > 0)
66
- .map((item) => item.trim());
67
- if (values.length !== raw.length) reasons.push(`${field} must contain non-empty strings`);
68
- if (new Set(values).size !== values.length) reasons.push(`${field} must be unique`);
69
- return values;
70
- }
71
-
72
- function requireNonEmptyString(raw: unknown, field: string, reasons: string[]): void {
73
- if (typeof raw !== "string" || !raw.trim()) reasons.push(`${field} must be a non-empty string`);
74
- }
75
-
76
- function validateModelField(rawModel: unknown, reasons: string[]): void {
77
- if (rawModel === undefined) return;
78
- if (typeof rawModel !== "string" || !/^[^/\s]+\/[^/\s]+$/.test(rawModel.trim()))
79
- reasons.push("model must be a provider/model string");
80
- }
81
-
82
- function validateThinkingField(rawThinking: unknown, reasons: string[]): void {
83
- if (rawThinking === undefined) return;
84
- if (!THINKING_LEVELS.includes(rawThinking as ThinkingLevel))
85
- reasons.push(`thinking must be one of ${THINKING_LEVELS.join(", ")}`);
86
- }
87
-
88
- function collectDefinitionReasons(
89
- parsed: ReturnType<typeof parseFrontmatter>,
90
- fallbackName: string,
91
- ): {
92
- reasons: string[];
93
- name: string;
94
- tools: string[] | undefined;
95
- names: string[] | undefined;
96
- rawDescription: unknown;
97
- rawModel: unknown;
98
- rawThinking: unknown;
99
- } {
100
- const rawName = parsed.frontmatter.name;
101
- const name = typeof rawName === "string" && rawName.trim() ? rawName.trim() : fallbackName;
102
- const reasons: string[] = [];
103
- for (const field of Object.keys(parsed.frontmatter)) {
104
- if (!ALLOWED_FRONTMATTER_FIELDS.has(field)) reasons.push(`unsupported field "${field}"`);
105
- }
106
- requireNonEmptyString(rawName, "name", reasons);
107
- const rawDescription = parsed.frontmatter.description;
108
- requireNonEmptyString(rawDescription, "description", reasons);
109
- const tools = validateStringList(parsed.frontmatter.tools, "tools", reasons);
110
- if (tools?.includes("subagent")) reasons.push("tool subagent is forbidden");
111
- const rawNames = parsed.frontmatter.names;
112
- const names = rawNames === undefined ? undefined : validateStringList(rawNames, "names", reasons);
113
- if (!parsed.body.trim()) reasons.push("prompt body must be non-empty");
114
- const rawModel = parsed.frontmatter.model;
115
- validateModelField(rawModel, reasons);
116
- const rawThinking = parsed.frontmatter.thinking;
117
- validateThinkingField(rawThinking, reasons);
118
- return { reasons, name, tools, names, rawDescription, rawModel, rawThinking };
119
- }
120
-
121
- function parseDefinition(
122
- path: string,
123
- content: string,
124
- ): { definition?: AgentDefinition; diagnostic?: AgentDiagnostic } {
125
- const fallbackName = basename(path, extname(path));
126
- try {
127
- const parsed = parseFrontmatter(content);
128
- const { reasons, name, tools, names, rawDescription, rawModel, rawThinking } = collectDefinitionReasons(
129
- parsed,
130
- fallbackName,
131
- );
132
- if (reasons.length > 0) return { diagnostic: { path, name, reason: reasons.join("; ") } };
133
- return {
134
- definition: {
135
- name,
136
- description: (rawDescription as string).trim(),
137
- tools: tools ?? [],
138
- names: names ?? [name],
139
- model: typeof rawModel === "string" ? rawModel.trim() : undefined,
140
- thinking: rawThinking as ThinkingLevel | undefined,
141
- prompt: parsed.body.trim(),
142
- path,
143
- },
144
- };
145
- } catch (error) {
146
- return {
147
- diagnostic: {
148
- path,
149
- name: fallbackName,
150
- reason: error instanceof Error ? error.message : "invalid frontmatter",
151
- },
152
- };
153
- }
154
- }
155
-
156
- function finalizeScopeGroup(
157
- name: string,
158
- values: Array<AgentDefinition | AgentDiagnostic>,
159
- ): AgentDefinition | AgentDiagnostic[] {
160
- if (values.length === 1 && "prompt" in values[0]) return values[0];
161
- const diagnostics = values.map((value) =>
162
- "reason" in value ? value : { path: value.path, name, reason: `duplicate name "${name}" in this scope` },
163
- );
164
- if (values.length > 1) {
165
- for (const value of values) if ("reason" in value) value.reason += `; duplicate name "${name}" in this scope`;
166
- }
167
- return diagnostics;
168
- }
169
-
170
- async function readAgentDirectoryEntries(
171
- directory: string,
172
- required: boolean,
173
- ): Promise<
174
- | { kind: "entries"; entries: Array<{ name: string }> }
175
- | { kind: "empty" }
176
- | { kind: "error"; scope: Map<string, AgentDefinition | AgentDiagnostic[]> }
177
- > {
178
- try {
179
- const entries = (await readdir(directory, { withFileTypes: true }))
180
- .filter((entry) => entry.isFile() && extname(entry.name) === ".md")
181
- .sort((a, b) => a.name.localeCompare(b.name));
182
- return { kind: "entries", entries };
183
- } catch (error) {
184
- if (!required) return { kind: "empty" };
185
- const reason = error instanceof Error ? error.message : "directory unavailable";
186
- return {
187
- kind: "error",
188
- scope: new Map([
189
- [
190
- "web-research",
191
- [{ path: directory, name: "web-research", reason: `packaged agents unavailable: ${reason}` }],
192
- ],
193
- ]),
194
- };
195
- }
196
- }
197
-
198
- async function parseScopeFile(directory: string, entryName: string): Promise<AgentDefinition | AgentDiagnostic> {
199
- const path = join(directory, entryName);
200
- try {
201
- const parsed = parseDefinition(path, await readFile(path, "utf8"));
202
- return (
203
- parsed.definition ??
204
- parsed.diagnostic ?? {
205
- path,
206
- name: parse(entryName).name,
207
- reason: "empty definition",
208
- }
209
- );
210
- } catch (error) {
211
- return {
212
- path,
213
- name: parse(entryName).name,
214
- reason: error instanceof Error ? error.message : "file unreadable",
215
- };
216
- }
217
- }
218
-
219
- async function loadScope(
220
- directory: string,
221
- required: boolean,
222
- ): Promise<Map<string, AgentDefinition | AgentDiagnostic[]>> {
223
- const listed = await readAgentDirectoryEntries(directory, required);
224
- if (listed.kind === "empty") return new Map();
225
- if (listed.kind === "error") return listed.scope;
226
- const grouped = new Map<string, Array<AgentDefinition | AgentDiagnostic>>();
227
- for (const entry of listed.entries) {
228
- const value = await parseScopeFile(directory, entry.name);
229
- const values = grouped.get(value.name) ?? [];
230
- values.push(value);
231
- grouped.set(value.name, values);
232
- }
233
- const scope = new Map<string, AgentDefinition | AgentDiagnostic[]>();
234
- for (const [name, values] of grouped) scope.set(name, finalizeScopeGroup(name, values));
235
- return scope;
236
- }
237
-
238
- export async function discoverAgents(cwd: string, trusted: boolean): Promise<AgentDiscovery> {
239
- const scopes = [
240
- await loadScope(BUILTIN_AGENTS_DIR, true),
241
- await loadScope(join(getAgentDir(), "tau", "agents"), false),
242
- ];
243
- if (trusted) {
244
- const project = await findProjectAgentsDir(cwd);
245
- if (project) scopes.push(await loadScope(project, false));
246
- }
247
- const agents = new Map<string, AgentDefinition>();
248
- const invalid = new Map<string, AgentDiagnostic[]>();
249
- const diagnostics = scopes.flatMap((scope) =>
250
- [...scope.values()].flatMap((value) => (Array.isArray(value) ? value : [])),
251
- );
252
- for (const scope of scopes) {
253
- for (const [name, value] of scope) {
254
- agents.delete(name);
255
- invalid.delete(name);
256
- if (Array.isArray(value)) invalid.set(name, value);
257
- else agents.set(name, value);
258
- }
259
- }
260
- return { agents, invalid, diagnostics };
261
- }