@runfusion/fusion 0.23.0 → 0.24.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 (204) hide show
  1. package/dist/bin.js +26610 -20597
  2. package/dist/client/assets/AgentDetailView-BwJaLqZh.css +1 -0
  3. package/dist/client/assets/AgentDetailView-gy_5SUj2.js +18 -0
  4. package/dist/client/assets/AgentsView-BkB9FiMT.js +29 -0
  5. package/dist/client/assets/{AgentsView-DSGQWObq.css → AgentsView-CV3vm7Qk.css} +1 -1
  6. package/dist/client/assets/ChatView-B_-B8fqu.js +1 -0
  7. package/dist/client/assets/ChatView-DwJAd5G1.css +1 -0
  8. package/dist/client/assets/{DevServerView-C9lzHrcT.js → DevServerView-BkvtjZBa.js} +1 -1
  9. package/dist/client/assets/{DirectoryPicker-aVdFaV37.js → DirectoryPicker-BK-KbnhP.js} +1 -1
  10. package/dist/client/assets/{DocumentsView-DIpg3NSP.js → DocumentsView-BEg1CQAk.js} +1 -1
  11. package/dist/client/assets/{DocumentsView-BrhyOdeE.css → DocumentsView-gv4zG3aT.css} +1 -1
  12. package/dist/client/assets/EvalsView-Berf9bQm.js +1 -0
  13. package/dist/client/assets/EvalsView-CUNJ1TLc.css +1 -0
  14. package/dist/client/assets/{agentSkills-DDHJnrkn.css → ExperimentalAgentOnboardingModal-B-APN_lM.css} +1 -1
  15. package/dist/client/assets/ExperimentalAgentOnboardingModal-jcInE50G.js +499 -0
  16. package/dist/client/assets/InsightsView-B0J4mhzV.css +1 -0
  17. package/dist/client/assets/InsightsView-BX5bSF1J.js +11 -0
  18. package/dist/client/assets/{MemoryView-nXlTqebk.js → MemoryView-CKElJY_3.js} +2 -2
  19. package/dist/client/assets/NodesView-DLUOBLf6.js +14 -0
  20. package/dist/client/assets/NodesView-DT4pXowv.css +1 -0
  21. package/dist/client/assets/{PiExtensionsManager-Buopv-jb.js → PiExtensionsManager-COlJf0Kx.js} +2 -2
  22. package/dist/client/assets/PluginManager-CfW55BF4.js +1 -0
  23. package/dist/client/assets/PluginManager-DtRQXia5.css +1 -0
  24. package/dist/client/assets/{ResearchView-_BHXUv2j.js → ResearchView-B256Lr8I.js} +1 -1
  25. package/dist/client/assets/SettingsModal-BeA_nQtW.js +31 -0
  26. package/dist/client/assets/SettingsModal-DzsLquBu.css +1 -0
  27. package/dist/client/assets/{SettingsModal-C89Ikhfm.js → SettingsModal-yRqM4DV8.js} +1 -1
  28. package/dist/client/assets/SetupWizardModal-uUZk3TKT.js +1 -0
  29. package/dist/client/assets/{SkillsView-hDpTBdFT.js → SkillsView-CP8JX0P_.js} +1 -1
  30. package/dist/client/assets/TodoView-Cx9cVhq7.css +1 -0
  31. package/dist/client/assets/TodoView-DCRIkDZ-.js +6 -0
  32. package/dist/client/assets/createLucideIcon-BazL2hk5.js +21 -0
  33. package/dist/client/assets/dashboard-view-BkTMSZYn.css +1 -0
  34. package/dist/client/assets/dashboard-view-CyWN-d02.js +63 -0
  35. package/dist/client/assets/dashboard-view-lR7YYmSC.js +21 -0
  36. package/dist/client/assets/{folder-open-usZkXdq2.js → folder-open-DHjELt8-.js} +1 -1
  37. package/dist/client/assets/index-CQyVRLOb.js +692 -0
  38. package/dist/client/assets/index-CxA2Nn0_.css +1 -0
  39. package/dist/client/assets/projectDetection-G3XuxD2X.js +1 -0
  40. package/dist/client/assets/{star-BAT_ObKE.js → star-DYesq1AV.js} +1 -1
  41. package/dist/client/assets/{upload-BC2YKNEV.js → upload-DTWF3Db5.js} +1 -1
  42. package/dist/client/assets/{users-Dkd4rtrN.js → users--syrel4l.js} +1 -1
  43. package/dist/client/index.html +12 -20
  44. package/dist/client/theme-data.css +106 -0
  45. package/dist/client/version.json +1 -1
  46. package/dist/droid-cli/package.json +1 -1
  47. package/dist/extension.js +14287 -9568
  48. package/dist/pi-claude-cli/package.json +1 -1
  49. package/dist/plugins/fusion-plugin-cursor-runtime/bundled.js +218 -0
  50. package/dist/plugins/fusion-plugin-cursor-runtime/manifest.json +6 -0
  51. package/dist/plugins/fusion-plugin-cursor-runtime/package.json +11 -0
  52. package/dist/plugins/fusion-plugin-dependency-graph/manifest.json +1 -1
  53. package/dist/plugins/fusion-plugin-dependency-graph/package.json +6 -4
  54. package/dist/plugins/fusion-plugin-dependency-graph/src/DependencyGraph.css +58 -0
  55. package/dist/plugins/fusion-plugin-dependency-graph/src/DependencyGraph.tsx +301 -0
  56. package/dist/plugins/fusion-plugin-dependency-graph/src/GraphHighlight.css +27 -0
  57. package/dist/plugins/fusion-plugin-dependency-graph/src/GraphTaskNode.css +157 -0
  58. package/dist/plugins/fusion-plugin-dependency-graph/src/GraphTaskNode.tsx +126 -0
  59. package/dist/plugins/fusion-plugin-dependency-graph/src/GraphToolbar.css +35 -0
  60. package/dist/plugins/fusion-plugin-dependency-graph/src/GraphToolbar.tsx +36 -0
  61. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/DependencyGraph.highlighting.test.tsx +112 -0
  62. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/DependencyGraph.persistence.test.tsx +115 -0
  63. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/DependencyGraph.test.tsx +128 -0
  64. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphTaskNode.drag.test.tsx +82 -0
  65. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphTaskNode.test.tsx +307 -0
  66. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphToolbar.test.tsx +60 -0
  67. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/edges.test.tsx +75 -0
  68. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/filtering.test.tsx +62 -0
  69. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/filters.test.ts +78 -0
  70. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/graphPositionStorage.test.ts +95 -0
  71. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/host-integration.test.ts +74 -0
  72. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/index.test.ts +58 -0
  73. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/interactions.test.tsx +121 -0
  74. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/layout.test.ts +70 -0
  75. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/persistence.test.tsx +89 -0
  76. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/useGraphData.test.ts +86 -0
  77. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/useGraphInteraction.test.ts +167 -0
  78. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/useGraphPositions.test.ts +66 -0
  79. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/useNodeDrag.test.ts +81 -0
  80. package/dist/plugins/fusion-plugin-dependency-graph/src/dashboard-interop.d.ts +35 -0
  81. package/dist/plugins/fusion-plugin-dependency-graph/src/dashboard-view.tsx +19 -0
  82. package/dist/plugins/fusion-plugin-dependency-graph/src/edges.tsx +70 -0
  83. package/dist/plugins/fusion-plugin-dependency-graph/src/filters.ts +8 -0
  84. package/dist/plugins/fusion-plugin-dependency-graph/src/hooks/__tests__/useDependencyChain.test.ts +53 -0
  85. package/dist/plugins/fusion-plugin-dependency-graph/src/hooks/useDependencyChain.ts +60 -0
  86. package/dist/plugins/fusion-plugin-dependency-graph/src/hooks/useGraphPositions.ts +45 -0
  87. package/dist/plugins/fusion-plugin-dependency-graph/src/hooks/useNodeDrag.ts +114 -0
  88. package/dist/plugins/fusion-plugin-dependency-graph/src/index.ts +1 -2
  89. package/dist/plugins/fusion-plugin-dependency-graph/src/layout.ts +91 -0
  90. package/dist/plugins/fusion-plugin-dependency-graph/src/styles/drag.css +15 -0
  91. package/dist/plugins/fusion-plugin-dependency-graph/src/types.ts +21 -0
  92. package/dist/plugins/fusion-plugin-dependency-graph/src/useGraphData.ts +17 -0
  93. package/dist/plugins/fusion-plugin-dependency-graph/src/useGraphInteraction.ts +292 -0
  94. package/dist/plugins/fusion-plugin-dependency-graph/src/utils/graphPositionStorage.ts +65 -0
  95. package/dist/plugins/fusion-plugin-droid-runtime/bundled.js +136680 -0
  96. package/dist/plugins/fusion-plugin-droid-runtime/manifest.json +13 -0
  97. package/dist/plugins/fusion-plugin-droid-runtime/mcp-schema-server.cjs +49 -0
  98. package/dist/plugins/fusion-plugin-droid-runtime/package.json +11 -0
  99. package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
  100. package/dist/plugins/fusion-plugin-openclaw-runtime/bundled.js +93 -6
  101. package/dist/plugins/fusion-plugin-openclaw-runtime/mcp-schema-server.cjs +59 -0
  102. package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
  103. package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
  104. package/dist/plugins/fusion-plugin-reports/manifest.json +33 -0
  105. package/dist/plugins/fusion-plugin-reports/package.json +26 -0
  106. package/dist/plugins/fusion-plugin-reports/src/__tests__/manifest.test.ts +51 -0
  107. package/dist/plugins/fusion-plugin-reports/src/__tests__/review-panel.test.ts +166 -0
  108. package/dist/plugins/fusion-plugin-reports/src/__tests__/settings.test.ts +157 -0
  109. package/dist/plugins/fusion-plugin-reports/src/index.ts +41 -0
  110. package/dist/plugins/fusion-plugin-reports/src/review-panel.ts +294 -0
  111. package/dist/plugins/fusion-plugin-reports/src/review-types.ts +75 -0
  112. package/dist/plugins/fusion-plugin-reports/src/settings.ts +105 -0
  113. package/dist/plugins/fusion-plugin-roadmap/manifest.json +16 -0
  114. package/dist/plugins/fusion-plugin-roadmap/package.json +48 -0
  115. package/dist/plugins/fusion-plugin-roadmap/src/__tests__/api-client.test.ts +101 -0
  116. package/dist/plugins/fusion-plugin-roadmap/src/__tests__/index.test.ts +92 -0
  117. package/dist/plugins/fusion-plugin-roadmap/src/__tests__/roadmap-routes.test.ts +48 -0
  118. package/dist/plugins/fusion-plugin-roadmap/src/__tests__/roadmap-suggestions.test.ts +31 -0
  119. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/RoadmapsView.css +1299 -0
  120. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/RoadmapsView.tsx +2559 -0
  121. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/__tests__/RoadmapsView.test.tsx +1144 -0
  122. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/__tests__/useRoadmaps.test.ts +1756 -0
  123. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/api.ts +70 -0
  124. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/test-setup.ts +7 -0
  125. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/types.ts +1 -0
  126. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/useConfirm.ts +8 -0
  127. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/useRoadmaps.ts +1188 -0
  128. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/useViewportMode.ts +20 -0
  129. package/dist/plugins/fusion-plugin-roadmap/src/dashboard-view.tsx +6 -0
  130. package/dist/plugins/fusion-plugin-roadmap/src/index.ts +74 -0
  131. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-routes.ts +1 -0
  132. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-schema.ts +41 -0
  133. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-suggestions.d.ts +15 -0
  134. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-suggestions.ts +15 -0
  135. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.d.ts +283 -0
  136. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.d.ts.map +1 -0
  137. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.js +21 -0
  138. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.js.map +1 -0
  139. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.ts +310 -0
  140. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.d.ts +5 -0
  141. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.d.ts.map +1 -0
  142. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.js +361 -0
  143. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.js.map +1 -0
  144. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.ts +408 -0
  145. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.d.ts +68 -0
  146. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.d.ts.map +1 -0
  147. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.js +300 -0
  148. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.js.map +1 -0
  149. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.ts +381 -0
  150. package/dist/plugins/fusion-plugin-roadmap/src/server/index.d.ts +3 -0
  151. package/dist/plugins/fusion-plugin-roadmap/src/server/index.ts +1 -0
  152. package/dist/plugins/fusion-plugin-roadmap/src/store/__tests__/roadmap-handoff.test.ts +445 -0
  153. package/dist/plugins/fusion-plugin-roadmap/src/store/__tests__/roadmap-ordering.test.ts +334 -0
  154. package/dist/plugins/fusion-plugin-roadmap/src/store/__tests__/roadmap-store.test.ts +1318 -0
  155. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-handoff.ts +163 -0
  156. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.d.ts +37 -0
  157. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.d.ts.map +1 -0
  158. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.js +188 -0
  159. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.js.map +1 -0
  160. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.ts +311 -0
  161. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.d.ts +299 -0
  162. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.d.ts.map +1 -0
  163. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.js +765 -0
  164. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.js.map +1 -0
  165. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.ts +1001 -0
  166. package/dist/plugins/fusion-plugin-whatsapp-chat/manifest.json +8 -0
  167. package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +34 -0
  168. package/dist/plugins/fusion-plugin-whatsapp-chat/src/__tests__/auth-state.test.ts +99 -0
  169. package/dist/plugins/fusion-plugin-whatsapp-chat/src/__tests__/connection.test.ts +145 -0
  170. package/dist/plugins/fusion-plugin-whatsapp-chat/src/__tests__/index.test.ts +216 -0
  171. package/dist/plugins/fusion-plugin-whatsapp-chat/src/__tests__/reply.test.ts +52 -0
  172. package/dist/plugins/fusion-plugin-whatsapp-chat/src/auth-state.ts +89 -0
  173. package/dist/plugins/fusion-plugin-whatsapp-chat/src/connection.ts +253 -0
  174. package/dist/plugins/fusion-plugin-whatsapp-chat/src/index.ts +262 -0
  175. package/dist/plugins/fusion-plugin-whatsapp-chat/src/qrcode.d.ts +1 -0
  176. package/dist/plugins/fusion-plugin-whatsapp-chat/src/reply.ts +37 -0
  177. package/package.json +2 -2
  178. package/skill/fusion/SKILL.md +2 -2
  179. package/skill/fusion/references/engine-tools.md +3 -0
  180. package/skill/fusion/references/extension-tools.md +39 -0
  181. package/skill/fusion/references/fusion-capabilities.md +3 -0
  182. package/dist/client/assets/AgentDetailView-C1XceMgi.js +0 -18
  183. package/dist/client/assets/AgentDetailView-CeO_1MK7.css +0 -1
  184. package/dist/client/assets/AgentsView-Deh125ss.js +0 -527
  185. package/dist/client/assets/ChatView-7D_RQDqT.js +0 -1
  186. package/dist/client/assets/InsightsView-AWo5o_81.css +0 -1
  187. package/dist/client/assets/InsightsView-jKjEFAx_.js +0 -11
  188. package/dist/client/assets/NodesView-Di2SvOhg.js +0 -14
  189. package/dist/client/assets/NodesView-fXqDk9ur.css +0 -1
  190. package/dist/client/assets/PluginManager-B9-NbQ8f.js +0 -1
  191. package/dist/client/assets/PluginManager-C1DbPaar.css +0 -1
  192. package/dist/client/assets/RoadmapsView-DHWjUoc8.js +0 -6
  193. package/dist/client/assets/SettingsModal-DHitIpsa.css +0 -1
  194. package/dist/client/assets/SettingsModal-DR_yirvK.js +0 -31
  195. package/dist/client/assets/SetupWizardModal-BtDMY9pa.js +0 -1
  196. package/dist/client/assets/agentSkills-B-w5wFHh.js +0 -1
  197. package/dist/client/assets/index-Bc6ZdGMz.css +0 -1
  198. package/dist/client/assets/index-D__RMku8.js +0 -694
  199. package/dist/plugins/fusion-plugin-dependency-graph/src/DependencyGraphView.css +0 -141
  200. package/dist/plugins/fusion-plugin-dependency-graph/src/DependencyGraphView.tsx +0 -428
  201. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/DependencyGraphView.test.tsx +0 -261
  202. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/storage.test.ts +0 -41
  203. package/dist/plugins/fusion-plugin-dependency-graph/src/storage.ts +0 -22
  204. /package/dist/client/assets/{RoadmapsView-DdGlfuu-.css → dashboard-view-DdGlfuu-.css} +0 -0
