@tequila99/githuman 0.11.2 → 0.13.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 (325) hide show
  1. package/README.md +74 -2
  2. package/dist/cli/commands/mcp.js +112 -0
  3. package/dist/cli/commands/pi-acp.js +21 -0
  4. package/dist/cli/config.js +10 -1
  5. package/dist/cli/index.js +19 -8
  6. package/dist/cli/server-runtime.js +5 -0
  7. package/dist/server/adapters/agents/artifact-permission.js +88 -0
  8. package/dist/server/adapters/pi-acp/agent.js +98 -0
  9. package/dist/server/adapters/pi-acp/compatibility.js +57 -0
  10. package/dist/server/adapters/pi-acp/control.js +76 -0
  11. package/dist/server/adapters/pi-acp/errors.js +17 -0
  12. package/dist/server/adapters/pi-acp/extension.js +255 -0
  13. package/dist/server/adapters/pi-acp/launch.js +16 -0
  14. package/dist/server/adapters/pi-acp/mcp.js +153 -0
  15. package/dist/server/adapters/pi-acp/process.js +199 -0
  16. package/dist/server/adapters/pi-acp/session.js +409 -0
  17. package/dist/server/adapters/pi-acp/vendor/LICENSE +21 -0
  18. package/dist/server/adapters/pi-acp/vendor/README.md +29 -0
  19. package/dist/server/adapters/pi-acp/vendor/prompt.js +56 -0
  20. package/dist/server/app.js +95 -5
  21. package/dist/server/config/agents.js +4 -6
  22. package/dist/server/db/artifact-graph-migrations.js +56 -0
  23. package/dist/server/db/artifact-migrations.js +70 -0
  24. package/dist/server/db/artifact-review-migrations.js +24 -0
  25. package/dist/server/db/artifact-search-migrations.js +31 -0
  26. package/dist/server/db/artifact-validation-migrations.js +70 -0
  27. package/dist/server/db/index.js +3 -0
  28. package/dist/server/db/migrations.js +10 -0
  29. package/dist/server/db/sqlite-error.js +15 -0
  30. package/dist/server/db/transaction.js +40 -0
  31. package/dist/server/errors/artifacts.js +28 -0
  32. package/dist/server/errors/database.js +2 -0
  33. package/dist/server/hooks/access/local-request.js +5 -0
  34. package/dist/server/hooks/access/require-access.js +13 -2
  35. package/dist/server/repositories/artifact-catalog.repo.js +283 -0
  36. package/dist/server/repositories/artifact-files.repo.js +181 -0
  37. package/dist/server/repositories/artifact-graph.repo.js +50 -0
  38. package/dist/server/repositories/artifact-search.repo.js +192 -0
  39. package/dist/server/repositories/artifact-transition.repo.js +85 -0
  40. package/dist/server/repositories/artifact-validation.repo.js +157 -0
  41. package/dist/server/repositories/notes-catalog.repo.js +2 -10
  42. package/dist/server/repositories/task.repo.js +114 -0
  43. package/dist/server/routes/agent.js +8 -1
  44. package/dist/server/routes/artifact-actions.js +129 -0
  45. package/dist/server/routes/artifact-agent.js +199 -0
  46. package/dist/server/routes/artifact-definitions.js +163 -0
  47. package/dist/server/routes/artifact-transitions.js +80 -0
  48. package/dist/server/routes/artifacts.js +111 -0
  49. package/dist/server/routes/events.js +5 -1
  50. package/dist/server/services/agent/agent-session.js +180 -10
  51. package/dist/server/services/agent/context-builder.js +30 -2
  52. package/dist/server/services/agent/session-registry.js +136 -10
  53. package/dist/server/services/agent/turn-task-context.js +1 -0
  54. package/dist/server/services/artifact-authorization.service.js +199 -0
  55. package/dist/server/services/artifact-definition.service.js +96 -0
  56. package/dist/server/services/artifact-export.service.js +44 -0
  57. package/dist/server/services/artifact-graph-validation.js +541 -0
  58. package/dist/server/services/artifact-graph.service.js +81 -0
  59. package/dist/server/services/artifact-lifecycle.service.js +431 -0
  60. package/dist/server/services/artifact-maintenance.service.js +55 -0
  61. package/dist/server/services/artifact-search.service.js +331 -0
  62. package/dist/server/services/artifact-storage.service.js +646 -0
  63. package/dist/server/services/artifact-transition.service.js +608 -0
  64. package/dist/server/services/artifact-type.service.js +74 -0
  65. package/dist/server/services/artifact-validation-jobs.service.js +848 -0
  66. package/dist/server/services/device-connections.js +9 -1
  67. package/dist/server/services/review.service.js +6 -5
  68. package/dist/server/services/task.service.js +225 -0
  69. package/dist/server/utils/artifact-actor.js +13 -0
  70. package/dist/server/utils/artifact-page.js +42 -0
  71. package/dist/server/utils/take-within-bytes.js +16 -0
  72. package/dist/server/utils/text.js +7 -0
  73. package/dist/shared/agents/schemas.js +30 -0
  74. package/dist/shared/app/storage-constants.js +2 -0
  75. package/dist/shared/artifact-tools/descriptions.js +17 -0
  76. package/dist/shared/artifact-tools/instructions.js +14 -0
  77. package/dist/shared/artifact-tools/schemas.js +124 -0
  78. package/dist/shared/artifact-transitions/constants.js +21 -0
  79. package/dist/shared/artifact-transitions/schemas.js +111 -0
  80. package/dist/shared/artifact-transitions/transitions.js +61 -0
  81. package/dist/shared/artifact-transitions/types.js +1 -0
  82. package/dist/shared/artifacts/constants.js +34 -0
  83. package/dist/shared/artifacts/mcp-constants.js +24 -0
  84. package/dist/shared/artifacts/normalize.js +9 -0
  85. package/dist/shared/artifacts/schemas.js +874 -0
  86. package/dist/shared/artifacts/search.js +101 -0
  87. package/dist/shared/artifacts/types.js +1 -0
  88. package/dist/shared/events/schemas.js +1 -1
  89. package/dist/shared/events/types.js +1 -0
  90. package/dist/shared/tasks/schemas.js +261 -0
  91. package/dist/shared/tasks/types.js +1 -0
  92. package/dist/shared/utils/artifact-body.js +10 -0
  93. package/dist/shared/utils/canonical-json.js +22 -0
  94. package/dist/shared/utils/is-safe-prefixed-filename.js +10 -0
  95. package/dist/shared/utils/is-uuid.js +4 -2
  96. package/dist/shared/utils/is-valid-artifact-type-id.js +9 -0
  97. package/dist/shared/utils/is-valid-filename-prefix.js +13 -0
  98. package/dist/web/assets/AccessPage-D9xfFUBC.js +1 -0
  99. package/dist/web/assets/AddButton-DKNKmS1i.js +4 -0
  100. package/dist/web/assets/AddToAgentButton-wC1dzbDE.js +1 -0
  101. package/dist/web/assets/AppPage-Cozv2vqG.js +1 -0
  102. package/dist/web/assets/ArtifactsPage-IHUO7SbH.js +2 -0
  103. package/dist/web/assets/ArtifactsPage-nRD9XiRm.css +1 -0
  104. package/dist/web/assets/CancelButton-mkxRHwoZ.js +1 -0
  105. package/dist/web/assets/ClosePopup-Bd5a-Lu_.js +1 -0
  106. package/dist/web/assets/CreateButton-zCmb7snC.js +1 -0
  107. package/dist/web/assets/CreateReviewFabButton-D58IHr36.js +1 -0
  108. package/dist/web/assets/DialogHeader-BVrtb0Ri.js +1 -0
  109. package/dist/web/assets/DialogHeader-CPL9OtDV.css +1 -0
  110. package/dist/web/assets/{ErrorNotFound-DucDzOUJ.js → ErrorNotFound-fXS367lZ.js} +1 -1
  111. package/dist/web/assets/{IndexPage-D5PvyGrX.css → IndexPage-5V5GLtgi.css} +1 -1
  112. package/dist/web/assets/IndexPage-BJ1EQdlT.js +1 -0
  113. package/dist/web/assets/MainLayout-C3Ie5yOo.css +1 -0
  114. package/dist/web/assets/MainLayout-CeogGMHH.js +7 -0
  115. package/dist/web/assets/{NoteVisualEditor-CawpxEFB.css → NoteVisualEditor-DE4ACTRJ.css} +1 -1
  116. package/dist/web/assets/NoteVisualEditor-DqAZZig1.js +1054 -0
  117. package/dist/web/assets/NotesPage-C_eoCUl7.css +1 -0
  118. package/dist/web/assets/NotesPage-DXgI-kwN.js +4 -0
  119. package/dist/web/assets/{PdfPreview-rlkcqoMF.js → PdfPreview-ByeBjQMQ.js} +2 -2
  120. package/dist/web/assets/ProjectFilePage-DV1anYfN.js +1 -0
  121. package/dist/web/assets/QBtn-V3VXtJXi.js +1 -0
  122. package/dist/web/assets/QFab-DJhLqo9i.js +1 -0
  123. package/dist/web/assets/{QForm-Be8-ucuJ.js → QForm-ClpSneeD.js} +1 -1
  124. package/dist/web/assets/QInput-BnqLJGXl.js +1 -0
  125. package/dist/web/assets/QScrollArea-qf2G1s2r.js +1 -0
  126. package/dist/web/assets/QSeparator-B8uLaDiI.js +1 -0
  127. package/dist/web/assets/QSpace-CVp0CPR_.js +1 -0
  128. package/dist/web/assets/QSplitter-K2LjbupZ.js +1 -0
  129. package/dist/web/assets/QTabs-Bl_rIz4_.js +1 -0
  130. package/dist/web/assets/QVirtualScroll-BeEkuL6x.js +1 -0
  131. package/dist/web/assets/ReviewDetailPage-BgxcsTqY.css +1 -0
  132. package/dist/web/assets/ReviewDetailPage-CL4Dmz0L.js +1 -0
  133. package/dist/web/assets/ReviewsPage-BPJFTulG.js +1 -0
  134. package/dist/web/assets/SaveButton-D1q2suzR.js +3 -0
  135. package/dist/web/assets/SaveButton-Vtjvcbt0.css +1 -0
  136. package/dist/web/assets/SearchInput-DapVXrFj.js +1 -0
  137. package/dist/web/assets/StatusSwitcher-BtbSIr5q.js +1 -0
  138. package/dist/web/assets/{StatusSwitcher-D6i8_OfR.css → StatusSwitcher-DaE1ufAA.css} +1 -1
  139. package/dist/web/assets/TagFilterField-CPl-BxtL.js +1 -0
  140. package/dist/web/assets/TagFilterField-DjUH0m3_.css +1 -0
  141. package/dist/web/assets/{TerminalView-D4elkzN4.js → TerminalView-B56cEx8g.js} +12 -12
  142. package/dist/web/assets/{TerminalView-617DZuPt.css → TerminalView-BCeke4xd.css} +1 -1
  143. package/dist/web/assets/{abnfDiagram-O67JEVCF-Cugru96L.js → abnfDiagram-O67JEVCF-D_6l0gCA.js} +1 -1
  144. package/dist/web/assets/{access-store-Cdg8oRAM.js → access-store-CwkOl71l.js} +1 -1
  145. package/dist/web/assets/{agent-store-D_W7iD_B.js → agent-store-Cs-hu1v1.js} +5 -5
  146. package/dist/web/assets/architecture-7GRP2DOG-KT2-_RA5.js +1 -0
  147. package/dist/web/assets/{architectureDiagram-NJMV4G6O-7rMsE4Wt.js → architectureDiagram-NJMV4G6O-BFgjp9nL.js} +1 -1
  148. package/dist/web/assets/{blockDiagram-BEXU5L5S-BvSnwUrI.js → blockDiagram-BEXU5L5S-BWfbJk26.js} +1 -1
  149. package/dist/web/assets/{c4Diagram-YGBWAQC7-CgmQlCVx.js → c4Diagram-YGBWAQC7-oIqgOwQa.js} +1 -1
  150. package/dist/web/assets/channel-CAhty-fl.js +1 -0
  151. package/dist/web/assets/{chunk-3FUC2YCW-_i8udThz.js → chunk-3FUC2YCW-WI2pliXT.js} +1 -1
  152. package/dist/web/assets/{chunk-5DYCD2WN-Mhk2LlLk.js → chunk-5DYCD2WN-DYvaF8o2.js} +1 -1
  153. package/dist/web/assets/{chunk-742MDFTN-CDVcwgEp.js → chunk-742MDFTN-xd7B3x5h.js} +1 -1
  154. package/dist/web/assets/{chunk-7INBJB4K-C2c1fi9s.js → chunk-7INBJB4K-nsyx3RGB.js} +1 -1
  155. package/dist/web/assets/{chunk-7M6MHVWA-BeGkoIBL.js → chunk-7M6MHVWA-BxPQ9AEv.js} +1 -1
  156. package/dist/web/assets/{chunk-7PRAP22T-BVWTh9sT.js → chunk-7PRAP22T-CvHw2y-F.js} +1 -1
  157. package/dist/web/assets/{chunk-GTNCS2PH-gVhX7NZP.js → chunk-GTNCS2PH-DIfN1SOI.js} +1 -1
  158. package/dist/web/assets/{chunk-GWA4HPMP-BPdnIcQH.js → chunk-GWA4HPMP-2WZX1fG-.js} +1 -1
  159. package/dist/web/assets/{chunk-MBY4JIJT-B2VfO_06.js → chunk-MBY4JIJT-BL7akw7q.js} +1 -1
  160. package/dist/web/assets/{chunk-NETBCI7D-DZF_gITO.js → chunk-NETBCI7D-CQX1tY3W.js} +1 -1
  161. package/dist/web/assets/{chunk-O7XYJQB3-CDpBWiLq.js → chunk-O7XYJQB3-CtGXf4J6.js} +1 -1
  162. package/dist/web/assets/{chunk-UA2S7LBM-Iv7TSRE_.js → chunk-UA2S7LBM-C_3M_IIp.js} +1 -1
  163. package/dist/web/assets/{chunk-WEXAMYUT-Ce6goZ2Y.js → chunk-WEXAMYUT-6MO559WJ.js} +1 -1
  164. package/dist/web/assets/{chunk-Z7XXMR3K-CY6E6_Ux.js → chunk-Z7XXMR3K-CHJ55IlF.js} +1 -1
  165. package/dist/web/assets/{chunk-ZIGJFQKS-BLzsXZBc.js → chunk-ZIGJFQKS-533qcfPb.js} +1 -1
  166. package/dist/web/assets/{classDiagram-v2-NBCMYWYE-Dz0kD9P6.js → classDiagram-v2-NBCMYWYE-CFySVswx.js} +1 -1
  167. package/dist/web/assets/copy-code-XoEu_Yh6.js +1 -0
  168. package/dist/web/assets/create-DedXY-KP.js +1 -0
  169. package/dist/web/assets/{cynefin-OW5HDTMX-DWIx3wRm.js → cynefin-OW5HDTMX-Dfg_2VTu.js} +1 -1
  170. package/dist/web/assets/{cynefinDiagram-VND7K2PF-2D80uocv.js → cynefinDiagram-VND7K2PF-DaSXiPph.js} +1 -1
  171. package/dist/web/assets/{dagre-6A5THRUB-BLBhukn-.js → dagre-6A5THRUB-D7d1gbRO.js} +1 -1
  172. package/dist/web/assets/date-snFAXn1W.js +1 -0
  173. package/dist/web/assets/{devices-store-BfZKPOhg.js → devices-store-DB_tWC1Q.js} +1 -1
  174. package/dist/web/assets/{diagram-22UHCM2B-DY8dMLZ3.js → diagram-22UHCM2B-CNuTss_x.js} +1 -1
  175. package/dist/web/assets/{diagram-3UASUU5V-Bf6Od1NH.js → diagram-3UASUU5V--Rt3-ARV.js} +1 -1
  176. package/dist/web/assets/{diagram-ATOU4E4O-CjLGcYD5.js → diagram-ATOU4E4O-BP79L-gP.js} +1 -1
  177. package/dist/web/assets/{diagram-CDSNMT55-gIvaVNvs.js → diagram-CDSNMT55-CRO5cocs.js} +1 -1
  178. package/dist/web/assets/{diagram-MLGK6HIB-VR5kiCi5.js → diagram-MLGK6HIB-Difu0Zmh.js} +1 -1
  179. package/dist/web/assets/{diagram-MPIPVDR6-CdAdj25I.js → diagram-MPIPVDR6-grQ3ie17.js} +1 -1
  180. package/dist/web/assets/diff-store-D4XiTkfi.js +93 -0
  181. package/dist/web/assets/{dist-CBGyhCYv.js → dist-75uxNoTf.js} +1 -1
  182. package/dist/web/assets/{dist-CIMEaJAX.js → dist-9rzCziV2.js} +1 -1
  183. package/dist/web/assets/{dist-CEl-Ko94.js → dist-B9i6cUYu.js} +1 -1
  184. package/dist/web/assets/{dist-yzAhvE-V.js → dist-BU-fZfEv.js} +1 -1
  185. package/dist/web/assets/{dist-CTd7Kuqx.js → dist-BeUB43Ur.js} +1 -1
  186. package/dist/web/assets/{dist-C85t5q3z.js → dist-BxIdk5Al.js} +1 -1
  187. package/dist/web/assets/{dist-joQDFmfy.js → dist-By534Syk.js} +1 -1
  188. package/dist/web/assets/{dist-C54FrSb8.js → dist-DFjLCq-0.js} +1 -1
  189. package/dist/web/assets/{dist-lgw4kcVA.js → dist-DNrP7O-J.js} +1 -1
  190. package/dist/web/assets/{dist-BU8erXQF.js → dist-Du5VPTAq.js} +1 -1
  191. package/dist/web/assets/{dist-DLuI1PxH.js → dist-DyBqP777.js} +1 -1
  192. package/dist/web/assets/{ebnfDiagram-ZINNZB2B-irTAlMFO.js → ebnfDiagram-ZINNZB2B-DX2rcvMK.js} +1 -1
  193. package/dist/web/assets/{elk-276RUBZZ-Dj5ligvC.js → elk-276RUBZZ-DSHIl9hv.js} +1 -1
  194. package/dist/web/assets/{erDiagram-OPXOYQCR-Bvn6rbL1.js → erDiagram-OPXOYQCR-BIZVJO8N.js} +1 -1
  195. package/dist/web/assets/eventmodeling-NTZA5JFV-Bh_xDKx6.js +1 -0
  196. package/dist/web/assets/flowDiagram-KWPJA3E3-DXkIxaXL.js +1 -0
  197. package/dist/web/assets/{ganttDiagram-FUAMR5RP-BNGAUF5H.js → ganttDiagram-FUAMR5RP-D4kPfh4X.js} +1 -1
  198. package/dist/web/assets/{gitGraph-4MIJSDKK-DYSHOl72.js → gitGraph-4MIJSDKK-B41R-oAb.js} +1 -1
  199. package/dist/web/assets/{gitGraphDiagram-X574FWY7-BGwRHrPq.js → gitGraphDiagram-X574FWY7-xXrZiSJI.js} +1 -1
  200. package/dist/web/assets/i18n-Cfw_OfRR.js +1 -0
  201. package/dist/web/assets/icons-BIOTvmdT.js +1 -0
  202. package/dist/web/assets/index-B1Bugudn.js +2 -0
  203. package/dist/web/assets/index-BQU3KK1E.css +1 -0
  204. package/dist/web/assets/{info-A6RAGUB7-DNWR20pW.js → info-A6RAGUB7-CgSmK-La.js} +1 -1
  205. package/dist/web/assets/{infoDiagram-VRGFBTTK-BhNwJLY5.js → infoDiagram-VRGFBTTK-CDNEJ_R-.js} +1 -1
  206. package/dist/web/assets/{init-app-cg-B_TFN.js → init-app-C3tD3faT.js} +1 -1
  207. package/dist/web/assets/{is-D0waCELe.js → is-B1kBQ5Va.js} +1 -1
  208. package/dist/web/assets/{ishikawaDiagram-OU5B5YK6-CSkDpn64.js → ishikawaDiagram-OU5B5YK6-ZcU1IxFn.js} +1 -1
  209. package/dist/web/assets/{journeyDiagram-ZHPQQLJL-D2CEecC3.js → journeyDiagram-ZHPQQLJL-g-8ZRhJj.js} +1 -1
  210. package/dist/web/assets/{kanban-definition-PNTS6WVX-CQHlo4Hh.js → kanban-definition-PNTS6WVX-nfsfxVOE.js} +1 -1
  211. package/dist/web/assets/{line-C-izH1ks.js → line-BpuX_ban.js} +1 -1
  212. package/dist/web/assets/markdown-ogvtoQzt.js +16 -0
  213. package/dist/web/assets/mdi-Du7sIqtH.js +1 -0
  214. package/dist/web/assets/{mermaid-parser.core-CAGwq6D-.js → mermaid-parser.core-BBj_ep7Z.js} +3 -3
  215. package/dist/web/assets/{mermaid.core--xlI_0tK.js → mermaid.core-DP4USVSM.js} +4 -4
  216. package/dist/web/assets/{mindmap-definition-NLK3R4M7-BuCxhlwy.js → mindmap-definition-NLK3R4M7-C5GhUvi3.js} +1 -1
  217. package/dist/web/assets/{packet-AYTQ26CC-Bi7x5lJK.js → packet-AYTQ26CC-BIdb-vfg.js} +1 -1
  218. package/dist/web/assets/{pegDiagram-GJSIUBJH-qWzBPkCQ.js → pegDiagram-GJSIUBJH-DH-SdKj6.js} +1 -1
  219. package/dist/web/assets/{pie-WAS4IAKB-Di0wC4XB.js → pie-WAS4IAKB-Du3-jRjK.js} +1 -1
  220. package/dist/web/assets/{pieDiagram-5QR66LMP-BCDVY9q2.js → pieDiagram-5QR66LMP-CJh2ngqY.js} +1 -1
  221. package/dist/web/assets/pinia-BfqEuvsi.js +1 -0
  222. package/dist/web/assets/pinia-mdReENsc.js +1 -0
  223. package/dist/web/assets/preview-store-DOnSmm_C.js +2 -0
  224. package/dist/web/assets/{project-files-store-byqyG2ts.js → project-files-store-CHP6RYFI.js} +1 -1
  225. package/dist/web/assets/{quadrantDiagram-O4NWA36T-CpFObUjT.js → quadrantDiagram-O4NWA36T-DYsSScxc.js} +1 -1
  226. package/dist/web/assets/{radar-RG4KPBEZ-Br8yH8rq.js → radar-RG4KPBEZ-BBjvaVVB.js} +1 -1
  227. package/dist/web/assets/{railroad-74A4TZTK-CACSqhA-.js → railroad-74A4TZTK-B-CRncBU.js} +1 -1
  228. package/dist/web/assets/railroad-abnf-HS5TGJTU-D37zpoYN.js +1 -0
  229. package/dist/web/assets/railroad-ebnf-LZEXJU2U-ZnMxH6Ym.js +1 -0
  230. package/dist/web/assets/railroad-peg-WCYAUIDC-Dg3g-VsM.js +1 -0
  231. package/dist/web/assets/{railroadDiagram-XR7U4H2S-sYXRKGK1.js → railroadDiagram-XR7U4H2S-hqp-2UHb.js} +1 -1
  232. package/dist/web/assets/{render-C7fu7uYY.js → render-Brao9Nj2.js} +1 -1
  233. package/dist/web/assets/repository-store-DZgQ5H7J.js +1 -0
  234. package/dist/web/assets/{requirementDiagram-PLB6GJNP-C9I1JbQc.js → requirementDiagram-PLB6GJNP-Dhuw5jcQ.js} +1 -1
  235. package/dist/web/assets/runtime-core.esm-bundler-DDnHUoIy.js +1 -0
  236. package/dist/web/assets/{safe-storage-C76bPypO.js → safe-storage-BchoYSbq.js} +1 -1
  237. package/dist/web/assets/{sankeyDiagram-IPEJSGJF-CuvFQhPh.js → sankeyDiagram-IPEJSGJF-D5rCIxGR.js} +1 -1
  238. package/dist/web/assets/{sequenceDiagram-PO4LG4MO-CaVb9Y6e.js → sequenceDiagram-PO4LG4MO-YEHR4Mp-.js} +1 -1
  239. package/dist/web/assets/single-flight-CyDZB3rb.js +1 -0
  240. package/dist/web/assets/{stateDiagram-v2-GCMORJYK-Dp_APryr.js → stateDiagram-v2-GCMORJYK-9iWmz6Xf.js} +1 -1
  241. package/dist/web/assets/{swimlanes-2SLR337P-FclUI-k0.js → swimlanes-2SLR337P-Cjwvgt_n.js} +1 -1
  242. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-gJwxhWoO.js +8 -0
  243. package/dist/web/assets/{terminal-store-BnEkXuBj.js → terminal-store-i7HHlWzR.js} +1 -1
  244. package/dist/web/assets/theme-CQ9InaWa.js +1 -0
  245. package/dist/web/assets/{timeline-definition-EJHVYXUP-BAqwqj3c.js → timeline-definition-EJHVYXUP-Cd5n2-Py.js} +1 -1
  246. package/dist/web/assets/{treeView-Q6P3EWNA-D87jJbUQ.js → treeView-Q6P3EWNA-C_QH9vtR.js} +1 -1
  247. package/dist/web/assets/{treemap-WGGIJYW6-B6nhGhRc.js → treemap-WGGIJYW6-CVnU7TVz.js} +1 -1
  248. package/dist/web/assets/use-add-agent-context-D5VqMr0p.js +1 -0
  249. package/dist/web/assets/use-app-theme-DgC4ivPF.js +1 -0
  250. package/dist/web/assets/use-countdown-D6_qSOZJ.js +1 -0
  251. package/dist/web/assets/use-notify-error-Dx2D9IEP.js +1 -0
  252. package/dist/web/assets/use-router-link-D7geCGQI.js +1 -0
  253. package/dist/web/assets/use-row-height-BgHg4-J-.js +1 -0
  254. package/dist/web/assets/use-server-events-CvcbsbIf.js +1 -0
  255. package/dist/web/assets/use-virtual-scroll-DixIsJk4.js +1 -0
  256. package/dist/web/assets/{use-workspace-navigation-CnebLvBV.js → use-workspace-navigation-Dq-Aw8Xq.js} +1 -1
  257. package/dist/web/assets/{usecaseDiagram-POWQR4AR-7YcIKxAb.js → usecaseDiagram-POWQR4AR-Cbj2Pvab.js} +1 -1
  258. package/dist/web/assets/{vennDiagram-UO4OBE2U-BZq5cFC4.js → vennDiagram-UO4OBE2U-2j79YqCN.js} +1 -1
  259. package/dist/web/assets/viewport-store-NqQS7itT.js +1 -0
  260. package/dist/web/assets/{vue-i18n.runtime-CEtihHHL.js → vue-i18n.runtime-JOo8b7-t.js} +2 -2
  261. package/dist/web/assets/{wardley-WFR3VGLG-BT8YO9H4.js → wardley-WFR3VGLG-wsMK7zm3.js} +1 -1
  262. package/dist/web/assets/{wardleyDiagram-VNRHLVJA-rDpXFf6D.js → wardleyDiagram-VNRHLVJA-D6PKTQIJ.js} +1 -1
  263. package/dist/web/assets/weighted-lru-BFtZKA_y.js +1 -0
  264. package/dist/web/assets/workspace-navigation-CoXKJJpP.js +1 -0
  265. package/dist/web/assets/{xychartDiagram-PMCCYNJV-ClniUuZc.js → xychartDiagram-PMCCYNJV-D1fQN7KG.js} +1 -1
  266. package/dist/web/index.html +10 -9
  267. package/package.json +5 -3
  268. package/dist/web/assets/AccessPage-CX3w30zw.js +0 -1
  269. package/dist/web/assets/AppPage-PFJG281y.js +0 -1
  270. package/dist/web/assets/CreateButton-Ch0b7juj.js +0 -1
  271. package/dist/web/assets/CreateReviewFabButton-DTuJzw3q.js +0 -1
  272. package/dist/web/assets/IndexPage-tgW07Qvs.js +0 -1
  273. package/dist/web/assets/MainLayout-C6yBhQnf.css +0 -1
  274. package/dist/web/assets/MainLayout-Cw4jpB1x.js +0 -8
  275. package/dist/web/assets/NoteVisualEditor-DiGhLhQg.js +0 -1054
  276. package/dist/web/assets/NotesPage-CVyVr9tl.js +0 -4
  277. package/dist/web/assets/NotesPage-CxGnZuVQ.css +0 -1
  278. package/dist/web/assets/PanelResize-B6FP9ZVB.js +0 -1
  279. package/dist/web/assets/ProjectFilePage-CkxfX9b1.js +0 -1
  280. package/dist/web/assets/QBtn-CyyV1Bx_.js +0 -1
  281. package/dist/web/assets/QExpansionItem-Dp9lbdVb.js +0 -4
  282. package/dist/web/assets/QFab-LOmZ9rev.js +0 -1
  283. package/dist/web/assets/QInput-Dq6131uz.js +0 -1
  284. package/dist/web/assets/QSelect-B34OqtIH.js +0 -1
  285. package/dist/web/assets/QSpace-BhOVGpQh.js +0 -1
  286. package/dist/web/assets/QVirtualScroll-BYgJ1Kh2.js +0 -1
  287. package/dist/web/assets/ReviewDetailPage-CkCilMNW.css +0 -1
  288. package/dist/web/assets/ReviewDetailPage-GNjBa4X0.js +0 -1
  289. package/dist/web/assets/ReviewsPage-An5fRS92.js +0 -1
  290. package/dist/web/assets/StatusSwitcher-DrFHHwGm.js +0 -1
  291. package/dist/web/assets/architecture-7GRP2DOG-BPCNj4lR.js +0 -1
  292. package/dist/web/assets/channel-CzRYxuLe.js +0 -1
  293. package/dist/web/assets/create-B9Tn5Xvi.js +0 -1
  294. package/dist/web/assets/diff-store-B4adGxC2.js +0 -93
  295. package/dist/web/assets/eventmodeling-NTZA5JFV-BJsTZ_nG.js +0 -1
  296. package/dist/web/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNa-Dr0goTwe.woff +0 -0
  297. package/dist/web/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ-D-x-0Q06.woff2 +0 -0
  298. package/dist/web/assets/flowDiagram-KWPJA3E3-CvXh1Df7.js +0 -1
  299. package/dist/web/assets/i18n-DnlAcIbT.js +0 -1
  300. package/dist/web/assets/index-Cw7GJi3K.js +0 -2
  301. package/dist/web/assets/index-wsOPzloa.css +0 -1
  302. package/dist/web/assets/markdown-CvtLTiBZ.js +0 -16
  303. package/dist/web/assets/pinia-DO0AdKGg.js +0 -1
  304. package/dist/web/assets/pinia-uOZCmBi-.js +0 -1
  305. package/dist/web/assets/preview-store-DyO8G-ox.js +0 -1
  306. package/dist/web/assets/railroad-abnf-HS5TGJTU-DTBsRB1c.js +0 -1
  307. package/dist/web/assets/railroad-ebnf-LZEXJU2U-D3mrY4ar.js +0 -1
  308. package/dist/web/assets/railroad-peg-WCYAUIDC-33ZjWX0u.js +0 -1
  309. package/dist/web/assets/repository-store-DtRDth1L.js +0 -1
  310. package/dist/web/assets/runtime-core.esm-bundler-CAvPiMpd.js +0 -1
  311. package/dist/web/assets/single-flight-fPyIy7h5.js +0 -1
  312. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-CuUvsn0e.js +0 -8
  313. package/dist/web/assets/theme-CnbaLamF.js +0 -1
  314. package/dist/web/assets/use-add-agent-context-xHAjjOf1.js +0 -1
  315. package/dist/web/assets/use-app-theme-DEowpWRR.js +0 -1
  316. package/dist/web/assets/use-countdown-DlcwwJrs.js +0 -1
  317. package/dist/web/assets/use-panel-width-CjSWCFr4.js +0 -1
  318. package/dist/web/assets/use-router-link-CUoYaYUw.js +0 -1
  319. package/dist/web/assets/use-row-height-BsE9-rO2.js +0 -1
  320. package/dist/web/assets/use-server-events-C2X4Xz00.js +0 -1
  321. package/dist/web/assets/use-virtual-scroll-BAZGswHv.js +0 -1
  322. package/dist/web/assets/viewport-store-CCHiY2QG.js +0 -1
  323. package/dist/web/assets/weighted-lru-BGovtJ2a.js +0 -1
  324. package/dist/web/assets/workspace-navigation-DdW0rSGz.js +0 -1
  325. /package/dist/web/assets/{PanelResize-XjrbOj2S.css → AddToAgentButton-XjrbOj2S.css} +0 -0
