@mediadatafusion/pi-workflow-suite 0.0.16 → 0.0.18

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/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  All notable public releases will be documented in this file.
4
4
 
5
+ ## [0.0.18] - 2026-06-10
6
+
7
+ ### Improved
8
+
9
+ - Improved package metadata and compaction settings documentation.
10
+ - Refined compaction safeguards for default and custom configuration flows.
11
+
12
+ ## [0.0.17] - 2026-06-10
13
+
14
+ ### Improved
15
+
16
+ - Improved package metadata to better reflect the suite's workflow modes, configuration surfaces, and orchestration features.
17
+
5
18
  ## [0.0.16] - 2026-06-09
6
19
 
7
20
  ### Fixed
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  [![Install](https://cdn.jsdelivr.net/npm/@mediadatafusion/pi-workflow-suite@0.0.12/docs/assets/readme-link-install.svg)](#installation) [![Quick Start](https://cdn.jsdelivr.net/npm/@mediadatafusion/pi-workflow-suite@0.0.12/docs/assets/readme-link-quick-start.svg)](#quick-start) [![Commands](https://cdn.jsdelivr.net/npm/@mediadatafusion/pi-workflow-suite@0.0.12/docs/assets/readme-link-commands.svg)](#core-commands) [![Settings](https://cdn.jsdelivr.net/npm/@mediadatafusion/pi-workflow-suite@0.0.12/docs/assets/readme-link-settings.svg)](#settings-reference)
6
6
 
7
- **Workflow Suite Version:** `v0.0.16`
7
+ **Workflow Suite Version:** `v0.0.18`
8
8
 
9
9
  ## Overview
10
10
 
@@ -118,7 +118,7 @@ Pi Workflow Suite turns Pi into a guided workflow environment:
118
118
  | Interactive Diagrams | `workflow_diagram` Mermaid support with terminal preview, SVG-first clickable artifacts, PNG/runtime rendering support, dark-mode-friendly styling, and runtime artifact storage. |
119
119
  | Web Research & Browser Verification | First-party `workflow_web_search`, `workflow_web_fetch`, and `workflow_browser_check` tools. Search and fetch for public web evidence with source URLs, blocked local/private/internal hosts, and time/size limits. Headless browser verification for runtime web app validation with interactive UI actions (click, type, read, screenshot, evaluate). |
120
120
  | Repo Lock | Project-scoped Global Safety control that constrains normal file tools, bash path checks, and sub-agents to the active repository, with protected configuration paths and clear non-sandbox caveats. |
121
- | Compaction | Pi default, custom model, or disabled Workflow Suite compaction so context summarization can use its own provider/model, proactive threshold checks, idle-boundary execution, custom token tuning, adaptive fitting, status reporting, and safe fallback. |
121
+ | Compaction | Shared compaction controls for Pi default or custom summary model selection, Pi default or custom trigger thresholds, idle-boundary proactive checks, custom token tuning, adaptive fitting, status reporting, and safe fallback. |
122
122
  | Token Budgets | Optional per-mode token and runtime caps (`maxTokens`, `maxRuntimeHours`) for Plan, Mission, and Standard Mode. Off by default (unlimited). When enabled, Workflow Suite tracks cumulative usage and blocks further agent turns when the budget is exceeded. |
123
123
  | Workflow Roles | Planner, Executor, Reviewer, Validator, Mission, and compaction responsibilities are separated by phase so each job has clear boundaries and can be matched to the right model. |
124
124
  | Model Selection | Configure which provider/model and thinking level powers each workflow role, with shared defaults plus Standard-specific and Mission-specific overrides for simpler or higher-rigor setups. |
@@ -799,27 +799,34 @@ Compaction settings are available through:
799
799
  /workflow settings Shared Compaction
800
800
  ```
801
801
 
802
- Supported modes:
802
+ The grouped Shared Compaction menu separates trigger behavior from summary model selection. The trigger can use Pi default behavior or an explicit Workflow Suite threshold, while the compaction provider/model controls which model writes compaction summaries when configured.
803
803
 
804
- - **Pi default** preserve Pi built-in compaction.
805
- - **Custom model** — route compaction summaries through a configured provider/model when available.
806
- - **Disabled** — disables Workflow Suite custom compaction and leaves Pi fallback behavior.
807
- - **Custom agent** — planned-only/backward-compatible mode; current releases keep Pi default fallback behavior instead of running agent-routed compaction.
804
+ Workflow Suite can request proactive compaction when context usage reaches the configured threshold. Actual proactive compaction runs only at a safe after-turn idle agent boundary, so it does not interrupt arbitrary tool execution or queued workflow handoffs. Pi default auto-compaction remains available as a safety fallback near the model limit.
808
805
 
809
- Workflow Suite can request proactive compaction when context usage reaches the configured threshold. For Custom model mode, a configured compaction provider/model with custom compaction enabled arms the threshold trigger even when generic auto-trigger behavior is otherwise off. This lets compaction use a different model than planning, execution, review, or validation, which is useful when context summarization has a different cost, speed, or context-window requirement than active workflow work. Actual proactive compaction runs only at a safe after-turn idle agent boundary, so it does not interrupt arbitrary tool execution or queued workflow handoffs. Pi default auto-compaction remains available as a safety fallback near the model limit.
806
+ The menu controls provider/model preference, auto-trigger behavior, trigger threshold, cooldown, reserve tokens, and keep-recent tokens:
810
807
 
811
- The grouped Shared Compaction menu controls auto-compaction, trigger threshold, cooldown, reserve tokens, keep-recent tokens, and custom compaction model behavior.
808
+ ```text
809
+ Shared Compaction Settings
810
+
811
+ Compaction Provider
812
+ Compaction Model
813
+ Workflow Auto Trigger Enabled
814
+ Workflow Trigger Percent
815
+ Workflow Trigger Cooldown
816
+ Custom Reserve Tokens
817
+ Custom Keep Recent Tokens
818
+ List Current Settings
819
+ Back
820
+ ```
821
+
822
+ Each configurable compaction setting includes a Pi default option. Provider and model choices do not change the trigger threshold. The trigger reports `Pi default` when the native Pi threshold formula is in control and reports `custom, <percent>%` only when an explicit Workflow Suite trigger override is set.
812
823
 
813
824
  Important behavior:
814
825
 
815
826
  - **Safe boundary**: proactive compaction runs at a safe after-turn idle agent boundary only.
816
827
  - **Cooldown**: the cooldown is the minimum wait between proactive attempts; it is not a delay before compaction starts and does not block manual compaction.
817
- - **Adaptive fit**: Custom model preparation is fit to the selected compaction model context window using configured reserve and keep-recent token settings.
818
- - **Fallback**: Custom model compaction falls back to Pi default behavior if required settings are missing, the model cannot be found, authentication or API key checks fail, preparation cannot fit, or the compaction call errors.
819
- - **Status**: `/workflow settings Show Current Settings` reports the effective trigger state, runtime status, selected custom model, reserve/keep-recent token settings, latest check decision, and `Last Custom Compaction Status`.
820
-
821
- The legacy `workflowCompactionCheckMode` setting may still exist for compatibility, but the primary release behavior is safe after-turn idle-boundary compaction. It is not permission to compact in the middle of arbitrary tool execution.
822
-
828
+ - **Adaptive fit**: Custom summary model preparation is fit to the selected model context window using configured reserve and keep-recent token settings.
829
+ - **Fallback**: Custom summary model compaction falls back to Pi default behavior if required settings are missing, the model cannot be found, authentication or API key checks fail, preparation cannot fit, or the compaction call errors.
823
830
  ## Diagram Support
824
831
 
825
832
  Workflow Suite includes a `workflow_diagram` tool for Mermaid diagrams in workflow explanations, architecture notes, state transitions, data flows, export/share flows, and dependency maps. It is available to Workflow Suite execution and validation surfaces through the shared workflow tool set.
@@ -1007,8 +1014,8 @@ pi install -l npm:@mediadatafusion/pi-workflow-suite
1007
1014
  ### Installing specific versions
1008
1015
 
1009
1016
  ```bash
1010
- pi install npm:@mediadatafusion/pi-workflow-suite@0.0.16
1011
- pi install -l npm:@mediadatafusion/pi-workflow-suite@0.0.16
1017
+ pi install npm:@mediadatafusion/pi-workflow-suite@0.0.18
1018
+ pi install -l npm:@mediadatafusion/pi-workflow-suite@0.0.18
1012
1019
  ```
1013
1020
 
1014
1021
  An unversioned install follows normal update behavior: `pi update` and `pi update --extensions` will pick up new package releases. A versioned install pins the package to that version. Pinned package specs are intentionally skipped by Pi's normal package update commands. To move a pinned install to a newer version, reinstall with the desired version. To switch back to latest tracking, use the unversioned install command without `@<version>`.
@@ -1124,7 +1131,7 @@ Primary settings areas:
1124
1131
  - `standard` — Standard Mode enablement, dynamic To Do tracking, configurable clarification, widgets, model role, Standard sub-agent orchestration, and shared model selection.
1125
1132
  - `missions` — Mission Mode autonomy, runtime, checkpoints, validation, repair, and mission-specific model selection.
1126
1133
  - `subagents` — workflow phase policies, worker targets, activity indicator, parallelism preferences, and edit-concurrency guidance. These settings do not edit arbitrary sub-agent tool permissions.
1127
- - `context` — compaction mode, custom compaction provider/model, proactive trigger percentage, check mode, cooldown, and fallback status.
1134
+ - `context` — compaction provider/model, auto-trigger behavior, trigger percentage, cooldown, reserve tokens, and keep-recent tokens.
1128
1135
  - `ui` — widgets, shortcuts, Workflow Suite theme selection, startup visuals, startup logo text styling, footer/status hints, and optional themed input borders.
1129
1136
  - `safety` — bash/tool guard settings.
1130
1137
 
@@ -1207,17 +1214,17 @@ See `docs/TROUBLESHOOTING.md` for detailed diagnostics.
1207
1214
 
1208
1215
  ## Operational Notes
1209
1216
 
1210
- - Use Custom model mode for active custom compaction today. Custom agent mode is reserved for agent-routed compaction and keeps Pi fallback behavior in current releases.
1217
+ - Configure a compaction provider/model when summaries should use a custom model; trigger behavior remains separate and can stay on Pi default behavior.
1211
1218
  - Mission checkpoints are created at workflow gates and progress saves. The checkpoint interval setting records the preferred cadence for future timed checkpoint automation.
1212
1219
  - Watchdog status can report stale mission activity when enabled. Recovery remains user-supervised so Mission Mode does not resume or mutate work unexpectedly.
1213
1220
  - Parallel file edits are governed separately from parallel sub-agent work. Scoped parallel edit mode requires conflict protection; otherwise the main workflow keeps writes serialized for safety.
1214
1221
 
1215
1222
  ## Versioning
1216
1223
 
1217
- The current preparation version is `v0.0.16`. Version information is intentionally aligned across:
1224
+ The current preparation version is `v0.0.18`. Version information is intentionally aligned across:
1218
1225
 
1219
- - `VERSION` (`v0.0.16`),
1220
- - `package.json` (`0.0.16`),
1226
+ - `VERSION` (`v0.0.18`),
1227
+ - `package.json` (`0.0.18`),
1221
1228
  - `package-lock.json`,
1222
1229
  - this README,
1223
1230
  - Workflow Suite settings/about output.
package/VERSION CHANGED
@@ -1 +1 @@
1
- v0.0.16
1
+ v0.0.18
@@ -1434,14 +1434,8 @@ export function workflowSettingsConsistencyDiagnostics(settings: WorkflowSetting
1434
1434
  if (settings.missions.finalValidationEnabled === true && (!settings.models.validator.enabled || !roleIsConfigured(settings.models.validator))) {
1435
1435
  diagnostics.push("mission final validation is enabled but the shared validator model is disabled or unconfigured");
1436
1436
  }
1437
- if (settings.context.compactionMode === "custom_model" && (!settings.context.compactionModelProvider || !settings.context.compactionModel)) {
1438
- diagnostics.push("custom compaction model mode is selected but compaction provider/model is incomplete");
1439
- }
1440
- if (settings.context.compactionMode === "custom_model" && settings.context.customCompactionEnabled !== true) {
1441
- diagnostics.push("custom compaction mode is selected but customCompactionEnabled is not set — custom compaction will not activate");
1442
- }
1443
- if (settings.context.customCompactionEnabled === true && settings.context.compactionMode !== "custom_model") {
1444
- diagnostics.push(`customCompactionEnabled=true but compactionMode=${settings.context.compactionMode}`);
1437
+ if ((settings.context.compactionModelProvider || settings.context.compactionModel) && (!settings.context.compactionModelProvider || !settings.context.compactionModel)) {
1438
+ diagnostics.push("custom compaction summary model preference is incomplete");
1445
1439
  }
1446
1440
  if (settings.missions.autoRepairReviewFailures !== false && settings.missions.reviewRetryMode === "off") {
1447
1441
  diagnostics.push("mission review auto-repair is enabled but reviewRetryMode is off — override to safe_only will be applied at runtime; set reviewRetryMode explicitly to avoid confusion");
@@ -206,6 +206,15 @@ type PiSessionEntry = SessionEntry;
206
206
  const DEFAULT_PI_COMPACTION_RESERVE_TOKENS = 16_384;
207
207
  const DEFAULT_PI_COMPACTION_KEEP_RECENT_TOKENS = 20_000;
208
208
  const CUSTOM_COMPACTION_PROMPT_OVERHEAD_TOKENS = 8_192;
209
+ const PI_SETTINGS_FILE = join(AGENT_DIR, "settings.json");
210
+
211
+ type PiNativeCompactionSettings = {
212
+ enabled: boolean;
213
+ reserveTokens: number;
214
+ keepRecentTokens: number;
215
+ source: "default" | "global" | "project";
216
+ file?: string;
217
+ };
209
218
 
210
219
  function loadPiCompactionApi(): PiCompactionApi {
211
220
  return {
@@ -215,6 +224,85 @@ function loadPiCompactionApi(): PiCompactionApi {
215
224
  };
216
225
  }
217
226
 
227
+ function readJsonObject(path: string): Record<string, unknown> | undefined {
228
+ try {
229
+ const parsed = JSON.parse(readFileSync(path, "utf8")) as unknown;
230
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed as Record<string, unknown> : undefined;
231
+ } catch {
232
+ return undefined;
233
+ }
234
+ }
235
+
236
+ function findProjectPiSettings(cwd: string): string | undefined {
237
+ let dir = cwd;
238
+ for (let i = 0; i < 40; i += 1) {
239
+ const candidate = join(dir, ".pi", "settings.json");
240
+ if (existsSync(candidate)) return candidate;
241
+ const parent = dirname(dir);
242
+ if (parent === dir) break;
243
+ dir = parent;
244
+ }
245
+ return undefined;
246
+ }
247
+
248
+ function coercePiCompactionSettings(raw: unknown): Partial<PiNativeCompactionSettings> | undefined {
249
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return undefined;
250
+ const compaction = raw as Record<string, unknown>;
251
+ const settings: Partial<PiNativeCompactionSettings> = {};
252
+ if (typeof compaction.enabled === "boolean") settings.enabled = compaction.enabled;
253
+ const reserveTokens = Number(compaction.reserveTokens);
254
+ if (Number.isFinite(reserveTokens) && reserveTokens > 0) settings.reserveTokens = Math.round(reserveTokens);
255
+ const keepRecentTokens = Number(compaction.keepRecentTokens);
256
+ if (Number.isFinite(keepRecentTokens) && keepRecentTokens > 0) settings.keepRecentTokens = Math.round(keepRecentTokens);
257
+ return settings;
258
+ }
259
+
260
+ function loadPiNativeCompactionSettings(cwd?: string): PiNativeCompactionSettings {
261
+ let settings: PiNativeCompactionSettings = {
262
+ enabled: true,
263
+ reserveTokens: DEFAULT_PI_COMPACTION_RESERVE_TOKENS,
264
+ keepRecentTokens: DEFAULT_PI_COMPACTION_KEEP_RECENT_TOKENS,
265
+ source: "default",
266
+ };
267
+ const globalSettings = existsSync(PI_SETTINGS_FILE) ? coercePiCompactionSettings(readJsonObject(PI_SETTINGS_FILE)?.compaction) : undefined;
268
+ if (globalSettings) settings = { ...settings, ...globalSettings, source: "global", file: PI_SETTINGS_FILE };
269
+
270
+ const projectFile = cwd ? findProjectPiSettings(cwd) : undefined;
271
+ const projectSettings = projectFile ? coercePiCompactionSettings(readJsonObject(projectFile)?.compaction) : undefined;
272
+ if (projectSettings) settings = { ...settings, ...projectSettings, source: "project", file: projectFile };
273
+ return settings;
274
+ }
275
+
276
+ function contextWindowFrom(ctx?: ExtensionContext, usage?: { contextWindow?: number }): number | undefined {
277
+ const contextWindow = Number(usage?.contextWindow ?? ctx?.getContextUsage?.()?.contextWindow ?? ctx?.model?.contextWindow);
278
+ return Number.isFinite(contextWindow) && contextWindow > 0 ? contextWindow : undefined;
279
+ }
280
+
281
+ function piNativeCompactionThreshold(cwd: string | undefined, contextWindow: number | undefined): { settings: PiNativeCompactionSettings; triggerTokens?: number; triggerPercent?: number } {
282
+ const settings = loadPiNativeCompactionSettings(cwd);
283
+ if (!settings.enabled || !contextWindow) return { settings };
284
+ const threshold = compactionThresholdForReserve(contextWindow, settings.reserveTokens);
285
+ return { settings, ...threshold };
286
+ }
287
+
288
+ function compactionThresholdForReserve(contextWindow: number, reserveTokens: number): { triggerTokens: number; triggerPercent: number } {
289
+ const triggerTokens = Math.max(0, contextWindow - reserveTokens);
290
+ return { triggerTokens, triggerPercent: (triggerTokens / contextWindow) * 100 };
291
+ }
292
+
293
+ function formatPiNativeCompactionThreshold(cwd: string | undefined, contextWindow: number | undefined): string {
294
+ const threshold = piNativeCompactionThreshold(cwd, contextWindow);
295
+ if (!threshold.settings.enabled) return "disabled";
296
+ return "Pi default";
297
+ }
298
+
299
+ function formatSharedCompactionTrigger(settings: ReturnType<typeof loadWorkflowSettings>, ctx?: ExtensionContext): string {
300
+ if (settings.context.compactionMode === "disabled" || settings.context.autoCompactionEnabled === false) return "disabled";
301
+ const override = compactionTriggerPercentOverride(settings);
302
+ if (override != null) return `custom, ${override}%`;
303
+ return formatPiNativeCompactionThreshold(ctx?.cwd, contextWindowFrom(ctx));
304
+ }
305
+
218
306
  function createWorkflowCompactionFileOps(): FileOperations {
219
307
  return { read: new Set<string>(), written: new Set<string>(), edited: new Set<string>() };
220
308
  }
@@ -2675,15 +2763,15 @@ Mission Mode settings:
2675
2763
 
2676
2764
  Compaction:
2677
2765
  - /workflow-settings configure compaction opens the selectable compaction menu
2678
- - Compaction Provider and Compaction Model use the model registry provider/model picker
2679
- - /workflow-settings set context compactionMode pi_default|custom_model|disabled (custom_agent remains backward-compatible but hidden until implemented)
2680
- - /workflow-settings set context customCompactionEnabled true|false
2681
- - /workflow-settings set context autoCompactionEnabled true|false
2766
+ - Compaction Provider and Compaction Model select the summary model preference
2767
+ - /workflow-settings set context autoCompactionEnabled true|false|default
2682
2768
  - /workflow-settings set context compactionTriggerPercent 50-95|default|reset
2683
- - /workflow-settings set context compactionCooldownMinutes 0-240
2769
+ - /workflow-settings set context compactionCooldownMinutes 0-240|default|reset
2684
2770
  - /workflow-settings set context customCompactionReserveTokens 4096-65536|default|reset
2685
2771
  - /workflow-settings set context customCompactionKeepRecentTokens 1000-200000|default|reset
2686
2772
  - /workflow-settings set context workflowCompactionCheckMode boundary|in_session (legacy/backward-compatible; primary UI uses safe after-turn compaction)
2773
+ - /workflow-settings set context compactionMode pi_default|custom_model|disabled (legacy/backward-compatible; primary UI uses trigger/provider/model controls)
2774
+ - /workflow-settings set context customCompactionEnabled true|false (legacy/backward-compatible; no longer required for summary model routing)
2687
2775
  - /workflow-settings set context compactionModelProvider <provider> (advanced/manual fallback)
2688
2776
  - /workflow-settings set context compactionModel <model> (advanced/manual fallback)
2689
2777
  - /workflow-settings set context compactionAgent <agent> (legacy/backward-compatible; custom agent is planned only)
@@ -3172,40 +3260,34 @@ function workflowCompactionCheckRuntimeStatus(): string {
3172
3260
  return `Last Check: ${workflowLastCompactionCheckAt}\nLast Decision: ${workflowLastCompactionDecision}`;
3173
3261
  }
3174
3262
 
3175
- function customModelCompactionConfigured(settings: ReturnType<typeof loadWorkflowSettings>): boolean {
3176
- return settings.context.customCompactionEnabled === true
3177
- && settings.context.compactionMode === "custom_model"
3178
- && Boolean(settings.context.compactionModelProvider)
3263
+ function summaryModelConfigured(settings: ReturnType<typeof loadWorkflowSettings>): boolean {
3264
+ return Boolean(settings.context.compactionModelProvider)
3179
3265
  && Boolean(settings.context.compactionModel);
3180
3266
  }
3181
3267
 
3268
+ function customModelCompactionConfigured(settings: ReturnType<typeof loadWorkflowSettings>): boolean {
3269
+ return settings.context.compactionMode !== "disabled" && summaryModelConfigured(settings);
3270
+ }
3271
+
3182
3272
  function workflowProactiveCompactionEffective(settings: ReturnType<typeof loadWorkflowSettings>): boolean {
3183
3273
  if (settings.context.compactionMode === "disabled") return false;
3184
3274
  return settings.context.autoCompactionEnabled === true;
3185
3275
  }
3186
3276
 
3187
3277
  function compactionIntegrationStatus(settings: ReturnType<typeof loadWorkflowSettings>): string {
3188
- if (!settings.context.customCompactionEnabled || settings.context.compactionMode === "pi_default") return "Pi default behavior is active";
3189
- if (settings.context.compactionMode === "custom_model") {
3190
- if (!settings.context.compactionModelProvider || !settings.context.compactionModel) return "Custom model selected but provider/model is missing. Fallback: Pi default compaction.";
3191
- return `Custom model active through Pi's session_before_compact hook using ${settings.context.compactionModelProvider}/${settings.context.compactionModel}. Fallback: Pi default if custom compaction cannot run or fails.`;
3192
- }
3278
+ if (settings.context.compactionMode === "disabled") return "Workflow compaction disabled";
3279
+ if (summaryModelConfigured(settings)) return `Custom summary model configured through Pi's session_before_compact hook using ${settings.context.compactionModelProvider}/${settings.context.compactionModel}. Fallback: Pi default if custom compaction cannot run or fails.`;
3193
3280
  if (settings.context.compactionMode === "custom_agent") return "Custom agent compaction is planned only. Fallback: Pi default compaction.";
3194
- return "Workflow custom compaction disabled; Pi default behavior applies";
3195
- }
3196
-
3197
- function defaultCompactionTriggerPercent(): number {
3198
- return 50;
3281
+ return "Pi default summary model is active";
3199
3282
  }
