@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,56 @@
1
+ export function promptToPiMessage(blocks) {
2
+ let message = '';
3
+ const images = [];
4
+ for (const b of blocks) {
5
+ switch (b.type) {
6
+ case 'text':
7
+ message += b.text;
8
+ break;
9
+ case 'resource_link':
10
+ // A lightweight, human-readable hint for the LLM.
11
+ message += `\n[Context] ${b.uri}`;
12
+ break;
13
+ case 'image': {
14
+ // pi expects base64 image bytes in `data` without a data-url prefix.
15
+ images.push({
16
+ type: 'image',
17
+ mimeType: b.mimeType,
18
+ data: b.data
19
+ });
20
+ break;
21
+ }
22
+ case 'resource': {
23
+ // Clients should not send this if embeddedContext=false, but be resilient.
24
+ const r = b.resource;
25
+ const uri = typeof r?.uri === 'string' ? r.uri : '(unknown)';
26
+ if ('text' in r && typeof r.text === 'string') {
27
+ // TextResourceContents
28
+ const mime = typeof r?.mimeType === 'string' ? r.mimeType : 'text/plain';
29
+ message += `\n[Embedded Context] ${uri} (${mime})\n${r.text}`;
30
+ }
31
+ else if ('blob' in r && typeof r.blob === 'string') {
32
+ // BlobResourceContents
33
+ const mime = typeof r?.mimeType === 'string'
34
+ ? r.mimeType
35
+ : 'application/octet-stream';
36
+ const bytes = Buffer.byteLength(r.blob, 'base64');
37
+ message += `\n[Embedded Context] ${uri} (${mime}, ${bytes} bytes)`;
38
+ }
39
+ else {
40
+ message += `\n[Embedded Context] ${uri}`;
41
+ }
42
+ break;
43
+ }
44
+ case 'audio': {
45
+ // Not supported by pi. Provide a marker so we don't silently drop context.
46
+ const bytes = Buffer.byteLength(b.data, 'base64');
47
+ message += `\n[Audio] (${b.mimeType}, ${bytes} bytes) not supported by pi-acp`;
48
+ break;
49
+ }
50
+ default:
51
+ // Ignore unknown block types for now.
52
+ break;
53
+ }
54
+ }
55
+ return { message, images };
56
+ }
@@ -1,3 +1,13 @@
1
+ import { artifactActionRoutes } from "./routes/artifact-actions.js";
2
+ import { artifactTransitionRoutes } from "./routes/artifact-transitions.js";
3
+ import { addressUrl } from "../shared/network/address-url.js";
4
+ import { artifactAgentRoutes } from "./routes/artifact-agent.js";
5
+ import { ForbiddenError } from "./errors/http.js";
6
+ import { artifactDefinitionRoutes } from "./routes/artifact-definitions.js";
7
+ import { DEFAULT_DB_PREFIX } from "../shared/app/storage-constants.js";
8
+ import { artifactRoutes } from "./routes/artifacts.js";
9
+ import { ArtifactStorageService } from "./services/artifact-storage.service.js";
10
+ import { ArtifactMaintenanceService } from "./services/artifact-maintenance.service.js";
1
11
  import { watchNotes } from "./services/notes-watcher.service.js";
2
12
  import { notesRoutes } from "./routes/notes.js";
3
13
  import { NotesService } from "./services/notes.service.js";
@@ -49,8 +59,15 @@ const DEVICE_SWEEP_MS = 60 * 60 * 1000;
49
59
  // also when another server process on the same database revoked it.
50
60
  // One indexed SELECT per connected device.
51
61
  const DEVICE_RECHECK_MS = 2000;
62
+ // A wildcard bind accepts loopback connections; the MCP process connects there.
63
+ const WILDCARD_LOOPBACK = {
64
+ '::': '::1',
65
+ '0.0.0.0': '127.0.0.1'
66
+ };
52
67
  export function buildApp(options = {}) {
53
68
  const app = Fastify({
69
+ // Logging is off. Redact the Authorization header before you turn it on:
70
+ // it carries the agent session key (ADR 0096).
54
71
  logger: false,
55
72
  forceCloseConnections: true,
56
73
  ajv: {
@@ -62,6 +79,7 @@ export function buildApp(options = {}) {
62
79
  ]
63
80
  }
64
81
  });
82
+ const allowedHosts = new AllowedHosts(options.allowedHosts);
65
83
  const repositoryPath = options.repositoryPath ?? process.cwd();
66
84
  const db = options.db ?? createTestDatabase();
67
85
  const eventBus = options.eventBus ?? createEventBus();
@@ -70,6 +88,8 @@ export function buildApp(options = {}) {
70
88
  onChange: () => eventBus.publish({ type: 'devices:changed' })
71
89
  });
72
90
  const deviceConnections = new DeviceConnections(devices);
91
+ // Set after the access hook exists, because the registry needs the listening server address.
92
+ let agentRegistry;
73
93
  devices.removeExpired();
74
94
  const sweep = setInterval(() => devices.removeExpired(), DEVICE_SWEEP_MS);
75
95
  sweep.unref();
