@synergenius/flow-weaver-pack-weaver 0.9.14 → 0.9.15

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.
@@ -26,71 +26,144 @@ __export(evolution_panel_exports, {
26
26
  module.exports = __toCommonJS(evolution_panel_exports);
27
27
  var import_react = require("react");
28
28
  var import_jsx_runtime = require("react/jsx-runtime");
29
- function WeaverEvolutionPanel({ projectDir }) {
30
- const [data, setData] = (0, import_react.useState)(null);
29
+ function WeaverEvolutionPanel() {
30
+ const [health, setHealth] = (0, import_react.useState)(null);
31
+ const [improve, setImprove] = (0, import_react.useState)(null);
31
32
  const [error, setError] = (0, import_react.useState)(null);
33
+ const [loading, setLoading] = (0, import_react.useState)(true);
34
+ const [deviceId, setDeviceId] = (0, import_react.useState)(null);
35
+ const [deviceName, setDeviceName] = (0, import_react.useState)("");
36
+ const [improving, setImproving] = (0, import_react.useState)(false);
37
+ const fetchData = (0, import_react.useCallback)(async () => {
38
+ try {
39
+ const devRes = await fetch("/api/devices", { credentials: "include" });
40
+ if (!devRes.ok) {
41
+ setError("Not connected");
42
+ setLoading(false);
43
+ return;
44
+ }
45
+ const devices = await devRes.json();
46
+ const device = devices.find((d) => d.capabilities?.includes("improve"));
47
+ if (!device) {
48
+ setError("No device with improve capability");
49
+ setLoading(false);
50
+ return;
51
+ }
52
+ setDeviceId(device.id);
53
+ setDeviceName(device.name);
54
+ const [healthRes, improveRes] = await Promise.allSettled([
55
+ fetch(`/api/devices/${device.id}/health`, { credentials: "include" }),
56
+ fetch(`/api/devices/${device.id}/improve/status`, { credentials: "include" })
57
+ ]);
58
+ if (healthRes.status === "fulfilled" && healthRes.value.ok) {
59
+ setHealth(await healthRes.value.json());
60
+ }
61
+ if (improveRes.status === "fulfilled" && improveRes.value.ok) {
62
+ const data = await improveRes.value.json();
63
+ setImprove(data);
64
+ setImproving(data.running ?? false);
65
+ }
66
+ setError(null);
67
+ } catch (e) {
68
+ setError(e instanceof Error ? e.message : "Failed to load");
69
+ } finally {
70
+ setLoading(false);
71
+ }
72
+ }, []);
32
73
  (0, import_react.useEffect)(() => {
33
- if (!projectDir) return;
34
- fetch(`/api/mcp/fw_weaver_insights?projectDir=${encodeURIComponent(projectDir)}`).then((r) => r.json()).then(setData).catch((e) => setError(e.message));
35
- }, [projectDir]);
36
- if (error) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16, color: "#ef4444" }, children: [
37
- "Error: ",
38
- error
39
- ] });
40
- if (!data) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: 16, opacity: 0.5 }, children: "Loading evolution data..." });
41
- const { evolution, trust } = data;
42
- const outcomeColor = {
43
- applied: "#22c55e",
44
- "rolled-back": "#ef4444",
45
- rejected: "#f59e0b",
46
- "no-change": "#6b7280",
47
- error: "#ef4444"
74
+ fetchData();
75
+ const interval = setInterval(fetchData, 15e3);
76
+ return () => clearInterval(interval);
77
+ }, [fetchData]);
78
+ const startImprove = async () => {
79
+ if (!deviceId) return;
80
+ setImproving(true);
81
+ try {
82
+ await fetch(`/api/devices/${deviceId}/improve`, {
83
+ method: "POST",
84
+ headers: { "Content-Type": "application/json" },
85
+ credentials: "include",
86
+ body: JSON.stringify({ maxCycles: 5 })
87
+ });
88
+ } catch {
89
+ }
48
90
  };
49
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16, fontFamily: "system-ui, sans-serif", fontSize: 13 }, children: [
50
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: 16 }, children: [
51
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontWeight: 600, marginBottom: 4 }, children: "Trust Level" }),
52
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "baseline", gap: 8 }, children: [
53
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { fontSize: 20, fontWeight: 700 }, children: [
54
- "Phase ",
55
- trust.phase
91
+ if (loading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: 16, opacity: 0.5 }, children: "Loading..." });
92
+ if (error) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16 }, children: [
93
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { color: "#ef4444", marginBottom: 8 }, children: error }),
94
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: fetchData, style: { fontSize: 12, color: "#6b7280", background: "none", border: "1px solid #333", borderRadius: 4, padding: "4px 8px", cursor: "pointer" }, children: "Retry" })
95
+ ] });
96
+ const trustPhase = health?.trust?.phase ?? 1;
97
+ const trustScore = health?.trust?.score ?? 0;
98
+ const healthScore = health?.health?.overall ?? 0;
99
+ const outcomeIcon = { success: "\u2713", failure: "\u2717" };
100
+ const outcomeColor = { success: "#22c55e", failure: "#ef4444" };
101
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16, fontFamily: "system-ui, sans-serif", fontSize: 13, color: "var(--color-text-high, #e5e5e5)" }, children: [
102
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 10, textTransform: "uppercase", letterSpacing: "0.05em", opacity: 0.5, marginBottom: 12 }, children: deviceName }),
103
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: 16, marginBottom: 16 }, children: [
104
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1, padding: 12, border: "1px solid rgba(128,128,128,0.2)", borderRadius: 6 }, children: [
105
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { fontSize: 20, fontWeight: 700 }, children: [
106
+ "P",
107
+ trustPhase
56
108
  ] }),
