@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,125 @@
1
+ /**
2
+ * Input validation utilities
3
+ *
4
+ * Lightweight validation without external dependencies.
5
+ * Prevents crashes from malformed input and provides clear error messages.
6
+ */
7
+
8
+ export interface ValidationError {
9
+ field: string
10
+ message: string
11
+ }
12
+
13
+ export class ValidationException extends Error {
14
+ constructor(public errors: ValidationError[]) {
15
+ super(`Validation failed: ${errors.map((e) => `${e.field}: ${e.message}`).join(", ")}`)
16
+ this.name = "ValidationException"
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Validate /run activity request body
22
+ */
23
+ export function validateRunActivityRequest(body: unknown): {
24
+ template: string
25
+ variables: Record<string, unknown>
26
+ reason?: string
27
+ } {
28
+ const errors: ValidationError[] = []
29
+
30
+ if (!body || typeof body !== "object") {
31
+ throw new ValidationException([
32
+ { field: "body", message: "Request body must be a JSON object" },
33
+ ])
34
+ }
35
+
36
+ const data = body as Record<string, unknown>
37
+
38
+ // Validate template (required)
39
+ const template = data.template ?? data.templatePath
40
+ if (!template || typeof template !== "string") {
41
+ errors.push({
42
+ field: "template",
43
+ message: "Missing required field (string expected)",
44
+ })
45
+ }
46
+
47
+ if (template && typeof template === "string" && template.length > 1000) {
48
+ errors.push({
49
+ field: "template",
50
+ message: "Template path too long (max 1000 characters)",
51
+ })
52
+ }
53
+
54
+ // Validate variables (optional, default to empty object)
55
+ let variables: Record<string, unknown> = {}
56
+ if (data.variables !== undefined) {
57
+ if (typeof data.variables !== "object" || data.variables === null) {
58
+ errors.push({
59
+ field: "variables",
60
+ message: "Must be an object",
61
+ })
62
+ } else {
63
+ variables = data.variables as Record<string, unknown>
64
+ }
65
+ }
66
+
67
+ // Validate reason (optional)
68
+ let reason: string | undefined
69
+ if (data.reason !== undefined) {
70
+ if (typeof data.reason !== "string") {
71
+ errors.push({
72
+ field: "reason",
73
+ message: "Must be a string",
74
+ })
75
+ } else if (data.reason.length > 5000) {
76
+ errors.push({
77
+ field: "reason",
78
+ message: "Reason too long (max 5000 characters)",
79
+ })
80
+ } else {
81
+ reason = data.reason
82
+ }
83
+ }
84
+
85
+ if (errors.length > 0) {
86
+ throw new ValidationException(errors)
87
+ }
88
+
89
+ return {
90
+ template: template as string,
91
+ variables,
92
+ reason,
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Validate request body size (prevent DoS)
98
+ * @param contentLength Content-Length header value
99
+ * @param maxSizeBytes Maximum allowed size in bytes (default: 10MB)
100
+ */
101
+ export function validateRequestSize(
102
+ contentLength: string | null,
103
+ maxSizeBytes: number = 10 * 1024 * 1024
104
+ ): void {
105
+ if (!contentLength) {
106
+ // No Content-Length header - will be checked during parsing
107
+ return
108
+ }
109
+
110
+ const size = parseInt(contentLength, 10)
111
+ if (isNaN(size)) {
112
+ throw new ValidationException([
113
+ { field: "Content-Length", message: "Invalid header value" },
114
+ ])
115
+ }
116
+
117
+ if (size > maxSizeBytes) {
118
+ throw new ValidationException([
119
+ {
120
+ field: "Content-Length",
121
+ message: `Request too large: ${size} bytes (max ${maxSizeBytes} bytes)`,
122
+ },
123
+ ])
124
+ }
125
+ }
@@ -0,0 +1,440 @@
1
+ /**
2
+ * Vessel Bootstrap
3
+ *
4
+ * Universal vessel conversion module.
5
+ * Import this into any application to make it self-aware and activity-controlled.
6
+ *
7
+ * Core Concept: Continuous Learning
8
+ * ---------------------------------
9
+ * The vessel doesn't have a separate "observation mode" - it continuously learns
10
+ * during normal operation. When activities execute:
11
+ *
12
+ * Intent → Code (instructional → functional, choosing what to execute)
13
+ * Code → Outcome (instructional → functional, execution results)
14
+ *
15
+ * Both transformations are mediated by activities. The existing infrastructure
16
+ * handles this automatically:
17
+ *
18
+ * 1. ActivityExecutor captures execution traces
19
+ * 2. Backend stores traces and applies Thompson Sampling
20
+ * 3. runtime-mapping maps traces to source and infers intent
21
+ *
22
+ * The burrow command lays the groundwork (injects bootstrap, creates config).
23
+ * Normal operation continuously builds understanding through execution traces.
24
+ *
25
+ * Usage:
26
+ * ```typescript
27
+ * import { initializeVessel } from '@metabob/minibob/vessel-bootstrap'
28
+ *
29
+ * const vessel = await initializeVessel({
30
+ * workingDirectory: __dirname,
31
+ * provider: 'anthropic',
32
+ * apiKey: process.env.ANTHROPIC_API_KEY!,
33
+ * model: 'claude-sonnet-4-20250514',
34
+ * vesselType: 'my-app',
35
+ * })
36
+ *
37
+ * // Register components for runtime access
38
+ * vessel.state.register('myComponent', myComponentInstance)
39
+ *
40
+ * // Run activities - learning happens automatically via execution traces
41
+ * await vessel.runGoal('Optimize database query performance')
42
+ * ```
43
+ */
44
+
45
+ import { ActivityExecutor, type ExecutorConfig } from './activity'
46
+ import { GoalProcessor } from './goal-processor'
47
+ import type { ActivityTemplate } from './types'
48
+ import { initializeMCP } from './mcp'
49
+
50
+ const log = {
51
+ info: (...args: any[]) => console.log('[Vessel]', ...args),
52
+ warn: (...args: any[]) => console.warn('[Vessel]', ...args),
53
+ error: (...args: any[]) => console.error('[Vessel]', ...args),
54
+ debug: (...args: any[]) => console.debug('[Vessel]', ...args),
55
+ }
56
+
57
+ export interface VesselConfig extends ExecutorConfig {
58
+ /**
59
+ * Vessel type identifier (for telemetry)
60
+ */
61
+ vesselType?: string
62
+
63
+ /**
64
+ * Enable self-improvement loop
65
+ * Vessel will periodically run optimization activities on itself
66
+ */
67
+ enableSelfImprovement?: boolean
68
+
69
+ /**
70
+ * Self-improvement interval (ms)
71
+ * Default: 5 minutes
72
+ */
73
+ selfImprovementInterval?: number
74
+ }
75
+
76
+ export interface VesselState {
77
+ /**
78
+ * Components registered for runtime access
79
+ */
80
+ registry: Map<string, any>
81
+
82
+ /**
83
+ * Vessel metadata
84
+ */
85
+ metadata: {
86
+ type: string
87
+ startTime: number
88
+ workingDirectory: string
89
+ }
90
+
91
+ /**
92
+ * Performance metrics
93
+ */
94
+ metrics: {
95
+ activitiesExecuted: number
96
+ goalsExecuted: number
97
+ totalCost: number
98
+ avgDuration: number
99
+ uptime: number
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Vessel Bootstrap
105
+ *
106
+ * Converts any application into a self-aware, activity-controlled vessel.
107
+ * Continuous learning happens automatically through execution traces.
108
+ */
109
+ export class VesselBootstrap {
110
+ private executor: ActivityExecutor
111
+ private goalProcessor: GoalProcessor
112
+ public state: VesselStateManager
113
+ private selfImprovementInterval?: NodeJS.Timeout
114
+ private config: VesselConfig
115
+
116
+ constructor(config: VesselConfig) {
117
+ this.config = {
118
+ vesselType: 'generic',
119
+ enableSelfImprovement: false,
120
+ selfImprovementInterval: 5 * 60 * 1000, // 5 minutes
121
+ ...config
122
+ }
123
+
124
+ log.info('Initializing vessel', {
125
+ type: this.config.vesselType,
126
+ workingDirectory: this.config.workingDirectory
127
+ })
128
+
129
+ // Initialize executor
130
+ this.executor = new ActivityExecutor({
131
+ provider: this.config.provider,
132
+ apiKey: this.config.apiKey,
133
+ model: this.config.model,
134
+ workingDirectory: this.config.workingDirectory,
135
+ systemPrompt: this.config.systemPrompt,
136
+ customTools: this.config.customTools,
137
+ maxNestingDepth: this.config.maxNestingDepth,
138
+ })
139
+
140
+ // Initialize goal processor
141
+ this.goalProcessor = new GoalProcessor({
142
+ workingDirectory: this.config.workingDirectory,
143
+ executor: this.executor
144
+ })
145
+
146
+ // Initialize state manager
147
+ this.state = new VesselStateManager({
148
+ type: this.config.vesselType!,
149
+ workingDirectory: this.config.workingDirectory
150
+ })
151
+
152
+ // Register vessel internals
153
+ this.state.register('vessel', {
154
+ executor: this.executor,
155
+ goalProcessor: this.goalProcessor,
156
+ runActivity: this.runActivity.bind(this),
157
+ runGoal: this.runGoal.bind(this),
158
+ getState: () => this.state.snapshot(),
159
+ shutdown: this.shutdown.bind(this)
160
+ })
161
+
162
+ // Start self-improvement loop if enabled
163
+ if (this.config.enableSelfImprovement) {
164
+ this.startSelfImprovementLoop()
165
+ }
166
+
167
+ log.info('Vessel initialized successfully', {
168
+ type: this.config.vesselType,
169
+ selfImprovement: this.config.enableSelfImprovement
170
+ })
171
+ }
172
+
173
+ /**
174
+ * Run activity on this vessel
175
+ *
176
+ * @param template Activity template or template ID
177
+ * @param variables Variables for the activity
178
+ * @returns Activity execution result
179
+ */
180
+ async runActivity(template: ActivityTemplate | string, variables: any = {}) {
181
+ log.info('Running activity on vessel', {
182
+ template: typeof template === 'string' ? template : template.id
183
+ })
184
+
185
+ this.state.incrementMetric('activitiesExecuted')
186
+ const startTime = Date.now()
187
+
188
+ try {
189
+ // If template is string, need to fetch it (not implemented here)
190
+ if (typeof template === 'string') {
191
+ throw new Error('Template fetching not implemented. Pass ActivityTemplate object directly.')
192
+ }
193
+
194
+ const result = await this.executor.execute({
195
+ template,
196
+ variables: {
197
+ ...variables,
198
+ // Inject vessel context
199
+ vesselType: this.config.vesselType,
200
+ vesselWorkingDirectory: this.config.workingDirectory
201
+ }
202
+ })
203
+
204
+ // Update metrics
205
+ const duration = Date.now() - startTime
206
+ this.state.updateMetrics({
207
+ avgDuration: duration,
208
+ totalCost: result.metrics?.cost || 0
209
+ })
210
+
211
+ return result
212
+ } catch (error) {
213
+ log.error('Activity failed', { error: error instanceof Error ? error.message : String(error) })
214
+ throw error
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Run goal on this vessel
220
+ *
221
+ * Execution traces are automatically captured and sent to the backend
222
+ * where Thompson Sampling learns which activities work best.
223
+ *
224
+ * @param goal Natural language goal description
225
+ * @param context Additional context for the goal
226
+ * @param options Goal execution options
227
+ * @returns Goal execution result
228
+ */
229
+ async runGoal(goal: string, context: any = {}, options: any = {}) {
230
+ log.info('Running goal on vessel', { goal: goal.slice(0, 100) })
231
+
232
+ this.state.incrementMetric('goalsExecuted')
233
+
234
+ try {
235
+ const result = await this.goalProcessor.executeGoal(goal, {
236
+ ...context,
237
+ // Inject vessel context
238
+ vesselType: this.config.vesselType,
239
+ vesselWorkingDirectory: this.config.workingDirectory
240
+ }, options)
241
+
242
+ return result
243
+ } catch (error) {
244
+ log.error('Goal failed', { error: error instanceof Error ? error.message : String(error) })
245
+ throw error
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Start self-improvement loop
251
+ * Vessel periodically analyzes and optimizes itself
252
+ */
253
+ private startSelfImprovementLoop() {
254
+ log.info('Starting self-improvement loop', {
255
+ interval: this.config.selfImprovementInterval
256
+ })
257
+
258
+ this.selfImprovementInterval = setInterval(async () => {
259
+ try {
260
+ log.info('Running self-improvement cycle')
261
+
262
+ await this.runGoal(
263
+ 'Analyze vessel performance and apply optimizations if beneficial. Focus on reducing cost and duration while maintaining quality.',
264
+ {
265
+ files: [this.config.workingDirectory + '/**/*.ts'],
266
+ currentMetrics: this.state.snapshot().metrics
267
+ },
268
+ {
269
+ maxActivities: 3,
270
+ maxCost: 2.0
271
+ }
272
+ )
273
+ } catch (error) {
274
+ log.error('Self-improvement cycle failed', {
275
+ error: error instanceof Error ? error.message : String(error)
276
+ })
277
+ }
278
+ }, this.config.selfImprovementInterval)
279
+ }
280
+
281
+ /**
282
+ * Shutdown vessel
283
+ */
284
+ shutdown() {
285
+ log.info('Shutting down vessel')
286
+
287
+ if (this.selfImprovementInterval) {
288
+ clearInterval(this.selfImprovementInterval)
289
+ }
290
+
291
+ log.info('Vessel shutdown complete')
292
+ }
293
+ }
294
+
295
+ /**
296
+ * Vessel State Manager
297
+ *
298
+ * Manages observable state for the vessel
299
+ * Components register themselves for runtime access
300
+ */
301
+ export class VesselStateManager {
302
+ private registry: Map<string, any> = new Map()
303
+ private metadata: VesselState['metadata']
304
+ private metrics: VesselState['metrics']
305
+
306
+ constructor(config: { type: string, workingDirectory: string }) {
307
+ this.metadata = {
308
+ type: config.type,
309
+ startTime: Date.now(),
310
+ workingDirectory: config.workingDirectory
311
+ }
312
+
313
+ this.metrics = {
314
+ activitiesExecuted: 0,
315
+ goalsExecuted: 0,
316
+ totalCost: 0,
317
+ avgDuration: 0,
318
+ uptime: 0
319
+ }
320
+ }
321
+
322
+ /**
323
+ * Register component for runtime access
324
+ *
325
+ * @param name Component identifier
326
+ * @param component Any object to make accessible
327
+ */
328
+ register(name: string, component: any) {
329
+ this.registry.set(name, component)
330
+ log.debug('Component registered', { name })
331
+ }
332
+
333
+ /**
334
+ * Get registered component
335
+ */
336
+ get(name: string): any {
337
+ return this.registry.get(name)
338
+ }
339
+
340
+ /**
341
+ * List all registered components
342
+ */
343
+ list(): string[] {
344
+ return Array.from(this.registry.keys())
345
+ }
346
+
347
+ /**
348
+ * Get full state snapshot
349
+ */
350
+ snapshot(): VesselState {
351
+ this.metrics.uptime = Date.now() - this.metadata.startTime
352
+
353
+ return {
354
+ registry: this.registry,
355
+ metadata: this.metadata,
356
+ metrics: { ...this.metrics }
357
+ }
358
+ }
359
+
360
+ /**
361
+ * Increment metric
362
+ */
363
+ incrementMetric(metric: keyof VesselState['metrics']) {
364
+ if (typeof this.metrics[metric] === 'number') {
365
+ (this.metrics[metric] as number)++
366
+ }
367
+ }
368
+
369
+ /**
370
+ * Update metrics
371
+ */
372
+ updateMetrics(updates: Partial<VesselState['metrics']>) {
373
+ if (updates.totalCost !== undefined) {
374
+ this.metrics.totalCost += updates.totalCost
375
+ }
376
+
377
+ if (updates.avgDuration !== undefined) {
378
+ // Running average
379
+ const count = this.metrics.activitiesExecuted || 1
380
+ this.metrics.avgDuration =
381
+ (this.metrics.avgDuration * (count - 1) + updates.avgDuration) / count
382
+ }
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Global vessel instance
388
+ * Applications can access via globalThis.__vessel
389
+ */
390
+ declare global {
391
+ var __vessel: VesselBootstrap | undefined
392
+ }
393
+
394
+ /**
395
+ * Initialize vessel globally
396
+ * Convenience function for applications
397
+ *
398
+ * @param config Vessel configuration
399
+ * @returns Vessel instance
400
+ */
401
+ export async function initializeVessel(config: VesselConfig): Promise<VesselBootstrap> {
402
+ if (globalThis.__vessel) {
403
+ log.warn('Vessel already initialized, returning existing instance')
404
+ return globalThis.__vessel
405
+ }
406
+
407
+ // Initialize MCP first if endpoint configured
408
+ const mcpEndpoint = process.env.MINIBOB_MCP_ENDPOINT
409
+ if (mcpEndpoint) {
410
+ log.info('Initializing MCP client', { endpoint: mcpEndpoint })
411
+ try {
412
+ const client = await initializeMCP({ endpoint: mcpEndpoint })
413
+ if (client) {
414
+ log.info('MCP client initialized successfully')
415
+ } else {
416
+ log.warn('MCP client initialization failed - backend may be unavailable')
417
+ }
418
+ } catch (err) {
419
+ log.error('MCP initialization error:', err instanceof Error ? err.message : String(err))
420
+ }
421
+ } else {
422
+ log.warn('MINIBOB_MCP_ENDPOINT not set, MCP features unavailable')
423
+ }
424
+
425
+ const vessel = new VesselBootstrap(config)
426
+ globalThis.__vessel = vessel
427
+
428
+ // Register shutdown handlers
429
+ process.on('SIGTERM', () => vessel.shutdown())
430
+ process.on('SIGINT', () => vessel.shutdown())
431
+
432
+ return vessel
433
+ }
434
+
435
+ /**
436
+ * Get global vessel instance
437
+ */
438
+ export function getVessel(): VesselBootstrap | undefined {
439
+ return globalThis.__vessel
440
+ }