@tequila99/githuman 0.11.2 → 0.12.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 (322) hide show
  1. package/README.md +62 -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 +7 -1
  5. package/dist/cli/index.js +19 -8
  6. package/dist/cli/server-runtime.js +1 -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 +92 -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/require-access.js +8 -0
  34. package/dist/server/repositories/artifact-catalog.repo.js +283 -0
  35. package/dist/server/repositories/artifact-files.repo.js +181 -0
  36. package/dist/server/repositories/artifact-graph.repo.js +50 -0
  37. package/dist/server/repositories/artifact-search.repo.js +192 -0
  38. package/dist/server/repositories/artifact-transition.repo.js +85 -0
  39. package/dist/server/repositories/artifact-validation.repo.js +146 -0
  40. package/dist/server/repositories/notes-catalog.repo.js +2 -10
  41. package/dist/server/repositories/task.repo.js +114 -0
  42. package/dist/server/routes/agent.js +8 -1
  43. package/dist/server/routes/artifact-actions.js +129 -0
  44. package/dist/server/routes/artifact-agent.js +183 -0
  45. package/dist/server/routes/artifact-definitions.js +163 -0
  46. package/dist/server/routes/artifact-transitions.js +80 -0
  47. package/dist/server/routes/artifacts.js +111 -0
  48. package/dist/server/routes/events.js +5 -1
  49. package/dist/server/services/agent/agent-session.js +180 -10
  50. package/dist/server/services/agent/context-builder.js +30 -2
  51. package/dist/server/services/agent/session-registry.js +106 -2
  52. package/dist/server/services/agent/turn-task-context.js +1 -0
  53. package/dist/server/services/artifact-authorization.service.js +174 -0
  54. package/dist/server/services/artifact-definition.service.js +96 -0
  55. package/dist/server/services/artifact-export.service.js +44 -0
  56. package/dist/server/services/artifact-graph-validation.js +497 -0
  57. package/dist/server/services/artifact-graph.service.js +81 -0
  58. package/dist/server/services/artifact-lifecycle.service.js +343 -0
  59. package/dist/server/services/artifact-maintenance.service.js +55 -0
  60. package/dist/server/services/artifact-search.service.js +331 -0
  61. package/dist/server/services/artifact-storage.service.js +646 -0
  62. package/dist/server/services/artifact-transition.service.js +607 -0
  63. package/dist/server/services/artifact-type.service.js +74 -0
  64. package/dist/server/services/artifact-validation-jobs.service.js +518 -0
  65. package/dist/server/services/device-connections.js +9 -1
  66. package/dist/server/services/review.service.js +6 -5
  67. package/dist/server/services/task.service.js +225 -0
  68. package/dist/server/utils/artifact-actor.js +13 -0
  69. package/dist/server/utils/artifact-page.js +42 -0
  70. package/dist/server/utils/take-within-bytes.js +16 -0
  71. package/dist/server/utils/text.js +7 -0
  72. package/dist/shared/agents/schemas.js +27 -0
  73. package/dist/shared/app/storage-constants.js +2 -0
  74. package/dist/shared/artifact-tools/descriptions.js +11 -0
  75. package/dist/shared/artifact-tools/instructions.js +14 -0
  76. package/dist/shared/artifact-tools/schemas.js +119 -0
  77. package/dist/shared/artifact-transitions/constants.js +21 -0
  78. package/dist/shared/artifact-transitions/schemas.js +111 -0
  79. package/dist/shared/artifact-transitions/transitions.js +61 -0
  80. package/dist/shared/artifact-transitions/types.js +1 -0
  81. package/dist/shared/artifacts/constants.js +34 -0
  82. package/dist/shared/artifacts/mcp-constants.js +24 -0
  83. package/dist/shared/artifacts/normalize.js +9 -0
  84. package/dist/shared/artifacts/schemas.js +820 -0
  85. package/dist/shared/artifacts/search.js +101 -0
  86. package/dist/shared/artifacts/types.js +1 -0
  87. package/dist/shared/events/schemas.js +1 -1
  88. package/dist/shared/events/types.js +1 -0
  89. package/dist/shared/tasks/schemas.js +261 -0
  90. package/dist/shared/tasks/types.js +1 -0
  91. package/dist/shared/utils/artifact-body.js +10 -0
  92. package/dist/shared/utils/canonical-json.js +22 -0
  93. package/dist/shared/utils/is-safe-prefixed-filename.js +10 -0
  94. package/dist/shared/utils/is-uuid.js +4 -2
  95. package/dist/shared/utils/is-valid-artifact-type-id.js +9 -0
  96. package/dist/shared/utils/is-valid-filename-prefix.js +13 -0
  97. package/dist/web/assets/AccessPage-Dvj0aCkm.js +1 -0
  98. package/dist/web/assets/AddButton-CMC9qf51.js +4 -0
  99. package/dist/web/assets/AddToAgentButton-BitTdCEg.js +1 -0
  100. package/dist/web/assets/AppPage-Cozv2vqG.js +1 -0
  101. package/dist/web/assets/ArtifactDefinitionPicker-BQOoU60k.js +3 -0
  102. package/dist/web/assets/ArtifactDefinitionPicker-D3qdrxlC.css +1 -0
  103. package/dist/web/assets/ArtifactsPage-BJR-XYYs.js +2 -0
  104. package/dist/web/assets/ArtifactsPage-Cu1xdYNf.css +1 -0
  105. package/dist/web/assets/CancelButton-mkxRHwoZ.js +1 -0
  106. package/dist/web/assets/ClosePopup-BtDeDZsk.js +1 -0
  107. package/dist/web/assets/CreateButton-zCmb7snC.js +1 -0
  108. package/dist/web/assets/CreateReviewFabButton-BCg1TvFh.js +1 -0
  109. package/dist/web/assets/DialogHeader-CPL9OtDV.css +1 -0
  110. package/dist/web/assets/DialogHeader-CQvAZVx7.js +1 -0
  111. package/dist/web/assets/{ErrorNotFound-DucDzOUJ.js → ErrorNotFound-fXS367lZ.js} +1 -1
  112. package/dist/web/assets/{IndexPage-D5PvyGrX.css → IndexPage-5V5GLtgi.css} +1 -1
  113. package/dist/web/assets/IndexPage-DMTZyWQA.js +1 -0
  114. package/dist/web/assets/MainLayout-DWJ_-EMV.js +7 -0
  115. package/dist/web/assets/MainLayout-Dy74m_q1.css +1 -0
  116. package/dist/web/assets/NoteVisualEditor-5rHBQ3j0.js +1054 -0
  117. package/dist/web/assets/{NoteVisualEditor-CawpxEFB.css → NoteVisualEditor-DE4ACTRJ.css} +1 -1
  118. package/dist/web/assets/NotesPage-C_eoCUl7.css +1 -0
  119. package/dist/web/assets/NotesPage-CjaxjLBW.js +4 -0
  120. package/dist/web/assets/{PdfPreview-rlkcqoMF.js → PdfPreview-2NQ5gkjh.js} +2 -2
  121. package/dist/web/assets/ProjectFilePage-B-WUUxnq.js +1 -0
  122. package/dist/web/assets/QBtn-V3VXtJXi.js +1 -0
  123. package/dist/web/assets/QFab-CxpedhPI.js +1 -0
  124. package/dist/web/assets/{QForm-Be8-ucuJ.js → QForm-ClpSneeD.js} +1 -1
  125. package/dist/web/assets/QInput-BnqLJGXl.js +1 -0
  126. package/dist/web/assets/QScrollArea-js5dkUz1.js +1 -0
  127. package/dist/web/assets/QSpace-C7iAnGlU.js +1 -0
  128. package/dist/web/assets/QTabs-DIy5gWMb.js +1 -0
  129. package/dist/web/assets/QVirtualScroll-DkD0TczV.js +1 -0
  130. package/dist/web/assets/ReviewDetailPage-BgxcsTqY.css +1 -0
  131. package/dist/web/assets/ReviewDetailPage-Bmc8DmSu.js +1 -0
  132. package/dist/web/assets/ReviewsPage-DR1IWCmu.js +1 -0
  133. package/dist/web/assets/SearchInput-BRyN93Z1.js +1 -0
  134. package/dist/web/assets/StatusSwitcher-CpkH8BuC.js +1 -0
  135. package/dist/web/assets/{StatusSwitcher-D6i8_OfR.css → StatusSwitcher-DaE1ufAA.css} +1 -1
  136. package/dist/web/assets/TagFilterField-Bl3FBkB5.js +1 -0
  137. package/dist/web/assets/TagFilterField-DjUH0m3_.css +1 -0
  138. package/dist/web/assets/{TerminalView-617DZuPt.css → TerminalView-BCeke4xd.css} +1 -1
  139. package/dist/web/assets/{TerminalView-D4elkzN4.js → TerminalView-tEyLM5qZ.js} +12 -12
  140. package/dist/web/assets/{abnfDiagram-O67JEVCF-Cugru96L.js → abnfDiagram-O67JEVCF-DN9xiV9p.js} +1 -1
  141. package/dist/web/assets/{access-store-Cdg8oRAM.js → access-store-CwkOl71l.js} +1 -1
  142. package/dist/web/assets/{agent-store-D_W7iD_B.js → agent-store-Grbx4jkz.js} +5 -5
  143. package/dist/web/assets/architecture-7GRP2DOG-DmaYCP8T.js +1 -0
  144. package/dist/web/assets/{architectureDiagram-NJMV4G6O-7rMsE4Wt.js → architectureDiagram-NJMV4G6O-BVAJOucG.js} +1 -1
  145. package/dist/web/assets/{blockDiagram-BEXU5L5S-BvSnwUrI.js → blockDiagram-BEXU5L5S-TJAfSX44.js} +1 -1
  146. package/dist/web/assets/{c4Diagram-YGBWAQC7-CgmQlCVx.js → c4Diagram-YGBWAQC7-5UHyT_tW.js} +1 -1
  147. package/dist/web/assets/channel-B4_5H-fb.js +1 -0
  148. package/dist/web/assets/{chunk-3FUC2YCW-_i8udThz.js → chunk-3FUC2YCW-CelgLyJi.js} +1 -1
  149. package/dist/web/assets/{chunk-5DYCD2WN-Mhk2LlLk.js → chunk-5DYCD2WN-bhEAVIbQ.js} +1 -1
  150. package/dist/web/assets/{chunk-742MDFTN-CDVcwgEp.js → chunk-742MDFTN-ByLz4EcD.js} +1 -1
  151. package/dist/web/assets/{chunk-7INBJB4K-C2c1fi9s.js → chunk-7INBJB4K-CK2i0csa.js} +1 -1
  152. package/dist/web/assets/{chunk-7M6MHVWA-BeGkoIBL.js → chunk-7M6MHVWA-CoCiujjC.js} +1 -1
  153. package/dist/web/assets/{chunk-7PRAP22T-BVWTh9sT.js → chunk-7PRAP22T-CY4NCrCY.js} +1 -1
  154. package/dist/web/assets/{chunk-GTNCS2PH-gVhX7NZP.js → chunk-GTNCS2PH-DMB7bQGB.js} +1 -1
  155. package/dist/web/assets/{chunk-GWA4HPMP-BPdnIcQH.js → chunk-GWA4HPMP-F6qclPTR.js} +1 -1
  156. package/dist/web/assets/{chunk-MBY4JIJT-B2VfO_06.js → chunk-MBY4JIJT-CSiiBtlX.js} +1 -1
  157. package/dist/web/assets/{chunk-NETBCI7D-DZF_gITO.js → chunk-NETBCI7D-CBh7GChL.js} +1 -1
  158. package/dist/web/assets/{chunk-O7XYJQB3-CDpBWiLq.js → chunk-O7XYJQB3-B2aqz0sN.js} +1 -1
  159. package/dist/web/assets/{chunk-UA2S7LBM-Iv7TSRE_.js → chunk-UA2S7LBM-B_-P_eR0.js} +1 -1
  160. package/dist/web/assets/{chunk-WEXAMYUT-Ce6goZ2Y.js → chunk-WEXAMYUT-19nMFxzK.js} +1 -1
  161. package/dist/web/assets/{chunk-Z7XXMR3K-CY6E6_Ux.js → chunk-Z7XXMR3K-Co7gzRY1.js} +1 -1
  162. package/dist/web/assets/{chunk-ZIGJFQKS-BLzsXZBc.js → chunk-ZIGJFQKS-Eoi4K5bK.js} +1 -1
  163. package/dist/web/assets/{classDiagram-v2-NBCMYWYE-Dz0kD9P6.js → classDiagram-v2-NBCMYWYE-CfFGzL-A.js} +1 -1
  164. package/dist/web/assets/copy-code-DaqEfdXL.js +1 -0
  165. package/dist/web/assets/create-DedXY-KP.js +1 -0
  166. package/dist/web/assets/{cynefin-OW5HDTMX-DWIx3wRm.js → cynefin-OW5HDTMX-DdZXl3KU.js} +1 -1
  167. package/dist/web/assets/{cynefinDiagram-VND7K2PF-2D80uocv.js → cynefinDiagram-VND7K2PF-WU21nYH-.js} +1 -1
  168. package/dist/web/assets/{dagre-6A5THRUB-BLBhukn-.js → dagre-6A5THRUB-48z3F8Pl.js} +1 -1
  169. package/dist/web/assets/date-DZIuZp-w.js +1 -0
  170. package/dist/web/assets/{devices-store-BfZKPOhg.js → devices-store-Bfk2wdrQ.js} +1 -1
  171. package/dist/web/assets/{diagram-22UHCM2B-DY8dMLZ3.js → diagram-22UHCM2B-B0AZgJW9.js} +1 -1
  172. package/dist/web/assets/{diagram-3UASUU5V-Bf6Od1NH.js → diagram-3UASUU5V-CzEzrxuO.js} +1 -1
  173. package/dist/web/assets/{diagram-ATOU4E4O-CjLGcYD5.js → diagram-ATOU4E4O-w6h1bD3w.js} +1 -1
  174. package/dist/web/assets/{diagram-CDSNMT55-gIvaVNvs.js → diagram-CDSNMT55-BdTHQd_o.js} +1 -1
  175. package/dist/web/assets/{diagram-MLGK6HIB-VR5kiCi5.js → diagram-MLGK6HIB-pB4cj6c2.js} +1 -1
  176. package/dist/web/assets/{diagram-MPIPVDR6-CdAdj25I.js → diagram-MPIPVDR6-C7C06qyV.js} +1 -1
  177. package/dist/web/assets/diff-store-DBDL5Hw5.js +93 -0
  178. package/dist/web/assets/{dist-lgw4kcVA.js → dist-B7Cu0FBW.js} +1 -1
  179. package/dist/web/assets/{dist-yzAhvE-V.js → dist-B8i8y3GY.js} +1 -1
  180. package/dist/web/assets/{dist-C85t5q3z.js → dist-BNgPhnKo.js} +1 -1
  181. package/dist/web/assets/{dist-CEl-Ko94.js → dist-BWoPV5P3.js} +1 -1
  182. package/dist/web/assets/{dist-DLuI1PxH.js → dist-CJ7isxli.js} +1 -1
  183. package/dist/web/assets/{dist-joQDFmfy.js → dist-CTJJYvYG.js} +1 -1
  184. package/dist/web/assets/{dist-CBGyhCYv.js → dist-CZz0KXAA.js} +1 -1
  185. package/dist/web/assets/{dist-CIMEaJAX.js → dist-DNpwgDba.js} +1 -1
  186. package/dist/web/assets/{dist-CTd7Kuqx.js → dist-DWCdhk-O.js} +1 -1
  187. package/dist/web/assets/{dist-BU8erXQF.js → dist-DqSUdjG9.js} +1 -1
  188. package/dist/web/assets/{dist-C54FrSb8.js → dist-Dy9K9LOR.js} +1 -1
  189. package/dist/web/assets/{ebnfDiagram-ZINNZB2B-irTAlMFO.js → ebnfDiagram-ZINNZB2B-75vt7539.js} +1 -1
  190. package/dist/web/assets/{elk-276RUBZZ-Dj5ligvC.js → elk-276RUBZZ-DXDjJVTy.js} +1 -1
  191. package/dist/web/assets/{erDiagram-OPXOYQCR-Bvn6rbL1.js → erDiagram-OPXOYQCR-tNNInTmk.js} +1 -1
  192. package/dist/web/assets/eventmodeling-NTZA5JFV-QMRehQtM.js +1 -0
  193. package/dist/web/assets/flowDiagram-KWPJA3E3-DJAgsbXC.js +1 -0
  194. package/dist/web/assets/{ganttDiagram-FUAMR5RP-BNGAUF5H.js → ganttDiagram-FUAMR5RP-h3E8lnCd.js} +1 -1
  195. package/dist/web/assets/{gitGraph-4MIJSDKK-DYSHOl72.js → gitGraph-4MIJSDKK-BUwY-iv2.js} +1 -1
  196. package/dist/web/assets/{gitGraphDiagram-X574FWY7-BGwRHrPq.js → gitGraphDiagram-X574FWY7-C0AgQH5k.js} +1 -1
  197. package/dist/web/assets/i18n-CfTd46OX.js +1 -0
  198. package/dist/web/assets/icons-BoaZfGBj.js +1 -0
  199. package/dist/web/assets/index-BQU3KK1E.css +1 -0
  200. package/dist/web/assets/index-BTpokWxr.js +2 -0
  201. package/dist/web/assets/{info-A6RAGUB7-DNWR20pW.js → info-A6RAGUB7-DVMZ4hdK.js} +1 -1
  202. package/dist/web/assets/{infoDiagram-VRGFBTTK-BhNwJLY5.js → infoDiagram-VRGFBTTK-kLaGXGQ2.js} +1 -1
  203. package/dist/web/assets/{init-app-cg-B_TFN.js → init-app-Bxc_l2yT.js} +1 -1
  204. package/dist/web/assets/{is-D0waCELe.js → is-B1kBQ5Va.js} +1 -1
  205. package/dist/web/assets/{ishikawaDiagram-OU5B5YK6-CSkDpn64.js → ishikawaDiagram-OU5B5YK6-Oj7bZlyo.js} +1 -1
  206. package/dist/web/assets/{journeyDiagram-ZHPQQLJL-D2CEecC3.js → journeyDiagram-ZHPQQLJL-BG7V2Srs.js} +1 -1
  207. package/dist/web/assets/{kanban-definition-PNTS6WVX-CQHlo4Hh.js → kanban-definition-PNTS6WVX-CIJiDE6d.js} +1 -1
  208. package/dist/web/assets/{line-C-izH1ks.js → line-D2vEKYRZ.js} +1 -1
  209. package/dist/web/assets/markdown-Bm2wfvok.js +16 -0
  210. package/dist/web/assets/mdi-Byw1NZrZ.js +1 -0
  211. package/dist/web/assets/{mermaid-parser.core-CAGwq6D-.js → mermaid-parser.core-BcBEWjW6.js} +3 -3
  212. package/dist/web/assets/{mermaid.core--xlI_0tK.js → mermaid.core-DobO0deo.js} +4 -4
  213. package/dist/web/assets/{mindmap-definition-NLK3R4M7-BuCxhlwy.js → mindmap-definition-NLK3R4M7-y7Qz5gBF.js} +1 -1
  214. package/dist/web/assets/{packet-AYTQ26CC-Bi7x5lJK.js → packet-AYTQ26CC-oRQgKr9d.js} +1 -1
  215. package/dist/web/assets/{pegDiagram-GJSIUBJH-qWzBPkCQ.js → pegDiagram-GJSIUBJH-RIU8XOHq.js} +1 -1
  216. package/dist/web/assets/{pie-WAS4IAKB-Di0wC4XB.js → pie-WAS4IAKB-qmOlsLkx.js} +1 -1
  217. package/dist/web/assets/{pieDiagram-5QR66LMP-BCDVY9q2.js → pieDiagram-5QR66LMP-BM932TDA.js} +1 -1
  218. package/dist/web/assets/pinia-BfqEuvsi.js +1 -0
  219. package/dist/web/assets/pinia-Cmp1GI8k.js +1 -0
  220. package/dist/web/assets/preview-store-D7qF6SgX.js +2 -0
  221. package/dist/web/assets/{project-files-store-byqyG2ts.js → project-files-store-OhGs_H7U.js} +1 -1
  222. package/dist/web/assets/{quadrantDiagram-O4NWA36T-CpFObUjT.js → quadrantDiagram-O4NWA36T-qQZYwTy7.js} +1 -1
  223. package/dist/web/assets/{radar-RG4KPBEZ-Br8yH8rq.js → radar-RG4KPBEZ-Kdglk6eY.js} +1 -1
  224. package/dist/web/assets/{railroad-74A4TZTK-CACSqhA-.js → railroad-74A4TZTK-kVhMYx4I.js} +1 -1
  225. package/dist/web/assets/railroad-abnf-HS5TGJTU-MiKgjOXr.js +1 -0
  226. package/dist/web/assets/railroad-ebnf-LZEXJU2U-BFL9h7Zx.js +1 -0
  227. package/dist/web/assets/railroad-peg-WCYAUIDC-ChGGfIn7.js +1 -0
  228. package/dist/web/assets/{railroadDiagram-XR7U4H2S-sYXRKGK1.js → railroadDiagram-XR7U4H2S-K9SrOGcT.js} +1 -1
  229. package/dist/web/assets/{render-C7fu7uYY.js → render-Brao9Nj2.js} +1 -1
  230. package/dist/web/assets/repository-store-DZgQ5H7J.js +1 -0
  231. package/dist/web/assets/{requirementDiagram-PLB6GJNP-C9I1JbQc.js → requirementDiagram-PLB6GJNP-CtA9ZKEx.js} +1 -1
  232. package/dist/web/assets/runtime-core.esm-bundler-DDnHUoIy.js +1 -0
  233. package/dist/web/assets/{safe-storage-C76bPypO.js → safe-storage-CvVvrm4v.js} +1 -1
  234. package/dist/web/assets/{sankeyDiagram-IPEJSGJF-CuvFQhPh.js → sankeyDiagram-IPEJSGJF-BH_t4exx.js} +1 -1
  235. package/dist/web/assets/{sequenceDiagram-PO4LG4MO-CaVb9Y6e.js → sequenceDiagram-PO4LG4MO-hPNPqpDa.js} +1 -1
  236. package/dist/web/assets/single-flight-CyDZB3rb.js +1 -0
  237. package/dist/web/assets/{stateDiagram-v2-GCMORJYK-Dp_APryr.js → stateDiagram-v2-GCMORJYK-BVmjZDRD.js} +1 -1
  238. package/dist/web/assets/{swimlanes-2SLR337P-FclUI-k0.js → swimlanes-2SLR337P-Tgk8-6jj.js} +1 -1
  239. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-W1zDrK61.js +8 -0
  240. package/dist/web/assets/{terminal-store-BnEkXuBj.js → terminal-store-CKgJjtC6.js} +1 -1
  241. package/dist/web/assets/theme-slSxLNv6.js +1 -0
  242. package/dist/web/assets/{timeline-definition-EJHVYXUP-BAqwqj3c.js → timeline-definition-EJHVYXUP-lAIHEGe8.js} +1 -1
  243. package/dist/web/assets/{treeView-Q6P3EWNA-D87jJbUQ.js → treeView-Q6P3EWNA-CdvzKtWf.js} +1 -1
  244. package/dist/web/assets/{treemap-WGGIJYW6-B6nhGhRc.js → treemap-WGGIJYW6-BxDSJfrJ.js} +1 -1
  245. package/dist/web/assets/use-add-agent-context-BSHGL5iV.js +1 -0
  246. package/dist/web/assets/use-app-theme-fmVV2QM8.js +1 -0
  247. package/dist/web/assets/use-countdown-D6_qSOZJ.js +1 -0
  248. package/dist/web/assets/use-notify-error-Dx2D9IEP.js +1 -0
  249. package/dist/web/assets/{use-panel-width-CjSWCFr4.js → use-panel-width-BYOPuHat.js} +1 -1
  250. package/dist/web/assets/use-router-link-D7geCGQI.js +1 -0
  251. package/dist/web/assets/use-row-height-BK8kNihW.js +1 -0
  252. package/dist/web/assets/use-server-events-CvcbsbIf.js +1 -0
  253. package/dist/web/assets/use-virtual-scroll-DixIsJk4.js +1 -0
  254. package/dist/web/assets/{use-workspace-navigation-CnebLvBV.js → use-workspace-navigation-AaRMWmjQ.js} +1 -1
  255. package/dist/web/assets/{usecaseDiagram-POWQR4AR-7YcIKxAb.js → usecaseDiagram-POWQR4AR-BH3KvMpz.js} +1 -1
  256. package/dist/web/assets/{vennDiagram-UO4OBE2U-BZq5cFC4.js → vennDiagram-UO4OBE2U-Dfi-raKp.js} +1 -1
  257. package/dist/web/assets/viewport-store-NqQS7itT.js +1 -0
  258. package/dist/web/assets/{vue-i18n.runtime-CEtihHHL.js → vue-i18n.runtime-JOo8b7-t.js} +2 -2
  259. package/dist/web/assets/{wardley-WFR3VGLG-BT8YO9H4.js → wardley-WFR3VGLG-DmbtSM24.js} +1 -1
  260. package/dist/web/assets/{wardleyDiagram-VNRHLVJA-rDpXFf6D.js → wardleyDiagram-VNRHLVJA-DIzhGvI7.js} +1 -1
  261. package/dist/web/assets/weighted-lru-BFtZKA_y.js +1 -0
  262. package/dist/web/assets/workspace-navigation-xfxULfM7.js +1 -0
  263. package/dist/web/assets/{xychartDiagram-PMCCYNJV-ClniUuZc.js → xychartDiagram-PMCCYNJV-XgqSHH_N.js} +1 -1
  264. package/dist/web/index.html +9 -9
  265. package/package.json +5 -3
  266. package/dist/web/assets/AccessPage-CX3w30zw.js +0 -1
  267. package/dist/web/assets/AppPage-PFJG281y.js +0 -1
  268. package/dist/web/assets/CreateButton-Ch0b7juj.js +0 -1
  269. package/dist/web/assets/CreateReviewFabButton-DTuJzw3q.js +0 -1
  270. package/dist/web/assets/IndexPage-tgW07Qvs.js +0 -1
  271. package/dist/web/assets/MainLayout-C6yBhQnf.css +0 -1
  272. package/dist/web/assets/MainLayout-Cw4jpB1x.js +0 -8
  273. package/dist/web/assets/NoteVisualEditor-DiGhLhQg.js +0 -1054
  274. package/dist/web/assets/NotesPage-CVyVr9tl.js +0 -4
  275. package/dist/web/assets/NotesPage-CxGnZuVQ.css +0 -1
  276. package/dist/web/assets/PanelResize-B6FP9ZVB.js +0 -1
  277. package/dist/web/assets/ProjectFilePage-CkxfX9b1.js +0 -1
  278. package/dist/web/assets/QBtn-CyyV1Bx_.js +0 -1
  279. package/dist/web/assets/QExpansionItem-Dp9lbdVb.js +0 -4
  280. package/dist/web/assets/QFab-LOmZ9rev.js +0 -1
  281. package/dist/web/assets/QInput-Dq6131uz.js +0 -1
  282. package/dist/web/assets/QSelect-B34OqtIH.js +0 -1
  283. package/dist/web/assets/QSpace-BhOVGpQh.js +0 -1
  284. package/dist/web/assets/QVirtualScroll-BYgJ1Kh2.js +0 -1
  285. package/dist/web/assets/ReviewDetailPage-CkCilMNW.css +0 -1
  286. package/dist/web/assets/ReviewDetailPage-GNjBa4X0.js +0 -1
  287. package/dist/web/assets/ReviewsPage-An5fRS92.js +0 -1
  288. package/dist/web/assets/StatusSwitcher-DrFHHwGm.js +0 -1
  289. package/dist/web/assets/architecture-7GRP2DOG-BPCNj4lR.js +0 -1
  290. package/dist/web/assets/channel-CzRYxuLe.js +0 -1
  291. package/dist/web/assets/create-B9Tn5Xvi.js +0 -1
  292. package/dist/web/assets/diff-store-B4adGxC2.js +0 -93
  293. package/dist/web/assets/eventmodeling-NTZA5JFV-BJsTZ_nG.js +0 -1
  294. package/dist/web/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNa-Dr0goTwe.woff +0 -0
  295. package/dist/web/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ-D-x-0Q06.woff2 +0 -0
  296. package/dist/web/assets/flowDiagram-KWPJA3E3-CvXh1Df7.js +0 -1
  297. package/dist/web/assets/i18n-DnlAcIbT.js +0 -1
  298. package/dist/web/assets/index-Cw7GJi3K.js +0 -2
  299. package/dist/web/assets/index-wsOPzloa.css +0 -1
  300. package/dist/web/assets/markdown-CvtLTiBZ.js +0 -16
  301. package/dist/web/assets/pinia-DO0AdKGg.js +0 -1
  302. package/dist/web/assets/pinia-uOZCmBi-.js +0 -1
  303. package/dist/web/assets/preview-store-DyO8G-ox.js +0 -1
  304. package/dist/web/assets/railroad-abnf-HS5TGJTU-DTBsRB1c.js +0 -1
  305. package/dist/web/assets/railroad-ebnf-LZEXJU2U-D3mrY4ar.js +0 -1
  306. package/dist/web/assets/railroad-peg-WCYAUIDC-33ZjWX0u.js +0 -1
  307. package/dist/web/assets/repository-store-DtRDth1L.js +0 -1
  308. package/dist/web/assets/runtime-core.esm-bundler-CAvPiMpd.js +0 -1
  309. package/dist/web/assets/single-flight-fPyIy7h5.js +0 -1
  310. package/dist/web/assets/swimlanesDiagram-TC7HE7FX-CuUvsn0e.js +0 -8
  311. package/dist/web/assets/theme-CnbaLamF.js +0 -1
  312. package/dist/web/assets/use-add-agent-context-xHAjjOf1.js +0 -1
  313. package/dist/web/assets/use-app-theme-DEowpWRR.js +0 -1
  314. package/dist/web/assets/use-countdown-DlcwwJrs.js +0 -1
  315. package/dist/web/assets/use-router-link-CUoYaYUw.js +0 -1
  316. package/dist/web/assets/use-row-height-BsE9-rO2.js +0 -1
  317. package/dist/web/assets/use-server-events-C2X4Xz00.js +0 -1
  318. package/dist/web/assets/use-virtual-scroll-BAZGswHv.js +0 -1
  319. package/dist/web/assets/viewport-store-CCHiY2QG.js +0 -1
  320. package/dist/web/assets/weighted-lru-BGovtJ2a.js +0 -1
  321. package/dist/web/assets/workspace-navigation-DdW0rSGz.js +0 -1
  322. /package/dist/web/assets/{PanelResize-XjrbOj2S.css → AddToAgentButton-XjrbOj2S.css} +0 -0
