0xray 3.1.1 → 3.2.0

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.
Files changed (296) hide show
  1. package/.opencode/codex.codex +1 -1
  2. package/.opencode/commands/auto-format.md +3 -3
  3. package/.opencode/commands/auto-summary-capture.md +2 -2
  4. package/.opencode/commands/enforcer-daily-scan.md +3 -3
  5. package/.opencode/commands/framework-compliance-audit.md +4 -4
  6. package/.opencode/commands/interactive-validator.md +2 -2
  7. package/.opencode/commands/job-summary-logger.md +2 -2
  8. package/.opencode/commands/mode-switch.md +1 -1
  9. package/.opencode/commands/model-health-check.md +2 -2
  10. package/.opencode/commands/performance-analysis.md +2 -2
  11. package/.opencode/commands/pre-commit-introspection.md +3 -3
  12. package/.opencode/commands/security-scan.md +3 -3
  13. package/.opencode/commands/sisyphus-validation.md +3 -3
  14. package/.opencode/commands/summary-logger.md +2 -2
  15. package/AGENTS.md +3 -13
  16. package/README.md +3 -3
  17. package/dist/AGENTS.md +3 -13
  18. package/dist/CHANGELOG.md +29 -0
  19. package/dist/README.md +3 -3
  20. package/dist/analytics/consent-manager.js +0 -1
  21. package/dist/analytics/emerging-pattern-detector.js +0 -1
  22. package/dist/analytics/pattern-learning-engine.js +0 -1
  23. package/dist/analytics/pattern-performance-tracker.js +0 -1
  24. package/dist/analytics/predictive-analytics.js +0 -1
  25. package/dist/analytics/prompt-pattern-analyzer.js +0 -1
  26. package/dist/analytics/routing-performance-analyzer.js +0 -1
  27. package/dist/analytics/routing-refiner.js +0 -1
  28. package/dist/analytics/simple-pattern-analyzer.js +0 -1
  29. package/dist/cli/commands/archive-logs.js +0 -1
  30. package/dist/cli/commands/opencode-install.js +13 -0
  31. package/dist/cli/commands/plugin-commands.js +0 -1
  32. package/dist/cli/commands/status.js +11 -13
  33. package/dist/cli/index.js +2 -7
  34. package/dist/core/activity-logger.js +0 -1
  35. package/dist/core/adaptive-kernel.js +0 -1
  36. package/dist/core/boot-orchestrator.js +2 -12
  37. package/dist/core/bridge.mjs +0 -1
  38. package/dist/core/codex-formatter.js +0 -1
  39. package/dist/core/codex-injector.js +0 -1
  40. package/dist/core/config-loader.js +36 -28
  41. package/dist/core/context-loader.js +0 -1
  42. package/dist/core/context-validator.js +0 -1
  43. package/dist/core/features-config.js +37 -1
  44. package/dist/core/kernel-patterns.js +0 -1
  45. package/dist/core/model-router.js +23 -64
  46. package/dist/core/system-prompt-generator.js +0 -1
  47. package/dist/delegation/agent-delegator.js +0 -1
  48. package/dist/delegation/agent-expertise.js +0 -1
  49. package/dist/delegation/analytics/index.js +0 -1
  50. package/dist/delegation/analytics/learning-engine.js +0 -1
  51. package/dist/delegation/analytics/outcome-tracker.js +0 -1
  52. package/dist/delegation/analytics/routing-analytics.js +0 -1
  53. package/dist/delegation/ast-code-parser.js +0 -1
  54. package/dist/delegation/complexity-analyzer.js +0 -1
  55. package/dist/delegation/dependency-graph-builder.js +0 -1
  56. package/dist/delegation/index.js +0 -1
  57. package/dist/delegation/metrics-aggregator.js +0 -1
  58. package/dist/delegation/session-coordinator.js +0 -1
  59. package/dist/delegation/strategy-selector.js +0 -1
  60. package/dist/delegation/voting-coordinator.js +0 -1
  61. package/dist/delegation/voting-types.js +0 -1
  62. package/dist/delegation/weighted-voting-aggregator.js +0 -1
  63. package/dist/enforcement/core/index.js +0 -1
  64. package/dist/enforcement/core/rule-executor.js +0 -1
  65. package/dist/enforcement/core/rule-hierarchy.js +0 -1
  66. package/dist/enforcement/core/rule-registry.js +0 -1
  67. package/dist/enforcement/core/violation-fixer.js +0 -1
  68. package/dist/enforcement/index.js +0 -1
  69. package/dist/enforcement/loaders/agent-triage-loader.js +0 -1
  70. package/dist/enforcement/loaders/agents-md-validation-loader.js +0 -1
  71. package/dist/enforcement/loaders/base-loader.js +0 -1
  72. package/dist/enforcement/loaders/codex-loader.js +0 -1
  73. package/dist/enforcement/loaders/index.js +0 -1
  74. package/dist/enforcement/loaders/loader-orchestrator.js +0 -1
  75. package/dist/enforcement/loaders/processor-loader.js +0 -1
  76. package/dist/enforcement/types.js +0 -1
  77. package/dist/enforcement/validators/architecture-validators.js +0 -1
  78. package/dist/enforcement/validators/base-validator.js +0 -1
  79. package/dist/enforcement/validators/code-quality-validators.js +0 -1
  80. package/dist/enforcement/validators/index.js +0 -1
  81. package/dist/enforcement/validators/security-validators.js +0 -1
  82. package/dist/enforcement/validators/testing-validators.js +0 -1
  83. package/dist/enforcement/validators/validator-registry.js +0 -1
  84. package/dist/governance/codex-policy.service.js +0 -1
  85. package/dist/inference/inference-agent-invoker.js +67 -0
  86. package/dist/inference/inference-applier.js +178 -0
  87. package/dist/inference/inference-cycle.js +51 -827
  88. package/dist/inference/inference-proposal-generator.js +149 -0
  89. package/dist/infrastructure/iac-validator.js +465 -0
  90. package/dist/infrastructure/schemas/cloud-schemas.js +488 -0
  91. package/dist/integrations/base/ExampleIntegration.js +0 -1
  92. package/dist/integrations/base/Integration.js +0 -1
  93. package/dist/integrations/base/index.js +0 -1
  94. package/dist/integrations/base/registry.js +0 -1
  95. package/dist/integrations/base/types.js +0 -1
  96. package/dist/integrations/enforcement-gate.js +3 -3
  97. package/dist/integrations/governance/governance-client.js +0 -1
  98. package/dist/integrations/governance/index.js +11 -19
  99. package/dist/integrations/governance/types.js +0 -1
  100. package/dist/integrations/hermes-agent/__init__.py +4 -0
  101. package/dist/integrations/hermes-agent/bridge.mjs +37 -0
  102. package/dist/integrations/openclaw/api-server.js +0 -1
  103. package/dist/integrations/openclaw/client.js +0 -1
  104. package/dist/integrations/openclaw/config.js +0 -1
  105. package/dist/integrations/openclaw/hooks/xray-hooks.js +0 -1
  106. package/dist/integrations/openclaw/index.js +0 -1
  107. package/dist/integrations/openclaw/types.js +0 -1
  108. package/dist/integrations/plugins/index.js +0 -1
  109. package/dist/integrations/plugins/plugin-integration.js +0 -1
  110. package/dist/integrations/plugins/plugin-registry.js +0 -1
  111. package/dist/mcps/agent-resolver.js +0 -1
  112. package/dist/mcps/architect-tools.server.js +119 -185
  113. package/dist/mcps/auto-format.server.js +56 -74
  114. package/dist/mcps/boot-orchestrator.server.js +67 -85
  115. package/dist/mcps/config/config-loader.js +6 -0
  116. package/dist/mcps/config/plugin-server-registry.js +0 -1
  117. package/dist/mcps/enforcer-tools.server.js +207 -273
  118. package/dist/mcps/estimation.server.js +63 -107
  119. package/dist/mcps/framework-compliance-audit.server.js +52 -81
  120. package/dist/mcps/framework-help.server.js +71 -89
  121. package/dist/mcps/governance.server.js +97 -149
  122. package/dist/mcps/knowledge-skills/api-design.server.js +33 -65
  123. package/dist/mcps/knowledge-skills/architecture-patterns.server.js +33 -65
  124. package/dist/mcps/knowledge-skills/bug-triage-specialist.server.js +123 -163
  125. package/dist/mcps/knowledge-skills/code-analyzer.server.js +210 -237
  126. package/dist/mcps/knowledge-skills/code-review.server.js +108 -142
  127. package/dist/mcps/knowledge-skills/content-creator.server.js +203 -225
  128. package/dist/mcps/knowledge-skills/database-design.server.js +116 -150
  129. package/dist/mcps/knowledge-skills/devops-deployment.server.js +123 -157
  130. package/dist/mcps/knowledge-skills/git-workflow.server.js +34 -67
  131. package/dist/mcps/knowledge-skills/growth-strategist.server.js +301 -323
  132. package/dist/mcps/knowledge-skills/log-monitor.server.js +139 -159
  133. package/dist/mcps/knowledge-skills/mobile-development.server.js +170 -200
  134. package/dist/mcps/knowledge-skills/multimodal-looker.server.js +122 -158
  135. package/dist/mcps/knowledge-skills/performance-optimization.server.js +88 -138
  136. package/dist/mcps/knowledge-skills/project-analysis.server.js +131 -218
  137. package/dist/mcps/knowledge-skills/refactoring-strategies.server.js +115 -150
  138. package/dist/mcps/knowledge-skills/security-audit.server.js +16 -143
  139. package/dist/mcps/knowledge-skills/seo-consultant.server.js +20 -213
  140. package/dist/mcps/knowledge-skills/session-management.server.js +13 -90
  141. package/dist/mcps/knowledge-skills/skill-invocation.server.js +43 -421
  142. package/dist/mcps/knowledge-skills/strategist.server.js +10 -55
  143. package/dist/mcps/knowledge-skills/tech-writer.server.js +16 -191
  144. package/dist/mcps/knowledge-skills/testing-best-practices.server.js +18 -191
  145. package/dist/mcps/knowledge-skills/testing-strategy.server.js +18 -156
  146. package/dist/mcps/knowledge-skills/ui-ux-design.server.js +22 -296
  147. package/dist/mcps/lint.server.js +50 -91
  148. package/dist/mcps/model-health-check.server.js +32 -60
  149. package/dist/mcps/orchestrator/aside-context.js +207 -0
  150. package/dist/mcps/orchestrator/handlers/complexity-handler.js +5 -1
  151. package/dist/mcps/orchestrator/handlers/status-handler.js +8 -1
  152. package/dist/mcps/orchestrator/handlers/task-handler.js +5 -0
  153. package/dist/mcps/orchestrator/index.js +5 -0
  154. package/dist/mcps/orchestrator/server.js +63 -8
  155. package/dist/mcps/performance-analysis.server.js +56 -87
  156. package/dist/mcps/processor-pipeline.server.js +68 -99
  157. package/dist/mcps/researcher.server.js +80 -153
  158. package/dist/mcps/security-scan.server.js +56 -70
  159. package/dist/mcps/shared/knowledge-skill-base.js +11 -4
  160. package/dist/mcps/state-manager.server.js +124 -152
  161. package/dist/monitoring/nudge-watchdog.js +0 -1
  162. package/dist/nucleus/default-plugins.js +13 -19
  163. package/dist/nucleus/index.js +0 -1
  164. package/dist/orchestrator/agent-spawn-governor.js +0 -1
  165. package/dist/orchestrator/intelligent-commit-batcher.js +2 -10
  166. package/dist/orchestrator/multi-agent-orchestration-coordinator.js +0 -1
  167. package/dist/orchestrator/orchestrator.js +8 -18
  168. package/dist/orchestrator/self-direction-activation.js +0 -1
  169. package/dist/orchestrator/universal-registry-bridge.js +0 -1
  170. package/dist/performance/performance-budget-enforcer.js +0 -1
  171. package/dist/performance/performance-regression-tester.js +0 -1
  172. package/dist/plugin/plugin-codex-context.js +87 -0
  173. package/dist/plugin/plugin-helpers.js +155 -0
  174. package/dist/plugin/plugin-logger.js +46 -0
  175. package/dist/plugin/plugin-modules.js +144 -0
  176. package/dist/plugin/plugin-quality-gate.js +88 -0
  177. package/dist/plugin/plugin-types.js +1 -0
  178. package/dist/plugin/xray-codex-injection.js +338 -0
  179. package/dist/postprocessor/PostProcessor.js +3 -9
  180. package/dist/postprocessor/analysis/CodeChangeAnalyzer.js +50 -0
  181. package/dist/postprocessor/config/ProcessorConfigLoader.js +16 -0
  182. package/dist/postprocessor/integration.js +194 -0
  183. package/dist/postprocessor/metamorphosis/SelfProposalEngine.js +472 -0
  184. package/dist/postprocessor/metamorphosis/index.js +1 -0
  185. package/dist/postprocessor/redeploy/RetryHandler.js +118 -0
  186. package/dist/postprocessor/services/RegressionAnalysisService.js +0 -1
  187. package/dist/postprocessor/triggers/APITrigger.js +0 -1
  188. package/dist/postprocessor/triggers/WebhookTrigger.js +0 -1
  189. package/dist/processors/implementations/agents-md-validation-processor.js +0 -1
  190. package/dist/processors/implementations/async-pattern-processor.js +0 -1
  191. package/dist/processors/implementations/console-log-guard-processor.js +0 -1
  192. package/dist/processors/implementations/coverage-analysis-processor.js +0 -1
  193. package/dist/processors/implementations/inference-improvement-processor.js +2 -9
  194. package/dist/processors/implementations/log-protection-processor.js +0 -1
  195. package/dist/processors/implementations/nudge-processor.js +0 -1
  196. package/dist/processors/implementations/performance-budget-processor.js +0 -1
  197. package/dist/processors/implementations/postprocessor-chain-validator.js +0 -1
  198. package/dist/processors/implementations/publish-preflight-processor.js +4 -12
  199. package/dist/processors/implementations/session-summary-processor.js +8 -16
  200. package/dist/processors/implementations/spawn-governance-processor.js +0 -1
  201. package/dist/processors/implementations/storytelling-trigger-processor.js +3 -28
  202. package/dist/processors/implementations/test-auto-creation-processor.js +0 -1
  203. package/dist/processors/implementations/typescript-compilation-processor.js +0 -1
  204. package/dist/processors/implementations/version-compliance-processor.js +0 -1
  205. package/dist/processors/processor-interfaces.js +0 -1
  206. package/dist/processors/processor-types.js +0 -1
  207. package/dist/scripts/integration.js +0 -1
  208. package/dist/security/comprehensive-security-audit.js +0 -1
  209. package/dist/security/security-agent-coordinator.js +0 -1
  210. package/dist/security/security-auditor.js +0 -1
  211. package/dist/security/security-hardener.js +0 -1
  212. package/dist/security/security-hardening-system.js +0 -1
  213. package/dist/security/security-headers.js +0 -1
  214. package/dist/security/security-orchestration-layer.js +0 -1
  215. package/dist/services/inference-tuner.js +0 -1
  216. package/dist/session/session-cleanup-manager.js +0 -1
  217. package/dist/session/session-monitor.js +0 -1
  218. package/dist/session/session-state-manager.js +0 -1
  219. package/dist/skills/xray-orchestrator/index.js +0 -1
  220. package/dist/utils/codex-parser.js +0 -1
  221. package/dist/utils/language-detector.js +0 -1
  222. package/dist/utils/shutdown-handler.js +0 -1
  223. package/dist/validation/agent-config-validator.js +280 -0
  224. package/dist/validation/orchestration-flow-validator.js +606 -0
  225. package/package.json +1 -1
  226. package/scripts/node/release.mjs +2 -2
  227. package/scripts/node/setup.cjs +28 -0
  228. package/scripts/node/universal-version-manager.js +1 -1
  229. package/src/mcps/agent-resolver.ts +0 -1
  230. package/src/mcps/architect-tools.server.ts +129 -211
  231. package/src/mcps/auto-format.server.ts +74 -103
  232. package/src/mcps/boot-orchestrator.server.ts +71 -96
  233. package/src/mcps/config/__tests__/config-loader.test.ts +6 -6
  234. package/src/mcps/config/config-loader.ts +7 -0
  235. package/src/mcps/config/plugin-server-registry.ts +0 -1
  236. package/src/mcps/enforcer-tools.server.ts +217 -294
  237. package/src/mcps/estimation.server.ts +62 -122
  238. package/src/mcps/framework-compliance-audit.server.ts +53 -95
  239. package/src/mcps/framework-help.server.ts +74 -96
  240. package/src/mcps/governance.server.ts +97 -162
  241. package/src/mcps/knowledge-skills/api-design.server.ts +33 -72
  242. package/src/mcps/knowledge-skills/architecture-patterns.server.ts +33 -72
  243. package/src/mcps/knowledge-skills/bug-triage-specialist.server.ts +148 -193
  244. package/src/mcps/knowledge-skills/code-analyzer.server.ts +222 -254
  245. package/src/mcps/knowledge-skills/code-review.server.ts +115 -157
  246. package/src/mcps/knowledge-skills/content-creator.server.ts +219 -246
  247. package/src/mcps/knowledge-skills/database-design.server.ts +119 -160
  248. package/src/mcps/knowledge-skills/devops-deployment.server.ts +126 -167
  249. package/src/mcps/knowledge-skills/git-workflow.server.ts +32 -77
  250. package/src/mcps/knowledge-skills/growth-strategist.server.ts +322 -360
  251. package/src/mcps/knowledge-skills/log-monitor.server.ts +68 -95
  252. package/src/mcps/knowledge-skills/mobile-development.server.ts +16 -56
  253. package/src/mcps/knowledge-skills/multimodal-looker.server.ts +13 -59
  254. package/src/mcps/knowledge-skills/performance-optimization.server.ts +13 -74
  255. package/src/mcps/knowledge-skills/project-analysis.server.ts +13 -120
  256. package/src/mcps/knowledge-skills/refactoring-strategies.server.ts +13 -60
  257. package/src/mcps/knowledge-skills/security-audit.server.ts +19 -157
  258. package/src/mcps/knowledge-skills/seo-consultant.server.ts +23 -227
  259. package/src/mcps/knowledge-skills/session-management.server.ts +17 -106
  260. package/src/mcps/knowledge-skills/skill-invocation.server.ts +45 -446
  261. package/src/mcps/knowledge-skills/strategist.server.ts +13 -63
  262. package/src/mcps/knowledge-skills/tech-writer.server.ts +19 -204
  263. package/src/mcps/knowledge-skills/testing-best-practices.server.ts +21 -204
  264. package/src/mcps/knowledge-skills/testing-strategy.server.ts +21 -165
  265. package/src/mcps/knowledge-skills/ui-ux-design.server.ts +25 -342
  266. package/src/mcps/lint.server.ts +52 -105
  267. package/src/mcps/model-health-check.server.ts +34 -70
  268. package/src/mcps/orchestrator/aside-context.ts +245 -0
  269. package/src/mcps/orchestrator/handlers/complexity-handler.ts +9 -3
  270. package/src/mcps/orchestrator/handlers/status-handler.ts +11 -1
  271. package/src/mcps/orchestrator/handlers/task-handler.ts +8 -1
  272. package/src/mcps/orchestrator/index.ts +29 -0
  273. package/src/mcps/orchestrator/server.ts +79 -24
  274. package/src/mcps/orchestrator/types.ts +37 -0
  275. package/src/mcps/performance-analysis.server.ts +59 -102
  276. package/src/mcps/processor-pipeline.server.ts +70 -108
  277. package/src/mcps/researcher.server.ts +87 -175
  278. package/src/mcps/security-scan.server.ts +57 -76
  279. package/src/mcps/shared/knowledge-skill-base.ts +12 -4
  280. package/src/mcps/state-manager.server.ts +129 -169
  281. package/src/opencode/codex.codex +1 -1
  282. package/src/opencode/commands/auto-format.md +3 -3
  283. package/src/opencode/commands/auto-summary-capture.md +2 -2
  284. package/src/opencode/commands/enforcer-daily-scan.md +3 -3
  285. package/src/opencode/commands/framework-compliance-audit.md +4 -4
  286. package/src/opencode/commands/interactive-validator.md +2 -2
  287. package/src/opencode/commands/job-summary-logger.md +2 -2
  288. package/src/opencode/commands/mode-switch.md +1 -1
  289. package/src/opencode/commands/model-health-check.md +2 -2
  290. package/src/opencode/commands/performance-analysis.md +2 -2
  291. package/src/opencode/commands/pre-commit-introspection.md +3 -3
  292. package/src/opencode/commands/security-scan.md +3 -3
  293. package/src/opencode/commands/sisyphus-validation.md +3 -3
  294. package/src/opencode/commands/summary-logger.md +2 -2
  295. package/src/skills/xray-orchestrator/index.ts +0 -1
  296. package/dist/nucleus/orchestrator.js +0 -479
