@runfusion/fusion 0.25.0 → 0.27.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 (226) hide show
  1. package/README.md +6 -0
  2. package/dist/bin.js +28004 -16888
  3. package/dist/client/assets/AgentDetailView-B7QRcHJH.css +1 -0
  4. package/dist/client/assets/AgentDetailView-DwLmRXTY.js +18 -0
  5. package/dist/client/assets/{AgentsView-B3jYk8Kt.js → AgentsView-D-N6aA0P.js} +12 -7
  6. package/dist/client/assets/ChatView-DnCdKu8Z.js +1 -0
  7. package/dist/client/assets/{DevServerView-DyGDEiBP.js → DevServerView-BiA1nYtt.js} +1 -1
  8. package/dist/client/assets/{DirectoryPicker-D5UIeIl6.js → DirectoryPicker-DvBviDG6.js} +1 -1
  9. package/dist/client/assets/{DocumentsView-DNHu1T8K.js → DocumentsView-BWXOxpuq.js} +1 -1
  10. package/dist/client/assets/{EvalsView-CpRobtDi.js → EvalsView-CJFbtL7i.js} +1 -1
  11. package/dist/client/assets/{ExperimentalAgentOnboardingModal-DOY_oZi7.js → ExperimentalAgentOnboardingModal-DuGIPd0B.js} +1 -1
  12. package/dist/client/assets/InsightsView-BBpRiolN.js +11 -0
  13. package/dist/client/assets/{MemoryView-PSc5lGJt.js → MemoryView-48LuNkKk.js} +2 -2
  14. package/dist/client/assets/NodesView-CGQWSNZM.js +14 -0
  15. package/dist/client/assets/{PiExtensionsManager-DL_QcN56.js → PiExtensionsManager-i-7UL2oh.js} +2 -2
  16. package/dist/client/assets/PluginManager-DoSAykD6.js +1 -0
  17. package/dist/client/assets/{ResearchView-BzCcDAS4.css → ResearchView-BEI4ZSGs.css} +1 -1
  18. package/dist/client/assets/ResearchView-XZuRtOxE.js +1 -0
  19. package/dist/client/assets/SettingsModal-Ci0_sqbU.css +1 -0
  20. package/dist/client/assets/{SettingsModal-CUCyaAyE.js → SettingsModal-CmeF8CN4.js} +1 -1
  21. package/dist/client/assets/SettingsModal-DBcjf9Bu.js +31 -0
  22. package/dist/client/assets/SettingsModal-DWKgRxBA.css +1 -0
  23. package/dist/client/assets/{SetupWizardModal-BKscasuh.js → SetupWizardModal-CgtvpMX9.js} +1 -1
  24. package/dist/client/assets/{SkillsView-BdELqTy7.js → SkillsView-DErYRumF.js} +1 -1
  25. package/dist/client/assets/StashRecoveryView-B_8WIQEo.css +1 -0
  26. package/dist/client/assets/StashRecoveryView-QJrNS4Vg.js +1 -0
  27. package/dist/client/assets/{TodoView-DFNGBDNV.js → TodoView-BD9NRwq0.js} +2 -2
  28. package/dist/client/assets/createLucideIcon-BazL2hk5.js +21 -0
  29. package/dist/client/assets/dashboard-view-BWGH_fAq.js +63 -0
  30. package/dist/client/assets/dashboard-view-BoTzlP8b.css +1 -0
  31. package/dist/client/assets/dashboard-view-DdGlfuu-.css +1 -0
  32. package/dist/client/assets/dashboard-view-Ws9_ZnKu.js +21 -0
  33. package/dist/client/assets/{folder-open-k1xmUMyr.js → folder-open-CHSlllzf.js} +1 -1
  34. package/dist/client/assets/index-DCovGm5b.css +1 -0
  35. package/dist/client/assets/index-bEwSVl7B.js +692 -0
  36. package/dist/client/assets/{star-ne32r3Y4.js → star-BgVwWAPz.js} +1 -1
  37. package/dist/client/assets/{upload-MS-2Gx53.js → upload-CAzycxr9.js} +1 -1
  38. package/dist/client/assets/{users-C519GSjH.js → users-CZnxCCCJ.js} +1 -1
  39. package/dist/client/index.html +2 -2
  40. package/dist/client/version.json +1 -1
  41. package/dist/droid-cli/package.json +1 -1
  42. package/dist/droid-cli/src/__tests__/index.test.ts +228 -0
  43. package/dist/extension.js +15810 -10205
  44. package/dist/pi-claude-cli/package.json +1 -1
  45. package/dist/pi-claude-cli/src/__tests__/provider.test.ts +36 -22
  46. package/dist/pi-claude-cli/src/provider.ts +7 -1
  47. package/dist/plugins/fusion-plugin-cli-printing-press/manifest.json +24 -0
  48. package/dist/plugins/fusion-plugin-cli-printing-press/package.json +44 -0
  49. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/TestRunnerPanel.test.tsx +99 -0
  50. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/config-flow.test.ts +91 -0
  51. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/dashboard-view.test.tsx +40 -0
  52. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/dashboard-views.test.ts +46 -0
  53. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/draft-store.test.ts +50 -0
  54. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/fixtures/exec-mock.ts +80 -0
  55. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/fixtures/fixtures.test.ts +40 -0
  56. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/fixtures/registry.ts +82 -0
  57. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/generator.test.ts +54 -0
  58. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/manage-view.test.tsx +98 -0
  59. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/manifest.test.ts +36 -0
  60. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/registration.test.ts +29 -0
  61. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/run-routes.test.ts +98 -0
  62. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/runner.test.ts +55 -0
  63. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/runtime-availability.test.ts +61 -0
  64. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/validation.test.ts +30 -0
  65. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/wizard-routes.test.ts +61 -0
  66. package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/workflow-integration.test.ts +19 -0
  67. package/dist/plugins/fusion-plugin-cli-printing-press/src/dashboard-view.css +43 -0
  68. package/dist/plugins/fusion-plugin-cli-printing-press/src/dashboard-view.tsx +49 -0
  69. package/dist/plugins/fusion-plugin-cli-printing-press/src/generation/generator.ts +95 -0
  70. package/dist/plugins/fusion-plugin-cli-printing-press/src/generation/redact.ts +9 -0
  71. package/dist/plugins/fusion-plugin-cli-printing-press/src/generation/runner.ts +79 -0
  72. package/dist/plugins/fusion-plugin-cli-printing-press/src/generation/types.ts +31 -0
  73. package/dist/plugins/fusion-plugin-cli-printing-press/src/index.ts +58 -0
  74. package/dist/plugins/fusion-plugin-cli-printing-press/src/manage/EditDraftModal.tsx +75 -0
  75. package/dist/plugins/fusion-plugin-cli-printing-press/src/manage/useDrafts.ts +73 -0
  76. package/dist/plugins/fusion-plugin-cli-printing-press/src/manage-view.css +79 -0
  77. package/dist/plugins/fusion-plugin-cli-printing-press/src/manage-view.tsx +122 -0
  78. package/dist/plugins/fusion-plugin-cli-printing-press/src/routes/wizard-routes.ts +272 -0
  79. package/dist/plugins/fusion-plugin-cli-printing-press/src/run/TestRunnerPanel.css +70 -0
  80. package/dist/plugins/fusion-plugin-cli-printing-press/src/run/TestRunnerPanel.tsx +98 -0
  81. package/dist/plugins/fusion-plugin-cli-printing-press/src/run/useRunGeneratedCli.ts +37 -0
  82. package/dist/plugins/fusion-plugin-cli-printing-press/src/runtime/__tests__/executor-runtime-env.test.ts +191 -0
  83. package/dist/plugins/fusion-plugin-cli-printing-press/src/runtime/executor-runtime-env.ts +75 -0
  84. package/dist/plugins/fusion-plugin-cli-printing-press/src/storage/draft-store.ts +85 -0
  85. package/dist/plugins/fusion-plugin-cli-printing-press/src/store/__tests__/cli-press-store.test.ts +128 -0
  86. package/dist/plugins/fusion-plugin-cli-printing-press/src/store/__tests__/credentials.test.ts +62 -0
  87. package/dist/plugins/fusion-plugin-cli-printing-press/src/store/cli-press-store.ts +427 -0
  88. package/dist/plugins/fusion-plugin-cli-printing-press/src/store/cli-press-types.ts +110 -0
  89. package/dist/plugins/fusion-plugin-cli-printing-press/src/store/credentials.ts +95 -0
  90. package/dist/plugins/fusion-plugin-cli-printing-press/src/wizard/steps.tsx +55 -0
  91. package/dist/plugins/fusion-plugin-cli-printing-press/src/wizard/types.ts +33 -0
  92. package/dist/plugins/fusion-plugin-cli-printing-press/src/wizard/validation.ts +63 -0
  93. package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
  94. package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
  95. package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
  96. package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
  97. package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
  98. package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
  99. package/dist/plugins/fusion-plugin-reports/manifest.json +10 -0
  100. package/dist/plugins/fusion-plugin-reports/package.json +18 -2
  101. package/dist/plugins/fusion-plugin-reports/src/__tests__/approval.test.ts +164 -0
  102. package/dist/plugins/fusion-plugin-reports/src/__tests__/manifest.test.ts +14 -0
  103. package/dist/plugins/fusion-plugin-reports/src/__tests__/routes-approval.test.ts +109 -0
  104. package/dist/plugins/fusion-plugin-reports/src/__tests__/scaffold.test.ts +60 -0
  105. package/dist/plugins/fusion-plugin-reports/src/__tests__/share-blocks.test.ts +83 -0
  106. package/dist/plugins/fusion-plugin-reports/src/aggregation.ts +23 -0
  107. package/dist/plugins/fusion-plugin-reports/src/approval.ts +97 -0
  108. package/dist/plugins/fusion-plugin-reports/src/cadence.ts +23 -0
  109. package/dist/plugins/fusion-plugin-reports/src/dashboard/ReportsView.css +82 -0
  110. package/dist/plugins/fusion-plugin-reports/src/dashboard/ReportsView.tsx +24 -0
  111. package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/ReportComparisonDrawer.test.tsx +12 -0
  112. package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/ReportDetailPanel.test.tsx +12 -0
  113. package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/ReportFiltersBar.test.tsx +14 -0
  114. package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/ReportsView.test.tsx +27 -0
  115. package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/api.test.ts +19 -0
  116. package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/useReportSectionDiff.test.ts +11 -0
  117. package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/useReports.test.ts +13 -0
  118. package/dist/plugins/fusion-plugin-reports/src/dashboard/api.ts +85 -0
  119. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportApprovalPanel.css +59 -0
  120. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportApprovalPanel.tsx +58 -0
  121. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportComparisonDrawer.tsx +21 -0
  122. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportDetailPanel.tsx +29 -0
  123. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportEmptyState.tsx +3 -0
  124. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportFiltersBar.tsx +19 -0
  125. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportListItem.tsx +8 -0
  126. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ShareBlocksPanel.css +29 -0
  127. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ShareBlocksPanel.tsx +43 -0
  128. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/__tests__/ReportApprovalPanel.test.tsx +38 -0
  129. package/dist/plugins/fusion-plugin-reports/src/dashboard/components/__tests__/ShareBlocksPanel.test.tsx +24 -0
  130. package/dist/plugins/fusion-plugin-reports/src/dashboard/test-setup.ts +18 -0
  131. package/dist/plugins/fusion-plugin-reports/src/dashboard/types.ts +22 -0
  132. package/dist/plugins/fusion-plugin-reports/src/dashboard/useReportPreview.ts +44 -0
  133. package/dist/plugins/fusion-plugin-reports/src/dashboard/useReportSectionDiff.ts +59 -0
  134. package/dist/plugins/fusion-plugin-reports/src/dashboard/useReports.ts +71 -0
  135. package/dist/plugins/fusion-plugin-reports/src/dashboard/useViewportMode.ts +13 -0
  136. package/dist/plugins/fusion-plugin-reports/src/dashboard-view.tsx +6 -0
  137. package/dist/plugins/fusion-plugin-reports/src/index.ts +48 -2
  138. package/dist/plugins/fusion-plugin-reports/src/pipeline.ts +58 -0
  139. package/dist/plugins/fusion-plugin-reports/src/render/__tests__/escape.test.ts +20 -0
  140. package/dist/plugins/fusion-plugin-reports/src/render/__tests__/html-template.test.ts +110 -0
  141. package/dist/plugins/fusion-plugin-reports/src/render/__tests__/standalone-html.test.ts +66 -0
  142. package/dist/plugins/fusion-plugin-reports/src/render/escape.ts +12 -0
  143. package/dist/plugins/fusion-plugin-reports/src/render/html-styles.ts +40 -0
  144. package/dist/plugins/fusion-plugin-reports/src/render/html-template.ts +137 -0
  145. package/dist/plugins/fusion-plugin-reports/src/render/index.ts +4 -0
  146. package/dist/plugins/fusion-plugin-reports/src/render/standalone-html.ts +75 -0
  147. package/dist/plugins/fusion-plugin-reports/src/report-schema.ts +31 -0
  148. package/dist/plugins/fusion-plugin-reports/src/routes/__tests__/report-export-routes.test.ts +104 -0
  149. package/dist/plugins/fusion-plugin-reports/src/routes/report-approval-routes.ts +98 -0
  150. package/dist/plugins/fusion-plugin-reports/src/routes/report-export-routes.ts +77 -0
  151. package/dist/plugins/fusion-plugin-reports/src/routes/report-list-routes.ts +72 -0
  152. package/dist/plugins/fusion-plugin-reports/src/runs-store.ts +69 -0
  153. package/dist/plugins/fusion-plugin-reports/src/share-blocks.ts +82 -0
  154. package/dist/plugins/fusion-plugin-reports/src/store/report-store.ts +51 -2
  155. package/dist/plugins/fusion-plugin-reports/src/store/report-types.ts +6 -1
  156. package/dist/plugins/fusion-plugin-roadmap/bundled.js +1672 -0
  157. package/dist/plugins/fusion-plugin-roadmap/manifest.json +1 -1
  158. package/dist/plugins/fusion-plugin-roadmap/package.json +4 -41
  159. package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
  160. package/package.json +2 -3
  161. package/skill/fusion/references/engine-tools.md +1 -1
  162. package/skill/fusion/references/extension-tools.md +3 -3
  163. package/skill/fusion/references/fusion-capabilities.md +1 -1
  164. package/dist/client/assets/AgentDetailView-BwJaLqZh.css +0 -1
  165. package/dist/client/assets/AgentDetailView-ZbHEbYRT.js +0 -18
  166. package/dist/client/assets/ChatView-DhPkiEGs.js +0 -1
  167. package/dist/client/assets/InsightsView-vp0RE8Mg.js +0 -11
  168. package/dist/client/assets/NodesView-DMj6HGeC.js +0 -14
  169. package/dist/client/assets/PluginManager-BtYKm8IT.js +0 -1
  170. package/dist/client/assets/ResearchView-BhWqfdV0.js +0 -1
  171. package/dist/client/assets/SettingsModal-BAgB4_AR.js +0 -31
  172. package/dist/client/assets/SettingsModal-BNSrO1M9.css +0 -1
  173. package/dist/client/assets/SettingsModal-DzsLquBu.css +0 -1
  174. package/dist/client/assets/index-Qq2JOOWx.css +0 -1
  175. package/dist/client/assets/index-TFYXEVpn.js +0 -692
  176. package/dist/plugins/fusion-plugin-roadmap/src/__tests__/api-client.test.ts +0 -101
  177. package/dist/plugins/fusion-plugin-roadmap/src/__tests__/index.test.ts +0 -92
  178. package/dist/plugins/fusion-plugin-roadmap/src/__tests__/roadmap-routes.test.ts +0 -48
  179. package/dist/plugins/fusion-plugin-roadmap/src/__tests__/roadmap-suggestions.test.ts +0 -31
  180. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/RoadmapsView.css +0 -1299
  181. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/RoadmapsView.tsx +0 -2559
  182. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/__tests__/RoadmapsView.test.tsx +0 -1144
  183. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/__tests__/useRoadmaps.test.ts +0 -1756
  184. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/api.ts +0 -70
  185. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/test-setup.ts +0 -7
  186. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/types.ts +0 -1
  187. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/useConfirm.ts +0 -8
  188. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/useRoadmaps.ts +0 -1188
  189. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/useViewportMode.ts +0 -20
  190. package/dist/plugins/fusion-plugin-roadmap/src/dashboard-view.tsx +0 -6
  191. package/dist/plugins/fusion-plugin-roadmap/src/index.ts +0 -74
  192. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-routes.ts +0 -1
  193. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-schema.ts +0 -41
  194. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-suggestions.d.ts +0 -15
  195. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-suggestions.ts +0 -15
  196. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.d.ts +0 -283
  197. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.d.ts.map +0 -1
  198. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.js +0 -21
  199. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.js.map +0 -1
  200. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.ts +0 -310
  201. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.d.ts +0 -5
  202. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.d.ts.map +0 -1
  203. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.js +0 -361
  204. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.js.map +0 -1
  205. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.ts +0 -408
  206. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.d.ts +0 -68
  207. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.d.ts.map +0 -1
  208. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.js +0 -300
  209. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.js.map +0 -1
  210. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.ts +0 -381
  211. package/dist/plugins/fusion-plugin-roadmap/src/server/index.d.ts +0 -3
  212. package/dist/plugins/fusion-plugin-roadmap/src/server/index.ts +0 -1
  213. package/dist/plugins/fusion-plugin-roadmap/src/store/__tests__/roadmap-handoff.test.ts +0 -445
  214. package/dist/plugins/fusion-plugin-roadmap/src/store/__tests__/roadmap-ordering.test.ts +0 -334
  215. package/dist/plugins/fusion-plugin-roadmap/src/store/__tests__/roadmap-store.test.ts +0 -1318
  216. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-handoff.ts +0 -163
  217. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.d.ts +0 -37
  218. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.d.ts.map +0 -1
  219. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.js +0 -188
  220. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.js.map +0 -1
  221. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.ts +0 -311
  222. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.d.ts +0 -299
  223. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.d.ts.map +0 -1
  224. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.js +0 -765
  225. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.js.map +0 -1
  226. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.ts +0 -1001
