@diegopetrucci/pi-extensions 0.1.61 → 0.1.63
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/.pi-fleet-tested-version +1 -1
- package/README.md +3 -2
- package/extensions/agent-workflow-audit/.pi-fleet-tested-version +1 -1
- package/extensions/agent-workflow-audit/package.json +1 -1
- package/extensions/annotate-git-diff/.pi-fleet-tested-version +1 -1
- package/extensions/annotate-git-diff/package.json +1 -1
- package/extensions/annotate-last-message/.pi-fleet-tested-version +1 -1
- package/extensions/annotate-last-message/package.json +1 -1
- package/extensions/brrr/.pi-fleet-tested-version +1 -1
- package/extensions/brrr/package.json +1 -1
- package/extensions/claude-fast/.pi-fleet-tested-version +1 -1
- package/extensions/claude-fast/README.md +2 -0
- package/extensions/claude-fast/package.json +1 -1
- package/extensions/code-reviewer/.pi-fleet-tested-version +1 -1
- package/extensions/code-reviewer/README.md +1 -0
- package/extensions/code-reviewer/index.ts +24 -2
- package/extensions/code-reviewer/package.json +1 -1
- package/extensions/confirm-destructive/.pi-fleet-tested-version +1 -1
- package/extensions/confirm-destructive/package.json +1 -1
- package/extensions/context-cap/.pi-fleet-tested-version +1 -1
- package/extensions/context-cap/package.json +1 -1
- package/extensions/context-inspector/.pi-fleet-tested-version +1 -1
- package/extensions/context-inspector/package.json +1 -1
- package/extensions/contrarian/.pi-fleet-tested-version +1 -1
- package/extensions/contrarian/README.md +2 -0
- package/extensions/contrarian/index.ts +34 -8
- package/extensions/contrarian/package.json +1 -1
- package/extensions/dirty-repo-guard/.pi-fleet-tested-version +1 -1
- package/extensions/dirty-repo-guard/package.json +1 -1
- package/extensions/dynamic-context-pruning/.pi-fleet-tested-version +1 -1
- package/extensions/dynamic-context-pruning/README.md +78 -11
- package/extensions/dynamic-context-pruning/index.ts +171 -21
- package/extensions/dynamic-context-pruning/package.json +1 -1
- package/extensions/fast/.pi-fleet-tested-version +1 -0
- package/extensions/fast/README.md +119 -0
- package/extensions/fast/fast.example.json +4 -0
- package/extensions/fast/index.ts +383 -0
- package/extensions/fast/package.json +39 -0
- package/extensions/git-footer/.pi-fleet-tested-version +1 -1
- package/extensions/git-footer/package.json +1 -1
- package/extensions/gnosis/.pi-fleet-tested-version +1 -1
- package/extensions/gnosis/package.json +1 -1
- package/extensions/illustrations-to-explain-things/.pi-fleet-tested-version +1 -1
- package/extensions/illustrations-to-explain-things/package.json +1 -1
- package/extensions/inline-bash/.pi-fleet-tested-version +1 -1
- package/extensions/inline-bash/package.json +1 -1
- package/extensions/librarian/.pi-fleet-tested-version +1 -1
- package/extensions/librarian/README.md +1 -1
- package/extensions/librarian/index.ts +34 -5
- package/extensions/librarian/package.json +1 -1
- package/extensions/minimal-footer/.pi-fleet-tested-version +1 -1
- package/extensions/minimal-footer/package.json +1 -1
- package/extensions/notify/.pi-fleet-tested-version +1 -1
- package/extensions/notify/package.json +1 -1
- package/extensions/openai-fast/.pi-fleet-tested-version +1 -1
- package/extensions/openai-fast/README.md +2 -0
- package/extensions/openai-fast/package.json +1 -1
- package/extensions/oracle/.pi-fleet-tested-version +1 -1
- package/extensions/oracle/README.md +2 -0
- package/extensions/oracle/index.ts +34 -8
- package/extensions/oracle/package.json +1 -1
- package/extensions/permission-gate/.pi-fleet-tested-version +1 -1
- package/extensions/permission-gate/package.json +1 -1
- package/extensions/quiet-tools/.pi-fleet-tested-version +1 -1
- package/extensions/quiet-tools/package.json +1 -1
- package/extensions/review/.pi-fleet-tested-version +1 -1
- package/extensions/review/package.json +1 -1
- package/extensions/todo/.pi-fleet-tested-version +1 -1
- package/extensions/todo/package.json +1 -1
- package/extensions/triage-comments/.pi-fleet-tested-version +1 -1
- package/extensions/triage-comments/package.json +1 -1
- package/package.json +5 -6
|
@@ -165,10 +165,16 @@ export interface PruneProtections {
|
|
|
165
165
|
|
|
166
166
|
export interface PruneThresholds {
|
|
167
167
|
/**
|
|
168
|
-
* Minimum characters a prune must remove
|
|
169
|
-
*
|
|
170
|
-
* (
|
|
171
|
-
*
|
|
168
|
+
* Minimum raw text characters a fresh AUTOMATIC prune proposal must remove
|
|
169
|
+
* (original text chars minus placeholder chars) to be considered at all
|
|
170
|
+
* (pe-qdzb). Enforced by `runDynamicContextPruningPipeline` BEFORE the
|
|
171
|
+
* net-benefit gate (see `PruneGateConfig`/`gate`, which uses break-even token
|
|
172
|
+
* math and is a separate, later check): text-only candidates below this floor
|
|
173
|
+
* are dropped up front and never reach gate/batch consideration. Image-bearing
|
|
174
|
+
* tool-result candidates bypass this raw-text floor (images receive no
|
|
175
|
+
* synthetic character count) and proceed to the token-based gate. Manual
|
|
176
|
+
* prunes and already-persisted/replayed decisions always bypass this floor,
|
|
177
|
+
* same as the gate. A value of 0 disables the floor (no filtering).
|
|
172
178
|
*/
|
|
173
179
|
minCharsSaved: number;
|
|
174
180
|
}
|
|
@@ -194,19 +200,42 @@ export type GateMode = "on" | "off" | "always-apply";
|
|
|
194
200
|
* making tool calls) vs idle (waiting on the user). Both states default to
|
|
195
201
|
* the same threshold today.
|
|
196
202
|
*
|
|
197
|
-
* pe-c5n9 evidence
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
* default. However, the only live runtime
|
|
201
|
-
* handler below)
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
203
|
+
* pe-c5n9 evidence (turn-END definition, since superseded -- see below):
|
|
204
|
+
* the representative-corpus benchmark showed mid_loop candidates carrying
|
|
205
|
+
* ~all realized net benefit while idle candidates carried ~none. That would
|
|
206
|
+
* have argued for a stricter idle default. However, the only live runtime
|
|
207
|
+
* caller (the `context` event handler below) did not yet perform real
|
|
208
|
+
* mid-loop/idle detection at the time, so no default change was made.
|
|
209
|
+
*
|
|
210
|
+
* pe-zy4s update (2026-07-08): real runtime mid_loop/idle detection is now
|
|
211
|
+
* wired through the `context` event handler (`classifyAgentStateFromMessages`),
|
|
212
|
+
* using a turn-START definition (idle = this is the first LLM call of a turn;
|
|
213
|
+
* mid_loop = the agent already made a call or received a tool result this
|
|
214
|
+
* turn) -- see that function's doc comment for the exact runtime-observable
|
|
215
|
+
* semantics. The benchmark's candidate labeling was aligned to this SAME
|
|
216
|
+
* definition (retiring the old turn-END definition above, which was never
|
|
217
|
+
* runtime-observable), and the state-split was re-derived on the
|
|
218
|
+
* representative corpus (~/.the-last-harness/agent/sessions, 460 candidates)
|
|
219
|
+
* under it.
|
|
220
|
+
*
|
|
221
|
+
* The result REVERSES the earlier (turn-END) finding: at r=0.1 the
|
|
222
|
+
* turn-START "idle" population (calls made at the start of a turn) carries
|
|
223
|
+
* essentially ALL the realized net benefit (idle-optimal T=22, total realized
|
|
224
|
+
* net benefit ~20,594 token-units), while "mid_loop" carries essentially NONE
|
|
225
|
+
* (mid_loop-optimal T=1, total realized net benefit ~0.0 -- i.e. no mid_loop
|
|
226
|
+
* candidate is ever worth accepting at r=0.1 under this definition). This is
|
|
227
|
+
* the OPPOSITE of what the pe-c5n9 evidence (measured under the old,
|
|
228
|
+
* non-runtime-observable turn-END definition) suggested.
|
|
229
|
+
*
|
|
230
|
+
* Per the pe-zy4s decision rule (stricter idle default only if idle itself is
|
|
231
|
+
* shown to be ~worthless): idle is NOT worthless here -- it is the ONLY state
|
|
232
|
+
* carrying value at r=0.1 -- so no state-conditioned strictness is justified
|
|
233
|
+
* in either direction. The per-state split is therefore kept at PARITY
|
|
234
|
+
* (`breakEvenThresholdByState: { idle: DEFAULT_BREAK_EVEN_THRESHOLD, mid_loop:
|
|
235
|
+
* DEFAULT_BREAK_EVEN_THRESHOLD }`), both config-overridable. A future ticket
|
|
236
|
+
* could consider a stricter mid_loop default given this evidence, but that is
|
|
237
|
+
* a new, unreviewed direction outside this ticket's authorized decision rule
|
|
238
|
+
* and is left for a follow-up.
|
|
210
239
|
*/
|
|
211
240
|
export type AgentState = "idle" | "mid_loop";
|
|
212
241
|
|
|
@@ -397,6 +426,41 @@ export function estimateTokensForContent(content: string | (MinimalTextContent |
|
|
|
397
426
|
|
|
398
427
|
export type TokenEstimator = (text: string) => number;
|
|
399
428
|
|
|
429
|
+
/**
|
|
430
|
+
* Classify the agent's current state from the message payload alone (pe-zy4s):
|
|
431
|
+
* this is the runtime-observable definition used both by the live `context`
|
|
432
|
+
* event handler and (via re-export) by the offline benchmark, so the two can
|
|
433
|
+
* never drift apart.
|
|
434
|
+
*
|
|
435
|
+
* Walk backwards from the end of `messages`, skipping any message that is
|
|
436
|
+
* neither `user`, `assistant`, nor `toolResult` (e.g. system-role bookkeeping
|
|
437
|
+
* entries). The first such message found determines the state:
|
|
438
|
+
* - `user` (a plain user turn message) => "idle": this call is the FIRST LLM
|
|
439
|
+
* call of a turn, i.e. the decision is being made from a turn-start state.
|
|
440
|
+
* - `assistant` or `toolResult` (mid-chain) => "mid_loop": the agent is
|
|
441
|
+
* iterating (already made at least one call this turn, or is reacting to a
|
|
442
|
+
* tool result).
|
|
443
|
+
* - No such message found (empty history, or system-only) => "idle" (safe
|
|
444
|
+
* default: nothing suggests an in-progress loop).
|
|
445
|
+
*
|
|
446
|
+
* The benchmark (scripts/benchmark.mjs) reclassifies its candidate labels to
|
|
447
|
+
* this SAME definition (pe-zy4s), reusing this function directly, so the
|
|
448
|
+
* defaults below rest on evidence measured with the exact definition the
|
|
449
|
+
* runtime observes. (An earlier turn-end-based benchmark definition -- "idle"
|
|
450
|
+
* meant "this IS the turn's last assistant message", only knowable in
|
|
451
|
+
* hindsight -- has been retired: the runtime cannot know in advance whether a
|
|
452
|
+
* call is a turn's last one, so that definition was never runtime-observable.)
|
|
453
|
+
*/
|
|
454
|
+
export function classifyAgentStateFromMessages(messages: MinimalMessage[]): AgentState {
|
|
455
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
456
|
+
const role = messages[i].role;
|
|
457
|
+
if (role === "user") return "idle";
|
|
458
|
+
if (role === "assistant" || role === "toolResult") return "mid_loop";
|
|
459
|
+
// Any other role (e.g. "system") is skipped: keep walking further back.
|
|
460
|
+
}
|
|
461
|
+
return "idle";
|
|
462
|
+
}
|
|
463
|
+
|
|
400
464
|
/** Resolve which break-even threshold applies for a given agent state (pe-s2ho state-conditioning hook). */
|
|
401
465
|
export function resolveBreakEvenThreshold(gateConfig: PruneGateConfig, agentState: AgentState): number {
|
|
402
466
|
const override = gateConfig.breakEvenThresholdByState[agentState];
|
|
@@ -892,6 +956,20 @@ function contentTextLength(content: (MinimalTextContent | MinimalImageContent)[]
|
|
|
892
956
|
return content.reduce((sum, block) => (block.type === "text" ? sum + block.text.length : sum), 0);
|
|
893
957
|
}
|
|
894
958
|
|
|
959
|
+
/** Return true when a decision targets a tool result containing one or more images. */
|
|
960
|
+
function isImageBearingToolResultTarget(
|
|
961
|
+
messages: MinimalMessage[],
|
|
962
|
+
decision: PruneDecisionRecord,
|
|
963
|
+
pairIndex?: Map<string, ToolCallPairIndices>,
|
|
964
|
+
): boolean {
|
|
965
|
+
if (decision.kind !== "tool_result_content" || decision.correlation.type !== "toolCallId") return false;
|
|
966
|
+
const pair = findToolCallPairIndices(messages, decision.correlation.toolCallId, pairIndex);
|
|
967
|
+
if (pair.resultIndex === undefined) return false;
|
|
968
|
+
const target = messages[pair.resultIndex];
|
|
969
|
+
if (target?.role !== "toolResult") return false;
|
|
970
|
+
return (target as MinimalToolResultMessage).content.some((block) => block.type === "image");
|
|
971
|
+
}
|
|
972
|
+
|
|
895
973
|
export interface ApplyResult {
|
|
896
974
|
applied: boolean;
|
|
897
975
|
charsRemoved: number;
|
|
@@ -1050,6 +1128,55 @@ export function estimateDecisionSavings(
|
|
|
1050
1128
|
return undefined;
|
|
1051
1129
|
}
|
|
1052
1130
|
|
|
1131
|
+
/**
|
|
1132
|
+
* Estimate the character savings of a single decision (pe-qdzb), i.e. the
|
|
1133
|
+
* same before/after delta basis as `estimateDecisionSavings` but measured in
|
|
1134
|
+
* raw characters rather than estimated tokens. Used by the pipeline to
|
|
1135
|
+
* enforce `thresholds.minCharsSaved` against fresh automatic proposals
|
|
1136
|
+
* *before* they reach the net-benefit gate. Returns undefined under the same
|
|
1137
|
+
* conditions as `estimateDecisionSavings` (target absent / would not apply).
|
|
1138
|
+
* Mirrors `estimateDecisionSavings`'s `Math.max(0, ...)` clamp, so a
|
|
1139
|
+
* decision whose placeholder text would be LONGER than the original content
|
|
1140
|
+
* (negative savings) is reported as 0 chars saved, not a negative number.
|
|
1141
|
+
*/
|
|
1142
|
+
export function estimateDecisionCharsSaved(
|
|
1143
|
+
messages: MinimalMessage[],
|
|
1144
|
+
decision: PruneDecisionRecord,
|
|
1145
|
+
pairIndex?: Map<string, ToolCallPairIndices>,
|
|
1146
|
+
): number | undefined {
|
|
1147
|
+
if (decision.correlation.type !== "toolCallId") return undefined;
|
|
1148
|
+
const pair = findToolCallPairIndices(messages, decision.correlation.toolCallId, pairIndex);
|
|
1149
|
+
|
|
1150
|
+
// Same non-mutating preview approach as `estimateDecisionSavings`.
|
|
1151
|
+
const preview = messages.slice();
|
|
1152
|
+
const result = applyPruneDecision(preview, decision, pairIndex);
|
|
1153
|
+
if (!result.applied) return undefined;
|
|
1154
|
+
|
|
1155
|
+
if (decision.kind === "tool_result_content") {
|
|
1156
|
+
if (pair.resultIndex === undefined) return undefined;
|
|
1157
|
+
const before = messages[pair.resultIndex] as MinimalToolResultMessage;
|
|
1158
|
+
const after = preview[pair.resultIndex] as MinimalToolResultMessage;
|
|
1159
|
+
const charsBefore = contentTextLength(before.content);
|
|
1160
|
+
const charsAfter = contentTextLength(after.content);
|
|
1161
|
+
return Math.max(0, charsBefore - charsAfter);
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
if (decision.kind === "tool_call_input") {
|
|
1165
|
+
if (pair.assistantIndex === undefined || pair.toolCallBlockIndex === undefined) return undefined;
|
|
1166
|
+
const beforeBlock = (messages[pair.assistantIndex] as MinimalAssistantMessage).content[
|
|
1167
|
+
pair.toolCallBlockIndex
|
|
1168
|
+
] as MinimalToolCallContent;
|
|
1169
|
+
const afterBlock = (preview[pair.assistantIndex] as MinimalAssistantMessage).content[
|
|
1170
|
+
pair.toolCallBlockIndex
|
|
1171
|
+
] as MinimalToolCallContent;
|
|
1172
|
+
const charsBefore = JSON.stringify(beforeBlock.arguments ?? {}).length;
|
|
1173
|
+
const charsAfter = JSON.stringify(afterBlock.arguments ?? {}).length;
|
|
1174
|
+
return Math.max(0, charsBefore - charsAfter);
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
return undefined;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1053
1180
|
/**
|
|
1054
1181
|
* Cache-aware cost model (pe-s2ho ticket NOTES, binding over the looser prose
|
|
1055
1182
|
* in the ticket body): prompt caches are prefix-based, so pruning at message
|
|
@@ -1877,7 +2004,29 @@ export function runDynamicContextPruningPipeline(input: PipelineInput): Pipeline
|
|
|
1877
2004
|
// against the pre-mutation `messages` clone, before the apply loop below
|
|
1878
2005
|
// mutates anything.
|
|
1879
2006
|
const freshManual = dedupedFresh.filter((decision) => decision.source === "manual");
|
|
1880
|
-
const
|
|
2007
|
+
const freshAutomaticUnfiltered = dedupedFresh.filter((decision) => decision.source !== "manual");
|
|
2008
|
+
|
|
2009
|
+
// Minimum-size floor (pe-qdzb): drop fresh automatic proposals whose
|
|
2010
|
+
// character savings (original content chars minus placeholder chars) fall
|
|
2011
|
+
// below `thresholds.minCharsSaved`, BEFORE gate evaluation so dropped
|
|
2012
|
+
// candidates never consume batch/earliest-position gate consideration.
|
|
2013
|
+
// Applies to automatic proposals only; manual (`freshManual` above) and
|
|
2014
|
+
// already-persisted decisions (`input.persistedDecisions`, applied later)
|
|
2015
|
+
// bypass this floor entirely, same as the net-benefit gate. Tool-result
|
|
2016
|
+
// candidates containing images also bypass this raw-text floor because
|
|
2017
|
+
// images have no character count; their token savings still go through the
|
|
2018
|
+
// existing net-benefit gate. A floor of 0 (the config default before
|
|
2019
|
+
// pe-c5n9's DEFAULT_MIN_CHARS_SAVED constant was introduced) is a no-op:
|
|
2020
|
+
// every non-negative delta clears it.
|
|
2021
|
+
const minCharsSaved = input.config.thresholds.minCharsSaved;
|
|
2022
|
+
const freshAutomatic =
|
|
2023
|
+
minCharsSaved <= 0
|
|
2024
|
+
? freshAutomaticUnfiltered
|
|
2025
|
+
: freshAutomaticUnfiltered.filter((decision) => {
|
|
2026
|
+
if (isImageBearingToolResultTarget(messages, decision, pairIndex)) return true;
|
|
2027
|
+
const charsSaved = estimateDecisionCharsSaved(messages, decision, pairIndex);
|
|
2028
|
+
return charsSaved !== undefined && charsSaved >= minCharsSaved;
|
|
2029
|
+
});
|
|
1881
2030
|
|
|
1882
2031
|
// Collapse fresh AUTOMATIC candidates that target the same (kind,
|
|
1883
2032
|
// correlation) down to a single decision (pe-j7sb). Two strategies (e.g.
|
|
@@ -2360,9 +2509,10 @@ export default function dynamicContextPruningExtension(pi: ExtensionAPI) {
|
|
|
2360
2509
|
persistedDecisions,
|
|
2361
2510
|
knownIdempotencyKeys,
|
|
2362
2511
|
restoredIdempotencyKeys,
|
|
2363
|
-
// Real mid-loop/idle agent-state detection
|
|
2364
|
-
//
|
|
2365
|
-
|
|
2512
|
+
// Real mid-loop/idle agent-state detection (pe-zy4s): derived straight from
|
|
2513
|
+
// the message payload via classifyAgentStateFromMessages -- see its doc
|
|
2514
|
+
// comment for the exact runtime-observable definition.
|
|
2515
|
+
agentState: classifyAgentStateFromMessages(event.messages as unknown as MinimalMessage[]),
|
|
2366
2516
|
cwd: ctx.sessionManager.getCwd(),
|
|
2367
2517
|
});
|
|
2368
2518
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.83.0
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# fast
|
|
2
|
+
|
|
3
|
+
A pi extension that provides one `/fast` toggle for supported OpenAI Codex and Anthropic Claude models. It selects the provider-specific Fast-mode request shape from the currently selected model.
|
|
4
|
+
|
|
5
|
+
For eligible OpenAI Codex models it injects:
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"service_tier": "priority"
|
|
10
|
+
}
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
For eligible Anthropic Claude models it injects:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"speed": "fast"
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
It also adds the required Anthropic beta header value:
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
anthropic-beta: fast-mode-2026-02-01
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Eligibility
|
|
28
|
+
|
|
29
|
+
OpenAI Fast mode requires all of the following:
|
|
30
|
+
|
|
31
|
+
- Provider `openai-codex`.
|
|
32
|
+
- API `openai-codex-responses`.
|
|
33
|
+
- Model `gpt-5.4`, `gpt-5.5`, `gpt-5.6-sol`, `gpt-5.6-terra`, or `gpt-5.6-luna`.
|
|
34
|
+
- ChatGPT OAuth/subscription auth, not API-key auth.
|
|
35
|
+
- No existing `service_tier` field in the request payload.
|
|
36
|
+
|
|
37
|
+
Anthropic Fast mode requires all of the following:
|
|
38
|
+
|
|
39
|
+
- Provider `anthropic`.
|
|
40
|
+
- API `anthropic-messages`.
|
|
41
|
+
- Model `claude-opus-4-6`, `claude-opus-4-7`, or `claude-opus-4-8`.
|
|
42
|
+
- No existing `speed` field in the request payload.
|
|
43
|
+
|
|
44
|
+
Anthropic Fast mode supports both API-key and Claude Code OAuth access when the account has access to the research preview.
|
|
45
|
+
|
|
46
|
+
## Commands
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
/fast
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Run `/fast` to toggle Fast mode for the current session/runtime. The enabled state follows model changes: switching between eligible OpenAI and Anthropic models automatically changes the request format without resetting the toggle. The footer shows `fast` only while the selected model is eligible and Fast mode is enabled.
|
|
53
|
+
|
|
54
|
+
The extension defaults to off so installing the full collection does not accidentally spend Fast-mode credits.
|
|
55
|
+
|
|
56
|
+
## Config
|
|
57
|
+
|
|
58
|
+
Optional global config:
|
|
59
|
+
|
|
60
|
+
```text
|
|
61
|
+
~/<pi-config-dir>/agent/extensions/fast.json
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Optional project config:
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
<project>/<pi-config-dir>/fast.json
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Here `<pi-config-dir>` is Pi's runtime config directory name (`CONFIG_DIR_NAME`; `.pi` by default). Project config overrides global config after Pi reports that the project is trusted.
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"enabled": false,
|
|
75
|
+
"showStatus": true
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- `enabled`: default Fast-mode state when there is no session override.
|
|
80
|
+
- `showStatus`: show a compact `fast` status when Fast mode is active for the selected model.
|
|
81
|
+
|
|
82
|
+
The unified extension intentionally does not read `openai-fast.json` or `claude-fast.json`; those files belong to the provider-specific standalone packages and may contain conflicting defaults.
|
|
83
|
+
|
|
84
|
+
## Install
|
|
85
|
+
|
|
86
|
+
### Standalone npm package
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
pi install npm:@diegopetrucci/pi-fast
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Collection package
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pi install npm:@diegopetrucci/pi-extensions
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### GitHub package
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pi install git:github.com/diegopetrucci/pi-extensions
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Then reload pi:
|
|
105
|
+
|
|
106
|
+
```text
|
|
107
|
+
/reload
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Legacy provider-specific packages
|
|
111
|
+
|
|
112
|
+
The collection package loads this unified extension instead of `openai-fast` and `claude-fast`. Both provider-specific packages remain available as standalone alternatives. Do not load them together with this extension: independent Fast-mode state and duplicate `/fast` registrations can produce conflicting behavior.
|
|
113
|
+
|
|
114
|
+
## Notes
|
|
115
|
+
|
|
116
|
+
- Anthropic Fast mode has separate rate limits, costs more than standard speed, and does not share prompt-cache prefixes with standard-speed requests.
|
|
117
|
+
- OpenAI Fast mode intentionally does not affect API-key models.
|
|
118
|
+
- Existing `speed` and `service_tier` fields are never overwritten.
|
|
119
|
+
- Cost accounting still depends on the provider reporting the effective tier in its streamed response.
|