principles-disciple 1.8.0 → 1.8.2

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 (460) hide show
  1. package/ADVANCED_CONFIG_ZH.md +97 -0
  2. package/AGENT_INSTALL.md +173 -0
  3. package/AGENT_INSTALL_EN.md +173 -0
  4. package/INSTALL.md +256 -0
  5. package/SKILL.md +63 -0
  6. package/docs/COMMAND_REFERENCE.md +76 -0
  7. package/docs/COMMAND_REFERENCE_EN.md +79 -0
  8. package/esbuild.config.js +75 -0
  9. package/openclaw.plugin.json +6 -1
  10. package/package.json +13 -15
  11. package/scripts/build-web.mjs +46 -0
  12. package/scripts/install-dependencies.cjs +47 -0
  13. package/scripts/sync-plugin.mjs +802 -0
  14. package/scripts/verify-build.mjs +109 -0
  15. package/src/agents/nocturnal-dreamer.md +152 -0
  16. package/src/agents/nocturnal-philosopher.md +138 -0
  17. package/src/agents/nocturnal-reflector.md +126 -0
  18. package/src/agents/nocturnal-scribe.md +164 -0
  19. package/src/commands/capabilities.ts +85 -0
  20. package/{dist/commands/context.js → src/commands/context.ts} +78 -38
  21. package/src/commands/evolution-status.ts +146 -0
  22. package/src/commands/export.ts +111 -0
  23. package/src/commands/focus.ts +533 -0
  24. package/src/commands/nocturnal-review.ts +311 -0
  25. package/src/commands/nocturnal-rollout.ts +763 -0
  26. package/src/commands/nocturnal-train.ts +1002 -0
  27. package/{dist/commands/pain.js → src/commands/pain.ts} +68 -49
  28. package/src/commands/principle-rollback.ts +27 -0
  29. package/{dist/commands/rollback.js → src/commands/rollback.ts} +44 -12
  30. package/src/commands/samples.ts +60 -0
  31. package/src/commands/strategy.ts +38 -0
  32. package/{dist/commands/thinking-os.js → src/commands/thinking-os.ts} +59 -36
  33. package/src/commands/workflow-debug.ts +128 -0
  34. package/{dist/config/defaults/runtime.js → src/config/defaults/runtime.ts} +12 -5
  35. package/src/config/errors.ts +163 -0
  36. package/{dist/config/index.d.ts → src/config/index.ts} +2 -1
  37. package/src/constants/diagnostician.ts +66 -0
  38. package/src/constants/tools.ts +62 -0
  39. package/src/core/adaptive-thresholds.ts +476 -0
  40. package/{dist/core/config-service.js → src/core/config-service.ts} +7 -4
  41. package/{dist/core/config.js → src/core/config.ts} +158 -46
  42. package/src/core/control-ui-db.ts +435 -0
  43. package/{dist/core/detection-funnel.js → src/core/detection-funnel.ts} +36 -21
  44. package/{dist/core/detection-service.js → src/core/detection-service.ts} +7 -4
  45. package/{dist/core/dictionary-service.js → src/core/dictionary-service.ts} +7 -4
  46. package/{dist/core/dictionary.js → src/core/dictionary.ts} +57 -34
  47. package/src/core/empathy-keyword-matcher.ts +327 -0
  48. package/src/core/empathy-types.ts +218 -0
  49. package/src/core/event-log.ts +544 -0
  50. package/src/core/evolution-engine.ts +612 -0
  51. package/src/core/evolution-logger.ts +353 -0
  52. package/src/core/evolution-migration.ts +77 -0
  53. package/src/core/evolution-reducer.ts +731 -0
  54. package/src/core/evolution-types.ts +456 -0
  55. package/src/core/external-training-contract.ts +527 -0
  56. package/src/core/focus-history.ts +1458 -0
  57. package/src/core/hygiene/tracker.ts +117 -0
  58. package/{dist/core/init.js → src/core/init.ts} +39 -26
  59. package/src/core/local-worker-routing.ts +617 -0
  60. package/{dist/core/migration.js → src/core/migration.ts} +18 -11
  61. package/src/core/model-deployment-registry.ts +722 -0
  62. package/src/core/model-training-registry.ts +813 -0
  63. package/src/core/nocturnal-arbiter.ts +706 -0
  64. package/src/core/nocturnal-candidate-scoring.ts +392 -0
  65. package/src/core/nocturnal-compliance.ts +1075 -0
  66. package/src/core/nocturnal-dataset.ts +668 -0
  67. package/src/core/nocturnal-executability.ts +428 -0
  68. package/src/core/nocturnal-export.ts +390 -0
  69. package/{dist/core/nocturnal-paths.js → src/core/nocturnal-paths.ts} +49 -23
  70. package/src/core/nocturnal-trajectory-extractor.ts +484 -0
  71. package/src/core/nocturnal-trinity.ts +1384 -0
  72. package/src/core/pain.ts +122 -0
  73. package/{dist/core/path-resolver.js → src/core/path-resolver.ts} +157 -36
  74. package/{dist/core/paths.js → src/core/paths.ts} +13 -4
  75. package/src/core/principle-training-state.ts +450 -0
  76. package/src/core/profile.ts +226 -0
  77. package/src/core/promotion-gate.ts +822 -0
  78. package/{dist/core/risk-calculator.js → src/core/risk-calculator.ts} +42 -16
  79. package/{dist/core/session-tracker.js → src/core/session-tracker.ts} +185 -63
  80. package/src/core/shadow-observation-registry.ts +534 -0
  81. package/{dist/core/system-logger.js → src/core/system-logger.ts} +9 -5
  82. package/src/core/thinking-models.ts +217 -0
  83. package/src/core/training-program.ts +630 -0
  84. package/src/core/trajectory-types.ts +243 -0
  85. package/src/core/trajectory.ts +1673 -0
  86. package/{dist/core/workspace-context.js → src/core/workspace-context.ts} +57 -32
  87. package/src/hooks/bash-risk.ts +171 -0
  88. package/src/hooks/edit-verification.ts +295 -0
  89. package/src/hooks/gate-block-helper.ts +160 -0
  90. package/src/hooks/gate.ts +210 -0
  91. package/src/hooks/gfi-gate.ts +177 -0
  92. package/src/hooks/lifecycle.ts +326 -0
  93. package/{dist/hooks/llm.js → src/hooks/llm.ts} +166 -139
  94. package/src/hooks/message-sanitize.ts +45 -0
  95. package/src/hooks/pain.ts +384 -0
  96. package/src/hooks/progressive-trust-gate.ts +174 -0
  97. package/src/hooks/prompt.ts +920 -0
  98. package/src/hooks/subagent.ts +207 -0
  99. package/src/hooks/thinking-checkpoint.ts +73 -0
  100. package/src/hooks/trajectory-collector.ts +290 -0
  101. package/src/http/principles-console-route.ts +716 -0
  102. package/src/i18n/commands.ts +117 -0
  103. package/src/index.ts +694 -0
  104. package/src/service/central-database.ts +831 -0
  105. package/src/service/control-ui-query-service.ts +888 -0
  106. package/src/service/evolution-query-service.ts +405 -0
  107. package/src/service/evolution-worker.ts +1646 -0
  108. package/src/service/health-query-service.ts +836 -0
  109. package/{dist/service/nocturnal-runtime.js → src/service/nocturnal-runtime.ts} +263 -36
  110. package/src/service/nocturnal-service.ts +1015 -0
  111. package/src/service/nocturnal-target-selector.ts +532 -0
  112. package/src/service/phase3-input-filter.ts +237 -0
  113. package/src/service/runtime-summary-service.ts +757 -0
  114. package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +513 -0
  115. package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +603 -0
  116. package/src/service/subagent-workflow/index.ts +51 -0
  117. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +856 -0
  118. package/src/service/subagent-workflow/runtime-direct-driver.ts +166 -0
  119. package/src/service/subagent-workflow/types.ts +378 -0
  120. package/src/service/subagent-workflow/workflow-store.ts +328 -0
  121. package/src/service/trajectory-service.ts +15 -0
  122. package/{dist/tools/critique-prompt.js → src/tools/critique-prompt.ts} +25 -8
  123. package/src/tools/deep-reflect.ts +349 -0
  124. package/{dist/tools/model-index.js → src/tools/model-index.ts} +33 -17
  125. package/src/types/event-types.ts +453 -0
  126. package/src/types/hygiene-types.ts +31 -0
  127. package/src/types/principle-tree-schema.ts +244 -0
  128. package/src/types/runtime-summary.ts +49 -0
  129. package/src/types.ts +74 -0
  130. package/src/utils/file-lock.ts +391 -0
  131. package/{dist/utils/glob-match.js → src/utils/glob-match.ts} +21 -20
  132. package/{dist/utils/hashing.js → src/utils/hashing.ts} +6 -4
  133. package/src/utils/io.ts +110 -0
  134. package/{dist/utils/nlp.js → src/utils/nlp.ts} +19 -12
  135. package/{dist/utils/plugin-logger.js → src/utils/plugin-logger.ts} +33 -8
  136. package/src/utils/subagent-probe.ts +94 -0
  137. package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +70 -1
  138. package/templates/pain_settings.json +2 -1
  139. package/tests/README.md +120 -0
  140. package/tests/build-artifacts.test.ts +111 -0
  141. package/tests/commands/evolution-status.test.ts +222 -0
  142. package/tests/commands/evolver.test.ts +22 -0
  143. package/tests/commands/export.test.ts +78 -0
  144. package/tests/commands/nocturnal-review.test.ts +448 -0
  145. package/tests/commands/nocturnal-train.test.ts +97 -0
  146. package/tests/commands/pain.test.ts +108 -0
  147. package/tests/commands/samples.test.ts +65 -0
  148. package/tests/commands/strategy.test.ts +34 -0
  149. package/tests/commands/thinking-os.test.ts +88 -0
  150. package/tests/core/adaptive-thresholds.test.ts +261 -0
  151. package/tests/core/config-service.test.ts +89 -0
  152. package/tests/core/config.test.ts +90 -0
  153. package/tests/core/control-ui-db.test.ts +75 -0
  154. package/tests/core/core-template-guidance.test.ts +21 -0
  155. package/tests/core/detection-funnel.test.ts +63 -0
  156. package/tests/core/detection-service.test.ts +50 -0
  157. package/tests/core/dictionary-service.test.ts +116 -0
  158. package/tests/core/dictionary.test.ts +168 -0
  159. package/tests/core/empathy-keyword-matcher.test.ts +209 -0
  160. package/tests/core/event-log.test.ts +181 -0
  161. package/tests/core/evolution-e2e.test.ts +58 -0
  162. package/tests/core/evolution-engine-gate-integration.test.ts +543 -0
  163. package/tests/core/evolution-engine.test.ts +562 -0
  164. package/tests/core/evolution-logger.test.ts +148 -0
  165. package/tests/core/evolution-migration.test.ts +50 -0
  166. package/tests/core/evolution-paths.test.ts +21 -0
  167. package/tests/core/evolution-reducer.detector-metadata.test.ts +602 -0
  168. package/tests/core/evolution-reducer.test.ts +180 -0
  169. package/tests/core/evolution-types-loop.test.ts +48 -0
  170. package/tests/core/evolution-user-stories.e2e.test.ts +249 -0
  171. package/tests/core/external-training-contract.test.ts +463 -0
  172. package/tests/core/focus-history.test.ts +682 -0
  173. package/tests/core/init-flatten.test.ts +69 -0
  174. package/tests/core/init-refactor.test.ts +87 -0
  175. package/tests/core/init-v1.3.test.ts +46 -0
  176. package/tests/core/init.test.ts +190 -0
  177. package/tests/core/local-worker-routing.test.ts +757 -0
  178. package/tests/core/migration.test.ts +84 -0
  179. package/tests/core/model-deployment-registry.test.ts +845 -0
  180. package/tests/core/model-training-registry.test.ts +889 -0
  181. package/tests/core/nocturnal-arbiter.test.ts +494 -0
  182. package/tests/core/nocturnal-candidate-scoring.test.ts +400 -0
  183. package/tests/core/nocturnal-compliance.test.ts +646 -0
  184. package/tests/core/nocturnal-dataset.test.ts +892 -0
  185. package/tests/core/nocturnal-executability.test.ts +357 -0
  186. package/tests/core/nocturnal-export.test.ts +462 -0
  187. package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +428 -0
  188. package/tests/core/nocturnal-trajectory-extractor.test.ts +634 -0
  189. package/tests/core/nocturnal-trinity.test.ts +953 -0
  190. package/tests/core/pain.test.ts +33 -0
  191. package/tests/core/path-resolver.test.ts +57 -0
  192. package/tests/core/paths-refactor.test.ts +42 -0
  193. package/tests/core/phase7-rollout-integration.test.ts +477 -0
  194. package/tests/core/principle-training-state.test.ts +712 -0
  195. package/tests/core/profile.test.ts +56 -0
  196. package/tests/core/promotion-gate.test.ts +556 -0
  197. package/tests/core/risk-calculator.test.ts +168 -0
  198. package/tests/core/session-tracker.test.ts +191 -0
  199. package/tests/core/training-program.test.ts +472 -0
  200. package/tests/core/trajectory.test.ts +265 -0
  201. package/tests/core/workspace-context-factory.test.ts +18 -0
  202. package/tests/core/workspace-context.test.ts +134 -0
  203. package/tests/fixtures/nocturnal-reviewed-subset.json +183 -0
  204. package/tests/fixtures/production-compatibility.test.ts +147 -0
  205. package/tests/fixtures/production-mock-generator.ts +282 -0
  206. package/tests/hooks/bash-risk-integration.test.ts +137 -0
  207. package/tests/hooks/bash-risk.test.ts +81 -0
  208. package/tests/hooks/edit-verification.test.ts +678 -0
  209. package/tests/hooks/gate-edit-verification-p1.test.ts +632 -0
  210. package/tests/hooks/gate-edit-verification.test.ts +435 -0
  211. package/tests/hooks/gate-pipeline-integration.test.ts +404 -0
  212. package/tests/hooks/gate.test.ts +271 -0
  213. package/tests/hooks/gfi-gate-unit.test.ts +422 -0
  214. package/tests/hooks/gfi-gate.test.ts +669 -0
  215. package/tests/hooks/lifecycle.test.ts +248 -0
  216. package/tests/hooks/llm.test.ts +308 -0
  217. package/tests/hooks/message-sanitize.test.ts +36 -0
  218. package/tests/hooks/pain.test.ts +141 -0
  219. package/tests/hooks/progressive-trust-gate.test.ts +277 -0
  220. package/tests/hooks/prompt.test.ts +1411 -0
  221. package/tests/hooks/subagent.test.ts +467 -0
  222. package/tests/hooks/thinking-gate.test.ts +313 -0
  223. package/tests/http/principles-console-route.test.ts +140 -0
  224. package/tests/hygiene-tracker.test.ts +77 -0
  225. package/tests/index.integration.test.ts +179 -0
  226. package/tests/index.shadow-routing.integration.test.ts +140 -0
  227. package/tests/index.test.ts +9 -0
  228. package/tests/integration/empathy-workflow-integration.test.ts +627 -0
  229. package/tests/service/control-ui-query-service.test.ts +121 -0
  230. package/tests/service/empathy-observer-workflow-manager.test.ts +176 -0
  231. package/tests/service/evolution-worker.test.ts +585 -0
  232. package/tests/service/nocturnal-runtime.test.ts +470 -0
  233. package/tests/service/nocturnal-service.test.ts +577 -0
  234. package/tests/service/nocturnal-target-selector.test.ts +615 -0
  235. package/tests/service/nocturnal-workflow-manager.test.ts +439 -0
  236. package/tests/service/phase3-input-filter.test.ts +289 -0
  237. package/tests/service/runtime-summary-service.test.ts +919 -0
  238. package/tests/task-compliance.test.ts +166 -0
  239. package/tests/test-utils.ts +48 -0
  240. package/tests/tools/critique-prompt.test.ts +260 -0
  241. package/tests/tools/deep-reflect.test.ts +232 -0
  242. package/tests/tools/model-index.test.ts +246 -0
  243. package/tests/ui/app.test.tsx +114 -0
  244. package/tests/utils/file-lock.test.ts +407 -0
  245. package/tests/utils/hashing.test.ts +32 -0
  246. package/tests/utils/io.test.ts +39 -0
  247. package/tests/utils/nlp.test.ts +53 -0
  248. package/tests/utils/plugin-logger.test.ts +156 -0
  249. package/tsconfig.json +16 -0
  250. package/tsconfig.tsbuildinfo +1 -0
  251. package/ui/src/App.tsx +45 -0
  252. package/ui/src/api.ts +216 -0
  253. package/ui/src/charts.tsx +586 -0
  254. package/ui/src/components/ErrorState.tsx +6 -0
  255. package/ui/src/components/Loading.tsx +13 -0
  256. package/ui/src/components/ProtectedRoute.tsx +12 -0
  257. package/ui/src/components/Shell.tsx +91 -0
  258. package/ui/src/components/WorkspaceConfig.tsx +146 -0
  259. package/ui/src/components/index.ts +5 -0
  260. package/ui/src/context/auth.tsx +80 -0
  261. package/ui/src/context/theme.tsx +66 -0
  262. package/ui/src/hooks/useAutoRefresh.ts +39 -0
  263. package/ui/src/i18n/ui.ts +363 -0
  264. package/ui/src/main.tsx +16 -0
  265. package/ui/src/pages/EvolutionPage.tsx +352 -0
  266. package/ui/src/pages/FeedbackPage.tsx +140 -0
  267. package/ui/src/pages/GateMonitorPage.tsx +136 -0
  268. package/ui/src/pages/LoginPage.tsx +88 -0
  269. package/ui/src/pages/OverviewPage.tsx +238 -0
  270. package/ui/src/pages/SamplesPage.tsx +174 -0
  271. package/ui/src/pages/ThinkingModelsPage.tsx +127 -0
  272. package/ui/src/styles.css +1661 -0
  273. package/ui/src/types.ts +368 -0
  274. package/ui/src/utils/format.ts +15 -0
  275. package/vitest.config.ts +23 -0
  276. package/dist/commands/capabilities.d.ts +0 -3
  277. package/dist/commands/capabilities.js +0 -73
  278. package/dist/commands/context.d.ts +0 -5
  279. package/dist/commands/evolution-status.d.ts +0 -4
  280. package/dist/commands/evolution-status.js +0 -117
  281. package/dist/commands/evolver.d.ts +0 -9
  282. package/dist/commands/evolver.js +0 -26
  283. package/dist/commands/export.d.ts +0 -2
  284. package/dist/commands/export.js +0 -98
  285. package/dist/commands/focus.d.ts +0 -14
  286. package/dist/commands/focus.js +0 -457
  287. package/dist/commands/nocturnal-review.d.ts +0 -24
  288. package/dist/commands/nocturnal-review.js +0 -265
  289. package/dist/commands/nocturnal-rollout.d.ts +0 -27
  290. package/dist/commands/nocturnal-rollout.js +0 -671
  291. package/dist/commands/nocturnal-train.d.ts +0 -25
  292. package/dist/commands/nocturnal-train.js +0 -919
  293. package/dist/commands/pain.d.ts +0 -5
  294. package/dist/commands/principle-rollback.d.ts +0 -4
  295. package/dist/commands/principle-rollback.js +0 -22
  296. package/dist/commands/rollback.d.ts +0 -19
  297. package/dist/commands/samples.d.ts +0 -2
  298. package/dist/commands/samples.js +0 -55
  299. package/dist/commands/strategy.d.ts +0 -3
  300. package/dist/commands/strategy.js +0 -29
  301. package/dist/commands/thinking-os.d.ts +0 -2
  302. package/dist/config/defaults/runtime.d.ts +0 -40
  303. package/dist/config/errors.d.ts +0 -84
  304. package/dist/config/errors.js +0 -94
  305. package/dist/config/index.js +0 -7
  306. package/dist/constants/diagnostician.d.ts +0 -12
  307. package/dist/constants/diagnostician.js +0 -56
  308. package/dist/constants/tools.d.ts +0 -17
  309. package/dist/constants/tools.js +0 -54
  310. package/dist/core/adaptive-thresholds.d.ts +0 -186
  311. package/dist/core/adaptive-thresholds.js +0 -300
  312. package/dist/core/config-service.d.ts +0 -15
  313. package/dist/core/config.d.ts +0 -127
  314. package/dist/core/control-ui-db.d.ts +0 -95
  315. package/dist/core/control-ui-db.js +0 -292
  316. package/dist/core/detection-funnel.d.ts +0 -33
  317. package/dist/core/detection-service.d.ts +0 -15
  318. package/dist/core/dictionary-service.d.ts +0 -15
  319. package/dist/core/dictionary.d.ts +0 -38
  320. package/dist/core/event-log.d.ts +0 -82
  321. package/dist/core/event-log.js +0 -463
  322. package/dist/core/evolution-engine.d.ts +0 -118
  323. package/dist/core/evolution-engine.js +0 -464
  324. package/dist/core/evolution-logger.d.ts +0 -137
  325. package/dist/core/evolution-logger.js +0 -256
  326. package/dist/core/evolution-migration.d.ts +0 -5
  327. package/dist/core/evolution-migration.js +0 -65
  328. package/dist/core/evolution-reducer.d.ts +0 -98
  329. package/dist/core/evolution-reducer.js +0 -465
  330. package/dist/core/evolution-types.d.ts +0 -287
  331. package/dist/core/evolution-types.js +0 -78
  332. package/dist/core/external-training-contract.d.ts +0 -276
  333. package/dist/core/external-training-contract.js +0 -269
  334. package/dist/core/focus-history.d.ts +0 -210
  335. package/dist/core/focus-history.js +0 -1185
  336. package/dist/core/hygiene/tracker.d.ts +0 -22
  337. package/dist/core/hygiene/tracker.js +0 -106
  338. package/dist/core/init.d.ts +0 -12
  339. package/dist/core/local-worker-routing.d.ts +0 -175
  340. package/dist/core/local-worker-routing.js +0 -525
  341. package/dist/core/migration.d.ts +0 -6
  342. package/dist/core/model-deployment-registry.d.ts +0 -218
  343. package/dist/core/model-deployment-registry.js +0 -503
  344. package/dist/core/model-training-registry.d.ts +0 -295
  345. package/dist/core/model-training-registry.js +0 -475
  346. package/dist/core/nocturnal-arbiter.d.ts +0 -159
  347. package/dist/core/nocturnal-arbiter.js +0 -534
  348. package/dist/core/nocturnal-candidate-scoring.d.ts +0 -137
  349. package/dist/core/nocturnal-candidate-scoring.js +0 -266
  350. package/dist/core/nocturnal-compliance.d.ts +0 -175
  351. package/dist/core/nocturnal-compliance.js +0 -824
  352. package/dist/core/nocturnal-dataset.d.ts +0 -224
  353. package/dist/core/nocturnal-dataset.js +0 -443
  354. package/dist/core/nocturnal-executability.d.ts +0 -85
  355. package/dist/core/nocturnal-executability.js +0 -331
  356. package/dist/core/nocturnal-export.d.ts +0 -124
  357. package/dist/core/nocturnal-export.js +0 -275
  358. package/dist/core/nocturnal-paths.d.ts +0 -124
  359. package/dist/core/nocturnal-trajectory-extractor.d.ts +0 -242
  360. package/dist/core/nocturnal-trajectory-extractor.js +0 -307
  361. package/dist/core/nocturnal-trinity.d.ts +0 -311
  362. package/dist/core/nocturnal-trinity.js +0 -880
  363. package/dist/core/pain.d.ts +0 -4
  364. package/dist/core/pain.js +0 -70
  365. package/dist/core/path-resolver.d.ts +0 -46
  366. package/dist/core/paths.d.ts +0 -65
  367. package/dist/core/principle-training-state.d.ts +0 -121
  368. package/dist/core/principle-training-state.js +0 -321
  369. package/dist/core/profile.d.ts +0 -62
  370. package/dist/core/profile.js +0 -210
  371. package/dist/core/promotion-gate.d.ts +0 -238
  372. package/dist/core/promotion-gate.js +0 -529
  373. package/dist/core/risk-calculator.d.ts +0 -22
  374. package/dist/core/session-tracker.d.ts +0 -99
  375. package/dist/core/shadow-observation-registry.d.ts +0 -217
  376. package/dist/core/shadow-observation-registry.js +0 -308
  377. package/dist/core/system-logger.d.ts +0 -8
  378. package/dist/core/thinking-models.d.ts +0 -38
  379. package/dist/core/thinking-models.js +0 -170
  380. package/dist/core/training-program.d.ts +0 -233
  381. package/dist/core/training-program.js +0 -433
  382. package/dist/core/trajectory.d.ts +0 -411
  383. package/dist/core/trajectory.js +0 -1307
  384. package/dist/core/workspace-context.d.ts +0 -71
  385. package/dist/hooks/bash-risk.d.ts +0 -57
  386. package/dist/hooks/bash-risk.js +0 -137
  387. package/dist/hooks/edit-verification.d.ts +0 -62
  388. package/dist/hooks/edit-verification.js +0 -256
  389. package/dist/hooks/gate-block-helper.d.ts +0 -44
  390. package/dist/hooks/gate-block-helper.js +0 -119
  391. package/dist/hooks/gate.d.ts +0 -24
  392. package/dist/hooks/gate.js +0 -173
  393. package/dist/hooks/gfi-gate.d.ts +0 -40
  394. package/dist/hooks/gfi-gate.js +0 -113
  395. package/dist/hooks/lifecycle.d.ts +0 -5
  396. package/dist/hooks/lifecycle.js +0 -284
  397. package/dist/hooks/llm.d.ts +0 -12
  398. package/dist/hooks/message-sanitize.d.ts +0 -3
  399. package/dist/hooks/message-sanitize.js +0 -37
  400. package/dist/hooks/pain.d.ts +0 -5
  401. package/dist/hooks/pain.js +0 -301
  402. package/dist/hooks/progressive-trust-gate.d.ts +0 -51
  403. package/dist/hooks/progressive-trust-gate.js +0 -89
  404. package/dist/hooks/prompt.d.ts +0 -47
  405. package/dist/hooks/prompt.js +0 -884
  406. package/dist/hooks/subagent.d.ts +0 -10
  407. package/dist/hooks/subagent.js +0 -387
  408. package/dist/hooks/thinking-checkpoint.d.ts +0 -37
  409. package/dist/hooks/thinking-checkpoint.js +0 -51
  410. package/dist/hooks/trajectory-collector.d.ts +0 -32
  411. package/dist/hooks/trajectory-collector.js +0 -256
  412. package/dist/http/principles-console-route.d.ts +0 -9
  413. package/dist/http/principles-console-route.js +0 -567
  414. package/dist/i18n/commands.d.ts +0 -26
  415. package/dist/i18n/commands.js +0 -116
  416. package/dist/index.d.ts +0 -7
  417. package/dist/index.js +0 -581
  418. package/dist/service/central-database.d.ts +0 -104
  419. package/dist/service/central-database.js +0 -649
  420. package/dist/service/control-ui-query-service.d.ts +0 -221
  421. package/dist/service/control-ui-query-service.js +0 -543
  422. package/dist/service/empathy-observer-manager.d.ts +0 -52
  423. package/dist/service/empathy-observer-manager.js +0 -229
  424. package/dist/service/evolution-query-service.d.ts +0 -155
  425. package/dist/service/evolution-query-service.js +0 -258
  426. package/dist/service/evolution-worker.d.ts +0 -101
  427. package/dist/service/evolution-worker.js +0 -974
  428. package/dist/service/nocturnal-runtime.d.ts +0 -183
  429. package/dist/service/nocturnal-service.d.ts +0 -163
  430. package/dist/service/nocturnal-service.js +0 -787
  431. package/dist/service/nocturnal-target-selector.d.ts +0 -145
  432. package/dist/service/nocturnal-target-selector.js +0 -315
  433. package/dist/service/phase3-input-filter.d.ts +0 -73
  434. package/dist/service/phase3-input-filter.js +0 -172
  435. package/dist/service/runtime-summary-service.d.ts +0 -122
  436. package/dist/service/runtime-summary-service.js +0 -485
  437. package/dist/service/trajectory-service.d.ts +0 -2
  438. package/dist/service/trajectory-service.js +0 -15
  439. package/dist/tools/critique-prompt.d.ts +0 -14
  440. package/dist/tools/deep-reflect.d.ts +0 -39
  441. package/dist/tools/deep-reflect.js +0 -350
  442. package/dist/tools/model-index.d.ts +0 -9
  443. package/dist/types/event-types.d.ts +0 -306
  444. package/dist/types/event-types.js +0 -106
  445. package/dist/types/hygiene-types.d.ts +0 -20
  446. package/dist/types/hygiene-types.js +0 -12
  447. package/dist/types/runtime-summary.d.ts +0 -47
  448. package/dist/types/runtime-summary.js +0 -1
  449. package/dist/types.d.ts +0 -50
  450. package/dist/types.js +0 -22
  451. package/dist/utils/file-lock.d.ts +0 -71
  452. package/dist/utils/file-lock.js +0 -309
  453. package/dist/utils/glob-match.d.ts +0 -28
  454. package/dist/utils/hashing.d.ts +0 -9
  455. package/dist/utils/io.d.ts +0 -6
  456. package/dist/utils/io.js +0 -106
  457. package/dist/utils/nlp.d.ts +0 -9
  458. package/dist/utils/plugin-logger.d.ts +0 -39
  459. package/dist/utils/subagent-probe.d.ts +0 -34
  460. package/dist/utils/subagent-probe.js +0 -81
