create-principles-disciple 1.101.0 → 1.103.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 (317) hide show
  1. package/console/dist/server/config/pd-config-store.d.ts +50 -1
  2. package/console/dist/server/config/pd-config-store.js +364 -1
  3. package/console/dist/server/index.js +9 -0
  4. package/console/dist/server/models/ActivationsConsoleModel.d.ts +44 -5
  5. package/console/dist/server/models/ActivationsConsoleModel.js +118 -16
  6. package/console/dist/server/routes/config.js +92 -2
  7. package/console/dist/server/routes/onboarding.d.ts +34 -0
  8. package/console/dist/server/routes/onboarding.js +324 -0
  9. package/console/dist/ui/App.js +74 -15
  10. package/console/dist/ui/api.d.ts +60 -3
  11. package/console/dist/ui/api.js +63 -2
  12. package/console/dist/ui/components/onboarding/CircuitDiagram.d.ts +8 -0
  13. package/console/dist/ui/components/onboarding/CircuitDiagram.js +18 -0
  14. package/console/dist/ui/components/onboarding/DemoResultView.d.ts +22 -0
  15. package/console/dist/ui/components/onboarding/DemoResultView.js +33 -0
  16. package/console/dist/ui/i18n/en.json +189 -1
  17. package/console/dist/ui/i18n/zh-CN.json +189 -1
  18. package/console/dist/ui/pages/activation/ActivationPage.js +11 -11
  19. package/console/dist/ui/pages/activation/ActivationValidators.js +15 -14
  20. package/console/dist/ui/pages/control-center/AgentCard.js +66 -10
  21. package/console/dist/ui/pages/control-center/ControlCenterPage.js +3 -2
  22. package/console/dist/ui/pages/control-center/RuntimeProfileManager.d.ts +31 -0
  23. package/console/dist/ui/pages/control-center/RuntimeProfileManager.js +234 -0
  24. package/console/dist/ui/pages/debt/DebtPage.js +2 -2
  25. package/console/dist/ui/pages/settings/SettingsPage.js +64 -5
  26. package/console/dist/ui/pages/signal-keywords/KeywordEditDialog.d.ts +12 -0
  27. package/console/dist/ui/pages/signal-keywords/KeywordEditDialog.js +41 -0
  28. package/console/dist/ui/pages/signal-keywords/KeywordListSection.d.ts +10 -0
  29. package/console/dist/ui/pages/signal-keywords/KeywordListSection.js +39 -0
  30. package/console/dist/ui/pages/signal-keywords/PendingTermsSection.d.ts +14 -0
  31. package/console/dist/ui/pages/signal-keywords/PendingTermsSection.js +35 -0
  32. package/console/dist/ui/pages/signal-keywords/SignalKeywordsPage.d.ts +1 -0
  33. package/console/dist/ui/pages/signal-keywords/SignalKeywordsPage.js +49 -0
  34. package/console/dist/ui/pages/welcome/WelcomePage.d.ts +5 -0
  35. package/console/dist/ui/pages/welcome/WelcomePage.js +185 -0
  36. package/console/dist/ui/utils/__tests__/onboarding-state.test.d.ts +1 -0
  37. package/console/dist/ui/utils/__tests__/onboarding-state.test.js +72 -0
  38. package/console/dist/ui/utils/agent-metadata.d.ts +13 -0
  39. package/console/dist/ui/utils/agent-metadata.js +26 -0
  40. package/console/dist/ui/utils/onboarding-state.d.ts +25 -0
  41. package/console/dist/ui/utils/onboarding-state.js +77 -0
  42. package/console/dist/ui/utils/signal-keywords-api.d.ts +68 -0
  43. package/console/dist/ui/utils/signal-keywords-api.js +121 -0
  44. package/console/dist/ui/utils/signal-keywords-types.d.ts +57 -0
  45. package/console/dist/ui/utils/signal-keywords-types.js +10 -0
  46. package/console/dist/ui/utils/signal-keywords-validators.d.ts +20 -0
  47. package/console/dist/ui/utils/signal-keywords-validators.js +114 -0
  48. package/console/dist/ui/utils/validators.d.ts +26 -3
  49. package/console/dist/ui/utils/validators.js +23 -6
  50. package/console/dist/web/assets/app.css +442 -0
  51. package/console/dist/web/assets/app.js +7793 -1966
  52. package/core/dist/index.d.ts +2 -0
  53. package/core/dist/index.d.ts.map +1 -1
  54. package/core/dist/index.js +1 -0
  55. package/core/dist/index.js.map +1 -1
  56. package/core/dist/runtime-v2/__tests__/architecture-regression.test.js +14 -2
  57. package/core/dist/runtime-v2/__tests__/architecture-regression.test.js.map +1 -1
  58. package/core/dist/runtime-v2/__tests__/evaluator-runner-vslice-v2.test.js +28 -0
  59. package/core/dist/runtime-v2/__tests__/evaluator-runner-vslice-v2.test.js.map +1 -1
  60. package/core/dist/runtime-v2/__tests__/pain-to-principle-service.test.js +8 -0
  61. package/core/dist/runtime-v2/__tests__/pain-to-principle-service.test.js.map +1 -1
  62. package/core/dist/runtime-v2/__tests__/resolve-runtime-config-from-pd-config.test.js +8 -10
  63. package/core/dist/runtime-v2/__tests__/resolve-runtime-config-from-pd-config.test.js.map +1 -1
  64. package/core/dist/runtime-v2/activation/index.d.ts +1 -1
  65. package/core/dist/runtime-v2/activation/index.d.ts.map +1 -1
  66. package/core/dist/runtime-v2/activation/index.js +1 -1
  67. package/core/dist/runtime-v2/activation/index.js.map +1 -1
  68. package/core/dist/runtime-v2/activation/writers/__tests__/rule-host-writer.test.js +240 -1
  69. package/core/dist/runtime-v2/activation/writers/__tests__/rule-host-writer.test.js.map +1 -1
  70. package/core/dist/runtime-v2/activation/writers/rule-host-writer.d.ts +12 -0
  71. package/core/dist/runtime-v2/activation/writers/rule-host-writer.d.ts.map +1 -1
  72. package/core/dist/runtime-v2/activation/writers/rule-host-writer.js +81 -1
  73. package/core/dist/runtime-v2/activation/writers/rule-host-writer.js.map +1 -1
  74. package/core/dist/runtime-v2/adapter/__tests__/l2-resolve-model-compat.test.d.ts +2 -0
  75. package/core/dist/runtime-v2/adapter/__tests__/l2-resolve-model-compat.test.d.ts.map +1 -0
  76. package/core/dist/runtime-v2/adapter/__tests__/l2-resolve-model-compat.test.js +81 -0
  77. package/core/dist/runtime-v2/adapter/__tests__/l2-resolve-model-compat.test.js.map +1 -0
  78. package/core/dist/runtime-v2/adapter/__tests__/pi-ai-runtime-adapter.test.js +19 -0
  79. package/core/dist/runtime-v2/adapter/__tests__/pi-ai-runtime-adapter.test.js.map +1 -1
  80. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.d.ts +8 -0
  81. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.d.ts.map +1 -1
  82. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js +4 -1
  83. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js.map +1 -1
  84. package/core/dist/runtime-v2/candidate-intake-service.d.ts.map +1 -1
  85. package/core/dist/runtime-v2/candidate-intake-service.js +92 -6
  86. package/core/dist/runtime-v2/candidate-intake-service.js.map +1 -1
  87. package/core/dist/runtime-v2/candidate-intake.d.ts +38 -0
  88. package/core/dist/runtime-v2/candidate-intake.d.ts.map +1 -1
  89. package/core/dist/runtime-v2/candidate-intake.js +48 -0
  90. package/core/dist/runtime-v2/candidate-intake.js.map +1 -1
  91. package/core/dist/runtime-v2/config/__tests__/internal-agent-names.test.d.ts +2 -0
  92. package/core/dist/runtime-v2/config/__tests__/internal-agent-names.test.d.ts.map +1 -0
  93. package/core/dist/runtime-v2/config/__tests__/internal-agent-names.test.js +15 -0
  94. package/core/dist/runtime-v2/config/__tests__/internal-agent-names.test.js.map +1 -0
  95. package/core/dist/runtime-v2/config/__tests__/pd-config-agent-binding.test.js +33 -1
  96. package/core/dist/runtime-v2/config/__tests__/pd-config-agent-binding.test.js.map +1 -1
  97. package/core/dist/runtime-v2/config/__tests__/pd-config-contract.test.js +10 -5
  98. package/core/dist/runtime-v2/config/__tests__/pd-config-contract.test.js.map +1 -1
  99. package/core/dist/runtime-v2/config/__tests__/pd-config-redaction-deep.test.js +1 -0
  100. package/core/dist/runtime-v2/config/__tests__/pd-config-redaction-deep.test.js.map +1 -1
  101. package/core/dist/runtime-v2/config/__tests__/pd-config-validation-edge-cases.test.js +54 -33
  102. package/core/dist/runtime-v2/config/__tests__/pd-config-validation-edge-cases.test.js.map +1 -1
  103. package/core/dist/runtime-v2/config/index.d.ts +5 -3
  104. package/core/dist/runtime-v2/config/index.d.ts.map +1 -1
  105. package/core/dist/runtime-v2/config/index.js +8 -2
  106. package/core/dist/runtime-v2/config/index.js.map +1 -1
  107. package/core/dist/runtime-v2/config/pd-config-agent-binding.d.ts +2 -0
  108. package/core/dist/runtime-v2/config/pd-config-agent-binding.d.ts.map +1 -1
  109. package/core/dist/runtime-v2/config/pd-config-agent-binding.js +3 -0
  110. package/core/dist/runtime-v2/config/pd-config-agent-binding.js.map +1 -1
  111. package/core/dist/runtime-v2/config/pd-config-defaults.d.ts +8 -3
  112. package/core/dist/runtime-v2/config/pd-config-defaults.d.ts.map +1 -1
  113. package/core/dist/runtime-v2/config/pd-config-defaults.js +39 -3
  114. package/core/dist/runtime-v2/config/pd-config-defaults.js.map +1 -1
  115. package/core/dist/runtime-v2/config/pd-config-effective.d.ts.map +1 -1
  116. package/core/dist/runtime-v2/config/pd-config-effective.js +25 -1
  117. package/core/dist/runtime-v2/config/pd-config-effective.js.map +1 -1
  118. package/core/dist/runtime-v2/config/pd-config-redaction.d.ts.map +1 -1
  119. package/core/dist/runtime-v2/config/pd-config-redaction.js +14 -2
  120. package/core/dist/runtime-v2/config/pd-config-redaction.js.map +1 -1
  121. package/core/dist/runtime-v2/config/pd-config-types.d.ts +116 -1
  122. package/core/dist/runtime-v2/config/pd-config-types.d.ts.map +1 -1
  123. package/core/dist/runtime-v2/config/pd-config-types.js +7 -0
  124. package/core/dist/runtime-v2/config/pd-config-types.js.map +1 -1
  125. package/core/dist/runtime-v2/config/pd-config-validate.d.ts.map +1 -1
  126. package/core/dist/runtime-v2/config/pd-config-validate.js +125 -7
  127. package/core/dist/runtime-v2/config/pd-config-validate.js.map +1 -1
  128. package/core/dist/runtime-v2/config/pd-profile-constants.d.ts +27 -0
  129. package/core/dist/runtime-v2/config/pd-profile-constants.d.ts.map +1 -0
  130. package/core/dist/runtime-v2/config/pd-profile-constants.js +145 -0
  131. package/core/dist/runtime-v2/config/pd-profile-constants.js.map +1 -0
  132. package/core/dist/runtime-v2/config/pd-validate-profile.d.ts +28 -0
  133. package/core/dist/runtime-v2/config/pd-validate-profile.d.ts.map +1 -0
  134. package/core/dist/runtime-v2/config/pd-validate-profile.js +482 -0
  135. package/core/dist/runtime-v2/config/pd-validate-profile.js.map +1 -0
  136. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js +26 -0
  137. package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js.map +1 -1
  138. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts.map +1 -1
  139. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js +8 -0
  140. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js.map +1 -1
  141. package/core/dist/runtime-v2/index.d.ts +7 -4
  142. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  143. package/core/dist/runtime-v2/index.js +5 -2
  144. package/core/dist/runtime-v2/index.js.map +1 -1
  145. package/core/dist/runtime-v2/internalization/__tests__/artificer-prompt-builder-v2.test.js +32 -0
  146. package/core/dist/runtime-v2/internalization/__tests__/artificer-prompt-builder-v2.test.js.map +1 -1
  147. package/core/dist/runtime-v2/internalization/__tests__/artificer-rule-output.test.js +139 -0
  148. package/core/dist/runtime-v2/internalization/__tests__/artificer-rule-output.test.js.map +1 -1
  149. package/core/dist/runtime-v2/internalization/__tests__/diag-chain-e2e.test.js +20 -1
  150. package/core/dist/runtime-v2/internalization/__tests__/diag-chain-e2e.test.js.map +1 -1
  151. package/core/dist/runtime-v2/internalization/__tests__/diag-rootcause-intent-tension.test.js +7 -0
  152. package/core/dist/runtime-v2/internalization/__tests__/diag-rootcause-intent-tension.test.js.map +1 -1
  153. package/core/dist/runtime-v2/internalization/__tests__/diag-rootcause-runner.test.js +7 -6
  154. package/core/dist/runtime-v2/internalization/__tests__/diag-rootcause-runner.test.js.map +1 -1
  155. package/core/dist/runtime-v2/internalization/__tests__/internalization-task-guards.test.js +1 -53
  156. package/core/dist/runtime-v2/internalization/__tests__/internalization-task-guards.test.js.map +1 -1
  157. package/core/dist/runtime-v2/internalization/__tests__/rule-host-input-builder.test.js +23 -0
  158. package/core/dist/runtime-v2/internalization/__tests__/rule-host-input-builder.test.js.map +1 -1
  159. package/core/dist/runtime-v2/internalization/artificer-output.d.ts +7 -0
  160. package/core/dist/runtime-v2/internalization/artificer-output.d.ts.map +1 -1
  161. package/core/dist/runtime-v2/internalization/artificer-output.js +22 -1
  162. package/core/dist/runtime-v2/internalization/artificer-output.js.map +1 -1
  163. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.d.ts.map +1 -1
  164. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js +2 -0
  165. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js.map +1 -1
  166. package/core/dist/runtime-v2/internalization/artificer-runner.d.ts.map +1 -1
  167. package/core/dist/runtime-v2/internalization/artificer-runner.js +13 -0
  168. package/core/dist/runtime-v2/internalization/artificer-runner.js.map +1 -1
  169. package/core/dist/runtime-v2/internalization/diag-distiller-runner.js +4 -4
  170. package/core/dist/runtime-v2/internalization/diag-distiller-runner.js.map +1 -1
  171. package/core/dist/runtime-v2/internalization/diag-rootcause-runner.js +5 -5
  172. package/core/dist/runtime-v2/internalization/diag-rootcause-runner.js.map +1 -1
  173. package/core/dist/runtime-v2/internalization/diag-router-runner.js +4 -4
  174. package/core/dist/runtime-v2/internalization/diag-router-runner.js.map +1 -1
  175. package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
  176. package/core/dist/runtime-v2/internalization/evaluator-runner.js +7 -1
  177. package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
  178. package/core/dist/runtime-v2/internalization/index.d.ts +1 -1
  179. package/core/dist/runtime-v2/internalization/index.d.ts.map +1 -1
  180. package/core/dist/runtime-v2/internalization/index.js +1 -1
  181. package/core/dist/runtime-v2/internalization/index.js.map +1 -1
  182. package/core/dist/runtime-v2/internalization/internalization-task-guards.d.ts +0 -19
  183. package/core/dist/runtime-v2/internalization/internalization-task-guards.d.ts.map +1 -1
  184. package/core/dist/runtime-v2/internalization/internalization-task-guards.js +1 -29
  185. package/core/dist/runtime-v2/internalization/internalization-task-guards.js.map +1 -1
  186. package/core/dist/runtime-v2/pain-signal-runtime-factory.d.ts +2 -0
  187. package/core/dist/runtime-v2/pain-signal-runtime-factory.d.ts.map +1 -1
  188. package/core/dist/runtime-v2/pain-signal-runtime-factory.js +2 -0
  189. package/core/dist/runtime-v2/pain-signal-runtime-factory.js.map +1 -1
  190. package/core/dist/runtime-v2/signal-collector/__tests__/keyword-stage.test.d.ts +2 -0
  191. package/core/dist/runtime-v2/signal-collector/__tests__/keyword-stage.test.d.ts.map +1 -0
  192. package/core/dist/runtime-v2/signal-collector/__tests__/keyword-stage.test.js +39 -0
  193. package/core/dist/runtime-v2/signal-collector/__tests__/keyword-stage.test.js.map +1 -0
  194. package/core/dist/runtime-v2/signal-collector/__tests__/llm-stage.test.d.ts +2 -0
  195. package/core/dist/runtime-v2/signal-collector/__tests__/llm-stage.test.d.ts.map +1 -0
  196. package/core/dist/runtime-v2/signal-collector/__tests__/llm-stage.test.js +34 -0
  197. package/core/dist/runtime-v2/signal-collector/__tests__/llm-stage.test.js.map +1 -0
  198. package/core/dist/runtime-v2/signal-collector/__tests__/signal-collector.test.d.ts +2 -0
  199. package/core/dist/runtime-v2/signal-collector/__tests__/signal-collector.test.d.ts.map +1 -0
  200. package/core/dist/runtime-v2/signal-collector/__tests__/signal-collector.test.js +222 -0
  201. package/core/dist/runtime-v2/signal-collector/__tests__/signal-collector.test.js.map +1 -0
  202. package/core/dist/runtime-v2/signal-collector/__tests__/types.test.d.ts +2 -0
  203. package/core/dist/runtime-v2/signal-collector/__tests__/types.test.d.ts.map +1 -0
  204. package/core/dist/runtime-v2/signal-collector/__tests__/types.test.js +35 -0
  205. package/core/dist/runtime-v2/signal-collector/__tests__/types.test.js.map +1 -0
  206. package/core/dist/runtime-v2/signal-collector/index.d.ts +5 -0
  207. package/core/dist/runtime-v2/signal-collector/index.d.ts.map +1 -0
  208. package/core/dist/runtime-v2/signal-collector/index.js +5 -0
  209. package/core/dist/runtime-v2/signal-collector/index.js.map +1 -0
  210. package/core/dist/runtime-v2/signal-collector/keyword-stage.d.ts +20 -0
  211. package/core/dist/runtime-v2/signal-collector/keyword-stage.d.ts.map +1 -0
  212. package/core/dist/runtime-v2/signal-collector/keyword-stage.js +61 -0
  213. package/core/dist/runtime-v2/signal-collector/keyword-stage.js.map +1 -0
  214. package/core/dist/runtime-v2/signal-collector/llm-stage.d.ts +16 -0
  215. package/core/dist/runtime-v2/signal-collector/llm-stage.d.ts.map +1 -0
  216. package/core/dist/runtime-v2/signal-collector/llm-stage.js +34 -0
  217. package/core/dist/runtime-v2/signal-collector/llm-stage.js.map +1 -0
  218. package/core/dist/runtime-v2/signal-collector/signal-collector.d.ts +16 -0
  219. package/core/dist/runtime-v2/signal-collector/signal-collector.d.ts.map +1 -0
  220. package/core/dist/runtime-v2/signal-collector/signal-collector.js +71 -0
  221. package/core/dist/runtime-v2/signal-collector/signal-collector.js.map +1 -0
  222. package/core/dist/runtime-v2/signal-collector/types.d.ts +61 -0
  223. package/core/dist/runtime-v2/signal-collector/types.d.ts.map +1 -0
  224. package/core/dist/runtime-v2/signal-collector/types.js +45 -0
  225. package/core/dist/runtime-v2/signal-collector/types.js.map +1 -0
  226. package/core/dist/runtime-v2/store/schema-conformance.test.js +43 -0
  227. package/core/dist/runtime-v2/store/schema-conformance.test.js.map +1 -1
  228. package/core/dist/runtime-v2/types/event-types.d.ts +40 -3
  229. package/core/dist/runtime-v2/types/event-types.d.ts.map +1 -1
  230. package/core/dist/runtime-v2/types/event-types.js +9 -0
  231. package/core/dist/runtime-v2/types/event-types.js.map +1 -1
  232. package/core/dist/runtime-v2/types/index.d.ts +2 -2
  233. package/core/dist/runtime-v2/types/index.d.ts.map +1 -1
  234. package/core/dist/runtime-v2/types/index.js +1 -1
  235. package/core/dist/runtime-v2/types/index.js.map +1 -1
  236. package/core/dist/workflow-funnel-loader.js +1 -1
  237. package/core/dist/workflow-funnel-loader.js.map +1 -1
  238. package/dist/i18n.d.ts.map +1 -1
  239. package/dist/i18n.js +6 -0
  240. package/dist/i18n.js.map +1 -1
  241. package/dist/index.js +12 -3
  242. package/dist/index.js.map +1 -1
  243. package/dist/installer.d.ts +3 -0
  244. package/dist/installer.d.ts.map +1 -1
  245. package/dist/installer.js +150 -1
  246. package/dist/installer.js.map +1 -1
  247. package/dist/mvp-config.d.ts +8 -0
  248. package/dist/mvp-config.d.ts.map +1 -1
  249. package/dist/mvp-config.js +34 -12
  250. package/dist/mvp-config.js.map +1 -1
  251. package/package.json +1 -1
  252. package/pd-cli/dist/commands/__tests__/runtime-probe-config.test.js +2 -2
  253. package/pd-cli/dist/commands/__tests__/runtime-probe-config.test.js.map +1 -1
  254. package/pd-cli/dist/commands/demo-story-a.d.ts.map +1 -1
  255. package/pd-cli/dist/commands/demo-story-a.js +4 -2
  256. package/pd-cli/dist/commands/demo-story-a.js.map +1 -1
  257. package/pd-cli/dist/commands/proven-channel-baseline.d.ts.map +1 -1
  258. package/pd-cli/dist/commands/proven-channel-baseline.js +4 -2
  259. package/pd-cli/dist/commands/proven-channel-baseline.js.map +1 -1
  260. package/pd-cli/dist/commands/runtime-activation.d.ts +4 -0
  261. package/pd-cli/dist/commands/runtime-activation.d.ts.map +1 -1
  262. package/pd-cli/dist/commands/runtime-activation.js +187 -10
  263. package/pd-cli/dist/commands/runtime-activation.js.map +1 -1
  264. package/pd-cli/dist/commands/runtime-canary.d.ts.map +1 -1
  265. package/pd-cli/dist/commands/runtime-canary.js +4 -2
  266. package/pd-cli/dist/commands/runtime-canary.js.map +1 -1
  267. package/pd-cli/dist/commands/runtime-health-snapshot.d.ts.map +1 -1
  268. package/pd-cli/dist/commands/runtime-health-snapshot.js +4 -2
  269. package/pd-cli/dist/commands/runtime-health-snapshot.js.map +1 -1
  270. package/pd-cli/dist/commands/runtime-internalization-integrity.d.ts.map +1 -1
  271. package/pd-cli/dist/commands/runtime-internalization-integrity.js +4 -2
  272. package/pd-cli/dist/commands/runtime-internalization-integrity.js.map +1 -1
  273. package/pd-cli/dist/index.js +5 -63
  274. package/pd-cli/dist/index.js.map +1 -1
  275. package/pd-cli/dist/services/config-doctor.d.ts.map +1 -1
  276. package/pd-cli/dist/services/config-doctor.js +9 -2
  277. package/pd-cli/dist/services/config-doctor.js.map +1 -1
  278. package/pd-cli/dist/services/runtime-adapter-resolver.d.ts.map +1 -1
  279. package/pd-cli/dist/services/runtime-adapter-resolver.js +1 -0
  280. package/pd-cli/dist/services/runtime-adapter-resolver.js.map +1 -1
  281. package/plugin/dist/commands/context.js +17 -11
  282. package/plugin/dist/commands/samples.js +25 -0
  283. package/plugin/dist/core/event-log.d.ts +11 -1
  284. package/plugin/dist/core/event-log.js +13 -0
  285. package/plugin/dist/core/init.js +3 -4
  286. package/plugin/dist/core/rule-host.d.ts +50 -0
  287. package/plugin/dist/core/rule-host.js +116 -19
  288. package/plugin/dist/core/signal-collector-host.d.ts +105 -0
  289. package/plugin/dist/core/signal-collector-host.js +337 -0
  290. package/plugin/dist/hooks/gate.js +1 -1
  291. package/plugin/dist/hooks/prompt.d.ts +3 -2
  292. package/plugin/dist/hooks/prompt.js +48 -586
  293. package/plugin/dist/service/evolution-dedup.d.ts +1 -1
  294. package/plugin/dist/service/evolution-worker.d.ts +7 -31
  295. package/plugin/dist/service/evolution-worker.js +17 -36
  296. package/plugin/dist/service/queue-io.js +14 -2
  297. package/plugin/dist/service/queue-migration.d.ts +27 -26
  298. package/plugin/dist/service/queue-migration.js +57 -0
  299. package/plugin/dist/types/event-types.d.ts +1 -1
  300. package/plugin/dist/types.d.ts +5 -25
  301. package/plugin/dist/types.js +4 -9
  302. package/plugin/dist/utils/hashing.js +1 -1
  303. package/plugin/templates/langs/en/core/AGENTS.md +2 -2
  304. package/plugin/templates/langs/en/core/TOOLS.md +1 -1
  305. package/plugin/templates/langs/zh/core/AGENTS.md +2 -2
  306. package/plugin/templates/langs/zh/core/TOOLS.md +1 -1
  307. package/templates/langs/en/core/AGENTS.md +2 -2
  308. package/templates/langs/en/core/TOOLS.md +1 -1
  309. package/templates/langs/zh/core/AGENTS.md +2 -2
  310. package/templates/langs/zh/core/TOOLS.md +1 -1
  311. package/plugin/dist/constants/diagnostician.d.ts +0 -12
  312. package/plugin/dist/constants/diagnostician.js +0 -65
  313. package/plugin/dist/service/evolution-queue-migration.d.ts +0 -128
  314. package/plugin/dist/service/evolution-queue-migration.js +0 -53
  315. package/plugin/templates/langs/en/skills/pd-diagnostician/SKILL.md +0 -466
  316. package/plugin/templates/langs/zh/skills/pd-diagnostician/SKILL.md +0 -391
  317. package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +0 -287
