@mediadatafusion/pi-workflow-suite 0.0.17 → 0.0.19

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.
@@ -8,7 +8,7 @@ import { StringEnum } from "@earendil-works/pi-ai";
8
8
  import type { AssistantMessage, TextContent } from "@earendil-works/pi-ai";
9
9
  import { CustomEditor, VERSION, compact as piCompact, estimateTokens as piEstimateTokens, findCutPoint as piFindCutPoint, getAgentDir, getMarkdownTheme, type ExtensionAPI, type ExtensionContext, type FileOperations, type SessionEntry, type ToolDefinition } from "@earendil-works/pi-coding-agent";
10
10
  import { Type } from "typebox";
11
- import { activeWorkflowPresetLabel, applyMissionModelForRole, applyModelForRole, applyStandardModelForRole, applyWorkflowPreset, compactionModeLabel, createProjectSettingsOverride, createWorkflowPreset, defaultWorkflowSettings, deleteWorkflowPreset, effectivePlanApprovalRequired, effectiveReviewAutoRun, effectiveValidateAfterExecution, effectiveValidationAutoRun, effectiveRepairGate, formatRole, getDefaultWriteTarget, loadEffectiveSettings, loadGlobalSettings, loadWorkflowSettings, normalizeWorkflowPresetName, parseMissionModelRole, parseRole, parseThinkingLevel, renameWorkflowPreset, renderActiveWorkflowPresetSummary, renderStandardModelStrategy, renderWorkflowModels, renderWorkflowPresets, resolveWorkflowPresetName, roleIsConfigured, saveCurrentWorkflowPreset, setMissionModelForRole, setMissionThinkingForRole, setModelForRole, setRoleEnabled, setStandardModelForRole, setStandardThinkingForRole, setThinkingForRole, standardModelSource, standardModelSourceLabel, standardTodoTriggerModeLabel, updateSettings, workflowCompactionCheckModeLabel, workflowPresetCatalog, workflowPresetLabel, workflowPresetNames, workflowPresetPickerLabel, workflowRoleLabel, workflowSettingsConsistencyDiagnostics, WORKFLOW_CUSTOM_PRESET_MARKER, WORKFLOW_SETTINGS_FILE, type MissionModelRole, type RoleModelSettings, type WorkflowRole, type WorkflowSettingsScope, type WorkflowStartupLogo, type WorkflowStartupLogoColorStyle, type WorkflowStartupLogoFont, type WorkflowStartupLogoShadowDirection, type WorkflowStartupVisual, type CustomBrandBaseVisual, type StandardClarificationMode, type StandardModelRole, type StandardTodoTriggerMode, type WorkflowAgentScope } from "./workflow-model-router.js";
11
+ import { activeWorkflowPresetLabel, applyMissionModelForRole, applyModelForRole, applyStandardModelForRole, applyWorkflowPreset, compactionModeLabel, createProjectSettingsOverride, createWorkflowPreset, defaultWorkflowSettings, deleteWorkflowPreset, effectivePlanApprovalRequired, effectiveReviewAutoRun, effectiveValidateAfterExecution, effectiveValidationAutoRun, effectiveRepairGate, formatRole, getDefaultWriteTarget, loadEffectiveSettings, loadGlobalSettings, loadWorkflowSettings, normalizeWorkflowPresetName, parseMissionModelRole, parseRole, parseThinkingLevel, renameWorkflowPreset, renderActiveWorkflowPresetSummary, renderStandardModelStrategy, renderWorkflowModels, renderWorkflowPresets, resolveWorkflowPresetName, roleIsConfigured, saveCurrentWorkflowPreset, setMissionModelForRole, setMissionThinkingForRole, setModelForRole, setRoleEnabled, setStandardModelForRole, setStandardThinkingForRole, setThinkingForRole, standardModelSource, standardModelSourceLabel, standardTodoTriggerModeLabel, updateSettings, workflowCompactionCheckModeLabel, workflowPresetCatalog, workflowPresetLabel, workflowPresetNames, workflowPresetPickerLabel, workflowRoleLabel, workflowSettingsConsistencyDiagnostics, WORKFLOW_CUSTOM_PRESET_MARKER, WORKFLOW_SETTINGS_FILE, type MissionModelRole, type RoleModelSettings, type WorkflowRole, type WorkflowSettingsScope, type WorkflowStartupLogo, type WorkflowStartupLogoColorStyle, type WorkflowStartupLogoFont, type WorkflowStartupLogoShadowDirection, type WorkflowStartupVisual, type WorkflowEditorHintContrast, type CustomBrandBaseVisual, type StandardClarificationMode, type StandardModelRole, type StandardTodoTriggerMode, type WorkflowAgentScope } from "./workflow-model-router.js";
12
12
  import { renderHandoffProjectContext, renderWorkflowStatus, renderWorkflowSummary } from "./workflow-summary.js";
13
13
  import { BASE_EXECUTE_TOOLS, EXECUTE_TOOLS, PLAN_TOOLS, REVIEW_TOOLS, WORKFLOW_DIAGRAM_TOOL, WORKFLOW_PLAN_RESULT_TOOL, WORKFLOW_REVIEW_RESULT_TOOL, WORKFLOW_EXECUTION_RESULT_TOOL, WORKFLOW_VALIDATION_RESULT_TOOL, WORKFLOW_REPAIR_RESULT_TOOL, WORKFLOW_PROGRESS_TOOL, MISSION_PLAN_RESULT_TOOL, MISSION_MILESTONE_RESULT_TOOL, STANDARD_HANDOFF_RESULT_TOOL, isBlockedExecuteCommand, registerToolGuard, standardSafeReadOnlyBash, VALIDATOR_TOOLS } from "./workflow-tool-guard.js";
14
14
  import { refreshRuntimeWebTools, registerWorkflowWebTools, runtimeWebResearchGuidance, webSafePlanTools, withRuntimeWebTools } from "./workflow-web-tools.js";
@@ -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
  }
@@ -2592,6 +2680,7 @@ UI Widgets:
2592
2680
  - /workflow-settings set ui showActiveWorkflowSwitchHint true|false
2593
2681
  - /workflow-settings set ui showWidgetShortcutHint true|false
2594
2682
  - /workflow-settings set ui showPresetShortcutHint true|false
2683
+ - /workflow-settings set ui editorHintContrast subtle|normal|bright|high
2595
2684
 
2596
2685
  Sub-agents and parallelism:
2597
2686
  - /workflow-settings set subagents enabled true|false
@@ -2675,15 +2764,15 @@ Mission Mode settings:
2675
2764
 
2676
2765
  Compaction:
2677
2766
  - /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
2767
+ - Compaction Provider and Compaction Model select the summary model preference
2768
+ - /workflow-settings set context autoCompactionEnabled true|false|default
2682
2769
  - /workflow-settings set context compactionTriggerPercent 50-95|default|reset
2683
- - /workflow-settings set context compactionCooldownMinutes 0-240
2770
+ - /workflow-settings set context compactionCooldownMinutes 0-240|default|reset
2684
2771
  - /workflow-settings set context customCompactionReserveTokens 4096-65536|default|reset
2685
2772
  - /workflow-settings set context customCompactionKeepRecentTokens 1000-200000|default|reset
2686
2773
  - /workflow-settings set context workflowCompactionCheckMode boundary|in_session (legacy/backward-compatible; primary UI uses safe after-turn compaction)
2774
+ - /workflow-settings set context compactionMode pi_default|custom_model|disabled (legacy/backward-compatible; primary UI uses trigger/provider/model controls)
2775
+ - /workflow-settings set context customCompactionEnabled true|false (legacy/backward-compatible; no longer required for summary model routing)
2687
2776
  - /workflow-settings set context compactionModelProvider <provider> (advanced/manual fallback)
2688
2777
  - /workflow-settings set context compactionModel <model> (advanced/manual fallback)
2689
2778
  - /workflow-settings set context compactionAgent <agent> (legacy/backward-compatible; custom agent is planned only)
@@ -3172,40 +3261,34 @@ function workflowCompactionCheckRuntimeStatus(): string {
3172
3261
  return `Last Check: ${workflowLastCompactionCheckAt}\nLast Decision: ${workflowLastCompactionDecision}`;
3173
3262
  }
3174
3263
 
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)
3264
+ function summaryModelConfigured(settings: ReturnType<typeof loadWorkflowSettings>): boolean {
3265
+ return Boolean(settings.context.compactionModelProvider)
3179
3266
  && Boolean(settings.context.compactionModel);
3180
3267
  }
3181
3268
 
3269
+ function customModelCompactionConfigured(settings: ReturnType<typeof loadWorkflowSettings>): boolean {
3270
+ return settings.context.compactionMode !== "disabled" && summaryModelConfigured(settings);
3271
+ }
3272
+
3182
3273
  function workflowProactiveCompactionEffective(settings: ReturnType<typeof loadWorkflowSettings>): boolean {
3183
3274
  if (settings.context.compactionMode === "disabled") return false;
3184
3275
  return settings.context.autoCompactionEnabled === true;
3185
3276
  }
3186
3277
 
3187
3278
  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
- }
3279
+ if (settings.context.compactionMode === "disabled") return "Workflow compaction disabled";
3280
+ 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
3281
  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";
3282
+ return "Pi default summary model is active";
3195
3283
  }
3196
3284
 
3197
- function defaultCompactionTriggerPercent(): number {
3198
- return 50;
3199
- }
3200
-
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;
3285
+ function compactionTriggerPercentOverride(settings: ReturnType<typeof loadWorkflowSettings>): number | undefined {
3286
+ const value = Number(settings.context.compactionTriggerPercent);
3287
+ return Number.isFinite(value) && value >= 50 && value <= 95 ? Math.round(value) : undefined;
3205
3288
  }
3206
3289
 
3207
3290
  function compactionTriggerOverrideLabel(settings: ReturnType<typeof loadWorkflowSettings>): string {
3208
- const value = settings.context.compactionTriggerPercent;
3291
+ const value = compactionTriggerPercentOverride(settings);
3209
3292
  return typeof value === "number" ? `${value}%` : "none";
3210
3293
  }
3211
3294
 
@@ -3220,18 +3303,9 @@ function resetCompactionContextToPiDefault(context: ReturnType<typeof loadWorkfl
3220
3303
  }
3221
3304
 
3222
3305
  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));
3306
+ const contextWindow = contextWindowFrom(ctx, usage);
3307
+ const nativeThreshold = piNativeCompactionThreshold(ctx.cwd, contextWindow);
3308
+ return compactionTriggerPercentOverride(settings) ?? nativeThreshold.triggerPercent ?? NaN;
3235
3309
  }
3236
3310
 
