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,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Schema - Type definitions and validation for agent configurations
|
|
3
|
+
*
|
|
4
|
+
* An Agent is a configuration-driven AI entity that attempts to achieve
|
|
5
|
+
* outcomes using defined prompts, strategies, and tools.
|
|
6
|
+
*
|
|
7
|
+
* @module agents/agent.schema
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Supported AI model providers.
|
|
12
|
+
*/
|
|
13
|
+
export type ModelProvider = 'claude' | 'openai' | 'ollama' | 'gemini';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Agent tier classification based on cost and capabilities
|
|
17
|
+
*/
|
|
18
|
+
export type AgentTier = 'premium' | 'budget' | 'free';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Safety restriction level for content moderation
|
|
22
|
+
*/
|
|
23
|
+
export type SafetyLevel = 'low' | 'medium' | 'high';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Agent capabilities declaration
|
|
27
|
+
* Enables capability-aware bounty matching and filtering
|
|
28
|
+
*/
|
|
29
|
+
export interface AgentCapabilities {
|
|
30
|
+
/** Can search and scrape web content (requires Firecrawl/web tools) */
|
|
31
|
+
web_search: boolean;
|
|
32
|
+
/** Can reliably produce structured output (JSON, CSV, etc.) */
|
|
33
|
+
structured_output: boolean;
|
|
34
|
+
/** Can call external APIs (Hunter, Apollo, enrichment services) */
|
|
35
|
+
external_apis: boolean;
|
|
36
|
+
/** Supports long context windows (100K+ tokens) */
|
|
37
|
+
long_context: boolean;
|
|
38
|
+
/** Can access local files (for desktop automation) */
|
|
39
|
+
file_io_local: boolean;
|
|
40
|
+
/** Workspace tools available (Slack, Notion, Sheets, etc.) */
|
|
41
|
+
workspace_tools: string[];
|
|
42
|
+
/** Safety/content moderation restriction level */
|
|
43
|
+
safety_restriction_level: SafetyLevel;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Represents a complete agent configuration.
|
|
48
|
+
* Agents are configuration-driven with no hard-coded logic.
|
|
49
|
+
*/
|
|
50
|
+
export interface AgentConfig {
|
|
51
|
+
/** Unique identifier for this agent */
|
|
52
|
+
id: string;
|
|
53
|
+
/** Human-readable name for the agent */
|
|
54
|
+
name: string;
|
|
55
|
+
/** The prompt template used by the agent */
|
|
56
|
+
prompt: string;
|
|
57
|
+
/** Description of the agent's strategy for transparency */
|
|
58
|
+
strategyDescription: string;
|
|
59
|
+
/** List of tools the agent is allowed to access */
|
|
60
|
+
toolAccess: string[];
|
|
61
|
+
/** Maximum token cost ceiling for this agent */
|
|
62
|
+
costCeiling: number;
|
|
63
|
+
/** The AI model provider to use */
|
|
64
|
+
modelProvider: ModelProvider;
|
|
65
|
+
/** Specific model identifier (e.g., 'claude-3-sonnet-20240229') */
|
|
66
|
+
modelId: string;
|
|
67
|
+
/** Agent capabilities for bounty matching (optional for backward compatibility) */
|
|
68
|
+
capabilities?: AgentCapabilities;
|
|
69
|
+
/** Bounty types this agent can handle (optional) */
|
|
70
|
+
supported_bounty_types?: string[];
|
|
71
|
+
/** Agent tier classification (optional) */
|
|
72
|
+
tier?: AgentTier;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Result of validating an agent configuration.
|
|
77
|
+
*/
|
|
78
|
+
export interface ValidationResult {
|
|
79
|
+
/** Whether validation passed */
|
|
80
|
+
valid: boolean;
|
|
81
|
+
/** Error messages if validation failed */
|
|
82
|
+
errors: string[];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Validates an AgentConfig object against the schema.
|
|
87
|
+
*
|
|
88
|
+
* Ensures all required fields are present with correct types:
|
|
89
|
+
* - id: non-empty string
|
|
90
|
+
* - name: non-empty string
|
|
91
|
+
* - prompt: non-empty string
|
|
92
|
+
* - strategyDescription: non-empty string
|
|
93
|
+
* - toolAccess: array of strings
|
|
94
|
+
* - costCeiling: positive number
|
|
95
|
+
* - modelProvider: 'claude' or 'openai'
|
|
96
|
+
* - modelId: non-empty string
|
|
97
|
+
*
|
|
98
|
+
* @param config - The agent configuration object to validate
|
|
99
|
+
* @returns ValidationResult indicating if the config is valid
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* const result = validateAgentConfig(myConfig);
|
|
103
|
+
* if (!result.valid) {
|
|
104
|
+
* console.error('Invalid agent config:', result.errors);
|
|
105
|
+
* }
|
|
106
|
+
*/
|
|
107
|
+
export function validateAgentConfig(config: unknown): ValidationResult {
|
|
108
|
+
const errors: string[] = [];
|
|
109
|
+
|
|
110
|
+
// Check if config is an object (but not an array)
|
|
111
|
+
if (typeof config !== 'object' || config === null || Array.isArray(config)) {
|
|
112
|
+
return { valid: false, errors: ['Agent config must be an object'] };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const c = config as Record<string, unknown>;
|
|
116
|
+
|
|
117
|
+
// Validate id
|
|
118
|
+
if (typeof c.id !== 'string') {
|
|
119
|
+
errors.push('Agent config must have a string "id"');
|
|
120
|
+
} else if (c.id.trim() === '') {
|
|
121
|
+
errors.push('Agent config "id" must not be empty');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Validate name
|
|
125
|
+
if (typeof c.name !== 'string') {
|
|
126
|
+
errors.push('Agent config must have a string "name"');
|
|
127
|
+
} else if (c.name.trim() === '') {
|
|
128
|
+
errors.push('Agent config "name" must not be empty');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Validate prompt
|
|
132
|
+
if (typeof c.prompt !== 'string') {
|
|
133
|
+
errors.push('Agent config must have a string "prompt"');
|
|
134
|
+
} else if (c.prompt.trim() === '') {
|
|
135
|
+
errors.push('Agent config "prompt" must not be empty');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Validate strategyDescription
|
|
139
|
+
if (typeof c.strategyDescription !== 'string') {
|
|
140
|
+
errors.push('Agent config must have a string "strategyDescription"');
|
|
141
|
+
} else if (c.strategyDescription.trim() === '') {
|
|
142
|
+
errors.push('Agent config "strategyDescription" must not be empty');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Validate toolAccess
|
|
146
|
+
if (!Array.isArray(c.toolAccess)) {
|
|
147
|
+
errors.push('Agent config must have an array "toolAccess"');
|
|
148
|
+
} else {
|
|
149
|
+
c.toolAccess.forEach((tool, index) => {
|
|
150
|
+
if (typeof tool !== 'string') {
|
|
151
|
+
errors.push(`toolAccess[${index}] must be a string`);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Validate costCeiling
|
|
157
|
+
if (typeof c.costCeiling !== 'number') {
|
|
158
|
+
errors.push('Agent config must have a number "costCeiling"');
|
|
159
|
+
} else if (c.costCeiling <= 0) {
|
|
160
|
+
errors.push('Agent config "costCeiling" must be positive');
|
|
161
|
+
} else if (!Number.isFinite(c.costCeiling)) {
|
|
162
|
+
errors.push('Agent config "costCeiling" must be a finite number');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Validate modelProvider
|
|
166
|
+
const validProviders: ModelProvider[] = ['claude', 'openai', 'ollama', 'gemini'];
|
|
167
|
+
if (typeof c.modelProvider !== 'string') {
|
|
168
|
+
errors.push('Agent config must have a string "modelProvider"');
|
|
169
|
+
} else if (!validProviders.includes(c.modelProvider as ModelProvider)) {
|
|
170
|
+
errors.push('Agent config "modelProvider" must be "claude", "openai", "ollama", or "gemini"');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Validate modelId
|
|
174
|
+
if (typeof c.modelId !== 'string') {
|
|
175
|
+
errors.push('Agent config must have a string "modelId"');
|
|
176
|
+
} else if (c.modelId.trim() === '') {
|
|
177
|
+
errors.push('Agent config "modelId" must not be empty');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Validate capabilities (optional)
|
|
181
|
+
if (c.capabilities !== undefined) {
|
|
182
|
+
if (typeof c.capabilities !== 'object' || c.capabilities === null) {
|
|
183
|
+
errors.push('Agent config "capabilities" must be an object');
|
|
184
|
+
} else {
|
|
185
|
+
const caps = c.capabilities as Record<string, unknown>;
|
|
186
|
+
|
|
187
|
+
if (typeof caps.web_search !== 'boolean') {
|
|
188
|
+
errors.push('capabilities.web_search must be a boolean');
|
|
189
|
+
}
|
|
190
|
+
if (typeof caps.structured_output !== 'boolean') {
|
|
191
|
+
errors.push('capabilities.structured_output must be a boolean');
|
|
192
|
+
}
|
|
193
|
+
if (typeof caps.external_apis !== 'boolean') {
|
|
194
|
+
errors.push('capabilities.external_apis must be a boolean');
|
|
195
|
+
}
|
|
196
|
+
if (typeof caps.long_context !== 'boolean') {
|
|
197
|
+
errors.push('capabilities.long_context must be a boolean');
|
|
198
|
+
}
|
|
199
|
+
if (typeof caps.file_io_local !== 'boolean') {
|
|
200
|
+
errors.push('capabilities.file_io_local must be a boolean');
|
|
201
|
+
}
|
|
202
|
+
if (!Array.isArray(caps.workspace_tools)) {
|
|
203
|
+
errors.push('capabilities.workspace_tools must be an array');
|
|
204
|
+
}
|
|
205
|
+
if (!['low', 'medium', 'high'].includes(caps.safety_restriction_level as string)) {
|
|
206
|
+
errors.push('capabilities.safety_restriction_level must be "low", "medium", or "high"');
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Validate tier (optional)
|
|
212
|
+
if (c.tier !== undefined) {
|
|
213
|
+
if (!['premium', 'budget', 'free'].includes(c.tier as string)) {
|
|
214
|
+
errors.push('Agent config "tier" must be "premium", "budget", or "free"');
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Validate supported_bounty_types (optional)
|
|
219
|
+
if (c.supported_bounty_types !== undefined) {
|
|
220
|
+
if (!Array.isArray(c.supported_bounty_types)) {
|
|
221
|
+
errors.push('Agent config "supported_bounty_types" must be an array');
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return { valid: errors.length === 0, errors };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Type guard to check if an unknown value is a valid AgentConfig.
|
|
230
|
+
*
|
|
231
|
+
* @param value - The value to check
|
|
232
|
+
* @returns True if the value is a valid AgentConfig
|
|
233
|
+
*/
|
|
234
|
+
export function isAgentConfig(value: unknown): value is AgentConfig {
|
|
235
|
+
return validateAgentConfig(value).valid;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Parses a YAML string into an AgentConfig object.
|
|
241
|
+
* Validates the parsed config against the schema.
|
|
242
|
+
*
|
|
243
|
+
* @param yamlContent - The YAML string to parse
|
|
244
|
+
* @returns The parsed and validated AgentConfig, or throws on error
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* import { readFile } from 'node:fs/promises';
|
|
248
|
+
* const yaml = await readFile('agent.yaml', 'utf-8');
|
|
249
|
+
* const config = parseAgentConfigYaml(yaml);
|
|
250
|
+
*/
|
|
251
|
+
export async function parseAgentConfigYaml(
|
|
252
|
+
yamlContent: string
|
|
253
|
+
): Promise<{ config: AgentConfig | null; validation: ValidationResult }> {
|
|
254
|
+
const { parse } = await import('yaml');
|
|
255
|
+
const parsed = parse(yamlContent) as unknown;
|
|
256
|
+
const validation = validateAgentConfig(parsed);
|
|
257
|
+
|
|
258
|
+
if (validation.valid) {
|
|
259
|
+
return { config: parsed as AgentConfig, validation };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return { config: null, validation };
|
|
263
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agents Module - Configuration-driven AI agent definitions
|
|
3
|
+
*
|
|
4
|
+
* @module agents
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
AgentConfig,
|
|
9
|
+
ModelProvider,
|
|
10
|
+
ValidationResult,
|
|
11
|
+
validateAgentConfig,
|
|
12
|
+
isAgentConfig,
|
|
13
|
+
parseAgentConfigYaml,
|
|
14
|
+
} from './agent.schema.js';
|
|
15
|
+
|
|
16
|
+
// Registered Agent Schema - Production WAIdex registration
|
|
17
|
+
export {
|
|
18
|
+
type DeploymentProvider,
|
|
19
|
+
type AgentSpecialization,
|
|
20
|
+
type TaskType,
|
|
21
|
+
type AgentFramework,
|
|
22
|
+
type ExtendedModelProvider,
|
|
23
|
+
type AgentEndpoint,
|
|
24
|
+
type ModelInfo,
|
|
25
|
+
type DeploymentInfo,
|
|
26
|
+
type AgentStats,
|
|
27
|
+
type RegisteredAgent,
|
|
28
|
+
type RegisterAgentRequest,
|
|
29
|
+
type HealthCheckRequest,
|
|
30
|
+
type HealthCheckResponse,
|
|
31
|
+
validateRegisterAgentRequest,
|
|
32
|
+
createDefaultStats,
|
|
33
|
+
createDefaultDeploymentInfo,
|
|
34
|
+
generateSlug,
|
|
35
|
+
} from './registered-agent.schema.js';
|
|
36
|
+
|
|
37
|
+
export {
|
|
38
|
+
loadAllAgents,
|
|
39
|
+
getAgent,
|
|
40
|
+
getAllAgentIds,
|
|
41
|
+
getAllAgents,
|
|
42
|
+
hasAgent,
|
|
43
|
+
} from './registry.js';
|
|
44
|
+
|
|
45
|
+
export {
|
|
46
|
+
runOpenAIAgent,
|
|
47
|
+
mockOpenAIAgent,
|
|
48
|
+
type LeadData,
|
|
49
|
+
type AgentRunResult,
|
|
50
|
+
} from './adapters/openai.adapter.js';
|
|
51
|
+
|
|
52
|
+
export {
|
|
53
|
+
runClaudeAgent,
|
|
54
|
+
mockClaudeAgent,
|
|
55
|
+
} from './adapters/anthropic.adapter.js';
|
|
56
|
+
|
|
57
|
+
export {
|
|
58
|
+
runGeminiAgent,
|
|
59
|
+
mockGeminiAgent,
|
|
60
|
+
} from './adapters/gemini.adapter.js';
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registered Agent Schema - Production-ready agent registration for WAIdex
|
|
3
|
+
*
|
|
4
|
+
* A RegisteredAgent extends AgentConfig with endpoint, deployment, and
|
|
5
|
+
* competition data needed for production battles.
|
|
6
|
+
*
|
|
7
|
+
* @module agents/registered-agent.schema
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { AgentConfig } from './agent.schema.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Supported deployment providers for BYOD (Bring Your Own Deploy).
|
|
14
|
+
*/
|
|
15
|
+
export type DeploymentProvider =
|
|
16
|
+
| 'railway'
|
|
17
|
+
| 'render'
|
|
18
|
+
| 'flyio'
|
|
19
|
+
| 'vercel'
|
|
20
|
+
| 'cloudrun'
|
|
21
|
+
| 'self-hosted'
|
|
22
|
+
| 'tunnel';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Agent specialization categories for matchmaking.
|
|
26
|
+
*/
|
|
27
|
+
export type AgentSpecialization =
|
|
28
|
+
| 'cold-email'
|
|
29
|
+
| 'lead-qualification'
|
|
30
|
+
| 'research'
|
|
31
|
+
| 'code-generation'
|
|
32
|
+
| 'data-analysis'
|
|
33
|
+
| 'customer-support'
|
|
34
|
+
| 'content-creation';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Task types that agents can perform in battles.
|
|
38
|
+
*/
|
|
39
|
+
export type TaskType =
|
|
40
|
+
| 'qualify_lead'
|
|
41
|
+
| 'write_cold_email'
|
|
42
|
+
| 'research_company'
|
|
43
|
+
| 'fix_bug'
|
|
44
|
+
| 'analyze_data'
|
|
45
|
+
| 'summarize_document'
|
|
46
|
+
| 'generate_report';
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Supported agent frameworks.
|
|
50
|
+
*/
|
|
51
|
+
export type AgentFramework =
|
|
52
|
+
| 'agno'
|
|
53
|
+
| 'adk'
|
|
54
|
+
| 'langchain'
|
|
55
|
+
| 'langgraph'
|
|
56
|
+
| 'letta'
|
|
57
|
+
| 'custom';
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Supported model providers (extended for production).
|
|
61
|
+
*/
|
|
62
|
+
export type ExtendedModelProvider =
|
|
63
|
+
| 'ollama'
|
|
64
|
+
| 'gemini'
|
|
65
|
+
| 'openai'
|
|
66
|
+
| 'anthropic'
|
|
67
|
+
| 'groq'
|
|
68
|
+
| 'together'
|
|
69
|
+
| 'local';
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Agent endpoint configuration for battle communication.
|
|
73
|
+
*/
|
|
74
|
+
export interface AgentEndpoint {
|
|
75
|
+
/** The full URL to the agent's chat/completion endpoint */
|
|
76
|
+
url: string;
|
|
77
|
+
/** Authentication type */
|
|
78
|
+
authType: 'bearer' | 'api-key' | 'none';
|
|
79
|
+
/** Header name for auth (default: 'Authorization') */
|
|
80
|
+
authHeader?: string;
|
|
81
|
+
/** Encrypted auth token - WAIESL uses this to call the agent */
|
|
82
|
+
encryptedAuthToken?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Model information for the agent.
|
|
87
|
+
*/
|
|
88
|
+
export interface ModelInfo {
|
|
89
|
+
/** The model provider */
|
|
90
|
+
provider: ExtendedModelProvider;
|
|
91
|
+
/** Model identifier (e.g., 'llama3.3:70b', 'gemini-2.0-flash') */
|
|
92
|
+
modelId: string;
|
|
93
|
+
/** Whether the model runs locally (affects latency expectations) */
|
|
94
|
+
isLocal: boolean;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Deployment status and metadata.
|
|
99
|
+
*/
|
|
100
|
+
export interface DeploymentInfo {
|
|
101
|
+
/** Where the agent is deployed */
|
|
102
|
+
provider: DeploymentProvider;
|
|
103
|
+
/** Current status */
|
|
104
|
+
status: 'online' | 'offline' | 'degraded' | 'deploying';
|
|
105
|
+
/** Last successful health check */
|
|
106
|
+
lastHealthCheck: Date | null;
|
|
107
|
+
/** Average response latency in milliseconds */
|
|
108
|
+
averageLatencyMs: number;
|
|
109
|
+
/** Uptime percentage (0-100) */
|
|
110
|
+
uptimePercent: number;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Competition statistics for the agent.
|
|
115
|
+
*/
|
|
116
|
+
export interface AgentStats {
|
|
117
|
+
/** Total battles participated */
|
|
118
|
+
totalBattles: number;
|
|
119
|
+
/** Total wins */
|
|
120
|
+
wins: number;
|
|
121
|
+
/** Total losses */
|
|
122
|
+
losses: number;
|
|
123
|
+
/** Win rate (0-1) */
|
|
124
|
+
winRate: number;
|
|
125
|
+
/** Total earnings in USD */
|
|
126
|
+
totalEarnings: number;
|
|
127
|
+
/** ELO rating for matchmaking */
|
|
128
|
+
elo: number;
|
|
129
|
+
/** Current ranking in the league */
|
|
130
|
+
rank?: number;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* A fully registered agent in the WAIdex registry.
|
|
135
|
+
* Extends basic AgentConfig with production-ready fields.
|
|
136
|
+
*/
|
|
137
|
+
export interface RegisteredAgent extends AgentConfig {
|
|
138
|
+
/** URL-safe slug for the agent */
|
|
139
|
+
slug: string;
|
|
140
|
+
/** User ID who owns this agent */
|
|
141
|
+
ownerId: string;
|
|
142
|
+
|
|
143
|
+
/** Endpoint configuration for battle communication */
|
|
144
|
+
endpoint: AgentEndpoint;
|
|
145
|
+
|
|
146
|
+
/** Agent specializations for matchmaking */
|
|
147
|
+
specializations: AgentSpecialization[];
|
|
148
|
+
|
|
149
|
+
/** Task types this agent can perform */
|
|
150
|
+
supportedTasks: TaskType[];
|
|
151
|
+
|
|
152
|
+
/** Framework used to build the agent */
|
|
153
|
+
framework: AgentFramework;
|
|
154
|
+
|
|
155
|
+
/** Model information */
|
|
156
|
+
modelInfo: ModelInfo;
|
|
157
|
+
|
|
158
|
+
/** Deployment information */
|
|
159
|
+
deployment: DeploymentInfo;
|
|
160
|
+
|
|
161
|
+
/** Competition statistics */
|
|
162
|
+
stats: AgentStats;
|
|
163
|
+
|
|
164
|
+
/** Optional avatar URL */
|
|
165
|
+
avatar?: string;
|
|
166
|
+
|
|
167
|
+
/** Optional description */
|
|
168
|
+
description?: string;
|
|
169
|
+
|
|
170
|
+
/** Whether the agent is publicly listed */
|
|
171
|
+
isPublic: boolean;
|
|
172
|
+
|
|
173
|
+
/** Whether the agent is available for battles */
|
|
174
|
+
isActive: boolean;
|
|
175
|
+
|
|
176
|
+
/** Creation timestamp */
|
|
177
|
+
createdAt: Date;
|
|
178
|
+
|
|
179
|
+
/** Last update timestamp */
|
|
180
|
+
updatedAt: Date;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Request body for registering a new agent.
|
|
185
|
+
*/
|
|
186
|
+
export interface RegisterAgentRequest {
|
|
187
|
+
/** Agent name */
|
|
188
|
+
name: string;
|
|
189
|
+
/** System prompt */
|
|
190
|
+
prompt: string;
|
|
191
|
+
/** Strategy description */
|
|
192
|
+
strategyDescription: string;
|
|
193
|
+
/** Tools the agent can use */
|
|
194
|
+
toolAccess: string[];
|
|
195
|
+
/** Cost ceiling */
|
|
196
|
+
costCeiling: number;
|
|
197
|
+
|
|
198
|
+
/** Endpoint URL */
|
|
199
|
+
endpointUrl: string;
|
|
200
|
+
/** Endpoint auth type */
|
|
201
|
+
endpointAuthType: 'bearer' | 'api-key' | 'none';
|
|
202
|
+
/** Auth token (will be encrypted) */
|
|
203
|
+
authToken?: string;
|
|
204
|
+
|
|
205
|
+
/** Specializations */
|
|
206
|
+
specializations: AgentSpecialization[];
|
|
207
|
+
/** Supported tasks */
|
|
208
|
+
supportedTasks: TaskType[];
|
|
209
|
+
|
|
210
|
+
/** Framework */
|
|
211
|
+
framework: AgentFramework;
|
|
212
|
+
|
|
213
|
+
/** Model provider */
|
|
214
|
+
modelProvider: ExtendedModelProvider;
|
|
215
|
+
/** Model ID */
|
|
216
|
+
modelId: string;
|
|
217
|
+
/** Is model local? */
|
|
218
|
+
isLocalModel: boolean;
|
|
219
|
+
|
|
220
|
+
/** Deployment provider */
|
|
221
|
+
deploymentProvider: DeploymentProvider;
|
|
222
|
+
|
|
223
|
+
/** Public listing */
|
|
224
|
+
isPublic?: boolean;
|
|
225
|
+
|
|
226
|
+
/** Avatar URL */
|
|
227
|
+
avatar?: string;
|
|
228
|
+
/** Description */
|
|
229
|
+
description?: string;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Health check request sent to agents.
|
|
234
|
+
*/
|
|
235
|
+
export interface HealthCheckRequest {
|
|
236
|
+
type: 'health_check';
|
|
237
|
+
timestamp: string;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Expected health check response from agents.
|
|
242
|
+
*/
|
|
243
|
+
export interface HealthCheckResponse {
|
|
244
|
+
status: 'healthy' | 'degraded' | 'unhealthy';
|
|
245
|
+
version: string;
|
|
246
|
+
capabilities: string[];
|
|
247
|
+
averageResponseTimeMs?: number;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Validates a RegisterAgentRequest.
|
|
252
|
+
*
|
|
253
|
+
* @param request - The request to validate
|
|
254
|
+
* @returns Validation result
|
|
255
|
+
*/
|
|
256
|
+
export function validateRegisterAgentRequest(
|
|
257
|
+
request: unknown
|
|
258
|
+
): { valid: boolean; errors: string[] } {
|
|
259
|
+
const errors: string[] = [];
|
|
260
|
+
|
|
261
|
+
if (typeof request !== 'object' || request === null) {
|
|
262
|
+
return { valid: false, errors: ['Request must be an object'] };
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const r = request as Record<string, unknown>;
|
|
266
|
+
|
|
267
|
+
// Required string fields
|
|
268
|
+
const requiredStrings = ['name', 'prompt', 'strategyDescription', 'endpointUrl', 'modelId'];
|
|
269
|
+
for (const field of requiredStrings) {
|
|
270
|
+
if (typeof r[field] !== 'string' || (r[field] as string).trim() === '') {
|
|
271
|
+
errors.push(`"${field}" is required and must be a non-empty string`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Validate endpoint URL
|
|
276
|
+
if (typeof r.endpointUrl === 'string') {
|
|
277
|
+
try {
|
|
278
|
+
new URL(r.endpointUrl);
|
|
279
|
+
} catch {
|
|
280
|
+
errors.push('"endpointUrl" must be a valid URL');
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Validate arrays
|
|
285
|
+
if (!Array.isArray(r.toolAccess)) {
|
|
286
|
+
errors.push('"toolAccess" must be an array');
|
|
287
|
+
}
|
|
288
|
+
if (!Array.isArray(r.specializations)) {
|
|
289
|
+
errors.push('"specializations" must be an array');
|
|
290
|
+
}
|
|
291
|
+
if (!Array.isArray(r.supportedTasks)) {
|
|
292
|
+
errors.push('"supportedTasks" must be an array');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Validate enums
|
|
296
|
+
const validAuthTypes = ['bearer', 'api-key', 'none'];
|
|
297
|
+
if (!validAuthTypes.includes(r.endpointAuthType as string)) {
|
|
298
|
+
errors.push('"endpointAuthType" must be "bearer", "api-key", or "none"');
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const validFrameworks: AgentFramework[] = ['agno', 'adk', 'langchain', 'langgraph', 'letta', 'custom'];
|
|
302
|
+
if (!validFrameworks.includes(r.framework as AgentFramework)) {
|
|
303
|
+
errors.push(`"framework" must be one of: ${validFrameworks.join(', ')}`);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const validProviders: ExtendedModelProvider[] = ['ollama', 'gemini', 'openai', 'anthropic', 'groq', 'together', 'local'];
|
|
307
|
+
if (!validProviders.includes(r.modelProvider as ExtendedModelProvider)) {
|
|
308
|
+
errors.push(`"modelProvider" must be one of: ${validProviders.join(', ')}`);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Validate costCeiling
|
|
312
|
+
if (typeof r.costCeiling !== 'number' || r.costCeiling <= 0) {
|
|
313
|
+
errors.push('"costCeiling" must be a positive number');
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return { valid: errors.length === 0, errors };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Creates default stats for a new agent.
|
|
321
|
+
*/
|
|
322
|
+
export function createDefaultStats(): AgentStats {
|
|
323
|
+
return {
|
|
324
|
+
totalBattles: 0,
|
|
325
|
+
wins: 0,
|
|
326
|
+
losses: 0,
|
|
327
|
+
winRate: 0,
|
|
328
|
+
totalEarnings: 0,
|
|
329
|
+
elo: 1000, // Starting ELO
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Creates default deployment info for a new agent.
|
|
335
|
+
*/
|
|
336
|
+
export function createDefaultDeploymentInfo(
|
|
337
|
+
provider: DeploymentProvider
|
|
338
|
+
): DeploymentInfo {
|
|
339
|
+
return {
|
|
340
|
+
provider,
|
|
341
|
+
status: 'offline',
|
|
342
|
+
lastHealthCheck: null,
|
|
343
|
+
averageLatencyMs: 0,
|
|
344
|
+
uptimePercent: 0,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Generates a URL-safe slug from a name.
|
|
350
|
+
*/
|
|
351
|
+
export function generateSlug(name: string): string {
|
|
352
|
+
return name
|
|
353
|
+
.toLowerCase()
|
|
354
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
355
|
+
.replace(/^-|-$/g, '');
|
|
356
|
+
}
|