create-principles-disciple 1.73.0 → 1.74.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 (272) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +24 -0
  3. package/console/dist/server/index.js +13 -1
  4. package/console/dist/server/routes/agents.js +3 -118
  5. package/console/dist/server/routes/update-history.d.ts +12 -0
  6. package/console/dist/server/routes/update-history.js +52 -0
  7. package/console/dist/server/routes/update.d.ts +10 -0
  8. package/console/dist/server/routes/update.js +435 -0
  9. package/console/dist/ui/App.js +2 -1
  10. package/console/dist/ui/components/UpdateBanner.d.ts +1 -0
  11. package/console/dist/ui/components/UpdateBanner.js +41 -0
  12. package/console/dist/ui/components/UpdateProgressDialog.d.ts +12 -0
  13. package/console/dist/ui/components/UpdateProgressDialog.js +28 -0
  14. package/console/dist/ui/i18n/en.json +0 -1
  15. package/console/dist/ui/i18n/zh-CN.json +0 -1
  16. package/console/dist/ui/pages/AgentsPage.js +3 -17
  17. package/console/dist/ui/pages/TasksPage.js +0 -1
  18. package/console/dist/ui/pages/UpdatePage.d.ts +1 -0
  19. package/console/dist/ui/pages/UpdatePage.js +142 -0
  20. package/console/dist/web/assets/app.css +7 -0
  21. package/console/dist/web/assets/app.js +456 -173
  22. package/console/package.json +2 -0
  23. package/core/dist/runtime-v2/adapter/__tests__/schema-prompt-adapter.test.d.ts +2 -0
  24. package/core/dist/runtime-v2/adapter/__tests__/schema-prompt-adapter.test.d.ts.map +1 -0
  25. package/core/dist/runtime-v2/adapter/__tests__/schema-prompt-adapter.test.js +270 -0
  26. package/core/dist/runtime-v2/adapter/__tests__/schema-prompt-adapter.test.js.map +1 -0
  27. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.d.ts +1 -1
  28. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.d.ts.map +1 -1
  29. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js +4 -3
  30. package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js.map +1 -1
  31. package/core/dist/runtime-v2/adapter/schema-prompt-adapter.d.ts +13 -0
  32. package/core/dist/runtime-v2/adapter/schema-prompt-adapter.d.ts.map +1 -0
  33. package/core/dist/runtime-v2/adapter/schema-prompt-adapter.js +266 -0
  34. package/core/dist/runtime-v2/adapter/schema-prompt-adapter.js.map +1 -0
  35. package/core/dist/runtime-v2/adapter/tools/diagnostician-tool.d.ts +3 -7
  36. package/core/dist/runtime-v2/adapter/tools/diagnostician-tool.d.ts.map +1 -1
  37. package/core/dist/runtime-v2/adapter/tools/diagnostician-tool.js +7 -19
  38. package/core/dist/runtime-v2/adapter/tools/diagnostician-tool.js.map +1 -1
  39. package/core/dist/runtime-v2/diagnostician/__tests__/diagnostician-prompt-builder.test.js +97 -15
  40. package/core/dist/runtime-v2/diagnostician/__tests__/diagnostician-prompt-builder.test.js.map +1 -1
  41. package/core/dist/runtime-v2/diagnostician-output.d.ts +0 -10
  42. package/core/dist/runtime-v2/diagnostician-output.d.ts.map +1 -1
  43. package/core/dist/runtime-v2/diagnostician-output.js +5 -10
  44. package/core/dist/runtime-v2/diagnostician-output.js.map +1 -1
  45. package/core/dist/runtime-v2/diagnostician-prompt-builder.d.ts +6 -1
  46. package/core/dist/runtime-v2/diagnostician-prompt-builder.d.ts.map +1 -1
  47. package/core/dist/runtime-v2/diagnostician-prompt-builder.js +23 -15
  48. package/core/dist/runtime-v2/diagnostician-prompt-builder.js.map +1 -1
  49. package/dist/index.js +0 -0
  50. package/dist/installer.d.ts.map +1 -1
  51. package/dist/installer.js +67 -0
  52. package/dist/installer.js.map +1 -1
  53. package/dist/uninstaller.d.ts +0 -5
  54. package/dist/uninstaller.d.ts.map +1 -1
  55. package/dist/uninstaller.js +53 -8
  56. package/dist/uninstaller.js.map +1 -1
  57. package/dist/updater.d.ts +45 -0
  58. package/dist/updater.d.ts.map +1 -0
  59. package/dist/updater.js +333 -0
  60. package/dist/updater.js.map +1 -0
  61. package/dist/utils/env.d.ts +6 -0
  62. package/dist/utils/env.d.ts.map +1 -1
  63. package/dist/utils/env.js +54 -0
  64. package/dist/utils/env.js.map +1 -1
  65. package/package.json +6 -2
  66. package/pd-cli/dist/index.js +4 -1
  67. package/pd-cli/dist/index.js.map +1 -1
  68. package/pd-cli/package.json +2 -2
  69. package/plugin/templates/langs/en/skills/ai-sprint-orchestration/runtime/.gitignore +2 -2
  70. package/plugin/templates/langs/zh/skills/ai-sprint-orchestration/runtime/.gitignore +2 -2
  71. package/core/dist/runtime-v2/__tests__/nocturnal-compliance-trust-boundary.test.d.ts +0 -2
  72. package/core/dist/runtime-v2/__tests__/nocturnal-compliance-trust-boundary.test.d.ts.map +0 -1
  73. package/core/dist/runtime-v2/__tests__/nocturnal-compliance-trust-boundary.test.js +0 -215
  74. package/core/dist/runtime-v2/__tests__/nocturnal-compliance-trust-boundary.test.js.map +0 -1
  75. package/core/dist/runtime-v2/idle-trigger/__tests__/idle-trigger-config-resolve.test.d.ts +0 -2
  76. package/core/dist/runtime-v2/idle-trigger/__tests__/idle-trigger-config-resolve.test.d.ts.map +0 -1
  77. package/core/dist/runtime-v2/idle-trigger/__tests__/idle-trigger-config-resolve.test.js +0 -92
  78. package/core/dist/runtime-v2/idle-trigger/__tests__/idle-trigger-config-resolve.test.js.map +0 -1
  79. package/core/dist/runtime-v2/idle-trigger/__tests__/idle-trigger-decision.test.d.ts +0 -2
  80. package/core/dist/runtime-v2/idle-trigger/__tests__/idle-trigger-decision.test.d.ts.map +0 -1
  81. package/core/dist/runtime-v2/idle-trigger/__tests__/idle-trigger-decision.test.js +0 -134
  82. package/core/dist/runtime-v2/idle-trigger/__tests__/idle-trigger-decision.test.js.map +0 -1
  83. package/core/dist/runtime-v2/idle-trigger/__tests__/idle-trigger-edge-cases.test.d.ts +0 -2
  84. package/core/dist/runtime-v2/idle-trigger/__tests__/idle-trigger-edge-cases.test.d.ts.map +0 -1
  85. package/core/dist/runtime-v2/idle-trigger/__tests__/idle-trigger-edge-cases.test.js +0 -226
  86. package/core/dist/runtime-v2/idle-trigger/__tests__/idle-trigger-edge-cases.test.js.map +0 -1
  87. package/core/dist/runtime-v2/idle-trigger/idle-trigger-decision.d.ts +0 -3
  88. package/core/dist/runtime-v2/idle-trigger/idle-trigger-decision.d.ts.map +0 -1
  89. package/core/dist/runtime-v2/idle-trigger/idle-trigger-decision.js +0 -5
  90. package/core/dist/runtime-v2/idle-trigger/idle-trigger-decision.js.map +0 -1
  91. package/core/dist/runtime-v2/idle-trigger/idle-trigger-policy.d.ts +0 -4
  92. package/core/dist/runtime-v2/idle-trigger/idle-trigger-policy.d.ts.map +0 -1
  93. package/core/dist/runtime-v2/idle-trigger/idle-trigger-policy.js +0 -81
  94. package/core/dist/runtime-v2/idle-trigger/idle-trigger-policy.js.map +0 -1
  95. package/core/dist/runtime-v2/idle-trigger/idle-trigger-types.d.ts +0 -29
  96. package/core/dist/runtime-v2/idle-trigger/idle-trigger-types.d.ts.map +0 -1
  97. package/core/dist/runtime-v2/idle-trigger/idle-trigger-types.js +0 -15
  98. package/core/dist/runtime-v2/idle-trigger/idle-trigger-types.js.map +0 -1
  99. package/core/dist/runtime-v2/idle-trigger/index.d.ts +0 -5
  100. package/core/dist/runtime-v2/idle-trigger/index.d.ts.map +0 -1
  101. package/core/dist/runtime-v2/idle-trigger/index.js +0 -4
  102. package/core/dist/runtime-v2/idle-trigger/index.js.map +0 -1
  103. package/core/dist/runtime-v2/nocturnal/candidate-scoring.d.ts +0 -486
  104. package/core/dist/runtime-v2/nocturnal/candidate-scoring.d.ts.map +0 -1
  105. package/core/dist/runtime-v2/nocturnal/candidate-scoring.js +0 -493
  106. package/core/dist/runtime-v2/nocturnal/candidate-scoring.js.map +0 -1
  107. package/core/dist/runtime-v2/nocturnal/index.d.ts +0 -9
  108. package/core/dist/runtime-v2/nocturnal/index.d.ts.map +0 -1
  109. package/core/dist/runtime-v2/nocturnal/index.js +0 -5
  110. package/core/dist/runtime-v2/nocturnal/index.js.map +0 -1
  111. package/core/dist/runtime-v2/nocturnal/nocturnal-compliance.d.ts +0 -185
  112. package/core/dist/runtime-v2/nocturnal/nocturnal-compliance.d.ts.map +0 -1
  113. package/core/dist/runtime-v2/nocturnal/nocturnal-compliance.js +0 -912
  114. package/core/dist/runtime-v2/nocturnal/nocturnal-compliance.js.map +0 -1
  115. package/core/dist/runtime-v2/nocturnal/snapshot-contract.d.ts +0 -192
  116. package/core/dist/runtime-v2/nocturnal/snapshot-contract.d.ts.map +0 -1
  117. package/core/dist/runtime-v2/nocturnal/snapshot-contract.js +0 -185
  118. package/core/dist/runtime-v2/nocturnal/snapshot-contract.js.map +0 -1
  119. package/core/dist/runtime-v2/nocturnal/trinity-types.d.ts +0 -421
  120. package/core/dist/runtime-v2/nocturnal/trinity-types.d.ts.map +0 -1
  121. package/core/dist/runtime-v2/nocturnal/trinity-types.js +0 -182
  122. package/core/dist/runtime-v2/nocturnal/trinity-types.js.map +0 -1
  123. package/pd-cli/dist/commands/runtime-idle-trigger.d.ts +0 -12
  124. package/pd-cli/dist/commands/runtime-idle-trigger.d.ts.map +0 -1
  125. package/pd-cli/dist/commands/runtime-idle-trigger.js +0 -102
  126. package/pd-cli/dist/commands/runtime-idle-trigger.js.map +0 -1
  127. package/plugin/dist/bundle.js +0 -2424
  128. package/plugin/dist/core/trajectory/index.js +0 -1804
  129. package/plugin/dist/openclaw.plugin.json +0 -65
  130. package/plugin/dist/templates/langs/en/core/AGENTS.md +0 -205
  131. package/plugin/dist/templates/langs/en/core/BOOT.md +0 -60
  132. package/plugin/dist/templates/langs/en/core/BOOTSTRAP.md +0 -273
  133. package/plugin/dist/templates/langs/en/core/HEARTBEAT.md +0 -74
  134. package/plugin/dist/templates/langs/en/core/IDENTITY.md +0 -8
  135. package/plugin/dist/templates/langs/en/core/PRINCIPLES.md +0 -10
  136. package/plugin/dist/templates/langs/en/core/SOUL.md +0 -46
  137. package/plugin/dist/templates/langs/en/core/TOOLS.md +0 -15
  138. package/plugin/dist/templates/langs/en/core/USER.md +0 -10
  139. package/plugin/dist/templates/langs/en/pain/00_seed_samples.md +0 -23
  140. package/plugin/dist/templates/langs/en/pain_dictionary.json +0 -22
  141. package/plugin/dist/templates/langs/en/principles/THINKING_OS.md +0 -64
  142. package/plugin/dist/templates/langs/en/skills/admin/SKILL.md +0 -39
  143. package/plugin/dist/templates/langs/en/skills/ai-sprint-orchestration/EXAMPLES.md +0 -63
  144. package/plugin/dist/templates/langs/en/skills/ai-sprint-orchestration/REFERENCE.md +0 -136
  145. package/plugin/dist/templates/langs/en/skills/ai-sprint-orchestration/SKILL.md +0 -67
  146. package/plugin/dist/templates/langs/en/skills/ai-sprint-orchestration/references/agent-registry.json +0 -143
  147. package/plugin/dist/templates/langs/en/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +0 -107
  148. package/plugin/dist/templates/langs/en/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +0 -107
  149. package/plugin/dist/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +0 -95
  150. package/plugin/dist/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +0 -98
  151. package/plugin/dist/templates/langs/en/skills/ai-sprint-orchestration/references/workflow-v1-acceptance-checklist.md +0 -58
  152. package/plugin/dist/templates/langs/en/skills/ai-sprint-orchestration/references/workflow-v1.4-work-unit-handoff.md +0 -190
  153. package/plugin/dist/templates/langs/en/skills/ai-sprint-orchestration/runtime/.gitignore +0 -2
  154. package/plugin/dist/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/archive.mjs +0 -310
  155. package/plugin/dist/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/contract-enforcement.mjs +0 -683
  156. package/plugin/dist/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/decision.mjs +0 -604
  157. package/plugin/dist/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/state-store.mjs +0 -32
  158. package/plugin/dist/templates/langs/en/skills/ai-sprint-orchestration/scripts/lib/task-specs.mjs +0 -707
  159. package/plugin/dist/templates/langs/en/skills/ai-sprint-orchestration/scripts/run.mjs +0 -3455
  160. package/plugin/dist/templates/langs/en/skills/bootstrap-tools/SKILL.md +0 -53
  161. package/plugin/dist/templates/langs/en/skills/deductive-audit/SKILL.md +0 -36
  162. package/plugin/dist/templates/langs/en/skills/evolution-framework-update/SKILL.md +0 -31
  163. package/plugin/dist/templates/langs/en/skills/evolve-system/SKILL.md +0 -46
  164. package/plugin/dist/templates/langs/en/skills/evolve-task/SKILL.md +0 -83
  165. package/plugin/dist/templates/langs/en/skills/feedback/SKILL.md +0 -51
  166. package/plugin/dist/templates/langs/en/skills/init-strategy/SKILL.md +0 -54
  167. package/plugin/dist/templates/langs/en/skills/inject-rule/SKILL.md +0 -19
  168. package/plugin/dist/templates/langs/en/skills/manage-okr/SKILL.md +0 -96
  169. package/plugin/dist/templates/langs/en/skills/pd-auditor/SKILL.md +0 -61
  170. package/plugin/dist/templates/langs/en/skills/pd-cli-operator/SKILL.md +0 -67
  171. package/plugin/dist/templates/langs/en/skills/pd-daily/SKILL.md +0 -199
  172. package/plugin/dist/templates/langs/en/skills/pd-diagnostician/SKILL.md +0 -466
  173. package/plugin/dist/templates/langs/en/skills/pd-explorer/SKILL.md +0 -65
  174. package/plugin/dist/templates/langs/en/skills/pd-grooming/SKILL.md +0 -46
  175. package/plugin/dist/templates/langs/en/skills/pd-implementer/SKILL.md +0 -68
  176. package/plugin/dist/templates/langs/en/skills/pd-mentor/SKILL.md +0 -230
  177. package/plugin/dist/templates/langs/en/skills/pd-pain-signal/SKILL.md +0 -30
  178. package/plugin/dist/templates/langs/en/skills/pd-planner/SKILL.md +0 -65
  179. package/plugin/dist/templates/langs/en/skills/pd-runtime-v2/SKILL.md +0 -61
  180. package/plugin/dist/templates/langs/en/skills/plan-script/SKILL.md +0 -32
  181. package/plugin/dist/templates/langs/en/skills/profile/SKILL.md +0 -24
  182. package/plugin/dist/templates/langs/en/skills/reflection/SKILL.md +0 -40
  183. package/plugin/dist/templates/langs/en/skills/reflection-log/SKILL.md +0 -37
  184. package/plugin/dist/templates/langs/en/skills/report/SKILL.md +0 -13
  185. package/plugin/dist/templates/langs/en/skills/root-cause/SKILL.md +0 -33
  186. package/plugin/dist/templates/langs/en/skills/triage/SKILL.md +0 -29
  187. package/plugin/dist/templates/langs/en/skills/watch-evolution/SKILL.md +0 -33
  188. package/plugin/dist/templates/langs/zh/core/AGENTS.md +0 -197
  189. package/plugin/dist/templates/langs/zh/core/BOOT.md +0 -60
  190. package/plugin/dist/templates/langs/zh/core/BOOTSTRAP.md +0 -273
  191. package/plugin/dist/templates/langs/zh/core/HEARTBEAT.md +0 -98
  192. package/plugin/dist/templates/langs/zh/core/IDENTITY.md +0 -8
  193. package/plugin/dist/templates/langs/zh/core/PRINCIPLES.md +0 -7
  194. package/plugin/dist/templates/langs/zh/core/SOUL.md +0 -46
  195. package/plugin/dist/templates/langs/zh/core/TOOLS.md +0 -15
  196. package/plugin/dist/templates/langs/zh/core/USER.md +0 -10
  197. package/plugin/dist/templates/langs/zh/pain/00_seed_samples.md +0 -24
  198. package/plugin/dist/templates/langs/zh/pain_dictionary.json +0 -18
  199. package/plugin/dist/templates/langs/zh/principles/THINKING_OS.md +0 -64
  200. package/plugin/dist/templates/langs/zh/skills/admin/SKILL.md +0 -41
  201. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/EXAMPLES.md +0 -63
  202. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/REFERENCE.md +0 -136
  203. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/SKILL.md +0 -67
  204. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/references/agent-registry.json +0 -143
  205. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +0 -107
  206. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +0 -107
  207. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/nocturnal-trinity-quality-enhancement.json +0 -111
  208. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +0 -95
  209. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +0 -98
  210. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/references/workflow-v1-acceptance-checklist.md +0 -58
  211. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/references/workflow-v1.4-work-unit-handoff.md +0 -190
  212. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/runtime/.gitignore +0 -2
  213. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/archive.mjs +0 -310
  214. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/contract-enforcement.mjs +0 -683
  215. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/decision.mjs +0 -604
  216. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/state-store.mjs +0 -32
  217. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/scripts/lib/task-specs.mjs +0 -707
  218. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/scripts/run.mjs +0 -3455
  219. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/test/archive.test.mjs +0 -230
  220. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/test/contract-enforcement.test.mjs +0 -672
  221. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/test/decision.test.mjs +0 -1321
  222. package/plugin/dist/templates/langs/zh/skills/ai-sprint-orchestration/test/run.test.mjs +0 -1435
  223. package/plugin/dist/templates/langs/zh/skills/bootstrap-tools/SKILL.md +0 -52
  224. package/plugin/dist/templates/langs/zh/skills/deductive-audit/SKILL.md +0 -36
  225. package/plugin/dist/templates/langs/zh/skills/evolution-framework-update/SKILL.md +0 -31
  226. package/plugin/dist/templates/langs/zh/skills/evolve-system/SKILL.md +0 -46
  227. package/plugin/dist/templates/langs/zh/skills/evolve-task/SKILL.md +0 -83
  228. package/plugin/dist/templates/langs/zh/skills/feedback/SKILL.md +0 -53
  229. package/plugin/dist/templates/langs/zh/skills/init-strategy/SKILL.md +0 -54
  230. package/plugin/dist/templates/langs/zh/skills/inject-rule/SKILL.md +0 -19
  231. package/plugin/dist/templates/langs/zh/skills/manage-okr/SKILL.md +0 -109
  232. package/plugin/dist/templates/langs/zh/skills/pd-auditor/SKILL.md +0 -61
  233. package/plugin/dist/templates/langs/zh/skills/pd-cli-operator/SKILL.md +0 -67
  234. package/plugin/dist/templates/langs/zh/skills/pd-daily/SKILL.md +0 -283
  235. package/plugin/dist/templates/langs/zh/skills/pd-diagnostician/SKILL.md +0 -391
  236. package/plugin/dist/templates/langs/zh/skills/pd-explorer/SKILL.md +0 -65
  237. package/plugin/dist/templates/langs/zh/skills/pd-grooming/SKILL.md +0 -46
  238. package/plugin/dist/templates/langs/zh/skills/pd-implementer/SKILL.md +0 -68
  239. package/plugin/dist/templates/langs/zh/skills/pd-mentor/SKILL.md +0 -230
  240. package/plugin/dist/templates/langs/zh/skills/pd-pain-signal/SKILL.md +0 -30
  241. package/plugin/dist/templates/langs/zh/skills/pd-planner/SKILL.md +0 -65
  242. package/plugin/dist/templates/langs/zh/skills/pd-runtime-v2/SKILL.md +0 -61
  243. package/plugin/dist/templates/langs/zh/skills/plan-script/SKILL.md +0 -32
  244. package/plugin/dist/templates/langs/zh/skills/profile/SKILL.md +0 -24
  245. package/plugin/dist/templates/langs/zh/skills/reflection/SKILL.md +0 -40
  246. package/plugin/dist/templates/langs/zh/skills/reflection-log/SKILL.md +0 -37
  247. package/plugin/dist/templates/langs/zh/skills/report/SKILL.md +0 -13
  248. package/plugin/dist/templates/langs/zh/skills/root-cause/SKILL.md +0 -33
  249. package/plugin/dist/templates/langs/zh/skills/triage/SKILL.md +0 -29
  250. package/plugin/dist/templates/langs/zh/skills/watch-evolution/SKILL.md +0 -33
  251. package/plugin/dist/templates/pain_dictionary.json +0 -36
  252. package/plugin/dist/templates/pain_settings.json +0 -103
  253. package/plugin/dist/templates/workspace/.principles/DECISION_POLICY.json +0 -44
  254. package/plugin/dist/templates/workspace/.principles/PRINCIPLES.md +0 -20
  255. package/plugin/dist/templates/workspace/.principles/PROFILE.json +0 -54
  256. package/plugin/dist/templates/workspace/.principles/PROFILE.schema.json +0 -56
  257. package/plugin/dist/templates/workspace/.principles/THINKING_OS.md +0 -64
  258. package/plugin/dist/templates/workspace/.principles/THINKING_OS_ARCHIVE.md +0 -7
  259. package/plugin/dist/templates/workspace/.principles/THINKING_OS_CANDIDATES.md +0 -9
  260. package/plugin/dist/templates/workspace/.principles/models/_INDEX.md +0 -27
  261. package/plugin/dist/templates/workspace/.principles/models/first_principles.md +0 -62
  262. package/plugin/dist/templates/workspace/.principles/models/marketing_4p.md +0 -52
  263. package/plugin/dist/templates/workspace/.principles/models/porter_five.md +0 -63
  264. package/plugin/dist/templates/workspace/.principles/models/swot.md +0 -60
  265. package/plugin/dist/templates/workspace/.principles/models/user_story_map.md +0 -63
  266. package/plugin/dist/templates/workspace/.state/WORKBOARD.json +0 -4
  267. package/plugin/dist/templates/workspace/AUDIT.md +0 -15
  268. package/plugin/dist/templates/workspace/PLAN.md +0 -2
  269. package/plugin/dist/templates/workspace/okr/CURRENT_FOCUS.md +0 -57
  270. package/plugin/dist/templates/workspace/okr/RECOVERY_PROTOCOL.md +0 -56
  271. package/plugin/dist/templates/workspace/okr/TASK_CHANGES.jsonl +0 -6
  272. package/plugin/dist/templates/workspace/okr/WEEK_TASKS.json +0 -6