3237
3311
  function compactionCooldownMinutes(settings: ReturnType<typeof loadWorkflowSettings>): number {
@@ -3239,6 +3313,16 @@ function compactionCooldownMinutes(settings: ReturnType<typeof loadWorkflowSetti
3239
3313
  return Number.isFinite(value) && value >= 0 && value <= 240 ? Math.round(value) : 5;
3240
3314
  }
3241
3315
 
3316
+ function compactionCooldownLabel(settings: ReturnType<typeof loadWorkflowSettings>): string {
3317
+ const value = compactionCooldownMinutes(settings);
3318
+ return value === 5 ? "Pi default" : `${value} min`;
3319
+ }
3320
+
3321
+ function workflowAutoTriggerLabel(settings: ReturnType<typeof loadWorkflowSettings>): string {
3322
+ if (settings.context.compactionMode === "disabled" || settings.context.autoCompactionEnabled === false) return "disabled";
3323
+ return settings.context.autoCompactionEnabled === true ? "enabled" : "Pi default";
3324
+ }
3325
+
3242
3326
  function customCompactionReserveTokens(settings: ReturnType<typeof loadWorkflowSettings>): number {
3243
3327
  const value = Number(settings.context.customCompactionReserveTokens ?? DEFAULT_PI_COMPACTION_RESERVE_TOKENS);
3244
3328
  return Number.isFinite(value) && value >= 4096 && value <= 65536 ? Math.round(value) : DEFAULT_PI_COMPACTION_RESERVE_TOKENS;
@@ -3249,6 +3333,16 @@ function customCompactionKeepRecentTokens(settings: ReturnType<typeof loadWorkfl
3249
3333
  return Number.isFinite(value) && value >= 1000 && value <= 200000 ? Math.round(value) : DEFAULT_PI_COMPACTION_KEEP_RECENT_TOKENS;
3250
3334
  }
3251
3335
 
3336
+ function compactionReserveTokensLabel(settings: ReturnType<typeof loadWorkflowSettings>): string {
3337
+ const value = customCompactionReserveTokens(settings);
3338
+ return value === DEFAULT_PI_COMPACTION_RESERVE_TOKENS ? "Pi default" : value.toLocaleString();
3339
+ }
3340
+
3341
+ function compactionKeepRecentTokensLabel(settings: ReturnType<typeof loadWorkflowSettings>): string {
3342
+ const value = customCompactionKeepRecentTokens(settings);
3343
+ return value === DEFAULT_PI_COMPACTION_KEEP_RECENT_TOKENS ? "Pi default" : value.toLocaleString();
3344
+ }
3345
+
3252
3346
  function customCompactionPreparationSettings(settings: ReturnType<typeof loadWorkflowSettings>, fallback: PiCompactionPreparation["settings"]): PiCompactionPreparation["settings"] {
3253
3347
  return {
3254
3348
  ...fallback,
@@ -3261,14 +3355,15 @@ function compactionTriggerStatus(settings: ReturnType<typeof loadWorkflowSetting
3261
3355
  if (settings.context.compactionMode === "disabled") return "Workflow compaction disabled";
3262
3356
  const cooldown = `${compactionCooldownMinutes(settings)} minute cooldown`;
3263
3357
  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.`;
3358
+ const override = compactionTriggerPercentOverride(settings);
3359
+ if (workflowProactiveCompactionEffective(settings)) return `Armed at ${override == null ? "Pi default formula" : `${override}%`}; ${cooldown}; Compaction runs at ${safeBoundary}`;
3360
+ 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
3361
  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`;
3362
+ return "Pi default auto-compaction applies";
3268
3363
  }
3269
3364
 
3270
3365
  function missionCheckpointModelLabel(settings: ReturnType<typeof loadWorkflowSettings>): string {
3271
- if (settings.context.customCompactionEnabled && settings.context.compactionMode === "custom_model" && settings.context.compactionModelProvider && settings.context.compactionModel) {
3366
+ if (customModelCompactionConfigured(settings)) {
3272
3367
  return `concise checkpoint summaries; Pi session compaction uses configured compaction model when active (currently ${settings.context.compactionModelProvider}/${settings.context.compactionModel})`;
3273
3368
  }
3274
3369
  return "concise checkpoint summaries; Pi session compaction uses configured compaction model when active";
@@ -3294,7 +3389,7 @@ Mission Checkpoint Model: ${missionCheckpointModelLabel(settings)}`;
3294
3389
  function renderPlanModelSettings(settings: ReturnType<typeof loadWorkflowSettings>): string {
3295
3390
  return `Plan Model Source: shared workflow role models
3296
3391
  ${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"}`;
3392
+ Compaction Model: ${settings.context.compactionModelProvider && settings.context.compactionModel ? `${settings.context.compactionModelProvider}/${settings.context.compactionModel}` : "Pi default"}`;
3298
3393
  }
3299
3394
 
3300
3395
  function renderPlanSubagentWorkerSettings(settings: ReturnType<typeof loadWorkflowSettings>): string {
@@ -3393,8 +3488,10 @@ function renderWorkflowSettingsHealth(settings: ReturnType<typeof loadWorkflowSe
3393
3488
  `Validator: ${configuredLabel(settings.models.validator)}`,
3394
3489
  "",
3395
3490
  "## Compaction",
3396
- `Mode: ${compactionModeLabel(settings.context.compactionMode)}`,
3397
- `Workflow Auto Trigger: ${workflowProactiveCompactionEffective(settings) ? "armed" : "inactive"}`,
3491
+ `Provider: ${settings.context.compactionModelProvider || "Pi default"}`,
3492
+ `Model: ${settings.context.compactionModel || "Pi default"}`,
3493
+ `Workflow Auto Trigger: ${workflowAutoTriggerLabel(settings)}`,
3494
+ `Trigger: ${formatSharedCompactionTrigger(settings)}`,
3398
3495
  "Runtime: safe after-turn compaction",
3399
3496
  "",
3400
3497
  "## Capability Matrix Summary",
@@ -3588,7 +3685,7 @@ ${renderPlanModelSettings(settings)}
3588
3685
 
3589
3686
  ${renderMissionModelStrategy(settings)}
3590
3687
 
3591
- ## UI / Footer
3688
+ ## UI / Widgets
3592
3689
  Plan Top Widget: ${widgetVisibilityLabel(settings, "planTop")}
3593
3690
  Plan Bottom Widget: ${widgetVisibilityLabel(settings, "planBottom")}
3594
3691
  Mission Top Widget: ${widgetVisibilityLabel(settings, "missionTop")}
@@ -3599,6 +3696,7 @@ Idle Entry Hint: ${workflowWidgetUi(settings).showIdleWorkflowEntryHint !== fals
3599
3696
  Active Switch Hint: ${workflowWidgetUi(settings).showActiveWorkflowSwitchHint !== false ? "visible" : "hidden"}
3600
3697
  Widget Shortcut Hint: ${workflowWidgetUi(settings).showWidgetShortcutHint !== false ? "visible" : "hidden"}
3601
3698
  Preset Shortcut Hint: ${workflowWidgetUi(settings).showPresetShortcutHint !== false ? "visible" : "hidden"}
3699
+ Editor Hint Contrast: ${workflowEditorHintContrastLabel(workflowEditorHintContrast(settings))}
3602
3700
  Workflow Theme: ${workflowThemeDisplayName(workflowThemeName(settings))}
3603
3701
  Startup Visual: ${startupVisualOrDefault(settings)}
3604
3702
  Startup Logo: ${startupLogoOrDefault(settings)}
@@ -3652,18 +3750,16 @@ Note: Parallel File Edits controls simultaneous file writes only. It must not di
3652
3750
  ${renderSafetySettings(settings)}
3653
3751
 
3654
3752
  ## 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"}
3753
+ Compaction Provider: ${settings.context.compactionModelProvider || "Pi default"}
3754
+ Compaction Model: ${settings.context.compactionModel || "Pi default"}
3755
+ ${compactionAgentLine(settings)}${compactionAgentLine(settings) ? "\n" : ""}Workflow Auto Compaction Trigger Setting: ${workflowAutoTriggerLabel(settings)}
3660
3756
  Effective Workflow Compaction Trigger: ${workflowProactiveCompactionEffective(settings) ? "armed" : "inactive"}
3661
3757
  Compaction Runtime: safe after-turn idle boundary only
3662
3758
  Workflow Compaction Trigger Override: ${compactionTriggerOverrideLabel(settings)}
3663
- Effective Workflow Compaction Trigger Percent: ${compactionTriggerPercent(settings)}%
3759
+ Trigger: ${formatSharedCompactionTrigger(settings)}
3664
3760
  Workflow Compaction Cooldown: ${compactionCooldownMinutes(settings)} minute(s)
3665
- Custom Compaction Reserve Tokens: ${customCompactionReserveTokens(settings)}
3666
- Custom Compaction Keep Recent Tokens: ${customCompactionKeepRecentTokens(settings)}
3761
+ Compaction Reserve Tokens: ${compactionReserveTokensLabel(settings)}
3762
+ Compaction Keep Recent Tokens: ${compactionKeepRecentTokensLabel(settings)}
3667
3763
  Compaction Integration Status: ${compactionIntegrationStatus(settings)}
3668
3764
  Compaction Trigger Status: ${compactionTriggerStatus(settings)}
3669
3765
  ${workflowCompactionCheckRuntimeStatus()}
@@ -5433,8 +5529,8 @@ class WorkflowSuiteEditor extends CustomEditor {
5433
5529
  try {
5434
5530
  const settings = loadWorkflowSettings(this.workflowCwd);
5435
5531
  const decorate = workflowRoleTextDecoration(settings, "muted");
5436
- let hintStyled = `\x1b[2m\x1b[38;5;240m${workflowEditorHintText}\x1b[0m`;
5437
- if (decorate) hintStyled = decorate(hintStyled);
5532
+ let hintStyled = workflowEditorHintStyledText(settings, workflowEditorHintText);
5533
+ if (workflowEditorHintContrast(settings) === "subtle" && decorate) hintStyled = decorate(hintStyled);
5438
5534
  const innerWidth = width - 2;
5439
5535
  const hintLen = visibleTextWidth(hintStyled);
5440
5536
  const displayHint = hintLen > innerWidth ? truncateVisibleText(hintStyled, innerWidth) : hintStyled;
@@ -6431,6 +6527,13 @@ function isMissionWorkflowMode(state: WorkflowState): boolean {
6431
6527
  }
6432
6528
 
6433
6529
  type WorkflowWidgetSlot = "planTop" | "planBottom" | "missionTop" | "missionBottom" | "standardTop" | "standardBottom";
6530
+ const WORKFLOW_EDITOR_HINT_CONTRASTS: WorkflowEditorHintContrast[] = ["subtle", "normal", "bright", "high"];
6531
+ const WORKFLOW_EDITOR_HINT_CONTRAST_LABELS: Record<WorkflowEditorHintContrast, string> = {
6532
+ subtle: "Subtle",
6533
+ normal: "Normal",
6534
+ bright: "Bright",
6535
+ high: "High Contrast",
6536
+ };
6434
6537
  type WorkflowWidgetUiSettings = ReturnType<typeof loadWorkflowSettings>["ui"] & {
6435
6538
  showPlanModeIndicator?: boolean;
6436
6539
  planModeIndicatorText?: string;
@@ -6444,6 +6547,7 @@ type WorkflowWidgetUiSettings = ReturnType<typeof loadWorkflowSettings>["ui"] &
6444
6547
  showActiveWorkflowSwitchHint?: boolean;
6445
6548
  showWidgetShortcutHint?: boolean;
6446
6549
  showPresetShortcutHint?: boolean;
6550
+ editorHintContrast?: WorkflowEditorHintContrast;
6447
6551
  };
6448
6552
 
6449
6553
  const widgetVisibilityOverrides: Partial<Record<WorkflowWidgetSlot, boolean>> = {};
@@ -6460,6 +6564,38 @@ function workflowWidgetUi(settings: ReturnType<typeof loadWorkflowSettings>): Wo
6460
6564
  return settings.ui as WorkflowWidgetUiSettings;
6461
6565
  }
6462
6566
 
6567
+ function parseEditorHintContrast(value?: string): WorkflowEditorHintContrast | undefined {
6568
+ const input = value?.trim().toLowerCase().replace(/[\s_-]+/g, "");
6569
+ if (!input) return undefined;
6570
+ if (input === "subtle" || input === "muted" || input === "dim") return "subtle";
6571
+ if (input === "normal" || input === "default") return "normal";
6572
+ if (input === "bright") return "bright";
6573
+ if (input === "high" || input === "highcontrast" || input === "maximum") return "high";
6574
+ return undefined;
6575
+ }
6576
+
6577
+ function workflowEditorHintContrast(settings: ReturnType<typeof loadWorkflowSettings>): WorkflowEditorHintContrast {
6578
+ return parseEditorHintContrast(workflowWidgetUi(settings).editorHintContrast) ?? "normal";
6579
+ }
6580
+
6581
+ function workflowEditorHintContrastLabel(contrast: WorkflowEditorHintContrast): string {
6582
+ return WORKFLOW_EDITOR_HINT_CONTRAST_LABELS[contrast];
6583
+ }
6584
+
6585
+ function workflowEditorHintStyledText(settings: ReturnType<typeof loadWorkflowSettings>, text: string): string {
6586
+ switch (workflowEditorHintContrast(settings)) {
6587
+ case "subtle":
6588
+ return `\x1b[2m\x1b[38;5;240m${text}\x1b[0m`;
6589
+ case "bright":
6590
+ return `\x1b[38;5;250m${text}\x1b[0m`;
6591
+ case "high":
6592
+ return `\x1b[1m\x1b[38;5;255m${text}\x1b[0m`;
6593
+ case "normal":
6594
+ default:
6595
+ return `\x1b[38;5;245m${text}\x1b[0m`;
6596
+ }
6597
+ }
6598
+
6463
6599
  function workflowWidgetVisible(settings: ReturnType<typeof loadWorkflowSettings>, slot: WorkflowWidgetSlot): boolean {
6464
6600
  const override = widgetVisibilityOverrides[slot];
6465
6601
  if (override !== undefined) return override;
@@ -9624,7 +9760,7 @@ export default function workflowModes(pi: ExtensionAPI): void {
9624
9760
  const route = await applyModelForRole(pi, ctx, "planner", { cwd: ctx.cwd });
9625
9761
  if (!route) return false;
9626
9762
  updateState({ modelsUsed: { ...(state.modelsUsed ?? {}), planner: modelLabel(route) } }, ctx);
9627
- if (showNotice) ctx.ui.notify(`Active Plan Mode planner route reapplied: ${modelLabel(route)}\n${activeModelDiagnostic(ctx)}`, "info");
9763
+ if (showNotice) workflowUiNotify(ctx, `Active Plan Mode planner route reapplied: ${modelLabel(route)}\n${activeModelDiagnostic(ctx)}`, "info");
9628
9764
  return true;
9629
9765
  };
9630
9766
 
@@ -9633,13 +9769,13 @@ export default function workflowModes(pi: ExtensionAPI): void {
9633
9769
  const settings = loadWorkflowSettings(ctx.cwd);
9634
9770
  const standardRole = effectiveStandardModelRole(settings);
9635
9771
  if (standardRole === "current") {
9636
- if (showNotice) ctx.ui.notify(`Standard Mode keeps the current Pi model.\n${activeModelDiagnostic(ctx)}`, "info");
9772
+ if (showNotice) workflowUiNotify(ctx, `Standard Mode keeps the current Pi model.\n${activeModelDiagnostic(ctx)}`, "info");
9637
9773
  return false;
9638
9774
  }
9639
9775
  const route = await applyStandardModelForRole(pi, ctx, standardRole, { cwd: ctx.cwd });
9640
9776
  if (!route) return false;
9641
9777
  updateState({ modelsUsed: { ...(state.modelsUsed ?? {}), [standardRole]: modelLabel(route) } }, ctx);
9642
- if (showNotice) ctx.ui.notify(`Active Standard Mode ${standardRole} route applied: ${modelLabel(route)}\n${activeModelDiagnostic(ctx)}`, "info");
9778
+ if (showNotice) workflowUiNotify(ctx, `Active Standard Mode ${standardRole} route applied: ${modelLabel(route)}\n${activeModelDiagnostic(ctx)}`, "info");
9643
9779
  return true;
9644
9780
  };
9645
9781
 
@@ -9654,7 +9790,7 @@ export default function workflowModes(pi: ExtensionAPI): void {
9654
9790
  const settings = loadWorkflowSettings(ctx.cwd);
9655
9791
  const globalUi = workflowWidgetUi(loadGlobalSettings());
9656
9792
  const ui = workflowWidgetUi(settings);
9657
- const common = `Active Mode: ${state.mode}\nShortcuts: ${globalUi.enableWidgetShortcuts !== false ? "enabled" : "disabled"}\nRemember Visibility: ${globalUi.rememberWidgetVisibility !== false ? "enabled" : "disabled"}\nStorage: ${globalUi.rememberWidgetVisibility !== false ? "global settings" : "current Pi session"}\nFooter Hints: idle entry ${ui.showIdleWorkflowEntryHint !== false ? "visible" : "hidden"}, active switch ${ui.showActiveWorkflowSwitchHint !== false ? "visible" : "hidden"}, widgets ${ui.showWidgetShortcutHint !== false ? "visible" : "hidden"}, preset ${ui.showPresetShortcutHint !== false ? "visible" : "hidden"}`;
9793
+ const common = `Active Mode: ${state.mode}\nShortcuts: ${globalUi.enableWidgetShortcuts !== false ? "enabled" : "disabled"}\nRemember Visibility: ${globalUi.rememberWidgetVisibility !== false ? "enabled" : "disabled"}\nStorage: ${globalUi.rememberWidgetVisibility !== false ? "global settings" : "current Pi session"}\nEditor Hints: idle entry ${ui.showIdleWorkflowEntryHint !== false ? "visible" : "hidden"}, active switch ${ui.showActiveWorkflowSwitchHint !== false ? "visible" : "hidden"}, widgets ${ui.showWidgetShortcutHint !== false ? "visible" : "hidden"}, preset ${ui.showPresetShortcutHint !== false ? "visible" : "hidden"}, contrast ${workflowEditorHintContrastLabel(workflowEditorHintContrast(settings))}`;
9658
9794
  if (isPlanWorkflowUiMode(state)) {
9659
9795
  const bottom = planBottomRelevant(state) ? widgetVisibilityLabel(settings, "planBottom") : "not applicable";
9660
9796
  return `# Workflow Widgets\n\nPlan Top Widget: ${widgetVisibilityLabel(settings, "planTop")}\nPlan Bottom Widget: ${bottom}\nStatus Line: ${widgetVisibilityStatus(state, settings) ?? "none"}\n${common}`;
@@ -9667,7 +9803,13 @@ export default function workflowModes(pi: ExtensionAPI): void {
9667
9803
  if (isStandardWorkflowMode(state)) {
9668
9804
  return `# Workflow Widgets\n\nStandard Top Widget: ${widgetVisibilityLabel(settings, "standardTop")}\nStandard To Do Widget: ${widgetVisibilityLabel(settings, "standardBottom")}\nStatus Line: ${widgetVisibilityStatus(state, settings) ?? "none"}\n${common}`;
9669
9805
  }
9670
- return `# Workflow Widgets\n\nNo active Plan/Mission/Standard widget is currently visible.\nStatus Line: ${widgetVisibilityStatus(state, settings) ?? "none"}\n\nEntry Shortcuts:\n- ${workflowEntryShortcutLabel("standard")}\n- ${workflowEntryShortcutLabel("plan")}\n- ${workflowEntryShortcutLabel("mission")}\n\nActive-mode footer uses compact hints such as: Widgets:Ctrl+Shift+T/B Preset:${activeWorkflowPresetLabel(settings)} Ctrl+Shift+U Standard:Ctrl+Shift+S Mission:Ctrl+Shift+M\nWidget toggles and preset cycling are visible only while Plan/Mission/Standard Mode is active.\n\n${common}`;
9806
+ return `# Workflow Widgets\n\nNo active Plan/Mission/Standard widget is currently visible.\nStatus Line: ${widgetVisibilityStatus(state, settings) ?? "none"}\n\nEntry Shortcuts:\n- ${workflowEntryShortcutLabel("standard")}\n- ${workflowEntryShortcutLabel("plan")}\n- ${workflowEntryShortcutLabel("mission")}\n\nActive-mode editor hints use compact text such as: Widgets:Ctrl+Shift+T/B Preset:${activeWorkflowPresetLabel(settings)} Ctrl+Shift+U Standard:Ctrl+Shift+S Mission:Ctrl+Shift+M\nWidget toggles and preset cycling are visible only while Plan/Mission/Standard Mode is active.\n\n${common}`;
9807
+ };
9808
+
9809
+ const renderEditorHintsSettings = (ctx: ExtensionContext): string => {
9810
+ const settings = loadWorkflowSettings(ctx.cwd);
9811
+ const ui = workflowWidgetUi(settings);
9812
+ return `# Editor Hints\n\nThese hints render inline in the editor/input box.\n\nIdle Entry Hint: ${ui.showIdleWorkflowEntryHint !== false ? "visible" : "hidden"}\nActive Switch Hint: ${ui.showActiveWorkflowSwitchHint !== false ? "visible" : "hidden"}\nWidget Shortcut Hint: ${ui.showWidgetShortcutHint !== false ? "visible" : "hidden"}\nPreset Shortcut Hint: ${ui.showPresetShortcutHint !== false ? "visible" : "hidden"}\nHint Contrast: ${workflowEditorHintContrastLabel(workflowEditorHintContrast(settings))}`;
9671
9813
  };
9672
9814
 
9673
9815
  const setWorkflowWidgetVisibility = (ctx: ExtensionContext, slot: WorkflowWidgetSlot, visible: boolean): string => {
@@ -9720,7 +9862,7 @@ export default function workflowModes(pi: ExtensionAPI): void {
9720
9862
  const messages = (["planTop", "planBottom", "missionTop", "missionBottom", "standardTop", "standardBottom"] as WorkflowWidgetSlot[]).map((slot) => setWorkflowWidgetVisibility(ctx, slot, visible));
9721
9863
  return show(pi, `# Workflow Widgets\n\n${messages.join("\n")}\n\n${renderWorkflowWidgetsStatus(ctx)}`);
9722
9864
  }
9723
- return show(pi, "# Workflow Widgets\n\nUsage:\n- /workflow widgets status\n- /workflow widgets list\n- /workflow widgets configure\n- /workflow widgets toggle top\n- /workflow widgets toggle bottom\n- /workflow widgets on\n- /workflow widgets off\n\nFooter behavior:\n- Idle: Plan/Mission entry shortcuts only\n- Active modes: compact widget/preset hints plus Plan/Mission switch hints by default\n- Active switch hints can be configured in /workflow-settings configure widgets Footer Hints");
9865
+ return show(pi, "# Workflow Widgets\n\nUsage:\n- /workflow widgets status\n- /workflow widgets list\n- /workflow widgets configure\n- /workflow widgets toggle top\n- /workflow widgets toggle bottom\n- /workflow widgets on\n- /workflow widgets off\n\nEditor hint behavior:\n- Idle: Plan/Mission entry shortcuts only\n- Active modes: compact widget/preset hints plus Plan/Mission switch hints by default\n- Active switch hints can be configured in /workflow-settings configure widgets -> Editor Hints");
9724
9866
  };
9725
9867
 
9726
9868
  const persistCurrentPlan = (ctx: ExtensionContext, approvalStatus: "draft" | "approved" | "revised" | "completed" | "archived", saveReason: string, handoff: { executionSummary?: string; reviewerReport?: string; validationReport?: string; repairAttempt?: string; finalReport?: string } = {}): string | undefined => {
@@ -14301,7 +14443,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14301
14443
  const plannerReapplied = role === "planner" ? await reapplyPlannerRouteIfPlanMode(ctx, true) : false;
14302
14444
  const standardSettings = loadWorkflowSettings(ctx.cwd);
14303
14445
  const standardReapplied = effectiveStandardModelRole(standardSettings) === role ? await reapplyStandardRouteIfStandardMode(ctx, true) : false;
14304
- ctx.ui.notify(`${role} model set to ${provider}/${model} in ${result.file}${plannerReapplied || standardReapplied ? `\n${activeModelDiagnostic(ctx)}` : ""}`, "info");
14446
+ workflowUiNotify(ctx, `${role} model set to ${provider}/${model} in ${result.file}${plannerReapplied || standardReapplied ? `\n${activeModelDiagnostic(ctx)}` : ""}`, "info");
14305
14447
  }
14306
14448
 
14307
14449
  async function configureStandardRoleModel(ctx: ExtensionContext, role: WorkflowRole) {
@@ -14309,14 +14451,14 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14309
14451
  if (!selected) return;
14310
14452
  const result = setStandardModelForRole(role, selected.provider, selected.model, ctx.cwd);
14311
14453
  await reapplyStandardRouteIfStandardMode(ctx, true);
14312
- ctx.ui.notify(`standard ${role} model set to ${selected.provider}/${selected.model} in ${result.file}`, "info");
14454
+ workflowUiNotify(ctx, `standard ${role} model set to ${selected.provider}/${selected.model} in ${result.file}`, "info");
14313
14455
  }
14314
14456
 
14315
14457
  async function configureMissionRoleModel(ctx: ExtensionContext, role: MissionModelRole) {
14316
14458
  const selected = await selectProviderAndModel(ctx, `Mission ${role} model`);
14317
14459
  if (!selected) return;
14318
14460
  const result = setMissionModelForRole(role, selected.provider, selected.model, ctx.cwd);
14319
- ctx.ui.notify(`mission ${role} model set to ${selected.provider}/${selected.model} in ${result.file}`, "info");
14461
+ workflowUiNotify(ctx, `mission ${role} model set to ${selected.provider}/${selected.model} in ${result.file}`, "info");
14320
14462
  }
14321
14463
 
14322
14464
  async function configureStandardThinking(ctx: ExtensionContext) {
@@ -14326,7 +14468,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14326
14468
  if (!level) return;
14327
14469
  const result = setStandardThinkingForRole(role, level, ctx.cwd);
14328
14470
  await reapplyStandardRouteIfStandardMode(ctx, true);
14329
- ctx.ui.notify(`standard ${role} thinking set to ${level} in ${result.file}`, "info");
14471
+ workflowUiNotify(ctx, `standard ${role} thinking set to ${level} in ${result.file}`, "info");
14330
14472
  }
14331
14473
 
14332
14474
  async function configureMissionThinking(ctx: ExtensionContext) {
@@ -14335,7 +14477,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14335
14477
  const level = parseThinkingLevel((await ctx.ui.select("Select mission thinking level:", ["off", "minimal", "low", "medium", "high", "xhigh"])) ?? "");
14336
14478
  if (!level) return;
14337
14479
  const result = setMissionThinkingForRole(role, level, ctx.cwd);
14338
- ctx.ui.notify(`mission ${role} thinking set to ${level} in ${result.file}`, "info");
14480
+ workflowUiNotify(ctx, `mission ${role} thinking set to ${level} in ${result.file}`, "info");
14339
14481
  }
14340
14482
 
14341
14483
  async function configureThinking(ctx: ExtensionContext) {
@@ -14347,7 +14489,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14347
14489
  const plannerReapplied = role === "planner" ? await reapplyPlannerRouteIfPlanMode(ctx, true) : false;
14348
14490
  const standardSettings = loadWorkflowSettings(ctx.cwd);
14349
14491
  const standardReapplied = effectiveStandardModelRole(standardSettings) === role ? await reapplyStandardRouteIfStandardMode(ctx, true) : false;
14350
- ctx.ui.notify(`${role} thinking set to ${level} in ${result.file}${plannerReapplied || standardReapplied ? `\n${activeModelDiagnostic(ctx)}` : ""}`, "info");
14492
+ workflowUiNotify(ctx, `${role} thinking set to ${level} in ${result.file}${plannerReapplied || standardReapplied ? `\n${activeModelDiagnostic(ctx)}` : ""}`, "info");
14351
14493
  }
14352
14494
 
14353
14495
  async function chooseBool(ctx: ExtensionContext, title: string): Promise<boolean | undefined> {
@@ -14355,6 +14497,128 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14355
14497
  return parseBool(choice ?? undefined);
14356
14498
  }
14357
14499
 
14500
+ function humanizeWorkflowSettingSegment(segment: string): string {
14501
+ return segment
14502
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
14503
+ .replace(/[_-]+/g, " ")
14504
+ .replace(/\bui\b/gi, "UI")
14505
+ .replace(/\bapi\b/gi, "API")
14506
+ .replace(/\bto do\b/gi, "To Do")
14507
+ .trim()
14508
+ .replace(/\s+/g, " ")
14509
+ .replace(/\w\S*/g, (word) => word === "UI" || word === "API" ? word : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase());
14510
+ }
14511
+
14512
+ function workflowSettingDisplayLabel(setting: string): string {
14513
+ const labels: Record<string, string> = {
14514
+ "ui.editorHintContrast": "Editor hint contrast",
14515
+ "ui.showIdleWorkflowEntryHint": "Idle entry hint",
14516
+ "ui.showActiveWorkflowSwitchHint": "Active switch hint",
14517
+ "ui.showWidgetShortcutHint": "Widget shortcut hint",
14518
+ "ui.showPresetShortcutHint": "Preset shortcut hint",
14519
+ "ui.enableWidgetShortcuts": "Widget shortcuts",
14520
+ "ui.rememberWidgetVisibility": "Remember widget visibility",
14521
+ "ui.workflowTheme": "Workflow theme",
14522
+ "ui.widgetTextStyle": "Widget text style",
14523
+ "ui.startupTextStyle": "Startup text style",
14524
+ "ui.startupVisual": "Startup visual",
14525
+ "ui.startupLogo": "Startup logo",
14526
+ "ui.startupLogoText": "Startup logo text",
14527
+ "ui.startupLogoFont": "Startup logo font",
14528
+ "ui.startupLogoShadowDirection": "Startup logo shadow direction",
14529
+ "ui.startupLogoColorStyle": "Startup logo color style",
14530
+ "ui.startupVisualOnSessionStart": "Startup on session start",
14531
+ "ui.customBrandEnabled": "Custom brand",
14532
+ "ui.customBrandText": "Custom brand text",
14533
+ "ui.customBrandBaseVisual": "Custom brand base visual",
14534
+ "interactiveClarificationEnabled": "Interactive clarification",
14535
+ "maxClarificationQuestions": "Max clarification questions",
14536
+ "planning.depth": "Planning depth",
14537
+ "planning.clarificationMode": "Planning clarification mode",
14538
+ "planning.interactiveClarificationEnabled": "Interactive clarification",
14539
+ "planning.maxClarificationQuestions": "Max clarification questions",
14540
+ "planning.clarificationTiming": "Planning clarification timing",
14541
+ "planning.maxTokens": "Planning token budget",
14542
+ "planning.maxRuntimeHours": "Planning runtime budget",
14543
+ "standard.allowSubagents": "Standard sub-agents",
14544
+ "standard.subagentScope": "Standard sub-agent scope",
14545
+ "standard.todoTriggerMode": "To Do trigger mode",
14546
+ "standard.clarificationMode": "Standard clarification mode",
14547
+ "standard.interactiveClarificationEnabled": "Standard interactive clarification",
14548
+ "standard.maxClarificationQuestions": "Standard max clarification questions",
14549
+ "standard.clarificationTiming": "Standard clarification timing",
14550
+ "standard.maxTokens": "Standard token budget",
14551
+ "standard.enabled": "Standard Mode",
14552
+ "subagents.enabled": "Sub-agents",
14553
+ "subagents.activityIndicatorEnabled": "Sub-agent activity indicator",
14554
+ "subagents.allowBackgroundSubagents": "Background sub-agents",
14555
+ "subagents.editConcurrencyMode": "Sub-agent edit concurrency mode",
14556
+ "subagents.planningOrchestrationPolicy": "Planning orchestration policy",
14557
+ "missions.enabled": "Mission Mode",
14558
+ "missions.defaultAutonomy": "Mission default autonomy",
14559
+ "missions.subagentPolicy": "Mission sub-agent policy",
14560
+ "missions.planningDepth": "Mission planning depth",
14561
+ "missions.clarificationMode": "Mission clarification mode",
14562
+ "missions.interactiveClarificationEnabled": "Mission interactive clarification",
14563
+ "missions.maxClarificationQuestions": "Mission max clarification questions",
14564
+ "missions.clarificationTiming": "Mission clarification timing",
14565
+ "missions.maxTokens": "Mission token budget",
14566
+ "missions.maxRuntimeHours": "Mission runtime budget",
14567
+ "missions.checkpointIntervalMinutes": "Mission checkpoint interval",
14568
+ "missions.heartbeatEnabled": "Mission heartbeat",
14569
+ "missions.watchdogEnabled": "Mission watchdog",
14570
+ "missions.watchdogStaleMinutes": "Mission watchdog stale minutes",
14571
+ "missions.missionHistoryLimit": "Mission history limit",
14572
+ "context.autoCompactionEnabled": "Workflow auto trigger",
14573
+ "context.compactionTriggerPercent": "Workflow trigger percent",
14574
+ "context.compactionCooldownMinutes": "Compaction cooldown",
14575
+ "context.customCompactionReserveTokens": "Custom compaction reserve tokens",
14576
+ "context.customCompactionKeepRecentTokens": "Custom compaction keep-recent tokens",
14577
+ "workflow.validationRetryMode": "Validation retry mode",
14578
+ "workflow.planHistoryLimit": "Plan history limit",
14579
+ "workflow.planShowProgressBar": "Plan progress bar",
14580
+ "safety.repoLockEnabled": "Project Repo Lock",
14581
+ };
14582
+ if (labels[setting]) return labels[setting];
14583
+ const parts = setting.split(".");
14584
+ return humanizeWorkflowSettingSegment(parts[parts.length - 1] || setting);
14585
+ }
14586
+
14587
+ function workflowSettingDisplayValue(setting: string, value: string): string {
14588
+ const clean = value.trim().replace(/\s+/g, " ");
14589
+ if (setting === "ui.editorHintContrast") {
14590
+ return workflowEditorHintContrastLabel(parseEditorHintContrast(clean) ?? "normal");
14591
+ }
14592
+ if (clean === "true") {
14593
+ return /^ui\.show.*Hint$/.test(setting) ? "shown" : "enabled";
14594
+ }
14595
+ if (clean === "false") {
14596
+ return /^ui\.show.*Hint$/.test(setting) ? "hidden" : "disabled";
14597
+ }
14598
+ return clean
14599
+ .replace(/_/g, " ")
14600
+ .replace(/\bpi\b/gi, "Pi")
14601
+ .replace(/\w\S*/g, (word) => word === "Pi" || word.toUpperCase() === "API" ? word : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase());
14602
+ }
14603
+
14604
+ function workflowFriendlyNotification(message: string, level?: "info" | "warning" | "error"): string {
14605
+ if (level !== "info") return message;
14606
+ let text = message.replace(/\s+in\s+\/\S+/g, "").replace(/\s+/g, " ").trim();
14607
+ const settingMatch = text.match(/^([A-Za-z][A-Za-z0-9_]*(?:\.[A-Za-z0-9_]+)*)\s+set to\s+([^.;]+)([.;]?.*)$/);
14608
+ if (settingMatch) {
14609
+ const [, setting, value, tail = ""] = settingMatch;
14610
+ const label = workflowSettingDisplayLabel(setting);
14611
+ const displayValue = workflowSettingDisplayValue(setting, value);
14612
+ const suffix = tail.replace(/^[.;]\s*/, "").trim();
14613
+ return `${label} set to ${displayValue}.${suffix ? ` ${suffix}` : ""}`;
14614
+ }
14615
+ return text.charAt(0).toUpperCase() + text.slice(1);
14616
+ }
14617
+
14618
+ function workflowUiNotify(ctx: ExtensionContext, message: string, level?: "info" | "warning" | "error"): void {
14619
+ ctx.ui.notify(workflowFriendlyNotification(message, level), level);
14620
+ }
14621
+
14358
14622
  async function showPlanningSettingsMenu(ctx: ExtensionContext) {
14359
14623
  if (!ctx.hasUI) return show(pi, renderFullWorkflowSettings(loadWorkflowSettings(ctx.cwd)));
14360
14624
  while (ctx.hasUI) {
@@ -14362,7 +14626,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14362
14626
  if (!choice || choice === "Back") return;
14363
14627
  if (choice === "Set planning.depth") {
14364
14628
  const depth = parsePlanningDepth((await ctx.ui.select("Planning depth", ["fast", "standard", "deep", "maximum"])) ?? "");
14365
- if (depth) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.depth = depth; }); ctx.ui.notify(`planning.depth set to ${depth} in ${r.file}`, "info"); }
14629
+ if (depth) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.depth = depth; }); workflowUiNotify(ctx, `planning.depth set to ${depth} in ${r.file}`, "info"); }
14366
14630
  } else if (choice === "List Current Settings") {
14367
14631
  const s = loadWorkflowSettings(ctx.cwd);
14368
14632
  show(pi, `# Planning Settings\n\nplanning.depth: ${s.planning.depth}`);
@@ -14377,13 +14641,13 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14377
14641
  if (!choice || choice === "Back") return;
14378
14642
  if (choice === "Set clarificationMode") {
14379
14643
  const mode = parseClarificationMode((await ctx.ui.select("Clarification mode", ["auto", "always_for_nontrivial", "never"])) ?? "");
14380
- if (mode) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.clarificationMode = mode; }); ctx.ui.notify(`planning.clarificationMode set to ${mode} in ${r.file}`, "info"); }
14644
+ if (mode) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.clarificationMode = mode; }); workflowUiNotify(ctx, `planning.clarificationMode set to ${mode} in ${r.file}`, "info"); }
14381
14645
  } else if (choice === "Set interactiveClarificationEnabled") {
14382
14646
  const enabled = await chooseBool(ctx, "Interactive clarification enabled?");
14383
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.interactiveClarificationEnabled = enabled; }); ctx.ui.notify(`interactiveClarificationEnabled set to ${enabled} in ${r.file}`, "info"); }
14647
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.interactiveClarificationEnabled = enabled; }); workflowUiNotify(ctx, `interactiveClarificationEnabled set to ${enabled} in ${r.file}`, "info"); }
14384
14648
  } else if (choice === "Set maxClarificationQuestions") {
14385
14649
  const count = parsePositiveInt((await ctx.ui.select("Max clarification questions", ["1", "2", "3", "4", "5"])) ?? "");
14386
- if (count) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.maxClarificationQuestions = count; }); ctx.ui.notify(`maxClarificationQuestions set to ${count} in ${r.file}`, "info"); }
14650
+ if (count) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.maxClarificationQuestions = count; }); workflowUiNotify(ctx, `maxClarificationQuestions set to ${count} in ${r.file}`, "info"); }
14387
14651
  } else if (choice === "List Current Settings") {
14388
14652
  const s = loadWorkflowSettings(ctx.cwd);
14389
14653
  show(pi, `# Clarification Settings\n\nClarification Mode: ${s.planning.clarificationMode}\nInteractive Clarification: ${s.planning.interactiveClarificationEnabled !== false ? "enabled" : "disabled"}\nMax Clarification Questions: ${s.planning.maxClarificationQuestions}`);
@@ -14399,11 +14663,11 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14399
14663
  if (!choice || choice === "Back") return;
14400
14664
  if (choice.endsWith("Policy")) {
14401
14665
  const policy = parseSubagentPolicy((await ctx.ui.select(`${label} policy`, ["off", "auto", "deep", "maximum", "forced"])) ?? "");
14402
- if (policy) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.subagents as typeof s.subagents & Record<string, string>)[keys.policyKey] = policy; }); ctx.ui.notify(`subagents.${keys.policyKey} set to ${policy} in ${r.file}`, "info"); }
14666
+ if (policy) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.subagents as typeof s.subagents & Record<string, string>)[keys.policyKey] = policy; }); workflowUiNotify(ctx, `subagents.${keys.policyKey} set to ${policy} in ${r.file}`, "info"); }
14403
14667
  } else {
14404
14668
  const key = choice.includes("Deep") ? keys.deepKey : keys.maximumKey;
14405
14669
  const count = parsePositiveInt((await ctx.ui.select(`${label} ${choice.includes("Deep") ? "deep" : "maximum / forced"} workers`, ["0", "1", "2", "3", "4", "5", "6", "7", "8"])) ?? "");
14406
- if (count !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.subagents as typeof s.subagents & Record<string, number>)[key] = count; }); ctx.ui.notify(`subagents.${key} set to ${count} in ${r.file}. Forced policy uses this phase's Maximum / Forced worker count.`, "info"); }
14670
+ if (count !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.subagents as typeof s.subagents & Record<string, number>)[key] = count; }); workflowUiNotify(ctx, `subagents.${key} set to ${count} in ${r.file}. Forced policy uses this phase's Maximum / Forced worker count.`, "info"); }
14407
14671
  }
