@diegopetrucci/pi-extensions 0.1.67 → 0.1.69
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/extensions/code-reviewer/index.ts +181 -2
- package/extensions/code-reviewer/package.json +1 -1
- package/extensions/context-inspector/index.ts +84 -5
- package/extensions/context-inspector/package.json +1 -1
- package/extensions/contrarian/index.ts +151 -14
- package/extensions/contrarian/package.json +1 -1
- package/extensions/dynamic-context-pruning/README.md +25 -16
- package/extensions/dynamic-context-pruning/index.ts +146 -29
- package/extensions/dynamic-context-pruning/package.json +1 -1
- package/extensions/oracle/index.ts +125 -15
- package/extensions/oracle/package.json +1 -1
- package/package.json +4 -4
|
@@ -75,6 +75,54 @@ export interface MinimalSessionEntry {
|
|
|
75
75
|
[key: string]: unknown;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
export interface MinimalProjectedSessionEntry {
|
|
79
|
+
sourceEntry: MinimalSessionEntry;
|
|
80
|
+
messages: MinimalMessage[];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface MinimalSessionProjection {
|
|
84
|
+
entries: MinimalProjectedSessionEntry[];
|
|
85
|
+
messages?: MinimalMessage[];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
89
|
+
return typeof value === "object" && value !== null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function isMinimalMessage(value: unknown): value is MinimalMessage {
|
|
93
|
+
return isRecord(value) && typeof value.role === "string" && value.role.length > 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function isProjectionSourceEntry(value: unknown): value is MinimalSessionEntry {
|
|
97
|
+
return isRecord(value)
|
|
98
|
+
&& typeof value.type === "string"
|
|
99
|
+
&& value.type.length > 0
|
|
100
|
+
&& typeof value.id === "string"
|
|
101
|
+
&& value.id.length > 0;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function normalizeSessionProjection(value: unknown): MinimalSessionProjection | undefined {
|
|
105
|
+
if (!isRecord(value) || !Array.isArray(value.entries)) return undefined;
|
|
106
|
+
|
|
107
|
+
const entries: MinimalProjectedSessionEntry[] = [];
|
|
108
|
+
for (const candidate of value.entries) {
|
|
109
|
+
if (!isRecord(candidate) || !isProjectionSourceEntry(candidate.sourceEntry) || !Array.isArray(candidate.messages)) return undefined;
|
|
110
|
+
if (!candidate.messages.every(isMinimalMessage)) return undefined;
|
|
111
|
+
entries.push({
|
|
112
|
+
sourceEntry: candidate.sourceEntry,
|
|
113
|
+
messages: candidate.messages,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
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 };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function sessionProjectionToMessages(projection: MinimalSessionProjection): MinimalMessage[] {
|
|
123
|
+
return projection.messages ?? projection.entries.flatMap((entry) => entry.messages);
|
|
124
|
+
}
|
|
125
|
+
|
|
78
126
|
// ============================================================================
|
|
79
127
|
// Constants
|
|
80
128
|
// ============================================================================
|
|
@@ -114,6 +162,22 @@ const DEFAULT_CACHED_PRICE_RATIO = 0.1;
|
|
|
114
162
|
*/
|
|
115
163
|
const DEFAULT_BREAK_EVEN_THRESHOLD = 22;
|
|
116
164
|
|
|
165
|
+
/**
|
|
166
|
+
* Default mid_loop break-even threshold (pe-zy4s follow-up). The package's own
|
|
167
|
+
* representative-corpus benchmark (turn-START state definition — the same one
|
|
168
|
+
* the runtime `context` handler uses) favored rejecting mid_loop candidates
|
|
169
|
+
* at cachedPriceRatio r=0.1 (mid_loop-optimal T=1, total realized
|
|
170
|
+
* net benefit ~0.0). This supports a conservative default for the measured
|
|
171
|
+
* corpus, not a universal claim that mid_loop pruning cannot pay off.
|
|
172
|
+
* The reported 2026-09-08 byte-level capture (a mid_loop prune at T=22
|
|
173
|
+
* invalidating 81% of a warm request prefix) illustrates cache invalidation,
|
|
174
|
+
* not an inability to amortize it: enough subsequent calls can recoup a
|
|
175
|
+
* one-time bust. Idle keeps T=22; turn-start classification does not imply
|
|
176
|
+
* the provider cache is cold. Users who want the old behavior can override
|
|
177
|
+
* via gate.breakEvenThresholdByState.mid_loop.
|
|
178
|
+
*/
|
|
179
|
+
const DEFAULT_MID_LOOP_BREAK_EVEN_THRESHOLD = 1;
|
|
180
|
+
|
|
117
181
|
/** Tool names never pruned by default: orchestration/subagent-style tools and todo-like state. */
|
|
118
182
|
const DEFAULT_PROTECTED_TOOL_NAMES = [
|
|
119
183
|
"todo",
|
|
@@ -222,20 +286,19 @@ export type GateMode = "on" | "off" | "always-apply";
|
|
|
222
286
|
* turn-START "idle" population (calls made at the start of a turn) carries
|
|
223
287
|
* essentially ALL the realized net benefit (idle-optimal T=22, total realized
|
|
224
288
|
* net benefit ~20,594 token-units), while "mid_loop" carries essentially NONE
|
|
225
|
-
* (mid_loop-optimal T=1, total realized net benefit ~0.0
|
|
226
|
-
* candidate is ever worth accepting at r=0.1 under this definition). This is
|
|
289
|
+
* (mid_loop-optimal T=1, total realized net benefit ~0.0 on this corpus). This is
|
|
227
290
|
* the OPPOSITE of what the pe-c5n9 evidence (measured under the old,
|
|
228
291
|
* non-runtime-observable turn-END definition) suggested.
|
|
229
292
|
*
|
|
230
293
|
* Per the pe-zy4s decision rule (stricter idle default only if idle itself is
|
|
231
294
|
* shown to be ~worthless): idle is NOT worthless here -- it is the ONLY state
|
|
232
|
-
* carrying value at r=0.1 -- so
|
|
233
|
-
*
|
|
234
|
-
* (
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
295
|
+
* carrying value at r=0.1 -- so the idle default stays at
|
|
296
|
+
* DEFAULT_BREAK_EVEN_THRESHOLD. The mid_loop default, by the same benchmark,
|
|
297
|
+
* is now DEFAULT_MID_LOOP_BREAK_EVEN_THRESHOLD (=1): the measured candidates
|
|
298
|
+
* did not justify a more permissive threshold at r=0.1. Mid_loop pruning can
|
|
299
|
+
* still amortize over enough subsequent calls; state alone does not establish
|
|
300
|
+
* cache warmth or profitability. Both defaults remain config-overridable via
|
|
301
|
+
* gate.breakEvenThresholdByState.
|
|
239
302
|
*/
|
|
240
303
|
export type AgentState = "idle" | "mid_loop";
|
|
241
304
|
|
|
@@ -285,7 +348,7 @@ export function defaultConfig(): DynamicContextPruningConfig {
|
|
|
285
348
|
mode: "on",
|
|
286
349
|
cachedPriceRatio: DEFAULT_CACHED_PRICE_RATIO,
|
|
287
350
|
breakEvenThreshold: DEFAULT_BREAK_EVEN_THRESHOLD,
|
|
288
|
-
breakEvenThresholdByState: { idle: DEFAULT_BREAK_EVEN_THRESHOLD, mid_loop:
|
|
351
|
+
breakEvenThresholdByState: { idle: DEFAULT_BREAK_EVEN_THRESHOLD, mid_loop: DEFAULT_MID_LOOP_BREAK_EVEN_THRESHOLD },
|
|
289
352
|
},
|
|
290
353
|
};
|
|
291
354
|
}
|
|
@@ -684,13 +747,7 @@ export function findToolCallPairIndices(
|
|
|
684
747
|
return pair;
|
|
685
748
|
}
|
|
686
749
|
|
|
687
|
-
/**
|
|
688
|
-
* Fallback correlation for decisions that reference a session entry rather
|
|
689
|
-
* than a toolCallId (reserved for future non-tool-call strategies). Zips
|
|
690
|
-
* message-producing session entries against the current context projection
|
|
691
|
-
* by position; entry/message counts are expected to match in the common
|
|
692
|
-
* case, but this degrades gracefully otherwise.
|
|
693
|
-
*/
|
|
750
|
+
/** Map raw session-entry provenance to projected message positions when Pi exposes it. */
|
|
694
751
|
function expectedRoleForEntry(entry: MinimalSessionEntry): string | undefined {
|
|
695
752
|
if (entry.type === "message") return entry.message?.role;
|
|
696
753
|
if (entry.type === "custom_message") return "custom";
|
|
@@ -699,15 +756,53 @@ function expectedRoleForEntry(entry: MinimalSessionEntry): string | undefined {
|
|
|
699
756
|
return undefined;
|
|
700
757
|
}
|
|
701
758
|
|
|
702
|
-
|
|
759
|
+
function buildProjectedEntryIdToMessageIndexMap(
|
|
703
760
|
entries: MinimalSessionEntry[],
|
|
704
761
|
messages: MinimalMessage[],
|
|
762
|
+
projection: MinimalSessionProjection,
|
|
705
763
|
): Map<string, number> {
|
|
706
764
|
const map = new Map<string, number>();
|
|
765
|
+
const entryIds = new Set(entries.map((entry) => entry.id));
|
|
766
|
+
let cursor = 0;
|
|
767
|
+
|
|
768
|
+
for (const projectedEntry of projection.entries) {
|
|
769
|
+
let firstMessageIndex: number | undefined;
|
|
770
|
+
for (const projectedMessage of projectedEntry.messages) {
|
|
771
|
+
let messageIndex = messages.indexOf(projectedMessage, cursor);
|
|
772
|
+
if (messageIndex < cursor) messageIndex = -1;
|
|
773
|
+
|
|
774
|
+
if (messageIndex < 0 && typeof projectedMessage.timestamp === "number") {
|
|
775
|
+
messageIndex = messages.findIndex(
|
|
776
|
+
(candidate, index) =>
|
|
777
|
+
index >= cursor && candidate.role === projectedMessage.role && candidate.timestamp === projectedMessage.timestamp,
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
if (messageIndex < 0) continue;
|
|
781
|
+
firstMessageIndex ??= messageIndex;
|
|
782
|
+
cursor = messageIndex + 1;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
if (firstMessageIndex !== undefined && entryIds.has(projectedEntry.sourceEntry.id)) {
|
|
786
|
+
map.set(projectedEntry.sourceEntry.id, firstMessageIndex);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
return map;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
export function buildEntryIdToMessageIndexMap(
|
|
793
|
+
entries: MinimalSessionEntry[],
|
|
794
|
+
messages: MinimalMessage[],
|
|
795
|
+
projection?: MinimalSessionProjection,
|
|
796
|
+
): Map<string, number> {
|
|
797
|
+
if (projection) return buildProjectedEntryIdToMessageIndexMap(entries, messages, projection);
|
|
798
|
+
|
|
799
|
+
// Older runtimes expose only raw branch entries; preserve their positional
|
|
800
|
+
// correlation and use role/timestamp only when the counts diverge.
|
|
707
801
|
const producing = entries.filter((entry) =>
|
|
708
802
|
entry.type === "message" || entry.type === "custom_message" || entry.type === "branch_summary" || entry.type === "compaction",
|
|
709
803
|
);
|
|
710
804
|
const length = Math.min(producing.length, messages.length);
|
|
805
|
+
const map = new Map<string, number>();
|
|
711
806
|
for (let i = 0; i < length; i++) {
|
|
712
807
|
const entry = producing[i];
|
|
713
808
|
const expectedRole = expectedRoleForEntry(entry);
|
|
@@ -732,15 +827,21 @@ export function findMessageIndexByRoleAndTimestamp(
|
|
|
732
827
|
return index >= 0 ? index : undefined;
|
|
733
828
|
}
|
|
734
829
|
|
|
735
|
-
/** Resolve a session entry id
|
|
830
|
+
/** Resolve a session entry id using projection provenance, then legacy role+timestamp fallback. */
|
|
736
831
|
export function resolveMessageIndexForEntry(
|
|
737
832
|
entryId: string,
|
|
738
833
|
entries: MinimalSessionEntry[],
|
|
739
834
|
messages: MinimalMessage[],
|
|
835
|
+
projection?: MinimalSessionProjection,
|
|
740
836
|
): number | undefined {
|
|
741
|
-
const zipMap = buildEntryIdToMessageIndexMap(entries, messages);
|
|
837
|
+
const zipMap = buildEntryIdToMessageIndexMap(entries, messages, projection);
|
|
742
838
|
const zipped = zipMap.get(entryId);
|
|
743
839
|
if (zipped !== undefined) return zipped;
|
|
840
|
+
|
|
841
|
+
// An empty projected contribution is an explicit omission/replacement state;
|
|
842
|
+
// only a validated projection may make that context-edit-aware decision.
|
|
843
|
+
if (projection?.entries.some((entry) => entry.sourceEntry.id === entryId && entry.messages.length === 0)) return undefined;
|
|
844
|
+
|
|
744
845
|
const entry = entries.find((candidate) => candidate.id === entryId);
|
|
745
846
|
if (!entry) return undefined;
|
|
746
847
|
return findMessageIndexByRoleAndTimestamp(entry, messages);
|
|
@@ -2157,8 +2258,22 @@ export function rebuildDecisionStateFromEntries(entries: MinimalSessionEntry[]):
|
|
|
2157
2258
|
// /prune picker helpers (pe-8re9)
|
|
2158
2259
|
// ============================================================================
|
|
2159
2260
|
|
|
2160
|
-
|
|
2161
|
-
|
|
2261
|
+
function resolveSessionProjection(ctx: ExtensionContext): MinimalSessionProjection | undefined {
|
|
2262
|
+
try {
|
|
2263
|
+
const sessionManager = ctx.sessionManager as unknown as { buildSessionProjection?: () => unknown };
|
|
2264
|
+
const buildProjection = sessionManager.buildSessionProjection;
|
|
2265
|
+
if (typeof buildProjection !== "function") return undefined;
|
|
2266
|
+
return normalizeSessionProjection(buildProjection.call(sessionManager));
|
|
2267
|
+
} catch {
|
|
2268
|
+
return undefined;
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
export function sessionEntriesToMessages(
|
|
2273
|
+
entries: MinimalSessionEntry[],
|
|
2274
|
+
projection?: MinimalSessionProjection,
|
|
2275
|
+
): MinimalMessage[] {
|
|
2276
|
+
if (projection) return sessionProjectionToMessages(projection);
|
|
2162
2277
|
const messages: MinimalMessage[] = [];
|
|
2163
2278
|
for (const entry of entries) {
|
|
2164
2279
|
if (entry.type === "message" && entry.message) messages.push(entry.message);
|
|
@@ -2273,7 +2388,7 @@ export function formatPrunableItemDetail(item: PrunableItem, cost?: CacheCostMod
|
|
|
2273
2388
|
|
|
2274
2389
|
/** Plain-text report used both by the non-UI /prune fallback and as a debugging aid. */
|
|
2275
2390
|
export function formatPrunableItemsReport(items: PrunableItem[]): string[] {
|
|
2276
|
-
if (items.length === 0) return ["No prunable tool results found in
|
|
2391
|
+
if (items.length === 0) return ["No prunable tool results found in the current model context."];
|
|
2277
2392
|
return items.map((item, index) => `${index + 1}. ${formatPrunableItemOption(item)}`);
|
|
2278
2393
|
}
|
|
2279
2394
|
|
|
@@ -2410,7 +2525,7 @@ export function formatStatusReport(input: StatusReportInput): string[] {
|
|
|
2410
2525
|
const lines: string[] = [];
|
|
2411
2526
|
lines.push(`Dynamic context pruning: ${config.enabled ? "ENABLED" : "disabled"}`);
|
|
2412
2527
|
lines.push(
|
|
2413
|
-
`Gate: mode=${config.gate.mode}
|
|
2528
|
+
`Gate: mode=${config.gate.mode} thresholds={idle:${resolveBreakEvenThreshold(config.gate, "idle")}, mid_loop:${resolveBreakEvenThreshold(config.gate, "mid_loop")}} cachedPriceRatio=${config.gate.cachedPriceRatio}`,
|
|
2414
2529
|
);
|
|
2415
2530
|
lines.push("Strategies:");
|
|
2416
2531
|
lines.push(` dedupe: ${config.strategies.dedupe.enabled ? "on" : "off"}`);
|
|
@@ -2581,7 +2696,8 @@ export default function dynamicContextPruningExtension(pi: ExtensionAPI) {
|
|
|
2581
2696
|
description: "Review, manually prune, and restore prunable tool results (interactive picker in TUI/RPC)",
|
|
2582
2697
|
handler: async (_args, ctx) => {
|
|
2583
2698
|
const branchEntries = ctx.sessionManager.getBranch() as unknown as MinimalSessionEntry[];
|
|
2584
|
-
const
|
|
2699
|
+
const projection = resolveSessionProjection(ctx);
|
|
2700
|
+
const messages = sessionEntriesToMessages(branchEntries, projection);
|
|
2585
2701
|
const activeByToolCallId = buildActiveResultDecisionMap(persistedDecisions);
|
|
2586
2702
|
const restoredToolCallIds = new Set(
|
|
2587
2703
|
Array.from(lastDecisionByKey.values())
|
|
@@ -2591,19 +2707,20 @@ export default function dynamicContextPruningExtension(pi: ExtensionAPI) {
|
|
|
2591
2707
|
const items = buildPrunableItems(messages, activeByToolCallId, restoredToolCallIds);
|
|
2592
2708
|
|
|
2593
2709
|
if (!ctx.hasUI) {
|
|
2594
|
-
notify(ctx, ["/prune: no interactive UI available. Prunable tool results in
|
|
2710
|
+
notify(ctx, ["/prune: no interactive UI available. Prunable tool results in the current model context:", "", ...formatPrunableItemsReport(items)].join("\n"), "info");
|
|
2595
2711
|
return;
|
|
2596
2712
|
}
|
|
2597
2713
|
|
|
2598
2714
|
if (items.length === 0) {
|
|
2599
|
-
notify(ctx, "No prunable tool results found in
|
|
2715
|
+
notify(ctx, "No prunable tool results found in the current model context.", "info");
|
|
2600
2716
|
return;
|
|
2601
2717
|
}
|
|
2602
2718
|
|
|
2603
2719
|
const doneLabel = "Done";
|
|
2604
2720
|
while (true) {
|
|
2605
2721
|
const latestBranch = ctx.sessionManager.getBranch() as unknown as MinimalSessionEntry[];
|
|
2606
|
-
const
|
|
2722
|
+
const latestProjection = resolveSessionProjection(ctx);
|
|
2723
|
+
const latestMessages = sessionEntriesToMessages(latestBranch, latestProjection);
|
|
2607
2724
|
const latestActive = buildActiveResultDecisionMap(persistedDecisions);
|
|
2608
2725
|
const latestRestoredToolCallIds = new Set(
|
|
2609
2726
|
Array.from(lastDecisionByKey.values())
|
|
@@ -2612,7 +2729,7 @@ export default function dynamicContextPruningExtension(pi: ExtensionAPI) {
|
|
|
2612
2729
|
);
|
|
2613
2730
|
const latestItems = buildPrunableItems(latestMessages, latestActive, latestRestoredToolCallIds);
|
|
2614
2731
|
if (latestItems.length === 0) {
|
|
2615
|
-
notify(ctx, "No prunable tool results found in
|
|
2732
|
+
notify(ctx, "No prunable tool results found in the current model context.", "info");
|
|
2616
2733
|
return;
|
|
2617
2734
|
}
|
|
2618
2735
|
|
|
@@ -107,6 +107,13 @@ const ORACLE_CONFIG_FILE = "oracle.json";
|
|
|
107
107
|
|
|
108
108
|
const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
109
109
|
"amazon-bedrock": [
|
|
110
|
+
"global.anthropic.claude-opus-5-5 ",
|
|
111
|
+
"au.anthropic.claude-opus-5-5 ",
|
|
112
|
+
"eu.anthropic.claude-opus-5-5 ",
|
|
113
|
+
"jp.anthropic.claude-opus-5-5 ",
|
|
114
|
+
"us.anthropic.claude-opus-5-5 ",
|
|
115
|
+
"anthropic.claude-opus-5-5 ",
|
|
116
|
+
"claude-opus-5-5",
|
|
110
117
|
"claude-opus-5",
|
|
111
118
|
"claude-fable-5",
|
|
112
119
|
"claude-opus-4-8",
|
|
@@ -115,6 +122,10 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
115
122
|
"claude-opus-4-5",
|
|
116
123
|
"claude-opus-4-1",
|
|
117
124
|
"claude-opus-4",
|
|
125
|
+
"global.openai.gpt-6-astra ",
|
|
126
|
+
"us.openai.gpt-6-astra ",
|
|
127
|
+
"openai.gpt-6-astra ",
|
|
128
|
+
"gpt-6-astra",
|
|
118
129
|
"openai.gpt-5.6-sol",
|
|
119
130
|
"openai.gpt-5.6-terra",
|
|
120
131
|
"openai.gpt-5.6-luna",
|
|
@@ -123,7 +134,6 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
123
134
|
"claude-sonnet-4-5",
|
|
124
135
|
"claude-sonnet-4",
|
|
125
136
|
"deepseek.v3.2",
|
|
126
|
-
"deepseek.r1",
|
|
127
137
|
"kimi-k2.5",
|
|
128
138
|
"minimax-m2.5",
|
|
129
139
|
"minimax-m2.1",
|
|
@@ -131,6 +141,8 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
131
141
|
"xai.grok-4.6",
|
|
132
142
|
],
|
|
133
143
|
anthropic: [
|
|
144
|
+
"claude-opus-5-5 ",
|
|
145
|
+
"claude-opus-5-5",
|
|
134
146
|
"claude-opus-5",
|
|
135
147
|
"claude-fable-5",
|
|
136
148
|
"claude-opus-4-8",
|
|
@@ -143,8 +155,14 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
143
155
|
],
|
|
144
156
|
"ant-ling": ["Ling-2.6-1T", "Ling-2.6-flash"],
|
|
145
157
|
"azure-openai-responses": [
|
|
158
|
+
"gpt-6-astra ",
|
|
159
|
+
"gpt-6-astra",
|
|
160
|
+
"gpt-6-sol ",
|
|
161
|
+
"gpt-6-sol",
|
|
146
162
|
"gpt-5.6-sol",
|
|
147
163
|
"gpt-5.6-terra",
|
|
164
|
+
"gpt-6-luna ",
|
|
165
|
+
"gpt-6-luna",
|
|
148
166
|
"gpt-5.6-luna",
|
|
149
167
|
"gpt-5.5-pro",
|
|
150
168
|
"gpt-5.5",
|
|
@@ -181,7 +199,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
181
199
|
"zai-org/GLM-5.2-Fast",
|
|
182
200
|
"zai-org/GLM-4.7",
|
|
183
201
|
],
|
|
184
|
-
cerebras: ["gpt-oss-120b", "
|
|
202
|
+
cerebras: ["gpt-oss-120b", "qwen-3.8-27b"],
|
|
185
203
|
"cloudflare-ai-gateway": [
|
|
186
204
|
"claude-fable-5",
|
|
187
205
|
"claude-opus-5",
|
|
@@ -189,6 +207,8 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
189
207
|
"claude-opus-4.7",
|
|
190
208
|
"claude-opus-4.6",
|
|
191
209
|
"claude-opus-4.5",
|
|
210
|
+
"gpt-6-astra ",
|
|
211
|
+
"gpt-6-astra",
|
|
192
212
|
"gpt-5.5",
|
|
193
213
|
"gpt-5.4",
|
|
194
214
|
"workers-ai/@cf/moonshotai/kimi-k2.7-code",
|
|
@@ -212,7 +232,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
212
232
|
"@cf/qwen/qwen3-30b-a3b-fp8",
|
|
213
233
|
"@cf/zai-org/glm-4.7-flash",
|
|
214
234
|
],
|
|
215
|
-
deepseek: ["deepseek-v4-pro", "deepseek-
|
|
235
|
+
deepseek: ["deepseek-v4-pro", "deepseek-flash"],
|
|
216
236
|
fireworks: [
|
|
217
237
|
"accounts/fireworks/models/kimi-k3",
|
|
218
238
|
"accounts/fireworks/routers/kimi-k3-fast",
|
|
@@ -226,13 +246,20 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
226
246
|
"accounts/fireworks/models/gpt-oss-120b",
|
|
227
247
|
],
|
|
228
248
|
"github-copilot": [
|
|
249
|
+
"claude-opus-5.5 ",
|
|
250
|
+
"claude-opus-5.5",
|
|
229
251
|
"claude-opus-5",
|
|
230
252
|
"claude-fable-5",
|
|
231
253
|
"claude-opus-4.8",
|
|
232
254
|
"claude-opus-4.7",
|
|
255
|
+
"gpt-6-astra ",
|
|
233
256
|
"gpt-6-astra",
|
|
257
|
+
"gpt-6-sol ",
|
|
258
|
+
"gpt-6-sol",
|
|
234
259
|
"gpt-5.6-sol",
|
|
235
260
|
"gpt-5.6-terra",
|
|
261
|
+
"gpt-6-luna ",
|
|
262
|
+
"gpt-6-luna",
|
|
236
263
|
"gpt-5.6-luna",
|
|
237
264
|
"gpt-5.5",
|
|
238
265
|
"gpt-5.4",
|
|
@@ -241,6 +268,10 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
241
268
|
"claude-sonnet-5",
|
|
242
269
|
"claude-sonnet-4.6",
|
|
243
270
|
"gpt-5-mini",
|
|
271
|
+
"grok-4.7 ",
|
|
272
|
+
"grok-4.7",
|
|
273
|
+
"grok-4.6",
|
|
274
|
+
"grok-4.5",
|
|
244
275
|
"kimi-k3",
|
|
245
276
|
"kimi-k2.7-code",
|
|
246
277
|
],
|
|
@@ -285,6 +316,13 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
285
316
|
"Qwen/Qwen3-Coder-Next",
|
|
286
317
|
],
|
|
287
318
|
"kimi-coding": ["k3", "kimi-for-coding", "kimi-for-coding-highspeed"],
|
|
319
|
+
meta: [
|
|
320
|
+
"muse-spark-1.3",
|
|
321
|
+
"muse-spark-1.3-contributor",
|
|
322
|
+
"muse-spark-1.2",
|
|
323
|
+
"muse-spark-1.2-contributor",
|
|
324
|
+
"muse-spark-1.1",
|
|
325
|
+
],
|
|
288
326
|
minimax: ["MiniMax-M3", "MiniMax-M2.7-highspeed", "MiniMax-M2.7"],
|
|
289
327
|
"minimax-cn": ["MiniMax-M3", "MiniMax-M2.7-highspeed", "MiniMax-M2.7"],
|
|
290
328
|
mistral: [
|
|
@@ -301,9 +339,14 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
301
339
|
"zai-glm-5-2",
|
|
302
340
|
],
|
|
303
341
|
openai: [
|
|
342
|
+
"gpt-6-astra ",
|
|
304
343
|
"gpt-6-astra",
|
|
344
|
+
"gpt-6-sol ",
|
|
345
|
+
"gpt-6-sol",
|
|
305
346
|
"gpt-5.6-sol",
|
|
306
347
|
"gpt-5.6-terra",
|
|
348
|
+
"gpt-6-luna ",
|
|
349
|
+
"gpt-6-luna",
|
|
307
350
|
"gpt-5.6-luna",
|
|
308
351
|
"gpt-5.5-pro",
|
|
309
352
|
"gpt-5.5",
|
|
@@ -321,18 +364,32 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
321
364
|
"gpt-5-mini",
|
|
322
365
|
],
|
|
323
366
|
"openai-codex": [
|
|
367
|
+
"gpt-6-astra ",
|
|
324
368
|
"gpt-6-astra",
|
|
369
|
+
"gpt-6-sol ",
|
|
370
|
+
"gpt-6-sol",
|
|
325
371
|
"gpt-5.6-sol",
|
|
326
372
|
"gpt-5.6-terra",
|
|
373
|
+
"gpt-6-luna ",
|
|
374
|
+
"gpt-6-luna",
|
|
327
375
|
"gpt-5.6-luna",
|
|
328
376
|
"gpt-5.5",
|
|
329
|
-
"gpt-5.4",
|
|
330
|
-
"gpt-5.4-mini",
|
|
331
377
|
"gpt-5.3-codex-spark",
|
|
332
378
|
],
|
|
333
379
|
opencode: [
|
|
380
|
+
"claude-opus-5-5 ",
|
|
381
|
+
"claude-opus-5-5",
|
|
334
382
|
"claude-opus-5",
|
|
335
383
|
"claude-fable-5",
|
|
384
|
+
"gpt-6-astra ",
|
|
385
|
+
"gpt-6-astra",
|
|
386
|
+
"gpt-6-sol ",
|
|
387
|
+
"gpt-6-sol",
|
|
388
|
+
"gpt-5.6-sol",
|
|
389
|
+
"gpt-5.6-terra",
|
|
390
|
+
"gpt-6-luna ",
|
|
391
|
+
"gpt-6-luna",
|
|
392
|
+
"gpt-5.6-luna",
|
|
336
393
|
"gpt-5.5-pro",
|
|
337
394
|
"gpt-5.5",
|
|
338
395
|
"gpt-5.4-pro",
|
|
@@ -344,6 +401,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
344
401
|
"gpt-5.3-codex",
|
|
345
402
|
"gpt-5.2-codex",
|
|
346
403
|
"gpt-5.1-codex",
|
|
404
|
+
"grok-4.6",
|
|
347
405
|
"grok-4.5",
|
|
348
406
|
"gemini-3.5-flash",
|
|
349
407
|
"gemini-3.1-pro",
|
|
@@ -374,8 +432,13 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
374
432
|
"kimi-k3",
|
|
375
433
|
"kimi-k2.7-code",
|
|
376
434
|
"kimi-k2.6",
|
|
435
|
+
"grok-4.7 ",
|
|
436
|
+
"grok-4.7",
|
|
437
|
+
"grok-4.6",
|
|
377
438
|
],
|
|
378
439
|
openrouter: [
|
|
440
|
+
"anthropic/claude-opus-5.5 ",
|
|
441
|
+
"anthropic/claude-opus-5.5",
|
|
379
442
|
"anthropic/claude-opus-5",
|
|
380
443
|
"anthropic/claude-fable-5",
|
|
381
444
|
"~anthropic/claude-fable-latest",
|
|
@@ -384,15 +447,21 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
384
447
|
"anthropic/claude-opus-4.6",
|
|
385
448
|
"anthropic/claude-opus-4.5",
|
|
386
449
|
"anthropic/claude-opus-4.1",
|
|
450
|
+
"openai/gpt-6-astra ",
|
|
451
|
+
"openai/gpt-6-astra",
|
|
452
|
+
"openai/gpt-6-sol ",
|
|
453
|
+
"openai/gpt-6-sol",
|
|
387
454
|
"openai/gpt-5.6-sol-pro",
|
|
388
455
|
"openai/gpt-5.6-sol",
|
|
389
456
|
"openai/gpt-5.6-terra-pro",
|
|
390
457
|
"openai/gpt-5.6-terra",
|
|
458
|
+
"openai/gpt-6-luna ",
|
|
459
|
+
"openai/gpt-6-luna",
|
|
391
460
|
"openai/gpt-5.6-luna-pro",
|
|
392
461
|
"openai/gpt-5.6-luna",
|
|
393
462
|
"openai/gpt-5.5-pro",
|
|
394
463
|
"openai/gpt-5.5",
|
|
395
|
-
"~openai/gpt-latest",
|
|
464
|
+
"~openai/gpt-astra-latest",
|
|
396
465
|
"google/gemini-3.1-pro-preview-customtools",
|
|
397
466
|
"google/gemini-3.1-pro-preview",
|
|
398
467
|
"~google/gemini-pro-latest",
|
|
@@ -403,6 +472,9 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
403
472
|
"~moonshotai/kimi-latest",
|
|
404
473
|
"moonshotai/kimi-k2.6",
|
|
405
474
|
"moonshotai/kimi-k2-thinking",
|
|
475
|
+
"x-ai/grok-4.7 ",
|
|
476
|
+
"x-ai/grok-4.7",
|
|
477
|
+
"x-ai/grok-4.6",
|
|
406
478
|
"x-ai/grok-4.5",
|
|
407
479
|
"~x-ai/grok-latest",
|
|
408
480
|
"deepseek/deepseek-v4-pro",
|
|
@@ -444,6 +516,34 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
444
516
|
"glm-5.2",
|
|
445
517
|
"deepseek-v4-flash-0731",
|
|
446
518
|
],
|
|
519
|
+
radius: [
|
|
520
|
+
"gpt-6-astra ",
|
|
521
|
+
"gpt-6-astra",
|
|
522
|
+
"claude-opus-5-5 ",
|
|
523
|
+
"claude-opus-5-5",
|
|
524
|
+
"claude-opus-5",
|
|
525
|
+
"gpt-6-sol ",
|
|
526
|
+
"gpt-6-sol",
|
|
527
|
+
"gpt-5.6-sol",
|
|
528
|
+
"gpt-5.6-terra",
|
|
529
|
+
"gpt-6-luna ",
|
|
530
|
+
"gpt-6-luna",
|
|
531
|
+
"gpt-5.6-luna",
|
|
532
|
+
"claude-fable-5-1",
|
|
533
|
+
"claude-fable-5",
|
|
534
|
+
"claude-opus-4-8",
|
|
535
|
+
"claude-sonnet-5",
|
|
536
|
+
"gpt-5.5",
|
|
537
|
+
"gpt-5.4",
|
|
538
|
+
"gpt-5.3-codex",
|
|
539
|
+
"kimi-k3",
|
|
540
|
+
"deepseek-v4-pro",
|
|
541
|
+
"glm-5.3",
|
|
542
|
+
"kimi-k2.7-code",
|
|
543
|
+
"deepseek-v4.1-flash",
|
|
544
|
+
"gpt-5.4-mini",
|
|
545
|
+
"glm-5.3-flash",
|
|
546
|
+
],
|
|
447
547
|
together: [
|
|
448
548
|
"deepseek-ai/DeepSeek-V4-Pro",
|
|
449
549
|
"moonshotai/Kimi-K3",
|
|
@@ -461,6 +561,8 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
461
561
|
"google/gemma-4-31B-it",
|
|
462
562
|
],
|
|
463
563
|
"vercel-ai-gateway": [
|
|
564
|
+
"anthropic/claude-opus-5.5 ",
|
|
565
|
+
"anthropic/claude-opus-5.5",
|
|
464
566
|
"anthropic/claude-opus-5",
|
|
465
567
|
"anthropic/claude-fable-5",
|
|
466
568
|
"anthropic/claude-opus-4.8",
|
|
@@ -469,8 +571,14 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
469
571
|
"anthropic/claude-opus-4.5",
|
|
470
572
|
"anthropic/claude-sonnet-5",
|
|
471
573
|
"anthropic/claude-sonnet-4.6",
|
|
574
|
+
"openai/gpt-6-astra ",
|
|
575
|
+
"openai/gpt-6-astra",
|
|
576
|
+
"openai/gpt-6-sol ",
|
|
577
|
+
"openai/gpt-6-sol",
|
|
472
578
|
"openai/gpt-5.6-sol",
|
|
473
579
|
"openai/gpt-5.6-terra",
|
|
580
|
+
"openai/gpt-6-luna ",
|
|
581
|
+
"openai/gpt-6-luna",
|
|
474
582
|
"openai/gpt-5.6-luna",
|
|
475
583
|
"openai/gpt-5.5-pro",
|
|
476
584
|
"openai/gpt-5.5",
|
|
@@ -495,6 +603,9 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
495
603
|
"google/gemini-3.1-pro-preview",
|
|
496
604
|
"google/gemini-3.5-flash",
|
|
497
605
|
"google/gemini-3-flash",
|
|
606
|
+
"spacexai/grok-4.7 ",
|
|
607
|
+
"spacexai/grok-4.7",
|
|
608
|
+
"spacexai/grok-4.6",
|
|
498
609
|
"xai/grok-4.5",
|
|
499
610
|
"xai/grok-4.20-reasoning",
|
|
500
611
|
"minimax/minimax-m3",
|
|
@@ -503,6 +614,8 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
503
614
|
"zai/glm-5.1",
|
|
504
615
|
],
|
|
505
616
|
xai: [
|
|
617
|
+
"grok-4.7 ",
|
|
618
|
+
"grok-4.7",
|
|
506
619
|
"grok-4.6",
|
|
507
620
|
"grok-4.5",
|
|
508
621
|
"grok-4.3",
|
|
@@ -515,9 +628,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
515
628
|
"nvidia/nemotron-3-ultra-550b-a55b",
|
|
516
629
|
"nvidia/nemotron-3-super-120b-a12b",
|
|
517
630
|
"moonshotai/kimi-k3",
|
|
518
|
-
"deepseek-ai/deepseek-v4-pro-0813",
|
|
519
631
|
"moonshotai/kimi-k2.6",
|
|
520
|
-
"minimaxai/minimax-m3",
|
|
521
632
|
"nvidia/nemotron-3-nano-omni-30b-a3b-reasoning",
|
|
522
633
|
],
|
|
523
634
|
zai: [
|
|
@@ -528,13 +639,12 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
528
639
|
],
|
|
529
640
|
"zai-coding-cn": [
|
|
530
641
|
"glm-5.3",
|
|
531
|
-
"glm-5.
|
|
532
|
-
"glm-5-
|
|
533
|
-
"glm-
|
|
534
|
-
"glm-4.7",
|
|
642
|
+
"glm-5.3-highspeed",
|
|
643
|
+
"glm-5.3-flash",
|
|
644
|
+
"glm-4.6v",
|
|
535
645
|
],
|
|
536
|
-
moonshotai: ["kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed", "kimi-k2.6"
|
|
537
|
-
"moonshotai-cn": ["kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed", "kimi-k2.6"
|
|
646
|
+
moonshotai: ["kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed", "kimi-k2.6"],
|
|
647
|
+
"moonshotai-cn": ["kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed", "kimi-k2.6"],
|
|
538
648
|
};
|
|
539
649
|
|
|
540
650
|
const ORACLE_SYSTEM_PROMPT = [
|
|
@@ -913,7 +1023,7 @@ function clampThinkingLevel(model: PiModel, requested: ThinkingLevel): ThinkingL
|
|
|
913
1023
|
|
|
914
1024
|
function isGpt56SolModel(model: PiModel | undefined): boolean {
|
|
915
1025
|
if (!model) return false;
|
|
916
|
-
return /\bgpt-5\.6-sol\b/i.test(`${model.id} ${model.name ?? ""}`);
|
|
1026
|
+
return /\bgpt-(?:5\.6|6)-sol\b/i.test(`${model.id} ${model.name ?? ""}`);
|
|
917
1027
|
}
|
|
918
1028
|
|
|
919
1029
|
function resolveThinkingLevel(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@diegopetrucci/pi-extensions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.69",
|
|
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",
|
|
@@ -71,9 +71,9 @@
|
|
|
71
71
|
]
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
|
-
"@earendil-works/pi-ai": "^0.
|
|
75
|
-
"@earendil-works/pi-coding-agent": "^0.
|
|
76
|
-
"@earendil-works/pi-tui": "^0.
|
|
74
|
+
"@earendil-works/pi-ai": "^0.87.1",
|
|
75
|
+
"@earendil-works/pi-coding-agent": "^0.87.1",
|
|
76
|
+
"@earendil-works/pi-tui": "^0.87.1",
|
|
77
77
|
"@types/node": "^25.9.1",
|
|
78
78
|
"husky": "^9.1.7",
|
|
79
79
|
"typebox": "^1.3.6",
|