@dv.nghiem/flowdeck 0.3.6 → 0.3.8
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/dist/agents/index.d.ts +2 -1
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/supervisor.d.ts +3 -0
- package/dist/agents/supervisor.d.ts.map +1 -0
- package/dist/config/schema.d.ts +36 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/dashboard/lib/state-reader.d.ts.map +1 -1
- package/dist/dashboard/server.mjs +789 -905
- package/dist/dashboard/types.d.ts +0 -2
- package/dist/dashboard/types.d.ts.map +1 -1
- package/dist/dashboard/views/index.ejs +0 -6
- package/dist/dashboard/views/partials/header.ejs +0 -4
- package/dist/hooks/command-ref-guard.d.ts +21 -0
- package/dist/hooks/command-ref-guard.d.ts.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +819 -12
- package/dist/lib/impact-radar.d.ts +2 -2
- package/dist/services/agent-contract-registry.d.ts.map +1 -1
- package/dist/services/command-validator.d.ts +50 -0
- package/dist/services/command-validator.d.ts.map +1 -0
- package/dist/services/command-validator.test.d.ts +2 -0
- package/dist/services/command-validator.test.d.ts.map +1 -0
- package/dist/services/quick-router.d.ts +118 -0
- package/dist/services/quick-router.d.ts.map +1 -0
- package/dist/services/quick-router.test.d.ts +13 -0
- package/dist/services/quick-router.test.d.ts.map +1 -0
- package/dist/services/supervisor-binding.d.ts +114 -0
- package/dist/services/supervisor-binding.d.ts.map +1 -0
- package/dist/services/supervisor.test.d.ts +14 -0
- package/dist/services/supervisor.test.d.ts.map +1 -0
- package/dist/services/telemetry.d.ts +1 -1
- package/dist/services/telemetry.d.ts.map +1 -1
- package/dist/services/workflow-scorecard.d.ts +20 -0
- package/dist/services/workflow-scorecard.d.ts.map +1 -1
- package/docs/agents.md +1 -1
- package/docs/commands/fd-ask.md +1 -1
- package/docs/commands/fd-deploy-check.md +1 -1
- package/docs/commands/fd-discuss.md +1 -1
- package/docs/commands/fd-fix-bug.md +1 -1
- package/docs/commands/fd-new-feature.md +1 -1
- package/docs/commands/fd-verify.md +18 -0
- package/docs/commands/fd-write-docs.md +1 -1
- package/docs/commands.md +43 -23
- package/docs/feature-integration-architecture.md +1 -1
- package/docs/notifications.md +2 -2
- package/docs/quick-start.md +3 -1
- package/docs/skills.md +1 -1
- package/docs/workflows.md +1 -1
- package/package.json +8 -8
- package/src/commands/fd-discuss.md +1 -1
- package/src/commands/fd-multi-repo.md +2 -2
- package/src/commands/fd-new-project.md +0 -1
- package/src/commands/fd-plan.md +1 -1
- package/src/commands/fd-quick.md +240 -55
- package/src/rules/common/agent-orchestration.md +1 -1
- package/src/skills/blast-radius-preview/SKILL.md +1 -1
- package/src/skills/change-impact-radar/SKILL.md +2 -2
- package/src/skills/confidence-aware-planning/SKILL.md +1 -1
- package/src/skills/context-load/SKILL.md +1 -1
- package/src/skills/design-tokens/SKILL.md +250 -0
- package/src/skills/git-release/SKILL.md +1 -1
- package/src/skills/human-review-routing/SKILL.md +3 -3
- package/src/skills/intent-translator/SKILL.md +2 -2
- package/src/skills/regression-prediction/SKILL.md +1 -1
- package/src/skills/test-gap-detector/SKILL.md +1 -1
- package/src/skills/ui-design/SKILL.md +313 -0
- package/src/skills/volatility-map/SKILL.md +1 -1
package/dist/index.js
CHANGED
|
@@ -2480,7 +2480,7 @@ async function guardRailsHook(ctx, input, _output) {
|
|
|
2480
2480
|
for (const pattern of BUILD_DEPLOY_PATTERNS) {
|
|
2481
2481
|
if (cmd.includes(pattern)) {
|
|
2482
2482
|
if (!getPlanConfirmed(statePath2)) {
|
|
2483
|
-
throw new Error(`[flowdeck] WARNING: Build/deploy command detected but plan is not confirmed. Run /plan first.`);
|
|
2483
|
+
throw new Error(`[flowdeck] WARNING: Build/deploy command detected but plan is not confirmed. Run /fd-plan first.`);
|
|
2484
2484
|
}
|
|
2485
2485
|
break;
|
|
2486
2486
|
}
|
|
@@ -2543,15 +2543,15 @@ function getPlanConfirmed(statePath2) {
|
|
|
2543
2543
|
}
|
|
2544
2544
|
function getWarningMessage(planningDir2) {
|
|
2545
2545
|
if (!existsSync18(join18(planningDir2, STATE_FILE2))) {
|
|
2546
|
-
return "No .planning/ found. Run /new-project first.";
|
|
2546
|
+
return "No .planning/ found. Run /fd-new-project first.";
|
|
2547
2547
|
}
|
|
2548
|
-
return "Plan not confirmed. Run /plan and confirm to enable execution.";
|
|
2548
|
+
return "Plan not confirmed. Run /fd-plan and confirm to enable execution.";
|
|
2549
2549
|
}
|
|
2550
2550
|
function getBlockMessage(planningDir2) {
|
|
2551
2551
|
if (!existsSync18(join18(planningDir2, STATE_FILE2))) {
|
|
2552
|
-
return "No .planning/ found. Run /new-project first.";
|
|
2552
|
+
return "No .planning/ found. Run /fd-new-project first.";
|
|
2553
2553
|
}
|
|
2554
|
-
return "Plan not confirmed. Run /plan and confirm to enable execution.";
|
|
2554
|
+
return "Plan not confirmed. Run /fd-plan and confirm to enable execution.";
|
|
2555
2555
|
}
|
|
2556
2556
|
|
|
2557
2557
|
// src/hooks/tool-guard.ts
|
|
@@ -2689,7 +2689,7 @@ async function sessionStartHook(ctx) {
|
|
|
2689
2689
|
return {
|
|
2690
2690
|
flowdeck_phase: null,
|
|
2691
2691
|
flowdeck_status: "no_plan",
|
|
2692
|
-
flowdeck_warning: "Run /new-project or /map-codebase to initialize.",
|
|
2692
|
+
flowdeck_warning: "Run /fd-new-project or /fd-map-codebase to initialize.",
|
|
2693
2693
|
flowdeck_has_codebase: existsSync20(codebaseDirectory),
|
|
2694
2694
|
...workspaceRoot && config?.sub_repos ? {
|
|
2695
2695
|
flowdeck_workspace_root: workspaceRoot,
|
|
@@ -3605,16 +3605,16 @@ MUST execute at session start:
|
|
|
3605
3605
|
3. Check which steps are marked complete
|
|
3606
3606
|
4. Begin execution from the first incomplete step
|
|
3607
3607
|
|
|
3608
|
-
If STATE.md does not exist, tell the user: "No STATE.md found. Run \`/new-project\` to initialize."
|
|
3608
|
+
If STATE.md does not exist, tell the user: "No STATE.md found. Run \`/fd-new-project\` to initialize."
|
|
3609
3609
|
|
|
3610
3610
|
## Phase Gating
|
|
3611
3611
|
|
|
3612
3612
|
Only orchestrate in the **execute** phase.
|
|
3613
3613
|
|
|
3614
3614
|
If the project is in another phase:
|
|
3615
|
-
- **discuss** phase: "Run \`/discuss\` to complete requirements gathering first."
|
|
3616
|
-
- **plan** phase: "Run \`/plan\` to create the implementation plan first."
|
|
3617
|
-
- **review** phase: "Run \`/
|
|
3615
|
+
- **discuss** phase: "Run \`/fd-discuss\` to complete requirements gathering first."
|
|
3616
|
+
- **plan** phase: "Run \`/fd-plan\` to create the implementation plan first."
|
|
3617
|
+
- **review** phase: "Run \`/fd-verify\` to complete the review phase."
|
|
3618
3618
|
|
|
3619
3619
|
## Step Execution
|
|
3620
3620
|
|
|
@@ -5539,7 +5539,7 @@ Discussion is complete when:
|
|
|
5539
5539
|
- All decisions recorded in DISCUSS.md
|
|
5540
5540
|
- No open questions remain
|
|
5541
5541
|
|
|
5542
|
-
Report: "Requirements gathering complete. N decisions recorded. Ready for /plan."`;
|
|
5542
|
+
Report: "Requirements gathering complete. N decisions recorded. Ready for /fd-plan."`;
|
|
5543
5543
|
var createTaskSplitterAgent = (model, customPrompt, customAppendPrompt) => {
|
|
5544
5544
|
const prompt = resolvePrompt(TASK_SPLITTER_PROMPT, customPrompt, customAppendPrompt);
|
|
5545
5545
|
return {
|
|
@@ -6278,6 +6278,133 @@ var createDesignAgent = (model, customPrompt, customAppendPrompt) => {
|
|
|
6278
6278
|
};
|
|
6279
6279
|
};
|
|
6280
6280
|
|
|
6281
|
+
// src/agents/supervisor.ts
|
|
6282
|
+
var SUPERVISOR_PROMPT = `You are the FlowDeck Supervisor Agent — a governance layer that reviews existing commands and agents before or after execution.
|
|
6283
|
+
|
|
6284
|
+
## Role and Hard Constraints
|
|
6285
|
+
|
|
6286
|
+
**You review. You do not execute.**
|
|
6287
|
+
|
|
6288
|
+
You sit above the orchestrator's execution path. Your only job is to inspect an already-selected command or agent, validate it against policy, and return a structured decision.
|
|
6289
|
+
|
|
6290
|
+
### You MUST NEVER:
|
|
6291
|
+
- Invent a new command name
|
|
6292
|
+
- Invent a new workflow definition
|
|
6293
|
+
- Suggest creating a new agent
|
|
6294
|
+
- Replace or duplicate the orchestrator
|
|
6295
|
+
- Execute implementation tasks
|
|
6296
|
+
- Become a second dispatcher
|
|
6297
|
+
- Modify the intent of an existing command
|
|
6298
|
+
|
|
6299
|
+
### You MAY:
|
|
6300
|
+
- Inspect an existing registered command or agent
|
|
6301
|
+
- Validate that required stages are present
|
|
6302
|
+
- Detect policy violations in the selected target
|
|
6303
|
+
- Flag risk before execution
|
|
6304
|
+
- Decide: approve / revise / block / escalate
|
|
6305
|
+
- Request that the orchestrator obtain missing prerequisites
|
|
6306
|
+
|
|
6307
|
+
## Registered Commands (source of truth — do not add to this list)
|
|
6308
|
+
|
|
6309
|
+
fd-ask, fd-checkpoint, fd-deploy-check, fd-design, fd-discuss, fd-doctor,
|
|
6310
|
+
fd-execute, fd-fix-bug, fd-map-codebase, fd-multi-repo, fd-new-feature,
|
|
6311
|
+
fd-new-project, fd-plan, fd-quick, fd-reflect, fd-resume, fd-status,
|
|
6312
|
+
fd-suggest, fd-translate-intent, fd-verify, fd-write-docs
|
|
6313
|
+
|
|
6314
|
+
## Registered Agents (source of truth — do not add to this list)
|
|
6315
|
+
|
|
6316
|
+
orchestrator, planner, backend-coder, frontend-coder, devops, plan-checker,
|
|
6317
|
+
tester, reviewer, researcher, writer, security-auditor, doc-updater, mapper,
|
|
6318
|
+
code-explorer, debug-specialist, build-error-resolver, task-splitter, discusser,
|
|
6319
|
+
architect, risk-analyst, policy-enforcer, performance-optimizer, refactor-guide,
|
|
6320
|
+
auto-learner, design, supervisor
|
|
6321
|
+
|
|
6322
|
+
## Policy Checks
|
|
6323
|
+
|
|
6324
|
+
When reviewing a command or agent, evaluate ONLY the following against what already exists:
|
|
6325
|
+
|
|
6326
|
+
### Design-first policy
|
|
6327
|
+
- If the task is UI-heavy (dashboard, landing page, web app, UI, UX, admin panel) and the current phase is "execute", the design stage MUST have completed with approval.
|
|
6328
|
+
- If design approval is absent: decision = revise | required change = complete design stage first.
|
|
6329
|
+
|
|
6330
|
+
### Bugfix regression policy
|
|
6331
|
+
- If the command is fd-fix-bug, a regression test MUST exist before implementation.
|
|
6332
|
+
- If no regression test: decision = revise | required change = write failing regression test first.
|
|
6333
|
+
|
|
6334
|
+
### Phase ordering policy
|
|
6335
|
+
- fd-execute must only run in the "execute" phase.
|
|
6336
|
+
- If invoked in a different phase: decision = revise.
|
|
6337
|
+
|
|
6338
|
+
### Missing inputs policy
|
|
6339
|
+
- If a registered agent has required inputs listed in its contract and they are absent: decision = revise.
|
|
6340
|
+
|
|
6341
|
+
### Approval gate policy
|
|
6342
|
+
- If an operation requires explicit human approval and none was granted: decision = escalate.
|
|
6343
|
+
|
|
6344
|
+
### Unregistered target policy
|
|
6345
|
+
- If the requested command or agent is NOT in the registered lists above: decision = block.
|
|
6346
|
+
- Do NOT suggest or create a replacement. Report that the target is unavailable.
|
|
6347
|
+
|
|
6348
|
+
## Decision Output Format
|
|
6349
|
+
|
|
6350
|
+
Always respond with a valid JSON object matching this schema exactly:
|
|
6351
|
+
|
|
6352
|
+
\`\`\`json
|
|
6353
|
+
{
|
|
6354
|
+
"decision": "approve" | "revise" | "block" | "escalate",
|
|
6355
|
+
"targetType": "command" | "agent" | "workflow",
|
|
6356
|
+
"targetName": "<exact registered name>",
|
|
6357
|
+
"exists": true | false,
|
|
6358
|
+
"reasons": ["<human-readable reason>"],
|
|
6359
|
+
"missingRequirements": ["<what is absent>"],
|
|
6360
|
+
"riskFlags": ["<risk description>"],
|
|
6361
|
+
"requiredChanges": ["<what must change before proceeding>"],
|
|
6362
|
+
"approvalStatus": "approved" | "pending" | "denied" | "escalated",
|
|
6363
|
+
"confidenceScore": 0.0–1.0,
|
|
6364
|
+
"reviewPhase": "preflight" | "post-stage",
|
|
6365
|
+
"timestamp": "<ISO 8601>"
|
|
6366
|
+
}
|
|
6367
|
+
\`\`\`
|
|
6368
|
+
|
|
6369
|
+
### Decision rules:
|
|
6370
|
+
- **approve**: target exists, all policy checks pass, confidence ≥ threshold
|
|
6371
|
+
- **revise**: target exists, fixable issues found — list requiredChanges so caller can resolve
|
|
6372
|
+
- **block**: target does not exist OR critical unfixable policy violation
|
|
6373
|
+
- **escalate**: human approval required OR confidence below threshold
|
|
6374
|
+
|
|
6375
|
+
### On unregistered targets:
|
|
6376
|
+
If a requested command or workflow is not in the registered lists, set:
|
|
6377
|
+
- decision: "block"
|
|
6378
|
+
- exists: false
|
|
6379
|
+
- reasons: explain the target is not registered
|
|
6380
|
+
- requiredChanges: list valid registered alternatives
|
|
6381
|
+
- Do NOT invent a new command or workflow to substitute
|
|
6382
|
+
|
|
6383
|
+
## Diagnostics
|
|
6384
|
+
|
|
6385
|
+
Before issuing a decision, log:
|
|
6386
|
+
1. Which existing command/agent was reviewed
|
|
6387
|
+
2. Whether it exists in the registry
|
|
6388
|
+
3. Which policy checks ran
|
|
6389
|
+
4. Why the decision was reached
|
|
6390
|
+
5. Whether review is preflight or post-stage
|
|
6391
|
+
6. Whether human escalation is recommended`;
|
|
6392
|
+
function createSupervisorAgent(model, customPrompt, customAppendPrompt) {
|
|
6393
|
+
const prompt = resolvePrompt(SUPERVISOR_PROMPT, customPrompt, customAppendPrompt);
|
|
6394
|
+
const definition = {
|
|
6395
|
+
name: "supervisor",
|
|
6396
|
+
description: "Governance supervisor that reviews existing commands and agents before execution. Approves, revises, blocks, or escalates — never creates new commands or workflows.",
|
|
6397
|
+
config: {
|
|
6398
|
+
temperature: 0.1,
|
|
6399
|
+
prompt
|
|
6400
|
+
}
|
|
6401
|
+
};
|
|
6402
|
+
if (typeof model === "string" && model) {
|
|
6403
|
+
definition.config.model = model;
|
|
6404
|
+
}
|
|
6405
|
+
return definition;
|
|
6406
|
+
}
|
|
6407
|
+
|
|
6281
6408
|
// src/agents/index.ts
|
|
6282
6409
|
var AGENT_NAMES = [
|
|
6283
6410
|
"orchestrator",
|
|
@@ -6304,7 +6431,8 @@ var AGENT_NAMES = [
|
|
|
6304
6431
|
"performance-optimizer",
|
|
6305
6432
|
"refactor-guide",
|
|
6306
6433
|
"auto-learner",
|
|
6307
|
-
"design"
|
|
6434
|
+
"design",
|
|
6435
|
+
"supervisor"
|
|
6308
6436
|
];
|
|
6309
6437
|
var PRIMARY_AGENTS = new Set(["orchestrator"]);
|
|
6310
6438
|
var ALL_MODES_AGENTS = new Set;
|
|
@@ -6370,6 +6498,8 @@ function createAgent(name, model, customPrompt, customAppendPrompt) {
|
|
|
6370
6498
|
return createAutoLearnerAgent(model);
|
|
6371
6499
|
case "design":
|
|
6372
6500
|
return createDesignAgent(model, customPrompt, customAppendPrompt);
|
|
6501
|
+
case "supervisor":
|
|
6502
|
+
return createSupervisorAgent(model, customPrompt, customAppendPrompt);
|
|
6373
6503
|
default:
|
|
6374
6504
|
console.warn(`[flowdeck] Unknown agent: ${name}`);
|
|
6375
6505
|
return;
|
|
@@ -6407,6 +6537,632 @@ function getAgentConfigs(agentModels) {
|
|
|
6407
6537
|
return configs;
|
|
6408
6538
|
}
|
|
6409
6539
|
|
|
6540
|
+
// src/services/agent-contract-registry.ts
|
|
6541
|
+
var CONTRACTS = [
|
|
6542
|
+
{
|
|
6543
|
+
agent: "orchestrator",
|
|
6544
|
+
role: "Coordinate multi-agent execution. Delegates all work — never implements directly.",
|
|
6545
|
+
allowedTaskTypes: ["orchestration", "coordination", "delegation", "phase-management"],
|
|
6546
|
+
requiredInputs: ["STATE.md", "PLAN.md"],
|
|
6547
|
+
expectedOutputFields: ["delegated_steps", "completed_steps", "current_phase"],
|
|
6548
|
+
allowedTools: [
|
|
6549
|
+
"delegate",
|
|
6550
|
+
"run-pipeline",
|
|
6551
|
+
"council",
|
|
6552
|
+
"planning-state",
|
|
6553
|
+
"codebase-state",
|
|
6554
|
+
"workspace-state",
|
|
6555
|
+
"repo-memory",
|
|
6556
|
+
"decision-trace",
|
|
6557
|
+
"policy-engine",
|
|
6558
|
+
"context-generator",
|
|
6559
|
+
"create-skill",
|
|
6560
|
+
"reflect"
|
|
6561
|
+
],
|
|
6562
|
+
forbiddenActions: [
|
|
6563
|
+
"write_file",
|
|
6564
|
+
"edit_file",
|
|
6565
|
+
"create_file",
|
|
6566
|
+
"bash",
|
|
6567
|
+
"patch",
|
|
6568
|
+
"apply_patch",
|
|
6569
|
+
"read source files directly"
|
|
6570
|
+
],
|
|
6571
|
+
escalationConditions: [
|
|
6572
|
+
"delegated agent fails twice",
|
|
6573
|
+
"delegation budget exhausted",
|
|
6574
|
+
"deadlock detected",
|
|
6575
|
+
"all agents blocked on the same step"
|
|
6576
|
+
],
|
|
6577
|
+
stopConditions: [
|
|
6578
|
+
"all PLAN.md steps completed",
|
|
6579
|
+
"user requests stop",
|
|
6580
|
+
"budget exceeded with no fallback"
|
|
6581
|
+
],
|
|
6582
|
+
successCriteria: [
|
|
6583
|
+
"all plan steps delegated and completed",
|
|
6584
|
+
"STATE.md phase updated to review",
|
|
6585
|
+
"no implementation performed directly by orchestrator"
|
|
6586
|
+
]
|
|
6587
|
+
},
|
|
6588
|
+
{
|
|
6589
|
+
agent: "planner",
|
|
6590
|
+
role: "Create detailed implementation plans. Output PLAN.md with numbered steps.",
|
|
6591
|
+
allowedTaskTypes: ["planning", "task-breakdown", "step-decomposition"],
|
|
6592
|
+
requiredInputs: ["task description or STATE.md"],
|
|
6593
|
+
expectedOutputFields: ["steps", "phase"],
|
|
6594
|
+
allowedTools: ["read", "glob", "grep", "planning-state", "workspace-state"],
|
|
6595
|
+
forbiddenActions: [
|
|
6596
|
+
"write source files",
|
|
6597
|
+
"run bash commands",
|
|
6598
|
+
"edit application code",
|
|
6599
|
+
"implement features"
|
|
6600
|
+
],
|
|
6601
|
+
escalationConditions: [
|
|
6602
|
+
"requirements are ambiguous",
|
|
6603
|
+
"dependencies between steps unclear",
|
|
6604
|
+
"conflicting constraints"
|
|
6605
|
+
],
|
|
6606
|
+
stopConditions: ["PLAN.md written and reviewed by plan-checker", "user confirms plan"],
|
|
6607
|
+
successCriteria: [
|
|
6608
|
+
"PLAN.md contains numbered steps with assigned agents",
|
|
6609
|
+
"each step has clear success criteria",
|
|
6610
|
+
"no implementation performed"
|
|
6611
|
+
]
|
|
6612
|
+
},
|
|
6613
|
+
{
|
|
6614
|
+
agent: "plan-checker",
|
|
6615
|
+
role: "Review PLAN.md quality before execution. Read-only.",
|
|
6616
|
+
allowedTaskTypes: ["plan-review", "quality-check"],
|
|
6617
|
+
requiredInputs: ["PLAN.md"],
|
|
6618
|
+
expectedOutputFields: ["verdict", "issues", "recommendations"],
|
|
6619
|
+
allowedTools: ["read", "glob", "grep"],
|
|
6620
|
+
forbiddenActions: ["write or edit any files", "modify PLAN.md"],
|
|
6621
|
+
escalationConditions: ["plan is fundamentally flawed", "critical gaps found"],
|
|
6622
|
+
stopConditions: ["review complete", "verdict issued"],
|
|
6623
|
+
successCriteria: ["structured review output", "no file modifications"]
|
|
6624
|
+
},
|
|
6625
|
+
{
|
|
6626
|
+
agent: "design",
|
|
6627
|
+
role: "Design UX, wireframes, and visual systems for UI-heavy tasks.",
|
|
6628
|
+
allowedTaskTypes: ["ux-design", "wireframe", "visual-system", "design-handoff", "frontend-handoff"],
|
|
6629
|
+
requiredInputs: ["task description", "requirements"],
|
|
6630
|
+
expectedOutputFields: ["design_stage", "wireframes", "component_structure", "design_tokens"],
|
|
6631
|
+
allowedTools: ["read", "write", "glob", "grep", "planning-state"],
|
|
6632
|
+
forbiddenActions: [
|
|
6633
|
+
"run bash commands",
|
|
6634
|
+
"write application logic",
|
|
6635
|
+
"implement backend code",
|
|
6636
|
+
"implement React components"
|
|
6637
|
+
],
|
|
6638
|
+
escalationConditions: [
|
|
6639
|
+
"design requirements unclear",
|
|
6640
|
+
"conflicting UX requirements",
|
|
6641
|
+
"brand guidelines missing"
|
|
6642
|
+
],
|
|
6643
|
+
stopConditions: ["design_stage=handoff_complete", "design_approved=true"],
|
|
6644
|
+
successCriteria: [
|
|
6645
|
+
"design document written",
|
|
6646
|
+
"design_stage set to handoff_complete",
|
|
6647
|
+
"design_approved set to true",
|
|
6648
|
+
"no application code written"
|
|
6649
|
+
]
|
|
6650
|
+
},
|
|
6651
|
+
{
|
|
6652
|
+
agent: "backend-coder",
|
|
6653
|
+
role: "Implement backend features: API, services, data layer, business logic.",
|
|
6654
|
+
allowedTaskTypes: ["implementation", "backend", "api", "database", "service", "bugfix"],
|
|
6655
|
+
requiredInputs: ["PLAN.md step description", "relevant context files"],
|
|
6656
|
+
expectedOutputFields: ["files_modified", "summary"],
|
|
6657
|
+
allowedTools: ["read", "write", "edit", "bash", "glob", "grep"],
|
|
6658
|
+
forbiddenActions: [
|
|
6659
|
+
"modify frontend UI component files",
|
|
6660
|
+
"change CI/CD config without devops involvement"
|
|
6661
|
+
],
|
|
6662
|
+
escalationConditions: [
|
|
6663
|
+
"architecture decision needed",
|
|
6664
|
+
"security-sensitive change without audit",
|
|
6665
|
+
"database migration required"
|
|
6666
|
+
],
|
|
6667
|
+
stopConditions: ["step implementation complete", "tests pass", "reviewer approves"],
|
|
6668
|
+
successCriteria: [
|
|
6669
|
+
"code written per plan step",
|
|
6670
|
+
"no regressions introduced",
|
|
6671
|
+
"tests exist or updated"
|
|
6672
|
+
]
|
|
6673
|
+
},
|
|
6674
|
+
{
|
|
6675
|
+
agent: "frontend-coder",
|
|
6676
|
+
role: "Implement frontend features: UI components, client state, rendering.",
|
|
6677
|
+
allowedTaskTypes: ["implementation", "frontend", "ui", "component", "styling", "bugfix"],
|
|
6678
|
+
requiredInputs: ["PLAN.md step description", "design handoff for UI-heavy tasks"],
|
|
6679
|
+
expectedOutputFields: ["files_modified", "summary"],
|
|
6680
|
+
allowedTools: ["read", "write", "edit", "bash", "glob", "grep"],
|
|
6681
|
+
forbiddenActions: [
|
|
6682
|
+
"modify backend API files",
|
|
6683
|
+
"change server configuration",
|
|
6684
|
+
"implement without approved design for UI-heavy tasks"
|
|
6685
|
+
],
|
|
6686
|
+
escalationConditions: [
|
|
6687
|
+
"design handoff missing for UI-heavy task",
|
|
6688
|
+
"component library or design system unclear"
|
|
6689
|
+
],
|
|
6690
|
+
stopConditions: ["step implementation complete", "tests pass", "reviewer approves"],
|
|
6691
|
+
successCriteria: [
|
|
6692
|
+
"components implemented per approved design",
|
|
6693
|
+
"no regressions introduced",
|
|
6694
|
+
"tests exist or updated"
|
|
6695
|
+
]
|
|
6696
|
+
},
|
|
6697
|
+
{
|
|
6698
|
+
agent: "devops",
|
|
6699
|
+
role: "Implement DevOps and infrastructure changes: CI/CD, deployment, infra scripts.",
|
|
6700
|
+
allowedTaskTypes: ["implementation", "ci-cd", "deployment", "infrastructure", "operations"],
|
|
6701
|
+
requiredInputs: ["PLAN.md step description"],
|
|
6702
|
+
expectedOutputFields: ["files_modified", "summary"],
|
|
6703
|
+
allowedTools: ["read", "write", "edit", "bash", "glob", "grep"],
|
|
6704
|
+
forbiddenActions: [
|
|
6705
|
+
"modify application source code",
|
|
6706
|
+
"deploy to production without approval"
|
|
6707
|
+
],
|
|
6708
|
+
escalationConditions: [
|
|
6709
|
+
"production deployment requires approval",
|
|
6710
|
+
"destructive infra change"
|
|
6711
|
+
],
|
|
6712
|
+
stopConditions: ["pipeline or infra change complete", "reviewer approves"],
|
|
6713
|
+
successCriteria: ["infrastructure code written per plan", "no prod deployment without approval"]
|
|
6714
|
+
},
|
|
6715
|
+
{
|
|
6716
|
+
agent: "tester",
|
|
6717
|
+
role: "Write and run tests following TDD principles. Tests before implementation.",
|
|
6718
|
+
allowedTaskTypes: ["testing", "tdd", "regression", "integration-test", "unit-test"],
|
|
6719
|
+
requiredInputs: ["feature or step description", "relevant source files"],
|
|
6720
|
+
expectedOutputFields: ["test_files_written", "tests_passing", "coverage_summary"],
|
|
6721
|
+
allowedTools: ["read", "write", "edit", "bash", "glob", "grep"],
|
|
6722
|
+
forbiddenActions: [
|
|
6723
|
+
"delete failing tests to make suite pass",
|
|
6724
|
+
"implement application features",
|
|
6725
|
+
"skip TDD cycle (red → green → refactor)"
|
|
6726
|
+
],
|
|
6727
|
+
escalationConditions: [
|
|
6728
|
+
"test infrastructure broken",
|
|
6729
|
+
"flaky tests blocking all progress"
|
|
6730
|
+
],
|
|
6731
|
+
stopConditions: ["all tests pass", "coverage meets threshold"],
|
|
6732
|
+
successCriteria: [
|
|
6733
|
+
"tests written before implementation",
|
|
6734
|
+
"all new tests pass",
|
|
6735
|
+
"no test deletions to fix failures"
|
|
6736
|
+
]
|
|
6737
|
+
},
|
|
6738
|
+
{
|
|
6739
|
+
agent: "reviewer",
|
|
6740
|
+
role: "Review code quality, security, and convention adherence. Read-only.",
|
|
6741
|
+
allowedTaskTypes: ["review", "code-review", "quality-check"],
|
|
6742
|
+
requiredInputs: ["files to review", "context of changes"],
|
|
6743
|
+
expectedOutputFields: ["verdict", "issues", "recommendations"],
|
|
6744
|
+
allowedTools: ["read", "glob", "grep"],
|
|
6745
|
+
forbiddenActions: [
|
|
6746
|
+
"write or edit any files",
|
|
6747
|
+
"make code changes",
|
|
6748
|
+
"approve security-sensitive changes without security audit"
|
|
6749
|
+
],
|
|
6750
|
+
escalationConditions: [
|
|
6751
|
+
"security issues found",
|
|
6752
|
+
"critical bugs found",
|
|
6753
|
+
"architectural violations"
|
|
6754
|
+
],
|
|
6755
|
+
stopConditions: ["review complete", "verdict issued"],
|
|
6756
|
+
successCriteria: [
|
|
6757
|
+
"structured review output with severity levels",
|
|
6758
|
+
"issues categorized",
|
|
6759
|
+
"no file modifications"
|
|
6760
|
+
]
|
|
6761
|
+
},
|
|
6762
|
+
{
|
|
6763
|
+
agent: "security-auditor",
|
|
6764
|
+
role: "Security audit: OWASP Top 10, injection, auth vulnerabilities. Read-only.",
|
|
6765
|
+
allowedTaskTypes: ["security-audit", "vulnerability-scan", "auth-review"],
|
|
6766
|
+
requiredInputs: ["files to audit", "change context"],
|
|
6767
|
+
expectedOutputFields: ["findings", "severity_breakdown", "recommendations"],
|
|
6768
|
+
allowedTools: ["read", "glob", "grep"],
|
|
6769
|
+
forbiddenActions: [
|
|
6770
|
+
"write or edit files",
|
|
6771
|
+
"make changes to fix vulnerabilities directly"
|
|
6772
|
+
],
|
|
6773
|
+
escalationConditions: [
|
|
6774
|
+
"CRITICAL vulnerability found",
|
|
6775
|
+
"auth bypass detected",
|
|
6776
|
+
"data exposure found"
|
|
6777
|
+
],
|
|
6778
|
+
stopConditions: ["audit complete", "all findings documented"],
|
|
6779
|
+
successCriteria: [
|
|
6780
|
+
"OWASP checklist evaluated",
|
|
6781
|
+
"findings documented with severity levels",
|
|
6782
|
+
"no file modifications"
|
|
6783
|
+
]
|
|
6784
|
+
},
|
|
6785
|
+
{
|
|
6786
|
+
agent: "researcher",
|
|
6787
|
+
role: "Research documentation, APIs, best practices. Read-only analysis.",
|
|
6788
|
+
allowedTaskTypes: ["research", "api-lookup", "documentation", "best-practices"],
|
|
6789
|
+
requiredInputs: ["research topic or question"],
|
|
6790
|
+
expectedOutputFields: ["findings", "references", "recommendations"],
|
|
6791
|
+
allowedTools: ["read", "glob", "grep", "web-search"],
|
|
6792
|
+
forbiddenActions: ["write or edit files", "implement solutions"],
|
|
6793
|
+
escalationConditions: [
|
|
6794
|
+
"critical information unavailable",
|
|
6795
|
+
"conflicting official documentation"
|
|
6796
|
+
],
|
|
6797
|
+
stopConditions: ["research question answered", "findings documented"],
|
|
6798
|
+
successCriteria: [
|
|
6799
|
+
"findings clearly summarized",
|
|
6800
|
+
"sources cited",
|
|
6801
|
+
"no file modifications"
|
|
6802
|
+
]
|
|
6803
|
+
},
|
|
6804
|
+
{
|
|
6805
|
+
agent: "architect",
|
|
6806
|
+
role: "Design system architecture, create ADRs, define API contracts.",
|
|
6807
|
+
allowedTaskTypes: ["architecture", "adr", "api-design", "system-design"],
|
|
6808
|
+
requiredInputs: ["feature or system description", "existing codebase context"],
|
|
6809
|
+
expectedOutputFields: ["architecture_document", "adr", "api_contracts"],
|
|
6810
|
+
allowedTools: ["read", "write", "glob", "grep", "planning-state"],
|
|
6811
|
+
forbiddenActions: ["write application code", "run bash commands"],
|
|
6812
|
+
escalationConditions: [
|
|
6813
|
+
"major architectural conflict with existing system",
|
|
6814
|
+
"breaking API change required"
|
|
6815
|
+
],
|
|
6816
|
+
stopConditions: ["ADR written", "architecture reviewed"],
|
|
6817
|
+
successCriteria: [
|
|
6818
|
+
"architecture documented with tradeoffs",
|
|
6819
|
+
"no application code written"
|
|
6820
|
+
]
|
|
6821
|
+
},
|
|
6822
|
+
{
|
|
6823
|
+
agent: "writer",
|
|
6824
|
+
role: "Draft project documentation: README, API docs, user guides.",
|
|
6825
|
+
allowedTaskTypes: ["documentation", "readme", "api-docs", "user-guide"],
|
|
6826
|
+
requiredInputs: ["feature description or codebase context"],
|
|
6827
|
+
expectedOutputFields: ["documentation_files"],
|
|
6828
|
+
allowedTools: ["read", "write", "edit", "glob", "grep"],
|
|
6829
|
+
forbiddenActions: ["modify application code", "run bash commands"],
|
|
6830
|
+
escalationConditions: ["documentation scope unclear"],
|
|
6831
|
+
stopConditions: ["docs written", "user confirms completeness"],
|
|
6832
|
+
successCriteria: [
|
|
6833
|
+
"documentation written and accurate",
|
|
6834
|
+
"no application code changed"
|
|
6835
|
+
]
|
|
6836
|
+
},
|
|
6837
|
+
{
|
|
6838
|
+
agent: "doc-updater",
|
|
6839
|
+
role: "Update existing documentation after code changes.",
|
|
6840
|
+
allowedTaskTypes: ["documentation-update", "doc-sync"],
|
|
6841
|
+
requiredInputs: ["changed files", "change summary"],
|
|
6842
|
+
expectedOutputFields: ["updated_docs"],
|
|
6843
|
+
allowedTools: ["read", "write", "edit", "glob", "grep"],
|
|
6844
|
+
forbiddenActions: [
|
|
6845
|
+
"modify application code",
|
|
6846
|
+
"delete documentation without replacement"
|
|
6847
|
+
],
|
|
6848
|
+
escalationConditions: ["documentation conflicts with implementation"],
|
|
6849
|
+
stopConditions: ["docs updated and synced"],
|
|
6850
|
+
successCriteria: ["docs reflect current code", "no application code changed"]
|
|
6851
|
+
},
|
|
6852
|
+
{
|
|
6853
|
+
agent: "supervisor",
|
|
6854
|
+
role: "Governance review layer. Inspects existing commands/agents, validates policy, returns structured approve/revise/block/escalate decision. Never creates new commands or workflows.",
|
|
6855
|
+
allowedTaskTypes: ["governance-review", "policy-check", "pre-execution-review", "post-stage-review"],
|
|
6856
|
+
requiredInputs: ["target name (command or agent)", "task context"],
|
|
6857
|
+
expectedOutputFields: ["decision", "targetType", "targetName", "exists", "reasons", "missingRequirements", "riskFlags", "requiredChanges", "approvalStatus", "confidenceScore"],
|
|
6858
|
+
allowedTools: ["read", "glob", "grep", "planning-state", "policy-engine"],
|
|
6859
|
+
forbiddenActions: [
|
|
6860
|
+
"create new commands",
|
|
6861
|
+
"create new workflows",
|
|
6862
|
+
"invent new agent names",
|
|
6863
|
+
"modify command intent",
|
|
6864
|
+
"replace orchestrator",
|
|
6865
|
+
"become second dispatcher",
|
|
6866
|
+
"execute implementation tasks",
|
|
6867
|
+
"write or edit source files",
|
|
6868
|
+
"run bash commands",
|
|
6869
|
+
"modify PLAN.md or STATE.md"
|
|
6870
|
+
],
|
|
6871
|
+
escalationConditions: [
|
|
6872
|
+
"human approval required and not granted",
|
|
6873
|
+
"confidence below threshold",
|
|
6874
|
+
"critical policy violation with no safe path forward"
|
|
6875
|
+
],
|
|
6876
|
+
stopConditions: ["structured decision issued", "review complete"],
|
|
6877
|
+
successCriteria: [
|
|
6878
|
+
"structured SupervisorDecision returned",
|
|
6879
|
+
"no new commands or workflows created",
|
|
6880
|
+
"existing registry not modified",
|
|
6881
|
+
"decision is one of: approve, revise, block, escalate"
|
|
6882
|
+
]
|
|
6883
|
+
}
|
|
6884
|
+
];
|
|
6885
|
+
var REGISTRY = new Map(CONTRACTS.map((c) => [c.agent, c]));
|
|
6886
|
+
function getContract(agent) {
|
|
6887
|
+
return REGISTRY.get(agent) ?? null;
|
|
6888
|
+
}
|
|
6889
|
+
|
|
6890
|
+
// src/services/supervisor-binding.ts
|
|
6891
|
+
var REGISTERED_COMMANDS = [
|
|
6892
|
+
"fd-ask",
|
|
6893
|
+
"fd-checkpoint",
|
|
6894
|
+
"fd-deploy-check",
|
|
6895
|
+
"fd-design",
|
|
6896
|
+
"fd-discuss",
|
|
6897
|
+
"fd-doctor",
|
|
6898
|
+
"fd-execute",
|
|
6899
|
+
"fd-fix-bug",
|
|
6900
|
+
"fd-map-codebase",
|
|
6901
|
+
"fd-multi-repo",
|
|
6902
|
+
"fd-new-feature",
|
|
6903
|
+
"fd-new-project",
|
|
6904
|
+
"fd-plan",
|
|
6905
|
+
"fd-quick",
|
|
6906
|
+
"fd-reflect",
|
|
6907
|
+
"fd-resume",
|
|
6908
|
+
"fd-status",
|
|
6909
|
+
"fd-suggest",
|
|
6910
|
+
"fd-translate-intent",
|
|
6911
|
+
"fd-verify",
|
|
6912
|
+
"fd-write-docs"
|
|
6913
|
+
];
|
|
6914
|
+
function resolveSupervisorConfig(directory) {
|
|
6915
|
+
try {
|
|
6916
|
+
const config = loadFlowDeckConfig(directory);
|
|
6917
|
+
const sup = config?.governance?.supervisor ?? {};
|
|
6918
|
+
return {
|
|
6919
|
+
enabled: sup.enabled ?? false,
|
|
6920
|
+
mode: sup.mode ?? "advisory",
|
|
6921
|
+
reviewedTargets: sup.reviewedTargets ?? [],
|
|
6922
|
+
canBlock: sup.canBlock ?? true,
|
|
6923
|
+
confidenceThreshold: sup.confidenceThreshold ?? 0.7,
|
|
6924
|
+
postExecutionReview: sup.postExecutionReview ?? false
|
|
6925
|
+
};
|
|
6926
|
+
} catch {
|
|
6927
|
+
return {
|
|
6928
|
+
enabled: false,
|
|
6929
|
+
mode: "advisory",
|
|
6930
|
+
reviewedTargets: [],
|
|
6931
|
+
canBlock: true,
|
|
6932
|
+
confidenceThreshold: 0.7,
|
|
6933
|
+
postExecutionReview: false
|
|
6934
|
+
};
|
|
6935
|
+
}
|
|
6936
|
+
}
|
|
6937
|
+
function isRegisteredCommand(name) {
|
|
6938
|
+
return REGISTERED_COMMANDS.includes(name);
|
|
6939
|
+
}
|
|
6940
|
+
function isRegisteredAgent(name) {
|
|
6941
|
+
return AGENT_NAMES.includes(name);
|
|
6942
|
+
}
|
|
6943
|
+
function isRegisteredTarget(name) {
|
|
6944
|
+
if (isRegisteredCommand(name))
|
|
6945
|
+
return { exists: true, type: "command" };
|
|
6946
|
+
if (isRegisteredAgent(name))
|
|
6947
|
+
return { exists: true, type: "agent" };
|
|
6948
|
+
return { exists: false, type: "agent" };
|
|
6949
|
+
}
|
|
6950
|
+
function checkCommandPolicy(commandName, ctx) {
|
|
6951
|
+
const reasons = [];
|
|
6952
|
+
const riskFlags = [];
|
|
6953
|
+
const missingRequirements = [];
|
|
6954
|
+
const requiredChanges = [];
|
|
6955
|
+
if (commandName === "fd-new-feature" || commandName === "fd-execute") {
|
|
6956
|
+
const taskLower = (ctx.taskDescription ?? "").toLowerCase();
|
|
6957
|
+
const isUiHeavy = /landing page|dashboard|admin panel|website|web app|ui|ux|interface|frontend|component/.test(taskLower);
|
|
6958
|
+
if (isUiHeavy && ctx.currentPhase === "execute" && ctx.designApprovalPresent === false) {
|
|
6959
|
+
missingRequirements.push("design approval (design stage must complete before execute for UI-heavy tasks)");
|
|
6960
|
+
riskFlags.push("UI-heavy task entering execute phase without design approval");
|
|
6961
|
+
requiredChanges.push("Run /fd-design first and obtain design approval before proceeding to execute");
|
|
6962
|
+
}
|
|
6963
|
+
}
|
|
6964
|
+
if (commandName === "fd-fix-bug") {
|
|
6965
|
+
if (ctx.regressionTestPresent === false) {
|
|
6966
|
+
missingRequirements.push("regression test (required before bugfix implementation)");
|
|
6967
|
+
riskFlags.push("Bugfix command invoked without a regression test");
|
|
6968
|
+
requiredChanges.push("Write a failing regression test before implementing the fix");
|
|
6969
|
+
}
|
|
6970
|
+
}
|
|
6971
|
+
if (commandName === "fd-deploy-check") {
|
|
6972
|
+
if (ctx.prerequisitesMet === false && ctx.missingInputs && ctx.missingInputs.length > 0) {
|
|
6973
|
+
missingRequirements.push(...ctx.missingInputs);
|
|
6974
|
+
riskFlags.push("Deploy check attempted with unmet prerequisites");
|
|
6975
|
+
}
|
|
6976
|
+
}
|
|
6977
|
+
if (commandName === "fd-execute" && ctx.currentPhase && ctx.currentPhase !== "execute") {
|
|
6978
|
+
riskFlags.push(`fd-execute invoked in phase "${ctx.currentPhase}" instead of "execute"`);
|
|
6979
|
+
requiredChanges.push(`Ensure project phase is "execute" before running fd-execute (currently: ${ctx.currentPhase})`);
|
|
6980
|
+
}
|
|
6981
|
+
if (ctx.approvalRequired && !ctx.approvalGranted) {
|
|
6982
|
+
missingRequirements.push("human approval (required for this command)");
|
|
6983
|
+
riskFlags.push("Approval gate not satisfied");
|
|
6984
|
+
requiredChanges.push("Obtain explicit human approval before proceeding");
|
|
6985
|
+
}
|
|
6986
|
+
const passed = missingRequirements.length === 0 && riskFlags.length === 0 && requiredChanges.length === 0;
|
|
6987
|
+
if (passed) {
|
|
6988
|
+
reasons.push(`Command "${commandName}" passed all policy checks`);
|
|
6989
|
+
}
|
|
6990
|
+
return { passed, reasons, riskFlags, missingRequirements, requiredChanges };
|
|
6991
|
+
}
|
|
6992
|
+
function checkAgentPolicy(agentName, ctx) {
|
|
6993
|
+
const reasons = [];
|
|
6994
|
+
const riskFlags = [];
|
|
6995
|
+
const missingRequirements = [];
|
|
6996
|
+
const requiredChanges = [];
|
|
6997
|
+
const contract = getContract(agentName);
|
|
6998
|
+
if (!contract) {
|
|
6999
|
+
riskFlags.push(`Agent "${agentName}" has no registered capability contract`);
|
|
7000
|
+
return { passed: false, reasons, riskFlags, missingRequirements, requiredChanges };
|
|
7001
|
+
}
|
|
7002
|
+
if (ctx.missingInputs && ctx.missingInputs.length > 0) {
|
|
7003
|
+
for (const missing of ctx.missingInputs) {
|
|
7004
|
+
const isRequired = contract.requiredInputs.some((r) => r.toLowerCase().includes(missing.toLowerCase()) || missing.toLowerCase().includes(r.toLowerCase()));
|
|
7005
|
+
if (isRequired) {
|
|
7006
|
+
missingRequirements.push(missing);
|
|
7007
|
+
requiredChanges.push(`Provide "${missing}" before delegating to ${agentName}`);
|
|
7008
|
+
}
|
|
7009
|
+
}
|
|
7010
|
+
}
|
|
7011
|
+
if (ctx.approvalRequired && !ctx.approvalGranted) {
|
|
7012
|
+
const needsApproval = contract.escalationConditions.some((c) => c.toLowerCase().includes("approval") || c.toLowerCase().includes("approve"));
|
|
7013
|
+
if (needsApproval) {
|
|
7014
|
+
missingRequirements.push("human approval");
|
|
7015
|
+
riskFlags.push(`Agent "${agentName}" requires approval via escalation condition`);
|
|
7016
|
+
requiredChanges.push("Obtain explicit human approval before proceeding");
|
|
7017
|
+
}
|
|
7018
|
+
}
|
|
7019
|
+
if (agentName === "design" || agentName === "frontend-coder") {
|
|
7020
|
+
const taskLower = (ctx.taskDescription ?? "").toLowerCase();
|
|
7021
|
+
const isUiHeavy = /landing page|dashboard|admin panel|website|web app|ui|ux|interface|frontend|component/.test(taskLower);
|
|
7022
|
+
if (agentName === "frontend-coder" && isUiHeavy && ctx.designApprovalPresent === false) {
|
|
7023
|
+
missingRequirements.push("design handoff approval");
|
|
7024
|
+
riskFlags.push("frontend-coder invoked for UI-heavy task without approved design handoff");
|
|
7025
|
+
requiredChanges.push("Complete design stage and obtain design approval before delegating to frontend-coder");
|
|
7026
|
+
}
|
|
7027
|
+
}
|
|
7028
|
+
const passed = missingRequirements.length === 0 && riskFlags.length === 0;
|
|
7029
|
+
if (passed) {
|
|
7030
|
+
reasons.push(`Agent "${agentName}" passed all policy checks`);
|
|
7031
|
+
}
|
|
7032
|
+
return { passed, reasons, riskFlags, missingRequirements, requiredChanges };
|
|
7033
|
+
}
|
|
7034
|
+
function computeConfidence(exists, policyResult, ctx) {
|
|
7035
|
+
if (!exists)
|
|
7036
|
+
return 0;
|
|
7037
|
+
if (policyResult.riskFlags.length >= 3)
|
|
7038
|
+
return 0.2;
|
|
7039
|
+
if (policyResult.riskFlags.length === 2)
|
|
7040
|
+
return 0.4;
|
|
7041
|
+
if (policyResult.riskFlags.length === 1)
|
|
7042
|
+
return 0.6;
|
|
7043
|
+
if (policyResult.missingRequirements.length > 0)
|
|
7044
|
+
return 0.5;
|
|
7045
|
+
if (ctx.prerequisitesMet === false)
|
|
7046
|
+
return 0.45;
|
|
7047
|
+
return 0.95;
|
|
7048
|
+
}
|
|
7049
|
+
function resolveDecision(exists, policyResult, confidenceScore, threshold, ctx) {
|
|
7050
|
+
if (!exists) {
|
|
7051
|
+
return { decision: "block", approvalStatus: "denied" };
|
|
7052
|
+
}
|
|
7053
|
+
if (ctx.approvalRequired && !ctx.approvalGranted) {
|
|
7054
|
+
return { decision: "escalate", approvalStatus: "escalated" };
|
|
7055
|
+
}
|
|
7056
|
+
if (!policyResult.passed) {
|
|
7057
|
+
if (policyResult.requiredChanges.length > 0) {
|
|
7058
|
+
return { decision: "revise", approvalStatus: "pending" };
|
|
7059
|
+
}
|
|
7060
|
+
return { decision: "block", approvalStatus: "denied" };
|
|
7061
|
+
}
|
|
7062
|
+
if (confidenceScore < threshold) {
|
|
7063
|
+
return { decision: "escalate", approvalStatus: "escalated" };
|
|
7064
|
+
}
|
|
7065
|
+
return { decision: "approve", approvalStatus: "approved" };
|
|
7066
|
+
}
|
|
7067
|
+
function runSupervisorReview(directory, targetName, ctx = {}) {
|
|
7068
|
+
const config = resolveSupervisorConfig(directory);
|
|
7069
|
+
const reviewPhase = ctx.reviewPhase ?? "preflight";
|
|
7070
|
+
const timestamp2 = new Date().toISOString();
|
|
7071
|
+
if (config.reviewedTargets.length > 0 && !config.reviewedTargets.includes(targetName)) {
|
|
7072
|
+
return {
|
|
7073
|
+
decision: "approve",
|
|
7074
|
+
targetType: "agent",
|
|
7075
|
+
targetName,
|
|
7076
|
+
exists: true,
|
|
7077
|
+
reasons: [`Target "${targetName}" is not in the reviewed targets list — auto-approved`],
|
|
7078
|
+
missingRequirements: [],
|
|
7079
|
+
riskFlags: [],
|
|
7080
|
+
requiredChanges: [],
|
|
7081
|
+
approvalStatus: "approved",
|
|
7082
|
+
confidenceScore: 1,
|
|
7083
|
+
reviewPhase,
|
|
7084
|
+
timestamp: timestamp2
|
|
7085
|
+
};
|
|
7086
|
+
}
|
|
7087
|
+
const { exists, type: targetType } = isRegisteredTarget(targetName);
|
|
7088
|
+
if (!exists) {
|
|
7089
|
+
const decision2 = {
|
|
7090
|
+
decision: "block",
|
|
7091
|
+
targetType,
|
|
7092
|
+
targetName,
|
|
7093
|
+
exists: false,
|
|
7094
|
+
reasons: [
|
|
7095
|
+
`Target "${targetName}" is not registered in the FlowDeck command or agent registry.`,
|
|
7096
|
+
"The supervisor does not create new commands or workflows.",
|
|
7097
|
+
"Only registered targets can be executed."
|
|
7098
|
+
],
|
|
7099
|
+
missingRequirements: [],
|
|
7100
|
+
riskFlags: [`Unregistered target: "${targetName}"`],
|
|
7101
|
+
requiredChanges: [
|
|
7102
|
+
`Use one of the registered commands: ${REGISTERED_COMMANDS.join(", ")}`,
|
|
7103
|
+
`Or use one of the registered agents: ${AGENT_NAMES.join(", ")}`
|
|
7104
|
+
],
|
|
7105
|
+
approvalStatus: "denied",
|
|
7106
|
+
confidenceScore: 0,
|
|
7107
|
+
reviewPhase,
|
|
7108
|
+
timestamp: timestamp2
|
|
7109
|
+
};
|
|
7110
|
+
_emitTelemetry(directory, decision2, ctx);
|
|
7111
|
+
return decision2;
|
|
7112
|
+
}
|
|
7113
|
+
const policyResult = targetType === "command" ? checkCommandPolicy(targetName, ctx) : checkAgentPolicy(targetName, ctx);
|
|
7114
|
+
const confidenceScore = computeConfidence(exists, policyResult, ctx);
|
|
7115
|
+
const { decision, approvalStatus } = resolveDecision(exists, policyResult, confidenceScore, config.confidenceThreshold, ctx);
|
|
7116
|
+
const reasons = policyResult.reasons.length > 0 ? policyResult.reasons : decision === "approve" ? [`Target "${targetName}" reviewed and approved for execution`] : [`Target "${targetName}" reviewed — decision: ${decision}`];
|
|
7117
|
+
const supervisorDecision = {
|
|
7118
|
+
decision,
|
|
7119
|
+
targetType,
|
|
7120
|
+
targetName,
|
|
7121
|
+
exists,
|
|
7122
|
+
reasons,
|
|
7123
|
+
missingRequirements: policyResult.missingRequirements,
|
|
7124
|
+
riskFlags: policyResult.riskFlags,
|
|
7125
|
+
requiredChanges: policyResult.requiredChanges,
|
|
7126
|
+
approvalStatus,
|
|
7127
|
+
confidenceScore,
|
|
7128
|
+
reviewPhase,
|
|
7129
|
+
timestamp: timestamp2
|
|
7130
|
+
};
|
|
7131
|
+
_emitTelemetry(directory, supervisorDecision, ctx);
|
|
7132
|
+
return supervisorDecision;
|
|
7133
|
+
}
|
|
7134
|
+
function shouldProceed(decision, mode, canBlock) {
|
|
7135
|
+
if (!decision.exists)
|
|
7136
|
+
return false;
|
|
7137
|
+
if (!canBlock)
|
|
7138
|
+
return true;
|
|
7139
|
+
if (mode === "strict") {
|
|
7140
|
+
return decision.decision === "approve" || decision.decision === "revise";
|
|
7141
|
+
}
|
|
7142
|
+
return decision.decision !== "block" || decision.confidenceScore > 0.3;
|
|
7143
|
+
}
|
|
7144
|
+
function _emitTelemetry(directory, decision, ctx) {
|
|
7145
|
+
try {
|
|
7146
|
+
appendEvent(directory, {
|
|
7147
|
+
session_id: ctx.session_id ?? "session-0",
|
|
7148
|
+
run_id: ctx.run_id ?? "unknown",
|
|
7149
|
+
event: "supervisor.review",
|
|
7150
|
+
agent: "supervisor",
|
|
7151
|
+
status: decision.decision === "approve" ? "ok" : decision.decision === "block" ? "blocked" : decision.decision === "escalate" ? "approved" : "ok",
|
|
7152
|
+
meta: {
|
|
7153
|
+
targetName: decision.targetName,
|
|
7154
|
+
targetType: decision.targetType,
|
|
7155
|
+
exists: decision.exists,
|
|
7156
|
+
decision: decision.decision,
|
|
7157
|
+
confidenceScore: decision.confidenceScore,
|
|
7158
|
+
riskFlags: decision.riskFlags,
|
|
7159
|
+
missingRequirements: decision.missingRequirements,
|
|
7160
|
+
reviewPhase: decision.reviewPhase
|
|
7161
|
+
}
|
|
7162
|
+
});
|
|
7163
|
+
} catch {}
|
|
7164
|
+
}
|
|
7165
|
+
|
|
6410
7166
|
// src/index.ts
|
|
6411
7167
|
function loadRulePaths() {
|
|
6412
7168
|
const __dir = dirname4(fileURLToPath2(import.meta.url));
|
|
@@ -6626,6 +7382,33 @@ var plugin = async (input, _options) => {
|
|
|
6626
7382
|
}
|
|
6627
7383
|
}
|
|
6628
7384
|
orchestratorGuard.check(toolInput.sessionID ?? "", toolInput.tool ?? toolInput.name ?? "");
|
|
7385
|
+
const toolName = toolInput.tool ?? toolInput.name ?? "";
|
|
7386
|
+
if (toolName === "delegate" || toolName === "run-pipeline") {
|
|
7387
|
+
const supConfig = resolveSupervisorConfig(directory);
|
|
7388
|
+
if (supConfig.enabled) {
|
|
7389
|
+
const args = toolOutput?.args ?? toolInput?.args ?? {};
|
|
7390
|
+
const agentTarget = typeof args.agent === "string" ? args.agent.replace(/^@/, "") : Array.isArray(args.steps) && args.steps[0]?.agent ? String(args.steps[0].agent).replace(/^@/, "") : "";
|
|
7391
|
+
if (agentTarget) {
|
|
7392
|
+
const decision = runSupervisorReview(directory, agentTarget, {
|
|
7393
|
+
taskDescription: typeof args.prompt === "string" ? args.prompt : undefined,
|
|
7394
|
+
reviewPhase: "preflight",
|
|
7395
|
+
session_id: toolInput.sessionID ?? toolInput.sessionId ?? ""
|
|
7396
|
+
});
|
|
7397
|
+
const proceed = shouldProceed(decision, supConfig.mode, supConfig.canBlock);
|
|
7398
|
+
appLog(`[Supervisor] ${decision.reviewPhase} review of "${decision.targetName}": ` + `decision=${decision.decision} exists=${decision.exists} confidence=${decision.confidenceScore.toFixed(2)} ` + `${decision.riskFlags.length > 0 ? `risks=[${decision.riskFlags.join("; ")}]` : ""}`);
|
|
7399
|
+
if (!proceed) {
|
|
7400
|
+
const summary = [
|
|
7401
|
+
`[Supervisor] Execution blocked for target "${decision.targetName}".`,
|
|
7402
|
+
...decision.reasons,
|
|
7403
|
+
...decision.missingRequirements.length > 0 ? [`Missing: ${decision.missingRequirements.join(", ")}`] : [],
|
|
7404
|
+
...decision.requiredChanges.length > 0 ? [`Required changes: ${decision.requiredChanges.join("; ")}`] : []
|
|
7405
|
+
].join(`
|
|
7406
|
+
`);
|
|
7407
|
+
throw new Error(summary);
|
|
7408
|
+
}
|
|
7409
|
+
}
|
|
7410
|
+
}
|
|
7411
|
+
}
|
|
6629
7412
|
await telemetryHook({ directory }, toolInput, toolOutput);
|
|
6630
7413
|
await approvalHook({ directory }, toolInput, toolOutput);
|
|
6631
7414
|
await guardRailsHook({ directory }, toolInput, toolOutput);
|
|
@@ -6643,6 +7426,30 @@ var plugin = async (input, _options) => {
|
|
|
6643
7426
|
} catch (err) {
|
|
6644
7427
|
console.error("[FlowDeck Memory] Tool execution error:", err);
|
|
6645
7428
|
}
|
|
7429
|
+
const afterToolName = toolInput.tool ?? toolInput.name ?? "";
|
|
7430
|
+
if (afterToolName === "delegate" || afterToolName === "run-pipeline") {
|
|
7431
|
+
try {
|
|
7432
|
+
const supConfig = resolveSupervisorConfig(directory);
|
|
7433
|
+
if (supConfig.enabled && supConfig.postExecutionReview) {
|
|
7434
|
+
const args = toolOutput?.args ?? toolInput?.args ?? {};
|
|
7435
|
+
const agentTarget = typeof args.agent === "string" ? args.agent.replace(/^@/, "") : Array.isArray(args.steps) && args.steps[0]?.agent ? String(args.steps[0].agent).replace(/^@/, "") : "";
|
|
7436
|
+
if (agentTarget) {
|
|
7437
|
+
const executionErrored = toolOutput?.error != null || toolOutput?.status === "error" || typeof toolOutput?.output === "string" && toolOutput.output.startsWith("Error:");
|
|
7438
|
+
const decision = runSupervisorReview(directory, agentTarget, {
|
|
7439
|
+
taskDescription: typeof args.prompt === "string" ? args.prompt : undefined,
|
|
7440
|
+
reviewPhase: "post-stage",
|
|
7441
|
+
session_id: toolInput.sessionID ?? toolInput.sessionId ?? "",
|
|
7442
|
+
prerequisitesMet: !executionErrored
|
|
7443
|
+
});
|
|
7444
|
+
const logLevel = decision.decision === "block" || decision.decision === "escalate" ? "[Supervisor][WARN]" : "[Supervisor]";
|
|
7445
|
+
appLog(`${logLevel} post-stage review of "${decision.targetName}": ` + `decision=${decision.decision} exists=${decision.exists} confidence=${decision.confidenceScore.toFixed(2)} ` + `executionErrored=${executionErrored} ` + `${decision.riskFlags.length > 0 ? `risks=[${decision.riskFlags.join("; ")}]` : ""}`);
|
|
7446
|
+
if (supConfig.mode === "strict" && !shouldProceed(decision, "strict", supConfig.canBlock)) {
|
|
7447
|
+
appLog(`[Supervisor][STRICT] Post-execution governance violation detected for "${decision.targetName}". ` + `Review the scorecard and telemetry for this run. ` + `Reasons: ${decision.reasons.join("; ")}`);
|
|
7448
|
+
}
|
|
7449
|
+
}
|
|
7450
|
+
}
|
|
7451
|
+
} catch {}
|
|
7452
|
+
}
|
|
6646
7453
|
await contextMonitor["tool.execute.after"](toolInput, toolOutput);
|
|
6647
7454
|
}
|
|
6648
7455
|
};
|