@synergenius/flow-weaver-pack-weaver 0.9.59 → 0.9.77

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/dist/ai-chat-provider.d.ts +12 -0
  2. package/dist/ai-chat-provider.d.ts.map +1 -1
  3. package/dist/ai-chat-provider.js +351 -335
  4. package/dist/ai-chat-provider.js.map +1 -1
  5. package/dist/bot/agent-loop.d.ts +20 -0
  6. package/dist/bot/agent-loop.d.ts.map +1 -0
  7. package/dist/bot/agent-loop.js +331 -0
  8. package/dist/bot/agent-loop.js.map +1 -0
  9. package/dist/bot/ai-router.d.ts +19 -0
  10. package/dist/bot/ai-router.d.ts.map +1 -0
  11. package/dist/bot/ai-router.js +104 -0
  12. package/dist/bot/ai-router.js.map +1 -0
  13. package/dist/bot/assistant-tools.d.ts.map +1 -1
  14. package/dist/bot/assistant-tools.js +49 -33
  15. package/dist/bot/assistant-tools.js.map +1 -1
  16. package/dist/bot/async-mutex.d.ts +13 -0
  17. package/dist/bot/async-mutex.d.ts.map +1 -0
  18. package/dist/bot/async-mutex.js +37 -0
  19. package/dist/bot/async-mutex.js.map +1 -0
  20. package/dist/bot/bot-manager.d.ts +2 -2
  21. package/dist/bot/bot-manager.d.ts.map +1 -1
  22. package/dist/bot/bot-manager.js +3 -3
  23. package/dist/bot/bot-manager.js.map +1 -1
  24. package/dist/bot/bot-registry.js +2 -2
  25. package/dist/bot/bot-registry.js.map +1 -1
  26. package/dist/bot/conversation-store.d.ts +1 -0
  27. package/dist/bot/conversation-store.d.ts.map +1 -1
  28. package/dist/bot/conversation-store.js.map +1 -1
  29. package/dist/bot/dashboard.d.ts.map +1 -1
  30. package/dist/bot/dashboard.js +17 -8
  31. package/dist/bot/dashboard.js.map +1 -1
  32. package/dist/bot/improve-loop.js.map +1 -1
  33. package/dist/bot/index.d.ts +2 -4
  34. package/dist/bot/index.d.ts.map +1 -1
  35. package/dist/bot/index.js +1 -2
  36. package/dist/bot/index.js.map +1 -1
  37. package/dist/bot/instance-manager.d.ts +31 -0
  38. package/dist/bot/instance-manager.d.ts.map +1 -0
  39. package/dist/bot/instance-manager.js +115 -0
  40. package/dist/bot/instance-manager.js.map +1 -0
  41. package/dist/bot/orchestrator.d.ts +36 -0
  42. package/dist/bot/orchestrator.d.ts.map +1 -0
  43. package/dist/bot/orchestrator.js +176 -0
  44. package/dist/bot/orchestrator.js.map +1 -0
  45. package/dist/bot/profile-store.d.ts +36 -0
  46. package/dist/bot/profile-store.d.ts.map +1 -0
  47. package/dist/bot/profile-store.js +208 -0
  48. package/dist/bot/profile-store.js.map +1 -0
  49. package/dist/bot/profile-types.d.ts +126 -0
  50. package/dist/bot/profile-types.d.ts.map +1 -0
  51. package/dist/bot/profile-types.js +7 -0
  52. package/dist/bot/profile-types.js.map +1 -0
  53. package/dist/bot/run-store.d.ts.map +1 -1
  54. package/dist/bot/run-store.js +8 -0
  55. package/dist/bot/run-store.js.map +1 -1
  56. package/dist/bot/runner.d.ts +4 -0
  57. package/dist/bot/runner.d.ts.map +1 -1
  58. package/dist/bot/runner.js +5 -1
  59. package/dist/bot/runner.js.map +1 -1
  60. package/dist/bot/swarm-controller.d.ts +109 -0
  61. package/dist/bot/swarm-controller.d.ts.map +1 -0
  62. package/dist/bot/swarm-controller.js +640 -0
  63. package/dist/bot/swarm-controller.js.map +1 -0
  64. package/dist/bot/swarm-event-log.d.ts +28 -0
  65. package/dist/bot/swarm-event-log.d.ts.map +1 -0
  66. package/dist/bot/swarm-event-log.js +54 -0
  67. package/dist/bot/swarm-event-log.js.map +1 -0
  68. package/dist/bot/task-prompt-builder.d.ts +22 -0
  69. package/dist/bot/task-prompt-builder.d.ts.map +1 -0
  70. package/dist/bot/task-prompt-builder.js +240 -0
  71. package/dist/bot/task-prompt-builder.js.map +1 -0
  72. package/dist/bot/task-store.d.ts +21 -0
  73. package/dist/bot/task-store.d.ts.map +1 -0
  74. package/dist/bot/task-store.js +364 -0
  75. package/dist/bot/task-store.js.map +1 -0
  76. package/dist/bot/task-types.d.ts +79 -0
  77. package/dist/bot/task-types.d.ts.map +1 -0
  78. package/dist/bot/task-types.js +6 -0
  79. package/dist/bot/task-types.js.map +1 -0
  80. package/dist/bot/types.d.ts +8 -0
  81. package/dist/bot/types.d.ts.map +1 -1
  82. package/dist/cli-handlers.d.ts.map +1 -1
  83. package/dist/cli-handlers.js +79 -54
  84. package/dist/cli-handlers.js.map +1 -1
  85. package/dist/cli.d.ts +3 -0
  86. package/dist/cli.d.ts.map +1 -0
  87. package/dist/cli.js +749 -0
  88. package/dist/cli.js.map +1 -0
  89. package/dist/docs/docs/weaver-bot-usage.md +35 -18
  90. package/dist/docs/docs/weaver-config.md +20 -0
  91. package/dist/docs/docs/weaver-task-queue.md +31 -19
  92. package/dist/docs/weaver-config.md +15 -9
  93. package/dist/index.d.ts +2 -2
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +1 -1
  96. package/dist/index.js.map +1 -1
  97. package/dist/mcp-tools.d.ts +17 -0
  98. package/dist/mcp-tools.d.ts.map +1 -1
  99. package/dist/mcp-tools.js +98 -279
  100. package/dist/mcp-tools.js.map +1 -1
  101. package/dist/node-types/bot-report.d.ts.map +1 -1
  102. package/dist/node-types/bot-report.js +6 -24
  103. package/dist/node-types/bot-report.js.map +1 -1
  104. package/dist/node-types/orchestrator-dispatch.d.ts +17 -0
  105. package/dist/node-types/orchestrator-dispatch.d.ts.map +1 -0
  106. package/dist/node-types/orchestrator-dispatch.js +63 -0
  107. package/dist/node-types/orchestrator-dispatch.js.map +1 -0
  108. package/dist/node-types/orchestrator-load-state.d.ts +16 -0
  109. package/dist/node-types/orchestrator-load-state.d.ts.map +1 -0
  110. package/dist/node-types/orchestrator-load-state.js +60 -0
  111. package/dist/node-types/orchestrator-load-state.js.map +1 -0
  112. package/dist/node-types/orchestrator-route.d.ts +16 -0
  113. package/dist/node-types/orchestrator-route.d.ts.map +1 -0
  114. package/dist/node-types/orchestrator-route.js +28 -0
  115. package/dist/node-types/orchestrator-route.js.map +1 -0
  116. package/dist/node-types/receive-task.d.ts +2 -3
  117. package/dist/node-types/receive-task.d.ts.map +1 -1
  118. package/dist/node-types/receive-task.js +3 -48
  119. package/dist/node-types/receive-task.js.map +1 -1
  120. package/dist/templates/weaver-template.d.ts +11 -0
  121. package/dist/templates/weaver-template.d.ts.map +1 -0
  122. package/dist/templates/weaver-template.js +53 -0
  123. package/dist/templates/weaver-template.js.map +1 -0
  124. package/dist/ui/bot-activity.js +2 -2
  125. package/dist/ui/bot-constants.d.ts +14 -0
  126. package/dist/ui/bot-constants.d.ts.map +1 -0
  127. package/dist/ui/bot-constants.js +189 -0
  128. package/dist/ui/bot-constants.js.map +1 -0
  129. package/dist/ui/bot-panel.js +207 -245
  130. package/dist/ui/bot-slot-card.js +141 -0
  131. package/dist/ui/budget-bar.js +59 -0
  132. package/dist/ui/chat-task-result.js +178 -0
  133. package/dist/ui/decision-log.js +136 -0
  134. package/dist/ui/profile-card.js +158 -0
  135. package/dist/ui/profile-editor.js +597 -0
  136. package/dist/ui/swarm-controls.js +245 -0
  137. package/dist/ui/swarm-dashboard.js +3012 -0
  138. package/dist/ui/task-create-form.js +98 -0
  139. package/dist/ui/task-detail-view.js +1044 -0
  140. package/dist/ui/task-pool-list.js +156 -0
  141. package/dist/workflows/orchestrator.d.ts +21 -0
  142. package/dist/workflows/orchestrator.d.ts.map +1 -0
  143. package/dist/workflows/orchestrator.js +281 -0
  144. package/dist/workflows/orchestrator.js.map +1 -0
  145. package/dist/workflows/weaver-bot-session.d.ts +65 -0
  146. package/dist/workflows/weaver-bot-session.d.ts.map +1 -0
  147. package/dist/workflows/weaver-bot-session.js +68 -0
  148. package/dist/workflows/weaver-bot-session.js.map +1 -0
  149. package/dist/workflows/weaver.d.ts +24 -0
  150. package/dist/workflows/weaver.d.ts.map +1 -0
  151. package/dist/workflows/weaver.js +28 -0
  152. package/dist/workflows/weaver.js.map +1 -0
  153. package/flowweaver.manifest.json +547 -133
  154. package/package.json +1 -1
  155. package/src/ai-chat-provider.ts +378 -371
  156. package/src/bot/ai-router.ts +132 -0
  157. package/src/bot/assistant-tools.ts +47 -29
  158. package/src/bot/async-mutex.ts +37 -0
  159. package/src/bot/bot-manager.ts +3 -3
  160. package/src/bot/bot-registry.ts +2 -2
  161. package/src/bot/conversation-store.ts +2 -1
  162. package/src/bot/dashboard.ts +17 -8
  163. package/src/bot/improve-loop.ts +6 -6
  164. package/src/bot/index.ts +2 -4
  165. package/src/bot/instance-manager.ts +128 -0
  166. package/src/bot/orchestrator.ts +244 -0
  167. package/src/bot/profile-store.ts +225 -0
  168. package/src/bot/profile-types.ts +141 -0
  169. package/src/bot/run-store.ts +8 -0
  170. package/src/bot/runner.ts +9 -1
  171. package/src/bot/swarm-controller.ts +780 -0
  172. package/src/bot/swarm-event-log.ts +57 -0
  173. package/src/bot/task-prompt-builder.ts +309 -0
  174. package/src/bot/task-store.ts +407 -0
  175. package/src/bot/task-types.ts +100 -0
  176. package/src/bot/types.ts +8 -0
  177. package/src/cli-handlers.ts +78 -53
  178. package/src/docs/weaver-bot-usage.md +35 -18
  179. package/src/docs/weaver-config.md +20 -0
  180. package/src/docs/weaver-task-queue.md +31 -19
  181. package/src/index.ts +5 -4
  182. package/src/mcp-tools.ts +129 -372
  183. package/src/node-types/bot-report.ts +6 -24
  184. package/src/node-types/orchestrator-dispatch.ts +71 -0
  185. package/src/node-types/orchestrator-load-state.ts +66 -0
  186. package/src/node-types/orchestrator-route.ts +33 -0
  187. package/src/node-types/receive-task.ts +3 -57
  188. package/src/ui/bot-activity.tsx +2 -2
  189. package/src/ui/bot-constants.ts +192 -0
  190. package/src/ui/bot-panel.tsx +213 -247
  191. package/src/ui/bot-slot-card.tsx +139 -0
  192. package/src/ui/budget-bar.tsx +30 -0
  193. package/src/ui/chat-task-result.tsx +236 -0
  194. package/src/ui/decision-log.tsx +148 -0
  195. package/src/ui/profile-card.tsx +157 -0
  196. package/src/ui/profile-editor.tsx +384 -0
  197. package/src/ui/swarm-controls.tsx +260 -0
  198. package/src/ui/swarm-dashboard.tsx +647 -0
  199. package/src/ui/task-create-form.tsx +87 -0
  200. package/src/ui/task-detail-view.tsx +841 -0
  201. package/src/ui/task-pool-list.tsx +187 -0
  202. package/src/workflows/orchestrator.ts +302 -0
  203. package/dist/docs/weaver-bot-usage.md +0 -34
  204. package/dist/docs/weaver-genesis.md +0 -32
  205. package/dist/docs/weaver-task-queue.md +0 -34
  206. package/dist/ui/bot-workspace.js +0 -1015
  207. package/dist/ui/chat-bot-result.js +0 -71
  208. package/dist/ui/queue-input.js +0 -82
  209. package/dist/ui/session-bar.js +0 -174
  210. package/src/bot/error-guide.ts +0 -4
  211. package/src/bot/retry-utils.ts +0 -4
  212. package/src/bot/session-state.ts +0 -116
  213. package/src/bot/task-queue.ts +0 -262
  214. package/src/ui/bot-workspace.tsx +0 -442
  215. package/src/ui/chat-bot-result.tsx +0 -81
  216. package/src/ui/queue-input.tsx +0 -56
  217. package/src/ui/session-bar.tsx +0 -157
