@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.
- package/dist/bin.js +26610 -20597
- package/dist/client/assets/AgentDetailView-BwJaLqZh.css +1 -0
- package/dist/client/assets/AgentDetailView-gy_5SUj2.js +18 -0
- package/dist/client/assets/AgentsView-BkB9FiMT.js +29 -0
- package/dist/client/assets/{AgentsView-DSGQWObq.css → AgentsView-CV3vm7Qk.css} +1 -1
- package/dist/client/assets/ChatView-B_-B8fqu.js +1 -0
- package/dist/client/assets/ChatView-DwJAd5G1.css +1 -0
- package/dist/client/assets/{DevServerView-C9lzHrcT.js → DevServerView-BkvtjZBa.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-aVdFaV37.js → DirectoryPicker-BK-KbnhP.js} +1 -1
- package/dist/client/assets/{DocumentsView-DIpg3NSP.js → DocumentsView-BEg1CQAk.js} +1 -1
- package/dist/client/assets/{DocumentsView-BrhyOdeE.css → DocumentsView-gv4zG3aT.css} +1 -1
- package/dist/client/assets/EvalsView-Berf9bQm.js +1 -0
- package/dist/client/assets/EvalsView-CUNJ1TLc.css +1 -0
- package/dist/client/assets/{agentSkills-DDHJnrkn.css → ExperimentalAgentOnboardingModal-B-APN_lM.css} +1 -1
- package/dist/client/assets/ExperimentalAgentOnboardingModal-jcInE50G.js +499 -0
- package/dist/client/assets/InsightsView-B0J4mhzV.css +1 -0
- package/dist/client/assets/InsightsView-BX5bSF1J.js +11 -0
- package/dist/client/assets/{MemoryView-nXlTqebk.js → MemoryView-CKElJY_3.js} +2 -2
- package/dist/client/assets/NodesView-DLUOBLf6.js +14 -0
- package/dist/client/assets/NodesView-DT4pXowv.css +1 -0
- package/dist/client/assets/{PiExtensionsManager-Buopv-jb.js → PiExtensionsManager-COlJf0Kx.js} +2 -2
- package/dist/client/assets/PluginManager-CfW55BF4.js +1 -0
- package/dist/client/assets/PluginManager-DtRQXia5.css +1 -0
- package/dist/client/assets/{ResearchView-_BHXUv2j.js → ResearchView-B256Lr8I.js} +1 -1
- package/dist/client/assets/SettingsModal-BeA_nQtW.js +31 -0
- package/dist/client/assets/SettingsModal-DzsLquBu.css +1 -0
- package/dist/client/assets/{SettingsModal-C89Ikhfm.js → SettingsModal-yRqM4DV8.js} +1 -1
- package/dist/client/assets/SetupWizardModal-uUZk3TKT.js +1 -0
- package/dist/client/assets/{SkillsView-hDpTBdFT.js → SkillsView-CP8JX0P_.js} +1 -1
- package/dist/client/assets/TodoView-Cx9cVhq7.css +1 -0
- package/dist/client/assets/TodoView-DCRIkDZ-.js +6 -0
- package/dist/client/assets/createLucideIcon-BazL2hk5.js +21 -0
- package/dist/client/assets/dashboard-view-BkTMSZYn.css +1 -0
- package/dist/client/assets/dashboard-view-CyWN-d02.js +63 -0
- package/dist/client/assets/dashboard-view-lR7YYmSC.js +21 -0
- package/dist/client/assets/{folder-open-usZkXdq2.js → folder-open-DHjELt8-.js} +1 -1
- package/dist/client/assets/index-CQyVRLOb.js +692 -0
- package/dist/client/assets/index-CxA2Nn0_.css +1 -0
- package/dist/client/assets/projectDetection-G3XuxD2X.js +1 -0
- package/dist/client/assets/{star-BAT_ObKE.js → star-DYesq1AV.js} +1 -1
- package/dist/client/assets/{upload-BC2YKNEV.js → upload-DTWF3Db5.js} +1 -1
- package/dist/client/assets/{users-Dkd4rtrN.js → users--syrel4l.js} +1 -1
- package/dist/client/index.html +12 -20
- package/dist/client/theme-data.css +106 -0
- package/dist/client/version.json +1 -1
- package/dist/droid-cli/package.json +1 -1
- package/dist/extension.js +14287 -9568
- package/dist/pi-claude-cli/package.json +1 -1
- package/dist/plugins/fusion-plugin-cursor-runtime/bundled.js +218 -0
- package/dist/plugins/fusion-plugin-cursor-runtime/manifest.json +6 -0
- package/dist/plugins/fusion-plugin-cursor-runtime/package.json +11 -0
- package/dist/plugins/fusion-plugin-dependency-graph/manifest.json +1 -1
- package/dist/plugins/fusion-plugin-dependency-graph/package.json +6 -4
- package/dist/plugins/fusion-plugin-dependency-graph/src/DependencyGraph.css +58 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/DependencyGraph.tsx +301 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/GraphHighlight.css +27 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/GraphTaskNode.css +157 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/GraphTaskNode.tsx +126 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/GraphToolbar.css +35 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/GraphToolbar.tsx +36 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/DependencyGraph.highlighting.test.tsx +112 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/DependencyGraph.persistence.test.tsx +115 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/DependencyGraph.test.tsx +128 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphTaskNode.drag.test.tsx +82 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphTaskNode.test.tsx +307 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphToolbar.test.tsx +60 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/edges.test.tsx +75 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/filtering.test.tsx +62 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/filters.test.ts +78 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/graphPositionStorage.test.ts +95 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/host-integration.test.ts +74 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/index.test.ts +58 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/interactions.test.tsx +121 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/layout.test.ts +70 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/persistence.test.tsx +89 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/useGraphData.test.ts +86 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/useGraphInteraction.test.ts +167 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/useGraphPositions.test.ts +66 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/useNodeDrag.test.ts +81 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/dashboard-interop.d.ts +35 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/dashboard-view.tsx +19 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/edges.tsx +70 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/filters.ts +8 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/hooks/__tests__/useDependencyChain.test.ts +53 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/hooks/useDependencyChain.ts +60 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/hooks/useGraphPositions.ts +45 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/hooks/useNodeDrag.ts +114 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/index.ts +1 -2
- package/dist/plugins/fusion-plugin-dependency-graph/src/layout.ts +91 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/styles/drag.css +15 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/types.ts +21 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/useGraphData.ts +17 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/useGraphInteraction.ts +292 -0
- package/dist/plugins/fusion-plugin-dependency-graph/src/utils/graphPositionStorage.ts +65 -0
- package/dist/plugins/fusion-plugin-droid-runtime/bundled.js +136680 -0
- package/dist/plugins/fusion-plugin-droid-runtime/manifest.json +13 -0
- package/dist/plugins/fusion-plugin-droid-runtime/mcp-schema-server.cjs +49 -0
- package/dist/plugins/fusion-plugin-droid-runtime/package.json +11 -0
- package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-openclaw-runtime/bundled.js +93 -6
- package/dist/plugins/fusion-plugin-openclaw-runtime/mcp-schema-server.cjs +59 -0
- package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-reports/manifest.json +33 -0
- package/dist/plugins/fusion-plugin-reports/package.json +26 -0
- package/dist/plugins/fusion-plugin-reports/src/__tests__/manifest.test.ts +51 -0
- package/dist/plugins/fusion-plugin-reports/src/__tests__/review-panel.test.ts +166 -0
- package/dist/plugins/fusion-plugin-reports/src/__tests__/settings.test.ts +157 -0
- package/dist/plugins/fusion-plugin-reports/src/index.ts +41 -0
- package/dist/plugins/fusion-plugin-reports/src/review-panel.ts +294 -0
- package/dist/plugins/fusion-plugin-reports/src/review-types.ts +75 -0
- package/dist/plugins/fusion-plugin-reports/src/settings.ts +105 -0
- package/dist/plugins/fusion-plugin-roadmap/manifest.json +16 -0
- package/dist/plugins/fusion-plugin-roadmap/package.json +48 -0
- package/dist/plugins/fusion-plugin-roadmap/src/__tests__/api-client.test.ts +101 -0
- package/dist/plugins/fusion-plugin-roadmap/src/__tests__/index.test.ts +92 -0
- package/dist/plugins/fusion-plugin-roadmap/src/__tests__/roadmap-routes.test.ts +48 -0
- package/dist/plugins/fusion-plugin-roadmap/src/__tests__/roadmap-suggestions.test.ts +31 -0
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard/RoadmapsView.css +1299 -0
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard/RoadmapsView.tsx +2559 -0
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard/__tests__/RoadmapsView.test.tsx +1144 -0
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard/__tests__/useRoadmaps.test.ts +1756 -0
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard/api.ts +70 -0
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard/test-setup.ts +7 -0
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard/types.ts +1 -0
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard/useConfirm.ts +8 -0
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard/useRoadmaps.ts +1188 -0
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard/useViewportMode.ts +20 -0
- package/dist/plugins/fusion-plugin-roadmap/src/dashboard-view.tsx +6 -0
- package/dist/plugins/fusion-plugin-roadmap/src/index.ts +74 -0
- package/dist/plugins/fusion-plugin-roadmap/src/roadmap-routes.ts +1 -0
- package/dist/plugins/fusion-plugin-roadmap/src/roadmap-schema.ts +41 -0
- package/dist/plugins/fusion-plugin-roadmap/src/roadmap-suggestions.d.ts +15 -0
- package/dist/plugins/fusion-plugin-roadmap/src/roadmap-suggestions.ts +15 -0
- package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.d.ts +283 -0
- package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.d.ts.map +1 -0
- package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.js +21 -0
- package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.js.map +1 -0
- package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.ts +310 -0
- package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.d.ts +5 -0
- package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.d.ts.map +1 -0
- package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.js +361 -0
- package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.js.map +1 -0
- package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.ts +408 -0
- package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.d.ts +68 -0
- package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.d.ts.map +1 -0
- package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.js +300 -0
- package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.js.map +1 -0
- package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.ts +381 -0
- package/dist/plugins/fusion-plugin-roadmap/src/server/index.d.ts +3 -0
- package/dist/plugins/fusion-plugin-roadmap/src/server/index.ts +1 -0
- package/dist/plugins/fusion-plugin-roadmap/src/store/__tests__/roadmap-handoff.test.ts +445 -0
- package/dist/plugins/fusion-plugin-roadmap/src/store/__tests__/roadmap-ordering.test.ts +334 -0
- package/dist/plugins/fusion-plugin-roadmap/src/store/__tests__/roadmap-store.test.ts +1318 -0
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-handoff.ts +163 -0
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.d.ts +37 -0
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.d.ts.map +1 -0
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.js +188 -0
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.js.map +1 -0
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.ts +311 -0
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.d.ts +299 -0
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.d.ts.map +1 -0
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.js +765 -0
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.js.map +1 -0
- package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.ts +1001 -0
- package/dist/plugins/fusion-plugin-whatsapp-chat/manifest.json +8 -0
- package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +34 -0
- package/dist/plugins/fusion-plugin-whatsapp-chat/src/__tests__/auth-state.test.ts +99 -0
- package/dist/plugins/fusion-plugin-whatsapp-chat/src/__tests__/connection.test.ts +145 -0
- package/dist/plugins/fusion-plugin-whatsapp-chat/src/__tests__/index.test.ts +216 -0
- package/dist/plugins/fusion-plugin-whatsapp-chat/src/__tests__/reply.test.ts +52 -0
- package/dist/plugins/fusion-plugin-whatsapp-chat/src/auth-state.ts +89 -0
- package/dist/plugins/fusion-plugin-whatsapp-chat/src/connection.ts +253 -0
- package/dist/plugins/fusion-plugin-whatsapp-chat/src/index.ts +262 -0
- package/dist/plugins/fusion-plugin-whatsapp-chat/src/qrcode.d.ts +1 -0
- package/dist/plugins/fusion-plugin-whatsapp-chat/src/reply.ts +37 -0
- package/package.json +2 -2
- package/skill/fusion/SKILL.md +2 -2
- package/skill/fusion/references/engine-tools.md +3 -0
- package/skill/fusion/references/extension-tools.md +39 -0
- package/skill/fusion/references/fusion-capabilities.md +3 -0
- package/dist/client/assets/AgentDetailView-C1XceMgi.js +0 -18
- package/dist/client/assets/AgentDetailView-CeO_1MK7.css +0 -1
- package/dist/client/assets/AgentsView-Deh125ss.js +0 -527
- package/dist/client/assets/ChatView-7D_RQDqT.js +0 -1
- package/dist/client/assets/InsightsView-AWo5o_81.css +0 -1
- package/dist/client/assets/InsightsView-jKjEFAx_.js +0 -11
- package/dist/client/assets/NodesView-Di2SvOhg.js +0 -14
- package/dist/client/assets/NodesView-fXqDk9ur.css +0 -1
- package/dist/client/assets/PluginManager-B9-NbQ8f.js +0 -1
- package/dist/client/assets/PluginManager-C1DbPaar.css +0 -1
- package/dist/client/assets/RoadmapsView-DHWjUoc8.js +0 -6
- package/dist/client/assets/SettingsModal-DHitIpsa.css +0 -1
- package/dist/client/assets/SettingsModal-DR_yirvK.js +0 -31
- package/dist/client/assets/SetupWizardModal-BtDMY9pa.js +0 -1
- package/dist/client/assets/agentSkills-B-w5wFHh.js +0 -1
- package/dist/client/assets/index-Bc6ZdGMz.css +0 -1
- package/dist/client/assets/index-D__RMku8.js +0 -694
- package/dist/plugins/fusion-plugin-dependency-graph/src/DependencyGraphView.css +0 -141
- package/dist/plugins/fusion-plugin-dependency-graph/src/DependencyGraphView.tsx +0 -428
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/DependencyGraphView.test.tsx +0 -261
- package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/storage.test.ts +0 -41
- package/dist/plugins/fusion-plugin-dependency-graph/src/storage.ts +0 -22
- /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
|
+
});
|
|
@@ -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,
|
|
50
|
-
const args = ["--no-color"
|
|
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
|
|
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
|
+
});
|
|
@@ -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
|
+
});
|