tau-coding-agent 0.1.5 → 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 -73
- package/extensions/branch-term/README.md +7 -0
- package/extensions/{branch-term.ts → branch-term/index.ts} +113 -104
- package/extensions/btw.ts +90 -4
- 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 +138 -183
- package/extensions/loop.ts +150 -135
- package/extensions/memory.ts +172 -97
- 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 +43 -40
- package/extensions/review/review.ts +161 -166
- package/extensions/review/runner.ts +139 -147
- package/extensions/review/runtime.ts +173 -120
- package/extensions/review/submit-review-tool.ts +1 -5
- package/extensions/review/submit-triage-tool.ts +1 -0
- 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 +138 -2850
- package/extensions/sandbox/macos-sandbox-shell.mjs +202 -0
- 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/anthropic.ts +5 -6
- package/extensions/usage/github-copilot.ts +30 -9
- package/extensions/usage/index.ts +250 -357
- package/extensions/usage/openai-codex.ts +2 -2
- 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 +44 -32
- 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 +4 -4
- package/skills/oracle/scripts/oracle +64 -51
- 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,29 +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
53
|
};
|
|
58
54
|
|
|
59
|
-
async function withPromptSignal<T>(pi: ExtensionAPI, run: () => Promise<T>): Promise<T> {
|
|
60
|
-
pi.events.emit("ui:prompt_start", { source: "loop" });
|
|
61
|
-
|
|
62
|
-
let status: PromptStatus = "completed";
|
|
63
|
-
try {
|
|
64
|
-
return await run();
|
|
65
|
-
} catch (error) {
|
|
66
|
-
status = "error";
|
|
67
|
-
throw error;
|
|
68
|
-
} finally {
|
|
69
|
-
pi.events.emit("ui:prompt_end", { source: "loop", status });
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
55
|
function detectModelFamily(provider: string): ModelFamily | null {
|
|
74
56
|
const normalizedProvider = provider.toLowerCase();
|
|
75
57
|
if (normalizedProvider.includes("openai")) return "openai";
|
|
@@ -120,7 +102,11 @@ function getConditionText(mode: LoopMode, condition?: string): string {
|
|
|
120
102
|
}
|
|
121
103
|
}
|
|
122
104
|
|
|
123
|
-
async function selectSummaryModel(
|
|
105
|
+
async function selectSummaryModel(
|
|
106
|
+
ctx: ExtensionContext,
|
|
107
|
+
signal: AbortSignal,
|
|
108
|
+
): Promise<ModelSelection | null> {
|
|
109
|
+
signal.throwIfAborted();
|
|
124
110
|
if (!ctx.model) return null;
|
|
125
111
|
|
|
126
112
|
const family = detectModelFamily(ctx.model.provider);
|
|
@@ -136,23 +122,24 @@ async function selectSummaryModel(ctx: ExtensionContext): Promise<ModelRequestAu
|
|
|
136
122
|
if (!candidate) continue;
|
|
137
123
|
|
|
138
124
|
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(candidate);
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
125
|
+
signal.throwIfAborted();
|
|
126
|
+
if (auth.ok) return { model: candidate };
|
|
142
127
|
}
|
|
143
128
|
}
|
|
144
129
|
|
|
145
130
|
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(ctx.model);
|
|
146
|
-
|
|
131
|
+
signal.throwIfAborted();
|
|
132
|
+
return auth.ok ? { model: ctx.model } : null;
|
|
147
133
|
}
|
|
148
134
|
|
|
149
135
|
async function summarizeBreakoutCondition(
|
|
150
136
|
ctx: ExtensionContext,
|
|
151
137
|
mode: LoopMode,
|
|
152
|
-
condition
|
|
138
|
+
condition: string | undefined,
|
|
139
|
+
signal: AbortSignal,
|
|
153
140
|
): Promise<string> {
|
|
154
141
|
const fallback = summarizeCondition(mode, condition);
|
|
155
|
-
const selection = await selectSummaryModel(ctx);
|
|
142
|
+
const selection = await selectSummaryModel(ctx, signal);
|
|
156
143
|
if (!selection) return fallback;
|
|
157
144
|
|
|
158
145
|
const conditionText = getConditionText(mode, condition);
|
|
@@ -162,10 +149,13 @@ async function summarizeBreakoutCondition(
|
|
|
162
149
|
timestamp: Date.now(),
|
|
163
150
|
};
|
|
164
151
|
|
|
165
|
-
const response = await complete(
|
|
152
|
+
const response = await ctx.modelRegistry.complete(
|
|
166
153
|
selection.model,
|
|
167
|
-
{
|
|
168
|
-
|
|
154
|
+
{
|
|
155
|
+
systemPrompt: SUMMARY_SYSTEM_PROMPT,
|
|
156
|
+
messages: [userMessage],
|
|
157
|
+
},
|
|
158
|
+
{ signal },
|
|
169
159
|
);
|
|
170
160
|
|
|
171
161
|
if (response.stopReason === "aborted" || response.stopReason === "error") {
|
|
@@ -201,8 +191,8 @@ function updateStatus(ctx: ExtensionContext, state: LoopStateData): void {
|
|
|
201
191
|
ctx.ui.setWidget("loop", [ctx.ui.theme.fg("accent", text)]);
|
|
202
192
|
}
|
|
203
193
|
|
|
204
|
-
|
|
205
|
-
const entries = ctx.sessionManager.
|
|
194
|
+
function loadState(ctx: ExtensionContext): LoopStateData {
|
|
195
|
+
const entries = ctx.sessionManager.getBranch();
|
|
206
196
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
207
197
|
const entry = entries[i] as { type: string; customType?: string; data?: LoopStateData };
|
|
208
198
|
if (entry.type === "custom" && entry.customType === LOOP_STATE_ENTRY && entry.data) {
|
|
@@ -214,41 +204,74 @@ async function loadState(ctx: ExtensionContext): Promise<LoopStateData> {
|
|
|
214
204
|
|
|
215
205
|
export default function loopExtension(pi: ExtensionAPI): void {
|
|
216
206
|
let loopState: LoopStateData = { active: false };
|
|
217
|
-
let
|
|
207
|
+
let loopGeneration = 0;
|
|
208
|
+
let lastAssistantStopReason: string | undefined;
|
|
218
209
|
let pendingLoopPrompt: ReturnType<typeof setImmediate> | undefined;
|
|
210
|
+
let pendingSummary: { controller: AbortController; done: Promise<void> } | undefined;
|
|
219
211
|
|
|
220
212
|
function persistState(state: LoopStateData): void {
|
|
221
213
|
pi.appendEntry(LOOP_STATE_ENTRY, state);
|
|
222
214
|
}
|
|
223
215
|
|
|
224
|
-
function setLoopState(state: LoopStateData, ctx: ExtensionContext):
|
|
216
|
+
async function setLoopState(state: LoopStateData, ctx: ExtensionContext): Promise<boolean> {
|
|
217
|
+
const generation = ++loopGeneration;
|
|
218
|
+
cancelScheduledLoopPrompt();
|
|
225
219
|
loopState = state;
|
|
226
|
-
|
|
227
|
-
|
|
220
|
+
const cancelled = cancelSummary();
|
|
221
|
+
try {
|
|
222
|
+
persistState(state);
|
|
223
|
+
updateStatus(ctx, state);
|
|
224
|
+
} finally {
|
|
225
|
+
await cancelled;
|
|
226
|
+
}
|
|
227
|
+
return generation === loopGeneration;
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
-
function clearLoopState(ctx: ExtensionContext): void {
|
|
231
|
-
|
|
232
|
-
loopState = cleared;
|
|
233
|
-
persistState(cleared);
|
|
234
|
-
updateStatus(ctx, cleared);
|
|
230
|
+
async function clearLoopState(ctx: ExtensionContext): Promise<void> {
|
|
231
|
+
await setLoopState({ active: false }, ctx);
|
|
235
232
|
}
|
|
236
233
|
|
|
237
|
-
function breakLoop(ctx: ExtensionContext): void {
|
|
238
|
-
clearLoopState(ctx);
|
|
234
|
+
async function breakLoop(ctx: ExtensionContext): Promise<void> {
|
|
235
|
+
await clearLoopState(ctx);
|
|
239
236
|
ctx.ui.notify("Loop ended", "info");
|
|
240
237
|
}
|
|
241
238
|
|
|
242
|
-
function
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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");
|
|
249
261
|
}
|
|
250
|
-
|
|
251
|
-
|
|
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
|
+
});
|
|
252
275
|
}
|
|
253
276
|
|
|
254
277
|
function triggerLoopPrompt(ctx: ExtensionContext): void {
|
|
@@ -298,41 +321,39 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
298
321
|
description: preset.description,
|
|
299
322
|
}));
|
|
300
323
|
|
|
301
|
-
const selection = await
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
render(width
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
invalidate()
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
handleInput(data
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
}),
|
|
335
|
-
);
|
|
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
|
+
});
|
|
336
357
|
|
|
337
358
|
if (!selection) return null;
|
|
338
359
|
|
|
@@ -342,9 +363,7 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
342
363
|
case "self":
|
|
343
364
|
return { active: true, mode: "self", prompt: buildPrompt("self") };
|
|
344
365
|
case "custom": {
|
|
345
|
-
const condition = await
|
|
346
|
-
ctx.ui.editor("Enter loop breakout condition:", ""),
|
|
347
|
-
);
|
|
366
|
+
const condition = await ctx.ui.editor("Enter loop breakout condition:", "");
|
|
348
367
|
if (!condition?.trim()) return null;
|
|
349
368
|
return {
|
|
350
369
|
active: true,
|
|
@@ -420,7 +439,7 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
420
439
|
};
|
|
421
440
|
}
|
|
422
441
|
|
|
423
|
-
clearLoopState(ctx);
|
|
442
|
+
await clearLoopState(ctx);
|
|
424
443
|
|
|
425
444
|
return {
|
|
426
445
|
content: [{ type: "text", text: "Loop ended." }],
|
|
@@ -450,9 +469,7 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
450
469
|
|
|
451
470
|
if (loopState.active) {
|
|
452
471
|
const confirm = ctx.hasUI
|
|
453
|
-
? await
|
|
454
|
-
ctx.ui.confirm("Replace active loop?", "A loop is already active. Replace it?"),
|
|
455
|
-
)
|
|
472
|
+
? await ctx.ui.confirm("Replace active loop?", "A loop is already active. Replace it?")
|
|
456
473
|
: true;
|
|
457
474
|
if (!confirm) {
|
|
458
475
|
ctx.ui.notify("Loop unchanged", "info");
|
|
@@ -461,36 +478,37 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
461
478
|
}
|
|
462
479
|
|
|
463
480
|
const summarizedState: LoopStateData = { ...nextState, summary: undefined, loopCount: 0 };
|
|
464
|
-
setLoopState(summarizedState, ctx);
|
|
481
|
+
if (!(await setLoopState(summarizedState, ctx))) return;
|
|
465
482
|
ctx.ui.notify("Loop active", "info");
|
|
466
483
|
triggerLoopPrompt(ctx);
|
|
467
484
|
|
|
468
|
-
|
|
469
|
-
const condition = nextState.condition;
|
|
470
|
-
void (async () => {
|
|
471
|
-
const summary = await summarizeBreakoutCondition(ctx, mode, condition);
|
|
472
|
-
if (!loopState.active || loopState.mode !== mode || loopState.condition !== condition)
|
|
473
|
-
return;
|
|
474
|
-
loopState = { ...loopState, summary };
|
|
475
|
-
persistState(loopState);
|
|
476
|
-
updateStatus(ctx, loopState);
|
|
477
|
-
})();
|
|
485
|
+
startSummary(ctx);
|
|
478
486
|
},
|
|
479
487
|
});
|
|
480
488
|
|
|
481
|
-
pi.on("agent_end",
|
|
482
|
-
|
|
489
|
+
pi.on("agent_end", (event) => {
|
|
490
|
+
lastAssistantStopReason = event.messages.findLast(
|
|
491
|
+
(message) => message.role === "assistant",
|
|
492
|
+
)?.stopReason;
|
|
483
493
|
});
|
|
484
494
|
|
|
485
495
|
pi.on("agent_settled", async (_event, ctx) => {
|
|
486
496
|
if (!loopState.active) return;
|
|
487
497
|
|
|
488
|
-
if (
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
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;
|
|
492
510
|
if (confirm) {
|
|
493
|
-
breakLoop(ctx);
|
|
511
|
+
await breakLoop(ctx);
|
|
494
512
|
return;
|
|
495
513
|
}
|
|
496
514
|
}
|
|
@@ -500,9 +518,6 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
500
518
|
|
|
501
519
|
pi.on("session_before_compact", async (event, ctx) => {
|
|
502
520
|
if (!loopState.active || !loopState.mode || !ctx.model) return;
|
|
503
|
-
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(ctx.model);
|
|
504
|
-
if (!auth.ok) return;
|
|
505
|
-
|
|
506
521
|
const instructionParts = [
|
|
507
522
|
event.customInstructions,
|
|
508
523
|
getCompactionInstructions(loopState.mode, loopState.condition),
|
|
@@ -514,14 +529,16 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
514
529
|
const compaction = await compact(
|
|
515
530
|
event.preparation,
|
|
516
531
|
ctx.model,
|
|
517
|
-
|
|
518
|
-
|
|
532
|
+
undefined,
|
|
533
|
+
undefined,
|
|
519
534
|
instructionParts,
|
|
520
535
|
event.signal,
|
|
536
|
+
ctx.thinkingLevel,
|
|
537
|
+
ctx.modelRegistry.streamSimple.bind(ctx.modelRegistry),
|
|
521
538
|
);
|
|
522
539
|
return { compaction };
|
|
523
540
|
} catch (error) {
|
|
524
|
-
if (ctx.hasUI) {
|
|
541
|
+
if (ctx.hasUI && !event.signal.aborted) {
|
|
525
542
|
const message = error instanceof Error ? error.message : String(error);
|
|
526
543
|
ctx.ui.notify(`Loop compaction failed: ${message}`, "warning");
|
|
527
544
|
}
|
|
@@ -530,28 +547,26 @@ export default function loopExtension(pi: ExtensionAPI): void {
|
|
|
530
547
|
});
|
|
531
548
|
|
|
532
549
|
async function restoreLoopState(ctx: ExtensionContext): Promise<void> {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
return;
|
|
543
|
-
loopState = { ...loopState, summary };
|
|
544
|
-
persistState(loopState);
|
|
545
|
-
updateStatus(ctx, loopState);
|
|
546
|
-
})();
|
|
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;
|
|
547
559
|
}
|
|
560
|
+
if (generation === loopGeneration) startSummary(ctx);
|
|
548
561
|
}
|
|
549
562
|
|
|
550
|
-
pi.on("session_start",
|
|
551
|
-
|
|
552
|
-
|
|
563
|
+
pi.on("session_start", (_event, ctx) => restoreLoopState(ctx));
|
|
564
|
+
|
|
565
|
+
pi.on("session_tree", (_event, ctx) => restoreLoopState(ctx));
|
|
553
566
|
|
|
554
567
|
pi.on("session_shutdown", async () => {
|
|
568
|
+
loopGeneration += 1;
|
|
555
569
|
cancelScheduledLoopPrompt();
|
|
570
|
+
await cancelSummary();
|
|
556
571
|
});
|
|
557
572
|
}
|