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.
Files changed (113) hide show
  1. package/README.md +261 -0
  2. package/package.json +95 -0
  3. package/src/agents/README.md +139 -0
  4. package/src/agents/adapters/anthropic.adapter.ts +166 -0
  5. package/src/agents/adapters/dalle.adapter.ts +145 -0
  6. package/src/agents/adapters/gemini.adapter.ts +134 -0
  7. package/src/agents/adapters/imagen.adapter.ts +106 -0
  8. package/src/agents/adapters/nano-banana.adapter.ts +129 -0
  9. package/src/agents/adapters/openai.adapter.ts +165 -0
  10. package/src/agents/adapters/veo.adapter.ts +130 -0
  11. package/src/agents/agent.schema.property.test.ts +379 -0
  12. package/src/agents/agent.schema.test.ts +148 -0
  13. package/src/agents/agent.schema.ts +263 -0
  14. package/src/agents/index.ts +60 -0
  15. package/src/agents/registered-agent.schema.ts +356 -0
  16. package/src/agents/registry.ts +97 -0
  17. package/src/agents/tournament-configs.property.test.ts +266 -0
  18. package/src/cli/README.md +145 -0
  19. package/src/cli/commands/define.ts +79 -0
  20. package/src/cli/commands/list.ts +46 -0
  21. package/src/cli/commands/logs.ts +83 -0
  22. package/src/cli/commands/run.ts +416 -0
  23. package/src/cli/commands/verify.ts +110 -0
  24. package/src/cli/index.ts +81 -0
  25. package/src/config/README.md +128 -0
  26. package/src/config/env.ts +262 -0
  27. package/src/config/index.ts +19 -0
  28. package/src/eval/README.md +318 -0
  29. package/src/eval/ai-judge.test.ts +435 -0
  30. package/src/eval/ai-judge.ts +368 -0
  31. package/src/eval/code-validators.ts +414 -0
  32. package/src/eval/evaluateOutcome.property.test.ts +1174 -0
  33. package/src/eval/evaluateOutcome.ts +591 -0
  34. package/src/eval/immigration-validators.ts +122 -0
  35. package/src/eval/index.ts +90 -0
  36. package/src/eval/judge-cache.ts +402 -0
  37. package/src/eval/tournament-validators.property.test.ts +439 -0
  38. package/src/eval/validators.property.test.ts +1118 -0
  39. package/src/eval/validators.ts +1199 -0
  40. package/src/eval/weighted-scorer.ts +285 -0
  41. package/src/index.ts +17 -0
  42. package/src/league/README.md +188 -0
  43. package/src/league/health-check.ts +353 -0
  44. package/src/league/index.ts +93 -0
  45. package/src/league/killAgent.ts +151 -0
  46. package/src/league/league.test.ts +1151 -0
  47. package/src/league/runLeague.ts +843 -0
  48. package/src/league/scoreAgent.ts +175 -0
  49. package/src/modules/omnibridge/__tests__/.gitkeep +1 -0
  50. package/src/modules/omnibridge/__tests__/auth-tunnel.property.test.ts +524 -0
  51. package/src/modules/omnibridge/__tests__/deterministic-logger.property.test.ts +965 -0
  52. package/src/modules/omnibridge/__tests__/ghost-api.property.test.ts +461 -0
  53. package/src/modules/omnibridge/__tests__/omnibridge-integration.test.ts +542 -0
  54. package/src/modules/omnibridge/__tests__/parallel-executor.property.test.ts +671 -0
  55. package/src/modules/omnibridge/__tests__/semantic-normalizer.property.test.ts +521 -0
  56. package/src/modules/omnibridge/__tests__/semantic-normalizer.test.ts +254 -0
  57. package/src/modules/omnibridge/__tests__/session-vault.property.test.ts +367 -0
  58. package/src/modules/omnibridge/__tests__/shadow-session.property.test.ts +523 -0
  59. package/src/modules/omnibridge/__tests__/triangulation-engine.property.test.ts +292 -0
  60. package/src/modules/omnibridge/__tests__/verification-engine.property.test.ts +769 -0
  61. package/src/modules/omnibridge/api/.gitkeep +1 -0
  62. package/src/modules/omnibridge/api/ghost-api.ts +1087 -0
  63. package/src/modules/omnibridge/auth/.gitkeep +1 -0
  64. package/src/modules/omnibridge/auth/auth-tunnel.ts +843 -0
  65. package/src/modules/omnibridge/auth/session-vault.ts +577 -0
  66. package/src/modules/omnibridge/core/.gitkeep +1 -0
  67. package/src/modules/omnibridge/core/semantic-normalizer.ts +702 -0
  68. package/src/modules/omnibridge/core/triangulation-engine.ts +530 -0
  69. package/src/modules/omnibridge/core/types.ts +610 -0
  70. package/src/modules/omnibridge/execution/.gitkeep +1 -0
  71. package/src/modules/omnibridge/execution/deterministic-logger.ts +629 -0
  72. package/src/modules/omnibridge/execution/parallel-executor.ts +542 -0
  73. package/src/modules/omnibridge/execution/shadow-session.ts +794 -0
  74. package/src/modules/omnibridge/index.ts +212 -0
  75. package/src/modules/omnibridge/omnibridge.ts +510 -0
  76. package/src/modules/omnibridge/verification/.gitkeep +1 -0
  77. package/src/modules/omnibridge/verification/verification-engine.ts +783 -0
  78. package/src/outcomes/README.md +75 -0
  79. package/src/outcomes/acquire-pilot-customer.ts +297 -0
  80. package/src/outcomes/code-delivery-outcomes.ts +89 -0
  81. package/src/outcomes/code-outcomes.ts +256 -0
  82. package/src/outcomes/code_review_battle.test.ts +135 -0
  83. package/src/outcomes/code_review_battle.ts +135 -0
  84. package/src/outcomes/cold_email_battle.ts +97 -0
  85. package/src/outcomes/content_creation_battle.ts +160 -0
  86. package/src/outcomes/f1_stem_opt_compliance.ts +61 -0
  87. package/src/outcomes/index.ts +107 -0
  88. package/src/outcomes/lead_gen_battle.test.ts +113 -0
  89. package/src/outcomes/lead_gen_battle.ts +99 -0
  90. package/src/outcomes/outcome.schema.property.test.ts +229 -0
  91. package/src/outcomes/outcome.schema.ts +187 -0
  92. package/src/outcomes/qualified_sales_interest.ts +118 -0
  93. package/src/outcomes/swarm_planner.property.test.ts +370 -0
  94. package/src/outcomes/swarm_planner.ts +96 -0
  95. package/src/outcomes/web_extraction.ts +234 -0
  96. package/src/runtime/README.md +220 -0
  97. package/src/runtime/agentRunner.test.ts +341 -0
  98. package/src/runtime/agentRunner.ts +746 -0
  99. package/src/runtime/claudeAdapter.ts +232 -0
  100. package/src/runtime/costTracker.ts +123 -0
  101. package/src/runtime/index.ts +34 -0
  102. package/src/runtime/modelAdapter.property.test.ts +305 -0
  103. package/src/runtime/modelAdapter.ts +144 -0
  104. package/src/runtime/openaiAdapter.ts +235 -0
  105. package/src/utils/README.md +122 -0
  106. package/src/utils/command-runner.ts +134 -0
  107. package/src/utils/cost-guard.ts +379 -0
  108. package/src/utils/errors.test.ts +290 -0
  109. package/src/utils/errors.ts +442 -0
  110. package/src/utils/index.ts +37 -0
  111. package/src/utils/logger.test.ts +361 -0
  112. package/src/utils/logger.ts +419 -0
  113. 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
+ }