@lll9p/pi-better-compaction 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -44,6 +44,10 @@ If the file doesn't exist, all defaults apply. The extension never creates this
44
44
  ```jsonc
45
45
  {
46
46
  "enabled": true,
47
+ "midRun": {
48
+ "enabled": false,
49
+ "thresholdPercent": 80
50
+ },
47
51
  "compactionVersion": "v2",
48
52
  "compactionModel": null,
49
53
  "compactionThinkingLevel": "off",
@@ -65,6 +69,8 @@ If the file doesn't exist, all defaults apply. The extension never creates this
65
69
  | Option | Type | Default | Description |
66
70
  |--------|------|---------|-------------|
67
71
  | `enabled` | `boolean` | `true` | Master switch. Set `false` to disable the extension entirely. |
72
+ | `midRun.enabled` | `boolean` | `false` | Reserved and currently ignored. The mid-run guard remains disabled even when set to `true`. |
73
+ | `midRun.thresholdPercent` | `number` | `80` | Reserved threshold for the disabled mid-run guard. Must be greater than 0 and at most 100. |
68
74
  | `compactionVersion` | `"v1" \| "v2"` | `"v2"` | Protocol for Responses-family APIs. **V2** (streaming, encrypted blob) is the current OpenAI default. **V1** uses the legacy `/responses/compact` endpoint. |
69
75
  | `compactionModel` | `string \| null` | `null` | Model for fallback compaction (non-Responses APIs, or when native compact fails). Format: `"provider/model-id"`, e.g. `"openai/gpt-5.1-mini"`. `null` = let pi use the current chat model. |
70
76
  | `compactionThinkingLevel` | `string` | `"off"` | Thinking level for the fallback compaction model. One of: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`. |
package/README.zh-CN.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English](README.md) | 中文
4
4
 
