@vanillagreen/pi-qol 2.5.0 → 2.5.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
CHANGED
|
@@ -52,7 +52,7 @@ Open `/extensions:settings`; settings appear under the **QOL** tab. Project sett
|
|
|
52
52
|
- `gatewayPrivacy.enabled`: require zero data retention and no prompt training for main-agent and QOL model requests through OpenRouter and Vercel AI Gateway. Covers QOL compaction, branch summaries, imported-session summaries, handoff and session naming. Keeps other routing options and overrides weaker privacy values. Pi's built-in compaction and branch summaries cannot carry these fields, so on a gateway session model QOL writes them in place of Pi's, whatever `compaction.customEnabled` and `compaction.branchSummaryEnabled` say, and a failed or refused summary cancels the compaction or tree move instead of falling back to Pi's. Custom remote compaction endpoints keep their own privacy policy.
|
|
53
53
|
- Statusline and editor: `statusline.enabled`, `statusline.showProvider`, `statusline.showAccount`, `replaceFooter`, `compactPrompt`, `showSessionNameTitle`, `showSessionNameWindow`, `inputBottomPaddingLines`, `gitRefreshTimeoutMs`, `showDirtyMarker`, `newlineOnShiftEnter`, `newlineFallbackKey`, `pendingQueue.asciiGreen`, `showImageChips`, `showAttachmentCountInStatus`.
|
|
54
54
|
- Commands: `enableSessionNameCommand`, `enableHandoffCommand`, `enableScheduleCommand`, `enableContextCommand`, `handoffReviewPrompt`.
|
|
55
|
-
- Session naming: `sessionAutoRename.*` (model, fallback model, deterministic fallback, prefix, prompt, limits, notify, debug). `sessionAutoRename.maxTokens` includes the naming model's reasoning tokens. `sessionAutoRename.maxNameChars` limits the title length.
|
|
55
|
+
- Session naming: `sessionAutoRename.*` (model, fallback model, deterministic fallback, prefix, prompt, limits, notify, debug). `sessionAutoRename.maxTokens` includes the naming model's reasoning tokens. The naming request asks for the lowest reasoning effort the model accepts, and turns reasoning off where the model allows it. `sessionAutoRename.maxNameChars` limits the title length.
|
|
56
56
|
- Session search: `sessionSearch.*` (shortcut, result and row limits, snippets, overlay width, cache TTL, summary model and limits).
|
|
57
57
|
- Rate limits: `rateLimitAutoResume.*`.
|
|
58
58
|
- Permission gate: `permissionGate.enabled`, `permissionGate.commands` (comma-separated literal fragments or `/regex/flags`), `permissionGate.previewLines`, `permissionGate.previewChars`.
|
|
@@ -18,11 +18,27 @@ type RetryAssistantCall = (
|
|
|
18
18
|
},
|
|
19
19
|
) => Promise<any>;
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
if (typeof
|
|
21
|
+
async function dispatchCompletion(name: "complete" | "completeSimple", model: unknown, context: unknown, options: unknown, deps: PiAiCompatDeps): Promise<any> {
|
|
22
|
+
const rootFunction = (deps.root ?? piAi as any)[name];
|
|
23
|
+
if (typeof rootFunction === "function") return await rootFunction(model, context, options);
|
|
24
24
|
const compat = deps.loadCompat ? await deps.loadCompat() : await dynamicImport("@earendil-works/pi-ai/compat") as any;
|
|
25
|
-
return await compat
|
|
25
|
+
return await compat[name](model, context, options);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function complete(model: unknown, context: unknown, options?: unknown, deps: PiAiCompatDeps = {}): Promise<any> {
|
|
29
|
+
return await dispatchCompletion("complete", model, context, options, deps);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function completeSimple(model: unknown, context: unknown, options?: unknown, deps: PiAiCompatDeps = {}): Promise<any> {
|
|
33
|
+
return await dispatchCompletion("completeSimple", model, context, options, deps);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// The lowest reasoning level the model accepts, as `SimpleStreamOptions.reasoning`
|
|
37
|
+
// takes it. Simple options express "off" by leaving the level out, so a model that
|
|
38
|
+
// can turn reasoning off gets undefined. Levels come from pi-ai's model catalog.
|
|
39
|
+
export function lowestReasoningLevel(model: unknown, deps: { root?: any } = {}): string | undefined {
|
|
40
|
+
const [lowest] = (deps.root ?? piAi as any).getSupportedThinkingLevels(model) as string[];
|
|
41
|
+
return lowest === "off" ? undefined : lowest;
|
|
26
42
|
}
|
|
27
43
|
|
|
28
44
|
export async function retryAssistantCallCompat(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Message } from "@earendil-works/pi-ai";
|
|
2
|
-
import {
|
|
2
|
+
import { completeSimple, lowestReasoningLevel } from "./pi-ai-compat.js";
|
|
3
3
|
import { gatewayPrivacyOnPayload } from "./gateway-privacy.js";
|
|
4
4
|
import type { ExtensionContext, SessionEntry } from "@earendil-works/pi-coding-agent";
|
|
5
5
|
import { textFromContent } from "./agent-end.js";
|
|
@@ -248,6 +248,21 @@ export function headerRecord(headers: unknown): Record<string, string | null> |
|
|
|
248
248
|
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
+
// Pi's agent path sends a request to the base URL the credential resolves, not the
|
|
252
|
+
// catalog's: a Copilot enterprise token names the enterprise proxy host
|
|
253
|
+
// (pi-ai auth/oauth/github-copilot.js `toAuth`, pi-coding-agent
|
|
254
|
+
// core/agent-session.js `_getRequiredRequestAuth`).
|
|
255
|
+
function autoRenameAuth(model: any, auth: { apiKey?: string; baseUrl?: string; env?: Record<string, string>; headers?: unknown }, source: string): AutoRenameAuth {
|
|
256
|
+
return {
|
|
257
|
+
apiKey: auth.apiKey,
|
|
258
|
+
env: auth.env,
|
|
259
|
+
headers: headerRecord(auth.headers),
|
|
260
|
+
label: modelLabel(model),
|
|
261
|
+
model: auth.baseUrl ? { ...model, baseUrl: auth.baseUrl } : model,
|
|
262
|
+
source,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
251
266
|
function modelCost(model: any): number {
|
|
252
267
|
const cost = model?.cost;
|
|
253
268
|
return Number(cost?.input ?? 0) + Number(cost?.output ?? 0);
|
|
@@ -267,8 +282,7 @@ async function cheapestAvailableAutoRenameModel(ctx: ExtensionContext): Promise<
|
|
|
267
282
|
for (const model of models) {
|
|
268
283
|
try {
|
|
269
284
|
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
|
|
270
|
-
|
|
271
|
-
if (auth.ok) return { apiKey: auth.apiKey, env: auth.env, headers, label: modelLabel(model), model, source: "cheapest" };
|
|
285
|
+
if (auth.ok) return autoRenameAuth(model, auth, "cheapest");
|
|
272
286
|
} catch (error) {
|
|
273
287
|
if (isStaleCtxError(error)) return undefined;
|
|
274
288
|
// Try the next available model.
|
|
@@ -287,12 +301,11 @@ async function resolveAutoRenameModel(ctx: ExtensionContext, configured: string)
|
|
|
287
301
|
}
|
|
288
302
|
try {
|
|
289
303
|
const auth = await ctx.modelRegistry.getApiKeyAndHeaders(model);
|
|
290
|
-
const headers = headerRecord(auth.headers);
|
|
291
304
|
if (!auth.ok) {
|
|
292
305
|
autoRenameDebug(ctx, `auth unavailable for ${modelLabel(model)}: ${auth.error}`, "warning");
|
|
293
306
|
return undefined;
|
|
294
307
|
}
|
|
295
|
-
return
|
|
308
|
+
return autoRenameAuth(model, auth, configured);
|
|
296
309
|
} catch (error) {
|
|
297
310
|
if (isStaleCtxError(error)) return undefined;
|
|
298
311
|
autoRenameDebug(ctx, `auth failed for ${modelLabel(model)}: ${stringifyError(error)}`, "warning");
|
|
@@ -329,10 +342,12 @@ export async function generateAutoRenameName(query: string, ctx: ExtensionContex
|
|
|
329
342
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
330
343
|
timeout.unref?.();
|
|
331
344
|
try {
|
|
332
|
-
|
|
345
|
+
// A reasoning model left at its default effort can spend the whole token
|
|
346
|
+
// cap reasoning and write no title.
|
|
347
|
+
const response = await completeSimple(
|
|
333
348
|
resolved.model,
|
|
334
349
|
{ messages: [message], systemPrompt: AUTO_RENAME_SYSTEM_PROMPT },
|
|
335
|
-
{ apiKey: resolved.apiKey, env: resolved.env, headers: resolved.headers, maxTokens, signal: controller.signal, onPayload: gatewayPrivacyOnPayload(ctx) },
|
|
350
|
+
{ apiKey: resolved.apiKey, env: resolved.env, headers: resolved.headers, maxTokens, reasoning: lowestReasoningLevel(resolved.model), signal: controller.signal, onPayload: gatewayPrivacyOnPayload(ctx) },
|
|
336
351
|
);
|
|
337
352
|
if (response.stopReason === "error") {
|
|
338
353
|
autoRenameDebug(ctx, `${resolved.label} failed: ${response.errorMessage ?? "unknown error"}`, "warning");
|
|
@@ -71,11 +71,15 @@ export function installThinkingTimerPatch(): void {
|
|
|
71
71
|
const labelComponents: Text[] = [];
|
|
72
72
|
for (const child of this.contentContainer.children as any[]) {
|
|
73
73
|
if (!child || typeof child !== "object") continue;
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
// Pi's SDK setHiddenThinkingLabel applies one label globally, not per block.
|
|
75
|
+
// Until it supports per-block labels, depend on the private contentContainer
|
|
76
|
+
// and MouseRegion.child (verified at Pi 0.86.0 and 1.0.1).
|
|
77
|
+
const label = child.child ?? child;
|
|
78
|
+
if (typeof label.setText !== "function") continue;
|
|
79
|
+
if (typeof label.text !== "string") continue;
|
|
76
80
|
const expectedLabel = thinkingLabel(store.cwd);
|
|
77
|
-
if (!
|
|
78
|
-
labelComponents.push(
|
|
81
|
+
if (!label.text.includes(expectedLabel) && !label.text.includes("Thinking...")) continue;
|
|
82
|
+
labelComponents.push(label as Text);
|
|
79
83
|
}
|
|
80
84
|
if (labelComponents.length === 0) return;
|
|
81
85
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vanillagreen/pi-qol",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"description": "Pi quality-of-life extension: compact statusline/π prompt, reliable multiline input, styled pasted-image chips, session naming/search/context import, scheduled prompts, handoff, permission prompts, notifications, custom compaction, and a collapsed-thinking timer.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|