57
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { opacity: 0.5 }, children: [
58
- "Score: ",
59
- trust.score,
109
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { fontSize: 10, opacity: 0.5, textTransform: "uppercase", marginTop: 2 }, children: [
110
+ "Trust \xB7 ",
111
+ trustScore,
60
112
  "/100"
61
113
  ] })
114
+ ] }),
115
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1, padding: 12, border: "1px solid rgba(128,128,128,0.2)", borderRadius: 6 }, children: [
116
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 20, fontWeight: 700 }, children: healthScore }),
117
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 10, opacity: 0.5, textTransform: "uppercase", marginTop: 2 }, children: "Health" })
62
118
  ] })
63
119
  ] }),
64
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginBottom: 16 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { fontWeight: 600, marginBottom: 4 }, children: [
65
- "Genesis Cycles: ",
66
- evolution.totalCycles,
67
- " (",
68
- evolution.totalCycles > 0 ? Math.round(evolution.successRate * 100) : 0,
69
- "% success)"
70
- ] }) }),
71
- Object.keys(evolution.byOperationType).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: 16 }, children: [
72
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontWeight: 600, marginBottom: 4 }, children: "Operation Effectiveness" }),
73
- Object.entries(evolution.byOperationType).map(([op, stats]) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: "2px 0", display: "flex", justifyContent: "space-between" }, children: [
74
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: op }),
75
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { opacity: 0.7 }, children: [
76
- Math.round(stats.effectiveness * 100),
77
- "% (",
78
- stats.applied,
79
- "/",
80
- stats.proposed,
81
- ")"
82
- ] })
83
- ] }, op))
84
- ] }),
85
- evolution.recentCycles.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
86
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontWeight: 600, marginBottom: 4 }, children: "Recent Cycles" }),
87
- evolution.recentCycles.slice(-5).reverse().map((cycle) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: "4px 0", borderBottom: "1px solid rgba(128,128,128,0.1)" }, children: [
88
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: outcomeColor[cycle.outcome] ?? "#6b7280", fontWeight: 600, marginRight: 8 }, children: cycle.outcome }),
89
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { opacity: 0.7 }, children: cycle.id }),
90
- cycle.proposal && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { opacity: 0.6, paddingLeft: 8 }, children: cycle.proposal.summary })
91
- ] }, cycle.id))
120
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: 16 }, children: [
121
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 8 }, children: [
122
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontWeight: 600, fontSize: 11, textTransform: "uppercase", letterSpacing: "0.04em", opacity: 0.6 }, children: "Improve" }),
123
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
124
+ "button",
125
+ {
126
+ onClick: startImprove,
127
+ disabled: improving,
128
+ style: {
129
+ fontSize: 11,
130
+ fontWeight: 500,
131
+ padding: "3px 10px",
132
+ borderRadius: 4,
133
+ border: "1px solid rgba(128,128,128,0.3)",
134
+ background: improving ? "rgba(34,197,94,0.1)" : "transparent",
135
+ color: improving ? "#22c55e" : "var(--color-text-medium, #999)",
136
+ cursor: improving ? "default" : "pointer",
137
+ opacity: improving ? 0.8 : 1
138
+ },
139
+ children: improving ? "Running..." : "Start Improve"
140
+ }
141
+ )
142
+ ] }),
143
+ improve?.lastRun && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
144
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: 8, marginBottom: 8 }, children: [
145
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { color: "#22c55e", fontWeight: 600 }, children: [
146
+ improve.lastRun.successes,
147
+ " committed"
148
+ ] }),
149
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { color: "#ef4444", fontWeight: 600 }, children: [
150
+ improve.lastRun.failures,
151
+ " rolled back"
152
+ ] }),
153
+ improve.lastRun.skips > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { opacity: 0.5 }, children: [
154
+ improve.lastRun.skips,
155
+ " skipped"
156
+ ] })
157
+ ] }),
158
+ improve.lastRun.cycles.slice(-5).reverse().map((cy) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: "4px 0", borderBottom: "1px solid rgba(128,128,128,0.1)", display: "flex", alignItems: "flex-start", gap: 6 }, children: [
159
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: outcomeColor[cy.outcome] ?? "#6b7280", fontWeight: 700, fontSize: 12, width: 14, textAlign: "center", flexShrink: 0 }, children: outcomeIcon[cy.outcome] ?? "\u25CB" }),
160
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { flex: 1, opacity: 0.8 }, children: cy.description.slice(0, 80) }),
161
+ cy.commitHash && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontFamily: "monospace", fontSize: 10, opacity: 0.4 }, children: cy.commitHash })
162
+ ] }, cy.cycle))
163
+ ] }),
164
+ !improve?.lastRun && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { opacity: 0.5, textAlign: "center", padding: 16 }, children: 'No improve runs yet. Click "Start Improve" to begin.' })
92
165
  ] }),
