@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.
- package/ARCHITECTURE.md +255 -0
- package/CHANGELOG.md +112 -0
- package/README.md +380 -0
- package/bin/minibob.js +36 -0
- package/dist/acp-gossip.d.ts +72 -0
- package/dist/acp-gossip.d.ts.map +1 -0
- package/dist/acp-gossip.js +156 -0
- package/dist/acp-gossip.js.map +1 -0
- package/dist/acp.d.ts +62 -0
- package/dist/acp.d.ts.map +1 -0
- package/dist/acp.js +292 -0
- package/dist/acp.js.map +1 -0
- package/dist/activity.d.ts +157 -0
- package/dist/activity.d.ts.map +1 -0
- package/dist/activity.js +518 -0
- package/dist/activity.js.map +1 -0
- package/dist/agent-runtime.d.ts +104 -0
- package/dist/agent-runtime.d.ts.map +1 -0
- package/dist/boredom.d.ts +125 -0
- package/dist/boredom.d.ts.map +1 -0
- package/dist/boredom.js +244 -0
- package/dist/boredom.js.map +1 -0
- package/dist/cli/acp-server.d.ts +23 -0
- package/dist/cli/acp-server.d.ts.map +1 -0
- package/dist/cli/burrow.d.ts +26 -0
- package/dist/cli/burrow.d.ts.map +1 -0
- package/dist/cli/doctor.d.ts +22 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/goal.d.ts +22 -0
- package/dist/cli/goal.d.ts.map +1 -0
- package/dist/cli/index.d.ts +47 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/instance-registry.d.ts +78 -0
- package/dist/cli/instance-registry.d.ts.map +1 -0
- package/dist/cli/observe.d.ts +35 -0
- package/dist/cli/observe.d.ts.map +1 -0
- package/dist/cli/vessel.d.ts +14 -0
- package/dist/cli/vessel.d.ts.map +1 -0
- package/dist/composition-observer.d.ts +96 -0
- package/dist/composition-observer.d.ts.map +1 -0
- package/dist/config.d.ts +36 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +128 -0
- package/dist/config.js.map +1 -0
- package/dist/docker/Dockerfile +35 -0
- package/dist/environment.d.ts +72 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +142 -0
- package/dist/environment.js.map +1 -0
- package/dist/goal-processor.d.ts +165 -0
- package/dist/goal-processor.d.ts.map +1 -0
- package/dist/helm/minibob-cluster/Chart.yaml +13 -0
- package/dist/helm/minibob-cluster/templates/_helpers.tpl +60 -0
- package/dist/helm/minibob-cluster/templates/configmap.yaml +11 -0
- package/dist/helm/minibob-cluster/templates/deployment.yaml +108 -0
- package/dist/helm/minibob-cluster/templates/secret.yaml +10 -0
- package/dist/helm/minibob-cluster/templates/service.yaml +37 -0
- package/dist/helm/minibob-cluster/values-local.yaml +41 -0
- package/dist/helm/minibob-cluster/values-production.yaml +57 -0
- package/dist/helm/minibob-cluster/values-testing-cluster.yaml +43 -0
- package/dist/helm/minibob-cluster/values.yaml +127 -0
- package/dist/improviser.d.ts +74 -0
- package/dist/improviser.d.ts.map +1 -0
- package/dist/impulse-filter.d.ts +74 -0
- package/dist/impulse-filter.d.ts.map +1 -0
- package/dist/impulse.d.ts +92 -0
- package/dist/impulse.d.ts.map +1 -0
- package/dist/impulse.js +234 -0
- package/dist/impulse.js.map +1 -0
- package/dist/lib.d.ts +29 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +18561 -0
- package/dist/lib.js.map +98 -0
- package/dist/lifecycle-hooks.d.ts +99 -0
- package/dist/lifecycle-hooks.d.ts.map +1 -0
- package/dist/lifecycle-hooks.js +135 -0
- package/dist/lifecycle-hooks.js.map +1 -0
- package/dist/llm.d.ts +31 -0
- package/dist/llm.d.ts.map +1 -0
- package/dist/llm.js +349 -0
- package/dist/llm.js.map +1 -0
- package/dist/mcp-activity-bridge.d.ts +66 -0
- package/dist/mcp-activity-bridge.d.ts.map +1 -0
- package/dist/mcp-activity-bridge.js +126 -0
- package/dist/mcp-activity-bridge.js.map +1 -0
- package/dist/mcp.d.ts +216 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +292 -0
- package/dist/mcp.js.map +1 -0
- package/dist/memory-agent.d.ts +92 -0
- package/dist/memory-agent.d.ts.map +1 -0
- package/dist/memory-agent.js +277 -0
- package/dist/memory-agent.js.map +1 -0
- package/dist/runtime-mapping.d.ts +97 -0
- package/dist/runtime-mapping.d.ts.map +1 -0
- package/dist/search-first-executor.d.ts +113 -0
- package/dist/search-first-executor.d.ts.map +1 -0
- package/dist/session.d.ts +48 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/template-extractor.d.ts +9 -0
- package/dist/template-extractor.d.ts.map +1 -0
- package/dist/template-generator.d.ts +12 -0
- package/dist/template-generator.d.ts.map +1 -0
- package/dist/tools.d.ts +58 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +771 -0
- package/dist/tools.js.map +1 -0
- package/dist/types.d.ts +503 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/understanding/analyzer.d.ts +55 -0
- package/dist/understanding/analyzer.d.ts.map +1 -0
- package/dist/understanding/explorer.d.ts +73 -0
- package/dist/understanding/explorer.d.ts.map +1 -0
- package/dist/understanding/index.d.ts +7 -0
- package/dist/understanding/index.d.ts.map +1 -0
- package/dist/understanding/types.d.ts +136 -0
- package/dist/understanding/types.d.ts.map +1 -0
- package/dist/validation.d.ts +29 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +106 -0
- package/dist/validation.js.map +1 -0
- package/dist/vessel-bootstrap.d.ts +190 -0
- package/dist/vessel-bootstrap.d.ts.map +1 -0
- package/dist/vessel-registry.d.ts +229 -0
- package/dist/vessel-registry.d.ts.map +1 -0
- package/index.ts +1329 -0
- package/package.json +54 -0
- package/src/acp-gossip.ts +193 -0
- package/src/acp.ts +362 -0
- package/src/activity.ts +1464 -0
- package/src/agent-runtime.ts +365 -0
- package/src/boredom.ts +423 -0
- package/src/cli/acp-server.ts +377 -0
- package/src/cli/burrow.ts +896 -0
- package/src/cli/doctor.ts +526 -0
- package/src/cli/goal.ts +224 -0
- package/src/cli/index.ts +147 -0
- package/src/cli/instance-registry.ts +271 -0
- package/src/cli/observe.ts +682 -0
- package/src/cli/vessel.ts +287 -0
- package/src/components/SystemOverview.tsx +331 -0
- package/src/composition-observer.ts +449 -0
- package/src/config.ts +172 -0
- package/src/environment.ts +167 -0
- package/src/goal-processor.ts +654 -0
- package/src/improviser.ts +591 -0
- package/src/impulse-filter.ts +273 -0
- package/src/impulse.ts +311 -0
- package/src/lib.ts +147 -0
- package/src/lifecycle-hooks.ts +181 -0
- package/src/llm.ts +434 -0
- package/src/mcp-activity-bridge.ts +158 -0
- package/src/mcp.ts +747 -0
- package/src/memory-agent.ts +316 -0
- package/src/runtime-mapping.ts +527 -0
- package/src/search-first-executor.ts +666 -0
- package/src/session.ts +141 -0
- package/src/template-extractor.ts +256 -0
- package/src/template-generator.ts +130 -0
- package/src/tools.ts +924 -0
- package/src/types.ts +497 -0
- package/src/understanding/analyzer.ts +354 -0
- package/src/understanding/explorer.ts +488 -0
- package/src/understanding/index.ts +27 -0
- package/src/understanding/types.ts +153 -0
- package/src/validation.ts +125 -0
- package/src/vessel-bootstrap.ts +440 -0
- package/src/vessel-registry.ts +621 -0
- package/templates/core/edit-file.json +85 -0
- package/templates/understanding/diagnose-problem.json +32 -0
- package/templates/understanding/explore-codebase-v2.json +57 -0
- 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
|
+
}
|