3200
3283
 
3201
- function compactionTriggerPercent(settings: ReturnType<typeof loadWorkflowSettings>): number {
3202
- const fallback = defaultCompactionTriggerPercent();
3203
- const value = Number(settings.context.compactionTriggerPercent ?? fallback);
3204
- return Number.isFinite(value) && value >= 50 && value <= 95 ? Math.round(value) : fallback;
3284
+ function compactionTriggerPercentOverride(settings: ReturnType<typeof loadWorkflowSettings>): number | undefined {
3285
+ const value = Number(settings.context.compactionTriggerPercent);
3286
+ return Number.isFinite(value) && value >= 50 && value <= 95 ? Math.round(value) : undefined;
3205
3287
  }
3206
3288
 
3207
3289
  function compactionTriggerOverrideLabel(settings: ReturnType<typeof loadWorkflowSettings>): string {
3208
- const value = settings.context.compactionTriggerPercent;
3290
+ const value = compactionTriggerPercentOverride(settings);
3209
3291
  return typeof value === "number" ? `${value}%` : "none";
3210
3292
  }
3211
3293
 
@@ -3220,18 +3302,9 @@ function resetCompactionContextToPiDefault(context: ReturnType<typeof loadWorkfl
3220
3302
  }
3221
3303
 
3222
3304
  function effectiveWorkflowCompactionTriggerPercent(settings: ReturnType<typeof loadWorkflowSettings>, ctx: ExtensionContext, usage: { contextWindow?: number }): number {
3223
- const configured = compactionTriggerPercent(settings);
3224
- if (!customModelCompactionConfigured(settings)) return configured;
3225
- const contextWindow = Number(usage.contextWindow);
3226
- if (!Number.isFinite(contextWindow) || contextWindow <= 0) return configured;
3227
- const model = ctx.modelRegistry.find(settings.context.compactionModelProvider, settings.context.compactionModel);
3228
- if (!model) return configured;
3229
- const safeInputTokens = customCompactionSafeInputTokens(model);
3230
- if (!safeInputTokens) return configured;
3231
- const safeTotalBeforeCompaction = safeInputTokens + DEFAULT_PI_COMPACTION_KEEP_RECENT_TOKENS;
3232
- const modelSafePercent = Math.floor((safeTotalBeforeCompaction / contextWindow) * 100);
3233
- if (!Number.isFinite(modelSafePercent) || modelSafePercent <= 0) return configured;
3234
- return Math.max(10, Math.min(configured, modelSafePercent));
3305
+ const contextWindow = contextWindowFrom(ctx, usage);
3306
+ const nativeThreshold = piNativeCompactionThreshold(ctx.cwd, contextWindow);
3307
+ return compactionTriggerPercentOverride(settings) ?? nativeThreshold.triggerPercent ?? NaN;
3235
3308
  }
3236
3309
 
3237
3310
  function compactionCooldownMinutes(settings: ReturnType<typeof loadWorkflowSettings>): number {
@@ -3239,6 +3312,16 @@ function compactionCooldownMinutes(settings: ReturnType<typeof loadWorkflowSetti
3239
3312
  return Number.isFinite(value) && value >= 0 && value <= 240 ? Math.round(value) : 5;
3240
3313
  }
3241
3314
 
3315
+ function compactionCooldownLabel(settings: ReturnType<typeof loadWorkflowSettings>): string {
3316
+ const value = compactionCooldownMinutes(settings);
3317
+ return value === 5 ? "Pi default" : `${value} min`;
3318
+ }
3319
+
3320
+ function workflowAutoTriggerLabel(settings: ReturnType<typeof loadWorkflowSettings>): string {
3321
+ if (settings.context.compactionMode === "disabled" || settings.context.autoCompactionEnabled === false) return "disabled";
3322
+ return settings.context.autoCompactionEnabled === true ? "enabled" : "Pi default";
3323
+ }
3324
+
3242
3325
  function customCompactionReserveTokens(settings: ReturnType<typeof loadWorkflowSettings>): number {
3243
3326
  const value = Number(settings.context.customCompactionReserveTokens ?? DEFAULT_PI_COMPACTION_RESERVE_TOKENS);
3244
3327
  return Number.isFinite(value) && value >= 4096 && value <= 65536 ? Math.round(value) : DEFAULT_PI_COMPACTION_RESERVE_TOKENS;
@@ -3249,6 +3332,16 @@ function customCompactionKeepRecentTokens(settings: ReturnType<typeof loadWorkfl
3249
3332
  return Number.isFinite(value) && value >= 1000 && value <= 200000 ? Math.round(value) : DEFAULT_PI_COMPACTION_KEEP_RECENT_TOKENS;
3250
3333
  }
3251
3334
 
3335
+ function compactionReserveTokensLabel(settings: ReturnType<typeof loadWorkflowSettings>): string {
3336
+ const value = customCompactionReserveTokens(settings);
3337
+ return value === DEFAULT_PI_COMPACTION_RESERVE_TOKENS ? "Pi default" : value.toLocaleString();
3338
+ }
3339
+
3340
+ function compactionKeepRecentTokensLabel(settings: ReturnType<typeof loadWorkflowSettings>): string {
3341
+ const value = customCompactionKeepRecentTokens(settings);
3342
+ return value === DEFAULT_PI_COMPACTION_KEEP_RECENT_TOKENS ? "Pi default" : value.toLocaleString();
3343
+ }
3344
+
3252
3345
  function customCompactionPreparationSettings(settings: ReturnType<typeof loadWorkflowSettings>, fallback: PiCompactionPreparation["settings"]): PiCompactionPreparation["settings"] {
3253
3346
  return {
3254
3347
  ...fallback,
@@ -3261,14 +3354,15 @@ function compactionTriggerStatus(settings: ReturnType<typeof loadWorkflowSetting
3261
3354
  if (settings.context.compactionMode === "disabled") return "Workflow compaction disabled";
3262
3355
  const cooldown = `${compactionCooldownMinutes(settings)} minute cooldown`;
3263
3356
  const safeBoundary = "safe after-turn idle boundary only";
3264
- if (customModelCompactionConfigured(settings) && settings.context.autoCompactionEnabled === true) return `Armed at ${compactionTriggerPercent(settings)}%; ${cooldown}; Compaction runs at ${safeBoundary}; Custom Model: ${settings.context.compactionModelProvider}/${settings.context.compactionModel}; Custom Prep: keep ${customCompactionKeepRecentTokens(settings).toLocaleString()} recent tokens, reserve ${customCompactionReserveTokens(settings).toLocaleString()} tokens`;
3265
- if (customModelCompactionConfigured(settings)) return `Custom compaction model configured for Pi session_before_compact only: ${settings.context.compactionModelProvider}/${settings.context.compactionModel}. Workflow proactive trigger is not enabled.`;
3357
+ const override = compactionTriggerPercentOverride(settings);
3358
+ if (workflowProactiveCompactionEffective(settings)) return `Armed at ${override == null ? "Pi default formula" : `${override}%`}; ${cooldown}; Compaction runs at ${safeBoundary}`;
3359
+ if (summaryModelConfigured(settings)) return `Custom summary model configured for Pi session_before_compact: ${settings.context.compactionModelProvider}/${settings.context.compactionModel}. Workflow proactive trigger is not enabled.`;
3266
3360
  if (settings.context.autoCompactionEnabled !== true) return "Workflow proactive compaction disabled; Pi default auto-compaction still applies";
3267
- return `Armed at ${compactionTriggerPercent(settings)}%; ${cooldown}; Compaction runs at ${safeBoundary}; Pi default fallback enabled`;
3361
+ return "Pi default auto-compaction applies";
3268
3362
  }
3269
3363
 
3270
3364
  function missionCheckpointModelLabel(settings: ReturnType<typeof loadWorkflowSettings>): string {
3271
- if (settings.context.customCompactionEnabled && settings.context.compactionMode === "custom_model" && settings.context.compactionModelProvider && settings.context.compactionModel) {
3365
+ if (customModelCompactionConfigured(settings)) {
3272
3366
  return `concise checkpoint summaries; Pi session compaction uses configured compaction model when active (currently ${settings.context.compactionModelProvider}/${settings.context.compactionModel})`;
3273
3367
  }
3274
3368
  return "concise checkpoint summaries; Pi session compaction uses configured compaction model when active";
@@ -3294,7 +3388,7 @@ Mission Checkpoint Model: ${missionCheckpointModelLabel(settings)}`;
3294
3388
  function renderPlanModelSettings(settings: ReturnType<typeof loadWorkflowSettings>): string {
3295
3389
  return `Plan Model Source: shared workflow role models
3296
3390
  ${renderWorkflowModels(settings)}
3297
- Compaction Model: ${settings.context.compactionMode === "custom_model" && settings.context.compactionModelProvider && settings.context.compactionModel ? `${settings.context.compactionModelProvider}/${settings.context.compactionModel}` : "Pi default / not configured"}`;
3391
+ Compaction Model: ${settings.context.compactionModelProvider && settings.context.compactionModel ? `${settings.context.compactionModelProvider}/${settings.context.compactionModel}` : "Pi default"}`;
3298
3392
  }
3299
3393
 
3300
3394
  function renderPlanSubagentWorkerSettings(settings: ReturnType<typeof loadWorkflowSettings>): string {
@@ -3393,8 +3487,10 @@ function renderWorkflowSettingsHealth(settings: ReturnType<typeof loadWorkflowSe
3393
3487
  `Validator: ${configuredLabel(settings.models.validator)}`,
3394
3488
  "",
3395
3489
  "## Compaction",
3396
- `Mode: ${compactionModeLabel(settings.context.compactionMode)}`,
3397
- `Workflow Auto Trigger: ${workflowProactiveCompactionEffective(settings) ? "armed" : "inactive"}`,
3490
+ `Provider: ${settings.context.compactionModelProvider || "Pi default"}`,
3491
+ `Model: ${settings.context.compactionModel || "Pi default"}`,
3492
+ `Workflow Auto Trigger: ${workflowAutoTriggerLabel(settings)}`,
3493
+ `Trigger: ${formatSharedCompactionTrigger(settings)}`,
3398
3494
  "Runtime: safe after-turn compaction",
3399
3495
  "",
3400
3496
  "## Capability Matrix Summary",
@@ -3652,18 +3748,16 @@ Note: Parallel File Edits controls simultaneous file writes only. It must not di
3652
3748
  ${renderSafetySettings(settings)}
3653
3749
 
3654
3750
  ## Compaction
3655
- Compaction Mode: ${compactionModeLabel(settings.context.compactionMode)}
3656
- Custom Compaction Enabled: ${settings.context.customCompactionEnabled ? "enabled" : "disabled"}
3657
- Compaction Model Provider: ${settings.context.compactionModelProvider || "(not set)"}
3658
- Compaction Model: ${settings.context.compactionModel || "(not set)"}
3659
- ${compactionAgentLine(settings)}${compactionAgentLine(settings) ? "\n" : ""}Workflow Auto Compaction Trigger Setting: ${settings.context.autoCompactionEnabled === true ? "enabled" : "disabled"}
3751
+ Compaction Provider: ${settings.context.compactionModelProvider || "Pi default"}
3752
+ Compaction Model: ${settings.context.compactionModel || "Pi default"}
3753
+ ${compactionAgentLine(settings)}${compactionAgentLine(settings) ? "\n" : ""}Workflow Auto Compaction Trigger Setting: ${workflowAutoTriggerLabel(settings)}
3660
3754
  Effective Workflow Compaction Trigger: ${workflowProactiveCompactionEffective(settings) ? "armed" : "inactive"}
3661
3755
  Compaction Runtime: safe after-turn idle boundary only
3662
3756
  Workflow Compaction Trigger Override: ${compactionTriggerOverrideLabel(settings)}
3663
- Effective Workflow Compaction Trigger Percent: ${compactionTriggerPercent(settings)}%
3757
+ Trigger: ${formatSharedCompactionTrigger(settings)}
3664
3758
  Workflow Compaction Cooldown: ${compactionCooldownMinutes(settings)} minute(s)
3665
- Custom Compaction Reserve Tokens: ${customCompactionReserveTokens(settings)}
3666
- Custom Compaction Keep Recent Tokens: ${customCompactionKeepRecentTokens(settings)}
3759
+ Compaction Reserve Tokens: ${compactionReserveTokensLabel(settings)}
3760
+ Compaction Keep Recent Tokens: ${compactionKeepRecentTokensLabel(settings)}
3667
3761
  Compaction Integration Status: ${compactionIntegrationStatus(settings)}
3668
3762
  Compaction Trigger Status: ${compactionTriggerStatus(settings)}
3669
3763
  ${workflowCompactionCheckRuntimeStatus()}
@@ -14651,33 +14745,62 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14651
14745
  return { provider: providerChoice, model: modelChoice.replace(`${providerChoice}/`, "") };
14652
14746
  }
14653
14747
 
14654
- async function selectCompactionModel(ctx: ExtensionContext) {
14655
- const selected = await selectProviderAndModel(ctx, "Compaction model");
14656
- if (!selected) return;
14748
+ async function chooseCompactionProvider(ctx: ExtensionContext): Promise<string | undefined> {
14749
+ const models = ctx.modelRegistry.getAll();
14750
+ const providers = [...new Set(models.map((m) => m.provider))].sort();
14751
+ const providerChoice = await ctx.ui.select("Compaction Provider", ["Pi default", "Custom provider", "Back"]);
14752
+ if (!providerChoice || providerChoice === "Back") return undefined;
14753
+ if (providerChoice === "Pi default") return "";
14754
+ const choice = await ctx.ui.select("Custom provider", [...providers, "Custom provider name", "Back"]);
14755
+ if (!choice || choice === "Back") return undefined;
14756
+ if (choice === "Custom provider name") return (await ctx.ui.input("Custom compaction provider:", "provider-name"))?.trim();
14757
+ return choice;
14758
+ }
14759
+
14760
+ async function selectCompactionProvider(ctx: ExtensionContext) {
14761
+ const provider = await chooseCompactionProvider(ctx);
14762
+ if (provider === undefined) return;
14657
14763
  const result = updateSettings(ctx.cwd, undefined, (s) => {
14658
- s.context.compactionMode = "custom_model";
14659
- s.context.compactionModelProvider = selected.provider;
14660
- s.context.compactionModel = selected.model;
14661
- s.context.customCompactionEnabled = true;
14764
+ s.context.compactionModelProvider = provider;
14765
+ s.context.compactionModel = "";
14662
14766
  });
14663
- ctx.ui.notify(`Compaction model stored as ${selected.provider}/${selected.model} in ${result.file}. It will be used by session_before_compact when available, with Pi default fallback on failure.`, "info");
14767
+ ctx.ui.notify(provider ? `Compaction provider preference stored as ${provider} in ${result.file}. Trigger behavior was not changed.` : `Compaction provider reset to Pi default in ${result.file}. Trigger behavior was not changed.`, "info");
14664
14768
  }
14665
14769
 
14666
- async function selectCompactionModelForCurrentProvider(ctx: ExtensionContext) {
14770
+ async function selectCompactionModel(ctx: ExtensionContext) {
14667
14771
  const settings = loadWorkflowSettings(ctx.cwd);
14668
14772
  const provider = settings.context.compactionModelProvider;
14669
- if (!provider) return selectCompactionModel(ctx);
14670
- const providerModels = ctx.modelRegistry.getAll().filter((m) => m.provider === provider).map((m) => m.id).sort();
14671
- if (providerModels.length === 0) return selectCompactionModel(ctx);
14672
- const modelChoice = await ctx.ui.select(`Compaction model for ${provider}:`, providerModels.map((model) => `${provider}/${model}`));
14673
- if (!modelChoice) return;
14674
- const model = modelChoice.replace(`${provider}/`, "");
14773
+ const mode = await ctx.ui.select("Compaction Model", ["Pi default", "Custom model", "Back"]);
14774
+ if (!mode || mode === "Back") return;
14775
+ if (mode === "Pi default") {
14776
+ const result = updateSettings(ctx.cwd, undefined, (s) => {
14777
+ s.context.compactionModelProvider = "";
14778
+ s.context.compactionModel = "";
14779
+ });
14780
+ ctx.ui.notify(`Compaction summary model reset to Pi default in ${result.file}. Trigger behavior was not changed.`, "info");
14781
+ return;
14782
+ }
14783
+
14784
+ let selectedProvider = provider;
14785
+ if (!selectedProvider) {
14786
+ const chosen = await chooseCompactionProvider(ctx);
14787
+ if (chosen === undefined) return;
14788
+ selectedProvider = chosen;
14789
+ }
14790
+ if (!selectedProvider) {
14791
+ ctx.ui.notify("Select a custom compaction provider before selecting a custom model.", "warning");
14792
+ return;
14793
+ }
14794
+ const providerModels = ctx.modelRegistry.getAll().filter((m) => m.provider === selectedProvider).map((m) => m.id).sort();
14795
+ const modelChoice = await ctx.ui.select(`Custom model for ${selectedProvider}:`, [...providerModels.map((model) => `${selectedProvider}/${model}`), "Custom model name", "Back"]);
14796
+ if (!modelChoice || modelChoice === "Back") return;
14797
+ const model = modelChoice === "Custom model name" ? (await ctx.ui.input("Custom compaction model:", "model-name"))?.trim() : modelChoice.replace(`${selectedProvider}/`, "");
14798
+ if (!model) return;
14675
14799
  const result = updateSettings(ctx.cwd, undefined, (s) => {
14676
- s.context.compactionMode = "custom_model";
14800
+ s.context.compactionModelProvider = selectedProvider;
14677
14801
  s.context.compactionModel = model;
14678
- s.context.customCompactionEnabled = true;
14679
14802
  });
14680
- ctx.ui.notify(`Compaction model stored as ${provider}/${model} in ${result.file}. It will be used by session_before_compact when available, with Pi default fallback on failure.`, "info");
14803
+ ctx.ui.notify(`Compaction model preference stored as ${selectedProvider}/${model} in ${result.file}. Trigger behavior was not changed.`, "info");
14681
14804
  }
14682
14805
 
14683
14806
  async function selectCompactionAgent(ctx: ExtensionContext) {
@@ -14697,60 +14820,70 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14697
14820
  async function showCompactionSettingsMenu(ctx: ExtensionContext) {
14698
14821
  if (!ctx.hasUI) return show(pi, renderFullWorkflowSettings(loadWorkflowSettings(ctx.cwd)));
14699
14822
  while (ctx.hasUI) {
14700
- const choice = await ctx.ui.select("Shared Compaction Settings", ["Compaction Mode", "Compaction Provider", "Compaction Model", "Custom Compaction Enabled", "Workflow Auto Trigger Enabled", "Workflow Trigger Percent", "Workflow Trigger Cooldown", "Custom Reserve Tokens", "Custom Keep Recent Tokens", "List Current Settings", "Back"]);
14823
+ const choice = await ctx.ui.select("Shared Compaction Settings", ["Compaction Provider", "Compaction Model", "Workflow Auto Trigger Enabled", "Workflow Trigger Percent", "Workflow Trigger Cooldown", "Custom Reserve Tokens", "Custom Keep Recent Tokens", "List Current Settings", "Back"]);
14701
14824
  if (!choice || choice === "Back") return;
14702
- if (choice === "Compaction Mode") {
14703
- const mode = parseCompactionMode((await ctx.ui.select("Compaction mode", ["Pi default", "Custom model", "Disabled"])) ?? "");
14704
- if (!mode) continue;
14705
- if (mode === "custom_model") { await selectCompactionModel(ctx); continue; }
14706
- const r = updateSettings(ctx.cwd, undefined, (s) => {
14707
- if (mode === "pi_default") resetCompactionContextToPiDefault(s.context);
14708
- else {
14709
- s.context.compactionMode = mode;
14710
- s.context.customCompactionEnabled = false;
14711
- }
14712
- });
14713
- ctx.ui.notify(`Compaction mode set to ${compactionModeLabel(mode)} in ${r.file}${mode === "pi_default" ? "; custom compaction overrides reset, selected provider/model preserved" : ""}.`, "info");
14714
- } else if (choice === "Compaction Provider") {
14715
- await selectCompactionModel(ctx);
14825
+ if (choice === "Compaction Provider") {
14826
+ await selectCompactionProvider(ctx);
14716
14827
  } else if (choice === "Compaction Model") {
14717
- await selectCompactionModelForCurrentProvider(ctx);
14718
- } else if (choice === "Custom Compaction Enabled") {
14719
- const enabled = await chooseBool(ctx, "Custom compaction enabled?");
14720
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.customCompactionEnabled = enabled; }); ctx.ui.notify(`Custom compaction set to ${enabled ? "enabled" : "disabled"} in ${r.file}. Custom model routing uses the session compaction hook when configured; Pi default fallback remains enabled.`, "info"); }
14828
+ await selectCompactionModel(ctx);
14721
14829
  } else if (choice === "Workflow Auto Trigger Enabled") {
14722
- const enabled = await chooseBool(ctx, "Workflow proactive auto-compaction trigger enabled?");
14723
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.autoCompactionEnabled = enabled; }); ctx.ui.notify(`Workflow auto trigger set to ${enabled ? "enabled" : "disabled"} in ${r.file}. Pi default auto-compaction remains available as fallback.`, "info"); }
14830
+ const trigger = await ctx.ui.select("Workflow Auto Trigger Enabled", ["Pi default", "Enabled", "Disabled", "Back"]);
14831
+ if (!trigger || trigger === "Back") continue;
14832
+ const r = updateSettings(ctx.cwd, undefined, (s) => {
14833
+ if (trigger === "Pi default") delete s.context.autoCompactionEnabled;
14834
+ else s.context.autoCompactionEnabled = trigger === "Enabled";
14835
+ });
14836
+ ctx.ui.notify(`Workflow auto trigger set to ${trigger.toLowerCase()} in ${r.file}.`, "info");
14724
14837
  } else if (choice === "Workflow Trigger Percent") {
14725
- const current = loadWorkflowSettings(ctx.cwd).context.compactionTriggerPercent;
14726
- const raw = String((await ctx.ui.input("Workflow compaction trigger percent (50-95, default, or reset)", current == null ? "Pi default" : String(compactionTriggerPercent(loadWorkflowSettings(ctx.cwd)))) ?? "")).trim().toLowerCase();
14727
- if (raw === "default" || raw === "reset") {
14838
+ const mode = await ctx.ui.select("Workflow Trigger Percent", ["Pi default", "Custom percent", "Back"]);
14839
+ if (!mode || mode === "Back") continue;
14840
+ if (mode === "Pi default") {
14728
14841
  const r = updateSettings(ctx.cwd, undefined, (s) => { delete s.context.compactionTriggerPercent; });
14729
- ctx.ui.notify(`Workflow compaction trigger percent override removed in ${r.file}; Pi default compaction behavior remains in control.`, "info");
14842
+ ctx.ui.notify(`Workflow compaction trigger percent override removed in ${r.file}; Pi native trigger formula remains in control.`, "info");
14730
14843
  } else {
14844
+ const raw = String((await ctx.ui.input("Workflow compaction trigger percent (50-95)", String(compactionTriggerPercentOverride(loadWorkflowSettings(ctx.cwd)) ?? 85))) ?? "").trim();
14731
14845
  const count = Number(raw);
14732
14846
  if (Number.isInteger(count) && count >= 50 && count <= 95) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.compactionTriggerPercent = count; }); ctx.ui.notify(`Workflow compaction trigger percent set to ${count}% in ${r.file}`, "info"); }
14733
- else ctx.ui.notify("Trigger percent must be an integer from 50 to 95, default, or reset.", "error");
14847
+ else ctx.ui.notify("Trigger percent must be an integer from 50 to 95.", "error");
14734
14848
  }
14735
14849
  } else if (choice === "Workflow Trigger Cooldown") {
14736
- const count = Number((await ctx.ui.input("Minimum minutes between Workflow Suite proactive compaction attempts", String(compactionCooldownMinutes(loadWorkflowSettings(ctx.cwd)))) ?? ""));
14737
- if (Number.isInteger(count) && count >= 0 && count <= 240) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.compactionCooldownMinutes = count; }); ctx.ui.notify(`Compaction cooldown set to ${count} minute(s) in ${r.file}. This is the minimum wait between proactive compaction attempts, not a delay before compaction starts.`, "info"); }
14738
- else ctx.ui.notify("Cooldown must be an integer from 0 to 240 minutes.", "error");
14850
+ const mode = await ctx.ui.select("Workflow Trigger Cooldown", ["Pi default", "Custom cooldown", "Back"]);
14851
+ if (!mode || mode === "Back") continue;
14852
+ if (mode === "Pi default") {
14853
+ const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.compactionCooldownMinutes = 5; });
14854
+ ctx.ui.notify(`Compaction cooldown reset to Pi default (5 minutes) in ${r.file}.`, "info");
14855
+ } else {
14856
+ const count = Number((await ctx.ui.input("Minimum minutes between Workflow Suite proactive compaction attempts", String(compactionCooldownMinutes(loadWorkflowSettings(ctx.cwd)))) ?? ""));
14857
+ if (Number.isInteger(count) && count >= 0 && count <= 240) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.compactionCooldownMinutes = count; }); ctx.ui.notify(`Compaction cooldown set to ${count} minute(s) in ${r.file}. This is the minimum wait between proactive compaction attempts, not a delay before compaction starts.`, "info"); }
14858
+ else ctx.ui.notify("Cooldown must be an integer from 0 to 240 minutes.", "error");
14859
+ }
14739
14860
  } else if (choice === "Custom Reserve Tokens") {
14740
- const raw = String((await ctx.ui.input("Custom compaction reserve tokens (4096-65536, default, or reset)", String(customCompactionReserveTokens(loadWorkflowSettings(ctx.cwd)))) ?? "")).trim().toLowerCase();
14741
- const count = Number(raw);
14742
- if (raw === "default" || raw === "reset") { const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.customCompactionReserveTokens = DEFAULT_PI_COMPACTION_RESERVE_TOKENS; }); ctx.ui.notify(`Custom compaction reserve tokens reset to ${DEFAULT_PI_COMPACTION_RESERVE_TOKENS.toLocaleString()} in ${r.file}`, "info"); }
14743
- else if (Number.isInteger(count) && count >= 4096 && count <= 65536) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.customCompactionReserveTokens = count; }); ctx.ui.notify(`Custom compaction reserve tokens set to ${count.toLocaleString()} in ${r.file}`, "info"); }
14744
- else ctx.ui.notify("Reserve tokens must be an integer from 4096 to 65536, default, or reset.", "error");
14861
+ const mode = await ctx.ui.select("Custom Reserve Tokens", ["Pi default", "Custom reserve tokens", "Back"]);
14862
+ if (!mode || mode === "Back") continue;
14863
+ if (mode === "Pi default") {
14864
+ const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.customCompactionReserveTokens = DEFAULT_PI_COMPACTION_RESERVE_TOKENS; });
14865
+ ctx.ui.notify(`Custom compaction reserve tokens reset to Pi default (${DEFAULT_PI_COMPACTION_RESERVE_TOKENS.toLocaleString()}) in ${r.file}`, "info");
14866
+ } else {
14867
+ const raw = String((await ctx.ui.input("Custom compaction reserve tokens (4096-65536)", String(customCompactionReserveTokens(loadWorkflowSettings(ctx.cwd)))) ?? "")).trim();
14868
+ const count = Number(raw);
14869
+ if (Number.isInteger(count) && count >= 4096 && count <= 65536) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.customCompactionReserveTokens = count; }); ctx.ui.notify(`Custom compaction reserve tokens set to ${count.toLocaleString()} in ${r.file}`, "info"); }
14870
+ else ctx.ui.notify("Reserve tokens must be an integer from 4096 to 65536.", "error");
14871
+ }
14745
14872
  } else if (choice === "Custom Keep Recent Tokens") {
14746
- const raw = String((await ctx.ui.input("Custom compaction keep-recent tokens (1000-200000, default, or reset)", String(customCompactionKeepRecentTokens(loadWorkflowSettings(ctx.cwd)))) ?? "")).trim().toLowerCase();
14747
- const count = Number(raw);
14748
- if (raw === "default" || raw === "reset") { const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.customCompactionKeepRecentTokens = DEFAULT_PI_COMPACTION_KEEP_RECENT_TOKENS; }); ctx.ui.notify(`Custom compaction keep-recent tokens reset to ${DEFAULT_PI_COMPACTION_KEEP_RECENT_TOKENS.toLocaleString()} in ${r.file}`, "info"); }
14749
- else if (Number.isInteger(count) && count >= 1000 && count <= 200000) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.customCompactionKeepRecentTokens = count; }); ctx.ui.notify(`Custom compaction keep-recent tokens set to ${count.toLocaleString()} in ${r.file}`, "info"); }
14750
- else ctx.ui.notify("Keep-recent tokens must be an integer from 1000 to 200000, default, or reset.", "error");
14873
+ const mode = await ctx.ui.select("Custom Keep Recent Tokens", ["Pi default", "Custom keep-recent tokens", "Back"]);
14874
+ if (!mode || mode === "Back") continue;
14875
+ if (mode === "Pi default") {
14876
+ const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.customCompactionKeepRecentTokens = DEFAULT_PI_COMPACTION_KEEP_RECENT_TOKENS; });
14877
+ ctx.ui.notify(`Custom compaction keep-recent tokens reset to Pi default (${DEFAULT_PI_COMPACTION_KEEP_RECENT_TOKENS.toLocaleString()}) in ${r.file}`, "info");
14878
+ } else {
14879
+ const raw = String((await ctx.ui.input("Custom compaction keep-recent tokens (1000-200000)", String(customCompactionKeepRecentTokens(loadWorkflowSettings(ctx.cwd)))) ?? "")).trim();
14880
+ const count = Number(raw);
14881
+ if (Number.isInteger(count) && count >= 1000 && count <= 200000) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.customCompactionKeepRecentTokens = count; }); ctx.ui.notify(`Custom compaction keep-recent tokens set to ${count.toLocaleString()} in ${r.file}`, "info"); }
14882
+ else ctx.ui.notify("Keep-recent tokens must be an integer from 1000 to 200000.", "error");
14883
+ }
14751
14884
  } else if (choice === "List Current Settings") {
14752
14885
  const s = loadWorkflowSettings(ctx.cwd);
14753
- show(pi, `# Shared Compaction\n\nCompaction Mode: ${s.context.compactionMode ?? "pi_default"}\nCustom Compaction: ${s.context.customCompactionEnabled ? "enabled" : "disabled"}\nProvider: ${s.context.compactionModelProvider ?? "none"}\nModel: ${s.context.compactionModel ?? "none"}\nWorkflow Auto Trigger: ${s.context.autoCompactionEnabled === true ? "enabled" : "disabled; Pi default applies"}\nWorkflow Trigger Percent Override: ${compactionTriggerOverrideLabel(s)}\nEffective Workflow Trigger Percent: ${compactionTriggerPercent(s)}%\nCooldown: ${s.context.compactionCooldownMinutes ?? 0} min\nReserve Tokens: ${s.context.customCompactionReserveTokens?.toLocaleString() ?? "default"}\nKeep Recent Tokens: ${s.context.customCompactionKeepRecentTokens?.toLocaleString() ?? "default"}`);
14886
+ show(pi, `# Shared Compaction\n\nProvider: ${s.context.compactionModelProvider || "Pi default"}\nModel: ${s.context.compactionModel || "Pi default"}\nWorkflow Auto Trigger: ${workflowAutoTriggerLabel(s)}\nTrigger: ${formatSharedCompactionTrigger(s, ctx)}\nCooldown: ${compactionCooldownLabel(s)}\nReserve Tokens: ${compactionReserveTokensLabel(s)}\nKeep Recent Tokens: ${compactionKeepRecentTokensLabel(s)}`);
14754
14887
  }
