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
@@ -1,149 +1,173 @@
1
- import { resolvePdPath } from './paths.js';
1
+ import { resolvePdPath, PD_FILES } from './paths.js';
2
2
  import { PathResolver } from './path-resolver.js';
3
3
  import { ConfigService } from './config-service.js';
4
- import { EventLogService } from './event-log.js';
4
+ import { PainConfig } from './config.js';
5
+ import { EventLogService, EventLog } from './event-log.js';
5
6
  import { DictionaryService } from './dictionary-service.js';
7
+ import { PainDictionary } from './dictionary.js';
6
8
  import { HygieneTracker } from './hygiene/tracker.js';
7
9
  import { EvolutionReducerImpl } from './evolution-reducer.js';
8
- import { TrajectoryRegistry } from './trajectory.js';
10
+ import { TrajectoryDatabase, TrajectoryRegistry, TrajectoryDatabaseOptions } from './trajectory.js';
11
+
9
12
  /**
10
13
  * WorkspaceContext - Centralized management of workspace-specific paths and services.
11
14
  * Implements a cached singleton pattern per workspace directory.
12
15
  */
13
16
  export class WorkspaceContext {
14
- static instances = new Map();
15
- static pathResolver = new PathResolver();
16
- workspaceDir;
17
- stateDir;
18
- _config;
19
- _eventLog;
20
- _dictionary;
21
- _hygiene;
22
- _evolutionReducer;
23
- _trajectory;
24
- constructor(workspaceDir, stateDir) {
17
+ private static instances = new Map<string, WorkspaceContext>();
18
+ private static pathResolver = new PathResolver();
19
+
20
+ public readonly workspaceDir: string;
21
+ public readonly stateDir: string;
22
+
23
+ private _config?: PainConfig;
24
+ private _eventLog?: EventLog;
25
+ private _dictionary?: PainDictionary;
26
+ private _hygiene?: HygieneTracker;
27
+ private _evolutionReducer?: EvolutionReducerImpl;
28
+ private _trajectory?: TrajectoryDatabase;
29
+
30
+ private constructor(workspaceDir: string, stateDir: string) {
25
31
  this.workspaceDir = workspaceDir;
26
32
  this.stateDir = stateDir;
27
33
  }
34
+
28
35
  /**
29
36
  * Governance configuration for this workspace.
30
37
  */
31
- get config() {
38
+ get config(): PainConfig {
32
39
  if (!this._config) {
33
40
  this._config = ConfigService.get(this.stateDir);
34
41
  }
35
42
  return this._config;
36
43
  }
44
+
37
45
  /**
38
46
  * Event logging service for this workspace.
39
47
  */
40
- get eventLog() {
48
+ get eventLog(): EventLog {
41
49
  if (!this._eventLog) {
42
50
  this._eventLog = EventLogService.get(this.stateDir);
43
51
  }
44
52
  return this._eventLog;
45
53
  }
54
+
46
55
  /**
47
56
  * Pain dictionary service for this workspace.
48
57
  */
49
- get dictionary() {
58
+ get dictionary(): PainDictionary {
50
59
  if (!this._dictionary) {
51
60
  this._dictionary = DictionaryService.get(this.stateDir);
52
61
  }
53
62
  return this._dictionary;
54
63
  }
64
+
55
65
  /**
56
66
  * Hygiene tracking service for this workspace.
57
67
  */
58
- get hygiene() {
68
+ get hygiene(): HygieneTracker {
59
69
  if (!this._hygiene) {
60
70
  this._hygiene = new HygieneTracker(this.stateDir);
61
71
  }
62
72
  return this._hygiene;
63
73
  }
74
+
75
+
64
76
  /**
65
77
  * Evolution reducer singleton for this workspace.
66
78
  */
67
- get evolutionReducer() {
79
+ get evolutionReducer(): EvolutionReducerImpl {
68
80
  if (!this._evolutionReducer) {
69
81
  this._evolutionReducer = new EvolutionReducerImpl({ workspaceDir: this.workspaceDir });
70
82
  }
71
83
  return this._evolutionReducer;
72
84
  }
85
+
73
86
  /**
74
87
  * Trajectory database for analytics and sample curation.
75
88
  */
76
- get trajectory() {
89
+ get trajectory(): TrajectoryDatabase {
77
90
  if (!this._trajectory) {
78
91
  this._trajectory = TrajectoryRegistry.get(this.workspaceDir, this.getTrajectoryOptions());
79
92
  }
80
93
  return this._trajectory;
81
94
  }
82
- getTrajectoryOptions() {
95
+
96
+ private getTrajectoryOptions(): Omit<TrajectoryDatabaseOptions, 'workspaceDir'> {
83
97
  const inlineThreshold = Number(this.config.get('trajectory.blob_inline_threshold_bytes'));
84
98
  const busyTimeoutMs = Number(this.config.get('trajectory.busy_timeout_ms'));
85
99
  const orphanBlobGraceDays = Number(this.config.get('trajectory.orphan_blob_grace_days'));
100
+
86
101
  return {
87
102
  blobInlineThresholdBytes: Number.isFinite(inlineThreshold) && inlineThreshold > 0 ? inlineThreshold : undefined,
88
103
  busyTimeoutMs: Number.isFinite(busyTimeoutMs) && busyTimeoutMs >= 0 ? busyTimeoutMs : undefined,
89
104
  orphanBlobGraceDays: Number.isFinite(orphanBlobGraceDays) && orphanBlobGraceDays >= 0 ? orphanBlobGraceDays : undefined,
90
105
  };
91
106
  }
107
+
92
108
  /**
93
109
  * Creates or retrieves a WorkspaceContext instance from an OpenClaw hook context.
94
110
  * Uses PathResolver to handle path normalization and fallback logic.
95
111
  * @throws Error if workspaceDir is missing and no fallback available.
96
112
  */
97
- static fromHookContext(ctx) {
113
+ static fromHookContext(ctx: any): WorkspaceContext {
98
114
  const logger = ctx.logger;
99
- const log = (msg) => logger?.info?.(msg) ?? console.log(msg);
100
- const logWarn = (msg) => logger?.warn?.(msg) ?? console.warn(msg);
115
+ const log = (msg: string) => logger?.info?.(msg);
116
+ const logWarn = (msg: string) => logger?.warn?.(msg);
117
+
101
118
  let workspaceDir = ctx.workspaceDir;
119
+
102
120
  if (!workspaceDir) {
103
121
  logWarn('[PD:WorkspaceContext] workspaceDir not provided in context, using PathResolver fallback');
104
122
  workspaceDir = this.pathResolver.getWorkspaceDir();
105
123
  log(`[PD:WorkspaceContext] Resolved workspaceDir to: ${workspaceDir}`);
106
- }
107
- else {
124
+ } else {
108
125
  const normalized = this.pathResolver.normalizeWorkspacePath(workspaceDir);
109
126
  if (normalized !== workspaceDir) {
110
127
  log(`[PD:WorkspaceContext] Normalized workspaceDir: ${workspaceDir} -> ${normalized}`);
111
128
  workspaceDir = normalized;
112
129
  }
113
130
  }
131
+
114
132
  const existing = this.instances.get(workspaceDir);
115
- if (existing)
116
- return existing;
133
+ if (existing) return existing;
134
+
117
135
  let stateDir = ctx.stateDir;
118
136
  if (!stateDir) {
119
137
  stateDir = resolvePdPath(workspaceDir, 'STATE_DIR');
120
138
  log(`[PD:WorkspaceContext] Computed stateDir: ${stateDir}`);
121
139
  }
140
+
122
141
  const instance = new WorkspaceContext(workspaceDir, stateDir);
123
142
  this.instances.set(workspaceDir, instance);
143
+
124
144
  log(`[PD:WorkspaceContext] Created new context for workspace: ${workspaceDir}`);
145
+
125
146
  return instance;
126
147
  }
148
+
127
149
  /**
128
150
  * Resolves a PD file path within the workspace.
129
151
  */
130
- resolve(fileKey) {
152
+ resolve(fileKey: keyof typeof PD_FILES): string {
131
153
  return resolvePdPath(this.workspaceDir, fileKey);
132
154
  }
155
+
133
156
  /**
134
157
  * Resets internal caches for services and paths.
135
158
  */
136
- invalidate() {
159
+ invalidate(): void {
137
160
  this._config = undefined;
138
161
  this._eventLog = undefined;
139
162
  this._dictionary = undefined;
140
163
  this._evolutionReducer = undefined;
141
164
  this._trajectory = undefined;
142
165
  }
166
+
143
167
  /**
144
168
  * Removes a workspace from the cache.
145
169
  */
146
- static dispose(workspaceDir) {
170
+ static dispose(workspaceDir: string): void {
147
171
  const normalized = this.pathResolver.normalizeWorkspacePath(workspaceDir);
148
172
  const instance = this.instances.get(normalized);
149
173
  if (instance) {
@@ -152,10 +176,11 @@ export class WorkspaceContext {
152
176
  }
153
177
  TrajectoryRegistry.dispose(normalized);
154
178
  }
179
+
155
180
  /**
156
181
  * Clears the instance cache (primarily for testing).
157
182
  */
158
- static clearCache() {
183
+ static clearCache(): void {
159
184
  for (const instance of this.instances.values()) {
160
185
  instance.invalidate();
161
186
  }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Bash Risk Analysis Module
3
+ *
4
+ * Analyzes bash command security risks and determines command categorization.
5
+ *
6
+ * **Responsibilities:**
7
+ * - De-obfuscate Unicode/Cyrillic lookalike characters (security bypass prevention)
8
+ * - Tokenize command chains to detect multi-command bypasses
9
+ * - Classify commands as: 'safe', 'dangerous', or 'normal'
10
+ * - Pattern matching against safe/dangerous regex patterns
11
+ * - Fail-closed behavior (invalid regex = dangerous)
12
+ *
13
+ * **Configuration:**
14
+ * - Bash safe patterns from gfi_gate.bash_safe_patterns
15
+ * - Bash dangerous patterns from gfi_gate.bash_dangerous_patterns
16
+ */
17
+
18
+ // TODO: Extract types from gate.ts related to bash risk analysis
19
+ export interface BashRiskConfig {
20
+ bash_safe_patterns?: string[];
21
+ bash_dangerous_patterns?: string[];
22
+ }
23
+
24
+ export type BashRiskLevel = 'safe' | 'dangerous' | 'normal';
25
+
26
+ /**
27
+ * Analyzes a bash command to determine its risk level.
28
+ *
29
+ * Implements security features:
30
+ * - Unicode/Cyrillic de-obfuscation to detect homograph attacks
31
+ * - Command chain tokenization to catch multi-command bypasses
32
+ * - Pattern matching against safe/dangerous regex patterns
33
+ * - Fail-closed behavior (invalid dangerous regex = dangerous)
34
+ *
35
+ * @param command - The bash command to analyze
36
+ * @param safePatterns - Regex patterns that indicate safe commands
37
+ * @param dangerousPatterns - Regex patterns that indicate dangerous commands
38
+ * @param logger - Optional logger for warnings about invalid patterns
39
+ * @returns The risk level: 'safe', 'dangerous', or 'normal'
40
+ */
41
+ export function analyzeBashCommand(
42
+ command: string,
43
+ safePatterns: string[],
44
+ dangerousPatterns: string[],
45
+ logger?: { warn?: (message: string) => void }
46
+ ): BashRiskLevel {
47
+ let normalizedCmd = command.trim().toLowerCase();
48
+
49
+ // Unicode de-obfuscation — convert Cyrillic/Unicode lookalikes to ASCII equivalents
50
+ // Common Cyrillic lookalikes that could bypass detection: аеорсух (Cyrillic) → aeopcyx (Latin)
51
+ const CYRILLIC_TO_LATIN: Record<string, string> = {
52
+ 'а': 'a', 'е': 'e', 'о': 'o', 'р': 'p', 'с': 'c', 'у': 'y', 'х': 'x',
53
+ 'А': 'a', 'Е': 'e', 'О': 'o', 'Р': 'p', 'С': 'c', 'У': 'y', 'Х': 'x',
54
+ // Additional confusable chars
55
+ 'і': 'i', 'ј': 'j', 'ѕ': 's', 'ԁ': 'd', 'ɡ': 'g', 'һ': 'h', 'ⅰ': 'i',
56
+ 'ƚ': 'l', 'м': 'm', 'п': 'n', 'ѵ': 'v', 'ѡ': 'w', 'ᴦ': 'r', 'ꜱ': 's',
57
+ };
58
+ normalizedCmd = normalizedCmd.replace(/[а-яА-Яіјѕԁɡһⅰƚмпеꜱѵѡᴦꜱ]/g, m => CYRILLIC_TO_LATIN[m] ?? m);
59
+
60
+ // Zero-width character detection — detect hidden characters that could bypass pattern matching
61
+ // Common zero-width characters used in command injection:
62
+ // - Zero-width space (U+200B)
63
+ // - Zero-width non-joiner (U+200C)
64
+ // - Zero-width joiner (U+200D)
65
+ // - Word joiner (U+2060)
66
+ // - Zero-width invisible separator (U+FEFF)
67
+ const ZERO_WIDTH_CHARS = /[\u200B\u200C\u200D\u2060\uFEFF]/g;
68
+ if (ZERO_WIDTH_CHARS.test(command)) {
69
+ logger?.warn?.(`[PD_GATE] Bash command contains zero-width characters — blocking as dangerous`);
70
+ return 'dangerous'; // Fail-closed: zero-width chars are suspicious
71
+ }
72
+
73
+ // Tokenize command chain before pattern matching to catch `cmd1 && cmd2` bypasses
74
+ // Only split on statement separators (; && ||), NOT on pipe (|) which is part of the command
75
+ const tokens = normalizedCmd
76
+ .split(/\s*(?:;|&&|\|\|)\s*/)
77
+ .map(t => t.trim())
78
+ .filter(t => t.length > 0);
79
+
80
+ // If no tokens (e.g., pure pipe-only), use the original
81
+ const segments = tokens.length > 0 ? tokens : [normalizedCmd];
82
+
83
+ // Also strip outer $() and backticks from each segment, but PRESERVE inner content
84
+ const cleanSegments = segments.map(seg => {
85
+ let s = seg;
86
+ // Extract inner content from $() or ${} or backtick-wrapped commands
87
+ // IMPORTANT: Preserve the inner command for analysis, don't drop it entirely
88
+ s = s.replace(/^\$\(([^)]+)\)$/, '$1').replace(/^\$\{([^}]+)\}$/, '$1').replace(/^`([^`]+)`$/, '$1');
89
+ return s.trim();
90
+ }).filter(s => s.length > 0);
91
+
92
+ // SECURITY: If original input was non-empty but we have no analyzable content, fail closed
93
+ if (cleanSegments.length === 0 && normalizedCmd.trim().length > 0) {
94
+ logger?.warn?.(`[PD_GATE] Bash command analysis produced empty segments from non-empty input, failing closed: ${normalizedCmd.substring(0, 100)}`);
95
+ return 'dangerous';
96
+ }
97
+
98
+ // 1. Check dangerous patterns against each segment
99
+ for (const seg of cleanSegments) {
100
+ for (const pattern of dangerousPatterns) {
101
+ try {
102
+ if (new RegExp(pattern, 'i').test(seg)) {
103
+ return 'dangerous';
104
+ }
105
+ } catch (error) {
106
+ logger?.warn?.(`[PD_GATE] Invalid dangerous bash regex "${pattern}": ${String(error)}. Failing closed.`);
107
+ return 'dangerous';
108
+ // Fail-closed: 无效的危险模式正则视为匹配危险命令
109
+ }
110
+ }
111
+ }
112
+
113
+ // 2. Check safe patterns (only if ALL segments are safe)
114
+ for (const seg of cleanSegments) {
115
+ let isSafe = false;
116
+ for (const pattern of safePatterns) {
117
+ try {
118
+ if (new RegExp(pattern, 'i').test(seg)) {
119
+ isSafe = true;
120
+ break;
121
+ }
122
+ } catch (error) {
123
+ logger?.warn?.(`[PD_GATE] Invalid safe bash regex "${pattern}": ${String(error)}. Ignoring safe override.`);
124
+ }
125
+ }
126
+ if (!isSafe) {
127
+ // Not all segments are safe → treat as normal
128
+ return 'normal';
129
+ }
130
+ }
131
+
132
+ // All segments are safe
133
+ return 'safe';
134
+ }
135
+
136
+ export interface DynamicThresholdConfig {
137
+ large_change_lines: number;
138
+ ep_tier_multipliers: Record<string, number>;
139
+ }
140
+
141
+ /**
142
+ * Calculates the dynamic GFI threshold based on EP tier and line changes.
143
+ *
144
+ * The threshold is adjusted by:
145
+ * 1. EP tier multiplier (higher tiers get higher thresholds)
146
+ * 2. Large change reduction (big edits lower the threshold to catch more issues)
147
+ *
148
+ * @param baseThreshold - The base GFI threshold (typically 50 for GFI)
149
+ * @param epTier - Current EP tier (1-5)
150
+ * @param lineChanges - Number of lines being changed
151
+ * @param config - Configuration with large_change_lines and ep_tier_multipliers
152
+ * @returns The adjusted threshold (minimum 0)
153
+ */
154
+ export function calculateDynamicThreshold(
155
+ baseThreshold: number,
156
+ epTier: number,
157
+ lineChanges: number,
158
+ config: DynamicThresholdConfig
159
+ ): number {
160
+ // 1. EP Tier multiplier
161
+ const tierMultiplier = config.ep_tier_multipliers[epTier.toString()] || 1.0;
162
+ let threshold = baseThreshold * tierMultiplier;
163
+
164
+ // 2. Large scale modification reduces threshold
165
+ if (lineChanges > config.large_change_lines) {
166
+ const ratio = Math.min(lineChanges / 200, 0.5); // Reduce by up to 50%
167
+ threshold = threshold * (1 - ratio);
168
+ }
169
+
170
+ return Math.round(Math.max(threshold, 0));
171
+ }
@@ -0,0 +1,295 @@
1
+ /**
2
+ * Edit Verification Module
3
+ *
4
+ * Enforces P-03 (precise verification principle) for edit tool operations.
5
+ *
6
+ * **Responsibilities:**
7
+ * - Verify oldText matches current file content before edit
8
+ * - Fuzzy matching for whitespace-agnostic comparison
9
+ * - File size limits and binary file detection
10
+ * - Automatic correction of whitespace mismatches
11
+ * - Detailed error messages with guidance for fix
12
+ *
13
+ * **Configuration:**
14
+ * - Edit verification settings from profile.edit_verification
15
+ * - Max file size threshold (default 10MB)
16
+ * - Fuzzy match threshold (default 0.8)
17
+ * - Skip action for large files (warn/block)
18
+ */
19
+
20
+ import * as fs from 'fs';
21
+ import * as path from 'path';
22
+ import type { PluginHookBeforeToolCallEvent, PluginHookBeforeToolCallResult } from '../openclaw-sdk.js';
23
+ import type { WorkspaceContext } from '../core/workspace-context.js';
24
+
25
+ export interface EditVerificationConfig {
26
+ enabled?: boolean;
27
+ max_file_size_bytes?: number;
28
+ fuzzy_match_enabled?: boolean;
29
+ fuzzy_match_threshold?: number;
30
+ skip_large_file_action?: 'warn' | 'block';
31
+ }
32
+
33
+ /**
34
+ * Normalize a line for fuzzy matching by collapsing whitespace
35
+ */
36
+ export function normalizeLine(line: string): string {
37
+ return line.replace(/\s+/g, ' ').trim();
38
+ }
39
+
40
+ /**
41
+ * Find fuzzy match between oldText and current file content
42
+ * @param lines - File content split into lines
43
+ * @param oldLines - oldText split into lines
44
+ * @param threshold - Match threshold (0-1)
45
+ * @returns Match index or -1 if not found
46
+ */
47
+ export function findFuzzyMatch(lines: string[], oldLines: string[], threshold: number = 0.8): number {
48
+ if (oldLines.length === 0) return -1; // P2 fix: empty array boundary check
49
+
50
+ const normalizedLines = lines.map(normalizeLine);
51
+ const normalizedOldLines = oldLines.map(normalizeLine);
52
+
53
+ // Try to find matching sequence
54
+ for (let i = 0; i <= lines.length - oldLines.length; i++) {
55
+ let matchCount = 0;
56
+ for (let j = 0; j < oldLines.length; j++) {
57
+ if (normalizedLines[i + j] === normalizedOldLines[j]) {
58
+ matchCount++;
59
+ }
60
+ }
61
+
62
+ // Use threshold from config
63
+ if (matchCount >= oldLines.length * threshold) {
64
+ return i;
65
+ }
66
+ }
67
+
68
+ return -1;
69
+ }
70
+
71
+ /**
72
+ * Try to find a fuzzy match for oldText in current content
73
+ * @param currentContent - Current file content
74
+ * @param oldText - Text to match
75
+ * @param threshold - Match threshold (0-1)
76
+ * @returns Object with found status and corrected text if found
77
+ */
78
+ export function tryFuzzyMatch(currentContent: string, oldText: string, threshold: number = 0.8): { found: boolean; correctedText?: string } {
79
+ const lines = currentContent.split('\n');
80
+ const oldLines = oldText.split('\n');
81
+
82
+ const matchIndex = findFuzzyMatch(lines, oldLines, threshold);
83
+
84
+ if (matchIndex !== -1) {
85
+ // Found fuzzy match, extract actual text from file
86
+ const correctedText = lines.slice(matchIndex, matchIndex + oldLines.length).join('\n');
87
+ return { found: true, correctedText };
88
+ }
89
+
90
+ return { found: false };
91
+ }
92
+
93
+ /**
94
+ * Generate a helpful error message for edit verification failure
95
+ */
96
+ export function generateEditError(filePath: string, oldText: string, currentContent: string): string {
97
+ const expectedSnippet = oldText.split('\n').slice(0, 3).join('\n').substring(0, 200);
98
+ const actualSnippet = currentContent.substring(0, 200);
99
+
100
+ return `[P-03 Violation] Edit verification failed
101
+
102
+ File: ${filePath}
103
+
104
+ The text you're trying to replace does not match the current file content.
105
+
106
+ Expected to find:
107
+ ${expectedSnippet}${oldText.length > 200 ? '...' : ''}
108
+
109
+ Actual file contains:
110
+ ${actualSnippet}${currentContent.length > 200 ? '...' : ''}
111
+
112
+ Possible reasons:
113
+ - File has been modified by another process
114
+ - Whitespace characters do not match (spaces, tabs, newlines)
115
+ - Context compression caused outdated information
116
+
117
+ Solution:
118
+ 1. Use 'read' tool to get current file content
119
+ 2. Update your edit command with exact text from file
120
+ 3. Retry edit operation
121
+
122
+ This is enforced by P-03 (精确匹配前验证原则).`;
123
+ }
124
+
125
+ /**
126
+ * Handle edit tool verification before allowing operation
127
+ * This enforces P-03 at the tool layer
128
+ */
129
+ export function handleEditVerification(
130
+ event: PluginHookBeforeToolCallEvent,
131
+ wctx: WorkspaceContext,
132
+ ctx: { logger?: any; sessionId?: string },
133
+ config: EditVerificationConfig = {}
134
+ ): PluginHookBeforeToolCallResult | void {
135
+ // Skip verification if disabled - return early without any processing or logging
136
+ if (config.enabled === false) {
137
+ return;
138
+ }
139
+
140
+ const logger = ctx.logger || console;
141
+ const maxSizeBytes = config.max_file_size_bytes ?? 10 * 1024 * 1024; // Default 10MB
142
+ const fuzzyMatchEnabled = config.fuzzy_match_enabled !== false;
143
+ const fuzzyMatchThreshold = config.fuzzy_match_threshold ?? 0.8;
144
+ const skipAction: 'warn' | 'block' = config.skip_large_file_action ?? 'warn';
145
+
146
+ // 1. Extract parameters (handle both parameter naming conventions)
147
+ const filePath = event.params.file_path || event.params.path || event.params.file;
148
+ const oldText = event.params.oldText || event.params.old_string;
149
+
150
+ if (!filePath || !oldText) {
151
+ // Missing required parameters, let it fail naturally
152
+ return;
153
+ }
154
+
155
+ // 2. Resolve and read file
156
+ let absolutePath: string;
157
+ try {
158
+ absolutePath = wctx.resolve(filePath);
159
+ } catch (error) {
160
+ // Path resolution error, let it fail naturally
161
+ return;
162
+ }
163
+
164
+ // 2.5. Skip verification for binary files
165
+ const BINARY_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.bmp', '.svg',
166
+ '.pdf', '.zip', '.tar', '.gz', '.7z', '.rar',
167
+ '.exe', '.dll', '.so', '.dylib', '.bin',
168
+ '.mp3', '.mp4', '.avi', '.mov', '.wav',
169
+ '.ttf', '.otf', '.woff', '.woff2',
170
+ '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx'];
171
+ const ext = path.extname(absolutePath).toLowerCase();
172
+ if (BINARY_EXTENSIONS.includes(ext)) {
173
+ logger?.info?.(`[PD_GATE:EDIT_VERIFY] Skipping verification for binary file: ${path.basename(filePath)}`);
174
+ return;
175
+ }
176
+
177
+ try {
178
+ // 2.6. Check file size before reading (P-03 improvement)
179
+ try {
180
+ const stats = fs.statSync(absolutePath);
181
+ const fileSizeBytes = stats.size;
182
+ const fileSizeMB = fileSizeBytes / (1024 * 1024);
183
+
184
+ if (fileSizeBytes > maxSizeBytes) {
185
+ const message = `[PD_GATE:EDIT_VERIFY] File size check: ${path.basename(filePath)} is ${fileSizeMB.toFixed(2)}MB (threshold: ${(maxSizeBytes / (1024 * 1024)).toFixed(2)}MB)`;
186
+
187
+ if (skipAction === 'block') {
188
+ logger?.warn?.(message + ' - BLOCKED');
189
+ return {
190
+ block: true,
191
+ blockReason: `${message}\n\nFile is too large for edit verification. Increase max_file_size_bytes in PROFILE.json or reduce file size.`
192
+ };
193
+ } else {
194
+ logger?.warn?.(message + ' - SKIPPING verification');
195
+ return; // Skip verification but allow operation
196
+ }
197
+ }
198
+
199
+ logger?.info?.(`[PD_GATE:EDIT_VERIFY] File size check passed: ${path.basename(filePath)} (${fileSizeMB.toFixed(2)}MB)`);
200
+ } catch (statError) {
201
+ // File stat error (e.g., permission denied)
202
+ const errStr = statError instanceof Error ? statError.message : String(statError);
203
+ const errCode = (statError as any).code;
204
+
205
+ if (errCode === 'EACCES' || errCode === 'EPERM') {
206
+ logger?.error?.(`[PD_GATE:EDIT_VERIFY] Permission denied accessing file: ${path.basename(filePath)} (${errStr})`);
207
+ return {
208
+ block: true,
209
+ blockReason: `[P-03 Error] Permission denied: Cannot access file ${absolutePath}\n\nError: ${errStr}\n\nSolution: Check file permissions or run with appropriate access rights.`
210
+ };
211
+ } else if (errCode === 'ENOENT') {
212
+ logger?.warn?.(`[PD_GATE:EDIT_VERIFY] File not found: ${path.basename(filePath)} (${errStr})`);
213
+ // File doesn't exist - let edit operation proceed (it will create file)
214
+ return;
215
+ } else {
216
+ logger?.warn?.(`[PD_GATE:EDIT_VERIFY] Stat error: ${errStr}`);
217
+ // Let it fail naturally on read attempt
218
+ }
219
+ }
220
+
221
+ // 3. Read current file content with improved error handling
222
+ let currentContent: string;
223
+ try {
224
+ currentContent = fs.readFileSync(absolutePath, 'utf-8');
225
+ } catch (readError) {
226
+ const errStr = readError instanceof Error ? readError.message : String(readError);
227
+ const errCode = (readError as any).code;
228
+
229
+ if (errCode === 'EACCES' || errCode === 'EPERM') {
230
+ logger?.error?.(`[PD_GATE:EDIT_VERIFY] Permission denied reading file: ${path.basename(filePath)} (${errStr})`);
231
+ return {
232
+ block: true,
233
+ blockReason: `[P-03 Error] Permission denied: Cannot read file ${absolutePath}\n\nError: ${errStr}\n\nSolution: Check file permissions or run with appropriate access rights.`
234
+ };
235
+ } else if (errCode === 'ENOENT') {
236
+ logger?.warn?.(`[PD_GATE:EDIT_VERIFY] File not found: ${path.basename(filePath)} (${errStr})`);
237
+ // File doesn't exist - let edit operation proceed
238
+ return;
239
+ } else if (errStr.includes('UTF-8') || errStr.includes('encoding')) {
240
+ logger?.error?.(`[PD_GATE:EDIT_VERIFY] Encoding error reading file: ${path.basename(filePath)} (${errStr})`);
241
+ return {
242
+ block: true,
243
+ blockReason: `[P-03 Error] Encoding error: Cannot read file ${absolutePath}\n\nError: ${errStr}\n\nThe file appears to use an encoding other than UTF-8. Edit verification requires UTF-8 readable text files.\n\nSolution: Ensure file is UTF-8 encoded text, or mark binary extensions to skip verification.`
244
+ };
245
+ } else {
246
+ logger?.warn?.(`[PD_GATE:EDIT_VERIFY] Read error: ${errStr}`);
247
+ // Let it fail naturally
248
+ return;
249
+ }
250
+ }
251
+
252
+ // 4. Verify oldText exists in current content
253
+ if (!currentContent.includes(oldText)) {
254
+ logger?.info?.(`[PD_GATE:EDIT_VERIFY] Exact match failed for ${path.basename(filePath)}, trying fuzzy match`);
255
+
256
+ // 5. Try fuzzy matching (if enabled)
257
+ if (fuzzyMatchEnabled) {
258
+ const fuzzyResult = tryFuzzyMatch(currentContent, oldText, fuzzyMatchThreshold);
259
+
260
+ if (fuzzyResult.found && fuzzyResult.correctedText) {
261
+ logger?.info?.(`[PD_GATE:EDIT_VERIFY] Fuzzy match found for ${path.basename(filePath)}, auto-correcting oldText`);
262
+
263
+ // Return corrected parameters
264
+ return {
265
+ params: {
266
+ ...event.params,
267
+ oldText: fuzzyResult.correctedText,
268
+ old_string: fuzzyResult.correctedText
269
+ }
270
+ };
271
+ }
272
+ }
273
+
274
+ // 6. No match found, block operation with helpful error
275
+ const errorMsg = generateEditError(absolutePath, oldText, currentContent);
276
+
277
+ logger?.error?.(`[PD_GATE:EDIT_VERIFY] Block edit on ${path.basename(filePath)}: oldText not found`);
278
+
279
+ return {
280
+ block: true,
281
+ blockReason: errorMsg
282
+ };
283
+ }
284
+
285
+ // 7. Verification passed, allow edit to proceed
286
+ logger?.info?.(`[PD_GATE:EDIT_VERIFY] Verified edit on ${path.basename(filePath)}`);
287
+ return;
288
+
289
+ } catch (error) {
290
+ // Unexpected error - let it fail naturally
291
+ const errorStr = error instanceof Error ? error.message : String(error);
292
+ logger?.warn?.(`[PD_GATE:EDIT_VERIFY] Unexpected error: ${errorStr}`);
293
+ return;
294
+ }
295
+ }