@@ -92,7 +112,16 @@ export function buildApp(options = {}) {
92
112
  app.register(fastifyPlugin(async (root) => {
93
113
  root.addHook('onRequest', createAccessHook({
94
114
  devices,
95
- allowedHosts: new AllowedHosts(options.allowedHosts)
115
+ allowedHosts,
116
+ authorizeArtifactAgent(request, reply) {
117
+ const id = agentRegistry?.authenticateArtifactKey(request.headers.authorization);
118
+ if (!id || !agentRegistry) {
119
+ throw new ForbiddenError('Artifact session access denied.');
120
+ }
121
+ const release = agentRegistry.trackArtifactRequest(id, () => reply.raw.destroy());
122
+ reply.raw.once('finish', release);
123
+ reply.raw.once('close', release);
124
+ }
96
125
  }));
97
126
  }));
98
127
  // The spec collects routes as they are added, so it comes before them.
@@ -120,6 +149,48 @@ export function buildApp(options = {}) {
120
149
  watcher.close();
121
150
  });
122
151
  }
152
+ const artifacts = options.artifacts ??
153
+ new ArtifactStorageService({
154
+ db,
155
+ repositoryPath,
156
+ prefix: options.artifactPrefix ?? DEFAULT_DB_PREFIX,
157
+ eventBus
158
+ });
159
+ app.register(artifactRoutes, { service: artifacts });
160
+ app.register(artifactActionRoutes, {
161
+ service: artifacts.lifecycle,
162
+ recheckAccess: createAccessHook({ devices, allowedHosts })
163
+ });
164
+ artifacts.lifecycle.transitions.setActorGuard(actor => actor.id === 'local' || devices.isTrusted(actor.id));
165
+ artifacts.lifecycle.transitions.setErrorHandler(error => app.log.error({ err: error }, 'Artifact transition work failed'));
166
+ if (options.onValidationDebug) {
167
+ artifacts.lifecycle.jobs.setDebug(options.onValidationDebug);
168
+ }
169
+ app.register(artifactTransitionRoutes, {
170
+ service: artifacts.lifecycle.transitions,
171
+ recheckAccess: createAccessHook({ devices, allowedHosts })
172
+ });
173
+ app.addHook('onReady', async () => {
174
+ artifacts.lifecycle.jobs.recover();
175
+ artifacts.lifecycle.transitions.start();
176
+ });
177
+ app.addHook('onClose', async () => {
178
+ artifacts.lifecycle.transitions.stop();
179
+ artifacts.lifecycle.jobs.close();
180
+ });
181
+ app.register(artifactDefinitionRoutes, { storage: artifacts });
182
+ const artifactMaintenance = new ArtifactMaintenanceService({
183
+ storage: artifacts,
184
+ onError: (error, phase) => {
185
+ app.log.error({ err: error, phase }, 'Artifact maintenance failed');
186
+ }
187
+ });
188
+ app.addHook('onReady', async () => {
189
+ artifactMaintenance.start();
190
+ });
191
+ app.addHook('onClose', async () => {
192
+ artifactMaintenance.stop();
193
+ });
123
194
  app.register(diffRoutes, { repositoryPath });
124
195
  app.register(gitRoutes, { repositoryPath });
125
196
  app.register(projectFileRoutes, { repositoryPath });
@@ -148,12 +219,31 @@ export function buildApp(options = {}) {
148
219
  });
149
220
  }