14755
14888
  }
14756
14889
  }
@@ -15868,10 +16001,9 @@ Pi Version: v${VERSION}
15868
16001
 
15869
16002
  function workflowAutoCompactionDecision(ctx: ExtensionContext, options: { allowPendingMessages?: boolean } = {}): { ok: true; percent: number; trigger: number; cooldownMinutes: number } | { ok: false; reason: string } {
15870
16003
  const settings = loadWorkflowSettings(ctx.cwd);
15871
- const customModelTrigger = customModelCompactionConfigured(settings);
15872
16004
  if (!workflowProactiveCompactionEffective(settings)) return { ok: false, reason: "workflow proactive compaction disabled" };
15873
16005
  if (settings.context.compactionMode === "disabled") return { ok: false, reason: "workflow compaction mode disabled" };
15874
- if (!customModelTrigger && !workflowAutoCompactionModeEligible(state.mode)) return { ok: false, reason: `workflow mode ${state.mode} is active` };
16006
+ if (!workflowAutoCompactionModeEligible(state.mode)) return { ok: false, reason: `workflow mode ${state.mode} is active` };
15875
16007
  if (!options.allowPendingMessages) {
15876
16008
  if (workflowScheduledAgentTurns > 0) return { ok: false, reason: "pending workflow handoff queued" };
15877
16009
  try { if (ctx.hasPendingMessages()) return { ok: false, reason: "pending messages queued" }; } catch { /* pending state unavailable */ }
@@ -15880,6 +16012,7 @@ Pi Version: v${VERSION}
15880
16012
  const percent = Number(usage?.percent);
15881
16013
  if (!Number.isFinite(percent)) return { ok: false, reason: "context usage unavailable" };
15882
16014
  const trigger = effectiveWorkflowCompactionTriggerPercent(settings, ctx, usage ?? {});
16015
+ if (!Number.isFinite(trigger)) return { ok: false, reason: "compaction trigger unavailable" };
15883
16016
  if (percent < trigger) return { ok: false, reason: `context ${percent.toFixed(1)}% below trigger ${trigger}%` };
15884
16017
  const cooldownMinutes = compactionCooldownMinutes(settings);
15885
16018
  const cooldownMs = cooldownMinutes * 60 * 1000;
@@ -15992,19 +16125,11 @@ Pi Version: v${VERSION}
15992
16125
 
15993
16126
  pi.on("session_before_compact", async (event, ctx) => {
15994
16127
  const settings = loadWorkflowSettings(ctx.cwd);
15995
- if (!settings.context.customCompactionEnabled) return;
15996
- if (settings.context.compactionMode === "pi_default" || settings.context.compactionMode === "disabled") return;
15997
- if (settings.context.compactionMode !== "custom_model") {
15998
- rememberCustomCompactionStatus(`fallback to Pi default: ${compactionModeLabel(settings.context.compactionMode)} uses Pi fallback behavior in current releases`);
15999
- ctx.ui.notify("Agent-routed custom compaction uses Pi default fallback behavior in current releases.", "warning");
16000
- return;
16001
- }
16128
+ if (settings.context.compactionMode === "disabled") return;
16002
16129
 
16003
16130
  const provider = settings.context.compactionModelProvider;
16004
16131
  const modelId = settings.context.compactionModel;
16005
16132
  if (!provider || !modelId) {
16006
- rememberCustomCompactionStatus("fallback to Pi default: custom compaction model is missing provider/model");
16007
- ctx.ui.notify("Custom compaction model is missing provider/model; using Pi default compaction.", "warning");
16008
16133
  return;
16009
16134
  }
16010
16135
 
@@ -16715,7 +16840,7 @@ Pi Version: v${VERSION}
16715
16840
  if (action === "scope" || action === "write-target") {
16716
16841
  const effective = loadEffectiveSettings(ctx.cwd);
16717
16842
  const target = getDefaultWriteTarget(ctx.cwd);
16718
- show(pi, `# Workflow Settings Scope\n\nCurrent Directory: ${ctx.cwd}\nProject Override: ${effective.projectOverridePath ?? "none"}\nWrite Target: ${target.scope}\nWrite Target File: ${target.file}\nGlobal Settings File: ${WORKFLOW_SETTINGS_FILE}\nSettings Priority: project workflow settings > global workflow-settings.json > example config defaults > built-in emergency fallback\nCompaction Effective Source: ${effective.projectOverridePath ? "project override may override global compaction settings" : "global workflow-settings.json"}\nEffective Compaction Model: ${effective.settings.context.compactionMode === "custom_model" ? `${effective.settings.context.compactionModelProvider || "(missing provider)"}/${effective.settings.context.compactionModel || "(missing model)"}` : effective.settings.context.compactionMode}`);
16843
+ show(pi, `# Workflow Settings Scope\n\nCurrent Directory: ${ctx.cwd}\nProject Override: ${effective.projectOverridePath ?? "none"}\nWrite Target: ${target.scope}\nWrite Target File: ${target.file}\nGlobal Settings File: ${WORKFLOW_SETTINGS_FILE}\nSettings Priority: project workflow settings > global workflow-settings.json > example config defaults > built-in emergency fallback\nCompaction Effective Source: ${effective.projectOverridePath ? "project override may override global compaction settings" : "global workflow-settings.json"}\nCompaction Summary Model: ${effective.settings.context.compactionModelProvider && effective.settings.context.compactionModel ? `${effective.settings.context.compactionModelProvider}/${effective.settings.context.compactionModel}` : "Pi default"}`);
16719
16844
  return;
16720
16845
  }
16721
16846
  if (action === "create-project-override") {
@@ -17022,7 +17147,12 @@ Pi Version: v${VERSION}
17022
17147
  return show(pi, updatedMessage(result.scope, result.file, "context.customCompactionEnabled", String(bool)));
17023
17148
  }
17024
17149
  if (subject === "context" && key === "autoCompactionEnabled") {
17025
- if (bool === undefined) return show(pi, "# Error\n\nUsage: `/workflow-settings set context autoCompactionEnabled <true|false>`");
17150
+ const normalizedValue = String(value ?? "").trim().toLowerCase();
17151
+ if (normalizedValue === "default" || normalizedValue === "reset") {
17152
+ const result = updateSettings(ctx.cwd, scope, (s) => { delete s.context.autoCompactionEnabled; });
17153
+ return show(pi, updatedMessage(result.scope, result.file, "context.autoCompactionEnabled", "Pi default") + `\n\n${compactionTriggerStatus(result.settings)}`);
17154
+ }
17155
+ if (bool === undefined) return show(pi, "# Error\n\nUsage: `/workflow-settings set context autoCompactionEnabled <true|false|default>`");
17026
17156
  const result = updateSettings(ctx.cwd, scope, (s) => { s.context.autoCompactionEnabled = bool; });
17027
17157
  return show(pi, updatedMessage(result.scope, result.file, "context.autoCompactionEnabled", String(bool)) + `\n\n${compactionTriggerStatus(result.settings)}`);
17028
17158
  }
@@ -17036,18 +17166,18 @@ Pi Version: v${VERSION}
17036
17166
  const normalizedValue = String(value ?? "").trim().toLowerCase();
17037
17167
  if (key === "compactionTriggerPercent" && (normalizedValue === "default" || normalizedValue === "reset")) {
17038
17168
  const result = updateSettings(ctx.cwd, scope, (s) => { delete s.context.compactionTriggerPercent; });
17039
- return show(pi, updatedMessage(result.scope, result.file, "context.compactionTriggerPercent", "removed override; Pi default applies") + `\n\n${compactionTriggerStatus(result.settings)}`);
17169
+ return show(pi, updatedMessage(result.scope, result.file, "context.compactionTriggerPercent", "removed override; Pi native formula applies") + `\n\n${compactionTriggerStatus(result.settings)}`);
17040
17170
  }
17041
- if ((key === "customCompactionReserveTokens" || key === "customCompactionKeepRecentTokens") && (normalizedValue === "default" || normalizedValue === "reset")) {
17042
- const fallback = key === "customCompactionReserveTokens" ? DEFAULT_PI_COMPACTION_RESERVE_TOKENS : DEFAULT_PI_COMPACTION_KEEP_RECENT_TOKENS;
17171
+ if ((key === "compactionCooldownMinutes" || key === "customCompactionReserveTokens" || key === "customCompactionKeepRecentTokens") && (normalizedValue === "default" || normalizedValue === "reset")) {
17172
+ const fallback = key === "compactionCooldownMinutes" ? 5 : key === "customCompactionReserveTokens" ? DEFAULT_PI_COMPACTION_RESERVE_TOKENS : DEFAULT_PI_COMPACTION_KEEP_RECENT_TOKENS;
17043
17173
  const result = updateSettings(ctx.cwd, scope, (s) => { (s.context as typeof s.context & Record<string, number>)[key] = fallback; });
17044
- return show(pi, updatedMessage(result.scope, result.file, `context.${key}`, `default ${fallback}`) + `\n\n${compactionTriggerStatus(result.settings)}`);
17174
+ return show(pi, updatedMessage(result.scope, result.file, `context.${key}`, `Pi default ${fallback}`) + `\n\n${compactionTriggerStatus(result.settings)}`);
17045
17175
  }
17046
17176
  const rawCount = value === undefined ? NaN : Number(value);
17047
17177
  const count = Number.isInteger(rawCount) ? rawCount : undefined;
17048
17178
  const min = key === "compactionTriggerPercent" ? 50 : key === "compactionCooldownMinutes" ? 0 : key === "customCompactionReserveTokens" ? 4096 : 1000;
17049
17179
  const max = key === "compactionTriggerPercent" ? 95 : key === "compactionCooldownMinutes" ? 240 : key === "customCompactionReserveTokens" ? 65536 : 200000;
17050
- const usage = key === "compactionTriggerPercent" || key === "customCompactionReserveTokens" || key === "customCompactionKeepRecentTokens" ? `<${min}-${max}|default|reset>` : `<${min}-${max}>`;
17180
+ const usage = `<${min}-${max}|default|reset>`;
17051
17181
  if (count === undefined || count < min || count > max) return show(pi, `# Error\n\nUsage: \`/workflow-settings set context ${key} ${usage}\``);
17052
17182
  const result = updateSettings(ctx.cwd, scope, (s) => { (s.context as typeof s.context & Record<string, number>)[key] = count; });
17053
17183
  return show(pi, updatedMessage(result.scope, result.file, `context.${key}`, String(count)) + `\n\n${compactionTriggerStatus(result.settings)}`);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mediadatafusion/pi-workflow-suite",
3
- "version": "0.0.16",
4
- "description": "Structured workflow orchestration suite for Pi with Standard, Plan, Mission, compaction, diagrams, web access, repo lock, and safety gates.",
3
+ "version": "0.0.18",
4
+ "description": "Multi-agent workflow suite for Pi with Idle, Standard, Plan, Mission, approval gates, reviewer/validator roles, sub-agents, model routing, web search/fetch, browser checks, diagrams, compaction, presets, settings, themes, widgets, and Repo Lock.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
@@ -24,9 +24,20 @@
24
24
  "workflow-suite",
25
25
  "workflow-orchestration",
26
26
  "agent-workflow",
27
+ "idle-mode",
27
28
  "plan-mode",
28
29
  "mission-mode",
29
30
  "standard-mode",
31
+ "multi-agent",
32
+ "approval-gates",
33
+ "review-validation",
34
+ "sub-agent-orchestration",
35
+ "model-routing",
36
+ "web-search",
37
+ "browser-verification",
38
+ "repo-lock",
39
+ "workflow-settings",
40
+ "workflow-widgets",
30
41
  "subagents",
31
42
  "skills",
32
43
  "prompts",