forge-openclaw-plugin 0.3.15 → 0.3.17

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 (415) hide show
  1. package/README.md +6 -6
  2. package/dist/assets/{action-bar-B9MYlps2.js → action-bar-D3CEYlj4.js} +1 -1
  3. package/dist/assets/{activity-page-DbzbChcE.js → activity-page-Ce1pHIu5.js} +1 -1
  4. package/dist/assets/{ai-surface-workspace-CF0257Hs.js → ai-surface-workspace-DEliwOjF.js} +1 -1
  5. package/dist/assets/artifacts-page-BgyFVIVM.js +2 -0
  6. package/dist/assets/{atlas-panel-CO3RYAKn.js → atlas-panel-VZOnJa1c.js} +1 -1
  7. package/dist/assets/{board-CuxQRKPJ.js → board-DprRipIG.js} +1 -1
  8. package/dist/assets/{calendar-page-BuuHKEHC.js → calendar-page-Zstsn4uG.js} +1 -1
  9. package/dist/assets/{calendar-rules-DKftgNx5.js → calendar-rules-BGIBuEDk.js} +1 -1
  10. package/dist/assets/{calendar-week-toolbar-ChIpkT-G.js → calendar-week-toolbar-DXFkMkbq.js} +1 -1
  11. package/dist/assets/{charts-BzT4pUPg.js → charts-C5S0-BL7.js} +1 -1
  12. package/dist/assets/{companion-sync-lab-page-BZRX4Btw.js → companion-sync-lab-page-DIPPLFKt.js} +1 -1
  13. package/dist/assets/{daily-metrics-dashboard-CXDsaAQd.js → daily-metrics-dashboard-CG-OOCJz.js} +1 -1
  14. package/dist/assets/{define-workbench-box-CpG0Zb1L.js → define-workbench-box-BEEdtBkQ.js} +1 -1
  15. package/dist/assets/{entity-link-multiselect-Dl4rZqdg.js → entity-link-multiselect-BfDAROHB.js} +1 -1
  16. package/dist/assets/{entity-note-count-link-Bs1aKYyD.js → entity-note-count-link-BGOiaash.js} +1 -1
  17. package/dist/assets/{entity-notes-surface-B56XSw7M.js → entity-notes-surface-DPsJwqQa.js} +1 -1
  18. package/dist/assets/execution-board-Dg-2GSKv.js +1 -0
  19. package/dist/assets/{faceted-token-search-Dg2rjknH.js → faceted-token-search-DV7MSVYb.js} +1 -1
  20. package/dist/assets/{flagship-signal-deck-C6KVPhmM.js → flagship-signal-deck-D3wVL3bC.js} +1 -1
  21. package/dist/assets/{floating-action-menu-DAFAEBcA.js → floating-action-menu-BUs382px.js} +1 -1
  22. package/dist/assets/{forms-D1qJ3oOP.js → forms-ByKjodjN.js} +1 -1
  23. package/dist/assets/{generic-node-view-C6DK5hJ6.js → generic-node-view-BY5dbPXJ.js} +1 -1
  24. package/dist/assets/{goal-detail-page-CC4VXud6.js → goal-detail-page-MYr6j2B4.js} +1 -1
  25. package/dist/assets/{goal-dialog-15hD8EBp.js → goal-dialog-nkdME5Uy.js} +1 -1
  26. package/dist/assets/goals-page-BsFeBzU1.js +1 -0
  27. package/dist/assets/{graph-BF4IsheG.js → graph-CHVj0Z7n.js} +1 -1
  28. package/dist/assets/habits-page-wO461EVU.js +1 -0
  29. package/dist/assets/{health-boxes-DqgvIYoL.js → health-boxes-BphMeSJj.js} +1 -1
  30. package/dist/assets/index-CSd8ylsy.css +1 -0
  31. package/dist/assets/index-gmPaoLN-.js +2 -0
  32. package/dist/assets/{inline-note-fields-COgzxr_7.js → inline-note-fields-BvQwmMAv.js} +1 -1
  33. package/dist/assets/{insight-flow-dialog-Dmb6NSGp.js → insight-flow-dialog-H_eaOmst.js} +1 -1
  34. package/dist/assets/{insights-page-OnqR4cYI.js → insights-page-DINYe3cB.js} +3 -3
  35. package/dist/assets/{kanban-boxes-BWUzntCV.js → kanban-boxes-oACOTDYG.js} +1 -1
  36. package/dist/assets/{kanban-page-BI16Gzp_.js → kanban-page-BGLXH91f.js} +1 -1
  37. package/dist/assets/knowledge-graph-page-VfMvw5jL.js +1 -0
  38. package/dist/assets/{life-force-page-CDEXEQai.js → life-force-page-DQPb6UL-.js} +1 -1
  39. package/dist/assets/{life-force-workspace-C7UOnJEf.js → life-force-workspace-BJ1aI3JE.js} +1 -1
  40. package/dist/assets/{maps-BTVHALP8.js → maps-CvEHVkBk.js} +1 -1
  41. package/dist/assets/{metric-tile-boeHB1R1.js → metric-tile-CbEe19Gd.js} +1 -1
  42. package/dist/assets/{motion-DcgUnXhY.js → motion-BibSzp57.js} +1 -1
  43. package/dist/assets/{movement-boxes-BUSqaTL2.js → movement-boxes-dtDYhw5c.js} +1 -1
  44. package/dist/assets/{movement-page-DcbO0497.js → movement-page-BhmwCHjs.js} +1 -1
  45. package/dist/assets/{note-markdown-DXXI3W3V.js → note-markdown-CG8u2d6p.js} +1 -1
  46. package/dist/assets/{note-tags-input-CYh3TVW2.js → note-tags-input-D_conqDO.js} +1 -1
  47. package/dist/assets/{notes-boxes-CMJXX2K0.js → notes-boxes-BLKucFqW.js} +1 -1
  48. package/dist/assets/notes-page-CafVxWjb.js +1 -0
  49. package/dist/assets/{open-in-graph-button-4UYrp1XP.js → open-in-graph-button-DFQ4r6SN.js} +1 -1
  50. package/dist/assets/{orbit-map-BwK7sDaC.js → orbit-map-DsEl3ori.js} +1 -1
  51. package/dist/assets/overview-page-BffcX-pM.js +1 -0
  52. package/dist/assets/page-hero-U603l9rQ.js +1 -0
  53. package/dist/assets/pill-cluster-BYVFNppV.js +1 -0
  54. package/dist/assets/{preference-entity-handoff-button-C2ATjvws.js → preference-entity-handoff-button-cPrTpHsV.js} +1 -1
  55. package/dist/assets/{preferences-page-BAexXHye.js → preferences-page-DQjFIOmt.js} +1 -1
  56. package/dist/assets/{project-collections-B9nr-Ts-.js → project-collections-B9OGHGqm.js} +1 -1
  57. package/dist/assets/project-detail-page-r1Sw2kcO.js +1 -0
  58. package/dist/assets/{project-dialog-CBA-D65n.js → project-dialog-B2_PcXAf.js} +1 -1
  59. package/dist/assets/project-management-hierarchy-page-qLNG8hsh.js +1 -0
  60. package/dist/assets/{project-management-section-nav-DJ3QKCtr.js → project-management-section-nav-DFUlmMEB.js} +1 -1
  61. package/dist/assets/{projects-boxes-iBu_PRqe.js → projects-boxes-BA7_XaqX.js} +1 -1
  62. package/dist/assets/projects-page-B6ggXWFw.js +1 -0
  63. package/dist/assets/psyche-behaviors-page-ByWpXlLq.js +5 -0
  64. package/dist/assets/{psyche-flashcards-page-DQaw_vUQ.js → psyche-flashcards-page-B9nSc0mk.js} +1 -1
  65. package/dist/assets/{psyche-goal-map-page-C-ZTVOEP.js → psyche-goal-map-page-BQU7qI88.js} +1 -1
  66. package/dist/assets/{psyche-graph-DYzeClxn.js → psyche-graph-DCeP7r2b.js} +1 -1
  67. package/dist/assets/{psyche-metrics-page-C9hKn10A.js → psyche-metrics-page-DD2lOP-J.js} +1 -1
  68. package/dist/assets/psyche-mode-guide-page-DbtZgnjr.js +1 -0
  69. package/dist/assets/psyche-modes-page-3zFJWFQf.js +1 -0
  70. package/dist/assets/psyche-page-D5vXYkfB.js +1 -0
  71. package/dist/assets/psyche-patterns-page-BneAAeKS.js +5 -0
  72. package/dist/assets/{psyche-questionnaire-builder-page-gRwdGXde.js → psyche-questionnaire-builder-page-BH1la5C9.js} +1 -1
  73. package/dist/assets/{psyche-questionnaire-detail-page-CIP9b2UI.js → psyche-questionnaire-detail-page-DYav634P.js} +1 -1
  74. package/dist/assets/psyche-questionnaire-run-detail-page-9VaijhJm.js +1 -0
  75. package/dist/assets/{psyche-questionnaire-run-page-CXiJyd5i.js → psyche-questionnaire-run-page-e1T6k_dS.js} +1 -1
  76. package/dist/assets/{psyche-questionnaires-page-CFPKwA3O.js → psyche-questionnaires-page-Dyv367z9.js} +1 -1
  77. package/dist/assets/{psyche-report-detail-page-dU30a2WE.js → psyche-report-detail-page-hqxHIXw2.js} +3 -3
  78. package/dist/assets/psyche-reports-page-DhIfcGbt.js +1 -0
  79. package/dist/assets/{psyche-schemas-DDol0j-g.js → psyche-schemas-Cg51Ztxz.js} +1 -1
  80. package/dist/assets/psyche-schemas-beliefs-page-Dgb5o9vx.js +9 -0
  81. package/dist/assets/{psyche-screen-time-page-lIe6GQxJ.js → psyche-screen-time-page-D6MnJGNa.js} +1 -1
  82. package/dist/assets/{psyche-self-observation-page-BTE3KfIl.js → psyche-self-observation-page-C1Vyezbv.js} +1 -1
  83. package/dist/assets/{psyche-values-page-DclBZ9xw.js → psyche-values-page-B_JWVw1Q.js} +2 -2
  84. package/dist/assets/{question-flow-dialog-Ded2E85L.js → question-flow-dialog-yMYVk998.js} +2 -2
  85. package/dist/assets/{report-chain-fields-DY640iqL.js → report-chain-fields-D4NM9a6I.js} +1 -1
  86. package/dist/assets/rewards-page-B5-bcL4R.js +1 -0
  87. package/dist/assets/{scheduling-rules-editor-jakFfxqF.js → scheduling-rules-editor-De72IUax.js} +1 -1
  88. package/dist/assets/{schema-badge-30B5syHA.js → schema-badge-jX28kUa6.js} +1 -1
  89. package/dist/assets/{schemas-Db29G8NU.js → schemas-BlFy-uPR.js} +1 -1
  90. package/dist/assets/{select-menu-BF2zI3RW.js → select-menu-BXv1rsVc.js} +1 -1
  91. package/dist/assets/{settings-agents-page-Bh-Bv6FQ.js → settings-agents-page-DN_np10S.js} +1 -1
  92. package/dist/assets/settings-bin-page-BM5BpK_i.js +1 -0
  93. package/dist/assets/{settings-calendar-page-BA4_Qqiu.js → settings-calendar-page-Cg0gSSFs.js} +3 -3
  94. package/dist/assets/{settings-data-page-K4kpmQJY.js → settings-data-page-Dwmp2rLy.js} +1 -1
  95. package/dist/assets/{settings-logs-page-DkuNPAZo.js → settings-logs-page-DQNM21Re.js} +1 -1
  96. package/dist/assets/{settings-mobile-page-4pCNwD91.js → settings-mobile-page-DQdiRa-a.js} +1 -1
  97. package/dist/assets/settings-models-page-CAEPpuHF.js +1 -0
  98. package/dist/assets/{settings-page-B_Be0vOY.js → settings-page-Cl2KiCFg.js} +1 -1
  99. package/dist/assets/{settings-rewards-page-C6nFTWJu.js → settings-rewards-page-DoIUumhe.js} +1 -1
  100. package/dist/assets/{settings-section-nav-Lo-VKCfZ.js → settings-section-nav-D_N87IG0.js} +1 -1
  101. package/dist/assets/{settings-users-page-CVzNp4-m.js → settings-users-page-CPesR62N.js} +1 -1
  102. package/dist/assets/{settings-wiki-page-DXM--7BB.js → settings-wiki-page-BxMQM5Vi.js} +1 -1
  103. package/dist/assets/{sleep-page-DvPdZMKx.js → sleep-page-DUYmQKrg.js} +1 -1
  104. package/dist/assets/{sports-page-DjuZlOur.js → sports-page-Bzi5TF9T.js} +1 -1
  105. package/dist/assets/{state-Bpe5dF3T.js → state-BFyKSULP.js} +1 -1
  106. package/dist/assets/{strategies-page-PE1IlatS.js → strategies-page-C-T2MZx4.js} +1 -1
  107. package/dist/assets/{strategy-detail-page-C4KiEGCI.js → strategy-detail-page-3tYuSZu-.js} +1 -1
  108. package/dist/assets/{strategy-dialog-Cwf7Y3P5.js → strategy-dialog-BWm4slo4.js} +1 -1
  109. package/dist/assets/{surface-ubuOfukq.js → surface-BJObZeRw.js} +1 -1
  110. package/dist/assets/{table-U7otr5go.js → table-CTLroJKN.js} +1 -1
  111. package/dist/assets/task-detail-page-ZZOXSpSF.js +1 -0
  112. package/dist/assets/{task-dialog-CI_Fy_FW.js → task-dialog-2wy0xI7f.js} +5 -5
  113. package/dist/assets/timebox-planning-dialog-C6h0Hp71.js +1 -0
  114. package/dist/assets/{today-boxes-CYuxSkZf.js → today-boxes-B_TdgSDz.js} +1 -1
  115. package/dist/assets/today-page-Dipns9tl.js +1 -0
  116. package/dist/assets/training-load-page-B78e1-o-.js +1 -0
  117. package/dist/assets/{ui-B9TWEtCx.js → ui-uo_bNP7c.js} +1 -1
  118. package/dist/assets/{use-anchored-overlay-position-BY4kNzPj.js → use-anchored-overlay-position-BSxFX6Hi.js} +1 -1
  119. package/dist/assets/{use-psyche-focus-target-BhNedCZB.js → use-psyche-focus-target-1GLkhYiv.js} +1 -1
  120. package/dist/assets/{user-badge-ByhC6bMU.js → user-badge-C4eZu1e_.js} +1 -1
  121. package/dist/assets/{user-select-field-fx129Uh6.js → user-select-field-CmKwcbZt.js} +1 -1
  122. package/dist/assets/{utility-widgets-ilORjDmg.js → utility-widgets-B06wQFrd.js} +2 -2
  123. package/dist/assets/{vendor-BwL6m4SE.js → vendor-B7FKSwHK.js} +222 -212
  124. package/dist/assets/{vitals-page-mdEqIm_9.js → vitals-page-BC7QFsm2.js} +1 -1
  125. package/dist/assets/{weekly-review-page-C8W-yg5r.js → weekly-review-page-BjNsNoWj.js} +1 -1
  126. package/dist/assets/{weight-loss-page-BApTknCn.js → weight-loss-page-BOahGY9r.js} +1 -1
  127. package/dist/assets/{wiki-article-markdown-DBccllQg.js → wiki-article-markdown-DO47HnZf.js} +2 -2
  128. package/dist/assets/{wiki-editor-page-BfRfH1O3.js → wiki-editor-page-OWRUHl4O.js} +5 -5
  129. package/dist/assets/{wiki-ingest-history-page-C-6H3MU6.js → wiki-ingest-history-page-CuHpg2lZ.js} +1 -1
  130. package/dist/assets/{wiki-ingest-modal-DnlhByD_.js → wiki-ingest-modal-EZ3kX-vA.js} +1 -1
  131. package/dist/assets/wiki-page-BPV3HIgu.js +1 -0
  132. package/dist/assets/{workbench-flow-page-_-NKIx5R.js → workbench-flow-page-C53KueEz.js} +3 -3
  133. package/dist/assets/{workbench-page-NIAzggwX.js → workbench-page-DJ7nT2JQ.js} +1 -1
  134. package/dist/assets/workout-detail-page-Dhyu0q7o.js +2 -0
  135. package/dist/index.html +8 -8
  136. package/dist/openclaw/api-client.js +28 -2
  137. package/dist/openclaw/local-runtime.js +31 -14
  138. package/dist/openclaw/parity.d.ts +1 -1
  139. package/dist/openclaw/parity.js +10 -0
  140. package/dist/openclaw/routes.js +78 -0
  141. package/dist/openclaw/tools.js +42 -0
  142. package/dist/server/apps/api/migrations/072_artifact_store.sql +106 -0
  143. package/dist/server/{server → apps/api}/src/app.js +365 -4
  144. package/dist/server/{server → apps/api}/src/connectors/box-registry.js +3 -3
  145. package/dist/server/{server → apps/api}/src/db.js +23 -4
  146. package/dist/server/{server → apps/api}/src/health-weight-loss.js +1 -1
  147. package/dist/server/{server → apps/api}/src/openapi.js +570 -1
  148. package/dist/server/{server → apps/api}/src/questionnaire-seeds.js +1 -1
  149. package/dist/server/{server → apps/api}/src/repositories/ai-connectors.js +1 -1
  150. package/dist/server/apps/api/src/repositories/entity-links.js +66 -0
  151. package/dist/server/{server → apps/api}/src/repositories/habits.js +1 -1
  152. package/dist/server/apps/api/src/services/artifacts.js +1059 -0
  153. package/dist/server/{server → apps/api}/src/services/companion-iroh.js +4 -1
  154. package/dist/server/{server → apps/api}/src/services/doctor.js +3 -3
  155. package/dist/server/{server → apps/api}/src/services/entity-crud.js +22 -0
  156. package/dist/server/{server → apps/api}/src/services/gamification-assets.js +1 -1
  157. package/dist/server/{server → apps/api}/src/services/gamification.js +1 -1
  158. package/dist/server/{server → apps/api}/src/services/knowledge-graph.js +48 -3
  159. package/dist/server/{server → apps/api}/src/services/psyche-observation-calendar.js +1 -0
  160. package/dist/server/{server → apps/api}/src/types.js +4 -2
  161. package/dist/server/{server → apps/api}/src/watch-mobile.js +1 -1
  162. package/dist/server/{src → apps/web/src}/lib/api.js +69 -0
  163. package/dist/server/{src → apps/web/src}/lib/entity-visuals.js +10 -1
  164. package/dist/server/{src → apps/web/src}/lib/knowledge-graph-types.js +9 -0
  165. package/openclaw.plugin.json +2 -1
  166. package/package.json +3 -3
  167. package/server/index.js +3 -3
  168. package/server/migrations/072_artifact_store.sql +106 -0
  169. package/skills/forge-openclaw/SKILL.md +60 -7
  170. package/skills/forge-openclaw/entity_conversation_playbooks.md +212 -14
  171. package/skills/forge-openclaw/psyche_entity_playbooks.md +20 -0
  172. package/dist/assets/execution-board-D66C_ikW.js +0 -1
  173. package/dist/assets/goals-page-CQ2lJMzI.js +0 -1
  174. package/dist/assets/habits-page-a7KVPaQp.js +0 -1
  175. package/dist/assets/index-ClJbJhca.css +0 -1
  176. package/dist/assets/index-FpGANF9S.js +0 -2
  177. package/dist/assets/knowledge-graph-page-DxEBaEke.js +0 -1
  178. package/dist/assets/notes-page-DvHMcQey.js +0 -1
  179. package/dist/assets/overview-page-Z5vaUTm3.js +0 -1
  180. package/dist/assets/page-hero-DvrM83_C.js +0 -1
  181. package/dist/assets/pill-cluster-DYI3Ibj8.js +0 -1
  182. package/dist/assets/project-detail-page-B9PqyPu9.js +0 -1
  183. package/dist/assets/project-management-hierarchy-page-DXK14jn0.js +0 -1
  184. package/dist/assets/projects-page-CdAk-ByT.js +0 -1
  185. package/dist/assets/psyche-behaviors-page-CbhhTfU2.js +0 -5
  186. package/dist/assets/psyche-mode-guide-page-CR8e984W.js +0 -1
  187. package/dist/assets/psyche-modes-page-lQdpAcY3.js +0 -1
  188. package/dist/assets/psyche-page-CTdIDkw9.js +0 -1
  189. package/dist/assets/psyche-patterns-page-Drgm-f7I.js +0 -5
  190. package/dist/assets/psyche-questionnaire-run-detail-page-SYndwtF3.js +0 -1
  191. package/dist/assets/psyche-reports-page-Cn0EBndy.js +0 -1
  192. package/dist/assets/psyche-schemas-beliefs-page-Bab4xSWv.js +0 -9
  193. package/dist/assets/rewards-page-FxUXB76B.js +0 -1
  194. package/dist/assets/settings-bin-page-DT8JJero.js +0 -1
  195. package/dist/assets/settings-models-page-CZHG3t7W.js +0 -1
  196. package/dist/assets/task-detail-page-B3SMFZQ3.js +0 -1
  197. package/dist/assets/timebox-planning-dialog-BtFuVoA1.js +0 -1
  198. package/dist/assets/today-page-CHGpLEZ3.js +0 -1
  199. package/dist/assets/training-load-page-GZJF-Yy5.js +0 -1
  200. package/dist/assets/wiki-page-DPZ55e3x.js +0 -1
  201. package/dist/assets/workout-detail-page-3fxr6HL4.js +0 -2
  202. /package/dist/server/{server → apps/api}/migrations/001_core.sql +0 -0
  203. /package/dist/server/{server → apps/api}/migrations/002_psyche.sql +0 -0
  204. /package/dist/server/{server → apps/api}/migrations/003_habits.sql +0 -0
  205. /package/dist/server/{server → apps/api}/migrations/004_habit_links.sql +0 -0
  206. /package/dist/server/{server → apps/api}/migrations/005_habit_psyche_links.sql +0 -0
  207. /package/dist/server/{server → apps/api}/migrations/006_work_adjustments.sql +0 -0
  208. /package/dist/server/{server → apps/api}/migrations/007_weekly_review_closures.sql +0 -0
  209. /package/dist/server/{server → apps/api}/migrations/008_calendar_execution.sql +0 -0
  210. /package/dist/server/{server → apps/api}/migrations/009_true_calendar_events.sql +0 -0
  211. /package/dist/server/{server → apps/api}/migrations/010_calendar_selection_state.sql +0 -0
  212. /package/dist/server/{server → apps/api}/migrations/011_calendar_timezone_backfill.sql +0 -0
  213. /package/dist/server/{server → apps/api}/migrations/012_work_block_ranges.sql +0 -0
  214. /package/dist/server/{server → apps/api}/migrations/013_microsoft_local_auth_settings.sql +0 -0
  215. /package/dist/server/{server → apps/api}/migrations/014_note_tags_and_ephemeral.sql +0 -0
  216. /package/dist/server/{server → apps/api}/migrations/015_multi_user_and_strategies.sql +0 -0
  217. /package/dist/server/{server → apps/api}/migrations/016_health_companion.sql +0 -0
  218. /package/dist/server/{server → apps/api}/migrations/016_strategy_contracts_and_user_graph.sql +0 -0
  219. /package/dist/server/{server → apps/api}/migrations/017_preferences.sql +0 -0
  220. /package/dist/server/{server → apps/api}/migrations/018_preference_catalogs.sql +0 -0
  221. /package/dist/server/{server → apps/api}/migrations/019_wiki_memory.sql +0 -0
  222. /package/dist/server/{server → apps/api}/migrations/020_wiki_page_hierarchy.sql +0 -0
  223. /package/dist/server/{server → apps/api}/migrations/021_hide_evidence_from_wiki_index.sql +0 -0
  224. /package/dist/server/{server → apps/api}/migrations/022_wiki_ingest_background.sql +0 -0
  225. /package/dist/server/{server → apps/api}/migrations/023_diagnostic_logs.sql +0 -0
  226. /package/dist/server/{server → apps/api}/migrations/024_questionnaires.sql +0 -0
  227. /package/dist/server/{server → apps/api}/migrations/025_ai_model_connections.sql +0 -0
  228. /package/dist/server/{server → apps/api}/migrations/026_custom_theme_settings.sql +0 -0
  229. /package/dist/server/{server → apps/api}/migrations/027_ai_processors.sql +0 -0
  230. /package/dist/server/{server → apps/api}/migrations/028_movement_domain.sql +0 -0
  231. /package/dist/server/{server → apps/api}/migrations/029_watch_micro_capture.sql +0 -0
  232. /package/dist/server/{server → apps/api}/migrations/030_surface_layouts.sql +0 -0
  233. /package/dist/server/{server → apps/api}/migrations/031_ai_processor_runtime_upgrades.sql +0 -0
  234. /package/dist/server/{server → apps/api}/migrations/032_ai_connectors.sql +0 -0
  235. /package/dist/server/{server → apps/api}/migrations/033_movement_trip_point_sync.sql +0 -0
  236. /package/dist/server/{server → apps/api}/migrations/034_movement_segment_sync.sql +0 -0
  237. /package/dist/server/{server → apps/api}/migrations/035_google_local_auth_settings.sql +0 -0
  238. /package/dist/server/{server → apps/api}/migrations/036_google_local_auth_client_secret.sql +0 -0
  239. /package/dist/server/{server → apps/api}/migrations/037_workbench_public_inputs_and_run_inputs.sql +0 -0
  240. /package/dist/server/{server → apps/api}/migrations/038_data_management_settings.sql +0 -0
  241. /package/dist/server/{server → apps/api}/migrations/039_life_force_and_action_points.sql +0 -0
  242. /package/dist/server/{server → apps/api}/migrations/040_screen_time_domain.sql +0 -0
  243. /package/dist/server/{server → apps/api}/migrations/041_companion_source_states.sql +0 -0
  244. /package/dist/server/{server → apps/api}/migrations/042_movement_boxes.sql +0 -0
  245. /package/dist/server/{server → apps/api}/migrations/043_movement_box_overlap_overrides.sql +0 -0
  246. /package/dist/server/{server → apps/api}/migrations/044_macos_local_calendar_provider.sql +0 -0
  247. /package/dist/server/{server → apps/api}/migrations/045_sleep_nights_and_raw_segments.sql +0 -0
  248. /package/dist/server/{server → apps/api}/migrations/046_work_item_hierarchy.sql +0 -0
  249. /package/dist/server/{server → apps/api}/migrations/047_sleep_source_records.sql +0 -0
  250. /package/dist/server/{server → apps/api}/migrations/048_task_run_git_context.sql +0 -0
  251. /package/dist/server/{server → apps/api}/migrations/049_agent_runtime_sessions.sql +0 -0
  252. /package/dist/server/{server → apps/api}/migrations/050_agent_token_bootstrap_policy.sql +0 -0
  253. /package/dist/server/{server → apps/api}/migrations/051_agent_token_scope_policy.sql +0 -0
  254. /package/dist/server/{server → apps/api}/migrations/052_agent_identity_tightening.sql +0 -0
  255. /package/dist/server/{server → apps/api}/migrations/053_agent_runtime_session_canonical_labels.sql +0 -0
  256. /package/dist/server/{server → apps/api}/migrations/054_sqlite_backed_wiki_memory.sql +0 -0
  257. /package/dist/server/{server → apps/api}/migrations/055_gamification_progression.sql +0 -0
  258. /package/dist/server/{server → apps/api}/migrations/056_gamification_equipment.sql +0 -0
  259. /package/dist/server/{server → apps/api}/migrations/057_gamification_catalog_cleanup.sql +0 -0
  260. /package/dist/server/{server → apps/api}/migrations/058_gamification_theme_preference.sql +0 -0
  261. /package/dist/server/{server → apps/api}/migrations/059_data_backup_retention.sql +0 -0
  262. /package/dist/server/{server → apps/api}/migrations/060_psyche_devrage_metrics.sql +0 -0
  263. /package/dist/server/{server → apps/api}/migrations/061_health_workout_raw_evidence.sql +0 -0
  264. /package/dist/server/{server → apps/api}/migrations/062_health_mobile_sync_sessions.sql +0 -0
  265. /package/dist/server/{server → apps/api}/migrations/063_psyche_flashcards.sql +0 -0
  266. /package/dist/server/{server → apps/api}/migrations/064_health_workout_time_series_identity.sql +0 -0
  267. /package/dist/server/{server → apps/api}/migrations/065_weight_loss_nutrition_insights.sql +0 -0
  268. /package/dist/server/{server → apps/api}/migrations/066_watch_action_receipts.sql +0 -0
  269. /package/dist/server/{server → apps/api}/migrations/067_weight_loss_daily_active_overrides.sql +0 -0
  270. /package/dist/server/{server → apps/api}/migrations/068_remove_generated_movement_wiki_logs.sql +0 -0
  271. /package/dist/server/{server → apps/api}/migrations/069_psyche_devrage_cumulative_rage.sql +0 -0
  272. /package/dist/server/{server → apps/api}/migrations/070_health_mobile_sync_completion_index.sql +0 -0
  273. /package/dist/server/{server → apps/api}/migrations/071_agent_runtime_claude_identity.sql +0 -0
  274. /package/dist/server/{server → apps/api}/src/data-management-types.js +0 -0
  275. /package/dist/server/{server → apps/api}/src/debug.js +0 -0
  276. /package/dist/server/{server → apps/api}/src/demo-data.js +0 -0
  277. /package/dist/server/{server → apps/api}/src/discovery-advertiser.js +0 -0
  278. /package/dist/server/{server → apps/api}/src/e2e-server.js +0 -0
  279. /package/dist/server/{server → apps/api}/src/errors.js +0 -0
  280. /package/dist/server/{server → apps/api}/src/health-workout-adapters.js +0 -0
  281. /package/dist/server/{server → apps/api}/src/health-workout-analytics.js +0 -0
  282. /package/dist/server/{server → apps/api}/src/health.js +0 -0
  283. /package/dist/server/{server → apps/api}/src/index.js +0 -0
  284. /package/dist/server/{server → apps/api}/src/managers/base.js +0 -0
  285. /package/dist/server/{server → apps/api}/src/managers/contracts.js +0 -0
  286. /package/dist/server/{server → apps/api}/src/managers/platform/api-gateway-manager.js +0 -0
  287. /package/dist/server/{server → apps/api}/src/managers/platform/audit-manager.js +0 -0
  288. /package/dist/server/{server → apps/api}/src/managers/platform/authentication-manager.js +0 -0
  289. /package/dist/server/{server → apps/api}/src/managers/platform/authorization-manager.js +0 -0
  290. /package/dist/server/{server → apps/api}/src/managers/platform/background-job-manager.js +0 -0
  291. /package/dist/server/{server → apps/api}/src/managers/platform/configuration-manager.js +0 -0
  292. /package/dist/server/{server → apps/api}/src/managers/platform/database-manager.js +0 -0
  293. /package/dist/server/{server → apps/api}/src/managers/platform/event-bus-manager.js +0 -0
  294. /package/dist/server/{server → apps/api}/src/managers/platform/external-service-manager.js +0 -0
  295. /package/dist/server/{server → apps/api}/src/managers/platform/health-manager.js +0 -0
  296. /package/dist/server/{server → apps/api}/src/managers/platform/llm-manager.js +0 -0
  297. /package/dist/server/{server → apps/api}/src/managers/platform/migration-manager.js +0 -0
  298. /package/dist/server/{server → apps/api}/src/managers/platform/mock-workbench-provider.js +0 -0
  299. /package/dist/server/{server → apps/api}/src/managers/platform/openai-responses-provider.js +0 -0
  300. /package/dist/server/{server → apps/api}/src/managers/platform/search-index-manager.js +0 -0
  301. /package/dist/server/{server → apps/api}/src/managers/platform/secrets-manager.js +0 -0
  302. /package/dist/server/{server → apps/api}/src/managers/platform/session-manager.js +0 -0
  303. /package/dist/server/{server → apps/api}/src/managers/platform/storage-manager.js +0 -0
  304. /package/dist/server/{server → apps/api}/src/managers/platform/token-manager.js +0 -0
  305. /package/dist/server/{server → apps/api}/src/managers/platform/transaction-manager.js +0 -0
  306. /package/dist/server/{server → apps/api}/src/managers/platform/trusted-network.js +0 -0
  307. /package/dist/server/{server → apps/api}/src/managers/runtime.js +0 -0
  308. /package/dist/server/{server → apps/api}/src/managers/type-guards.js +0 -0
  309. /package/dist/server/{server → apps/api}/src/movement.js +0 -0
  310. /package/dist/server/{server → apps/api}/src/preferences-seeds.js +0 -0
  311. /package/dist/server/{server → apps/api}/src/preferences-types.js +0 -0
  312. /package/dist/server/{server → apps/api}/src/psyche-types.js +0 -0
  313. /package/dist/server/{server → apps/api}/src/questionnaire-flow.js +0 -0
  314. /package/dist/server/{server → apps/api}/src/questionnaire-types.js +0 -0
  315. /package/dist/server/{server → apps/api}/src/repositories/activity-events.js +0 -0
  316. /package/dist/server/{server → apps/api}/src/repositories/agent-runtime-sessions.js +0 -0
  317. /package/dist/server/{server → apps/api}/src/repositories/ai-processors.js +0 -0
  318. /package/dist/server/{server → apps/api}/src/repositories/calendar.js +0 -0
  319. /package/dist/server/{server → apps/api}/src/repositories/collaboration.js +0 -0
  320. /package/dist/server/{server → apps/api}/src/repositories/deleted-entities.js +0 -0
  321. /package/dist/server/{server → apps/api}/src/repositories/diagnostic-logs.js +0 -0
  322. /package/dist/server/{server → apps/api}/src/repositories/domains.js +0 -0
  323. /package/dist/server/{server → apps/api}/src/repositories/entity-ownership.js +0 -0
  324. /package/dist/server/{server → apps/api}/src/repositories/event-log.js +0 -0
  325. /package/dist/server/{server → apps/api}/src/repositories/gamification.js +0 -0
  326. /package/dist/server/{server → apps/api}/src/repositories/goals.js +0 -0
  327. /package/dist/server/{server → apps/api}/src/repositories/model-settings.js +0 -0
  328. /package/dist/server/{server → apps/api}/src/repositories/notes.js +0 -0
  329. /package/dist/server/{server → apps/api}/src/repositories/preferences.js +0 -0
  330. /package/dist/server/{server → apps/api}/src/repositories/projects.js +0 -0
  331. /package/dist/server/{server → apps/api}/src/repositories/psyche.js +0 -0
  332. /package/dist/server/{server → apps/api}/src/repositories/questionnaires.js +0 -0
  333. /package/dist/server/{server → apps/api}/src/repositories/rewards.js +0 -0
  334. /package/dist/server/{server → apps/api}/src/repositories/settings.js +0 -0
  335. /package/dist/server/{server → apps/api}/src/repositories/strategies.js +0 -0
  336. /package/dist/server/{server → apps/api}/src/repositories/surface-layouts.js +0 -0
  337. /package/dist/server/{server → apps/api}/src/repositories/tags.js +0 -0
  338. /package/dist/server/{server → apps/api}/src/repositories/task-runs.js +0 -0
  339. /package/dist/server/{server → apps/api}/src/repositories/tasks.js +0 -0
  340. /package/dist/server/{server → apps/api}/src/repositories/users.js +0 -0
  341. /package/dist/server/{server → apps/api}/src/repositories/weekly-reviews.js +0 -0
  342. /package/dist/server/{server → apps/api}/src/repositories/wiki-memory.js +0 -0
  343. /package/dist/server/{server → apps/api}/src/repositories/work-adjustments.js +0 -0
  344. /package/dist/server/{server → apps/api}/src/runtime-data-root.js +0 -0
  345. /package/dist/server/{server → apps/api}/src/screen-time.js +0 -0
  346. /package/dist/server/{server → apps/api}/src/seed-demo.js +0 -0
  347. /package/dist/server/{server → apps/api}/src/services/calendar-runtime.js +0 -0
  348. /package/dist/server/{server → apps/api}/src/services/context.js +0 -0
  349. /package/dist/server/{server → apps/api}/src/services/dashboard.js +0 -0
  350. /package/dist/server/{server → apps/api}/src/services/data-management.js +0 -0
  351. /package/dist/server/{server → apps/api}/src/services/devrage-scanner.js +0 -0
  352. /package/dist/server/{server → apps/api}/src/services/devrage.js +0 -0
  353. /package/dist/server/{server → apps/api}/src/services/git-helper.js +0 -0
  354. /package/dist/server/{server → apps/api}/src/services/google-calendar-oauth-config.js +0 -0
  355. /package/dist/server/{server → apps/api}/src/services/insights.js +0 -0
  356. /package/dist/server/{server → apps/api}/src/services/legacy-wiki-markdown-import.js +0 -0
  357. /package/dist/server/{server → apps/api}/src/services/life-force-model.js +0 -0
  358. /package/dist/server/{server → apps/api}/src/services/life-force.js +0 -0
  359. /package/dist/server/{server → apps/api}/src/services/macos-calendar-helper.js +0 -0
  360. /package/dist/server/{server → apps/api}/src/services/openai-codex-oauth.js +0 -0
  361. /package/dist/server/{server → apps/api}/src/services/projects.js +0 -0
  362. /package/dist/server/{server → apps/api}/src/services/psyche.js +0 -0
  363. /package/dist/server/{server → apps/api}/src/services/relations.js +0 -0
  364. /package/dist/server/{server → apps/api}/src/services/reviews.js +0 -0
  365. /package/dist/server/{server → apps/api}/src/services/run-recovery.js +0 -0
  366. /package/dist/server/{server → apps/api}/src/services/tagging.js +0 -0
  367. /package/dist/server/{server → apps/api}/src/services/task-run-watchdog.js +0 -0
  368. /package/dist/server/{server → apps/api}/src/services/work-time.js +0 -0
  369. /package/dist/server/{server → apps/api}/src/web.js +0 -0
  370. /package/dist/server/{src → apps/web/src}/components/customization/utility-widgets.js +0 -0
  371. /package/dist/server/{src → apps/web/src}/components/ui/info-tooltip.js +0 -0
  372. /package/dist/server/{src → apps/web/src}/components/workbench-boxes/calendar/calendar-boxes.js +0 -0
  373. /package/dist/server/{src → apps/web/src}/components/workbench-boxes/goals/goals-boxes.js +0 -0
  374. /package/dist/server/{src → apps/web/src}/components/workbench-boxes/habits/habits-boxes.js +0 -0
  375. /package/dist/server/{src → apps/web/src}/components/workbench-boxes/health/health-boxes.js +0 -0
  376. /package/dist/server/{src → apps/web/src}/components/workbench-boxes/insights/insights-boxes.js +0 -0
  377. /package/dist/server/{src → apps/web/src}/components/workbench-boxes/kanban/kanban-boxes.js +0 -0
  378. /package/dist/server/{src → apps/web/src}/components/workbench-boxes/movement/movement-boxes.js +0 -0
  379. /package/dist/server/{src → apps/web/src}/components/workbench-boxes/notes/notes-boxes.js +0 -0
  380. /package/dist/server/{src → apps/web/src}/components/workbench-boxes/overview/overview-boxes.js +0 -0
  381. /package/dist/server/{src → apps/web/src}/components/workbench-boxes/preferences/preferences-boxes.js +0 -0
  382. /package/dist/server/{src → apps/web/src}/components/workbench-boxes/projects/projects-boxes.js +0 -0
  383. /package/dist/server/{src → apps/web/src}/components/workbench-boxes/psyche/psyche-boxes.js +0 -0
  384. /package/dist/server/{src → apps/web/src}/components/workbench-boxes/questionnaires/questionnaires-boxes.js +0 -0
  385. /package/dist/server/{src → apps/web/src}/components/workbench-boxes/review/review-boxes.js +0 -0
  386. /package/dist/server/{src → apps/web/src}/components/workbench-boxes/shared/define-workbench-box.js +0 -0
  387. /package/dist/server/{src → apps/web/src}/components/workbench-boxes/shared/generic-node-view.js +0 -0
  388. /package/dist/server/{src → apps/web/src}/components/workbench-boxes/strategies/strategies-boxes.js +0 -0
  389. /package/dist/server/{src → apps/web/src}/components/workbench-boxes/tasks/tasks-boxes.js +0 -0
  390. /package/dist/server/{src → apps/web/src}/components/workbench-boxes/today/today-boxes.js +0 -0
  391. /package/dist/server/{src → apps/web/src}/components/workbench-boxes/wiki/wiki-boxes.js +0 -0
  392. /package/dist/server/{src → apps/web/src}/lib/api-error.js +0 -0
  393. /package/dist/server/{src → apps/web/src}/lib/calendar-name-deduper.js +0 -0
  394. /package/dist/server/{src → apps/web/src}/lib/data-management-types.js +0 -0
  395. /package/dist/server/{src → apps/web/src}/lib/date-keys.js +0 -0
  396. /package/dist/server/{src → apps/web/src}/lib/diagnostics.js +0 -0
  397. /package/dist/server/{src → apps/web/src}/lib/gamification-catalog.js +0 -0
  398. /package/dist/server/{src → apps/web/src}/lib/knowledge-graph.js +0 -0
  399. /package/dist/server/{src → apps/web/src}/lib/psyche-schemas.js +0 -0
  400. /package/dist/server/{src → apps/web/src}/lib/psyche-types.js +0 -0
  401. /package/dist/server/{src → apps/web/src}/lib/questionnaire-types.js +0 -0
  402. /package/dist/server/{src → apps/web/src}/lib/runtime-paths.js +0 -0
  403. /package/dist/server/{src → apps/web/src}/lib/schemas.js +0 -0
  404. /package/dist/server/{src → apps/web/src}/lib/snapshot-normalizer.js +0 -0
  405. /package/dist/server/{src → apps/web/src}/lib/theme-system.js +0 -0
  406. /package/dist/server/{src → apps/web/src}/lib/types.js +0 -0
  407. /package/dist/server/{src → apps/web/src}/lib/utils.js +0 -0
  408. /package/dist/server/{src → apps/web/src}/lib/weight-loss-energy-model.js +0 -0
  409. /package/dist/server/{src → apps/web/src}/lib/weight-loss-types.js +0 -0
  410. /package/dist/server/{src → apps/web/src}/lib/workbench/boxes.js +0 -0
  411. /package/dist/server/{src → apps/web/src}/lib/workbench/contracts.js +0 -0
  412. /package/dist/server/{src → apps/web/src}/lib/workbench/nodes.js +0 -0
  413. /package/dist/server/{src → apps/web/src}/lib/workbench/registry.js +0 -0
  414. /package/dist/server/{src → apps/web/src}/lib/workbench/runtime.js +0 -0
  415. /package/dist/server/{src → apps/web/src}/lib/workbench/tool-catalog.js +0 -0