@@ -0,0 +1,431 @@
1
+ import { errorMessage } from "../../shared/utils/error-message.js";
2
+ import { randomUUID } from 'node:crypto';
3
+ import { Value } from '@sinclair/typebox/value';
4
+ import { ArtifactChangeSetProposeBody, ArtifactAuthorizationRequestBody, ArtifactLifecycleCommitBody } from "../../shared/artifacts/schemas.js";
5
+ import { canonicalJson } from "../../shared/utils/canonical-json.js";
6
+ import { sha256Hex } from "../utils/hash.js";
7
+ import { HEARTBEAT_STALE_MS } from "../../shared/artifact-transitions/constants.js";
8
+ import { hasErrorCode } from "../utils/system-error.js";
9
+ import { ArtifactConflictError, ArtifactInputError } from "../errors/artifacts.js";
10
+ import { canonicalChangeSet, graphIssuesMessage, previewArtifactChangeSet } from "./artifact-graph-validation.js";
11
+ import { ArtifactValidationRepository } from "../repositories/artifact-validation.repo.js";
12
+ import { ArtifactAuthorizationService, artifactActionDigest } from "./artifact-authorization.service.js";
13
+ import { ArtifactValidationJobsService } from "./artifact-validation-jobs.service.js";
14
+ import { ArtifactTransitionRepository } from "../repositories/artifact-transition.repo.js";
15
+ import { ArtifactTransitionService } from "./artifact-transition.service.js";
16
+ // Manifest changes invalidate all earlier semantic conclusions.
17
+ const VALIDATION_RULES_VERSION = 3;
18
+ // The server is the author of a proposal version that only follows the grown set (ADR 0121).
19
+ const REBASE_ACTOR = { kind: 'system', id: 'validation-rebase' };
20
+ // Preview errors that a rebase can fix: they only name versions of the set context.
21
+ const REBASE_CODES = new Set(['missing_expected_version', 'stale_version']);
22
+ // The reason in the request list is cut to this length; the full text stays in the proposal.
23
+ const ACTION_REASON_CHARS = 300;
24
+ export function artifactSessionId(actor) {
25
+ return actor.kind === 'user' ? `user:${actor.id}` : actor.id;
26
+ }
27
+ /**
28
+ * The digest covers the affected set, not the store epoch. A store change outside
29
+ * the set does not make a running validation stale (ADR 0119).
30
+ */
31
+ function setDigest(manifest) {
32
+ const { validationEpoch: _epoch, ...content } = manifest;
33
+ return artifactActionDigest(content);
34
+ }
35
+ export class ArtifactLifecycleService {
36
+ repository;
37
+ authorization;
38
+ jobs;
39
+ transitions;
40
+ registry;
41
+ storage;
42
+ now;
43
+ constructor(storage, db, now = Date.now) {
44
+ this.storage = storage;
45
+ this.now = now;
46
+ this.repository = new ArtifactValidationRepository(db, storage.storeId);
47
+ this.authorization = new ArtifactAuthorizationService(this.repository, now, action => this.checkAction(action), id => id.startsWith('user:') ||
48
+ Boolean(this.registry?.get(id) && this.registry.get(id).status !== 'closed'), () => this.registry?.notifyArtifactActionsChanged?.());
49
+ this.jobs = new ArtifactValidationJobsService(this, now);
50
+ this.transitions = new ArtifactTransitionService(this, new ArtifactTransitionRepository(db, storage.storeId), now);
51
+ }
52
+ /**
53
+ * True when the process of `owner` exists and its heartbeat is fresh. A
54
+ * missing heartbeat counts as alive, so old data keeps its behavior. The own
55
+ * instance is always alive (ADR 0108).
56
+ */
57
+ ownerAlive(owner, pid) {
58
+ if (owner === this.jobs.ownerId) {
59
+ return true;
60
+ }
61
+ try {
62
+ process.kill(pid, 0);
63
+ }
64
+ catch (error) {
65
+ if (hasErrorCode(error, 'ESRCH')) {
66
+ return false;
67
+ }
68
+ }
69
+ const beat = this.repository.heartbeat(owner);
70
+ return beat === undefined || this.now() - beat <= HEARTBEAT_STALE_MS;
71
+ }
72
+ attachSessions(registry) {
73
+ this.registry = registry;
74
+ this.jobs.attachSessions(registry);
75
+ }
76
+ propose(input, actor) {
77
+ if (!Value.Check(ArtifactChangeSetProposeBody, input) ||
78
+ !input.changeSet.reason.trim()) {
79
+ throw new ArtifactInputError('Invalid change set proposal.');
80
+ }
81
+ if (Buffer.byteLength(JSON.stringify(input.changeSet)) > 48 * 1024) {
82
+ throw new ArtifactInputError('Change set exceeds the bounded proposal size.');
83
+ }
84
+ return this.storage.catalog.transaction(() => this.proposeInTransaction(input, actor));
85
+ }
86
+ /** Proposes inside a transaction owned by the caller. */
87
+ proposeInTransaction(input, actor) {
88
+ const changeDigest = artifactActionDigest(canonicalChangeSet(input.changeSet));
89
+ const requestDigest = artifactActionDigest({
90
+ id: input.id ?? null,
91
+ expectedVersion: input.expectedVersion ?? null,
92
+ changeDigest,
93
+ actor
94
+ });
95
+ const replay = this.repository.proposalRequest(input.requestId);
96
+ if (replay) {
97
+ if (replay.requestDigest !== requestDigest ||
98
+ canonicalJson(replay.actor) !== canonicalJson(actor) ||
99
+ (input.id && replay.id !== input.id)) {
100
+ throw new ArtifactConflictError('Proposal request ID was reused.');
101
+ }
102
+ return replay;
103
+ }
104
+ const preview = this.preview(input.changeSet);
105
+ if (preview.errors.length) {
106
+ throw new ArtifactInputError(graphIssuesMessage(preview.errors, preview.expectedVersions));
107
+ }
108
+ const previous = input.id ? this.repository.proposal(input.id) : undefined;
109
+ if ((previous && previous.version !== input.expectedVersion) ||
110
+ (!previous && input.expectedVersion)) {
111
+ throw new ArtifactConflictError('Change set version changed.');
112
+ }
113
+ const proposal = {
114
+ id: previous?.id ?? randomUUID(),
115
+ version: (previous?.version ?? 0) + 1,
116
+ requestId: input.requestId,
117
+ requestDigest,
118
+ actor,
119
+ changeSet: preview.canonical,
120
+ changeDigest: preview.changeDigest
121
+ };
122
+ this.repository.appendProposal(proposal);
123
+ return proposal;
124
+ }
125
+ inspectValidation(id) {
126
+ return this.storage.catalog.readSnapshot(() => {
127
+ const validation = this.jobs.get(id);
128
+ const proposal = this.repository.proposalVersion(validation.changeSetId, validation.version);
129
+ const preview = this.preview(proposal.changeSet);
130
+ let ready = false;
131
+ let problem = null;
132
+ try {
133
+ this.checkAction({
134
+ kind: 'lifecycle',
135
+ changeSetId: proposal.id,
136
+ version: proposal.version,
137
+ validationId: id
138
+ });
139
+ ready = true;
140
+ }
141
+ catch (error) {
142
+ problem = errorMessage(error);
143
+ }
144
+ return {
145
+ storeId: this.storage.storeId,
146
+ validation,
147
+ proposal,
148
+ preview,
149
+ ready,
150
+ problem
151
+ };
152
+ });
153
+ }
154
+ /** The pending requests, each with a short summary so the user can tell them apart. */
155
+ listActions() {
156
+ return this.storage.catalog.readSnapshot(() => this.authorization.list().map(pending => {
157
+ try {
158
+ const { changeSet } = this.repository.proposalVersion(pending.action.changeSetId, pending.action.version);
159
+ return {
160
+ ...pending,
161
+ summary: {
162
+ command: changeSet.command,
163
+ reason: changeSet.reason.slice(0, ACTION_REASON_CHARS),
164
+ documentCount: changeSet.targetStatuses.length
165
+ }
166
+ };
167
+ }
168
+ catch {
169
+ // A proposal that cannot be read must not hide the request itself.
170
+ return { ...pending, summary: null };
171
+ }
172
+ }));
173
+ }
174
+ inspectAction(id, actor) {
175
+ return this.storage.catalog.readSnapshot(() => {
176
+ const value = this.authorization.describe(id);
177
+ const action = value.pending.action;
178
+ const proposal = this.repository.proposalVersion(action.changeSetId, action.version);
179
+ const validation = this.jobs.get(action.validationId);
180
+ const preview = this.preview(proposal.changeSet);
181
+ let ready = false;
182
+ let problem = null;
183
+ if (value.phase !== 'consumed') {
184
+ try {
185
+ const binding = this.checkAction(action);
186
+ if (binding.actionDigest !== value.pending.actionDigest ||
187
+ binding.manifestDigest !== value.pending.manifestDigest) {
188
+ throw new ArtifactConflictError('Action scope changed.');
189
+ }
190
+ ready = true;
191
+ }
192
+ catch (error) {
193
+ problem = errorMessage(error);
194
+ }
195
+ }
196
+ const canExecute = value.pending.sessionId === artifactSessionId(actor);
197
+ // Project only the original request fields; grants also hold private audit bindings.
198
+ const { id: pendingId, sessionId, action: pendingAction, actionDigest, manifestDigest, expiresAt } = value.pending;
199
+ return {
200
+ storeId: this.storage.storeId,
201
+ pending: {
202
+ id: pendingId,
203
+ sessionId,
204
+ action: pendingAction,
205
+ actionDigest,
206
+ manifestDigest,
207
+ expiresAt
208
+ },
209
+ phase: value.phase,
210
+ canExecute,
211
+ ready,
212
+ problem,
213
+ proposal,
214
+ validation,
215
+ preview,
216
+ authorizationId: canExecute ? value.authorizationId : null
217
+ };
218
+ });
219
+ }
220
+ get(id) {
221
+ return this.repository.proposal(id);
222
+ }
223
+ preview(changeSet) {
224
+ return previewArtifactChangeSet(this.storage.catalog.all(), changeSet, this.storage.catalog.supersededArtifactIds());
225
+ }
226
+ prepare(changeSetId, version) {
227
+ return this.storage.catalog.transaction(() => this.prepareInTransaction(changeSetId, version));
228
+ }
229
+ /** Prepares inside a transaction owned by the caller. */
230
+ prepareInTransaction(changeSetId, version) {
231
+ const proposal = this.repository.proposal(changeSetId);
232
+ if (proposal.version !== version) {
233
+ throw new ArtifactConflictError('Change set version changed.');
234
+ }
235
+ const manifest = this.manifest(proposal);
236
+ const value = {
237
+ id: randomUUID(),
238
+ changeSetId,
239
+ version,
240
+ manifest,
241
+ manifestDigest: setDigest(manifest),
242
+ state: 'prepared',
243
+ validatorSessionId: null,
244
+ owner: null,
245
+ expiresAt: null
246
+ };
247
+ this.repository.appendValidation(value);
248
+ return value;
249
+ }
250
+ manifest(proposal) {
251
+ const preview = this.preview(proposal.changeSet);
252
+ if (preview.errors.length) {
253
+ throw new ArtifactConflictError(graphIssuesMessage(preview.errors, preview.expectedVersions));
254
+ }
255
+ const artifacts = preview.affectedIds.map(id => this.storage.readVerifiedFile(id).artifact);
256
+ return {
257
+ rulesVersion: VALIDATION_RULES_VERSION,
258
+ validationEpoch: this.storage.catalog.validationEpoch(),
259
+ changeDigest: proposal.changeDigest,
260
+ changeSet: proposal.changeSet,
261
+ artifacts,
262
+ requiredContentIds: artifacts.map(item => item.id)
263
+ };
264
+ }
265
+ /**
266
+ * Gives the next proposal version and manifest of a running validation when only
267
+ * the context of its set changed (ADR 0121). The caller owns the transaction.
268
+ * Returns undefined when the change needs a decision of the proposal author.
269
+ */
270
+ rebaseInTransaction(validation) {
271
+ const proposal = this.repository.proposal(validation.changeSetId);
272
+ if (proposal.version !== validation.version ||
273
+ validation.manifest.rulesVersion !== VALIDATION_RULES_VERSION) {
274
+ return undefined;
275
+ }
276
+ const subjects = new Set(proposal.changeSet.subjectIds);
277
+ const preview = this.preview(proposal.changeSet);
278
+ const decided = proposal.changeSet.supplementDispositions
279
+ .map(item => item.artifactId)
280
+ .sort();
281
+ if (!preview.errors.every(issue => REBASE_CODES.has(issue.code) &&
282
+ issue.artifactIds.every(id => !subjects.has(id))) ||
283
+ canonicalJson(preview.requiredSupplementDispositions) !==
284
+ canonicalJson(decided)) {
285
+ return undefined;
286
+ }
287
+ const command = proposal.changeSet.command;
288
+ // A historical command has no current proposal form; its validation stays stale.
289
+ if (command === 'submit_review') {
290
+ return undefined;
291
+ }
292
+ let next = proposal;
293
+ if (preview.errors.length) {
294
+ next = this.proposeInTransaction({
295
+ requestId: `validation-rebase:${validation.id}:${proposal.version}`,
296
+ id: proposal.id,
297
+ expectedVersion: proposal.version,
298
+ changeSet: {
299
+ ...proposal.changeSet,
300
+ command,
301
+ expectedVersions: preview.expectedVersions
302
+ }
303
+ }, REBASE_ACTOR);
304
+ }
305
+ const manifest = this.manifest(next);
306
+ return { proposal: next, manifest, digest: setDigest(manifest) };
307
+ }
308
+ assertFresh(validation) {
309
+ const proposal = this.repository.proposal(validation.changeSetId);
310
+ let digest;
311
+ try {
312
+ digest = setDigest(this.manifest(proposal));
313
+ }
314
+ catch (error) {
315
+ // A store change can make the change set invalid, for example a new accepted
316
+ // artifact without tasks. That is staleness too, so the caller sees one error.
317
+ if (!(error instanceof ArtifactConflictError)) {
318
+ throw error;
319
+ }
320
+ throw new ArtifactConflictError(`VALIDATION_STALE: manifest changed (${error.message}); prepare a new validation.`);
321
+ }
322
+ if (validation.manifest.rulesVersion !== VALIDATION_RULES_VERSION ||
323
+ proposal.version !== validation.version ||
324
+ digest !== validation.manifestDigest) {
325
+ throw new ArtifactConflictError('VALIDATION_STALE: manifest changed; prepare a new validation.');
326
+ }
327
+ return proposal;
328
+ }
329
+ checkAction(action) {
330
+ if (!Value.Check(ArtifactAuthorizationRequestBody, action)) {
331
+ throw new ArtifactInputError('Invalid authorization action.');
332
+ }
333
+ const validation = this.repository.validation(action.validationId);
334
+ const proposal = this.assertFresh(validation);
335
+ if (validation.state !== 'pass' ||
336
+ proposal.id !== action.changeSetId ||
337
+ proposal.version !== action.version) {
338
+ throw new ArtifactConflictError('A fresh matching validation pass is required.');
339
+ }
340
+ return {
341
+ actionDigest: proposal.changeDigest,
342
+ manifestDigest: validation.manifestDigest
343
+ };
344
+ }
345
+ commit(id, input, actor) {
346
+ if (!Value.Check(ArtifactLifecycleCommitBody, input)) {
347
+ throw new ArtifactInputError('Invalid commit request.');
348
+ }
349
+ const payloadHash = sha256Hex(canonicalJson({ id, input, actor }));
350
+ const result = this.storage.catalog.transaction(() => {
351
+ const replay = this.repository.commitRequest(input.requestId);
352
+ if (replay) {
353
+ if (replay.payloadHash !== payloadHash) {
354
+ throw new ArtifactConflictError('Commit request ID was reused.');
355
+ }
356
+ return replay.result;
357
+ }
358
+ const action = {
359
+ kind: 'lifecycle',
360
+ changeSetId: id,
361
+ version: input.version,
362
+ validationId: input.validationId
363
+ };
364
+ this.authorization.assert(input.authorizationId, artifactSessionId(actor), action);
365
+ const value = this.applyChangeSet(id, input.version, input.requestId, payloadHash, actor);
366
+ this.authorization.consume(input.authorizationId);
367
+ return value;
368
+ });
369
+ this.storage.pollChanges();
370
+ return result;
371
+ }
372
+ /**
373
+ * Commits a proposal on the strength of a stored prior consent instead of a
374
+ * session grant. The caller checks the consent; `inTransaction` writes the
375
+ * consumption and the operation result atomically with the status changes.
376
+ */
377
+ commitConfirmed(input, actor, inTransaction) {
378
+ const payloadHash = sha256Hex(canonicalJson({ input, actor }));
379
+ const result = this.storage.catalog.transaction(() => {
380
+ const replay = this.repository.commitRequest(input.requestId);
381
+ if (replay) {
382
+ if (replay.payloadHash !== payloadHash) {
383
+ throw new ArtifactConflictError('Commit request ID was reused.');
384
+ }
385
+ return replay.result;
386
+ }
387
+ this.checkAction({
388
+ kind: 'lifecycle',
389
+ changeSetId: input.changeSetId,
390
+ version: input.version,
391
+ validationId: input.validationId
392
+ });
393
+ const value = this.applyChangeSet(input.changeSetId, input.version, input.requestId, payloadHash, actor);
394
+ inTransaction();
395
+ return value;
396
+ });
397
+ this.storage.pollChanges();
398
+ return result;
399
+ }
400
+ applyChangeSet(id, version, requestId, payloadHash, actor) {
401
+ const proposal = this.repository.proposal(id);
402
+ const preview = this.preview(proposal.changeSet);
403
+ if (preview.errors.length) {
404
+ throw new ArtifactConflictError('Change set post-state is invalid.');
405
+ }
406
+ const updated = [];
407
+ for (const node of preview.postState) {
408
+ const previous = this.storage.get(node.id);
409
+ if (canonicalJson(node) === canonicalJson(previous))
410
+ continue;
411
+ const next = {
412
+ ...node,
413
+ metadataVersion: previous.metadataVersion + 1,
414
+ metadataUpdatedAt: new Date(this.now()).toISOString()
415
+ };
416
+ this.storage.catalog.appendMetadata(next, `lifecycle:${sha256Hex(canonicalJson([requestId, node.id]))}`, actor, 'metadata_updated');
417
+ updated.push({ id: next.id, metadataVersion: next.metadataVersion });
418
+ }
419
+ const value = {
420
+ storeId: this.storage.storeId,
421
+ changeSetId: id,
422
+ version,
423
+ affectedIds: updated.map(item => item.id),
424
+ metadataVersions: updated,
425
+ includedIn: preview.includedIn,
426
+ storeEpoch: this.storage.catalog.epoch()
427
+ };
428
+ const eventSeq = this.repository.appendCommit(requestId, payloadHash, value);
429
+ return { ...value, eventSeq };
430
+ }
431
+ }
@@ -0,0 +1,55 @@
1
+ import { errorMessage } from "../../shared/utils/error-message.js";
2
+ // Detect other writers and retry expired operation leases without device settings.
3
+ const MAINTENANCE_INTERVAL_MS = 2000;
4
+ export class ArtifactMaintenanceService {
5
+ options;
6
+ timer;
7
+ running = false;
8
+ // Last reported message per phase; a repeating failure is reported once.
9
+ reported = new Map();
10
+ constructor(options) {
11
+ this.options = options;
12
+ }
13
+ start() {
14
+ if (this.running)
15
+ return;
16
+ this.running = true;
17
+ // Reads verify every file, so a failed first pass must not block the server.
18
+ this.run(true);
19
+ if (!this.running)
20
+ return;
21
+ this.timer = setInterval(() => this.check(), MAINTENANCE_INTERVAL_MS);
22
+ this.timer.unref();
23
+ }
24
+ stop() {
25
+ this.running = false;
26
+ clearInterval(this.timer);
27
+ this.timer = undefined;
28
+ }
29
+ check() {
30
+ if (!this.running)
31
+ return;
32
+ this.run(false);
33
+ }
34
+ run(cleanupCommitted) {
35
+ this.attempt('recovery', () => this.options.storage.recover(cleanupCommitted));
36
+ // The error handler may stop the service.
37
+ if (!this.running)
38
+ return;
39
+ // A failed recovery must not hide changes committed by another process.
40
+ this.attempt('poll', () => this.options.storage.pollChanges());
41
+ }
42
+ attempt(phase, run) {
43
+ try {
44
+ run();
45
+ this.reported.delete(phase);
46
+ }
47
+ catch (error) {
48
+ const message = errorMessage(error);
49
+ if (this.reported.get(phase) === message)
50
+ return;
51
+ this.reported.set(phase, message);
52
+ this.options.onError(error, phase);
53
+ }
54
+ }
55
+ }