@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,646 @@
1
+ import { ArtifactSearchService } from "./artifact-search.service.js";
2
+ import { ArtifactLifecycleService } from "./artifact-lifecycle.service.js";
3
+ import { takeWithinBytes } from "../utils/take-within-bytes.js";
4
+ import { ArtifactTypeService } from "./artifact-type.service.js";
5
+ import { TaskService } from "./task.service.js";
6
+ import { ArtifactGraphService } from "./artifact-graph.service.js";
7
+ import { artifactIssues, graphIssuesMessage } from "./artifact-graph-validation.js";
8
+ import { ArtifactGraphRepository } from "../repositories/artifact-graph.repo.js";
9
+ import { TaskRepository } from "../repositories/task.repo.js";
10
+ import { normalizeArtifact } from "../../shared/artifacts/normalize.js";
11
+ import { ArtifactGraphFields } from "../../shared/artifacts/schemas.js";
12
+ import { randomUUID } from 'node:crypto';
13
+ import { stringify } from 'yaml';
14
+ import { Value } from '@sinclair/typebox/value';
15
+ import { ArtifactActorSchema, ArtifactMetadataInputSchema } from "../../shared/artifacts/schemas.js";
16
+ import { MAX_ARTIFACT_BODY_BYTES, MAX_ARTIFACT_BYTES, MAX_ARTIFACT_CHUNK_BYTES, MAX_ARTIFACT_METADATA_BYTES, MAX_ARTIFACT_PAGE_SIZE } from "../../shared/artifacts/constants.js";
17
+ import { isUuid } from "../../shared/utils/is-uuid.js";
18
+ import { ArtifactConflictError, ArtifactInputError, ArtifactIntegrityError, ArtifactNotFoundError, ArtifactTooLargeError } from "../errors/artifacts.js";
19
+ import { ArtifactCatalogRepository } from "../repositories/artifact-catalog.repo.js";
20
+ import { ArtifactFilesRepository } from "../repositories/artifact-files.repo.js";
21
+ import { sha256Hex } from "../utils/hash.js";
22
+ // Recover a stopped owner without taking an active writer's operation.
23
+ const OPERATION_LEASE_MS = 30_000;
24
+ // Fix the property order so equal identities give equal payload hashes.
25
+ function canonicalActor(actor) {
26
+ return { kind: actor.kind, id: actor.id };
27
+ }
28
+ // Compare code units, not locale rules, so hashes do not depend on the locale.
29
+ function compareText(left, right) {
30
+ if (left === right)
31
+ return 0;
32
+ return left < right ? -1 : 1;
33
+ }
34
+ // Task and relation lists are sets: their order must not change a request hash.
35
+ function sortedTaskIds(taskIds) {
36
+ return [...taskIds].sort(compareText);
37
+ }
38
+ function sortedRelations(relations) {
39
+ return relations
40
+ .map(relation => ({ kind: relation.kind, targetId: relation.targetId }))
41
+ .sort((left, right) => compareText(`${left.kind}:${left.targetId}`, `${right.kind}:${right.targetId}`));
42
+ }
43
+ function graphPayload(metadata) {
44
+ return {
45
+ ...(metadata.sourcePath === undefined
46
+ ? {}
47
+ : { sourcePath: metadata.sourcePath }),
48
+ ...(metadata.typeId === undefined ? {} : { typeId: metadata.typeId }),
49
+ ...(metadata.taskIds === undefined
50
+ ? {}
51
+ : { taskIds: sortedTaskIds(metadata.taskIds) }),
52
+ ...(metadata.relations === undefined
53
+ ? {}
54
+ : { relations: sortedRelations(metadata.relations) })
55
+ };
56
+ }
57
+ export class ArtifactStorageService {
58
+ search;
59
+ lifecycle;
60
+ files;
61
+ types;
62
+ tasks;
63
+ graph;
64
+ graphRepository;
65
+ definitions;
66
+ catalog;
67
+ owner = randomUUID();
68
+ options;
69
+ now;
70
+ observedEpoch;
71
+ constructor(options) {
72
+ this.options = options;
73
+ this.now = options.now ?? Date.now;
74
+ this.files = new ArtifactFilesRepository(options.repositoryPath, options.prefix);
75
+ this.catalog = new ArtifactCatalogRepository(options.db, this.files.repositoryPath, options.prefix);
76
+ this.search = new ArtifactSearchService(this, options.db);
77
+ this.catalog.onMetadata = artifact => this.search.index(artifact);
78
+ this.definitions = new TaskRepository(options.db, this.storeId);
79
+ const definitions = {
80
+ repository: this.definitions,
81
+ catalog: this.catalog,
82
+ onChange: () => this.pollChanges(),
83
+ now: this.now
84
+ };
85
+ this.types = new ArtifactTypeService(definitions);
86
+ this.tasks = new TaskService(definitions);
87
+ this.graphRepository = new ArtifactGraphRepository(options.db, this.catalog);
88
+ this.graph = new ArtifactGraphService(this);
89
+ this.lifecycle = new ArtifactLifecycleService(this, options.db, this.now);
90
+ this.observedEpoch = this.catalog.epoch();
91
+ }
92
+ get storeId() {
93
+ return this.catalog.storeId;
94
+ }
95
+ validate(requestId, metadata, actor) {
96
+ if (typeof requestId !== 'string' ||
97
+ requestId.length < 1 ||
98
+ requestId.length > 128) {
99
+ throw new ArtifactInputError('Invalid artifact request identifier.');
100
+ }
101
+ if (!Value.Check(ArtifactMetadataInputSchema, metadata) ||
102
+ !metadata.title.trim()) {
103
+ throw new ArtifactInputError('Invalid artifact metadata.');
104
+ }
105
+ if (!Value.Check(ArtifactActorSchema, actor)) {
106
+ throw new ArtifactInputError('Invalid artifact actor.');
107
+ }
108
+ if (Buffer.byteLength(JSON.stringify(metadata)) > MAX_ARTIFACT_METADATA_BYTES) {
109
+ throw new ArtifactTooLargeError();
110
+ }
111
+ }
112
+ fileName(id) {
113
+ if (!isUuid(id)) {
114
+ throw new ArtifactInputError('Invalid artifact identifier.');
115
+ }
116
+ return `${this.files.prefix}${id}.md`;
117
+ }
118
+ document(artifact, body) {
119
+ const header = stringify({
120
+ schema_version: 1,
121
+ store_id: this.storeId,
122
+ artifact_id: artifact.id,
123
+ title: artifact.title,
124
+ ...(artifact.sourcePath === undefined
125
+ ? {}
126
+ : { source_path: artifact.sourcePath }),
127
+ type_id: artifact.typeId,
128
+ status: artifact.status,
129
+ created_at: artifact.createdAt,
130
+ metadata_updated_at: artifact.createdAt,
131
+ task_ids: artifact.taskIds ?? [],
132
+ tags: artifact.tags,
133
+ relations: (artifact.relations ?? []).map(relation => ({
134
+ kind: relation.kind,
135
+ target_id: relation.targetId
136
+ })),
137
+ revision_of: artifact.revisionOf ?? []
138
+ });
139
+ return Buffer.from(`---\n${header}---\n${body}`, 'utf8');
140
+ }
141
+ create(input, suppliedActor, turn) {
142
+ return this.createArtifact(input, suppliedActor, undefined, turn);
143
+ }
144
+ // Recovery must wait for the caller to recheck its scope and join this commit.
145
+ createWithCommit(input, suppliedActor, commit) {
146
+ return this.createArtifact(input, suppliedActor, commit);
147
+ }
148
+ createArtifact(input, suppliedActor, commit, turn) {
149
+ this.validate(input.requestId, input.metadata, suppliedActor);
150
+ const actor = canonicalActor(suppliedActor);
151
+ if (!Value.Check(ArtifactGraphFields.revisionOf, input.revisionOf ?? [])) {
152
+ throw new ArtifactInputError('Invalid revision lineage.');
153
+ }
154
+ if (typeof input.body !== 'string' || input.body.includes('\0')) {
155
+ throw new ArtifactInputError('Artifact body must be valid UTF-8 text without NUL.');
156
+ }
157
+ if (Buffer.byteLength(input.body) > MAX_ARTIFACT_BODY_BYTES) {
158
+ throw new ArtifactTooLargeError();
159
+ }
160
+ if (Buffer.from(input.body).toString('utf8') !== input.body) {
161
+ throw new ArtifactInputError('Artifact body must be valid UTF-8 text.');
162
+ }
163
+ const payloadHash = sha256Hex(JSON.stringify({
164
+ kind: 'create',
165
+ body: input.body,
166
+ title: input.metadata.title,
167
+ tags: input.metadata.tags,
168
+ ...graphPayload(input.metadata),
169
+ ...(input.revisionOf === undefined
170
+ ? {}
171
+ : { revisionOf: input.revisionOf }),
172
+ actor
173
+ }));
174
+ const operation = this.catalog.transaction(() => {
175
+ this.definitions.assertArtifactRequest(input.requestId);
176
+ const previous = this.catalog.operation(input.requestId);
177
+ if (previous) {
178
+ this.checkReplay(previous, payloadHash);
179
+ if (previous.data.callerCommit && !commit) {
180
+ throw new ArtifactConflictError('This artifact requires its caller commit.');
181
+ }
182
+ if (previous.state !== 'committed') {
183
+ this.catalog.claim(input.requestId, this.owner, this.now(), OPERATION_LEASE_MS);
184
+ }
185
+ return previous;
186
+ }
187
+ // Resolve only new operations; retries retain the originally reserved task IDs.
188
+ const inherited = input.metadata.taskIds === undefined &&
189
+ actor.kind === 'agent' &&
190
+ turn?.sessionId === actor.id &&
191
+ turn.taskIds.length === 1;
192
+ const taskIds = input.metadata.taskIds ?? (inherited ? turn.taskIds : []);
193
+ const now = new Date(this.now()).toISOString();
194
+ const artifact = {
195
+ storeId: this.storeId,
196
+ id: randomUUID(),
197
+ sha256: '',
198
+ bytes: 0,
199
+ bodyStartLine: 0,
200
+ createdAt: now,
201
+ metadataVersion: 1,
202
+ metadataUpdatedAt: now,
203
+ title: input.metadata.title,
204
+ ...(input.metadata.sourcePath === undefined
205
+ ? {}
206
+ : { sourcePath: input.metadata.sourcePath }),
207
+ tags: [...input.metadata.tags],
208
+ typeId: input.metadata.typeId ?? 'generic',
209
+ taskIds: [...taskIds],
210
+ relations: structuredClone(input.metadata.relations ?? []),
211
+ revisionOf: [...(input.revisionOf ?? [])],
212
+ status: 'draft'
213
+ };
214
+ this.validateGraph(artifact);
215
+ const content = this.document(artifact, input.body);
216
+ if (content.length > MAX_ARTIFACT_BYTES)
217
+ throw new ArtifactTooLargeError();
218
+ const headerBytes = content.length - Buffer.byteLength(input.body);
219
+ artifact.sha256 = sha256Hex(content);
220
+ artifact.bytes = content.length;
221
+ artifact.bodyStartLine = content
222
+ .subarray(0, headerBytes)
223
+ .toString('utf8')
224
+ .split('\n').length;
225
+ const fileName = this.fileName(artifact.id);
226
+ const data = {
227
+ artifact,
228
+ taskBinding: inherited
229
+ ? turn.source === 'turn_activation'
230
+ ? {
231
+ source: 'turn_activation',
232
+ sessionId: turn.sessionId,
233
+ turnId: turn.turnId,
234
+ assignmentVersion: turn.assignmentVersion,
235
+ activationRequestId: turn.activationRequestId
236
+ }
237
+ : {
238
+ source: 'turn_assignment',
239
+ sessionId: turn.sessionId,
240
+ turnId: turn.turnId,
241
+ assignmentVersion: turn.assignmentVersion
242
+ }
243
+ : {
244
+ source: input.metadata.taskIds === undefined
245
+ ? 'legacy_default'
246
+ : 'explicit'
247
+ },
248
+ actor: { ...actor },
249
+ fileName,
250
+ tempName: `${fileName}.${randomUUID()}.tmp`,
251
+ ...(commit ? { callerCommit: true } : {})
252
+ };
253
+ const created = {
254
+ requestId: input.requestId,
255
+ payloadHash,
256
+ artifactId: artifact.id,
257
+ kind: 'create',
258
+ data,
259
+ state: 'pending'
260
+ };
261
+ this.catalog.reserve(created, now);
262
+ this.catalog.claim(input.requestId, this.owner, this.now(), OPERATION_LEASE_MS);
263
+ return created;
264
+ });
265
+ if (operation.state === 'committed') {
266
+ this.files.verify(operation.data.fileName, operation.data.artifact.sha256, operation.data.artifact.bytes);
267
+ const artifact = normalizeArtifact(operation.data.artifact);
268
+ if (commit)
269
+ this.catalog.transaction(() => commit(artifact));
270
+ return artifact;
271
+ }
272
+ this.checkpoint('reserved', operation.data);
273
+ const content = this.document(operation.data.artifact, input.body);
274
+ if (sha256Hex(content) !== operation.data.artifact.sha256) {
275
+ throw new ArtifactIntegrityError('Reserved artifact content cannot be reproduced.');
276
+ }
277
+ return this.finish(operation, content, commit);
278
+ }
279
+ checkReplay(operation, payloadHash) {
280
+ if (operation.payloadHash !== payloadHash) {
281
+ throw new ArtifactConflictError('Request identifier was already used for a different operation.');
282
+ }
283
+ if (operation.state === 'failed') {
284
+ throw new ArtifactIntegrityError('Artifact operation failed; inspect storage diagnostics.');
285
+ }
286
+ }
287
+ checkpoint(phase, data) {
288
+ this.options.checkpoint?.(phase, data);
289
+ }
290
+ finish(operation, supplied, commit) {
291
+ const data = operation.data;
292
+ const { artifact } = data;
293
+ this.catalog.assertOwner(operation.requestId, this.owner, this.now());
294
+ const final = this.files.read(data.fileName);
295
+ if (final) {
296
+ this.files.verify(data.fileName, artifact.sha256, artifact.bytes);
297
+ this.files.syncFile(data.fileName);
298
+ }
299
+ else {
300
+ const temp = this.files.read(data.tempName);
301
+ if (!temp ||
302
+ temp.length !== artifact.bytes ||
303
+ sha256Hex(temp) !== artifact.sha256) {
304
+ if (!supplied) {
305
+ throw new ArtifactInputError('Artifact content must be supplied again.');
306
+ }
307
+ this.catalog.assertOwner(operation.requestId, this.owner, this.now());
308
+ if (temp)
309
+ this.files.removeTemp(data.tempName);
310
+ this.files.writeTemp(data.tempName, supplied);
311
+ }
312
+ this.checkpoint('temp_synced', data);
313
+ this.catalog.assertOwner(operation.requestId, this.owner, this.now());
314
+ this.files.publish(data.tempName, data.fileName, artifact.sha256, artifact.bytes);
315
+ }
316
+ this.checkpoint('published', data);
317
+ this.checkpoint('before_commit', data);
318
+ this.catalog.transaction(() => {
319
+ this.catalog.assertOwner(operation.requestId, this.owner, this.now());
320
+ this.files.verify(data.fileName, artifact.sha256, artifact.bytes);
321
+ commit?.(normalizeArtifact(artifact));
322
+ this.catalog.insertArtifact(data);
323
+ this.graphRepository.appendLineage(normalizeArtifact(artifact));
324
+ this.catalog.appendMetadata(normalizeArtifact(artifact), operation.requestId, data.actor, 'created');
325
+ this.catalog.transition(operation.requestId, 'committed', new Date(this.now()).toISOString());
326
+ this.catalog.release(operation.requestId, this.owner);
327
+ });
328
+ this.checkpoint('after_commit', data);
329
+ this.files.removeTemp(data.tempName);
330
+ this.pollChanges();
331
+ return normalizeArtifact(artifact);
332
+ }
333
+ recover(cleanupCommitted = true) {
334
+ const result = {
335
+ recovered: [],
336
+ awaitingRetry: [],
337
+ failed: [],
338
+ busy: []
339
+ };
340
+ const failures = [];
341
+ for (const operation of this.catalog.operations(cleanupCommitted)) {
342
+ try {
343
+ this.recoverOne(operation, result);
344
+ }
345
+ catch (error) {
346
+ // One failed operation must not block the operations after it.
347
+ const cause = error instanceof Error ? error : new Error(String(error));
348
+ failures.push(new Error(`Artifact operation ${operation.requestId}: ${cause.message}`, {
349
+ cause
350
+ }));
351
+ }
352
+ }
353
+ if (failures.length === 1)
354
+ throw failures[0].cause;
355
+ if (failures.length > 1) {
356
+ throw new AggregateError(failures.map(failure => failure.cause), failures.map(failure => failure.message).join('; '));
357
+ }
358
+ return result;
359
+ }
360
+ recoverOne(listed, result) {
361
+ let operation = listed;
362
+ if (operation.state === 'failed') {
363
+ result.failed.push(operation.artifactId);
364
+ return;
365
+ }
366
+ if (operation.state === 'committed') {
367
+ this.files.removeTemp(operation.data.tempName);
368
+ return;
369
+ }
370
+ try {
371
+ operation = this.catalog.transaction(() => {
372
+ const current = this.catalog.operation(operation.requestId);
373
+ if (current.state !== 'committed' && current.state !== 'failed') {
374
+ this.catalog.claim(current.requestId, this.owner, this.now(), OPERATION_LEASE_MS);
375
+ }
376
+ return current;
377
+ });
378
+ if (operation.state === 'committed' || operation.state === 'failed') {
379
+ return;
380
+ }
381
+ }
382
+ catch (error) {
383
+ if (!(error instanceof ArtifactConflictError))
384
+ throw error;
385
+ result.busy.push(operation.artifactId);
386
+ return;
387
+ }
388
+ if (operation.data.callerCommit) {
389
+ this.catalog.transaction(() => {
390
+ this.catalog.assertOwner(operation.requestId, this.owner, this.now());
391
+ if (operation.state !== 'awaiting_retry') {
392
+ this.catalog.transition(operation.requestId, 'awaiting_retry', new Date(this.now()).toISOString());
393
+ }
394
+ this.catalog.release(operation.requestId, this.owner);
395
+ });
396
+ result.awaitingRetry.push(operation.artifactId);
397
+ return;
398
+ }
399
+ try {
400
+ this.finish(operation);
401
+ result.recovered.push(operation.artifactId);
402
+ }
403
+ catch (error) {
404
+ if (this.catalog.operation(operation.requestId)?.state === 'committed') {
405
+ throw error;
406
+ }
407
+ if (!(error instanceof ArtifactInputError) &&
408
+ !(error instanceof ArtifactIntegrityError)) {
409
+ try {
410
+ this.catalog.release(operation.requestId, this.owner);
411
+ }
412
+ catch {
413
+ // The original failure is the useful one; the lease expires by itself.
414
+ }
415
+ throw error;
416
+ }
417
+ const state = error instanceof ArtifactInputError ? 'awaiting_retry' : 'failed';
418
+ this.catalog.transaction(() => {
419
+ this.catalog.assertOwner(operation.requestId, this.owner, this.now());
420
+ if (operation.state !== state) {
421
+ this.catalog.transition(operation.requestId, state, new Date(this.now()).toISOString());
422
+ }
423
+ this.catalog.release(operation.requestId, this.owner);
424
+ });
425
+ result[state === 'awaiting_retry' ? 'awaitingRetry' : 'failed'].push(operation.artifactId);
426
+ }
427
+ }
428
+ diagnostics() {
429
+ const operations = this.catalog.operations();
430
+ const known = new Set(operations.flatMap(operation => [
431
+ operation.data.fileName,
432
+ operation.data.tempName
433
+ ]));
434
+ return {
435
+ orphanFiles: this.files.names().filter(name => !known.has(name)),
436
+ operations: operations.map(operation => ({
437
+ artifactId: operation.artifactId,
438
+ state: operation.state
439
+ }))
440
+ };
441
+ }
442
+ get(id) {
443
+ this.fileName(id);
444
+ const artifact = this.catalog.get(id);
445
+ if (!artifact)
446
+ throw new ArtifactNotFoundError();
447
+ return artifact;
448
+ }
449
+ readVerifiedFile(id) {
450
+ const artifact = this.get(id);
451
+ const content = this.files.verify(this.fileName(id), artifact.sha256, artifact.bytes);
452
+ return { artifact, content };
453
+ }
454
+ readContent(id, offset = 0, limit = MAX_ARTIFACT_CHUNK_BYTES) {
455
+ if (!Number.isSafeInteger(offset) ||
456
+ offset < 0 ||
457
+ !Number.isInteger(limit) ||
458
+ limit < 4 ||
459
+ limit > MAX_ARTIFACT_CHUNK_BYTES) {
460
+ throw new ArtifactInputError('Invalid content range.');
461
+ }
462
+ const { artifact, content } = this.readVerifiedFile(id);
463
+ if (offset > content.length ||
464
+ (offset < content.length && (content[offset] & 0xc0) === 0x80)) {
465
+ throw new ArtifactInputError('Content offset must be a UTF-8 boundary.');
466
+ }
467
+ let end = Math.min(offset + limit, content.length);
468
+ while (end < content.length && (content[end] & 0xc0) === 0x80)
469
+ end--;
470
+ return {
471
+ storeId: this.storeId,
472
+ id,
473
+ sha256: artifact.sha256,
474
+ bodyStartLine: artifact.bodyStartLine,
475
+ totalBytes: content.length,
476
+ offset,
477
+ nextOffset: end < content.length ? end : null,
478
+ text: content.subarray(offset, end).toString('utf8')
479
+ };
480
+ }
481
+ list(limit = 50, cursor) {
482
+ return this.catalog.readSnapshot(() => this.listSnapshot(limit, cursor));
483
+ }
484
+ listSnapshot(limit, cursor) {
485
+ this.assertLimit(limit);
486
+ const epoch = this.catalog.epoch();
487
+ let after;
488
+ if (cursor) {
489
+ let decoded;
490
+ try {
491
+ decoded = JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8'));
492
+ }
493
+ catch {
494
+ throw new ArtifactInputError('Invalid artifact cursor.');
495
+ }
496
+ if (!decoded ||
497
+ decoded.storeId !== this.storeId ||
498
+ typeof decoded.createdAt !== 'string' ||
499
+ !isUuid(decoded.id)) {
500
+ throw new ArtifactInputError('Invalid artifact cursor.');
501
+ }
502
+ if (decoded.epoch !== epoch) {
503
+ throw new ArtifactConflictError('Artifact catalog changed. Restart pagination.');
504
+ }
505
+ after = { createdAt: decoded.createdAt, id: decoded.id };
506
+ }
507
+ const rows = this.catalog.list(limit + 1, after);
508
+ const items = takeWithinBytes(rows, limit);
509
+ const last = items.at(-1);
510
+ const nextCursor = rows.length > items.length && last
511
+ ? Buffer.from(JSON.stringify({
512
+ storeId: this.storeId,
513
+ epoch,
514
+ createdAt: last.createdAt,
515
+ id: last.id
516
+ })).toString('base64url')
517
+ : null;
518
+ return { storeId: this.storeId, storeEpoch: epoch, items, nextCursor };
519
+ }
520
+ history(id, afterSeq = 0, limit = 50) {
521
+ this.get(id);
522
+ this.assertLimit(limit);
523
+ if (!Number.isSafeInteger(afterSeq) || afterSeq < 0) {
524
+ throw new ArtifactInputError('Invalid history sequence.');
525
+ }
526
+ const rows = this.catalog.history(id, afterSeq, limit + 1);
527
+ const items = takeWithinBytes(rows, limit);
528
+ return {
529
+ storeId: this.storeId,
530
+ items,
531
+ nextAfterSeq: rows.length > items.length ? items.at(-1).seq : null
532
+ };
533
+ }
534
+ updateMetadata(id, input, suppliedActor) {
535
+ this.validate(input.requestId, input.metadata, suppliedActor);
536
+ const actor = canonicalActor(suppliedActor);
537
+ this.fileName(id);
538
+ if (!Number.isSafeInteger(input.expectedVersion) ||
539
+ input.expectedVersion < 1) {
540
+ throw new ArtifactInputError('Invalid metadata version.');
541
+ }
542
+ const payloadHash = sha256Hex(JSON.stringify({
543
+ kind: 'metadata',
544
+ id,
545
+ expectedVersion: input.expectedVersion,
546
+ title: input.metadata.title,
547
+ tags: input.metadata.tags,
548
+ ...graphPayload(input.metadata),
549
+ actor
550
+ }));
551
+ const result = this.catalog.transaction(() => {
552
+ this.definitions.assertArtifactRequest(input.requestId);
553
+ const existing = this.catalog.operation(input.requestId);
554
+ if (existing) {
555
+ this.checkReplay(existing, payloadHash);
556
+ return normalizeArtifact(existing.data.artifact);
557
+ }
558
+ const previous = this.get(id);
559
+ if (previous.metadataVersion !== input.expectedVersion) {
560
+ throw new ArtifactConflictError('Artifact metadata version changed.');
561
+ }
562
+ this.files.verify(this.fileName(id), previous.sha256, previous.bytes);
563
+ const updated = {
564
+ ...previous,
565
+ ...(input.metadata.sourcePath === undefined
566
+ ? {}
567
+ : { sourcePath: input.metadata.sourcePath }),
568
+ title: input.metadata.title,
569
+ tags: [...input.metadata.tags],
570
+ typeId: input.metadata.typeId ?? previous.typeId,
571
+ taskIds: [...(input.metadata.taskIds ?? previous.taskIds)],
572
+ relations: structuredClone(input.metadata.relations ?? previous.relations),
573
+ metadataVersion: previous.metadataVersion + 1,
574
+ metadataUpdatedAt: new Date(this.now()).toISOString()
575
+ };
576
+ if (previous.status === 'accepted' &&
577
+ (JSON.stringify(sortedRelations(updated.relations)) !==
578
+ JSON.stringify(sortedRelations(previous.relations)) ||
579
+ JSON.stringify(sortedTaskIds(updated.taskIds)) !==
580
+ JSON.stringify(sortedTaskIds(previous.taskIds)))) {
581
+ throw new ArtifactConflictError('Active graph changes require a validated change set.');
582
+ }
583
+ this.validateGraph(updated, previous);
584
+ this.catalog.reserve({
585
+ requestId: input.requestId,
586
+ kind: 'metadata',
587
+ payloadHash,
588
+ artifactId: id,
589
+ data: {
590
+ artifact: updated,
591
+ actor,
592
+ fileName: this.fileName(id),
593
+ tempName: ''
594
+ }
595
+ }, updated.metadataUpdatedAt);
596
+ this.catalog.appendMetadata(updated, input.requestId, actor, 'metadata_updated');
597
+ this.catalog.transition(input.requestId, 'committed', updated.metadataUpdatedAt);
598
+ return updated;
599
+ });
600
+ this.pollChanges();
601
+ return result;
602
+ }
603
+ // A missing reference in the request body is bad input, not a missing resource.
604
+ reference(read, kind, id) {
605
+ try {
606
+ return read();
607
+ }
608
+ catch (error) {
609
+ if (error instanceof ArtifactNotFoundError) {
610
+ throw new ArtifactInputError(`Unknown artifact ${kind} "${id}".`);
611
+ }
612
+ throw error;
613
+ }
614
+ }
615
+ validateGraph(artifact, previous) {
616
+ const type = this.reference(() => this.types.get(artifact.typeId), 'type', artifact.typeId);
617
+ if (!type.active && (!previous || previous.typeId !== artifact.typeId)) {
618
+ throw new ArtifactInputError('Artifact type is inactive.');
619
+ }
620
+ for (const taskId of artifact.taskIds) {
621
+ this.reference(() => this.tasks.get(taskId), 'task', taskId);
622
+ }
623
+ const issues = artifactIssues(artifact, id => this.catalog.get(id), this.storeId);
624
+ if (issues.length) {
625
+ throw new ArtifactInputError(graphIssuesMessage(issues));
626
+ }
627
+ }
628
+ assertLimit(limit) {
629
+ if (!Number.isInteger(limit) ||
630
+ limit < 1 ||
631
+ limit > MAX_ARTIFACT_PAGE_SIZE) {
632
+ throw new ArtifactInputError('Invalid page size.');
633
+ }
634
+ }
635
+ pollChanges() {
636
+ const epoch = this.catalog.epoch();
637
+ if (epoch === this.observedEpoch)
638
+ return;
639
+ this.observedEpoch = epoch;
640
+ this.options.eventBus?.publish({
641
+ type: 'artifacts:changed',
642
+ storeId: this.storeId,
643
+ storeEpoch: epoch
644
+ });
645
+ }
646
+ }