@shanepadgett/tau-agent 0.42.3 → 0.43.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.
- package/README.md +0 -1
- package/docs/context.md +1 -1
- package/docs/extending-tau-agent.md +1 -2
- package/extensions/attention/README.md +1 -1
- package/extensions/cache-diagnostics/index.ts +4 -0
- package/extensions/context/README.md +1 -24
- package/extensions/context/definitions.ts +1 -52
- package/extensions/context/index.ts +1 -150
- package/extensions/context/panel.ts +0 -72
- package/extensions/explore/guidance.ts +9 -1
- package/extensions/footer/README.md +1 -1
- package/extensions/footer/index.ts +3 -25
- package/extensions/qna/choice-question-body.ts +7 -2
- package/extensions/run-summary/README.md +1 -1
- package/extensions/run-summary/index.ts +18 -25
- package/extensions/runtime-context/README.md +1 -1
- package/extensions/runtime-context/context.ts +1 -1
- package/extensions/runtime-context/index.ts +10 -16
- package/extensions/silent-command-runner/README.md +0 -2
- package/extensions/silent-command-runner/index.ts +12 -41
- package/extensions/soul/README.md +5 -8
- package/extensions/soul/context.ts +115 -0
- package/extensions/soul/index.ts +141 -7
- package/extensions/soul/prompt.ts +47 -102
- package/extensions/soul/state.ts +114 -0
- package/extensions/soul/tools.ts +31 -0
- package/extensions/tau-help/help.md +6 -18
- package/extensions/tau-help/index.ts +10 -4
- package/extensions/tool-approval/README.md +2 -0
- package/extensions/tool-approval/index.ts +65 -44
- package/extensions/tool-approval/panel.ts +153 -0
- package/extensions/tool-loader/README.md +1 -1
- package/extensions/tool-loader/index.ts +91 -22
- package/package.json +2 -2
- package/schemas/tau.schema.json +0 -104
- package/shared/bounded-text-result.ts +0 -1
- package/shared/events.ts +19 -15
- package/shared/isolated-session.ts +1 -2
- package/shared/model-effort.ts +15 -21
- package/shared/prompt-contributions.ts +24 -0
- package/src/index.ts +1 -1
- package/docs/subagents.md +0 -92
- package/extensions/auto-compact/README.md +0 -9
- package/extensions/auto-compact/index.ts +0 -122
- package/extensions/auto-compact/settings.ts +0 -30
- package/extensions/context/settings.ts +0 -62
- package/extensions/context/sync.ts +0 -276
- package/extensions/context/validation.ts +0 -88
- package/extensions/effort/README.md +0 -7
- package/extensions/effort/index.ts +0 -134
- package/extensions/effort/state.ts +0 -18
- package/extensions/qna/inline-editor-row.ts +0 -56
- package/extensions/soul/overseer.ts +0 -273
- package/extensions/soul/settings.ts +0 -34
- package/extensions/subagent/README.md +0 -67
- package/extensions/subagent/agents/context-sync.md +0 -244
- package/extensions/subagent/agents/dormant/generalist.md +0 -33
- package/extensions/subagent/agents/scout.md +0 -104
- package/extensions/subagent/agents/web-research.md +0 -101
- package/extensions/subagent/agents.ts +0 -261
- package/extensions/subagent/cmux-dashboard.ts +0 -495
- package/extensions/subagent/index.ts +0 -425
- package/extensions/subagent/panel.ts +0 -124
- package/extensions/subagent/render.ts +0 -74
- package/extensions/subagent/resume.ts +0 -78
- package/extensions/subagent/run.ts +0 -641
- package/extensions/subagent/runtime.ts +0 -1296
- package/extensions/subagent/session-resource.ts +0 -61
- package/extensions/subagent/settings.ts +0 -18
|
@@ -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
|
-
}
|