@the-open-engine/zeroshot 5.4.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 (295) hide show
  1. package/CHANGELOG.md +539 -0
  2. package/LICENSE +21 -0
  3. package/README.md +508 -0
  4. package/cli/commands/inspect-render.js +135 -0
  5. package/cli/commands/inspect.js +294 -0
  6. package/cli/commands/providers.js +149 -0
  7. package/cli/index.js +5431 -0
  8. package/cli/lib/first-run.js +211 -0
  9. package/cli/lib/update-checker.js +281 -0
  10. package/cli/message-formatter-utils.js +75 -0
  11. package/cli/message-formatters-normal.js +275 -0
  12. package/cli/message-formatters-watch.js +185 -0
  13. package/cluster-templates/base-templates/debug-workflow.json +422 -0
  14. package/cluster-templates/base-templates/full-workflow.json +727 -0
  15. package/cluster-templates/base-templates/heavy-validation.json +272 -0
  16. package/cluster-templates/base-templates/quick-validation.json +285 -0
  17. package/cluster-templates/base-templates/single-worker.json +71 -0
  18. package/cluster-templates/base-templates/worker-validator.json +230 -0
  19. package/cluster-templates/conductor-bootstrap.json +130 -0
  20. package/docker/zeroshot-cluster/Dockerfile +153 -0
  21. package/docker/zeroshot-cluster/pre-baked-deps.json +28 -0
  22. package/lib/agent-cli-provider/adapters/claude-parser.d.ts +3 -0
  23. package/lib/agent-cli-provider/adapters/claude-parser.d.ts.map +1 -0
  24. package/lib/agent-cli-provider/adapters/claude-parser.js +122 -0
  25. package/lib/agent-cli-provider/adapters/claude-parser.js.map +1 -0
  26. package/lib/agent-cli-provider/adapters/claude-recovery.d.ts +15 -0
  27. package/lib/agent-cli-provider/adapters/claude-recovery.d.ts.map +1 -0
  28. package/lib/agent-cli-provider/adapters/claude-recovery.js +165 -0
  29. package/lib/agent-cli-provider/adapters/claude-recovery.js.map +1 -0
  30. package/lib/agent-cli-provider/adapters/claude.d.ts +3 -0
  31. package/lib/agent-cli-provider/adapters/claude.d.ts.map +1 -0
  32. package/lib/agent-cli-provider/adapters/claude.js +181 -0
  33. package/lib/agent-cli-provider/adapters/claude.js.map +1 -0
  34. package/lib/agent-cli-provider/adapters/codex-parser.d.ts +3 -0
  35. package/lib/agent-cli-provider/adapters/codex-parser.d.ts.map +1 -0
  36. package/lib/agent-cli-provider/adapters/codex-parser.js +192 -0
  37. package/lib/agent-cli-provider/adapters/codex-parser.js.map +1 -0
  38. package/lib/agent-cli-provider/adapters/codex.d.ts +3 -0
  39. package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -0
  40. package/lib/agent-cli-provider/adapters/codex.js +151 -0
  41. package/lib/agent-cli-provider/adapters/codex.js.map +1 -0
  42. package/lib/agent-cli-provider/adapters/common.d.ts +28 -0
  43. package/lib/agent-cli-provider/adapters/common.d.ts.map +1 -0
  44. package/lib/agent-cli-provider/adapters/common.js +71 -0
  45. package/lib/agent-cli-provider/adapters/common.js.map +1 -0
  46. package/lib/agent-cli-provider/adapters/gemini.d.ts +3 -0
  47. package/lib/agent-cli-provider/adapters/gemini.d.ts.map +1 -0
  48. package/lib/agent-cli-provider/adapters/gemini.js +176 -0
  49. package/lib/agent-cli-provider/adapters/gemini.js.map +1 -0
  50. package/lib/agent-cli-provider/adapters/index.d.ts +9 -0
  51. package/lib/agent-cli-provider/adapters/index.d.ts.map +1 -0
  52. package/lib/agent-cli-provider/adapters/index.js +95 -0
  53. package/lib/agent-cli-provider/adapters/index.js.map +1 -0
  54. package/lib/agent-cli-provider/adapters/opencode.d.ts +3 -0
  55. package/lib/agent-cli-provider/adapters/opencode.d.ts.map +1 -0
  56. package/lib/agent-cli-provider/adapters/opencode.js +228 -0
  57. package/lib/agent-cli-provider/adapters/opencode.js.map +1 -0
  58. package/lib/agent-cli-provider/contract-actions.d.ts +5 -0
  59. package/lib/agent-cli-provider/contract-actions.d.ts.map +1 -0
  60. package/lib/agent-cli-provider/contract-actions.js +140 -0
  61. package/lib/agent-cli-provider/contract-actions.js.map +1 -0
  62. package/lib/agent-cli-provider/contract-env.d.ts +11 -0
  63. package/lib/agent-cli-provider/contract-env.d.ts.map +1 -0
  64. package/lib/agent-cli-provider/contract-env.js +113 -0
  65. package/lib/agent-cli-provider/contract-env.js.map +1 -0
  66. package/lib/agent-cli-provider/contract-envelope.d.ts +49 -0
  67. package/lib/agent-cli-provider/contract-envelope.d.ts.map +1 -0
  68. package/lib/agent-cli-provider/contract-envelope.js +52 -0
  69. package/lib/agent-cli-provider/contract-envelope.js.map +1 -0
  70. package/lib/agent-cli-provider/contract-errors.d.ts +22 -0
  71. package/lib/agent-cli-provider/contract-errors.d.ts.map +1 -0
  72. package/lib/agent-cli-provider/contract-errors.js +71 -0
  73. package/lib/agent-cli-provider/contract-errors.js.map +1 -0
  74. package/lib/agent-cli-provider/contract-fallback.d.ts +7 -0
  75. package/lib/agent-cli-provider/contract-fallback.d.ts.map +1 -0
  76. package/lib/agent-cli-provider/contract-fallback.js +102 -0
  77. package/lib/agent-cli-provider/contract-fallback.js.map +1 -0
  78. package/lib/agent-cli-provider/contract-invoke.d.ts +5 -0
  79. package/lib/agent-cli-provider/contract-invoke.d.ts.map +1 -0
  80. package/lib/agent-cli-provider/contract-invoke.js +89 -0
  81. package/lib/agent-cli-provider/contract-invoke.js.map +1 -0
  82. package/lib/agent-cli-provider/contract-options.d.ts +3 -0
  83. package/lib/agent-cli-provider/contract-options.d.ts.map +1 -0
  84. package/lib/agent-cli-provider/contract-options.js +140 -0
  85. package/lib/agent-cli-provider/contract-options.js.map +1 -0
  86. package/lib/agent-cli-provider/contract-parse.d.ts +15 -0
  87. package/lib/agent-cli-provider/contract-parse.d.ts.map +1 -0
  88. package/lib/agent-cli-provider/contract-parse.js +81 -0
  89. package/lib/agent-cli-provider/contract-parse.js.map +1 -0
  90. package/lib/agent-cli-provider/contract-support.d.ts +18 -0
  91. package/lib/agent-cli-provider/contract-support.d.ts.map +1 -0
  92. package/lib/agent-cli-provider/contract-support.js +154 -0
  93. package/lib/agent-cli-provider/contract-support.js.map +1 -0
  94. package/lib/agent-cli-provider/contract.d.ts +13 -0
  95. package/lib/agent-cli-provider/contract.d.ts.map +1 -0
  96. package/lib/agent-cli-provider/contract.js +30 -0
  97. package/lib/agent-cli-provider/contract.js.map +1 -0
  98. package/lib/agent-cli-provider/env-safety.d.ts +11 -0
  99. package/lib/agent-cli-provider/env-safety.d.ts.map +1 -0
  100. package/lib/agent-cli-provider/env-safety.js +83 -0
  101. package/lib/agent-cli-provider/env-safety.js.map +1 -0
  102. package/lib/agent-cli-provider/errors.d.ts +5 -0
  103. package/lib/agent-cli-provider/errors.d.ts.map +1 -0
  104. package/lib/agent-cli-provider/errors.js +115 -0
  105. package/lib/agent-cli-provider/errors.js.map +1 -0
  106. package/lib/agent-cli-provider/executable.d.ts +3 -0
  107. package/lib/agent-cli-provider/executable.d.ts.map +1 -0
  108. package/lib/agent-cli-provider/executable.js +24 -0
  109. package/lib/agent-cli-provider/executable.js.map +1 -0
  110. package/lib/agent-cli-provider/index.d.ts +8 -0
  111. package/lib/agent-cli-provider/index.d.ts.map +1 -0
  112. package/lib/agent-cli-provider/index.js +31 -0
  113. package/lib/agent-cli-provider/index.js.map +1 -0
  114. package/lib/agent-cli-provider/invoke-evidence.d.ts +4 -0
  115. package/lib/agent-cli-provider/invoke-evidence.d.ts.map +1 -0
  116. package/lib/agent-cli-provider/invoke-evidence.js +17 -0
  117. package/lib/agent-cli-provider/invoke-evidence.js.map +1 -0
  118. package/lib/agent-cli-provider/json.d.ts +16 -0
  119. package/lib/agent-cli-provider/json.d.ts.map +1 -0
  120. package/lib/agent-cli-provider/json.js +110 -0
  121. package/lib/agent-cli-provider/json.js.map +1 -0
  122. package/lib/agent-cli-provider/log-prefix.d.ts +2 -0
  123. package/lib/agent-cli-provider/log-prefix.d.ts.map +1 -0
  124. package/lib/agent-cli-provider/log-prefix.js +22 -0
  125. package/lib/agent-cli-provider/log-prefix.js.map +1 -0
  126. package/lib/agent-cli-provider/process-runner.d.ts +17 -0
  127. package/lib/agent-cli-provider/process-runner.d.ts.map +1 -0
  128. package/lib/agent-cli-provider/process-runner.js +89 -0
  129. package/lib/agent-cli-provider/process-runner.js.map +1 -0
  130. package/lib/agent-cli-provider/redaction.d.ts +9 -0
  131. package/lib/agent-cli-provider/redaction.d.ts.map +1 -0
  132. package/lib/agent-cli-provider/redaction.js +227 -0
  133. package/lib/agent-cli-provider/redaction.js.map +1 -0
  134. package/lib/agent-cli-provider/schema.d.ts +5 -0
  135. package/lib/agent-cli-provider/schema.d.ts.map +1 -0
  136. package/lib/agent-cli-provider/schema.js +136 -0
  137. package/lib/agent-cli-provider/schema.js.map +1 -0
  138. package/lib/agent-cli-provider/single-agent-runtime.d.ts +15 -0
  139. package/lib/agent-cli-provider/single-agent-runtime.d.ts.map +1 -0
  140. package/lib/agent-cli-provider/single-agent-runtime.js +228 -0
  141. package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -0
  142. package/lib/agent-cli-provider/types.d.ts +194 -0
  143. package/lib/agent-cli-provider/types.d.ts.map +1 -0
  144. package/lib/agent-cli-provider/types.js +12 -0
  145. package/lib/agent-cli-provider/types.js.map +1 -0
  146. package/lib/completion.js +174 -0
  147. package/lib/detached-startup.js +220 -0
  148. package/lib/docker-config.js +220 -0
  149. package/lib/git-remote-utils.js +165 -0
  150. package/lib/id-detector.js +55 -0
  151. package/lib/provider-defaults.js +62 -0
  152. package/lib/provider-detection.js +59 -0
  153. package/lib/provider-names.js +57 -0
  154. package/lib/repo-settings.js +69 -0
  155. package/lib/settings/claude-auth.js +78 -0
  156. package/lib/settings.js +542 -0
  157. package/lib/start-cluster.js +321 -0
  158. package/lib/stream-json-parser.js +67 -0
  159. package/package.json +162 -0
  160. package/scripts/fix-node-pty-permissions.js +75 -0
  161. package/scripts/record-demo.sh +279 -0
  162. package/scripts/setup-merge-queue.sh +170 -0
  163. package/scripts/test-install.sh +40 -0
  164. package/scripts/validate-templates.js +107 -0
  165. package/src/agent/agent-config.js +266 -0
  166. package/src/agent/agent-context-builder.js +189 -0
  167. package/src/agent/agent-context-sections.js +338 -0
  168. package/src/agent/agent-context-sources.js +147 -0
  169. package/src/agent/agent-hook-executor.js +721 -0
  170. package/src/agent/agent-input-injector.js +141 -0
  171. package/src/agent/agent-lifecycle.js +982 -0
  172. package/src/agent/agent-quality-gate-schema.js +93 -0
  173. package/src/agent/agent-quality-gates-context.js +51 -0
  174. package/src/agent/agent-stuck-detector.js +256 -0
  175. package/src/agent/agent-task-executor.js +2028 -0
  176. package/src/agent/agent-trigger-evaluator.js +67 -0
  177. package/src/agent/context-metrics.js +160 -0
  178. package/src/agent/context-pack-builder.js +367 -0
  179. package/src/agent/context-replay-policy.js +51 -0
  180. package/src/agent/guidance-queue.js +77 -0
  181. package/src/agent/output-extraction.js +367 -0
  182. package/src/agent/output-reformatter.js +175 -0
  183. package/src/agent/pr-verification.js +653 -0
  184. package/src/agent/rate-limit-backoff.js +82 -0
  185. package/src/agent/schema-utils.js +146 -0
  186. package/src/agent/validation-platform.js +35 -0
  187. package/src/agent-cli-provider/adapters/claude-parser.ts +133 -0
  188. package/src/agent-cli-provider/adapters/claude-recovery.ts +203 -0
  189. package/src/agent-cli-provider/adapters/claude.ts +247 -0
  190. package/src/agent-cli-provider/adapters/codex-parser.ts +211 -0
  191. package/src/agent-cli-provider/adapters/codex.ts +217 -0
  192. package/src/agent-cli-provider/adapters/common.ts +124 -0
  193. package/src/agent-cli-provider/adapters/gemini.ts +243 -0
  194. package/src/agent-cli-provider/adapters/index.ts +126 -0
  195. package/src/agent-cli-provider/adapters/opencode.ts +286 -0
  196. package/src/agent-cli-provider/contract-actions.ts +150 -0
  197. package/src/agent-cli-provider/contract-env.ts +111 -0
  198. package/src/agent-cli-provider/contract-envelope.ts +110 -0
  199. package/src/agent-cli-provider/contract-errors.ts +66 -0
  200. package/src/agent-cli-provider/contract-fallback.ts +121 -0
  201. package/src/agent-cli-provider/contract-invoke.ts +104 -0
  202. package/src/agent-cli-provider/contract-options.ts +173 -0
  203. package/src/agent-cli-provider/contract-parse.ts +94 -0
  204. package/src/agent-cli-provider/contract-support.ts +167 -0
  205. package/src/agent-cli-provider/contract.ts +56 -0
  206. package/src/agent-cli-provider/env-safety.ts +82 -0
  207. package/src/agent-cli-provider/errors.ts +122 -0
  208. package/src/agent-cli-provider/executable.ts +24 -0
  209. package/src/agent-cli-provider/index.ts +83 -0
  210. package/src/agent-cli-provider/invoke-evidence.ts +18 -0
  211. package/src/agent-cli-provider/json.ts +114 -0
  212. package/src/agent-cli-provider/log-prefix.ts +20 -0
  213. package/src/agent-cli-provider/process-runner.ts +145 -0
  214. package/src/agent-cli-provider/redaction.ts +282 -0
  215. package/src/agent-cli-provider/schema.ts +115 -0
  216. package/src/agent-cli-provider/single-agent-runtime.ts +311 -0
  217. package/src/agent-cli-provider/types.ts +237 -0
  218. package/src/agent-wrapper.js +615 -0
  219. package/src/agents/git-pusher-template.js +705 -0
  220. package/src/attach/attach-client.js +438 -0
  221. package/src/attach/attach-server.js +543 -0
  222. package/src/attach/index.js +37 -0
  223. package/src/attach/protocol.js +220 -0
  224. package/src/attach/ring-buffer.js +121 -0
  225. package/src/attach/send-input.js +88 -0
  226. package/src/attach/socket-discovery.js +267 -0
  227. package/src/claude-task-runner.js +661 -0
  228. package/src/config-router.js +89 -0
  229. package/src/config-validator.js +2202 -0
  230. package/src/copy-worker.js +43 -0
  231. package/src/guidance-topics.js +10 -0
  232. package/src/input-helpers.js +65 -0
  233. package/src/isolation-manager.js +1734 -0
  234. package/src/issue-providers/README.md +305 -0
  235. package/src/issue-providers/azure-devops-provider.js +307 -0
  236. package/src/issue-providers/base-provider.js +232 -0
  237. package/src/issue-providers/github-provider.js +210 -0
  238. package/src/issue-providers/gitlab-provider.js +262 -0
  239. package/src/issue-providers/index.js +196 -0
  240. package/src/issue-providers/jira-provider.js +260 -0
  241. package/src/ledger.js +692 -0
  242. package/src/lib/gc.js +232 -0
  243. package/src/lib/safe-exec.js +88 -0
  244. package/src/logic-engine.js +201 -0
  245. package/src/message-buffer.js +81 -0
  246. package/src/message-bus-bridge.js +144 -0
  247. package/src/message-bus.js +256 -0
  248. package/src/name-generator.js +232 -0
  249. package/src/orchestrator.js +3924 -0
  250. package/src/preflight.js +712 -0
  251. package/src/process-metrics.js +608 -0
  252. package/src/providers/anthropic/index.js +3 -0
  253. package/src/providers/base-provider.js +355 -0
  254. package/src/providers/capabilities.js +60 -0
  255. package/src/providers/google/index.js +3 -0
  256. package/src/providers/index.js +293 -0
  257. package/src/providers/openai/index.js +3 -0
  258. package/src/providers/opencode/index.js +3 -0
  259. package/src/quality-gates.js +143 -0
  260. package/src/schemas/sub-cluster.js +208 -0
  261. package/src/state-snapshot.js +398 -0
  262. package/src/state-snapshotter.js +142 -0
  263. package/src/status-footer.js +1026 -0
  264. package/src/sub-cluster-wrapper.js +693 -0
  265. package/src/task-runner.js +30 -0
  266. package/src/template-resolver.js +425 -0
  267. package/src/template-validation/index.js +338 -0
  268. package/src/template-validation/simulate-consensus-gates.js +324 -0
  269. package/src/template-validation/simulate-random-topology.js +541 -0
  270. package/src/template-validation/simulate-two-stage-validation.js +270 -0
  271. package/src/worktree-claude-config.js +135 -0
  272. package/src/worktree-tooling-env.js +150 -0
  273. package/task-lib/attachable-watcher.js +381 -0
  274. package/task-lib/commands/clean.js +50 -0
  275. package/task-lib/commands/episodes.js +105 -0
  276. package/task-lib/commands/get-log-path.js +23 -0
  277. package/task-lib/commands/kill.js +32 -0
  278. package/task-lib/commands/list.js +105 -0
  279. package/task-lib/commands/logs.js +439 -0
  280. package/task-lib/commands/resume.js +42 -0
  281. package/task-lib/commands/run.js +57 -0
  282. package/task-lib/commands/schedule.js +105 -0
  283. package/task-lib/commands/scheduler-cmd.js +96 -0
  284. package/task-lib/commands/schedules.js +148 -0
  285. package/task-lib/commands/status.js +44 -0
  286. package/task-lib/commands/unschedule.js +16 -0
  287. package/task-lib/completion.js +9 -0
  288. package/task-lib/config.js +11 -0
  289. package/task-lib/name-generator.js +230 -0
  290. package/task-lib/package.json +3 -0
  291. package/task-lib/provider-helper-runtime.js +29 -0
  292. package/task-lib/runner.js +190 -0
  293. package/task-lib/scheduler.js +252 -0
  294. package/task-lib/store.js +529 -0
  295. package/task-lib/watcher.js +305 -0