14408
14672
  }
14409
14673
  }
@@ -14421,7 +14685,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14421
14685
  const standard = s.standard as typeof s.standard & { subagents?: Record<string, boolean | number | string | undefined> };
14422
14686
  standard.subagents = { ...(standard.subagents ?? {}), [keys.policyKey]: policy };
14423
14687
  });
14424
- ctx.ui.notify(`standard.subagents.${keys.policyKey} set to ${policy} in ${r.file}`, "info");
14688
+ workflowUiNotify(ctx, `standard.subagents.${keys.policyKey} set to ${policy} in ${r.file}`, "info");
14425
14689
  }
14426
14690
  } else {
14427
14691
  const key = choice.includes("Deep") ? keys.deepKey : keys.maximumKey;
@@ -14431,7 +14695,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14431
14695
  const standard = s.standard as typeof s.standard & { subagents?: Record<string, boolean | number | string | undefined> };
14432
14696
  standard.subagents = { ...(standard.subagents ?? {}), [key]: count };
14433
14697
  });
14434
- ctx.ui.notify(`standard.subagents.${key} set to ${count} in ${r.file}. Forced policy uses this Standard phase's Maximum / Forced worker count.`, "info");
14698
+ workflowUiNotify(ctx, `standard.subagents.${key} set to ${count} in ${r.file}. Forced policy uses this Standard phase's Maximum / Forced worker count.`, "info");
14435
14699
  }
14436
14700
  }
14437
14701
  }
@@ -14450,7 +14714,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14450
14714
  const standard = s.standard as typeof s.standard & { subagents?: Record<string, boolean | number | string | undefined> };
14451
14715
  standard.subagents = { ...(standard.subagents ?? {}), [key]: enabled };
14452
14716
  });
14453
- ctx.ui.notify(`standard.subagents.${key} set to ${enabled} in ${r.file}`, "info");
14717
+ workflowUiNotify(ctx, `standard.subagents.${key} set to ${enabled} in ${r.file}`, "info");
14454
14718
  }
14455
14719
  }
14456
14720
  }
@@ -14467,7 +14731,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14467
14731
  const standard = s.standard as typeof s.standard & { subagents?: Record<string, boolean | number | string | undefined> };
14468
14732
  standard.subagents = { ...(standard.subagents ?? {}), [key]: enabled };
14469
14733
  });
14470
- ctx.ui.notify(`standard.subagents.${key} set to ${enabled} in ${r.file}`, "info");
14734
+ workflowUiNotify(ctx, `standard.subagents.${key} set to ${enabled} in ${r.file}`, "info");
14471
14735
  }
14472
14736
  }
14473
14737
  }
@@ -14479,7 +14743,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14479
14743
  if (!choice || choice === "Back") return;
14480
14744
  if (choice === "Enable / Disable Standard Sub-agents") {
14481
14745
  const enabled = await chooseBool(ctx, "standard.allowSubagents?");
14482
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.standard.allowSubagents = enabled; }); ctx.ui.notify(`standard.allowSubagents set to ${enabled} in ${r.file}`, "info"); }
14746
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.standard.allowSubagents = enabled; }); workflowUiNotify(ctx, `standard.allowSubagents set to ${enabled} in ${r.file}`, "info"); }
14483
14747
  } else if (choice === "Standard Planning / Research Policy / Workers") await showStandardSubagentPhaseSettingsMenu(ctx, "Planning", "Standard Planning / Research");
14484
14748
  else if (choice === "Standard Execution Policy / Workers") await showStandardSubagentPhaseSettingsMenu(ctx, "Execution", "Standard Execution");
14485
14749
  else if (choice === "Standard Repair Policy / Workers") await showStandardSubagentPhaseSettingsMenu(ctx, "Repair", "Standard Repair");
@@ -14487,15 +14751,15 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14487
14751
  else if (choice === "Standard Validation Policy / Workers") await showStandardSubagentPhaseSettingsMenu(ctx, "Validation", "Standard Validation");
14488
14752
  else if (choice === "Agent Scope") {
14489
14753
  const scope = parseWorkflowAgentScope((await ctx.ui.select("Standard agent scope", ["user", "project", "both"])) ?? "");
14490
- if (scope) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.standard.subagentScope = scope; }); ctx.ui.notify(`standard.subagentScope set to ${scope} in ${r.file}`, "info"); }
14754
+ if (scope) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.standard.subagentScope = scope; }); workflowUiNotify(ctx, `standard.subagentScope set to ${scope} in ${r.file}`, "info"); }
14491
14755
  } else if (choice === "Auto-use Toggles") await showStandardSubagentAutoUseMenu(ctx);
14492
14756
  else if (choice === "Parallel Agent Settings") await showStandardParallelismSettingsMenu(ctx);
14493
14757
  else if (choice === "Activity Indicator") {
14494
14758
  const enabled = await chooseBool(ctx, "subagents.activityIndicatorEnabled?");
14495
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.subagents.activityIndicatorEnabled = enabled; }); ctx.ui.notify(`subagents.activityIndicatorEnabled set to ${enabled} in ${r.file}`, "info"); renderWorkflowSubagentActivity(ctx); }
14759
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.subagents.activityIndicatorEnabled = enabled; }); workflowUiNotify(ctx, `subagents.activityIndicatorEnabled set to ${enabled} in ${r.file}`, "info"); renderWorkflowSubagentActivity(ctx); }
14496
14760
  } else if (choice === "List Current Settings") {
14497
14761
  const s = loadWorkflowSettings(ctx.cwd);
14498
- show(pi, `# Plan Sub-agents / Workers\n\n${renderPlanSubagentWorkerSettings(s)}`);
14762
+ show(pi, `# Standard Sub-agents / Workers\n\nSub-agents: ${s.standard.allowSubagents !== false ? "enabled" : "disabled"}\nAgent Scope: ${s.standard.subagentScope ?? "user"}\nActivity Indicator: ${s.subagents.activityIndicatorEnabled !== false ? "enabled" : "disabled"}\nAuto-use Planning: ${s.standard.subagents?.autoUseDuringPlanning !== false ? "on" : "off"}\nAuto-use Execution: ${s.standard.subagents?.autoUseDuringExecution !== false ? "on" : "off"}\nAuto-use Repair: ${s.standard.subagents?.autoUseDuringRepair !== false ? "on" : "off"}\nAuto-use Review: ${s.standard.subagents?.autoUseDuringReview !== false ? "on" : "off"}\nAuto-use Validation: ${s.standard.subagents?.autoUseDuringValidation !== false ? "on" : "off"}`);
14499
14763
  }
14500
14764
  }
14501
14765
  }
@@ -14507,11 +14771,11 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14507
14771
  if (!choice || choice === "Back") return;
14508
14772
  if (choice === "Mission Planning Policy") {
14509
14773
  const policy = parseSubagentPolicy((await ctx.ui.select("Mission planning policy", ["off", "auto", "deep", "maximum", "forced"])) ?? "");
14510
- if (policy) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.subagentPolicy = policy; }); ctx.ui.notify(`missions.subagentPolicy set to ${policy} in ${r.file}`, "info"); }
14774
+ if (policy) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.subagentPolicy = policy; }); workflowUiNotify(ctx, `missions.subagentPolicy set to ${policy} in ${r.file}`, "info"); }
14511
14775
  } else {
14512
14776
  const key = choice.includes("Deep") ? "minWorkersForDeep" : "minWorkersForMaximum";
14513
14777
  const count = parsePositiveInt((await ctx.ui.select(choice, ["0", "1", "2", "3", "4", "5", "6", "7", "8"])) ?? "");
14514
- if (count !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.missions as typeof s.missions & Record<string, number>)[key] = count; }); ctx.ui.notify(`missions.${key} set to ${count} in ${r.file}. Forced mission planning uses Mission Planning Maximum / Forced Workers.`, "info"); }
14778
+ if (count !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.missions as typeof s.missions & Record<string, number>)[key] = count; }); workflowUiNotify(ctx, `missions.${key} set to ${count} in ${r.file}. Forced mission planning uses Mission Planning Maximum / Forced Workers.`, "info"); }
14515
14779
  }
14516
14780
  }
14517
14781
  }
@@ -14524,7 +14788,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14524
14788
  const keyMap: Record<string, string> = { "Planning Auto-use": "autoUseDuringPlanning", "Execution Auto-use": "autoUseDuringExecution", "Repair Auto-use": "autoUseDuringRepair", "Review Auto-use": "autoUseDuringReview", "Validation Auto-use": "autoUseDuringValidation" };
14525
14789
  const key = keyMap[choice];
14526
14790
  const enabled = await chooseBool(ctx, `${key}?`);
14527
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.subagents as typeof s.subagents & Record<string, boolean>)[key] = enabled; }); ctx.ui.notify(`subagents.${key} set to ${enabled} in ${r.file}`, "info"); }
14791
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.subagents as typeof s.subagents & Record<string, boolean>)[key] = enabled; }); workflowUiNotify(ctx, `subagents.${key} set to ${enabled} in ${r.file}`, "info"); }
14528
14792
  }
14529
14793
  }
14530
14794
 
@@ -14535,7 +14799,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14535
14799
  if (!choice || choice === "Back") return;
14536
14800
  if (choice === "Enable / Disable Sub-agents") {
14537
14801
  const enabled = await chooseBool(ctx, "subagents.enabled?");
14538
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.subagents.enabled = enabled; }); ctx.ui.notify(`subagents.enabled set to ${enabled} in ${r.file}`, "info"); }
14802
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.subagents.enabled = enabled; }); workflowUiNotify(ctx, `subagents.enabled set to ${enabled} in ${r.file}`, "info"); }
14539
14803
  } else if (choice === "Planning Policy / Workers") await showSubagentPhaseSettingsMenu(ctx, "Planning", "Shared Planning");
14540
14804
  else if (choice === "Execution Policy / Workers") await showSubagentPhaseSettingsMenu(ctx, "Execution", "Shared Execution");
14541
14805
  else if (choice === "Repair Policy / Workers") await showSubagentPhaseSettingsMenu(ctx, "Repair", "Shared Repair");
@@ -14545,10 +14809,10 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14545
14809
  else if (choice === "Parallel Agent Settings") await showParallelismSettingsMenu(ctx);
14546
14810
  else if (choice === "Background Sub-agents") {
14547
14811
  const enabled = await chooseBool(ctx, "subagents.allowBackgroundSubagents?");
14548
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.subagents.allowBackgroundSubagents = enabled; }); ctx.ui.notify(`subagents.allowBackgroundSubagents set to ${enabled} in ${r.file}. Background sub-agents run in Planning/Review/Validation phases without blocking the parent.`, "info"); }
14812
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.subagents.allowBackgroundSubagents = enabled; }); workflowUiNotify(ctx, `subagents.allowBackgroundSubagents set to ${enabled} in ${r.file}. Background sub-agents run in Planning/Review/Validation phases without blocking the parent.`, "info"); }
14549
14813
  } else if (choice === "Activity Indicator") {
14550
14814
  const enabled = await chooseBool(ctx, "subagents.activityIndicatorEnabled?");
14551
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.subagents.activityIndicatorEnabled = enabled; }); ctx.ui.notify(`subagents.activityIndicatorEnabled set to ${enabled} in ${r.file}`, "info"); renderWorkflowSubagentActivity(ctx); }
14815
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.subagents.activityIndicatorEnabled = enabled; }); workflowUiNotify(ctx, `subagents.activityIndicatorEnabled set to ${enabled} in ${r.file}`, "info"); renderWorkflowSubagentActivity(ctx); }
14552
14816
  } else if (choice === "List Current Settings") {
14553
14817
  const s = loadWorkflowSettings(ctx.cwd);
14554
14818
  show(pi, `# Shared Sub-agents / Workers\n\nSub-agents: ${s.subagents.enabled !== false ? "enabled" : "disabled"}\nBackground Sub-agents: ${s.subagents.allowBackgroundSubagents === true ? "enabled" : "disabled"}\nActivity Indicator: ${s.subagents.activityIndicatorEnabled !== false ? "enabled" : "disabled"}\nAuto-use Planning: ${s.subagents.autoUseDuringPlanning !== false ? "on" : "off"}\nAuto-use Execution: ${s.subagents.autoUseDuringExecution !== false ? "on" : "off"}\nAuto-use Repair: ${s.subagents.autoUseDuringRepair !== false ? "on" : "off"}\nAuto-use Review: ${s.subagents.autoUseDuringReview !== false ? "on" : "off"}\nAuto-use Validation: ${s.subagents.autoUseDuringValidation !== false ? "on" : "off"}`);
@@ -14563,7 +14827,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14563
14827
  if (!choice || choice === "Back") return;
14564
14828
  if (choice === "Enable / Disable Sub-agents") {
14565
14829
  const enabled = await chooseBool(ctx, "subagents.enabled?");
14566
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.subagents.enabled = enabled; }); ctx.ui.notify(`subagents.enabled set to ${enabled} in ${r.file}`, "info"); }
14830
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.subagents.enabled = enabled; }); workflowUiNotify(ctx, `subagents.enabled set to ${enabled} in ${r.file}`, "info"); }
14567
14831
  } else if (choice === "Plan Planning Policy / Workers") await showSubagentPhaseSettingsMenu(ctx, "Planning", "Plan Planning");
14568
14832
  else if (choice === "Plan Execution Policy / Workers") await showSubagentPhaseSettingsMenu(ctx, "Execution", "Plan Execution");
14569
14833
  else if (choice === "Plan Repair Policy / Workers") await showSubagentPhaseSettingsMenu(ctx, "Repair", "Plan Repair");
