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
@@ -0,0 +1,499 @@
1
+ /**
2
+ * IntentDecisions route — HTTP surface for IntentDecisionRecord (PRI-470, SPEC §21.7).
3
+ *
4
+ * Endpoints (all gated by the `intent_engineering` feature flag):
5
+ * - POST /api/v1/intent-decisions → create (201) or idempotent replay (200)
6
+ * - GET /api/v1/intent-decisions?painId=… → list by painId
7
+ * - GET /api/v1/intent-decisions?taskId=… → list by taskId
8
+ * - GET /api/v1/intent-decisions/summary → tallied summary (MUST precede /:id)
9
+ * - GET /api/v1/intent-decisions/:id → single record
10
+ * - POST /api/v1/intent-decisions/:id/follow-up → dispatch governed follow-up (PRI-471)
11
+ *
12
+ * PRI-471 follow-up types (SPEC §22.1.4):
13
+ * - link_candidate — record which existing principle candidate the Owner
14
+ * chose to link. Does NOT create a candidate.
15
+ * - guide_rulehost — returns the CLI guidance for promoting to RuleHost.
16
+ * Does NOT create a rule or approval directly.
17
+ * - generate_patch_proposal — generates a read-only Intent Patch Proposal and
18
+ * records its id on the decision. Does NOT modify
19
+ * `.principles/INTENT.md`.
20
+ *
21
+ * Runtime Contract rules applied:
22
+ * - Rule 1: parsed JSON body treated as `unknown` (ERR-001)
23
+ * - Rule 2: enum fields validated via core type guards, never `as` (ERR-001/ERR-005)
24
+ * - Rule 3: required fields fail loud → 400 with reason (ERR-009/ERR-010)
25
+ * - Rule 5: `Object.hasOwn()` for untrusted object keys (ERR-013)
26
+ * - Rule 9: every degraded path carries reason + nextAction (ERR-002)
27
+ */
28
+ import { isIntentTensionSource, isEvidenceStrength, isIntentRelatedField, isSuggestedOwnerAction, generateIntentPatchProposal, } from '@principles/core/runtime-v2';
29
+ import { IntentDecisionModel } from '../models/IntentDecisionModel.js';
30
+ import { sendJson, sendSuccess, sendError, sendNotFound, sendBadRequest } from '../utils/response.js';
31
+ import { parseQuery, readBody } from '../utils/request.js';
32
+ import { loadPdConfig, computeFlagsFromLoadResult } from '../config/pd-config-store.js';
33
+ // Per-workspace model cache (same pattern as approvals.ts). IntentDecisionModel
34
+ // holds workspaceDir, so each workspace needs its own instance.
35
+ const models = new Map();
36
+ function getModel(workspaceDir) {
37
+ let model = models.get(workspaceDir);
38
+ if (!model) {
39
+ model = new IntentDecisionModel(workspaceDir);
40
+ models.set(workspaceDir, model);
41
+ }
42
+ return model;
43
+ }
44
+ function getErrorMessage(err) {
45
+ return err instanceof Error ? err.message : String(err);
46
+ }
47
+ function isRecord(value) {
48
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
49
+ }
50
+ function tryParseJson(body) {
51
+ try {
52
+ return JSON.parse(body);
53
+ }
54
+ catch {
55
+ return undefined;
56
+ }
57
+ }
58
+ function parseJsonBody(body, res) {
59
+ const raw = tryParseJson(body);
60
+ if (raw === undefined) {
61
+ sendBadRequest(res, 'Invalid JSON body');
62
+ return null;
63
+ }
64
+ if (!isRecord(raw)) {
65
+ sendBadRequest(res, 'Request body must be a JSON object');
66
+ return null;
67
+ }
68
+ return raw;
69
+ }
70
+ /**
71
+ * Feature flag gate: `intent_engineering` MUST be enabled for any
72
+ * intent-decisions operation. Fail-closed with a structured reason (ERR-002).
73
+ * Returns true when the caller may proceed.
74
+ */
75
+ function checkFlag(workspaceDir, res) {
76
+ const configResult = loadPdConfig(workspaceDir);
77
+ const flagsResult = computeFlagsFromLoadResult(configResult);
78
+ const flagEnabled = flagsResult.flags.intent_engineering?.enabled === true;
79
+ if (!flagEnabled) {
80
+ sendError(res, 403, 'Feature intent_engineering is disabled', 'Feature intent_engineering is disabled', {
81
+ reason: 'flag_disabled',
82
+ nextAction: 'Enable intent_engineering flag to use this endpoint',
83
+ });
84
+ return false;
85
+ }
86
+ return true;
87
+ }
88
+ /**
89
+ * Validate an untrusted JSON object into a fully-typed IntentDecisionInput.
90
+ * Returns null (after sending a 400) on any validation failure.
91
+ *
92
+ * Required fields are destructured first (satisfies prefer-destructuring) then
93
+ * narrowed by type guards on the LOCAL binding — this keeps TypeScript's control
94
+ * flow narrowing reliable across subsequent function calls (Rule 2: no `as`).
95
+ * Optional fields use Object.hasOwn to distinguish present-vs-absent (Rule 5).
96
+ */
97
+ function validateInput(parsed, res) {
98
+ const { id, source, evidenceStrength, ownerAction, relatedIntentFields, evidenceRefs } = parsed;
99
+ // id — required non-empty string
100
+ if (typeof id !== 'string' || id === '') {
101
+ sendBadRequest(res, 'id is required and must be a non-empty string');
102
+ return null;
103
+ }
104
+ // source — required IntentTensionSource (Rule 2: type guard, no `as`)
105
+ if (!isIntentTensionSource(source)) {
106
+ sendBadRequest(res, 'source is required and must be a valid IntentTensionSource');
107
+ return null;
108
+ }
109
+ // evidenceStrength — required EvidenceStrength
110
+ if (!isEvidenceStrength(evidenceStrength)) {
111
+ sendBadRequest(res, 'evidenceStrength is required and must be a valid EvidenceStrength');
112
+ return null;
113
+ }
114
+ // ownerAction — required SuggestedOwnerAction
115
+ if (!isSuggestedOwnerAction(ownerAction)) {
116
+ sendBadRequest(res, 'ownerAction is required and must be a valid SuggestedOwnerAction');
117
+ return null;
118
+ }
119
+ // relatedIntentFields — required IntentRelatedField[] (Rule 4: element-wise)
120
+ if (!Array.isArray(relatedIntentFields)) {
121
+ sendBadRequest(res, 'relatedIntentFields is required and must be an array');
122
+ return null;
123
+ }
124
+ const typedFields = [];
125
+ for (const field of relatedIntentFields) {
126
+ if (!isIntentRelatedField(field)) {
127
+ sendBadRequest(res, 'relatedIntentFields contains an invalid IntentRelatedField: ' + String(field));
128
+ return null;
129
+ }
130
+ typedFields.push(field);
131
+ }
132
+ // evidenceRefs — required string[] (Rule 4: element-wise)
133
+ if (!Array.isArray(evidenceRefs)) {
134
+ sendBadRequest(res, 'evidenceRefs is required and must be an array');
135
+ return null;
136
+ }
137
+ const typedRefs = [];
138
+ for (const ref of evidenceRefs) {
139
+ if (typeof ref !== 'string') {
140
+ sendBadRequest(res, 'evidenceRefs must contain only strings');
141
+ return null;
142
+ }
143
+ typedRefs.push(ref);
144
+ }
145
+ // Optional string fields — validated only when present (Rule 5: Object.hasOwn)
146
+ const optionalStringFields = ['painId', 'taskId', 'runId', 'intentDocHash', 'note'];
147
+ const optional = {};
148
+ for (const key of optionalStringFields) {
149
+ if (Object.hasOwn(parsed, key)) {
150
+ const value = parsed[key];
151
+ if (typeof value !== 'string') {
152
+ sendBadRequest(res, key + ' must be a string when present');
153
+ return null;
154
+ }
155
+ optional[key] = value;
156
+ }
157
+ }
158
+ return {
159
+ id,
160
+ source,
161
+ evidenceStrength,
162
+ ownerAction,
163
+ relatedIntentFields: typedFields,
164
+ evidenceRefs: typedRefs,
165
+ painId: optional.painId,
166
+ taskId: optional.taskId,
167
+ runId: optional.runId,
168
+ intentDocHash: optional.intentDocHash,
169
+ note: optional.note,
170
+ };
171
+ }
172
+ /**
173
+ * Map a model-level failure (IntentDecisionRecordResultModel ok:false) to an
174
+ * HTTP error with reason + nextAction (Rule 9).
175
+ */
176
+ function sendModelFailure(res, result) {
177
+ if (result.reason === 'state_db_not_found') {
178
+ sendError(res, 409, 'workspace_not_initialized', result.reason, {
179
+ reason: result.reason,
180
+ nextAction: result.nextAction,
181
+ });
182
+ }
183
+ else {
184
+ sendError(res, 500, 'intent_decision_store_error', result.reason, {
185
+ reason: result.reason,
186
+ nextAction: result.nextAction,
187
+ });
188
+ }
189
+ }
190
+ const FOLLOW_UP_TYPES = new Set([
191
+ 'link_candidate',
192
+ 'guide_rulehost',
193
+ 'generate_patch_proposal',
194
+ ]);
195
+ /**
196
+ * Validate the body of POST /api/v1/intent-decisions/:id/follow-up.
197
+ *
198
+ * Body shape: `{ type: 'link_candidate' | 'guide_rulehost' | 'generate_patch_proposal', candidateId?: string }`
199
+ *
200
+ * - `type` is required and must be one of the three allowed values (Rule 2).
201
+ * - `candidateId` is REQUIRED when `type === 'link_candidate'` (Rule 3: fail loud).
202
+ * - `candidateId` is IGNORED for other types (not an error — the frontend may
203
+ * send it as part of a generic payload).
204
+ */
205
+ function validateFollowUpInput(parsed, res) {
206
+ if (!Object.hasOwn(parsed, 'type')) {
207
+ sendBadRequest(res, 'type is required');
208
+ return null;
209
+ }
210
+ const { type } = parsed;
211
+ if (typeof type !== 'string' || !FOLLOW_UP_TYPES.has(type)) {
212
+ sendBadRequest(res, 'type must be one of: link_candidate, guide_rulehost, generate_patch_proposal');
213
+ return null;
214
+ }
215
+ const typedType = type;
216
+ // candidateId: required for link_candidate, optional otherwise.
217
+ if (typedType === 'link_candidate') {
218
+ if (!Object.hasOwn(parsed, 'candidateId')) {
219
+ sendBadRequest(res, 'candidateId is required when type is link_candidate');
220
+ return null;
221
+ }
222
+ const { candidateId } = parsed;
223
+ if (typeof candidateId !== 'string') {
224
+ sendBadRequest(res, 'candidateId must be a string when type is link_candidate');
225
+ return null;
226
+ }
227
+ // Normalize at the trust boundary (EP-01): trim, then reject
228
+ // whitespace-only ids so they cannot pollute the audit trail.
229
+ const trimmed = candidateId.trim();
230
+ if (trimmed === '') {
231
+ sendBadRequest(res, 'candidateId must be a non-empty string when type is link_candidate');
232
+ return null;
233
+ }
234
+ return { type: typedType, candidateId: trimmed };
235
+ }
236
+ // For other types, candidateId is optional and ignored if present.
237
+ // We do not validate its type here — it is simply not read.
238
+ return { type: typedType, candidateId: '' };
239
+ }
240
+ /**
241
+ * Map a follow-up model-level failure to an HTTP error with reason + nextAction (Rule 9).
242
+ */
243
+ function sendFollowUpModelFailure(res, result) {
244
+ if (result.reason === 'decision_not_found') {
245
+ sendNotFound(res, result.nextAction);
246
+ }
247
+ else if (result.reason === 'state_db_not_found') {
248
+ sendError(res, 409, 'workspace_not_initialized', result.reason, {
249
+ reason: result.reason,
250
+ nextAction: result.nextAction,
251
+ });
252
+ }
253
+ else {
254
+ sendError(res, 500, 'intent_decision_follow_up_store_error', result.reason, {
255
+ reason: result.reason,
256
+ nextAction: result.nextAction,
257
+ });
258
+ }
259
+ }
260
+ /* eslint-disable @typescript-eslint/max-params */
261
+ export async function handleIntentDecisionsRoute(req, res, workspaceDir, subPath) {
262
+ // Feature flag gate applies to every method on this route.
263
+ if (!checkFlag(workspaceDir, res))
264
+ return;
265
+ const model = getModel(workspaceDir);
266
+ // POST /api/v1/intent-decisions — create / idempotent replay
267
+ if (req.method === 'POST' && (subPath === '' || subPath === '/')) {
268
+ try {
269
+ const body = await readBody(req);
270
+ const parsed = parseJsonBody(body, res);
271
+ if (!parsed)
272
+ return;
273
+ const input = validateInput(parsed, res);
274
+ if (!input)
275
+ return;
276
+ const result = await model.record(input);
277
+ if (!result.ok) {
278
+ sendModelFailure(res, result);
279
+ return;
280
+ }
281
+ // Response envelope matches IntentDecisionResultData: { record, created }.
282
+ // The `created` flag lets the frontend distinguish a fresh write from a
283
+ // replay (200 vs 201 HTTP status also conveys this).
284
+ const responseData = { record: result.record, created: result.created };
285
+ if (result.created) {
286
+ // Fresh create → 201 (sendJson with explicit status; sendSuccess is 200-only).
287
+ sendJson(res, 201, { success: true, data: responseData });
288
+ }
289
+ else {
290
+ // Idempotent replay → 200 with the pre-existing record.
291
+ sendSuccess(res, responseData);
292
+ }
293
+ }
294
+ catch (err) {
295
+ const message = getErrorMessage(err);
296
+ if (message === 'Request body too large') {
297
+ sendError(res, 413, 'payload_too_large', message);
298
+ }
299
+ else {
300
+ sendError(res, 500, 'intent_decision_create_error', message);
301
+ }
302
+ }
303
+ return;
304
+ }
305
+ // GET /api/v1/intent-decisions — list by painId OR taskId (exactly one required)
306
+ if (req.method === 'GET' && (subPath === '' || subPath === '/')) {
307
+ try {
308
+ const query = parseQuery(req.url ?? '');
309
+ const hasPainId = Object.hasOwn(query, 'painId');
310
+ const hasTaskId = Object.hasOwn(query, 'taskId');
311
+ if (!hasPainId && !hasTaskId) {
312
+ sendBadRequest(res, 'Either painId or taskId query parameter is required');
313
+ return;
314
+ }
315
+ if (hasPainId && hasTaskId) {
316
+ sendBadRequest(res, 'Provide either painId or taskId, not both');
317
+ return;
318
+ }
319
+ if (hasPainId) {
320
+ const { painId } = query;
321
+ if (!painId) {
322
+ sendBadRequest(res, 'painId must not be empty');
323
+ return;
324
+ }
325
+ const items = await model.listByPainId(painId);
326
+ sendSuccess(res, items);
327
+ }
328
+ else {
329
+ const { taskId } = query;
330
+ if (!taskId) {
331
+ sendBadRequest(res, 'taskId must not be empty');
332
+ return;
333
+ }
334
+ const items = await model.listByTaskId(taskId);
335
+ sendSuccess(res, items);
336
+ }
337
+ }
338
+ catch (err) {
339
+ sendError(res, 500, 'intent_decision_list_error', getErrorMessage(err));
340
+ }
341
+ return;
342
+ }
343
+ // GET /api/v1/intent-decisions/summary — MUST be matched before /:id
344
+ if (req.method === 'GET' && subPath === '/summary') {
345
+ try {
346
+ const summary = await model.getSummary();
347
+ sendSuccess(res, summary);
348
+ }
349
+ catch (err) {
350
+ sendError(res, 500, 'intent_decision_summary_error', getErrorMessage(err));
351
+ }
352
+ return;
353
+ }
354
+ // GET /api/v1/intent-decisions/:id — single record
355
+ const detailMatch = /^[/]([^/]+)$/.exec(subPath);
356
+ if (req.method === 'GET' && detailMatch) {
357
+ const [, rawId] = detailMatch;
358
+ if (!rawId) {
359
+ sendError(res, 400, 'invalid_id', 'Intent decision id is missing');
360
+ return;
361
+ }
362
+ let recordId;
363
+ try {
364
+ recordId = decodeURIComponent(rawId);
365
+ }
366
+ catch {
367
+ sendError(res, 400, 'invalid_id', 'Intent decision id contains invalid URI encoding');
368
+ return;
369
+ }
370
+ try {
371
+ const record = await model.getById(recordId);
372
+ if (!record) {
373
+ sendNotFound(res, 'Intent decision ' + recordId + ' not found');
374
+ return;
375
+ }
376
+ sendSuccess(res, record);
377
+ }
378
+ catch (err) {
379
+ sendError(res, 500, 'intent_decision_detail_error', getErrorMessage(err));
380
+ }
381
+ return;
382
+ }
383
+ // POST /api/v1/intent-decisions/:id/follow-up — PRI-471 governed follow-up
384
+ // (SPEC §22.1.4). MUST be matched before the catch-all 404 below.
385
+ const followUpMatch = /^[/]([^/]+)[/]follow-up$/.exec(subPath);
386
+ if (req.method === 'POST' && followUpMatch) {
387
+ try {
388
+ const [, rawId] = followUpMatch;
389
+ if (!rawId) {
390
+ sendError(res, 400, 'invalid_id', 'Intent decision id is missing');
391
+ return;
392
+ }
393
+ let decisionId;
394
+ try {
395
+ decisionId = decodeURIComponent(rawId);
396
+ }
397
+ catch {
398
+ sendError(res, 400, 'invalid_id', 'Intent decision id contains invalid URI encoding');
399
+ return;
400
+ }
401
+ const body = await readBody(req);
402
+ const parsed = parseJsonBody(body, res);
403
+ if (!parsed)
404
+ return;
405
+ const followUpInput = validateFollowUpInput(parsed, res);
406
+ if (!followUpInput)
407
+ return;
408
+ // ── guide_rulehost: pure guidance, no DB write ──────────────────────
409
+ // SPEC §22.1.4: promote_to_rulehost goes through the existing RuleHost
410
+ // candidate/approval path. PD does NOT create a rule or approval here —
411
+ // the Owner runs the CLI command, which enqueues a code_tool_hook
412
+ // approval that the Owner can review in the Governance Queue.
413
+ if (followUpInput.type === 'guide_rulehost') {
414
+ // Verify the decision exists so we don't return guidance for a
415
+ // non-existent record (fail loud, ERR-009).
416
+ const existing = await model.getById(decisionId);
417
+ if (!existing) {
418
+ sendNotFound(res, 'Intent decision ' + decisionId + ' not found');
419
+ return;
420
+ }
421
+ sendSuccess(res, {
422
+ type: 'guide_rulehost',
423
+ cliCommand: 'pd runtime rulehost',
424
+ note: 'Run this command in your workspace terminal. PD will create a RuleHost approval that you can review in the Governance Queue. After approval, the resulting rule candidate id can be linked back to this decision.',
425
+ decisionId,
426
+ });
427
+ return;
428
+ }
429
+ // ── generate_patch_proposal: pure function + DB write ───────────────
430
+ // SPEC §10 + §22.1.4: revise_intent creates a read-only patch proposal
431
+ // that the Owner can review and manually apply. PD does NOT auto-apply
432
+ // the patch to .principles/INTENT.md.
433
+ if (followUpInput.type === 'generate_patch_proposal') {
434
+ const existing = await model.getById(decisionId);
435
+ if (!existing) {
436
+ sendNotFound(res, 'Intent decision ' + decisionId + ' not found');
437
+ return;
438
+ }
439
+ const proposal = generateIntentPatchProposal(existing);
440
+ const patch = { patchProposalId: proposal.id };
441
+ const result = await model.updateFollowUp(decisionId, patch);
442
+ if (!result.ok) {
443
+ sendFollowUpModelFailure(res, result);
444
+ return;
445
+ }
446
+ sendSuccess(res, {
447
+ type: 'generate_patch_proposal',
448
+ decisionId,
449
+ record: result.record,
450
+ patchProposal: { id: proposal.id, markdown: proposal.markdown },
451
+ });
452
+ return;
453
+ }
454
+ // ── link_candidate: record which candidate the Owner chose ─────────
455
+ // SPEC §22.1.4: confirm_drift can link to an existing principle
456
+ // candidate. PD does NOT create a candidate here — candidates are
457
+ // created by the diagnostician committer during normal pain →
458
+ // diagnosis flow. The Owner links an existing candidate so the audit
459
+ // trail records which candidate the Owner chose to sediment this
460
+ // decision into.
461
+ if (followUpInput.type === 'link_candidate') {
462
+ const { candidateId } = followUpInput;
463
+ const patch = { resultingCandidateId: candidateId };
464
+ const result = await model.updateFollowUp(decisionId, patch);
465
+ if (!result.ok) {
466
+ sendFollowUpModelFailure(res, result);
467
+ return;
468
+ }
469
+ sendSuccess(res, {
470
+ type: 'link_candidate',
471
+ decisionId,
472
+ record: result.record,
473
+ linkedCandidateId: candidateId,
474
+ });
475
+ return;
476
+ }
477
+ // Unreachable: validateFollowUpInput already narrowed the union.
478
+ // Defensive guard for future enum additions.
479
+ sendBadRequest(res, 'Unknown follow-up type: ' + String(followUpInput.type));
480
+ }
481
+ catch (err) {
482
+ const message = getErrorMessage(err);
483
+ if (message === 'Request body too large') {
484
+ sendError(res, 413, 'payload_too_large', message);
485
+ }
486
+ else {
487
+ sendError(res, 500, 'intent_decision_follow_up_error', message);
488
+ }
489
+ }
490
+ return;
491
+ }
492
+ sendNotFound(res, 'Route /api/v1/intent-decisions' + subPath + ' not found');
493
+ }
494
+ /**
495
+ * Dispose hook for graceful shutdown — clears the per-workspace model cache.
496
+ */
497
+ export function disposeIntentDecisionModels() {
498
+ models.clear();
499
+ }
@@ -0,0 +1,3 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ export declare function handleIntentRoute(req: IncomingMessage, res: ServerResponse, workspaceDir: string): Promise<void>;
3
+ export declare function disposeIntentModels(): void;
@@ -0,0 +1,35 @@
1
+ import { IntentPageModel } from '../models/IntentPageModel.js';
2
+ import { sendSuccess, sendError } from '../utils/response.js';
3
+ import { loadPdConfig, computeFlagsFromLoadResult } from '../config/pd-config-store.js';
4
+ const MODEL_CACHE_TTL_MS = 30 * 1000;
5
+ const models = new Map();
6
+ function getModel(workspaceDir) {
7
+ const cached = models.get(workspaceDir);
8
+ if (cached && Date.now() - cached.cachedAt < MODEL_CACHE_TTL_MS) {
9
+ return cached.model;
10
+ }
11
+ const model = new IntentPageModel(workspaceDir);
12
+ models.set(workspaceDir, { model, cachedAt: Date.now() });
13
+ return model;
14
+ }
15
+ export async function handleIntentRoute(req, res, workspaceDir) {
16
+ if (req.method !== 'GET') {
17
+ sendError(res, 405, 'method_not_allowed', 'Only GET method is allowed');
18
+ return;
19
+ }
20
+ try {
21
+ const configResult = loadPdConfig(workspaceDir);
22
+ const flagsResult = computeFlagsFromLoadResult(configResult);
23
+ const flagEnabled = flagsResult.flags.intent_engineering?.enabled === true;
24
+ const model = getModel(workspaceDir);
25
+ const summary = await model.getSummary(flagEnabled);
26
+ sendSuccess(res, summary);
27
+ }
28
+ catch (err) {
29
+ sendError(res, 500, 'intent_route_error', err instanceof Error ? err.message : 'Unknown error');
30
+ }
31
+ }
32
+ export function disposeIntentModels() {
33
+ // IntentPageModel holds no persistent resources; just clear the cache.
34
+ models.clear();
35
+ }
@@ -73,11 +73,77 @@ async function getDecidedPrincipleIds(workspaceDir) {
73
73
  }
