@girardmedia/bootspring 2.0.21 → 2.0.22

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 (147) hide show
  1. package/cli/preseed/index.js +16 -0
  2. package/cli/preseed/interactive.js +143 -0
  3. package/cli/preseed/templates.js +227 -0
  4. package/cli/seed/builders/ai-context-builder.js +85 -0
  5. package/cli/seed/builders/index.js +13 -0
  6. package/cli/seed/builders/seed-builder.js +272 -0
  7. package/cli/seed/extractors/content-extractors.js +383 -0
  8. package/cli/seed/extractors/index.js +47 -0
  9. package/cli/seed/extractors/metadata-extractors.js +167 -0
  10. package/cli/seed/extractors/section-extractor.js +54 -0
  11. package/cli/seed/extractors/stack-extractors.js +228 -0
  12. package/cli/seed/index.js +18 -0
  13. package/cli/seed/utils/folder-structure.js +84 -0
  14. package/cli/seed/utils/index.js +11 -0
  15. package/dist/cli/index.d.ts +3 -0
  16. package/dist/cli/index.js +3220 -0
  17. package/dist/cli/index.js.map +1 -0
  18. package/dist/context-McpJQa_2.d.ts +5710 -0
  19. package/dist/core/index.d.ts +635 -0
  20. package/dist/core/index.js +2593 -0
  21. package/dist/core/index.js.map +1 -0
  22. package/dist/index-QqbeEiDm.d.ts +857 -0
  23. package/dist/index-UiYCgwiH.d.ts +174 -0
  24. package/dist/index.d.ts +453 -0
  25. package/dist/index.js +44228 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/mcp/index.d.ts +1 -0
  28. package/dist/mcp/index.js +41173 -0
  29. package/dist/mcp/index.js.map +1 -0
  30. package/generators/index.ts +82 -0
  31. package/intelligence/orchestrator/config/failure-signatures.js +48 -0
  32. package/intelligence/orchestrator/config/index.js +20 -0
  33. package/intelligence/orchestrator/config/phases.js +111 -0
  34. package/intelligence/orchestrator/config/remediation.js +150 -0
  35. package/intelligence/orchestrator/config/workflows.js +168 -0
  36. package/intelligence/orchestrator/core/index.js +16 -0
  37. package/intelligence/orchestrator/core/state-manager.js +88 -0
  38. package/intelligence/orchestrator/core/telemetry.js +24 -0
  39. package/intelligence/orchestrator/index.js +17 -0
  40. package/mcp/contracts/mcp-contract.v1.json +1 -1
  41. package/package.json +16 -3
  42. package/src/cli/agent.ts +703 -0
  43. package/src/cli/analyze.ts +640 -0
  44. package/src/cli/audit.ts +707 -0
  45. package/src/cli/auth.ts +930 -0
  46. package/src/cli/billing.ts +364 -0
  47. package/src/cli/build.ts +1089 -0
  48. package/src/cli/business.ts +508 -0
  49. package/src/cli/checkpoint-utils.ts +236 -0
  50. package/src/cli/checkpoint.ts +757 -0
  51. package/src/cli/cloud-sync.ts +534 -0
  52. package/src/cli/content.ts +273 -0
  53. package/src/cli/context.ts +667 -0
  54. package/src/cli/dashboard.ts +133 -0
  55. package/src/cli/deploy.ts +704 -0
  56. package/src/cli/doctor.ts +480 -0
  57. package/src/cli/fundraise.ts +494 -0
  58. package/src/cli/generate.ts +346 -0
  59. package/src/cli/github-cmd.ts +566 -0
  60. package/src/cli/health.ts +599 -0
  61. package/src/cli/index.ts +113 -0
  62. package/src/cli/init.ts +838 -0
  63. package/src/cli/legal.ts +495 -0
  64. package/src/cli/log.ts +316 -0
  65. package/src/cli/loop.ts +1660 -0
  66. package/src/cli/manager.ts +878 -0
  67. package/src/cli/mcp.ts +275 -0
  68. package/src/cli/memory.ts +346 -0
  69. package/src/cli/metrics.ts +590 -0
  70. package/src/cli/monitor.ts +960 -0
  71. package/src/cli/mvp.ts +662 -0
  72. package/src/cli/onboard.ts +663 -0
  73. package/src/cli/orchestrator.ts +622 -0
  74. package/src/cli/plugin.ts +483 -0
  75. package/src/cli/prd.ts +671 -0
  76. package/src/cli/preseed-start.ts +1633 -0
  77. package/src/cli/preseed.ts +2434 -0
  78. package/src/cli/project.ts +526 -0
  79. package/src/cli/quality.ts +885 -0
  80. package/src/cli/security.ts +1079 -0
  81. package/src/cli/seed.ts +1224 -0
  82. package/src/cli/skill.ts +537 -0
  83. package/src/cli/suggest.ts +1225 -0
  84. package/src/cli/switch.ts +518 -0
  85. package/src/cli/task.ts +780 -0
  86. package/src/cli/telemetry.ts +172 -0
  87. package/src/cli/todo.ts +627 -0
  88. package/src/cli/types.ts +15 -0
  89. package/src/cli/update.ts +334 -0
  90. package/src/cli/visualize.ts +609 -0
  91. package/src/cli/watch.ts +895 -0
  92. package/src/cli/workspace.ts +709 -0
  93. package/src/core/action-recorder.ts +673 -0
  94. package/src/core/analyze-workflow.ts +1453 -0
  95. package/src/core/api-client.ts +1120 -0
  96. package/src/core/audit-workflow.ts +1681 -0
  97. package/src/core/auth.ts +471 -0
  98. package/src/core/build-orchestrator.ts +509 -0
  99. package/src/core/build-state.ts +621 -0
  100. package/src/core/checkpoint-engine.ts +482 -0
  101. package/src/core/config.ts +1285 -0
  102. package/src/core/context-loader.ts +694 -0
  103. package/src/core/context.ts +410 -0
  104. package/src/core/deploy-workflow.ts +1085 -0
  105. package/src/core/entitlements.ts +322 -0
  106. package/src/core/github-sync.ts +720 -0
  107. package/src/core/index.ts +981 -0
  108. package/src/core/ingest.ts +1186 -0
  109. package/src/core/metrics-engine.ts +886 -0
  110. package/src/core/mvp.ts +847 -0
  111. package/src/core/onboard-workflow.ts +1293 -0
  112. package/src/core/policies.ts +81 -0
  113. package/src/core/preseed-workflow.ts +1163 -0
  114. package/src/core/preseed.ts +1826 -0
  115. package/src/core/project-context.ts +380 -0
  116. package/src/core/project-state.ts +699 -0
  117. package/src/core/r2-sync.ts +691 -0
  118. package/src/core/scaffold.ts +1715 -0
  119. package/src/core/session.ts +286 -0
  120. package/src/core/task-extractor.ts +799 -0
  121. package/src/core/telemetry.ts +371 -0
  122. package/src/core/tier-enforcement.ts +737 -0
  123. package/src/core/utils.ts +437 -0
  124. package/src/index.ts +29 -0
  125. package/src/intelligence/agent-collab.ts +2376 -0
  126. package/src/intelligence/auto-suggest.ts +713 -0
  127. package/src/intelligence/content-gen.ts +1351 -0
  128. package/src/intelligence/cross-project.ts +1692 -0
  129. package/src/intelligence/git-memory.ts +529 -0
  130. package/src/intelligence/index.ts +318 -0
  131. package/src/intelligence/orchestrator.ts +534 -0
  132. package/src/intelligence/prd.ts +466 -0
  133. package/src/intelligence/recommendations.ts +982 -0
  134. package/src/intelligence/workflow-composer.ts +1472 -0
  135. package/src/mcp/capabilities.ts +233 -0
  136. package/src/mcp/index.ts +37 -0
  137. package/src/mcp/registry.ts +1268 -0
  138. package/src/mcp/response-formatter.ts +797 -0
  139. package/src/mcp/server.ts +240 -0
  140. package/src/types/agent.ts +69 -0
  141. package/src/types/config.ts +86 -0
  142. package/src/types/context.ts +77 -0
  143. package/src/types/index.ts +53 -0
  144. package/src/types/mcp.ts +91 -0
  145. package/src/types/skills.ts +47 -0
  146. package/src/types/workflow.ts +155 -0
  147. package/generators/index.js +0 -18