93
- evolution.totalCycles === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { opacity: 0.5, textAlign: "center", padding: 20 }, children: "No genesis cycles yet. Use /genesis in the assistant to start evolving bot workflows." })
166
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { textAlign: "right" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: fetchData, style: { fontSize: 11, color: "#6b7280", background: "none", border: "none", cursor: "pointer" }, children: "Refresh" }) })
94
167
  ] });
95
168
  }
96
169
  var evolution_panel_default = WeaverEvolutionPanel;
@@ -26,58 +26,90 @@ __export(insights_widget_exports, {
26
26
  module.exports = __toCommonJS(insights_widget_exports);
27
27
  var import_react = require("react");
28
28
  var import_jsx_runtime = require("react/jsx-runtime");
29
- function WeaverInsightsWidget({ projectDir }) {
29
+ function WeaverInsightsWidget() {
30
30
  const [data, setData] = (0, import_react.useState)(null);
31
31
  const [error, setError] = (0, import_react.useState)(null);
32
+ const [loading, setLoading] = (0, import_react.useState)(true);
33
+ const [deviceName, setDeviceName] = (0, import_react.useState)("");
34
+ const fetchData = (0, import_react.useCallback)(async () => {
35
+ try {
36
+ const devRes = await fetch("/api/devices", { credentials: "include" });
37
+ if (!devRes.ok) {
38
+ setError("Not connected");
39
+ setLoading(false);
40
+ return;
41
+ }
42
+ const devices = await devRes.json();
43
+ const device = devices.find((d) => d.capabilities?.includes("insights"));
44
+ if (!device) {
45
+ setError("No device with insights capability");
46
+ setLoading(false);
47
+ return;
48
+ }
49
+ setDeviceName(device.name);
50
+ const [healthRes, insightsRes] = await Promise.allSettled([
51
+ fetch(`/api/devices/${device.id}/health`, { credentials: "include" }),
52
+ fetch(`/api/devices/${device.id}/insights`, { credentials: "include" })
53
+ ]);
54
+ const health = healthRes.status === "fulfilled" && healthRes.value.ok ? await healthRes.value.json() : null;
55
+ const insights = insightsRes.status === "fulfilled" && insightsRes.value.ok ? await insightsRes.value.json() : [];
56
+ setData({
57
+ health: health?.health ?? { overall: 0, workflows: [] },
58
+ bots: health?.bots ?? [],
59
+ insights: Array.isArray(insights) ? insights : [],
60
+ cost: health?.cost ?? { last7Days: 0, trend: "stable" },
61
+ trust: health?.trust ?? { phase: 1, score: 0 }
62
+ });
63
+ setError(null);
64
+ } catch (e) {
65
+ setError(e instanceof Error ? e.message : "Failed to load");
66
+ } finally {
67
+ setLoading(false);
68
+ }
69
+ }, []);
32
70
  (0, import_react.useEffect)(() => {
33
- if (!projectDir) return;
34
- fetch(`/api/mcp/fw_weaver_insights?projectDir=${encodeURIComponent(projectDir)}`).then((r) => r.json()).then(setData).catch((e) => setError(e.message));
35
- }, [projectDir]);
36
- if (error) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16, color: "#ef4444" }, children: [
37
- "Error: ",
38
- error
71
+ fetchData();
72
+ const interval = setInterval(fetchData, 3e4);
73
+ return () => clearInterval(interval);
74
+ }, [fetchData]);
75
+ if (loading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: 16, opacity: 0.5 }, children: "Loading insights..." });
76
+ if (error) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16 }, children: [
77
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { color: "#ef4444", marginBottom: 8 }, children: error }),
78
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: fetchData, style: { fontSize: 12, color: "#6b7280", background: "none", border: "1px solid #333", borderRadius: 4, padding: "4px 8px", cursor: "pointer" }, children: "Retry" })
39
79
  ] });
40
- if (!data) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: 16, opacity: 0.5 }, children: "Loading insights..." });
41
- const severityColor = {
42
- critical: "#ef4444",
43
- warning: "#f59e0b",
44
- info: "#6b7280"
45
- };
46
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16, fontFamily: "system-ui, sans-serif", fontSize: 13 }, children: [
47
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "baseline", gap: 12, marginBottom: 12 }, children: [
80
+ if (!data) return null;
81
+ const severityColor = { critical: "#ef4444", warning: "#f59e0b", info: "#6b7280" };
82
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: 16, fontFamily: "system-ui, sans-serif", fontSize: 13, color: "var(--color-text-high, #e5e5e5)" }, children: [
83
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontSize: 10, textTransform: "uppercase", letterSpacing: "0.05em", opacity: 0.5, marginBottom: 12 }, children: deviceName }),
84
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "baseline", gap: 12, marginBottom: 16 }, children: [
48
85
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: 28, fontWeight: 700 }, children: data.health.overall }),
49
86
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { opacity: 0.6 }, children: "/100 health" }),
50
87
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { marginLeft: "auto", opacity: 0.5 }, children: [
51
- "Phase ",
88
+ "P",
52
89
  data.trust.phase,
53
90
  " \xB7 $",
54
91
  data.cost.last7Days.toFixed(2),
55
- "/7d (",
56
- data.cost.trend,
57
- ")"
92
+ "/7d"
58
93
  ] })
