@shanepadgett/tau-agent 0.30.0 → 0.31.1
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.
|
@@ -36,7 +36,7 @@ Adds `/context` to set branch-local reusable repository work scopes from `.pi/co
|
|
|
36
36
|
|
|
37
37
|
## working-memory
|
|
38
38
|
|
|
39
|
-
Gives agent `working_memory` for selective hard checkpoints. Model-only references identify useful
|
|
39
|
+
Gives agent `working_memory` for selective hard checkpoints. Model-only references identify useful user messages and visible assistant text. Requested source files return as structural outlines, deferred files remain cheap conditional reminders, and a continuation note carries conclusions extracted from exploration. Tool history and full file reads leave future model input without changing saved session. Advisory reminders begin at 40k active-context tokens. Run `/prune` to request reassessment manually.
|
|
40
40
|
|
|
41
41
|
## explore
|
|
42
42
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Working Memory gives agent selective checkpoints without changing saved conversation.
|
|
4
4
|
|
|
5
|
-
`working_memory` keeps referenced
|
|
5
|
+
`working_memory` keeps referenced user messages and visible assistant text, carries requested source files as structural outlines, and records deferred files as conditional reminders. Tool history and full file reads leave model context; useful findings from them are distilled into a compact continuation note.
|
|
6
6
|
|
|
7
7
|
Automatic reminders begin at 40,000 active-context tokens and remain advisory. Run `/prune` to request reassessment manually.
|
|
8
8
|
|
|
@@ -3,7 +3,7 @@ import { type ExtensionAPI, type ExtensionContext, type SessionEntry } from "@ea
|
|
|
3
3
|
import { type Static, Type } from "typebox";
|
|
4
4
|
import { truncateBoundedHead } from "../../shared/bounded-text-result.ts";
|
|
5
5
|
import { requestOutlineInjections, type PreparedOutlineInjection } from "../../shared/outline-injection.ts";
|
|
6
|
-
import { buildMemoryCatalog } from "./memory.ts";
|
|
6
|
+
import { buildMemoryCatalog, collectPrunedRowIds } from "./memory.ts";
|
|
7
7
|
import { WORKING_MEMORY_TOOL, type DeferredFile, type WorkingMemoryCheckpointDetailsV1 } from "./state.ts";
|
|
8
8
|
|
|
9
9
|
const PATH = Type.String({ minLength: 1, maxLength: 500, pattern: "\\S" });
|
|
@@ -58,7 +58,7 @@ export async function executeWorkingMemory(options: ExecuteWorkingMemoryOptions)
|
|
|
58
58
|
const unit = catalog.get(ref);
|
|
59
59
|
return unit ? [unit] : [];
|
|
60
60
|
})
|
|
61
|
-
.sort((left, right) => left.order - right.order
|
|
61
|
+
.sort((left, right) => left.order - right.order);
|
|
62
62
|
const retainedRefs = retained.map((unit) => unit.ref);
|
|
63
63
|
const warnings = requestedRefs
|
|
64
64
|
.filter((ref) => !catalog.has(ref))
|
|
@@ -89,9 +89,8 @@ export async function executeWorkingMemory(options: ExecuteWorkingMemoryOptions)
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
const anchorIndex = findAnchorEntry(branch, options.toolCallId);
|
|
92
|
-
const retainedSet = new Set(retainedRefs);
|
|
93
92
|
const preAnchorUnits = [...catalog.values()].filter((unit) => unit.order < anchorIndex);
|
|
94
|
-
const prunedRowIds =
|
|
93
|
+
const prunedRowIds = collectPrunedRowIds(branch, anchorIndex);
|
|
95
94
|
const details: WorkingMemoryCheckpointDetailsV1 = {
|
|
96
95
|
v: 1,
|
|
97
96
|
anchorToolCallId: options.toolCallId,
|
|
@@ -22,7 +22,7 @@ import { replayWorkingMemoryState, WORKING_MEMORY_TOOL, type WorkingMemoryCheckp
|
|
|
22
22
|
const NUDGE_TYPE = "tau.working-memory.nudge";
|
|
23
23
|
const BASELINE_TYPE = "tau.working-memory.nudge-baseline";
|
|
24
24
|
const TOOL_DESCRIPTION =
|
|
25
|
-
"Create a selective hard checkpoint for future model context. Keep valuable
|
|
25
|
+
"Create a selective hard checkpoint for future model context. Keep valuable user and visible assistant messages, carry file structure as outlines, defer conditionally relevant files without reading them, and distill exploration findings into one compact continuation note.";
|
|
26
26
|
|
|
27
27
|
interface NudgeState {
|
|
28
28
|
anchorToolCallId: string | undefined;
|
|
@@ -98,9 +98,9 @@ export default function workingMemoryExtension(pi: ExtensionAPI): void {
|
|
|
98
98
|
promptSnippet: "Reassess and selectively checkpoint active working memory",
|
|
99
99
|
promptGuidelines: [
|
|
100
100
|
"Use working_memory when stale evidence has accumulated or a memory reminder asks for reassessment; continue coherent exploration when current evidence remains useful.",
|
|
101
|
-
"A hidden working-memory reference catalog provides keep refs
|
|
102
|
-
"
|
|
103
|
-
"Everything before working_memory leaves future model context unless selected in keep. Put durable decisions, constraints, unresolved matters, and next action in continuation without duplicating retained
|
|
101
|
+
"A hidden working-memory reference catalog provides keep refs only for user messages and visible assistant text. Tool calls, tool results, hidden reasoning, and framework messages cannot be retained.",
|
|
102
|
+
"Outline files that remain in the repository working set, defer paths whose relevance is conditional, and discard exploration history after extracting its useful conclusions.",
|
|
103
|
+
"Everything before working_memory leaves future model context unless selected in keep. Put findings from tools, durable decisions, constraints, unresolved matters, and next action in continuation without duplicating retained messages.",
|
|
104
104
|
],
|
|
105
105
|
parameters: workingMemoryParameters,
|
|
106
106
|
executionMode: "sequential",
|
|
@@ -9,23 +9,13 @@ export interface MemoryUnit {
|
|
|
9
9
|
label: string;
|
|
10
10
|
preview: string;
|
|
11
11
|
order: number;
|
|
12
|
-
suborder: number;
|
|
13
12
|
messages: ContextMessage[];
|
|
14
|
-
rowIds: string[];
|
|
15
13
|
}
|
|
16
14
|
|
|
17
|
-
const EXCLUDED_CUSTOM_TYPES = new Set(["tau.autoread", "tau.runtime-context", "tau.working-memory.nudge"]);
|
|
18
15
|
const REFERENCE_CATALOG_TYPE = "tau.working-memory.references";
|
|
19
16
|
|
|
20
17
|
export function buildMemoryCatalog(branch: readonly SessionEntry[]): Map<string, MemoryUnit> {
|
|
21
18
|
const catalog = new Map<string, MemoryUnit>();
|
|
22
|
-
const results = new Map<string, ContextMessage>();
|
|
23
|
-
for (const entry of branch) {
|
|
24
|
-
for (const message of sessionEntryToContextMessages(entry)) {
|
|
25
|
-
if (message.role === "toolResult") results.set(message.toolCallId, message);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
19
|
for (let order = 0; order < branch.length; order += 1) {
|
|
30
20
|
const entry = branch[order];
|
|
31
21
|
if (!entry) continue;
|
|
@@ -35,61 +25,56 @@ export function buildMemoryCatalog(branch: readonly SessionEntry[]): Map<string,
|
|
|
35
25
|
const calls = message.content.filter((block) => block.type === "toolCall");
|
|
36
26
|
const frameworkCall = calls.some((call) => call.name === "working_memory");
|
|
37
27
|
if (frameworkCall) continue;
|
|
38
|
-
const
|
|
39
|
-
if (
|
|
28
|
+
const text = message.content.filter((block) => block.type === "text");
|
|
29
|
+
if (text.length > 0) {
|
|
40
30
|
const ref = `m:${entry.id}`;
|
|
41
31
|
catalog.set(ref, {
|
|
42
32
|
ref,
|
|
43
33
|
label: "assistant",
|
|
44
|
-
preview: previewAssistant(
|
|
45
|
-
order,
|
|
46
|
-
suborder: 0,
|
|
47
|
-
messages: [{ ...message, content: prose }],
|
|
48
|
-
rowIds: [],
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
for (let index = 0; index < calls.length; index += 1) {
|
|
52
|
-
const call = calls[index];
|
|
53
|
-
if (!call) continue;
|
|
54
|
-
const result = results.get(call.id);
|
|
55
|
-
if (result?.role !== "toolResult" || result.toolName !== call.name) continue;
|
|
56
|
-
const ref = `t:${entry.id}:${index + 1}`;
|
|
57
|
-
catalog.set(ref, {
|
|
58
|
-
ref,
|
|
59
|
-
label: `tool ${call.name}`,
|
|
60
|
-
preview: previewTool(call.arguments),
|
|
34
|
+
preview: previewAssistant(text),
|
|
61
35
|
order,
|
|
62
|
-
|
|
63
|
-
messages: [{ ...message, content: [call] }, result],
|
|
64
|
-
rowIds: [call.id],
|
|
36
|
+
messages: [{ ...message, content: text }],
|
|
65
37
|
});
|
|
66
38
|
}
|
|
67
39
|
continue;
|
|
68
40
|
}
|
|
69
|
-
if (message.role
|
|
70
|
-
if (message.role === "custom" && EXCLUDED_CUSTOM_TYPES.has(message.customType)) continue;
|
|
71
|
-
if (
|
|
72
|
-
message.role === "custom" &&
|
|
73
|
-
message.customType === "tau.injected-context" &&
|
|
74
|
-
message.details &&
|
|
75
|
-
typeof message.details === "object" &&
|
|
76
|
-
(message.details as Record<string, unknown>).source === "context"
|
|
77
|
-
)
|
|
78
|
-
continue;
|
|
41
|
+
if (message.role !== "user") continue;
|
|
79
42
|
const ref = `m:${entry.id}`;
|
|
80
43
|
catalog.set(ref, {
|
|
81
44
|
ref,
|
|
82
|
-
label:
|
|
83
|
-
preview:
|
|
45
|
+
label: "user",
|
|
46
|
+
preview: previewUser(message),
|
|
84
47
|
order,
|
|
85
|
-
suborder: 0,
|
|
86
48
|
messages: [message],
|
|
87
|
-
rowIds: outlineRowIds(message),
|
|
88
49
|
});
|
|
89
50
|
}
|
|
90
51
|
return catalog;
|
|
91
52
|
}
|
|
92
53
|
|
|
54
|
+
export function collectPrunedRowIds(branch: readonly SessionEntry[], before: number): string[] {
|
|
55
|
+
const rowIds = new Set<string>();
|
|
56
|
+
for (let index = 0; index < before; index += 1) {
|
|
57
|
+
const entry = branch[index];
|
|
58
|
+
if (!entry) continue;
|
|
59
|
+
for (const message of sessionEntryToContextMessages(entry)) {
|
|
60
|
+
if (message.role === "assistant") {
|
|
61
|
+
for (const block of message.content) {
|
|
62
|
+
if (block.type === "toolCall" && block.name !== "working_memory") rowIds.add(block.id);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (
|
|
66
|
+
message.role === "custom" &&
|
|
67
|
+
message.customType === "tau.explore.outline" &&
|
|
68
|
+
isRecord(message.details) &&
|
|
69
|
+
typeof message.details.rowId === "string"
|
|
70
|
+
) {
|
|
71
|
+
rowIds.add(message.details.rowId);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return [...rowIds];
|
|
76
|
+
}
|
|
77
|
+
|
|
93
78
|
export function projectWorkingMemory(
|
|
94
79
|
messages: readonly ContextMessage[],
|
|
95
80
|
state: ActiveWorkingMemoryState,
|
|
@@ -109,7 +94,7 @@ export function projectWorkingMemory(
|
|
|
109
94
|
const unit = catalog.get(ref);
|
|
110
95
|
return unit ? [unit] : [];
|
|
111
96
|
})
|
|
112
|
-
.sort((left, right) => left.order - right.order
|
|
97
|
+
.sort((left, right) => left.order - right.order);
|
|
113
98
|
const projected: ContextMessage[] = [];
|
|
114
99
|
const projectedRefs: MemoryUnit[][] = [];
|
|
115
100
|
for (const unit of retained) {
|
|
@@ -137,12 +122,6 @@ function referenceCurrentMessages(
|
|
|
137
122
|
(message) => !isContextProjectionMessage(message) && !isLegacyContextMessage(message),
|
|
138
123
|
);
|
|
139
124
|
if (generated.length !== sessionMessages.length) return messages.map(() => []);
|
|
140
|
-
const toolRefs = new Map<string, MemoryUnit[]>();
|
|
141
|
-
for (const unit of catalog.values()) {
|
|
142
|
-
const result = unit.messages.find((message) => message.role === "toolResult");
|
|
143
|
-
if (result?.role !== "toolResult") continue;
|
|
144
|
-
toolRefs.set(result.toolCallId, [...(toolRefs.get(result.toolCallId) ?? []), unit]);
|
|
145
|
-
}
|
|
146
125
|
let generatedIndex = 0;
|
|
147
126
|
return messages.map((currentMessage) => {
|
|
148
127
|
if (isContextProjectionMessage(currentMessage) || isLegacyContextMessage(currentMessage)) return [];
|
|
@@ -150,13 +129,9 @@ function referenceCurrentMessages(
|
|
|
150
129
|
generatedIndex += 1;
|
|
151
130
|
if (!current) return [];
|
|
152
131
|
const { entry, message } = current;
|
|
153
|
-
if (message.role
|
|
154
|
-
const unit = catalog.get(`m:${entry.id}`);
|
|
155
|
-
return unit && currentMessage.role === "assistant" ? [unit] : [];
|
|
156
|
-
}
|
|
157
|
-
if (message.role === "toolResult") return toolRefs.get(message.toolCallId) ?? [];
|
|
132
|
+
if (message.role !== "user" && message.role !== "assistant") return [];
|
|
158
133
|
const unit = catalog.get(`m:${entry.id}`);
|
|
159
|
-
return unit ? [unit] : [];
|
|
134
|
+
return unit && currentMessage.role === message.role ? [unit] : [];
|
|
160
135
|
});
|
|
161
136
|
}
|
|
162
137
|
|
|
@@ -174,17 +149,33 @@ function addReferenceCatalog(
|
|
|
174
149
|
timestamp: 0,
|
|
175
150
|
},
|
|
176
151
|
];
|
|
152
|
+
let deferredAssistantUnits: MemoryUnit[] = [];
|
|
153
|
+
let pendingToolCallIds = new Set<string>();
|
|
177
154
|
for (let index = 0; index < messages.length; index += 1) {
|
|
178
155
|
const message = messages[index];
|
|
179
156
|
if (!message) continue;
|
|
180
157
|
projected.push(message);
|
|
181
158
|
const unique = new Map((unitsByMessage[index] ?? []).map((unit) => [unit.ref, unit]));
|
|
159
|
+
if (message.role === "assistant") {
|
|
160
|
+
pendingToolCallIds = new Set(
|
|
161
|
+
message.content.filter((block) => block.type === "toolCall").map((block) => block.id),
|
|
162
|
+
);
|
|
163
|
+
if (pendingToolCallIds.size > 0) {
|
|
164
|
+
deferredAssistantUnits = [...unique.values()];
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (message.role === "toolResult" && pendingToolCallIds.delete(message.toolCallId)) {
|
|
169
|
+
if (pendingToolCallIds.size > 0) continue;
|
|
170
|
+
for (const unit of deferredAssistantUnits) unique.set(unit.ref, unit);
|
|
171
|
+
deferredAssistantUnits = [];
|
|
172
|
+
}
|
|
182
173
|
if (unique.size === 0) continue;
|
|
183
174
|
projected.push({
|
|
184
175
|
role: "custom",
|
|
185
176
|
customType: REFERENCE_CATALOG_TYPE,
|
|
186
177
|
content: [...unique.values()]
|
|
187
|
-
.sort((left, right) => left.order - right.order
|
|
178
|
+
.sort((left, right) => left.order - right.order)
|
|
188
179
|
.map((unit) => `${unit.ref} (${unit.label}): ${unit.preview}`)
|
|
189
180
|
.join("\n"),
|
|
190
181
|
display: false,
|
|
@@ -224,36 +215,14 @@ function findAnchor(messages: readonly ContextMessage[], id: string): number {
|
|
|
224
215
|
function previewAssistant(blocks: ReadonlyArray<{ type: string }>): string {
|
|
225
216
|
for (const block of blocks) {
|
|
226
217
|
if (block.type === "text" && "text" in block && typeof block.text === "string") return compact(block.text);
|
|
227
|
-
if (block.type === "thinking" && "thinking" in block && typeof block.thinking === "string") {
|
|
228
|
-
return compact(block.thinking);
|
|
229
|
-
}
|
|
230
218
|
}
|
|
231
219
|
return "";
|
|
232
220
|
}
|
|
233
221
|
|
|
234
|
-
function
|
|
235
|
-
return compact(
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
function previewMessage(message: ContextMessage): string {
|
|
239
|
-
if (message.role === "user" || message.role === "custom") {
|
|
240
|
-
if (typeof message.content === "string") return compact(message.content);
|
|
241
|
-
const text = message.content.find((part) => part.type === "text");
|
|
242
|
-
return text?.type === "text" ? compact(text.text) : "";
|
|
243
|
-
}
|
|
244
|
-
if (message.role === "bashExecution") return compact(`${message.command}: ${message.output}`);
|
|
245
|
-
if (message.role === "assistant") return previewAssistant(message.content);
|
|
246
|
-
if (message.role === "toolResult") {
|
|
247
|
-
const text = message.content.find((part) => part.type === "text");
|
|
248
|
-
return text?.type === "text" ? compact(text.text) : "";
|
|
249
|
-
}
|
|
250
|
-
return compact(message.summary);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
function outlineRowIds(message: ContextMessage): string[] {
|
|
254
|
-
if (message.role !== "custom" || message.customType !== "tau.explore.outline") return [];
|
|
255
|
-
if (!isRecord(message.details) || typeof message.details.rowId !== "string") return [];
|
|
256
|
-
return [message.details.rowId];
|
|
222
|
+
function previewUser(message: Extract<ContextMessage, { role: "user" }>): string {
|
|
223
|
+
if (typeof message.content === "string") return compact(message.content);
|
|
224
|
+
const text = message.content.find((part) => part.type === "text");
|
|
225
|
+
return text?.type === "text" ? compact(text.text) : "";
|
|
257
226
|
}
|
|
258
227
|
|
|
259
228
|
function compact(value: string): string {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shanepadgett/tau-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.1",
|
|
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.0",
|
|
38
|
-
"@shanepadgett/tau-tui": "0.
|
|
38
|
+
"@shanepadgett/tau-tui": "0.31.1",
|
|
39
39
|
"@vscode/tree-sitter-wasm": "0.3.1",
|
|
40
40
|
"image-size": "2.0.2",
|
|
41
41
|
"smol-toml": "1.7.0",
|