@@ -14573,10 +14837,10 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14573
14837
  else if (choice === "Parallel Agent Settings") await showParallelismSettingsMenu(ctx);
14574
14838
  else if (choice === "Activity Indicator") {
14575
14839
  const enabled = await chooseBool(ctx, "subagents.activityIndicatorEnabled?");
14576
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.subagents.activityIndicatorEnabled = enabled; }); ctx.ui.notify(`subagents.activityIndicatorEnabled set to ${enabled} in ${r.file}`, "info"); renderWorkflowSubagentActivity(ctx); }
14840
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.subagents.activityIndicatorEnabled = enabled; }); workflowUiNotify(ctx, `subagents.activityIndicatorEnabled set to ${enabled} in ${r.file}`, "info"); renderWorkflowSubagentActivity(ctx); }
14577
14841
  } else if (choice === "List Current Settings") {
14578
14842
  const s = loadWorkflowSettings(ctx.cwd);
14579
- show(pi, `# Standard Sub-agents / Workers\n\nSub-agents: ${s.standard.allowSubagents !== false ? "enabled" : "disabled"}\nAgent Scope: ${s.standard.subagentScope ?? "user"}\nActivity Indicator: ${s.subagents.activityIndicatorEnabled !== false ? "enabled" : "disabled"}\nAuto-use Planning: ${s.subagents.autoUseDuringPlanning !== false ? "on" : "off"}\nAuto-use Execution: ${s.subagents.autoUseDuringExecution !== false ? "on" : "off"}\nAuto-use Repair: ${s.subagents.autoUseDuringRepair !== false ? "on" : "off"}\nAuto-use Review: ${s.subagents.autoUseDuringReview !== false ? "on" : "off"}\nAuto-use Validation: ${s.subagents.autoUseDuringValidation !== false ? "on" : "off"}`);
14843
+ show(pi, `# Plan Sub-agents / Workers\n\n${renderPlanSubagentWorkerSettings(s)}`);
14580
14844
  }
14581
14845
  }
14582
14846
  }
@@ -14588,7 +14852,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14588
14852
  if (!choice || choice === "Back") return;
14589
14853
  if (choice === "Enable / Disable Sub-agents") {
14590
14854
  const enabled = await chooseBool(ctx, "subagents.enabled?");
14591
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.subagents.enabled = enabled; }); ctx.ui.notify(`subagents.enabled set to ${enabled} in ${r.file}`, "info"); }
14855
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.subagents.enabled = enabled; }); workflowUiNotify(ctx, `subagents.enabled set to ${enabled} in ${r.file}`, "info"); }
14592
14856
  } else if (choice === "Mission Planning Policy / Workers") await showMissionPlanningSubagentSettingsMenu(ctx);
14593
14857
  else if (choice === "Mission Execution Policy / Workers") await showSubagentPhaseSettingsMenu(ctx, "Execution", "Mission Execution");
14594
14858
  else if (choice === "Mission Repair Policy / Workers") await showSubagentPhaseSettingsMenu(ctx, "Repair", "Mission Repair");
@@ -14598,7 +14862,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14598
14862
  else if (choice === "Parallel Agent Settings") await showParallelismSettingsMenu(ctx);
14599
14863
  else if (choice === "Activity Indicator") {
14600
14864
  const enabled = await chooseBool(ctx, "subagents.activityIndicatorEnabled?");
14601
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.subagents.activityIndicatorEnabled = enabled; }); ctx.ui.notify(`subagents.activityIndicatorEnabled set to ${enabled} in ${r.file}`, "info"); renderWorkflowSubagentActivity(ctx); }
14865
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.subagents.activityIndicatorEnabled = enabled; }); workflowUiNotify(ctx, `subagents.activityIndicatorEnabled set to ${enabled} in ${r.file}`, "info"); renderWorkflowSubagentActivity(ctx); }
14602
14866
  } else if (choice === "List Current Settings") {
14603
14867
  const s = loadWorkflowSettings(ctx.cwd);
14604
14868
  show(pi, `# Mission Sub-agents / Workers\n\n${renderMissionSubagentWorkerSettings(s)}`);
@@ -14614,10 +14878,10 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14614
14878
  if (choice?.startsWith("Set allow") || choice === "Set requireParallelEditConflictProtection") {
14615
14879
  const key = choice.replace("Set ", "");
14616
14880
  const enabled = await chooseBool(ctx, `${key}?`);
14617
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.subagents as typeof s.subagents & Record<string, boolean>)[key] = enabled; }); ctx.ui.notify(`subagents.${key} set to ${enabled} in ${r.file}`, "info"); }
14881
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.subagents as typeof s.subagents & Record<string, boolean>)[key] = enabled; }); workflowUiNotify(ctx, `subagents.${key} set to ${enabled} in ${r.file}`, "info"); }
14618
14882
  } else if (choice === "Set editConcurrencyMode") {
14619
14883
  const mode = parseEditConcurrencyMode((await ctx.ui.select("Edit concurrency mode", ["sequential", "scoped", "blocked"])) ?? "");
14620
- if (mode) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.subagents.editConcurrencyMode = mode; }); ctx.ui.notify(`subagents.editConcurrencyMode set to ${mode} in ${r.file}`, "info"); }
14884
+ if (mode) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.subagents.editConcurrencyMode = mode; }); workflowUiNotify(ctx, `subagents.editConcurrencyMode set to ${mode} in ${r.file}`, "info"); }
14621
14885
  } else if (choice === "List Current Settings") {
14622
14886
  const s = loadWorkflowSettings(ctx.cwd);
14623
14887
  show(pi, `# Parallelism Settings\n\nParallel Read-Only: ${s.subagents.allowParallelReadOnly !== false ? "enabled" : "disabled"}\nParallel Planning: ${s.subagents.allowParallelPlanning !== false ? "enabled" : "disabled"}\nParallel Execution: ${s.subagents.allowParallelExecution !== false ? "enabled" : "disabled"}\nParallel Repair: ${s.subagents.allowParallelRepair !== false ? "enabled" : "disabled"}\nParallel Review: ${s.subagents.allowParallelReview !== false ? "enabled" : "disabled"}\nParallel Validation: ${s.subagents.allowParallelValidation !== false ? "enabled" : "disabled"}\nParallel File Edits: ${s.subagents.allowParallelEdits !== false ? "enabled" : "disabled"}\nEdit Concurrency Mode: ${s.subagents.editConcurrencyMode ?? "sequential"}\nConflict Protection: ${s.subagents.requireParallelEditConflictProtection !== false ? "enabled" : "disabled"}`);
@@ -14629,7 +14893,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14629
14893
  const models = ctx.modelRegistry.getAll();
14630
14894
  const providers = [...new Set(models.map((m) => m.provider))].sort();
14631
14895
  if (providers.length === 0) {
14632
- ctx.ui.notify("No models are available in the model registry.", "error");
14896
+ workflowUiNotify(ctx, "No models are available in the model registry.", "error");
14633
14897
  return undefined;
14634
14898
  }
14635
14899
  const providerChoice = await ctx.ui.select(`${title}: select provider`, [...providers, "Custom provider name"]);
@@ -14643,7 +14907,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14643
14907
  }
14644
14908
  const providerModels = models.filter((m) => m.provider === providerChoice).map((m) => m.id).sort();
14645
14909
  if (providerModels.length === 0) {
14646
- ctx.ui.notify(`No models found for provider ${providerChoice}.`, "error");
14910
+ workflowUiNotify(ctx, `No models found for provider ${providerChoice}.`, "error");
14647
14911
  return undefined;
14648
14912
  }
14649
14913
  const modelChoice = await ctx.ui.select(`${title}: select model`, providerModels.map((model) => `${providerChoice}/${model}`));
@@ -14651,33 +14915,62 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14651
14915
  return { provider: providerChoice, model: modelChoice.replace(`${providerChoice}/`, "") };
14652
14916
  }
14653
14917
 
14654
- async function selectCompactionModel(ctx: ExtensionContext) {
14655
- const selected = await selectProviderAndModel(ctx, "Compaction model");
14656
- if (!selected) return;
14918
+ async function chooseCompactionProvider(ctx: ExtensionContext): Promise<string | undefined> {
14919
+ const models = ctx.modelRegistry.getAll();
14920
+ const providers = [...new Set(models.map((m) => m.provider))].sort();
14921
+ const providerChoice = await ctx.ui.select("Compaction Provider", ["Pi default", "Custom provider", "Back"]);
14922
+ if (!providerChoice || providerChoice === "Back") return undefined;
14923
+ if (providerChoice === "Pi default") return "";
14924
+ const choice = await ctx.ui.select("Custom provider", [...providers, "Custom provider name", "Back"]);
14925
+ if (!choice || choice === "Back") return undefined;
14926
+ if (choice === "Custom provider name") return (await ctx.ui.input("Custom compaction provider:", "provider-name"))?.trim();
14927
+ return choice;
14928
+ }
14929
+
14930
+ async function selectCompactionProvider(ctx: ExtensionContext) {
14931
+ const provider = await chooseCompactionProvider(ctx);
14932
+ if (provider === undefined) return;
14657
14933
  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;
14934
+ s.context.compactionModelProvider = provider;
14935
+ s.context.compactionModel = "";
14662
14936
  });
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");
14937
+ workflowUiNotify(ctx, 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
14938
  }
14665
14939
 
14666
- async function selectCompactionModelForCurrentProvider(ctx: ExtensionContext) {
14940
+ async function selectCompactionModel(ctx: ExtensionContext) {
14667
14941
  const settings = loadWorkflowSettings(ctx.cwd);
14668
14942
  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}/`, "");
14943
+ const mode = await ctx.ui.select("Compaction Model", ["Pi default", "Custom model", "Back"]);
14944
+ if (!mode || mode === "Back") return;
14945
+ if (mode === "Pi default") {
14946
+ const result = updateSettings(ctx.cwd, undefined, (s) => {
14947
+ s.context.compactionModelProvider = "";
14948
+ s.context.compactionModel = "";
14949
+ });
14950
+ workflowUiNotify(ctx, `Compaction summary model reset to Pi default in ${result.file}. Trigger behavior was not changed.`, "info");
14951
+ return;
14952
+ }
14953
+
14954
+ let selectedProvider = provider;
14955
+ if (!selectedProvider) {
14956
+ const chosen = await chooseCompactionProvider(ctx);
14957
+ if (chosen === undefined) return;
14958
+ selectedProvider = chosen;
14959
+ }
14960
+ if (!selectedProvider) {
14961
+ workflowUiNotify(ctx, "Select a custom compaction provider before selecting a custom model.", "warning");
14962
+ return;
14963
+ }
14964
+ const providerModels = ctx.modelRegistry.getAll().filter((m) => m.provider === selectedProvider).map((m) => m.id).sort();
14965
+ const modelChoice = await ctx.ui.select(`Custom model for ${selectedProvider}:`, [...providerModels.map((model) => `${selectedProvider}/${model}`), "Custom model name", "Back"]);
14966
+ if (!modelChoice || modelChoice === "Back") return;
14967
+ const model = modelChoice === "Custom model name" ? (await ctx.ui.input("Custom compaction model:", "model-name"))?.trim() : modelChoice.replace(`${selectedProvider}/`, "");
14968
+ if (!model) return;
14675
14969
  const result = updateSettings(ctx.cwd, undefined, (s) => {
14676
- s.context.compactionMode = "custom_model";
14970
+ s.context.compactionModelProvider = selectedProvider;
14677
14971
  s.context.compactionModel = model;
14678
- s.context.customCompactionEnabled = true;
14679
14972
  });
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");
14973
+ workflowUiNotify(ctx, `Compaction model preference stored as ${selectedProvider}/${model} in ${result.file}. Trigger behavior was not changed.`, "info");
14681
14974
  }
14682
14975
 
14683
14976
  async function selectCompactionAgent(ctx: ExtensionContext) {
@@ -14691,66 +14984,76 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14691
14984
  s.context.compactionAgent = agent;
14692
14985
  s.context.customCompactionEnabled = true;
14693
14986
  });
14694
- ctx.ui.notify(`Compaction agent stored as ${agent} in ${result.file}. Current releases keep Pi default fallback behavior for agent-routed compaction.`, "info");
14987
+ workflowUiNotify(ctx, `Compaction agent stored as ${agent} in ${result.file}. Current releases keep Pi default fallback behavior for agent-routed compaction.`, "info");
14695
14988
  }
14696
14989
 
14697
14990
  async function showCompactionSettingsMenu(ctx: ExtensionContext) {
14698
14991
  if (!ctx.hasUI) return show(pi, renderFullWorkflowSettings(loadWorkflowSettings(ctx.cwd)));
14699
14992
  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"]);
14993
+ 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
14994
  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);
14995
+ if (choice === "Compaction Provider") {
14996
+ await selectCompactionProvider(ctx);
14716
14997
  } 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"); }
14998
+ await selectCompactionModel(ctx);
14721
14999
  } 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"); }
15000
+ const trigger = await ctx.ui.select("Workflow Auto Trigger Enabled", ["Pi default", "Enabled", "Disabled", "Back"]);
15001
+ if (!trigger || trigger === "Back") continue;
15002
+ const r = updateSettings(ctx.cwd, undefined, (s) => {
15003
+ if (trigger === "Pi default") delete s.context.autoCompactionEnabled;
15004
+ else s.context.autoCompactionEnabled = trigger === "Enabled";
15005
+ });
15006
+ workflowUiNotify(ctx, `Workflow auto trigger set to ${trigger.toLowerCase()} in ${r.file}.`, "info");
14724
15007
  } 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") {
15008
+ const mode = await ctx.ui.select("Workflow Trigger Percent", ["Pi default", "Custom percent", "Back"]);
15009
+ if (!mode || mode === "Back") continue;
15010
+ if (mode === "Pi default") {
14728
15011
  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");
15012
+ workflowUiNotify(ctx, `Workflow compaction trigger percent override removed in ${r.file}; Pi native trigger formula remains in control.`, "info");
14730
15013
  } else {
15014
+ const raw = String((await ctx.ui.input("Workflow compaction trigger percent (50-95)", String(compactionTriggerPercentOverride(loadWorkflowSettings(ctx.cwd)) ?? 85))) ?? "").trim();
14731
15015
  const count = Number(raw);
14732
- 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");
15016
+ if (Number.isInteger(count) && count >= 50 && count <= 95) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.compactionTriggerPercent = count; }); workflowUiNotify(ctx, `Workflow compaction trigger percent set to ${count}% in ${r.file}`, "info"); }
15017
+ else workflowUiNotify(ctx, "Trigger percent must be an integer from 50 to 95.", "error");
14734
15018
  }
14735
15019
  } 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");
15020
+ const mode = await ctx.ui.select("Workflow Trigger Cooldown", ["Pi default", "Custom cooldown", "Back"]);
15021
+ if (!mode || mode === "Back") continue;
15022
+ if (mode === "Pi default") {
15023
+ const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.compactionCooldownMinutes = 5; });
15024
+ workflowUiNotify(ctx, `Compaction cooldown reset to Pi default (5 minutes) in ${r.file}.`, "info");
15025
+ } else {
15026
+ const count = Number((await ctx.ui.input("Minimum minutes between Workflow Suite proactive compaction attempts", String(compactionCooldownMinutes(loadWorkflowSettings(ctx.cwd)))) ?? ""));
15027
+ if (Number.isInteger(count) && count >= 0 && count <= 240) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.compactionCooldownMinutes = count; }); workflowUiNotify(ctx, `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"); }
15028
+ else workflowUiNotify(ctx, "Cooldown must be an integer from 0 to 240 minutes.", "error");
15029
+ }
14739
15030
  } 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");
15031
+ const mode = await ctx.ui.select("Custom Reserve Tokens", ["Pi default", "Custom reserve tokens", "Back"]);
15032
+ if (!mode || mode === "Back") continue;
15033
+ if (mode === "Pi default") {
15034
+ const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.customCompactionReserveTokens = DEFAULT_PI_COMPACTION_RESERVE_TOKENS; });
15035
+ workflowUiNotify(ctx, `Custom compaction reserve tokens reset to Pi default (${DEFAULT_PI_COMPACTION_RESERVE_TOKENS.toLocaleString()}) in ${r.file}`, "info");
15036
+ } else {
15037
+ const raw = String((await ctx.ui.input("Custom compaction reserve tokens (4096-65536)", String(customCompactionReserveTokens(loadWorkflowSettings(ctx.cwd)))) ?? "")).trim();
15038
+ const count = Number(raw);
15039
+ if (Number.isInteger(count) && count >= 4096 && count <= 65536) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.customCompactionReserveTokens = count; }); workflowUiNotify(ctx, `Custom compaction reserve tokens set to ${count.toLocaleString()} in ${r.file}`, "info"); }
15040
+ else workflowUiNotify(ctx, "Reserve tokens must be an integer from 4096 to 65536.", "error");
15041
+ }
14745
15042
  } 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");
15043
+ const mode = await ctx.ui.select("Custom Keep Recent Tokens", ["Pi default", "Custom keep-recent tokens", "Back"]);
15044
+ if (!mode || mode === "Back") continue;
15045
+ if (mode === "Pi default") {
15046
+ const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.customCompactionKeepRecentTokens = DEFAULT_PI_COMPACTION_KEEP_RECENT_TOKENS; });
15047
+ workflowUiNotify(ctx, `Custom compaction keep-recent tokens reset to Pi default (${DEFAULT_PI_COMPACTION_KEEP_RECENT_TOKENS.toLocaleString()}) in ${r.file}`, "info");
15048
+ } else {
15049
+ const raw = String((await ctx.ui.input("Custom compaction keep-recent tokens (1000-200000)", String(customCompactionKeepRecentTokens(loadWorkflowSettings(ctx.cwd)))) ?? "")).trim();
15050
+ const count = Number(raw);
15051
+ if (Number.isInteger(count) && count >= 1000 && count <= 200000) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.context.customCompactionKeepRecentTokens = count; }); workflowUiNotify(ctx, `Custom compaction keep-recent tokens set to ${count.toLocaleString()} in ${r.file}`, "info"); }
15052
+ else workflowUiNotify(ctx, "Keep-recent tokens must be an integer from 1000 to 200000.", "error");
15053
+ }
14751
15054
  } else if (choice === "List Current Settings") {
14752
15055
  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"}`);
15056
+ 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
15057
  }
14755
15058
  }
14756
15059
  }
@@ -14797,7 +15100,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14797
15100
  }
14798
15101
  });
14799
15102
  await reapplyStandardRouteIfStandardMode(ctx, true);
14800
- ctx.ui.notify(`Standard model source set to ${source} in ${result.file}`, "info");
15103
+ workflowUiNotify(ctx, `Standard model source set to ${source} in ${result.file}`, "info");
14801
15104
  } else if (choice === "Standard Model Role") {
14802
15105
  const role = parseStandardModelRole((await ctx.ui.select("Standard model role", ["Current Pi model", "Planner", "Executor", "Reviewer", "Validator"])) ?? "");
14803
15106
  if (!role) continue;
@@ -14811,7 +15114,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14811
15114
  }
14812
15115
  });
14813
15116
  await reapplyStandardRouteIfStandardMode(ctx, true);
14814
- ctx.ui.notify(`Standard model role set to ${workflowRoleLabel(role)} in ${result.file}`, "info");
15117
+ workflowUiNotify(ctx, `Standard model role set to ${workflowRoleLabel(role)} in ${result.file}`, "info");
14815
15118
  } else if (choice === "Configure Standard Planner") await configureStandardRoleModel(ctx, "planner");
14816
15119
  else if (choice === "Configure Standard Executor") await configureStandardRoleModel(ctx, "executor");
14817
15120
  else if (choice === "Configure Standard Reviewer") await configureStandardRoleModel(ctx, "reviewer");
@@ -14832,10 +15135,10 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14832
15135
  const options = key === "maxValidationRetriesPerPlan" ? ["0", "1", "2", "3", "4", "5", "10"] : ["0", "1", "2", "4", "8", "12", "20", "50", "100"];
14833
15136
  const raw = await ctx.ui.select(setting, options);
14834
15137
  const count = raw === undefined ? undefined : Number(raw);
14835
- if (Number.isInteger(count) && count >= 0 && count <= max) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.workflow as typeof s.workflow & Record<string, number>)[key] = count; }); ctx.ui.notify(`workflow.${key} set to ${count} in ${r.file}`, "info"); }
15138
+ if (Number.isInteger(count) && count >= 0 && count <= max) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.workflow as typeof s.workflow & Record<string, number>)[key] = count; }); workflowUiNotify(ctx, `workflow.${key} set to ${count} in ${r.file}`, "info"); }
14836
15139
  } else if (setting === "Validation Retry Mode") {
14837
15140
  const mode = parseValidationRetryMode((await ctx.ui.select("Validation retry mode", ["off", "safe_only", "aggressive_within_scope"])) ?? "");
14838
- if (mode) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.validationRetryMode = mode; }); ctx.ui.notify(`workflow.validationRetryMode set to ${mode} in ${r.file}`, "info"); }
15141
+ if (mode) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.validationRetryMode = mode; }); workflowUiNotify(ctx, `workflow.validationRetryMode set to ${mode} in ${r.file}`, "info"); }
14839
15142
  } else {
14840
15143
  const keyMap: Record<string, keyof ReturnType<typeof loadWorkflowSettings>["workflow"]> = {
14841
15144
  "Auto Repair Validation Failures": "autoRepairValidationFailures",
@@ -14845,7 +15148,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14845
15148
  };
14846
15149
  const key = keyMap[setting];
14847
15150
  const enabled = await chooseBool(ctx, `${String(key)}?`);
14848
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.workflow as Record<string, unknown>)[String(key)] = enabled; }); ctx.ui.notify(`workflow.${String(key)} set to ${enabled} in ${r.file}`, "info"); }
15151
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.workflow as Record<string, unknown>)[String(key)] = enabled; }); workflowUiNotify(ctx, `workflow.${String(key)} set to ${enabled} in ${r.file}`, "info"); }
14849
15152
  }
