Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
"vitest": "1.6.0"
},
"dependencies": {
"@agentx/agent-platform": "workspace:*",
"@agentx/core-runtime": "workspace:*",
"@agentx/provider-sdk": "workspace:*",
"@agentx/runtime-adapters": "workspace:*",
"@agentx/runtime-production": "workspace:*",
"@agentx/tool-sdk": "workspace:*",
Expand Down
9 changes: 7 additions & 2 deletions apps/cli/src/commands/submit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ import { getRuntime } from '../lib/runtime.js';
import { TaskStatus, TaskPriority } from '@agentx/core-runtime';

export async function submit(args: string[]): Promise<string> {
const goal = args.join(' ');
// Parse --role flag
const roleIndex = args.findIndex((a) => a === '--role');
const assignedRole = roleIndex >= 0 ? args[roleIndex + 1] : 'coder';
const goal = args.filter((a) => !a.startsWith('--') && a !== '--role').join(' ');

if (!goal) {
throw new Error('Usage: agentx submit "<goal>"');
throw new Error('Usage: agentx submit "<goal>" [--role <agent-role>]');
}

const { scheduler } = getRuntime();
Expand All @@ -22,6 +26,7 @@ export async function submit(args: string[]): Promise<string> {
rootTaskId: taskId,
dependsOn: [],
traceId: graphId,
assignedAgentRole: assignedRole,
metadata: {
retryCount: 0,
},
Expand Down
54 changes: 54 additions & 0 deletions apps/cli/src/lib/agent-registry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import type { TaskModel, TaskContext } from '@agentx/core-runtime';

export interface Agent {
readonly id: string;
readonly role: string;
execute(task: TaskModel, context: unknown): Promise<unknown>;
}

export class AgentRegistry {
private agents = new Map<string, Agent>();

constructor() {}

register(agent: Agent): void {
this.agents.set(agent.id, agent);
}

unregister(agentId: string): void {
this.agents.delete(agentId);
}

get(agentId: string): Agent | undefined {
return this.agents.get(agentId);
}

getByRole(role: string): Agent | undefined {
for (const agent of this.agents.values()) {
if (agent.role === role) {
return agent;
}
}
return undefined;
}

list(): Agent[] {
return Array.from(this.agents.values());
}

async execute(agentId: string, task: TaskModel, context: TaskContext): Promise<unknown> {
const agent = this.get(agentId);
if (!agent) {
throw new Error(`Agent not found: ${agentId}`);
}
return agent.execute(task, context);
}

async executeByRole(role: string, task: TaskModel, context: TaskContext): Promise<unknown> {
const agent = this.getByRole(role);
if (!agent) {
throw new Error(`Agent not found for role: ${role}`);
}
return agent.execute(task, context);
}
}
55 changes: 52 additions & 3 deletions apps/cli/src/lib/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,71 @@ import { ProductionRuntime } from '@agentx/runtime-production';
import { Scheduler, InMemoryEventBus } from '@agentx/core-runtime';
import type { ITaskRepository, IEventBus } from '@agentx/core-runtime';
import { InMemoryTaskRepository } from './in-memory-task-repository.js';
import { ProviderRegistry, CredentialResolver, ProviderFactory } from '@agentx/provider-sdk';
import { CoderAgent, ReviewerAgent, TesterAgent, SecurityAgent } from '@agentx/agent-platform';
import { AgentRegistry } from './agent-registry.js';

let _runtime: ProductionRuntime | null = null;
let _testRuntime: { scheduler: Scheduler; bus: IEventBus; taskRepo: ITaskRepository } | null = null;
let _testRuntime: {
scheduler: Scheduler;
bus: IEventBus;
taskRepo: ITaskRepository;
agentRegistry: AgentRegistry;
providerRegistry: ProviderRegistry;
} | null = null;

const USE_TEST_RUNTIME = process.env.NODE_ENV === 'test' || process.env.VITEST === 'true';

function createProviderRegistry(): ProviderRegistry {
const registry = new ProviderRegistry();
const credentialResolver = new CredentialResolver();
const factory = new ProviderFactory(credentialResolver);

// Register Anthropic provider if API key is available
try {
const anthropic = factory.createProvider({
providerId: 'anthropic',
defaultModelId: 'claude-sonnet-4-20250514',
});
registry.register(anthropic);
} catch (error) {
console.warn('Anthropic provider not configured (missing API key)');
}

return registry;
}

function createAgentRegistry(providerRegistry: ProviderRegistry): AgentRegistry {
const registry = new AgentRegistry();

// Register core agents with shared ProviderRegistry
registry.register(new CoderAgent('coder-1', { providerId: 'anthropic' }, providerRegistry));
registry.register(new ReviewerAgent('reviewer-1', { providerId: 'anthropic' }, providerRegistry));
registry.register(new TesterAgent('tester-1', { providerId: 'anthropic' }, providerRegistry));
registry.register(new SecurityAgent('security-1', { providerId: 'anthropic' }, providerRegistry));

return registry;
}

export function getRuntime(): {
scheduler: Scheduler;
bus: IEventBus;
prisma?: unknown;
taskRepo: ITaskRepository;
agentRegistry: AgentRegistry;
providerRegistry: ProviderRegistry;
} {
if (USE_TEST_RUNTIME) {
if (!_testRuntime) {
const taskRepo = new InMemoryTaskRepository();
const bus = new InMemoryEventBus();
const providerRegistry = createProviderRegistry();
const agentRegistry = createAgentRegistry(providerRegistry);
const scheduler = new Scheduler(bus, taskRepo);
_testRuntime = { scheduler, bus, taskRepo };
// Wire agent registry to scheduler
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(scheduler as unknown as { setAgentRegistry: (r: unknown) => void }).setAgentRegistry(agentRegistry);
_testRuntime = { scheduler, bus, taskRepo, agentRegistry, providerRegistry };
}
return _testRuntime;
}
Expand All @@ -33,6 +80,8 @@ export function getRuntime(): {
bus: _runtime.eventBus,
prisma: _runtime.prisma,
taskRepo: _runtime.taskRepo,
agentRegistry: new AgentRegistry() as InstanceType<typeof AgentRegistry>,
providerRegistry: new ProviderRegistry() as InstanceType<typeof ProviderRegistry>,
};
}

Expand All @@ -44,4 +93,4 @@ export function resetRuntime(): void {
if (_testRuntime) {
_testRuntime = null;
}
}
}
3 changes: 2 additions & 1 deletion apps/cli/test/cli-commands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ describe('CLI submit', () => {
expect(tasks).toHaveLength(1);
const task = tasks[0];
expect(task.goal).toBe('build a REST API');
expect(['CREATED', 'RUNNING']).toContain(task.status);
// Task may be in any state from CREATED to COMPLETED/FAILED depending on agent execution
expect(task.assignedAgentRole).toBe('coder');
});

it('throws when no goal provided', async () => {
Expand Down
4 changes: 3 additions & 1 deletion apps/cli/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
{ "path": "../../packages/shared/core-runtime" },
{ "path": "../../packages/runtime/runtime-production" },
{ "path": "../../packages/runtime/runtime-adapters" },
{ "path": "../../packages/shared/tool-sdk" }
{ "path": "../../packages/shared/tool-sdk" },
{ "path": "../../packages/provider/provider-sdk" },
{ "path": "../../packages/agent/agent-platform" }
]
}
2 changes: 1 addition & 1 deletion apps/cli/tsconfig.tsbuildinfo

Large diffs are not rendered by default.

24 changes: 17 additions & 7 deletions packages/agent/agent-platform/dist/sub-agents/sub-agent.d.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,40 @@
import type { SubAgent, AgentRole, AgentConfig } from './interfaces.js';
import type { TaskModel } from '@agentx/core-runtime';
import type { CompletionResponse } from '@agentx/provider-sdk';
import { ProviderRegistry } from '@agentx/provider-sdk';
export declare class BaseSubAgent implements SubAgent {
readonly id: string;
readonly role: AgentRole;
protected readonly providerId?: string;
protected readonly promptTemplate?: string;
constructor(id: string, role: AgentRole, config?: AgentConfig);
protected readonly providerRegistry: ProviderRegistry;
constructor(id: string, role: AgentRole, config?: AgentConfig, providerRegistry?: ProviderRegistry);
buildPrompt(task: TaskModel): string;
protected callLLM(prompt: string, systemPrompt?: string, modelId?: string): Promise<CompletionResponse>;
execute(task: TaskModel, _context: unknown): Promise<unknown>;
}
export declare class PlannerAgent extends BaseSubAgent {
constructor(id: string, config?: AgentConfig);
constructor(id: string, config?: AgentConfig, providerRegistry?: ProviderRegistry);
buildPrompt(task: TaskModel): string;
}
export declare class ArchitectAgent extends BaseSubAgent {
constructor(id: string, config?: AgentConfig);
constructor(id: string, config?: AgentConfig, providerRegistry?: ProviderRegistry);
buildPrompt(task: TaskModel): string;
}
export declare class CoderAgent extends BaseSubAgent {
constructor(id: string, config?: AgentConfig);
constructor(id: string, config?: AgentConfig, providerRegistry?: ProviderRegistry);
buildPrompt(task: TaskModel): string;
}
export declare class ReviewerAgent extends BaseSubAgent {
constructor(id: string, config?: AgentConfig);
constructor(id: string, config?: AgentConfig, providerRegistry?: ProviderRegistry);
buildPrompt(task: TaskModel): string;
}
export declare class TesterAgent extends BaseSubAgent {
constructor(id: string, config?: AgentConfig);
constructor(id: string, config?: AgentConfig, providerRegistry?: ProviderRegistry);
buildPrompt(task: TaskModel): string;
}
export declare class SecurityAgent extends BaseSubAgent {
constructor(id: string, config?: AgentConfig);
constructor(id: string, config?: AgentConfig, providerRegistry?: ProviderRegistry);
buildPrompt(task: TaskModel): string;
}
//# sourceMappingURL=sub-agent.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading