pi-advisor-flow 0.1.9 → 0.2.1
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 -11
- package/extensions/index.ts +26 -1
- package/package.json +33 -21
- package/src/commands.ts +396 -123
- package/src/config.ts +405 -99
- package/src/conversation.ts +217 -47
- package/src/herdr.ts +142 -30
- package/src/session-state.ts +190 -41
- package/src/tools.ts +804 -199
- package/src/ui.ts +376 -106
package/src/commands.ts
CHANGED
|
@@ -1,152 +1,400 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type ExtensionAPI,
|
|
3
|
+
type ExtensionContext,
|
|
4
|
+
getMarkdownTheme,
|
|
5
|
+
} from "@earendil-works/pi-coding-agent";
|
|
2
6
|
import { Box, Markdown, Text } from "@earendil-works/pi-tui";
|
|
3
7
|
import {
|
|
4
|
-
advisorAutoLoopGateRef,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
advisorAutoLoopGateRef,
|
|
9
|
+
advisorBlockOnBlockedRef,
|
|
10
|
+
advisorCollapseResponsesRef,
|
|
11
|
+
advisorCompletionGateRef,
|
|
12
|
+
advisorCustomInvocationRef,
|
|
13
|
+
advisorEffortRef,
|
|
14
|
+
advisorFailureGateRef,
|
|
15
|
+
advisorFailureModeRef,
|
|
16
|
+
advisorHerdrIntegrationRef,
|
|
17
|
+
advisorLoopThresholdRef,
|
|
18
|
+
advisorMaxCallsPerSessionRef,
|
|
19
|
+
advisorPlanGateRef,
|
|
20
|
+
advisorRef,
|
|
21
|
+
advisorSessionSummaryRef,
|
|
22
|
+
advisorToolResultMaxBytesRef,
|
|
23
|
+
advisorToolResultMaxLinesRef,
|
|
24
|
+
contextMaxCharsRef,
|
|
25
|
+
executorEffortRef,
|
|
26
|
+
executorRef,
|
|
27
|
+
loadConfig,
|
|
28
|
+
parseArgs,
|
|
29
|
+
saveConfig,
|
|
30
|
+
setAdvisorAutoLoopGateRef,
|
|
31
|
+
setAdvisorBlockOnBlockedRef,
|
|
32
|
+
setAdvisorCollapseResponsesRef,
|
|
33
|
+
setAdvisorCompletionGateRef,
|
|
34
|
+
setAdvisorCustomInvocationRef,
|
|
35
|
+
setAdvisorEffortRef,
|
|
36
|
+
setAdvisorFailureGateRef,
|
|
37
|
+
setAdvisorFailureModeRef,
|
|
38
|
+
setAdvisorHerdrIntegrationRef,
|
|
39
|
+
setAdvisorLoopThresholdRef,
|
|
40
|
+
setAdvisorMaxCallsPerSessionRef,
|
|
41
|
+
setAdvisorPlanGateRef,
|
|
42
|
+
setAdvisorRef,
|
|
43
|
+
setAdvisorSessionSummaryRef,
|
|
44
|
+
setAdvisorToolResultMaxBytesRef,
|
|
45
|
+
setAdvisorToolResultMaxLinesRef,
|
|
46
|
+
setContextMaxCharsRef,
|
|
47
|
+
setExecutorEffortRef,
|
|
48
|
+
setExecutorRef,
|
|
49
|
+
splitRef,
|
|
9
50
|
} from "./config.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
|
|
51
|
+
import { herdrAdvisorActivity, notifyHerdrAdvisorFailure } from "./herdr.js";
|
|
52
|
+
import {
|
|
53
|
+
adviceForDisplay,
|
|
54
|
+
advisorSessionState,
|
|
55
|
+
consultAdvisor,
|
|
56
|
+
renderAdvisorCallBox,
|
|
57
|
+
resolveAdvisorRequest,
|
|
58
|
+
} from "./tools.js";
|
|
59
|
+
import {
|
|
60
|
+
type AdvisorSettings,
|
|
61
|
+
AdvisorSettingsSelector,
|
|
62
|
+
type ContextPreset,
|
|
63
|
+
SearchableModelSelector,
|
|
64
|
+
} from "./ui.js";
|
|
13
65
|
|
|
14
|
-
const EFFORT_LEVELS = [
|
|
66
|
+
const EFFORT_LEVELS = [
|
|
67
|
+
"Default (Model Default)",
|
|
68
|
+
"off",
|
|
69
|
+
"minimal",
|
|
70
|
+
"low",
|
|
71
|
+
"medium",
|
|
72
|
+
"high",
|
|
73
|
+
"xhigh",
|
|
74
|
+
"max",
|
|
75
|
+
];
|
|
15
76
|
|
|
16
77
|
const CONTEXT_PRESETS: ContextPreset[] = [
|
|
17
|
-
{
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
78
|
+
{
|
|
79
|
+
description:
|
|
80
|
+
"No conversation history. The Advisor receives only its standing instructions.",
|
|
81
|
+
label: "0",
|
|
82
|
+
value: 0,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
description: "The most recent 10,000 characters of the current branch.",
|
|
86
|
+
label: "10k",
|
|
87
|
+
value: 10_000,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
description: "The most recent 25,000 characters of the current branch.",
|
|
91
|
+
label: "25k",
|
|
92
|
+
value: 25_000,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
description: "The most recent 100,000 characters of the current branch.",
|
|
96
|
+
label: "100k",
|
|
97
|
+
value: 100_000,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
description: "The most recent 200,000 characters of the current branch.",
|
|
101
|
+
label: "200k",
|
|
102
|
+
value: 200_000,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
description:
|
|
106
|
+
"The complete reconstructed conversation branch. Cost and model context limits apply.",
|
|
107
|
+
label: "ALL",
|
|
108
|
+
value: Number.MAX_SAFE_INTEGER,
|
|
109
|
+
},
|
|
23
110
|
];
|
|
24
111
|
|
|
25
|
-
type ManualConsult = (
|
|
112
|
+
type ManualConsult = (
|
|
113
|
+
ctx: ExtensionContext,
|
|
114
|
+
question?: string,
|
|
115
|
+
signal?: AbortSignal
|
|
116
|
+
) => Promise<{ markdown: string; thinkingText: string }>;
|
|
117
|
+
type ThinkingLevel = Parameters<ExtensionAPI["setThinkingLevel"]>[0];
|
|
118
|
+
|
|
119
|
+
const notify = (
|
|
120
|
+
ctx: ExtensionContext,
|
|
121
|
+
message: string,
|
|
122
|
+
level: "error" | "info" | "warning"
|
|
123
|
+
) => {
|
|
124
|
+
if (ctx.hasUI) {
|
|
125
|
+
ctx.ui.notify(message, level);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const findConfiguredModel = (ctx: ExtensionContext, ref: string) => {
|
|
130
|
+
const [provider, modelId] = splitRef(ref);
|
|
131
|
+
return ctx.modelRegistry.find(provider, modelId);
|
|
132
|
+
};
|
|
26
133
|
|
|
27
|
-
export const registerCommands = (
|
|
134
|
+
export const registerCommands = (
|
|
135
|
+
pi: ExtensionAPI,
|
|
136
|
+
dependencies: { consult?: ManualConsult } = {}
|
|
137
|
+
) => {
|
|
28
138
|
const flowEnabled = () => pi.getActiveTools().includes("ask_advisor");
|
|
29
|
-
const requestAdvisor =
|
|
139
|
+
const requestAdvisor =
|
|
140
|
+
dependencies.consult ??
|
|
141
|
+
((ctx, question, signal) =>
|
|
142
|
+
consultAdvisor(ctx, question, signal, undefined, "manual"));
|
|
30
143
|
const manualConsultations = new Set<AbortController>();
|
|
31
144
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
145
|
+
const startManualConsultation = (
|
|
146
|
+
ctx: ExtensionContext,
|
|
147
|
+
question: string | undefined,
|
|
148
|
+
controller: AbortController
|
|
149
|
+
) => {
|
|
150
|
+
herdrAdvisorActivity.start();
|
|
151
|
+
return requestAdvisor(ctx, question, controller.signal)
|
|
152
|
+
.then(({ markdown }) => {
|
|
153
|
+
advisorSessionState.recordInvocation({
|
|
154
|
+
executionEffect: "continued",
|
|
155
|
+
kind: "markdown",
|
|
156
|
+
model: advisorRef,
|
|
157
|
+
trigger: "manual",
|
|
158
|
+
});
|
|
159
|
+
if (controller.signal.aborted) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
pi.sendMessage(
|
|
163
|
+
{
|
|
164
|
+
content: `Manual Advisor consultation${question ? ` (${question})` : ""}:\n\n${markdown}`,
|
|
165
|
+
customType: "advisor-manual-result",
|
|
166
|
+
details: { advisor: advisorRef, question, text: markdown },
|
|
167
|
+
display: true,
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
// Steer lets the current turn finish its active work; the Executor sees
|
|
171
|
+
// the result before its next model call rather than being interrupted.
|
|
172
|
+
deliverAs: "steer",
|
|
173
|
+
triggerTurn: true,
|
|
174
|
+
}
|
|
175
|
+
);
|
|
176
|
+
})
|
|
177
|
+
.catch((error: unknown) => {
|
|
178
|
+
if (controller.signal.aborted) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
182
|
+
advisorSessionState.recordInvocation({
|
|
183
|
+
executionEffect: "continued",
|
|
184
|
+
failure: "provider-error",
|
|
185
|
+
kind: "markdown",
|
|
186
|
+
model: advisorRef,
|
|
187
|
+
trigger: "manual",
|
|
188
|
+
});
|
|
189
|
+
pi.sendMessage(
|
|
190
|
+
{
|
|
191
|
+
content: `Manual Advisor consultation failed: ${message}`,
|
|
192
|
+
customType: "advisor-manual-result",
|
|
193
|
+
details: {
|
|
194
|
+
advisor: advisorRef,
|
|
195
|
+
text: `**Advisor consultation failed:** ${message}`,
|
|
196
|
+
},
|
|
197
|
+
display: true,
|
|
198
|
+
},
|
|
199
|
+
{ deliverAs: "steer", triggerTurn: true }
|
|
200
|
+
);
|
|
201
|
+
notify(ctx, `Advisor consultation failed: ${message}`, "error");
|
|
202
|
+
notifyHerdrAdvisorFailure("Advisor consultation failed", message);
|
|
203
|
+
})
|
|
204
|
+
.finally(() => {
|
|
205
|
+
manualConsultations.delete(controller);
|
|
206
|
+
herdrAdvisorActivity.finish();
|
|
207
|
+
});
|
|
208
|
+
};
|
|
36
209
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
210
|
+
const activateAdvisor = async (args: string, ctx: ExtensionContext) => {
|
|
211
|
+
loadConfig(ctx);
|
|
212
|
+
const argumentError = parseArgs(args);
|
|
213
|
+
if (argumentError) {
|
|
214
|
+
notify(ctx, argumentError, "error");
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const executor = findConfiguredModel(ctx, executorRef);
|
|
218
|
+
if (!executor) {
|
|
219
|
+
notify(ctx, `Executor model not found: ${executorRef}`, "error");
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (!findConfiguredModel(ctx, advisorRef)) {
|
|
223
|
+
notify(ctx, `Advisor model not found: ${advisorRef}`, "error");
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (!(await pi.setModel(executor))) {
|
|
227
|
+
notify(ctx, `No API key for Executor ${executorRef}`, "error");
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (executorEffortRef) {
|
|
231
|
+
pi.setThinkingLevel(executorEffortRef as ThinkingLevel);
|
|
232
|
+
}
|
|
233
|
+
if (!flowEnabled()) {
|
|
234
|
+
pi.setActiveTools([...pi.getActiveTools(), "ask_advisor"]);
|
|
235
|
+
}
|
|
236
|
+
notify(
|
|
237
|
+
ctx,
|
|
238
|
+
`Advisor flow ready — Executor: ${executorRef} (thinking: ${executorEffortRef || "default"}) · Advisor: ${advisorRef} (thinking: ${advisorEffortRef || "default"})`,
|
|
239
|
+
"info"
|
|
240
|
+
);
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
pi.registerEntryRenderer?.(
|
|
244
|
+
"advisor-manual-call",
|
|
245
|
+
(entry, _options, theme) => {
|
|
246
|
+
const { question } = (entry.data ?? {}) as { question?: string };
|
|
247
|
+
return renderAdvisorCallBox(question, theme);
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
pi.registerMessageRenderer?.(
|
|
252
|
+
"advisor-manual-result",
|
|
253
|
+
(message, { expanded }, theme) => {
|
|
254
|
+
const details = message.details as
|
|
255
|
+
| { advisor?: string; text?: string }
|
|
256
|
+
| undefined;
|
|
257
|
+
const box = new Box(1, 1, (text) => theme.bg("customMessageBg", text));
|
|
258
|
+
box.addChild(
|
|
259
|
+
new Text(theme.fg("warning", theme.bold("◆ ADVISOR RESPONSE")), 0, 0)
|
|
260
|
+
);
|
|
261
|
+
if (details?.advisor) {
|
|
262
|
+
box.addChild(new Text(theme.fg("dim", ` ${details.advisor}`), 0, 0));
|
|
263
|
+
}
|
|
264
|
+
const advice =
|
|
265
|
+
details?.text ??
|
|
266
|
+
(typeof message.content === "string"
|
|
267
|
+
? message.content
|
|
268
|
+
: "(Advisor returned no advice.)");
|
|
269
|
+
box.addChild(
|
|
270
|
+
new Markdown(
|
|
271
|
+
adviceForDisplay(advice, expanded),
|
|
272
|
+
0,
|
|
273
|
+
0,
|
|
274
|
+
getMarkdownTheme()
|
|
275
|
+
)
|
|
276
|
+
);
|
|
277
|
+
return box;
|
|
278
|
+
}
|
|
279
|
+
);
|
|
46
280
|
|
|
47
281
|
pi.on("session_shutdown", () => {
|
|
48
|
-
for (const controller of manualConsultations)
|
|
282
|
+
for (const controller of manualConsultations) {
|
|
283
|
+
controller.abort();
|
|
284
|
+
}
|
|
49
285
|
manualConsultations.clear();
|
|
50
286
|
herdrAdvisorActivity.clear();
|
|
51
287
|
});
|
|
52
288
|
|
|
53
289
|
pi.registerCommand("advisor-manual", {
|
|
54
|
-
description:
|
|
55
|
-
|
|
290
|
+
description:
|
|
291
|
+
"Consult the Advisor in parallel; accepts an optional focused question and fans its response out to the Executor",
|
|
292
|
+
handler: (args, ctx) => {
|
|
56
293
|
loadConfig(ctx);
|
|
57
|
-
if (!advisorSessionState.
|
|
58
|
-
|
|
59
|
-
|
|
294
|
+
if (!advisorSessionState.canConsult(advisorMaxCallsPerSessionRef)) {
|
|
295
|
+
const message = "Advisor call budget exhausted for this session.";
|
|
296
|
+
notify(ctx, message, "warning");
|
|
297
|
+
notifyHerdrAdvisorFailure("Advisor budget exhausted", message);
|
|
298
|
+
return Promise.resolve();
|
|
60
299
|
}
|
|
61
|
-
advisorSessionState.
|
|
300
|
+
advisorSessionState.consumeCall();
|
|
62
301
|
const question = resolveAdvisorRequest(args);
|
|
63
302
|
// A single visible progress surface avoids competing consultations overwriting
|
|
64
303
|
// each other's streamed state. A newer manual request replaces the previous one.
|
|
65
|
-
for (const pending of manualConsultations)
|
|
304
|
+
for (const pending of manualConsultations) {
|
|
305
|
+
pending.abort();
|
|
306
|
+
}
|
|
66
307
|
manualConsultations.clear();
|
|
67
308
|
const controller = new AbortController();
|
|
68
309
|
manualConsultations.add(controller);
|
|
69
310
|
pi.appendEntry?.("advisor-manual-call", { question });
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
void requestAdvisor(ctx, question, controller.signal)
|
|
73
|
-
.then(({ advice }) => {
|
|
74
|
-
advisorSessionState.recordConsultation("manual");
|
|
75
|
-
if (controller.signal.aborted) return;
|
|
76
|
-
pi.sendMessage({
|
|
77
|
-
customType: "advisor-manual-result",
|
|
78
|
-
content: `Manual Advisor consultation${question ? ` (${question})` : ""}:\n\n${advice}`,
|
|
79
|
-
display: true,
|
|
80
|
-
details: { advisor: advisorRef, question, text: advice },
|
|
81
|
-
}, {
|
|
82
|
-
// Steer lets the current turn finish its active work; the Executor sees
|
|
83
|
-
// the result before its next model call rather than being interrupted.
|
|
84
|
-
deliverAs: "steer",
|
|
85
|
-
triggerTurn: true,
|
|
86
|
-
});
|
|
87
|
-
})
|
|
88
|
-
.catch((error: unknown) => {
|
|
89
|
-
if (controller.signal.aborted) return;
|
|
90
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
91
|
-
if (ctx.hasUI) ctx.ui.notify(`Advisor consultation failed: ${message}`, "error");
|
|
92
|
-
})
|
|
93
|
-
.finally(() => {
|
|
94
|
-
manualConsultations.delete(controller);
|
|
95
|
-
herdrAdvisorActivity.finish();
|
|
96
|
-
});
|
|
311
|
+
startManualConsultation(ctx, question, controller);
|
|
312
|
+
return Promise.resolve();
|
|
97
313
|
},
|
|
98
314
|
});
|
|
99
315
|
|
|
100
316
|
pi.registerCommand("advisor", {
|
|
101
|
-
description:
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const argumentError = parseArgs(args);
|
|
105
|
-
if (argumentError) {
|
|
106
|
-
if (ctx.hasUI) ctx.ui.notify(argumentError, "error");
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
const [provider, modelId] = splitRef(executorRef);
|
|
110
|
-
const executor = ctx.modelRegistry.find(provider, modelId);
|
|
111
|
-
if (!executor) return ctx.hasUI ? ctx.ui.notify(`Executor model not found: ${executorRef}`, "error") : undefined;
|
|
112
|
-
const [ap, am] = splitRef(advisorRef);
|
|
113
|
-
if (!ctx.modelRegistry.find(ap, am)) return ctx.hasUI ? ctx.ui.notify(`Advisor model not found: ${advisorRef}`, "error") : undefined;
|
|
114
|
-
if (!(await pi.setModel(executor))) return ctx.hasUI ? ctx.ui.notify(`No API key for Executor ${executorRef}`, "error") : undefined;
|
|
115
|
-
if (executorEffortRef) pi.setThinkingLevel(executorEffortRef as any);
|
|
116
|
-
if (!flowEnabled()) pi.setActiveTools([...pi.getActiveTools(), "ask_advisor"]);
|
|
117
|
-
if (ctx.hasUI) ctx.ui.notify(`Advisor flow ready — Executor: ${executorRef} (thinking: ${executorEffortRef || "default"}) · Advisor: ${advisorRef} (thinking: ${advisorEffortRef || "default"})`, "info");
|
|
118
|
-
},
|
|
317
|
+
description:
|
|
318
|
+
"Enable the Executor/Advisor flow and switch to the configured Executor model; accepts contextMaxChars=N",
|
|
319
|
+
handler: (args, ctx) => activateAdvisor(args, ctx),
|
|
119
320
|
});
|
|
120
321
|
|
|
121
322
|
pi.registerCommand("advisor-models", {
|
|
122
|
-
description:
|
|
323
|
+
description:
|
|
324
|
+
"Select and persist the Executor and Advisor models with reasoning levels",
|
|
123
325
|
handler: async (_args, ctx) => {
|
|
124
|
-
|
|
125
|
-
|
|
326
|
+
loadConfig(ctx);
|
|
327
|
+
if (!ctx.hasUI) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const refs = ctx.modelRegistry
|
|
331
|
+
.getAvailable()
|
|
332
|
+
.map((m) => `${m.provider}/${m.id}`);
|
|
126
333
|
|
|
127
|
-
const executor = await ctx.ui.custom<string | undefined>(
|
|
128
|
-
|
|
334
|
+
const executor = await ctx.ui.custom<string | undefined>(
|
|
335
|
+
(tui, theme, keybindings, done) =>
|
|
336
|
+
new SearchableModelSelector({
|
|
337
|
+
allOptions: refs,
|
|
338
|
+
keybindings,
|
|
339
|
+
onCancel: () => done(undefined),
|
|
340
|
+
onSelect: done,
|
|
341
|
+
theme,
|
|
342
|
+
title: "Select Executor Model",
|
|
343
|
+
tui,
|
|
344
|
+
})
|
|
129
345
|
);
|
|
130
|
-
if (!executor)
|
|
346
|
+
if (!executor) {
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
131
349
|
|
|
132
|
-
const executorEffort = await ctx.ui.select(
|
|
133
|
-
|
|
350
|
+
const executorEffort = await ctx.ui.select(
|
|
351
|
+
"Select Executor Reasoning/Thinking Level",
|
|
352
|
+
EFFORT_LEVELS
|
|
353
|
+
);
|
|
354
|
+
if (!executorEffort) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
134
357
|
|
|
135
|
-
const advisor = await ctx.ui.custom<string | undefined>(
|
|
136
|
-
|
|
358
|
+
const advisor = await ctx.ui.custom<string | undefined>(
|
|
359
|
+
(tui, theme, keybindings, done) =>
|
|
360
|
+
new SearchableModelSelector({
|
|
361
|
+
allOptions: refs,
|
|
362
|
+
keybindings,
|
|
363
|
+
onCancel: () => done(undefined),
|
|
364
|
+
onSelect: done,
|
|
365
|
+
theme,
|
|
366
|
+
title: "Select Advisor Model",
|
|
367
|
+
tui,
|
|
368
|
+
})
|
|
137
369
|
);
|
|
138
|
-
if (!advisor)
|
|
370
|
+
if (!advisor) {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
139
373
|
|
|
140
|
-
const advisorEffort = await ctx.ui.select(
|
|
141
|
-
|
|
374
|
+
const advisorEffort = await ctx.ui.select(
|
|
375
|
+
"Select Advisor Reasoning/Thinking Level",
|
|
376
|
+
EFFORT_LEVELS
|
|
377
|
+
);
|
|
378
|
+
if (!advisorEffort) {
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
142
381
|
|
|
143
382
|
setExecutorRef(executor);
|
|
144
383
|
setAdvisorRef(advisor);
|
|
145
|
-
setExecutorEffortRef(
|
|
146
|
-
|
|
384
|
+
setExecutorEffortRef(
|
|
385
|
+
executorEffort === "Default (Model Default)"
|
|
386
|
+
? undefined
|
|
387
|
+
: executorEffort
|
|
388
|
+
);
|
|
389
|
+
setAdvisorEffortRef(
|
|
390
|
+
advisorEffort === "Default (Model Default)" ? undefined : advisorEffort
|
|
391
|
+
);
|
|
147
392
|
|
|
148
393
|
const path = saveConfig(ctx);
|
|
149
|
-
ctx.ui.notify(
|
|
394
|
+
ctx.ui.notify(
|
|
395
|
+
`Saved Executor + Advisor configurations to ${path}`,
|
|
396
|
+
"info"
|
|
397
|
+
);
|
|
150
398
|
},
|
|
151
399
|
});
|
|
152
400
|
|
|
@@ -154,36 +402,49 @@ export const registerCommands = (pi: ExtensionAPI, dependencies: { consult?: Man
|
|
|
154
402
|
description: "Configure Advisor context and reasoning effort",
|
|
155
403
|
handler: async (_args, ctx) => {
|
|
156
404
|
loadConfig(ctx);
|
|
157
|
-
if (!ctx.hasUI)
|
|
405
|
+
if (!ctx.hasUI) {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
158
408
|
|
|
159
409
|
const initial: AdvisorSettings = {
|
|
410
|
+
autoLoopGate: advisorAutoLoopGateRef,
|
|
411
|
+
blockOnBlocked: advisorBlockOnBlockedRef,
|
|
412
|
+
collapseResponses: advisorCollapseResponsesRef,
|
|
413
|
+
completionGate: advisorCompletionGateRef,
|
|
160
414
|
contextMaxChars: contextMaxCharsRef,
|
|
415
|
+
customRule: advisorCustomInvocationRef,
|
|
161
416
|
effort: advisorEffortRef,
|
|
162
|
-
planGate: advisorPlanGateRef,
|
|
163
417
|
failureGate: advisorFailureGateRef,
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
customRule: advisorCustomInvocationRef,
|
|
167
|
-
blockOnBlocked: advisorBlockOnBlockedRef,
|
|
168
|
-
autoLoopGate: advisorAutoLoopGateRef,
|
|
418
|
+
failureMode: advisorFailureModeRef,
|
|
419
|
+
herdrIntegration: advisorHerdrIntegrationRef,
|
|
169
420
|
loopThreshold: advisorLoopThresholdRef,
|
|
170
421
|
maxCallsPerSession: advisorMaxCallsPerSessionRef,
|
|
422
|
+
planGate: advisorPlanGateRef,
|
|
171
423
|
sessionSummary: advisorSessionSummaryRef,
|
|
424
|
+
toolResultMaxBytes: advisorToolResultMaxBytesRef,
|
|
425
|
+
toolResultMaxLines: advisorToolResultMaxLinesRef,
|
|
172
426
|
};
|
|
173
|
-
const settings = await ctx.ui.custom<AdvisorSettings | undefined>(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
427
|
+
const settings = await ctx.ui.custom<AdvisorSettings | undefined>(
|
|
428
|
+
(tui, theme, _keybindings, done) =>
|
|
429
|
+
new AdvisorSettingsSelector({
|
|
430
|
+
effortLevels: EFFORT_LEVELS,
|
|
431
|
+
initial,
|
|
432
|
+
onCancel: () => done(undefined),
|
|
433
|
+
onSave: done,
|
|
434
|
+
presets: CONTEXT_PRESETS,
|
|
435
|
+
theme,
|
|
436
|
+
tui,
|
|
437
|
+
})
|
|
183
438
|
);
|
|
184
|
-
if (!settings)
|
|
439
|
+
if (!settings) {
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
185
442
|
|
|
186
|
-
setAdvisorEffortRef(
|
|
443
|
+
setAdvisorEffortRef(
|
|
444
|
+
settings.effort === "Default (Model Default)"
|
|
445
|
+
? undefined
|
|
446
|
+
: settings.effort
|
|
447
|
+
);
|
|
187
448
|
setContextMaxCharsRef(settings.contextMaxChars);
|
|
188
449
|
setAdvisorPlanGateRef(settings.planGate);
|
|
189
450
|
setAdvisorFailureGateRef(settings.failureGate);
|
|
@@ -195,6 +456,10 @@ export const registerCommands = (pi: ExtensionAPI, dependencies: { consult?: Man
|
|
|
195
456
|
setAdvisorLoopThresholdRef(settings.loopThreshold ?? 3);
|
|
196
457
|
setAdvisorMaxCallsPerSessionRef(settings.maxCallsPerSession);
|
|
197
458
|
setAdvisorSessionSummaryRef(settings.sessionSummary ?? true);
|
|
459
|
+
setAdvisorFailureModeRef(settings.failureMode ?? "block-session");
|
|
460
|
+
setAdvisorHerdrIntegrationRef(settings.herdrIntegration ?? true);
|
|
461
|
+
setAdvisorToolResultMaxLinesRef(settings.toolResultMaxLines ?? 2000);
|
|
462
|
+
setAdvisorToolResultMaxBytesRef(settings.toolResultMaxBytes ?? 50 * 1024);
|
|
198
463
|
const path = saveConfig(ctx);
|
|
199
464
|
ctx.ui.notify(`Saved Advisor settings to ${path}`, "info");
|
|
200
465
|
},
|
|
@@ -202,9 +467,17 @@ export const registerCommands = (pi: ExtensionAPI, dependencies: { consult?: Man
|
|
|
202
467
|
|
|
203
468
|
pi.registerCommand("advisor-off", {
|
|
204
469
|
description: "Disable on-demand Advisor calls; keep the current model",
|
|
205
|
-
handler:
|
|
206
|
-
pi.setActiveTools(
|
|
207
|
-
|
|
470
|
+
handler: (_args, ctx) => {
|
|
471
|
+
pi.setActiveTools(
|
|
472
|
+
pi.getActiveTools().filter((name) => name !== "ask_advisor")
|
|
473
|
+
);
|
|
474
|
+
if (ctx.hasUI) {
|
|
475
|
+
ctx.ui.notify(
|
|
476
|
+
"Advisor flow disabled. Current model unchanged.",
|
|
477
|
+
"info"
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
return Promise.resolve();
|
|
208
481
|
},
|
|
209
482
|
});
|
|
210
483
|
};
|