create-principles-disciple 1.144.26 → 1.145.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 (343) hide show
  1. package/_release/product-identity.json +2 -2
  2. package/codex-adapter/dist/codex-evidence-recorder.js +8 -2
  3. package/codex-adapter/dist/index.d.ts +2 -2
  4. package/codex-adapter/dist/index.js +1 -1
  5. package/codex-adapter/dist/ingestion/admission.js +16 -0
  6. package/codex-adapter/dist/ingestion/codex-version.d.ts +10 -4
  7. package/codex-adapter/dist/ingestion/codex-version.js +10 -4
  8. package/codex-adapter/dist/ingestion/explicit-owner-statement.d.ts +48 -0
  9. package/codex-adapter/dist/ingestion/explicit-owner-statement.js +123 -0
  10. package/codex-adapter/dist/ingestion/ingestion.js +50 -1
  11. package/codex-adapter/dist/pd-hook.js +66 -3
  12. package/codex-adapter/dist/worker/workspace-worker.d.ts +14 -0
  13. package/codex-adapter/dist/worker/workspace-worker.js +194 -11
  14. package/codex-adapter/package.json +2 -2
  15. package/console/dist/server/models/ApprovalsConsoleModel.d.ts +2 -2
  16. package/console/dist/server/models/ApprovalsConsoleModel.js +1 -1
  17. package/console/dist/server/models/ApprovalsGroupedConsoleModel.js +1 -1
  18. package/console/dist/server/models/PrinciplesConsoleModel.d.ts +26 -1
  19. package/console/dist/server/models/PrinciplesConsoleModel.js +18 -1
  20. package/console/dist/server/routes/activations.js +4 -4
  21. package/console/dist/server/routes/approvals.js +2 -2
  22. package/console/dist/server/routes/failed-tasks.d.ts +9 -0
  23. package/console/dist/server/routes/failed-tasks.js +116 -7
  24. package/console/dist/server/routes/principles.js +73 -1
  25. package/console/dist/shared/prompt-injection-contract.d.ts +1 -1
  26. package/console/dist/ui/api.d.ts +5 -5
  27. package/console/dist/ui/i18n/en.json +13 -2
  28. package/console/dist/ui/i18n/zh-CN.json +13 -2
  29. package/console/dist/ui/pages/activation/ActivationPage.js +2 -1
  30. package/console/dist/ui/pages/failed-tasks/FailedTasksPage.js +42 -4
  31. package/console/dist/ui/pages/focus/FocusPage.js +3 -3
  32. package/console/dist/ui/pages/principles/PrinciplesPage.js +7 -2
  33. package/console/dist/ui/utils/validators.d.ts +6 -2
  34. package/console/dist/ui/utils/validators.js +19 -4
  35. package/console/dist/web/assets/app.css +26 -0
  36. package/console/dist/web/assets/app.js +24 -24
  37. package/console/package.json +4 -4
  38. package/core/dist/host/host-installer.d.ts +3 -1
  39. package/core/dist/host/host-installer.d.ts.map +1 -1
  40. package/core/dist/host/host-installer.js.map +1 -1
  41. package/core/dist/runtime-v2/admission-gate.d.ts +8 -0
  42. package/core/dist/runtime-v2/admission-gate.d.ts.map +1 -1
  43. package/core/dist/runtime-v2/admission-gate.js +3 -2
  44. package/core/dist/runtime-v2/admission-gate.js.map +1 -1
  45. package/core/dist/runtime-v2/config/pd-config-defaults.d.ts.map +1 -1
  46. package/core/dist/runtime-v2/config/pd-config-defaults.js +16 -1
  47. package/core/dist/runtime-v2/config/pd-config-defaults.js.map +1 -1
  48. package/core/dist/runtime-v2/context-payload.d.ts +43 -3
  49. package/core/dist/runtime-v2/context-payload.d.ts.map +1 -1
  50. package/core/dist/runtime-v2/context-payload.js +15 -1
  51. package/core/dist/runtime-v2/context-payload.js.map +1 -1
  52. package/core/dist/runtime-v2/evidence-guards.d.ts +6 -0
  53. package/core/dist/runtime-v2/evidence-guards.d.ts.map +1 -1
  54. package/core/dist/runtime-v2/evidence-guards.js +11 -0
  55. package/core/dist/runtime-v2/evidence-guards.js.map +1 -1
  56. package/core/dist/runtime-v2/evolution/evolution-types.d.ts +4 -4
  57. package/core/dist/runtime-v2/evolution/evolution-types.d.ts.map +1 -1
  58. package/core/dist/runtime-v2/evolution/evolution-types.js +1 -1
  59. package/core/dist/runtime-v2/evolution/evolution-types.js.map +1 -1
  60. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts.map +1 -1
  61. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js +6 -0
  62. package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js.map +1 -1
  63. package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.d.ts.map +1 -1
  64. package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.js +8 -0
  65. package/core/dist/runtime-v2/feature-flags/feature-flag-lifecycle.js.map +1 -1
  66. package/core/dist/runtime-v2/index.d.ts +5 -2
  67. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  68. package/core/dist/runtime-v2/index.js +3 -1
  69. package/core/dist/runtime-v2/index.js.map +1 -1
  70. package/core/dist/runtime-v2/internalization/adversarial-feedback.d.ts +3 -1
  71. package/core/dist/runtime-v2/internalization/adversarial-feedback.d.ts.map +1 -1
  72. package/core/dist/runtime-v2/internalization/adversarial-feedback.js +10 -1
  73. package/core/dist/runtime-v2/internalization/adversarial-feedback.js.map +1 -1
  74. package/core/dist/runtime-v2/internalization/evaluator-output.d.ts +11 -0
  75. package/core/dist/runtime-v2/internalization/evaluator-output.d.ts.map +1 -1
  76. package/core/dist/runtime-v2/internalization/evaluator-output.js +10 -0
  77. package/core/dist/runtime-v2/internalization/evaluator-output.js.map +1 -1
  78. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts +3 -3
  79. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts.map +1 -1
  80. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js +49 -5
  81. package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js.map +1 -1
  82. package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
  83. package/core/dist/runtime-v2/internalization/evaluator-runner.js +13 -16
  84. package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
  85. package/core/dist/runtime-v2/internalization/owner-decision-review.d.ts.map +1 -1
  86. package/core/dist/runtime-v2/internalization/owner-decision-review.js +5 -1
  87. package/core/dist/runtime-v2/internalization/owner-decision-review.js.map +1 -1
  88. package/core/dist/runtime-v2/internalization/owner-review.d.ts +2 -2
  89. package/core/dist/runtime-v2/internalization/owner-review.js +2 -2
  90. package/core/dist/runtime-v2/internalization/scribe-output.d.ts +12 -0
  91. package/core/dist/runtime-v2/internalization/scribe-output.d.ts.map +1 -1
  92. package/core/dist/runtime-v2/internalization/scribe-output.js +20 -0
  93. package/core/dist/runtime-v2/internalization/scribe-output.js.map +1 -1
  94. package/core/dist/runtime-v2/internalization/scribe-prompt-builder.d.ts +32 -1
  95. package/core/dist/runtime-v2/internalization/scribe-prompt-builder.d.ts.map +1 -1
  96. package/core/dist/runtime-v2/internalization/scribe-prompt-builder.js +38 -1
  97. package/core/dist/runtime-v2/internalization/scribe-prompt-builder.js.map +1 -1
  98. package/core/dist/runtime-v2/internalization-queue-read-model.d.ts +6 -0
  99. package/core/dist/runtime-v2/internalization-queue-read-model.d.ts.map +1 -1
  100. package/core/dist/runtime-v2/internalization-queue-read-model.js +8 -1
  101. package/core/dist/runtime-v2/internalization-queue-read-model.js.map +1 -1
  102. package/core/dist/runtime-v2/intervention-evidence-normalizer.d.ts.map +1 -1
  103. package/core/dist/runtime-v2/intervention-evidence-normalizer.js +10 -3
  104. package/core/dist/runtime-v2/intervention-evidence-normalizer.js.map +1 -1
  105. package/core/dist/runtime-v2/pain-ingress.d.ts +1 -1
  106. package/core/dist/runtime-v2/pain-ingress.d.ts.map +1 -1
  107. package/core/dist/runtime-v2/pain-signal-bridge.d.ts +68 -2
  108. package/core/dist/runtime-v2/pain-signal-bridge.d.ts.map +1 -1
  109. package/core/dist/runtime-v2/pain-signal-bridge.js +541 -18
  110. package/core/dist/runtime-v2/pain-signal-bridge.js.map +1 -1
  111. package/core/dist/runtime-v2/pain-signal-runtime-factory.d.ts.map +1 -1
  112. package/core/dist/runtime-v2/pain-signal-runtime-factory.js +4 -0
  113. package/core/dist/runtime-v2/pain-signal-runtime-factory.js.map +1 -1
  114. package/core/dist/runtime-v2/product-telemetry/snapshot-contract.d.ts +2 -2
  115. package/core/dist/runtime-v2/product-telemetry/snapshot-contract.d.ts.map +1 -1
  116. package/core/dist/runtime-v2/product-telemetry/snapshot-contract.js +2 -2
  117. package/core/dist/runtime-v2/product-telemetry/snapshot-contract.js.map +1 -1
  118. package/core/dist/runtime-v2/store/candidate/admission-document.d.ts +90 -0
  119. package/core/dist/runtime-v2/store/candidate/admission-document.d.ts.map +1 -0
  120. package/core/dist/runtime-v2/store/candidate/admission-document.js +142 -0
  121. package/core/dist/runtime-v2/store/candidate/admission-document.js.map +1 -0
  122. package/core/dist/runtime-v2/store/candidate/candidate-store.d.ts +53 -0
  123. package/core/dist/runtime-v2/store/candidate/candidate-store.d.ts.map +1 -1
  124. package/core/dist/runtime-v2/store/candidate/memory-candidate-store.d.ts +35 -0
  125. package/core/dist/runtime-v2/store/candidate/memory-candidate-store.d.ts.map +1 -1
  126. package/core/dist/runtime-v2/store/candidate/memory-candidate-store.js +48 -0
  127. package/core/dist/runtime-v2/store/candidate/memory-candidate-store.js.map +1 -1
  128. package/core/dist/runtime-v2/store/candidate/sqlite-candidate-store.d.ts +44 -0
  129. package/core/dist/runtime-v2/store/candidate/sqlite-candidate-store.d.ts.map +1 -1
  130. package/core/dist/runtime-v2/store/candidate/sqlite-candidate-store.js +136 -0
  131. package/core/dist/runtime-v2/store/candidate/sqlite-candidate-store.js.map +1 -1
  132. package/core/dist/runtime-v2/store/context/sqlite-context-assembler.js.map +1 -1
  133. package/core/dist/runtime-v2/store/intervention-evidence-schema.js +1 -1
  134. package/core/dist/runtime-v2/store/runtime-state-manager.d.ts +65 -0
  135. package/core/dist/runtime-v2/store/runtime-state-manager.d.ts.map +1 -1
  136. package/core/dist/runtime-v2/store/runtime-state-manager.js +46 -0
  137. package/core/dist/runtime-v2/store/runtime-state-manager.js.map +1 -1
  138. package/core/dist/runtime-v2/store/sqlite-connection.d.ts.map +1 -1
  139. package/core/dist/runtime-v2/store/sqlite-connection.js +42 -0
  140. package/core/dist/runtime-v2/store/sqlite-connection.js.map +1 -1
  141. package/core/dist/runtime-v2/store/sqlite-intervention-evidence-store.d.ts +1 -1
  142. package/core/dist/runtime-v2/store/sqlite-intervention-evidence-store.d.ts.map +1 -1
  143. package/core/dist/runtime-v2/store/sqlite-intervention-evidence-store.js.map +1 -1
  144. package/core/dist/runtime-v2/store/task/sqlite-family-reopen.d.ts +19 -0
  145. package/core/dist/runtime-v2/store/task/sqlite-family-reopen.d.ts.map +1 -0
  146. package/core/dist/runtime-v2/store/task/sqlite-family-reopen.js +36 -0
  147. package/core/dist/runtime-v2/store/task/sqlite-family-reopen.js.map +1 -0
  148. package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts +23 -0
  149. package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts.map +1 -1
  150. package/core/dist/runtime-v2/store/task/sqlite-task-store.js +50 -0
  151. package/core/dist/runtime-v2/store/task/sqlite-task-store.js.map +1 -1
  152. package/core/dist/runtime-v2/task-status.d.ts +3 -1
  153. package/core/dist/runtime-v2/task-status.d.ts.map +1 -1
  154. package/core/dist/runtime-v2/task-status.js +1 -1
  155. package/core/dist/runtime-v2/task-status.js.map +1 -1
  156. package/core/dist/runtime-v2/trajectory-schema.d.ts.map +1 -1
  157. package/core/dist/runtime-v2/trajectory-schema.js +11 -0
  158. package/core/dist/runtime-v2/trajectory-schema.js.map +1 -1
  159. package/core/dist/runtime-v2/types/event-types.d.ts +2 -2
  160. package/core/dist/runtime-v2/types/event-types.d.ts.map +1 -1
  161. package/core/dist/runtime-v2/types/event-types.js +1 -1
  162. package/core/dist/runtime-v2/types/event-types.js.map +1 -1
  163. package/core/dist/runtime-v2/types/evidence-chain-contract.d.ts +1 -1
  164. package/core/dist/runtime-v2/types/evidence-chain-contract.d.ts.map +1 -1
  165. package/core/dist/runtime-v2/types/evidence-chain-contract.js +2 -2
  166. package/core/dist/runtime-v2/types/evidence-chain-contract.js.map +1 -1
  167. package/core/dist/runtime-v2/types/intervention-evidence-contract.d.ts +3 -3
  168. package/core/dist/runtime-v2/types/intervention-evidence-contract.d.ts.map +1 -1
  169. package/core/dist/runtime-v2/types/intervention-evidence-contract.js +1 -0
  170. package/core/dist/runtime-v2/types/intervention-evidence-contract.js.map +1 -1
  171. package/core/dist/telemetry-event.d.ts +2 -2
  172. package/core/dist/telemetry-event.d.ts.map +1 -1
  173. package/core/dist/telemetry-event.js +4 -0
  174. package/core/dist/telemetry-event.js.map +1 -1
  175. package/core/dist/trajectory-types.d.ts +2 -2
  176. package/core/dist/trajectory-types.d.ts.map +1 -1
  177. package/core/package.json +3 -3
  178. package/dist/index.d.ts.map +1 -1
  179. package/dist/index.js +84 -8
  180. package/dist/index.js.map +1 -1
  181. package/dist/installer.d.ts +42 -11
  182. package/dist/installer.d.ts.map +1 -1
  183. package/dist/installer.js +140 -36
  184. package/dist/installer.js.map +1 -1
  185. package/dist/installers/index.d.ts +18 -7
  186. package/dist/installers/index.d.ts.map +1 -1
  187. package/dist/installers/index.js +13 -4
  188. package/dist/installers/index.js.map +1 -1
  189. package/dist/installers/workbuddy-host-installer.d.ts +246 -0
  190. package/dist/installers/workbuddy-host-installer.d.ts.map +1 -0
  191. package/dist/installers/workbuddy-host-installer.js +1419 -0
  192. package/dist/installers/workbuddy-host-installer.js.map +1 -0
  193. package/dist/mvp-config.d.ts +24 -0
  194. package/dist/mvp-config.d.ts.map +1 -1
  195. package/dist/mvp-config.js +36 -0
  196. package/dist/mvp-config.js.map +1 -1
  197. package/dist/prompts.d.ts +13 -0
  198. package/dist/prompts.d.ts.map +1 -1
  199. package/dist/prompts.js +45 -8
  200. package/dist/prompts.js.map +1 -1
  201. package/dist/uninstaller.d.ts +1 -1
  202. package/dist/uninstaller.d.ts.map +1 -1
  203. package/dist/uninstaller.js +29 -4
  204. package/dist/uninstaller.js.map +1 -1
  205. package/host-runtime/dist/governance-signal-admission.d.ts +63 -0
  206. package/host-runtime/dist/governance-signal-admission.js +322 -53
  207. package/host-runtime/dist/host-liveness-contract.js +7 -1
  208. package/host-runtime/dist/index.d.ts +1 -0
  209. package/host-runtime/dist/index.js +5 -1
  210. package/host-runtime/dist/internalization-consumer-cycle.d.ts +12 -0
  211. package/host-runtime/dist/internalization-consumer-cycle.js +43 -1
  212. package/host-runtime/dist/intervention-evidence-ingress.d.ts +1 -1
  213. package/host-runtime/dist/product-telemetry/service.js +1 -1
  214. package/host-runtime/dist/production-pain-evidence.d.ts +59 -1
  215. package/host-runtime/dist/production-pain-evidence.js +200 -17
  216. package/host-runtime/dist/prompt-activation-deliverability.js +5 -4
  217. package/host-runtime/dist/prompt-injection-event-evidence.d.ts +1 -1
  218. package/host-runtime/dist/prompt-injection-event-evidence.js +5 -1
  219. package/host-runtime/dist/prompt-injection-projection.d.ts +14 -5
  220. package/host-runtime/dist/prompt-injection-projection.js +30 -16
  221. package/host-runtime/dist/workbuddy-worker-status.d.ts +24 -0
  222. package/host-runtime/dist/workbuddy-worker-status.js +52 -0
  223. package/host-runtime/package.json +3 -3
  224. package/install-layout/dist/index.d.ts +18 -1
  225. package/install-layout/dist/index.js +15 -2
  226. package/install-layout/package.json +1 -1
  227. package/package.json +6 -5
  228. package/pd-cli/dist/commands/candidate.d.ts.map +1 -1
  229. package/pd-cli/dist/commands/candidate.js +44 -4
  230. package/pd-cli/dist/commands/candidate.js.map +1 -1
  231. package/pd-cli/dist/commands/codex-worker.d.ts.map +1 -1
  232. package/pd-cli/dist/commands/codex-worker.js +30 -1
  233. package/pd-cli/dist/commands/codex-worker.js.map +1 -1
  234. package/pd-cli/dist/commands/config-doctor.d.ts.map +1 -1
  235. package/pd-cli/dist/commands/config-doctor.js +19 -4
  236. package/pd-cli/dist/commands/config-doctor.js.map +1 -1
  237. package/pd-cli/dist/commands/diagnose.d.ts.map +1 -1
  238. package/pd-cli/dist/commands/diagnose.js +4 -1
  239. package/pd-cli/dist/commands/diagnose.js.map +1 -1
  240. package/pd-cli/dist/commands/health-codex-install.d.ts +85 -0
  241. package/pd-cli/dist/commands/health-codex-install.d.ts.map +1 -0
  242. package/pd-cli/dist/commands/health-codex-install.js +207 -0
  243. package/pd-cli/dist/commands/health-codex-install.js.map +1 -0
  244. package/pd-cli/dist/commands/health-codex.d.ts.map +1 -1
  245. package/pd-cli/dist/commands/health-codex.js +78 -0
  246. package/pd-cli/dist/commands/health-codex.js.map +1 -1
  247. package/pd-cli/dist/commands/pain-list.d.ts +3 -2
  248. package/pd-cli/dist/commands/pain-list.d.ts.map +1 -1
  249. package/pd-cli/dist/commands/pain-list.js +10 -10
  250. package/pd-cli/dist/commands/pain-list.js.map +1 -1
  251. package/pd-cli/dist/commands/pain-record.d.ts +2 -0
  252. package/pd-cli/dist/commands/pain-record.d.ts.map +1 -1
  253. package/pd-cli/dist/commands/pain-record.js +36 -0
  254. package/pd-cli/dist/commands/pain-record.js.map +1 -1
  255. package/pd-cli/dist/commands/pain-retry.d.ts +6 -0
  256. package/pd-cli/dist/commands/pain-retry.d.ts.map +1 -1
  257. package/pd-cli/dist/commands/pain-retry.js +152 -5
  258. package/pd-cli/dist/commands/pain-retry.js.map +1 -1
  259. package/pd-cli/dist/commands/runtime-activation.d.ts +1 -1
  260. package/pd-cli/dist/commands/runtime-activation.d.ts.map +1 -1
  261. package/pd-cli/dist/commands/runtime-activation.js +3 -3
  262. package/pd-cli/dist/commands/runtime-activation.js.map +1 -1
  263. package/pd-cli/dist/commands/workbuddy-worker.d.ts +19 -0
  264. package/pd-cli/dist/commands/workbuddy-worker.d.ts.map +1 -0
  265. package/pd-cli/dist/commands/workbuddy-worker.js +168 -0
  266. package/pd-cli/dist/commands/workbuddy-worker.js.map +1 -0
  267. package/pd-cli/dist/index.js +29 -1
  268. package/pd-cli/dist/index.js.map +1 -1
  269. package/pd-cli/dist/resolve-workspace.d.ts +14 -2
  270. package/pd-cli/dist/resolve-workspace.d.ts.map +1 -1
  271. package/pd-cli/dist/resolve-workspace.js +88 -21
  272. package/pd-cli/dist/resolve-workspace.js.map +1 -1
  273. package/pd-cli/dist/services/admission-write-gate.d.ts +24 -0
  274. package/pd-cli/dist/services/admission-write-gate.d.ts.map +1 -0
  275. package/pd-cli/dist/services/admission-write-gate.js +41 -0
  276. package/pd-cli/dist/services/admission-write-gate.js.map +1 -0
  277. package/pd-cli/dist/services/pd-config-loader.d.ts +39 -7
  278. package/pd-cli/dist/services/pd-config-loader.d.ts.map +1 -1
  279. package/pd-cli/dist/services/pd-config-loader.js +111 -18
  280. package/pd-cli/dist/services/pd-config-loader.js.map +1 -1
  281. package/pd-cli/dist/services/rulehost-pipeline-runner.d.ts +10 -0
  282. package/pd-cli/dist/services/rulehost-pipeline-runner.d.ts.map +1 -1
  283. package/pd-cli/dist/services/rulehost-pipeline-runner.js +59 -4
  284. package/pd-cli/dist/services/rulehost-pipeline-runner.js.map +1 -1
  285. package/pd-cli/package.json +4 -3
  286. package/plugin/dist/bundle.js +564 -509
  287. package/plugin/dist/governance-audit.js +1 -1
  288. package/plugin/dist/openclaw.plugin.json +1 -1
  289. package/plugin/dist/rulehost-evidence.js +1 -1
  290. package/plugin/openclaw.plugin.json +1 -1
  291. package/plugin/package.json +3 -3
  292. package/release-manager/dist/index.d.ts.map +1 -1
  293. package/release-manager/dist/index.js +84 -8
  294. package/release-manager/dist/index.js.map +1 -1
  295. package/release-manager/dist/installer.d.ts +42 -11
  296. package/release-manager/dist/installer.d.ts.map +1 -1
  297. package/release-manager/dist/installer.js +140 -36
  298. package/release-manager/dist/installer.js.map +1 -1
  299. package/release-manager/dist/installers/index.d.ts +18 -7
  300. package/release-manager/dist/installers/index.d.ts.map +1 -1
  301. package/release-manager/dist/installers/index.js +13 -4
  302. package/release-manager/dist/installers/index.js.map +1 -1
  303. package/release-manager/dist/installers/workbuddy-host-installer.d.ts +246 -0
  304. package/release-manager/dist/installers/workbuddy-host-installer.d.ts.map +1 -0
  305. package/release-manager/dist/installers/workbuddy-host-installer.js +1419 -0
  306. package/release-manager/dist/installers/workbuddy-host-installer.js.map +1 -0
  307. package/release-manager/dist/mvp-config.d.ts +24 -0
  308. package/release-manager/dist/mvp-config.d.ts.map +1 -1
  309. package/release-manager/dist/mvp-config.js +36 -0
  310. package/release-manager/dist/mvp-config.js.map +1 -1
  311. package/release-manager/dist/prompts.d.ts +13 -0
  312. package/release-manager/dist/prompts.d.ts.map +1 -1
  313. package/release-manager/dist/prompts.js +45 -8
  314. package/release-manager/dist/prompts.js.map +1 -1
  315. package/release-manager/dist/uninstaller.d.ts +1 -1
  316. package/release-manager/dist/uninstaller.d.ts.map +1 -1
  317. package/release-manager/dist/uninstaller.js +29 -4
  318. package/release-manager/dist/uninstaller.js.map +1 -1
  319. package/release-manager/package.json +5 -4
  320. package/workbuddy-adapter/dist/codec/index.d.ts +2 -0
  321. package/workbuddy-adapter/dist/codec/index.js +2 -0
  322. package/workbuddy-adapter/dist/codec/input-decoder.d.ts +30 -0
  323. package/workbuddy-adapter/dist/codec/input-decoder.js +232 -0
  324. package/workbuddy-adapter/dist/codec/output-encoder.d.ts +62 -0
  325. package/workbuddy-adapter/dist/codec/output-encoder.js +75 -0
  326. package/workbuddy-adapter/dist/host-adapter.d.ts +33 -0
  327. package/workbuddy-adapter/dist/host-adapter.js +23 -0
  328. package/workbuddy-adapter/dist/index.d.ts +16 -0
  329. package/workbuddy-adapter/dist/index.js +16 -0
  330. package/workbuddy-adapter/dist/pd-hook.d.ts +17 -0
  331. package/workbuddy-adapter/dist/pd-hook.js +505 -0
  332. package/workbuddy-adapter/dist/tool-semantics.d.ts +22 -0
  333. package/workbuddy-adapter/dist/tool-semantics.js +33 -0
  334. package/workbuddy-adapter/dist/workbuddy-evidence-recorder.d.ts +50 -0
  335. package/workbuddy-adapter/dist/workbuddy-evidence-recorder.js +238 -0
  336. package/workbuddy-adapter/dist/worker/workspace-worker.d.ts +41 -0
  337. package/workbuddy-adapter/dist/worker/workspace-worker.js +219 -0
  338. package/workbuddy-adapter/dist/workspace-resolution.d.ts +15 -0
  339. package/workbuddy-adapter/dist/workspace-resolution.js +53 -0
  340. package/workbuddy-adapter/package.json +62 -0
  341. package/workbuddy-adapter/plugin/.codebuddy-plugin/plugin.json +6 -0
  342. package/workbuddy-adapter/plugin/hooks/hooks.json +62 -0
  343. package/workbuddy-adapter/plugin/scripts/wrapper.cjs +79 -0
