principles-disciple 1.8.1 → 1.8.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (508) 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 +4 -4
  10. package/package.json +11 -13
  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} +175 -62
  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} +160 -80
  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} +235 -79
  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/{dist/service/subagent-workflow/empathy-observer-workflow-manager.js → src/service/subagent-workflow/empathy-observer-workflow-manager.ts} +240 -117
  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/{dist/service/subagent-workflow/types.d.ts → src/service/subagent-workflow/types.ts} +137 -18
  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/en/skills/ai-sprint-orchestration/EXAMPLES.md +63 -0
  138. package/templates/langs/en/skills/ai-sprint-orchestration/REFERENCE.md +136 -0
  139. package/templates/langs/en/skills/ai-sprint-orchestration/SKILL.md +67 -0
  140. package/templates/langs/en/skills/ai-sprint-orchestration/references/agent-registry.json +214 -0
  141. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +107 -0
  142. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +107 -0
  143. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +105 -0
  144. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +108 -0
  145. package/templates/langs/en/skills/ai-sprint-orchestration/references/workflow-v1-acceptance-checklist.md +58 -0
  146. package/templates/langs/en/skills/ai-sprint-orchestration/references/workflow-v1.4-work-unit-handoff.md +190 -0
  147. package/templates/langs/en/skills/ai-sprint-orchestration/runtime/.gitignore +2 -0
  148. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/archive.mjs +310 -0
  149. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/contract-enforcement.mjs +683 -0
  150. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/decision.mjs +604 -0
  151. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/state-store.mjs +32 -0
  152. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/task-specs.mjs +707 -0
  153. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/run.mjs +3419 -0
  154. package/templates/langs/zh/skills/ai-sprint-orchestration/EXAMPLES.md +63 -0
  155. package/templates/langs/zh/skills/ai-sprint-orchestration/REFERENCE.md +136 -0
  156. package/templates/langs/zh/skills/ai-sprint-orchestration/SKILL.md +67 -0
  157. package/templates/langs/zh/skills/ai-sprint-orchestration/references/agent-registry.json +214 -0
  158. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +107 -0
  159. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +107 -0
  160. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +105 -0
  161. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +108 -0
  162. package/templates/langs/zh/skills/ai-sprint-orchestration/references/workflow-v1-acceptance-checklist.md +58 -0
  163. package/templates/langs/zh/skills/ai-sprint-orchestration/references/workflow-v1.4-work-unit-handoff.md +190 -0
  164. package/templates/langs/zh/skills/ai-sprint-orchestration/runtime/.gitignore +2 -0
  165. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/archive.mjs +310 -0
  166. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/contract-enforcement.mjs +683 -0
  167. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/decision.mjs +604 -0
  168. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/state-store.mjs +32 -0
  169. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/task-specs.mjs +707 -0
  170. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/run.mjs +3419 -0
  171. package/templates/langs/zh/skills/ai-sprint-orchestration/test/archive.test.mjs +230 -0
  172. package/templates/langs/zh/skills/ai-sprint-orchestration/test/contract-enforcement.test.mjs +672 -0
  173. package/templates/langs/zh/skills/ai-sprint-orchestration/test/decision.test.mjs +1321 -0
  174. package/templates/langs/zh/skills/ai-sprint-orchestration/test/run.test.mjs +1419 -0
  175. package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +70 -1
  176. package/templates/pain_settings.json +2 -1
  177. package/tests/README.md +120 -0
  178. package/tests/build-artifacts.test.ts +111 -0
  179. package/tests/commands/evolution-status.test.ts +222 -0
  180. package/tests/commands/evolver.test.ts +22 -0
  181. package/tests/commands/export.test.ts +78 -0
  182. package/tests/commands/nocturnal-review.test.ts +448 -0
  183. package/tests/commands/nocturnal-train.test.ts +97 -0
  184. package/tests/commands/pain.test.ts +108 -0
  185. package/tests/commands/samples.test.ts +65 -0
  186. package/tests/commands/strategy.test.ts +34 -0
  187. package/tests/commands/thinking-os.test.ts +88 -0
  188. package/tests/core/adaptive-thresholds.test.ts +261 -0
  189. package/tests/core/config-service.test.ts +89 -0
  190. package/tests/core/config.test.ts +90 -0
  191. package/tests/core/control-ui-db.test.ts +75 -0
  192. package/tests/core/core-template-guidance.test.ts +21 -0
  193. package/tests/core/detection-funnel.test.ts +63 -0
  194. package/tests/core/detection-service.test.ts +50 -0
  195. package/tests/core/dictionary-service.test.ts +116 -0
  196. package/tests/core/dictionary.test.ts +168 -0
  197. package/tests/core/empathy-keyword-matcher.test.ts +209 -0
  198. package/tests/core/event-log.test.ts +181 -0
  199. package/tests/core/evolution-e2e.test.ts +58 -0
  200. package/tests/core/evolution-engine-gate-integration.test.ts +543 -0
  201. package/tests/core/evolution-engine.test.ts +562 -0
  202. package/tests/core/evolution-logger.test.ts +148 -0
  203. package/tests/core/evolution-migration.test.ts +50 -0
  204. package/tests/core/evolution-paths.test.ts +21 -0
  205. package/tests/core/evolution-reducer.detector-metadata.test.ts +602 -0
  206. package/tests/core/evolution-reducer.test.ts +180 -0
  207. package/tests/core/evolution-types-loop.test.ts +48 -0
  208. package/tests/core/evolution-user-stories.e2e.test.ts +249 -0
  209. package/tests/core/external-training-contract.test.ts +463 -0
  210. package/tests/core/focus-history.test.ts +682 -0
  211. package/tests/core/init-flatten.test.ts +69 -0
  212. package/tests/core/init-refactor.test.ts +87 -0
  213. package/tests/core/init-v1.3.test.ts +46 -0
  214. package/tests/core/init.test.ts +190 -0
  215. package/tests/core/local-worker-routing.test.ts +757 -0
  216. package/tests/core/migration.test.ts +84 -0
  217. package/tests/core/model-deployment-registry.test.ts +845 -0
  218. package/tests/core/model-training-registry.test.ts +889 -0
  219. package/tests/core/nocturnal-arbiter.test.ts +494 -0
  220. package/tests/core/nocturnal-candidate-scoring.test.ts +400 -0
  221. package/tests/core/nocturnal-compliance.test.ts +646 -0
  222. package/tests/core/nocturnal-dataset.test.ts +892 -0
  223. package/tests/core/nocturnal-executability.test.ts +357 -0
  224. package/tests/core/nocturnal-export.test.ts +462 -0
  225. package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +428 -0
  226. package/tests/core/nocturnal-trajectory-extractor.test.ts +634 -0
  227. package/tests/core/nocturnal-trinity.test.ts +953 -0
  228. package/tests/core/pain.test.ts +33 -0
  229. package/tests/core/path-resolver.test.ts +57 -0
  230. package/tests/core/paths-refactor.test.ts +42 -0
  231. package/tests/core/phase7-rollout-integration.test.ts +477 -0
  232. package/tests/core/principle-training-state.test.ts +712 -0
  233. package/tests/core/profile.test.ts +56 -0
  234. package/tests/core/promotion-gate.test.ts +556 -0
  235. package/tests/core/risk-calculator.test.ts +168 -0
  236. package/tests/core/session-tracker.test.ts +191 -0
  237. package/tests/core/training-program.test.ts +472 -0
  238. package/tests/core/trajectory.test.ts +265 -0
  239. package/tests/core/workspace-context-factory.test.ts +18 -0
  240. package/tests/core/workspace-context.test.ts +134 -0
  241. package/tests/fixtures/nocturnal-reviewed-subset.json +183 -0
  242. package/tests/fixtures/production-compatibility.test.ts +147 -0
  243. package/tests/fixtures/production-mock-generator.ts +282 -0
  244. package/tests/hooks/bash-risk-integration.test.ts +137 -0
  245. package/tests/hooks/bash-risk.test.ts +81 -0
  246. package/tests/hooks/edit-verification.test.ts +678 -0
  247. package/tests/hooks/gate-edit-verification-p1.test.ts +632 -0
  248. package/tests/hooks/gate-edit-verification.test.ts +435 -0
  249. package/tests/hooks/gate-pipeline-integration.test.ts +404 -0
  250. package/tests/hooks/gate.test.ts +271 -0
  251. package/tests/hooks/gfi-gate-unit.test.ts +422 -0
  252. package/tests/hooks/gfi-gate.test.ts +669 -0
  253. package/tests/hooks/lifecycle.test.ts +248 -0
  254. package/tests/hooks/llm.test.ts +308 -0
  255. package/tests/hooks/message-sanitize.test.ts +36 -0
  256. package/tests/hooks/pain.test.ts +141 -0
  257. package/tests/hooks/progressive-trust-gate.test.ts +277 -0
  258. package/tests/hooks/prompt.test.ts +1411 -0
  259. package/tests/hooks/subagent.test.ts +467 -0
  260. package/tests/hooks/thinking-gate.test.ts +313 -0
  261. package/tests/http/principles-console-route.test.ts +140 -0
  262. package/tests/hygiene-tracker.test.ts +77 -0
  263. package/tests/index.integration.test.ts +179 -0
  264. package/tests/index.shadow-routing.integration.test.ts +140 -0
  265. package/tests/index.test.ts +9 -0
  266. package/tests/integration/empathy-workflow-integration.test.ts +627 -0
  267. package/tests/service/control-ui-query-service.test.ts +121 -0
  268. package/tests/service/empathy-observer-workflow-manager.test.ts +176 -0
  269. package/tests/service/evolution-worker.test.ts +585 -0
  270. package/tests/service/nocturnal-runtime.test.ts +470 -0
  271. package/tests/service/nocturnal-service.test.ts +577 -0
  272. package/tests/service/nocturnal-target-selector.test.ts +615 -0
  273. package/tests/service/nocturnal-workflow-manager.test.ts +439 -0
  274. package/tests/service/phase3-input-filter.test.ts +289 -0
  275. package/tests/service/runtime-summary-service.test.ts +919 -0
  276. package/tests/task-compliance.test.ts +166 -0
  277. package/tests/test-utils.ts +48 -0
  278. package/tests/tools/critique-prompt.test.ts +260 -0
  279. package/tests/tools/deep-reflect.test.ts +232 -0
  280. package/tests/tools/model-index.test.ts +246 -0
  281. package/tests/ui/app.test.tsx +114 -0
  282. package/tests/utils/file-lock.test.ts +407 -0
  283. package/tests/utils/hashing.test.ts +32 -0
  284. package/tests/utils/io.test.ts +39 -0
  285. package/tests/utils/nlp.test.ts +53 -0
  286. package/tests/utils/plugin-logger.test.ts +156 -0
  287. package/tsconfig.json +16 -0
  288. package/tsconfig.tsbuildinfo +1 -0
  289. package/ui/src/App.tsx +45 -0
  290. package/ui/src/api.ts +216 -0
  291. package/ui/src/charts.tsx +586 -0
  292. package/ui/src/components/ErrorState.tsx +6 -0
  293. package/ui/src/components/Loading.tsx +13 -0
  294. package/ui/src/components/ProtectedRoute.tsx +12 -0
  295. package/ui/src/components/Shell.tsx +91 -0
  296. package/ui/src/components/WorkspaceConfig.tsx +146 -0
  297. package/ui/src/components/index.ts +5 -0
  298. package/ui/src/context/auth.tsx +80 -0
  299. package/ui/src/context/theme.tsx +66 -0
  300. package/ui/src/hooks/useAutoRefresh.ts +39 -0
  301. package/ui/src/i18n/ui.ts +363 -0
  302. package/ui/src/main.tsx +16 -0
  303. package/ui/src/pages/EvolutionPage.tsx +352 -0
  304. package/ui/src/pages/FeedbackPage.tsx +140 -0
  305. package/ui/src/pages/GateMonitorPage.tsx +136 -0
  306. package/ui/src/pages/LoginPage.tsx +88 -0
  307. package/ui/src/pages/OverviewPage.tsx +238 -0
  308. package/ui/src/pages/SamplesPage.tsx +174 -0
  309. package/ui/src/pages/ThinkingModelsPage.tsx +127 -0
  310. package/ui/src/styles.css +1661 -0
  311. package/ui/src/types.ts +368 -0
  312. package/ui/src/utils/format.ts +15 -0
  313. package/vitest.config.ts +23 -0
  314. package/dist/commands/capabilities.d.ts +0 -3
  315. package/dist/commands/capabilities.js +0 -73
  316. package/dist/commands/context.d.ts +0 -5
  317. package/dist/commands/evolution-status.d.ts +0 -4
  318. package/dist/commands/evolution-status.js +0 -117
  319. package/dist/commands/evolver.d.ts +0 -9
  320. package/dist/commands/evolver.js +0 -26
  321. package/dist/commands/export.d.ts +0 -2
  322. package/dist/commands/export.js +0 -98
  323. package/dist/commands/focus.d.ts +0 -14
  324. package/dist/commands/focus.js +0 -457
  325. package/dist/commands/nocturnal-review.d.ts +0 -24
  326. package/dist/commands/nocturnal-review.js +0 -265
  327. package/dist/commands/nocturnal-rollout.d.ts +0 -27
  328. package/dist/commands/nocturnal-rollout.js +0 -671
  329. package/dist/commands/nocturnal-train.d.ts +0 -25
  330. package/dist/commands/nocturnal-train.js +0 -919
  331. package/dist/commands/pain.d.ts +0 -5
  332. package/dist/commands/principle-rollback.d.ts +0 -4
  333. package/dist/commands/principle-rollback.js +0 -22
  334. package/dist/commands/rollback.d.ts +0 -19
  335. package/dist/commands/samples.d.ts +0 -2
  336. package/dist/commands/samples.js +0 -55
  337. package/dist/commands/strategy.d.ts +0 -3
  338. package/dist/commands/strategy.js +0 -29
  339. package/dist/commands/thinking-os.d.ts +0 -2
  340. package/dist/config/defaults/runtime.d.ts +0 -40
  341. package/dist/config/errors.d.ts +0 -84
  342. package/dist/config/errors.js +0 -94
  343. package/dist/config/index.js +0 -7
  344. package/dist/constants/diagnostician.d.ts +0 -12
  345. package/dist/constants/diagnostician.js +0 -56
  346. package/dist/constants/tools.d.ts +0 -17
  347. package/dist/constants/tools.js +0 -54
  348. package/dist/core/adaptive-thresholds.d.ts +0 -186
  349. package/dist/core/adaptive-thresholds.js +0 -300
  350. package/dist/core/config-service.d.ts +0 -15
  351. package/dist/core/config.d.ts +0 -129
  352. package/dist/core/control-ui-db.d.ts +0 -95
  353. package/dist/core/control-ui-db.js +0 -292
  354. package/dist/core/detection-funnel.d.ts +0 -33
  355. package/dist/core/detection-service.d.ts +0 -15
  356. package/dist/core/dictionary-service.d.ts +0 -15
  357. package/dist/core/dictionary.d.ts +0 -38
  358. package/dist/core/event-log.d.ts +0 -82
  359. package/dist/core/event-log.js +0 -463
  360. package/dist/core/evolution-engine.d.ts +0 -118
  361. package/dist/core/evolution-engine.js +0 -464
  362. package/dist/core/evolution-logger.d.ts +0 -137
  363. package/dist/core/evolution-logger.js +0 -256
  364. package/dist/core/evolution-migration.d.ts +0 -5
  365. package/dist/core/evolution-migration.js +0 -65
  366. package/dist/core/evolution-reducer.d.ts +0 -98
  367. package/dist/core/evolution-reducer.js +0 -465
  368. package/dist/core/evolution-types.d.ts +0 -287
  369. package/dist/core/evolution-types.js +0 -78
  370. package/dist/core/external-training-contract.d.ts +0 -276
  371. package/dist/core/external-training-contract.js +0 -269
  372. package/dist/core/focus-history.d.ts +0 -210
  373. package/dist/core/focus-history.js +0 -1185
  374. package/dist/core/hygiene/tracker.d.ts +0 -22
  375. package/dist/core/hygiene/tracker.js +0 -106
  376. package/dist/core/init.d.ts +0 -12
  377. package/dist/core/local-worker-routing.d.ts +0 -175
  378. package/dist/core/local-worker-routing.js +0 -525
  379. package/dist/core/migration.d.ts +0 -6
  380. package/dist/core/model-deployment-registry.d.ts +0 -218
  381. package/dist/core/model-deployment-registry.js +0 -503
  382. package/dist/core/model-training-registry.d.ts +0 -295
  383. package/dist/core/model-training-registry.js +0 -475
  384. package/dist/core/nocturnal-arbiter.d.ts +0 -159
  385. package/dist/core/nocturnal-arbiter.js +0 -534
  386. package/dist/core/nocturnal-candidate-scoring.d.ts +0 -137
  387. package/dist/core/nocturnal-candidate-scoring.js +0 -266
  388. package/dist/core/nocturnal-compliance.d.ts +0 -175
  389. package/dist/core/nocturnal-compliance.js +0 -824
  390. package/dist/core/nocturnal-dataset.d.ts +0 -224
  391. package/dist/core/nocturnal-dataset.js +0 -443
  392. package/dist/core/nocturnal-executability.d.ts +0 -85
  393. package/dist/core/nocturnal-executability.js +0 -331
  394. package/dist/core/nocturnal-export.d.ts +0 -124
  395. package/dist/core/nocturnal-export.js +0 -275
  396. package/dist/core/nocturnal-paths.d.ts +0 -124
  397. package/dist/core/nocturnal-trajectory-extractor.d.ts +0 -242
  398. package/dist/core/nocturnal-trajectory-extractor.js +0 -307
  399. package/dist/core/nocturnal-trinity.d.ts +0 -311
  400. package/dist/core/nocturnal-trinity.js +0 -880
  401. package/dist/core/pain.d.ts +0 -4
  402. package/dist/core/pain.js +0 -70
  403. package/dist/core/path-resolver.d.ts +0 -46
  404. package/dist/core/paths.d.ts +0 -65
  405. package/dist/core/principle-training-state.d.ts +0 -121
  406. package/dist/core/principle-training-state.js +0 -321
  407. package/dist/core/profile.d.ts +0 -62
  408. package/dist/core/profile.js +0 -210
  409. package/dist/core/promotion-gate.d.ts +0 -238
  410. package/dist/core/promotion-gate.js +0 -529
  411. package/dist/core/risk-calculator.d.ts +0 -22
  412. package/dist/core/session-tracker.d.ts +0 -101
  413. package/dist/core/shadow-observation-registry.d.ts +0 -217
  414. package/dist/core/shadow-observation-registry.js +0 -308
  415. package/dist/core/system-logger.d.ts +0 -8
  416. package/dist/core/thinking-models.d.ts +0 -38
  417. package/dist/core/thinking-models.js +0 -170
  418. package/dist/core/training-program.d.ts +0 -233
  419. package/dist/core/training-program.js +0 -433
  420. package/dist/core/trajectory.d.ts +0 -411
  421. package/dist/core/trajectory.js +0 -1307
  422. package/dist/core/workspace-context.d.ts +0 -71
  423. package/dist/hooks/bash-risk.d.ts +0 -57
  424. package/dist/hooks/bash-risk.js +0 -137
  425. package/dist/hooks/edit-verification.d.ts +0 -62
  426. package/dist/hooks/edit-verification.js +0 -256
  427. package/dist/hooks/gate-block-helper.d.ts +0 -44
  428. package/dist/hooks/gate-block-helper.js +0 -119
  429. package/dist/hooks/gate.d.ts +0 -24
  430. package/dist/hooks/gate.js +0 -173
  431. package/dist/hooks/gfi-gate.d.ts +0 -40
  432. package/dist/hooks/gfi-gate.js +0 -113
  433. package/dist/hooks/lifecycle.d.ts +0 -5
  434. package/dist/hooks/lifecycle.js +0 -284
  435. package/dist/hooks/llm.d.ts +0 -13
  436. package/dist/hooks/message-sanitize.d.ts +0 -3
  437. package/dist/hooks/message-sanitize.js +0 -37
  438. package/dist/hooks/pain.d.ts +0 -5
  439. package/dist/hooks/pain.js +0 -301
  440. package/dist/hooks/progressive-trust-gate.d.ts +0 -52
  441. package/dist/hooks/progressive-trust-gate.js +0 -134
  442. package/dist/hooks/prompt.d.ts +0 -49
  443. package/dist/hooks/prompt.js +0 -905
  444. package/dist/hooks/subagent.d.ts +0 -10
  445. package/dist/hooks/subagent.js +0 -387
  446. package/dist/hooks/thinking-checkpoint.d.ts +0 -37
  447. package/dist/hooks/thinking-checkpoint.js +0 -51
  448. package/dist/hooks/trajectory-collector.d.ts +0 -32
  449. package/dist/hooks/trajectory-collector.js +0 -256
  450. package/dist/http/principles-console-route.d.ts +0 -9
  451. package/dist/http/principles-console-route.js +0 -681
  452. package/dist/i18n/commands.d.ts +0 -26
  453. package/dist/i18n/commands.js +0 -116
  454. package/dist/index.d.ts +0 -7
  455. package/dist/index.js +0 -581
  456. package/dist/service/central-database.d.ts +0 -104
  457. package/dist/service/central-database.js +0 -649
  458. package/dist/service/control-ui-query-service.d.ts +0 -221
  459. package/dist/service/control-ui-query-service.js +0 -543
  460. package/dist/service/empathy-observer-manager.d.ts +0 -88
  461. package/dist/service/empathy-observer-manager.js +0 -414
  462. package/dist/service/evolution-query-service.d.ts +0 -155
  463. package/dist/service/evolution-query-service.js +0 -258
  464. package/dist/service/evolution-worker.d.ts +0 -101
  465. package/dist/service/evolution-worker.js +0 -975
  466. package/dist/service/health-query-service.d.ts +0 -170
  467. package/dist/service/health-query-service.js +0 -662
  468. package/dist/service/nocturnal-runtime.d.ts +0 -183
  469. package/dist/service/nocturnal-service.d.ts +0 -163
  470. package/dist/service/nocturnal-service.js +0 -787
  471. package/dist/service/nocturnal-target-selector.d.ts +0 -145
  472. package/dist/service/nocturnal-target-selector.js +0 -315
  473. package/dist/service/phase3-input-filter.d.ts +0 -73
  474. package/dist/service/phase3-input-filter.js +0 -172
  475. package/dist/service/runtime-summary-service.d.ts +0 -122
  476. package/dist/service/runtime-summary-service.js +0 -485
  477. package/dist/service/subagent-workflow/empathy-observer-workflow-manager.d.ts +0 -48
  478. package/dist/service/subagent-workflow/index.d.ts +0 -4
  479. package/dist/service/subagent-workflow/index.js +0 -3
  480. package/dist/service/subagent-workflow/runtime-direct-driver.d.ts +0 -77
  481. package/dist/service/subagent-workflow/runtime-direct-driver.js +0 -75
  482. package/dist/service/subagent-workflow/types.js +0 -11
  483. package/dist/service/subagent-workflow/workflow-store.d.ts +0 -26
  484. package/dist/service/subagent-workflow/workflow-store.js +0 -165
  485. package/dist/service/trajectory-service.d.ts +0 -2
  486. package/dist/service/trajectory-service.js +0 -15
  487. package/dist/tools/critique-prompt.d.ts +0 -14
  488. package/dist/tools/deep-reflect.d.ts +0 -39
  489. package/dist/tools/deep-reflect.js +0 -350
  490. package/dist/tools/model-index.d.ts +0 -9
  491. package/dist/types/event-types.d.ts +0 -306
  492. package/dist/types/event-types.js +0 -106
  493. package/dist/types/hygiene-types.d.ts +0 -20
  494. package/dist/types/hygiene-types.js +0 -12
  495. package/dist/types/runtime-summary.d.ts +0 -47
  496. package/dist/types/runtime-summary.js +0 -1
  497. package/dist/types.d.ts +0 -50
  498. package/dist/types.js +0 -22
  499. package/dist/utils/file-lock.d.ts +0 -71
  500. package/dist/utils/file-lock.js +0 -309
  501. package/dist/utils/glob-match.d.ts +0 -28
  502. package/dist/utils/hashing.d.ts +0 -9
  503. package/dist/utils/io.d.ts +0 -6
  504. package/dist/utils/io.js +0 -106
  505. package/dist/utils/nlp.d.ts +0 -9
  506. package/dist/utils/plugin-logger.d.ts +0 -39
  507. package/dist/utils/subagent-probe.d.ts +0 -34
  508. package/dist/utils/subagent-probe.js +0 -81