@@ -0,0 +1,2376 @@
1
+ /**
2
+ * Bootspring Agent Collaboration Framework
3
+ *
4
+ * Enables multi-agent task delegation with handoff protocol.
5
+ * Allows agents to collaborate on complex tasks by delegating
6
+ * subtasks to specialized agents.
7
+ *
8
+ * @package bootspring
9
+ * @module intelligence/agent-collab
10
+ */
11
+
12
+ import * as fs from 'fs';
13
+ import * as path from 'path';
14
+
15
+ // Telemetry module (JavaScript)
16
+ const telemetry = require('../../core/telemetry') as {
17
+ emitEvent: (event: string, payload?: Record<string, unknown>, options?: Record<string, unknown>) => void;
18
+ };
19
+
20
+ // ============================================================================
21
+ // Types
22
+ // ============================================================================
23
+
24
+ export type TaskPriorityValue = 'critical' | 'high' | 'medium' | 'low';
25
+ export type TaskStatusValue = 'pending' | 'in_progress' | 'blocked' | 'complete' | 'failed';
26
+ export type HandoffStatusValue = 'pending' | 'accepted' | 'rejected' | 'completed';
27
+ export type AgentRoleValue = 'primary' | 'supporting' | 'specialist';
28
+
29
+ export interface AgentParticipant {
30
+ role: AgentRoleValue;
31
+ joinedAt: string;
32
+ tasksAssigned: number;
33
+ tasksCompleted: number;
34
+ status: 'active' | 'standby' | 'working';
35
+ }
36
+
37
+ export interface ProgressNote {
38
+ note: string;
39
+ timestamp: string;
40
+ }
41
+
42
+ export interface Artifact {
43
+ id?: string | undefined;
44
+ name: string;
45
+ type: string;
46
+ content?: unknown | undefined;
47
+ description?: string | undefined;
48
+ fromAgent?: string | undefined;
49
+ subtaskId?: string | undefined;
50
+ addedAt?: string | undefined;
51
+ sharedAt?: string | undefined;
52
+ }
53
+
54
+ export interface SubtaskResult {
55
+ output?: unknown | undefined;
56
+ success: boolean;
57
+ duration?: number | null | undefined;
58
+ }
59
+
60
+ export interface SubtaskFailure {
61
+ reason: string;
62
+ canRetry: boolean;
63
+ suggestedAction?: string | null | undefined;
64
+ failedAt: string;
65
+ }
66
+
67
+ export interface TransferHistoryEntry {
68
+ from: string;
69
+ to: string;
70
+ handoffId: string;
71
+ timestamp: string;
72
+ }
73
+
74
+ export interface Subtask {
75
+ id: string;
76
+ title: string;
77
+ description?: string | undefined;
78
+ delegatedTo: string;
79
+ delegatedBy: string;
80
+ context: Record<string, unknown>;
81
+ priority: TaskPriorityValue;
82
+ dependencies: string[];
83
+ unmetDependencies: string[];
84
+ expectedDuration?: number | null | undefined;
85
+ acceptanceCriteria: string[];
86
+ artifacts: Artifact[];
87
+ status: TaskStatusValue;
88
+ createdAt: string;
89
+ startedAt?: string | null | undefined;
90
+ completedAt?: string | null | undefined;
91
+ result?: SubtaskResult | null | undefined;
92
+ failure?: SubtaskFailure | undefined;
93
+ feedback?: unknown | undefined;
94
+ retryCount: number;
95
+ progress?: number | undefined;
96
+ progressNotes?: ProgressNote[] | undefined;
97
+ blockedReason?: string | undefined;
98
+ reassignedFrom?: string | undefined;
99
+ transferHistory?: TransferHistoryEntry[] | undefined;
100
+ }
101
+
102
+ export interface SessionContext {
103
+ initial: Record<string, unknown>;
104
+ current: Record<string, unknown>;
105
+ history: Array<{
106
+ update: Record<string, unknown>;
107
+ source: string;
108
+ timestamp: string;
109
+ }>;
110
+ }
111
+
112
+ export interface SessionMetrics {
113
+ startTime: number;
114
+ handoffCount: number;
115
+ delegationCount: number;
116
+ blockedTime: number;
117
+ }
118
+
119
+ export interface Handoff {
120
+ id: string;
121
+ fromAgent: string;
122
+ toAgent: string;
123
+ reason: string;
124
+ context: Record<string, unknown>;
125
+ artifacts: Artifact[];
126
+ transferPrimaryRole: boolean;
127
+ subtaskId?: string | null | undefined;
128
+ urgency: 'low' | 'normal' | 'high' | 'critical';
129
+ expectedDuration?: number | null | undefined;
130
+ status: HandoffStatusValue;
131
+ requestedAt: string;
132
+ acceptedAt?: string | null | undefined;
133
+ completedAt?: string | null | undefined;
134
+ rejectionReason?: string | null | undefined;
135
+ rejectedAt?: string | undefined;
136
+ acknowledgment?: string | null | undefined;
137
+ estimatedCompletion?: string | null | undefined;
138
+ result?: Record<string, unknown> | undefined;
139
+ }
140
+
141
+ export interface Checkpoint {
142
+ id: string;
143
+ description: string;
144
+ createdAt: string;
145
+ snapshot: {
146
+ subtasks: Subtask[];
147
+ context: SessionContext;
148
+ results: Record<string, unknown>;
149
+ artifacts: Artifact[];
150
+ primaryAgent: string;
151
+ };
152
+ }
153
+
154
+ export interface Learning {
155
+ content: unknown;
156
+ subtaskId: string;
157
+ agent: string;
158
+ timestamp: string;
159
+ }
160
+
161
+ export interface Broadcast {
162
+ id: string;
163
+ type: string;
164
+ content: unknown;
165
+ fromAgent: string;
166
+ priority: string;
167
+ timestamp: string;
168
+ recipients: string[];
169
+ }
170
+
171
+ export interface SessionSummary {
172
+ task: string;
173
+ primaryAgent: string;
174
+ subtasksCompleted: number;
175
+ subtasksFailed: number;
176
+ totalSubtasks: number;
177
+ handoffsCount: number;
178
+ acceptedHandoffs: number;
179
+ rejectedHandoffs: number;
180
+ duration: number;
181
+ durationFormatted: string;
182
+ artifactsCount: number;
183
+ agentParticipation: Array<{
184
+ agent: string;
185
+ role: AgentRoleValue;
186
+ tasksAssigned: number;
187
+ tasksCompleted: number;
188
+ }>;
189
+ learnings: Learning[];
190
+ checkpointsCount: number;
191
+ successRate: number;
192
+ }
193
+
194
+ export interface CollabSession {
195
+ id: string;
196
+ task: string;
197
+ primaryAgent: string;
198
+ supportingAgents: string[];
199
+ agentParticipants: Record<string, AgentParticipant>;
200
+ status: 'active' | 'complete' | 'aborted';
201
+ createdAt: string;
202
+ parentSessionId?: string | null | undefined;
203
+ subtasks: Subtask[];
204
+ taskQueue: Subtask[];
205
+ handoffs: Handoff[];
206
+ results: Record<string, unknown>;
207
+ artifacts: Artifact[];
208
+ context: SessionContext;
209
+ checkpoints: Checkpoint[];
210
+ metrics: SessionMetrics;
211
+ broadcasts?: Broadcast[] | undefined;
212
+ learnings?: Learning[] | undefined;
213
+ completedAt?: string | undefined;
214
+ abortedAt?: string | undefined;
215
+ abortReason?: string | undefined;
216
+ summary?: SessionSummary | undefined;
217
+ }
218
+
219
+ export interface TaskQueueItem {
220
+ sessionId: string;
221
+ subtaskId: string;
222
+ priority: TaskPriorityValue;
223
+ createdAt: string;
224
+ }
225
+
226
+ export interface BlockedTask {
227
+ sessionId: string;
228
+ subtaskId: string;
229
+ dependencies?: string[] | undefined;
230
+ reason?: string | undefined;
231
+ blockedAt?: string | undefined;
232
+ }
233
+
234
+ export interface CollabMetrics {
235
+ totalSessions: number;
236
+ successfulSessions: number;
237
+ totalHandoffs: number;
238
+ totalSubtasks: number;
239
+ avgSessionDuration: number;
240
+ }
241
+
242
+ export interface HistoryEntry {
243
+ action: string;
244
+ sessionId?: string | undefined;
245
+ subtaskId?: string | undefined;
246
+ handoffId?: string | undefined;
247
+ checkpointId?: string | undefined;
248
+ broadcastId?: string | undefined;
249
+ artifactId?: string | undefined;
250
+ agent?: string | undefined;
251
+ fromAgent?: string | undefined;
252
+ toAgent?: string | undefined;
253
+ from?: string | undefined;
254
+ to?: string | undefined;
255
+ byAgent?: string | undefined;
256
+ primaryAgent?: string | undefined;
257
+ supportingAgents?: string[] | undefined;
258
+ parentSessionId?: string | null | undefined;
259
+ transferPrimaryRole?: boolean | undefined;
260
+ previousPrimary?: string | null | undefined;
261
+ priority?: TaskPriorityValue | undefined;
262
+ hasDepedencies?: boolean | undefined;
263
+ success?: boolean | undefined;
264
+ reason?: string | undefined;
265
+ canRetry?: boolean | undefined;
266
+ retryCount?: number | undefined;
267
+ reassignedTo?: string | null | undefined;
268
+ urgency?: string | undefined;
269
+ description?: string | undefined;
270
+ name?: string | undefined;
271
+ type?: string | undefined;
272
+ recipientCount?: number | undefined;
273
+ unblockedTasks?: string[] | undefined;
274
+ duration?: number | undefined;
275
+ successRate?: number | undefined;
276
+ timestamp: string;
277
+ }
278
+
279
+ export interface PendingHandoffItem extends Handoff {
280
+ sessionId: string;
281
+ handoffId: string;
282
+ }
283
+
284
+ export interface CollabState {
285
+ activeSessions: Record<string, CollabSession>;
286
+ pendingHandoffs: PendingHandoffItem[];
287
+ completedSessions: CollabSession[];
288
+ taskQueues: Record<string, TaskQueueItem[]>;
289
+ agentStates: Record<string, unknown>;
290
+ blockedTasks: BlockedTask[];
291
+ history: HistoryEntry[];
292
+ metrics: CollabMetrics;
293
+ lastUpdated?: string | undefined;
294
+ }
295
+
296
+ export interface SubtaskTemplate {
297
+ title: string;
298
+ delegatedTo: string;
299
+ priority: TaskPriorityValue;
300
+ dependencies?: string[] | undefined;
301
+ }
302
+
303
+ export interface CollabPattern {
304
+ name: string;
305
+ description: string;
306
+ primaryAgent: string;
307
+ supportingAgents: string[];
308
+ subtaskTemplate: SubtaskTemplate[];
309
+ }
310
+
311
+ export interface OperationResult {
312
+ success: boolean;
313
+ error?: string | undefined;
314
+ message?: string | undefined;
315
+ }
316
+
317
+ export interface StartSessionOptions {
318
+ task: string;
319
+ primaryAgent: string;
320
+ supportingAgents?: string[] | undefined;
321
+ context?: Record<string, unknown> | undefined;
322
+ parentSessionId?: string | null | undefined;
323
+ }
324
+
325
+ export interface StartSessionResult extends OperationResult {
326
+ sessionId?: string | undefined;
327
+ session?: CollabSession | undefined;
328
+ }
329
+
330
+ export interface DelegateTaskInput {
331
+ title: string;
332
+ description?: string | undefined;
333
+ delegatedTo: string;
334
+ delegatedBy?: string | undefined;
335
+ context?: Record<string, unknown> | undefined;
336
+ priority?: TaskPriorityValue | undefined;
337
+ dependencies?: string[] | undefined;
338
+ expectedDuration?: number | null | undefined;
339
+ acceptanceCriteria?: string[] | undefined;
340
+ artifacts?: Artifact[] | undefined;
341
+ }
342
+
343
+ export interface DelegateTaskResult extends OperationResult {
344
+ sessionId?: string | undefined;
345
+ subtaskId?: string | undefined;
346
+ subtask?: Subtask | undefined;
347
+ isBlocked?: boolean | undefined;
348
+ }
349
+
350
+ export interface SubtaskOperationResult extends OperationResult {
351
+ sessionId?: string | undefined;
352
+ subtaskId?: string | undefined;
353
+ subtask?: Subtask | undefined;
354
+ }
355
+
356
+ export interface CompleteSubtaskInput {
357
+ output?: unknown | undefined;
358
+ artifacts?: Artifact[] | undefined;
359
+ contextUpdates?: Record<string, unknown> | undefined;
360
+ learnings?: unknown[] | undefined;
361
+ success?: boolean | undefined;
362
+ }
363
+
364
+ export interface CompleteSubtaskResult extends OperationResult {
365
+ sessionId?: string | undefined;
366
+ subtaskId?: string | undefined;
367
+ taskSuccess?: boolean | undefined;
368
+ allSubtasksComplete?: boolean | undefined;
369
+ allSubtasksSuccessful?: boolean | undefined;
370
+ unblockedTasks?: string[] | undefined;
371
+ }
372
+
373
+ export interface FailSubtaskInput {
374
+ reason: string;
375
+ canRetry?: boolean | undefined;
376
+ suggestedAction?: string | null | undefined;
377
+ }
378
+
379
+ export interface FailSubtaskResult extends OperationResult {
380
+ sessionId?: string | undefined;
381
+ subtaskId?: string | undefined;
382
+ canRetry?: boolean | undefined;
383
+ suggestedAction?: string | null | undefined;
384
+ }
385
+
386
+ export interface RetryOptions {
387
+ reassignTo?: string | undefined;
388
+ additionalContext?: Record<string, unknown> | undefined;
389
+ }
390
+
391
+ export interface RetryResult extends OperationResult {
392
+ sessionId?: string | undefined;
393
+ subtaskId?: string | undefined;
394
+ retryCount?: number | undefined;
395
+ }
396
+
397
+ export interface RequestHandoffInput {
398
+ fromAgent: string;
399
+ toAgent: string;
400
+ reason: string;
401
+ context?: Record<string, unknown> | undefined;
402
+ artifacts?: Artifact[] | undefined;
403
+ transferPrimaryRole?: boolean | undefined;
404
+ subtaskId?: string | null | undefined;
405
+ urgency?: 'low' | 'normal' | 'high' | 'critical' | undefined;
406
+ expectedDuration?: number | null | undefined;
407
+ }
408
+
409
+ export interface HandoffResult extends OperationResult {
410
+ sessionId?: string | undefined;
411
+ handoffId?: string | undefined;
412
+ handoff?: Handoff | undefined;
413
+ newPrimaryAgent?: string | undefined;
414
+ transferredSubtask?: string | null | undefined;
415
+ context?: Record<string, unknown> | undefined;
416
+ rejectionReason?: string | undefined;
417
+ }
418
+
419
+ export interface AcceptHandoffOptions {
420
+ acknowledgment?: string | null | undefined;
421
+ estimatedCompletion?: string | null | undefined;
422
+ }
423
+
424
+ export interface CheckpointResult extends OperationResult {
425
+ sessionId?: string | undefined;
426
+ checkpointId?: string | undefined;
427
+ }
428
+
429
+ export interface SessionStatusResult {
430
+ found: boolean;
431
+ session?: Partial<CollabSession> | undefined;
432
+ isActive?: boolean | undefined;
433
+ summary?: SessionSummary | undefined;
434
+ progress?: {
435
+ subtasks: {
436
+ pending: number;
437
+ inProgress: number;
438
+ blocked: number;
439
+ completed: number;
440
+ failed: number;
441
+ total: number;
442
+ percent: number;
443
+ };
444
+ handoffs: {
445
+ pending: number;
446
+ total: number;
447
+ accepted: number;
448
+ };
449
+ artifacts: number;
450
+ checkpoints: number;
451
+ } | undefined;
452
+ agents?: Array<{
453
+ agent: string;
454
+ role: AgentRoleValue;
455
+ status: string;
456
+ tasksAssigned: number;
457
+ tasksCompleted: number;
458
+ }> | undefined;
459
+ timing?: {
460
+ elapsed: number;
461
+ elapsedFormatted: string;
462
+ } | undefined;
463
+ nextActions?: NextAction[] | undefined;
464
+ }
465
+
466
+ export interface NextAction {
467
+ type: string;
468
+ urgency: 'low' | 'medium' | 'high';
469
+ count?: number | undefined;
470
+ message: string;
471
+ }
472
+
473
+ export interface NextTaskResult {
474
+ hasTask: boolean;
475
+ blocked?: boolean | undefined;
476
+ sessionId?: string | undefined;
477
+ subtask?: Subtask | undefined;
478
+ sessionContext?: Record<string, unknown> | undefined;
479
+ message: string;
480
+ }
481
+
482
+ export interface ActiveSessionInfo {
483
+ id: string;
484
+ task: string;
485
+ primaryAgent: string;
486
+ status: string;
487
+ subtaskCount: number;
488
+ createdAt: string;
489
+ }
490
+
491
+ export interface PatternInfo {
492
+ key: string;
493
+ name: string;
494
+ description: string;
495
+ primaryAgent: string;
496
+ supportingAgents: string[];
497
+ subtaskCount: number;
498
+ }
499
+
500
+ export interface StartFromPatternOptions {
501
+ context?: Record<string, unknown> | undefined;
502
+ autoStart?: boolean | undefined;
503
+ }
504
+
505
+ export interface StartFromPatternResult extends OperationResult {
506
+ sessionId?: string | undefined;
507
+ pattern?: string | undefined;
508
+ patternName?: string | undefined;
509
+ primaryAgent?: string | undefined;
510
+ subtasks?: Array<{
511
+ id?: string | undefined;
512
+ title?: string | undefined;
513
+ delegatedTo?: string | undefined;
514
+ status?: TaskStatusValue | undefined;
515
+ }> | undefined;
516
+ availablePatterns?: string[] | undefined;
517
+ }
518
+
519
+ export interface CollabStats {
520
+ activeSessions: number;
521
+ completedSessions: number;
522
+ totalSessions: number;
523
+ successfulSessions: number;
524
+ totalSubtasks: number;
525
+ totalHandoffs: number;
526
+ activeSubtasks: number;
527
+ pendingHandoffs: number;
528
+ avgSessionDuration: number;
529
+ avgSessionDurationFormatted: string;
530
+ blockedTasks: number;
531
+ }
532
+
533
+ export interface BroadcastInput {
534
+ type: string;
535
+ content: unknown;
536
+ fromAgent: string;
537
+ priority?: string | undefined;
538
+ }
539
+
540
+ export interface BroadcastResult extends OperationResult {
541
+ broadcastId?: string | undefined;
542
+ recipients?: string[] | undefined;
543
+ }
544
+
545
+ export interface ShareArtifactInput {
546
+ name: string;
547
+ type: string;
548
+ content?: unknown | undefined;
549
+ fromAgent: string;
550
+ description?: string | undefined;
551
+ }
552
+
553
+ export interface ShareArtifactResult extends OperationResult {
554
+ artifactId?: string | undefined;
555
+ }
556
+
557
+ export interface GetArtifactsResult extends OperationResult {
558
+ sessionId?: string | undefined;
559
+ artifacts?: Artifact[] | undefined;
560
+ count?: number | undefined;
561
+ }
562
+
563
+ export interface AgentScore {
564
+ agentId: string;
565
+ agent: Record<string, unknown>;
566
+ score: number;
567
+ }
568
+
569
+ export interface UpdateProgressInput {
570
+ progress?: number | undefined;
571
+ notes?: string | undefined;
572
+ artifacts?: Artifact[] | undefined;
573
+ blockedReason?: string | undefined;
574
+ }
575
+
576
+ // ============================================================================
577
+ // Constants
578
+ // ============================================================================
579
+
580
+ export const TASK_PRIORITY: Record<string, TaskPriorityValue> = {
581
+ CRITICAL: 'critical',
582
+ HIGH: 'high',
583
+ MEDIUM: 'medium',
584
+ LOW: 'low'
585
+ };
586
+
587
+ export const TASK_STATUS: Record<string, TaskStatusValue> = {
588
+ PENDING: 'pending',
589
+ IN_PROGRESS: 'in_progress',
590
+ BLOCKED: 'blocked',
591
+ COMPLETE: 'complete',
592
+ FAILED: 'failed'
593
+ };
594
+
595
+ export const HANDOFF_STATUS: Record<string, HandoffStatusValue> = {
596
+ PENDING: 'pending',
597
+ ACCEPTED: 'accepted',
598
+ REJECTED: 'rejected',
599
+ COMPLETED: 'completed'
600
+ };
601
+
602
+ export const AGENT_ROLE: Record<string, AgentRoleValue> = {
603
+ PRIMARY: 'primary',
604
+ SUPPORTING: 'supporting',
605
+ SPECIALIST: 'specialist'
606
+ };
607
+
608
+ // ============================================================================
609
+ // Collaboration Patterns
610
+ // ============================================================================
611
+
612
+ export const COLLAB_PATTERNS: Record<string, CollabPattern> = {
613
+ 'code-review': {
614
+ name: 'Code Review',
615
+ description: 'Multi-agent code review with security and performance focus',
616
+ primaryAgent: 'code-review-expert',
617
+ supportingAgents: ['security-expert', 'performance-expert'],
618
+ subtaskTemplate: [
619
+ { title: 'Security Review', delegatedTo: 'security-expert', priority: 'high' },
620
+ { title: 'Performance Review', delegatedTo: 'performance-expert', priority: 'medium' }
621
+ ]
622
+ },
623
+ 'feature-build': {
624
+ name: 'Feature Build',
625
+ description: 'Collaborative feature development with parallel tracks',
626
+ primaryAgent: 'architecture-expert',
627
+ supportingAgents: ['backend-expert', 'frontend-expert', 'database-expert', 'testing-expert'],
628
+ subtaskTemplate: [
629
+ { title: 'Architecture Design', delegatedTo: 'architecture-expert', priority: 'critical' },
630
+ { title: 'Database Schema', delegatedTo: 'database-expert', priority: 'high', dependencies: ['subtask-1'] },
631
+ { title: 'API Implementation', delegatedTo: 'backend-expert', priority: 'high', dependencies: ['subtask-2'] },
632
+ { title: 'UI Implementation', delegatedTo: 'frontend-expert', priority: 'high', dependencies: ['subtask-1'] },
633
+ { title: 'Integration Tests', delegatedTo: 'testing-expert', priority: 'medium', dependencies: ['subtask-3', 'subtask-4'] }
634
+ ]
635
+ },
636
+ 'security-hardening': {
637
+ name: 'Security Hardening',
638
+ description: 'Security-focused collaboration',
639
+ primaryAgent: 'security-expert',
640
+ supportingAgents: ['backend-expert', 'devops-expert'],
641
+ subtaskTemplate: [
642
+ { title: 'Vulnerability Assessment', delegatedTo: 'security-expert', priority: 'critical' },
643
+ { title: 'Code Security Fixes', delegatedTo: 'backend-expert', priority: 'high', dependencies: ['subtask-1'] },
644
+ { title: 'Infrastructure Hardening', delegatedTo: 'devops-expert', priority: 'high', dependencies: ['subtask-1'] },
645
+ { title: 'Security Verification', delegatedTo: 'security-expert', priority: 'high', dependencies: ['subtask-2', 'subtask-3'] }
646
+ ]
647
+ },
648
+ 'api-development': {
649
+ name: 'API Development',
650
+ description: 'End-to-end API development with documentation',
651
+ primaryAgent: 'api-expert',
652
+ supportingAgents: ['database-expert', 'security-expert', 'testing-expert'],
653
+ subtaskTemplate: [
654
+ { title: 'API Design', delegatedTo: 'api-expert', priority: 'critical' },
655
+ { title: 'Data Model', delegatedTo: 'database-expert', priority: 'high', dependencies: ['subtask-1'] },
656
+ { title: 'API Implementation', delegatedTo: 'api-expert', priority: 'high', dependencies: ['subtask-2'] },
657
+ { title: 'Security Review', delegatedTo: 'security-expert', priority: 'high', dependencies: ['subtask-3'] },
658
+ { title: 'API Tests', delegatedTo: 'testing-expert', priority: 'medium', dependencies: ['subtask-3'] }
659
+ ]
660
+ },
661
+ 'full-stack-feature': {
662
+ name: 'Full Stack Feature',
663
+ description: 'Complete full-stack feature with frontend, backend, and testing',
664
+ primaryAgent: 'architecture-expert',
665
+ supportingAgents: ['frontend-expert', 'backend-expert', 'database-expert', 'testing-expert', 'ui-ux-expert'],
666
+ subtaskTemplate: [
667
+ { title: 'Feature Specification', delegatedTo: 'architecture-expert', priority: 'critical' },
668
+ { title: 'UI/UX Design', delegatedTo: 'ui-ux-expert', priority: 'high', dependencies: ['subtask-1'] },
669
+ { title: 'Data Model', delegatedTo: 'database-expert', priority: 'high', dependencies: ['subtask-1'] },
670
+ { title: 'Backend API', delegatedTo: 'backend-expert', priority: 'high', dependencies: ['subtask-3'] },
671
+ { title: 'Frontend UI', delegatedTo: 'frontend-expert', priority: 'high', dependencies: ['subtask-2', 'subtask-4'] },
672
+ { title: 'Integration Testing', delegatedTo: 'testing-expert', priority: 'medium', dependencies: ['subtask-4', 'subtask-5'] }
673
+ ]
674
+ },
675
+ 'performance-optimization': {
676
+ name: 'Performance Optimization',
677
+ description: 'Performance analysis and optimization across the stack',
678
+ primaryAgent: 'performance-expert',
679
+ supportingAgents: ['database-expert', 'frontend-expert', 'backend-expert'],
680
+ subtaskTemplate: [
681
+ { title: 'Performance Audit', delegatedTo: 'performance-expert', priority: 'critical' },
682
+ { title: 'Database Optimization', delegatedTo: 'database-expert', priority: 'high', dependencies: ['subtask-1'] },
683
+ { title: 'Backend Optimization', delegatedTo: 'backend-expert', priority: 'high', dependencies: ['subtask-1'] },
684
+ { title: 'Frontend Optimization', delegatedTo: 'frontend-expert', priority: 'high', dependencies: ['subtask-1'] },
685
+ { title: 'Performance Verification', delegatedTo: 'performance-expert', priority: 'medium', dependencies: ['subtask-2', 'subtask-3', 'subtask-4'] }
686
+ ]
687
+ },
688
+ 'launch-prep': {
689
+ name: 'Launch Preparation',
690
+ description: 'Pre-launch checklist with security, performance, and deployment',
691
+ primaryAgent: 'devops-expert',
692
+ supportingAgents: ['security-expert', 'performance-expert', 'testing-expert'],
693
+ subtaskTemplate: [
694
+ { title: 'Security Audit', delegatedTo: 'security-expert', priority: 'critical' },
695
+ { title: 'Performance Testing', delegatedTo: 'performance-expert', priority: 'critical' },
696
+ { title: 'End-to-End Tests', delegatedTo: 'testing-expert', priority: 'high' },
697
+ { title: 'Deployment Config', delegatedTo: 'devops-expert', priority: 'high', dependencies: ['subtask-1', 'subtask-2'] },
698
+ { title: 'Launch Verification', delegatedTo: 'devops-expert', priority: 'high', dependencies: ['subtask-3', 'subtask-4'] }
699
+ ]
700
+ }
701
+ };
702
+
703
+ // ============================================================================
704
+ // Internal Functions
705
+ // ============================================================================
706
+
707
+ function getCollabPath(): string {
708
+ const projectRoot = process.cwd();
709
+ return path.join(projectRoot, '.bootspring', 'collab-state.json');
710
+ }
711
+
712
+ function emitCollabTelemetry(event: string, payload: Record<string, unknown> = {}): void {
713
+ try {
714
+ telemetry.emitEvent(event, payload, { projectRoot: process.cwd() });
715
+ } catch {
716
+ // Telemetry should not block collaboration execution
717
+ }
718
+ }
719
+
720
+ function createDefaultCollabState(): CollabState {
721
+ return {
722
+ activeSessions: {},
723
+ pendingHandoffs: [],
724
+ completedSessions: [],
725
+ taskQueues: {},
726
+ agentStates: {},
727
+ blockedTasks: [],
728
+ history: [],
729
+ metrics: {
730
+ totalSessions: 0,
731
+ successfulSessions: 0,
732
+ totalHandoffs: 0,
733
+ totalSubtasks: 0,
734
+ avgSessionDuration: 0
735
+ }
736
+ };
737
+ }
738
+
739
+ export function loadCollabState(): CollabState {
740
+ const statePath = getCollabPath();
741
+ if (fs.existsSync(statePath)) {
742
+ try {
743
+ return JSON.parse(fs.readFileSync(statePath, 'utf8')) as CollabState;
744
+ } catch {
745
+ return createDefaultCollabState();
746
+ }
747
+ }
748
+ return createDefaultCollabState();
749
+ }
750
+
751
+ function saveCollabState(state: CollabState): void {
752
+ const statePath = getCollabPath();
753
+ const dir = path.dirname(statePath);
754
+ if (!fs.existsSync(dir)) {
755
+ fs.mkdirSync(dir, { recursive: true });
756
+ }
757
+ state.lastUpdated = new Date().toISOString();
758
+ fs.writeFileSync(statePath, JSON.stringify(state, null, 2));
759
+ }
760
+
761
+ function generateSessionId(): string {
762
+ return `collab-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
763
+ }
764
+
765
+ function getAgentRegistry(): Record<string, Record<string, unknown>> {
766
+ try {
767
+ const agentsModule = require('../../agents') as { AGENTS?: Record<string, Record<string, unknown>> };
768
+ return agentsModule.AGENTS ?? {};
769
+ } catch {
770
+ try {
771
+ const agentCli = require('../../cli/agent') as { AGENTS?: Record<string, Record<string, unknown>> };
772
+ return agentCli.AGENTS ?? {};
773
+ } catch {
774
+ return {};
775
+ }
776
+ }
777
+ }
778
+
779
+ function insertIntoTaskQueue(queue: Subtask[], task: Subtask): void {
780
+ const priorityOrder: Record<string, number> = {
781
+ 'critical': 0,
782
+ 'high': 1,
783
+ 'medium': 2,
784
+ 'low': 3
785
+ };
786
+
787
+ const taskPriority = priorityOrder[task.priority] ?? 2;
788
+ let inserted = false;
789
+
790
+ for (let i = 0; i < queue.length; i++) {
791
+ const queueItem = queue[i];
792
+ if (!queueItem) continue;
793
+ const queuePriority = priorityOrder[queueItem.priority] ?? 2;
794
+ if (taskPriority < queuePriority) {
795
+ queue.splice(i, 0, task);
796
+ inserted = true;
797
+ break;
798
+ }
799
+ }
800
+
801
+ if (!inserted) {
802
+ queue.push(task);
803
+ }
804
+ }
805
+
806
+ function formatDuration(ms: number): string {
807
+ const seconds = Math.floor(ms / 1000);
808
+ const minutes = Math.floor(seconds / 60);
809
+ const hours = Math.floor(minutes / 60);
810
+
811
+ if (hours > 0) {
812
+ return `${hours}h ${minutes % 60}m`;
813
+ } else if (minutes > 0) {
814
+ return `${minutes}m ${seconds % 60}s`;
815
+ }
816
+ return `${seconds}s`;
817
+ }
818
+
819
+ function getNextActions(
820
+ session: CollabSession,
821
+ pendingSubtasks: Subtask[],
822
+ pendingHandoffs: Handoff[],
823
+ blockedSubtasks: Subtask[]
824
+ ): NextAction[] {
825
+ const actions: NextAction[] = [];
826
+
827
+ if (pendingHandoffs.length > 0) {
828
+ actions.push({
829
+ type: 'accept_handoffs',
830
+ urgency: 'high',
831
+ count: pendingHandoffs.length,
832
+ message: `${pendingHandoffs.length} handoff(s) awaiting acceptance`
833
+ });
834
+ }
835
+
836
+ if (blockedSubtasks.length > 0) {
837
+ actions.push({
838
+ type: 'resolve_blockers',
839
+ urgency: 'medium',
840
+ count: blockedSubtasks.length,
841
+ message: `${blockedSubtasks.length} task(s) blocked on dependencies`
842
+ });
843
+ }
844
+
845
+ if (pendingSubtasks.length > 0) {
846
+ const highPriority = pendingSubtasks.filter(t =>
847
+ t.priority === 'high' || t.priority === 'critical'
848
+ );
849
+ if (highPriority.length > 0) {
850
+ actions.push({
851
+ type: 'start_high_priority',
852
+ urgency: 'high',
853
+ count: highPriority.length,
854
+ message: `${highPriority.length} high priority task(s) ready to start`
855
+ });
856
+ }
857
+ }
858
+
859
+ const allComplete = session.subtasks.length > 0 &&
860
+ session.subtasks.every(s =>
861
+ s.status === 'complete' || s.status === 'failed'
862
+ );
863
+
864
+ if (allComplete) {
865
+ actions.push({
866
+ type: 'finalize_session',
867
+ urgency: 'low',
868
+ message: 'All tasks processed. Ready to finalize session.'
869
+ });
870
+ }
871
+
872
+ return actions;
873
+ }
874
+
875
+ // ============================================================================
876
+ // Public Functions
877
+ // ============================================================================
878
+
879
+ export function findBestAgentForTask(
880
+ taskDescription: string,
881
+ availableAgents: string[] = []
882
+ ): AgentScore | null {
883
+ const registry = getAgentRegistry();
884
+ const descLower = taskDescription.toLowerCase();
885
+ const scores: AgentScore[] = [];
886
+
887
+ const searchAgents = availableAgents.length > 0
888
+ ? availableAgents
889
+ : Object.keys(registry);
890
+
891
+ for (const agentId of searchAgents) {
892
+ const agent = registry[agentId];
893
+ if (!agent) continue;
894
+
895
+ let score = 0;
896
+ const expertise = (agent.expertise as string[]) ?? [];
897
+
898
+ for (const skill of expertise) {
899
+ if (typeof skill === 'string' && descLower.includes(skill.toLowerCase())) {
900
+ score += 10;
901
+ }
902
+ }
903
+
904
+ const agentDesc = agent.description;
905
+ if (typeof agentDesc === 'string' && descLower.includes(agentDesc.toLowerCase())) {
906
+ score += 5;
907
+ }
908
+
909
+ if (score > 0) {
910
+ scores.push({ agentId, agent, score });
911
+ }
912
+ }
913
+
914
+ scores.sort((a, b) => b.score - a.score);
915
+
916
+ return scores.length > 0 ? scores[0] ?? null : null;
917
+ }
918
+
919
+ export function startSession(options: StartSessionOptions): StartSessionResult {
920
+ const {
921
+ task,
922
+ primaryAgent,
923
+ supportingAgents = [],
924
+ context = {},
925
+ parentSessionId = null
926
+ } = options;
927
+
928
+ if (!task || !primaryAgent) {
929
+ return { success: false, error: 'Task and primaryAgent are required' };
930
+ }
931
+
932
+ const state = loadCollabState();
933
+ const sessionId = generateSessionId();
934
+
935
+ const agentParticipants: Record<string, AgentParticipant> = {
936
+ [primaryAgent]: {
937
+ role: 'primary',
938
+ joinedAt: new Date().toISOString(),
939
+ tasksAssigned: 0,
940
+ tasksCompleted: 0,
941
+ status: 'active'
942
+ }
943
+ };
944
+
945
+ for (const agent of supportingAgents) {
946
+ agentParticipants[agent] = {
947
+ role: 'supporting',
948
+ joinedAt: new Date().toISOString(),
949
+ tasksAssigned: 0,
950
+ tasksCompleted: 0,
951
+ status: 'standby'
952
+ };
953
+ }
954
+
955
+ const session: CollabSession = {
956
+ id: sessionId,
957
+ task,
958
+ primaryAgent,
959
+ supportingAgents,
960
+ agentParticipants,
961
+ status: 'active',
962
+ createdAt: new Date().toISOString(),
963
+ parentSessionId,
964
+ subtasks: [],
965
+ taskQueue: [],
966
+ handoffs: [],
967
+ results: {},
968
+ artifacts: [],
969
+ context: {
970
+ initial: context,
971
+ current: { ...context },
972
+ history: []
973
+ },
974
+ checkpoints: [],
975
+ metrics: {
976
+ startTime: Date.now(),
977
+ handoffCount: 0,
978
+ delegationCount: 0,
979
+ blockedTime: 0
980
+ }
981
+ };
982
+
983
+ state.activeSessions[sessionId] = session;
984
+ state.metrics.totalSessions++;
985
+
986
+ if (!state.taskQueues[primaryAgent]) {
987
+ state.taskQueues[primaryAgent] = [];
988
+ }
989
+
990
+ state.history.push({
991
+ action: 'session_started',
992
+ sessionId,
993
+ primaryAgent,
994
+ supportingAgents,
995
+ parentSessionId,
996
+ timestamp: new Date().toISOString()
997
+ });
998
+
999
+ saveCollabState(state);
1000
+
1001
+ emitCollabTelemetry('collab_session_started', {
1002
+ sessionId,
1003
+ primaryAgent,
1004
+ supportingAgentCount: supportingAgents.length
1005
+ });
1006
+
1007
+ return {
1008
+ success: true,
1009
+ sessionId,
1010
+ session,
1011
+ message: `Collaboration session started with ${primaryAgent} as primary agent`
1012
+ };
1013
+ }
1014
+
1015
+ export function delegateTask(sessionId: string, subtask: DelegateTaskInput): DelegateTaskResult {
1016
+ const state = loadCollabState();
1017
+ const session = state.activeSessions[sessionId];
1018
+
1019
+ if (!session) {
1020
+ return { success: false, error: `Session not found: ${sessionId}` };
1021
+ }
1022
+
1023
+ const {
1024
+ title,
1025
+ description = '',
1026
+ delegatedTo,
1027
+ delegatedBy = session.primaryAgent,
1028
+ context = {},
1029
+ priority = 'medium',
1030
+ dependencies = [],
1031
+ expectedDuration = null,
1032
+ acceptanceCriteria = [],
1033
+ artifacts = []
1034
+ } = subtask;
1035
+
1036
+ if (!title || !delegatedTo) {
1037
+ return { success: false, error: 'Title and delegatedTo are required' };
1038
+ }
1039
+
1040
+ const subtaskId = `subtask-${session.subtasks.length + 1}-${Date.now()}`;
1041
+
1042
+ const unmetDependencies = dependencies.filter(depId => {
1043
+ const dep = session.subtasks.find(t => t.id === depId);
1044
+ return !dep || dep.status !== 'complete';
1045
+ });
1046
+
1047
+ const newSubtask: Subtask = {
1048
+ id: subtaskId,
1049
+ title,
1050
+ description,
1051
+ delegatedTo,
1052
+ delegatedBy,
1053
+ context: {
1054
+ ...session.context.current,
1055
+ ...context
1056
+ },
1057
+ priority,
1058
+ dependencies,
1059
+ unmetDependencies,
1060
+ expectedDuration,
1061
+ acceptanceCriteria,
1062
+ artifacts,
1063
+ status: unmetDependencies.length > 0 ? 'blocked' : 'pending',
1064
+ createdAt: new Date().toISOString(),
1065
+ startedAt: null,
1066
+ completedAt: null,
1067
+ result: null,
1068
+ feedback: null,
1069
+ retryCount: 0
1070
+ };
1071
+
1072
+ session.subtasks.push(newSubtask);
1073
+ session.metrics.delegationCount++;
1074
+
1075
+ const participant = session.agentParticipants[delegatedTo];
1076
+ if (participant) {
1077
+ participant.tasksAssigned++;
1078
+ } else {
1079
+ session.agentParticipants[delegatedTo] = {
1080
+ role: 'specialist',
1081
+ joinedAt: new Date().toISOString(),
1082
+ tasksAssigned: 1,
1083
+ tasksCompleted: 0,
1084
+ status: 'active'
1085
+ };
1086
+ }
1087
+
1088
+ insertIntoTaskQueue(session.taskQueue, newSubtask);
1089
+
1090
+ if (!state.taskQueues[delegatedTo]) {
1091
+ state.taskQueues[delegatedTo] = [];
1092
+ }
1093
+ state.taskQueues[delegatedTo].push({
1094
+ sessionId,
1095
+ subtaskId,
1096
+ priority,
1097
+ createdAt: newSubtask.createdAt
1098
+ });
1099
+
1100
+ if (newSubtask.status === 'blocked') {
1101
+ state.blockedTasks.push({
1102
+ sessionId,
1103
+ subtaskId,
1104
+ dependencies: unmetDependencies
1105
+ });
1106
+ }
1107
+
1108
+ state.metrics.totalSubtasks++;
1109
+
1110
+ state.history.push({
1111
+ action: 'task_delegated',
1112
+ sessionId,
1113
+ subtaskId,
1114
+ from: delegatedBy,
1115
+ to: delegatedTo,
1116
+ priority,
1117
+ hasDepedencies: dependencies.length > 0,
1118
+ timestamp: new Date().toISOString()
1119
+ });
1120
+
1121
+ saveCollabState(state);
1122
+
1123
+ emitCollabTelemetry('task_delegated', {
1124
+ sessionId,
1125
+ subtaskId,
1126
+ delegatedTo,
1127
+ priority
1128
+ });
1129
+
1130
+ return {
1131
+ success: true,
1132
+ sessionId,
1133
+ subtaskId,
1134
+ subtask: newSubtask,
1135
+ isBlocked: newSubtask.status === 'blocked',
1136
+ message: newSubtask.status === 'blocked'
1137
+ ? `Task "${title}" delegated to ${delegatedTo} (blocked on dependencies)`
1138
+ : `Task "${title}" delegated to ${delegatedTo}`
1139
+ };
1140
+ }
1141
+
1142
+ export function startSubtask(sessionId: string, subtaskId: string): SubtaskOperationResult {
1143
+ const state = loadCollabState();
1144
+ const session = state.activeSessions[sessionId];
1145
+
1146
+ if (!session) {
1147
+ return { success: false, error: `Session not found: ${sessionId}` };
1148
+ }
1149
+
1150
+ const subtask = session.subtasks.find(s => s.id === subtaskId);
1151
+ if (!subtask) {
1152
+ return { success: false, error: `Subtask not found: ${subtaskId}` };
1153
+ }
1154
+
1155
+ if (subtask.status === 'blocked') {
1156
+ return {
1157
+ success: false,
1158
+ error: 'Subtask is blocked on dependencies'
1159
+ };
1160
+ }
1161
+
1162
+ if (subtask.status === 'in_progress') {
1163
+ return { success: false, error: 'Subtask already in progress' };
1164
+ }
1165
+
1166
+ subtask.status = 'in_progress';
1167
+ subtask.startedAt = new Date().toISOString();
1168
+
1169
+ const participant = session.agentParticipants[subtask.delegatedTo];
1170
+ if (participant) {
1171
+ participant.status = 'working';
1172
+ }
1173
+
1174
+ state.history.push({
1175
+ action: 'subtask_started',
1176
+ sessionId,
1177
+ subtaskId,
1178
+ agent: subtask.delegatedTo,
1179
+ timestamp: new Date().toISOString()
1180
+ });
1181
+
1182
+ saveCollabState(state);
1183
+
1184
+ return {
1185
+ success: true,
1186
+ sessionId,
1187
+ subtaskId,
1188
+ subtask,
1189
+ message: `Started working on "${subtask.title}"`
1190
+ };
1191
+ }
1192
+
1193
+ export function updateSubtaskProgress(
1194
+ sessionId: string,
1195
+ subtaskId: string,
1196
+ update: UpdateProgressInput
1197
+ ): SubtaskOperationResult {
1198
+ const state = loadCollabState();
1199
+ const session = state.activeSessions[sessionId];
1200
+
1201
+ if (!session) {
1202
+ return { success: false, error: `Session not found: ${sessionId}` };
1203
+ }
1204
+
1205
+ const subtask = session.subtasks.find(s => s.id === subtaskId);
1206
+ if (!subtask) {
1207
+ return { success: false, error: `Subtask not found: ${subtaskId}` };
1208
+ }
1209
+
1210
+ const { progress, notes, artifacts, blockedReason } = update;
1211
+
1212
+ if (progress !== undefined) {
1213
+ subtask.progress = progress;
1214
+ }
1215
+
1216
+ if (notes) {
1217
+ subtask.progressNotes = subtask.progressNotes ?? [];
1218
+ subtask.progressNotes.push({
1219
+ note: notes,
1220
+ timestamp: new Date().toISOString()
1221
+ });
1222
+ }
1223
+
1224
+ if (artifacts && artifacts.length > 0) {
1225
+ subtask.artifacts = [...(subtask.artifacts ?? []), ...artifacts];
1226
+ session.artifacts.push(...artifacts.map(a => ({
1227
+ ...a,
1228
+ subtaskId,
1229
+ addedAt: new Date().toISOString()
1230
+ })));
1231
+ }
1232
+
1233
+ if (blockedReason) {
1234
+ subtask.status = 'blocked';
1235
+ subtask.blockedReason = blockedReason;
1236
+ state.blockedTasks.push({
1237
+ sessionId,
1238
+ subtaskId,
1239
+ reason: blockedReason,
1240
+ blockedAt: new Date().toISOString()
1241
+ });
1242
+ }
1243
+
1244
+ saveCollabState(state);
1245
+
1246
+ return {
1247
+ success: true,
1248
+ sessionId,
1249
+ subtaskId,
1250
+ subtask,
1251
+ message: 'Subtask progress updated'
1252
+ };
1253
+ }
1254
+
1255
+ export function completeSubtask(
1256
+ sessionId: string,
1257
+ subtaskId: string,
1258
+ result: CompleteSubtaskInput
1259
+ ): CompleteSubtaskResult {
1260
+ const state = loadCollabState();
1261
+ const session = state.activeSessions[sessionId];
1262
+
1263
+ if (!session) {
1264
+ return { success: false, error: `Session not found: ${sessionId}` };
1265
+ }
1266
+
1267
+ const subtask = session.subtasks.find(s => s.id === subtaskId);
1268
+ if (!subtask) {
1269
+ return { success: false, error: `Subtask not found: ${subtaskId}` };
1270
+ }
1271
+
1272
+ const {
1273
+ output,
1274
+ artifacts = [],
1275
+ contextUpdates = {},
1276
+ learnings = [],
1277
+ success = true
1278
+ } = result;
1279
+
1280
+ subtask.status = success ? 'complete' : 'failed';
1281
+ subtask.completedAt = new Date().toISOString();
1282
+ subtask.result = {
1283
+ output,
1284
+ success,
1285
+ duration: subtask.startedAt
1286
+ ? Date.now() - new Date(subtask.startedAt).getTime()
1287
+ : null
1288
+ };
1289
+
1290
+ if (artifacts.length > 0) {
1291
+ subtask.artifacts = [...(subtask.artifacts ?? []), ...artifacts];
1292
+ session.artifacts.push(...artifacts.map(a => ({
1293
+ ...a,
1294
+ subtaskId,
1295
+ addedAt: new Date().toISOString()
1296
+ })));
1297
+ }
1298
+
1299
+ if (Object.keys(contextUpdates).length > 0) {
1300
+ session.context.history.push({
1301
+ update: contextUpdates,
1302
+ source: subtaskId,
1303
+ timestamp: new Date().toISOString()
1304
+ });
1305
+ session.context.current = {
1306
+ ...session.context.current,
1307
+ ...contextUpdates
1308
+ };
1309
+ }
1310
+
1311
+ if (learnings.length > 0) {
1312
+ session.learnings = session.learnings ?? [];
1313
+ session.learnings.push(...learnings.map(l => ({
1314
+ content: l,
1315
+ subtaskId,
1316
+ agent: subtask.delegatedTo,
1317
+ timestamp: new Date().toISOString()
1318
+ })));
1319
+ }
1320
+
1321
+ session.results[subtaskId] = result;
1322
+
1323
+ const participant = session.agentParticipants[subtask.delegatedTo];
1324
+ if (participant) {
1325
+ participant.tasksCompleted++;
1326
+ participant.status = 'standby';
1327
+ }
1328
+
1329
+ const agentQueue = state.taskQueues[subtask.delegatedTo];
1330
+ if (agentQueue) {
1331
+ state.taskQueues[subtask.delegatedTo] = agentQueue
1332
+ .filter(t => !(t.sessionId === sessionId && t.subtaskId === subtaskId));
1333
+ }
1334
+
1335
+ const unblockedTasks: string[] = [];
1336
+ for (const t of session.subtasks) {
1337
+ if (t.status === 'blocked' && t.dependencies.includes(subtaskId)) {
1338
+ t.unmetDependencies = t.unmetDependencies.filter(d => d !== subtaskId);
1339
+ if (t.unmetDependencies.length === 0) {
1340
+ t.status = 'pending';
1341
+ unblockedTasks.push(t.id);
1342
+ }
1343
+ }
1344
+ }
1345
+
1346
+ state.blockedTasks = state.blockedTasks.filter(
1347
+ bt => !(bt.sessionId === sessionId && bt.subtaskId === subtaskId)
1348
+ );
1349
+
1350
+ state.history.push({
1351
+ action: 'subtask_completed',
1352
+ sessionId,
1353
+ subtaskId,
1354
+ agent: subtask.delegatedTo,
1355
+ success,
1356
+ unblockedTasks,
1357
+ timestamp: new Date().toISOString()
1358
+ });
1359
+
1360
+ saveCollabState(state);
1361
+
1362
+ emitCollabTelemetry('subtask_completed', {
1363
+ sessionId,
1364
+ subtaskId,
1365
+ agent: subtask.delegatedTo,
1366
+ success,
1367
+ unblockedCount: unblockedTasks.length
1368
+ });
1369
+
1370
+ const allComplete = session.subtasks.every(
1371
+ s => s.status === 'complete' || s.status === 'failed'
1372
+ );
1373
+ const allSuccessful = session.subtasks.every(s => s.status === 'complete');
1374
+
1375
+ return {
1376
+ success: true,
1377
+ sessionId,
1378
+ subtaskId,
1379
+ taskSuccess: success,
1380
+ allSubtasksComplete: allComplete,
1381
+ allSubtasksSuccessful: allSuccessful,
1382
+ unblockedTasks,
1383
+ message: allComplete
1384
+ ? allSuccessful
1385
+ ? 'All subtasks complete. Ready to finalize session.'
1386
+ : 'All subtasks processed with some failures.'
1387
+ : `Subtask "${subtask.title}" completed`
1388
+ };
1389
+ }
1390
+
1391
+ export function failSubtask(
1392
+ sessionId: string,
1393
+ subtaskId: string,
1394
+ failure: FailSubtaskInput
1395
+ ): FailSubtaskResult {
1396
+ const state = loadCollabState();
1397
+ const session = state.activeSessions[sessionId];
1398
+
1399
+ if (!session) {
1400
+ return { success: false, error: `Session not found: ${sessionId}` };
1401
+ }
1402
+
1403
+ const subtask = session.subtasks.find(s => s.id === subtaskId);
1404
+ if (!subtask) {
1405
+ return { success: false, error: `Subtask not found: ${subtaskId}` };
1406
+ }
1407
+
1408
+ const { reason, canRetry = false, suggestedAction = null } = failure;
1409
+
1410
+ subtask.status = 'failed';
1411
+ subtask.completedAt = new Date().toISOString();
1412
+ subtask.failure = {
1413
+ reason,
1414
+ canRetry,
1415
+ suggestedAction,
1416
+ failedAt: new Date().toISOString()
1417
+ };
1418
+ subtask.retryCount++;
1419
+
1420
+ const participant = session.agentParticipants[subtask.delegatedTo];
1421
+ if (participant) {
1422
+ participant.status = 'standby';
1423
+ }
1424
+
1425
+ state.history.push({
1426
+ action: 'subtask_failed',
1427
+ sessionId,
1428
+ subtaskId,
1429
+ agent: subtask.delegatedTo,
1430
+ reason,
1431
+ canRetry,
1432
+ timestamp: new Date().toISOString()
1433
+ });
1434
+
1435
+ saveCollabState(state);
1436
+
1437
+ return {
1438
+ success: true,
1439
+ sessionId,
1440
+ subtaskId,
1441
+ canRetry,
1442
+ suggestedAction,
1443
+ message: `Subtask "${subtask.title}" failed: ${reason}`
1444
+ };
1445
+ }
1446
+
1447
+ export function retrySubtask(
1448
+ sessionId: string,
1449
+ subtaskId: string,
1450
+ options: RetryOptions = {}
1451
+ ): RetryResult {
1452
+ const state = loadCollabState();
1453
+ const session = state.activeSessions[sessionId];
1454
+
1455
+ if (!session) {
1456
+ return { success: false, error: `Session not found: ${sessionId}` };
1457
+ }
1458
+
1459
+ const subtask = session.subtasks.find(s => s.id === subtaskId);
1460
+ if (!subtask) {
1461
+ return { success: false, error: `Subtask not found: ${subtaskId}` };
1462
+ }
1463
+
1464
+ if (subtask.status !== 'failed') {
1465
+ return { success: false, error: 'Can only retry failed subtasks' };
1466
+ }
1467
+
1468
+ const { reassignTo, additionalContext = {} } = options;
1469
+
1470
+ subtask.status = 'pending';
1471
+ subtask.completedAt = null;
1472
+ subtask.startedAt = null;
1473
+ subtask.result = null;
1474
+
1475
+ if (reassignTo) {
1476
+ subtask.reassignedFrom = subtask.delegatedTo;
1477
+ subtask.delegatedTo = reassignTo;
1478
+ }
1479
+
1480
+ if (Object.keys(additionalContext).length > 0) {
1481
+ subtask.context = {
1482
+ ...subtask.context,
1483
+ ...additionalContext,
1484
+ previousAttemptFailed: true
1485
+ };
1486
+ }
1487
+
1488
+ insertIntoTaskQueue(session.taskQueue, subtask);
1489
+
1490
+ state.history.push({
1491
+ action: 'subtask_retried',
1492
+ sessionId,
1493
+ subtaskId,
1494
+ retryCount: subtask.retryCount,
1495
+ reassignedTo: reassignTo ?? null,
1496
+ timestamp: new Date().toISOString()
1497
+ });
1498
+
1499
+ saveCollabState(state);
1500
+
1501
+ return {
1502
+ success: true,
1503
+ sessionId,
1504
+ subtaskId,
1505
+ retryCount: subtask.retryCount,
1506
+ message: `Subtask "${subtask.title}" queued for retry`
1507
+ };
1508
+ }
1509
+
1510
+ export function requestHandoff(sessionId: string, handoff: RequestHandoffInput): HandoffResult {
1511
+ const state = loadCollabState();
1512
+ const session = state.activeSessions[sessionId];
1513
+
1514
+ if (!session) {
1515
+ return { success: false, error: `Session not found: ${sessionId}` };
1516
+ }
1517
+
1518
+ const {
1519
+ fromAgent,
1520
+ toAgent,
1521
+ reason,
1522
+ context = {},
1523
+ artifacts = [],
1524
+ transferPrimaryRole = false,
1525
+ subtaskId = null,
1526
+ urgency = 'normal',
1527
+ expectedDuration = null
1528
+ } = handoff;
1529
+
1530
+ if (!fromAgent || !toAgent) {
1531
+ return { success: false, error: 'fromAgent and toAgent are required' };
1532
+ }
1533
+
1534
+ const handoffId = `handoff-${session.handoffs.length + 1}-${Date.now()}`;
1535
+
1536
+ const handoffContext: Record<string, unknown> = {
1537
+ ...session.context.current,
1538
+ ...context,
1539
+ handoffReason: reason,
1540
+ previousAgent: fromAgent,
1541
+ sessionProgress: {
1542
+ completedSubtasks: session.subtasks.filter(s => s.status === 'complete').length,
1543
+ totalSubtasks: session.subtasks.length,
1544
+ artifacts: session.artifacts.length
1545
+ }
1546
+ };
1547
+
1548
+ if (subtaskId) {
1549
+ const transferredSubtask = session.subtasks.find(s => s.id === subtaskId);
1550
+ if (transferredSubtask) {
1551
+ handoffContext.subtask = {
1552
+ id: transferredSubtask.id,
1553
+ title: transferredSubtask.title,
1554
+ description: transferredSubtask.description,
1555
+ progress: transferredSubtask.progress,
1556
+ progressNotes: transferredSubtask.progressNotes
1557
+ };
1558
+ }
1559
+ }
1560
+
1561
+ const newHandoff: Handoff = {
1562
+ id: handoffId,
1563
+ fromAgent,
1564
+ toAgent,
1565
+ reason,
1566
+ context: handoffContext,
1567
+ artifacts,
1568
+ transferPrimaryRole,
1569
+ subtaskId,
1570
+ urgency,
1571
+ expectedDuration,
1572
+ status: 'pending',
1573
+ requestedAt: new Date().toISOString(),
1574
+ acceptedAt: null,
1575
+ completedAt: null,
1576
+ rejectionReason: null
1577
+ };
1578
+
1579
+ session.handoffs.push(newHandoff);
1580
+ session.metrics.handoffCount++;
1581
+
1582
+ state.pendingHandoffs.push({
1583
+ sessionId,
1584
+ handoffId,
1585
+ ...newHandoff
1586
+ });
1587
+
1588
+ state.metrics.totalHandoffs++;
1589
+
1590
+ state.history.push({
1591
+ action: 'handoff_requested',
1592
+ sessionId,
1593
+ handoffId,
1594
+ fromAgent,
1595
+ toAgent,
1596
+ transferPrimaryRole,
1597
+ urgency,
1598
+ timestamp: new Date().toISOString()
1599
+ });
1600
+
1601
+ saveCollabState(state);
1602
+
1603
+ emitCollabTelemetry('handoff_requested', {
1604
+ sessionId,
1605
+ handoffId,
1606
+ fromAgent,
1607
+ toAgent,
1608
+ transferPrimaryRole,
1609
+ urgency
1610
+ });
1611
+
1612
+ return {
1613
+ success: true,
1614
+ sessionId,
1615
+ handoffId,
1616
+ handoff: newHandoff,
1617
+ message: transferPrimaryRole
1618
+ ? `Primary role handoff requested from ${fromAgent} to ${toAgent}`
1619
+ : `Handoff requested from ${fromAgent} to ${toAgent}`
1620
+ };
1621
+ }
1622
+
1623
+ export function acceptHandoff(
1624
+ sessionId: string,
1625
+ handoffId: string,
1626
+ options: AcceptHandoffOptions = {}
1627
+ ): HandoffResult {
1628
+ const state = loadCollabState();
1629
+ const session = state.activeSessions[sessionId];
1630
+
1631
+ if (!session) {
1632
+ return { success: false, error: `Session not found: ${sessionId}` };
1633
+ }
1634
+
1635
+ const handoff = session.handoffs.find(h => h.id === handoffId);
1636
+ if (!handoff) {
1637
+ return { success: false, error: `Handoff not found: ${handoffId}` };
1638
+ }
1639
+
1640
+ if (handoff.status !== 'pending') {
1641
+ return { success: false, error: `Handoff already ${handoff.status}` };
1642
+ }
1643
+
1644
+ const { acknowledgment = null, estimatedCompletion = null } = options;
1645
+
1646
+ handoff.status = 'accepted';
1647
+ handoff.acceptedAt = new Date().toISOString();
1648
+ handoff.acknowledgment = acknowledgment;
1649
+ handoff.estimatedCompletion = estimatedCompletion;
1650
+
1651
+ const previousPrimary = session.primaryAgent;
1652
+ if (handoff.transferPrimaryRole) {
1653
+ session.primaryAgent = handoff.toAgent;
1654
+
1655
+ const prevParticipant = session.agentParticipants[previousPrimary];
1656
+ if (prevParticipant) {
1657
+ prevParticipant.role = 'supporting';
1658
+ }
1659
+ const newParticipant = session.agentParticipants[handoff.toAgent];
1660
+ if (newParticipant) {
1661
+ newParticipant.role = 'primary';
1662
+ }
1663
+ }
1664
+
1665
+ if (handoff.subtaskId) {
1666
+ const subtask = session.subtasks.find(s => s.id === handoff.subtaskId);
1667
+ if (subtask) {
1668
+ subtask.delegatedTo = handoff.toAgent;
1669
+ subtask.transferHistory = subtask.transferHistory ?? [];
1670
+ subtask.transferHistory.push({
1671
+ from: handoff.fromAgent,
1672
+ to: handoff.toAgent,
1673
+ handoffId,
1674
+ timestamp: new Date().toISOString()
1675
+ });
1676
+ }
1677
+ }
1678
+
1679
+ const toParticipant = session.agentParticipants[handoff.toAgent];
1680
+ if (toParticipant) {
1681
+ toParticipant.status = 'active';
1682
+ }
1683
+
1684
+ state.pendingHandoffs = state.pendingHandoffs.filter(
1685
+ h => !(h.sessionId === sessionId && h.handoffId === handoffId)
1686
+ );
1687
+
1688
+ state.history.push({
1689
+ action: 'handoff_accepted',
1690
+ sessionId,
1691
+ handoffId,
1692
+ byAgent: handoff.toAgent,
1693
+ transferPrimaryRole: handoff.transferPrimaryRole,
1694
+ previousPrimary: handoff.transferPrimaryRole ? previousPrimary : null,
1695
+ timestamp: new Date().toISOString()
1696
+ });
1697
+
1698
+ saveCollabState(state);
1699
+
1700
+ emitCollabTelemetry('handoff_accepted', {
1701
+ sessionId,
1702
+ handoffId,
1703
+ byAgent: handoff.toAgent,
1704
+ transferPrimaryRole: handoff.transferPrimaryRole
1705
+ });
1706
+
1707
+ return {
1708
+ success: true,
1709
+ sessionId,
1710
+ handoffId,
1711
+ newPrimaryAgent: session.primaryAgent,
1712
+ transferredSubtask: handoff.subtaskId,
1713
+ context: handoff.context,
1714
+ message: handoff.transferPrimaryRole
1715
+ ? `Primary role transferred to ${handoff.toAgent}`
1716
+ : `Handoff accepted by ${handoff.toAgent}`
1717
+ };
1718
+ }
1719
+
1720
+ export function rejectHandoff(sessionId: string, handoffId: string, reason: string): HandoffResult {
1721
+ const state = loadCollabState();
1722
+ const session = state.activeSessions[sessionId];
1723
+
1724
+ if (!session) {
1725
+ return { success: false, error: `Session not found: ${sessionId}` };
1726
+ }
1727
+
1728
+ const handoff = session.handoffs.find(h => h.id === handoffId);
1729
+ if (!handoff) {
1730
+ return { success: false, error: `Handoff not found: ${handoffId}` };
1731
+ }
1732
+
1733
+ if (handoff.status !== 'pending') {
1734
+ return { success: false, error: `Handoff already ${handoff.status}` };
1735
+ }
1736
+
1737
+ handoff.status = 'rejected';
1738
+ handoff.rejectionReason = reason;
1739
+ handoff.rejectedAt = new Date().toISOString();
1740
+
1741
+ state.pendingHandoffs = state.pendingHandoffs.filter(
1742
+ h => !(h.sessionId === sessionId && h.handoffId === handoffId)
1743
+ );
1744
+
1745
+ state.history.push({
1746
+ action: 'handoff_rejected',
1747
+ sessionId,
1748
+ handoffId,
1749
+ byAgent: handoff.toAgent,
1750
+ reason,
1751
+ timestamp: new Date().toISOString()
1752
+ });
1753
+
1754
+ saveCollabState(state);
1755
+
1756
+ return {
1757
+ success: true,
1758
+ sessionId,
1759
+ handoffId,
1760
+ rejectionReason: reason,
1761
+ message: `Handoff rejected by ${handoff.toAgent}: ${reason}`
1762
+ };
1763
+ }
1764
+
1765
+ export function completeHandoff(
1766
+ sessionId: string,
1767
+ handoffId: string,
1768
+ result: Record<string, unknown> = {}
1769
+ ): HandoffResult {
1770
+ const state = loadCollabState();
1771
+ const session = state.activeSessions[sessionId];
1772
+
1773
+ if (!session) {
1774
+ return { success: false, error: `Session not found: ${sessionId}` };
1775
+ }
1776
+
1777
+ const handoff = session.handoffs.find(h => h.id === handoffId);
1778
+ if (!handoff) {
1779
+ return { success: false, error: `Handoff not found: ${handoffId}` };
1780
+ }
1781
+
1782
+ if (handoff.status !== 'accepted') {
1783
+ return { success: false, error: 'Handoff must be accepted before completing' };
1784
+ }
1785
+
1786
+ handoff.status = 'completed';
1787
+ handoff.completedAt = new Date().toISOString();
1788
+ handoff.result = result;
1789
+
1790
+ state.history.push({
1791
+ action: 'handoff_completed',
1792
+ sessionId,
1793
+ handoffId,
1794
+ byAgent: handoff.toAgent,
1795
+ timestamp: new Date().toISOString()
1796
+ });
1797
+
1798
+ saveCollabState(state);
1799
+
1800
+ return {
1801
+ success: true,
1802
+ sessionId,
1803
+ handoffId,
1804
+ message: `Handoff completed by ${handoff.toAgent}`
1805
+ };
1806
+ }
1807
+
1808
+ export function createCheckpoint(sessionId: string, description: string = ''): CheckpointResult {
1809
+ const state = loadCollabState();
1810
+ const session = state.activeSessions[sessionId];
1811
+
1812
+ if (!session) {
1813
+ return { success: false, error: `Session not found: ${sessionId}` };
1814
+ }
1815
+
1816
+ const checkpointId = `checkpoint-${session.checkpoints.length + 1}`;
1817
+ const checkpoint: Checkpoint = {
1818
+ id: checkpointId,
1819
+ description,
1820
+ createdAt: new Date().toISOString(),
1821
+ snapshot: {
1822
+ subtasks: JSON.parse(JSON.stringify(session.subtasks)),
1823
+ context: JSON.parse(JSON.stringify(session.context)),
1824
+ results: JSON.parse(JSON.stringify(session.results)),
1825
+ artifacts: JSON.parse(JSON.stringify(session.artifacts)),
1826
+ primaryAgent: session.primaryAgent
1827
+ }
1828
+ };
1829
+
1830
+ session.checkpoints.push(checkpoint);
1831
+
1832
+ state.history.push({
1833
+ action: 'checkpoint_created',
1834
+ sessionId,
1835
+ checkpointId,
1836
+ description,
1837
+ timestamp: new Date().toISOString()
1838
+ });
1839
+
1840
+ saveCollabState(state);
1841
+
1842
+ return {
1843
+ success: true,
1844
+ sessionId,
1845
+ checkpointId,
1846
+ message: `Checkpoint created: ${description || checkpointId}`
1847
+ };
1848
+ }
1849
+
1850
+ export function restoreFromCheckpoint(sessionId: string, checkpointId: string): CheckpointResult {
1851
+ const state = loadCollabState();
1852
+ const session = state.activeSessions[sessionId];
1853
+
1854
+ if (!session) {
1855
+ return { success: false, error: `Session not found: ${sessionId}` };
1856
+ }
1857
+
1858
+ const checkpoint = session.checkpoints.find(c => c.id === checkpointId);
1859
+ if (!checkpoint) {
1860
+ return { success: false, error: `Checkpoint not found: ${checkpointId}` };
1861
+ }
1862
+
1863
+ session.subtasks = JSON.parse(JSON.stringify(checkpoint.snapshot.subtasks));
1864
+ session.context = JSON.parse(JSON.stringify(checkpoint.snapshot.context));
1865
+ session.results = JSON.parse(JSON.stringify(checkpoint.snapshot.results));
1866
+ session.artifacts = JSON.parse(JSON.stringify(checkpoint.snapshot.artifacts));
1867
+ session.primaryAgent = checkpoint.snapshot.primaryAgent;
1868
+
1869
+ state.history.push({
1870
+ action: 'checkpoint_restored',
1871
+ sessionId,
1872
+ checkpointId,
1873
+ timestamp: new Date().toISOString()
1874
+ });
1875
+
1876
+ saveCollabState(state);
1877
+
1878
+ return {
1879
+ success: true,
1880
+ sessionId,
1881
+ checkpointId,
1882
+ message: `Session restored to checkpoint: ${checkpoint.description || checkpointId}`
1883
+ };
1884
+ }
1885
+
1886
+ export function finalizeSession(
1887
+ sessionId: string,
1888
+ summary: Record<string, unknown> = {}
1889
+ ): StartSessionResult {
1890
+ const state = loadCollabState();
1891
+ const session = state.activeSessions[sessionId];
1892
+
1893
+ if (!session) {
1894
+ return { success: false, error: `Session not found: ${sessionId}` };
1895
+ }
1896
+
1897
+ const endTime = Date.now();
1898
+ const duration = endTime - session.metrics.startTime;
1899
+
1900
+ const completedSubtasks = session.subtasks.filter(s => s.status === 'complete');
1901
+ const failedSubtasks = session.subtasks.filter(s => s.status === 'failed');
1902
+
1903
+ session.status = 'complete';
1904
+ session.completedAt = new Date().toISOString();
1905
+ session.summary = {
1906
+ ...summary,
1907
+ task: session.task,
1908
+ primaryAgent: session.primaryAgent,
1909
+ subtasksCompleted: completedSubtasks.length,
1910
+ subtasksFailed: failedSubtasks.length,
1911
+ totalSubtasks: session.subtasks.length,
1912
+ handoffsCount: session.handoffs.length,
1913
+ acceptedHandoffs: session.handoffs.filter(h => h.status === 'accepted').length,
1914
+ rejectedHandoffs: session.handoffs.filter(h => h.status === 'rejected').length,
1915
+ duration,
1916
+ durationFormatted: formatDuration(duration),
1917
+ artifactsCount: session.artifacts.length,
1918
+ agentParticipation: Object.entries(session.agentParticipants).map(([agent, data]) => ({
1919
+ agent,
1920
+ role: data.role,
1921
+ tasksAssigned: data.tasksAssigned,
1922
+ tasksCompleted: data.tasksCompleted
1923
+ })),
1924
+ learnings: session.learnings ?? [],
1925
+ checkpointsCount: session.checkpoints.length,
1926
+ successRate: session.subtasks.length > 0
1927
+ ? Math.round((completedSubtasks.length / session.subtasks.length) * 100)
1928
+ : 100
1929
+ };
1930
+
1931
+ state.completedSessions.push(session);
1932
+ delete state.activeSessions[sessionId];
1933
+
1934
+ for (const agent of Object.keys(session.agentParticipants)) {
1935
+ const agentQueue = state.taskQueues[agent];
1936
+ if (agentQueue) {
1937
+ state.taskQueues[agent] = agentQueue.filter(t => t.sessionId !== sessionId);
1938
+ }
1939
+ }
1940
+
1941
+ state.metrics.successfulSessions++;
1942
+ const totalDuration = state.metrics.avgSessionDuration * (state.metrics.successfulSessions - 1);
1943
+ state.metrics.avgSessionDuration = (totalDuration + duration) / state.metrics.successfulSessions;
1944
+
1945
+ state.history.push({
1946
+ action: 'session_finalized',
1947
+ sessionId,
1948
+ duration,
1949
+ successRate: session.summary.successRate,
1950
+ timestamp: new Date().toISOString()
1951
+ });
1952
+
1953
+ saveCollabState(state);
1954
+
1955
+ emitCollabTelemetry('collab_session_completed', {
1956
+ sessionId,
1957
+ duration,
1958
+ successRate: session.summary.successRate,
1959
+ subtaskCount: session.subtasks.length,
1960
+ handoffCount: session.handoffs.length
1961
+ });
1962
+
1963
+ return {
1964
+ success: true,
1965
+ sessionId,
1966
+ session,
1967
+ message: 'Collaboration session finalized'
1968
+ };
1969
+ }
1970
+
1971
+ export function abortSession(sessionId: string, reason: string = ''): StartSessionResult {
1972
+ const state = loadCollabState();
1973
+ const session = state.activeSessions[sessionId];
1974
+
1975
+ if (!session) {
1976
+ return { success: false, error: `Session not found: ${sessionId}` };
1977
+ }
1978
+
1979
+ session.status = 'aborted';
1980
+ session.abortedAt = new Date().toISOString();
1981
+ session.abortReason = reason;
1982
+
1983
+ state.completedSessions.push(session);
1984
+ delete state.activeSessions[sessionId];
1985
+
1986
+ state.pendingHandoffs = state.pendingHandoffs.filter(h => h.sessionId !== sessionId);
1987
+
1988
+ for (const agent of Object.keys(session.agentParticipants)) {
1989
+ const agentQueue = state.taskQueues[agent];
1990
+ if (agentQueue) {
1991
+ state.taskQueues[agent] = agentQueue.filter(t => t.sessionId !== sessionId);
1992
+ }
1993
+ }
1994
+
1995
+ state.history.push({
1996
+ action: 'session_aborted',
1997
+ sessionId,
1998
+ reason,
1999
+ timestamp: new Date().toISOString()
2000
+ });
2001
+
2002
+ saveCollabState(state);
2003
+
2004
+ return {
2005
+ success: true,
2006
+ sessionId,
2007
+ message: `Collaboration session aborted: ${reason}`
2008
+ };
2009
+ }
2010
+
2011
+ export function getSessionStatus(sessionId: string): SessionStatusResult {
2012
+ const state = loadCollabState();
2013
+ const session = state.activeSessions[sessionId];
2014
+
2015
+ if (!session) {
2016
+ const completed = state.completedSessions.find(s => s.id === sessionId);
2017
+ if (completed) {
2018
+ return {
2019
+ found: true,
2020
+ session: completed,
2021
+ isActive: false,
2022
+ summary: completed.summary
2023
+ };
2024
+ }
2025
+ return { found: false };
2026
+ }
2027
+
2028
+ const pendingSubtasks = session.subtasks.filter(s => s.status === 'pending');
2029
+ const inProgressSubtasks = session.subtasks.filter(s => s.status === 'in_progress');
2030
+ const blockedSubtasks = session.subtasks.filter(s => s.status === 'blocked');
2031
+ const completedSubtasks = session.subtasks.filter(s => s.status === 'complete');
2032
+ const failedSubtasks = session.subtasks.filter(s => s.status === 'failed');
2033
+ const pendingHandoffs = session.handoffs.filter(h => h.status === 'pending');
2034
+
2035
+ const elapsed = Date.now() - session.metrics.startTime;
2036
+
2037
+ return {
2038
+ found: true,
2039
+ isActive: true,
2040
+ session: {
2041
+ id: session.id,
2042
+ task: session.task,
2043
+ status: session.status,
2044
+ primaryAgent: session.primaryAgent,
2045
+ supportingAgents: session.supportingAgents,
2046
+ createdAt: session.createdAt
2047
+ },
2048
+ progress: {
2049
+ subtasks: {
2050
+ pending: pendingSubtasks.length,
2051
+ inProgress: inProgressSubtasks.length,
2052
+ blocked: blockedSubtasks.length,
2053
+ completed: completedSubtasks.length,
2054
+ failed: failedSubtasks.length,
2055
+ total: session.subtasks.length,
2056
+ percent: session.subtasks.length > 0
2057
+ ? Math.round((completedSubtasks.length / session.subtasks.length) * 100)
2058
+ : 0
2059
+ },
2060
+ handoffs: {
2061
+ pending: pendingHandoffs.length,
2062
+ total: session.handoffs.length,
2063
+ accepted: session.handoffs.filter(h => h.status === 'accepted').length
2064
+ },
2065
+ artifacts: session.artifacts.length,
2066
+ checkpoints: session.checkpoints.length
2067
+ },
2068
+ agents: Object.entries(session.agentParticipants).map(([agent, data]) => ({
2069
+ agent,
2070
+ role: data.role,
2071
+ status: data.status,
2072
+ tasksAssigned: data.tasksAssigned,
2073
+ tasksCompleted: data.tasksCompleted
2074
+ })),
2075
+ timing: {
2076
+ elapsed,
2077
+ elapsedFormatted: formatDuration(elapsed)
2078
+ },
2079
+ nextActions: getNextActions(session, pendingSubtasks, pendingHandoffs, blockedSubtasks)
2080
+ };
2081
+ }
2082
+
2083
+ export function getNextTaskForAgent(agentName: string): NextTaskResult {
2084
+ const state = loadCollabState();
2085
+ const agentQueue = state.taskQueues[agentName] ?? [];
2086
+
2087
+ if (agentQueue.length === 0) {
2088
+ return { hasTask: false, message: 'No tasks in queue' };
2089
+ }
2090
+
2091
+ const priorityOrder: Record<string, number> = {
2092
+ 'critical': 0,
2093
+ 'high': 1,
2094
+ 'medium': 2,
2095
+ 'low': 3
2096
+ };
2097
+
2098
+ agentQueue.sort((a, b) => {
2099
+ const priorityDiff = (priorityOrder[a.priority] ?? 2) - (priorityOrder[b.priority] ?? 2);
2100
+ if (priorityDiff !== 0) return priorityDiff;
2101
+ return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
2102
+ });
2103
+
2104
+ const nextItem = agentQueue[0];
2105
+ if (!nextItem) {
2106
+ return { hasTask: false, message: 'No tasks in queue' };
2107
+ }
2108
+
2109
+ const session = state.activeSessions[nextItem.sessionId];
2110
+
2111
+ if (!session) {
2112
+ state.taskQueues[agentName] = agentQueue.slice(1);
2113
+ saveCollabState(state);
2114
+ return getNextTaskForAgent(agentName);
2115
+ }
2116
+
2117
+ const subtask = session.subtasks.find(s => s.id === nextItem.subtaskId);
2118
+
2119
+ if (!subtask || subtask.status === 'blocked') {
2120
+ return {
2121
+ hasTask: false,
2122
+ blocked: true,
2123
+ message: 'Next task is blocked on dependencies'
2124
+ };
2125
+ }
2126
+
2127
+ return {
2128
+ hasTask: true,
2129
+ sessionId: nextItem.sessionId,
2130
+ subtask,
2131
+ sessionContext: session.context.current,
2132
+ message: `Next task: ${subtask.title}`
2133
+ };
2134
+ }
2135
+
2136
+ export function listActiveSessions(): ActiveSessionInfo[] {
2137
+ const state = loadCollabState();
2138
+
2139
+ return Object.values(state.activeSessions).map(session => ({
2140
+ id: session.id,
2141
+ task: session.task,
2142
+ primaryAgent: session.primaryAgent,
2143
+ status: session.status,
2144
+ subtaskCount: session.subtasks.length,
2145
+ createdAt: session.createdAt
2146
+ }));
2147
+ }
2148
+
2149
+ export function getPendingHandoffsForAgent(agentName: string): PendingHandoffItem[] {
2150
+ const state = loadCollabState();
2151
+ return state.pendingHandoffs.filter(h => h.toAgent === agentName);
2152
+ }
2153
+
2154
+ export function startFromPattern(
2155
+ patternName: string,
2156
+ task: string,
2157
+ options: StartFromPatternOptions = {}
2158
+ ): StartFromPatternResult {
2159
+ const pattern = COLLAB_PATTERNS[patternName];
2160
+ if (!pattern) {
2161
+ return {
2162
+ success: false,
2163
+ error: `Unknown pattern: ${patternName}`,
2164
+ availablePatterns: Object.keys(COLLAB_PATTERNS)
2165
+ };
2166
+ }
2167
+
2168
+ const { context = {}, autoStart = false } = options;
2169
+
2170
+ const sessionResult = startSession({
2171
+ task,
2172
+ primaryAgent: pattern.primaryAgent,
2173
+ supportingAgents: pattern.supportingAgents,
2174
+ context: {
2175
+ pattern: patternName,
2176
+ ...context
2177
+ }
2178
+ });
2179
+
2180
+ if (!sessionResult.success || !sessionResult.sessionId) {
2181
+ return sessionResult;
2182
+ }
2183
+
2184
+ const subtaskResults: DelegateTaskResult[] = [];
2185
+ const idMapping: Record<string, string> = {};
2186
+
2187
+ for (let i = 0; i < pattern.subtaskTemplate.length; i++) {
2188
+ const template = pattern.subtaskTemplate[i];
2189
+ if (!template) continue;
2190
+
2191
+ const templateId = `subtask-${i + 1}`;
2192
+
2193
+ const mappedDependencies = (template.dependencies ?? []).map(depId => {
2194
+ return idMapping[depId] ?? depId;
2195
+ });
2196
+
2197
+ const result = delegateTask(sessionResult.sessionId, {
2198
+ ...template,
2199
+ description: `${template.title} for: ${task}`,
2200
+ dependencies: mappedDependencies
2201
+ });
2202
+
2203
+ if (result.success && result.subtaskId) {
2204
+ idMapping[templateId] = result.subtaskId;
2205
+ }
2206
+ subtaskResults.push(result);
2207
+ }
2208
+
2209
+ const firstResult = subtaskResults[0];
2210
+ if (autoStart && firstResult && firstResult.success && firstResult.subtaskId) {
2211
+ startSubtask(sessionResult.sessionId, firstResult.subtaskId);
2212
+ }
2213
+
2214
+ return {
2215
+ success: true,
2216
+ sessionId: sessionResult.sessionId,
2217
+ pattern: patternName,
2218
+ patternName: pattern.name,
2219
+ primaryAgent: pattern.primaryAgent,
2220
+ subtasks: subtaskResults.map(r => ({
2221
+ id: r.subtaskId,
2222
+ title: r.subtask?.title,
2223
+ delegatedTo: r.subtask?.delegatedTo,
2224
+ status: r.subtask?.status
2225
+ })),
2226
+ message: `Started collaboration from "${pattern.name}" pattern`
2227
+ };
2228
+ }
2229
+
2230
+ export function getCollabStats(): CollabStats {
2231
+ const state = loadCollabState();
2232
+
2233
+ const activeSessions = Object.values(state.activeSessions);
2234
+ const totalActive = activeSessions.length;
2235
+ const totalSubtasksActive = activeSessions.reduce((sum, s) => sum + s.subtasks.length, 0);
2236
+ const pendingHandoffs = state.pendingHandoffs.length;
2237
+
2238
+ return {
2239
+ activeSessions: totalActive,
2240
+ completedSessions: state.completedSessions.length,
2241
+ totalSessions: state.metrics.totalSessions,
2242
+ successfulSessions: state.metrics.successfulSessions,
2243
+ totalSubtasks: state.metrics.totalSubtasks,
2244
+ totalHandoffs: state.metrics.totalHandoffs,
2245
+ activeSubtasks: totalSubtasksActive,
2246
+ pendingHandoffs,
2247
+ avgSessionDuration: state.metrics.avgSessionDuration,
2248
+ avgSessionDurationFormatted: formatDuration(state.metrics.avgSessionDuration || 0),
2249
+ blockedTasks: state.blockedTasks.length
2250
+ };
2251
+ }
2252
+
2253
+ export function getSessionHistory(sessionId: string | null = null): HistoryEntry[] {
2254
+ const state = loadCollabState();
2255
+
2256
+ if (sessionId) {
2257
+ return state.history.filter(h => h.sessionId === sessionId);
2258
+ }
2259
+
2260
+ return state.history.slice(-100);
2261
+ }
2262
+
2263
+ export function listPatterns(): PatternInfo[] {
2264
+ return Object.entries(COLLAB_PATTERNS).map(([key, pattern]) => ({
2265
+ key,
2266
+ name: pattern.name,
2267
+ description: pattern.description,
2268
+ primaryAgent: pattern.primaryAgent,
2269
+ supportingAgents: pattern.supportingAgents,
2270
+ subtaskCount: pattern.subtaskTemplate.length
2271
+ }));
2272
+ }
2273
+
2274
+ export function broadcastToSession(sessionId: string, message: BroadcastInput): BroadcastResult {
2275
+ const state = loadCollabState();
2276
+ const session = state.activeSessions[sessionId];
2277
+
2278
+ if (!session) {
2279
+ return { success: false, error: `Session not found: ${sessionId}` };
2280
+ }
2281
+
2282
+ const { type, content, fromAgent, priority = 'normal' } = message;
2283
+
2284
+ const broadcast: Broadcast = {
2285
+ id: `broadcast-${Date.now()}`,
2286
+ type,
2287
+ content,
2288
+ fromAgent,
2289
+ priority,
2290
+ timestamp: new Date().toISOString(),
2291
+ recipients: Object.keys(session.agentParticipants)
2292
+ };
2293
+
2294
+ session.broadcasts = session.broadcasts ?? [];
2295
+ session.broadcasts.push(broadcast);
2296
+
2297
+ state.history.push({
2298
+ action: 'broadcast_sent',
2299
+ sessionId,
2300
+ broadcastId: broadcast.id,
2301
+ fromAgent,
2302
+ recipientCount: broadcast.recipients.length,
2303
+ timestamp: broadcast.timestamp
2304
+ });
2305
+
2306
+ saveCollabState(state);
2307
+
2308
+ return {
2309
+ success: true,
2310
+ broadcastId: broadcast.id,
2311
+ recipients: broadcast.recipients,
2312
+ message: `Broadcast sent to ${broadcast.recipients.length} agents`
2313
+ };
2314
+ }
2315
+
2316
+ export function shareArtifact(sessionId: string, artifact: ShareArtifactInput): ShareArtifactResult {
2317
+ const state = loadCollabState();
2318
+ const session = state.activeSessions[sessionId];
2319
+
2320
+ if (!session) {
2321
+ return { success: false, error: `Session not found: ${sessionId}` };
2322
+ }
2323
+
2324
+ const { name, type, content, fromAgent, description = '' } = artifact;
2325
+
2326
+ if (!name || !type || !fromAgent) {
2327
+ return { success: false, error: 'name, type, and fromAgent are required' };
2328
+ }
2329
+
2330
+ const sharedArtifact: Artifact = {
2331
+ id: `artifact-${Date.now()}`,
2332
+ name,
2333
+ type,
2334
+ content,
2335
+ description,
2336
+ fromAgent,
2337
+ sharedAt: new Date().toISOString()
2338
+ };
2339
+
2340
+ session.artifacts.push(sharedArtifact);
2341
+
2342
+ state.history.push({
2343
+ action: 'artifact_shared',
2344
+ sessionId,
2345
+ artifactId: sharedArtifact.id,
2346
+ name,
2347
+ type,
2348
+ fromAgent,
2349
+ timestamp: sharedArtifact.sharedAt ?? new Date().toISOString()
2350
+ });
2351
+
2352
+ saveCollabState(state);
2353
+
2354
+ return {
2355
+ success: true,
2356
+ artifactId: sharedArtifact.id,
2357
+ message: `Artifact "${name}" shared with session`
2358
+ };
2359
+ }
2360
+
2361
+ export function getSessionArtifacts(sessionId: string): GetArtifactsResult {
2362
+ const state = loadCollabState();
2363
+ const session = state.activeSessions[sessionId] ??
2364
+ state.completedSessions.find(s => s.id === sessionId);
2365
+
2366
+ if (!session) {
2367
+ return { success: false, error: `Session not found: ${sessionId}` };
2368
+ }
2369
+
2370
+ return {
2371
+ success: true,
2372
+ sessionId,
2373
+ artifacts: session.artifacts ?? [],
2374
+ count: (session.artifacts ?? []).length
2375
+ };
2376
+ }