@girardmedia/bootspring 1.2.0 → 2.0.3

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 (253) hide show
  1. package/README.md +107 -14
  2. package/bin/bootspring.js +166 -27
  3. package/cli/agent.js +189 -17
  4. package/cli/analyze.js +499 -0
  5. package/cli/audit.js +557 -0
  6. package/cli/auth.js +495 -38
  7. package/cli/billing.js +302 -0
  8. package/cli/build.js +695 -0
  9. package/cli/business.js +109 -26
  10. package/cli/checkpoint-utils.js +168 -0
  11. package/cli/checkpoint.js +639 -0
  12. package/cli/cloud-sync.js +447 -0
  13. package/cli/content.js +198 -0
  14. package/cli/context.js +1 -1
  15. package/cli/deploy.js +543 -0
  16. package/cli/fundraise.js +112 -50
  17. package/cli/github-cmd.js +435 -0
  18. package/cli/health.js +477 -0
  19. package/cli/init.js +84 -13
  20. package/cli/legal.js +107 -95
  21. package/cli/log.js +2 -2
  22. package/cli/loop.js +976 -73
  23. package/cli/manager.js +711 -0
  24. package/cli/metrics.js +480 -0
  25. package/cli/monitor.js +812 -0
  26. package/cli/onboard.js +521 -0
  27. package/cli/orchestrator.js +12 -24
  28. package/cli/prd.js +594 -0
  29. package/cli/preseed-start.js +1483 -0
  30. package/cli/preseed.js +2302 -0
  31. package/cli/project.js +436 -0
  32. package/cli/quality.js +233 -0
  33. package/cli/security.js +913 -0
  34. package/cli/seed.js +1441 -5
  35. package/cli/skill.js +273 -211
  36. package/cli/suggest.js +989 -0
  37. package/cli/switch.js +453 -0
  38. package/cli/visualize.js +527 -0
  39. package/cli/watch.js +769 -0
  40. package/cli/workspace.js +607 -0
  41. package/core/analyze-workflow.js +1134 -0
  42. package/core/api-client.js +535 -22
  43. package/core/audit-workflow.js +1350 -0
  44. package/core/build-orchestrator.js +480 -0
  45. package/core/build-state.js +577 -0
  46. package/core/checkpoint-engine.js +408 -0
  47. package/core/config.js +1109 -26
  48. package/core/context-loader.js +21 -1
  49. package/core/deploy-workflow.js +836 -0
  50. package/core/entitlements.js +93 -22
  51. package/core/github-sync.js +610 -0
  52. package/core/index.js +8 -1
  53. package/core/ingest.js +1111 -0
  54. package/core/metrics-engine.js +768 -0
  55. package/core/onboard-workflow.js +1007 -0
  56. package/core/preseed-workflow.js +934 -0
  57. package/core/preseed.js +1617 -0
  58. package/core/project-context.js +325 -0
  59. package/core/project-state.js +694 -0
  60. package/core/r2-sync.js +583 -0
  61. package/core/scaffold.js +525 -7
  62. package/core/session.js +258 -0
  63. package/core/task-extractor.js +758 -0
  64. package/core/telemetry.js +28 -6
  65. package/core/tier-enforcement.js +737 -0
  66. package/core/utils.js +38 -14
  67. package/generators/questionnaire.js +15 -12
  68. package/generators/sections/ai.js +7 -7
  69. package/generators/sections/content.js +300 -0
  70. package/generators/sections/index.js +3 -0
  71. package/generators/sections/plugins.js +7 -6
  72. package/generators/templates/build-planning.template.js +596 -0
  73. package/generators/templates/content.template.js +819 -0
  74. package/generators/templates/index.js +2 -1
  75. package/hooks/git-autopilot.js +1250 -0
  76. package/hooks/index.js +9 -0
  77. package/intelligence/agent-collab.js +2057 -0
  78. package/intelligence/auto-suggest.js +634 -0
  79. package/intelligence/content-gen.js +1589 -0
  80. package/intelligence/cross-project.js +1647 -0
  81. package/intelligence/index.js +184 -0
  82. package/intelligence/learning/insights.json +517 -7
  83. package/intelligence/learning/pattern-learner.js +1008 -14
  84. package/intelligence/memory/decision-tracker.js +1431 -31
  85. package/intelligence/memory/decisions.jsonl +0 -0
  86. package/intelligence/orchestrator.js +2896 -1
  87. package/intelligence/prd.js +92 -1
  88. package/intelligence/recommendation-weights.json +14 -2
  89. package/intelligence/recommendations.js +463 -9
  90. package/intelligence/workflow-composer.js +1451 -0
  91. package/marketplace/index.d.ts +324 -0
  92. package/marketplace/index.js +1921 -0
  93. package/mcp/contracts/mcp-contract.v1.json +342 -4
  94. package/mcp/registry.js +680 -3
  95. package/mcp/response-formatter.js +23 -0
  96. package/mcp/tools/assist-tool.js +78 -4
  97. package/mcp/tools/autopilot-tool.js +408 -0
  98. package/mcp/tools/content-tool.js +571 -0
  99. package/mcp/tools/dashboard-tool.js +251 -5
  100. package/mcp/tools/mvp-tool.js +344 -0
  101. package/mcp/tools/plugin-tool.js +23 -1
  102. package/mcp/tools/prd-tool.js +579 -0
  103. package/mcp/tools/seed-tool.js +447 -0
  104. package/mcp/tools/skill-tool.js +43 -14
  105. package/mcp/tools/suggest-tool.js +147 -0
  106. package/package.json +15 -6
  107. package/agents/README.md +0 -93
  108. package/agents/ai-integration-expert/context.md +0 -386
  109. package/agents/api-expert/context.md +0 -416
  110. package/agents/architecture-expert/context.md +0 -454
  111. package/agents/auth-expert/context.md +0 -399
  112. package/agents/backend-expert/context.md +0 -483
  113. package/agents/business-strategy-expert/context.md +0 -180
  114. package/agents/code-review-expert/context.md +0 -365
  115. package/agents/competitive-analysis-expert/context.md +0 -239
  116. package/agents/data-modeling-expert/context.md +0 -352
  117. package/agents/database-expert/context.md +0 -250
  118. package/agents/devops-expert/context.md +0 -446
  119. package/agents/email-expert/context.md +0 -379
  120. package/agents/financial-expert/context.md +0 -213
  121. package/agents/frontend-expert/context.md +0 -364
  122. package/agents/fundraising-expert/context.md +0 -257
  123. package/agents/growth-expert/context.md +0 -249
  124. package/agents/index.js +0 -140
  125. package/agents/investor-relations-expert/context.md +0 -266
  126. package/agents/legal-expert/context.md +0 -284
  127. package/agents/marketing-expert/context.md +0 -236
  128. package/agents/monitoring-expert/context.md +0 -362
  129. package/agents/operations-expert/context.md +0 -279
  130. package/agents/partnerships-expert/context.md +0 -286
  131. package/agents/payment-expert/context.md +0 -340
  132. package/agents/performance-expert/context.md +0 -377
  133. package/agents/private-equity-expert/context.md +0 -246
  134. package/agents/railway-expert/context.md +0 -284
  135. package/agents/research-expert/context.md +0 -245
  136. package/agents/sales-expert/context.md +0 -241
  137. package/agents/security-expert/context.md +0 -343
  138. package/agents/testing-expert/context.md +0 -414
  139. package/agents/ui-ux-expert/context.md +0 -448
  140. package/agents/vercel-expert/context.md +0 -426
  141. package/skills/index.js +0 -787
  142. package/skills/patterns/README.md +0 -163
  143. package/skills/patterns/ai/agents.md +0 -281
  144. package/skills/patterns/ai/claude.md +0 -138
  145. package/skills/patterns/ai/embeddings.md +0 -150
  146. package/skills/patterns/ai/rag.md +0 -266
  147. package/skills/patterns/ai/streaming.md +0 -170
  148. package/skills/patterns/ai/structured-output.md +0 -162
  149. package/skills/patterns/ai/tools.md +0 -154
  150. package/skills/patterns/analytics/tracking.md +0 -220
  151. package/skills/patterns/api/errors.md +0 -296
  152. package/skills/patterns/api/graphql.md +0 -440
  153. package/skills/patterns/api/middleware.md +0 -279
  154. package/skills/patterns/api/openapi.md +0 -285
  155. package/skills/patterns/api/rate-limiting.md +0 -231
  156. package/skills/patterns/api/route-handler.md +0 -217
  157. package/skills/patterns/api/server-action.md +0 -249
  158. package/skills/patterns/api/versioning.md +0 -443
  159. package/skills/patterns/api/webhooks.md +0 -247
  160. package/skills/patterns/auth/clerk.md +0 -132
  161. package/skills/patterns/auth/mfa.md +0 -313
  162. package/skills/patterns/auth/nextauth.md +0 -140
  163. package/skills/patterns/auth/oauth.md +0 -237
  164. package/skills/patterns/auth/rbac.md +0 -152
  165. package/skills/patterns/auth/session-management.md +0 -367
  166. package/skills/patterns/auth/session.md +0 -120
  167. package/skills/patterns/database/audit.md +0 -177
  168. package/skills/patterns/database/migrations.md +0 -177
  169. package/skills/patterns/database/pagination.md +0 -230
  170. package/skills/patterns/database/pooling.md +0 -357
  171. package/skills/patterns/database/prisma.md +0 -180
  172. package/skills/patterns/database/relations.md +0 -187
  173. package/skills/patterns/database/seeding.md +0 -246
  174. package/skills/patterns/database/soft-delete.md +0 -153
  175. package/skills/patterns/database/transactions.md +0 -162
  176. package/skills/patterns/deployment/ci-cd.md +0 -231
  177. package/skills/patterns/deployment/docker.md +0 -188
  178. package/skills/patterns/deployment/monitoring.md +0 -387
  179. package/skills/patterns/deployment/vercel.md +0 -160
  180. package/skills/patterns/email/resend.md +0 -143
  181. package/skills/patterns/email/templates.md +0 -245
  182. package/skills/patterns/email/transactional.md +0 -503
  183. package/skills/patterns/email/verification.md +0 -176
  184. package/skills/patterns/files/download.md +0 -243
  185. package/skills/patterns/files/upload.md +0 -239
  186. package/skills/patterns/i18n/nextintl.md +0 -188
  187. package/skills/patterns/logging/structured.md +0 -292
  188. package/skills/patterns/notifications/email-queue.md +0 -248
  189. package/skills/patterns/notifications/push.md +0 -279
  190. package/skills/patterns/payments/checkout.md +0 -303
  191. package/skills/patterns/payments/invoices.md +0 -287
  192. package/skills/patterns/payments/portal.md +0 -245
  193. package/skills/patterns/payments/stripe.md +0 -272
  194. package/skills/patterns/payments/subscriptions.md +0 -300
  195. package/skills/patterns/payments/usage.md +0 -279
  196. package/skills/patterns/performance/caching.md +0 -276
  197. package/skills/patterns/performance/code-splitting.md +0 -233
  198. package/skills/patterns/performance/edge.md +0 -254
  199. package/skills/patterns/performance/isr.md +0 -266
  200. package/skills/patterns/performance/lazy-loading.md +0 -281
  201. package/skills/patterns/realtime/sse.md +0 -327
  202. package/skills/patterns/realtime/websockets.md +0 -336
  203. package/skills/patterns/search/filtering.md +0 -329
  204. package/skills/patterns/search/fulltext.md +0 -260
  205. package/skills/patterns/security/audit-logging.md +0 -444
  206. package/skills/patterns/security/csrf.md +0 -234
  207. package/skills/patterns/security/headers.md +0 -252
  208. package/skills/patterns/security/sanitization.md +0 -258
  209. package/skills/patterns/security/secrets.md +0 -261
  210. package/skills/patterns/security/validation.md +0 -268
  211. package/skills/patterns/security/xss.md +0 -229
  212. package/skills/patterns/seo/metadata.md +0 -252
  213. package/skills/patterns/state/context.md +0 -349
  214. package/skills/patterns/state/react-query.md +0 -313
  215. package/skills/patterns/state/url-state.md +0 -482
  216. package/skills/patterns/state/zustand.md +0 -262
  217. package/skills/patterns/testing/api.md +0 -259
  218. package/skills/patterns/testing/component.md +0 -233
  219. package/skills/patterns/testing/coverage.md +0 -207
  220. package/skills/patterns/testing/fixtures.md +0 -225
  221. package/skills/patterns/testing/integration.md +0 -436
  222. package/skills/patterns/testing/mocking.md +0 -177
  223. package/skills/patterns/testing/playwright.md +0 -162
  224. package/skills/patterns/testing/snapshot.md +0 -175
  225. package/skills/patterns/testing/vitest.md +0 -307
  226. package/skills/patterns/ui/accordions.md +0 -395
  227. package/skills/patterns/ui/cards.md +0 -299
  228. package/skills/patterns/ui/dropdowns.md +0 -476
  229. package/skills/patterns/ui/empty-states.md +0 -320
  230. package/skills/patterns/ui/forms.md +0 -405
  231. package/skills/patterns/ui/inputs.md +0 -319
  232. package/skills/patterns/ui/layouts.md +0 -282
  233. package/skills/patterns/ui/loading.md +0 -291
  234. package/skills/patterns/ui/modals.md +0 -338
  235. package/skills/patterns/ui/navigation.md +0 -374
  236. package/skills/patterns/ui/tables.md +0 -407
  237. package/skills/patterns/ui/toasts.md +0 -300
  238. package/skills/patterns/ui/tooltips.md +0 -396
  239. package/skills/patterns/utils/dates.md +0 -435
  240. package/skills/patterns/utils/errors.md +0 -451
  241. package/skills/patterns/utils/formatting.md +0 -345
  242. package/skills/patterns/utils/validation.md +0 -434
  243. package/templates/bootspring.config.js +0 -83
  244. package/templates/business/business-model-canvas.md +0 -246
  245. package/templates/business/business-plan.md +0 -266
  246. package/templates/business/competitive-analysis.md +0 -312
  247. package/templates/fundraising/data-room-checklist.md +0 -300
  248. package/templates/fundraising/investor-research.md +0 -243
  249. package/templates/fundraising/pitch-deck-outline.md +0 -253
  250. package/templates/legal/gdpr-checklist.md +0 -339
  251. package/templates/legal/privacy-policy.md +0 -285
  252. package/templates/legal/terms-of-service.md +0 -222
  253. package/templates/mcp.json +0 -9