150
221
  if (options.agentPresets) {
222
+ const registry = createSessionRegistry({
223
+ presets: options.agentPresets,
224
+ connections: deviceConnections,
225
+ artifactUrl: () => {
226
+ const address = app.server.address();
227
+ // Before listen there is no address; only tests that skip listen get here.
228
+ if (!address)
229
+ return undefined;
230
+ if (typeof address === 'string') {
231
+ throw new Error(`Artifact tools need a TCP address, but the server listens on "${address}".`);
232
+ }
233
+ return `${addressUrl(WILDCARD_LOOPBACK[address.address] ?? address.address, address.port)}/`;
234
+ },
235
+ repositoryPath,
236
+ taskIds: sessionId => artifacts.definitions.assignment(sessionId).taskIds,
237
+ taskAssignment: sessionId => artifacts.definitions.assignment(sessionId)
238
+ });
239
+ agentRegistry = registry;
240
+ app.register(artifactAgentRoutes, { storage: artifacts, registry });
241
+ artifacts.tasks.attachSessions(registry);
242
+ artifacts.lifecycle.attachSessions(registry);
151
243
  app.register(agentRoutes, {
244
+ artifacts,
152
245
  notes,
153
- registry: createSessionRegistry({
154
- presets: options.agentPresets,
155
- repositoryPath
156
- }),
246
+ registry,
157
247
  db,
158
248
  repositoryPath,
159
249
  eventBus,
@@ -1,3 +1,4 @@
1
+ import { localPiLaunch } from "../adapters/pi-acp/launch.js";
1
2
  import { isSystemError } from "../utils/system-error.js";
2
3
  import { readFileSync } from 'node:fs';
3
4
  import { Type } from '@sinclair/typebox';
@@ -5,10 +6,8 @@ import { Value } from '@sinclair/typebox/value';
5
6
  import { isOnPath } from "../utils/executable.js";
6
7
  import { isRecord } from "../../shared/utils/guards.js";
7
8
  /**
8
- * Packages are pinned to the versions the ADR 0023 spike was run against —
9
- * `npx -y` otherwise executes whatever was published last. Cursor and Codex
10
- * are not verified yet (Codex's adapter moved to the agentclientprotocol org
11
- * after zed-industries/codex-acp was archived) and stay unpinned.
9
+ * Pin external adapters where verified. The local pi bridge uses the installed runtime.
10
+ * Cursor and Codex keep their existing launch configuration.
12
11
  */
13
12
  export const DEFAULT_AGENT_PRESETS = [
14
13
  {
@@ -21,8 +20,7 @@ export const DEFAULT_AGENT_PRESETS = [
21
20
  {
22
21
  id: 'pi',
23
22
  title: 'pi',
24
- command: 'npx',
25
- args: ['-y', 'pi-acp@0.0.34'],
23
+ ...localPiLaunch(),
26
24
  requires: 'pi',
27
25
  autoApprovesEdits: true
28
26
  },
@@ -0,0 +1,56 @@
1
+ import { protectArtifactTables } from "./artifact-migrations.js";
2
+ export function applyArtifactGraphMigrations(db) {
3
+ db.exec(`
4
+ CREATE TABLE IF NOT EXISTS artifact_store_changes (
5
+ seq INTEGER PRIMARY KEY AUTOINCREMENT, store_id TEXT NOT NULL,
6
+ entity_kind TEXT NOT NULL, entity_id TEXT NOT NULL, request_id TEXT NOT NULL,
7
+ UNIQUE(store_id,entity_kind,request_id));
8
+ CREATE TABLE IF NOT EXISTS artifact_revision_parents (
9
+ store_id TEXT NOT NULL, child_id TEXT NOT NULL, parent_id TEXT NOT NULL,
10
+ PRIMARY KEY(store_id,child_id));
11
+ CREATE INDEX IF NOT EXISTS artifact_revision_children ON artifact_revision_parents(store_id,parent_id,child_id);
12
+ CREATE TABLE IF NOT EXISTS artifact_definition_operations (
13
+ store_id TEXT NOT NULL, request_id TEXT NOT NULL, payload_hash TEXT NOT NULL,
14
+ result_json TEXT NOT NULL, PRIMARY KEY(store_id,request_id));
15
+ CREATE TABLE IF NOT EXISTS artifact_type_versions (
16
+ store_id TEXT NOT NULL, id TEXT NOT NULL, version INTEGER NOT NULL,
17
+ request_id TEXT NOT NULL, snapshot_json TEXT NOT NULL, actor_json TEXT NOT NULL,
18
+ created_at TEXT NOT NULL, PRIMARY KEY(store_id,id,version), UNIQUE(store_id,request_id));
19
+ CREATE TABLE IF NOT EXISTS artifact_type_current (
20
+ store_id TEXT NOT NULL, id TEXT NOT NULL, snapshot_json TEXT NOT NULL,
21
+ PRIMARY KEY(store_id,id));
22
+ CREATE TABLE IF NOT EXISTS task_versions (
23
+ store_id TEXT NOT NULL, id TEXT NOT NULL, version INTEGER NOT NULL,
24
+ request_id TEXT NOT NULL, snapshot_json TEXT NOT NULL, actor_json TEXT NOT NULL,
25
+ created_at TEXT NOT NULL, PRIMARY KEY(store_id,id,version), UNIQUE(store_id,request_id));
26
+ CREATE TABLE IF NOT EXISTS task_current (
27
+ store_id TEXT NOT NULL, id TEXT NOT NULL, snapshot_json TEXT NOT NULL,
28
+ PRIMARY KEY(store_id,id));
29
+ CREATE TABLE IF NOT EXISTS task_chat_link_events (
30
+ seq INTEGER PRIMARY KEY AUTOINCREMENT,store_id TEXT NOT NULL,session_id TEXT NOT NULL,
31
+ version INTEGER NOT NULL,request_id TEXT NOT NULL, task_ids_json TEXT NOT NULL,
32
+ chat_title_snapshot TEXT NOT NULL,actor_json TEXT NOT NULL,created_at TEXT NOT NULL,
33
+ UNIQUE(store_id,session_id,version),UNIQUE(store_id,request_id));
34
+ `);
35
+ const columns = db
36
+ .prepare('PRAGMA table_info(artifact_store_changes)')
37
+ .all();
38
+ if (!columns.some(column => column.name === 'validation_relevant')) {
39
+ // Older changes conservatively invalidate validations after upgrade.
40
+ db.exec('ALTER TABLE artifact_store_changes ADD COLUMN validation_relevant INTEGER NOT NULL DEFAULT 1');
41
+ }
42
+ db.exec('CREATE INDEX IF NOT EXISTS artifact_validation_changes ON artifact_store_changes(store_id,validation_relevant,seq)');
43
+ // Copy an 0088 history once: the journal is empty only before the first copy.
44
+ // Events that older code adds after this point are not copied.
45
+ db.exec(`INSERT INTO artifact_store_changes(seq,store_id,entity_kind,entity_id,request_id)
46
+ SELECT seq,store_id,'artifact',artifact_id,request_id FROM artifact_events e
47
+ WHERE NOT EXISTS(SELECT 1 FROM artifact_store_changes);`);
48
+ protectArtifactTables(db, {
49
+ artifact_store_changes: 'seq=NEW.seq OR (store_id=NEW.store_id AND entity_kind=NEW.entity_kind AND request_id=NEW.request_id)',
50
+ artifact_revision_parents: 'store_id=NEW.store_id AND child_id=NEW.child_id',
51
+ artifact_definition_operations: 'store_id=NEW.store_id AND request_id=NEW.request_id',
52
+ artifact_type_versions: 'store_id=NEW.store_id AND ((id=NEW.id AND version=NEW.version) OR request_id=NEW.request_id)',
53
+ task_versions: 'store_id=NEW.store_id AND ((id=NEW.id AND version=NEW.version) OR request_id=NEW.request_id)',
54
+ task_chat_link_events: 'seq=NEW.seq OR (store_id=NEW.store_id AND ((session_id=NEW.session_id AND version=NEW.version) OR request_id=NEW.request_id))'
55
+ });
56
+ }
@@ -0,0 +1,70 @@
1
+ // Authoritative records remain append-only even when a caller bypasses the repository.
2
+ const IMMUTABLE_KEYS = {
3
+ artifact_stores: 'id=NEW.id OR (canonical_root=NEW.canonical_root AND db_namespace=NEW.db_namespace)',
4
+ artifacts: 'store_id=NEW.store_id AND (id=NEW.id OR file_name=NEW.file_name)',
5
+ artifact_metadata_versions: 'store_id=NEW.store_id AND ((artifact_id=NEW.artifact_id AND version=NEW.version) OR request_id=NEW.request_id)',
6
+ artifact_events: 'seq=NEW.seq OR (store_id=NEW.store_id AND request_id=NEW.request_id)',
7
+ artifact_operations: 'store_id=NEW.store_id AND request_id=NEW.request_id',
8
+ artifact_operation_events: 'seq=NEW.seq'
9
+ };
10
+ export function applyArtifactMigrations(db) {
11
+ db.exec(`
12
+ CREATE TABLE IF NOT EXISTS artifact_stores (
13
+ id TEXT PRIMARY KEY, canonical_root TEXT NOT NULL, db_namespace TEXT NOT NULL,
14
+ schema_version INTEGER NOT NULL, UNIQUE(canonical_root, db_namespace)
15
+ );
16
+ CREATE TABLE IF NOT EXISTS artifacts (
17
+ store_id TEXT NOT NULL, id TEXT NOT NULL, file_name TEXT NOT NULL,
18
+ sha256 TEXT NOT NULL, bytes INTEGER NOT NULL, body_start_line INTEGER NOT NULL,
19
+ created_at TEXT NOT NULL, PRIMARY KEY(store_id,id), UNIQUE(store_id,file_name)
20
+ );
21
+ CREATE INDEX IF NOT EXISTS artifact_catalog_order ON artifacts(store_id,created_at,id);
22
+ CREATE TABLE IF NOT EXISTS artifact_metadata_versions (
23
+ store_id TEXT NOT NULL, artifact_id TEXT NOT NULL, version INTEGER NOT NULL,
24
+ request_id TEXT NOT NULL, snapshot_json TEXT NOT NULL,
25
+ PRIMARY KEY(store_id,artifact_id,version), UNIQUE(store_id,request_id)
26
+ );
27
+ CREATE TABLE IF NOT EXISTS artifact_events (
28
+ seq INTEGER PRIMARY KEY AUTOINCREMENT, store_id TEXT NOT NULL,
29
+ artifact_id TEXT NOT NULL, request_id TEXT NOT NULL, data_json TEXT NOT NULL,
30
+ UNIQUE(store_id,request_id)
31
+ );
32
+ CREATE INDEX IF NOT EXISTS artifact_history ON artifact_events(store_id,artifact_id,seq);
33
+ CREATE TABLE IF NOT EXISTS artifact_current (
34
+ store_id TEXT NOT NULL, artifact_id TEXT NOT NULL, version INTEGER NOT NULL,
35
+ snapshot_json TEXT NOT NULL, PRIMARY KEY(store_id,artifact_id)
36
+ );
37
+ CREATE TABLE IF NOT EXISTS artifact_store_epochs (
38
+ store_id TEXT PRIMARY KEY, store_epoch INTEGER NOT NULL DEFAULT 0
39
+ );
40
+ CREATE TABLE IF NOT EXISTS artifact_operations (
41
+ store_id TEXT NOT NULL, request_id TEXT NOT NULL, kind TEXT NOT NULL,
42
+ payload_hash TEXT NOT NULL, artifact_id TEXT NOT NULL, data_json TEXT NOT NULL,
43
+ PRIMARY KEY(store_id,request_id)
44
+ );
45
+ CREATE TABLE IF NOT EXISTS artifact_operation_events (
46
+ seq INTEGER PRIMARY KEY AUTOINCREMENT, store_id TEXT NOT NULL,
47
+ request_id TEXT NOT NULL, state TEXT NOT NULL, created_at TEXT NOT NULL
48
+ );
49
+ CREATE INDEX IF NOT EXISTS artifact_operation_history ON artifact_operation_events(store_id,request_id,seq);
50
+ CREATE TABLE IF NOT EXISTS artifact_operation_leases (
51
+ store_id TEXT NOT NULL, request_id TEXT NOT NULL, owner TEXT NOT NULL,
52
+ expires_at INTEGER NOT NULL, PRIMARY KEY(store_id,request_id)
53
+ );
54
+ `);
55
+ protectArtifactTables(db, IMMUTABLE_KEYS);
56
+ }
57
+ export function protectArtifactTables(db, tables) {
58
+ for (const [table, keys] of Object.entries(tables)) {
59
+ // REPLACE can bypass DELETE triggers when recursive triggers are disabled.
60
+ // The rowid test stops REPLACE with an explicit rowid in tables without an
61
+ // integer primary key. SQLite does not define NEW.rowid when it assigns the rowid.
62
+ db.exec(`CREATE TRIGGER IF NOT EXISTS ${table}_no_replace BEFORE INSERT ON ${table}
63
+ WHEN EXISTS(SELECT 1 FROM ${table} WHERE rowid=NEW.rowid OR (${keys}))
64
+ BEGIN SELECT RAISE(ABORT, 'Artifact history is immutable'); END;`);
65
+ for (const action of ['UPDATE', 'DELETE']) {
66
+ db.exec(`CREATE TRIGGER IF NOT EXISTS ${table}_no_${action.toLowerCase()}
67
+ BEFORE ${action} ON ${table} BEGIN SELECT RAISE(ABORT, 'Artifact history is immutable'); END;`);
68
+ }
69
+ }
70
+ }
@@ -0,0 +1,24 @@
1
+ import { protectArtifactTables } from "./artifact-migrations.js";
2
+ export function applyArtifactReviewMigrations(db) {
3
+ db.exec(`
4
+ CREATE TABLE IF NOT EXISTS artifact_review_events (
5
+ seq INTEGER PRIMARY KEY AUTOINCREMENT, store_id TEXT NOT NULL,
6
+ thread_id TEXT NOT NULL, origin_artifact_id TEXT NOT NULL,
7
+ version INTEGER NOT NULL CHECK(version >= 1), request_id TEXT NOT NULL,
8
+ data_json TEXT NOT NULL,
9
+ UNIQUE(store_id,thread_id,version), UNIQUE(store_id,request_id)
10
+ );
11
+ CREATE INDEX IF NOT EXISTS artifact_review_history
12
+ ON artifact_review_events(store_id,thread_id,seq);
13
+ CREATE TABLE IF NOT EXISTS artifact_thread_current (
14
+ store_id TEXT NOT NULL, thread_id TEXT NOT NULL,
15
+ origin_artifact_id TEXT NOT NULL, version INTEGER NOT NULL,
16
+ snapshot_json TEXT NOT NULL, PRIMARY KEY(store_id,thread_id)
17
+ );
18
+ CREATE INDEX IF NOT EXISTS artifact_thread_origin
19
+ ON artifact_thread_current(store_id,origin_artifact_id,thread_id);
20
+ `);
21
+ protectArtifactTables(db, {
22
+ artifact_review_events: 'seq=NEW.seq OR (store_id=NEW.store_id AND ((thread_id=NEW.thread_id AND version=NEW.version) OR request_id=NEW.request_id))'
23
+ });
24
+ }
@@ -0,0 +1,31 @@
1
+ import { withSavepoint } from "./transaction.js";
2
+ export function probeArtifactSearch(db) {
3
+ try {
4
+ withSavepoint(db, () => {
5
+ db.exec("CREATE VIRTUAL TABLE temp.ght_search_probe_words USING fts5(text,tokenize='unicode61'); CREATE VIRTUAL TABLE temp.ght_search_probe_grams USING fts5(text,tokenize='trigram'); DROP TABLE temp.ght_search_probe_words; DROP TABLE temp.ght_search_probe_grams;");
6
+ });
7
+ return true;
8
+ }
9
+ catch {
10
+ return false;
11
+ }
12
+ }
13
+ export function applyArtifactSearchMigrations(db) {
14
+ db.exec(`CREATE TABLE IF NOT EXISTS artifact_search_capabilities (id INTEGER PRIMARY KEY CHECK(id=1), available INTEGER NOT NULL);
15
+ CREATE TABLE IF NOT EXISTS artifact_search_documents (
16
+ rowid INTEGER PRIMARY KEY, store_id TEXT NOT NULL, artifact_id TEXT NOT NULL,
17
+ version INTEGER NOT NULL, sha256 TEXT NOT NULL, title TEXT NOT NULL, body TEXT NOT NULL,
18
+ title_offsets TEXT NOT NULL, body_offsets TEXT NOT NULL, UNIQUE(store_id,artifact_id));
19
+ CREATE TABLE IF NOT EXISTS artifact_search_suppressed (store_id TEXT NOT NULL, artifact_id TEXT NOT NULL, reason TEXT NOT NULL, PRIMARY KEY(store_id,artifact_id));
20
+ CREATE TABLE IF NOT EXISTS artifact_search_words (store_id TEXT NOT NULL, token TEXT NOT NULL, length INTEGER NOT NULL, frequency INTEGER NOT NULL, PRIMARY KEY(store_id,token));
21
+ CREATE INDEX IF NOT EXISTS artifact_search_word_length ON artifact_search_words(store_id,length);
22
+ CREATE TABLE IF NOT EXISTS artifact_search_word_grams (store_id TEXT NOT NULL, gram TEXT NOT NULL, width INTEGER NOT NULL, token TEXT NOT NULL, PRIMARY KEY(store_id,width,gram,token));
23
+ CREATE TABLE IF NOT EXISTS artifact_search_document_words (store_id TEXT NOT NULL, artifact_id TEXT NOT NULL, token TEXT NOT NULL, PRIMARY KEY(store_id,artifact_id,token));
24
+ CREATE TABLE IF NOT EXISTS artifact_search_epochs (store_id TEXT PRIMARY KEY, epoch INTEGER NOT NULL DEFAULT 0);
25
+ `);
26
+ const available = probeArtifactSearch(db);
27
+ db.prepare('INSERT INTO artifact_search_capabilities VALUES(1,?) ON CONFLICT(id) DO UPDATE SET available=excluded.available').run(Number(available));
28
+ if (available) {
29
+ db.exec("CREATE VIRTUAL TABLE IF NOT EXISTS artifact_search_fts USING fts5(title,body, tokenize='unicode61 remove_diacritics 0'); CREATE VIRTUAL TABLE IF NOT EXISTS artifact_search_trigrams USING fts5(title,body, tokenize='trigram');");
30
+ }
31
+ }
@@ -0,0 +1,70 @@
1
+ import { protectArtifactTables } from "./artifact-migrations.js";
2
+ export function applyArtifactValidationMigrations(db) {
3
+ db.exec(`
4
+ CREATE TABLE IF NOT EXISTS artifact_change_set_events (
5
+ seq INTEGER PRIMARY KEY AUTOINCREMENT, store_id TEXT NOT NULL,
6
+ id TEXT NOT NULL, version INTEGER NOT NULL, request_id TEXT NOT NULL,
7
+ payload_json TEXT NOT NULL, UNIQUE(store_id,id,version), UNIQUE(store_id,request_id)
8
+ );
9
+ CREATE TABLE IF NOT EXISTS artifact_validation_events (
10
+ seq INTEGER PRIMARY KEY AUTOINCREMENT, store_id TEXT NOT NULL,
11
+ id TEXT NOT NULL, payload_json TEXT NOT NULL
12
+ );
13
+ CREATE INDEX IF NOT EXISTS artifact_validation_history ON artifact_validation_events(store_id,id,seq);
14
+ CREATE TABLE IF NOT EXISTS artifact_validation_current (
15
+ store_id TEXT NOT NULL, id TEXT NOT NULL, payload_json TEXT NOT NULL,
16
+ PRIMARY KEY(store_id,id)
17
+ );
18
+ CREATE TABLE IF NOT EXISTS artifact_validation_leases (
19
+ store_id TEXT PRIMARY KEY, validation_id TEXT NOT NULL,
20
+ owner TEXT NOT NULL, owner_pid INTEGER NOT NULL, expires_at INTEGER NOT NULL
21
+ );
22
+ CREATE TABLE IF NOT EXISTS artifact_user_authorizations (
23
+ seq INTEGER PRIMARY KEY AUTOINCREMENT, store_id TEXT NOT NULL,
24
+ id TEXT NOT NULL, payload_json TEXT NOT NULL, UNIQUE(store_id,id)
25
+ );
26
+ CREATE TABLE IF NOT EXISTS artifact_authorization_events (
27
+ seq INTEGER PRIMARY KEY AUTOINCREMENT, store_id TEXT NOT NULL,
28
+ id TEXT NOT NULL, kind TEXT NOT NULL, payload_json TEXT NOT NULL
29
+ );
30
+ CREATE TABLE IF NOT EXISTS artifact_owner_heartbeats (
31
+ store_id TEXT NOT NULL, owner TEXT NOT NULL, beat_at INTEGER NOT NULL,
32
+ PRIMARY KEY(store_id,owner)
33
+ );
34
+ CREATE TABLE IF NOT EXISTS artifact_transition_events (
35
+ seq INTEGER PRIMARY KEY AUTOINCREMENT, store_id TEXT NOT NULL,
36
+ id TEXT NOT NULL, payload_json TEXT NOT NULL
37
+ );
38
+ CREATE INDEX IF NOT EXISTS artifact_transition_history ON artifact_transition_events(store_id,id,seq);
39
+ CREATE TABLE IF NOT EXISTS artifact_transition_current (
40
+ store_id TEXT NOT NULL, id TEXT NOT NULL, artifact_id TEXT NOT NULL,
41
+ payload_json TEXT NOT NULL, PRIMARY KEY(store_id,id)
42
+ );
43
+ CREATE INDEX IF NOT EXISTS artifact_transition_by_artifact ON artifact_transition_current(store_id,artifact_id);
44
+ CREATE TABLE IF NOT EXISTS artifact_transition_requests (
45
+ seq INTEGER PRIMARY KEY AUTOINCREMENT, store_id TEXT NOT NULL,
46
+ request_id TEXT NOT NULL, payload_hash TEXT NOT NULL, operation_id TEXT NOT NULL,
47
+ UNIQUE(store_id,request_id)
48
+ );
49
+ CREATE TABLE IF NOT EXISTS artifact_transition_consents (
50
+ seq INTEGER PRIMARY KEY AUTOINCREMENT, store_id TEXT NOT NULL,
51
+ operation_id TEXT NOT NULL, payload_json TEXT NOT NULL,
52
+ UNIQUE(store_id,operation_id)
53
+ );
54
+ CREATE TABLE IF NOT EXISTS artifact_lifecycle_commits (
55
+ seq INTEGER PRIMARY KEY AUTOINCREMENT, store_id TEXT NOT NULL,
56
+ request_id TEXT NOT NULL, payload_hash TEXT NOT NULL, result_json TEXT NOT NULL,
57
+ UNIQUE(store_id,request_id)
58
+ );
59
+ `);
60
+ protectArtifactTables(db, {
61
+ artifact_change_set_events: 'seq=NEW.seq OR (store_id=NEW.store_id AND ((id=NEW.id AND version=NEW.version) OR request_id=NEW.request_id))',
62
+ artifact_validation_events: 'seq=NEW.seq',
63
+ artifact_user_authorizations: 'seq=NEW.seq OR (store_id=NEW.store_id AND id=NEW.id)',
64
+ artifact_authorization_events: 'seq=NEW.seq',
65
+ artifact_transition_events: 'seq=NEW.seq',
66
+ artifact_transition_requests: 'seq=NEW.seq OR (store_id=NEW.store_id AND request_id=NEW.request_id)',
67
+ artifact_transition_consents: 'seq=NEW.seq OR (store_id=NEW.store_id AND operation_id=NEW.operation_id)',
68
+ artifact_lifecycle_commits: 'seq=NEW.seq OR (store_id=NEW.store_id AND request_id=NEW.request_id)'
69
+ });
70
+ }
@@ -2,6 +2,8 @@ import { DatabaseSync } from 'node:sqlite';
2
2
  import { mkdirSync } from 'node:fs';
3
3
  import { dirname } from 'node:path';
4
4
  import { applyMigrations } from "./migrations.js";
5
+ // Give concurrent local processes time to finish short write transactions.
6
+ const WRITE_BUSY_TIMEOUT_MS = 5000;
5
7
  export function createTestDatabase() {
6
8
  const db = new DatabaseSync(':memory:');
7
9
  applyMigrations(db);
@@ -10,6 +12,7 @@ export function createTestDatabase() {
10
12
  export function createFileDatabase(path) {
11
13
  mkdirSync(dirname(path), { recursive: true });
12
14
  const db = new DatabaseSync(path);
15
+ db.exec(`PRAGMA busy_timeout = ${WRITE_BUSY_TIMEOUT_MS};`);
13
16
  // WAL allows a concurrent reader (`list`/`export`) while `serve` holds the file open.
14
17
  db.exec('PRAGMA journal_mode = WAL;');
15
18
  applyMigrations(db);
@@ -1,4 +1,14 @@
1
+ import { applyArtifactSearchMigrations } from "./artifact-search-migrations.js";
2
+ import { applyArtifactValidationMigrations } from "./artifact-validation-migrations.js";
3
+ import { applyArtifactReviewMigrations } from "./artifact-review-migrations.js";
4
+ import { applyArtifactGraphMigrations } from "./artifact-graph-migrations.js";
5
+ import { applyArtifactMigrations } from "./artifact-migrations.js";
1
6
  export function applyMigrations(db) {
7
+ applyArtifactMigrations(db);
8
+ applyArtifactGraphMigrations(db);
9
+ applyArtifactReviewMigrations(db);
10
+ applyArtifactValidationMigrations(db);
11
+ applyArtifactSearchMigrations(db);
2
12
  db.exec(`
3
13
  CREATE TABLE IF NOT EXISTS note_storages (
4
14
  id TEXT PRIMARY KEY,
@@ -0,0 +1,15 @@
1
+ // SQLite stores the primary result code in the low byte of extended codes.
2
+ const PRIMARY_CODE_MASK = 0xff;
3
+ // Another connection owns a conflicting database lock.
4
+ const SQLITE_BUSY = 5;
5
+ // A conflicting operation holds a table or connection lock.
6
+ const SQLITE_LOCKED = 6;
7
+ export function isSqliteBusyError(error) {
8
+ if (!(error instanceof Error) ||
9
+ !('errcode' in error) ||
10
+ typeof error.errcode !== 'number') {
11
+ return false;
12
+ }
13
+ const code = error.errcode & PRIMARY_CODE_MASK;
14
+ return code === SQLITE_BUSY || code === SQLITE_LOCKED;
15
+ }
@@ -0,0 +1,40 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { isPromise } from 'node:util/types';
3
+ import { AsyncTransactionError } from "../errors/database.js";
4
+ function complete(db, run, commit, rollback) {
5
+ try {
6
+ const result = run();
7
+ if (result !== null &&
8
+ (typeof result === 'object' || typeof result === 'function') &&
9
+ 'then' in result &&
10
+ typeof result.then === 'function') {
11
+ // Reject async results without leaving a native Promise rejection unhandled.
12
+ if (isPromise(result))
13
+ void result.catch(() => { });
14
+ throw new AsyncTransactionError();
15
+ }
16
+ db.exec(commit);
17
+ return result;
18
+ }
19
+ catch (error) {
20
+ try {
21
+ db.exec(rollback);
22
+ }
23
+ catch (rollbackError) {
24
+ // oxlint-disable-next-line preserve-caught-error -- Both errors are retained; the operation failure remains the primary cause.
25
+ throw new AggregateError([error, rollbackError], 'Transaction failed and rollback also failed.', { cause: error });
26
+ }
27
+ throw error;
28
+ }
29
+ }
30
+ export function withTransaction(db, run, options = { mode: 'deferred' }) {
31
+ // A failed BEGIN must not roll back a transaction owned by the caller.
32
+ db.exec(options.mode === 'immediate' ? 'BEGIN IMMEDIATE' : 'BEGIN DEFERRED');
33
+ return complete(db, run, 'COMMIT', 'ROLLBACK');
34
+ }
35
+ export function withSavepoint(db, run) {
36
+ // Unique names keep nested scopes independent of other repository calls.
37
+ const name = `ght_${randomUUID().replaceAll('-', '')}`;
38
+ db.exec(`SAVEPOINT ${name}`);
39
+ return complete(db, run, `RELEASE ${name}`, `ROLLBACK TO ${name}; RELEASE ${name}`);
40
+ }
@@ -0,0 +1,28 @@
1
+ import createError from '@fastify/error';
2
+ // @fastify/error matches instanceof by code, so a subclass cannot be told apart.
3
+ // Markers keep the public code GHT_ARTIFACT_CONFLICT while callers still can.
4
+ const BUSY = Symbol('artifact.busy');
5
+ const LEASE_BUSY = Symbol('artifact.leaseBusy');
6
+ export const ArtifactInputError = createError('GHT_ARTIFACT_INVALID_INPUT', '%s', 400);
7
+ export const ArtifactNotFoundError = createError('GHT_ARTIFACT_NOT_FOUND', 'Artifact not found.', 404);
8
+ export const ArtifactConflictError = createError('GHT_ARTIFACT_CONFLICT', '%s', 409);
9
+ function marked(message, marker) {
10
+ return Object.assign(new ArtifactConflictError(message), { [marker]: true });
11
+ }
12
+ /** The database is locked by another writer; the same call can succeed later. */
13
+ export function artifactBusyError(message) {
14
+ return marked(message, BUSY);
15
+ }
16
+ /** Another validation job holds the store lease; start again when it ends. */
17
+ export function artifactLeaseBusyError(message) {
18
+ return marked(message, LEASE_BUSY);
19
+ }
20
+ export function isArtifactBusyError(error) {
21
+ return typeof error === 'object' && error !== null && BUSY in error;
22
+ }
23
+ export function isArtifactLeaseBusyError(error) {
24
+ return typeof error === 'object' && error !== null && LEASE_BUSY in error;
25
+ }
26
+ export const ArtifactIntegrityError = createError('GHT_ARTIFACT_INTEGRITY', '%s', 422);
27
+ export const ArtifactTooLargeError = createError('GHT_ARTIFACT_TOO_LARGE', 'Artifact exceeds the storage limit.', 413);
28
+ export const ArtifactSearchUnavailableError = createError('GHT_ARTIFACT_SEARCH_UNAVAILABLE', 'Artifact search is unavailable: SQLite FTS5 unicode61 and trigram are required.', 503);
@@ -0,0 +1,2 @@
1
+ import createError from '@fastify/error';
2
+ export const AsyncTransactionError = createError('GHT_DB_ASYNC_TRANSACTION', 'Database transaction callbacks must be synchronous.', 500);
@@ -1,5 +1,10 @@
1
1
  import { isLoopbackHost } from "../../../shared/network/loopback.js";
2
2
  import { forwardedAddresses, isLoopbackAddress, parseHost, sameOrigin } from "../../utils/request-origin.js";
3
+ /** True when the `Host` header names this machine (`localhost`, `127.0.0.1`, `[::1]`). */
4
+ export function isLoopbackHostHeader(host) {
5
+ const target = parseHost(host);
6
+ return target !== undefined && isLoopbackHost(target.hostname);
7
+ }
3
8
  /**
4
9
  * True when the request comes from a page or a client on this machine
5
10
  * (ADR 0079). The `Host` header alone is not proof: with a bind on