@diegopetrucci/pi-extensions 0.1.60 → 0.1.62

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 (63) hide show
  1. package/.pi-fleet-tested-version +1 -1
  2. package/extensions/agent-workflow-audit/.pi-fleet-tested-version +1 -1
  3. package/extensions/agent-workflow-audit/index.ts +9 -6
  4. package/extensions/agent-workflow-audit/package.json +1 -1
  5. package/extensions/annotate-git-diff/.pi-fleet-tested-version +1 -1
  6. package/extensions/annotate-git-diff/package.json +1 -1
  7. package/extensions/annotate-last-message/.pi-fleet-tested-version +1 -1
  8. package/extensions/annotate-last-message/package.json +1 -1
  9. package/extensions/brrr/.pi-fleet-tested-version +1 -1
  10. package/extensions/brrr/package.json +1 -1
  11. package/extensions/claude-fast/.pi-fleet-tested-version +1 -1
  12. package/extensions/claude-fast/package.json +1 -1
  13. package/extensions/code-reviewer/.pi-fleet-tested-version +1 -1
  14. package/extensions/code-reviewer/README.md +1 -0
  15. package/extensions/code-reviewer/index.ts +28 -2
  16. package/extensions/code-reviewer/package.json +1 -1
  17. package/extensions/confirm-destructive/.pi-fleet-tested-version +1 -1
  18. package/extensions/confirm-destructive/package.json +1 -1
  19. package/extensions/context-cap/.pi-fleet-tested-version +1 -1
  20. package/extensions/context-cap/package.json +1 -1
  21. package/extensions/context-inspector/.pi-fleet-tested-version +1 -1
  22. package/extensions/context-inspector/package.json +1 -1
  23. package/extensions/contrarian/.pi-fleet-tested-version +1 -1
  24. package/extensions/contrarian/README.md +2 -0
  25. package/extensions/contrarian/index.ts +41 -18
  26. package/extensions/contrarian/package.json +1 -1
  27. package/extensions/dirty-repo-guard/.pi-fleet-tested-version +1 -1
  28. package/extensions/dirty-repo-guard/package.json +1 -1
  29. package/extensions/dynamic-context-pruning/.pi-fleet-tested-version +1 -1
  30. package/extensions/dynamic-context-pruning/package.json +1 -1
  31. package/extensions/git-footer/.pi-fleet-tested-version +1 -1
  32. package/extensions/git-footer/package.json +1 -1
  33. package/extensions/gnosis/.pi-fleet-tested-version +1 -1
  34. package/extensions/gnosis/package.json +1 -1
  35. package/extensions/illustrations-to-explain-things/.pi-fleet-tested-version +1 -1
  36. package/extensions/illustrations-to-explain-things/package.json +1 -1
  37. package/extensions/inline-bash/.pi-fleet-tested-version +1 -1
  38. package/extensions/inline-bash/package.json +1 -1
  39. package/extensions/librarian/.pi-fleet-tested-version +1 -1
  40. package/extensions/librarian/README.md +1 -1
  41. package/extensions/librarian/index.ts +34 -5
  42. package/extensions/librarian/package.json +1 -1
  43. package/extensions/minimal-footer/.pi-fleet-tested-version +1 -1
  44. package/extensions/minimal-footer/package.json +1 -1
  45. package/extensions/notify/.pi-fleet-tested-version +1 -1
  46. package/extensions/notify/package.json +1 -1
  47. package/extensions/openai-fast/.pi-fleet-tested-version +1 -1
  48. package/extensions/openai-fast/package.json +1 -1
  49. package/extensions/oracle/.pi-fleet-tested-version +1 -1
  50. package/extensions/oracle/README.md +2 -0
  51. package/extensions/oracle/index.ts +40 -18
  52. package/extensions/oracle/package.json +1 -1
  53. package/extensions/permission-gate/.pi-fleet-tested-version +1 -1
  54. package/extensions/permission-gate/package.json +1 -1
  55. package/extensions/quiet-tools/.pi-fleet-tested-version +1 -1
  56. package/extensions/quiet-tools/package.json +1 -1
  57. package/extensions/review/.pi-fleet-tested-version +1 -1
  58. package/extensions/review/package.json +1 -1
  59. package/extensions/todo/.pi-fleet-tested-version +1 -1
  60. package/extensions/todo/package.json +1 -1
  61. package/extensions/triage-comments/.pi-fleet-tested-version +1 -1
  62. package/extensions/triage-comments/package.json +1 -1
  63. package/package.json +4 -4
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1123,7 +1123,10 @@ export const __test__ = {
1123
1123
  };
1124
1124
 
