outcome-cli 1.0.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.
- package/README.md +261 -0
- package/package.json +95 -0
- package/src/agents/README.md +139 -0
- package/src/agents/adapters/anthropic.adapter.ts +166 -0
- package/src/agents/adapters/dalle.adapter.ts +145 -0
- package/src/agents/adapters/gemini.adapter.ts +134 -0
- package/src/agents/adapters/imagen.adapter.ts +106 -0
- package/src/agents/adapters/nano-banana.adapter.ts +129 -0
- package/src/agents/adapters/openai.adapter.ts +165 -0
- package/src/agents/adapters/veo.adapter.ts +130 -0
- package/src/agents/agent.schema.property.test.ts +379 -0
- package/src/agents/agent.schema.test.ts +148 -0
- package/src/agents/agent.schema.ts +263 -0
- package/src/agents/index.ts +60 -0
- package/src/agents/registered-agent.schema.ts +356 -0
- package/src/agents/registry.ts +97 -0
- package/src/agents/tournament-configs.property.test.ts +266 -0
- package/src/cli/README.md +145 -0
- package/src/cli/commands/define.ts +79 -0
- package/src/cli/commands/list.ts +46 -0
- package/src/cli/commands/logs.ts +83 -0
- package/src/cli/commands/run.ts +416 -0
- package/src/cli/commands/verify.ts +110 -0
- package/src/cli/index.ts +81 -0
- package/src/config/README.md +128 -0
- package/src/config/env.ts +262 -0
- package/src/config/index.ts +19 -0
- package/src/eval/README.md +318 -0
- package/src/eval/ai-judge.test.ts +435 -0
- package/src/eval/ai-judge.ts +368 -0
- package/src/eval/code-validators.ts +414 -0
- package/src/eval/evaluateOutcome.property.test.ts +1174 -0
- package/src/eval/evaluateOutcome.ts +591 -0
- package/src/eval/immigration-validators.ts +122 -0
- package/src/eval/index.ts +90 -0
- package/src/eval/judge-cache.ts +402 -0
- package/src/eval/tournament-validators.property.test.ts +439 -0
- package/src/eval/validators.property.test.ts +1118 -0
- package/src/eval/validators.ts +1199 -0
- package/src/eval/weighted-scorer.ts +285 -0
- package/src/index.ts +17 -0
- package/src/league/README.md +188 -0
- package/src/league/health-check.ts +353 -0
- package/src/league/index.ts +93 -0
- package/src/league/killAgent.ts +151 -0
- package/src/league/league.test.ts +1151 -0
- package/src/league/runLeague.ts +843 -0
- package/src/league/scoreAgent.ts +175 -0
- package/src/modules/omnibridge/__tests__/.gitkeep +1 -0
- package/src/modules/omnibridge/__tests__/auth-tunnel.property.test.ts +524 -0
- package/src/modules/omnibridge/__tests__/deterministic-logger.property.test.ts +965 -0
- package/src/modules/omnibridge/__tests__/ghost-api.property.test.ts +461 -0
- package/src/modules/omnibridge/__tests__/omnibridge-integration.test.ts +542 -0
- package/src/modules/omnibridge/__tests__/parallel-executor.property.test.ts +671 -0
- package/src/modules/omnibridge/__tests__/semantic-normalizer.property.test.ts +521 -0
- package/src/modules/omnibridge/__tests__/semantic-normalizer.test.ts +254 -0
- package/src/modules/omnibridge/__tests__/session-vault.property.test.ts +367 -0
- package/src/modules/omnibridge/__tests__/shadow-session.property.test.ts +523 -0
- package/src/modules/omnibridge/__tests__/triangulation-engine.property.test.ts +292 -0
- package/src/modules/omnibridge/__tests__/verification-engine.property.test.ts +769 -0
- package/src/modules/omnibridge/api/.gitkeep +1 -0
- package/src/modules/omnibridge/api/ghost-api.ts +1087 -0
- package/src/modules/omnibridge/auth/.gitkeep +1 -0
- package/src/modules/omnibridge/auth/auth-tunnel.ts +843 -0
- package/src/modules/omnibridge/auth/session-vault.ts +577 -0
- package/src/modules/omnibridge/core/.gitkeep +1 -0
- package/src/modules/omnibridge/core/semantic-normalizer.ts +702 -0
- package/src/modules/omnibridge/core/triangulation-engine.ts +530 -0
- package/src/modules/omnibridge/core/types.ts +610 -0
- package/src/modules/omnibridge/execution/.gitkeep +1 -0
- package/src/modules/omnibridge/execution/deterministic-logger.ts +629 -0
- package/src/modules/omnibridge/execution/parallel-executor.ts +542 -0
- package/src/modules/omnibridge/execution/shadow-session.ts +794 -0
- package/src/modules/omnibridge/index.ts +212 -0
- package/src/modules/omnibridge/omnibridge.ts +510 -0
- package/src/modules/omnibridge/verification/.gitkeep +1 -0
- package/src/modules/omnibridge/verification/verification-engine.ts +783 -0
- package/src/outcomes/README.md +75 -0
- package/src/outcomes/acquire-pilot-customer.ts +297 -0
- package/src/outcomes/code-delivery-outcomes.ts +89 -0
- package/src/outcomes/code-outcomes.ts +256 -0
- package/src/outcomes/code_review_battle.test.ts +135 -0
- package/src/outcomes/code_review_battle.ts +135 -0
- package/src/outcomes/cold_email_battle.ts +97 -0
- package/src/outcomes/content_creation_battle.ts +160 -0
- package/src/outcomes/f1_stem_opt_compliance.ts +61 -0
- package/src/outcomes/index.ts +107 -0
- package/src/outcomes/lead_gen_battle.test.ts +113 -0
- package/src/outcomes/lead_gen_battle.ts +99 -0
- package/src/outcomes/outcome.schema.property.test.ts +229 -0
- package/src/outcomes/outcome.schema.ts +187 -0
- package/src/outcomes/qualified_sales_interest.ts +118 -0
- package/src/outcomes/swarm_planner.property.test.ts +370 -0
- package/src/outcomes/swarm_planner.ts +96 -0
- package/src/outcomes/web_extraction.ts +234 -0
- package/src/runtime/README.md +220 -0
- package/src/runtime/agentRunner.test.ts +341 -0
- package/src/runtime/agentRunner.ts +746 -0
- package/src/runtime/claudeAdapter.ts +232 -0
- package/src/runtime/costTracker.ts +123 -0
- package/src/runtime/index.ts +34 -0
- package/src/runtime/modelAdapter.property.test.ts +305 -0
- package/src/runtime/modelAdapter.ts +144 -0
- package/src/runtime/openaiAdapter.ts +235 -0
- package/src/utils/README.md +122 -0
- package/src/utils/command-runner.ts +134 -0
- package/src/utils/cost-guard.ts +379 -0
- package/src/utils/errors.test.ts +290 -0
- package/src/utils/errors.ts +442 -0
- package/src/utils/index.ts +37 -0
- package/src/utils/logger.test.ts +361 -0
- package/src/utils/logger.ts +419 -0
- package/src/utils/output-parsers.ts +216 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health Check System - Monitors agent availability
|
|
3
|
+
*
|
|
4
|
+
* Periodically checks registered agents to verify they are online
|
|
5
|
+
* and responsive. Updates agent deployment status accordingly.
|
|
6
|
+
*
|
|
7
|
+
* @module league/health-check
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
RegisteredAgent,
|
|
12
|
+
HealthCheckResponse,
|
|
13
|
+
DeploymentInfo,
|
|
14
|
+
} from '../agents/registered-agent.schema.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Health check result for a single agent.
|
|
18
|
+
*/
|
|
19
|
+
export interface HealthCheckResult {
|
|
20
|
+
agentId: string;
|
|
21
|
+
agentName: string;
|
|
22
|
+
status: 'healthy' | 'degraded' | 'unhealthy';
|
|
23
|
+
latencyMs: number;
|
|
24
|
+
checkedAt: Date;
|
|
25
|
+
error?: string;
|
|
26
|
+
response?: HealthCheckResponse;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Configuration for the health check service.
|
|
31
|
+
*/
|
|
32
|
+
export interface HealthCheckConfig {
|
|
33
|
+
/** Timeout for health check requests in milliseconds */
|
|
34
|
+
timeoutMs: number;
|
|
35
|
+
/** Number of consecutive failures before marking offline */
|
|
36
|
+
failureThreshold: number;
|
|
37
|
+
/** Interval between health checks in milliseconds */
|
|
38
|
+
checkIntervalMs: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Default health check configuration.
|
|
43
|
+
*/
|
|
44
|
+
export const DEFAULT_HEALTH_CHECK_CONFIG: HealthCheckConfig = {
|
|
45
|
+
timeoutMs: 5000,
|
|
46
|
+
failureThreshold: 3,
|
|
47
|
+
checkIntervalMs: 60000, // 1 minute
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Tracks consecutive failures per agent.
|
|
52
|
+
*/
|
|
53
|
+
const failureCounters: Map<string, number> = new Map();
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Perform a health check on a single agent.
|
|
57
|
+
*
|
|
58
|
+
* @param agent - The agent to check
|
|
59
|
+
* @param config - Health check configuration
|
|
60
|
+
* @returns Health check result
|
|
61
|
+
*/
|
|
62
|
+
export async function checkAgentHealth(
|
|
63
|
+
agent: RegisteredAgent,
|
|
64
|
+
config: HealthCheckConfig = DEFAULT_HEALTH_CHECK_CONFIG
|
|
65
|
+
): Promise<HealthCheckResult> {
|
|
66
|
+
const startTime = Date.now();
|
|
67
|
+
const controller = new AbortController();
|
|
68
|
+
const timeout = setTimeout(() => controller.abort(), config.timeoutMs);
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
// Determine health endpoint URL
|
|
72
|
+
const healthUrl = getHealthEndpoint(agent.endpoint.url);
|
|
73
|
+
|
|
74
|
+
const response = await fetch(healthUrl, {
|
|
75
|
+
method: 'GET',
|
|
76
|
+
headers: buildHeaders(agent),
|
|
77
|
+
signal: controller.signal,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const latencyMs = Date.now() - startTime;
|
|
81
|
+
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
return createUnhealthyResult(agent, latencyMs, `HTTP ${response.status}`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const data: HealthCheckResponse = await response.json();
|
|
87
|
+
|
|
88
|
+
// Reset failure counter on success
|
|
89
|
+
failureCounters.set(agent.id, 0);
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
agentId: agent.id,
|
|
93
|
+
agentName: agent.name,
|
|
94
|
+
status: data.status,
|
|
95
|
+
latencyMs,
|
|
96
|
+
checkedAt: new Date(),
|
|
97
|
+
response: data,
|
|
98
|
+
};
|
|
99
|
+
} catch (error) {
|
|
100
|
+
const latencyMs = Date.now() - startTime;
|
|
101
|
+
const errorMessage =
|
|
102
|
+
error instanceof Error ? error.message : 'Unknown error';
|
|
103
|
+
|
|
104
|
+
// Increment failure counter
|
|
105
|
+
const currentFailures = (failureCounters.get(agent.id) ?? 0) + 1;
|
|
106
|
+
failureCounters.set(agent.id, currentFailures);
|
|
107
|
+
|
|
108
|
+
return createUnhealthyResult(agent, latencyMs, errorMessage);
|
|
109
|
+
} finally {
|
|
110
|
+
clearTimeout(timeout);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Check health of multiple agents in parallel.
|
|
116
|
+
*
|
|
117
|
+
* @param agents - Array of agents to check
|
|
118
|
+
* @param config - Health check configuration
|
|
119
|
+
* @returns Array of health check results
|
|
120
|
+
*/
|
|
121
|
+
export async function checkMultipleAgents(
|
|
122
|
+
agents: RegisteredAgent[],
|
|
123
|
+
config: HealthCheckConfig = DEFAULT_HEALTH_CHECK_CONFIG
|
|
124
|
+
): Promise<HealthCheckResult[]> {
|
|
125
|
+
const results = await Promise.all(
|
|
126
|
+
agents.map((agent) => checkAgentHealth(agent, config))
|
|
127
|
+
);
|
|
128
|
+
return results;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Determine if an agent should be marked offline based on failure count.
|
|
133
|
+
*
|
|
134
|
+
* @param agentId - The agent ID
|
|
135
|
+
* @param config - Health check configuration
|
|
136
|
+
* @returns True if agent should be marked offline
|
|
137
|
+
*/
|
|
138
|
+
export function shouldMarkOffline(
|
|
139
|
+
agentId: string,
|
|
140
|
+
config: HealthCheckConfig = DEFAULT_HEALTH_CHECK_CONFIG
|
|
141
|
+
): boolean {
|
|
142
|
+
const failures = failureCounters.get(agentId) ?? 0;
|
|
143
|
+
return failures >= config.failureThreshold;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Get current failure count for an agent.
|
|
148
|
+
*
|
|
149
|
+
* @param agentId - The agent ID
|
|
150
|
+
* @returns Number of consecutive failures
|
|
151
|
+
*/
|
|
152
|
+
export function getFailureCount(agentId: string): number {
|
|
153
|
+
return failureCounters.get(agentId) ?? 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Reset failure counter for an agent.
|
|
158
|
+
*
|
|
159
|
+
* @param agentId - The agent ID
|
|
160
|
+
*/
|
|
161
|
+
export function resetFailureCount(agentId: string): void {
|
|
162
|
+
failureCounters.set(agentId, 0);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Update deployment info based on health check result.
|
|
167
|
+
*
|
|
168
|
+
* @param currentInfo - Current deployment info
|
|
169
|
+
* @param result - Health check result
|
|
170
|
+
* @param config - Health check configuration
|
|
171
|
+
* @returns Updated deployment info
|
|
172
|
+
*/
|
|
173
|
+
export function updateDeploymentInfo(
|
|
174
|
+
currentInfo: DeploymentInfo,
|
|
175
|
+
result: HealthCheckResult,
|
|
176
|
+
config: HealthCheckConfig = DEFAULT_HEALTH_CHECK_CONFIG
|
|
177
|
+
): DeploymentInfo {
|
|
178
|
+
const shouldBeOffline = shouldMarkOffline(result.agentId, config);
|
|
179
|
+
|
|
180
|
+
let newStatus: DeploymentInfo['status'];
|
|
181
|
+
if (shouldBeOffline) {
|
|
182
|
+
newStatus = 'offline';
|
|
183
|
+
} else if (result.status === 'healthy') {
|
|
184
|
+
newStatus = 'online';
|
|
185
|
+
} else if (result.status === 'degraded') {
|
|
186
|
+
newStatus = 'degraded';
|
|
187
|
+
} else {
|
|
188
|
+
newStatus = currentInfo.status; // Keep current if single failure
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Calculate rolling average latency
|
|
192
|
+
const avgLatency =
|
|
193
|
+
currentInfo.averageLatencyMs === 0
|
|
194
|
+
? result.latencyMs
|
|
195
|
+
: Math.round((currentInfo.averageLatencyMs + result.latencyMs) / 2);
|
|
196
|
+
|
|
197
|
+
// Calculate uptime percentage (simplified)
|
|
198
|
+
const wasOnline = currentInfo.status === 'online';
|
|
199
|
+
const isNowOnline = newStatus === 'online';
|
|
200
|
+
const uptimeChange = isNowOnline ? 1 : wasOnline ? -5 : 0;
|
|
201
|
+
const newUptime = Math.max(
|
|
202
|
+
0,
|
|
203
|
+
Math.min(100, currentInfo.uptimePercent + uptimeChange)
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
...currentInfo,
|
|
208
|
+
status: newStatus,
|
|
209
|
+
lastHealthCheck: result.checkedAt,
|
|
210
|
+
averageLatencyMs: avgLatency,
|
|
211
|
+
uptimePercent: newUptime,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Get the health endpoint URL from a chat endpoint URL.
|
|
217
|
+
*/
|
|
218
|
+
function getHealthEndpoint(chatUrl: string): string {
|
|
219
|
+
const url = new URL(chatUrl);
|
|
220
|
+
|
|
221
|
+
// Try common health check paths
|
|
222
|
+
if (url.pathname.endsWith('/chat')) {
|
|
223
|
+
url.pathname = url.pathname.replace(/\/chat$/, '/health');
|
|
224
|
+
} else if (url.pathname.endsWith('/v1/chat/completions')) {
|
|
225
|
+
url.pathname = url.pathname.replace(/\/v1\/chat\/completions$/, '/health');
|
|
226
|
+
} else {
|
|
227
|
+
// Append /health to the base
|
|
228
|
+
url.pathname = url.pathname.replace(/\/$/, '') + '/health';
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return url.toString();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Build authentication headers for an agent.
|
|
236
|
+
*/
|
|
237
|
+
function buildHeaders(agent: RegisteredAgent): Record<string, string> {
|
|
238
|
+
if (agent.endpoint.authType === 'none') {
|
|
239
|
+
return {};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Note: In production, this would decrypt the token
|
|
243
|
+
const token = agent.endpoint.encryptedAuthToken ?? '';
|
|
244
|
+
const headerName = agent.endpoint.authHeader ?? 'Authorization';
|
|
245
|
+
const headerValue =
|
|
246
|
+
agent.endpoint.authType === 'bearer' ? `Bearer ${token}` : token;
|
|
247
|
+
|
|
248
|
+
return { [headerName]: headerValue };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Create an unhealthy result.
|
|
253
|
+
*/
|
|
254
|
+
function createUnhealthyResult(
|
|
255
|
+
agent: RegisteredAgent,
|
|
256
|
+
latencyMs: number,
|
|
257
|
+
error: string
|
|
258
|
+
): HealthCheckResult {
|
|
259
|
+
return {
|
|
260
|
+
agentId: agent.id,
|
|
261
|
+
agentName: agent.name,
|
|
262
|
+
status: 'unhealthy',
|
|
263
|
+
latencyMs,
|
|
264
|
+
checkedAt: new Date(),
|
|
265
|
+
error,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Health Check Service class for managing periodic checks.
|
|
271
|
+
*/
|
|
272
|
+
export class HealthCheckService {
|
|
273
|
+
private config: HealthCheckConfig;
|
|
274
|
+
private intervalId: NodeJS.Timeout | null = null;
|
|
275
|
+
private onStatusChange?: (
|
|
276
|
+
agentId: string,
|
|
277
|
+
oldStatus: string,
|
|
278
|
+
newStatus: string
|
|
279
|
+
) => void;
|
|
280
|
+
|
|
281
|
+
constructor(
|
|
282
|
+
config: Partial<HealthCheckConfig> = {},
|
|
283
|
+
onStatusChange?: (
|
|
284
|
+
agentId: string,
|
|
285
|
+
oldStatus: string,
|
|
286
|
+
newStatus: string
|
|
287
|
+
) => void
|
|
288
|
+
) {
|
|
289
|
+
this.config = { ...DEFAULT_HEALTH_CHECK_CONFIG, ...config };
|
|
290
|
+
this.onStatusChange = onStatusChange;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Start periodic health checks.
|
|
295
|
+
*
|
|
296
|
+
* @param getAgents - Function to get current list of agents
|
|
297
|
+
* @param updateAgent - Function to update agent deployment info
|
|
298
|
+
*/
|
|
299
|
+
start(
|
|
300
|
+
getAgents: () => RegisteredAgent[],
|
|
301
|
+
updateAgent: (agentId: string, deployment: DeploymentInfo) => void
|
|
302
|
+
): void {
|
|
303
|
+
if (this.intervalId) {
|
|
304
|
+
this.stop();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const runChecks = async () => {
|
|
308
|
+
const agents = getAgents();
|
|
309
|
+
const results = await checkMultipleAgents(agents, this.config);
|
|
310
|
+
|
|
311
|
+
for (const result of results) {
|
|
312
|
+
const agent = agents.find((a) => a.id === result.agentId);
|
|
313
|
+
if (!agent) continue;
|
|
314
|
+
|
|
315
|
+
const oldStatus = agent.deployment.status;
|
|
316
|
+
const newDeployment = updateDeploymentInfo(
|
|
317
|
+
agent.deployment,
|
|
318
|
+
result,
|
|
319
|
+
this.config
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
if (oldStatus !== newDeployment.status && this.onStatusChange) {
|
|
323
|
+
this.onStatusChange(agent.id, oldStatus, newDeployment.status);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
updateAgent(agent.id, newDeployment);
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
// Run immediately
|
|
331
|
+
runChecks();
|
|
332
|
+
|
|
333
|
+
// Then run periodically
|
|
334
|
+
this.intervalId = setInterval(runChecks, this.config.checkIntervalMs);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Stop periodic health checks.
|
|
339
|
+
*/
|
|
340
|
+
stop(): void {
|
|
341
|
+
if (this.intervalId) {
|
|
342
|
+
clearInterval(this.intervalId);
|
|
343
|
+
this.intervalId = null;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Check if the service is running.
|
|
349
|
+
*/
|
|
350
|
+
isRunning(): boolean {
|
|
351
|
+
return this.intervalId !== null;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* League Module - Parallel agent competition system
|
|
3
|
+
*
|
|
4
|
+
* @module league
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
type LeagueConfig,
|
|
9
|
+
type AgentResult,
|
|
10
|
+
type LeagueResult,
|
|
11
|
+
runLeague,
|
|
12
|
+
runLeagueMock,
|
|
13
|
+
} from './runLeague.js';
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
type KillReason,
|
|
17
|
+
type AgentLimits,
|
|
18
|
+
type RunningAgent,
|
|
19
|
+
shouldKillAgent,
|
|
20
|
+
killAgent,
|
|
21
|
+
checkAllAgents,
|
|
22
|
+
} from './killAgent.js';
|
|
23
|
+
|
|
24
|
+
export {
|
|
25
|
+
type AgentMetrics,
|
|
26
|
+
type AgentScore,
|
|
27
|
+
scoreAgent,
|
|
28
|
+
determineWinner,
|
|
29
|
+
rankAgents,
|
|
30
|
+
calculateLeagueStats,
|
|
31
|
+
} from './scoreAgent.js';
|
|
32
|
+
|
|
33
|
+
// Battle Orchestrator - Production battle management
|
|
34
|
+
export {
|
|
35
|
+
type BattleConfig,
|
|
36
|
+
type BattleTask,
|
|
37
|
+
type BattleResult,
|
|
38
|
+
type AgentBattleRequest,
|
|
39
|
+
type AgentBattleResponse,
|
|
40
|
+
type AgentBattleResult,
|
|
41
|
+
BattleOrchestrator,
|
|
42
|
+
AgentOfflineError,
|
|
43
|
+
createBattleOrchestrator,
|
|
44
|
+
} from './battle-orchestrator.js';
|
|
45
|
+
|
|
46
|
+
// Health Check System - Agent availability monitoring
|
|
47
|
+
export {
|
|
48
|
+
type HealthCheckResult,
|
|
49
|
+
type HealthCheckConfig,
|
|
50
|
+
DEFAULT_HEALTH_CHECK_CONFIG,
|
|
51
|
+
checkAgentHealth,
|
|
52
|
+
checkMultipleAgents,
|
|
53
|
+
shouldMarkOffline,
|
|
54
|
+
getFailureCount,
|
|
55
|
+
resetFailureCount,
|
|
56
|
+
updateDeploymentInfo,
|
|
57
|
+
HealthCheckService,
|
|
58
|
+
} from './health-check.js';
|
|
59
|
+
|
|
60
|
+
// Team Coordinator - Team battle management
|
|
61
|
+
export {
|
|
62
|
+
type TeamConfig,
|
|
63
|
+
type TeamState,
|
|
64
|
+
type StateUpdateResult,
|
|
65
|
+
type MemberContribution,
|
|
66
|
+
type TeamPayoutDistribution,
|
|
67
|
+
type TeamStateChangeEvent,
|
|
68
|
+
type TeamStateChangeListener,
|
|
69
|
+
StateConflictError,
|
|
70
|
+
TeamNotFoundError,
|
|
71
|
+
NotTeamMemberError,
|
|
72
|
+
TeamCoordinator,
|
|
73
|
+
createTeamCoordinator,
|
|
74
|
+
} from './team-coordinator.js';
|
|
75
|
+
|
|
76
|
+
// Multi-Step Orchestrator - Multi-step bounty execution
|
|
77
|
+
export {
|
|
78
|
+
type TaskNode,
|
|
79
|
+
type MultiStepBounty,
|
|
80
|
+
type TaskExecutionResult,
|
|
81
|
+
type MultiStepResult,
|
|
82
|
+
type TaskContext,
|
|
83
|
+
type MultiStepAgent,
|
|
84
|
+
CyclicDependencyError,
|
|
85
|
+
InvalidDependencyError,
|
|
86
|
+
InvalidFinalTaskError,
|
|
87
|
+
TaskExecutionError,
|
|
88
|
+
validateTaskGraph,
|
|
89
|
+
validateMultiStepBounty,
|
|
90
|
+
getTopologicalOrder,
|
|
91
|
+
MultiStepOrchestrator,
|
|
92
|
+
createMultiStepOrchestrator,
|
|
93
|
+
} from './multi-step-orchestrator.js';
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kill Agent - Agent termination logic
|
|
3
|
+
*
|
|
4
|
+
* Handles agent termination conditions and cleanup.
|
|
5
|
+
*
|
|
6
|
+
* @module league/killAgent
|
|
7
|
+
* @see Requirements 4.3, 4.4, 10.2
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { CostTracker } from '../runtime/costTracker.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Reason for agent termination.
|
|
14
|
+
*/
|
|
15
|
+
export interface KillReason {
|
|
16
|
+
/** Type of termination */
|
|
17
|
+
type: 'cost_exceeded' | 'attempts_exceeded' | 'timeout' | 'competitor_won';
|
|
18
|
+
/** Human-readable details */
|
|
19
|
+
details: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Limits that trigger agent termination.
|
|
24
|
+
*/
|
|
25
|
+
export interface AgentLimits {
|
|
26
|
+
/** Maximum tokens allowed */
|
|
27
|
+
maxTokens: number;
|
|
28
|
+
/** Maximum attempts allowed */
|
|
29
|
+
maxAttempts: number;
|
|
30
|
+
/** Maximum runtime in milliseconds */
|
|
31
|
+
maxRuntimeMs: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* State of a running agent.
|
|
36
|
+
*/
|
|
37
|
+
export interface RunningAgent {
|
|
38
|
+
/** Agent ID */
|
|
39
|
+
agentId: string;
|
|
40
|
+
/** Current attempt count */
|
|
41
|
+
attempts: number;
|
|
42
|
+
/** Cost tracker for the agent */
|
|
43
|
+
costTracker: CostTracker;
|
|
44
|
+
/** Start time of the agent run */
|
|
45
|
+
startTime: number;
|
|
46
|
+
/** Whether a competitor has already won */
|
|
47
|
+
competitorWon: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Checks if an agent should be terminated based on current state.
|
|
52
|
+
*
|
|
53
|
+
* @param agent - Current agent state
|
|
54
|
+
* @param limits - Termination limits
|
|
55
|
+
* @returns KillReason if agent should be killed, null otherwise
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* const reason = shouldKillAgent(agent, {
|
|
59
|
+
* maxTokens: 10000,
|
|
60
|
+
* maxAttempts: 5,
|
|
61
|
+
* maxRuntimeMs: 300000
|
|
62
|
+
* });
|
|
63
|
+
* if (reason) {
|
|
64
|
+
* await killAgent(agent.agentId, reason);
|
|
65
|
+
* }
|
|
66
|
+
*
|
|
67
|
+
* @see Requirements 4.3, 4.4, 10.2
|
|
68
|
+
*/
|
|
69
|
+
export function shouldKillAgent(
|
|
70
|
+
agent: RunningAgent,
|
|
71
|
+
limits: AgentLimits
|
|
72
|
+
): KillReason | null {
|
|
73
|
+
// Check if competitor already won (Requirement 4.5)
|
|
74
|
+
if (agent.competitorWon) {
|
|
75
|
+
return {
|
|
76
|
+
type: 'competitor_won',
|
|
77
|
+
details: `Another agent achieved success first`,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Check cost ceiling (Requirement 4.4, 10.1)
|
|
82
|
+
if (agent.costTracker.tokensSpent > limits.maxTokens) {
|
|
83
|
+
return {
|
|
84
|
+
type: 'cost_exceeded',
|
|
85
|
+
details: `Token usage ${agent.costTracker.tokensSpent} exceeded ceiling ${limits.maxTokens}`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Check attempt limit (Requirement 4.3)
|
|
90
|
+
if (agent.attempts >= limits.maxAttempts) {
|
|
91
|
+
return {
|
|
92
|
+
type: 'attempts_exceeded',
|
|
93
|
+
details: `Attempt count ${agent.attempts} reached limit ${limits.maxAttempts}`,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Check runtime limit (Requirement 10.2)
|
|
98
|
+
const elapsed = Date.now() - agent.startTime;
|
|
99
|
+
if (elapsed >= limits.maxRuntimeMs) {
|
|
100
|
+
return {
|
|
101
|
+
type: 'timeout',
|
|
102
|
+
details: `Runtime ${elapsed}ms exceeded limit ${limits.maxRuntimeMs}ms`,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Terminates an agent with the given reason.
|
|
111
|
+
*
|
|
112
|
+
* @param agentId - ID of the agent to terminate
|
|
113
|
+
* @param reason - Reason for termination
|
|
114
|
+
*
|
|
115
|
+
* @see Requirements 4.3, 4.4
|
|
116
|
+
*/
|
|
117
|
+
export async function killAgent(
|
|
118
|
+
agentId: string,
|
|
119
|
+
reason: KillReason
|
|
120
|
+
): Promise<void> {
|
|
121
|
+
// Log termination
|
|
122
|
+
console.log(`🔴 Agent ${agentId} terminated: ${reason.type} - ${reason.details}`);
|
|
123
|
+
|
|
124
|
+
// In production, this would:
|
|
125
|
+
// 1. Send termination signal to the agent
|
|
126
|
+
// 2. Clean up any resources
|
|
127
|
+
// 3. Record termination in Durable Objects
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Checks multiple agents and returns which ones should be killed.
|
|
132
|
+
*
|
|
133
|
+
* @param agents - Array of running agents
|
|
134
|
+
* @param limits - Termination limits
|
|
135
|
+
* @returns Map of agentId to KillReason for agents that should be killed
|
|
136
|
+
*/
|
|
137
|
+
export function checkAllAgents(
|
|
138
|
+
agents: RunningAgent[],
|
|
139
|
+
limits: AgentLimits
|
|
140
|
+
): Map<string, KillReason> {
|
|
141
|
+
const toKill = new Map<string, KillReason>();
|
|
142
|
+
|
|
143
|
+
for (const agent of agents) {
|
|
144
|
+
const reason = shouldKillAgent(agent, limits);
|
|
145
|
+
if (reason) {
|
|
146
|
+
toKill.set(agent.agentId, reason);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return toKill;
|
|
151
|
+
}
|