@shanepadgett/tau-agent 0.26.0 → 0.28.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/docs/subagents.md +1 -1
- package/extensions/appshot/README.md +1 -1
- package/extensions/appshot/index.ts +5 -11
- package/extensions/context/sync.ts +1 -9
- package/extensions/context-pruning/README.md +2 -2
- package/extensions/context-pruning/index.ts +58 -57
- package/extensions/context-pruning/prune.ts +2 -2
- package/extensions/context-pruning/render.ts +51 -39
- package/extensions/context-pruning/settings.ts +4 -5
- package/extensions/explore/README.md +13 -14
- package/extensions/explore/ast/adapter.ts +97 -0
- package/extensions/explore/ast/engine.ts +470 -0
- package/extensions/explore/ast/format/ast-search.ts +97 -0
- package/extensions/explore/ast/format/composite.ts +26 -0
- package/extensions/explore/ast/format/context.ts +66 -0
- package/extensions/explore/ast/format/deps.ts +86 -0
- package/extensions/explore/ast/format/discover.ts +85 -0
- package/extensions/explore/ast/format/impact.ts +105 -0
- package/extensions/explore/ast/format/outline.ts +66 -0
- package/extensions/explore/ast/format/relationships.ts +81 -0
- package/extensions/explore/ast/format/show.ts +38 -0
- package/extensions/explore/ast/grammars/kotlin.wasm +0 -0
- package/extensions/explore/ast/grammars/manifest.json +41 -0
- package/extensions/explore/ast/grammars/manifest.ts +99 -0
- package/extensions/explore/ast/grammars/odin.wasm +0 -0
- package/extensions/explore/ast/grammars/swift.wasm +0 -0
- package/extensions/explore/ast/graph/file-graph.ts +360 -0
- package/extensions/explore/ast/graph/relationships.ts +597 -0
- package/extensions/explore/ast/identity.ts +119 -0
- package/extensions/explore/ast/ir.ts +130 -0
- package/extensions/explore/ast/languages/csharp-file-deps.ts +189 -0
- package/extensions/explore/ast/languages/csharp.ts +493 -0
- package/extensions/explore/ast/languages/file-dep-util.ts +120 -0
- package/extensions/explore/ast/languages/fixtures/sample.cs +17 -0
- package/extensions/explore/ast/languages/fixtures/sample.java +21 -0
- package/extensions/explore/ast/languages/fixtures/sample.kt +15 -0
- package/extensions/explore/ast/languages/fixtures/sample.odin +28 -0
- package/extensions/explore/ast/languages/fixtures/sample.rs +31 -0
- package/extensions/explore/ast/languages/fixtures/sample.swift +25 -0
- package/extensions/explore/ast/languages/go-file-deps.ts +139 -0
- package/extensions/explore/ast/languages/go.ts +545 -0
- package/extensions/explore/ast/languages/java.ts +412 -0
- package/extensions/explore/ast/languages/jvm-file-deps.ts +217 -0
- package/extensions/explore/ast/languages/kotlin.ts +611 -0
- package/extensions/explore/ast/languages/odin-file-deps.ts +175 -0
- package/extensions/explore/ast/languages/odin.ts +407 -0
- package/extensions/explore/ast/languages/rust-file-deps.ts +159 -0
- package/extensions/explore/ast/languages/rust.ts +506 -0
- package/extensions/explore/ast/languages/swift-file-deps.ts +59 -0
- package/extensions/explore/ast/languages/swift.ts +466 -0
- package/extensions/explore/ast/languages/tree.ts +220 -0
- package/extensions/explore/ast/languages/typescript-file-deps.ts +259 -0
- package/extensions/explore/ast/languages/typescript-package-surface.ts +440 -0
- package/extensions/explore/ast/languages/typescript.ts +716 -0
- package/extensions/explore/ast/markdown.ts +145 -0
- package/extensions/explore/ast/package-surface.ts +42 -0
- package/extensions/explore/ast/queries/ast-search.ts +306 -0
- package/extensions/explore/ast/queries/composite-target.ts +77 -0
- package/extensions/explore/ast/queries/context.ts +406 -0
- package/extensions/explore/ast/queries/discover.ts +427 -0
- package/extensions/explore/ast/queries/impact.ts +250 -0
- package/extensions/explore/ast/queries/outline.ts +203 -0
- package/extensions/explore/ast/queries/show.ts +262 -0
- package/extensions/explore/ast/query.ts +48 -0
- package/extensions/explore/ast/read/hook.ts +125 -0
- package/extensions/explore/ast/read/policy.ts +55 -0
- package/extensions/explore/ast/registry.ts +120 -0
- package/extensions/explore/ast/scan.ts +76 -0
- package/extensions/explore/ast/slice.ts +31 -0
- package/extensions/explore/ast/tools/ast-search.ts +138 -0
- package/extensions/explore/ast/tools/context.ts +126 -0
- package/extensions/explore/ast/tools/deps.ts +72 -0
- package/extensions/explore/ast/tools/discover.ts +208 -0
- package/extensions/explore/ast/tools/impact.ts +135 -0
- package/extensions/explore/ast/tools/outline.ts +200 -0
- package/extensions/explore/ast/tools/relationships.ts +160 -0
- package/extensions/explore/ast/tools/render.ts +121 -0
- package/extensions/explore/ast/tools/reverse-deps.ts +72 -0
- package/extensions/explore/ast/tools/show.ts +116 -0
- package/extensions/explore/guidance.ts +52 -0
- package/extensions/explore/index.ts +91 -45
- package/extensions/explore/read/autoread.ts +118 -0
- package/extensions/explore/settings.ts +64 -0
- package/extensions/explore/traverse.ts +260 -86
- package/extensions/handoff/index.ts +1 -1
- package/extensions/patch/executor.ts +95 -1
- package/extensions/patch/index.ts +1 -0
- package/extensions/publish/index.ts +18 -85
- package/extensions/publish/publish-ui.ts +0 -4
- package/extensions/soul/README.md +7 -4
- package/extensions/soul/index.ts +11 -5
- package/extensions/soul/prompt.ts +30 -14
- package/extensions/soul/settings.ts +7 -2
- package/extensions/subagent/agents/context-sync.md +6 -10
- package/extensions/subagent/agents/review.md +44 -24
- package/extensions/subagent/agents/scout.md +53 -34
- package/extensions/subagent/run.ts +1 -1
- package/extensions/tau-help/help.md +2 -2
- package/package.json +6 -5
- package/schemas/tau.schema.json +51 -6
- package/{extensions/explore → shared}/autoread.ts +21 -7
- package/shared/bounded-text-result.ts +304 -0
- package/shared/events.ts +1 -0
- package/shared/full-file-knowledge.ts +49 -0
- package/shared/temporary-output-store.ts +275 -0
- package/extensions/explore/ast-tools.ts +0 -828
- package/extensions/explore/ast-worker.ts +0 -356
- package/extensions/explore/find.ts +0 -147
- package/extensions/explore/full-file-knowledge.ts +0 -234
- package/extensions/explore/grep.ts +0 -749
- package/extensions/explore/limits.ts +0 -22
- package/extensions/explore/ls.ts +0 -113
- package/extensions/explore/path-display.ts +0 -41
- package/extensions/explore/path-tree.ts +0 -150
- package/extensions/explore/read-cache.ts +0 -273
- package/extensions/explore/read-snapshots.ts +0 -57
- package/extensions/explore/read-stats-panel.ts +0 -154
- package/extensions/explore/read-stats.ts +0 -120
- package/extensions/explore/read.ts +0 -335
- package/extensions/explore/result.ts +0 -29
- package/native-bin/darwin-arm64/tau-ast +0 -0
package/docs/subagents.md
CHANGED
|
@@ -83,7 +83,7 @@ Tau assigns one display name to each fresh child and keeps it for follow-up turn
|
|
|
83
83
|
## Built-ins
|
|
84
84
|
|
|
85
85
|
- `review` — adversarial, read-only review for correctness, runtime risks, duplication, and over- or under-engineering
|
|
86
|
-
- `scout` —
|
|
86
|
+
- `scout` — tiered, AST-first local discovery of files, symbols, data flow, constraints, and unknowns without changing anything
|
|
87
87
|
- `web-research` — `websearch`, `codesearch`, `webfetch`
|
|
88
88
|
- `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`
|
|
89
89
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Appshot
|
|
2
2
|
|
|
3
|
-
Appshot gives Tau direct discovery and capture of macOS application windows. Tau can list visible windows as compact
|
|
3
|
+
Appshot gives Tau direct discovery and capture of macOS application windows. Tau can list visible windows as compact JSON with their application identity, title, process ID, bounds, and stable window ID; capture an exact window as a PNG for visual inspection; and bring an application forward when needed. Captures preserve aspect ratio and fit within 1568×1568 pixels to keep image payloads bounded.
|
|
4
4
|
|
|
5
5
|
The extension provides three tools:
|
|
6
6
|
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
type ExtensionAPI,
|
|
7
7
|
} from "@earendil-works/pi-coding-agent";
|
|
8
8
|
import { Text } from "@earendil-works/pi-tui";
|
|
9
|
-
import { encode } from "@toon-format/toon";
|
|
10
9
|
import { randomUUID } from "node:crypto";
|
|
11
10
|
import { mkdir, readFile, rename, rm, stat } from "node:fs/promises";
|
|
12
11
|
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
@@ -86,20 +85,15 @@ function encodeWindowList(windows: WindowInfo[]): string {
|
|
|
86
85
|
height: window.bounds.height,
|
|
87
86
|
}));
|
|
88
87
|
const render = (count: number, omitted: number) =>
|
|
89
|
-
|
|
90
|
-
indent: 1,
|
|
91
|
-
});
|
|
88
|
+
JSON.stringify(omitted === 0 ? { windows: rows.slice(0, count) } : { windows: rows.slice(0, count), omitted });
|
|
92
89
|
const fits = (value: string) =>
|
|
93
90
|
Buffer.byteLength(value, "utf8") <= DEFAULT_MAX_BYTES && value.split("\n").length <= DEFAULT_MAX_LINES;
|
|
94
|
-
const maximumRows = Math.max(0, DEFAULT_MAX_LINES - 2);
|
|
95
91
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (fits(full)) return full;
|
|
99
|
-
}
|
|
92
|
+
const full = render(rows.length, 0);
|
|
93
|
+
if (fits(full)) return full;
|
|
100
94
|
|
|
101
95
|
let low = 0;
|
|
102
|
-
let high =
|
|
96
|
+
let high = rows.length;
|
|
103
97
|
while (low < high) {
|
|
104
98
|
const middle = Math.ceil((low + high) / 2);
|
|
105
99
|
if (fits(render(middle, rows.length - middle))) low = middle;
|
|
@@ -116,7 +110,7 @@ function registerAppshotTools(pi: ExtensionAPI, runHelper: RunHelper): void {
|
|
|
116
110
|
name: "list_windows",
|
|
117
111
|
label: "List Windows",
|
|
118
112
|
description:
|
|
119
|
-
"List visible normal macOS windows as compact
|
|
113
|
+
"List visible normal macOS windows as compact JSON with window IDs, titles, application identity, process IDs, and bounds. Use list_windows to discover exact window IDs and application PIDs before screenshot_window or activate_app. Requires macOS 14 or newer and Screen & System Audio Recording permission.",
|
|
120
114
|
parameters: listWindowsSchema,
|
|
121
115
|
async execute(_toolCallId, _params, signal) {
|
|
122
116
|
if (process.platform !== "darwin") throw new Error("list_windows is only available on macOS");
|
|
@@ -223,12 +223,4 @@ function changedCatalogPaths(before: Map<string, string>, after: Map<string, str
|
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
// Ensure evidence tool name stays aligned with agent definition tooling.
|
|
226
|
-
export const CONTEXT_SYNC_REQUIRED_TOOLS = [
|
|
227
|
-
"read",
|
|
228
|
-
"ls",
|
|
229
|
-
"find",
|
|
230
|
-
"grep",
|
|
231
|
-
"bash",
|
|
232
|
-
"patch",
|
|
233
|
-
CONTEXT_SYNC_EVIDENCE_TOOL,
|
|
234
|
-
] as const;
|
|
226
|
+
export const CONTEXT_SYNC_REQUIRED_TOOLS = ["read", "bash", "patch", CONTEXT_SYNC_EVIDENCE_TOOL] as const;
|
|
@@ -20,7 +20,7 @@ Context Pruning does not impose a minimum token saving and does not reject a che
|
|
|
20
20
|
|
|
21
21
|
## Automatic and manual requests
|
|
22
22
|
|
|
23
|
-
Tau checks context
|
|
23
|
+
Tau checks active context size after tool-using turns and can send progressively stronger private instructions from `nudgeInstructions`. Hints occur at fixed token intervals rather than percentages of the model's context window. After a checkpoint, Tau suppresses boundaries already crossed by the resulting context and resumes at the next boundary. Branch navigation and compaction reconstruct that state from the active branch.
|
|
24
24
|
|
|
25
25
|
Run `/prune` with no arguments to ask the agent to create a checkpoint and continue its task immediately.
|
|
26
26
|
|
|
@@ -35,5 +35,5 @@ Normal Pi compaction remains independent. When a compaction no longer includes a
|
|
|
35
35
|
Settings live under `extensions.contextPruning` in Tau settings.
|
|
36
36
|
|
|
37
37
|
- `enabled`: enables the tool, `/prune`, projection, markers, and branch replay. Defaults to `true`.
|
|
38
|
-
- `
|
|
38
|
+
- `nudgeEveryTokens`: active-context interval between automatic hints. Defaults to `30000`, producing the default instruction ladder at 30k, 60k, and 90k tokens.
|
|
39
39
|
- `nudgeInstructions`: ordered list of one through five nonempty instructions. Later reminders repeat the final instruction. Defaults to three escalating instructions.
|
|
@@ -15,11 +15,11 @@ import { createToolRowStateStore } from "../../shared/tool-row-state.ts";
|
|
|
15
15
|
import { contextPruneParameters, executeContextPrune } from "./prune.ts";
|
|
16
16
|
import { projectContext } from "./projection.ts";
|
|
17
17
|
import {
|
|
18
|
-
|
|
18
|
+
parseContextPruningNudgeDetailsV3,
|
|
19
19
|
renderContextPruneCall,
|
|
20
20
|
renderContextPruneResult,
|
|
21
21
|
renderContextPruningNudge,
|
|
22
|
-
type
|
|
22
|
+
type ContextPruningNudgeDetailsV3,
|
|
23
23
|
} from "./render.ts";
|
|
24
24
|
import contextPruningSettings from "./settings.ts";
|
|
25
25
|
|
|
@@ -30,8 +30,8 @@ const NUDGE_BASELINE_ENTRY_TYPE = "tau.context-pruning.nudge-baseline";
|
|
|
30
30
|
|
|
31
31
|
interface NudgeState {
|
|
32
32
|
anchorToolCallId: string | undefined;
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
suppressedThroughTokens: number | undefined;
|
|
34
|
+
highestBoundaryTokens: number;
|
|
35
35
|
highestTier: number;
|
|
36
36
|
terminalTierReached: boolean;
|
|
37
37
|
}
|
|
@@ -39,20 +39,20 @@ interface NudgeState {
|
|
|
39
39
|
export default function contextPruningExtension(pi: ExtensionAPI): void {
|
|
40
40
|
let enabled = false;
|
|
41
41
|
let lifecycleGeneration = 0;
|
|
42
|
-
let
|
|
42
|
+
let nudgeEveryTokens = contextPruningSettings.defaults.nudgeEveryTokens;
|
|
43
43
|
let nudgeInstructions = contextPruningSettings.defaults.nudgeInstructions;
|
|
44
44
|
let toolRegistered = false;
|
|
45
45
|
let commandRegistered = false;
|
|
46
46
|
let visualRows = new Set<string>();
|
|
47
47
|
let nudgeState: NudgeState = {
|
|
48
48
|
anchorToolCallId: undefined,
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
suppressedThroughTokens: 0,
|
|
50
|
+
highestBoundaryTokens: 0,
|
|
51
51
|
highestTier: 0,
|
|
52
52
|
terminalTierReached: false,
|
|
53
53
|
};
|
|
54
54
|
const rowState = createToolRowStateStore(pi, "context-pruning.tool-row-state");
|
|
55
|
-
pi.registerMessageRenderer<
|
|
55
|
+
pi.registerMessageRenderer<ContextPruningNudgeDetailsV3>(NUDGE_MESSAGE_TYPE, (message, _options, theme) =>
|
|
56
56
|
renderContextPruningNudge(message.details, theme),
|
|
57
57
|
);
|
|
58
58
|
|
|
@@ -98,7 +98,7 @@ export default function contextPruningExtension(pi: ExtensionAPI): void {
|
|
|
98
98
|
const settings = await loadTauExtensionSettings(ctx, contextPruningSettings);
|
|
99
99
|
if (generation !== lifecycleGeneration) return;
|
|
100
100
|
enabled = settings.enabled;
|
|
101
|
-
|
|
101
|
+
nudgeEveryTokens = settings.nudgeEveryTokens;
|
|
102
102
|
nudgeInstructions = settings.nudgeInstructions;
|
|
103
103
|
setContextPruningEnabled(enabled);
|
|
104
104
|
if (enabled && !toolRegistered) {
|
|
@@ -162,22 +162,22 @@ export default function contextPruningExtension(pi: ExtensionAPI): void {
|
|
|
162
162
|
commandContext.sessionManager.getBranch(),
|
|
163
163
|
true,
|
|
164
164
|
).latestAnchorToolCallId;
|
|
165
|
-
pi.sendMessage<
|
|
165
|
+
pi.sendMessage<ContextPruningNudgeDetailsV3>(
|
|
166
166
|
{
|
|
167
167
|
customType: NUDGE_MESSAGE_TYPE,
|
|
168
168
|
content: manualPruneSteeringMessage(),
|
|
169
169
|
display: true,
|
|
170
170
|
details: {
|
|
171
|
-
v:
|
|
171
|
+
v: 3,
|
|
172
172
|
kind: "manual",
|
|
173
|
-
|
|
174
|
-
|
|
173
|
+
tokens: null,
|
|
174
|
+
boundaryTokens: null,
|
|
175
175
|
reminder: null,
|
|
176
176
|
tier: null,
|
|
177
177
|
tierCount: null,
|
|
178
178
|
tierFloor: null,
|
|
179
179
|
anchorToolCallId: anchorToolCallId ?? null,
|
|
180
|
-
|
|
180
|
+
suppressedThroughTokens: null,
|
|
181
181
|
},
|
|
182
182
|
},
|
|
183
183
|
{ deliverAs: "steer", triggerTurn: true },
|
|
@@ -204,8 +204,8 @@ export default function contextPruningExtension(pi: ExtensionAPI): void {
|
|
|
204
204
|
visualRows.clear();
|
|
205
205
|
nudgeState = {
|
|
206
206
|
anchorToolCallId: undefined,
|
|
207
|
-
|
|
208
|
-
|
|
207
|
+
suppressedThroughTokens: 0,
|
|
208
|
+
highestBoundaryTokens: 0,
|
|
209
209
|
highestTier: 0,
|
|
210
210
|
terminalTierReached: false,
|
|
211
211
|
};
|
|
@@ -216,45 +216,44 @@ export default function contextPruningExtension(pi: ExtensionAPI): void {
|
|
|
216
216
|
pi.on("turn_end", (event, ctx) => {
|
|
217
217
|
if (!enabled || event.toolResults.length === 0) return undefined;
|
|
218
218
|
const usage = ctx.getContextUsage();
|
|
219
|
-
if (!usage || usage.
|
|
220
|
-
const
|
|
221
|
-
const percent = Math.floor(rawPercent);
|
|
219
|
+
if (!usage || usage.tokens === null || !Number.isFinite(usage.tokens)) return undefined;
|
|
220
|
+
const tokens = Math.max(0, Math.floor(usage.tokens));
|
|
222
221
|
const activeAnchor = replayContextPruningState(ctx.sessionManager.getBranch(), true).latestAnchorToolCallId;
|
|
223
222
|
if (activeAnchor !== nudgeState.anchorToolCallId) {
|
|
224
223
|
nudgeState = reconstructNudgeState(ctx.sessionManager.getBranch(), activeAnchor);
|
|
225
224
|
}
|
|
226
|
-
if (activeAnchor !== undefined && nudgeState.
|
|
227
|
-
const
|
|
225
|
+
if (activeAnchor !== undefined && nudgeState.suppressedThroughTokens === undefined) {
|
|
226
|
+
const suppressedThroughTokens = Math.floor(tokens / nudgeEveryTokens) * nudgeEveryTokens;
|
|
228
227
|
pi.appendEntry(NUDGE_BASELINE_ENTRY_TYPE, {
|
|
229
|
-
v:
|
|
228
|
+
v: 2,
|
|
230
229
|
anchorToolCallId: activeAnchor,
|
|
231
|
-
|
|
230
|
+
suppressedThroughTokens,
|
|
232
231
|
});
|
|
233
|
-
nudgeState.
|
|
232
|
+
nudgeState.suppressedThroughTokens = suppressedThroughTokens;
|
|
233
|
+
nudgeState.highestBoundaryTokens = suppressedThroughTokens;
|
|
234
234
|
return undefined;
|
|
235
235
|
}
|
|
236
|
-
const
|
|
237
|
-
const reminder = Math.floor((percent - baseline) / nudgeEveryPercent);
|
|
236
|
+
const reminder = Math.floor(tokens / nudgeEveryTokens);
|
|
238
237
|
if (reminder < 1) return undefined;
|
|
239
|
-
const
|
|
240
|
-
if (
|
|
238
|
+
const boundaryTokens = reminder * nudgeEveryTokens;
|
|
239
|
+
if (boundaryTokens <= nudgeState.highestBoundaryTokens) return undefined;
|
|
241
240
|
const tierCount = nudgeInstructions.length;
|
|
242
241
|
const tierFloor = nudgeState.terminalTierReached ? tierCount : Math.min(nudgeState.highestTier, tierCount);
|
|
243
242
|
const tier = Math.max(Math.min(reminder, tierCount), tierFloor);
|
|
244
243
|
const instruction = nudgeInstructions[tier - 1] ?? nudgeInstructions[0];
|
|
245
|
-
const details:
|
|
246
|
-
v:
|
|
244
|
+
const details: ContextPruningNudgeDetailsV3 = {
|
|
245
|
+
v: 3,
|
|
247
246
|
kind: "automatic",
|
|
248
|
-
|
|
249
|
-
|
|
247
|
+
tokens,
|
|
248
|
+
boundaryTokens,
|
|
250
249
|
reminder,
|
|
251
250
|
tier,
|
|
252
251
|
tierCount,
|
|
253
252
|
tierFloor,
|
|
254
253
|
anchorToolCallId: activeAnchor ?? null,
|
|
255
|
-
|
|
254
|
+
suppressedThroughTokens: nudgeState.suppressedThroughTokens ?? 0,
|
|
256
255
|
};
|
|
257
|
-
pi.sendMessage<
|
|
256
|
+
pi.sendMessage<ContextPruningNudgeDetailsV3>(
|
|
258
257
|
{
|
|
259
258
|
customType: NUDGE_MESSAGE_TYPE,
|
|
260
259
|
content: automaticPruneSteeringMessage(instruction, tier === tierCount),
|
|
@@ -263,7 +262,7 @@ export default function contextPruningExtension(pi: ExtensionAPI): void {
|
|
|
263
262
|
},
|
|
264
263
|
{ deliverAs: "steer" },
|
|
265
264
|
);
|
|
266
|
-
nudgeState.
|
|
265
|
+
nudgeState.highestBoundaryTokens = boundaryTokens;
|
|
267
266
|
nudgeState.highestTier = Math.max(nudgeState.highestTier, tier);
|
|
268
267
|
nudgeState.terminalTierReached ||= tier === tierCount;
|
|
269
268
|
return undefined;
|
|
@@ -289,8 +288,8 @@ function setsEqual(left: ReadonlySet<string>, right: ReadonlySet<string>): boole
|
|
|
289
288
|
}
|
|
290
289
|
|
|
291
290
|
function reconstructNudgeState(branch: readonly SessionEntry[], anchorToolCallId: string | undefined): NudgeState {
|
|
292
|
-
let
|
|
293
|
-
let
|
|
291
|
+
let suppressedThroughTokens = anchorToolCallId === undefined ? 0 : undefined;
|
|
292
|
+
let highestBoundaryTokens = 0;
|
|
294
293
|
let highestTier = 0;
|
|
295
294
|
let terminalTierReached = false;
|
|
296
295
|
let anchorResultIndex = -1;
|
|
@@ -310,65 +309,67 @@ function reconstructNudgeState(branch: readonly SessionEntry[], anchorToolCallId
|
|
|
310
309
|
const baseline = parseNudgeBaseline(entry.data);
|
|
311
310
|
if (
|
|
312
311
|
baseline &&
|
|
313
|
-
|
|
312
|
+
suppressedThroughTokens === undefined &&
|
|
314
313
|
index > anchorResultIndex &&
|
|
315
314
|
baseline.anchorToolCallId === anchorToolCallId
|
|
316
315
|
) {
|
|
317
|
-
|
|
316
|
+
suppressedThroughTokens = baseline.suppressedThroughTokens;
|
|
317
|
+
highestBoundaryTokens = baseline.suppressedThroughTokens;
|
|
318
318
|
}
|
|
319
319
|
continue;
|
|
320
320
|
}
|
|
321
321
|
if (entry.type !== "custom_message" || entry.customType !== NUDGE_MESSAGE_TYPE) continue;
|
|
322
|
-
const details =
|
|
322
|
+
const details = parseContextPruningNudgeDetailsV3(entry.details);
|
|
323
323
|
if (
|
|
324
324
|
!details ||
|
|
325
325
|
details.kind !== "automatic" ||
|
|
326
326
|
index <= anchorResultIndex ||
|
|
327
327
|
details.anchorToolCallId !== (anchorToolCallId ?? null) ||
|
|
328
|
-
details.
|
|
329
|
-
details.
|
|
330
|
-
(
|
|
328
|
+
details.boundaryTokens === null ||
|
|
329
|
+
details.suppressedThroughTokens === null ||
|
|
330
|
+
(suppressedThroughTokens !== undefined && details.suppressedThroughTokens !== suppressedThroughTokens)
|
|
331
331
|
)
|
|
332
332
|
continue;
|
|
333
333
|
const expectedTierFloor: number = terminalTierReached
|
|
334
334
|
? details.tierCount
|
|
335
335
|
: Math.min(highestTier, details.tierCount);
|
|
336
|
-
if (details.
|
|
337
|
-
|
|
336
|
+
if (details.boundaryTokens <= highestBoundaryTokens || details.tierFloor !== expectedTierFloor) continue;
|
|
337
|
+
highestBoundaryTokens = Math.max(highestBoundaryTokens, details.boundaryTokens);
|
|
338
338
|
highestTier = Math.max(highestTier, details.tier);
|
|
339
339
|
terminalTierReached ||= details.tier === details.tierCount;
|
|
340
|
-
|
|
340
|
+
suppressedThroughTokens = details.suppressedThroughTokens;
|
|
341
341
|
}
|
|
342
|
-
return { anchorToolCallId,
|
|
342
|
+
return { anchorToolCallId, suppressedThroughTokens, highestBoundaryTokens, highestTier, terminalTierReached };
|
|
343
343
|
}
|
|
344
344
|
|
|
345
|
-
function parseNudgeBaseline(
|
|
345
|
+
function parseNudgeBaseline(
|
|
346
|
+
value: unknown,
|
|
347
|
+
): { v: 2; anchorToolCallId: string; suppressedThroughTokens: number } | undefined {
|
|
346
348
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined;
|
|
347
349
|
const record = value as Record<string, unknown>;
|
|
348
350
|
if (
|
|
349
351
|
Object.keys(record).length !== 3 ||
|
|
350
352
|
!Object.hasOwn(record, "v") ||
|
|
351
353
|
!Object.hasOwn(record, "anchorToolCallId") ||
|
|
352
|
-
!Object.hasOwn(record, "
|
|
353
|
-
record.v !==
|
|
354
|
+
!Object.hasOwn(record, "suppressedThroughTokens") ||
|
|
355
|
+
record.v !== 2 ||
|
|
354
356
|
typeof record.anchorToolCallId !== "string" ||
|
|
355
357
|
record.anchorToolCallId.length === 0 ||
|
|
356
|
-
typeof record.
|
|
357
|
-
!Number.
|
|
358
|
-
record.
|
|
359
|
-
record.baselinePercent > 100
|
|
358
|
+
typeof record.suppressedThroughTokens !== "number" ||
|
|
359
|
+
!Number.isSafeInteger(record.suppressedThroughTokens) ||
|
|
360
|
+
record.suppressedThroughTokens < 0
|
|
360
361
|
)
|
|
361
362
|
return undefined;
|
|
362
363
|
return {
|
|
363
|
-
v:
|
|
364
|
+
v: 2,
|
|
364
365
|
anchorToolCallId: record.anchorToolCallId,
|
|
365
|
-
|
|
366
|
+
suppressedThroughTokens: record.suppressedThroughTokens,
|
|
366
367
|
};
|
|
367
368
|
}
|
|
368
369
|
|
|
369
370
|
function automaticPruneSteeringMessage(instruction: string, finalTier: boolean): string {
|
|
370
371
|
const silent =
|
|
371
|
-
"Internal context-management instruction. Follow it silently. Do not mention or acknowledge context
|
|
372
|
+
"Internal context-management instruction. Follow it silently. Do not mention or acknowledge context-token counts, prune messages, or internal context management.";
|
|
372
373
|
const protocol =
|
|
373
374
|
"When pruning, first preserve durable conclusions, user constraints, conditional relevance, and the next action in visible prose, then call context_prune.";
|
|
374
375
|
return finalTier
|
|
@@ -2,8 +2,8 @@ import { resolve } from "node:path";
|
|
|
2
2
|
import { type ExtensionContext, type SessionEntry } from "@earendil-works/pi-coding-agent";
|
|
3
3
|
import { type Static, Type } from "typebox";
|
|
4
4
|
import type { ContextPruneDeferredFileV2, ContextPruneDetailsV2 } from "../../shared/context-pruning-state.ts";
|
|
5
|
-
import { prepareAutoreadMessage, type PreparedAutoreadMessage } from "
|
|
6
|
-
import { MAX_COMPLETE_FILE_SNAPSHOT_BYTES } from "
|
|
5
|
+
import { prepareAutoreadMessage, type PreparedAutoreadMessage } from "../../shared/autoread.ts";
|
|
6
|
+
import { MAX_COMPLETE_FILE_SNAPSHOT_BYTES } from "../../shared/full-file-knowledge.ts";
|
|
7
7
|
|
|
8
8
|
export const contextPruneParameters = Type.Object(
|
|
9
9
|
{
|
|
@@ -10,48 +10,48 @@ const MAX_EXPANDED_LINE_CHARACTERS = 240;
|
|
|
10
10
|
const MAX_EXPANDED_TEXT_CHARACTERS = 3_000;
|
|
11
11
|
const MAX_WARNING_CHARACTERS = 1_000;
|
|
12
12
|
|
|
13
|
-
export type
|
|
13
|
+
export type ContextPruningNudgeDetailsV3 =
|
|
14
14
|
| {
|
|
15
|
-
v:
|
|
15
|
+
v: 3;
|
|
16
16
|
kind: "automatic";
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
tokens: number;
|
|
18
|
+
boundaryTokens: number;
|
|
19
19
|
reminder: number;
|
|
20
20
|
tier: number;
|
|
21
21
|
tierCount: number;
|
|
22
22
|
tierFloor: number;
|
|
23
23
|
anchorToolCallId: string | null;
|
|
24
|
-
|
|
24
|
+
suppressedThroughTokens: number;
|
|
25
25
|
}
|
|
26
26
|
| {
|
|
27
|
-
v:
|
|
27
|
+
v: 3;
|
|
28
28
|
kind: "manual";
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
tokens: null;
|
|
30
|
+
boundaryTokens: null;
|
|
31
31
|
reminder: null;
|
|
32
32
|
tier: null;
|
|
33
33
|
tierCount: null;
|
|
34
34
|
tierFloor: null;
|
|
35
35
|
anchorToolCallId: string | null;
|
|
36
|
-
|
|
36
|
+
suppressedThroughTokens: null;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
-
export function
|
|
39
|
+
export function parseContextPruningNudgeDetailsV3(value: unknown): ContextPruningNudgeDetailsV3 | undefined {
|
|
40
40
|
if (!isRecord(value)) return undefined;
|
|
41
41
|
const keys = [
|
|
42
42
|
"v",
|
|
43
43
|
"kind",
|
|
44
|
-
"
|
|
45
|
-
"
|
|
44
|
+
"tokens",
|
|
45
|
+
"boundaryTokens",
|
|
46
46
|
"reminder",
|
|
47
47
|
"tier",
|
|
48
48
|
"tierCount",
|
|
49
49
|
"tierFloor",
|
|
50
50
|
"anchorToolCallId",
|
|
51
|
-
"
|
|
51
|
+
"suppressedThroughTokens",
|
|
52
52
|
];
|
|
53
53
|
if (Object.keys(value).length !== keys.length || !keys.every((key) => Object.hasOwn(value, key))) return undefined;
|
|
54
|
-
if (value.v !==
|
|
54
|
+
if (value.v !== 3 || (value.kind !== "automatic" && value.kind !== "manual")) return undefined;
|
|
55
55
|
if (
|
|
56
56
|
value.anchorToolCallId !== null &&
|
|
57
57
|
(typeof value.anchorToolCallId !== "string" || value.anchorToolCallId.length === 0)
|
|
@@ -60,64 +60,69 @@ export function parseContextPruningNudgeDetailsV2(value: unknown): ContextPrunin
|
|
|
60
60
|
}
|
|
61
61
|
if (value.kind === "manual") {
|
|
62
62
|
if (
|
|
63
|
-
value.
|
|
64
|
-
value.
|
|
63
|
+
value.tokens !== null ||
|
|
64
|
+
value.boundaryTokens !== null ||
|
|
65
65
|
value.reminder !== null ||
|
|
66
66
|
value.tier !== null ||
|
|
67
67
|
value.tierCount !== null ||
|
|
68
68
|
value.tierFloor !== null ||
|
|
69
|
-
value.
|
|
69
|
+
value.suppressedThroughTokens !== null
|
|
70
70
|
)
|
|
71
71
|
return undefined;
|
|
72
72
|
return {
|
|
73
|
-
v:
|
|
73
|
+
v: 3,
|
|
74
74
|
kind: "manual",
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
tokens: null,
|
|
76
|
+
boundaryTokens: null,
|
|
77
77
|
reminder: null,
|
|
78
78
|
tier: null,
|
|
79
79
|
tierCount: null,
|
|
80
80
|
tierFloor: null,
|
|
81
81
|
anchorToolCallId: value.anchorToolCallId,
|
|
82
|
-
|
|
82
|
+
suppressedThroughTokens: null,
|
|
83
83
|
};
|
|
84
84
|
}
|
|
85
85
|
if (
|
|
86
|
-
!
|
|
87
|
-
!isBoundary(value.
|
|
86
|
+
!isTokenCount(value.tokens) ||
|
|
87
|
+
!isBoundary(value.boundaryTokens) ||
|
|
88
88
|
!isReminder(value.reminder) ||
|
|
89
89
|
!isTier(value.tier) ||
|
|
90
90
|
!isTier(value.tierCount) ||
|
|
91
91
|
!isTierFloor(value.tierFloor) ||
|
|
92
|
-
!
|
|
93
|
-
value.
|
|
94
|
-
value.
|
|
92
|
+
!isTokenCount(value.suppressedThroughTokens) ||
|
|
93
|
+
value.boundaryTokens > value.tokens ||
|
|
94
|
+
value.boundaryTokens <= value.suppressedThroughTokens ||
|
|
95
95
|
value.tier > value.tierCount ||
|
|
96
96
|
value.tierFloor > value.tierCount ||
|
|
97
97
|
value.tier !== Math.max(Math.min(value.reminder, value.tierCount), value.tierFloor) ||
|
|
98
|
-
(value.anchorToolCallId === null && value.
|
|
98
|
+
(value.anchorToolCallId === null && value.suppressedThroughTokens !== 0)
|
|
99
99
|
) {
|
|
100
100
|
return undefined;
|
|
101
101
|
}
|
|
102
|
-
const interval =
|
|
103
|
-
if (
|
|
102
|
+
const interval = value.boundaryTokens / value.reminder;
|
|
103
|
+
if (
|
|
104
|
+
!Number.isSafeInteger(interval) ||
|
|
105
|
+
interval < 1 ||
|
|
106
|
+
value.tokens - value.boundaryTokens >= interval ||
|
|
107
|
+
value.suppressedThroughTokens % interval !== 0
|
|
108
|
+
)
|
|
104
109
|
return undefined;
|
|
105
110
|
return {
|
|
106
|
-
v:
|
|
111
|
+
v: 3,
|
|
107
112
|
kind: "automatic",
|
|
108
|
-
|
|
109
|
-
|
|
113
|
+
tokens: value.tokens,
|
|
114
|
+
boundaryTokens: value.boundaryTokens,
|
|
110
115
|
reminder: value.reminder,
|
|
111
116
|
tier: value.tier,
|
|
112
117
|
tierCount: value.tierCount,
|
|
113
118
|
tierFloor: value.tierFloor,
|
|
114
119
|
anchorToolCallId: value.anchorToolCallId,
|
|
115
|
-
|
|
120
|
+
suppressedThroughTokens: value.suppressedThroughTokens,
|
|
116
121
|
};
|
|
117
122
|
}
|
|
118
123
|
|
|
119
124
|
export function renderContextPruningNudge(details: unknown, theme: Theme): Marker | undefined {
|
|
120
|
-
const parsed =
|
|
125
|
+
const parsed = parseContextPruningNudgeDetailsV3(details);
|
|
121
126
|
if (!parsed) return undefined;
|
|
122
127
|
return new Marker({
|
|
123
128
|
theme,
|
|
@@ -127,7 +132,7 @@ export function renderContextPruningNudge(details: unknown, theme: Theme): Marke
|
|
|
127
132
|
parsed.kind === "manual"
|
|
128
133
|
? ["Prune requested."]
|
|
129
134
|
: [
|
|
130
|
-
|
|
135
|
+
formatTokens(parsed.tokens),
|
|
131
136
|
...(parsed.tier === parsed.tierCount ? ["Prune now."] : parsed.tier > 1 ? ["Prune soon."] : []),
|
|
132
137
|
],
|
|
133
138
|
});
|
|
@@ -214,16 +219,23 @@ function firstResultText(result: AgentToolResult<unknown>): string {
|
|
|
214
219
|
return "";
|
|
215
220
|
}
|
|
216
221
|
|
|
217
|
-
function
|
|
218
|
-
|
|
222
|
+
function formatTokens(count: number): string {
|
|
223
|
+
if (count < 1_000) return `${count}`;
|
|
224
|
+
if (count < 10_000) return `${(count / 1_000).toFixed(1)}k`;
|
|
225
|
+
if (count < 1_000_000) return `${Math.round(count / 1_000)}k`;
|
|
226
|
+
return `${(count / 1_000_000).toFixed(1)}M`;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function isTokenCount(value: unknown): value is number {
|
|
230
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
219
231
|
}
|
|
220
232
|
|
|
221
233
|
function isBoundary(value: unknown): value is number {
|
|
222
|
-
return
|
|
234
|
+
return isTokenCount(value) && value > 0;
|
|
223
235
|
}
|
|
224
236
|
|
|
225
237
|
function isReminder(value: unknown): value is number {
|
|
226
|
-
return typeof value === "number" && Number.
|
|
238
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 1;
|
|
227
239
|
}
|
|
228
240
|
|
|
229
241
|
function isTier(value: unknown): value is number {
|
|
@@ -11,18 +11,17 @@ export default defineTauExtensionSettings({
|
|
|
11
11
|
key: "contextPruning",
|
|
12
12
|
defaults: {
|
|
13
13
|
enabled: true as boolean,
|
|
14
|
-
|
|
14
|
+
nudgeEveryTokens: 30_000 as number,
|
|
15
15
|
nudgeInstructions: DEFAULT_NUDGE_INSTRUCTIONS,
|
|
16
16
|
},
|
|
17
17
|
schema: Type.Object(
|
|
18
18
|
{
|
|
19
19
|
enabled: Type.Optional(Type.Boolean({ default: true, description: "Enable context pruning." })),
|
|
20
|
-
|
|
20
|
+
nudgeEveryTokens: Type.Optional(
|
|
21
21
|
Type.Integer({
|
|
22
|
-
default:
|
|
22
|
+
default: 30_000,
|
|
23
23
|
minimum: 1,
|
|
24
|
-
|
|
25
|
-
description: "Context growth interval between automatic pruning hints.",
|
|
24
|
+
description: "Active-context token interval between automatic pruning hints.",
|
|
26
25
|
}),
|
|
27
26
|
),
|
|
28
27
|
nudgeInstructions: Type.Optional(
|
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
# Explore
|
|
2
2
|
|
|
3
|
-
Explore
|
|
3
|
+
Explore gives Tau 12 structural source tools: outlines, declaration slices, discovery, structural search, graph and relationship queries, impact, and context packs. Targets use path + declaration name, with an optional line to disambiguate.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Pi keeps ordinary filesystem tools (`ls`, `find`, `grep`, `read`). Explore adds structure on top of supported source languages via in-process tree-sitter (WASM) on every platform supported by the Node runtime. Registered languages share the same tools and exploration workflow.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Large `read` / autoread
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
When `explore.read.enabled` is on (default), a full Pi `read` or autoread of a registered source file (including Markdown) above `explore.read.structureThresholdLines` (default 200) returns an **outline** — declarations or headings — not the full body. Use ranged `read` (`offset`/`limit`, capped by `explore.read.maxRangeLines`) or `show` for bodies and sections. Small files and unregistered paths stay ordinary Pi full text. Set `explore.read.enabled` to `false` to turn the overlay off.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
## Tools
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
7. Use ranged or whole-file `read` for cross-cutting logic, exact formatting, or parser gaps.
|
|
13
|
+
- `outline` — declarations and structure for a file, one-level directory, or recursive subtree (no bodies).
|
|
14
|
+
- `show` — exact signature / docs / declaration / declaration+imports for path+name targets.
|
|
15
|
+
- `discover` — find reusable declarations across a repo/package/subtree by name, kind, or docs (signatures only).
|
|
16
|
+
- `deps` / `reverse_deps` — file import graph forward and reverse.
|
|
17
|
+
- `callers` / `callees` / `references` / `implementations` — symbol relationship sites.
|
|
18
|
+
- `impact` — blast radius: symbol callees/callers plus file imports/importers/transitive dependents.
|
|
19
|
+
- `context` — budgeted pack of bodies/signatures around one symbol.
|
|
20
|
+
- `ast_search` — structural pattern search (ast-grep) over supported source.
|