@@ -0,0 +1,1059 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { existsSync } from "node:fs";
3
+ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
4
+ import path from "node:path";
5
+ import AdmZip from "adm-zip";
6
+ import { z } from "zod";
7
+ import { getDatabase, resolveDataDir, runInTransaction } from "../db.js";
8
+ import { filterDeletedEntities, isEntityDeleted } from "../repositories/deleted-entities.js";
9
+ import { listEntityLinksForSources, replaceEntityLinksForSource } from "../repositories/entity-links.js";
10
+ import { recordEventLog } from "../repositories/event-log.js";
11
+ import { listWikiLlmProfiles } from "../repositories/wiki-memory.js";
12
+ const MAX_ARTIFACT_BYTES = 100 * 1024 * 1024;
13
+ const MAX_TEXT_EXTRACTION_CHARS = 80_000;
14
+ const MAX_LLM_CONTEXT_CHARS = 24_000;
15
+ const MAX_ZIP_ENTRY_COUNT = 5000;
16
+ const MAX_ZIP_UNCOMPRESSED_BYTES = 250 * 1024 * 1024;
17
+ const MAX_ZIP_RATIO = 100;
18
+ const ALLOWED_EXTENSIONS = [
19
+ "xlsx",
20
+ "xlsm",
21
+ "docx",
22
+ "pptx",
23
+ "pdf",
24
+ "csv",
25
+ "tsv",
26
+ "txt",
27
+ "md",
28
+ "json",
29
+ "yaml",
30
+ "yml",
31
+ "png",
32
+ "jpg",
33
+ "jpeg",
34
+ "webp"
35
+ ];
36
+ const extensionToFormatFamily = {
37
+ xlsx: "spreadsheet",
38
+ xlsm: "spreadsheet",
39
+ csv: "spreadsheet",
40
+ tsv: "spreadsheet",
41
+ docx: "document",
42
+ pptx: "presentation",
43
+ pdf: "pdf",
44
+ txt: "text",
45
+ md: "text",
46
+ json: "structured_text",
47
+ yaml: "structured_text",
48
+ yml: "structured_text",
49
+ png: "image",
50
+ jpg: "image",
51
+ jpeg: "image",
52
+ webp: "image"
53
+ };
54
+ export const artifactStateSchema = z.enum([
55
+ "active",
56
+ "quarantined",
57
+ "blocked",
58
+ "archived",
59
+ "metadata_only"
60
+ ]);
61
+ export const artifactDangerLevelSchema = z.enum([
62
+ "low",
63
+ "moderate",
64
+ "high",
65
+ "blocked"
66
+ ]);
67
+ export const artifactDownloadPolicySchema = z.enum([
68
+ "human_only",
69
+ "disabled"
70
+ ]);
71
+ export const artifactFormatFamilySchema = z.enum([
72
+ "spreadsheet",
73
+ "document",
74
+ "presentation",
75
+ "pdf",
76
+ "text",
77
+ "structured_text",
78
+ "image"
79
+ ]);
80
+ export const artifactSourceKindSchema = z.enum([
81
+ "upload",
82
+ "agent_upload",
83
+ "wiki_ingest",
84
+ "external_reference",
85
+ "manual"
86
+ ]);
87
+ const trimmedString = z.string().trim();
88
+ const optionalTrimmedString = trimmedString.optional().default("");
89
+ const nullableId = trimmedString.nullable().optional().default(null);
90
+ export const entityLinkInputSchema = z.object({
91
+ entityType: z.string().trim().min(1),
92
+ entityId: z.string().trim().min(1),
93
+ anchorKey: z.string().trim().optional().default(""),
94
+ relationship: z.string().trim().optional().default("related")
95
+ });
96
+ export const artifactUploadSchema = z.object({
97
+ title: trimmedString.optional(),
98
+ shortDescription: optionalTrimmedString,
99
+ description: optionalTrimmedString,
100
+ originalFileName: trimmedString.min(1),
101
+ declaredMimeType: optionalTrimmedString,
102
+ contentBase64: z.string().min(1),
103
+ sourceKind: artifactSourceKindSchema.optional(),
104
+ sourceLabel: optionalTrimmedString,
105
+ uploadedByUserId: nullableId,
106
+ uploadedByAgentId: nullableId,
107
+ actingForUserId: nullableId,
108
+ downloadPolicy: artifactDownloadPolicySchema.optional().default("human_only"),
109
+ links: z.array(entityLinkInputSchema).optional().default([]),
110
+ metadata: z.record(z.string(), z.unknown()).optional().default({}),
111
+ useLlmEnrichment: z.boolean().optional().default(false),
112
+ llmProfileId: z.string().trim().optional()
113
+ });
114
+ export const artifactMetadataCreateSchema = z.object({
115
+ title: trimmedString.min(1),
116
+ shortDescription: optionalTrimmedString,
117
+ description: optionalTrimmedString,
118
+ originalFileName: trimmedString.optional().default("metadata-only"),
119
+ sourceKind: artifactSourceKindSchema.optional().default("manual"),
120
+ sourceLabel: optionalTrimmedString,
121
+ uploadedByUserId: nullableId,
122
+ uploadedByAgentId: nullableId,
123
+ actingForUserId: nullableId,
124
+ links: z.array(entityLinkInputSchema).optional().default([]),
125
+ metadata: z.record(z.string(), z.unknown()).optional().default({})
126
+ });
127
+ export const artifactMetadataPatchSchema = z.object({
128
+ title: trimmedString.min(1).optional(),
129
+ shortDescription: trimmedString.optional(),
130
+ description: trimmedString.optional(),
131
+ sourceLabel: trimmedString.optional(),
132
+ artifactState: artifactStateSchema.optional(),
133
+ downloadPolicy: artifactDownloadPolicySchema.optional(),
134
+ links: z.array(entityLinkInputSchema).optional(),
135
+ metadata: z.record(z.string(), z.unknown()).optional()
136
+ });
137
+ export const artifactListQuerySchema = z.object({
138
+ query: trimmedString.optional(),
139
+ artifactState: artifactStateSchema.optional(),
140
+ dangerLevel: artifactDangerLevelSchema.optional(),
141
+ formatFamily: artifactFormatFamilySchema.optional(),
142
+ linkedEntityType: z.string().trim().optional(),
143
+ linkedEntityId: z.string().trim().optional(),
144
+ limit: z.coerce.number().int().min(1).max(500).optional().default(100)
145
+ });
146
+ export const artifactTrustPatchSchema = z.object({
147
+ artifactState: artifactStateSchema,
148
+ reason: trimmedString.min(1),
149
+ downloadPolicy: artifactDownloadPolicySchema.optional()
150
+ });
151
+ export const artifactEnrichmentRequestSchema = z.object({
152
+ llmProfileId: z.string().trim().optional(),
153
+ fillMissingOnly: z.boolean().optional().default(true),
154
+ explicitApiKey: z.string().trim().optional()
155
+ });
156
+ function nowIso() {
157
+ return new Date().toISOString();
158
+ }
159
+ function artifactId() {
160
+ return `artifact_${randomUUID().replaceAll("-", "").slice(0, 16)}`;
161
+ }
162
+ function artifactVersionId() {
163
+ return `artifact_version_${randomUUID().replaceAll("-", "").slice(0, 14)}`;
164
+ }
165
+ function auditId() {
166
+ return `artifact_audit_${randomUUID().replaceAll("-", "").slice(0, 14)}`;
167
+ }
168
+ function parseJsonObject(raw) {
169
+ try {
170
+ const parsed = JSON.parse(raw);
171
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed)
172
+ ? parsed
173
+ : {};
174
+ }
175
+ catch {
176
+ return {};
177
+ }
178
+ }
179
+ function normalizeNullableText(value) {
180
+ const text = value?.trim();
181
+ return text && text.length > 0 ? text : null;
182
+ }
183
+ function sanitizeFileName(fileName) {
184
+ return path.basename(fileName).replace(/[^\w.\- ()[\]]+/g, "_").slice(0, 180);
185
+ }
186
+ function extensionFromFileName(fileName) {
187
+ return path.extname(sanitizeFileName(fileName)).replace(/^\./, "").toLowerCase();
188
+ }
189
+ function formatFamilyForExtension(extension) {
190
+ return extensionToFormatFamily[extension] ?? null;
191
+ }
192
+ function sha256(buffer) {
193
+ return createHash("sha256").update(buffer).digest("hex");
194
+ }
195
+ function detectMimeType(buffer, extension) {
196
+ if (buffer.subarray(0, 4).toString("utf8") === "%PDF") {
197
+ return "application/pdf";
198
+ }
199
+ if (buffer.subarray(0, 4).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47]))) {
200
+ return "image/png";
201
+ }
202
+ if (buffer.subarray(0, 3).equals(Buffer.from([0xff, 0xd8, 0xff]))) {
203
+ return "image/jpeg";
204
+ }
205
+ if (buffer.subarray(0, 4).toString("ascii") === "RIFF" &&
206
+ buffer.subarray(8, 12).toString("ascii") === "WEBP") {
207
+ return "image/webp";
208
+ }
209
+ if (buffer.subarray(0, 2).toString("ascii") === "PK") {
210
+ if (extension === "docx") {
211
+ return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
212
+ }
213
+ if (extension === "pptx") {
214
+ return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
215
+ }
216
+ if (extension === "xlsx") {
217
+ return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
218
+ }
219
+ if (extension === "xlsm") {
220
+ return "application/vnd.ms-excel.sheet.macroEnabled.12";
221
+ }
222
+ return "application/zip";
223
+ }
224
+ if (extension === "json") {
225
+ return "application/json";
226
+ }
227
+ if (extension === "csv") {
228
+ return "text/csv";
229
+ }
230
+ if (extension === "tsv") {
231
+ return "text/tab-separated-values";
232
+ }
233
+ if (extension === "md") {
234
+ return "text/markdown";
235
+ }
236
+ if (extension === "yaml" || extension === "yml") {
237
+ return "application/yaml";
238
+ }
239
+ return "text/plain";
240
+ }
241
+ function blobRoot() {
242
+ return path.join(resolveDataDir(), "artifacts", "blobs");
243
+ }
244
+ function storageKeyForHash(hash) {
245
+ return `sha256/${hash.slice(0, 2)}/${hash.slice(2, 4)}/${hash}.bin`;
246
+ }
247
+ function resolveStoragePath(storageKey) {
248
+ const root = path.resolve(blobRoot());
249
+ const resolved = path.resolve(root, storageKey);
250
+ if (!resolved.startsWith(`${root}${path.sep}`)) {
251
+ throw new Error("Artifact storage key resolved outside the artifact root.");
252
+ }
253
+ return resolved;
254
+ }
255
+ async function ensureBlobStored(buffer, detectedMimeType) {
256
+ const contentSha256 = sha256(buffer);
257
+ const storageKey = storageKeyForHash(contentSha256);
258
+ const storagePath = resolveStoragePath(storageKey);
259
+ const createdAt = nowIso();
260
+ const existing = getDatabase()
261
+ .prepare("SELECT content_sha256 FROM artifact_blobs WHERE content_sha256 = ?")
262
+ .get(contentSha256);
263
+ if (!existing && !existsSync(storagePath)) {
264
+ await mkdir(path.dirname(storagePath), { recursive: true });
265
+ const tmpPath = `${storagePath}.${process.pid}.${Date.now()}.tmp`;
266
+ try {
267
+ await writeFile(tmpPath, buffer, { flag: "wx" });
268
+ await rename(tmpPath, storagePath);
269
+ }
270
+ catch (error) {
271
+ await rm(tmpPath, { force: true }).catch(() => undefined);
272
+ if (!existsSync(storagePath)) {
273
+ throw error;
274
+ }
275
+ }
276
+ }
277
+ getDatabase()
278
+ .prepare(`INSERT OR IGNORE INTO artifact_blobs (
279
+ content_sha256, storage_key, byte_size, detected_mime_type, created_at
280
+ ) VALUES (?, ?, ?, ?, ?)`)
281
+ .run(contentSha256, storageKey, buffer.byteLength, detectedMimeType, createdAt);
282
+ return {
283
+ contentSha256,
284
+ storageKey,
285
+ storagePath
286
+ };
287
+ }
288
+ function addFinding(findings, severity, code, message) {
289
+ findings.push({ severity, code, message });
290
+ }
291
+ function severityScore(severity) {
292
+ switch (severity) {
293
+ case "blocked":
294
+ return 100;
295
+ case "high":
296
+ return 75;
297
+ case "moderate":
298
+ return 45;
299
+ case "low":
300
+ return 15;
301
+ default:
302
+ return 0;
303
+ }
304
+ }
305
+ function computeDanger(findings) {
306
+ const score = Math.min(100, findings.reduce((max, finding) => Math.max(max, severityScore(finding.severity)), 0) +
307
+ Math.max(0, findings.length - 1) * 4);
308
+ const level = score >= 90 ? "blocked" : score >= 70 ? "high" : score >= 35 ? "moderate" : "low";
309
+ return { score, level };
310
+ }
311
+ function safeUtf8(buffer, limit = MAX_TEXT_EXTRACTION_CHARS) {
312
+ return buffer.subarray(0, limit).toString("utf8").replace(/\u0000/g, "").trim();
313
+ }
314
+ function stripXml(xml) {
315
+ return xml
316
+ .replace(/<[^>]+>/g, " ")
317
+ .replace(/&amp;/g, "&")
318
+ .replace(/&lt;/g, "<")
319
+ .replace(/&gt;/g, ">")
320
+ .replace(/\s+/g, " ")
321
+ .trim();
322
+ }
323
+ function zipEntryText(zip, name) {
324
+ const entry = zip.getEntries().find((candidate) => candidate.entryName === name);
325
+ return entry ? entry.getData().toString("utf8") : "";
326
+ }
327
+ function extractOfficeText(zip, extension) {
328
+ if (extension === "docx") {
329
+ return stripXml(zipEntryText(zip, "word/document.xml")).slice(0, MAX_TEXT_EXTRACTION_CHARS);
330
+ }
331
+ if (extension === "pptx") {
332
+ return zip
333
+ .getEntries()
334
+ .filter((entry) => /^ppt\/slides\/slide\d+\.xml$/.test(entry.entryName))
335
+ .map((entry) => stripXml(entry.getData().toString("utf8")))
336
+ .join("\n")
337
+ .slice(0, MAX_TEXT_EXTRACTION_CHARS);
338
+ }
339
+ if (extension === "xlsx" || extension === "xlsm") {
340
+ const sharedStrings = stripXml(zipEntryText(zip, "xl/sharedStrings.xml"));
341
+ const sheetText = zip
342
+ .getEntries()
343
+ .filter((entry) => /^xl\/worksheets\/sheet\d+\.xml$/.test(entry.entryName))
344
+ .slice(0, 5)
345
+ .map((entry) => stripXml(entry.getData().toString("utf8")))
346
+ .join("\n");
347
+ return [sharedStrings, sheetText].filter(Boolean).join("\n").slice(0, MAX_TEXT_EXTRACTION_CHARS);
348
+ }
349
+ return "";
350
+ }
351
+ function scanOfficeZip(buffer, extension, findings) {
352
+ let extractedTextSample = "";
353
+ try {
354
+ const zip = new AdmZip(buffer);
355
+ const entries = zip.getEntries();
356
+ const totalUncompressed = entries.reduce((sum, entry) => sum + Math.max(0, entry.header?.size ?? 0), 0);
357
+ const totalCompressed = entries.reduce((sum, entry) => sum + Math.max(1, entry.header?.compressedSize ?? 1), 0);
358
+ const ratio = totalUncompressed / Math.max(1, totalCompressed);
359
+ if (entries.length > MAX_ZIP_ENTRY_COUNT) {
360
+ addFinding(findings, "blocked", "zip_entry_limit", "The archive has too many entries for safe static inspection.");
361
+ }
362
+ if (totalUncompressed > MAX_ZIP_UNCOMPRESSED_BYTES || ratio > MAX_ZIP_RATIO) {
363
+ addFinding(findings, "blocked", "zip_bomb_indicator", "The archive has unsafe compressed-to-uncompressed size characteristics.");
364
+ }
365
+ if (entries.some((entry) => entry.entryName.endsWith("EncryptedPackage"))) {
366
+ addFinding(findings, "high", "office_encrypted_package", "The Office document appears encrypted and cannot be inspected fully.");
367
+ }
368
+ if (entries.some((entry) => /vbaProject\.bin$/i.test(entry.entryName))) {
369
+ addFinding(findings, extension === "xlsm" ? "high" : "blocked", "office_macro_project", "The Office document contains a VBA macro project.");
370
+ }
371
+ if (entries.some((entry) => /oleObject|embeddings\//i.test(entry.entryName))) {
372
+ addFinding(findings, "high", "office_embedded_object", "The Office document contains embedded objects or OLE payloads.");
373
+ }
374
+ const relationshipText = entries
375
+ .filter((entry) => entry.entryName.endsWith(".rels"))
376
+ .map((entry) => entry.getData().toString("utf8"))
377
+ .join("\n");
378
+ if (/TargetMode\s*=\s*["']External["']/i.test(relationshipText)) {
379
+ addFinding(findings, "moderate", "office_external_relationship", "The Office document references external resources.");
380
+ }
381
+ if (extension === "xlsx" || extension === "xlsm") {
382
+ const workbookXml = zipEntryText(zip, "xl/workbook.xml");
383
+ if (/state\s*=\s*["'](?:hidden|veryHidden)["']/i.test(workbookXml)) {
384
+ addFinding(findings, "moderate", "spreadsheet_hidden_sheet", "The workbook contains hidden sheets.");
385
+ }
386
+ if (entries.some((entry) => /^xl\/externalLinks\//.test(entry.entryName))) {
387
+ addFinding(findings, "moderate", "spreadsheet_external_link", "The workbook contains external workbook links.");
388
+ }
389
+ if (entries
390
+ .filter((entry) => /^xl\/worksheets\/sheet\d+\.xml$/.test(entry.entryName))
391
+ .some((entry) => /<f(?:\s|>)/i.test(entry.getData().toString("utf8")))) {
392
+ addFinding(findings, "moderate", "spreadsheet_formulas", "The workbook contains formulas. Forge records this but does not evaluate them.");
393
+ }
394
+ }
395
+ extractedTextSample = extractOfficeText(zip, extension);
396
+ }
397
+ catch {
398
+ addFinding(findings, "high", "zip_parse_error", "The Office archive could not be parsed safely.");
399
+ }
400
+ return extractedTextSample;
401
+ }
402
+ function scanPdf(buffer, findings) {
403
+ const text = buffer.subarray(0, Math.min(buffer.byteLength, 2_000_000)).toString("latin1");
404
+ if (/\/JavaScript|\/JS\b/i.test(text)) {
405
+ addFinding(findings, "high", "pdf_javascript", "The PDF contains JavaScript actions.");
406
+ }
407
+ if (/\/OpenAction|\/AA\b/i.test(text)) {
408
+ addFinding(findings, "moderate", "pdf_auto_action", "The PDF contains automatic document actions.");
409
+ }
410
+ if (/\/EmbeddedFile|\/Filespec/i.test(text)) {
411
+ addFinding(findings, "high", "pdf_embedded_file", "The PDF contains embedded file references.");
412
+ }
413
+ return text
414
+ .replace(/[^\x09\x0a\x0d\x20-\x7e]+/g, " ")
415
+ .replace(/\s+/g, " ")
416
+ .trim()
417
+ .slice(0, MAX_TEXT_EXTRACTION_CHARS);
418
+ }
419
+ function scanDelimitedText(text, findings) {
420
+ if (/^[=+\-@]/m.test(text) || /[,;\t][=+\-@]/.test(text)) {
421
+ addFinding(findings, "moderate", "spreadsheet_formula_like_text", "The delimited text contains formula-like cells. Forge does not evaluate them.");
422
+ }
423
+ }
424
+ function scanStructuredText(extension, text, findings) {
425
+ if (extension === "json") {
426
+ try {
427
+ JSON.parse(text);
428
+ }
429
+ catch {
430
+ addFinding(findings, "low", "json_parse_error", "The JSON file did not parse as valid JSON.");
431
+ }
432
+ }
433
+ }
434
+ export function scanArtifactBytes(input) {
435
+ const detectedExtension = extensionFromFileName(input.originalFileName);
436
+ const detectedMimeType = detectMimeType(input.buffer, detectedExtension);
437
+ const formatFamily = formatFamilyForExtension(detectedExtension);
438
+ const findings = [];
439
+ if (!formatFamily || !ALLOWED_EXTENSIONS.includes(detectedExtension)) {
440
+ addFinding(findings, "blocked", "unsupported_extension", `Files with extension .${detectedExtension || "unknown"} are not allowed.`);
441
+ }
442
+ if (input.buffer.byteLength > MAX_ARTIFACT_BYTES) {
443
+ addFinding(findings, "blocked", "size_limit_exceeded", "The file exceeds Forge's artifact size limit.");
444
+ }
445
+ if (input.declaredMimeType?.trim() && input.declaredMimeType !== detectedMimeType) {
446
+ addFinding(findings, "low", "mime_mismatch", "The declared MIME type differs from static file detection.");
447
+ }
448
+ let extractedTextSample = "";
449
+ if (formatFamily === "document" || formatFamily === "presentation" || formatFamily === "spreadsheet") {
450
+ if (["docx", "pptx", "xlsx", "xlsm"].includes(detectedExtension)) {
451
+ extractedTextSample = scanOfficeZip(input.buffer, detectedExtension, findings);
452
+ }
453
+ else {
454
+ extractedTextSample = safeUtf8(input.buffer);
455
+ scanDelimitedText(extractedTextSample, findings);
456
+ }
457
+ }
458
+ else if (formatFamily === "pdf") {
459
+ extractedTextSample = scanPdf(input.buffer, findings);
460
+ }
461
+ else if (formatFamily === "text" || formatFamily === "structured_text") {
462
+ extractedTextSample = safeUtf8(input.buffer);
463
+ scanDelimitedText(extractedTextSample, findings);
464
+ scanStructuredText(detectedExtension, extractedTextSample, findings);
465
+ }
466
+ if (findings.length === 0) {
467
+ addFinding(findings, "info", "static_scan_clean", "Static inspection found no configured danger signal.");
468
+ }
469
+ const danger = computeDanger(findings);
470
+ const artifactState = danger.level === "blocked" ? "blocked" : danger.level === "high" ? "quarantined" : "active";
471
+ return {
472
+ detectedExtension,
473
+ detectedMimeType,
474
+ formatFamily: formatFamily ?? "text",
475
+ dangerScore: danger.score,
476
+ dangerLevel: danger.level,
477
+ artifactState,
478
+ scanResults: {
479
+ scannedAt: nowIso(),
480
+ scannerVersion: "artifact-static-scan-v1",
481
+ declaredExtension: detectedExtension,
482
+ detectedMimeType,
483
+ extensionAllowed: Boolean(formatFamily),
484
+ byteSize: input.buffer.byteLength,
485
+ findings,
486
+ extractedTextSample: extractedTextSample.slice(0, MAX_LLM_CONTEXT_CHARS),
487
+ extractedTextTruncated: extractedTextSample.length > MAX_LLM_CONTEXT_CHARS
488
+ }
489
+ };
490
+ }
491
+ function mapLink(row) {
492
+ return {
493
+ sourceEntityType: row.sourceEntityType,
494
+ sourceEntityId: row.sourceEntityId,
495
+ targetEntityType: row.targetEntityType,
496
+ targetEntityId: row.targetEntityId,
497
+ anchorKey: row.anchorKey,
498
+ relationship: row.relationship,
499
+ createdByActor: row.createdByActor,
500
+ createdAt: row.createdAt
501
+ };
502
+ }
503
+ function mapArtifact(row, links = []) {
504
+ return {
505
+ id: row.id,
506
+ title: row.title,
507
+ shortDescription: row.short_description,
508
+ description: row.description,
509
+ originalFileName: row.original_file_name,
510
+ storageKey: row.storage_key,
511
+ storagePath: row.storage_path,
512
+ contentSha256: row.content_sha256,
513
+ byteSize: row.byte_size,
514
+ detectedExtension: row.detected_extension,
515
+ declaredMimeType: row.declared_mime_type,
516
+ detectedMimeType: row.detected_mime_type,
517
+ formatFamily: row.format_family,
518
+ sourceKind: row.source_kind,
519
+ sourceLabel: row.source_label,
520
+ uploadedByUserId: row.uploaded_by_user_id,
521
+ uploadedByAgentId: row.uploaded_by_agent_id,
522
+ actingForUserId: row.acting_for_user_id,
523
+ artifactState: row.artifact_state,
524
+ dangerScore: row.danger_score,
525
+ dangerLevel: row.danger_level,
526
+ downloadPolicy: row.download_policy,
527
+ scanResults: parseJsonObject(row.scan_results_json),
528
+ enrichmentResults: parseJsonObject(row.enrichment_results_json),
529
+ metadata: parseJsonObject(row.metadata_json),
530
+ links,
531
+ createdAt: row.created_at,
532
+ updatedAt: row.updated_at
533
+ };
534
+ }
535
+ function getArtifactRow(id) {
536
+ return getDatabase()
537
+ .prepare(`SELECT id, title, short_description, description, original_file_name,
538
+ storage_key, storage_path, content_sha256, byte_size,
539
+ detected_extension, declared_mime_type, detected_mime_type,
540
+ format_family, source_kind, source_label, uploaded_by_user_id,
541
+ uploaded_by_agent_id, acting_for_user_id, artifact_state,
542
+ danger_score, danger_level, download_policy, scan_results_json,
543
+ enrichment_results_json, metadata_json, created_at, updated_at
544
+ FROM artifacts
545
+ WHERE id = ?`)
546
+ .get(id);
547
+ }
548
+ function listArtifactRows() {
549
+ return getDatabase()
550
+ .prepare(`SELECT id, title, short_description, description, original_file_name,
551
+ storage_key, storage_path, content_sha256, byte_size,
552
+ detected_extension, declared_mime_type, detected_mime_type,
553
+ format_family, source_kind, source_label, uploaded_by_user_id,
554
+ uploaded_by_agent_id, acting_for_user_id, artifact_state,
555
+ danger_score, danger_level, download_policy, scan_results_json,
556
+ enrichment_results_json, metadata_json, created_at, updated_at
557
+ FROM artifacts
558
+ ORDER BY updated_at DESC`)
559
+ .all();
560
+ }
561
+ function toEventMetadata(metadata) {
562
+ const normalized = {};
563
+ for (const [key, value] of Object.entries(metadata)) {
564
+ if (typeof value === "string" ||
565
+ typeof value === "number" ||
566
+ typeof value === "boolean" ||
567
+ value === null) {
568
+ normalized[key] = value;
569
+ continue;
570
+ }
571
+ normalized[key] = JSON.stringify(value);
572
+ }
573
+ return normalized;
574
+ }
575
+ function recordArtifactAudit(artifactId, eventType, context, metadata = {}) {
576
+ const createdAt = nowIso();
577
+ getDatabase()
578
+ .prepare(`INSERT INTO artifact_audit_events (
579
+ id, artifact_id, event_type, actor, source, metadata_json, created_at
580
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)`)
581
+ .run(auditId(), artifactId, eventType, context.actor ?? context.token?.agentLabel ?? null, context.source, JSON.stringify(metadata), createdAt);
582
+ recordEventLog({
583
+ eventKind: eventType,
584
+ entityType: "artifact",
585
+ entityId: artifactId,
586
+ actor: context.actor ?? context.token?.agentLabel ?? null,
587
+ source: context.source,
588
+ metadata: toEventMetadata(metadata)
589
+ });
590
+ }
591
+ function replaceEntityLinksForArtifact(artifactId, links, context) {
592
+ replaceEntityLinksForSource({
593
+ sourceEntityType: "artifact",
594
+ sourceEntityId: artifactId,
595
+ links,
596
+ actor: context.actor ?? context.token?.agentLabel ?? null
597
+ });
598
+ }
599
+ function insertArtifactVersion(input) {
600
+ const row = getDatabase()
601
+ .prepare(`SELECT COALESCE(MAX(version_number), 0) + 1 AS nextVersion
602
+ FROM artifact_versions
603
+ WHERE artifact_id = ?`)
604
+ .get(input.artifactId);
605
+ getDatabase()
606
+ .prepare(`INSERT INTO artifact_versions (
607
+ id, artifact_id, version_number, content_sha256, storage_key, byte_size,
608
+ original_file_name, scan_results_json, enrichment_results_json,
609
+ created_by_actor, created_at
610
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
611
+ .run(artifactVersionId(), input.artifactId, row.nextVersion, input.contentSha256, input.storageKey, input.byteSize, input.originalFileName, JSON.stringify(input.scanResults), JSON.stringify(input.enrichmentResults), input.context.actor ?? input.context.token?.agentLabel ?? null, nowIso());
612
+ }
613
+ function deriveFallbackTitle(originalFileName) {
614
+ const sanitized = sanitizeFileName(originalFileName);
615
+ return sanitized.replace(/\.[^.]+$/, "").replace(/[_-]+/g, " ").trim() || "Artifact";
616
+ }
617
+ export async function createArtifactFromUpload(input, context, services = {}) {
618
+ const parsed = artifactUploadSchema.parse(input);
619
+ const buffer = Buffer.from(parsed.contentBase64, "base64");
620
+ if (buffer.byteLength === 0) {
621
+ throw new Error("Artifact upload content is empty or invalid base64.");
622
+ }
623
+ const scan = scanArtifactBytes({
624
+ buffer,
625
+ originalFileName: parsed.originalFileName,
626
+ declaredMimeType: parsed.declaredMimeType
627
+ });
628
+ const blob = await ensureBlobStored(buffer, scan.detectedMimeType);
629
+ const id = artifactId();
630
+ const createdAt = nowIso();
631
+ const sourceKind = parsed.sourceKind ?? (context.source === "agent" ? "agent_upload" : "upload");
632
+ const uploadedByAgentId = parsed.uploadedByAgentId ?? context.token?.agentId ?? null;
633
+ const metadata = {
634
+ ...parsed.metadata,
635
+ safeHandling: "Forge stores and serves this file for human download only; it must not be executed by agents."
636
+ };
637
+ const initialEnrichment = {
638
+ generated: false,
639
+ status: parsed.useLlmEnrichment ? "pending" : "not_requested"
640
+ };
641
+ runInTransaction(() => {
642
+ getDatabase()
643
+ .prepare(`INSERT INTO artifacts (
644
+ id, title, short_description, description, original_file_name,
645
+ storage_key, storage_path, content_sha256, byte_size, detected_extension,
646
+ declared_mime_type, detected_mime_type, format_family, source_kind,
647
+ source_label, uploaded_by_user_id, uploaded_by_agent_id, acting_for_user_id,
648
+ artifact_state, danger_score, danger_level, download_policy,
649
+ scan_results_json, enrichment_results_json, metadata_json, created_at, updated_at
650
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
651
+ .run(id, parsed.title?.trim() || deriveFallbackTitle(parsed.originalFileName), parsed.shortDescription, parsed.description, sanitizeFileName(parsed.originalFileName), blob.storageKey, blob.storagePath, blob.contentSha256, buffer.byteLength, scan.detectedExtension, parsed.declaredMimeType, scan.detectedMimeType, scan.formatFamily, sourceKind, parsed.sourceLabel, parsed.uploadedByUserId, uploadedByAgentId, parsed.actingForUserId, scan.artifactState, scan.dangerScore, scan.dangerLevel, parsed.downloadPolicy, JSON.stringify(scan.scanResults), JSON.stringify(initialEnrichment), JSON.stringify(metadata), createdAt, createdAt);
652
+ replaceEntityLinksForArtifact(id, parsed.links, context);
653
+ insertArtifactVersion({
654
+ artifactId: id,
655
+ contentSha256: blob.contentSha256,
656
+ storageKey: blob.storageKey,
657
+ byteSize: buffer.byteLength,
658
+ originalFileName: sanitizeFileName(parsed.originalFileName),
659
+ scanResults: scan.scanResults,
660
+ enrichmentResults: initialEnrichment,
661
+ context
662
+ });
663
+ recordArtifactAudit(id, "artifact.created", context, {
664
+ contentSha256: blob.contentSha256,
665
+ dangerScore: scan.dangerScore,
666
+ dangerLevel: scan.dangerLevel,
667
+ sourceKind
668
+ });
669
+ });
670
+ if (parsed.useLlmEnrichment) {
671
+ await enrichArtifactWithLlm(id, {
672
+ llmProfileId: parsed.llmProfileId,
673
+ fillMissingOnly: true
674
+ }, context, services).catch((error) => {
675
+ runInTransaction(() => {
676
+ updateArtifactEnrichment(id, {
677
+ generated: false,
678
+ status: "failed",
679
+ error: error instanceof Error ? error.message : String(error),
680
+ generatedAt: nowIso()
681
+ });
682
+ recordArtifactAudit(id, "artifact.enrichment_failed", context, {
683
+ error: error instanceof Error ? error.message : String(error)
684
+ });
685
+ });
686
+ });
687
+ }
688
+ return getArtifactById(id);
689
+ }
690
+ export function createArtifactMetadata() {
691
+ throw new Error("Use POST /api/v1/artifacts for artifact creation. Batch CRUD may search, link, update metadata, delete, and restore artifact records, but it must not create file artifacts.");
692
+ }
693
+ export function listArtifacts(input = {}) {
694
+ const parsed = artifactListQuerySchema.parse(input);
695
+ const rows = filterDeletedEntities("artifact", listArtifactRows());
696
+ const linksByArtifactId = new Map();
697
+ for (const linkRow of listEntityLinksForSources("artifact", rows.map((row) => row.id))) {
698
+ const current = linksByArtifactId.get(linkRow.sourceEntityId) ?? [];
699
+ current.push(mapLink(linkRow));
700
+ linksByArtifactId.set(linkRow.sourceEntityId, current);
701
+ }
702
+ const query = parsed.query?.toLowerCase() ?? "";
703
+ return rows
704
+ .map((row) => mapArtifact(row, linksByArtifactId.get(row.id) ?? []))
705
+ .filter((artifact) => parsed.artifactState ? artifact.artifactState === parsed.artifactState : true)
706
+ .filter((artifact) => parsed.dangerLevel ? artifact.dangerLevel === parsed.dangerLevel : true)
707
+ .filter((artifact) => parsed.formatFamily ? artifact.formatFamily === parsed.formatFamily : true)
708
+ .filter((artifact) => query
709
+ ? JSON.stringify({
710
+ title: artifact.title,
711
+ shortDescription: artifact.shortDescription,
712
+ description: artifact.description,
713
+ originalFileName: artifact.originalFileName,
714
+ sourceLabel: artifact.sourceLabel,
715
+ metadata: artifact.metadata
716
+ })
717
+ .toLowerCase()
718
+ .includes(query)
719
+ : true)
720
+ .filter((artifact) => parsed.linkedEntityType && parsed.linkedEntityId
721
+ ? artifact.links.some((link) => link.targetEntityType === parsed.linkedEntityType &&
722
+ link.targetEntityId === parsed.linkedEntityId)
723
+ : true)
724
+ .slice(0, parsed.limit);
725
+ }
726
+ export function getArtifactById(id) {
727
+ if (isEntityDeleted("artifact", id)) {
728
+ return undefined;
729
+ }
730
+ const row = getArtifactRow(id);
731
+ if (!row) {
732
+ return undefined;
733
+ }
734
+ return mapArtifact(row, listEntityLinksForSources("artifact", [id]).map(mapLink));
735
+ }
736
+ export function updateArtifactMetadata(id, input, context) {
737
+ const existing = getArtifactById(id);
738
+ if (!existing) {
739
+ return undefined;
740
+ }
741
+ const parsed = artifactMetadataPatchSchema.parse(input);
742
+ const updatedAt = nowIso();
743
+ const nextMetadata = parsed.metadata
744
+ ? { ...existing.metadata, ...parsed.metadata }
745
+ : existing.metadata;
746
+ const nextLinks = parsed.links ?? existing.links.map((link) => ({
747
+ entityType: link.targetEntityType,
748
+ entityId: link.targetEntityId,
749
+ anchorKey: link.anchorKey ?? "",
750
+ relationship: link.relationship
751
+ }));
752
+ runInTransaction(() => {
753
+ getDatabase()
754
+ .prepare(`UPDATE artifacts
755
+ SET title = ?, short_description = ?, description = ?, source_label = ?,
756
+ artifact_state = ?, download_policy = ?, metadata_json = ?, updated_at = ?
757
+ WHERE id = ?`)
758
+ .run(parsed.title ?? existing.title, parsed.shortDescription ?? existing.shortDescription, parsed.description ?? existing.description, parsed.sourceLabel ?? existing.sourceLabel, parsed.artifactState ?? existing.artifactState, parsed.downloadPolicy ?? existing.downloadPolicy, JSON.stringify(nextMetadata), updatedAt, id);
759
+ if (parsed.links) {
760
+ replaceEntityLinksForArtifact(id, nextLinks, context);
761
+ }
762
+ recordArtifactAudit(id, "artifact.metadata_updated", context, {
763
+ fields: Object.keys(parsed)
764
+ });
765
+ });
766
+ return getArtifactById(id);
767
+ }
768
+ export function deleteArtifactMetadata(id, context) {
769
+ const existing = getArtifactById(id);
770
+ if (!existing) {
771
+ return undefined;
772
+ }
773
+ runInTransaction(() => {
774
+ replaceEntityLinksForSource({
775
+ sourceEntityType: "artifact",
776
+ sourceEntityId: id,
777
+ links: [],
778
+ actor: context.actor ?? context.token?.agentLabel ?? null
779
+ });
780
+ getDatabase().prepare("DELETE FROM artifacts WHERE id = ?").run(id);
781
+ recordEventLog({
782
+ eventKind: "artifact.metadata_deleted",
783
+ entityType: "artifact",
784
+ entityId: id,
785
+ actor: context.actor ?? context.token?.agentLabel ?? null,
786
+ source: context.source,
787
+ metadata: toEventMetadata({
788
+ contentSha256: existing.contentSha256,
789
+ storageKey: existing.storageKey,
790
+ blobPreserved: true,
791
+ entityLinksRemoved: true
792
+ })
793
+ });
794
+ });
795
+ return existing;
796
+ }
797
+ export async function readArtifactDownload(id) {
798
+ const artifact = getArtifactById(id);
799
+ if (!artifact) {
800
+ return null;
801
+ }
802
+ if (artifact.downloadPolicy !== "human_only" || artifact.artifactState === "blocked") {
803
+ throw new Error("This artifact is not downloadable in its current state.");
804
+ }
805
+ const storagePath = resolveStoragePath(artifact.storageKey);
806
+ return {
807
+ artifact,
808
+ bytes: await readFile(storagePath)
809
+ };
810
+ }
811
+ export async function rescanArtifact(id, context) {
812
+ const artifact = getArtifactById(id);
813
+ if (!artifact) {
814
+ return undefined;
815
+ }
816
+ const storagePath = resolveStoragePath(artifact.storageKey);
817
+ if (!existsSync(storagePath)) {
818
+ runInTransaction(() => {
819
+ recordArtifactAudit(id, "artifact.scan_failed", context, {
820
+ reason: "blob_missing",
821
+ storageKey: artifact.storageKey
822
+ });
823
+ });
824
+ throw new Error("Artifact blob is missing from local storage.");
825
+ }
826
+ const buffer = await readFile(storagePath);
827
+ const scan = scanArtifactBytes({
828
+ buffer,
829
+ originalFileName: artifact.originalFileName,
830
+ declaredMimeType: artifact.declaredMimeType
831
+ });
832
+ const updatedAt = nowIso();
833
+ runInTransaction(() => {
834
+ getDatabase()
835
+ .prepare(`UPDATE artifacts
836
+ SET detected_extension = ?, detected_mime_type = ?, format_family = ?,
837
+ artifact_state = ?, danger_score = ?, danger_level = ?,
838
+ scan_results_json = ?, updated_at = ?
839
+ WHERE id = ?`)
840
+ .run(scan.detectedExtension, scan.detectedMimeType, scan.formatFamily, scan.artifactState, scan.dangerScore, scan.dangerLevel, JSON.stringify(scan.scanResults), updatedAt, id);
841
+ recordArtifactAudit(id, "artifact.scanned", context, {
842
+ dangerScore: scan.dangerScore,
843
+ dangerLevel: scan.dangerLevel
844
+ });
845
+ });
846
+ return getArtifactById(id);
847
+ }
848
+ function updateArtifactEnrichment(id, enrichment) {
849
+ getDatabase()
850
+ .prepare(`UPDATE artifacts
851
+ SET enrichment_results_json = ?, updated_at = ?
852
+ WHERE id = ?`)
853
+ .run(JSON.stringify(enrichment), nowIso(), id);
854
+ }
855
+ function extractJsonObject(text) {
856
+ const trimmed = text.trim();
857
+ const start = trimmed.indexOf("{");
858
+ const end = trimmed.lastIndexOf("}");
859
+ if (start < 0 || end <= start) {
860
+ return {};
861
+ }
862
+ try {
863
+ const parsed = JSON.parse(trimmed.slice(start, end + 1));
864
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed)
865
+ ? parsed
866
+ : {};
867
+ }
868
+ catch {
869
+ return {};
870
+ }
871
+ }
872
+ function compactArtifactForPrompt(artifact) {
873
+ const scan = artifact.scanResults;
874
+ return {
875
+ title: artifact.title,
876
+ shortDescription: artifact.shortDescription,
877
+ description: artifact.description,
878
+ originalFileName: artifact.originalFileName,
879
+ detectedExtension: artifact.detectedExtension,
880
+ declaredMimeType: artifact.declaredMimeType,
881
+ detectedMimeType: artifact.detectedMimeType,
882
+ formatFamily: artifact.formatFamily,
883
+ byteSize: artifact.byteSize,
884
+ deterministicDangerScore: artifact.dangerScore,
885
+ deterministicDangerLevel: artifact.dangerLevel,
886
+ findings: Array.isArray(scan.findings) ? scan.findings : [],
887
+ extractedTextSample: typeof scan.extractedTextSample === "string"
888
+ ? scan.extractedTextSample.slice(0, MAX_LLM_CONTEXT_CHARS)
889
+ : ""
890
+ };
891
+ }
892
+ export async function enrichArtifactWithLlm(id, input, context, services = {}) {
893
+ const artifact = getArtifactById(id);
894
+ if (!artifact) {
895
+ return undefined;
896
+ }
897
+ const parsed = artifactEnrichmentRequestSchema.parse(input);
898
+ const profile = parsed.llmProfileId
899
+ ? listWikiLlmProfiles().find((entry) => entry.id === parsed.llmProfileId)
900
+ : listWikiLlmProfiles().find((entry) => entry.enabled);
901
+ if (!services.llm || !profile) {
902
+ const enrichment = {
903
+ generated: false,
904
+ status: "skipped",
905
+ reason: "No enabled LLM profile is connected.",
906
+ generatedAt: nowIso()
907
+ };
908
+ runInTransaction(() => {
909
+ updateArtifactEnrichment(id, enrichment);
910
+ recordArtifactAudit(id, "artifact.enrichment_skipped", context, {
911
+ reason: "no_llm_profile"
912
+ });
913
+ });
914
+ return getArtifactById(id);
915
+ }
916
+ const prompt = [
917
+ "You are enriching metadata for a Forge artifact store.",
918
+ "Do not infer executable behavior and do not lower deterministic safety findings.",
919
+ "Return only JSON with keys: title, shortDescription, description, documentType, keywords, suggestedForgeLinks, safetySummary, dangerReasons, dangerScoreAdjustment.",
920
+ JSON.stringify(compactArtifactForPrompt(artifact), null, 2)
921
+ ].join("\n\n");
922
+ const result = await services.llm.runTextPrompt(profile, {
923
+ explicitApiKey: parsed.explicitApiKey,
924
+ systemPrompt: "You summarize stored files from static, non-executed text only. You never say a file is safe if deterministic scanning found risk.",
925
+ prompt
926
+ }, (log) => {
927
+ recordArtifactAudit(id, "artifact.enrichment_log", context, {
928
+ level: log.level,
929
+ message: log.message
930
+ });
931
+ });
932
+ const generated = extractJsonObject(result.outputText);
933
+ const proposedScore = typeof generated.dangerScoreAdjustment === "number"
934
+ ? generated.dangerScoreAdjustment
935
+ : artifact.dangerScore;
936
+ const nextDangerScore = Math.max(artifact.dangerScore, Math.min(100, proposedScore));
937
+ const enrichment = {
938
+ generated: true,
939
+ status: "completed",
940
+ provider: profile.provider,
941
+ model: profile.model,
942
+ generatedAt: nowIso(),
943
+ output: {
944
+ ...generated,
945
+ dangerScore: nextDangerScore,
946
+ deterministicDangerScorePreserved: artifact.dangerScore
947
+ }
948
+ };
949
+ const title = !parsed.fillMissingOnly || !artifact.title.trim()
950
+ ? typeof generated.title === "string" && generated.title.trim()
951
+ ? generated.title.trim()
952
+ : artifact.title
953
+ : artifact.title;
954
+ const shortDescription = !parsed.fillMissingOnly || !artifact.shortDescription.trim()
955
+ ? typeof generated.shortDescription === "string"
956
+ ? generated.shortDescription.trim()
957
+ : artifact.shortDescription
958
+ : artifact.shortDescription;
959
+ const description = !parsed.fillMissingOnly || !artifact.description.trim()
960
+ ? typeof generated.description === "string"
961
+ ? generated.description.trim()
962
+ : artifact.description
963
+ : artifact.description;
964
+ runInTransaction(() => {
965
+ getDatabase()
966
+ .prepare(`UPDATE artifacts
967
+ SET title = ?, short_description = ?, description = ?,
968
+ danger_score = MAX(danger_score, ?), enrichment_results_json = ?,
969
+ updated_at = ?
970
+ WHERE id = ?`)
971
+ .run(title, shortDescription, description, nextDangerScore, JSON.stringify(enrichment), nowIso(), id);
972
+ recordArtifactAudit(id, "artifact.enriched_with_llm", context, {
973
+ provider: profile.provider,
974
+ model: profile.model,
975
+ dangerScore: nextDangerScore
976
+ });
977
+ });
978
+ return getArtifactById(id);
979
+ }
980
+ export function replaceArtifactEntityLinks(id, links, context) {
981
+ const artifact = getArtifactById(id);
982
+ if (!artifact) {
983
+ return undefined;
984
+ }
985
+ runInTransaction(() => {
986
+ replaceEntityLinksForArtifact(id, links, context);
987
+ getDatabase()
988
+ .prepare("UPDATE artifacts SET updated_at = ? WHERE id = ?")
989
+ .run(nowIso(), id);
990
+ recordArtifactAudit(id, "artifact.links_updated", context, {
991
+ linkCount: links.length
992
+ });
993
+ });
994
+ return getArtifactById(id);
995
+ }
996
+ export function patchArtifactTrust(id, input, context) {
997
+ const artifact = getArtifactById(id);
998
+ if (!artifact) {
999
+ return undefined;
1000
+ }
1001
+ const parsed = artifactTrustPatchSchema.parse(input);
1002
+ getDatabase()
1003
+ .prepare(`UPDATE artifacts
1004
+ SET artifact_state = ?, download_policy = ?, updated_at = ?
1005
+ WHERE id = ?`)
1006
+ .run(parsed.artifactState, parsed.downloadPolicy ?? artifact.downloadPolicy, nowIso(), id);
1007
+ recordArtifactAudit(id, "artifact.trust_state_updated", context, {
1008
+ from: artifact.artifactState,
1009
+ to: parsed.artifactState,
1010
+ reason: parsed.reason
1011
+ });
1012
+ return getArtifactById(id);
1013
+ }
1014
+ export function listArtifactVersions(id) {
1015
+ return getDatabase()
1016
+ .prepare(`SELECT id, artifact_id, version_number, content_sha256, storage_key,
1017
+ byte_size, original_file_name, scan_results_json,
1018
+ enrichment_results_json, created_by_actor, created_at
1019
+ FROM artifact_versions
1020
+ WHERE artifact_id = ?
1021
+ ORDER BY version_number DESC`)
1022
+ .all(id)
1023
+ .map((row) => {
1024
+ const version = row;
1025
+ return {
1026
+ id: version.id,
1027
+ artifactId: version.artifact_id,
1028
+ versionNumber: version.version_number,
1029
+ contentSha256: version.content_sha256,
1030
+ storageKey: version.storage_key,
1031
+ byteSize: version.byte_size,
1032
+ originalFileName: version.original_file_name,
1033
+ scanResults: parseJsonObject(version.scan_results_json),
1034
+ enrichmentResults: parseJsonObject(version.enrichment_results_json),
1035
+ createdByActor: version.created_by_actor,
1036
+ createdAt: version.created_at
1037
+ };
1038
+ });
1039
+ }
1040
+ export function listArtifactAuditEvents(id) {
1041
+ return getDatabase()
1042
+ .prepare(`SELECT id, artifact_id, event_type, actor, source, metadata_json, created_at
1043
+ FROM artifact_audit_events
1044
+ WHERE artifact_id = ?
1045
+ ORDER BY created_at DESC`)
1046
+ .all(id)
1047
+ .map((row) => {
1048
+ const event = row;
1049
+ return {
1050
+ id: event.id,
1051
+ artifactId: event.artifact_id,
1052
+ eventType: event.event_type,
1053
+ actor: event.actor,
1054
+ source: event.source,
1055
+ metadata: parseJsonObject(event.metadata_json),
1056
+ createdAt: event.created_at
1057
+ };
1058
+ });
1059
+ }