@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,621 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vessel Registry
|
|
3
|
+
*
|
|
4
|
+
* Tracks codebases that have been burrowed into and converted to vessels.
|
|
5
|
+
* Each vessel has:
|
|
6
|
+
* - Unique identity
|
|
7
|
+
* - Learned activities (from execution traces)
|
|
8
|
+
* - Defined impulses (context data)
|
|
9
|
+
* - Registered hooks (lifecycle events)
|
|
10
|
+
* - Provided tools (capabilities)
|
|
11
|
+
* - Composition graph (how activities relate)
|
|
12
|
+
*
|
|
13
|
+
* The registry enables:
|
|
14
|
+
* - Discovery: Find vessels available for use
|
|
15
|
+
* - Selection: Choose which vessel's activities to use
|
|
16
|
+
* - Learning: Track activity performance per vessel
|
|
17
|
+
* - Development: Debug and evolve vessel capabilities
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'
|
|
21
|
+
import { join, resolve } from 'path'
|
|
22
|
+
|
|
23
|
+
// Backend API endpoint for vessel registry sync
|
|
24
|
+
const BACKEND_ENDPOINT = process.env.MINIBOB_MCP_ENDPOINT || 'http://api.minibob.local'
|
|
25
|
+
|
|
26
|
+
// =============================================================================
|
|
27
|
+
// TYPES
|
|
28
|
+
// =============================================================================
|
|
29
|
+
|
|
30
|
+
export interface VesselIdentity {
|
|
31
|
+
/** Unique vessel ID */
|
|
32
|
+
id: string
|
|
33
|
+
/** Human-readable name */
|
|
34
|
+
name: string
|
|
35
|
+
/** Path to the codebase */
|
|
36
|
+
path: string
|
|
37
|
+
/** When the vessel was created (burrowed into) */
|
|
38
|
+
createdAt: number
|
|
39
|
+
/** Last time the vessel was active */
|
|
40
|
+
lastActiveAt: number
|
|
41
|
+
/** Version (from package.json or inferred) */
|
|
42
|
+
version?: string
|
|
43
|
+
/** Description */
|
|
44
|
+
description?: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface VesselActivity {
|
|
48
|
+
/** Activity ID */
|
|
49
|
+
id: string
|
|
50
|
+
/** Activity name */
|
|
51
|
+
name: string
|
|
52
|
+
/** How the activity was created */
|
|
53
|
+
origin: 'extracted' | 'defined' | 'improvised'
|
|
54
|
+
/** Source location in code (if known) */
|
|
55
|
+
sourceLocation?: {
|
|
56
|
+
file: string
|
|
57
|
+
line: number
|
|
58
|
+
function?: string
|
|
59
|
+
}
|
|
60
|
+
/** Execution count */
|
|
61
|
+
executions: number
|
|
62
|
+
/** Success rate */
|
|
63
|
+
successRate: number
|
|
64
|
+
/** Average duration (ms) */
|
|
65
|
+
avgDuration: number
|
|
66
|
+
/** Thompson Sampling parameters */
|
|
67
|
+
thompson: {
|
|
68
|
+
alpha: number
|
|
69
|
+
beta: number
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface VesselImpulse {
|
|
74
|
+
/** Impulse ID */
|
|
75
|
+
id: string
|
|
76
|
+
/** Impulse type */
|
|
77
|
+
type: 'config' | 'context' | 'feature-flag' | 'mock' | 'override'
|
|
78
|
+
/** Description */
|
|
79
|
+
description?: string
|
|
80
|
+
/** Default value */
|
|
81
|
+
defaultValue?: unknown
|
|
82
|
+
/** Current value (if set) */
|
|
83
|
+
currentValue?: unknown
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface VesselHook {
|
|
87
|
+
/** Hook ID */
|
|
88
|
+
id: string
|
|
89
|
+
/** Hook type */
|
|
90
|
+
type: 'startup' | 'shutdown' | 'error' | 'activity.before' | 'activity.after' | 'custom'
|
|
91
|
+
/** Handler location */
|
|
92
|
+
handler: string
|
|
93
|
+
/** Whether the hook is enabled */
|
|
94
|
+
enabled: boolean
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface VesselTool {
|
|
98
|
+
/** Tool name */
|
|
99
|
+
name: string
|
|
100
|
+
/** Tool description */
|
|
101
|
+
description: string
|
|
102
|
+
/** Input schema */
|
|
103
|
+
inputSchema?: Record<string, unknown>
|
|
104
|
+
/** How to invoke the tool */
|
|
105
|
+
invocation: {
|
|
106
|
+
type: 'function' | 'command' | 'http' | 'mcp'
|
|
107
|
+
target: string
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface VesselComposition {
|
|
112
|
+
/** Activity compositions recorded */
|
|
113
|
+
compositions: Array<{
|
|
114
|
+
parent: string
|
|
115
|
+
child: string
|
|
116
|
+
count: number
|
|
117
|
+
successRate: number
|
|
118
|
+
}>
|
|
119
|
+
/** Inferred activity sequences */
|
|
120
|
+
sequences: Array<{
|
|
121
|
+
activities: string[]
|
|
122
|
+
count: number
|
|
123
|
+
avgDuration: number
|
|
124
|
+
}>
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface Vessel extends VesselIdentity {
|
|
128
|
+
/** Activities available in this vessel */
|
|
129
|
+
activities: VesselActivity[]
|
|
130
|
+
/** Impulses defined for this vessel */
|
|
131
|
+
impulses: VesselImpulse[]
|
|
132
|
+
/** Hooks registered for this vessel */
|
|
133
|
+
hooks: VesselHook[]
|
|
134
|
+
/** Tools provided by this vessel */
|
|
135
|
+
tools: VesselTool[]
|
|
136
|
+
/** Activity composition data */
|
|
137
|
+
composition: VesselComposition
|
|
138
|
+
/** Runtime mapping data */
|
|
139
|
+
mapping: RuntimeMapping
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// =============================================================================
|
|
143
|
+
// RUNTIME MAPPING
|
|
144
|
+
// =============================================================================
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Maps runtime behavior to source code and intent
|
|
148
|
+
*/
|
|
149
|
+
export interface RuntimeMapping {
|
|
150
|
+
/** Function call → source location */
|
|
151
|
+
functionMap: Map<string, SourceLocation>
|
|
152
|
+
/** Source location → inferred intent */
|
|
153
|
+
intentMap: Map<string, InferredIntent>
|
|
154
|
+
/** Execution patterns → activities */
|
|
155
|
+
patternMap: Map<string, string>
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface SourceLocation {
|
|
159
|
+
file: string
|
|
160
|
+
line: number
|
|
161
|
+
column?: number
|
|
162
|
+
functionName?: string
|
|
163
|
+
className?: string
|
|
164
|
+
moduleName?: string
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export interface InferredIntent {
|
|
168
|
+
/** What the code does */
|
|
169
|
+
purpose: string
|
|
170
|
+
/** Why it exists */
|
|
171
|
+
rationale?: string
|
|
172
|
+
/** Expected inputs */
|
|
173
|
+
inputs?: string[]
|
|
174
|
+
/** Expected outputs */
|
|
175
|
+
outputs?: string[]
|
|
176
|
+
/** Constraints */
|
|
177
|
+
constraints?: string[]
|
|
178
|
+
/** Success criteria */
|
|
179
|
+
successCriteria?: string[]
|
|
180
|
+
/** Confidence in this inference */
|
|
181
|
+
confidence: number
|
|
182
|
+
/** How the intent was inferred */
|
|
183
|
+
source: 'function-name' | 'comment' | 'docstring' | 'variable-names' | 'data-flow' | 'llm-analysis'
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// =============================================================================
|
|
187
|
+
// REGISTRY STORAGE
|
|
188
|
+
// =============================================================================
|
|
189
|
+
|
|
190
|
+
const REGISTRY_DIR = '.minibob/registry'
|
|
191
|
+
const VESSELS_FILE = 'vessels.json'
|
|
192
|
+
|
|
193
|
+
interface RegistryData {
|
|
194
|
+
version: string
|
|
195
|
+
vessels: Record<string, Omit<Vessel, 'mapping'> & { mapping?: any }>
|
|
196
|
+
lastUpdated: number
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function getRegistryPath(): string {
|
|
200
|
+
// Use home directory for global registry
|
|
201
|
+
const home = process.env.HOME || process.env.USERPROFILE || '.'
|
|
202
|
+
return join(home, REGISTRY_DIR)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function ensureRegistryDir(): void {
|
|
206
|
+
const dir = getRegistryPath()
|
|
207
|
+
if (!existsSync(dir)) {
|
|
208
|
+
mkdirSync(dir, { recursive: true })
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function loadRegistry(): RegistryData {
|
|
213
|
+
ensureRegistryDir()
|
|
214
|
+
const filePath = join(getRegistryPath(), VESSELS_FILE)
|
|
215
|
+
|
|
216
|
+
if (!existsSync(filePath)) {
|
|
217
|
+
return {
|
|
218
|
+
version: '1.0.0',
|
|
219
|
+
vessels: {},
|
|
220
|
+
lastUpdated: Date.now(),
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
const data = readFileSync(filePath, 'utf-8')
|
|
226
|
+
return JSON.parse(data)
|
|
227
|
+
} catch (error) {
|
|
228
|
+
console.warn('[VesselRegistry] Failed to load registry, starting fresh')
|
|
229
|
+
return {
|
|
230
|
+
version: '1.0.0',
|
|
231
|
+
vessels: {},
|
|
232
|
+
lastUpdated: Date.now(),
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function saveRegistry(data: RegistryData): void {
|
|
238
|
+
ensureRegistryDir()
|
|
239
|
+
const filePath = join(getRegistryPath(), VESSELS_FILE)
|
|
240
|
+
data.lastUpdated = Date.now()
|
|
241
|
+
writeFileSync(filePath, JSON.stringify(data, null, 2))
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// =============================================================================
|
|
245
|
+
// BACKEND SYNC
|
|
246
|
+
// =============================================================================
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Sync vessel registration with backend
|
|
250
|
+
*/
|
|
251
|
+
async function syncVesselToBackend(vessel: Vessel, functionMappings?: Array<{
|
|
252
|
+
functionName: string
|
|
253
|
+
file: string
|
|
254
|
+
line: number
|
|
255
|
+
className?: string
|
|
256
|
+
intent?: InferredIntent
|
|
257
|
+
}>): Promise<boolean> {
|
|
258
|
+
try {
|
|
259
|
+
const payload = {
|
|
260
|
+
vessel_id: vessel.id,
|
|
261
|
+
name: vessel.name,
|
|
262
|
+
path: vessel.path,
|
|
263
|
+
description: vessel.description,
|
|
264
|
+
version: vessel.version,
|
|
265
|
+
framework: (vessel as any).framework,
|
|
266
|
+
activities: vessel.activities,
|
|
267
|
+
impulses: vessel.impulses,
|
|
268
|
+
hooks: vessel.hooks,
|
|
269
|
+
tools: vessel.tools,
|
|
270
|
+
composition: vessel.composition,
|
|
271
|
+
functionMappings: functionMappings || [],
|
|
272
|
+
created_at: new Date(vessel.createdAt).toISOString(),
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const response = await fetch(`${BACKEND_ENDPOINT}/v2/vessels/codebase/register`, {
|
|
276
|
+
method: 'POST',
|
|
277
|
+
headers: { 'Content-Type': 'application/json' },
|
|
278
|
+
body: JSON.stringify(payload),
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
if (!response.ok) {
|
|
282
|
+
const text = await response.text()
|
|
283
|
+
console.warn(`[VesselRegistry] Backend sync failed: ${response.status} ${text}`)
|
|
284
|
+
return false
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const result = await response.json()
|
|
288
|
+
console.log(`[VesselRegistry] Synced to backend: ${vessel.name} (${result.stored?.functionMappings || 0} mappings)`)
|
|
289
|
+
return true
|
|
290
|
+
|
|
291
|
+
} catch (error) {
|
|
292
|
+
console.warn(`[VesselRegistry] Backend sync failed:`, error instanceof Error ? error.message : error)
|
|
293
|
+
return false
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Record activity execution to backend (for Thompson Sampling)
|
|
299
|
+
*/
|
|
300
|
+
export async function recordActivityExecution(
|
|
301
|
+
vesselId: string,
|
|
302
|
+
activityId: string,
|
|
303
|
+
success: boolean,
|
|
304
|
+
durationMs: number
|
|
305
|
+
): Promise<boolean> {
|
|
306
|
+
try {
|
|
307
|
+
const response = await fetch(
|
|
308
|
+
`${BACKEND_ENDPOINT}/v2/vessels/registry/${vesselId}/activities/${activityId}/execution`,
|
|
309
|
+
{
|
|
310
|
+
method: 'POST',
|
|
311
|
+
headers: { 'Content-Type': 'application/json' },
|
|
312
|
+
body: JSON.stringify({ success, duration_ms: durationMs }),
|
|
313
|
+
}
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
if (!response.ok) {
|
|
317
|
+
console.warn(`[VesselRegistry] Failed to record execution: ${response.status}`)
|
|
318
|
+
return false
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return true
|
|
322
|
+
} catch (error) {
|
|
323
|
+
console.warn(`[VesselRegistry] Failed to record execution:`, error instanceof Error ? error.message : error)
|
|
324
|
+
return false
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// =============================================================================
|
|
329
|
+
// REGISTRY OPERATIONS
|
|
330
|
+
// =============================================================================
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Register a new vessel (after burrowing into a codebase)
|
|
334
|
+
*/
|
|
335
|
+
export function registerVessel(vessel: Omit<Vessel, 'id' | 'createdAt' | 'lastActiveAt' | 'mapping'>): Vessel {
|
|
336
|
+
const registry = loadRegistry()
|
|
337
|
+
|
|
338
|
+
// Generate ID from path
|
|
339
|
+
const normalizedPath = resolve(vessel.path)
|
|
340
|
+
const id = `vessel_${Buffer.from(normalizedPath).toString('base64url').slice(0, 16)}`
|
|
341
|
+
|
|
342
|
+
// Check if already registered
|
|
343
|
+
if (registry.vessels[id]) {
|
|
344
|
+
console.log(`[VesselRegistry] Vessel already registered: ${id}`)
|
|
345
|
+
return registry.vessels[id] as any
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const now = Date.now()
|
|
349
|
+
const fullVessel: Vessel = {
|
|
350
|
+
...vessel,
|
|
351
|
+
id,
|
|
352
|
+
createdAt: now,
|
|
353
|
+
lastActiveAt: now,
|
|
354
|
+
mapping: {
|
|
355
|
+
functionMap: new Map(),
|
|
356
|
+
intentMap: new Map(),
|
|
357
|
+
patternMap: new Map(),
|
|
358
|
+
},
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Store without Map objects (they don't serialize)
|
|
362
|
+
registry.vessels[id] = {
|
|
363
|
+
...fullVessel,
|
|
364
|
+
mapping: {
|
|
365
|
+
functionMap: {},
|
|
366
|
+
intentMap: {},
|
|
367
|
+
patternMap: {},
|
|
368
|
+
},
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
saveRegistry(registry)
|
|
372
|
+
console.log(`[VesselRegistry] Registered vessel: ${vessel.name} (${id})`)
|
|
373
|
+
|
|
374
|
+
return fullVessel
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Get a vessel by ID
|
|
379
|
+
*/
|
|
380
|
+
export function getVessel(id: string): Vessel | null {
|
|
381
|
+
const registry = loadRegistry()
|
|
382
|
+
const vessel = registry.vessels[id]
|
|
383
|
+
|
|
384
|
+
if (!vessel) return null
|
|
385
|
+
|
|
386
|
+
// Reconstitute Map objects
|
|
387
|
+
return {
|
|
388
|
+
...vessel,
|
|
389
|
+
mapping: {
|
|
390
|
+
functionMap: new Map(Object.entries(vessel.mapping?.functionMap || {})),
|
|
391
|
+
intentMap: new Map(Object.entries(vessel.mapping?.intentMap || {})),
|
|
392
|
+
patternMap: new Map(Object.entries(vessel.mapping?.patternMap || {})),
|
|
393
|
+
},
|
|
394
|
+
} as Vessel
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Get vessel by path
|
|
399
|
+
*/
|
|
400
|
+
export function getVesselByPath(path: string): Vessel | null {
|
|
401
|
+
const normalizedPath = resolve(path)
|
|
402
|
+
const registry = loadRegistry()
|
|
403
|
+
|
|
404
|
+
for (const vessel of Object.values(registry.vessels)) {
|
|
405
|
+
if (resolve(vessel.path) === normalizedPath) {
|
|
406
|
+
return getVessel(vessel.id)
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return null
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* List all registered vessels
|
|
415
|
+
*/
|
|
416
|
+
export function listVessels(): VesselIdentity[] {
|
|
417
|
+
const registry = loadRegistry()
|
|
418
|
+
return Object.values(registry.vessels).map(v => ({
|
|
419
|
+
id: v.id,
|
|
420
|
+
name: v.name,
|
|
421
|
+
path: v.path,
|
|
422
|
+
createdAt: v.createdAt,
|
|
423
|
+
lastActiveAt: v.lastActiveAt,
|
|
424
|
+
version: v.version,
|
|
425
|
+
description: v.description,
|
|
426
|
+
}))
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Update vessel activity data
|
|
431
|
+
*/
|
|
432
|
+
export function updateVesselActivities(vesselId: string, activities: VesselActivity[]): void {
|
|
433
|
+
const registry = loadRegistry()
|
|
434
|
+
|
|
435
|
+
if (!registry.vessels[vesselId]) {
|
|
436
|
+
throw new Error(`Vessel not found: ${vesselId}`)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
registry.vessels[vesselId].activities = activities
|
|
440
|
+
registry.vessels[vesselId].lastActiveAt = Date.now()
|
|
441
|
+
saveRegistry(registry)
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Add an activity to a vessel
|
|
446
|
+
*/
|
|
447
|
+
export function addVesselActivity(vesselId: string, activity: VesselActivity): void {
|
|
448
|
+
const registry = loadRegistry()
|
|
449
|
+
|
|
450
|
+
if (!registry.vessels[vesselId]) {
|
|
451
|
+
throw new Error(`Vessel not found: ${vesselId}`)
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const existing = registry.vessels[vesselId].activities.findIndex(a => a.id === activity.id)
|
|
455
|
+
if (existing >= 0) {
|
|
456
|
+
registry.vessels[vesselId].activities[existing] = activity
|
|
457
|
+
} else {
|
|
458
|
+
registry.vessels[vesselId].activities.push(activity)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
registry.vessels[vesselId].lastActiveAt = Date.now()
|
|
462
|
+
saveRegistry(registry)
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Update runtime mapping for a vessel
|
|
467
|
+
*/
|
|
468
|
+
export function updateVesselMapping(
|
|
469
|
+
vesselId: string,
|
|
470
|
+
type: 'function' | 'intent' | 'pattern',
|
|
471
|
+
key: string,
|
|
472
|
+
value: SourceLocation | InferredIntent | string
|
|
473
|
+
): void {
|
|
474
|
+
const registry = loadRegistry()
|
|
475
|
+
|
|
476
|
+
if (!registry.vessels[vesselId]) {
|
|
477
|
+
throw new Error(`Vessel not found: ${vesselId}`)
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (!registry.vessels[vesselId].mapping) {
|
|
481
|
+
registry.vessels[vesselId].mapping = {
|
|
482
|
+
functionMap: {},
|
|
483
|
+
intentMap: {},
|
|
484
|
+
patternMap: {},
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
switch (type) {
|
|
489
|
+
case 'function':
|
|
490
|
+
registry.vessels[vesselId].mapping.functionMap[key] = value as SourceLocation
|
|
491
|
+
break
|
|
492
|
+
case 'intent':
|
|
493
|
+
registry.vessels[vesselId].mapping.intentMap[key] = value as InferredIntent
|
|
494
|
+
break
|
|
495
|
+
case 'pattern':
|
|
496
|
+
registry.vessels[vesselId].mapping.patternMap[key] = value as string
|
|
497
|
+
break
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
saveRegistry(registry)
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Record activity composition for a vessel
|
|
505
|
+
*/
|
|
506
|
+
export function recordVesselComposition(
|
|
507
|
+
vesselId: string,
|
|
508
|
+
parent: string,
|
|
509
|
+
child: string,
|
|
510
|
+
success: boolean
|
|
511
|
+
): void {
|
|
512
|
+
const registry = loadRegistry()
|
|
513
|
+
|
|
514
|
+
if (!registry.vessels[vesselId]) {
|
|
515
|
+
throw new Error(`Vessel not found: ${vesselId}`)
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const compositions = registry.vessels[vesselId].composition?.compositions || []
|
|
519
|
+
const existing = compositions.find(c => c.parent === parent && c.child === child)
|
|
520
|
+
|
|
521
|
+
if (existing) {
|
|
522
|
+
existing.count++
|
|
523
|
+
// Update success rate
|
|
524
|
+
const successCount = Math.round(existing.successRate * (existing.count - 1) / 100)
|
|
525
|
+
existing.successRate = ((successCount + (success ? 1 : 0)) / existing.count) * 100
|
|
526
|
+
} else {
|
|
527
|
+
compositions.push({
|
|
528
|
+
parent,
|
|
529
|
+
child,
|
|
530
|
+
count: 1,
|
|
531
|
+
successRate: success ? 100 : 0,
|
|
532
|
+
})
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
registry.vessels[vesselId].composition = {
|
|
536
|
+
...registry.vessels[vesselId].composition,
|
|
537
|
+
compositions,
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
saveRegistry(registry)
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Deregister a vessel
|
|
545
|
+
*/
|
|
546
|
+
export function deregisterVessel(id: string): boolean {
|
|
547
|
+
const registry = loadRegistry()
|
|
548
|
+
|
|
549
|
+
if (!registry.vessels[id]) {
|
|
550
|
+
return false
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
delete registry.vessels[id]
|
|
554
|
+
saveRegistry(registry)
|
|
555
|
+
console.log(`[VesselRegistry] Deregistered vessel: ${id}`)
|
|
556
|
+
|
|
557
|
+
return true
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// =============================================================================
|
|
561
|
+
// EXPORTS
|
|
562
|
+
// =============================================================================
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Register a vessel and sync to backend (async version)
|
|
566
|
+
*/
|
|
567
|
+
export async function registerVesselWithSync(
|
|
568
|
+
vessel: Omit<Vessel, 'id' | 'createdAt' | 'lastActiveAt' | 'mapping'>,
|
|
569
|
+
functionMappings?: Array<{
|
|
570
|
+
functionName: string
|
|
571
|
+
file: string
|
|
572
|
+
line: number
|
|
573
|
+
className?: string
|
|
574
|
+
intent?: InferredIntent
|
|
575
|
+
}>
|
|
576
|
+
): Promise<Vessel> {
|
|
577
|
+
// Register locally first
|
|
578
|
+
const registeredVessel = registerVessel(vessel)
|
|
579
|
+
|
|
580
|
+
// Then sync to backend (non-blocking, but we await for reporting)
|
|
581
|
+
await syncVesselToBackend(registeredVessel, functionMappings)
|
|
582
|
+
|
|
583
|
+
return registeredVessel
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Fetch vessel from backend (if local not found)
|
|
588
|
+
*/
|
|
589
|
+
export async function fetchVesselFromBackend(vesselId: string): Promise<Vessel | null> {
|
|
590
|
+
try {
|
|
591
|
+
const response = await fetch(`${BACKEND_ENDPOINT}/v2/vessels/registry/${vesselId}`)
|
|
592
|
+
if (!response.ok) return null
|
|
593
|
+
|
|
594
|
+
const data = await response.json()
|
|
595
|
+
return {
|
|
596
|
+
...data,
|
|
597
|
+
mapping: {
|
|
598
|
+
functionMap: new Map(),
|
|
599
|
+
intentMap: new Map(),
|
|
600
|
+
patternMap: new Map(),
|
|
601
|
+
},
|
|
602
|
+
}
|
|
603
|
+
} catch {
|
|
604
|
+
return null
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
export const vesselRegistry = {
|
|
609
|
+
register: registerVessel,
|
|
610
|
+
registerWithSync: registerVesselWithSync,
|
|
611
|
+
get: getVessel,
|
|
612
|
+
getByPath: getVesselByPath,
|
|
613
|
+
list: listVessels,
|
|
614
|
+
updateActivities: updateVesselActivities,
|
|
615
|
+
addActivity: addVesselActivity,
|
|
616
|
+
updateMapping: updateVesselMapping,
|
|
617
|
+
recordComposition: recordVesselComposition,
|
|
618
|
+
recordExecution: recordActivityExecution,
|
|
619
|
+
deregister: deregisterVessel,
|
|
620
|
+
fetchFromBackend: fetchVesselFromBackend,
|
|
621
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"activity_id": "core-edit-file",
|
|
3
|
+
"variant_id": "core-edit-file-v1",
|
|
4
|
+
"variant_name": "Edit File with Specific Changes",
|
|
5
|
+
"category": "feature",
|
|
6
|
+
"description": "Modify an existing file by making specific changes to its content. Use when goal mentions: change, modify, edit, update, replace values in a file.",
|
|
7
|
+
"scope": "global",
|
|
8
|
+
"task_steps": [
|
|
9
|
+
{
|
|
10
|
+
"id": "read-file",
|
|
11
|
+
"description": "Read the target file to understand current content",
|
|
12
|
+
"dependencies": [],
|
|
13
|
+
"subagent": "general",
|
|
14
|
+
"prompt": {
|
|
15
|
+
"template": "Read the file {{filePath}} to see its current content:\n\nread({ filePath: \"{{filePath}}\" })\n\nTake note of the current values that need to be changed.",
|
|
16
|
+
"maxTokens": 4096,
|
|
17
|
+
"compressionStrategy": "filter",
|
|
18
|
+
"variables": [
|
|
19
|
+
{
|
|
20
|
+
"name": "filePath",
|
|
21
|
+
"type": "string",
|
|
22
|
+
"required": true,
|
|
23
|
+
"description": "Path to file to edit"
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
"retry": {
|
|
28
|
+
"maxAttempts": 2,
|
|
29
|
+
"strategy": "simple"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": "apply-changes",
|
|
34
|
+
"description": "Apply the specified changes using edit tool",
|
|
35
|
+
"dependencies": ["read-file"],
|
|
36
|
+
"subagent": "general",
|
|
37
|
+
"prompt": {
|
|
38
|
+
"template": "Now modify the file {{filePath}} to apply these changes:\n\n{{changes}}\n\nUse the edit tool to make ONLY the specified changes. Be precise with the oldString parameter - it must match the exact content being replaced.\n\nedit({\n filePath: \"{{filePath}}\",\n oldString: \"<exact string to replace>\",\n newString: \"<new string>\"\n})\n\nIf multiple changes are needed, make multiple edit calls.",
|
|
39
|
+
"maxTokens": 4096,
|
|
40
|
+
"compressionStrategy": "filter",
|
|
41
|
+
"variables": [
|
|
42
|
+
{
|
|
43
|
+
"name": "filePath",
|
|
44
|
+
"type": "string",
|
|
45
|
+
"required": true,
|
|
46
|
+
"description": "Path to file to edit"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "changes",
|
|
50
|
+
"type": "string",
|
|
51
|
+
"required": true,
|
|
52
|
+
"description": "Description of what changes to make"
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
"retry": {
|
|
57
|
+
"maxAttempts": 2,
|
|
58
|
+
"strategy": "simple"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"id": "verify-changes",
|
|
63
|
+
"description": "Read the file again to verify changes were applied",
|
|
64
|
+
"dependencies": ["apply-changes"],
|
|
65
|
+
"subagent": "general",
|
|
66
|
+
"prompt": {
|
|
67
|
+
"template": "Verify the changes by reading the file again:\n\nread({ filePath: \"{{filePath}}\" })\n\nConfirm that the requested changes have been applied correctly.",
|
|
68
|
+
"maxTokens": 4096,
|
|
69
|
+
"compressionStrategy": "filter",
|
|
70
|
+
"variables": [
|
|
71
|
+
{
|
|
72
|
+
"name": "filePath",
|
|
73
|
+
"type": "string",
|
|
74
|
+
"required": true,
|
|
75
|
+
"description": "Path to file to edit"
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
"retry": {
|
|
80
|
+
"maxAttempts": 2,
|
|
81
|
+
"strategy": "simple"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
}
|