tau-coding-agent 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/extensions/answer.ts +59 -47
- package/extensions/branch-term.ts +8 -13
- package/extensions/btw.ts +77 -6
- package/extensions/ghostty.ts +64 -11
- package/extensions/insights.ts +42 -4
- package/extensions/loop.ts +51 -12
- package/extensions/memory.ts +35 -17
- package/extensions/notify.ts +24 -5
- package/extensions/openai-fast.ts +6 -4
- package/extensions/openai-verbosity.ts +6 -4
- package/extensions/review/fix.ts +239 -0
- package/extensions/review/git.ts +517 -0
- package/extensions/review/index.ts +232 -0
- package/extensions/review/message-queue.ts +249 -0
- package/extensions/review/models.ts +589 -0
- package/extensions/review/prompts.ts +337 -0
- package/extensions/review/renderers/inline.ts +126 -0
- package/extensions/review/request.ts +504 -0
- package/extensions/review/review.ts +1305 -0
- package/extensions/review/runner.ts +522 -0
- package/extensions/review/runtime.ts +275 -0
- package/extensions/review/schema.ts +160 -0
- package/extensions/review/submit-review-tool.ts +62 -0
- package/extensions/review/submit-triage-tool.ts +60 -0
- package/extensions/review/triage.ts +746 -0
- package/extensions/sandbox/index.ts +976 -185
- package/extensions/sandbox/macos-sandbox-shell.mjs +202 -0
- package/extensions/{interlude.ts → stash.ts} +13 -11
- package/extensions/tool-display-mode.ts +2 -3
- package/extensions/usage/anthropic.ts +5 -6
- package/extensions/usage/github-copilot.ts +30 -9
- package/extensions/usage/index.ts +9 -5
- package/extensions/usage/openai-codex.ts +2 -2
- package/extensions/websearch/README.md +1 -1
- package/extensions/websearch/browser/chromium.ts +42 -31
- package/extensions/websearch/browser/discovery.ts +11 -9
- package/extensions/websearch/browser/firefox.ts +10 -7
- package/extensions/websearch/browser/fs.ts +10 -0
- package/extensions/websearch/browser/sqlite.ts +22 -15
- package/extensions/websearch/config.ts +2 -2
- package/extensions/websearch/index.ts +6 -6
- package/extensions/websearch/providers/pi-model.shared.ts +19 -1
- package/extensions/worktree.ts +33 -33
- package/package.json +4 -4
- package/skills/git-clean-history/SKILL.md +1 -0
- package/skills/git-commit/SKILL.md +2 -2
- package/skills/oracle/SKILL.md +3 -3
- package/skills/oracle/scripts/oracle +82 -61
- package/themes/tau-dark.json +2 -1
- package/extensions/review.ts +0 -4420
package/extensions/insights.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { complete
|
|
1
|
+
import { complete } from "@earendil-works/pi-ai/compat";
|
|
2
|
+
import type { Api, Model, UserMessage } from "@earendil-works/pi-ai";
|
|
2
3
|
import {
|
|
3
4
|
BorderedLoader,
|
|
4
5
|
SessionManager,
|
|
@@ -26,6 +27,7 @@ import {
|
|
|
26
27
|
} from "@earendil-works/pi-tui";
|
|
27
28
|
import { createHash, randomBytes } from "node:crypto";
|
|
28
29
|
import fs from "node:fs/promises";
|
|
30
|
+
import os from "node:os";
|
|
29
31
|
import path from "node:path";
|
|
30
32
|
|
|
31
33
|
// --- Constants ---
|
|
@@ -140,6 +142,7 @@ type ConfiguredModelSelection = {
|
|
|
140
142
|
model: Model<Api>;
|
|
141
143
|
apiKey?: string;
|
|
142
144
|
headers?: Record<string, string>;
|
|
145
|
+
env?: Record<string, string>;
|
|
143
146
|
};
|
|
144
147
|
|
|
145
148
|
type ReadonlySessionManager = Pick<
|
|
@@ -480,8 +483,8 @@ async function runInsightsCommand(
|
|
|
480
483
|
return;
|
|
481
484
|
}
|
|
482
485
|
|
|
483
|
-
if (
|
|
484
|
-
ctx.ui.notify("/insights currently requires
|
|
486
|
+
if (ctx.mode !== "tui") {
|
|
487
|
+
ctx.ui.notify("/insights currently requires TUI mode", "warning");
|
|
485
488
|
return;
|
|
486
489
|
}
|
|
487
490
|
|
|
@@ -561,9 +564,37 @@ async function runInsightsCommand(
|
|
|
561
564
|
return;
|
|
562
565
|
}
|
|
563
566
|
|
|
567
|
+
let reportPath: string | undefined;
|
|
568
|
+
try {
|
|
569
|
+
reportPath = await saveInsightsReport(result.reportMarkdown, result.generatedAt);
|
|
570
|
+
} catch (error) {
|
|
571
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
572
|
+
ctx.ui.notify(`Failed to save insights report: ${message}`, "warning");
|
|
573
|
+
}
|
|
574
|
+
|
|
564
575
|
await ctx.ui.custom<void>((tui, theme, _kb, done) => {
|
|
565
576
|
return new InsightsReportComponent(result, tui, theme, done);
|
|
566
577
|
});
|
|
578
|
+
|
|
579
|
+
if (reportPath) {
|
|
580
|
+
ctx.ui.notify(`Insights report saved to ${reportPath}`, "info");
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
async function saveInsightsReport(reportMarkdown: string, generatedAt: string): Promise<string> {
|
|
585
|
+
const timestamp = generatedAt.replace(/[:.]/g, "-");
|
|
586
|
+
const reportPath = path.join(os.tmpdir(), `tau-insights-${timestamp}.md`);
|
|
587
|
+
const reportFile = await fs.open(reportPath, "wx", 0o600);
|
|
588
|
+
|
|
589
|
+
try {
|
|
590
|
+
await reportFile.writeFile(`${reportMarkdown.trimEnd()}\n`, "utf8");
|
|
591
|
+
await reportFile.close();
|
|
592
|
+
return reportPath;
|
|
593
|
+
} catch (error) {
|
|
594
|
+
await reportFile.close().catch(() => undefined);
|
|
595
|
+
await fs.rm(reportPath, { force: true }).catch(() => undefined);
|
|
596
|
+
throw error;
|
|
597
|
+
}
|
|
567
598
|
}
|
|
568
599
|
|
|
569
600
|
async function runInsightsPipeline(
|
|
@@ -748,7 +779,12 @@ async function getConfiguredModelSelection(
|
|
|
748
779
|
|
|
749
780
|
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(ctx.model);
|
|
750
781
|
if (!auth.ok) return null;
|
|
751
|
-
return {
|
|
782
|
+
return {
|
|
783
|
+
model: ctx.model,
|
|
784
|
+
apiKey: auth.apiKey,
|
|
785
|
+
headers: auth.headers,
|
|
786
|
+
env: auth.env,
|
|
787
|
+
};
|
|
752
788
|
}
|
|
753
789
|
|
|
754
790
|
async function listTargets(
|
|
@@ -1341,6 +1377,7 @@ async function extractFacet(
|
|
|
1341
1377
|
{
|
|
1342
1378
|
apiKey: selection.apiKey,
|
|
1343
1379
|
headers: selection.headers,
|
|
1380
|
+
env: selection.env,
|
|
1344
1381
|
signal,
|
|
1345
1382
|
},
|
|
1346
1383
|
);
|
|
@@ -1513,6 +1550,7 @@ async function synthesizeReport(
|
|
|
1513
1550
|
{
|
|
1514
1551
|
apiKey: selection.apiKey,
|
|
1515
1552
|
headers: selection.headers,
|
|
1553
|
+
env: selection.env,
|
|
1516
1554
|
signal,
|
|
1517
1555
|
},
|
|
1518
1556
|
);
|
package/extensions/loop.ts
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { Type } from "typebox";
|
|
10
|
-
import { complete
|
|
10
|
+
import { complete } from "@earendil-works/pi-ai/compat";
|
|
11
|
+
import type { Api, Model, UserMessage } from "@earendil-works/pi-ai";
|
|
11
12
|
import {
|
|
12
13
|
compact,
|
|
13
14
|
defineTool,
|
|
@@ -53,6 +54,7 @@ type ModelRequestAuth = {
|
|
|
53
54
|
model: Model<Api>;
|
|
54
55
|
apiKey?: string;
|
|
55
56
|
headers?: Record<string, string>;
|
|
57
|
+
env?: Record<string, string>;
|
|
56
58
|
};
|
|
57
59
|
|
|
58
60
|
async function withPromptSignal<T>(pi: ExtensionAPI, run: () => Promise<T>): Promise<T> {
|
|
@@ -136,13 +138,20 @@ async function selectSummaryModel(ctx: ExtensionContext): Promise<ModelRequestAu
|
|
|
136
138
|
|
|
137
139
|
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(candidate);
|
|
138
140
|
if (auth.ok) {
|
|
139
|
-
return {
|
|
141
|
+
return {
|
|
142
|
+
model: candidate,
|
|
143
|
+
apiKey: auth.apiKey,
|
|
144
|
+
headers: auth.headers,
|
|
145
|
+
env: auth.env,
|
|
146
|
+
};
|
|
140
147
|
}
|
|
141
148
|
}
|
|
142
149
|
}
|
|
143
150
|
|
|
144
151
|
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(ctx.model);
|
|
145
|
-
return auth.ok
|
|
152
|
+
return auth.ok
|
|
153
|
+
? { model: ctx.model, apiKey: auth.apiKey, headers: auth.headers, env: auth.env }
|
|
154
|
+
: null;
|
|
146
155
|
}
|
|
147
156
|
|
|
148
157
|
async function summarizeBreakoutCondition(
|
|
@@ -164,7 +173,7 @@ async function summarizeBreakoutCondition(
|
|
|
164
173
|
const response = await complete(
|
|
165
174
|
selection.model,
|
|
166
175
|
{ systemPrompt: SUMMARY_SYSTEM_PROMPT, messages: [userMessage] },
|
|
167
|
-
{ apiKey: selection.apiKey, headers: selection.headers },
|
|
176
|
+
{ apiKey: selection.apiKey, headers: selection.headers, env: selection.env },
|
|
168
177
|
);
|
|
169
178
|
|
|
170
179
|
if (response.stopReason === "aborted" || response.stopReason === "error") {
|
|
@@ -213,6 +222,8 @@ async function loadState(ctx: ExtensionContext): Promise<LoopStateData> {
|
|
|
213
222
|
|
|
214
223
|
export default function loopExtension(pi: ExtensionAPI): void {
|
|
215
224
|
let loopState: LoopStateData = { active: false };
|
|
225
|
+
let lastAgentEndMessages: Array<{ role?: string; stopReason?: string }> = [];
|
|
226
|
+
let pendingLoopPrompt: ReturnType<typeof setImmediate> | undefined;
|
|
216
227
|
|
|
217
228
|
function persistState(state: LoopStateData): void {
|
|
218
229
|
pi.appendEntry(LOOP_STATE_ENTRY, state);
|
|
@@ -271,6 +282,23 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
271
282
|
);
|
|
272
283
|
}
|
|
273
284
|
|
|
285
|
+
function scheduleLoopPrompt(ctx: ExtensionContext): void {
|
|
286
|
+
if (pendingLoopPrompt) return;
|
|
287
|
+
|
|
288
|
+
pendingLoopPrompt = setImmediate(() => {
|
|
289
|
+
pendingLoopPrompt = undefined;
|
|
290
|
+
if (!loopState.active || !ctx.isIdle()) return;
|
|
291
|
+
triggerLoopPrompt(ctx);
|
|
292
|
+
});
|
|
293
|
+
pendingLoopPrompt.unref?.();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function cancelScheduledLoopPrompt(): void {
|
|
297
|
+
if (!pendingLoopPrompt) return;
|
|
298
|
+
clearImmediate(pendingLoopPrompt);
|
|
299
|
+
pendingLoopPrompt = undefined;
|
|
300
|
+
}
|
|
301
|
+
|
|
274
302
|
async function showLoopSelector(ctx: ExtensionContext): Promise<LoopStateData | null> {
|
|
275
303
|
const items: SelectItem[] = LOOP_PRESETS.map((preset) => ({
|
|
276
304
|
value: preset.value,
|
|
@@ -385,11 +413,11 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
385
413
|
name: "signal_loop_success",
|
|
386
414
|
label: "Signal Loop Success",
|
|
387
415
|
description:
|
|
388
|
-
"Stop the active loop when the breakout condition is satisfied. Only call
|
|
416
|
+
"Stop the active loop when the breakout condition is satisfied. Only call signal_loop_success when explicitly instructed to do so by the user, tool, or system prompt.",
|
|
389
417
|
promptSnippet: "Signal that the active /loop breakout condition has been satisfied",
|
|
390
418
|
promptGuidelines: [
|
|
391
|
-
"Call
|
|
392
|
-
"Do not call
|
|
419
|
+
"Call signal_loop_success only when the active loop's breakout condition is actually satisfied.",
|
|
420
|
+
"Do not call signal_loop_success unless a loop is currently active.",
|
|
393
421
|
],
|
|
394
422
|
parameters: Type.Object({}),
|
|
395
423
|
async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
|
|
@@ -416,7 +444,7 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
416
444
|
handler: async (args, ctx) => {
|
|
417
445
|
let nextState = parseArgs(args);
|
|
418
446
|
if (!nextState) {
|
|
419
|
-
if (
|
|
447
|
+
if (ctx.mode !== "tui") {
|
|
420
448
|
ctx.ui.notify("Usage: /loop tests | /loop custom <condition> | /loop self", "warning");
|
|
421
449
|
return;
|
|
422
450
|
}
|
|
@@ -458,10 +486,14 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
458
486
|
},
|
|
459
487
|
});
|
|
460
488
|
|
|
461
|
-
pi.on("agent_end", async (event
|
|
489
|
+
pi.on("agent_end", async (event) => {
|
|
490
|
+
lastAgentEndMessages = event.messages;
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
pi.on("agent_settled", async (_event, ctx) => {
|
|
462
494
|
if (!loopState.active) return;
|
|
463
495
|
|
|
464
|
-
if (ctx.hasUI && wasLastAssistantAborted(
|
|
496
|
+
if (ctx.hasUI && wasLastAssistantAborted(lastAgentEndMessages)) {
|
|
465
497
|
const confirm = await withPromptSignal(pi, () =>
|
|
466
498
|
ctx.ui.confirm("Break active loop?", "Operation aborted. Break out of the loop?"),
|
|
467
499
|
);
|
|
@@ -471,13 +503,13 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
471
503
|
}
|
|
472
504
|
}
|
|
473
505
|
|
|
474
|
-
|
|
506
|
+
scheduleLoopPrompt(ctx);
|
|
475
507
|
});
|
|
476
508
|
|
|
477
509
|
pi.on("session_before_compact", async (event, ctx) => {
|
|
478
510
|
if (!loopState.active || !loopState.mode || !ctx.model) return;
|
|
479
511
|
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(ctx.model);
|
|
480
|
-
if (!auth.ok
|
|
512
|
+
if (!auth.ok) return;
|
|
481
513
|
|
|
482
514
|
const instructionParts = [
|
|
483
515
|
event.customInstructions,
|
|
@@ -494,6 +526,9 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
494
526
|
auth.headers,
|
|
495
527
|
instructionParts,
|
|
496
528
|
event.signal,
|
|
529
|
+
ctx.thinkingLevel,
|
|
530
|
+
undefined, // Use Pi's default summarization stream.
|
|
531
|
+
auth.env,
|
|
497
532
|
);
|
|
498
533
|
return { compaction };
|
|
499
534
|
} catch (error) {
|
|
@@ -526,4 +561,8 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
526
561
|
pi.on("session_start", async (_event, ctx) => {
|
|
527
562
|
await restoreLoopState(ctx);
|
|
528
563
|
});
|
|
564
|
+
|
|
565
|
+
pi.on("session_shutdown", async () => {
|
|
566
|
+
cancelScheduledLoopPrompt();
|
|
567
|
+
});
|
|
529
568
|
}
|
package/extensions/memory.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { complete } from "@earendil-works/pi-ai/compat";
|
|
1
2
|
import {
|
|
2
|
-
complete,
|
|
3
3
|
StringEnum,
|
|
4
4
|
type Api,
|
|
5
5
|
type Model,
|
|
6
|
+
type Usage,
|
|
6
7
|
type UserMessage,
|
|
7
8
|
} from "@earendil-works/pi-ai";
|
|
8
9
|
import {
|
|
@@ -176,6 +177,11 @@ type DreamResult = {
|
|
|
176
177
|
summaryPath: string | null;
|
|
177
178
|
};
|
|
178
179
|
|
|
180
|
+
type DreamExecution = {
|
|
181
|
+
result: DreamResult;
|
|
182
|
+
usage?: Usage;
|
|
183
|
+
};
|
|
184
|
+
|
|
179
185
|
type AutoDreamStatus = {
|
|
180
186
|
coreLines: number;
|
|
181
187
|
coreChars: number;
|
|
@@ -194,6 +200,7 @@ type ModelSelection = {
|
|
|
194
200
|
model: Model<Api>;
|
|
195
201
|
apiKey?: string;
|
|
196
202
|
headers?: Record<string, string>;
|
|
203
|
+
env?: Record<string, string>;
|
|
197
204
|
};
|
|
198
205
|
|
|
199
206
|
class MemoryReadmeMissingError extends Error {
|
|
@@ -209,12 +216,17 @@ async function toolDream(
|
|
|
209
216
|
cwd: string,
|
|
210
217
|
ctx: ExtensionContext,
|
|
211
218
|
reason?: string,
|
|
212
|
-
): Promise<{
|
|
219
|
+
): Promise<{
|
|
220
|
+
content: Array<{ type: "text"; text: string }>;
|
|
221
|
+
details: DreamResult;
|
|
222
|
+
usage?: Usage;
|
|
223
|
+
}> {
|
|
213
224
|
try {
|
|
214
|
-
const
|
|
225
|
+
const execution = await runMemoryDream(cwd, ctx, reason);
|
|
215
226
|
return {
|
|
216
|
-
content: [{ type: "text", text: result.summary }],
|
|
217
|
-
details: result,
|
|
227
|
+
content: [{ type: "text", text: execution.result.summary }],
|
|
228
|
+
details: execution.result,
|
|
229
|
+
usage: execution.usage,
|
|
218
230
|
};
|
|
219
231
|
} catch (error) {
|
|
220
232
|
if (error instanceof MemoryReadmeMissingError) {
|
|
@@ -374,7 +386,7 @@ async function runMemoryDream(
|
|
|
374
386
|
cwd: string,
|
|
375
387
|
ctx: ExtensionContext,
|
|
376
388
|
reason?: string,
|
|
377
|
-
): Promise<
|
|
389
|
+
): Promise<DreamExecution> {
|
|
378
390
|
const paths = getMemoryPaths(cwd);
|
|
379
391
|
const lockPaths = [
|
|
380
392
|
paths.readmeFile,
|
|
@@ -399,13 +411,15 @@ async function runMemoryDream(
|
|
|
399
411
|
const summary = "Memory dream: nothing to consolidate.";
|
|
400
412
|
notify(ctx, summary, "info");
|
|
401
413
|
return {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
414
|
+
result: {
|
|
415
|
+
summary,
|
|
416
|
+
updatedBlocks: [],
|
|
417
|
+
consumedLogs: 0,
|
|
418
|
+
consumedCompactions: 0,
|
|
419
|
+
lastDreamAt: snapshot.state?.lastDreamAt ?? nowIso(),
|
|
420
|
+
lastDreamedLogAt: snapshot.state?.lastDreamedLogAt ?? null,
|
|
421
|
+
summaryPath: null,
|
|
422
|
+
},
|
|
409
423
|
};
|
|
410
424
|
}
|
|
411
425
|
|
|
@@ -419,6 +433,7 @@ async function runMemoryDream(
|
|
|
419
433
|
{
|
|
420
434
|
apiKey: selection.apiKey,
|
|
421
435
|
headers: selection.headers,
|
|
436
|
+
env: selection.env,
|
|
422
437
|
signal: ctx.signal,
|
|
423
438
|
},
|
|
424
439
|
);
|
|
@@ -469,7 +484,7 @@ async function runMemoryDream(
|
|
|
469
484
|
);
|
|
470
485
|
}
|
|
471
486
|
|
|
472
|
-
|
|
487
|
+
const result = await withMemoryMutationQueue(lockPaths, async () => {
|
|
473
488
|
ensureMemoryInitialized(await pathExists(paths.memoryRoot));
|
|
474
489
|
|
|
475
490
|
const currentReplay = await collectDreamReplay(cwd);
|
|
@@ -517,6 +532,8 @@ async function runMemoryDream(
|
|
|
517
532
|
summaryPath,
|
|
518
533
|
};
|
|
519
534
|
});
|
|
535
|
+
|
|
536
|
+
return { result, usage: response.usage };
|
|
520
537
|
}
|
|
521
538
|
|
|
522
539
|
async function loadMemoryPrompt(cwd: string): Promise<string | undefined> {
|
|
@@ -967,6 +984,7 @@ async function selectDreamModel(ctx: ExtensionContext): Promise<ModelSelection>
|
|
|
967
984
|
model: ctx.model,
|
|
968
985
|
apiKey: auth.apiKey,
|
|
969
986
|
headers: auth.headers,
|
|
987
|
+
env: auth.env,
|
|
970
988
|
};
|
|
971
989
|
}
|
|
972
990
|
|
|
@@ -1710,7 +1728,7 @@ export default function memoryExtension(pi: ExtensionAPI): void {
|
|
|
1710
1728
|
description: `Update one .agents/memory/core block while enforcing the shared ${CORE_LINE_CAP}-line and ${CORE_CHAR_CAP}-character caps`,
|
|
1711
1729
|
promptSnippet: `Update one core memory block in .agents/memory/core with ${CORE_LINE_CAP}-line and ${CORE_CHAR_CAP}-character cap enforcement`,
|
|
1712
1730
|
promptGuidelines: [
|
|
1713
|
-
"Use
|
|
1731
|
+
"Use memory_update_block when updating .agents/memory/core/directives.md, context.md, focus.md, or pending.md.",
|
|
1714
1732
|
`If a write would exceed the ${CORE_LINE_CAP}-line or ${CORE_CHAR_CAP}-character core cap, run memory_dream or remove content first.`,
|
|
1715
1733
|
],
|
|
1716
1734
|
parameters: MEMORY_UPDATE_BLOCK_PARAMS,
|
|
@@ -1741,7 +1759,7 @@ export default function memoryExtension(pi: ExtensionAPI): void {
|
|
|
1741
1759
|
description: "Append an entry to .agents/memory/log.md using the repo memory log format",
|
|
1742
1760
|
promptSnippet: "Append an entry to the repo memory log at .agents/memory/log.md",
|
|
1743
1761
|
promptGuidelines: [
|
|
1744
|
-
"Use
|
|
1762
|
+
"Use memory_append_log for important decisions, discoveries, plans, experiments, prompt ingests, and attachment additions worth remembering.",
|
|
1745
1763
|
"Use importance labels high, medium, or low.",
|
|
1746
1764
|
"If this entry replaces or corrects prior memory, set supersedes and/or invalidates links explicitly.",
|
|
1747
1765
|
"The memory log is append-only. Do not rewrite or truncate older entries.",
|
|
@@ -1779,7 +1797,7 @@ export default function memoryExtension(pi: ExtensionAPI): void {
|
|
|
1779
1797
|
promptSnippet: "Consolidate repo memory with dream-based core compression",
|
|
1780
1798
|
promptGuidelines: [
|
|
1781
1799
|
"Dream is the only consolidation mechanism for .agents/memory/core.",
|
|
1782
|
-
"Use
|
|
1800
|
+
"Use memory_dream when logs have accumulated, core needs compression, or recent compaction context should be folded into memory.",
|
|
1783
1801
|
],
|
|
1784
1802
|
parameters: MEMORY_DREAM_PARAMS,
|
|
1785
1803
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
package/extensions/notify.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* - Windows toast: Windows Terminal (WSL)
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
import { execFile } from "node:child_process";
|
|
13
14
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
14
15
|
|
|
15
16
|
const REVIEW_EVENT_START = "review:start";
|
|
@@ -39,7 +40,6 @@ function notifyOSC99(title: string, body: string): void {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
function notifyWindows(title: string, body: string): void {
|
|
42
|
-
const { execFile } = require("child_process");
|
|
43
43
|
execFile("powershell.exe", ["-NoProfile", "-Command", windowsToastScript(title, body)]);
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -92,13 +92,33 @@ export default function (pi: ExtensionAPI) {
|
|
|
92
92
|
let pendingPromptCount = 0;
|
|
93
93
|
const activeReviewSessions = new Set<string>();
|
|
94
94
|
let currentSessionKey: string | undefined;
|
|
95
|
+
let pendingReadyNotification: ReturnType<typeof setImmediate> | undefined;
|
|
95
96
|
|
|
96
97
|
const hasCurrentSessionReviewRun = () => {
|
|
97
98
|
if (!currentSessionKey) return false;
|
|
98
99
|
return activeReviewSessions.has(currentSessionKey);
|
|
99
100
|
};
|
|
100
101
|
|
|
102
|
+
const cancelReadyNotification = () => {
|
|
103
|
+
if (!pendingReadyNotification) return;
|
|
104
|
+
clearImmediate(pendingReadyNotification);
|
|
105
|
+
pendingReadyNotification = undefined;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const scheduleReadyNotification = (ctx: ExtensionContext) => {
|
|
109
|
+
cancelReadyNotification();
|
|
110
|
+
pendingReadyNotification = setImmediate(() => {
|
|
111
|
+
pendingReadyNotification = undefined;
|
|
112
|
+
if (!ctx.isIdle()) return;
|
|
113
|
+
if (pendingPromptCount > 0) return;
|
|
114
|
+
if (hasCurrentSessionReviewRun()) return;
|
|
115
|
+
notify("Pi", "Ready for input");
|
|
116
|
+
});
|
|
117
|
+
pendingReadyNotification.unref?.();
|
|
118
|
+
};
|
|
119
|
+
|
|
101
120
|
pi.on("session_start", async (_event, ctx) => {
|
|
121
|
+
cancelReadyNotification();
|
|
102
122
|
currentSessionKey = getSessionKey(ctx);
|
|
103
123
|
});
|
|
104
124
|
|
|
@@ -143,14 +163,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
143
163
|
notify("Pi", "Review failed");
|
|
144
164
|
});
|
|
145
165
|
|
|
146
|
-
pi.on("
|
|
166
|
+
pi.on("agent_settled", async (_event, ctx) => {
|
|
147
167
|
currentSessionKey = getSessionKey(ctx);
|
|
148
|
-
|
|
149
|
-
if (hasCurrentSessionReviewRun()) return;
|
|
150
|
-
notify("Pi", "Ready for input");
|
|
168
|
+
scheduleReadyNotification(ctx);
|
|
151
169
|
});
|
|
152
170
|
|
|
153
171
|
pi.on("session_shutdown", async (_event, ctx) => {
|
|
172
|
+
cancelReadyNotification();
|
|
154
173
|
pendingPromptCount = 0;
|
|
155
174
|
const sessionKey = getSessionKey(ctx);
|
|
156
175
|
activeReviewSessions.delete(sessionKey);
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
-
import os from "node:os";
|
|
3
2
|
import path from "node:path";
|
|
4
|
-
import
|
|
3
|
+
import {
|
|
4
|
+
getAgentDir,
|
|
5
|
+
type ExtensionAPI,
|
|
6
|
+
type ExtensionContext,
|
|
7
|
+
} from "@earendil-works/pi-coding-agent";
|
|
5
8
|
|
|
6
9
|
const STATUS_KEY = "openai-fast";
|
|
7
10
|
const SUPPORTED_APIS = new Set([
|
|
@@ -23,8 +26,7 @@ function emptyConfig(): FastConfig {
|
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
function getConfigPath(): string {
|
|
26
|
-
|
|
27
|
-
return path.join(agentDir, "openai-fast.json");
|
|
29
|
+
return path.join(getAgentDir(), "openai-fast.json");
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
function isObject(value: unknown): value is JsonObject {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
-
import os from "node:os";
|
|
3
2
|
import path from "node:path";
|
|
4
|
-
import
|
|
3
|
+
import {
|
|
4
|
+
getAgentDir,
|
|
5
|
+
type ExtensionAPI,
|
|
6
|
+
type ExtensionContext,
|
|
7
|
+
} from "@earendil-works/pi-coding-agent";
|
|
5
8
|
|
|
6
9
|
const STATUS_KEY = "openai-verbosity";
|
|
7
10
|
const SUPPORTED_APIS = new Set([
|
|
@@ -23,8 +26,7 @@ function emptyConfig(): VerbosityConfig {
|
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
function getConfigPath(): string {
|
|
26
|
-
|
|
27
|
-
return path.join(agentDir, "openai-verbosity.json");
|
|
29
|
+
return path.join(getAgentDir(), "openai-verbosity.json");
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
function isObject(value: unknown): value is JsonObject {
|