@@ -0,0 +1,139 @@
1
+ /**
2
+ * BotSlotCard — single row in the bot table for the swarm dashboard.
3
+ * Displays bot name, status, current task, tokens, and cost in a table row layout.
4
+ */
5
+ import React from 'react';
6
+ import { Flex, Typography, Icon, IconButton } from '@fw/plugin-ui-kit';
7
+
8
+ // ---------------------------------------------------------------------------
9
+ // Types
10
+ // ---------------------------------------------------------------------------
11
+
12
+ interface InstanceSlot {
13
+ botId: string;
14
+ botName: string;
15
+ status: 'idle' | 'executing' | 'paused' | 'stopped';
16
+ currentTaskId?: string;
17
+ currentRunId?: string;
18
+ startedAt?: string;
19
+ tokensUsed: number;
20
+ cost: number;
21
+ }
22
+
23
+ interface BotSlotCardProps {
24
+ bot: InstanceSlot;
25
+ currentTaskTitle?: string;
26
+ profileName?: string;
27
+ botDisplayName?: string;
28
+ botIcon?: string;
29
+ botColor?: string;
30
+ onPause?: (botId: string) => void;
31
+ onResume?: (botId: string) => void;
32
+ onStop?: (botId: string) => void;
33
+ }
34
+
35
+ // ---------------------------------------------------------------------------
36
+ // Helpers
37
+ // ---------------------------------------------------------------------------
38
+
39
+ const statusToLabel: Record<string, string> = {
40
+ idle: 'Idle',
41
+ executing: 'Executing',
42
+ paused: 'Paused',
43
+ stopped: 'Stopped',
44
+ };
45
+
46
+ function formatTokens(n: number): string {
47
+ if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
48
+ if (n >= 1_000) return `${(n / 1_000).toFixed(1)}k`;
49
+ return String(n);
50
+ }
51
+
52
+ function formatCost(n: number): string {
53
+ if (n < 0.01 && n > 0) return '<$0.01';
54
+ return `$${n.toFixed(2)}`;
55
+ }
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // Component
59
+ // ---------------------------------------------------------------------------
60
+
61
+ function BotSlotCard({ bot, currentTaskTitle, profileName, botDisplayName, botIcon, botColor, onPause, onResume, onStop }: BotSlotCardProps) {
62
+ const { botId, botName, status, currentTaskId, tokensUsed, cost } = bot;
63
+ const isExecuting = status === 'executing';
64
+ const isPaused = status === 'paused';
65
+ const label = statusToLabel[status] ?? status;
66
+ const taskText = isExecuting ? (currentTaskTitle || currentTaskId || '-') : '-';
67
+
68
+ return React.createElement(Flex, {
69
+ variant: 'row-center-start-nowrap-8',
70
+ style: { padding: '6px 16px', minHeight: '38px', borderBottom: '1px solid var(--color-border-default)' },
71
+ },
72
+ // Instance name
73
+ React.createElement(Typography, {
74
+ variant: 'smallCaption-regular', color: 'color-text-high',
75
+ style: { width: '120px', flexShrink: 0 },
76
+ }, profileName ? `${profileName} #${botId.split('-').pop() ?? '0'}` : botName),
77
+
78
+ // Bot (icon + name)
79
+ React.createElement(Flex, {
80
+ variant: 'row-center-start-nowrap-4',
81
+ style: { width: '110px', flexShrink: 0, color: botColor ? `var(--${botColor})` : undefined },
82
+ },
83
+ React.createElement(Icon, { name: botIcon || 'smartToy', size: 12 }),
84
+ React.createElement(Typography, {
85
+ variant: 'smallCaption-regular', color: 'color-text-subtle',
86
+ }, botDisplayName || '-'),
87
+ ),
88
+
89
+ // Status
90
+ React.createElement(Typography, {
91
+ variant: 'smallCaption-regular',
92
+ color: isExecuting ? 'color-brand-main' : 'color-text-subtle',
93
+ style: { width: '70px', flexShrink: 0 },
94
+ }, label),
95
+
96
+ // Current task
97
+ React.createElement(Typography, {
98
+ variant: 'smallCaption-regular',
99
+ color: isExecuting ? 'color-text-medium' : 'color-text-subtle',
100
+ style: { flex: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' },
101
+ }, taskText),
102
+
103
+ // Tokens
104
+ React.createElement(Typography, {
105
+ variant: 'smallCaption-regular',
106
+ color: 'color-text-subtle',
107
+ style: { width: '50px', flexShrink: 0, textAlign: 'right' },
108
+ }, formatTokens(tokensUsed)),
109
+
110
+ // Cost
111
+ React.createElement(Typography, {
112
+ variant: 'smallCaption-regular',
113
+ color: 'color-text-subtle',
114
+ style: { width: '50px', flexShrink: 0, textAlign: 'right' },
115
+ }, formatCost(cost)),
116
+
117
+ // Actions
118
+ React.createElement(Flex, {
119
+ variant: 'row-center-end-nowrap-1',
120
+ style: { width: '50px', flexShrink: 0 },
121
+ },
122
+ isExecuting && onPause && React.createElement(IconButton, {
123
+ icon: 'pause', size: 'xs', variant: 'clear',
124
+ onClick: () => onPause(botId), title: 'Pause',
125
+ }),
126
+ isPaused && onResume && React.createElement(IconButton, {
127
+ icon: 'playArrow', size: 'xs', variant: 'clear',
128
+ onClick: () => onResume(botId), title: 'Resume',
129
+ }),
130
+ (isExecuting || isPaused) && onStop && React.createElement(IconButton, {
131
+ icon: 'stop', size: 'xs', variant: 'clear', color: 'danger',
132
+ onClick: () => onStop(botId), title: 'Stop',
133
+ }),
134
+ ),
135
+ );
136
+ }
137
+
138
+ export { BotSlotCard };
139
+ export default BotSlotCard;
@@ -0,0 +1,30 @@
1
+ const React = require('react');
2
+ const { Flex, Typography } = require('@fw/plugin-ui-kit');
3
+
4
+ function BudgetBar({ label, used, limit, unit }: {
5
+ label: string; used: number; limit: number; unit: string;
6
+ }) {
7
+ const pct = limit > 0 ? Math.min(100, (used / limit) * 100) : 0;
8
+ const color = pct > 90 ? 'var(--color-status-negative)' : pct > 70 ? 'var(--color-status-caution)' : 'var(--color-brand-main)';
9
+
10
+ return React.createElement(Flex, { variant: 'column-start-start-nowrap-2', style: { width: '100%' } },
11
+ React.createElement(Flex, { variant: 'row-center-space-between-nowrap-4' },
12
+ React.createElement(Typography, { variant: 'smallCaption-regular', color: 'color-text-subtle' }, label),
13
+ React.createElement(Typography, { variant: 'smallCaption-regular', color: 'color-text-subtle' },
14
+ `${used.toLocaleString()} / ${limit.toLocaleString()} ${unit}`),
15
+ ),
16
+ React.createElement(Flex, {
17
+ variant: 'row-start-start-nowrap-0',
18
+ style: { width: '100%', height: '4px', borderRadius: '2px', backgroundColor: 'var(--color-surface-raised)', overflow: 'hidden' },
19
+ },
20
+ React.createElement(Flex, {
21
+ variant: 'row-start-start-nowrap-0',
22
+ style: { width: `${pct}%`, height: '100%', borderRadius: '2px', backgroundColor: color, transition: 'width 0.3s' },
23
+ }),
24
+ ),
25
+ );
26
+ }
27
+
28
+ export { BudgetBar };
29
+ export default BudgetBar;
30
+ module.exports = BudgetBar;
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Chat Task Result — compact inline card for the AI chat thread.
3
+ *
4
+ * Renders when `fw_weaver_task_create` returns a result. Shows task title,
5
+ * live status (polls fw_weaver_task_get every 3s), subtask progress for
6
+ * parent tasks, assigned bot, and an "Open Dashboard" button.
7
+ *
8
+ * Runs in the pack sandbox — CommonJS require, React.createElement throughout.
9
+ */
10
+ const React = require('react');
11
+ const { useState, useEffect, useCallback, useRef } = React;
12
+ const { Flex, Typography, StatusIcon, Button } = require('@fw/plugin-ui-kit');
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Types
16
+ // ---------------------------------------------------------------------------
17
+
18
+ interface TaskData {
19
+ id: string;
20
+ title: string;
21
+ status: 'pending' | 'in-progress' | 'blocked' | 'done' | 'failed' | 'cancelled';
22
+ isParent: boolean;
23
+ currentBotId?: string;
24
+ assignedProfile?: string;
25
+ }
26
+
27
+ interface SubtaskData {
28
+ id: string;
29
+ title: string;
30
+ status: 'pending' | 'in-progress' | 'blocked' | 'done' | 'failed' | 'cancelled';
31
+ }
32
+
33
+ interface ChatTaskResultProps {
34
+ result: unknown;
35
+ toolName: string;
36
+ args: Record<string, unknown>;
37
+ callTool: (tool: string, args?: Record<string, unknown>) => Promise<unknown>;
38
+ openWorkspace: (data: Record<string, unknown>) => void;
39
+ }
40
+
41
+ // ---------------------------------------------------------------------------
42
+ // Helpers
43
+ // ---------------------------------------------------------------------------
44
+
45
+ function parseResult(result: unknown): { task: TaskData | null; subtasks: SubtaskData[] } {
46
+ try {
47
+ const parsed = typeof result === 'string' ? JSON.parse(result) : result;
48
+ if (!parsed || typeof parsed !== 'object') return { task: null, subtasks: [] };
49
+ const obj = parsed as Record<string, unknown>;
50
+ const task = (obj.task ?? parsed) as TaskData | null;
51
+ const subtasks = (Array.isArray(obj.subtasks) ? obj.subtasks : []) as SubtaskData[];
52
+ return { task: task?.id ? task : null, subtasks };
53
+ } catch {
54
+ return { task: null, subtasks: [] };
55
+ }
56
+ }
57
+
58
+ type StatusKind = 'running' | 'completed' | 'failed' | 'pending';
59
+
60
+ function statusToIcon(status: TaskData['status']): StatusKind {
61
+ switch (status) {
62
+ case 'pending':
63
+ case 'blocked':
64
+ return 'pending';
65
+ case 'in-progress':
66
+ return 'running';
67
+ case 'done':
68
+ return 'completed';
69
+ case 'failed':
70
+ case 'cancelled':
71
+ return 'failed';
72
+ default:
73
+ return 'pending';
74
+ }
75
+ }
76
+
77
+ function statusLabel(status: TaskData['status']): string {
78
+ switch (status) {
79
+ case 'pending': return 'Pending';
80
+ case 'in-progress': return 'In Progress';
81
+ case 'blocked': return 'Blocked';
82
+ case 'done': return 'Done';
83
+ case 'failed': return 'Failed';
84
+ case 'cancelled': return 'Cancelled';
85
+ default: return status;
86
+ }
87
+ }
88
+
89
+ function isTerminal(status: TaskData['status']): boolean {
90
+ return status === 'done' || status === 'failed' || status === 'cancelled';
91
+ }
92
+
93
+ // ---------------------------------------------------------------------------
94
+ // Component
95
+ // ---------------------------------------------------------------------------
96
+
97
+ function ChatTaskResult({ result, args, callTool, openWorkspace }: ChatTaskResultProps) {
98
+ const initial = parseResult(result);
99
+ const [task, setTask] = useState<TaskData | null>(initial.task);
100
+ const [subtasks, setSubtasks] = useState<SubtaskData[]>(initial.subtasks);
101
+ const mountedRef = useRef(true);
102
+
103
+ useEffect(() => {
104
+ mountedRef.current = true;
105
+ return () => { mountedRef.current = false; };
106
+ }, []);
107
+
108
+ // Poll fw_weaver_task_get every 3s until terminal
109
+ useEffect(() => {
110
+ if (!task?.id) return;
111
+ if (isTerminal(task.status)) return;
112
+
113
+ const poll = async () => {
114
+ try {
115
+ const raw = await callTool('fw_weaver_task_get', { id: task.id });
116
+ if (!mountedRef.current) return;
117
+ const { task: updated, subtasks: updatedSubs } = parseResult(raw);
118
+ if (updated) {
119
+ setTask(updated);
120
+ if (updatedSubs.length > 0) setSubtasks(updatedSubs);
121
+ }
122
+ } catch {
123
+ // non-fatal — keep polling
124
+ }
125
+ };
126
+
127
+ const interval = setInterval(poll, 3000);
128
+ // Initial poll after a short delay to catch quick updates
129
+ const timeout = setTimeout(poll, 1000);
130
+ return () => {
131
+ clearInterval(interval);
132
+ clearTimeout(timeout);
133
+ };
134
+ }, [task?.id, task?.status, callTool]);
135
+
136
+ // If we couldn't parse a task, show a minimal fallback
137
+ if (!task) {
138
+ return React.createElement(Flex, {
139
+ variant: 'row-center-start-nowrap-10',
140
+ style: {
141
+ borderRadius: 'var(--border-radius-regular)',
142
+ border: '1px solid var(--color-border-default)',
143
+ backgroundColor: 'var(--color-surface-low)',
144
+ },
145
+ },
146
+ React.createElement(StatusIcon, { status: 'pending', size: 'sm' }),
147
+ React.createElement(Typography, {
148
+ variant: 'caption-thick', color: 'color-text-medium',
149
+ }, 'Task created'),
150
+ );
151
+ }
152
+
153
+ // Subtask progress
154
+ const doneCount = subtasks.filter(s => s.status === 'done').length;
155
+ const totalCount = subtasks.length;
156
+ const hasSubtasks = task.isParent && totalCount > 0;
157
+
158
+ // Bot info
159
+ const botLabel = task.currentBotId
160
+ ? `Bot: ${task.currentBotId}`
161
+ : task.assignedProfile
162
+ ? `Profile: ${task.assignedProfile}`
163
+ : null;
164
+
165
+ const handleOpenDashboard = useCallback(() => {
166
+ openWorkspace({
167
+ packId: '@synergenius/flow-weaver-pack-weaver',
168
+ taskId: task.id,
169
+ });
170
+ }, [openWorkspace, task.id]);
171
+
172
+ return React.createElement(Flex, {
173
+ variant: 'row-center-start-nowrap-10',
174
+ style: {
175
+ borderRadius: 'var(--border-radius-regular)',
176
+ border: '1px solid var(--color-border-default)',
177
+ backgroundColor: 'var(--color-surface-low)',
178
+ },
179
+ },
180
+ // Status icon
181
+ React.createElement(StatusIcon, {
182
+ status: statusToIcon(task.status),
183
+ size: 'sm',
184
+ }),
185
+
186
+ // Title + meta column
187
+ React.createElement(Flex, {
188
+ variant: 'column-start-start-nowrap-2',
189
+ style: { flex: 1, minWidth: 0 },
190
+ },
191
+ // Title row
192
+ React.createElement(Typography, {
193
+ variant: 'caption-thick',
194
+ color: 'color-text-high',
195
+ truncate: true,
196
+ }, task.title),
197
+
198
+ // Status + subtask progress + bot
199
+ React.createElement(Flex, {
200
+ variant: 'row-center-start-wrap-8',
201
+ },
202
+ // Status label
203
+ React.createElement(Typography, {
204
+ variant: 'smallCaption-regular',
205
+ color: task.status === 'done' ? 'color-text-positive' :
206
+ task.status === 'failed' ? 'color-text-negative' :
207
+ task.status === 'in-progress' ? 'color-text-info' :
208
+ 'color-text-medium',
209
+ }, statusLabel(task.status)),
210
+
211
+ // Subtask progress
212
+ hasSubtasks && React.createElement(Typography, {
213
+ variant: 'smallCaption-regular',
214
+ color: 'color-text-medium',
215
+ }, `${doneCount}/${totalCount} done`),
216
+
217
+ // Bot
218
+ botLabel && React.createElement(Typography, {
219
+ variant: 'smallCaption-regular',
220
+ color: 'color-text-low',
221
+ truncate: true,
222
+ }, botLabel),
223
+ ),
224
+ ),
225
+
226
+ // Open Dashboard button
227
+ React.createElement(Button, {
228
+ size: 'xs',
229
+ variant: 'clear',
230
+ color: 'secondary',
231
+ onClick: handleOpenDashboard,
232
+ }, 'Open Dashboard'),
233
+ );
234
+ }
235
+
236
+ module.exports = ChatTaskResult;
@@ -0,0 +1,148 @@
1
+ /**
2
+ * DecisionLog — displays recent orchestrator routing decisions.
3
+ *
4
+ * Each decision shows timestamp, task title, assigned instance, routing method,
5
+ * and the reasoning. AI-routed decisions also show confidence percentage.
6
+ *
7
+ * Pattern: CommonJS require for platform deps, React.createElement throughout.
8
+ */
9
+ const React = require('react');
10
+ const {
11
+ Flex, Typography, ScrollArea, Tag, SectionTitle,
12
+ } = require('@fw/plugin-ui-kit');
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Types
16
+ // ---------------------------------------------------------------------------
17
+
18
+ type RoutingMethod = 'exact-match' | 'single-eligible' | 'ai-routed' | 'round-robin' | 'manual';
19
+
20
+ interface Decision {
21
+ id: number;
22
+ timestamp: number;
23
+ taskId: string;
24
+ taskTitle: string;
25
+ assignedProfileId: string;
26
+ assignedInstanceId: string;
27
+ reason: string;
28
+ method: RoutingMethod;
29
+ confidence?: number;
30
+ }
31
+
32
+ interface DecisionLogProps {
33
+ decisions: Decision[];
34
+ }
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // Helpers
38
+ // ---------------------------------------------------------------------------
39
+
40
+ const methodColors: Record<RoutingMethod, string> = {
41
+ 'exact-match': 'info',
42
+ 'single-eligible': 'positive',
43
+ 'ai-routed': 'secondary',
44
+ 'round-robin': 'secondary',
45
+ 'manual': 'caution',
46
+ };
47
+
48
+ const methodLabels: Record<RoutingMethod, string> = {
49
+ 'exact-match': 'exact',
50
+ 'single-eligible': 'single',
51
+ 'ai-routed': 'ai',
52
+ 'round-robin': 'robin',
53
+ 'manual': 'manual',
54
+ };
55
+
56
+ function formatTime(ts: number): string {
57
+ const d = new Date(ts);
58
+ return d.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
59
+ }
60
+
61
+ function truncate(text: string, max: number): string {
62
+ return text.length > max ? text.slice(0, max - 1) + '\u2026' : text;
63
+ }
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Component
67
+ // ---------------------------------------------------------------------------
68
+
69
+ function DecisionLog({ decisions }: DecisionLogProps) {
70
+ if (!decisions || decisions.length === 0) {
71
+ return React.createElement(Flex, {
72
+ variant: 'column-stretch-start-nowrap-4',
73
+ },
74
+ React.createElement(SectionTitle, null, 'Decision Log'),
75
+ React.createElement(Typography, {
76
+ variant: 'smallCaption-regular', color: 'color-text-subtle',
77
+ }, 'No routing decisions yet.'),
78
+ );
79
+ }
80
+
81
+ // Show newest first
82
+ const sorted = [...decisions].sort((a, b) => b.timestamp - a.timestamp);
83
+
84
+ return React.createElement(Flex, {
85
+ variant: 'column-stretch-start-nowrap-4',
86
+ },
87
+ React.createElement(SectionTitle, null, 'Decision Log'),
88
+ React.createElement(ScrollArea, {
89
+ style: { maxHeight: '260px' },
90
+ },
91
+ React.createElement(Flex, {
92
+ variant: 'column-stretch-start-nowrap-0',
93
+ },
94
+ ...sorted.map((d: Decision) => {
95
+ const methodLabel = d.method === 'ai-routed' && d.confidence != null
96
+ ? `ai ${d.confidence}%`
97
+ : methodLabels[d.method] || d.method;
98
+
99
+ return React.createElement(Flex, {
100
+ key: d.id,
101
+ variant: 'column-stretch-start-nowrap-2',
102
+ style: {
103
+ padding: '6px 8px',
104
+ borderBottom: '1px solid var(--color-border-default)',
105
+ },
106
+ },
107
+ // Main line: time, title, instance, method
108
+ React.createElement(Flex, { variant: 'row-center-start-nowrap-8' },
109
+ React.createElement(Typography, {
110
+ variant: 'smallCaption-regular',
111
+ color: 'color-text-subtle',
112
+ style: { flexShrink: 0, fontFamily: 'var(--font-mono, monospace)', fontSize: '11px' },
113
+ }, formatTime(d.timestamp)),
114
+
115
+ React.createElement(Typography, {
116
+ variant: 'smallCaption-regular',
117
+ color: 'color-text-high',
118
+ style: { flex: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' },
119
+ }, `"${truncate(d.taskTitle, 40)}"`),
120
+
121
+ React.createElement(Typography, {
122
+ variant: 'smallCaption-regular',
123
+ color: 'color-text-medium',
124
+ style: { flexShrink: 0 },
125
+ }, `\u2192 ${d.assignedInstanceId}`),
126
+
127
+ React.createElement(Tag, {
128
+ size: 'small',
129
+ color: methodColors[d.method] || 'secondary',
130
+ }, methodLabel),
131
+ ),
132
+
133
+ // Reason line
134
+ React.createElement(Typography, {
135
+ variant: 'smallCaption-regular',
136
+ color: 'color-text-subtle',
137
+ style: { paddingLeft: '70px' },
138
+ }, d.reason),
139
+ );
140
+ }),
141
+ ),
142
+ ),
143
+ );
144
+ }
145
+
146
+ export { DecisionLog };
147
+ export default DecisionLog;
148
+ module.exports = DecisionLog;