@bacnh85/pi-advisor 0.2.4 → 0.3.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 +20 -0
- package/README.md +11 -7
- package/extensions/commands/advisor.ts +8 -8
- package/extensions/lib/config.ts +14 -0
- package/extensions/lib/isolated-model.ts +8 -3
- package/extensions/lib/model-picker.ts +14 -2
- package/extensions/lib/models-panel.ts +46 -19
- package/extensions/lib/watcher.ts +5 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0 (2026-09-13)
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Per-slot thinking levels.** A trailing `:level`
|
|
8
|
+
(`minimal|low|medium|high|xhigh|max`) on a `pi-advisor.models` entry pins
|
|
9
|
+
that candidate's thinking; it rides into the stream call as
|
|
10
|
+
`SimpleStreamOptions.reasoning`. Entries without a suffix (and `:off`)
|
|
11
|
+
keep the provider default, so each fallback in the chain can carry its own
|
|
12
|
+
level. Strict trailing match — openrouter `:free` ids stay intact.
|
|
13
|
+
`/advisor models` gains a thinking row per slot (blank = model default);
|
|
14
|
+
the chain canonicalizer preserves pinned levels on save.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Evidence sizing now strips a trailing `:level` before the registry lookup —
|
|
19
|
+
previously a pinned entry failed `parseModelRef` and the transcript budget
|
|
20
|
+
silently fell back to the 32k default (over-sizing for small-context
|
|
21
|
+
fallback models, whose context errors count toward the 3-strike pause).
|
|
22
|
+
|
|
3
23
|
## 0.2.4 (2026-09-12)
|
|
4
24
|
|
|
5
25
|
### Removed
|
package/README.md
CHANGED
|
@@ -44,7 +44,7 @@ npm install -g @bacnh85/pi-advisor
|
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
46
|
/advisor <provider/model[, …]> # set the chain (one model or comma-separated fallbacks)
|
|
47
|
-
/advisor models # edit the
|
|
47
|
+
/advisor models # edit the chain + per-slot thinking rows (TUI panel; non-TUI prints it)
|
|
48
48
|
/advisor status # model chain, watch state, counters
|
|
49
49
|
/advisor on # enable watch for this session (also clears a pause)
|
|
50
50
|
/advisor watch-off # disable background watch for this session
|
|
@@ -57,18 +57,22 @@ Settings live in `~/.pi/agent/settings.json` (global) and `.pi/settings.json`
|
|
|
57
57
|
```json
|
|
58
58
|
{
|
|
59
59
|
"pi-advisor": {
|
|
60
|
-
"models": ["zai-coding-cn/glm-5.3", "opencode-go/deepseek-v4-pro"],
|
|
60
|
+
"models": ["zai-coding-cn/glm-5.3:high", "opencode-go/deepseek-v4-pro"],
|
|
61
61
|
"watch": { "enabled": true, "minToolCalls": 3, "immuneTurns": 3 }
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
- `models` — ordered fallback chain, first entry is primary. Accepts an array
|
|
67
|
-
or a comma-separated string (`"a/b, c/d"`).
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
or a comma-separated string (`"a/b, c/d"`). A trailing `:level`
|
|
68
|
+
(`minimal|low|medium|high|xhigh|max`) on an entry pins that candidate's
|
|
69
|
+
thinking; entries without one use the model's provider default, so each
|
|
70
|
+
fallback can carry its own level (`:high` on a strong primary, none on a
|
|
71
|
+
flash fallback). Legacy single `model` string is still honored. If the
|
|
72
|
+
primary is rate-limited or unavailable at review/consult time, the next
|
|
73
|
+
candidate serves automatically; a whole-chain failure counts as one review
|
|
74
|
+
failure (the 3-strike pause still applies). The advisor never falls back to
|
|
75
|
+
the primary model — it must never review its own turns.
|
|
72
76
|
- `watch.enabled` (default `true`) — turn-end reviewing on session start (TUI only — print/rpc/json runs skip the watch; the on-demand advisor tool still works)
|
|
73
77
|
- `watch.minToolCalls` (default `3`, `0` = every turn) — skip trivial turns
|
|
74
78
|
- `watch.immuneTurns` (default `3`) — review window during which the same
|
|
@@ -2,7 +2,7 @@ import { buildSessionContext, type ExtensionAPI, type ExtensionContext } from "@
|
|
|
2
2
|
import { fuzzyFilter } from "@earendil-works/pi-tui";
|
|
3
3
|
import { Type } from "typebox";
|
|
4
4
|
import { runIsolatedChain } from "../lib/isolated-model";
|
|
5
|
-
import { chooseModel, exactModel, firstAvailable, modelRef, modelSearchText } from "../lib/model-picker";
|
|
5
|
+
import { canonicalEntry, chooseModel, exactModel, firstAvailable, modelRef, modelSearchText } from "../lib/model-picker";
|
|
6
6
|
import { buildEvidence } from "../lib/watcher";
|
|
7
7
|
import type { WatcherRuntime } from "../lib/watcher";
|
|
8
8
|
|
|
@@ -153,6 +153,7 @@ export function registerAdvisor(pi: ExtensionAPI, state: AdvisorState): void {
|
|
|
153
153
|
"Advisor models (ordered fallback, first = primary):",
|
|
154
154
|
...(models.length > 0 ? models.map((m, i) => ` #${i + 1} ${m}`) : [" (none — advisor inactive)"]),
|
|
155
155
|
"",
|
|
156
|
+
`Append :level (minimal…max) to an entry to pin thinking per slot.`,
|
|
156
157
|
`Edit ~/.pi/agent/settings.json → pi-advisor.models, or run /advisor models in a TUI.`,
|
|
157
158
|
];
|
|
158
159
|
pi.sendMessage({ customType: "pi-advisor", content: lines.join("\n"), display: true });
|
|
@@ -160,10 +161,10 @@ export function registerAdvisor(pi: ExtensionAPI, state: AdvisorState): void {
|
|
|
160
161
|
}
|
|
161
162
|
const working = panel.buildModelsPanelCfg(models);
|
|
162
163
|
const actions: Record<string, { label: string; run: (prompt: (label: string, onDone: (value: string | undefined) => void) => void) => Promise<void> | void }> = {
|
|
163
|
-
addModel: { label: "+ Add model slot", run: () => { working.models.push(""); } },
|
|
164
|
+
addModel: { label: "+ Add model slot", run: () => { working.models.push({ ref: "", thinking: "" }); } },
|
|
164
165
|
removeLast: { label: "− Remove last slot", run: () => {
|
|
165
166
|
const popped = working.models.pop();
|
|
166
|
-
if (popped) ctx.ui.notify(`Removed slot #${working.models.length + 1} ("${popped}" discarded).`, "warning");
|
|
167
|
+
if (popped?.ref) ctx.ui.notify(`Removed slot #${working.models.length + 1} ("${popped.ref}" discarded).`, "warning");
|
|
167
168
|
} },
|
|
168
169
|
};
|
|
169
170
|
const panelOptions = { models: () => (registry?.getAvailable() ?? []).map((m) => modelRef(m)) };
|
|
@@ -174,6 +175,8 @@ export function registerAdvisor(pi: ExtensionAPI, state: AdvisorState): void {
|
|
|
174
175
|
title: "Advisor models (ordered fallback)",
|
|
175
176
|
onSave: (saved) => {
|
|
176
177
|
if (!saved) return;
|
|
178
|
+
const invalid = panel.invalidThinkingSlots(working);
|
|
179
|
+
if (invalid.length > 0) ctx.ui.notify(`Dropped invalid thinking in slot(s): ${invalid.map((i) => `#${i + 1}`).join(", ")} (valid: minimal…max).`, "warning");
|
|
177
180
|
// set() persists, updates the runtime, syncs tool availability, and
|
|
178
181
|
// notifies; surface unexpected rejections instead of dropping them.
|
|
179
182
|
set(panel.cfgToModels(working), ctx).catch((error) => ctx.ui.notify(`Advisor update failed: ${String(error)}`, "error"));
|
|
@@ -229,13 +232,10 @@ export function registerAdvisor(pi: ExtensionAPI, state: AdvisorState): void {
|
|
|
229
232
|
// chain runner and availability gate skip dead entries at call time).
|
|
230
233
|
// Dedupe here too: two raw spellings can resolve to the same provider/id.
|
|
231
234
|
const available = ctx.modelRegistry.getAvailable();
|
|
232
|
-
return await set([...new Set(chain.map((entry) =>
|
|
233
|
-
const match = exactModel(available, entry);
|
|
234
|
-
return match ? modelRef(match) : entry;
|
|
235
|
-
}))], ctx);
|
|
235
|
+
return await set([...new Set(chain.map((entry) => canonicalEntry(available, entry)))], ctx);
|
|
236
236
|
}
|
|
237
237
|
const match = chain.length === 1 ? exactModel(ctx.modelRegistry.getAvailable(), chain[0]) : undefined;
|
|
238
|
-
if (match) return await set([
|
|
238
|
+
if (match) return await set([canonicalEntry(ctx.modelRegistry.getAvailable(), chain[0])], ctx);
|
|
239
239
|
if (ctx.mode !== "tui") throw new Error("Usage: /advisor <provider/model[, …]|models|on|off|status>");
|
|
240
240
|
const choice = await chooseModel(ctx, firstAvailable(ctx, state.getModels()), args.trim() || undefined);
|
|
241
241
|
if (!choice) return;
|
package/extensions/lib/config.ts
CHANGED
|
@@ -108,6 +108,20 @@ export function parseModel(value: string): { provider: string; id: string } | un
|
|
|
108
108
|
return { provider: value.slice(0, slash), id: value.slice(slash + 1) };
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
/** Thinking levels accepted as a trailing `:level` on a chain entry.
|
|
112
|
+
* `off` maps to "no explicit level" (SimpleStreamOptions.reasoning has no off). */
|
|
113
|
+
export const THINKING_LEVELS: readonly string[] = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
114
|
+
|
|
115
|
+
/** Split a trailing `:thinking` suffix. Strict trailing match only — openrouter
|
|
116
|
+
* ids like `model:free` stay intact. */
|
|
117
|
+
export function splitThinkingSuffix(entry: string): { name: string; thinking?: string } {
|
|
118
|
+
const idx = entry.lastIndexOf(":");
|
|
119
|
+
if (idx <= 0 || idx >= entry.length - 1) return { name: entry };
|
|
120
|
+
const suffix = entry.slice(idx + 1);
|
|
121
|
+
if (!THINKING_LEVELS.includes(suffix)) return { name: entry };
|
|
122
|
+
return { name: entry.slice(0, idx), thinking: suffix };
|
|
123
|
+
}
|
|
124
|
+
|
|
111
125
|
/**
|
|
112
126
|
* One-shot legacy migration: if pi-advisor.model is unset, adopt the old
|
|
113
127
|
* pi-plan advisorModel preference so existing users keep their advisor.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { streamSimple } from "@earendil-works/pi-ai/compat";
|
|
2
2
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import { parseModel } from "./config";
|
|
3
|
+
import { parseModel, splitThinkingSuffix } from "./config";
|
|
4
4
|
|
|
5
5
|
export interface IsolatedContext {
|
|
6
6
|
systemPrompt: string;
|
|
@@ -21,14 +21,19 @@ export async function runIsolated(
|
|
|
21
21
|
/** Progress hook — every stream event (incl. non-text deltas) resets the caller's idle deadline. */
|
|
22
22
|
onEvent?: () => void,
|
|
23
23
|
): Promise<string> {
|
|
24
|
-
|
|
24
|
+
// A trailing `:level` on the chain entry pins thinking for this candidate;
|
|
25
|
+
// `:off` and no suffix fall back to the chain-wide reasoning (or the
|
|
26
|
+
// provider default when that is unset too).
|
|
27
|
+
const { name, thinking } = modelId ? splitThinkingSuffix(modelId) : { name: modelId, thinking: undefined };
|
|
28
|
+
const effectiveReasoning = thinking && thinking !== "off" ? thinking : reasoning;
|
|
29
|
+
const parsed = name ? parseModel(name) : undefined;
|
|
25
30
|
if (modelId && !parsed) throw new Error(`Invalid model: ${modelId}`);
|
|
26
31
|
const model = parsed ? ctx.modelRegistry.find(parsed.provider, parsed.id) : ctx.model;
|
|
27
32
|
if (!model) throw new Error(`Model unavailable: ${modelId ?? "active"}`);
|
|
28
33
|
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
|
|
29
34
|
if (!auth.ok) throw new Error(auth.error);
|
|
30
35
|
const provider = ctx.modelRegistry.getRegisteredProviderConfig(model.provider);
|
|
31
|
-
const options: Record<string, unknown> = { apiKey: auth.apiKey, headers: auth.headers, env: auth.env, signal, reasoning };
|
|
36
|
+
const options: Record<string, unknown> = { apiKey: auth.apiKey, headers: auth.headers, env: auth.env, signal, reasoning: effectiveReasoning };
|
|
32
37
|
// ponytail: providers accept SimpleStreamOptions which expects ThinkingLevel for reasoning
|
|
33
38
|
const streamOptions = options as any;
|
|
34
39
|
const response = provider?.streamSimple
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ModelSelectorComponent, type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { splitThinkingSuffix } from "./config";
|
|
2
3
|
|
|
3
4
|
export type Model = ReturnType<ExtensionContext["modelRegistry"]["getAvailable"]>[number];
|
|
4
5
|
|
|
@@ -6,9 +7,11 @@ export function modelRef(model: Pick<Model, "provider" | "id">): string {
|
|
|
6
7
|
return `${model.provider}/${model.id}`;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
/** Resolve a `provider/model` (or unambiguous bare id) reference against available models.
|
|
10
|
+
/** Resolve a `provider/model` (or unambiguous bare id) reference against available models.
|
|
11
|
+
* A trailing `:level` thinking suffix is ignored for matching. */
|
|
10
12
|
export function exactModel(models: Model[], reference: string): Model | undefined {
|
|
11
|
-
const
|
|
13
|
+
const { name } = splitThinkingSuffix(reference.trim());
|
|
14
|
+
const value = name.toLowerCase();
|
|
12
15
|
if (!value) return undefined;
|
|
13
16
|
const canonical = models.filter((model) => modelRef(model).toLowerCase() === value);
|
|
14
17
|
if (canonical.length === 1) return canonical[0];
|
|
@@ -21,6 +24,15 @@ export function modelAvailable(ctx: ExtensionContext, modelId: string | undefine
|
|
|
21
24
|
return !!modelId && !!exactModel(ctx.modelRegistry.getAvailable(), modelId);
|
|
22
25
|
}
|
|
23
26
|
|
|
27
|
+
/** Canonicalize a chain entry: resolve the ref (ignoring a trailing `:level`),
|
|
28
|
+
* re-attach the level so a pinned thinking level survives the save. */
|
|
29
|
+
export function canonicalEntry(models: Model[], entry: string): string {
|
|
30
|
+
const { name, thinking } = splitThinkingSuffix(entry.trim());
|
|
31
|
+
const match = exactModel(models, name);
|
|
32
|
+
if (!match) return entry;
|
|
33
|
+
return thinking ? `${modelRef(match)}:${thinking}` : modelRef(match);
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
/** First registry-available ref in the chain, or undefined when none resolve. */
|
|
25
37
|
export function firstAvailable(ctx: ExtensionContext, models: readonly string[]): string | undefined {
|
|
26
38
|
return models.find((model) => modelAvailable(ctx, model));
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `/advisor models` panel (kernel lives in @bacnh85/pi-config-panel).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `pi-advisor.models`
|
|
4
|
+
* Per slot: a model-ref row plus a thinking row (blank = provider default).
|
|
5
|
+
* A pinned level serializes into the chain entry as `provider/id:level`
|
|
6
|
+
* (strict trailing match; openrouter `:free` ids stay intact), so the saved
|
|
7
|
+
* `pi-advisor.models` shape stays a plain string array. Plus "Add model slot"
|
|
8
|
+
* / "Remove last" action rows. Saving writes to the GLOBAL
|
|
9
|
+
* `~/.pi/agent/settings.json` via saveModels (merge + atomic rename).
|
|
8
10
|
*/
|
|
9
11
|
|
|
10
12
|
import { row } from "@bacnh85/pi-config-panel";
|
|
11
13
|
import type { PanelGroup, PanelAction } from "@bacnh85/pi-config-panel";
|
|
14
|
+
import { splitThinkingSuffix, THINKING_LEVELS } from "./config";
|
|
12
15
|
|
|
13
16
|
/** Completion sources for the panel's model rows (lazy — resolved per keypress). */
|
|
14
17
|
export interface ModelsPanelOptions {
|
|
@@ -17,37 +20,61 @@ export interface ModelsPanelOptions {
|
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
export interface ModelsPanelCfg {
|
|
20
|
-
/** Working copy: ordered
|
|
21
|
-
models: string[];
|
|
23
|
+
/** Working copy: ordered slots; blank ref = removed slot. */
|
|
24
|
+
models: { ref: string; thinking: string }[];
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
/** Seed a working config from the current effective chain. */
|
|
27
|
+
/** Seed a working config from the current effective chain (`ref:level` entries parsed). */
|
|
25
28
|
export function buildModelsPanelCfg(models: readonly string[]): ModelsPanelCfg {
|
|
26
|
-
return {
|
|
29
|
+
return {
|
|
30
|
+
models: models.map((entry) => {
|
|
31
|
+
const { name, thinking } = splitThinkingSuffix(String(entry ?? "").trim());
|
|
32
|
+
return { ref: name, thinking: thinking ?? "" };
|
|
33
|
+
}),
|
|
34
|
+
};
|
|
27
35
|
}
|
|
28
36
|
|
|
29
|
-
/** Build panel groups:
|
|
37
|
+
/** Build panel groups: per-slot model + thinking rows + add/remove actions.
|
|
30
38
|
* `options` adds inline model completions (optional so unit tests and
|
|
31
39
|
* non-TUI callers stay unchanged). */
|
|
32
40
|
export function buildRows(cfg: ModelsPanelCfg, options?: ModelsPanelOptions, actions: Record<string, PanelAction> = {}): PanelGroup[] {
|
|
33
41
|
const modelItems = (): { value: string }[] =>
|
|
34
42
|
(options?.models() ?? []).sort().map((ref) => ({ value: ref }));
|
|
35
43
|
const withCompletions = options ? { completions: modelItems } : {};
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
const levelItems = () => THINKING_LEVELS.filter((l) => l !== "off").map((level) => ({ value: level }));
|
|
45
|
+
const rows = cfg.models.flatMap((slot, index) => {
|
|
46
|
+
const modelRow = row(`model.${index}`, `#${index + 1}${index === 0 ? " (primary)" : ""}`, "string", slot.ref, (v) => {
|
|
47
|
+
slot.ref = String(v ?? "").trim();
|
|
48
|
+
}, withCompletions);
|
|
49
|
+
const thinkingRow = row(`model.${index}.thinking`, `#${index + 1} thinking (blank = model default)`, "string", slot.thinking, (v) => {
|
|
50
|
+
slot.thinking = String(v ?? "").trim();
|
|
51
|
+
}, { completions: levelItems } as unknown as { mask?: boolean });
|
|
52
|
+
return [modelRow, thinkingRow];
|
|
53
|
+
});
|
|
41
54
|
const actionRows = Object.entries(actions).map(([key, action]) => ({ key, label: action.label, kind: "action" as const, value: "", set: action.run as unknown as (v: unknown) => void }));
|
|
42
|
-
return [{ key: "models", label: "Model chain (ordered fallback, first = primary)", rows: [...
|
|
55
|
+
return [{ key: "models", label: "Model chain (ordered fallback, first = primary)", rows: [...rows, ...actionRows] }];
|
|
43
56
|
}
|
|
44
57
|
|
|
45
|
-
/**
|
|
58
|
+
/** Slot indexes whose thinking value is non-blank but not a valid level
|
|
59
|
+
* (typo guard — cfgToModels drops them). Empty when all values are OK. */
|
|
60
|
+
export function invalidThinkingSlots(cfg: ModelsPanelCfg): number[] {
|
|
61
|
+
const out: number[] = [];
|
|
62
|
+
cfg.models.forEach((slot, index) => {
|
|
63
|
+
const t = slot.thinking.trim();
|
|
64
|
+
if (t && !THINKING_LEVELS.includes(t)) out.push(index);
|
|
65
|
+
});
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Convert a working config back to the saved chain: `ref:level` when a valid
|
|
70
|
+
* level is pinned; blank refs and invalid levels dropped. */
|
|
46
71
|
export function cfgToModels(cfg: ModelsPanelCfg): string[] {
|
|
47
72
|
const out: string[] = [];
|
|
48
|
-
for (const
|
|
49
|
-
const
|
|
50
|
-
if (
|
|
73
|
+
for (const slot of cfg.models) {
|
|
74
|
+
const ref = slot.ref.trim();
|
|
75
|
+
if (!ref) continue;
|
|
76
|
+
const thinking = slot.thinking.trim();
|
|
77
|
+
out.push(thinking && THINKING_LEVELS.includes(thinking) && thinking !== "off" ? `${ref}:${thinking}` : ref);
|
|
51
78
|
}
|
|
52
79
|
return out;
|
|
53
80
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { buildSessionContext, convertToLlm, type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { runIsolatedChain } from "./isolated-model";
|
|
3
3
|
import { createGuard, guardCheck, nextCycle, parseReviewOutput, type GuardState, type Severity } from "./emission-guard";
|
|
4
|
-
import type
|
|
4
|
+
import { splitThinkingSuffix, type AdvisorConfig } from "./config";
|
|
5
5
|
|
|
6
6
|
export const REVIEW_ENTRY = "pi-advisor";
|
|
7
7
|
export type { Severity };
|
|
@@ -103,9 +103,10 @@ export function buildEvidence(ctx: ExtensionContext, modelId: string | readonly
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
function parseModelRef(value: string): { provider: string; id: string } | undefined {
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
106
|
+
const { name } = splitThinkingSuffix(value);
|
|
107
|
+
const slash = name.indexOf("/");
|
|
108
|
+
if (slash <= 0 || slash === name.length - 1) return undefined;
|
|
109
|
+
return { provider: name.slice(0, slash), id: name.slice(slash + 1) };
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
function toolCallCount(entries: any[], sinceId: string | undefined): number {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bacnh85/pi-advisor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Pi extension for an automatic advisor: a second model that reviews each settled turn and injects severity-routed notes, plus an on-demand consult tool.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|