@synergenius/flow-weaver-pack-weaver 0.9.20 → 0.9.22

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.
@@ -5,12 +5,13 @@ var import_jsx_runtime = require("react/jsx-runtime");
5
5
  var React = require("react");
6
6
  var { useState, useEffect, useCallback } = React;
7
7
  var { Typography } = require("@fw/plugin-ui-kit");
8
- var { Icon } = require("@fw/plugin-ui-kit");
9
8
  var { CollapsibleSection } = require("@fw/plugin-ui-kit");
10
9
  var { LoadingSpinner } = require("@fw/plugin-ui-kit");
11
10
  var { Banner } = require("@fw/plugin-ui-kit");
12
11
  var { IconButton } = require("@fw/plugin-ui-kit");
12
+ var { Badge } = require("@fw/plugin-ui-kit");
13
13
  var { styled } = require("@fw/plugin-theme");
14
+ var BASE = "/api/pack-tool/@synergenius/flow-weaver-pack-weaver";
14
15
  var ConfigRow = styled.div({
15
16
  display: "flex",
16
17
  justifyContent: "space-between",
@@ -19,31 +20,44 @@ var ConfigRow = styled.div({
19
20
  borderBottom: "1px solid $color-border-default",
20
21
  "&:last-of-type": { borderBottom: "none" }
21
22
  });
22
- var TOOL_URL = "/api/pack-tool/@synergenius/flow-weaver-pack-weaver/fw_weaver_insights";
23
+ var Footer = styled.div({
24
+ display: "flex",
25
+ justifyContent: "space-between",
26
+ alignItems: "center",
27
+ marginTop: "10px",
28
+ paddingTop: "6px",
29
+ borderTop: "1px solid $color-border-default"
30
+ });
31
+ function callTool(tool, body = {}) {
32
+ return fetch(`${BASE}/${tool}`, {
33
+ method: "POST",
34
+ headers: { "Content-Type": "application/json" },
35
+ body: JSON.stringify(body),
36
+ credentials: "include"
37
+ }).then(async (res) => {
38
+ if (!res.ok) throw new Error("Request failed");
39
+ const json = await res.json();
40
+ if (json.isError) throw new Error(json.result);
41
+ try {
42
+ return JSON.parse(json.result);
43
+ } catch {
44
+ return json.result;
45
+ }
46
+ });
47
+ }
23
48
  function BotConfig({ packName, botId }) {
24
- const [data, setData] = useState(null);
49
+ const [insights, setInsights] = useState(null);
50
+ const [providers, setProviders] = useState([]);
25
51
  const [error, setError] = useState(null);
26
52
  const [loading, setLoading] = useState(true);
27
53
  const fetchData = useCallback(async () => {
28
54
  try {
29
- const res = await fetch(TOOL_URL, {
30
- method: "POST",
31
- headers: { "Content-Type": "application/json" },
32
- body: JSON.stringify({}),
33
- credentials: "include"
34
- });
35
- if (!res.ok) {
36
- setError("Failed to fetch");
37
- setLoading(false);
38
- return;
39
- }
40
- const json = await res.json();
41
- if (json.isError) {
42
- setError(json.result);
43
- setLoading(false);
44
- return;
45
- }
46
- setData(JSON.parse(json.result));
55
+ const [ins, provs] = await Promise.allSettled([
56
+ callTool("fw_weaver_insights"),
57
+ callTool("fw_weaver_providers")
58
+ ]);
59
+ if (ins.status === "fulfilled") setInsights(ins.value);
60
+ if (provs.status === "fulfilled" && Array.isArray(provs.value)) setProviders(provs.value);
47
61
  setError(null);
48
62
  } catch (e) {
49
63
  setError(e.message ?? "Failed to load");
@@ -54,36 +68,44 @@ function BotConfig({ packName, botId }) {
54
68
  useEffect(() => {
55
69
  fetchData();
56
70
  }, [fetchData]);
57
- if (loading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoadingSpinner, { size: "small" });
58
- if (error) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: "8px 12px" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Banner, { status: "danger", size: "small", children: error }) });
59
- const bot = data?.bots?.find((b) => b.name === botId) ?? data?.bots?.[0];
60
- const provider = bot?.provider ?? "Not configured";
71
+ if (loading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: 16, display: "flex", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoadingSpinner, { size: "small" }) });
72
+ if (error && !insights) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { padding: "8px 12px" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Banner, { status: "danger", size: "small", children: error }) });
73
+ const bot = insights?.bots?.find((b) => b.name === botId) ?? insights?.bots?.[0];
74
+ const configuredProvider = bot?.provider;
75
+ const availableProvider = providers.find((p) => p.envVarsSet);
76
+ const activeProvider = configuredProvider || availableProvider?.name;
77
+ const isAutoDetected = !configuredProvider && !!availableProvider;
61
78
  const approval = bot?.approvalMode ?? "auto";
