pi-advisor-flow 0.9.2-dev.5 → 0.10.1-dev.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/CHANGELOG.md +10 -0
- package/README.md +40 -5
- package/dist/index.js +464 -86
- package/package.json +2 -2
- package/src/commands/activation-preparation.ts +4 -0
- package/src/commands/manual-consultation.ts +41 -29
- package/src/commands/model-commands.ts +5 -1
- package/src/commands/model-picker.ts +44 -2
- package/src/commands/runtime.ts +9 -1
- package/src/commands/settings-persistence.ts +2 -0
- package/src/commands/types.ts +2 -0
- package/src/config/defaults.ts +6 -0
- package/src/config/schema.ts +7 -0
- package/src/config/state.ts +5 -0
- package/src/config/types.ts +1 -0
- package/src/config.ts +2 -0
- package/src/follow-up.ts +85 -0
- package/src/session-state.ts +33 -0
- package/src/tools/advisor-output.ts +28 -0
- package/src/tools/consultation.ts +311 -41
- package/src/tools/model-access.ts +7 -3
- package/src/tools/register-ask-advisor.ts +133 -31
- package/src/tools/register-lifecycle.ts +11 -0
- package/src/tools/render-advisor-result.ts +3 -0
- package/src/tools/render-common.ts +6 -2
- package/src/tools/types.ts +2 -0
- package/src/ui/settings-items.ts +36 -0
- package/src/ui/settings-mutations.ts +8 -0
- package/src/ui/settings-selector.ts +9 -0
- package/src/ui/types.ts +3 -0
|
@@ -5,10 +5,13 @@ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
advisorEffortRef,
|
|
8
|
+
advisorFallbackModelRef,
|
|
8
9
|
advisorRedactSecretsRef,
|
|
9
10
|
advisorRef,
|
|
11
|
+
getAdvisorSettings,
|
|
10
12
|
} from "../config/state.ts";
|
|
11
13
|
import { loadConfig } from "../config/storage.ts";
|
|
14
|
+
import type { AdvisorFollowUpPayload } from "../follow-up.ts";
|
|
12
15
|
import { escapeRepositoryText } from "../git.ts";
|
|
13
16
|
import type { GitContextLevel } from "../git.ts";
|
|
14
17
|
import { collectTextStream, resolveConfiguredModel } from "../model-stream.ts";
|
|
@@ -20,6 +23,7 @@ import type { ConsultationContext } from "./consult-context.ts";
|
|
|
20
23
|
import { parseAutomaticDecision } from "./gate-protocol.ts";
|
|
21
24
|
import {
|
|
22
25
|
advisorModelAccessReason,
|
|
26
|
+
fallbackSameModelAdvisorNotice,
|
|
23
27
|
sameModelAdvisorDisabled,
|
|
24
28
|
sameModelAdvisorNotice,
|
|
25
29
|
} from "./model-access.ts";
|
|
@@ -41,6 +45,17 @@ class AdvisorNoAdviceError extends Error {
|
|
|
41
45
|
}
|
|
42
46
|
}
|
|
43
47
|
|
|
48
|
+
export interface AdvisorFollowUpRequest {
|
|
49
|
+
adviceId: string;
|
|
50
|
+
payload: AdvisorFollowUpPayload;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type AdvisorFollowUpCapture = (
|
|
54
|
+
adviceId: string,
|
|
55
|
+
payload: AdvisorFollowUpPayload,
|
|
56
|
+
replacesAdviceId?: string
|
|
57
|
+
) => void;
|
|
58
|
+
|
|
44
59
|
interface CollectAdvisorResponseOptions {
|
|
45
60
|
ctx: ExtensionContext;
|
|
46
61
|
currentInvocationId?: string;
|
|
@@ -53,8 +68,22 @@ interface CollectAdvisorResponseOptions {
|
|
|
53
68
|
question?: string;
|
|
54
69
|
signal?: AbortSignal;
|
|
55
70
|
systemPrompt: string;
|
|
71
|
+
followUp?: AdvisorFollowUpRequest;
|
|
56
72
|
}
|
|
57
73
|
|
|
74
|
+
interface PreparedAdvisorRequest {
|
|
75
|
+
context?: ConsultationContext;
|
|
76
|
+
messages: Message[];
|
|
77
|
+
privacyKey: string;
|
|
78
|
+
redacted: boolean;
|
|
79
|
+
systemPrompt: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
type CollectedAdvisorResponse = Omit<
|
|
83
|
+
AdvisorConsultationResult,
|
|
84
|
+
"adviceId" | "trigger"
|
|
85
|
+
> & { followUpPayload: AdvisorFollowUpPayload };
|
|
86
|
+
|
|
58
87
|
const fileTag = (item: { path: string; text: string }) =>
|
|
59
88
|
`<file path=${JSON.stringify(item.path)}>\n${item.text}\n</file>`;
|
|
60
89
|
|
|
@@ -86,26 +115,79 @@ const advisorImageNotice = (context: ConsultationContext) => {
|
|
|
86
115
|
return `\n\nImage disclosure: ${disclosure}. Only the images explicitly attached below have pixels available. Other image markers, image paths, and text descriptions are not visual evidence.`;
|
|
87
116
|
};
|
|
88
117
|
|
|
89
|
-
const
|
|
90
|
-
|
|
118
|
+
const advisorPrivacyKey = (ctx: ExtensionContext) => {
|
|
119
|
+
const settings = getAdvisorSettings();
|
|
120
|
+
return JSON.stringify({
|
|
121
|
+
agentsMdContext: settings.agentsMdContext,
|
|
122
|
+
contextMaxChars: settings.contextMaxChars,
|
|
123
|
+
cwd: ctx.cwd,
|
|
124
|
+
gitContext: settings.gitContext,
|
|
125
|
+
gitContextMaxChars: settings.gitContextMaxChars,
|
|
126
|
+
projectTrusted: ctx.isProjectTrusted(),
|
|
127
|
+
redactSecrets: settings.redactSecrets,
|
|
128
|
+
scoutEnabled: settings.scoutEnabled,
|
|
129
|
+
toolPolicies: settings.toolPolicies,
|
|
130
|
+
toolResultMaxBytes: settings.toolResultMaxBytes,
|
|
131
|
+
toolResultMaxLines: settings.toolResultMaxLines,
|
|
132
|
+
trackedFileContent: settings.trackedFileContent,
|
|
133
|
+
untrackedContent: settings.untrackedContent,
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export const assertAdvisorFollowUpPrivacy = (
|
|
138
|
+
payload: AdvisorFollowUpPayload,
|
|
139
|
+
ctx: ExtensionContext
|
|
91
140
|
) => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
141
|
+
if (payload.privacyKey !== advisorPrivacyKey(ctx)) {
|
|
142
|
+
throw new Error(
|
|
143
|
+
"Follow-up consultation unavailable: privacy or disclosure settings changed after the original consultation. Issue a fresh consultation."
|
|
144
|
+
);
|
|
96
145
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const prepareFollowUp = (
|
|
149
|
+
followUp: AdvisorFollowUpRequest,
|
|
150
|
+
question: string | undefined,
|
|
151
|
+
ctx: ExtensionContext
|
|
152
|
+
): PreparedAdvisorRequest => {
|
|
153
|
+
if (!question?.trim()) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
"Follow-up consultations require a non-empty question. Issue a fresh consultation if no question is needed."
|
|
156
|
+
);
|
|
100
157
|
}
|
|
101
|
-
|
|
158
|
+
assertAdvisorFollowUpPrivacy(followUp.payload, ctx);
|
|
159
|
+
const outboundQuestion =
|
|
160
|
+
advisorRedactSecretsRef || followUp.payload.redacted
|
|
161
|
+
? redactSecrets(question)
|
|
162
|
+
: question;
|
|
163
|
+
const messages = structuredClone(followUp.payload.messages);
|
|
164
|
+
messages.push({
|
|
165
|
+
content: [
|
|
166
|
+
{
|
|
167
|
+
text: `Follow-up focus:\n${outboundQuestion}`,
|
|
168
|
+
type: "text",
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
role: "user",
|
|
172
|
+
timestamp: Date.now(),
|
|
173
|
+
});
|
|
174
|
+
return {
|
|
175
|
+
messages,
|
|
176
|
+
privacyKey: followUp.payload.privacyKey,
|
|
177
|
+
redacted: advisorRedactSecretsRef || followUp.payload.redacted,
|
|
178
|
+
systemPrompt: followUp.payload.systemPrompt,
|
|
179
|
+
};
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const prepareFreshRequest = async (
|
|
183
|
+
options: CollectAdvisorResponseOptions,
|
|
184
|
+
resolved: Awaited<ReturnType<typeof resolveConfiguredModel>>
|
|
185
|
+
): Promise<PreparedAdvisorRequest> => {
|
|
186
|
+
const { question } = options;
|
|
102
187
|
const context = await assembleConsultationContext({
|
|
103
188
|
...options,
|
|
104
189
|
supportsImages: resolved.model.input.includes("image"),
|
|
105
190
|
});
|
|
106
|
-
|
|
107
|
-
// Keep the original question available to local UI callers, but never send
|
|
108
|
-
// its credential-shaped values to the provider when redaction is enabled.
|
|
109
191
|
const outboundQuestion =
|
|
110
192
|
advisorRedactSecretsRef && question !== undefined
|
|
111
193
|
? redactSecrets(question)
|
|
@@ -139,45 +221,227 @@ const collectAdvisorResponse = async (
|
|
|
139
221
|
timestamp: Date.now(),
|
|
140
222
|
},
|
|
141
223
|
];
|
|
224
|
+
return {
|
|
225
|
+
context,
|
|
226
|
+
messages,
|
|
227
|
+
privacyKey: advisorPrivacyKey(options.ctx),
|
|
228
|
+
redacted: advisorRedactSecretsRef,
|
|
229
|
+
systemPrompt: options.systemPrompt,
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const combinedFallbackError = (
|
|
234
|
+
primaryModel: string,
|
|
235
|
+
primaryError: Error,
|
|
236
|
+
fallbackModel: string,
|
|
237
|
+
fallbackError: Error
|
|
238
|
+
) =>
|
|
239
|
+
new Error(
|
|
240
|
+
`Advisor model ${primaryModel} failed: ${primaryError.message}. Fallback Advisor model ${fallbackModel} failed: ${fallbackError.message}.`,
|
|
241
|
+
{ cause: fallbackError }
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
const advisorContextDetails = (context: ConsultationContext | undefined) => ({
|
|
245
|
+
agentRulesBytes: context?.projectRules?.bytes || undefined,
|
|
246
|
+
draftBytes: context?.draftText
|
|
247
|
+
? Buffer.byteLength(context.draftText, "utf-8")
|
|
248
|
+
: undefined,
|
|
249
|
+
imageBytes:
|
|
250
|
+
context?.images.reduce(
|
|
251
|
+
(sum, item) => sum + Buffer.from(item.image.data, "base64").length,
|
|
252
|
+
0
|
|
253
|
+
) || undefined,
|
|
254
|
+
imageCount: context?.images.length,
|
|
255
|
+
imageOmissions: context?.imageOmissions || undefined,
|
|
256
|
+
imagePartsSeen: context?.imagePartsSeen || undefined,
|
|
257
|
+
preferenceBytes: context?.preferences?.bytes,
|
|
258
|
+
scout: context?.scout,
|
|
259
|
+
trackedBytes:
|
|
260
|
+
context?.tracked.reduce((sum, item) => sum + item.bytes, 0) || undefined,
|
|
261
|
+
untrackedBytes:
|
|
262
|
+
context?.untracked.reduce((sum, item) => sum + item.bytes, 0) || undefined,
|
|
263
|
+
});
|
|
142
264
|
|
|
265
|
+
const runAdvisorAttempt = async (
|
|
266
|
+
options: CollectAdvisorResponseOptions,
|
|
267
|
+
prepared: PreparedAdvisorRequest,
|
|
268
|
+
resolved: Awaited<ReturnType<typeof resolveConfiguredModel>>,
|
|
269
|
+
modelRef: string
|
|
270
|
+
): Promise<CollectedAdvisorResponse> => {
|
|
143
271
|
const streamed = await collectTextStream(resolved, {
|
|
144
|
-
messages,
|
|
272
|
+
messages: structuredClone(prepared.messages),
|
|
145
273
|
onChunk: options.onChunk,
|
|
146
274
|
reasoning: advisorEffortRef,
|
|
147
|
-
signal,
|
|
148
|
-
systemPrompt,
|
|
275
|
+
signal: options.signal,
|
|
276
|
+
systemPrompt: prepared.systemPrompt,
|
|
149
277
|
});
|
|
150
|
-
const markdown = streamed
|
|
278
|
+
const { text: markdown } = streamed;
|
|
151
279
|
if (!markdown.trim()) {
|
|
152
280
|
throw new AdvisorNoAdviceError();
|
|
153
281
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
:
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
imageOmissions: context.imageOmissions || undefined,
|
|
166
|
-
imagePartsSeen: context.imagePartsSeen || undefined,
|
|
282
|
+
return {
|
|
283
|
+
...advisorContextDetails(prepared.context),
|
|
284
|
+
followUp: Boolean(options.followUp),
|
|
285
|
+
followUpPayload: {
|
|
286
|
+
depth: options.followUp ? options.followUp.payload.depth + 1 : 0,
|
|
287
|
+
messages: structuredClone(prepared.messages),
|
|
288
|
+
model: modelRef,
|
|
289
|
+
privacyKey: prepared.privacyKey,
|
|
290
|
+
redacted: prepared.redacted,
|
|
291
|
+
systemPrompt: prepared.systemPrompt,
|
|
292
|
+
},
|
|
167
293
|
markdown,
|
|
168
|
-
model:
|
|
169
|
-
preferenceBytes: context.preferences?.bytes,
|
|
294
|
+
model: modelRef,
|
|
170
295
|
thinkingText: streamed.thinking,
|
|
171
|
-
trackedBytes:
|
|
172
|
-
context.tracked.reduce((sum, item) => sum + item.bytes, 0) || undefined,
|
|
173
|
-
untrackedBytes:
|
|
174
|
-
context.untracked.reduce((sum, item) => sum + item.bytes, 0) || undefined,
|
|
175
296
|
usage: streamed.usage,
|
|
176
297
|
};
|
|
177
|
-
|
|
178
|
-
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const runFallbackAdvisorAttempt = async (
|
|
301
|
+
options: CollectAdvisorResponseOptions,
|
|
302
|
+
primaryModel: string,
|
|
303
|
+
primaryError: Error,
|
|
304
|
+
prepared: PreparedAdvisorRequest | undefined
|
|
305
|
+
): Promise<CollectedAdvisorResponse> => {
|
|
306
|
+
const { ctx } = options;
|
|
307
|
+
let request = prepared;
|
|
308
|
+
if (!advisorFallbackModelRef) {
|
|
309
|
+
throw primaryError;
|
|
310
|
+
}
|
|
311
|
+
const fallbackModel = advisorFallbackModelRef;
|
|
312
|
+
if (fallbackModel === primaryModel) {
|
|
313
|
+
throw combinedFallbackError(
|
|
314
|
+
primaryModel,
|
|
315
|
+
primaryError,
|
|
316
|
+
fallbackModel,
|
|
317
|
+
new Error("fallback is the same model as the primary Advisor")
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
if (sameModelAdvisorDisabled(ctx, ctx.model, fallbackModel)) {
|
|
321
|
+
throw combinedFallbackError(
|
|
322
|
+
primaryModel,
|
|
323
|
+
primaryError,
|
|
324
|
+
fallbackModel,
|
|
325
|
+
new Error(fallbackSameModelAdvisorNotice)
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
const fallbackAccessReason = advisorModelAccessReason(ctx);
|
|
329
|
+
if (fallbackAccessReason) {
|
|
330
|
+
throw combinedFallbackError(
|
|
331
|
+
primaryModel,
|
|
332
|
+
primaryError,
|
|
333
|
+
fallbackModel,
|
|
334
|
+
new Error(fallbackAccessReason)
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
let resolvedFallback: Awaited<ReturnType<typeof resolveConfiguredModel>>;
|
|
338
|
+
try {
|
|
339
|
+
resolvedFallback = await resolveConfiguredModel(
|
|
340
|
+
ctx,
|
|
341
|
+
fallbackModel,
|
|
342
|
+
"Advisor fallback"
|
|
343
|
+
);
|
|
344
|
+
} catch (error) {
|
|
345
|
+
throw combinedFallbackError(
|
|
346
|
+
primaryModel,
|
|
347
|
+
primaryError,
|
|
348
|
+
fallbackModel,
|
|
349
|
+
error instanceof Error ? error : new Error(String(error))
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
if (!request) {
|
|
353
|
+
try {
|
|
354
|
+
request = options.followUp
|
|
355
|
+
? prepareFollowUp(options.followUp, options.question, options.ctx)
|
|
356
|
+
: await prepareFreshRequest(options, resolvedFallback);
|
|
357
|
+
} catch (error) {
|
|
358
|
+
throw combinedFallbackError(
|
|
359
|
+
primaryModel,
|
|
360
|
+
primaryError,
|
|
361
|
+
fallbackModel,
|
|
362
|
+
error instanceof Error ? error : new Error(String(error))
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
try {
|
|
367
|
+
return await runAdvisorAttempt(
|
|
368
|
+
options,
|
|
369
|
+
request,
|
|
370
|
+
resolvedFallback,
|
|
371
|
+
fallbackModel
|
|
372
|
+
);
|
|
373
|
+
} catch (error) {
|
|
374
|
+
if (options.signal?.aborted) {
|
|
375
|
+
throw error;
|
|
376
|
+
}
|
|
377
|
+
throw combinedFallbackError(
|
|
378
|
+
primaryModel,
|
|
379
|
+
primaryError,
|
|
380
|
+
fallbackModel,
|
|
381
|
+
error instanceof Error ? error : new Error(String(error))
|
|
382
|
+
);
|
|
179
383
|
}
|
|
180
|
-
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
const collectAdvisorResponse = async (
|
|
387
|
+
options: CollectAdvisorResponseOptions
|
|
388
|
+
): Promise<CollectedAdvisorResponse> => {
|
|
389
|
+
const { ctx, signal } = options;
|
|
390
|
+
loadConfig(ctx);
|
|
391
|
+
const primaryModel = options.followUp?.payload.model ?? advisorRef;
|
|
392
|
+
if (sameModelAdvisorDisabled(ctx, ctx.model, primaryModel)) {
|
|
393
|
+
throw new Error(
|
|
394
|
+
primaryModel === advisorRef
|
|
395
|
+
? sameModelAdvisorNotice
|
|
396
|
+
: fallbackSameModelAdvisorNotice
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
const accessReason = advisorModelAccessReason(ctx);
|
|
400
|
+
if (accessReason) {
|
|
401
|
+
throw new Error(accessReason);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
let primaryError: Error | undefined;
|
|
405
|
+
let prepared: PreparedAdvisorRequest | undefined;
|
|
406
|
+
let resolvedPrimary:
|
|
407
|
+
| Awaited<ReturnType<typeof resolveConfiguredModel>>
|
|
408
|
+
| undefined;
|
|
409
|
+
try {
|
|
410
|
+
resolvedPrimary = await resolveConfiguredModel(
|
|
411
|
+
ctx,
|
|
412
|
+
primaryModel,
|
|
413
|
+
"Advisor"
|
|
414
|
+
);
|
|
415
|
+
} catch (error) {
|
|
416
|
+
primaryError = error instanceof Error ? error : new Error(String(error));
|
|
417
|
+
}
|
|
418
|
+
if (resolvedPrimary) {
|
|
419
|
+
prepared = options.followUp
|
|
420
|
+
? prepareFollowUp(options.followUp, options.question, options.ctx)
|
|
421
|
+
: await prepareFreshRequest(options, resolvedPrimary);
|
|
422
|
+
try {
|
|
423
|
+
return await runAdvisorAttempt(
|
|
424
|
+
options,
|
|
425
|
+
prepared,
|
|
426
|
+
resolvedPrimary,
|
|
427
|
+
primaryModel
|
|
428
|
+
);
|
|
429
|
+
} catch (error) {
|
|
430
|
+
primaryError = error instanceof Error ? error : new Error(String(error));
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
if (!primaryError) {
|
|
434
|
+
throw new Error("Advisor request failed before an error was recorded.");
|
|
435
|
+
}
|
|
436
|
+
if (signal?.aborted) {
|
|
437
|
+
throw primaryError;
|
|
438
|
+
}
|
|
439
|
+
return runFallbackAdvisorAttempt(
|
|
440
|
+
options,
|
|
441
|
+
primaryModel,
|
|
442
|
+
primaryError,
|
|
443
|
+
prepared
|
|
444
|
+
);
|
|
181
445
|
};
|
|
182
446
|
|
|
183
447
|
export const consultAdvisor = async (
|
|
@@ -191,12 +455,15 @@ export const consultAdvisor = async (
|
|
|
191
455
|
includeUntracked?: string[],
|
|
192
456
|
includeTracked?: string[],
|
|
193
457
|
onScout?: (event: ScoutLifecycleEvent) => void,
|
|
194
|
-
currentInvocationId?: string
|
|
458
|
+
currentInvocationId?: string,
|
|
459
|
+
followUp?: AdvisorFollowUpRequest,
|
|
460
|
+
onFollowUpPayload?: AdvisorFollowUpCapture
|
|
195
461
|
): Promise<AdvisorConsultationResult> => {
|
|
196
462
|
const result = await collectAdvisorResponse({
|
|
197
463
|
ctx,
|
|
198
464
|
currentInvocationId,
|
|
199
465
|
draft,
|
|
466
|
+
followUp,
|
|
200
467
|
gitContext,
|
|
201
468
|
includeTracked,
|
|
202
469
|
includeUntracked,
|
|
@@ -206,7 +473,10 @@ export const consultAdvisor = async (
|
|
|
206
473
|
signal,
|
|
207
474
|
systemPrompt: ADVISOR_SYSTEM,
|
|
208
475
|
});
|
|
209
|
-
|
|
476
|
+
const adviceId = randomUUID();
|
|
477
|
+
const { followUpPayload, ...publicResult } = result;
|
|
478
|
+
onFollowUpPayload?.(adviceId, followUpPayload, followUp?.adviceId);
|
|
479
|
+
return { ...publicResult, adviceId, trigger };
|
|
210
480
|
};
|
|
211
481
|
|
|
212
482
|
export const runAdvisorGate = async (
|
|
@@ -41,17 +41,21 @@ export const advisorModelAccess = (
|
|
|
41
41
|
|
|
42
42
|
export const sameModelAdvisorDisabled = (
|
|
43
43
|
ctx: Pick<ExtensionContext, "model">,
|
|
44
|
-
model: { id: string; provider: string } | undefined = ctx.model
|
|
44
|
+
model: { id: string; provider: string } | undefined = ctx.model,
|
|
45
|
+
advisorModelRef: string | undefined = advisorRef
|
|
45
46
|
): boolean => {
|
|
46
|
-
|
|
47
|
+
const selectedAdvisorRef = advisorModelRef ?? advisorRef;
|
|
48
|
+
if (!(advisorDisableSameModelRef && selectedAdvisorRef && model)) {
|
|
47
49
|
return false;
|
|
48
50
|
}
|
|
49
|
-
const [provider, id] = splitRef(
|
|
51
|
+
const [provider, id] = splitRef(selectedAdvisorRef);
|
|
50
52
|
return provider === model.provider && id === model.id;
|
|
51
53
|
};
|
|
52
54
|
|
|
53
55
|
export const sameModelAdvisorNotice =
|
|
54
56
|
"Advisor disabled: executor and advisor are the same model.";
|
|
57
|
+
export const fallbackSameModelAdvisorNotice =
|
|
58
|
+
"Advisor fallback skipped: executor and fallback Advisor are the same model.";
|
|
55
59
|
|
|
56
60
|
export const advisorModelIsAllowed = (
|
|
57
61
|
ctx: Pick<ExtensionContext, "model">
|