@juicesharp/rpiv-advisor 1.6.0 → 1.7.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/advisor.ts +95 -14
- package/index.ts +5 -3
- package/package.json +1 -1
package/advisor.ts
CHANGED
|
@@ -22,10 +22,10 @@ import { completeSimple, getSupportedThinkingLevels, type Message, type Thinking
|
|
|
22
22
|
import {
|
|
23
23
|
type AgentToolResult,
|
|
24
24
|
type AgentToolUpdateCallback,
|
|
25
|
+
buildSessionContext,
|
|
25
26
|
convertToLlm,
|
|
26
27
|
type ExtensionAPI,
|
|
27
28
|
type ExtensionContext,
|
|
28
|
-
type SessionEntry,
|
|
29
29
|
type ToolInfo,
|
|
30
30
|
} from "@earendil-works/pi-coding-agent";
|
|
31
31
|
import type { SelectItem } from "@earendil-works/pi-tui";
|
|
@@ -84,6 +84,10 @@ const msgAdvisorEnabled = (label: string, effort: ThinkingLevel | undefined) =>
|
|
|
84
84
|
`Advisor: ${label}${effort ? `, ${effort}` : ""}`;
|
|
85
85
|
const msgAdvisorRestored = (label: string, effort: ThinkingLevel | undefined) =>
|
|
86
86
|
`Advisor restored: ${label}${effort ? `, ${effort}` : ""}`;
|
|
87
|
+
const msgAdvisorRestoredInactive = (label: string, effort: ThinkingLevel | undefined) =>
|
|
88
|
+
`Advisor restored: ${label}${effort ? `, ${effort}` : ""} (inactive for current executor)`;
|
|
89
|
+
const msgAdvisorEnabledInactive = (label: string, effort: ThinkingLevel | undefined) =>
|
|
90
|
+
`Advisor: ${label}${effort ? `, ${effort}` : ""} (inactive for current executor)`;
|
|
87
91
|
const msgConsulting = (label: string, effort: ThinkingLevel | undefined) =>
|
|
88
92
|
`Consulting advisor (${label}${effort ? `, ${effort}` : ""})…`;
|
|
89
93
|
|
|
@@ -100,6 +104,7 @@ interface AdvisorConfig {
|
|
|
100
104
|
modelKey?: string;
|
|
101
105
|
effort?: ThinkingLevel;
|
|
102
106
|
guidance?: GuidanceFields;
|
|
107
|
+
disabledForModels?: string[];
|
|
103
108
|
}
|
|
104
109
|
|
|
105
110
|
export function loadAdvisorConfig(): AdvisorConfig {
|
|
@@ -128,6 +133,11 @@ function validateGuidanceFields(fields: unknown): GuidanceFields {
|
|
|
128
133
|
return result;
|
|
129
134
|
}
|
|
130
135
|
|
|
136
|
+
function validateDisabledForModels(value: unknown): string[] {
|
|
137
|
+
if (!Array.isArray(value)) return [];
|
|
138
|
+
return value.filter((entry): entry is string => typeof entry === "string" && entry.length > 0);
|
|
139
|
+
}
|
|
140
|
+
|
|
131
141
|
export function saveAdvisorConfig(key: string | undefined, effort: ThinkingLevel | undefined): void {
|
|
132
142
|
const existing = loadAdvisorConfig();
|
|
133
143
|
const config: AdvisorConfig = { ...existing };
|
|
@@ -297,12 +307,21 @@ export function setAdvisorEffort(effort: ThinkingLevel | undefined): void {
|
|
|
297
307
|
selectedAdvisorEffort = effort;
|
|
298
308
|
}
|
|
299
309
|
|
|
310
|
+
let disabledForModelsCache: string[] = [];
|
|
311
|
+
|
|
312
|
+
export function setDisabledForModels(models: string[]): void {
|
|
313
|
+
disabledForModelsCache = models;
|
|
314
|
+
}
|
|
315
|
+
|
|
300
316
|
// ---------------------------------------------------------------------------
|
|
301
317
|
// Session restoration — called from index.ts session_start handler
|
|
302
318
|
// ---------------------------------------------------------------------------
|
|
303
319
|
|
|
304
320
|
export function restoreAdvisorState(ctx: ExtensionContext, pi: ExtensionAPI): void {
|
|
305
321
|
const config = loadAdvisorConfig();
|
|
322
|
+
|
|
323
|
+
setDisabledForModels(validateDisabledForModels(config.disabledForModels));
|
|
324
|
+
|
|
306
325
|
if (!config.modelKey) return;
|
|
307
326
|
|
|
308
327
|
const parsed = parseModelKey(config.modelKey);
|
|
@@ -321,6 +340,14 @@ export function restoreAdvisorState(ctx: ExtensionContext, pi: ExtensionAPI): vo
|
|
|
321
340
|
setAdvisorEffort(config.effort);
|
|
322
341
|
}
|
|
323
342
|
|
|
343
|
+
if (isExecutorBlocked(ctx)) {
|
|
344
|
+
if (ctx.hasUI) {
|
|
345
|
+
const advisorLabel = `${model.provider}:${model.id}`;
|
|
346
|
+
ctx.ui.notify(msgAdvisorRestoredInactive(advisorLabel, config.effort), "info");
|
|
347
|
+
}
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
324
351
|
const active = pi.getActiveTools();
|
|
325
352
|
if (!active.includes(ADVISOR_TOOL_NAME)) {
|
|
326
353
|
pi.setActiveTools([...active, ADVISOR_TOOL_NAME]);
|
|
@@ -377,14 +404,16 @@ async function executeAdvisor(
|
|
|
377
404
|
}
|
|
378
405
|
|
|
379
406
|
// Live-read every call — advisor runs mid-turn so any message_end snapshot
|
|
380
|
-
// is always one turn stale.
|
|
381
|
-
//
|
|
382
|
-
//
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
.
|
|
387
|
-
|
|
407
|
+
// is always one turn stale. buildSessionContext() preserves Pi's resolved
|
|
408
|
+
// LLM context, including compaction summaries and branch summaries, instead
|
|
409
|
+
// of replaying raw pre-compaction branch messages. convertToLlm is
|
|
410
|
+
// pass-through for user/assistant/toolResult (messages.js:111-114), so
|
|
411
|
+
// element refs are stable across calls via the session store.
|
|
412
|
+
const { messages: sessionMessages } = buildSessionContext(
|
|
413
|
+
ctx.sessionManager.getEntries(),
|
|
414
|
+
ctx.sessionManager.getLeafId(),
|
|
415
|
+
);
|
|
416
|
+
const branchMessages = ensureUserTailForAdvisor(stripInflightAdvisorCall(convertToLlm(sessionMessages)));
|
|
388
417
|
const inventoryMessage = getInventoryMessage(pi.getAllTools());
|
|
389
418
|
const messages: Message[] = inventoryMessage ? [inventoryMessage, ...branchMessages] : branchMessages;
|
|
390
419
|
|
|
@@ -509,8 +538,9 @@ export function registerAdvisorTool(pi: ExtensionAPI): void {
|
|
|
509
538
|
// ---------------------------------------------------------------------------
|
|
510
539
|
|
|
511
540
|
export function registerAdvisorBeforeAgentStart(pi: ExtensionAPI): void {
|
|
512
|
-
pi.on("before_agent_start", async () => {
|
|
513
|
-
|
|
541
|
+
pi.on("before_agent_start", async (_event, ctx) => {
|
|
542
|
+
const shouldStrip = !getAdvisorModel() || isExecutorBlocked(ctx);
|
|
543
|
+
if (shouldStrip) {
|
|
514
544
|
const active = pi.getActiveTools();
|
|
515
545
|
if (active.includes(ADVISOR_TOOL_NAME)) {
|
|
516
546
|
pi.setActiveTools(active.filter((n) => n !== ADVISOR_TOOL_NAME));
|
|
@@ -519,6 +549,38 @@ export function registerAdvisorBeforeAgentStart(pi: ExtensionAPI): void {
|
|
|
519
549
|
});
|
|
520
550
|
}
|
|
521
551
|
|
|
552
|
+
// ---------------------------------------------------------------------------
|
|
553
|
+
// model_select handler — mid-session model switches strip/re-add advisor
|
|
554
|
+
// ---------------------------------------------------------------------------
|
|
555
|
+
|
|
556
|
+
export function registerModelSelectHandler(pi: ExtensionAPI): void {
|
|
557
|
+
pi.on("model_select", async (event, ctx) => {
|
|
558
|
+
// session_start restore path is owned by restoreAdvisorState — it already
|
|
559
|
+
// activates the tool and notifies. Skipping "restore" here prevents a
|
|
560
|
+
// duplicate notification on initial model load.
|
|
561
|
+
if (event.source === "restore") return;
|
|
562
|
+
|
|
563
|
+
const advisor = getAdvisorModel();
|
|
564
|
+
if (!advisor) return;
|
|
565
|
+
|
|
566
|
+
const blocked = isModelBlocked(event.model);
|
|
567
|
+
const active = pi.getActiveTools();
|
|
568
|
+
const hasTool = active.includes(ADVISOR_TOOL_NAME);
|
|
569
|
+
|
|
570
|
+
if (blocked && hasTool) {
|
|
571
|
+
pi.setActiveTools(active.filter((n) => n !== ADVISOR_TOOL_NAME));
|
|
572
|
+
if (ctx.hasUI) {
|
|
573
|
+
ctx.ui.notify(`Advisor disabled for ${modelKey(event.model)}`, "info");
|
|
574
|
+
}
|
|
575
|
+
} else if (!blocked && !hasTool) {
|
|
576
|
+
pi.setActiveTools([...active, ADVISOR_TOOL_NAME]);
|
|
577
|
+
if (ctx.hasUI) {
|
|
578
|
+
ctx.ui.notify(msgAdvisorRestored(modelKey(advisor), getAdvisorEffort()), "info");
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
|
|
522
584
|
// ---------------------------------------------------------------------------
|
|
523
585
|
// /advisor slash command — opens selector panel for picking the advisor model
|
|
524
586
|
// ---------------------------------------------------------------------------
|
|
@@ -527,6 +589,15 @@ function modelKey(m: { provider: string; id: string }): string {
|
|
|
527
589
|
return `${m.provider}:${m.id}`;
|
|
528
590
|
}
|
|
529
591
|
|
|
592
|
+
function isModelBlocked(model: Model<Api> | undefined): boolean {
|
|
593
|
+
if (!model) return false;
|
|
594
|
+
return disabledForModelsCache.includes(modelKey(model));
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function isExecutorBlocked(ctx: ExtensionContext): boolean {
|
|
598
|
+
return isModelBlocked(ctx?.model);
|
|
599
|
+
}
|
|
600
|
+
|
|
530
601
|
export function registerAdvisorCommand(pi: ExtensionAPI): void {
|
|
531
602
|
pi.registerCommand("advisor", {
|
|
532
603
|
description: "Configure the advisor model for the advisor-strategy pattern",
|
|
@@ -600,10 +671,20 @@ export function registerAdvisorCommand(pi: ExtensionAPI): void {
|
|
|
600
671
|
setAdvisorEffort(effortChoice);
|
|
601
672
|
setAdvisorModel(picked);
|
|
602
673
|
saveAdvisorConfig(modelKey(picked), effortChoice);
|
|
603
|
-
|
|
604
|
-
|
|
674
|
+
|
|
675
|
+
// Re-read after the effort-picker await — the snapshot taken before
|
|
676
|
+
// `showEffortPicker` is stale once execution yields.
|
|
677
|
+
const activeToolsNow = pi.getActiveTools();
|
|
678
|
+
const activeHasNow = activeToolsNow.includes(ADVISOR_TOOL_NAME);
|
|
679
|
+
const blocked = isExecutorBlocked(ctx);
|
|
680
|
+
if (!activeHasNow && !blocked) {
|
|
681
|
+
pi.setActiveTools([...activeToolsNow, ADVISOR_TOOL_NAME]);
|
|
682
|
+
}
|
|
683
|
+
if (blocked) {
|
|
684
|
+
ctx.ui.notify(msgAdvisorEnabledInactive(modelKey(picked), effortChoice), "info");
|
|
685
|
+
} else {
|
|
686
|
+
ctx.ui.notify(msgAdvisorEnabled(modelKey(picked), effortChoice), "info");
|
|
605
687
|
}
|
|
606
|
-
ctx.ui.notify(msgAdvisorEnabled(modelKey(picked), effortChoice), "info");
|
|
607
688
|
},
|
|
608
689
|
});
|
|
609
690
|
}
|
package/index.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* rpiv-advisor — Pi extension
|
|
3
3
|
*
|
|
4
|
-
* Registers the `advisor` tool, `/advisor` command, and the
|
|
5
|
-
* hooks (session_start restore, before_agent_start strip
|
|
6
|
-
* implement the advisor-strategy pattern.
|
|
4
|
+
* Registers the `advisor` tool, `/advisor` command, and the three lifecycle
|
|
5
|
+
* hooks (session_start restore, before_agent_start strip, model_select
|
|
6
|
+
* re-evaluation) that together implement the advisor-strategy pattern.
|
|
7
7
|
*
|
|
8
8
|
* Config persists at ~/.config/rpiv-advisor/advisor.json. Tool name
|
|
9
9
|
* preserved verbatim from rpiv-pi@7525a5d.
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
registerAdvisorBeforeAgentStart,
|
|
15
15
|
registerAdvisorCommand,
|
|
16
16
|
registerAdvisorTool,
|
|
17
|
+
registerModelSelectHandler,
|
|
17
18
|
restoreAdvisorState,
|
|
18
19
|
} from "./advisor.js";
|
|
19
20
|
|
|
@@ -21,6 +22,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
21
22
|
registerAdvisorTool(pi);
|
|
22
23
|
registerAdvisorCommand(pi);
|
|
23
24
|
registerAdvisorBeforeAgentStart(pi);
|
|
25
|
+
registerModelSelectHandler(pi);
|
|
24
26
|
|
|
25
27
|
pi.on("session_start", async (_event, ctx) => {
|
|
26
28
|
restoreAdvisorState(ctx, pi);
|