@shanepadgett/tau-agent 0.33.1 → 0.35.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/context.md +29 -20
- package/docs/extending-tau-agent.md +63 -24
- package/extensions/appshot/index.ts +112 -111
- package/extensions/aside/README.md +7 -0
- package/extensions/aside/index.ts +168 -0
- package/extensions/aside/panel.ts +83 -0
- package/extensions/bash-approval/README.md +20 -0
- package/extensions/bash-approval/index.ts +235 -0
- package/extensions/bash-approval/settings.ts +24 -0
- package/extensions/branch/index.ts +38 -21
- package/extensions/cache-diagnostics/index.ts +70 -58
- package/extensions/checkpoint/README.md +9 -0
- package/extensions/checkpoint/checkpoint-budget.ts +66 -0
- package/extensions/checkpoint/checkpoint.ts +279 -0
- package/extensions/checkpoint/index.ts +102 -0
- package/extensions/checkpoint/messages.ts +170 -0
- package/extensions/checkpoint/prompt.ts +24 -0
- package/extensions/checkpoint/settings.ts +27 -0
- package/extensions/commit/commit-effort.ts +3 -3
- package/extensions/commit/commit-plan.ts +2 -2
- package/extensions/context/README.md +12 -8
- package/extensions/context/definitions.ts +178 -55
- package/extensions/context/index.ts +192 -160
- package/extensions/context/panel.ts +57 -37
- package/extensions/context/sync.ts +139 -89
- package/extensions/context/validation.ts +37 -25
- package/extensions/effort/README.md +1 -1
- package/extensions/effort/index.ts +3 -3
- package/extensions/effort/state.ts +3 -18
- package/extensions/explore/guidance.ts +6 -47
- package/extensions/explore/index.ts +38 -53
- package/extensions/explore/{ast/read → read}/hook.ts +12 -7
- package/extensions/explore/{ast/tools → tools}/ast-search.ts +15 -19
- package/extensions/explore/{ast/tools → tools}/context.ts +16 -23
- package/extensions/explore/{ast/tools → tools}/deps.ts +11 -15
- package/extensions/explore/{ast/tools → tools}/discover.ts +15 -17
- package/extensions/explore/{ast/tools → tools}/impact.ts +20 -23
- package/extensions/explore/{ast/tools → tools}/outline.ts +21 -24
- package/extensions/explore/{ast/tools → tools}/relationships.ts +23 -32
- package/extensions/explore/{ast/tools → tools}/render.ts +1 -1
- package/extensions/explore/{ast/tools → tools}/reverse-deps.ts +11 -15
- package/extensions/explore/{ast/tools → tools}/show.ts +16 -19
- package/extensions/explore/tools/target-params.ts +9 -0
- package/extensions/footer/index.ts +75 -56
- package/extensions/handoff/index.ts +8 -30
- package/extensions/image-gen/index.ts +46 -40
- package/extensions/patch/executor.ts +190 -135
- package/extensions/patch/matcher.ts +82 -48
- package/extensions/patch/parser.ts +149 -105
- package/extensions/patch/render.ts +32 -39
- package/extensions/patch/summary.ts +30 -26
- package/extensions/ready/README.md +7 -0
- package/extensions/ready/index.ts +58 -0
- package/extensions/ready/model.ts +128 -0
- package/extensions/ready/packs.ts +272 -0
- package/extensions/ready/render.ts +445 -0
- package/extensions/ready/scan.ts +760 -0
- package/extensions/reference/index.ts +2 -0
- package/extensions/reference/panel.ts +94 -31
- package/extensions/review/README.md +14 -6
- package/extensions/review/index.ts +58 -99
- package/extensions/review/model.ts +13 -33
- package/extensions/review/session.ts +9 -23
- package/extensions/runtime-context/context.ts +30 -26
- package/extensions/script-runner/README.md +2 -2
- package/extensions/script-runner/index.ts +122 -82
- package/extensions/silent-command-runner/index.ts +38 -23
- package/extensions/soul/README.md +2 -2
- package/extensions/soul/index.ts +4 -4
- package/extensions/soul/prompt.ts +6 -10
- package/extensions/soul/settings.ts +6 -3
- package/extensions/subagent/agents/context-sync.md +185 -33
- package/extensions/subagent/agents/scout.md +2 -2
- package/extensions/subagent/agents/web-research.md +2 -2
- package/extensions/subagent/agents.ts +137 -84
- package/extensions/subagent/cmux-dashboard.ts +39 -30
- package/extensions/subagent/index.ts +79 -59
- package/extensions/subagent/resume.ts +1 -1
- package/extensions/subagent/run.ts +308 -193
- package/extensions/subagent/runtime.ts +781 -343
- package/extensions/tau/index.ts +47 -38
- package/extensions/tau-help/help.md +20 -8
- package/extensions/tool-loader/README.md +4 -2
- package/extensions/tool-loader/index.ts +77 -52
- package/extensions/web/index.ts +6 -3
- package/extensions/web/webfetch.ts +114 -86
- package/package.json +3 -3
- package/schemas/tau.schema.json +38 -35
- package/shared/bounded-text-result.ts +1 -0
- package/shared/checkpoint-visibility.ts +9 -0
- package/shared/events.ts +28 -10
- package/shared/isolated-session.ts +72 -30
- package/shared/model-effort.ts +23 -22
- package/shared/model-fallback/index.ts +66 -41
- package/shared/settings/load.ts +45 -38
- package/shared/temporary-output-store.ts +16 -10
- package/{extensions/explore → src}/ast/adapter.ts +11 -2
- package/{extensions/explore → src}/ast/engine.ts +54 -38
- package/{extensions/explore → src}/ast/format/ast-search.ts +15 -6
- package/{extensions/explore → src}/ast/format/context.ts +3 -6
- package/src/ast/format/deps.ts +94 -0
- package/{extensions/explore → src}/ast/format/discover.ts +2 -2
- package/{extensions/explore → src}/ast/format/impact.ts +9 -11
- package/{extensions/explore → src}/ast/format/outline.ts +2 -2
- package/{extensions/explore → src}/ast/format/relationships.ts +11 -7
- package/{extensions/explore → src}/ast/format/show.ts +1 -1
- package/src/ast/format/targeted-result.ts +20 -0
- package/src/ast/grammars/c_sharp.wasm +0 -0
- package/{extensions/explore → src}/ast/grammars/manifest.json +9 -12
- package/{extensions/explore → src}/ast/grammars/manifest.ts +0 -4
- package/{extensions/explore → src}/ast/graph/file-graph.ts +210 -98
- package/{extensions/explore → src}/ast/graph/relationships.ts +285 -72
- package/{extensions/explore → src}/ast/ir.ts +6 -0
- package/src/ast/languages/csharp-file-deps.ts +90 -0
- package/{extensions/explore → src}/ast/languages/csharp.ts +9 -40
- package/src/ast/languages/dotted-index.ts +131 -0
- package/{extensions/explore → src}/ast/languages/file-dep-util.ts +22 -13
- package/src/ast/languages/fixtures/sample.cs +53 -0
- package/src/ast/languages/fixtures/sample.cs.expected.json +156 -0
- package/src/ast/languages/fixtures/sample.go +37 -0
- package/src/ast/languages/fixtures/sample.go.expected.json +96 -0
- package/src/ast/languages/fixtures/sample.java +40 -0
- package/src/ast/languages/fixtures/sample.java.expected.json +126 -0
- package/src/ast/languages/fixtures/sample.kt +31 -0
- package/src/ast/languages/fixtures/sample.kt.expected.json +134 -0
- package/src/ast/languages/fixtures/sample.md +23 -0
- package/src/ast/languages/fixtures/sample.md.expected.json +44 -0
- package/src/ast/languages/fixtures/sample.rs +54 -0
- package/src/ast/languages/fixtures/sample.rs.expected.json +178 -0
- package/src/ast/languages/fixtures/sample.swift +45 -0
- package/src/ast/languages/fixtures/sample.swift.expected.json +147 -0
- package/src/ast/languages/fixtures/sample.ts +45 -0
- package/src/ast/languages/fixtures/sample.ts.expected.json +141 -0
- package/src/ast/languages/fixtures/sample.tsx +36 -0
- package/src/ast/languages/fixtures/sample.tsx.expected.json +59 -0
- package/{extensions/explore → src}/ast/languages/go-file-deps.ts +6 -0
- package/{extensions/explore → src}/ast/languages/go.ts +21 -3
- package/{extensions/explore → src}/ast/languages/java.ts +5 -38
- package/src/ast/languages/jvm-file-deps.ts +135 -0
- package/{extensions/explore → src}/ast/languages/kotlin.ts +5 -39
- package/{extensions/explore → src}/ast/languages/rust-file-deps.ts +3 -3
- package/{extensions/explore → src}/ast/languages/rust.ts +21 -13
- package/{extensions/explore → src}/ast/languages/swift-file-deps.ts +22 -15
- package/{extensions/explore → src}/ast/languages/swift.ts +9 -39
- package/{extensions/explore → src}/ast/languages/tree.ts +47 -0
- package/{extensions/explore → src}/ast/languages/typescript-file-deps.ts +108 -0
- package/{extensions/explore → src}/ast/languages/typescript-package-surface.ts +4 -3
- package/{extensions/explore → src}/ast/languages/typescript.ts +3 -5
- package/{extensions/explore → src}/ast/markdown.ts +72 -47
- package/{extensions/explore → src}/ast/queries/ast-search.ts +164 -146
- package/{extensions/explore → src}/ast/queries/composite-target.ts +1 -1
- package/{extensions/explore → src}/ast/queries/context.ts +116 -80
- package/{extensions/explore → src}/ast/queries/discover.ts +154 -105
- package/src/ast/queries/impact.ts +291 -0
- package/{extensions/explore → src}/ast/queries/outline.ts +4 -2
- package/{extensions/explore → src}/ast/queries/show.ts +46 -42
- package/{extensions/explore → src}/ast/query.ts +1 -1
- package/{extensions/explore → src}/ast/registry.ts +1 -2
- package/{extensions/explore → src}/ast/scan.ts +1 -1
- package/src/ast/session.ts +50 -0
- package/{extensions/explore → src}/ast/slice.ts +8 -0
- package/{extensions/explore → src/ast}/traverse.ts +32 -20
- package/src/file-injection/index.ts +547 -0
- package/src/image-generation/client.ts +50 -31
- package/src/image-generation/index.ts +177 -134
- package/src/index.ts +2 -0
- package/src/tool-loading/index.ts +58 -0
- package/extensions/context/evidence.ts +0 -517
- package/extensions/context/projection.ts +0 -141
- package/extensions/context/state.ts +0 -30
- package/extensions/context/write-scope.ts +0 -109
- package/extensions/explore/ast/format/deps.ts +0 -86
- package/extensions/explore/ast/grammars/odin.wasm +0 -0
- package/extensions/explore/ast/languages/csharp-file-deps.ts +0 -189
- package/extensions/explore/ast/languages/fixtures/sample.cs +0 -17
- package/extensions/explore/ast/languages/fixtures/sample.java +0 -21
- package/extensions/explore/ast/languages/fixtures/sample.kt +0 -15
- package/extensions/explore/ast/languages/fixtures/sample.odin +0 -28
- package/extensions/explore/ast/languages/fixtures/sample.rs +0 -31
- package/extensions/explore/ast/languages/fixtures/sample.swift +0 -25
- package/extensions/explore/ast/languages/jvm-file-deps.ts +0 -217
- package/extensions/explore/ast/languages/odin-file-deps.ts +0 -175
- package/extensions/explore/ast/languages/odin.ts +0 -407
- package/extensions/explore/ast/queries/impact.ts +0 -250
- package/extensions/explore/outline-injection.ts +0 -151
- package/extensions/explore/read/autoread.ts +0 -118
- package/extensions/review/panel.ts +0 -128
- package/extensions/working-memory/README.md +0 -17
- package/extensions/working-memory/checkpoint.ts +0 -242
- package/extensions/working-memory/index.ts +0 -341
- package/extensions/working-memory/memory.ts +0 -235
- package/extensions/working-memory/render.ts +0 -185
- package/extensions/working-memory/settings.ts +0 -38
- package/extensions/working-memory/state.ts +0 -158
- package/shared/autoread.ts +0 -234
- package/shared/context-messages.ts +0 -19
- package/shared/outline-injection.ts +0 -56
- /package/{extensions/explore → src}/ast/format/composite.ts +0 -0
- /package/{extensions/explore → src}/ast/grammars/kotlin.wasm +0 -0
- /package/{extensions/explore → src}/ast/grammars/swift.wasm +0 -0
- /package/{extensions/explore → src}/ast/identity.ts +0 -0
- /package/{extensions/explore → src}/ast/package-surface.ts +0 -0
- /package/{extensions/explore/ast/read/policy.ts → src/ast/read-policy.ts} +0 -0
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import type { ExtensionAPI, Theme } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { Text } from "@earendil-works/pi-tui";
|
|
3
|
-
import { Marker } from "@shanepadgett/tau-tui";
|
|
4
|
-
import { BoundedTextResultBuilder } from "../../shared/bounded-text-result.ts";
|
|
5
|
-
import {
|
|
6
|
-
registerOutlineInjectionProvider,
|
|
7
|
-
type OutlineInjectionDetails,
|
|
8
|
-
type PreparedOutlineInjection,
|
|
9
|
-
} from "../../shared/outline-injection.ts";
|
|
10
|
-
import type { TemporaryOutputStore } from "../../shared/temporary-output-store.ts";
|
|
11
|
-
import type { ToolRowStateStore } from "../../shared/tool-row-state.ts";
|
|
12
|
-
import type { ExploreEngine } from "./ast/engine.ts";
|
|
13
|
-
import { formatOutlineEmpty, formatOutlineFile } from "./ast/format/outline.ts";
|
|
14
|
-
import { outlinePath } from "./ast/queries/outline.ts";
|
|
15
|
-
import { formatPathForDisplay, stripLeadingAt } from "./traverse.ts";
|
|
16
|
-
|
|
17
|
-
const OPTIONS = { includePrivate: false, includeDocs: false, names: [] as readonly string[] };
|
|
18
|
-
|
|
19
|
-
export function registerExploreOutlineInjection(
|
|
20
|
-
pi: ExtensionAPI,
|
|
21
|
-
rowState: ToolRowStateStore,
|
|
22
|
-
temporaryOutput: TemporaryOutputStore,
|
|
23
|
-
engineFor: (cwd: string) => ExploreEngine,
|
|
24
|
-
): void {
|
|
25
|
-
registerOutlineInjectionProvider(pi, async (request) => {
|
|
26
|
-
const messages: PreparedOutlineInjection[] = [];
|
|
27
|
-
const warnings: string[] = [];
|
|
28
|
-
const engine = engineFor(request.cwd);
|
|
29
|
-
for (let index = 0; index < request.paths.length; index += 1) {
|
|
30
|
-
const path = stripLeadingAt(request.paths[index] ?? "");
|
|
31
|
-
assertCurrent(request.signal, request.isLifecycleCurrent);
|
|
32
|
-
try {
|
|
33
|
-
const abort = request.signal ?? new AbortController().signal;
|
|
34
|
-
const result = await outlinePath(engine, path, OPTIONS, abort);
|
|
35
|
-
if (result.mode !== "file") throw new Error("Working-memory outlines require a file path");
|
|
36
|
-
const body =
|
|
37
|
-
result.file.rows.length === 0
|
|
38
|
-
? formatOutlineEmpty(OPTIONS.names)
|
|
39
|
-
: formatOutlineFile(result.file, engine.cwd, false);
|
|
40
|
-
const builder = new BoundedTextResultBuilder(temporaryOutput, "completeBlocks");
|
|
41
|
-
let content: string;
|
|
42
|
-
try {
|
|
43
|
-
await builder.appendBlock(
|
|
44
|
-
result.file.path,
|
|
45
|
-
formatPathForDisplay(result.file.path, engine.cwd),
|
|
46
|
-
`${path}\n${body}`,
|
|
47
|
-
);
|
|
48
|
-
assertCurrent(request.signal, request.isLifecycleCurrent);
|
|
49
|
-
content = (await builder.finish()).content;
|
|
50
|
-
} catch (error) {
|
|
51
|
-
await builder.abort();
|
|
52
|
-
throw error;
|
|
53
|
-
}
|
|
54
|
-
messages.push({
|
|
55
|
-
customType: "tau.explore.outline" as const,
|
|
56
|
-
content,
|
|
57
|
-
display: true as const,
|
|
58
|
-
details: {
|
|
59
|
-
v: 1 as const,
|
|
60
|
-
rowId: `${request.batchId}:${index}`,
|
|
61
|
-
path,
|
|
62
|
-
cwd: request.cwd,
|
|
63
|
-
batchId: request.batchId,
|
|
64
|
-
},
|
|
65
|
-
});
|
|
66
|
-
} catch (error) {
|
|
67
|
-
if (request.signal?.aborted || !request.isLifecycleCurrent()) throw error;
|
|
68
|
-
warnings.push(`${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return { messages, warnings };
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
pi.registerMessageRenderer<OutlineInjectionDetails>("tau.explore.outline", (message, options, theme) => {
|
|
75
|
-
const details = parseDetails(message.details);
|
|
76
|
-
if (!details) return undefined;
|
|
77
|
-
return new OutlineMessageComponent(
|
|
78
|
-
rowState,
|
|
79
|
-
details.rowId,
|
|
80
|
-
details.path,
|
|
81
|
-
typeof message.content === "string" ? message.content : "",
|
|
82
|
-
options.expanded,
|
|
83
|
-
theme,
|
|
84
|
-
);
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function assertCurrent(signal: AbortSignal | undefined, isLifecycleCurrent: () => boolean): void {
|
|
89
|
-
signal?.throwIfAborted();
|
|
90
|
-
if (!isLifecycleCurrent()) throw new Error("Outline preparation crossed a session lifecycle boundary");
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function parseDetails(value: unknown): OutlineInjectionDetails | undefined {
|
|
94
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
|
|
95
|
-
const details = value as Record<string, unknown>;
|
|
96
|
-
if (
|
|
97
|
-
details.v !== 1 ||
|
|
98
|
-
typeof details.rowId !== "string" ||
|
|
99
|
-
typeof details.path !== "string" ||
|
|
100
|
-
typeof details.cwd !== "string" ||
|
|
101
|
-
typeof details.batchId !== "string"
|
|
102
|
-
) {
|
|
103
|
-
return undefined;
|
|
104
|
-
}
|
|
105
|
-
return {
|
|
106
|
-
v: 1,
|
|
107
|
-
rowId: details.rowId,
|
|
108
|
-
path: details.path,
|
|
109
|
-
cwd: details.cwd,
|
|
110
|
-
batchId: details.batchId,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
class OutlineMessageComponent {
|
|
115
|
-
private readonly rowState: ToolRowStateStore;
|
|
116
|
-
private readonly rowId: string;
|
|
117
|
-
private readonly path: string;
|
|
118
|
-
private readonly content: string;
|
|
119
|
-
private readonly expanded: boolean;
|
|
120
|
-
private readonly theme: Theme;
|
|
121
|
-
|
|
122
|
-
constructor(
|
|
123
|
-
rowState: ToolRowStateStore,
|
|
124
|
-
rowId: string,
|
|
125
|
-
path: string,
|
|
126
|
-
content: string,
|
|
127
|
-
expanded: boolean,
|
|
128
|
-
theme: Theme,
|
|
129
|
-
) {
|
|
130
|
-
this.rowState = rowState;
|
|
131
|
-
this.rowId = rowId;
|
|
132
|
-
this.path = path;
|
|
133
|
-
this.content = content;
|
|
134
|
-
this.expanded = expanded;
|
|
135
|
-
this.theme = theme;
|
|
136
|
-
this.rowState.watch(this.rowId, () => this.invalidate());
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
render(width: number): string[] {
|
|
140
|
-
const marker = new Marker({
|
|
141
|
-
theme: this.theme,
|
|
142
|
-
state: this.rowState.get(this.rowId) === "pruned" ? "warning" : "complete",
|
|
143
|
-
label: "outline",
|
|
144
|
-
parts: [this.path],
|
|
145
|
-
}).render(width);
|
|
146
|
-
if (!this.expanded) return marker;
|
|
147
|
-
return [...marker, ...new Text(this.theme.fg("dim", this.content), 1, 0).render(width)];
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
invalidate(): void {}
|
|
151
|
-
}
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { resolve } from "node:path";
|
|
3
|
-
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
4
|
-
import { prepareAutoreadMessage, registerAutoread, type PreparedAutoreadMessage } from "../../../shared/autoread.ts";
|
|
5
|
-
import { createCompleteFileMeta } from "../../../shared/full-file-knowledge.ts";
|
|
6
|
-
import { loadTauExtensionSettings } from "../../../shared/settings/load.ts";
|
|
7
|
-
import type { ToolRowStateStore } from "../../../shared/tool-row-state.ts";
|
|
8
|
-
import type { ExploreEngine } from "../ast/engine.ts";
|
|
9
|
-
import { formatOutlineFile } from "../ast/format/outline.ts";
|
|
10
|
-
import { formatLargeReadOutline, shouldOutlineFullRead, type ExploreReadSettings } from "../ast/read/policy.ts";
|
|
11
|
-
import { outlinePath } from "../ast/queries/outline.ts";
|
|
12
|
-
import exploreSettings from "../settings.ts";
|
|
13
|
-
import { resolveExplorePath } from "../traverse.ts";
|
|
14
|
-
|
|
15
|
-
const DEFAULT_OUTLINE_OPTIONS = {
|
|
16
|
-
includePrivate: false,
|
|
17
|
-
includeDocs: false,
|
|
18
|
-
names: [] as readonly string[],
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export function registerExploreAutoread(
|
|
22
|
-
pi: ExtensionAPI,
|
|
23
|
-
rowState: ToolRowStateStore,
|
|
24
|
-
engineFor: (cwd: string) => ExploreEngine,
|
|
25
|
-
): void {
|
|
26
|
-
let readSettings: ExploreReadSettings = exploreSettings.defaults.read;
|
|
27
|
-
|
|
28
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
29
|
-
const settings = await loadTauExtensionSettings(ctx, exploreSettings);
|
|
30
|
-
readSettings = settings.read;
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
registerAutoread(pi, rowState, async (options) => {
|
|
34
|
-
return prepareExploreAutoreadMessage({
|
|
35
|
-
...options,
|
|
36
|
-
engine: engineFor(options.cwd),
|
|
37
|
-
readSettings,
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function prepareExploreAutoreadMessage(options: {
|
|
43
|
-
rowId: string;
|
|
44
|
-
path: string;
|
|
45
|
-
cwd: string;
|
|
46
|
-
source: string;
|
|
47
|
-
batchId: string;
|
|
48
|
-
signal: AbortSignal | undefined;
|
|
49
|
-
isLifecycleCurrent: () => boolean;
|
|
50
|
-
maximumBytes?: number;
|
|
51
|
-
engine: ExploreEngine;
|
|
52
|
-
readSettings: ExploreReadSettings;
|
|
53
|
-
}): Promise<PreparedAutoreadMessage> {
|
|
54
|
-
const { engine, readSettings, ...shared } = options;
|
|
55
|
-
if (!readSettings.enabled) return prepareAutoreadMessage(shared);
|
|
56
|
-
|
|
57
|
-
const absolutePath = resolveExplorePath(options.cwd, options.path);
|
|
58
|
-
if (engine.registry.adapterForPath(absolutePath) === undefined) {
|
|
59
|
-
return prepareAutoreadMessage(shared);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
assertPreparationCurrent(options.signal, options.isLifecycleCurrent);
|
|
63
|
-
|
|
64
|
-
let lineCount: number;
|
|
65
|
-
try {
|
|
66
|
-
const source = await engine.sourceForFile(absolutePath);
|
|
67
|
-
lineCount = source.ir.lineCount;
|
|
68
|
-
} catch {
|
|
69
|
-
return prepareAutoreadMessage(shared);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (!shouldOutlineFullRead(lineCount, readSettings.structureThresholdLines)) {
|
|
73
|
-
return prepareAutoreadMessage(shared);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const abort = options.signal ?? new AbortController().signal;
|
|
77
|
-
const result = await outlinePath(engine, options.path, DEFAULT_OUTLINE_OPTIONS, abort);
|
|
78
|
-
assertPreparationCurrent(options.signal, options.isLifecycleCurrent);
|
|
79
|
-
if (result.mode !== "file") {
|
|
80
|
-
return prepareAutoreadMessage(shared);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const outlineBody =
|
|
84
|
-
result.file.rows.length === 0 ? "No declarations" : formatOutlineFile(result.file, engine.cwd, false);
|
|
85
|
-
const outlineText = formatLargeReadOutline(outlineBody);
|
|
86
|
-
const messageContent = `${options.path}\n${outlineText}`;
|
|
87
|
-
const pathKey = resolve(options.cwd, options.path);
|
|
88
|
-
const readCache = createCompleteFileMeta({
|
|
89
|
-
pathKey,
|
|
90
|
-
presentation: "plain",
|
|
91
|
-
servedHash: createHash("sha256").update(outlineText, "utf8").digest("hex"),
|
|
92
|
-
mode: "baseline",
|
|
93
|
-
sourceText: outlineText,
|
|
94
|
-
returnedText: messageContent,
|
|
95
|
-
totalLines: lineCount,
|
|
96
|
-
summary: `outline (${lineCount} lines)`,
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
return {
|
|
100
|
-
customType: "tau.autoread",
|
|
101
|
-
content: messageContent,
|
|
102
|
-
display: true,
|
|
103
|
-
details: {
|
|
104
|
-
rowId: options.rowId,
|
|
105
|
-
path: options.path,
|
|
106
|
-
cwd: options.cwd,
|
|
107
|
-
source: options.source,
|
|
108
|
-
batchId: options.batchId,
|
|
109
|
-
status: "read",
|
|
110
|
-
readCache,
|
|
111
|
-
},
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function assertPreparationCurrent(signal: AbortSignal | undefined, isLifecycleCurrent: () => boolean): void {
|
|
116
|
-
signal?.throwIfAborted();
|
|
117
|
-
if (!isLifecycleCurrent()) throw new Error("Autoread preparation crossed a session lifecycle boundary");
|
|
118
|
-
}
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import { bindingHint, bindingsHint, rawHint, ToolPanel } from "@shanepadgett/tau-tui";
|
|
2
|
-
import { getMarkdownTheme, type Theme } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import { Markdown, type Component, type KeybindingsManager, truncateToWidth, type TUI } from "@earendil-works/pi-tui";
|
|
4
|
-
import { formatReviewMarkdown, reviewModeLabel, type ReviewMode, type ReviewRecord } from "./model.ts";
|
|
5
|
-
|
|
6
|
-
export class ReviewProgressPanel implements Component {
|
|
7
|
-
private readonly tui: TUI;
|
|
8
|
-
private readonly panel: ToolPanel;
|
|
9
|
-
private readonly lines: string[] = [];
|
|
10
|
-
|
|
11
|
-
constructor(tui: TUI, theme: Theme, mode: ReviewMode, keys: KeybindingsManager, cancel: () => void) {
|
|
12
|
-
this.tui = tui;
|
|
13
|
-
this.panel = new ToolPanel(theme, {
|
|
14
|
-
title: `${reviewModeLabel(mode)} review`,
|
|
15
|
-
secondary: "Fresh isolated review · hidden from parent context",
|
|
16
|
-
body: {
|
|
17
|
-
render: (width) =>
|
|
18
|
-
(this.lines.length ? this.lines.slice(-5) : ["Starting review"]).map((line) =>
|
|
19
|
-
truncateToWidth(theme.fg("muted", line), width, "…"),
|
|
20
|
-
),
|
|
21
|
-
invalidate() {},
|
|
22
|
-
},
|
|
23
|
-
footer: { kind: "hints", hints: [bindingHint("tui.select.cancel", "cancel")] },
|
|
24
|
-
border: "box",
|
|
25
|
-
});
|
|
26
|
-
this.handleInput = (data) => {
|
|
27
|
-
if (!keys.matches(data, "tui.select.cancel")) return;
|
|
28
|
-
cancel();
|
|
29
|
-
this.update("Cancelling review");
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
handleInput: (data: string) => void;
|
|
34
|
-
|
|
35
|
-
update(status: string): void {
|
|
36
|
-
if (this.lines.at(-1) !== status) {
|
|
37
|
-
this.lines.push(status);
|
|
38
|
-
if (this.lines.length > 5) this.lines.shift();
|
|
39
|
-
}
|
|
40
|
-
this.tui.requestRender();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
render(width: number): string[] {
|
|
44
|
-
return this.panel.render(width);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
invalidate(): void {
|
|
48
|
-
this.panel.invalidate();
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export type ReviewResultAction = "send" | "export" | "close";
|
|
53
|
-
|
|
54
|
-
export class ReviewResultPanel implements Component {
|
|
55
|
-
private readonly panel: ToolPanel;
|
|
56
|
-
private readonly body: ReviewMarkdownBody;
|
|
57
|
-
|
|
58
|
-
constructor(
|
|
59
|
-
tui: TUI,
|
|
60
|
-
theme: Theme,
|
|
61
|
-
keys: KeybindingsManager,
|
|
62
|
-
review: ReviewRecord,
|
|
63
|
-
done: (action: ReviewResultAction) => void,
|
|
64
|
-
) {
|
|
65
|
-
this.body = new ReviewMarkdownBody(tui, formatReviewMarkdown(review));
|
|
66
|
-
this.panel = new ToolPanel(theme, {
|
|
67
|
-
title: `${reviewModeLabel(review.mode)} review · ${review.verdict}`,
|
|
68
|
-
secondary: `${review.findings.length} finding${review.findings.length === 1 ? "" : "s"} · not in parent context`,
|
|
69
|
-
body: this.body,
|
|
70
|
-
footer: {
|
|
71
|
-
kind: "hints",
|
|
72
|
-
hints: [
|
|
73
|
-
bindingsHint(["tui.select.up", "tui.select.down"], "scroll"),
|
|
74
|
-
rawHint("a", "send to agent"),
|
|
75
|
-
rawHint("e", "export"),
|
|
76
|
-
bindingHint("tui.select.cancel", "close"),
|
|
77
|
-
],
|
|
78
|
-
},
|
|
79
|
-
border: "box",
|
|
80
|
-
});
|
|
81
|
-
this.handleInput = (data) => {
|
|
82
|
-
if (keys.matches(data, "tui.select.up")) this.body.scroll(-1);
|
|
83
|
-
else if (keys.matches(data, "tui.select.down")) this.body.scroll(1);
|
|
84
|
-
else if (data === "a") done("send");
|
|
85
|
-
else if (data === "e") done("export");
|
|
86
|
-
else if (keys.matches(data, "tui.select.cancel")) done("close");
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
handleInput: (data: string) => void;
|
|
91
|
-
|
|
92
|
-
render(width: number): string[] {
|
|
93
|
-
return this.panel.render(width);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
invalidate(): void {
|
|
97
|
-
this.panel.invalidate();
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
class ReviewMarkdownBody implements Component {
|
|
102
|
-
private readonly tui: TUI;
|
|
103
|
-
private readonly markdown: Markdown;
|
|
104
|
-
private offset = 0;
|
|
105
|
-
private maxOffset = 0;
|
|
106
|
-
|
|
107
|
-
constructor(tui: TUI, content: string) {
|
|
108
|
-
this.tui = tui;
|
|
109
|
-
this.markdown = new Markdown(content, 0, 0, getMarkdownTheme());
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
scroll(delta: number): void {
|
|
113
|
-
this.offset = Math.max(0, Math.min(this.maxOffset, this.offset + delta));
|
|
114
|
-
this.tui.requestRender();
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
render(width: number): string[] {
|
|
118
|
-
const lines = this.markdown.render(width);
|
|
119
|
-
const height = Math.max(4, Math.floor(this.tui.terminal.rows * 0.9) - 9);
|
|
120
|
-
this.maxOffset = Math.max(0, lines.length - height);
|
|
121
|
-
this.offset = Math.min(this.offset, this.maxOffset);
|
|
122
|
-
return lines.slice(this.offset, this.offset + height);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
invalidate(): void {
|
|
126
|
-
this.markdown.invalidate();
|
|
127
|
-
}
|
|
128
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# Working Memory
|
|
2
|
-
|
|
3
|
-
Working Memory gives agent selective checkpoints without changing saved conversation.
|
|
4
|
-
|
|
5
|
-
`working_memory` retains at least one referenced user message or visible assistant text. Keep task framing, constraints, decisions, and active work chain; continuation and file tiers support that conversation context. It has three file tiers:
|
|
6
|
-
|
|
7
|
-
- Auto-read full source into next turn when agent needs its body to decide, edit, or debug.
|
|
8
|
-
- Carry a structural outline when symbols and locations will support a later scoped read.
|
|
9
|
-
- Defer inactive files with reason and condition that makes them relevant again.
|
|
10
|
-
|
|
11
|
-
Use one tier for each path. `readFiles` publishes Tau's built-in autoread request; `outlineFiles` injects a structural outline; `deferFiles` records a conditional reminder.
|
|
12
|
-
|
|
13
|
-
`continuation` is a working note for next step: keep durable decisions, findings, unresolved questions, remaining work, and next action. It should carry enough reasoning to resume without rereading discarded results.
|
|
14
|
-
|
|
15
|
-
Automatic reminders begin at 40,000 active-context tokens and remain advisory. Run `/prune` to request reassessment manually.
|
|
16
|
-
|
|
17
|
-
Settings live under `extensions.workingMemory`.
|
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
import { resolve } from "node:path";
|
|
2
|
-
import { type ExtensionAPI, type ExtensionContext, type SessionEntry } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import { type Static, Type } from "typebox";
|
|
4
|
-
import { truncateBoundedHead } from "../../shared/bounded-text-result.ts";
|
|
5
|
-
import { emitTauEvent } from "../../shared/events.ts";
|
|
6
|
-
import { requestOutlineInjections, type PreparedOutlineInjection } from "../../shared/outline-injection.ts";
|
|
7
|
-
import { buildMemoryCatalog, collectPrunedRowIds } from "./memory.ts";
|
|
8
|
-
import {
|
|
9
|
-
replayWorkingMemoryState,
|
|
10
|
-
WORKING_MEMORY_TOOL,
|
|
11
|
-
type DeferredFile,
|
|
12
|
-
type WorkingMemoryCheckpointDetailsV2,
|
|
13
|
-
} from "./state.ts";
|
|
14
|
-
|
|
15
|
-
const PATH = Type.String({ minLength: 1, maxLength: 500, pattern: "\\S" });
|
|
16
|
-
|
|
17
|
-
export const workingMemoryParameters = Type.Object(
|
|
18
|
-
{
|
|
19
|
-
continuation: Type.String({
|
|
20
|
-
minLength: 1,
|
|
21
|
-
maxLength: 8_000,
|
|
22
|
-
pattern: "\\S",
|
|
23
|
-
description:
|
|
24
|
-
"Working note for resuming mid-task. Carry durable decisions, concrete findings, live reasoning, unresolved questions, remaining work, and next action; include enough detail to continue without rereading discarded results.",
|
|
25
|
-
}),
|
|
26
|
-
keep: Type.Array(Type.String({ minLength: 3, maxLength: 100 }), { minItems: 1, maxItems: 100 }),
|
|
27
|
-
readFiles: Type.Array(PATH, {
|
|
28
|
-
maxItems: 12,
|
|
29
|
-
description:
|
|
30
|
-
"Files whose source should be auto-read into the next turn. Use when the next work needs the complete file; do not also outline or defer these files.",
|
|
31
|
-
}),
|
|
32
|
-
outlineFiles: Type.Array(PATH, {
|
|
33
|
-
maxItems: 12,
|
|
34
|
-
description:
|
|
35
|
-
"Files whose structural outline should be carried into the next turn. Use when symbols and locations will guide later scoped inspection; do not use when the full source is still needed.",
|
|
36
|
-
}),
|
|
37
|
-
deferFiles: Type.Array(
|
|
38
|
-
Type.Object(
|
|
39
|
-
{
|
|
40
|
-
path: PATH,
|
|
41
|
-
reason: Type.String({ minLength: 1, maxLength: 300, pattern: "\\S" }),
|
|
42
|
-
relevantWhen: Type.String({ minLength: 1, maxLength: 300, pattern: "\\S" }),
|
|
43
|
-
},
|
|
44
|
-
{ additionalProperties: false },
|
|
45
|
-
),
|
|
46
|
-
{
|
|
47
|
-
maxItems: 8,
|
|
48
|
-
description:
|
|
49
|
-
"Files to leave out of active context. Record why they are inactive and the concrete condition that would make them relevant.",
|
|
50
|
-
},
|
|
51
|
-
),
|
|
52
|
-
},
|
|
53
|
-
{ additionalProperties: false },
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
export type WorkingMemoryInput = Static<typeof workingMemoryParameters>;
|
|
57
|
-
|
|
58
|
-
interface ExecuteWorkingMemoryOptions {
|
|
59
|
-
pi: Pick<ExtensionAPI, "events">;
|
|
60
|
-
toolCallId: string;
|
|
61
|
-
params: WorkingMemoryInput;
|
|
62
|
-
signal: AbortSignal | undefined;
|
|
63
|
-
ctx: ExtensionContext;
|
|
64
|
-
generation: number;
|
|
65
|
-
currentGeneration(): number;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export interface WorkingMemoryExecution {
|
|
69
|
-
result: {
|
|
70
|
-
content: Array<{ type: "text"; text: string }>;
|
|
71
|
-
details: WorkingMemoryCheckpointDetailsV2;
|
|
72
|
-
};
|
|
73
|
-
outlines: PreparedOutlineInjection[];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export async function executeWorkingMemory(options: ExecuteWorkingMemoryOptions): Promise<WorkingMemoryExecution> {
|
|
77
|
-
assertCurrent(options);
|
|
78
|
-
const branch = options.ctx.sessionManager.getBranch();
|
|
79
|
-
const catalog = buildMemoryCatalog(branch);
|
|
80
|
-
const anchorIndex = findAnchorEntry(branch, options.toolCallId);
|
|
81
|
-
const previousState = replayWorkingMemoryState(branch, true);
|
|
82
|
-
const previousAnchorIndex =
|
|
83
|
-
previousState.latestAnchorToolCallId === undefined
|
|
84
|
-
? -1
|
|
85
|
-
: findAnchorEntry(branch, previousState.latestAnchorToolCallId);
|
|
86
|
-
const requestedRefs = [...new Set(options.params.keep)];
|
|
87
|
-
const retained = requestedRefs
|
|
88
|
-
.flatMap((ref) => {
|
|
89
|
-
const unit = catalog.get(ref);
|
|
90
|
-
return unit ? [unit] : [];
|
|
91
|
-
})
|
|
92
|
-
.sort((left, right) => left.order - right.order);
|
|
93
|
-
const retainedRefs = retained.map((unit) => unit.ref);
|
|
94
|
-
if (retained.length === 0) {
|
|
95
|
-
throw new Error("working_memory requires at least one valid keep reference");
|
|
96
|
-
}
|
|
97
|
-
const warnings = requestedRefs
|
|
98
|
-
.filter((ref) => !catalog.has(ref))
|
|
99
|
-
.map((ref) => `${ref}: memory reference is unavailable and was not retained`);
|
|
100
|
-
|
|
101
|
-
const rootInstructionsKey = resolve(options.ctx.cwd, "AGENTS.md");
|
|
102
|
-
const requestedRootInstructions = [
|
|
103
|
-
...options.params.readFiles,
|
|
104
|
-
...options.params.outlineFiles,
|
|
105
|
-
...options.params.deferFiles.map((file) => file.path),
|
|
106
|
-
].some((path) => resolve(options.ctx.cwd, normalizePath(path)) === rootInstructionsKey);
|
|
107
|
-
if (requestedRootInstructions) {
|
|
108
|
-
warnings.push(
|
|
109
|
-
"AGENTS.md: omitted from working-memory checkpoint because it is already in your context window. Read it directly only when actively changing it.",
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const readPaths = dedupePaths(options.params.readFiles, options.ctx.cwd).filter(
|
|
114
|
-
(path) => resolve(options.ctx.cwd, path) !== rootInstructionsKey,
|
|
115
|
-
);
|
|
116
|
-
const readKeys = new Set(readPaths.map((path) => resolve(options.ctx.cwd, path)));
|
|
117
|
-
const outlinePaths = dedupePaths(options.params.outlineFiles, options.ctx.cwd).filter(
|
|
118
|
-
(path) => resolve(options.ctx.cwd, path) !== rootInstructionsKey && !readKeys.has(resolve(options.ctx.cwd, path)),
|
|
119
|
-
);
|
|
120
|
-
const outlineResponse = await requestOutlineInjections(options.pi, {
|
|
121
|
-
cwd: options.ctx.cwd,
|
|
122
|
-
batchId: options.toolCallId,
|
|
123
|
-
paths: outlinePaths,
|
|
124
|
-
signal: options.signal,
|
|
125
|
-
isLifecycleCurrent: () => options.generation === options.currentGeneration(),
|
|
126
|
-
});
|
|
127
|
-
warnings.push(...outlineResponse.warnings.map((warning) => boundedWarning(warning)));
|
|
128
|
-
assertCurrent(options);
|
|
129
|
-
|
|
130
|
-
const carriedKeys = new Set([
|
|
131
|
-
...readKeys,
|
|
132
|
-
...outlineResponse.messages.map((message) => resolve(options.ctx.cwd, message.details.path)),
|
|
133
|
-
]);
|
|
134
|
-
const deferredFiles: DeferredFile[] = [];
|
|
135
|
-
const deferredKeys = new Set<string>();
|
|
136
|
-
for (const file of options.params.deferFiles) {
|
|
137
|
-
const path = normalizePath(file.path);
|
|
138
|
-
const key = resolve(options.ctx.cwd, path);
|
|
139
|
-
if (key === rootInstructionsKey || carriedKeys.has(key) || deferredKeys.has(key)) continue;
|
|
140
|
-
deferredKeys.add(key);
|
|
141
|
-
deferredFiles.push({ path, reason: file.reason.trim(), relevantWhen: file.relevantWhen.trim() });
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (readPaths.length > 0) {
|
|
145
|
-
emitTauEvent(options.pi, "tau:autoread.requested", {
|
|
146
|
-
source: WORKING_MEMORY_TOOL,
|
|
147
|
-
title: "Working-memory checkpoint",
|
|
148
|
-
cwd: options.ctx.cwd,
|
|
149
|
-
batchId: options.toolCallId,
|
|
150
|
-
files: readPaths.map((path) => ({ path })),
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const prunedRowIds = collectPrunedRowIds(branch, anchorIndex);
|
|
155
|
-
const activeRefs = new Set(previousState.retainedRefs.filter((ref) => catalog.has(ref)));
|
|
156
|
-
for (const unit of catalog.values()) {
|
|
157
|
-
if (unit.order > previousAnchorIndex && unit.order < anchorIndex) activeRefs.add(unit.ref);
|
|
158
|
-
}
|
|
159
|
-
const newlyPrunedRows = prunedRowIds.filter((rowId) => !previousState.prunedRowIds.has(rowId));
|
|
160
|
-
const droppedBlocks = [...activeRefs].filter((ref) => !retainedRefs.includes(ref)).length + newlyPrunedRows.length;
|
|
161
|
-
const details: WorkingMemoryCheckpointDetailsV2 = {
|
|
162
|
-
v: 2,
|
|
163
|
-
anchorToolCallId: options.toolCallId,
|
|
164
|
-
retainedRefs,
|
|
165
|
-
retainedLabels: retained.map((unit) => ({ ref: unit.ref, label: unit.label, preview: unit.preview })),
|
|
166
|
-
prunedRowIds,
|
|
167
|
-
readFiles: readPaths,
|
|
168
|
-
outlinedFiles: outlineResponse.messages.map((message) => ({
|
|
169
|
-
path: message.details.path,
|
|
170
|
-
rowId: message.details.rowId,
|
|
171
|
-
})),
|
|
172
|
-
deferredFiles,
|
|
173
|
-
removedUnits: droppedBlocks,
|
|
174
|
-
warnings,
|
|
175
|
-
};
|
|
176
|
-
const markdown = formatResult(options.params.continuation.trim(), deferredFiles, warnings);
|
|
177
|
-
return {
|
|
178
|
-
result: { content: [{ type: "text", text: truncateBoundedHead(markdown).content }], details },
|
|
179
|
-
outlines: outlineResponse.messages,
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function formatResult(continuation: string, deferred: readonly DeferredFile[], warnings: readonly string[]): string {
|
|
184
|
-
const sections = [`## Continue\n\n${continuation}`];
|
|
185
|
-
if (deferred.length > 0) {
|
|
186
|
-
sections.push(
|
|
187
|
-
`## Deferred files\n\n${deferred
|
|
188
|
-
.map((file) => `- \`${escapeCode(file.path)}\` — ${file.reason} Reconsider when: ${file.relevantWhen}.`)
|
|
189
|
-
.join("\n")}`,
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
if (warnings.length > 0) sections.push(`## Warnings\n\n${warnings.map((warning) => `- ${warning}`).join("\n")}`);
|
|
193
|
-
return sections.join("\n\n");
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
function dedupePaths(paths: readonly string[], cwd: string): string[] {
|
|
197
|
-
const keys = new Set<string>();
|
|
198
|
-
const result: string[] = [];
|
|
199
|
-
for (const raw of paths) {
|
|
200
|
-
const path = normalizePath(raw);
|
|
201
|
-
const key = resolve(cwd, path);
|
|
202
|
-
if (keys.has(key)) continue;
|
|
203
|
-
keys.add(key);
|
|
204
|
-
result.push(path);
|
|
205
|
-
}
|
|
206
|
-
return result;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function findAnchorEntry(branch: readonly SessionEntry[], toolCallId: string): number {
|
|
210
|
-
for (let index = branch.length - 1; index >= 0; index -= 1) {
|
|
211
|
-
const entry = branch[index];
|
|
212
|
-
if (
|
|
213
|
-
entry?.type === "message" &&
|
|
214
|
-
entry.message.role === "assistant" &&
|
|
215
|
-
entry.message.content.some(
|
|
216
|
-
(block) => block.type === "toolCall" && block.id === toolCallId && block.name === WORKING_MEMORY_TOOL,
|
|
217
|
-
)
|
|
218
|
-
) {
|
|
219
|
-
return index;
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
return -1;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function normalizePath(path: string): string {
|
|
226
|
-
return path.trim().replace(/^@/, "");
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
function escapeCode(path: string): string {
|
|
230
|
-
return path.replaceAll("`", "\\`");
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function boundedWarning(warning: string): string {
|
|
234
|
-
return warning.length <= 500 ? warning : `${warning.slice(0, 499)}…`;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function assertCurrent(options: ExecuteWorkingMemoryOptions): void {
|
|
238
|
-
options.signal?.throwIfAborted();
|
|
239
|
-
if (options.generation !== options.currentGeneration()) {
|
|
240
|
-
throw new Error("Working-memory checkpoint crossed a session lifecycle boundary");
|
|
241
|
-
}
|
|
242
|
-
}
|