@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,365 @@
1
+ /**
2
+ * Agent Runtime - Reusable backend integration for CLI and server modes
3
+ *
4
+ * Provides:
5
+ * - Backend registration/deregistration
6
+ * - Heartbeat reporting
7
+ * - Execution metrics tracking
8
+ * - Graceful shutdown
9
+ *
10
+ * Usage:
11
+ * const runtime = await AgentRuntime.initialize(config)
12
+ * // ... do work ...
13
+ * await runtime.reportExecution(execution)
14
+ * await runtime.shutdown()
15
+ */
16
+
17
+ import type { MinibobConfig, ActivityExecution, VesselManifest } from "./types"
18
+ import type { MCPClient } from "./mcp"
19
+ import { initializeMCP, getMCPClient, isMCPEnabled } from "./mcp"
20
+ import { generateManifest, type RuntimeContext } from "./config"
21
+ import { detectCompleteEnvironment } from "./environment"
22
+
23
+ // =============================================================================
24
+ // TYPES
25
+ // =============================================================================
26
+
27
+ export interface AgentRuntimeConfig {
28
+ config: MinibobConfig
29
+ mode: "cli" | "server"
30
+ instanceId?: string
31
+ enableHeartbeat?: boolean
32
+ heartbeatInterval?: number
33
+ }
34
+
35
+ export interface AgentMetrics {
36
+ executionsCompleted: number
37
+ executionsFailed: number
38
+ totalCost: number
39
+ totalTokens: {
40
+ input: number
41
+ output: number
42
+ }
43
+ uptime: number
44
+ }
45
+
46
+ export interface CurrentActivity {
47
+ id: string
48
+ name?: string
49
+ task?: string
50
+ startedAt: number
51
+ }
52
+
53
+ // =============================================================================
54
+ // AGENT RUNTIME
55
+ // =============================================================================
56
+
57
+ export class AgentRuntime {
58
+ private config: MinibobConfig
59
+ private mode: "cli" | "server"
60
+ private instanceId: string
61
+ private startTime: number
62
+ private mcp: MCPClient | null = null
63
+ private manifest: VesselManifest | null = null
64
+
65
+ private heartbeatInterval: NodeJS.Timeout | null = null
66
+ private heartbeatIntervalMs: number
67
+ private enableHeartbeat: boolean
68
+
69
+ private metrics: AgentMetrics = {
70
+ executionsCompleted: 0,
71
+ executionsFailed: 0,
72
+ totalCost: 0,
73
+ totalTokens: { input: 0, output: 0 },
74
+ uptime: 0
75
+ }
76
+
77
+ private currentActivity: CurrentActivity | null = null
78
+ private shuttingDown = false
79
+
80
+ private constructor(options: AgentRuntimeConfig) {
81
+ this.config = options.config
82
+ this.mode = options.mode
83
+ this.instanceId = options.instanceId || this.generateInstanceId()
84
+ this.startTime = Date.now()
85
+ this.enableHeartbeat = options.enableHeartbeat ?? (options.mode === "server")
86
+ this.heartbeatIntervalMs = options.heartbeatInterval ?? 30000
87
+ }
88
+
89
+ /**
90
+ * Initialize agent runtime with backend connection
91
+ */
92
+ static async initialize(options: AgentRuntimeConfig): Promise<AgentRuntime> {
93
+ const runtime = new AgentRuntime(options)
94
+
95
+ console.log(`[AgentRuntime] Initializing in ${options.mode} mode (instance: ${runtime.instanceId})`)
96
+
97
+ // Detect environment
98
+ const mcpEndpoint = runtime.config.vessels.metabob?.endpoint
99
+ const envInfo = await detectCompleteEnvironment(mcpEndpoint)
100
+
101
+ console.log(`[AgentRuntime] Environment: ${envInfo.environment}`)
102
+ console.log(`[AgentRuntime] Backend Available: ${envInfo.backendAvailable}`)
103
+
104
+ // Initialize MCP if backend available
105
+ if (runtime.config.vessels.metabob && envInfo.backendAvailable) {
106
+ console.log(`[AgentRuntime] Connecting to backend: ${mcpEndpoint}`)
107
+
108
+ try {
109
+ runtime.mcp = await initializeMCP(
110
+ {
111
+ endpoint: mcpEndpoint!,
112
+ apiKey: runtime.config.apiKey
113
+ },
114
+ options.mode === "cli" // Skip health check in CLI mode for faster startup
115
+ )
116
+
117
+ // Generate manifest
118
+ const runtimeContext: RuntimeContext = {
119
+ environment: envInfo.environment,
120
+ clusterMode: envInfo.clusterMode,
121
+ peerCount: envInfo.peerCount,
122
+ boredomEnabled: false, // CLI mode doesn't do boredom
123
+ acpGossipEnabled: false,
124
+ backendAvailable: true,
125
+ }
126
+
127
+ runtime.manifest = generateManifest(runtime.config, runtimeContext)
128
+
129
+ // Register with backend
130
+ if (runtime.mcp) {
131
+ await runtime.mcp.registerVessel({
132
+ id: runtime.manifest.id,
133
+ name: runtime.manifest.name,
134
+ version: runtime.manifest.version,
135
+ capabilities: runtime.manifest.capabilities,
136
+ endpoint: runtime.manifest.acpEndpoint,
137
+ })
138
+ console.log("[AgentRuntime] ✓ Registered with backend")
139
+
140
+ // Start heartbeat if enabled
141
+ if (runtime.enableHeartbeat) {
142
+ runtime.startHeartbeat()
143
+ }
144
+ }
145
+ } catch (error) {
146
+ console.warn("[AgentRuntime] Failed to connect to backend:", error instanceof Error ? error.message : String(error))
147
+ console.warn("[AgentRuntime] Continuing in standalone mode")
148
+ }
149
+ } else {
150
+ console.log("[AgentRuntime] Running in standalone mode (no backend)")
151
+ }
152
+
153
+ // Setup graceful shutdown handlers
154
+ runtime.setupShutdownHandlers()
155
+
156
+ return runtime
157
+ }
158
+
159
+ /**
160
+ * Start heartbeat reporting
161
+ */
162
+ private startHeartbeat(): void {
163
+ if (!this.mcp || !this.manifest) return
164
+
165
+ const sendHeartbeat = async () => {
166
+ if (!this.mcp || this.shuttingDown) return
167
+
168
+ try {
169
+ const endpoint = this.config.vessels.metabob?.endpoint
170
+ if (!endpoint) return
171
+
172
+ const status = this.currentActivity ? 'executing' : 'idle'
173
+ const heartbeatUrl = `${endpoint}/v2/vessels/heartbeat`
174
+
175
+ await fetch(heartbeatUrl, {
176
+ method: 'POST',
177
+ headers: { 'Content-Type': 'application/json' },
178
+ body: JSON.stringify({
179
+ pod_name: this.instanceId,
180
+ namespace: this.mode === 'cli' ? 'cli' : 'local',
181
+ status,
182
+ current_activity: this.currentActivity ? {
183
+ variant_id: this.currentActivity.id,
184
+ activity_id: this.currentActivity.id,
185
+ variant_name: this.currentActivity.name || this.currentActivity.id,
186
+ started_at: new Date(this.currentActivity.startedAt).toISOString(),
187
+ current_task: this.currentActivity.task,
188
+ } : null,
189
+ metrics: {
190
+ executions_completed: this.metrics.executionsCompleted,
191
+ total_cost_usd: this.metrics.totalCost,
192
+ uptime_seconds: Math.floor((Date.now() - this.startTime) / 1000),
193
+ },
194
+ }),
195
+ signal: AbortSignal.timeout(5000),
196
+ })
197
+ } catch (error) {
198
+ // Silently ignore heartbeat failures
199
+ }
200
+ }
201
+
202
+ // Send initial heartbeat
203
+ sendHeartbeat()
204
+
205
+ // Start interval
206
+ this.heartbeatInterval = setInterval(sendHeartbeat, this.heartbeatIntervalMs)
207
+ console.log(`[AgentRuntime] ✓ Heartbeat started (${this.heartbeatIntervalMs}ms interval)`)
208
+ }
209
+
210
+ /**
211
+ * Mark start of activity execution
212
+ */
213
+ setCurrentActivity(activity: CurrentActivity): void {
214
+ this.currentActivity = activity
215
+ }
216
+
217
+ /**
218
+ * Clear current activity
219
+ */
220
+ clearCurrentActivity(): void {
221
+ this.currentActivity = null
222
+ }
223
+
224
+ /**
225
+ * Report execution to backend
226
+ */
227
+ async reportExecution(execution: ActivityExecution): Promise<void> {
228
+ // Update metrics
229
+ if (execution.status === "completed") {
230
+ this.metrics.executionsCompleted++
231
+ } else if (execution.status === "failed") {
232
+ this.metrics.executionsFailed++
233
+ }
234
+
235
+ if (execution.metrics) {
236
+ this.metrics.totalCost += execution.metrics.cost
237
+ this.metrics.totalTokens.input += execution.metrics.totalTokens.input
238
+ this.metrics.totalTokens.output += execution.metrics.totalTokens.output
239
+ }
240
+
241
+ // Store execution trace in backend if available
242
+ if (this.mcp) {
243
+ try {
244
+ await this.mcp.storeExecutionTrace(execution)
245
+ console.log(`[AgentRuntime] ✓ Execution trace stored: ${execution.id}`)
246
+ } catch (error) {
247
+ console.warn("[AgentRuntime] Failed to store execution trace:", error instanceof Error ? error.message : String(error))
248
+ }
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Get current metrics
254
+ */
255
+ getMetrics(): AgentMetrics {
256
+ return {
257
+ ...this.metrics,
258
+ uptime: Date.now() - this.startTime
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Get instance ID
264
+ */
265
+ getInstanceId(): string {
266
+ return this.instanceId
267
+ }
268
+
269
+ /**
270
+ * Check if backend is available
271
+ */
272
+ hasBackend(): boolean {
273
+ return this.mcp !== null
274
+ }
275
+
276
+ /**
277
+ * Get MCP client (if available)
278
+ */
279
+ getMCP(): MCPClient | null {
280
+ return this.mcp
281
+ }
282
+
283
+ /**
284
+ * Setup graceful shutdown handlers
285
+ */
286
+ private setupShutdownHandlers(): void {
287
+ const gracefulShutdown = async (signal: string) => {
288
+ if (this.shuttingDown) return
289
+ this.shuttingDown = true
290
+
291
+ console.log(`\n[AgentRuntime] Received ${signal}, shutting down gracefully...`)
292
+ await this.shutdown()
293
+ process.exit(0)
294
+ }
295
+
296
+ process.on("SIGTERM", () => gracefulShutdown("SIGTERM"))
297
+ process.on("SIGINT", () => gracefulShutdown("SIGINT"))
298
+ }
299
+
300
+ /**
301
+ * Shutdown runtime and cleanup
302
+ */
303
+ async shutdown(): Promise<void> {
304
+ if (this.shuttingDown && !this.heartbeatInterval) {
305
+ return // Already shut down
306
+ }
307
+
308
+ console.log("[AgentRuntime] Shutting down...")
309
+
310
+ // Stop heartbeat
311
+ if (this.heartbeatInterval) {
312
+ clearInterval(this.heartbeatInterval)
313
+ this.heartbeatInterval = null
314
+ console.log("[AgentRuntime] ✓ Heartbeat stopped")
315
+ }
316
+
317
+ // Deregister from backend
318
+ if (this.mcp && this.manifest) {
319
+ try {
320
+ // Send final heartbeat with shutdown status
321
+ const endpoint = this.config.vessels.metabob?.endpoint
322
+ if (endpoint) {
323
+ await fetch(`${endpoint}/v2/vessels/heartbeat`, {
324
+ method: 'POST',
325
+ headers: { 'Content-Type': 'application/json' },
326
+ body: JSON.stringify({
327
+ pod_name: this.instanceId,
328
+ namespace: this.mode === 'cli' ? 'cli' : 'local',
329
+ status: 'shutdown',
330
+ metrics: {
331
+ executions_completed: this.metrics.executionsCompleted,
332
+ total_cost_usd: this.metrics.totalCost,
333
+ uptime_seconds: Math.floor((Date.now() - this.startTime) / 1000),
334
+ },
335
+ }),
336
+ signal: AbortSignal.timeout(2000),
337
+ })
338
+ }
339
+ console.log("[AgentRuntime] ✓ Deregistered from backend")
340
+ } catch (error) {
341
+ // Ignore errors during shutdown
342
+ }
343
+ }
344
+
345
+ // Print final metrics
346
+ const metrics = this.getMetrics()
347
+ console.log("\n[AgentRuntime] Final Metrics:")
348
+ console.log(` Executions: ${metrics.executionsCompleted} completed, ${metrics.executionsFailed} failed`)
349
+ console.log(` Cost: $${metrics.totalCost.toFixed(4)}`)
350
+ console.log(` Tokens: ${metrics.totalTokens.input} in / ${metrics.totalTokens.output} out`)
351
+ console.log(` Uptime: ${Math.floor(metrics.uptime / 1000)}s`)
352
+
353
+ console.log("[AgentRuntime] ✓ Shutdown complete")
354
+ }
355
+
356
+ /**
357
+ * Generate unique instance ID
358
+ */
359
+ private generateInstanceId(): string {
360
+ const hostname = process.env.HOSTNAME || "local"
361
+ const timestamp = Date.now()
362
+ const random = Math.random().toString(36).substring(7)
363
+ return `minibob-${hostname}-${timestamp}-${random}`
364
+ }
365
+ }