@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.
Files changed (72) hide show
  1. package/.pi-fleet-tested-version +1 -1
  2. package/README.md +3 -2
  3. package/extensions/agent-workflow-audit/.pi-fleet-tested-version +1 -1
  4. package/extensions/agent-workflow-audit/package.json +1 -1
  5. package/extensions/annotate-git-diff/.pi-fleet-tested-version +1 -1
  6. package/extensions/annotate-git-diff/package.json +1 -1
  7. package/extensions/annotate-last-message/.pi-fleet-tested-version +1 -1
  8. package/extensions/annotate-last-message/package.json +1 -1
  9. package/extensions/brrr/.pi-fleet-tested-version +1 -1
  10. package/extensions/brrr/package.json +1 -1
  11. package/extensions/claude-fast/.pi-fleet-tested-version +1 -1
  12. package/extensions/claude-fast/README.md +2 -0
  13. package/extensions/claude-fast/package.json +1 -1
  14. package/extensions/code-reviewer/.pi-fleet-tested-version +1 -1
  15. package/extensions/code-reviewer/README.md +1 -0
  16. package/extensions/code-reviewer/index.ts +24 -2
  17. package/extensions/code-reviewer/package.json +1 -1
  18. package/extensions/confirm-destructive/.pi-fleet-tested-version +1 -1
  19. package/extensions/confirm-destructive/package.json +1 -1
  20. package/extensions/context-cap/.pi-fleet-tested-version +1 -1
  21. package/extensions/context-cap/package.json +1 -1
  22. package/extensions/context-inspector/.pi-fleet-tested-version +1 -1
  23. package/extensions/context-inspector/package.json +1 -1
  24. package/extensions/contrarian/.pi-fleet-tested-version +1 -1
  25. package/extensions/contrarian/README.md +2 -0
  26. package/extensions/contrarian/index.ts +34 -8
  27. package/extensions/contrarian/package.json +1 -1
  28. package/extensions/dirty-repo-guard/.pi-fleet-tested-version +1 -1
  29. package/extensions/dirty-repo-guard/package.json +1 -1
  30. package/extensions/dynamic-context-pruning/.pi-fleet-tested-version +1 -1
  31. package/extensions/dynamic-context-pruning/README.md +78 -11
  32. package/extensions/dynamic-context-pruning/index.ts +171 -21
  33. package/extensions/dynamic-context-pruning/package.json +1 -1
  34. package/extensions/fast/.pi-fleet-tested-version +1 -0
  35. package/extensions/fast/README.md +119 -0
  36. package/extensions/fast/fast.example.json +4 -0
  37. package/extensions/fast/index.ts +383 -0
  38. package/extensions/fast/package.json +39 -0
  39. package/extensions/git-footer/.pi-fleet-tested-version +1 -1
  40. package/extensions/git-footer/package.json +1 -1
  41. package/extensions/gnosis/.pi-fleet-tested-version +1 -1
  42. package/extensions/gnosis/package.json +1 -1
  43. package/extensions/illustrations-to-explain-things/.pi-fleet-tested-version +1 -1
  44. package/extensions/illustrations-to-explain-things/package.json +1 -1
  45. package/extensions/inline-bash/.pi-fleet-tested-version +1 -1
  46. package/extensions/inline-bash/package.json +1 -1
  47. package/extensions/librarian/.pi-fleet-tested-version +1 -1
  48. package/extensions/librarian/README.md +1 -1
  49. package/extensions/librarian/index.ts +34 -5
  50. package/extensions/librarian/package.json +1 -1
  51. package/extensions/minimal-footer/.pi-fleet-tested-version +1 -1
  52. package/extensions/minimal-footer/package.json +1 -1
  53. package/extensions/notify/.pi-fleet-tested-version +1 -1
  54. package/extensions/notify/package.json +1 -1
  55. package/extensions/openai-fast/.pi-fleet-tested-version +1 -1
  56. package/extensions/openai-fast/README.md +2 -0
  57. package/extensions/openai-fast/package.json +1 -1
  58. package/extensions/oracle/.pi-fleet-tested-version +1 -1
  59. package/extensions/oracle/README.md +2 -0
  60. package/extensions/oracle/index.ts +34 -8
  61. package/extensions/oracle/package.json +1 -1
  62. package/extensions/permission-gate/.pi-fleet-tested-version +1 -1
  63. package/extensions/permission-gate/package.json +1 -1
  64. package/extensions/quiet-tools/.pi-fleet-tested-version +1 -1
  65. package/extensions/quiet-tools/package.json +1 -1
  66. package/extensions/review/.pi-fleet-tested-version +1 -1
  67. package/extensions/review/package.json +1 -1
  68. package/extensions/todo/.pi-fleet-tested-version +1 -1
  69. package/extensions/todo/package.json +1 -1
  70. package/extensions/triage-comments/.pi-fleet-tested-version +1 -1
  71. package/extensions/triage-comments/package.json +1 -1
  72. package/package.json +5 -6