1125
1125
  export default function agentWorkflowAuditExtension(pi: ExtensionAPI) {
1126
- pi.registerMessageRenderer(CUSTOM_TYPE, (message, { expanded }, theme) => {
1126
+ pi.registerMessageRenderer(CUSTOM_TYPE, (message, options, theme) => {
1127
+ const { expanded } = options;
1128
+ const rendererOptions = options as { outputPad?: number };
1129
+ const outputPad = rendererOptions.outputPad ?? 0;
1127
1130
  const details = message.details as AuditDetails | undefined;
1128
1131
  const report = messageContentToText(message.content) || "(no report)";
1129
1132
  const status = details?.status ?? "done";
@@ -1143,15 +1146,15 @@ export default function agentWorkflowAuditExtension(pi: ExtensionAPI) {
1143
1146
  const header = `${icon} ${theme.fg("toolTitle", theme.bold("agent-workflow-audit "))}${theme.fg("dim", meta)}`;
1144
1147
 
1145
1148
  if (!expanded) {
1146
- return new Text(`${header}\n\n${theme.fg("toolOutput", renderCollapsedReport(report))}`, 0, 0);
1149
+ return new Text(`${header}\n\n${theme.fg("toolOutput", renderCollapsedReport(report))}`, outputPad, 0);
1147
1150
  }
1148
1151
 
1149
1152
  const container = new Container();
1150
- container.addChild(new Text(header, 0, 0));
1151
- if (details?.cwd) container.addChild(new Text(theme.fg("dim", `cwd: ${details.cwd}`), 0, 0));
1152
- if (details?.focus) container.addChild(new Text(theme.fg("dim", `focus: ${details.focus}`), 0, 0));
1153
+ container.addChild(new Text(header, outputPad, 0));
1154
+ if (details?.cwd) container.addChild(new Text(theme.fg("dim", `cwd: ${details.cwd}`), outputPad, 0));
1155
+ if (details?.focus) container.addChild(new Text(theme.fg("dim", `focus: ${details.focus}`), outputPad, 0));
1153
1156
  container.addChild(new Spacer(1));
1154
- container.addChild(new Markdown(report, 0, 0, getMarkdownTheme()));
1157
+ container.addChild(new Markdown(report, outputPad, 0, getMarkdownTheme()));
1155
1158
  return container;
1156
1159
  });
1157
1160
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-agent-workflow-audit",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "A pi extension that runs an isolated repo workflow audit and returns only the final report to the main session.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-annotate-git-diff",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "A standalone pi extension that adds /annotate-git-diff, a native Glimpse UI for annotating git diffs and sending feedback to the editor.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-annotate-last-message",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "A standalone pi extension that adds /annotate-last-message, a native Glimpse UI for annotating the latest assistant reply.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-brrr",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "A pi extension that sends brrr push notifications when pi is ready for input.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-claude-fast",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "A pi extension that enables Anthropic Claude Fast mode for supported Claude Opus models by injecting speed=fast.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -44,6 +44,7 @@ When called, it launches an isolated in-memory child agent with:
44
44
 
45
45
  - no inherited extensions, skills, prompt templates, themes, context files, or agents files;
46
46
  - an auto-selected review model that prefers contrarian-style opposite-provider and opposite-family candidates before same-provider fallbacks;
47
+ - automatic model candidates constrained to the current Pi session's model scope when that scope is non-empty;
47
48
  - requested thinking taken from the active session when available, otherwise defaulting to `high` for reasoning models and `off` for non-reasoning models, then clamped to the selected model's supported level;
48
49
  - read-only tools only: `read`, `grep`, `find`, `ls`, and guarded `bash`;
49
50
  - a local-checkout path guard for file inspection;
@@ -27,6 +27,7 @@ const CODE_REVIEWER_MODEL_PREFERENCES = [
27
27
  "gpt-5.6-terra",
28
28
  "gpt-5.6-luna",
29
29
  "gpt-5.5",
30
+ "claude-opus-5",
30
31
  "claude-opus-4-8",
31
32
  "claude-opus-4.8",
32
33
  "claude-sonnet-5",
@@ -40,6 +41,7 @@ const CODE_REVIEWER_MODEL_PREFERENCES = [
40
41
  ];
41
42
  const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
42
43
  "amazon-bedrock": [
44
+ "claude-opus-5",
43
45
  "claude-fable-5",
44
46
  "claude-opus-4-8",
45
47
  "claude-sonnet-5",
@@ -47,6 +49,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
47
49
  "claude-sonnet-4-5",
48
50
  ],
49
51
  anthropic: [
52
+ "claude-opus-5",
50
53
  "claude-fable-5",
51
54
  "claude-opus-4-8",
52
55
  "claude-sonnet-5",
@@ -64,6 +67,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
64
67
  "gpt-5.3-codex-spark",
65
68
  ],
66
69
  "vercel-ai-gateway": [
70
+ "anthropic/claude-opus-5",
67
71
  "anthropic/claude-fable-5",
68
72
  "anthropic/claude-opus-4.8",
69
73
  "anthropic/claude-opus-4.7",
@@ -220,8 +224,28 @@ type PiModel = {
220
224
  type ModelRegistryContext = {
221
225
  model?: PiModel;
222
226
  modelRegistry: { getAvailable(): PiModel[] | Promise<PiModel[]> };
227
+ scopedModels?: ReadonlyArray<{ model: PiModel; thinkingLevel?: ThinkingLevel }>;
223
228
  };
224
229
 
230
+ function modelKey(model: PiModel): string {
231
+ return `${model.provider}/${model.id}`.toLowerCase();
232
+ }
233
+
234
+ function hasModelScope(ctx: ModelRegistryContext): boolean {
235
+ return Array.isArray(ctx.scopedModels) && ctx.scopedModels.length > 0;
236
+ }
237
+
238
+ function isModelInScope(ctx: ModelRegistryContext, model: PiModel): boolean {
239
+ if (!hasModelScope(ctx)) return true;
240
+ const key = modelKey(model);
241
+ return ctx.scopedModels!.some((entry) => modelKey(entry.model) === key);
242
+ }
243
+
244
+ async function getAutoSelectableModels(ctx: ModelRegistryContext): Promise<PiModel[]> {
245
+ const available = await ctx.modelRegistry.getAvailable();
246
+ return hasModelScope(ctx) ? available.filter((model) => isModelInScope(ctx, model)) : available;
247
+ }
248
+
225
249
  type CreateAgentSessionOptions = NonNullable<Parameters<typeof createAgentSession>[0]>;
226
250
  type CreateAgentSessionModel = CreateAgentSessionOptions["model"];
227
251
 
@@ -350,11 +374,13 @@ function appendOrderedCandidates(
350
374
  async function selectCodeReviewerModel(
351
375
  ctx: ModelRegistryContext,
352
376
  ): Promise<{ ok: true; selection: PiModel; ordered: PiModel[] } | { ok: false; error: string }> {
353
- const available = await ctx.modelRegistry.getAvailable();
377
+ const available = await getAutoSelectableModels(ctx);
354
378
  if (available.length === 0) {
355
379
  return {
356
380
  ok: false,
357
- error: "No authenticated models are available. Log in or configure an API key first.",
381
+ error: hasModelScope(ctx)
382
+ ? "No authenticated models are available in the current session model scope. Adjust the scope, log in, or configure an API key."
383
+ : "No authenticated models are available. Log in or configure an API key first.",
358
384
  };
359
385
  }
360
386
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-code-reviewer",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "A standalone pi extension that adds a read-only code_reviewer subagent tool for isolated code reviews.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-confirm-destructive",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "A pi extension that confirms destructive session actions.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-context-cap",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "A pi extension that caps effective model context windows at 200k tokens for earlier auto-compaction.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-context-inspector",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "A pi extension that opens a local HTML dashboard explaining where the current session context is going.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -77,6 +77,8 @@ Tool-call parameters still win over these defaults. `auto` clears the configured
77
77
  - `thinkingLevel` - optional reasoning/thinking override; falls back to the `/contrarian thinking` default, then built-in defaults
78
78
  - `cwd` - optional working directory override
79
79
 
80
+ When the current Pi session has a non-empty model scope, Contrarian limits automatic model selection and automatic fallbacks to that scope. An explicit tool or `/contrarian model` override remains authoritative even when it is outside the session scope.
81
+
80
82
  ## Notes
81
83
 
82
84
  - The contrarian is intentionally **read-only**.
@@ -20,6 +20,31 @@ type PiModel = {
20
20
  thinkingLevelMap?: ThinkingLevelMap;
21
21
  };
22
22
 
23
+ type ModelSelectionContext = {
24
+ model?: PiModel;
25
+ modelRegistry: { getAvailable(): PiModel[] | Promise<PiModel[]> };
26
+ scopedModels?: ReadonlyArray<{ model: PiModel; thinkingLevel?: ThinkingLevel }>;
27
+ };
28
+
29
+ function modelKey(model: PiModel): string {
30
+ return `${model.provider}/${model.id}`.toLowerCase();
31
+ }
32
+
33
+ function hasModelScope(ctx: ModelSelectionContext): boolean {
34
+ return Array.isArray(ctx.scopedModels) && ctx.scopedModels.length > 0;
35
+ }
36
+
37
+ function isModelInScope(ctx: ModelSelectionContext, model: PiModel): boolean {
38
+ if (!hasModelScope(ctx)) return true;
39
+ const key = modelKey(model);
40
+ return ctx.scopedModels!.some((entry) => modelKey(entry.model) === key);
41
+ }
42
+
43
+ async function getAutoSelectableModels(ctx: ModelSelectionContext): Promise<PiModel[]> {
44
+ const available = await ctx.modelRegistry.getAvailable();
45
+ return hasModelScope(ctx) ? available.filter((model) => isModelInScope(ctx, model)) : available;
46
+ }
47
+
23
48
  interface UsageStats {
24
49
  input: number;
25
50
  output: number;
@@ -84,6 +109,7 @@ const CONTRARIAN_MODEL_PREFERENCES = [
84
109
  "gpt-5.6-terra",
85
110
  "gpt-5.6-luna",
86
111
  "gpt-5.5",
112
+ "claude-opus-5",
87
113
  "claude-opus-4-8",
88
114
  "claude-opus-4.8",
89
115
  "claude-sonnet-5",
@@ -98,6 +124,7 @@ const CONTRARIAN_MODEL_PREFERENCES = [
98
124
 
99
125
  const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
100
126
  "amazon-bedrock": [
127
+ "claude-opus-5",
101
128
  "claude-fable-5",
102
129
  "claude-opus-4-8",
103
130
  "claude-opus-4-7",
@@ -117,6 +144,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
117
144
  "zai.glm-5",
118
145
  ],
119
146
  anthropic: [
147
+ "claude-opus-5",
120
148
  "claude-fable-5",
121
149
  "claude-opus-4-8",
122
150
  "claude-opus-4-7",
@@ -141,13 +169,8 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
141
169
  "gpt-5-chat-latest",
142
170
  "gpt-5.2-pro",
143
171
  "gpt-5.2",
144
- "gpt-5.2-codex",
145
- "gpt-5.1-codex-max",
146
- "gpt-5.1-codex",
147
172
  "gpt-5.1",
148
- "gpt-5.1-chat-latest",
149
173
  "o3-pro",
150
- "o3-deep-research",
151
174
  "o1-pro",
152
175
  "gpt-5.4-mini",
153
176
  "gpt-5-mini",
@@ -182,8 +205,6 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
182
205
  "accounts/fireworks/models/glm-5p2",
183
206
  "accounts/fireworks/routers/glm-5p2-fast",
184
207
  "accounts/fireworks/models/minimax-m3",
185
- "accounts/fireworks/models/glm-5p1",
186
- "accounts/fireworks/routers/glm-5p1-fast",
187
208
  "accounts/fireworks/models/kimi-k2p6",
188
209
  "accounts/fireworks/routers/kimi-k2p6-turbo",
189
210
  "accounts/fireworks/routers/kimi-k2p6-fast",
@@ -193,6 +214,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
193
214
  "accounts/fireworks/models/gpt-oss-20b",
194
215
  ],
195
216
  "github-copilot": [
217
+ "claude-opus-5",
196
218
  "claude-fable-5",
197
219
  "claude-opus-4.8",
198
220
  "claude-opus-4.7",
@@ -282,13 +304,8 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
282
304
  "gpt-5-chat-latest",
283
305
  "gpt-5.2-pro",
284
306
  "gpt-5.2",
285
- "gpt-5.2-codex",
286
- "gpt-5.1-codex-max",
287
- "gpt-5.1-codex",
288
307
  "gpt-5.1",
289
- "gpt-5.1-chat-latest",
290
308
  "o3-pro",
291
- "o3-deep-research",
292
309
  "o1-pro",
293
310
  "gpt-5.4-mini",
294
311
  "gpt-5-mini",
@@ -303,6 +320,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
303
320
  "gpt-5.3-codex-spark",
304
321
  ],
305
322
  opencode: [
323
+ "claude-opus-5",
306
324
  "claude-fable-5",
307
325
  "gpt-5.5-pro",
308
326
  "gpt-5.5",
@@ -344,6 +362,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
344
362
  "kimi-k2.6",
345
363
  ],
346
364
  openrouter: [
365
+ "anthropic/claude-opus-5",
347
366
  "anthropic/claude-fable-5",
348
367
  "~anthropic/claude-fable-latest",
349
368
  "anthropic/claude-opus-4.8",
@@ -417,6 +436,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
417
436
  "google/gemma-4-31B-it",
418
437
  ],
419
438
  "vercel-ai-gateway": [
439
+ "anthropic/claude-opus-5",
420
440
  "anthropic/claude-fable-5",
421
441
  "anthropic/claude-opus-4.8",
422
442
  "anthropic/claude-opus-4.7",
@@ -921,7 +941,7 @@ function appendThinkingLevelClampReason(
921
941
  }
922
942
 
923
943
  async function findAvailableModel(
924
- ctx: { model?: PiModel; modelRegistry: { getAvailable(): PiModel[] | Promise<PiModel[]> } },
944
+ ctx: ModelSelectionContext,
925
945
  modelRef: string,
926
946
  ): Promise<PiModel | undefined> {
927
947
  const available = await ctx.modelRegistry.getAvailable();
@@ -967,11 +987,11 @@ function toContrarianSelection(
967
987
  }
968
988
 
969
989
  function buildSessionFallbackSelection(
970
- ctx: { model?: PiModel },
990
+ ctx: ModelSelectionContext,
971
991
  thinkingLevelOverride: ThinkingLevel | undefined,
972
992
  ): ContrarianSelection | undefined {
973
993
  const model = ctx.model;
974
- if (!model) return undefined;
994
+ if (!model || !isModelInScope(ctx, model)) return undefined;
975
995
  return toContrarianSelection(
976
996
  model,
977
997
  thinkingLevelOverride,
@@ -987,14 +1007,16 @@ function withFallbackReason(selection: ContrarianSelection, previous: Contrarian
987
1007
  }
988
1008
 
989
1009
  async function selectContrarianModel(
990
- ctx: { model?: PiModel; modelRegistry: { getAvailable(): PiModel[] | Promise<PiModel[]> } },
1010
+ ctx: ModelSelectionContext,
991
1011
  thinkingLevelOverride?: ThinkingLevel,
992
1012
  ): Promise<{ ok: true; selection: ContrarianSelection; ordered: ContrarianSelection[] } | { ok: false; error: string }> {
993
- const available = await ctx.modelRegistry.getAvailable();
1013
+ const available = await getAutoSelectableModels(ctx);
994
1014
  if (available.length === 0) {
995
1015
  return {
996
1016
  ok: false,
997
- error: "No authenticated models are available. Log in or configure an API key first.",
1017
+ error: hasModelScope(ctx)
1018
+ ? "No authenticated models are available in the current session model scope. Adjust the scope, log in, or configure an API key."
1019
+ : "No authenticated models are available. Log in or configure an API key first.",
998
1020
  };
999
1021
  }
1000
1022
 
@@ -1343,6 +1365,7 @@ type ContrarianExtensionDeps = {
1343
1365
  };
1344
1366
 
1345
1367
  export const __test__ = {
1368
+ buildSessionFallbackSelection,
1346
1369
  createContrarianExtension,
1347
1370
  findAvailableModel,
1348
1371
  isModelAvailabilityError,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-contrarian",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
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",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-dirty-repo-guard",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "A pi extension that prompts before session changes when the current git repo has uncommitted changes.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-dynamic-context-pruning",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "type": "module",
5
5
  "description": "A pi extension that dynamically prunes stale context to keep sessions lean.",
6
6
  "keywords": [
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-git-footer",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "A TLH-style git status add-on for pi's built-in footer.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-gnosis",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "A pi extension that exposes the gnosis repo-local knowledge base CLI as an agent tool.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-illustrations-to-explain-things",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "A pi skill for generating clean, absurd Xiaohei-style article illustrations, shot lists, and visual metaphors.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-inline-bash",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "A pi extension that expands inline bash commands in user prompts.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -4,7 +4,7 @@ A pi GitHub research scout inspired by `pi-librarian`, with a local checkout cac
4
4
 
5
5
  When the `librarian` tool runs, it can cache/reuse repository checkouts locally. Use `/librarian-cache off` to force GitHub API/search and temporary fetched files only, or `/librarian-cache on` to re-enable cached local checkouts.
6
6
 
7
- The internal librarian subagent uses a fast auto-selected model by default, requests `low` thinking, and prompts its scout to batch independent GitHub probes in parallel. Use `/librarian-config` to set a persistent internal model or thinking-level preference.
7
+ The internal librarian subagent uses a fast auto-selected model by default, requests `low` thinking, and prompts its scout to batch independent GitHub probes in parallel. Automatic candidates and fallbacks stay within the current Pi session's model scope when that scope is non-empty; an explicit tool or `/librarian-config` model remains authoritative. Use `/librarian-config` to set a persistent internal model or thinking-level preference.
8
8
 
9
9
  ## Install
10
10
 
@@ -46,6 +46,31 @@ type PiModel = {
46
46
  thinkingLevelMap?: ThinkingLevelMap;
47
47
  };
48
48
 
49
+ type ModelSelectionContext = {
50
+ model?: any;
51
+ modelRegistry: { getAvailable(): any[] | Promise<any[]> };
52
+ scopedModels?: ReadonlyArray<{ model: any; thinkingLevel?: ThinkingLevel }>;
53
+ };
54
+
55
+ function modelKey(model: any): string {
56
+ return `${model.provider}/${model.id}`.toLowerCase();
57
+ }
58
+
59
+ function hasModelScope(ctx: ModelSelectionContext): boolean {
60
+ return Array.isArray(ctx.scopedModels) && ctx.scopedModels.length > 0;
61
+ }
62
+
63
+ function isModelInScope(ctx: ModelSelectionContext, model: any): boolean {
64
+ if (!hasModelScope(ctx)) return true;
65
+ const key = modelKey(model);
66
+ return ctx.scopedModels!.some((entry) => modelKey(entry.model) === key);
67
+ }
68
+
69
+ async function getAutoSelectableModels(ctx: ModelSelectionContext): Promise<any[]> {
70
+ const available = await ctx.modelRegistry.getAvailable();
71
+ return hasModelScope(ctx) ? available.filter((model) => isModelInScope(ctx, model)) : available;
72
+ }
73
+
49
74
  type CreateAgentSessionOptions = NonNullable<Parameters<typeof createAgentSession>[0]>;
50
75
 
51
76
  function getModelRuntimeOption(ctx: { modelRegistry?: unknown }): Pick<CreateAgentSessionOptions, "modelRuntime"> {
@@ -677,7 +702,7 @@ function rankPreferredFastLibrarianModel(model: any): number {
677
702
  }
678
703
 
679
704
  async function findAvailableModel(
680
- ctx: { model?: any; modelRegistry: { getAvailable(): any[] | Promise<any[]> } },
705
+ ctx: ModelSelectionContext,
681
706
  modelPreference: string,
682
707
  ): Promise<any | undefined> {
683
708
  const available = await ctx.modelRegistry.getAvailable();
@@ -732,7 +757,7 @@ function withLibrarianFallbackReason(candidate: LibrarianCandidate, previous: Li
732
757
  // these in order and skip ones that fail with a model-availability error,
733
758
  // ultimately falling back to the known-good current session model.
734
759
  async function buildLibrarianCandidates(
735
- ctx: { model?: any; modelRegistry: { getAvailable(): any[] | Promise<any[]> } },
760
+ ctx: ModelSelectionContext,
736
761
  modelPreference: string | undefined,
737
762
  thinkingLevel: ThinkingLevel,
738
763
  ): Promise<LibrarianCandidate[]> {
@@ -762,7 +787,7 @@ async function buildLibrarianCandidates(
762
787
  }
763
788
  }
764
789
 
765
- const available = await ctx.modelRegistry.getAvailable();
790
+ const available = await getAutoSelectableModels(ctx);
766
791
  const preferred = available.filter(isPreferredFastLibrarianModel);
767
792
  const ranked = preferred.length > 0
768
793
  ? [...preferred].sort((a, b) => rankPreferredFastLibrarianModel(a) - rankPreferredFastLibrarianModel(b))
@@ -785,7 +810,7 @@ async function buildLibrarianCandidates(
785
810
  });
786
811
 
787
812
  // Final fallback: the active session model is known to be servable.
788
- if (ctx.model) {
813
+ if (ctx.model && isModelInScope(ctx, ctx.model)) {
789
814
  push(ctx.model, {
790
815
  modelRef: modelRef(ctx.model),
791
816
  provider: ctx.model.provider,
@@ -797,7 +822,11 @@ async function buildLibrarianCandidates(
797
822
  }
798
823
 
799
824
  if (candidates.length === 0) {
800
- throw new Error("No authenticated models are available for Librarian. Log in or configure an API key first.");
825
+ throw new Error(
826
+ hasModelScope(ctx)
827
+ ? "No authenticated models are available for Librarian in the current session model scope. Adjust the scope, log in, or configure an API key."
828
+ : "No authenticated models are available for Librarian. Log in or configure an API key first.",
829
+ );
801
830
  }
802
831
 
803
832
  return candidates;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-librarian",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
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",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-minimal-footer",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "A minimal custom footer for pi.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-notify",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "A pi extension that sends a notification when the agent is ready for input.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-openai-fast",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "A pi extension that enables OpenAI Codex Fast mode for ChatGPT-auth GPT-5.4, GPT-5.5, and GPT-5.6 Codex variants by injecting the priority service tier.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -106,6 +106,8 @@ Tool-call parameters still win over these defaults. `auto` clears the configured
106
106
  - `thinkingLevel` - optional reasoning/thinking override; falls back to the `/oracle thinking` default, then built-in defaults
107
107
  - `cwd` - optional working directory override
108
108
 
109
+ When the current Pi session has a non-empty model scope, Oracle limits automatic model selection and automatic fallbacks to that scope. An explicit tool or `/oracle model` override remains authoritative even when it is outside the session scope.
110
+
109
111
  ## Notes
110
112
 
111
113
  - The oracle is intentionally **read-only by default**.
@@ -20,6 +20,31 @@ type PiModel = {
20
20
  thinkingLevelMap?: ThinkingLevelMap;
21
21
  };
22
22
 
23
+ type ModelSelectionContext = {
24
+ model?: PiModel;
25
+ modelRegistry: { getAvailable(): PiModel[] | Promise<PiModel[]> };
26
+ scopedModels?: ReadonlyArray<{ model: PiModel; thinkingLevel?: ThinkingLevel }>;
27
+ };
28
+
29
+ function modelKey(model: PiModel): string {
30
+ return `${model.provider}/${model.id}`.toLowerCase();
31
+ }
32
+
33
+ function hasModelScope(ctx: ModelSelectionContext): boolean {
34
+ return Array.isArray(ctx.scopedModels) && ctx.scopedModels.length > 0;
35
+ }
36
+
37
+ function isModelInScope(ctx: ModelSelectionContext, model: PiModel): boolean {
38
+ if (!hasModelScope(ctx)) return true;
39
+ const key = modelKey(model);
40
+ return ctx.scopedModels!.some((entry) => modelKey(entry.model) === key);
41
+ }
42
+
43
+ async function getAutoSelectableModels(ctx: ModelSelectionContext): Promise<PiModel[]> {
44
+ const available = await ctx.modelRegistry.getAvailable();
45
+ return hasModelScope(ctx) ? available.filter((model) => isModelInScope(ctx, model)) : available;
46
+ }
47
+
23
48
  interface UsageStats {
24
49
  input: number;
25
50
  output: number;
@@ -82,6 +107,7 @@ const ORACLE_CONFIG_FILE = "oracle.json";
82
107
 
83
108
  const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
84
109
  "amazon-bedrock": [
110
+ "claude-opus-5",
85
111
  "claude-fable-5",
86
112
  "claude-opus-4-8",
87
113
  "claude-opus-4-7",
@@ -101,6 +127,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
101
127
  "zai.glm-5",
102
128
  ],
103
129
  anthropic: [
130
+ "claude-opus-5",
104
131
  "claude-fable-5",
105
132
  "claude-opus-4-8",
106
133
  "claude-opus-4-7",
@@ -125,13 +152,8 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
125
152
  "gpt-5-chat-latest",
126
153
  "gpt-5.2-pro",
127
154
  "gpt-5.2",
128
- "gpt-5.2-codex",
129
- "gpt-5.1-codex-max",
130
- "gpt-5.1-codex",
131
155
  "gpt-5.1",
132
- "gpt-5.1-chat-latest",
133
156
  "o3-pro",
134
- "o3-deep-research",
135
157
  "o1-pro",
136
158
  "gpt-5.4-mini",
137
159
  "gpt-5-mini",
@@ -166,8 +188,6 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
166
188
  "accounts/fireworks/models/glm-5p2",
167
189
  "accounts/fireworks/routers/glm-5p2-fast",
168
190
  "accounts/fireworks/models/minimax-m3",
169
- "accounts/fireworks/models/glm-5p1",
170
- "accounts/fireworks/routers/glm-5p1-fast",
171
191
  "accounts/fireworks/models/kimi-k2p6",
172
192
  "accounts/fireworks/routers/kimi-k2p6-turbo",
173
193
  "accounts/fireworks/routers/kimi-k2p6-fast",
@@ -177,6 +197,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
177
197
  "accounts/fireworks/models/gpt-oss-20b",
178
198
  ],
179
199
  "github-copilot": [
200
+ "claude-opus-5",
180
201
  "claude-fable-5",
181
202
  "claude-opus-4.8",
182
203
  "claude-opus-4.7",
@@ -266,13 +287,8 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
266
287
  "gpt-5-chat-latest",
267
288
  "gpt-5.2-pro",
268
289
  "gpt-5.2",
269
- "gpt-5.2-codex",
270
- "gpt-5.1-codex-max",
271
- "gpt-5.1-codex",
272
290
  "gpt-5.1",
273
- "gpt-5.1-chat-latest",
274
291
  "o3-pro",
275
- "o3-deep-research",
276
292
  "o1-pro",
277
293
  "gpt-5.4-mini",
278
294
  "gpt-5-mini",
@@ -287,6 +303,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
287
303
  "gpt-5.3-codex-spark",
288
304
  ],
289
305
  opencode: [
306
+ "claude-opus-5",
290
307
  "claude-fable-5",
291
308
  "gpt-5.5-pro",
292
309
  "gpt-5.5",
@@ -328,6 +345,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
328
345
  "kimi-k2.6",
329
346
  ],
330
347
  openrouter: [
348
+ "anthropic/claude-opus-5",
331
349
  "anthropic/claude-fable-5",
332
350
  "~anthropic/claude-fable-latest",
333
351
  "anthropic/claude-opus-4.8",
@@ -401,6 +419,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
401
419
  "google/gemma-4-31B-it",
402
420
  ],
403
421
  "vercel-ai-gateway": [
422
+ "anthropic/claude-opus-5",
404
423
  "anthropic/claude-fable-5",
405
424
  "anthropic/claude-opus-4.8",
406
425
  "anthropic/claude-opus-4.7",
@@ -877,7 +896,7 @@ function appendThinkingLevelClampReason(
877
896
  }
878
897
 
879
898
  async function findAvailableModel(
880
- ctx: { model?: PiModel; modelRegistry: { getAvailable(): PiModel[] | Promise<PiModel[]> } },
899
+ ctx: ModelSelectionContext,
881
900
  modelRef: string,
882
901
  ): Promise<PiModel | undefined> {
883
902
  const available = await ctx.modelRegistry.getAvailable();
@@ -923,11 +942,11 @@ function toOracleSelection(
923
942
  }
924
943
 
925
944
  function buildSessionFallbackSelection(
926
- ctx: { model?: PiModel },
945
+ ctx: ModelSelectionContext,
927
946
  thinkingLevelOverride: ThinkingLevel | undefined,
928
947
  ): OracleSelection | undefined {
929
948
  const model = ctx.model;
930
- if (!model) return undefined;
949
+ if (!model || !isModelInScope(ctx, model)) return undefined;
931
950
  return toOracleSelection(
932
951
  model,
933
952
  thinkingLevelOverride,
@@ -943,14 +962,16 @@ function withFallbackReason(selection: OracleSelection, previous: OracleSelectio
943
962
  }
944
963
 
945
964
  async function selectOracleModel(
946
- ctx: { model?: PiModel; modelRegistry: { getAvailable(): PiModel[] | Promise<PiModel[]> } },
965
+ ctx: ModelSelectionContext,
947
966
  thinkingLevelOverride?: ThinkingLevel,
948
967
  ): Promise<{ ok: true; selection: OracleSelection; ordered: OracleSelection[] } | { ok: false; error: string }> {
949
- const available = await ctx.modelRegistry.getAvailable();
968
+ const available = await getAutoSelectableModels(ctx);
950
969
  if (available.length === 0) {
951
970
  return {
952
971
  ok: false,
953
- error: "No authenticated models are available. Log in or configure an API key first.",
972
+ error: hasModelScope(ctx)
973
+ ? "No authenticated models are available in the current session model scope. Adjust the scope, log in, or configure an API key."
974
+ : "No authenticated models are available. Log in or configure an API key first.",
954
975
  };
955
976
  }
956
977
 
@@ -1257,6 +1278,7 @@ type OracleExtensionDeps = {
1257
1278
  };
1258
1279
 
1259
1280
  export const __test__ = {
1281
+ buildSessionFallbackSelection,
1260
1282
  createOracleExtension,
1261
1283
  findAvailableModel,
1262
1284
  isModelAvailabilityError,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-oracle",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "description": "An Amp-style oracle extension for pi that consults the strongest reasoning model on your current provider.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-permission-gate",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "A pi extension that prompts before dangerous bash commands and protected file writes.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-quiet-tools",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "A pi extension that visually compacts collapsed built-in tool rows in the TUI without changing tool results sent to the model.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-review",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "A standalone pi extension that adds /review and /end-review commands adapted from mitsuhiko/agent-stuff.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-todo",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "A pi extension that adds a branch-aware todo tool and /todos viewer.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.0
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-triage-comments",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "A pi extension that adds /triage-comments and a read-only triage_comments subagent tool for review-comment triage.",
5
5
  "keywords": [
6
6
  "pi-package",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-extensions",
3
- "version": "0.1.60",
3
+ "version": "0.1.62",
4
4
  "description": "A collection of pi extensions and skills for annotation UIs, context management, workflow audits, contrarian review, review-comment triage, notifications, brrr push alerts, safety guards, GitHub research, repo-local knowledge, todos, tool rendering, model/provider helpers, and Xiaohei-style article illustrations.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -72,9 +72,9 @@
72
72
  ]
73
73
  },
74
74
  "devDependencies": {
75
- "@earendil-works/pi-ai": "^0.82.0",
76
- "@earendil-works/pi-coding-agent": "^0.82.0",
77
- "@earendil-works/pi-tui": "^0.82.0",
75
+ "@earendil-works/pi-ai": "^0.83.0",
76
+ "@earendil-works/pi-coding-agent": "^0.83.0",
77
+ "@earendil-works/pi-tui": "^0.83.0",
78
78
  "@types/node": "^25.9.1",
79
79
  "husky": "^9.1.7",
80
80
  "typebox": "^1.3.6",