@@ -1,6 +1,14 @@
1
- import { SqliteConnection, SqliteActivationStateStore, SqlitePIArtifactStore, extractPrincipleId, } from '@principles/core/runtime-v2';
1
+ import { SqliteConnection, SqliteActivationStateStore, SqlitePIArtifactStore, extractPrincipleId, extractEvidenceRefs, } from '@principles/core/runtime-v2';
2
+ import { loadPdConfig, computeFlagsFromLoadResult } from '../config/pd-config-store.js';
2
3
  import * as fs from 'node:fs';
3
4
  import * as path from 'node:path';
5
+ /**
6
+ * Type guard for parsed JSON objects (rc-2-no-as-bypass).
7
+ * Replaces `as Record<string, unknown>` casts on untrusted contentJson.
8
+ */
9
+ function isRecord(value) {
10
+ return value !== null && value !== undefined && typeof value === 'object' && !Array.isArray(value);
11
+ }
4
12
  function isMissingTableError(err) {
5
13
  if (!(err instanceof Error))
6
14
  return false;
@@ -34,7 +42,7 @@ export class ActivationsConsoleModel {
34
42
  async getActivations() {
35
43
  const stateDbPath = path.join(this.workspaceDir, '.pd', 'state.db');
36
44
  if (!fs.existsSync(stateDbPath)) {
37
- return { activations: [], generatedAt: new Date().toISOString(), note: 'state.db not found — workspace may not be initialized' };
45
+ return { activations: [], status: 'degraded', reason: 'state.db not found — workspace may not be initialized', nextAction: 'Run pd runtime diagnostics to check workspace state' };
38
46
  }
39
47
  const conn = new SqliteConnection({ workspaceDir: this.workspaceDir, readonly: true });
40
48
  try {
@@ -46,7 +54,7 @@ export class ActivationsConsoleModel {
46
54
  }
47
55
  catch (err) {
48
56
  if (isMissingTableError(err)) {
49
- return { activations: [], generatedAt: new Date().toISOString(), note: 'activation table not found — workspace may not be initialized' };
57
+ return { activations: [], status: 'degraded', reason: 'activation table not found — workspace may not be initialized', nextAction: 'Run pd runtime internalization integrity to check schema' };
50
58
  }
51
59
  throw err;
52
60
  }
@@ -56,13 +64,45 @@ export class ActivationsConsoleModel {
56
64
  // Without this, dreamer artifacts whose sourcePrincipleId was stripped
57
65
  // (non-core-principle case) would show 'unlinked' even when contentJson
58
66
  // carries a resolvable principleId.
67
+ //
68
+ // PRI-491: also collect contextVersion + evidenceRefs from contentJson so
69
+ // the Console can show whether a rule will block and why. rc-1/rc-2:
70
+ // contentJson is parsed as unknown and type-narrowed with typeof; never
71
+ // `as`-cast without a prior typeof check.
59
72
  const artifactPrincipleMap = new Map();
73
+ const artifactMetadata = new Map();
74
+ const danglingArtifactIds = new Set();
60
75
  for (const activation of allActivations) {
61
76
  if (!artifactPrincipleMap.has(activation.artifactId)) {
62
77
  try {
63
78
  const artifact = await artifactStore.getArtifactById(activation.artifactId);
64
- const principleId = artifact ? extractPrincipleId(toSnapshot(artifact)) : null;
65
- artifactPrincipleMap.set(activation.artifactId, principleId);
79
+ if (!artifact) {
80
+ danglingArtifactIds.add(activation.artifactId);
81
+ artifactPrincipleMap.set(activation.artifactId, null);
82
+ }
83
+ else {
84
+ const principleId = extractPrincipleId(toSnapshot(artifact));
85
+ artifactPrincipleMap.set(activation.artifactId, principleId);
86
+ // PRI-491: extract contextVersion + evidenceRefs from contentJson.
87
+ // rc-1: treat parsed JSON as unknown; rc-2: narrow with typeof.
88
+ let parsedContent = null;
89
+ try {
90
+ const parsed = JSON.parse(artifact.contentJson);
91
+ if (isRecord(parsed)) {
92
+ parsedContent = parsed;
93
+ }
94
+ }
95
+ catch {
96
+ // Malformed contentJson — treat as no metadata. Not dangling,
97
+ // just unreadable; the principleId may still resolve via column.
98
+ }
99
+ const requiresCtxV2 = parsedContent !== null
100
+ && Object.hasOwn(parsedContent, 'requiresContextVersion')
101
+ && parsedContent.requiresContextVersion === 2;
102
+ const contextVersion = requiresCtxV2 ? 'v2' : 'v1';
103
+ const evidenceRefs = parsedContent !== null ? extractEvidenceRefs(parsedContent) : null;
104
+ artifactMetadata.set(activation.artifactId, { contextVersion, evidenceRefs });
105
+ }
66
106
  }
67
107
  catch (err) {
68
108
  if (isMissingTableError(err)) {
@@ -74,19 +114,81 @@ export class ActivationsConsoleModel {
74
114
  }
75
115
  }
76
116
  }
77
- const facts = allActivations.map((record) => ({
78
- id: record.activationId,
79
- artifactId: record.artifactId,
80
- principleId: artifactPrincipleMap.get(record.artifactId) ?? 'unlinked',
81
- channel: record.channel,
82
- action: record.action,
83
- targetRef: record.targetRef,
84
- activatedAt: record.activatedAt,
85
- status: record.deactivatedAt === null ? 'active' : 'inactive',
86
- }));
117
+ // PRI-491: Probe rulecode_context_v2 flag to determine suspended_by_flag
118
+ // status for v2 activations. When the flag is off, v2 activations are
119
+ // suspended (not executing) even though they remain active in the DB.
120
+ const featureFlags = computeFlagsFromLoadResult(loadPdConfig(this.workspaceDir));
121
+ const v2FlagEnabled = featureFlags.flags.rulecode_context_v2?.enabled === true;
122
+ const facts = allActivations.map((record) => {
123
+ const meta = artifactMetadata.get(record.artifactId);
124
+ const contextVersion = meta?.contextVersion;
125
+ const evidenceRefs = meta?.evidenceRefs ?? undefined;
126
+ const evidenceSummary = evidenceRefs && evidenceRefs.length > 0
127
+ ? `${evidenceRefs.length} evidence ref(s): ${evidenceRefs.slice(0, 3).join(', ')}${evidenceRefs.length > 3 ? '...' : ''}`
128
+ : undefined;
129
+ // Derive mode from action (shadow_activate -> shadow, live_activate -> live).
130
+ const mode = record.action === 'code_tool_hook_shadow_activate'
131
+ ? 'shadow'
132
+ : record.action === 'code_tool_hook_live_activate'
133
+ ? 'live'
134
+ : undefined;
135
+ // Derive status: deactivated > suspended_by_flag > active (matches CLI).
136
+ let status;
137
+ let nextAction;
138
+ if (record.deactivatedAt) {
139
+ status = 'deactivated';
140
+ nextAction = undefined;
141
+ }
142
+ else if (contextVersion === 'v2' && !v2FlagEnabled) {
143
+ status = 'suspended_by_flag';
144
+ nextAction = `Enable rulecode_context_v2 flag or deactivate: pd activation deactivate --activation-id ${record.activationId} --confirm`;
145
+ }
146
+ else {
147
+ status = 'active';
148
+ if (mode === 'shadow') {
149
+ nextAction = `pd activation promote --activation-id ${record.activationId} --confirm`;
150
+ }
151
+ else if (mode === 'live') {
152
+ nextAction = `pd activation deactivate --activation-id ${record.activationId} --confirm`;
153
+ }
154
+ else {
155
+ nextAction = undefined;
156
+ }
157
+ }
158
+ const enriched = {
159
+ activationId: record.activationId,
160
+ artifactId: record.artifactId,
161
+ principleId: artifactPrincipleMap.get(record.artifactId) ?? 'unlinked',
162
+ channel: record.channel,
163
+ action: record.action,
164
+ targetRef: record.targetRef,
165
+ activatedAt: record.activatedAt,
166
+ promotedAt: record.promotedAt ?? null,
167
+ deactivatedAt: record.deactivatedAt,
168
+ mode,
169
+ status,
170
+ contextVersion,
171
+ evidenceRefs,
172
+ evidenceSummary,
173
+ nextAction,
174
+ };
175
+ if (danglingArtifactIds.has(record.artifactId)) {
176
+ enriched.warning = `artifact_id "${record.artifactId}" does not exist in pi_artifacts - activation is orphaned`;
177
+ }
178
+ return enriched;
179
+ });
180
+ // rc-9: surface dangling references instead of silently returning a degraded list.
181
+ if (danglingArtifactIds.size > 0) {
182
+ return {
183
+ activations: facts,
184
+ status: 'degraded',
185
+ reason: `${danglingArtifactIds.size} activation(s) reference non-existent artifact_id(s): ${Array.from(danglingArtifactIds).join(', ')}`,
186
+ nextAction: 'Run pd runtime internalization integrity to check for orphaned activations',
187
+ };
188
+ }
87
189
  return {
88
190
  activations: facts,
89
- generatedAt: new Date().toISOString(),
191
+ status: 'ok',
90
192
  };
91
193
  }
92
194
  finally {
@@ -20,8 +20,8 @@
20
20
  * - ERR-014/ERR-016/ERR-017: Safe serialization for previews
21
21
  * - ERR-045: ANY-segment redaction for sensitive keys
22
22
  */
23
- import { sendSuccess, sendError, sendNotFound, sendMethodNotAllowed, sendBadRequest } from '../utils/response.js';
24
- import { getConfigSummary, getConfigCatalog, updateAgentBinding, updateDefaultRuntime, checkReadiness, getPrinciplesOutputLanguage, updatePrinciplesOutputLanguage, updateFeatureFlag, } from '../config/pd-config-store.js';
23
+ import { sendSuccess, sendError, sendNotFound, sendMethodNotAllowed, sendBadRequest, sendJson } from '../utils/response.js';
24
+ import { getConfigSummary, getConfigCatalog, updateAgentBinding, updateDefaultRuntime, checkReadiness, getPrinciplesOutputLanguage, updatePrinciplesOutputLanguage, updateFeatureFlag, createRuntimeProfile, updateRuntimeProfile, deleteRuntimeProfile, } from '../config/pd-config-store.js';
25
25
  // ── Constants ────────────────────────────────────────────────────────────────
26
26
  const MAX_BODY_SIZE = 1024 * 64; // 64 KB — generous for config binding updates
27
27
  // ── Request Body Reader ──────────────────────────────────────────────────────
@@ -262,6 +262,96 @@ export async function handleConfigRoute(req, res, ctx) {
262
262
  sendMethodNotAllowed(res);
263
263
  return;
264
264
  }
265
+ // POST /profiles — create a new runtime profile
266
+ if (subPath === '/profiles') {
267
+ if (method !== 'POST') {
268
+ sendMethodNotAllowed(res);
269
+ return;
270
+ }
271
+ let bodyText;
272
+ try {
273
+ bodyText = await readBody(req);
274
+ }
275
+ catch {
276
+ sendBadRequest(res, 'Request body exceeds maximum allowed size');
277
+ return;
278
+ }
279
+ const body = safeParseBody(bodyText);
280
+ if (body === null) {
281
+ sendBadRequest(res, 'Invalid JSON body');
282
+ return;
283
+ }
284
+ // Validate body shape: { id: string, profile: object } (ERR-001: no `as` bypass)
285
+ if (!isObject(body)) {
286
+ sendBadRequest(res, 'Body must be a JSON object with id and profile fields');
287
+ return;
288
+ }
289
+ if (!Object.hasOwn(body, 'id')) {
290
+ sendBadRequest(res, 'Missing required field: id');
291
+ return;
292
+ }
293
+ if (typeof body.id !== 'string' || body.id.length === 0) {
294
+ sendBadRequest(res, 'id must be a non-empty string');
295
+ return;
296
+ }
297
+ if (!Object.hasOwn(body, 'profile')) {
298
+ sendBadRequest(res, 'Missing required field: profile');
299
+ return;
300
+ }
301
+ const result = createRuntimeProfile(workspaceDir, body.id, body.profile);
302
+ if (!result.ok) {
303
+ sendError(res, result.statusCode, result.error, result.message);
304
+ return;
305
+ }
306
+ // 201 Created for new resource
307
+ sendJson(res, 201, { success: true, data: { profileId: result.profileId, profile: result.profile } });
308
+ return;
309
+ }
310
+ // PATCH/DELETE /profiles/:profileId — update or delete a runtime profile
311
+ const profileMatch = /^\/profiles\/([^/]+)$/.exec(subPath);
312
+ if (profileMatch) {
313
+ let profileId;
314
+ try {
315
+ profileId = decodeURIComponent(profileMatch[1] ?? '');
316
+ }
317
+ catch {
318
+ sendBadRequest(res, 'Invalid profile ID encoding');
319
+ return;
320
+ }
321
+ if (method === 'PATCH') {
322
+ let bodyText;
323
+ try {
324
+ bodyText = await readBody(req);
325
+ }
326
+ catch {
327
+ sendBadRequest(res, 'Request body exceeds maximum allowed size');
328
+ return;
329
+ }
330
+ const body = safeParseBody(bodyText);
331
+ if (body === null) {
332
+ sendBadRequest(res, 'Invalid JSON body');
333
+ return;
334
+ }
335
+ const result = updateRuntimeProfile(workspaceDir, profileId, body);
336
+ if (!result.ok) {
337
+ sendError(res, result.statusCode, result.error, result.message);
338
+ return;
339
+ }
340
+ sendSuccess(res, { profileId: result.profileId, profile: result.profile });
341
+ return;
342
+ }
343
+ if (method === 'DELETE') {
344
+ const result = deleteRuntimeProfile(workspaceDir, profileId);
345
+ if (!result.ok) {
346
+ sendError(res, result.statusCode, result.error, result.message);
347
+ return;
348
+ }
349
+ sendSuccess(res, { profileId: result.profileId, profile: result.profile });
350
+ return;
351
+ }
352
+ sendMethodNotAllowed(res);
353
+ return;
354
+ }
265
355
  // 404 fallback
266
356
  sendNotFound(res, `Route /api/v1/config${subPath} not found`);
267
357
  }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Onboarding route - POST /api/v1/onboarding/run-demo
3
+ *
4
+ * Spec: docs/superpowers/specs/2026-06-30-new-user-onboarding-design.md 6.3 change 5
5
+ *
6
+ * Spawns `pd demo story-a --workspace <path> --json` as a subprocess, waits for
7
+ * it to complete, validates the JSON stdout, and returns 200 with the demo
8
+ * result. The console backend NEVER writes SQLite directly - all DB I/O happens
9
+ * inside the pd-cli subprocess (EP-06 Source of Truth).
10
+ *
11
+ * Feature flag gate: `new_user_onboarding` (default true, registered in
12
+ * packages/principles-core/src/runtime-v2/feature-flags/feature-flag-contract.ts).
13
+ *
14
+ * ERR entries considered:
15
+ * - EP-02 (Production Path Wiring): registered in server/index.ts handleRequest
16
+ * - EP-03 (Fail Loud): spawn failure -> 500 with reason + nextAction;
17
+ * flag disabled -> 403 with reason + nextAction (rc-9-no-silent-fallback)
18
+ * - EP-06 (Source of Truth): reuses pd-cli demo command, no direct DB writes
19
+ * - EP-08 (Security Boundary): no shell:true — argv passed directly to OS,
20
+ * eliminating command-injection risk; demo runs in a temp workspace so no
21
+ * user-controlled path enters the command at all
22
+ * - rc-1-treat-as-unknown / rc-2-no-as-bypass / rc-4-validate-array-elements:
23
+ * parsed stdout is validated by parseDemoStdout before use
24
+ * - rc-9-no-silent-fallback: timeout, error, and invalid-stdout paths each
25
+ * return a structured error with reason + nextAction
26
+ */
27
+ import type { IncomingMessage, ServerResponse } from 'node:http';
28
+ export interface OnboardingRouteContext {
29
+ workspaceDir: string;
30
+ subPath: string;
31
+ }
32
+ export declare function handleOnboardingRoute(req: IncomingMessage, res: ServerResponse, ctx: OnboardingRouteContext): Promise<void>;
33
+ /** No persistent resources to dispose - included for parity with other routes. */
34
+ export declare function disposeOnboardingModels(): void;
@@ -0,0 +1,324 @@
1
+ /**
2
+ * Onboarding route - POST /api/v1/onboarding/run-demo
3
+ *
4
+ * Spec: docs/superpowers/specs/2026-06-30-new-user-onboarding-design.md 6.3 change 5
5
+ *
6
+ * Spawns `pd demo story-a --workspace <path> --json` as a subprocess, waits for
7
+ * it to complete, validates the JSON stdout, and returns 200 with the demo
8
+ * result. The console backend NEVER writes SQLite directly - all DB I/O happens
9
+ * inside the pd-cli subprocess (EP-06 Source of Truth).
10
+ *
11
+ * Feature flag gate: `new_user_onboarding` (default true, registered in
12
+ * packages/principles-core/src/runtime-v2/feature-flags/feature-flag-contract.ts).
13
+ *
14
+ * ERR entries considered:
15
+ * - EP-02 (Production Path Wiring): registered in server/index.ts handleRequest
16
+ * - EP-03 (Fail Loud): spawn failure -> 500 with reason + nextAction;
17
+ * flag disabled -> 403 with reason + nextAction (rc-9-no-silent-fallback)
18
+ * - EP-06 (Source of Truth): reuses pd-cli demo command, no direct DB writes
19
+ * - EP-08 (Security Boundary): no shell:true — argv passed directly to OS,
20
+ * eliminating command-injection risk; demo runs in a temp workspace so no
21
+ * user-controlled path enters the command at all
22
+ * - rc-1-treat-as-unknown / rc-2-no-as-bypass / rc-4-validate-array-elements:
23
+ * parsed stdout is validated by parseDemoStdout before use
24
+ * - rc-9-no-silent-fallback: timeout, error, and invalid-stdout paths each
25
+ * return a structured error with reason + nextAction
26
+ */
27
+ import { spawn } from 'node:child_process';
28
+ import { fileURLToPath } from 'node:url';
29
+ import * as fs from 'fs';
30
+ import * as os from 'os';
31
+ import * as path from 'path';
32
+ import { loadPdConfig, computeFlagsFromLoadResult } from '../config/pd-config-store.js';
33
+ import { sendError, sendJson } from '../utils/response.js';
34
+ /** Demo subprocess timeout in milliseconds (60s). */
35
+ const DEMO_TIMEOUT_MS = 60_000;
36
+ // P1-1: Best-effort removal of the temp demo workspace.
37
+ function cleanupTempWorkspace(dir) {
38
+ try {
39
+ fs.rmSync(dir, { recursive: true, force: true });
40
+ }
41
+ catch {
42
+ // Best-effort cleanup — temp dir will be cleaned by OS eventually.
43
+ }
44
+ }
45
+ /** Send a structured error with reason + nextAction (Runtime Contract rc-9). */
46
+ function sendStructuredError(res, payload) {
47
+ sendError(res, payload.statusCode, payload.error, payload.reason, {
48
+ reason: payload.reason,
49
+ nextAction: payload.nextAction,
50
+ });
51
+ }
52
+ /** Load the new_user_onboarding flag state from .pd/config.yaml. */
53
+ function loadOnboardingFlagEnabled(workspaceDir) {
54
+ const configResult = loadPdConfig(workspaceDir);
55
+ const flagsResult = computeFlagsFromLoadResult(configResult);
56
+ return flagsResult.flags.new_user_onboarding?.enabled === true;
57
+ }
58
+ /**
59
+ * Resolve the pd CLI invocation: spawn `process.execPath` (Node itself) with
60
+ * the absolute path to the pd-cli `dist/index.js` entry, instead of spawning
61
+ * the bare `pd` binary.
62
+ *
63
+ * P1-A (Windows .cmd resolution): `spawn('pd', args, { shell: false })` fails
64
+ * on Windows with ENOENT because Node's CreateProcess cannot resolve npm's
65
+ * `.cmd` shims (pd.cmd) without a shell. Spawning `process.execPath` with the
66
+ * JS entry path sidesteps this entirely — there is no `.cmd` involved. This is
67
+ * the same pattern used in installer.ts:autoLaunchConsole and
68
+ * pd-cli's runtime-uat.ts `findPdCliPath`.
69
+ *
70
+ * P1-2 (command injection): this approach also keeps `shell: false` — argv is
71
+ * passed directly to the OS, so no user-controlled value is ever interpreted
72
+ * by a shell (EP-08 Security Boundary).
73
+ *
74
+ * Returns `{ cmd, extraArgs }`:
75
+ * - On success: `{ cmd: process.execPath, extraArgs: [pdCliEntryPath] }`
76
+ * - On failure (pd-cli not resolvable / entry missing): falls back to
77
+ * `{ cmd: 'pd', extraArgs: [] }`. This works on Linux/macOS via PATH; on
78
+ * Windows the spawn will emit 'error', which the caller handles with a
79
+ * clear nextAction (rc-9-no-silent-fallback).
80
+ */
81
+ function findPdCli() {
82
+ // Both the monorepo and installed extension place pd-cli beside console.
83
+ // source: pd-console/src/server/routes -> packages/pd-cli
84
+ // install: console/dist/server/routes -> principles-disciple/pd-cli
85
+ const entryPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../pd-cli/dist/index.js');
86
+ // Do not preflight with existsSync: the Console test job intentionally runs
87
+ // before pd-cli is built, while production installation guarantees this
88
+ // sibling layout. Node will exit non-zero with a structured error if the
89
+ // delivered entry is unexpectedly missing.
90
+ return { cmd: process.execPath, extraArgs: [entryPath] };
91
+ }
92
+ /**
93
+ * Validate the parsed JSON stdout from `pd demo story-a --json`.
94
+ *
95
+ * rc-1-treat-as-unknown: parsed JSON is treated as unknown until validated.
96
+ * rc-2-no-as-bypass: no `as` cast is used to bypass validation - all fields
97
+ * are checked with typeof / Array.isArray before the value is accepted.
98
+ * rc-4-validate-array-elements: the `stages` array is type-checked as an array
99
+ * (element-level shape validation happens downstream in the UI validator).
100
+ *
101
+ * Required fields: status (string), generatedAt (string), narrative (string),
102
+ * stages (array). Returns the validated object or null if invalid.
103
+ */
104
+ function parseDemoStdout(stdout) {
105
+ let parsed;
106
+ try {
107
+ parsed = JSON.parse(stdout);
108
+ }
109
+ catch {
110
+ return null;
111
+ }
112
+ if (typeof parsed !== 'object' || parsed === null)
113
+ return null;
114
+ const status = Object.hasOwn(parsed, 'status') ? Reflect.get(parsed, 'status') : undefined;
115
+ const generatedAt = Object.hasOwn(parsed, 'generatedAt') ? Reflect.get(parsed, 'generatedAt') : undefined;
116
+ const narrative = Object.hasOwn(parsed, 'narrative') ? Reflect.get(parsed, 'narrative') : undefined;
117
+ const stages = Object.hasOwn(parsed, 'stages') ? Reflect.get(parsed, 'stages') : undefined;
118
+ if (typeof status !== 'string' || typeof generatedAt !== 'string' || typeof narrative !== 'string')
119
+ return null;
120
+ if (!Array.isArray(stages) || !stages.every((stage) => {
121
+ if (typeof stage !== 'object' || stage === null)
122
+ return false;
123
+ const name = Object.hasOwn(stage, 'name') ? Reflect.get(stage, 'name') : undefined;
124
+ const stageStatus = Object.hasOwn(stage, 'status') ? Reflect.get(stage, 'status') : undefined;
125
+ return typeof name === 'string' &&
126
+ (stageStatus === 'passed' || stageStatus === 'failed' || stageStatus === 'degraded' || stageStatus === 'skipped');
127
+ }))
128
+ return null;
129
+ return parsed;
130
+ }
131
+ /**
132
+ * Spawn `pd demo story-a --json`, wait for it to complete, validate stdout,
133
+ * and return 200 with { success: true, data: { simulated: true, demo: ... } }.
134
+ *
135
+ * The demo typically completes in < 30s. We block on the subprocess so the
136
+ * response carries the validated demo result in a single round-trip - the
137
+ * frontend does not need to poll for the demo itself (it still polls
138
+ * /api/v1/evidence-chain in step 3 for live evidence detection).
139
+ *
140
+ * Failure modes (rc-9-no-silent-fallback - every path returns a structured
141
+ * error with reason + nextAction):
142
+ * - spawn throws synchronously (e.g. ENOENT) -> 500 demo_spawn_failed
143
+ * - child emits 'error' after spawn -> 500 demo_subprocess_error
144
+ * - subprocess exceeds DEMO_TIMEOUT_MS -> 504 demo_timeout (subprocess killed)
145
+ * - subprocess exits with non-zero code -> 500 demo_exit_nonzero
146
+ * - stdout cannot be parsed/validated -> 500 demo_invalid_stdout
147
+ */
148
+ async function handleRunDemo(res, _workspaceDir) {
149
+ // P1-1: Use a temporary workspace so the demo's simulated DB writes
150
+ // (tasks, artifacts, approvals, activations) do NOT pollute the user's
151
+ // real workspace state.db.
152
+ const tempWorkspace = fs.mkdtempSync(path.join(os.tmpdir(), 'pd-onboarding-demo-'));
153
+ let child;
154
+ try {
155
+ const { cmd, extraArgs } = findPdCli();
156
+ // P1-A: spawn process.execPath (Node) with the pd-cli entry JS path to
157
+ // avoid the Windows .cmd resolution problem. P1-2: no shell:true — argv is
158
+ // passed directly to the OS, eliminating command-injection risk from
159
+ // user-controlled paths (the temp workspace path is the only variable
160
+ // here, and it is a mkdtemp result, not user input).
161
+ child = spawn(cmd, [...extraArgs, 'demo', 'story-a', '--workspace', tempWorkspace, '--json'], {
162
+ stdio: ['pipe', 'pipe', 'pipe'],
163
+ // No shell:true — argv is passed directly to the OS.
164
+ });
165
+ }
166
+ catch (err) {
167
+ const message = err instanceof Error ? err.message : String(err);
168
+ cleanupTempWorkspace(tempWorkspace);
169
+ sendStructuredError(res, {
170
+ statusCode: 500,
171
+ error: 'demo_spawn_failed',
172
+ reason: `spawn failed: ${message}`,
173
+ nextAction: 'Check that the pd CLI is installed (npm i -g @principles/pd-cli) and that its dist/index.js entry exists, or that pd is on PATH.',
174
+ });
175
+ return;
176
+ }
177
+ // P2-D: declared outside the try so the finally block can clear it. Captures
178
+ // the 60s timeout handle to prevent the timer from leaking after demo ends.
179
+ let timeoutHandle;
180
+ try {
181
+ // Capture stdout into a buffer. stderr is logged for observability (rc-9).
182
+ const stdoutChunks = [];
183
+ let stderrText = '';
184
+ child.stdout?.on('data', (data) => {
185
+ stdoutChunks.push(data);
186
+ });
187
+ child.stderr?.on('data', (data) => {
188
+ const chunk = data.toString();
189
+ stderrText += chunk;
190
+ console.error('[pd-console] onboarding demo stderr:', chunk.trim());
191
+ });
192
+ // Wrap the subprocess lifecycle in a Promise that resolves with the exit code
193
+ // or rejects on 'error'. A timeout is enforced via a separate Promise that
194
+ // resolves with a sentinel value - Promise.race is used below.
195
+ const settled = new Promise((resolve) => {
196
+ let done = false;
197
+ const finish = (code, error) => {
198
+ if (done)
199
+ return;
200
+ done = true;
201
+ resolve({ code, error });
202
+ };
203
+ child.on('error', (err) => {
204
+ finish(null, err);
205
+ });
206
+ child.on('close', (code) => {
207
+ finish(code, null);
208
+ });
209
+ });
210
+ const timeout = new Promise((resolve) => {
211
+ timeoutHandle = setTimeout(() => resolve('timeout'), DEMO_TIMEOUT_MS);
212
+ });
213
+ const outcome = await Promise.race([settled, timeout]);
214
+ // -- Timeout: kill the subprocess and return 504 --
215
+ if (outcome === 'timeout') {
216
+ try {
217
+ child.kill();
218
+ }
219
+ catch {
220
+ // Best-effort kill; ignore failures (rc-9: the 504 response still carries
221
+ // the reason + nextAction so the operator is not left in the dark).
222
+ }
223
+ sendStructuredError(res, {
224
+ statusCode: 504,
225
+ error: 'demo_timeout',
226
+ reason: `demo subprocess exceeded ${DEMO_TIMEOUT_MS}ms timeout`,
227
+ nextAction: 'Retry the demo; if it persists, check pd CLI health (pd doctor).',
228
+ });
229
+ return;
230
+ }
231
+ // -- 'error' event after spawn (e.g. EACCES, broken pipe) -> 500 --
232
+ if (outcome.error !== null) {
233
+ sendStructuredError(res, {
234
+ statusCode: 500,
235
+ error: 'demo_subprocess_error',
236
+ reason: `subprocess error: ${outcome.error.message}`,
237
+ nextAction: 'Check pd CLI installation and permissions; see server logs for stderr.',
238
+ });
239
+ return;
240
+ }
241
+ // -- Non-zero exit code -> 500 --
242
+ if (outcome.code !== 0) {
243
+ sendStructuredError(res, {
244
+ statusCode: 500,
245
+ error: 'demo_exit_nonzero',
246
+ reason: `demo exited with code ${outcome.code}`,
247
+ nextAction: stderrText.trim()
248
+ ? `pd stderr: ${stderrText.trim()}`
249
+ : 'Run `pd demo story-a --json` manually for diagnostics.',
250
+ });
251
+ return;
252
+ }
253
+ // -- Validate stdout (rc-1/rc-2/rc-4) --
254
+ const stdout = Buffer.concat(stdoutChunks).toString('utf8');
255
+ const validated = parseDemoStdout(stdout);
256
+ if (validated === null) {
257
+ sendStructuredError(res, {
258
+ statusCode: 500,
259
+ error: 'demo_invalid_stdout',
260
+ reason: 'demo stdout did not match the expected JSON schema',
261
+ nextAction: 'Run `pd demo story-a --json` manually and inspect the output.',
262
+ });
263
+ return;
264
+ }
265
+ // -- 200 OK with validated demo result --
266
+ sendJson(res, 200, {
267
+ success: true,
268
+ data: {
269
+ simulated: true,
270
+ demo: validated,
271
+ },
272
+ });
273
+ }
274
+ finally {
275
+ // P2-D: clear the 60s timeout timer so it doesn't leak after the demo
276
+ // completes. The guard handles the case where an error was thrown before
277
+ // the timeout Promise executor assigned the handle.
278
+ if (timeoutHandle)
279
+ clearTimeout(timeoutHandle);
280
+ // P1-1: always clean up the temp workspace, whether the demo succeeded,
281
+ // failed, timed out, or threw. Best-effort — OS will reap temp dirs eventually.
282
+ cleanupTempWorkspace(tempWorkspace);
283
+ }
284
+ }
285
+ export async function handleOnboardingRoute(req, res, ctx) {
286
+ const { workspaceDir, subPath } = ctx;
287
+ // -- Feature flag gate (spec 6.3 change 5) --
288
+ // Checked BEFORE method/sub-path dispatch so a disabled flag cannot be probed
289
+ // via 405/404 side-channels (rc-9-no-silent-fallback: explicit 403 + reason).
290
+ if (!loadOnboardingFlagEnabled(workspaceDir)) {
291
+ sendStructuredError(res, {
292
+ statusCode: 403,
293
+ error: 'flag_disabled',
294
+ reason: 'flag_disabled',
295
+ nextAction: 'Enable the new_user_onboarding feature flag in .pd/config.yaml or the Settings page.',
296
+ });
297
+ return;
298
+ }
299
+ // -- POST /api/v1/onboarding/run-demo --
300
+ if (subPath === '/run-demo') {
301
+ if (req.method !== 'POST') {
302
+ sendStructuredError(res, {
303
+ statusCode: 405,
304
+ error: 'method_not_allowed',
305
+ reason: 'method_not_allowed',
306
+ nextAction: 'Use POST to trigger the demo.',
307
+ });
308
+ return;
309
+ }
310
+ await handleRunDemo(res, workspaceDir);
311
+ return;
312
+ }
313
+ // -- Unknown sub-path --
314
+ sendStructuredError(res, {
315
+ statusCode: 404,
316
+ error: 'not_found',
317
+ reason: 'not_found',
318
+ nextAction: `Route /api/v1/onboarding${subPath} not found. Use POST /api/v1/onboarding/run-demo.`,
319
+ });
320
+ }
321
+ /** No persistent resources to dispose - included for parity with other routes. */
322
+ export function disposeOnboardingModels() {
323
+ // No-op: handleRunDemo holds no cached models or open handles.
324
+ }