pi-advisor-flow 0.4.0 → 0.5.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/CHANGELOG.md +23 -0
- package/README.md +8 -5
- package/package.json +11 -11
- package/src/commands.ts +327 -113
- package/src/config.ts +22 -0
- package/src/model-stream.ts +123 -0
- package/src/session-state.ts +26 -2
- package/src/tools.ts +117 -42
- package/src/ui.ts +1060 -600
package/src/commands.ts
CHANGED
|
@@ -2,8 +2,9 @@ import {
|
|
|
2
2
|
type ExtensionAPI,
|
|
3
3
|
type ExtensionContext,
|
|
4
4
|
getMarkdownTheme,
|
|
5
|
+
type Theme,
|
|
5
6
|
} from "@earendil-works/pi-coding-agent";
|
|
6
|
-
import { Box, Markdown, Text } from "@earendil-works/pi-tui";
|
|
7
|
+
import { Box, type Component, Markdown, Text } from "@earendil-works/pi-tui";
|
|
7
8
|
import {
|
|
8
9
|
advisorEffortRef,
|
|
9
10
|
advisorRef,
|
|
@@ -46,9 +47,12 @@ import {
|
|
|
46
47
|
setContextMaxCharsRef,
|
|
47
48
|
setExecutorEffortRef,
|
|
48
49
|
setExecutorRef,
|
|
50
|
+
setShowUsageDetailsRef,
|
|
51
|
+
setShowUsageFooterRef,
|
|
49
52
|
setSimpleModeRef,
|
|
50
53
|
splitRef,
|
|
51
54
|
} from "./config.js";
|
|
55
|
+
import type { GitContextLevel } from "./git.js";
|
|
52
56
|
import { herdrAdvisorActivity, notifyHerdrAdvisorFailure } from "./herdr.js";
|
|
53
57
|
import type { ScoutLifecycleEvent } from "./scout.js";
|
|
54
58
|
import type { AdvisorSessionState } from "./session-state.js";
|
|
@@ -60,13 +64,18 @@ import {
|
|
|
60
64
|
hasSoundVerdict,
|
|
61
65
|
renderAdvisorCallBox,
|
|
62
66
|
renderAdvisorResponseHeader,
|
|
67
|
+
renderScoutDetails,
|
|
63
68
|
resolveAdvisorRequest,
|
|
64
69
|
ScoutStatusManager,
|
|
70
|
+
type ScoutToolDetails,
|
|
71
|
+
SPINNER_FRAMES,
|
|
65
72
|
} from "./tools.js";
|
|
66
73
|
import {
|
|
67
74
|
type AdvisorSettings,
|
|
68
75
|
AdvisorSettingsSelector,
|
|
69
76
|
type ContextPreset,
|
|
77
|
+
ManualAdvisorDialog,
|
|
78
|
+
type ManualAdvisorRequest,
|
|
70
79
|
SearchableModelSelector,
|
|
71
80
|
} from "./ui.js";
|
|
72
81
|
import {
|
|
@@ -143,7 +152,8 @@ type ManualConsult = (
|
|
|
143
152
|
question?: string,
|
|
144
153
|
signal?: AbortSignal,
|
|
145
154
|
onChunk?: (thinking: string, text: string) => void,
|
|
146
|
-
onScout?: (event: ScoutLifecycleEvent) => void
|
|
155
|
+
onScout?: (event: ScoutLifecycleEvent) => void,
|
|
156
|
+
gitContext?: GitContextLevel
|
|
147
157
|
) => Promise<{
|
|
148
158
|
markdown: string;
|
|
149
159
|
thinkingText: string;
|
|
@@ -163,11 +173,157 @@ const notify = (
|
|
|
163
173
|
}
|
|
164
174
|
};
|
|
165
175
|
|
|
176
|
+
type ManualAdvisorProgressPhase =
|
|
177
|
+
| "preparing"
|
|
178
|
+
| "active"
|
|
179
|
+
| "complete"
|
|
180
|
+
| "cancelled"
|
|
181
|
+
| "error";
|
|
182
|
+
interface ManualAdvisorProgressState {
|
|
183
|
+
phase: ManualAdvisorProgressPhase;
|
|
184
|
+
scout?: ScoutToolDetails;
|
|
185
|
+
text?: string;
|
|
186
|
+
thinking?: string;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
class ManualAdvisorProgressComponent implements Component {
|
|
190
|
+
private readonly question: string | undefined;
|
|
191
|
+
private readonly state: ManualAdvisorProgressState;
|
|
192
|
+
private readonly expanded: boolean;
|
|
193
|
+
private readonly theme: Theme;
|
|
194
|
+
|
|
195
|
+
constructor(
|
|
196
|
+
question: string | undefined,
|
|
197
|
+
state: ManualAdvisorProgressState,
|
|
198
|
+
expanded: boolean,
|
|
199
|
+
theme: Theme
|
|
200
|
+
) {
|
|
201
|
+
this.question = question;
|
|
202
|
+
this.state = state;
|
|
203
|
+
this.expanded = expanded;
|
|
204
|
+
this.theme = theme;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
render(width: number): string[] {
|
|
208
|
+
const box = renderAdvisorCallBox(this.question, this.theme);
|
|
209
|
+
if (this.state.phase === "complete" || this.state.phase === "cancelled") {
|
|
210
|
+
return box.render(width);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const { scout } = this.state;
|
|
214
|
+
const scoutActive =
|
|
215
|
+
scout?.status === "calling" || scout?.status === "streaming";
|
|
216
|
+
if (scoutActive) {
|
|
217
|
+
renderScoutDetails(box, scout, this.expanded, this.theme);
|
|
218
|
+
return box.render(width);
|
|
219
|
+
}
|
|
220
|
+
if (scout?.status === "cancelled") {
|
|
221
|
+
return box.render(width);
|
|
222
|
+
}
|
|
223
|
+
if (this.state.phase === "error") {
|
|
224
|
+
box.addChild(
|
|
225
|
+
new Text(
|
|
226
|
+
this.theme.fg("error", this.theme.bold("◆ ADVISOR · FAILED")),
|
|
227
|
+
0,
|
|
228
|
+
0
|
|
229
|
+
)
|
|
230
|
+
);
|
|
231
|
+
return box.render(width);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const frame =
|
|
235
|
+
SPINNER_FRAMES[Math.floor(Date.now() / 80) % SPINNER_FRAMES.length];
|
|
236
|
+
let status = "Working…";
|
|
237
|
+
if (this.state.phase === "preparing") {
|
|
238
|
+
status = "Preparing…";
|
|
239
|
+
} else if (this.state.text?.trim()) {
|
|
240
|
+
status = "Responding…";
|
|
241
|
+
}
|
|
242
|
+
box.addChild(
|
|
243
|
+
new Text(
|
|
244
|
+
`${this.theme.fg("warning", this.theme.bold(`◆ ADVISOR ${frame}`))} ${this.theme.fg("dim", `· ${status}`)}`,
|
|
245
|
+
0,
|
|
246
|
+
0
|
|
247
|
+
)
|
|
248
|
+
);
|
|
249
|
+
if (this.state.thinking) {
|
|
250
|
+
box.addChild(
|
|
251
|
+
new Text(
|
|
252
|
+
this.theme.fg(
|
|
253
|
+
"thinkingText",
|
|
254
|
+
` 💭 ${this.state.thinking.replace(/\n/g, " ").slice(-200)}`
|
|
255
|
+
),
|
|
256
|
+
0,
|
|
257
|
+
0
|
|
258
|
+
)
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
if (this.state.text) {
|
|
262
|
+
box.addChild(
|
|
263
|
+
new Markdown(
|
|
264
|
+
adviceForDisplay(this.state.text, this.expanded),
|
|
265
|
+
0,
|
|
266
|
+
0,
|
|
267
|
+
getMarkdownTheme()
|
|
268
|
+
)
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
return box.render(width);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
invalidate(): void {
|
|
275
|
+
// The live progress state is read during each render.
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
166
279
|
const findConfiguredModel = (ctx: ExtensionContext, ref: string) => {
|
|
167
280
|
const [provider, modelId] = splitRef(ref);
|
|
168
281
|
return ctx.modelRegistry.find(provider, modelId);
|
|
169
282
|
};
|
|
170
283
|
|
|
284
|
+
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: one settings form maps every persisted control.
|
|
285
|
+
const applyAdvisorSettings = (settings: AdvisorSettings) => {
|
|
286
|
+
setAdvisorEffortRef(
|
|
287
|
+
settings.effort === "Default (Model Default)" ? undefined : settings.effort
|
|
288
|
+
);
|
|
289
|
+
setContextMaxCharsRef(settings.contextMaxChars);
|
|
290
|
+
setAdvisorPlanGateRef(settings.planGate);
|
|
291
|
+
setAdvisorFailureGateRef(settings.failureGate);
|
|
292
|
+
setAdvisorCompletionGateRef(settings.completionGate);
|
|
293
|
+
setAdvisorCollapseResponsesRef(settings.collapseResponses);
|
|
294
|
+
setAdvisorCustomInvocationRef(settings.customRule);
|
|
295
|
+
setAdvisorBlockOnBlockedRef(settings.blockOnBlocked ?? true);
|
|
296
|
+
setAdvisorAutoLoopGateRef(settings.autoLoopGate ?? true);
|
|
297
|
+
setAdvisorLoopThresholdRef(settings.loopThreshold ?? 3);
|
|
298
|
+
setAdvisorMaxCallsPerSessionRef(settings.maxCallsPerSession);
|
|
299
|
+
setAdvisorSessionSummaryRef(settings.sessionSummary ?? false);
|
|
300
|
+
setAdvisorScoutEnabledRef(settings.scoutEnabled ?? false);
|
|
301
|
+
setShowUsageDetailsRef(settings.showUsageDetails ?? true);
|
|
302
|
+
setShowUsageFooterRef(settings.showUsageFooter ?? false);
|
|
303
|
+
setSimpleModeRef(settings.simpleMode ?? false);
|
|
304
|
+
setAlwaysOnRef(settings.alwaysOn ?? false);
|
|
305
|
+
setAdvisorFailureModeRef(settings.failureMode ?? "block-session");
|
|
306
|
+
setAdvisorHerdrIntegrationRef(settings.herdrIntegration ?? true);
|
|
307
|
+
setAdvisorToolResultMaxLinesRef(settings.toolResultMaxLines ?? 2000);
|
|
308
|
+
setAdvisorToolResultMaxBytesRef(settings.toolResultMaxBytes ?? 50 * 1024);
|
|
309
|
+
setAdvisorRedactSecretsRef(settings.redactSecrets ?? false);
|
|
310
|
+
setAdvisorGitContextRef(settings.gitContext ?? "summary");
|
|
311
|
+
setAdvisorGitContextMaxCharsRef(settings.gitContextMaxChars ?? 20_000);
|
|
312
|
+
setAdvisorToolPoliciesRef(settings.toolPolicies ?? {});
|
|
313
|
+
setAdvisorTrackedFileContentRef(settings.trackedFileContent ?? false);
|
|
314
|
+
setAdvisorUntrackedContentRef(settings.untrackedContent ?? false);
|
|
315
|
+
setAdvisorOutcomeLoggingRef(settings.outcomeLogging ?? false);
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
const saveAdvisorSettings = (
|
|
319
|
+
ctx: ExtensionContext,
|
|
320
|
+
settings: AdvisorSettings
|
|
321
|
+
) => {
|
|
322
|
+
applyAdvisorSettings(settings);
|
|
323
|
+
saveConfig(ctx);
|
|
324
|
+
saveGlobalOutcomeLogging(settings.outcomeLogging ?? false);
|
|
325
|
+
};
|
|
326
|
+
|
|
171
327
|
export const registerCommands = (
|
|
172
328
|
pi: ExtensionAPI,
|
|
173
329
|
dependencies: {
|
|
@@ -178,18 +334,19 @@ export const registerCommands = (
|
|
|
178
334
|
) => {
|
|
179
335
|
const advisorSessionState =
|
|
180
336
|
dependencies.sessionState ?? defaultAdvisorSessionState;
|
|
181
|
-
const scoutStatus =
|
|
337
|
+
const scoutStatus =
|
|
338
|
+
dependencies.statusManager ?? new ScoutStatusManager(false);
|
|
182
339
|
const flowEnabled = () => pi.getActiveTools().includes("ask_advisor");
|
|
183
340
|
const requestAdvisor =
|
|
184
341
|
dependencies.consult ??
|
|
185
|
-
((ctx, question, signal, onChunk, onScout) =>
|
|
342
|
+
((ctx, question, signal, onChunk, onScout, gitContext) =>
|
|
186
343
|
consultAdvisor(
|
|
187
344
|
ctx,
|
|
188
345
|
question,
|
|
189
346
|
signal,
|
|
190
347
|
onChunk,
|
|
191
348
|
"manual",
|
|
192
|
-
|
|
349
|
+
gitContext,
|
|
193
350
|
undefined,
|
|
194
351
|
undefined,
|
|
195
352
|
undefined,
|
|
@@ -197,23 +354,34 @@ export const registerCommands = (
|
|
|
197
354
|
undefined
|
|
198
355
|
));
|
|
199
356
|
const manualConsultations = new Map<AbortController, symbol>();
|
|
357
|
+
const manualProgressTimers = new Map<
|
|
358
|
+
AbortController,
|
|
359
|
+
ReturnType<typeof setInterval>
|
|
360
|
+
>();
|
|
361
|
+
const manualProgress = new Map<string, ManualAdvisorProgressState>();
|
|
362
|
+
let manualProgressSequence = 0;
|
|
200
363
|
const updateAdvisorUsageStatus = (ctx: ExtensionContext) => {
|
|
201
364
|
if (ctx.hasUI) {
|
|
202
|
-
ctx.ui.setStatus(
|
|
365
|
+
ctx.ui.setStatus(
|
|
366
|
+
"advisor-usage",
|
|
367
|
+
getAdvisorSettings().showUsageFooter
|
|
368
|
+
? advisorSessionState.usageStatus()
|
|
369
|
+
: undefined
|
|
370
|
+
);
|
|
203
371
|
}
|
|
204
372
|
};
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
) {
|
|
216
|
-
ctx.ui.setStatus("advisor-manual",
|
|
373
|
+
const reportManualBudgetExhausted = (ctx: ExtensionContext) => {
|
|
374
|
+
const message = "Advisor call budget exhausted for this session.";
|
|
375
|
+
notify(ctx, message, "warning");
|
|
376
|
+
notifyHerdrAdvisorFailure("Advisor budget exhausted", message);
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
// A status update with an undefined value is an invisible render pulse. The
|
|
380
|
+
// transcript owns manual progress now, so no consultation text is kept in the
|
|
381
|
+
// footer while this pulse still drives the live spinner.
|
|
382
|
+
const requestManualRender = (ctx: ExtensionContext) => {
|
|
383
|
+
if (ctx.hasUI) {
|
|
384
|
+
ctx.ui.setStatus("advisor-manual", undefined);
|
|
217
385
|
}
|
|
218
386
|
};
|
|
219
387
|
|
|
@@ -221,10 +389,26 @@ export const registerCommands = (
|
|
|
221
389
|
ctx: ExtensionContext,
|
|
222
390
|
question: string | undefined,
|
|
223
391
|
controller: AbortController,
|
|
224
|
-
scoutStatusToken: symbol
|
|
392
|
+
scoutStatusToken: symbol,
|
|
393
|
+
progress: ManualAdvisorProgressState,
|
|
394
|
+
gitContext?: GitContextLevel
|
|
225
395
|
) => {
|
|
226
396
|
herdrAdvisorActivity.start();
|
|
227
|
-
|
|
397
|
+
progress.phase = "preparing";
|
|
398
|
+
requestManualRender(ctx);
|
|
399
|
+
if (ctx.hasUI) {
|
|
400
|
+
const timer = setInterval(() => {
|
|
401
|
+
if (
|
|
402
|
+
controller.signal.aborted ||
|
|
403
|
+
manualConsultations.get(controller) !== scoutStatusToken
|
|
404
|
+
) {
|
|
405
|
+
clearInterval(timer);
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
requestManualRender(ctx);
|
|
409
|
+
}, 80);
|
|
410
|
+
manualProgressTimers.set(controller, timer);
|
|
411
|
+
}
|
|
228
412
|
let scoutDetails: Parameters<typeof appendScoutLifecycleEntry>[2];
|
|
229
413
|
return requestAdvisor(
|
|
230
414
|
ctx,
|
|
@@ -234,41 +418,27 @@ export const registerCommands = (
|
|
|
234
418
|
if (controller.signal.aborted) {
|
|
235
419
|
return;
|
|
236
420
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
if (text.trim()) {
|
|
242
|
-
status = "Advisor responding…";
|
|
243
|
-
}
|
|
244
|
-
setManualStatus(ctx, controller, scoutStatusToken, status);
|
|
421
|
+
progress.phase = "active";
|
|
422
|
+
progress.thinking = thinking;
|
|
423
|
+
progress.text = text;
|
|
424
|
+
requestManualRender(ctx);
|
|
245
425
|
},
|
|
246
426
|
(event) => {
|
|
247
427
|
if (!controller.signal.aborted) {
|
|
248
428
|
scoutStatus.update(ctx, scoutStatusToken, event);
|
|
249
|
-
if (event.type === "call" || event.type === "chunk") {
|
|
250
|
-
setManualStatus(
|
|
251
|
-
ctx,
|
|
252
|
-
controller,
|
|
253
|
-
scoutStatusToken,
|
|
254
|
-
"Advisor Scout curating…"
|
|
255
|
-
);
|
|
256
|
-
} else if (event.type === "success" || event.type === "fallback") {
|
|
257
|
-
setManualStatus(
|
|
258
|
-
ctx,
|
|
259
|
-
controller,
|
|
260
|
-
scoutStatusToken,
|
|
261
|
-
"Advisor working…"
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
429
|
scoutDetails = appendScoutLifecycleEntry(pi, event, scoutDetails);
|
|
430
|
+
progress.scout = scoutDetails;
|
|
431
|
+
progress.phase = "active";
|
|
432
|
+
requestManualRender(ctx);
|
|
265
433
|
}
|
|
266
|
-
}
|
|
434
|
+
},
|
|
435
|
+
gitContext
|
|
267
436
|
)
|
|
268
437
|
.then(({ markdown, usage }) => {
|
|
269
438
|
if (controller.signal.aborted) {
|
|
270
439
|
return;
|
|
271
440
|
}
|
|
441
|
+
progress.phase = "complete";
|
|
272
442
|
advisorSessionState.recordInvocation({
|
|
273
443
|
cost: advisorUsageCost(usage),
|
|
274
444
|
executionEffect: "continued",
|
|
@@ -303,6 +473,7 @@ export const registerCommands = (
|
|
|
303
473
|
if (controller.signal.aborted) {
|
|
304
474
|
return;
|
|
305
475
|
}
|
|
476
|
+
progress.phase = "error";
|
|
306
477
|
const message = error instanceof Error ? error.message : String(error);
|
|
307
478
|
advisorSessionState.recordInvocation({
|
|
308
479
|
executionEffect: "continued",
|
|
@@ -328,12 +499,15 @@ export const registerCommands = (
|
|
|
328
499
|
notifyHerdrAdvisorFailure("Advisor consultation failed", message);
|
|
329
500
|
})
|
|
330
501
|
.finally(() => {
|
|
331
|
-
if (
|
|
332
|
-
|
|
333
|
-
manualConsultations.get(controller) === scoutStatusToken
|
|
334
|
-
) {
|
|
335
|
-
ctx.ui.setStatus("advisor-manual", undefined);
|
|
502
|
+
if (controller.signal.aborted) {
|
|
503
|
+
progress.phase = "cancelled";
|
|
336
504
|
}
|
|
505
|
+
const timer = manualProgressTimers.get(controller);
|
|
506
|
+
if (timer) {
|
|
507
|
+
clearInterval(timer);
|
|
508
|
+
manualProgressTimers.delete(controller);
|
|
509
|
+
}
|
|
510
|
+
requestManualRender(ctx);
|
|
337
511
|
scoutStatus.release(ctx, scoutStatusToken);
|
|
338
512
|
manualConsultations.delete(controller);
|
|
339
513
|
herdrAdvisorActivity.finish();
|
|
@@ -432,9 +606,20 @@ export const registerCommands = (
|
|
|
432
606
|
|
|
433
607
|
pi.registerEntryRenderer?.(
|
|
434
608
|
"advisor-manual-call",
|
|
435
|
-
(entry,
|
|
436
|
-
const { question } = (entry.data ?? {}) as {
|
|
437
|
-
|
|
609
|
+
(entry, { expanded }, theme) => {
|
|
610
|
+
const { progressId, question } = (entry.data ?? {}) as {
|
|
611
|
+
progressId?: string;
|
|
612
|
+
question?: string;
|
|
613
|
+
};
|
|
614
|
+
const progress = progressId ? manualProgress.get(progressId) : undefined;
|
|
615
|
+
return progress
|
|
616
|
+
? new ManualAdvisorProgressComponent(
|
|
617
|
+
question,
|
|
618
|
+
progress,
|
|
619
|
+
Boolean(expanded),
|
|
620
|
+
theme
|
|
621
|
+
)
|
|
622
|
+
: renderAdvisorCallBox(question, theme);
|
|
438
623
|
}
|
|
439
624
|
);
|
|
440
625
|
|
|
@@ -461,9 +646,11 @@ export const registerCommands = (
|
|
|
461
646
|
if (details?.advisor) {
|
|
462
647
|
box.addChild(new Text(theme.fg("dim", ` ${details.advisor}`), 0, 0));
|
|
463
648
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
649
|
+
if (getAdvisorSettings().showUsageDetails) {
|
|
650
|
+
const usage = formatAdvisorUsage(details?.usage);
|
|
651
|
+
if (usage) {
|
|
652
|
+
box.addChild(new Text(theme.fg("dim", ` Usage: ${usage}`), 0, 0));
|
|
653
|
+
}
|
|
467
654
|
}
|
|
468
655
|
box.addChild(
|
|
469
656
|
new Markdown(
|
|
@@ -507,24 +694,30 @@ export const registerCommands = (
|
|
|
507
694
|
|
|
508
695
|
pi.on("session_shutdown", (_event, ctx) => {
|
|
509
696
|
if (ctx.hasUI) {
|
|
510
|
-
ctx.ui.setStatus("advisor-manual", undefined);
|
|
511
697
|
ctx.ui.setStatus("advisor-usage", undefined);
|
|
512
698
|
}
|
|
513
699
|
for (const [controller, token] of manualConsultations) {
|
|
514
700
|
controller.abort();
|
|
701
|
+
const timer = manualProgressTimers.get(controller);
|
|
702
|
+
if (timer) {
|
|
703
|
+
clearInterval(timer);
|
|
704
|
+
manualProgressTimers.delete(controller);
|
|
705
|
+
}
|
|
515
706
|
scoutStatus.release(ctx, token);
|
|
516
707
|
}
|
|
517
708
|
scoutStatus.clear(ctx);
|
|
518
709
|
manualConsultations.clear();
|
|
710
|
+
manualProgressTimers.clear();
|
|
711
|
+
manualProgress.clear();
|
|
519
712
|
herdrAdvisorActivity.clear();
|
|
520
713
|
});
|
|
521
714
|
|
|
522
715
|
pi.registerCommand("advisor-manual", {
|
|
523
716
|
description:
|
|
524
717
|
"Consult the Advisor in parallel; accepts an optional focused question and fans its response out to the Executor",
|
|
525
|
-
handler: (args, ctx) => {
|
|
718
|
+
handler: async (args, ctx) => {
|
|
526
719
|
if (!loadCommandConfig(ctx)) {
|
|
527
|
-
return
|
|
720
|
+
return;
|
|
528
721
|
}
|
|
529
722
|
if (
|
|
530
723
|
!(
|
|
@@ -532,15 +725,57 @@ export const registerCommands = (
|
|
|
532
725
|
advisorSessionState.canConsult(getAdvisorMaxCallsPerSession())
|
|
533
726
|
)
|
|
534
727
|
) {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
notifyHerdrAdvisorFailure("Advisor budget exhausted", message);
|
|
538
|
-
return Promise.resolve();
|
|
728
|
+
reportManualBudgetExhausted(ctx);
|
|
729
|
+
return;
|
|
539
730
|
}
|
|
731
|
+
|
|
732
|
+
let gitContext: GitContextLevel | undefined;
|
|
733
|
+
let question: string | undefined;
|
|
734
|
+
if (ctx.mode === "tui") {
|
|
735
|
+
const request = await ctx.ui.custom<ManualAdvisorRequest | undefined>(
|
|
736
|
+
(tui, theme, keybindings, done) =>
|
|
737
|
+
new ManualAdvisorDialog({
|
|
738
|
+
gitContext: getAdvisorSettings().gitContext,
|
|
739
|
+
initialMessage: args,
|
|
740
|
+
keybindings,
|
|
741
|
+
onCancel: () => done(undefined),
|
|
742
|
+
onSubmit: done,
|
|
743
|
+
theme,
|
|
744
|
+
tui,
|
|
745
|
+
}),
|
|
746
|
+
{
|
|
747
|
+
overlay: true,
|
|
748
|
+
overlayOptions: {
|
|
749
|
+
anchor: "center",
|
|
750
|
+
margin: 2,
|
|
751
|
+
maxHeight: "80%",
|
|
752
|
+
minWidth: 56,
|
|
753
|
+
width: 76,
|
|
754
|
+
},
|
|
755
|
+
}
|
|
756
|
+
);
|
|
757
|
+
if (!request) {
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
if (
|
|
761
|
+
!(
|
|
762
|
+
isSimpleMode() ||
|
|
763
|
+
advisorSessionState.canConsult(getAdvisorMaxCallsPerSession())
|
|
764
|
+
)
|
|
765
|
+
) {
|
|
766
|
+
reportManualBudgetExhausted(ctx);
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
const { gitContext: selectedGitContext, message } = request;
|
|
770
|
+
gitContext = selectedGitContext;
|
|
771
|
+
question = resolveAdvisorRequest(message);
|
|
772
|
+
} else {
|
|
773
|
+
question = resolveAdvisorRequest(args);
|
|
774
|
+
}
|
|
775
|
+
|
|
540
776
|
if (!isSimpleMode()) {
|
|
541
777
|
advisorSessionState.consumeCall();
|
|
542
778
|
}
|
|
543
|
-
const question = resolveAdvisorRequest(args);
|
|
544
779
|
// A single visible progress surface avoids competing consultations overwriting
|
|
545
780
|
// each other's streamed state. A newer manual request replaces the previous one.
|
|
546
781
|
for (const [pending, token] of manualConsultations) {
|
|
@@ -550,11 +785,21 @@ export const registerCommands = (
|
|
|
550
785
|
manualConsultations.clear();
|
|
551
786
|
const controller = new AbortController();
|
|
552
787
|
const scoutStatusToken = Symbol("manual-scout");
|
|
788
|
+
manualProgressSequence += 1;
|
|
789
|
+
const progressId = `manual-${manualProgressSequence}`;
|
|
790
|
+
const progress: ManualAdvisorProgressState = { phase: "preparing" };
|
|
791
|
+
manualProgress.set(progressId, progress);
|
|
553
792
|
scoutStatus.register(scoutStatusToken);
|
|
554
793
|
manualConsultations.set(controller, scoutStatusToken);
|
|
555
|
-
pi.appendEntry?.("advisor-manual-call", { question });
|
|
556
|
-
startManualConsultation(
|
|
557
|
-
|
|
794
|
+
pi.appendEntry?.("advisor-manual-call", { progressId, question });
|
|
795
|
+
startManualConsultation(
|
|
796
|
+
ctx,
|
|
797
|
+
question,
|
|
798
|
+
controller,
|
|
799
|
+
scoutStatusToken,
|
|
800
|
+
progress,
|
|
801
|
+
gitContext
|
|
802
|
+
);
|
|
558
803
|
},
|
|
559
804
|
});
|
|
560
805
|
|
|
@@ -639,68 +884,37 @@ export const registerCommands = (
|
|
|
639
884
|
});
|
|
640
885
|
|
|
641
886
|
pi.registerCommand("advisor-settings", {
|
|
642
|
-
description: "Configure Advisor
|
|
643
|
-
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: one settings form maps every persisted control.
|
|
887
|
+
description: "Configure Advisor settings",
|
|
644
888
|
handler: async (_args, ctx) => {
|
|
645
889
|
if (!(loadCommandConfig(ctx) && ctx.hasUI)) {
|
|
646
890
|
return;
|
|
647
891
|
}
|
|
648
892
|
|
|
649
893
|
const initial: AdvisorSettings = getAdvisorSettings();
|
|
650
|
-
|
|
894
|
+
await ctx.ui.custom<void>(
|
|
651
895
|
(tui, theme, _keybindings, done) =>
|
|
652
896
|
new AdvisorSettingsSelector({
|
|
653
897
|
effortLevels: EFFORT_LEVELS,
|
|
654
898
|
initial,
|
|
655
|
-
onCancel: () => done(
|
|
656
|
-
|
|
899
|
+
onCancel: () => done(),
|
|
900
|
+
onChange: (settings) => {
|
|
901
|
+
try {
|
|
902
|
+
saveAdvisorSettings(ctx, settings);
|
|
903
|
+
updateAdvisorUsageStatus(ctx);
|
|
904
|
+
} catch (error) {
|
|
905
|
+
const message =
|
|
906
|
+
error instanceof Error ? error.message : String(error);
|
|
907
|
+
ctx.ui.notify(
|
|
908
|
+
`Could not save Advisor settings: ${message}`,
|
|
909
|
+
"error"
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
},
|
|
657
913
|
presets: CONTEXT_PRESETS,
|
|
658
914
|
theme,
|
|
659
915
|
tui,
|
|
660
916
|
})
|
|
661
917
|
);
|
|
662
|
-
if (!settings) {
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
setAdvisorEffortRef(
|
|
667
|
-
settings.effort === "Default (Model Default)"
|
|
668
|
-
? undefined
|
|
669
|
-
: settings.effort
|
|
670
|
-
);
|
|
671
|
-
setContextMaxCharsRef(settings.contextMaxChars);
|
|
672
|
-
setAdvisorPlanGateRef(settings.planGate);
|
|
673
|
-
setAdvisorFailureGateRef(settings.failureGate);
|
|
674
|
-
setAdvisorCompletionGateRef(settings.completionGate);
|
|
675
|
-
setAdvisorCollapseResponsesRef(settings.collapseResponses);
|
|
676
|
-
setAdvisorCustomInvocationRef(settings.customRule);
|
|
677
|
-
setAdvisorBlockOnBlockedRef(settings.blockOnBlocked ?? true);
|
|
678
|
-
setAdvisorAutoLoopGateRef(settings.autoLoopGate ?? true);
|
|
679
|
-
setAdvisorLoopThresholdRef(settings.loopThreshold ?? 3);
|
|
680
|
-
setAdvisorMaxCallsPerSessionRef(settings.maxCallsPerSession);
|
|
681
|
-
setAdvisorSessionSummaryRef(settings.sessionSummary ?? false);
|
|
682
|
-
setAdvisorScoutEnabledRef(settings.scoutEnabled ?? false);
|
|
683
|
-
setSimpleModeRef(settings.simpleMode ?? false);
|
|
684
|
-
setAlwaysOnRef(settings.alwaysOn ?? false);
|
|
685
|
-
setAdvisorFailureModeRef(settings.failureMode ?? "block-session");
|
|
686
|
-
setAdvisorHerdrIntegrationRef(settings.herdrIntegration ?? true);
|
|
687
|
-
setAdvisorToolResultMaxLinesRef(settings.toolResultMaxLines ?? 2000);
|
|
688
|
-
setAdvisorToolResultMaxBytesRef(settings.toolResultMaxBytes ?? 50 * 1024);
|
|
689
|
-
setAdvisorRedactSecretsRef(settings.redactSecrets ?? false);
|
|
690
|
-
setAdvisorGitContextRef(settings.gitContext ?? "summary");
|
|
691
|
-
setAdvisorGitContextMaxCharsRef(settings.gitContextMaxChars ?? 20_000);
|
|
692
|
-
setAdvisorToolPoliciesRef(settings.toolPolicies ?? {});
|
|
693
|
-
setAdvisorTrackedFileContentRef(settings.trackedFileContent ?? false);
|
|
694
|
-
setAdvisorUntrackedContentRef(settings.untrackedContent ?? false);
|
|
695
|
-
setAdvisorOutcomeLoggingRef(settings.outcomeLogging ?? false);
|
|
696
|
-
const path = saveConfig(ctx);
|
|
697
|
-
const globalPath = saveGlobalOutcomeLogging(
|
|
698
|
-
settings.outcomeLogging ?? false
|
|
699
|
-
);
|
|
700
|
-
ctx.ui.notify(
|
|
701
|
-
`Saved Advisor settings to ${path}; outcome logging globally to ${globalPath}`,
|
|
702
|
-
"info"
|
|
703
|
-
);
|
|
704
918
|
},
|
|
705
919
|
});
|
|
706
920
|
|