59
94
  ] }),
60
- data.insights.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: 12 }, children: [
61
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontWeight: 600, marginBottom: 6 }, children: "Insights" }),
62
- data.insights.slice(0, 3).map((insight, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: "4px 0", borderBottom: "1px solid rgba(128,128,128,0.1)" }, children: [
63
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: severityColor[insight.severity] ?? "#6b7280", fontWeight: 600, marginRight: 8 }, children: insight.severity.toUpperCase() }),
64
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: insight.title }),
65
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { opacity: 0.5, marginLeft: 8 }, children: [
66
- Math.round(insight.confidence * 100),
67
- "%"
68
- ] })
95
+ data.insights.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: 16 }, children: [
96
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontWeight: 600, marginBottom: 6, fontSize: 11, textTransform: "uppercase", letterSpacing: "0.04em", opacity: 0.6 }, children: "Insights" }),
97
+ data.insights.slice(0, 5).map((insight, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: "6px 0", borderBottom: "1px solid rgba(128,128,128,0.15)" }, children: [
98
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: severityColor[insight.severity] ?? "#6b7280", fontWeight: 600, marginRight: 8, fontSize: 10, textTransform: "uppercase" }, children: insight.severity }),
99
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: insight.title })
69
100
  ] }, i))
70
101
  ] }),
102
+ data.insights.length === 0 && data.health.overall === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { opacity: 0.5, textAlign: "center", padding: 20 }, children: "No data yet. Connect a device and run some workflows." }),
71
103
  data.bots.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
72
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontWeight: 600, marginBottom: 6 }, children: "Bots" }),
73
- data.bots.map((bot, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: "2px 0" }, children: [
74
- bot.name,
75
- ": ",
76
- Math.round(bot.successRate * 100),
77
- "% success (",
78
- bot.totalTasksRun,
79
- " tasks)",
80
- bot.ejected && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { opacity: 0.5 }, children: " \xB7 ejected" })
104
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontWeight: 600, marginBottom: 6, fontSize: 11, textTransform: "uppercase", letterSpacing: "0.04em", opacity: 0.6 }, children: "Bots" }),
105
+ data.bots.map((bot, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: "4px 0", display: "flex", justifyContent: "space-between" }, children: [
106
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: bot.name }),
107
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { opacity: 0.6 }, children: [
108
+ Math.round(bot.successRate * 100),
109
+ "% (",
110
+ bot.totalTasksRun,
111
+ ")"
112
+ ] })
81
113
  ] }, i))
82
114
  ] })
83
115
  ] });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synergenius/flow-weaver-pack-weaver",
3
- "version": "0.9.14",
3
+ "version": "0.9.15",
4
4
  "description": "AI bot for Flow Weaver. Execute tasks, run workflows, evolve autonomously.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,94 +1,179 @@
1
1
  /**
2
- * Weaver Evolution Panel — shows Genesis cycle history and operation effectiveness.
3
- * Loaded dynamically by the platform's pack UI contributions loader.
2
+ * Weaver Evolution Panel — shows Genesis cycle history, trust level, improve status.
3
+ * Fetches data from the connected device via platform relay endpoints.
4
4
  */
5
5
 
6
- import React, { useEffect, useState } from 'react';
6
+ import React, { useEffect, useState, useCallback } from 'react';
7
7
 
