@metabob/minibob 0.1.2

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 (174) hide show
  1. package/ARCHITECTURE.md +255 -0
  2. package/CHANGELOG.md +112 -0
  3. package/README.md +380 -0
  4. package/bin/minibob.js +36 -0
  5. package/dist/acp-gossip.d.ts +72 -0
  6. package/dist/acp-gossip.d.ts.map +1 -0
  7. package/dist/acp-gossip.js +156 -0
  8. package/dist/acp-gossip.js.map +1 -0
  9. package/dist/acp.d.ts +62 -0
  10. package/dist/acp.d.ts.map +1 -0
  11. package/dist/acp.js +292 -0
  12. package/dist/acp.js.map +1 -0
  13. package/dist/activity.d.ts +157 -0
  14. package/dist/activity.d.ts.map +1 -0
  15. package/dist/activity.js +518 -0
  16. package/dist/activity.js.map +1 -0
  17. package/dist/agent-runtime.d.ts +104 -0
  18. package/dist/agent-runtime.d.ts.map +1 -0
  19. package/dist/boredom.d.ts +125 -0
  20. package/dist/boredom.d.ts.map +1 -0
  21. package/dist/boredom.js +244 -0
  22. package/dist/boredom.js.map +1 -0
  23. package/dist/cli/acp-server.d.ts +23 -0
  24. package/dist/cli/acp-server.d.ts.map +1 -0
  25. package/dist/cli/burrow.d.ts +26 -0
  26. package/dist/cli/burrow.d.ts.map +1 -0
  27. package/dist/cli/doctor.d.ts +22 -0
  28. package/dist/cli/doctor.d.ts.map +1 -0
  29. package/dist/cli/goal.d.ts +22 -0
  30. package/dist/cli/goal.d.ts.map +1 -0
  31. package/dist/cli/index.d.ts +47 -0
  32. package/dist/cli/index.d.ts.map +1 -0
  33. package/dist/cli/instance-registry.d.ts +78 -0
  34. package/dist/cli/instance-registry.d.ts.map +1 -0
  35. package/dist/cli/observe.d.ts +35 -0
  36. package/dist/cli/observe.d.ts.map +1 -0
  37. package/dist/cli/vessel.d.ts +14 -0
  38. package/dist/cli/vessel.d.ts.map +1 -0
  39. package/dist/composition-observer.d.ts +96 -0
  40. package/dist/composition-observer.d.ts.map +1 -0
  41. package/dist/config.d.ts +36 -0
  42. package/dist/config.d.ts.map +1 -0
  43. package/dist/config.js +128 -0
  44. package/dist/config.js.map +1 -0
  45. package/dist/docker/Dockerfile +35 -0
  46. package/dist/environment.d.ts +72 -0
  47. package/dist/environment.d.ts.map +1 -0
  48. package/dist/environment.js +142 -0
  49. package/dist/environment.js.map +1 -0
  50. package/dist/goal-processor.d.ts +165 -0
  51. package/dist/goal-processor.d.ts.map +1 -0
  52. package/dist/helm/minibob-cluster/Chart.yaml +13 -0
  53. package/dist/helm/minibob-cluster/templates/_helpers.tpl +60 -0
  54. package/dist/helm/minibob-cluster/templates/configmap.yaml +11 -0
  55. package/dist/helm/minibob-cluster/templates/deployment.yaml +108 -0
  56. package/dist/helm/minibob-cluster/templates/secret.yaml +10 -0
  57. package/dist/helm/minibob-cluster/templates/service.yaml +37 -0
  58. package/dist/helm/minibob-cluster/values-local.yaml +41 -0
  59. package/dist/helm/minibob-cluster/values-production.yaml +57 -0
  60. package/dist/helm/minibob-cluster/values-testing-cluster.yaml +43 -0
  61. package/dist/helm/minibob-cluster/values.yaml +127 -0
  62. package/dist/improviser.d.ts +74 -0
  63. package/dist/improviser.d.ts.map +1 -0
  64. package/dist/impulse-filter.d.ts +74 -0
  65. package/dist/impulse-filter.d.ts.map +1 -0
  66. package/dist/impulse.d.ts +92 -0
  67. package/dist/impulse.d.ts.map +1 -0
  68. package/dist/impulse.js +234 -0
  69. package/dist/impulse.js.map +1 -0
  70. package/dist/lib.d.ts +29 -0
  71. package/dist/lib.d.ts.map +1 -0
  72. package/dist/lib.js +18561 -0
  73. package/dist/lib.js.map +98 -0
  74. package/dist/lifecycle-hooks.d.ts +99 -0
  75. package/dist/lifecycle-hooks.d.ts.map +1 -0
  76. package/dist/lifecycle-hooks.js +135 -0
  77. package/dist/lifecycle-hooks.js.map +1 -0
  78. package/dist/llm.d.ts +31 -0
  79. package/dist/llm.d.ts.map +1 -0
  80. package/dist/llm.js +349 -0
  81. package/dist/llm.js.map +1 -0
  82. package/dist/mcp-activity-bridge.d.ts +66 -0
  83. package/dist/mcp-activity-bridge.d.ts.map +1 -0
  84. package/dist/mcp-activity-bridge.js +126 -0
  85. package/dist/mcp-activity-bridge.js.map +1 -0
  86. package/dist/mcp.d.ts +216 -0
  87. package/dist/mcp.d.ts.map +1 -0
  88. package/dist/mcp.js +292 -0
  89. package/dist/mcp.js.map +1 -0
  90. package/dist/memory-agent.d.ts +92 -0
  91. package/dist/memory-agent.d.ts.map +1 -0
  92. package/dist/memory-agent.js +277 -0
  93. package/dist/memory-agent.js.map +1 -0
  94. package/dist/runtime-mapping.d.ts +97 -0
  95. package/dist/runtime-mapping.d.ts.map +1 -0
  96. package/dist/search-first-executor.d.ts +113 -0
  97. package/dist/search-first-executor.d.ts.map +1 -0
  98. package/dist/session.d.ts +48 -0
  99. package/dist/session.d.ts.map +1 -0
  100. package/dist/template-extractor.d.ts +9 -0
  101. package/dist/template-extractor.d.ts.map +1 -0
  102. package/dist/template-generator.d.ts +12 -0
  103. package/dist/template-generator.d.ts.map +1 -0
  104. package/dist/tools.d.ts +58 -0
  105. package/dist/tools.d.ts.map +1 -0
  106. package/dist/tools.js +771 -0
  107. package/dist/tools.js.map +1 -0
  108. package/dist/types.d.ts +503 -0
  109. package/dist/types.d.ts.map +1 -0
  110. package/dist/types.js +8 -0
  111. package/dist/types.js.map +1 -0
  112. package/dist/understanding/analyzer.d.ts +55 -0
  113. package/dist/understanding/analyzer.d.ts.map +1 -0
  114. package/dist/understanding/explorer.d.ts +73 -0
  115. package/dist/understanding/explorer.d.ts.map +1 -0
  116. package/dist/understanding/index.d.ts +7 -0
  117. package/dist/understanding/index.d.ts.map +1 -0
  118. package/dist/understanding/types.d.ts +136 -0
  119. package/dist/understanding/types.d.ts.map +1 -0
  120. package/dist/validation.d.ts +29 -0
  121. package/dist/validation.d.ts.map +1 -0
  122. package/dist/validation.js +106 -0
  123. package/dist/validation.js.map +1 -0
  124. package/dist/vessel-bootstrap.d.ts +190 -0
  125. package/dist/vessel-bootstrap.d.ts.map +1 -0
  126. package/dist/vessel-registry.d.ts +229 -0
  127. package/dist/vessel-registry.d.ts.map +1 -0
  128. package/index.ts +1329 -0
  129. package/package.json +54 -0
  130. package/src/acp-gossip.ts +193 -0
  131. package/src/acp.ts +362 -0
  132. package/src/activity.ts +1464 -0
  133. package/src/agent-runtime.ts +365 -0
  134. package/src/boredom.ts +423 -0
  135. package/src/cli/acp-server.ts +377 -0
  136. package/src/cli/burrow.ts +896 -0
  137. package/src/cli/doctor.ts +526 -0
  138. package/src/cli/goal.ts +224 -0
  139. package/src/cli/index.ts +147 -0
  140. package/src/cli/instance-registry.ts +271 -0
  141. package/src/cli/observe.ts +682 -0
  142. package/src/cli/vessel.ts +287 -0
  143. package/src/components/SystemOverview.tsx +331 -0
  144. package/src/composition-observer.ts +449 -0
  145. package/src/config.ts +172 -0
  146. package/src/environment.ts +167 -0
  147. package/src/goal-processor.ts +654 -0
  148. package/src/improviser.ts +591 -0
  149. package/src/impulse-filter.ts +273 -0
  150. package/src/impulse.ts +311 -0
  151. package/src/lib.ts +147 -0
  152. package/src/lifecycle-hooks.ts +181 -0
  153. package/src/llm.ts +434 -0
  154. package/src/mcp-activity-bridge.ts +158 -0
  155. package/src/mcp.ts +747 -0
  156. package/src/memory-agent.ts +316 -0
  157. package/src/runtime-mapping.ts +527 -0
  158. package/src/search-first-executor.ts +666 -0
  159. package/src/session.ts +141 -0
  160. package/src/template-extractor.ts +256 -0
  161. package/src/template-generator.ts +130 -0
  162. package/src/tools.ts +924 -0
  163. package/src/types.ts +497 -0
  164. package/src/understanding/analyzer.ts +354 -0
  165. package/src/understanding/explorer.ts +488 -0
  166. package/src/understanding/index.ts +27 -0
  167. package/src/understanding/types.ts +153 -0
  168. package/src/validation.ts +125 -0
  169. package/src/vessel-bootstrap.ts +440 -0
  170. package/src/vessel-registry.ts +621 -0
  171. package/templates/core/edit-file.json +85 -0
  172. package/templates/understanding/diagnose-problem.json +32 -0
  173. package/templates/understanding/explore-codebase-v2.json +57 -0
  174. package/templates/understanding/explore-codebase.json +37 -0
