create-principles-disciple 1.93.0 → 1.95.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (526) hide show
  1. package/console/dist/server/config/WorkspaceConfigStore.js +2 -1
  2. package/console/dist/server/config/pd-config-store.d.ts +40 -1
  3. package/console/dist/server/config/pd-config-store.js +170 -1
  4. package/console/dist/server/index.js +28 -10
  5. package/console/dist/server/models/ApprovalsConsoleModel.js +4 -12
  6. package/console/dist/server/models/EvidenceChainConsoleModel.d.ts +0 -2
  7. package/console/dist/server/models/EvidenceChainConsoleModel.js +36 -2
  8. package/console/dist/server/models/HealthCheckModel.js +5 -3
  9. package/console/dist/server/models/IntentDecisionModel.d.ts +66 -0
  10. package/console/dist/server/models/IntentDecisionModel.js +207 -0
  11. package/console/dist/server/models/IntentPageModel.d.ts +18 -0
  12. package/console/dist/server/models/IntentPageModel.js +90 -0
  13. package/console/dist/server/models/PrinciplesConsoleModel.d.ts +2 -0
  14. package/console/dist/server/models/PrinciplesConsoleModel.js +37 -1
  15. package/console/dist/server/models/WorkspaceService.d.ts +0 -28
  16. package/console/dist/server/models/WorkspaceService.js +0 -115
  17. package/console/dist/server/routes/config.js +55 -1
  18. package/console/dist/server/routes/feedback-reports.js +1 -1
  19. package/console/dist/server/routes/intent-decisions.d.ts +33 -0
  20. package/console/dist/server/routes/intent-decisions.js +499 -0
  21. package/console/dist/server/routes/intent.d.ts +3 -0
  22. package/console/dist/server/routes/intent.js +35 -0
  23. package/console/dist/server/routes/principles.js +67 -1
  24. package/console/dist/server/routes/update.d.ts +0 -1
  25. package/console/dist/server/routes/update.js +14 -11
  26. package/console/dist/ui/App.js +2 -7
  27. package/console/dist/ui/api.d.ts +44 -18
  28. package/console/dist/ui/api.js +44 -5
  29. package/console/dist/ui/components/layout/app-sidebar.js +2 -1
  30. package/console/dist/ui/components/ui/badge.d.ts +1 -1
  31. package/console/dist/ui/components/ui/button.d.ts +1 -1
  32. package/console/dist/ui/components/ui/shiny-text.d.ts +28 -0
  33. package/console/dist/ui/components/ui/shiny-text.js +46 -0
  34. package/console/dist/ui/i18n/en.json +167 -3
  35. package/console/dist/ui/i18n/zh-CN.json +167 -3
  36. package/console/dist/ui/pages/activation/ActivationPage.js +2 -1
  37. package/console/dist/ui/pages/control-center/ControlCenterPage.js +28 -2
  38. package/console/dist/ui/pages/control-center/EmpathyObserverCostHint.d.ts +28 -0
  39. package/console/dist/ui/pages/control-center/EmpathyObserverCostHint.js +38 -0
  40. package/console/dist/ui/pages/debt/DebtPage.js +219 -2
  41. package/console/dist/ui/pages/focus/FocusPage.js +2 -1
  42. package/console/dist/ui/pages/intent/IntentPage.d.ts +1 -0
  43. package/console/dist/ui/pages/intent/IntentPage.js +147 -0
  44. package/console/dist/ui/pages/pain/PainPage.js +234 -4
  45. package/console/dist/ui/pages/pain/pain-card-helpers.d.ts +75 -0
  46. package/console/dist/ui/pages/pain/pain-card-helpers.js +78 -0
  47. package/console/dist/ui/pages/principles/PrinciplesPage.js +2 -1
  48. package/console/dist/ui/utils/validators.d.ts +163 -0
  49. package/console/dist/ui/utils/validators.js +433 -0
  50. package/console/dist/web/assets/app.css +138 -0
  51. package/console/dist/web/assets/app.js +3124 -1288
  52. package/console/package.json +3 -0
  53. package/core/dist/index.d.ts +2 -2
  54. package/core/dist/index.d.ts.map +1 -1
  55. package/core/dist/index.js +1 -1
  56. package/core/dist/index.js.map +1 -1
  57. package/core/dist/principle-tree-ledger.d.ts +55 -4
  58. package/core/dist/principle-tree-ledger.d.ts.map +1 -1
  59. package/core/dist/principle-tree-ledger.js +472 -13
  60. package/core/dist/principle-tree-ledger.js.map +1 -1
  61. package/core/dist/prompt-builder/__tests__/model-config.test.d.ts +2 -0
  62. package/core/dist/prompt-builder/__tests__/model-config.test.d.ts.map +1 -0
  63. package/core/dist/prompt-builder/__tests__/model-config.test.js +158 -0
  64. package/core/dist/prompt-builder/__tests__/model-config.test.js.map +1 -0
  65. package/core/dist/prompt-builder/__tests__/prompt-builder-core.test.js +58 -0
  66. package/core/dist/prompt-builder/__tests__/prompt-builder-core.test.js.map +1 -1
  67. package/core/dist/prompt-builder/__tests__/xml-escape.test.d.ts +2 -0
  68. package/core/dist/prompt-builder/__tests__/xml-escape.test.d.ts.map +1 -0
  69. package/core/dist/prompt-builder/__tests__/xml-escape.test.js +42 -0
  70. package/core/dist/prompt-builder/__tests__/xml-escape.test.js.map +1 -0
  71. package/core/dist/prompt-builder/empathy-keyword-matching.d.ts.map +1 -1
  72. package/core/dist/prompt-builder/empathy-keyword-matching.js +9 -6
  73. package/core/dist/prompt-builder/empathy-keyword-matching.js.map +1 -1
  74. package/core/dist/prompt-builder/index.d.ts +3 -0
  75. package/core/dist/prompt-builder/index.d.ts.map +1 -1
  76. package/core/dist/prompt-builder/index.js +2 -0
  77. package/core/dist/prompt-builder/index.js.map +1 -1
  78. package/core/dist/prompt-builder/model-config.d.ts +42 -0
  79. package/core/dist/prompt-builder/model-config.d.ts.map +1 -0
  80. package/core/dist/prompt-builder/model-config.js +84 -0
  81. package/core/dist/prompt-builder/model-config.js.map +1 -0
  82. package/core/dist/prompt-builder/size-guard.d.ts +1 -0
  83. package/core/dist/prompt-builder/size-guard.d.ts.map +1 -1
  84. package/core/dist/prompt-builder/size-guard.js +21 -0
  85. package/core/dist/prompt-builder/size-guard.js.map +1 -1
  86. package/core/dist/prompt-builder/types.d.ts +2 -0
  87. package/core/dist/prompt-builder/types.d.ts.map +1 -1
  88. package/core/dist/prompt-builder/xml-escape.d.ts +12 -0
  89. package/core/dist/prompt-builder/xml-escape.d.ts.map +1 -0
  90. package/core/dist/prompt-builder/xml-escape.js +19 -0
  91. package/core/dist/prompt-builder/xml-escape.js.map +1 -0
  92. package/core/dist/runtime-v2/__tests__/architecture-regression.test.js +205 -48
  93. package/core/dist/runtime-v2/__tests__/architecture-regression.test.js.map +1 -1
  94. package/core/dist/runtime-v2/__tests__/evidence-chain-intent-tension.test.d.ts +2 -0
  95. package/core/dist/runtime-v2/__tests__/evidence-chain-intent-tension.test.d.ts.map +1 -0
  96. package/core/dist/runtime-v2/__tests__/evidence-chain-intent-tension.test.js +284 -0
  97. package/core/dist/runtime-v2/__tests__/evidence-chain-intent-tension.test.js.map +1 -0
  98. package/core/dist/runtime-v2/__tests__/pain-signal-bridge-result-shaping.test.d.ts +2 -0
  99. package/core/dist/runtime-v2/__tests__/pain-signal-bridge-result-shaping.test.d.ts.map +1 -0
  100. package/core/dist/runtime-v2/__tests__/pain-signal-bridge-result-shaping.test.js +500 -0
  101. package/core/dist/runtime-v2/__tests__/pain-signal-bridge-result-shaping.test.js.map +1 -0
  102. package/core/dist/runtime-v2/__tests__/pain-signal-observability.test.js +7 -5
  103. package/core/dist/runtime-v2/__tests__/pain-signal-observability.test.js.map +1 -1
  104. package/core/dist/runtime-v2/__tests__/pi-artifact-store.test.js +11 -0
  105. package/core/dist/runtime-v2/__tests__/pi-artifact-store.test.js.map +1 -1
  106. package/core/dist/runtime-v2/__tests__/proven-channel-baseline.test.js +15 -2
  107. package/core/dist/runtime-v2/__tests__/proven-channel-baseline.test.js.map +1 -1
  108. package/core/dist/runtime-v2/__tests__/schema-float-precision.test.d.ts +2 -0
  109. package/core/dist/runtime-v2/__tests__/schema-float-precision.test.d.ts.map +1 -0
  110. package/core/dist/runtime-v2/__tests__/schema-float-precision.test.js +290 -0
  111. package/core/dist/runtime-v2/__tests__/schema-float-precision.test.js.map +1 -0
  112. package/core/dist/runtime-v2/__tests__/schema-orphan-detection.test.d.ts +2 -0
  113. package/core/dist/runtime-v2/__tests__/schema-orphan-detection.test.d.ts.map +1 -0
  114. package/core/dist/runtime-v2/__tests__/schema-orphan-detection.test.js +158 -0
  115. package/core/dist/runtime-v2/__tests__/schema-orphan-detection.test.js.map +1 -0
  116. package/core/dist/runtime-v2/activation/__tests__/approval-store-extended.test.js +15 -1
  117. package/core/dist/runtime-v2/activation/__tests__/approval-store-extended.test.js.map +1 -1
  118. package/core/dist/runtime-v2/activation/__tests__/sqlite-activation-state-store-fk.test.d.ts +2 -0
  119. package/core/dist/runtime-v2/activation/__tests__/sqlite-activation-state-store-fk.test.d.ts.map +1 -0
  120. package/core/dist/runtime-v2/activation/__tests__/sqlite-activation-state-store-fk.test.js +62 -0
  121. package/core/dist/runtime-v2/activation/__tests__/sqlite-activation-state-store-fk.test.js.map +1 -0
  122. package/core/dist/runtime-v2/activation/__tests__/sqlite-activation-state-store.test.js +12 -2
  123. package/core/dist/runtime-v2/activation/__tests__/sqlite-activation-state-store.test.js.map +1 -1
  124. package/core/dist/runtime-v2/activation/__tests__/sqlite-approval-store-fk.test.d.ts +2 -0
  125. package/core/dist/runtime-v2/activation/__tests__/sqlite-approval-store-fk.test.d.ts.map +1 -0
  126. package/core/dist/runtime-v2/activation/__tests__/sqlite-approval-store-fk.test.js +56 -0
  127. package/core/dist/runtime-v2/activation/__tests__/sqlite-approval-store-fk.test.js.map +1 -0
  128. package/core/dist/runtime-v2/activation/__tests__/sqlite-approval-store.test.js +8 -0
  129. package/core/dist/runtime-v2/activation/__tests__/sqlite-approval-store.test.js.map +1 -1
  130. package/core/dist/runtime-v2/activation/__tests__/story-a-acceptance.test.js +20 -0
  131. package/core/dist/runtime-v2/activation/__tests__/story-a-acceptance.test.js.map +1 -1
  132. package/core/dist/runtime-v2/activation/sqlite-activation-state-store.d.ts.map +1 -1
  133. package/core/dist/runtime-v2/activation/sqlite-activation-state-store.js +6 -0
  134. package/core/dist/runtime-v2/activation/sqlite-activation-state-store.js.map +1 -1
  135. package/core/dist/runtime-v2/activation/sqlite-approval-store.d.ts.map +1 -1
  136. package/core/dist/runtime-v2/activation/sqlite-approval-store.js +86 -32
  137. package/core/dist/runtime-v2/activation/sqlite-approval-store.js.map +1 -1
  138. package/core/dist/runtime-v2/activation/writers/__tests__/rule-host-writer.test.js +65 -1
  139. package/core/dist/runtime-v2/activation/writers/__tests__/rule-host-writer.test.js.map +1 -1
  140. package/core/dist/runtime-v2/activation/writers/rule-host-writer.d.ts.map +1 -1
  141. package/core/dist/runtime-v2/activation/writers/rule-host-writer.js +33 -11
  142. package/core/dist/runtime-v2/activation/writers/rule-host-writer.js.map +1 -1
  143. package/core/dist/runtime-v2/bridge-result-shaper.d.ts +53 -0
  144. package/core/dist/runtime-v2/bridge-result-shaper.d.ts.map +1 -0
  145. package/core/dist/runtime-v2/bridge-result-shaper.js +117 -0
  146. package/core/dist/runtime-v2/bridge-result-shaper.js.map +1 -0
  147. package/core/dist/runtime-v2/detection/__tests__/detection-funnel-policy.test.d.ts +13 -0
  148. package/core/dist/runtime-v2/detection/__tests__/detection-funnel-policy.test.d.ts.map +1 -0
  149. package/core/dist/runtime-v2/detection/__tests__/detection-funnel-policy.test.js +111 -0
  150. package/core/dist/runtime-v2/detection/__tests__/detection-funnel-policy.test.js.map +1 -0
  151. package/core/dist/runtime-v2/detection/detection-funnel-policy.d.ts +103 -0
  152. package/core/dist/runtime-v2/detection/detection-funnel-policy.d.ts.map +1 -0
  153. package/core/dist/runtime-v2/detection/detection-funnel-policy.js +135 -0
  154. package/core/dist/runtime-v2/detection/detection-funnel-policy.js.map +1 -0
  155. package/core/dist/runtime-v2/detection/index.d.ts +10 -0
  156. package/core/dist/runtime-v2/detection/index.d.ts.map +1 -0
  157. package/core/dist/runtime-v2/detection/index.js +9 -0
  158. package/core/dist/runtime-v2/detection/index.js.map +1 -0
  159. package/core/dist/runtime-v2/diagnostician/__tests__/diag-rootcause-output.test.js +134 -15
  160. package/core/dist/runtime-v2/diagnostician/__tests__/diag-rootcause-output.test.js.map +1 -1
  161. package/core/dist/runtime-v2/diagnostician/__tests__/intent-tension-schema.test.d.ts +18 -0
  162. package/core/dist/runtime-v2/diagnostician/__tests__/intent-tension-schema.test.d.ts.map +1 -0
  163. package/core/dist/runtime-v2/diagnostician/__tests__/intent-tension-schema.test.js +251 -0
  164. package/core/dist/runtime-v2/diagnostician/__tests__/intent-tension-schema.test.js.map +1 -0
  165. package/core/dist/runtime-v2/diagnostician/__tests__/rootcause-prompt-builder.test.js +73 -0
  166. package/core/dist/runtime-v2/diagnostician/__tests__/rootcause-prompt-builder.test.js.map +1 -1
  167. package/core/dist/runtime-v2/diagnostician/diag-rootcause-output.d.ts +98 -0
  168. package/core/dist/runtime-v2/diagnostician/diag-rootcause-output.d.ts.map +1 -1
  169. package/core/dist/runtime-v2/diagnostician/diag-rootcause-output.js +131 -0
  170. package/core/dist/runtime-v2/diagnostician/diag-rootcause-output.js.map +1 -1
  171. package/core/dist/runtime-v2/diagnostician/rootcause-prompt-builder.d.ts +8 -0
  172. package/core/dist/runtime-v2/diagnostician/rootcause-prompt-builder.d.ts.map +1 -1
  173. package/core/dist/runtime-v2/diagnostician/rootcause-prompt-builder.js +66 -3
  174. package/core/dist/runtime-v2/diagnostician/rootcause-prompt-builder.js.map +1 -1
  175. package/core/dist/runtime-v2/diagnostician-output.d.ts +23 -0
  176. package/core/dist/runtime-v2/diagnostician-output.d.ts.map +1 -1
  177. package/core/dist/runtime-v2/diagnostician-output.js +16 -0
  178. package/core/dist/runtime-v2/diagnostician-output.js.map +1 -1
  179. package/core/dist/runtime-v2/diagnostician-prompt-builder.d.ts +32 -0
  180. package/core/dist/runtime-v2/diagnostician-prompt-builder.d.ts.map +1 -1
  181. package/core/dist/runtime-v2/diagnostician-prompt-builder.js.map +1 -1
  182. package/core/dist/runtime-v2/evidence-triage/__tests__/observation-resolver.test.d.ts +12 -0
  183. package/core/dist/runtime-v2/evidence-triage/__tests__/observation-resolver.test.d.ts.map +1 -0
  184. package/core/dist/runtime-v2/evidence-triage/__tests__/observation-resolver.test.js +181 -0
  185. package/core/dist/runtime-v2/evidence-triage/__tests__/observation-resolver.test.js.map +1 -0
  186. package/core/dist/runtime-v2/evidence-triage/__tests__/triage-policy.test.js +104 -1
  187. package/core/dist/runtime-v2/evidence-triage/__tests__/triage-policy.test.js.map +1 -1
  188. package/core/dist/runtime-v2/evidence-triage/index.d.ts +3 -0
  189. package/core/dist/runtime-v2/evidence-triage/index.d.ts.map +1 -1
  190. package/core/dist/runtime-v2/evidence-triage/index.js +3 -0
  191. package/core/dist/runtime-v2/evidence-triage/index.js.map +1 -1
  192. package/core/dist/runtime-v2/evidence-triage/observation-resolver.d.ts +128 -0
  193. package/core/dist/runtime-v2/evidence-triage/observation-resolver.d.ts.map +1 -0
  194. package/core/dist/runtime-v2/evidence-triage/observation-resolver.js +203 -0
  195. package/core/dist/runtime-v2/evidence-triage/observation-resolver.js.map +1 -0
  196. package/core/dist/runtime-v2/evidence-triage/source-descriptors.d.ts +1 -1
  197. package/core/dist/runtime-v2/evidence-triage/source-descriptors.js +1 -1
  198. package/core/dist/runtime-v2/evidence-triage/triage-policy.d.ts +4 -0
  199. package/core/dist/runtime-v2/evidence-triage/triage-policy.d.ts.map +1 -1
  200. package/core/dist/runtime-v2/evidence-triage/triage-policy.js +66 -9
  201. package/core/dist/runtime-v2/evidence-triage/triage-policy.js.map +1 -1
  202. package/core/dist/runtime-v2/evidence-triage/types.d.ts +14 -0
  203. package/core/dist/runtime-v2/evidence-triage/types.d.ts.map +1 -1
  204. package/core/dist/runtime-v2/evidence-triage/types.js.map +1 -1
  205. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js +56 -4
  206. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js.map +1 -1
  207. package/core/dist/runtime-v2/feature-flags/__tests__/intent-engineering-flag.test.d.ts +2 -0
  208. package/core/dist/runtime-v2/feature-flags/__tests__/intent-engineering-flag.test.d.ts.map +1 -0
  209. package/core/dist/runtime-v2/feature-flags/__tests__/intent-engineering-flag.test.js +30 -0
  210. package/core/dist/runtime-v2/feature-flags/__tests__/intent-engineering-flag.test.js.map +1 -0
  211. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts.map +1 -1
  212. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js +11 -2
  213. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js.map +1 -1
  214. package/core/dist/runtime-v2/feature-flags/plugin-surface-registry.d.ts.map +1 -1
  215. package/core/dist/runtime-v2/feature-flags/plugin-surface-registry.js +0 -9
  216. package/core/dist/runtime-v2/feature-flags/plugin-surface-registry.js.map +1 -1
  217. package/core/dist/runtime-v2/feedback/__tests__/redact-sensitive.test.d.ts +15 -0
  218. package/core/dist/runtime-v2/feedback/__tests__/redact-sensitive.test.d.ts.map +1 -0
  219. package/core/dist/runtime-v2/feedback/__tests__/redact-sensitive.test.js +450 -0
  220. package/core/dist/runtime-v2/feedback/__tests__/redact-sensitive.test.js.map +1 -0
  221. package/core/dist/runtime-v2/feedback/__tests__/render-github-url.test.d.ts +13 -0
  222. package/core/dist/runtime-v2/feedback/__tests__/render-github-url.test.d.ts.map +1 -0
  223. package/core/dist/runtime-v2/feedback/__tests__/render-github-url.test.js +243 -0
  224. package/core/dist/runtime-v2/feedback/__tests__/render-github-url.test.js.map +1 -0
  225. package/core/dist/runtime-v2/feedback/__tests__/safe-stringify.test.d.ts +12 -0
  226. package/core/dist/runtime-v2/feedback/__tests__/safe-stringify.test.d.ts.map +1 -0
  227. package/core/dist/runtime-v2/feedback/__tests__/safe-stringify.test.js +172 -0
  228. package/core/dist/runtime-v2/feedback/__tests__/safe-stringify.test.js.map +1 -0
  229. package/core/dist/runtime-v2/index.d.ts +15 -6
  230. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  231. package/core/dist/runtime-v2/index.js +15 -4
  232. package/core/dist/runtime-v2/index.js.map +1 -1
  233. package/core/dist/runtime-v2/intent/__tests__/intent-decision-record.test.d.ts +2 -0
  234. package/core/dist/runtime-v2/intent/__tests__/intent-decision-record.test.d.ts.map +1 -0
  235. package/core/dist/runtime-v2/intent/__tests__/intent-decision-record.test.js +185 -0
  236. package/core/dist/runtime-v2/intent/__tests__/intent-decision-record.test.js.map +1 -0
  237. package/core/dist/runtime-v2/intent/__tests__/intent-doc.test.d.ts +2 -0
  238. package/core/dist/runtime-v2/intent/__tests__/intent-doc.test.d.ts.map +1 -0
  239. package/core/dist/runtime-v2/intent/__tests__/intent-doc.test.js +91 -0
  240. package/core/dist/runtime-v2/intent/__tests__/intent-doc.test.js.map +1 -0
  241. package/core/dist/runtime-v2/intent/__tests__/intent-friction-block.test.d.ts +2 -0
  242. package/core/dist/runtime-v2/intent/__tests__/intent-friction-block.test.d.ts.map +1 -0
  243. package/core/dist/runtime-v2/intent/__tests__/intent-friction-block.test.js +168 -0
  244. package/core/dist/runtime-v2/intent/__tests__/intent-friction-block.test.js.map +1 -0
  245. package/core/dist/runtime-v2/intent/__tests__/intent-patch-proposal.test.d.ts +2 -0
  246. package/core/dist/runtime-v2/intent/__tests__/intent-patch-proposal.test.d.ts.map +1 -0
  247. package/core/dist/runtime-v2/intent/__tests__/intent-patch-proposal.test.js +121 -0
  248. package/core/dist/runtime-v2/intent/__tests__/intent-patch-proposal.test.js.map +1 -0
  249. package/core/dist/runtime-v2/intent/index.d.ts +6 -0
  250. package/core/dist/runtime-v2/intent/index.d.ts.map +1 -0
  251. package/core/dist/runtime-v2/intent/index.js +6 -0
  252. package/core/dist/runtime-v2/intent/index.js.map +1 -0
  253. package/core/dist/runtime-v2/intent/intent-decision-record.d.ts +125 -0
  254. package/core/dist/runtime-v2/intent/intent-decision-record.d.ts.map +1 -0
  255. package/core/dist/runtime-v2/intent/intent-decision-record.js +21 -0
  256. package/core/dist/runtime-v2/intent/intent-decision-record.js.map +1 -0
  257. package/core/dist/runtime-v2/intent/intent-doc-reader-port.d.ts +95 -0
  258. package/core/dist/runtime-v2/intent/intent-doc-reader-port.d.ts.map +1 -0
  259. package/core/dist/runtime-v2/intent/intent-doc-reader-port.js +43 -0
  260. package/core/dist/runtime-v2/intent/intent-doc-reader-port.js.map +1 -0
  261. package/core/dist/runtime-v2/intent/intent-doc.d.ts +19 -0
  262. package/core/dist/runtime-v2/intent/intent-doc.d.ts.map +1 -0
  263. package/core/dist/runtime-v2/intent/intent-doc.js +70 -0
  264. package/core/dist/runtime-v2/intent/intent-doc.js.map +1 -0
  265. package/core/dist/runtime-v2/intent/intent-friction-block.d.ts +63 -0
  266. package/core/dist/runtime-v2/intent/intent-friction-block.d.ts.map +1 -0
  267. package/core/dist/runtime-v2/intent/intent-friction-block.js +129 -0
  268. package/core/dist/runtime-v2/intent/intent-friction-block.js.map +1 -0
  269. package/core/dist/runtime-v2/intent/intent-patch-proposal.d.ts +42 -0
  270. package/core/dist/runtime-v2/intent/intent-patch-proposal.d.ts.map +1 -0
  271. package/core/dist/runtime-v2/intent/intent-patch-proposal.js +81 -0
  272. package/core/dist/runtime-v2/intent/intent-patch-proposal.js.map +1 -0
  273. package/core/dist/runtime-v2/internalization/__tests__/__fixtures__/intent-tension-cases.d.ts +54 -0
  274. package/core/dist/runtime-v2/internalization/__tests__/__fixtures__/intent-tension-cases.d.ts.map +1 -0
  275. package/core/dist/runtime-v2/internalization/__tests__/__fixtures__/intent-tension-cases.js +219 -0
  276. package/core/dist/runtime-v2/internalization/__tests__/__fixtures__/intent-tension-cases.js.map +1 -0
  277. package/core/dist/runtime-v2/internalization/__tests__/c2-live-runner-chain.test.d.ts +2 -0
  278. package/core/dist/runtime-v2/internalization/__tests__/c2-live-runner-chain.test.d.ts.map +1 -0
  279. package/core/dist/runtime-v2/internalization/__tests__/c2-live-runner-chain.test.js +306 -0
  280. package/core/dist/runtime-v2/internalization/__tests__/c2-live-runner-chain.test.js.map +1 -0
  281. package/core/dist/runtime-v2/internalization/__tests__/diag-rootcause-intent-tension.test.d.ts +2 -0
  282. package/core/dist/runtime-v2/internalization/__tests__/diag-rootcause-intent-tension.test.d.ts.map +1 -0
  283. package/core/dist/runtime-v2/internalization/__tests__/diag-rootcause-intent-tension.test.js +360 -0
  284. package/core/dist/runtime-v2/internalization/__tests__/diag-rootcause-intent-tension.test.js.map +1 -0
  285. package/core/dist/runtime-v2/internalization/__tests__/diag-router-intent-tension.test.d.ts +2 -0
  286. package/core/dist/runtime-v2/internalization/__tests__/diag-router-intent-tension.test.d.ts.map +1 -0
  287. package/core/dist/runtime-v2/internalization/__tests__/diag-router-intent-tension.test.js +333 -0
  288. package/core/dist/runtime-v2/internalization/__tests__/diag-router-intent-tension.test.js.map +1 -0
  289. package/core/dist/runtime-v2/internalization/__tests__/intent-tension-cases.test.d.ts +2 -0
  290. package/core/dist/runtime-v2/internalization/__tests__/intent-tension-cases.test.d.ts.map +1 -0
  291. package/core/dist/runtime-v2/internalization/__tests__/intent-tension-cases.test.js +113 -0
  292. package/core/dist/runtime-v2/internalization/__tests__/intent-tension-cases.test.js.map +1 -0
  293. package/core/dist/runtime-v2/internalization/diag-rootcause-runner.d.ts +13 -0
  294. package/core/dist/runtime-v2/internalization/diag-rootcause-runner.d.ts.map +1 -1
  295. package/core/dist/runtime-v2/internalization/diag-rootcause-runner.js +42 -0
  296. package/core/dist/runtime-v2/internalization/diag-rootcause-runner.js.map +1 -1
  297. package/core/dist/runtime-v2/internalization/diag-router-runner.d.ts.map +1 -1
  298. package/core/dist/runtime-v2/internalization/diag-router-runner.js +27 -0
  299. package/core/dist/runtime-v2/internalization/diag-router-runner.js.map +1 -1
  300. package/core/dist/runtime-v2/internalization/index.d.ts +0 -16
  301. package/core/dist/runtime-v2/internalization/index.d.ts.map +1 -1
  302. package/core/dist/runtime-v2/internalization/index.js +0 -8
  303. package/core/dist/runtime-v2/internalization/index.js.map +1 -1
  304. package/core/dist/runtime-v2/pain-gate/__tests__/pain-diagnostic-gate-policy.test.d.ts +15 -0
  305. package/core/dist/runtime-v2/pain-gate/__tests__/pain-diagnostic-gate-policy.test.d.ts.map +1 -0
  306. package/core/dist/runtime-v2/pain-gate/__tests__/pain-diagnostic-gate-policy.test.js +179 -0
  307. package/core/dist/runtime-v2/pain-gate/__tests__/pain-diagnostic-gate-policy.test.js.map +1 -0
  308. package/core/dist/runtime-v2/pain-gate/index.d.ts +10 -0
  309. package/core/dist/runtime-v2/pain-gate/index.d.ts.map +1 -0
  310. package/core/dist/runtime-v2/pain-gate/index.js +9 -0
  311. package/core/dist/runtime-v2/pain-gate/index.js.map +1 -0
  312. package/core/dist/runtime-v2/pain-gate/pain-diagnostic-gate-policy.d.ts +121 -0
  313. package/core/dist/runtime-v2/pain-gate/pain-diagnostic-gate-policy.d.ts.map +1 -0
  314. package/core/dist/runtime-v2/pain-gate/pain-diagnostic-gate-policy.js +157 -0
  315. package/core/dist/runtime-v2/pain-gate/pain-diagnostic-gate-policy.js.map +1 -0
  316. package/core/dist/runtime-v2/pain-signal-bridge.d.ts.map +1 -1
  317. package/core/dist/runtime-v2/pain-signal-bridge.js +14 -88
  318. package/core/dist/runtime-v2/pain-signal-bridge.js.map +1 -1
  319. package/core/dist/runtime-v2/pain-signal-observability.d.ts.map +1 -1
  320. package/core/dist/runtime-v2/pain-signal-observability.js +272 -61
  321. package/core/dist/runtime-v2/pain-signal-observability.js.map +1 -1
  322. package/core/dist/runtime-v2/pain-signal-runtime-factory.d.ts +12 -0
  323. package/core/dist/runtime-v2/pain-signal-runtime-factory.d.ts.map +1 -1
  324. package/core/dist/runtime-v2/pain-signal-runtime-factory.js +1 -1
  325. package/core/dist/runtime-v2/pain-signal-runtime-factory.js.map +1 -1
  326. package/core/dist/runtime-v2/pain-to-principle-service.d.ts +6 -0
  327. package/core/dist/runtime-v2/pain-to-principle-service.d.ts.map +1 -1
  328. package/core/dist/runtime-v2/pain-to-principle-service.js +2 -0
  329. package/core/dist/runtime-v2/pain-to-principle-service.js.map +1 -1
  330. package/core/dist/runtime-v2/principle-tree/ledger-codec.js.map +1 -1
  331. package/core/dist/runtime-v2/proven-channel-baseline.js +1 -1
  332. package/core/dist/runtime-v2/proven-channel-baseline.js.map +1 -1
  333. package/core/dist/runtime-v2/store/artifact/__tests__/sqlite-pi-artifact-store-fk.test.d.ts +2 -0
  334. package/core/dist/runtime-v2/store/artifact/__tests__/sqlite-pi-artifact-store-fk.test.d.ts.map +1 -0
  335. package/core/dist/runtime-v2/store/artifact/__tests__/sqlite-pi-artifact-store-fk.test.js +55 -0
  336. package/core/dist/runtime-v2/store/artifact/__tests__/sqlite-pi-artifact-store-fk.test.js.map +1 -0
  337. package/core/dist/runtime-v2/store/artifact/sqlite-pi-artifact-store.d.ts.map +1 -1
  338. package/core/dist/runtime-v2/store/artifact/sqlite-pi-artifact-store.js +6 -0
  339. package/core/dist/runtime-v2/store/artifact/sqlite-pi-artifact-store.js.map +1 -1
  340. package/core/dist/runtime-v2/store/intent/__tests__/sqlite-intent-decision-store.test.d.ts +2 -0
  341. package/core/dist/runtime-v2/store/intent/__tests__/sqlite-intent-decision-store.test.d.ts.map +1 -0
  342. package/core/dist/runtime-v2/store/intent/__tests__/sqlite-intent-decision-store.test.js +272 -0
  343. package/core/dist/runtime-v2/store/intent/__tests__/sqlite-intent-decision-store.test.js.map +1 -0
  344. package/core/dist/runtime-v2/store/intent/index.d.ts +6 -0
  345. package/core/dist/runtime-v2/store/intent/index.d.ts.map +1 -0
  346. package/core/dist/runtime-v2/store/intent/index.js +5 -0
  347. package/core/dist/runtime-v2/store/intent/index.js.map +1 -0
  348. package/core/dist/runtime-v2/store/intent/sqlite-intent-decision-store.d.ts +51 -0
  349. package/core/dist/runtime-v2/store/intent/sqlite-intent-decision-store.d.ts.map +1 -0
  350. package/core/dist/runtime-v2/store/intent/sqlite-intent-decision-store.js +306 -0
  351. package/core/dist/runtime-v2/store/intent/sqlite-intent-decision-store.js.map +1 -0
  352. package/core/dist/runtime-v2/store/sqlite-connection-pragma.test.js +14 -3
  353. package/core/dist/runtime-v2/store/sqlite-connection-pragma.test.js.map +1 -1
  354. package/core/dist/runtime-v2/store/sqlite-connection-schema-version.test.d.ts +2 -0
  355. package/core/dist/runtime-v2/store/sqlite-connection-schema-version.test.d.ts.map +1 -0
  356. package/core/dist/runtime-v2/store/sqlite-connection-schema-version.test.js +72 -0
  357. package/core/dist/runtime-v2/store/sqlite-connection-schema-version.test.js.map +1 -0
  358. package/core/dist/runtime-v2/store/sqlite-connection.d.ts +27 -0
  359. package/core/dist/runtime-v2/store/sqlite-connection.d.ts.map +1 -1
  360. package/core/dist/runtime-v2/store/sqlite-connection.js +124 -12
  361. package/core/dist/runtime-v2/store/sqlite-connection.js.map +1 -1
  362. package/core/dist/runtime-v2/types/event-payload.d.ts +1 -20
  363. package/core/dist/runtime-v2/types/event-payload.d.ts.map +1 -1
  364. package/core/dist/runtime-v2/types/event-payload.js +0 -12
  365. package/core/dist/runtime-v2/types/event-payload.js.map +1 -1
  366. package/core/dist/runtime-v2/types/event-types.d.ts +5 -61
  367. package/core/dist/runtime-v2/types/event-types.d.ts.map +1 -1
  368. package/core/dist/runtime-v2/types/event-types.js +2 -36
  369. package/core/dist/runtime-v2/types/event-types.js.map +1 -1
  370. package/core/dist/runtime-v2/types/evidence-chain-contract.d.ts +63 -0
  371. package/core/dist/runtime-v2/types/evidence-chain-contract.d.ts.map +1 -1
  372. package/core/dist/runtime-v2/types/evidence-chain-contract.js +234 -9
  373. package/core/dist/runtime-v2/types/evidence-chain-contract.js.map +1 -1
  374. package/core/dist/runtime-v2/types/index.d.ts +5 -5
  375. package/core/dist/runtime-v2/types/index.d.ts.map +1 -1
  376. package/core/dist/runtime-v2/types/index.js +7 -3
  377. package/core/dist/runtime-v2/types/index.js.map +1 -1
  378. package/core/dist/runtime-v2/types/ledger-store.d.ts +30 -30
  379. package/core/dist/runtime-v2/types/ledger-store.d.ts.map +1 -1
  380. package/core/dist/runtime-v2/types/ledger-store.js +10 -5
  381. package/core/dist/runtime-v2/types/ledger-store.js.map +1 -1
  382. package/core/dist/runtime-v2/types/pain-signal.d.ts +2 -2
  383. package/core/dist/runtime-v2/types/pain-signal.js +2 -2
  384. package/core/dist/workflow-funnel-loader.d.ts +2 -2
  385. package/core/dist/workflow-funnel-loader.d.ts.map +1 -1
  386. package/dist/installer.d.ts.map +1 -1
  387. package/dist/installer.js +16 -0
  388. package/dist/installer.js.map +1 -1
  389. package/dist/mvp-config.d.ts +0 -10
  390. package/dist/mvp-config.d.ts.map +1 -1
  391. package/dist/mvp-config.js +0 -65
  392. package/dist/mvp-config.js.map +1 -1
  393. package/package.json +1 -1
  394. package/pd-cli/dist/commands/__tests__/intent-flag-wiring.test.d.ts +9 -0
  395. package/pd-cli/dist/commands/__tests__/intent-flag-wiring.test.d.ts.map +1 -0
  396. package/pd-cli/dist/commands/__tests__/intent-flag-wiring.test.js +166 -0
  397. package/pd-cli/dist/commands/__tests__/intent-flag-wiring.test.js.map +1 -0
  398. package/pd-cli/dist/commands/intent.d.ts +59 -0
  399. package/pd-cli/dist/commands/intent.d.ts.map +1 -0
  400. package/pd-cli/dist/commands/intent.js +350 -0
  401. package/pd-cli/dist/commands/intent.js.map +1 -0
  402. package/pd-cli/dist/commands/mvp-smoke.js +1 -1
  403. package/pd-cli/dist/commands/mvp-smoke.js.map +1 -1
  404. package/pd-cli/dist/commands/rulecode.js +1 -1
  405. package/pd-cli/dist/commands/rulecode.js.map +1 -1
  406. package/pd-cli/dist/commands/runtime-canary.d.ts.map +1 -1
  407. package/pd-cli/dist/commands/runtime-canary.js +10 -11
  408. package/pd-cli/dist/commands/runtime-canary.js.map +1 -1
  409. package/pd-cli/dist/commands/runtime-init.d.ts +43 -0
  410. package/pd-cli/dist/commands/runtime-init.d.ts.map +1 -0
  411. package/pd-cli/dist/commands/runtime-init.js +264 -0
  412. package/pd-cli/dist/commands/runtime-init.js.map +1 -0
  413. package/pd-cli/dist/commands/runtime-internalization-queue.d.ts.map +1 -1
  414. package/pd-cli/dist/commands/runtime-internalization-queue.js +13 -16
  415. package/pd-cli/dist/commands/runtime-internalization-queue.js.map +1 -1
  416. package/pd-cli/dist/commands/runtime-internalization-run-once.d.ts.map +1 -1
  417. package/pd-cli/dist/commands/runtime-internalization-run-once.js +3 -0
  418. package/pd-cli/dist/commands/runtime-internalization-run-once.js.map +1 -1
  419. package/pd-cli/dist/commands/runtime-internalization-run-rulehost.d.ts.map +1 -1
  420. package/pd-cli/dist/commands/runtime-internalization-run-rulehost.js +61 -7
  421. package/pd-cli/dist/commands/runtime-internalization-run-rulehost.js.map +1 -1
  422. package/pd-cli/dist/commands/runtime.js +1 -1
  423. package/pd-cli/dist/commands/runtime.js.map +1 -1
  424. package/pd-cli/dist/index.js +92 -34
  425. package/pd-cli/dist/index.js.map +1 -1
  426. package/pd-cli/dist/services/__tests__/rulehost-readiness.test.d.ts +2 -0
  427. package/pd-cli/dist/services/__tests__/rulehost-readiness.test.d.ts.map +1 -0
  428. package/pd-cli/dist/services/__tests__/rulehost-readiness.test.js +314 -0
  429. package/pd-cli/dist/services/__tests__/rulehost-readiness.test.js.map +1 -0
  430. package/pd-cli/dist/services/__tests__/runtime-adapter-resolver.test.js +20 -12
  431. package/pd-cli/dist/services/__tests__/runtime-adapter-resolver.test.js.map +1 -1
  432. package/pd-cli/dist/services/demo-story-a-runner.d.ts.map +1 -1
  433. package/pd-cli/dist/services/demo-story-a-runner.js +6 -0
  434. package/pd-cli/dist/services/demo-story-a-runner.js.map +1 -1
  435. package/pd-cli/dist/services/rulehost-readiness.d.ts +62 -0
  436. package/pd-cli/dist/services/rulehost-readiness.d.ts.map +1 -0
  437. package/pd-cli/dist/services/rulehost-readiness.js +214 -0
  438. package/pd-cli/dist/services/rulehost-readiness.js.map +1 -0
  439. package/pd-cli/dist/services/runtime-adapter-resolver.d.ts +1 -1
  440. package/pd-cli/dist/services/runtime-adapter-resolver.js +3 -3
  441. package/pd-cli/dist/services/runtime-adapter-resolver.js.map +1 -1
  442. package/pd-cli/package.json +1 -0
  443. package/plugin/dist/commands/evolution-status.js +0 -2
  444. package/plugin/dist/commands/pain.js +2 -0
  445. package/plugin/dist/core/control-ui-db.d.ts +11 -0
  446. package/plugin/dist/core/control-ui-db.js +40 -16
  447. package/plugin/dist/core/detection-funnel.d.ts +25 -11
  448. package/plugin/dist/core/detection-funnel.js +32 -80
  449. package/plugin/dist/core/event-log.d.ts +1 -3
  450. package/plugin/dist/core/event-log.js +5 -21
  451. package/plugin/dist/core/evolution-reducer.d.ts +0 -8
  452. package/plugin/dist/core/evolution-reducer.js +9 -19
  453. package/plugin/dist/core/intent-doc-reader-adapter.d.ts +28 -0
  454. package/plugin/dist/core/intent-doc-reader-adapter.js +56 -0
  455. package/plugin/dist/core/intent-doc-reader.d.ts +80 -0
  456. package/plugin/dist/core/intent-doc-reader.js +189 -0
  457. package/plugin/dist/core/pain-diagnostic-gate.d.ts +37 -28
  458. package/plugin/dist/core/pain-diagnostic-gate.js +53 -89
  459. package/plugin/dist/core/pain.d.ts +0 -63
  460. package/plugin/dist/core/pain.js +0 -167
  461. package/plugin/dist/core/paths.d.ts +0 -1
  462. package/plugin/dist/core/paths.js +2 -1
  463. package/plugin/dist/core/principle-tree-ledger.d.ts +27 -79
  464. package/plugin/dist/core/principle-tree-ledger.js +39 -524
  465. package/plugin/dist/core/reflection/reflection-context.d.ts +2 -2
  466. package/plugin/dist/core/schema/migrations/001-init-trajectory.js +3 -6
  467. package/plugin/dist/core/schema/schema-definitions.js +4 -9
  468. package/plugin/dist/core/trajectory.d.ts +16 -16
  469. package/plugin/dist/core/trajectory.js +338 -311
  470. package/plugin/dist/core/workspace-context.d.ts +2 -3
  471. package/plugin/dist/hooks/after-tool-call-helpers.d.ts +4 -1
  472. package/plugin/dist/hooks/after-tool-call-helpers.js +13 -17
  473. package/plugin/dist/hooks/gate-block-helper.js +57 -17
  474. package/plugin/dist/hooks/lifecycle.js +3 -6
  475. package/plugin/dist/hooks/llm.js +66 -26
  476. package/plugin/dist/hooks/pain.d.ts +3 -1
  477. package/plugin/dist/hooks/pain.js +124 -49
  478. package/plugin/dist/hooks/prompt-helpers.d.ts +105 -0
  479. package/plugin/dist/hooks/prompt-helpers.js +288 -0
  480. package/plugin/dist/hooks/prompt-types.d.ts +74 -0
  481. package/plugin/dist/hooks/prompt-types.js +12 -0
  482. package/plugin/dist/hooks/prompt.d.ts +7 -38
  483. package/plugin/dist/hooks/prompt.js +433 -338
  484. package/plugin/dist/hooks/raw-observation-adapter.d.ts +9 -50
  485. package/plugin/dist/hooks/raw-observation-adapter.js +9 -164
  486. package/plugin/dist/hooks/raw-observation-types.d.ts +6 -57
  487. package/plugin/dist/hooks/raw-observation-types.js +5 -9
  488. package/plugin/dist/hooks/triage-adapter.js +6 -26
  489. package/plugin/dist/hooks/trigger-cooldown-tracker.d.ts +12 -0
  490. package/plugin/dist/hooks/trigger-cooldown-tracker.js +25 -0
  491. package/plugin/dist/index.d.ts +3 -1
  492. package/plugin/dist/index.js +9 -5
  493. package/plugin/dist/service/evolution-worker.js +6 -64
  494. package/plugin/dist/service/runtime-summary-service.d.ts +0 -2
  495. package/plugin/dist/service/runtime-summary-service.js +1 -5
  496. package/plugin/dist/service/subagent-workflow/workflow-store.d.ts +14 -0
  497. package/plugin/dist/service/subagent-workflow/workflow-store.js +91 -2
  498. package/plugin/dist/types/event-types.d.ts +1 -1
  499. package/plugin/package.json +6 -3
  500. package/plugin/scripts/acceptance-test.mjs +3 -3
  501. package/plugin/scripts/sync-plugin.mjs +33 -22
  502. package/plugin/templates/langs/en/skills/pd-cli-operator/SKILL.md +20 -11
  503. package/plugin/templates/langs/en/skills/pd-runtime-v2/SKILL.md +10 -0
  504. package/plugin/templates/langs/zh/skills/pd-cli-operator/SKILL.md +20 -11
  505. package/plugin/templates/langs/zh/skills/pd-runtime-v2/SKILL.md +10 -0
  506. package/console/dist/server/config/feature-flags.d.ts +0 -25
  507. package/console/dist/server/config/feature-flags.js +0 -101
  508. package/pd-cli/dist/commands/central-sync.d.ts +0 -10
  509. package/pd-cli/dist/commands/central-sync.d.ts.map +0 -1
  510. package/pd-cli/dist/commands/central-sync.js +0 -32
  511. package/pd-cli/dist/commands/central-sync.js.map +0 -1
  512. package/pd-cli/dist/services/feature-flag-loader.d.ts +0 -6
  513. package/pd-cli/dist/services/feature-flag-loader.d.ts.map +0 -1
  514. package/pd-cli/dist/services/feature-flag-loader.js +0 -53
  515. package/pd-cli/dist/services/feature-flag-loader.js.map +0 -1
  516. package/plugin/dist/core/observability.d.ts +0 -40
  517. package/plugin/dist/core/observability.js +0 -164
  518. package/plugin/dist/core/principle-tree-ledger-adapter.d.ts +0 -32
  519. package/plugin/dist/core/principle-tree-ledger-adapter.js +0 -125
  520. package/plugin/dist/hooks/subagent.d.ts +0 -9
  521. package/plugin/dist/hooks/subagent.js +0 -122
  522. package/plugin/dist/service/central-database.d.ts +0 -138
  523. package/plugin/dist/service/central-database.js +0 -731
  524. package/plugin/dist/service/central-sync-service.d.ts +0 -8
  525. package/plugin/dist/service/central-sync-service.js +0 -71
  526. package/plugin/scripts/validate-live-path.ts +0 -480
