@mediadatafusion/pi-workflow-suite 0.0.9 → 0.0.11
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 +38 -0
- package/CONTRIBUTING.md +14 -4
- package/README.md +153 -129
- package/SECURITY.md +6 -2
- package/SUPPORT.md +3 -5
- package/VERSION +1 -1
- package/config/prompts/mission-final-validation.md +3 -2
- package/config/prompts/mission-review-prompt.md +42 -0
- package/config/prompts/validate-approved-plan.md +4 -3
- package/config/prompts/workflow-reviewer-prompt.md +44 -0
- package/extensions/subagent/index.ts +69 -3
- package/extensions/subagent/repolock-guard.ts +111 -0
- package/extensions/subagent/runner.ts +51 -3
- package/extensions/workflow-model-router.ts +28 -14
- package/extensions/workflow-modes.ts +1256 -337
- package/extensions/workflow-parsers.ts +2 -1
- package/extensions/workflow-state.ts +64 -6
- package/extensions/workflow-tool-guard.ts +172 -43
- package/extensions/workflow-validation-classifier.ts +5 -2
- package/package.json +5 -3
- package/scripts/install-to-live.sh +2 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
CRITICAL: Call workflow_review_result as your FIRST action in this turn. Do not output any text, analysis, or diagrams before the tool call. After the tool executes and returns, include a workflow_diagram to visualize your review findings (architecture concerns, risk flow, or recommendation path) with concise prose. Place the diagram inline -- not batched at the end.
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
description: Review the mission milestone plan before approval and execution
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are in PI MISSION MODE REVIEWER MODE.
|
|
8
|
+
|
|
9
|
+
Use read-only tools only. Do not edit, write, or run bash. Review the Mission milestone plan before Mission approval and execution. Reviewer is not validation. Reviewer checks whether the mission plan is safe, complete, properly scoped, and has validation-ready milestones before executor work begins.
|
|
10
|
+
|
|
11
|
+
Review checklist:
|
|
12
|
+
- Milestones are parser-safe, ordered, and scoped to the mission goal.
|
|
13
|
+
- Each milestone has clear objective, steps, acceptance criteria, required evidence, and risks.
|
|
14
|
+
- The mission plan does not authorize destructive, secret, auth/session/log/runtime-state, database, deployment, push, or out-of-scope work without explicit approval.
|
|
15
|
+
- Validation strategy is strong enough for per-milestone validation and optional final comprehensive validation.
|
|
16
|
+
- Autonomy and pause/continue behavior are safe for the mission scope.
|
|
17
|
+
- Any repair recommendation must revise the mission plan only; do not execute.
|
|
18
|
+
|
|
19
|
+
Output exactly:
|
|
20
|
+
# Review Report
|
|
21
|
+
## Verdict
|
|
22
|
+
PASS — plan is complete, safe, scoped correctly, and ready for approval.
|
|
23
|
+
NOTES — plan is acceptable but has non-blocking observations for the executor.
|
|
24
|
+
NEEDS REPAIR — plan has concrete gaps that should be repaired before approval (missing requirements, unclear milestones, insufficient validation).
|
|
25
|
+
FAIL — plan has serious issues that block safe execution (missing safety constraints, out-of-scope work, broken dependencies).
|
|
26
|
+
BLOCKED — plan cannot proceed without external resolution (missing credentials, unavailable services, blocked dependencies).
|
|
27
|
+
|
|
28
|
+
Verdict criteria:
|
|
29
|
+
- PASS only when: no repairable issues remain and the plan is ready for approval.
|
|
30
|
+
- NOTES when: plan is sound but has minor observations the executor should consider.
|
|
31
|
+
- NEEDS REPAIR when: milestones lack acceptance criteria, validation plan is weak, scope is unclear, or concrete missing requirements are identified.
|
|
32
|
+
- FAIL when: plan authorizes destructive/secret/auth/database/deploy/push work without explicit approval, or safety constraints are absent.
|
|
33
|
+
- BLOCKED when: plan requires unavailable resources or external dependencies that cannot be resolved by repair.
|
|
34
|
+
## Reason
|
|
35
|
+
## Mission Plan Coverage
|
|
36
|
+
## Milestone Quality
|
|
37
|
+
## Validation Plan Review
|
|
38
|
+
## Safety And Scope Review
|
|
39
|
+
## Missing Requirements
|
|
40
|
+
## Repairable Plan Issues
|
|
41
|
+
## Regression Risks
|
|
42
|
+
## Recommended Next Action
|
|
@@ -13,9 +13,10 @@ Use validation sub-agents aggressively for independent checks, regression review
|
|
|
13
13
|
|
|
14
14
|
Verdict rules:
|
|
15
15
|
- PASS only when the approved plan is fully satisfied with no blocking unresolved risk.
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
- Manual visual-verification caveats alone are not repairable
|
|
16
|
+
- FAIL when concrete missing requirements, unexpected changes, regressions, broken checks, unsafe/out-of-scope work, or concrete code/content/citation/source/file/metadata/artifact fixes remain.
|
|
17
|
+
- PARTIAL PASS is only for manual/visual/browser verification caveats or evidence gaps without a concrete repairable issue.
|
|
18
|
+
- Manual visual-verification caveats alone are not repairable failures; recommend manual QA/revalidation instead of repair.
|
|
19
|
+
- If concrete repairable issues remain in code, content, citations, sources, generated files, indexes, metadata, artifacts, or validation artifacts, mark Concrete Repairable Issue: yes, list them clearly under Missing Requirements or Recommended Next Action, and prefer FAIL over PARTIAL PASS.
|
|
19
20
|
- Evidence gaps are not repairable defects unless a concrete missing requirement or artifact is identified.
|
|
20
21
|
|
|
21
22
|
Mermaid diagrams are rendered by Workflow Suite in a uniform dark-mode visual style. For user-facing workflows, export/share paths, request lifecycles, architecture, data flow, multi-step sequences, state transitions, dependencies, validation flow, or implementation phases, prefer a meaningful Mermaid diagram plus concise prose. Use concise labels and the right diagram type; do not hardcode random style/classDef/light-theme overrides unless the user explicitly asks. Skip diagrams for trivial responses.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
CRITICAL: Call workflow_review_result as your FIRST action in this turn. Do not output any text, analysis, or diagrams before the tool call. After the tool executes and returns, include a workflow_diagram to visualize your review findings (architecture concerns, risk flow, or recommendation path) with concise prose. Place the diagram inline -- not batched at the end.
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
description: Review the approved plan before execution
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
You are in PI WORKFLOW REVIEWER MODE.
|
|
8
|
+
|
|
9
|
+
Use read-only tools only. Do not edit, write, or run bash. Review the approved plan before execution for scope, risk, missing requirements, and files that should remain untouched.
|
|
10
|
+
|
|
11
|
+
Reviewer is not validation. Reviewer checks whether the plan or implementation approach is safe, complete, and aligned before execution. Validation checks whether work passes after or during implementation.
|
|
12
|
+
|
|
13
|
+
Review checklist:
|
|
14
|
+
- Plan scope is clear, bounded, and aligned with the user's request.
|
|
15
|
+
- Implementation steps are ordered correctly with no circular dependencies.
|
|
16
|
+
- Required files are identified and files to avoid are listed.
|
|
17
|
+
- Validation strategy covers all deliverables with concrete acceptance criteria.
|
|
18
|
+
- Risk assessment covers security, data loss, breaking changes, and deployment concerns.
|
|
19
|
+
- The plan does not authorize destructive, secret, auth/session/log/runtime-state, database, deployment, push, or out-of-scope work without explicit approval.
|
|
20
|
+
- Test and build verification is included where applicable.
|
|
21
|
+
|
|
22
|
+
Output exactly:
|
|
23
|
+
# Reviewer Report
|
|
24
|
+
## Verdict
|
|
25
|
+
PASS — plan is complete, safe, properly scoped, and ready for execution.
|
|
26
|
+
NOTES — plan is sound with non-blocking observations for the executor.
|
|
27
|
+
NEEDS REPAIR — plan has concrete gaps (missing steps, unclear files, weak validation, scope creep, risks not addressed).
|
|
28
|
+
FAIL — plan has serious blockers (safety violations, missing security constraints, broken dependencies, impossible steps).
|
|
29
|
+
BLOCKED — plan cannot proceed without external resolution.
|
|
30
|
+
|
|
31
|
+
Do not write APPROVED, APPROVE, OK, or PROCEED as the verdict label.
|
|
32
|
+
|
|
33
|
+
Verdict criteria:
|
|
34
|
+
- PASS only when: all checklist items are satisfied and no repairable issues remain.
|
|
35
|
+
- NOTES when: minor observations exist (suggested file order, additional test ideas, optional improvements).
|
|
36
|
+
- NEEDS REPAIR when: concrete missing requirements, unclear scope boundaries, insufficient validation, or unaddressed risks.
|
|
37
|
+
- FAIL when: safety/security violations, circular dependencies, impossible steps, or work that exceeds approved scope without authorization.
|
|
38
|
+
- BLOCKED when: plan requires unavailable resources or external dependencies that cannot be resolved by repair.
|
|
39
|
+
## Reason
|
|
40
|
+
## Scope Risks
|
|
41
|
+
## Missing Information
|
|
42
|
+
## Files To Be Careful With
|
|
43
|
+
## Required Plan Revisions
|
|
44
|
+
## Recommended Execution Notes
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* Uses JSON mode to capture structured output from subagents.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import { spawn } from "node:child_process";
|
|
18
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
19
19
|
import { createRequire } from "node:module";
|
|
20
20
|
import * as fs from "node:fs";
|
|
21
21
|
import * as os from "node:os";
|
|
@@ -109,6 +109,42 @@ class SafeContainer {
|
|
|
109
109
|
const MAX_PARALLEL_TASKS = 8;
|
|
110
110
|
const MAX_CONCURRENCY = 4;
|
|
111
111
|
const COLLAPSED_ITEM_COUNT = 10;
|
|
112
|
+
const REPOLOCK_GUARD_EXTENSION = path.join(path.dirname(new URL(import.meta.url).pathname), "repolock-guard.ts");
|
|
113
|
+
|
|
114
|
+
function safeRealpath(candidate: string): string {
|
|
115
|
+
try {
|
|
116
|
+
return fs.realpathSync(candidate);
|
|
117
|
+
} catch {
|
|
118
|
+
return candidate;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function pathInsideRoot(candidate: string, root: string): boolean {
|
|
123
|
+
return candidate === root || candidate.startsWith(`${root}${path.sep}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function resolveSubagentCwd(candidate: string | undefined, defaultCwd: string): string {
|
|
127
|
+
return safeRealpath(path.resolve(defaultCwd, candidate || "."));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function repoRootForCwd(cwd: string): string {
|
|
131
|
+
try {
|
|
132
|
+
const root = execFileSync("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
133
|
+
return safeRealpath(root || cwd);
|
|
134
|
+
} catch {
|
|
135
|
+
return safeRealpath(cwd);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function repoLockRootForSubagent(defaultCwd: string, settings: ReturnType<typeof loadWorkflowSettings>): string | undefined {
|
|
140
|
+
if (process.env.PI_WORKFLOW_REPO_LOCK_ENABLED === "1" && process.env.PI_WORKFLOW_REPO_LOCK_ROOT) return safeRealpath(process.env.PI_WORKFLOW_REPO_LOCK_ROOT);
|
|
141
|
+
return settings.safety.repoLockEnabled === true ? repoRootForCwd(defaultCwd) : undefined;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function repoLockCwdError(candidate: string | undefined, defaultCwd: string, root: string): string | undefined {
|
|
145
|
+
const resolved = resolveSubagentCwd(candidate, defaultCwd);
|
|
146
|
+
return pathInsideRoot(resolved, root) ? undefined : `Repo Lock blocked sub-agent cwd outside current repository: ${resolved} (repo root: ${root})`;
|
|
147
|
+
}
|
|
112
148
|
|
|
113
149
|
function formatTokens(count: number): string {
|
|
114
150
|
if (count < 1000) return count.toString();
|
|
@@ -345,7 +381,23 @@ async function runSingleAgent(
|
|
|
345
381
|
};
|
|
346
382
|
}
|
|
347
383
|
|
|
348
|
-
const
|
|
384
|
+
const settings = loadWorkflowSettings(defaultCwd);
|
|
385
|
+
const lockRoot = repoLockRootForSubagent(defaultCwd, settings);
|
|
386
|
+
const effectiveCwd = resolveSubagentCwd(cwd, defaultCwd);
|
|
387
|
+
if (lockRoot && !pathInsideRoot(effectiveCwd, lockRoot)) {
|
|
388
|
+
return {
|
|
389
|
+
agent: agentName,
|
|
390
|
+
agentSource: agent.source,
|
|
391
|
+
task,
|
|
392
|
+
exitCode: 1,
|
|
393
|
+
messages: [],
|
|
394
|
+
stderr: `Repo Lock blocked sub-agent cwd outside current repository: ${effectiveCwd} (repo root: ${lockRoot})`,
|
|
395
|
+
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
|
|
396
|
+
step,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const args: string[] = ["--no-extensions", "--extension", REPOLOCK_GUARD_EXTENSION, "--mode", "json", "-p", "--no-session"];
|
|
349
401
|
if (agent.model) args.push("--model", agent.model);
|
|
350
402
|
if (agent.tools && agent.tools.length > 0) args.push("--tools", agent.tools.join(","));
|
|
351
403
|
|
|
@@ -390,13 +442,14 @@ async function runSingleAgent(
|
|
|
390
442
|
const exitCode = await new Promise<number>((resolve) => {
|
|
391
443
|
const invocation = getPiInvocation(args);
|
|
392
444
|
const proc = spawn(invocation.command, invocation.args, {
|
|
393
|
-
cwd:
|
|
445
|
+
cwd: effectiveCwd,
|
|
394
446
|
shell: false,
|
|
395
447
|
stdio: ["ignore", "pipe", "pipe"],
|
|
396
448
|
env: {
|
|
397
449
|
...process.env,
|
|
398
450
|
PI_SUBAGENT_WORKER: "1",
|
|
399
451
|
PI_SUBAGENT_NAME: agent.name,
|
|
452
|
+
...(lockRoot ? { PI_WORKFLOW_REPO_LOCK_ENABLED: "1", PI_WORKFLOW_REPO_LOCK_ROOT: lockRoot } : {}),
|
|
400
453
|
},
|
|
401
454
|
});
|
|
402
455
|
let buffer = "";
|
|
@@ -562,6 +615,19 @@ export default function (pi: ExtensionAPI) {
|
|
|
562
615
|
|
|
563
616
|
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
564
617
|
const settings = loadWorkflowSettings(ctx.cwd);
|
|
618
|
+
const lockRoot = repoLockRootForSubagent(ctx.cwd, settings);
|
|
619
|
+
if (lockRoot) {
|
|
620
|
+
const cwdErrors = [params.cwd, ...(params.tasks ?? []).map((task) => task.cwd), ...(params.chain ?? []).map((step) => step.cwd)]
|
|
621
|
+
.map((cwdValue) => repoLockCwdError(cwdValue, ctx.cwd, lockRoot))
|
|
622
|
+
.filter((error): error is string => Boolean(error));
|
|
623
|
+
if (cwdErrors.length) {
|
|
624
|
+
return {
|
|
625
|
+
content: [{ type: "text", text: cwdErrors[0] }],
|
|
626
|
+
details: { mode: "single", agentScope: params.agentScope ?? "user", projectAgentsDir: null, results: [] },
|
|
627
|
+
isError: true,
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
}
|
|
565
631
|
const subagentLimits = { timeoutMinutes: settings.subagents.subagentTimeoutMinutes, staleMinutes: settings.subagents.subagentStaleMinutes };
|
|
566
632
|
if (settings.subagents.requireApprovalBeforeRun === true) {
|
|
567
633
|
const requested = params.tasks?.length
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, resolve } from "node:path";
|
|
3
|
+
import { getAgentDir, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
|
|
5
|
+
const PATH_SCOPED_TOOLS = new Set(["read", "grep", "find", "ls", "edit", "write"]);
|
|
6
|
+
|
|
7
|
+
function safeRealpath(path: string): string {
|
|
8
|
+
try {
|
|
9
|
+
return realpathSync(path);
|
|
10
|
+
} catch {
|
|
11
|
+
return path;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function resolveCandidatePath(pathValue: string, cwd: string): string {
|
|
16
|
+
const expanded = pathValue === "~" || pathValue.startsWith("~/") ? resolve(process.env.HOME || cwd, pathValue.slice(2)) : pathValue;
|
|
17
|
+
const resolved = isAbsolute(expanded) ? resolve(expanded) : resolve(cwd, expanded || ".");
|
|
18
|
+
if (existsSync(resolved)) return safeRealpath(resolved);
|
|
19
|
+
const existingParent = safeRealpath(resolve(resolved, ".."));
|
|
20
|
+
return resolve(existingParent, resolved.split(/[\\/]/).pop() || "");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function pathInsideRoot(candidate: string, root: string): boolean {
|
|
24
|
+
return candidate === root || candidate.startsWith(`${root}/`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function protectedRepoPath(candidate: string, root: string): boolean {
|
|
28
|
+
const rel = candidate === root ? "" : candidate.slice(root.length + 1);
|
|
29
|
+
return rel === ".pi" || rel.startsWith(".pi/");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function piRuntimeInstructionPath(candidate: string): boolean {
|
|
33
|
+
const root = safeRealpath(getAgentDir());
|
|
34
|
+
if (!pathInsideRoot(candidate, root)) return false;
|
|
35
|
+
const rel = candidate === root ? "" : candidate.slice(root.length + 1);
|
|
36
|
+
return rel === "skills" || rel.startsWith("skills/")
|
|
37
|
+
|| rel === "agents" || rel.startsWith("agents/")
|
|
38
|
+
|| rel === "config/prompts" || rel.startsWith("config/prompts/")
|
|
39
|
+
|| rel === "prompts" || rel.startsWith("prompts/")
|
|
40
|
+
|| rel === "themes" || rel.startsWith("themes/");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function repoLockPathBlock(pathValue: unknown, cwd: string, tool: string): string | undefined {
|
|
44
|
+
if (process.env.PI_WORKFLOW_REPO_LOCK_ENABLED !== "1") return undefined;
|
|
45
|
+
const root = safeRealpath(process.env.PI_WORKFLOW_REPO_LOCK_ROOT || cwd);
|
|
46
|
+
const candidate = resolveCandidatePath(typeof pathValue === "string" && pathValue.trim() ? pathValue.trim() : ".", cwd);
|
|
47
|
+
if (!pathInsideRoot(candidate, root)) {
|
|
48
|
+
if ((tool === "read" || tool === "grep" || tool === "find" || tool === "ls") && piRuntimeInstructionPath(candidate)) return undefined;
|
|
49
|
+
return `Repo Lock blocked sub-agent path outside current repository: ${candidate} (repo root: ${root})`;
|
|
50
|
+
}
|
|
51
|
+
if ((tool === "edit" || tool === "write") && protectedRepoPath(candidate, root)) return `Repo Lock blocked sub-agent ${tool} for protected project control path: ${candidate}`;
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function stripHereDocBodies(command: string): string {
|
|
56
|
+
const lines = command.split("\n");
|
|
57
|
+
const kept: string[] = [];
|
|
58
|
+
for (let i = 0; i < lines.length; i++) {
|
|
59
|
+
const line = lines[i];
|
|
60
|
+
kept.push(line);
|
|
61
|
+
const match = line.match(/<<[-]?\s*['\"]?([A-Za-z_][A-Za-z0-9_]*)['\"]?/);
|
|
62
|
+
if (!match) continue;
|
|
63
|
+
const marker = match[1];
|
|
64
|
+
i++;
|
|
65
|
+
while (i < lines.length && lines[i].trim() !== marker) i++;
|
|
66
|
+
}
|
|
67
|
+
return kept.join("\n");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function stripUriTokens(command: string): string {
|
|
71
|
+
return command.replace(/\b[A-Za-z][A-Za-z0-9+.-]*:\/\/[^\s'"`;&|)]*/g, " ");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function bashPathCandidates(command: string): string[] {
|
|
75
|
+
const trimmed = stripUriTokens(stripHereDocBodies(command)).trim();
|
|
76
|
+
if (!trimmed) return [];
|
|
77
|
+
return Array.from(trimmed.matchAll(/(?:^|[\s=:'"`])((?:\.{1,2}|~|\/)[^\s'"`;&|)]*)/g)).map((match) => match[1]).filter(Boolean);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function repoLockBashBlock(command: string, cwd: string): string | undefined {
|
|
81
|
+
if (process.env.PI_WORKFLOW_REPO_LOCK_ENABLED !== "1") return undefined;
|
|
82
|
+
const root = safeRealpath(process.env.PI_WORKFLOW_REPO_LOCK_ROOT || cwd);
|
|
83
|
+
const candidates = bashPathCandidates(command);
|
|
84
|
+
for (const raw of candidates) {
|
|
85
|
+
if (raw === "." || raw === "./" || raw === "/") continue;
|
|
86
|
+
const cleaned = raw.replace(/[),]+$/, "");
|
|
87
|
+
if (!cleaned || cleaned.startsWith("./node_modules/.bin")) continue;
|
|
88
|
+
const candidate = resolveCandidatePath(cleaned, cwd);
|
|
89
|
+
if (!pathInsideRoot(candidate, root)) return `Repo Lock blocked sub-agent bash path outside current repository: ${cleaned} -> ${candidate} (repo root: ${root})`;
|
|
90
|
+
}
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export default function repoLockSubagentGuard(pi: ExtensionAPI): void {
|
|
95
|
+
pi.on("tool_call", (event, ctx) => {
|
|
96
|
+
if (PATH_SCOPED_TOOLS.has(event.toolName)) {
|
|
97
|
+
const reason = repoLockPathBlock((event.input as { path?: unknown; file_path?: unknown }).path ?? (event.input as { file_path?: unknown }).file_path, ctx.cwd, event.toolName);
|
|
98
|
+
if (reason) return { block: true, reason };
|
|
99
|
+
}
|
|
100
|
+
if (event.toolName === "bash") {
|
|
101
|
+
const command = String((event.input as { command?: unknown }).command ?? "");
|
|
102
|
+
const reason = repoLockBashBlock(command, ctx.cwd);
|
|
103
|
+
if (reason) return { block: true, reason };
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
pi.on("user_bash", (event, ctx) => {
|
|
108
|
+
const reason = repoLockBashBlock(event.command, ctx.cwd);
|
|
109
|
+
if (reason) return { result: { output: reason, exitCode: 1, cancelled: false, truncated: false } };
|
|
110
|
+
});
|
|
111
|
+
}
|
|
@@ -5,12 +5,13 @@
|
|
|
5
5
|
* sub-agent policy before the main planner/executor/reviewer/validator turn.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { spawn } from "node:child_process";
|
|
8
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
9
9
|
import * as fs from "node:fs";
|
|
10
10
|
import * as os from "node:os";
|
|
11
11
|
import * as path from "node:path";
|
|
12
12
|
import type { Message } from "@earendil-works/pi-ai";
|
|
13
13
|
import { withFileMutationQueue } from "@earendil-works/pi-coding-agent";
|
|
14
|
+
import { loadWorkflowSettings } from "../workflow-model-router.js";
|
|
14
15
|
import { type AgentConfig, type AgentScope, type AgentSource, discoverAgents } from "./agents.js";
|
|
15
16
|
|
|
16
17
|
export interface WorkflowSubagentTask {
|
|
@@ -60,6 +61,37 @@ export interface WorkflowSubagentRunOptions {
|
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
const MAX_CONCURRENCY = 4;
|
|
64
|
+
const REPOLOCK_GUARD_EXTENSION = path.join(path.dirname(new URL(import.meta.url).pathname), "repolock-guard.ts");
|
|
65
|
+
|
|
66
|
+
function safeRealpath(candidate: string): string {
|
|
67
|
+
try {
|
|
68
|
+
return fs.realpathSync(candidate);
|
|
69
|
+
} catch {
|
|
70
|
+
return candidate;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function pathInsideRoot(candidate: string, root: string): boolean {
|
|
75
|
+
return candidate === root || candidate.startsWith(`${root}${path.sep}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function resolveSubagentCwd(candidate: string | undefined, defaultCwd: string): string {
|
|
79
|
+
return safeRealpath(path.resolve(defaultCwd, candidate || "."));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function repoRootForCwd(cwd: string): string {
|
|
83
|
+
try {
|
|
84
|
+
const root = execFileSync("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
85
|
+
return safeRealpath(root || cwd);
|
|
86
|
+
} catch {
|
|
87
|
+
return safeRealpath(cwd);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function repoLockRootForSubagent(defaultCwd: string): string | undefined {
|
|
92
|
+
if (process.env.PI_WORKFLOW_REPO_LOCK_ENABLED === "1" && process.env.PI_WORKFLOW_REPO_LOCK_ROOT) return safeRealpath(process.env.PI_WORKFLOW_REPO_LOCK_ROOT);
|
|
93
|
+
return loadWorkflowSettings(defaultCwd).safety.repoLockEnabled === true ? repoRootForCwd(defaultCwd) : undefined;
|
|
94
|
+
}
|
|
63
95
|
|
|
64
96
|
function finalOutput(messages: Message[]): string {
|
|
65
97
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -132,7 +164,22 @@ async function runSingleWorkflowSubagent(
|
|
|
132
164
|
};
|
|
133
165
|
}
|
|
134
166
|
|
|
135
|
-
const
|
|
167
|
+
const lockRoot = repoLockRootForSubagent(defaultCwd);
|
|
168
|
+
const effectiveCwd = resolveSubagentCwd(task.cwd, defaultCwd);
|
|
169
|
+
if (lockRoot && !pathInsideRoot(effectiveCwd, lockRoot)) {
|
|
170
|
+
return {
|
|
171
|
+
agent: task.agent,
|
|
172
|
+
agentSource: agent.source,
|
|
173
|
+
agentTools: agent.tools,
|
|
174
|
+
task: task.task,
|
|
175
|
+
exitCode: 1,
|
|
176
|
+
output: "",
|
|
177
|
+
stderr: `Repo Lock blocked sub-agent cwd outside current repository: ${effectiveCwd} (repo root: ${lockRoot})`,
|
|
178
|
+
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const args: string[] = ["--no-extensions", "--extension", REPOLOCK_GUARD_EXTENSION, "--mode", "json", "-p", "--no-session"];
|
|
136
183
|
if (agent.model) args.push("--model", agent.model);
|
|
137
184
|
if (agent.tools && agent.tools.length > 0) args.push("--tools", agent.tools.join(","));
|
|
138
185
|
|
|
@@ -162,13 +209,14 @@ async function runSingleWorkflowSubagent(
|
|
|
162
209
|
const exitCode = await new Promise<number>((resolve) => {
|
|
163
210
|
const invocation = getPiInvocation(args);
|
|
164
211
|
const proc = spawn(invocation.command, invocation.args, {
|
|
165
|
-
cwd:
|
|
212
|
+
cwd: effectiveCwd,
|
|
166
213
|
shell: false,
|
|
167
214
|
stdio: ["ignore", "pipe", "pipe"],
|
|
168
215
|
env: {
|
|
169
216
|
...process.env,
|
|
170
217
|
PI_SUBAGENT_WORKER: "1",
|
|
171
218
|
PI_SUBAGENT_NAME: agent.name,
|
|
219
|
+
...(lockRoot ? { PI_WORKFLOW_REPO_LOCK_ENABLED: "1", PI_WORKFLOW_REPO_LOCK_ROOT: lockRoot } : {}),
|
|
172
220
|
},
|
|
173
221
|
});
|
|
174
222
|
let buffer = "";
|
|
@@ -106,6 +106,8 @@ export interface WorkflowSettings {
|
|
|
106
106
|
clarificationQualityGate?: boolean;
|
|
107
107
|
allowClarificationWithoutAnalysis?: boolean;
|
|
108
108
|
useSubagentsBeforeClarification?: boolean;
|
|
109
|
+
maxTokens?: number;
|
|
110
|
+
maxRuntimeHours?: number;
|
|
109
111
|
};
|
|
110
112
|
workflow: {
|
|
111
113
|
requirePlanApprovalBeforeExecute: boolean;
|
|
@@ -138,6 +140,7 @@ export interface WorkflowSettings {
|
|
|
138
140
|
planHistoryLimit?: number;
|
|
139
141
|
planProgressEnabled?: boolean;
|
|
140
142
|
planRuntimeEnabled?: boolean;
|
|
143
|
+
planShowProgressBar?: boolean;
|
|
141
144
|
};
|
|
142
145
|
standard: {
|
|
143
146
|
enabled: boolean;
|
|
@@ -160,11 +163,13 @@ export interface WorkflowSettings {
|
|
|
160
163
|
useStandardSpecificModels?: boolean;
|
|
161
164
|
modelRole?: StandardModelRole;
|
|
162
165
|
models?: Record<WorkflowRole, RoleModelSettings>;
|
|
166
|
+
maxTokens?: number;
|
|
163
167
|
};
|
|
164
168
|
missions: {
|
|
165
169
|
enabled: boolean;
|
|
166
170
|
defaultAutonomy: MissionAutonomy;
|
|
167
171
|
maxRuntimeHours: number;
|
|
172
|
+
maxTokens?: number;
|
|
168
173
|
checkpointIntervalMinutes: number;
|
|
169
174
|
requireApprovalForDestructiveActions: boolean;
|
|
170
175
|
requireValidationPerMilestone: boolean;
|
|
@@ -214,6 +219,7 @@ export interface WorkflowSettings {
|
|
|
214
219
|
disableBashInPlanMode: boolean;
|
|
215
220
|
disableBashInValidatorMode: boolean;
|
|
216
221
|
blockDestructiveCommands: boolean;
|
|
222
|
+
allowPackageInstallInExecution: boolean;
|
|
217
223
|
};
|
|
218
224
|
ui: {
|
|
219
225
|
showWorkflowStatus: boolean;
|
|
@@ -242,6 +248,7 @@ export interface WorkflowSettings {
|
|
|
242
248
|
customBrandEnabled?: boolean;
|
|
243
249
|
customBrandText?: string;
|
|
244
250
|
customBrandBaseVisual?: CustomBrandBaseVisual;
|
|
251
|
+
debugPlanStepTracking?: boolean;
|
|
245
252
|
};
|
|
246
253
|
subagents: WorkflowSubagentSettings;
|
|
247
254
|
context: {
|
|
@@ -365,6 +372,7 @@ const BUILTIN_DEFAULT_WORKFLOW_SETTINGS = {
|
|
|
365
372
|
"planHistoryLimit": 50,
|
|
366
373
|
"planProgressEnabled": true,
|
|
367
374
|
"planRuntimeEnabled": true,
|
|
375
|
+
"planShowProgressBar": true,
|
|
368
376
|
"requireApprovalBeforeExecution": true,
|
|
369
377
|
"requireApprovalPerStep": false,
|
|
370
378
|
"validateAfterEachStep": false,
|
|
@@ -415,7 +423,8 @@ const BUILTIN_DEFAULT_WORKFLOW_SETTINGS = {
|
|
|
415
423
|
"model": null,
|
|
416
424
|
"thinkingLevel": "xhigh"
|
|
417
425
|
}
|
|
418
|
-
}
|
|
426
|
+
},
|
|
427
|
+
"maxTokens": 0
|
|
419
428
|
},
|
|
420
429
|
"missions": {
|
|
421
430
|
"enabled": true,
|
|
@@ -429,9 +438,9 @@ const BUILTIN_DEFAULT_WORKFLOW_SETTINGS = {
|
|
|
429
438
|
"autoRunAfterApproval": true,
|
|
430
439
|
"offerReviewerBeforeApprove": false,
|
|
431
440
|
"autoRunReviewerBeforeApprove": false,
|
|
432
|
-
"autoRepairReviewFailures":
|
|
433
|
-
"reviewRetryMode": "
|
|
434
|
-
"maxReviewRetriesPerMission":
|
|
441
|
+
"autoRepairReviewFailures": true,
|
|
442
|
+
"reviewRetryMode": "safe_only",
|
|
443
|
+
"maxReviewRetriesPerMission": 2,
|
|
435
444
|
"continueAcrossMilestones": true,
|
|
436
445
|
"pauseBetweenMilestones": false,
|
|
437
446
|
"progressWidgetEnabled": true,
|
|
@@ -488,13 +497,15 @@ const BUILTIN_DEFAULT_WORKFLOW_SETTINGS = {
|
|
|
488
497
|
"clarificationTiming": "after_initial_analysis",
|
|
489
498
|
"clarificationQualityGate": true,
|
|
490
499
|
"allowClarificationWithoutAnalysis": false,
|
|
491
|
-
"useSubagentsBeforeClarification": true
|
|
500
|
+
"useSubagentsBeforeClarification": true,
|
|
501
|
+
"maxTokens": 0
|
|
492
502
|
},
|
|
493
503
|
"safety": {
|
|
494
|
-
"repoLockEnabled":
|
|
495
|
-
"disableBashInPlanMode":
|
|
504
|
+
"repoLockEnabled": true,
|
|
505
|
+
"disableBashInPlanMode": false,
|
|
496
506
|
"disableBashInValidatorMode": true,
|
|
497
|
-
"blockDestructiveCommands": true
|
|
507
|
+
"blockDestructiveCommands": true,
|
|
508
|
+
"allowPackageInstallInExecution": true
|
|
498
509
|
},
|
|
499
510
|
"ui": {
|
|
500
511
|
"showWorkflowStatus": true,
|
|
@@ -522,7 +533,8 @@ const BUILTIN_DEFAULT_WORKFLOW_SETTINGS = {
|
|
|
522
533
|
"startupVisualOnSessionStart": true,
|
|
523
534
|
"customBrandEnabled": false,
|
|
524
535
|
"customBrandText": "",
|
|
525
|
-
"customBrandBaseVisual": "mission_control"
|
|
536
|
+
"customBrandBaseVisual": "mission_control",
|
|
537
|
+
"debugPlanStepTracking": false
|
|
526
538
|
},
|
|
527
539
|
"shortcuts": {
|
|
528
540
|
"planMode": null
|
|
@@ -572,7 +584,9 @@ const BUILTIN_DEFAULT_WORKFLOW_SETTINGS = {
|
|
|
572
584
|
"clarificationTiming": "after_initial_analysis",
|
|
573
585
|
"clarificationQualityGate": true,
|
|
574
586
|
"allowClarificationWithoutAnalysis": false,
|
|
575
|
-
"useSubagentsBeforeClarification": true
|
|
587
|
+
"useSubagentsBeforeClarification": true,
|
|
588
|
+
"maxTokens": 0,
|
|
589
|
+
"maxRuntimeHours": 0
|
|
576
590
|
},
|
|
577
591
|
"context": {
|
|
578
592
|
"compactionMode": "pi_default",
|
|
@@ -869,7 +883,7 @@ export function builtInWorkflowPresets(): Record<string, WorkflowPresetBundle> {
|
|
|
869
883
|
displayName: "Simple",
|
|
870
884
|
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
885
|
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 },
|
|
886
|
+
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, planShowProgressBar: true },
|
|
873
887
|
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
888
|
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
889
|
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 },
|
|
@@ -878,7 +892,7 @@ export function builtInWorkflowPresets(): Record<string, WorkflowPresetBundle> {
|
|
|
878
892
|
displayName: "Standard",
|
|
879
893
|
description: "Default end-to-end workflow with useful clarification, automatic execution/validation after approval, safe repair retries, and balanced worker support.",
|
|
880
894
|
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 },
|
|
895
|
+
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, planShowProgressBar: true },
|
|
882
896
|
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
897
|
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
898
|
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 },
|
|
@@ -887,7 +901,7 @@ export function builtInWorkflowPresets(): Record<string, WorkflowPresetBundle> {
|
|
|
887
901
|
displayName: "Deep",
|
|
888
902
|
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
903
|
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 },
|
|
904
|
+
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, planShowProgressBar: true },
|
|
891
905
|
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
906
|
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
907
|
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 },
|
|
@@ -896,7 +910,7 @@ export function builtInWorkflowPresets(): Record<string, WorkflowPresetBundle> {
|
|
|
896
910
|
displayName: "Maximum",
|
|
897
911
|
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
912
|
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 },
|
|
913
|
+
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, planShowProgressBar: true },
|
|
900
914
|
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
915
|
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
916
|
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 },
|