@@ -0,0 +1,338 @@
1
+ const fs = require('node:fs');
2
+ const path = require('node:path');
3
+
4
+ const { validateConfig } = require('../config-validator');
5
+ const { getConfig } = require('../config-router');
6
+ const TemplateResolver = require('../template-resolver');
7
+ const { SHARED_TRIGGER_SCRIPT } = require('../agents/git-pusher-template');
8
+ const { simulateConsensusGates } = require('./simulate-consensus-gates');
9
+ const { simulateTwoStageValidation } = require('./simulate-two-stage-validation');
10
+ const { simulateRandomTopology } = require('./simulate-random-topology');
11
+
12
+ const CONDUCTOR_COMPLEXITIES = ['TRIVIAL', 'SIMPLE', 'STANDARD', 'CRITICAL'];
13
+ const CONDUCTOR_TASK_TYPES = ['TASK', 'DEBUG', 'INQUIRY'];
14
+
15
+ function findJsonFiles(dir) {
16
+ const files = [];
17
+ if (!fs.existsSync(dir)) return files;
18
+
19
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
20
+ const fullPath = path.join(dir, entry.name);
21
+ if (entry.isDirectory()) {
22
+ files.push(...findJsonFiles(fullPath));
23
+ } else if (entry.name.endsWith('.json')) {
24
+ files.push(fullPath);
25
+ }
26
+ }
27
+ return files;
28
+ }
29
+
30
+ function inferTemplateIdFromPath(filePath) {
31
+ const base = path.basename(filePath, '.json');
32
+ return base || 'unknown';
33
+ }
34
+
35
+ async function validateTemplateConfig({
36
+ config,
37
+ templateId,
38
+ deep,
39
+ randomSampling = false,
40
+ templatesDir,
41
+ randomOptions = {},
42
+ }) {
43
+ const result = validateConfig(config);
44
+ const isParameterizedTemplate = !!(config?.params && Object.keys(config.params).length > 0);
45
+
46
+ if (result.valid) {
47
+ const simErrors = [];
48
+ simErrors.push(
49
+ ...simulateConsensusGates(config, {
50
+ allowExternalTopics: isParameterizedTemplate
51
+ ? ['IMPLEMENTATION_READY', 'QUICK_VALIDATION_PASSED']
52
+ : [],
53
+ })
54
+ );
55
+ if (deep) {
56
+ simErrors.push(...(await simulateTwoStageValidation({ templateId, config })));
57
+ }
58
+ if (randomSampling) {
59
+ simErrors.push(
60
+ ...(await simulateRandomTopology({
61
+ config,
62
+ templateId,
63
+ templatesDir,
64
+ ...randomOptions,
65
+ }))
66
+ );
67
+ }
68
+ if (simErrors.length > 0) {
69
+ result.valid = false;
70
+ result.errors.push(...simErrors);
71
+ }
72
+ }
73
+
74
+ return result;
75
+ }
76
+
77
+ function hasCompletionHandler(config) {
78
+ const agents = Array.isArray(config?.agents) ? config.agents : [];
79
+ return agents.some(
80
+ (agent) =>
81
+ agent.id === 'completion-detector' ||
82
+ agent.id === 'git-pusher' ||
83
+ agent.hooks?.onComplete?.config?.topic === 'CLUSTER_COMPLETE' ||
84
+ agent.triggers?.some((trigger) => trigger.action === 'stop_cluster')
85
+ );
86
+ }
87
+
88
+ function mergeAgentsById(primaryConfig, additionalConfig) {
89
+ const merged = {
90
+ ...primaryConfig,
91
+ agents: Array.isArray(primaryConfig?.agents) ? [...primaryConfig.agents] : [],
92
+ };
93
+ const seen = new Set(merged.agents.map((agent) => agent.id));
94
+
95
+ for (const agent of additionalConfig?.agents || []) {
96
+ if (seen.has(agent.id)) {
97
+ continue;
98
+ }
99
+ merged.agents.push(agent);
100
+ seen.add(agent.id);
101
+ }
102
+
103
+ return merged;
104
+ }
105
+
106
+ function augmentCriticalResolvedConfig({ resolver, config, params }) {
107
+ if (params?.complexity !== 'CRITICAL' || params?.validator_count !== 0) {
108
+ return config;
109
+ }
110
+
111
+ const quickValidationConfig = resolver.resolve('quick-validation', {
112
+ validator_level: params.validator_level,
113
+ max_tokens: params.max_tokens,
114
+ timeout: params.timeout || 0,
115
+ });
116
+ return mergeAgentsById(config, quickValidationConfig);
117
+ }
118
+
119
+ function buildSyntheticGitPusher() {
120
+ return {
121
+ id: 'git-pusher',
122
+ role: 'completion-detector',
123
+ modelLevel: 'level1',
124
+ triggers: [
125
+ {
126
+ topic: 'VALIDATION_RESULT',
127
+ logic: {
128
+ engine: 'javascript',
129
+ script: SHARED_TRIGGER_SCRIPT,
130
+ },
131
+ action: 'execute_task',
132
+ },
133
+ ],
134
+ hooks: {
135
+ onComplete: {
136
+ action: 'publish_message',
137
+ config: {
138
+ topic: 'CLUSTER_COMPLETE',
139
+ content: {
140
+ text: 'PR flow completed.',
141
+ },
142
+ },
143
+ },
144
+ },
145
+ };
146
+ }
147
+
148
+ function buildSyntheticCompletionDetector() {
149
+ return {
150
+ id: 'completion-detector',
151
+ role: 'orchestrator',
152
+ modelLevel: 'level1',
153
+ triggers: [
154
+ {
155
+ topic: 'VALIDATION_RESULT',
156
+ logic: {
157
+ engine: 'javascript',
158
+ script: SHARED_TRIGGER_SCRIPT,
159
+ },
160
+ action: 'stop_cluster',
161
+ },
162
+ ],
163
+ };
164
+ }
165
+
166
+ function buildResolvedRoute({ conductorTemplatePath, resolver, complexity, taskType, autoPr }) {
167
+ const { base, params } = getConfig(complexity, taskType, { autoPr });
168
+ const resolved = resolver.resolve(base, params);
169
+ const augmented = augmentCriticalResolvedConfig({
170
+ resolver,
171
+ config: resolved,
172
+ params,
173
+ });
174
+ const configWithCompletion = ensureCompletionHandler(augmented, { autoPr });
175
+ const modeSuffix = autoPr ? '-autopr' : '';
176
+
177
+ return {
178
+ filePath: `${conductorTemplatePath}#resolved${modeSuffix}:${complexity}-${taskType}`,
179
+ templateId: `resolved-${base}${modeSuffix}`,
180
+ config: configWithCompletion,
181
+ };
182
+ }
183
+
184
+ function getRouteModes(taskType) {
185
+ if (taskType === 'INQUIRY') {
186
+ return [false];
187
+ }
188
+
189
+ return [false, true];
190
+ }
191
+
192
+ function buildResolvedConductorRoutes(templatesDir) {
193
+ const conductorTemplatePath = path.join(templatesDir, 'conductor-bootstrap.json');
194
+ if (!fs.existsSync(conductorTemplatePath)) {
195
+ return [];
196
+ }
197
+
198
+ const resolver = new TemplateResolver(templatesDir);
199
+ const routeConfigs = [];
200
+
201
+ for (const complexity of CONDUCTOR_COMPLEXITIES) {
202
+ for (const taskType of CONDUCTOR_TASK_TYPES) {
203
+ for (const autoPr of getRouteModes(taskType)) {
204
+ routeConfigs.push(
205
+ buildResolvedRoute({
206
+ conductorTemplatePath,
207
+ resolver,
208
+ complexity,
209
+ taskType,
210
+ autoPr,
211
+ })
212
+ );
213
+ }
214
+ }
215
+ }
216
+
217
+ return routeConfigs;
218
+ }
219
+
220
+ function shouldSkipConfig(config) {
221
+ return !config.agents && !config.name;
222
+ }
223
+
224
+ function updateValidationSummary(summary, result) {
225
+ summary.results.push(result);
226
+ summary.validated++;
227
+ if (!result.result.valid) {
228
+ summary.hasErrors = true;
229
+ }
230
+ }
231
+
232
+ async function validateConfigEntry({
233
+ filePath,
234
+ config,
235
+ templateId,
236
+ deep,
237
+ randomSampling,
238
+ templatesDir,
239
+ randomOptions,
240
+ }) {
241
+ try {
242
+ const result = await validateTemplateConfig({
243
+ config,
244
+ templateId,
245
+ deep,
246
+ randomSampling,
247
+ templatesDir,
248
+ randomOptions,
249
+ });
250
+
251
+ return { filePath, result };
252
+ } catch (err) {
253
+ return {
254
+ filePath,
255
+ result: { valid: false, errors: [err.message], warnings: [] },
256
+ };
257
+ }
258
+ }
259
+
260
+ async function validateTemplates({
261
+ templatesDir,
262
+ deep = false,
263
+ randomSampling = false,
264
+ randomScope = 'resolved',
265
+ randomOptions = {},
266
+ }) {
267
+ const templateFiles = [...findJsonFiles(templatesDir)];
268
+ const resolvedConductorRoutes = buildResolvedConductorRoutes(templatesDir);
269
+
270
+ const summary = {
271
+ hasErrors: false,
272
+ validated: 0,
273
+ skipped: 0,
274
+ results: [],
275
+ };
276
+
277
+ for (const filePath of templateFiles) {
278
+ const content = fs.readFileSync(filePath, 'utf-8');
279
+ const config = JSON.parse(content);
280
+
281
+ if (shouldSkipConfig(config)) {
282
+ summary.skipped++;
283
+ continue;
284
+ }
285
+
286
+ const result = await validateConfigEntry({
287
+ filePath,
288
+ config,
289
+ templateId: inferTemplateIdFromPath(filePath),
290
+ deep,
291
+ randomSampling: randomSampling && randomScope === 'all',
292
+ templatesDir,
293
+ randomOptions,
294
+ });
295
+ updateValidationSummary(summary, result);
296
+ }
297
+
298
+ for (const resolvedRoute of resolvedConductorRoutes) {
299
+ const result = await validateConfigEntry({
300
+ filePath: resolvedRoute.filePath,
301
+ config: resolvedRoute.config,
302
+ templateId: resolvedRoute.templateId,
303
+ deep,
304
+ randomSampling,
305
+ templatesDir,
306
+ randomOptions,
307
+ });
308
+ updateValidationSummary(summary, result);
309
+ }
310
+
311
+ return {
312
+ valid: !summary.hasErrors,
313
+ validated: summary.validated,
314
+ skipped: summary.skipped,
315
+ results: summary.results,
316
+ };
317
+ }
318
+
319
+ function ensureCompletionHandler(config, options = {}) {
320
+ const { autoPr = false } = options;
321
+
322
+ if (hasCompletionHandler(config)) {
323
+ return config;
324
+ }
325
+
326
+ return {
327
+ ...config,
328
+ agents: [
329
+ ...(config.agents || []),
330
+ autoPr ? buildSyntheticGitPusher() : buildSyntheticCompletionDetector(),
331
+ ],
332
+ };
333
+ }
334
+
335
+ module.exports = {
336
+ validateTemplates,
337
+ validateTemplateConfig,
338
+ };
@@ -0,0 +1,324 @@
1
+ const Ledger = require('../ledger');
2
+ const MessageBus = require('../message-bus');
3
+ const LogicEngine = require('../logic-engine');
4
+
5
+ const STAGE_START_TOPICS = ['IMPLEMENTATION_READY', 'QUICK_VALIDATION_PASSED'];
6
+ const EXTERNAL_STAGE_SENDERS = {
7
+ IMPLEMENTATION_READY: 'worker',
8
+ QUICK_VALIDATION_PASSED: 'consensus-coordinator',
9
+ };
10
+
11
+ function scriptReferencesTopic(logicScript, topic) {
12
+ return logicScript.includes(`topic: '${topic}'`) || logicScript.includes(`topic: "${topic}"`);
13
+ }
14
+
15
+ function getRequiredStageTopics(logicScript) {
16
+ return STAGE_START_TOPICS.filter((topic) => scriptReferencesTopic(logicScript, topic));
17
+ }
18
+
19
+ function publishStageStartMessages({
20
+ messageBus,
21
+ clusterId,
22
+ producersByTopic,
23
+ requiredStageTopics,
24
+ allowExternalTopics,
25
+ }) {
26
+ let timestamp = Date.now();
27
+
28
+ for (const topic of requiredStageTopics) {
29
+ const producers = Array.from(producersByTopic.get(topic) || []);
30
+ const sender =
31
+ producers[0] || (allowExternalTopics.includes(topic) ? EXTERNAL_STAGE_SENDERS[topic] : null);
32
+ if (!sender) {
33
+ continue;
34
+ }
35
+
36
+ messageBus.publish({
37
+ cluster_id: clusterId,
38
+ topic,
39
+ sender,
40
+ timestamp: timestamp++,
41
+ });
42
+ }
43
+ }
44
+
45
+ function collectTopicProducers(config) {
46
+ const producersByTopic = new Map();
47
+
48
+ for (const agent of config.agents || []) {
49
+ const topic = getPublishedTopic(agent);
50
+ if (!topic) continue;
51
+ if (!producersByTopic.has(topic)) {
52
+ producersByTopic.set(topic, new Set());
53
+ }
54
+ producersByTopic.get(topic).add(agent.id);
55
+ }
56
+
57
+ return producersByTopic;
58
+ }
59
+
60
+ function getPublishedTopic(agent) {
61
+ const onComplete = agent?.hooks?.onComplete;
62
+ if (!onComplete) return null;
63
+ if (onComplete.action !== 'publish_message') return null;
64
+ if (!onComplete.config?.topic) return null;
65
+ return String(onComplete.config.topic);
66
+ }
67
+
68
+ function hasConsensusLikeId(agentId) {
69
+ return agentId.includes('consensus') || agentId.includes('coordinator');
70
+ }
71
+
72
+ function hasStopClusterTrigger(agent) {
73
+ return agent?.triggers?.some((trigger) => trigger.action === 'stop_cluster');
74
+ }
75
+
76
+ function isConsensusLikeAgent(agent) {
77
+ const agentId = String(agent?.id || '');
78
+ const explicitIds = ['git-pusher', 'completion-detector'];
79
+
80
+ return (
81
+ agent?.role === 'coordinator' ||
82
+ explicitIds.includes(agentId) ||
83
+ hasConsensusLikeId(agentId) ||
84
+ hasStopClusterTrigger(agent)
85
+ );
86
+ }
87
+
88
+ function getMissingStageTopics(requiredStageTopics, producersByTopic, allowExternalTopics) {
89
+ return requiredStageTopics.filter((stageTopic) => {
90
+ const producers = producersByTopic.get(stageTopic);
91
+ return (!producers || producers.size === 0) && !allowExternalTopics.includes(stageTopic);
92
+ });
93
+ }
94
+
95
+ function createSimulationContext(config, options = {}) {
96
+ const agents = Array.isArray(config.agents) ? config.agents : [];
97
+
98
+ return {
99
+ agents,
100
+ producersByTopic: collectTopicProducers(config),
101
+ allowExternalTopics: Array.isArray(options.allowExternalTopics)
102
+ ? options.allowExternalTopics
103
+ : [],
104
+ cluster: {
105
+ id: 'template-sim',
106
+ agents: agents.map((a) => ({
107
+ id: a.id,
108
+ role: a.role,
109
+ requiredQualityGates: a.requiredQualityGates || [],
110
+ })),
111
+ },
112
+ };
113
+ }
114
+
115
+ function evaluateScenario({
116
+ agentId,
117
+ cluster,
118
+ topic,
119
+ script,
120
+ requiredQualityGates,
121
+ producers,
122
+ producersByTopic,
123
+ requiredStageTopics,
124
+ allowExternalTopics,
125
+ publishMessages,
126
+ }) {
127
+ const ledger = new Ledger(':memory:');
128
+ const messageBus = new MessageBus(ledger);
129
+ const logicEngine = new LogicEngine(messageBus, cluster);
130
+
131
+ publishStageStartMessages({
132
+ messageBus,
133
+ clusterId: cluster.id,
134
+ producersByTopic,
135
+ requiredStageTopics,
136
+ allowExternalTopics,
137
+ });
138
+
139
+ publishMessages(messageBus, producers, cluster.id);
140
+
141
+ const result = logicEngine.evaluate(
142
+ script,
143
+ { id: agentId, cluster_id: cluster.id, requiredQualityGates },
144
+ { topic }
145
+ );
146
+ ledger.close();
147
+ return result;
148
+ }
149
+
150
+ function getPassingQualityGate(requiredGate) {
151
+ const scope = requiredGate.scope || 'template-sim';
152
+ return {
153
+ id: requiredGate.id,
154
+ status: 'PASS',
155
+ scope,
156
+ completedAt: Date.now(),
157
+ evidence: {
158
+ command: `quality-check --scope ${scope}`,
159
+ exitCode: 0,
160
+ output: 'template simulation quality pass',
161
+ },
162
+ };
163
+ }
164
+
165
+ function getApprovedResultData(context) {
166
+ const data = { approved: true };
167
+ const requiredQualityGates = Array.isArray(context.requiredQualityGates)
168
+ ? context.requiredQualityGates
169
+ : [];
170
+ if (
171
+ context.agentId === 'git-pusher' &&
172
+ context.topic === 'VALIDATION_RESULT' &&
173
+ requiredQualityGates.length > 0
174
+ ) {
175
+ data.qualityGates = requiredQualityGates.map(getPassingQualityGate);
176
+ }
177
+ return data;
178
+ }
179
+
180
+ function checkDuplicateProducerScenario(context) {
181
+ return evaluateScenario({
182
+ ...context,
183
+ publishMessages(messageBus, producers, clusterId) {
184
+ messageBus.publish({
185
+ cluster_id: clusterId,
186
+ topic: context.topic,
187
+ sender: producers[0],
188
+ content: { data: getApprovedResultData(context) },
189
+ });
190
+ messageBus.publish({
191
+ cluster_id: clusterId,
192
+ topic: context.topic,
193
+ sender: producers[0],
194
+ content: { data: getApprovedResultData(context) },
195
+ });
196
+ },
197
+ });
198
+ }
199
+
200
+ function checkDistinctProducerScenario(context) {
201
+ return evaluateScenario({
202
+ ...context,
203
+ publishMessages(messageBus, producers, clusterId) {
204
+ for (const producer of producers) {
205
+ messageBus.publish({
206
+ cluster_id: clusterId,
207
+ topic: context.topic,
208
+ sender: producer,
209
+ content: { data: getApprovedResultData(context) },
210
+ });
211
+ }
212
+ },
213
+ });
214
+ }
215
+
216
+ function getMissingStageFailure(agentId, topic, missingStageTopics) {
217
+ if (missingStageTopics.length === 0) {
218
+ return null;
219
+ }
220
+
221
+ return (
222
+ `Agent "${agentId}" trigger on "${topic}" depends on missing stage topic(s): ${missingStageTopics.join(', ')}. ` +
223
+ 'Preflight must validate real producers, not synthesize stage-start messages.'
224
+ );
225
+ }
226
+
227
+ function getConsensusScenarioContext(agent, trigger, simulation) {
228
+ const topic = trigger?.topic;
229
+ const script = trigger?.logic?.script;
230
+ if (!topic || !script) {
231
+ return null;
232
+ }
233
+
234
+ const requiredStageTopics = getRequiredStageTopics(script);
235
+ const missingStageTopics = getMissingStageTopics(
236
+ requiredStageTopics,
237
+ simulation.producersByTopic,
238
+ simulation.allowExternalTopics
239
+ );
240
+ const missingStageFailure = getMissingStageFailure(agent.id, topic, missingStageTopics);
241
+ if (missingStageFailure) {
242
+ return { failure: missingStageFailure };
243
+ }
244
+
245
+ const producers = Array.from(simulation.producersByTopic.get(topic) || []);
246
+ if (producers.length < 2) {
247
+ return { failure: null };
248
+ }
249
+
250
+ return {
251
+ context: {
252
+ agentId: agent.id,
253
+ cluster: simulation.cluster,
254
+ topic,
255
+ script,
256
+ requiredQualityGates: agent.requiredQualityGates || [],
257
+ producers,
258
+ producersByTopic: simulation.producersByTopic,
259
+ requiredStageTopics,
260
+ allowExternalTopics: simulation.allowExternalTopics,
261
+ },
262
+ failure: null,
263
+ };
264
+ }
265
+
266
+ function validateConsensusTrigger(agent, trigger, simulation) {
267
+ const scenario = getConsensusScenarioContext(agent, trigger, simulation);
268
+ if (!scenario) {
269
+ return [];
270
+ }
271
+
272
+ if (scenario.failure) {
273
+ return [scenario.failure];
274
+ }
275
+
276
+ if (!scenario.context) {
277
+ return [];
278
+ }
279
+
280
+ const { context } = scenario;
281
+ const { producers, topic } = context;
282
+
283
+ const failures = [];
284
+ if (checkDuplicateProducerScenario(context)) {
285
+ failures.push(
286
+ `Agent "${agent.id}" trigger on "${topic}" fires early on duplicate sender (${producers[0]}). ` +
287
+ `Gate must require distinct producers: ${producers.join(', ')}`
288
+ );
289
+ }
290
+
291
+ if (!checkDistinctProducerScenario(context)) {
292
+ failures.push(
293
+ `Agent "${agent.id}" trigger on "${topic}" did not fire after all producers published. ` +
294
+ `Expected producers: ${producers.join(', ')}`
295
+ );
296
+ }
297
+
298
+ return failures;
299
+ }
300
+
301
+ /**
302
+ * Micro-sim: consensus-like trigger gates must not fire early due to duplicate publishes
303
+ * from the same producer (common in retries / double-publish bugs).
304
+ *
305
+ * Returns an array of error strings.
306
+ */
307
+ function simulateConsensusGates(config, options = {}) {
308
+ const simulation = createSimulationContext(config, options);
309
+ const failures = [];
310
+
311
+ for (const agent of simulation.agents) {
312
+ if (!isConsensusLikeAgent(agent)) continue;
313
+ for (const trigger of agent.triggers || []) {
314
+ failures.push(...validateConsensusTrigger(agent, trigger, simulation));
315
+ }
316
+ }
317
+
318
+ return failures;
319
+ }
320
+
321
+ module.exports = {
322
+ collectTopicProducers,
323
+ simulateConsensusGates,
324
+ };