confused-ai-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/FEATURES.md +169 -0
  2. package/package.json +119 -0
  3. package/src/agent.ts +187 -0
  4. package/src/agentic/index.ts +87 -0
  5. package/src/agentic/runner.ts +386 -0
  6. package/src/agentic/types.ts +91 -0
  7. package/src/artifacts/artifact.ts +417 -0
  8. package/src/artifacts/index.ts +42 -0
  9. package/src/artifacts/media.ts +304 -0
  10. package/src/cli/index.ts +122 -0
  11. package/src/core/base-agent.ts +151 -0
  12. package/src/core/context-builder.ts +106 -0
  13. package/src/core/index.ts +8 -0
  14. package/src/core/schemas.ts +17 -0
  15. package/src/core/types.ts +158 -0
  16. package/src/create-agent.ts +309 -0
  17. package/src/debug-logger.ts +188 -0
  18. package/src/dx/agent.ts +88 -0
  19. package/src/dx/define-agent.ts +183 -0
  20. package/src/dx/dev-logger.ts +57 -0
  21. package/src/dx/index.ts +11 -0
  22. package/src/errors.ts +175 -0
  23. package/src/execution/engine.ts +522 -0
  24. package/src/execution/graph-builder.ts +362 -0
  25. package/src/execution/index.ts +8 -0
  26. package/src/execution/types.ts +257 -0
  27. package/src/execution/worker-pool.ts +308 -0
  28. package/src/extensions/index.ts +123 -0
  29. package/src/guardrails/allowlist.ts +155 -0
  30. package/src/guardrails/index.ts +17 -0
  31. package/src/guardrails/types.ts +159 -0
  32. package/src/guardrails/validator.ts +265 -0
  33. package/src/index.ts +74 -0
  34. package/src/knowledge/index.ts +5 -0
  35. package/src/knowledge/types.ts +52 -0
  36. package/src/learning/in-memory-store.ts +72 -0
  37. package/src/learning/index.ts +6 -0
  38. package/src/learning/types.ts +42 -0
  39. package/src/llm/cache.ts +300 -0
  40. package/src/llm/index.ts +22 -0
  41. package/src/llm/model-resolver.ts +81 -0
  42. package/src/llm/openai-provider.ts +313 -0
  43. package/src/llm/openrouter-provider.ts +29 -0
  44. package/src/llm/types.ts +131 -0
  45. package/src/memory/in-memory-store.ts +255 -0
  46. package/src/memory/index.ts +7 -0
  47. package/src/memory/types.ts +193 -0
  48. package/src/memory/vector-store.ts +251 -0
  49. package/src/observability/console-logger.ts +123 -0
  50. package/src/observability/index.ts +12 -0
  51. package/src/observability/metrics.ts +85 -0
  52. package/src/observability/otlp-exporter.ts +417 -0
  53. package/src/observability/tracer.ts +105 -0
  54. package/src/observability/types.ts +341 -0
  55. package/src/orchestration/agent-adapter.ts +33 -0
  56. package/src/orchestration/index.ts +34 -0
  57. package/src/orchestration/load-balancer.ts +151 -0
  58. package/src/orchestration/mcp-types.ts +59 -0
  59. package/src/orchestration/message-bus.ts +192 -0
  60. package/src/orchestration/orchestrator.ts +349 -0
  61. package/src/orchestration/pipeline.ts +66 -0
  62. package/src/orchestration/supervisor.ts +107 -0
  63. package/src/orchestration/swarm.ts +1099 -0
  64. package/src/orchestration/toolkit.ts +47 -0
  65. package/src/orchestration/types.ts +339 -0
  66. package/src/planner/classical-planner.ts +383 -0
  67. package/src/planner/index.ts +8 -0
  68. package/src/planner/llm-planner.ts +353 -0
  69. package/src/planner/types.ts +227 -0
  70. package/src/planner/validator.ts +297 -0
  71. package/src/production/circuit-breaker.ts +290 -0
  72. package/src/production/graceful-shutdown.ts +251 -0
  73. package/src/production/health.ts +333 -0
  74. package/src/production/index.ts +57 -0
  75. package/src/production/latency-eval.ts +62 -0
  76. package/src/production/rate-limiter.ts +287 -0
  77. package/src/production/resumable-stream.ts +289 -0
  78. package/src/production/types.ts +81 -0
  79. package/src/sdk/index.ts +374 -0
  80. package/src/session/db-driver.ts +50 -0
  81. package/src/session/in-memory-store.ts +235 -0
  82. package/src/session/index.ts +12 -0
  83. package/src/session/sql-store.ts +315 -0
  84. package/src/session/sqlite-store.ts +61 -0
  85. package/src/session/types.ts +153 -0
  86. package/src/tools/base-tool.ts +223 -0
  87. package/src/tools/browser-tool.ts +123 -0
  88. package/src/tools/calculator-tool.ts +265 -0
  89. package/src/tools/file-tools.ts +394 -0
  90. package/src/tools/github-tool.ts +432 -0
  91. package/src/tools/hackernews-tool.ts +187 -0
  92. package/src/tools/http-tool.ts +118 -0
  93. package/src/tools/index.ts +99 -0
  94. package/src/tools/jira-tool.ts +373 -0
  95. package/src/tools/notion-tool.ts +322 -0
  96. package/src/tools/openai-tool.ts +236 -0
  97. package/src/tools/registry.ts +131 -0
  98. package/src/tools/serpapi-tool.ts +234 -0
  99. package/src/tools/shell-tool.ts +118 -0
  100. package/src/tools/slack-tool.ts +327 -0
  101. package/src/tools/telegram-tool.ts +127 -0
  102. package/src/tools/types.ts +229 -0
  103. package/src/tools/websearch-tool.ts +335 -0
  104. package/src/tools/wikipedia-tool.ts +177 -0
  105. package/src/tools/yfinance-tool.ts +33 -0
  106. package/src/voice/index.ts +17 -0
  107. package/src/voice/voice-provider.ts +228 -0
  108. package/tests/artifact.test.ts +241 -0
  109. package/tests/circuit-breaker.test.ts +171 -0
  110. package/tests/health.test.ts +192 -0
  111. package/tests/llm-cache.test.ts +186 -0
  112. package/tests/rate-limiter.test.ts +161 -0
  113. package/tsconfig.json +29 -0
  114. package/vitest.config.ts +47 -0
