tau-coding-agent 0.1.6 → 0.2.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/README.md +15 -12
- package/extensions/answer.ts +129 -79
- package/extensions/branch-term/README.md +7 -0
- package/extensions/{branch-term.ts → branch-term/index.ts} +113 -104
- package/extensions/btw.ts +17 -2
- package/extensions/caffeinate/README.md +5 -0
- package/extensions/caffeinate/index.ts +144 -0
- package/extensions/fast.ts +292 -0
- package/extensions/ghostty.ts +214 -211
- package/extensions/git-diff-stats.ts +124 -84
- package/extensions/git-pr-status.ts +274 -208
- package/extensions/insights.ts +109 -191
- package/extensions/loop.ts +149 -145
- package/extensions/memory.ts +138 -86
- package/extensions/notify.ts +14 -26
- package/extensions/openai-verbosity.ts +108 -42
- package/extensions/review/fix.ts +15 -6
- package/extensions/review/git.ts +93 -103
- package/extensions/review/index.ts +60 -62
- package/extensions/review/interrupt.ts +117 -0
- package/extensions/review/message-queue.ts +45 -12
- package/extensions/review/models.ts +79 -3
- package/extensions/review/prompts.ts +41 -38
- package/extensions/review/review.ts +161 -166
- package/extensions/review/runner.ts +139 -147
- package/extensions/review/runtime.ts +173 -120
- package/extensions/review/triage.ts +41 -44
- package/extensions/sandbox/bash.ts +775 -0
- package/extensions/sandbox/command.ts +605 -0
- package/extensions/sandbox/config.ts +764 -0
- package/extensions/sandbox/index.ts +132 -3338
- package/extensions/sandbox/permissions/dialog.ts +118 -0
- package/extensions/sandbox/permissions/filesystem.ts +559 -0
- package/extensions/sandbox/permissions/mach-lookup.ts +186 -0
- package/extensions/sandbox/permissions/network.ts +164 -0
- package/extensions/sandbox/permissions/unsandboxed.ts +270 -0
- package/extensions/sandbox/runtime.ts +616 -0
- package/extensions/stash.ts +28 -15
- package/extensions/subagent/README.md +73 -0
- package/extensions/subagent/index.ts +822 -0
- package/extensions/subagent/interrupt.ts +117 -0
- package/extensions/subagent/permissions.ts +101 -0
- package/extensions/subagent/rpc.ts +177 -0
- package/extensions/tool-display-mode.ts +267 -64
- package/extensions/usage/index.ts +249 -356
- package/extensions/usage/openrouter.ts +10 -2
- package/extensions/websearch/README.md +12 -47
- package/extensions/websearch/config.ts +5 -2
- package/extensions/websearch/index.ts +94 -109
- package/extensions/websearch/output.ts +51 -0
- package/extensions/websearch/providers/anthropic.pi.ts +27 -44
- package/extensions/websearch/providers/gemini.browser.ts +68 -77
- package/extensions/websearch/providers/gemini.pi.ts +17 -17
- package/extensions/websearch/providers/openai-codex.pi.ts +145 -7
- package/extensions/websearch/providers/pi-model.shared.ts +33 -39
- package/extensions/websearch/providers/shared.ts +87 -2
- package/extensions/websearch/types.ts +0 -3
- package/extensions/worktree.ts +132 -172
- package/package.json +10 -5
- package/skills/browser-tools/SKILL.md +29 -234
- package/skills/browser-tools/references/cookies.md +36 -0
- package/skills/browser-tools/references/interaction.md +90 -0
- package/skills/browser-tools/references/logging.md +34 -0
- package/skills/git-clean-history/SKILL.md +6 -6
- package/skills/git-commit/SKILL.md +5 -3
- package/skills/github-pull-request/SKILL.md +60 -0
- package/skills/github-pull-request/references/create.md +40 -0
- package/skills/github-pull-request/references/stewardship.md +60 -0
- package/skills/oracle/SKILL.md +3 -3
- package/skills/oracle/scripts/oracle +10 -1
- package/skills/sentry/SKILL.md +15 -185
- package/skills/sentry/references/events.md +79 -0
- package/skills/sentry/references/issues.md +62 -0
- package/skills/sentry/references/logs.md +46 -0
- package/skills/update-changelog/SKILL.md +27 -121
- package/skills/web-design/SKILL.md +16 -105
- package/themes/tau-dark.json +4 -0
- package/extensions/openai-fast.ts +0 -229
- package/extensions/websearch/providers/openai-codex.browser.ts +0 -77
- package/extensions/websearch/providers/openai-codex.shared.ts +0 -123
package/extensions/loop.ts
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { Type } from "typebox";
|
|
10
|
-
import { complete } from "@earendil-works/pi-ai/compat";
|
|
11
10
|
import type { Api, Model, UserMessage } from "@earendil-works/pi-ai";
|
|
12
11
|
import {
|
|
13
12
|
compact,
|
|
@@ -25,7 +24,7 @@ const LOOP_PRESETS = [
|
|
|
25
24
|
] as const;
|
|
26
25
|
|
|
27
26
|
const LOOP_STATE_ENTRY = "loop-state";
|
|
28
|
-
const OPENAI_FAST_MODEL_ID = "gpt-5.
|
|
27
|
+
const OPENAI_FAST_MODEL_ID = "gpt-5.6-luna";
|
|
29
28
|
const ANTHROPIC_FAST_MODEL_ID = "claude-haiku-4-5";
|
|
30
29
|
|
|
31
30
|
const SUMMARY_SYSTEM_PROMPT = `You summarize loop breakout conditions for a status widget.
|
|
@@ -47,30 +46,12 @@ type LoopStateData = {
|
|
|
47
46
|
loopCount?: number;
|
|
48
47
|
};
|
|
49
48
|
|
|
50
|
-
type PromptStatus = "completed" | "error";
|
|
51
49
|
type ModelFamily = "openai" | "anthropic";
|
|
52
50
|
|
|
53
|
-
type
|
|
51
|
+
type ModelSelection = {
|
|
54
52
|
model: Model<Api>;
|
|
55
|
-
apiKey?: string;
|
|
56
|
-
headers?: Record<string, string>;
|
|
57
|
-
env?: Record<string, string>;
|
|
58
53
|
};
|
|
59
54
|
|
|
60
|
-
async function withPromptSignal<T>(pi: ExtensionAPI, run: () => Promise<T>): Promise<T> {
|
|
61
|
-
pi.events.emit("ui:prompt_start", { source: "loop" });
|
|
62
|
-
|
|
63
|
-
let status: PromptStatus = "completed";
|
|
64
|
-
try {
|
|
65
|
-
return await run();
|
|
66
|
-
} catch (error) {
|
|
67
|
-
status = "error";
|
|
68
|
-
throw error;
|
|
69
|
-
} finally {
|
|
70
|
-
pi.events.emit("ui:prompt_end", { source: "loop", status });
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
55
|
function detectModelFamily(provider: string): ModelFamily | null {
|
|
75
56
|
const normalizedProvider = provider.toLowerCase();
|
|
76
57
|
if (normalizedProvider.includes("openai")) return "openai";
|
|
@@ -121,7 +102,11 @@ function getConditionText(mode: LoopMode, condition?: string): string {
|
|
|
121
102
|
}
|
|
122
103
|
}
|
|
123
104
|
|
|
124
|
-
async function selectSummaryModel(
|
|
105
|
+
async function selectSummaryModel(
|
|
106
|
+
ctx: ExtensionContext,
|
|
107
|
+
signal: AbortSignal,
|
|
108
|
+
): Promise<ModelSelection | null> {
|
|
109
|
+
signal.throwIfAborted();
|
|
125
110
|
if (!ctx.model) return null;
|
|
126
111
|
|
|
127
112
|
const family = detectModelFamily(ctx.model.provider);
|
|
@@ -137,30 +122,24 @@ async function selectSummaryModel(ctx: ExtensionContext): Promise<ModelRequestAu
|
|
|
137
122
|
if (!candidate) continue;
|
|
138
123
|
|
|
139
124
|
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(candidate);
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
model: candidate,
|
|
143
|
-
apiKey: auth.apiKey,
|
|
144
|
-
headers: auth.headers,
|
|
145
|
-
env: auth.env,
|
|
146
|
-
};
|
|
147
|
-
}
|
|
125
|
+
signal.throwIfAborted();
|
|
126
|
+
if (auth.ok) return { model: candidate };
|
|
148
127
|
}
|
|
149
128
|
}
|
|
150
129
|
|
|
151
130
|
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(ctx.model);
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
: null;
|
|
131
|
+
signal.throwIfAborted();
|
|
132
|
+
return auth.ok ? { model: ctx.model } : null;
|
|
155
133
|
}
|
|
156
134
|
|
|
157
135
|
async function summarizeBreakoutCondition(
|
|
158
136
|
ctx: ExtensionContext,
|
|
159
137
|
mode: LoopMode,
|
|
160
|
-
condition
|
|
138
|
+
condition: string | undefined,
|
|
139
|
+
signal: AbortSignal,
|
|
161
140
|
): Promise<string> {
|
|
162
141
|
const fallback = summarizeCondition(mode, condition);
|
|
163
|
-
const selection = await selectSummaryModel(ctx);
|
|
142
|
+
const selection = await selectSummaryModel(ctx, signal);
|
|
164
143
|
if (!selection) return fallback;
|
|
165
144
|
|
|
166
145
|
const conditionText = getConditionText(mode, condition);
|
|
@@ -170,10 +149,13 @@ async function summarizeBreakoutCondition(
|
|
|
170
149
|
timestamp: Date.now(),
|
|
171
150
|
};
|
|
172
151
|
|
|
173
|
-
const response = await complete(
|
|
152
|
+
const response = await ctx.modelRegistry.complete(
|
|
174
153
|
selection.model,
|
|
175
|
-
{
|
|
176
|
-
|
|
154
|
+
{
|
|
155
|
+
systemPrompt: SUMMARY_SYSTEM_PROMPT,
|
|
156
|
+
messages: [userMessage],
|
|
157
|
+
},
|
|
158
|
+
{ signal },
|
|
177
159
|
);
|
|
178
160
|
|
|
179
161
|
if (response.stopReason === "aborted" || response.stopReason === "error") {
|
|
@@ -209,8 +191,8 @@ function updateStatus(ctx: ExtensionContext, state: LoopStateData): void {
|
|
|
209
191
|
ctx.ui.setWidget("loop", [ctx.ui.theme.fg("accent", text)]);
|
|
210
192
|
}
|
|
211
193
|
|
|
212
|
-
|
|
213
|
-
const entries = ctx.sessionManager.
|
|
194
|
+
function loadState(ctx: ExtensionContext): LoopStateData {
|
|
195
|
+
const entries = ctx.sessionManager.getBranch();
|
|
214
196
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
215
197
|
const entry = entries[i] as { type: string; customType?: string; data?: LoopStateData };
|
|
216
198
|
if (entry.type === "custom" && entry.customType === LOOP_STATE_ENTRY && entry.data) {
|
|
@@ -222,41 +204,74 @@ async function loadState(ctx: ExtensionContext): Promise<LoopStateData> {
|
|
|
222
204
|
|
|
223
205
|
export default function loopExtension(pi: ExtensionAPI): void {
|
|
224
206
|
let loopState: LoopStateData = { active: false };
|
|
225
|
-
let
|
|
207
|
+
let loopGeneration = 0;
|
|
208
|
+
let lastAssistantStopReason: string | undefined;
|
|
226
209
|
let pendingLoopPrompt: ReturnType<typeof setImmediate> | undefined;
|
|
210
|
+
let pendingSummary: { controller: AbortController; done: Promise<void> } | undefined;
|
|
227
211
|
|
|
228
212
|
function persistState(state: LoopStateData): void {
|
|
229
213
|
pi.appendEntry(LOOP_STATE_ENTRY, state);
|
|
230
214
|
}
|
|
231
215
|
|
|
232
|
-
function setLoopState(state: LoopStateData, ctx: ExtensionContext):
|
|
216
|
+
async function setLoopState(state: LoopStateData, ctx: ExtensionContext): Promise<boolean> {
|
|
217
|
+
const generation = ++loopGeneration;
|
|
218
|
+
cancelScheduledLoopPrompt();
|
|
233
219
|
loopState = state;
|
|
234
|
-
|
|
235
|
-
|
|
220
|
+
const cancelled = cancelSummary();
|
|
221
|
+
try {
|
|
222
|
+
persistState(state);
|
|
223
|
+
updateStatus(ctx, state);
|
|
224
|
+
} finally {
|
|
225
|
+
await cancelled;
|
|
226
|
+
}
|
|
227
|
+
return generation === loopGeneration;
|
|
236
228
|
}
|
|
237
229
|
|
|
238
|
-
function clearLoopState(ctx: ExtensionContext): void {
|
|
239
|
-
|
|
240
|
-
loopState = cleared;
|
|
241
|
-
persistState(cleared);
|
|
242
|
-
updateStatus(ctx, cleared);
|
|
230
|
+
async function clearLoopState(ctx: ExtensionContext): Promise<void> {
|
|
231
|
+
await setLoopState({ active: false }, ctx);
|
|
243
232
|
}
|
|
244
233
|
|
|
245
|
-
function breakLoop(ctx: ExtensionContext): void {
|
|
246
|
-
clearLoopState(ctx);
|
|
234
|
+
async function breakLoop(ctx: ExtensionContext): Promise<void> {
|
|
235
|
+
await clearLoopState(ctx);
|
|
247
236
|
ctx.ui.notify("Loop ended", "info");
|
|
248
237
|
}
|
|
249
238
|
|
|
250
|
-
function
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
239
|
+
async function cancelSummary(): Promise<void> {
|
|
240
|
+
const summary = pendingSummary;
|
|
241
|
+
if (!summary) return;
|
|
242
|
+
summary.controller.abort();
|
|
243
|
+
await summary.done;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function startSummary(ctx: ExtensionContext): void {
|
|
247
|
+
if (!loopState.active || !loopState.mode || loopState.summary) return;
|
|
248
|
+
const { mode, condition } = loopState;
|
|
249
|
+
const generation = loopGeneration;
|
|
250
|
+
// A loop can survive multiple turns and declined abort confirmations.
|
|
251
|
+
const controller = new AbortController();
|
|
252
|
+
const done = (async () => {
|
|
253
|
+
let summary: string;
|
|
254
|
+
try {
|
|
255
|
+
summary = await summarizeBreakoutCondition(ctx, mode, condition, controller.signal);
|
|
256
|
+
} catch (error) {
|
|
257
|
+
if (controller.signal.aborted || generation !== loopGeneration) return;
|
|
258
|
+
summary = summarizeCondition(mode, condition);
|
|
259
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
260
|
+
ctx.ui.notify(`Loop summary failed: ${message}`, "warning");
|
|
257
261
|
}
|
|
258
|
-
|
|
259
|
-
|
|
262
|
+
if (controller.signal.aborted || generation !== loopGeneration) return;
|
|
263
|
+
loopState = { ...loopState, summary };
|
|
264
|
+
persistState(loopState);
|
|
265
|
+
updateStatus(ctx, loopState);
|
|
266
|
+
})().finally(() => {
|
|
267
|
+
if (pendingSummary?.controller === controller) pendingSummary = undefined;
|
|
268
|
+
});
|
|
269
|
+
pendingSummary = { controller, done };
|
|
270
|
+
// Publication failures must also be observed when no lifecycle handler is joining the task.
|
|
271
|
+
void done.catch((error: unknown) => {
|
|
272
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
273
|
+
ctx.ui.notify(`Loop summary failed: ${message}`, "warning");
|
|
274
|
+
});
|
|
260
275
|
}
|
|
261
276
|
|
|
262
277
|
function triggerLoopPrompt(ctx: ExtensionContext): void {
|
|
@@ -306,41 +321,39 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
306
321
|
description: preset.description,
|
|
307
322
|
}));
|
|
308
323
|
|
|
309
|
-
const selection = await
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
render(width
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
invalidate()
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
handleInput(data
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
}),
|
|
343
|
-
);
|
|
324
|
+
const selection = await ctx.ui.custom<string | null>((tui, theme, _kb, done) => {
|
|
325
|
+
const container = new Container();
|
|
326
|
+
container.addChild(new DynamicBorder((str) => theme.fg("accent", str)));
|
|
327
|
+
container.addChild(new Text(theme.fg("accent", theme.bold("Select a loop preset"))));
|
|
328
|
+
|
|
329
|
+
const selectList = new SelectList(items, Math.min(items.length, 10), {
|
|
330
|
+
selectedPrefix: (text) => theme.fg("accent", text),
|
|
331
|
+
selectedText: (text) => theme.fg("accent", text),
|
|
332
|
+
description: (text) => theme.fg("muted", text),
|
|
333
|
+
scrollInfo: (text) => theme.fg("dim", text),
|
|
334
|
+
noMatch: (text) => theme.fg("warning", text),
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
selectList.onSelect = (item) => done(item.value);
|
|
338
|
+
selectList.onCancel = () => done(null);
|
|
339
|
+
|
|
340
|
+
container.addChild(selectList);
|
|
341
|
+
container.addChild(new Text(theme.fg("dim", "Press enter to confirm or esc to cancel")));
|
|
342
|
+
container.addChild(new DynamicBorder((str) => theme.fg("accent", str)));
|
|
343
|
+
|
|
344
|
+
return {
|
|
345
|
+
render(width: number) {
|
|
346
|
+
return container.render(width);
|
|
347
|
+
},
|
|
348
|
+
invalidate() {
|
|
349
|
+
container.invalidate();
|
|
350
|
+
},
|
|
351
|
+
handleInput(data: string) {
|
|
352
|
+
selectList.handleInput(data);
|
|
353
|
+
tui.requestRender();
|
|
354
|
+
},
|
|
355
|
+
};
|
|
356
|
+
});
|
|
344
357
|
|
|
345
358
|
if (!selection) return null;
|
|
346
359
|
|
|
@@ -350,9 +363,7 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
350
363
|
case "self":
|
|
351
364
|
return { active: true, mode: "self", prompt: buildPrompt("self") };
|
|
352
365
|
case "custom": {
|
|
353
|
-
const condition = await
|
|
354
|
-
ctx.ui.editor("Enter loop breakout condition:", ""),
|
|
355
|
-
);
|
|
366
|
+
const condition = await ctx.ui.editor("Enter loop breakout condition:", "");
|
|
356
367
|
if (!condition?.trim()) return null;
|
|
357
368
|
return {
|
|
358
369
|
active: true,
|
|
@@ -428,7 +439,7 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
428
439
|
};
|
|
429
440
|
}
|
|
430
441
|
|
|
431
|
-
clearLoopState(ctx);
|
|
442
|
+
await clearLoopState(ctx);
|
|
432
443
|
|
|
433
444
|
return {
|
|
434
445
|
content: [{ type: "text", text: "Loop ended." }],
|
|
@@ -458,9 +469,7 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
458
469
|
|
|
459
470
|
if (loopState.active) {
|
|
460
471
|
const confirm = ctx.hasUI
|
|
461
|
-
? await
|
|
462
|
-
ctx.ui.confirm("Replace active loop?", "A loop is already active. Replace it?"),
|
|
463
|
-
)
|
|
472
|
+
? await ctx.ui.confirm("Replace active loop?", "A loop is already active. Replace it?")
|
|
464
473
|
: true;
|
|
465
474
|
if (!confirm) {
|
|
466
475
|
ctx.ui.notify("Loop unchanged", "info");
|
|
@@ -469,36 +478,37 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
469
478
|
}
|
|
470
479
|
|
|
471
480
|
const summarizedState: LoopStateData = { ...nextState, summary: undefined, loopCount: 0 };
|
|
472
|
-
setLoopState(summarizedState, ctx);
|
|
481
|
+
if (!(await setLoopState(summarizedState, ctx))) return;
|
|
473
482
|
ctx.ui.notify("Loop active", "info");
|
|
474
483
|
triggerLoopPrompt(ctx);
|
|
475
484
|
|
|
476
|
-
|
|
477
|
-
const condition = nextState.condition;
|
|
478
|
-
void (async () => {
|
|
479
|
-
const summary = await summarizeBreakoutCondition(ctx, mode, condition);
|
|
480
|
-
if (!loopState.active || loopState.mode !== mode || loopState.condition !== condition)
|
|
481
|
-
return;
|
|
482
|
-
loopState = { ...loopState, summary };
|
|
483
|
-
persistState(loopState);
|
|
484
|
-
updateStatus(ctx, loopState);
|
|
485
|
-
})();
|
|
485
|
+
startSummary(ctx);
|
|
486
486
|
},
|
|
487
487
|
});
|
|
488
488
|
|
|
489
|
-
pi.on("agent_end",
|
|
490
|
-
|
|
489
|
+
pi.on("agent_end", (event) => {
|
|
490
|
+
lastAssistantStopReason = event.messages.findLast(
|
|
491
|
+
(message) => message.role === "assistant",
|
|
492
|
+
)?.stopReason;
|
|
491
493
|
});
|
|
492
494
|
|
|
493
495
|
pi.on("agent_settled", async (_event, ctx) => {
|
|
494
496
|
if (!loopState.active) return;
|
|
495
497
|
|
|
496
|
-
if (
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
498
|
+
if (lastAssistantStopReason === "error") {
|
|
499
|
+
await clearLoopState(ctx);
|
|
500
|
+
ctx.ui.notify("Loop stopped after an agent error", "error");
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
if (lastAssistantStopReason === "aborted") {
|
|
505
|
+
const generation = loopGeneration;
|
|
506
|
+
const confirm =
|
|
507
|
+
!ctx.hasUI ||
|
|
508
|
+
(await ctx.ui.confirm("Break active loop?", "Operation aborted. Break out of the loop?"));
|
|
509
|
+
if (generation !== loopGeneration) return;
|
|
500
510
|
if (confirm) {
|
|
501
|
-
breakLoop(ctx);
|
|
511
|
+
await breakLoop(ctx);
|
|
502
512
|
return;
|
|
503
513
|
}
|
|
504
514
|
}
|
|
@@ -508,9 +518,6 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
508
518
|
|
|
509
519
|
pi.on("session_before_compact", async (event, ctx) => {
|
|
510
520
|
if (!loopState.active || !loopState.mode || !ctx.model) return;
|
|
511
|
-
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(ctx.model);
|
|
512
|
-
if (!auth.ok) return;
|
|
513
|
-
|
|
514
521
|
const instructionParts = [
|
|
515
522
|
event.customInstructions,
|
|
516
523
|
getCompactionInstructions(loopState.mode, loopState.condition),
|
|
@@ -522,17 +529,16 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
522
529
|
const compaction = await compact(
|
|
523
530
|
event.preparation,
|
|
524
531
|
ctx.model,
|
|
525
|
-
|
|
526
|
-
|
|
532
|
+
undefined,
|
|
533
|
+
undefined,
|
|
527
534
|
instructionParts,
|
|
528
535
|
event.signal,
|
|
529
536
|
ctx.thinkingLevel,
|
|
530
|
-
|
|
531
|
-
auth.env,
|
|
537
|
+
ctx.modelRegistry.streamSimple.bind(ctx.modelRegistry),
|
|
532
538
|
);
|
|
533
539
|
return { compaction };
|
|
534
540
|
} catch (error) {
|
|
535
|
-
if (ctx.hasUI) {
|
|
541
|
+
if (ctx.hasUI && !event.signal.aborted) {
|
|
536
542
|
const message = error instanceof Error ? error.message : String(error);
|
|
537
543
|
ctx.ui.notify(`Loop compaction failed: ${message}`, "warning");
|
|
538
544
|
}
|
|
@@ -541,28 +547,26 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
541
547
|
});
|
|
542
548
|
|
|
543
549
|
async function restoreLoopState(ctx: ExtensionContext): Promise<void> {
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
return;
|
|
554
|
-
loopState = { ...loopState, summary };
|
|
555
|
-
persistState(loopState);
|
|
556
|
-
updateStatus(ctx, loopState);
|
|
557
|
-
})();
|
|
550
|
+
cancelScheduledLoopPrompt();
|
|
551
|
+
const generation = ++loopGeneration;
|
|
552
|
+
lastAssistantStopReason = undefined;
|
|
553
|
+
const cancelled = cancelSummary();
|
|
554
|
+
try {
|
|
555
|
+
loopState = loadState(ctx);
|
|
556
|
+
updateStatus(ctx, loopState);
|
|
557
|
+
} finally {
|
|
558
|
+
await cancelled;
|
|
558
559
|
}
|
|
560
|
+
if (generation === loopGeneration) startSummary(ctx);
|
|
559
561
|
}
|
|
560
562
|
|
|
561
|
-
pi.on("session_start",
|
|
562
|
-
|
|
563
|
-
|
|
563
|
+
pi.on("session_start", (_event, ctx) => restoreLoopState(ctx));
|
|
564
|
+
|
|
565
|
+
pi.on("session_tree", (_event, ctx) => restoreLoopState(ctx));
|
|
564
566
|
|
|
565
567
|
pi.on("session_shutdown", async () => {
|
|
568
|
+
loopGeneration += 1;
|
|
566
569
|
cancelScheduledLoopPrompt();
|
|
570
|
+
await cancelSummary();
|
|
567
571
|
});
|
|
568
572
|
}
|