@@ -2,31 +2,18 @@ import * as fs from "fs";
2
2
  import * as path from "path";
3
3
  import { shouldTriggerCycle, accumulateCorpus } from "./inference-accumulator.js";
4
4
  import { DeployVerifier } from "./deploy-verifier.js";
5
- import { VotingCoordinator } from "../delegation/voting-coordinator.js";
6
- import { XrayStateManager } from "../state/state-manager.js";
7
5
  import { frameworkLogger } from "../core/framework-logger.js";
8
- import { getGovernanceIntegration } from "../integrations/governance/index.js";
9
6
  import { featuresConfigLoader } from "../core/features-config.js";
10
7
  import { mcpClientManager } from "../mcps/mcp-client.js";
11
8
  import { getConfigDir } from "../core/config-paths.js";
12
- import { invokeViaOpencode as invokeOpencodeFromEngine } from "../execution/opencode-cli-invoker.js";
13
- import { ProposalApplier } from "../execution/proposal-applier.js";
9
+ import { generateProposals } from "./inference-proposal-generator.js";
10
+ import { applyProposals as applyProposalsEx } from "./inference-applier.js";
14
11
  const CYCLE_STATE_FILE = "inference-cycle-state.json";
15
12
  const CYCLE_HISTORY_FILE = "inference-cycle-history.json";