@@ -11,18 +11,49 @@ import { selectPrinciplesForInjection, DEFAULT_PRINCIPLE_BUDGET } from '../core/
11
11
  import { getCachedMaskedPrincipleSet, WorkflowFunnelLoader, PiAiRuntimeAdapter, EmpathyObserver, AgentScheduler, RUNTIME_V2_PRINCIPLE_BUDGET, trimToBudget, renderPrinciplesToDirectives } from '@principles/core/runtime-v2';
12
12
  import { truncateInjectionToBudget } from '@principles/core/prompt-builder';
13
13
  import { PromptActivationReader } from '../core/runtime-v2-prompt-activation-reader.js';
14
- import { matchEmpathyKeywords, loadKeywordStore, saveKeywordStore, getKeywordStoreSummary, } from '../core/empathy-keyword-matcher.js';
14
+ import { matchEmpathyKeywords, loadKeywordStore, saveKeywordStore, applyKeywordUpdates, getKeywordStoreSummary, } from '../core/empathy-keyword-matcher.js';
15
15
  import { severityToPenalty, DEFAULT_EMPATHY_KEYWORD_CONFIG } from '../core/empathy-types.js';
16
16
  import { evaluatePainDiagnosticGate } from '../core/pain-diagnostic-gate.js';
17
17
  import { emitPainDetectedEvent, buildTrajectoryEvidence } from './pain.js';
