pi-advisor-flow 0.5.0 → 0.5.2
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 +54 -0
- package/README.md +37 -71
- package/extensions/index.ts +7 -7
- package/package.json +19 -6
- package/src/commands/activation-preparation.ts +117 -0
- package/src/commands/activation.ts +135 -0
- package/src/commands/lifecycle.ts +83 -0
- package/src/commands/manual-command.ts +107 -0
- package/src/commands/manual-consultation.ts +144 -0
- package/src/commands/manual-progress.ts +93 -0
- package/src/commands/model-commands.ts +66 -0
- package/src/commands/model-options.ts +181 -0
- package/src/commands/model-picker.ts +110 -0
- package/src/commands/registration.ts +27 -0
- package/src/commands/renderers.ts +76 -0
- package/src/commands/runtime.ts +114 -0
- package/src/commands/settings-commands.ts +80 -0
- package/src/commands/settings-persistence.ts +81 -0
- package/src/commands/types.ts +78 -0
- package/src/commands.ts +9 -946
- package/src/config/args.ts +37 -0
- package/src/config/defaults.ts +190 -0
- package/src/config/state.ts +199 -0
- package/src/config/storage.ts +224 -0
- package/src/config/types.ts +63 -0
- package/src/config/validation.ts +234 -0
- package/src/config.ts +104 -791
- package/src/conversation.ts +4 -2
- package/src/herdr.ts +1 -1
- package/src/model-stream.ts +25 -2
- package/src/scout-context.ts +2 -2
- package/src/scout.ts +1 -1
- package/src/tools/consultation.ts +337 -0
- package/src/tools/gate-policy.ts +132 -0
- package/src/tools/gate-protocol.ts +127 -0
- package/src/tools/loop-gate.ts +210 -0
- package/src/tools/prompts.ts +159 -0
- package/src/tools/register-ask-advisor.ts +225 -0
- package/src/tools/register-lifecycle.ts +89 -0
- package/src/tools/register-outcome.ts +75 -0
- package/src/tools/register-renderers.ts +88 -0
- package/src/tools/registration.ts +35 -0
- package/src/tools/render-advisor-result.ts +168 -0
- package/src/tools/render-common.ts +120 -0
- package/src/tools/scout-status.ts +206 -0
- package/src/tools/session.ts +3 -0
- package/src/tools/types.ts +119 -0
- package/src/tools.ts +42 -1811
- package/src/ui/manual-dialog-render.ts +150 -0
- package/src/ui/manual-dialog.ts +305 -0
- package/src/ui/model-selector.ts +166 -0
- package/src/ui/settings-formatting.ts +133 -0
- package/src/ui/settings-items.ts +323 -0
- package/src/ui/settings-list-adapter.ts +103 -0
- package/src/ui/settings-mutations.ts +80 -0
- package/src/ui/settings-selector.ts +161 -0
- package/src/ui/text-setting-submenu.ts +66 -0
- package/src/ui/types.ts +111 -0
- package/src/ui.ts +9 -1418
package/src/commands.ts
CHANGED
|
@@ -1,946 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
advisorRef,
|
|
11
|
-
alwaysOnRef,
|
|
12
|
-
contextMaxCharsRef,
|
|
13
|
-
executorEffortRef,
|
|
14
|
-
executorRef,
|
|
15
|
-
getAdvisorMaxCallsPerSession,
|
|
16
|
-
getAdvisorSettings,
|
|
17
|
-
isSimpleMode,
|
|
18
|
-
loadConfig,
|
|
19
|
-
parseArgs,
|
|
20
|
-
saveConfig,
|
|
21
|
-
saveGlobalOutcomeLogging,
|
|
22
|
-
setAdvisorAutoLoopGateRef,
|
|
23
|
-
setAdvisorBlockOnBlockedRef,
|
|
24
|
-
setAdvisorCollapseResponsesRef,
|
|
25
|
-
setAdvisorCompletionGateRef,
|
|
26
|
-
setAdvisorCustomInvocationRef,
|
|
27
|
-
setAdvisorEffortRef,
|
|
28
|
-
setAdvisorFailureGateRef,
|
|
29
|
-
setAdvisorFailureModeRef,
|
|
30
|
-
setAdvisorGitContextMaxCharsRef,
|
|
31
|
-
setAdvisorGitContextRef,
|
|
32
|
-
setAdvisorHerdrIntegrationRef,
|
|
33
|
-
setAdvisorLoopThresholdRef,
|
|
34
|
-
setAdvisorMaxCallsPerSessionRef,
|
|
35
|
-
setAdvisorOutcomeLoggingRef,
|
|
36
|
-
setAdvisorPlanGateRef,
|
|
37
|
-
setAdvisorRedactSecretsRef,
|
|
38
|
-
setAdvisorRef,
|
|
39
|
-
setAdvisorScoutEnabledRef,
|
|
40
|
-
setAdvisorSessionSummaryRef,
|
|
41
|
-
setAdvisorToolPoliciesRef,
|
|
42
|
-
setAdvisorToolResultMaxBytesRef,
|
|
43
|
-
setAdvisorToolResultMaxLinesRef,
|
|
44
|
-
setAdvisorTrackedFileContentRef,
|
|
45
|
-
setAdvisorUntrackedContentRef,
|
|
46
|
-
setAlwaysOnRef,
|
|
47
|
-
setContextMaxCharsRef,
|
|
48
|
-
setExecutorEffortRef,
|
|
49
|
-
setExecutorRef,
|
|
50
|
-
setShowUsageDetailsRef,
|
|
51
|
-
setShowUsageFooterRef,
|
|
52
|
-
setSimpleModeRef,
|
|
53
|
-
splitRef,
|
|
54
|
-
} from "./config.js";
|
|
55
|
-
import type { GitContextLevel } from "./git.js";
|
|
56
|
-
import { herdrAdvisorActivity, notifyHerdrAdvisorFailure } from "./herdr.js";
|
|
57
|
-
import type { ScoutLifecycleEvent } from "./scout.js";
|
|
58
|
-
import type { AdvisorSessionState } from "./session-state.js";
|
|
59
|
-
import {
|
|
60
|
-
adviceForDisplay,
|
|
61
|
-
appendScoutLifecycleEntry,
|
|
62
|
-
consultAdvisor,
|
|
63
|
-
advisorSessionState as defaultAdvisorSessionState,
|
|
64
|
-
hasSoundVerdict,
|
|
65
|
-
renderAdvisorCallBox,
|
|
66
|
-
renderAdvisorResponseHeader,
|
|
67
|
-
renderScoutDetails,
|
|
68
|
-
resolveAdvisorRequest,
|
|
69
|
-
ScoutStatusManager,
|
|
70
|
-
type ScoutToolDetails,
|
|
71
|
-
SPINNER_FRAMES,
|
|
72
|
-
} from "./tools.js";
|
|
73
|
-
import {
|
|
74
|
-
type AdvisorSettings,
|
|
75
|
-
AdvisorSettingsSelector,
|
|
76
|
-
type ContextPreset,
|
|
77
|
-
ManualAdvisorDialog,
|
|
78
|
-
type ManualAdvisorRequest,
|
|
79
|
-
SearchableModelSelector,
|
|
80
|
-
} from "./ui.js";
|
|
81
|
-
import {
|
|
82
|
-
advisorUsageCost,
|
|
83
|
-
formatAdvisorUsage,
|
|
84
|
-
snapshotAdvisorUsage,
|
|
85
|
-
} from "./usage.js";
|
|
86
|
-
|
|
87
|
-
const DEFAULT_EFFORT_LEVEL = "Default (Model Default)";
|
|
88
|
-
const SELECTED_PREFIX = "✓ ";
|
|
89
|
-
const EFFORT_LEVELS = [
|
|
90
|
-
DEFAULT_EFFORT_LEVEL,
|
|
91
|
-
"off",
|
|
92
|
-
"minimal",
|
|
93
|
-
"low",
|
|
94
|
-
"medium",
|
|
95
|
-
"high",
|
|
96
|
-
"xhigh",
|
|
97
|
-
"max",
|
|
98
|
-
];
|
|
99
|
-
|
|
100
|
-
const effortChoices = (configured: string | undefined): string[] => {
|
|
101
|
-
const current = configured ?? DEFAULT_EFFORT_LEVEL;
|
|
102
|
-
return [
|
|
103
|
-
`${SELECTED_PREFIX}${current}`,
|
|
104
|
-
...EFFORT_LEVELS.filter((level) => level !== current),
|
|
105
|
-
];
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
const selectedEffort = (choice: string): string | undefined => {
|
|
109
|
-
const effort = choice.startsWith(SELECTED_PREFIX)
|
|
110
|
-
? choice.slice(SELECTED_PREFIX.length)
|
|
111
|
-
: choice;
|
|
112
|
-
return effort === DEFAULT_EFFORT_LEVEL ? undefined : effort;
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
const CONTEXT_PRESETS: ContextPreset[] = [
|
|
116
|
-
{
|
|
117
|
-
description:
|
|
118
|
-
"No conversation history. The Advisor receives only its standing instructions.",
|
|
119
|
-
label: "0",
|
|
120
|
-
value: 0,
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
description: "The most recent 10,000 characters of the current branch.",
|
|
124
|
-
label: "10k",
|
|
125
|
-
value: 10_000,
|
|
126
|
-
},
|
|
127
|
-
{
|
|
128
|
-
description: "The most recent 25,000 characters of the current branch.",
|
|
129
|
-
label: "25k",
|
|
130
|
-
value: 25_000,
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
description: "The most recent 100,000 characters of the current branch.",
|
|
134
|
-
label: "100k",
|
|
135
|
-
value: 100_000,
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
description: "The most recent 200,000 characters of the current branch.",
|
|
139
|
-
label: "200k",
|
|
140
|
-
value: 200_000,
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
description:
|
|
144
|
-
"The complete reconstructed conversation branch. Cost and model context limits apply.",
|
|
145
|
-
label: "ALL",
|
|
146
|
-
value: Number.MAX_SAFE_INTEGER,
|
|
147
|
-
},
|
|
148
|
-
];
|
|
149
|
-
|
|
150
|
-
type ManualConsult = (
|
|
151
|
-
ctx: ExtensionContext,
|
|
152
|
-
question?: string,
|
|
153
|
-
signal?: AbortSignal,
|
|
154
|
-
onChunk?: (thinking: string, text: string) => void,
|
|
155
|
-
onScout?: (event: ScoutLifecycleEvent) => void,
|
|
156
|
-
gitContext?: GitContextLevel
|
|
157
|
-
) => Promise<{
|
|
158
|
-
markdown: string;
|
|
159
|
-
thinkingText: string;
|
|
160
|
-
draftBytes?: number;
|
|
161
|
-
preferenceBytes?: number;
|
|
162
|
-
usage?: unknown;
|
|
163
|
-
}>;
|
|
164
|
-
type ThinkingLevel = Parameters<ExtensionAPI["setThinkingLevel"]>[0];
|
|
165
|
-
|
|
166
|
-
const notify = (
|
|
167
|
-
ctx: ExtensionContext,
|
|
168
|
-
message: string,
|
|
169
|
-
level: "error" | "info" | "warning"
|
|
170
|
-
) => {
|
|
171
|
-
if (ctx.hasUI) {
|
|
172
|
-
ctx.ui.notify(message, level);
|
|
173
|
-
}
|
|
174
|
-
};
|
|
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
|
-
|
|
279
|
-
const findConfiguredModel = (ctx: ExtensionContext, ref: string) => {
|
|
280
|
-
const [provider, modelId] = splitRef(ref);
|
|
281
|
-
return ctx.modelRegistry.find(provider, modelId);
|
|
282
|
-
};
|
|
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
|
-
|
|
327
|
-
export const registerCommands = (
|
|
328
|
-
pi: ExtensionAPI,
|
|
329
|
-
dependencies: {
|
|
330
|
-
consult?: ManualConsult;
|
|
331
|
-
sessionState?: AdvisorSessionState;
|
|
332
|
-
statusManager?: ScoutStatusManager;
|
|
333
|
-
} = {}
|
|
334
|
-
) => {
|
|
335
|
-
const advisorSessionState =
|
|
336
|
-
dependencies.sessionState ?? defaultAdvisorSessionState;
|
|
337
|
-
const scoutStatus =
|
|
338
|
-
dependencies.statusManager ?? new ScoutStatusManager(false);
|
|
339
|
-
const flowEnabled = () => pi.getActiveTools().includes("ask_advisor");
|
|
340
|
-
const requestAdvisor =
|
|
341
|
-
dependencies.consult ??
|
|
342
|
-
((ctx, question, signal, onChunk, onScout, gitContext) =>
|
|
343
|
-
consultAdvisor(
|
|
344
|
-
ctx,
|
|
345
|
-
question,
|
|
346
|
-
signal,
|
|
347
|
-
onChunk,
|
|
348
|
-
"manual",
|
|
349
|
-
gitContext,
|
|
350
|
-
undefined,
|
|
351
|
-
undefined,
|
|
352
|
-
undefined,
|
|
353
|
-
onScout,
|
|
354
|
-
undefined
|
|
355
|
-
));
|
|
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;
|
|
363
|
-
const updateAdvisorUsageStatus = (ctx: ExtensionContext) => {
|
|
364
|
-
if (ctx.hasUI) {
|
|
365
|
-
ctx.ui.setStatus(
|
|
366
|
-
"advisor-usage",
|
|
367
|
-
getAdvisorSettings().showUsageFooter
|
|
368
|
-
? advisorSessionState.usageStatus()
|
|
369
|
-
: undefined
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
};
|
|
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);
|
|
385
|
-
}
|
|
386
|
-
};
|
|
387
|
-
|
|
388
|
-
const startManualConsultation = (
|
|
389
|
-
ctx: ExtensionContext,
|
|
390
|
-
question: string | undefined,
|
|
391
|
-
controller: AbortController,
|
|
392
|
-
scoutStatusToken: symbol,
|
|
393
|
-
progress: ManualAdvisorProgressState,
|
|
394
|
-
gitContext?: GitContextLevel
|
|
395
|
-
) => {
|
|
396
|
-
herdrAdvisorActivity.start();
|
|
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
|
-
}
|
|
412
|
-
let scoutDetails: Parameters<typeof appendScoutLifecycleEntry>[2];
|
|
413
|
-
return requestAdvisor(
|
|
414
|
-
ctx,
|
|
415
|
-
question,
|
|
416
|
-
controller.signal,
|
|
417
|
-
(thinking, text) => {
|
|
418
|
-
if (controller.signal.aborted) {
|
|
419
|
-
return;
|
|
420
|
-
}
|
|
421
|
-
progress.phase = "active";
|
|
422
|
-
progress.thinking = thinking;
|
|
423
|
-
progress.text = text;
|
|
424
|
-
requestManualRender(ctx);
|
|
425
|
-
},
|
|
426
|
-
(event) => {
|
|
427
|
-
if (!controller.signal.aborted) {
|
|
428
|
-
scoutStatus.update(ctx, scoutStatusToken, event);
|
|
429
|
-
scoutDetails = appendScoutLifecycleEntry(pi, event, scoutDetails);
|
|
430
|
-
progress.scout = scoutDetails;
|
|
431
|
-
progress.phase = "active";
|
|
432
|
-
requestManualRender(ctx);
|
|
433
|
-
}
|
|
434
|
-
},
|
|
435
|
-
gitContext
|
|
436
|
-
)
|
|
437
|
-
.then(({ markdown, usage }) => {
|
|
438
|
-
if (controller.signal.aborted) {
|
|
439
|
-
return;
|
|
440
|
-
}
|
|
441
|
-
progress.phase = "complete";
|
|
442
|
-
advisorSessionState.recordInvocation({
|
|
443
|
-
cost: advisorUsageCost(usage),
|
|
444
|
-
executionEffect: "continued",
|
|
445
|
-
kind: "markdown",
|
|
446
|
-
model: advisorRef,
|
|
447
|
-
trigger: "manual",
|
|
448
|
-
usage,
|
|
449
|
-
});
|
|
450
|
-
updateAdvisorUsageStatus(ctx);
|
|
451
|
-
const normalizedUsage = snapshotAdvisorUsage(usage);
|
|
452
|
-
pi.sendMessage(
|
|
453
|
-
{
|
|
454
|
-
content: `Manual Advisor consultation${question ? ` (${question})` : ""}:\n\n${markdown}`,
|
|
455
|
-
customType: "advisor-manual-result",
|
|
456
|
-
details: {
|
|
457
|
-
advisor: advisorRef,
|
|
458
|
-
question,
|
|
459
|
-
text: markdown,
|
|
460
|
-
...(normalizedUsage ? { usage: normalizedUsage } : {}),
|
|
461
|
-
},
|
|
462
|
-
display: true,
|
|
463
|
-
},
|
|
464
|
-
{
|
|
465
|
-
// Steer lets the current turn finish its active work; the Executor sees
|
|
466
|
-
// the result before its next model call rather than being interrupted.
|
|
467
|
-
deliverAs: "steer",
|
|
468
|
-
triggerTurn: true,
|
|
469
|
-
}
|
|
470
|
-
);
|
|
471
|
-
})
|
|
472
|
-
.catch((error: unknown) => {
|
|
473
|
-
if (controller.signal.aborted) {
|
|
474
|
-
return;
|
|
475
|
-
}
|
|
476
|
-
progress.phase = "error";
|
|
477
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
478
|
-
advisorSessionState.recordInvocation({
|
|
479
|
-
executionEffect: "continued",
|
|
480
|
-
failure: "provider-error",
|
|
481
|
-
kind: "markdown",
|
|
482
|
-
model: advisorRef,
|
|
483
|
-
trigger: "manual",
|
|
484
|
-
});
|
|
485
|
-
updateAdvisorUsageStatus(ctx);
|
|
486
|
-
pi.sendMessage(
|
|
487
|
-
{
|
|
488
|
-
content: `Manual Advisor consultation failed: ${message}`,
|
|
489
|
-
customType: "advisor-manual-result",
|
|
490
|
-
details: {
|
|
491
|
-
advisor: advisorRef,
|
|
492
|
-
text: `**Advisor consultation failed:** ${message}`,
|
|
493
|
-
},
|
|
494
|
-
display: true,
|
|
495
|
-
},
|
|
496
|
-
{ deliverAs: "steer", triggerTurn: true }
|
|
497
|
-
);
|
|
498
|
-
notify(ctx, `Advisor consultation failed: ${message}`, "error");
|
|
499
|
-
notifyHerdrAdvisorFailure("Advisor consultation failed", message);
|
|
500
|
-
})
|
|
501
|
-
.finally(() => {
|
|
502
|
-
if (controller.signal.aborted) {
|
|
503
|
-
progress.phase = "cancelled";
|
|
504
|
-
}
|
|
505
|
-
const timer = manualProgressTimers.get(controller);
|
|
506
|
-
if (timer) {
|
|
507
|
-
clearInterval(timer);
|
|
508
|
-
manualProgressTimers.delete(controller);
|
|
509
|
-
}
|
|
510
|
-
requestManualRender(ctx);
|
|
511
|
-
scoutStatus.release(ctx, scoutStatusToken);
|
|
512
|
-
manualConsultations.delete(controller);
|
|
513
|
-
herdrAdvisorActivity.finish();
|
|
514
|
-
});
|
|
515
|
-
};
|
|
516
|
-
|
|
517
|
-
/** Resolves both models and their auth, or reports why activation cannot proceed. */
|
|
518
|
-
const resolveActivationModels = async (ctx: ExtensionContext) => {
|
|
519
|
-
const executor = findConfiguredModel(ctx, executorRef);
|
|
520
|
-
if (!executor) {
|
|
521
|
-
return { error: `Executor model not found: ${executorRef}` };
|
|
522
|
-
}
|
|
523
|
-
const advisor = findConfiguredModel(ctx, advisorRef);
|
|
524
|
-
if (!advisor) {
|
|
525
|
-
return { error: `Advisor model not found: ${advisorRef}` };
|
|
526
|
-
}
|
|
527
|
-
const advisorAuth = await ctx.modelRegistry.getApiKeyAndHeaders(advisor);
|
|
528
|
-
if (!(advisorAuth.ok && advisorAuth.apiKey)) {
|
|
529
|
-
return { error: `No API key for Advisor ${advisorRef}` };
|
|
530
|
-
}
|
|
531
|
-
if (!(await pi.setModel(executor))) {
|
|
532
|
-
return { error: `No API key for Executor ${executorRef}` };
|
|
533
|
-
}
|
|
534
|
-
return {};
|
|
535
|
-
};
|
|
536
|
-
|
|
537
|
-
const loadCommandConfig = (ctx: ExtensionContext) => {
|
|
538
|
-
try {
|
|
539
|
-
loadConfig(ctx);
|
|
540
|
-
return true;
|
|
541
|
-
} catch (error) {
|
|
542
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
543
|
-
notify(
|
|
544
|
-
ctx,
|
|
545
|
-
`Advisor command could not load configuration: ${message} Fix advisor.json and retry.`,
|
|
546
|
-
"error"
|
|
547
|
-
);
|
|
548
|
-
return false;
|
|
549
|
-
}
|
|
550
|
-
};
|
|
551
|
-
|
|
552
|
-
const activateAdvisor = async (
|
|
553
|
-
args: string,
|
|
554
|
-
ctx: ExtensionContext,
|
|
555
|
-
announce = true
|
|
556
|
-
) => {
|
|
557
|
-
if (!loadCommandConfig(ctx)) {
|
|
558
|
-
return;
|
|
559
|
-
}
|
|
560
|
-
const previous = {
|
|
561
|
-
advisor: advisorRef,
|
|
562
|
-
contextMaxChars: contextMaxCharsRef,
|
|
563
|
-
executor: executorRef,
|
|
564
|
-
};
|
|
565
|
-
const restoreRefs = () => {
|
|
566
|
-
setAdvisorRef(previous.advisor);
|
|
567
|
-
setContextMaxCharsRef(previous.contextMaxChars);
|
|
568
|
-
setExecutorRef(previous.executor);
|
|
569
|
-
};
|
|
570
|
-
const argumentError = parseArgs(args);
|
|
571
|
-
if (argumentError) {
|
|
572
|
-
restoreRefs();
|
|
573
|
-
notify(ctx, argumentError, "error");
|
|
574
|
-
return;
|
|
575
|
-
}
|
|
576
|
-
const { error } = await resolveActivationModels(ctx);
|
|
577
|
-
if (error) {
|
|
578
|
-
restoreRefs();
|
|
579
|
-
notify(ctx, error, "error");
|
|
580
|
-
return;
|
|
581
|
-
}
|
|
582
|
-
// parseArgs only mutates in-memory refs, and every later loadConfig resets
|
|
583
|
-
// them from disk. Persist supplied arguments once they are known to resolve,
|
|
584
|
-
// so an unusable model reference is never written to the configuration.
|
|
585
|
-
if (args.trim()) {
|
|
586
|
-
saveConfig(ctx);
|
|
587
|
-
}
|
|
588
|
-
if (executorEffortRef) {
|
|
589
|
-
pi.setThinkingLevel(executorEffortRef as ThinkingLevel);
|
|
590
|
-
}
|
|
591
|
-
if (!flowEnabled()) {
|
|
592
|
-
pi.setActiveTools([
|
|
593
|
-
...pi.getActiveTools(),
|
|
594
|
-
"ask_advisor",
|
|
595
|
-
"record_advisor_outcome",
|
|
596
|
-
]);
|
|
597
|
-
}
|
|
598
|
-
if (announce) {
|
|
599
|
-
notify(
|
|
600
|
-
ctx,
|
|
601
|
-
`Advisor flow ready — Executor: ${executorRef} (thinking: ${executorEffortRef || "default"}) · Advisor: ${advisorRef} (thinking: ${advisorEffortRef || "default"})`,
|
|
602
|
-
"info"
|
|
603
|
-
);
|
|
604
|
-
}
|
|
605
|
-
};
|
|
606
|
-
|
|
607
|
-
pi.registerEntryRenderer?.(
|
|
608
|
-
"advisor-manual-call",
|
|
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);
|
|
623
|
-
}
|
|
624
|
-
);
|
|
625
|
-
|
|
626
|
-
pi.registerMessageRenderer?.(
|
|
627
|
-
"advisor-manual-result",
|
|
628
|
-
(message, { expanded }, theme) => {
|
|
629
|
-
const details = message.details as
|
|
630
|
-
| { advisor?: string; text?: string; usage?: unknown }
|
|
631
|
-
| undefined;
|
|
632
|
-
const box = new Box(1, 1, (text) => theme.bg("customMessageBg", text));
|
|
633
|
-
const advice =
|
|
634
|
-
details?.text ??
|
|
635
|
-
(typeof message.content === "string"
|
|
636
|
-
? message.content
|
|
637
|
-
: "(Advisor returned no advice.)");
|
|
638
|
-
// Manual consultations must render exactly like an Executor ask_advisor call.
|
|
639
|
-
box.addChild(
|
|
640
|
-
new Text(
|
|
641
|
-
renderAdvisorResponseHeader(hasSoundVerdict(advice), theme),
|
|
642
|
-
0,
|
|
643
|
-
0
|
|
644
|
-
)
|
|
645
|
-
);
|
|
646
|
-
if (details?.advisor) {
|
|
647
|
-
box.addChild(new Text(theme.fg("dim", ` ${details.advisor}`), 0, 0));
|
|
648
|
-
}
|
|
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
|
-
}
|
|
654
|
-
}
|
|
655
|
-
box.addChild(
|
|
656
|
-
new Markdown(
|
|
657
|
-
adviceForDisplay(advice, expanded),
|
|
658
|
-
0,
|
|
659
|
-
0,
|
|
660
|
-
getMarkdownTheme()
|
|
661
|
-
)
|
|
662
|
-
);
|
|
663
|
-
return box;
|
|
664
|
-
}
|
|
665
|
-
);
|
|
666
|
-
|
|
667
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
668
|
-
// A malformed advisor.json or a provider auth failure must not reject a
|
|
669
|
-
// lifecycle handler and break session startup.
|
|
670
|
-
try {
|
|
671
|
-
loadConfig(ctx);
|
|
672
|
-
if (alwaysOnRef) {
|
|
673
|
-
await activateAdvisor("", ctx, false);
|
|
674
|
-
}
|
|
675
|
-
} catch (error) {
|
|
676
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
677
|
-
notify(ctx, `Advisor activation failed: ${message}`, "error");
|
|
678
|
-
}
|
|
679
|
-
});
|
|
680
|
-
|
|
681
|
-
pi.on("model_select", (event, ctx) => {
|
|
682
|
-
// Only an explicit user selection redefines the Executor. "restore" replays a
|
|
683
|
-
// stored session model and would otherwise overwrite saved configuration.
|
|
684
|
-
if (event.source !== "set" || !flowEnabled()) {
|
|
685
|
-
return;
|
|
686
|
-
}
|
|
687
|
-
const selected = `${event.model.provider}/${event.model.id}`;
|
|
688
|
-
if (selected === executorRef) {
|
|
689
|
-
return;
|
|
690
|
-
}
|
|
691
|
-
setExecutorRef(selected);
|
|
692
|
-
saveConfig(ctx);
|
|
693
|
-
});
|
|
694
|
-
|
|
695
|
-
pi.on("session_shutdown", (_event, ctx) => {
|
|
696
|
-
if (ctx.hasUI) {
|
|
697
|
-
ctx.ui.setStatus("advisor-usage", undefined);
|
|
698
|
-
}
|
|
699
|
-
for (const [controller, token] of manualConsultations) {
|
|
700
|
-
controller.abort();
|
|
701
|
-
const timer = manualProgressTimers.get(controller);
|
|
702
|
-
if (timer) {
|
|
703
|
-
clearInterval(timer);
|
|
704
|
-
manualProgressTimers.delete(controller);
|
|
705
|
-
}
|
|
706
|
-
scoutStatus.release(ctx, token);
|
|
707
|
-
}
|
|
708
|
-
scoutStatus.clear(ctx);
|
|
709
|
-
manualConsultations.clear();
|
|
710
|
-
manualProgressTimers.clear();
|
|
711
|
-
manualProgress.clear();
|
|
712
|
-
herdrAdvisorActivity.clear();
|
|
713
|
-
});
|
|
714
|
-
|
|
715
|
-
pi.registerCommand("advisor-manual", {
|
|
716
|
-
description:
|
|
717
|
-
"Consult the Advisor in parallel; accepts an optional focused question and fans its response out to the Executor",
|
|
718
|
-
handler: async (args, ctx) => {
|
|
719
|
-
if (!loadCommandConfig(ctx)) {
|
|
720
|
-
return;
|
|
721
|
-
}
|
|
722
|
-
if (
|
|
723
|
-
!(
|
|
724
|
-
isSimpleMode() ||
|
|
725
|
-
advisorSessionState.canConsult(getAdvisorMaxCallsPerSession())
|
|
726
|
-
)
|
|
727
|
-
) {
|
|
728
|
-
reportManualBudgetExhausted(ctx);
|
|
729
|
-
return;
|
|
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
|
-
|
|
776
|
-
if (!isSimpleMode()) {
|
|
777
|
-
advisorSessionState.consumeCall();
|
|
778
|
-
}
|
|
779
|
-
// A single visible progress surface avoids competing consultations overwriting
|
|
780
|
-
// each other's streamed state. A newer manual request replaces the previous one.
|
|
781
|
-
for (const [pending, token] of manualConsultations) {
|
|
782
|
-
pending.abort();
|
|
783
|
-
scoutStatus.release(ctx, token);
|
|
784
|
-
}
|
|
785
|
-
manualConsultations.clear();
|
|
786
|
-
const controller = new AbortController();
|
|
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);
|
|
792
|
-
scoutStatus.register(scoutStatusToken);
|
|
793
|
-
manualConsultations.set(controller, scoutStatusToken);
|
|
794
|
-
pi.appendEntry?.("advisor-manual-call", { progressId, question });
|
|
795
|
-
startManualConsultation(
|
|
796
|
-
ctx,
|
|
797
|
-
question,
|
|
798
|
-
controller,
|
|
799
|
-
scoutStatusToken,
|
|
800
|
-
progress,
|
|
801
|
-
gitContext
|
|
802
|
-
);
|
|
803
|
-
},
|
|
804
|
-
});
|
|
805
|
-
|
|
806
|
-
pi.registerCommand("advisor", {
|
|
807
|
-
description:
|
|
808
|
-
"Enable the Executor/Advisor flow and switch to the configured Executor model; accepts contextMaxChars=N",
|
|
809
|
-
handler: (args, ctx) => activateAdvisor(args, ctx),
|
|
810
|
-
});
|
|
811
|
-
|
|
812
|
-
pi.registerCommand("advisor-models", {
|
|
813
|
-
description:
|
|
814
|
-
"Select and persist the Executor and Advisor models with reasoning levels",
|
|
815
|
-
handler: async (_args, ctx) => {
|
|
816
|
-
if (!(loadCommandConfig(ctx) && ctx.hasUI)) {
|
|
817
|
-
return;
|
|
818
|
-
}
|
|
819
|
-
const refs = ctx.modelRegistry
|
|
820
|
-
.getAvailable()
|
|
821
|
-
.map((m) => `${m.provider}/${m.id}`);
|
|
822
|
-
|
|
823
|
-
const executor = await ctx.ui.custom<string | undefined>(
|
|
824
|
-
(tui, theme, keybindings, done) =>
|
|
825
|
-
new SearchableModelSelector({
|
|
826
|
-
allOptions: refs,
|
|
827
|
-
currentOption: executorRef,
|
|
828
|
-
keybindings,
|
|
829
|
-
onCancel: () => done(undefined),
|
|
830
|
-
onSelect: done,
|
|
831
|
-
theme,
|
|
832
|
-
title: "Select Executor Model",
|
|
833
|
-
tui,
|
|
834
|
-
})
|
|
835
|
-
);
|
|
836
|
-
if (!executor) {
|
|
837
|
-
return;
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
const executorEffort = await ctx.ui.select(
|
|
841
|
-
"Select Executor Reasoning/Thinking Level",
|
|
842
|
-
effortChoices(executorEffortRef)
|
|
843
|
-
);
|
|
844
|
-
if (!executorEffort) {
|
|
845
|
-
return;
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
const advisor = await ctx.ui.custom<string | undefined>(
|
|
849
|
-
(tui, theme, keybindings, done) =>
|
|
850
|
-
new SearchableModelSelector({
|
|
851
|
-
allOptions: refs,
|
|
852
|
-
currentOption: advisorRef,
|
|
853
|
-
keybindings,
|
|
854
|
-
onCancel: () => done(undefined),
|
|
855
|
-
onSelect: done,
|
|
856
|
-
theme,
|
|
857
|
-
title: "Select Advisor Model",
|
|
858
|
-
tui,
|
|
859
|
-
})
|
|
860
|
-
);
|
|
861
|
-
if (!advisor) {
|
|
862
|
-
return;
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
const advisorEffort = await ctx.ui.select(
|
|
866
|
-
"Select Advisor Reasoning/Thinking Level",
|
|
867
|
-
effortChoices(advisorEffortRef)
|
|
868
|
-
);
|
|
869
|
-
if (!advisorEffort) {
|
|
870
|
-
return;
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
setExecutorRef(executor);
|
|
874
|
-
setAdvisorRef(advisor);
|
|
875
|
-
setExecutorEffortRef(selectedEffort(executorEffort));
|
|
876
|
-
setAdvisorEffortRef(selectedEffort(advisorEffort));
|
|
877
|
-
|
|
878
|
-
const path = saveConfig(ctx);
|
|
879
|
-
ctx.ui.notify(
|
|
880
|
-
`Saved Executor + Advisor configurations to ${path}`,
|
|
881
|
-
"info"
|
|
882
|
-
);
|
|
883
|
-
},
|
|
884
|
-
});
|
|
885
|
-
|
|
886
|
-
pi.registerCommand("advisor-settings", {
|
|
887
|
-
description: "Configure Advisor settings",
|
|
888
|
-
handler: async (_args, ctx) => {
|
|
889
|
-
if (!(loadCommandConfig(ctx) && ctx.hasUI)) {
|
|
890
|
-
return;
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
const initial: AdvisorSettings = getAdvisorSettings();
|
|
894
|
-
await ctx.ui.custom<void>(
|
|
895
|
-
(tui, theme, _keybindings, done) =>
|
|
896
|
-
new AdvisorSettingsSelector({
|
|
897
|
-
effortLevels: EFFORT_LEVELS,
|
|
898
|
-
initial,
|
|
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
|
-
},
|
|
913
|
-
presets: CONTEXT_PRESETS,
|
|
914
|
-
theme,
|
|
915
|
-
tui,
|
|
916
|
-
})
|
|
917
|
-
);
|
|
918
|
-
},
|
|
919
|
-
});
|
|
920
|
-
|
|
921
|
-
pi.registerCommand("advisor-off", {
|
|
922
|
-
description: "Disable on-demand Advisor calls; keep the current model",
|
|
923
|
-
handler: (_args, ctx) => {
|
|
924
|
-
pi.setActiveTools(
|
|
925
|
-
pi
|
|
926
|
-
.getActiveTools()
|
|
927
|
-
.filter(
|
|
928
|
-
(name) =>
|
|
929
|
-
name !== "ask_advisor" && name !== "record_advisor_outcome"
|
|
930
|
-
)
|
|
931
|
-
);
|
|
932
|
-
// Leaving alwaysOn set would silently reactivate the flow next session.
|
|
933
|
-
const wasAlwaysOn = alwaysOnRef;
|
|
934
|
-
if (wasAlwaysOn) {
|
|
935
|
-
setAlwaysOnRef(false);
|
|
936
|
-
saveConfig(ctx);
|
|
937
|
-
}
|
|
938
|
-
notify(
|
|
939
|
-
ctx,
|
|
940
|
-
`Advisor flow disabled. Current model unchanged.${wasAlwaysOn ? " Always on turned off." : ""}`,
|
|
941
|
-
"info"
|
|
942
|
-
);
|
|
943
|
-
return Promise.resolve();
|
|
944
|
-
},
|
|
945
|
-
});
|
|
946
|
-
};
|
|
1
|
+
// biome-ignore lint/performance/noBarrelFile: this facade intentionally preserves the public command module contract.
|
|
2
|
+
export { registerCommands } from "./commands/registration.js";
|
|
3
|
+
export type {
|
|
4
|
+
CommandDependencies,
|
|
5
|
+
ManualAdvisorProgressPhase,
|
|
6
|
+
ManualAdvisorProgressState,
|
|
7
|
+
ManualConsult,
|
|
8
|
+
ThinkingLevel,
|
|
9
|
+
} from "./commands/types.js";
|