16
- const GOVERNANCE_AGENTS = {
17
- // Real individual knowledge-skill MCP servers (have analyze_proposal handlers)
18
- fix: ["code-review", "security-audit", "researcher"],
19
- refactor: ["code-review", "security-audit", "researcher"],
20
- guard: ["code-review", "security-audit", "researcher"],
21
- automate: ["code-review", "security-audit", "researcher"],
22
- codify: ["code-review", "security-audit", "researcher"],
23
- };
24
13
  export class InferenceCycle {
25
- // Singleton registry + state management to prevent recursive agent spawning
26
14
  static instances = new Map();
27
15
  static reEntryLock = false;
28
16
  static governedProposalIds = new Set();
29
- static votingCoordinator = null;
30
17
  static getInstance(projectRoot, options) {
31
18
  const root = path.resolve(projectRoot || process.cwd());
32
19
  if (!InferenceCycle.instances.has(root)) {
@@ -38,7 +25,6 @@ export class InferenceCycle {
38
25
  InferenceCycle.instances.clear();
39
26
  InferenceCycle.reEntryLock = false;
40
27
  InferenceCycle.governedProposalIds.clear();
41
- InferenceCycle.votingCoordinator = null;
42
28
  }
43
29
  inferenceDir;
44
30
  stateDir;
@@ -55,7 +41,6 @@ export class InferenceCycle {
55
41
  this.options = options ?? {};
56
42
  }
57
43
  async governExternalProposals(proposals) {
58
- // GATE: prevent re-entry — same as maybeRunCycle
59
44
  if (InferenceCycle.reEntryLock) {
60
45
  const blockedId = `blocked-${Date.now()}`;
61
46
  frameworkLogger.log("inference-cycle", "external-re-entry-blocked", "warning", {});
@@ -93,12 +78,10 @@ export class InferenceCycle {
93
78
  p.status = "approved";
94
79
  for (const p of proposals.filter((p) => p.status !== "approved"))
95
80
  p.status = "rejected";
96
- // Track governed proposals for dedup across cycles
97
81
  for (const p of proposals)
98
82
  InferenceCycle.governedProposalIds.add(p.id);
99
83
  this.setPhase("complete");
100
84
  this.saveCycleState(cycleId);
101
- this.saveGovernanceState(this.getCoordinator());
102
85
  const result = this.buildResult(cycleId, true, "external proposals", startTime, undefined, proposals, votes);
103
86
  this.appendHistory(result);
104
87
  frameworkLogger.log("inference-cycle", "external-governance-complete", "info", {
@@ -113,7 +96,6 @@ export class InferenceCycle {
113
96
  }
114
97
  }
115
98
  async maybeRunCycle() {
116
- // GATE: prevent recursive re-entry while a cycle is in progress
117
99
  if (InferenceCycle.reEntryLock) {
118
100
  const blockedId = `blocked-${Date.now()}`;
119
101
  frameworkLogger.log("inference-cycle", "re-entry-blocked", "warning", {});
@@ -136,14 +118,30 @@ export class InferenceCycle {
136
118
  });
137
119
  const corpus = accumulateCorpus(this.inferenceDir);
138
120
  this.setPhase("proposing");
139
- const proposals = this.generateProposals(corpus);
140
- this.adjustFromHistory(proposals);
121
+ const proposals = generateProposals(corpus, this.loadHistory());
141
122
  if (proposals.length === 0) {
142
123
  this.setPhase("complete");
143
124
  return this.buildResult(cycleId, true, threshold.reason, startTime, corpus, proposals);
144
125
  }
145
126
  this.setPhase("governing");
146
- const votes = await this.governProposals(proposals);
127
+ let votes = [];
128
+ try {
129
+ votes = await this.governProposals(proposals);
130
+ }
131
+ catch (e) {
132
+ frameworkLogger.log("inference-cycle", "govern-proposals-error", "error", {
133
+ error: e.message,
134
+ stack: e.stack?.substring(0, 500),
135
+ });
136
+ for (const proposal of proposals) {
137
+ votes.push({
138
+ proposalId: proposal.id,
139
+ decision: "reject",
140
+ confidence: 0,
141
+ details: [`rejected: governance error: ${e.message}`],
142
+ });
143
+ }
144
+ }
147
145
  // Track governed proposals for dedup across cycles
148
146
  for (const p of proposals)
149
147
  InferenceCycle.governedProposalIds.add(p.id);
@@ -157,7 +155,7 @@ export class InferenceCycle {
157
155
  if (approved.length > 0) {
158
156
  if (!this.options.skipApply) {
159
157
  this.setPhase("applying");
160
- await this.applyProposals(approved);
158
+ await applyProposalsEx(approved, this.projectRoot, this.agentInvoker, this.options);
161
159
  }
162
160
  let deployResult;
163
161
  if (!this.options.skipDeployVerify) {
@@ -188,7 +186,6 @@ export class InferenceCycle {
188
186
  }
189
187
  this.setPhase("complete");
190
188
  this.saveCycleState(cycleId);
191
- this.saveGovernanceState(this.getCoordinator());
192
189
  this.appendHistory(this.buildResult(cycleId, true, threshold.reason, startTime, corpus, proposals, votes, deployResult));
193
190
  return this.buildResult(cycleId, true, threshold.reason, startTime, corpus, proposals, votes, deployResult);
194
191
  }
@@ -201,311 +198,38 @@ export class InferenceCycle {
201
198
  InferenceCycle.reEntryLock = false;
202
199
  }
203
200
  }
204
- generateProposals(corpus) {
205
- const proposals = [];
206
- for (const problem of corpus.recurringProblems) {
207
- proposals.push({
208
- id: `prop-${Date.now()}-${proposals.length}`,
209
- type: this.classifyProposalType(problem.pattern),
210
- title: this.generateTitle(problem),
211
- description: `Recurring across ${problem.occurrences} sessions: ${problem.pattern}`,
212
- evidence: problem.sessions.map((s) => `Seen in session ${s}`),
213
- confidence: Math.min(0.95, 0.5 + problem.occurrences * 0.15),
214
- source: "recurring_problem",
215
- status: "pending",
216
- });
217
- }
218
- const seenPatterns = new Set(corpus.recurringProblems.map((p) => p.pattern));
219
- const allProblems = corpus.sessions.flatMap((s) => s.problems.map((p) => ({ problem: p, session: s.sessionId })));
220
- for (const { problem, session } of allProblems) {
221
- const normalized = problem.replace(/\([a-f0-9]{7}\)/g, "").trim();
222
- if (seenPatterns.has(normalized))
223
- continue;
224
- if (proposals.length >= 5)
225
- break;
226
- proposals.push({
227
- id: `prop-${Date.now()}-${proposals.length}`,
228
- type: this.classifyProposalType(problem),
229
- title: `Investigate: ${problem.substring(0, 80)}`,
230
- description: `Observed in session ${session}: ${problem}`,
231
- evidence: [problem],
232
- confidence: 0.4,
233
- source: "recurring_problem",
234
- status: "pending",
235
- });
236
- seenPatterns.add(normalized);
237
- }
238
- for (const pattern of corpus.recurringPatterns) {
239
- if (pattern.occurrences < 2)
240
- continue;
241
- const type = this.patternToProposalType(pattern);
242
- proposals.push({
243
- id: `prop-${Date.now()}-${proposals.length}`,
244
- type,
245
- title: `Codify ${pattern.name} pattern`,
246
- description: `${pattern.name} detected across ${pattern.occurrences} sessions (avg confidence: ${Math.round(pattern.avgConfidence * 100)}%). ${pattern.description}`,
247
- evidence: pattern.evidence,
248
- confidence: pattern.avgConfidence,
249
- source: "recurring_pattern",
250
- status: "pending",
251
- });
252
- }
253
- const wrongTurns = corpus.allWrongTurns.slice(0, 2);
254
- for (const wt of wrongTurns) {
255
- const summary = wt.length > 50 ? `${wt.substring(0, 47)}...` : wt;
256
- proposals.push({
257
- id: `prop-${Date.now()}-${proposals.length}`,
258
- type: "guard",
259
- title: `Guard against: ${summary}`,
260
- description: `Recurring wrong turn detected: ${wt}. Add a guard or validation to prevent this pattern.`,
261
- evidence: [wt],
262
- confidence: 0.7,
263
- source: "wrong_turn",
264
- status: "pending",
265
- });
266
- }
267
- return proposals.sort((a, b) => b.confidence - a.confidence).slice(0, 3);
268
- }
269
- adjustFromHistory(proposals) {
270
- this.adjustConfidenceFromHistory(proposals);
271
- proposals.sort((a, b) => b.confidence - a.confidence);
272
- }
273
- getCoordinator() {
274
- if (!InferenceCycle.votingCoordinator) {
275
- const stateManager = this.getGovernanceStateManager();
276
- InferenceCycle.votingCoordinator = new VotingCoordinator(stateManager);
277
- }
278
- return InferenceCycle.votingCoordinator;
279
- }
280
- async applyProposals(proposals) {
281
- const applier = new ProposalApplier(this.projectRoot, async (p) => this.applyProposalWork(p), async (p, prUrl) => {
282
- if (this.options.skipResearcherReview)
283
- return "go";
284
- return this.researcherReview(p, prUrl);
285
- });
286
- const results = await applier.applyProposals(proposals);
287
- for (const r of results) {
288
- const p = proposals.find(pr => pr.id === r.proposalId);
289
- if (p)
290
- p.status = r.success ? "applied" : "failed";
291
- }
292
- }
293
- async applyProposalWork(p) {
294
- let filesChanged = false;
295
- if (p.type === "fix" || p.type === "refactor") {
296
- filesChanged = await this.applyCodeChange(p);
297
- }
298
- else if (p.type === "guard") {
299
- filesChanged = await this.applyGuard(p);
300
- }
301
- else if (p.type === "automate") {
302
- filesChanged = await this.applyAutomation(p);
303
- }
304
- return filesChanged;
305
- }
306
- async applyCodeChange(p) {
307
- const targetFiles = this.extractTargetFiles(p.evidence);
308
- const prompt = [
309
- `Apply approved inference proposal`,
310
- ``,
311
- `Type: ${p.type}`,
312
- `Title: ${p.title}`,
313
- `Description: ${p.description}`,
314
- targetFiles.length > 0 ? `Target files: ${targetFiles.join(", ")}` : "No specific target files identified",
315
- `Evidence: ${p.evidence.slice(0, 5).join("; ")}`,
316
- `Confidence: ${(p.confidence * 100).toFixed(0)}%`,
317
- ``,
318
- `1. Read the relevant source files`,
319
- `2. Apply the ${p.type} described above`,
320
- `3. Make minimal, surgical changes`,
321
- `4. If the change is unsafe or unclear, skip and explain`,
322
- ].join("\n");
323
- frameworkLogger.log("inference-cycle", "apply-invoking-agent", "info", {
324
- proposalId: p.id,
325
- proposalType: p.type,
326
- targetFiles,
327
- });
328
- try {
329
- let agentName = p.type === "refactor" ? "refactorer" : "code-reviewer";
330
- // In pure MCP mode, use real skill server names so the orchestrator dispatches to actual MCP tools
331
- if (process.env.XRAY_FORCE_MCP_GOVERNANCE === 'true') {
332
- agentName = p.type === "refactor" ? "refactoring-strategies" : "code-review";
333
- }
334
- await this.invokeAgentInternal(agentName, prompt);
335
- return true;
336
- }
337
- catch (err) {
338
- frameworkLogger.log("inference-cycle", "apply-agent-failed", "warning", {
339
- proposalId: p.id,
340
- error: String(err),
341
- });
342
- return false;
343
- }
344
- }
345
- async applyGuard(p) {
346
- const prompt = [
347
- `Add guard/validation for the following issue:`,
348
- ``,
349
- `Title: ${p.title}`,
350
- `Description: ${p.description}`,
351
- `Evidence: ${p.evidence.join("; ")}`,
352
- `Confidence: ${(p.confidence * 100).toFixed(0)}%`,
353
- ``,
354
- `1. Read the relevant source files`,
355
- `2. Add the missing guard, validation, or edge case handling`,
356
- `3. If this is a codex rule, add the term to .xray/codex.json`,
357
- `4. Make minimal, surgical changes`,
358
- ].join("\n");
359
- try {
360
- const agentName = process.env.XRAY_FORCE_MCP_GOVERNANCE === 'true'
361
- ? "code-review"
362
- : "code-reviewer";
363
- await this.invokeAgentInternal(agentName, prompt);
364
- return true;
365
- }
366
- catch (err) {
367
- frameworkLogger.log("inference-cycle", "apply-guard-failed", "warning", {
368
- proposalId: p.id,
369
- error: String(err),
370
- });
371
- const guardPath = path.join(this.projectRoot, "docs", "guards", `${p.title.replace(/[^a-z0-9]/gi, "-")}.md`);
372
- fs.mkdirSync(path.dirname(guardPath), { recursive: true });
373
- fs.writeFileSync(guardPath, `# Guard: ${p.title}\n\n${p.description}\n\n## Evidence\n${p.evidence.join("\n")}`);
374
- return true;
375
- }
376
- }
377
- async applyAutomation(p) {
378
- const prompt = [
379
- `Design automation for the following manual process:`,
380
- ``,
381
- `Title: ${p.title}`,
382
- `Description: ${p.description}`,
383
- `Evidence: ${p.evidence.join("; ")}`,
384
- `Confidence: ${(p.confidence * 100).toFixed(0)}%`,
385
- ``,
386
- `1. Read the relevant source files`,
387
- `2. Design the automation (script, processor, or config change)`,
388
- `3. Implement it if straightforward, otherwise describe the design`,
389
- ].join("\n");
390
- try {
391
- const agentName = process.env.XRAY_FORCE_MCP_GOVERNANCE === 'true'
392
- ? "architecture-patterns"
393
- : "architect";
394
- await this.invokeAgentInternal(agentName, prompt);
395
- return true;
396
- }
397
- catch (err) {
398
- frameworkLogger.log("inference-cycle", "apply-automation-failed", "warning", {
399
- proposalId: p.id,
400
- error: String(err),
401
- });
402
- const automationPath = path.join(this.projectRoot, "docs", "automation-proposals.md");
403
- const entry = `\n## ${p.title}\n\n${p.description}\n\n**Evidence:** ${p.evidence.join(", ")}\n`;
404
- fs.mkdirSync(path.dirname(automationPath), { recursive: true });
405
- fs.appendFileSync(automationPath, entry);
406
- return true;
407
- }
408
- }
409
- extractTargetFiles(evidence) {
410
- const filePattern = /[a-zA-Z0-9/_-]+\.(ts|js|mjs|json|yml|yaml)/g;
411
- const files = new Set();
412
- for (const item of evidence) {
413
- const matches = item.matchAll(filePattern);
414
- for (const match of matches) {
415
- const f = match[0];
416
- if (f.startsWith("src/") || f.startsWith("dist/") || f.startsWith(".opencode/")) {
417
- files.add(f);
418
- }
419
- }
420
- }
421
- return [...files];
422
- }
423
- async researcherReview(p, prUrl) {
424
- const prompt = `You are a researcher agent reviewing a PR for proposal: "${p.title}".
425
-
426
- PR URL: ${prUrl}
427
-
428
- Proposal Type: ${p.type}
429
- Description: ${p.description}
430
- Evidence: ${p.evidence.slice(0, 5).join("; ")}
431
-
432
- Your job: Review the actual codebase to verify this proposal makes sense. Search the code to confirm:
433
- 1. Does the problem described actually exist in the codebase?
434
- 2. Is the proposed solution appropriate?
435
- 3. Are there any missed edge cases?
436
-
437
- Respond with EXACTLY one of:
438
- - GO (proposal is valid, approve)
439
- - NO-GO (proposal is invalid, reject)
440
- - MODIFY: <specific changes needed>`;
441
- try {
442
- const result = await this.invokeAgentInternal("researcher", prompt);
443
- const output = result.toLowerCase();
444
- if (output.includes("no-go"))
445
- return "no-go";
446
- if (output.includes("modify"))
447
- return "modify";
448
- return "go";
449
- }
450
- catch (err) {
451
- frameworkLogger.log("inference-cycle", "researcher-review-failed", "warning", { error: String(err) });
452
- return "go";
453
- }
454
- }
455
201
  async governProposals(proposals) {
456
- // Primary path: Use the first-class Governance MCP (real skill servers + required Dynamo)
457
- // This is the clean, centralized path (governance.server.ts + GovernanceService)
202
+ // Governance MCP is the sole governance path (governance.server.ts + GovernanceService).
203
+ // It calls individual skill MCPs (code-review, security-audit, researcher) internally
204
+ // and integrates with external Dynamo Solar SSOT.
458
205
  const useGovernanceMcp = process.env.XRAY_FORCE_MCP_GOVERNANCE === 'true' ||
459
206
  this.isGovernanceMcpPreferred();
460
- if (useGovernanceMcp) {
461
- try {
462
- const result = await Promise.race([
463
- mcpClientManager.callServerTool("governance", "govern_proposals", {
464
- proposals: proposals.map(p => ({
465
- id: p.id,
466
- type: p.type,
467
- title: p.title,
468
- description: p.description,
469
- evidence: p.evidence || [],
470
- source: p.source || "inference",
471
- confidence: p.confidence || 0.8,
472
- })),
473
- context: { source: "inference-cycle" },
474
- options: { require_external: true },
475
- }),
476
- new Promise((_, reject) => setTimeout(() => reject(new Error("Governance MCP timed out after 8s")), 8000)),
477
- ]);
478
- const text = result?.content?.[0]?.text || "";
479
- const parsed = this.parseGovernanceMcpResponse(text, proposals);
480
- frameworkLogger.log("inference-cycle", "governance-mcp-primary-path", "info", {
481
- proposalCount: proposals.length,
482
- overall: parsed.overallDecision,
483
- });
484
- return parsed.votes;
485
- }
486
- catch (err) {
487
- frameworkLogger.log("inference-cycle", "governance-mcp-failed", "error", {
488
- error: err instanceof Error ? err.message : String(err),
489
- });
490
- // In forced pure MCP mode we must not silently fall back
491
- if (process.env.XRAY_FORCE_MCP_GOVERNANCE === 'true') {
492
- throw err;
493
- }
494
- // In normal mode, fall back to legacy path with deprecation warning.
495
- // The legacy path will be removed once all consumers have migrated to the Governance MCP + Dynamo Solar SSOT model.
496
- frameworkLogger.log("inference-cycle", "governance-legacy-fallback", "warning", {
497
- message: "Falling back to legacy governance path. This path is deprecated and will be removed in a future version.",
498
- });
499
- }
500
- }
501
- // Legacy internal path (deprecated)
502
- const internalVotes = await this.governProposalsInternal(proposals);
503
- const governanceIntegration = getGovernanceIntegration();
504
- if (governanceIntegration?.isAvailable()) {
505
- const externalVotes = await this.governProposalsExternal(proposals);
506
- return this.mergeGovernanceVotes(internalVotes, externalVotes, proposals);
507
- }
508
- return internalVotes;
207
+ if (!useGovernanceMcp) {
208
+ throw new Error("Governance MCP is required but not available");
209
+ }
210
+ const result = await Promise.race([
211
+ mcpClientManager.callServerTool("governance", "govern_proposals", {
212
+ proposals: proposals.map(p => ({
213
+ id: p.id,
214
+ type: p.type,
215
+ title: p.title,
216
+ description: p.description,
217
+ evidence: p.evidence || [],
218
+ source: p.source || "inference",
219
+ confidence: p.confidence || 0.8,
220
+ })),
221
+ context: { source: "inference-cycle" },
222
+ options: { require_external: true },
223
+ }),
224
+ new Promise((_, reject) => setTimeout(() => reject(new Error("Governance MCP timed out after 8s")), 8000)),
225
+ ]);
226
+ const text = result?.content?.[0]?.text || "";
227
+ const parsed = this.parseGovernanceMcpResponse(text, proposals);
228
+ frameworkLogger.log("inference-cycle", "governance-mcp-primary-path", "info", {
229
+ proposalCount: proposals.length,
230
+ overall: parsed.overallDecision,
231
+ });
232
+ return parsed.votes;
509
233
  }
510
234
  isGovernanceMcpPreferred() {
511
235
  try {
@@ -547,415 +271,6 @@ Respond with EXACTLY one of:
547
271
  return { votes, overallDecision: "needs_revision" };
548
272
  }
549
273
  }
550
- /**
551
- * Oscillator 1: Internal VotingCoordinator-based governance
552
- */
553
- async governProposalsInternal(proposals) {
554
- // Defensive: if pure MCP mode is forced, use individual skill servers only
555
- if (process.env.XRAY_FORCE_MCP_GOVERNANCE === 'true') {
556
- return this.governProposalsWithIndividualSkills(proposals);
557
- }
558
- const coordinator = this.getCoordinator();
559
- const sessionId = `inference-governance-${Date.now()}`;
560
- const results = [];
561
- const todoPrompt = [
562
- `Vote on ${proposals.length} inference proposals. Output EXACTLY one PROPOSAL block per proposal.`,
563
- ``,
564
- `FORMAT (exact, no extra text, no markdown):`,
565
- `PROPOSAL: <number>`,
566
- ` AGENT: architect`,
567
- ` DECISION: approve|reject|abstain`,
568
- ` CONFIDENCE: 0.XX`,
569
- ` REASONING: <brief reason>`,
570
- ``,
571
- `Example:`,
572
- `PROPOSAL: 1`,
573
- ` AGENT: architect`,
574
- ` DECISION: approve`,
575
- ` CONFIDENCE: 0.85`,
576
- ` REASONING: Cleanup reduces maintenance burden`,
577
- ``,
578
- `PROPOSALS:`,
579
- ];
580
- for (let i = 0; i < proposals.length; i++) {
581
- const p = proposals[i];
582
- todoPrompt.push(`${i + 1}. [${p.type}] "${p.title}"`);
583
- }
584
- try {
585
- const jsonOutput = await this.invokeAgentInternal("architect", todoPrompt.join("\n"));
586
- const allVotes = this.parseSubagentVotes(jsonOutput, proposals);
587
- for (const proposal of proposals) {
588
- const agents = GOVERNANCE_AGENTS[proposal.type] ?? ["code-reviewer"];
589
- const participants = ["architect", ...agents];
590
- const voteId = await coordinator.initiateVoting(sessionId, proposal.title, proposal.description, participants, {
591
- complexity: Math.min(50, 10 + proposal.evidence.length * 5 + (proposal.confidence > 0.8 ? 10 : 0)),
592
- riskLevel: proposal.type === "fix" ? "low" : proposal.type === "guard" ? "high" : "medium",
593
- hasSecurityConcerns: proposal.type === "guard" || proposal.type === "fix",
594
- hasArchitecturalImpact: proposal.type === "codify" || proposal.type === "automate",
595
- participantCount: participants.length,
596
- });
597
- const proposalVotes = allVotes.filter((v) => v.proposalId === proposal.id);
598
- for (const v of proposalVotes) {
599
- coordinator.submitVote(voteId, v.agentName, v.decision, v.confidence, v.reasoning);
600
- }
601
- const resolved = coordinator.resolveVoting(voteId);
602
- if (resolved) {
603
- results.push({
604
- proposalId: proposal.id,
605
- decision: resolved.decision === "approve" ? "approve" : "reject",
606
- confidence: resolved.confidence,
607
- details: resolved.details?.map((d) => `${d.agentName}: vote=${d.vote}, weight=${d.weight.toFixed(2)}`) || [],
608
- });
609
- if (resolved.details) {
610
- for (const detail of resolved.details) {
611
- this.getCoordinator().getAggregator().updateAgentPerformance(detail.agentName, resolved.decision, detail.vote, detail.vote === resolved.decision, proposal.confidence);
612
- }
613
- }
614
- }
615
- else {
616
- results.push(this.rejectNoQuorum(proposal, "agents did not vote (no quorum)"));
617
- }
618
- }
619
- }
620
- catch (error) {
621
- frameworkLogger.log("inference-cycle", "architect-governance-failed", "error", { error: String(error) });
622
- for (const proposal of proposals) {
623
- results.push(this.rejectNoQuorum(proposal, `agent invocation failed: ${error instanceof Error ? error.message : String(error)}`));
624
- }
625
- }
626
- const metrics = coordinator.getMetrics();
627
- frameworkLogger.log("inference-cycle", "governance-metrics", "info", {
628
- totalVotes: metrics.totalVotes,
629
- avgConfidence: metrics.averageConfidence.toFixed(2),
630
- strategyUsage: metrics.strategyUsage,
631
- });
632
- return results;
633
- }
634
- /**
635
- * Pure individual knowledge-skill MCP path for governance.
636
- * Used when XRAY_FORCE_MCP_GOVERNANCE=true.
637
- * Each proposal is evaluated directly by the relevant skill servers using analyze_proposal.
638
- */
639
- async governProposalsWithIndividualSkills(proposals) {
640
- const results = [];
641
- const GOVERNANCE_AGENTS = {
642
- fix: ["code-review", "security-audit", "researcher"],
643
- refactor: ["code-review", "security-audit", "researcher"],
644
- guard: ["code-review", "security-audit", "researcher"],
645
- automate: ["code-review", "security-audit", "researcher"],
646
- codify: ["code-review", "security-audit", "researcher"],
647
- };
648
- for (const proposal of proposals) {
649
- const agents = GOVERNANCE_AGENTS[proposal.type] ?? ["code-review", "security-audit"];
650
- const skillVotes = [];
651
- for (const agent of agents) {
652
- try {
653
- const skillResult = await mcpClientManager.callServerTool(agent, "analyze_proposal", {
654
- proposalTitle: proposal.title,
655
- proposalDescription: proposal.description,
656
- evidence: proposal.evidence,
657
- proposalType: proposal.type,
658
- });
659
- let structured = "";
660
- const contents = skillResult?.content || [];
661
- for (const c of contents) {
662
- if (c?.text && c.text.includes("DECISION:")) {
663
- structured = c.text.trim();
664
- break;
665
- }
666
- }
667
- if (!structured) {
668
- const full = JSON.stringify(skillResult);
669
- const m = full.match(/DECISION:\s*(approve|reject|abstain)[\s\S]{0,300}?REASONING:[^\n"]*/i);
670
- if (m)
671
- structured = m[0].trim();
672
- }
673
- skillVotes.push({
674
- agent,
675
- toolUsed: "analyze_proposal",
676
- rawResponse: structured || JSON.stringify(skillResult),
677
- structuredVote: structured || null,
678
- });
679
- }
680
- catch (err) {
681
- skillVotes.push({
682
- agent,
683
- toolUsed: "analyze_proposal",
684
- rawResponse: `error: ${err}`,
685
- structuredVote: null,
686
- });
687
- }
688
- }
689
- const approves = skillVotes.filter((v) => v.structuredVote && v.structuredVote.includes("DECISION: approve")).length;
690
- const rejects = skillVotes.filter((v) => v.structuredVote && v.structuredVote.includes("DECISION: reject")).length;
691
- const decision = approves > rejects ? "approve" : (rejects > approves ? "reject" : "abstain");
692
- let avgConf = 0.75;
693
- const confMatches = skillVotes.map((v) => {
694
- if (!v.structuredVote)
695
- return 0.75;
696
- const m = v.structuredVote.match(/CONFIDENCE:\s*([0-9.]+)/);
697
- return m ? parseFloat(m[1]) : 0.75;
698
- });
699
- if (confMatches.length > 0)
700
- avgConf = confMatches.reduce((a, b) => a + b, 0) / confMatches.length;
701
- results.push({
702
- proposalId: proposal.id,
703
- decision: decision,
704
- confidence: Math.round(avgConf * 100) / 100,
705
- details: skillVotes.map((v) => `${v.agent}: ${v.structuredVote?.split('\n')[0] || 'no structured vote'}`),
706
- });
707
- }
708
- return results;
709
- }
710
- /**
711
- * Merge internal and external governance votes.
712
- * A proposal passes both oscillators — must be approved by internal AND external.
713
- */
714
- mergeGovernanceVotes(internalVotes, externalVotes, proposals) {
715
- const merged = [];
716
- for (const proposal of proposals) {
717
- const internal = internalVotes.find((v) => v.proposalId === proposal.id);
718
- const external = externalVotes.find((v) => v.proposalId === proposal.id);
719
- if (!internal) {
720
- merged.push(this.rejectNoQuorum(proposal, "internal governance vote missing"));
721
- continue;
722
- }
723
- const internalApproved = internal.decision === "approve";
724
- const externalApproved = external?.decision === "approve";
725
- const bothApproved = internalApproved && (!external || externalApproved);
726
- merged.push({
727
- proposalId: proposal.id,
728
- decision: bothApproved ? "approve" : "reject",
729
- confidence: internal.confidence * (external ? external.confidence : 1.0),
730
- details: [
731
- ...internal.details,
732
- ...(external ? [`External governance: ${external.decision} (conf: ${external.confidence.toFixed(2)})`, ...external.details] : []),
733
- ],
734
- });
735
- }
736
- return merged;
737
- }
738
- // Removed: buildOrchestratorGovernancePrompt (replaced by inline TODO prompt in governProposals)
739
- /**
740
- * Govern proposals using external chrono-warp-drive Dynamo endpoint
741
- */
742
- async governProposalsExternal(proposals) {
743
- const governanceIntegration = getGovernanceIntegration();
744
- if (!governanceIntegration) {
745
- throw new Error("Governance integration not available");
746
- }
747
- // Build agent reviews from proposal evidence
748
- const agentReviews = proposals.flatMap((p) => p.evidence.slice(0, 2).map((e) => `[${p.type}] ${e}`));
749
- try {
750
- const batchResult = await governanceIntegration.checkProposals(proposals, agentReviews, []);
751
- const votes = batchResult.results.map((result) => ({
752
- proposalId: result.governanceResponse.proposalId,
753
- decision: result.vote.toLowerCase() === "yes" ? "approve" : "reject",
754
- confidence: result.governanceResponse.confidence,
755
- details: [
756
- `Governance: ${result.governanceResponse.recommendation}`,
757
- `Isotope: ${result.governanceResponse.governanceIsotopeId}`,
758
- ...result.governanceResponse.reasons.slice(0, 2),
759
- ],
760
- }));
761
- frameworkLogger.log("inference-cycle", "external-governance-complete", "info", {
762
- proposalCount: proposals.length,
763
- passedCount: batchResult.results.filter((r) => r.passed).length,
764
- });
765
- return votes;
766
- }
767
- catch (error) {
768
- frameworkLogger.log("inference-cycle", "external-governance-failed", "error", {
769
- error: error instanceof Error ? error.message : String(error),
770
- });
771
- return proposals.map((p) => this.rejectNoQuorum(p, "external governance endpoint failed"));
772
- }
773
- }
774
- async invokeAgentInternal(agentName, prompt) {
775
- frameworkLogger.log("inference-cycle", "invoke-agent-internal", "info", {
776
- agentName,
777
- promptLength: prompt.length,
778
- });
779
- try {
780
- const { mcpClientManager } = await import("../mcps/mcp-client.js");
781
- const MCP_TIMEOUT_MS = 8000;
782
- const result = await Promise.race([
783
- mcpClientManager.callServerTool("orchestrator", "orchestrate-task", {
784
- description: prompt,
785
- tasks: [{
786
- id: `task-${Date.now()}`,
787
- description: prompt,
788
- type: agentName,
789
- priority: "high",
790
- }],
791
- executionMode: "sequential",
792
- }),
793
- new Promise((_, reject) => setTimeout(() => reject(new Error(`Orchestrator MCP timed out after ${MCP_TIMEOUT_MS}ms`)), MCP_TIMEOUT_MS)),
794
- ]);
795
- const content = result.content;
796
- let responseText = "";
797
- if (content && Array.isArray(content)) {
798
- responseText = content.map((c) => c.text ?? "").join("");
799
- }
800
- else {
801
- responseText = JSON.stringify(result);
802
- }
803
- // In pure MCP governance mode, trust the orchestrator response (it now does real work)
804
- const isPureMcp = process.env.XRAY_FORCE_MCP_GOVERNANCE === 'true';
805
- const hasRealContent = /PROPOSAL:\s*\d+/i.test(responseText) ||
806
- /DECISION:\s*(approve|reject|abstain)/i.test(responseText) ||
807
- /Agent Outputs \(real MCP responses\):/i.test(responseText);
808
- if (hasRealContent || isPureMcp) {
809
- if (isPureMcp && !hasRealContent) {
810
- frameworkLogger.log("inference-cycle", "pure-mcp-orchestrator-response", "info", {
811
- agentName,
812
- responsePreview: responseText.substring(0, 300),
813
- });
814
- }
815
- return responseText;
816
- }
817
- frameworkLogger.log("inference-cycle", "mcp-no-useful-content", "info", {
818
- agentName,
819
- responsePreview: responseText.substring(0, 200),
820
- });
821
- }
822
- catch (mcpError) {
823
- frameworkLogger.log("inference-cycle", "mcp-invocation-failed", "info", {
824
- agentName,
825
- error: String(mcpError),
826
- });
827
- }
828
- if (this.agentInvoker) {
829
- frameworkLogger.log("inference-cycle", "invoke-via-callback", "info", { agentName });
830
- return this.agentInvoker(agentName, prompt);
831
- }
832
- // Only fall back to OpenCode if not in forced pure MCP mode
833
- if (process.env.XRAY_FORCE_MCP_GOVERNANCE === 'true') {
834
- throw new Error(`[PURE MCP] Orchestrator returned no usable response for agent "${agentName}" and OpenCode fallback is disabled`);
835
- }
836
- return this.invokeViaOpencode(agentName, prompt);
837
- }
838
- async invokeViaOpencode(agentName, prompt) {
839
- return invokeOpencodeFromEngine(agentName, prompt, this.projectRoot);
840
- }
841
- parseSubagentVotes(jsonOutput, proposals) {
842
- const votes = [];
843
- const lines = jsonOutput.split("\n").filter((l) => l.trim());
844
- for (const line of lines) {
845
- try {
846
- const obj = JSON.parse(line);
847
- if (obj.type === "tool_use" && obj.part?.type === "tool" && obj.part.tool === "task") {
848
- const agentName = obj.part.state?.input?.subagent_type || "";
849
- const output = obj.part.state?.output || "";
850
- // Each task output may contain multiple PROPOSAL blocks
851
- const blocks = output.split(/PROPOSAL:\s*/).filter((b) => b.trim().length > 0);
852
- for (const block of blocks) {
853
- const numMatch = block.match(/^(\d+)/);
854
- if (!numMatch)
855
- continue;
856
- const proposalIdx = parseInt(numMatch[1], 10) - 1;
857
- if (proposalIdx < 0 || proposalIdx >= proposals.length)
858
- continue;
859
- const decisionMatch = block.match(/DECISION:\s*(\w+)/i);
860
- const confMatch = block.match(/CONFIDENCE:\s*(0?\.\d+|1\.0|1|0)/i);
861
- const reasonMatch = block.match(/REASONING:\s*(.+)/i);
862
- if (decisionMatch) {
863
- const proposal = proposals[proposalIdx];
864
- if (proposal) {
865
- votes.push({
866
- proposalId: proposal.id,
867
- agentName,
868
- decision: decisionMatch[1].toLowerCase(),
869
- confidence: confMatch ? Math.min(1, Math.max(0, parseFloat(confMatch[1]))) : 0.5,
870
- reasoning: reasonMatch ? reasonMatch[1].trim() : "",
871
- });
872
- }
873
- }
874
- }
875
- }
876
- }
877
- catch {
878
- // Not JSON, skip
879
- }
880
- }
881
- // Fallback: if no votes found from JSON format, try parsing plain-text
882
- // PROPOSAL/DECISION blocks directly (e.g. from opencode CLI fallback path)
883
- if (votes.length === 0 && /PROPOSAL:\s*\d+/i.test(jsonOutput)) {
884
- const blocks = jsonOutput.split(/PROPOSAL:\s*/).filter((b) => b.trim().length > 0);
885
- for (const block of blocks) {
886
- const numMatch = block.match(/^(\d+)/);
887
- if (!numMatch)
888
- continue;
889
- const numStr = numMatch[1];
890
- if (!numStr)
891
- continue;
892
- const proposalIdx = parseInt(numStr, 10) - 1;
893
- if (proposalIdx < 0 || proposalIdx >= proposals.length)
894
- continue;
895
- const agentMatch = block.match(/AGENT:\s*(\w[\w-]*)/i);
896
- const decisionMatch = block.match(/DECISION:\s*(\w+)/i);
897
- const confMatch = block.match(/CONFIDENCE:\s*(0?\.\d+|1\.0|1|0)/i);
898
- const reasonMatch = block.match(/REASONING:\s*(.+)/i);
899
- if (decisionMatch) {
900
- const proposal = proposals[proposalIdx];
901
- if (proposal) {
902
- let decision = decisionMatch[1].toLowerCase();
903
- if (decision === "yes")
904
- decision = "approve";
905
- if (decision === "no")
906
- decision = "reject";
907
- votes.push({
908
- proposalId: proposal.id,
909
- agentName: agentMatch?.[1] ?? "architect",
910
- decision,
911
- confidence: confMatch ? Math.min(1, Math.max(0, parseFloat(confMatch[1]))) : 0.5,
912
- reasoning: reasonMatch ? reasonMatch[1].trim() : "",
913
- });
914
- }
915
- }
916
- }
917
- }
918
- return votes;
919
- }
920
- getGovernanceStateManager() {
921
- if (!fs.existsSync(this.stateDir)) {
922
- fs.mkdirSync(this.stateDir, { recursive: true });
923
- }
924
- const stateFile = path.join(this.stateDir, "governance-state.json");
925
- const stateManager = new XrayStateManager();
926
- if (fs.existsSync(stateFile)) {
927
- try {
928
- const data = JSON.parse(fs.readFileSync(stateFile, "utf-8"));
929
- for (const [key, value] of Object.entries(data)) {
930
- stateManager.set(key, value);
931
- }
932
- }
933
- catch {
934
- frameworkLogger.log("inference-cycle", "governance-state-load-failed", "warning", {});
935
- }
936
- }
937
- return stateManager;
938
- }
939
- saveGovernanceState(coordinator) {
940
- const stateFile = path.join(this.stateDir, "governance-state.json");
941
- try {
942
- // Export the coordinator's internal state (voting history, metrics, etc.)
943
- const history = coordinator.getVotingHistory();
944
- const metrics = coordinator.getMetrics();
945
- const exportData = {
946
- votingHistory: history,
947
- metrics,
948
- exportedAt: new Date().toISOString(),
949
- };
950
- if (!fs.existsSync(this.stateDir)) {
951
- fs.mkdirSync(this.stateDir, { recursive: true });
952
- }
953
- fs.writeFileSync(stateFile, JSON.stringify(exportData, null, 2));
954
- }
955
- catch (error) {
956
- frameworkLogger.log("inference-cycle", "governance-state-save-failed", "warning", { error: String(error) });
957
- }
958
- }
959
274
  extractTextFromNdjson(output) {
960
275
  const texts = [];
961
276
  for (const line of output.split("\n")) {
@@ -975,13 +290,10 @@ Respond with EXACTLY one of:
975
290
  return texts.join("\n").trim();
976
291
  }
977
292
  resolveOpencodeRoot() {
978
- // Use the provider-agnostic config path resolver (prefers .xray/)
979
293
  const configDir = getConfigDir(this.projectRoot);
980
- // If we resolved to a .xray or custom dir, use its parent as the "root"
981
294
  if (configDir.includes(".xray") || configDir.includes("xray")) {
982
295
  return path.dirname(configDir);
983
296
  }
984
- // Legacy fallback
985
297
  let dir = this.projectRoot;
986
298
  for (let i = 0; i < 10; i++) {
987
299
  if (fs.existsSync(path.join(dir, ".opencode")))
@@ -996,61 +308,6 @@ Respond with EXACTLY one of:
996
308
  return cwd;
997
309
  return this.projectRoot;
998
310
  }
999
- rejectNoQuorum(proposal, reason) {
1000
- return {
1001
- proposalId: proposal.id,
1002
- decision: "reject",
1003
- confidence: 0,
1004
- details: [`rejected: ${reason}`],
1005
- };
1006
- }
1007
- classifyProposalType(problemPattern) {
1008
- const lower = problemPattern.toLowerCase();
1009
- if (lower.includes("bug") || lower.includes("fix") || lower.includes("stability"))
1010
- return "fix";
1011
- if (lower.includes("dead code") || lower.includes("remove") || lower.includes("health"))
1012
- return "refactor";
1013
- if (lower.includes("manual") || lower.includes("automate"))
1014
- return "automate";
1015
- if (lower.includes("guard") || lower.includes("path") || lower.includes("timing") || lower.includes("edge case"))
1016
- return "guard";
1017
- return "codify";
1018
- }
1019
- patternToProposalType(pattern) {
1020
- const name = pattern.name.toLowerCase();
1021
- if (name.includes("dead code"))
1022
- return "refactor";
1023
- if (name.includes("extract"))
1024
- return "refactor";
1025
- if (name.includes("registry") || name.includes("facade"))
1026
- return "codify";
1027
- if (name.includes("test"))
1028
- return "guard";
1029
- if (name.includes("stability"))
1030
- return "fix";
1031
- return "codify";
1032
- }
1033
- generateTitle(problem) {
1034
- const pattern = problem.pattern;
1035
- const ACTION_MAP = [
1036
- [/^Bug fix$/i, "Fix recurring bug pattern"],
1037
- [/^Code health cleanup$/i, "Clean up code health issues"],
1038
- [/^Accumulated dead code$/i, "Remove accumulated dead code"],
1039
- [/^Incomplete implementation$/i, "Complete partial implementation"],
1040
- [/^Technical debt/i, "Address technical debt"],
1041
- [/^Missing guard/i, "Add missing guard"],
1042
- [/^Manual step/i, "Automate manual step"],
1043
- ];
1044
- for (const [re, title] of ACTION_MAP) {
1045
- if (re.test(pattern)) {
1046
- return `${title} (${problem.occurrences}x across ${problem.sessions.length} sessions)`;
1047
- }
1048
- }
1049
- if (pattern.length > 80) {
1050
- return `Address: ${pattern.substring(0, 77)}... (${problem.occurrences}x)`;
1051
- }
1052
- return `Address: ${pattern} (${problem.occurrences}x)`;
1053
- }
1054
311
  setPhase(phase) {
1055
312
  this.phase = phase;
1056
313
  frameworkLogger.log("inference-cycle", "phase-change", "info", { phase });
@@ -1098,39 +355,6 @@ Respond with EXACTLY one of:
1098
355
  return [];
1099
356
  }
1100
357
  }
1101
- adjustConfidenceFromHistory(proposals) {
1102
- const history = this.loadHistory();
1103
- if (history.length === 0)
1104
- return;
1105
- const recentVotes = history.slice(-10).flatMap((h) => h.votes);
1106
- const approvedIds = new Set(recentVotes.filter((v) => v.decision === "approve").map((v) => v.proposalId));
1107
- const approvedTypes = new Map();
1108
- const rejectedTypes = new Map();
1109
- for (const h of history.slice(-10)) {
1110
- for (const p of h.proposals) {
1111
- if (p.status === "approved" || p.status === "applied") {
1112
- approvedTypes.set(p.type, (approvedTypes.get(p.type) ?? 0) + 1);
1113
- }
1114
- else if (p.status === "rejected" || p.status === "failed") {
1115
- rejectedTypes.set(p.type, (rejectedTypes.get(p.type) ?? 0) + 1);
1116
- }
1117
- }
1118
- }
1119
- for (const proposal of proposals) {
1120
- const approved = approvedTypes.get(proposal.type) ?? 0;
1121
- const rejected = rejectedTypes.get(proposal.type) ?? 0;
1122
- const total = approved + rejected;
1123
- if (total >= 3) {
1124
- const successRate = approved / total;
1125
- if (successRate < 0.3) {
1126
- proposal.confidence *= 0.8;
1127
- }
1128
- else if (successRate > 0.7) {
1129
- proposal.confidence = Math.min(0.95, proposal.confidence * 1.05);
1130
- }
1131
- }
1132
- }
1133
- }
1134
358
  buildBlockedResult(cycleId, reason) {
1135
359
  return {
1136
360
  cycleId,