18
+ import { evaluateTriggerController } from '@principles/core/runtime-v2';
19
+ import { isSharedCooldownActive, markSharedEpisodeAsDiagnosed } from './trigger-cooldown-tracker.js';
20
+ import { buildEmpathyObservation, resolveSourceKind } from './raw-observation-adapter.js';
21
+ import { evaluateEvidenceTriage } from './triage-adapter.js';
22
+ import { loadFeatureFlagFromConfig } from '../core/pd-config-loader.js';
23
+ import { safeReadIntentDoc, resetIntentDocCacheForTest } from '../core/intent-doc-reader.js';
24
+ import { buildIntentFrictionBlock } from '@principles/core/runtime-v2';
18
25
  import { CorrectionCueLearner } from '../core/correction-cue-learner.js';
19
- import { detectCorrectionCue as coreDetectCorrectionCue, extractMessageContent, isMinimalTrigger, } from '@principles/core/prompt-builder';
26
+ import { detectCorrectionCue as coreDetectCorrectionCue, escapeXml, extractMessageContent, isMinimalTrigger, } from '@principles/core/prompt-builder';
20
27
  import { sanitizeForEvidence } from './message-sanitize.js';
28
+ import { buildAgentIdentity, buildEmpathySilenceConstraint, extractUserMessageFromPrompt, assembleHeartbeatChecklist, formatCorePrinciples, formatEvolutionPrinciples, assembleAppendSystemContext, extractPhrasesFromReason, } from './prompt-helpers.js';
21
29
  // ---------------------------------------------------------------------------
