@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 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
@@ -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 coreTaskPermissions = Object.fromEntries([
834
+ const specialistTaskPermissions = Object.fromEntries([
783
835
  ["*", "deny"],
784
- ...Object.keys(AGENTS).filter((name) => name !== "Atlas").map((name) => [name, "allow"])
836
+ ...SPECIALIST_AGENT_NAMES.map((name) => [name, "allow"])
785
837
  ]);
786
- const capabilityTaskPermissions = Object.fromEntries([
838
+ const atlasTaskPermissions = Object.fromEntries([
787
839
  ["*", "deny"],
788
- ...Object.keys(SUBAGENTS).map((name) => [name, "allow"])
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
- ...name === "Atlas" ? { edit: "deny", bash: "ask" } : {},
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");