@diegopetrucci/pi-extensions 0.1.72 → 0.1.73

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.
Files changed (53) hide show
  1. package/README.md +2 -2
  2. package/extensions/agent-workflow-audit/index.ts +41 -8
  3. package/extensions/agent-workflow-audit/package.json +1 -1
  4. package/extensions/annotate-git-diff/README.md +7 -3
  5. package/extensions/annotate-git-diff/git.ts +3 -2
  6. package/extensions/annotate-git-diff/index.ts +366 -81
  7. package/extensions/annotate-git-diff/package.json +2 -2
  8. package/extensions/annotate-git-diff/quiet-glimpse.ts +4 -2
  9. package/extensions/annotate-git-diff/types.ts +6 -0
  10. package/extensions/annotate-git-diff/web/app.js +54 -4
  11. package/extensions/annotate-git-diff/web/index.html +2 -2
  12. package/extensions/annotate-last-message/README.md +9 -1
  13. package/extensions/annotate-last-message/index.ts +53 -13
  14. package/extensions/annotate-last-message/package.json +2 -1
  15. package/extensions/annotate-last-message/quiet-glimpse.ts +4 -2
  16. package/extensions/annotate-last-message/theme.ts +247 -0
  17. package/extensions/annotate-last-message/ui.ts +28 -2
  18. package/extensions/annotate-last-message/web/app.js +351 -162
  19. package/extensions/annotate-last-message/web/index.html +153 -24
  20. package/extensions/annotate-last-message/web/md-renderer.js +458 -0
  21. package/extensions/code-reviewer/index.ts +12 -1
  22. package/extensions/code-reviewer/package.json +2 -1
  23. package/extensions/context-cap/README.md +33 -4
  24. package/extensions/context-cap/index.ts +271 -68
  25. package/extensions/context-cap/package.json +1 -1
  26. package/extensions/context-inspector/README.md +2 -0
  27. package/extensions/context-inspector/index.ts +90 -9
  28. package/extensions/context-inspector/package.json +1 -1
  29. package/extensions/contrarian/index.ts +21 -15
  30. package/extensions/contrarian/package.json +1 -1
  31. package/extensions/dynamic-context-pruning/README.md +17 -5
  32. package/extensions/dynamic-context-pruning/index.ts +17 -4
  33. package/extensions/dynamic-context-pruning/package.json +1 -1
  34. package/extensions/fast/README.md +24 -3
  35. package/extensions/fast/index.ts +2 -2
  36. package/extensions/fast/package.json +1 -1
  37. package/extensions/git-footer/README.md +1 -0
  38. package/extensions/git-footer/package.json +1 -1
  39. package/extensions/librarian/index.ts +7 -0
  40. package/extensions/librarian/package.json +2 -1
  41. package/extensions/minimal-footer/README.md +16 -4
  42. package/extensions/minimal-footer/index.ts +263 -14
  43. package/extensions/minimal-footer/openai-usage.ts +62 -8
  44. package/extensions/minimal-footer/package.json +1 -1
  45. package/extensions/oracle/index.ts +19 -18
  46. package/extensions/oracle/package.json +1 -1
  47. package/extensions/permission-gate/README.md +6 -0
  48. package/extensions/permission-gate/index.ts +37 -16
  49. package/extensions/permission-gate/package.json +1 -1
  50. package/extensions/quiet-tools/README.md +1 -1
  51. package/extensions/quiet-tools/package.json +1 -1
  52. package/extensions/triage-comments/package.json +2 -1
  53. package/package.json +4 -4