8
- interface EvolutionData {
9
- evolution: {
10
- totalCycles: number;
11
- successRate: number;
12
- byOperationType: Record<string, { proposed: number; applied: number; effectiveness: number }>;
13
- recentCycles: Array<{ id: string; outcome: string; proposal?: { summary: string; impactLevel: string } }>;
8
+ interface ImproveStatus {
9
+ running: boolean;
10
+ lastRun?: {
11
+ successes: number;
12
+ failures: number;
13
+ skips: number;
14
+ cycles: Array<{ cycle: number; outcome: string; description: string; commitHash?: string }>;
14
15
  };
15
- trust: { phase: number; score: number };
16
16
  }
17
17
 
18
- export function WeaverEvolutionPanel({ projectDir }: { projectDir?: string }) {
19
- const [data, setData] = useState<EvolutionData | null>(null);
18
+ interface HealthData {
19
+ health?: { overall: number };
20
+ trust?: { phase: number; score: number };
21
+ cost?: { last7Days: number; trend: string };
22
+ }
23
+
24
+ interface Device {
25
+ id: string;
26
+ name: string;
27
+ capabilities: string[];
28
+ }
29
+
30
+ export function WeaverEvolutionPanel() {
31
+ const [health, setHealth] = useState<HealthData | null>(null);
32
+ const [improve, setImprove] = useState<ImproveStatus | null>(null);
20
33
  const [error, setError] = useState<string | null>(null);
34
+ const [loading, setLoading] = useState(true);
35
+ const [deviceId, setDeviceId] = useState<string | null>(null);
36
+ const [deviceName, setDeviceName] = useState('');
37
+ const [improving, setImproving] = useState(false);
38
+
39
+ const fetchData = useCallback(async () => {
40
+ try {
41
+ const devRes = await fetch('/api/devices', { credentials: 'include' });
42
+ if (!devRes.ok) { setError('Not connected'); setLoading(false); return; }
43
+ const devices: Device[] = await devRes.json();
44
+ const device = devices.find(d => d.capabilities?.includes('improve'));
45
+ if (!device) { setError('No device with improve capability'); setLoading(false); return; }
46
+ setDeviceId(device.id);
47
+ setDeviceName(device.name);
48
+
49
+ const [healthRes, improveRes] = await Promise.allSettled([
50
+ fetch(`/api/devices/${device.id}/health`, { credentials: 'include' }),
51
+ fetch(`/api/devices/${device.id}/improve/status`, { credentials: 'include' }),
52
+ ]);
53
+
54
+ if (healthRes.status === 'fulfilled' && healthRes.value.ok) {
55
+ setHealth(await healthRes.value.json());
56
+ }
57
+ if (improveRes.status === 'fulfilled' && improveRes.value.ok) {
58
+ const data = await improveRes.value.json();
59
+ setImprove(data);
60
+ setImproving(data.running ?? false);
61
+ }
62
+ setError(null);
63
+ } catch (e) {
64
+ setError(e instanceof Error ? e.message : 'Failed to load');
65
+ } finally {
66
+ setLoading(false);
67
+ }
68
+ }, []);
21
69
 
22
70
  useEffect(() => {
23
- if (!projectDir) return;
24
- fetch(`/api/mcp/fw_weaver_insights?projectDir=${encodeURIComponent(projectDir)}`)
25
- .then(r => r.json())
26
- .then(setData)
27
- .catch(e => setError(e.message));
28
- }, [projectDir]);
29
-
30
- if (error) return <div style={{ padding: 16, color: '#ef4444' }}>Error: {error}</div>;
31
- if (!data) return <div style={{ padding: 16, opacity: 0.5 }}>Loading evolution data...</div>;
32
-
33
- const { evolution, trust } = data;
34
- const outcomeColor: Record<string, string> = {
35
- applied: '#22c55e',
36
- 'rolled-back': '#ef4444',
37
- rejected: '#f59e0b',
38
- 'no-change': '#6b7280',
39
- error: '#ef4444',
71
+ fetchData();
72
+ const interval = setInterval(fetchData, 15_000);
73
+ return () => clearInterval(interval);
74
+ }, [fetchData]);
75
+
76
+ const startImprove = async () => {
77
+ if (!deviceId) return;
78
+ setImproving(true);
79
+ try {
80
+ await fetch(`/api/devices/${deviceId}/improve`, {
81
+ method: 'POST',
82
+ headers: { 'Content-Type': 'application/json' },
83
+ credentials: 'include',
84
+ body: JSON.stringify({ maxCycles: 5 }),
85
+ });
86
+ } catch { /* ignore */ }
40
87
  };
41
88
 
89
+ if (loading) return <div style={{ padding: 16, opacity: 0.5 }}>Loading...</div>;
90
+ if (error) return (
91
+ <div style={{ padding: 16 }}>
92
+ <div style={{ color: '#ef4444', marginBottom: 8 }}>{error}</div>
93
+ <button onClick={fetchData} style={{ fontSize: 12, color: '#6b7280', background: 'none', border: '1px solid #333', borderRadius: 4, padding: '4px 8px', cursor: 'pointer' }}>Retry</button>
94
+ </div>
95
+ );
96
+
97
+ const trustPhase = health?.trust?.phase ?? 1;
98
+ const trustScore = health?.trust?.score ?? 0;
99
+ const healthScore = health?.health?.overall ?? 0;
100
+
101
+ const outcomeIcon: Record<string, string> = { success: '\u2713', failure: '\u2717' };
102
+ const outcomeColor: Record<string, string> = { success: '#22c55e', failure: '#ef4444' };
103
+
42
104
  return (
43
- <div style={{ padding: 16, fontFamily: 'system-ui, sans-serif', fontSize: 13 }}>
44
- <div style={{ marginBottom: 16 }}>
45
- <div style={{ fontWeight: 600, marginBottom: 4 }}>Trust Level</div>
46
- <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
47
- <span style={{ fontSize: 20, fontWeight: 700 }}>Phase {trust.phase}</span>
48
- <span style={{ opacity: 0.5 }}>Score: {trust.score}/100</span>
49
- </div>
105
+ <div style={{ padding: 16, fontFamily: 'system-ui, sans-serif', fontSize: 13, color: 'var(--color-text-high, #e5e5e5)' }}>
106
+ <div style={{ fontSize: 10, textTransform: 'uppercase', letterSpacing: '0.05em', opacity: 0.5, marginBottom: 12 }}>
107
+ {deviceName}
50
108
  </div>
51
109
 
52
- <div style={{ marginBottom: 16 }}>
53
- <div style={{ fontWeight: 600, marginBottom: 4 }}>
54
- Genesis Cycles: {evolution.totalCycles} ({evolution.totalCycles > 0 ? Math.round(evolution.successRate * 100) : 0}% success)
110
+ {/* Trust + Health */}
111
+ <div style={{ display: 'flex', gap: 16, marginBottom: 16 }}>
112
+ <div style={{ flex: 1, padding: 12, border: '1px solid rgba(128,128,128,0.2)', borderRadius: 6 }}>
113
+ <div style={{ fontSize: 20, fontWeight: 700 }}>P{trustPhase}</div>
114
+ <div style={{ fontSize: 10, opacity: 0.5, textTransform: 'uppercase', marginTop: 2 }}>Trust · {trustScore}/100</div>
115
+ </div>
116
+ <div style={{ flex: 1, padding: 12, border: '1px solid rgba(128,128,128,0.2)', borderRadius: 6 }}>
117
+ <div style={{ fontSize: 20, fontWeight: 700 }}>{healthScore}</div>
118
+ <div style={{ fontSize: 10, opacity: 0.5, textTransform: 'uppercase', marginTop: 2 }}>Health</div>
55
119
  </div>
56
120
  </div>
57
121
 
58
- {Object.keys(evolution.byOperationType).length > 0 && (
59
- <div style={{ marginBottom: 16 }}>
60
- <div style={{ fontWeight: 600, marginBottom: 4 }}>Operation Effectiveness</div>
61
- {Object.entries(evolution.byOperationType).map(([op, stats]) => (
62
- <div key={op} style={{ padding: '2px 0', display: 'flex', justifyContent: 'space-between' }}>
63
- <span>{op}</span>
64
- <span style={{ opacity: 0.7 }}>{Math.round(stats.effectiveness * 100)}% ({stats.applied}/{stats.proposed})</span>
65
- </div>
66
- ))}
122
+ {/* Improve */}
123
+ <div style={{ marginBottom: 16 }}>
124
+ <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 8 }}>
125
+ <div style={{ fontWeight: 600, fontSize: 11, textTransform: 'uppercase', letterSpacing: '0.04em', opacity: 0.6 }}>
126
+ Improve
127
+ </div>
128
+ <button
129
+ onClick={startImprove}
130
+ disabled={improving}
131
+ style={{
132
+ fontSize: 11,
133
+ fontWeight: 500,
134
+ padding: '3px 10px',
135
+ borderRadius: 4,
136
+ border: '1px solid rgba(128,128,128,0.3)',
137
+ background: improving ? 'rgba(34,197,94,0.1)' : 'transparent',
138
+ color: improving ? '#22c55e' : 'var(--color-text-medium, #999)',
139
+ cursor: improving ? 'default' : 'pointer',
140
+ opacity: improving ? 0.8 : 1,
141
+ }}
142
+ >
143
+ {improving ? 'Running...' : 'Start Improve'}
144
+ </button>
67
145
  </div>
68
- )}
69
-
70
- {evolution.recentCycles.length > 0 && (
71
- <div>
72
- <div style={{ fontWeight: 600, marginBottom: 4 }}>Recent Cycles</div>
73
- {evolution.recentCycles.slice(-5).reverse().map((cycle) => (
74
- <div key={cycle.id} style={{ padding: '4px 0', borderBottom: '1px solid rgba(128,128,128,0.1)' }}>
75
- <span style={{ color: outcomeColor[cycle.outcome] ?? '#6b7280', fontWeight: 600, marginRight: 8 }}>
76
- {cycle.outcome}
77
- </span>
78
- <span style={{ opacity: 0.7 }}>{cycle.id}</span>
79
- {cycle.proposal && (
80
- <div style={{ opacity: 0.6, paddingLeft: 8 }}>{cycle.proposal.summary}</div>
81
- )}
146
+
147
+ {improve?.lastRun && (
148
+ <>
149
+ <div style={{ display: 'flex', gap: 8, marginBottom: 8 }}>
150
+ <span style={{ color: '#22c55e', fontWeight: 600 }}>{improve.lastRun.successes} committed</span>
151
+ <span style={{ color: '#ef4444', fontWeight: 600 }}>{improve.lastRun.failures} rolled back</span>
152
+ {improve.lastRun.skips > 0 && <span style={{ opacity: 0.5 }}>{improve.lastRun.skips} skipped</span>}
82
153
  </div>
83
- ))}
84
- </div>
85
- )}
86
154
 
87
- {evolution.totalCycles === 0 && (
88
- <div style={{ opacity: 0.5, textAlign: 'center', padding: 20 }}>
89
- No genesis cycles yet. Use /genesis in the assistant to start evolving bot workflows.
90
- </div>
91
- )}
155
+ {improve.lastRun.cycles.slice(-5).reverse().map(cy => (
156
+ <div key={cy.cycle} style={{ padding: '4px 0', borderBottom: '1px solid rgba(128,128,128,0.1)', display: 'flex', alignItems: 'flex-start', gap: 6 }}>
157
+ <span style={{ color: outcomeColor[cy.outcome] ?? '#6b7280', fontWeight: 700, fontSize: 12, width: 14, textAlign: 'center', flexShrink: 0 }}>
158
+ {outcomeIcon[cy.outcome] ?? '\u25CB'}
159
+ </span>
160
+ <span style={{ flex: 1, opacity: 0.8 }}>{cy.description.slice(0, 80)}</span>
161
+ {cy.commitHash && <span style={{ fontFamily: 'monospace', fontSize: 10, opacity: 0.4 }}>{cy.commitHash}</span>}
162
+ </div>
163
+ ))}
164
+ </>
165
+ )}
166
+
167
+ {!improve?.lastRun && (
168
+ <div style={{ opacity: 0.5, textAlign: 'center', padding: 16 }}>
169
+ No improve runs yet. Click "Start Improve" to begin.
170
+ </div>
171
+ )}
172
+ </div>
173
+
174
+ <div style={{ textAlign: 'right' }}>
175
+ <button onClick={fetchData} style={{ fontSize: 11, color: '#6b7280', background: 'none', border: 'none', cursor: 'pointer' }}>Refresh</button>
176
+ </div>
92
177
  </div>
93
178
  );
94
179
  }