14850
15153
  }
14851
15154
  }
@@ -14859,26 +15162,26 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14859
15162
  await showModelSettingsMenu(ctx);
14860
15163
  } else if (choice === "Planning Depth") {
14861
15164
  const depth = parsePlanningDepth((await ctx.ui.select("Planning depth", ["fast", "standard", "deep", "maximum"])) ?? "");
14862
- if (depth) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.depth = depth; }); ctx.ui.notify(`planning.depth set to ${depth} in ${r.file}`, "info"); }
15165
+ if (depth) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.depth = depth; }); workflowUiNotify(ctx, `planning.depth set to ${depth} in ${r.file}`, "info"); }
14863
15166
  } else if (choice === "Plan Clarification") {
14864
15167
  const setting = await ctx.ui.select("Plan Clarification", ["Clarification Mode", "Interactive Clarification", "Max Clarification Questions", "Timing", "Quality Gate", "Allow Without Analysis", "Use Sub-agents Before Clarification", "Back"]);
14865
15168
  if (!setting || setting === "Back") continue;
14866
15169
  if (setting === "Clarification Mode") {
14867
15170
  const mode = parseClarificationMode((await ctx.ui.select("Plan clarification mode", ["auto", "always_for_nontrivial", "never"])) ?? "");
14868
- if (mode) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.clarificationMode = mode; }); ctx.ui.notify(`planning.clarificationMode set to ${mode} in ${r.file}`, "info"); }
15171
+ if (mode) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.clarificationMode = mode; }); workflowUiNotify(ctx, `planning.clarificationMode set to ${mode} in ${r.file}`, "info"); }
14869
15172
  } else if (setting === "Interactive Clarification") {
14870
15173
  const enabled = await chooseBool(ctx, "Interactive clarification enabled?");
14871
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.interactiveClarificationEnabled = enabled; }); ctx.ui.notify(`planning.interactiveClarificationEnabled set to ${enabled} in ${r.file}`, "info"); }
15174
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.interactiveClarificationEnabled = enabled; }); workflowUiNotify(ctx, `planning.interactiveClarificationEnabled set to ${enabled} in ${r.file}`, "info"); }
14872
15175
  } else if (setting === "Max Clarification Questions") {
14873
15176
  const count = parsePositiveInt((await ctx.ui.select("Max clarification questions", ["1", "2", "3", "4", "5"])) ?? "");
14874
- if (count) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.maxClarificationQuestions = count; }); ctx.ui.notify(`planning.maxClarificationQuestions set to ${count} in ${r.file}`, "info"); }
15177
+ if (count) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.maxClarificationQuestions = count; }); workflowUiNotify(ctx, `planning.maxClarificationQuestions set to ${count} in ${r.file}`, "info"); }
14875
15178
  } else if (setting === "Timing") {
14876
15179
  const timing = parseClarificationTiming((await ctx.ui.select("Plan clarification timing", ["after_initial_analysis", "immediate"])) ?? "");
14877
- if (timing) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.clarificationTiming = timing; }); ctx.ui.notify(`planning.clarificationTiming set to ${timing} in ${r.file}`, "info"); }
15180
+ if (timing) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.clarificationTiming = timing; }); workflowUiNotify(ctx, `planning.clarificationTiming set to ${timing} in ${r.file}`, "info"); }
14878
15181
  } else {
14879
15182
  const key = setting === "Quality Gate" ? "clarificationQualityGate" : setting === "Allow Without Analysis" ? "allowClarificationWithoutAnalysis" : "useSubagentsBeforeClarification";
14880
15183
  const enabled = await chooseBool(ctx, `planning.${key}?`);
14881
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.planning as typeof s.planning & Record<string, boolean | string | number | undefined>)[key] = enabled; }); ctx.ui.notify(`planning.${key} set to ${enabled} in ${r.file}`, "info"); }
15184
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.planning as typeof s.planning & Record<string, boolean | string | number | undefined>)[key] = enabled; }); workflowUiNotify(ctx, `planning.${key} set to ${enabled} in ${r.file}`, "info"); }
14882
15185
  }
14883
15186
  } else if (choice === "Plan Sub-agents / Workers") {
14884
15187
  await showPlanSubagentWorkerSettingsMenu(ctx);
@@ -14887,16 +15190,16 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14887
15190
  if (!setting || setting === "Back") continue;
14888
15191
  const enabled = await chooseBool(ctx, `${setting}?`);
14889
15192
  if (enabled === undefined) continue;
14890
- if (setting === "Reviewer Enabled") { const r = setRoleEnabled("reviewer", enabled, ctx.cwd); ctx.ui.notify(`reviewer.enabled set to ${enabled} in ${r.file}`, "info"); }
14891
- else if (setting === "Validator Enabled") { const r = setRoleEnabled("validator", enabled, ctx.cwd); ctx.ui.notify(`validator.enabled set to ${enabled} in ${r.file}`, "info"); }
14892
- else if (setting === "Auto Run Reviewer") { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.autoRunReviewerBeforeExecute = enabled; }); ctx.ui.notify(`workflow.autoRunReviewerBeforeExecute set to ${enabled} in ${r.file}`, "info"); }
14893
- else if (setting === "Auto Run Validation") { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.autoRunValidationAfterExecute = enabled; }); ctx.ui.notify(`workflow.autoRunValidationAfterExecute set to ${enabled} in ${r.file}`, "info"); }
14894
- else if (setting === "Auto Repair Review Failures") { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.autoRepairReviewFailures = enabled; }); ctx.ui.notify(`workflow.autoRepairReviewFailures set to ${enabled} in ${r.file}`, "info"); }
14895
- else if (setting === "Pause After Review Failure") { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.pauseAfterReviewFailure = enabled; }); ctx.ui.notify(`workflow.pauseAfterReviewFailure set to ${enabled} in ${r.file}`, "info"); }
14896
- else if (setting === "Approval Before Execution") { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.requireApprovalBeforeExecution = enabled; s.workflow.requirePlanApprovalBeforeExecute = enabled; }); ctx.ui.notify(`workflow.requireApprovalBeforeExecution set to ${enabled} in ${r.file}`, "info"); }
14897
- else if (setting === "Approval Per Step") { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.requireApprovalPerStep = enabled; }); ctx.ui.notify(`workflow.requireApprovalPerStep set to ${enabled} in ${r.file}`, "info"); }
14898
- else if (setting === "Validate After Each Step") { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.validateAfterEachStep = enabled; }); ctx.ui.notify(`workflow.validateAfterEachStep set to ${enabled} in ${r.file}`, "info"); }
14899
- else if (setting === "Validate After Full Execution") { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.validateAfterExecution = enabled; s.workflow.autoRunValidationAfterExecute = enabled; }); ctx.ui.notify(`workflow.validateAfterExecution set to ${enabled} in ${r.file}`, "info"); }
15193
+ if (setting === "Reviewer Enabled") { const r = setRoleEnabled("reviewer", enabled, ctx.cwd); workflowUiNotify(ctx, `reviewer.enabled set to ${enabled} in ${r.file}`, "info"); }
15194
+ else if (setting === "Validator Enabled") { const r = setRoleEnabled("validator", enabled, ctx.cwd); workflowUiNotify(ctx, `validator.enabled set to ${enabled} in ${r.file}`, "info"); }
15195
+ else if (setting === "Auto Run Reviewer") { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.autoRunReviewerBeforeExecute = enabled; }); workflowUiNotify(ctx, `workflow.autoRunReviewerBeforeExecute set to ${enabled} in ${r.file}`, "info"); }
15196
+ else if (setting === "Auto Run Validation") { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.autoRunValidationAfterExecute = enabled; }); workflowUiNotify(ctx, `workflow.autoRunValidationAfterExecute set to ${enabled} in ${r.file}`, "info"); }
15197
+ else if (setting === "Auto Repair Review Failures") { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.autoRepairReviewFailures = enabled; }); workflowUiNotify(ctx, `workflow.autoRepairReviewFailures set to ${enabled} in ${r.file}`, "info"); }
15198
+ else if (setting === "Pause After Review Failure") { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.pauseAfterReviewFailure = enabled; }); workflowUiNotify(ctx, `workflow.pauseAfterReviewFailure set to ${enabled} in ${r.file}`, "info"); }
15199
+ else if (setting === "Approval Before Execution") { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.requireApprovalBeforeExecution = enabled; s.workflow.requirePlanApprovalBeforeExecute = enabled; }); workflowUiNotify(ctx, `workflow.requireApprovalBeforeExecution set to ${enabled} in ${r.file}`, "info"); }
15200
+ else if (setting === "Approval Per Step") { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.requireApprovalPerStep = enabled; }); workflowUiNotify(ctx, `workflow.requireApprovalPerStep set to ${enabled} in ${r.file}`, "info"); }
15201
+ else if (setting === "Validate After Each Step") { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.validateAfterEachStep = enabled; }); workflowUiNotify(ctx, `workflow.validateAfterEachStep set to ${enabled} in ${r.file}`, "info"); }
15202
+ else if (setting === "Validate After Full Execution") { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.validateAfterExecution = enabled; s.workflow.autoRunValidationAfterExecute = enabled; }); workflowUiNotify(ctx, `workflow.validateAfterExecution set to ${enabled} in ${r.file}`, "info"); }
14900
15203
  } else if (choice === "Repair / Validation Retry") {
14901
15204
  await showWorkflowRepairRetrySettingsMenu(ctx);
14902
15205
  } else if (choice === "Plan Progress / Runtime") {
@@ -14912,28 +15215,28 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14912
15215
  const choice = await ctx.ui.select("Plan Token Budget", ["Default (unlimited)", "Custom..."]);
14913
15216
  if (choice === "Default (unlimited)") {
14914
15217
  const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.maxTokens = 0; });
14915
- ctx.ui.notify(`planning.maxTokens set to default (unlimited) in ${r.file}`, "info");
15218
+ workflowUiNotify(ctx, `planning.maxTokens set to default (unlimited) in ${r.file}`, "info");
14916
15219
  } else if (choice === "Custom...") {
14917
15220
  const count = parsePositiveInt((await ctx.ui.input("Enter custom plan token budget", String(current > 0 ? current : ""))) ?? "");
14918
- if (count !== undefined && count >= 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.maxTokens = count; }); ctx.ui.notify(`planning.maxTokens set to ${count === 0 ? "unlimited" : count.toLocaleString()} in ${r.file}`, "info"); }
15221
+ if (count !== undefined && count >= 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.maxTokens = count; }); workflowUiNotify(ctx, `planning.maxTokens set to ${count === 0 ? "unlimited" : count.toLocaleString()} in ${r.file}`, "info"); }
14919
15222
  }
14920
15223
  } else if (setting === "Runtime Budget") {
14921
15224
  const current = loadWorkflowSettings(ctx.cwd).planning.maxRuntimeHours ?? 0;
14922
15225
  const choice = await ctx.ui.select("Plan Runtime Budget", ["Default (unlimited)", "Custom..."]);
14923
15226
  if (choice === "Default (unlimited)") {
14924
15227
  const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.maxRuntimeHours = 0; });
14925
- ctx.ui.notify(`planning.maxRuntimeHours set to default (unlimited) in ${r.file}`, "info");
15228
+ workflowUiNotify(ctx, `planning.maxRuntimeHours set to default (unlimited) in ${r.file}`, "info");
14926
15229
  } else if (choice === "Custom...") {
14927
15230
  const count = parsePositiveInt((await ctx.ui.input("Enter custom plan runtime budget (hours)", String(current > 0 ? current : ""))) ?? "");
14928
- if (count !== undefined && count > 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.maxRuntimeHours = count; }); ctx.ui.notify(`planning.maxRuntimeHours set to ${count} hours in ${r.file}`, "info"); }
15231
+ if (count !== undefined && count > 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.planning.maxRuntimeHours = count; }); workflowUiNotify(ctx, `planning.maxRuntimeHours set to ${count} hours in ${r.file}`, "info"); }
14929
15232
  }
14930
15233
  } else if (setting === "Progress Bar Display") {
14931
15234
  const enabled = await chooseBool(ctx, "workflow.planShowProgressBar?");
14932
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.planShowProgressBar = enabled; }); ctx.ui.notify(`workflow.planShowProgressBar set to ${enabled} in ${r.file}`, "info"); }
15235
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.planShowProgressBar = enabled; }); workflowUiNotify(ctx, `workflow.planShowProgressBar set to ${enabled} in ${r.file}`, "info"); }
14933
15236
  } else {
14934
15237
  const key = setting === "Progress Tracking" ? "planProgressEnabled" : "planRuntimeEnabled";
14935
15238
  const enabled = await chooseBool(ctx, `workflow.${key}?`);
14936
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.workflow as typeof s.workflow & Record<string, boolean>)[key] = enabled; }); ctx.ui.notify(`workflow.${key} set to ${enabled} in ${r.file}`, "info"); }
15239
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.workflow as typeof s.workflow & Record<string, boolean>)[key] = enabled; }); workflowUiNotify(ctx, `workflow.${key} set to ${enabled} in ${r.file}`, "info"); }
14937
15240
  }
14938
15241
  } else if (choice === "Plan History") {
14939
15242
  const setting = await ctx.ui.select("Plan History", ["Save Plans", "Save Plan History", "Plan History Limit", "Back"]);
@@ -14941,11 +15244,11 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14941
15244
  if (!setting || setting === "Back") continue;
14942
15245
  if (setting === "Plan History Limit") {
14943
15246
  const count = parsePositiveInt((await ctx.ui.input("Plan history limit", String(workflow.planHistoryLimit ?? 50))) ?? "");
14944
- if (count !== undefined && count > 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.planHistoryLimit = count; }); ctx.ui.notify(`workflow.planHistoryLimit set to ${count} in ${r.file}`, "info"); }
15247
+ if (count !== undefined && count > 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.workflow.planHistoryLimit = count; }); workflowUiNotify(ctx, `workflow.planHistoryLimit set to ${count} in ${r.file}`, "info"); }
14945
15248
  } else {
14946
15249
  const key = setting === "Save Plans" ? "savePlans" : "savePlanHistory";
14947
15250
  const enabled = await chooseBool(ctx, `${key}?`);
14948
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.workflow as typeof s.workflow & Record<string, boolean>)[key] = enabled; }); ctx.ui.notify(`workflow.${key} set to ${enabled} in ${r.file}`, "info"); }
15251
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.workflow as typeof s.workflow & Record<string, boolean>)[key] = enabled; }); workflowUiNotify(ctx, `workflow.${key} set to ${enabled} in ${r.file}`, "info"); }
14949
15252
  }
14950
15253
  } else if (choice === "List Current Settings") {
14951
15254
  const s = loadWorkflowSettings(ctx.cwd);
@@ -14961,11 +15264,11 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14961
15264
  if (!setting || setting === "Back") return;
14962
15265
  if (setting === "To Do Trigger Mode") {
14963
15266
  const mode = parseStandardTodoTriggerMode((await ctx.ui.select("To Do Trigger Mode", ["Disabled", "On request", "Automatic when useful", "Required"])) ?? "");
14964
- if (mode) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.standard.todoTriggerMode = mode; s.standard.autoTodoEnabled = mode !== "off"; }); ctx.ui.notify(`To Do Trigger Mode set to ${standardTodoTriggerModeLabel(mode)} in ${r.file}`, "info"); }
15267
+ if (mode) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.standard.todoTriggerMode = mode; s.standard.autoTodoEnabled = mode !== "off"; }); workflowUiNotify(ctx, `To Do Trigger Mode set to ${standardTodoTriggerModeLabel(mode)} in ${r.file}`, "info"); }
14965
15268
  } else {
14966
15269
  const key = setting === "Automatic To Do Enabled" ? "autoTodoEnabled" : "todoProgressVisible";
14967
15270
  const enabled = await chooseBool(ctx, `standard.${key}?`);
14968
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.standard as typeof s.standard & Record<string, boolean>)[key] = enabled; }); ctx.ui.notify(`standard.${key} set to ${enabled} in ${r.file}`, "info"); }
15271
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.standard as typeof s.standard & Record<string, boolean>)[key] = enabled; }); workflowUiNotify(ctx, `standard.${key} set to ${enabled} in ${r.file}`, "info"); }
14969
15272
  }
14970
15273
  }
14971
15274
  }