@@ -111,6 +111,11 @@ const CONTRARIAN_MODEL_PREFERENCES = [
111
111
  "openai.gpt-6-astra ",
112
112
  "gpt-6-astra ",
113
113
  "gpt-6-astra",
114
+ "openai/gpt-6.1-sol ",
115
+ "global.openai.gpt-6.1-sol ",
116
+ "us.openai.gpt-6.1-sol ",
117
+ "openai.gpt-6.1-sol ",
118
+ "gpt-6.1-sol ",
114
119
  "openai/gpt-6-sol ",
115
120
  "gpt-6-sol ",
116
121
  "gpt-6-sol",
@@ -171,6 +176,9 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
171
176
  "us.openai.gpt-6-astra ",
172
177
  "openai.gpt-6-astra ",
173
178
  "gpt-6-astra",
179
+ "global.openai.gpt-6.1-sol ",
180
+ "us.openai.gpt-6.1-sol ",
181
+ "openai.gpt-6.1-sol ",
174
182
  "openai.gpt-5.6-sol",
175
183
  "openai.gpt-5.6-terra",
176
184
  "openai.gpt-5.6-luna",
@@ -202,6 +210,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
202
210
  "azure-openai-responses": [
203
211
  "gpt-6-astra ",
204
212
  "gpt-6-astra",
213
+ "gpt-6.1-sol ",
205
214
  "gpt-6-sol ",
206
215
  "gpt-6-sol",
207
216
  "gpt-5.6-sol",
@@ -281,13 +290,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
281
290
  fireworks: [
282
291
  "accounts/fireworks/models/kimi-k3",
283
292
  "accounts/fireworks/routers/kimi-k3-fast",
284
- "accounts/fireworks/models/deepseek-v4-pro",
285
- "accounts/fireworks/models/kimi-k2p7-code",
286
- "accounts/fireworks/models/glm-5p2",
287
- "accounts/fireworks/routers/glm-5p2-fast",
288
293
  "accounts/fireworks/models/minimax-m3",
289
- "accounts/fireworks/models/kimi-k2p6",
290
- "accounts/fireworks/models/qwen3p7-plus",
291
294
  "accounts/fireworks/models/gpt-oss-120b",
292
295
  ],
293
296
  "github-copilot": [
@@ -299,6 +302,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
299
302
  "claude-opus-4.7",
300
303
  "gpt-6-astra ",
301
304
  "gpt-6-astra",
305
+ "gpt-6.1-sol ",
302
306
  "gpt-6-sol ",
303
307
  "gpt-6-sol",
304
308
  "gpt-5.6-sol",
@@ -386,6 +390,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
386
390
  openai: [
387
391
  "gpt-6-astra ",
388
392
  "gpt-6-astra",
393
+ "gpt-6.1-sol ",
389
394
  "gpt-6-sol ",
390
395
  "gpt-6-sol",
391
396
  "gpt-5.6-sol",
@@ -411,6 +416,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
411
416
  "openai-codex": [
412
417
  "gpt-6-astra ",
413
418
  "gpt-6-astra",
419
+ "gpt-6.1-sol ",
414
420
  "gpt-6-sol ",
415
421
  "gpt-6-sol",
416
422
  "gpt-5.6-sol",
@@ -428,6 +434,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
428
434
  "claude-fable-5",
429
435
  "gpt-6-astra ",
430
436
  "gpt-6-astra",
437
+ "gpt-6.1-sol ",
431
438
  "gpt-6-sol ",
432
439
  "gpt-6-sol",
433
440
  "gpt-5.6-sol",
@@ -466,17 +473,13 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
466
473
  "deepseek-v4-pro",
467
474
  "glm-5.3",
468
475
  "glm-5.2",
469
- "glm-5.1",
470
- "qwen3.7-max",
471
476
  "qwen3.7-plus",
472
- "qwen3.6-plus",
473
477
  "mimo-v2.5-pro",
474
478
  "mimo-v2.5",
475
479
  "minimax-m3",
476
480
  "minimax-m2.7",
477
481
  "kimi-k3",
478
482
  "kimi-k2.7-code",
479
- "kimi-k2.6",
480
483
  "grok-4.7 ",
481
484
  "grok-4.7",
482
485
  "grok-4.6",
@@ -494,6 +497,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
494
497
  "anthropic/claude-opus-4.1",
495
498
  "openai/gpt-6-astra ",
496
499
  "openai/gpt-6-astra",
500
+ "openai/gpt-6.1-sol ",
497
501
  "openai/gpt-6-sol ",
498
502
  "openai/gpt-6-sol",
499
503
  "openai/gpt-5.6-sol-pro",
@@ -582,9 +586,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
582
586
  "gpt-5.4",
583
587
  "gpt-5.3-codex",
584
588
  "kimi-k3",
585
- "deepseek-v4-pro",
586
589
  "glm-5.3",
587
- "kimi-k2.7-code",
588
590
  "deepseek-v4.1-flash",
589
591
  "gpt-5.4-mini",
590
592
  "glm-5.3-flash",
@@ -594,8 +596,6 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
594
596
  "moonshotai/Kimi-K3",
595
597
  "zai-org/GLM-5.3-Flash",
596
598
  "zai-org/GLM-5.2",
597
- "moonshotai/Kimi-K2.7-Code",
598
- "moonshotai/Kimi-K2.6",
599
599
  "Qwen/Qwen3.7-Max",
600
600
  "Qwen/Qwen3.6-Plus",
601
601
  "MiniMaxAI/MiniMax-M3",
@@ -605,6 +605,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
605
605
  "nvidia/nemotron-3-ultra-550b-a55b",
606
606
  "google/gemma-4-31B-it",
607
607
  ],
608
+ typesafe: [],
608
609
  "vercel-ai-gateway": [
609
610
  "anthropic/claude-opus-5.5 ",
610
611
  "anthropic/claude-opus-5.5",
@@ -618,6 +619,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
618
619
  "anthropic/claude-sonnet-4.6",
619
620
  "openai/gpt-6-astra ",
620
621
  "openai/gpt-6-astra",
622
+ "openai/gpt-6.1-sol ",
621
623
  "openai/gpt-6-sol ",
622
624
  "openai/gpt-6-sol",
623
625
  "openai/gpt-5.6-sol",
@@ -1387,7 +1389,10 @@ async function runContrarian(
1387
1389
  }
1388
1390
 
1389
1391
  if (event.type === "message_start" && event.message?.role === "assistant") {
1392
+ // A later assistant turn supersedes any earlier tool/intermediate answer.
1393
+ // Keep only the current message so a tool-only final turn cannot return stale text.
1390
1394
  currentText = "";
1395
+ finalOutput = "";
1391
1396
  emit();
1392
1397
  return;
1393
1398
  }
@@ -1400,7 +1405,8 @@ async function runContrarian(
1400
1405
 
1401
1406
  if (event.type === "message_end" && event.message?.role === "assistant") {
1402
1407
  const text = extractTextFromContent(event.message.content) || currentText;
1403
- if (text) finalOutput = text;
1408
+ // message_end is authoritative, including an empty tool-only message.
1409
+ finalOutput = text;
1404
1410
  currentText = "";
1405
1411
 
1406
1412
  const stopReason = event.message.stopReason;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-contrarian",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "A read-only contrarian subagent extension for pi that stress-tests plans, designs, assumptions, and conclusions.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -27,11 +27,23 @@ and why it hasn't been built yet.
27
27
  key wins when replaying history, so a decision can be pruned, restored, and
28
28
  re-pruned any number of times.
29
29
  - **Recomputed every call, not on a timer.** All three automatic strategies run
30
- fresh on every `context` event, over the current message array. Nothing is
31
- pruned "once and forgotten"; if a tool result would no longer qualify (e.g.
32
- it's now within the protected recency window), it isn't proposed again, but
33
- an already-applied, persisted decision still applies until explicitly
34
- restored.
30
+ fresh on every `context` event, over the current message array. Pi 0.99's
31
+ `context` hook intentionally receives the mutable conversation without system
32
+ messages; Pi restores its system prompt and tool state after the handler. The
33
+ extension does not try to rebuild that request-only envelope.
34
+ - **Canonical picker input.** `/prune` uses Pi's canonical session projection when
35
+ available, so context omissions and replacements, branch selection, and tool
36
+ pairing match the provider-visible conversation. Older hosts retain the raw
37
+ message-entry fallback.
38
+ - **Nested calls stay metadata.** Pi records bounded `nestedCalls` metadata on a
39
+ parent tool result, but nested results are not transcript messages. Strategies
40
+ count and prune only the parent transcript tool result; they never materialize
41
+ child entries, double-count child output, or infer complete history from a
42
+ truncated/incomplete record.
43
+ - **Persisted decisions remain branch-local.** Nothing is pruned "once and
44
+ forgotten"; if a tool result would no longer qualify (e.g. it's now within the
45
+ protected recency window), it isn't proposed again, but an already-applied,
46
+ persisted decision still applies until explicitly restored.
35
47
 
36
48
  ## Strategies
37
49
 
@@ -51,6 +51,8 @@ export interface MinimalToolResultMessage {
51
51
  toolCallId: string;
52
52
  toolName: string;
53
53
  content: (MinimalTextContent | MinimalImageContent)[];
54
+ /** Bounded nested-call metadata; child calls have no transcript results to prune. */
55
+ nestedCalls?: unknown;
54
56
  isError: boolean;
55
57
  timestamp?: number;
56
58
  [key: string]: unknown;
@@ -114,13 +116,16 @@ export function normalizeSessionProjection(value: unknown): MinimalSessionProjec
114
116
  });
115
117
  }
116
118
 
119
+ // Entries carry the provenance that Pi uses to apply omissions and
120
+ // replacements. Validate the optional flattened field for compatibility, but
121
+ // derive the messages used by pruning from entries so stale/invented records
122
+ // cannot bypass canonical context edits.
117
123
  if ("messages" in value && (!Array.isArray(value.messages) || !value.messages.every(isMinimalMessage))) return undefined;
118
- const messages = Array.isArray(value.messages) ? value.messages : entries.flatMap((entry) => entry.messages);
119
- return { entries, messages };
124
+ return { entries, messages: entries.flatMap((entry) => entry.messages) };
120
125
  }
121
126
 
122
127
  export function sessionProjectionToMessages(projection: MinimalSessionProjection): MinimalMessage[] {
123
- return projection.messages ?? projection.entries.flatMap((entry) => entry.messages);
128
+ return projection.entries.flatMap((entry) => entry.messages);
124
129
  }
125
130
 
126
131
  // ============================================================================
@@ -1598,7 +1603,13 @@ export interface ToolCallOccurrence {
1598
1603
  isError: boolean;
1599
1604
  }
1600
1605
 
1601
- /** Collect every assistant toolCall block that has a matching toolResult message, in message order. */
1606
+ /**
1607
+ * Collect every transcript assistant toolCall block that has a matching
1608
+ * transcript toolResult message, in message order. `nestedCalls` is deliberately
1609
+ * not expanded: Pi keeps only bounded call metadata there and does not record
1610
+ * child results as transcript messages, so expanding it would double-count or
1611
+ * invent prune targets.
1612
+ */
1602
1613
  export function collectCompletedToolCallOccurrences(messages: MinimalMessage[]): ToolCallOccurrence[] {
1603
1614
  const resultIndexById = new Map<string, number>();
1604
1615
  const isErrorById = new Map<string, boolean>();
@@ -2318,6 +2329,8 @@ export function buildPrunableItems(
2318
2329
  const items: PrunableItem[] = [];
2319
2330
  for (const occurrence of collectCompletedToolCallOccurrences(messages)) {
2320
2331
  const resultMessage = messages[occurrence.resultIndex] as MinimalToolResultMessage;
2332
+ // Only provider-visible transcript content is economically attributable;
2333
+ // bounded nested-call metadata has no child result content to estimate.
2321
2334
  const estimatedTokens = estimateTokensForContent(resultMessage.content);
2322
2335
  const activeDecision = activeByToolCallId.get(occurrence.toolCallId);
2323
2336
  const status: PrunableItemStatus = activeDecision
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-dynamic-context-pruning",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "type": "module",
5
5
  "description": "A pi extension that dynamically prunes stale context to keep sessions lean.",
6
6
  "keywords": [
@@ -38,9 +38,19 @@ Direct OpenAI API Fast mode requires all of the following:
38
38
 
39
39
  - Provider `openai`.
40
40
  - API `openai-responses` or `openai-completions`.
41
- - Model `gpt-6-astra`, `gpt-6-sol`, or `gpt-6-luna`.
41
+ - Model `gpt-6.1-sol`, `gpt-6-astra`, `gpt-6-sol`, or `gpt-6-luna`.
42
+ - API-key auth or Pi's `openai` ChatGPT OAuth (`Sign in with ChatGPT`).
42
43
  - No existing `service_tier` field in the request payload.
43
44
 
45
+ The direct OpenAI route stays on OpenAI's direct API contract for both API-key
46
+ auth and Pi's `openai` ChatGPT OAuth. Acceptance of `service_tier: "fast"` by
47
+ Pi's `openai` ChatGPT OAuth sign-in endpoint has not been live-verified and
48
+ remains deferred to manual gate `pel-71t6`. This direct route is never forwarded
49
+ to legacy `openai-codex` or its subscription-usage endpoint. The documented
50
+ request shape is support evidence, not live certification; this change was
51
+ validated only with mocked eligibility and request payloads, not live or paid
52
+ provider calls.
53
+
44
54
  OpenAI Codex Fast mode requires all of the following:
45
55
 
46
56
  - Provider `openai-codex`.
@@ -49,6 +59,13 @@ OpenAI Codex Fast mode requires all of the following:
49
59
  - ChatGPT OAuth/subscription auth, not API-key auth.
50
60
  - No existing `service_tier` field in the request payload.
51
61
 
62
+ GPT-6.1 Sol is intentionally unsupported on this legacy Codex route. The
63
+ Codex speed documentation lists GPT-6.1 Sol Fast with `service_tier: "fast"`,
64
+ but Pi's legacy Codex service-tier cost handling currently accounts for
65
+ `priority` and `flex`, not `fast`. The direct OpenAI API's `priority` alias is
66
+ not evidence for the legacy Codex endpoint, so this extension leaves the route
67
+ fail-closed without patching upstream pricing.
68
+
52
69
  Anthropic Fast mode requires all of the following:
53
70
 
54
71
  - Provider `anthropic`.
@@ -129,7 +146,11 @@ The collection package loads this unified extension instead of `openai-fast` and
129
146
  ## Notes
130
147
 
131
148
  - Anthropic Fast mode has separate rate limits, costs more than standard speed, and does not share prompt-cache prefixes with standard-speed requests.
132
- - Direct OpenAI Fast mode is limited to the confirmed, officially priced GPT-6 API models above; Fast pricing, availability, and rate limits remain provider- and account-dependent.
133
- - OpenAI Codex Fast mode intentionally does not affect API-key models.
149
+ - Direct OpenAI Fast mode is limited to the confirmed GPT-6 API models above; Fast pricing, availability, and rate limits remain provider- and account-dependent.
150
+ - Authoritative support evidence: [OpenAI Fast mode](https://developers.openai.com/api/docs/guides/fast-mode) explicitly uses `gpt-6.1-sol` with `service_tier: "fast"` and documents `priority` as an equivalent alias for supported API models; the [GPT-6.1 Sol model page](https://developers.openai.com/api/docs/models/gpt-6.1-sol) documents the model itself.
151
+ - The [Codex speed guide](https://developers.openai.com/codex/speed/) documents GPT-6.1 Sol Fast for ChatGPT-signed-in Codex, but legacy Codex support remains pending separate wire and cost validation; this ticket makes no live-certification claim.
152
+ - Pi's provider catalog remains the source of reported model pricing, including any tiered pricing; this extension does not apply guessed discounts or rewrite cost metadata.
153
+ - OpenAI Codex Fast mode intentionally does not affect API-key models, and GPT-6.1 Sol remains blocked on the legacy Codex route.
154
+ - The retired `openai-fast` and `claude-fast` packages remain legacy standalone boundaries; new provider/auth behavior belongs only to this unified extension.
134
155
  - Existing `speed` and `service_tier` fields are never overwritten.
135
156
  - Pi's model catalog may not include a Fast-mode premium. Cost accounting depends on the provider reporting the effective tier in its streamed response, and this extension does not patch usage totals.
@@ -29,7 +29,7 @@ const OPENAI_SUPPORTED_MODELS = new Set([
29
29
  const OPENAI_API_PROVIDER_ID = "openai";
30
30
  const OPENAI_API_IDS = new Set(["openai-responses", "openai-completions"]);
31
31
  const OPENAI_API_FAST_SERVICE_TIER = "fast";
32
- const OPENAI_API_SUPPORTED_MODELS = new Set(["gpt-6-astra", "gpt-6-sol", "gpt-6-luna"]);
32
+ const OPENAI_API_SUPPORTED_MODELS = new Set(["gpt-6.1-sol", "gpt-6-astra", "gpt-6-sol", "gpt-6-luna"]);
33
33
 
34
34
  const DEFAULT_CONFIG: FastConfig = {
35
35
  enabled: false,
@@ -216,7 +216,7 @@ function getEligibility(ctx: ExtensionContext): Eligibility {
216
216
  return {
217
217
  eligible: false,
218
218
  modelKey: key,
219
- reason: "Fast mode is only enabled for GPT-6 Astra, GPT-6 Sol, and GPT-6 Luna on direct OpenAI APIs",
219
+ reason: "Fast mode is only enabled for GPT-6.1 Sol, GPT-6 Astra, GPT-6 Sol, and GPT-6 Luna on direct OpenAI APIs",
220
220
  };
221
221
  }
222
222
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-fast",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "A pi extension that enables Fast mode for supported direct OpenAI API, OpenAI Codex, and Anthropic Claude models based on the currently selected model.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -40,4 +40,5 @@ Then reload pi:
40
40
  - Does not replace pi's built-in footer.
41
41
  - Uses `ctx.ui.setStatus()`, so pi renders the git summary with other extension statuses.
42
42
  - The current pi extension API does not support literally appending text inside the built-in footer's first `cwd (branch)` line without replacing the footer.
43
+ - Status text is passed through Pi's active `dim` theme color; the extension does not embed terminal escape sequences, so theme changes remain host-controlled.
43
44
  - Git and GitHub CLI lookups run on a short background interval with timeouts.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-git-footer",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "A TLH-style git status add-on for pi's built-in footer.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -10,6 +10,7 @@ import type { Usage } from "@earendil-works/pi-ai";
10
10
  import {
11
11
  DefaultResourceLoader,
12
12
  SessionManager,
13
+ SettingsManager,
13
14
  createAgentSession,
14
15
  getAgentDir,
15
16
  getMarkdownTheme,
@@ -1434,9 +1435,14 @@ export default function librarianExtension(pi: ExtensionAPI) {
1434
1435
  cacheRoot,
1435
1436
  });
1436
1437
 
1438
+ // Keep the research session from inheriting user/project tool, shell,
1439
+ // retry, or provider-request settings. The host model runtime is still
1440
+ // passed explicitly below so the selected provider/auth policy is kept.
1441
+ const isolatedSettingsManager = SettingsManager.inMemory({});
1437
1442
  const resourceLoader = new DefaultResourceLoader({
1438
1443
  cwd: workspace,
1439
1444
  agentDir: getAgentDir(),
1445
+ settingsManager: isolatedSettingsManager,
1440
1446
  noExtensions: true,
1441
1447
  noSkills: true,
1442
1448
  noPromptTemplates: true,
@@ -1469,6 +1475,7 @@ export default function librarianExtension(pi: ExtensionAPI) {
1469
1475
  cwd: workspace,
1470
1476
  ...getModelRuntimeOption(ctx),
1471
1477
  resourceLoader,
1478
+ settingsManager: isolatedSettingsManager,
1472
1479
  sessionManager: SessionManager.inMemory(workspace),
1473
1480
  model: candidate.model,
1474
1481
  thinkingLevel: candidate.details.thinkingLevel,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-librarian",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "A pi GitHub research scout with a toggleable local repo checkout cache under the user's OS cache directory.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -30,6 +30,7 @@
30
30
  ]
31
31
  },
32
32
  "peerDependencies": {
33
+ "@earendil-works/pi-ai": "*",
33
34
  "@earendil-works/pi-coding-agent": "*",
34
35
  "@earendil-works/pi-tui": "*",
35
36
  "typebox": "*"
@@ -45,7 +45,7 @@ When context usage is above 200k tokens, the bottom-left line includes a red war
45
45
  44.1% · DUMB ZONE
46
46
  ```
47
47
 
48
- When using `openai-codex`, the bottom-left line also includes subscription usage. A typical account with both windows looks like:
48
+ When using legacy `openai-codex`, the bottom-left line also includes subscription usage. A typical account with both windows looks like:
49
49
 
50
50
  ```text
51
51
  44.1% · 5h 12% · 7d 38%
@@ -53,6 +53,15 @@ When using `openai-codex`, the bottom-left line also includes subscription usage
53
53
 
54
54
  Window labels come from OpenAI's reported durations, so an account that reports only a weekly primary window is shown correctly (for example, `7d 38%`) instead of being assumed to have a 5-hour window.
55
55
 
56
+ Pi's `openai` ChatGPT OAuth uses the direct OpenAI API and has no documented
57
+ account-usage endpoint compatible with this legacy WHAM reader. In that case,
58
+ the footer shows `usage unsupported` and does not send the new token to
59
+ `chatgpt.com/backend-api/wham/usage`. Direct OpenAI API-key users have no
60
+ subscription-usage line. Legacy Codex fetch failures, missing OAuth, or a
61
+ malformed/mismatched token account claim show `usage unavailable` rather than
62
+ retaining an older account's snapshot; the request is not sent until the token
63
+ claim and lifecycle identity agree.
64
+
56
65
  When `PI_EXPERIMENTAL=1`, the bottom-left line also includes an experimental marker:
57
66
 
58
67
  ```text
@@ -203,8 +212,8 @@ Disable git dirty/ahead/PR status:
203
212
  - `context.dumbZone.thresholdTokens`: token threshold for `DUMB ZONE`
204
213
  - `context.dumbZone.label`: warning text
205
214
  - `context.dumbZone.color`: theme color for the warning (`error`, `warning`, `accent`, `text`, or `dim`)
206
- - `codexUsage.enabled`: show OpenAI Codex session-limit usage when using `openai-codex`
207
- - `codexUsage.cacheTtlMs`: in-memory usage cache duration
215
+ - `codexUsage.enabled`: show OpenAI Codex session-limit usage when using `openai-codex`; also controls the explicit unsupported/unavailable status for account usage
216
+ - `codexUsage.cacheTtlMs`: in-memory usage-result cache duration and account-only login detection interval; provider/model/account changes invalidate the cached snapshot. The timer only compares identity and does not fetch unchanged-account usage. The default is 5 minutes; `0` disables usage-result caching but keeps identity checks every 5 minutes. Positive values above Node's `2,147,483,647` ms timer limit use that maximum only for scheduling; the configured TTL still controls lifecycle cache semantics.
208
217
  - `codexUsage.requestTimeoutMs`: usage request timeout
209
218
  - `codexUsage.windows.primary.enabled`: show the primary usage window
210
219
  - `codexUsage.windows.primary.label`: `"auto"` derives the primary label from OpenAI's reported duration; any other value overrides it
@@ -224,6 +233,7 @@ Disable git dirty/ahead/PR status:
224
233
  - **Top right:** current repo directory name
225
234
  - **Bottom left:** current context usage percentage, plus red `DUMB ZONE` above 200k context tokens
226
235
  - **Bottom left on `openai-codex`:** current context usage percentage plus the Codex usage windows reported for the account
236
+ - **Bottom left on `openai` ChatGPT OAuth:** current context usage percentage plus `usage unsupported`; no legacy WHAM request is attempted
227
237
  - **Bottom left with `PI_EXPERIMENTAL=1`:** current context usage percentage plus `xp`
228
238
  - **Bottom right:** model id and thinking level
229
239
 
@@ -240,4 +250,6 @@ This extension also lives inside the broader [`pi-extensions`](../../README.md)
240
250
  - Shows the model id rather than a provider-specific display label.
241
251
  - Shows `xp` when `PI_EXPERIMENTAL=1`.
242
252
  - For `openai-codex`, reads pi's stored OAuth login and fetches usage from ChatGPT's backend usage endpoint. Window labels are inferred from each window's reported duration; missing or unrecognized durations use neutral `usage` labels rather than guessing from window position.
243
- - Usage is cached briefly in memory and refreshed after turns.
253
+ - For direct `openai` ChatGPT OAuth, no usage endpoint is assumed: the footer marks usage unsupported and never calls the legacy WHAM endpoint. API-key requests do not use subscription usage.
254
+ - Usage is cached briefly in memory and refreshed after turns. A provider/model/account boundary clears the old snapshot, and late results from an earlier request are ignored. Because Pi 0.99 emits no auth-change event, account-only logins are detected by one per-session identity-only timer: it uses `cacheTtlMs` when positive, checks every five minutes when `cacheTtlMs: 0`, and clamps larger schedules to Node's `2,147,483,647` ms maximum. Unchanged-account ticks make no WHAM request; an account change uses the normal safe refresh. The timer is disposed on shutdown/restart, and no credential file is read during footer rendering.
255
+ - Footer colors continue to come from Pi's active theme; the extension does not embed ANSI color codes.