@@ -1 +1 @@
1
- 0.82.1
1
+ 0.83.0
package/README.md CHANGED
@@ -13,8 +13,9 @@ A collection of [pi](https://pi.dev) agent extensions I made. (btw, [I am also b
13
13
 
14
14
  ### Providers
15
15
 
16
- - [`claude-fast`](./extensions/claude-fast): Adds `/claude-fast` to enable Anthropic Claude Fast mode for supported Claude Opus models by injecting `speed: "fast"`.
17
- - [`openai-fast`](./extensions/openai-fast): Adds `/fast` to enable OpenAI Codex Fast mode for ChatGPT-auth GPT-5.4, GPT-5.5, and GPT-5.6 Codex variants by injecting the priority service tier.
16
+ - [`claude-fast`](./extensions/claude-fast): Standalone-only provider-specific alternative that adds `/claude-fast` for supported Anthropic Claude Opus models.
17
+ - [`fast`](./extensions/fast): Adds one `/fast` toggle that enables the provider-specific Fast mode for the currently selected supported OpenAI Codex or Anthropic Claude model.
18
+ - [`openai-fast`](./extensions/openai-fast): Standalone-only provider-specific alternative that adds `/fast` for ChatGPT-auth GPT-5.4, GPT-5.5, and GPT-5.6 Codex variants.
18
19
 
19
20
  ### UI
20
21
 
@@ -1 +1 @@
1
- 0.82.1
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-agent-workflow-audit",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "A pi extension that runs an isolated repo workflow audit and returns only the final report to the main session.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.1
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-annotate-git-diff",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "A standalone pi extension that adds /annotate-git-diff, a native Glimpse UI for annotating git diffs and sending feedback to the editor.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.1
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-annotate-last-message",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "A standalone pi extension that adds /annotate-last-message, a native Glimpse UI for annotating the latest assistant reply.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.1
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-brrr",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "A pi extension that sends brrr push notifications when pi is ready for input.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.1
1
+ 0.83.0
@@ -2,6 +2,8 @@
2
2
 
3
3
  A pi extension that enables Anthropic Claude Fast mode for supported Claude Opus models.
4
4
 
5
+ This package is standalone-only and is not auto-loaded by the `@diegopetrucci/pi-extensions` collection package. The collection uses the unified [`fast`](../fast) extension instead.
6
+
5
7
  When active, the extension injects this into eligible Anthropic request payloads:
6
8
 
7
9
  ```json
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-claude-fast",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "A pi extension that enables Anthropic Claude Fast mode for supported Claude Opus models by injecting speed=fast.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.1
1
+ 0.83.0
@@ -44,6 +44,7 @@ When called, it launches an isolated in-memory child agent with:
44
44
 
45
45
  - no inherited extensions, skills, prompt templates, themes, context files, or agents files;
46
46
  - an auto-selected review model that prefers contrarian-style opposite-provider and opposite-family candidates before same-provider fallbacks;
47
+ - automatic model candidates constrained to the current Pi session's model scope when that scope is non-empty;
47
48
  - requested thinking taken from the active session when available, otherwise defaulting to `high` for reasoning models and `off` for non-reasoning models, then clamped to the selected model's supported level;
48
49
  - read-only tools only: `read`, `grep`, `find`, `ls`, and guarded `bash`;
49
50
  - a local-checkout path guard for file inspection;
@@ -224,8 +224,28 @@ type PiModel = {
224
224
  type ModelRegistryContext = {
225
225
  model?: PiModel;
226
226
  modelRegistry: { getAvailable(): PiModel[] | Promise<PiModel[]> };
227
+ scopedModels?: ReadonlyArray<{ model: PiModel; thinkingLevel?: ThinkingLevel }>;
227
228
  };
228
229
 
230
+ function modelKey(model: PiModel): string {
231
+ return `${model.provider}/${model.id}`.toLowerCase();
232
+ }
233
+
234
+ function hasModelScope(ctx: ModelRegistryContext): boolean {
235
+ return Array.isArray(ctx.scopedModels) && ctx.scopedModels.length > 0;
236
+ }
237
+
238
+ function isModelInScope(ctx: ModelRegistryContext, model: PiModel): boolean {
239
+ if (!hasModelScope(ctx)) return true;
240
+ const key = modelKey(model);
241
+ return ctx.scopedModels!.some((entry) => modelKey(entry.model) === key);
242
+ }
243
+
244
+ async function getAutoSelectableModels(ctx: ModelRegistryContext): Promise<PiModel[]> {
245
+ const available = await ctx.modelRegistry.getAvailable();
246
+ return hasModelScope(ctx) ? available.filter((model) => isModelInScope(ctx, model)) : available;
247
+ }
248
+
229
249
  type CreateAgentSessionOptions = NonNullable<Parameters<typeof createAgentSession>[0]>;
230
250
  type CreateAgentSessionModel = CreateAgentSessionOptions["model"];
231
251
 
@@ -354,11 +374,13 @@ function appendOrderedCandidates(
354
374
  async function selectCodeReviewerModel(
355
375
  ctx: ModelRegistryContext,
356
376
  ): Promise<{ ok: true; selection: PiModel; ordered: PiModel[] } | { ok: false; error: string }> {
357
- const available = await ctx.modelRegistry.getAvailable();
377
+ const available = await getAutoSelectableModels(ctx);
358
378
  if (available.length === 0) {
359
379
  return {
360
380
  ok: false,
361
- error: "No authenticated models are available. Log in or configure an API key first.",
381
+ error: hasModelScope(ctx)
382
+ ? "No authenticated models are available in the current session model scope. Adjust the scope, log in, or configure an API key."
383
+ : "No authenticated models are available. Log in or configure an API key first.",
362
384
  };
363
385
  }
364
386
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-code-reviewer",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "A standalone pi extension that adds a read-only code_reviewer subagent tool for isolated code reviews.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.1
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-confirm-destructive",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "A pi extension that confirms destructive session actions.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.1
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-context-cap",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "A pi extension that caps effective model context windows at 200k tokens for earlier auto-compaction.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.1
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-context-inspector",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "A pi extension that opens a local HTML dashboard explaining where the current session context is going.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.1
1
+ 0.83.0
@@ -77,6 +77,8 @@ Tool-call parameters still win over these defaults. `auto` clears the configured
77
77
  - `thinkingLevel` - optional reasoning/thinking override; falls back to the `/contrarian thinking` default, then built-in defaults
78
78
  - `cwd` - optional working directory override
79
79
 
80
+ When the current Pi session has a non-empty model scope, Contrarian limits automatic model selection and automatic fallbacks to that scope. An explicit tool or `/contrarian model` override remains authoritative even when it is outside the session scope.
81
+
80
82
  ## Notes
81
83
 
82
84
  - The contrarian is intentionally **read-only**.
@@ -20,6 +20,31 @@ type PiModel = {
20
20
  thinkingLevelMap?: ThinkingLevelMap;
21
21
  };
22
22
 
23
+ type ModelSelectionContext = {
24
+ model?: PiModel;
25
+ modelRegistry: { getAvailable(): PiModel[] | Promise<PiModel[]> };
26
+ scopedModels?: ReadonlyArray<{ model: PiModel; thinkingLevel?: ThinkingLevel }>;
27
+ };
28
+
29
+ function modelKey(model: PiModel): string {
30
+ return `${model.provider}/${model.id}`.toLowerCase();
31
+ }
32
+
33
+ function hasModelScope(ctx: ModelSelectionContext): boolean {
34
+ return Array.isArray(ctx.scopedModels) && ctx.scopedModels.length > 0;
35
+ }
36
+
37
+ function isModelInScope(ctx: ModelSelectionContext, model: PiModel): boolean {
38
+ if (!hasModelScope(ctx)) return true;
39
+ const key = modelKey(model);
40
+ return ctx.scopedModels!.some((entry) => modelKey(entry.model) === key);
41
+ }
42
+
43
+ async function getAutoSelectableModels(ctx: ModelSelectionContext): Promise<PiModel[]> {
44
+ const available = await ctx.modelRegistry.getAvailable();
45
+ return hasModelScope(ctx) ? available.filter((model) => isModelInScope(ctx, model)) : available;
46
+ }
47
+
23
48
  interface UsageStats {
24
49
  input: number;
25
50
  output: number;
@@ -180,8 +205,6 @@ const PROVIDER_MODEL_PREFERENCES: Record<string, string[]> = {
180
205
  "accounts/fireworks/models/glm-5p2",
181
206
  "accounts/fireworks/routers/glm-5p2-fast",
182
207
  "accounts/fireworks/models/minimax-m3",
183
- "accounts/fireworks/models/glm-5p1",
184
- "accounts/fireworks/routers/glm-5p1-fast",
185
208
  "accounts/fireworks/models/kimi-k2p6",
186
209
  "accounts/fireworks/routers/kimi-k2p6-turbo",
187
210
  "accounts/fireworks/routers/kimi-k2p6-fast",
@@ -918,7 +941,7 @@ function appendThinkingLevelClampReason(
918
941
  }
919
942
 
920
943
  async function findAvailableModel(
921
- ctx: { model?: PiModel; modelRegistry: { getAvailable(): PiModel[] | Promise<PiModel[]> } },
944
+ ctx: ModelSelectionContext,
922
945
  modelRef: string,
923
946
  ): Promise<PiModel | undefined> {
924
947
  const available = await ctx.modelRegistry.getAvailable();
@@ -964,11 +987,11 @@ function toContrarianSelection(
964
987
  }
965
988
 
966
989
  function buildSessionFallbackSelection(
967
- ctx: { model?: PiModel },
990
+ ctx: ModelSelectionContext,
968
991
  thinkingLevelOverride: ThinkingLevel | undefined,
969
992
  ): ContrarianSelection | undefined {
970
993
  const model = ctx.model;
971
- if (!model) return undefined;
994
+ if (!model || !isModelInScope(ctx, model)) return undefined;
972
995
  return toContrarianSelection(
973
996
  model,
974
997
  thinkingLevelOverride,
@@ -984,14 +1007,16 @@ function withFallbackReason(selection: ContrarianSelection, previous: Contrarian
984
1007
  }
985
1008
 
986
1009
  async function selectContrarianModel(
987
- ctx: { model?: PiModel; modelRegistry: { getAvailable(): PiModel[] | Promise<PiModel[]> } },
1010
+ ctx: ModelSelectionContext,
988
1011
  thinkingLevelOverride?: ThinkingLevel,
989
1012
  ): Promise<{ ok: true; selection: ContrarianSelection; ordered: ContrarianSelection[] } | { ok: false; error: string }> {
990
- const available = await ctx.modelRegistry.getAvailable();
1013
+ const available = await getAutoSelectableModels(ctx);
991
1014
  if (available.length === 0) {
992
1015
  return {
993
1016
  ok: false,
994
- error: "No authenticated models are available. Log in or configure an API key first.",
1017
+ error: hasModelScope(ctx)
1018
+ ? "No authenticated models are available in the current session model scope. Adjust the scope, log in, or configure an API key."
1019
+ : "No authenticated models are available. Log in or configure an API key first.",
995
1020
  };
996
1021
  }
997
1022
 
@@ -1340,6 +1365,7 @@ type ContrarianExtensionDeps = {
1340
1365
  };
1341
1366
 
1342
1367
  export const __test__ = {
1368
+ buildSessionFallbackSelection,
1343
1369
  createContrarianExtension,
1344
1370
  findAvailableModel,
1345
1371
  isModelAvailabilityError,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-contrarian",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "A read-only contrarian subagent extension for pi that stress-tests plans, designs, assumptions, and conclusions.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.1
1
+ 0.83.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegopetrucci/pi-dirty-repo-guard",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "A pi extension that prompts before session changes when the current git repo has uncommitted changes.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1 +1 @@
1
- 0.82.1
1
+ 0.83.0
@@ -230,19 +230,32 @@ never required to exist. Full shape, with defaults:
230
230
 
231
231
  Notes:
232
232
 
233
- - `thresholds.minCharsSaved` is reserved for a future minimum-size filter and
234
- is **not** currently enforced by the pipeline; the net-benefit gate's
235
- break-even math is what actually decides whether a prune applies.
233
+ - `thresholds.minCharsSaved` is a minimum-size floor enforced against fresh
234
+ **automatic** strategy proposals only, before the net-benefit gate runs: a
235
+ text-only candidate whose raw character savings (original text chars minus
236
+ placeholder chars) fall below this value is dropped up front and never
237
+ reaches gate or batch consideration. A tool-result candidate containing one
238
+ or more image blocks bypasses this raw-text floor (images do not receive
239
+ synthetic character counts) and is evaluated by the existing token-based
240
+ net-benefit gate. Manual prunes (`/prune`) and already-persisted/replayed
241
+ decisions always bypass this floor, same as the gate. Set to `0` to disable
242
+ filtering entirely. This is independent of the net-benefit gate's
243
+ break-even math (`gate.*`), which is a separate, later check on whatever
244
+ survives this floor.
236
245
  - `gate.breakEvenThresholdByState` lets `idle` vs `mid_loop` agent states use
237
246
  different thresholds; both default to `gate.breakEvenThreshold`. Real
238
- mid-loop/idle detection isn't wired through yet, so this always evaluates as
239
- `idle` in v1 — the field exists so the seam is ready once that lands. The
240
- representative-corpus benchmark (see [Roadmap](#roadmap)) shows `mid_loop`
241
- candidates carry ~all realized net benefit and `idle` candidates carry ~none
242
- at r=0.1, which argues for a stricter `idle` default — but since every live
243
- evaluation runs as `idle` today, defaulting it stricter would silently
244
- disable most automatic pruning. Both states are intentionally kept at
245
- parity until real agent-state detection lands; that's a follow-up ticket.
247
+ mid-loop/idle detection is wired through the `context` event handler
248
+ (pe-zy4s): the state is classified straight from the message payload —
249
+ `idle` if the most recent relevant message is a user message (this is the
250
+ first LLM call of the turn), `mid_loop` if it's an assistant message or a
251
+ tool result (the agent is iterating mid-turn). The representative-corpus
252
+ benchmark (see [Roadmap](#roadmap)) was re-derived against this same
253
+ runtime-observable definition and found the state-split REVERSED from an
254
+ earlier turn-end-based analysis: at r=0.1, `idle` candidates now carry
255
+ essentially all the realized net benefit and `mid_loop` candidates carry
256
+ essentially none. Since `idle` is not the worthless state under this
257
+ definition, both states are kept at parity (`22`/`22`) rather than forcing
258
+ a stricter default in either direction.
246
259
  - `gate.breakEvenThreshold`'s default of `22` is calibrated from the
247
260
  representative-corpus benchmark at `cachedPriceRatio` r=0.1 (aggressive
248
261
  prompt caching, the common case) — see [Roadmap](#roadmap) for the full
@@ -296,8 +309,46 @@ Options:
296
309
  - `--ratio R`: cached-price ratio(s) to model; repeatable and/or
297
310
  comma-separated (e.g. `--ratio 0.1,0.2`). Default: `0.1`.
298
311
  - `--json`: emit a full machine-readable JSON dump instead of aligned text.
312
+ - `--simulate-compression`: additionally SIMULATE v2-style range compression
313
+ (docs/v2-design.md §1/§4 go/no-go evidence) alongside — never mixed into —
314
+ the deterministic results. See below.
315
+ - `--sim-summary-fraction F`: summary size as a fraction of range tokens;
316
+ repeatable/comma-separated (e.g. `--sim-summary-fraction 0.1,0.15,0.3`).
317
+ Default: `0.15`.
318
+ - `--sim-summary-min-tokens N`: floor on summary tokens regardless of
319
+ fraction. Default: `200`.
320
+ - `--sim-summarizer-cost-mult M`: relative per-token price of the summarizer
321
+ call vs. the main model. Default: `1.0` (same per-token price; a
322
+ deliberate simplification that ignores summarizer latency/availability).
323
+ - `--sim-min-range-tokens N`: minimum contiguous range size to be
324
+ considered. Default: `2000`.
299
325
  - `--help`: print usage.
300
326
 
327
+ ### Compression-simulation mode (`--simulate-compression`)
328
+
329
+ This is a v2 go/no-go evidence generator that does **not** require building
330
+ v2: it identifies plausible v2-style `compress` (range mode) opportunities —
331
+ contiguous spans of complete, non-recent, non-protected messages, never
332
+ splitting a toolCall/toolResult pair — during the same replay pass, and
333
+ models the outcome:
334
+
335
+ ```text
336
+ summaryTokens = max(fraction * rangeTokens, summaryMinTokens)
337
+ oneTimeCost = cacheBustPenalty(earliest range position)
338
+ + summarizerCostMult * (rangeTokens + summaryTokens)
339
+ recurringSaving = r * (rangeTokens - summaryTokens)
340
+ realizedNetBenefit = actualRemainingCalls * recurringSaving - oneTimeCost
341
+ ```
342
+
343
+ Each distinct range is recorded once, at the earliest call boundary where it
344
+ first exists and meets `--sim-min-range-tokens`; ranges never overlap. The
345
+ same auto-expanding break-even threshold sweep as the deterministic
346
+ strategies runs against this separate SIMULATED population, reported
347
+ side-by-side with (but never combined with) the deterministic results — the
348
+ two populations are computed **independently**; a simulated range may fully
349
+ contain deterministic candidates, and no overlap/interaction between the two
350
+ is modeled or netted out.
351
+
301
352
  With no positional paths, it defaults to every `*.jsonl` file found
302
353
  recursively under `~/.pi/agent/sessions`.
303
354
 
@@ -387,6 +438,22 @@ realized benefit across the whole 1,390-session corpus is only ~20.6k
387
438
  token-units — economically marginal, on the order of pennies. Savings only
388
439
  become material at weaker caching, r>=0.25.
389
440
 
441
+ **pe-zy4s update (2026-07-08): this idle/mid_loop split used a turn-END
442
+ definition** ("idle" = this call IS the turn's final assistant message, only
443
+ knowable in hindsight via replay) **that was never runtime-observable.** Once
444
+ real runtime agent-state detection landed (turn-START definition: "idle" =
445
+ this is the FIRST LLM call of a turn; see the config-notes bullet above), the
446
+ benchmark's candidate labeling was aligned to that same definition and
447
+ re-derived on the representative corpus (460 gate-eligible candidates on the
448
+ current `~/.the-last-harness/agent/sessions` corpus). The split **reverses**:
449
+ at r=0.1, `idle` (T=22, ~20.6k realized net benefit) now carries essentially
450
+ all the value, and `mid_loop` (T=1, ~0 realized net benefit) carries
451
+ essentially none. Per the decision rule (stricter idle default only if idle
452
+ is shown ~worthless), this evidence does **not** support a stricter idle
453
+ default — parity (`22`/`22`) is kept. The table above is retained as the
454
+ historical turn-END-definition record; it no longer reflects how the runtime
455
+ classifies agent state.
456
+
390
457
  **This reframes, rather than weakens, the case for v2.** Small deterministic
391
458
  removals (dedupe/error-purge/superseded-file-ops) structurally cannot beat
392
459
  the cache-bust penalty at r=0.1 — there just isn't enough single-message