@@ -0,0 +1,1384 @@
1
+ /**
2
+ * Nocturnal Trinity — Three-Stage Reflection Chain
3
+ * ================================================
4
+ *
5
+ * PURPOSE: Upgrade single-reflector nocturnal sample generation to a
6
+ * Dreamer -> Philosopher -> Scribe Trinity chain that produces higher quality
7
+ * decision-point samples through structured multi-stage reflection.
8
+ *
9
+ * TRINITY STAGES:
10
+ * 1. Dreamer — Generates multiple candidate corrections/alternatives
11
+ * 2. Philosopher — Provides principle-grounded critique and ranking
12
+ * 3. Scribe — Produces the final structured artifact draft using tournament selection
13
+ *
14
+ * DESIGN CONSTRAINTS:
15
+ * - All stage I/O is structured JSON contracts (not prose)
16
+ * - Any malformed stage output fails the entire chain closed
17
+ * - Single-reflector fallback is preserved via useTrinity flag
18
+ * - Trinity mode is configurable but defaults to enabled
19
+ * - Final artifact still passes arbiter + executability validation
20
+ * - Telemetry records chain mode, stage outcomes, candidate counts
21
+ * - Tournament selection is deterministic (same inputs → same winner)
22
+ *
23
+ * RUNTIME ADAPTER:
24
+ * - useStubs=true: uses synchronous stub implementations (no external calls)
25
+ * - useStubs=false: requires a TrinityRuntimeAdapter for real subagent execution
26
+ * - Adapter uses ONLY public plugin runtime APIs (api.runtime.subagent.*)
27
+ */
28
+
29
+ import { randomUUID } from 'crypto';
30
+ import * as fs from 'fs';
31
+ import * as path from 'path';
32
+ import * as url from 'url';
33
+ import type { NocturnalSessionSnapshot } from './nocturnal-trajectory-extractor.js';
34
+ import { computeThinkingModelDelta } from './nocturnal-trajectory-extractor.js';
35
+ import {
36
+ runTournament,
37
+ DEFAULT_SCORING_WEIGHTS,
38
+ type ScoringWeights,
39
+ type TournamentTraceEntry,
40
+ } from './nocturnal-candidate-scoring.js';
41
+ import {
42
+ DEFAULT_THRESHOLDS,
43
+ getEffectiveThresholds,
44
+ type ThresholdValues,
45
+ } from './adaptive-thresholds.js';
46
+
47
+ // ---------------------------------------------------------------------------
48
+ // Role Prompt Loading
49
+ // ---------------------------------------------------------------------------
50
+
51
+ const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
52
+ const AGENTS_DIR = path.join(__dirname, '../agents');
53
+
54
+ function loadRolePrompt(filename: string): string {
55
+ const filePath = path.join(AGENTS_DIR, filename);
56
+ try {
57
+ return fs.readFileSync(filePath, 'utf-8');
58
+ } catch {
59
+ console.warn(`[Trinity] Could not load role prompt: ${filename}`);
60
+ return '';
61
+ }
62
+ }
63
+
64
+ // ---------------------------------------------------------------------------
65
+ // Trinity Runtime Adapter
66
+ // ---------------------------------------------------------------------------
67
+
68
+ /**
69
+ * Interface for Trinity stage invocation.
70
+ * Implementations can use real subagent runtimes or stubs.
71
+ */
72
+ export interface TrinityRuntimeAdapter {
73
+ /**
74
+ * Invoke the Dreamer stage.
75
+ * @param snapshot Session trajectory snapshot
76
+ * @param principleId Target principle ID
77
+ * @param maxCandidates Maximum number of candidates to generate
78
+ * @returns Dreamer output JSON
79
+ */
80
+ invokeDreamer(
81
+ snapshot: NocturnalSessionSnapshot,
82
+ principleId: string,
83
+ maxCandidates: number
84
+ ): Promise<DreamerOutput>;
85
+
86
+ /**
87
+ * Invoke the Philosopher stage.
88
+ * @param dreamerOutput Dreamer's output
89
+ * @param principleId Target principle ID
90
+ * @returns Philosopher output JSON
91
+ */
92
+ invokePhilosopher(
93
+ dreamerOutput: DreamerOutput,
94
+ principleId: string
95
+ ): Promise<PhilosopherOutput>;
96
+
97
+ /**
98
+ * Invoke the Scribe stage.
99
+ * @param dreamerOutput Dreamer's output
100
+ * @param philosopherOutput Philosopher's output
101
+ * @param snapshot Session snapshot
102
+ * @param principleId Target principle ID
103
+ * @param telemetry Running telemetry
104
+ * @param config Trinity config
105
+ * @returns Scribe draft artifact or null if failed
106
+ */
107
+ invokeScribe(
108
+ dreamerOutput: DreamerOutput,
109
+ philosopherOutput: PhilosopherOutput,
110
+ snapshot: NocturnalSessionSnapshot,
111
+ principleId: string,
112
+ telemetry: TrinityTelemetry,
113
+ config: TrinityConfig
114
+ ): Promise<TrinityDraftArtifact | null>;
115
+
116
+ /**
117
+ * Clean up any resources used by the adapter.
118
+ * Called after Trinity chain completes (success or failure).
119
+ */
120
+ close?(): Promise<void>;
121
+ }
122
+
123
+ // ---------------------------------------------------------------------------
124
+ // OpenClaw Runtime Adapter
125
+ // ---------------------------------------------------------------------------
126
+
127
+ /**
128
+ * OpenClaw-backed Trinity runtime adapter.
129
+ * Uses ONLY public plugin runtime APIs (api.runtime.subagent.*).
130
+ * Does NOT depend on OpenClaw internals.
131
+ */
132
+ export class OpenClawTrinityRuntimeAdapter implements TrinityRuntimeAdapter {
133
+ private readonly api: {
134
+ runtime: {
135
+ subagent: {
136
+ run: (opts: {
137
+ sessionKey: string;
138
+ message: string;
139
+ extraSystemPrompt?: string;
140
+ deliver?: boolean;
141
+ }) => Promise<{ runId: string }>;
142
+ waitForRun: (opts: { runId: string; timeoutMs: number }) => Promise<{
143
+ status: string;
144
+ error?: string;
145
+ }>;
146
+ getSessionMessages: (opts: {
147
+ sessionKey: string;
148
+ limit: number;
149
+ }) => Promise<{
150
+ messages: unknown[];
151
+ }>;
152
+ deleteSession: (opts: {
153
+ sessionKey: string;
154
+ deleteTranscript?: boolean;
155
+ }) => Promise<void>;
156
+ };
157
+ };
158
+ };
159
+
160
+ private readonly stageTimeoutMs: number;
161
+
162
+ constructor(
163
+ api: OpenClawTrinityRuntimeAdapter['api'],
164
+ stageTimeoutMs = 180_000
165
+ ) {
166
+ this.api = api;
167
+ this.stageTimeoutMs = stageTimeoutMs;
168
+ }
169
+
170
+ async invokeDreamer(
171
+ snapshot: NocturnalSessionSnapshot,
172
+ principleId: string,
173
+ maxCandidates: number
174
+ ): Promise<DreamerOutput> {
175
+ const sessionKey = `agent:main:subagent:ne-dreamer-${randomUUID()}`;
176
+ const systemPrompt = loadRolePrompt('nocturnal-dreamer.md');
177
+
178
+ const prompt = this.buildDreamerPrompt(snapshot, principleId, maxCandidates);
179
+
180
+ try {
181
+ const { runId } = await this.api.runtime.subagent.run({
182
+ sessionKey,
183
+ message: prompt,
184
+ extraSystemPrompt: systemPrompt,
185
+ deliver: false,
186
+ });
187
+
188
+ const result = await this.api.runtime.subagent.waitForRun({
189
+ runId,
190
+ timeoutMs: this.stageTimeoutMs,
191
+ });
192
+
193
+ if (result.status !== 'ok') {
194
+ return {
195
+ valid: false,
196
+ candidates: [],
197
+ reason: `Dreamer subagent failed: ${result.error ?? result.status}`,
198
+ generatedAt: new Date().toISOString(),
199
+ };
200
+ }
201
+
202
+ const messages = await this.api.runtime.subagent.getSessionMessages({
203
+ sessionKey,
204
+ limit: 5,
205
+ });
206
+
207
+ const outputText = this.extractAssistantText(messages.messages as Array<{ role: string; text?: string; content?: string }>);
208
+ return this.parseDreamerOutput(outputText);
209
+ } finally {
210
+ await this.api.runtime.subagent.deleteSession({
211
+ sessionKey,
212
+ deleteTranscript: true,
213
+ }).catch(() => {});
214
+ }
215
+ }
216
+
217
+ async invokePhilosopher(
218
+ dreamerOutput: DreamerOutput,
219
+ principleId: string
220
+ ): Promise<PhilosopherOutput> {
221
+ const sessionKey = `agent:main:subagent:ne-philosopher-${randomUUID()}`;
222
+ const systemPrompt = loadRolePrompt('nocturnal-philosopher.md');
223
+
224
+ const prompt = this.buildPhilosopherPrompt(dreamerOutput, principleId);
225
+
226
+ try {
227
+ const { runId } = await this.api.runtime.subagent.run({
228
+ sessionKey,
229
+ message: prompt,
230
+ extraSystemPrompt: systemPrompt,
231
+ deliver: false,
232
+ });
233
+
234
+ const result = await this.api.runtime.subagent.waitForRun({
235
+ runId,
236
+ timeoutMs: this.stageTimeoutMs,
237
+ });
238
+
239
+ if (result.status !== 'ok') {
240
+ return {
241
+ valid: false,
242
+ judgments: [],
243
+ overallAssessment: '',
244
+ reason: `Philosopher subagent failed: ${result.error ?? result.status}`,
245
+ generatedAt: new Date().toISOString(),
246
+ };
247
+ }
248
+
249
+ const messages = await this.api.runtime.subagent.getSessionMessages({
250
+ sessionKey,
251
+ limit: 5,
252
+ });
253
+
254
+ const outputText = this.extractAssistantText(messages.messages as Array<{ role: string; text?: string; content?: string }>);
255
+ return this.parsePhilosopherOutput(outputText);
256
+ } finally {
257
+ await this.api.runtime.subagent.deleteSession({
258
+ sessionKey,
259
+ deleteTranscript: true,
260
+ }).catch(() => {});
261
+ }
262
+ }
263
+
264
+ async invokeScribe(
265
+ dreamerOutput: DreamerOutput,
266
+ philosopherOutput: PhilosopherOutput,
267
+ snapshot: NocturnalSessionSnapshot,
268
+ principleId: string,
269
+ telemetry: TrinityTelemetry,
270
+ config: TrinityConfig
271
+ ): Promise<TrinityDraftArtifact | null> {
272
+ const sessionKey = `agent:main:subagent:ne-scribe-${randomUUID()}`;
273
+ const systemPrompt = loadRolePrompt('nocturnal-scribe.md');
274
+
275
+ const prompt = this.buildScribePrompt(dreamerOutput, philosopherOutput, snapshot, principleId);
276
+
277
+ try {
278
+ const { runId } = await this.api.runtime.subagent.run({
279
+ sessionKey,
280
+ message: prompt,
281
+ extraSystemPrompt: systemPrompt,
282
+ deliver: false,
283
+ });
284
+
285
+ const result = await this.api.runtime.subagent.waitForRun({
286
+ runId,
287
+ timeoutMs: this.stageTimeoutMs,
288
+ });
289
+
290
+ if (result.status !== 'ok') {
291
+ return null;
292
+ }
293
+
294
+ const messages = await this.api.runtime.subagent.getSessionMessages({
295
+ sessionKey,
296
+ limit: 5,
297
+ });
298
+
299
+ const outputText = this.extractAssistantText(messages.messages as Array<{ role: string; text?: string; content?: string }>);
300
+ return this.parseScribeOutput(outputText, snapshot, principleId, telemetry);
301
+ } finally {
302
+ await this.api.runtime.subagent.deleteSession({
303
+ sessionKey,
304
+ deleteTranscript: true,
305
+ }).catch(() => {});
306
+ }
307
+ }
308
+
309
+ async close(): Promise<void> {
310
+ // Nothing to clean up in this implementation
311
+ }
312
+
313
+ // ---------------------------------------------------------------------------
314
+ // Private Helper Methods
315
+ // ---------------------------------------------------------------------------
316
+
317
+ private extractAssistantText(
318
+ messages: Array<{ role: string; text?: string; content?: string }>
319
+ ): string {
320
+ for (let i = messages.length - 1; i >= 0; i--) {
321
+ const msg = messages[i] as { role: string; text?: string; content?: string };
322
+ if (msg.role === 'assistant') {
323
+ return msg.text ?? msg.content ?? '';
324
+ }
325
+ }
326
+ return '';
327
+ }
328
+
329
+ private buildDreamerPrompt(
330
+ snapshot: NocturnalSessionSnapshot,
331
+ principleId: string,
332
+ maxCandidates: number
333
+ ): string {
334
+ return `Target Principle: ${principleId}
335
+
336
+ Session Snapshot:
337
+ - Session ID: ${snapshot.sessionId}
338
+ - Assistant Turns: ${snapshot.stats.totalAssistantTurns}
339
+ - Tool Calls: ${snapshot.stats.totalToolCalls}
340
+ - Failures: ${snapshot.stats.failureCount}
341
+ - Pain Events: ${snapshot.stats.totalPainEvents}
342
+ - Gate Blocks: ${snapshot.stats.totalGateBlocks}
343
+
344
+ Please analyze this session and generate ${maxCandidates} candidate corrections. Each candidate should identify a bad decision and propose a better alternative grounded in the target principle.
345
+
346
+ Respond with ONLY a valid JSON object matching the DreamerOutput contract.`;
347
+ }
348
+
349
+ private buildPhilosopherPrompt(
350
+ dreamerOutput: DreamerOutput,
351
+ principleId: string
352
+ ): string {
353
+ const candidatesJson = JSON.stringify(dreamerOutput.candidates, null, 2);
354
+ return `Target Principle: ${principleId}
355
+
356
+ Dreamer's Candidates:
357
+ ${candidatesJson}
358
+
359
+ Please evaluate each candidate and rank them by principle alignment, specificity, and actionability. Respond with ONLY a valid JSON object matching the PhilosopherOutput contract.`;
360
+ }
361
+
362
+ private buildScribePrompt(
363
+ dreamerOutput: DreamerOutput,
364
+ philosopherOutput: PhilosopherOutput,
365
+ snapshot: NocturnalSessionSnapshot,
366
+ principleId: string
367
+ ): string {
368
+ const candidatesJson = JSON.stringify(dreamerOutput.candidates, null, 2);
369
+ const judgmentsJson = JSON.stringify(philosopherOutput.judgments, null, 2);
370
+ return `Target Principle: ${principleId}
371
+ Session ID: ${snapshot.sessionId}
372
+
373
+ Dreamer's Candidates:
374
+ ${candidatesJson}
375
+
376
+ Philosopher's Judgments:
377
+ ${judgmentsJson}
378
+
379
+ Select the best candidate (Philosopher's rank 1) and synthesize it into a final TrinityDraftArtifact. Respond with ONLY a valid JSON object.`;
380
+ }
381
+
382
+ private parseDreamerOutput(text: string): DreamerOutput {
383
+ const json = this.extractJson(text);
384
+ if (!json) {
385
+ return {
386
+ valid: false,
387
+ candidates: [],
388
+ reason: 'Failed to parse Dreamer output as JSON',
389
+ generatedAt: new Date().toISOString(),
390
+ };
391
+ }
392
+
393
+ try {
394
+ const parsed = JSON.parse(json);
395
+ // Validate required structure
396
+ if (typeof parsed.valid !== 'boolean') {
397
+ return {
398
+ valid: false,
399
+ candidates: [],
400
+ reason: 'Dreamer output missing "valid" field',
401
+ generatedAt: new Date().toISOString(),
402
+ };
403
+ }
404
+ if (!Array.isArray(parsed.candidates)) {
405
+ return {
406
+ valid: false,
407
+ candidates: [],
408
+ reason: 'Dreamer output missing "candidates" array',
409
+ generatedAt: new Date().toISOString(),
410
+ };
411
+ }
412
+ return {
413
+ valid: parsed.valid,
414
+ candidates: parsed.candidates,
415
+ reason: parsed.reason,
416
+ generatedAt: parsed.generatedAt ?? new Date().toISOString(),
417
+ };
418
+ } catch {
419
+ return {
420
+ valid: false,
421
+ candidates: [],
422
+ reason: `JSON parse error: ${text.slice(0, 100)}`,
423
+ generatedAt: new Date().toISOString(),
424
+ };
425
+ }
426
+ }
427
+
428
+ private parsePhilosopherOutput(text: string): PhilosopherOutput {
429
+ const json = this.extractJson(text);
430
+ if (!json) {
431
+ return {
432
+ valid: false,
433
+ judgments: [],
434
+ overallAssessment: '',
435
+ reason: 'Failed to parse Philosopher output as JSON',
436
+ generatedAt: new Date().toISOString(),
437
+ };
438
+ }
439
+
440
+ try {
441
+ const parsed = JSON.parse(json);
442
+ if (typeof parsed.valid !== 'boolean') {
443
+ return {
444
+ valid: false,
445
+ judgments: [],
446
+ overallAssessment: '',
447
+ reason: 'Philosopher output missing "valid" field',
448
+ generatedAt: new Date().toISOString(),
449
+ };
450
+ }
451
+ if (!Array.isArray(parsed.judgments)) {
452
+ return {
453
+ valid: false,
454
+ judgments: [],
455
+ overallAssessment: '',
456
+ reason: 'Philosopher output missing "judgments" array',
457
+ generatedAt: new Date().toISOString(),
458
+ };
459
+ }
460
+ return {
461
+ valid: parsed.valid,
462
+ judgments: parsed.judgments,
463
+ overallAssessment: parsed.overallAssessment ?? '',
464
+ reason: parsed.reason,
465
+ generatedAt: parsed.generatedAt ?? new Date().toISOString(),
466
+ };
467
+ } catch {
468
+ return {
469
+ valid: false,
470
+ judgments: [],
471
+ overallAssessment: '',
472
+ reason: `JSON parse error: ${text.slice(0, 100)}`,
473
+ generatedAt: new Date().toISOString(),
474
+ };
475
+ }
476
+ }
477
+
478
+ private parseScribeOutput(
479
+ text: string,
480
+ snapshot: NocturnalSessionSnapshot,
481
+ principleId: string,
482
+ telemetry: TrinityTelemetry
483
+ ): TrinityDraftArtifact | null {
484
+ const json = this.extractJson(text);
485
+ if (!json) {
486
+ return null;
487
+ }
488
+
489
+ try {
490
+ const parsed = JSON.parse(json);
491
+ if (typeof parsed.selectedCandidateIndex !== 'number') {
492
+ return null;
493
+ }
494
+
495
+ return {
496
+ selectedCandidateIndex: parsed.selectedCandidateIndex,
497
+ badDecision: parsed.badDecision ?? '',
498
+ betterDecision: parsed.betterDecision ?? '',
499
+ rationale: parsed.rationale ?? '',
500
+ sessionId: snapshot.sessionId,
501
+ principleId,
502
+ sourceSnapshotRef: `snapshot-${snapshot.sessionId}-${Date.now()}`,
503
+ telemetry: {
504
+ chainMode: 'trinity',
505
+ usedStubs: false,
506
+ dreamerPassed: true,
507
+ philosopherPassed: true,
508
+ scribePassed: true,
509
+ candidateCount: parsed.candidateCount ?? 0,
510
+ selectedCandidateIndex: parsed.selectedCandidateIndex,
511
+ stageFailures: [],
512
+ },
513
+ };
514
+ } catch {
515
+ return null;
516
+ }
517
+ }
518
+
519
+ /**
520
+ * Extract JSON object from text that may contain markdown code blocks.
521
+ */
522
+ private extractJson(text: string): string | null {
523
+ // Try direct parse first
524
+ try {
525
+ JSON.parse(text);
526
+ return text;
527
+ } catch {
528
+ // Try extracting from markdown code blocks
529
+ }
530
+
531
+ // Match triple-backtick JSON blocks
532
+ const codeBlockMatch = text.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
533
+ if (codeBlockMatch) {
534
+ const extracted = codeBlockMatch[1].trim();
535
+ try {
536
+ JSON.parse(extracted);
537
+ return extracted;
538
+ } catch {
539
+ // Not valid JSON
540
+ }
541
+ }
542
+
543
+ // Try to find first { and last } to extract JSON object
544
+ const firstBrace = text.indexOf('{');
545
+ const lastBrace = text.lastIndexOf('}');
546
+ if (firstBrace !== -1 && lastBrace !== -1 && lastBrace > firstBrace) {
547
+ const extracted = text.slice(firstBrace, lastBrace + 1);
548
+ try {
549
+ JSON.parse(extracted);
550
+ return extracted;
551
+ } catch {
552
+ // Not valid JSON
553
+ }
554
+ }
555
+
556
+ return null;
557
+ }
558
+ }
559
+
560
+ // ---------------------------------------------------------------------------
561
+ // Trinity Mode Configuration
562
+ // ---------------------------------------------------------------------------
563
+
564
+ /**
565
+ * Configuration for Trinity chain execution.
566
+ */
567
+ export interface TrinityConfig {
568
+ /**
569
+ * Whether to use Trinity chain (true) or single-reflector (false).
570
+ * Default: true
571
+ */
572
+ useTrinity: boolean;
573
+
574
+ /**
575
+ * Maximum candidates Dreamer should generate.
576
+ * Default: 3
577
+ */
578
+ maxCandidates: number;
579
+
580
+ /**
581
+ * Whether to use stub stage outputs (for testing without real model calls).
582
+ * Default: false (real subagent calls via runtimeAdapter)
583
+ */
584
+ useStubs: boolean;
585
+
586
+ /**
587
+ * Runtime adapter for real subagent execution.
588
+ * Required when useStubs is false. Ignored when useStubs is true.
589
+ * Default: undefined
590
+ */
591
+ runtimeAdapter?: TrinityRuntimeAdapter;
592
+
593
+ /**
594
+ * Scoring weights for tournament selection.
595
+ * Default: DEFAULT_SCORING_WEIGHTS
596
+ */
597
+ scoringWeights?: ScoringWeights;
598
+
599
+ /**
600
+ * Threshold values for tournament eligibility.
601
+ * Default: DEFAULT_THRESHOLDS
602
+ */
603
+ thresholds?: ThresholdValues;
604
+
605
+ /**
606
+ * State directory for threshold persistence.
607
+ * If provided, thresholds will be loaded from state.
608
+ */
609
+ stateDir?: string;
610
+ }
611
+
612
+ // ---------------------------------------------------------------------------
613
+ // Trinity Intermediate Contracts
614
+ // ---------------------------------------------------------------------------
615
+
616
+ /**
617
+ * Dreamer output — multiple candidate corrections.
618
+ * Each candidate represents an alternative "better decision" approach.
619
+ */
620
+ export interface DreamerCandidate {
621
+ /** Unique index for this candidate within the Dreamer output */
622
+ candidateIndex: number;
623
+ /** The bad decision this candidate addresses */
624
+ badDecision: string;
625
+ /** The alternative/better decision */
626
+ betterDecision: string;
627
+ /** Why this alternative is better (brief) */
628
+ rationale: string;
629
+ /** Confidence that this candidate is valid (0-1) */
630
+ confidence: number;
631
+ }
632
+
633
+ export interface DreamerOutput {
634
+ /** Whether Dreamer succeeded */
635
+ valid: boolean;
636
+ /** List of candidate corrections */
637
+ candidates: DreamerCandidate[];
638
+ /** Why Dreamer could not generate (if valid === false) */
639
+ reason?: string;
640
+ /** Timestamp of generation */
641
+ generatedAt: string;
642
+ }
643
+
644
+ /**
645
+ * Philosopher output — principle-grounded critique and ranking.
646
+ * Philosopher evaluates Dreamer's candidates and ranks them.
647
+ */
648
+ export interface PhilosopherJudgment {
649
+ /** Index of the judged candidate (references DreamerCandidate.candidateIndex) */
650
+ candidateIndex: number;
651
+ /** Principle-grounded critique of this candidate */
652
+ critique: string;
653
+ /** Whether this candidate aligns with the target principle */
654
+ principleAligned: boolean;
655
+ /** Ranking score (higher = better, 0-1) */
656
+ score: number;
657
+ /** Rank among all candidates (1 = best) */
658
+ rank: number;
659
+ }
660
+
661
+ export interface PhilosopherOutput {
662
+ /** Whether Philosopher succeeded */
663
+ valid: boolean;
664
+ /** Judgments for each candidate */
665
+ judgments: PhilosopherJudgment[];
666
+ /** Overall assessment of the candidate set */
667
+ overallAssessment: string;
668
+ /** Why Philosopher could not judge (if valid === false) */
669
+ reason?: string;
670
+ /** Timestamp of generation */
671
+ generatedAt: string;
672
+ }
673
+
674
+ /**
675
+ * Scribe output — final structured artifact draft.
676
+ * Scribe synthesizes the best candidate into an approved artifact format.
677
+ */
678
+ export interface TrinityDraftArtifact {
679
+ /** The selected winning candidate index */
680
+ selectedCandidateIndex: number;
681
+ /** The final badDecision */
682
+ badDecision: string;
683
+ /** The final betterDecision */
684
+ betterDecision: string;
685
+ /** The final rationale */
686
+ rationale: string;
687
+ /** Source session from snapshot */
688
+ sessionId: string;
689
+ /** Target principle ID */
690
+ principleId: string;
691
+ /** Reference to snapshot used */
692
+ sourceSnapshotRef: string;
693
+ /** Chain telemetry */
694
+ telemetry: TrinityTelemetry;
695
+ /** Reflection quality: delta in thinking model activation (-1 to 1) */
696
+ thinkingModelDelta?: number;
697
+ /** Reflection quality: gain in planning ratio (-1 to 1) */
698
+ planningRatioGain?: number;
699
+ }
700
+
701
+ export interface TrinityTelemetry {
702
+ /** Whether Trinity or single-reflector was used */
703
+ chainMode: 'trinity' | 'single-reflector';
704
+ /** Whether stub implementations were used (always true in Phase 8) */
705
+ usedStubs: boolean;
706
+ /** Whether each stage passed */
707
+ dreamerPassed: boolean;
708
+ philosopherPassed: boolean;
709
+ scribePassed: boolean;
710
+ /** Number of candidates generated */
711
+ candidateCount: number;
712
+ /** Final selected candidate index */
713
+ selectedCandidateIndex: number;
714
+ /** Stage failure reasons (if any) */
715
+ stageFailures: string[];
716
+ /** Tournament trace for explainability (optional) */
717
+ tournamentTrace?: TournamentTraceEntry[];
718
+ /** Winner aggregate score (optional) */
719
+ winnerAggregateScore?: number;
720
+ /** Whether winner passed all thresholds (optional) */
721
+ winnerThresholdPassed?: boolean;
722
+ /** Number of eligible candidates after threshold check (optional) */
723
+ eligibleCandidateCount?: number;
724
+ }
725
+
726
+ // ---------------------------------------------------------------------------
727
+ // Trinity Stage Validation
728
+ // ---------------------------------------------------------------------------
729
+
730
+ /**
731
+ * Validation failure for a Trinity stage.
732
+ */
733
+ export interface TrinityStageFailure {
734
+ stage: 'dreamer' | 'philosopher' | 'scribe';
735
+ reason: string;
736
+ }
737
+
738
+ /**
739
+ * Result of Trinity chain execution.
740
+ */
741
+ export interface TrinityResult {
742
+ /** Whether Trinity chain completed successfully */
743
+ success: boolean;
744
+ /** The final draft artifact (if success) */
745
+ artifact?: TrinityDraftArtifact;
746
+ /** Telemetry about the chain execution */
747
+ telemetry: TrinityTelemetry;
748
+ /** Stage failures (if any) */
749
+ failures: TrinityStageFailure[];
750
+ /** Whether fallback to single-reflector occurred */
751
+ fallbackOccurred: boolean;
752
+ }
753
+
754
+ // ---------------------------------------------------------------------------
755
+ // Internal Types for Trinity Execution
756
+ // ---------------------------------------------------------------------------
757
+
758
+ /**
759
+ * Enriched candidate after Philosopher judgment.
760
+ */
761
+ interface EnrichedCandidate extends DreamerCandidate {
762
+ judgment: PhilosopherJudgment;
763
+ }
764
+
765
+ // ---------------------------------------------------------------------------
766
+ // Stub Stage Implementations (Phase 2 — no real subagent calls)
767
+ // ---------------------------------------------------------------------------
768
+
769
+ /**
770
+ * STUB DREAMER — generates synthetic candidates for testing.
771
+ *
772
+ * In production, this would call the actual Dreamer subagent.
773
+ * The stub generates plausible candidates based on snapshot signals.
774
+ */
775
+ export function invokeStubDreamer(
776
+ snapshot: NocturnalSessionSnapshot,
777
+ principleId: string,
778
+ maxCandidates: number
779
+ ): DreamerOutput {
780
+ const hasFailures = snapshot.stats.failureCount > 0;
781
+ const hasPain = snapshot.stats.totalPainEvents > 0;
782
+ const hasGateBlocks = snapshot.stats.totalGateBlocks > 0;
783
+
784
+ const candidates: DreamerCandidate[] = [];
785
+
786
+ // Generate candidates based on available signals
787
+ // NOTE: betterDecision includes thinking model patterns so computeThinkingModelDelta > 0
788
+ // (these activate T-03, T-05, T-08 patterns respectively)
789
+ if (hasGateBlocks) {
790
+ candidates.push({
791
+ candidateIndex: 0,
792
+ badDecision: 'Proceeded with a tool call despite receiving a gate block, bypassing the safety check',
793
+ betterDecision: 'Review docs/gateblocks.md and verify authorization requirements first; based on the evidence, this irreversible action must be reviewed before proceeding',
794
+ rationale: 'Respecting gate blocks prevents unintended system modifications',
795
+ confidence: 0.95,
796
+ });
797
+ if (maxCandidates >= 2) {
798
+ candidates.push({
799
+ candidateIndex: 1,
800
+ badDecision: 'Retried the same operation immediately after gate block without understanding why',
801
+ betterDecision: 'Check the gatekeeper source first to diagnose the block reason; this is irreversible, so we must be certain before proceeding',
802
+ rationale: 'Understanding why a gate blocked prevents repeated blocks',
803
+ confidence: 0.85,
804
+ });
805
+ }
806
+ if (maxCandidates >= 3) {
807
+ candidates.push({
808
+ candidateIndex: 2,
809
+ badDecision: 'Modified the target of the blocked operation to bypass the check',
810
+ betterDecision: 'Review docs/auth.md first to understand the authorization structure, then request proper review before any change',
811
+ rationale: 'Proper authorization ensures accountability and prevents unintended changes',
812
+ confidence: 0.75,
813
+ });
814
+ }
815
+ } else if (hasPain) {
816
+ candidates.push({
817
+ candidateIndex: 0,
818
+ badDecision: 'Continued executing operations without pausing to address accumulated pain signals',
819
+ betterDecision: 'Check logs/pain.json first to analyze pain signals; this error indicates we should stop and reconsider before proceeding',
820
+ rationale: 'Pain signals indicate accumulated friction or error conditions',
821
+ confidence: 0.90,
822
+ });
823
+ if (maxCandidates >= 2) {
824
+ candidates.push({
825
+ candidateIndex: 1,
826
+ badDecision: 'Ignored warning pain events and proceeded with high-risk operations',
827
+ betterDecision: 'Review src/pain-detector.ts first; based on the evidence, this indicates a deeper issue we must not ignore',
828
+ rationale: 'Addressing friction reduces error rates and improves outcomes',
829
+ confidence: 0.80,
830
+ });
831
+ }
832
+ if (maxCandidates >= 3) {
833
+ candidates.push({
834
+ candidateIndex: 2,
835
+ badDecision: 'Retried failing operations without analyzing why they caused pain',
836
+ betterDecision: 'Analyze logs/errors.json first to identify the failure pattern; this suggests we should stop and rethink before retrying',
837
+ rationale: 'Pattern analysis prevents recurring pain from the same source',
838
+ confidence: 0.70,
839
+ });
840
+ }
841
+ } else if (hasFailures) {
842
+ candidates.push({
843
+ candidateIndex: 0,
844
+ badDecision: 'Retried a failing operation without diagnosing the root cause',
845
+ betterDecision: 'Verify config.json preconditions first, based on the error in logs/failure.json, before retrying',
846
+ rationale: 'Diagnosing failures before retry prevents repeated failures',
847
+ confidence: 0.92,
848
+ });
849
+ if (maxCandidates >= 2) {
850
+ candidates.push({
851
+ candidateIndex: 1,
852
+ badDecision: 'Continued to the next operation after a failure without addressing it',
853
+ betterDecision: 'Check docs/debugging.md first to diagnose what failed; we must not ignore this when the action is irreversible',
854
+ rationale: 'Unaddressed failures compound and cause larger issues',
855
+ confidence: 0.85,
856
+ });
857
+ }
858
+ if (maxCandidates >= 3) {
859
+ candidates.push({
860
+ candidateIndex: 2,
861
+ badDecision: 'Assumed the failure was transient and retried without investigation',
862
+ betterDecision: 'Verify src/validator.ts state first; this error indicates a deeper problem before assuming resolution',
863
+ rationale: 'Verification prevents cascading failures from unresolved issues',
864
+ confidence: 0.78,
865
+ });
866
+ }
867
+ } else {
868
+ // No signal available - cannot generate meaningful candidates
869
+ // Return empty candidates array to trigger invalid output
870
+ // (Real Dreamer would also fail with no signal)
871
+ return {
872
+ valid: false,
873
+ candidates: [],
874
+ reason: 'No signal available for candidate generation (failureCount=0, painEvents=0, gateBlocks=0)',
875
+ generatedAt: new Date().toISOString(),
876
+ };
877
+ }
878
+
879
+ // Ensure we don't exceed maxCandidates
880
+ const limitedCandidates = candidates.slice(0, Math.min(candidates.length, maxCandidates));
881
+
882
+ return {
883
+ valid: limitedCandidates.length > 0,
884
+ candidates: limitedCandidates,
885
+ generatedAt: new Date().toISOString(),
886
+ reason: limitedCandidates.length === 0 ? 'No signal available for candidate generation' : undefined,
887
+ };
888
+ }
889
+
890
+ /**
891
+ * STUB PHILOSOPHER — ranks candidates based on simple heuristics.
892
+ *
893
+ * In production, this would call the actual Philosopher subagent.
894
+ * The stub applies principle alignment heuristics.
895
+ */
896
+ export function invokeStubPhilosopher(
897
+ dreamerOutput: DreamerOutput,
898
+ principleId: string
899
+ ): PhilosopherOutput {
900
+ if (!dreamerOutput.valid || dreamerOutput.candidates.length === 0) {
901
+ return {
902
+ valid: false,
903
+ judgments: [],
904
+ overallAssessment: '',
905
+ reason: 'No candidates to judge',
906
+ generatedAt: new Date().toISOString(),
907
+ };
908
+ }
909
+
910
+ // Simple heuristic scoring based on candidate structure
911
+ const judgments: PhilosopherJudgment[] = dreamerOutput.candidates.map((candidate) => {
912
+ let principleAligned = true;
913
+ let score = candidate.confidence;
914
+
915
+ // Heuristic: longer rationales tend to be more principled
916
+ if (candidate.rationale.length < 30) {
917
+ score *= 0.8;
918
+ principleAligned = false;
919
+ }
920
+
921
+ // Heuristic: betterDecision should be actionable (contain verbs)
922
+ const actionableVerbs = ['read', 'check', 'verify', 'edit', 'write', 'search', 'review', 'analyze'];
923
+ const hasActionable = actionableVerbs.some((v) => candidate.betterDecision.toLowerCase().includes(v));
924
+ if (!hasActionable) {
925
+ score *= 0.85;
926
+ principleAligned = false;
927
+ }
928
+
929
+ // Heuristic: badDecision should be specific (not generic)
930
+ const genericPatterns = ['something went wrong', 'it did not work', 'mistake was made'];
931
+ const isGeneric = genericPatterns.some((p) => candidate.badDecision.toLowerCase().includes(p));
932
+ if (isGeneric) {
933
+ score *= 0.75;
934
+ principleAligned = false;
935
+ }
936
+
937
+ return {
938
+ candidateIndex: candidate.candidateIndex,
939
+ critique: `Candidate ${candidate.candidateIndex} scored ${score.toFixed(2)}. ${
940
+ principleAligned
941
+ ? 'Principle-aligned with specific actionable alternative.'
942
+ : 'May need refinement for principle alignment.'
943
+ }`,
944
+ principleAligned,
945
+ score: Math.min(1, Math.max(0, score)),
946
+ rank: 0, // Will be set after sorting
947
+ };
948
+ });
949
+
950
+ // Sort by score descending and assign ranks
951
+ judgments.sort((a, b) => b.score - a.score);
952
+ judgments.forEach((j, idx) => {
953
+ j.rank = idx + 1;
954
+ });
955
+
956
+ const topJudgment = judgments[0];
957
+
958
+ return {
959
+ valid: true,
960
+ judgments,
961
+ overallAssessment: `Best candidate is #${topJudgment.candidateIndex} with score ${topJudgment.score.toFixed(2)}. ${topJudgment.principleAligned ? 'Well-aligned with principle.' : 'Alignment could be improved.'}`,
962
+ generatedAt: new Date().toISOString(),
963
+ };
964
+ }
965
+
966
+ /**
967
+ * STUB SCRIBE — synthesizes best candidate into final artifact using tournament selection.
968
+ *
969
+ * In production, this would call the actual Scribe subagent.
970
+ * The stub uses tournament selection (scoring + thresholds) to pick the winner.
971
+ */
972
+ export function invokeStubScribe(
973
+ dreamerOutput: DreamerOutput,
974
+ philosopherOutput: PhilosopherOutput,
975
+ snapshot: NocturnalSessionSnapshot,
976
+ principleId: string,
977
+ telemetry: TrinityTelemetry,
978
+ config: TrinityConfig
979
+ ): TrinityDraftArtifact | null {
980
+ if (!dreamerOutput.valid || !philosopherOutput.valid) {
981
+ return null;
982
+ }
983
+
984
+ // Get thresholds (from config or state, or defaults)
985
+ const thresholds = config.thresholds ?? (config.stateDir ? getEffectiveThresholds(config.stateDir) : { ...DEFAULT_THRESHOLDS });
986
+ const weights = config.scoringWeights ?? DEFAULT_SCORING_WEIGHTS;
987
+
988
+ // Run tournament selection
989
+ const tournamentResult = runTournament(
990
+ dreamerOutput.candidates,
991
+ philosopherOutput.judgments,
992
+ thresholds,
993
+ weights
994
+ );
995
+
996
+ if (!tournamentResult.success || !tournamentResult.winner) {
997
+ // Tournament failed — no eligible candidate
998
+ return null;
999
+ }
1000
+
1001
+ const winner = tournamentResult.winner;
1002
+
1003
+ // Update telemetry with tournament info
1004
+ const updatedTelemetry: TrinityTelemetry = {
1005
+ ...telemetry,
1006
+ tournamentTrace: tournamentResult.trace,
1007
+ winnerAggregateScore: winner.scores.aggregate,
1008
+ winnerThresholdPassed: winner.thresholdPassed,
1009
+ eligibleCandidateCount: tournamentResult.rankedCandidates.filter((c) => c.thresholdPassed).length,
1010
+ };
1011
+
1012
+ return {
1013
+ selectedCandidateIndex: winner.candidateIndex,
1014
+ badDecision: winner.candidate.badDecision,
1015
+ betterDecision: winner.candidate.betterDecision,
1016
+ rationale: winner.candidate.rationale,
1017
+ sessionId: snapshot.sessionId,
1018
+ principleId,
1019
+ sourceSnapshotRef: `snapshot-${snapshot.sessionId}-${Date.now()}`,
1020
+ telemetry: updatedTelemetry,
1021
+ };
1022
+ }
1023
+
1024
+ // ---------------------------------------------------------------------------
1025
+ // Trinity Chain Execution
1026
+ // ---------------------------------------------------------------------------
1027
+
1028
+ export interface RunTrinityOptions {
1029
+ /** Snapshot to generate candidates from */
1030
+ snapshot: NocturnalSessionSnapshot;
1031
+ /** Target principle ID */
1032
+ principleId: string;
1033
+ /** Trinity configuration */
1034
+ config: TrinityConfig;
1035
+ }
1036
+
1037
+ /**
1038
+ * Execute the Trinity chain using stubs (synchronous).
1039
+ * Use runTrinityAsync for real subagent execution via runtime adapter.
1040
+ *
1041
+ * @param options - Trinity execution options
1042
+ * @returns TrinityResult with final artifact or failure info
1043
+ */
1044
+ export function runTrinity(options: RunTrinityOptions): TrinityResult {
1045
+ const { snapshot, principleId, config } = options;
1046
+
1047
+ // Stub path: use synchronous stub implementations
1048
+ if (config.useStubs) {
1049
+ return runTrinityWithStubs(snapshot, principleId, config);
1050
+ }
1051
+
1052
+ // Real execution path: requires runtimeAdapter
1053
+ // This is handled asynchronously in runTrinityAsync
1054
+ const errorMsg = '[Trinity] useStubs=false requires a runtimeAdapter. Use runTrinityAsync for real subagent execution.';
1055
+ const failures: TrinityStageFailure[] = [{ stage: 'dreamer', reason: errorMsg }];
1056
+ const telemetry: TrinityTelemetry = {
1057
+ chainMode: 'trinity',
1058
+ usedStubs: false,
1059
+ dreamerPassed: false,
1060
+ philosopherPassed: false,
1061
+ scribePassed: false,
1062
+ candidateCount: 0,
1063
+ selectedCandidateIndex: -1,
1064
+ stageFailures: [`Configuration: ${errorMsg}`],
1065
+ };
1066
+ console.error(`[Trinity] ERROR: ${errorMsg}`);
1067
+ return {
1068
+ success: false,
1069
+ telemetry,
1070
+ failures,
1071
+ fallbackOccurred: false,
1072
+ };
1073
+ }
1074
+
1075
+ /**
1076
+ * Execute the Trinity chain with real subagent runtime (asynchronous).
1077
+ * Requires config.runtimeAdapter to be set.
1078
+ *
1079
+ * @param options - Trinity execution options
1080
+ * @returns Promise<TrinityResult> with final artifact or failure info
1081
+ */
1082
+ export async function runTrinityAsync(options: RunTrinityOptions): Promise<TrinityResult> {
1083
+ const { snapshot, principleId, config } = options;
1084
+
1085
+ if (config.useStubs) {
1086
+ // Stub path: use synchronous stubs
1087
+ return runTrinityWithStubs(snapshot, principleId, config);
1088
+ }
1089
+
1090
+ if (!config.runtimeAdapter) {
1091
+ const errorMsg = '[Trinity] useStubs=false requires config.runtimeAdapter to be set.';
1092
+ const failures: TrinityStageFailure[] = [{ stage: 'dreamer', reason: errorMsg }];
1093
+ const telemetry: TrinityTelemetry = {
1094
+ chainMode: 'trinity',
1095
+ usedStubs: false,
1096
+ dreamerPassed: false,
1097
+ philosopherPassed: false,
1098
+ scribePassed: false,
1099
+ candidateCount: 0,
1100
+ selectedCandidateIndex: -1,
1101
+ stageFailures: [`Configuration: ${errorMsg}`],
1102
+ };
1103
+ console.error(`[Trinity] ERROR: ${errorMsg}`);
1104
+ return {
1105
+ success: false,
1106
+ telemetry,
1107
+ failures,
1108
+ fallbackOccurred: false,
1109
+ };
1110
+ }
1111
+
1112
+ const adapter = config.runtimeAdapter;
1113
+ const telemetry: TrinityTelemetry = {
1114
+ chainMode: 'trinity',
1115
+ usedStubs: false,
1116
+ dreamerPassed: false,
1117
+ philosopherPassed: false,
1118
+ scribePassed: false,
1119
+ candidateCount: 0,
1120
+ selectedCandidateIndex: -1,
1121
+ stageFailures: [],
1122
+ };
1123
+
1124
+ const failures: TrinityStageFailure[] = [];
1125
+
1126
+ try {
1127
+ // Step 1: Dreamer — generate candidates via real subagent
1128
+ const dreamerOutput = await adapter.invokeDreamer(snapshot, principleId, config.maxCandidates);
1129
+
1130
+ if (!dreamerOutput.valid || dreamerOutput.candidates.length === 0) {
1131
+ failures.push({
1132
+ stage: 'dreamer',
1133
+ reason: dreamerOutput.reason ?? 'No valid candidates generated',
1134
+ });
1135
+ telemetry.stageFailures.push(`Dreamer: ${dreamerOutput.reason ?? 'failed'}`);
1136
+ return { success: false, telemetry, failures, fallbackOccurred: false };
1137
+ }
1138
+
1139
+ telemetry.dreamerPassed = true;
1140
+ telemetry.candidateCount = dreamerOutput.candidates.length;
1141
+
1142
+ // Step 2: Philosopher — rank candidates via real subagent
1143
+ const philosopherOutput = await adapter.invokePhilosopher(dreamerOutput, principleId);
1144
+
1145
+ if (!philosopherOutput.valid || philosopherOutput.judgments.length === 0) {
1146
+ failures.push({
1147
+ stage: 'philosopher',
1148
+ reason: philosopherOutput.reason ?? 'No judgments produced',
1149
+ });
1150
+ telemetry.stageFailures.push(`Philosopher: ${philosopherOutput.reason ?? 'failed'}`);
1151
+ return { success: false, telemetry, failures, fallbackOccurred: false };
1152
+ }
1153
+
1154
+ telemetry.philosopherPassed = true;
1155
+
1156
+ // Step 3: Scribe — synthesize final artifact via real subagent
1157
+ const draftArtifact = await adapter.invokeScribe(
1158
+ dreamerOutput,
1159
+ philosopherOutput,
1160
+ snapshot,
1161
+ principleId,
1162
+ telemetry,
1163
+ config
1164
+ );
1165
+
1166
+ if (!draftArtifact) {
1167
+ failures.push({ stage: 'scribe', reason: 'Failed to synthesize artifact from candidates' });
1168
+ telemetry.stageFailures.push('Scribe: synthesis failed');
1169
+ return { success: false, telemetry, failures, fallbackOccurred: false };
1170
+ }
1171
+
1172
+ telemetry.scribePassed = true;
1173
+ telemetry.selectedCandidateIndex = draftArtifact.selectedCandidateIndex;
1174
+
1175
+ if (draftArtifact.telemetry) {
1176
+ telemetry.tournamentTrace = draftArtifact.telemetry.tournamentTrace;
1177
+ telemetry.winnerAggregateScore = draftArtifact.telemetry.winnerAggregateScore;
1178
+ telemetry.winnerThresholdPassed = draftArtifact.telemetry.winnerThresholdPassed;
1179
+ telemetry.eligibleCandidateCount = draftArtifact.telemetry.eligibleCandidateCount;
1180
+ }
1181
+
1182
+ return { success: true, artifact: draftArtifact, telemetry, failures: [], fallbackOccurred: false };
1183
+ } finally {
1184
+ if (adapter.close) {
1185
+ await adapter.close().catch(() => {});
1186
+ }
1187
+ }
1188
+ }
1189
+
1190
+ /**
1191
+ * Internal: Run Trinity chain with stub implementations (synchronous).
1192
+ */
1193
+ function runTrinityWithStubs(
1194
+ snapshot: NocturnalSessionSnapshot,
1195
+ principleId: string,
1196
+ config: TrinityConfig
1197
+ ): TrinityResult {
1198
+ const telemetry: TrinityTelemetry = {
1199
+ chainMode: 'trinity',
1200
+ usedStubs: true,
1201
+ dreamerPassed: false,
1202
+ philosopherPassed: false,
1203
+ scribePassed: false,
1204
+ candidateCount: 0,
1205
+ selectedCandidateIndex: -1,
1206
+ stageFailures: [],
1207
+ };
1208
+
1209
+ const failures: TrinityStageFailure[] = [];
1210
+
1211
+ // Step 1: Dreamer — generate candidates (stub)
1212
+ const dreamerOutput = invokeStubDreamer(snapshot, principleId, config.maxCandidates);
1213
+
1214
+ if (!dreamerOutput.valid || dreamerOutput.candidates.length === 0) {
1215
+ failures.push({
1216
+ stage: 'dreamer',
1217
+ reason: dreamerOutput.reason ?? 'No valid candidates generated',
1218
+ });
1219
+ telemetry.stageFailures.push(`Dreamer: ${dreamerOutput.reason ?? 'failed'}`);
1220
+ return {
1221
+ success: false,
1222
+ telemetry,
1223
+ failures,
1224
+ fallbackOccurred: false,
1225
+ };
1226
+ }
1227
+
1228
+ telemetry.dreamerPassed = true;
1229
+ telemetry.candidateCount = dreamerOutput.candidates.length;
1230
+
1231
+ // Step 2: Philosopher — rank candidates (stub)
1232
+ const philosopherOutput = invokeStubPhilosopher(dreamerOutput, principleId);
1233
+
1234
+ if (!philosopherOutput.valid || philosopherOutput.judgments.length === 0) {
1235
+ failures.push({
1236
+ stage: 'philosopher',
1237
+ reason: philosopherOutput.reason ?? 'No judgments produced',
1238
+ });
1239
+ telemetry.stageFailures.push(`Philosopher: ${philosopherOutput.reason ?? 'failed'}`);
1240
+ return {
1241
+ success: false,
1242
+ telemetry,
1243
+ failures,
1244
+ fallbackOccurred: false,
1245
+ };
1246
+ }
1247
+
1248
+ telemetry.philosopherPassed = true;
1249
+
1250
+ // Step 3: Scribe — produce final artifact using tournament selection (stub)
1251
+ const draftArtifact = invokeStubScribe(dreamerOutput, philosopherOutput, snapshot, principleId, telemetry, config);
1252
+
1253
+ if (!draftArtifact) {
1254
+ failures.push({
1255
+ stage: 'scribe',
1256
+ reason: 'Failed to synthesize artifact from candidates',
1257
+ });
1258
+ telemetry.stageFailures.push('Scribe: synthesis failed');
1259
+ return {
1260
+ success: false,
1261
+ telemetry,
1262
+ failures,
1263
+ fallbackOccurred: false,
1264
+ };
1265
+ }
1266
+
1267
+ telemetry.scribePassed = true;
1268
+ telemetry.selectedCandidateIndex = draftArtifact.selectedCandidateIndex;
1269
+
1270
+ if (draftArtifact.telemetry) {
1271
+ telemetry.tournamentTrace = draftArtifact.telemetry.tournamentTrace;
1272
+ telemetry.winnerAggregateScore = draftArtifact.telemetry.winnerAggregateScore;
1273
+ telemetry.winnerThresholdPassed = draftArtifact.telemetry.winnerThresholdPassed;
1274
+ telemetry.eligibleCandidateCount = draftArtifact.telemetry.eligibleCandidateCount;
1275
+ }
1276
+
1277
+ return {
1278
+ success: true,
1279
+ artifact: draftArtifact,
1280
+ telemetry,
1281
+ failures: [],
1282
+ fallbackOccurred: false,
1283
+ };
1284
+ }
1285
+
1286
+ // ---------------------------------------------------------------------------
1287
+ // Trinity Validation (for Arbiter integration)
1288
+ // ---------------------------------------------------------------------------
1289
+
1290
+ /**
1291
+ * Validate that a Trinity draft artifact can pass final arbiter validation.
1292
+ * This checks the draft against the same rules as single-reflector artifacts.
1293
+ */
1294
+ export interface DraftValidationResult {
1295
+ valid: boolean;
1296
+ failures: string[];
1297
+ }
1298
+
1299
+ /**
1300
+ * Validate a TrinityDraftArtifact before passing to arbiter.
1301
+ */
1302
+ export function validateDraftArtifact(draft: TrinityDraftArtifact): DraftValidationResult {
1303
+ const failures: string[] = [];
1304
+
1305
+ if (!draft.badDecision || draft.badDecision.trim().length === 0) {
1306
+ failures.push('badDecision is required and non-empty');
1307
+ }
1308
+
1309
+ if (!draft.betterDecision || draft.betterDecision.trim().length === 0) {
1310
+ failures.push('betterDecision is required and non-empty');
1311
+ }
1312
+
1313
+ if (!draft.rationale || draft.rationale.trim().length < 20) {
1314
+ failures.push('rationale must be at least 20 characters');
1315
+ }
1316
+
1317
+ if (!draft.principleId || draft.principleId.trim().length === 0) {
1318
+ failures.push('principleId is required');
1319
+ }
1320
+
1321
+ if (!draft.sessionId || draft.sessionId.trim().length === 0) {
1322
+ failures.push('sessionId is required');
1323
+ }
1324
+
1325
+ // badDecision should not be identical to betterDecision
1326
+ if (
1327
+ typeof draft.badDecision === 'string' &&
1328
+ typeof draft.betterDecision === 'string' &&
1329
+ draft.badDecision.trim().length > 0 &&
1330
+ draft.betterDecision.trim().length > 0 &&
1331
+ draft.badDecision.trim() === draft.betterDecision.trim()
1332
+ ) {
1333
+ failures.push('badDecision and betterDecision cannot be identical');
1334
+ }
1335
+
1336
+ return {
1337
+ valid: failures.length === 0,
1338
+ failures,
1339
+ };
1340
+ }
1341
+
1342
+ /**
1343
+ * Convert a TrinityDraftArtifact to a NocturnalArtifact-compatible structure.
1344
+ */
1345
+ export function draftToArtifact(draft: TrinityDraftArtifact): {
1346
+ artifactId: string;
1347
+ sessionId: string;
1348
+ principleId: string;
1349
+ sourceSnapshotRef: string;
1350
+ badDecision: string;
1351
+ betterDecision: string;
1352
+ rationale: string;
1353
+ createdAt: string;
1354
+ thinkingModelDelta?: number;
1355
+ planningRatioGain?: number;
1356
+ } {
1357
+ // Compute reflection quality metrics
1358
+ const thinkingModelDelta = draft.thinkingModelDelta ?? computeThinkingModelDelta(draft.badDecision, draft.betterDecision);
1359
+ // planningRatioGain requires an improved snapshot — Trinity draft doesn't have one, so default to 0
1360
+ const planningRatioGain = draft.planningRatioGain ?? 0;
1361
+
1362
+ return {
1363
+ artifactId: randomUUID(),
1364
+ sessionId: draft.sessionId,
1365
+ principleId: draft.principleId,
1366
+ sourceSnapshotRef: draft.sourceSnapshotRef,
1367
+ badDecision: draft.badDecision,
1368
+ betterDecision: draft.betterDecision,
1369
+ rationale: draft.rationale,
1370
+ createdAt: new Date().toISOString(),
1371
+ thinkingModelDelta,
1372
+ planningRatioGain,
1373
+ };
1374
+ }
1375
+
1376
+ // ---------------------------------------------------------------------------
1377
+ // Default Configuration
1378
+ // ---------------------------------------------------------------------------
1379
+
1380
+ export const DEFAULT_TRINITY_CONFIG: TrinityConfig = {
1381
+ useTrinity: true,
1382
+ maxCandidates: 3,
1383
+ useStubs: false, // Real subagent execution is the default; set useStubs=true for stub-only mode
1384
+ };