@@ -0,0 +1,13 @@
1
+ {
2
+ "id": "fusion-plugin-droid-runtime",
3
+ "name": "Droid Runtime Plugin",
4
+ "version": "0.1.0",
5
+ "description": "Droid runtime plugin for Fusion",
6
+ "author": "Fusion Team",
7
+ "runtime": {
8
+ "runtimeId": "droid",
9
+ "name": "Droid Runtime",
10
+ "description": "Drives the Droid CLI for Fusion agents",
11
+ "version": "0.1.0"
12
+ }
13
+ }
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ // Schema-only MCP server. Reads tool schemas from a JSON file.
3
+ // Only implements initialize + tools/list. tools/call is never reached
4
+ // because the parent process kills the Claude subprocess at message_stop
5
+ // before tool execution (break-early pattern).
6
+ "use strict";
7
+
8
+ const fs = require("fs");
9
+ const readline = require("readline");
10
+
11
+ const schemaPath = process.argv[2];
12
+ if (!schemaPath) {
13
+ process.exit(1);
14
+ }
15
+
16
+ let tools = [];
17
+ try {
18
+ tools = JSON.parse(fs.readFileSync(schemaPath, "utf-8"));
19
+ } catch {
20
+ process.exit(1);
21
+ }
22
+
23
+ const rl = readline.createInterface({ input: process.stdin });
24
+ rl.on("line", (line) => {
25
+ let msg;
26
+ try {
27
+ msg = JSON.parse(line);
28
+ } catch {
29
+ return;
30
+ }
31
+
32
+ if (msg.method === "initialize") {
33
+ const resp = {
34
+ jsonrpc: "2.0",
35
+ id: msg.id,
36
+ result: {
37
+ protocolVersion: "2024-11-05",
38
+ capabilities: { tools: {} },
39
+ serverInfo: { name: "custom-tools", version: "1.0.0" },
40
+ },
41
+ };
42
+ process.stdout.write(JSON.stringify(resp) + "\n");
43
+ } else if (msg.method === "tools/list") {
44
+ const resp = { jsonrpc: "2.0", id: msg.id, result: { tools } };
45
+ process.stdout.write(JSON.stringify(resp) + "\n");
46
+ }
47
+ // notifications/initialized: no response needed (notification)
48
+ // tools/call: never reached (break-early kills subprocess first)
49
+ });
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@fusion-plugin-examples/droid-runtime",
3
+ "version": "0.1.8",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "import": "./bundled.js"
8
+ }
9
+ },
10
+ "private": true
11
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusion-plugin-examples/hermes-runtime",
3
- "version": "0.2.31",
3
+ "version": "0.2.32",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -6,6 +6,7 @@ function definePlugin(plugin2) {
6
6
  // ../../plugins/fusion-plugin-openclaw-runtime/dist/pi-module.js
7
7
  import { spawn } from "node:child_process";
8
8
  import { randomUUID } from "node:crypto";
9
+ import { readFile } from "node:fs/promises";
9
10
  var DEFAULT_BINARY = "openclaw";
10
11
  var DEFAULT_AGENT_ID = "main";
11
12
  var DEFAULT_THINKING = "off";
@@ -46,12 +47,16 @@ function resolveCliConfig(settings) {
46
47
  useGateway: asBool(settings?.useGateway) ?? asBool(process.env.OPENCLAW_USE_GATEWAY) ?? false
47
48
  };
48
49
  }