@@ -1,1804 +0,0 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
- // src/core/trajectory.ts
9
- import Database from "better-sqlite3";
10
- import fs3 from "fs";
11
- import path3 from "path";
12
- import crypto from "crypto";
13
-
14
- // src/utils/file-lock.ts
15
- import * as fs from "fs";
16
- import * as path from "path";
17
- var LockAcquisitionError = class extends Error {
18
- filePath;
19
- lockPath;
20
- constructor(message, filePath, lockPath) {
21
- super(message);
22
- this.name = "LockAcquisitionError";
23
- this.filePath = filePath;
24
- this.lockPath = lockPath;
25
- }
26
- };
27
- var DEFAULT_OPTIONS = {
28
- maxRetries: 50,
29
- baseRetryDelayMs: 10,
30
- maxRetryDelayMs: 500,
31
- lockStaleMs: 1e4,
32
- lockSuffix: ".lock"
33
- };
34
- function isProcessAlive(pid) {
35
- try {
36
- process.kill(pid, 0);
37
- return true;
38
- } catch {
39
- return false;
40
- }
41
- }
42
- function tryAcquireLock(lockPath, pid) {
43
- try {
44
- const lockDir = path.dirname(lockPath);
45
- if (!fs.existsSync(lockDir)) {
46
- fs.mkdirSync(lockDir, { recursive: true });
47
- }
48
- const flags = fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL;
49
- const fd = fs.openSync(lockPath, flags);
50
- fs.writeSync(fd, String(pid));
51
- fs.fsyncSync(fd);
52
- fs.closeSync(fd);
53
- return true;
54
- } catch (err) {
55
- if (err.code === "EEXIST") {
56
- return false;
57
- }
58
- throw err;
59
- }
60
- }
61
- function readLockPid(lockPath) {
62
- try {
63
- const content = fs.readFileSync(lockPath, "utf8");
64
- const pid = parseInt(content.trim(), 10);
65
- return Number.isNaN(pid) ? null : pid;
66
- } catch {
67
- return null;
68
- }
69
- }
70
- function safeReleaseLock(lockPath, expectedPid) {
71
- try {
72
- const pid = readLockPid(lockPath);
73
- if (pid === expectedPid) {
74
- fs.unlinkSync(lockPath);
75
- }
76
- } catch {
77
- }
78
- }
79
- function cleanupStaleLock(lockPath, staleMs) {
80
- try {
81
- const stat = fs.statSync(lockPath);
82
- const pid = readLockPid(lockPath);
83
- const isStale = Date.now() - stat.mtimeMs > staleMs;
84
- const isDead = pid === null || !isProcessAlive(pid);
85
- if (isStale || isDead) {
86
- try {
87
- fs.unlinkSync(lockPath);
88
- return true;
89
- } catch {
90
- return false;
91
- }
92
- }
93
- return false;
94
- } catch {
95
- return false;
96
- }
97
- }
98
- function calculateBackoff(attempt, baseMs, maxMs) {
99
- const exponentialDelay = Math.min(baseMs * Math.pow(2, attempt), maxMs);
100
- const jitter = exponentialDelay * 0.2 * Math.random();
101
- return Math.floor(exponentialDelay + jitter);
102
- }
103
- function sleepSync(ms) {
104
- const end = Date.now() + ms;
105
- while (Date.now() < end) {
106
- }
107
- }
108
- function buildLockError(filePath, lockPath) {
109
- const holderPid = readLockPid(lockPath);
110
- const holderStatus = holderPid !== null ? isProcessAlive(holderPid) ? `alive (PID ${holderPid})` : `dead (PID ${holderPid})` : "unknown";
111
- return new LockAcquisitionError(
112
- `Failed to acquire lock for ${filePath}. Lock holder: ${holderStatus}.`,
113
- filePath,
114
- lockPath
115
- );
116
- }
117
- function tryAcquireWithStaleCleanup(filePath, opts, pid) {
118
- const lockPath = filePath + opts.lockSuffix;
119
- if (tryAcquireLock(lockPath, pid)) {
120
- const actualPid = readLockPid(lockPath);
121
- if (actualPid === pid) {
122
- return { lockPath, pid, acquiredAt: Date.now() };
123
- }
124
- }
125
- cleanupStaleLock(lockPath, opts.lockStaleMs);
126
- if (tryAcquireLock(lockPath, pid)) {
127
- const actualPid = readLockPid(lockPath);
128
- if (actualPid === pid) {
129
- return { lockPath, pid, acquiredAt: Date.now() };
130
- }
131
- }
132
- return null;
133
- }
134
- function acquireLock(filePath, options = {}) {
135
- const opts = { ...DEFAULT_OPTIONS, ...options };
136
- const { pid } = process;
137
- for (let attempt = 0; attempt < opts.maxRetries; attempt++) {
138
- const ctx = tryAcquireWithStaleCleanup(filePath, opts, pid);
139
- if (ctx) {
140
- return ctx;
141
- }
142
- if (attempt < opts.maxRetries - 1) {
143
- const delay = calculateBackoff(attempt, opts.baseRetryDelayMs, opts.maxRetryDelayMs);
144
- sleepSync(delay);
145
- }
146
- }
147
- throw buildLockError(filePath, filePath + opts.lockSuffix);
148
- }
149
- function releaseLock(ctx) {
150
- safeReleaseLock(ctx.lockPath, ctx.pid);
151
- }
152
- function withLock(filePath, fn, options = {}) {
153
- const ctx = acquireLock(filePath, options);
154
- try {
155
- return fn();
156
- } finally {
157
- releaseLock(ctx);
158
- }
159
- }
160
-
161
- // src/utils/io.ts
162
- import * as fs2 from "fs";
163
-
164
- // src/core/paths.ts
165
- import * as path2 from "path";
166
- var posixJoin = (...parts) => path2.posix.join(...parts);
167
- function isWindowsPath(input) {
168
- return /^[A-Za-z]:[\\/]/.test(input) || input.startsWith("\\\\");
169
- }
170
- function joinWorkspacePath(workspaceDir, relativePath) {
171
- if (isWindowsPath(workspaceDir)) {
172
- return path2.win32.join(workspaceDir, relativePath);
173
- }
174
- if (workspaceDir.startsWith("/")) {
175
- return path2.posix.join(workspaceDir, relativePath);
176
- }
177
- return path2.join(workspaceDir, relativePath);
178
- }
179
- var PD_DIRS = {
180
- IDENTITY: ".principles",
181
- MODELS: posixJoin(".principles", "models"),
182
- STATE: ".state",
183
- MEMORY: "memory",
184
- OKR: posixJoin("memory", "okr"),
185
- LOGS: posixJoin("memory", "logs"),
186
- SESSIONS: posixJoin(".state", "sessions"),
187
- PAIN_SAMPLES: posixJoin("memory", "pain"),
188
- LOCKS: posixJoin("memory", ".locks"),
189
- NOCTURNAL_SAMPLES: posixJoin(".state", "nocturnal", "samples"),
190
- NOCTURNAL_MEMORY: posixJoin(".state", "nocturnal", "memory"),
191
- NOCTURNAL_EXPORTS: posixJoin(".state", "exports", "orpo"),
192
- IMPL_CODE_DIR: posixJoin(".state", "principles", "implementations")
193
- };
194
- var PD_FILES = {
195
- PROFILE: posixJoin(PD_DIRS.IDENTITY, "PROFILE.json"),
196
- PRINCIPLES: posixJoin(PD_DIRS.IDENTITY, "PRINCIPLES.md"),
197
- THINKING_OS: posixJoin(PD_DIRS.IDENTITY, "THINKING_OS.md"),
198
- DECISION_POLICY: posixJoin(PD_DIRS.IDENTITY, "DECISION_POLICY.json"),
199
- MODELS_DIR: PD_DIRS.MODELS,
200
- STATE_DIR: PD_DIRS.STATE,
201
- EVOLUTION_QUEUE: posixJoin(PD_DIRS.STATE, "evolution_queue.json"),
202
- EVOLUTION_DIRECTIVE: posixJoin(PD_DIRS.STATE, "evolution_directive.json"),
203
- WORKBOARD: posixJoin(PD_DIRS.STATE, "WORKBOARD.json"),
204
- AGENT_SCORECARD: posixJoin(PD_DIRS.STATE, "AGENT_SCORECARD.json"),
205
- PAIN_FLAG: posixJoin(PD_DIRS.STATE, ".pain_flag"),
206
- SYSTEM_CAPABILITIES: posixJoin(PD_DIRS.STATE, "SYSTEM_CAPABILITIES.json"),
207
- PAIN_SETTINGS: posixJoin(PD_DIRS.STATE, "pain_settings.json"),
208
- PAIN_CANDIDATES: posixJoin(PD_DIRS.STATE, "pain_candidates.json"),
209
- THINKING_OS_USAGE: posixJoin(PD_DIRS.STATE, "thinking_os_usage.json"),
210
- TRAJECTORY_DB: posixJoin(PD_DIRS.STATE, "trajectory.db"),
211
- TRAJECTORY_BLOBS_DIR: posixJoin(PD_DIRS.STATE, "blobs"),
212
- EXPORTS_DIR: posixJoin(PD_DIRS.STATE, "exports"),
213
- SESSION_DIR: PD_DIRS.SESSIONS,
214
- DICTIONARY: posixJoin(PD_DIRS.STATE, "pain_dictionary.json"),
215
- PRINCIPLE_BLACKLIST: posixJoin(PD_DIRS.STATE, "principle_blacklist.json"),
216
- WORKFLOWS_YAML: posixJoin(PD_DIRS.STATE, "workflows.yaml"),
217
- NOCTURNAL_SAMPLES_DIR: PD_DIRS.NOCTURNAL_SAMPLES,
218
- NOCTURNAL_MEMORY_DIR: PD_DIRS.NOCTURNAL_MEMORY,
219
- NOCTURNAL_EXPORTS_DIR: PD_DIRS.NOCTURNAL_EXPORTS,
220
- IMPL_CODE_DIR: PD_DIRS.IMPL_CODE_DIR,
221
- PLAN: "PLAN.md",
222
- MEMORY_MD: "MEMORY.md",
223
- HEARTBEAT: "HEARTBEAT.md",
224
- SYSTEM_LOG: posixJoin(PD_DIRS.LOGS, "SYSTEM.log"),
225
- REFLECTION_LOG: posixJoin(PD_DIRS.MEMORY, "reflection-log.md"),
226
- USER_CONTEXT: posixJoin(PD_DIRS.MEMORY, "USER_CONTEXT.md"),
227
- OKR_DIR: PD_DIRS.OKR,
228
- CURRENT_FOCUS: posixJoin(PD_DIRS.OKR, "CURRENT_FOCUS.md"),
229
- WEEK_STATE: posixJoin(PD_DIRS.OKR, "WEEK_STATE.json"),
230
- THINKING_OS_CANDIDATES: posixJoin(PD_DIRS.MEMORY, "THINKING_OS_CANDIDATES.md"),
231
- SEMANTIC_PAIN: posixJoin(PD_DIRS.PAIN_SAMPLES, "confusion_samples.md"),
232
- EVOLUTION_STREAM: posixJoin(PD_DIRS.MEMORY, "evolution.jsonl"),
233
- EVOLUTION_LOCK: posixJoin(PD_DIRS.LOCKS, "evolution")
234
- };
235
- function resolvePdPath(workspaceDir, fileKey) {
236
- return joinWorkspacePath(workspaceDir, PD_FILES[fileKey]);
237
- }
238
-
239
- // src/utils/io.ts
240
- var RENAME_MAX_RETRIES = 3;
241
- var RENAME_BASE_DELAY_MS = 50;
242
- function atomicWriteFileSync(filePath, data) {
243
- const tmpPath = filePath + ".tmp";
244
- fs2.writeFileSync(tmpPath, data, "utf8");
245
- let lastError;
246
- for (let attempt = 0; attempt < RENAME_MAX_RETRIES; attempt++) {
247
- try {
248
- fs2.renameSync(tmpPath, filePath);
249
- return;
250
- } catch (err) {
251
- lastError = err;
252
- const code = err.code;
253
- if (code === "EPERM" || code === "EBUSY" || code === "EACCES") {
254
- if (attempt < RENAME_MAX_RETRIES - 1) {
255
- const delay = RENAME_BASE_DELAY_MS * Math.pow(2, attempt);
256
- const waitUntil = Date.now() + delay;
257
- let yielded = false;
258
- while (Date.now() < waitUntil) {
259
- if (!yielded && Date.now() >= waitUntil - 10) {
260
- try {
261
- __require("fs").accessSync?.(tmpPath);
262
- } catch {
263
- }
264
- yielded = true;
265
- }
266
- }
267
- }
268
- continue;
269
- }
270
- break;
271
- }
272
- }
273
- try {
274
- fs2.unlinkSync(tmpPath);
275
- } catch {
276
- }
277
- throw lastError;
278
- }
279
-
280
- // src/config/defaults/runtime.ts
281
- var MS_PER_SECOND = 1e3;
282
- var SECONDS_PER_MINUTE = 60;
283
- var MINUTES_PER_HOUR = 60;
284
- var HOURS_PER_DAY = 24;
285
- var DAYS_PER_WEEK = 7;
286
- var ONE_MINUTE_MS = SECONDS_PER_MINUTE * MS_PER_SECOND;
287
- var ONE_HOUR_MS = MINUTES_PER_HOUR * SECONDS_PER_MINUTE * MS_PER_SECOND;
288
- var ONE_DAY_MS = HOURS_PER_DAY * ONE_HOUR_MS;
289
- var ONE_WEEK_MS = DAYS_PER_WEEK * ONE_DAY_MS;
290
- var WORKFLOW_TTL_MS = 5 * ONE_MINUTE_MS;
291
- var WORKFLOW_TIMEOUT_MS = 15 * ONE_MINUTE_MS;
292
- var WORKFLOW_SWEEP_MS = 30 * ONE_MINUTE_MS;
293
- var THINKING_CHECKPOINT_WINDOW_MS = 5 * ONE_MINUTE_MS;
294
- var EVOLUTION_WORKER_POLL_INTERVAL_MS = 15 * ONE_MINUTE_MS;
295
- var SESSION_MAX_IDLE_MS = 30 * ONE_MINUTE_MS;
296
- var EVENT_LOG_FLUSH_INTERVAL_MS = 30 * MS_PER_SECOND;
297
- var DEFAULT_BUSY_TIMEOUT_MS = 5 * MS_PER_SECOND;
298
- var DEFAULT_IDLE_THRESHOLD_MS = 30 * ONE_MINUTE_MS;
299
- var DEFAULT_COOLDOWN_MS = 30 * ONE_MINUTE_MS;
300
- var MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024;
301
- var TWO_HOURS_MS = 2 * ONE_HOUR_MS;
302
-
303
- // src/config/errors.ts
304
- var codeToCategory = {
305
- LOCK_UNAVAILABLE: "lease_conflict",
306
- PATH_RESOLUTION_ERROR: "workspace_invalid",
307
- WORKSPACE_NOT_FOUND: "workspace_invalid",
308
- SAMPLE_NOT_FOUND: "storage_unavailable",
309
- CONFIGURATION_ERROR: "input_invalid",
310
- DEPENDENCY_ERROR: "runtime_unavailable",
311
- EVOLUTION_PROCESSING_ERROR: "execution_failed",
312
- TRAJECTORY_ERROR: "storage_unavailable"
313
- };
314
- var PdError = class extends Error {
315
- constructor(message, code, options) {
316
- super(message, options);
317
- this.code = code;
318
- this.name = "PdError";
319
- this.category = codeToCategory[code] ?? "execution_failed";
320
- }
321
- code;
322
- category;
323
- };
324
- var SampleNotFoundError = class extends PdError {
325
- constructor(sampleId, options) {
326
- super(
327
- `[PD:Sample] Correction sample not found: ${sampleId}`,
328
- "SAMPLE_NOT_FOUND",
329
- options
330
- );
331
- this.name = "SampleNotFoundError";
332
- }
333
- };
334
-
335
- // src/core/trajectory.ts
336
- var DEFAULT_INLINE_THRESHOLD = 16 * 1024;
337
- var DEFAULT_BUSY_TIMEOUT_MS2 = 5e3;
338
- var DEFAULT_ORPHAN_BLOB_GRACE_DAYS = 7;
339
- var SCHEMA_VERSION = 1;
340
- function nowIso() {
341
- return (/* @__PURE__ */ new Date()).toISOString();
342
- }
343
- function safeJson(value) {
344
- return JSON.stringify(value ?? {});
345
- }
346
- function fileSizeIfExists(filePath) {
347
- try {
348
- return fs3.existsSync(filePath) ? fs3.statSync(filePath).size : 0;
349
- } catch {
350
- return 0;
351
- }
352
- }
353
- function summarizeForDiff(text) {
354
- return text.length > 240 ? `${text.slice(0, 237)}...` : text;
355
- }
356
- function redactText(text) {
357
- return text.replace(/[A-Za-z]:\\[^\s"'`]+/g, "<WINDOWS_PATH>").replace(/\/[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)+/g, "<PATH>").replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi, "<EMAIL>").replace(/\b(sk|rk|pk)_[A-Za-z0-9]+\b/g, "<TOKEN>");
358
- }
359
- var TrajectoryDatabase = class {
360
- workspaceDir;
361
- stateDir;
362
- dbPath;
363
- blobDir;
364
- exportDir;
365
- blobInlineThresholdBytes;
366
- orphanBlobGraceMs;
367
- db;
368
- constructor(opts) {
369
- this.workspaceDir = path3.resolve(opts.workspaceDir);
370
- this.stateDir = resolvePdPath(this.workspaceDir, "STATE_DIR");
371
- this.dbPath = resolvePdPath(this.workspaceDir, "TRAJECTORY_DB");
372
- this.blobDir = resolvePdPath(this.workspaceDir, "TRAJECTORY_BLOBS_DIR");
373
- this.exportDir = resolvePdPath(this.workspaceDir, "EXPORTS_DIR");
374
- this.blobInlineThresholdBytes = opts.blobInlineThresholdBytes ?? DEFAULT_INLINE_THRESHOLD;
375
- this.orphanBlobGraceMs = Math.max(0, (opts.orphanBlobGraceDays ?? DEFAULT_ORPHAN_BLOB_GRACE_DAYS) * 24 * 60 * 60 * 1e3);
376
- fs3.mkdirSync(this.stateDir, { recursive: true });
377
- fs3.mkdirSync(this.blobDir, { recursive: true });
378
- fs3.mkdirSync(this.exportDir, { recursive: true });
379
- this.db = new Database(this.dbPath);
380
- this.db.pragma("journal_mode = WAL");
381
- this.db.pragma("foreign_keys = ON");
382
- this.db.pragma("synchronous = NORMAL");
383
- this.db.pragma(`busy_timeout = ${Math.max(0, opts.busyTimeoutMs ?? DEFAULT_BUSY_TIMEOUT_MS2)}`);
384
- this.initSchema();
385
- this.importLegacyArtifacts();
386
- this.pruneUnreferencedBlobs();
387
- }
388
- dispose() {
389
- this.db.close();
390
- }
391
- recordSession(input) {
392
- const startedAt = input.startedAt ?? nowIso();
393
- this.withWrite(() => {
394
- this.db.prepare(`
395
- INSERT INTO sessions (session_id, started_at, updated_at)
396
- VALUES (?, ?, ?)
397
- ON CONFLICT(session_id) DO UPDATE SET updated_at = excluded.updated_at
398
- `).run(input.sessionId, startedAt, nowIso());
399
- });
400
- }
401
- recordAssistantTurn(input) {
402
- this.recordSession({ sessionId: input.sessionId, startedAt: input.createdAt });
403
- const rawStorage = this.storeRawText("assistant", input.rawText);
404
- const createdAt = input.createdAt ?? nowIso();
405
- return this.withWrite(() => {
406
- const result = this.db.prepare(`
407
- INSERT INTO assistant_turns (
408
- session_id, run_id, provider, model, raw_text, sanitized_text, usage_json,
409
- empathy_signal_json, blob_ref, raw_excerpt, created_at
410
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
411
- `).run(
412
- input.sessionId,
413
- input.runId,
414
- input.provider,
415
- input.model,
416
- rawStorage.inlineText,
417
- input.sanitizedText,
418
- safeJson(input.usageJson),
419
- safeJson(input.empathySignalJson),
420
- rawStorage.blobRef,
421
- rawStorage.excerpt,
422
- createdAt
423
- );
424
- return Number(result.lastInsertRowid);
425
- });
426
- }
427
- recordUserTurn(input) {
428
- this.recordSession({ sessionId: input.sessionId, startedAt: input.createdAt });
429
- const rawStorage = this.storeRawText("user", input.rawText);
430
- const createdAt = input.createdAt ?? nowIso();
431
- return this.withWrite(() => {
432
- const result = this.db.prepare(`
433
- INSERT INTO user_turns (
434
- session_id, turn_index, raw_text, blob_ref, raw_excerpt,
435
- correction_detected, correction_cue, references_assistant_turn_id, created_at
436
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
437
- `).run(
438
- input.sessionId,
439
- input.turnIndex,
440
- rawStorage.inlineText,
441
- rawStorage.blobRef,
442
- rawStorage.excerpt,
443
- input.correctionDetected ? 1 : 0,
444
- input.correctionCue ?? null,
445
- input.referencesAssistantTurnId ?? null,
446
- createdAt
447
- );
448
- return Number(result.lastInsertRowid);
449
- });
450
- }
451
- recordToolCall(input) {
452
- this.recordSession({ sessionId: input.sessionId, startedAt: input.createdAt });
453
- const createdAt = input.createdAt ?? nowIso();
454
- const rowId = this.withWrite(() => {
455
- const result = this.db.prepare(`
456
- INSERT INTO tool_calls (
457
- session_id, tool_name, outcome, duration_ms, exit_code, error_type, error_message,
458
- gfi_before, gfi_after, params_json, created_at
459
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
460
- `).run(
461
- input.sessionId,
462
- input.toolName,
463
- input.outcome,
464
- input.durationMs ?? null,
465
- input.exitCode ?? null,
466
- input.errorType ?? null,
467
- input.errorMessage ?? null,
468
- input.gfiBefore ?? null,
469
- input.gfiAfter ?? null,
470
- safeJson(input.paramsJson),
471
- createdAt
472
- );
473
- return Number(result.lastInsertRowid);
474
- });
475
- if (input.outcome === "success") {
476
- this.maybeCreateCorrectionSample(input.sessionId);
477
- }
478
- return rowId;
479
- }
480
- recordPainEvent(input) {
481
- this.recordSession({ sessionId: input.sessionId, startedAt: input.createdAt });
482
- let insertedId = -1;
483
- this.withWrite(() => {
484
- const runResult = this.db.prepare(`
485
- INSERT INTO pain_events (
486
- session_id, source, score, reason, severity, origin, confidence, text, created_at
487
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
488
- `).run(
489
- input.sessionId,
490
- input.source,
491
- input.score,
492
- input.reason ?? null,
493
- input.severity ?? null,
494
- input.origin ?? null,
495
- input.confidence ?? null,
496
- input.text ?? null,
497
- input.createdAt ?? nowIso()
498
- );
499
- insertedId = runResult.lastInsertRowid;
500
- });
501
- if (input.text && insertedId > 0) {
502
- try {
503
- this.db.prepare(`
504
- INSERT INTO pain_events_fts (text, pain_event_id) VALUES (?, ?)
505
- `).run(input.text, insertedId);
506
- } catch (err) {
507
- console.warn(`[trajectory] FTS index insert failed for pain_event ${insertedId}: ${String(err)}`);
508
- }
509
- }
510
- return insertedId;
511
- }
512
- /**
513
- * Search pain_events using FTS5 full-text search (MEM-04).
514
- * Returns pain events matching the query, ordered by relevance.
515
- */
516
- searchPainEvents(query, limit = 10) {
517
- if (!query || query.trim().length === 0) {
518
- return [];
519
- }
520
- const ftsQuery = query.trim().split(/\s+/).map((term) => `"${term.replace(/"/g, '""')}"`).join(" ");
521
- try {
522
- const results = this.db.prepare(`
523
- SELECT pe.*
524
- FROM pain_events_fts pf
525
- JOIN pain_events pe ON pe.id = pf.pain_event_id
526
- WHERE pain_events_fts MATCH ?
527
- ORDER BY bm25(pain_events_fts) DESC
528
- LIMIT ?
529
- `).all(ftsQuery, limit);
530
- return results.map((row) => ({
531
- id: row.id,
532
- sessionId: row.session_id,
533
- source: row.source,
534
- score: row.score,
535
- reason: row.reason,
536
- severity: row.severity,
537
- origin: row.origin,
538
- confidence: row.confidence,
539
- text: row.text,
540
- createdAt: row.created_at
541
- }));
542
- } catch (err) {
543
- console.warn(`[PD:TrajectoryDatabase] FTS5 search failed: ${String(err)}`);
544
- return [];
545
- }
546
- }
547
- recordGateBlock(input) {
548
- this.withWrite(() => {
549
- this.db.prepare(`
550
- INSERT INTO gate_blocks (session_id, tool_name, file_path, reason, plan_status, created_at)
551
- VALUES (?, ?, ?, ?, ?, ?)
552
- `).run(
553
- input.sessionId ?? null,
554
- input.toolName,
555
- input.filePath ?? null,
556
- input.reason,
557
- input.planStatus ?? null,
558
- input.createdAt ?? nowIso()
559
- );
560
- });
561
- }
562
- recordTrustChange(input) {
563
- this.withWrite(() => {
564
- this.db.prepare(`
565
- INSERT INTO trust_changes (session_id, previous_score, new_score, delta, reason, created_at)
566
- VALUES (?, ?, ?, ?, ?, ?)
567
- `).run(
568
- input.sessionId ?? null,
569
- input.previousScore,
570
- input.newScore,
571
- input.delta,
572
- input.reason,
573
- input.createdAt ?? nowIso()
574
- );
575
- });
576
- }
577
- recordPrincipleEvent(input) {
578
- this.withWrite(() => {
579
- this.db.prepare(`
580
- INSERT INTO principle_events (principle_id, event_type, payload_json, created_at)
581
- VALUES (?, ?, ?, ?)
582
- `).run(
583
- input.principleId ?? null,
584
- input.eventType,
585
- safeJson(input.payload),
586
- input.createdAt ?? nowIso()
587
- );
588
- });
589
- }
590
- recordTaskOutcome(input) {
591
- this.withWrite(() => {
592
- this.db.prepare(`
593
- INSERT INTO task_outcomes (session_id, task_id, outcome, summary, principle_ids_json, created_at)
594
- VALUES (?, ?, ?, ?, ?, ?)
595
- `).run(
596
- input.sessionId,
597
- input.taskId ?? null,
598
- input.outcome,
599
- input.summary ?? null,
600
- safeJson(input.principleIdsJson),
601
- input.createdAt ?? nowIso()
602
- );
603
- });
604
- }
605
- recordEvolutionTask(input) {
606
- const now = nowIso();
607
- const v2 = input;
608
- this.withWrite(() => {
609
- this.db.prepare(`
610
- INSERT INTO evolution_tasks (
611
- task_id, trace_id, source, reason, score, status,
612
- enqueued_at, started_at, completed_at, resolution, created_at, updated_at,
613
- task_kind, priority, retry_count, max_retries, last_error, result_ref
614
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
615
- ON CONFLICT(task_id) DO UPDATE SET
616
- status = excluded.status,
617
- started_at = excluded.started_at,
618
- completed_at = excluded.completed_at,
619
- resolution = excluded.resolution,
620
- updated_at = excluded.updated_at,
621
- task_kind = excluded.task_kind,
622
- priority = excluded.priority,
623
- retry_count = excluded.retry_count,
624
- max_retries = excluded.max_retries,
625
- last_error = excluded.last_error,
626
- result_ref = excluded.result_ref
627
- `).run(
628
- input.taskId,
629
- input.traceId,
630
- input.source,
631
- input.reason ?? null,
632
- input.score ?? 0,
633
- input.status ?? "pending",
634
- input.enqueuedAt ?? null,
635
- input.startedAt ?? null,
636
- input.completedAt ?? null,
637
- input.resolution ?? null,
638
- input.createdAt ?? now,
639
- input.updatedAt ?? now,
640
- v2.taskKind ?? null,
641
- v2.priority ?? null,
642
- v2.retryCount ?? null,
643
- v2.maxRetries ?? null,
644
- v2.lastError ?? null,
645
- v2.resultRef ?? null
646
- );
647
- });
648
- }
649
- updateEvolutionTask(taskId, updates) {
650
- const now = nowIso();
651
- const v2Updates = updates;
652
- this.withWrite(() => {
653
- const setClauses = ["updated_at = ?"];
654
- const values = [now];
655
- if (updates.status !== void 0) {
656
- setClauses.push("status = ?");
657
- values.push(updates.status);
658
- }
659
- if (updates.startedAt !== void 0) {
660
- setClauses.push("started_at = ?");
661
- values.push(updates.startedAt);
662
- }
663
- if (updates.completedAt !== void 0) {
664
- setClauses.push("completed_at = ?");
665
- values.push(updates.completedAt);
666
- }
667
- if (updates.resolution !== void 0) {
668
- setClauses.push("resolution = ?");
669
- values.push(updates.resolution);
670
- }
671
- if (updates.score !== void 0) {
672
- setClauses.push("score = ?");
673
- values.push(updates.score);
674
- }
675
- if (v2Updates.taskKind !== void 0) {
676
- setClauses.push("task_kind = ?");
677
- values.push(v2Updates.taskKind);
678
- }
679
- if (v2Updates.priority !== void 0) {
680
- setClauses.push("priority = ?");
681
- values.push(v2Updates.priority);
682
- }
683
- if (v2Updates.retryCount !== void 0) {
684
- setClauses.push("retry_count = ?");
685
- values.push(v2Updates.retryCount);
686
- }
687
- if (v2Updates.maxRetries !== void 0) {
688
- setClauses.push("max_retries = ?");
689
- values.push(v2Updates.maxRetries);
690
- }
691
- if (v2Updates.lastError !== void 0) {
692
- setClauses.push("last_error = ?");
693
- values.push(v2Updates.lastError);
694
- }
695
- if (v2Updates.resultRef !== void 0) {
696
- setClauses.push("result_ref = ?");
697
- values.push(v2Updates.resultRef);
698
- }
699
- values.push(taskId);
700
- this.db.prepare(`
701
- UPDATE evolution_tasks SET ${setClauses.join(", ")} WHERE task_id = ?
702
- `).run(...values);
703
- });
704
- }
705
- recordEvolutionEvent(input) {
706
- this.withWrite(() => {
707
- this.db.prepare(`
708
- INSERT INTO evolution_events (trace_id, task_id, stage, level, message, summary, metadata_json, created_at)
709
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
710
- `).run(
711
- input.traceId,
712
- input.taskId ?? null,
713
- input.stage,
714
- input.level ?? "info",
715
- input.message,
716
- input.summary ?? null,
717
- safeJson(input.metadata),
718
- input.createdAt ?? nowIso()
719
- );
720
- });
721
- }
722
- /**
723
- * List evolution tasks with optional filtering.
724
- *
725
- * Returns: Analytics data aggregated from trajectory database.
726
- * Not: Runtime truth or real-time queue state.
727
- */
728
- listEvolutionTasks(filters = {}) {
729
- const conditions = [];
730
- const values = [];
731
- if (filters.status) {
732
- conditions.push("status = ?");
733
- values.push(filters.status);
734
- }
735
- if (filters.dateFrom) {
736
- conditions.push("created_at >= ?");
737
- values.push(filters.dateFrom);
738
- }
739
- if (filters.dateTo) {
740
- conditions.push("created_at <= ?");
741
- values.push(filters.dateTo);
742
- }
743
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
744
- const limit = filters.limit ?? 50;
745
- const offset = filters.offset ?? 0;
746
- const rows = this.db.prepare(`
747
- SELECT id, task_id, trace_id, source, reason, score, status,
748
- enqueued_at, started_at, completed_at, resolution, created_at, updated_at,
749
- task_kind, priority, retry_count, max_retries, last_error, result_ref
750
- FROM evolution_tasks
751
- ${whereClause}
752
- ORDER BY created_at DESC
753
- LIMIT ? OFFSET ?
754
- `).all(...values, limit, offset);
755
- return rows.map((row) => ({
756
- id: Number(row.id),
757
- taskId: String(row.task_id),
758
- traceId: String(row.trace_id),
759
- source: String(row.source),
760
- reason: row.reason ? String(row.reason) : null,
761
- score: Number(row.score ?? 0),
762
- status: String(row.status),
763
- enqueuedAt: row.enqueued_at ? String(row.enqueued_at) : null,
764
- startedAt: row.started_at ? String(row.started_at) : null,
765
- completedAt: row.completed_at ? String(row.completed_at) : null,
766
- resolution: row.resolution ? String(row.resolution) : null,
767
- createdAt: String(row.created_at),
768
- updatedAt: String(row.updated_at),
769
- taskKind: row.task_kind ? row.task_kind : null,
770
- priority: row.priority ? row.priority : null,
771
- retryCount: row.retry_count != null ? Number(row.retry_count) : null,
772
- maxRetries: row.max_retries != null ? Number(row.max_retries) : null,
773
- lastError: row.last_error ? String(row.last_error) : null,
774
- resultRef: row.result_ref ? String(row.result_ref) : null
775
- }));
776
- }
777
- /**
778
- * List evolution events for a trace or globally.
779
- *
780
- * Returns: Analytics data aggregated from trajectory database.
781
- * Not: Runtime truth or real-time queue state.
782
- */
783
- listEvolutionEvents(traceId, filters = {}) {
784
- const limit = filters.limit ?? 100;
785
- const offset = filters.offset ?? 0;
786
- let rows;
787
- if (traceId) {
788
- rows = this.db.prepare(`
789
- SELECT id, trace_id, task_id, stage, level, message, summary, metadata_json, created_at
790
- FROM evolution_events
791
- WHERE trace_id = ?
792
- ORDER BY created_at ASC
793
- LIMIT ? OFFSET ?
794
- `).all(traceId, limit, offset);
795
- } else {
796
- rows = this.db.prepare(`
797
- SELECT id, trace_id, task_id, stage, level, message, summary, metadata_json, created_at
798
- FROM evolution_events
799
- ORDER BY created_at DESC
800
- LIMIT ? OFFSET ?
801
- `).all(limit, offset);
802
- }
803
- return rows.map((row) => ({
804
- id: Number(row.id),
805
- traceId: String(row.trace_id),
806
- taskId: row.task_id ? String(row.task_id) : null,
807
- stage: String(row.stage),
808
- level: String(row.level ?? "info"),
809
- message: String(row.message),
810
- summary: row.summary ? String(row.summary) : null,
811
- metadata: JSON.parse(String(row.metadata_json ?? "{}")),
812
- createdAt: String(row.created_at)
813
- }));
814
- }
815
- /**
816
- * Get evolution task by trace ID.
817
- *
818
- * Returns: Analytics data aggregated from trajectory database.
819
- * Not: Runtime truth or real-time queue state.
820
- */
821
- getEvolutionTaskByTraceId(traceId) {
822
- const row = this.db.prepare(`
823
- SELECT id, task_id, trace_id, source, reason, score, status,
824
- enqueued_at, started_at, completed_at, resolution, created_at, updated_at,
825
- task_kind, priority, retry_count, max_retries, last_error, result_ref
826
- FROM evolution_tasks
827
- WHERE trace_id = ?
828
- LIMIT 1
829
- `).get(traceId);
830
- if (!row) return null;
831
- return {
832
- id: Number(row.id),
833
- taskId: String(row.task_id),
834
- traceId: String(row.trace_id),
835
- source: String(row.source),
836
- reason: row.reason ? String(row.reason) : null,
837
- score: Number(row.score ?? 0),
838
- status: String(row.status),
839
- enqueuedAt: row.enqueued_at ? String(row.enqueued_at) : null,
840
- startedAt: row.started_at ? String(row.started_at) : null,
841
- completedAt: row.completed_at ? String(row.completed_at) : null,
842
- resolution: row.resolution ? String(row.resolution) : null,
843
- createdAt: String(row.created_at),
844
- updatedAt: String(row.updated_at),
845
- taskKind: row.task_kind ? row.task_kind : null,
846
- priority: row.priority ? row.priority : null,
847
- retryCount: row.retry_count != null ? Number(row.retry_count) : null,
848
- maxRetries: row.max_retries != null ? Number(row.max_retries) : null,
849
- lastError: row.last_error ? String(row.last_error) : null,
850
- resultRef: row.result_ref ? String(row.result_ref) : null
851
- };
852
- }
853
- /**
854
- * Get evolution task statistics.
855
- *
856
- * Returns: Analytics data aggregated from trajectory database.
857
- * Not: Runtime truth or real-time queue state.
858
- */
859
- getEvolutionStats() {
860
- const rows = this.db.prepare(`
861
- SELECT status, COUNT(*) as count FROM evolution_tasks GROUP BY status
862
- `).all();
863
- const stats = { total: 0, pending: 0, inProgress: 0, completed: 0, failed: 0 };
864
- for (const row of rows) {
865
- stats.total += row.count;
866
- if (row.status === "pending") stats.pending = row.count;
867
- else if (row.status === "in_progress") stats.inProgress = row.count;
868
- else if (row.status === "completed") stats.completed = row.count;
869
- else if (row.status === "failed") stats.failed = row.count;
870
- }
871
- return stats;
872
- }
873
- /**
874
- * List recent sessions from the trajectory database.
875
- *
876
- * Returns: Recent session records ordered by most recently updated.
877
- *
878
- * @param options.limit - Maximum number of sessions to return (default: 20)
879
- * @param options.dateFrom - Only return sessions updated after this date
880
- * @param options.dateTo - Only return sessions updated before this date
881
- */
882
- listRecentSessions(options = {}) {
883
- const conditions = [];
884
- const values = [];
885
- if (options.dateFrom) {
886
- conditions.push("updated_at >= ?");
887
- values.push(options.dateFrom);
888
- }
889
- if (options.dateTo) {
890
- conditions.push("updated_at <= ?");
891
- values.push(options.dateTo);
892
- }
893
- const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
894
- const limit = options.limit ?? 20;
895
- const rows = this.db.prepare(`
896
- SELECT session_id, started_at, updated_at
897
- FROM sessions
898
- ${whereClause}
899
- ORDER BY updated_at DESC
900
- LIMIT ?
901
- `).all(...values, limit);
902
- return rows.map((row) => ({
903
- sessionId: String(row.session_id),
904
- startedAt: String(row.started_at),
905
- updatedAt: String(row.updated_at)
906
- }));
907
- }
908
- /**
909
- * List assistant turns for a session.
910
- *
911
- * Returns: Analytics data aggregated from trajectory database.
912
- * Not: Runtime truth or real-time queue state.
913
- */
914
- listAssistantTurns(sessionId) {
915
- const rows = this.db.prepare(`
916
- SELECT id, session_id, run_id, provider, model, raw_text, sanitized_text, blob_ref, created_at
917
- FROM assistant_turns
918
- WHERE session_id = ?
919
- ORDER BY id ASC
920
- `).all(sessionId);
921
- return rows.map((row) => ({
922
- id: Number(row.id),
923
- sessionId: String(row.session_id),
924
- runId: String(row.run_id),
925
- provider: String(row.provider),
926
- model: String(row.model),
927
- rawText: this.restoreRawText(row.raw_text, row.blob_ref),
928
- sanitizedText: String(row.sanitized_text ?? ""),
929
- blobRef: row.blob_ref ? String(row.blob_ref) : null,
930
- createdAt: String(row.created_at)
931
- }));
932
- }
933
- /**
934
- * List tool calls for a session.
935
- *
936
- * Returns: Analytics data aggregated from trajectory database.
937
- * Not: Runtime truth or real-time queue state.
938
- */
939
- listToolCallsForSession(sessionId) {
940
- const rows = this.db.prepare(`
941
- SELECT id, tool_name, outcome, params_json, duration_ms, exit_code, error_type, error_message,
942
- gfi_before, gfi_after, created_at
943
- FROM tool_calls
944
- WHERE session_id = ?
945
- ORDER BY id ASC
946
- `).all(sessionId);
947
- return rows.map((row) => {
948
- let filePath = null;
949
- if (row.params_json && typeof row.params_json === "string") {
950
- try {
951
- const params = JSON.parse(row.params_json);
952
- if (params && typeof params.filePath === "string") {
953
- filePath = params.filePath;
954
- }
955
- } catch {
956
- }
957
- }
958
- return {
959
- id: Number(row.id),
960
- toolName: String(row.tool_name),
961
- outcome: String(row.outcome),
962
- filePath,
963
- durationMs: row.duration_ms != null ? Number(row.duration_ms) : null,
964
- exitCode: row.exit_code != null ? Number(row.exit_code) : null,
965
- errorType: row.error_type ? String(row.error_type) : null,
966
- errorMessage: row.error_message ? String(row.error_message) : null,
967
- gfiBefore: row.gfi_before != null ? Number(row.gfi_before) : null,
968
- gfiAfter: row.gfi_after != null ? Number(row.gfi_after) : null,
969
- createdAt: String(row.created_at)
970
- };
971
- });
972
- }
973
- /**
974
- * List pain events for a session.
975
- *
976
- * Returns: Analytics data aggregated from trajectory database.
977
- * Not: Runtime truth or real-time queue state.
978
- */
979
- listPainEventsForSession(sessionId) {
980
- const rows = this.db.prepare(`
981
- SELECT id, source, score, reason, severity, origin, confidence, created_at
982
- FROM pain_events
983
- WHERE session_id = ?
984
- ORDER BY created_at ASC
985
- `).all(sessionId);
986
- return rows.map((row) => ({
987
- id: Number(row.id),
988
- source: String(row.source),
989
- score: Number(row.score),
990
- reason: row.reason ? String(row.reason) : null,
991
- severity: row.severity ? String(row.severity) : null,
992
- origin: row.origin ? String(row.origin) : null,
993
- confidence: row.confidence != null ? Number(row.confidence) : null,
994
- createdAt: String(row.created_at)
995
- }));
996
- }
997
- /**
998
- * List user turns for a session.
999
- * Returns sanitized/reduced fields for nocturnal use — NO raw text.
1000
- */
1001
- listUserTurnsForSession(sessionId) {
1002
- const rows = this.db.prepare(`
1003
- SELECT id, turn_index, raw_excerpt, correction_detected, correction_cue, created_at
1004
- FROM user_turns
1005
- WHERE session_id = ?
1006
- ORDER BY turn_index ASC
1007
- `).all(sessionId);
1008
- return rows.map((row) => ({
1009
- id: Number(row.id),
1010
- turnIndex: Number(row.turn_index),
1011
- rawExcerpt: String(row.raw_excerpt ?? ""),
1012
- correctionDetected: Boolean(row.correction_detected),
1013
- correctionCue: row.correction_cue ? String(row.correction_cue) : null,
1014
- createdAt: String(row.created_at)
1015
- }));
1016
- }
1017
- /**
1018
- * List correction samples with optional review status filter.
1019
- *
1020
- * Returns: Analytics data aggregated from trajectory database.
1021
- * Not: Runtime truth or real-time queue state.
1022
- */
1023
- listCorrectionSamples(status = "pending") {
1024
- const rows = this.db.prepare(`
1025
- SELECT sample_id, session_id, bad_assistant_turn_id, user_correction_turn_id,
1026
- recovery_tool_span_json, diff_excerpt, principle_ids_json, quality_score,
1027
- review_status, export_mode, created_at, updated_at
1028
- FROM correction_samples
1029
- WHERE review_status = ?
1030
- ORDER BY created_at DESC
1031
- `).all(status);
1032
- return rows.map((row) => ({
1033
- sampleId: String(row.sample_id),
1034
- sessionId: String(row.session_id),
1035
- badAssistantTurnId: Number(row.bad_assistant_turn_id),
1036
- userCorrectionTurnId: Number(row.user_correction_turn_id),
1037
- recoveryToolSpanJson: String(row.recovery_tool_span_json),
1038
- diffExcerpt: String(row.diff_excerpt ?? ""),
1039
- principleIdsJson: String(row.principle_ids_json ?? "[]"),
1040
- qualityScore: Number(row.quality_score),
1041
- reviewStatus: row.review_status,
1042
- exportMode: row.export_mode,
1043
- createdAt: String(row.created_at),
1044
- updatedAt: String(row.updated_at)
1045
- }));
1046
- }
1047
- /**
1048
- * List gate blocks for a session.
1049
- * Returns minimal fields for nocturnal use — no raw text.
1050
- */
1051
- listGateBlocksForSession(sessionId) {
1052
- const rows = this.db.prepare(`
1053
- SELECT id, tool_name, file_path, reason, plan_status, created_at
1054
- FROM gate_blocks
1055
- WHERE session_id = ?
1056
- ORDER BY id ASC
1057
- `).all(sessionId);
1058
- return rows.map((row) => ({
1059
- id: Number(row.id),
1060
- toolName: String(row.tool_name),
1061
- filePath: row.file_path ? String(row.file_path) : null,
1062
- reason: String(row.reason),
1063
- planStatus: row.plan_status ? String(row.plan_status) : null,
1064
- createdAt: String(row.created_at)
1065
- }));
1066
- }
1067
- /**
1068
- * List correction samples for a specific session.
1069
- * Returns minimal fields for nocturnal use — correction cue only.
1070
- * #268: Wire correction_samples into nocturnal pipeline.
1071
- */
1072
- listCorrectionSamplesForSession(sessionId) {
1073
- const rows = this.db.prepare(`
1074
- SELECT cs.sample_id, ut.correction_cue
1075
- FROM correction_samples cs
1076
- LEFT JOIN user_turns ut ON ut.id = cs.user_correction_turn_id
1077
- WHERE cs.session_id = ?
1078
- ORDER BY cs.created_at DESC
1079
- `).all(sessionId);
1080
- return rows.map((row) => ({
1081
- correctionCue: row.correction_cue ? String(row.correction_cue) : null
1082
- }));
1083
- }
1084
- reviewCorrectionSample(sampleId, status, note) {
1085
- const updatedAt = nowIso();
1086
- const updated = this.withWrite(() => {
1087
- const updateResult = this.db.prepare(`
1088
- UPDATE correction_samples
1089
- SET review_status = ?, updated_at = ?
1090
- WHERE sample_id = ?
1091
- `).run(status, updatedAt, sampleId);
1092
- if (updateResult.changes === 0) {
1093
- return false;
1094
- }
1095
- this.db.prepare(`
1096
- INSERT INTO sample_reviews (sample_id, review_status, note, created_at)
1097
- VALUES (?, ?, ?, ?)
1098
- `).run(sampleId, status, note ?? null, updatedAt);
1099
- return true;
1100
- });
1101
- if (!updated) {
1102
- throw new SampleNotFoundError(sampleId);
1103
- }
1104
- const record = this.db.prepare(`
1105
- SELECT sample_id, session_id, bad_assistant_turn_id, user_correction_turn_id,
1106
- recovery_tool_span_json, diff_excerpt, principle_ids_json, quality_score,
1107
- review_status, export_mode, created_at, updated_at
1108
- FROM correction_samples
1109
- WHERE sample_id = ?
1110
- `).get(sampleId);
1111
- if (!record) {
1112
- throw new SampleNotFoundError(`${sampleId} (after update)`);
1113
- }
1114
- if (status === "rejected") {
1115
- this.recordCorrectionRejectedPain({
1116
- session_id: record.session_id,
1117
- quality_score: record.quality_score,
1118
- diff_excerpt: record.diff_excerpt,
1119
- principle_ids_json: record.principle_ids_json,
1120
- created_at: record.created_at
1121
- });
1122
- }
1123
- return {
1124
- sampleId: String(record.sample_id),
1125
- sessionId: String(record.session_id),
1126
- badAssistantTurnId: Number(record.bad_assistant_turn_id),
1127
- userCorrectionTurnId: Number(record.user_correction_turn_id),
1128
- recoveryToolSpanJson: String(record.recovery_tool_span_json),
1129
- diffExcerpt: String(record.diff_excerpt ?? ""),
1130
- principleIdsJson: String(record.principle_ids_json ?? "[]"),
1131
- qualityScore: Number(record.quality_score),
1132
- reviewStatus: record.review_status,
1133
- exportMode: record.export_mode,
1134
- createdAt: String(record.created_at),
1135
- updatedAt: String(record.updated_at)
1136
- };
1137
- }
1138
- /**
1139
- * When a correction sample is rejected, emit a pain event to the trajectory.
1140
- * This feeds rejected corrections into the nocturnal pipeline as a high-fidelity
1141
- * violation signal (human-verified, unlike heuristic pain detection).
1142
- */
1143
- recordCorrectionRejectedPain(record) {
1144
- const sessionId = String(record.session_id);
1145
- const qualityScore = Number(record.quality_score);
1146
- const diffExcerpt = String(record.diff_excerpt ?? "");
1147
- const principleIds = String(record.principle_ids_json ?? "[]");
1148
- const painScore = Math.max(0, Math.min(100, Math.round(Number(qualityScore) || 0)));
1149
- const reason = `Correction rejected (quality ${qualityScore.toFixed(2)}). Principles: ${principleIds}${diffExcerpt ? ` \u2014 ${diffExcerpt.slice(0, 120)}` : ""}`;
1150
- try {
1151
- this.recordPainEvent({
1152
- sessionId,
1153
- source: "correction_rejected",
1154
- score: painScore,
1155
- reason,
1156
- severity: painScore >= 70 ? "severe" : painScore >= 40 ? "moderate" : "mild",
1157
- origin: "system_infer",
1158
- text: diffExcerpt || void 0,
1159
- createdAt: String(record.created_at)
1160
- });
1161
- } catch (err) {
1162
- console.warn(`[Trajectory] Failed to record correction_rejected pain event: ${String(err)}`);
1163
- }
1164
- }
1165
- /**
1166
- * Export correction samples to JSONL file.
1167
- *
1168
- * Returns: Analytics data aggregated from trajectory database.
1169
- * Not: Runtime truth or real-time queue state.
1170
- */
1171
- exportCorrections(opts) {
1172
- const rows = this.db.prepare(`
1173
- SELECT cs.sample_id, cs.session_id, cs.recovery_tool_span_json, cs.diff_excerpt, cs.quality_score,
1174
- at.raw_text AS assistant_raw_text, at.blob_ref AS assistant_blob_ref, at.sanitized_text,
1175
- ut.raw_text AS user_raw_text, ut.blob_ref AS user_blob_ref, ut.correction_cue
1176
- FROM correction_samples cs
1177
- JOIN assistant_turns at ON at.id = cs.bad_assistant_turn_id
1178
- JOIN user_turns ut ON ut.id = cs.user_correction_turn_id
1179
- WHERE (? = 0 OR cs.review_status = 'approved')
1180
- ORDER BY cs.created_at ASC
1181
- `).all(opts.approvedOnly ? 1 : 0);
1182
- const exportPath = path3.join(this.exportDir, `corrections-${Date.now()}-${opts.mode}.jsonl`);
1183
- const lines = rows.map((row) => {
1184
- const assistantRaw = this.restoreRawText(row.assistant_raw_text, row.assistant_blob_ref);
1185
- const userRaw = this.restoreRawText(row.user_raw_text, row.user_blob_ref);
1186
- const assistantText = opts.mode === "redacted" ? redactText(assistantRaw) : assistantRaw;
1187
- const userText = opts.mode === "redacted" ? redactText(userRaw) : userRaw;
1188
- return JSON.stringify({
1189
- sample_id: row.sample_id,
1190
- session_id: row.session_id,
1191
- instruction: userText,
1192
- input_context: assistantText,
1193
- bad_attempt_summary: String(row.diff_excerpt ?? ""),
1194
- preferred_response: userText,
1195
- labels: {
1196
- correction_cue: row.correction_cue,
1197
- quality_score: row.quality_score
1198
- },
1199
- metadata: {
1200
- mode: opts.mode,
1201
- recovery_tool_span_json: row.recovery_tool_span_json
1202
- }
1203
- });
1204
- });
1205
- atomicWriteFileSync(exportPath, `${lines.join("\n")}${lines.length > 0 ? "\n" : ""}`);
1206
- this.recordExportAudit("corrections", opts.mode, opts.approvedOnly, exportPath, rows.length);
1207
- return { filePath: exportPath, count: rows.length, mode: opts.mode };
1208
- }
1209
- /**
1210
- * Export analytics data to JSON file.
1211
- *
1212
- * Returns: Analytics data aggregated from trajectory database.
1213
- * Not: Runtime truth or real-time queue state.
1214
- */
1215
- exportAnalytics() {
1216
- const payload = {
1217
- generatedAt: nowIso(),
1218
- stats: this.getDataStats(),
1219
- dailyMetrics: this.dailyMetrics(),
1220
- errorClusters: this.db.prepare("SELECT * FROM v_error_clusters").all(),
1221
- principleEffectiveness: this.db.prepare("SELECT * FROM v_principle_effectiveness").all(),
1222
- sampleQueue: this.db.prepare("SELECT * FROM v_sample_queue").all()
1223
- };
1224
- const exportPath = path3.join(this.exportDir, `analytics-${Date.now()}.json`);
1225
- atomicWriteFileSync(exportPath, JSON.stringify(payload, null, 2));
1226
- this.recordExportAudit("analytics", "raw", true, exportPath, Array.isArray(payload.dailyMetrics) ? payload.dailyMetrics.length : 0);
1227
- return { filePath: exportPath, count: Array.isArray(payload.dailyMetrics) ? payload.dailyMetrics.length : 0 };
1228
- }
1229
- /**
1230
- * Get trajectory database statistics.
1231
- *
1232
- * Returns: Analytics data aggregated from trajectory database.
1233
- * Not: Runtime truth or real-time queue state.
1234
- */
1235
- getDataStats() {
1236
- const getCount = (table, where) => {
1237
- const sql = where ? `SELECT COUNT(*) as count FROM ${table} WHERE ${where}` : `SELECT COUNT(*) as count FROM ${table}`;
1238
- return Number(this.db.prepare(sql).get().count);
1239
- };
1240
- const lastIngest = this.db.prepare(`
1241
- SELECT MAX(ts) AS ts FROM (
1242
- SELECT MAX(created_at) AS ts FROM assistant_turns
1243
- UNION ALL SELECT MAX(created_at) AS ts FROM user_turns
1244
- UNION ALL SELECT MAX(created_at) AS ts FROM tool_calls
1245
- UNION ALL SELECT MAX(created_at) AS ts FROM pain_events
1246
- )
1247
- `).get();
1248
- return {
1249
- dbPath: this.dbPath,
1250
- dbSizeBytes: fileSizeIfExists(this.dbPath),
1251
- assistantTurns: getCount("assistant_turns"),
1252
- userTurns: getCount("user_turns"),
1253
- toolCalls: getCount("tool_calls"),
1254
- painEvents: getCount("pain_events"),
1255
- pendingSamples: getCount("correction_samples", `review_status = 'pending'`),
1256
- approvedSamples: getCount("correction_samples", `review_status = 'approved'`),
1257
- blobBytes: this.computeBlobBytes(),
1258
- lastIngestAt: lastIngest.ts ?? null
1259
- };
1260
- }
1261
- cleanupBlobStorage() {
1262
- return this.pruneUnreferencedBlobs();
1263
- }
1264
- initSchema() {
1265
- this.db.exec(`
1266
- CREATE TABLE IF NOT EXISTS schema_version (version INTEGER NOT NULL);
1267
- CREATE TABLE IF NOT EXISTS ingest_checkpoint (
1268
- source_key TEXT PRIMARY KEY,
1269
- imported_at TEXT NOT NULL
1270
- );
1271
- CREATE TABLE IF NOT EXISTS sessions (
1272
- session_id TEXT PRIMARY KEY,
1273
- started_at TEXT NOT NULL,
1274
- updated_at TEXT NOT NULL
1275
- );
1276
- CREATE TABLE IF NOT EXISTS assistant_turns (
1277
- id INTEGER PRIMARY KEY AUTOINCREMENT,
1278
- session_id TEXT NOT NULL,
1279
- run_id TEXT NOT NULL,
1280
- provider TEXT NOT NULL,
1281
- model TEXT NOT NULL,
1282
- raw_text TEXT,
1283
- sanitized_text TEXT NOT NULL,
1284
- usage_json TEXT NOT NULL,
1285
- empathy_signal_json TEXT NOT NULL,
1286
- blob_ref TEXT,
1287
- raw_excerpt TEXT,
1288
- created_at TEXT NOT NULL
1289
- );
1290
- CREATE TABLE IF NOT EXISTS user_turns (
1291
- id INTEGER PRIMARY KEY AUTOINCREMENT,
1292
- session_id TEXT NOT NULL,
1293
- turn_index INTEGER NOT NULL,
1294
- raw_text TEXT,
1295
- blob_ref TEXT,
1296
- raw_excerpt TEXT,
1297
- correction_detected INTEGER NOT NULL DEFAULT 0,
1298
- correction_cue TEXT,
1299
- references_assistant_turn_id INTEGER,
1300
- created_at TEXT NOT NULL
1301
- );
1302
- CREATE TABLE IF NOT EXISTS tool_calls (
1303
- id INTEGER PRIMARY KEY AUTOINCREMENT,
1304
- session_id TEXT NOT NULL,
1305
- tool_name TEXT NOT NULL,
1306
- outcome TEXT NOT NULL,
1307
- duration_ms INTEGER,
1308
- exit_code INTEGER,
1309
- error_type TEXT,
1310
- error_message TEXT,
1311
- gfi_before REAL,
1312
- gfi_after REAL,
1313
- params_json TEXT NOT NULL,
1314
- created_at TEXT NOT NULL
1315
- );
1316
- CREATE TABLE IF NOT EXISTS pain_events (
1317
- id INTEGER PRIMARY KEY AUTOINCREMENT,
1318
- session_id TEXT NOT NULL,
1319
- source TEXT NOT NULL,
1320
- score REAL NOT NULL,
1321
- reason TEXT,
1322
- severity TEXT,
1323
- origin TEXT,
1324
- confidence REAL,
1325
- text TEXT,
1326
- created_at TEXT NOT NULL
1327
- );
1328
- CREATE TABLE IF NOT EXISTS gate_blocks (
1329
- id INTEGER PRIMARY KEY AUTOINCREMENT,
1330
- session_id TEXT,
1331
- tool_name TEXT NOT NULL,
1332
- file_path TEXT,
1333
- reason TEXT NOT NULL,
1334
- plan_status TEXT,
1335
- created_at TEXT NOT NULL
1336
- );
1337
- CREATE TABLE IF NOT EXISTS trust_changes (
1338
- id INTEGER PRIMARY KEY AUTOINCREMENT,
1339
- session_id TEXT,
1340
- previous_score REAL NOT NULL,
1341
- new_score REAL NOT NULL,
1342
- delta REAL NOT NULL,
1343
- reason TEXT NOT NULL,
1344
- created_at TEXT NOT NULL
1345
- );
1346
- CREATE TABLE IF NOT EXISTS principle_events (
1347
- id INTEGER PRIMARY KEY AUTOINCREMENT,
1348
- principle_id TEXT,
1349
- event_type TEXT NOT NULL,
1350
- payload_json TEXT NOT NULL,
1351
- created_at TEXT NOT NULL
1352
- );
1353
- CREATE TABLE IF NOT EXISTS task_outcomes (
1354
- id INTEGER PRIMARY KEY AUTOINCREMENT,
1355
- session_id TEXT NOT NULL,
1356
- task_id TEXT,
1357
- outcome TEXT NOT NULL,
1358
- summary TEXT,
1359
- principle_ids_json TEXT NOT NULL,
1360
- created_at TEXT NOT NULL
1361
- );
1362
- CREATE TABLE IF NOT EXISTS correction_samples (
1363
- sample_id TEXT PRIMARY KEY,
1364
- session_id TEXT NOT NULL,
1365
- bad_assistant_turn_id INTEGER NOT NULL,
1366
- user_correction_turn_id INTEGER NOT NULL,
1367
- recovery_tool_span_json TEXT NOT NULL,
1368
- diff_excerpt TEXT NOT NULL,
1369
- principle_ids_json TEXT NOT NULL,
1370
- quality_score REAL NOT NULL,
1371
- review_status TEXT NOT NULL,
1372
- export_mode TEXT NOT NULL,
1373
- created_at TEXT NOT NULL,
1374
- updated_at TEXT NOT NULL
1375
- );
1376
- CREATE TABLE IF NOT EXISTS sample_reviews (
1377
- id INTEGER PRIMARY KEY AUTOINCREMENT,
1378
- sample_id TEXT NOT NULL,
1379
- review_status TEXT NOT NULL,
1380
- note TEXT,
1381
- created_at TEXT NOT NULL
1382
- );
1383
- CREATE TABLE IF NOT EXISTS exports_audit (
1384
- id INTEGER PRIMARY KEY AUTOINCREMENT,
1385
- export_kind TEXT NOT NULL,
1386
- mode TEXT NOT NULL,
1387
- approved_only INTEGER NOT NULL,
1388
- file_path TEXT NOT NULL,
1389
- row_count INTEGER NOT NULL,
1390
- created_at TEXT NOT NULL
1391
- );
1392
- CREATE TABLE IF NOT EXISTS evolution_tasks (
1393
- id INTEGER PRIMARY KEY AUTOINCREMENT,
1394
- task_id TEXT UNIQUE NOT NULL,
1395
- trace_id TEXT NOT NULL,
1396
- source TEXT NOT NULL,
1397
- reason TEXT,
1398
- score INTEGER DEFAULT 0,
1399
- status TEXT DEFAULT 'pending',
1400
- enqueued_at TEXT,
1401
- started_at TEXT,
1402
- completed_at TEXT,
1403
- resolution TEXT,
1404
- created_at TEXT NOT NULL,
1405
- updated_at TEXT NOT NULL
1406
- );
1407
- CREATE TABLE IF NOT EXISTS evolution_events (
1408
- id INTEGER PRIMARY KEY AUTOINCREMENT,
1409
- trace_id TEXT NOT NULL,
1410
- task_id TEXT,
1411
- stage TEXT NOT NULL,
1412
- level TEXT DEFAULT 'info',
1413
- message TEXT NOT NULL,
1414
- summary TEXT,
1415
- metadata_json TEXT,
1416
- created_at TEXT NOT NULL
1417
- );
1418
- `);
1419
- try {
1420
- this.db.exec(`ALTER TABLE pain_events ADD COLUMN text TEXT`);
1421
- } catch (err) {
1422
- const message = err instanceof Error ? err.message : String(err);
1423
- if (!message.includes("duplicate column name") && !message.includes("no column named")) {
1424
- throw err;
1425
- }
1426
- }
1427
- this.db.exec(`
1428
- CREATE VIRTUAL TABLE IF NOT EXISTS pain_events_fts USING fts5(
1429
- text,
1430
- pain_event_id UNINDEXED,
1431
- tokenize='porter unicode61'
1432
- )
1433
- `);
1434
- const v2Columns = [
1435
- { name: "task_kind", type: "TEXT" },
1436
- { name: "priority", type: "TEXT" },
1437
- { name: "retry_count", type: "INTEGER" },
1438
- { name: "max_retries", type: "INTEGER" },
1439
- { name: "last_error", type: "TEXT" },
1440
- { name: "result_ref", type: "TEXT" }
1441
- ];
1442
- for (const col of v2Columns) {
1443
- const exists = this.db.prepare(`PRAGMA table_info(evolution_tasks)`).all().some((row2) => row2.name === col.name);
1444
- if (!exists) {
1445
- this.db.exec(`ALTER TABLE evolution_tasks ADD COLUMN ${col.name} ${col.type}`);
1446
- }
1447
- }
1448
- this.db.exec(`
1449
- CREATE VIEW IF NOT EXISTS v_error_clusters AS
1450
- SELECT tool_name, COALESCE(error_type, 'unknown') AS error_type, COUNT(*) AS occurrences
1451
- FROM tool_calls
1452
- WHERE outcome = 'failure'
1453
- GROUP BY tool_name, COALESCE(error_type, 'unknown')
1454
- ORDER BY occurrences DESC;
1455
- CREATE VIEW IF NOT EXISTS v_principle_effectiveness AS
1456
- SELECT event_type, COUNT(*) AS total
1457
- FROM principle_events
1458
- GROUP BY event_type
1459
- ORDER BY total DESC;
1460
- CREATE VIEW IF NOT EXISTS v_sample_queue AS
1461
- SELECT review_status, COUNT(*) AS total
1462
- FROM correction_samples
1463
- GROUP BY review_status;
1464
- CREATE INDEX IF NOT EXISTS idx_assistant_turns_session_id ON assistant_turns(session_id);
1465
- CREATE INDEX IF NOT EXISTS idx_assistant_turns_created_at ON assistant_turns(created_at);
1466
- CREATE INDEX IF NOT EXISTS idx_assistant_turns_provider_model ON assistant_turns(provider, model);
1467
- CREATE INDEX IF NOT EXISTS idx_user_turns_session_id ON user_turns(session_id);
1468
- CREATE INDEX IF NOT EXISTS idx_tool_calls_session_id ON tool_calls(session_id);
1469
- CREATE INDEX IF NOT EXISTS idx_tool_calls_created_at ON tool_calls(created_at);
1470
- CREATE INDEX IF NOT EXISTS idx_pain_events_session_id ON pain_events(session_id);
1471
- CREATE INDEX IF NOT EXISTS idx_correction_samples_review_status ON correction_samples(review_status);
1472
- CREATE INDEX IF NOT EXISTS idx_evolution_tasks_trace_id ON evolution_tasks(trace_id);
1473
- CREATE INDEX IF NOT EXISTS idx_evolution_tasks_status ON evolution_tasks(status);
1474
- CREATE INDEX IF NOT EXISTS idx_evolution_tasks_created_at ON evolution_tasks(created_at);
1475
- CREATE INDEX IF NOT EXISTS idx_evolution_events_trace_id ON evolution_events(trace_id);
1476
- CREATE INDEX IF NOT EXISTS idx_evolution_events_created_at ON evolution_events(created_at);
1477
- `);
1478
- const row = this.db.prepare("SELECT version FROM schema_version LIMIT 1").get();
1479
- this.migrateSchema(row?.version);
1480
- if (!row) {
1481
- this.db.prepare("INSERT INTO schema_version (version) VALUES (?)").run(SCHEMA_VERSION);
1482
- } else if (row.version !== SCHEMA_VERSION) {
1483
- this.db.prepare("UPDATE schema_version SET version = ?").run(SCHEMA_VERSION);
1484
- }
1485
- }
1486
- importLegacyArtifacts() {
1487
- this.importLegacySessions();
1488
- this.importLegacyEvents();
1489
- this.importLegacyEvolution();
1490
- }
1491
- migrateSchema(_fromVersion) {
1492
- this.db.exec(`
1493
- DROP VIEW IF EXISTS v_daily_metrics;
1494
- CREATE VIEW IF NOT EXISTS v_daily_metrics AS
1495
- WITH tool_daily AS (
1496
- SELECT
1497
- substr(created_at, 1, 10) AS day,
1498
- COUNT(*) AS tool_calls,
1499
- SUM(CASE WHEN outcome = 'failure' THEN 1 ELSE 0 END) AS failures
1500
- FROM tool_calls
1501
- GROUP BY substr(created_at, 1, 10)
1502
- ),
1503
- correction_daily AS (
1504
- SELECT
1505
- substr(created_at, 1, 10) AS day,
1506
- SUM(CASE WHEN correction_detected = 1 THEN 1 ELSE 0 END) AS user_corrections
1507
- FROM user_turns
1508
- GROUP BY substr(created_at, 1, 10)
1509
- )
1510
- SELECT
1511
- tool_daily.day AS day,
1512
- tool_daily.tool_calls AS tool_calls,
1513
- tool_daily.failures AS failures,
1514
- COALESCE(correction_daily.user_corrections, 0) AS user_corrections
1515
- FROM tool_daily
1516
- LEFT JOIN correction_daily ON correction_daily.day = tool_daily.day;
1517
- `);
1518
- }
1519
- /**
1520
- * Get daily metrics for analytics.
1521
- *
1522
- * Returns: Analytics data aggregated from trajectory database.
1523
- * Not: Runtime truth or real-time queue state.
1524
- */
1525
- dailyMetrics() {
1526
- return this.db.prepare("SELECT * FROM v_daily_metrics ORDER BY day ASC").all();
1527
- }
1528
- importLegacySessions() {
1529
- const key = "legacy:sessions";
1530
- if (this.isImported(key)) return;
1531
- const sessionDir = resolvePdPath(this.workspaceDir, "SESSION_DIR");
1532
- if (!fs3.existsSync(sessionDir)) return;
1533
- for (const file of fs3.readdirSync(sessionDir).filter((entry) => entry.endsWith(".json"))) {
1534
- try {
1535
- const content = JSON.parse(fs3.readFileSync(path3.join(sessionDir, file), "utf8"));
1536
- if (content.sessionId) {
1537
- const startedAt = typeof content.lastActivityAt === "number" ? new Date(content.lastActivityAt).toISOString() : nowIso();
1538
- this.recordSession({ sessionId: content.sessionId, startedAt });
1539
- }
1540
- } catch {
1541
- }
1542
- }
1543
- this.markImported(key);
1544
- }
1545
- importLegacyEvents() {
1546
- const key = "legacy:events";
1547
- if (this.isImported(key)) return;
1548
- const eventsPath = path3.join(this.stateDir, "logs", "events.jsonl");
1549
- if (!fs3.existsSync(eventsPath)) return;
1550
- const raw = fs3.readFileSync(eventsPath, "utf8").trim();
1551
- if (!raw) {
1552
- this.markImported(key);
1553
- return;
1554
- }
1555
- for (const line of raw.split("\n")) {
1556
- try {
1557
- const event = JSON.parse(line);
1558
- if (event.type === "pain_signal" && event.sessionId) {
1559
- this.recordPainEvent({
1560
- sessionId: event.sessionId,
1561
- source: String(event.data?.source ?? "legacy"),
1562
- score: Number(event.data?.score ?? 0),
1563
- reason: typeof event.data?.reason === "string" ? event.data.reason : null,
1564
- severity: typeof event.data?.severity === "string" ? event.data.severity : null,
1565
- origin: typeof event.data?.origin === "string" ? event.data.origin : null,
1566
- confidence: typeof event.data?.confidence === "number" ? event.data.confidence : null,
1567
- createdAt: event.ts
1568
- });
1569
- }
1570
- if (event.type === "trust_change") {
1571
- this.recordTrustChange({
1572
- sessionId: event.sessionId,
1573
- previousScore: Number(event.data?.previousScore ?? 0),
1574
- newScore: Number(event.data?.newScore ?? 0),
1575
- delta: Number(event.data?.delta ?? 0),
1576
- reason: String(event.data?.reason ?? "legacy"),
1577
- createdAt: event.ts
1578
- });
1579
- }
1580
- if (event.type === "gate_block") {
1581
- this.recordGateBlock({
1582
- sessionId: event.sessionId,
1583
- toolName: String(event.data?.toolName ?? "unknown"),
1584
- filePath: typeof event.data?.filePath === "string" ? event.data.filePath : null,
1585
- reason: String(event.data?.reason ?? "legacy"),
1586
- planStatus: typeof event.data?.planStatus === "string" ? event.data.planStatus : null,
1587
- createdAt: event.ts
1588
- });
1589
- }
1590
- } catch {
1591
- }
1592
- }
1593
- this.markImported(key);
1594
- }
1595
- importLegacyEvolution() {
1596
- const key = "legacy:evolution";
1597
- if (this.isImported(key)) return;
1598
- const evolutionPath = resolvePdPath(this.workspaceDir, "EVOLUTION_STREAM");
1599
- if (!fs3.existsSync(evolutionPath)) return;
1600
- const raw = fs3.readFileSync(evolutionPath, "utf8").trim();
1601
- if (!raw) {
1602
- this.markImported(key);
1603
- return;
1604
- }
1605
- for (const line of raw.split("\n")) {
1606
- try {
1607
- const event = JSON.parse(line);
1608
- this.recordPrincipleEvent({
1609
- principleId: typeof event.data?.principleId === "string" ? event.data.principleId : null,
1610
- eventType: String(event.type ?? "legacy"),
1611
- payload: event.data ?? {},
1612
- createdAt: event.ts
1613
- });
1614
- } catch {
1615
- }
1616
- }
1617
- this.markImported(key);
1618
- }
1619
- markImported(sourceKey) {
1620
- this.withWrite(() => {
1621
- this.db.prepare(`
1622
- INSERT INTO ingest_checkpoint (source_key, imported_at)
1623
- VALUES (?, ?)
1624
- ON CONFLICT(source_key) DO UPDATE SET imported_at = excluded.imported_at
1625
- `).run(sourceKey, nowIso());
1626
- });
1627
- }
1628
- isImported(sourceKey) {
1629
- const row = this.db.prepare("SELECT source_key FROM ingest_checkpoint WHERE source_key = ?").get(sourceKey);
1630
- return Boolean(row);
1631
- }
1632
- maybeCreateCorrectionSample(sessionId) {
1633
- const pending = this.db.prepare(`
1634
- SELECT sample_id FROM correction_samples
1635
- WHERE session_id = ? AND review_status = 'pending'
1636
- ORDER BY created_at DESC
1637
- LIMIT 1
1638
- `).get(sessionId);
1639
- if (pending?.sample_id) return;
1640
- const correctionTurn = this.db.prepare(`
1641
- SELECT id, references_assistant_turn_id, correction_cue, raw_text, blob_ref
1642
- FROM user_turns
1643
- WHERE session_id = ? AND correction_detected = 1
1644
- ORDER BY id DESC
1645
- LIMIT 1
1646
- `).get(sessionId);
1647
- if (!correctionTurn || !correctionTurn.references_assistant_turn_id) return;
1648
- const failedCall = this.db.prepare(`
1649
- SELECT id, tool_name, error_type, error_message
1650
- FROM tool_calls
1651
- WHERE session_id = ? AND outcome = 'failure'
1652
- ORDER BY id DESC
1653
- LIMIT 1
1654
- `).get(sessionId);
1655
- const recentCalls = this.db.prepare(`
1656
- SELECT id, tool_name, outcome
1657
- FROM tool_calls
1658
- WHERE session_id = ?
1659
- ORDER BY id DESC
1660
- LIMIT 5
1661
- `).all(sessionId);
1662
- const successfulCalls = recentCalls.filter((c) => c.outcome === "success");
1663
- const refForHash = successfulCalls[0]?.id ?? correctionTurn.id;
1664
- const sampleId = `sample_${crypto.createHash("md5").update(`${sessionId}:${correctionTurn.id}:${refForHash}`).digest("hex").slice(0, 12)}`;
1665
- const userRawText = this.restoreRawText(correctionTurn.raw_text, correctionTurn.blob_ref);
1666
- const qualityScore = [
1667
- correctionTurn.references_assistant_turn_id ? 35 : 0,
1668
- correctionTurn.correction_cue ? 20 : 0,
1669
- failedCall ? 20 : 0,
1670
- Math.min(successfulCalls.length, 3) * 5
1671
- ].reduce((sum, value) => sum + value, 0);
1672
- const diffText = userRawText || (failedCall ? String(failedCall.error_message ?? failedCall.error_type ?? failedCall.tool_name) : "") || String(correctionTurn.correction_cue ?? "user correction");
1673
- this.withWrite(() => {
1674
- this.db.prepare(`
1675
- INSERT OR IGNORE INTO correction_samples (
1676
- sample_id, session_id, bad_assistant_turn_id, user_correction_turn_id,
1677
- recovery_tool_span_json, diff_excerpt, principle_ids_json, quality_score,
1678
- review_status, export_mode, created_at, updated_at
1679
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'pending', 'raw', ?, ?)
1680
- `).run(
1681
- sampleId,
1682
- sessionId,
1683
- Number(correctionTurn.references_assistant_turn_id),
1684
- Number(correctionTurn.id),
1685
- safeJson(successfulCalls.map((call) => ({ id: call.id, toolName: call.tool_name, outcome: call.outcome }))),
1686
- summarizeForDiff(diffText),
1687
- "[]",
1688
- qualityScore,
1689
- nowIso(),
1690
- nowIso()
1691
- );
1692
- });
1693
- }
1694
- recordExportAudit(exportKind, mode, approvedOnly, filePath, rowCount) {
1695
- this.withWrite(() => {
1696
- this.db.prepare(`
1697
- INSERT INTO exports_audit (export_kind, mode, approved_only, file_path, row_count, created_at)
1698
- VALUES (?, ?, ?, ?, ?, ?)
1699
- `).run(exportKind, mode, approvedOnly ? 1 : 0, filePath, rowCount, nowIso());
1700
- });
1701
- }
1702
- storeRawText(kind, text) {
1703
- const excerpt = text.length > 200 ? `${text.slice(0, 197)}...` : text;
1704
- const bytes = Buffer.byteLength(text, "utf8");
1705
- if (bytes <= this.blobInlineThresholdBytes) {
1706
- return { inlineText: text, blobRef: null, excerpt };
1707
- }
1708
- const hash = crypto.createHash("sha256").update(text).digest("hex");
1709
- const relativePath = `${kind}-${hash}.txt`;
1710
- const fullPath = path3.join(this.blobDir, relativePath);
1711
- if (!fs3.existsSync(fullPath)) {
1712
- atomicWriteFileSync(fullPath, text);
1713
- }
1714
- return { inlineText: null, blobRef: relativePath, excerpt };
1715
- }
1716
- restoreRawText(inlineText, blobRef) {
1717
- if (inlineText) return inlineText;
1718
- if (!blobRef) return "";
1719
- const fullPath = path3.join(this.blobDir, blobRef);
1720
- return fs3.existsSync(fullPath) ? fs3.readFileSync(fullPath, "utf8") : "";
1721
- }
1722
- computeBlobBytes() {
1723
- if (!fs3.existsSync(this.blobDir)) return 0;
1724
- return fs3.readdirSync(this.blobDir).reduce((sum, file) => sum + fileSizeIfExists(path3.join(this.blobDir, file)), 0);
1725
- }
1726
- pruneUnreferencedBlobs() {
1727
- if (!fs3.existsSync(this.blobDir)) {
1728
- return { removedFiles: 0, reclaimedBytes: 0 };
1729
- }
1730
- const referenced = /* @__PURE__ */ new Set();
1731
- const rows = this.db.prepare(`
1732
- SELECT blob_ref FROM assistant_turns WHERE blob_ref IS NOT NULL
1733
- UNION
1734
- SELECT blob_ref FROM user_turns WHERE blob_ref IS NOT NULL
1735
- `).all();
1736
- for (const row of rows) {
1737
- if (row.blob_ref) referenced.add(String(row.blob_ref));
1738
- }
1739
- const now = Date.now();
1740
- let removedFiles = 0;
1741
- let reclaimedBytes = 0;
1742
- for (const entry of fs3.readdirSync(this.blobDir)) {
1743
- if (referenced.has(entry)) continue;
1744
- const fullPath = path3.join(this.blobDir, entry);
1745
- let stat;
1746
- try {
1747
- stat = fs3.statSync(fullPath);
1748
- } catch {
1749
- continue;
1750
- }
1751
- if (!stat.isFile()) continue;
1752
- if (this.orphanBlobGraceMs > 0 && now - stat.mtimeMs < this.orphanBlobGraceMs) continue;
1753
- reclaimedBytes += stat.size;
1754
- removedFiles += 1;
1755
- fs3.rmSync(fullPath, { force: true });
1756
- }
1757
- return { removedFiles, reclaimedBytes };
1758
- }
1759
- withWrite(fn) {
1760
- return withLock(this.dbPath, fn, { lockSuffix: ".trajectory.lock", lockStaleMs: 3e4 });
1761
- }
1762
- };
1763
- var TrajectoryRegistry = class {
1764
- static instances = /* @__PURE__ */ new Map();
1765
- static get(workspaceDir, opts = {}) {
1766
- const normalized = path3.resolve(workspaceDir);
1767
- const existing = this.instances.get(normalized);
1768
- if (existing) return existing;
1769
- const created = new TrajectoryDatabase({ workspaceDir: normalized, ...opts });
1770
- this.instances.set(normalized, created);
1771
- return created;
1772
- }
1773
- static dispose(workspaceDir) {
1774
- const normalized = path3.resolve(workspaceDir);
1775
- const instance = this.instances.get(normalized);
1776
- if (instance) {
1777
- instance.dispose();
1778
- this.instances.delete(normalized);
1779
- }
1780
- }
1781
- static clear() {
1782
- for (const instance of this.instances.values()) {
1783
- instance.dispose();
1784
- }
1785
- this.instances.clear();
1786
- }
1787
- static use(workspaceDir, fn, opts = {}) {
1788
- const normalized = path3.resolve(workspaceDir);
1789
- const existing = this.instances.get(normalized);
1790
- if (existing) {
1791
- return fn(existing);
1792
- }
1793
- const transient = new TrajectoryDatabase({ workspaceDir: normalized, ...opts });
1794
- try {
1795
- return fn(transient);
1796
- } finally {
1797
- transient.dispose();
1798
- }
1799
- }
1800
- };
1801
- export {
1802
- TrajectoryDatabase,
1803
- TrajectoryRegistry
1804
- };