@mediadatafusion/pi-workflow-suite 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/CONTRIBUTING.md +9 -0
- package/LICENSE.md +201 -0
- package/NOTICE +6 -0
- package/README.md +1208 -0
- package/SECURITY.md +7 -0
- package/SUPPORT.md +9 -0
- package/TRADEMARKS.md +14 -0
- package/VERSION +1 -0
- package/agents/codebase-research.md +42 -0
- package/agents/general-worker.md +26 -0
- package/agents/implementation-planning.md +46 -0
- package/agents/quality-validation.md +43 -0
- package/agents/workflow-orchestrator.md +44 -0
- package/config/prompts/execute-approved-plan.md +43 -0
- package/config/prompts/mission-checkpoint.md +26 -0
- package/config/prompts/mission-final-validation.md +21 -0
- package/config/prompts/mission-plan.md +129 -0
- package/config/prompts/mission-repair.md +33 -0
- package/config/prompts/mission-run.md +37 -0
- package/config/prompts/validate-approved-plan.md +42 -0
- package/config/prompts/workflow-plan-prompt.md +93 -0
- package/config/prompts/workflow-repair.md +20 -0
- package/config/prompts/workflow-summary.md +23 -0
- package/config/workflow-settings.example.json +335 -0
- package/docs/assets/mediadatafusion-logo.png +0 -0
- package/docs/assets/pi-workflow-suite-card.png +0 -0
- package/docs/assets/pi-workflow-suite-header.png +0 -0
- package/docs/assets/pi-workflow-suite-video-thumb.png +0 -0
- package/docs/assets/readme-link-commands.svg +10 -0
- package/docs/assets/readme-link-install.svg +10 -0
- package/docs/assets/readme-link-quick-start.svg +10 -0
- package/docs/assets/readme-link-settings.svg +10 -0
- package/extensions/subagent/agents.ts +149 -0
- package/extensions/subagent/index.ts +1136 -0
- package/extensions/subagent/runner.ts +291 -0
- package/extensions/workflow-model-router.ts +1485 -0
- package/extensions/workflow-modes.ts +14778 -0
- package/extensions/workflow-parsers.ts +212 -0
- package/extensions/workflow-settings-capabilities.ts +282 -0
- package/extensions/workflow-state.ts +978 -0
- package/extensions/workflow-subagent-policy.ts +180 -0
- package/extensions/workflow-summary.ts +381 -0
- package/extensions/workflow-tool-guard.ts +302 -0
- package/extensions/workflow-validation-classifier.ts +102 -0
- package/extensions/workflow-web-tools.ts +356 -0
- package/package.json +1 -0
- package/scripts/audit-live.sh +69 -0
- package/scripts/audit-settings.sh +136 -0
- package/scripts/backup-live.sh +63 -0
- package/scripts/bootstrap-project.sh +220 -0
- package/scripts/install-to-live.sh +87 -0
- package/scripts/quarantine-live-junk.sh +69 -0
- package/scripts/verify-live.sh +128 -0
- package/skills/codebase-discovery/SKILL.md +20 -0
- package/skills/find-skills/SKILL.md +155 -0
- package/skills/git-safe-summary/SKILL.md +20 -0
- package/skills/implementation-planning/SKILL.md +20 -0
- package/skills/project-rules-audit/SKILL.md +20 -0
- package/skills/safe-execution/SKILL.md +20 -0
- package/skills/validation-review/SKILL.md +20 -0
|
@@ -0,0 +1,1485 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { getAgentDir, type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
|
|
6
|
+
export type WorkflowRole = "planner" | "executor" | "validator" | "reviewer";
|
|
7
|
+
export type MissionModelRole = WorkflowRole;
|
|
8
|
+
export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
|
9
|
+
export type WorkflowSettingsScope = "global" | "project";
|
|
10
|
+
|
|
11
|
+
export interface RoleModelSettings {
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
provider: string | null;
|
|
14
|
+
model: string | null;
|
|
15
|
+
thinkingLevel: ThinkingLevel;
|
|
16
|
+
askBeforeRun?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type PlanningClarificationMode = "auto" | "always_for_nontrivial" | "never";
|
|
20
|
+
export type PlanningDepth = "fast" | "standard" | "deep" | "maximum";
|
|
21
|
+
export type ClarificationTiming = "immediate" | "after_initial_analysis";
|
|
22
|
+
export type SubagentPolicy = "off" | "auto" | "deep" | "maximum" | "forced";
|
|
23
|
+
export type SubagentPlanningPolicy = SubagentPolicy;
|
|
24
|
+
export type EditConcurrencyMode = "sequential" | "scoped" | "blocked";
|
|
25
|
+
export type PlanningOrchestrationPolicy = "off" | "auto" | "orchestrator_first" | "forced_orchestrated";
|
|
26
|
+
export type CompactionMode = "pi_default" | "custom_model" | "custom_agent" | "disabled";
|
|
27
|
+
export type WorkflowCompactionCheckMode = "boundary" | "in_session";
|
|
28
|
+
export type StandardTodoTriggerMode = "off" | "manual" | "auto" | "required";
|
|
29
|
+
export type StandardClarificationMode = PlanningClarificationMode | "off" | "minimal";
|
|
30
|
+
export type StandardModelRole = "current" | WorkflowRole;
|
|
31
|
+
export type StandardModelSource = "current" | "shared" | "standard_specific";
|
|
32
|
+
export type WorkflowAgentScope = "user" | "project" | "both";
|
|
33
|
+
export type MissionAutonomy = "manual" | "approval_gated" | "supervised_auto" | "full_auto";
|
|
34
|
+
export type ValidationRetryMode = "off" | "safe_only" | "aggressive_within_scope";
|
|
35
|
+
export type WorkflowStartupVisual = "none" | "minimal" | "diagnostic_center" | "workflow_duo" | "mission_control" | "data_stream" | "neural_grid" | "custom_brand";
|
|
36
|
+
export type WorkflowStartupLogo = "none" | "pi" | "custom";
|
|
37
|
+
export type WorkflowStartupLogoFont = "block" | "shadow" | "outline" | "wide" | "double" | "three_d" | "solid";
|
|
38
|
+
export type WorkflowStartupLogoShadowDirection = "down_right" | "down" | "up" | "left" | "right";
|
|
39
|
+
export type WorkflowStartupLogoColorStyle = "theme" | "primary" | "split";
|
|
40
|
+
export type CustomBrandBaseVisual = "minimal" | "diagnostic_center" | "workflow_duo" | "mission_control" | "data_stream" | "neural_grid";
|
|
41
|
+
export type RepairRetryGateName = "review" | "validation" | "missionValidation" | "missionFinalValidation";
|
|
42
|
+
|
|
43
|
+
export interface RepairRetryGateSettings {
|
|
44
|
+
autoRepairFailures?: boolean;
|
|
45
|
+
retryMode?: ValidationRetryMode;
|
|
46
|
+
maxRetriesPerItem?: number;
|
|
47
|
+
maxRetriesPerWorkflow?: number;
|
|
48
|
+
pauseAfterFailure?: boolean;
|
|
49
|
+
requireApprovalForOutOfScopeRepair?: boolean;
|
|
50
|
+
requireApprovalForDestructiveRepair?: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface RepairRetrySettings {
|
|
54
|
+
enabled?: boolean;
|
|
55
|
+
maxTotalRetries?: number;
|
|
56
|
+
defaults?: RepairRetryGateSettings;
|
|
57
|
+
gates?: Partial<Record<RepairRetryGateName, RepairRetryGateSettings>>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface WorkflowSubagentSettings {
|
|
61
|
+
enabled: boolean;
|
|
62
|
+
requireApprovalBeforeRun: boolean;
|
|
63
|
+
activityIndicatorEnabled?: boolean;
|
|
64
|
+
autoUseDuringPlanning?: boolean;
|
|
65
|
+
autoUseDuringExecution?: boolean;
|
|
66
|
+
autoUseDuringRepair?: boolean;
|
|
67
|
+
autoUseDuringReview?: boolean;
|
|
68
|
+
autoUseDuringValidation?: boolean;
|
|
69
|
+
planningPolicy?: SubagentPolicy;
|
|
70
|
+
executionPolicy?: SubagentPolicy;
|
|
71
|
+
repairPolicy?: SubagentPolicy;
|
|
72
|
+
reviewPolicy?: SubagentPolicy;
|
|
73
|
+
validationPolicy?: SubagentPolicy;
|
|
74
|
+
minPlanningWorkersForDeep?: number;
|
|
75
|
+
minPlanningWorkersForMaximum?: number;
|
|
76
|
+
minExecutionWorkersForDeep?: number;
|
|
77
|
+
minExecutionWorkersForMaximum?: number;
|
|
78
|
+
minRepairWorkersForDeep?: number;
|
|
79
|
+
minRepairWorkersForMaximum?: number;
|
|
80
|
+
minReviewWorkersForDeep?: number;
|
|
81
|
+
minReviewWorkersForMaximum?: number;
|
|
82
|
+
minValidationWorkersForDeep?: number;
|
|
83
|
+
minValidationWorkersForMaximum?: number;
|
|
84
|
+
allowParallelReadOnly?: boolean;
|
|
85
|
+
allowParallelPlanning?: boolean;
|
|
86
|
+
allowParallelExecution?: boolean;
|
|
87
|
+
allowParallelRepair?: boolean;
|
|
88
|
+
allowParallelReview?: boolean;
|
|
89
|
+
allowParallelValidation?: boolean;
|
|
90
|
+
allowParallelEdits?: boolean;
|
|
91
|
+
editConcurrencyMode?: EditConcurrencyMode;
|
|
92
|
+
requireParallelEditConflictProtection?: boolean;
|
|
93
|
+
planningOrchestrationPolicy?: PlanningOrchestrationPolicy;
|
|
94
|
+
subagentTimeoutMinutes?: number;
|
|
95
|
+
subagentStaleMinutes?: number;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface WorkflowSettings {
|
|
99
|
+
models: Record<WorkflowRole, RoleModelSettings>;
|
|
100
|
+
planning: {
|
|
101
|
+
clarificationMode: PlanningClarificationMode;
|
|
102
|
+
maxClarificationQuestions: number;
|
|
103
|
+
interactiveClarificationEnabled: boolean;
|
|
104
|
+
depth: PlanningDepth;
|
|
105
|
+
clarificationTiming?: ClarificationTiming;
|
|
106
|
+
clarificationQualityGate?: boolean;
|
|
107
|
+
allowClarificationWithoutAnalysis?: boolean;
|
|
108
|
+
useSubagentsBeforeClarification?: boolean;
|
|
109
|
+
};
|
|
110
|
+
workflow: {
|
|
111
|
+
requirePlanApprovalBeforeExecute: boolean;
|
|
112
|
+
requireApprovalBeforeExecution?: boolean;
|
|
113
|
+
requireApprovalPerStep?: boolean;
|
|
114
|
+
offerValidationAfterExecute: boolean;
|
|
115
|
+
autoRunValidationAfterExecute: boolean;
|
|
116
|
+
validateAfterExecution?: boolean;
|
|
117
|
+
validateAfterEachStep?: boolean;
|
|
118
|
+
offerReviewerBeforeExecute: boolean;
|
|
119
|
+
autoRunReviewerBeforeExecute: boolean;
|
|
120
|
+
allowPlanRevisionBeforeExecute: boolean;
|
|
121
|
+
autoRepairReviewFailures?: boolean;
|
|
122
|
+
maxReviewRetriesPerPlan?: number;
|
|
123
|
+
maxReviewRetriesPerWorkflow?: number;
|
|
124
|
+
reviewRetryMode?: ValidationRetryMode;
|
|
125
|
+
pauseAfterReviewFailure?: boolean;
|
|
126
|
+
executionChecklistEnabled?: boolean;
|
|
127
|
+
returnToPlanModeAfterWorkflow?: boolean;
|
|
128
|
+
autoRepairValidationFailures?: boolean;
|
|
129
|
+
validationRetryMode?: ValidationRetryMode;
|
|
130
|
+
maxValidationRetriesPerPlan?: number;
|
|
131
|
+
maxValidationRetriesPerWorkflow?: number;
|
|
132
|
+
pauseAfterValidationFailure?: boolean;
|
|
133
|
+
requireApprovalForOutOfScopeRepair?: boolean;
|
|
134
|
+
requireApprovalForDestructiveRepair?: boolean;
|
|
135
|
+
repairRetry?: RepairRetrySettings;
|
|
136
|
+
savePlans?: boolean;
|
|
137
|
+
savePlanHistory?: boolean;
|
|
138
|
+
planHistoryLimit?: number;
|
|
139
|
+
planProgressEnabled?: boolean;
|
|
140
|
+
planRuntimeEnabled?: boolean;
|
|
141
|
+
};
|
|
142
|
+
standard: {
|
|
143
|
+
enabled: boolean;
|
|
144
|
+
autoTodoEnabled?: boolean;
|
|
145
|
+
todoProgressVisible?: boolean;
|
|
146
|
+
todoTriggerMode?: StandardTodoTriggerMode;
|
|
147
|
+
clarificationEnabled?: boolean;
|
|
148
|
+
clarificationMode?: StandardClarificationMode;
|
|
149
|
+
maxClarificationQuestions?: number;
|
|
150
|
+
interactiveClarificationEnabled?: boolean;
|
|
151
|
+
clarificationTiming?: ClarificationTiming;
|
|
152
|
+
clarificationQualityGate?: boolean;
|
|
153
|
+
allowClarificationWithoutAnalysis?: boolean;
|
|
154
|
+
useSubagentsBeforeClarification?: boolean;
|
|
155
|
+
allowSubagents?: boolean;
|
|
156
|
+
subagentScope?: WorkflowAgentScope;
|
|
157
|
+
subagents?: Partial<WorkflowSubagentSettings>;
|
|
158
|
+
statusWidgetVisible?: boolean;
|
|
159
|
+
useSharedExecutorModel?: boolean;
|
|
160
|
+
useStandardSpecificModels?: boolean;
|
|
161
|
+
modelRole?: StandardModelRole;
|
|
162
|
+
models?: Record<WorkflowRole, RoleModelSettings>;
|
|
163
|
+
};
|
|
164
|
+
missions: {
|
|
165
|
+
enabled: boolean;
|
|
166
|
+
defaultAutonomy: MissionAutonomy;
|
|
167
|
+
maxRuntimeHours: number;
|
|
168
|
+
checkpointIntervalMinutes: number;
|
|
169
|
+
requireApprovalForDestructiveActions: boolean;
|
|
170
|
+
requireValidationPerMilestone: boolean;
|
|
171
|
+
autoResume: boolean;
|
|
172
|
+
allowFullAuto: boolean;
|
|
173
|
+
autoRunAfterApproval?: boolean;
|
|
174
|
+
offerReviewerBeforeApprove?: boolean;
|
|
175
|
+
autoRunReviewerBeforeApprove?: boolean;
|
|
176
|
+
autoRepairReviewFailures?: boolean;
|
|
177
|
+
reviewRetryMode?: ValidationRetryMode;
|
|
178
|
+
maxReviewRetriesPerMission?: number;
|
|
179
|
+
continueAcrossMilestones?: boolean;
|
|
180
|
+
pauseBetweenMilestones?: boolean;
|
|
181
|
+
progressWidgetEnabled?: boolean;
|
|
182
|
+
progressOutputMode?: "compact" | "detailed";
|
|
183
|
+
showProgressBar?: boolean;
|
|
184
|
+
heartbeatEnabled?: boolean;
|
|
185
|
+
watchdogEnabled?: boolean;
|
|
186
|
+
watchdogStaleMinutes?: number;
|
|
187
|
+
autoRepairValidationFailures?: boolean;
|
|
188
|
+
maxValidationRetriesPerMilestone?: number;
|
|
189
|
+
maxValidationRetriesPerMission?: number;
|
|
190
|
+
validationRetryMode?: ValidationRetryMode;
|
|
191
|
+
pauseAfterValidationFailure?: boolean;
|
|
192
|
+
requireApprovalForOutOfScopeRepair?: boolean;
|
|
193
|
+
requireApprovalForDestructiveRepair?: boolean;
|
|
194
|
+
finalValidationEnabled?: boolean;
|
|
195
|
+
finalValidationRequiresPass?: boolean;
|
|
196
|
+
autoRepairFinalValidationFailures?: boolean;
|
|
197
|
+
maxFinalValidationRetries?: number;
|
|
198
|
+
clarificationMode?: PlanningClarificationMode;
|
|
199
|
+
interactiveClarificationEnabled?: boolean;
|
|
200
|
+
maxClarificationQuestions?: number;
|
|
201
|
+
planningDepth?: PlanningDepth;
|
|
202
|
+
clarificationTiming?: ClarificationTiming;
|
|
203
|
+
clarificationQualityGate?: boolean;
|
|
204
|
+
allowClarificationWithoutAnalysis?: boolean;
|
|
205
|
+
useSubagentsBeforeClarification?: boolean;
|
|
206
|
+
subagentPolicy?: SubagentPolicy;
|
|
207
|
+
minWorkersForDeep?: number;
|
|
208
|
+
minWorkersForMaximum?: number;
|
|
209
|
+
useMissionSpecificModels?: boolean;
|
|
210
|
+
models?: Record<MissionModelRole, RoleModelSettings>;
|
|
211
|
+
};
|
|
212
|
+
safety: {
|
|
213
|
+
repoLockEnabled?: boolean;
|
|
214
|
+
disableBashInPlanMode: boolean;
|
|
215
|
+
disableBashInValidatorMode: boolean;
|
|
216
|
+
blockDestructiveCommands: boolean;
|
|
217
|
+
};
|
|
218
|
+
ui: {
|
|
219
|
+
showWorkflowStatus: boolean;
|
|
220
|
+
showPlanModeIndicator?: boolean;
|
|
221
|
+
planModeIndicatorText?: string;
|
|
222
|
+
enableHotkeys: boolean;
|
|
223
|
+
planTopWidgetVisible?: boolean;
|
|
224
|
+
planBottomWidgetVisible?: boolean;
|
|
225
|
+
missionTopWidgetVisible?: boolean;
|
|
226
|
+
missionBottomWidgetVisible?: boolean;
|
|
227
|
+
rememberWidgetVisibility?: boolean;
|
|
228
|
+
enableWidgetShortcuts?: boolean;
|
|
229
|
+
showIdleWorkflowEntryHint?: boolean;
|
|
230
|
+
showActiveWorkflowSwitchHint?: boolean;
|
|
231
|
+
showWidgetShortcutHint?: boolean;
|
|
232
|
+
showPresetShortcutHint?: boolean;
|
|
233
|
+
workflowTheme?: string;
|
|
234
|
+
workflowThemeOverrides?: Record<string, string>;
|
|
235
|
+
startupVisual?: WorkflowStartupVisual;
|
|
236
|
+
startupLogo?: WorkflowStartupLogo;
|
|
237
|
+
startupLogoText?: string;
|
|
238
|
+
startupLogoFont?: WorkflowStartupLogoFont;
|
|
239
|
+
startupLogoShadowDirection?: WorkflowStartupLogoShadowDirection;
|
|
240
|
+
startupLogoColorStyle?: WorkflowStartupLogoColorStyle;
|
|
241
|
+
startupVisualOnSessionStart?: boolean;
|
|
242
|
+
customBrandEnabled?: boolean;
|
|
243
|
+
customBrandText?: string;
|
|
244
|
+
customBrandBaseVisual?: CustomBrandBaseVisual;
|
|
245
|
+
};
|
|
246
|
+
subagents: WorkflowSubagentSettings;
|
|
247
|
+
context: {
|
|
248
|
+
compactionMode: CompactionMode;
|
|
249
|
+
compactionModelProvider: string;
|
|
250
|
+
compactionModel: string;
|
|
251
|
+
compactionAgent: string;
|
|
252
|
+
customCompactionEnabled: boolean;
|
|
253
|
+
autoCompactionEnabled: boolean;
|
|
254
|
+
compactionTriggerPercent: number;
|
|
255
|
+
compactionCooldownMinutes: number;
|
|
256
|
+
customCompactionReserveTokens?: number;
|
|
257
|
+
customCompactionKeepRecentTokens?: number;
|
|
258
|
+
workflowCompactionCheckMode?: WorkflowCompactionCheckMode;
|
|
259
|
+
};
|
|
260
|
+
presets?: {
|
|
261
|
+
activePreset?: string;
|
|
262
|
+
items?: Record<string, WorkflowPresetBundle>;
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export interface WorkflowPresetBundle {
|
|
267
|
+
displayName?: string;
|
|
268
|
+
description?: string;
|
|
269
|
+
planning?: Partial<WorkflowSettings["planning"]>;
|
|
270
|
+
workflow?: Partial<WorkflowSettings["workflow"]>;
|
|
271
|
+
standard?: Partial<Omit<WorkflowSettings["standard"], "models">> & { models?: Partial<Record<WorkflowRole, RoleModelSettings>> };
|
|
272
|
+
missions?: Partial<Omit<WorkflowSettings["missions"], "models">>;
|
|
273
|
+
subagents?: Partial<WorkflowSettings["subagents"]>;
|
|
274
|
+
ui?: Partial<WorkflowSettings["ui"]>;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export const WORKFLOW_SETTINGS_FILE = join(getAgentDir(), "workflow-settings.json");
|
|
278
|
+
const EXTENSION_DIR = dirname(fileURLToPath(import.meta.url));
|
|
279
|
+
const EXAMPLE_SETTINGS_FILE = join(EXTENSION_DIR, "..", "config", "workflow-settings.example.json");
|
|
280
|
+
|
|
281
|
+
const BUILTIN_DEFAULT_WORKFLOW_SETTINGS = {
|
|
282
|
+
"models": {
|
|
283
|
+
"planner": {
|
|
284
|
+
"enabled": true,
|
|
285
|
+
"provider": null,
|
|
286
|
+
"model": null,
|
|
287
|
+
"thinkingLevel": "high"
|
|
288
|
+
},
|
|
289
|
+
"executor": {
|
|
290
|
+
"enabled": true,
|
|
291
|
+
"provider": null,
|
|
292
|
+
"model": null,
|
|
293
|
+
"thinkingLevel": "high"
|
|
294
|
+
},
|
|
295
|
+
"validator": {
|
|
296
|
+
"enabled": true,
|
|
297
|
+
"provider": null,
|
|
298
|
+
"model": null,
|
|
299
|
+
"thinkingLevel": "xhigh",
|
|
300
|
+
"askBeforeRun": false
|
|
301
|
+
},
|
|
302
|
+
"reviewer": {
|
|
303
|
+
"enabled": true,
|
|
304
|
+
"provider": null,
|
|
305
|
+
"model": null,
|
|
306
|
+
"thinkingLevel": "xhigh",
|
|
307
|
+
"askBeforeRun": false
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
"workflow": {
|
|
311
|
+
"requirePlanApprovalBeforeExecute": true,
|
|
312
|
+
"offerValidationAfterExecute": true,
|
|
313
|
+
"autoRunValidationAfterExecute": true,
|
|
314
|
+
"offerReviewerBeforeExecute": false,
|
|
315
|
+
"autoRunReviewerBeforeExecute": false,
|
|
316
|
+
"allowPlanRevisionBeforeExecute": true,
|
|
317
|
+
"autoRepairReviewFailures": true,
|
|
318
|
+
"maxReviewRetriesPerPlan": 2,
|
|
319
|
+
"reviewRetryMode": "safe_only",
|
|
320
|
+
"pauseAfterReviewFailure": false,
|
|
321
|
+
"autoRepairValidationFailures": true,
|
|
322
|
+
"validationRetryMode": "safe_only",
|
|
323
|
+
"maxValidationRetriesPerPlan": 2,
|
|
324
|
+
"maxValidationRetriesPerWorkflow": 4,
|
|
325
|
+
"pauseAfterValidationFailure": false,
|
|
326
|
+
"requireApprovalForOutOfScopeRepair": true,
|
|
327
|
+
"requireApprovalForDestructiveRepair": true,
|
|
328
|
+
"repairRetry": {
|
|
329
|
+
"enabled": true,
|
|
330
|
+
"maxTotalRetries": 6,
|
|
331
|
+
"defaults": {
|
|
332
|
+
"autoRepairFailures": true,
|
|
333
|
+
"retryMode": "safe_only",
|
|
334
|
+
"maxRetriesPerItem": 2,
|
|
335
|
+
"maxRetriesPerWorkflow": 4,
|
|
336
|
+
"pauseAfterFailure": false,
|
|
337
|
+
"requireApprovalForOutOfScopeRepair": true,
|
|
338
|
+
"requireApprovalForDestructiveRepair": true
|
|
339
|
+
},
|
|
340
|
+
"gates": {
|
|
341
|
+
"review": {
|
|
342
|
+
"autoRepairFailures": true,
|
|
343
|
+
"retryMode": "safe_only",
|
|
344
|
+
"maxRetriesPerItem": 2,
|
|
345
|
+
"maxRetriesPerWorkflow": 4,
|
|
346
|
+
"pauseAfterFailure": false,
|
|
347
|
+
"requireApprovalForOutOfScopeRepair": true,
|
|
348
|
+
"requireApprovalForDestructiveRepair": true
|
|
349
|
+
},
|
|
350
|
+
"validation": {
|
|
351
|
+
"autoRepairFailures": true,
|
|
352
|
+
"retryMode": "safe_only",
|
|
353
|
+
"maxRetriesPerItem": 2,
|
|
354
|
+
"maxRetriesPerWorkflow": 4,
|
|
355
|
+
"pauseAfterFailure": false,
|
|
356
|
+
"requireApprovalForOutOfScopeRepair": true,
|
|
357
|
+
"requireApprovalForDestructiveRepair": true
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
"executionChecklistEnabled": true,
|
|
362
|
+
"returnToPlanModeAfterWorkflow": true,
|
|
363
|
+
"savePlans": true,
|
|
364
|
+
"savePlanHistory": true,
|
|
365
|
+
"planHistoryLimit": 50,
|
|
366
|
+
"planProgressEnabled": true,
|
|
367
|
+
"planRuntimeEnabled": true,
|
|
368
|
+
"requireApprovalBeforeExecution": true,
|
|
369
|
+
"requireApprovalPerStep": false,
|
|
370
|
+
"validateAfterEachStep": false,
|
|
371
|
+
"validateAfterExecution": true
|
|
372
|
+
},
|
|
373
|
+
"standard": {
|
|
374
|
+
"enabled": true,
|
|
375
|
+
"autoTodoEnabled": true,
|
|
376
|
+
"todoProgressVisible": true,
|
|
377
|
+
"todoTriggerMode": "auto",
|
|
378
|
+
"clarificationEnabled": true,
|
|
379
|
+
"clarificationMode": "auto",
|
|
380
|
+
"maxClarificationQuestions": 1,
|
|
381
|
+
"interactiveClarificationEnabled": true,
|
|
382
|
+
"clarificationTiming": "after_initial_analysis",
|
|
383
|
+
"clarificationQualityGate": true,
|
|
384
|
+
"allowClarificationWithoutAnalysis": false,
|
|
385
|
+
"useSubagentsBeforeClarification": false,
|
|
386
|
+
"allowSubagents": true,
|
|
387
|
+
"subagentScope": "user",
|
|
388
|
+
"subagents": {},
|
|
389
|
+
"statusWidgetVisible": true,
|
|
390
|
+
"useSharedExecutorModel": true,
|
|
391
|
+
"useStandardSpecificModels": false,
|
|
392
|
+
"modelRole": "executor",
|
|
393
|
+
"models": {
|
|
394
|
+
"planner": {
|
|
395
|
+
"enabled": true,
|
|
396
|
+
"provider": null,
|
|
397
|
+
"model": null,
|
|
398
|
+
"thinkingLevel": "medium"
|
|
399
|
+
},
|
|
400
|
+
"executor": {
|
|
401
|
+
"enabled": true,
|
|
402
|
+
"provider": null,
|
|
403
|
+
"model": null,
|
|
404
|
+
"thinkingLevel": "medium"
|
|
405
|
+
},
|
|
406
|
+
"reviewer": {
|
|
407
|
+
"enabled": true,
|
|
408
|
+
"provider": null,
|
|
409
|
+
"model": null,
|
|
410
|
+
"thinkingLevel": "xhigh"
|
|
411
|
+
},
|
|
412
|
+
"validator": {
|
|
413
|
+
"enabled": true,
|
|
414
|
+
"provider": null,
|
|
415
|
+
"model": null,
|
|
416
|
+
"thinkingLevel": "xhigh"
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
"missions": {
|
|
421
|
+
"enabled": true,
|
|
422
|
+
"defaultAutonomy": "approval_gated",
|
|
423
|
+
"maxRuntimeHours": 13,
|
|
424
|
+
"checkpointIntervalMinutes": 30,
|
|
425
|
+
"requireApprovalForDestructiveActions": true,
|
|
426
|
+
"requireValidationPerMilestone": true,
|
|
427
|
+
"autoResume": false,
|
|
428
|
+
"allowFullAuto": false,
|
|
429
|
+
"autoRunAfterApproval": true,
|
|
430
|
+
"offerReviewerBeforeApprove": false,
|
|
431
|
+
"autoRunReviewerBeforeApprove": false,
|
|
432
|
+
"autoRepairReviewFailures": false,
|
|
433
|
+
"reviewRetryMode": "off",
|
|
434
|
+
"maxReviewRetriesPerMission": 0,
|
|
435
|
+
"continueAcrossMilestones": true,
|
|
436
|
+
"pauseBetweenMilestones": false,
|
|
437
|
+
"progressWidgetEnabled": true,
|
|
438
|
+
"progressOutputMode": "compact",
|
|
439
|
+
"showProgressBar": true,
|
|
440
|
+
"heartbeatEnabled": true,
|
|
441
|
+
"watchdogEnabled": false,
|
|
442
|
+
"watchdogStaleMinutes": 30,
|
|
443
|
+
"autoRepairValidationFailures": true,
|
|
444
|
+
"maxValidationRetriesPerMilestone": 2,
|
|
445
|
+
"maxValidationRetriesPerMission": 8,
|
|
446
|
+
"validationRetryMode": "safe_only",
|
|
447
|
+
"pauseAfterValidationFailure": false,
|
|
448
|
+
"requireApprovalForOutOfScopeRepair": true,
|
|
449
|
+
"requireApprovalForDestructiveRepair": true,
|
|
450
|
+
"finalValidationEnabled": false,
|
|
451
|
+
"finalValidationRequiresPass": true,
|
|
452
|
+
"autoRepairFinalValidationFailures": false,
|
|
453
|
+
"maxFinalValidationRetries": 1,
|
|
454
|
+
"clarificationMode": "always_for_nontrivial",
|
|
455
|
+
"interactiveClarificationEnabled": true,
|
|
456
|
+
"maxClarificationQuestions": 4,
|
|
457
|
+
"planningDepth": "deep",
|
|
458
|
+
"subagentPolicy": "forced",
|
|
459
|
+
"minWorkersForDeep": 1,
|
|
460
|
+
"minWorkersForMaximum": 1,
|
|
461
|
+
"useMissionSpecificModels": false,
|
|
462
|
+
"models": {
|
|
463
|
+
"planner": {
|
|
464
|
+
"enabled": true,
|
|
465
|
+
"provider": null,
|
|
466
|
+
"model": null,
|
|
467
|
+
"thinkingLevel": "medium"
|
|
468
|
+
},
|
|
469
|
+
"executor": {
|
|
470
|
+
"enabled": true,
|
|
471
|
+
"provider": null,
|
|
472
|
+
"model": null,
|
|
473
|
+
"thinkingLevel": "medium"
|
|
474
|
+
},
|
|
475
|
+
"reviewer": {
|
|
476
|
+
"enabled": true,
|
|
477
|
+
"provider": null,
|
|
478
|
+
"model": null,
|
|
479
|
+
"thinkingLevel": "xhigh"
|
|
480
|
+
},
|
|
481
|
+
"validator": {
|
|
482
|
+
"enabled": true,
|
|
483
|
+
"provider": null,
|
|
484
|
+
"model": null,
|
|
485
|
+
"thinkingLevel": "xhigh"
|
|
486
|
+
}
|
|
487
|
+
},
|
|
488
|
+
"clarificationTiming": "after_initial_analysis",
|
|
489
|
+
"clarificationQualityGate": true,
|
|
490
|
+
"allowClarificationWithoutAnalysis": false,
|
|
491
|
+
"useSubagentsBeforeClarification": true
|
|
492
|
+
},
|
|
493
|
+
"safety": {
|
|
494
|
+
"repoLockEnabled": false,
|
|
495
|
+
"disableBashInPlanMode": true,
|
|
496
|
+
"disableBashInValidatorMode": true,
|
|
497
|
+
"blockDestructiveCommands": true
|
|
498
|
+
},
|
|
499
|
+
"ui": {
|
|
500
|
+
"showWorkflowStatus": true,
|
|
501
|
+
"showPlanModeIndicator": true,
|
|
502
|
+
"planModeIndicatorText": "PLAN MODE ACTIVE - enter your planning request, or use /plan cancel to exit",
|
|
503
|
+
"enableHotkeys": false,
|
|
504
|
+
"planTopWidgetVisible": true,
|
|
505
|
+
"planBottomWidgetVisible": true,
|
|
506
|
+
"missionTopWidgetVisible": true,
|
|
507
|
+
"missionBottomWidgetVisible": true,
|
|
508
|
+
"rememberWidgetVisibility": true,
|
|
509
|
+
"enableWidgetShortcuts": true,
|
|
510
|
+
"showIdleWorkflowEntryHint": true,
|
|
511
|
+
"showActiveWorkflowSwitchHint": true,
|
|
512
|
+
"showWidgetShortcutHint": true,
|
|
513
|
+
"showPresetShortcutHint": true,
|
|
514
|
+
"workflowTheme": "aurora",
|
|
515
|
+
"workflowThemeOverrides": {},
|
|
516
|
+
"startupVisual": "mission_control",
|
|
517
|
+
"startupLogo": "pi",
|
|
518
|
+
"startupLogoText": "",
|
|
519
|
+
"startupLogoFont": "block",
|
|
520
|
+
"startupLogoShadowDirection": "down_right",
|
|
521
|
+
"startupLogoColorStyle": "theme",
|
|
522
|
+
"startupVisualOnSessionStart": true,
|
|
523
|
+
"customBrandEnabled": false,
|
|
524
|
+
"customBrandText": "",
|
|
525
|
+
"customBrandBaseVisual": "mission_control"
|
|
526
|
+
},
|
|
527
|
+
"shortcuts": {
|
|
528
|
+
"planMode": null
|
|
529
|
+
},
|
|
530
|
+
"subagents": {
|
|
531
|
+
"enabled": true,
|
|
532
|
+
"activityIndicatorEnabled": true,
|
|
533
|
+
"requireApprovalBeforeRun": false,
|
|
534
|
+
"autoUseDuringPlanning": true,
|
|
535
|
+
"autoUseDuringExecution": true,
|
|
536
|
+
"autoUseDuringRepair": true,
|
|
537
|
+
"autoUseDuringReview": true,
|
|
538
|
+
"autoUseDuringValidation": true,
|
|
539
|
+
"planningPolicy": "forced",
|
|
540
|
+
"executionPolicy": "forced",
|
|
541
|
+
"repairPolicy": "forced",
|
|
542
|
+
"reviewPolicy": "forced",
|
|
543
|
+
"validationPolicy": "forced",
|
|
544
|
+
"minPlanningWorkersForDeep": 1,
|
|
545
|
+
"minPlanningWorkersForMaximum": 1,
|
|
546
|
+
"minExecutionWorkersForDeep": 2,
|
|
547
|
+
"minExecutionWorkersForMaximum": 2,
|
|
548
|
+
"minRepairWorkersForDeep": 2,
|
|
549
|
+
"minRepairWorkersForMaximum": 2,
|
|
550
|
+
"minReviewWorkersForDeep": 2,
|
|
551
|
+
"minReviewWorkersForMaximum": 2,
|
|
552
|
+
"minValidationWorkersForDeep": 2,
|
|
553
|
+
"minValidationWorkersForMaximum": 2,
|
|
554
|
+
"allowParallelReadOnly": true,
|
|
555
|
+
"allowParallelPlanning": true,
|
|
556
|
+
"allowParallelExecution": true,
|
|
557
|
+
"allowParallelRepair": true,
|
|
558
|
+
"allowParallelReview": true,
|
|
559
|
+
"allowParallelValidation": true,
|
|
560
|
+
"allowParallelEdits": false,
|
|
561
|
+
"editConcurrencyMode": "sequential",
|
|
562
|
+
"requireParallelEditConflictProtection": true,
|
|
563
|
+
"planningOrchestrationPolicy": "orchestrator_first",
|
|
564
|
+
"subagentTimeoutMinutes": 20,
|
|
565
|
+
"subagentStaleMinutes": 8
|
|
566
|
+
},
|
|
567
|
+
"planning": {
|
|
568
|
+
"clarificationMode": "auto",
|
|
569
|
+
"maxClarificationQuestions": 3,
|
|
570
|
+
"interactiveClarificationEnabled": true,
|
|
571
|
+
"depth": "standard",
|
|
572
|
+
"clarificationTiming": "after_initial_analysis",
|
|
573
|
+
"clarificationQualityGate": true,
|
|
574
|
+
"allowClarificationWithoutAnalysis": false,
|
|
575
|
+
"useSubagentsBeforeClarification": true
|
|
576
|
+
},
|
|
577
|
+
"context": {
|
|
578
|
+
"compactionMode": "pi_default",
|
|
579
|
+
"compactionModelProvider": "",
|
|
580
|
+
"compactionModel": "",
|
|
581
|
+
"compactionAgent": "",
|
|
582
|
+
"customCompactionEnabled": false,
|
|
583
|
+
"autoCompactionEnabled": false,
|
|
584
|
+
"compactionTriggerPercent": 85,
|
|
585
|
+
"compactionCooldownMinutes": 5,
|
|
586
|
+
"customCompactionReserveTokens": 16384,
|
|
587
|
+
"customCompactionKeepRecentTokens": 20000,
|
|
588
|
+
"workflowCompactionCheckMode": "boundary"
|
|
589
|
+
},
|
|
590
|
+
"presets": {
|
|
591
|
+
"activePreset": "custom",
|
|
592
|
+
"items": {}
|
|
593
|
+
}
|
|
594
|
+
} as WorkflowSettings;
|
|
595
|
+
const workflowSettingsWarnings = new Set<string>();
|
|
596
|
+
|
|
597
|
+
function cloneWorkflowSettings(settings: WorkflowSettings): WorkflowSettings {
|
|
598
|
+
return JSON.parse(JSON.stringify(settings)) as WorkflowSettings;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
function rememberWorkflowSettingsWarning(message: string): void {
|
|
602
|
+
workflowSettingsWarnings.add(message);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
function parseWorkflowSettingsFile(file: string, fallback: WorkflowSettings, label: string): Partial<WorkflowSettings> | WorkflowSettings {
|
|
606
|
+
try {
|
|
607
|
+
return JSON.parse(readFileSync(file, "utf8")) as Partial<WorkflowSettings>;
|
|
608
|
+
} catch (error) {
|
|
609
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
610
|
+
rememberWorkflowSettingsWarning(`${label} unreadable; using safe defaults (${detail})`);
|
|
611
|
+
return cloneWorkflowSettings(fallback);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
export function workflowSettingsDiagnostics(): string[] {
|
|
616
|
+
return Array.from(workflowSettingsWarnings);
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
export function clearWorkflowSettingsDiagnostics(): void {
|
|
620
|
+
workflowSettingsWarnings.clear();
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
export function defaultWorkflowSettings(): WorkflowSettings {
|
|
624
|
+
const defaults = cloneWorkflowSettings(BUILTIN_DEFAULT_WORKFLOW_SETTINGS);
|
|
625
|
+
if (!existsSync(EXAMPLE_SETTINGS_FILE)) {
|
|
626
|
+
rememberWorkflowSettingsWarning(`example settings missing at ${EXAMPLE_SETTINGS_FILE}; using built-in defaults`);
|
|
627
|
+
return defaults;
|
|
628
|
+
}
|
|
629
|
+
const parsed = parseWorkflowSettingsFile(EXAMPLE_SETTINGS_FILE, defaults, "example workflow settings") as Partial<WorkflowSettings>;
|
|
630
|
+
return {
|
|
631
|
+
...defaults,
|
|
632
|
+
...parsed,
|
|
633
|
+
models: {
|
|
634
|
+
planner: normalizeRole(defaults.models.planner, parsed.models?.planner),
|
|
635
|
+
executor: normalizeRole(defaults.models.executor, parsed.models?.executor),
|
|
636
|
+
validator: normalizeRole(defaults.models.validator, parsed.models?.validator),
|
|
637
|
+
reviewer: normalizeRole(defaults.models.reviewer, parsed.models?.reviewer),
|
|
638
|
+
},
|
|
639
|
+
planning: { ...defaults.planning, ...(parsed.planning ?? {}) },
|
|
640
|
+
workflow: { ...defaults.workflow, ...(parsed.workflow ?? {}) },
|
|
641
|
+
standard: normalizeStandardSettings(defaults, parsed.standard),
|
|
642
|
+
missions: { ...defaults.missions, ...(parsed.missions ?? {}), models: normalizeMissionModels(defaults, parsed.missions) },
|
|
643
|
+
safety: { ...defaults.safety, ...(parsed.safety ?? {}) },
|
|
644
|
+
ui: normalizeUiSettings({ ...defaults.ui, ...(parsed.ui ?? {}) }),
|
|
645
|
+
subagents: { ...defaults.subagents, ...(parsed.subagents ?? {}) },
|
|
646
|
+
context: { ...defaults.context, ...(parsed.context ?? {}) },
|
|
647
|
+
presets: { ...(defaults.presets ?? {}), ...(parsed.presets ?? {}), items: { ...(defaults.presets?.items ?? {}), ...(parsed.presets?.items ?? {}) } },
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
function normalizeRole(defaultRole: RoleModelSettings, parsedRole?: Partial<RoleModelSettings>): RoleModelSettings {
|
|
652
|
+
return { ...defaultRole, ...(parsedRole ?? {}) };
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function normalizeUiSettings(ui: WorkflowSettings["ui"]): WorkflowSettings["ui"] {
|
|
656
|
+
const { startupVisualAnimation: _legacyStartupVisualAnimation, ...rest } = ui as WorkflowSettings["ui"] & { startupVisualAnimation?: boolean };
|
|
657
|
+
return rest;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
function normalizeStandardClarificationMode(value: unknown, fallback: StandardClarificationMode = "auto"): StandardClarificationMode {
|
|
661
|
+
if (value === "never" || value === "off") return "never";
|
|
662
|
+
if (value === "always_for_nontrivial") return "always_for_nontrivial";
|
|
663
|
+
if (value === "auto" || value === "minimal") return "auto";
|
|
664
|
+
return fallback;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function normalizeStandardSettings(defaults: WorkflowSettings, base?: Partial<WorkflowSettings>["standard"], override?: Partial<WorkflowSettings>["standard"]): WorkflowSettings["standard"] {
|
|
668
|
+
const merged = {
|
|
669
|
+
...defaults.standard,
|
|
670
|
+
...(base ?? {}),
|
|
671
|
+
...(override ?? {}),
|
|
672
|
+
subagents: {
|
|
673
|
+
...(defaults.standard.subagents ?? {}),
|
|
674
|
+
...(base?.subagents ?? {}),
|
|
675
|
+
...(override?.subagents ?? {}),
|
|
676
|
+
},
|
|
677
|
+
models: normalizeStandardModels(defaults, base, override),
|
|
678
|
+
};
|
|
679
|
+
return {
|
|
680
|
+
...merged,
|
|
681
|
+
clarificationMode: normalizeStandardClarificationMode(merged.clarificationMode, defaults.standard.clarificationMode ?? "auto"),
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
function normalizeStandardModels(defaults: WorkflowSettings, base?: Partial<WorkflowSettings>["standard"], override?: Partial<WorkflowSettings>["standard"]): Record<WorkflowRole, RoleModelSettings> {
|
|
686
|
+
const defaultModels = defaults.standard.models ?? {
|
|
687
|
+
planner: { enabled: true, provider: null, model: null, thinkingLevel: defaults.models.planner.thinkingLevel },
|
|
688
|
+
executor: { enabled: true, provider: null, model: null, thinkingLevel: defaults.models.executor.thinkingLevel },
|
|
689
|
+
reviewer: { enabled: true, provider: null, model: null, thinkingLevel: defaults.models.reviewer.thinkingLevel },
|
|
690
|
+
validator: { enabled: true, provider: null, model: null, thinkingLevel: defaults.models.validator.thinkingLevel },
|
|
691
|
+
};
|
|
692
|
+
const baseModels: Partial<Record<WorkflowRole, RoleModelSettings>> = base?.models ?? {};
|
|
693
|
+
const overrideModels: Partial<Record<WorkflowRole, RoleModelSettings>> = override?.models ?? {};
|
|
694
|
+
return {
|
|
695
|
+
planner: normalizeRole(defaultModels.planner, { ...(baseModels.planner ?? {}), ...(overrideModels.planner ?? {}) }),
|
|
696
|
+
executor: normalizeRole(defaultModels.executor, { ...(baseModels.executor ?? {}), ...(overrideModels.executor ?? {}) }),
|
|
697
|
+
reviewer: normalizeRole(defaultModels.reviewer, { ...(baseModels.reviewer ?? {}), ...(overrideModels.reviewer ?? {}) }),
|
|
698
|
+
validator: normalizeRole(defaultModels.validator, { ...(baseModels.validator ?? {}), ...(overrideModels.validator ?? {}) }),
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
function normalizeMissionModels(defaults: WorkflowSettings, base?: Partial<WorkflowSettings>["missions"], override?: Partial<WorkflowSettings>["missions"]): Record<MissionModelRole, RoleModelSettings> {
|
|
703
|
+
const defaultModels = defaults.missions.models ?? {
|
|
704
|
+
planner: { enabled: true, provider: null, model: null, thinkingLevel: defaults.models.planner.thinkingLevel },
|
|
705
|
+
executor: { enabled: true, provider: null, model: null, thinkingLevel: defaults.models.executor.thinkingLevel },
|
|
706
|
+
reviewer: { enabled: true, provider: null, model: null, thinkingLevel: defaults.models.reviewer.thinkingLevel },
|
|
707
|
+
validator: { enabled: true, provider: null, model: null, thinkingLevel: defaults.models.validator.thinkingLevel },
|
|
708
|
+
};
|
|
709
|
+
const baseModels: Partial<Record<MissionModelRole, RoleModelSettings>> = base?.models ?? {};
|
|
710
|
+
const overrideModels: Partial<Record<MissionModelRole, RoleModelSettings>> = override?.models ?? {};
|
|
711
|
+
return {
|
|
712
|
+
planner: normalizeRole(defaultModels.planner, { ...(baseModels.planner ?? {}), ...(overrideModels.planner ?? {}) }),
|
|
713
|
+
executor: normalizeRole(defaultModels.executor, { ...(baseModels.executor ?? {}), ...(overrideModels.executor ?? {}) }),
|
|
714
|
+
reviewer: normalizeRole(defaultModels.reviewer, { ...(baseModels.reviewer ?? {}), ...(overrideModels.reviewer ?? {}) }),
|
|
715
|
+
validator: normalizeRole(defaultModels.validator, { ...(baseModels.validator ?? {}), ...(overrideModels.validator ?? {}) }),
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/** Walk up from cwd looking for .pi/workflow-settings.json. Returns path or undefined. */
|
|
720
|
+
export function findProjectSettings(cwd: string): string | undefined {
|
|
721
|
+
let dir = cwd;
|
|
722
|
+
for (let i = 0; i < 40; i++) {
|
|
723
|
+
const candidate = join(dir, ".pi", "workflow-settings.json");
|
|
724
|
+
if (existsSync(candidate)) return candidate;
|
|
725
|
+
const parent = dirname(dir);
|
|
726
|
+
if (parent === dir) break; // filesystem root
|
|
727
|
+
dir = parent;
|
|
728
|
+
}
|
|
729
|
+
return undefined;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/** Resolve the git repo root by walking up looking for .git. Returns root or undefined. */
|
|
733
|
+
function findRepoRoot(cwd: string): string | undefined {
|
|
734
|
+
let dir = cwd;
|
|
735
|
+
for (let i = 0; i < 40; i++) {
|
|
736
|
+
if (existsSync(join(dir, ".git"))) return dir;
|
|
737
|
+
const parent = dirname(dir);
|
|
738
|
+
if (parent === dir) break;
|
|
739
|
+
dir = parent;
|
|
740
|
+
}
|
|
741
|
+
return undefined;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/** Deep merge project overrides into global settings. Project wins on conflicts. */
|
|
745
|
+
function mergeSettings(global: WorkflowSettings, project: Partial<WorkflowSettings>): WorkflowSettings {
|
|
746
|
+
const defaults = defaultWorkflowSettings();
|
|
747
|
+
return {
|
|
748
|
+
...global,
|
|
749
|
+
...project,
|
|
750
|
+
models: {
|
|
751
|
+
planner: normalizeRole(global.models.planner, project.models?.planner),
|
|
752
|
+
executor: normalizeRole(global.models.executor, project.models?.executor),
|
|
753
|
+
validator: normalizeRole(global.models.validator, project.models?.validator),
|
|
754
|
+
reviewer: normalizeRole(global.models.reviewer, project.models?.reviewer),
|
|
755
|
+
},
|
|
756
|
+
planning: { ...defaults.planning, ...global.planning, ...(project.planning ?? {}) },
|
|
757
|
+
workflow: { ...defaults.workflow, ...global.workflow, ...(project.workflow ?? {}) },
|
|
758
|
+
standard: normalizeStandardSettings(defaults, global.standard, project.standard),
|
|
759
|
+
missions: { ...defaults.missions, ...global.missions, ...(project.missions ?? {}), models: normalizeMissionModels(defaults, global.missions, project.missions) },
|
|
760
|
+
safety: { ...defaults.safety, ...global.safety, ...(project.safety ?? {}) },
|
|
761
|
+
ui: normalizeUiSettings({ ...defaults.ui, ...global.ui, ...(project.ui ?? {}) }),
|
|
762
|
+
subagents: { ...defaults.subagents, ...global.subagents, ...(project.subagents ?? {}) },
|
|
763
|
+
context: { ...defaults.context, ...global.context, ...(project.context ?? {}) },
|
|
764
|
+
presets: { ...(defaults.presets ?? {}), ...(global.presets ?? {}), ...(project.presets ?? {}), items: { ...(defaults.presets?.items ?? {}), ...(global.presets?.items ?? {}), ...(project.presets?.items ?? {}) } },
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
export interface EffectiveSettings {
|
|
769
|
+
settings: WorkflowSettings;
|
|
770
|
+
projectOverridePath: string | undefined;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
export interface SettingsWriteResult {
|
|
774
|
+
settings: WorkflowSettings;
|
|
775
|
+
scope: WorkflowSettingsScope;
|
|
776
|
+
file: string;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/** Load effective settings: project overrides merged over global. */
|
|
780
|
+
export function loadEffectiveSettings(cwd: string): EffectiveSettings {
|
|
781
|
+
const global = loadGlobalSettings();
|
|
782
|
+
const projectPath = findProjectSettings(cwd);
|
|
783
|
+
if (!projectPath) return { settings: global, projectOverridePath: undefined };
|
|
784
|
+
try {
|
|
785
|
+
const project = JSON.parse(readFileSync(projectPath, "utf8")) as Partial<WorkflowSettings>;
|
|
786
|
+
return { settings: mergeSettings(global, project), projectOverridePath: projectPath };
|
|
787
|
+
} catch {
|
|
788
|
+
return { settings: global, projectOverridePath: projectPath };
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
/** Load global settings only (no project override). */
|
|
793
|
+
export function loadGlobalSettings(): WorkflowSettings {
|
|
794
|
+
const defaults = defaultWorkflowSettings();
|
|
795
|
+
if (!existsSync(WORKFLOW_SETTINGS_FILE)) {
|
|
796
|
+
saveWorkflowSettings(defaults);
|
|
797
|
+
return defaults;
|
|
798
|
+
}
|
|
799
|
+
const parsed = parseWorkflowSettingsFile(WORKFLOW_SETTINGS_FILE, defaults, "global workflow settings") as Partial<WorkflowSettings>;
|
|
800
|
+
return mergeSettings(defaults, parsed);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/** Backwards-compatible: loads effective settings for a given cwd. */
|
|
804
|
+
export function loadWorkflowSettings(cwd?: string): WorkflowSettings {
|
|
805
|
+
if (cwd) return loadEffectiveSettings(cwd).settings;
|
|
806
|
+
return loadGlobalSettings();
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
/** Get the project root for creating overrides. Prefers git root, falls back to cwd. */
|
|
810
|
+
export function getProjectRoot(cwd: string): string {
|
|
811
|
+
return findRepoRoot(cwd) ?? cwd;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
export function saveWorkflowSettings(settings: WorkflowSettings): void {
|
|
815
|
+
saveSettingsFile(WORKFLOW_SETTINGS_FILE, settings);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
function saveSettingsFile(file: string, settings: WorkflowSettings): void {
|
|
819
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
820
|
+
const content = JSON.stringify(settings, null, 2) + "\n";
|
|
821
|
+
JSON.parse(content);
|
|
822
|
+
const tmp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
823
|
+
writeFileSync(tmp, content, "utf8");
|
|
824
|
+
JSON.parse(readFileSync(tmp, "utf8"));
|
|
825
|
+
renameSync(tmp, file);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
function loadSettingsFile(file: string): WorkflowSettings {
|
|
829
|
+
const defaults = defaultWorkflowSettings();
|
|
830
|
+
if (!existsSync(file)) return defaults;
|
|
831
|
+
const parsed = parseWorkflowSettingsFile(file, defaults, `workflow settings ${file}`) as Partial<WorkflowSettings>;
|
|
832
|
+
return mergeSettings(defaults, parsed);
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
export function getProjectSettingsFile(cwd: string): string {
|
|
836
|
+
return join(getProjectRoot(cwd), ".pi", "workflow-settings.json");
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
export function getDefaultWriteTarget(cwd: string): { scope: WorkflowSettingsScope; file: string } {
|
|
840
|
+
const project = findProjectSettings(cwd);
|
|
841
|
+
if (project) return { scope: "project", file: project };
|
|
842
|
+
return { scope: "global", file: WORKFLOW_SETTINGS_FILE };
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
export function getWriteTarget(cwd: string, requestedScope?: WorkflowSettingsScope): { scope: WorkflowSettingsScope; file: string } {
|
|
846
|
+
if (requestedScope === "global") return { scope: "global", file: WORKFLOW_SETTINGS_FILE };
|
|
847
|
+
if (requestedScope === "project") return { scope: "project", file: getProjectSettingsFile(cwd) };
|
|
848
|
+
return getDefaultWriteTarget(cwd);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
export function createProjectSettingsOverride(cwd: string): SettingsWriteResult {
|
|
852
|
+
const file = getProjectSettingsFile(cwd);
|
|
853
|
+
const settings = loadEffectiveSettings(cwd).settings;
|
|
854
|
+
saveSettingsFile(file, settings);
|
|
855
|
+
return { settings, scope: "project", file };
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
export function updateSettings(cwd: string, requestedScope: WorkflowSettingsScope | undefined, updater: (settings: WorkflowSettings) => void): SettingsWriteResult {
|
|
859
|
+
const target = getWriteTarget(cwd, requestedScope);
|
|
860
|
+
const settings = target.scope === "global" ? loadGlobalSettings() : loadEffectiveSettings(cwd).settings;
|
|
861
|
+
updater(settings);
|
|
862
|
+
saveSettingsFile(target.file, settings);
|
|
863
|
+
return { settings, scope: target.scope, file: target.file };
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
export function builtInWorkflowPresets(): Record<string, WorkflowPresetBundle> {
|
|
867
|
+
return {
|
|
868
|
+
simple: {
|
|
869
|
+
displayName: "Simple",
|
|
870
|
+
description: "Fast end-to-end Plan/Mission/Standard workflow with minimal ceremony, automatic validation when work runs, low safe repair retries, and one-worker sub-agent support.",
|
|
871
|
+
planning: { depth: "fast", clarificationMode: "auto", maxClarificationQuestions: 2, interactiveClarificationEnabled: true, clarificationQualityGate: false, useSubagentsBeforeClarification: true },
|
|
872
|
+
workflow: { offerReviewerBeforeExecute: false, autoRunReviewerBeforeExecute: false, offerValidationAfterExecute: true, autoRunValidationAfterExecute: true, validateAfterExecution: true, requirePlanApprovalBeforeExecute: false, requireApprovalBeforeExecution: false, autoRepairReviewFailures: false, autoRepairValidationFailures: true, reviewRetryMode: "off", validationRetryMode: "safe_only", maxReviewRetriesPerPlan: 0, maxReviewRetriesPerWorkflow: 0, maxValidationRetriesPerPlan: 1, maxValidationRetriesPerWorkflow: 2, pauseAfterReviewFailure: true, pauseAfterValidationFailure: false, planProgressEnabled: true, planRuntimeEnabled: true },
|
|
873
|
+
standard: { enabled: true, autoTodoEnabled: true, todoProgressVisible: true, todoTriggerMode: "auto", clarificationEnabled: true, clarificationMode: "auto", maxClarificationQuestions: 1, interactiveClarificationEnabled: true, clarificationTiming: "after_initial_analysis", clarificationQualityGate: false, allowClarificationWithoutAnalysis: false, useSubagentsBeforeClarification: false, allowSubagents: true, subagentScope: "user", subagents: { planningPolicy: "forced", executionPolicy: "forced", repairPolicy: "forced", reviewPolicy: "auto", validationPolicy: "forced", autoUseDuringPlanning: true, autoUseDuringExecution: true, autoUseDuringRepair: true, autoUseDuringReview: true, autoUseDuringValidation: true, minPlanningWorkersForDeep: 1, minPlanningWorkersForMaximum: 1, minExecutionWorkersForDeep: 1, minExecutionWorkersForMaximum: 1, minRepairWorkersForDeep: 1, minRepairWorkersForMaximum: 1, minReviewWorkersForDeep: 1, minReviewWorkersForMaximum: 1, minValidationWorkersForDeep: 1, minValidationWorkersForMaximum: 1 }, statusWidgetVisible: true, useSharedExecutorModel: true, useStandardSpecificModels: false, modelRole: "executor" },
|
|
874
|
+
missions: { defaultAutonomy: "approval_gated", requireValidationPerMilestone: true, autoRunAfterApproval: true, continueAcrossMilestones: true, pauseBetweenMilestones: false, clarificationMode: "auto", maxClarificationQuestions: 2, planningDepth: "fast", useSubagentsBeforeClarification: true, autoRepairValidationFailures: true, validationRetryMode: "safe_only", maxValidationRetriesPerMilestone: 1, maxValidationRetriesPerMission: 2, finalValidationEnabled: false, autoRepairFinalValidationFailures: false, maxFinalValidationRetries: 0, subagentPolicy: "forced", minWorkersForDeep: 1, minWorkersForMaximum: 1 },
|
|
875
|
+
subagents: { planningPolicy: "forced", executionPolicy: "forced", repairPolicy: "forced", reviewPolicy: "auto", validationPolicy: "forced", autoUseDuringPlanning: true, autoUseDuringExecution: true, autoUseDuringRepair: true, autoUseDuringReview: true, autoUseDuringValidation: true, minPlanningWorkersForDeep: 1, minPlanningWorkersForMaximum: 1, minExecutionWorkersForDeep: 1, minExecutionWorkersForMaximum: 1, minRepairWorkersForDeep: 1, minRepairWorkersForMaximum: 1, minReviewWorkersForDeep: 1, minReviewWorkersForMaximum: 1, minValidationWorkersForDeep: 1, minValidationWorkersForMaximum: 1 },
|
|
876
|
+
},
|
|
877
|
+
standard: {
|
|
878
|
+
displayName: "Standard",
|
|
879
|
+
description: "Default end-to-end workflow with useful clarification, automatic execution/validation after approval, safe repair retries, and balanced worker support.",
|
|
880
|
+
planning: { depth: "standard", clarificationMode: "auto", maxClarificationQuestions: 3, interactiveClarificationEnabled: true, clarificationQualityGate: true, useSubagentsBeforeClarification: true },
|
|
881
|
+
workflow: { offerReviewerBeforeExecute: false, autoRunReviewerBeforeExecute: false, offerValidationAfterExecute: true, autoRunValidationAfterExecute: true, validateAfterExecution: true, requirePlanApprovalBeforeExecute: false, requireApprovalBeforeExecution: false, autoRepairReviewFailures: true, autoRepairValidationFailures: true, reviewRetryMode: "safe_only", validationRetryMode: "safe_only", maxReviewRetriesPerPlan: 2, maxReviewRetriesPerWorkflow: 4, maxValidationRetriesPerPlan: 2, maxValidationRetriesPerWorkflow: 4, pauseAfterReviewFailure: false, pauseAfterValidationFailure: false, planProgressEnabled: true, planRuntimeEnabled: true },
|
|
882
|
+
standard: { enabled: true, autoTodoEnabled: true, todoProgressVisible: true, todoTriggerMode: "auto", clarificationEnabled: true, clarificationMode: "auto", maxClarificationQuestions: 1, interactiveClarificationEnabled: true, clarificationTiming: "after_initial_analysis", clarificationQualityGate: true, allowClarificationWithoutAnalysis: false, useSubagentsBeforeClarification: false, allowSubagents: true, subagentScope: "user", subagents: { planningPolicy: "forced", executionPolicy: "forced", repairPolicy: "forced", reviewPolicy: "forced", validationPolicy: "forced", autoUseDuringPlanning: true, autoUseDuringExecution: true, autoUseDuringRepair: true, autoUseDuringReview: true, autoUseDuringValidation: true, minPlanningWorkersForDeep: 1, minPlanningWorkersForMaximum: 1, minExecutionWorkersForDeep: 2, minExecutionWorkersForMaximum: 2, minRepairWorkersForDeep: 2, minRepairWorkersForMaximum: 2, minReviewWorkersForDeep: 2, minReviewWorkersForMaximum: 2, minValidationWorkersForDeep: 2, minValidationWorkersForMaximum: 2 }, statusWidgetVisible: true, useSharedExecutorModel: true, useStandardSpecificModels: false, modelRole: "executor" },
|
|
883
|
+
missions: { defaultAutonomy: "approval_gated", requireValidationPerMilestone: true, autoRunAfterApproval: true, continueAcrossMilestones: true, pauseBetweenMilestones: false, clarificationMode: "auto", maxClarificationQuestions: 3, planningDepth: "standard", useSubagentsBeforeClarification: true, autoRepairValidationFailures: true, validationRetryMode: "safe_only", maxValidationRetriesPerMilestone: 2, maxValidationRetriesPerMission: 6, finalValidationEnabled: false, autoRepairFinalValidationFailures: false, maxFinalValidationRetries: 1, subagentPolicy: "forced", minWorkersForDeep: 1, minWorkersForMaximum: 1 },
|
|
884
|
+
subagents: { planningPolicy: "forced", executionPolicy: "forced", repairPolicy: "forced", reviewPolicy: "forced", validationPolicy: "forced", autoUseDuringPlanning: true, autoUseDuringExecution: true, autoUseDuringRepair: true, autoUseDuringReview: true, autoUseDuringValidation: true, minPlanningWorkersForDeep: 1, minPlanningWorkersForMaximum: 1, minExecutionWorkersForDeep: 2, minExecutionWorkersForMaximum: 2, minRepairWorkersForDeep: 2, minRepairWorkersForMaximum: 2, minReviewWorkersForDeep: 2, minReviewWorkersForMaximum: 2, minValidationWorkersForDeep: 2, minValidationWorkersForMaximum: 2 },
|
|
885
|
+
},
|
|
886
|
+
deep: {
|
|
887
|
+
displayName: "Deep",
|
|
888
|
+
description: "Careful end-to-end workflow for risky or codebase-heavy work with stronger clarification, automatic review/validation, final mission validation, and larger worker teams.",
|
|
889
|
+
planning: { depth: "deep", clarificationMode: "always_for_nontrivial", maxClarificationQuestions: 5, interactiveClarificationEnabled: true, clarificationQualityGate: true, useSubagentsBeforeClarification: true },
|
|
890
|
+
workflow: { offerReviewerBeforeExecute: false, autoRunReviewerBeforeExecute: true, offerValidationAfterExecute: true, autoRunValidationAfterExecute: true, validateAfterExecution: true, requirePlanApprovalBeforeExecute: false, requireApprovalBeforeExecution: false, autoRepairReviewFailures: true, autoRepairValidationFailures: true, reviewRetryMode: "safe_only", validationRetryMode: "safe_only", maxReviewRetriesPerPlan: 3, maxReviewRetriesPerWorkflow: 6, maxValidationRetriesPerPlan: 3, maxValidationRetriesPerWorkflow: 6, pauseAfterReviewFailure: false, pauseAfterValidationFailure: false, planProgressEnabled: true, planRuntimeEnabled: true },
|
|
891
|
+
standard: { enabled: true, autoTodoEnabled: true, todoProgressVisible: true, todoTriggerMode: "required", clarificationEnabled: true, clarificationMode: "always_for_nontrivial", maxClarificationQuestions: 2, interactiveClarificationEnabled: true, clarificationTiming: "after_initial_analysis", clarificationQualityGate: true, allowClarificationWithoutAnalysis: false, useSubagentsBeforeClarification: true, allowSubagents: true, subagentScope: "user", subagents: { planningPolicy: "forced", executionPolicy: "forced", repairPolicy: "forced", reviewPolicy: "forced", validationPolicy: "forced", autoUseDuringPlanning: true, autoUseDuringExecution: true, autoUseDuringRepair: true, autoUseDuringReview: true, autoUseDuringValidation: true, minPlanningWorkersForDeep: 2, minPlanningWorkersForMaximum: 2, minExecutionWorkersForDeep: 3, minExecutionWorkersForMaximum: 3, minRepairWorkersForDeep: 2, minRepairWorkersForMaximum: 2, minReviewWorkersForDeep: 3, minReviewWorkersForMaximum: 3, minValidationWorkersForDeep: 3, minValidationWorkersForMaximum: 3 }, statusWidgetVisible: true, useSharedExecutorModel: true, useStandardSpecificModels: false, modelRole: "executor" },
|
|
892
|
+
missions: { defaultAutonomy: "approval_gated", requireValidationPerMilestone: true, autoRunAfterApproval: true, continueAcrossMilestones: true, pauseBetweenMilestones: false, clarificationMode: "always_for_nontrivial", maxClarificationQuestions: 5, planningDepth: "deep", useSubagentsBeforeClarification: true, autoRepairValidationFailures: true, validationRetryMode: "safe_only", maxValidationRetriesPerMilestone: 3, maxValidationRetriesPerMission: 8, finalValidationEnabled: true, autoRepairFinalValidationFailures: true, maxFinalValidationRetries: 2, subagentPolicy: "forced", minWorkersForDeep: 3, minWorkersForMaximum: 3 },
|
|
893
|
+
subagents: { planningPolicy: "forced", executionPolicy: "forced", repairPolicy: "forced", reviewPolicy: "forced", validationPolicy: "forced", autoUseDuringPlanning: true, autoUseDuringExecution: true, autoUseDuringRepair: true, autoUseDuringReview: true, autoUseDuringValidation: true, minPlanningWorkersForDeep: 3, minPlanningWorkersForMaximum: 3, minExecutionWorkersForDeep: 3, minExecutionWorkersForMaximum: 3, minRepairWorkersForDeep: 2, minRepairWorkersForMaximum: 2, minReviewWorkersForDeep: 3, minReviewWorkersForMaximum: 3, minValidationWorkersForDeep: 3, minValidationWorkersForMaximum: 3 },
|
|
894
|
+
},
|
|
895
|
+
maximum: {
|
|
896
|
+
displayName: "Maximum",
|
|
897
|
+
description: "Highest-rigor end-to-end workflow with strongest clarification, automatic review/validation, final mission validation, aggressive in-scope repair, and maximum worker teams.",
|
|
898
|
+
planning: { depth: "maximum", clarificationMode: "always_for_nontrivial", maxClarificationQuestions: 5, interactiveClarificationEnabled: true, clarificationQualityGate: true, useSubagentsBeforeClarification: true },
|
|
899
|
+
workflow: { offerReviewerBeforeExecute: false, autoRunReviewerBeforeExecute: true, offerValidationAfterExecute: true, autoRunValidationAfterExecute: true, validateAfterExecution: true, requirePlanApprovalBeforeExecute: false, requireApprovalBeforeExecution: false, autoRepairReviewFailures: true, autoRepairValidationFailures: true, reviewRetryMode: "aggressive_within_scope", validationRetryMode: "aggressive_within_scope", maxReviewRetriesPerPlan: 5, maxReviewRetriesPerWorkflow: 8, maxValidationRetriesPerPlan: 5, maxValidationRetriesPerWorkflow: 8, pauseAfterReviewFailure: false, pauseAfterValidationFailure: false, planProgressEnabled: true, planRuntimeEnabled: true },
|
|
900
|
+
standard: { enabled: true, autoTodoEnabled: true, todoProgressVisible: true, todoTriggerMode: "required", clarificationEnabled: true, clarificationMode: "always_for_nontrivial", maxClarificationQuestions: 2, interactiveClarificationEnabled: true, clarificationTiming: "after_initial_analysis", clarificationQualityGate: true, allowClarificationWithoutAnalysis: false, useSubagentsBeforeClarification: true, allowSubagents: true, subagentScope: "user", subagents: { planningPolicy: "forced", executionPolicy: "forced", repairPolicy: "forced", reviewPolicy: "forced", validationPolicy: "forced", autoUseDuringPlanning: true, autoUseDuringExecution: true, autoUseDuringRepair: true, autoUseDuringReview: true, autoUseDuringValidation: true, minPlanningWorkersForDeep: 3, minPlanningWorkersForMaximum: 3, minExecutionWorkersForDeep: 4, minExecutionWorkersForMaximum: 4, minRepairWorkersForDeep: 3, minRepairWorkersForMaximum: 3, minReviewWorkersForDeep: 4, minReviewWorkersForMaximum: 4, minValidationWorkersForDeep: 4, minValidationWorkersForMaximum: 4 }, statusWidgetVisible: true, useSharedExecutorModel: true, useStandardSpecificModels: false, modelRole: "executor" },
|
|
901
|
+
missions: { defaultAutonomy: "supervised_auto", requireValidationPerMilestone: true, autoRunAfterApproval: true, continueAcrossMilestones: true, pauseBetweenMilestones: false, clarificationMode: "always_for_nontrivial", maxClarificationQuestions: 6, planningDepth: "maximum", useSubagentsBeforeClarification: true, autoRepairValidationFailures: true, validationRetryMode: "aggressive_within_scope", maxValidationRetriesPerMilestone: 4, maxValidationRetriesPerMission: 12, finalValidationEnabled: true, autoRepairFinalValidationFailures: true, maxFinalValidationRetries: 4, subagentPolicy: "forced", minWorkersForDeep: 4, minWorkersForMaximum: 4 },
|
|
902
|
+
subagents: { planningPolicy: "forced", executionPolicy: "forced", repairPolicy: "forced", reviewPolicy: "forced", validationPolicy: "forced", autoUseDuringPlanning: true, autoUseDuringExecution: true, autoUseDuringRepair: true, autoUseDuringReview: true, autoUseDuringValidation: true, minPlanningWorkersForDeep: 4, minPlanningWorkersForMaximum: 4, minExecutionWorkersForDeep: 4, minExecutionWorkersForMaximum: 4, minRepairWorkersForDeep: 3, minRepairWorkersForMaximum: 3, minReviewWorkersForDeep: 4, minReviewWorkersForMaximum: 4, minValidationWorkersForDeep: 4, minValidationWorkersForMaximum: 4 },
|
|
903
|
+
},
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
function normalizeWorkflowPresetBundle(preset: WorkflowPresetBundle): WorkflowPresetBundle & { standard: WorkflowSettings["standard"] } {
|
|
908
|
+
const defaults = defaultWorkflowSettings();
|
|
909
|
+
return {
|
|
910
|
+
...preset,
|
|
911
|
+
// Legacy custom presets saved before Standard Mode do not contain a
|
|
912
|
+
// standard section. Hydrate it from safe defaults during apply so stale
|
|
913
|
+
// Standard settings from the previously active preset cannot leak forward.
|
|
914
|
+
standard: normalizeStandardSettings(defaults, defaults.standard, preset.standard as Partial<WorkflowSettings>["standard"] | undefined),
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
function applyPresetBundle(settings: WorkflowSettings, preset: WorkflowPresetBundle, name: string): void {
|
|
919
|
+
const defaults = defaultWorkflowSettings();
|
|
920
|
+
const normalized = normalizeWorkflowPresetBundle(preset);
|
|
921
|
+
if (normalized.planning) settings.planning = { ...settings.planning, ...normalized.planning };
|
|
922
|
+
if (normalized.workflow) settings.workflow = { ...settings.workflow, ...normalized.workflow };
|
|
923
|
+
settings.standard = { ...defaults.standard, ...normalized.standard, subagents: { ...(defaults.standard.subagents ?? {}), ...(normalized.standard?.subagents ?? {}) }, models: normalizeStandardModels(defaults, settings.standard, preset.standard as Partial<WorkflowSettings>["standard"] | undefined) };
|
|
924
|
+
if (normalized.missions) settings.missions = { ...settings.missions, ...normalized.missions, models: settings.missions.models };
|
|
925
|
+
if (normalized.subagents) settings.subagents = { ...settings.subagents, ...normalized.subagents };
|
|
926
|
+
if (normalized.ui) settings.ui = { ...settings.ui, ...normalized.ui };
|
|
927
|
+
settings.presets = { ...(settings.presets ?? {}), activePreset: name, items: { ...(settings.presets?.items ?? {}) } };
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
export function normalizeWorkflowPresetName(name: string): string {
|
|
931
|
+
return name.trim().toLowerCase().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
export const WORKFLOW_MANUAL_PRESET = "custom";
|
|
935
|
+
|
|
936
|
+
export function workflowPresetCatalog(settings: WorkflowSettings = loadGlobalSettings()): Record<string, WorkflowPresetBundle> {
|
|
937
|
+
const items = { ...(settings.presets?.items ?? {}) };
|
|
938
|
+
delete items[WORKFLOW_MANUAL_PRESET];
|
|
939
|
+
return { ...builtInWorkflowPresets(), ...items };
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
export function workflowPresetNames(settings: WorkflowSettings = loadGlobalSettings()): string[] {
|
|
943
|
+
const builtIns = ["simple", "standard", "deep", "maximum"];
|
|
944
|
+
const custom = Object.keys(settings.presets?.items ?? {}).sort((a, b) => a.localeCompare(b));
|
|
945
|
+
return [...builtIns, ...custom.filter((name) => name !== WORKFLOW_MANUAL_PRESET && !builtIns.includes(name))];
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
export function workflowPresetLabel(name: string, preset?: WorkflowPresetBundle): string {
|
|
949
|
+
if (name === WORKFLOW_MANUAL_PRESET) return "Manual settings";
|
|
950
|
+
return preset?.displayName?.trim() || name;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
export function workflowPresetTitle(name: string, preset?: WorkflowPresetBundle): string {
|
|
954
|
+
if (name === WORKFLOW_MANUAL_PRESET) return "Manual settings — no saved preset active";
|
|
955
|
+
return preset?.displayName?.trim() || name;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
export function activeWorkflowPresetLabel(settings: WorkflowSettings): string {
|
|
959
|
+
const active = settings.presets?.activePreset ?? WORKFLOW_MANUAL_PRESET;
|
|
960
|
+
if (active === WORKFLOW_MANUAL_PRESET) return "Manual settings";
|
|
961
|
+
return workflowPresetLabel(active, workflowPresetCatalog(settings)[active]);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
function assertValidCustomPresetName(name: string, safe: string): void {
|
|
965
|
+
if (safe === WORKFLOW_MANUAL_PRESET) throw new Error(`Reserved workflow preset name: ${name}. Use a specific preset name instead of custom.`);
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
function sectionCoverage(section: unknown): "configured" | "missing" {
|
|
969
|
+
return section && typeof section === "object" && Object.keys(section as Record<string, unknown>).length > 0 ? "configured" : "missing";
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
export function workflowPresetDiagnostics(name: string, preset?: WorkflowPresetBundle): string[] {
|
|
973
|
+
if (name === WORKFLOW_MANUAL_PRESET) return ["Diagnostic: manual settings marker; no saved preset bundle is active."];
|
|
974
|
+
const lines: string[] = [];
|
|
975
|
+
if (normalizeWorkflowPresetName(name) === WORKFLOW_MANUAL_PRESET) lines.push("Diagnostic: reserved-name collision; rename this preset before use.");
|
|
976
|
+
const standard = sectionCoverage(preset?.standard);
|
|
977
|
+
const planning = sectionCoverage(preset?.planning);
|
|
978
|
+
const workflow = sectionCoverage(preset?.workflow);
|
|
979
|
+
const missions = sectionCoverage(preset?.missions);
|
|
980
|
+
const subagents = sectionCoverage(preset?.subagents);
|
|
981
|
+
lines.push(`Coverage: Standard ${standard}; Plan ${planning === "configured" || workflow === "configured" ? "configured" : "missing"}; Mission ${missions}; Shared sub-agents ${subagents}.`);
|
|
982
|
+
if (missions === "configured" && standard === "missing") lines.push("Warning: Mission settings are configured but Standard Mode is missing; safe Standard defaults apply when used.");
|
|
983
|
+
if (missions === "configured" && planning === "missing" && workflow === "missing") lines.push("Warning: Mission settings are configured but Plan Mode is missing; current/default Plan settings may not match this preset's Mission rigor.");
|
|
984
|
+
const missionHigh = preset?.missions?.planningDepth === "maximum" || preset?.missions?.subagentPolicy === "forced" || preset?.missions?.defaultAutonomy === "supervised_auto" || preset?.missions?.defaultAutonomy === "full_auto";
|
|
985
|
+
const planLight = preset?.planning?.depth === "fast" || preset?.workflow?.validateAfterExecution === false || preset?.workflow?.autoRunValidationAfterExecute === false;
|
|
986
|
+
if (missionHigh && planLight) lines.push("Warning: Mission rigor is high while Plan Mode appears lightweight or validation-light.");
|
|
987
|
+
const sharedForced = preset?.subagents?.planningPolicy === "forced" || preset?.subagents?.executionPolicy === "forced" || preset?.subagents?.validationPolicy === "forced";
|
|
988
|
+
const standardLight = preset?.standard?.todoTriggerMode === "off" || preset?.standard?.allowSubagents === false;
|
|
989
|
+
if (sharedForced && standardLight) lines.push("Warning: shared sub-agents are forced while Standard Mode appears lightweight or sub-agent disabled.");
|
|
990
|
+
return lines;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
export function workflowPresetPickerLabel(name: string, preset?: WorkflowPresetBundle): string {
|
|
994
|
+
const title = workflowPresetTitle(name, preset);
|
|
995
|
+
const summaries: Record<string, string> = {
|
|
996
|
+
simple: "fast autonomous validation, low repair retries, 1-worker phases",
|
|
997
|
+
standard: "balanced autonomous validation and safe repair",
|
|
998
|
+
deep: "careful autonomous review, validation, and final mission validation",
|
|
999
|
+
maximum: "maximum autonomous rigor with bounded aggressive repair",
|
|
1000
|
+
};
|
|
1001
|
+
return summaries[name] ? `${title} — ${summaries[name]}` : title;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
export function workflowPresetMeaningLines(name: string, preset?: WorkflowPresetBundle): string[] {
|
|
1005
|
+
if (name === WORKFLOW_MANUAL_PRESET) return [
|
|
1006
|
+
"Name: Manual settings — no saved preset active",
|
|
1007
|
+
"Purpose: uses the current workflow settings exactly as saved; this is not a built-in speed or rigor profile.",
|
|
1008
|
+
"Applies to: the Standard, Plan, Mission, and shared sub-agent settings currently saved in workflow-settings.json.",
|
|
1009
|
+
"Standard Mode: uses the saved Standard Mode To Do, clarification, continuation, and sub-agent settings.",
|
|
1010
|
+
"Plan Mode: uses the saved planning depth, clarification, approval, review, validation, and repair settings.",
|
|
1011
|
+
"Mission Mode: uses the saved mission autonomy, approval, milestone continuation, validation, and retry settings.",
|
|
1012
|
+
"Shared sub-agents: uses the saved planning, execution, repair, review, and validation worker policies.",
|
|
1013
|
+
"Custom preset source: user workflow-settings.json, not package source.",
|
|
1014
|
+
"Sync boundary: live install and main promotion preserve workflow-settings.json; they do not copy, ship, or overwrite custom presets.",
|
|
1015
|
+
"Does not change: models/providers/API keys/auth/session/runtime state/compaction model settings unless those values already exist in user settings.",
|
|
1016
|
+
];
|
|
1017
|
+
const builtIn: Record<string, string[]> = {
|
|
1018
|
+
simple: [
|
|
1019
|
+
"Name: Simple — Fast Autonomous",
|
|
1020
|
+
"Purpose: fastest built-in profile for small or familiar work that should still validate before finishing.",
|
|
1021
|
+
"Applies to: Standard Mode, Plan Mode, Mission Mode, and shared sub-agent intensity.",
|
|
1022
|
+
"Standard Mode: creates a To Do only when useful, asks clarification only when useful, resumes automatically after answers, and uses one-worker phase support.",
|
|
1023
|
+
"Plan Mode: uses fast planning, avoids a second execution-approval stop after the initial approval, skips automatic review, runs lightweight validation, and allows a small safe repair retry budget.",
|
|
1024
|
+
"Mission Mode: starts after approval, auto-runs after approval, continues milestones without pause, keeps milestone validation on, and leaves final comprehensive validation off by default.",
|
|
1025
|
+
"Shared sub-agents: keeps planning, execution, repair, and validation lean with one-worker support; review remains available but is not forced automatically.",
|
|
1026
|
+
"Does not change: models/providers/API keys/auth/session/runtime state/compaction model settings.",
|
|
1027
|
+
],
|
|
1028
|
+
standard: [
|
|
1029
|
+
"Name: Standard — Balanced Autonomous",
|
|
1030
|
+
"Purpose: default built-in profile for normal work that should proceed end-to-end with balanced validation and repair.",
|
|
1031
|
+
"Applies to: Standard Mode, Plan Mode, Mission Mode, and shared sub-agent intensity.",
|
|
1032
|
+
"Standard Mode: creates a To Do for normal substantive work, asks useful clarification, resumes automatically after answers, and uses balanced Standard workers.",
|
|
1033
|
+
"Plan Mode: uses standard planning, avoids a second execution-approval stop after the initial approval, leaves review manual/optional, runs validation automatically, and performs safe repair/revalidation.",
|
|
1034
|
+
"Mission Mode: starts after approval, auto-runs after approval, continues milestones without pause, keeps milestone validation on, and leaves final comprehensive validation off by default.",
|
|
1035
|
+
"Shared sub-agents: uses one-worker planning and two-worker execution, repair, review, and validation when those phases run.",
|
|
1036
|
+
"Does not change: models/providers/API keys/auth/session/runtime state/compaction model settings.",
|
|
1037
|
+
],
|
|
1038
|
+
deep: [
|
|
1039
|
+
"Name: Deep — Careful Autonomous",
|
|
1040
|
+
"Purpose: higher-rigor built-in profile for broad, risky, or codebase-heavy work that still should not stall unnecessarily.",
|
|
1041
|
+
"Applies to: Standard Mode, Plan Mode, Mission Mode, and shared sub-agent intensity.",
|
|
1042
|
+
"Standard Mode: requires a To Do for substantive work, uses stronger clarification, resumes automatically after answers, and uses deeper Standard worker coverage.",
|
|
1043
|
+
"Plan Mode: uses deep planning, clarifies non-trivial work, avoids a second execution-approval stop after the initial approval, runs automatic review and validation, and retries safe repairs.",
|
|
1044
|
+
"Mission Mode: starts after approval, auto-runs after approval, continues milestones without pause, keeps milestone validation on, and enables final comprehensive validation.",
|
|
1045
|
+
"Shared sub-agents: forces larger teams across planning, execution, repair, review, and validation.",
|
|
1046
|
+
"Does not change: models/providers/API keys/auth/session/runtime state/compaction model settings.",
|
|
1047
|
+
],
|
|
1048
|
+
maximum: [
|
|
1049
|
+
"Name: Maximum — Thorough Autonomous",
|
|
1050
|
+
"Purpose: highest-rigor built-in profile for complex or high-risk work within bounded safety limits.",
|
|
1051
|
+
"Applies to: Standard Mode, Plan Mode, Mission Mode, and shared sub-agent intensity.",
|
|
1052
|
+
"Standard Mode: requires a To Do, uses the strongest clarification behavior, resumes automatically after answers, and uses maximum Standard worker coverage.",
|
|
1053
|
+
"Plan Mode: uses maximum planning, avoids a second execution-approval stop after the initial approval, runs automatic review and validation, and uses the largest bounded in-scope repair budget.",
|
|
1054
|
+
"Mission Mode: uses supervised-auto mission autonomy, auto-runs after approval, continues milestones without pause, keeps milestone and final validation on, and uses the highest bounded retry budget.",
|
|
1055
|
+
"Shared sub-agents: forces maximum teams across planning, execution, repair, review, and validation.",
|
|
1056
|
+
"Does not change: models/providers/API keys/auth/session/runtime state/compaction model settings.",
|
|
1057
|
+
],
|
|
1058
|
+
};
|
|
1059
|
+
if (builtIn[name]) return builtIn[name];
|
|
1060
|
+
return [
|
|
1061
|
+
`Name: ${workflowPresetTitle(name, preset)}`,
|
|
1062
|
+
`Purpose: ${preset?.description ?? "custom saved workflow preset."}`,
|
|
1063
|
+
"Applies to: the Standard, Plan, Mission, and shared sub-agent sections stored in this custom preset.",
|
|
1064
|
+
"Custom preset source: user workflow-settings.json, not package source.",
|
|
1065
|
+
"Sync boundary: live install and main promotion preserve workflow-settings.json; they do not copy, ship, or overwrite custom presets.",
|
|
1066
|
+
...(!preset?.standard ? ["Legacy/Incomplete: missing Standard Mode section; safe Standard defaults are applied when this preset is used."] : []),
|
|
1067
|
+
`Standard Mode: To Do ${preset?.standard?.todoTriggerMode ? standardTodoTriggerModeLabel(preset.standard.todoTriggerMode) : "safe defaults"}; clarification ${preset?.standard?.clarificationMode ?? "safe defaults"}; sub-agents ${preset?.standard?.allowSubagents === true ? "enabled" : preset?.standard?.allowSubagents === false ? "disabled" : "safe defaults"}.`,
|
|
1068
|
+
`Plan Mode: ${preset?.planning?.depth ?? "uses current"} planning; clarification ${preset?.planning?.clarificationMode ?? "uses current"}; max questions ${preset?.planning?.maxClarificationQuestions ?? "uses current"}; validation ${(preset?.workflow?.validateAfterExecution ?? preset?.workflow?.autoRunValidationAfterExecute) === true ? "automatic" : (preset?.workflow?.validateAfterExecution ?? preset?.workflow?.autoRunValidationAfterExecute) === false ? "manual/optional" : "uses current setting"}.`,
|
|
1069
|
+
`Mission Mode: autonomy ${preset?.missions?.defaultAutonomy ?? "uses current"}; auto-run after approval ${preset?.missions?.autoRunAfterApproval === true ? "enabled" : preset?.missions?.autoRunAfterApproval === false ? "disabled" : "uses current"}; milestone validation ${preset?.missions?.requireValidationPerMilestone === true ? "on" : preset?.missions?.requireValidationPerMilestone === false ? "off" : "uses current"}; final validation ${preset?.missions?.finalValidationEnabled === true ? "on" : preset?.missions?.finalValidationEnabled === false ? "off" : "uses current"}.`,
|
|
1070
|
+
`Shared sub-agents: planning ${preset?.subagents?.planningPolicy ?? "uses current"}; execution ${preset?.subagents?.executionPolicy ?? "uses current"}; repair ${preset?.subagents?.repairPolicy ?? "uses current"}; review ${preset?.subagents?.reviewPolicy ?? "uses current"}; validation ${preset?.subagents?.validationPolicy ?? "uses current"}.`,
|
|
1071
|
+
...workflowPresetDiagnostics(name, preset),
|
|
1072
|
+
"Does not change: models/providers/API keys/auth/session/runtime state/compaction model settings unless those values already exist in user settings.",
|
|
1073
|
+
];
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
export function renderWorkflowPresetCard(name: string, preset?: WorkflowPresetBundle, active = false): string {
|
|
1077
|
+
return `${active ? "* " : ""}${workflowPresetTitle(name, preset)}${active ? " (active)" : ""}\n${workflowPresetMeaningLines(name, preset).map((line) => ` ${line}`).join("\n")}`;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
export function renderActiveWorkflowPresetSummary(settings: WorkflowSettings): string {
|
|
1081
|
+
const active = settings.presets?.activePreset ?? WORKFLOW_MANUAL_PRESET;
|
|
1082
|
+
const preset = active === WORKFLOW_MANUAL_PRESET ? undefined : workflowPresetCatalog(settings)[active];
|
|
1083
|
+
const lines = workflowPresetMeaningLines(active, preset);
|
|
1084
|
+
lines.push("Effective Settings: detailed current values are listed below.");
|
|
1085
|
+
if (active !== WORKFLOW_MANUAL_PRESET) lines.push(`Reapply Command: /workflow presets apply ${active}`);
|
|
1086
|
+
return lines.join("\n");
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
export function resolveWorkflowPresetName(settings: WorkflowSettings, input: string): string | undefined {
|
|
1090
|
+
const raw = input.trim();
|
|
1091
|
+
if (!raw) return undefined;
|
|
1092
|
+
const catalog = workflowPresetCatalog(settings);
|
|
1093
|
+
if (catalog[raw]) return raw;
|
|
1094
|
+
const lower = raw.toLowerCase();
|
|
1095
|
+
const exactLower = Object.keys(catalog).find((name) => name.toLowerCase() === lower || catalog[name]?.displayName?.toLowerCase() === lower);
|
|
1096
|
+
if (exactLower) return exactLower;
|
|
1097
|
+
const normalized = normalizeWorkflowPresetName(raw);
|
|
1098
|
+
const normalizedMatches = Object.keys(catalog).filter((name) => normalizeWorkflowPresetName(name) === normalized || normalizeWorkflowPresetName(catalog[name]?.displayName ?? "") === normalized);
|
|
1099
|
+
if (normalizedMatches.length === 1) return normalizedMatches[0];
|
|
1100
|
+
return undefined;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
export function renderWorkflowPresets(settings: WorkflowSettings = loadGlobalSettings()): string {
|
|
1104
|
+
const catalog = workflowPresetCatalog(settings);
|
|
1105
|
+
const active = settings.presets?.activePreset ?? WORKFLOW_MANUAL_PRESET;
|
|
1106
|
+
const names = workflowPresetNames(settings);
|
|
1107
|
+
const cards = [renderWorkflowPresetCard(WORKFLOW_MANUAL_PRESET, undefined, active === WORKFLOW_MANUAL_PRESET), ...names.map((name) => renderWorkflowPresetCard(name, catalog[name], name === active))];
|
|
1108
|
+
return `# Workflow Presets\n\nActive Preset: ${activeWorkflowPresetLabel(settings)}\nShortcut: Ctrl+Shift+U cycles saved presets while Plan/Mission/Standard Mode is active\nSelector: /workflow presets\n\n${cards.join("\n\n") || "No presets available."}\n\nQuick commands:\n- /workflow presets list\n- /workflow presets apply <name>\n- /workflow presets next\n- /workflow presets prev\n- /workflow presets manual\n- /workflow presets save <name>\n- /workflow presets create <name> from simple|standard|deep|maximum\n- /workflow presets edit <name>\n- /workflow presets rename <old> to <new>\n- /workflow presets delete <name>\n\nBuilt-in presets are package-defined and sync with the extension. User-named custom presets are saved entries in workflow-settings.json and stay in hotkey cycling. Manual settings is only the no-saved-preset-active marker; live install/main promotion preserves workflow-settings.json and does not ship or overwrite custom presets. Presets adjust workflow behavior only and preserve model/provider choices, API keys, auth/session files, runtime workflow state, and compaction model settings.`;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
export function applyWorkflowPreset(cwd: string, requestedScope: WorkflowSettingsScope | undefined, name: string): SettingsWriteResult {
|
|
1112
|
+
return updateSettings(cwd, requestedScope, (settings) => {
|
|
1113
|
+
const resolved = resolveWorkflowPresetName(settings, name);
|
|
1114
|
+
const preset = resolved ? workflowPresetCatalog(settings)[resolved] : undefined;
|
|
1115
|
+
if (!resolved || !preset) throw new Error(`Unknown workflow preset: ${name}`);
|
|
1116
|
+
applyPresetBundle(settings, preset, resolved);
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
export function saveCurrentWorkflowPreset(cwd: string, requestedScope: WorkflowSettingsScope | undefined, name: string): SettingsWriteResult {
|
|
1121
|
+
const safe = normalizeWorkflowPresetName(name);
|
|
1122
|
+
if (!safe) throw new Error("Preset name is required.");
|
|
1123
|
+
assertValidCustomPresetName(name, safe);
|
|
1124
|
+
return updateSettings(cwd, requestedScope, (settings) => {
|
|
1125
|
+
settings.presets = { ...(settings.presets ?? {}), activePreset: safe, items: { ...(settings.presets?.items ?? {}) } };
|
|
1126
|
+
settings.presets.items![safe] = {
|
|
1127
|
+
displayName: name.trim(),
|
|
1128
|
+
description: "Custom saved workflow preset.",
|
|
1129
|
+
planning: { ...settings.planning },
|
|
1130
|
+
workflow: { ...settings.workflow },
|
|
1131
|
+
standard: { ...settings.standard },
|
|
1132
|
+
missions: { ...settings.missions, models: undefined } as WorkflowPresetBundle["missions"],
|
|
1133
|
+
subagents: { ...settings.subagents },
|
|
1134
|
+
ui: { showWorkflowStatus: settings.ui.showWorkflowStatus, showPlanModeIndicator: settings.ui.showPlanModeIndicator, planTopWidgetVisible: settings.ui.planTopWidgetVisible, missionTopWidgetVisible: settings.ui.missionTopWidgetVisible, missionBottomWidgetVisible: settings.ui.missionBottomWidgetVisible, workflowTheme: settings.ui.workflowTheme, workflowThemeOverrides: settings.ui.workflowThemeOverrides, startupVisual: settings.ui.startupVisual, startupVisualOnSessionStart: settings.ui.startupVisualOnSessionStart, customBrandEnabled: settings.ui.customBrandEnabled, customBrandText: settings.ui.customBrandText },
|
|
1135
|
+
};
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
export function createWorkflowPreset(cwd: string, requestedScope: WorkflowSettingsScope | undefined, name: string, templateName?: string): SettingsWriteResult {
|
|
1140
|
+
const safe = normalizeWorkflowPresetName(name);
|
|
1141
|
+
if (!safe) throw new Error("Preset name is required.");
|
|
1142
|
+
assertValidCustomPresetName(name, safe);
|
|
1143
|
+
return updateSettings(cwd, requestedScope, (settings) => {
|
|
1144
|
+
const resolvedTemplate = templateName ? resolveWorkflowPresetName(settings, templateName) : undefined;
|
|
1145
|
+
const template = resolvedTemplate ? workflowPresetCatalog(settings)[resolvedTemplate] : undefined;
|
|
1146
|
+
if (templateName && !template) throw new Error(`Unknown workflow preset template: ${templateName}`);
|
|
1147
|
+
settings.presets = { ...(settings.presets ?? {}), items: { ...(settings.presets?.items ?? {}) } };
|
|
1148
|
+
settings.presets.items![safe] = template ? { ...template, displayName: name.trim(), description: `Custom preset created from ${resolvedTemplate}.` } : { displayName: name.trim(), description: "Custom workflow preset.", planning: {}, workflow: {}, standard: {}, missions: {}, subagents: {}, ui: {} };
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
export function renameWorkflowPreset(cwd: string, requestedScope: WorkflowSettingsScope | undefined, oldName: string, newName: string): SettingsWriteResult {
|
|
1153
|
+
const safe = normalizeWorkflowPresetName(newName);
|
|
1154
|
+
if (!safe) throw new Error("New preset name is required.");
|
|
1155
|
+
assertValidCustomPresetName(newName, safe);
|
|
1156
|
+
if (builtInWorkflowPresets()[oldName]) throw new Error(`Cannot rename built-in workflow preset: ${oldName}`);
|
|
1157
|
+
return updateSettings(cwd, requestedScope, (settings) => {
|
|
1158
|
+
const resolved = resolveWorkflowPresetName(settings, oldName);
|
|
1159
|
+
if (!resolved || builtInWorkflowPresets()[resolved]) throw new Error(`Cannot rename unknown or built-in workflow preset: ${oldName}`);
|
|
1160
|
+
const items = { ...(settings.presets?.items ?? {}) };
|
|
1161
|
+
const existing = items[resolved];
|
|
1162
|
+
if (!existing) throw new Error(`Unknown workflow preset: ${oldName}`);
|
|
1163
|
+
delete items[resolved];
|
|
1164
|
+
items[safe] = { ...existing, displayName: newName.trim() };
|
|
1165
|
+
settings.presets = { ...(settings.presets ?? {}), activePreset: settings.presets?.activePreset === resolved ? safe : settings.presets?.activePreset, items };
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
export function deleteWorkflowPreset(cwd: string, requestedScope: WorkflowSettingsScope | undefined, name: string): SettingsWriteResult {
|
|
1170
|
+
if (builtInWorkflowPresets()[name]) throw new Error(`Cannot delete built-in workflow preset: ${name}`);
|
|
1171
|
+
return updateSettings(cwd, requestedScope, (settings) => {
|
|
1172
|
+
const resolved = resolveWorkflowPresetName(settings, name);
|
|
1173
|
+
if (!resolved || builtInWorkflowPresets()[resolved]) throw new Error(`Cannot delete unknown or built-in workflow preset: ${name}`);
|
|
1174
|
+
const items = { ...(settings.presets?.items ?? {}) };
|
|
1175
|
+
delete items[resolved];
|
|
1176
|
+
settings.presets = { ...(settings.presets ?? {}), activePreset: settings.presets?.activePreset === resolved ? "custom" : settings.presets?.activePreset, items };
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
export function getModelForRole(role: WorkflowRole): RoleModelSettings {
|
|
1181
|
+
return loadWorkflowSettings().models[role];
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
export function setModelForRole(role: WorkflowRole, provider: string, model: string, cwd?: string, scope?: WorkflowSettingsScope): SettingsWriteResult {
|
|
1185
|
+
return updateSettings(cwd ?? process.cwd(), scope, (settings) => {
|
|
1186
|
+
settings.models[role] = { ...settings.models[role], provider, model };
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
export function setRoleEnabled(role: WorkflowRole, enabled: boolean, cwd?: string, scope?: WorkflowSettingsScope): SettingsWriteResult {
|
|
1191
|
+
return updateSettings(cwd ?? process.cwd(), scope, (settings) => {
|
|
1192
|
+
settings.models[role] = { ...settings.models[role], enabled };
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
export function setThinkingForRole(role: WorkflowRole, thinkingLevel: ThinkingLevel, cwd?: string, scope?: WorkflowSettingsScope): SettingsWriteResult {
|
|
1197
|
+
return updateSettings(cwd ?? process.cwd(), scope, (settings) => {
|
|
1198
|
+
settings.models[role] = { ...settings.models[role], thinkingLevel };
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
export function setStandardModelForRole(role: WorkflowRole, provider: string, model: string, cwd?: string, scope?: WorkflowSettingsScope): SettingsWriteResult {
|
|
1203
|
+
return updateSettings(cwd ?? process.cwd(), scope, (settings) => {
|
|
1204
|
+
const models = settings.standard.models ?? normalizeStandardModels(defaultWorkflowSettings(), settings.standard);
|
|
1205
|
+
models[role] = { ...models[role], enabled: true, provider, model };
|
|
1206
|
+
settings.standard.models = models;
|
|
1207
|
+
settings.standard.useSharedExecutorModel = true;
|
|
1208
|
+
settings.standard.useStandardSpecificModels = true;
|
|
1209
|
+
if (!settings.standard.modelRole || settings.standard.modelRole === "current") settings.standard.modelRole = role;
|
|
1210
|
+
});
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
export function setStandardThinkingForRole(role: WorkflowRole, thinkingLevel: ThinkingLevel, cwd?: string, scope?: WorkflowSettingsScope): SettingsWriteResult {
|
|
1214
|
+
return updateSettings(cwd ?? process.cwd(), scope, (settings) => {
|
|
1215
|
+
const models = settings.standard.models ?? normalizeStandardModels(defaultWorkflowSettings(), settings.standard);
|
|
1216
|
+
models[role] = { ...models[role], thinkingLevel };
|
|
1217
|
+
settings.standard.models = models;
|
|
1218
|
+
settings.standard.useSharedExecutorModel = true;
|
|
1219
|
+
settings.standard.useStandardSpecificModels = true;
|
|
1220
|
+
if (!settings.standard.modelRole || settings.standard.modelRole === "current") settings.standard.modelRole = role;
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
export function setMissionModelForRole(role: MissionModelRole, provider: string, model: string, cwd?: string, scope?: WorkflowSettingsScope): SettingsWriteResult {
|
|
1225
|
+
return updateSettings(cwd ?? process.cwd(), scope, (settings) => {
|
|
1226
|
+
const models = settings.missions.models ?? normalizeMissionModels(defaultWorkflowSettings(), settings.missions);
|
|
1227
|
+
models[role] = { ...models[role], enabled: true, provider, model };
|
|
1228
|
+
settings.missions.models = models;
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
export function setMissionThinkingForRole(role: MissionModelRole, thinkingLevel: ThinkingLevel, cwd?: string, scope?: WorkflowSettingsScope): SettingsWriteResult {
|
|
1233
|
+
return updateSettings(cwd ?? process.cwd(), scope, (settings) => {
|
|
1234
|
+
const models = settings.missions.models ?? normalizeMissionModels(defaultWorkflowSettings(), settings.missions);
|
|
1235
|
+
models[role] = { ...models[role], thinkingLevel };
|
|
1236
|
+
settings.missions.models = models;
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
export function parseRole(value: string): WorkflowRole | undefined {
|
|
1241
|
+
return ["planner", "executor", "validator", "reviewer"].includes(value) ? (value as WorkflowRole) : undefined;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
export function parseMissionModelRole(value: string): MissionModelRole | undefined {
|
|
1245
|
+
return ["planner", "executor", "validator", "reviewer"].includes(value) ? (value as MissionModelRole) : undefined;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
export function parseThinkingLevel(value: string): ThinkingLevel | undefined {
|
|
1249
|
+
return ["off", "minimal", "low", "medium", "high", "xhigh"].includes(value) ? (value as ThinkingLevel) : undefined;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
export function roleIsConfigured(role: RoleModelSettings): boolean {
|
|
1253
|
+
return Boolean(role.provider && role.model);
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
export function effectivePlanApprovalRequired(settings: WorkflowSettings): boolean {
|
|
1257
|
+
return (settings.workflow.requirePlanApprovalBeforeExecute ?? settings.workflow.requireApprovalBeforeExecution) !== false;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
export function effectiveValidateAfterExecution(settings: WorkflowSettings): boolean {
|
|
1261
|
+
return (settings.workflow.validateAfterExecution ?? settings.workflow.autoRunValidationAfterExecute) !== false;
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
export function effectiveReviewAutoRun(settings: WorkflowSettings): boolean {
|
|
1265
|
+
return settings.workflow.autoRunReviewerBeforeExecute === true;
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
export function effectiveValidationAutoRun(settings: WorkflowSettings): boolean {
|
|
1269
|
+
return settings.workflow.autoRunValidationAfterExecute === true && effectiveValidateAfterExecution(settings);
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
export function effectiveRepairGate(settings: WorkflowSettings, gate: RepairRetryGateName): RepairRetryGateSettings {
|
|
1273
|
+
const defaults = settings.workflow.repairRetry?.defaults ?? {};
|
|
1274
|
+
const configured = settings.workflow.repairRetry?.gates?.[gate] ?? {};
|
|
1275
|
+
const fallback: RepairRetryGateSettings = gate === "review"
|
|
1276
|
+
? {
|
|
1277
|
+
autoRepairFailures: settings.workflow.autoRepairReviewFailures,
|
|
1278
|
+
retryMode: settings.workflow.reviewRetryMode,
|
|
1279
|
+
maxRetriesPerItem: settings.workflow.maxReviewRetriesPerPlan,
|
|
1280
|
+
maxRetriesPerWorkflow: settings.workflow.maxReviewRetriesPerWorkflow,
|
|
1281
|
+
pauseAfterFailure: settings.workflow.pauseAfterReviewFailure,
|
|
1282
|
+
}
|
|
1283
|
+
: gate === "validation"
|
|
1284
|
+
? {
|
|
1285
|
+
autoRepairFailures: settings.workflow.autoRepairValidationFailures,
|
|
1286
|
+
retryMode: settings.workflow.validationRetryMode,
|
|
1287
|
+
maxRetriesPerItem: settings.workflow.maxValidationRetriesPerPlan,
|
|
1288
|
+
maxRetriesPerWorkflow: settings.workflow.maxValidationRetriesPerWorkflow,
|
|
1289
|
+
pauseAfterFailure: settings.workflow.pauseAfterValidationFailure,
|
|
1290
|
+
requireApprovalForOutOfScopeRepair: settings.workflow.requireApprovalForOutOfScopeRepair,
|
|
1291
|
+
requireApprovalForDestructiveRepair: settings.workflow.requireApprovalForDestructiveRepair,
|
|
1292
|
+
}
|
|
1293
|
+
: {};
|
|
1294
|
+
return { ...defaults, ...fallback, ...configured };
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
export function workflowSettingsConsistencyDiagnostics(settings: WorkflowSettings): string[] {
|
|
1298
|
+
const diagnostics: string[] = [];
|
|
1299
|
+
const workflow = settings.workflow;
|
|
1300
|
+
if (workflow.validateAfterExecution !== undefined && workflow.validateAfterExecution !== workflow.autoRunValidationAfterExecute) {
|
|
1301
|
+
diagnostics.push(`validation alias mismatch: validateAfterExecution=${workflow.validateAfterExecution} but autoRunValidationAfterExecute=${workflow.autoRunValidationAfterExecute}`);
|
|
1302
|
+
}
|
|
1303
|
+
if (workflow.requireApprovalBeforeExecution !== undefined && workflow.requireApprovalBeforeExecution !== workflow.requirePlanApprovalBeforeExecute) {
|
|
1304
|
+
diagnostics.push(`approval alias mismatch: requireApprovalBeforeExecution=${workflow.requireApprovalBeforeExecution} but requirePlanApprovalBeforeExecute=${workflow.requirePlanApprovalBeforeExecute}`);
|
|
1305
|
+
}
|
|
1306
|
+
const repair = workflow.repairRetry;
|
|
1307
|
+
const validationGate = repair?.gates?.validation;
|
|
1308
|
+
if (validationGate?.autoRepairFailures !== undefined && validationGate.autoRepairFailures !== workflow.autoRepairValidationFailures) {
|
|
1309
|
+
diagnostics.push(`validation repair alias mismatch: repairRetry.gates.validation.autoRepairFailures=${validationGate.autoRepairFailures} but autoRepairValidationFailures=${workflow.autoRepairValidationFailures}`);
|
|
1310
|
+
}
|
|
1311
|
+
const reviewGate = repair?.gates?.review;
|
|
1312
|
+
if (reviewGate?.autoRepairFailures !== undefined && reviewGate.autoRepairFailures !== workflow.autoRepairReviewFailures) {
|
|
1313
|
+
diagnostics.push(`review repair alias mismatch: repairRetry.gates.review.autoRepairFailures=${reviewGate.autoRepairFailures} but autoRepairReviewFailures=${workflow.autoRepairReviewFailures}`);
|
|
1314
|
+
}
|
|
1315
|
+
if (settings.missions.defaultAutonomy === "full_auto" && settings.missions.allowFullAuto !== true) {
|
|
1316
|
+
diagnostics.push("mission autonomy mismatch: defaultAutonomy=full_auto but allowFullAuto=false, so full auto will be blocked");
|
|
1317
|
+
}
|
|
1318
|
+
if (settings.missions.finalValidationEnabled === true && (!settings.models.validator.enabled || !roleIsConfigured(settings.models.validator))) {
|
|
1319
|
+
diagnostics.push("mission final validation is enabled but the shared validator model is disabled or unconfigured");
|
|
1320
|
+
}
|
|
1321
|
+
if (settings.context.compactionMode === "custom_model" && (!settings.context.compactionModelProvider || !settings.context.compactionModel)) {
|
|
1322
|
+
diagnostics.push("custom compaction model mode is selected but compaction provider/model is incomplete");
|
|
1323
|
+
}
|
|
1324
|
+
if (settings.context.customCompactionEnabled === true && settings.context.compactionMode !== "custom_model") {
|
|
1325
|
+
diagnostics.push(`customCompactionEnabled=true but compactionMode=${settings.context.compactionMode}`);
|
|
1326
|
+
}
|
|
1327
|
+
if (settings.missions.watchdogEnabled === true) {
|
|
1328
|
+
diagnostics.push("mission watchdog is enabled but interval/watchdog enforcement is partial in the current MVP");
|
|
1329
|
+
}
|
|
1330
|
+
if (settings.subagents.allowParallelEdits === true || settings.subagents.editConcurrencyMode === "scoped") {
|
|
1331
|
+
diagnostics.push("parallel edit settings are advanced/partial; main workflow writes remain serialized unless scoped conflict protection is implemented");
|
|
1332
|
+
}
|
|
1333
|
+
return diagnostics;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
export function formatRole(role: WorkflowRole, settings = loadWorkflowSettings()): string {
|
|
1337
|
+
const model = settings.models[role];
|
|
1338
|
+
const label = role.charAt(0).toUpperCase() + role.slice(1);
|
|
1339
|
+
if (!model.enabled) return `${label}: disabled`;
|
|
1340
|
+
if (!roleIsConfigured(model)) return `${label}: enabled, not configured`;
|
|
1341
|
+
return `${label}: enabled, ${model.provider}/${model.model}, thinking: ${model.thinkingLevel}`;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
export function renderWorkflowModels(settings: WorkflowSettings): string {
|
|
1345
|
+
return `${formatRole("planner", settings)}\n${formatRole("executor", settings)}\n${formatRole("validator", settings)}\n${formatRole("reviewer", settings)}`;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
export function standardModelSource(settings: WorkflowSettings): StandardModelSource {
|
|
1349
|
+
if (settings.standard.useSharedExecutorModel === false || settings.standard.modelRole === "current") return "current";
|
|
1350
|
+
return settings.standard.useStandardSpecificModels === true ? "standard_specific" : "shared";
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
export function workflowRoleLabel(role: WorkflowRole | StandardModelRole): string {
|
|
1354
|
+
if (role === "current") return "Current Pi model";
|
|
1355
|
+
return role.charAt(0).toUpperCase() + role.slice(1);
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
export function standardModelSourceLabel(source: StandardModelSource): string {
|
|
1359
|
+
if (source === "standard_specific") return "Standard-specific";
|
|
1360
|
+
if (source === "shared") return "Shared";
|
|
1361
|
+
return "Current Pi model";
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
export function compactionModeLabel(mode: CompactionMode): string {
|
|
1365
|
+
if (mode === "pi_default") return "Pi default";
|
|
1366
|
+
if (mode === "custom_model") return "Custom model";
|
|
1367
|
+
if (mode === "custom_agent") return "Custom agent";
|
|
1368
|
+
return "Disabled";
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
export function workflowCompactionCheckModeLabel(mode: WorkflowCompactionCheckMode | undefined): string {
|
|
1372
|
+
return mode === "in_session" ? "In-session" : "Boundary only";
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
export function standardTodoTriggerModeLabel(value?: StandardTodoTriggerMode): string {
|
|
1376
|
+
if (value === "off") return "Disabled";
|
|
1377
|
+
if (value === "manual") return "On request";
|
|
1378
|
+
if (value === "required") return "Required";
|
|
1379
|
+
return "Automatic when useful";
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
function standardRoleRoute(settings: WorkflowSettings, role: WorkflowRole): { source: StandardModelSource; route: RoleModelSettings } {
|
|
1383
|
+
const source = standardModelSource(settings);
|
|
1384
|
+
const standardRoute = settings.standard.models?.[role];
|
|
1385
|
+
if (source === "standard_specific" && standardRoute?.provider && standardRoute?.model) return { source, route: standardRoute };
|
|
1386
|
+
if (source === "standard_specific") return { source: "shared", route: settings.models[role] };
|
|
1387
|
+
if (source === "current") return { source, route: settings.models[role] };
|
|
1388
|
+
return { source: "shared", route: settings.models[role] };
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
export function renderStandardModelStrategy(settings: WorkflowSettings): string {
|
|
1392
|
+
const source = standardModelSource(settings);
|
|
1393
|
+
const role = settings.standard.modelRole ?? "executor";
|
|
1394
|
+
const effectiveRole = role === "current" ? "current" : role;
|
|
1395
|
+
const lines = [`Standard Model Source: ${standardModelSourceLabel(source)}`, `Standard Model Role: ${workflowRoleLabel(effectiveRole)}`];
|
|
1396
|
+
if (effectiveRole === "current" || source === "current") lines.push("Effective Standard Model: Current active Pi model");
|
|
1397
|
+
else {
|
|
1398
|
+
const route = standardRoleRoute(settings, effectiveRole as WorkflowRole);
|
|
1399
|
+
const fallback = source === "standard_specific" && route.source === "shared" ? " (Standard-specific route not configured; falling back to Shared)" : "";
|
|
1400
|
+
lines.push(`Effective Standard Model: ${standardModelSourceLabel(route.source)} ${workflowRoleLabel(effectiveRole as WorkflowRole)} — ${modelLabelForRoute(route.route)}${fallback}`);
|
|
1401
|
+
}
|
|
1402
|
+
lines.push("", "## Standard-Specific Models");
|
|
1403
|
+
const standardModels = settings.standard.models ?? normalizeStandardModels(defaultWorkflowSettings(), settings.standard);
|
|
1404
|
+
lines.push(`Planner: ${modelLabelForRoute(standardModels.planner)}`);
|
|
1405
|
+
lines.push(`Executor: ${modelLabelForRoute(standardModels.executor)}`);
|
|
1406
|
+
lines.push(`Validator: ${modelLabelForRoute(standardModels.validator)}`);
|
|
1407
|
+
lines.push(`Reviewer: ${modelLabelForRoute(standardModels.reviewer)}`);
|
|
1408
|
+
lines.push("", "## Shared Workflow Models", renderWorkflowModels(settings));
|
|
1409
|
+
return lines.join("\n");
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
function modelLabelForRoute(model: RoleModelSettings): string {
|
|
1413
|
+
return roleIsConfigured(model) ? `${model.provider}/${model.model}, thinking: ${model.thinkingLevel}` : `not configured, thinking: ${model.thinkingLevel}`;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
async function applySpecificModel(pi: ExtensionAPI, ctx: ExtensionContext, label: string, route: RoleModelSettings, requireEnabled: boolean): Promise<RoleModelSettings | undefined> {
|
|
1417
|
+
if (requireEnabled && !route.enabled) {
|
|
1418
|
+
ctx.ui.notify(`Workflow: ${label} is disabled. Enable/configure it in /workflow-settings.`, "warning");
|
|
1419
|
+
return undefined;
|
|
1420
|
+
}
|
|
1421
|
+
if (!roleIsConfigured(route)) {
|
|
1422
|
+
ctx.ui.notify(`Workflow: ${label} model is not configured. Use /workflow-settings or /workflow models list.`, "error");
|
|
1423
|
+
return undefined;
|
|
1424
|
+
}
|
|
1425
|
+
if (route.askBeforeRun === true) {
|
|
1426
|
+
if (!ctx.hasUI) {
|
|
1427
|
+
ctx.ui.notify(`Workflow: ${label} askBeforeRun=true but no UI is available to approve the run.`, "warning");
|
|
1428
|
+
return undefined;
|
|
1429
|
+
}
|
|
1430
|
+
const approved = await ctx.ui.confirm(
|
|
1431
|
+
`Run ${label}?`,
|
|
1432
|
+
`Configured model: ${route.provider}/${route.model}\nThinking: ${route.thinkingLevel}\n\nThis role has askBeforeRun=true.`,
|
|
1433
|
+
);
|
|
1434
|
+
if (!approved) {
|
|
1435
|
+
ctx.ui.notify(`Workflow: ${label} run cancelled by askBeforeRun gate.`, "info");
|
|
1436
|
+
return undefined;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
const model = ctx.modelRegistry.find(route.provider!, route.model!);
|
|
1441
|
+
if (!model) {
|
|
1442
|
+
const available = ctx.modelRegistry.getAll().slice(0, 40).map((m) => `${m.provider}/${m.id}`).join("\n");
|
|
1443
|
+
ctx.ui.notify(`Workflow: configured ${label} model not found: ${route.provider}/${route.model}. Use /workflow-settings. Available examples:\n${available}`, "error");
|
|
1444
|
+
return undefined;
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
const ok = await pi.setModel(model);
|
|
1448
|
+
if (!ok) {
|
|
1449
|
+
ctx.ui.notify(`Workflow: no API key for configured ${label} model: ${route.provider}/${route.model}.`, "error");
|
|
1450
|
+
return undefined;
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
pi.setThinkingLevel(route.thinkingLevel);
|
|
1454
|
+
const appliedThinking = pi.getThinkingLevel();
|
|
1455
|
+
if (appliedThinking !== route.thinkingLevel) {
|
|
1456
|
+
ctx.ui.notify(`Workflow: configured ${label} thinking ${route.thinkingLevel} was applied as ${appliedThinking}. Pi may have clamped the level for ${route.provider}/${route.model}.`, "warning");
|
|
1457
|
+
}
|
|
1458
|
+
return route;
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
export async function applyModelForRole(pi: ExtensionAPI, ctx: ExtensionContext, role: WorkflowRole, options: { requireEnabled?: boolean; cwd?: string } = {}): Promise<RoleModelSettings | undefined> {
|
|
1462
|
+
const settings = loadWorkflowSettings(options.cwd);
|
|
1463
|
+
return applySpecificModel(pi, ctx, role, settings.models[role], options.requireEnabled !== false);
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
export async function applyStandardModelForRole(pi: ExtensionAPI, ctx: ExtensionContext, role: WorkflowRole, options: { requireEnabled?: boolean; cwd?: string } = {}): Promise<RoleModelSettings | undefined> {
|
|
1467
|
+
const settings = loadWorkflowSettings(options.cwd);
|
|
1468
|
+
const source = standardModelSource(settings);
|
|
1469
|
+
if (source === "current") return undefined;
|
|
1470
|
+
const route = standardRoleRoute(settings, role);
|
|
1471
|
+
return applySpecificModel(pi, ctx, route.source === "standard_specific" ? `standard ${role}` : role, route.route, options.requireEnabled !== false);
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
export async function applyMissionModelForRole(pi: ExtensionAPI, ctx: ExtensionContext, role: WorkflowRole, options: { requireEnabled?: boolean; cwd?: string } = {}): Promise<RoleModelSettings | undefined> {
|
|
1475
|
+
const settings = loadWorkflowSettings(options.cwd);
|
|
1476
|
+
const missionModels = settings.missions.models;
|
|
1477
|
+
const missionRoute = missionModels?.[role];
|
|
1478
|
+
if (settings.missions.useMissionSpecificModels && missionRoute?.provider && missionRoute?.model) {
|
|
1479
|
+
return applySpecificModel(pi, ctx, `mission ${role}`, missionRoute, options.requireEnabled !== false);
|
|
1480
|
+
}
|
|
1481
|
+
return applyModelForRole(pi, ctx, role, options);
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
// No-op default export so this helper module can be safely auto-discovered as a Pi extension.
|
|
1485
|
+
export default function workflowSuiteNoopExtension(): void {}
|