family-ai-agent 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/.env.example +49 -0
- package/README.md +161 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +336 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/index.d.ts +37 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +68 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/models.d.ts +17 -0
- package/dist/config/models.d.ts.map +1 -0
- package/dist/config/models.js +128 -0
- package/dist/config/models.js.map +1 -0
- package/dist/core/agents/agent-factory.d.ts +31 -0
- package/dist/core/agents/agent-factory.d.ts.map +1 -0
- package/dist/core/agents/agent-factory.js +151 -0
- package/dist/core/agents/agent-factory.js.map +1 -0
- package/dist/core/agents/base-agent.d.ts +51 -0
- package/dist/core/agents/base-agent.d.ts.map +1 -0
- package/dist/core/agents/base-agent.js +245 -0
- package/dist/core/agents/base-agent.js.map +1 -0
- package/dist/core/agents/index.d.ts +8 -0
- package/dist/core/agents/index.d.ts.map +1 -0
- package/dist/core/agents/index.js +9 -0
- package/dist/core/agents/index.js.map +1 -0
- package/dist/core/agents/personalities/automation.d.ts +14 -0
- package/dist/core/agents/personalities/automation.d.ts.map +1 -0
- package/dist/core/agents/personalities/automation.js +146 -0
- package/dist/core/agents/personalities/automation.js.map +1 -0
- package/dist/core/agents/personalities/chat.d.ts +10 -0
- package/dist/core/agents/personalities/chat.d.ts.map +1 -0
- package/dist/core/agents/personalities/chat.js +132 -0
- package/dist/core/agents/personalities/chat.js.map +1 -0
- package/dist/core/agents/personalities/coding.d.ts +16 -0
- package/dist/core/agents/personalities/coding.d.ts.map +1 -0
- package/dist/core/agents/personalities/coding.js +166 -0
- package/dist/core/agents/personalities/coding.js.map +1 -0
- package/dist/core/agents/personalities/research.d.ts +13 -0
- package/dist/core/agents/personalities/research.d.ts.map +1 -0
- package/dist/core/agents/personalities/research.js +133 -0
- package/dist/core/agents/personalities/research.js.map +1 -0
- package/dist/core/agents/types.d.ts +102 -0
- package/dist/core/agents/types.d.ts.map +1 -0
- package/dist/core/agents/types.js +2 -0
- package/dist/core/agents/types.js.map +1 -0
- package/dist/core/orchestrator/graph.d.ts +118 -0
- package/dist/core/orchestrator/graph.d.ts.map +1 -0
- package/dist/core/orchestrator/graph.js +233 -0
- package/dist/core/orchestrator/graph.js.map +1 -0
- package/dist/database/client.d.ts +19 -0
- package/dist/database/client.d.ts.map +1 -0
- package/dist/database/client.js +95 -0
- package/dist/database/client.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +67 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/openrouter-client.d.ts +45 -0
- package/dist/llm/openrouter-client.d.ts.map +1 -0
- package/dist/llm/openrouter-client.js +155 -0
- package/dist/llm/openrouter-client.js.map +1 -0
- package/dist/memory/conversation/index.d.ts +37 -0
- package/dist/memory/conversation/index.d.ts.map +1 -0
- package/dist/memory/conversation/index.js +196 -0
- package/dist/memory/conversation/index.js.map +1 -0
- package/dist/memory/index.d.ts +4 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +5 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/knowledge-base/index.d.ts +51 -0
- package/dist/memory/knowledge-base/index.d.ts.map +1 -0
- package/dist/memory/knowledge-base/index.js +222 -0
- package/dist/memory/knowledge-base/index.js.map +1 -0
- package/dist/memory/longterm/vector-store.d.ts +44 -0
- package/dist/memory/longterm/vector-store.d.ts.map +1 -0
- package/dist/memory/longterm/vector-store.js +229 -0
- package/dist/memory/longterm/vector-store.js.map +1 -0
- package/dist/safety/audit-logger.d.ts +68 -0
- package/dist/safety/audit-logger.d.ts.map +1 -0
- package/dist/safety/audit-logger.js +215 -0
- package/dist/safety/audit-logger.js.map +1 -0
- package/dist/safety/guardrails/input-guardrail.d.ts +21 -0
- package/dist/safety/guardrails/input-guardrail.d.ts.map +1 -0
- package/dist/safety/guardrails/input-guardrail.js +145 -0
- package/dist/safety/guardrails/input-guardrail.js.map +1 -0
- package/dist/safety/guardrails/output-guardrail.d.ts +18 -0
- package/dist/safety/guardrails/output-guardrail.d.ts.map +1 -0
- package/dist/safety/guardrails/output-guardrail.js +125 -0
- package/dist/safety/guardrails/output-guardrail.js.map +1 -0
- package/dist/safety/index.d.ts +4 -0
- package/dist/safety/index.d.ts.map +1 -0
- package/dist/safety/index.js +5 -0
- package/dist/safety/index.js.map +1 -0
- package/dist/utils/errors.d.ts +36 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +94 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +47 -0
- package/dist/utils/logger.js.map +1 -0
- package/docker/init-db.sql +149 -0
- package/docker/sandbox/Dockerfile.sandbox +29 -0
- package/docker-compose.yml +61 -0
- package/package.json +80 -0
- package/src/cli/index.ts +392 -0
- package/src/config/index.ts +85 -0
- package/src/config/models.ts +156 -0
- package/src/core/agents/agent-factory.ts +192 -0
- package/src/core/agents/base-agent.ts +333 -0
- package/src/core/agents/index.ts +27 -0
- package/src/core/agents/personalities/automation.ts +202 -0
- package/src/core/agents/personalities/chat.ts +159 -0
- package/src/core/agents/personalities/coding.ts +227 -0
- package/src/core/agents/personalities/research.ts +177 -0
- package/src/core/agents/types.ts +124 -0
- package/src/core/orchestrator/graph.ts +305 -0
- package/src/database/client.ts +109 -0
- package/src/index.ts +104 -0
- package/src/llm/openrouter-client.ts +218 -0
- package/src/memory/conversation/index.ts +313 -0
- package/src/memory/index.ts +23 -0
- package/src/memory/knowledge-base/index.ts +357 -0
- package/src/memory/longterm/vector-store.ts +364 -0
- package/src/safety/audit-logger.ts +357 -0
- package/src/safety/guardrails/input-guardrail.ts +191 -0
- package/src/safety/guardrails/output-guardrail.ts +160 -0
- package/src/safety/index.ts +21 -0
- package/src/utils/errors.ts +120 -0
- package/src/utils/logger.ts +74 -0
- package/tsconfig.json +37 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import type { StructuredTool } from '@langchain/core/tools';
|
|
2
|
+
import { nanoid } from 'nanoid';
|
|
3
|
+
|
|
4
|
+
import { BaseAgent } from './base-agent.js';
|
|
5
|
+
import { ChatAgent, createChatAgent } from './personalities/chat.js';
|
|
6
|
+
import { ResearchAgent, createResearchAgent } from './personalities/research.js';
|
|
7
|
+
import { CodingAgent, createCodingAgent } from './personalities/coding.js';
|
|
8
|
+
import { AutomationAgent, createAutomationAgent } from './personalities/automation.js';
|
|
9
|
+
import type { AgentRole, AgentIdentity } from './types.js';
|
|
10
|
+
import { createLogger } from '../../utils/logger.js';
|
|
11
|
+
|
|
12
|
+
const logger = createLogger('AgentFactory');
|
|
13
|
+
|
|
14
|
+
export interface AgentPoolConfig {
|
|
15
|
+
maxAgentsPerRole: number;
|
|
16
|
+
defaultTools: StructuredTool[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const DEFAULT_POOL_CONFIG: AgentPoolConfig = {
|
|
20
|
+
maxAgentsPerRole: 3,
|
|
21
|
+
defaultTools: [],
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export class AgentFactory {
|
|
25
|
+
private config: AgentPoolConfig;
|
|
26
|
+
private agentPool: Map<AgentRole, BaseAgent[]> = new Map();
|
|
27
|
+
private allAgents: Map<string, BaseAgent> = new Map();
|
|
28
|
+
|
|
29
|
+
constructor(config: Partial<AgentPoolConfig> = {}) {
|
|
30
|
+
this.config = { ...DEFAULT_POOL_CONFIG, ...config };
|
|
31
|
+
this.initializePools();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private initializePools(): void {
|
|
35
|
+
const roles: AgentRole[] = ['chat', 'research', 'coding', 'automation'];
|
|
36
|
+
for (const role of roles) {
|
|
37
|
+
this.agentPool.set(role, []);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Create a new agent of specific type
|
|
42
|
+
createAgent(role: AgentRole, tools?: StructuredTool[]): BaseAgent {
|
|
43
|
+
const agentTools = tools ?? this.config.defaultTools;
|
|
44
|
+
|
|
45
|
+
let agent: BaseAgent;
|
|
46
|
+
|
|
47
|
+
switch (role) {
|
|
48
|
+
case 'chat':
|
|
49
|
+
agent = createChatAgent(agentTools);
|
|
50
|
+
break;
|
|
51
|
+
case 'research':
|
|
52
|
+
agent = createResearchAgent(agentTools);
|
|
53
|
+
break;
|
|
54
|
+
case 'coding':
|
|
55
|
+
agent = createCodingAgent(agentTools);
|
|
56
|
+
break;
|
|
57
|
+
case 'automation':
|
|
58
|
+
agent = createAutomationAgent(agentTools);
|
|
59
|
+
break;
|
|
60
|
+
default:
|
|
61
|
+
throw new Error(`Unknown agent role: ${role}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Register agent
|
|
65
|
+
this.allAgents.set(agent.id, agent);
|
|
66
|
+
|
|
67
|
+
// Add to pool
|
|
68
|
+
const pool = this.agentPool.get(role) ?? [];
|
|
69
|
+
pool.push(agent);
|
|
70
|
+
this.agentPool.set(role, pool);
|
|
71
|
+
|
|
72
|
+
logger.info('Agent created', { agentId: agent.id, role });
|
|
73
|
+
|
|
74
|
+
return agent;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Get or create an available agent
|
|
78
|
+
getAgent(role: AgentRole): BaseAgent {
|
|
79
|
+
const pool = this.agentPool.get(role) ?? [];
|
|
80
|
+
|
|
81
|
+
// Find an idle agent
|
|
82
|
+
const idleAgent = pool.find((a) => a.getStatus() === 'idle');
|
|
83
|
+
if (idleAgent) {
|
|
84
|
+
return idleAgent;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Create new agent if under limit
|
|
88
|
+
if (pool.length < this.config.maxAgentsPerRole) {
|
|
89
|
+
return this.createAgent(role);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Return least busy agent (waiting status preferred over executing)
|
|
93
|
+
const waitingAgent = pool.find((a) => a.getStatus() === 'waiting');
|
|
94
|
+
if (waitingAgent) {
|
|
95
|
+
return waitingAgent;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Return first agent as fallback
|
|
99
|
+
logger.warn('All agents busy, returning first agent', { role });
|
|
100
|
+
return pool[0]!;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Get agent by ID
|
|
104
|
+
getAgentById(id: string): BaseAgent | undefined {
|
|
105
|
+
return this.allAgents.get(id);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Get all agents of a role
|
|
109
|
+
getAgentsByRole(role: AgentRole): BaseAgent[] {
|
|
110
|
+
return this.agentPool.get(role) ?? [];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Get all active agent identities
|
|
114
|
+
getActiveAgentIdentities(): AgentIdentity[] {
|
|
115
|
+
return Array.from(this.allAgents.values()).map((a) => a.getInfo());
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Release/reset an agent
|
|
119
|
+
releaseAgent(agentId: string): void {
|
|
120
|
+
const agent = this.allAgents.get(agentId);
|
|
121
|
+
if (agent) {
|
|
122
|
+
agent.reset();
|
|
123
|
+
logger.debug('Agent released', { agentId });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Remove agent from pool
|
|
128
|
+
removeAgent(agentId: string): boolean {
|
|
129
|
+
const agent = this.allAgents.get(agentId);
|
|
130
|
+
if (!agent) return false;
|
|
131
|
+
|
|
132
|
+
// Remove from role pool
|
|
133
|
+
const role = agent.role as AgentRole;
|
|
134
|
+
const pool = this.agentPool.get(role) ?? [];
|
|
135
|
+
const index = pool.findIndex((a) => a.id === agentId);
|
|
136
|
+
if (index > -1) {
|
|
137
|
+
pool.splice(index, 1);
|
|
138
|
+
this.agentPool.set(role, pool);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Remove from all agents
|
|
142
|
+
this.allAgents.delete(agentId);
|
|
143
|
+
|
|
144
|
+
logger.info('Agent removed', { agentId, role });
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Get pool statistics
|
|
149
|
+
getPoolStats(): Record<AgentRole, { total: number; idle: number; busy: number }> {
|
|
150
|
+
const stats: Record<string, { total: number; idle: number; busy: number }> = {};
|
|
151
|
+
|
|
152
|
+
for (const [role, agents] of this.agentPool) {
|
|
153
|
+
const idle = agents.filter((a) => a.getStatus() === 'idle').length;
|
|
154
|
+
stats[role] = {
|
|
155
|
+
total: agents.length,
|
|
156
|
+
idle,
|
|
157
|
+
busy: agents.length - idle,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return stats as Record<AgentRole, { total: number; idle: number; busy: number }>;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Clear all agents
|
|
165
|
+
clear(): void {
|
|
166
|
+
for (const agent of this.allAgents.values()) {
|
|
167
|
+
agent.reset();
|
|
168
|
+
}
|
|
169
|
+
this.allAgents.clear();
|
|
170
|
+
this.initializePools();
|
|
171
|
+
logger.info('Agent pool cleared');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Singleton factory instance
|
|
176
|
+
let factoryInstance: AgentFactory | null = null;
|
|
177
|
+
|
|
178
|
+
export function getAgentFactory(config?: Partial<AgentPoolConfig>): AgentFactory {
|
|
179
|
+
if (!factoryInstance) {
|
|
180
|
+
factoryInstance = new AgentFactory(config);
|
|
181
|
+
}
|
|
182
|
+
return factoryInstance;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function resetAgentFactory(): void {
|
|
186
|
+
if (factoryInstance) {
|
|
187
|
+
factoryInstance.clear();
|
|
188
|
+
factoryInstance = null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export default AgentFactory;
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import { ChatOpenAI } from '@langchain/openai';
|
|
2
|
+
import { createReactAgent } from '@langchain/langgraph/prebuilt';
|
|
3
|
+
import { SystemMessage, HumanMessage, AIMessage } from '@langchain/core/messages';
|
|
4
|
+
import type { BaseMessage } from '@langchain/core/messages';
|
|
5
|
+
import type { StructuredTool } from '@langchain/core/tools';
|
|
6
|
+
import { nanoid } from 'nanoid';
|
|
7
|
+
|
|
8
|
+
import { getOpenRouterClient } from '../../llm/openrouter-client.js';
|
|
9
|
+
import { createLogger, logAgentAction } from '../../utils/logger.js';
|
|
10
|
+
import { AgentError } from '../../utils/errors.js';
|
|
11
|
+
import type {
|
|
12
|
+
AgentConfig,
|
|
13
|
+
AgentIdentity,
|
|
14
|
+
AgentState,
|
|
15
|
+
AgentStatus,
|
|
16
|
+
AgentEvent,
|
|
17
|
+
TaskDefinition,
|
|
18
|
+
TaskResult,
|
|
19
|
+
RetrievedMemory,
|
|
20
|
+
HandoffDecision,
|
|
21
|
+
} from './types.js';
|
|
22
|
+
|
|
23
|
+
const logger = createLogger('BaseAgent');
|
|
24
|
+
|
|
25
|
+
export abstract class BaseAgent {
|
|
26
|
+
protected readonly identity: AgentIdentity;
|
|
27
|
+
protected readonly model: ChatOpenAI;
|
|
28
|
+
protected readonly systemPrompt: string;
|
|
29
|
+
protected readonly tools: StructuredTool[];
|
|
30
|
+
protected readonly maxIterations: number;
|
|
31
|
+
protected readonly timeout: number;
|
|
32
|
+
|
|
33
|
+
protected status: AgentStatus = 'idle';
|
|
34
|
+
protected currentTask: TaskDefinition | null = null;
|
|
35
|
+
protected iterationCount = 0;
|
|
36
|
+
|
|
37
|
+
private eventListeners: ((event: AgentEvent) => void)[] = [];
|
|
38
|
+
|
|
39
|
+
constructor(config: AgentConfig) {
|
|
40
|
+
this.identity = config.identity;
|
|
41
|
+
this.systemPrompt = config.systemPrompt;
|
|
42
|
+
this.tools = config.tools;
|
|
43
|
+
this.maxIterations = config.maxIterations ?? 10;
|
|
44
|
+
this.timeout = config.timeout ?? 120000; // 2 minutes default
|
|
45
|
+
|
|
46
|
+
// Get model from OpenRouter
|
|
47
|
+
const client = getOpenRouterClient();
|
|
48
|
+
this.model = client.getChatModelForAgent({
|
|
49
|
+
model: config.model,
|
|
50
|
+
temperature: config.temperature ?? 0.7,
|
|
51
|
+
maxTokens: config.maxTokens,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Identity getters
|
|
56
|
+
get id(): string {
|
|
57
|
+
return this.identity.id;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get name(): string {
|
|
61
|
+
return this.identity.name;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
get role(): string {
|
|
65
|
+
return this.identity.role;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
get capabilities(): string[] {
|
|
69
|
+
return this.identity.capabilities;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
getStatus(): AgentStatus {
|
|
73
|
+
return this.status;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Abstract methods that subclasses must implement
|
|
77
|
+
protected abstract buildSystemPrompt(
|
|
78
|
+
context: Record<string, unknown>,
|
|
79
|
+
memories: RetrievedMemory[]
|
|
80
|
+
): string;
|
|
81
|
+
|
|
82
|
+
protected abstract shouldHandoff(
|
|
83
|
+
state: AgentState,
|
|
84
|
+
result: TaskResult
|
|
85
|
+
): Promise<HandoffDecision>;
|
|
86
|
+
|
|
87
|
+
// Create the LangGraph agent
|
|
88
|
+
protected createAgent() {
|
|
89
|
+
return createReactAgent({
|
|
90
|
+
llm: this.model,
|
|
91
|
+
tools: this.tools,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Main execution method
|
|
96
|
+
async execute(
|
|
97
|
+
input: string,
|
|
98
|
+
context: Record<string, unknown> = {},
|
|
99
|
+
memories: RetrievedMemory[] = []
|
|
100
|
+
): Promise<TaskResult> {
|
|
101
|
+
const taskId = nanoid();
|
|
102
|
+
const startTime = Date.now();
|
|
103
|
+
|
|
104
|
+
this.status = 'thinking';
|
|
105
|
+
this.iterationCount = 0;
|
|
106
|
+
|
|
107
|
+
this.emitEvent('started', { taskId, input });
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
// Build the full system prompt with context and memories
|
|
111
|
+
const fullSystemPrompt = this.buildSystemPrompt(context, memories);
|
|
112
|
+
|
|
113
|
+
// Create messages
|
|
114
|
+
const messages: BaseMessage[] = [
|
|
115
|
+
new SystemMessage(fullSystemPrompt),
|
|
116
|
+
new HumanMessage(input),
|
|
117
|
+
];
|
|
118
|
+
|
|
119
|
+
// Create and invoke the agent
|
|
120
|
+
const agent = this.createAgent();
|
|
121
|
+
|
|
122
|
+
this.status = 'executing';
|
|
123
|
+
this.emitEvent('thinking', { taskId });
|
|
124
|
+
|
|
125
|
+
const result = await this.invokeWithTimeout(agent, messages);
|
|
126
|
+
|
|
127
|
+
const executionTime = Date.now() - startTime;
|
|
128
|
+
this.status = 'completed';
|
|
129
|
+
|
|
130
|
+
const taskResult: TaskResult = {
|
|
131
|
+
taskId,
|
|
132
|
+
agentId: this.id,
|
|
133
|
+
status: 'success',
|
|
134
|
+
output: this.extractOutput(result),
|
|
135
|
+
executionTimeMs: executionTime,
|
|
136
|
+
metadata: {
|
|
137
|
+
iterations: this.iterationCount,
|
|
138
|
+
model: this.model.modelName,
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
this.emitEvent('completed', { taskId, result: taskResult });
|
|
143
|
+
logAgentAction(this.id, 'execute', {
|
|
144
|
+
taskId,
|
|
145
|
+
success: true,
|
|
146
|
+
duration: executionTime,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
return taskResult;
|
|
150
|
+
} catch (error) {
|
|
151
|
+
const executionTime = Date.now() - startTime;
|
|
152
|
+
this.status = 'error';
|
|
153
|
+
|
|
154
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
155
|
+
|
|
156
|
+
const taskResult: TaskResult = {
|
|
157
|
+
taskId,
|
|
158
|
+
agentId: this.id,
|
|
159
|
+
status: 'failed',
|
|
160
|
+
output: null,
|
|
161
|
+
error: errorMessage,
|
|
162
|
+
executionTimeMs: executionTime,
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
this.emitEvent('error', { taskId, error: errorMessage });
|
|
166
|
+
logAgentAction(this.id, 'execute', {
|
|
167
|
+
taskId,
|
|
168
|
+
success: false,
|
|
169
|
+
error: errorMessage,
|
|
170
|
+
duration: executionTime,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
throw new AgentError(
|
|
174
|
+
`Agent execution failed: ${errorMessage}`,
|
|
175
|
+
this.id,
|
|
176
|
+
{ taskId, originalError: errorMessage }
|
|
177
|
+
);
|
|
178
|
+
} finally {
|
|
179
|
+
this.currentTask = null;
|
|
180
|
+
this.iterationCount = 0;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Execute with timeout
|
|
185
|
+
private async invokeWithTimeout(
|
|
186
|
+
agent: ReturnType<typeof createReactAgent>,
|
|
187
|
+
messages: BaseMessage[]
|
|
188
|
+
): Promise<unknown> {
|
|
189
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
190
|
+
setTimeout(() => {
|
|
191
|
+
reject(new AgentError('Agent execution timed out', this.id));
|
|
192
|
+
}, this.timeout);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const executionPromise = agent.invoke({
|
|
196
|
+
messages,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
return Promise.race([executionPromise, timeoutPromise]);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Extract output from agent result
|
|
203
|
+
protected extractOutput(result: unknown): string {
|
|
204
|
+
if (!result || typeof result !== 'object') {
|
|
205
|
+
return String(result);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const agentResult = result as Record<string, unknown>;
|
|
209
|
+
|
|
210
|
+
if (Array.isArray(agentResult.messages)) {
|
|
211
|
+
const lastMessage = agentResult.messages[agentResult.messages.length - 1];
|
|
212
|
+
if (lastMessage && typeof lastMessage === 'object' && 'content' in lastMessage) {
|
|
213
|
+
const content = (lastMessage as AIMessage).content;
|
|
214
|
+
return typeof content === 'string' ? content : JSON.stringify(content);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return JSON.stringify(result);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Event handling
|
|
222
|
+
onEvent(listener: (event: AgentEvent) => void): () => void {
|
|
223
|
+
this.eventListeners.push(listener);
|
|
224
|
+
return () => {
|
|
225
|
+
const index = this.eventListeners.indexOf(listener);
|
|
226
|
+
if (index > -1) {
|
|
227
|
+
this.eventListeners.splice(index, 1);
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
protected emitEvent(type: AgentEvent['type'], data: Record<string, unknown>): void {
|
|
233
|
+
const event: AgentEvent = {
|
|
234
|
+
type,
|
|
235
|
+
agentId: this.id,
|
|
236
|
+
timestamp: new Date(),
|
|
237
|
+
data,
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
for (const listener of this.eventListeners) {
|
|
241
|
+
try {
|
|
242
|
+
listener(event);
|
|
243
|
+
} catch (error) {
|
|
244
|
+
logger.error('Event listener error', { error });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Stream execution for real-time output
|
|
250
|
+
async *stream(
|
|
251
|
+
input: string,
|
|
252
|
+
context: Record<string, unknown> = {},
|
|
253
|
+
memories: RetrievedMemory[] = []
|
|
254
|
+
): AsyncGenerator<string, TaskResult, unknown> {
|
|
255
|
+
const taskId = nanoid();
|
|
256
|
+
const startTime = Date.now();
|
|
257
|
+
|
|
258
|
+
this.status = 'thinking';
|
|
259
|
+
this.emitEvent('started', { taskId, input });
|
|
260
|
+
|
|
261
|
+
try {
|
|
262
|
+
const fullSystemPrompt = this.buildSystemPrompt(context, memories);
|
|
263
|
+
const messages: BaseMessage[] = [
|
|
264
|
+
new SystemMessage(fullSystemPrompt),
|
|
265
|
+
new HumanMessage(input),
|
|
266
|
+
];
|
|
267
|
+
|
|
268
|
+
const agent = this.createAgent();
|
|
269
|
+
this.status = 'executing';
|
|
270
|
+
|
|
271
|
+
let fullOutput = '';
|
|
272
|
+
|
|
273
|
+
const stream = await agent.stream({
|
|
274
|
+
messages,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
for await (const chunk of stream) {
|
|
278
|
+
if (chunk && typeof chunk === 'object') {
|
|
279
|
+
const chunkData = chunk as Record<string, unknown>;
|
|
280
|
+
if (Array.isArray(chunkData.messages)) {
|
|
281
|
+
const lastMsg = chunkData.messages[chunkData.messages.length - 1];
|
|
282
|
+
if (lastMsg && typeof lastMsg === 'object' && 'content' in lastMsg) {
|
|
283
|
+
const content = (lastMsg as AIMessage).content;
|
|
284
|
+
if (typeof content === 'string') {
|
|
285
|
+
fullOutput = content;
|
|
286
|
+
yield content;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const executionTime = Date.now() - startTime;
|
|
294
|
+
this.status = 'completed';
|
|
295
|
+
|
|
296
|
+
const taskResult: TaskResult = {
|
|
297
|
+
taskId,
|
|
298
|
+
agentId: this.id,
|
|
299
|
+
status: 'success',
|
|
300
|
+
output: fullOutput,
|
|
301
|
+
executionTimeMs: executionTime,
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
this.emitEvent('completed', { taskId, result: taskResult });
|
|
305
|
+
return taskResult;
|
|
306
|
+
} catch (error) {
|
|
307
|
+
this.status = 'error';
|
|
308
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
309
|
+
this.emitEvent('error', { taskId, error: errorMessage });
|
|
310
|
+
throw new AgentError(`Stream execution failed: ${errorMessage}`, this.id);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Check if agent can handle a task
|
|
315
|
+
canHandle(task: TaskDefinition): boolean {
|
|
316
|
+
const requiredCaps = task.requiredCapabilities || [];
|
|
317
|
+
return requiredCaps.every((cap) => this.capabilities.includes(cap));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Reset agent state
|
|
321
|
+
reset(): void {
|
|
322
|
+
this.status = 'idle';
|
|
323
|
+
this.currentTask = null;
|
|
324
|
+
this.iterationCount = 0;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Get agent info for display
|
|
328
|
+
getInfo(): AgentIdentity {
|
|
329
|
+
return { ...this.identity };
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export default BaseAgent;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Agent exports
|
|
2
|
+
export { BaseAgent } from './base-agent.js';
|
|
3
|
+
export { AgentFactory, getAgentFactory, resetAgentFactory } from './agent-factory.js';
|
|
4
|
+
|
|
5
|
+
// Personalities
|
|
6
|
+
export { ChatAgent, createChatAgent } from './personalities/chat.js';
|
|
7
|
+
export { ResearchAgent, createResearchAgent } from './personalities/research.js';
|
|
8
|
+
export { CodingAgent, createCodingAgent } from './personalities/coding.js';
|
|
9
|
+
export { AutomationAgent, createAutomationAgent } from './personalities/automation.js';
|
|
10
|
+
|
|
11
|
+
// Types
|
|
12
|
+
export type {
|
|
13
|
+
AgentRole,
|
|
14
|
+
AgentStatus,
|
|
15
|
+
AgentIdentity,
|
|
16
|
+
AgentConfig,
|
|
17
|
+
AgentState,
|
|
18
|
+
TaskDefinition,
|
|
19
|
+
TaskResult,
|
|
20
|
+
RetrievedMemory,
|
|
21
|
+
AgentMessage,
|
|
22
|
+
HandoffDecision,
|
|
23
|
+
RoutingDecision,
|
|
24
|
+
ToolResult,
|
|
25
|
+
AgentEventType,
|
|
26
|
+
AgentEvent,
|
|
27
|
+
} from './types.js';
|