@@ -0,0 +1,2057 @@
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
+ * Features:
9
+ * - Multi-agent task delegation with priority queues
10
+ * - Handoff protocol with context preservation
11
+ * - Primary agent leadership with supporting agent coordination
12
+ * - Dependency-aware task scheduling
13
+ * - Agent capability matching
14
+ * - Real-time collaboration state tracking
15
+ *
16
+ * @package bootspring
17
+ * @module intelligence/agent-collab
18
+ */
19
+
20
+ const fs = require('fs');
21
+ const path = require('path');
22
+ const telemetry = require('../core/telemetry');
23
+
24
+ /**
25
+ * Task priority levels
26
+ */
27
+ const TASK_PRIORITY = {
28
+ CRITICAL: 'critical',
29
+ HIGH: 'high',
30
+ MEDIUM: 'medium',
31
+ LOW: 'low'
32
+ };
33
+
34
+ /**
35
+ * Task status values
36
+ */
37
+ const TASK_STATUS = {
38
+ PENDING: 'pending',
39
+ IN_PROGRESS: 'in_progress',
40
+ BLOCKED: 'blocked',
41
+ COMPLETE: 'complete',
42
+ FAILED: 'failed'
43
+ };
44
+
45
+ /**
46
+ * Handoff status values
47
+ */
48
+ const HANDOFF_STATUS = {
49
+ PENDING: 'pending',
50
+ ACCEPTED: 'accepted',
51
+ REJECTED: 'rejected',
52
+ COMPLETED: 'completed'
53
+ };
54
+
55
+ /**
56
+ * Agent roles in collaboration
57
+ */
58
+ const AGENT_ROLE = {
59
+ PRIMARY: 'primary',
60
+ SUPPORTING: 'supporting',
61
+ SPECIALIST: 'specialist'
62
+ };
63
+
64
+ /**
65
+ * Get bootspring directory
66
+ */
67
+ function getBootspringDir() {
68
+ let bootspringDir = __dirname;
69
+ while (!fs.existsSync(path.join(bootspringDir, 'package.json')) && bootspringDir !== '/') {
70
+ bootspringDir = path.dirname(bootspringDir);
71
+ }
72
+ return bootspringDir;
73
+ }
74
+
75
+ /**
76
+ * Collaboration state file path
77
+ */
78
+ function getCollabPath() {
79
+ const projectRoot = process.cwd();
80
+ return path.join(projectRoot, '.bootspring', 'collab-state.json');
81
+ }
82
+
83
+ /**
84
+ * Emit telemetry event
85
+ */
86
+ function emitCollabTelemetry(event, payload = {}) {
87
+ try {
88
+ telemetry.emitEvent(event, payload, { projectRoot: process.cwd() });
89
+ } catch {
90
+ // Telemetry should not block collaboration execution
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Load collaboration state
96
+ */
97
+ function loadCollabState() {
98
+ const statePath = getCollabPath();
99
+ if (fs.existsSync(statePath)) {
100
+ try {
101
+ return JSON.parse(fs.readFileSync(statePath, 'utf8'));
102
+ } catch {
103
+ return createDefaultCollabState();
104
+ }
105
+ }
106
+ return createDefaultCollabState();
107
+ }
108
+
109
+ /**
110
+ * Create default collaboration state
111
+ */
112
+ function createDefaultCollabState() {
113
+ return {
114
+ activeSessions: {},
115
+ pendingHandoffs: [],
116
+ completedSessions: [],
117
+ taskQueues: {}, // Per-agent task queues
118
+ agentStates: {}, // Current state of each agent
119
+ blockedTasks: [], // Tasks waiting on dependencies
120
+ history: [],
121
+ metrics: {
122
+ totalSessions: 0,
123
+ successfulSessions: 0,
124
+ totalHandoffs: 0,
125
+ totalSubtasks: 0,
126
+ avgSessionDuration: 0
127
+ }
128
+ };
129
+ }
130
+
131
+ /**
132
+ * Save collaboration state
133
+ */
134
+ function saveCollabState(state) {
135
+ const statePath = getCollabPath();
136
+ const dir = path.dirname(statePath);
137
+ if (!fs.existsSync(dir)) {
138
+ fs.mkdirSync(dir, { recursive: true });
139
+ }
140
+ state.lastUpdated = new Date().toISOString();
141
+ fs.writeFileSync(statePath, JSON.stringify(state, null, 2));
142
+ }
143
+
144
+ /**
145
+ * Generate unique session ID
146
+ */
147
+ function generateSessionId() {
148
+ return `collab-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
149
+ }
150
+
151
+ /**
152
+ * Get agent registry for capability matching
153
+ * Thin client: agents module may not exist locally
154
+ * Agent contexts are now served from the API
155
+ */
156
+ function getAgentRegistry() {
157
+ try {
158
+ const agentsModule = require('../agents');
159
+ return agentsModule.AGENTS || {};
160
+ } catch {
161
+ // Thin client fallback - use cli/agent.js AGENTS registry
162
+ try {
163
+ const agentCli = require('../cli/agent');
164
+ return agentCli.AGENTS || {};
165
+ } catch {
166
+ return {};
167
+ }
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Find best agent for a task based on expertise
173
+ * @param {string} taskDescription - Task description
174
+ * @param {Array<string>} availableAgents - List of available agents
175
+ */
176
+ function findBestAgentForTask(taskDescription, availableAgents = []) {
177
+ const registry = getAgentRegistry();
178
+ const descLower = taskDescription.toLowerCase();
179
+ const scores = [];
180
+
181
+ const searchAgents = availableAgents.length > 0
182
+ ? availableAgents
183
+ : Object.keys(registry);
184
+
185
+ for (const agentId of searchAgents) {
186
+ const agent = registry[agentId];
187
+ if (!agent) continue;
188
+
189
+ let score = 0;
190
+ const expertise = agent.expertise || [];
191
+
192
+ for (const skill of expertise) {
193
+ if (descLower.includes(skill.toLowerCase())) {
194
+ score += 10;
195
+ }
196
+ }
197
+
198
+ // Check description match
199
+ if (agent.description && descLower.includes(agent.description.toLowerCase())) {
200
+ score += 5;
201
+ }
202
+
203
+ if (score > 0) {
204
+ scores.push({ agentId, agent, score });
205
+ }
206
+ }
207
+
208
+ // Sort by score descending
209
+ scores.sort((a, b) => b.score - a.score);
210
+
211
+ return scores.length > 0 ? scores[0] : null;
212
+ }
213
+
214
+ /**
215
+ * Start a new collaboration session
216
+ * @param {object} options - Session options
217
+ * @param {string} options.task - Main task description
218
+ * @param {string} options.primaryAgent - Primary agent leading the collaboration
219
+ * @param {Array<string>} options.supportingAgents - Supporting agents
220
+ * @param {object} options.context - Initial context for the session
221
+ * @param {string} options.parentSessionId - Parent session if this is a nested collaboration
222
+ */
223
+ function startSession(options) {
224
+ const {
225
+ task,
226
+ primaryAgent,
227
+ supportingAgents = [],
228
+ context = {},
229
+ parentSessionId = null
230
+ } = options;
231
+
232
+ if (!task || !primaryAgent) {
233
+ return { success: false, error: 'Task and primaryAgent are required' };
234
+ }
235
+
236
+ const state = loadCollabState();
237
+ const sessionId = generateSessionId();
238
+
239
+ // Initialize agent states for this session
240
+ const agentParticipants = {
241
+ [primaryAgent]: {
242
+ role: AGENT_ROLE.PRIMARY,
243
+ joinedAt: new Date().toISOString(),
244
+ tasksAssigned: 0,
245
+ tasksCompleted: 0,
246
+ status: 'active'
247
+ }
248
+ };
249
+
250
+ supportingAgents.forEach(agent => {
251
+ agentParticipants[agent] = {
252
+ role: AGENT_ROLE.SUPPORTING,
253
+ joinedAt: new Date().toISOString(),
254
+ tasksAssigned: 0,
255
+ tasksCompleted: 0,
256
+ status: 'standby'
257
+ };
258
+ });
259
+
260
+ const session = {
261
+ id: sessionId,
262
+ task,
263
+ primaryAgent,
264
+ supportingAgents,
265
+ agentParticipants,
266
+ status: 'active',
267
+ createdAt: new Date().toISOString(),
268
+ parentSessionId,
269
+ subtasks: [],
270
+ taskQueue: [], // Priority queue for tasks
271
+ handoffs: [],
272
+ results: {},
273
+ artifacts: [], // Shared artifacts between agents
274
+ context: {
275
+ initial: context,
276
+ current: { ...context },
277
+ history: []
278
+ },
279
+ checkpoints: [], // Session checkpoints for recovery
280
+ metrics: {
281
+ startTime: Date.now(),
282
+ handoffCount: 0,
283
+ delegationCount: 0,
284
+ blockedTime: 0
285
+ }
286
+ };
287
+
288
+ state.activeSessions[sessionId] = session;
289
+ state.metrics.totalSessions++;
290
+
291
+ // Initialize task queue for primary agent
292
+ if (!state.taskQueues[primaryAgent]) {
293
+ state.taskQueues[primaryAgent] = [];
294
+ }
295
+
296
+ state.history.push({
297
+ action: 'session_started',
298
+ sessionId,
299
+ primaryAgent,
300
+ supportingAgents,
301
+ parentSessionId,
302
+ timestamp: new Date().toISOString()
303
+ });
304
+
305
+ saveCollabState(state);
306
+
307
+ emitCollabTelemetry('collab_session_started', {
308
+ sessionId,
309
+ primaryAgent,
310
+ supportingAgentCount: supportingAgents.length
311
+ });
312
+
313
+ return {
314
+ success: true,
315
+ sessionId,
316
+ session,
317
+ message: `Collaboration session started with ${primaryAgent} as primary agent`
318
+ };
319
+ }
320
+
321
+ /**
322
+ * Create a subtask and delegate to an agent
323
+ * @param {string} sessionId - Collaboration session ID
324
+ * @param {object} subtask - Subtask definition
325
+ */
326
+ function delegateTask(sessionId, subtask) {
327
+ const state = loadCollabState();
328
+ const session = state.activeSessions[sessionId];
329
+
330
+ if (!session) {
331
+ return { success: false, error: `Session not found: ${sessionId}` };
332
+ }
333
+
334
+ const {
335
+ title,
336
+ description,
337
+ delegatedTo,
338
+ delegatedBy = session.primaryAgent,
339
+ context = {},
340
+ priority = TASK_PRIORITY.MEDIUM,
341
+ dependencies = [],
342
+ expectedDuration = null,
343
+ acceptanceCriteria = [],
344
+ artifacts = []
345
+ } = subtask;
346
+
347
+ if (!title || !delegatedTo) {
348
+ return { success: false, error: 'Title and delegatedTo are required' };
349
+ }
350
+
351
+ const subtaskId = `subtask-${session.subtasks.length + 1}-${Date.now()}`;
352
+
353
+ // Check if dependencies are met
354
+ const unmetDependencies = dependencies.filter(depId => {
355
+ const dep = session.subtasks.find(t => t.id === depId);
356
+ return !dep || dep.status !== TASK_STATUS.COMPLETE;
357
+ });
358
+
359
+ const newSubtask = {
360
+ id: subtaskId,
361
+ title,
362
+ description,
363
+ delegatedTo,
364
+ delegatedBy,
365
+ context: {
366
+ ...session.context.current,
367
+ ...context
368
+ },
369
+ priority,
370
+ dependencies,
371
+ unmetDependencies,
372
+ expectedDuration,
373
+ acceptanceCriteria,
374
+ artifacts,
375
+ status: unmetDependencies.length > 0 ? TASK_STATUS.BLOCKED : TASK_STATUS.PENDING,
376
+ createdAt: new Date().toISOString(),
377
+ startedAt: null,
378
+ completedAt: null,
379
+ result: null,
380
+ feedback: null,
381
+ retryCount: 0
382
+ };
383
+
384
+ session.subtasks.push(newSubtask);
385
+ session.metrics.delegationCount++;
386
+
387
+ // Update agent participant stats
388
+ if (session.agentParticipants[delegatedTo]) {
389
+ session.agentParticipants[delegatedTo].tasksAssigned++;
390
+ } else {
391
+ // Add new specialist agent
392
+ session.agentParticipants[delegatedTo] = {
393
+ role: AGENT_ROLE.SPECIALIST,
394
+ joinedAt: new Date().toISOString(),
395
+ tasksAssigned: 1,
396
+ tasksCompleted: 0,
397
+ status: 'active'
398
+ };
399
+ }
400
+
401
+ // Add to priority queue
402
+ insertIntoTaskQueue(session.taskQueue, newSubtask);
403
+
404
+ // Add to agent's global task queue
405
+ if (!state.taskQueues[delegatedTo]) {
406
+ state.taskQueues[delegatedTo] = [];
407
+ }
408
+ state.taskQueues[delegatedTo].push({
409
+ sessionId,
410
+ subtaskId,
411
+ priority,
412
+ createdAt: newSubtask.createdAt
413
+ });
414
+
415
+ // Track blocked tasks
416
+ if (newSubtask.status === TASK_STATUS.BLOCKED) {
417
+ state.blockedTasks.push({
418
+ sessionId,
419
+ subtaskId,
420
+ dependencies: unmetDependencies
421
+ });
422
+ }
423
+
424
+ state.metrics.totalSubtasks++;
425
+
426
+ state.history.push({
427
+ action: 'task_delegated',
428
+ sessionId,
429
+ subtaskId,
430
+ from: delegatedBy,
431
+ to: delegatedTo,
432
+ priority,
433
+ hasDepedencies: dependencies.length > 0,
434
+ timestamp: new Date().toISOString()
435
+ });
436
+
437
+ saveCollabState(state);
438
+
439
+ emitCollabTelemetry('task_delegated', {
440
+ sessionId,
441
+ subtaskId,
442
+ delegatedTo,
443
+ priority
444
+ });
445
+
446
+ return {
447
+ success: true,
448
+ sessionId,
449
+ subtaskId,
450
+ subtask: newSubtask,
451
+ isBlocked: newSubtask.status === TASK_STATUS.BLOCKED,
452
+ message: newSubtask.status === TASK_STATUS.BLOCKED
453
+ ? `Task "${title}" delegated to ${delegatedTo} (blocked on dependencies)`
454
+ : `Task "${title}" delegated to ${delegatedTo}`
455
+ };
456
+ }
457
+
458
+ /**
459
+ * Insert task into priority queue maintaining priority order
460
+ */
461
+ function insertIntoTaskQueue(queue, task) {
462
+ const priorityOrder = {
463
+ [TASK_PRIORITY.CRITICAL]: 0,
464
+ [TASK_PRIORITY.HIGH]: 1,
465
+ [TASK_PRIORITY.MEDIUM]: 2,
466
+ [TASK_PRIORITY.LOW]: 3
467
+ };
468
+
469
+ const taskPriority = priorityOrder[task.priority] ?? 2;
470
+ let inserted = false;
471
+
472
+ for (let i = 0; i < queue.length; i++) {
473
+ const queuePriority = priorityOrder[queue[i].priority] ?? 2;
474
+ if (taskPriority < queuePriority) {
475
+ queue.splice(i, 0, task);
476
+ inserted = true;
477
+ break;
478
+ }
479
+ }
480
+
481
+ if (!inserted) {
482
+ queue.push(task);
483
+ }
484
+ }
485
+
486
+ /**
487
+ * Start working on a subtask
488
+ * @param {string} sessionId - Collaboration session ID
489
+ * @param {string} subtaskId - Subtask ID
490
+ */
491
+ function startSubtask(sessionId, subtaskId) {
492
+ const state = loadCollabState();
493
+ const session = state.activeSessions[sessionId];
494
+
495
+ if (!session) {
496
+ return { success: false, error: `Session not found: ${sessionId}` };
497
+ }
498
+
499
+ const subtask = session.subtasks.find(s => s.id === subtaskId);
500
+ if (!subtask) {
501
+ return { success: false, error: `Subtask not found: ${subtaskId}` };
502
+ }
503
+
504
+ if (subtask.status === TASK_STATUS.BLOCKED) {
505
+ return {
506
+ success: false,
507
+ error: 'Subtask is blocked on dependencies',
508
+ unmetDependencies: subtask.unmetDependencies
509
+ };
510
+ }
511
+
512
+ if (subtask.status === TASK_STATUS.IN_PROGRESS) {
513
+ return { success: false, error: 'Subtask already in progress' };
514
+ }
515
+
516
+ subtask.status = TASK_STATUS.IN_PROGRESS;
517
+ subtask.startedAt = new Date().toISOString();
518
+
519
+ // Update agent status
520
+ if (session.agentParticipants[subtask.delegatedTo]) {
521
+ session.agentParticipants[subtask.delegatedTo].status = 'working';
522
+ }
523
+
524
+ state.history.push({
525
+ action: 'subtask_started',
526
+ sessionId,
527
+ subtaskId,
528
+ agent: subtask.delegatedTo,
529
+ timestamp: new Date().toISOString()
530
+ });
531
+
532
+ saveCollabState(state);
533
+
534
+ return {
535
+ success: true,
536
+ sessionId,
537
+ subtaskId,
538
+ subtask,
539
+ message: `Started working on "${subtask.title}"`
540
+ };
541
+ }
542
+
543
+ /**
544
+ * Update subtask progress
545
+ * @param {string} sessionId - Collaboration session ID
546
+ * @param {string} subtaskId - Subtask ID
547
+ * @param {object} update - Progress update
548
+ */
549
+ function updateSubtaskProgress(sessionId, subtaskId, update) {
550
+ const state = loadCollabState();
551
+ const session = state.activeSessions[sessionId];
552
+
553
+ if (!session) {
554
+ return { success: false, error: `Session not found: ${sessionId}` };
555
+ }
556
+
557
+ const subtask = session.subtasks.find(s => s.id === subtaskId);
558
+ if (!subtask) {
559
+ return { success: false, error: `Subtask not found: ${subtaskId}` };
560
+ }
561
+
562
+ const { progress, notes, artifacts, blockedReason } = update;
563
+
564
+ if (progress !== undefined) {
565
+ subtask.progress = progress;
566
+ }
567
+
568
+ if (notes) {
569
+ subtask.progressNotes = subtask.progressNotes || [];
570
+ subtask.progressNotes.push({
571
+ note: notes,
572
+ timestamp: new Date().toISOString()
573
+ });
574
+ }
575
+
576
+ if (artifacts && artifacts.length > 0) {
577
+ subtask.artifacts = [...(subtask.artifacts || []), ...artifacts];
578
+ // Also add to session artifacts
579
+ session.artifacts.push(...artifacts.map(a => ({
580
+ ...a,
581
+ subtaskId,
582
+ addedAt: new Date().toISOString()
583
+ })));
584
+ }
585
+
586
+ if (blockedReason) {
587
+ subtask.status = TASK_STATUS.BLOCKED;
588
+ subtask.blockedReason = blockedReason;
589
+ state.blockedTasks.push({
590
+ sessionId,
591
+ subtaskId,
592
+ reason: blockedReason,
593
+ blockedAt: new Date().toISOString()
594
+ });
595
+ }
596
+
597
+ saveCollabState(state);
598
+
599
+ return {
600
+ success: true,
601
+ sessionId,
602
+ subtaskId,
603
+ subtask,
604
+ message: 'Subtask progress updated'
605
+ };
606
+ }
607
+
608
+ /**
609
+ * Complete a subtask
610
+ * @param {string} sessionId - Collaboration session ID
611
+ * @param {string} subtaskId - Subtask ID
612
+ * @param {object} result - Subtask result
613
+ */
614
+ function completeSubtask(sessionId, subtaskId, result) {
615
+ const state = loadCollabState();
616
+ const session = state.activeSessions[sessionId];
617
+
618
+ if (!session) {
619
+ return { success: false, error: `Session not found: ${sessionId}` };
620
+ }
621
+
622
+ const subtask = session.subtasks.find(s => s.id === subtaskId);
623
+ if (!subtask) {
624
+ return { success: false, error: `Subtask not found: ${subtaskId}` };
625
+ }
626
+
627
+ const {
628
+ output,
629
+ artifacts = [],
630
+ contextUpdates = {},
631
+ learnings = [],
632
+ success = true
633
+ } = result;
634
+
635
+ subtask.status = success ? TASK_STATUS.COMPLETE : TASK_STATUS.FAILED;
636
+ subtask.completedAt = new Date().toISOString();
637
+ subtask.result = {
638
+ output,
639
+ success,
640
+ duration: subtask.startedAt
641
+ ? Date.now() - new Date(subtask.startedAt).getTime()
642
+ : null
643
+ };
644
+
645
+ // Add artifacts to session
646
+ if (artifacts.length > 0) {
647
+ subtask.artifacts = [...(subtask.artifacts || []), ...artifacts];
648
+ session.artifacts.push(...artifacts.map(a => ({
649
+ ...a,
650
+ subtaskId,
651
+ addedAt: new Date().toISOString()
652
+ })));
653
+ }
654
+
655
+ // Update session context with learnings from this subtask
656
+ if (Object.keys(contextUpdates).length > 0) {
657
+ session.context.history.push({
658
+ update: contextUpdates,
659
+ source: subtaskId,
660
+ timestamp: new Date().toISOString()
661
+ });
662
+ session.context.current = {
663
+ ...session.context.current,
664
+ ...contextUpdates
665
+ };
666
+ }
667
+
668
+ // Store learnings
669
+ if (learnings.length > 0) {
670
+ session.learnings = session.learnings || [];
671
+ session.learnings.push(...learnings.map(l => ({
672
+ ...l,
673
+ subtaskId,
674
+ agent: subtask.delegatedTo,
675
+ timestamp: new Date().toISOString()
676
+ })));
677
+ }
678
+
679
+ session.results[subtaskId] = result;
680
+
681
+ // Update agent participant stats
682
+ if (session.agentParticipants[subtask.delegatedTo]) {
683
+ session.agentParticipants[subtask.delegatedTo].tasksCompleted++;
684
+ session.agentParticipants[subtask.delegatedTo].status = 'standby';
685
+ }
686
+
687
+ // Remove from agent's task queue
688
+ if (state.taskQueues[subtask.delegatedTo]) {
689
+ state.taskQueues[subtask.delegatedTo] = state.taskQueues[subtask.delegatedTo]
690
+ .filter(t => !(t.sessionId === sessionId && t.subtaskId === subtaskId));
691
+ }
692
+
693
+ // Unblock dependent tasks
694
+ const unblockedTasks = [];
695
+ session.subtasks.forEach(t => {
696
+ if (t.status === TASK_STATUS.BLOCKED && t.dependencies.includes(subtaskId)) {
697
+ t.unmetDependencies = t.unmetDependencies.filter(d => d !== subtaskId);
698
+ if (t.unmetDependencies.length === 0) {
699
+ t.status = TASK_STATUS.PENDING;
700
+ unblockedTasks.push(t.id);
701
+ }
702
+ }
703
+ });
704
+
705
+ // Remove from blocked tasks list
706
+ state.blockedTasks = state.blockedTasks.filter(
707
+ bt => !(bt.sessionId === sessionId && bt.subtaskId === subtaskId)
708
+ );
709
+
710
+ state.history.push({
711
+ action: 'subtask_completed',
712
+ sessionId,
713
+ subtaskId,
714
+ agent: subtask.delegatedTo,
715
+ success,
716
+ unblockedTasks,
717
+ timestamp: new Date().toISOString()
718
+ });
719
+
720
+ saveCollabState(state);
721
+
722
+ emitCollabTelemetry('subtask_completed', {
723
+ sessionId,
724
+ subtaskId,
725
+ agent: subtask.delegatedTo,
726
+ success,
727
+ unblockedCount: unblockedTasks.length
728
+ });
729
+
730
+ // Check if all subtasks are complete
731
+ const allComplete = session.subtasks.every(
732
+ s => s.status === TASK_STATUS.COMPLETE || s.status === TASK_STATUS.FAILED
733
+ );
734
+ const allSuccessful = session.subtasks.every(s => s.status === TASK_STATUS.COMPLETE);
735
+
736
+ return {
737
+ success: true,
738
+ sessionId,
739
+ subtaskId,
740
+ taskSuccess: success,
741
+ allSubtasksComplete: allComplete,
742
+ allSubtasksSuccessful: allSuccessful,
743
+ unblockedTasks,
744
+ message: allComplete
745
+ ? allSuccessful
746
+ ? 'All subtasks complete. Ready to finalize session.'
747
+ : 'All subtasks processed with some failures.'
748
+ : `Subtask "${subtask.title}" completed`
749
+ };
750
+ }
751
+
752
+ /**
753
+ * Fail a subtask
754
+ * @param {string} sessionId - Collaboration session ID
755
+ * @param {string} subtaskId - Subtask ID
756
+ * @param {object} failure - Failure details
757
+ */
758
+ function failSubtask(sessionId, subtaskId, failure) {
759
+ const state = loadCollabState();
760
+ const session = state.activeSessions[sessionId];
761
+
762
+ if (!session) {
763
+ return { success: false, error: `Session not found: ${sessionId}` };
764
+ }
765
+
766
+ const subtask = session.subtasks.find(s => s.id === subtaskId);
767
+ if (!subtask) {
768
+ return { success: false, error: `Subtask not found: ${subtaskId}` };
769
+ }
770
+
771
+ const { reason, canRetry = false, suggestedAction = null } = failure;
772
+
773
+ subtask.status = TASK_STATUS.FAILED;
774
+ subtask.completedAt = new Date().toISOString();
775
+ subtask.failure = {
776
+ reason,
777
+ canRetry,
778
+ suggestedAction,
779
+ failedAt: new Date().toISOString()
780
+ };
781
+ subtask.retryCount++;
782
+
783
+ // Update agent status
784
+ if (session.agentParticipants[subtask.delegatedTo]) {
785
+ session.agentParticipants[subtask.delegatedTo].status = 'standby';
786
+ }
787
+
788
+ state.history.push({
789
+ action: 'subtask_failed',
790
+ sessionId,
791
+ subtaskId,
792
+ agent: subtask.delegatedTo,
793
+ reason,
794
+ canRetry,
795
+ timestamp: new Date().toISOString()
796
+ });
797
+
798
+ saveCollabState(state);
799
+
800
+ return {
801
+ success: true,
802
+ sessionId,
803
+ subtaskId,
804
+ canRetry,
805
+ suggestedAction,
806
+ message: `Subtask "${subtask.title}" failed: ${reason}`
807
+ };
808
+ }
809
+
810
+ /**
811
+ * Retry a failed subtask
812
+ * @param {string} sessionId - Collaboration session ID
813
+ * @param {string} subtaskId - Subtask ID
814
+ * @param {object} options - Retry options
815
+ */
816
+ function retrySubtask(sessionId, subtaskId, options = {}) {
817
+ const state = loadCollabState();
818
+ const session = state.activeSessions[sessionId];
819
+
820
+ if (!session) {
821
+ return { success: false, error: `Session not found: ${sessionId}` };
822
+ }
823
+
824
+ const subtask = session.subtasks.find(s => s.id === subtaskId);
825
+ if (!subtask) {
826
+ return { success: false, error: `Subtask not found: ${subtaskId}` };
827
+ }
828
+
829
+ if (subtask.status !== TASK_STATUS.FAILED) {
830
+ return { success: false, error: 'Can only retry failed subtasks' };
831
+ }
832
+
833
+ const { reassignTo, additionalContext = {} } = options;
834
+
835
+ // Reset subtask state
836
+ subtask.status = TASK_STATUS.PENDING;
837
+ subtask.completedAt = null;
838
+ subtask.startedAt = null;
839
+ subtask.result = null;
840
+
841
+ // Optionally reassign to different agent
842
+ if (reassignTo) {
843
+ subtask.delegatedTo = reassignTo;
844
+ subtask.reassignedFrom = subtask.delegatedTo;
845
+ }
846
+
847
+ // Add additional context
848
+ if (Object.keys(additionalContext).length > 0) {
849
+ subtask.context = {
850
+ ...subtask.context,
851
+ ...additionalContext,
852
+ previousAttemptFailed: true
853
+ };
854
+ }
855
+
856
+ // Re-add to task queue
857
+ insertIntoTaskQueue(session.taskQueue, subtask);
858
+
859
+ state.history.push({
860
+ action: 'subtask_retried',
861
+ sessionId,
862
+ subtaskId,
863
+ retryCount: subtask.retryCount,
864
+ reassignedTo: reassignTo || null,
865
+ timestamp: new Date().toISOString()
866
+ });
867
+
868
+ saveCollabState(state);
869
+
870
+ return {
871
+ success: true,
872
+ sessionId,
873
+ subtaskId,
874
+ retryCount: subtask.retryCount,
875
+ message: `Subtask "${subtask.title}" queued for retry`
876
+ };
877
+ }
878
+
879
+ /**
880
+ * Request handoff from one agent to another
881
+ * @param {string} sessionId - Collaboration session ID
882
+ * @param {object} handoff - Handoff details
883
+ */
884
+ function requestHandoff(sessionId, handoff) {
885
+ const state = loadCollabState();
886
+ const session = state.activeSessions[sessionId];
887
+
888
+ if (!session) {
889
+ return { success: false, error: `Session not found: ${sessionId}` };
890
+ }
891
+
892
+ const {
893
+ fromAgent,
894
+ toAgent,
895
+ reason,
896
+ context = {},
897
+ artifacts = [],
898
+ transferPrimaryRole = false,
899
+ subtaskId = null,
900
+ urgency = 'normal',
901
+ expectedDuration = null
902
+ } = handoff;
903
+
904
+ if (!fromAgent || !toAgent) {
905
+ return { success: false, error: 'fromAgent and toAgent are required' };
906
+ }
907
+
908
+ const handoffId = `handoff-${session.handoffs.length + 1}-${Date.now()}`;
909
+
910
+ // Collect context for handoff
911
+ const handoffContext = {
912
+ ...session.context.current,
913
+ ...context,
914
+ handoffReason: reason,
915
+ previousAgent: fromAgent,
916
+ sessionProgress: {
917
+ completedSubtasks: session.subtasks.filter(s => s.status === TASK_STATUS.COMPLETE).length,
918
+ totalSubtasks: session.subtasks.length,
919
+ artifacts: session.artifacts.length
920
+ }
921
+ };
922
+
923
+ // If transferring a specific subtask, include its context
924
+ let transferredSubtask = null;
925
+ if (subtaskId) {
926
+ transferredSubtask = session.subtasks.find(s => s.id === subtaskId);
927
+ if (transferredSubtask) {
928
+ handoffContext.subtask = {
929
+ id: transferredSubtask.id,
930
+ title: transferredSubtask.title,
931
+ description: transferredSubtask.description,
932
+ progress: transferredSubtask.progress,
933
+ progressNotes: transferredSubtask.progressNotes
934
+ };
935
+ }
936
+ }
937
+
938
+ const newHandoff = {
939
+ id: handoffId,
940
+ fromAgent,
941
+ toAgent,
942
+ reason,
943
+ context: handoffContext,
944
+ artifacts,
945
+ transferPrimaryRole,
946
+ subtaskId,
947
+ urgency,
948
+ expectedDuration,
949
+ status: HANDOFF_STATUS.PENDING,
950
+ requestedAt: new Date().toISOString(),
951
+ acceptedAt: null,
952
+ completedAt: null,
953
+ rejectionReason: null
954
+ };
955
+
956
+ session.handoffs.push(newHandoff);
957
+ session.metrics.handoffCount++;
958
+
959
+ state.pendingHandoffs.push({
960
+ sessionId,
961
+ handoffId,
962
+ ...newHandoff
963
+ });
964
+
965
+ state.metrics.totalHandoffs++;
966
+
967
+ state.history.push({
968
+ action: 'handoff_requested',
969
+ sessionId,
970
+ handoffId,
971
+ fromAgent,
972
+ toAgent,
973
+ transferPrimaryRole,
974
+ urgency,
975
+ timestamp: new Date().toISOString()
976
+ });
977
+
978
+ saveCollabState(state);
979
+
980
+ emitCollabTelemetry('handoff_requested', {
981
+ sessionId,
982
+ handoffId,
983
+ fromAgent,
984
+ toAgent,
985
+ transferPrimaryRole,
986
+ urgency
987
+ });
988
+
989
+ return {
990
+ success: true,
991
+ sessionId,
992
+ handoffId,
993
+ handoff: newHandoff,
994
+ message: transferPrimaryRole
995
+ ? `Primary role handoff requested from ${fromAgent} to ${toAgent}`
996
+ : `Handoff requested from ${fromAgent} to ${toAgent}`
997
+ };
998
+ }
999
+
1000
+ /**
1001
+ * Accept a handoff
1002
+ * @param {string} sessionId - Collaboration session ID
1003
+ * @param {string} handoffId - Handoff ID
1004
+ * @param {object} options - Accept options
1005
+ */
1006
+ function acceptHandoff(sessionId, handoffId, options = {}) {
1007
+ const state = loadCollabState();
1008
+ const session = state.activeSessions[sessionId];
1009
+
1010
+ if (!session) {
1011
+ return { success: false, error: `Session not found: ${sessionId}` };
1012
+ }
1013
+
1014
+ const handoff = session.handoffs.find(h => h.id === handoffId);
1015
+ if (!handoff) {
1016
+ return { success: false, error: `Handoff not found: ${handoffId}` };
1017
+ }
1018
+
1019
+ if (handoff.status !== HANDOFF_STATUS.PENDING) {
1020
+ return { success: false, error: `Handoff already ${handoff.status}` };
1021
+ }
1022
+
1023
+ const { acknowledgment = null, estimatedCompletion = null } = options;
1024
+
1025
+ handoff.status = HANDOFF_STATUS.ACCEPTED;
1026
+ handoff.acceptedAt = new Date().toISOString();
1027
+ handoff.acknowledgment = acknowledgment;
1028
+ handoff.estimatedCompletion = estimatedCompletion;
1029
+
1030
+ // Update primary agent if this was a primary role transfer
1031
+ const previousPrimary = session.primaryAgent;
1032
+ if (handoff.transferPrimaryRole) {
1033
+ session.primaryAgent = handoff.toAgent;
1034
+
1035
+ // Update agent roles
1036
+ if (session.agentParticipants[previousPrimary]) {
1037
+ session.agentParticipants[previousPrimary].role = AGENT_ROLE.SUPPORTING;
1038
+ }
1039
+ if (session.agentParticipants[handoff.toAgent]) {
1040
+ session.agentParticipants[handoff.toAgent].role = AGENT_ROLE.PRIMARY;
1041
+ }
1042
+ }
1043
+
1044
+ // Transfer subtask if specified
1045
+ if (handoff.subtaskId) {
1046
+ const subtask = session.subtasks.find(s => s.id === handoff.subtaskId);
1047
+ if (subtask) {
1048
+ subtask.delegatedTo = handoff.toAgent;
1049
+ subtask.transferHistory = subtask.transferHistory || [];
1050
+ subtask.transferHistory.push({
1051
+ from: handoff.fromAgent,
1052
+ to: handoff.toAgent,
1053
+ handoffId,
1054
+ timestamp: new Date().toISOString()
1055
+ });
1056
+ }
1057
+ }
1058
+
1059
+ // Update agent states
1060
+ if (session.agentParticipants[handoff.toAgent]) {
1061
+ session.agentParticipants[handoff.toAgent].status = 'active';
1062
+ }
1063
+
1064
+ // Remove from pending
1065
+ state.pendingHandoffs = state.pendingHandoffs.filter(
1066
+ h => !(h.sessionId === sessionId && h.handoffId === handoffId)
1067
+ );
1068
+
1069
+ state.history.push({
1070
+ action: 'handoff_accepted',
1071
+ sessionId,
1072
+ handoffId,
1073
+ byAgent: handoff.toAgent,
1074
+ transferPrimaryRole: handoff.transferPrimaryRole,
1075
+ previousPrimary: handoff.transferPrimaryRole ? previousPrimary : null,
1076
+ timestamp: new Date().toISOString()
1077
+ });
1078
+
1079
+ saveCollabState(state);
1080
+
1081
+ emitCollabTelemetry('handoff_accepted', {
1082
+ sessionId,
1083
+ handoffId,
1084
+ byAgent: handoff.toAgent,
1085
+ transferPrimaryRole: handoff.transferPrimaryRole
1086
+ });
1087
+
1088
+ return {
1089
+ success: true,
1090
+ sessionId,
1091
+ handoffId,
1092
+ newPrimaryAgent: session.primaryAgent,
1093
+ transferredSubtask: handoff.subtaskId,
1094
+ context: handoff.context,
1095
+ message: handoff.transferPrimaryRole
1096
+ ? `Primary role transferred to ${handoff.toAgent}`
1097
+ : `Handoff accepted by ${handoff.toAgent}`
1098
+ };
1099
+ }
1100
+
1101
+ /**
1102
+ * Reject a handoff
1103
+ * @param {string} sessionId - Collaboration session ID
1104
+ * @param {string} handoffId - Handoff ID
1105
+ * @param {string} reason - Rejection reason
1106
+ */
1107
+ function rejectHandoff(sessionId, handoffId, reason) {
1108
+ const state = loadCollabState();
1109
+ const session = state.activeSessions[sessionId];
1110
+
1111
+ if (!session) {
1112
+ return { success: false, error: `Session not found: ${sessionId}` };
1113
+ }
1114
+
1115
+ const handoff = session.handoffs.find(h => h.id === handoffId);
1116
+ if (!handoff) {
1117
+ return { success: false, error: `Handoff not found: ${handoffId}` };
1118
+ }
1119
+
1120
+ if (handoff.status !== HANDOFF_STATUS.PENDING) {
1121
+ return { success: false, error: `Handoff already ${handoff.status}` };
1122
+ }
1123
+
1124
+ handoff.status = HANDOFF_STATUS.REJECTED;
1125
+ handoff.rejectionReason = reason;
1126
+ handoff.rejectedAt = new Date().toISOString();
1127
+
1128
+ // Remove from pending
1129
+ state.pendingHandoffs = state.pendingHandoffs.filter(
1130
+ h => !(h.sessionId === sessionId && h.handoffId === handoffId)
1131
+ );
1132
+
1133
+ state.history.push({
1134
+ action: 'handoff_rejected',
1135
+ sessionId,
1136
+ handoffId,
1137
+ byAgent: handoff.toAgent,
1138
+ reason,
1139
+ timestamp: new Date().toISOString()
1140
+ });
1141
+
1142
+ saveCollabState(state);
1143
+
1144
+ return {
1145
+ success: true,
1146
+ sessionId,
1147
+ handoffId,
1148
+ rejectionReason: reason,
1149
+ message: `Handoff rejected by ${handoff.toAgent}: ${reason}`
1150
+ };
1151
+ }
1152
+
1153
+ /**
1154
+ * Complete a handoff (mark work as done)
1155
+ * @param {string} sessionId - Collaboration session ID
1156
+ * @param {string} handoffId - Handoff ID
1157
+ * @param {object} result - Handoff result
1158
+ */
1159
+ function completeHandoff(sessionId, handoffId, result = {}) {
1160
+ const state = loadCollabState();
1161
+ const session = state.activeSessions[sessionId];
1162
+
1163
+ if (!session) {
1164
+ return { success: false, error: `Session not found: ${sessionId}` };
1165
+ }
1166
+
1167
+ const handoff = session.handoffs.find(h => h.id === handoffId);
1168
+ if (!handoff) {
1169
+ return { success: false, error: `Handoff not found: ${handoffId}` };
1170
+ }
1171
+
1172
+ if (handoff.status !== HANDOFF_STATUS.ACCEPTED) {
1173
+ return { success: false, error: 'Handoff must be accepted before completing' };
1174
+ }
1175
+
1176
+ handoff.status = HANDOFF_STATUS.COMPLETED;
1177
+ handoff.completedAt = new Date().toISOString();
1178
+ handoff.result = result;
1179
+
1180
+ state.history.push({
1181
+ action: 'handoff_completed',
1182
+ sessionId,
1183
+ handoffId,
1184
+ byAgent: handoff.toAgent,
1185
+ timestamp: new Date().toISOString()
1186
+ });
1187
+
1188
+ saveCollabState(state);
1189
+
1190
+ return {
1191
+ success: true,
1192
+ sessionId,
1193
+ handoffId,
1194
+ message: `Handoff completed by ${handoff.toAgent}`
1195
+ };
1196
+ }
1197
+
1198
+ /**
1199
+ * Create a checkpoint for session recovery
1200
+ * @param {string} sessionId - Collaboration session ID
1201
+ * @param {string} description - Checkpoint description
1202
+ */
1203
+ function createCheckpoint(sessionId, description = '') {
1204
+ const state = loadCollabState();
1205
+ const session = state.activeSessions[sessionId];
1206
+
1207
+ if (!session) {
1208
+ return { success: false, error: `Session not found: ${sessionId}` };
1209
+ }
1210
+
1211
+ const checkpointId = `checkpoint-${session.checkpoints.length + 1}`;
1212
+ const checkpoint = {
1213
+ id: checkpointId,
1214
+ description,
1215
+ createdAt: new Date().toISOString(),
1216
+ snapshot: {
1217
+ subtasks: JSON.parse(JSON.stringify(session.subtasks)),
1218
+ context: JSON.parse(JSON.stringify(session.context)),
1219
+ results: JSON.parse(JSON.stringify(session.results)),
1220
+ artifacts: JSON.parse(JSON.stringify(session.artifacts)),
1221
+ primaryAgent: session.primaryAgent
1222
+ }
1223
+ };
1224
+
1225
+ session.checkpoints.push(checkpoint);
1226
+
1227
+ state.history.push({
1228
+ action: 'checkpoint_created',
1229
+ sessionId,
1230
+ checkpointId,
1231
+ description,
1232
+ timestamp: new Date().toISOString()
1233
+ });
1234
+
1235
+ saveCollabState(state);
1236
+
1237
+ return {
1238
+ success: true,
1239
+ sessionId,
1240
+ checkpointId,
1241
+ message: `Checkpoint created: ${description || checkpointId}`
1242
+ };
1243
+ }
1244
+
1245
+ /**
1246
+ * Restore session from checkpoint
1247
+ * @param {string} sessionId - Collaboration session ID
1248
+ * @param {string} checkpointId - Checkpoint ID
1249
+ */
1250
+ function restoreFromCheckpoint(sessionId, checkpointId) {
1251
+ const state = loadCollabState();
1252
+ const session = state.activeSessions[sessionId];
1253
+
1254
+ if (!session) {
1255
+ return { success: false, error: `Session not found: ${sessionId}` };
1256
+ }
1257
+
1258
+ const checkpoint = session.checkpoints.find(c => c.id === checkpointId);
1259
+ if (!checkpoint) {
1260
+ return { success: false, error: `Checkpoint not found: ${checkpointId}` };
1261
+ }
1262
+
1263
+ // Restore from snapshot
1264
+ session.subtasks = JSON.parse(JSON.stringify(checkpoint.snapshot.subtasks));
1265
+ session.context = JSON.parse(JSON.stringify(checkpoint.snapshot.context));
1266
+ session.results = JSON.parse(JSON.stringify(checkpoint.snapshot.results));
1267
+ session.artifacts = JSON.parse(JSON.stringify(checkpoint.snapshot.artifacts));
1268
+ session.primaryAgent = checkpoint.snapshot.primaryAgent;
1269
+
1270
+ state.history.push({
1271
+ action: 'checkpoint_restored',
1272
+ sessionId,
1273
+ checkpointId,
1274
+ timestamp: new Date().toISOString()
1275
+ });
1276
+
1277
+ saveCollabState(state);
1278
+
1279
+ return {
1280
+ success: true,
1281
+ sessionId,
1282
+ checkpointId,
1283
+ message: `Session restored to checkpoint: ${checkpoint.description || checkpointId}`
1284
+ };
1285
+ }
1286
+
1287
+ /**
1288
+ * Finalize collaboration session
1289
+ * @param {string} sessionId - Collaboration session ID
1290
+ * @param {object} summary - Final summary
1291
+ */
1292
+ function finalizeSession(sessionId, summary = {}) {
1293
+ const state = loadCollabState();
1294
+ const session = state.activeSessions[sessionId];
1295
+
1296
+ if (!session) {
1297
+ return { success: false, error: `Session not found: ${sessionId}` };
1298
+ }
1299
+
1300
+ const endTime = Date.now();
1301
+ const duration = endTime - session.metrics.startTime;
1302
+
1303
+ const completedSubtasks = session.subtasks.filter(s => s.status === TASK_STATUS.COMPLETE);
1304
+ const failedSubtasks = session.subtasks.filter(s => s.status === TASK_STATUS.FAILED);
1305
+
1306
+ session.status = 'complete';
1307
+ session.completedAt = new Date().toISOString();
1308
+ session.summary = {
1309
+ ...summary,
1310
+ task: session.task,
1311
+ primaryAgent: session.primaryAgent,
1312
+ subtasksCompleted: completedSubtasks.length,
1313
+ subtasksFailed: failedSubtasks.length,
1314
+ totalSubtasks: session.subtasks.length,
1315
+ handoffsCount: session.handoffs.length,
1316
+ acceptedHandoffs: session.handoffs.filter(h => h.status === HANDOFF_STATUS.ACCEPTED).length,
1317
+ rejectedHandoffs: session.handoffs.filter(h => h.status === HANDOFF_STATUS.REJECTED).length,
1318
+ duration,
1319
+ durationFormatted: formatDuration(duration),
1320
+ artifactsCount: session.artifacts.length,
1321
+ agentParticipation: Object.entries(session.agentParticipants).map(([agent, data]) => ({
1322
+ agent,
1323
+ role: data.role,
1324
+ tasksAssigned: data.tasksAssigned,
1325
+ tasksCompleted: data.tasksCompleted
1326
+ })),
1327
+ learnings: session.learnings || [],
1328
+ checkpointsCount: session.checkpoints.length
1329
+ };
1330
+
1331
+ // Calculate success rate
1332
+ session.summary.successRate = session.subtasks.length > 0
1333
+ ? Math.round((completedSubtasks.length / session.subtasks.length) * 100)
1334
+ : 100;
1335
+
1336
+ // Move to completed
1337
+ state.completedSessions.push(session);
1338
+ delete state.activeSessions[sessionId];
1339
+
1340
+ // Clean up agent task queues
1341
+ for (const agent of Object.keys(session.agentParticipants)) {
1342
+ if (state.taskQueues[agent]) {
1343
+ state.taskQueues[agent] = state.taskQueues[agent]
1344
+ .filter(t => t.sessionId !== sessionId);
1345
+ }
1346
+ }
1347
+
1348
+ // Update global metrics
1349
+ state.metrics.successfulSessions++;
1350
+ const totalDuration = state.metrics.avgSessionDuration * (state.metrics.successfulSessions - 1);
1351
+ state.metrics.avgSessionDuration = (totalDuration + duration) / state.metrics.successfulSessions;
1352
+
1353
+ state.history.push({
1354
+ action: 'session_finalized',
1355
+ sessionId,
1356
+ duration,
1357
+ successRate: session.summary.successRate,
1358
+ timestamp: new Date().toISOString()
1359
+ });
1360
+
1361
+ saveCollabState(state);
1362
+
1363
+ emitCollabTelemetry('collab_session_completed', {
1364
+ sessionId,
1365
+ duration,
1366
+ successRate: session.summary.successRate,
1367
+ subtaskCount: session.subtasks.length,
1368
+ handoffCount: session.handoffs.length
1369
+ });
1370
+
1371
+ return {
1372
+ success: true,
1373
+ sessionId,
1374
+ session,
1375
+ summary: session.summary,
1376
+ message: 'Collaboration session finalized'
1377
+ };
1378
+ }
1379
+
1380
+ /**
1381
+ * Abort a collaboration session
1382
+ * @param {string} sessionId - Collaboration session ID
1383
+ * @param {string} reason - Abort reason
1384
+ */
1385
+ function abortSession(sessionId, reason = '') {
1386
+ const state = loadCollabState();
1387
+ const session = state.activeSessions[sessionId];
1388
+
1389
+ if (!session) {
1390
+ return { success: false, error: `Session not found: ${sessionId}` };
1391
+ }
1392
+
1393
+ session.status = 'aborted';
1394
+ session.abortedAt = new Date().toISOString();
1395
+ session.abortReason = reason;
1396
+
1397
+ // Move to completed (as aborted)
1398
+ state.completedSessions.push(session);
1399
+ delete state.activeSessions[sessionId];
1400
+
1401
+ // Clean up pending handoffs
1402
+ state.pendingHandoffs = state.pendingHandoffs
1403
+ .filter(h => h.sessionId !== sessionId);
1404
+
1405
+ // Clean up agent task queues
1406
+ for (const agent of Object.keys(session.agentParticipants)) {
1407
+ if (state.taskQueues[agent]) {
1408
+ state.taskQueues[agent] = state.taskQueues[agent]
1409
+ .filter(t => t.sessionId !== sessionId);
1410
+ }
1411
+ }
1412
+
1413
+ state.history.push({
1414
+ action: 'session_aborted',
1415
+ sessionId,
1416
+ reason,
1417
+ timestamp: new Date().toISOString()
1418
+ });
1419
+
1420
+ saveCollabState(state);
1421
+
1422
+ return {
1423
+ success: true,
1424
+ sessionId,
1425
+ reason,
1426
+ message: `Collaboration session aborted: ${reason}`
1427
+ };
1428
+ }
1429
+
1430
+ /**
1431
+ * Format duration in human-readable format
1432
+ */
1433
+ function formatDuration(ms) {
1434
+ const seconds = Math.floor(ms / 1000);
1435
+ const minutes = Math.floor(seconds / 60);
1436
+ const hours = Math.floor(minutes / 60);
1437
+
1438
+ if (hours > 0) {
1439
+ return `${hours}h ${minutes % 60}m`;
1440
+ } else if (minutes > 0) {
1441
+ return `${minutes}m ${seconds % 60}s`;
1442
+ }
1443
+ return `${seconds}s`;
1444
+ }
1445
+
1446
+ /**
1447
+ * Get session status
1448
+ * @param {string} sessionId - Collaboration session ID
1449
+ */
1450
+ function getSessionStatus(sessionId) {
1451
+ const state = loadCollabState();
1452
+ const session = state.activeSessions[sessionId];
1453
+
1454
+ if (!session) {
1455
+ // Check completed sessions
1456
+ const completed = state.completedSessions.find(s => s.id === sessionId);
1457
+ if (completed) {
1458
+ return {
1459
+ found: true,
1460
+ session: completed,
1461
+ isActive: false,
1462
+ summary: completed.summary
1463
+ };
1464
+ }
1465
+ return { found: false };
1466
+ }
1467
+
1468
+ const pendingSubtasks = session.subtasks.filter(s => s.status === TASK_STATUS.PENDING);
1469
+ const inProgressSubtasks = session.subtasks.filter(s => s.status === TASK_STATUS.IN_PROGRESS);
1470
+ const blockedSubtasks = session.subtasks.filter(s => s.status === TASK_STATUS.BLOCKED);
1471
+ const completedSubtasks = session.subtasks.filter(s => s.status === TASK_STATUS.COMPLETE);
1472
+ const failedSubtasks = session.subtasks.filter(s => s.status === TASK_STATUS.FAILED);
1473
+ const pendingHandoffs = session.handoffs.filter(h => h.status === HANDOFF_STATUS.PENDING);
1474
+
1475
+ const elapsed = Date.now() - session.metrics.startTime;
1476
+
1477
+ return {
1478
+ found: true,
1479
+ isActive: true,
1480
+ session: {
1481
+ id: session.id,
1482
+ task: session.task,
1483
+ status: session.status,
1484
+ primaryAgent: session.primaryAgent,
1485
+ supportingAgents: session.supportingAgents,
1486
+ createdAt: session.createdAt
1487
+ },
1488
+ progress: {
1489
+ subtasks: {
1490
+ pending: pendingSubtasks.length,
1491
+ inProgress: inProgressSubtasks.length,
1492
+ blocked: blockedSubtasks.length,
1493
+ completed: completedSubtasks.length,
1494
+ failed: failedSubtasks.length,
1495
+ total: session.subtasks.length,
1496
+ percent: session.subtasks.length > 0
1497
+ ? Math.round((completedSubtasks.length / session.subtasks.length) * 100)
1498
+ : 0
1499
+ },
1500
+ handoffs: {
1501
+ pending: pendingHandoffs.length,
1502
+ total: session.handoffs.length,
1503
+ accepted: session.handoffs.filter(h => h.status === HANDOFF_STATUS.ACCEPTED).length
1504
+ },
1505
+ artifacts: session.artifacts.length,
1506
+ checkpoints: session.checkpoints.length
1507
+ },
1508
+ agents: Object.entries(session.agentParticipants).map(([agent, data]) => ({
1509
+ agent,
1510
+ role: data.role,
1511
+ status: data.status,
1512
+ tasksAssigned: data.tasksAssigned,
1513
+ tasksCompleted: data.tasksCompleted
1514
+ })),
1515
+ timing: {
1516
+ elapsed,
1517
+ elapsedFormatted: formatDuration(elapsed)
1518
+ },
1519
+ nextActions: getNextActions(session, pendingSubtasks, pendingHandoffs, blockedSubtasks)
1520
+ };
1521
+ }
1522
+
1523
+ /**
1524
+ * Get suggested next actions for a session
1525
+ */
1526
+ function getNextActions(session, pendingSubtasks, pendingHandoffs, blockedSubtasks) {
1527
+ const actions = [];
1528
+
1529
+ // Pending handoffs need attention
1530
+ if (pendingHandoffs.length > 0) {
1531
+ actions.push({
1532
+ type: 'accept_handoffs',
1533
+ urgency: 'high',
1534
+ count: pendingHandoffs.length,
1535
+ message: `${pendingHandoffs.length} handoff(s) awaiting acceptance`
1536
+ });
1537
+ }
1538
+
1539
+ // Blocked tasks need resolution
1540
+ if (blockedSubtasks.length > 0) {
1541
+ actions.push({
1542
+ type: 'resolve_blockers',
1543
+ urgency: 'medium',
1544
+ count: blockedSubtasks.length,
1545
+ message: `${blockedSubtasks.length} task(s) blocked on dependencies`
1546
+ });
1547
+ }
1548
+
1549
+ // Pending tasks ready to start
1550
+ if (pendingSubtasks.length > 0) {
1551
+ const highPriority = pendingSubtasks.filter(t => t.priority === TASK_PRIORITY.HIGH || t.priority === TASK_PRIORITY.CRITICAL);
1552
+ if (highPriority.length > 0) {
1553
+ actions.push({
1554
+ type: 'start_high_priority',
1555
+ urgency: 'high',
1556
+ count: highPriority.length,
1557
+ message: `${highPriority.length} high priority task(s) ready to start`
1558
+ });
1559
+ }
1560
+ }
1561
+
1562
+ // Check if ready to finalize
1563
+ const allComplete = session.subtasks.length > 0 &&
1564
+ session.subtasks.every(s =>
1565
+ s.status === TASK_STATUS.COMPLETE || s.status === TASK_STATUS.FAILED
1566
+ );
1567
+
1568
+ if (allComplete) {
1569
+ actions.push({
1570
+ type: 'finalize_session',
1571
+ urgency: 'low',
1572
+ message: 'All tasks processed. Ready to finalize session.'
1573
+ });
1574
+ }
1575
+
1576
+ return actions;
1577
+ }
1578
+
1579
+ /**
1580
+ * Get the next task for an agent
1581
+ * @param {string} agentName - Agent name
1582
+ */
1583
+ function getNextTaskForAgent(agentName) {
1584
+ const state = loadCollabState();
1585
+ const agentQueue = state.taskQueues[agentName] || [];
1586
+
1587
+ if (agentQueue.length === 0) {
1588
+ return { hasTask: false, message: 'No tasks in queue' };
1589
+ }
1590
+
1591
+ // Sort by priority and creation time
1592
+ const priorityOrder = {
1593
+ [TASK_PRIORITY.CRITICAL]: 0,
1594
+ [TASK_PRIORITY.HIGH]: 1,
1595
+ [TASK_PRIORITY.MEDIUM]: 2,
1596
+ [TASK_PRIORITY.LOW]: 3
1597
+ };
1598
+
1599
+ agentQueue.sort((a, b) => {
1600
+ const priorityDiff = (priorityOrder[a.priority] || 2) - (priorityOrder[b.priority] || 2);
1601
+ if (priorityDiff !== 0) return priorityDiff;
1602
+ return new Date(a.createdAt) - new Date(b.createdAt);
1603
+ });
1604
+
1605
+ const nextItem = agentQueue[0];
1606
+ const session = state.activeSessions[nextItem.sessionId];
1607
+
1608
+ if (!session) {
1609
+ // Task from completed/aborted session, remove it
1610
+ state.taskQueues[agentName] = agentQueue.slice(1);
1611
+ saveCollabState(state);
1612
+ return getNextTaskForAgent(agentName); // Recurse
1613
+ }
1614
+
1615
+ const subtask = session.subtasks.find(s => s.id === nextItem.subtaskId);
1616
+
1617
+ if (!subtask || subtask.status === TASK_STATUS.BLOCKED) {
1618
+ // Skip blocked tasks
1619
+ return {
1620
+ hasTask: false,
1621
+ blocked: true,
1622
+ message: 'Next task is blocked on dependencies'
1623
+ };
1624
+ }
1625
+
1626
+ return {
1627
+ hasTask: true,
1628
+ sessionId: nextItem.sessionId,
1629
+ subtask,
1630
+ sessionContext: session.context.current,
1631
+ message: `Next task: ${subtask.title}`
1632
+ };
1633
+ }
1634
+
1635
+ /**
1636
+ * List active sessions
1637
+ */
1638
+ function listActiveSessions() {
1639
+ const state = loadCollabState();
1640
+
1641
+ return Object.values(state.activeSessions).map(session => ({
1642
+ id: session.id,
1643
+ task: session.task,
1644
+ primaryAgent: session.primaryAgent,
1645
+ status: session.status,
1646
+ subtaskCount: session.subtasks.length,
1647
+ createdAt: session.createdAt
1648
+ }));
1649
+ }
1650
+
1651
+ /**
1652
+ * Get pending handoffs for an agent
1653
+ * @param {string} agentName - Agent name
1654
+ */
1655
+ function getPendingHandoffsForAgent(agentName) {
1656
+ const state = loadCollabState();
1657
+
1658
+ return state.pendingHandoffs.filter(h => h.toAgent === agentName);
1659
+ }
1660
+
1661
+ /**
1662
+ * Collaboration patterns for common scenarios
1663
+ */
1664
+ const COLLAB_PATTERNS = {
1665
+ 'code-review': {
1666
+ name: 'Code Review',
1667
+ description: 'Multi-agent code review with security and performance focus',
1668
+ primaryAgent: 'code-review-expert',
1669
+ supportingAgents: ['security-expert', 'performance-expert'],
1670
+ subtaskTemplate: [
1671
+ { title: 'Security Review', delegatedTo: 'security-expert', priority: TASK_PRIORITY.HIGH },
1672
+ { title: 'Performance Review', delegatedTo: 'performance-expert', priority: TASK_PRIORITY.MEDIUM }
1673
+ ]
1674
+ },
1675
+ 'feature-build': {
1676
+ name: 'Feature Build',
1677
+ description: 'Collaborative feature development with parallel tracks',
1678
+ primaryAgent: 'architecture-expert',
1679
+ supportingAgents: ['backend-expert', 'frontend-expert', 'database-expert', 'testing-expert'],
1680
+ subtaskTemplate: [
1681
+ { title: 'Architecture Design', delegatedTo: 'architecture-expert', priority: TASK_PRIORITY.CRITICAL },
1682
+ { title: 'Database Schema', delegatedTo: 'database-expert', priority: TASK_PRIORITY.HIGH, dependencies: ['subtask-1'] },
1683
+ { title: 'API Implementation', delegatedTo: 'backend-expert', priority: TASK_PRIORITY.HIGH, dependencies: ['subtask-2'] },
1684
+ { title: 'UI Implementation', delegatedTo: 'frontend-expert', priority: TASK_PRIORITY.HIGH, dependencies: ['subtask-1'] },
1685
+ { title: 'Integration Tests', delegatedTo: 'testing-expert', priority: TASK_PRIORITY.MEDIUM, dependencies: ['subtask-3', 'subtask-4'] }
1686
+ ]
1687
+ },
1688
+ 'security-hardening': {
1689
+ name: 'Security Hardening',
1690
+ description: 'Security-focused collaboration',
1691
+ primaryAgent: 'security-expert',
1692
+ supportingAgents: ['backend-expert', 'devops-expert'],
1693
+ subtaskTemplate: [
1694
+ { title: 'Vulnerability Assessment', delegatedTo: 'security-expert', priority: TASK_PRIORITY.CRITICAL },
1695
+ { title: 'Code Security Fixes', delegatedTo: 'backend-expert', priority: TASK_PRIORITY.HIGH, dependencies: ['subtask-1'] },
1696
+ { title: 'Infrastructure Hardening', delegatedTo: 'devops-expert', priority: TASK_PRIORITY.HIGH, dependencies: ['subtask-1'] },
1697
+ { title: 'Security Verification', delegatedTo: 'security-expert', priority: TASK_PRIORITY.HIGH, dependencies: ['subtask-2', 'subtask-3'] }
1698
+ ]
1699
+ },
1700
+ 'api-development': {
1701
+ name: 'API Development',
1702
+ description: 'End-to-end API development with documentation',
1703
+ primaryAgent: 'api-expert',
1704
+ supportingAgents: ['database-expert', 'security-expert', 'testing-expert'],
1705
+ subtaskTemplate: [
1706
+ { title: 'API Design', delegatedTo: 'api-expert', priority: TASK_PRIORITY.CRITICAL },
1707
+ { title: 'Data Model', delegatedTo: 'database-expert', priority: TASK_PRIORITY.HIGH, dependencies: ['subtask-1'] },
1708
+ { title: 'API Implementation', delegatedTo: 'api-expert', priority: TASK_PRIORITY.HIGH, dependencies: ['subtask-2'] },
1709
+ { title: 'Security Review', delegatedTo: 'security-expert', priority: TASK_PRIORITY.HIGH, dependencies: ['subtask-3'] },
1710
+ { title: 'API Tests', delegatedTo: 'testing-expert', priority: TASK_PRIORITY.MEDIUM, dependencies: ['subtask-3'] }
1711
+ ]
1712
+ },
1713
+ 'full-stack-feature': {
1714
+ name: 'Full Stack Feature',
1715
+ description: 'Complete full-stack feature with frontend, backend, and testing',
1716
+ primaryAgent: 'architecture-expert',
1717
+ supportingAgents: ['frontend-expert', 'backend-expert', 'database-expert', 'testing-expert', 'ui-ux-expert'],
1718
+ subtaskTemplate: [
1719
+ { title: 'Feature Specification', delegatedTo: 'architecture-expert', priority: TASK_PRIORITY.CRITICAL },
1720
+ { title: 'UI/UX Design', delegatedTo: 'ui-ux-expert', priority: TASK_PRIORITY.HIGH, dependencies: ['subtask-1'] },
1721
+ { title: 'Data Model', delegatedTo: 'database-expert', priority: TASK_PRIORITY.HIGH, dependencies: ['subtask-1'] },
1722
+ { title: 'Backend API', delegatedTo: 'backend-expert', priority: TASK_PRIORITY.HIGH, dependencies: ['subtask-3'] },
1723
+ { title: 'Frontend UI', delegatedTo: 'frontend-expert', priority: TASK_PRIORITY.HIGH, dependencies: ['subtask-2', 'subtask-4'] },
1724
+ { title: 'Integration Testing', delegatedTo: 'testing-expert', priority: TASK_PRIORITY.MEDIUM, dependencies: ['subtask-4', 'subtask-5'] }
1725
+ ]
1726
+ },
1727
+ 'performance-optimization': {
1728
+ name: 'Performance Optimization',
1729
+ description: 'Performance analysis and optimization across the stack',
1730
+ primaryAgent: 'performance-expert',
1731
+ supportingAgents: ['database-expert', 'frontend-expert', 'backend-expert'],
1732
+ subtaskTemplate: [
1733
+ { title: 'Performance Audit', delegatedTo: 'performance-expert', priority: TASK_PRIORITY.CRITICAL },
1734
+ { title: 'Database Optimization', delegatedTo: 'database-expert', priority: TASK_PRIORITY.HIGH, dependencies: ['subtask-1'] },
1735
+ { title: 'Backend Optimization', delegatedTo: 'backend-expert', priority: TASK_PRIORITY.HIGH, dependencies: ['subtask-1'] },
1736
+ { title: 'Frontend Optimization', delegatedTo: 'frontend-expert', priority: TASK_PRIORITY.HIGH, dependencies: ['subtask-1'] },
1737
+ { title: 'Performance Verification', delegatedTo: 'performance-expert', priority: TASK_PRIORITY.MEDIUM, dependencies: ['subtask-2', 'subtask-3', 'subtask-4'] }
1738
+ ]
1739
+ },
1740
+ 'launch-prep': {
1741
+ name: 'Launch Preparation',
1742
+ description: 'Pre-launch checklist with security, performance, and deployment',
1743
+ primaryAgent: 'devops-expert',
1744
+ supportingAgents: ['security-expert', 'performance-expert', 'testing-expert'],
1745
+ subtaskTemplate: [
1746
+ { title: 'Security Audit', delegatedTo: 'security-expert', priority: TASK_PRIORITY.CRITICAL },
1747
+ { title: 'Performance Testing', delegatedTo: 'performance-expert', priority: TASK_PRIORITY.CRITICAL },
1748
+ { title: 'End-to-End Tests', delegatedTo: 'testing-expert', priority: TASK_PRIORITY.HIGH },
1749
+ { title: 'Deployment Config', delegatedTo: 'devops-expert', priority: TASK_PRIORITY.HIGH, dependencies: ['subtask-1', 'subtask-2'] },
1750
+ { title: 'Launch Verification', delegatedTo: 'devops-expert', priority: TASK_PRIORITY.HIGH, dependencies: ['subtask-3', 'subtask-4'] }
1751
+ ]
1752
+ }
1753
+ };
1754
+
1755
+ /**
1756
+ * Start session from pattern
1757
+ * @param {string} patternName - Pattern name
1758
+ * @param {string} task - Task description
1759
+ * @param {object} options - Additional options
1760
+ */
1761
+ function startFromPattern(patternName, task, options = {}) {
1762
+ const pattern = COLLAB_PATTERNS[patternName];
1763
+ if (!pattern) {
1764
+ return {
1765
+ success: false,
1766
+ error: `Unknown pattern: ${patternName}`,
1767
+ availablePatterns: Object.keys(COLLAB_PATTERNS)
1768
+ };
1769
+ }
1770
+
1771
+ const { context = {}, autoStart = false } = options;
1772
+
1773
+ // Start session
1774
+ const sessionResult = startSession({
1775
+ task,
1776
+ primaryAgent: pattern.primaryAgent,
1777
+ supportingAgents: pattern.supportingAgents,
1778
+ context: {
1779
+ pattern: patternName,
1780
+ ...context
1781
+ }
1782
+ });
1783
+
1784
+ if (!sessionResult.success) {
1785
+ return sessionResult;
1786
+ }
1787
+
1788
+ // Create subtasks from template, mapping template dependencies to real IDs
1789
+ const subtaskResults = [];
1790
+ const idMapping = {};
1791
+
1792
+ for (let i = 0; i < pattern.subtaskTemplate.length; i++) {
1793
+ const template = pattern.subtaskTemplate[i];
1794
+ const templateId = `subtask-${i + 1}`;
1795
+
1796
+ // Map template dependencies to actual subtask IDs
1797
+ const mappedDependencies = (template.dependencies || []).map(depId => {
1798
+ return idMapping[depId] || depId;
1799
+ });
1800
+
1801
+ const result = delegateTask(sessionResult.sessionId, {
1802
+ ...template,
1803
+ description: `${template.title} for: ${task}`,
1804
+ dependencies: mappedDependencies
1805
+ });
1806
+
1807
+ if (result.success) {
1808
+ idMapping[templateId] = result.subtaskId;
1809
+ }
1810
+ subtaskResults.push(result);
1811
+ }
1812
+
1813
+ // Auto-start first task if requested
1814
+ if (autoStart && subtaskResults.length > 0 && subtaskResults[0].success) {
1815
+ startSubtask(sessionResult.sessionId, subtaskResults[0].subtaskId);
1816
+ }
1817
+
1818
+ return {
1819
+ success: true,
1820
+ sessionId: sessionResult.sessionId,
1821
+ pattern: patternName,
1822
+ patternName: pattern.name,
1823
+ primaryAgent: pattern.primaryAgent,
1824
+ subtasks: subtaskResults.map(r => ({
1825
+ id: r.subtaskId,
1826
+ title: r.subtask?.title,
1827
+ delegatedTo: r.subtask?.delegatedTo,
1828
+ status: r.subtask?.status
1829
+ })),
1830
+ message: `Started collaboration from "${pattern.name}" pattern`
1831
+ };
1832
+ }
1833
+
1834
+ /**
1835
+ * Get collaboration statistics
1836
+ */
1837
+ function getCollabStats() {
1838
+ const state = loadCollabState();
1839
+
1840
+ const activeSessions = Object.values(state.activeSessions);
1841
+ const totalActive = activeSessions.length;
1842
+ const totalSubtasksActive = activeSessions.reduce((sum, s) => sum + s.subtasks.length, 0);
1843
+ const pendingHandoffs = state.pendingHandoffs.length;
1844
+
1845
+ return {
1846
+ activeSessions: totalActive,
1847
+ completedSessions: state.completedSessions.length,
1848
+ totalSessions: state.metrics.totalSessions,
1849
+ successfulSessions: state.metrics.successfulSessions,
1850
+ totalSubtasks: state.metrics.totalSubtasks,
1851
+ totalHandoffs: state.metrics.totalHandoffs,
1852
+ activeSubtasks: totalSubtasksActive,
1853
+ pendingHandoffs,
1854
+ avgSessionDuration: state.metrics.avgSessionDuration,
1855
+ avgSessionDurationFormatted: formatDuration(state.metrics.avgSessionDuration || 0),
1856
+ blockedTasks: state.blockedTasks.length
1857
+ };
1858
+ }
1859
+
1860
+ /**
1861
+ * Get session history
1862
+ * @param {string} sessionId - Optional session ID for filtered history
1863
+ */
1864
+ function getSessionHistory(sessionId = null) {
1865
+ const state = loadCollabState();
1866
+
1867
+ if (sessionId) {
1868
+ return state.history.filter(h => h.sessionId === sessionId);
1869
+ }
1870
+
1871
+ return state.history.slice(-100); // Last 100 events
1872
+ }
1873
+
1874
+ /**
1875
+ * List available patterns
1876
+ */
1877
+ function listPatterns() {
1878
+ return Object.entries(COLLAB_PATTERNS).map(([key, pattern]) => ({
1879
+ key,
1880
+ name: pattern.name,
1881
+ description: pattern.description,
1882
+ primaryAgent: pattern.primaryAgent,
1883
+ supportingAgents: pattern.supportingAgents,
1884
+ subtaskCount: pattern.subtaskTemplate.length
1885
+ }));
1886
+ }
1887
+
1888
+ /**
1889
+ * Broadcast message to all agents in a session
1890
+ * @param {string} sessionId - Collaboration session ID
1891
+ * @param {object} message - Message to broadcast
1892
+ */
1893
+ function broadcastToSession(sessionId, message) {
1894
+ const state = loadCollabState();
1895
+ const session = state.activeSessions[sessionId];
1896
+
1897
+ if (!session) {
1898
+ return { success: false, error: `Session not found: ${sessionId}` };
1899
+ }
1900
+
1901
+ const { type, content, fromAgent, priority = 'normal' } = message;
1902
+
1903
+ const broadcast = {
1904
+ id: `broadcast-${Date.now()}`,
1905
+ type,
1906
+ content,
1907
+ fromAgent,
1908
+ priority,
1909
+ timestamp: new Date().toISOString(),
1910
+ recipients: Object.keys(session.agentParticipants)
1911
+ };
1912
+
1913
+ session.broadcasts = session.broadcasts || [];
1914
+ session.broadcasts.push(broadcast);
1915
+
1916
+ state.history.push({
1917
+ action: 'broadcast_sent',
1918
+ sessionId,
1919
+ broadcastId: broadcast.id,
1920
+ fromAgent,
1921
+ recipientCount: broadcast.recipients.length,
1922
+ timestamp: broadcast.timestamp
1923
+ });
1924
+
1925
+ saveCollabState(state);
1926
+
1927
+ return {
1928
+ success: true,
1929
+ broadcastId: broadcast.id,
1930
+ recipients: broadcast.recipients,
1931
+ message: `Broadcast sent to ${broadcast.recipients.length} agents`
1932
+ };
1933
+ }
1934
+
1935
+ /**
1936
+ * Share an artifact with the session
1937
+ * @param {string} sessionId - Collaboration session ID
1938
+ * @param {object} artifact - Artifact to share
1939
+ */
1940
+ function shareArtifact(sessionId, artifact) {
1941
+ const state = loadCollabState();
1942
+ const session = state.activeSessions[sessionId];
1943
+
1944
+ if (!session) {
1945
+ return { success: false, error: `Session not found: ${sessionId}` };
1946
+ }
1947
+
1948
+ const { name, type, content, fromAgent, description = '' } = artifact;
1949
+
1950
+ if (!name || !type || !fromAgent) {
1951
+ return { success: false, error: 'name, type, and fromAgent are required' };
1952
+ }
1953
+
1954
+ const sharedArtifact = {
1955
+ id: `artifact-${Date.now()}`,
1956
+ name,
1957
+ type,
1958
+ content,
1959
+ description,
1960
+ fromAgent,
1961
+ sharedAt: new Date().toISOString()
1962
+ };
1963
+
1964
+ session.artifacts.push(sharedArtifact);
1965
+
1966
+ state.history.push({
1967
+ action: 'artifact_shared',
1968
+ sessionId,
1969
+ artifactId: sharedArtifact.id,
1970
+ name,
1971
+ type,
1972
+ fromAgent,
1973
+ timestamp: sharedArtifact.sharedAt
1974
+ });
1975
+
1976
+ saveCollabState(state);
1977
+
1978
+ return {
1979
+ success: true,
1980
+ artifactId: sharedArtifact.id,
1981
+ message: `Artifact "${name}" shared with session`
1982
+ };
1983
+ }
1984
+
1985
+ /**
1986
+ * Get all artifacts from a session
1987
+ * @param {string} sessionId - Collaboration session ID
1988
+ */
1989
+ function getSessionArtifacts(sessionId) {
1990
+ const state = loadCollabState();
1991
+ const session = state.activeSessions[sessionId] ||
1992
+ state.completedSessions.find(s => s.id === sessionId);
1993
+
1994
+ if (!session) {
1995
+ return { success: false, error: `Session not found: ${sessionId}` };
1996
+ }
1997
+
1998
+ return {
1999
+ success: true,
2000
+ sessionId,
2001
+ artifacts: session.artifacts || [],
2002
+ count: (session.artifacts || []).length
2003
+ };
2004
+ }
2005
+
2006
+ module.exports = {
2007
+ // Session management
2008
+ startSession,
2009
+ finalizeSession,
2010
+ abortSession,
2011
+ getSessionStatus,
2012
+ listActiveSessions,
2013
+ getSessionHistory,
2014
+
2015
+ // Task delegation
2016
+ delegateTask,
2017
+ startSubtask,
2018
+ updateSubtaskProgress,
2019
+ completeSubtask,
2020
+ failSubtask,
2021
+ retrySubtask,
2022
+ getNextTaskForAgent,
2023
+
2024
+ // Handoff protocol
2025
+ requestHandoff,
2026
+ acceptHandoff,
2027
+ rejectHandoff,
2028
+ completeHandoff,
2029
+ getPendingHandoffsForAgent,
2030
+
2031
+ // Checkpoints and recovery
2032
+ createCheckpoint,
2033
+ restoreFromCheckpoint,
2034
+
2035
+ // Patterns
2036
+ startFromPattern,
2037
+ listPatterns,
2038
+ COLLAB_PATTERNS,
2039
+
2040
+ // Communication
2041
+ broadcastToSession,
2042
+ shareArtifact,
2043
+ getSessionArtifacts,
2044
+
2045
+ // Agent capabilities
2046
+ findBestAgentForTask,
2047
+
2048
+ // Statistics and state
2049
+ getCollabStats,
2050
+ loadCollabState,
2051
+
2052
+ // Constants
2053
+ TASK_PRIORITY,
2054
+ TASK_STATUS,
2055
+ HANDOFF_STATUS,
2056
+ AGENT_ROLE
2057
+ };