@@ -0,0 +1,617 @@
1
+ /**
2
+ * Local Worker Routing Policy — Task Classification and Routing Decisions
3
+ * ======================================================================
4
+ *
5
+ * PURPOSE: Provide an explainable, testable policy that decides whether a given
6
+ * task can be delegated to a local-worker profile (local-reader or local-editor)
7
+ * or must stay on the main agent.
8
+ *
9
+ * ARCHITECTURE:
10
+ * - This module is POLICY ONLY — it makes routing decisions but does NOT execute them
11
+ * - The main agent (or a delegation hook in a future phase) is responsible for
12
+ * actually routing the task based on the RoutingDecision returned here
13
+ * - All decisions are deterministic and based on structured input fields
14
+ * - No model inference, no learning, no dynamic adaptation
15
+ *
16
+ * TASK CLASSIFICATION TAXONOMY:
17
+ * reader_eligible — clearly suitable for local-reader
18
+ * editor_eligible — clearly suitable for local-editor
19
+ * high_entropy_disallowed — high-complexity tasks that must stay on main agent
20
+ * ambiguous_scope — tasks that are unclear and need main-agent judgment
21
+ * deployment_unavailable — no enabled deployment exists for the target profile
22
+ *
23
+ * NOTE: risk_disallowed has been removed. Risk signals are now advisory only —
24
+ * the main agent decides whether to delegate based on full context.
25
+ *
26
+ * FAIL-CLOSED PRINCIPLE:
27
+ * - When in doubt → stay_main
28
+ * - Unclear intent → stay_main
29
+ * - High complexity → stay_main
30
+ * - No enabled deployment → stay_main
31
+ *
32
+ * DESIGN CONSTRAINTS:
33
+ * - No actual task execution
34
+ * - No automatic learning or route optimization
35
+ * - No Trinity or adaptive threshold logic
36
+ * - Routing decisions are fully explainable (return `reason` + `blockers[]`)
37
+ */
38
+
39
+ import type { WorkerProfile } from './model-deployment-registry.js';
40
+ import {
41
+ isRoutingEnabledForProfile,
42
+ getDeployment,
43
+ } from './model-deployment-registry.js';
44
+ import { isCheckpointDeployable } from './model-training-registry.js';
45
+ import { getPromotionState } from './promotion-gate.js';
46
+
47
+ // ---------------------------------------------------------------------------
48
+ // Routing Input Contract
49
+ // ---------------------------------------------------------------------------
50
+
51
+ /**
52
+ * The input contract for a routing decision.
53
+ * All fields are optional — the classifier handles missing data gracefully
54
+ * by treating it as ambiguous (stay_main).
55
+ */
56
+ export interface RoutingInput {
57
+ /**
58
+ * A short label or name for the task intent.
59
+ * E.g., "read_file", "edit_config", "debug_memory_leak", "design_system"
60
+ */
61
+ taskIntent?: string;
62
+
63
+ /**
64
+ * Natural-language description of the task.
65
+ * The classifier examines this for keywords indicating complexity/risk.
66
+ */
67
+ taskDescription?: string;
68
+
69
+ /**
70
+ * Specific tools requested or implied by the task.
71
+ * These are examined for risk signals (e.g., bash, rm, git push).
72
+ */
73
+ requestedTools?: string[];
74
+
75
+ /**
76
+ * Specific files involved or targeted.
77
+ * Examined for risk-path indicators (e.g., .git/, node_modules, production configs).
78
+ */
79
+ requestedFiles?: string[];
80
+
81
+ /**
82
+ * Shape of expected output.
83
+ * E.g., "json", "markdown", "one_line", "full_report"
84
+ */
85
+ expectedOutputShape?: string;
86
+
87
+ /**
88
+ * Complexity hints for the task.
89
+ * E.g., ["multi_step", "cross_file", "ambiguous", "requires_planning"]
90
+ */
91
+ complexityHints?: string[];
92
+
93
+ /**
94
+ * Target worker profile for routing consideration.
95
+ * If omitted, both profiles are evaluated and the best match is returned.
96
+ */
97
+ targetProfile?: WorkerProfile;
98
+ }
99
+
100
+ // ---------------------------------------------------------------------------
101
+ // Routing Decision Contract
102
+ // ---------------------------------------------------------------------------
103
+
104
+ /**
105
+ * The result of a routing classification decision.
106
+ * Always includes a `reason` and a `blockers` list for full explainability.
107
+ */
108
+ export interface RoutingDecision {
109
+ /**
110
+ * The routing verdict.
111
+ * - `route_local` — the task may be delegated to `targetProfile`
112
+ * - `stay_main` — the task must remain on the main agent
113
+ */
114
+ decision: 'route_local' | 'stay_main';
115
+
116
+ /**
117
+ * Which profile the task should be routed to (if decision === 'route_local').
118
+ * Null if decision === 'stay_main'.
119
+ */
120
+ targetProfile: WorkerProfile | null;
121
+
122
+ /**
123
+ * The task classification category that led to this decision.
124
+ */
125
+ classification:
126
+ | 'reader_eligible'
127
+ | 'editor_eligible'
128
+ | 'high_entropy_disallowed'
129
+ | 'ambiguous_scope'
130
+ | 'profile_mismatch'
131
+ | 'deployment_unavailable';
132
+
133
+ /**
134
+ * Human-readable explanation of the routing decision.
135
+ * Must be specific enough that a developer can understand why a task was accepted/rejected.
136
+ */
137
+ reason: string;
138
+
139
+ /**
140
+ * List of specific reasons that blocked routing (if decision === 'stay_main').
141
+ * Empty if decision === 'route_local'.
142
+ */
143
+ blockers: string[];
144
+
145
+ /**
146
+ * Whether a deployment check was performed and whether it passed.
147
+ * Useful for diagnostics when deployment_unavailable is the classification.
148
+ */
149
+ deploymentCheck: {
150
+ performed: boolean;
151
+ profileAvailable: boolean;
152
+ routingEnabled: boolean;
153
+ /** Whether the active checkpoint is currently marked as deployable in the training registry. */
154
+ checkpointDeployable: boolean;
155
+ };
156
+
157
+ /**
158
+ * The active checkpoint ID that would be used for routing (if decision === 'route_local').
159
+ * This is the checkpoint from the deployment registry.
160
+ * Null if decision === 'stay_main' or if no checkpoint is active.
161
+ *
162
+ * USE FOR SHADOW OBSERVATIONS:
163
+ * When routing in shadow mode (checkpoint is in shadow_ready state),
164
+ * the caller should record a shadow observation using this checkpoint ID.
165
+ */
166
+ activeCheckpointId: string | null;
167
+
168
+ /**
169
+ * The promotion state of the active checkpoint.
170
+ * Indicates whether this is a regular deployment or a shadow rollout.
171
+ * Useful for determining whether to record shadow observations.
172
+ */
173
+ activeCheckpointState?: 'promotable' | 'shadow_ready' | 'candidate_only';
174
+
175
+ /**
176
+ * Deprecated: runtime shadow observations are now recorded from real
177
+ * subagent lifecycle hooks instead of from classifyTask().
178
+ */
179
+ shadowObservationId?: string;
180
+
181
+ }
182
+
183
+ // ---------------------------------------------------------------------------
184
+ // Keyword Classifiers
185
+ // ---------------------------------------------------------------------------
186
+
187
+ /**
188
+ * Keywords that indicate a task is suitable for `local-reader`.
189
+ * Matched against taskIntent + taskDescription.
190
+ */
191
+ const READER_KEYWORDS = [
192
+ 'read', 'view', 'show', 'get', 'find', 'search', 'grep', 'look',
193
+ 'inspect', 'examine', 'list', 'cat', 'head', 'tail', 'diff',
194
+ 'summary', 'summarize', 'extract', 'parse', 'review',
195
+ 'check', 'verify', 'status', 'describe', 'explain_what',
196
+ 'browse', 'fetch', 'show_content', 'file_content', 'code_read',
197
+ ];
198
+
199
+ /**
200
+ * Keywords that indicate a task is suitable for `local-editor`.
201
+ * Matched against taskIntent + taskDescription.
202
+ */
203
+ const EDITOR_KEYWORDS = [
204
+ 'edit', 'update', 'modify', 'change', 'fix', 'patch', 'replace',
205
+ 'add', 'remove', 'delete', 'insert', 'rewrite', 'refactor',
206
+ 'apply', 'execute', 'run', 'transform', 'convert', 'migrate',
207
+ 'write', 'create_file', 'append', 'touch', 'rename',
208
+ ];
209
+
210
+ /**
211
+ * Keywords that indicate HIGH ENTROPY — tasks that must stay on main agent.
212
+ * These indicate open-ended, multi-step, or ambiguous tasks.
213
+ */
214
+ const HIGH_ENTROPY_KEYWORDS = [
215
+ 'design', 'architect', 'plan', 'strategy', 'roadmap', 'propose',
216
+ 'research', 'investigate', 'explore', 'evaluate', 'compare',
217
+ 'decide', 'choose', 'recommend', 'suggest', 'analyze_tradeoffs',
218
+ 'unclear', 'vague', 'ambiguous', 'open_ended', 'multiple_options',
219
+ 'architecture', 'system_design', 'high_level', 'blueprint',
220
+ 'thinking', 'reasoning', '思考', '分析', '设计',
221
+ ];
222
+
223
+ // ---------------------------------------------------------------------------
224
+ // Classification Helpers
225
+ // ---------------------------------------------------------------------------
226
+
227
+ /**
228
+ * Simple case-insensitive keyword match.
229
+ */
230
+ function containsKeyword(text: string | undefined, keywords: string[]): boolean {
231
+ if (!text) return false;
232
+ const lower = text.toLowerCase();
233
+ return keywords.some((kw) => lower.includes(kw));
234
+ }
235
+
236
+ /**
237
+ * Compute a combined text from all input fields for keyword scanning.
238
+ */
239
+ function computeCombinedText(input: RoutingInput): string {
240
+ const parts: string[] = [];
241
+ if (input.taskIntent) parts.push(input.taskIntent);
242
+ if (input.taskDescription) parts.push(input.taskDescription);
243
+ if (input.expectedOutputShape) parts.push(input.expectedOutputShape);
244
+ if (input.complexityHints) parts.push(input.complexityHints.join(' '));
245
+ return parts.join(' ').toLowerCase();
246
+ }
247
+
248
+ // ---------------------------------------------------------------------------
249
+ // Core Classification Logic
250
+ // ---------------------------------------------------------------------------
251
+
252
+ /**
253
+ * Classify the task based on its input fields.
254
+ * Returns a raw classification category (before deployment check).
255
+ */
256
+ function classifyTaskKind(input: RoutingInput): RoutingDecision['classification'] {
257
+ const text = computeCombinedText(input);
258
+ const { taskIntent, taskDescription, requestedFiles, complexityHints } = input;
259
+
260
+ // --- Step 1: High-entropy keyword detection ---
261
+ if (complexityHints?.some((h) =>
262
+ ['multi_step', 'cross_file', 'ambiguous', 'requires_planning', 'open_ended', 'unclear'].includes(h)
263
+ )) {
264
+ return 'high_entropy_disallowed';
265
+ }
266
+
267
+ if (containsKeyword(text, HIGH_ENTROPY_KEYWORDS)) {
268
+ return 'high_entropy_disallowed';
269
+ }
270
+
271
+ if (containsKeyword(taskIntent, ['design', 'architect', 'plan', 'propose']) ||
272
+ containsKeyword(taskDescription, ['design', 'architect', 'plan', 'propose'])) {
273
+ return 'high_entropy_disallowed';
274
+ }
275
+
276
+ // --- Step 2: Reader eligibility ---
277
+ const intentIsReader = containsKeyword(taskIntent, READER_KEYWORDS);
278
+ const descIsReader = containsKeyword(taskDescription, READER_KEYWORDS);
279
+
280
+ if (intentIsReader && (descIsReader || !taskDescription)) {
281
+ return 'reader_eligible';
282
+ }
283
+
284
+ // --- Step 3: Editor eligibility ---
285
+ const uniqueFiles = requestedFiles
286
+ ? [...new Set(requestedFiles.filter((f) => f.trim().length > 0))]
287
+ : [];
288
+ const intentIsEditor = containsKeyword(taskIntent, EDITOR_KEYWORDS);
289
+ const descIsEditor = containsKeyword(taskDescription, EDITOR_KEYWORDS);
290
+
291
+ if (intentIsEditor && (descIsEditor || !taskDescription)) {
292
+ if (uniqueFiles.length >= 4) {
293
+ return 'high_entropy_disallowed';
294
+ }
295
+ return 'editor_eligible';
296
+ }
297
+
298
+ // --- Step 4: Ambiguous scope ---
299
+ if (taskDescription && taskDescription.trim().length > 0) {
300
+ const trimmed = taskDescription.trim();
301
+ if (trimmed.length < 20 || ['todo', 'fix', 'improve', 'change', 'update', 'something'].includes(trimmed.toLowerCase())) {
302
+ return 'ambiguous_scope';
303
+ }
304
+ if (/\b(why|how|should|could|would|what if|should we|whether to)\b/i.test(trimmed)) {
305
+ return 'ambiguous_scope';
306
+ }
307
+ }
308
+
309
+ if (!taskIntent && !taskDescription) {
310
+ return 'ambiguous_scope';
311
+ }
312
+
313
+ return 'ambiguous_scope';
314
+ }
315
+
316
+ /**
317
+ * Build the reason string for a given classification.
318
+ */
319
+ function buildReason(
320
+ classification: RoutingDecision['classification'],
321
+ input: RoutingInput
322
+ ): string {
323
+ const { taskIntent, taskDescription } = input;
324
+
325
+ switch (classification) {
326
+ case 'reader_eligible':
327
+ return `Task "${taskIntent || taskDescription || '(unnamed)'}" is classified as reader_eligible. ` +
328
+ `Keywords indicate focused reading, inspection, or information retrieval. ` +
329
+ `No high-entropy or risk signals detected.`;
330
+
331
+ case 'editor_eligible':
332
+ return `Task "${taskIntent || taskDescription || '(unnamed)'}" is classified as editor_eligible. ` +
333
+ `Keywords indicate bounded editing, modification, or repair. ` +
334
+ `No high-entropy or risk signals detected.`;
335
+
336
+ case 'high_entropy_disallowed': {
337
+ const uniqueFiles = input.requestedFiles
338
+ ? [...new Set(input.requestedFiles.filter((f) => f.trim().length > 0))]
339
+ : [];
340
+ const isLargeScaleEdit = uniqueFiles.length >= 4;
341
+ if (isLargeScaleEdit) {
342
+ return `Task "${taskIntent || taskDescription || '(unnamed)'}" is blocked as high_entropy_disallowed. ` +
343
+ `Editing ${uniqueFiles.length} files simultaneously exceeds the bounded-scope limit for local-editor. ` +
344
+ `Large-scale multi-file edits require the main agent's coordination and risk judgment.`;
345
+ }
346
+ return `Task "${taskIntent || taskDescription || '(unnamed)'}" is blocked as high_entropy_disallowed. ` +
347
+ `Keywords indicate open-ended planning, architecture design, or ambiguous multi-step work. ` +
348
+ `These tasks require the main agent's full reasoning capability.`;
349
+ }
350
+
351
+ case 'ambiguous_scope':
352
+ return `Task "${taskIntent || taskDescription || '(unnamed)'}" is blocked as ambiguous_scope. ` +
353
+ `The task description is too vague, too short, or contains open-ended question words. ` +
354
+ `Main agent must clarify scope before delegation.`;
355
+
356
+ case 'profile_mismatch':
357
+ return `Task profile does not match the requested target profile. ` +
358
+ `The task's natural classification is incompatible with the specified worker profile. ` +
359
+ `Main agent must re-route or choose a compatible profile.`;
360
+
361
+ case 'deployment_unavailable':
362
+ return `No enabled deployment available for routing. ` +
363
+ `Either no checkpoint is bound to the profile, or routing has been disabled. ` +
364
+ `Main agent must handle this task.`;
365
+ }
366
+ }
367
+
368
+ /**
369
+ * Build the blockers list for a given classification.
370
+ */
371
+ function buildBlockers(
372
+ classification: RoutingDecision['classification'],
373
+ input: RoutingInput
374
+ ): string[] {
375
+ switch (classification) {
376
+ case 'reader_eligible':
377
+ return [];
378
+ case 'editor_eligible':
379
+ return [];
380
+ case 'high_entropy_disallowed': {
381
+ const uniqueFiles = input.requestedFiles
382
+ ? [...new Set(input.requestedFiles.filter((f) => f.trim().length > 0))]
383
+ : [];
384
+ const isLargeScaleEdit = uniqueFiles.length >= 4;
385
+ return [
386
+ isLargeScaleEdit
387
+ ? `large-scale multi-file edit detected (${uniqueFiles.length} files): scope too broad for local-editor`
388
+ : 'task contains high-entropy keywords (design/plan/architect/investigate)',
389
+ 'complexity hint indicates multi-step or open-ended work',
390
+ 'main agent required for full reasoning and judgment',
391
+ ];
392
+ }
393
+ case 'ambiguous_scope':
394
+ return [
395
+ 'task description too vague or generic',
396
+ 'task intent not provided or unclear',
397
+ 'open-ended question words detected',
398
+ 'main agent must clarify scope before delegation',
399
+ ];
400
+ case 'profile_mismatch':
401
+ return [
402
+ 'task natural profile incompatible with requested target profile',
403
+ 'main agent must re-route or select a compatible profile',
404
+ ];
405
+
406
+ case 'deployment_unavailable':
407
+ return [
408
+ 'no enabled deployment found for target profile',
409
+ 'routing may be disabled in deployment registry',
410
+ 'main agent must handle task directly',
411
+ ];
412
+ }
413
+ }
414
+
415
+ // ---------------------------------------------------------------------------
416
+ // Public API
417
+ // ---------------------------------------------------------------------------
418
+
419
+ /**
420
+ * Classify a task and produce a routing decision.
421
+ *
422
+ * This is the main entry point for routing policy evaluation.
423
+ * It:
424
+ * 1. Classifies the task kind based on keywords and heuristics
425
+ * 2. Checks deployment availability for the target profile
426
+ * 3. Returns a fully explainable RoutingDecision
427
+ *
428
+ * @param input - The routing input describing the task
429
+ * @param stateDir - Workspace state directory (for deployment registry lookup)
430
+ * @returns RoutingDecision with classification, reason, blockers, and routing verdict
431
+ */
432
+ export function classifyTask(
433
+ input: RoutingInput,
434
+ stateDir: string
435
+ ): RoutingDecision {
436
+ // --- Determine the raw task classification ---
437
+ const classification = classifyTaskKind(input);
438
+
439
+ // --- Determine the target profile ---
440
+ // If input specifies a target, use it. Otherwise, pick based on classification.
441
+ // NOTE: When explicitly specified, we must validate profile-task compatibility below.
442
+ const targetProfile: WorkerProfile | null =
443
+ input.targetProfile ??
444
+ (classification === 'reader_eligible'
445
+ ? 'local-reader'
446
+ : classification === 'editor_eligible'
447
+ ? 'local-editor'
448
+ : null);
449
+
450
+ // --- Profile-task compatibility check ---
451
+ // Only applies when input.targetProfile is EXPLICITLY set.
452
+ // When auto-derived (input.targetProfile is null), compatibility is already
453
+ // guaranteed by the auto-derivation logic above (reader_eligible → local-reader).
454
+ // This check prevents routing a reader task to an editor profile (or vice versa)
455
+ // when the caller explicitly requests the wrong profile.
456
+ const isProfileCompatible =
457
+ input.targetProfile === undefined
458
+ ? true // Auto-derived profile is always compatible by construction
459
+ : targetProfile === 'local-reader'
460
+ ? classification === 'reader_eligible'
461
+ : targetProfile === 'local-editor'
462
+ ? classification === 'editor_eligible'
463
+ : false;
464
+
465
+ // --- Deployment availability check ---
466
+ let deploymentCheck: RoutingDecision['deploymentCheck'] = {
467
+ performed: false,
468
+ profileAvailable: false,
469
+ routingEnabled: false,
470
+ checkpointDeployable: false,
471
+ };
472
+
473
+ if (targetProfile) {
474
+ const deployment = getDeployment(stateDir, targetProfile);
475
+ const activeCheckpointId = deployment?.activeCheckpointId ?? null;
476
+ // Re-check deployability on every routing decision — a checkpoint may have been revoked
477
+ const checkpointDeployable = activeCheckpointId
478
+ ? isCheckpointDeployable(stateDir, activeCheckpointId)
479
+ : false;
480
+ deploymentCheck = {
481
+ performed: true,
482
+ profileAvailable: deployment !== null,
483
+ routingEnabled: isRoutingEnabledForProfile(stateDir, targetProfile),
484
+ checkpointDeployable,
485
+ };
486
+ }
487
+
488
+ // --- Build the decision ---
489
+ const blockers = buildBlockers(classification, input);
490
+ const reason = buildReason(classification, input);
491
+
492
+ // FAIL-CLOSED: route_local only if:
493
+ // 1. Classification is eligible (reader_eligible or editor_eligible)
494
+ // 2. A target profile was identified
495
+ // 3. The task's natural profile is compatible with the target profile
496
+ // 4. Deployment is available and routing is enabled
497
+ const isEligibleForRouting =
498
+ (classification === 'reader_eligible' || classification === 'editor_eligible') &&
499
+ targetProfile !== null &&
500
+ isProfileCompatible &&
501
+ deploymentCheck.routingEnabled;
502
+
503
+ const decision: RoutingDecision['decision'] = isEligibleForRouting
504
+ ? 'route_local'
505
+ : 'stay_main';
506
+
507
+ // Derive the final classification — preserves the root cause of stay_main:
508
+ // - profile_mismatch: task would be eligible but wrong profile requested
509
+ // - deployment_unavailable: eligible and compatible but no routing enabled
510
+ // - raw classification: blocked by high_entropy / risk / ambiguous
511
+ const isEligible = classification === 'reader_eligible' || classification === 'editor_eligible';
512
+ const finalClassification: RoutingDecision['classification'] =
513
+ isEligibleForRouting
514
+ ? classification
515
+ : isEligible && targetProfile !== null && !isProfileCompatible
516
+ ? 'profile_mismatch'
517
+ : isEligible
518
+ ? 'deployment_unavailable'
519
+ : classification;
520
+
521
+ // Build explainability fields specific to the stay_main reason
522
+ let finalReason = reason;
523
+ let finalBlockers = blockers;
524
+
525
+ if (decision === 'stay_main') {
526
+ if (finalClassification === 'profile_mismatch') {
527
+ const wanted = classification === 'reader_eligible' ? 'local-reader' : 'local-editor';
528
+ finalReason = `Task is ${classification} but was explicitly targeted at ${targetProfile}. ` +
529
+ `Routing requires "${wanted}" profile. Ensure the task intent matches the requested profile.`;
530
+ finalBlockers = [
531
+ `profile mismatch: task is ${classification} but targetProfile is ${targetProfile}`,
532
+ `required profile: ${wanted}`,
533
+ ];
534
+ } else if (finalClassification === 'deployment_unavailable') {
535
+ if (!deploymentCheck.performed) {
536
+ finalReason = reason;
537
+ } else if (!deploymentCheck.profileAvailable) {
538
+ finalReason = `Task is ${classification} but no deployment exists for ${targetProfile}. ` +
539
+ `Bind a checkpoint via bindCheckpointToWorkerProfile() and enable routing.`;
540
+ finalBlockers = [`no deployment found for profile: ${targetProfile}`];
541
+ } else if (!deploymentCheck.checkpointDeployable) {
542
+ finalReason = `Task is ${classification} but the active checkpoint has been revoked (no longer deployable). ` +
543
+ `Re-bind a passing checkpoint or re-evaluate the current one.`;
544
+ finalBlockers = [
545
+ `active checkpoint is no longer deployable: ${targetProfile}`,
546
+ 'revoked checkpoints must not be used for routing',
547
+ ];
548
+ } else if (!deploymentCheck.routingEnabled) {
549
+ finalReason = `Task is ${classification} and deployment exists for ${targetProfile} but routing is not enabled. ` +
550
+ `Enable routing via enableRoutingForProfile() in the deployment registry.`;
551
+ finalBlockers = [`routing is disabled for profile: ${targetProfile}`];
552
+ }
553
+ }
554
+ }
555
+
556
+ // --- Get active checkpoint ID and state for shadow observation integration ---
557
+ let activeCheckpointId: string | null = null;
558
+ let activeCheckpointState: 'promotable' | 'shadow_ready' | 'candidate_only' | null = null;
559
+
560
+ if (targetProfile && deploymentCheck.performed) {
561
+ const deployment = getDeployment(stateDir, targetProfile);
562
+ activeCheckpointId = deployment?.activeCheckpointId ?? null;
563
+ if (activeCheckpointId) {
564
+ const promotionState = getPromotionState(stateDir, activeCheckpointId);
565
+ if (promotionState === 'shadow_ready' || promotionState === 'promotable' || promotionState === 'candidate_only') {
566
+ activeCheckpointState = promotionState;
567
+ }
568
+ }
569
+ }
570
+
571
+ return {
572
+ decision,
573
+ targetProfile: decision === 'route_local' ? targetProfile : null,
574
+ classification: finalClassification,
575
+ reason: finalReason,
576
+ blockers: decision === 'stay_main' ? finalBlockers : [],
577
+ deploymentCheck,
578
+ activeCheckpointId,
579
+ activeCheckpointState: activeCheckpointState ?? undefined,
580
+ shadowObservationId: undefined,
581
+ };
582
+ }
583
+
584
+ /**
585
+ * Convenience: check if a specific profile can handle a task.
586
+ * Equivalent to calling classifyTask with targetProfile set.
587
+ */
588
+ export function canRouteToProfile(
589
+ input: RoutingInput,
590
+ stateDir: string,
591
+ profile: WorkerProfile
592
+ ): boolean {
593
+ const decision = classifyTask({ ...input, targetProfile: profile }, stateDir);
594
+ return decision.decision === 'route_local';
595
+ }
596
+
597
+ // ---------------------------------------------------------------------------
598
+ // Read-Only Query Helpers
599
+ // ---------------------------------------------------------------------------
600
+
601
+ /**
602
+ * Check if any local worker routing is currently enabled for any profile.
603
+ */
604
+ export function isAnyLocalRoutingEnabled(stateDir: string): boolean {
605
+ return isRoutingEnabledForProfile(stateDir, 'local-reader') ||
606
+ isRoutingEnabledForProfile(stateDir, 'local-editor');
607
+ }
608
+
609
+ /**
610
+ * List all profiles that currently have routing enabled.
611
+ */
612
+ export function listEnabledProfiles(stateDir: string): WorkerProfile[] {
613
+ const enabled: WorkerProfile[] = [];
614
+ if (isRoutingEnabledForProfile(stateDir, 'local-reader')) enabled.push('local-reader');
615
+ if (isRoutingEnabledForProfile(stateDir, 'local-editor')) enabled.push('local-editor');
616
+ return enabled;
617
+ }