49
- function buildOpenClawArgs(config, sessionId, message) {
50
- const args = ["--no-color", "agent"];
50
+ function buildOpenClawArgs(config, session, message) {
51
+ const args = ["--no-color"];
52
+ if (session.mcpProfile) {
53
+ args.push("--profile", session.mcpProfile);
54
+ }
55
+ args.push("agent");
51
56
  if (!config.useGateway)
52
57
  args.push("--local");
53
58
  args.push("--json");
54
- args.push("--session-id", sessionId);
59
+ args.push("--session-id", session.sessionId);
55
60
  args.push("--message", message);
56
61
  args.push("--agent", config.agentId);
57
62
  if (config.model) {
@@ -78,11 +83,33 @@ function createCliSession(opts) {
78
83
  messages: [{ role: "developer", content: opts.systemPrompt }],
79
84
  lastModelDescription: `openclaw/${opts.agentId ?? DEFAULT_AGENT_ID}`,
80
85
  lastUsage: void 0,
81
- callbacks: opts.callbacks
86
+ callbacks: opts.callbacks,
87
+ mcpProfile: opts.mcpProfile,
88
+ mcpConfigPath: opts.mcpConfigPath
82
89
  };
83
90
  }
91
+ async function configureOpenClawMcpServer(opts) {
92
+ const serverValue = await readFile(opts.serverConfigPath, "utf-8");
93
+ await new Promise((resolve, reject) => {
94
+ const child = spawn(opts.binaryPath, ["--no-color", "--profile", opts.profile, "mcp", "set", opts.serverName, serverValue], {
95
+ stdio: ["ignore", "pipe", "pipe"]
96
+ });
97
+ let stderr = "";
98
+ child.stderr?.on("data", (chunk) => {
99
+ stderr += chunk.toString("utf-8");
100
+ });
101
+ child.on("error", (err) => reject(new Error(`openclaw: failed to configure MCP server \u2014 ${err.message}`)));
102
+ child.on("close", (code) => {
103
+ if (code === 0) {
104
+ resolve();
105
+ return;
106
+ }
107
+ reject(new Error(`openclaw: mcp set failed (${String(code)}): ${extractStderrError(stderr)}`));
108
+ });
109
+ });
110
+ }
84
111
  async function promptCli(session, message, config, callbacks, signal) {
85
- const args = buildOpenClawArgs(config, session.sessionId, message);
112
+ const args = buildOpenClawArgs(config, session, message);
86
113
  const cb = { ...session.callbacks, ...callbacks };
87
114
  cb.onToolStart?.("openclaw.agent", { sessionId: session.sessionId });
88
115
  return new Promise((resolve, reject) => {
@@ -186,7 +213,44 @@ function describeCliModel(session) {
186
213
  return session.lastModelDescription || `openclaw/${session.agentId}`;
187
214
  }
188
215
 
216
+ // ../../plugins/fusion-plugin-openclaw-runtime/dist/mcp-config.js
217
+ import { writeFileSync } from "node:fs";
218
+ import { tmpdir } from "node:os";
219
+ import { join } from "node:path";
220
+ import { fileURLToPath } from "node:url";
221
+ import { dirname } from "node:path";
222
+ var BUILT_IN_TOOL_NAMES = /* @__PURE__ */ new Set(["read", "write", "edit", "bash", "grep", "find"]);
223
+ function toolsToMcpToolDefs(tools) {
224
+ if (!Array.isArray(tools))
225
+ return [];
226
+ return tools.filter((tool) => tool && typeof tool.name === "string" && !BUILT_IN_TOOL_NAMES.has(tool.name)).map((tool) => ({
227
+ name: tool.name,
228
+ description: typeof tool.description === "string" ? tool.description : "",
229
+ inputSchema: tool.parameters ?? { type: "object", properties: {} }
230
+ }));
231
+ }
232
+ function writeOpenClawMcpBridgeFiles(toolDefs, cacheKey) {
233
+ const suffix = cacheKey ? `${process.pid}-${cacheKey}` : `${process.pid}`;
234
+ const schemaPath = join(tmpdir(), `openclaw-runtime-mcp-schemas-${suffix}.json`);
235
+ writeFileSync(schemaPath, JSON.stringify(toolDefs));
236
+ const __filename = fileURLToPath(import.meta.url);
237
+ const __dirname = dirname(__filename);
238
+ const serverPath = join(__dirname, "mcp-schema-server.cjs");
239
+ const serverConfig = {
240
+ command: "node",
241
+ args: [serverPath, schemaPath]
242
+ };
243
+ const serverConfigPath = join(tmpdir(), `openclaw-runtime-mcp-server-${suffix}.json`);
244
+ writeFileSync(serverConfigPath, JSON.stringify(serverConfig));
245
+ return {
246
+ schemaPath,
247
+ serverConfigPath,
248
+ serverName: "fusion-custom-tools"
249
+ };
250
+ }
251
+
189
252
  // ../../plugins/fusion-plugin-openclaw-runtime/dist/runtime-adapter.js
253
+ import { randomUUID as randomUUID2 } from "node:crypto";
190
254
  var OpenClawRuntimeAdapter = class {
191
255
  id = "openclaw";
192
256
  name = "OpenClaw Runtime";
@@ -195,9 +259,29 @@ var OpenClawRuntimeAdapter = class {
195
259
  this.config = resolveCliConfig(settings);
196
260
  }
197
261
  async createSession(options) {
262
+ const contextTools = [
263
+ ...Array.isArray(options.tools) ? options.tools : [],
264
+ ...Array.isArray(options.customTools) ? options.customTools : []
265
+ ];
266
+ const toolDefs = toolsToMcpToolDefs(contextTools);
267
+ let mcpProfile;
268
+ let mcpConfigPath;
269
+ if (toolDefs.length > 0) {
270
+ const bridge = writeOpenClawMcpBridgeFiles(toolDefs, randomUUID2());
271
+ mcpProfile = `fusion-${randomUUID2()}`;
272
+ mcpConfigPath = bridge.serverConfigPath;
273
+ await configureOpenClawMcpServer({
274
+ binaryPath: this.config.binaryPath,
275
+ profile: mcpProfile,
276
+ serverName: bridge.serverName,
277
+ serverConfigPath: bridge.serverConfigPath
278
+ });
279
+ }
198
280
  const session = createCliSession({
199
281
  systemPrompt: options.systemPrompt,
200
282
  agentId: this.config.agentId,
283
+ mcpProfile,
284
+ mcpConfigPath,
201
285
  callbacks: {
202
286
  onText: options.onText,
203
287
  onThinking: options.onThinking,
@@ -357,6 +441,7 @@ export {
357
441
  OPENCLAW_RUNTIME_ID,
358
442
  OpenClawRuntimeAdapter,
359
443
  buildOpenClawArgs,
444
+ configureOpenClawMcpServer,
360
445
  createCliSession,
361
446
  index_default as default,
362
447
  describeCliModel,
@@ -365,5 +450,7 @@ export {
365
450
  openclawRuntimeMetadata,
366
451
  probeOpenClawBinary,
367
452
  promptCli,
368
- resolveCliConfig
453
+ resolveCliConfig,
454
+ toolsToMcpToolDefs,
455
+ writeOpenClawMcpBridgeFiles
369
456
  };
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+ const readline = require("readline");
5
+
6
+ const schemaPath = process.argv[2];
7
+ if (!schemaPath) process.exit(1);
8
+
9
+ let tools = [];
10
+ try {
11
+ tools = JSON.parse(fs.readFileSync(schemaPath, "utf-8"));
12
+ } catch {
13
+ process.exit(1);
14
+ }
15
+
16
+ const rl = readline.createInterface({ input: process.stdin });
17
+ rl.on("line", (line) => {
18
+ let msg;
19
+ try {
20
+ msg = JSON.parse(line);
21
+ } catch {
22
+ return;
23
+ }
24
+
25
+ if (msg.method === "initialize") {
26
+ process.stdout.write(
27
+ JSON.stringify({
28
+ jsonrpc: "2.0",
29
+ id: msg.id,
30
+ result: {
31
+ protocolVersion: "2024-11-05",
32
+ capabilities: { tools: {} },
33
+ serverInfo: { name: "fusion-custom-tools", version: "1.0.0" },
34
+ },
35
+ }) + "\n",
36
+ );
37
+ return;
38
+ }
39
+
40
+ if (msg.method === "tools/list") {
41
+ process.stdout.write(
42
+ JSON.stringify({ jsonrpc: "2.0", id: msg.id, result: { tools } }) + "\n",
43
+ );
44
+ return;
45
+ }
46
+
47
+ if (msg.method === "tools/call") {
48
+ process.stdout.write(
49
+ JSON.stringify({
50
+ jsonrpc: "2.0",
51
+ id: msg.id,
52
+ result: {
53
+ content: [{ type: "text", text: "Tool execution is handled by Fusion runtime." }],
54
+ isError: true,
55
+ },
56
+ }) + "\n",
57
+ );
58
+ }
59
+ });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusion-plugin-examples/openclaw-runtime",
3
- "version": "0.2.31",
3
+ "version": "0.2.32",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusion-plugin-examples/paperclip-runtime",
3
- "version": "0.2.31",
3
+ "version": "0.2.32",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -0,0 +1,33 @@
1
+ {
2
+ "id": "fusion-plugin-reports",
3
+ "name": "Reports",
4
+ "version": "0.1.0",
5
+ "description": "Generates beautiful HTML system-activity reports with multi-agent review.",
6
+ "author": "Fusion Team",
7
+ "fusionVersion": ">=0.1.0",
8
+ "settingsSchema": {
9
+ "dailyEnabled": { "type": "boolean", "label": "Enable Daily Reports", "description": "Generate daily reports on schedule.", "group": "Schedules", "defaultValue": true },
10
+ "dailyCron": { "type": "string", "label": "Daily Schedule (cron)", "description": "Cron expression for daily report generation.", "group": "Schedules", "required": true, "defaultValue": "0 8 * * *" },
11
+ "weeklyEnabled": { "type": "boolean", "label": "Enable Weekly Reports", "description": "Generate weekly reports on schedule.", "group": "Schedules", "defaultValue": true },
12
+ "weeklyCron": { "type": "string", "label": "Weekly Schedule (cron)", "description": "Cron expression for weekly report generation.", "group": "Schedules", "required": true, "defaultValue": "0 8 * * 1" },
13
+ "timezone": { "type": "string", "label": "Timezone", "description": "IANA timezone used for schedule evaluation.", "group": "Schedules", "required": true, "defaultValue": "UTC" },
14
+ "generationPromptDaily": { "type": "string", "multiline": true, "label": "Daily Generation Prompt", "description": "Prompt template used to generate daily reports.", "group": "Prompts", "required": true, "defaultValue": "Summarize today's system activity with clear wins, highlights, lowlights, and proposals." },
15
+ "generationPromptWeekly": { "type": "string", "multiline": true, "label": "Weekly Generation Prompt", "description": "Prompt template used to generate weekly reports.", "group": "Prompts", "required": true, "defaultValue": "Summarize this week's system activity trends, major outcomes, and actionable proposals." },
16
+ "reviewPrompt": { "type": "string", "multiline": true, "label": "Review Prompt", "description": "Prompt template used by reviewer agents.", "group": "Prompts", "required": true, "defaultValue": "Review this report for factual accuracy, clarity, and actionability. Provide concrete revision suggestions." },
17
+ "reviewPanelAgentIds": { "type": "array", "itemType": "string", "label": "Reviewer Agent IDs", "description": "Agent IDs allowed to review generated reports.", "group": "Review Panel", "defaultValue": [] },
18
+ "reviewMinApprovals": { "type": "number", "label": "Minimum Approvals", "description": "How many approvals are required from the review panel.", "group": "Review Panel", "defaultValue": 1 },
19
+ "reviewBlockingMode": { "type": "enum", "enumValues": ["block", "advisory"], "label": "Review Blocking Mode", "description": "Choose whether failed review blocks publication.", "group": "Review Panel", "defaultValue": "advisory" },
20
+ "sectionOrder": { "type": "array", "itemType": "string", "label": "Section Order", "description": "Ordered section IDs for report rendering.", "group": "Sections", "defaultValue": ["wins", "highlights", "lowlights", "proposals", "deep-dives", "per-agent"] },
21
+ "enabledSections": { "type": "array", "itemType": "string", "label": "Enabled Sections", "description": "Section IDs enabled for output.", "group": "Sections", "defaultValue": ["wins", "highlights", "lowlights", "proposals", "deep-dives", "per-agent"] },
22
+ "includeSystemSummary": { "type": "boolean", "label": "Include System Summary", "description": "Include top-level system summary in report output.", "group": "Sections", "defaultValue": true },
23
+ "includePerAgentSections": { "type": "boolean", "label": "Include Per-Agent Sections", "description": "Include per-agent detail sections.", "group": "Sections", "defaultValue": true },
24
+ "brandTitle": { "type": "string", "label": "Report Title", "description": "Primary report title for branding.", "group": "Branding", "required": true, "defaultValue": "Fusion Activity Report" },
25
+ "brandLogoUrl": { "type": "string", "label": "Logo URL", "description": "Optional URL to a brand logo image.", "group": "Branding" },
26
+ "themeMode": { "type": "enum", "enumValues": ["light", "dark", "auto"], "label": "Theme Mode", "description": "Theme style for generated reports.", "group": "Branding", "defaultValue": "auto" },
27
+ "accentColor": { "type": "string", "label": "Accent Color", "description": "Accent color used in report styling.", "group": "Branding", "defaultValue": "#5B8DEF" },
28
+ "approvalRequired": { "type": "boolean", "label": "Require Approval", "description": "Require explicit approval before publishing.", "group": "Approval Flow", "defaultValue": false },
29
+ "autoPublishOnApproval": { "type": "boolean", "label": "Auto Publish on Approval", "description": "Publish automatically after approval threshold is met.", "group": "Approval Flow", "defaultValue": true },
30
+ "approverAgentIds": { "type": "array", "itemType": "string", "label": "Approver Agent IDs", "description": "Agent IDs permitted to approve final publish.", "group": "Approval Flow", "defaultValue": [] },
31
+ "publishTargets": { "type": "array", "itemType": "string", "label": "Publish Targets", "description": "Destinations to publish reports to (for example dashboard, html-export).", "group": "Approval Flow", "defaultValue": [] }
32
+ }
33
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@fusion-plugin-examples/reports",
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "description": "Reports plugin for Fusion",
6
+ "private": true,
7
+ "exports": {
8
+ ".": {
9
+ "types": "./src/index.ts",
10
+ "import": "./src/index.ts"
11
+ }
12
+ },
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "test": "vitest run --silent=passed-only --reporter=dot"
16
+ },
17
+ "dependencies": {
18
+ "@fusion/core": "workspace:*",
19
+ "@fusion/plugin-sdk": "workspace:*"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^25.5.2",
23
+ "typescript": "^5.7.0",
24
+ "vitest": "^3.2.4"
25
+ }
26
+ }
@@ -0,0 +1,51 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import manifest from "../../manifest.json";
3
+ import plugin from "../index.js";
4
+
5
+ const expectedKeys = [
6
+ "dailyEnabled",
7
+ "dailyCron",
8
+ "weeklyEnabled",
9
+ "weeklyCron",
10
+ "timezone",
11
+ "generationPromptDaily",
12
+ "generationPromptWeekly",
13
+ "reviewPrompt",
14
+ "reviewPanelAgentIds",
15
+ "reviewMinApprovals",
16
+ "reviewBlockingMode",
17
+ "sectionOrder",
18
+ "enabledSections",
19
+ "includeSystemSummary",
20
+ "includePerAgentSections",
21
+ "brandTitle",
22
+ "brandLogoUrl",
23
+ "themeMode",
24
+ "accentColor",
25
+ "approvalRequired",
26
+ "autoPublishOnApproval",
27
+ "approverAgentIds",
28
+ "publishTargets",
29
+ ] as const;
30
+
31
+ describe("reports plugin manifest", () => {
32
+ it("exports expected plugin id", () => {
33
+ expect(plugin.manifest.id).toBe("fusion-plugin-reports");
34
+ });
35
+
36
+ it("keeps runtime manifest metadata aligned with manifest.json", () => {
37
+ expect(plugin.manifest.id).toBe(manifest.id);
38
+ expect(plugin.manifest.name).toBe(manifest.name);
39
+ expect(plugin.manifest.version).toBe(manifest.version);
40
+ expect(plugin.manifest.description).toBe(manifest.description);
41
+ expect(plugin.manifest.author).toBe(manifest.author);
42
+ expect(plugin.manifest.fusionVersion).toBe(manifest.fusionVersion);
43
+ });
44
+
45
+ it("includes full settings schema", () => {
46
+ expect(plugin.manifest.settingsSchema).toBeDefined();
47
+ for (const key of expectedKeys) {
48
+ expect(plugin.manifest.settingsSchema).toHaveProperty(key);
49
+ }
50
+ });
51
+ });
@@ -0,0 +1,166 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+ import type { CreateAiSessionFactory, PluginContext } from "@fusion/core";
3
+ import { __setCreateAiSessionFactory, combineReviews, runReviewPanel } from "../review-panel.js";
4
+ import type { ReviewPanelMember } from "../review-types.js";
5
+ import { ReviewPanelError } from "../review-types.js";
6
+
7
+ function createContext(createAiSession?: CreateAiSessionFactory): PluginContext {
8
+ return {
9
+ pluginId: "fusion-plugin-reports",
10
+ taskStore: {} as PluginContext["taskStore"],
11
+ settings: {},
12
+ logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() },
13
+ emitEvent: vi.fn(),
14
+ createAiSession,
15
+ };
16
+ }
17
+
18
+ const panel: ReviewPanelMember[] = [
19
+ { id: "qa", name: "QA", perspective: "Quality" },
20
+ { id: "ops", name: "Ops", perspective: "Operations" },
21
+ { id: "pm", name: "PM", perspective: "Product" },
22
+ ];
23
+
24
+ afterEach(() => {
25
+ __setCreateAiSessionFactory(undefined);
26
+ vi.restoreAllMocks();
27
+ vi.useRealTimers();
28
+ });
29
+
30
+ describe("runReviewPanel", () => {
31
+ it("returns individual and combined reviews for a 3-member panel", async () => {
32
+ const createAiSession = vi.fn(async ({ systemPrompt }) => ({
33
+ session: {
34
+ prompt: vi.fn(async () => {}),
35
+ state: {
36
+ messages: [{ role: "assistant", content: JSON.stringify({ verdict: "approve", summary: systemPrompt, highlights: ["A"], lowlights: ["B"], suggestions: ["C"] }) }],
37
+ },
38
+ dispose: vi.fn(),
39
+ },
40
+ }));
41
+
42
+ const result = await runReviewPanel({
43
+ reportDraft: "draft",
44
+ reportMetadata: { reportId: "r-1", cadence: "daily", periodStart: "2026-01-01", periodEnd: "2026-01-02" },
45
+ panel,
46
+ cwd: "/tmp",
47
+ }, createContext(createAiSession));
48
+
49
+ expect(result.individual).toHaveLength(3);
50
+ expect(result.failures).toEqual([]);
51
+ expect(result.overallVerdict).toBe("approve");
52
+ expect(result.consensusSummary).toContain("Quality:");
53
+ });
54
+
55
+ it("deduplicates merged arrays in first-seen order", () => {
56
+ const merged = combineReviews([
57
+ { memberId: "a", memberName: "A", perspective: "P1", verdict: "approve", summary: "ok", highlights: [" Alpha ", "Beta"], lowlights: ["Lag"], suggestions: ["Fix docs"], rawText: "", durationMs: 1 },
58
+ { memberId: "b", memberName: "B", perspective: "P2", verdict: "approve", summary: "ok", highlights: ["alpha", "Gamma"], lowlights: [" lag "], suggestions: ["fix docs", "Add tests"], rawText: "", durationMs: 1 },
59
+ ], []);
60
+
61
+ expect(merged.mergedHighlights).toEqual(["Alpha", "Beta", "Gamma"]);
62
+ expect(merged.mergedLowlights).toEqual(["Lag"]);
63
+ expect(merged.mergedSuggestions).toEqual(["Fix docs", "Add tests"]);
64
+ });
65
+
66
+ it("applies verdict precedence", () => {
67
+ const revise = combineReviews([
68
+ { memberId: "a", memberName: "A", perspective: "P1", verdict: "approve", summary: "ok", highlights: [], lowlights: [], suggestions: [], rawText: "", durationMs: 1 },
69
+ { memberId: "b", memberName: "B", perspective: "P2", verdict: "revise", summary: "rev", highlights: [], lowlights: [], suggestions: [], rawText: "", durationMs: 1 },
70
+ ], []);
71
+ expect(revise.overallVerdict).toBe("revise");
72
+
73
+ const reject = combineReviews([
74
+ { memberId: "a", memberName: "A", perspective: "P1", verdict: "approve", summary: "ok", highlights: [], lowlights: [], suggestions: [], rawText: "", durationMs: 1 },
75
+ { memberId: "b", memberName: "B", perspective: "P2", verdict: "reject", summary: "no", highlights: [], lowlights: [], suggestions: [], rawText: "", durationMs: 1 },
76
+ ], []);
77
+ expect(reject.overallVerdict).toBe("reject");
78
+ });
79
+
80
+ it("records timeout failure without aborting other reviewers", async () => {
81
+ vi.useFakeTimers();
82
+ const createAiSession = vi.fn(async ({ systemPrompt }) => {
83
+ if (systemPrompt.includes("Operations")) {
84
+ return {
85
+ session: {
86
+ prompt: vi.fn(async () => new Promise(() => {})),
87
+ state: { messages: [] },
88
+ dispose: vi.fn(),
89
+ },
90
+ };
91
+ }
92
+ return {
93
+ session: {
94
+ prompt: vi.fn(async () => {}),
95
+ state: { messages: [{ role: "assistant", content: JSON.stringify({ verdict: "approve", summary: "ok", highlights: [], lowlights: [], suggestions: [] }) }] },
96
+ dispose: vi.fn(),
97
+ },
98
+ };
99
+ });
100
+
101
+ const promise = runReviewPanel({
102
+ reportDraft: "draft",
103
+ reportMetadata: { reportId: "r-1", cadence: "daily", periodStart: "2026-01-01", periodEnd: "2026-01-02" },
104
+ panel,
105
+ cwd: "/tmp",
106
+ }, createContext(createAiSession));
107
+
108
+ await vi.advanceTimersByTimeAsync(120_000);
109
+ const result = await promise;
110
+
111
+ expect(result.individual).toHaveLength(2);
112
+ expect(result.failures).toHaveLength(1);
113
+ expect(result.failures[0]?.reason).toBe("timeout");
114
+ });
115
+
116
+ it("retries malformed JSON once then reports parse_error", async () => {
117
+ const prompt = vi.fn(async () => {});
118
+ const createAiSession = vi.fn(async () => ({
119
+ session: {
120
+ prompt,
121
+ state: { messages: [{ role: "assistant", content: "not-json" }] },
122
+ dispose: vi.fn(),
123
+ },
124
+ }));
125
+
126
+ const result = await runReviewPanel({
127
+ reportDraft: "draft",
128
+ reportMetadata: { reportId: "r-1", cadence: "daily", periodStart: "2026-01-01", periodEnd: "2026-01-02" },
129
+ panel: [panel[0]],
130
+ cwd: "/tmp",
131
+ }, createContext(createAiSession));
132
+
133
+ expect(prompt).toHaveBeenCalledTimes(2);
134
+ expect(result.individual).toEqual([]);
135
+ expect(result.failures).toHaveLength(1);
136
+ expect(result.failures[0]?.reason).toBe("parse_error");
137
+ });
138
+
139
+ it("throws ReviewPanelError when createAiSession is unavailable", async () => {
140
+ await expect(runReviewPanel({
141
+ reportDraft: "draft",
142
+ reportMetadata: { reportId: "r-1", cadence: "daily", periodStart: "2026-01-01", periodEnd: "2026-01-02" },
143
+ panel,
144
+ cwd: "/tmp",
145
+ }, createContext(undefined))).rejects.toBeInstanceOf(ReviewPanelError);
146
+ });
147
+
148
+ it("forwards provider/modelId to createAiSession per member", async () => {
149
+ const createAiSession = vi.fn(async () => ({
150
+ session: {
151
+ prompt: vi.fn(async () => {}),
152
+ state: { messages: [{ role: "assistant", content: JSON.stringify({ verdict: "approve", summary: "ok", highlights: [], lowlights: [], suggestions: [] }) }] },
153
+ dispose: vi.fn(),
154
+ },
155
+ }));
156
+
157
+ await runReviewPanel({
158
+ reportDraft: "draft",
159
+ reportMetadata: { reportId: "r-1", cadence: "daily", periodStart: "2026-01-01", periodEnd: "2026-01-02" },
160
+ panel: [{ id: "m1", name: "M1", perspective: "P", provider: "anthropic", modelId: "claude" }],
161
+ cwd: "/tmp",
162
+ }, createContext(createAiSession));
163
+
164
+ expect(createAiSession).toHaveBeenCalledWith(expect.objectContaining({ defaultProvider: "anthropic", defaultModelId: "claude" }));
165
+ });
166
+ });