@@ -1,9 +1,9 @@
1
1
  /**
2
- * Weaver Insights Dashboard Widget — shows project health, insights, and cost summary.
3
- * Loaded dynamically by the platform's pack UI contributions loader.
2
+ * Weaver Insights Widget — shows project health, insights, and cost summary.
3
+ * Fetches data from the connected device via platform relay endpoints.
4
4
  */
5
5
 
6
- import React, { useEffect, useState } from 'react';
6
+ import React, { useEffect, useState, useCallback } from 'react';
7
7
 
8
8
  interface InsightsData {
9
9
  health: { overall: number; workflows: Array<{ file: string; score: number; trend: string }> };
@@ -13,59 +13,112 @@ interface InsightsData {
13
13
  trust: { phase: number; score: number };
14
14
  }
15
15
 
16
- export function WeaverInsightsWidget({ projectDir }: { projectDir?: string }) {
16
+ interface Device {
17
+ id: string;
18
+ name: string;
19
+ capabilities: string[];
20
+ }
21
+
22
+ export function WeaverInsightsWidget() {
17
23
  const [data, setData] = useState<InsightsData | null>(null);
18
24
  const [error, setError] = useState<string | null>(null);
25
+ const [loading, setLoading] = useState(true);
26
+ const [deviceName, setDeviceName] = useState('');
27
+
28
+ const fetchData = useCallback(async () => {
29
+ try {
30
+ // Find first connected device with insights capability
31
+ const devRes = await fetch('/api/devices', { credentials: 'include' });
32
+ if (!devRes.ok) { setError('Not connected'); setLoading(false); return; }
33
+ const devices: Device[] = await devRes.json();
34
+ const device = devices.find(d => d.capabilities?.includes('insights'));
35
+ if (!device) { setError('No device with insights capability'); setLoading(false); return; }
36
+ setDeviceName(device.name);
37
+
38
+ // Fetch health + insights from device
39
+ const [healthRes, insightsRes] = await Promise.allSettled([
40
+ fetch(`/api/devices/${device.id}/health`, { credentials: 'include' }),
41
+ fetch(`/api/devices/${device.id}/insights`, { credentials: 'include' }),
42
+ ]);
43
+
44
+ const health = healthRes.status === 'fulfilled' && healthRes.value.ok
45
+ ? await healthRes.value.json() : null;
46
+ const insights = insightsRes.status === 'fulfilled' && insightsRes.value.ok
47
+ ? await insightsRes.value.json() : [];
48
+
49
+ setData({
50
+ health: health?.health ?? { overall: 0, workflows: [] },
51
+ bots: health?.bots ?? [],
52
+ insights: Array.isArray(insights) ? insights : [],
53
+ cost: health?.cost ?? { last7Days: 0, trend: 'stable' },
54
+ trust: health?.trust ?? { phase: 1, score: 0 },
55
+ });
56
+ setError(null);
57
+ } catch (e) {
58
+ setError(e instanceof Error ? e.message : 'Failed to load');
59
+ } finally {
60
+ setLoading(false);
61
+ }
62
+ }, []);
19
63
 
20
64
  useEffect(() => {
21
- if (!projectDir) return;
22
- fetch(`/api/mcp/fw_weaver_insights?projectDir=${encodeURIComponent(projectDir)}`)
23
- .then(r => r.json())
24
- .then(setData)
25
- .catch(e => setError(e.message));
26
- }, [projectDir]);
27
-
28
- if (error) return <div style={{ padding: 16, color: '#ef4444' }}>Error: {error}</div>;
29
- if (!data) return <div style={{ padding: 16, opacity: 0.5 }}>Loading insights...</div>;
30
-
31
- const severityColor: Record<string, string> = {
32
- critical: '#ef4444',
33
- warning: '#f59e0b',
34
- info: '#6b7280',
35
- };
65
+ fetchData();
66
+ const interval = setInterval(fetchData, 30_000);
67
+ return () => clearInterval(interval);
68
+ }, [fetchData]);
69
+
70
+ if (loading) return <div style={{ padding: 16, opacity: 0.5 }}>Loading insights...</div>;
71
+ if (error) return (
72
+ <div style={{ padding: 16 }}>
73
+ <div style={{ color: '#ef4444', marginBottom: 8 }}>{error}</div>
74
+ <button onClick={fetchData} style={{ fontSize: 12, color: '#6b7280', background: 'none', border: '1px solid #333', borderRadius: 4, padding: '4px 8px', cursor: 'pointer' }}>Retry</button>
75
+ </div>
76
+ );
77
+ if (!data) return null;
78
+
79
+ const severityColor: Record<string, string> = { critical: '#ef4444', warning: '#f59e0b', info: '#6b7280' };
36
80
 
37
81
  return (
38
- <div style={{ padding: 16, fontFamily: 'system-ui, sans-serif', fontSize: 13 }}>
39
- <div style={{ display: 'flex', alignItems: 'baseline', gap: 12, marginBottom: 12 }}>
82
+ <div style={{ padding: 16, fontFamily: 'system-ui, sans-serif', fontSize: 13, color: 'var(--color-text-high, #e5e5e5)' }}>
83
+ <div style={{ fontSize: 10, textTransform: 'uppercase', letterSpacing: '0.05em', opacity: 0.5, marginBottom: 12 }}>
84
+ {deviceName}
85
+ </div>
86
+
87
+ <div style={{ display: 'flex', alignItems: 'baseline', gap: 12, marginBottom: 16 }}>
40
88
  <span style={{ fontSize: 28, fontWeight: 700 }}>{data.health.overall}</span>
41
89
  <span style={{ opacity: 0.6 }}>/100 health</span>
42
90
  <span style={{ marginLeft: 'auto', opacity: 0.5 }}>
43
- Phase {data.trust.phase} · ${data.cost.last7Days.toFixed(2)}/7d ({data.cost.trend})
91
+ P{data.trust.phase} · ${data.cost.last7Days.toFixed(2)}/7d
44
92
  </span>
45
93
  </div>
46
94
 
47
95
  {data.insights.length > 0 && (
48
- <div style={{ marginBottom: 12 }}>
49
- <div style={{ fontWeight: 600, marginBottom: 6 }}>Insights</div>
50
- {data.insights.slice(0, 3).map((insight, i) => (
51
- <div key={i} style={{ padding: '4px 0', borderBottom: '1px solid rgba(128,128,128,0.1)' }}>
52
- <span style={{ color: severityColor[insight.severity] ?? '#6b7280', fontWeight: 600, marginRight: 8 }}>
53
- {insight.severity.toUpperCase()}
96
+ <div style={{ marginBottom: 16 }}>
97
+ <div style={{ fontWeight: 600, marginBottom: 6, fontSize: 11, textTransform: 'uppercase', letterSpacing: '0.04em', opacity: 0.6 }}>Insights</div>
98
+ {data.insights.slice(0, 5).map((insight, i) => (
99
+ <div key={i} style={{ padding: '6px 0', borderBottom: '1px solid rgba(128,128,128,0.15)' }}>
100
+ <span style={{ color: severityColor[insight.severity] ?? '#6b7280', fontWeight: 600, marginRight: 8, fontSize: 10, textTransform: 'uppercase' }}>
101
+ {insight.severity}
54
102
  </span>
55
103
  <span>{insight.title}</span>
56
- <span style={{ opacity: 0.5, marginLeft: 8 }}>{Math.round(insight.confidence * 100)}%</span>
57
104
  </div>
58
105
  ))}
59
106
  </div>
60
107
  )}
61
108
 
109
+ {data.insights.length === 0 && data.health.overall === 0 && (
110
+ <div style={{ opacity: 0.5, textAlign: 'center', padding: 20 }}>
111
+ No data yet. Connect a device and run some workflows.
112
+ </div>
113
+ )}
114
+
62
115
  {data.bots.length > 0 && (
63
116
  <div>
64
- <div style={{ fontWeight: 600, marginBottom: 6 }}>Bots</div>
117
+ <div style={{ fontWeight: 600, marginBottom: 6, fontSize: 11, textTransform: 'uppercase', letterSpacing: '0.04em', opacity: 0.6 }}>Bots</div>
65
118
  {data.bots.map((bot, i) => (
66
- <div key={i} style={{ padding: '2px 0' }}>
67
- {bot.name}: {Math.round(bot.successRate * 100)}% success ({bot.totalTasksRun} tasks)
68
- {bot.ejected && <span style={{ opacity: 0.5 }}> · ejected</span>}
119
+ <div key={i} style={{ padding: '4px 0', display: 'flex', justifyContent: 'space-between' }}>
120
+ <span>{bot.name}</span>
121
+ <span style={{ opacity: 0.6 }}>{Math.round(bot.successRate * 100)}% ({bot.totalTasksRun})</span>
69
122
  </div>
70
123
  ))}
71
124
  </div>