@williambeto/ai-workflow 2.9.1 โ 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 +34 -0
- package/bin/ai-workflow.js +73 -13
- 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,39 @@
|
|
|
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
|
+
|
|
23
|
+
## [2.9.2] - 2026-07-16
|
|
24
|
+
|
|
25
|
+
### ๐ Bug Fixes
|
|
26
|
+
|
|
27
|
+
- Stop injecting agent-level `deny` and `ask` permissions into consumer
|
|
28
|
+
`opencode.jsonc` files, preserving the consumer's OpenCode permission defaults
|
|
29
|
+
- Keep agent ownership and routing boundaries in AIWK instructions without
|
|
30
|
+
causing repeated per-project approval prompts
|
|
31
|
+
|
|
32
|
+
### ๐งช Testing
|
|
33
|
+
|
|
34
|
+
- Add regression coverage that rejects generated AIWK agent permission
|
|
35
|
+
overrides while preserving the managed agent registry
|
|
36
|
+
|
|
3
37
|
## [2.9.1] - 2026-07-15
|
|
4
38
|
|
|
5
39
|
### ๐ 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,13 +831,14 @@ var COMMANDS = {
|
|
|
779
831
|
};
|
|
780
832
|
function buildManagedConfig() {
|
|
781
833
|
const agent = {};
|
|
782
|
-
const
|
|
834
|
+
const specialistTaskPermissions = Object.fromEntries([
|
|
783
835
|
["*", "deny"],
|
|
784
|
-
...
|
|
836
|
+
...SPECIALIST_AGENT_NAMES.map((name) => [name, "allow"])
|
|
785
837
|
]);
|
|
786
|
-
const
|
|
838
|
+
const atlasTaskPermissions = Object.fromEntries([
|
|
787
839
|
["*", "deny"],
|
|
788
|
-
...
|
|
840
|
+
...CORE_AGENT_NAMES.filter((name) => name !== "Atlas").map((name) => [name, "allow"]),
|
|
841
|
+
...SPECIALIST_AGENT_NAMES.map((name) => [name, "allow"])
|
|
789
842
|
]);
|
|
790
843
|
for (const [name, def] of Object.entries(AGENTS)) {
|
|
791
844
|
agent[name] = {
|
|
@@ -793,9 +846,7 @@ function buildManagedConfig() {
|
|
|
793
846
|
description: def.description,
|
|
794
847
|
prompt: `{file:./.ai-workflow/opencode/agents/${name.toLowerCase()}.md}`,
|
|
795
848
|
permission: {
|
|
796
|
-
|
|
797
|
-
...name === "Astra" ? { edit: "allow" } : {},
|
|
798
|
-
task: name === "Atlas" ? coreTaskPermissions : capabilityTaskPermissions
|
|
849
|
+
task: name === "Atlas" ? atlasTaskPermissions : specialistTaskPermissions
|
|
799
850
|
}
|
|
800
851
|
};
|
|
801
852
|
}
|
|
@@ -803,12 +854,7 @@ function buildManagedConfig() {
|
|
|
803
854
|
agent[name] = {
|
|
804
855
|
mode: "subagent",
|
|
805
856
|
description: def.description,
|
|
806
|
-
prompt: `{file:./.ai-workflow/opencode/skills/${def.skill}/SKILL.md}
|
|
807
|
-
permission: {
|
|
808
|
-
edit: "deny",
|
|
809
|
-
bash: "ask",
|
|
810
|
-
task: { "*": "deny" }
|
|
811
|
-
}
|
|
857
|
+
prompt: `{file:./.ai-workflow/opencode/skills/${def.skill}/SKILL.md}`
|
|
812
858
|
};
|
|
813
859
|
}
|
|
814
860
|
const command = {};
|
|
@@ -822,6 +868,7 @@ function buildManagedConfig() {
|
|
|
822
868
|
return {
|
|
823
869
|
$schema: "https://opencode.ai/config.json",
|
|
824
870
|
default_agent: "Atlas",
|
|
871
|
+
subagent_depth: MIN_SUBAGENT_DEPTH,
|
|
825
872
|
agent,
|
|
826
873
|
skills: {
|
|
827
874
|
paths: ["./.ai-workflow/opencode/skills"]
|
|
@@ -857,6 +904,7 @@ async function mergeOpencodeConfig(cwd, { force = false, backupRoot = ".ai-workf
|
|
|
857
904
|
const next = {
|
|
858
905
|
...currentWithoutLegacy,
|
|
859
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,
|
|
860
908
|
...managed.mcp ? {
|
|
861
909
|
mcp: {
|
|
862
910
|
...current.mcp ?? {},
|
|
@@ -2006,6 +2054,18 @@ async function checkOpencodeJson(cwd, state) {
|
|
|
2006
2054
|
console.log(`FAIL opencode agent entries missing: ${missingAgents.join(", ")}`);
|
|
2007
2055
|
}
|
|
2008
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
|
+
}
|
|
2009
2069
|
} catch {
|
|
2010
2070
|
state.hasFailure = true;
|
|
2011
2071
|
console.log("FAIL opencode.jsonc is not valid JSONC");
|