@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.
Files changed (69) 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/silent-command-runner/README.md +0 -2
  20. package/extensions/silent-command-runner/index.ts +12 -41
  21. package/extensions/soul/README.md +5 -8
  22. package/extensions/soul/context.ts +115 -0
  23. package/extensions/soul/index.ts +141 -7
  24. package/extensions/soul/prompt.ts +47 -102
  25. package/extensions/soul/state.ts +114 -0
  26. package/extensions/soul/tools.ts +31 -0
  27. package/extensions/tau-help/help.md +6 -18
  28. package/extensions/tau-help/index.ts +10 -4
  29. package/extensions/tool-approval/README.md +2 -0
  30. package/extensions/tool-approval/index.ts +65 -44
  31. package/extensions/tool-approval/panel.ts +153 -0
  32. package/extensions/tool-loader/README.md +1 -1
  33. package/extensions/tool-loader/index.ts +91 -22
  34. package/package.json +2 -2
  35. package/schemas/tau.schema.json +0 -104
  36. package/shared/bounded-text-result.ts +0 -1
  37. package/shared/events.ts +19 -15
  38. package/shared/isolated-session.ts +1 -2
  39. package/shared/model-effort.ts +15 -21
  40. package/shared/prompt-contributions.ts +24 -0
  41. package/src/index.ts +1 -1
  42. package/docs/subagents.md +0 -92
  43. package/extensions/auto-compact/README.md +0 -9
  44. package/extensions/auto-compact/index.ts +0 -122
  45. package/extensions/auto-compact/settings.ts +0 -30
  46. package/extensions/context/settings.ts +0 -62
  47. package/extensions/context/sync.ts +0 -276
  48. package/extensions/context/validation.ts +0 -88
  49. package/extensions/effort/README.md +0 -7
  50. package/extensions/effort/index.ts +0 -134
  51. package/extensions/effort/state.ts +0 -18
  52. package/extensions/qna/inline-editor-row.ts +0 -56
  53. package/extensions/soul/overseer.ts +0 -273
  54. package/extensions/soul/settings.ts +0 -34
  55. package/extensions/subagent/README.md +0 -67
  56. package/extensions/subagent/agents/context-sync.md +0 -244
  57. package/extensions/subagent/agents/dormant/generalist.md +0 -33
  58. package/extensions/subagent/agents/scout.md +0 -104
  59. package/extensions/subagent/agents/web-research.md +0 -101
  60. package/extensions/subagent/agents.ts +0 -261
  61. package/extensions/subagent/cmux-dashboard.ts +0 -495
  62. package/extensions/subagent/index.ts +0 -425
  63. package/extensions/subagent/panel.ts +0 -124
  64. package/extensions/subagent/render.ts +0 -74
  65. package/extensions/subagent/resume.ts +0 -78
  66. package/extensions/subagent/run.ts +0 -641
  67. package/extensions/subagent/runtime.ts +0 -1296
  68. package/extensions/subagent/session-resource.ts +0 -61
  69. package/extensions/subagent/settings.ts +0 -18