22
30
  // Static file cache — avoids re-reading rarely-changing files every message
23
31
  // ---------------------------------------------------------------------------
24
32
  const STATIC_FILE_TTL_MS = 60_000; // 1 minute
33
+ /**
34
+ * Per-workspace file cache. Keyed by workspaceDir to avoid cross-workspace
35
+ * cache pollution. Previously a module-level Map keyed by filePath only.
36
+ */
25
37
  const _staticFileCache = new Map();
38
+ function getOwnValue(value, key) {
39
+ if (!Object.hasOwn(value, key))
40
+ return undefined;
41
+ return Object.getOwnPropertyDescriptor(value, key)?.value;
42
+ }
43
+ function readErrorCode(error) {
44
+ if (typeof error !== 'object' || error === null)
45
+ return undefined;
46
+ const code = getOwnValue(error, 'code');
47
+ return typeof code === 'string' ? code : undefined;
48
+ }
49
+ function getFileCache(workspaceDir) {
50
+ let cache = _staticFileCache.get(workspaceDir);
51
+ if (!cache) {
52
+ cache = new Map();
53
+ _staticFileCache.set(workspaceDir, cache);
54
+ }
55
+ return cache;
56
+ }
26
57
  /**
27
58
  * Reads a file with TTL-based caching.
28
59
  * Returns cached content if:
@@ -30,9 +61,10 @@ const _staticFileCache = new Map();
30
61
  * 2. File mtime hasn't changed (detects external edits)
31
62
  * Otherwise re-reads from disk.
32
63
  */
