@williambeto/ai-workflow 2.9.2 โ 2.9.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/CHANGELOG.md +20 -0
- package/bin/ai-workflow.js +79 -1
- package/bin/ai-workflow.js.map +1 -1
- package/dist-assets/AGENTS.md +1 -1
- package/dist-assets/agents/astra.md +5 -0
- package/dist-assets/agents/atlas.md +4 -3
- package/dist-assets/agents/nexus.md +5 -0
- package/dist-assets/agents/orion.md +5 -0
- package/dist-assets/agents/phoenix.md +5 -0
- package/dist-assets/agents/sage.md +5 -0
- package/dist-assets/docs/policies/SKILLS_COMMON_GOVERNANCE.md +9 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
## [unreleased]
|
|
2
2
|
|
|
3
|
+
## [2.9.3] - 2026-07-16
|
|
4
|
+
|
|
5
|
+
### ๐ Bug Fixes
|
|
6
|
+
|
|
7
|
+
- Restore scoped specialist delegation for every core AIWK agent without
|
|
8
|
+
reintroducing `bash` or `edit` approval prompts
|
|
9
|
+
- Allow Atlas to reach both workflow agents and all registered specialists
|
|
10
|
+
while preventing specialists from delegating recursively
|
|
11
|
+
- Set OpenCode's bounded subagent depth to two so workflow agents delegated by
|
|
12
|
+
Atlas can still consult one specialist
|
|
13
|
+
- Detect stale or incomplete specialist routing through `ai-workflow doctor`
|
|
14
|
+
|
|
15
|
+
### ๐งช Testing
|
|
16
|
+
|
|
17
|
+
- Cover the complete six-caller by twenty-specialist routing matrix
|
|
18
|
+
- Verify upgrades from permission-free `2.9.2` configurations and reject
|
|
19
|
+
recursive specialist delegation
|
|
20
|
+
- Exercise Atlas to workflow-agent to specialist routing in a real OpenCode
|
|
21
|
+
runtime smoke test
|
|
22
|
+
|
|
3
23
|
## [2.9.2] - 2026-07-16
|
|
4
24
|
|
|
5
25
|
### ๐ Bug Fixes
|
package/bin/ai-workflow.js
CHANGED
|
@@ -761,6 +761,58 @@ var SUBAGENTS = {
|
|
|
761
761
|
"Technical-Leader": { description: "Capability only: analyze technical decisions, architecture, and trade-offs", skill: "technical-leadership" },
|
|
762
762
|
"UI-UX-Engineer": { description: "Capability only: review usability and interface clarity", skill: "ui-ux-design" }
|
|
763
763
|
};
|
|
764
|
+
var CORE_AGENT_NAMES = Object.freeze(Object.keys(AGENTS));
|
|
765
|
+
var SPECIALIST_AGENT_NAMES = Object.freeze(Object.keys(SUBAGENTS));
|
|
766
|
+
var MIN_SUBAGENT_DEPTH = 2;
|
|
767
|
+
function inspectSpecialistRoutingConfig(opencodeConfig) {
|
|
768
|
+
const errors = [];
|
|
769
|
+
const warnings = [];
|
|
770
|
+
const agents = opencodeConfig?.agent ?? {};
|
|
771
|
+
if (typeof opencodeConfig?.subagent_depth !== "number" || opencodeConfig.subagent_depth < MIN_SUBAGENT_DEPTH) {
|
|
772
|
+
errors.push(`OpenCode subagent_depth must be at least ${MIN_SUBAGENT_DEPTH} for workflow-agent to specialist routing`);
|
|
773
|
+
}
|
|
774
|
+
for (const specialist of SPECIALIST_AGENT_NAMES) {
|
|
775
|
+
if (agents[specialist]?.mode !== "subagent") {
|
|
776
|
+
errors.push(`specialist missing or not configured as subagent: ${specialist}`);
|
|
777
|
+
}
|
|
778
|
+
if (agents[specialist]?.permission?.task !== void 0) {
|
|
779
|
+
errors.push(`specialist must not delegate recursively: ${specialist}`);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
for (const caller of CORE_AGENT_NAMES) {
|
|
783
|
+
const taskPermissions = agents[caller]?.permission?.task;
|
|
784
|
+
if (!taskPermissions || typeof taskPermissions !== "object" || Array.isArray(taskPermissions)) {
|
|
785
|
+
errors.push(`core agent lacks explicit specialist task routing: ${caller}`);
|
|
786
|
+
continue;
|
|
787
|
+
}
|
|
788
|
+
if (taskPermissions["*"] !== "deny") {
|
|
789
|
+
errors.push(`core agent task routing must deny unspecified targets: ${caller}`);
|
|
790
|
+
}
|
|
791
|
+
if (Object.values(taskPermissions).includes("ask")) {
|
|
792
|
+
errors.push(`core agent task routing must not request approval: ${caller}`);
|
|
793
|
+
}
|
|
794
|
+
const blockedSpecialists = SPECIALIST_AGENT_NAMES.filter((specialist) => taskPermissions[specialist] !== "allow");
|
|
795
|
+
if (blockedSpecialists.length > 0) {
|
|
796
|
+
errors.push(`core agent cannot reach all specialists: ${caller} (${blockedSpecialists.join(", ")})`);
|
|
797
|
+
}
|
|
798
|
+
const otherCoreAgents = CORE_AGENT_NAMES.filter((name) => name !== caller);
|
|
799
|
+
if (caller === "Atlas") {
|
|
800
|
+
const blockedCoreAgents = otherCoreAgents.filter((name) => taskPermissions[name] !== "allow");
|
|
801
|
+
if (blockedCoreAgents.length > 0) {
|
|
802
|
+
errors.push(`Atlas cannot reach all workflow agents: ${blockedCoreAgents.join(", ")}`);
|
|
803
|
+
}
|
|
804
|
+
} else {
|
|
805
|
+
const recursiveCoreAgents = otherCoreAgents.filter((name) => taskPermissions[name] === "allow");
|
|
806
|
+
if (recursiveCoreAgents.length > 0) {
|
|
807
|
+
errors.push(`non-Atlas core agent can recursively delegate workflow ownership: ${caller} (${recursiveCoreAgents.join(", ")})`);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
if (opencodeConfig?.permission?.task !== void 0) {
|
|
812
|
+
warnings.push("consumer-level permission.task may override AIWK specialist routing or enable recursive delegation");
|
|
813
|
+
}
|
|
814
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
815
|
+
}
|
|
764
816
|
var COMMANDS = {
|
|
765
817
|
atlas: { description: "Decide safest next step and delegate to the appropriate workflow or agent", agent: "Atlas" },
|
|
766
818
|
run: { description: "Execute the master orchestration pipeline (Gates, SDD, Implementation, Validation, Healing)", agent: "Atlas" },
|
|
@@ -779,11 +831,23 @@ var COMMANDS = {
|
|
|
779
831
|
};
|
|
780
832
|
function buildManagedConfig() {
|
|
781
833
|
const agent = {};
|
|
834
|
+
const specialistTaskPermissions = Object.fromEntries([
|
|
835
|
+
["*", "deny"],
|
|
836
|
+
...SPECIALIST_AGENT_NAMES.map((name) => [name, "allow"])
|
|
837
|
+
]);
|
|
838
|
+
const atlasTaskPermissions = Object.fromEntries([
|
|
839
|
+
["*", "deny"],
|
|
840
|
+
...CORE_AGENT_NAMES.filter((name) => name !== "Atlas").map((name) => [name, "allow"]),
|
|
841
|
+
...SPECIALIST_AGENT_NAMES.map((name) => [name, "allow"])
|
|
842
|
+
]);
|
|
782
843
|
for (const [name, def] of Object.entries(AGENTS)) {
|
|
783
844
|
agent[name] = {
|
|
784
845
|
mode: def.mode,
|
|
785
846
|
description: def.description,
|
|
786
|
-
prompt: `{file:./.ai-workflow/opencode/agents/${name.toLowerCase()}.md}
|
|
847
|
+
prompt: `{file:./.ai-workflow/opencode/agents/${name.toLowerCase()}.md}`,
|
|
848
|
+
permission: {
|
|
849
|
+
task: name === "Atlas" ? atlasTaskPermissions : specialistTaskPermissions
|
|
850
|
+
}
|
|
787
851
|
};
|
|
788
852
|
}
|
|
789
853
|
for (const [name, def] of Object.entries(SUBAGENTS)) {
|
|
@@ -804,6 +868,7 @@ function buildManagedConfig() {
|
|
|
804
868
|
return {
|
|
805
869
|
$schema: "https://opencode.ai/config.json",
|
|
806
870
|
default_agent: "Atlas",
|
|
871
|
+
subagent_depth: MIN_SUBAGENT_DEPTH,
|
|
807
872
|
agent,
|
|
808
873
|
skills: {
|
|
809
874
|
paths: ["./.ai-workflow/opencode/skills"]
|
|
@@ -839,6 +904,7 @@ async function mergeOpencodeConfig(cwd, { force = false, backupRoot = ".ai-workf
|
|
|
839
904
|
const next = {
|
|
840
905
|
...currentWithoutLegacy,
|
|
841
906
|
$schema: current.$schema ?? managed.$schema,
|
|
907
|
+
subagent_depth: typeof current.subagent_depth === "number" ? Math.max(current.subagent_depth, managed.subagent_depth) : managed.subagent_depth,
|
|
842
908
|
...managed.mcp ? {
|
|
843
909
|
mcp: {
|
|
844
910
|
...current.mcp ?? {},
|
|
@@ -1988,6 +2054,18 @@ async function checkOpencodeJson(cwd, state) {
|
|
|
1988
2054
|
console.log(`FAIL opencode agent entries missing: ${missingAgents.join(", ")}`);
|
|
1989
2055
|
}
|
|
1990
2056
|
}
|
|
2057
|
+
const specialistRouting = inspectSpecialistRoutingConfig(opencodeConfig);
|
|
2058
|
+
if (specialistRouting.valid) {
|
|
2059
|
+
console.log(`PASS OpenCode specialist routing available (${CORE_AGENT_NAMES.length} callers, ${SPECIALIST_AGENT_NAMES.length} specialists)`);
|
|
2060
|
+
} else {
|
|
2061
|
+
state.hasWarning = true;
|
|
2062
|
+
for (const error of specialistRouting.errors) console.log(`WARN ${error}`);
|
|
2063
|
+
console.log("WARN specialist routing is stale or incomplete; rerun `ai-workflow init --yes --no-install`");
|
|
2064
|
+
}
|
|
2065
|
+
for (const warning of specialistRouting.warnings) {
|
|
2066
|
+
state.hasWarning = true;
|
|
2067
|
+
console.log(`WARN ${warning}`);
|
|
2068
|
+
}
|
|
1991
2069
|
} catch {
|
|
1992
2070
|
state.hasFailure = true;
|
|
1993
2071
|
console.log("FAIL opencode.jsonc is not valid JSONC");
|