5
- 一个 [pi](https://github.com/nicepkg/pi) 扩展,通过两条策略提升上下文压缩效果:
5
+ 一个 [pi](https://github.com/nicepkg/pi) 扩展,通过两条协同策略提升上下文压缩效果:
6
6
 
7
7
  1. **OpenAI Responses 系列 API** 使用提供商原生压缩端点,保留纯文本摘要无法留存的不透明上下文。
8
8
  2. **其他所有 API**(Anthropic、Gemini 等)可用一个**独立的低成本模型**执行 pi 内置压缩,避免在主模型上消耗额度。
@@ -44,6 +44,10 @@ cd pi-better-compaction && pi install .
44
44
  ```jsonc
45
45
  {
46
46
  "enabled": true,
47
+ "midRun": {
48
+ "enabled": false,
49
+ "thresholdPercent": 80
50
+ },
47
51
  "compactionVersion": "v2",
48
52
  "compactionModel": null,
49
53
  "compactionThinkingLevel": "off",
@@ -65,6 +69,8 @@ cd pi-better-compaction && pi install .
65
69
  | 选项 | 类型 | 默认值 | 说明 |
66
70
  |------|------|--------|------|
67
71
  | `enabled` | `boolean` | `true` | 总开关。设为 `false` 完全禁用扩展。 |
72
+ | `midRun.enabled` | `boolean` | `false` | 保留配置,当前会被忽略。即使设为 `true`,mid-run guard 仍保持禁用。 |
73
+ | `midRun.thresholdPercent` | `number` | `80` | 已禁用的 mid-run guard 的保留阈值。必须大于 0 且不超过 100。 |
68
74
  | `compactionVersion` | `"v1" \| "v2"` | `"v2"` | Responses 系列 API 的压缩协议。**V2**(流式,加密 blob)是 OpenAI 当前默认协议;**V1** 使用旧版 `/responses/compact` 端点。 |
69
75
  | `compactionModel` | `string \| null` | `null` | 回退压缩使用的模型(用于非 Responses API,或原生压缩失败时)。格式:`"provider/model-id"`,如 `"openai/gpt-5.1-mini"`。`null` = 由 pi 使用当前对话模型。 |
70
76
  | `compactionThinkingLevel` | `string` | `"off"` | 回退压缩模型的思考级别。可选:`off`、`minimal`、`low`、`medium`、`high`、`xhigh`、`max`。 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lll9p/pi-better-compaction",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "description": "Better compaction for pi: native /responses/compact replay for OpenAI Responses APIs, plus a configurable compaction model driving pi's native summarization everywhere else.",
6
6
  "author": "Lilin Lao",
@@ -38,6 +38,7 @@
38
38
  "src/debug.ts",
39
39
  "src/details-store.ts",
40
40
  "src/extension-runtime.ts",
41
+ "src/midrun.ts",
41
42
  "src/native-fallback.ts",
42
43
  "src/payload-rewrite.ts",
43
44
  "src/request-context-cache.ts",
package/src/config.ts CHANGED
@@ -60,6 +60,15 @@ function toBoolean(value: unknown, fieldPath: string, warnings: string[]): boole
60
60
  return undefined;
61
61
  }
62
62
 
63
+ function toThresholdPercent(value: unknown, fieldPath: string, warnings: string[]): number | undefined {
64
+ if (value === undefined) return undefined;
65
+ if (typeof value === "number" && Number.isFinite(value) && value > 0 && value <= 100) {
66
+ return value;
67
+ }
68
+ warnings.push(`Ignoring ${fieldPath}: expected a number greater than 0 and at most 100.`);
69
+ return undefined;
70
+ }
71
+
63
72
  function toModelSpec(value: unknown, fieldPath: string, warnings: string[]): string | null | undefined {
64
73
  if (value === undefined) return undefined;
65
74
  // Explicit null clears a spec, matching the documented "unset = current model" behavior.
@@ -119,6 +128,7 @@ export function loadExtensionConfig(configPath: string = CONFIG_PATH): LoadedExt
119
128
  const warnings: string[] = [];
120
129
  const resolved: ExtensionConfig = {
121
130
  ...DEFAULT_EXTENSION_CONFIG,
131
+ midRun: { ...DEFAULT_EXTENSION_CONFIG.midRun },
122
132
  responsesCompactApis: [...DEFAULT_EXTENSION_CONFIG.responsesCompactApis],
123
133
  };
124
134
  let source: string | undefined;
@@ -128,6 +138,19 @@ export function loadExtensionConfig(configPath: string = CONFIG_PATH): LoadedExt
128
138
  source = configPath;
129
139
 
130
140
  resolved.enabled = toBoolean(raw.enabled, "enabled", warnings) ?? resolved.enabled;
141
+
142
+ if (raw.midRun === undefined) {
143
+ // Keep defaults.
144
+ } else if (isRecord(raw.midRun)) {
145
+ resolved.midRun.enabled =
146
+ toBoolean(raw.midRun.enabled, "midRun.enabled", warnings) ?? resolved.midRun.enabled;
147
+ resolved.midRun.thresholdPercent =
148
+ toThresholdPercent(raw.midRun.thresholdPercent, "midRun.thresholdPercent", warnings) ??
149
+ resolved.midRun.thresholdPercent;
150
+ } else {
151
+ warnings.push("Ignoring midRun: expected a JSON object.");
152
+ }
153
+
131
154
  resolved.allowCompactionContinuityBreak =
132
155
  toBoolean(raw.allowCompactionContinuityBreak, "allowCompactionContinuityBreak", warnings) ??
133
156
  resolved.allowCompactionContinuityBreak;
@@ -40,6 +40,20 @@ type ResponsesCompactOutcome =
40
40
  | { outcome: "aborted" }
41
41
  | { outcome: "failed" };
42
42
 
43
+ export type ExtensionRuntimeDependencies = {
44
+ loadExtensionConfig: typeof loadExtensionConfig;
45
+ executeNativeCompaction: typeof executeNativeCompaction;
46
+ executeV2Compaction: typeof executeV2Compaction;
47
+ runNativeFallbackCompaction: typeof runNativeFallbackCompaction;
48
+ };
49
+
50
+ const DEFAULT_DEPENDENCIES: ExtensionRuntimeDependencies = {
51
+ loadExtensionConfig,
52
+ executeNativeCompaction,
53
+ executeV2Compaction,
54
+ runNativeFallbackCompaction,
55
+ };
56
+
43
57
  function buildCompactionRequestMeta(event: SessionBeforeCompactEvent): NativeCompactionRequestMeta {
44
58
  return {
45
59
  tokensBefore: event.preparation.tokensBefore,
@@ -91,7 +105,7 @@ function buildCompactionInstructions(systemPrompt: string, customInstructions?:
91
105
  return systemPrompt;
92
106
  }
93
107
 
94
- return `${systemPrompt}\n\nAdditional user guidance for this manual /compact request:\n${guidance}`;
108
+ return `${systemPrompt}\n\nAdditional compaction guidance:\n${guidance}`;
95
109
  }
96
110
 
97
111
  async function runResponsesV1Compact(
@@ -99,6 +113,7 @@ async function runResponsesV1Compact(
99
113
  ctx: ExtensionContext,
100
114
  config: ExtensionConfig,
101
115
  runtime: NativeCompactionRuntime,
116
+ dependencies: ExtensionRuntimeDependencies,
102
117
  ): Promise<ResponsesCompactOutcome> {
103
118
  const instructions = buildCompactionInstructions(ctx.getSystemPrompt(), event.customInstructions);
104
119
  const branchEntries = ctx.sessionManager.getBranch();
@@ -161,7 +176,7 @@ async function runResponsesV1Compact(
161
176
  request = { ...request, ...extras };
162
177
  }
163
178
 
164
- const compactResult = await executeNativeCompaction({
179
+ const compactResult = await dependencies.executeNativeCompaction({
165
180
  runtime,
166
181
  request,
167
182
  signal: event.signal,
@@ -251,6 +266,7 @@ async function runResponsesV2Compact(
251
266
  ctx: ExtensionContext,
252
267
  config: ExtensionConfig,
253
268
  runtime: NativeCompactionRuntime,
269
+ dependencies: ExtensionRuntimeDependencies,
254
270
  ): Promise<ResponsesCompactOutcome> {
255
271
  const instructions = buildCompactionInstructions(ctx.getSystemPrompt(), event.customInstructions);
256
272
  const branchEntries = ctx.sessionManager.getBranch();
@@ -311,7 +327,7 @@ async function runResponsesV2Compact(
311
327
  request = { ...request, ...extras };
312
328
  }
313
329
 
314
- const v2Result = await executeV2Compaction({
330
+ const v2Result = await dependencies.executeV2Compaction({
315
331
  runtime,
316
332
  request,
317
333
  signal: event.signal,
@@ -400,8 +416,12 @@ async function runResponsesV2Compact(
400
416
  return { outcome: "success", compaction };
401
417
  }
402
418
 
403
- async function handleSessionBeforeCompact(event: SessionBeforeCompactEvent, ctx: ExtensionContext) {
404
- const { config } = loadExtensionConfig();
419
+ async function handleSessionBeforeCompact(
420
+ event: SessionBeforeCompactEvent,
421
+ ctx: ExtensionContext,
422
+ dependencies: ExtensionRuntimeDependencies,
423
+ ) {
424
+ const { config } = dependencies.loadExtensionConfig();
405
425
  if (!config.enabled) {
406
426
  return undefined;
407
427
  }
@@ -436,9 +456,9 @@ async function handleSessionBeforeCompact(event: SessionBeforeCompactEvent, ctx:
436
456
  let responsesOutcome: ResponsesCompactOutcome;
437
457
 
438
458
  if (config.compactionVersion === "v2") {
439
- responsesOutcome = await runResponsesV2Compact(event, ctx, config, resolution.runtime);
459
+ responsesOutcome = await runResponsesV2Compact(event, ctx, config, resolution.runtime, dependencies);
440
460
  } else {
441
- responsesOutcome = await runResponsesV1Compact(event, ctx, config, resolution.runtime);
461
+ responsesOutcome = await runResponsesV1Compact(event, ctx, config, resolution.runtime, dependencies);
442
462
  }
443
463
 
444
464
  if (responsesOutcome.outcome === "success") {
@@ -465,7 +485,12 @@ async function handleSessionBeforeCompact(event: SessionBeforeCompactEvent, ctx:
465
485
  }
466
486
 
467
487
  // Branch 2: run pi's native compaction method with the configured model.
468
- const fallback = await runNativeFallbackCompaction({ ctx, event, config, sessionId: getSessionId(ctx) });
488
+ const fallback = await dependencies.runNativeFallbackCompaction({
489
+ ctx,
490
+ event,
491
+ config,
492
+ sessionId: getSessionId(ctx),
493
+ });
469
494
  if (fallback.ok) {
470
495
  if (ctx.hasUI) {
471
496
  ctx.ui.notify(
@@ -514,8 +539,12 @@ async function handleSessionBeforeCompact(event: SessionBeforeCompactEvent, ctx:
514
539
  return undefined;
515
540
  }
516
541
 
517
- async function handleBeforeProviderRequest(event: BeforeProviderRequestEvent, ctx: ExtensionContext) {
518
- const { config } = loadExtensionConfig();
542
+ async function handleBeforeProviderRequest(
543
+ event: BeforeProviderRequestEvent,
544
+ ctx: ExtensionContext,
545
+ dependencies: ExtensionRuntimeDependencies,
546
+ ) {
547
+ const { config } = dependencies.loadExtensionConfig();
519
548
  if (!config.enabled) {
520
549
  return undefined;
521
550
  }
@@ -638,9 +667,13 @@ async function handleBeforeProviderRequest(event: BeforeProviderRequestEvent, ct
638
667
  return rewrite.rewrittenPayload;
639
668
  }
640
669
 
641
- export default function (pi: ExtensionAPI) {
670
+ export function registerExtensionRuntime(
671
+ pi: ExtensionAPI,
672
+ dependencies: ExtensionRuntimeDependencies = DEFAULT_DEPENDENCIES,
673
+ ): void {
674
+ // Mid-run compaction is intentionally disabled regardless of config.
642
675
  pi.on("session_start", (_event, ctx) => {
643
- const { config, source, warnings } = loadExtensionConfig();
676
+ const { config, source, warnings } = dependencies.loadExtensionConfig();
644
677
  if (!config.enabled) return;
645
678
 
646
679
  if (warnings.length > 0 && ctx.hasUI && config.debug) {
@@ -669,11 +702,15 @@ export default function (pi: ExtensionAPI) {
669
702
  }
670
703
  });
671
704
 
672
- pi.on("session_before_compact", handleSessionBeforeCompact);
673
- pi.on("before_provider_request", handleBeforeProviderRequest);
705
+ pi.on("session_before_compact", (event, ctx) =>
706
+ handleSessionBeforeCompact(event, ctx, dependencies),
707
+ );
708
+ pi.on("before_provider_request", (event, ctx) =>
709
+ handleBeforeProviderRequest(event, ctx, dependencies),
710
+ );
674
711
 
675
712
  pi.on("session_compact_failed", (event, ctx) => {
676
- const { config } = loadExtensionConfig();
713
+ const { config } = dependencies.loadExtensionConfig();
677
714
  if (!config.enabled) return;
678
715
 
679
716
  writeDebugArtifact(
@@ -691,3 +728,5 @@ export default function (pi: ExtensionAPI) {
691
728
  );
692
729
  });
693
730
  }
731
+
732
+ export default registerExtensionRuntime;
package/src/midrun.ts ADDED
@@ -0,0 +1,229 @@
1
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ import { loadExtensionConfig } from "./config";
3
+ import { writeDebugArtifact } from "./debug";
4
+ import { EXTENSION_ID, type LoadedExtensionConfig } from "./types";
5
+
6
+ type MidRunPhase = "idle" | "abort-pending" | "compacting" | "resume-pending" | "failed";
7
+
8
+ type MidRunState = {
9
+ phase: MidRunPhase;
10
+ generation: number;
11
+ sessionId?: string;
12
+ baselineCompactionId?: string;
13
+ triggerTokens?: number;
14
+ triggerPercent?: number;
15
+ triggerContextWindow?: number;
16
+ };
17
+
18
+ type ConfigLoader = () => LoadedExtensionConfig;
19
+
20
+ const RESUME_CUSTOM_TYPE = "pi-better-compaction-midrun-resume";
21
+ const RESUME_PROMPT = `[pi-better-compaction/midrun]
22
+ Context compaction completed. Continue the interrupted task from the exact point where execution stopped.`;
23
+ const MIDRUN_COMPACTION_INSTRUCTIONS =
24
+ "Preserve the active task, completed work, decisions, changed files, failures, current tool-loop state, and exact next steps so execution can resume immediately after compaction.";
25
+
26
+ function getSessionId(ctx: ExtensionContext): string | undefined {
27
+ try {
28
+ return ctx.sessionManager.getSessionId();
29
+ } catch {
30
+ return undefined;
31
+ }
32
+ }
33
+
34
+ function getLatestCompactionId(ctx: ExtensionContext): string | undefined {
35
+ const branch = ctx.sessionManager.getBranch();
36
+ for (let index = branch.length - 1; index >= 0; index--) {
37
+ const entry = branch[index];
38
+ if (entry?.type === "compaction") return entry.id;
39
+ }
40
+ return undefined;
41
+ }
42
+
43
+ function resetState(state: MidRunState): void {
44
+ state.phase = "idle";
45
+ state.generation += 1;
46
+ state.sessionId = undefined;
47
+ state.baselineCompactionId = undefined;
48
+ state.triggerTokens = undefined;
49
+ state.triggerPercent = undefined;
50
+ state.triggerContextWindow = undefined;
51
+ }
52
+
53
+ function sameSession(state: MidRunState, ctx: ExtensionContext): boolean {
54
+ return state.sessionId !== undefined && state.sessionId === getSessionId(ctx);
55
+ }
56
+
57
+ function notifyFailure(ctx: ExtensionContext, message: string): void {
58
+ if (ctx.hasUI) {
59
+ ctx.ui.notify(`${EXTENSION_ID}: ${message}`, "error");
60
+ }
61
+ }
62
+
63
+ function scheduleResume(
64
+ pi: ExtensionAPI,
65
+ ctx: ExtensionContext,
66
+ state: MidRunState,
67
+ generation: number,
68
+ ): void {
69
+ state.phase = "resume-pending";
70
+
71
+ setImmediate(() => {
72
+ if (
73
+ state.generation !== generation ||
74
+ state.phase !== "resume-pending" ||
75
+ !sameSession(state, ctx)
76
+ ) {
77
+ return;
78
+ }
79
+
80
+ if (!ctx.isIdle() || ctx.hasPendingMessages()) {
81
+ resetState(state);
82
+ return;
83
+ }
84
+
85
+ resetState(state);
86
+ pi.sendMessage(
87
+ {
88
+ customType: RESUME_CUSTOM_TYPE,
89
+ content: RESUME_PROMPT,
90
+ display: false,
91
+ details: { source: "midrun-compaction" },
92
+ },
93
+ { triggerTurn: true },
94
+ );
95
+ });
96
+ }
97
+
98
+ export function registerMidRunGuard(
99
+ pi: ExtensionAPI,
100
+ loadConfig: ConfigLoader = loadExtensionConfig,
101
+ ): void {
102
+ const state: MidRunState = { phase: "idle", generation: 0 };
103
+
104
+ pi.on("turn_end", (event, ctx) => {
105
+ const { config } = loadConfig();
106
+ if (!config.enabled || !config.midRun.enabled || state.phase !== "idle") return;
107
+ if (event.toolResults.length === 0 || ctx.hasPendingMessages()) return;
108
+
109
+ const usage = ctx.getContextUsage();
110
+ if (!usage || usage.tokens == null || usage.percent == null) return;
111
+ if (usage.percent < config.midRun.thresholdPercent) return;
112
+
113
+ const sessionId = getSessionId(ctx);
114
+ if (!sessionId) return;
115
+
116
+ state.phase = "abort-pending";
117
+ state.generation += 1;
118
+ state.sessionId = sessionId;
119
+ state.baselineCompactionId = getLatestCompactionId(ctx);
120
+ state.triggerTokens = usage.tokens;
121
+ state.triggerPercent = usage.percent;
122
+ state.triggerContextWindow = usage.contextWindow;
123
+
124
+ writeDebugArtifact(
125
+ "lifecycle",
126
+ {
127
+ event: "midrun.threshold",
128
+ turnIndex: event.turnIndex,
129
+ tokens: usage.tokens,
130
+ contextWindow: usage.contextWindow,
131
+ percent: usage.percent,
132
+ thresholdPercent: config.midRun.thresholdPercent,
133
+ baselineCompactionId: state.baselineCompactionId,
134
+ },
135
+ config,
136
+ ctx,
137
+ );
138
+
139
+ // Never compact while the agent run is active; let Pi settle first.
140
+ ctx.abort();
141
+ });
142
+
143
+ pi.on("agent_settled", (_event, ctx) => {
144
+ if (state.phase !== "abort-pending") return;
145
+ if (!sameSession(state, ctx)) {
146
+ resetState(state);
147
+ return;
148
+ }
149
+
150
+ // An earlier agent_settled handler may already have started or queued another run.
151
+ if (!ctx.isIdle() || ctx.hasPendingMessages()) {
152
+ resetState(state);
153
+ return;
154
+ }
155
+
156
+ const generation = state.generation;
157
+ const latestCompactionId = getLatestCompactionId(ctx);
158
+ if (latestCompactionId !== state.baselineCompactionId) {
159
+ const { config } = loadConfig();
160
+ writeDebugArtifact(
161
+ "lifecycle",
162
+ {
163
+ event: "midrun.coalesced",
164
+ reason: "compaction-already-occurred-during-abort",
165
+ baselineCompactionId: state.baselineCompactionId,
166
+ latestCompactionId,
167
+ },
168
+ config,
169
+ ctx,
170
+ );
171
+ scheduleResume(pi, ctx, state, generation);
172
+ return;
173
+ }
174
+
175
+ const { config } = loadConfig();
176
+ if (!config.enabled || !config.midRun.enabled) {
177
+ scheduleResume(pi, ctx, state, generation);
178
+ return;
179
+ }
180
+
181
+ const usage = ctx.getContextUsage();
182
+ if (usage?.percent != null && usage.percent < config.midRun.thresholdPercent) {
183
+ scheduleResume(pi, ctx, state, generation);
184
+ return;
185
+ }
186
+
187
+ state.phase = "compacting";
188
+ ctx.compact({
189
+ customInstructions: MIDRUN_COMPACTION_INSTRUCTIONS,
190
+ onComplete: () => {
191
+ if (
192
+ state.generation !== generation ||
193
+ state.phase !== "compacting" ||
194
+ !sameSession(state, ctx)
195
+ ) {
196
+ return;
197
+ }
198
+ scheduleResume(pi, ctx, state, generation);
199
+ },
200
+ onError: (error) => {
201
+ if (state.generation !== generation || !sameSession(state, ctx)) return;
202
+
203
+ const latest = getLatestCompactionId(ctx);
204
+ if (/Already compacted/i.test(error.message) && latest !== state.baselineCompactionId) {
205
+ scheduleResume(pi, ctx, state, generation);
206
+ return;
207
+ }
208
+
209
+ state.phase = "failed";
210
+ writeDebugArtifact(
211
+ "lifecycle",
212
+ {
213
+ event: "midrun.failed",
214
+ errorMessage: error.message,
215
+ triggerTokens: state.triggerTokens,
216
+ triggerPercent: state.triggerPercent,
217
+ triggerContextWindow: state.triggerContextWindow,
218
+ },
219
+ config,
220
+ ctx,
221
+ );
222
+ notifyFailure(ctx, `mid-run compaction failed: ${error.message}`);
223
+ },
224
+ });
225
+ });
226
+
227
+ pi.on("session_start", () => resetState(state));
228
+ pi.on("session_shutdown", () => resetState(state));
229
+ }
package/src/types.ts CHANGED
@@ -33,8 +33,14 @@ export type DebugArtifactKind =
33
33
  | "compaction-event"
34
34
  | "lifecycle";
35
35
 
36
+ export type MidRunConfig = {
37
+ enabled: boolean;
38
+ thresholdPercent: number;
39
+ };
40
+
36
41
  export type ExtensionConfig = {
37
42
  enabled: boolean;
43
+ midRun: MidRunConfig;
38
44
  /**
39
45
  * Allow a Responses session whose latest compaction was not created by this extension
40
46
  * to restart native compaction from Pi's current serialized session context.
@@ -304,6 +310,10 @@ export function createNativeCompactionResult(
304
310
 
305
311
  export const DEFAULT_EXTENSION_CONFIG: ExtensionConfig = {
306
312
  enabled: true,
313
+ midRun: {
314
+ enabled: false,
315
+ thresholdPercent: 80,
316
+ },
307
317
  allowCompactionContinuityBreak: false,
308
318
  compactionModel: undefined,
309
319
  compactionThinkingLevel: "off",