@@ -0,0 +1,77 @@
1
+ import type { PluginContext, PluginRouteDefinition, PluginRouteResponse } from "@fusion/core";
2
+ import { ReportStore } from "../store/report-store.js";
3
+ import { renderReportHtml } from "../render/html-template.js";
4
+ import { renderStandaloneReportHtml, slugifyReportFilename } from "../render/standalone-html.js";
5
+
6
+ interface RouteRequest {
7
+ params: Record<string, string>;
8
+ }
9
+
10
+ const reportStoreCache = new WeakMap<object, ReportStore>();
11
+
12
+ function getStore(ctx: PluginContext): ReportStore {
13
+ const taskStoreWithReports = ctx.taskStore as PluginContext["taskStore"] & { getReportStore?: () => ReportStore };
14
+ if (typeof taskStoreWithReports.getReportStore === "function") {
15
+ return taskStoreWithReports.getReportStore();
16
+ }
17
+ const key = ctx.taskStore as object;
18
+ const cached = reportStoreCache.get(key);
19
+ if (cached) return cached;
20
+ const store = new ReportStore(ctx.taskStore.getDatabase());
21
+ reportStoreCache.set(key, store);
22
+ return store;
23
+ }
24
+
25
+ function notFound(message: string): PluginRouteResponse {
26
+ return { status: 404, body: { error: message } };
27
+ }
28
+
29
+ function conflict(message: string): PluginRouteResponse {
30
+ return { status: 409, body: { error: message } };
31
+ }
32
+
33
+ export function createReportExportRoutes(): PluginRouteDefinition[] {
34
+ return [
35
+ {
36
+ method: "GET",
37
+ path: "/reports/:id/export.html",
38
+ handler: async (req: unknown, ctx: PluginContext): Promise<PluginRouteResponse> => {
39
+ const request = req as RouteRequest;
40
+ const id = request.params.id;
41
+ const store = getStore(ctx);
42
+ const record = store.getReport(id);
43
+ if (!record) return notFound(`Report ${id} not found`);
44
+ if (record.status === "generating") return conflict(`Report ${id} is not generated yet`);
45
+ const html = record.renderedHtml ?? renderStandaloneReportHtml(record);
46
+ if (!record.renderedHtml) {
47
+ store.setRenderedHtml(id, html);
48
+ }
49
+ return {
50
+ status: 200,
51
+ body: html,
52
+ contentType: "text/html; charset=utf-8",
53
+ headers: {
54
+ "Content-Disposition": `attachment; filename="${slugifyReportFilename(record)}"`,
55
+ },
56
+ };
57
+ },
58
+ },
59
+ {
60
+ method: "GET",
61
+ path: "/reports/:id/preview.html",
62
+ handler: async (req: unknown, ctx: PluginContext): Promise<PluginRouteResponse> => {
63
+ const request = req as RouteRequest;
64
+ const id = request.params.id;
65
+ const store = getStore(ctx);
66
+ const record = store.getReport(id);
67
+ if (!record) return notFound(`Report ${id} not found`);
68
+ if (record.status === "generating") return conflict(`Report ${id} is not generated yet`);
69
+ return {
70
+ status: 200,
71
+ body: renderReportHtml(record, { includeChrome: false }),
72
+ contentType: "text/html; charset=utf-8",
73
+ };
74
+ },
75
+ },
76
+ ];
77
+ }
@@ -0,0 +1,72 @@
1
+ import type { PluginContext, PluginRouteDefinition, PluginRouteResponse } from "@fusion/core";
2
+ import { ReportStore } from "../store/report-store.js";
3
+
4
+ interface RouteRequest {
5
+ params: Record<string, string>;
6
+ query?: Record<string, string | string[] | undefined>;
7
+ }
8
+
9
+ const reportStoreCache = new WeakMap<object, ReportStore>();
10
+
11
+ function getStore(ctx: PluginContext): ReportStore {
12
+ const key = ctx.taskStore as object;
13
+ const cached = reportStoreCache.get(key);
14
+ if (cached) return cached;
15
+ const store = new ReportStore(ctx.taskStore.getDatabase());
16
+ reportStoreCache.set(key, store);
17
+ return store;
18
+ }
19
+
20
+ function badRequest(message: string): PluginRouteResponse {
21
+ return { status: 400, body: { error: message } };
22
+ }
23
+
24
+ export function createReportListRoutes(): PluginRouteDefinition[] {
25
+ return [
26
+ {
27
+ method: "GET",
28
+ path: "/reports",
29
+ handler: async (req: unknown, ctx: PluginContext): Promise<PluginRouteResponse> => {
30
+ const request = req as RouteRequest;
31
+ const query = request.query ?? {};
32
+ const cadence = typeof query.cadence === "string" && query.cadence.length > 0 ? query.cadence : undefined;
33
+ const status = typeof query.status === "string" && query.status.length > 0 ? query.status : undefined;
34
+ const periodStartFrom = typeof query.from === "string" && query.from.length > 0 ? query.from : undefined;
35
+ const periodStartTo = typeof query.to === "string" && query.to.length > 0 ? query.to : undefined;
36
+ const q = typeof query.q === "string" && query.q.length > 0 ? query.q.toLowerCase() : undefined;
37
+ const agent = typeof query.agentId === "string" && query.agentId.length > 0 ? query.agentId.toLowerCase() : undefined;
38
+
39
+ const store = getStore(ctx);
40
+ const reports = store.listReports({
41
+ cadence: cadence as never,
42
+ status: status as never,
43
+ periodStartFrom,
44
+ periodStartTo,
45
+ orderBy: "periodStart",
46
+ orderDir: "desc",
47
+ limit: 500,
48
+ });
49
+
50
+ const filtered = reports.filter((report) => {
51
+ if (q && !report.title.toLowerCase().includes(q)) return false;
52
+ if (agent) {
53
+ const agentIds = ((report.metadata?.agentIds as string[] | undefined) ?? []).map((id) => id.toLowerCase());
54
+ if (!agentIds.some((id) => id.includes(agent))) return false;
55
+ }
56
+ return true;
57
+ });
58
+ return { status: 200, body: { reports: filtered } };
59
+ },
60
+ },
61
+ {
62
+ method: "GET",
63
+ path: "/reports/:id",
64
+ handler: async (req: unknown, ctx: PluginContext): Promise<PluginRouteResponse> => {
65
+ const request = req as RouteRequest;
66
+ const report = getStore(ctx).getReport(request.params.id);
67
+ if (!report) return { status: 404, body: { error: `Report ${request.params.id} not found` } };
68
+ return { status: 200, body: { report } };
69
+ },
70
+ },
71
+ ];
72
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * INTERIM IN-MEMORY STORE — replaced by FN-3784's persistent storage.
3
+ * The `ReportRunRecord` shape is intended to be a strict subset of FN-3784's eventual schema.
4
+ */
5
+ import type { ReportsCadence } from "./cadence.js";
6
+
7
+ export type ReportRunStatus = "queued" | "running" | "review" | "approved" | "published" | "failed";
8
+
9
+ export interface ReportRunRecord {
10
+ id: string;
11
+ cadence: ReportsCadence;
12
+ status: ReportRunStatus;
13
+ createdAt: string;
14
+ updatedAt: string;
15
+ error?: string;
16
+ reportId?: string;
17
+ }
18
+
19
+ export interface ReportsRunsStore {
20
+ create(record: ReportRunRecord): Promise<ReportRunRecord>;
21
+ update(
22
+ id: string,
23
+ patch: Partial<Omit<ReportRunRecord, "id" | "createdAt">>,
24
+ ): Promise<ReportRunRecord | undefined>;
25
+ get(id: string): Promise<ReportRunRecord | undefined>;
26
+ list(limit?: number): Promise<ReportRunRecord[]>;
27
+ }
28
+
29
+ function cloneRecord(record: ReportRunRecord): ReportRunRecord {
30
+ return { ...record };
31
+ }
32
+
33
+ export function createInMemoryReportsRunsStore(seed: ReportRunRecord[] = []): ReportsRunsStore {
34
+ const records = new Map<string, ReportRunRecord>(seed.map((record) => [record.id, cloneRecord(record)]));
35
+
36
+ return {
37
+ async create(record) {
38
+ const stored = cloneRecord(record);
39
+ records.set(stored.id, stored);
40
+ return cloneRecord(stored);
41
+ },
42
+
43
+ async update(id, patch) {
44
+ const current = records.get(id);
45
+ if (!current) return undefined;
46
+
47
+ const next: ReportRunRecord = {
48
+ ...current,
49
+ ...patch,
50
+ updatedAt: patch.updatedAt ?? new Date().toISOString(),
51
+ };
52
+ records.set(id, next);
53
+ return cloneRecord(next);
54
+ },
55
+
56
+ async get(id) {
57
+ const record = records.get(id);
58
+ return record ? cloneRecord(record) : undefined;
59
+ },
60
+
61
+ async list(limit = 50) {
62
+ const clampedLimit = Math.max(0, limit);
63
+ return Array.from(records.values())
64
+ .sort((a, b) => b.createdAt.localeCompare(a.createdAt))
65
+ .slice(0, clampedLimit)
66
+ .map((record) => cloneRecord(record));
67
+ },
68
+ };
69
+ }
@@ -0,0 +1,82 @@
1
+ import type { Report } from "./store/report-types.js";
2
+
3
+ const MAX_ITEMS_PER_SECTION = 5;
4
+ const MAX_BLOCK_LENGTH = 1500;
5
+
6
+ export interface ShareBlocks {
7
+ plainText: string;
8
+ markdown: string;
9
+ slack: string;
10
+ emailHtml: string;
11
+ }
12
+
13
+ function sliceWithEllipsis(items: string[]): string[] {
14
+ if (items.length <= MAX_ITEMS_PER_SECTION) return items;
15
+ return [...items.slice(0, MAX_ITEMS_PER_SECTION), "…"];
16
+ }
17
+
18
+ function getSections(report: Report): Array<{ heading: string; items: string[] }> {
19
+ const review = report.combinedReview;
20
+ const sections: Array<{ heading: string; items: string[] }> = [];
21
+ const wins = review?.mergedHighlights ?? [];
22
+ const highlights = review?.mergedSuggestions ?? [];
23
+ const lowlights = review?.mergedLowlights ?? [];
24
+ if (wins.length > 0) sections.push({ heading: "Wins", items: sliceWithEllipsis(wins) });
25
+ if (highlights.length > 0) sections.push({ heading: "Highlights", items: sliceWithEllipsis(highlights) });
26
+ if (lowlights.length > 0) sections.push({ heading: "Lowlights", items: sliceWithEllipsis(lowlights) });
27
+ return sections;
28
+ }
29
+
30
+ function trimBlock(text: string): string {
31
+ return text.length <= MAX_BLOCK_LENGTH ? text : `${text.slice(0, MAX_BLOCK_LENGTH - 1)}…`;
32
+ }
33
+
34
+ export function escapeMarkdown(value: string): string {
35
+ return value.replace(/[\\`*_{}\[\]()#+\-.!|<>]/g, "\\$&");
36
+ }
37
+
38
+ function escapeHtml(value: string): string {
39
+ return value
40
+ .replaceAll("&", "&amp;")
41
+ .replaceAll("<", "&lt;")
42
+ .replaceAll(">", "&gt;")
43
+ .replaceAll('"', "&quot;")
44
+ .replaceAll("'", "&#39;");
45
+ }
46
+
47
+ /** Builds deterministic share-ready text blocks. */
48
+ export function buildShareBlocks(report: Report): ShareBlocks {
49
+ const sections = getSections(report);
50
+ const heading = `${report.title}\n${report.periodStart} → ${report.periodEnd}`;
51
+ const plainText = trimBlock([
52
+ heading,
53
+ ...sections.map((section) => `${section.heading}:\n${section.items.map((item) => `- ${item}`).join("\n")}`),
54
+ ].join("\n\n"));
55
+
56
+ const reportUrl = `/reports/${encodeURIComponent(report.id)}`;
57
+ const markdown = trimBlock([
58
+ `## ${escapeMarkdown(report.title)}`,
59
+ `Period: ${escapeMarkdown(report.periodStart)} → ${escapeMarkdown(report.periodEnd)}`,
60
+ ...sections.map((section) => `### ${section.heading}\n${section.items.map((item) => `- ${escapeMarkdown(item)}`).join("\n")}`),
61
+ `[Open report](${reportUrl})`,
62
+ ].join("\n\n"));
63
+
64
+ const slack = trimBlock([
65
+ `*${report.title}*`,
66
+ `${report.periodStart} → ${report.periodEnd}`,
67
+ ...sections.map((section) => `*${section.heading}*\n${section.items.map((item) => `• ${item}`).join("\n")}`),
68
+ `<${reportUrl}|Open report>`,
69
+ ].join("\n\n"));
70
+
71
+ // Note: emailHtml uses hardcoded hex/inline styles for email-client compatibility — design-token rule does not apply here.
72
+ const emailHtml = [
73
+ `<div style="font-family:Arial,sans-serif;color:#1f2328;line-height:1.5;">`,
74
+ `<h2 style="margin:0 0 12px;color:#5B8DEF;">${escapeHtml(report.title)}</h2>`,
75
+ `<p style="margin:0 0 12px;">Period: ${escapeHtml(report.periodStart)} → ${escapeHtml(report.periodEnd)}</p>`,
76
+ ...sections.map((section) => `<h3 style="margin:12px 0 6px;">${escapeHtml(section.heading)}</h3><ul style="margin:0 0 12px;padding-left:20px;">${section.items.map((item) => `<li>${escapeHtml(item)}</li>`).join("")}</ul>`),
77
+ `<p style="margin:0;"><a href="${escapeHtml(reportUrl)}" style="color:#5B8DEF;">Open report</a></p>`,
78
+ `</div>`,
79
+ ].join("");
80
+
81
+ return { plainText, markdown, slack, emailHtml };
82
+ }
@@ -1,6 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { EventEmitter } from "node:events";
3
3
  import type { Database } from "@fusion/core";
4
+ import type { ApprovalDecision, ApprovalState } from "../approval.js";
4
5
  import type { CombinedReview } from "../review-types.js";
5
6
  import {
6
7
  type Report,
@@ -27,8 +28,12 @@ interface ReportRow {
27
28
  publishedAt: string | null;
28
29
  archivedAt: string | null;
29
30
  failureReason: string | null;
31
+ approval_state: ApprovalState;
32
+ approval_history: string;
30
33
  draftMarkdown: string | null;
31
34
  renderedHtmlPath: string | null;
35
+ rendered_html: string | null;
36
+ rendered_html_generated_at: string | null;
32
37
  metadataJson: string;
33
38
  combinedReviewJson: string | null;
34
39
  createdAt: string;
@@ -74,8 +79,12 @@ export class ReportStore extends EventEmitter<ReportStoreEvents> {
74
79
  publishedAt: null,
75
80
  archivedAt: null,
76
81
  failureReason: null,
82
+ approvalState: "not_required",
83
+ approvalHistory: [],
77
84
  draftMarkdown: input.draftMarkdown ?? null,
78
85
  renderedHtmlPath: null,
86
+ renderedHtml: null,
87
+ renderedHtmlGeneratedAt: null,
79
88
  metadata: input.metadata ?? {},
80
89
  combinedReview: null,
81
90
  createdAt: now,
@@ -88,12 +97,14 @@ export class ReportStore extends EventEmitter<ReportStoreEvents> {
88
97
  id, cadence, periodStart, periodEnd, title, status,
89
98
  generationStartedAt, generationCompletedAt, reviewStartedAt, reviewCompletedAt,
90
99
  approvedAt, approvedBy, publishedAt, archivedAt, failureReason,
91
- draftMarkdown, renderedHtmlPath, metadataJson, combinedReviewJson, createdAt, updatedAt
100
+ approval_state, approval_history,
101
+ draftMarkdown, renderedHtmlPath, rendered_html, rendered_html_generated_at, metadataJson, combinedReviewJson, createdAt, updatedAt
92
102
  ) VALUES (
93
103
  @id, @cadence, @periodStart, @periodEnd, @title, @status,
94
104
  @generationStartedAt, @generationCompletedAt, @reviewStartedAt, @reviewCompletedAt,
95
105
  @approvedAt, @approvedBy, @publishedAt, @archivedAt, @failureReason,
96
- @draftMarkdown, @renderedHtmlPath, @metadataJson, @combinedReviewJson, @createdAt, @updatedAt
106
+ @approvalState, @approvalHistory,
107
+ @draftMarkdown, @renderedHtmlPath, @renderedHtml, @renderedHtmlGeneratedAt, @metadataJson, @combinedReviewJson, @createdAt, @updatedAt
97
108
  )
98
109
  `).run(this.toDbParams(report, true));
99
110
  });
@@ -157,7 +168,16 @@ export class ReportStore extends EventEmitter<ReportStoreEvents> {
157
168
  draftMarkdown: patch.draftMarkdown ?? current.draftMarkdown,
158
169
  renderedHtmlPath: patch.renderedHtmlPath ?? current.renderedHtmlPath,
159
170
  metadata: patch.metadata ?? current.metadata,
171
+ renderedHtml: patch.renderedHtml ?? current.renderedHtml,
172
+ renderedHtmlGeneratedAt: patch.renderedHtmlGeneratedAt ?? current.renderedHtmlGeneratedAt,
160
173
  failureReason: patch.failureReason ?? current.failureReason,
174
+ approvalState: patch.approvalState ?? current.approvalState,
175
+ approvalHistory: patch.approvalHistory ?? current.approvalHistory,
176
+ status: patch.status ?? current.status,
177
+ approvedAt: patch.approvedAt ?? current.approvedAt,
178
+ approvedBy: patch.approvedBy ?? current.approvedBy,
179
+ publishedAt: patch.publishedAt ?? current.publishedAt,
180
+ reviewCompletedAt: patch.reviewCompletedAt ?? current.reviewCompletedAt,
161
181
  updatedAt: new Date().toISOString(),
162
182
  };
163
183
 
@@ -224,6 +244,13 @@ export class ReportStore extends EventEmitter<ReportStoreEvents> {
224
244
  return this.updateReport(id, { renderedHtmlPath: htmlPath });
225
245
  }
226
246
 
247
+ setRenderedHtml(id: string, html: string): Report {
248
+ return this.updateReport(id, {
249
+ renderedHtml: html,
250
+ renderedHtmlGeneratedAt: new Date().toISOString(),
251
+ });
252
+ }
253
+
227
254
  deleteReport(id: string): void {
228
255
  this.requireReport(id);
229
256
  this.db.transaction(() => {
@@ -256,8 +283,12 @@ export class ReportStore extends EventEmitter<ReportStoreEvents> {
256
283
  publishedAt: row.publishedAt,
257
284
  archivedAt: row.archivedAt,
258
285
  failureReason: row.failureReason,
286
+ approvalState: row.approval_state,
287
+ approvalHistory: this.parseApprovalHistory(row.approval_history),
259
288
  draftMarkdown: row.draftMarkdown,
260
289
  renderedHtmlPath: row.renderedHtmlPath,
290
+ renderedHtml: row.rendered_html,
291
+ renderedHtmlGeneratedAt: row.rendered_html_generated_at,
261
292
  metadata: this.parseMetadata(row.metadataJson),
262
293
  combinedReview: this.parseCombinedReview(row.combinedReviewJson),
263
294
  createdAt: row.createdAt,
@@ -282,8 +313,12 @@ export class ReportStore extends EventEmitter<ReportStoreEvents> {
282
313
  publishedAt = @publishedAt,
283
314
  archivedAt = @archivedAt,
284
315
  failureReason = @failureReason,
316
+ approval_state = @approvalState,
317
+ approval_history = @approvalHistory,
285
318
  draftMarkdown = @draftMarkdown,
286
319
  renderedHtmlPath = @renderedHtmlPath,
320
+ rendered_html = @renderedHtml,
321
+ rendered_html_generated_at = @renderedHtmlGeneratedAt,
287
322
  metadataJson = @metadataJson,
288
323
  combinedReviewJson = @combinedReviewJson,
289
324
  updatedAt = @updatedAt
@@ -312,8 +347,12 @@ export class ReportStore extends EventEmitter<ReportStoreEvents> {
312
347
  publishedAt: report.publishedAt,
313
348
  archivedAt: report.archivedAt,
314
349
  failureReason: report.failureReason,
350
+ approvalState: report.approvalState,
351
+ approvalHistory: JSON.stringify(report.approvalHistory ?? []),
315
352
  draftMarkdown: report.draftMarkdown,
316
353
  renderedHtmlPath: report.renderedHtmlPath,
354
+ renderedHtml: report.renderedHtml,
355
+ renderedHtmlGeneratedAt: report.renderedHtmlGeneratedAt,
317
356
  metadataJson: JSON.stringify(report.metadata ?? {}),
318
357
  combinedReviewJson: report.combinedReview ? JSON.stringify(report.combinedReview) : null,
319
358
  ...(includeCreatedAt ? { createdAt: report.createdAt } : {}),
@@ -338,4 +377,14 @@ export class ReportStore extends EventEmitter<ReportStoreEvents> {
338
377
  return null;
339
378
  }
340
379
  }
380
+
381
+ private parseApprovalHistory(json: string | null): ApprovalDecision[] {
382
+ if (!json) return [];
383
+ try {
384
+ const parsed = JSON.parse(json);
385
+ return Array.isArray(parsed) ? parsed as ApprovalDecision[] : [];
386
+ } catch {
387
+ return [];
388
+ }
389
+ }
341
390
  }
@@ -1,3 +1,4 @@
1
+ import type { ApprovalDecision, ApprovalState } from "../approval.js";
1
2
  import type { CombinedReview } from "../review-types.js";
2
3
 
3
4
  export type ReportCadence = "daily" | "weekly" | "monthly" | "quarterly" | "manual";
@@ -28,8 +29,12 @@ export interface Report {
28
29
  publishedAt: string | null;
29
30
  archivedAt: string | null;
30
31
  failureReason: string | null;
32
+ approvalState: ApprovalState;
33
+ approvalHistory: ApprovalDecision[];
31
34
  draftMarkdown: string | null;
32
35
  renderedHtmlPath: string | null;
36
+ renderedHtml: string | null;
37
+ renderedHtmlGeneratedAt: string | null;
33
38
  metadata: Record<string, unknown>;
34
39
  combinedReview: CombinedReview | null;
35
40
  createdAt: string;
@@ -45,7 +50,7 @@ export interface ReportCreateInput {
45
50
  draftMarkdown?: string;
46
51
  }
47
52
 
48
- export type ReportUpdateInput = Partial<Pick<Report, "title" | "draftMarkdown" | "renderedHtmlPath" | "metadata" | "failureReason">>;
53
+ export type ReportUpdateInput = Partial<Pick<Report, "title" | "draftMarkdown" | "renderedHtmlPath" | "renderedHtml" | "renderedHtmlGeneratedAt" | "metadata" | "failureReason" | "approvalState" | "approvalHistory" | "status" | "approvedAt" | "approvedBy" | "publishedAt" | "reviewCompletedAt">>;
49
54
 
50
55
  export interface ReportListFilter {
51
56
  cadence?: ReportCadence;