@williambeto/ai-workflow 2.4.9 → 2.6.3
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/bin/ai-workflow.d.ts +3 -0
- package/bin/ai-workflow.js +2669 -1
- package/bin/ai-workflow.js.map +1 -0
- package/chunk-2AOV2ATY.js +749 -0
- package/chunk-5WRI5ZAA.js +31 -0
- package/chunk-6EMG6FAO.js +734 -0
- package/chunk-UHLZX2EG.js +1827 -0
- package/core/index.d.ts +440 -0
- package/core/index.js +32 -0
- package/core/index.js.map +1 -0
- package/dist-assets/AGENTS.md +23 -6
- package/dist-assets/agents/atlas.md +8 -8
- package/dist-assets/agents/nexus.md +4 -4
- package/dist-assets/agents/orion.md +1 -1
- package/dist-assets/agents/phoenix.md +1 -1
- package/dist-assets/agents/sage.md +1 -1
- package/dist-assets/commands/implement.md +1 -1
- package/dist-assets/docs/QUICKSTART.md +8 -8
- package/dist-assets/docs/cli-reference.md +7 -7
- package/dist-assets/docs/compatibility/provider-usage.md +1 -9
- package/dist-assets/docs/compatibility/runtime-matrix.md +5 -6
- package/dist-assets/docs/consumer-onboarding.md +2 -2
- package/dist-assets/docs/design-patterns-policy.md +1 -1
- package/dist-assets/docs/npm-consumer-quickstart.md +70 -109
- package/dist-assets/docs/policies/01-BRANCH_GATE.md +1 -1
- package/dist-assets/docs/policies/06-FINAL_EVIDENCE_CONTRACT.md +2 -2
- package/dist-assets/docs/policies/08-PRODUCT_TRUTHFULNESS_AND_PROJECT_DOCS.md +1 -1
- package/dist-assets/docs/policies/10-BEHAVIORAL_CONTRACT_HARDENING.md +1 -1
- package/dist-assets/docs/policies/ORCHESTRATION_PROTOCOL.md +8 -9
- package/dist-assets/docs/policies/PROCEDURE_DELIVERY_ARTIFACTS.md +4 -10
- package/dist-assets/docs/policies/SKILLS_COMMON_GOVERNANCE.md +1 -1
- package/dist-assets/docs/profiles/README.md +2 -2
- package/dist-assets/docs/specs/runtime-operational-contract.md +12 -12
- package/dist-assets/docs/troubleshooting-guide.md +2 -2
- package/dist-assets/docs/visual-validation-guide.md +3 -3
- package/dist-assets/schemas/evidence.schema.json +109 -0
- package/dist-assets/templates/ANTIGRAVITY.md.template +7 -5
- package/dist-assets/templates/CLAUDE.md.template +7 -5
- package/dist-assets/templates/CODEX.md.template +7 -5
- package/evidence-validator-76ZQQYDU.js +6661 -0
- package/package.json +7 -64
- package/{src/core/workspace/read-only-workspace.js → read-only-workspace-PZBE7KAX.js} +14 -31
- package/{src/commands/validate.js → validate-Q2NLLQ5G.js} +30 -22
- package/AGENTS.md +0 -34
- package/CHANGELOG.md +0 -259
- package/LICENSE +0 -21
- package/PUBLISH_MANIFEST.json +0 -34
- package/README.md +0 -129
- package/dist-assets/templates/.geminiignore.template +0 -8
- package/dist-assets/templates/GEMINI.md.template +0 -20
- package/docs/compatibility/provider-usage.md +0 -46
- package/docs/compatibility/runtime-matrix.md +0 -31
- package/docs/getting-started/DESKTOP_PROMPT.md +0 -52
- package/docs/getting-started/authoring-custom-skills.md +0 -80
- package/docs/getting-started/quickstart.md +0 -117
- package/docs/getting-started/troubleshooting.md +0 -123
- package/docs/getting-started/upgrading-to-v2.md +0 -62
- package/src/adapters/index.js +0 -4
- package/src/adapters/platforms/antigravity.js +0 -382
- package/src/adapters/platforms/claude.js +0 -126
- package/src/adapters/platforms/codex.js +0 -113
- package/src/adapters/platforms/gemini.js +0 -245
- package/src/cli.js +0 -155
- package/src/commands/clean.js +0 -106
- package/src/commands/collect-evidence.js +0 -25
- package/src/commands/doctor.js +0 -202
- package/src/commands/execute.js +0 -382
- package/src/commands/init.js +0 -359
- package/src/commands/run.js +0 -112
- package/src/core/backup.js +0 -65
- package/src/core/completion-contract.js +0 -35
- package/src/core/delegation-controller.js +0 -193
- package/src/core/evidence/evidence-ledger.js +0 -98
- package/src/core/execution-planner.js +0 -62
- package/src/core/filesystem.js +0 -141
- package/src/core/finalization/finalizer.js +0 -165
- package/src/core/finalization/workspace-snapshot.js +0 -110
- package/src/core/gates/branch-gate.js +0 -134
- package/src/core/gates/merge-gate.js +0 -74
- package/src/core/handoff/handoff-engine.js +0 -104
- package/src/core/healing/cli-remediation-executor.js +0 -153
- package/src/core/healing/healer-engine.js +0 -212
- package/src/core/healing/runtime-remediation-executor.js +0 -136
- package/src/core/identity.js +0 -43
- package/src/core/install-plan.js +0 -34
- package/src/core/opencode-merge.js +0 -150
- package/src/core/package-assets.js +0 -141
- package/src/core/request-classifier.js +0 -73
- package/src/core/request-router.js +0 -289
- package/src/core/runtime/opencode-adapter.js +0 -202
- package/src/core/sdd/validator.js +0 -67
- package/src/core/statuses.js +0 -29
- package/src/core/symlink-layout.js +0 -93
- package/src/core/templates.js +0 -229
- package/src/core/validation/artifact-fidelity-gate.js +0 -445
- package/src/core/validation/canonical-finalization.js +0 -43
- package/src/core/validation/delivery-decision-engine.js +0 -344
- package/src/core/validation/evidence-collector.js +0 -245
- package/src/core/validation/quality-guard.js +0 -251
- package/src/core/validation/stack-detector.js +0 -65
- package/src/core/validation/validation-planner.js +0 -134
- package/src/core/validation/visual-verifier.js +0 -175
- package/src/core/workflow-profiles.js +0 -107
- package/src/core/workflow-state-machine.js +0 -46
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Package asset resolution for @williambeto/ai-workflow.
|
|
3
|
-
*
|
|
4
|
-
* Resolves the installed package root and reads real skill/agent/command
|
|
5
|
-
* files so `init` copies them to the target project instead of generating stubs.
|
|
6
|
-
*/
|
|
7
|
-
import { fileURLToPath } from "node:url";
|
|
8
|
-
import { dirname, resolve } from "node:path";
|
|
9
|
-
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
10
|
-
|
|
11
|
-
let cachedRoot = null;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Return the absolute path to the installed package root
|
|
15
|
-
* (the directory containing `.agents/skills/`).
|
|
16
|
-
*
|
|
17
|
-
* It dynamically searches for the package.json belonging to @williambeto/ai-workflow
|
|
18
|
-
* by climbing up from the current module location.
|
|
19
|
-
*/
|
|
20
|
-
export function getPackageRoot() {
|
|
21
|
-
if (cachedRoot) return cachedRoot;
|
|
22
|
-
let currentDir = dirname(fileURLToPath(import.meta.url));
|
|
23
|
-
|
|
24
|
-
while (true) {
|
|
25
|
-
const pkgPath = resolve(currentDir, "package.json");
|
|
26
|
-
try {
|
|
27
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
28
|
-
if (pkg.name === "@williambeto/ai-workflow") {
|
|
29
|
-
cachedRoot = currentDir;
|
|
30
|
-
return cachedRoot;
|
|
31
|
-
}
|
|
32
|
-
} catch {
|
|
33
|
-
// package.json doesn't exist or is invalid, keep climbing
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const parentDir = dirname(currentDir);
|
|
37
|
-
if (parentDir === currentDir) {
|
|
38
|
-
break; // Reached filesystem root
|
|
39
|
-
}
|
|
40
|
-
currentDir = parentDir;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
throw new Error("Failed to resolve @williambeto/ai-workflow package root physically.");
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Read a file relative to the package root.
|
|
48
|
-
* Returns null if the file does not exist.
|
|
49
|
-
*/
|
|
50
|
-
export function readPackageFile(relativePath) {
|
|
51
|
-
try {
|
|
52
|
-
const fullPath = resolve(getPackageRoot(), relativePath);
|
|
53
|
-
return readFileSync(fullPath, "utf8");
|
|
54
|
-
} catch {
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Resolve the current kit version from package metadata.
|
|
61
|
-
* Returns null if package.json cannot be read/parsed.
|
|
62
|
-
*/
|
|
63
|
-
export function getPackageVersion() {
|
|
64
|
-
try {
|
|
65
|
-
const raw = readPackageFile("package.json");
|
|
66
|
-
if (!raw) return null;
|
|
67
|
-
const parsed = JSON.parse(raw);
|
|
68
|
-
return typeof parsed.version === "string" ? parsed.version : null;
|
|
69
|
-
} catch {
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Discover ALL files under a package-relative directory.
|
|
76
|
-
* Returns a flat map of { relativePath: content }.
|
|
77
|
-
* Skips non-existent directories silently.
|
|
78
|
-
*/
|
|
79
|
-
export function discoverPackageFiles(baseRelativePath) {
|
|
80
|
-
const files = {};
|
|
81
|
-
const basePath = resolve(getPackageRoot(), baseRelativePath);
|
|
82
|
-
|
|
83
|
-
try {
|
|
84
|
-
if (!statSync(basePath).isDirectory()) {
|
|
85
|
-
return files;
|
|
86
|
-
}
|
|
87
|
-
} catch {
|
|
88
|
-
return files;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function walk(dir, relativePrefix) {
|
|
92
|
-
const entries = readdirSync(dir, { withFileTypes: true });
|
|
93
|
-
for (const entry of entries) {
|
|
94
|
-
const fullPath = resolve(dir, entry.name);
|
|
95
|
-
const relativePath = relativePrefix
|
|
96
|
-
? `${relativePrefix}/${entry.name}`
|
|
97
|
-
: entry.name;
|
|
98
|
-
|
|
99
|
-
if (entry.isDirectory()) {
|
|
100
|
-
walk(fullPath, relativePath);
|
|
101
|
-
} else if (entry.isFile()) {
|
|
102
|
-
try {
|
|
103
|
-
files[relativePath] = readFileSync(fullPath, "utf8");
|
|
104
|
-
} catch {
|
|
105
|
-
// skip unreadable files
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
walk(basePath, baseRelativePath);
|
|
112
|
-
return files;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Read the SKILL.md for a given skill name, plus any reference files
|
|
117
|
-
* found under a directory.
|
|
118
|
-
* Returns a map of { relativePath: content }.
|
|
119
|
-
*/
|
|
120
|
-
export function getFullSkillFiles(skillName) {
|
|
121
|
-
const skillRelPath = `dist-assets/skills/${skillName}`;
|
|
122
|
-
const files = discoverPackageFiles(skillRelPath);
|
|
123
|
-
return files;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Read the agent .md file for a given agent name.
|
|
128
|
-
* Returns the content string or null.
|
|
129
|
-
*/
|
|
130
|
-
export function getFullAgentContent(agentName) {
|
|
131
|
-
return readPackageFile(`dist-assets/agents/${agentName}.md`);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Read the command .md file for a given command name.
|
|
136
|
-
* Returns the content string or null.
|
|
137
|
-
*/
|
|
138
|
-
export function getFullCommandContent(commandName) {
|
|
139
|
-
return readPackageFile(`dist-assets/commands/${commandName}.md`);
|
|
140
|
-
}
|
|
141
|
-
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { resolveWorkflowProfile, getWorkflowProfile } from "./workflow-profiles.js";
|
|
2
|
-
import { RequestRouter } from "./request-router.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* RequestClassifier - Classifies natural requests into structured, testable contracts.
|
|
6
|
-
*/
|
|
7
|
-
export class RequestClassifier {
|
|
8
|
-
/**
|
|
9
|
-
* Classifies a natural request.
|
|
10
|
-
* @param {string} request - The natural language request string.
|
|
11
|
-
* @returns {Object} Structured classification contract.
|
|
12
|
-
*/
|
|
13
|
-
classify(request = "", options = {}) {
|
|
14
|
-
const text = String(request).trim();
|
|
15
|
-
if (!text) {
|
|
16
|
-
throw new Error("Cannot classify an empty request.");
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const cwd = options.cwd || process.cwd();
|
|
20
|
-
|
|
21
|
-
// 1. Resolve Profile
|
|
22
|
-
const profile = resolveWorkflowProfile({ request: text });
|
|
23
|
-
const profileDef = getWorkflowProfile(profile);
|
|
24
|
-
|
|
25
|
-
// 2. Classify Intent
|
|
26
|
-
const readOnlyPatterns = /\b(analyze|check|inspect|review|find|search|show|list|read|view|verify)\b/i;
|
|
27
|
-
const isReadOnly = readOnlyPatterns.test(text);
|
|
28
|
-
const intent = isReadOnly ? "read-only" : "write";
|
|
29
|
-
|
|
30
|
-
// 3. Classify Mode
|
|
31
|
-
let mode = "standard";
|
|
32
|
-
if (/\b(deep|architectural|migration|major|full|\[deep\])\b/i.test(text)) {
|
|
33
|
-
mode = "full";
|
|
34
|
-
} else if (/\b(tiny|small|quick|simple|only\s+update|typo|comment|\[tiny\])\b/i.test(text)) {
|
|
35
|
-
const isPureDocsOrTypo = profile === "documentation" || /\b(typo|comment|readme|docs|documentation)\b/i.test(text);
|
|
36
|
-
if (intent === "write" && !isPureDocsOrTypo) {
|
|
37
|
-
mode = "standard";
|
|
38
|
-
} else {
|
|
39
|
-
mode = "quick";
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// 4. Classify Risk
|
|
44
|
-
let risk = "medium";
|
|
45
|
-
if (mode === "full" || profile === "security-review") {
|
|
46
|
-
risk = "high";
|
|
47
|
-
} else if (mode === "quick" || intent === "read-only") {
|
|
48
|
-
risk = "low";
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const specNeeded = mode === "full" && intent === "write";
|
|
52
|
-
const validationNeeded = intent === "write";
|
|
53
|
-
|
|
54
|
-
// Integrate central RequestRouter
|
|
55
|
-
const router = new RequestRouter({ cwd });
|
|
56
|
-
const requestUnderstanding = router.understand(text);
|
|
57
|
-
const routingDecision = router.route(requestUnderstanding);
|
|
58
|
-
|
|
59
|
-
return {
|
|
60
|
-
request: text,
|
|
61
|
-
profile,
|
|
62
|
-
owner: routingDecision.selectedActor || profileDef.owner,
|
|
63
|
-
intent: requestUnderstanding.mutationIntent === "readonly" ? "read-only" : "write",
|
|
64
|
-
mode,
|
|
65
|
-
risk: requestUnderstanding.riskLevel,
|
|
66
|
-
specNeeded,
|
|
67
|
-
validationNeeded,
|
|
68
|
-
skills: [...profileDef.skills],
|
|
69
|
-
requestUnderstanding,
|
|
70
|
-
routingDecision
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
}
|
|
@@ -1,289 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import fs from "node:fs";
|
|
3
|
-
|
|
4
|
-
export class RequestRouter {
|
|
5
|
-
constructor({ cwd = process.cwd() } = {}) {
|
|
6
|
-
this.cwd = cwd;
|
|
7
|
-
this.validActors = ["Atlas", "Nexus", "Orion", "Astra", "Sage", "Phoenix"];
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Understands and classifies a natural language request.
|
|
12
|
-
* @param {string} request
|
|
13
|
-
* @returns {Object} RequestUnderstanding
|
|
14
|
-
*/
|
|
15
|
-
understand(request) {
|
|
16
|
-
const text = String(request).trim();
|
|
17
|
-
if (!text) {
|
|
18
|
-
throw new Error("Cannot classify an empty request.");
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// 1. Extract requested actor
|
|
22
|
-
let requestedActor = undefined;
|
|
23
|
-
let invalidActor = undefined;
|
|
24
|
-
|
|
25
|
-
// Direct addressing/reference pattern e.g., "Nexus, discover...", "ask Sage to..."
|
|
26
|
-
const askMatch = text.match(/\b(?:ask|use|tell|run|agent|actor)\s+([a-zA-Z]+)\b/i);
|
|
27
|
-
const addressMatch = text.match(/\b([a-zA-Z]+)\s*(?:,)/);
|
|
28
|
-
|
|
29
|
-
let candidate = null;
|
|
30
|
-
if (askMatch) {
|
|
31
|
-
candidate = askMatch[1];
|
|
32
|
-
} else if (addressMatch) {
|
|
33
|
-
candidate = addressMatch[1];
|
|
34
|
-
} else {
|
|
35
|
-
// General match for known actors
|
|
36
|
-
const knownMatch = text.match(/\b(Atlas|Nexus|Orion|Astra|Sage|Phoenix)\b/i);
|
|
37
|
-
if (knownMatch) {
|
|
38
|
-
candidate = knownMatch[1];
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (candidate) {
|
|
43
|
-
const normalized = candidate.charAt(0).toUpperCase() + candidate.slice(1).toLowerCase();
|
|
44
|
-
if (this.validActors.includes(normalized)) {
|
|
45
|
-
requestedActor = normalized;
|
|
46
|
-
} else {
|
|
47
|
-
invalidActor = candidate;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// 2. Extract operationType and requestedCapability
|
|
52
|
-
let operationType = "explain"; // default
|
|
53
|
-
let requestedCapability = undefined;
|
|
54
|
-
|
|
55
|
-
const capabilityMappings = [
|
|
56
|
-
{ op: "discover", pattern: /\b(discover|inspect|map|scan|structure|architecture|repo|search|find)\b/i },
|
|
57
|
-
{ op: "validate", pattern: /\b(validate|audit|review|check|verify|test)\b/i },
|
|
58
|
-
{ op: "implement", pattern: /\b(implement|build|create|add|develop|write|change|modify)\b/i },
|
|
59
|
-
{ op: "fix", pattern: /\b(fix|repair|correct|heal|solve|remediate)\b/i },
|
|
60
|
-
{ op: "release", pattern: /\b(release|tag|prepare release)\b/i },
|
|
61
|
-
{ op: "deploy", pattern: /\b(deploy|publish|push)\b/i }
|
|
62
|
-
];
|
|
63
|
-
|
|
64
|
-
for (const mapping of capabilityMappings) {
|
|
65
|
-
const match = text.match(mapping.pattern);
|
|
66
|
-
if (match) {
|
|
67
|
-
operationType = mapping.op;
|
|
68
|
-
requestedCapability = match[1];
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// 3. Determine mutationIntent
|
|
74
|
-
const writePatterns = /\b(implement|build|fix|write|change|modify|create|add|refactor|update)\b/i;
|
|
75
|
-
const readOnlyPatterns = /\b(analyze|check|inspect|review|find|search|show|list|read|view|verify|validate|discover|map)\b/i;
|
|
76
|
-
|
|
77
|
-
let mutationIntent = "write"; // default
|
|
78
|
-
if (/\b(read-only|readonly)\b/i.test(text)) {
|
|
79
|
-
mutationIntent = "readonly";
|
|
80
|
-
} else if (writePatterns.test(text)) {
|
|
81
|
-
mutationIntent = "write";
|
|
82
|
-
} else if (readOnlyPatterns.test(text) || ["discover", "validate"].includes(operationType)) {
|
|
83
|
-
mutationIntent = "readonly";
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (/\b(publish|release|deploy|push to npm|push to remote)\b/i.test(text)) {
|
|
87
|
-
mutationIntent = "publish";
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Determine Mode (needed for riskLevel mapping)
|
|
91
|
-
let mode = "standard";
|
|
92
|
-
if (/\b(deep|architectural|migration|major|full|\[deep\])\b/i.test(text)) {
|
|
93
|
-
mode = "full";
|
|
94
|
-
} else if (/\b(tiny|small|quick|simple|only\s+update|typo|comment|\[tiny\])\b/i.test(text)) {
|
|
95
|
-
mode = "quick";
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// 4. Determine riskLevel
|
|
99
|
-
let riskLevel = "medium";
|
|
100
|
-
if (mode === "full" || mutationIntent === "publish" || /\b(bypass|force|direct)\b/i.test(text)) {
|
|
101
|
-
riskLevel = "high";
|
|
102
|
-
} else if (mode === "quick" || mutationIntent === "readonly") {
|
|
103
|
-
riskLevel = "low";
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// 5. safetyConstraints and requiredEvidence
|
|
107
|
-
const safetyConstraints = [];
|
|
108
|
-
const requiredEvidence = [];
|
|
109
|
-
|
|
110
|
-
if (mutationIntent === "publish") {
|
|
111
|
-
safetyConstraints.push("Require release gate", "Awaiting user authorization");
|
|
112
|
-
requiredEvidence.push("release-decision", "tarball-hash");
|
|
113
|
-
} else if (mutationIntent === "write") {
|
|
114
|
-
safetyConstraints.push("Require branch gate", "No direct commits to main");
|
|
115
|
-
requiredEvidence.push("tests", "commit-hash");
|
|
116
|
-
} else {
|
|
117
|
-
safetyConstraints.push("No workspace mutations allowed");
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (/\b(bypass|force|direct)\b/i.test(text)) {
|
|
121
|
-
safetyConstraints.push("Block bypass attempts");
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return {
|
|
125
|
-
rawRequest: text,
|
|
126
|
-
taskGoal: `Perform ${operationType} operation under ${mutationIntent} intent`,
|
|
127
|
-
requestedActor,
|
|
128
|
-
invalidActor,
|
|
129
|
-
requestedCapability,
|
|
130
|
-
operationType,
|
|
131
|
-
mutationIntent,
|
|
132
|
-
riskLevel,
|
|
133
|
-
requiredEvidence,
|
|
134
|
-
safetyConstraints
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Decides the routing decision.
|
|
140
|
-
* @param {Object} understanding - RequestUnderstanding
|
|
141
|
-
* @returns {Object} RoutingDecision
|
|
142
|
-
*/
|
|
143
|
-
route(understanding) {
|
|
144
|
-
const { requestedActor, invalidActor, operationType, mutationIntent, rawRequest } = understanding;
|
|
145
|
-
|
|
146
|
-
// 1. Check for bypass safety attempts
|
|
147
|
-
if (/\b(bypass safety|bypass validation|force push|directly to main|push to remote without gate)\b/i.test(rawRequest)) {
|
|
148
|
-
return {
|
|
149
|
-
requestedActor,
|
|
150
|
-
selectedActor: "Atlas",
|
|
151
|
-
operationType,
|
|
152
|
-
mutationIntent,
|
|
153
|
-
permissionDecision: "blocked",
|
|
154
|
-
reason: "Security block: Attempt to bypass safety or push directly is forbidden.",
|
|
155
|
-
workflowPath: ["Atlas"]
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// 2. Check for invalid/ambiguous actor names in critical actions
|
|
160
|
-
if (invalidActor) {
|
|
161
|
-
if (["release", "deploy", "fix", "implement"].includes(operationType) || mutationIntent === "publish") {
|
|
162
|
-
return {
|
|
163
|
-
requestedActor: undefined,
|
|
164
|
-
selectedActor: "Atlas",
|
|
165
|
-
operationType,
|
|
166
|
-
mutationIntent,
|
|
167
|
-
permissionDecision: "blocked",
|
|
168
|
-
reason: `Security block: Request mentions invalid/unrecognized actor '${invalidActor}' for a critical operation.`,
|
|
169
|
-
workflowPath: ["Atlas"]
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// 3. Permission matrix check
|
|
175
|
-
const PERMISSION_MATRIX = {
|
|
176
|
-
Atlas: {
|
|
177
|
-
allowedOperations: ["explain"],
|
|
178
|
-
allowedMutations: ["readonly"]
|
|
179
|
-
},
|
|
180
|
-
Nexus: {
|
|
181
|
-
allowedOperations: ["discover", "explain", "plan"],
|
|
182
|
-
allowedMutations: ["readonly", "write"]
|
|
183
|
-
},
|
|
184
|
-
Orion: {
|
|
185
|
-
allowedOperations: ["plan", "release", "deploy"],
|
|
186
|
-
allowedMutations: ["readonly", "write", "publish"]
|
|
187
|
-
},
|
|
188
|
-
Astra: {
|
|
189
|
-
allowedOperations: ["implement", "fix", "explain"],
|
|
190
|
-
allowedMutations: ["readonly", "write"]
|
|
191
|
-
},
|
|
192
|
-
Sage: {
|
|
193
|
-
allowedOperations: ["validate", "explain"],
|
|
194
|
-
allowedMutations: ["readonly"]
|
|
195
|
-
},
|
|
196
|
-
Phoenix: {
|
|
197
|
-
allowedOperations: ["fix"],
|
|
198
|
-
allowedMutations: ["readonly", "write"]
|
|
199
|
-
}
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
let selectedActor = undefined;
|
|
203
|
-
let permissionDecision = "allowed";
|
|
204
|
-
let reason = "Routed successfully.";
|
|
205
|
-
let workflowPath = ["Atlas"];
|
|
206
|
-
|
|
207
|
-
if (requestedActor) {
|
|
208
|
-
const perms = PERMISSION_MATRIX[requestedActor];
|
|
209
|
-
const isOpAllowed = perms.allowedOperations.includes(operationType);
|
|
210
|
-
const isMutationAllowed = perms.allowedMutations.includes(mutationIntent);
|
|
211
|
-
|
|
212
|
-
if (isOpAllowed && isMutationAllowed) {
|
|
213
|
-
selectedActor = requestedActor;
|
|
214
|
-
} else {
|
|
215
|
-
// Valid actor but unauthorized operation or mutation
|
|
216
|
-
if (mutationIntent === "publish") {
|
|
217
|
-
selectedActor = "Orion";
|
|
218
|
-
permissionDecision = "rerouted";
|
|
219
|
-
reason = `Rerouted: Requested actor '${requestedActor}' is not authorized to publish. Redirecting to Orion.`;
|
|
220
|
-
} else {
|
|
221
|
-
selectedActor = requestedActor;
|
|
222
|
-
permissionDecision = "blocked";
|
|
223
|
-
reason = `Blocked: Requested actor '${requestedActor}' does not have permission for operation '${operationType}' or mutation '${mutationIntent}'.`;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
} else {
|
|
227
|
-
// Infer actor from operation, risk, capability
|
|
228
|
-
if (operationType === "discover") {
|
|
229
|
-
selectedActor = "Nexus";
|
|
230
|
-
} else if (operationType === "validate") {
|
|
231
|
-
selectedActor = "Sage";
|
|
232
|
-
} else if (operationType === "fix") {
|
|
233
|
-
selectedActor = "Phoenix";
|
|
234
|
-
} else if (operationType === "implement") {
|
|
235
|
-
selectedActor = "Astra";
|
|
236
|
-
} else if (operationType === "release" || operationType === "deploy") {
|
|
237
|
-
selectedActor = "Orion";
|
|
238
|
-
} else {
|
|
239
|
-
// Fallback for general explain/plan
|
|
240
|
-
selectedActor = mutationIntent === "write" ? "Astra" : "Atlas";
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Phoenix findings check: A fix request requires findings before Phoenix acts
|
|
245
|
-
if (selectedActor === "Phoenix") {
|
|
246
|
-
const requestPath = path.join(this.cwd, ".ai-workflow/remediation-request.json");
|
|
247
|
-
let hasFindings = false;
|
|
248
|
-
if (fs.existsSync(requestPath)) {
|
|
249
|
-
try {
|
|
250
|
-
const reqData = JSON.parse(fs.readFileSync(requestPath, "utf8"));
|
|
251
|
-
if (reqData.affectedChecks && reqData.affectedChecks.length > 0) {
|
|
252
|
-
hasFindings = true;
|
|
253
|
-
}
|
|
254
|
-
} catch {
|
|
255
|
-
// ignore
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
if (!hasFindings) {
|
|
259
|
-
permissionDecision = "blocked";
|
|
260
|
-
reason = "Blocked: Phoenix acts only with concrete findings and bounded remediation.";
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (selectedActor && !workflowPath.includes(selectedActor)) {
|
|
265
|
-
workflowPath.push(selectedActor);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Atlas self-execution guard
|
|
269
|
-
if (selectedActor === "Atlas" && (requestedActor || ["discover", "validate", "fix", "release", "deploy"].includes(operationType))) {
|
|
270
|
-
permissionDecision = "blocked";
|
|
271
|
-
reason = "Atlas self-execution guard: Atlas cannot silently execute specialized actor or capability requests.";
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Sage and Phoenix revalidation loop path in full mode:
|
|
275
|
-
if (selectedActor === "Astra" && understanding.riskLevel === "high") {
|
|
276
|
-
workflowPath.push("Sage", "Phoenix", "Sage");
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
return {
|
|
280
|
-
requestedActor,
|
|
281
|
-
selectedActor,
|
|
282
|
-
operationType,
|
|
283
|
-
mutationIntent,
|
|
284
|
-
permissionDecision,
|
|
285
|
-
reason,
|
|
286
|
-
workflowPath
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
}
|