@diegopetrucci/pi-extensions 0.1.76 → 0.1.79
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 +8 -8
- package/extensions/agent-workflow-audit/README.md +1 -1
- package/extensions/agent-workflow-audit/index.ts +275 -59
- package/extensions/agent-workflow-audit/package.json +1 -1
- package/extensions/annotate-git-diff/git.ts +0 -8
- package/extensions/annotate-git-diff/glimpseui.d.ts +5 -65
- package/extensions/annotate-git-diff/package.json +1 -1
- package/extensions/annotate-last-message/glimpseui.d.ts +5 -65
- package/extensions/annotate-last-message/package.json +1 -1
- package/extensions/annotate-last-message/web/index.html +3 -30
- package/extensions/code-reviewer/README.md +5 -5
- package/extensions/code-reviewer/index.ts +252 -82
- package/extensions/code-reviewer/package.json +1 -1
- package/extensions/confirm-destructive/index.ts +2 -8
- package/extensions/confirm-destructive/package.json +1 -1
- package/extensions/context-cap/README.md +9 -9
- package/extensions/context-cap/index.ts +3 -3
- package/extensions/context-cap/package.json +2 -2
- package/extensions/context-inspector/index.ts +2 -46
- package/extensions/context-inspector/package.json +1 -1
- package/extensions/contrarian/README.md +4 -3
- package/extensions/contrarian/index.ts +48 -67
- package/extensions/contrarian/package.json +1 -1
- package/extensions/dirty-repo-guard/index.ts +0 -4
- package/extensions/dirty-repo-guard/package.json +1 -1
- package/extensions/dynamic-context-pruning/README.md +20 -11
- package/extensions/dynamic-context-pruning/index.ts +118 -341
- package/extensions/dynamic-context-pruning/package.json +1 -1
- package/extensions/fast/README.md +18 -11
- package/extensions/fast/index.ts +9 -5
- package/extensions/fast/package.json +1 -1
- package/extensions/git-footer/index.ts +2 -16
- package/extensions/git-footer/package.json +1 -1
- package/extensions/inline-bash/index.ts +0 -16
- package/extensions/inline-bash/package.json +1 -1
- package/extensions/librarian/README.md +1 -1
- package/extensions/librarian/index.ts +246 -70
- package/extensions/librarian/package.json +1 -1
- package/extensions/minimal-footer/README.md +5 -5
- package/extensions/minimal-footer/index.ts +5 -26
- package/extensions/minimal-footer/minimal-footer.example.json +1 -1
- package/extensions/minimal-footer/openai-usage.ts +2 -11
- package/extensions/minimal-footer/package.json +1 -1
- package/extensions/oracle/README.md +4 -3
- package/extensions/oracle/index.ts +46 -65
- package/extensions/oracle/package.json +1 -1
- package/extensions/permission-gate/README.md +3 -0
- package/extensions/permission-gate/index.ts +127 -36
- package/extensions/permission-gate/package.json +1 -1
- package/extensions/project-mcp-json/README.md +2 -2
- package/extensions/project-mcp-json/package.json +1 -1
- package/extensions/quiet-tools/.pi-fleet-tested-version +1 -1
- package/extensions/quiet-tools/README.md +30 -19
- package/extensions/quiet-tools/index.ts +233 -166
- package/extensions/quiet-tools/package.json +2 -2
- package/extensions/review/README.md +2 -0
- package/extensions/review/package.json +1 -1
- package/extensions/todo/index.ts +0 -19
- package/extensions/todo/package.json +1 -1
- package/extensions/triage-comments/README.md +4 -2
- package/extensions/triage-comments/index.ts +344 -39
- package/extensions/triage-comments/package.json +1 -1
- package/package.json +4 -4
|
@@ -143,7 +143,6 @@ type MinimalMessage = {
|
|
|
143
143
|
customType?: string;
|
|
144
144
|
display?: boolean;
|
|
145
145
|
summary?: string;
|
|
146
|
-
fromId?: string;
|
|
147
146
|
tokensBefore?: number;
|
|
148
147
|
command?: string;
|
|
149
148
|
output?: string;
|
|
@@ -165,7 +164,6 @@ type MinimalEntry = {
|
|
|
165
164
|
display?: boolean;
|
|
166
165
|
details?: unknown;
|
|
167
166
|
summary?: string;
|
|
168
|
-
fromId?: string;
|
|
169
167
|
tokensBefore?: number;
|
|
170
168
|
firstKeptEntryId?: string;
|
|
171
169
|
};
|
|
@@ -177,7 +175,6 @@ type MinimalProjectionEntry = {
|
|
|
177
175
|
|
|
178
176
|
type MinimalSessionProjection = {
|
|
179
177
|
entries: MinimalProjectionEntry[];
|
|
180
|
-
messages?: MinimalMessage[];
|
|
181
178
|
};
|
|
182
179
|
|
|
183
180
|
type AnalysisResult = {
|
|
@@ -192,7 +189,6 @@ type Segment = {
|
|
|
192
189
|
source: string;
|
|
193
190
|
role: string;
|
|
194
191
|
tokens: number;
|
|
195
|
-
chars: number;
|
|
196
192
|
turn: number;
|
|
197
193
|
sequence: number;
|
|
198
194
|
entryId?: string;
|
|
@@ -211,7 +207,6 @@ type Segment = {
|
|
|
211
207
|
type CategoryStat = {
|
|
212
208
|
id: CategoryId;
|
|
213
209
|
label: string;
|
|
214
|
-
shortLabel: string;
|
|
215
210
|
color: string;
|
|
216
211
|
description: string;
|
|
217
212
|
tokens: number;
|
|
@@ -230,13 +225,9 @@ type AggregateStat = {
|
|
|
230
225
|
|
|
231
226
|
type DatasetStats = {
|
|
232
227
|
tokens: number;
|
|
233
|
-
rawTokens: number;
|
|
234
|
-
visibleRawTokens: number;
|
|
235
228
|
excludedTokens: number;
|
|
236
229
|
providerDeltaTokens: number;
|
|
237
230
|
estimatorOverageTokens: number;
|
|
238
|
-
reconciliationScale: number;
|
|
239
|
-
segmentCount: number;
|
|
240
231
|
messageCount: number;
|
|
241
232
|
categories: CategoryStat[];
|
|
242
233
|
topSegments: Segment[];
|
|
@@ -247,8 +238,6 @@ type DatasetStats = {
|
|
|
247
238
|
|
|
248
239
|
type Dataset = {
|
|
249
240
|
id: DatasetId;
|
|
250
|
-
label: string;
|
|
251
|
-
description: string;
|
|
252
241
|
segments: Segment[];
|
|
253
242
|
stats: DatasetStats;
|
|
254
243
|
};
|
|
@@ -259,7 +248,6 @@ type RoutedModel = {
|
|
|
259
248
|
};
|
|
260
249
|
|
|
261
250
|
type ReportData = {
|
|
262
|
-
generatedAt: string;
|
|
263
251
|
cwd: string;
|
|
264
252
|
session: {
|
|
265
253
|
id?: string;
|
|
@@ -280,7 +268,6 @@ type ReportData = {
|
|
|
280
268
|
percent: number | null;
|
|
281
269
|
};
|
|
282
270
|
options: {
|
|
283
|
-
redacted: boolean;
|
|
284
271
|
defaultDataset: DatasetId;
|
|
285
272
|
};
|
|
286
273
|
categoryMeta: typeof CATEGORY_META;
|
|
@@ -507,7 +494,6 @@ function addTextSegment(
|
|
|
507
494
|
source: base.source,
|
|
508
495
|
role: base.role,
|
|
509
496
|
tokens,
|
|
510
|
-
chars,
|
|
511
497
|
turn: base.turn ?? state.turn,
|
|
512
498
|
entryId: base.entryId,
|
|
513
499
|
timestamp: base.timestamp,
|
|
@@ -543,7 +529,6 @@ function addImageSegment(
|
|
|
543
529
|
source: base.source,
|
|
544
530
|
role: base.role,
|
|
545
531
|
tokens: IMAGE_TOKEN_ESTIMATE,
|
|
546
|
-
chars: IMAGE_TOKEN_ESTIMATE * 4,
|
|
547
532
|
turn: base.turn ?? state.turn,
|
|
548
533
|
entryId: base.entryId,
|
|
549
534
|
timestamp: base.timestamp,
|
|
@@ -820,7 +805,6 @@ function contextEntryToMessage(entry: MinimalEntry): MinimalMessage | undefined
|
|
|
820
805
|
return {
|
|
821
806
|
role: "branchSummary",
|
|
822
807
|
summary: entry.summary,
|
|
823
|
-
fromId: entry.fromId,
|
|
824
808
|
timestamp: entry.timestamp ? new Date(entry.timestamp).getTime() : undefined,
|
|
825
809
|
};
|
|
826
810
|
}
|
|
@@ -916,10 +900,7 @@ function resolveSessionProjection(
|
|
|
916
900
|
(!Array.isArray(projection.messages) || !projection.messages.every(isProjectionMessage))
|
|
917
901
|
)
|
|
918
902
|
return undefined;
|
|
919
|
-
return {
|
|
920
|
-
entries,
|
|
921
|
-
messages: entries.flatMap((entry) => entry.messages),
|
|
922
|
-
};
|
|
903
|
+
return { entries };
|
|
923
904
|
}
|
|
924
905
|
|
|
925
906
|
function resolveCurrentContextEntries(
|
|
@@ -1045,8 +1026,6 @@ function aggregateBy(
|
|
|
1045
1026
|
|
|
1046
1027
|
function finalizeDataset(
|
|
1047
1028
|
id: DatasetId,
|
|
1048
|
-
label: string,
|
|
1049
|
-
description: string,
|
|
1050
1029
|
segments: Segment[],
|
|
1051
1030
|
messageCount: number,
|
|
1052
1031
|
authoritativeTokens: number | null,
|
|
@@ -1091,7 +1070,6 @@ function finalizeDataset(
|
|
|
1091
1070
|
return {
|
|
1092
1071
|
id: categoryId,
|
|
1093
1072
|
label: meta.label,
|
|
1094
|
-
shortLabel: meta.shortLabel,
|
|
1095
1073
|
color: meta.color,
|
|
1096
1074
|
description: meta.description,
|
|
1097
1075
|
tokens: Math.round(rawTokens),
|
|
@@ -1105,18 +1083,12 @@ function finalizeDataset(
|
|
|
1105
1083
|
|
|
1106
1084
|
return {
|
|
1107
1085
|
id,
|
|
1108
|
-
label,
|
|
1109
|
-
description,
|
|
1110
1086
|
segments,
|
|
1111
1087
|
stats: {
|
|
1112
1088
|
tokens: Math.round(displayTotal),
|
|
1113
|
-
rawTokens: Math.round(segments.reduce((sum, segment) => sum + segment.tokens, 0)),
|
|
1114
|
-
visibleRawTokens: Math.round(visibleRawTokens),
|
|
1115
1089
|
excludedTokens: Math.round(excludedTokens),
|
|
1116
1090
|
providerDeltaTokens,
|
|
1117
1091
|
estimatorOverageTokens,
|
|
1118
|
-
reconciliationScale,
|
|
1119
|
-
segmentCount: segments.length,
|
|
1120
1092
|
messageCount,
|
|
1121
1093
|
categories,
|
|
1122
1094
|
topSegments: [...visibleSegments].sort((a, b) => b.tokens - a.tokens).slice(0, 24),
|
|
@@ -1192,20 +1164,11 @@ function buildReportData(
|
|
|
1192
1164
|
const fullSegments = [...overhead, ...fullAnalysis.segments];
|
|
1193
1165
|
const currentDataset = finalizeDataset(
|
|
1194
1166
|
"current",
|
|
1195
|
-
"Current model context",
|
|
1196
|
-
"What pi is expected to send to the model on the next turn: latest compaction summary plus unsummarized messages, system prompt, and active tool schemas.",
|
|
1197
1167
|
currentSegments,
|
|
1198
1168
|
currentAnalysis.messageCount,
|
|
1199
1169
|
contextTokens,
|
|
1200
1170
|
);
|
|
1201
|
-
const fullDataset = finalizeDataset(
|
|
1202
|
-
"full",
|
|
1203
|
-
"Full active branch history",
|
|
1204
|
-
"Every context-bearing entry on the active branch, including pre-compaction history that may no longer be sent verbatim.",
|
|
1205
|
-
fullSegments,
|
|
1206
|
-
fullAnalysis.messageCount,
|
|
1207
|
-
null,
|
|
1208
|
-
);
|
|
1171
|
+
const fullDataset = finalizeDataset("full", fullSegments, fullAnalysis.messageCount, null);
|
|
1209
1172
|
|
|
1210
1173
|
const notes: string[] = [
|
|
1211
1174
|
"Per-component token counts are local estimates based mostly on characters/4. Providers expose aggregate usage, not exact per-message attribution.",
|
|
@@ -1250,7 +1213,6 @@ function buildReportData(
|
|
|
1250
1213
|
}
|
|
1251
1214
|
|
|
1252
1215
|
return {
|
|
1253
|
-
generatedAt: new Date().toISOString(),
|
|
1254
1216
|
cwd: options.redact ? "[redacted cwd]" : ctx.cwd,
|
|
1255
1217
|
session: {
|
|
1256
1218
|
id: options.redact ? "[redacted session]" : safeCall(() => ctx.sessionManager.getSessionId()),
|
|
@@ -1273,7 +1235,6 @@ function buildReportData(
|
|
|
1273
1235
|
percent: contextPercent,
|
|
1274
1236
|
},
|
|
1275
1237
|
options: {
|
|
1276
|
-
redacted: options.redact,
|
|
1277
1238
|
defaultDataset: options.defaultDataset,
|
|
1278
1239
|
},
|
|
1279
1240
|
categoryMeta: CATEGORY_META,
|
|
@@ -1368,16 +1329,12 @@ function buildHtml(data: ReportData): string {
|
|
|
1368
1329
|
color-scheme: dark;
|
|
1369
1330
|
--bg: #070a12;
|
|
1370
1331
|
--panel: rgba(15, 23, 42, 0.72);
|
|
1371
|
-
--panel-strong: rgba(15, 23, 42, 0.94);
|
|
1372
1332
|
--line: rgba(148, 163, 184, 0.18);
|
|
1373
1333
|
--text: #e5edf7;
|
|
1374
1334
|
--muted: #94a3b8;
|
|
1375
1335
|
--dim: #64748b;
|
|
1376
1336
|
--accent: #8b5cf6;
|
|
1377
1337
|
--accent-2: #06b6d4;
|
|
1378
|
-
--good: #22c55e;
|
|
1379
|
-
--warn: #f59e0b;
|
|
1380
|
-
--bad: #ef4444;
|
|
1381
1338
|
--shadow: 0 24px 80px rgba(0, 0, 0, 0.35);
|
|
1382
1339
|
}
|
|
1383
1340
|
* { box-sizing: border-box; }
|
|
@@ -1588,7 +1545,6 @@ pre { margin: 0; white-space: pre-wrap; word-break: break-word; color: #cbd5e1;
|
|
|
1588
1545
|
for (const [key, value] of Object.entries(attrs)) {
|
|
1589
1546
|
if (key === 'class') node.className = value;
|
|
1590
1547
|
else if (key === 'text') node.textContent = value;
|
|
1591
|
-
else if (key === 'html') node.innerHTML = value;
|
|
1592
1548
|
else if (key.startsWith('on') && typeof value === 'function') node.addEventListener(key.slice(2), value);
|
|
1593
1549
|
else if (value !== undefined && value !== null) node.setAttribute(key, value);
|
|
1594
1550
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# contrarian
|
|
2
2
|
|
|
3
|
-
A read-only
|
|
3
|
+
A contrarian subagent with read-only built-in tools and configured MCP tools for [pi](https://github.com/earendil-works/pi).
|
|
4
4
|
|
|
5
5
|
It adds a `contrarian` tool that spins up a separate pi subprocess to stress-test plans, designs, assumptions, bug hypotheses, review conclusions, and product directions by steelmanning the strongest credible opposing case.
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ This is adapted from the TLH [`contrarian` subagent prompt](https://github.com/d
|
|
|
8
8
|
|
|
9
9
|
## What it does
|
|
10
10
|
|
|
11
|
-
- creates an isolated read-only
|
|
11
|
+
- creates an isolated subprocess with read-only built-in tools (read, grep, find, ls) and any configured MCP tools
|
|
12
12
|
- prefers a strong model on the opposite model family/provider when available, then falls back to the best available model
|
|
13
13
|
- requests `high` reasoning by default for reasoning models, then clamps to the model-supported thinking level
|
|
14
14
|
- defaults to `read,grep,find,ls`, with optional non-mutating bash inspection
|
|
@@ -81,7 +81,8 @@ When the current Pi session has a non-empty model scope, Contrarian limits autom
|
|
|
81
81
|
|
|
82
82
|
## Notes
|
|
83
83
|
|
|
84
|
-
- The contrarian
|
|
84
|
+
- The contrarian subprocess uses read-only built-in tools (read, grep, find, ls) by default, plus any configured MCP tools. It can optionally include bash for non-mutating inspection.
|
|
85
|
+
- Configured MCP tools run without confirmation inside the subprocess and may have side effects — use them only for gathering evidence. MCP access requires host Pi >=1.0.4; older hosts degrade to no MCP, no error.
|
|
85
86
|
- It never implements fixes or produces patches.
|
|
86
87
|
- Its output separates confirmed objections, plausible concerns, and unresolved unknowns.
|
|
87
88
|
- It is best for adversarial review before committing to a plan, design, or conclusion.
|
|
@@ -102,8 +102,15 @@ interface ContrarianPreferences {
|
|
|
102
102
|
thinkingLevel?: ThinkingLevel;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
const
|
|
106
|
-
|
|
105
|
+
const SUBAGENT_MCP_TOOLS = [
|
|
106
|
+
"mcp__*",
|
|
107
|
+
"tool_search",
|
|
108
|
+
"list_mcp_resources",
|
|
109
|
+
"list_mcp_resource_templates",
|
|
110
|
+
"read_mcp_resource",
|
|
111
|
+
];
|
|
112
|
+
const READ_ONLY_TOOLS = ["read", "grep", "find", "ls", ...SUBAGENT_MCP_TOOLS];
|
|
113
|
+
const READ_ONLY_PLUS_BASH_TOOLS = ["read", "grep", "find", "ls", "bash", ...SUBAGENT_MCP_TOOLS];
|
|
107
114
|
const DEFAULT_THINKING_LEVEL: ThinkingLevel = "high";
|
|
108
115
|
const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"] as const;
|
|
109
116
|
const COLLAPSED_LINE_LIMIT = 8;
|
|
@@ -161,6 +168,33 @@ const CONTRARIAN_MODEL_PREFERENCES = [
|
|
|
161
168
|
"gemini-3.5-flash",
|
|
162
169
|
];
|
|
163
170
|
|
|
171
|
+
const AZURE_MODEL_PREFERENCES = [
|
|
172
|
+
"gpt-6-astra ",
|
|
173
|
+
"gpt-6-astra",
|
|
174
|
+
"gpt-6.1-sol ",
|
|
175
|
+
"gpt-6-sol ",
|
|
176
|
+
"gpt-6-sol",
|
|
177
|
+
"gpt-5.6-sol",
|
|
178
|
+
"gpt-5.6-terra",
|
|
179
|
+
"gpt-6-luna ",
|
|
180
|
+
"gpt-6-luna",
|
|
181
|
+
"gpt-5.6-luna",
|
|
182
|
+
"gpt-5.5-pro",
|
|
183
|
+
"gpt-5.5",
|
|
184
|
+
"gpt-5.4-pro",
|
|
185
|
+
"gpt-5.4 ",
|
|
186
|
+
"gpt-5.3-codex",
|
|
187
|
+
"gpt-5-pro",
|
|
188
|
+
"gpt-5-chat-latest",
|
|
189
|
+
"gpt-5.2-pro",
|
|
190
|
+
"gpt-5.2",
|
|
191
|
+
"gpt-5.1",
|
|
192
|
+
"o3-pro",
|
|
193
|
+
"o1-pro",
|
|
194
|
+
"gpt-5.4-mini",
|
|
195
|
+
"gpt-5-mini",
|
|
196
|
+
];
|
|
197
|
+
|
|
164
198
|
const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
165
199
|
"amazon-bedrock": [
|
|
166
200
|
"global.anthropic.claude-opus-5-5 ",
|
|
@@ -213,32 +247,9 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
213
247
|
"claude-sonnet-4-5",
|
|
214
248
|
],
|
|
215
249
|
"ant-ling": ["Ling-2.6-1T", "Ling-2.6-flash"],
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
"gpt-6.1-sol ",
|
|
220
|
-
"gpt-6-sol ",
|
|
221
|
-
"gpt-6-sol",
|
|
222
|
-
"gpt-5.6-sol",
|
|
223
|
-
"gpt-5.6-terra",
|
|
224
|
-
"gpt-6-luna ",
|
|
225
|
-
"gpt-6-luna",
|
|
226
|
-
"gpt-5.6-luna",
|
|
227
|
-
"gpt-5.5-pro",
|
|
228
|
-
"gpt-5.5",
|
|
229
|
-
"gpt-5.4-pro",
|
|
230
|
-
"gpt-5.4 ",
|
|
231
|
-
"gpt-5.3-codex",
|
|
232
|
-
"gpt-5-pro",
|
|
233
|
-
"gpt-5-chat-latest",
|
|
234
|
-
"gpt-5.2-pro",
|
|
235
|
-
"gpt-5.2",
|
|
236
|
-
"gpt-5.1",
|
|
237
|
-
"o3-pro",
|
|
238
|
-
"o1-pro",
|
|
239
|
-
"gpt-5.4-mini",
|
|
240
|
-
"gpt-5-mini",
|
|
241
|
-
],
|
|
250
|
+
azure: AZURE_MODEL_PREFERENCES,
|
|
251
|
+
// Legacy alias for Pi 1.0.0–1.0.2 hosts (renamed to `azure` in Pi 1.0.3).
|
|
252
|
+
"azure-openai-responses": AZURE_MODEL_PREFERENCES,
|
|
242
253
|
baseten: [
|
|
243
254
|
"moonshotai/Kimi-K3",
|
|
244
255
|
"moonshotai/Kimi-K2.7-Code ",
|
|
@@ -263,10 +274,6 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
263
274
|
"cloudflare-ai-gateway": [
|
|
264
275
|
"claude-fable-5",
|
|
265
276
|
"claude-opus-5",
|
|
266
|
-
"claude-opus-4.8",
|
|
267
|
-
"claude-opus-4.7",
|
|
268
|
-
"claude-opus-4.6",
|
|
269
|
-
"claude-opus-4.5",
|
|
270
277
|
"gpt-6-astra ",
|
|
271
278
|
"gpt-6-astra",
|
|
272
279
|
"gpt-5.5",
|
|
@@ -603,9 +610,7 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
603
610
|
"MiniMaxAI/MiniMax-M3",
|
|
604
611
|
"MiniMaxAI/MiniMax-M2.7",
|
|
605
612
|
"openai/gpt-oss-120b",
|
|
606
|
-
"openai/gpt-oss-20b",
|
|
607
613
|
"nvidia/nemotron-3-ultra-550b-a55b",
|
|
608
|
-
"google/gemma-4-31B-it",
|
|
609
614
|
],
|
|
610
615
|
typesafe: [],
|
|
611
616
|
"vercel-ai-gateway": [
|
|
@@ -681,13 +686,14 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
|
|
|
681
686
|
};
|
|
682
687
|
|
|
683
688
|
const CONTRARIAN_SYSTEM_PROMPT = [
|
|
684
|
-
"You are the Contrarian: an independent
|
|
689
|
+
"You are the Contrarian: an independent adversarial analyst with access to read-only built-in tools and any configured MCP tools.",
|
|
685
690
|
"Your job is to stress-test a proposal, plan, design, assumption, bug hypothesis, review conclusion, product direction, or decision by developing the strongest credible opposing case for the delegating primary agent.",
|
|
686
|
-
"
|
|
691
|
+
"Never modify files, create patches, install dependencies, change configuration, implement fixes, or delegate work to other agents. Your output is adversarial analysis, evidence, and recommendations only.",
|
|
687
692
|
"Use the available tools to inspect the repository and gather evidence. If bash is available, use it only for non-mutating inspection commands.",
|
|
693
|
+
"Configured MCP tools are available and run without confirmation inside this subagent. Use them only to gather evidence or context. Avoid side-effecting MCP actions unless the task explicitly asks for them.",
|
|
688
694
|
"Analysis process:",
|
|
689
695
|
"1. Identify the core claim, decision, or direction being challenged.",
|
|
690
|
-
"2. Gather the local
|
|
696
|
+
"2. Gather the local context needed to evaluate it accurately.",
|
|
691
697
|
"3. Steelman the strongest credible opposing position before judging whether it holds.",
|
|
692
698
|
"4. Identify hidden assumptions, failure modes, alternative interpretations, tradeoffs, and disconfirming evidence.",
|
|
693
699
|
"5. Clearly separate confirmed objections, plausible concerns, and unresolved unknowns.",
|
|
@@ -1814,15 +1820,16 @@ function createContrarianExtension(pi: ExtensionAPI, deps: ContrarianExtensionDe
|
|
|
1814
1820
|
name: "contrarian",
|
|
1815
1821
|
label: "Contrarian",
|
|
1816
1822
|
description:
|
|
1817
|
-
"Consult a separate read-only
|
|
1823
|
+
"Consult a separate contrarian subprocess with read-only built-in tools and configured MCP tools that stress-tests plans, designs, assumptions, bug hypotheses, and conclusions by steelmanning the strongest opposing case.",
|
|
1818
1824
|
promptSnippet:
|
|
1819
|
-
"Consult a
|
|
1825
|
+
"Consult a contrarian subprocess that prefers a strong opposite-family/provider model for adversarial analysis; exposes read-only built-in tools plus any configured MCP tools.",
|
|
1820
1826
|
promptGuidelines: [
|
|
1821
1827
|
"Use contrarian when you need to stress-test a proposal, implementation plan, design, assumption, bug hypothesis, review conclusion, or product direction.",
|
|
1822
1828
|
"Ask contrarian to steelman the strongest opposing case and separate confirmed objections, plausible concerns, and unresolved unknowns.",
|
|
1823
1829
|
"Do not use contrarian for routine low-value work; contrarian is slower than the main agent.",
|
|
1824
|
-
"The contrarian tool
|
|
1830
|
+
"The contrarian tool exposes read-only built-in tools (read, grep, find, and ls) plus any configured MCP tools; contrarian includeBash adds the bash inspection tool.",
|
|
1825
1831
|
"Set contrarian includeBash only when the extra bash inspection tool is genuinely useful; keep contrarian relying on read, grep, find, and ls otherwise.",
|
|
1832
|
+
"Configured MCP tools are available to contrarian and run without confirmation inside the subagent; they may have side effects — use them only for gathering evidence, not for state-changing actions unless the task explicitly asks. MCP access requires host Pi >=1.0.4; older hosts degrade to no MCP, no error.",
|
|
1826
1833
|
"The contrarian tool requests high by default for reasoning models; contrarian defaults and explicit thinkingLevel overrides are clamped to the effective model-supported level when the model is matched.",
|
|
1827
1834
|
],
|
|
1828
1835
|
parameters: ContrarianParams,
|
|
@@ -1937,8 +1944,7 @@ function createContrarianExtension(pi: ExtensionAPI, deps: ContrarianExtensionDe
|
|
|
1937
1944
|
onUpdate?.(partial);
|
|
1938
1945
|
};
|
|
1939
1946
|
|
|
1940
|
-
let
|
|
1941
|
-
for (let index = 0; index < attempts.length; index++) {
|
|
1947
|
+
for (let index = 0; ; index++) {
|
|
1942
1948
|
const previous = index > 0 ? attempts[index - 1] : undefined;
|
|
1943
1949
|
const attempt = previous
|
|
1944
1950
|
? withFallbackReason(attempts[index], previous)
|
|
@@ -1962,7 +1968,6 @@ function createContrarianExtension(pi: ExtensionAPI, deps: ContrarianExtensionDe
|
|
|
1962
1968
|
});
|
|
1963
1969
|
}
|
|
1964
1970
|
|
|
1965
|
-
lastErrorResult = result;
|
|
1966
1971
|
const canFallBack = index < attempts.length - 1 && isModelAvailabilityError(result.error);
|
|
1967
1972
|
if (!canFallBack) {
|
|
1968
1973
|
return finalizeResult({
|
|
@@ -1971,30 +1976,6 @@ function createContrarianExtension(pi: ExtensionAPI, deps: ContrarianExtensionDe
|
|
|
1971
1976
|
});
|
|
1972
1977
|
}
|
|
1973
1978
|
}
|
|
1974
|
-
|
|
1975
|
-
return finalizeResult({
|
|
1976
|
-
content: [
|
|
1977
|
-
{
|
|
1978
|
-
type: "text",
|
|
1979
|
-
text: lastErrorResult?.error ?? "Contrarian could not select an available model.",
|
|
1980
|
-
},
|
|
1981
|
-
],
|
|
1982
|
-
details: lastErrorResult?.details ?? {
|
|
1983
|
-
modelRef: "",
|
|
1984
|
-
provider: ctx.model?.provider ?? "unknown",
|
|
1985
|
-
modelId: "",
|
|
1986
|
-
modelName: undefined,
|
|
1987
|
-
thinkingLevel: configuredThinkingLevel ?? DEFAULT_THINKING_LEVEL,
|
|
1988
|
-
autoSelected: true,
|
|
1989
|
-
selectionReason: "No authenticated models were available to run the contrarian.",
|
|
1990
|
-
includeBash: params.includeBash ?? false,
|
|
1991
|
-
usage: createEmptyUsage(),
|
|
1992
|
-
stderr: "",
|
|
1993
|
-
exitCode: 1,
|
|
1994
|
-
durationMs: 0,
|
|
1995
|
-
cwd: params.cwd ?? ctx.cwd,
|
|
1996
|
-
},
|
|
1997
|
-
});
|
|
1998
1979
|
} finally {
|
|
1999
1980
|
activeRuns.delete(toolCallId);
|
|
2000
1981
|
updateContrarianUi(ctx, activeRuns);
|
|
@@ -12,11 +12,9 @@ async function checkDirtyRepo(
|
|
|
12
12
|
ctx: ExtensionContext,
|
|
13
13
|
action: string,
|
|
14
14
|
): Promise<{ cancel: boolean } | undefined> {
|
|
15
|
-
// Check for uncommitted changes
|
|
16
15
|
const { stdout, code } = await pi.exec("git", ["status", "--porcelain"]);
|
|
17
16
|
|
|
18
17
|
if (code !== 0) {
|
|
19
|
-
// Not a git repo, allow the action
|
|
20
18
|
return;
|
|
21
19
|
}
|
|
22
20
|
|
|
@@ -26,11 +24,9 @@ async function checkDirtyRepo(
|
|
|
26
24
|
}
|
|
27
25
|
|
|
28
26
|
if (!ctx.hasUI) {
|
|
29
|
-
// In non-interactive mode, block by default
|
|
30
27
|
return { cancel: true };
|
|
31
28
|
}
|
|
32
29
|
|
|
33
|
-
// Count changed files
|
|
34
30
|
const changedFiles = stdout.trim().split("\n").filter(Boolean).length;
|
|
35
31
|
|
|
36
32
|
const choice = await ctx.ui.select(
|
|
@@ -256,9 +256,9 @@ Notes:
|
|
|
256
256
|
survives this floor.
|
|
257
257
|
- `gate.breakEvenThresholdByState` lets `idle` vs `mid_loop` agent states use
|
|
258
258
|
different thresholds; `idle` defaults to `22` and
|
|
259
|
-
`mid_loop` defaults to `1`. Real
|
|
260
|
-
|
|
261
|
-
|
|
259
|
+
`mid_loop` defaults to `1`. Real mid-loop/idle detection is wired through
|
|
260
|
+
the `context` event handler: the state is classified straight from the
|
|
261
|
+
message payload —
|
|
262
262
|
`idle` if the most recent relevant message is a user message (this is the
|
|
263
263
|
first LLM call of the turn), `mid_loop` if it's an assistant message or a
|
|
264
264
|
tool result (the agent is iterating mid-turn). The representative-corpus
|
|
@@ -278,8 +278,10 @@ Notes:
|
|
|
278
278
|
- `gate.breakEvenThreshold`'s default of `22` is calibrated from the
|
|
279
279
|
representative-corpus benchmark at `cachedPriceRatio` r=0.1 (aggressive
|
|
280
280
|
prompt caching, the common case). It is **provider/ratio-
|
|
281
|
-
dependent
|
|
282
|
-
r=0.25, T=54 at r=0.5, T=58
|
|
281
|
+
dependent**. In the **historical, retired turn-end run**, the optimal
|
|
282
|
+
threshold rose as caching got weaker (T=29 at r=0.25, T=54 at r=0.5, T=58
|
|
283
|
+
at r=0.9); these are not current turn-start results. See
|
|
284
|
+
[`docs/v2-design.md` §4](./docs/v2-design.md#4-benchmark-decision-gate).
|
|
283
285
|
|
|
284
286
|
## Prompt-cache trade-off
|
|
285
287
|
|
|
@@ -326,6 +328,8 @@ Options:
|
|
|
326
328
|
- `--limit N`: only process the first N session files found.
|
|
327
329
|
- `--ratio R`: cached-price ratio(s) to model; repeatable and/or
|
|
328
330
|
comma-separated (e.g. `--ratio 0.1,0.2`). Default: `0.1`.
|
|
331
|
+
- `--sweep-max N`: explicit ceiling for the auto-expanding threshold sweep
|
|
332
|
+
(overrides the corpus-derived ceiling). Default: auto-expand from 30 as needed.
|
|
329
333
|
- `--json`: emit a full machine-readable JSON dump instead of aligned text.
|
|
330
334
|
- `--simulate-compression`: additionally SIMULATE v2-style range compression
|
|
331
335
|
(docs/v2-design.md §1/§4 go/no-go evidence) alongside — never mixed into —
|
|
@@ -390,13 +394,15 @@ reports two kinds of numbers:
|
|
|
390
394
|
prune point, and the *actual* net benefit that prune would have produced —
|
|
391
395
|
`realizedNetBenefit = actualRemainingCalls * recurringSaving - penalty`.
|
|
392
396
|
|
|
393
|
-
It also sweeps the break-even threshold `T` from 1 to 30
|
|
394
|
-
|
|
395
|
-
|
|
397
|
+
It also sweeps the break-even threshold `T` from 1 to 30, then keeps doubling
|
|
398
|
+
the range while the best `T` sits at the top edge, up to a corpus-derived
|
|
399
|
+
ceiling (hard cap 500). `--sweep-max N` sets that ceiling explicitly. It
|
|
400
|
+
reports the `T` that maximizes total realized net benefit across all
|
|
401
|
+
gate-eligible candidates, both overall and split by idle/mid-loop agent state.
|
|
396
402
|
|
|
397
403
|
## Comparison
|
|
398
404
|
|
|
399
|
-
- **[opencode-dynamic-context-pruning](https://github.com/
|
|
405
|
+
- **[opencode-dynamic-context-pruning](https://github.com/Tarquinen/opencode-dynamic-context-pruning) ("pi-dcp" upstream)**:
|
|
400
406
|
the OpenCode plugin this extension takes its name and general problem space
|
|
401
407
|
from. It ships a model-invoked `compress` tool (agentic, nested-summary
|
|
402
408
|
compression), autonomous nudges, manual mode, and a full `/dcp` command
|
|
@@ -428,9 +434,11 @@ net benefit, at sufficient candidate volume, with enough remaining-calls
|
|
|
428
434
|
runway per session to plausibly amortize a compression summary's own token
|
|
429
435
|
cost — see `docs/v2-design.md` §4 for the exact go/no-go bar.
|
|
430
436
|
|
|
431
|
-
The
|
|
437
|
+
The **historical, retired turn-end run** of the benchmark harness (over
|
|
432
438
|
`~/.the-last-harness/agent/sessions`: 1,390+ session files, 556 gate-eligible
|
|
433
|
-
candidates — see the corpus-choice note above) produced
|
|
439
|
+
candidates — see the corpus-choice note above) produced the following
|
|
440
|
+
figures, preserved as historical context rather than current turn-start
|
|
441
|
+
results (see [`docs/v2-design.md` §4](./docs/v2-design.md#4-benchmark-decision-gate)):
|
|
434
442
|
|
|
435
443
|
- **Candidates and tokens removed by strategy**: `dedupe` 111 candidates /
|
|
436
444
|
93,330 tokens removed; `error-purge` 187 / 28,191; `superseded-file-ops`
|
|
@@ -440,6 +448,7 @@ candidates — see the corpus-choice note above) produced:
|
|
|
440
448
|
this corpus is dominated by long, tool-heavy agent sessions rather than
|
|
441
449
|
short orchestrator sessions.
|
|
442
450
|
|
|
451
|
+
The current turn-start re-derivation has 460 gate-eligible candidates.
|
|
443
452
|
Shipped gate defaults remain `idle=22` / `mid_loop=1` (see
|
|
444
453
|
[Configuration](#configuration)).
|
|
445
454
|
|