62
- const hasBots = (data?.bots?.length ?? 0) > 0;
79
+ const trustPhase = insights?.trust?.phase ?? 1;
80
+ const trustScore = insights?.trust?.score ?? 0;
81
+ const noProvider = !activeProvider;
63
82
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CollapsibleSection, { title: "Configuration", defaultExpanded: true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: "4px 12px 12px" }, children: [
64
83
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ConfigRow, { children: [
65
84
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Typography, { variant: "smallCaption-bold", color: "color-text-subtle", children: "Provider" }),
66
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Typography, { variant: "caption-regular", color: "color-text-high", children: provider })
85
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
86
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Typography, { variant: "caption-regular", color: "color-text-high", children: activeProvider ?? "None detected" }),
87
+ isAutoDetected && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Badge, { variant: "default", children: "auto" })
88
+ ] })
67
89
  ] }),
68
90
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ConfigRow, { children: [
69
91
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Typography, { variant: "smallCaption-bold", color: "color-text-subtle", children: "Approval" }),
70
92
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Typography, { variant: "caption-regular", color: "color-text-high", children: approval })
71
93
  ] }),
72
94
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ConfigRow, { children: [
73
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Typography, { variant: "smallCaption-bold", color: "color-text-subtle", children: "Trust Phase" }),
95
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Typography, { variant: "smallCaption-bold", color: "color-text-subtle", children: "Trust" }),
74
96
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Typography, { variant: "caption-regular", color: "color-text-high", children: [
75
97
  "P",
76
- data?.trust?.phase ?? "?",
98
+ trustPhase,
77
99
  " (",
78
- data?.trust?.score ?? 0,
100
+ trustScore,
79
101
  "/100)"
80
102
  ] })
81
103
  ] }),
82
- !hasBots && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: 12 }, children: [
83
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Banner, { status: "info", size: "small", children: "No .weaver.json detected. Create one to configure providers and approval modes." }),
84
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Typography, { variant: "caption-regular", color: "color-text-subtle", style: { marginTop: 6, fontFamily: "var(--typography-family-mono)" }, children: "npx flow-weaver weaver init" })
85
- ] }),
86
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginTop: 10, display: "flex", justifyContent: "flex-end" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconButton, { icon: "refresh", size: "xs", variant: "clear", onClick: fetchData, title: "Refresh" }) })
104
+ noProvider && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginTop: 10 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Banner, { status: "warning", size: "small", children: "No AI provider detected. Set ANTHROPIC_API_KEY or install Claude CLI." }) }),
105
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Footer, { children: [
106
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Typography, { variant: "smallCaption-regular", color: "color-text-subtle", children: "Customize via .weaver.json" }),
107
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IconButton, { icon: "refresh", size: "xs", variant: "clear", onClick: fetchData, title: "Refresh" })
108
+ ] })
87
109
  ] }) });
88
110
  }
89
111
  module.exports = BotConfig;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifestVersion": 2,
3
3
  "name": "@synergenius/flow-weaver-pack-weaver",
4
- "version": "0.9.20",
4
+ "version": "0.9.22",
5
5
  "description": "AI bot for Flow Weaver. Execute tasks, run workflows, evolve autonomously.",
6
6
  "engineVersion": ">=0.22.10",
7
7
  "categories": [
@@ -1074,6 +1074,7 @@
1074
1074
  ],
1075
1075
  "uiContributions": [],
