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,147 @@
1
+ /**
2
+ * Production Compatibility Tests
3
+ *
4
+ * These tests validate that new code works correctly with production data patterns.
5
+ * Run with: npm test -- tests/fixtures/production-compatibility.test.ts
6
+ *
7
+ * NOTE: These tests require access to ~/.openclaw directory (production data)
8
+ */
9
+
10
+ import * as fs from 'fs';
11
+ import * as path from 'path';
12
+ import { describe, expect, it, beforeAll } from 'vitest';
13
+ import {
14
+ loadProductionPainFlag,
15
+ loadProductionEvolutionQueue,
16
+ loadProductionPainCandidates,
17
+ generateTestFixtureFromProduction,
18
+ validateProductionCompatibility,
19
+ createMockQueueItem,
20
+ createMockPainFlag,
21
+ PRODUCTION_FIXTURES,
22
+ } from './production-mock-generator.js';
23
+
24
+ // Skip tests if production data not available
25
+ const hasProductionData = fs.existsSync(PRODUCTION_FIXTURES.STATE_DIR);
26
+
27
+ describe.skipIf(!hasProductionData)('Production Data Compatibility', () => {
28
+ describe('Data Loading', () => {
29
+ it('should load production pain_flag', () => {
30
+ const painFlag = loadProductionPainFlag();
31
+ // May be null if no active pain
32
+ if (painFlag) {
33
+ expect(painFlag).toHaveProperty('score');
34
+ expect(painFlag).toHaveProperty('source');
35
+ expect(painFlag).toHaveProperty('reason');
36
+ }
37
+ });
38
+
39
+ it('should load production evolution_queue', () => {
40
+ const queue = loadProductionEvolutionQueue();
41
+ expect(Array.isArray(queue)).toBe(true);
42
+
43
+ if (queue.length > 0) {
44
+ const item = queue[0];
45
+ expect(item).toHaveProperty('id');
46
+ expect(item).toHaveProperty('score');
47
+ expect(item).toHaveProperty('status');
48
+ expect(['pending', 'in_progress', 'completed', 'resolved']).toContain(item.status);
49
+ }
50
+ });
51
+
52
+ it('should load production pain_candidates', () => {
53
+ const candidates = loadProductionPainCandidates();
54
+ expect(typeof candidates).toBe('object');
55
+ });
56
+ });
57
+
58
+ describe('New Field Compatibility', () => {
59
+ it('should detect missing session_id in pain_flag (expected: fails until production updated)', () => {
60
+ const result = validateProductionCompatibility();
61
+
62
+ // This test documents the current state - not a failure
63
+ console.log('Compatibility issues:', result.issues);
64
+ console.log('Production data:', result.productionData);
65
+
66
+ // New fields are expected to be missing in production
67
+ if (!result.compatible) {
68
+ console.log('⚠️ Production data does not have new fields (session_id, agent_id)');
69
+ console.log(' This is expected after code changes. Production will be updated on next pain event.');
70
+ }
71
+ });
72
+
73
+ it('should handle queue items without session_id gracefully', () => {
74
+ const queue = loadProductionEvolutionQueue();
75
+
76
+ for (const item of queue.slice(0, 5)) {
77
+ // Code should handle missing session_id/agent_id
78
+ expect(() => {
79
+ const normalized = {
80
+ ...item,
81
+ session_id: item.session_id || undefined,
82
+ agent_id: item.agent_id || undefined,
83
+ };
84
+ return normalized;
85
+ }).not.toThrow();
86
+ }
87
+ });
88
+ });
89
+
90
+ describe('Pattern Extraction', () => {
91
+ it('should extract patterns from production data', () => {
92
+ const fixture = generateTestFixtureFromProduction();
93
+
94
+ expect(fixture.patterns).toBeDefined();
95
+ expect(fixture.patterns.painSources.length).toBeGreaterThanOrEqual(0);
96
+
97
+ console.log('Pain sources:', fixture.patterns.painSources);
98
+ console.log('Score distribution:', fixture.patterns.scoreDistribution);
99
+ console.log('Status distribution:', fixture.patterns.statusDistribution);
100
+ });
101
+ });
102
+
103
+ describe('Mock Generation', () => {
104
+ it('should create realistic mock queue items', () => {
105
+ const item = createMockQueueItem({
106
+ session_id: 'test-session-123',
107
+ agent_id: 'main',
108
+ });
109
+
110
+ expect(item.session_id).toBe('test-session-123');
111
+ expect(item.agent_id).toBe('main');
112
+ expect(item.id).toBeDefined();
113
+ expect(item.timestamp).toBeDefined();
114
+ });
115
+
116
+ it('should create realistic mock pain flags', () => {
117
+ const flag = createMockPainFlag({
118
+ session_id: 'test-session-456',
119
+ agent_id: 'builder',
120
+ });
121
+
122
+ expect(flag.session_id).toBe('test-session-456');
123
+ expect(flag.agent_id).toBe('builder');
124
+ expect(flag.time).toBeDefined();
125
+ });
126
+ });
127
+ });
128
+
129
+ describe('Edge Cases from Production', () => {
130
+ it('should handle empty evolution queue', () => {
131
+ const item = createMockQueueItem(); // Uses fallback template
132
+ expect(item).toBeDefined();
133
+ expect(item.id).toBeDefined();
134
+ });
135
+
136
+ it('should handle very long reason strings', () => {
137
+ const longReason = 'A'.repeat(10000);
138
+ const item = createMockQueueItem({ reason: longReason });
139
+ expect(item.reason).toBe(longReason);
140
+ });
141
+
142
+ it('should handle special characters in reason', () => {
143
+ const specialReason = 'Error: "quotes" and \n newlines \t tabs';
144
+ const item = createMockQueueItem({ reason: specialReason });
145
+ expect(item.reason).toBe(specialReason);
146
+ });
147
+ });
@@ -0,0 +1,282 @@
1
+ /**
2
+ * Production Mock Data Generator
3
+ *
4
+ * Extracts patterns from production data to create realistic test fixtures.
5
+ * This ensures tests match real-world scenarios and catches edge cases.
6
+ */
7
+
8
+ import * as fs from 'fs';
9
+ import * as path from 'path';
10
+
11
+ // Production data paths
12
+ const OPENCLAW_HOME = process.env.HOME + '/.openclaw';
13
+ const WORKSPACE_MAIN = OPENCLAW_HOME + '/workspace-main';
14
+ const STATE_DIR = WORKSPACE_MAIN + '/.state';
15
+
16
+ // Types extracted from production
17
+ export interface ProductionPainFlag {
18
+ is_risky: string;
19
+ reason: string;
20
+ score: string;
21
+ source: string;
22
+ time: string;
23
+ status?: string;
24
+ task_id?: string;
25
+ session_id?: string; // New field
26
+ agent_id?: string; // New field
27
+ trace_id?: string; // From recent changes
28
+ }
29
+
30
+ export interface ProductionEvolutionQueueItem {
31
+ id: string;
32
+ score: number;
33
+ source: string;
34
+ reason: string;
35
+ trigger_text_preview: string;
36
+ timestamp: string;
37
+ enqueued_at: string;
38
+ status: 'pending' | 'in_progress' | 'completed' | 'resolved';
39
+ task?: string;
40
+ started_at?: string;
41
+ completed_at?: string;
42
+ resolved_at?: string;
43
+ assigned_session_key?: string;
44
+ resolution?: string;
45
+ session_id?: string; // New field
46
+ agent_id?: string; // New field
47
+ }
48
+
49
+ export interface ProductionPainCandidate {
50
+ count: number;
51
+ status?: string;
52
+ firstSeen: string;
53
+ lastSeen?: string;
54
+ samples: string[];
55
+ }
56
+
57
+ /**
58
+ * Load real pain_flag from production
59
+ */
60
+ export function loadProductionPainFlag(): ProductionPainFlag | null {
61
+ const painFlagPath = path.join(STATE_DIR, '.pain_flag');
62
+ if (!fs.existsSync(painFlagPath)) return null;
63
+
64
+ const content = fs.readFileSync(painFlagPath, 'utf8');
65
+ const result: ProductionPainFlag = {} as ProductionPainFlag;
66
+
67
+ for (const line of content.split('\n')) {
68
+ const match = line.match(/^(\w+):\s*(.+)$/);
69
+ if (match) {
70
+ (result as any)[match[1]] = match[2];
71
+ }
72
+ }
73
+
74
+ return result;
75
+ }
76
+
77
+ /**
78
+ * Load real evolution queue from production
79
+ */
80
+ export function loadProductionEvolutionQueue(): ProductionEvolutionQueueItem[] {
81
+ const queuePath = path.join(STATE_DIR, 'evolution_queue.json');
82
+ if (!fs.existsSync(queuePath)) return [];
83
+
84
+ try {
85
+ return JSON.parse(fs.readFileSync(queuePath, 'utf8'));
86
+ } catch {
87
+ return [];
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Load real pain candidates from production
93
+ */
94
+ export function loadProductionPainCandidates(): Record<string, ProductionPainCandidate> {
95
+ const candidatesPath = path.join(STATE_DIR, 'pain_candidates.json');
96
+ if (!fs.existsSync(candidatesPath)) return {};
97
+
98
+ try {
99
+ const data = JSON.parse(fs.readFileSync(candidatesPath, 'utf8'));
100
+ return data.candidates || {};
101
+ } catch {
102
+ return {};
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Sample a JSONL session file for message patterns
108
+ */
109
+ export function sampleSessionMessages(
110
+ sessionId: string,
111
+ agentId: string = 'main',
112
+ limit: number = 10
113
+ ): Array<{ role: string; content: string; timestamp: number }> {
114
+ const sessionPath = path.join(OPENCLAW_HOME, 'agents', agentId, 'sessions', `${sessionId}.jsonl`);
115
+ if (!fs.existsSync(sessionPath)) return [];
116
+
117
+ const messages: Array<{ role: string; content: string; timestamp: number }> = [];
118
+
119
+ try {
120
+ const lines = fs.readFileSync(sessionPath, 'utf8').split('\n').filter(Boolean);
121
+ for (const line of lines.slice(0, limit * 3)) { // Read more lines to get enough messages
122
+ try {
123
+ const entry = JSON.parse(line);
124
+ if (entry.type === 'message' && entry.message) {
125
+ const msg = entry.message;
126
+ let content = '';
127
+ if (typeof msg.content === 'string') {
128
+ content = msg.content;
129
+ } else if (Array.isArray(msg.content)) {
130
+ content = msg.content
131
+ .filter((c: any) => c.type === 'text')
132
+ .map((c: any) => c.text)
133
+ .join('\n');
134
+ }
135
+
136
+ // Truncate to reasonable size
137
+ if (content.length > 500) {
138
+ content = content.slice(0, 500) + '...';
139
+ }
140
+
141
+ messages.push({
142
+ role: msg.role,
143
+ content,
144
+ timestamp: entry.timestamp || Date.now(),
145
+ });
146
+
147
+ if (messages.length >= limit) break;
148
+ }
149
+ } catch {
150
+ // Skip malformed lines
151
+ }
152
+ }
153
+ } catch {
154
+ return [];
155
+ }
156
+
157
+ return messages;
158
+ }
159
+
160
+ /**
161
+ * Generate realistic test fixtures from production patterns
162
+ */
163
+ export function generateTestFixtureFromProduction() {
164
+ const painFlag = loadProductionPainFlag();
165
+ const queue = loadProductionEvolutionQueue();
166
+ const candidates = loadProductionPainCandidates();
167
+
168
+ // Extract patterns
169
+ const patterns = {
170
+ // Pain sources seen in production
171
+ painSources: [...new Set(queue.map(q => q.source))],
172
+
173
+ // Common error patterns
174
+ errorPatterns: queue.map(q => ({
175
+ type: q.source,
176
+ reasonPreview: q.reason.slice(0, 100),
177
+ })),
178
+
179
+ // Score distribution
180
+ scoreDistribution: queue.reduce((acc, q) => {
181
+ acc[q.score] = (acc[q.score] || 0) + 1;
182
+ return acc;
183
+ }, {} as Record<number, number>),
184
+
185
+ // Status distribution
186
+ statusDistribution: queue.reduce((acc, q) => {
187
+ acc[q.status] = (acc[q.status] || 0) + 1;
188
+ return acc;
189
+ }, {} as Record<string, number>),
190
+ };
191
+
192
+ return {
193
+ painFlag,
194
+ queue,
195
+ candidates,
196
+ patterns,
197
+ };
198
+ }
199
+
200
+ /**
201
+ * Create a mock evolution queue item based on production patterns
202
+ */
203
+ export function createMockQueueItem(overrides: Partial<ProductionEvolutionQueueItem> = {}): ProductionEvolutionQueueItem {
204
+ const queue = loadProductionEvolutionQueue();
205
+ const template = queue[0] || {
206
+ id: 'test-001',
207
+ score: 50,
208
+ source: 'tool_failure',
209
+ reason: 'Tool write failed on test.md. Error: Test error.',
210
+ trigger_text_preview: '',
211
+ timestamp: new Date().toISOString(),
212
+ enqueued_at: new Date().toISOString(),
213
+ status: 'pending',
214
+ };
215
+
216
+ return {
217
+ ...template,
218
+ ...overrides,
219
+ id: overrides.id || `test-${Date.now().toString(36)}`,
220
+ timestamp: overrides.timestamp || new Date().toISOString(),
221
+ enqueued_at: overrides.enqueued_at || new Date().toISOString(),
222
+ };
223
+ }
224
+
225
+ /**
226
+ * Create a mock pain flag based on production patterns
227
+ */
228
+ export function createMockPainFlag(overrides: Partial<ProductionPainFlag> = {}): ProductionPainFlag {
229
+ const realFlag = loadProductionPainFlag();
230
+ const template = realFlag || {
231
+ is_risky: 'false',
232
+ reason: 'Test pain signal',
233
+ score: '50',
234
+ source: 'tool_failure',
235
+ time: new Date().toISOString(),
236
+ };
237
+
238
+ return {
239
+ ...template,
240
+ ...overrides,
241
+ time: overrides.time || new Date().toISOString(),
242
+ };
243
+ }
244
+
245
+ /**
246
+ * Validate that new code handles production data correctly
247
+ */
248
+ export function validateProductionCompatibility() {
249
+ const issues: string[] = [];
250
+
251
+ // Check if pain_flag has session_id/agent_id (new fields)
252
+ const painFlag = loadProductionPainFlag();
253
+ if (painFlag && !painFlag.session_id) {
254
+ issues.push('pain_flag missing session_id (new field not yet in production)');
255
+ }
256
+ if (painFlag && !painFlag.agent_id) {
257
+ issues.push('pain_flag missing agent_id (new field not yet in production)');
258
+ }
259
+
260
+ // Check if queue items have session_id/agent_id
261
+ const queue = loadProductionEvolutionQueue();
262
+ const missingSessionId = queue.filter(q => !q.session_id);
263
+ if (missingSessionId.length > 0) {
264
+ issues.push(`evolution_queue: ${missingSessionId.length}/${queue.length} items missing session_id`);
265
+ }
266
+
267
+ return {
268
+ compatible: issues.length === 0,
269
+ issues,
270
+ productionData: {
271
+ painFlag,
272
+ queueCount: queue.length,
273
+ },
274
+ };
275
+ }
276
+
277
+ // Export for use in tests
278
+ export const PRODUCTION_FIXTURES = {
279
+ OPENCLAW_HOME,
280
+ WORKSPACE_MAIN,
281
+ STATE_DIR,
282
+ };
@@ -0,0 +1,137 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { analyzeBashCommand } from '../../src/hooks/bash-risk.js';
3
+
4
+ /**
5
+ * Integration tests for bash-risk module
6
+ * Tests zero-width character detection and command chain analysis
7
+ */
8
+ describe('Bash Risk Analysis - Integration', () => {
9
+ describe('Zero-width character detection', () => {
10
+ it('should block commands with zero-width space (U+200B)', () => {
11
+ // Zero-width space injected between characters
12
+ const maliciousCmd = 'rm\u200B -rf /';
13
+ const result = analyzeBashCommand(maliciousCmd, [], [], { warn: vi.fn() });
14
+ expect(result).toBe('dangerous');
15
+ });
16
+
17
+ it('should block commands with zero-width non-joiner (U+200C)', () => {
18
+ const maliciousCmd = 'rm\u200C -rf /';
19
+ const result = analyzeBashCommand(maliciousCmd, [], [], { warn: vi.fn() });
20
+ expect(result).toBe('dangerous');
21
+ });
22
+
23
+ it('should block commands with zero-width joiner (U+200D)', () => {
24
+ const maliciousCmd = 'rm\u200D -rf /';
25
+ const result = analyzeBashCommand(maliciousCmd, [], [], { warn: vi.fn() });
26
+ expect(result).toBe('dangerous');
27
+ });
28
+
29
+ it('should block commands with word joiner (U+2060)', () => {
30
+ const maliciousCmd = 'rm\u2060 -rf /';
31
+ const result = analyzeBashCommand(maliciousCmd, [], [], { warn: vi.fn() });
32
+ expect(result).toBe('dangerous');
33
+ });
34
+
35
+ it('should block commands with zero-width invisible separator (U+FEFF)', () => {
36
+ const maliciousCmd = 'rm\uFEFF -rf /';
37
+ const result = analyzeBashCommand(maliciousCmd, [], [], { warn: vi.fn() });
38
+ expect(result).toBe('dangerous');
39
+ });
40
+
41
+ it('should block commands with multiple zero-width characters', () => {
42
+ const maliciousCmd = 'rm\u200B\u200C\u200D -rf /';
43
+ const result = analyzeBashCommand(maliciousCmd, [], [], { warn: vi.fn() });
44
+ expect(result).toBe('dangerous');
45
+ });
46
+
47
+ it('should allow normal commands without zero-width characters', () => {
48
+ const normalCmd = 'rm -rf /tmp/test';
49
+ const result = analyzeBashCommand(normalCmd, ['^rm\\s'], [], { warn: vi.fn() });
50
+ expect(result).toBe('safe');
51
+ });
52
+
53
+ it('should detect zero-width characters hidden in normal-looking commands', () => {
54
+ // Command looks like 'git status' but contains hidden chars
55
+ const hiddenCmd = 'git\u200B status';
56
+ const result = analyzeBashCommand(hiddenCmd, [], [], { warn: vi.fn() });
57
+ expect(result).toBe('dangerous');
58
+ });
59
+ });
60
+
61
+ describe('Cyrillic homograph attack detection', () => {
62
+ it('should de-obfuscate Cyrillic characters and match dangerous patterns', () => {
63
+ // Cyrillic 'р' (U+0440) looks like Latin 'p' - 'g\u0440ush' → 'gpush' then 'push'
64
+ // After toLowerCase + deobfuscation: 'gpush' won't match dangerous, but 'push' alone might
65
+ // Actually: '\u0440' maps to 'p' so 'g\u0440ush' → 'gp' + 'ush' = 'gpush'
66
+ // Let's use Cyrillic 'е' which maps to 'e': '\u0435' → 'e'
67
+ const cyrillicCmd = 'r\u0435m -rf /tmp';
68
+ const result = analyzeBashCommand(cyrillicCmd, [], ['^rem'], { warn: vi.fn() });
69
+ expect(result).toBe('dangerous');
70
+ });
71
+
72
+ it('should handle Cyrillic а being converted to Latin a', () => {
73
+ // Cyrillic 'а' (U+0430) maps to Latin 'a' - 's\u0430do' → 'sado'
74
+ // After de-obfuscation: 'sado' - doesn't match dangerous patterns
75
+ const mixedCmd = 's\u0430do apt update';
76
+ const result = analyzeBashCommand(mixedCmd, [], ['^sudo'], { warn: vi.fn() });
77
+ expect(result).toBe('normal');
78
+ });
79
+ });
80
+
81
+ describe('Command chain tokenization', () => {
82
+ it('should detect dangerous commands in chains using &&', () => {
83
+ const chainCmd = 'echo "hello" && rm -rf /tmp/test';
84
+ const result = analyzeBashCommand(chainCmd, [], ['rm\\s+-rf'], { warn: vi.fn() });
85
+ expect(result).toBe('dangerous');
86
+ });
87
+
88
+ it('should detect dangerous commands in chains using ||', () => {
89
+ const chainCmd = 'ls || rm -rf /tmp/test';
90
+ const result = analyzeBashCommand(chainCmd, [], ['rm\\s+-rf'], { warn: vi.fn() });
91
+ expect(result).toBe('dangerous');
92
+ });
93
+
94
+ it('should detect dangerous commands in chains using ;', () => {
95
+ const chainCmd = 'ls ; rm -rf /tmp/test';
96
+ const result = analyzeBashCommand(chainCmd, [], ['rm\\s+-rf'], { warn: vi.fn() });
97
+ expect(result).toBe('dangerous');
98
+ });
99
+
100
+ it('should return safe if no segment matches dangerous patterns', () => {
101
+ const safeChain = 'echo "hello" && echo "world"';
102
+ const result = analyzeBashCommand(safeChain, [], ['rm\\s+-rf'], { warn: vi.fn() });
103
+ expect(result).toBe('normal');
104
+ });
105
+ });
106
+
107
+ describe('Fail-closed behavior', () => {
108
+ it('should return dangerous for invalid regex in dangerousPatterns', () => {
109
+ const cmd = 'ls';
110
+ const result = analyzeBashCommand(cmd, [], ['[invalid'], { warn: vi.fn() });
111
+ expect(result).toBe('dangerous'); // Fail-closed
112
+ });
113
+
114
+ it('should ignore invalid regex in safePatterns', () => {
115
+ const cmd = 'echo hello';
116
+ const warnFn = vi.fn();
117
+ const result = analyzeBashCommand(cmd, ['[invalid'], [], { warn: warnFn });
118
+ // Should not crash, should return normal (not all segments safe)
119
+ expect(result).toBe('normal');
120
+ expect(warnFn).toHaveBeenCalled();
121
+ });
122
+ });
123
+
124
+ describe('Safe pattern override', () => {
125
+ it('should return safe when all segments match safe patterns', () => {
126
+ const safeCmd = 'git status';
127
+ const result = analyzeBashCommand(safeCmd, ['^git\\s+status', '^ls'], []);
128
+ expect(result).toBe('safe');
129
+ });
130
+
131
+ it('should return normal when not all segments are safe', () => {
132
+ const mixedCmd = 'git status && rm -rf /tmp';
133
+ const result = analyzeBashCommand(mixedCmd, ['^git\\s+status'], ['rm\\s+-rf']);
134
+ expect(result).toBe('dangerous');
135
+ });
136
+ });
137
+ });
@@ -0,0 +1,81 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { analyzeBashCommand } from '../../src/hooks/bash-risk.js';
3
+
4
+ describe('analyzeBashCommand', () => {
5
+ it('should return safe for commands matching safe patterns', () => {
6
+ const result = analyzeBashCommand('npm install lodash', ['^npm\\s+install'], []);
7
+ expect(result).toBe('safe');
8
+ });
9
+
10
+ it('should return dangerous for commands matching dangerous patterns', () => {
11
+ const result = analyzeBashCommand('rm -rf /', [], ['rm\\s+.*-rf']);
12
+ expect(result).toBe('dangerous');
13
+ });
14
+
15
+ it('should return normal for commands not in safe/dangerous lists', () => {
16
+ const result = analyzeBashCommand('npm install lodash', [], []);
17
+ expect(result).toBe('normal');
18
+ });
19
+
20
+ it('should de-obfuscate Cyrillic lookalikes', () => {
21
+ // Using Cyrillic 'rеset' (with Cyrillic 'е' U+0435) instead of 'reset'
22
+ // This should de-obfuscate to 'git reset --hard' and match the dangerous pattern
23
+ const result = analyzeBashCommand('git rеset --hard', [], ['git\\s+(push\\s+.*--force|reset\\s+--hard|clean\\s+-fd)']);
24
+ expect(result).toBe('dangerous');
25
+ });
26
+
27
+ it('should tokenize command chains', () => {
28
+ const result = analyzeBashCommand('npm install && npm test', ['^npm\\s+install'], ['npm\\s+publish']);
29
+ expect(result).toBe('normal');
30
+ });
31
+
32
+ it('should fail-closed on invalid dangerous regex', () => {
33
+ const mockLogger = { warn: vi.fn() };
34
+ const result = analyzeBashCommand('echo test', ['^echo'], ['invalid('], mockLogger);
35
+ expect(result).toBe('dangerous'); // Fail-closed behavior
36
+ expect(mockLogger.warn).toHaveBeenCalledWith(
37
+ expect.stringContaining('Invalid dangerous bash regex')
38
+ );
39
+ });
40
+
41
+ it('should ignore safe pattern on invalid safe regex', () => {
42
+ const mockLogger = { warn: vi.fn() };
43
+ const result = analyzeBashCommand('echo test', ['invalid('], [], mockLogger);
44
+ expect(result).toBe('normal'); // Not safe because safe pattern is invalid and ignored
45
+ expect(mockLogger.warn).toHaveBeenCalledWith(
46
+ expect.stringContaining('Invalid safe bash regex')
47
+ );
48
+ });
49
+
50
+ it('should strip $() from commands before pattern matching', () => {
51
+ const result = analyzeBashCommand('$(npm install)', ['^npm\\s+install'], []);
52
+ expect(result).toBe('safe');
53
+ });
54
+
55
+ it('should strip backticks from commands before pattern matching', () => {
56
+ const result = analyzeBashCommand('`npm install`', ['^npm\\s+install'], []);
57
+ expect(result).toBe('safe');
58
+ });
59
+
60
+ it('should handle command chains with semicolon separator', () => {
61
+ const result = analyzeBashCommand('npm install ; npm test', ['^npm\\s+install'], ['rm\\s+']);
62
+ expect(result).toBe('normal'); // Second command not safe
63
+ });
64
+
65
+ it('should handle command chains with OR separator', () => {
66
+ const result = analyzeBashCommand('npm install || npm test', ['^npm\\s+install'], ['rm\\s+']);
67
+ expect(result).toBe('normal');
68
+ });
69
+
70
+ it('should convert uppercase Cyrillic to lowercase Latin', () => {
71
+ // Using uppercase Cyrillic 'Е' (U+0415) which should convert to 'e'
72
+ // 'git REsET --hard' should become 'git reset --hard' and match the dangerous pattern
73
+ const result = analyzeBashCommand('git rEsET --hard', [], ['git\\s+(push\\s+.*--force|reset\\s+--hard|clean\\s+-fd)']);
74
+ expect(result).toBe('dangerous');
75
+ });
76
+
77
+ it('should handle additional confusable Unicode characters', () => {
78
+ const result = analyzeBashCommand('del node_modules', [], ['rm\\s+']);
79
+ expect(result).toBe('normal'); // 'del' is not in dangerous patterns
80
+ });
81
+ });