@@ -85,6 +85,37 @@ async function safeRunRecoverySweep(stateManager, ports) {
85
85
  * - P1: 任何 reconcile_error 显式留痕 (不依赖 recovered>0)。
86
86
  */
87
87
  const RECONCILIATION_BUDGET = 5;
88
+ /**
89
+ * PRI-954 (F10) oldest-ready-stage-first ordering. Returns the configured
90
+ * runner kinds reordered to start at the stage that owns the globally oldest
91
+ * ready task (by `createdAt`); later stages keep their relative priority
92
+ * order, so dependency order inside one stage scan is untouched. Tasks
93
+ * without a readable createdAt sort last for ordering purposes (their stage
94
+ * keeps the default position) — unknown age never jumps the queue.
95
+ */
96
+ export function orderKindsByOldestReadyStage(runnerKinds, readyTasks) {
97
+ if (runnerKinds.length <= 1 || readyTasks.length === 0)
98
+ return [...runnerKinds];
99
+ const priorityIndex = new Map(runnerKinds.map((k, i) => [k, i]));
100
+ let oldest = null;
101
+ for (const task of readyTasks) {
102
+ if (typeof task.createdAt !== 'string' || task.createdAt.length === 0)
103
+ continue;
104
+ const index = priorityIndex.get(task.taskKind);
105
+ if (index === undefined)
106
+ continue; // stage outside this consumer's scope
107
+ if (oldest === null ||
108
+ task.createdAt < oldest.createdAt ||
109
+ (task.createdAt === oldest.createdAt && index < oldest.index)) {
110
+ oldest = { taskKind: task.taskKind, createdAt: task.createdAt, index };
111
+ }
112
+ }
113
+ const startKind = oldest?.taskKind;
114
+ const startIndex = startKind !== undefined ? runnerKinds.indexOf(startKind) : -1;
115
+ if (startIndex <= 0)
116
+ return [...runnerKinds];
117
+ return [...runnerKinds.slice(startIndex), ...runnerKinds.slice(0, startIndex)];
118
+ }
88
119
  async function runReconciliationBudget(workspaceDir, orchestrator, ports) {
89
120
  const { logger, emitEvent, logLabel } = ports;
90
121
  try {
@@ -254,7 +285,18 @@ export async function runInternalizationConsumerCycle(workspaceDir, ports) {
254
285
  let lastSkipDecision = 'no_ready_tasks';
255
286
  let lastSkipReason;
256
287
  let taskRuntimeConfig = null;
257
- for (const kind of decision.runnerKinds) {
288
+ // PRI-954 (F10): oldest-ready-stage-first fairness. The fixed dreamer-first
289
+ // order plus one-task-per-cycle let a continuous stream of new dreamer
290
+ // tasks (each real Pain creates one) keep downstream stages waiting for an
291
+ // unbounded number of cycles — measured at 35+ minutes of philosopher
292
+ // starvation under sustained load (2026-10-13 dogfood, findings F10).
293
+ // Starting the stage scan at the stage owning the GLOBALLY OLDEST ready
294
+ // task bounds every stage's wait without concurrency, without persisted
295
+ // scheduler state (works across one-shot `--once` worker processes), and
296
+ // without touching leases, idempotency, dependency order, or the
297
+ // single-task-per-cycle semantics. Ties keep the original priority order.
298
+ const orderedRunnerKinds = orderKindsByOldestReadyStage(decision.runnerKinds, snapshot.readyTasks);
299
+ for (const kind of orderedRunnerKinds) {
258
300
  const agentName = AGENT_NAME_FOR_TASK_KIND[kind];
259
301
  if (agentName === undefined) {
260
302
  lastSkipDecision = 'runtime_config_error';
@@ -8,7 +8,7 @@ export interface InterventionIngressAppendResult {
8
8
  nextAction?: string;
9
9
  }
10
10
  export interface ExistingInterventionObservationSource {
11
- sourceKind: 'openclaw_plugin_event_log' | 'codex_pd_hook_event_log';
11
+ sourceKind: 'openclaw_plugin_event_log' | 'codex_pd_hook_event_log' | 'workbuddy_pd_hook_event_log';
12
12
  sourceLocator: string;
13
13
  observationKey: string;
14
14
  principleId: string;
@@ -193,7 +193,7 @@ function resolveInstallContext(homeDir) {
193
193
  const legacyExtensionExists = fs.existsSync(paths.openClawExtensionDir);
194
194
  const layout = resolveInstallLayout({ homeDir, manifest, canonicalRuntimeExists, legacyExtensionExists });
195
195
  let hostKind = 'other';
196
- if (manifest && manifest.hosts.length === 1 && (manifest.hosts[0] === 'codex' || manifest.hosts[0] === 'openclaw')) {
196
+ if (manifest && manifest.hosts.length === 1 && (manifest.hosts[0] === 'codex' || manifest.hosts[0] === 'openclaw' || manifest.hosts[0] === 'workbuddy')) {
197
197
  [hostKind] = manifest.hosts;
198
198
  }
199
199
  else if (legacyExtensionExists) {
@@ -19,9 +19,14 @@ export interface ProductionPainEnrichment {
19
19
  }
20
20
  export type PainEnrichmentProvider = (event: HostEvent) => unknown | Promise<unknown>;
21
21
  export type PainDatabaseFactory = (databasePath: string) => Database.Database;
22
+ /** SPEC-CODEX-PAIN-CLOSURE-001 CR-01: a tool result is success, failure or unknown. */
23
+ export type ToolOutcomeClassification = 'success' | 'failure' | 'unknown';
22
24
  interface NormalizedOutcome {
25
+ outcome: ToolOutcomeClassification;
23
26
  failure: boolean;
24
- exitCode: number;
27
+ exitCode: number | null;
28
+ /** Bounded structured reason present when the outcome is unknown (rc-9). */
29
+ unknownReason?: string;
25
30
  error?: string;
26
31
  durationMs?: number;
27
32
  params: unknown;
@@ -32,6 +37,16 @@ export interface ProductionToolEventFields {
32
37
  workspaceDir: string;
33
38
  sessionId: string;
34
39
  turnId?: string;
40
+ /**
41
+ * I1: the host's real per-call identity (Codex `tool_use_id`, WorkBuddy
42
+ * `call_id`). Two calls with the same session/generation/tool/params/result
43
+ * but DIFFERENT call ids are two real occurrences and must derive two
44
+ * identities; a redelivery of the SAME call id is one occurrence. Optional:
45
+ * hosts that supply no call id keep the historical content-derived identity
46
+ * (identical-content calls then cannot be distinguished — a documented
47
+ * limitation, never fabricated precision).
48
+ */
49
+ toolCallId?: string;
35
50
  toolName?: string;
36
51
  toolInput?: unknown;
37
52
  toolOutput?: unknown;
@@ -51,6 +66,11 @@ export interface DerivedToolPainIdentity {
51
66
  * fields fed to the live production handler derive the same deterministic
52
67
  * `pain_host_<sha256>` id, so live and observation-delivered admissions of one
53
68
  * tool call converge on one canonical pain.
69
+ *
70
+ * I1 parity obligation: when the host supplies a call id, BOTH paths must pass
71
+ * the same `toolCallId` (Codex admission candidates carry it from the same
72
+ * `tool_use_id` the live decoder required) — otherwise one real call would
73
+ * derive two canonical pains.
54
74
  */
55
75
  export declare function deriveProductionToolPainIdentity(fields: ProductionToolEventFields & {
56
76
  canonicalEventId?: string;
@@ -76,6 +96,44 @@ export declare function deriveProductionCorrectionPainIdentity(fields: {
76
96
  painId: string;
77
97
  };
78
98
  declare function hasCanonicalSchema(db: Database.Database): boolean;
99
+ /** One host-verified tool outcome (SPEC-CODEX-PAIN-CLOSURE-001 CR-01). */
100
+ export interface VerifiedToolOutcomeInput {
101
+ /** Host tool-call id — the join key written by the live PostToolUse path. */
102
+ readonly toolUseId: string;
103
+ readonly toolName: string;
104
+ readonly params?: unknown;
105
+ /** Verified process exit code from the host's authoritative tool-fact record. */
106
+ readonly exitCode: number;
107
+ readonly outputPreview?: string;
108
+ }
109
+ export type VerifiedToolOutcomeStatus = {
110
+ status: 'updated';
111
+ id: number;
112
+ } | {
113
+ status: 'inserted';
114
+ id: number;
115
+ } | {
116
+ status: 'skipped_final';
117
+ id: number;
118
+ reason: 'outcome_already_final';
119
+ } | {
120
+ status: 'unavailable';
121
+ reason: 'trajectory_db_not_found' | 'trajectory_write_failed' | `trajectory_write_failed:${string}`;
122
+ };
123
+ /**
124
+ * SPEC-CODEX-PAIN-CLOSURE-001 CR-01: upgrade the live-hook observation of a
125
+ * tool call with the host-verified final result (exit code) read through the
126
+ * existing authorized transcript-facts path. Only rows still recorded as
127
+ * `unknown` (text-only live response) are upgraded — a structured live fact
128
+ * is never overwritten. Missing live rows are inserted so the verified fact
129
+ * is durable even when the live hook never fired.
130
+ */
131
+ export declare function recordVerifiedToolOutcomes(args: {
132
+ workspaceDir: string;
133
+ sessionId: string;
134
+ outcomes: readonly VerifiedToolOutcomeInput[];
135
+ painDatabaseFactory?: PainDatabaseFactory;
136
+ }): VerifiedToolOutcomeStatus[];
79
137
  /**
80
138
  * PRI-640: host attribution is supplied by the host adapter that constructs
81
139
  * this shared handler (OpenClaw / Codex adapters) — never guessed inside the
@@ -2,7 +2,7 @@ import { createHash } from 'node:crypto';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import Database from 'better-sqlite3';
5
- import { buildToolFailureObservation, evaluateTriage, evaluateTriggerController, resolveSourceKind, sanitizeToolParams, sanitizeValue, } from '@principles/core/runtime-v2';
5
+ import { applyTrajectorySchemaBase, buildToolFailureObservation, evaluateTriage, evaluateTriggerController, resolveSourceKind, sanitizeToolParams, sanitizeValue, } from '@principles/core/runtime-v2';
6
6
  const WRITE_TOOLS = new Set(['write', 'edit', 'apply_patch', 'write_file', 'edit_file', 'replace']);
7
7
  /** Shared with the governance admission path so both gate tool failures identically. */
8
8
  export const PRODUCTION_WRITE_TOOLS = WRITE_TOOLS;
@@ -17,19 +17,62 @@ function field(value, key) {
17
17
  return undefined;
18
18
  return Object.getOwnPropertyDescriptor(value, key)?.value;
19
19
  }
20
+ /**
21
+ * SPEC-CODEX-PAIN-CLOSURE-001 CR-01 (PRI-960): classify a tool result as
22
+ * success / failure / unknown from structured facts only — a missing exit
23
+ * code must never become a fabricated 0, and text-only responses must never
24
+ * be guessed into success or failure by keyword scanning.
25
+ *
26
+ * Verified Codex facts (codex-rs core/src/tools/context.rs): PostToolUse
27
+ * `tool_response` for the shell tool is a plain string (the truncated output
28
+ * text); the exit code is only published to the transcript CommandExecution
29
+ * record. Plain-text responses therefore classify as unknown here and are
30
+ * upgraded later by the consent-gated transcript path (recordVerifiedToolOutcomes).
31
+ *
32
+ * Structured read tools (object/array results without an error field) are a
33
+ * legitimate completion of the tool contract and stay success (SPEC §5.1).
34
+ */
20
35
  function normalizeOutcome(event) {
21
36
  const context = 'context' in event ? event.context : event;
22
37
  const envelope = context.toolOutput;
23
38
  const result = field(envelope, 'result') ?? envelope;
24
- const errorValue = field(envelope, 'error');
25
- const resultExit = field(result, 'exitCode');
26
- const detailsExit = field(field(result, 'details'), 'exitCode');
27
- const exitCode = typeof resultExit === 'number' ? resultExit : typeof detailsExit === 'number' ? detailsExit : 0;
39
+ const errorValue = field(envelope, 'error') ?? field(result, 'error');
28
40
  const error = errorValue === undefined || errorValue === null || errorValue === '' ? undefined : String(errorValue);
41
+ // Exit-code candidates: camelCase AND snake_case at the envelope, result,
42
+ // details and metadata levels (Codex transcript facts arrive snake_case).
43
+ const exitCandidates = [
44
+ field(envelope, 'exitCode'), field(envelope, 'exit_code'),
45
+ field(result, 'exitCode'), field(result, 'exit_code'),
46
+ field(field(result, 'details'), 'exitCode'), field(field(result, 'details'), 'exit_code'),
47
+ field(field(result, 'metadata'), 'exitCode'), field(field(result, 'metadata'), 'exit_code'),
48
+ ];
49
+ const exitCandidate = exitCandidates.find((value) => typeof value === 'number' && Number.isInteger(value));
50
+ const exitCode = typeof exitCandidate === 'number' ? exitCandidate : null;
29
51
  const durationValue = field(envelope, 'durationMs');
52
+ let outcome;
53
+ let unknownReason;
54
+ if (error !== undefined) {
55
+ outcome = 'failure';
56
+ }
57
+ else if (exitCode !== null) {
58
+ outcome = exitCode === 0 ? 'success' : 'failure';
59
+ }
60
+ else if (typeof envelope === 'string' || typeof result === 'string') {
61
+ outcome = 'unknown';
62
+ unknownReason = 'text_only_response';
63
+ }
64
+ else if (isRecord(envelope) || Array.isArray(envelope)) {
65
+ outcome = 'success';
66
+ }
67
+ else {
68
+ outcome = 'unknown';
69
+ unknownReason = 'unconfirmed_result_shape';
70
+ }
30
71
  return {
31
- failure: error !== undefined || exitCode !== 0,
72
+ outcome,
73
+ failure: outcome === 'failure',
32
74
  exitCode,
75
+ ...(unknownReason !== undefined ? { unknownReason } : {}),
33
76
  ...(error ? { error: error.slice(0, MAX_PREVIEW) } : {}),
34
77
  ...(typeof durationValue === 'number' && Number.isFinite(durationValue) && durationValue >= 0 ? { durationMs: durationValue } : {}),
35
78
  params: context.toolInput ?? {},
@@ -104,6 +147,9 @@ function preview(value) {
104
147
  }
105
148
  function ids(input) {
106
149
  const { outcome, sanitizedParams, canonicalEventId } = input;
150
+ // The toolCallId key is included ONLY when the host supplied one, so the
151
+ // canonical string of a no-call-id host is byte-identical to the historical
152
+ // derivation (persisted rows stay replay-stable across the upgrade).
107
153
  const canonical = stable({
108
154
  workspaceDir: path.resolve(input.workspaceDir),
109
155
  sessionId: input.sessionId,
@@ -111,6 +157,7 @@ function ids(input) {
111
157
  toolName: input.toolName,
112
158
  source: input.source,
113
159
  suppliedEventId: canonicalEventId ?? null,
160
+ ...(input.toolCallId !== undefined ? { toolCallId: input.toolCallId } : {}),
114
161
  params: sanitizedParams,
115
162
  result: sanitizeValue(outcome.result, 0, input.workspaceDir),
116
163
  error: outcome.error ?? null,
@@ -126,6 +173,11 @@ function ids(input) {
126
173
  * fields fed to the live production handler derive the same deterministic
127
174
  * `pain_host_<sha256>` id, so live and observation-delivered admissions of one
128
175
  * tool call converge on one canonical pain.
176
+ *
177
+ * I1 parity obligation: when the host supplies a call id, BOTH paths must pass
178
+ * the same `toolCallId` (Codex admission candidates carry it from the same
179
+ * `tool_use_id` the live decoder required) — otherwise one real call would
180
+ * derive two canonical pains.
129
181
  */
130
182
  export function deriveProductionToolPainIdentity(fields) {
131
183
  const outcome = normalizeOutcome(fields);
@@ -137,6 +189,7 @@ export function deriveProductionToolPainIdentity(fields) {
137
189
  ...(fields.toolName !== undefined ? { toolName: fields.toolName } : {}),
138
190
  source: fields.source,
139
191
  ...(fields.canonicalEventId !== undefined ? { canonicalEventId: fields.canonicalEventId } : {}),
192
+ ...(fields.toolCallId !== undefined ? { toolCallId: fields.toolCallId } : {}),
140
193
  outcome,
141
194
  sanitizedParams,
142
195
  });
@@ -220,13 +273,107 @@ function hasCanonicalSchema(db) {
220
273
  return false;
221
274
  // Preparing the exact production statements proves syntax/column readiness
222
275
  // without executing mutation or invoking host enrichment side effects.
276
+ db.prepare('SELECT 1 FROM tool_calls WHERE session_id = ? AND tool_use_id = ?');
223
277
  db.prepare('SELECT 1 FROM tool_calls WHERE session_id = ? AND tool_name = ? AND params_json = ? AND outcome = ? AND exit_code IS ? AND error_message IS ? AND result_preview IS ?');
224
278
  db.prepare('SELECT 1 FROM pain_events WHERE canonical_pain_id = ?');
225
279
  db.prepare('INSERT INTO sessions (session_id, started_at, updated_at) VALUES (?, ?, ?) ON CONFLICT(session_id) DO UPDATE SET updated_at = excluded.updated_at');
226
- db.prepare('INSERT INTO tool_calls (session_id, tool_name, outcome, duration_ms, exit_code, error_type, error_message, gfi_before, gfi_after, params_json, result_preview, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
280
+ db.prepare('INSERT INTO tool_calls (session_id, tool_name, outcome, duration_ms, exit_code, error_type, error_message, gfi_before, gfi_after, params_json, result_preview, created_at, tool_use_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
227
281
  db.prepare('INSERT INTO pain_events (session_id, source, score, reason, severity, origin, confidence, text, canonical_pain_id, runtime_task_id, host_kind, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
228
282
  return true;
229
283
  }
284
+ /**
285
+ * SPEC-CODEX-PAIN-CLOSURE-001 CR-01: additive migration for the host
286
+ * tool-call id join column. The schema-owning modules (trajectory-schema.ts
287
+ * applyTrajectorySchemaBase) add it for every fresh/managed open; this local
288
+ * ensure covers the raw production-handler open, which historically opens
289
+ * trajectory.db without running the schema apply.
290
+ */
291
+ function ensureToolCallsToolUseIdColumn(db) {
292
+ const columns = db.prepare('PRAGMA table_info(tool_calls)').all();
293
+ if (columns.some((row) => pragmaField(row, 'name') === 'tool_use_id'))
294
+ return;
295
+ try {
296
+ db.exec('ALTER TABLE tool_calls ADD COLUMN tool_use_id TEXT');
297
+ db.exec('CREATE INDEX IF NOT EXISTS idx_tool_calls_session_tool_use_id ON tool_calls(session_id, tool_use_id)');
298
+ }
299
+ catch {
300
+ // A concurrent writer may have added it between the check and the ALTER;
301
+ // the prepared-statement readiness check below fails loud if it is truly absent.
302
+ }
303
+ }
304
+ /**
305
+ * SPEC-CODEX-PAIN-CLOSURE-001 CR-01: upgrade the live-hook observation of a
306
+ * tool call with the host-verified final result (exit code) read through the
307
+ * existing authorized transcript-facts path. Only rows still recorded as
308
+ * `unknown` (text-only live response) are upgraded — a structured live fact
309
+ * is never overwritten. Missing live rows are inserted so the verified fact
310
+ * is durable even when the live hook never fired.
311
+ */
312
+ export function recordVerifiedToolOutcomes(args) {
313
+ const results = [];
314
+ if (args.outcomes.length === 0)
315
+ return results;
316
+ const dbPath = path.join(args.workspaceDir, '.state', 'trajectory.db');
317
+ if (!fs.existsSync(dbPath)) {
318
+ return args.outcomes.map(() => ({ status: 'unavailable', reason: 'trajectory_db_not_found' }));
319
+ }
320
+ let db;
321
+ try {
322
+ db = args.painDatabaseFactory ? args.painDatabaseFactory(dbPath) : new Database(dbPath);
323
+ db.pragma('busy_timeout = 5000');
324
+ // Workspaces whose trajectory.db only ever carried the governance tables
325
+ // (no live PostToolUse writer ran yet) lack tool_calls: apply the one
326
+ // canonical base schema before upgrading (idempotent, DDL authority stays
327
+ // with core's trajectory-schema).
328
+ const hasToolCalls = (db.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'tool_calls'").get() !== undefined);
329
+ if (!hasToolCalls)
330
+ applyTrajectorySchemaBase(db);
331
+ ensureToolCallsToolUseIdColumn(db);
332
+ const findByToolUseId = db.prepare('SELECT id, outcome FROM tool_calls WHERE session_id = ? AND tool_use_id = ? ORDER BY id LIMIT 1');
333
+ const upgradeUnknown = db.prepare("UPDATE tool_calls SET outcome = ?, exit_code = ?, result_preview = COALESCE(?, result_preview) WHERE id = ? AND outcome = 'unknown'");
334
+ const insertVerified = db.prepare('INSERT INTO tool_calls (session_id, tool_name, outcome, duration_ms, exit_code, error_type, error_message, gfi_before, gfi_after, params_json, result_preview, created_at, tool_use_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
335
+ const upsertSession = db.prepare('INSERT INTO sessions (session_id, started_at, updated_at) VALUES (?, ?, ?) ON CONFLICT(session_id) DO UPDATE SET updated_at = excluded.updated_at');
336
+ const createdAt = new Date().toISOString();
337
+ db.transaction(() => {
338
+ for (const input of args.outcomes) {
339
+ if (typeof input.exitCode !== 'number' || !Number.isInteger(input.exitCode))
340
+ continue;
341
+ const finalOutcome = input.exitCode === 0 ? 'success' : 'failure';
342
+ const existing = findByToolUseId.get(args.sessionId, input.toolUseId);
343
+ const boundedPreview = typeof input.outputPreview === 'string' && input.outputPreview.length > 0
344
+ ? input.outputPreview.slice(0, MAX_PREVIEW)
345
+ : null;
346
+ if (existing !== undefined) {
347
+ if (existing.outcome === 'unknown') {
348
+ upgradeUnknown.run(finalOutcome, input.exitCode, boundedPreview, existing.id);
349
+ results.push({ status: 'updated', id: existing.id });
350
+ }
351
+ else {
352
+ results.push({ status: 'skipped_final', id: existing.id, reason: 'outcome_already_final' });
353
+ }
354
+ continue;
355
+ }
356
+ upsertSession.run(args.sessionId, createdAt, createdAt);
357
+ const sanitizedParams = sanitizeToolParams(input.params ?? {}, args.workspaceDir);
358
+ const info = insertVerified.run(args.sessionId, input.toolName, finalOutcome, null, input.exitCode, null, null, null, null, stable(sanitizedParams), boundedPreview, createdAt, input.toolUseId);
359
+ results.push({ status: 'inserted', id: Number(info.lastInsertRowid) });
360
+ }
361
+ })();
362
+ }
363
+ catch (error) {
364
+ const reason = `trajectory_write_failed:${error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200)}`;
365
+ const filled = results.length;
366
+ for (let i = filled; i < args.outcomes.length; i += 1)
367
+ results.push({ status: 'unavailable', reason });
368
+ }
369
+ finally {
370
+ try {
371
+ db?.close();
372
+ }
373
+ catch { /* write result already determined; cleanup is best-effort */ }
374
+ }
375
+ return results;
376
+ }
230
377
  /**
231
378
  * PRI-640: host attribution is supplied by the host adapter that constructs
232
379
  * this shared handler (OpenClaw / Codex adapters) — never guessed inside the
@@ -248,7 +395,7 @@ export function createProductionPainEvidenceHandler(options = {}) {
248
395
  options.events?.recordToolCall(event.context.sessionId, {
249
396
  toolName: event.context.toolName ?? '',
250
397
  ...(earlyOutcome.error ? { error: earlyOutcome.error } : {}),
251
- ...(earlyOutcome.exitCode !== 0 ? { exitCode: earlyOutcome.exitCode } : {}),
398
+ ...(earlyOutcome.exitCode !== null && earlyOutcome.exitCode !== 0 ? { exitCode: earlyOutcome.exitCode } : {}),
252
399
  ...(event.context.turnId !== undefined ? { runId: event.context.turnId } : {}),
253
400
  ...(event.context.toolCallId !== undefined ? { toolCallId: event.context.toolCallId } : {}),
254
401
  });
@@ -264,6 +411,7 @@ export function createProductionPainEvidenceHandler(options = {}) {
264
411
  try {
265
412
  db = options.painDatabaseFactory ? options.painDatabaseFactory(dbPath) : new Database(dbPath);
266
413
  db.pragma('busy_timeout = 5000');
414
+ ensureToolCallsToolUseIdColumn(db);
267
415
  if (!hasCanonicalSchema(db)) {
268
416
  db.close();
269
417
  db = undefined;
@@ -296,22 +444,43 @@ export function createProductionPainEvidenceHandler(options = {}) {
296
444
  const outcome = normalizeOutcome(event);
297
445
  const sanitizedParams = sanitizeToolParams(outcome.params, event.context.workspaceDir);
298
446
  const toolName = event.context.toolName ?? '';
299
- const sourceObservation = buildToolFailureObservation({ toolName, error: outcome.error, exitCode: outcome.exitCode });
447
+ const sourceObservation = buildToolFailureObservation({ toolName, error: outcome.error, exitCode: outcome.exitCode ?? undefined });
448
+ const nonZeroExit = outcome.exitCode !== null && outcome.exitCode !== 0;
300
449
  const sourceKind = resolveSourceKind({
301
450
  observedAt: new Date().toISOString(), workspaceId: event.context.workspaceDir,
302
451
  sessionId: event.context.sessionId, toolName, failureSource: sourceObservation.failureSource,
303
- toolNotFound: sourceObservation.toolNotFound, nonZeroExit: outcome.exitCode !== 0,
452
+ toolNotFound: sourceObservation.toolNotFound, nonZeroExit,
304
453
  });
305
454
  const relativePath = enrichment.relativePath ?? String(field(outcome.params, 'file_path') ?? field(outcome.params, 'path') ?? 'unknown').slice(0, 500);
306
455
  const isRisky = enrichment.isRisky ?? (path.isAbsolute(relativePath) && !path.resolve(relativePath).startsWith(`${path.resolve(event.context.workspaceDir)}${path.sep}`));
307
- const painScore = enrichment.painScore ?? Math.min(100, (outcome.exitCode !== 0 ? 70 : 0) + (isRisky ? 20 : 0));
456
+ // Merged scoring semantics (WorkBuddy admission floor × CR-01): the 70
457
+ // failure base requires a VERIFIED non-zero exit code (branch review
458
+ // round: a thrown error with no exit evidence stays at the risky-path
459
+ // bonus only, honestly below the GOVERNANCE_STRONG_PAIN_SCORE floor, so
460
+ // unverified error shapes never spawn diagnostician work on their own).
461
+ // Classification and RECORDING remain main's three-way contract — the
462
+ // row still records outcome='failure' with exit_code NULL (never a
463
+ // fabricated 0).
464
+ const verifiedNonZeroExit = outcome.exitCode !== null && outcome.exitCode !== 0;
465
+ const painScore = enrichment.painScore ?? Math.min(100, (verifiedNonZeroExit ? 70 : 0) + (isRisky ? 20 : 0));
308
466
  const errorHash = enrichment.errorHash ?? createHash('sha256').update(outcome.error ?? String(outcome.exitCode)).digest('hex');
467
+ // Cooldown is DEBOUNCE, not event identity (I1): the key deliberately has
468
+ // NO toolCallId. Repeated failures of the same failureSource+error within
469
+ // the window suppress the diagnostic trigger storm, while each real call
470
+ // still earns its own tool_calls evidence row through call-id-sensitive
471
+ // identity below.
309
472
  const cooldownKey = `${path.resolve(event.context.workspaceDir)}:${event.context.sessionId}:${sourceObservation.failureSource}:${errorHash}`;
310
473
  const last = cooldowns.get(cooldownKey);
311
474
  const cooldownActive = last !== undefined && Date.now() - last < PAIN_COOLDOWN_WINDOW_MS;
312
475
  const triage = evaluateTriage({ sourceKind, score: painScore, consecutiveErrors: enrichment.consecutiveErrors, isRisky });
313
476
  const trigger = evaluateTriggerController({ triageResult: triage, isOwnerManual: false, isCooldownActive: cooldownActive, isValid: true, score: painScore, sessionId: event.context.sessionId });
314
- const admitted = outcome.failure && WRITE_TOOLS.has(toolName) && trigger.shouldCreateDiagnosticTask;
477
+ // Merged semantics (WorkBuddy × CR-01): the three-way classification from
478
+ // SPEC-CODEX-PAIN-CLOSURE-001 gates admission ('unknown' results are never
479
+ // pain), and WorkBuddy tool names are capitalized (Write/Edit — probe P03)
480
+ // while OpenClaw/Codex are lowercase; admission is about the tool FAMILY,
481
+ // so compare case-insensitively at this one boundary. tool_calls evidence
482
+ // above keeps the raw host name.
483
+ const admitted = outcome.outcome === 'failure' && WRITE_TOOLS.has(toolName.toLowerCase()) && trigger.shouldCreateDiagnosticTask;
315
484
  const { eventId, painId } = ids({
316
485
  workspaceDir: event.context.workspaceDir,
317
486
  sessionId: event.context.sessionId,
@@ -319,6 +488,11 @@ export function createProductionPainEvidenceHandler(options = {}) {
319
488
  ...(toolName !== undefined ? { toolName } : {}),
320
489
  source: event.source,
321
490
  ...(enrichment.eventId ? { canonicalEventId: enrichment.eventId } : {}),
491
+ // I1: the host's real call identity participates when supplied —
492
+ // distinct call ids are distinct events; a same-call-id redelivery
493
+ // derives the same identity and stays idempotent. Absent → historical
494
+ // content-derived identity (no fabricated precision).
495
+ ...(event.context.toolCallId !== undefined ? { toolCallId: event.context.toolCallId } : {}),
322
496
  outcome,
323
497
  sanitizedParams,
324
498
  });
@@ -329,14 +503,23 @@ export function createProductionPainEvidenceHandler(options = {}) {
329
503
  let duplicateAdmitted = false;
330
504
  try {
331
505
  db.transaction(() => {
332
- if (db.prepare('SELECT 1 FROM tool_calls WHERE session_id = ? AND tool_name = ? AND params_json = ? AND outcome = ? AND exit_code IS ? AND error_message IS ? AND result_preview IS ?').get(event.context.sessionId, toolName, paramsJson, outcome.failure ? 'failure' : 'success', outcome.exitCode, outcome.error ?? null, resultPreview)) {
506
+ // CR-01: the host tool-call id is the stable join key for the
507
+ // consent-gated transcript upgrade (recordVerifiedToolOutcomes);
508
+ // content-shape probing stays the fallback for hosts without ids.
509
+ if (event.context.toolCallId !== undefined
510
+ && db.prepare('SELECT 1 FROM tool_calls WHERE session_id = ? AND tool_use_id = ?').get(event.context.sessionId, event.context.toolCallId) !== undefined) {
511
+ duplicate = true;
512
+ duplicateAdmitted = db.prepare('SELECT 1 FROM pain_events WHERE canonical_pain_id = ?').get(painId) !== undefined;
513
+ return;
514
+ }
515
+ if (db.prepare('SELECT 1 FROM tool_calls WHERE session_id = ? AND tool_name = ? AND params_json = ? AND outcome = ? AND exit_code IS ? AND error_message IS ? AND result_preview IS ?').get(event.context.sessionId, toolName, paramsJson, outcome.outcome, outcome.exitCode, outcome.error ?? null, resultPreview)) {
333
516
  duplicate = true;
334
517
  duplicateAdmitted = db.prepare('SELECT 1 FROM pain_events WHERE canonical_pain_id = ?').get(painId) !== undefined;
335
518
  return;
336
519
  }
337
520
  db.prepare(`INSERT INTO sessions (session_id, started_at, updated_at) VALUES (?, ?, ?) ON CONFLICT(session_id) DO UPDATE SET updated_at = excluded.updated_at`).run(event.context.sessionId, createdAt, createdAt);
338
- db.prepare(`INSERT INTO tool_calls (session_id, tool_name, outcome, duration_ms, exit_code, error_type, error_message, gfi_before, gfi_after, params_json, result_preview, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
339
- .run(event.context.sessionId, toolName, outcome.failure ? 'failure' : 'success', outcome.durationMs ?? null, outcome.exitCode, outcome.error ? outcome.error.split(/[\s:]/, 1)[0] : null, outcome.error ?? null, null, null, paramsJson, resultPreview, createdAt);
521
+ db.prepare(`INSERT INTO tool_calls (session_id, tool_name, outcome, duration_ms, exit_code, error_type, error_message, gfi_before, gfi_after, params_json, result_preview, created_at, tool_use_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
522
+ .run(event.context.sessionId, toolName, outcome.outcome, outcome.durationMs ?? null, outcome.exitCode, outcome.error ? outcome.error.split(/[\s:]/, 1)[0] : null, outcome.error ?? null, null, null, paramsJson, resultPreview, createdAt, event.context.toolCallId ?? null);
340
523
  if (admitted) {
341
524
  // Raw observation only (Pain Diagnosis Persistence SPEC §8): the
342
525
  // pain row must record WHAT happened, not WHY. Attribution
@@ -364,7 +547,7 @@ export function createProductionPainEvidenceHandler(options = {}) {
364
547
  }
365
548
  }
366
549
  catch (error) {
367
- return { decision: 'observe', source: event.source, warnings: [...(emissionWarning ? [emissionWarning] : []), `trajectory_write_failed:${error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200)}`], metadata: { outcome: outcome.failure ? 'failure' : 'success', admitted: false, duplicate: false, nextAction: 'inspect the workspace trajectory database and retry' } };
550
+ return { decision: 'observe', source: event.source, warnings: [...(emissionWarning ? [emissionWarning] : []), `trajectory_write_failed:${error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200)}`], metadata: { outcome: outcome.outcome, admitted: false, duplicate: false, nextAction: 'inspect the workspace trajectory database and retry' } };
368
551
  }
369
552
  finally {
370
553
  try {
@@ -378,7 +561,7 @@ export function createProductionPainEvidenceHandler(options = {}) {
378
561
  source: event.source,
379
562
  ...(emissionWarning ? { warnings: [emissionWarning] } : {}),
380
563
  metadata: {
381
- eventId, painId: effectiveAdmitted ? painId : null, outcome: outcome.failure ? 'failure' : 'success', admitted: effectiveAdmitted, duplicate,
564
+ eventId, painId: effectiveAdmitted ? painId : null, outcome: outcome.outcome, ...(outcome.unknownReason !== undefined ? { outcomeReason: outcome.unknownReason } : {}), exitCode: outcome.exitCode, admitted: effectiveAdmitted, duplicate,
382
565
  sourceKind, failureSource: sourceObservation.failureSource ?? null, triggerOutcome: trigger.outcome,
383
566
  triggerReason: trigger.reason, painScore, isRisky, relativePath, agentId: enrichment.agentId ?? null,
384
567
  evidence: enrichment.evidence ?? [],
@@ -3,7 +3,7 @@ import path from 'node:path';
3
3
  import { RUNTIME_V2_PRINCIPLE_BUDGET, SqliteConnection, SqlitePIArtifactStore, computeFeatureFlagsFromConfig, listEntryLine, renderPrinciplesToDirectives, resolvePrincipleFromArtifact, trimToBudget, } from '@principles/core/runtime-v2';
4
4
  import { escapeXml } from '@principles/core/prompt-builder';
5
5
  import { loadPdConfigForPlugin } from './pd-config.js';
6
- import { PROMPT_INJECTION_UNIT, promptRouteForHost, resolvePromptInjectionRouteDecision, } from './prompt-injection-projection.js';
6
+ import { PROMPT_INJECTION_TARGET_HOSTS, PROMPT_INJECTION_UNIT, promptRouteForHost, resolvePromptInjectionRouteDecision, } from './prompt-injection-projection.js';
7
7
  function syntheticPendingActivation(artifactId) {
8
8
  return {
9
9
  activationId: `pending:${artifactId}`,
@@ -114,8 +114,9 @@ export async function checkPromptArtifactDeliverability(input) {
114
114
  }
115
115
  // Route unconfirmed: report per-host fit for every standard host instead of
116
116
  // guessing (R-A1) — the write gate refuses, the UI can show both facts.
117
- const hosts = ['openclaw', 'codex'];
118
- const perHost = hosts.map((hostKind) => {
117
+ // Roster derives from the single SSoT list (review P3-1: a hand-typed
118
+ // two-host array dropped WorkBuddy from unconfirmed deliverability reports).
119
+ const perHost = PROMPT_INJECTION_TARGET_HOSTS.map((hostKind) => {
119
120
  const route = promptRouteForHost(hostKind, decision.flagEnabled);
120
121
  const { fits, costChars } = computePromptSingleItemFit({
121
122
  principle: resolved.principle,
@@ -127,7 +128,7 @@ export async function checkPromptArtifactDeliverability(input) {
127
128
  return {
128
129
  status: 'unconfirmed',
129
130
  reason: `route_unconfirmed: ${decision.unconfirmedReason ?? 'effective injection route cannot be confirmed'}`,
130
- nextAction: decision.nextAction ?? 'pass an explicit target host (openclaw|codex)',
131
+ nextAction: decision.nextAction ?? 'pass an explicit target host (openclaw|codex|workbuddy)',
131
132
  perHost,
132
133
  };
133
134
  }
@@ -25,7 +25,7 @@
25
25
  export interface InjectionEvidenceRow {
26
26
  workspaceDir: string;
27
27
  activationId: string;
28
- hostKind: 'openclaw' | 'codex' | 'unknown';
28
+ hostKind: 'openclaw' | 'codex' | 'workbuddy' | 'unknown';
29
29
  hostIdentityComplete: boolean;
30
30
  eventIdentityComplete: boolean;
31
31
  /** Deduplicated injection facts: this activation appeared in the injected set. */
@@ -1,5 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
+ import { isPromptInjectionTargetHost } from './prompt-injection-projection.js';
3
4
  function isRecord(value) {
4
5
  return typeof value === 'object' && value !== null && !Array.isArray(value);
5
6
  }
@@ -63,7 +64,10 @@ export function readInjectionEventEvidence(input) {
63
64
  const runId = nonEmptyString(data.runId);
64
65
  const eventId = nonEmptyString(data.eventId);
65
66
  const rawHostKind = data.hostKind;
66
- const hostKind = rawHostKind === 'openclaw' || rawHostKind === 'codex' ? rawHostKind : 'unknown';
67
+ // Known-host membership comes from the roster predicate (review R1: the
68
+ // hand-typed openclaw|codex check classified real WorkBuddy injection
69
+ // events as the unknown bucket, contradicting this row's own type).
70
+ const hostKind = isPromptInjectionTargetHost(rawHostKind) ? rawHostKind : 'unknown';
67
71
  const { activationIds } = data;
68
72
  if (workspaceDir === undefined || sessionId === undefined || !Array.isArray(activationIds)
69
73
  || !activationIds.every((id) => typeof id === 'string')) {
@@ -1,7 +1,14 @@
1
1
  import { type PromptSelectionPolicy } from '@principles/core/runtime-v2';
2
2
  /** Hosts with a production prompt-injection path today (shared GovernanceHostKind boundary). */
3
- export type PromptInjectionTargetHost = 'openclaw' | 'codex';
3
+ export type PromptInjectionTargetHost = 'openclaw' | 'codex' | 'workbuddy';
4
+ /** Single host roster (review P2): every consumer derives from this list, so a
5
+ * new host cannot fork route-unconfirmed per-host forecasts from the routes. */
4
6
  export declare const PROMPT_INJECTION_TARGET_HOSTS: readonly PromptInjectionTargetHost[];
7
+ /** Runtime membership test derived from the roster (rc-2): the ONE guard for
8
+ * "is this untrusted host-kind string a host with a known prompt-injection
9
+ * route". Every consumer narrows with this — never by re-typing the literals
10
+ * (review R1: each re-typed list forked the roster and dropped WorkBuddy). */
11
+ export declare function isPromptInjectionTargetHost(kind: unknown): kind is PromptInjectionTargetHost;
5
12
  export type PromptInjectionRouteName = 'legacy_trim' | 'shared_render';
6
13
  /**
7
14
  * Which serialization the budget is charged against (SPEC R-A2): the legacy
@@ -48,9 +55,11 @@ export interface PromptInjectionRouteDecision {
48
55
  }
49
56
  /**
50
57
  * THE one route formula (P4): Codex always injects through the shared route
51
- * (codex-adapter has no flag branch); OpenClaw routes by its
52
- * `abstraction_layer_v1` gate. Every consumer that needs "which route would
53
- * host X take" must call this — never re-type the ternary.
58
+ * (codex-adapter has no flag branch); so does WorkBuddy (workbuddy-adapter
59
+ * renders through the shared runtime unconditionally — it has no legacy
60
+ * route); OpenClaw routes by its `abstraction_layer_v1` gate. Every consumer
61
+ * that needs "which route would host X take" must call this — never re-type
62
+ * the ternary.
54
63
  */
55
64
  export declare function promptRouteForHost(hostKind: PromptInjectionTargetHost, flagEnabled: boolean): PromptInjectionRouteName;
56
65
  /**
@@ -175,7 +184,7 @@ export declare function buildLivePromptInjectionProjection(input: {
175
184
  * rather than assuming FIFO.
176
185
  */
177
186
  roundKey?: number;
178
- /** AC-01: explicit route binding — 'codex' always shared, 'openclaw' by flag. */
187
+ /** AC-01: explicit route binding — 'codex'/'workbuddy' always shared, 'openclaw' by flag. */
179
188
  targetHost?: PromptInjectionTargetHost;
180
189
  }): Promise<PromptInjectionProjection>;
181
190
  /**