@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.
- package/cli/preseed/index.js +16 -0
- package/cli/preseed/interactive.js +143 -0
- package/cli/preseed/templates.js +227 -0
- package/cli/seed/builders/ai-context-builder.js +85 -0
- package/cli/seed/builders/index.js +13 -0
- package/cli/seed/builders/seed-builder.js +272 -0
- package/cli/seed/extractors/content-extractors.js +383 -0
- package/cli/seed/extractors/index.js +47 -0
- package/cli/seed/extractors/metadata-extractors.js +167 -0
- package/cli/seed/extractors/section-extractor.js +54 -0
- package/cli/seed/extractors/stack-extractors.js +228 -0
- package/cli/seed/index.js +18 -0
- package/cli/seed/utils/folder-structure.js +84 -0
- package/cli/seed/utils/index.js +11 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +3220 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/context-McpJQa_2.d.ts +5710 -0
- package/dist/core/index.d.ts +635 -0
- package/dist/core/index.js +2593 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index-QqbeEiDm.d.ts +857 -0
- package/dist/index-UiYCgwiH.d.ts +174 -0
- package/dist/index.d.ts +453 -0
- package/dist/index.js +44228 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.js +41173 -0
- package/dist/mcp/index.js.map +1 -0
- package/generators/index.ts +82 -0
- package/intelligence/orchestrator/config/failure-signatures.js +48 -0
- package/intelligence/orchestrator/config/index.js +20 -0
- package/intelligence/orchestrator/config/phases.js +111 -0
- package/intelligence/orchestrator/config/remediation.js +150 -0
- package/intelligence/orchestrator/config/workflows.js +168 -0
- package/intelligence/orchestrator/core/index.js +16 -0
- package/intelligence/orchestrator/core/state-manager.js +88 -0
- package/intelligence/orchestrator/core/telemetry.js +24 -0
- package/intelligence/orchestrator/index.js +17 -0
- package/mcp/contracts/mcp-contract.v1.json +1 -1
- package/package.json +16 -3
- package/src/cli/agent.ts +703 -0
- package/src/cli/analyze.ts +640 -0
- package/src/cli/audit.ts +707 -0
- package/src/cli/auth.ts +930 -0
- package/src/cli/billing.ts +364 -0
- package/src/cli/build.ts +1089 -0
- package/src/cli/business.ts +508 -0
- package/src/cli/checkpoint-utils.ts +236 -0
- package/src/cli/checkpoint.ts +757 -0
- package/src/cli/cloud-sync.ts +534 -0
- package/src/cli/content.ts +273 -0
- package/src/cli/context.ts +667 -0
- package/src/cli/dashboard.ts +133 -0
- package/src/cli/deploy.ts +704 -0
- package/src/cli/doctor.ts +480 -0
- package/src/cli/fundraise.ts +494 -0
- package/src/cli/generate.ts +346 -0
- package/src/cli/github-cmd.ts +566 -0
- package/src/cli/health.ts +599 -0
- package/src/cli/index.ts +113 -0
- package/src/cli/init.ts +838 -0
- package/src/cli/legal.ts +495 -0
- package/src/cli/log.ts +316 -0
- package/src/cli/loop.ts +1660 -0
- package/src/cli/manager.ts +878 -0
- package/src/cli/mcp.ts +275 -0
- package/src/cli/memory.ts +346 -0
- package/src/cli/metrics.ts +590 -0
- package/src/cli/monitor.ts +960 -0
- package/src/cli/mvp.ts +662 -0
- package/src/cli/onboard.ts +663 -0
- package/src/cli/orchestrator.ts +622 -0
- package/src/cli/plugin.ts +483 -0
- package/src/cli/prd.ts +671 -0
- package/src/cli/preseed-start.ts +1633 -0
- package/src/cli/preseed.ts +2434 -0
- package/src/cli/project.ts +526 -0
- package/src/cli/quality.ts +885 -0
- package/src/cli/security.ts +1079 -0
- package/src/cli/seed.ts +1224 -0
- package/src/cli/skill.ts +537 -0
- package/src/cli/suggest.ts +1225 -0
- package/src/cli/switch.ts +518 -0
- package/src/cli/task.ts +780 -0
- package/src/cli/telemetry.ts +172 -0
- package/src/cli/todo.ts +627 -0
- package/src/cli/types.ts +15 -0
- package/src/cli/update.ts +334 -0
- package/src/cli/visualize.ts +609 -0
- package/src/cli/watch.ts +895 -0
- package/src/cli/workspace.ts +709 -0
- package/src/core/action-recorder.ts +673 -0
- package/src/core/analyze-workflow.ts +1453 -0
- package/src/core/api-client.ts +1120 -0
- package/src/core/audit-workflow.ts +1681 -0
- package/src/core/auth.ts +471 -0
- package/src/core/build-orchestrator.ts +509 -0
- package/src/core/build-state.ts +621 -0
- package/src/core/checkpoint-engine.ts +482 -0
- package/src/core/config.ts +1285 -0
- package/src/core/context-loader.ts +694 -0
- package/src/core/context.ts +410 -0
- package/src/core/deploy-workflow.ts +1085 -0
- package/src/core/entitlements.ts +322 -0
- package/src/core/github-sync.ts +720 -0
- package/src/core/index.ts +981 -0
- package/src/core/ingest.ts +1186 -0
- package/src/core/metrics-engine.ts +886 -0
- package/src/core/mvp.ts +847 -0
- package/src/core/onboard-workflow.ts +1293 -0
- package/src/core/policies.ts +81 -0
- package/src/core/preseed-workflow.ts +1163 -0
- package/src/core/preseed.ts +1826 -0
- package/src/core/project-context.ts +380 -0
- package/src/core/project-state.ts +699 -0
- package/src/core/r2-sync.ts +691 -0
- package/src/core/scaffold.ts +1715 -0
- package/src/core/session.ts +286 -0
- package/src/core/task-extractor.ts +799 -0
- package/src/core/telemetry.ts +371 -0
- package/src/core/tier-enforcement.ts +737 -0
- package/src/core/utils.ts +437 -0
- package/src/index.ts +29 -0
- package/src/intelligence/agent-collab.ts +2376 -0
- package/src/intelligence/auto-suggest.ts +713 -0
- package/src/intelligence/content-gen.ts +1351 -0
- package/src/intelligence/cross-project.ts +1692 -0
- package/src/intelligence/git-memory.ts +529 -0
- package/src/intelligence/index.ts +318 -0
- package/src/intelligence/orchestrator.ts +534 -0
- package/src/intelligence/prd.ts +466 -0
- package/src/intelligence/recommendations.ts +982 -0
- package/src/intelligence/workflow-composer.ts +1472 -0
- package/src/mcp/capabilities.ts +233 -0
- package/src/mcp/index.ts +37 -0
- package/src/mcp/registry.ts +1268 -0
- package/src/mcp/response-formatter.ts +797 -0
- package/src/mcp/server.ts +240 -0
- package/src/types/agent.ts +69 -0
- package/src/types/config.ts +86 -0
- package/src/types/context.ts +77 -0
- package/src/types/index.ts +53 -0
- package/src/types/mcp.ts +91 -0
- package/src/types/skills.ts +47 -0
- package/src/types/workflow.ts +155 -0
- 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
|
+
}
|