confused-ai-core 0.1.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/FEATURES.md +169 -0
- package/package.json +119 -0
- package/src/agent.ts +187 -0
- package/src/agentic/index.ts +87 -0
- package/src/agentic/runner.ts +386 -0
- package/src/agentic/types.ts +91 -0
- package/src/artifacts/artifact.ts +417 -0
- package/src/artifacts/index.ts +42 -0
- package/src/artifacts/media.ts +304 -0
- package/src/cli/index.ts +122 -0
- package/src/core/base-agent.ts +151 -0
- package/src/core/context-builder.ts +106 -0
- package/src/core/index.ts +8 -0
- package/src/core/schemas.ts +17 -0
- package/src/core/types.ts +158 -0
- package/src/create-agent.ts +309 -0
- package/src/debug-logger.ts +188 -0
- package/src/dx/agent.ts +88 -0
- package/src/dx/define-agent.ts +183 -0
- package/src/dx/dev-logger.ts +57 -0
- package/src/dx/index.ts +11 -0
- package/src/errors.ts +175 -0
- package/src/execution/engine.ts +522 -0
- package/src/execution/graph-builder.ts +362 -0
- package/src/execution/index.ts +8 -0
- package/src/execution/types.ts +257 -0
- package/src/execution/worker-pool.ts +308 -0
- package/src/extensions/index.ts +123 -0
- package/src/guardrails/allowlist.ts +155 -0
- package/src/guardrails/index.ts +17 -0
- package/src/guardrails/types.ts +159 -0
- package/src/guardrails/validator.ts +265 -0
- package/src/index.ts +74 -0
- package/src/knowledge/index.ts +5 -0
- package/src/knowledge/types.ts +52 -0
- package/src/learning/in-memory-store.ts +72 -0
- package/src/learning/index.ts +6 -0
- package/src/learning/types.ts +42 -0
- package/src/llm/cache.ts +300 -0
- package/src/llm/index.ts +22 -0
- package/src/llm/model-resolver.ts +81 -0
- package/src/llm/openai-provider.ts +313 -0
- package/src/llm/openrouter-provider.ts +29 -0
- package/src/llm/types.ts +131 -0
- package/src/memory/in-memory-store.ts +255 -0
- package/src/memory/index.ts +7 -0
- package/src/memory/types.ts +193 -0
- package/src/memory/vector-store.ts +251 -0
- package/src/observability/console-logger.ts +123 -0
- package/src/observability/index.ts +12 -0
- package/src/observability/metrics.ts +85 -0
- package/src/observability/otlp-exporter.ts +417 -0
- package/src/observability/tracer.ts +105 -0
- package/src/observability/types.ts +341 -0
- package/src/orchestration/agent-adapter.ts +33 -0
- package/src/orchestration/index.ts +34 -0
- package/src/orchestration/load-balancer.ts +151 -0
- package/src/orchestration/mcp-types.ts +59 -0
- package/src/orchestration/message-bus.ts +192 -0
- package/src/orchestration/orchestrator.ts +349 -0
- package/src/orchestration/pipeline.ts +66 -0
- package/src/orchestration/supervisor.ts +107 -0
- package/src/orchestration/swarm.ts +1099 -0
- package/src/orchestration/toolkit.ts +47 -0
- package/src/orchestration/types.ts +339 -0
- package/src/planner/classical-planner.ts +383 -0
- package/src/planner/index.ts +8 -0
- package/src/planner/llm-planner.ts +353 -0
- package/src/planner/types.ts +227 -0
- package/src/planner/validator.ts +297 -0
- package/src/production/circuit-breaker.ts +290 -0
- package/src/production/graceful-shutdown.ts +251 -0
- package/src/production/health.ts +333 -0
- package/src/production/index.ts +57 -0
- package/src/production/latency-eval.ts +62 -0
- package/src/production/rate-limiter.ts +287 -0
- package/src/production/resumable-stream.ts +289 -0
- package/src/production/types.ts +81 -0
- package/src/sdk/index.ts +374 -0
- package/src/session/db-driver.ts +50 -0
- package/src/session/in-memory-store.ts +235 -0
- package/src/session/index.ts +12 -0
- package/src/session/sql-store.ts +315 -0
- package/src/session/sqlite-store.ts +61 -0
- package/src/session/types.ts +153 -0
- package/src/tools/base-tool.ts +223 -0
- package/src/tools/browser-tool.ts +123 -0
- package/src/tools/calculator-tool.ts +265 -0
- package/src/tools/file-tools.ts +394 -0
- package/src/tools/github-tool.ts +432 -0
- package/src/tools/hackernews-tool.ts +187 -0
- package/src/tools/http-tool.ts +118 -0
- package/src/tools/index.ts +99 -0
- package/src/tools/jira-tool.ts +373 -0
- package/src/tools/notion-tool.ts +322 -0
- package/src/tools/openai-tool.ts +236 -0
- package/src/tools/registry.ts +131 -0
- package/src/tools/serpapi-tool.ts +234 -0
- package/src/tools/shell-tool.ts +118 -0
- package/src/tools/slack-tool.ts +327 -0
- package/src/tools/telegram-tool.ts +127 -0
- package/src/tools/types.ts +229 -0
- package/src/tools/websearch-tool.ts +335 -0
- package/src/tools/wikipedia-tool.ts +177 -0
- package/src/tools/yfinance-tool.ts +33 -0
- package/src/voice/index.ts +17 -0
- package/src/voice/voice-provider.ts +228 -0
- package/tests/artifact.test.ts +241 -0
- package/tests/circuit-breaker.test.ts +171 -0
- package/tests/health.test.ts +192 -0
- package/tests/llm-cache.test.ts +186 -0
- package/tests/rate-limiter.test.ts +161 -0
- package/tsconfig.json +29 -0
- package/vitest.config.ts +47 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core types and interfaces for the Agent Framework
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { MemoryStore } from '../memory/types.js';
|
|
6
|
+
import type { ToolRegistry } from '../tools/types.js';
|
|
7
|
+
import type { Planner } from '../planner/types.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Unique identifier for agents, tasks, and other entities
|
|
11
|
+
*/
|
|
12
|
+
export type EntityId = string;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Agent execution state
|
|
16
|
+
*/
|
|
17
|
+
export enum AgentState {
|
|
18
|
+
IDLE = 'idle',
|
|
19
|
+
PLANNING = 'planning',
|
|
20
|
+
EXECUTING = 'executing',
|
|
21
|
+
PAUSED = 'paused',
|
|
22
|
+
COMPLETED = 'completed',
|
|
23
|
+
FAILED = 'failed',
|
|
24
|
+
CANCELLED = 'cancelled',
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Context provided to agents during execution
|
|
29
|
+
*/
|
|
30
|
+
export interface AgentContext {
|
|
31
|
+
readonly agentId: EntityId;
|
|
32
|
+
readonly memory: MemoryStore;
|
|
33
|
+
readonly tools: ToolRegistry;
|
|
34
|
+
readonly planner: Planner;
|
|
35
|
+
readonly metadata: Record<string, unknown>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Configuration for creating an agent
|
|
40
|
+
*/
|
|
41
|
+
export interface AgentConfig {
|
|
42
|
+
readonly id?: EntityId;
|
|
43
|
+
readonly name: string;
|
|
44
|
+
readonly description?: string;
|
|
45
|
+
readonly persona?: string;
|
|
46
|
+
readonly maxIterations?: number;
|
|
47
|
+
readonly timeoutMs?: number;
|
|
48
|
+
/** Enable debug logging for this agent */
|
|
49
|
+
readonly debug?: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Input to an agent execution
|
|
54
|
+
*/
|
|
55
|
+
export interface AgentInput {
|
|
56
|
+
readonly prompt: string;
|
|
57
|
+
readonly context?: Record<string, unknown>;
|
|
58
|
+
readonly attachments?: Attachment[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Output from an agent execution
|
|
63
|
+
*/
|
|
64
|
+
export interface AgentOutput {
|
|
65
|
+
readonly result: unknown;
|
|
66
|
+
readonly state: AgentState;
|
|
67
|
+
readonly metadata: ExecutionMetadata;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Attachment for agent input/output
|
|
72
|
+
*/
|
|
73
|
+
export interface Attachment {
|
|
74
|
+
readonly id: EntityId;
|
|
75
|
+
readonly type: string;
|
|
76
|
+
readonly content: unknown;
|
|
77
|
+
readonly metadata?: Record<string, unknown>;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Execution metadata
|
|
82
|
+
*/
|
|
83
|
+
export interface ExecutionMetadata {
|
|
84
|
+
readonly startTime: Date;
|
|
85
|
+
readonly endTime?: Date;
|
|
86
|
+
readonly durationMs?: number;
|
|
87
|
+
readonly iterations: number;
|
|
88
|
+
readonly tokensUsed?: number;
|
|
89
|
+
readonly cost?: number;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Hook for agent lifecycle events
|
|
94
|
+
*/
|
|
95
|
+
export interface AgentHooks {
|
|
96
|
+
beforeExecution?: (input: AgentInput, ctx: AgentContext) => Promise<void> | void;
|
|
97
|
+
afterExecution?: (output: AgentOutput, ctx: AgentContext) => Promise<void> | void;
|
|
98
|
+
onError?: (error: Error, ctx: AgentContext) => Promise<void> | void;
|
|
99
|
+
onStateChange?: (oldState: AgentState, newState: AgentState, ctx: AgentContext) => Promise<void> | void;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Abstract base class for all agents
|
|
104
|
+
*/
|
|
105
|
+
export abstract class Agent {
|
|
106
|
+
readonly id: EntityId;
|
|
107
|
+
readonly name: string;
|
|
108
|
+
readonly description: string;
|
|
109
|
+
readonly persona?: string;
|
|
110
|
+
readonly config: AgentConfig;
|
|
111
|
+
protected state: AgentState = AgentState.IDLE;
|
|
112
|
+
protected hooks: AgentHooks = {};
|
|
113
|
+
|
|
114
|
+
constructor(config: AgentConfig) {
|
|
115
|
+
this.config = config;
|
|
116
|
+
this.id = config.id ?? generateId();
|
|
117
|
+
this.name = config.name;
|
|
118
|
+
this.description = config.description ?? '';
|
|
119
|
+
this.persona = config.persona;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Execute the agent with the given input and context
|
|
124
|
+
*/
|
|
125
|
+
abstract run(input: AgentInput, ctx: AgentContext): Promise<AgentOutput>;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get current agent state
|
|
129
|
+
*/
|
|
130
|
+
getState(): AgentState {
|
|
131
|
+
return this.state;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Set lifecycle hooks
|
|
136
|
+
*/
|
|
137
|
+
setHooks(hooks: AgentHooks): void {
|
|
138
|
+
this.hooks = hooks;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Update agent state with hook notification
|
|
143
|
+
*/
|
|
144
|
+
protected async setState(newState: AgentState, ctx: AgentContext): Promise<void> {
|
|
145
|
+
const oldState = this.state;
|
|
146
|
+
this.state = newState;
|
|
147
|
+
if (this.hooks.onStateChange) {
|
|
148
|
+
await this.hooks.onStateChange(oldState, newState, ctx);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Generate a unique identifier
|
|
155
|
+
*/
|
|
156
|
+
function generateId(): EntityId {
|
|
157
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
|
158
|
+
}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One-line production-grade agent creation.
|
|
3
|
+
*
|
|
4
|
+
* Auto-configures: LLM (from env), memory/session, default tools, optional guardrails.
|
|
5
|
+
* Use createAgent({ name, instructions }) for minimal setup; override any part as needed.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { LLMProvider } from './llm/types.js';
|
|
9
|
+
import type { Message } from './llm/types.js';
|
|
10
|
+
import type { Tool, ToolRegistry, ToolMiddleware } from './tools/types.js';
|
|
11
|
+
import type { SessionStore } from './session/types.js';
|
|
12
|
+
import type { GuardrailEngine } from './guardrails/types.js';
|
|
13
|
+
import type { UserProfileStore } from './learning/types.js';
|
|
14
|
+
import type { LearningMode } from './learning/types.js';
|
|
15
|
+
import type { MemoryStore } from './memory/types.js';
|
|
16
|
+
import type { RAGEngine } from './knowledge/types.js';
|
|
17
|
+
import type { z } from 'zod';
|
|
18
|
+
import type { AgenticStreamHooks } from './agentic/types.js';
|
|
19
|
+
import type { AgenticRunResult } from './agentic/types.js';
|
|
20
|
+
import type { Logger } from './observability/types.js';
|
|
21
|
+
import { createAgenticAgent } from './agentic/index.js';
|
|
22
|
+
import { OpenAIProvider, resolveModelString, isModelString } from './llm/index.js';
|
|
23
|
+
import { HttpClientTool } from './tools/http-tool.js';
|
|
24
|
+
import { BrowserTool } from './tools/browser-tool.js';
|
|
25
|
+
import { InMemorySessionStore } from './session/index.js';
|
|
26
|
+
import { GuardrailValidator, createSensitiveDataRule } from './guardrails/index.js';
|
|
27
|
+
import { SessionState } from './session/types.js';
|
|
28
|
+
import { ConfigError } from './errors.js';
|
|
29
|
+
import { toToolRegistry, type ToolProvider } from './tools/registry.js';
|
|
30
|
+
import { createDevLogger } from './dx/dev-logger.js';
|
|
31
|
+
import { createDevToolMiddleware } from './dx/dev-logger.js';
|
|
32
|
+
|
|
33
|
+
/** Environment variable names for auto LLM config */
|
|
34
|
+
const ENV_API_KEY = 'OPENAI_API_KEY';
|
|
35
|
+
const ENV_MODEL = 'OPENAI_MODEL';
|
|
36
|
+
const ENV_BASE_URL = 'OPENAI_BASE_URL';
|
|
37
|
+
const ENV_OPENROUTER_API_KEY = 'OPENROUTER_API_KEY';
|
|
38
|
+
const ENV_OPENROUTER_MODEL = 'OPENROUTER_MODEL';
|
|
39
|
+
const OPENROUTER_BASE_URL = 'https://openrouter.ai/api/v1';
|
|
40
|
+
|
|
41
|
+
export interface CreateAgentOptions {
|
|
42
|
+
/** Agent name */
|
|
43
|
+
name: string;
|
|
44
|
+
/** System instructions / prompt */
|
|
45
|
+
instructions: string;
|
|
46
|
+
/** LLM provider. If omitted, uses OpenAI from OPENAI_API_KEY (and optional OPENAI_MODEL). */
|
|
47
|
+
llm?: LLMProvider;
|
|
48
|
+
/**
|
|
49
|
+
* Model: either a plain model id (e.g. gpt-4o) or Agno-style "provider:model_id".
|
|
50
|
+
* Examples: "openai:gpt-4o", "openrouter:anthropic/claude-3.5-sonnet", "ollama:llama3.2".
|
|
51
|
+
* Ignored if llm is provided.
|
|
52
|
+
*/
|
|
53
|
+
model?: string;
|
|
54
|
+
/** API key when using auto OpenAI. Falls back to OPENAI_API_KEY. Optional when baseURL is a local server (e.g. Ollama). */
|
|
55
|
+
apiKey?: string;
|
|
56
|
+
/** Base URL for the API (e.g. http://localhost:11434/v1 for Ollama). Falls back to OPENAI_BASE_URL. */
|
|
57
|
+
baseURL?: string;
|
|
58
|
+
/** Use OpenRouter (many models via one API). Set OPENROUTER_API_KEY or pass apiKey/model here. */
|
|
59
|
+
openRouter?: { apiKey?: string; model?: string };
|
|
60
|
+
/** Tools: array or registry. Default: [HttpClientTool(), BrowserTool()]. */
|
|
61
|
+
tools?: Tool[] | ToolRegistry;
|
|
62
|
+
/** Cross-tool middleware (logging, rate limit, etc.). */
|
|
63
|
+
toolMiddleware?: ToolMiddleware[];
|
|
64
|
+
/** Session store for conversation persistence. Default: InMemorySessionStore(). Plug any DB by implementing SessionStore. */
|
|
65
|
+
sessionStore?: SessionStore;
|
|
66
|
+
/** Guardrails. Default: GuardrailValidator with sensitive-data rule. Set false to disable. */
|
|
67
|
+
guardrails?: GuardrailEngine | false;
|
|
68
|
+
/** Max agentic steps (default: 10) */
|
|
69
|
+
maxSteps?: number;
|
|
70
|
+
/** Run timeout ms (default: 60000) */
|
|
71
|
+
timeoutMs?: number;
|
|
72
|
+
/** Retry policy for LLM/tool calls */
|
|
73
|
+
retry?: { maxRetries?: number; backoffMs?: number; maxBackoffMs?: number };
|
|
74
|
+
/** Optional logger for production observability */
|
|
75
|
+
logger?: Logger;
|
|
76
|
+
/** Learning: always persist/learn vs agentic (default: always when sessionStore used) */
|
|
77
|
+
learningMode?: LearningMode;
|
|
78
|
+
/** User profiles that persist across sessions (plug any DB) */
|
|
79
|
+
userProfileStore?: UserProfileStore;
|
|
80
|
+
/** Long-term memory / knowledge store (for RAG, memories that accumulate) */
|
|
81
|
+
memoryStore?: MemoryStore;
|
|
82
|
+
/** RAG engine: agentic RAG, hybrid search, reranking (optional) */
|
|
83
|
+
ragEngine?: RAGEngine;
|
|
84
|
+
/** Type-safe input: Zod schema for run input (optional) */
|
|
85
|
+
inputSchema?: z.ZodType;
|
|
86
|
+
/** Type-safe output: Zod schema for run output (optional) */
|
|
87
|
+
outputSchema?: z.ZodType;
|
|
88
|
+
/** Dev mode: auto-attach console logger + tool call logging (best DX) */
|
|
89
|
+
dev?: boolean;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface AgentRunOptions {
|
|
93
|
+
/** Optional session ID to load/save conversation. If provided, messages are persisted. */
|
|
94
|
+
sessionId?: string;
|
|
95
|
+
/** Optional user ID for session metadata */
|
|
96
|
+
userId?: string;
|
|
97
|
+
/** Stream / progress hooks */
|
|
98
|
+
onChunk?: (text: string) => void;
|
|
99
|
+
onToolCall?: (name: string, args: Record<string, unknown>) => void;
|
|
100
|
+
onToolResult?: (name: string, result: unknown) => void;
|
|
101
|
+
onStep?: (step: number) => void;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface CreateAgentResult {
|
|
105
|
+
name: string;
|
|
106
|
+
instructions: string;
|
|
107
|
+
/** Run the agent with a prompt. Use sessionId in options for conversation persistence. */
|
|
108
|
+
run(prompt: string, options?: AgentRunOptions): Promise<AgenticRunResult>;
|
|
109
|
+
/** Create a new session for this agent (returns sessionId to pass to run). */
|
|
110
|
+
createSession(userId?: string): Promise<string>;
|
|
111
|
+
/** Get messages for a session (if using sessionStore). */
|
|
112
|
+
getSessionMessages(sessionId: string): Promise<Message[]>;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Create a production-grade agent with one line.
|
|
117
|
+
* Auto-configures LLM (OpenAI from env), default tools (HTTP), session store, and optional guardrails.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* // Minimal: uses OPENAI_API_KEY, default tools and session
|
|
121
|
+
* const agent = createAgent({ name: 'Assistant', instructions: 'You are helpful.' });
|
|
122
|
+
* const result = await agent.run('Hello!');
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* // With session for conversation memory
|
|
126
|
+
* const agent = createAgent({ name: 'Assistant', instructions: 'You are helpful.' });
|
|
127
|
+
* const sessionId = await agent.createSession('user-1');
|
|
128
|
+
* const result = await agent.run('What did I just say?', { sessionId });
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* // Override model or provide your own LLM
|
|
132
|
+
* const agent = createAgent({
|
|
133
|
+
* name: 'Assistant',
|
|
134
|
+
* instructions: 'You are helpful.',
|
|
135
|
+
* model: 'gpt-4o-mini',
|
|
136
|
+
* apiKey: process.env.MY_KEY,
|
|
137
|
+
* });
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* // Llama/Bern or any OpenAI-compatible endpoint (e.g. Ollama)
|
|
141
|
+
* const agent = createAgent({
|
|
142
|
+
* name: 'Assistant',
|
|
143
|
+
* instructions: 'You are helpful.',
|
|
144
|
+
* baseURL: 'http://localhost:11434/v1',
|
|
145
|
+
* model: 'llama3.2',
|
|
146
|
+
* });
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* // Agno-style model string: provider:model_id (openai, openrouter, ollama)
|
|
150
|
+
* const agent = createAgent({
|
|
151
|
+
* name: 'Assistant',
|
|
152
|
+
* instructions: 'You are helpful.',
|
|
153
|
+
* model: 'openrouter:anthropic/claude-3.5-sonnet',
|
|
154
|
+
* });
|
|
155
|
+
*/
|
|
156
|
+
export function createAgent(options: CreateAgentOptions): CreateAgentResult {
|
|
157
|
+
const {
|
|
158
|
+
name,
|
|
159
|
+
instructions,
|
|
160
|
+
llm: providedLlm,
|
|
161
|
+
model = typeof process !== 'undefined' && process.env?.[ENV_MODEL] ? process.env[ENV_MODEL] : 'gpt-4o',
|
|
162
|
+
apiKey = typeof process !== 'undefined' && process.env?.[ENV_API_KEY] ? process.env[ENV_API_KEY] : undefined,
|
|
163
|
+
baseURL = (typeof process !== 'undefined' && process.env?.[ENV_BASE_URL]) ? process.env[ENV_BASE_URL] : undefined,
|
|
164
|
+
tools: toolsOption = [new HttpClientTool(), new BrowserTool()],
|
|
165
|
+
toolMiddleware,
|
|
166
|
+
sessionStore = new InMemorySessionStore(),
|
|
167
|
+
guardrails: guardrailsOption = true,
|
|
168
|
+
maxSteps = 10,
|
|
169
|
+
timeoutMs = 60_000,
|
|
170
|
+
retry,
|
|
171
|
+
logger,
|
|
172
|
+
dev,
|
|
173
|
+
} = options;
|
|
174
|
+
|
|
175
|
+
if (!name || typeof name !== 'string' || name.trim() === '') {
|
|
176
|
+
throw new ConfigError('createAgent: name is required and must be a non-empty string', { context: { options: { name } } });
|
|
177
|
+
}
|
|
178
|
+
if (!instructions || typeof instructions !== 'string' || instructions.trim() === '') {
|
|
179
|
+
throw new ConfigError('createAgent: instructions is required and must be a non-empty string', { context: { options: { name } } });
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const tools = toToolRegistry(toolsOption as ToolProvider);
|
|
183
|
+
|
|
184
|
+
const getEnv = typeof process !== 'undefined' ? (k: string) => process.env?.[k] : undefined;
|
|
185
|
+
let llm: LLMProvider;
|
|
186
|
+
if (providedLlm) {
|
|
187
|
+
llm = providedLlm;
|
|
188
|
+
} else if (model && isModelString(model)) {
|
|
189
|
+
const resolved = resolveModelString(model, getEnv);
|
|
190
|
+
if (!resolved) {
|
|
191
|
+
throw new Error(`createAgent: Unknown provider in model string "${model}". Use openai:, openrouter:, or ollama:.`);
|
|
192
|
+
}
|
|
193
|
+
if (!resolved.apiKey) {
|
|
194
|
+
throw new Error(
|
|
195
|
+
`createAgent: Model "${model}" requires an API key. Set OPENAI_API_KEY or OPENROUTER_API_KEY (Ollama needs no key).`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
llm = new OpenAIProvider({
|
|
199
|
+
apiKey: resolved.apiKey,
|
|
200
|
+
baseURL: resolved.baseURL,
|
|
201
|
+
model: resolved.model,
|
|
202
|
+
});
|
|
203
|
+
} else {
|
|
204
|
+
const openRouterKey =
|
|
205
|
+
options.openRouter?.apiKey ?? getEnv?.(ENV_OPENROUTER_API_KEY);
|
|
206
|
+
if (openRouterKey) {
|
|
207
|
+
const openRouterModel =
|
|
208
|
+
options.openRouter?.model ?? getEnv?.(ENV_OPENROUTER_MODEL) ?? model;
|
|
209
|
+
llm = new OpenAIProvider({
|
|
210
|
+
apiKey: openRouterKey,
|
|
211
|
+
baseURL: OPENROUTER_BASE_URL,
|
|
212
|
+
model: (openRouterModel as string) || 'openai/gpt-4o',
|
|
213
|
+
});
|
|
214
|
+
} else if (apiKey || baseURL) {
|
|
215
|
+
llm = new OpenAIProvider({ apiKey, baseURL, model: model as string });
|
|
216
|
+
} else {
|
|
217
|
+
throw new Error(
|
|
218
|
+
`createAgent: No LLM provided. Set ${ENV_API_KEY}, or ${ENV_OPENROUTER_API_KEY}, or ${ENV_BASE_URL}, or pass model "provider:model_id", { llm }, { apiKey }, or { baseURL }.`
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const guardrails: GuardrailEngine | undefined =
|
|
224
|
+
guardrailsOption === false
|
|
225
|
+
? undefined
|
|
226
|
+
: guardrailsOption === true
|
|
227
|
+
? (() => {
|
|
228
|
+
const engine = new GuardrailValidator({ rules: [createSensitiveDataRule()] });
|
|
229
|
+
return engine;
|
|
230
|
+
})()
|
|
231
|
+
: guardrailsOption;
|
|
232
|
+
|
|
233
|
+
const effectiveLogger = logger ?? (dev ? createDevLogger() : undefined);
|
|
234
|
+
const effectiveToolMiddleware = [
|
|
235
|
+
...(toolMiddleware ?? []),
|
|
236
|
+
...(dev ? [createDevToolMiddleware()] : []),
|
|
237
|
+
];
|
|
238
|
+
|
|
239
|
+
if (effectiveLogger?.debug) {
|
|
240
|
+
effectiveLogger.debug('createAgent: initializing', { agentId: name }, { toolsCount: tools.list().length });
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const agent = createAgenticAgent({
|
|
244
|
+
name,
|
|
245
|
+
instructions,
|
|
246
|
+
llm,
|
|
247
|
+
tools: tools,
|
|
248
|
+
toolMiddleware: effectiveToolMiddleware.length ? effectiveToolMiddleware : undefined,
|
|
249
|
+
maxSteps,
|
|
250
|
+
timeoutMs,
|
|
251
|
+
retry,
|
|
252
|
+
guardrails,
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
name,
|
|
257
|
+
instructions,
|
|
258
|
+
async run(prompt: string, runOptions?: AgentRunOptions): Promise<AgenticRunResult> {
|
|
259
|
+
const sessionId = runOptions?.sessionId;
|
|
260
|
+
const hooks: AgenticStreamHooks = {
|
|
261
|
+
onChunk: runOptions?.onChunk,
|
|
262
|
+
onToolCall: runOptions?.onToolCall,
|
|
263
|
+
onToolResult: runOptions?.onToolResult,
|
|
264
|
+
onStep: runOptions?.onStep,
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
let messages: Message[] | undefined;
|
|
268
|
+
if (sessionId) {
|
|
269
|
+
const session = await sessionStore.get(sessionId);
|
|
270
|
+
const history = session?.messages ?? [];
|
|
271
|
+
messages = [...history, { role: 'user', content: prompt }];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const result = await agent.run(
|
|
275
|
+
{
|
|
276
|
+
prompt: messages ? '' : prompt,
|
|
277
|
+
instructions,
|
|
278
|
+
messages,
|
|
279
|
+
maxSteps,
|
|
280
|
+
timeoutMs,
|
|
281
|
+
},
|
|
282
|
+
hooks
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
if (sessionId && result.messages?.length) {
|
|
286
|
+
await sessionStore.update(sessionId, {
|
|
287
|
+
messages: result.messages,
|
|
288
|
+
state: SessionState.ACTIVE,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return result;
|
|
293
|
+
},
|
|
294
|
+
async createSession(userId?: string): Promise<string> {
|
|
295
|
+
const session = await sessionStore.create({
|
|
296
|
+
agentId: name,
|
|
297
|
+
userId,
|
|
298
|
+
state: SessionState.ACTIVE,
|
|
299
|
+
messages: [],
|
|
300
|
+
metadata: {},
|
|
301
|
+
context: {},
|
|
302
|
+
});
|
|
303
|
+
return session.id;
|
|
304
|
+
},
|
|
305
|
+
async getSessionMessages(sessionId: string): Promise<Message[]> {
|
|
306
|
+
return sessionStore.getMessages(sessionId);
|
|
307
|
+
},
|
|
308
|
+
};
|
|
309
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized debug logger for the agent framework
|
|
3
|
+
*
|
|
4
|
+
* Provides comprehensive debug logging across all components with
|
|
5
|
+
* configurable debug mode support.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Logger, LogContext } from './observability/types.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Debug logger configuration
|
|
12
|
+
*/
|
|
13
|
+
export interface DebugLoggerConfig {
|
|
14
|
+
/** Enable debug logging */
|
|
15
|
+
enabled: boolean;
|
|
16
|
+
/** Component name prefix */
|
|
17
|
+
component: string;
|
|
18
|
+
/** Parent logger for nested contexts */
|
|
19
|
+
parent?: DebugLogger;
|
|
20
|
+
/** Additional context to include in all logs */
|
|
21
|
+
context?: Partial<LogContext>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Format metadata for output
|
|
26
|
+
*/
|
|
27
|
+
function formatMetadata(metadata?: Record<string, unknown>): string {
|
|
28
|
+
if (!metadata || Object.keys(metadata).length === 0) return '';
|
|
29
|
+
try {
|
|
30
|
+
return JSON.stringify(metadata);
|
|
31
|
+
} catch {
|
|
32
|
+
return '[metadata serialization failed]';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Centralized debug logger that respects debug mode settings
|
|
38
|
+
*/
|
|
39
|
+
export class DebugLogger implements Logger {
|
|
40
|
+
private config: DebugLoggerConfig;
|
|
41
|
+
private prefix: string;
|
|
42
|
+
|
|
43
|
+
constructor(config: DebugLoggerConfig) {
|
|
44
|
+
this.config = config;
|
|
45
|
+
this.prefix = `[${config.component}]`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Check if debug logging is enabled
|
|
50
|
+
*/
|
|
51
|
+
isEnabled(): boolean {
|
|
52
|
+
return this.config.enabled;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Log a debug message (only if debug is enabled)
|
|
57
|
+
*/
|
|
58
|
+
debug(message: string, _context?: Partial<LogContext>, metadata?: Record<string, unknown>): void {
|
|
59
|
+
if (!this.config.enabled) return;
|
|
60
|
+
console.debug(`${this.prefix} ${message}`, formatMetadata(metadata));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Log an info message
|
|
65
|
+
*/
|
|
66
|
+
info(message: string, _context?: Partial<LogContext>, metadata?: Record<string, unknown>): void {
|
|
67
|
+
console.info(`${this.prefix} ${message}`, formatMetadata(metadata));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Log a warning message
|
|
72
|
+
*/
|
|
73
|
+
warn(message: string, _context?: Partial<LogContext>, metadata?: Record<string, unknown>): void {
|
|
74
|
+
console.warn(`${this.prefix} ⚠️ ${message}`, formatMetadata(metadata));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Log an error message
|
|
79
|
+
*/
|
|
80
|
+
error(message: string, _context?: Partial<LogContext>, metadata?: Record<string, unknown>): void {
|
|
81
|
+
console.error(`${this.prefix} ❌ ${message}`, formatMetadata(metadata));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Log a fatal message
|
|
86
|
+
*/
|
|
87
|
+
fatal(message: string, _context?: Partial<LogContext>, metadata?: Record<string, unknown>): void {
|
|
88
|
+
console.error(`${this.prefix} 💀 [FATAL] ${message}`, formatMetadata(metadata));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Create a child logger with additional context
|
|
93
|
+
*/
|
|
94
|
+
child(additionalContext: Partial<LogContext>): DebugLogger {
|
|
95
|
+
return new DebugLogger({
|
|
96
|
+
enabled: this.config.enabled,
|
|
97
|
+
component: this.config.component,
|
|
98
|
+
parent: this,
|
|
99
|
+
context: { ...this.config.context, ...additionalContext },
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Log the start of an operation
|
|
105
|
+
*/
|
|
106
|
+
logStart(operation: string, metadata?: Record<string, unknown>): void {
|
|
107
|
+
if (!this.config.enabled) return;
|
|
108
|
+
console.debug(`${this.prefix} ▶️ Starting: ${operation}`, formatMetadata(metadata));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Log the completion of an operation
|
|
113
|
+
*/
|
|
114
|
+
logComplete(operation: string, durationMs?: number, metadata?: Record<string, unknown>): void {
|
|
115
|
+
if (!this.config.enabled) return;
|
|
116
|
+
const duration = durationMs !== undefined ? ` (${durationMs}ms)` : '';
|
|
117
|
+
console.debug(`${this.prefix} ✅ Completed: ${operation}${duration}`, formatMetadata(metadata));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Log a step in a multi-step process
|
|
122
|
+
*/
|
|
123
|
+
logStep(step: string, current: number, total: number, metadata?: Record<string, unknown>): void {
|
|
124
|
+
if (!this.config.enabled) return;
|
|
125
|
+
console.debug(`${this.prefix} 📍 Step ${current}/${total}: ${step}`, formatMetadata(metadata));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Log state changes
|
|
130
|
+
*/
|
|
131
|
+
logStateChange(entity: string, from: string, to: string, metadata?: Record<string, unknown>): void {
|
|
132
|
+
if (!this.config.enabled) return;
|
|
133
|
+
console.debug(`${this.prefix} 🔄 ${entity} state: ${from} → ${to}`, formatMetadata(metadata));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Log data for debugging
|
|
138
|
+
*/
|
|
139
|
+
logData(label: string, data: unknown): void {
|
|
140
|
+
if (!this.config.enabled) return;
|
|
141
|
+
const preview = typeof data === 'string' ? data : JSON.stringify(data);
|
|
142
|
+
const truncated = preview.length > 200 ? preview.slice(0, 200) + '...' : preview;
|
|
143
|
+
console.debug(`${this.prefix} 📊 ${label}: ${truncated}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Global debug configuration
|
|
149
|
+
*/
|
|
150
|
+
let globalDebugEnabled = false;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Enable or disable global debug mode
|
|
154
|
+
*/
|
|
155
|
+
export function setGlobalDebug(enabled: boolean): void {
|
|
156
|
+
globalDebugEnabled = enabled;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Check if global debug mode is enabled
|
|
161
|
+
*/
|
|
162
|
+
export function isGlobalDebugEnabled(): boolean {
|
|
163
|
+
return globalDebugEnabled;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Create a debug logger for a component
|
|
168
|
+
*/
|
|
169
|
+
export function createDebugLogger(component: string, enabled?: boolean): DebugLogger {
|
|
170
|
+
return new DebugLogger({
|
|
171
|
+
enabled: enabled ?? globalDebugEnabled,
|
|
172
|
+
component,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Create a no-op logger for when debugging is disabled
|
|
178
|
+
*/
|
|
179
|
+
export function createNoopLogger(): Logger {
|
|
180
|
+
return {
|
|
181
|
+
debug: () => { },
|
|
182
|
+
info: () => { },
|
|
183
|
+
warn: () => { },
|
|
184
|
+
error: () => { },
|
|
185
|
+
fatal: () => { },
|
|
186
|
+
child: () => createNoopLogger(),
|
|
187
|
+
};
|
|
188
|
+
}
|