@@ -14977,20 +15280,20 @@ ${renderMissionStatus(activeMission ?? paused)}`);
14977
15280
  if (!setting || setting === "Back") return;
14978
15281
  if (setting === "Clarification Mode") {
14979
15282
  const mode = parseStandardClarificationMode((await ctx.ui.select("Standard clarification mode", ["auto", "always_for_nontrivial", "never"])) ?? "");
14980
- if (mode) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.standard.clarificationMode = mode; s.standard.clarificationEnabled = mode !== "never"; }); ctx.ui.notify(`standard.clarificationMode set to ${mode} in ${r.file}`, "info"); }
15283
+ if (mode) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.standard.clarificationMode = mode; s.standard.clarificationEnabled = mode !== "never"; }); workflowUiNotify(ctx, `standard.clarificationMode set to ${mode} in ${r.file}`, "info"); }
14981
15284
  } else if (setting === "Interactive Clarification") {
14982
15285
  const enabled = await chooseBool(ctx, "Standard interactive clarification enabled?");
14983
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.standard.interactiveClarificationEnabled = enabled; }); ctx.ui.notify(`standard.interactiveClarificationEnabled set to ${enabled} in ${r.file}`, "info"); }
15286
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.standard.interactiveClarificationEnabled = enabled; }); workflowUiNotify(ctx, `standard.interactiveClarificationEnabled set to ${enabled} in ${r.file}`, "info"); }
14984
15287
  } else if (setting === "Max Clarification Questions") {
14985
15288
  const count = parsePositiveInt((await ctx.ui.select("Max Standard clarification questions", ["0", "1", "2"])) ?? "");
14986
- if (count !== undefined && count >= 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.standard.maxClarificationQuestions = count; }); ctx.ui.notify(`standard.maxClarificationQuestions set to ${count} in ${r.file}`, "info"); }
15289
+ if (count !== undefined && count >= 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.standard.maxClarificationQuestions = count; }); workflowUiNotify(ctx, `standard.maxClarificationQuestions set to ${count} in ${r.file}`, "info"); }
14987
15290
  } else if (setting === "Timing") {
14988
15291
  const timing = parseClarificationTiming((await ctx.ui.select("Standard clarification timing", ["after_initial_analysis", "immediate"])) ?? "");
14989
- if (timing) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.standard.clarificationTiming = timing; }); ctx.ui.notify(`standard.clarificationTiming set to ${timing} in ${r.file}`, "info"); }
15292
+ if (timing) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.standard.clarificationTiming = timing; }); workflowUiNotify(ctx, `standard.clarificationTiming set to ${timing} in ${r.file}`, "info"); }
14990
15293
  } else {
14991
15294
  const key = setting === "Quality Gate" ? "clarificationQualityGate" : setting === "Allow Without Analysis" ? "allowClarificationWithoutAnalysis" : "useSubagentsBeforeClarification";
14992
15295
  const enabled = await chooseBool(ctx, `standard.${key}?`);
14993
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.standard as typeof s.standard & Record<string, boolean | string | number | undefined>)[key] = enabled; }); ctx.ui.notify(`standard.${key} set to ${enabled} in ${r.file}`, "info"); }
15296
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.standard as typeof s.standard & Record<string, boolean | string | number | undefined>)[key] = enabled; }); workflowUiNotify(ctx, `standard.${key} set to ${enabled} in ${r.file}`, "info"); }
14994
15297
  }
14995
15298
  }
14996
15299
  }
@@ -15010,15 +15313,15 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15010
15313
  const choice = await ctx.ui.select("Standard Token Budget", ["Default (unlimited)", "Custom..."]);
15011
15314
  if (choice === "Default (unlimited)") {
15012
15315
  const r = updateSettings(ctx.cwd, undefined, (s) => { s.standard.maxTokens = 0; });
15013
- ctx.ui.notify(`standard.maxTokens set to default (unlimited) in ${r.file}`, "info");
15316
+ workflowUiNotify(ctx, `standard.maxTokens set to default (unlimited) in ${r.file}`, "info");
15014
15317
  } else if (choice === "Custom...") {
15015
15318
  const count = parsePositiveInt((await ctx.ui.input("Enter custom standard token budget", String(current > 0 ? current : ""))) ?? "");
15016
- if (count !== undefined && count >= 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.standard.maxTokens = count; }); ctx.ui.notify(`standard.maxTokens set to ${count === 0 ? "unlimited" : count.toLocaleString()} in ${r.file}`, "info"); }
15319
+ if (count !== undefined && count >= 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.standard.maxTokens = count; }); workflowUiNotify(ctx, `standard.maxTokens set to ${count === 0 ? "unlimited" : count.toLocaleString()} in ${r.file}`, "info"); }
15017
15320
  }
15018
15321
  } else {
15019
15322
  const key = setting === "Status Widget Visible" ? "statusWidgetVisible" : "todoProgressVisible";
15020
15323
  const enabled = await chooseBool(ctx, `standard.${key}?`);
15021
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.standard as typeof s.standard & Record<string, boolean>)[key] = enabled; }); ctx.ui.notify(`standard.${key} set to ${enabled} in ${r.file}`, "info"); }
15324
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.standard as typeof s.standard & Record<string, boolean>)[key] = enabled; }); workflowUiNotify(ctx, `standard.${key} set to ${enabled} in ${r.file}`, "info"); }
15022
15325
  }
15023
15326
  }
15024
15327
  }
@@ -15035,7 +15338,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15035
15338
  }
15036
15339
  if (choice === "Enable / Disable Standard Mode") {
15037
15340
  const enabled = await chooseBool(ctx, "standard.enabled?");
15038
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.standard.enabled = enabled; }); ctx.ui.notify(`standard.enabled set to ${enabled} in ${r.file}`, "info"); }
15341
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.standard.enabled = enabled; }); workflowUiNotify(ctx, `standard.enabled set to ${enabled} in ${r.file}`, "info"); }
15039
15342
  } else if (choice === "Standard To Do" || choice === "To Do Behavior") {
15040
15343
  await showStandardTodoSettingsMenu(ctx);
15041
15344
  } else if (choice === "Standard Clarification" || choice === "Clarification") {
@@ -15064,7 +15367,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15064
15367
  if (!source) continue;
15065
15368
  const enabled = source === "Mission-Specific Models";
15066
15369
  const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.useMissionSpecificModels = enabled; });
15067
- ctx.ui.notify(`missions.useMissionSpecificModels set to ${enabled} in ${r.file}`, "info");
15370
+ workflowUiNotify(ctx, `missions.useMissionSpecificModels set to ${enabled} in ${r.file}`, "info");
15068
15371
  } else if (choice === "Configure Mission Planner") await configureMissionRoleModel(ctx, "planner");
15069
15372
  else if (choice === "Configure Mission Executor") await configureMissionRoleModel(ctx, "executor");
15070
15373
  else if (choice === "Configure Mission Reviewer") await configureMissionRoleModel(ctx, "reviewer");
@@ -15085,13 +15388,13 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15085
15388
  show(pi, renderHeartbeatWatchdogStatus(loadWorkflowSettings(ctx.cwd), activeMission ?? loadMissionState()));
15086
15389
  } else if (choice === "Enable / Disable Heartbeat") {
15087
15390
  const enabled = await chooseBool(ctx, "missions.heartbeatEnabled?");
15088
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.heartbeatEnabled = enabled; }); ctx.ui.notify(`missions.heartbeatEnabled set to ${enabled} in ${r.file}`, "info"); }
15391
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.heartbeatEnabled = enabled; }); workflowUiNotify(ctx, `missions.heartbeatEnabled set to ${enabled} in ${r.file}`, "info"); }
15089
15392
  } else if (choice === "Enable / Disable Watchdog") {
15090
15393
  const enabled = await chooseBool(ctx, "missions.watchdogEnabled? (warning only; no auto-recovery)");
15091
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.watchdogEnabled = enabled; }); ctx.ui.notify(`missions.watchdogEnabled set to ${enabled} in ${r.file}`, "info"); }
15394
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.watchdogEnabled = enabled; }); workflowUiNotify(ctx, `missions.watchdogEnabled set to ${enabled} in ${r.file}`, "info"); }
15092
15395
  } else if (choice === "Set Watchdog Stale Minutes") {
15093
15396
  const count = parsePositiveInt((await ctx.ui.input("watchdogStaleMinutes", String(loadWorkflowSettings(ctx.cwd).missions.watchdogStaleMinutes ?? 30))) ?? "");
15094
- if (count !== undefined && count > 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.watchdogStaleMinutes = count; }); ctx.ui.notify(`missions.watchdogStaleMinutes set to ${count} in ${r.file}`, "info"); }
15397
+ if (count !== undefined && count > 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.watchdogStaleMinutes = count; }); workflowUiNotify(ctx, `missions.watchdogStaleMinutes set to ${count} in ${r.file}`, "info"); }
15095
15398
  }
15096
15399
  }
15097
15400
  }
@@ -15104,11 +15407,11 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15104
15407
  const enabled = await chooseBool(ctx, `${setting}?`);
15105
15408
  if (enabled === undefined) continue;
15106
15409
  if (setting === "Mission Reviewer Enabled") {
15107
- if (loadWorkflowSettings(ctx.cwd).missions.useMissionSpecificModels) { const r = updateSettings(ctx.cwd, undefined, (s) => { setMissionRoleEnabled(s, "reviewer", enabled); }); ctx.ui.notify(`missions.models.reviewer.enabled set to ${enabled} in ${r.file}`, "info"); }
15108
- else { const r = setRoleEnabled("reviewer", enabled, ctx.cwd); ctx.ui.notify(`reviewer.enabled set to ${enabled} in ${r.file}`, "info"); }
15410
+ if (loadWorkflowSettings(ctx.cwd).missions.useMissionSpecificModels) { const r = updateSettings(ctx.cwd, undefined, (s) => { setMissionRoleEnabled(s, "reviewer", enabled); }); workflowUiNotify(ctx, `missions.models.reviewer.enabled set to ${enabled} in ${r.file}`, "info"); }
15411
+ else { const r = setRoleEnabled("reviewer", enabled, ctx.cwd); workflowUiNotify(ctx, `reviewer.enabled set to ${enabled} in ${r.file}`, "info"); }
15109
15412
  } else if (setting === "Mission Validator Enabled") {
15110
- if (loadWorkflowSettings(ctx.cwd).missions.useMissionSpecificModels) { const r = updateSettings(ctx.cwd, undefined, (s) => { setMissionRoleEnabled(s, "validator", enabled); }); ctx.ui.notify(`missions.models.validator.enabled set to ${enabled} in ${r.file}`, "info"); }
15111
- else { const r = setRoleEnabled("validator", enabled, ctx.cwd); ctx.ui.notify(`validator.enabled set to ${enabled} in ${r.file}`, "info"); }
15413
+ if (loadWorkflowSettings(ctx.cwd).missions.useMissionSpecificModels) { const r = updateSettings(ctx.cwd, undefined, (s) => { setMissionRoleEnabled(s, "validator", enabled); }); workflowUiNotify(ctx, `missions.models.validator.enabled set to ${enabled} in ${r.file}`, "info"); }
15414
+ else { const r = setRoleEnabled("validator", enabled, ctx.cwd); workflowUiNotify(ctx, `validator.enabled set to ${enabled} in ${r.file}`, "info"); }
15112
15415
  } else {
15113
15416
  const keyMap: Record<string, string> = {
15114
15417
  "Offer Reviewer Before Approval": "offerReviewerBeforeApprove",
@@ -15126,7 +15429,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15126
15429
  s.missions.reviewRetryMode = "safe_only";
15127
15430
  }
15128
15431
  });
15129
- ctx.ui.notify(`missions.${key} set to ${enabled} in ${r.file}`, "info");
15432
+ workflowUiNotify(ctx, `missions.${key} set to ${enabled} in ${r.file}`, "info");
15130
15433
  }
15131
15434
  }
15132
15435
  }
@@ -15139,18 +15442,18 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15139
15442
  if (setting === "Max Review Retries Per Mission") {
15140
15443
  const raw = await ctx.ui.select(setting, ["0", "1", "2", "3", "4", "5", "10"]);
15141
15444
  const count = raw === undefined ? undefined : Number(raw);
15142
- if (Number.isInteger(count) && count >= 0 && count <= 10) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.maxReviewRetriesPerMission = count; }); ctx.ui.notify(`missions.maxReviewRetriesPerMission set to ${count} in ${r.file}`, "info"); }
15445
+ if (Number.isInteger(count) && count >= 0 && count <= 10) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.maxReviewRetriesPerMission = count; }); workflowUiNotify(ctx, `missions.maxReviewRetriesPerMission set to ${count} in ${r.file}`, "info"); }
15143
15446
  } else if (setting === "Max Retries Per Milestone" || setting === "Max Retries Per Mission" || setting === "Max Final Validation Retries") {
15144
15447
  const key = setting === "Max Retries Per Milestone" ? "maxValidationRetriesPerMilestone" : setting === "Max Retries Per Mission" ? "maxValidationRetriesPerMission" : "maxFinalValidationRetries";
15145
15448
  const max = key === "maxValidationRetriesPerMission" ? 100 : 10;
15146
15449
  const options = key === "maxValidationRetriesPerMission" ? ["0", "1", "2", "4", "8", "12", "20", "50", "100"] : ["0", "1", "2", "3", "4", "5", "10"];
15147
15450
  const raw = await ctx.ui.select(setting, options);
15148
15451
  const count = raw === undefined ? undefined : Number(raw);
15149
- if (Number.isInteger(count) && count >= 0 && count <= max) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.missions as typeof s.missions & Record<string, number>)[key] = count; }); ctx.ui.notify(`missions.${key} set to ${count} in ${r.file}`, "info"); }
15452
+ if (Number.isInteger(count) && count >= 0 && count <= max) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.missions as typeof s.missions & Record<string, number>)[key] = count; }); workflowUiNotify(ctx, `missions.${key} set to ${count} in ${r.file}`, "info"); }
15150
15453
  } else if (setting === "Validation Retry Mode" || setting === "Review Retry Mode") {
15151
15454
  const key = setting === "Validation Retry Mode" ? "validationRetryMode" : "reviewRetryMode";
15152
15455
  const mode = parseValidationRetryMode((await ctx.ui.select(`${key}`, ["off", "safe_only", "aggressive_within_scope"])) ?? "");
15153
- if (mode) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.missions as typeof s.missions & Record<string, string | undefined>)[key] = mode; }); ctx.ui.notify(`missions.${key} set to ${mode} in ${r.file}`, "info"); }
15456
+ if (mode) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.missions as typeof s.missions & Record<string, string | undefined>)[key] = mode; }); workflowUiNotify(ctx, `missions.${key} set to ${mode} in ${r.file}`, "info"); }
15154
15457
  } else {
15155
15458
  const keyMap: Record<string, string> = {
15156
15459
  "Auto Repair Review Failures": "autoRepairReviewFailures",
@@ -15163,7 +15466,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15163
15466
  };
15164
15467
  const key = keyMap[setting];
15165
15468
  const enabled = await chooseBool(ctx, `${String(key)}?`);
15166
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.missions as Record<string, unknown>)[String(key)] = enabled; if (key === "autoRepairReviewFailures" && enabled && s.missions.reviewRetryMode === "off") { s.missions.reviewRetryMode = "safe_only"; } }); ctx.ui.notify(`missions.${String(key)} set to ${enabled} in ${r.file}`, "info"); }
15469
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.missions as Record<string, unknown>)[String(key)] = enabled; if (key === "autoRepairReviewFailures" && enabled && s.missions.reviewRetryMode === "off") { s.missions.reviewRetryMode = "safe_only"; } }); workflowUiNotify(ctx, `missions.${String(key)} set to ${enabled} in ${r.file}`, "info"); }
15167
15470
  }
15168
15471
  }
15169
15472
  }
@@ -15176,12 +15479,12 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15176
15479
  if (setting === "Max Final Validation Retries") {
15177
15480
  const raw = await ctx.ui.select(setting, ["0", "1", "2", "3", "4", "5", "10"]);
15178
15481
  const count = raw === undefined ? undefined : Number(raw);
15179
- if (Number.isInteger(count) && count >= 0 && count <= 10) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.maxFinalValidationRetries = count; }); ctx.ui.notify(`missions.maxFinalValidationRetries set to ${count} in ${r.file}`, "info"); }
15482
+ if (Number.isInteger(count) && count >= 0 && count <= 10) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.maxFinalValidationRetries = count; }); workflowUiNotify(ctx, `missions.maxFinalValidationRetries set to ${count} in ${r.file}`, "info"); }
15180
15483
  } else {
15181
15484
  const keyMap: Record<string, string> = { "Final Comprehensive Validation": "finalValidationEnabled", "Final Validation Requires Pass": "finalValidationRequiresPass", "Auto Repair Final Validation Failures": "autoRepairFinalValidationFailures" };
15182
15485
  const key = keyMap[setting];
15183
15486
  const enabled = await chooseBool(ctx, `${String(key)}?`);
15184
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.missions as Record<string, unknown>)[String(key)] = enabled; }); ctx.ui.notify(`missions.${String(key)} set to ${enabled} in ${r.file}`, "info"); }
15487
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.missions as Record<string, unknown>)[String(key)] = enabled; }); workflowUiNotify(ctx, `missions.${String(key)} set to ${enabled} in ${r.file}`, "info"); }
15185
15488
  }
15186
15489
  }
15187
15490
  }
@@ -15196,12 +15499,12 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15196
15499
  const count = parsePositiveInt((await ctx.ui.input("Mission history limit", String(current))) ?? "");
15197
15500
  if (count !== undefined && count >= 1 && count <= 500) {
15198
15501
  const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.missionHistoryLimit = count; });
15199
- ctx.ui.notify(`missions.missionHistoryLimit set to ${count} in ${r.file}`, "info");
15502
+ workflowUiNotify(ctx, `missions.missionHistoryLimit set to ${count} in ${r.file}`, "info");
15200
15503
  }
15201
15504
  } else if (setting === "Cleanup Saved Missions") {
15202
15505
  const settings = loadWorkflowSettings(ctx.cwd);
15203
15506
  const removed = clearOldMissionStates(settings.missions.missionHistoryLimit ?? 50);
15204
- ctx.ui.notify(`Removed ${removed} old mission file${removed === 1 ? "" : "s"}.`, "info");
15507
+ workflowUiNotify(ctx, `Removed ${removed} old mission file${removed === 1 ? "" : "s"}.`, "info");
15205
15508
  } else if (setting === "List Current Settings") {
15206
15509
  const settings = loadWorkflowSettings(ctx.cwd);
15207
15510
  show(pi, `# Mission History\n\nMission History Limit: ${settings.missions.missionHistoryLimit ?? 50}\nMissions Directory: ${MISSION_HISTORY_DIR}`);
@@ -15218,48 +15521,48 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15218
15521
  await showMissionModelBehaviorMenu(ctx);
15219
15522
  } else if (choice === "Enable / Disable Mission Mode") {
15220
15523
  const enabled = await chooseBool(ctx, "Missions enabled?");
15221
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.enabled = enabled; }); ctx.ui.notify(`missions.enabled set to ${enabled} in ${r.file}`, "info"); }
15524
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.enabled = enabled; }); workflowUiNotify(ctx, `missions.enabled set to ${enabled} in ${r.file}`, "info"); }
15222
15525
  } else if (choice === "Mission Autonomy") {
15223
15526
  const setting = await ctx.ui.select("Mission Autonomy", ["Default Autonomy", "Allow Full Auto", "Auto Run After Approval", "Continue Across Milestones", "Pause Between Milestones", "Auto Resume", "Back"]);
15224
15527
  if (!setting || setting === "Back") continue;
15225
15528
  if (setting === "Default Autonomy") {
15226
15529
  const autonomy = parseMissionAutonomy((await ctx.ui.select("Mission default autonomy", ["manual", "approval_gated", "supervised_auto", "full_auto"])) ?? "");
15227
- if (autonomy) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.defaultAutonomy = autonomy; }); ctx.ui.notify(`missions.defaultAutonomy set to ${autonomy} in ${r.file}${autonomy === "full_auto" && !r.settings.missions.allowFullAuto ? "; full_auto will be blocked until allowFullAuto=true" : ""}`, "info"); }
15530
+ if (autonomy) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.defaultAutonomy = autonomy; }); workflowUiNotify(ctx, `missions.defaultAutonomy set to ${autonomy} in ${r.file}${autonomy === "full_auto" && !r.settings.missions.allowFullAuto ? "; full_auto will be blocked until allowFullAuto=true" : ""}`, "info"); }
15228
15531
  } else {
15229
15532
  const keyMap: Record<string, keyof ReturnType<typeof loadWorkflowSettings>["missions"]> = { "Allow Full Auto": "allowFullAuto", "Auto Run After Approval": "autoRunAfterApproval", "Continue Across Milestones": "continueAcrossMilestones", "Pause Between Milestones": "pauseBetweenMilestones", "Auto Resume": "autoResume" };
15230
15533
  const key = keyMap[setting];
15231
15534
  const enabled = await chooseBool(ctx, `${String(key)}?`);
15232
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.missions as Record<string, unknown>)[String(key)] = enabled; }); ctx.ui.notify(`missions.${String(key)} set to ${enabled} in ${r.file}${key === "allowFullAuto" && enabled === false && r.settings.missions.defaultAutonomy === "full_auto" ? "; full_auto default is retained but blocked" : ""}`, "info"); }
15535
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.missions as Record<string, unknown>)[String(key)] = enabled; }); workflowUiNotify(ctx, `missions.${String(key)} set to ${enabled} in ${r.file}${key === "allowFullAuto" && enabled === false && r.settings.missions.defaultAutonomy === "full_auto" ? "; full_auto default is retained but blocked" : ""}`, "info"); }
15233
15536
  }
15234
15537
  } else if (choice === "Default Autonomy") {
15235
15538
  const autonomy = parseMissionAutonomy((await ctx.ui.select("Mission default autonomy", ["manual", "approval_gated", "supervised_auto", "full_auto"])) ?? "");
15236
- if (autonomy) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.defaultAutonomy = autonomy; }); ctx.ui.notify(`missions.defaultAutonomy set to ${autonomy} in ${r.file}${autonomy === "full_auto" && !r.settings.missions.allowFullAuto ? "; full_auto will be blocked until allowFullAuto=true" : ""}`, "info"); }
15539
+ if (autonomy) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.defaultAutonomy = autonomy; }); workflowUiNotify(ctx, `missions.defaultAutonomy set to ${autonomy} in ${r.file}${autonomy === "full_auto" && !r.settings.missions.allowFullAuto ? "; full_auto will be blocked until allowFullAuto=true" : ""}`, "info"); }
15237
15540
  } else if (choice === "Mission Clarification") {
15238
15541
  const setting = await ctx.ui.select("Mission Clarification", ["Clarification Mode", "Interactive Clarification", "Max Clarification Questions", "Timing", "Quality Gate", "Allow Without Analysis", "Use Sub-agents Before Clarification", "Back"]);
15239
15542
  if (!setting || setting === "Back") continue;
15240
15543
  if (setting === "Clarification Mode") {
15241
15544
  const mode = parseClarificationMode((await ctx.ui.select("Mission clarification mode", ["auto", "always_for_nontrivial", "never"])) ?? "");
15242
- if (mode) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.clarificationMode = mode; }); ctx.ui.notify(`missions.clarificationMode set to ${mode} in ${r.file}`, "info"); }
15545
+ if (mode) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.clarificationMode = mode; }); workflowUiNotify(ctx, `missions.clarificationMode set to ${mode} in ${r.file}`, "info"); }
15243
15546
  } else if (setting === "Interactive Clarification") {
15244
15547
  const enabled = await chooseBool(ctx, "Mission interactive clarification enabled?");
15245
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.interactiveClarificationEnabled = enabled; }); ctx.ui.notify(`missions.interactiveClarificationEnabled set to ${enabled} in ${r.file}`, "info"); }
15548
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.interactiveClarificationEnabled = enabled; }); workflowUiNotify(ctx, `missions.interactiveClarificationEnabled set to ${enabled} in ${r.file}`, "info"); }
15246
15549
  } else if (setting === "Max Clarification Questions") {
15247
15550
  const count = parsePositiveInt((await ctx.ui.select("Mission max clarification questions", ["1", "2", "3", "4", "5", "6"])) ?? "");
15248
- if (count) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.maxClarificationQuestions = count; }); ctx.ui.notify(`missions.maxClarificationQuestions set to ${count} in ${r.file}`, "info"); }
15551
+ if (count) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.maxClarificationQuestions = count; }); workflowUiNotify(ctx, `missions.maxClarificationQuestions set to ${count} in ${r.file}`, "info"); }
15249
15552
  } else if (setting === "Timing") {
15250
15553
  const timing = parseClarificationTiming((await ctx.ui.select("Mission clarification timing", ["after_initial_analysis", "immediate"])) ?? "");
15251
- if (timing) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.clarificationTiming = timing; }); ctx.ui.notify(`missions.clarificationTiming set to ${timing} in ${r.file}`, "info"); }
15554
+ if (timing) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.clarificationTiming = timing; }); workflowUiNotify(ctx, `missions.clarificationTiming set to ${timing} in ${r.file}`, "info"); }
15252
15555
  } else {
15253
15556
  const key = setting === "Quality Gate" ? "clarificationQualityGate" : setting === "Allow Without Analysis" ? "allowClarificationWithoutAnalysis" : "useSubagentsBeforeClarification";
15254
15557
  const enabled = await chooseBool(ctx, `missions.${key}?`);
15255
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.missions as typeof s.missions & Record<string, boolean | string | number | undefined>)[key] = enabled; }); ctx.ui.notify(`missions.${key} set to ${enabled} in ${r.file}`, "info"); }
15558
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.missions as typeof s.missions & Record<string, boolean | string | number | undefined>)[key] = enabled; }); workflowUiNotify(ctx, `missions.${key} set to ${enabled} in ${r.file}`, "info"); }
15256
15559
  }
