claude-brain 0.14.2 → 0.14.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +191 -191
- package/VERSION +1 -1
- package/assets/CLAUDE-unified.md +11 -11
- package/assets/CLAUDE.md +11 -11
- package/bunfig.toml +8 -8
- package/package.json +80 -80
- package/packs/backend/node.json +173 -173
- package/packs/core/javascript.json +176 -176
- package/packs/core/typescript.json +222 -222
- package/packs/frontend/react.json +254 -254
- package/packs/meta/testing.json +172 -172
- package/src/automation/auto-context.ts +240 -240
- package/src/automation/decision-detector.ts +452 -452
- package/src/automation/index.ts +11 -11
- package/src/automation/phase12-manager.ts +456 -456
- package/src/automation/proactive-recall.ts +373 -373
- package/src/automation/project-detector.ts +310 -310
- package/src/automation/repo-scanner.ts +205 -205
- package/src/cli/auto-setup.ts +82 -82
- package/src/cli/bin.ts +202 -202
- package/src/cli/commands/chroma.ts +573 -573
- package/src/cli/commands/git-hook.ts +189 -189
- package/src/cli/commands/hooks.ts +213 -213
- package/src/cli/commands/init.ts +122 -122
- package/src/cli/commands/install-mcp.ts +92 -92
- package/src/cli/commands/pack.ts +197 -197
- package/src/cli/commands/serve.ts +167 -167
- package/src/cli/commands/start.ts +42 -42
- package/src/cli/commands/uninstall-mcp.ts +41 -41
- package/src/cli/commands/update.ts +121 -121
- package/src/cli/diagnose.ts +4 -4
- package/src/cli/health-check.ts +4 -4
- package/src/cli/migrate-chroma.ts +106 -106
- package/src/cli/setup.ts +4 -4
- package/src/cli/ui/animations.ts +80 -80
- package/src/cli/ui/components.ts +82 -82
- package/src/cli/ui/index.ts +4 -4
- package/src/cli/ui/logo.ts +36 -36
- package/src/cli/ui/theme.ts +55 -55
- package/src/config/defaults.ts +50 -50
- package/src/config/home.ts +55 -55
- package/src/config/index.ts +7 -7
- package/src/config/loader.ts +166 -166
- package/src/config/migration.ts +76 -76
- package/src/config/schema.ts +360 -360
- package/src/config/validator.ts +184 -184
- package/src/config/watcher.ts +86 -86
- package/src/context/assembler.ts +398 -398
- package/src/context/cache-manager.ts +101 -101
- package/src/context/formatter.ts +84 -84
- package/src/context/hierarchy.ts +85 -85
- package/src/context/index.ts +83 -83
- package/src/context/progress-tracker.ts +174 -174
- package/src/context/standards-manager.ts +287 -287
- package/src/context/types.ts +252 -252
- package/src/context/validator.ts +58 -58
- package/src/diagnostics/index.ts +123 -123
- package/src/health/index.ts +229 -229
- package/src/hooks/brain-hook.ts +112 -112
- package/src/hooks/capture.ts +168 -168
- package/src/hooks/deduplicator.ts +72 -72
- package/src/hooks/git-capture.ts +109 -109
- package/src/hooks/git-hook-installer.ts +207 -207
- package/src/hooks/index.ts +20 -20
- package/src/hooks/installer.ts +191 -194
- package/src/hooks/passive-classifier.ts +366 -366
- package/src/hooks/queue.ts +129 -129
- package/src/hooks/session-tracker.ts +275 -275
- package/src/hooks/types.ts +47 -47
- package/src/index.ts +7 -7
- package/src/intelligence/cross-project/affinity.ts +162 -162
- package/src/intelligence/cross-project/generalizer.ts +283 -283
- package/src/intelligence/cross-project/index.ts +13 -13
- package/src/intelligence/cross-project/transfer.ts +201 -201
- package/src/intelligence/index.ts +24 -24
- package/src/intelligence/optimization/index.ts +10 -10
- package/src/intelligence/optimization/precompute.ts +202 -202
- package/src/intelligence/optimization/semantic-cache.ts +207 -207
- package/src/intelligence/prediction/context-anticipator.ts +198 -198
- package/src/intelligence/prediction/decision-predictor.ts +184 -184
- package/src/intelligence/prediction/index.ts +13 -13
- package/src/intelligence/prediction/recommender.ts +268 -268
- package/src/intelligence/reasoning/chain-retrieval.ts +247 -247
- package/src/intelligence/reasoning/counterfactual.ts +248 -248
- package/src/intelligence/reasoning/index.ts +13 -13
- package/src/intelligence/reasoning/synthesizer.ts +169 -169
- package/src/intelligence/temporal/evolution.ts +197 -197
- package/src/intelligence/temporal/index.ts +16 -16
- package/src/intelligence/temporal/query-processor.ts +190 -190
- package/src/intelligence/temporal/timeline.ts +259 -259
- package/src/intelligence/temporal/trends.ts +263 -263
- package/src/knowledge/entity-extractor.ts +416 -416
- package/src/knowledge/graph/builder.ts +185 -185
- package/src/knowledge/graph/linker.ts +201 -201
- package/src/knowledge/graph/memory-graph.ts +359 -359
- package/src/knowledge/graph/schema.ts +99 -99
- package/src/knowledge/graph/search.ts +168 -168
- package/src/knowledge/relationship-extractor.ts +108 -108
- package/src/memory/chroma/client.ts +174 -174
- package/src/memory/chroma/collection-manager.ts +94 -94
- package/src/memory/chroma/config.ts +57 -57
- package/src/memory/chroma/embeddings.ts +153 -153
- package/src/memory/chroma/index.ts +82 -82
- package/src/memory/chroma/migration.ts +270 -270
- package/src/memory/chroma/schemas.ts +69 -69
- package/src/memory/chroma/search.ts +315 -315
- package/src/memory/chroma/store.ts +741 -741
- package/src/memory/consolidation/archiver.ts +164 -164
- package/src/memory/consolidation/merger.ts +186 -186
- package/src/memory/consolidation/scorer.ts +138 -138
- package/src/memory/context-builder.ts +236 -236
- package/src/memory/database.ts +169 -169
- package/src/memory/embedding-utils.ts +156 -156
- package/src/memory/embeddings.ts +226 -226
- package/src/memory/episodic/detector.ts +108 -108
- package/src/memory/episodic/manager.ts +351 -351
- package/src/memory/episodic/summarizer.ts +179 -179
- package/src/memory/episodic/types.ts +52 -52
- package/src/memory/index.ts +582 -582
- package/src/memory/knowledge-extractor.ts +455 -455
- package/src/memory/learning.ts +378 -378
- package/src/memory/patterns.ts +396 -396
- package/src/memory/schema.ts +88 -88
- package/src/memory/search.ts +309 -309
- package/src/memory/store.ts +787 -787
- package/src/memory/types.ts +121 -121
- package/src/orchestrator/coordinator.ts +272 -272
- package/src/orchestrator/decision-logger.ts +228 -228
- package/src/orchestrator/event-emitter.ts +198 -198
- package/src/orchestrator/event-queue.ts +184 -184
- package/src/orchestrator/handlers/base-handler.ts +70 -70
- package/src/orchestrator/handlers/context-handler.ts +73 -73
- package/src/orchestrator/handlers/decision-handler.ts +204 -204
- package/src/orchestrator/handlers/index.ts +10 -10
- package/src/orchestrator/handlers/status-handler.ts +131 -131
- package/src/orchestrator/handlers/task-handler.ts +171 -171
- package/src/orchestrator/index.ts +275 -275
- package/src/orchestrator/task-parser.ts +284 -284
- package/src/orchestrator/types.ts +98 -98
- package/src/packs/index.ts +9 -9
- package/src/packs/loader.ts +134 -134
- package/src/packs/manager.ts +204 -204
- package/src/packs/ranker.ts +78 -78
- package/src/packs/types.ts +81 -81
- package/src/phase12/index.ts +5 -5
- package/src/retrieval/bm25/index.ts +300 -300
- package/src/retrieval/bm25/tokenizer.ts +184 -184
- package/src/retrieval/feedback/adaptive.ts +223 -223
- package/src/retrieval/feedback/index.ts +16 -16
- package/src/retrieval/feedback/metrics.ts +223 -223
- package/src/retrieval/feedback/store.ts +283 -283
- package/src/retrieval/fusion/index.ts +194 -194
- package/src/retrieval/fusion/rrf.ts +163 -163
- package/src/retrieval/index.ts +12 -12
- package/src/retrieval/pipeline.ts +375 -375
- package/src/retrieval/query/expander.ts +198 -198
- package/src/retrieval/query/index.ts +27 -27
- package/src/retrieval/query/intent-classifier.ts +236 -236
- package/src/retrieval/query/temporal-parser.ts +295 -295
- package/src/retrieval/reranker/index.ts +188 -188
- package/src/retrieval/reranker/model.ts +95 -95
- package/src/retrieval/service.ts +125 -125
- package/src/retrieval/types.ts +162 -162
- package/src/routing/entity-extractor.ts +428 -428
- package/src/routing/intent-classifier.ts +436 -436
- package/src/routing/response-filter.ts +258 -254
- package/src/routing/router.ts +1322 -1314
- package/src/routing/search-engine.ts +475 -475
- package/src/routing/types.ts +94 -84
- package/src/scripts/health-check.ts +118 -118
- package/src/scripts/setup.ts +122 -122
- package/src/server/handlers/call-tool.ts +156 -156
- package/src/server/handlers/index.ts +9 -9
- package/src/server/handlers/list-tools.ts +35 -35
- package/src/server/handlers/tools/analyze-decision-evolution.ts +151 -151
- package/src/server/handlers/tools/auto-remember.ts +200 -200
- package/src/server/handlers/tools/brain.ts +85 -85
- package/src/server/handlers/tools/create-project.ts +135 -135
- package/src/server/handlers/tools/detect-trends.ts +144 -144
- package/src/server/handlers/tools/find-cross-project-patterns.ts +168 -168
- package/src/server/handlers/tools/get-activity-log.ts +194 -194
- package/src/server/handlers/tools/get-code-standards.ts +124 -124
- package/src/server/handlers/tools/get-corrections.ts +154 -154
- package/src/server/handlers/tools/get-decision-timeline.ts +172 -172
- package/src/server/handlers/tools/get-episode.ts +103 -103
- package/src/server/handlers/tools/get-patterns.ts +158 -158
- package/src/server/handlers/tools/get-phase12-status.ts +63 -63
- package/src/server/handlers/tools/get-project-context.ts +75 -75
- package/src/server/handlers/tools/get-recommendations.ts +145 -145
- package/src/server/handlers/tools/index.ts +31 -31
- package/src/server/handlers/tools/init-project.ts +757 -757
- package/src/server/handlers/tools/list-episodes.ts +90 -90
- package/src/server/handlers/tools/list-projects.ts +125 -125
- package/src/server/handlers/tools/rate-memory.ts +101 -101
- package/src/server/handlers/tools/recall-similar.ts +87 -87
- package/src/server/handlers/tools/recognize-pattern.ts +126 -126
- package/src/server/handlers/tools/record-correction.ts +125 -125
- package/src/server/handlers/tools/remember-decision.ts +153 -153
- package/src/server/handlers/tools/schemas.ts +253 -253
- package/src/server/handlers/tools/search-knowledge-graph.ts +102 -102
- package/src/server/handlers/tools/smart-context.ts +146 -146
- package/src/server/handlers/tools/update-progress.ts +131 -131
- package/src/server/handlers/tools/what-if-analysis.ts +135 -135
- package/src/server/http-api.ts +693 -693
- package/src/server/index.ts +40 -40
- package/src/server/mcp-server.ts +283 -283
- package/src/server/providers/index.ts +7 -7
- package/src/server/providers/prompts.ts +327 -327
- package/src/server/providers/resources.ts +622 -622
- package/src/server/services.ts +468 -468
- package/src/server/types.ts +39 -39
- package/src/server/utils/error-handler.ts +155 -155
- package/src/server/utils/index.ts +13 -13
- package/src/server/utils/memory-indicator.ts +83 -83
- package/src/server/utils/request-context.ts +122 -122
- package/src/server/utils/response-formatter.ts +129 -124
- package/src/server/utils/validators.ts +210 -210
- package/src/setup/index.ts +48 -48
- package/src/setup/wizard.ts +461 -461
- package/src/tools/index.ts +24 -24
- package/src/tools/registry.ts +115 -115
- package/src/tools/schemas.test.ts +30 -30
- package/src/tools/schemas.ts +617 -617
- package/src/tools/types.ts +412 -412
- package/src/utils/circuit-breaker.ts +130 -130
- package/src/utils/cleanup.ts +34 -34
- package/src/utils/error-handler.ts +132 -132
- package/src/utils/error-messages.ts +60 -60
- package/src/utils/fallback.ts +45 -45
- package/src/utils/index.ts +54 -54
- package/src/utils/logger-utils.ts +80 -80
- package/src/utils/logger.ts +88 -88
- package/src/utils/phase12-helper.ts +56 -56
- package/src/utils/retry.ts +94 -94
- package/src/utils/timing.ts +47 -47
- package/src/utils/transaction.ts +63 -63
- package/src/vault/frontmatter.ts +264 -264
- package/src/vault/index.ts +318 -318
- package/src/vault/paths.ts +106 -106
- package/src/vault/query.ts +422 -422
- package/src/vault/reader.ts +264 -264
- package/src/vault/templates.ts +186 -186
- package/src/vault/types.ts +73 -73
- package/src/vault/watcher.ts +277 -277
- package/src/vault/writer.ts +413 -413
- package/tsconfig.json +30 -30
|
@@ -1,272 +1,272 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgentCoordinator - Coordinate multiple operations and route events
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { Logger } from 'pino'
|
|
6
|
-
import type { Event, Operation, CoordinatorStats } from './types'
|
|
7
|
-
import { EventBus } from './event-emitter'
|
|
8
|
-
import { EventQueue } from './event-queue'
|
|
9
|
-
|
|
10
|
-
export class AgentCoordinator {
|
|
11
|
-
private logger: Logger
|
|
12
|
-
private eventBus: EventBus
|
|
13
|
-
private eventQueue: EventQueue
|
|
14
|
-
private operations: Map<string, Operation>
|
|
15
|
-
private maxConcurrent: number
|
|
16
|
-
private processingPromise: Promise<void> | null = null
|
|
17
|
-
private shouldStop: boolean = false
|
|
18
|
-
|
|
19
|
-
constructor(
|
|
20
|
-
logger: Logger,
|
|
21
|
-
eventBus: EventBus,
|
|
22
|
-
maxConcurrent: number = 5
|
|
23
|
-
) {
|
|
24
|
-
this.logger = logger.child({ component: 'agent-coordinator' })
|
|
25
|
-
this.eventBus = eventBus
|
|
26
|
-
this.eventQueue = new EventQueue(logger)
|
|
27
|
-
this.operations = new Map()
|
|
28
|
-
this.maxConcurrent = maxConcurrent
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Route event to appropriate handler via queue
|
|
33
|
-
*/
|
|
34
|
-
async routeEvent(event: Event): Promise<void> {
|
|
35
|
-
this.logger.debug(
|
|
36
|
-
{ eventId: event.id, type: event.type },
|
|
37
|
-
'Routing event'
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
// Add to queue
|
|
41
|
-
this.eventQueue.enqueue(event)
|
|
42
|
-
|
|
43
|
-
// Start processing if not already running
|
|
44
|
-
if (!this.processingPromise && !this.shouldStop) {
|
|
45
|
-
this.processingPromise = this.processQueue()
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Process event queue
|
|
51
|
-
*/
|
|
52
|
-
private async processQueue(): Promise<void> {
|
|
53
|
-
this.eventQueue.setProcessing(true)
|
|
54
|
-
|
|
55
|
-
try {
|
|
56
|
-
while (!this.shouldStop && !this.eventQueue.isEmpty()) {
|
|
57
|
-
const running = this.getRunningCount()
|
|
58
|
-
|
|
59
|
-
if (running >= this.maxConcurrent) {
|
|
60
|
-
this.logger.debug('Max concurrent operations reached, waiting')
|
|
61
|
-
await this.waitForCompletion()
|
|
62
|
-
continue
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const item = this.eventQueue.dequeue()
|
|
66
|
-
if (!item) {
|
|
67
|
-
// No ready items, wait a bit
|
|
68
|
-
await new Promise(resolve => setTimeout(resolve, 100))
|
|
69
|
-
continue
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Process the event
|
|
73
|
-
await this.processEvent(item)
|
|
74
|
-
}
|
|
75
|
-
} finally {
|
|
76
|
-
this.eventQueue.setProcessing(false)
|
|
77
|
-
this.processingPromise = null
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Process a single event
|
|
83
|
-
*/
|
|
84
|
-
private async processEvent(item: ReturnType<EventQueue['dequeue']>): Promise<void> {
|
|
85
|
-
if (!item) return
|
|
86
|
-
|
|
87
|
-
const operation: Operation = {
|
|
88
|
-
id: item.event.id,
|
|
89
|
-
type: item.event.type,
|
|
90
|
-
status: 'running',
|
|
91
|
-
startTime: new Date()
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
this.operations.set(operation.id, operation)
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
// Emit event to bus (handlers will process)
|
|
98
|
-
this.eventBus.emitEvent(
|
|
99
|
-
item.event.type,
|
|
100
|
-
item.event.payload,
|
|
101
|
-
'coordinator',
|
|
102
|
-
item.event.priority
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
operation.status = 'completed'
|
|
106
|
-
operation.endTime = new Date()
|
|
107
|
-
|
|
108
|
-
this.logger.debug(
|
|
109
|
-
{ operationId: operation.id, duration: this.getDuration(operation) },
|
|
110
|
-
'Operation completed'
|
|
111
|
-
)
|
|
112
|
-
} catch (error) {
|
|
113
|
-
operation.status = 'failed'
|
|
114
|
-
operation.error = error
|
|
115
|
-
operation.endTime = new Date()
|
|
116
|
-
|
|
117
|
-
this.logger.error(
|
|
118
|
-
{ error, operationId: operation.id },
|
|
119
|
-
'Operation failed'
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
// Reschedule if retriable
|
|
123
|
-
const delayMs = Math.pow(2, item.retries) * 1000 // Exponential backoff
|
|
124
|
-
const rescheduled = this.eventQueue.reschedule(item, delayMs)
|
|
125
|
-
|
|
126
|
-
if (!rescheduled) {
|
|
127
|
-
this.logger.error(
|
|
128
|
-
{ operationId: operation.id },
|
|
129
|
-
'Operation exhausted retries'
|
|
130
|
-
)
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Get operation duration in milliseconds
|
|
137
|
-
*/
|
|
138
|
-
private getDuration(operation: Operation): number | undefined {
|
|
139
|
-
if (operation.startTime && operation.endTime) {
|
|
140
|
-
return operation.endTime.getTime() - operation.startTime.getTime()
|
|
141
|
-
}
|
|
142
|
-
return undefined
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Wait for at least one operation to complete
|
|
147
|
-
*/
|
|
148
|
-
private async waitForCompletion(): Promise<void> {
|
|
149
|
-
// Simple polling approach
|
|
150
|
-
const checkInterval = 100
|
|
151
|
-
const maxWait = 10000
|
|
152
|
-
let waited = 0
|
|
153
|
-
|
|
154
|
-
while (waited < maxWait) {
|
|
155
|
-
const running = this.getRunningCount()
|
|
156
|
-
if (running < this.maxConcurrent) {
|
|
157
|
-
return
|
|
158
|
-
}
|
|
159
|
-
await new Promise(resolve => setTimeout(resolve, checkInterval))
|
|
160
|
-
waited += checkInterval
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Get count of running operations
|
|
166
|
-
*/
|
|
167
|
-
private getRunningCount(): number {
|
|
168
|
-
return Array.from(this.operations.values())
|
|
169
|
-
.filter(op => op.status === 'running').length
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Get operation by ID
|
|
174
|
-
*/
|
|
175
|
-
getOperation(id: string): Operation | undefined {
|
|
176
|
-
return this.operations.get(id)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Get all operations, optionally filtered by status
|
|
181
|
-
*/
|
|
182
|
-
getOperations(status?: Operation['status']): Operation[] {
|
|
183
|
-
const ops = Array.from(this.operations.values())
|
|
184
|
-
|
|
185
|
-
if (status) {
|
|
186
|
-
return ops.filter(op => op.status === status)
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return ops
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Get coordinator stats
|
|
194
|
-
*/
|
|
195
|
-
getStats(): CoordinatorStats {
|
|
196
|
-
const statuses = new Map<string, number>()
|
|
197
|
-
|
|
198
|
-
for (const op of this.operations.values()) {
|
|
199
|
-
const count = statuses.get(op.status) || 0
|
|
200
|
-
statuses.set(op.status, count + 1)
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return {
|
|
204
|
-
totalOperations: this.operations.size,
|
|
205
|
-
queueSize: this.eventQueue.size(),
|
|
206
|
-
operationsByStatus: Object.fromEntries(statuses),
|
|
207
|
-
maxConcurrent: this.maxConcurrent
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Clear completed operations to free memory
|
|
213
|
-
*/
|
|
214
|
-
clearCompleted(): number {
|
|
215
|
-
let cleared = 0
|
|
216
|
-
|
|
217
|
-
for (const [id, op] of this.operations) {
|
|
218
|
-
if (op.status === 'completed' || op.status === 'failed') {
|
|
219
|
-
this.operations.delete(id)
|
|
220
|
-
cleared++
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
if (cleared > 0) {
|
|
225
|
-
this.logger.debug({ cleared }, 'Completed operations cleared')
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
return cleared
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
* Stop the coordinator
|
|
233
|
-
*/
|
|
234
|
-
stop(): void {
|
|
235
|
-
this.shouldStop = true
|
|
236
|
-
this.eventQueue.clear()
|
|
237
|
-
this.logger.info('Coordinator stopped')
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Resume the coordinator
|
|
242
|
-
*/
|
|
243
|
-
resume(): void {
|
|
244
|
-
this.shouldStop = false
|
|
245
|
-
this.logger.info('Coordinator resumed')
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Check if coordinator is running
|
|
250
|
-
*/
|
|
251
|
-
isRunning(): boolean {
|
|
252
|
-
return !this.shouldStop && this.processingPromise !== null
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Get queue status
|
|
257
|
-
*/
|
|
258
|
-
getQueueStatus() {
|
|
259
|
-
return this.eventQueue.getStatus()
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Set max concurrent operations
|
|
264
|
-
*/
|
|
265
|
-
setMaxConcurrent(value: number): void {
|
|
266
|
-
if (value < 1) {
|
|
267
|
-
throw new Error('Max concurrent must be at least 1')
|
|
268
|
-
}
|
|
269
|
-
this.maxConcurrent = value
|
|
270
|
-
this.logger.info({ maxConcurrent: value }, 'Max concurrent updated')
|
|
271
|
-
}
|
|
272
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* AgentCoordinator - Coordinate multiple operations and route events
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Logger } from 'pino'
|
|
6
|
+
import type { Event, Operation, CoordinatorStats } from './types'
|
|
7
|
+
import { EventBus } from './event-emitter'
|
|
8
|
+
import { EventQueue } from './event-queue'
|
|
9
|
+
|
|
10
|
+
export class AgentCoordinator {
|
|
11
|
+
private logger: Logger
|
|
12
|
+
private eventBus: EventBus
|
|
13
|
+
private eventQueue: EventQueue
|
|
14
|
+
private operations: Map<string, Operation>
|
|
15
|
+
private maxConcurrent: number
|
|
16
|
+
private processingPromise: Promise<void> | null = null
|
|
17
|
+
private shouldStop: boolean = false
|
|
18
|
+
|
|
19
|
+
constructor(
|
|
20
|
+
logger: Logger,
|
|
21
|
+
eventBus: EventBus,
|
|
22
|
+
maxConcurrent: number = 5
|
|
23
|
+
) {
|
|
24
|
+
this.logger = logger.child({ component: 'agent-coordinator' })
|
|
25
|
+
this.eventBus = eventBus
|
|
26
|
+
this.eventQueue = new EventQueue(logger)
|
|
27
|
+
this.operations = new Map()
|
|
28
|
+
this.maxConcurrent = maxConcurrent
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Route event to appropriate handler via queue
|
|
33
|
+
*/
|
|
34
|
+
async routeEvent(event: Event): Promise<void> {
|
|
35
|
+
this.logger.debug(
|
|
36
|
+
{ eventId: event.id, type: event.type },
|
|
37
|
+
'Routing event'
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
// Add to queue
|
|
41
|
+
this.eventQueue.enqueue(event)
|
|
42
|
+
|
|
43
|
+
// Start processing if not already running
|
|
44
|
+
if (!this.processingPromise && !this.shouldStop) {
|
|
45
|
+
this.processingPromise = this.processQueue()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Process event queue
|
|
51
|
+
*/
|
|
52
|
+
private async processQueue(): Promise<void> {
|
|
53
|
+
this.eventQueue.setProcessing(true)
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
while (!this.shouldStop && !this.eventQueue.isEmpty()) {
|
|
57
|
+
const running = this.getRunningCount()
|
|
58
|
+
|
|
59
|
+
if (running >= this.maxConcurrent) {
|
|
60
|
+
this.logger.debug('Max concurrent operations reached, waiting')
|
|
61
|
+
await this.waitForCompletion()
|
|
62
|
+
continue
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const item = this.eventQueue.dequeue()
|
|
66
|
+
if (!item) {
|
|
67
|
+
// No ready items, wait a bit
|
|
68
|
+
await new Promise(resolve => setTimeout(resolve, 100))
|
|
69
|
+
continue
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Process the event
|
|
73
|
+
await this.processEvent(item)
|
|
74
|
+
}
|
|
75
|
+
} finally {
|
|
76
|
+
this.eventQueue.setProcessing(false)
|
|
77
|
+
this.processingPromise = null
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Process a single event
|
|
83
|
+
*/
|
|
84
|
+
private async processEvent(item: ReturnType<EventQueue['dequeue']>): Promise<void> {
|
|
85
|
+
if (!item) return
|
|
86
|
+
|
|
87
|
+
const operation: Operation = {
|
|
88
|
+
id: item.event.id,
|
|
89
|
+
type: item.event.type,
|
|
90
|
+
status: 'running',
|
|
91
|
+
startTime: new Date()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
this.operations.set(operation.id, operation)
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
// Emit event to bus (handlers will process)
|
|
98
|
+
this.eventBus.emitEvent(
|
|
99
|
+
item.event.type,
|
|
100
|
+
item.event.payload,
|
|
101
|
+
'coordinator',
|
|
102
|
+
item.event.priority
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
operation.status = 'completed'
|
|
106
|
+
operation.endTime = new Date()
|
|
107
|
+
|
|
108
|
+
this.logger.debug(
|
|
109
|
+
{ operationId: operation.id, duration: this.getDuration(operation) },
|
|
110
|
+
'Operation completed'
|
|
111
|
+
)
|
|
112
|
+
} catch (error) {
|
|
113
|
+
operation.status = 'failed'
|
|
114
|
+
operation.error = error
|
|
115
|
+
operation.endTime = new Date()
|
|
116
|
+
|
|
117
|
+
this.logger.error(
|
|
118
|
+
{ error, operationId: operation.id },
|
|
119
|
+
'Operation failed'
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
// Reschedule if retriable
|
|
123
|
+
const delayMs = Math.pow(2, item.retries) * 1000 // Exponential backoff
|
|
124
|
+
const rescheduled = this.eventQueue.reschedule(item, delayMs)
|
|
125
|
+
|
|
126
|
+
if (!rescheduled) {
|
|
127
|
+
this.logger.error(
|
|
128
|
+
{ operationId: operation.id },
|
|
129
|
+
'Operation exhausted retries'
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Get operation duration in milliseconds
|
|
137
|
+
*/
|
|
138
|
+
private getDuration(operation: Operation): number | undefined {
|
|
139
|
+
if (operation.startTime && operation.endTime) {
|
|
140
|
+
return operation.endTime.getTime() - operation.startTime.getTime()
|
|
141
|
+
}
|
|
142
|
+
return undefined
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Wait for at least one operation to complete
|
|
147
|
+
*/
|
|
148
|
+
private async waitForCompletion(): Promise<void> {
|
|
149
|
+
// Simple polling approach
|
|
150
|
+
const checkInterval = 100
|
|
151
|
+
const maxWait = 10000
|
|
152
|
+
let waited = 0
|
|
153
|
+
|
|
154
|
+
while (waited < maxWait) {
|
|
155
|
+
const running = this.getRunningCount()
|
|
156
|
+
if (running < this.maxConcurrent) {
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
await new Promise(resolve => setTimeout(resolve, checkInterval))
|
|
160
|
+
waited += checkInterval
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Get count of running operations
|
|
166
|
+
*/
|
|
167
|
+
private getRunningCount(): number {
|
|
168
|
+
return Array.from(this.operations.values())
|
|
169
|
+
.filter(op => op.status === 'running').length
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Get operation by ID
|
|
174
|
+
*/
|
|
175
|
+
getOperation(id: string): Operation | undefined {
|
|
176
|
+
return this.operations.get(id)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Get all operations, optionally filtered by status
|
|
181
|
+
*/
|
|
182
|
+
getOperations(status?: Operation['status']): Operation[] {
|
|
183
|
+
const ops = Array.from(this.operations.values())
|
|
184
|
+
|
|
185
|
+
if (status) {
|
|
186
|
+
return ops.filter(op => op.status === status)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return ops
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Get coordinator stats
|
|
194
|
+
*/
|
|
195
|
+
getStats(): CoordinatorStats {
|
|
196
|
+
const statuses = new Map<string, number>()
|
|
197
|
+
|
|
198
|
+
for (const op of this.operations.values()) {
|
|
199
|
+
const count = statuses.get(op.status) || 0
|
|
200
|
+
statuses.set(op.status, count + 1)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
totalOperations: this.operations.size,
|
|
205
|
+
queueSize: this.eventQueue.size(),
|
|
206
|
+
operationsByStatus: Object.fromEntries(statuses),
|
|
207
|
+
maxConcurrent: this.maxConcurrent
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Clear completed operations to free memory
|
|
213
|
+
*/
|
|
214
|
+
clearCompleted(): number {
|
|
215
|
+
let cleared = 0
|
|
216
|
+
|
|
217
|
+
for (const [id, op] of this.operations) {
|
|
218
|
+
if (op.status === 'completed' || op.status === 'failed') {
|
|
219
|
+
this.operations.delete(id)
|
|
220
|
+
cleared++
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (cleared > 0) {
|
|
225
|
+
this.logger.debug({ cleared }, 'Completed operations cleared')
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return cleared
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Stop the coordinator
|
|
233
|
+
*/
|
|
234
|
+
stop(): void {
|
|
235
|
+
this.shouldStop = true
|
|
236
|
+
this.eventQueue.clear()
|
|
237
|
+
this.logger.info('Coordinator stopped')
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Resume the coordinator
|
|
242
|
+
*/
|
|
243
|
+
resume(): void {
|
|
244
|
+
this.shouldStop = false
|
|
245
|
+
this.logger.info('Coordinator resumed')
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Check if coordinator is running
|
|
250
|
+
*/
|
|
251
|
+
isRunning(): boolean {
|
|
252
|
+
return !this.shouldStop && this.processingPromise !== null
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Get queue status
|
|
257
|
+
*/
|
|
258
|
+
getQueueStatus() {
|
|
259
|
+
return this.eventQueue.getStatus()
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Set max concurrent operations
|
|
264
|
+
*/
|
|
265
|
+
setMaxConcurrent(value: number): void {
|
|
266
|
+
if (value < 1) {
|
|
267
|
+
throw new Error('Max concurrent must be at least 1')
|
|
268
|
+
}
|
|
269
|
+
this.maxConcurrent = value
|
|
270
|
+
this.logger.info({ maxConcurrent: value }, 'Max concurrent updated')
|
|
271
|
+
}
|
|
272
|
+
}
|