@@ -0,0 +1,105 @@
1
+ /**
2
+ * In-Memory Tracer Implementation
3
+ *
4
+ * Simple in-memory tracing for development and debugging
5
+ */
6
+
7
+ import { Tracer, TraceSpan, SpanStatus, SpanEvent } from './types.js';
8
+ import type { EntityId } from '../core/types.js';
9
+
10
+ /**
11
+ * In-memory tracer implementation
12
+ */
13
+ export class InMemoryTracer implements Tracer {
14
+ private spans: Map<EntityId, TraceSpan> = new Map();
15
+ private currentSpanId?: EntityId;
16
+
17
+ startSpan(name: string, parentId?: EntityId): TraceSpan {
18
+ const id = this.generateId();
19
+ const traceId = parentId
20
+ ? this.spans.get(parentId)?.traceId ?? this.generateTraceId()
21
+ : this.generateTraceId();
22
+
23
+ const span: TraceSpan = {
24
+ id,
25
+ traceId,
26
+ parentId,
27
+ name,
28
+ startTime: new Date(),
29
+ status: SpanStatus.UNSET,
30
+ attributes: {},
31
+ events: [],
32
+ };
33
+
34
+ this.spans.set(id, span);
35
+ this.currentSpanId = id;
36
+
37
+ return span;
38
+ }
39
+
40
+ endSpan(spanId: EntityId, status: SpanStatus = SpanStatus.OK): void {
41
+ const span = this.spans.get(spanId);
42
+ if (span) {
43
+ Object.assign(span, {
44
+ endTime: new Date(),
45
+ status,
46
+ });
47
+ }
48
+
49
+ if (this.currentSpanId === spanId) {
50
+ this.currentSpanId = undefined;
51
+ }
52
+ }
53
+
54
+ addEvent(spanId: EntityId, event: Omit<SpanEvent, 'timestamp'>): void {
55
+ const span = this.spans.get(spanId);
56
+ if (span) {
57
+ span.events.push({
58
+ ...event,
59
+ timestamp: new Date(),
60
+ });
61
+ }
62
+ }
63
+
64
+ setAttributes(spanId: EntityId, attributes: Record<string, unknown>): void {
65
+ const span = this.spans.get(spanId);
66
+ if (span) {
67
+ Object.assign(span.attributes, attributes);
68
+ }
69
+ }
70
+
71
+ getSpan(spanId: EntityId): TraceSpan | undefined {
72
+ return this.spans.get(spanId);
73
+ }
74
+
75
+ getCurrentSpan(): TraceSpan | undefined {
76
+ return this.currentSpanId ? this.spans.get(this.currentSpanId) : undefined;
77
+ }
78
+
79
+ getTrace(traceId: string): TraceSpan[] {
80
+ return Array.from(this.spans.values()).filter(span => span.traceId === traceId);
81
+ }
82
+
83
+ /**
84
+ * Get all spans
85
+ */
86
+ getAllSpans(): TraceSpan[] {
87
+ return Array.from(this.spans.values());
88
+ }
89
+
90
+ /**
91
+ * Clear all spans
92
+ */
93
+ clear(): void {
94
+ this.spans.clear();
95
+ this.currentSpanId = undefined;
96
+ }
97
+
98
+ private generateId(): EntityId {
99
+ return `span-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
100
+ }
101
+
102
+ private generateTraceId(): string {
103
+ return `trace-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
104
+ }
105
+ }
@@ -0,0 +1,341 @@
1
+ /**
2
+ * Observability and telemetry types and interfaces
3
+ */
4
+
5
+ import type { EntityId } from '../core/types.js';
6
+ import type { AgentState } from '../core/types.js';
7
+ import type { TaskStatus } from '../planner/types.js';
8
+
9
+ /**
10
+ * Log levels
11
+ */
12
+ export enum LogLevel {
13
+ DEBUG = 'debug',
14
+ INFO = 'info',
15
+ WARN = 'warn',
16
+ ERROR = 'error',
17
+ FATAL = 'fatal',
18
+ }
19
+
20
+ /**
21
+ * Log entry
22
+ */
23
+ export interface LogEntry {
24
+ readonly id: EntityId;
25
+ readonly timestamp: Date;
26
+ readonly level: LogLevel;
27
+ readonly message: string;
28
+ readonly source: string;
29
+ readonly context: LogContext;
30
+ readonly metadata?: Record<string, unknown>;
31
+ }
32
+
33
+ /**
34
+ * Log context
35
+ */
36
+ export interface LogContext {
37
+ readonly agentId?: EntityId;
38
+ readonly taskId?: EntityId;
39
+ readonly planId?: EntityId;
40
+ readonly executionId?: EntityId;
41
+ readonly sessionId?: string;
42
+ readonly traceId?: string;
43
+ readonly spanId?: string;
44
+ readonly parentSpanId?: string;
45
+ }
46
+
47
+ /**
48
+ * Logger interface
49
+ */
50
+ export interface Logger {
51
+ /**
52
+ * Log a debug message
53
+ */
54
+ debug(message: string, context?: Partial<LogContext>, metadata?: Record<string, unknown>): void;
55
+
56
+ /**
57
+ * Log an info message
58
+ */
59
+ info(message: string, context?: Partial<LogContext>, metadata?: Record<string, unknown>): void;
60
+
61
+ /**
62
+ * Log a warning message
63
+ */
64
+ warn(message: string, context?: Partial<LogContext>, metadata?: Record<string, unknown>): void;
65
+
66
+ /**
67
+ * Log an error message
68
+ */
69
+ error(message: string, context?: Partial<LogContext>, metadata?: Record<string, unknown>): void;
70
+
71
+ /**
72
+ * Log a fatal message
73
+ */
74
+ fatal(message: string, context?: Partial<LogContext>, metadata?: Record<string, unknown>): void;
75
+
76
+ /**
77
+ * Create a child logger with additional context
78
+ */
79
+ child(additionalContext: Partial<LogContext>): Logger;
80
+ }
81
+
82
+ /**
83
+ * Log transport for outputting logs
84
+ */
85
+ export interface LogTransport {
86
+ /**
87
+ * Write a log entry
88
+ */
89
+ write(entry: LogEntry): Promise<void>;
90
+
91
+ /**
92
+ * Flush any buffered logs
93
+ */
94
+ flush(): Promise<void>;
95
+ }
96
+
97
+ /**
98
+ * Trace span representing an operation
99
+ */
100
+ export interface TraceSpan {
101
+ readonly id: EntityId;
102
+ readonly traceId: string;
103
+ readonly parentId?: EntityId;
104
+ readonly name: string;
105
+ readonly startTime: Date;
106
+ readonly endTime?: Date;
107
+ readonly status: SpanStatus;
108
+ readonly attributes: Record<string, unknown>;
109
+ readonly events: SpanEvent[];
110
+ }
111
+
112
+ /**
113
+ * Span status
114
+ */
115
+ export enum SpanStatus {
116
+ OK = 'ok',
117
+ ERROR = 'error',
118
+ UNSET = 'unset',
119
+ }
120
+
121
+ /**
122
+ * Span event
123
+ */
124
+ export interface SpanEvent {
125
+ readonly timestamp: Date;
126
+ readonly name: string;
127
+ readonly attributes?: Record<string, unknown>;
128
+ }
129
+
130
+ /**
131
+ * Tracer interface for distributed tracing
132
+ */
133
+ export interface Tracer {
134
+ /**
135
+ * Start a new span
136
+ */
137
+ startSpan(name: string, parentId?: EntityId): TraceSpan;
138
+
139
+ /**
140
+ * End a span
141
+ */
142
+ endSpan(spanId: EntityId, status?: SpanStatus): void;
143
+
144
+ /**
145
+ * Add an event to a span
146
+ */
147
+ addEvent(spanId: EntityId, event: Omit<SpanEvent, 'timestamp'>): void;
148
+
149
+ /**
150
+ * Set span attributes
151
+ */
152
+ setAttributes(spanId: EntityId, attributes: Record<string, unknown>): void;
153
+
154
+ /**
155
+ * Get a span by ID
156
+ */
157
+ getSpan(spanId: EntityId): TraceSpan | undefined;
158
+
159
+ /**
160
+ * Get the current active span
161
+ */
162
+ getCurrentSpan(): TraceSpan | undefined;
163
+
164
+ /**
165
+ * Get all spans for a trace
166
+ */
167
+ getTrace(traceId: string): TraceSpan[];
168
+ }
169
+
170
+ /**
171
+ * Metric types
172
+ */
173
+ export enum MetricType {
174
+ COUNTER = 'counter',
175
+ GAUGE = 'gauge',
176
+ HISTOGRAM = 'histogram',
177
+ SUMMARY = 'summary',
178
+ }
179
+
180
+ /**
181
+ * Metric value
182
+ */
183
+ export interface MetricValue {
184
+ readonly name: string;
185
+ readonly type: MetricType;
186
+ readonly value: number;
187
+ readonly labels: Record<string, string>;
188
+ readonly timestamp: Date;
189
+ }
190
+
191
+ /**
192
+ * Metrics collector
193
+ */
194
+ export interface MetricsCollector {
195
+ /**
196
+ * Record a counter metric
197
+ */
198
+ counter(name: string, value?: number, labels?: Record<string, string>): void;
199
+
200
+ /**
201
+ * Record a gauge metric
202
+ */
203
+ gauge(name: string, value: number, labels?: Record<string, string>): void;
204
+
205
+ /**
206
+ * Record a histogram metric
207
+ */
208
+ histogram(name: string, value: number, labels?: Record<string, string>): void;
209
+
210
+ /**
211
+ * Get all recorded metrics
212
+ */
213
+ getMetrics(): MetricValue[];
214
+
215
+ /**
216
+ * Clear all metrics
217
+ */
218
+ clear(): void;
219
+ }
220
+
221
+ /**
222
+ * Agent event for telemetry
223
+ */
224
+ export interface AgentEvent {
225
+ readonly timestamp: Date;
226
+ readonly agentId: EntityId;
227
+ readonly eventType: AgentEventType;
228
+ readonly previousState?: AgentState;
229
+ readonly currentState?: AgentState;
230
+ readonly metadata?: Record<string, unknown>;
231
+ }
232
+
233
+ /**
234
+ * Agent event types
235
+ */
236
+ export enum AgentEventType {
237
+ CREATED = 'created',
238
+ STARTED = 'started',
239
+ STATE_CHANGED = 'state_changed',
240
+ COMPLETED = 'completed',
241
+ FAILED = 'failed',
242
+ DESTROYED = 'destroyed',
243
+ }
244
+
245
+ /**
246
+ * Task event for telemetry
247
+ */
248
+ export interface TaskEvent {
249
+ readonly timestamp: Date;
250
+ readonly taskId: EntityId;
251
+ readonly agentId: EntityId;
252
+ readonly eventType: TaskEventType;
253
+ readonly previousStatus?: TaskStatus;
254
+ readonly currentStatus?: TaskStatus;
255
+ readonly executionTimeMs?: number;
256
+ readonly metadata?: Record<string, unknown>;
257
+ }
258
+
259
+ /**
260
+ * Task event types
261
+ */
262
+ export enum TaskEventType {
263
+ CREATED = 'created',
264
+ STARTED = 'started',
265
+ STATUS_CHANGED = 'status_changed',
266
+ COMPLETED = 'completed',
267
+ FAILED = 'failed',
268
+ RETRYING = 'retrying',
269
+ CANCELLED = 'cancelled',
270
+ }
271
+
272
+ /**
273
+ * Telemetry collector interface
274
+ */
275
+ export interface TelemetryCollector {
276
+ /**
277
+ * Record an agent event
278
+ */
279
+ recordAgentEvent(event: Omit<AgentEvent, 'timestamp'>): void;
280
+
281
+ /**
282
+ * Record a task event
283
+ */
284
+ recordTaskEvent(event: Omit<TaskEvent, 'timestamp'>): void;
285
+
286
+ /**
287
+ * Get agent events
288
+ */
289
+ getAgentEvents(agentId?: EntityId): AgentEvent[];
290
+
291
+ /**
292
+ * Get task events
293
+ */
294
+ getTaskEvents(taskId?: EntityId): TaskEvent[];
295
+
296
+ /**
297
+ * Get agent statistics
298
+ */
299
+ getAgentStats(agentId: EntityId): AgentStats;
300
+
301
+ /**
302
+ * Export telemetry data
303
+ */
304
+ export(format: ExportFormat): string;
305
+ }
306
+
307
+ /**
308
+ * Agent statistics
309
+ */
310
+ export interface AgentStats {
311
+ readonly agentId: EntityId;
312
+ readonly totalExecutions: number;
313
+ readonly successfulExecutions: number;
314
+ readonly failedExecutions: number;
315
+ readonly averageExecutionTimeMs: number;
316
+ readonly totalTokensUsed: number;
317
+ readonly totalCost: number;
318
+ readonly lastExecutionAt?: Date;
319
+ }
320
+
321
+ /**
322
+ * Export formats
323
+ */
324
+ export enum ExportFormat {
325
+ JSON = 'json',
326
+ CSV = 'csv',
327
+ OTLP = 'otlp',
328
+ }
329
+
330
+ /**
331
+ * Observability configuration
332
+ */
333
+ export interface ObservabilityConfig {
334
+ readonly logLevel: LogLevel;
335
+ readonly enableTracing: boolean;
336
+ readonly enableMetrics: boolean;
337
+ readonly enableTelemetry: boolean;
338
+ readonly samplingRate: number;
339
+ readonly transports: LogTransport[];
340
+ readonly exportIntervalMs?: number;
341
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Agent adapter for orchestration
3
+ *
4
+ * Wraps a run function as a core Agent so it can be registered with the Orchestrator.
5
+ */
6
+
7
+ import { Agent, AgentInput, AgentOutput, AgentContext } from '../core/types.js';
8
+
9
+ export interface RunnableAgentConfig {
10
+ readonly name: string;
11
+ readonly description?: string;
12
+ readonly run: (input: AgentInput, ctx: AgentContext) => Promise<AgentOutput>;
13
+ }
14
+
15
+ /**
16
+ * Creates a core Agent from a run function for use with the Orchestrator.
17
+ */
18
+ export function createRunnableAgent(config: RunnableAgentConfig): Agent {
19
+ return new RunnableAgentAdapter(config);
20
+ }
21
+
22
+ class RunnableAgentAdapter extends Agent {
23
+ constructor(private readonly runConfig: RunnableAgentConfig) {
24
+ super({
25
+ name: runConfig.name,
26
+ description: runConfig.description,
27
+ });
28
+ }
29
+
30
+ async run(input: AgentInput, ctx: AgentContext): Promise<AgentOutput> {
31
+ return this.runConfig.run(input, ctx);
32
+ }
33
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Orchestration module exports
3
+ */
4
+
5
+ export * from './types.js';
6
+ export { OrchestratorImpl } from './orchestrator.js';
7
+ export { MessageBusImpl } from './message-bus.js';
8
+ export { RoundRobinLoadBalancer } from './load-balancer.js';
9
+ export { createRunnableAgent } from './agent-adapter.js';
10
+ export type { RunnableAgentConfig } from './agent-adapter.js';
11
+ export { createSupervisor, createRole } from './supervisor.js';
12
+ export type { SupervisorConfig } from './supervisor.js';
13
+ export { createPipeline } from './pipeline.js';
14
+ export type { PipelineConfig } from './pipeline.js';
15
+ export { createToolkit, toolkitsToRegistry } from './toolkit.js';
16
+ export type { Toolkit } from './toolkit.js';
17
+ export type { MCPClient, MCPServerAdapter, MCPToolDescriptor, A2AMessage, A2AClient } from './mcp-types.js';
18
+
19
+ // Agent Swarm exports (Kimi K2.5 inspired)
20
+ export {
21
+ SwarmOrchestrator,
22
+ createSwarm,
23
+ createSwarmAgent,
24
+ } from './swarm.js';
25
+ export type {
26
+ SwarmConfig,
27
+ SwarmResult,
28
+ SubagentTemplate,
29
+ Subtask,
30
+ SubtaskResult,
31
+ ExecutionStage,
32
+ CriticalPathMetrics,
33
+ SubagentInstance,
34
+ } from './swarm.js';
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Load Balancer Implementation
3
+ *
4
+ * Distributes tasks across agents based on various strategies
5
+ */
6
+
7
+ import { LoadBalancer, AgentRegistration, DelegationTask } from './types.js';
8
+ import type { EntityId } from '../core/types.js';
9
+
10
+ /**
11
+ * Round-robin load balancer
12
+ */
13
+ export class RoundRobinLoadBalancer implements LoadBalancer {
14
+ private lastIndex = 0;
15
+ private agentMetrics: Map<EntityId, { totalTasks: number; failedTasks: number; totalExecutionTime: number }> = new Map();
16
+
17
+ selectAgent(candidates: AgentRegistration[], _task: DelegationTask): AgentRegistration | undefined {
18
+ if (candidates.length === 0) {
19
+ return undefined;
20
+ }
21
+
22
+ // Filter to agents under their max load
23
+ const available = candidates.filter(reg =>
24
+ reg.metadata.currentLoad < reg.metadata.maxConcurrentTasks
25
+ );
26
+
27
+ if (available.length === 0) {
28
+ // All agents at capacity, pick the one with lowest load
29
+ return candidates.sort((a, b) => a.metadata.currentLoad - b.metadata.currentLoad)[0];
30
+ }
31
+
32
+ // Round-robin selection
33
+ const index = this.lastIndex % available.length;
34
+ this.lastIndex = (this.lastIndex + 1) % available.length;
35
+
36
+ return available[index];
37
+ }
38
+
39
+ updateMetrics(agentId: EntityId, executionTimeMs: number, success: boolean): void {
40
+ const metrics = this.agentMetrics.get(agentId) ?? {
41
+ totalTasks: 0,
42
+ failedTasks: 0,
43
+ totalExecutionTime: 0,
44
+ };
45
+
46
+ metrics.totalTasks++;
47
+ metrics.totalExecutionTime += executionTimeMs;
48
+ if (!success) {
49
+ metrics.failedTasks++;
50
+ }
51
+
52
+ this.agentMetrics.set(agentId, metrics);
53
+ }
54
+
55
+ /**
56
+ * Get metrics for an agent
57
+ */
58
+ getMetrics(agentId: EntityId): { totalTasks: number; failedTasks: number; averageExecutionTime: number } | undefined {
59
+ const metrics = this.agentMetrics.get(agentId);
60
+ if (!metrics) return undefined;
61
+
62
+ return {
63
+ totalTasks: metrics.totalTasks,
64
+ failedTasks: metrics.failedTasks,
65
+ averageExecutionTime: metrics.totalTasks > 0
66
+ ? metrics.totalExecutionTime / metrics.totalTasks
67
+ : 0,
68
+ };
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Least connections load balancer
74
+ */
75
+ export class LeastConnectionsLoadBalancer implements LoadBalancer {
76
+ private agentMetrics: Map<EntityId, { totalTasks: number; failedTasks: number; totalExecutionTime: number }> = new Map();
77
+
78
+ selectAgent(candidates: AgentRegistration[], _task: DelegationTask): AgentRegistration | undefined {
79
+ if (candidates.length === 0) {
80
+ return undefined;
81
+ }
82
+
83
+ // Sort by current load (ascending)
84
+ return candidates.sort((a, b) => a.metadata.currentLoad - b.metadata.currentLoad)[0];
85
+ }
86
+
87
+ updateMetrics(agentId: EntityId, executionTimeMs: number, success: boolean): void {
88
+ const metrics = this.agentMetrics.get(agentId) ?? {
89
+ totalTasks: 0,
90
+ failedTasks: 0,
91
+ totalExecutionTime: 0,
92
+ };
93
+
94
+ metrics.totalTasks++;
95
+ metrics.totalExecutionTime += executionTimeMs;
96
+ if (!success) {
97
+ metrics.failedTasks++;
98
+ }
99
+
100
+ this.agentMetrics.set(agentId, metrics);
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Weighted response time load balancer
106
+ */
107
+ export class WeightedResponseTimeLoadBalancer implements LoadBalancer {
108
+ private agentMetrics: Map<EntityId, { totalTasks: number; failedTasks: number; totalExecutionTime: number }> = new Map();
109
+
110
+ selectAgent(candidates: AgentRegistration[], _task: DelegationTask): AgentRegistration | undefined {
111
+ if (candidates.length === 0) {
112
+ return undefined;
113
+ }
114
+
115
+ // Calculate score based on average response time and current load
116
+ const scored = candidates.map(reg => {
117
+ const metrics = this.agentMetrics.get(reg.agent.id);
118
+ const avgResponseTime = metrics && metrics.totalTasks > 0
119
+ ? metrics.totalExecutionTime / metrics.totalTasks
120
+ : 1000; // Default to 1s if no data
121
+
122
+ const loadFactor = reg.metadata.currentLoad / reg.metadata.maxConcurrentTasks;
123
+
124
+ // Lower score is better
125
+ const score = avgResponseTime * (1 + loadFactor);
126
+
127
+ return { reg, score };
128
+ });
129
+
130
+ // Sort by score (ascending)
131
+ scored.sort((a, b) => a.score - b.score);
132
+
133
+ return scored[0].reg;
134
+ }
135
+
136
+ updateMetrics(agentId: EntityId, executionTimeMs: number, success: boolean): void {
137
+ const metrics = this.agentMetrics.get(agentId) ?? {
138
+ totalTasks: 0,
139
+ failedTasks: 0,
140
+ totalExecutionTime: 0,
141
+ };
142
+
143
+ metrics.totalTasks++;
144
+ metrics.totalExecutionTime += executionTimeMs;
145
+ if (!success) {
146
+ metrics.failedTasks++;
147
+ }
148
+
149
+ this.agentMetrics.set(agentId, metrics);
150
+ }
151
+ }