15257
15560
  } else if (choice === "Mission Planning Depth") {
15258
15561
  const depth = parsePlanningDepth((await ctx.ui.select("Mission planning depth", ["fast", "standard", "deep", "maximum"])) ?? "");
15259
- if (depth) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.planningDepth = depth; }); ctx.ui.notify(`missions.planningDepth set to ${depth} in ${r.file}`, "info"); }
15562
+ if (depth) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.planningDepth = depth; }); workflowUiNotify(ctx, `missions.planningDepth set to ${depth} in ${r.file}`, "info"); }
15260
15563
  } else if (choice === "Mission Sub-Agent Policy") {
15261
15564
  const policy = parseSubagentPolicy((await ctx.ui.select("Mission sub-agent policy", ["off", "auto", "deep", "maximum", "forced"])) ?? "");
15262
- if (policy) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.subagentPolicy = policy; }); ctx.ui.notify(`missions.subagentPolicy set to ${policy} in ${r.file}`, "info"); }
15565
+ if (policy) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.subagentPolicy = policy; }); workflowUiNotify(ctx, `missions.subagentPolicy set to ${policy} in ${r.file}`, "info"); }
15263
15566
  } else if (choice === "Mission Sub-agents / Workers" || choice === "Mission Worker Targets") {
15264
15567
  await showMissionSubagentWorkerSettingsMenu(ctx);
15265
15568
  } else if (choice === "Mission History") {
@@ -15275,50 +15578,50 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15275
15578
  if (setting === "Progress Widget" || setting === "Progress Bar Display") {
15276
15579
  const key = setting === "Progress Widget" ? "progressWidgetEnabled" : "showProgressBar";
15277
15580
  const enabled = await chooseBool(ctx, `missions.${key}?`);
15278
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.missions as typeof s.missions & Record<string, boolean>)[key] = enabled; }); ctx.ui.notify(`missions.${key} set to ${enabled} in ${r.file}`, "info"); }
15581
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.missions as typeof s.missions & Record<string, boolean>)[key] = enabled; }); workflowUiNotify(ctx, `missions.${key} set to ${enabled} in ${r.file}`, "info"); }
15279
15582
  } else if (setting === "Heartbeat / Watchdog") await showHeartbeatWatchdogMenu(ctx);
15280
15583
  else if (setting === "Token Budget") {
15281
15584
  const current = loadWorkflowSettings(ctx.cwd).missions.maxTokens ?? 0;
15282
15585
  const choice = await ctx.ui.select("Mission Token Budget", ["Default (unlimited)", "Custom..."]);
15283
15586
  if (choice === "Default (unlimited)") {
15284
15587
  const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.maxTokens = 0; });
15285
- ctx.ui.notify(`missions.maxTokens set to default (unlimited) in ${r.file}`, "info");
15588
+ workflowUiNotify(ctx, `missions.maxTokens set to default (unlimited) in ${r.file}`, "info");
15286
15589
  } else if (choice === "Custom...") {
15287
15590
  const count = parsePositiveInt((await ctx.ui.input("Enter custom mission token budget", String(current > 0 ? current : ""))) ?? "");
15288
- if (count !== undefined && count >= 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.maxTokens = count; }); ctx.ui.notify(`missions.maxTokens set to ${count === 0 ? "unlimited" : count.toLocaleString()} in ${r.file}`, "info"); }
15591
+ if (count !== undefined && count >= 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.maxTokens = count; }); workflowUiNotify(ctx, `missions.maxTokens set to ${count === 0 ? "unlimited" : count.toLocaleString()} in ${r.file}`, "info"); }
15289
15592
  }
15290
15593
  } else if (setting === "Runtime Budget") {
15291
15594
  const current = loadWorkflowSettings(ctx.cwd).missions.maxRuntimeHours;
15292
15595
  const choice = await ctx.ui.select("Mission Runtime Budget", ["Default (13 hours)", "Custom..."]);
15293
15596
  if (choice === "Default (13 hours)") {
15294
15597
  const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.maxRuntimeHours = 13; });
15295
- ctx.ui.notify(`missions.maxRuntimeHours set to default (13 hours) in ${r.file}`, "info");
15598
+ workflowUiNotify(ctx, `missions.maxRuntimeHours set to default (13 hours) in ${r.file}`, "info");
15296
15599
  } else if (choice === "Custom...") {
15297
15600
  const count = parsePositiveInt((await ctx.ui.input("Enter custom runtime budget (hours)", String(current !== 13 ? current : ""))) ?? "");
15298
- if (count !== undefined && count > 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.maxRuntimeHours = count; }); ctx.ui.notify(`missions.maxRuntimeHours set to ${count} hours in ${r.file}`, "info"); }
15601
+ if (count !== undefined && count > 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.maxRuntimeHours = count; }); workflowUiNotify(ctx, `missions.maxRuntimeHours set to ${count} hours in ${r.file}`, "info"); }
15299
15602
  }
15300
15603
  } else {
15301
15604
  const count = parsePositiveInt((await ctx.ui.input("checkpointIntervalMinutes", "30")) ?? "");
15302
- if (count !== undefined && count > 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.checkpointIntervalMinutes = count; }); ctx.ui.notify(`missions.checkpointIntervalMinutes set to ${count} in ${r.file}`, "info"); }
15605
+ if (count !== undefined && count > 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.checkpointIntervalMinutes = count; }); workflowUiNotify(ctx, `missions.checkpointIntervalMinutes set to ${count} in ${r.file}`, "info"); }
15303
15606
  }
15304
15607
  } else if (choice === "Runtime Budget") {
15305
15608
  const current = loadWorkflowSettings(ctx.cwd).missions.maxRuntimeHours;
15306
15609
  const choice2 = await ctx.ui.select("Mission Runtime Budget", ["Default (13 hours)", "Custom..."]);
15307
15610
  if (choice2 === "Default (13 hours)") {
15308
15611
  const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.maxRuntimeHours = 13; });
15309
- ctx.ui.notify(`missions.maxRuntimeHours set to default (13 hours) in ${r.file}`, "info");
15612
+ workflowUiNotify(ctx, `missions.maxRuntimeHours set to default (13 hours) in ${r.file}`, "info");
15310
15613
  } else if (choice2 === "Custom...") {
15311
15614
  const count = parsePositiveInt((await ctx.ui.input("Enter custom runtime budget (hours)", String(current !== 13 ? current : ""))) ?? "");
15312
- if (count !== undefined && count > 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.maxRuntimeHours = count; }); ctx.ui.notify(`missions.maxRuntimeHours set to ${count} hours in ${r.file}`, "info"); }
15615
+ if (count !== undefined && count > 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.maxRuntimeHours = count; }); workflowUiNotify(ctx, `missions.maxRuntimeHours set to ${count} hours in ${r.file}`, "info"); }
15313
15616
  }
15314
15617
  } else if (choice === "Checkpoint Interval") {
15315
15618
  const count = parsePositiveInt((await ctx.ui.input("checkpointIntervalMinutes", "30")) ?? "");
15316
- if (count !== undefined && count > 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.checkpointIntervalMinutes = count; }); ctx.ui.notify(`missions.checkpointIntervalMinutes set to ${count} in ${r.file}`, "info"); }
15619
+ if (count !== undefined && count > 0) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.missions.checkpointIntervalMinutes = count; }); workflowUiNotify(ctx, `missions.checkpointIntervalMinutes set to ${count} in ${r.file}`, "info"); }
15317
15620
  } else if (choice === "Approval Safety" || choice === "Validation Per Milestone" || choice === "Full Auto Safety" || choice === "Auto Resume") {
15318
15621
  const keyMap: Record<string, keyof ReturnType<typeof loadWorkflowSettings>["missions"]> = { "Validation Per Milestone": "requireValidationPerMilestone", "Approval Safety": "requireApprovalForDestructiveActions", "Full Auto Safety": "allowFullAuto", "Auto Resume": "autoResume" };
15319
15622
  const key = keyMap[choice];
15320
15623
  const enabled = await chooseBool(ctx, `${String(key)}?`);
15321
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.missions as Record<string, unknown>)[String(key)] = enabled; }); ctx.ui.notify(`missions.${String(key)} set to ${enabled} in ${r.file}${key === "allowFullAuto" && enabled === false && r.settings.missions.defaultAutonomy === "full_auto" ? "; full_auto default is retained but blocked" : ""}`, "info"); }
15624
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { (s.missions as Record<string, unknown>)[String(key)] = enabled; }); workflowUiNotify(ctx, `missions.${String(key)} set to ${enabled} in ${r.file}${key === "allowFullAuto" && enabled === false && r.settings.missions.defaultAutonomy === "full_auto" ? "; full_auto default is retained but blocked" : ""}`, "info"); }
15322
15625
  } else if (choice === "Review / Validation Behavior") {
15323
15626
  await showMissionReviewValidationBehaviorMenu(ctx);
15324
15627
  } else if (choice === "Repair / Validation Retry" || choice === "Mission Repair / Validation" || choice === "Repair / Validation Retry Settings") {
@@ -15351,18 +15654,18 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15351
15654
  const enabled = await chooseBool(ctx, "safety.repoLockEnabled for this project only?");
15352
15655
  if (enabled !== undefined) {
15353
15656
  const r = updateSettings(ctx.cwd, "project", (s) => { s.safety.repoLockEnabled = enabled; });
15354
- ctx.ui.notify(`Project Repo Lock set to ${enabled} in ${r.file}. This project override wins over global settings and does not apply to other repos.`, "info");
15657
+ workflowUiNotify(ctx, `Project Repo Lock set to ${enabled} in ${r.file}. This project override wins over global settings and does not apply to other repos.`, "info");
15355
15658
  }
15356
15659
  } else {
15357
15660
  const key = keyMap[choice];
15358
15661
  const enabled = await chooseBool(ctx, `${String(key)}?`);
15359
- if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.safety[key] = enabled; }); ctx.ui.notify(`safety.${String(key)} set to ${enabled} in ${r.file}`, "info"); }
15662
+ if (enabled !== undefined) { const r = updateSettings(ctx.cwd, undefined, (s) => { s.safety[key] = enabled; }); workflowUiNotify(ctx, `safety.${String(key)} set to ${enabled} in ${r.file}`, "info"); }
15360
15663
  }
15361
15664
  }
15362
15665
  }
15363
15666
 
15364
- async function showFooterHintsSettingsMenu(ctx: ExtensionContext) {
15365
- if (!ctx.hasUI) return show(pi, renderWorkflowWidgetsStatus(ctx));
15667
+ async function showEditorHintsSettingsMenu(ctx: ExtensionContext) {
15668
+ if (!ctx.hasUI) return show(pi, renderEditorHintsSettings(ctx));
15366
15669
  const options = [
15367
15670
  { label: "Idle Entry Hint", key: "showIdleWorkflowEntryHint" as const },
15368
15671
  { label: "Active Switch Hint", key: "showActiveWorkflowSwitchHint" as const },
@@ -15373,17 +15676,31 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15373
15676
  const settings = loadWorkflowSettings(ctx.cwd);
15374
15677
  const ui = workflowWidgetUi(settings);
15375
15678
  const labels = options.map((option) => `${option.label}: ${ui[option.key] !== false ? "visible" : "hidden"}`);
15376
- const choice = await ctx.ui.select("Footer Hints", [...labels, "List Current Settings", "Back"]);
15679
+ const contrastLabel = `Hint Contrast: ${workflowEditorHintContrastLabel(workflowEditorHintContrast(settings))}`;
15680
+ const choice = await ctx.ui.select("Editor Hints", [...labels, contrastLabel, "List Current Settings", "Back"]);
15377
15681
  if (!choice || choice === "Back") return;
15378
- if (choice === "List Current Settings") { show(pi, renderWorkflowWidgetsStatus(ctx)); continue; }
15682
+ if (choice === "List Current Settings") { show(pi, renderEditorHintsSettings(ctx)); continue; }
15683
+ if (choice === contrastLabel) {
15684
+ const current = workflowEditorHintContrast(settings);
15685
+ const contrastChoice = await ctx.ui.select("Editor Hint Contrast", WORKFLOW_EDITOR_HINT_CONTRASTS.map((contrast) => `${workflowEditorHintContrastLabel(contrast)}${contrast === current ? " (current)" : ""}`).concat("Back"));
15686
+ if (!contrastChoice || contrastChoice === "Back") continue;
15687
+ const contrast = parseEditorHintContrast(contrastChoice.replace(/\s+\(current\)$/, ""));
15688
+ if (contrast) {
15689
+ updateSettings(ctx.cwd, "global", (s) => { workflowWidgetUi(s).editorHintContrast = contrast; });
15690
+ setWorkflowUi(ctx, state, activeSubagents);
15691
+ workflowEditorHintText = workflowEditorHintTextFor(state, loadWorkflowSettings(ctx.cwd));
15692
+ workflowUiNotify(ctx, `Editor hint contrast set to ${workflowEditorHintContrastLabel(contrast)}.`, "info");
15693
+ }
15694
+ continue;
15695
+ }
15379
15696
  const index = labels.indexOf(choice);
15380
15697
  const option = options[index];
15381
15698
  if (!option) continue;
15382
15699
  const enabled = await chooseBool(ctx, `ui.${option.key}?`);
15383
15700
  if (enabled !== undefined) {
15384
- const r = updateSettings(ctx.cwd, "global", (s) => { workflowWidgetUi(s)[option.key] = enabled; });
15701
+ updateSettings(ctx.cwd, "global", (s) => { workflowWidgetUi(s)[option.key] = enabled; });
15385
15702
  setWorkflowUi(ctx, state, activeSubagents);
15386
- ctx.ui.notify(`ui.${option.key} set to ${enabled} in ${r.file}`, "info");
15703
+ workflowUiNotify(ctx, `${option.label} ${enabled ? "shown" : "hidden"}.`, "info");
15387
15704
  }
15388
15705
  }
15389
15706
  }
@@ -15391,22 +15708,22 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15391
15708
  async function showUiWidgetsSettingsMenu(ctx: ExtensionContext) {
15392
15709
  if (!ctx.hasUI) return show(pi, renderWorkflowWidgetsStatus(ctx));
15393
15710
  while (ctx.hasUI) {
15394
- const choice = await ctx.ui.select("UI Widgets", ["Plan Top Widget", "Plan Bottom Widget", "Mission Top Widget", "Mission Bottom Widget", "Enable Widget Shortcuts", "Remember Widget Visibility", "Footer Hints", "List Current Settings", "Back"]);
15711
+ const choice = await ctx.ui.select("UI Widgets", ["Plan Top Widget", "Plan Bottom Widget", "Mission Top Widget", "Mission Bottom Widget", "Enable Widget Shortcuts", "Remember Widget Visibility", "Editor Hints", "List Current Settings", "Back"]);
15395
15712
  if (!choice || choice === "Back") return;
15396
15713
  if (choice === "List Current Settings") show(pi, renderWorkflowWidgetsStatus(ctx));
15397
- else if (choice === "Footer Hints") await showFooterHintsSettingsMenu(ctx);
15714
+ else if (choice === "Editor Hints") await showEditorHintsSettingsMenu(ctx);
15398
15715
  else if (choice === "Enable Widget Shortcuts" || choice === "Remember Widget Visibility") {
15399
15716
  const key = choice === "Enable Widget Shortcuts" ? "enableWidgetShortcuts" : "rememberWidgetVisibility";
15400
15717
  const enabled = await chooseBool(ctx, `ui.${key}?`);
15401
15718
  if (enabled !== undefined) {
15402
- const r = updateSettings(ctx.cwd, "global", (s) => { workflowWidgetUi(s)[key] = enabled; });
15719
+ updateSettings(ctx.cwd, "global", (s) => { workflowWidgetUi(s)[key] = enabled; });
15403
15720
  setWorkflowUi(ctx, state, activeSubagents);
15404
- ctx.ui.notify(`ui.${key} set to ${enabled} in ${r.file}`, "info");
15721
+ workflowUiNotify(ctx, `${choice} ${enabled ? "enabled" : "disabled"}.`, "info");
15405
15722
  }
15406
15723
  } else {
15407
15724
  const slot: WorkflowWidgetSlot = choice === "Plan Top Widget" ? "planTop" : choice === "Plan Bottom Widget" ? "planBottom" : choice === "Mission Top Widget" ? "missionTop" : "missionBottom";
15408
15725
  const visible = await chooseBool(ctx, `${widgetSlotLabel(slot)} visible?`);
15409
- if (visible !== undefined) ctx.ui.notify(setWorkflowWidgetVisibility(ctx, slot, visible), "info");
15726
+ if (visible !== undefined) workflowUiNotify(ctx, setWorkflowWidgetVisibility(ctx, slot, visible), "info");
15410
15727
  }
15411
15728
  }
15412
15729
  }
@@ -15458,14 +15775,14 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15458
15775
  const result = applyWorkflowPreset(ctx.cwd, undefined, resolved);
15459
15776
  setWorkflowUi(ctx, state, activeSubagents);
15460
15777
  const label = workflowPresetLabel(resolved, workflowPresetCatalog(result.settings)[resolved]);
15461
- if (notifyOnly) ctx.ui.notify(`Workflow preset active: ${label}`, "info");
15778
+ if (notifyOnly) workflowUiNotify(ctx, `Workflow preset active: ${label}`, "info");
15462
15779
  else show(pi, presetActionMessage("Workflow Preset Applied", label, result.file, result.scope));
15463
15780
  }
15464
15781
 
15465
15782
  async function markWorkflowPresetCustom(ctx: ExtensionContext): Promise<void> {
15466
15783
  const result = updateSettings(ctx.cwd, undefined, (s) => { s.presets = { ...(s.presets ?? {}), activePreset: WORKFLOW_CUSTOM_PRESET_MARKER, items: { ...(s.presets?.items ?? {}) } }; });
15467
15784
  setWorkflowUi(ctx, state, activeSubagents);
15468
- ctx.ui.notify(`Workflow preset marker cleared in ${result.file}; workflow settings unchanged.`, "info");
15785
+ workflowUiNotify(ctx, `Workflow preset marker cleared in ${result.file}; workflow settings unchanged.`, "info");
15469
15786
  }
15470
15787
 
15471
15788
  async function cycleWorkflowPreset(ctx: ExtensionContext, direction: 1 | -1): Promise<void> {
@@ -15533,7 +15850,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15533
15850
  if (policy) updateSettings(ctx.cwd, undefined, (s) => { const item = (s.presets!.items![name] ??= { planning: {}, workflow: {}, missions: {}, subagents: {} }); item.subagents = { ...(item.subagents ?? {}), planningPolicy: policy, executionPolicy: policy, repairPolicy: policy, reviewPolicy: policy, validationPolicy: policy }; });
15534
15851
  }
15535
15852
  setWorkflowUi(ctx, state, activeSubagents);
15536
- ctx.ui.notify(`Preset ${name} updated in ${r.file}`, "info");
15853
+ workflowUiNotify(ctx, `Preset ${name} updated in ${r.file}`, "info");
15537
15854
  } catch (e) { show(pi, `# Workflow Preset Error\n\n${e instanceof Error ? e.message : String(e)}`); }
15538
15855
  }
15539
15856
  }
@@ -15628,7 +15945,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15628
15945
  async function showCustomBrandStartupVisualMenu(ctx: ExtensionContext): Promise<boolean | undefined> {
15629
15946
  if (!ctx.hasUI) return undefined;
15630
15947
  const saved = updateSettings(ctx.cwd, undefined, (s) => { s.ui.startupVisual = "custom_brand"; });
15631
- ctx.ui.notify(`ui.startupVisual set to custom_brand in ${saved.file}`, "info");
15948
+ workflowUiNotify(ctx, `ui.startupVisual set to custom_brand in ${saved.file}`, "info");
15632
15949
  while (ctx.hasUI) {
15633
15950
  const choice = await ctx.ui.select("Custom Brand Startup Visual", ["Set Text", "Choose Base Template", "Preview", "List Theme Settings", "Back"]);
15634
15951
  if (!choice || choice === "Back") return undefined;
@@ -15637,13 +15954,13 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15637
15954
  } else if (choice === "Set Text") {
15638
15955
  const text = sanitizeCustomBrandText(await ctx.ui.input("Custom brand text", sanitizeCustomBrandText(loadWorkflowSettings(ctx.cwd).ui.customBrandText) || "WORKFLOW SUITE"));
15639
15956
  const r = updateSettings(ctx.cwd, undefined, (s) => { s.ui.customBrandEnabled = true; s.ui.customBrandText = text; s.ui.startupVisual = "custom_brand"; });
15640
- ctx.ui.notify(`ui.customBrandText set in ${r.file}`, "info");
15957
+ workflowUiNotify(ctx, `ui.customBrandText set in ${r.file}`, "info");
15641
15958
  void showStartupVisual(ctx, { intent: "manual" });
15642
15959
  } else if (choice === "Choose Base Template") {
15643
15960
  const base = parseCustomBrandBaseVisual((await ctx.ui.select("Custom brand base template", WORKFLOW_CUSTOM_BRAND_BASE_VISUALS)) ?? "");
15644
15961
  if (base) {
15645
15962
  const r = updateSettings(ctx.cwd, undefined, (s) => { s.ui.customBrandBaseVisual = base; s.ui.startupVisual = "custom_brand"; });
15646
- ctx.ui.notify(`ui.customBrandBaseVisual set to ${base} in ${r.file}`, "info");
15963
+ workflowUiNotify(ctx, `ui.customBrandBaseVisual set to ${base} in ${r.file}`, "info");
15647
15964
  void showStartupVisual(ctx, { intent: "manual" });
15648
15965
  }
15649
15966
  } else if (choice === "Preview") {
@@ -15665,27 +15982,27 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15665
15982
  const text = startupLogoText(loadWorkflowSettings(ctx.cwd)) || "WFS";
15666
15983
  const value = sanitizeCustomBrandText(await ctx.ui.input("Custom logo letters (max 6 A-Z/0-9)", text)).toUpperCase().replace(/[^A-Z0-9]/g, "").slice(0, 6);
15667
15984
  const r = updateSettings(ctx.cwd, undefined, (s) => { s.ui.startupLogo = "custom"; s.ui.startupLogoText = value; });
15668
- ctx.ui.notify(`ui.startupLogoText set to ${value || "(empty)"} in ${r.file}`, "info");
15985
+ workflowUiNotify(ctx, `ui.startupLogoText set to ${value || "(empty)"} in ${r.file}`, "info");
15669
15986
  void showStartupVisual(ctx, { intent: "manual" });
15670
15987
  } else if (choice === "Choose Font") {
15671
15988
  const font = parseStartupLogoFont((await ctx.ui.select("Custom logo font", WORKFLOW_STARTUP_LOGO_FONTS)) ?? "");
15672
15989
  if (font) {
15673
15990
  const r = updateSettings(ctx.cwd, undefined, (s) => { s.ui.startupLogo = "custom"; s.ui.startupLogoFont = font; });
15674
- ctx.ui.notify(`ui.startupLogoFont set to ${font} in ${r.file}`, "info");
15991
+ workflowUiNotify(ctx, `ui.startupLogoFont set to ${font} in ${r.file}`, "info");
15675
15992
  void showStartupVisual(ctx, { intent: "manual" });
15676
15993
  }
15677
15994
  } else if (choice === "Choose Shadow Direction") {
15678
15995
  const direction = parseStartupLogoShadowDirection((await ctx.ui.select("Custom logo shadow direction", WORKFLOW_STARTUP_LOGO_SHADOW_DIRECTIONS)) ?? "");
15679
15996
  if (direction) {
15680
15997
  const r = updateSettings(ctx.cwd, undefined, (s) => { s.ui.startupLogo = "custom"; s.ui.startupLogoShadowDirection = direction; });
15681
- ctx.ui.notify(`ui.startupLogoShadowDirection set to ${direction} in ${r.file}`, "info");
15998
+ workflowUiNotify(ctx, `ui.startupLogoShadowDirection set to ${direction} in ${r.file}`, "info");
15682
15999
  void showStartupVisual(ctx, { intent: "manual" });
15683
16000
  }
15684
16001
  } else if (choice === "Choose Color Style") {
15685
16002
  const colorStyle = parseStartupLogoColorStyle((await ctx.ui.select("Custom logo color style", WORKFLOW_STARTUP_LOGO_COLOR_STYLES)) ?? "");
15686
16003
  if (colorStyle) {
15687
16004
  const r = updateSettings(ctx.cwd, undefined, (s) => { s.ui.startupLogo = "custom"; s.ui.startupLogoColorStyle = colorStyle; });
15688
- ctx.ui.notify(`ui.startupLogoColorStyle set to ${colorStyle} in ${r.file}`, "info");
16005
+ workflowUiNotify(ctx, `ui.startupLogoColorStyle set to ${colorStyle} in ${r.file}`, "info");
15689
16006
  void showStartupVisual(ctx, { intent: "manual" });
15690
16007
  }
15691
16008
  } else if (choice === "Preview") {
@@ -15710,7 +16027,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15710
16027
  } else {
15711
16028
  const r = updateSettings(ctx.cwd, undefined, (s) => { s.ui.startupVisual = visual; });
15712
16029
  const canPreview = startupVisualOrDefault(r.settings) !== "none";
15713
- ctx.ui.notify(canPreview ? `Startup visual set to ${visual} in ${r.file}` : `Startup visual set to ${visual} (disabled) in ${r.file}`, "info");
16030
+ workflowUiNotify(ctx, canPreview ? `Startup visual set to ${visual} in ${r.file}` : `Startup visual set to ${visual} (disabled) in ${r.file}`, "info");
15714
16031
  if (canPreview) void showStartupVisual(ctx, { intent: "manual" });
15715
16032
  }
15716
16033
  }