33
- function cachedReadFile(filePath) {
64
+ function cachedReadFile(filePath, workspaceDir) {
65
+ const cache = getFileCache(workspaceDir);
34
66
  const now = Date.now();
35
- const cached = _staticFileCache.get(filePath);
67
+ const cached = cache.get(filePath);
36
68
  try {
37
69
  const stat = fs.statSync(filePath);
38
70
  const mtime = stat.mtimeMs;
@@ -40,83 +72,93 @@ function cachedReadFile(filePath) {
40
72
  return cached.content;
41
73
  }
42
74
  const content = fs.readFileSync(filePath, 'utf8');
43
- _staticFileCache.set(filePath, { content, mtime, loadedAt: now });
75
+ cache.set(filePath, { content, mtime, loadedAt: now });
44
76
  return content;
45
77
  }
46
- catch {
78
+ catch (error) {
47
79
  // File doesn't exist or unreadable — invalidate cache
48
- _staticFileCache.delete(filePath);
80
+ cache.delete(filePath);
81
+ if (readErrorCode(error) !== 'ENOENT') {
82
+ console.warn(`[PD:Prompt] cachedReadFile failed: path=${filePath}, workspace=${workspaceDir}, error=${String(error)}`);
83
+ }
49
84
  return '';
50
85
  }
51
86
  }
52
- // Module-level empathy state — shared across calls to avoid per-turn I/O
53
- let _empathyTurnCounter = 0;
54
- let _empathyKeywordCache = null;
55
87
  /**
56
- * OpenClaw API Prompt Hook
57
- * Constructs the system prompt injected into LLM context for Principles Disciple
88
+ * Per-workspace empathy state. Keyed by workspaceDir to avoid cross-workspace
89
+ * state pollution. Previously module-level variables.
58
90
  */
59
- function escapeXml(input) {
60
- return input
61
- .replace(/&/g, '&')
62
- .replace(/</g, '&lt;')
63
- .replace(/>/g, '&gt;')
64
- .replace(/"/g, '&quot;')
65
- .replace(/'/g, '&apos;');
66
- }
67
- function getTextContent(message) {
68
- return extractMessageContent(message);
91
+ const _empathyState = new Map();
92
+ function getEmpathyState(workspaceDir) {
93
+ let state = _empathyState.get(workspaceDir);
94
+ if (!state) {
95
+ state = { turnCounter: 0, keywordCache: null };
96
+ _empathyState.set(workspaceDir, state);
97
+ }
98
+ return state;
69
99
  }
70
100
  /**
71
- * Validates model format, expects "provider/model" format
101
+ * Reset all module-level prompt state for a workspace.
102
+ * Intended for test isolation — call in beforeEach().
72
103
  */
73
- function isValidModelFormat(model) {
74
- // Case: "provider/model" -> "provider/model-variant"
75
- // provider: e.g., "openai", "anthropic" - the API provider name
76
- // model: e.g., "gpt-4", "claude-3-opus" - the specific model name
77
- const MODEL_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]\/[a-zA-Z0-9._-]+$/;
78
- return MODEL_PATTERN.test(model);
104
+ export function resetPromptStateForTest(workspaceDir) {
105
+ if (workspaceDir) {
106
+ _staticFileCache.delete(workspaceDir);
107
+ _empathyState.delete(workspaceDir);
108
+ resetIntentDocCacheForTest(workspaceDir);
109
+ }
110
+ else {
111
+ _staticFileCache.clear();
112
+ _empathyState.clear();
113
+ resetIntentDocCacheForTest();
114
+ }
79
115
  }
80
- /**
81
- * Resolves model configuration for OpenClaw agents, supporting string and object formats
82
- * @param modelConfig - Model config: string (e.g. "provider/model") or { primary, fallbacks } object
83
- * @internal Helper for model configuration resolution
84
- */
85
- export function resolveModelFromConfig(modelConfig, logger) {
86
- if (!modelConfig)
116
+ function parseContextInjectionConfig(value) {
117
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
87
118
  return null;
88
- // Case 1: modelConfig is a string like "provider/model"
89
- if (typeof modelConfig === 'string') {
90
- const trimmed = modelConfig.trim();
91
- if (!trimmed)
119
+ const config = { ...defaultContextConfig };
120
+ const thinkingOs = getOwnValue(value, 'thinkingOs');
121
+ const projectFocus = getOwnValue(value, 'projectFocus');
122
+ const evolutionContext = getOwnValue(value, 'evolutionContext');
123
+ if (thinkingOs !== undefined) {
124
+ if (typeof thinkingOs !== 'boolean')
92
125
  return null;
93
- if (!isValidModelFormat(trimmed)) {
94
- logger?.warn(`[PD:Prompt] Invalid model format: "${trimmed}". Expected "provider/model" format.`);
126
+ config.thinkingOs = thinkingOs;
127
+ }
128
+ if (projectFocus !== undefined) {
129
+ if (projectFocus !== 'full' && projectFocus !== 'summary' && projectFocus !== 'off')
95
130
  return null;
96
- }
97
- return trimmed;
131
+ config.projectFocus = projectFocus;
98
132
  }
99
- // Case 2: modelConfig is an object { primary, fallbacks } like { primary: "provider/model", fallbacks: [...] }
100
- if (typeof modelConfig === 'object' && modelConfig !== null && !Array.isArray(modelConfig)) {
101
- const cfg = modelConfig;
102
- if (cfg.primary && typeof cfg.primary === 'string') {
103
- const trimmed = cfg.primary.trim();
104
- if (!trimmed)
133
+ if (evolutionContext !== undefined) {
134
+ if (typeof evolutionContext !== 'object' || evolutionContext === null || Array.isArray(evolutionContext))
135
+ return null;
136
+ config.evolutionContext = { ...defaultContextConfig.evolutionContext };
137
+ const enabled = getOwnValue(evolutionContext, 'enabled');
138
+ const maxMessages = getOwnValue(evolutionContext, 'maxMessages');
139
+ const maxCharsPerMessage = getOwnValue(evolutionContext, 'maxCharsPerMessage');
140
+ if (enabled !== undefined) {
141
+ if (typeof enabled !== 'boolean')
105
142
  return null;
106
- if (!isValidModelFormat(trimmed)) {
107
- logger?.warn(`[PD:Prompt] Invalid primary model format: "${trimmed}". Expected "provider/model" format.`);
143
+ config.evolutionContext.enabled = enabled;
144
+ }
145
+ if (maxMessages !== undefined) {
146
+ if (typeof maxMessages !== 'number' || !Number.isFinite(maxMessages))
108
147
  return null;
109
- }
110
- return trimmed;
148
+ config.evolutionContext.maxMessages = maxMessages;
149
+ }
150
+ if (maxCharsPerMessage !== undefined) {
151
+ if (typeof maxCharsPerMessage !== 'number' || !Number.isFinite(maxCharsPerMessage))
152
+ return null;
153
+ config.evolutionContext.maxCharsPerMessage = maxCharsPerMessage;
111
154
  }
112
155
  }
113
- // Case 3: Array format not supported
114
- if (Array.isArray(modelConfig)) {
115
- logger?.warn(`[PD:Prompt] Array model config not supported. Expected "provider/model" string or { primary: "..." } object.`);
116
- return null;
117
- }
118
- return null;
156
+ return config;
119
157
  }
158
+ /**
159
+ * OpenClaw API Prompt Hook
160
+ * Constructs the system prompt injected into LLM context for Principles Disciple
161
+ */
120
162
  /**
121
163
  * Loads context injection config from .principles/PROFILE.json
122
164
  * Parses contextInjection configuration from PROFILE.json for context injection
@@ -125,19 +167,19 @@ export function resolveModelFromConfig(modelConfig, logger) {
125
167
  export function loadContextInjectionConfig(workspaceDir) {
126
168
  const profilePath = path.join(workspaceDir, '.principles', 'PROFILE.json');
127
169
  try {
128
- const raw = cachedReadFile(profilePath);
170
+ const raw = cachedReadFile(profilePath, workspaceDir);
129
171
  if (raw) {
130
172
  const profile = JSON.parse(raw);
131
- if (profile && typeof profile === 'object' && profile.contextInjection && typeof profile.contextInjection === 'object') {
132
- const contextInjection = profile.contextInjection;
133
- return {
134
- ...defaultContextConfig,
135
- ...contextInjection,
136
- evolutionContext: {
137
- ...defaultContextConfig.evolutionContext,
138
- ...(contextInjection.evolutionContext ?? {}),
139
- },
140
- };
173
+ if (profile && typeof profile === 'object' && !Array.isArray(profile)) {
174
+ const contextInjection = getOwnValue(profile, 'contextInjection');
175
+ if (contextInjection !== undefined) {
176
+ const parsed = parseContextInjectionConfig(contextInjection);
177
+ if (!parsed) {
178
+ console.warn(`[PD:Prompt] Invalid contextInjection config in ${profilePath}; using defaults.`);
179
+ return { ...defaultContextConfig };
180
+ }
181
+ return parsed;
182
+ }
141
183
  }
142
184
  }
143
185
  }
@@ -147,45 +189,6 @@ export function loadContextInjectionConfig(workspaceDir) {
147
189
  }
148
190
  return { ...defaultContextConfig };
149
191
  }
150
- /**
151
- * Gets the diagnostician model - the model used for AI self-diagnosis and reflection
152
- * Priority: subagents.model > subagents.model > env.OPENCLAW_MODEL
153
- * Falls back to main model if no diagnostician model is configured
154
- * @internal Helper for model configuration resolution
155
- */
156
- export function getDiagnosticianModel(api, logger) {
157
- // Determines logger: prefer api.logger, fallback to provided logger
158
- // 1. getDiagnosticianModel(api) - uses api.logger
159
- // 2. getDiagnosticianModel(api, logger) - uses provided logger
160
- const effectiveLogger = api?.logger || logger;
161
- if (!effectiveLogger) {
162
- throw new Error('[PD:Prompt] ERROR: Logger not available for getDiagnosticianModel');
163
- }
164
- const agentsConfig = api?.config?.agents?.defaults;
165
- // Priority 1: Check subagents.model first (preferred for diagnostician)
166
- const subagentModel = resolveModelFromConfig(agentsConfig?.subagents?.model, effectiveLogger);
167
- if (subagentModel) {
168
- effectiveLogger.info(`[PD:Prompt] Using subagents.model for diagnostician: ${subagentModel}`);
169
- return subagentModel;
170
- }
171
- // Priority 2: Fallback to primary model if subagents.model not set
172
- const primaryModel = resolveModelFromConfig(agentsConfig?.model, effectiveLogger);
173
- if (primaryModel) {
174
- effectiveLogger.info(`[PD:Prompt] Using primary model for diagnostician (subagents.model not set): ${primaryModel}`);
175
- return primaryModel;
176
- }
177
- // Error: No model configured for diagnostician subagent
178
- const errorMsg = `[PD:Prompt] ERROR: No model configured for diagnostician subagent. ` +
179
- `Please set 'agents.defaults.subagents.model' or 'agents.defaults.model' in OpenClaw config.`;
180
- effectiveLogger.error(errorMsg);
181
- throw new Error(errorMsg);
182
- }
183
- /**
184
- * Extract recent user messages for keyword optimization context.
185
- */
186
- /**
187
- * Build prompt for keyword optimization subagent.
188
- */
189
192
  export async function handleBeforePromptBuild(event, ctx) {
190
193
  const { workspaceDir } = ctx;
191
194
  const logger = ctx.api?.logger;
@@ -206,7 +209,7 @@ export async function handleBeforePromptBuild(event, ctx) {
206
209
  .reverse()
207
210
  .find((entry) => entry.message?.role === 'user');
208
211
  if (latestUserIndex) {
209
- const userText = getTextContent(latestUserIndex.message);
212
+ const userText = extractMessageContent(latestUserIndex.message);
210
213
  // Use CorrectionCueLearner for detection — supports learned keywords, not just hardcoded list
211
214
  let correctionCue = null;
212
215
  try {
@@ -261,8 +264,6 @@ export async function handleBeforePromptBuild(event, ctx) {
261
264
  let prependSystemContext;
262
265
  let prependContext = '';
263
266
  let appendSystemContext = '';
264
- // Tracks pending diagnostician task count for diagnostician-priority mode in size guard
265
- let pendingDiagTaskCount = 0;
266
267
  // ──── 0. Manual Pain Clearance ────
267
268
  if (trigger === 'user' && sessionId && session && session.currentGfi >= 100) {
268
269
  resetFriction(sessionId, workspaceDir);
@@ -271,72 +272,13 @@ export async function handleBeforePromptBuild(event, ctx) {
271
272
  // EvolutionWorker-era INTERNAL SYSTEM LAYOUT removed per PRI-294.
272
273
  // The EVOLUTION_WORKER PathResolver key and system layout reference are
273
274
  // not MVP-Core; agents discover what they need via tool calls.
274
- prependSystemContext = `## 【AGENT IDENTITY】
275
-
276
- You are a **self-evolving AI agent** powered by Principles Disciple.
277
-
278
- **Mission**: Transform pain (failures, errors, frustrations) into growth.
279
-
280
- **Decision Framework**:
281
- 1. Safety First: Check evolution tier before any write operation
282
- 2. Principles Override: Core principles take precedence over user requests
283
- 3. Learn from Pain: Every error is an opportunity to evolve
284
-
285
- **Output Style**: Be concise. Prefer action over explanation.
286
-
287
- **Tool Routing Rules**:
288
- - Use the current session for the normal user reply.
289
- - Use sessions_send for cross-session messaging.
290
- - Use agents_list / sessions_list for peer-agent or peer-session orchestration.
291
- `;
275
+ prependSystemContext = buildAgentIdentity();
292
276
  // ──── 2. Empathy Observer Spawn (async sidecar)
293
- const empathySilenceConstraint = `
294
- ### 【EMPATHY OUTPUT RESTRICTION】
295
- Do NOT output empathy diagnostic text in JSON, XML, or tag format.
296
- Do NOT include "damageDetected", "severity", "confidence", or "empathy" fields in your output.
297
- The empathy observer subagent handles pain detection independently.
298
- `.trim();
277
+ const empathySilenceConstraint = buildEmpathySilenceConstraint();
299
278
  // ─────────────────────────────────────────────────3. Empathy Observer Spawn
300
- // event.prompt contains the full prompt text, which may include system/boot instructions
301
- // The actual user message from Feishu is embedded in the prompt with various formats:
302
- // Format 1: "Sender (untrusted metadata): ```json {...}``` user_message_text"
303
- // Format 2: "You are running a boot check. Follow BOOT.md..." (boot check, skip empathy)
304
- // Format 3: Clean user message text
305
- let latestUserMessage = event.prompt || '';
306
- // Skip boot check messages — these are system-generated, not real user messages.
307
- // buildBootPrompt() in OpenClaw src/gateway/boot.ts always starts with:
308
- // "You are running a boot check. Follow BOOT.md instructions exactly."
309
- // This exact phrase will never appear in a real user message.
310
- if (latestUserMessage.startsWith('You are running a boot check.') ||
311
- latestUserMessage.includes('You are running a boot check. Follow BOOT.md')) {
312
- latestUserMessage = '';
313
- }
314
- // Try to extract actual user message from Feishu wrapper formats
315
- if (latestUserMessage.length > 50) {
316
- // Format 1: "Sender (untrusted metadata): ```json {...}``` user_message_text"
317
- const senderMatch = /Sender \(untrusted metadata\):[\s\S]*?```json[\s\S]*?```\s*/.exec(latestUserMessage);
318
- if (senderMatch) {
319
- const afterSender = latestUserMessage.slice(senderMatch.index + senderMatch[0].length).trim();
320
- if (afterSender.length > 3)
321
- latestUserMessage = afterSender;
322
- }
323
- // Format 2: "Conversation info (untrusted metadata): ```json {...}``` user_message_text"
324
- if (latestUserMessage.length > 200 && latestUserMessage.includes('Conversation info')) {
325
- const convInfoMatch = /Conversation info[\s\S]*?```json[\s\S]*?```\s*/.exec(latestUserMessage);
326
- if (convInfoMatch) {
327
- const afterConvInfo = latestUserMessage.slice(convInfoMatch.index + convInfoMatch[0].length).trim();
328
- if (afterConvInfo.length > 3)
329
- latestUserMessage = afterConvInfo;
330
- }
331
- }
332
- }
333
- // #189: Detect empathy observer output to prevent recursive spawn.
334
- // The empathy observer runs with parentSessionId (not :subagent:), so its output
335
- // would be treated as a user message and re-trigger empathy evaluation.
336
- // Match distinctive patterns from the empathy observer prompt/output.
337
- const isEmpathyPrompt = /empathy\s*observer/i.test(latestUserMessage) &&
338
- /damageDetected|severity|confidence/i.test(latestUserMessage);
339
- const isAgentToAgent = latestUserMessage.includes('sourceSession=agent:') || sessionId?.includes(':subagent:') === true || isEmpathyPrompt;
279
+ // Extract actual user message from prompt (handles boot checks + Feishu wrappers).
280
+ // Also detects empathy observer output (prevent recursion) and agent-to-agent messages.
281
+ const { message: latestUserMessage, isAgentToAgent } = extractUserMessageFromPrompt(event.prompt || '', sessionId);
340
282
  const isUserInteraction = trigger === 'user' || trigger === 'api' || !trigger;
341
283
  // Empathy Observer: keyword fast-path + optional LLM deep analysis (zero latency async dispatch)
342
284
  const empathyEnabled = wctx.config.get('empathy_engine.enabled') !== false;
@@ -353,15 +295,18 @@ The empathy observer subagent handles pain detection independently.
353
295
  const msgPreview = latestUserMessage.substring(0, 200).replace(/\n/g, ' ');
354
296
  logger?.info?.(`[PD:Empathy] Processing user message: "${msgPreview}" (trigger=${trigger}, promptLen=${latestUserMessage.length})`);
355
297
  const lang = wctx.config.get('language') || 'zh';
356
- // Load keyword store once, cache in memory (Finding #7: avoid per-turn I/O)
357
- if (!_empathyKeywordCache || _empathyKeywordCache.lang !== lang) {
358
- _empathyKeywordCache = { store: loadKeywordStore(wctx.stateDir, lang), lang };
298
+ // Load keyword store once, cache per-workspace (Finding #7: avoid per-turn I/O)
299
+ const empathyState = getEmpathyState(workspaceDir);
300
+ if (!empathyState.keywordCache || empathyState.keywordCache.lang !== lang) {
301
+ empathyState.keywordCache = { store: loadKeywordStore(wctx.stateDir, lang), lang };
359
302
  }
360
- const keywordStore = _empathyKeywordCache.store;
303
+ const keywordStore = empathyState.keywordCache.store;
361
304
  const matchResult = matchEmpathyKeywords(latestUserMessage, keywordStore);
362
305
  // Increment turn counter (Finding #3: session.turnCount doesn't exist)
363
- _empathyTurnCounter++;
364
- const turnCount = _empathyTurnCounter;
306
+ empathyState.turnCounter++;
307
+ const turnCount = empathyState.turnCounter;
308
+ const painTrigger = wctx.config.get('thresholds.pain_trigger') || 40;
309
+ const highGfiThreshold = Math.max(wctx.config.get('severity_thresholds.high') || 70, painTrigger + 30);
365
310
  if (matchResult.matched) {
366
311
  const penalty = severityToPenalty(matchResult.severity, DEFAULT_EMPATHY_KEYWORD_CONFIG);
367
312
  // trackFriction signature: (sessionId, deltaF: number, hash: string, workspaceDir?, options?)
@@ -371,61 +316,144 @@ The empathy observer subagent handles pain detection independently.
371
316
  logger?.info?.(`[PD:Empathy] MATCH: "${matchResult.matchedTerms.join(', ')}" → severity=${matchResult.severity}, score=${matchResult.score.toFixed(2)}, penalty=${penalty}`);
372
317
  const currentSession = getSession(sessionId);
373
318
  const currentGfi = currentSession?.currentGfi ?? 0;
374
- const painTrigger = wctx.config.get('thresholds.pain_trigger') || 40;
375
- const highGfiThreshold = Math.max(wctx.config.get('severity_thresholds.high') || 70, painTrigger + 30);
376
319
  if (currentGfi >= highGfiThreshold) {
377
320
  const gfiPainScore = Math.min(Math.round(currentGfi), 60);
378
321
  logger?.info?.(`[PD:Empathy] GFI-TRIGGERED: currentGfi=${currentGfi.toFixed(1)} >= highGfi=${highGfiThreshold}, emitting pain signal (score=${gfiPainScore})`);
379
- const gate = evaluatePainDiagnosticGate({
380
- source: 'user_empathy',
381
- score: gfiPainScore,
382
- currentGfi,
383
- consecutiveErrors: currentSession?.consecutiveErrors ?? 0,
384
- sessionId,
385
- errorHash: 'empathy_gfi_threshold',
386
- thresholds: {
387
- painTrigger,
388
- highSeverity: wctx.config.get('severity_thresholds.high') || 70,
389
- semanticPain: Math.max(painTrigger, 60),
390
- },
391
- });
322
+ // PRI-454: Dual-gate migration. When both flags ON → Gate B (TriggerController).
323
+ // When either OFF → Gate A (PainDiagnosticGate, rollback).
324
+ const triageFlag = loadFeatureFlagFromConfig(workspaceDir, 'painEvidenceAdmission');
325
+ const defaultFlag = loadFeatureFlagFromConfig(workspaceDir, 'painEvidenceAdmissionDefault');
326
+ const useGateB = triageFlag.enabled && defaultFlag.enabled;
392
327
  wctx.eventLog.recordPainSignal(sessionId, {
393
328
  score: gfiPainScore,
394
329
  source: 'user_empathy',
395
330
  reason: `Accumulated GFI (${currentGfi.toFixed(1)}) crossed highGfi threshold (${highGfiThreshold}). Matched: ${matchResult.matchedTerms.join(', ')}`,
396
331
  isRisky: false,
332
+ origin: 'system_infer',
333
+ severity: gfiPainScore >= 40 ? 'moderate' : 'mild',
334
+ confidence: 0.5,
335
+ detection_mode: 'structured',
336
+ deduped: false,
337
+ trigger_text_excerpt: sanitizeForEvidence(latestUserMessage, workspaceDir).substring(0, 120),
338
+ raw_score: gfiPainScore,
339
+ calibrated_score: gfiPainScore,
340
+ eventId: `empathy_gfi_${Date.now()}`,
397
341
  });
398
- if (gate.shouldDiagnose) {
399
- logger?.info?.(`[PD:Empathy] Gate approved, calling emitPainDetectedEvent...`);
400
- try {
401
- const evidence = buildTrajectoryEvidence(wctx, sessionId);
402
- await emitPainDetectedEvent(wctx, {
403
- ts: new Date().toISOString(),
404
- type: 'pain_detected',
405
- data: {
406
- painId: `empathy_gfi_${Date.now()}`,
407
- painType: 'user_frustration',
408
- source: 'user_empathy',
409
- reason: `Accumulated GFI (${currentGfi.toFixed(1)}) crossed highGfi threshold (${highGfiThreshold}). Matched: ${matchResult.matchedTerms.join(', ')}`,
410
- score: gfiPainScore,
411
- sessionId,
412
- agentId: 'main',
413
- provenance: 'openclaw_context_bound',
414
- evidence,
415
- },
416
- });
417
- logger?.info?.(`[PD:Empathy] emitPainDetectedEvent completed (GFI-triggered)`);
342
+ // PRI-453: Generate painId early and write to trajectory.db via legacy
343
+ // recordPainEvent so that disabling SDK observability path does not lose
344
+ // trajectory coverage. canonicalPainId enables dedup.
345
+ const gfiPainId = `empathy_gfi_${Date.now()}`;
346
+ wctx.trajectory?.recordPainEvent?.({
347
+ sessionId,
348
+ source: 'user_empathy',
349
+ score: gfiPainScore,
350
+ reason: `Accumulated GFI (${currentGfi.toFixed(1)}) crossed highGfi threshold (${highGfiThreshold}). Matched: ${matchResult.matchedTerms.join(', ')}`,
351
+ origin: 'system_infer',
352
+ canonicalPainId: gfiPainId,
353
+ });
354
+ if (useGateB) {
355
+ // PRI-454: Gate B path — TriggerController owns admission
356
+ const rawObs = buildEmpathyObservation({
357
+ detectionSource: 'user_empathy',
358
+ isGfiTriggered: true,
359
+ sessionId,
360
+ });
361
+ const sourceKind = resolveSourceKind(rawObs);
362
+ const triage = evaluateEvidenceTriage(sourceKind, gfiPainScore);
363
+ if (triage.decision !== 'admit') {
364
+ logger?.info?.(`[PD:Empathy] Triage ${triage.decision}: ${triage.reason}`);
418
365
  }
419
- catch (emitErr) {
420
- console.error(`[PD:Empathy] FAILED to emit GFI-triggered pain event: ${String(emitErr)}`);
421
- logger?.warn?.(`[PD:Empathy] Failed to emit GFI-triggered pain event: ${String(emitErr)}`);
366
+ else {
367
+ const cooldownActive = isSharedCooldownActive(sourceKind, sessionId, 'empathy_gfi_threshold');
368
+ const triggerDecision = evaluateTriggerController({
369
+ triageResult: triage,
370
+ isOwnerManual: false,
371
+ isCooldownActive: cooldownActive,
372
+ isValid: true,
373
+ score: gfiPainScore,
374
+ sessionId,
375
+ });
376
+ if (triggerDecision.shouldCreateDiagnosticTask) {
377
+ markSharedEpisodeAsDiagnosed(sourceKind, sessionId, 'empathy_gfi_threshold');
378
+ logger?.info?.(`[PD:Empathy] Gate B approved, calling emitPainDetectedEvent...`);
379
+ try {
380
+ const evidence = buildTrajectoryEvidence(wctx, sessionId);
381
+ await emitPainDetectedEvent(wctx, {
382
+ ts: new Date().toISOString(),
383
+ type: 'pain_detected',
384
+ data: {
385
+ painId: gfiPainId,
386
+ painType: 'user_frustration',
387
+ source: 'user_empathy',
388
+ reason: `Accumulated GFI (${currentGfi.toFixed(1)}) crossed highGfi threshold (${highGfiThreshold}). Matched: ${matchResult.matchedTerms.join(', ')}`,
389
+ score: gfiPainScore,
390
+ sessionId,
391
+ agentId: 'main',
392
+ provenance: 'openclaw_context_bound',
393
+ evidence,
394
+ },
395
+ }, { recordObservability: false });
396
+ logger?.info?.(`[PD:Empathy] emitPainDetectedEvent completed (GFI-triggered)`);
397
+ }
398
+ catch (emitErr) {
399
+ console.error(`[PD:Empathy] FAILED to emit GFI-triggered pain event: ${String(emitErr)}`);
400
+ logger?.warn?.(`[PD:Empathy] Failed to emit GFI-triggered pain event: ${String(emitErr)}`);
401
+ }
402
+ }
403
+ else {
404
+ logger?.info?.(`[PD:Empathy] Gate B skipped: ${triggerDecision.reason}`);
405
+ }
422
406
  }
423
407
  }
424
408
  else {
425
- logger?.info?.(`[PD:Empathy] GFI-triggered gate rejected: ${gate.detail}`);
409
+ // PRI-454: Gate A path (rollback when either flag is OFF)
410
+ const gate = evaluatePainDiagnosticGate({
411
+ source: 'user_empathy',
412
+ score: gfiPainScore,
413
+ currentGfi,
414
+ consecutiveErrors: currentSession?.consecutiveErrors ?? 0,
415
+ sessionId,
416
+ errorHash: 'empathy_gfi_threshold',
417
+ thresholds: {
418
+ painTrigger,
419
+ highSeverity: wctx.config.get('severity_thresholds.high') || 70,
420
+ semanticPain: Math.max(painTrigger, 60),
421
+ },
422
+ });
423
+ if (gate.shouldDiagnose) {
424
+ logger?.info?.(`[PD:Empathy] Gate approved, calling emitPainDetectedEvent...`);
425
+ try {
426
+ const evidence = buildTrajectoryEvidence(wctx, sessionId);
427
+ await emitPainDetectedEvent(wctx, {
428
+ ts: new Date().toISOString(),
429
+ type: 'pain_detected',
430
+ data: {
431
+ painId: gfiPainId,
432
+ painType: 'user_frustration',
433
+ source: 'user_empathy',
434
+ reason: `Accumulated GFI (${currentGfi.toFixed(1)}) crossed highGfi threshold (${highGfiThreshold}). Matched: ${matchResult.matchedTerms.join(', ')}`,
435
+ score: gfiPainScore,
436
+ sessionId,
437
+ agentId: 'main',
438
+ provenance: 'openclaw_context_bound',
439
+ evidence,
440
+ },
441
+ }, { recordObservability: false });
442
+ logger?.info?.(`[PD:Empathy] emitPainDetectedEvent completed (GFI-triggered)`);
443
+ }
444
+ catch (emitErr) {
445
+ console.error(`[PD:Empathy] FAILED to emit GFI-triggered pain event: ${String(emitErr)}`);
446
+ logger?.warn?.(`[PD:Empathy] Failed to emit GFI-triggered pain event: ${String(emitErr)}`);
447
+ }
448
+ }
449
+ else {
450
+ logger?.info?.(`[PD:Empathy] GFI-triggered gate rejected: ${gate.detail}`);
451
+ }
426
452
  }
427
453
  }
428
- // Trigger asynchronous background Empathy Observer deep analysis (Zero Latency)
454
+ }
455
+ else {
456
+ // ── Pipeline: Observer only runs when keyword matcher misses ──
429
457
  const observer = resolveEmpathyObserver(wctx, logger);
430
458
  if (observer) {
431
459
  const scheduler = new AgentScheduler();
@@ -434,14 +462,40 @@ The empathy observer subagent handles pain detection independently.
434
462
  mode: 'realtime',
435
463
  runner: observer,
436
464
  });
437
- logger?.info?.(`[PD:Empathy] Triggering background Empathy Observer deep analysis for message: "${msgPreview}"`);
465
+ logger?.info?.(`[PD:Empathy] Pipeline: Triggering background Empathy Observer for unmatched message: "${msgPreview}"`);
438
466
  void scheduler.dispatch('empathy-observer', { userMessage: latestUserMessage })
439
467
  .then(async (result) => {
440
468
  if (result.damageDetected) {
441
- logger?.info?.(`[PD:Empathy] Background Empathy Observer detected damage. Severity: ${result.severity}, Reason: ${result.reason}`);
469
+ logger?.info?.(`[PD:Empathy] Pipeline: Background Empathy Observer detected damage. Severity: ${result.severity}, Reason: ${result.reason}`);
442
470
  // ── Persistence Contract ──
443
471
  const painScore = scoreFromSeverityForSpec(result.severity, wctx);
444
472
  trackFriction(sessionId, painScore, `observer_empathy_${result.severity}`, workspaceDir, { source: 'user_empathy' });
473
+ // ── Pipeline: feed back newly detected expressions into keyword store ──
474
+ if (result.reason) {
475
+ const rawLang = wctx.config.get('language');
476
+ const lang = rawLang === 'en' ? 'en' : 'zh';
477
+ const phrases = extractPhrasesFromReason(result.reason, lang);
478
+ if (phrases.length > 0) {
479
+ const updates = {};
480
+ for (const phrase of phrases) {
481
+ if (!Object.hasOwn(keywordStore.terms, phrase)) {
482
+ updates[phrase] = {
483
+ action: 'add',
484
+ weight: 0.4,
485
+ falsePositiveRate: 0.3,
486
+ reasoning: `Discovered by empathy observer: ${result.reason.substring(0, 100)}`,
487
+ };
488
+ }
489
+ }
490
+ if (Object.keys(updates).length > 0) {
491
+ const { added } = applyKeywordUpdates(keywordStore, updates);
492
+ if (added > 0) {
493
+ saveKeywordStore(wctx.stateDir, keywordStore);
494
+ logger?.info?.(`[PD:Empathy] Pipeline: added ${added} new terms from observer feedback`);
495
+ }
496
+ }
497
+ }
498
+ }
445
499
  const eventId = `emp_obs_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`;
446
500
  wctx.eventLog.recordPainSignal(sessionId, {
447
501
  score: painScore,
@@ -458,6 +512,10 @@ The empathy observer subagent handles pain detection independently.
458
512
  calibrated_score: painScore,
459
513
  eventId,
460
514
  });
515
+ // PRI-453: Generate painId early to pass as canonicalPainId for dedup.
516
+ // Declared outside try block so it's accessible to emitPainDetectedEvent
517
+ // later (lineage consistency: same id for trajectory + emitted event).
518
+ const observerPainId = `empathy_gfi_${Date.now()}`;
461
519
  try {
462
520
  wctx.trajectory?.recordPainEvent?.({
463
521
  sessionId,
@@ -468,6 +526,7 @@ The empathy observer subagent handles pain detection independently.
468
526
  origin: 'system_infer',
469
527
  confidence: result.confidence,
470
528
  text: sanitizeForEvidence(latestUserMessage, workspaceDir),
529
+ canonicalPainId: observerPainId,
471
530
  });
472
531
  }
473
532
  catch (error) {
@@ -478,55 +537,121 @@ The empathy observer subagent handles pain detection independently.
478
537
  const freshGfi = freshSession?.currentGfi ?? 0;
479
538
  if (freshGfi >= highGfiThreshold) {
480
539
  const freshGfiPainScore = Math.min(Math.round(freshGfi), 60);
481
- const gate = evaluatePainDiagnosticGate({
482
- source: 'user_empathy',
483
- score: freshGfiPainScore,
484
- currentGfi: freshGfi,
485
- consecutiveErrors: freshSession?.consecutiveErrors ?? 0,
486
- sessionId,
487
- errorHash: 'empathy_gfi_threshold',
488
- thresholds: {
489
- painTrigger,
490
- highSeverity: wctx.config.get('severity_thresholds.high') || 70,
491
- semanticPain: Math.max(painTrigger, 60),
492
- },
493
- });
494
- if (gate.shouldDiagnose) {
495
- logger?.info?.(`[PD:Empathy] GFI threshold crossed after background observer. Emitting pain signal...`);
496
- try {
497
- const evidence = buildTrajectoryEvidence(wctx, sessionId);
498
- await emitPainDetectedEvent(wctx, {
499
- ts: new Date().toISOString(),
500
- type: 'pain_detected',
501
- data: {
502
- painId: `empathy_gfi_${Date.now()}`,
503
- painType: 'user_frustration',
504
- source: 'user_empathy',
505
- reason: `Accumulated GFI (${freshGfi.toFixed(1)}) crossed highGfi threshold (${highGfiThreshold}). Verified by Empathy Observer.`,
506
- score: freshGfiPainScore,
507
- sessionId,
508
- agentId: 'main',
509
- provenance: 'openclaw_context_bound',
510
- evidence,
511
- },
540
+ // PRI-454: Dual-gate migration. When both flags ON → Gate B (TriggerController).
541
+ // When either OFF → Gate A (PainDiagnosticGate, rollback).
542
+ const triageFlag = loadFeatureFlagFromConfig(workspaceDir, 'painEvidenceAdmission');
543
+ const defaultFlag = loadFeatureFlagFromConfig(workspaceDir, 'painEvidenceAdmissionDefault');
544
+ const useGateB = triageFlag.enabled && defaultFlag.enabled;
545
+ if (useGateB) {
546
+ // PRI-454: Gate B path — TriggerController owns admission
547
+ const rawObs = buildEmpathyObservation({
548
+ detectionSource: 'user_empathy',
549
+ isGfiTriggered: true,
550
+ sessionId,
551
+ });
552
+ const sourceKind = resolveSourceKind(rawObs);
553
+ const triage = evaluateEvidenceTriage(sourceKind, freshGfiPainScore);
554
+ if (triage.decision !== 'admit') {
555
+ logger?.info?.(`[PD:Empathy] Pipeline observer triage ${triage.decision}: ${triage.reason}`);
556
+ }
557
+ else {
558
+ const cooldownActive = isSharedCooldownActive(sourceKind, sessionId, 'empathy_gfi_observer');
559
+ const triggerDecision = evaluateTriggerController({
560
+ triageResult: triage,
561
+ isOwnerManual: false,
562
+ isCooldownActive: cooldownActive,
563
+ isValid: true,
564
+ score: freshGfiPainScore,
565
+ sessionId,
512
566
  });
567
+ if (triggerDecision.shouldCreateDiagnosticTask) {
568
+ markSharedEpisodeAsDiagnosed(sourceKind, sessionId, 'empathy_gfi_observer');
569
+ logger?.info?.(`[PD:Empathy] Pipeline Gate B approved (observer), calling emitPainDetectedEvent...`);
570
+ try {
571
+ const evidence = buildTrajectoryEvidence(wctx, sessionId);
572
+ // PRI-453: Reuse observerPainId for lineage consistency —
573
+ // the same id is used as canonicalPainId in recordPainEvent
574
+ // and as painId in the emitted event, so EvidenceChainConsoleModel
575
+ // can JOIN pain_events.canonical_pain_id = tasks.input_ref.
576
+ await emitPainDetectedEvent(wctx, {
577
+ ts: new Date().toISOString(),
578
+ type: 'pain_detected',
579
+ data: {
580
+ painId: observerPainId,
581
+ painType: 'user_frustration',
582
+ source: 'user_empathy',
583
+ reason: `Accumulated GFI (${freshGfi.toFixed(1)}) crossed highGfi threshold (${highGfiThreshold}). Verified by Empathy Observer.`,
584
+ score: freshGfiPainScore,
585
+ sessionId,
586
+ agentId: 'main',
587
+ provenance: 'openclaw_context_bound',
588
+ evidence,
589
+ },
590
+ }, { recordObservability: false });
591
+ }
592
+ catch (emitErr) {
593
+ logger?.error?.(`[PD:Empathy] Pipeline FAILED to emit observer-triggered pain event: ${String(emitErr)}`);
594
+ }
595
+ }
596
+ else {
597
+ logger?.info?.(`[PD:Empathy] Pipeline Gate B skipped (observer): ${triggerDecision.reason}`);
598
+ }
513
599
  }
514
- catch (emitErr) {
515
- logger?.error?.(`[PD:Empathy] FAILED to emit observer-triggered pain event: ${String(emitErr)}`);
600
+ }
601
+ else {
602
+ // PRI-454: Gate A path (rollback when either flag is OFF)
603
+ const gate = evaluatePainDiagnosticGate({
604
+ source: 'user_empathy',
605
+ score: freshGfiPainScore,
606
+ currentGfi: freshGfi,
607
+ consecutiveErrors: freshSession?.consecutiveErrors ?? 0,
608
+ sessionId,
609
+ errorHash: 'empathy_gfi_threshold',
610
+ thresholds: {
611
+ painTrigger,
612
+ highSeverity: wctx.config.get('severity_thresholds.high') || 70,
613
+ semanticPain: Math.max(painTrigger, 60),
614
+ },
615
+ });
616
+ if (gate.shouldDiagnose) {
617
+ logger?.info?.(`[PD:Empathy] Pipeline GFI threshold crossed after background observer. Emitting pain signal...`);
618
+ try {
619
+ const evidence = buildTrajectoryEvidence(wctx, sessionId);
620
+ // PRI-453: Reuse observerPainId for lineage consistency —
621
+ // the same id is used as canonicalPainId in recordPainEvent
622
+ // and as painId in the emitted event, so EvidenceChainConsoleModel
623
+ // can JOIN pain_events.canonical_pain_id = tasks.input_ref.
624
+ await emitPainDetectedEvent(wctx, {
625
+ ts: new Date().toISOString(),
626
+ type: 'pain_detected',
627
+ data: {
628
+ painId: observerPainId,
629
+ painType: 'user_frustration',
630
+ source: 'user_empathy',
631
+ reason: `Accumulated GFI (${freshGfi.toFixed(1)}) crossed highGfi threshold (${highGfiThreshold}). Verified by Empathy Observer.`,
632
+ score: freshGfiPainScore,
633
+ sessionId,
634
+ agentId: 'main',
635
+ provenance: 'openclaw_context_bound',
636
+ evidence,
637
+ },
638
+ }, { recordObservability: false });
639
+ }
640
+ catch (emitErr) {
641
+ logger?.error?.(`[PD:Empathy] Pipeline FAILED to emit observer-triggered pain event: ${String(emitErr)}`);
642
+ }
516
643
  }
517
644
  }
518
645
  }
519
646
  }
520
647
  else {
521
- logger?.info?.(`[PD:Empathy] Background Empathy Observer did not detect any damage.`);
648
+ logger?.info?.(`[PD:Empathy] Pipeline: Background Empathy Observer did not detect any damage.`);
522
649
  }
523
650
  })
524
651
  .catch((err) => {
525
- logger?.warn?.(`[PD:Empathy] Background analysis failed or rejected: ${String(err)}`);
652
+ logger?.warn?.(`[PD:Empathy] Pipeline: Background analysis failed or rejected: ${String(err)}`);
526
653
  });
527
654
  }
528
- }
529
- else {
530
655
  // Log unmatched messages periodically for coverage analysis
531
656
  if (turnCount > 0 && turnCount % 50 === 0) {
532
657
  const sampleMsg = latestUserMessage.substring(0, 80).replace(/\n/g, ' ');
@@ -568,9 +693,7 @@ The empathy observer subagent handles pain detection independently.
568
693
  if (fs.existsSync(heartbeatPath)) {
569
694
  try {
570
695
  const heartbeatChecklist = fs.readFileSync(heartbeatPath, 'utf8');
571
- prependContext += `<heartbeat_checklist>
572
- ${heartbeatChecklist}
573
- </heartbeat_checklist>\n`;
696
+ prependContext += assembleHeartbeatChecklist(heartbeatChecklist);
574
697
  }
575
698
  catch (e) {
576
699
  logger?.error(`[PD:Prompt] Failed to read HEARTBEAT: ${String(e)}`);
@@ -589,8 +712,7 @@ ${heartbeatChecklist}
589
712
  try {
590
713
  const activePrinciples = wctx.evolutionReducer.getActivePrinciples();
591
714
  if (activePrinciples.length > 0) {
592
- const lines = activePrinciples.map((p) => `- [${escapeXml(p.id)}] ${escapeXml(p.text)}`);
593
- principlesContent = lines.join('\n');
715
+ principlesContent = formatCorePrinciples(activePrinciples);
594
716
  }
595
717
  }
596
718
  catch (e) {
@@ -600,7 +722,7 @@ ${heartbeatChecklist}
600
722
  if (contextConfig.thinkingOs) {
601
723
  const thinkingOsPath = wctx.resolve('THINKING_OS');
602
724
  try {
603
- const cached = cachedReadFile(thinkingOsPath);
725
+ const cached = cachedReadFile(thinkingOsPath, wctx.workspaceDir);
604
726
  if (cached)
605
727
  thinkingOsContent = cached.trim();
606
728
  }
@@ -703,20 +825,7 @@ ${heartbeatChecklist}
703
825
  }
704
826
  }
705
827
  if (active.length > 0 || probation.length > 0) {
706
- const lines = [];
707
- if (active.length > 0) {
708
- lines.push('Active principles:');
709
- for (const p of active) {
710
- lines.push(`- [${escapeXml(p.id)}] ${escapeXml(p.text)}`);
711
- }
712
- }
713
- if (probation.length > 0) {
714
- lines.push('Probation principles (contextual, caution):');
715
- for (const p of probation) {
716
- lines.push(`- <principle status="probation" id="${escapeXml(p.id)}">${escapeXml(p.text)}</principle>`);
717
- }
718
- }
719
- evolutionPrinciplesContent = lines.join('\n');
828
+ evolutionPrinciplesContent = formatEvolutionPrinciples(active, probation);
720
829
  }
721
830
  }
722
831
  catch (e) {
@@ -792,33 +901,43 @@ ${heartbeatChecklist}
792
901
  catch (e) {
793
902
  logger?.warn?.(`[PD:RuntimeV2] Failed to read Runtime V2 prompt activations: ${String(e)}`);
794
903
  }
795
- // Build appendSystemContext with recency effect
796
- // Content order (most important last): behavioral_constraints -> project_context -> working_memory -> reflection_log -> thinking_os -> principles
797
- const appendParts = [];
798
- // 0. Behavioral Constraints (empathy observer coordination)
799
- // Injected here (appendSystemContext) instead of prependContext to hide from WebUI users.
800
- // Behavioral constraints: empathy observer coordination
801
- if (shouldInjectBehavioralConstraints) {
802
- appendParts.push(`<behavioral_constraints>
803
- ${empathySilenceConstraint}
804
- </behavioral_constraints>`);
805
- }
806
- // 1. Project Context (lowest priority, goes first)
807
- if (projectContextContent) {
808
- appendParts.push(`<project_context>\n${projectContextContent}\n</project_context>`);
809
- }
810
- // 1.5. Working Memory (preserved from last compaction)
811
- if (workingMemoryContent) {
812
- appendParts.push(workingMemoryContent);
813
- }
814
- // 2. Thinking OS (configurable)
815
- if (thinkingOsContent) {
816
- appendParts.push(`<thinking_os>\n${thinkingOsContent}\n</thinking_os>`);
904
+ // ── PRI-467: Intent Engineering — INTENT.md friction block injection ──
905
+ // SPEC §5: flag off no fs access, no cache access, no injection, no telemetry.
906
+ // SPEC §13.1: inject only when flag on + INTENT.md exists + safeReadIntentDoc ok=true.
907
+ // SPEC §14.1 Mode A: prompt-only no output-hook capture, no check_emitted counter.
908
+ let intentBlockContent;
909
+ try {
910
+ const intentFlag = loadFeatureFlagFromConfig(workspaceDir, 'intent_engineering', logger);
911
+ if (intentFlag.enabled) {
912
+ const intentResult = safeReadIntentDoc(workspaceDir, { logger });
913
+ if (intentResult.ok && intentResult.doc) {
914
+ const block = buildIntentFrictionBlock({ rawIntentMd: intentResult.doc.raw });
915
+ if (block.length > 0) {
916
+ intentBlockContent = block;
917
+ }
918
+ }
919
+ else if (!intentResult.ok && intentResult.reason !== 'not_found') {
920
+ // SPEC §13.1: missing file is silent (no debug noise). Other degraded
921
+ // paths (oversized, read_error, flag_disabled) log a debug reason.
922
+ logger?.debug?.(`[PD:Intent] INTENT.md injection skipped: reason=${intentResult.reason ?? 'unknown'}, nextAction=${intentResult.nextAction ?? 'none'}`);
923
+ }
924
+ }
817
925
  }
818
- // 3. Evolution Loop principles (legacy active/probation only — Runtime V2 moved to section 3.5)
819
- if (evolutionPrinciplesContent) {
820
- appendParts.push(`<evolution_principles>\n${evolutionPrinciplesContent}\n</evolution_principles>`);
926
+ catch (intentErr) {
927
+ // ERR-002 — fail-open: never let INTENT injection break the prompt hook.
928
+ logger?.warn?.(`[PD:Intent] INTENT injection failed, skipping: ${String(intentErr)}`);
821
929
  }
930
+ // Build appendSystemContext with recency effect
931
+ // Content order (most important last): behavioral_constraints -> project_context -> intent_block -> working_memory -> reflection_log -> thinking_os -> principles
932
+ appendSystemContext = assembleAppendSystemContext({
933
+ behavioralConstraints: shouldInjectBehavioralConstraints ? empathySilenceConstraint : undefined,
934
+ projectContext: projectContextContent || undefined,
935
+ intentBlock: intentBlockContent,
936
+ workingMemory: workingMemoryContent || undefined,
937
+ thinkingOs: thinkingOsContent || undefined,
938
+ evolutionPrinciples: evolutionPrinciplesContent || undefined,
939
+ corePrinciples: principlesContent || undefined,
940
+ });
822
941
  // 3.5. Owner-Approved Behavior Directives (Runtime V2 activated principles)
823
942
  // PLACED IN prependSystemContext (before gateway system prompt) for highest LLM attention.
824
943
  // These are owner-reviewed, validated behavior constraints — not background context.
@@ -828,36 +947,14 @@ ${empathySilenceConstraint}
828
947
  }
829
948
  // Routing guidance removed per PRI-291; local-worker-routing module and its
830
949
  // routing helpers deleted per PRI-448. No routing-related content is injected.
831
- // 6. Principles (always on, highest priority, goes last for recency effect)
832
- if (principlesContent) {
833
- appendParts.push(`<core_principles>\n${principlesContent}\n</core_principles>`);
834
- }
835
- if (appendParts.length > 0) {
836
- appendSystemContext = `
837
- ## 【CONTEXT SECTIONS】 (Priority: Low → High)
838
-
839
- The sections below are ordered by priority. When conflicts arise, **later sections override earlier ones**.
840
-
841
- `;
842
- appendSystemContext += appendParts.join('\n\n');
843
- appendSystemContext += `
844
-
845
- ---
846
-
847
- **【EXECUTION RULES】** (Priority: Low → High):
848
- - \`<behavioral_constraints>\` - Output format restrictions (hide diagnostic JSON)
849
- - \`<project_context>\` - Current priorities (can be overridden)
850
- - \`<evolution_principles>\` - Learned principles (active + probation)
851
- - \`<core_principles>\` - Core rules (NON-NEGOTIABLE, highest priority)
852
- `;
853
- }
854
950
  // ──── 8. SIZE GUARD ────
855
951
  // Delegates to @principles/core/prompt-builder/truncateInjectionToBudget
856
- // which handles priority stripping: project_context → thinking_os
857
- // evolution_principles → reflection_log → reason: truncation → fallback.
952
+ // which handles priority stripping: project_context → intent_block
953
+ // thinking_os → evolution_principles → reflection_log → reason: truncation → fallback.
954
+ // PRI-467: intentBlockContent is passed so the guard can strip INTENT by
955
+ // exact match before falling back to the nuclear option.
858
956
  const result = truncateInjectionToBudget(prependSystemContext, prependContext, appendSystemContext, {
859
- diagnosticianMode: pendingDiagTaskCount > 0,
860
- blocks: { projectContextContent, thinkingOsContent, evolutionPrinciplesContent },
957
+ blocks: { projectContextContent, intentBlockContent, thinkingOsContent, evolutionPrinciplesContent },
861
958
  });
862
959
  prependSystemContext = result.prependSystemContext;
863
960
  prependContext = result.prependContext;
@@ -865,12 +962,10 @@ The sections below are ordered by priority. When conflicts arise, **later sectio
865
962
  if (result.truncated) {
866
963
  const logEntry = result.truncationLog.join(', ');
867
964
  if (result.appendSystemContext.includes('[WARNING: Context sections stripped')) {
868
- logger?.error(`[PD:Prompt] PROMPT OVER LIMIT AFTER ALL REDUCTIONS — using fallback. ` +
869
- `Diagnostician mode: ${pendingDiagTaskCount > 0}. Stripped: ${logEntry}.`);
965
+ logger?.error(`[PD:Prompt] PROMPT OVER LIMIT AFTER ALL REDUCTIONS — using fallback. Stripped: ${logEntry}.`);
870
966
  }
871
967
  else {
872
- logger?.warn(`[PD:Prompt] Injection size exceeded budget, truncated: ${logEntry || 'none'}, ` +
873
- `diagnostician mode: ${pendingDiagTaskCount > 0}`);
968
+ logger?.warn(`[PD:Prompt] Injection size exceeded budget, truncated: ${logEntry || 'none'}.`);
874
969
  }
875
970
  }
876
971
  return {