@@ -1,6 +1,9 @@
1
- import { defineTool, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
1
+ import { defineTool, type ExtensionAPI, type SessionEntry } from "@earendil-works/pi-coding-agent";
2
2
  import { type Static, Type } from "typebox";
3
3
  import { registeredDeferredToolGroups, type DeferredToolGroupInfo } from "../../src/tool-loading/index.ts";
4
+ import { emitTauEvent } from "../../shared/events.ts";
5
+ import { BoundedTextResultBuilder } from "../../shared/bounded-text-result.ts";
6
+ import { createTemporaryOutputStore } from "../../shared/temporary-output-store.ts";
4
7
 
5
8
  const loadToolsSchema = Type.Object(
6
9
  {
@@ -21,14 +24,25 @@ interface LoadToolsDetails {
21
24
  addedToolNames: string[];
22
25
  }
23
26
 
27
+ const PENDING_TYPE = "tau.tool-loader.pending";
28
+ const APPLIED_TYPE = "tau.tool-loader.applied";
29
+
30
+ interface AppliedLoad {
31
+ pendingId: string;
32
+ names: string[];
33
+ }
34
+
24
35
  export default function toolLoaderExtension(pi: ExtensionAPI): void {
36
+ let temporaryOutput = createTemporaryOutputStore();
25
37
  let managed = false;
26
38
  let allowedToolNames = new Map<string, ReadonlySet<string>>();
27
39
  let managedToolNames = new Set<string>();
28
40
 
29
41
  pi.registerTool(createLoadToolsTool(pi, []));
30
42
 
31
- pi.on("session_start", (_event, ctx) => {
43
+ pi.on("session_start", async (_event, ctx) => {
44
+ temporaryOutput = createTemporaryOutputStore();
45
+ await temporaryOutput.start();
32
46
  const groups = registeredDeferredToolGroups(pi);
33
47
  pi.registerTool(createLoadToolsTool(pi, groups));
34
48
 
@@ -39,20 +53,43 @@ export default function toolLoaderExtension(pi: ExtensionAPI): void {
39
53
  );
40
54
  managedToolNames = new Set(groups.flatMap((group) => group.toolNames));
41
55
  managed = initialSet.has("load_tools") && groups.length > 0;
42
- if (managed) restoreActiveTools(pi, initial, loadedCapabilities(ctx.sessionManager.getBranch()), groups);
56
+ if (managed) restoreActiveTools(pi, initial, loadedCapabilities(ctx.sessionManager.getBranch(), groups), groups);
43
57
  });
58
+ pi.on("session_shutdown", () => temporaryOutput.shutdown());
44
59
 
45
60
  pi.on("session_tree", (_event, ctx) => {
46
61
  if (managed) {
47
62
  restoreActiveTools(
48
63
  pi,
49
64
  pi.getActiveTools(),
50
- loadedCapabilities(ctx.sessionManager.getBranch()),
65
+ loadedCapabilities(ctx.sessionManager.getBranch(), registeredDeferredToolGroups(pi)),
51
66
  registeredDeferredToolGroups(pi),
52
67
  );
53
68
  }
54
69
  });
55
70
 
71
+ pi.on("session_compact", (_event, ctx) => {
72
+ const branch = ctx.sessionManager.getBranch();
73
+ const applied = new Set(
74
+ branch.flatMap((entry) =>
75
+ entry.type === "custom" && entry.customType === APPLIED_TYPE ? [(entry.data as AppliedLoad).pendingId] : [],
76
+ ),
77
+ );
78
+ for (const entry of branch) {
79
+ if (
80
+ entry.type !== "custom" ||
81
+ entry.customType !== PENDING_TYPE ||
82
+ applied.has(entry.id) ||
83
+ !Array.isArray(entry.data)
84
+ )
85
+ continue;
86
+ const names = entry.data.filter((name): name is string => typeof name === "string");
87
+ const allowed = new Set([...allowedToolNames.values()].flatMap((group) => [...group]));
88
+ pi.setActiveTools([...new Set([...pi.getActiveTools(), ...names.filter((name) => allowed.has(name))])]);
89
+ pi.appendEntry(APPLIED_TYPE, { pendingId: entry.id, names });
90
+ }
91
+ });
92
+
56
93
  function createLoadToolsTool(pi: ExtensionAPI, groups: readonly DeferredToolGroupInfo[]) {
57
94
  return defineTool<typeof loadToolsSchema, LoadToolsDetails>({
58
95
  name: "load_tools",
@@ -63,7 +100,7 @@ export default function toolLoaderExtension(pi: ExtensionAPI): void {
63
100
  "Use load_tools before attempting a registered specialist capability whose tools are not currently available.",
64
101
  ],
65
102
  parameters: loadToolsSchema,
66
- async execute(_toolCallId, params: LoadToolsParams) {
103
+ async execute(_toolCallId, params: LoadToolsParams, signal, _onUpdate, ctx) {
67
104
  const group = registeredDeferredToolGroups(pi).find((candidate) => candidate.id === params.capability);
68
105
  if (group === undefined) {
69
106
  throw new Error(
@@ -81,7 +118,25 @@ export default function toolLoaderExtension(pi: ExtensionAPI): void {
81
118
  }
82
119
 
83
120
  const beforeSet = new Set(before);
84
- pi.setActiveTools([...before, ...loadable.filter((name) => !beforeSet.has(name))]);
121
+ const next = [...before, ...loadable.filter((name) => !beforeSet.has(name))];
122
+ const nextSet = new Set(next);
123
+ let blocked: string | null = null;
124
+ emitTauEvent(pi, "tau:prompt.tools.check", {
125
+ ctx,
126
+ tools: pi.getAllTools().filter((tool) => nextSet.has(tool.name)),
127
+ reject(reason) {
128
+ blocked = reason;
129
+ },
130
+ });
131
+ if (blocked) {
132
+ pi.appendEntry(PENDING_TYPE, loadable);
133
+ return boundedResult(
134
+ `${blocked} ${params.capability} is queued for activation after successful compaction.`,
135
+ { version: 1, capability: params.capability, requestedToolNames: requested, addedToolNames: [] },
136
+ signal,
137
+ );
138
+ }
139
+ pi.setActiveTools(next);
85
140
  const after = pi.getActiveTools();
86
141
  const addedToolNames = requested.filter((name) => !beforeSet.has(name) && after.includes(name));
87
142
  const available = requested.filter((name) => after.includes(name));
@@ -91,24 +146,36 @@ export default function toolLoaderExtension(pi: ExtensionAPI): void {
91
146
  ? `Loaded ${params.capability} tools: ${addedToolNames.join(", ")}.`
92
147
  : `${params.capability} tools are already loaded: ${available.join(", ")}.`;
93
148
 
94
- return {
95
- content: [
96
- {
97
- type: "text" as const,
98
- text: unavailable.length ? `${text} Unavailable: ${unavailable.join(", ")}.` : text,
99
- },
100
- ],
101
- details: {
149
+ return boundedResult(
150
+ unavailable.length ? `${text} Unavailable: ${unavailable.join(", ")}.` : text,
151
+ {
102
152
  version: 1,
103
153
  capability: params.capability,
104
154
  requestedToolNames: requested,
105
155
  addedToolNames,
106
156
  },
107
- };
157
+ signal,
158
+ );
108
159
  },
109
160
  });
110
161
  }
111
162
 
163
+ async function boundedResult(text: string, details: LoadToolsDetails, signal: AbortSignal | undefined) {
164
+ const builder = new BoundedTextResultBuilder(temporaryOutput, "head");
165
+ try {
166
+ await builder.append(text);
167
+ signal?.throwIfAborted();
168
+ const result = await builder.finish();
169
+ return {
170
+ content: [{ type: "text" as const, text: result.content }],
171
+ details: { ...details, overflow: result.overflow },
172
+ };
173
+ } catch (error) {
174
+ await builder.abort();
175
+ throw error;
176
+ }
177
+ }
178
+
112
179
  function restoreActiveTools(
113
180
  pi: ExtensionAPI,
114
181
  current: readonly string[],
@@ -131,16 +198,18 @@ function formatGroupCatalog(groups: readonly DeferredToolGroupInfo[]): string {
131
198
  return ` Registered groups: ${catalog}.`;
132
199
  }
133
200
 
134
- function loadedCapabilities(entries: readonly unknown[]): Set<string> {
201
+ function loadedCapabilities(entries: readonly SessionEntry[], groups: readonly DeferredToolGroupInfo[]): Set<string> {
135
202
  const loaded = new Set<string>();
136
- for (const value of entries) {
137
- if (!value || typeof value !== "object") continue;
138
- const entry = value as Record<string, unknown>;
139
- if (entry.type !== "message" || !entry.message || typeof entry.message !== "object") continue;
140
- const message = entry.message as Record<string, unknown>;
203
+ for (const entry of entries) {
204
+ if (entry.type === "custom" && entry.customType === APPLIED_TYPE) {
205
+ const { names } = entry.data as AppliedLoad;
206
+ for (const group of groups) if (group.toolNames.some((name) => names.includes(name))) loaded.add(group.id);
207
+ }
208
+ if (entry.type !== "message") continue;
209
+ const message = entry.message;
141
210
  if (message.role !== "toolResult" || message.toolName !== "load_tools" || message.isError === true) continue;
142
211
  if (!isLoadToolsDetails(message.details)) continue;
143
- loaded.add(message.details.capability);
212
+ if (message.details.addedToolNames.length > 0) loaded.add(message.details.capability);
144
213
  }
145
214
  return loaded;
146
215
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shanepadgett/tau-agent",
3
- "version": "0.42.3",
3
+ "version": "0.43.0",
4
4
  "description": "Tau is a custom agentic harness built with pi extensions",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -35,7 +35,7 @@
35
35
  ],
36
36
  "dependencies": {
37
37
  "@ast-grep/wasm": "0.45.3",
38
- "@shanepadgett/tau-tui": "0.42.3",
38
+ "@shanepadgett/tau-tui": "0.43.0",
39
39
  "@vscode/tree-sitter-wasm": "0.3.1",
40
40
  "image-size": "2.0.4",
41
41
  "smol-toml": "1.8.0",
@@ -12,64 +12,6 @@
12
12
  "extensions": {
13
13
  "type": "object",
14
14
  "properties": {
15
- "autoCompact": {
16
- "type": "object",
17
- "properties": {
18
- "enabled": {
19
- "type": "boolean",
20
- "default": true,
21
- "description": "Enable automatic compaction when context reaches the token limit."
22
- },
23
- "tokenLimit": {
24
- "type": "integer",
25
- "minimum": 1,
26
- "default": 175000,
27
- "description": "Absolute context-token count that triggers compaction before the next model turn."
28
- }
29
- },
30
- "additionalProperties": false
31
- },
32
- "context": {
33
- "type": "object",
34
- "properties": {
35
- "sync": {
36
- "type": "object",
37
- "properties": {
38
- "enabled": {
39
- "type": "boolean",
40
- "default": true,
41
- "description": "Master switch for context-sync. When false: no /context-sync, parent cannot call the agent, validation does not auto-run sync."
42
- },
43
- "automation": {
44
- "type": "boolean",
45
- "default": true,
46
- "description": "When true with sync.enabled, the coding agent may call context-sync. When false, only manual /context-sync (validation auto-run still allowed if validation.enabled)."
47
- }
48
- },
49
- "additionalProperties": false
50
- },
51
- "validation": {
52
- "type": "object",
53
- "properties": {
54
- "enabled": {
55
- "type": "boolean",
56
- "default": false,
57
- "description": "Validate context membership after agent turns and auto-run context-sync on failure (requires sync.enabled)."
58
- },
59
- "ignoreGlobs": {
60
- "type": "array",
61
- "items": {
62
- "type": "string"
63
- },
64
- "default": [],
65
- "description": "Project-relative files excluded from context membership validation and sync."
66
- }
67
- },
68
- "additionalProperties": false
69
- }
70
- },
71
- "additionalProperties": false
72
- },
73
15
  "explore": {
74
16
  "type": "object",
75
17
  "properties": {
@@ -249,52 +191,6 @@
249
191
  },
250
192
  "additionalProperties": false
251
193
  },
252
- "soul": {
253
- "type": "object",
254
- "required": [
255
- "overseer"
256
- ],
257
- "properties": {
258
- "overseer": {
259
- "type": "object",
260
- "required": [
261
- "enabled",
262
- "toolCallInterval"
263
- ],
264
- "properties": {
265
- "enabled": {
266
- "type": "boolean",
267
- "default": true,
268
- "description": "Run hidden primary-directive reviews during long tool-using work."
269
- },
270
- "toolCallInterval": {
271
- "type": "integer",
272
- "minimum": 1,
273
- "maximum": 100,
274
- "default": 20,
275
- "description": "Unreviewed tool calls required before the next primary-directive review."
276
- }
277
- },
278
- "additionalProperties": false
279
- }
280
- },
281
- "additionalProperties": false
282
- },
283
- "subagent": {
284
- "type": "object",
285
- "properties": {
286
- "disabled": {
287
- "type": "array",
288
- "items": {
289
- "type": "string",
290
- "minLength": 1
291
- },
292
- "default": [],
293
- "description": "Agent names unavailable for delegation."
294
- }
295
- },
296
- "additionalProperties": false
297
- },
298
194
  "toolApproval": {
299
195
  "type": "object",
300
196
  "properties": {
@@ -63,7 +63,6 @@ export class BoundedTextResultBuilder {
63
63
  this.strategy = strategy;
64
64
  }
65
65
 
66
- // fallow-ignore-next-line unused-class-member -- head/tail strategy API; callers use completeBlocks today
67
66
  async append(text: string): Promise<void> {
68
67
  if (this.strategy === "completeBlocks") throw new Error("Complete-block results require appendBlock");
69
68
  this.requireOpen();
package/shared/events.ts CHANGED
@@ -1,9 +1,26 @@
1
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
- import type { ModelEffort } from "./model-effort.ts";
1
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ import type { Tool } from "@earendil-works/pi-ai";
3
3
  import type { ToolRowVisualState } from "./tool-row-state.js";
4
4
  import type { FileInjectionRequest, PreparedFileInjection } from "../src/file-injection/index.ts";
5
5
 
6
6
  export type TauAgentEvents = {
7
+ /** @internal Soul's runtime-scoped prompt contributors. */
8
+ "tau:prompt.sources": {
9
+ accept(source: {
10
+ key: string;
11
+ section: string;
12
+ refresh: "compaction" | "append";
13
+ read(ctx: ExtensionContext): Promise<string>;
14
+ }): void;
15
+ };
16
+ /** @internal Check a tool loadout before activation. */
17
+ "tau:prompt.tools.check": {
18
+ ctx: ExtensionContext;
19
+ tools: Tool[];
20
+ reject(reason: string): void;
21
+ };
22
+ /** @internal Exact projected Soul instructions, for prompt viewers. */
23
+ "tau:prompt.snapshot": { text: string };
7
24
  "tau:agent.blocked": {
8
25
  title?: string;
9
26
  body?: string;
@@ -16,15 +33,6 @@ export type TauAgentEvents = {
16
33
  id: string;
17
34
  disposition: "notify" | "discard";
18
35
  };
19
- /** Pause silent post-edit checks across a compact that will resume the same work chain. */
20
- "tau:silent-command-runner.pause": {
21
- id: string;
22
- };
23
- /** End a silent-check pause. continue keeps the chain open; settle finalizes a deferred settle. */
24
- "tau:silent-command-runner.resume": {
25
- id: string;
26
- disposition: "continue" | "settle";
27
- };
28
36
  "tau:file-mutation.applied": {
29
37
  source: "patch";
30
38
  toolCallId: string;
@@ -54,10 +62,6 @@ export type TauAgentEvents = {
54
62
  text?: string;
55
63
  priority?: number;
56
64
  };
57
- "tau:model-effort.changed": {
58
- effort: ModelEffort | undefined;
59
- };
60
- "tau:model-effort.snapshot.requested": Record<string, never>;
61
65
  "tau:tool-row-state.set": {
62
66
  rowId: string;
63
67
  state?: ToolRowVisualState;
@@ -158,7 +158,6 @@ export async function createIsolatedSessionResource(
158
158
  modelRuntime,
159
159
  thinkingLevel: inputs.thinkingLevel,
160
160
  tools: [...inputs.tools],
161
- excludeTools: ["subagent"],
162
161
  resourceLoader,
163
162
  customTools: [...inputs.customTools],
164
163
  sessionManager: SessionManager.inMemory(inputs.cwd),
@@ -169,7 +168,7 @@ export async function createIsolatedSessionResource(
169
168
  if (signal.aborted) throw new Error(`${inputs.label} startup aborted`);
170
169
  const active = session.getActiveToolNames().sort();
171
170
  const expected = [...inputs.tools].sort();
172
- if (active.join("\0") !== expected.join("\0") || active.includes("subagent")) {
171
+ if (active.join("\0") !== expected.join("\0")) {
173
172
  const missing = expected.filter((tool) => !active.includes(tool));
174
173
  throw new Error(
175
174
  `${inputs.label} startup failed: unavailable tools: ${missing.join(", ") || "active tool mismatch"}`,
@@ -33,47 +33,41 @@ const MODEL_PREFERENCES: Record<ModelEffort, readonly ProviderPreference[]> = {
33
33
  quick: [
34
34
  {
35
35
  provider: "openai-codex",
36
- models: [{ model: "gpt-5.6-luna", reasoning: "medium" }],
36
+ models: [
37
+ { model: "gpt-6-luna", reasoning: "medium" },
38
+ { model: "gpt-5.6-luna", reasoning: "medium" },
39
+ ],
37
40
  },
38
- { provider: "xai", models: [{ model: "grok-4.5", reasoning: "medium" }] },
39
- { provider: "anthropic", models: [{ model: "claude-haiku-4-5", reasoning: "high" }] },
41
+ { provider: "xai", models: [{ model: "grok-4.7", reasoning: "medium" }] },
42
+ { provider: "anthropic", models: [{ model: "claude-sonnet-5", reasoning: "medium" }] },
40
43
  ],
41
44
  standard: [
42
45
  {
43
46
  provider: "openai-codex",
44
- models: [{ model: "gpt-5.6-luna", reasoning: "max" }],
47
+ models: [
48
+ { model: "gpt-6-sol", reasoning: "high" },
49
+ { model: "gpt-6-luna", reasoning: "max" },
50
+ { model: "gpt-5.6-luna", reasoning: "max" },
51
+ ],
45
52
  },
46
- { provider: "xai", models: [{ model: "grok-4.5", reasoning: "high" }] },
53
+ { provider: "xai", models: [{ model: "grok-4.7", reasoning: "high" }] },
47
54
  { provider: "anthropic", models: [{ model: "claude-sonnet-5", reasoning: "high" }] },
48
55
  ],
49
56
  deep: [
50
57
  {
51
58
  provider: "openai-codex",
52
- models: [{ model: "gpt-5.6-sol", reasoning: "high" }],
59
+ models: [{ model: "gpt-6-astra", reasoning: "medium" }],
53
60
  },
54
61
  {
55
62
  provider: "anthropic",
56
63
  models: [
57
- { model: "claude-opus-5", reasoning: "high" },
58
- { model: "claude-fable-5", reasoning: "low" },
64
+ { model: "claude-opus-5.5", reasoning: "medium" },
65
+ { model: "claude-fable-5.1", reasoning: "low" },
59
66
  ],
60
67
  },
61
68
  ],
62
69
  };
63
70
 
64
- export function effortForSelection(
65
- provider: string | undefined,
66
- model: string | undefined,
67
- reasoning: string | undefined,
68
- ): ModelEffort | undefined {
69
- if (!provider || !model || !reasoning) return undefined;
70
- for (const effort of ["deep", "standard", "quick"] as const) {
71
- const preference = MODEL_PREFERENCES[effort].find((item) => item.provider === provider);
72
- if (preference?.models.some((item) => item.model === model && item.reasoning === reasoning)) return effort;
73
- }
74
- return undefined;
75
- }
76
-
77
71
  export function resolveEffortProviders(
78
72
  ctx: Pick<ExtensionContext, "modelRegistry">,
79
73
  effort: ModelEffort,
@@ -0,0 +1,24 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { emitTauEvent, onTauEventImmediately, type TauAgentEvents } from "./events.ts";
3
+
4
+ type PromptSource = Parameters<TauAgentEvents["tau:prompt.sources"]["accept"]>[0];
5
+
6
+ export type PromptValue = Pick<PromptSource, "key" | "section" | "refresh"> & { text: string };
7
+
8
+ export function collectPromptSources(pi: ExtensionAPI): PromptSource[] {
9
+ const sources: PromptSource[] = [];
10
+ emitTauEvent(pi, "tau:prompt.sources", { accept: (source) => sources.push(source) });
11
+ const keys = new Set<string>();
12
+ for (const source of sources) {
13
+ if (keys.has(source.key)) throw new Error(`Duplicate prompt source: ${source.key}`);
14
+ keys.add(source.key);
15
+ }
16
+ return sources.sort((a, b) => a.key.localeCompare(b.key, "en"));
17
+ }
18
+
19
+ export function registerPromptSource(pi: ExtensionAPI, source: PromptSource): void {
20
+ if (!/^[a-z][a-z0-9/-]*$/.test(source.key) || !/^[a-z][a-z0-9-]*$/.test(source.section)) {
21
+ throw new Error(`Invalid prompt source: ${source.key}`);
22
+ }
23
+ onTauEventImmediately(pi, `prompt.${source.key}`, "tau:prompt.sources", ({ accept }) => accept(source));
24
+ }
package/src/index.ts CHANGED
@@ -5,5 +5,5 @@ export {
5
5
  type ImageGenerationContext,
6
6
  type ImageProvider,
7
7
  } from "./image-generation/index.ts";
8
- export { FILE_INJECTION_TYPE, prepareFileInjection } from "./file-injection/index.ts";
8
+ export { prepareFileInjection } from "./file-injection/index.ts";
9
9
  export { registerDeferredToolGroup, type DeferredToolGroup } from "./tool-loading/index.ts";
package/docs/subagents.md DELETED
@@ -1,92 +0,0 @@
1
- # Custom subagents
2
-
3
- Tau's `subagent` tool delegates one focused task to an isolated child Pi session. You can add your own agent definitions without writing TypeScript.
4
-
5
- Each fresh call returns a thread ID. Continue that thread when feedback or follow-up work depends on the child's prior reads and reasoning:
6
-
7
- ```text
8
- subagent({ agent: "scout", task: "Trace configuration loading" })
9
- subagent({ thread: "thread-1", task: "Now check whether this proposed fix covers every caller" })
10
- ```
11
-
12
- Retained threads keep their complete child conversation for five minutes after the latest child response. A later follow-up keeps the same thread, agent, model, thinking level, tools, and cwd, but starts a clean child session. Tau supplies prior tasks, exact terminal results, and paths passed through `files`; it does not run a summarization request. Old source, tool history, intermediate responses, and thinking are absent, so the child reads current source before relying on a retained path. Start fresh for unrelated work. Tau keeps up to 16 threads and evicts the least recently used idle thread when needed.
13
-
14
- If the relevant files are already known, autoread them into a fresh or retained child turn:
15
-
16
- ```text
17
- subagent({ agent: "scout", task: "Trace the runtime change", files: ["src/runtime.ts", "test/runtime.test.ts"] })
18
- ```
19
-
20
- Paths may be relative to the parent's current working directory or absolute. Tau reads current, line-numbered snapshots when the turn starts. Unreadable files produce failed context entries without stopping the child. Keep the list focused because complete snapshots consume the child's context window.
21
-
22
- ## Where definitions live
23
-
24
- | Scope | Path | Use when |
25
- | --- | --- | --- |
26
- | **User (global)** | `~/.pi/agent/tau/agents/*.md` | You want the agent in every project |
27
- | **Project** | nearest trusted `.pi/tau/agents/*.md` | Repo-specific helpers |
28
-
29
- Precedence: **project overrides user**, which overrides Tau's built-ins (`scout`, `web-research`). Duplicate names in one scope are invalid.
30
-
31
- ## Definition format
32
-
33
- Markdown with frontmatter:
34
-
35
- ```markdown
36
- ---
37
- name: api-reader
38
- description: Inspect API declarations and usage
39
- tools:
40
- - read
41
- - grep
42
- names:
43
- - Ledger
44
- - Quill
45
- - Beacon
46
- model: openai-codex/gpt-5.4-mini
47
- thinking: medium
48
- ---
49
-
50
- Stay within the delegated task. Return exact paths and symbols.
51
- ```
52
-
53
- Required:
54
-
55
- - `name`
56
- - `description`
57
- - `tools` (unique tool names; `subagent` cannot be delegated)
58
-
59
- Optional:
60
-
61
- - `names`: a non-empty list of unique display names
62
- - `model` as `provider/model`
63
- - `thinking`: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max`
64
-
65
- Named tools and configured models must exist in the normally loaded child Pi environment (including tools from installed packages such as Tau).
66
-
67
- Tau assigns one display name to each fresh child and keeps it for follow-up turns on that thread. It cycles through the list in order. When the list wraps, reused names get numeric suffixes such as `Ledger-2`, so any number of concurrent children can still have distinct names. Without `names`, the agent name is used as a one-item pool.
68
-
69
- ## Runtime rules
70
-
71
- - Run `/agents` to enable or disable agents for the current session branch. Press Space to stage each toggle, then Enter to apply the changes. Session choices do not change Tau settings.
72
- - Set `extensions.subagent.disabled` to an array of agent names for persistent global or project-level disabling. Agents disabled this way appear as `disabled by Tau settings` in `/agents` and cannot be enabled there.
73
- - Disabled agents are omitted from the parent's available-agent list and cannot start or continue a retained thread.
74
- - Children use the parent's cwd and inherit model/thinking unless the definition overrides them.
75
- - Children do not receive the parent conversation.
76
- - Calls can include `files` to autoread line-numbered snapshots into that child turn.
77
- - Follow-up calls within five minutes reuse the complete child conversation. Colder calls resume from exact prior results and relevant paths in a clean session.
78
- - Cold resume keeps the selected model, thinking level, tools, cwd, definition, display name, and thread ID.
79
- - Children load only the extensions that own their declared tools. Unrelated extension hooks do not run in child sessions.
80
- - At most four children run at once; extra calls wait in order.
81
- - Calls to the same retained thread run one at a time.
82
- - Display names identify children in Tau's tool rows and cmux dashboard; thread IDs remain the identifiers used for continuation calls.
83
- - Returned text is capped (50 KB / 2,000 lines); full truncated output is saved to a private temp file.
84
- - Interactive cmux sessions get one temporary Markdown dashboard for waiting and running invocations. It is observational only: cmux latency or failure cannot delay, fail, or reorder children. The dashboard closes shortly after the active cohort finishes. Print mode never opens it.
85
-
86
- ## Built-ins
87
-
88
- - `scout` — substantial multi-hop local code lookup that would chew parent context; paths, declarations, imports, references, call edges; facts only. Skip small digs
89
- - `web-research` — `websearch`, `codesearch`, `webfetch`
90
- - `context-sync` — maps meaningful uncommitted work into `.pi/contexts`. Offered to the coding agent when `extensions.context.sync.enabled` and `sync.automation` are true. Manual `/context-sync` remains when sync is enabled with `automation` false. Validation can auto-run it when `validation.enabled` and `sync.enabled`
91
-
92
- Ask Tau to delegate, or let it call `subagent` with an agent name and task.
@@ -1,9 +0,0 @@
1
- # Auto Compact
2
-
3
- Compacts long conversations before the next model turn when their context reaches an absolute token limit. Active work resumes through a hidden continuation message after Pi's native compaction finishes.
4
-
5
- Set `extensions.autoCompact.enabled` to `false` to turn automatic compaction off. Set `extensions.autoCompact.tokenLimit` to change the limit. It defaults to 175,000 context tokens for every model. Pi still shows its native collapsed compaction entry in the chat.
6
-
7
- After changing this extension, run `/reload` before testing it.
8
-
9
- Automatic compaction holds attention notifications while it runs and while the hidden continuation resumes the interrupted work. The notification is released only after that resumed work settles. It also pauses silent post-edit checks across the compact so they do not run on incomplete work.