@diegopetrucci/pi-extensions 0.1.27 → 0.1.28

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
@@ -11,7 +11,7 @@ A collection of [pi](https://github.com/earendil-works/pi-mono) agent extensions
11
11
  - [`minimal-footer`](./extensions/minimal-footer): Replaces pi's built-in footer with a minimal configurable two-line layout: branch/repo on the first line, context/model on the second, optional `DUMB ZONE`, plus OpenAI Codex 5-hour and 7-day usage when available.
12
12
  - [`notify`](./extensions/notify): Sends configurable terminal, desktop, bell, and sound notifications when pi finishes and is ready for input.
13
13
  - [`openai-fast`](./extensions/openai-fast): Adds `/fast` to enable OpenAI Codex Fast mode for ChatGPT-auth GPT-5.4 and GPT-5.5 by injecting the priority service tier.
14
- - [`oracle`](./extensions/oracle): Adds an Amp-style read-only oracle tool that auto-selects the strongest reasoning model on the current provider/subscription, covers pi’s built-in providers with hardcoded rankings, sets reasoning to xhigh by default, and shows live status while running.
14
+ - [`oracle`](./extensions/oracle): Adds an Amp-style read-only oracle tool that auto-selects the strongest reasoning model on the current provider/subscription, covers pi’s built-in providers (including Together) with hardcoded rankings, requests xhigh reasoning by default and clamps to model capabilities, and shows live status while running.
15
15
  - [`permission-gate`](./extensions/permission-gate): Prompts for confirmation before dangerous bash commands like `rm -rf`, `sudo`, and `chmod 777`.
16
16
  - [`quiet-tools`](./extensions/quiet-tools): Renders collapsed built-in tool rows as a one-line invocation plus an expand hint without changing model-visible tool results; toggle temporarily with `/quiet-tools`.
17
17
  - [`todo`](./extensions/todo): Adds a branch-aware `todo` tool for the agent and a `/todos` viewer for users.
@@ -11,7 +11,7 @@ It adds an `oracle` tool that spins up a separate read-only pi subprocess and se
11
11
  - creates an isolated read-only subprocess
12
12
  - auto-picks the strongest reasoning model on the current provider
13
13
  - uses provider-specific hardcoded rankings first, then a heuristic fallback
14
- - sets reasoning/thinking to `xhigh` by default for reasoning models
14
+ - requests `xhigh` by default for reasoning models, then clamps to the model-supported thinking level
15
15
  - defaults to `read,grep,find,ls`
16
16
  - can optionally allow non-mutating `bash` inspection
17
17
  - shows a live oracle status line and widget while the subprocess is running
@@ -27,7 +27,7 @@ By default, the extension:
27
27
  4. tries a provider-specific hardcoded priority list first
28
28
  5. falls back to a heuristic that favors stronger tiers like `opus`, `pro`, newer versions, and penalizes `mini`, `flash`, `haiku`, `spark`, etc.
29
29
 
30
- The hardcoded rankings now cover pi's built-in provider set, including OpenAI/Codex, Anthropic, Google variants, GitHub Copilot, Bedrock, Azure OpenAI Responses, Cloudflare, DeepSeek, Fireworks, Groq, Hugging Face, Kimi/Moonshot, MiniMax, Mistral, OpenCode, OpenRouter, Vercel AI Gateway, xAI, ZAI, and Cerebras.
30
+ The hardcoded rankings now cover pi's built-in provider set, including Together; see the provider matrix for the current provider-by-provider top picks.
31
31
 
32
32
  If no reasoning model exists on the current provider, it falls back to the best available model on that provider.
33
33
 
@@ -35,9 +35,9 @@ If no reasoning model exists on the current provider, it falls back to the best
35
35
 
36
36
  Yes — the extension explicitly sets the oracle reasoning level.
37
37
 
38
- - reasoning models default to `xhigh`
38
+ - reasoning models request `xhigh` by default, then use the Pi-compatible effective thinking level supported by the matched model
39
39
  - non-reasoning models default to `off`
40
- - you can override it with the tool's optional `thinkingLevel` parameter
40
+ - you can override it with the tool's optional `thinkingLevel` parameter; matched models still clamp unsupported overrides and report the effective level
41
41
 
42
42
  Use `/oracle-model` inside pi to see what it would pick right now.
43
43
 
@@ -7,6 +7,7 @@ import { Container, Markdown, Spacer, Text } from "@earendil-works/pi-tui";
7
7
  import { Type } from "typebox";
8
8
 
9
9
  type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
10
+ type ThinkingLevelMap = Partial<Record<ThinkingLevel, unknown | null>>;
10
11
 
11
12
  type PiModel = {
12
13
  provider: string;
@@ -15,6 +16,7 @@ type PiModel = {
15
16
  reasoning?: boolean;
16
17
  contextWindow?: number;
17
18
  maxTokens?: number;
19
+ thinkingLevelMap?: ThinkingLevelMap;
18
20
  };
19
21
 
20
22
  interface UsageStats {
@@ -33,6 +35,8 @@ interface OracleSelection {
33
35
  modelId: string;
34
36
  modelName?: string;
35
37
  thinkingLevel: ThinkingLevel;
38
+ requestedThinkingLevel?: ThinkingLevel;
39
+ thinkingLevelClamped?: boolean;
36
40
  autoSelected: boolean;
37
41
  selectionReason: string;
38
42
  }
@@ -294,6 +298,20 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
294
298
  "minimax/minimax-m2.1",
295
299
  "z-ai/glm-5.1",
296
300
  ],
301
+ together: [
302
+ "deepseek-ai/DeepSeek-V4-Pro",
303
+ "zai-org/GLM-5.1",
304
+ "moonshotai/Kimi-K2.6",
305
+ "Qwen/Qwen3.6-Plus",
306
+ "MiniMaxAI/MiniMax-M2.7",
307
+ "Qwen/Qwen3.5-397B-A17B",
308
+ "Qwen/Qwen3-Coder-Next-FP8",
309
+ "Qwen/Qwen3-235B-A22B-Instruct-2507-tput",
310
+ "openai/gpt-oss-120b",
311
+ "moonshotai/Kimi-K2.5",
312
+ "deepseek-ai/DeepSeek-V3-1",
313
+ "MiniMaxAI/MiniMax-M2.5",
314
+ ],
297
315
  "vercel-ai-gateway": [
298
316
  "anthropic/claude-opus-4.7",
299
317
  "anthropic/claude-opus-4.6",
@@ -393,7 +411,7 @@ const OracleParams = Type.Object({
393
411
  thinkingLevel: Type.Optional(
394
412
  StringEnum(THINKING_LEVELS, {
395
413
  description:
396
- "Optional reasoning level override for the oracle subprocess. Default: xhigh for reasoning models, off for non-reasoning models.",
414
+ "Optional reasoning level override for the oracle subprocess. Defaults request xhigh for reasoning models and off for non-reasoning models, then clamp to matched model capabilities.",
397
415
  }),
398
416
  ),
399
417
  cwd: Type.Optional(Type.String({ description: "Optional working directory for the oracle subprocess." })),
@@ -530,9 +548,48 @@ function withThinking(modelRef: string, thinkingLevel: ThinkingLevel): string {
530
548
  return `${modelRef}:${thinkingLevel}`;
531
549
  }
532
550
 
533
- function resolveThinkingLevel(model: PiModel | undefined, override: ThinkingLevel | undefined): ThinkingLevel {
534
- if (override) return override;
535
- return model?.reasoning ? DEFAULT_THINKING_LEVEL : "off";
551
+ // Keep these local so the extension stays compatible with older pi peer installs that do not export clamp helpers.
552
+ function isThinkingLevelSupported(model: PiModel, level: ThinkingLevel): boolean {
553
+ if (!model.reasoning) return level === "off";
554
+
555
+ const map = model.thinkingLevelMap;
556
+ if (level === "xhigh") {
557
+ return !!map && Object.prototype.hasOwnProperty.call(map, "xhigh") && map.xhigh != null;
558
+ }
559
+ return map?.[level] !== null;
560
+ }
561
+
562
+ function clampThinkingLevel(model: PiModel, requested: ThinkingLevel): ThinkingLevel {
563
+ if (isThinkingLevelSupported(model, requested)) return requested;
564
+
565
+ const requestedIndex = THINKING_LEVELS.indexOf(requested);
566
+ for (let index = requestedIndex + 1; index < THINKING_LEVELS.length; index++) {
567
+ const level = THINKING_LEVELS[index];
568
+ if (isThinkingLevelSupported(model, level)) return level;
569
+ }
570
+ for (let index = requestedIndex - 1; index >= 0; index--) {
571
+ const level = THINKING_LEVELS[index];
572
+ if (isThinkingLevelSupported(model, level)) return level;
573
+ }
574
+
575
+ return "off";
576
+ }
577
+
578
+ function resolveThinkingLevel(
579
+ model: PiModel | undefined,
580
+ override: ThinkingLevel | undefined,
581
+ ): { requested: ThinkingLevel; effective: ThinkingLevel; clamped: boolean } {
582
+ const requested = override ?? (model?.reasoning ? DEFAULT_THINKING_LEVEL : "off");
583
+ const effective = model ? clampThinkingLevel(model, requested) : requested;
584
+ return { requested, effective, clamped: effective !== requested };
585
+ }
586
+
587
+ function appendThinkingLevelClampReason(
588
+ reason: string,
589
+ resolution: { requested: ThinkingLevel; effective: ThinkingLevel; clamped: boolean },
590
+ ): string {
591
+ if (!resolution.clamped) return reason;
592
+ return `${reason} Requested thinking level ${resolution.requested} was clamped to ${resolution.effective} based on the matched model's capabilities.`;
536
593
  }
537
594
 
538
595
  async function findAvailableModel(
@@ -599,9 +656,11 @@ async function selectOracleModel(
599
656
 
600
657
  const preferred = selectPreferredModel(candidates, providerForPreferences);
601
658
  const winner = preferred ?? [...candidates].sort((a, b) => rankModel(b) - rankModel(a))[0];
602
- const selectionReason = preferred
603
- ? `Selected ${winner.id} via the hardcoded preference list for ${winner.provider}.`
604
- : reason;
659
+ const thinking = resolveThinkingLevel(winner, thinkingLevelOverride);
660
+ const selectionReason = appendThinkingLevelClampReason(
661
+ preferred ? `Selected ${winner.id} via the hardcoded preference list for ${winner.provider}.` : reason,
662
+ thinking,
663
+ );
605
664
 
606
665
  return {
607
666
  ok: true,
@@ -610,7 +669,8 @@ async function selectOracleModel(
610
669
  provider: winner.provider,
611
670
  modelId: winner.id,
612
671
  modelName: winner.name,
613
- thinkingLevel: resolveThinkingLevel(winner, thinkingLevelOverride),
672
+ thinkingLevel: thinking.effective,
673
+ ...(thinking.clamped ? { requestedThinkingLevel: thinking.requested, thinkingLevelClamped: true } : {}),
614
674
  autoSelected: true,
615
675
  selectionReason,
616
676
  },
@@ -867,7 +927,7 @@ export default function oracleExtension(pi: ExtensionAPI) {
867
927
  "Use this tool sparingly when you want a second opinion, deeper analysis, code review, debugging help, or a higher-reasoning pass.",
868
928
  "Do not use it for routine low-value work; it is slower than the main agent.",
869
929
  "The oracle is read-only by default. Set includeBash only when shell-based inspection is genuinely useful.",
870
- "The oracle sets thinking to xhigh by default for reasoning models, unless the tool call explicitly overrides thinkingLevel.",
930
+ "The oracle requests xhigh by default for reasoning models; defaults and explicit thinkingLevel overrides are clamped to the effective model-supported level when the model is matched.",
871
931
  ],
872
932
  parameters: OracleParams,
873
933
 
@@ -888,16 +948,21 @@ export default function oracleExtension(pi: ExtensionAPI) {
888
948
  const provider =
889
949
  matched?.provider ?? (modelRef.includes("/") ? modelRef.split("/")[0] : ctx.model?.provider ?? "unknown");
890
950
  const modelId = matched?.id ?? (modelRef.includes("/") ? modelRef.split("/").slice(1).join("/") : modelRef);
951
+ const thinking = resolveThinkingLevel(matched, params.thinkingLevel);
952
+ const selectionReason = matched
953
+ ? appendThinkingLevelClampReason("Used the explicit model override provided in the tool call.", thinking)
954
+ : "Used the explicit model override provided in the tool call. The model was not matched against the authenticated model list, so the reasoning level fallback was applied.";
891
955
  selection = {
892
956
  modelRef: matched ? `${matched.provider}/${matched.id}` : modelRef,
893
957
  provider,
894
958
  modelId,
895
959
  modelName: matched?.name,
896
- thinkingLevel: resolveThinkingLevel(matched, params.thinkingLevel),
960
+ thinkingLevel: thinking.effective,
961
+ ...(matched && thinking.clamped
962
+ ? { requestedThinkingLevel: thinking.requested, thinkingLevelClamped: true }
963
+ : {}),
897
964
  autoSelected: false,
898
- selectionReason: matched
899
- ? "Used the explicit model override provided in the tool call."
900
- : "Used the explicit model override provided in the tool call. The model was not matched against the authenticated model list, so the reasoning level fallback was applied.",
965
+ selectionReason,
901
966
  };
902
967
  } else {
903
968
  const selectionResult = await selectOracleModel(ctx, params.thinkingLevel);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-oracle",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "An Amp-style oracle extension for pi that consults the strongest reasoning model on your current provider.",
5
5
  "keywords": ["pi-package", "pi", "oracle", "reasoning", "subagent"],
6
6
  "license": "MIT",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-extensions",
3
- "version": "0.1.27",
3
+ "version": "0.1.28",
4
4
  "description": "A collection of pi extensions for context management, review-comment triage, notifications, safety guards, GitHub research, todos, tool rendering, and model/provider helpers.",
5
5
  "keywords": ["pi-package", "pi", "terminal", "agent"],
6
6
  "license": "MIT",