@@ -15718,14 +16035,14 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15718
16035
  const name = parseWidgetTextPreset((await ctx.ui.select("Startup text style", (WORKFLOW_WIDGET_TEXT_PRESETS as readonly string[]).map((p) => WORKFLOW_WIDGET_TEXT_PRESET_LABELS[p]))) ?? "");
15719
16036
  if (name) {
15720
16037
  const r = updateSettings(ctx.cwd, undefined, (s) => { s.ui.startupTextStyle = name; });
15721
- ctx.ui.notify(`Startup text style set to ${WORKFLOW_WIDGET_TEXT_PRESET_LABELS[name]} in ${r.file}.`, "info");
16038
+ workflowUiNotify(ctx, `Startup text style set to ${WORKFLOW_WIDGET_TEXT_PRESET_LABELS[name]} in ${r.file}.`, "info");
15722
16039
  void showStartupVisual(ctx, { intent: "manual" });
15723
16040
  }
15724
16041
  } else if (choice === "Turn On" || choice === "Turn Off") {
15725
16042
  const visual: WorkflowStartupVisual = choice === "Turn On" ? "mission_control" : "none";
15726
16043
  const r = updateSettings(ctx.cwd, undefined, (s) => { s.ui.startupVisual = visual; });
15727
16044
  const canPreview = startupVisualOrDefault(r.settings) !== "none";
15728
- ctx.ui.notify(canPreview ? `Startup visual turned on (${visual}) in ${r.file}` : `Startup visual turned off in ${r.file}`, "info");
16045
+ workflowUiNotify(ctx, canPreview ? `Startup visual turned on (${visual}) in ${r.file}` : `Startup visual turned off in ${r.file}`, "info");
15729
16046
  if (canPreview) void showStartupVisual(ctx, { intent: "manual" });
15730
16047
  } else if (choice === "On Session Start") {
15731
16048
  const enabled = await chooseBool(ctx, "Show startup visual on session start?");
@@ -15734,12 +16051,12 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15734
16051
  s.ui.startupVisualOnSessionStart = enabled;
15735
16052
  if (enabled === true && (parseStartupVisual(s.ui.startupVisual) ?? "none") === "none") s.ui.startupVisual = "minimal";
15736
16053
  });
15737
- ctx.ui.notify(`Startup on session start ${enabled ? "enabled" : "disabled"} in ${r.file}`, "info");
16054
+ workflowUiNotify(ctx, `Startup on session start ${enabled ? "enabled" : "disabled"} in ${r.file}`, "info");
15738
16055
  void showStartupVisual(ctx, { intent: "manual" });
15739
16056
  }
15740
16057
  } else if (choice === "Preview") {
15741
16058
  const previewed = await showStartupVisual(ctx, { intent: "manual" });
15742
- if (!previewed) ctx.ui.notify("Startup visual is disabled. Choose a visual or turn it on to preview.", "info");
16059
+ if (!previewed) workflowUiNotify(ctx, "Startup visual is disabled. Choose a visual or turn it on to preview.", "info");
15743
16060
  }
15744
16061
  }
15745
16062
  return undefined;
@@ -15768,7 +16085,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15768
16085
  const r = updateSettings(ctx.cwd, undefined, (s) => { s.ui.workflowTheme = name; });
15769
16086
  applyWorkflowEditorTheme(ctx);
15770
16087
  setWorkflowUi(ctx, state, activeSubagents);
15771
- ctx.ui.notify(`ui.workflowTheme set to ${workflowThemeDisplayName(name)} in ${r.file}`, "info");
16088
+ workflowUiNotify(ctx, `ui.workflowTheme set to ${workflowThemeDisplayName(name)} in ${r.file}`, "info");
15772
16089
  void showStartupVisual(ctx, { intent: "manual" });
15773
16090
  }
15774
16091
  } else if (choice === "Startup Visual") {
@@ -15777,7 +16094,7 @@ ${renderMissionStatus(activeMission ?? paused)}`);
15777
16094
  const logo = parseStartupLogo((await ctx.ui.select("Startup logo", WORKFLOW_STARTUP_LOGOS)) ?? "");
15778
16095
  if (logo) {
15779
16096
  const r = updateSettings(ctx.cwd, undefined, (s) => { s.ui.startupLogo = logo; });
15780
- ctx.ui.notify(`ui.startupLogo set to ${logo} in ${r.file}`, "info");
16097
+ workflowUiNotify(ctx, `ui.startupLogo set to ${logo} in ${r.file}`, "info");
15781
16098
  if (logo === "custom") await showCustomStartupLogoMenu(ctx);
15782
16099
  else void showStartupVisual(ctx, { intent: "manual" });
15783
16100
  }
@@ -15868,10 +16185,9 @@ Pi Version: v${VERSION}
15868
16185
 
15869
16186
  function workflowAutoCompactionDecision(ctx: ExtensionContext, options: { allowPendingMessages?: boolean } = {}): { ok: true; percent: number; trigger: number; cooldownMinutes: number } | { ok: false; reason: string } {
15870
16187
  const settings = loadWorkflowSettings(ctx.cwd);
15871
- const customModelTrigger = customModelCompactionConfigured(settings);
15872
16188
  if (!workflowProactiveCompactionEffective(settings)) return { ok: false, reason: "workflow proactive compaction disabled" };
15873
16189
  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` };
16190
+ if (!workflowAutoCompactionModeEligible(state.mode)) return { ok: false, reason: `workflow mode ${state.mode} is active` };
15875
16191
  if (!options.allowPendingMessages) {
15876
16192
  if (workflowScheduledAgentTurns > 0) return { ok: false, reason: "pending workflow handoff queued" };
15877
16193
  try { if (ctx.hasPendingMessages()) return { ok: false, reason: "pending messages queued" }; } catch { /* pending state unavailable */ }
@@ -15880,6 +16196,7 @@ Pi Version: v${VERSION}
15880
16196
  const percent = Number(usage?.percent);
15881
16197
  if (!Number.isFinite(percent)) return { ok: false, reason: "context usage unavailable" };
15882
16198
  const trigger = effectiveWorkflowCompactionTriggerPercent(settings, ctx, usage ?? {});
16199
+ if (!Number.isFinite(trigger)) return { ok: false, reason: "compaction trigger unavailable" };
15883
16200
  if (percent < trigger) return { ok: false, reason: `context ${percent.toFixed(1)}% below trigger ${trigger}%` };
15884
16201
  const cooldownMinutes = compactionCooldownMinutes(settings);
15885
16202
  const cooldownMs = cooldownMinutes * 60 * 1000;
@@ -15937,20 +16254,20 @@ Pi Version: v${VERSION}
15937
16254
  workflowLastAutoCompactionAt = Date.now();
15938
16255
  clearDeferredWorkflowCompaction();
15939
16256
  rememberWorkflowCompactionCheck(`${checkpoint} boundary triggered at ${percent.toFixed(1)}% context`);
15940
- try { ctx.ui.notify(`Workflow auto-compaction triggered at ${percent.toFixed(1)}% context usage.`, "info"); } catch { /* UI may be unavailable */ }
16257
+ try { workflowUiNotify(ctx, `Workflow auto-compaction triggered at ${percent.toFixed(1)}% context usage.`, "info"); } catch { /* UI may be unavailable */ }
15941
16258
  ctx.compact({
15942
16259
  customInstructions,
15943
16260
  onComplete: () => {
15944
16261
  workflowAutoCompactionRunning = false;
15945
16262
  workflowLastAutoCompactionAt = Date.now();
15946
16263
  rememberWorkflowCompactionCheck(`${checkpoint} boundary compaction completed`);
15947
- try { ctx.ui.notify("Workflow auto-compaction complete.", "info"); } catch { /* UI may be unavailable */ }
16264
+ try { workflowUiNotify(ctx, "Workflow auto-compaction complete.", "info"); } catch { /* UI may be unavailable */ }
15948
16265
  },
15949
16266
  onError: (error: Error) => {
15950
16267
  workflowAutoCompactionRunning = false;
15951
16268
  rememberWorkflowCompactionCheck(`${checkpoint} boundary compaction failed — ${error.message}`);
15952
16269
  rememberCustomCompactionStatus(`proactive compaction request failed: ${error.message}`);
15953
- try { ctx.ui.notify(`Workflow auto-compaction skipped: ${error.message}`, "warning"); } catch { /* UI may be unavailable */ }
16270
+ try { workflowUiNotify(ctx, `Workflow auto-compaction skipped: ${error.message}`, "warning"); } catch { /* UI may be unavailable */ }
15954
16271
  },
15955
16272
  });
15956
16273
  }
@@ -15992,26 +16309,18 @@ Pi Version: v${VERSION}
15992
16309
 
15993
16310
  pi.on("session_before_compact", async (event, ctx) => {
15994
16311
  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
- }
16312
+ if (settings.context.compactionMode === "disabled") return;
16002
16313
 
16003
16314
  const provider = settings.context.compactionModelProvider;
16004
16315
  const modelId = settings.context.compactionModel;
16005
16316
  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
16317
  return;
16009
16318
  }
16010
16319
 
16011
16320
  const model = ctx.modelRegistry.find(provider, modelId);
16012
16321
  if (!model) {
16013
16322
  rememberCustomCompactionStatus(`fallback to Pi default: model not found ${provider}/${modelId}`);
16014
- ctx.ui.notify(`Custom compaction model not found: ${provider}/${modelId}; using Pi default compaction.`, "warning");
16323
+ workflowUiNotify(ctx, `Custom compaction model not found: ${provider}/${modelId}; using Pi default compaction.`, "warning");
16015
16324
  return;
16016
16325
  }
16017
16326
 
@@ -16019,12 +16328,12 @@ Pi Version: v${VERSION}
16019
16328
  if (auth.ok === false) {
16020
16329
  const authError = auth.error;
16021
16330
  rememberCustomCompactionStatus(`fallback to Pi default: auth failed for ${provider}/${modelId}: ${authError}`);
16022
- ctx.ui.notify(`Custom compaction auth failed for ${provider}/${modelId}: ${authError}; using Pi default compaction.`, "warning");
16331
+ workflowUiNotify(ctx, `Custom compaction auth failed for ${provider}/${modelId}: ${authError}; using Pi default compaction.`, "warning");
16023
16332
  return;
16024
16333
  }
16025
16334
  if (!auth.apiKey) {
16026
16335
  rememberCustomCompactionStatus(`fallback to Pi default: no API key for ${provider}/${modelId}`);
16027
- ctx.ui.notify(`No API key for custom compaction model ${provider}/${modelId}; using Pi default compaction.`, "warning");
16336
+ workflowUiNotify(ctx, `No API key for custom compaction model ${provider}/${modelId}; using Pi default compaction.`, "warning");
16028
16337
  return;
16029
16338
  }
16030
16339
 
@@ -16036,14 +16345,14 @@ Pi Version: v${VERSION}
16036
16345
  const fitNote = fitted.adjusted && fitted.safeInputTokens
16037
16346
  ? ` Adaptive fit: summarizing about ${Math.round(fitted.inputTokens).toLocaleString()} tokens within ${Math.round(fitted.safeInputTokens).toLocaleString()} safe input tokens for the compaction model.`
16038
16347
  : "";
16039
- ctx.ui.notify(`Custom compaction using ${provider}/${modelId}. Keep recent: ${customCompactionKeepRecentTokens(settings).toLocaleString()} tokens; reserve: ${customCompactionReserveTokens(settings).toLocaleString()} tokens.${fitNote}`, "info");
16348
+ workflowUiNotify(ctx, `Custom compaction using ${provider}/${modelId}. Keep recent: ${customCompactionKeepRecentTokens(settings).toLocaleString()} tokens; reserve: ${customCompactionReserveTokens(settings).toLocaleString()} tokens.${fitNote}`, "info");
16040
16349
  const result = await compactionApi.compact(fitted.preparation, model, auth.apiKey, auth.headers, event.customInstructions, event.signal);
16041
16350
  rememberCustomCompactionStatus(`custom compaction succeeded using ${provider}/${modelId}${fitted.adjusted ? " with adaptive context fit" : ""}`);
16042
16351
  return { compaction: result };
16043
16352
  } catch (error) {
16044
16353
  const message = error instanceof Error ? error.message : String(error);
16045
16354
  rememberCustomCompactionStatus(`fallback to Pi default: custom compaction failed for ${provider}/${modelId}: ${message}`);
16046
- if (!event.signal.aborted) ctx.ui.notify(`Custom compaction failed: ${message}; using Pi default compaction.`, "warning");
16355
+ if (!event.signal.aborted) workflowUiNotify(ctx, `Custom compaction failed: ${message}; using Pi default compaction.`, "warning");
16047
16356
  return;
16048
16357
  }
16049
16358
  });
@@ -16715,7 +17024,7 @@ Pi Version: v${VERSION}
16715
17024
  if (action === "scope" || action === "write-target") {
16716
17025
  const effective = loadEffectiveSettings(ctx.cwd);
16717
17026
  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}`);
17027
+ 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
17028
  return;
16720
17029
  }
16721
17030
  if (action === "create-project-override") {
@@ -16900,6 +17209,14 @@ Pi Version: v${VERSION}
16900
17209
  setWorkflowUi(ctx, state, activeSubagents);
16901
17210
  return show(pi, updatedMessage(result.scope, result.file, `ui.${key}`, String(bool)));
16902
17211
  }
17212
+ if (subject === "ui" && key === "editorHintContrast") {
17213
+ const contrast = parseEditorHintContrast(value);
17214
+ if (!contrast) return show(pi, "# Error\n\nUsage: `/workflow-settings set ui editorHintContrast <subtle|normal|bright|high>`");
17215
+ const result = updateSettings(ctx.cwd, scope ?? "global", (s) => { workflowWidgetUi(s).editorHintContrast = contrast; });
17216
+ setWorkflowUi(ctx, state, activeSubagents);
17217
+ workflowEditorHintText = workflowEditorHintTextFor(state, loadWorkflowSettings(ctx.cwd));
17218
+ return show(pi, updatedMessage(result.scope, result.file, "ui.editorHintContrast", contrast));
17219
+ }
16903
17220
  if (subject === "subagents" && (key === "enabled" || key === "activityIndicatorEnabled" || key === "autoUseDuringPlanning" || key === "autoUseDuringExecution" || key === "autoUseDuringRepair" || key === "autoUseDuringReview" || key === "autoUseDuringValidation" || key === "allowParallelReadOnly" || key === "allowParallelPlanning" || key === "allowParallelExecution" || key === "allowParallelRepair" || key === "allowParallelReview" || key === "allowParallelValidation" || key === "allowParallelEdits" || key === "requireParallelEditConflictProtection" || key === "allowBackgroundSubagents")) {
16904
17221
  if (bool === undefined) return show(pi, "# Error\n\nUsage: `/workflow-settings set subagents <enabled|activityIndicatorEnabled|autoUseDuringPlanning|autoUseDuringExecution|autoUseDuringRepair|autoUseDuringReview|autoUseDuringValidation|allowParallelReadOnly|allowParallelPlanning|allowParallelExecution|allowParallelRepair|allowParallelReview|allowParallelValidation|allowParallelEdits|requireParallelEditConflictProtection> <true|false>`");
16905
17222
  const result = updateSettings(ctx.cwd, scope, (s) => {
@@ -17022,7 +17339,12 @@ Pi Version: v${VERSION}
17022
17339
  return show(pi, updatedMessage(result.scope, result.file, "context.customCompactionEnabled", String(bool)));
17023
17340
  }
17024
17341
  if (subject === "context" && key === "autoCompactionEnabled") {
17025
- if (bool === undefined) return show(pi, "# Error\n\nUsage: `/workflow-settings set context autoCompactionEnabled <true|false>`");
17342
+ const normalizedValue = String(value ?? "").trim().toLowerCase();
17343
+ if (normalizedValue === "default" || normalizedValue === "reset") {
17344
+ const result = updateSettings(ctx.cwd, scope, (s) => { delete s.context.autoCompactionEnabled; });
17345
+ return show(pi, updatedMessage(result.scope, result.file, "context.autoCompactionEnabled", "Pi default") + `\n\n${compactionTriggerStatus(result.settings)}`);
17346
+ }
17347
+ if (bool === undefined) return show(pi, "# Error\n\nUsage: `/workflow-settings set context autoCompactionEnabled <true|false|default>`");
17026
17348
  const result = updateSettings(ctx.cwd, scope, (s) => { s.context.autoCompactionEnabled = bool; });
17027
17349
  return show(pi, updatedMessage(result.scope, result.file, "context.autoCompactionEnabled", String(bool)) + `\n\n${compactionTriggerStatus(result.settings)}`);
17028
17350
  }
@@ -17036,18 +17358,18 @@ Pi Version: v${VERSION}
17036
17358
  const normalizedValue = String(value ?? "").trim().toLowerCase();
17037
17359
  if (key === "compactionTriggerPercent" && (normalizedValue === "default" || normalizedValue === "reset")) {
17038
17360
  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)}`);
17361
+ return show(pi, updatedMessage(result.scope, result.file, "context.compactionTriggerPercent", "removed override; Pi native formula applies") + `\n\n${compactionTriggerStatus(result.settings)}`);
17040
17362
  }
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;
17363
+ if ((key === "compactionCooldownMinutes" || key === "customCompactionReserveTokens" || key === "customCompactionKeepRecentTokens") && (normalizedValue === "default" || normalizedValue === "reset")) {
17364
+ const fallback = key === "compactionCooldownMinutes" ? 5 : key === "customCompactionReserveTokens" ? DEFAULT_PI_COMPACTION_RESERVE_TOKENS : DEFAULT_PI_COMPACTION_KEEP_RECENT_TOKENS;
17043
17365
  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)}`);
17366
+ return show(pi, updatedMessage(result.scope, result.file, `context.${key}`, `Pi default ${fallback}`) + `\n\n${compactionTriggerStatus(result.settings)}`);
17045
17367
  }
17046
17368
  const rawCount = value === undefined ? NaN : Number(value);
17047
17369
  const count = Number.isInteger(rawCount) ? rawCount : undefined;
17048
17370
  const min = key === "compactionTriggerPercent" ? 50 : key === "compactionCooldownMinutes" ? 0 : key === "customCompactionReserveTokens" ? 4096 : 1000;
17049
17371
  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}>`;
17372
+ const usage = `<${min}-${max}|default|reset>`;
17051
17373
  if (count === undefined || count < min || count > max) return show(pi, `# Error\n\nUsage: \`/workflow-settings set context ${key} ${usage}\``);
17052
17374
  const result = updateSettings(ctx.cwd, scope, (s) => { (s.context as typeof s.context & Record<string, number>)[key] = count; });
17053
17375
  return show(pi, updatedMessage(result.scope, result.file, `context.${key}`, String(count)) + `\n\n${compactionTriggerStatus(result.settings)}`);