@@ -30,7 +30,11 @@ export async function eventRoutes(app, opts) {
30
30
  const unsubscribe = eventBus.subscribe(event => {
31
31
  safeSend(reply.sse, {
32
32
  event: event.type,
33
- data: { reviewId: event.reviewId }
33
+ data: {
34
+ reviewId: event.reviewId,
35
+ storeId: event.storeId,
36
+ storeEpoch: event.storeEpoch
37
+ }
34
38
  });
35
39
  });
36
40
  reply.sse.onClose(() => {
@@ -1,4 +1,8 @@
1
+ import { isLocalPiLaunch } from "../../adapters/pi-acp/launch.js";
2
+ import { isRecord } from "../../../shared/utils/guards.js";
1
3
  import { AgentSessionError, AgentSessionStateError, AgentConfigError } from "../../errors/agents.js";
4
+ import { ARTIFACT_TASK_PROMPT_HINT } from "../../../shared/artifact-tools/instructions.js";
5
+ import { ARTIFACT_AGENT_KEY_ENV, ARTIFACT_MCP_SERVER_NAME } from "../../../shared/artifacts/mcp-constants.js";
2
6
  import { spawn } from 'node:child_process';
3
7
  import { randomUUID } from 'node:crypto';
4
8
  import { rm } from 'node:fs/promises';
@@ -16,6 +20,8 @@ export const MAX_BUFFERED_EVENTS = 5_000;
16
20
  const STDERR_TAIL_CHARS = 2_000;
17
21
  /** How long a closed connection waits for the `exit` event that carries the reason. */
18
22
  const EXIT_GRACE_MS = 500;
23
+ // Bound pending tool context retained only for server permission policies.
24
+ const MAX_PERMISSION_TOOL_CALLS = 64;
19
25
  /** The option the server picks for an auto-approved request; never a rejection. */
20
26
  function autoApproveChoice(options) {
21
27
  return (options.find(o => o.kind === 'allow_once') ??
@@ -35,11 +41,14 @@ export class AgentSession {
35
41
  presetId;
36
42
  name;
37
43
  reviewId;
44
+ kind;
45
+ #turnTaskContext;
38
46
  #status = 'starting';
39
47
  /** Set when the user (or the server) ends the session: its exit is no failure. */
40
48
  #closing = false;
41
49
  #error = null;
42
50
  #autoApprove = false;
51
+ #permissionToolCalls = new Map();
43
52
  #child;
44
53
  #connection;
45
54
  #acpSessionId;
@@ -47,6 +56,8 @@ export class AgentSession {
47
56
  #image = false;
48
57
  #config = [];
49
58
  #stderrTail = '';
59
+ #mcpSecrets = [];
60
+ #hasArtifactMcp = false;
50
61
  /** Settles when the agent process has exited. */
51
62
  #exited = Promise.resolve();
52
63
  #ownEventId = 0;
@@ -61,10 +72,35 @@ export class AgentSession {
61
72
  this.presetId = options.preset.id;
62
73
  this.name = options.name;
63
74
  this.reviewId = options.reviewId ?? null;
75
+ this.kind = options.kind;
64
76
  }
65
77
  get status() {
66
78
  return this.#status;
67
79
  }
80
+ get turnTaskContext() {
81
+ return this.#turnTaskContext;
82
+ }
83
+ prepareTaskActivation(turnId, assignmentVersion, taskId, requestId) {
84
+ // Cancellation clears the context before the agent stops reporting busy.
85
+ if (!this.#turnTaskContext || this.#turnTaskContext.turnId !== turnId) {
86
+ throw new AgentSessionStateError('The response ended or its task context changed.');
87
+ }
88
+ const context = Object.freeze({
89
+ sessionId: this.id,
90
+ turnId,
91
+ assignmentVersion,
92
+ taskIds: Object.freeze([taskId]),
93
+ source: 'turn_activation',
94
+ activationRequestId: requestId
95
+ });
96
+ // The caller commits and applies synchronously, without yielding to cancellation or another prompt.
97
+ return {
98
+ context,
99
+ apply: () => {
100
+ this.#turnTaskContext = context;
101
+ }
102
+ };
103
+ }
68
104
  /** Whether the agent advertised `promptCapabilities.embeddedContext`. */
69
105
  get supportsEmbeddedContext() {
70
106
  return this.#embeddedContext;
@@ -76,8 +112,12 @@ export class AgentSession {
76
112
  info() {
77
113
  return {
78
114
  id: this.id,
115
+ ...(this.#options.taskIds
116
+ ? { taskIds: this.#options.taskIds(this.id) }
117
+ : {}),
79
118
  presetId: this.presetId,
80
119
  name: this.name,
120
+ ...(this.kind === undefined ? {} : { kind: this.kind }),
81
121
  status: this.#status,
82
122
  reviewId: this.reviewId,
83
123
  autoApprove: this.#autoApprove,
@@ -106,7 +146,7 @@ export class AgentSession {
106
146
  catch (error) {
107
147
  // An exit during the start already reported itself (`#onExit`).
108
148
  if (this.#error === null && !this.#closing) {
109
- const message = errorMessage(error);
149
+ const message = this.#redact(errorMessage(error));
110
150
  this.#error = message;
111
151
  this.#emit({ type: 'error', message });
112
152
  }
@@ -115,16 +155,28 @@ export class AgentSession {
115
155
  }
116
156
  async #start() {
117
157
  const { preset, cwd } = this.#options;
158
+ const localPi = isLocalPiLaunch(preset);
159
+ if (preset.id === 'pi' && this.kind === 'validation' && !localPi) {
160
+ throw new AgentSessionError('Pi validation requires the trusted local GitHuman pi launcher. The configured command is available for chat only.');
161
+ }
118
162
  const child = spawn(preset.command, preset.args, {
119
163
  cwd,
120
164
  stdio: ['pipe', 'pipe', 'pipe'],
121
- env: { ...process.env, ...preset.env }
165
+ env: {
166
+ ...process.env,
167
+ ...preset.env,
168
+ ...(localPi
169
+ ? {
170
+ GITHUMAN_PI_PROFILE: this.kind === 'validation' ? 'validator' : 'chat'
171
+ }
172
+ : {})
173
+ }
122
174
  });
123
175
  this.#child = child;
124
176
  this.#exited = new Promise(resolve => child.once('exit', resolve));
125
177
  child.stderr.setEncoding('utf-8');
126
178
  child.stderr.on('data', (chunk) => {
127
- this.#stderrTail = (this.#stderrTail + chunk).slice(-STDERR_TAIL_CHARS);
179
+ this.#stderrTail = this.#redact(this.#stderrTail + chunk).slice(-STDERR_TAIL_CHARS);
128
180
  });
129
181
  // Swallow stdin EPIPE after the agent died; the closed connection reports it.
130
182
  child.stdin.on('error', () => { });
@@ -181,10 +233,28 @@ export class AgentSession {
181
233
  const prompt = init.agentCapabilities?.promptCapabilities;
182
234
  this.#embeddedContext = prompt?.embeddedContext === true;
183
235
  this.#image = prompt?.image === true;
236
+ const mcpServers = typeof this.#options.mcpServers === 'function'
237
+ ? this.#options.mcpServers(this.id)
238
+ : (this.#options.mcpServers ?? []);
239
+ this.#hasArtifactMcp = mcpServers.some(server => server.name === ARTIFACT_MCP_SERVER_NAME);
240
+ this.#mcpSecrets = mcpServers.flatMap(server => 'env' in server
241
+ ? server.env
242
+ .filter(entry => entry.name === ARTIFACT_AGENT_KEY_ENV)
243
+ .map(entry => entry.value)
244
+ : []);
184
245
  const created = await connection.agent.request(acp.methods.agent.session.new, {
185
246
  cwd: this.#options.cwd,
186
- mcpServers: []
247
+ mcpServers
187
248
  });
249
+ if (isLocalPiLaunch(this.#options.preset)) {
250
+ const ack = created['_meta']?.githumanBridge;
251
+ if (!isRecord(ack) ||
252
+ ack.version !== 1 ||
253
+ ack.profile !== (this.kind === 'validation' ? 'validator' : 'chat') ||
254
+ ack.registryGeneration !== 1) {
255
+ throw new AgentSessionError('Pi bridge did not confirm its launch profile and MCP registry.');
256
+ }
257
+ }
188
258
  this.#acpSessionId = created.sessionId;
189
259
  this.#setConfig(mapConfigOptions(created.configOptions));
190
260
  }
@@ -223,7 +293,9 @@ export class AgentSession {
223
293
  this.#setConfig(mapConfigOptions(response.configOptions));
224
294
  }
225
295
  catch (error) {
226
- throw new AgentSessionError(errorMessage(error), { cause: error });
296
+ throw new AgentSessionError(this.#redact(errorMessage(error)), {
297
+ cause: error
298
+ });
227
299
  }
228
300
  }
229
301
  /** The agent process ended: report why, unless the session itself ended it. */
@@ -257,8 +329,15 @@ export class AgentSession {
257
329
  }
258
330
  this.#markClosed();
259
331
  }
332
+ // Hides the session key in errors and stderr. Messages that the agent itself writes stay as they are.
333
+ #redact(text) {
334
+ for (const secret of this.#mcpSecrets) {
335
+ text = text.replaceAll(secret, '[redacted]');
336
+ }
337
+ return text;
338
+ }
260
339
  #exitMessage(code, signal) {
261
- const tail = this.#stderrTail.trim();
340
+ const tail = this.#redact(this.#stderrTail.trim());
262
341
  return (`Agent exited (${signal ?? `code ${code}`})` +
263
342
  (tail === '' ? '' : `: ${tail.slice(-400)}`));
264
343
  }
@@ -268,13 +347,29 @@ export class AgentSession {
268
347
  */
269
348
  prompt(blocks, display) {
270
349
  const { connection, sessionId } = this.#readyChannel();
350
+ const assignment = this.#options.taskAssignment?.(this.id);
351
+ const turn = Object.freeze({
352
+ turnId: randomUUID(),
353
+ sessionId: this.id,
354
+ assignmentVersion: assignment?.version ?? 0,
355
+ source: 'turn_assignment',
356
+ taskIds: Object.freeze([...(assignment?.taskIds ?? [])])
357
+ });
358
+ this.#turnTaskContext = turn;
271
359
  this.#setStatus('busy');
272
360
  this.#emit({
273
361
  type: 'user',
274
362
  text: display.text,
275
363
  context: display.context ?? []
276
364
  });
277
- void this.#runPrompt(connection, sessionId, blocks);
365
+ // Append the reminder without changing the user transcript or validator assignments.
366
+ const prompt = this.#hasArtifactMcp && !this.#options.permissionPolicy
367
+ ? [
368
+ ...blocks,
369
+ { type: 'text', text: ARTIFACT_TASK_PROMPT_HINT }
370
+ ]
371
+ : blocks;
372
+ void this.#runPrompt(connection, sessionId, prompt, turn.turnId);
278
373
  }
279
374
  /**
280
375
  * Throws when the session cannot take a prompt now. The route calls this
@@ -293,12 +388,14 @@ export class AgentSession {
293
388
  }
294
389
  return { connection, sessionId };
295
390
  }
296
- async #runPrompt(connection, sessionId, blocks) {
391
+ async #runPrompt(connection, sessionId, blocks, turnId) {
297
392
  try {
298
393
  const response = await connection.agent.request(acp.methods.agent.session.prompt, { sessionId, prompt: blocks });
394
+ this.#clearTurnTaskContext(turnId);
299
395
  this.#emit({ type: 'stop', stopReason: response.stopReason });
300
396
  }
301
397
  catch (error) {
398
+ this.#clearTurnTaskContext(turnId);
302
399
  // A failure of the transport comes before the `exit` event, and only
303
400
  // that event can say why the agent died: let it report first.
304
401
  if (!(error instanceof acp.RequestError)) {
@@ -308,10 +405,14 @@ export class AgentSession {
308
405
  ]);
309
406
  }
310
407
  if (!this.#isClosed() && this.#error === null) {
311
- this.#emit({ type: 'error', message: errorMessage(error) });
408
+ this.#emit({
409
+ type: 'error',
410
+ message: this.#redact(errorMessage(error))
411
+ });
312
412
  }
313
413
  }
314
414
  finally {
415
+ this.#clearTurnTaskContext(turnId);
315
416
  // A request still open once the turn is over (it ended in an error, say)
316
417
  // is dead: answering it later would still make the agent act on it.
317
418
  this.#cancelPendingPermissions();
@@ -320,6 +421,11 @@ export class AgentSession {
320
421
  }
321
422
  }
322
423
  }
424
+ #clearTurnTaskContext(turnId) {
425
+ if (this.#turnTaskContext?.turnId === turnId) {
426
+ this.#turnTaskContext = undefined;
427
+ }
428
+ }
323
429
  /** A method, so TypeScript does not keep a stale narrowing across an `await`. */
324
430
  #isClosed() {
325
431
  return this.#status === 'closed';
@@ -328,6 +434,8 @@ export class AgentSession {
328
434
  if (this.#status !== 'busy' || !this.#connection || !this.#acpSessionId) {
329
435
  return;
330
436
  }
437
+ // Stop inheritance immediately; the agent may take time to finish cancellation.
438
+ this.#turnTaskContext = undefined;
331
439
  try {
332
440
  await this.#connection.agent.notify(acp.methods.agent.session.cancel, {
333
441
  sessionId: this.#acpSessionId
@@ -336,7 +444,9 @@ export class AgentSession {
336
444
  catch (error) {
337
445
  // The connection closed since the check above: the turn is over anyway.
338
446
  if (!this.#isClosed()) {
339
- throw new AgentSessionError(errorMessage(error), { cause: error });
447
+ throw new AgentSessionError(this.#redact(errorMessage(error)), {
448
+ cause: error
449
+ });
340
450
  }
341
451
  }
342
452
  }
@@ -367,6 +477,9 @@ export class AgentSession {
367
477
  if (this.#status === 'closed') {
368
478
  throw new AgentSessionStateError('The session is closed');
369
479
  }
480
+ if (enabled && this.#options.permissionPolicy) {
481
+ throw new AgentSessionStateError('This session uses a fixed permission policy.');
482
+ }
370
483
  if (this.#autoApprove === enabled) {
371
484
  return;
372
485
  }
@@ -396,6 +509,9 @@ export class AgentSession {
396
509
  if (this.#status !== 'busy') {
397
510
  return Promise.resolve({ outcome: { outcome: 'cancelled' } });
398
511
  }
512
+ if (this.#options.permissionPolicy) {
513
+ return Promise.resolve(this.#policyPermission(params));
514
+ }
399
515
  const requestId = randomUUID();
400
516
  const request = mapPermissionRequest(requestId, params);
401
517
  if (this.#autoApprove) {
@@ -416,6 +532,40 @@ export class AgentSession {
416
532
  this.#emit(request);
417
533
  });
418
534
  }
535
+ #policyPermission(params) {
536
+ const previous = this.#permissionToolCalls.get(params.toolCall.toolCallId);
537
+ this.#permissionToolCalls.delete(params.toolCall.toolCallId);
538
+ let decision = {
539
+ allow: false,
540
+ reason: 'Permission policy could not verify this action.'
541
+ };
542
+ try {
543
+ decision = this.#options.permissionPolicy(params, previous);
544
+ }
545
+ catch {
546
+ // Policy failures must not create a manual prompt or grant an action.
547
+ }
548
+ const choice = decision.allow
549
+ ? params.options.find(option => option.kind === 'allow_once')
550
+ : undefined;
551
+ if (choice) {
552
+ this.#emit({ type: 'auto-approved', title: decision.reason });
553
+ return { outcome: { outcome: 'selected', optionId: choice.optionId } };
554
+ }
555
+ this.#emit({
556
+ type: 'tool',
557
+ toolCallId: params.toolCall.toolCallId,
558
+ title: 'Validation permission denied',
559
+ status: 'failed',
560
+ output: decision.allow
561
+ ? 'Permission policy requires an allow_once option.'
562
+ : decision.reason
563
+ });
564
+ const reject = params.options.find(option => option.kind === 'reject_once');
565
+ return reject
566
+ ? { outcome: { outcome: 'selected', optionId: reject.optionId } }
567
+ : { outcome: { outcome: 'cancelled' } };
568
+ }
419
569
  #onUpdate(update) {
420
570
  // Agents may speak outside a turn — pi greets with its version and skill
421
571
  // list right after session/new. That is noise in a chat transcript.
@@ -423,6 +573,24 @@ export class AgentSession {
423
573
  update.sessionUpdate === 'agent_message_chunk') {
424
574
  return;
425
575
  }
576
+ if (this.#options.permissionPolicy &&
577
+ this.#status === 'busy' &&
578
+ (update.sessionUpdate === 'tool_call' ||
579
+ update.sessionUpdate === 'tool_call_update')) {
580
+ if (update.status === 'completed' || update.status === 'failed') {
581
+ this.#permissionToolCalls.delete(update.toolCallId);
582
+ }
583
+ else {
584
+ const previous = this.#permissionToolCalls.get(update.toolCallId);
585
+ this.#permissionToolCalls.set(update.toolCallId, {
586
+ ...previous,
587
+ ...update
588
+ });
589
+ if (this.#permissionToolCalls.size > MAX_PERMISSION_TOOL_CALLS) {
590
+ this.#permissionToolCalls.delete(this.#permissionToolCalls.keys().next().value);
591
+ }
592
+ }
593
+ }
426
594
  const event = mapSessionUpdate(update);
427
595
  if (event?.type === 'config') {
428
596
  // Keep `#config` current: `setConfig` checks values against it.
@@ -476,12 +644,14 @@ export class AgentSession {
476
644
  if (this.#status === 'closed') {
477
645
  return;
478
646
  }
647
+ this.#turnTaskContext = undefined;
479
648
  this.#setStatus('closed', this.#error ?? undefined);
480
649
  void rm(this.attachmentDir, { recursive: true, force: true }).catch(() => { });
481
650
  // Unanswered permission requests must not leave the agent blocked forever.
482
651
  this.#cancelPendingPermissions();
483
652
  }
484
653
  #cancelPendingPermissions() {
654
+ this.#permissionToolCalls.clear();
485
655
  // Each resolver removes itself from the map, which Map iteration allows.
486
656
  for (const pending of this.#pendingPermissions.values()) {
487
657
  pending.resolve({ outcome: { outcome: 'cancelled' } });
@@ -1,3 +1,4 @@
1
+ import { artifactBody } from "../../../shared/utils/artifact-body.js";
1
2
  import { MAX_ATTACHMENT_BYTES } from "../../../shared/agents/constants.js";
2
3
  import { AgentContextError } from "../../errors/agents.js";
3
4
  import { errorMessage } from "../../../shared/utils/error-message.js";
@@ -33,8 +34,9 @@ function fenceFor(text) {
33
34
  * goes in as an embedded resource when the agent supports it, otherwise as a
34
35
  * fenced block in a plain text part — same content either way.
35
36
  */
36
- function embed(deps, uri, title, mimeType, text) {
37
- const body = truncateText(text, MAX_EMBEDDED_CHARS, omitted => `\n… [truncated by githuman: ${omitted} more characters]`);
37
+ function embed(deps, uri, title, mimeType, text, prefix = '') {
38
+ const body = prefix +
39
+ truncateText(text, MAX_EMBEDDED_CHARS, omitted => `\n… [truncated by githuman: ${omitted} more characters]`);
38
40
  if (deps.embeddedContext) {
39
41
  return { type: 'resource', resource: { uri, mimeType, text: body } };
40
42
  }
@@ -173,7 +175,33 @@ async function buildNoteItem(item, deps) {
173
175
  throw asContextError(error);
174
176
  }
175
177
  }
178
+ async function buildArtifactItem(item, deps) {
179
+ try {
180
+ const storage = deps.artifacts;
181
+ if (!storage || item.storeId !== storage.storeId) {
182
+ throw new AgentContextError('Artifact store is unavailable or has changed');
183
+ }
184
+ const { content, ...snapshot } = storage.graph.readDocumentContext(item.artifactId);
185
+ const { artifact } = snapshot;
186
+ const prefix = 'Artifact metadata snapshot (current; original frontmatter is historical):\n' +
187
+ JSON.stringify(snapshot) +
188
+ '\n\n' +
189
+ 'Use artifacts.relations with direction and nextCursor to read remaining links. Read related documents explicitly via artifacts.get and artifacts.get_content; they are not embedded here.\n' +
190
+ 'Cite documents with /#/artifacts?storeId=' +
191
+ encodeURIComponent(storage.storeId) +
192
+ '&selectedId=' +
193
+ encodeURIComponent(artifact.id) +
194
+ '. Related document citations use their own selectedId.\n' +
195
+ 'Use artifacts.get_content with this artifact ID and offset=0 to read the full original UTF-8 file; follow nextOffset. bodyStartLine identifies the body. Cite artifact IDs and distinguish historical statuses from current accepted context.\n\nDocument body:\n';
196
+ return embed(deps, `githuman://artifact/${encodeURIComponent(item.storeId)}/${encodeURIComponent(artifact.id)}`, `Artifact ${artifact.title}:`, 'text/markdown', artifactBody(content.toString('utf8'), artifact.bodyStartLine), prefix);
197
+ }
198
+ catch (error) {
199
+ throw asContextError(error);
200
+ }
201
+ }
176
202
  function buildItem(item, deps) {
203
+ if (item.kind === 'artifact')
204
+ return buildArtifactItem(item, deps);
177
205
  if (item.kind === 'note')
178
206
  return buildNoteItem(item, deps);
179
207
  if (item.kind === 'diff') {
@@ -1,3 +1,8 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { ARTIFACT_MCP_SERVER_NAME, ARTIFACT_AGENT_BEARER_PATTERN, ARTIFACT_AGENT_KEY_BYTES, ARTIFACT_AGENT_KEY_ENV, ARTIFACT_AGENT_URL_ENV } from "../../../shared/artifacts/mcp-constants.js";
3
+ import { fileURLToPath } from 'node:url';
4
+ import { sha256Hex } from "../../utils/hash.js";
5
+ import { ForbiddenError } from "../../errors/http.js";
1
6
  import { UnknownAgentPresetError, TooManyAgentSessionsError } from "../../errors/agents.js";
2
7
  import { MAX_AGENT_SESSIONS } from "../../../shared/agents/constants.js";
3
8
  import { uniqueChatName } from "../../../shared/agents/chat-names.js";
@@ -7,13 +12,61 @@ export function createSessionRegistry(options) {
7
12
  const sessions = new Map();
8
13
  const unsubscribeSession = new Map();
9
14
  const listeners = new Set();
15
+ const keys = new Map();
16
+ const sessionKeys = new Map();
17
+ const deviceReleases = new Map();
18
+ const requests = new Map();
10
19
  let lastEventId = 0;
20
+ function revoke(id) {
21
+ const key = sessionKeys.get(id);
22
+ if (key)
23
+ keys.delete(key);
24
+ sessionKeys.delete(id);
25
+ deviceReleases.get(id)?.();
26
+ deviceReleases.delete(id);
27
+ for (const close of requests.get(id) ?? [])
28
+ close();
29
+ requests.delete(id);
30
+ }
31
+ function mcpServers(id) {
32
+ const url = options.artifactUrl?.();
33
+ if (!url)
34
+ return [];
35
+ if (sessions.get(id)?.status === 'closed') {
36
+ throw new ForbiddenError('Agent session is closed.');
37
+ }
38
+ // One live key per session: a second call replaces the first.
39
+ const previous = sessionKeys.get(id);
40
+ if (previous)
41
+ keys.delete(previous);
42
+ const key = randomBytes(ARTIFACT_AGENT_KEY_BYTES).toString('hex');
43
+ const hash = sha256Hex(key);
44
+ keys.set(hash, id);
45
+ sessionKeys.set(id, hash);
46
+ const extension = import.meta.url.endsWith('.ts') ? 'ts' : 'js';
47
+ return [
48
+ {
49
+ name: ARTIFACT_MCP_SERVER_NAME,
50
+ command: process.execPath,
51
+ args: [
52
+ fileURLToPath(new URL(`../../../cli/index.${extension}`, import.meta.url)),
53
+ 'mcp',
54
+ '--stdio'
55
+ ],
56
+ env: [
57
+ { name: ARTIFACT_AGENT_URL_ENV, value: url },
58
+ { name: ARTIFACT_AGENT_KEY_ENV, value: key }
59
+ ]
60
+ }
61
+ ];
62
+ }
11
63
  function broadcast(message) {
12
64
  for (const listener of listeners) {
13
65
  listener(message);
14
66
  }
15
67
  }
16
68
  function drop(id) {
69
+ revoke(id);
17
70
  unsubscribeSession.get(id)?.();
18
71
  unsubscribeSession.delete(id);
19
72
  sessions.delete(id);
@@ -27,8 +80,40 @@ export function createSessionRegistry(options) {
27
80
  }
28
81
  }
29
82
  return {
83
+ authenticateArtifactKey(authorization) {
84
+ options.connections?.recheck();
85
+ if (!authorization ||
86
+ !ARTIFACT_AGENT_BEARER_PATTERN.test(authorization)) {
87
+ return undefined;
88
+ }
89
+ const id = keys.get(sha256Hex(authorization.slice(7)));
90
+ if (!id || sessions.get(id)?.status === 'closed')
91
+ return undefined;
92
+ return id;
93
+ },
94
+ trackArtifactRequest(id, close) {
95
+ let pending = requests.get(id);
96
+ if (!pending) {
97
+ pending = new Set();
98
+ requests.set(id, pending);
99
+ }
100
+ pending.add(close);
101
+ let released = false;
102
+ // A response fires both finish and close, so the second call must do nothing.
103
+ return () => {
104
+ if (released)
105
+ return;
106
+ released = true;
107
+ pending.delete(close);
108
+ if (pending.size === 0 && requests.get(id) === pending) {
109
+ requests.delete(id);
110
+ }
111
+ };
112
+ },
113
+ notifyTasksChanged: () => broadcast({ type: 'sessions' }),
114
+ notifyArtifactActionsChanged: () => broadcast({ type: 'artifact-actions' }),
30
115
  listPresets: () => options.presets.map(describePreset),
31
- create(presetId, reviewId, name) {
116
+ create(presetId, reviewId, name, deviceId, permissionPolicy, kind) {
32
117
  const preset = options.presets.find(p => p.id === presetId);
33
118
  if (!preset) {
34
119
  throw new UnknownAgentPresetError(`Unknown agent "${presetId}"`);
@@ -38,10 +123,17 @@ export function createSessionRegistry(options) {
38
123
  }
39
124
  const session = new AgentSession({
40
125
  preset,
126
+ permissionPolicy,
127
+ mcpServers,
41
128
  // Open chats are told apart by name: a taken one gets "(1)", "(2)", …
42
129
  name: uniqueChatName(name?.trim() || preset.title, [...sessions.values()].map(s => s.name)),
43
130
  cwd: options.repositoryPath,
44
131
  reviewId,
132
+ ...(kind === undefined ? {} : { kind }),
133
+ ...(options.taskIds ? { taskIds: options.taskIds } : {}),
134
+ ...(options.taskAssignment
135
+ ? { taskAssignment: options.taskAssignment }
136
+ : {}),
45
137
  nextEventId: () => ++lastEventId,
46
138
  ...(options.bufferLimit === undefined
47
139
  ? {}
@@ -49,17 +141,29 @@ export function createSessionRegistry(options) {
49
141
  });
50
142
  sessions.set(session.id, session);
51
143
  unsubscribeSession.set(session.id, session.onEvent(envelope => {
144
+ if (envelope.event.type === 'status' &&
145
+ envelope.event.status === 'closed') {
146
+ revoke(session.id);
147
+ }
52
148
  broadcast({
53
149
  type: 'event',
54
150
  envelope: { ...envelope, sessionId: session.id }
55
151
  });
56
152
  }));
153
+ if (deviceId !== undefined) {
154
+ const release = options.connections?.trackDevice(deviceId, () => session.close());
155
+ if (!release) {
156
+ session.close();
157
+ return session;
158
+ }
159
+ deviceReleases.set(session.id, release);
160
+ }
57
161
  broadcast({ type: 'sessions' });
58
162
  void session.start();
59
163
  return session;
60
164
  },
61
165
  get: id => sessions.get(id),
62
- list: () => [...sessions.values()].map(s => s.info()),
166
+ list: () => [...sessions.values()].map(session => session.info()),
63
167
  close(id) {
64
168
  const session = sessions.get(id);
65
169
  if (!session) {
@@ -0,0 +1 @@
1
+ export {};