macro-agent 0.2.2 → 0.2.4
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/dist/boot-v2.d.ts +21 -0
- package/dist/boot-v2.d.ts.map +1 -1
- package/dist/boot-v2.js +53 -1
- package/dist/boot-v2.js.map +1 -1
- package/dist/map/cascade-diff-server.d.ts +43 -0
- package/dist/map/cascade-diff-server.d.ts.map +1 -0
- package/dist/map/cascade-diff-server.js +292 -0
- package/dist/map/cascade-diff-server.js.map +1 -0
- package/dist/map/sidecar.d.ts.map +1 -1
- package/dist/map/sidecar.js +26 -0
- package/dist/map/sidecar.js.map +1 -1
- package/dist/teams/team-loader.d.ts +28 -1
- package/dist/teams/team-loader.d.ts.map +1 -1
- package/dist/teams/team-loader.js +42 -0
- package/dist/teams/team-loader.js.map +1 -1
- package/dist/teams/team-manager-v2.d.ts +20 -0
- package/dist/teams/team-manager-v2.d.ts.map +1 -1
- package/dist/teams/team-manager-v2.js +25 -2
- package/dist/teams/team-manager-v2.js.map +1 -1
- package/dist/workspace/dataplane-adapter.d.ts +260 -0
- package/dist/workspace/dataplane-adapter.d.ts.map +1 -0
- package/dist/workspace/dataplane-adapter.js +416 -0
- package/dist/workspace/dataplane-adapter.js.map +1 -0
- package/package.json +6 -4
- package/renovate.json5 +6 -0
- package/src/boot-v2.ts +97 -1
- package/src/map/__tests__/cascade-diff-server.test.ts +434 -0
- package/src/map/__tests__/sidecar-diff-install-smoke.test.ts +90 -0
- package/src/map/cascade-diff-server.ts +404 -0
- package/src/map/sidecar.ts +29 -0
- package/src/teams/team-loader.ts +57 -0
- package/src/teams/team-manager-v2.ts +41 -3
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dataplane Adapter
|
|
3
|
+
*
|
|
4
|
+
* Wraps MultiAgentRepoTracker to integrate with macro-agent's event system
|
|
5
|
+
* and provide a simplified interface for workspace management.
|
|
6
|
+
*
|
|
7
|
+
* @module workspace/dataplane-adapter
|
|
8
|
+
* @implements [[s-7ktd]] Dataplane Integration section
|
|
9
|
+
*/
|
|
10
|
+
import { MultiAgentRepoTracker, workerTasks, diffStacks, } from 'git-cascade';
|
|
11
|
+
import { DEFAULT_DATAPLANE_CONFIG } from './config.js';
|
|
12
|
+
/**
|
|
13
|
+
* DataplaneAdapter wraps MultiAgentRepoTracker for macro-agent integration.
|
|
14
|
+
*
|
|
15
|
+
* Key responsibilities:
|
|
16
|
+
* - Initialize dataplane with shared or dedicated database
|
|
17
|
+
* - Emit events on dataplane operations
|
|
18
|
+
* - Provide simplified API for workspace management
|
|
19
|
+
*/
|
|
20
|
+
export class DataplaneAdapter {
|
|
21
|
+
tracker;
|
|
22
|
+
config;
|
|
23
|
+
eventListeners = new Set();
|
|
24
|
+
ownsDb;
|
|
25
|
+
/**
|
|
26
|
+
* Create a new DataplaneAdapter.
|
|
27
|
+
*
|
|
28
|
+
* @param config - Dataplane configuration
|
|
29
|
+
*/
|
|
30
|
+
constructor(config) {
|
|
31
|
+
const mergedConfig = {
|
|
32
|
+
...DEFAULT_DATAPLANE_CONFIG,
|
|
33
|
+
...config,
|
|
34
|
+
repoPath: config.repoPath ?? process.cwd(),
|
|
35
|
+
};
|
|
36
|
+
this.config = {
|
|
37
|
+
enabled: mergedConfig.enabled ?? true,
|
|
38
|
+
repoPath: mergedConfig.repoPath,
|
|
39
|
+
tablePrefix: mergedConfig.tablePrefix ?? 'dataplane_',
|
|
40
|
+
verbose: mergedConfig.verbose ?? false,
|
|
41
|
+
skipRecovery: mergedConfig.skipRecovery ?? false,
|
|
42
|
+
};
|
|
43
|
+
// Determine if we own the database connection
|
|
44
|
+
this.ownsDb = !config.db;
|
|
45
|
+
const trackerOptions = {
|
|
46
|
+
repoPath: this.config.repoPath,
|
|
47
|
+
tablePrefix: this.config.tablePrefix,
|
|
48
|
+
verbose: this.config.verbose,
|
|
49
|
+
skipRecovery: this.config.skipRecovery,
|
|
50
|
+
};
|
|
51
|
+
if (config.db) {
|
|
52
|
+
trackerOptions.db = config.db;
|
|
53
|
+
}
|
|
54
|
+
else if (config.dbPath) {
|
|
55
|
+
trackerOptions.dbPath = config.dbPath;
|
|
56
|
+
}
|
|
57
|
+
// If neither db nor dbPath provided, tracker uses default path
|
|
58
|
+
this.tracker = new MultiAgentRepoTracker(trackerOptions);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get whether dataplane is enabled.
|
|
62
|
+
*/
|
|
63
|
+
get enabled() {
|
|
64
|
+
return this.config.enabled;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get the repository path.
|
|
68
|
+
*/
|
|
69
|
+
get repoPath() {
|
|
70
|
+
return this.config.repoPath;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get the underlying database connection.
|
|
74
|
+
* Use with caution - prefer adapter methods for operations.
|
|
75
|
+
*/
|
|
76
|
+
get db() {
|
|
77
|
+
return this.tracker.db;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get the underlying tracker.
|
|
81
|
+
* Use with caution - prefer adapter methods for operations.
|
|
82
|
+
*/
|
|
83
|
+
get rawTracker() {
|
|
84
|
+
return this.tracker;
|
|
85
|
+
}
|
|
86
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
87
|
+
// Event System
|
|
88
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
89
|
+
/**
|
|
90
|
+
* Subscribe to dataplane events.
|
|
91
|
+
*
|
|
92
|
+
* @param callback - Function called when events occur
|
|
93
|
+
* @returns Unsubscribe function
|
|
94
|
+
*/
|
|
95
|
+
onEvent(callback) {
|
|
96
|
+
this.eventListeners.add(callback);
|
|
97
|
+
return () => this.eventListeners.delete(callback);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Emit an event to all listeners.
|
|
101
|
+
*/
|
|
102
|
+
emit(type, data) {
|
|
103
|
+
const event = {
|
|
104
|
+
type,
|
|
105
|
+
timestamp: Date.now(),
|
|
106
|
+
data,
|
|
107
|
+
};
|
|
108
|
+
for (const listener of this.eventListeners) {
|
|
109
|
+
try {
|
|
110
|
+
listener(event);
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
console.error('[DataplaneAdapter] Event listener error:', error);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
118
|
+
// Stream Operations
|
|
119
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
120
|
+
/**
|
|
121
|
+
* Create a new stream (integration branch).
|
|
122
|
+
*
|
|
123
|
+
* @param options - Stream creation options
|
|
124
|
+
* @returns Stream ID
|
|
125
|
+
*/
|
|
126
|
+
createStream(options) {
|
|
127
|
+
const streamId = this.tracker.createStream(options);
|
|
128
|
+
this.emit('stream:created', { streamId, ...options });
|
|
129
|
+
return streamId;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get a stream by ID.
|
|
133
|
+
*/
|
|
134
|
+
getStream(streamId) {
|
|
135
|
+
return this.tracker.getStream(streamId);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* List streams with optional filters.
|
|
139
|
+
*/
|
|
140
|
+
listStreams(options) {
|
|
141
|
+
return this.tracker.listStreams(options);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Update a stream.
|
|
145
|
+
*/
|
|
146
|
+
updateStream(streamId, updates) {
|
|
147
|
+
this.tracker.updateStream(streamId, updates);
|
|
148
|
+
this.emit('stream:updated', { streamId, updates });
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Abandon a stream.
|
|
152
|
+
*/
|
|
153
|
+
abandonStream(streamId, options) {
|
|
154
|
+
this.tracker.abandonStream(streamId, options);
|
|
155
|
+
this.emit('stream:abandoned', { streamId, ...options });
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get the git branch name for a stream.
|
|
159
|
+
*/
|
|
160
|
+
getStreamBranchName(streamId) {
|
|
161
|
+
return this.tracker.getStreamBranchName(streamId);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Get the HEAD commit of a stream.
|
|
165
|
+
*/
|
|
166
|
+
getStreamHead(streamId) {
|
|
167
|
+
return this.tracker.getStreamHead(streamId);
|
|
168
|
+
}
|
|
169
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
170
|
+
// Worktree Operations
|
|
171
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
172
|
+
/**
|
|
173
|
+
* Create a worktree for an agent.
|
|
174
|
+
*
|
|
175
|
+
* @param options - Worktree creation options
|
|
176
|
+
* @returns Created worktree info
|
|
177
|
+
*/
|
|
178
|
+
createWorktree(options) {
|
|
179
|
+
const worktree = this.tracker.createWorktree(options);
|
|
180
|
+
this.emit('worktree:created', { ...worktree });
|
|
181
|
+
return worktree;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Get a worktree by agent ID.
|
|
185
|
+
*/
|
|
186
|
+
getWorktree(agentId) {
|
|
187
|
+
return this.tracker.getWorktree(agentId);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* List all worktrees.
|
|
191
|
+
*/
|
|
192
|
+
listWorktrees() {
|
|
193
|
+
return this.tracker.listWorktrees();
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Update the stream associated with a worktree.
|
|
197
|
+
*/
|
|
198
|
+
updateWorktreeStream(agentId, streamId) {
|
|
199
|
+
this.tracker.updateWorktreeStream(agentId, streamId);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Deallocate a worktree.
|
|
203
|
+
*/
|
|
204
|
+
deallocateWorktree(agentId) {
|
|
205
|
+
this.tracker.deallocateWorktree(agentId);
|
|
206
|
+
this.emit('worktree:deallocated', { agentId });
|
|
207
|
+
}
|
|
208
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
209
|
+
// Worker Task Operations
|
|
210
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
211
|
+
/**
|
|
212
|
+
* Create a worker task under a stream.
|
|
213
|
+
*
|
|
214
|
+
* @param options - Task creation options
|
|
215
|
+
* @returns Task ID
|
|
216
|
+
*/
|
|
217
|
+
createTask(options) {
|
|
218
|
+
const taskId = this.tracker.createTask(options);
|
|
219
|
+
this.emit('task:created', { taskId, ...options });
|
|
220
|
+
return taskId;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Get a task by ID.
|
|
224
|
+
*/
|
|
225
|
+
getTask(taskId) {
|
|
226
|
+
return this.tracker.getTask(taskId);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* List tasks for a stream.
|
|
230
|
+
*/
|
|
231
|
+
listTasks(streamId, options) {
|
|
232
|
+
return this.tracker.listTasks(streamId, options);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Start a task - assigns agent and creates worker branch.
|
|
236
|
+
*
|
|
237
|
+
* @param options - Start task options
|
|
238
|
+
* @returns Branch name and start commit
|
|
239
|
+
*/
|
|
240
|
+
startTask(options) {
|
|
241
|
+
const result = this.tracker.startTask(options);
|
|
242
|
+
this.emit('task:started', {
|
|
243
|
+
taskId: options.taskId,
|
|
244
|
+
agentId: options.agentId,
|
|
245
|
+
branchName: result.branchName,
|
|
246
|
+
startCommit: result.startCommit,
|
|
247
|
+
});
|
|
248
|
+
return result;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Complete a task - merges worker branch to stream.
|
|
252
|
+
*
|
|
253
|
+
* @param options - Complete task options
|
|
254
|
+
* @returns Merge result
|
|
255
|
+
*/
|
|
256
|
+
completeTask(options) {
|
|
257
|
+
const result = this.tracker.completeTask(options);
|
|
258
|
+
this.emit('task:completed', { taskId: options.taskId, ...result });
|
|
259
|
+
return result;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Abandon a task.
|
|
263
|
+
*
|
|
264
|
+
* @param taskId - Task ID
|
|
265
|
+
* @param options - Options
|
|
266
|
+
*/
|
|
267
|
+
abandonTask(taskId, options) {
|
|
268
|
+
this.tracker.abandonTask(taskId, options);
|
|
269
|
+
this.emit('task:abandoned', { taskId, ...options });
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Release a task back to 'open' status.
|
|
273
|
+
*/
|
|
274
|
+
releaseTask(taskId) {
|
|
275
|
+
this.tracker.releaseTask(taskId);
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Detect conflicts for a task before completing.
|
|
279
|
+
*
|
|
280
|
+
* @param taskId - Task ID
|
|
281
|
+
* @param worktree - Worktree path
|
|
282
|
+
* @returns Array of conflicting file paths, empty if no conflicts
|
|
283
|
+
*/
|
|
284
|
+
detectTaskConflicts(taskId, worktree) {
|
|
285
|
+
return workerTasks.detectTaskConflicts(this.tracker.db, this.config.repoPath, taskId, worktree);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Recover stale tasks that have been in_progress too long.
|
|
289
|
+
*
|
|
290
|
+
* @param thresholdMs - Tasks older than this are considered stale
|
|
291
|
+
* @returns Result with released task IDs
|
|
292
|
+
*/
|
|
293
|
+
recoverStaleTasks(thresholdMs = 60 * 60 * 1000) {
|
|
294
|
+
return workerTasks.recoverStaleTasks(this.tracker.db, thresholdMs);
|
|
295
|
+
}
|
|
296
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
297
|
+
// Checkpoint Operations
|
|
298
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
299
|
+
/**
|
|
300
|
+
* Create checkpoints for commits made during a task.
|
|
301
|
+
*
|
|
302
|
+
* Creates a checkpoint for each commit between the task's startCommit and
|
|
303
|
+
* the current HEAD of the task's stream. This captures the work done during
|
|
304
|
+
* the task for future review and merge workflows.
|
|
305
|
+
*
|
|
306
|
+
* @param taskId - Task ID to create checkpoints for
|
|
307
|
+
* @param agentId - Agent ID (used as createdBy)
|
|
308
|
+
* @returns Array of created checkpoints
|
|
309
|
+
*/
|
|
310
|
+
createCheckpointsForTask(taskId, agentId) {
|
|
311
|
+
const task = this.getTask(taskId);
|
|
312
|
+
if (!task) {
|
|
313
|
+
console.warn(`[DataplaneAdapter] Task not found: ${taskId}`);
|
|
314
|
+
return [];
|
|
315
|
+
}
|
|
316
|
+
if (!task.streamId) {
|
|
317
|
+
console.warn(`[DataplaneAdapter] Task ${taskId} has no streamId`);
|
|
318
|
+
return [];
|
|
319
|
+
}
|
|
320
|
+
if (!task.startCommit) {
|
|
321
|
+
console.warn(`[DataplaneAdapter] Task ${taskId} has no startCommit`);
|
|
322
|
+
return [];
|
|
323
|
+
}
|
|
324
|
+
try {
|
|
325
|
+
// Create checkpoints from task's startCommit to stream's current HEAD
|
|
326
|
+
const checkpoints = diffStacks.createCheckpointsFromStream(this.tracker.db, this.config.repoPath, task.streamId, {
|
|
327
|
+
from: task.startCommit,
|
|
328
|
+
createdBy: agentId,
|
|
329
|
+
});
|
|
330
|
+
return checkpoints;
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
console.error(`[DataplaneAdapter] Failed to create checkpoints for task ${taskId}:`, error);
|
|
334
|
+
return [];
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
338
|
+
// Commit Operations
|
|
339
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
340
|
+
/**
|
|
341
|
+
* Commit changes in a worktree with Change tracking.
|
|
342
|
+
*
|
|
343
|
+
* @param options - Commit options
|
|
344
|
+
* @returns Commit hash and change ID
|
|
345
|
+
*/
|
|
346
|
+
commitChanges(options) {
|
|
347
|
+
return this.tracker.commitChanges(options);
|
|
348
|
+
}
|
|
349
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
350
|
+
// Maintenance Operations
|
|
351
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
352
|
+
/**
|
|
353
|
+
* Clean up old worker branches.
|
|
354
|
+
*
|
|
355
|
+
* Deletes branches for:
|
|
356
|
+
* - Completed tasks older than threshold (default 24h)
|
|
357
|
+
* - Abandoned tasks
|
|
358
|
+
* - Orphaned branches (no task record)
|
|
359
|
+
*
|
|
360
|
+
* @param options - Cleanup options
|
|
361
|
+
* @returns Deleted branches and any errors
|
|
362
|
+
*/
|
|
363
|
+
cleanupWorkerBranches(options) {
|
|
364
|
+
return workerTasks.cleanupWorkerBranches(this.tracker.db, this.config.repoPath, options);
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Delete a specific worker branch.
|
|
368
|
+
*
|
|
369
|
+
* @param branchName - Branch name to delete
|
|
370
|
+
* @returns true if deleted, false if branch didn't exist
|
|
371
|
+
*/
|
|
372
|
+
deleteWorkerBranch(branchName) {
|
|
373
|
+
try {
|
|
374
|
+
const { execSync } = require('child_process');
|
|
375
|
+
execSync(`git branch -D "${branchName}"`, {
|
|
376
|
+
cwd: this.config.repoPath,
|
|
377
|
+
stdio: 'pipe',
|
|
378
|
+
});
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
catch {
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
386
|
+
// Health & Recovery
|
|
387
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
388
|
+
/**
|
|
389
|
+
* Check system health.
|
|
390
|
+
*/
|
|
391
|
+
healthCheck() {
|
|
392
|
+
return this.tracker.healthCheck();
|
|
393
|
+
}
|
|
394
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
395
|
+
// Lifecycle
|
|
396
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
397
|
+
/**
|
|
398
|
+
* Close the adapter and release resources.
|
|
399
|
+
*
|
|
400
|
+
* Only closes the database if we created it (not if using shared DB).
|
|
401
|
+
*/
|
|
402
|
+
close() {
|
|
403
|
+
this.eventListeners.clear();
|
|
404
|
+
this.tracker.close();
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Create a DataplaneAdapter instance.
|
|
409
|
+
*
|
|
410
|
+
* @param config - Configuration options
|
|
411
|
+
* @returns DataplaneAdapter instance
|
|
412
|
+
*/
|
|
413
|
+
export function createDataplaneAdapter(config) {
|
|
414
|
+
return new DataplaneAdapter(config);
|
|
415
|
+
}
|
|
416
|
+
//# sourceMappingURL=dataplane-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataplane-adapter.js","sourceRoot":"","sources":["../../src/workspace/dataplane-adapter.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "macro-agent",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Interact with multiple agents as if they were a single agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"version:patch": "npm version patch && git push && git push --tags",
|
|
22
22
|
"version:minor": "npm version minor && git push && git push --tags",
|
|
23
23
|
"version:major": "npm version major && git push && git push --tags",
|
|
24
|
+
"prepublishOnly": "npm run build && publint",
|
|
24
25
|
"publish:npm": "npm run build && npm publish"
|
|
25
26
|
},
|
|
26
27
|
"keywords": [
|
|
@@ -43,6 +44,7 @@
|
|
|
43
44
|
"@types/node": "^20.10.0",
|
|
44
45
|
"@types/supertest": "^6.0.3",
|
|
45
46
|
"@types/ws": "^8.18.1",
|
|
47
|
+
"publint": "^0.3.21",
|
|
46
48
|
"supertest": "^7.2.2",
|
|
47
49
|
"typescript": "^5.3.0",
|
|
48
50
|
"vitest": "^1.0.0"
|
|
@@ -58,11 +60,11 @@
|
|
|
58
60
|
"chalk": "^5.6.2",
|
|
59
61
|
"commander": "^14.0.2",
|
|
60
62
|
"express": "^5.2.1",
|
|
61
|
-
"git-cascade": "^0.0.
|
|
63
|
+
"git-cascade": "^0.0.9",
|
|
62
64
|
"js-yaml": "^4.1.1",
|
|
63
65
|
"nanoid": "^5.0.0",
|
|
64
|
-
"opentasks": "^0.
|
|
65
|
-
"openteams": "^0.3.
|
|
66
|
+
"opentasks": "^0.1.3",
|
|
67
|
+
"openteams": "^0.3.1",
|
|
66
68
|
"swarm-dispatch": "^0.3.4",
|
|
67
69
|
"unique-names-generator": "^4.7.1",
|
|
68
70
|
"ws": "^8.20.0",
|
package/renovate.json5
ADDED
package/src/boot-v2.ts
CHANGED
|
@@ -317,6 +317,28 @@ export interface BootV2Config {
|
|
|
317
317
|
* restart restores the full macro-agent team, not just head managers.
|
|
318
318
|
*/
|
|
319
319
|
rehydrate?: "none" | "coordinators" | "all";
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Wire-delivered openteams binding from the host (e.g. OpenHive's
|
|
323
|
+
* spawn manager packing this into `OPENSWARM_BOOTSTRAP_TOKEN`).
|
|
324
|
+
* When `team_content` is present, bootV2 spawns the bootstrap
|
|
325
|
+
* agents via TeamRuntimeV2 from the inline manifest — no
|
|
326
|
+
* filesystem write, no MAP fetch. Path B of the OpenHive ↔
|
|
327
|
+
* macro-agent openteams integration.
|
|
328
|
+
*/
|
|
329
|
+
openteams?: {
|
|
330
|
+
loadout_bundle_id?: string;
|
|
331
|
+
team_bundle_id?: string;
|
|
332
|
+
role?: string;
|
|
333
|
+
mcp_servers?: unknown[];
|
|
334
|
+
prompt_addendum?: string;
|
|
335
|
+
team_content?: {
|
|
336
|
+
manifest: import("openteams").TeamManifest;
|
|
337
|
+
roles?: Record<string, import("./roles/types.js").RoleDefinition>;
|
|
338
|
+
loadouts?: Record<string, unknown>;
|
|
339
|
+
prompts?: Record<string, unknown>;
|
|
340
|
+
};
|
|
341
|
+
};
|
|
320
342
|
};
|
|
321
343
|
}
|
|
322
344
|
|
|
@@ -405,6 +427,38 @@ export async function bootV2(
|
|
|
405
427
|
// MACRO_BOOTSTRAP_CWD / MACRO_BOOTSTRAP_REHYDRATE into the structured
|
|
406
428
|
// bootstrap field if not already set programmatically. Programmatic
|
|
407
429
|
// config wins per field.
|
|
430
|
+
// OpenHive openteams binding bridge. The host (openswarm) only forwards
|
|
431
|
+
// a fixed-shape `bootConfig` to `bootV2` — it doesn't propagate the
|
|
432
|
+
// raw OPENSWARM_BOOTSTRAP_TOKEN.openteams block. Pull it out of the env
|
|
433
|
+
// ourselves so wire-delivered teams (Path B) work without requiring
|
|
434
|
+
// openswarm to learn a new field.
|
|
435
|
+
if (
|
|
436
|
+
process.env.OPENSWARM_BOOTSTRAP_TOKEN &&
|
|
437
|
+
!config.bootstrap?.openteams
|
|
438
|
+
) {
|
|
439
|
+
try {
|
|
440
|
+
const raw = Buffer.from(
|
|
441
|
+
process.env.OPENSWARM_BOOTSTRAP_TOKEN,
|
|
442
|
+
"base64",
|
|
443
|
+
).toString("utf-8");
|
|
444
|
+
const token = JSON.parse(raw) as { openteams?: unknown };
|
|
445
|
+
if (token.openteams && typeof token.openteams === "object") {
|
|
446
|
+
config = {
|
|
447
|
+
...config,
|
|
448
|
+
bootstrap: {
|
|
449
|
+
...(config.bootstrap ?? {}),
|
|
450
|
+
openteams: token.openteams as NonNullable<
|
|
451
|
+
BootV2Config["bootstrap"]
|
|
452
|
+
>["openteams"],
|
|
453
|
+
},
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
} catch {
|
|
457
|
+
// Token malformed or missing fields — non-fatal; the bootstrap
|
|
458
|
+
// coordinator just runs without the openteams binding.
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
408
462
|
if (
|
|
409
463
|
process.env.MACRO_BOOTSTRAP_COORDINATOR === "true" &&
|
|
410
464
|
!config.bootstrap?.coordinator
|
|
@@ -1013,7 +1067,49 @@ export async function bootV2(
|
|
|
1013
1067
|
// server restart — the prior conversations still exist on disk but get
|
|
1014
1068
|
// buried under stale, state='stopped' records that the UI treats as
|
|
1015
1069
|
// dead.
|
|
1016
|
-
|
|
1070
|
+
// OpenHive wire-delivered team (Path B). When the bootstrap token
|
|
1071
|
+
// carries `openteams.team_content`, instantiate a TeamManagerV2 and
|
|
1072
|
+
// start the team in-memory — no filesystem write, no MAP fetch. The
|
|
1073
|
+
// team's own `topology.root` + companions become the bootstrap
|
|
1074
|
+
// agents, so we short-circuit the generic `bootstrap.coordinator`
|
|
1075
|
+
// path below (those are mutually exclusive: a team-aware swarm has
|
|
1076
|
+
// its head decided by the team manifest, not by a generic
|
|
1077
|
+
// coordinator spawn).
|
|
1078
|
+
const openteamsContent = config.bootstrap?.openteams?.team_content as
|
|
1079
|
+
| {
|
|
1080
|
+
manifest: import("openteams").TeamManifest;
|
|
1081
|
+
roles?: Record<string, import("./roles/types.js").RoleDefinition>;
|
|
1082
|
+
loadouts?: Record<string, unknown>;
|
|
1083
|
+
prompts?: Record<string, unknown>;
|
|
1084
|
+
}
|
|
1085
|
+
| undefined;
|
|
1086
|
+
if (openteamsContent && config.bootstrap?.coordinator) {
|
|
1087
|
+
try {
|
|
1088
|
+
const { TeamManagerV2 } = await import("./teams/team-manager-v2.js");
|
|
1089
|
+
const teamManager = new TeamManagerV2({
|
|
1090
|
+
agentManager,
|
|
1091
|
+
inboxAdapter,
|
|
1092
|
+
tasksAdapter,
|
|
1093
|
+
workspaceManager: config.workspaceManager,
|
|
1094
|
+
});
|
|
1095
|
+
teamManager.install();
|
|
1096
|
+
const teamName =
|
|
1097
|
+
(openteamsContent.manifest?.name as string | undefined) ?? "openhive-team";
|
|
1098
|
+
const instanceId = await teamManager.startTeamFromContent(
|
|
1099
|
+
teamName,
|
|
1100
|
+
openteamsContent,
|
|
1101
|
+
);
|
|
1102
|
+
console.log(
|
|
1103
|
+
`[boot-v2] Wire-delivered team started: ${teamName} (instance ${instanceId})`,
|
|
1104
|
+
);
|
|
1105
|
+
} catch (err) {
|
|
1106
|
+
console.warn(
|
|
1107
|
+
`[boot-v2] Wire-delivered team start failed; falling back to generic coordinator: ${
|
|
1108
|
+
(err as Error).message
|
|
1109
|
+
}`,
|
|
1110
|
+
);
|
|
1111
|
+
}
|
|
1112
|
+
} else if (config.bootstrap?.coordinator) {
|
|
1017
1113
|
const opts = config.bootstrap.coordinator === true
|
|
1018
1114
|
? {}
|
|
1019
1115
|
: config.bootstrap.coordinator;
|