1076
1076
  "botUI": {
1077
+ "activity": "dist/ui/bot-activity.js",
1077
1078
  "config": "dist/ui/bot-config.js",
1078
1079
  "status": "dist/ui/bot-status.js",
1079
1080
  "dashboard": "dist/ui/bot-dashboard.js"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synergenius/flow-weaver-pack-weaver",
3
- "version": "0.9.20",
3
+ "version": "0.9.22",
4
4
  "description": "AI bot for Flow Weaver. Execute tasks, run workflows, evolve autonomously.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/bot/runner.ts CHANGED
@@ -2,10 +2,12 @@ import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
3
  import type {
4
4
  ApprovalMode,
5
+ AuditEvent,
5
6
  AuditEventCallback,
6
7
  BotConfig,
7
8
  BotNotifyConfig,
8
9
  ExecutionEvent,
10
+ RunCostSummary,
9
11
  RunOutcome,
10
12
  WeaverConfig,
11
13
  WorkflowResult,
@@ -118,7 +120,17 @@ export async function runWorkflow(
118
120
  try { store = new RunStore(); } catch { /* non-fatal */ }
119
121
  const runId = RunStore.newId();
120
122
  const startedAt = new Date().toISOString();
121
- initAuditLogger(runId, options?.onAuditEvent);
123
+
124
+ // Collect execution trace and audit events for persistence
125
+ const collectedTrace: ExecutionEvent[] = [];
126
+ const collectedAudit: AuditEvent[] = [];
127
+
128
+ const auditCollector: AuditEventCallback = (event) => {
129
+ collectedAudit.push(event);
130
+ options?.onAuditEvent?.(event);
131
+ };
132
+
133
+ initAuditLogger(runId, auditCollector);
122
134
 
123
135
  // Mark run as in-progress so abrupt kills leave a trace
124
136
  try { store?.markRunning(runId, absPath); } catch { /* non-fatal */ }
@@ -183,38 +195,55 @@ export async function runWorkflow(
183
195
  return dryResult;
184
196
  }
185
197
 
186
- // Forward trace events as ExecutionEvents
187
- const onTraceEvent = options?.onEvent
188
- ? (traceEvent: { type: string; timestamp: number; data?: Record<string, unknown> }) => {
189
- if (traceEvent.type !== 'STATUS_CHANGED' || !traceEvent.data) return;
190
- const nodeId = traceEvent.data.id as string | undefined;
191
- const status = traceEvent.data.status as string | undefined;
192
- if (!nodeId || !status) return;
193
-
194
- let eventType: ExecutionEvent['type'] | null = null;
195
- if (status === 'RUNNING') eventType = 'node-start';
196
- else if (status === 'SUCCEEDED') eventType = 'node-complete';
197
- else if (status === 'FAILED') eventType = 'node-error';
198
-
199
- if (eventType) {
200
- options.onEvent!({
201
- type: eventType,
202
- nodeId,
203
- nodeType: traceEvent.data.nodeTypeName as string | undefined,
204
- timestamp: traceEvent.timestamp,
205
- error: traceEvent.data.error as string | undefined,
206
- });
207
- }
208
- }
209
- : undefined;
198
+ // Always collect trace events for persistence; also forward to caller if provided.
199
+ // Track node start times so we can compute durationMs on completion.
200
+ const nodeStartTimes = new Map<string, number>();
201
+
202
+ const onTraceEvent = (traceEvent: { type: string; timestamp: number; data?: Record<string, unknown> }) => {
203
+ if (traceEvent.type !== 'STATUS_CHANGED' || !traceEvent.data) return;
204
+ const nodeId = traceEvent.data.id as string | undefined;
205
+ const status = traceEvent.data.status as string | undefined;
206
+ if (!nodeId || !status) return;
207
+
208
+ let eventType: ExecutionEvent['type'] | null = null;
209
+ let durationMs: number | undefined;
210
+
211
+ if (status === 'RUNNING') {
212
+ eventType = 'node-start';
213
+ nodeStartTimes.set(nodeId, traceEvent.timestamp);
214
+ } else if (status === 'SUCCEEDED') {
215
+ eventType = 'node-complete';
216
+ const startTime = nodeStartTimes.get(nodeId);
217
+ if (startTime) durationMs = traceEvent.timestamp - startTime;
218
+ nodeStartTimes.delete(nodeId);
219
+ } else if (status === 'FAILED') {
220
+ eventType = 'node-error';
221
+ const startTime = nodeStartTimes.get(nodeId);
222
+ if (startTime) durationMs = traceEvent.timestamp - startTime;
223
+ nodeStartTimes.delete(nodeId);
224
+ }
225
+
226
+ if (eventType) {
227
+ const event: ExecutionEvent = {
228
+ type: eventType,
229
+ nodeId,
230
+ nodeType: traceEvent.data.nodeTypeName as string | undefined,
231
+ timestamp: traceEvent.timestamp,
232
+ durationMs,
233
+ error: traceEvent.data.error as string | undefined,
234
+ };
235
+ collectedTrace.push(event);
236
+ options?.onEvent?.(event);
237
+ }
238
+ };
210
239
 
211
240
  const execResult = await executeWorkflowFromFile(
212
241
  absPath,
213
242
  options?.params ?? {},
214
243
  {
215
244
  agentChannel: botChannel,
216
- includeTrace: !!onTraceEvent,
217
- production: !onTraceEvent,
245
+ includeTrace: true,
246
+ production: false,
218
247
  onEvent: onTraceEvent,
219
248
  },
220
249
  );
@@ -224,15 +253,30 @@ export async function runWorkflow(
224
253
  const summary = buildSummary(result);
225
254
  const outcome = success ? 'completed' : 'failed';
226
255
 
227
- // Extract stepLog from WeaverContext if available
256
+ // Extract stepLog and plan from WeaverContext if available
228
257
  let stepLog: import('./types.js').StepLogEntry[] | undefined;
258
+ let plan: RunRecord['plan'] | undefined;
229
259
  try {
230
260
  const ctxStr = result?.ctx as string | undefined;
231
261
  if (ctxStr) {
232
262
  const ctx = JSON.parse(ctxStr);
233
263
  if (ctx.stepLogJson) stepLog = JSON.parse(ctx.stepLogJson);
264
+ if (ctx.planJson) {
265
+ const parsed = JSON.parse(ctx.planJson);
266
+ if (parsed?.steps) {
267
+ plan = {
268
+ summary: parsed.summary ?? '',
269
+ steps: (parsed.steps as Array<Record<string, unknown>>).map((s) => ({
270
+ id: String(s.id ?? ''),
271
+ operation: String(s.operation ?? ''),
272
+ description: String(s.description ?? ''),
273
+ ...(s.args ? { args: s.args as Record<string, unknown> } : {}),
274
+ })),
275
+ };
276
+ }
277
+ }
234
278
  }
235
- } catch { /* stepLog extraction is best-effort */ }
279
+ } catch { /* extraction is best-effort */ }
236
280
 
237
281
  await notifier({
238
282
  type: 'workflow-complete',
@@ -248,6 +292,10 @@ export async function runWorkflow(
248
292
  id: runId, workflowFile: absPath, startedAt, success, outcome: outcome as RunOutcome, summary,
249
293
  functionName: execResult.functionName, executionTime: execResult.executionTime,
250
294
  dryRun: false, provider: providerConfig.name, params: options?.params, stepLog,
295
+ trace: collectedTrace.length > 0 ? collectedTrace : undefined,
296
+ plan,
297
+ cost: costSummary,
298
+ auditTrail: collectedAudit.length > 0 ? collectedAudit : undefined,
251
299
  }, verbose);
252
300
 
253
301
  auditEmit('run-complete', { success, outcome, summary });
@@ -275,6 +323,9 @@ export async function runWorkflow(
275
323
  recordRun(store, {
276
324
  id: runId, workflowFile: absPath, startedAt, success: false, outcome: 'error', summary: msg,
277
325
  dryRun: options?.dryRun ?? false, provider: providerConfig.name, params: options?.params,
326
+ trace: collectedTrace.length > 0 ? collectedTrace : undefined,
327
+ cost: costSummary,
328
+ auditTrail: collectedAudit.length > 0 ? collectedAudit : undefined,
278
329
  }, verbose);
279
330
 
280
331
  auditEmit('run-complete', { success: false, error: msg });
@@ -285,15 +336,11 @@ export async function runWorkflow(
285
336
  }
286
337
  }
287
338
 
339
+ type RunRecord = import('./types.js').RunRecord;
340
+
288
341
  function recordRun(
289
342
  store: RunStore | null,
290
- data: {
291
- id: string; workflowFile: string; startedAt: string; success: boolean;
292
- outcome: RunOutcome; summary: string; functionName?: string;
293
- executionTime?: number; dryRun: boolean; provider?: string;
294
- params?: Record<string, unknown>;
295
- stepLog?: import('./types.js').StepLogEntry[];
296
- },
343
+ data: Omit<RunRecord, 'finishedAt' | 'durationMs'>,
297
344
  verbose: boolean,
298
345
  ): void {
299
346
  if (!store) return;
package/src/bot/types.ts CHANGED
@@ -194,6 +194,14 @@ export interface RunRecord {
194
194
  pipelineName?: string;
195
195
  stageName?: string;
196
196
  stepLog?: StepLogEntry[];
197
+ /** Node-by-node execution trace (node-start/complete/error events). */
198
+ trace?: ExecutionEvent[];
199
+ /** AI-generated plan for this run. */
200
+ plan?: { summary: string; steps: Array<{ id: string; operation: string; description: string; args?: Record<string, unknown> }> };
201
+ /** Token usage and estimated cost. */
202
+ cost?: RunCostSummary;
203
+ /** Lifecycle audit events (plan-created, approval, step-start/complete, etc.). */
204
+ auditTrail?: AuditEvent[];
197
205
  }
198
206
 
199
207
  export interface RunFilter {