74
74
  }
75
75
  export async function handlePrinciplesRoute({ req, res, workspaceDir, subPath, }) {
76
+ const model = getModel(workspaceDir);
77
+ // ── POST Routes ─────────────────────────────────────────────────────────────
78
+ if (req.method === 'POST') {
79
+ // POST /api/principles/:id/archive
80
+ const archiveMatch = /^\/([^/]*)\/archive$/.exec(subPath);
81
+ if (archiveMatch) {
82
+ const [, rawPrincipleId] = archiveMatch;
83
+ if (!rawPrincipleId) {
84
+ sendError(res, 400, 'invalid_principle_id', 'Principle ID is missing');
85
+ return;
86
+ }
87
+ let principleId;
88
+ try {
89
+ principleId = decodeURIComponent(rawPrincipleId);
90
+ }
91
+ catch {
92
+ sendError(res, 400, 'invalid_principle_id', 'Principle ID contains invalid URL encoding');
93
+ return;
94
+ }
95
+ try {
96
+ const ok = await model.archivePrinciple(principleId);
97
+ if (ok) {
98
+ sendSuccess(res, { success: true, principleId });
99
+ }
100
+ else {
101
+ sendError(res, 500, 'archive_failed', `Failed to archive principle "${principleId}"`);
102
+ }
103
+ }
104
+ catch (err) {
105
+ sendError(res, 500, 'archive_failed', err instanceof Error ? err.message : String(err));
106
+ }
107
+ return;
108
+ }
109
+ // POST /api/principles/:id/unarchive
110
+ const unarchiveMatch = /^\/([^/]*)\/unarchive$/.exec(subPath);
111
+ if (unarchiveMatch) {
112
+ const [, rawPrincipleId] = unarchiveMatch;
113
+ if (!rawPrincipleId) {
114
+ sendError(res, 400, 'invalid_principle_id', 'Principle ID is missing');
115
+ return;
116
+ }
117
+ let principleId;
118
+ try {
119
+ principleId = decodeURIComponent(rawPrincipleId);
120
+ }
121
+ catch {
122
+ sendError(res, 400, 'invalid_principle_id', 'Principle ID contains invalid URL encoding');
123
+ return;
124
+ }
125
+ try {
126
+ const ok = await model.unarchivePrinciple(principleId);
127
+ if (ok) {
128
+ sendSuccess(res, { success: true, principleId });
129
+ }
130
+ else {
131
+ sendError(res, 500, 'unarchive_failed', `Failed to unarchive principle "${principleId}"`);
132
+ }
133
+ }
134
+ catch (err) {
135
+ sendError(res, 500, 'unarchive_failed', err instanceof Error ? err.message : String(err));
136
+ }
137
+ return;
138
+ }
139
+ sendNotFound(res, `Route ${req.method} /api/principles${subPath} not found`);
140
+ return;
141
+ }
142
+ // ── GET Routes ──────────────────────────────────────────────────────────────
76
143
  if (req.method !== 'GET') {
77
144
  sendNotFound(res, `Route /api/principles${subPath} not found`);
78
145
  return;
79
146
  }
80
- const model = getModel(workspaceDir);
81
147
  if (subPath === '' || subPath === '/') {
82
148
  try {
83
149
  // Parse query string for filter parameter
@@ -3,7 +3,6 @@
3
3
  *
4
4
  * GET /check — Check for updates
5
5
  * POST /apply — Apply an update
6
- * GET /status — Get current update status
7
6
  * POST /rollback — Rollback an update
8
7
  */
9
8
  import type { IncomingMessage, ServerResponse } from 'node:http';
@@ -183,6 +183,11 @@ async function doCheckForUpdates(currentVersion) {
183
183
  return {
184
184
  hasUpdate: false,
185
185
  currentVersion,
186
+ // Contract with the UI (UpdatePage.tsx) requires latestVersion to be a
187
+ // string. When the registry check fails we don't know the latest version,
188
+ // so emit an empty string to keep the response shape stable and let the
189
+ // UI surface `error` instead of crashing the whole page.
190
+ latestVersion: '',
186
191
  error: error instanceof Error ? error.message : 'Unknown error',
187
192
  };
188
193
  }
@@ -369,9 +374,17 @@ export async function handleUpdateRoute(req, res, workspaceDir, subPath) {
369
374
  return;
370
375
  }
371
376
  try {
377
+ // ERR-002 / Runtime Contract Rule 9: 当无法确定当前版本时(如插件未安装),
378
+ // 返回 degraded 状态 + reason,而非 500。前端 validateUpdateStatus 要求
379
+ // currentVersion/latestVersion 为 string,hasUpdate 为 boolean。
372
380
  const currentVersion = readCurrentVersion(pluginDir);
373
381
  if (!currentVersion) {
374
- sendError(res, 500, 'version_not_found', 'Could not determine current version');
382
+ sendSuccess(res, {
383
+ hasUpdate: false,
384
+ currentVersion: 'unknown',
385
+ latestVersion: '',
386
+ error: 'Could not determine current version (plugin not installed)',
387
+ });
375
388
  return;
376
389
  }
377
390
  const result = await doCheckForUpdates(currentVersion);
@@ -427,16 +440,6 @@ export async function handleUpdateRoute(req, res, workspaceDir, subPath) {
427
440
  }
428
441
  return;
429
442
  }
430
- // GET /status
431
- if (subPath === '/status') {
432
- if (req.method !== 'GET') {
433
- sendMethodNotAllowed(res);
434
- return;
435
- }
436
- const currentVersion = readCurrentVersion(pluginDir) ?? 'unknown';
437
- sendSuccess(res, { checking: false, updating: false, currentVersion });
438
- return;
439
- }
440
443
  // POST /rollback
441
444
  if (subPath === '/rollback') {
442
445
  if (req.method !== 'POST') {