@williambeto/ai-workflow 2.7.1 → 2.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,120 @@
1
+ // src/core/gates/branch-gate.ts
2
+ import { execSync } from "child_process";
3
+ import fs from "fs";
4
+ import path from "path";
5
+ function slugify(value = "task") {
6
+ return String(value).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "task";
7
+ }
8
+ var BranchGate = class {
9
+ protectedBranches;
10
+ logPath;
11
+ cwd;
12
+ constructor({ protectedBranches = ["main", "master"], memoryDir, cwd = process.cwd() } = {}) {
13
+ this.protectedBranches = protectedBranches;
14
+ this.logPath = memoryDir ? path.join(memoryDir, "GATE_ALERTS.log") : null;
15
+ this.cwd = cwd;
16
+ }
17
+ run(command) {
18
+ return execSync(command, {
19
+ cwd: this.cwd,
20
+ encoding: "utf8",
21
+ stdio: ["ignore", "pipe", "pipe"]
22
+ }).trim();
23
+ }
24
+ log(event) {
25
+ if (!this.logPath) return;
26
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
27
+ const logEntry = `[${timestamp}] ${event}
28
+ `;
29
+ try {
30
+ if (!fs.existsSync(path.dirname(this.logPath))) fs.mkdirSync(path.dirname(this.logPath), { recursive: true });
31
+ fs.appendFileSync(this.logPath, logEntry);
32
+ } catch (error) {
33
+ console.error(`Failed to write to gate log: ${error.message}`);
34
+ }
35
+ }
36
+ getDirtyState() {
37
+ const lines = this.run("git status --short").split("\n").filter(Boolean);
38
+ const tracked = lines.filter((line) => !line.startsWith("??"));
39
+ const untracked = lines.filter((line) => line.startsWith("??"));
40
+ return { clean: lines.length === 0, tracked, untracked, lines };
41
+ }
42
+ createScopedBranch(taskSlug) {
43
+ const base = `feat/${slugify(taskSlug)}`;
44
+ let candidate = base;
45
+ let suffix = 2;
46
+ while (true) {
47
+ try {
48
+ this.run(`git show-ref --verify --quiet refs/heads/${candidate}`);
49
+ candidate = `${base}-${suffix++}`;
50
+ } catch {
51
+ break;
52
+ }
53
+ }
54
+ this.run(`git switch -c ${candidate}`);
55
+ return candidate;
56
+ }
57
+ getCurrentBranch() {
58
+ try {
59
+ return this.run("git branch --show-current") || this.run("git symbolic-ref --short HEAD") || "unknown";
60
+ } catch {
61
+ try {
62
+ return this.run("git symbolic-ref --short HEAD") || "unknown";
63
+ } catch {
64
+ return "unknown";
65
+ }
66
+ }
67
+ }
68
+ /**
69
+ * Strictly verifies branch safety without any override bypasses.
70
+ */
71
+ check(overrideIgnored = "", { taskSlug = "implementation", readOnly = false } = {}) {
72
+ if (readOnly) {
73
+ const currentBranch = this.getCurrentBranch();
74
+ return { blocked: false, branch: currentBranch, recovered: false, readOnly: true };
75
+ }
76
+ try {
77
+ try {
78
+ this.run("git rev-parse --is-inside-work-tree");
79
+ } catch (e) {
80
+ const reason = "Git is unavailable or not inside a Git repository. Implementation work is blocked.";
81
+ this.log(`BLOCKED: ${reason}`);
82
+ return { blocked: true, branch: "unknown", reason };
83
+ }
84
+ const currentBranch = this.getCurrentBranch();
85
+ if (!currentBranch || currentBranch === "unknown" || currentBranch.trim() === "") {
86
+ const reason = "Could not determine current Git branch name. Implementation work is blocked.";
87
+ this.log(`BLOCKED: ${reason}`);
88
+ return { blocked: true, branch: "unknown", reason };
89
+ }
90
+ const isProtected = this.protectedBranches.includes(currentBranch);
91
+ if (!isProtected) {
92
+ return { blocked: false, branch: currentBranch, recovered: false };
93
+ }
94
+ const dirty = this.getDirtyState();
95
+ if (!dirty.clean) {
96
+ const reason = `Direct writes to protected branch '${currentBranch}' are prohibited and the branch is dirty (has changes).`;
97
+ this.log(`BLOCKED: ${reason}`);
98
+ return { blocked: true, branch: currentBranch, reason, dirtyState: dirty };
99
+ }
100
+ const recoveredBranch = this.createScopedBranch(taskSlug);
101
+ this.log(`AUTO-RECOVERED '${currentBranch}' -> '${recoveredBranch}'`);
102
+ return {
103
+ blocked: false,
104
+ branch: recoveredBranch,
105
+ branchBefore: currentBranch,
106
+ recovered: true,
107
+ dirtyState: dirty
108
+ };
109
+ } catch (error) {
110
+ const reason = `Git command failure on branch gate check: ${error.message}`;
111
+ this.log(`BLOCKED: ${reason}`);
112
+ return { blocked: true, branch: "unknown", error: error.message, reason, recovered: false };
113
+ }
114
+ }
115
+ };
116
+
117
+ export {
118
+ BranchGate
119
+ };
120
+ //# sourceMappingURL=chunk-AINIR25D.js.map
@@ -0,0 +1,46 @@
1
+ // src/core/backup.ts
2
+ import fs from "fs/promises";
3
+ import path from "path";
4
+ function escapeRelativePath(relativePath) {
5
+ return relativePath.replace(/[\\/]/g, "__");
6
+ }
7
+ async function createManagedBackup(filePath, { cwd, backupRoot, maxPerFile = 20 }) {
8
+ const relativePath = path.relative(cwd, filePath);
9
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
10
+ const backupName = `${escapeRelativePath(relativePath)}.${stamp}.bak`;
11
+ const absoluteBackupRoot = path.join(cwd, backupRoot);
12
+ const backupPath = path.join(absoluteBackupRoot, backupName);
13
+ await fs.mkdir(absoluteBackupRoot, { recursive: true });
14
+ await fs.copyFile(filePath, backupPath);
15
+ const prefix = `${escapeRelativePath(relativePath)}.`;
16
+ const entries = await fs.readdir(absoluteBackupRoot);
17
+ const matching = entries.filter((entry) => entry.startsWith(prefix) && entry.endsWith(".bak")).sort();
18
+ if (matching.length > maxPerFile) {
19
+ const excess = matching.slice(0, matching.length - maxPerFile);
20
+ await Promise.all(excess.map((entry) => fs.unlink(path.join(absoluteBackupRoot, entry))));
21
+ }
22
+ return backupPath;
23
+ }
24
+ async function createManagedPathBackup(targetPath, { cwd, backupRoot, maxPerFile = 20 }) {
25
+ const relativePath = path.relative(cwd, targetPath);
26
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
27
+ const backupName = `${escapeRelativePath(relativePath)}.${stamp}.bak`;
28
+ const absoluteBackupRoot = path.join(cwd, backupRoot);
29
+ const backupPath = path.join(absoluteBackupRoot, backupName);
30
+ await fs.mkdir(absoluteBackupRoot, { recursive: true });
31
+ await fs.rename(targetPath, backupPath);
32
+ const prefix = `${escapeRelativePath(relativePath)}.`;
33
+ const entries = await fs.readdir(absoluteBackupRoot);
34
+ const matching = entries.filter((entry) => entry.startsWith(prefix) && entry.endsWith(".bak")).sort();
35
+ if (matching.length > maxPerFile) {
36
+ const excess = matching.slice(0, matching.length - maxPerFile);
37
+ await Promise.all(excess.map((entry) => fs.rm(path.join(absoluteBackupRoot, entry), { recursive: true, force: true })));
38
+ }
39
+ return backupPath;
40
+ }
41
+
42
+ export {
43
+ createManagedBackup,
44
+ createManagedPathBackup
45
+ };
46
+ //# sourceMappingURL=chunk-AY33SA5W.js.map
@@ -6552,10 +6552,12 @@ var EvidenceValidator = class {
6552
6552
  cwd;
6553
6553
  skipFileCheck;
6554
6554
  skipGitCheck;
6555
- constructor({ cwd = process.cwd(), skipFileCheck = false, skipGitCheck = false } = {}) {
6555
+ requireWorkflowEvidence;
6556
+ constructor({ cwd = process.cwd(), skipFileCheck = false, skipGitCheck = false, requireWorkflowEvidence = false } = {}) {
6556
6557
  this.cwd = cwd;
6557
6558
  this.skipFileCheck = skipFileCheck;
6558
6559
  this.skipGitCheck = skipGitCheck;
6560
+ this.requireWorkflowEvidence = requireWorkflowEvidence;
6559
6561
  }
6560
6562
  async validate() {
6561
6563
  const evidencePath = path.join(this.cwd, "EVIDENCE.json");
@@ -6595,6 +6597,21 @@ var EvidenceValidator = class {
6595
6597
  } catch (e) {
6596
6598
  return { valid: false, reason: `Evidence schema validation failed: ${e.message}` };
6597
6599
  }
6600
+ if (this.requireWorkflowEvidence) {
6601
+ const phases = evidence.phaseOrder || [];
6602
+ const actors = evidence.confirmedActors || [];
6603
+ const hashes = evidence.artifactHashes || {};
6604
+ if (!evidence.workflowId || !evidence.baseSha || !hashes.approved || !hashes.technicalPlan) {
6605
+ return { valid: false, reason: "Required executable SDD evidence fields are missing." };
6606
+ }
6607
+ if (phases.join(",") !== "specification,planning,implementation" && phases.join(",") !== "planning,implementation") {
6608
+ return { valid: false, reason: "Executable SDD phase order is invalid." };
6609
+ }
6610
+ const expectedActors = phases.map((phase) => phase === "specification" ? "Nexus" : phase === "planning" ? "Orion" : "Astra");
6611
+ if (actors.length !== expectedActors.length || actors.some((actor, index) => actor.requested !== expectedActors[index] || actor.applied !== expectedActors[index])) {
6612
+ return { valid: false, reason: "Executable SDD confirmed actor evidence is incomplete or out of order." };
6613
+ }
6614
+ }
6598
6615
  const changedFiles = evidence.changedFiles || [];
6599
6616
  const resolvedCwd = path.resolve(this.cwd);
6600
6617
  for (const file of changedFiles) {
@@ -6655,7 +6672,10 @@ var EvidenceValidator = class {
6655
6672
  return { valid: true };
6656
6673
  }
6657
6674
  };
6675
+
6658
6676
  export {
6677
+ require_codegen,
6678
+ require_ajv,
6659
6679
  EvidenceValidator
6660
6680
  };
6661
- //# sourceMappingURL=evidence-validator-76ZQQYDU.js.map
6681
+ //# sourceMappingURL=chunk-FNT7DN3N.js.map
@@ -112,17 +112,17 @@ var StackDetector = class {
112
112
  // src/core/validation/delivery-decision-engine.ts
113
113
  import fs2 from "fs/promises";
114
114
  import path2 from "path";
115
- function parseRequest(request = "") {
115
+ function parseDeliveryRequest(request = "") {
116
116
  const req = request.toLowerCase();
117
117
  let targetStack = null;
118
118
  if (/\b(vue|nuxt|quasar)\b/.test(req)) targetStack = "vue";
119
- else if (/\b(react|next)\b/.test(req)) targetStack = "react";
119
+ else if (/\breact\b|\bnext(?:\.js|js)\b|\bnext\s+js\b/.test(req)) targetStack = "react";
120
120
  else if (/\b(angular|svelte)\b/.test(req)) targetStack = "svelte-or-angular";
121
- else if (/\b(laravel|django|rails|express|nest|fastify)\b/.test(req)) {
121
+ else if (/\b(laravel|django|rails|express|nest|fastify|flask)\b/.test(req)) {
122
122
  if (/\blaravel\b/.test(req)) targetStack = "laravel";
123
123
  else targetStack = "backend";
124
124
  } else if (/\b(wordpress|wp)\b/.test(req)) targetStack = "wordpress";
125
- else if (/\b(cli|command|bin)\b/.test(req)) targetStack = "cli";
125
+ else if (/\b(cli|command[- ]line|executable|bin)\b/.test(req) || /\b(add|create|implement|build|scaffold)\b.{0,40}\bcommand\b/.test(req)) targetStack = "cli";
126
126
  let intent = "implementation";
127
127
  if (/\b(document|doc|readme)\b/.test(req) || /\b(create|add|write|update|edit)\b.*\b[\w.-]+\.md\b/.test(req)) intent = "docs";
128
128
  else if (/\b(audit|security|inspect|dependencies)\b/.test(req)) intent = "audit";
@@ -281,7 +281,7 @@ var DeliveryDecisionEngine = class {
281
281
  const isLaravel = ctx.isLaravel || false;
282
282
  const isCli = ctx.isCli || false;
283
283
  const usesJsFramework = ctx.usesJsFramework || null;
284
- const parsed = parseRequest(userRequest);
284
+ const parsed = parseDeliveryRequest(userRequest);
285
285
  const targetStack = parsed.targetStack;
286
286
  const intent = parsed.intent;
287
287
  let decision = "IMPLEMENT_IN_EXISTING_CONTEXT";
@@ -289,6 +289,7 @@ var DeliveryDecisionEngine = class {
289
289
  let forbiddenSubstitutes = ["success-without-validation"];
290
290
  let validationRequired = ["build-or-equivalent"];
291
291
  let approvalRequired = false;
292
+ let reason = null;
292
293
  if (intent === "audit") {
293
294
  decision = "READ_ONLY_AUDIT";
294
295
  allowedArtifactShape = "audit-report";
@@ -321,13 +322,22 @@ var DeliveryDecisionEngine = class {
321
322
  validationRequired.push("dependency-install-or-verification", "runtime-smoke-or-equivalent");
322
323
  }
323
324
  } else {
324
- const hasConflict = targetStack === "vue" && detectedStacks.includes("react") || targetStack === "react" && detectedStacks.includes("vue") || targetStack === "laravel" && !detectedStacks.includes("laravel") && detectedStacks.length > 0 || targetStack === "wordpress" && !detectedStacks.includes("wordpress") && detectedStacks.length > 0;
325
+ const requestedFrontend = targetStack === "vue" || targetStack === "react" || targetStack === "svelte-or-angular";
326
+ const detectedFrontend = detectedStacks.find((stack) => ["react", "next", "vue", "nuxt", "svelte", "@angular/core", "quasar"].includes(stack));
327
+ const frontendMatches = targetStack === "react" && ["react", "next"].includes(detectedFrontend || "") || targetStack === "vue" && ["vue", "nuxt", "quasar"].includes(detectedFrontend || "") || targetStack === "svelte-or-angular" && ["svelte", "@angular/core"].includes(detectedFrontend || "");
328
+ const hasConflict = requestedFrontend && Boolean(detectedFrontend) && !frontendMatches || targetStack === "laravel" && !detectedStacks.includes("laravel") && detectedStacks.length > 0 || targetStack === "wordpress" && !detectedStacks.includes("wordpress") && detectedStacks.length > 0;
325
329
  if (hasConflict) {
326
330
  decision = "BLOCK_STACK_CONFLICT";
331
+ reason = `Requested stack '${targetStack}' conflicts with the active project context (${detectedStacks.join(", ") || "unknown"}).`;
332
+ } else if (requestedFrontend && !frontendMatches) {
333
+ decision = "BLOCK_UNSUPPORTED_CONTEXT";
334
+ reason = `Requested stack '${targetStack}' is not present in the active project context.`;
327
335
  } else if (targetStack === "laravel" && !detectedStacks.includes("laravel")) {
328
336
  decision = "BLOCK_UNSUPPORTED_CONTEXT";
337
+ reason = "Laravel was requested, but the active project is not a Laravel project.";
329
338
  } else if (targetStack === "wordpress" && !detectedStacks.includes("wordpress")) {
330
339
  decision = "BLOCK_UNSUPPORTED_CONTEXT";
340
+ reason = "WordPress was requested, but the active project is not a WordPress project.";
331
341
  } else {
332
342
  if (targetStack === "vue" || targetStack === "react") {
333
343
  decision = "IMPLEMENT_IN_EXISTING_CONTEXT";
@@ -342,6 +352,7 @@ var DeliveryDecisionEngine = class {
342
352
  }
343
353
  if (userRequest && userRequest.trim().length < 3) {
344
354
  decision = "BLOCK_UNCLEAR_SCOPE";
355
+ reason = "The request is too short to establish a safe delivery scope.";
345
356
  }
346
357
  return {
347
358
  intent: intent === "implementation" ? "implementation" : intent,
@@ -354,7 +365,9 @@ var DeliveryDecisionEngine = class {
354
365
  allowedArtifactShape,
355
366
  forbiddenSubstitutes,
356
367
  validationRequired,
357
- approvalRequired: explicitApprovals.length > 0
368
+ approvalRequired: explicitApprovals.length > 0,
369
+ deliveryOutcome: decision.startsWith("BLOCK_") ? "NEEDS_CLARIFICATION" : null,
370
+ reason
358
371
  };
359
372
  }
360
373
  };
@@ -531,11 +544,12 @@ var ArtifactFidelityGate = class {
531
544
  violations.push("Release/deploy task proceeds without explicit approval token mismatch.");
532
545
  }
533
546
  }
534
- const reqReact = /(react|next)/.test(req);
535
- const reqVue = /(vue|nuxt)/.test(req);
536
- const reqBackend = /(laravel|django|rails|express|nest|fastify|flask)/.test(req);
537
- const reqWp = /(wordpress|wp)/.test(req);
538
- const reqCli = /(cli|command)/.test(req);
547
+ const requestedStack = Object.hasOwn(deliveryDecision, "requestedStack") ? deliveryDecision.requestedStack : parseDeliveryRequest(userRequest).targetStack;
548
+ const reqReact = requestedStack === "react";
549
+ const reqVue = requestedStack === "vue";
550
+ const reqBackend = requestedStack === "backend" || requestedStack === "laravel";
551
+ const reqWp = requestedStack === "wordpress";
552
+ const reqCli = requestedStack === "cli";
539
553
  if (reqReact && this.deliveryClass === "standalone-web" && !/prototype/.test(req)) {
540
554
  violations.push("React framework page requested, but a standalone HTML/CSS substitute mismatch was delivered instead.");
541
555
  }
@@ -746,10 +760,321 @@ async function hasWpPluginHeader(cwd, file) {
746
760
  }
747
761
  }
748
762
 
763
+ // src/core/request-router.ts
764
+ import path4 from "path";
765
+ import fs4 from "fs";
766
+ var RequestRouter = class {
767
+ cwd;
768
+ constructor({ cwd = process.cwd() } = {}) {
769
+ this.cwd = cwd;
770
+ }
771
+ /**
772
+ * Understands and classifies a natural language request.
773
+ */
774
+ understand(request) {
775
+ const text = String(request).trim();
776
+ if (!text) {
777
+ throw new Error("Cannot classify an empty request.");
778
+ }
779
+ const { operationType, requestedCapability } = this.extractCapability(text);
780
+ const mutationIntent = this.determineMutationIntent(text, operationType);
781
+ const deliveryMode = this.determineDeliveryMode(text, mutationIntent);
782
+ const capabilityRoles = this.determineCapabilityRoles(text);
783
+ const riskAssessment = this.determineRiskAssessment(text, mutationIntent);
784
+ const riskLevel = riskAssessment.riskLevel;
785
+ const { safetyConstraints, requiredEvidence } = this.determineSafetyAndEvidence(text, mutationIntent);
786
+ return {
787
+ rawRequest: text,
788
+ taskGoal: `Perform ${operationType} operation under ${mutationIntent} intent`,
789
+ requestedActor: void 0,
790
+ invalidActor: void 0,
791
+ requestedCapability,
792
+ capabilityRoles,
793
+ operationType,
794
+ mutationIntent,
795
+ deliveryMode,
796
+ riskLevel,
797
+ requiredEvidence,
798
+ safetyConstraints,
799
+ specPolicy: riskAssessment.specPolicy,
800
+ matchedSignals: riskAssessment.matchedSignals,
801
+ riskDrivers: riskAssessment.riskDrivers
802
+ };
803
+ }
804
+ extractCapability(text) {
805
+ let operationType = "explain";
806
+ let requestedCapability = void 0;
807
+ const capabilityMappings = [
808
+ { op: "discover", pattern: /\b(discover|inspect|map|scan|structure|architecture|repo|search|find)\b/i },
809
+ { op: "validate", pattern: /\b(validate|audit|review|check|verify|test)\b/i },
810
+ { op: "implement", pattern: /\b(implement|implementa|build|create|crie|cria|add|develop|write|change|modify|atualiza|atualizar|refatora|refatorar)\b/i },
811
+ { op: "remediate", pattern: /\b(remediate|remedia|heal|healing)\b/i },
812
+ { op: "fix", pattern: /\b(fix|repair|correct|corrige|corrigir|solve)\b/i },
813
+ { op: "release", pattern: /\b(release|tag|prepare release|prepara(?:r)?\s+(?:a\s+)?release)\b/i },
814
+ { op: "deploy", pattern: /\b(deploy|publish|push|produção|producao)\b/i }
815
+ ];
816
+ for (const mapping of capabilityMappings) {
817
+ const match = text.match(mapping.pattern);
818
+ if (match) {
819
+ operationType = mapping.op;
820
+ requestedCapability = match[1];
821
+ break;
822
+ }
823
+ }
824
+ return { operationType, requestedCapability };
825
+ }
826
+ determineMutationIntent(text, operationType) {
827
+ const writePatterns = /\b(implement|implementa|build|fix|corrige|write|change|modify|create|crie|cria|add|refactor|refatora|refatorar|update|atualiza|atualizar)\b/i;
828
+ const readOnlyPatterns = /\b(analyze|check|inspect|review|find|search|show|list|read|view|verify|validate|discover|map)\b/i;
829
+ let mutationIntent = "write";
830
+ if (/\b(read-only|readonly)\b/i.test(text)) {
831
+ mutationIntent = "readonly";
832
+ } else if (writePatterns.test(text)) {
833
+ mutationIntent = "write";
834
+ } else if (readOnlyPatterns.test(text) || ["discover", "validate"].includes(operationType)) {
835
+ mutationIntent = "readonly";
836
+ }
837
+ if (/\b(publish|release|deploy|produção|producao|push to npm|push to remote)\b/i.test(text)) {
838
+ mutationIntent = "publish";
839
+ }
840
+ return mutationIntent;
841
+ }
842
+ determineDeliveryMode(text, mutationIntent) {
843
+ if (mutationIntent === "readonly") return "answer-only";
844
+ const explicitAnswerOnlyMarker = /\b(answer only|preview only|example only|snippet only|apenas (?:responda|mostre|um exemplo|um trecho)|somente (?:resposta|preview|exemplo|trecho))\b/i;
845
+ const workspaceTarget = "(?:files?|workspace|repository|repo|code(?:base)?|project)";
846
+ const portugueseWorkspaceTarget = "(?:arquivos?|workspace|reposit[o\xF3]rio|repo|c[o\xF3]digo|codebase|projeto)";
847
+ const explicitNoWorkspaceEdit = new RegExp(
848
+ `\\b(?:(?:without\\s+(?:editing|changing|modifying)|(?:do not|don't)\\s+(?:edit|change|modify))\\s+(?:the\\s+)?${workspaceTarget}|no\\s+${workspaceTarget}\\s+changes?|(?:sem\\s+(?:editar|alterar|modificar)|n[a\xE3]o\\s+(?:editar|alterar|modificar|edite|altere|modifique))\\s+(?:(?:os?|as?)\\s+)?${portugueseWorkspaceTarget})\\b`,
849
+ "i"
850
+ );
851
+ const standaloneNoEdit = /(?:\b(?:without\s+(?:editing|changing|modifying)|(?:do not|don't)\s+(?:edit|change|modify)|sem\s+(?:editar|alterar|modificar)|n[aã]o\s+(?:editar|alterar|modificar|edite|altere|modifique)))\s*[.!?]?\s*$/i;
852
+ return explicitAnswerOnlyMarker.test(text) || explicitNoWorkspaceEdit.test(text) || standaloneNoEdit.test(text) ? "answer-only" : "workspace";
853
+ }
854
+ determineCapabilityRoles(text) {
855
+ const roles = [];
856
+ const add = (role, pattern) => {
857
+ if (pattern.test(text)) roles.push(role);
858
+ };
859
+ add("frontend-development", /\b(frontend|react|vue|angular|svelte|component|interface|ui|css|html)\b/i);
860
+ add("backend-development", /\b(backend|api|endpoint|server|database|laravel|django|express|nest|fastify)\b/i);
861
+ add("documentation", /\b(document|documentation|docs?|readme|markdown)\b/i);
862
+ add("qa-workflow", /\b(test|tests|validate|audit|review|verify|quality)\b/i);
863
+ add("release-workflow", /\b(release|publish|deploy|tag)\b/i);
864
+ return [...new Set(roles)];
865
+ }
866
+ determineRiskAssessment(text, mutationIntent) {
867
+ const matchedSignals = [];
868
+ const add = (signal, pattern) => {
869
+ if (pattern.test(text)) {
870
+ matchedSignals.push(signal);
871
+ return true;
872
+ }
873
+ return false;
874
+ };
875
+ const taskTypeSignals = {
876
+ docsCopy: add("task:docs-copy", /\b(README|docs?|documenta[cç][aã]o|typo|copy|frase|comment|coment[áa]rio)\b/i),
877
+ refactor: add("task:refactor", /\b(refactor|refatora|refatorar|simplificar|mais simples de manter|manuten[cç][aã]o)\b/i),
878
+ noBehaviorChange: add("scope:no-behavior-change", /\b(sem alterar (?:o )?comportamento|without changing behavior|without behaviour change|no behavior change|comportamento existente)\b/i),
879
+ landingPage: add("task:landing-page", /\b(landing page|hotsite|p[áa]gina de venda|p[áa]gina comercial)\b/i),
880
+ dashboard: add("task:dashboard", /\b(dashboard|painel)\b/i),
881
+ bugfix: add("task:bugfix", /\b(bug|fix|corrige|corrigir|erro|falha|n[aã]o envia|não envia)\b/i),
882
+ release: add("task:release", /\b(release|deploy|produ[cç][aã]o|publish|publicar|lan[cç]amento)\b/i)
883
+ };
884
+ const domainRiskSignals = {
885
+ adult: add("domain:adult", /\b(er[óo]tic[oa]s?|adult[oa]s?|sexual|sexo|porn(?:o|ografia)?|18\+)\b/i),
886
+ auth: add("domain:auth", /\b(login|auth|autentica[cç][aã]o|autoriza[cç][aã]o|permiss(?:ão|oes|ões)|admin|administrador|roles?|rbac)\b/i),
887
+ payment: add("domain:payment", /\b(pagamento|checkout|billing|cobran[cç]a|fatura[cç][aã]o|assinatura|cart[aã]o|stripe|paypal)\b/i),
888
+ securityGovernance: add("domain:security-governance", /\b(SECURITY\.md|seguran[cç]a|security policy|pol[ií]tica de seguran[cç]a|governance|governan[cç]a|compliance|branch safety)\b/i)
889
+ };
890
+ const impactSignals = {
891
+ personalData: add("impact:personal-data", /\b(leads?|dados pessoais|personal data|crm|contatos?|contactos?|clientes?|exporta[cç][aã]o|exportar|armazenamento|armazenar|integra[cç][aã]o|integrar)\b/i),
892
+ productionRelease: mutationIntent === "publish" || add("impact:production-release", /\b(produ[cç][aã]o|release|deploy|publish|publicar|npm)\b/i),
893
+ adminPermissions: add("impact:admin-permissions", /\b(admin|administrador|permiss(?:ão|oes|ões)|roles?|rbac)\b/i)
894
+ };
895
+ if (mutationIntent === "publish" && !matchedSignals.includes("impact:production-release")) {
896
+ matchedSignals.push("impact:production-release");
897
+ }
898
+ const scopeSignals = {
899
+ localized: add("scope:localized", /\b(localizado|uma frase|typo|pequeno|small|tiny|simples|README)\b/i),
900
+ broad: add("scope:broad", /\b(plataforma|sistema|completo|premium|end-to-end|amplo|v[aá]rias|m[úu]ltiplas|empresa)\b/i),
901
+ architectural: add("scope:architectural", /\b(deep|architectural|arquitetural|migration|migra[cç][aã]o|major|full|\[deep\])\b/i),
902
+ explicitHigh: add("scope:explicit-high", /\b(high-risk|high risk|alto risco)\b/i),
903
+ explicitRequiredSpec: add("scope:explicit-required-spec", /\b(formal spec|required spec|especifica[cç][aã]o formal)\b/i),
904
+ explicitRequiredEvidence: add("scope:explicit-required-evidence", /\b(required evidence|evidence required|evid[eê]ncia obrigat[óo]ria)\b/i)
905
+ };
906
+ let riskLevel = "medium";
907
+ const riskDrivers = [];
908
+ const highDrivers = [
909
+ ["scope:explicit-high", scopeSignals.explicitHigh],
910
+ ["scope:architectural", scopeSignals.architectural],
911
+ ["domain:adult", domainRiskSignals.adult],
912
+ ["domain:auth", domainRiskSignals.auth],
913
+ ["domain:payment", domainRiskSignals.payment],
914
+ ["domain:security-governance", domainRiskSignals.securityGovernance],
915
+ ["impact:production-release", impactSignals.productionRelease],
916
+ ["impact:admin-permissions", impactSignals.adminPermissions],
917
+ ["impact:personal-data", taskTypeSignals.dashboard && impactSignals.personalData]
918
+ ];
919
+ for (const [signal, active] of highDrivers) {
920
+ if (active) {
921
+ riskDrivers.push(signal);
922
+ }
923
+ }
924
+ if (riskDrivers.length > 0) {
925
+ riskLevel = "high";
926
+ } else if (mutationIntent === "readonly") {
927
+ riskLevel = "low";
928
+ riskDrivers.push("intent:readonly");
929
+ } else if (taskTypeSignals.docsCopy && scopeSignals.localized) {
930
+ riskLevel = "low";
931
+ riskDrivers.push("task:docs-copy");
932
+ } else if (taskTypeSignals.refactor || taskTypeSignals.bugfix || taskTypeSignals.landingPage || taskTypeSignals.dashboard) {
933
+ riskLevel = "medium";
934
+ if (taskTypeSignals.refactor) riskDrivers.push("task:refactor");
935
+ if (taskTypeSignals.bugfix) riskDrivers.push("task:bugfix");
936
+ if (taskTypeSignals.landingPage) riskDrivers.push("task:landing-page");
937
+ if (taskTypeSignals.dashboard) riskDrivers.push("task:dashboard");
938
+ }
939
+ const specPolicy = riskLevel === "high" || scopeSignals.explicitRequiredSpec || taskTypeSignals.landingPage && scopeSignals.broad ? "required" : "none";
940
+ return {
941
+ riskLevel,
942
+ specPolicy,
943
+ matchedSignals: [...new Set(matchedSignals)],
944
+ riskDrivers: [...new Set(riskDrivers)]
945
+ };
946
+ }
947
+ determineSafetyAndEvidence(text, mutationIntent) {
948
+ const safetyConstraints = [];
949
+ const requiredEvidence = [];
950
+ if (mutationIntent === "publish") {
951
+ safetyConstraints.push("Require release gate", "Awaiting user authorization");
952
+ requiredEvidence.push("release-decision", "tarball-hash");
953
+ } else if (mutationIntent === "write") {
954
+ safetyConstraints.push("Require branch gate", "No direct commits to main");
955
+ requiredEvidence.push("tests", "commit-hash");
956
+ } else {
957
+ safetyConstraints.push("No workspace mutations allowed");
958
+ }
959
+ if (/\b(bypass|force|direct)\b/i.test(text)) {
960
+ safetyConstraints.push("Block bypass attempts");
961
+ }
962
+ if (/\b(required evidence|evidence required)\b/i.test(text)) {
963
+ requiredEvidence.push("user-required-evidence");
964
+ }
965
+ if (/\b(formal spec|required spec)\b/i.test(text)) {
966
+ safetyConstraints.push("Require formal specification");
967
+ }
968
+ return { safetyConstraints, requiredEvidence };
969
+ }
970
+ /**
971
+ * Decides the routing decision.
972
+ */
973
+ route(understanding) {
974
+ const { operationType, mutationIntent, rawRequest, deliveryMode, capabilityRoles } = understanding;
975
+ const requestedActor = void 0;
976
+ if (/\b(bypass safety|bypass validation|force push|directly to main|push to remote without gate)\b/i.test(rawRequest)) {
977
+ return {
978
+ requestedActor,
979
+ selectedActor: "Atlas",
980
+ mutationOwner: "ControlPlane",
981
+ capabilityRoles,
982
+ operationType,
983
+ mutationIntent,
984
+ deliveryMode,
985
+ permissionDecision: "blocked",
986
+ reason: "Security block: Attempt to bypass safety or push directly is forbidden.",
987
+ workflowPath: ["Atlas"]
988
+ };
989
+ }
990
+ let { selectedActor, permissionDecision, reason } = this.evaluateActorPermission(operationType, mutationIntent);
991
+ let workflowPath = ["Atlas"];
992
+ if (selectedActor === "Phoenix") {
993
+ const requestPath = path4.join(this.cwd, ".ai-workflow/remediation-request.json");
994
+ let hasFindings = false;
995
+ if (fs4.existsSync(requestPath)) {
996
+ try {
997
+ const reqData = JSON.parse(fs4.readFileSync(requestPath, "utf8"));
998
+ if (reqData.affectedChecks && reqData.affectedChecks.length > 0) {
999
+ hasFindings = true;
1000
+ }
1001
+ } catch {
1002
+ }
1003
+ }
1004
+ if (!hasFindings) {
1005
+ permissionDecision = "blocked";
1006
+ reason = "Blocked: Phoenix acts only with concrete findings and bounded remediation.";
1007
+ }
1008
+ }
1009
+ if (understanding.specPolicy === "required") {
1010
+ workflowPath.push("Nexus", "Orion");
1011
+ }
1012
+ if (selectedActor && !workflowPath.includes(selectedActor)) {
1013
+ workflowPath.push(selectedActor);
1014
+ }
1015
+ const mutationOwner = mutationIntent === "readonly" || deliveryMode === "answer-only" ? null : mutationIntent === "publish" ? "Astra" : selectedActor === "Astra" || selectedActor === "Phoenix" ? selectedActor : "ControlPlane";
1016
+ if (mutationOwner && mutationOwner !== "ControlPlane" && !workflowPath.includes(mutationOwner)) {
1017
+ workflowPath.push(mutationOwner);
1018
+ }
1019
+ if (selectedActor === "Atlas" && ["discover", "validate", "fix", "release", "deploy"].includes(operationType)) {
1020
+ permissionDecision = "blocked";
1021
+ reason = "Atlas self-execution guard: Atlas cannot silently execute specialized actor or capability requests.";
1022
+ }
1023
+ if (mutationOwner === "Astra" && understanding.riskLevel === "high") {
1024
+ workflowPath.push("Sage", "Phoenix", "Sage");
1025
+ }
1026
+ if (understanding.specPolicy === "none") {
1027
+ workflowPath = workflowPath.filter((actor) => {
1028
+ if (actor === "Nexus" && selectedActor !== "Nexus") return false;
1029
+ if (actor === "Orion" && selectedActor !== "Orion") return false;
1030
+ return true;
1031
+ });
1032
+ }
1033
+ return {
1034
+ requestedActor,
1035
+ selectedActor,
1036
+ mutationOwner,
1037
+ capabilityRoles,
1038
+ operationType,
1039
+ mutationIntent,
1040
+ deliveryMode,
1041
+ permissionDecision,
1042
+ reason,
1043
+ workflowPath
1044
+ };
1045
+ }
1046
+ evaluateActorPermission(operationType, mutationIntent) {
1047
+ let selectedActor;
1048
+ let permissionDecision = "allowed";
1049
+ let reason = "Routed successfully.";
1050
+ if (mutationIntent === "publish") {
1051
+ selectedActor = "Orion";
1052
+ reason = "Routed to Orion for release planning and approval gates.";
1053
+ } else if (mutationIntent === "write" && operationType !== "remediate") {
1054
+ selectedActor = "Astra";
1055
+ reason = "Routed to Astra because write intent takes mutation ownership precedence.";
1056
+ } else if (operationType === "discover") {
1057
+ selectedActor = "Nexus";
1058
+ } else if (operationType === "validate") {
1059
+ selectedActor = "Sage";
1060
+ } else if (operationType === "remediate") {
1061
+ selectedActor = "Phoenix";
1062
+ } else if (operationType === "implement" || operationType === "fix") {
1063
+ selectedActor = "Astra";
1064
+ } else if (operationType === "release" || operationType === "deploy") {
1065
+ selectedActor = "Orion";
1066
+ } else {
1067
+ selectedActor = mutationIntent === "write" ? "Astra" : "Atlas";
1068
+ }
1069
+ return { selectedActor, permissionDecision, reason };
1070
+ }
1071
+ };
1072
+
749
1073
  export {
750
1074
  MergeGate,
751
1075
  StackDetector,
752
1076
  DeliveryDecisionEngine,
753
- ArtifactFidelityGate
1077
+ ArtifactFidelityGate,
1078
+ RequestRouter
754
1079
  };
755
- //# sourceMappingURL=chunk-XW747GIG.js.map
1080
+ //# sourceMappingURL=chunk-H7GIKXFO.js.map