@@ -0,0 +1,287 @@
1
+ /**
2
+ * Vessel Command
3
+ *
4
+ * Manage the vessel registry - list, inspect, and manage burrowed codebases.
5
+ */
6
+
7
+ import { parseArgs, formatHelp, printSuccess, printWarning, printInfo, exitWithError } from './index'
8
+ import {
9
+ listVessels,
10
+ getVessel,
11
+ getVesselByPath,
12
+ deregisterVessel,
13
+ type Vessel,
14
+ type VesselIdentity,
15
+ } from '../vessel-registry'
16
+ import { resolve } from 'path'
17
+
18
+ export interface VesselOptions {
19
+ json?: boolean
20
+ verbose?: boolean
21
+ }
22
+
23
+ /**
24
+ * Run vessel command
25
+ */
26
+ export async function vessel(args: string[]): Promise<void> {
27
+ const { flags, positional } = parseArgs(args)
28
+
29
+ const subcommand = positional[0]
30
+ const options: VesselOptions = {
31
+ json: flags.json,
32
+ verbose: flags.verbose || flags.v,
33
+ }
34
+
35
+ // Show help
36
+ if (flags.help || flags.h || !subcommand) {
37
+ console.log(formatHelp('vessel', 'Manage the vessel registry', [
38
+ { flag: 'list', description: 'List all registered vessels' },
39
+ { flag: 'info [path]', description: 'Show vessel info (current dir if no path)' },
40
+ { flag: 'remove <id|path>', description: 'Remove a vessel from the registry' },
41
+ { flag: '--json', description: 'Output in JSON format' },
42
+ { flag: '--verbose, -v', description: 'Show detailed information' },
43
+ { flag: '--help, -h', description: 'Show this help message' },
44
+ ]))
45
+ return
46
+ }
47
+
48
+ switch (subcommand) {
49
+ case 'list':
50
+ case 'ls':
51
+ await listVesselsCommand(options)
52
+ break
53
+
54
+ case 'info':
55
+ case 'show':
56
+ const infoPath = positional[1] || '.'
57
+ await showVesselInfo(infoPath, options)
58
+ break
59
+
60
+ case 'remove':
61
+ case 'rm':
62
+ case 'deregister':
63
+ const removeTarget = positional[1]
64
+ if (!removeTarget) {
65
+ exitWithError('Please specify a vessel ID or path to remove')
66
+ }
67
+ await removeVessel(removeTarget, options)
68
+ break
69
+
70
+ default:
71
+ exitWithError(`Unknown subcommand: ${subcommand}`)
72
+ }
73
+ }
74
+
75
+ /**
76
+ * List all registered vessels
77
+ */
78
+ async function listVesselsCommand(options: VesselOptions): Promise<void> {
79
+ const vessels = listVessels()
80
+
81
+ if (vessels.length === 0) {
82
+ if (options.json) {
83
+ console.log(JSON.stringify({ vessels: [] }, null, 2))
84
+ } else {
85
+ printInfo('No vessels registered. Use "minibob burrow <path>" to register a codebase.')
86
+ }
87
+ return
88
+ }
89
+
90
+ if (options.json) {
91
+ console.log(JSON.stringify({ vessels }, null, 2))
92
+ return
93
+ }
94
+
95
+ console.log('\n=== Registered Vessels ===\n')
96
+
97
+ for (const v of vessels) {
98
+ const lastActive = new Date(v.lastActiveAt).toLocaleDateString()
99
+ console.log(` ${v.name}`)
100
+ console.log(` ID: ${v.id}`)
101
+ console.log(` Path: ${v.path}`)
102
+ console.log(` Last Active: ${lastActive}`)
103
+ if (v.description) {
104
+ console.log(` Description: ${v.description}`)
105
+ }
106
+ console.log('')
107
+ }
108
+
109
+ console.log(`Total: ${vessels.length} vessel(s)\n`)
110
+ }
111
+
112
+ /**
113
+ * Show detailed vessel information
114
+ */
115
+ async function showVesselInfo(pathOrId: string, options: VesselOptions): Promise<void> {
116
+ // Try to find by path first
117
+ let vesselData: Vessel | null = null
118
+
119
+ // Check if it looks like an ID
120
+ if (pathOrId.startsWith('vessel_')) {
121
+ vesselData = getVessel(pathOrId)
122
+ }
123
+
124
+ // Try by path
125
+ if (!vesselData) {
126
+ const absPath = resolve(pathOrId)
127
+ vesselData = getVesselByPath(absPath)
128
+ }
129
+
130
+ if (!vesselData) {
131
+ if (options.json) {
132
+ console.log(JSON.stringify({ error: 'Vessel not found' }, null, 2))
133
+ } else {
134
+ exitWithError(`Vessel not found at: ${pathOrId}\nRun "minibob burrow ${pathOrId}" to register it.`)
135
+ }
136
+ return
137
+ }
138
+
139
+ if (options.json) {
140
+ // Serialize Maps to objects for JSON output
141
+ const jsonSafe = {
142
+ ...vesselData,
143
+ mapping: {
144
+ functionMap: Object.fromEntries(vesselData.mapping.functionMap),
145
+ intentMap: Object.fromEntries(vesselData.mapping.intentMap),
146
+ patternMap: Object.fromEntries(vesselData.mapping.patternMap),
147
+ },
148
+ }
149
+ console.log(JSON.stringify(jsonSafe, null, 2))
150
+ return
151
+ }
152
+
153
+ console.log('\n=== Vessel Information ===\n')
154
+ console.log(`Name: ${vesselData.name}`)
155
+ console.log(`ID: ${vesselData.id}`)
156
+ console.log(`Path: ${vesselData.path}`)
157
+ if (vesselData.version) {
158
+ console.log(`Version: ${vesselData.version}`)
159
+ }
160
+ if (vesselData.description) {
161
+ console.log(`Description: ${vesselData.description}`)
162
+ }
163
+ console.log(`Created: ${new Date(vesselData.createdAt).toLocaleString()}`)
164
+ console.log(`Last Active: ${new Date(vesselData.lastActiveAt).toLocaleString()}`)
165
+
166
+ // Activities
167
+ console.log('\n--- Activities ---')
168
+ if (vesselData.activities.length === 0) {
169
+ console.log(' No activities defined')
170
+ } else {
171
+ for (const activity of vesselData.activities) {
172
+ const successPct = activity.successRate > 0 ? `${(activity.successRate * 100).toFixed(0)}%` : 'N/A'
173
+ console.log(` ${activity.name} (${activity.id})`)
174
+ console.log(` Origin: ${activity.origin}, Executions: ${activity.executions}, Success: ${successPct}`)
175
+ }
176
+ }
177
+
178
+ // Impulses
179
+ if (vesselData.impulses.length > 0) {
180
+ console.log('\n--- Impulses ---')
181
+ for (const impulse of vesselData.impulses) {
182
+ console.log(` ${impulse.id} (${impulse.type})`)
183
+ if (impulse.description) {
184
+ console.log(` ${impulse.description}`)
185
+ }
186
+ }
187
+ }
188
+
189
+ // Hooks
190
+ if (vesselData.hooks.length > 0) {
191
+ console.log('\n--- Hooks ---')
192
+ for (const hook of vesselData.hooks) {
193
+ const status = hook.enabled ? 'enabled' : 'disabled'
194
+ console.log(` ${hook.id} (${hook.type}) - ${status}`)
195
+ console.log(` Handler: ${hook.handler}`)
196
+ }
197
+ }
198
+
199
+ // Tools
200
+ if (vesselData.tools.length > 0) {
201
+ console.log('\n--- Tools ---')
202
+ for (const tool of vesselData.tools) {
203
+ console.log(` ${tool.name}: ${tool.description}`)
204
+ }
205
+ }
206
+
207
+ // Runtime Mappings (verbose only)
208
+ if (options.verbose) {
209
+ console.log('\n--- Runtime Mappings ---')
210
+ const functionCount = vesselData.mapping.functionMap.size
211
+ const intentCount = vesselData.mapping.intentMap.size
212
+ const patternCount = vesselData.mapping.patternMap.size
213
+ console.log(` Functions mapped: ${functionCount}`)
214
+ console.log(` Intents inferred: ${intentCount}`)
215
+ console.log(` Patterns recorded: ${patternCount}`)
216
+
217
+ if (functionCount > 0 && functionCount <= 10) {
218
+ console.log('\n Function Mappings:')
219
+ for (const [name, loc] of vesselData.mapping.functionMap) {
220
+ console.log(` ${name} -> ${loc.file}:${loc.line}`)
221
+ }
222
+ }
223
+
224
+ if (intentCount > 0 && intentCount <= 10) {
225
+ console.log('\n Intent Mappings:')
226
+ for (const [name, intent] of vesselData.mapping.intentMap) {
227
+ console.log(` ${name}: ${intent.purpose} (${(intent.confidence * 100).toFixed(0)}%)`)
228
+ }
229
+ }
230
+ }
231
+
232
+ // Composition
233
+ if (vesselData.composition.compositions.length > 0) {
234
+ console.log('\n--- Activity Composition ---')
235
+ for (const comp of vesselData.composition.compositions.slice(0, 10)) {
236
+ console.log(` ${comp.parent} -> ${comp.child} (${comp.count} times, ${comp.successRate.toFixed(0)}% success)`)
237
+ }
238
+ }
239
+
240
+ console.log('')
241
+ }
242
+
243
+ /**
244
+ * Remove a vessel from the registry
245
+ */
246
+ async function removeVessel(target: string, options: VesselOptions): Promise<void> {
247
+ // Find the vessel
248
+ let vesselId: string | null = null
249
+
250
+ if (target.startsWith('vessel_')) {
251
+ vesselId = target
252
+ } else {
253
+ const absPath = resolve(target)
254
+ const v = getVesselByPath(absPath)
255
+ if (v) {
256
+ vesselId = v.id
257
+ }
258
+ }
259
+
260
+ if (!vesselId) {
261
+ if (options.json) {
262
+ console.log(JSON.stringify({ error: 'Vessel not found', target }, null, 2))
263
+ } else {
264
+ exitWithError(`Vessel not found: ${target}`)
265
+ }
266
+ return
267
+ }
268
+
269
+ const removed = deregisterVessel(vesselId)
270
+
271
+ if (options.json) {
272
+ console.log(JSON.stringify({ success: removed, vesselId }, null, 2))
273
+ } else if (removed) {
274
+ printSuccess(`Removed vessel: ${vesselId}`)
275
+ } else {
276
+ printWarning(`Failed to remove vessel: ${vesselId}`)
277
+ }
278
+ }
279
+
280
+ // CLI entry point
281
+ if (import.meta.main) {
282
+ const args = process.argv.slice(2)
283
+ vessel(args).catch((error) => {
284
+ console.error('Vessel command failed:', error)
285
+ process.exit(1)
286
+ })
287
+ }
@@ -0,0 +1,331 @@
1
+ /**
2
+ * System Overview Component
3
+ *
4
+ * Shows:
5
+ * - MiniBob instance status (from K8s API or metrics endpoint)
6
+ * - API health and connectivity
7
+ * - Overall system metrics (total executions, success rate, error rate, costs)
8
+ * - Recent activity feed
9
+ */
10
+
11
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
12
+ import { Badge } from "@/components/ui/badge";
13
+ import { Progress } from "@/components/ui/progress";
14
+ import { Activity, Server, Database, Cpu, TrendingUp, TrendingDown, Clock, DollarSign, Wifi, WifiOff } from "lucide-react";
15
+ import { useHealth } from "@/hooks/useHealth";
16
+ import { useTemplates } from "@/hooks/useTemplates";
17
+ import { useWebSocket } from "@/hooks/useWebSocket";
18
+ import { useMemo, useCallback } from "react";
19
+ import type { ActivityTemplate, WebSocketMessage } from "@/lib/types";
20
+
21
+ interface SystemMetrics {
22
+ totalExecutions: number;
23
+ successRate: number;
24
+ totalCost: number;
25
+ avgDuration: number;
26
+ activeTemplates: number;
27
+ }
28
+
29
+ export function SystemOverview() {
30
+ const { health, loading: healthLoading } = useHealth();
31
+ const { templates, total, loading: templatesLoading, refresh: refreshTemplates } = useTemplates();
32
+
33
+ // WebSocket for real-time updates
34
+ const handleWebSocketMessage = useCallback((message: WebSocketMessage) => {
35
+ console.log('[SystemOverview] Received WebSocket message:', message.type);
36
+
37
+ // Refresh templates when metrics are updated
38
+ if (message.type === 'execution_completed' || message.type === 'template_updated') {
39
+ refreshTemplates();
40
+ }
41
+ }, [refreshTemplates]);
42
+
43
+ const { connected: wsConnected } = useWebSocket({
44
+ enabled: true,
45
+ onMessage: handleWebSocketMessage,
46
+ });
47
+
48
+ // Calculate aggregate metrics
49
+ const metrics: SystemMetrics = useMemo(() => {
50
+ if (!templates || templates.length === 0) {
51
+ return {
52
+ totalExecutions: 0,
53
+ successRate: 0,
54
+ totalCost: 0,
55
+ avgDuration: 0,
56
+ activeTemplates: 0,
57
+ };
58
+ }
59
+
60
+ const allTemplates: ActivityTemplate[] = templates;
61
+ const totalExec = allTemplates.reduce((sum: number, t: ActivityTemplate) =>
62
+ sum + (t.metrics?.total_executions || 0), 0
63
+ );
64
+ const successfulExec = allTemplates.reduce((sum: number, t: ActivityTemplate) =>
65
+ sum + (t.metrics?.successful_executions || 0), 0
66
+ );
67
+ const totalCost = allTemplates.reduce((sum: number, t: ActivityTemplate) =>
68
+ sum + ((t.metrics?.avg_cost_usd || 0) * (t.metrics?.total_executions || 0)), 0
69
+ );
70
+ const totalDuration = allTemplates.reduce((sum: number, t: ActivityTemplate) =>
71
+ sum + ((t.metrics?.avg_duration_ms || 0) * (t.metrics?.total_executions || 0)), 0
72
+ );
73
+
74
+ return {
75
+ totalExecutions: totalExec,
76
+ successRate: totalExec > 0 ? (successfulExec / totalExec) * 100 : 0,
77
+ totalCost,
78
+ avgDuration: totalExec > 0 ? totalDuration / totalExec : 0,
79
+ activeTemplates: allTemplates.filter((t: ActivityTemplate) =>
80
+ (t.metrics?.total_executions || 0) > 0
81
+ ).length,
82
+ };
83
+ }, [templates]);
84
+
85
+ // Health status badge
86
+ const healthStatus = health?.status || 'unknown';
87
+ const healthColor = healthStatus === 'healthy' || healthStatus === 'ok' ? 'default' :
88
+ healthStatus === 'degraded' ? 'secondary' : 'destructive';
89
+
90
+ return (
91
+ <div className="space-y-6">
92
+ {/* Header */}
93
+ <div className="flex items-start justify-between">
94
+ <div>
95
+ <h2 className="text-3xl font-bold tracking-tight">System Overview</h2>
96
+ <p className="text-muted-foreground">
97
+ Real-time monitoring of MiniBob instances and activity execution
98
+ </p>
99
+ </div>
100
+ {/* WebSocket Status Indicator */}
101
+ <div className="flex items-center gap-2 text-sm">
102
+ {wsConnected ? (
103
+ <>
104
+ <Wifi className="h-4 w-4 text-green-600" />
105
+ <span className="text-green-600">Live</span>
106
+ </>
107
+ ) : (
108
+ <>
109
+ <WifiOff className="h-4 w-4 text-muted-foreground" />
110
+ <span className="text-muted-foreground">Reconnecting...</span>
111
+ </>
112
+ )}
113
+ </div>
114
+ </div>
115
+
116
+ {/* Top-level Status Cards */}
117
+ <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-5">
118
+ {/* API Health */}
119
+ <Card>
120
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
121
+ <CardTitle className="text-sm font-medium">API Health</CardTitle>
122
+ <Server className="h-4 w-4 text-muted-foreground" />
123
+ </CardHeader>
124
+ <CardContent>
125
+ {healthLoading ? (
126
+ <div className="text-2xl font-bold">Loading...</div>
127
+ ) : (
128
+ <>
129
+ <div className="flex items-center gap-2">
130
+ <Badge variant={healthColor}>{healthStatus}</Badge>
131
+ {health?.service && (
132
+ <span className="text-xs text-muted-foreground">{health.service}</span>
133
+ )}
134
+ </div>
135
+ {health?.checks && (
136
+ <div className="mt-2 space-y-1 text-xs text-muted-foreground">
137
+ <div className="flex justify-between">
138
+ <span>Redis:</span>
139
+ <span className={health.checks.redis?.status === 'healthy' ? 'text-green-600' : 'text-red-600'}>
140
+ {health.checks.redis?.status} ({health.checks.redis?.latency_ms}ms)
141
+ </span>
142
+ </div>
143
+ <div className="flex justify-between">
144
+ <span>SurrealDB:</span>
145
+ <span className={health.checks.surrealdb?.status === 'healthy' ? 'text-green-600' : 'text-red-600'}>
146
+ {health.checks.surrealdb?.status} ({health.checks.surrealdb?.latency_ms}ms)
147
+ </span>
148
+ </div>
149
+ </div>
150
+ )}
151
+ </>
152
+ )}
153
+ </CardContent>
154
+ </Card>
155
+
156
+ {/* Total Executions */}
157
+ <Card>
158
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
159
+ <CardTitle className="text-sm font-medium">Total Executions</CardTitle>
160
+ <Activity className="h-4 w-4 text-muted-foreground" />
161
+ </CardHeader>
162
+ <CardContent>
163
+ <div className="text-2xl font-bold">
164
+ {templatesLoading ? '...' : metrics.totalExecutions.toLocaleString()}
165
+ </div>
166
+ <p className="text-xs text-muted-foreground">
167
+ {metrics.activeTemplates} active templates
168
+ </p>
169
+ </CardContent>
170
+ </Card>
171
+
172
+ {/* Success Rate */}
173
+ <Card>
174
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
175
+ <CardTitle className="text-sm font-medium">Success Rate</CardTitle>
176
+ <TrendingUp className="h-4 w-4 text-green-500" />
177
+ </CardHeader>
178
+ <CardContent>
179
+ <div className="text-2xl font-bold text-green-600">
180
+ {templatesLoading ? '...' : `${metrics.successRate.toFixed(1)}%`}
181
+ </div>
182
+ <p className="text-xs text-muted-foreground">
183
+ {Math.round(metrics.totalExecutions * metrics.successRate / 100)} successful executions
184
+ </p>
185
+ <Progress
186
+ value={metrics.successRate}
187
+ className="mt-2"
188
+ />
189
+ </CardContent>
190
+ </Card>
191
+
192
+ {/* Error Rate */}
193
+ <Card>
194
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
195
+ <CardTitle className="text-sm font-medium">Error Rate</CardTitle>
196
+ <TrendingDown className="h-4 w-4 text-red-500" />
197
+ </CardHeader>
198
+ <CardContent>
199
+ <div className="text-2xl font-bold text-red-600">
200
+ {templatesLoading ? '...' : `${(100 - metrics.successRate).toFixed(1)}%`}
201
+ </div>
202
+ <p className="text-xs text-muted-foreground">
203
+ {Math.round(metrics.totalExecutions * (100 - metrics.successRate) / 100)} failed executions
204
+ </p>
205
+ <Progress
206
+ value={100 - metrics.successRate}
207
+ className="mt-2"
208
+ />
209
+ </CardContent>
210
+ </Card>
211
+
212
+ {/* Average Duration */}
213
+ <Card>
214
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
215
+ <CardTitle className="text-sm font-medium">Avg Duration</CardTitle>
216
+ <Clock className="h-4 w-4 text-muted-foreground" />
217
+ </CardHeader>
218
+ <CardContent>
219
+ <div className="text-2xl font-bold">
220
+ {templatesLoading ? '...' : (metrics.avgDuration / 1000).toFixed(1)}s
221
+ </div>
222
+ <p className="text-xs text-muted-foreground">
223
+ Per activity execution
224
+ </p>
225
+ </CardContent>
226
+ </Card>
227
+ </div>
228
+
229
+ {/* Second Row - Total Cost moved here */}
230
+ <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
231
+ {/* Total Cost */}
232
+ <Card>
233
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
234
+ <CardTitle className="text-sm font-medium">Total Cost</CardTitle>
235
+ <DollarSign className="h-4 w-4 text-muted-foreground" />
236
+ </CardHeader>
237
+ <CardContent>
238
+ <div className="text-2xl font-bold">
239
+ ${templatesLoading ? '...' : metrics.totalCost.toFixed(2)}
240
+ </div>
241
+ <p className="text-xs text-muted-foreground">
242
+ ${metrics.totalExecutions > 0 ? (metrics.totalCost / metrics.totalExecutions).toFixed(3) : '0.000'} per execution
243
+ </p>
244
+ </CardContent>
245
+ </Card>
246
+ </div>
247
+
248
+ {/* MiniBob Instances */}
249
+ <Card>
250
+ <CardHeader>
251
+ <CardTitle className="flex items-center gap-2">
252
+ <Cpu className="h-5 w-5" />
253
+ MiniBob Instances
254
+ </CardTitle>
255
+ <CardDescription>
256
+ Connected execution vessels (future: live K8s pod monitoring)
257
+ </CardDescription>
258
+ </CardHeader>
259
+ <CardContent>
260
+ <div className="space-y-3">
261
+ {/* Placeholder for MiniBob pods - will be populated via K8s API or metrics */}
262
+ <div className="flex items-center justify-between p-3 border rounded-lg">
263
+ <div className="flex items-center gap-3">
264
+ <div className="h-2 w-2 rounded-full bg-green-500 animate-pulse" />
265
+ <div>
266
+ <div className="font-medium">minibob-cluster-0</div>
267
+ <div className="text-xs text-muted-foreground">Namespace: activity-system</div>
268
+ </div>
269
+ </div>
270
+ <Badge variant="outline">Idle</Badge>
271
+ </div>
272
+
273
+ <div className="text-center p-6 border-2 border-dashed rounded-lg">
274
+ <Database className="h-8 w-8 mx-auto mb-2 text-muted-foreground" />
275
+ <p className="text-sm text-muted-foreground">
276
+ Additional MiniBob instances will appear here
277
+ </p>
278
+ <p className="text-xs text-muted-foreground mt-1">
279
+ Deploy more vessels to scale execution capacity
280
+ </p>
281
+ </div>
282
+ </div>
283
+ </CardContent>
284
+ </Card>
285
+
286
+ {/* Learning System Health */}
287
+ <Card>
288
+ <CardHeader>
289
+ <CardTitle className="flex items-center gap-2">
290
+ <TrendingUp className="h-5 w-5" />
291
+ Learning System Status
292
+ </CardTitle>
293
+ <CardDescription>
294
+ Thompson sampling and adaptive template selection
295
+ </CardDescription>
296
+ </CardHeader>
297
+ <CardContent>
298
+ <div className="space-y-4">
299
+ <div className="grid grid-cols-3 gap-4 text-center">
300
+ <div>
301
+ <div className="text-2xl font-bold">{total || 0}</div>
302
+ <div className="text-xs text-muted-foreground">Templates</div>
303
+ </div>
304
+ <div>
305
+ <div className="text-2xl font-bold">
306
+ {templatesLoading ? '...' : metrics.activeTemplates}
307
+ </div>
308
+ <div className="text-xs text-muted-foreground">Active (0+ exec)</div>
309
+ </div>
310
+ <div>
311
+ <div className="text-2xl font-bold">
312
+ {metrics.successRate > 0 ? metrics.successRate.toFixed(0) : '0'}%
313
+ </div>
314
+ <div className="text-xs text-muted-foreground">Avg Success</div>
315
+ </div>
316
+ </div>
317
+
318
+ <div className="pt-4 border-t">
319
+ <div className="text-sm font-medium mb-2">Thompson Sampling Status</div>
320
+ <div className="text-xs text-muted-foreground space-y-1">
321
+ <div>✓ Bayesian optimization enabled</div>
322
+ <div>✓ Exploration/exploitation balanced</div>
323
+ <div>✓ Real-time metric updates via API</div>
324
+ </div>
325
+ </div>
326
+ </div>
327
+ </CardContent>
328
+ </Card>
329
+ </div>
330
+ );
331
+ }