agentx-sdk 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/README.md +561 -0
- package/dist/agent.d.ts +105 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +690 -0
- package/dist/agent.js.map +1 -0
- package/dist/config/config.d.ts +346 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +93 -0
- package/dist/config/config.js.map +1 -0
- package/dist/contracts/entities/agent-event.d.ts +97 -0
- package/dist/contracts/entities/agent-event.d.ts.map +1 -0
- package/dist/contracts/entities/agent-event.js +2 -0
- package/dist/contracts/entities/agent-event.js.map +1 -0
- package/dist/contracts/entities/agent-skill.d.ts +59 -0
- package/dist/contracts/entities/agent-skill.d.ts.map +1 -0
- package/dist/contracts/entities/agent-skill.js +2 -0
- package/dist/contracts/entities/agent-skill.js.map +1 -0
- package/dist/contracts/entities/agent-tool.d.ts +42 -0
- package/dist/contracts/entities/agent-tool.d.ts.map +1 -0
- package/dist/contracts/entities/agent-tool.js +2 -0
- package/dist/contracts/entities/agent-tool.js.map +1 -0
- package/dist/contracts/entities/chat-message.d.ts +13 -0
- package/dist/contracts/entities/chat-message.d.ts.map +1 -0
- package/dist/contracts/entities/chat-message.js +2 -0
- package/dist/contracts/entities/chat-message.js.map +1 -0
- package/dist/contracts/entities/content-part.d.ts +16 -0
- package/dist/contracts/entities/content-part.d.ts.map +1 -0
- package/dist/contracts/entities/content-part.js +2 -0
- package/dist/contracts/entities/content-part.js.map +1 -0
- package/dist/contracts/entities/execution-context.d.ts +9 -0
- package/dist/contracts/entities/execution-context.d.ts.map +1 -0
- package/dist/contracts/entities/execution-context.js +2 -0
- package/dist/contracts/entities/execution-context.js.map +1 -0
- package/dist/contracts/entities/index.d.ts +11 -0
- package/dist/contracts/entities/index.d.ts.map +1 -0
- package/dist/contracts/entities/index.js +2 -0
- package/dist/contracts/entities/index.js.map +1 -0
- package/dist/contracts/entities/knowledge.d.ts +21 -0
- package/dist/contracts/entities/knowledge.d.ts.map +1 -0
- package/dist/contracts/entities/knowledge.js +2 -0
- package/dist/contracts/entities/knowledge.js.map +1 -0
- package/dist/contracts/entities/stores.d.ts +18 -0
- package/dist/contracts/entities/stores.d.ts.map +1 -0
- package/dist/contracts/entities/stores.js +2 -0
- package/dist/contracts/entities/stores.js.map +1 -0
- package/dist/contracts/entities/token-usage.d.ts +7 -0
- package/dist/contracts/entities/token-usage.d.ts.map +1 -0
- package/dist/contracts/entities/token-usage.js +2 -0
- package/dist/contracts/entities/token-usage.js.map +1 -0
- package/dist/contracts/entities/tool-call.d.ts +16 -0
- package/dist/contracts/entities/tool-call.d.ts.map +1 -0
- package/dist/contracts/entities/tool-call.js +2 -0
- package/dist/contracts/entities/tool-call.js.map +1 -0
- package/dist/contracts/enums/index.d.ts +21 -0
- package/dist/contracts/enums/index.d.ts.map +1 -0
- package/dist/contracts/enums/index.js +8 -0
- package/dist/contracts/enums/index.js.map +1 -0
- package/dist/contracts/index.d.ts +3 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +3 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/core/compaction/autocompact.d.ts +18 -0
- package/dist/core/compaction/autocompact.d.ts.map +1 -0
- package/dist/core/compaction/autocompact.js +68 -0
- package/dist/core/compaction/autocompact.js.map +1 -0
- package/dist/core/compaction/microcompact.d.ts +20 -0
- package/dist/core/compaction/microcompact.d.ts.map +1 -0
- package/dist/core/compaction/microcompact.js +38 -0
- package/dist/core/compaction/microcompact.js.map +1 -0
- package/dist/core/compaction/snip-compact.d.ts +22 -0
- package/dist/core/compaction/snip-compact.d.ts.map +1 -0
- package/dist/core/compaction/snip-compact.js +61 -0
- package/dist/core/compaction/snip-compact.js.map +1 -0
- package/dist/core/compaction/tool-result-budget.d.ts +24 -0
- package/dist/core/compaction/tool-result-budget.d.ts.map +1 -0
- package/dist/core/compaction/tool-result-budget.js +67 -0
- package/dist/core/compaction/tool-result-budget.js.map +1 -0
- package/dist/core/context-analysis.d.ts +24 -0
- package/dist/core/context-analysis.d.ts.map +1 -0
- package/dist/core/context-analysis.js +37 -0
- package/dist/core/context-analysis.js.map +1 -0
- package/dist/core/context-builder.d.ts +25 -0
- package/dist/core/context-builder.d.ts.map +1 -0
- package/dist/core/context-builder.js +108 -0
- package/dist/core/context-builder.js.map +1 -0
- package/dist/core/conversation-manager.d.ts +19 -0
- package/dist/core/conversation-manager.d.ts.map +1 -0
- package/dist/core/conversation-manager.js +62 -0
- package/dist/core/conversation-manager.js.map +1 -0
- package/dist/core/execution-context.d.ts +6 -0
- package/dist/core/execution-context.d.ts.map +1 -0
- package/dist/core/execution-context.js +14 -0
- package/dist/core/execution-context.js.map +1 -0
- package/dist/core/loop-deps.d.ts +15 -0
- package/dist/core/loop-deps.d.ts.map +1 -0
- package/dist/core/loop-deps.js +8 -0
- package/dist/core/loop-deps.js.map +1 -0
- package/dist/core/loop-types.d.ts +34 -0
- package/dist/core/loop-types.d.ts.map +1 -0
- package/dist/core/loop-types.js +15 -0
- package/dist/core/loop-types.js.map +1 -0
- package/dist/core/message-normalize.d.ts +18 -0
- package/dist/core/message-normalize.d.ts.map +1 -0
- package/dist/core/message-normalize.js +69 -0
- package/dist/core/message-normalize.js.map +1 -0
- package/dist/core/prompt-builders.d.ts +36 -0
- package/dist/core/prompt-builders.d.ts.map +1 -0
- package/dist/core/prompt-builders.js +89 -0
- package/dist/core/prompt-builders.js.map +1 -0
- package/dist/core/prompt-cache.d.ts +25 -0
- package/dist/core/prompt-cache.d.ts.map +1 -0
- package/dist/core/prompt-cache.js +34 -0
- package/dist/core/prompt-cache.js.map +1 -0
- package/dist/core/react-loop.d.ts +43 -0
- package/dist/core/react-loop.d.ts.map +1 -0
- package/dist/core/react-loop.js +403 -0
- package/dist/core/react-loop.js.map +1 -0
- package/dist/core/stop-hooks.d.ts +18 -0
- package/dist/core/stop-hooks.d.ts.map +1 -0
- package/dist/core/stop-hooks.js +18 -0
- package/dist/core/stop-hooks.js.map +1 -0
- package/dist/core/stream-emitter.d.ts +24 -0
- package/dist/core/stream-emitter.d.ts.map +1 -0
- package/dist/core/stream-emitter.js +65 -0
- package/dist/core/stream-emitter.js.map +1 -0
- package/dist/core/streaming-tool-executor.d.ts +54 -0
- package/dist/core/streaming-tool-executor.d.ts.map +1 -0
- package/dist/core/streaming-tool-executor.js +164 -0
- package/dist/core/streaming-tool-executor.js.map +1 -0
- package/dist/core/turn-end-hooks.d.ts +39 -0
- package/dist/core/turn-end-hooks.d.ts.map +1 -0
- package/dist/core/turn-end-hooks.js +36 -0
- package/dist/core/turn-end-hooks.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/dist/knowledge/chunking.d.ts +9 -0
- package/dist/knowledge/chunking.d.ts.map +1 -0
- package/dist/knowledge/chunking.js +49 -0
- package/dist/knowledge/chunking.js.map +1 -0
- package/dist/knowledge/embedding-service.d.ts +16 -0
- package/dist/knowledge/embedding-service.d.ts.map +1 -0
- package/dist/knowledge/embedding-service.js +43 -0
- package/dist/knowledge/embedding-service.js.map +1 -0
- package/dist/knowledge/knowledge-manager.d.ts +33 -0
- package/dist/knowledge/knowledge-manager.d.ts.map +1 -0
- package/dist/knowledge/knowledge-manager.js +62 -0
- package/dist/knowledge/knowledge-manager.js.map +1 -0
- package/dist/knowledge/sqlite-vector-store.d.ts +16 -0
- package/dist/knowledge/sqlite-vector-store.d.ts.map +1 -0
- package/dist/knowledge/sqlite-vector-store.js +56 -0
- package/dist/knowledge/sqlite-vector-store.js.map +1 -0
- package/dist/knowledge/vector-store.d.ts +2 -0
- package/dist/knowledge/vector-store.d.ts.map +1 -0
- package/dist/knowledge/vector-store.js +2 -0
- package/dist/knowledge/vector-store.js.map +1 -0
- package/dist/llm/errors.d.ts +15 -0
- package/dist/llm/errors.d.ts.map +1 -0
- package/dist/llm/errors.js +39 -0
- package/dist/llm/errors.js.map +1 -0
- package/dist/llm/message-types.d.ts +80 -0
- package/dist/llm/message-types.d.ts.map +1 -0
- package/dist/llm/message-types.js +2 -0
- package/dist/llm/message-types.js.map +1 -0
- package/dist/llm/openrouter-client.d.ts +18 -0
- package/dist/llm/openrouter-client.d.ts.map +1 -0
- package/dist/llm/openrouter-client.js +215 -0
- package/dist/llm/openrouter-client.js.map +1 -0
- package/dist/llm/reasoning.d.ts +10 -0
- package/dist/llm/reasoning.d.ts.map +1 -0
- package/dist/llm/reasoning.js +18 -0
- package/dist/llm/reasoning.js.map +1 -0
- package/dist/memory/file-memory-system.d.ts +98 -0
- package/dist/memory/file-memory-system.d.ts.map +1 -0
- package/dist/memory/file-memory-system.js +310 -0
- package/dist/memory/file-memory-system.js.map +1 -0
- package/dist/memory/memory-age.d.ts +22 -0
- package/dist/memory/memory-age.d.ts.map +1 -0
- package/dist/memory/memory-age.js +44 -0
- package/dist/memory/memory-age.js.map +1 -0
- package/dist/memory/memory-extractor.d.ts +56 -0
- package/dist/memory/memory-extractor.d.ts.map +1 -0
- package/dist/memory/memory-extractor.js +91 -0
- package/dist/memory/memory-extractor.js.map +1 -0
- package/dist/memory/memory-paths.d.ts +45 -0
- package/dist/memory/memory-paths.d.ts.map +1 -0
- package/dist/memory/memory-paths.js +121 -0
- package/dist/memory/memory-paths.js.map +1 -0
- package/dist/memory/memory-prompts.d.ts +41 -0
- package/dist/memory/memory-prompts.d.ts.map +1 -0
- package/dist/memory/memory-prompts.js +279 -0
- package/dist/memory/memory-prompts.js.map +1 -0
- package/dist/memory/memory-relevance.d.ts +16 -0
- package/dist/memory/memory-relevance.d.ts.map +1 -0
- package/dist/memory/memory-relevance.js +46 -0
- package/dist/memory/memory-relevance.js.map +1 -0
- package/dist/memory/memory-scanner.d.ts +22 -0
- package/dist/memory/memory-scanner.d.ts.map +1 -0
- package/dist/memory/memory-scanner.js +99 -0
- package/dist/memory/memory-scanner.js.map +1 -0
- package/dist/memory/memory-tools.d.ts +16 -0
- package/dist/memory/memory-tools.d.ts.map +1 -0
- package/dist/memory/memory-tools.js +196 -0
- package/dist/memory/memory-tools.js.map +1 -0
- package/dist/memory/memory-types.d.ts +47 -0
- package/dist/memory/memory-types.d.ts.map +1 -0
- package/dist/memory/memory-types.js +24 -0
- package/dist/memory/memory-types.js.map +1 -0
- package/dist/skills/skill-args.d.ts +23 -0
- package/dist/skills/skill-args.d.ts.map +1 -0
- package/dist/skills/skill-args.js +77 -0
- package/dist/skills/skill-args.js.map +1 -0
- package/dist/skills/skill-glob.d.ts +24 -0
- package/dist/skills/skill-glob.d.ts.map +1 -0
- package/dist/skills/skill-glob.js +60 -0
- package/dist/skills/skill-glob.js.map +1 -0
- package/dist/skills/skill-loader.d.ts +49 -0
- package/dist/skills/skill-loader.d.ts.map +1 -0
- package/dist/skills/skill-loader.js +197 -0
- package/dist/skills/skill-loader.js.map +1 -0
- package/dist/skills/skill-manager.d.ts +83 -0
- package/dist/skills/skill-manager.d.ts.map +1 -0
- package/dist/skills/skill-manager.js +338 -0
- package/dist/skills/skill-manager.js.map +1 -0
- package/dist/storage/sqlite-conversation-store.d.ts +15 -0
- package/dist/storage/sqlite-conversation-store.d.ts.map +1 -0
- package/dist/storage/sqlite-conversation-store.js +45 -0
- package/dist/storage/sqlite-conversation-store.js.map +1 -0
- package/dist/storage/sqlite-database.d.ts +14 -0
- package/dist/storage/sqlite-database.d.ts.map +1 -0
- package/dist/storage/sqlite-database.js +95 -0
- package/dist/storage/sqlite-database.js.map +1 -0
- package/dist/tools/builtin/ask-user.d.ts +7 -0
- package/dist/tools/builtin/ask-user.d.ts.map +1 -0
- package/dist/tools/builtin/ask-user.js +23 -0
- package/dist/tools/builtin/ask-user.js.map +1 -0
- package/dist/tools/builtin/bash.d.ts +3 -0
- package/dist/tools/builtin/bash.d.ts.map +1 -0
- package/dist/tools/builtin/bash.js +54 -0
- package/dist/tools/builtin/bash.js.map +1 -0
- package/dist/tools/builtin/file-edit.d.ts +3 -0
- package/dist/tools/builtin/file-edit.d.ts.map +1 -0
- package/dist/tools/builtin/file-edit.js +50 -0
- package/dist/tools/builtin/file-edit.js.map +1 -0
- package/dist/tools/builtin/file-read.d.ts +3 -0
- package/dist/tools/builtin/file-read.d.ts.map +1 -0
- package/dist/tools/builtin/file-read.js +47 -0
- package/dist/tools/builtin/file-read.js.map +1 -0
- package/dist/tools/builtin/file-write.d.ts +3 -0
- package/dist/tools/builtin/file-write.d.ts.map +1 -0
- package/dist/tools/builtin/file-write.js +29 -0
- package/dist/tools/builtin/file-write.js.map +1 -0
- package/dist/tools/builtin/glob.d.ts +3 -0
- package/dist/tools/builtin/glob.d.ts.map +1 -0
- package/dist/tools/builtin/glob.js +67 -0
- package/dist/tools/builtin/glob.js.map +1 -0
- package/dist/tools/builtin/grep.d.ts +3 -0
- package/dist/tools/builtin/grep.d.ts.map +1 -0
- package/dist/tools/builtin/grep.js +94 -0
- package/dist/tools/builtin/grep.js.map +1 -0
- package/dist/tools/builtin/index.d.ts +49 -0
- package/dist/tools/builtin/index.d.ts.map +1 -0
- package/dist/tools/builtin/index.js +65 -0
- package/dist/tools/builtin/index.js.map +1 -0
- package/dist/tools/builtin/web-fetch.d.ts +3 -0
- package/dist/tools/builtin/web-fetch.d.ts.map +1 -0
- package/dist/tools/builtin/web-fetch.js +56 -0
- package/dist/tools/builtin/web-fetch.js.map +1 -0
- package/dist/tools/json-schema-to-zod.d.ts +30 -0
- package/dist/tools/json-schema-to-zod.d.ts.map +1 -0
- package/dist/tools/json-schema-to-zod.js +123 -0
- package/dist/tools/json-schema-to-zod.js.map +1 -0
- package/dist/tools/mcp-adapter.d.ts +80 -0
- package/dist/tools/mcp-adapter.d.ts.map +1 -0
- package/dist/tools/mcp-adapter.js +326 -0
- package/dist/tools/mcp-adapter.js.map +1 -0
- package/dist/tools/skill-tool.d.ts +41 -0
- package/dist/tools/skill-tool.d.ts.map +1 -0
- package/dist/tools/skill-tool.js +149 -0
- package/dist/tools/skill-tool.js.map +1 -0
- package/dist/tools/sql/index.d.ts +4 -0
- package/dist/tools/sql/index.d.ts.map +1 -0
- package/dist/tools/sql/index.js +2 -0
- package/dist/tools/sql/index.js.map +1 -0
- package/dist/tools/sql/sql-query-def.d.ts +22 -0
- package/dist/tools/sql/sql-query-def.d.ts.map +1 -0
- package/dist/tools/sql/sql-query-def.js +2 -0
- package/dist/tools/sql/sql-query-def.js.map +1 -0
- package/dist/tools/sql/sql-tool-factory.d.ts +28 -0
- package/dist/tools/sql/sql-tool-factory.d.ts.map +1 -0
- package/dist/tools/sql/sql-tool-factory.js +136 -0
- package/dist/tools/sql/sql-tool-factory.js.map +1 -0
- package/dist/tools/tool-executor.d.ts +67 -0
- package/dist/tools/tool-executor.d.ts.map +1 -0
- package/dist/tools/tool-executor.js +232 -0
- package/dist/tools/tool-executor.js.map +1 -0
- package/dist/utils/cache.d.ts +22 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +61 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/logger.d.ts +15 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +46 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/model-context.d.ts +14 -0
- package/dist/utils/model-context.d.ts.map +1 -0
- package/dist/utils/model-context.js +52 -0
- package/dist/utils/model-context.js.map +1 -0
- package/dist/utils/retry.d.ts +13 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +41 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/token-counter.d.ts +6 -0
- package/dist/utils/token-counter.d.ts.map +1 -0
- package/dist/utils/token-counter.js +19 -0
- package/dist/utils/token-counter.js.map +1 -0
- package/package.json +43 -0
package/dist/agent.js
ADDED
|
@@ -0,0 +1,690 @@
|
|
|
1
|
+
import { AgentConfigSchema } from './config/config.js';
|
|
2
|
+
import { OpenRouterClient } from './llm/openrouter-client.js';
|
|
3
|
+
import { ToolExecutor } from './tools/tool-executor.js';
|
|
4
|
+
import { MCPAdapter } from './tools/mcp-adapter.js';
|
|
5
|
+
import { SkillManager } from './skills/skill-manager.js';
|
|
6
|
+
import { createSkillTool, SKILL_TOOL_NAME, buildSkillToolPrompt } from './tools/skill-tool.js';
|
|
7
|
+
import { FileMemorySystem } from './memory/file-memory-system.js';
|
|
8
|
+
import { extractMemories, shouldExtract } from './memory/memory-extractor.js';
|
|
9
|
+
import { memoryFreshnessNote } from './memory/memory-age.js';
|
|
10
|
+
import { KnowledgeManager } from './knowledge/knowledge-manager.js';
|
|
11
|
+
import { EmbeddingService } from './knowledge/embedding-service.js';
|
|
12
|
+
import { SQLiteDatabase } from './storage/sqlite-database.js';
|
|
13
|
+
import { SQLiteVectorStore } from './knowledge/sqlite-vector-store.js';
|
|
14
|
+
import { SQLiteConversationStore } from './storage/sqlite-conversation-store.js';
|
|
15
|
+
import { ConversationManager } from './core/conversation-manager.js';
|
|
16
|
+
import { createExecutionContext } from './core/execution-context.js';
|
|
17
|
+
import { buildContext } from './core/context-builder.js';
|
|
18
|
+
import { executeReactLoop } from './core/react-loop.js';
|
|
19
|
+
import { createLogger } from './utils/logger.js';
|
|
20
|
+
import { runTurnEndHooks } from './core/turn-end-hooks.js';
|
|
21
|
+
import { estimateTokens } from './utils/token-counter.js';
|
|
22
|
+
import { getModelContextWindow } from './utils/model-context.js';
|
|
23
|
+
import { buildToolUsagePrompt, buildEnvironmentPrompt } from './core/prompt-builders.js';
|
|
24
|
+
/**
|
|
25
|
+
* Main entry point — orchestrates all subsystems.
|
|
26
|
+
*/
|
|
27
|
+
export class Agent {
|
|
28
|
+
config;
|
|
29
|
+
client;
|
|
30
|
+
toolExecutor;
|
|
31
|
+
conversations;
|
|
32
|
+
logger;
|
|
33
|
+
skillManager;
|
|
34
|
+
fileMemorySystem;
|
|
35
|
+
knowledgeManager;
|
|
36
|
+
embeddingService;
|
|
37
|
+
mcpAdapter;
|
|
38
|
+
database;
|
|
39
|
+
costAccumulator = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
40
|
+
turnsSinceExtraction = 0;
|
|
41
|
+
destroyed = false;
|
|
42
|
+
/** Filenames already injected in this session — avoids re-surfacing the same memory. */
|
|
43
|
+
surfacedMemories = new Set();
|
|
44
|
+
/** Last date emitted to model — for midnight change detection. */
|
|
45
|
+
lastEmittedDate;
|
|
46
|
+
/** Turn-end hooks — run after each completed assistant turn. */
|
|
47
|
+
turnEndHooks = [];
|
|
48
|
+
constructor(config) {
|
|
49
|
+
this.config = config;
|
|
50
|
+
this.logger = createLogger({ level: config.logLevel });
|
|
51
|
+
this.client = new OpenRouterClient({
|
|
52
|
+
apiKey: config.apiKey,
|
|
53
|
+
model: config.model,
|
|
54
|
+
baseUrl: config.baseUrl,
|
|
55
|
+
});
|
|
56
|
+
this.toolExecutor = new ToolExecutor();
|
|
57
|
+
this.mcpAdapter = new MCPAdapter(this.toolExecutor);
|
|
58
|
+
// Conversation store — defaults to SQLite when database is available (persists across restarts)
|
|
59
|
+
if (config.conversation?.store) {
|
|
60
|
+
this.conversations = new ConversationManager(config.conversation.store);
|
|
61
|
+
}
|
|
62
|
+
else if (config.knowledge?.enabled !== false) {
|
|
63
|
+
// Database will be initialized for knowledge, reuse it for conversations
|
|
64
|
+
this.conversations = new ConversationManager(this.getDefaultConversationStore());
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
this.conversations = new ConversationManager();
|
|
68
|
+
}
|
|
69
|
+
// Embedding service (shared by memory + knowledge)
|
|
70
|
+
this.embeddingService = new EmbeddingService(this.client, { model: config.embeddingModel });
|
|
71
|
+
// Memory subsystem (file-based)
|
|
72
|
+
if (config.memory?.enabled !== false) {
|
|
73
|
+
this.fileMemorySystem = new FileMemorySystem({
|
|
74
|
+
memoryDir: config.memory?.memoryDir,
|
|
75
|
+
relevanceModel: config.memory?.relevanceModel,
|
|
76
|
+
extractionEnabled: config.memory?.extractionEnabled,
|
|
77
|
+
}, this.client, this.logger);
|
|
78
|
+
// Ensure memory directory exists (fire-and-forget)
|
|
79
|
+
void this.fileMemorySystem.ensureDir().catch(err => {
|
|
80
|
+
this.logger.debug('Failed to create memory dir', { error: String(err) });
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// Knowledge subsystem
|
|
84
|
+
if (config.knowledge?.enabled !== false) {
|
|
85
|
+
const vectorStore = config.knowledge?.store ?? this.getDefaultVectorStore();
|
|
86
|
+
this.knowledgeManager = new KnowledgeManager({
|
|
87
|
+
store: vectorStore,
|
|
88
|
+
embeddingService: this.embeddingService,
|
|
89
|
+
chunkSize: config.knowledge?.chunkSize,
|
|
90
|
+
chunkOverlap: config.knowledge?.chunkOverlap,
|
|
91
|
+
topK: config.knowledge?.topK,
|
|
92
|
+
minScore: config.knowledge?.minScore,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
// Skills
|
|
96
|
+
this.skillManager = new SkillManager({
|
|
97
|
+
embeddingService: this.embeddingService,
|
|
98
|
+
maxActiveSkills: config.skills?.maxActiveSkills,
|
|
99
|
+
});
|
|
100
|
+
// Auto-load skills from directory (fire-and-forget)
|
|
101
|
+
if (config.skills?.skillsDir) {
|
|
102
|
+
void this.skillManager.loadFromDirectory(config.skills.skillsDir).catch(err => {
|
|
103
|
+
this.logger.debug('Failed to load skills dir', { error: String(err) });
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
this.logger.info('Agent initialized', { model: config.model });
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Creates and validates an Agent instance.
|
|
110
|
+
*/
|
|
111
|
+
static create(input) {
|
|
112
|
+
const config = AgentConfigSchema.parse(input);
|
|
113
|
+
return new Agent(config);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Streaming API — primary interface. Returns AsyncIterableIterator<AgentEvent>.
|
|
117
|
+
* Uses AsyncGenerator pattern: the react loop yields events directly.
|
|
118
|
+
*/
|
|
119
|
+
async *stream(input, options) {
|
|
120
|
+
if (this.destroyed)
|
|
121
|
+
throw new Error('Agent is destroyed');
|
|
122
|
+
const threadId = options?.threadId ?? 'default';
|
|
123
|
+
const model = options?.model ?? this.config.model;
|
|
124
|
+
const ctx = createExecutionContext(threadId, model);
|
|
125
|
+
// Add user message
|
|
126
|
+
const userContent = typeof input === 'string' ? input : input.map(p => p.type === 'text' ? p.text : '[image]').join('');
|
|
127
|
+
await this.conversations.withThread(threadId, async () => {
|
|
128
|
+
this.conversations.appendMessage({
|
|
129
|
+
role: 'user',
|
|
130
|
+
content: input,
|
|
131
|
+
createdAt: Date.now(),
|
|
132
|
+
}, threadId);
|
|
133
|
+
});
|
|
134
|
+
// Start memory relevance prefetch (non-blocking, thread-scoped)
|
|
135
|
+
const memoryPrefetch = this.fileMemorySystem
|
|
136
|
+
? this.startMemoryPrefetch(userContent, threadId)
|
|
137
|
+
: undefined;
|
|
138
|
+
// Build context (memory prefetch resolves in parallel)
|
|
139
|
+
const { injections, skillToolNames } = await this.buildInjectionsWithSkills(userContent, threadId, memoryPrefetch);
|
|
140
|
+
// Register SkillTool so the model can invoke skills mid-loop
|
|
141
|
+
let skillToolRegistered = false;
|
|
142
|
+
if (this.skillManager && this.skillManager.listSkills().length > 0) {
|
|
143
|
+
const skillTool = createSkillTool(this.skillManager, this.toolExecutor, () => ({
|
|
144
|
+
threadId,
|
|
145
|
+
traceId: ctx.traceId,
|
|
146
|
+
}));
|
|
147
|
+
this.toolExecutor.register(skillTool);
|
|
148
|
+
skillToolRegistered = true;
|
|
149
|
+
}
|
|
150
|
+
const availableTools = this.toolExecutor.listTools();
|
|
151
|
+
if (availableTools.length > 0) {
|
|
152
|
+
const toolContent = buildToolUsagePrompt(availableTools);
|
|
153
|
+
injections.push({
|
|
154
|
+
source: 'tools',
|
|
155
|
+
priority: 10,
|
|
156
|
+
content: toolContent,
|
|
157
|
+
tokens: estimateTokens(toolContent),
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
// Environment info — gives model awareness of execution context
|
|
161
|
+
const today = new Date().toISOString().split('T')[0];
|
|
162
|
+
const envContent = buildEnvironmentPrompt({
|
|
163
|
+
model,
|
|
164
|
+
date: today,
|
|
165
|
+
platform: process.platform,
|
|
166
|
+
});
|
|
167
|
+
injections.push({
|
|
168
|
+
source: 'environment',
|
|
169
|
+
priority: 1,
|
|
170
|
+
content: envContent,
|
|
171
|
+
tokens: estimateTokens(envContent),
|
|
172
|
+
});
|
|
173
|
+
// Date change detection — notify model when day changes mid-session
|
|
174
|
+
if (this.lastEmittedDate && this.lastEmittedDate !== today) {
|
|
175
|
+
injections.push({
|
|
176
|
+
source: 'system:date_change',
|
|
177
|
+
priority: 10,
|
|
178
|
+
content: `The date has changed from ${this.lastEmittedDate} to ${today}.`,
|
|
179
|
+
tokens: 20,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
this.lastEmittedDate = today;
|
|
183
|
+
const history = this.conversations.getHistory(threadId);
|
|
184
|
+
const contextResult = buildContext({
|
|
185
|
+
systemPrompt: this.config.systemPrompt,
|
|
186
|
+
injections,
|
|
187
|
+
history,
|
|
188
|
+
maxTokens: this.config.maxContextTokens,
|
|
189
|
+
reserveTokens: this.config.reserveTokens,
|
|
190
|
+
maxPinnedMessages: this.config.maxPinnedMessages,
|
|
191
|
+
});
|
|
192
|
+
// Snapshot memory dir time for mutual exclusion with extraction
|
|
193
|
+
const turnStartMs = Date.now();
|
|
194
|
+
// Emit start
|
|
195
|
+
yield { type: 'agent_start', traceId: ctx.traceId, threadId, model };
|
|
196
|
+
// Emit skill_activated events for matched skills
|
|
197
|
+
for (const inj of injections.filter(i => i.source.startsWith('skill:') && i.source !== 'skill:listing')) {
|
|
198
|
+
yield { type: 'skill_activated', skillName: inj.source.replace('skill:', '') };
|
|
199
|
+
}
|
|
200
|
+
// Intercept events from the generator for persistence tracking
|
|
201
|
+
let assistantText = '';
|
|
202
|
+
const pendingToolCalls = [];
|
|
203
|
+
const pendingToolResults = [];
|
|
204
|
+
const loopGen = executeReactLoop(contextResult.messages, {
|
|
205
|
+
client: this.client,
|
|
206
|
+
toolExecutor: this.toolExecutor,
|
|
207
|
+
model,
|
|
208
|
+
maxIterations: this.config.maxIterations,
|
|
209
|
+
maxConsecutiveErrors: this.config.maxConsecutiveErrors,
|
|
210
|
+
onToolError: this.config.onToolError,
|
|
211
|
+
costPolicy: this.config.costPolicy ? {
|
|
212
|
+
maxTokensPerExecution: this.config.costPolicy.maxTokensPerExecution,
|
|
213
|
+
onLimitReached: this.config.costPolicy.onLimitReached,
|
|
214
|
+
} : undefined,
|
|
215
|
+
signal: options?.signal,
|
|
216
|
+
// Compaction & Recovery
|
|
217
|
+
maxContextTokens: this.config.maxContextTokens,
|
|
218
|
+
compactionThreshold: this.config.compactionThreshold,
|
|
219
|
+
fallbackModel: this.config.fallbackModel,
|
|
220
|
+
maxOutputTokens: this.config.maxOutputTokens,
|
|
221
|
+
escalatedMaxOutputTokens: this.config.escalatedMaxOutputTokens,
|
|
222
|
+
// Token budget
|
|
223
|
+
tokenBudget: this.config.tokenBudget,
|
|
224
|
+
// Tool intelligence: conditional skill activation from file operations
|
|
225
|
+
onFilePathsTouched: this.skillManager
|
|
226
|
+
? (paths) => this.skillManager.activateForPaths(paths)
|
|
227
|
+
: undefined,
|
|
228
|
+
});
|
|
229
|
+
// Consume the generator, intercept events, re-yield to consumer
|
|
230
|
+
let terminal;
|
|
231
|
+
try {
|
|
232
|
+
let result = await loopGen.next();
|
|
233
|
+
while (!result.done) {
|
|
234
|
+
const event = result.value;
|
|
235
|
+
// Track for persistence
|
|
236
|
+
if (event.type === 'text_delta')
|
|
237
|
+
assistantText += event.content;
|
|
238
|
+
if (event.type === 'tool_call_start') {
|
|
239
|
+
pendingToolCalls.push({
|
|
240
|
+
id: event.toolCall.id,
|
|
241
|
+
name: event.toolCall.function.name,
|
|
242
|
+
arguments: event.toolCall.function.arguments,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
if (event.type === 'tool_call_end') {
|
|
246
|
+
pendingToolResults.push({
|
|
247
|
+
toolCallId: event.toolCallId,
|
|
248
|
+
content: event.result.content,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
yield event;
|
|
252
|
+
result = await loopGen.next();
|
|
253
|
+
}
|
|
254
|
+
terminal = result.value;
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
// Persist partial text on unexpected error
|
|
258
|
+
if (assistantText) {
|
|
259
|
+
this.conversations.appendMessage({
|
|
260
|
+
role: 'assistant',
|
|
261
|
+
content: assistantText,
|
|
262
|
+
createdAt: Date.now(),
|
|
263
|
+
}, threadId);
|
|
264
|
+
}
|
|
265
|
+
yield { type: 'error', error: error instanceof Error ? error : new Error(String(error)), recoverable: false };
|
|
266
|
+
yield {
|
|
267
|
+
type: 'agent_end',
|
|
268
|
+
traceId: ctx.traceId,
|
|
269
|
+
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
270
|
+
reason: 'error',
|
|
271
|
+
duration: Date.now() - ctx.startedAt,
|
|
272
|
+
};
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
// --- Post-loop: persist conversation history ---
|
|
276
|
+
const now = Date.now();
|
|
277
|
+
if (pendingToolCalls.length > 0) {
|
|
278
|
+
this.conversations.appendMessage({
|
|
279
|
+
role: 'assistant',
|
|
280
|
+
content: '',
|
|
281
|
+
toolCalls: pendingToolCalls.map(tc => ({
|
|
282
|
+
id: tc.id,
|
|
283
|
+
type: 'function',
|
|
284
|
+
function: { name: tc.name, arguments: tc.arguments },
|
|
285
|
+
})),
|
|
286
|
+
createdAt: now - 2,
|
|
287
|
+
}, threadId);
|
|
288
|
+
for (const tr of pendingToolResults) {
|
|
289
|
+
this.conversations.appendMessage({
|
|
290
|
+
role: 'tool',
|
|
291
|
+
content: tr.content,
|
|
292
|
+
toolCallId: tr.toolCallId,
|
|
293
|
+
createdAt: now - 1,
|
|
294
|
+
}, threadId);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (assistantText) {
|
|
298
|
+
this.conversations.appendMessage({
|
|
299
|
+
role: 'assistant',
|
|
300
|
+
content: assistantText,
|
|
301
|
+
createdAt: now,
|
|
302
|
+
}, threadId);
|
|
303
|
+
}
|
|
304
|
+
// Accumulate cost
|
|
305
|
+
this.costAccumulator.inputTokens += terminal.usage.inputTokens;
|
|
306
|
+
this.costAccumulator.outputTokens += terminal.usage.outputTokens;
|
|
307
|
+
this.costAccumulator.totalTokens += terminal.usage.totalTokens;
|
|
308
|
+
// Cleanup skill-scoped tools and SkillTool
|
|
309
|
+
for (const name of skillToolNames) {
|
|
310
|
+
this.toolExecutor.unregister(name);
|
|
311
|
+
}
|
|
312
|
+
if (skillToolRegistered) {
|
|
313
|
+
this.toolExecutor.unregister(SKILL_TOOL_NAME);
|
|
314
|
+
}
|
|
315
|
+
// Emit end
|
|
316
|
+
yield {
|
|
317
|
+
type: 'agent_end',
|
|
318
|
+
traceId: ctx.traceId,
|
|
319
|
+
usage: terminal.usage,
|
|
320
|
+
reason: terminal.reason,
|
|
321
|
+
duration: Date.now() - ctx.startedAt,
|
|
322
|
+
};
|
|
323
|
+
// Turn-end hooks pipeline (memory extraction + custom hooks)
|
|
324
|
+
const turnEndContext = {
|
|
325
|
+
assistantText,
|
|
326
|
+
turnCount: 1,
|
|
327
|
+
threadId,
|
|
328
|
+
usage: terminal.usage,
|
|
329
|
+
};
|
|
330
|
+
// Built-in: memory extraction hook
|
|
331
|
+
this.turnsSinceExtraction++;
|
|
332
|
+
if (this.fileMemorySystem &&
|
|
333
|
+
this.config.memory?.extractionEnabled !== false &&
|
|
334
|
+
shouldExtract(userContent, this.turnsSinceExtraction, {
|
|
335
|
+
samplingRate: this.config.memory?.samplingRate,
|
|
336
|
+
extractionInterval: this.config.memory?.extractionInterval,
|
|
337
|
+
})) {
|
|
338
|
+
this.turnsSinceExtraction = 0;
|
|
339
|
+
const memSystem = this.fileMemorySystem;
|
|
340
|
+
const logger = this.logger;
|
|
341
|
+
const conversations = this.conversations;
|
|
342
|
+
const forkFn = this.fork.bind(this);
|
|
343
|
+
void (async () => {
|
|
344
|
+
try {
|
|
345
|
+
if (await memSystem.hasWritesSince(turnStartMs, threadId)) {
|
|
346
|
+
logger.debug('Skipping extraction — agent already wrote memories this turn');
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
const history = conversations.getHistory(threadId);
|
|
350
|
+
const recentMessages = history.slice(-10);
|
|
351
|
+
const conversationText = recentMessages.map(m => {
|
|
352
|
+
const text = typeof m.content === 'string' ? m.content : '[multimodal]';
|
|
353
|
+
return `${m.role}: ${text}`;
|
|
354
|
+
}).join('\n');
|
|
355
|
+
await extractMemories(conversationText, memSystem, forkFn, { threadId });
|
|
356
|
+
}
|
|
357
|
+
catch (err) {
|
|
358
|
+
logger.debug('Memory extraction failed', { error: String(err) });
|
|
359
|
+
}
|
|
360
|
+
})();
|
|
361
|
+
}
|
|
362
|
+
// Run registered turn-end hooks
|
|
363
|
+
if (this.turnEndHooks.length > 0) {
|
|
364
|
+
void runTurnEndHooks(this.turnEndHooks, turnEndContext).catch(err => {
|
|
365
|
+
this.logger.debug('Turn-end hooks failed', { error: String(err) });
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Simple chat API — consumes stream() and returns final text.
|
|
371
|
+
*/
|
|
372
|
+
async chat(input, options) {
|
|
373
|
+
let result = '';
|
|
374
|
+
for await (const event of this.stream(input, options)) {
|
|
375
|
+
if (event.type === 'text_delta')
|
|
376
|
+
result += event.content;
|
|
377
|
+
if (event.type === 'error' && !event.recoverable)
|
|
378
|
+
throw event.error;
|
|
379
|
+
}
|
|
380
|
+
return result;
|
|
381
|
+
}
|
|
382
|
+
addTool(tool) {
|
|
383
|
+
this.toolExecutor.register(tool);
|
|
384
|
+
this.logger.debug('Tool registered', { name: tool.name });
|
|
385
|
+
}
|
|
386
|
+
removeTool(name) {
|
|
387
|
+
const removed = this.toolExecutor.unregister(name);
|
|
388
|
+
if (removed)
|
|
389
|
+
this.logger.debug('Tool removed', { name });
|
|
390
|
+
return removed;
|
|
391
|
+
}
|
|
392
|
+
addSkill(skill) {
|
|
393
|
+
this.skillManager?.register(skill);
|
|
394
|
+
this.logger.debug('Skill registered', { name: skill.name });
|
|
395
|
+
}
|
|
396
|
+
removeSkill(name) {
|
|
397
|
+
const removed = this.skillManager?.unregister(name) ?? false;
|
|
398
|
+
if (removed)
|
|
399
|
+
this.logger.debug('Skill removed', { name });
|
|
400
|
+
return removed;
|
|
401
|
+
}
|
|
402
|
+
/** Load skills from a directory containing SKILL.md files. Returns count loaded. */
|
|
403
|
+
async loadSkillsDir(dir) {
|
|
404
|
+
if (!this.skillManager)
|
|
405
|
+
return 0;
|
|
406
|
+
const count = await this.skillManager.loadFromDirectory(dir);
|
|
407
|
+
this.logger.info('Skills loaded from directory', { dir, count });
|
|
408
|
+
return count;
|
|
409
|
+
}
|
|
410
|
+
/** Get all registered skills (unconditional + activated). */
|
|
411
|
+
listSkills() {
|
|
412
|
+
return this.skillManager?.listSkills() ?? [];
|
|
413
|
+
}
|
|
414
|
+
/** Activate conditional skills whose paths match the given file paths. */
|
|
415
|
+
activateSkillsForPaths(filePaths) {
|
|
416
|
+
return this.skillManager?.activateForPaths(filePaths) ?? [];
|
|
417
|
+
}
|
|
418
|
+
/** Register a hook that runs after each completed assistant turn. */
|
|
419
|
+
addTurnEndHook(hook) {
|
|
420
|
+
this.turnEndHooks.push(hook);
|
|
421
|
+
this.logger.debug('Turn-end hook registered', { name: hook.name });
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Fork a child agent that inherits parent config.
|
|
425
|
+
* Runs a single chat() call in isolation and returns the result.
|
|
426
|
+
* Ported from old_src/utils/forkedAgent.ts pattern.
|
|
427
|
+
*/
|
|
428
|
+
async fork(prompt, options) {
|
|
429
|
+
if (this.destroyed)
|
|
430
|
+
throw new Error('Agent is destroyed');
|
|
431
|
+
const run = async () => {
|
|
432
|
+
const child = Agent.create({
|
|
433
|
+
apiKey: this.config.apiKey,
|
|
434
|
+
model: options?.model ?? this.config.model,
|
|
435
|
+
baseUrl: this.config.baseUrl,
|
|
436
|
+
systemPrompt: options?.systemPrompt ?? this.config.systemPrompt,
|
|
437
|
+
memory: { enabled: false },
|
|
438
|
+
knowledge: { enabled: false },
|
|
439
|
+
maxIterations: this.config.maxIterations,
|
|
440
|
+
maxConsecutiveErrors: this.config.maxConsecutiveErrors,
|
|
441
|
+
onToolError: this.config.onToolError,
|
|
442
|
+
logLevel: this.config.logLevel,
|
|
443
|
+
});
|
|
444
|
+
if (options?.tools) {
|
|
445
|
+
for (const tool of options.tools) {
|
|
446
|
+
child.addTool(tool);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
try {
|
|
450
|
+
return await child.chat(prompt);
|
|
451
|
+
}
|
|
452
|
+
finally {
|
|
453
|
+
await child.destroy();
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
if (options?.background) {
|
|
457
|
+
void run().catch(err => {
|
|
458
|
+
this.logger.debug('Background fork failed', { error: String(err) });
|
|
459
|
+
});
|
|
460
|
+
return ''; // fire-and-forget — returns immediately
|
|
461
|
+
}
|
|
462
|
+
return run();
|
|
463
|
+
}
|
|
464
|
+
/** Get effective context window for the current model. */
|
|
465
|
+
getEffectiveContextWindow() {
|
|
466
|
+
return getModelContextWindow(this.config.model, this.config.maxContextTokens);
|
|
467
|
+
}
|
|
468
|
+
getHistory(threadId) {
|
|
469
|
+
return this.conversations.getHistory(threadId ?? 'default');
|
|
470
|
+
}
|
|
471
|
+
clearHistory(threadId) {
|
|
472
|
+
const tid = threadId ?? 'default';
|
|
473
|
+
this.conversations.clearThread(tid);
|
|
474
|
+
this.skillManager?.clearStickySkills(tid);
|
|
475
|
+
this.logger.info('Thread cleared', { threadId: tid });
|
|
476
|
+
}
|
|
477
|
+
async connectMCP(config) {
|
|
478
|
+
// Apply defaults (timeout, maxRetries, etc.)
|
|
479
|
+
const parsed = {
|
|
480
|
+
...config,
|
|
481
|
+
timeout: config.timeout ?? 30_000,
|
|
482
|
+
maxRetries: config.maxRetries ?? 3,
|
|
483
|
+
healthCheckInterval: config.healthCheckInterval ?? 60_000,
|
|
484
|
+
isolateErrors: config.isolateErrors ?? true,
|
|
485
|
+
};
|
|
486
|
+
const tools = await this.mcpAdapter.connect(parsed);
|
|
487
|
+
this.logger.info('MCP connected', { name: config.name, tools: tools.length });
|
|
488
|
+
// Register MCP prompts as skills
|
|
489
|
+
if (this.skillManager) {
|
|
490
|
+
for (const [name, prompt] of this.mcpAdapter.getPrompts()) {
|
|
491
|
+
const adapter = this.mcpAdapter;
|
|
492
|
+
this.skillManager.register({
|
|
493
|
+
name,
|
|
494
|
+
description: prompt.description ?? prompt.promptName,
|
|
495
|
+
instructions: '',
|
|
496
|
+
source: 'mcp',
|
|
497
|
+
getPrompt: async (args) => adapter.getPrompt(prompt.serverName, prompt.promptName, args),
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
async disconnectMCP(name) {
|
|
503
|
+
await this.mcpAdapter.disconnect(name);
|
|
504
|
+
this.logger.info('MCP disconnected', { name });
|
|
505
|
+
}
|
|
506
|
+
getHealth() {
|
|
507
|
+
return this.mcpAdapter.getHealth();
|
|
508
|
+
}
|
|
509
|
+
async remember(content, type = 'user', threadId) {
|
|
510
|
+
if (!this.fileMemorySystem)
|
|
511
|
+
throw new Error('Memory subsystem not enabled');
|
|
512
|
+
const name = content.slice(0, 40).replace(/[^a-zA-Z0-9\s]/g, '').trim();
|
|
513
|
+
return this.fileMemorySystem.saveMemory({
|
|
514
|
+
name: name || 'memory',
|
|
515
|
+
description: content.slice(0, 100),
|
|
516
|
+
type,
|
|
517
|
+
content,
|
|
518
|
+
}, threadId);
|
|
519
|
+
}
|
|
520
|
+
async recall(query, threadId) {
|
|
521
|
+
if (!this.fileMemorySystem)
|
|
522
|
+
throw new Error('Memory subsystem not enabled');
|
|
523
|
+
return this.fileMemorySystem.findRelevant(query, undefined, undefined, threadId);
|
|
524
|
+
}
|
|
525
|
+
async ingestKnowledge(document) {
|
|
526
|
+
if (!this.knowledgeManager)
|
|
527
|
+
throw new Error('Knowledge subsystem not enabled');
|
|
528
|
+
const chunks = await this.knowledgeManager.ingest(document);
|
|
529
|
+
this.logger.info('Knowledge ingested', { chunks });
|
|
530
|
+
}
|
|
531
|
+
async searchKnowledge(query) {
|
|
532
|
+
if (!this.knowledgeManager)
|
|
533
|
+
throw new Error('Knowledge subsystem not enabled');
|
|
534
|
+
return this.knowledgeManager.search(query);
|
|
535
|
+
}
|
|
536
|
+
getUsage() {
|
|
537
|
+
return { ...this.costAccumulator };
|
|
538
|
+
}
|
|
539
|
+
async destroy() {
|
|
540
|
+
this.destroyed = true;
|
|
541
|
+
this.skillManager?.clearAllStickySessions();
|
|
542
|
+
await this.mcpAdapter.disconnectAll();
|
|
543
|
+
this.database?.close();
|
|
544
|
+
this.logger.info('Agent destroyed');
|
|
545
|
+
}
|
|
546
|
+
getDefaultConversationStore() {
|
|
547
|
+
this.ensureDatabase();
|
|
548
|
+
return new SQLiteConversationStore(this.database);
|
|
549
|
+
}
|
|
550
|
+
getDefaultVectorStore() {
|
|
551
|
+
this.ensureDatabase();
|
|
552
|
+
return new SQLiteVectorStore(this.database);
|
|
553
|
+
}
|
|
554
|
+
ensureDatabase() {
|
|
555
|
+
if (!this.database) {
|
|
556
|
+
// Expand ~ to home directory
|
|
557
|
+
let dbPath = this.config.dbPath;
|
|
558
|
+
if (dbPath.startsWith('~/')) {
|
|
559
|
+
const os = require('node:os');
|
|
560
|
+
dbPath = dbPath.replace('~', os.homedir());
|
|
561
|
+
}
|
|
562
|
+
this.database = new SQLiteDatabase(dbPath);
|
|
563
|
+
this.database.initialize();
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
/** Timeout for memory relevance prefetch (ms). */
|
|
567
|
+
static MEMORY_PREFETCH_TIMEOUT = 5_000;
|
|
568
|
+
/**
|
|
569
|
+
* Start memory relevance selection asynchronously.
|
|
570
|
+
* Returns a promise that resolves with relevant MemoryFiles.
|
|
571
|
+
* Races against a timeout so it never blocks the response indefinitely.
|
|
572
|
+
*/
|
|
573
|
+
startMemoryPrefetch(userInput, threadId) {
|
|
574
|
+
const controller = new AbortController();
|
|
575
|
+
const timeout = setTimeout(() => controller.abort(), Agent.MEMORY_PREFETCH_TIMEOUT);
|
|
576
|
+
return this.fileMemorySystem
|
|
577
|
+
.findRelevant(userInput, controller.signal, this.surfacedMemories, threadId)
|
|
578
|
+
.catch(() => [])
|
|
579
|
+
.finally(() => clearTimeout(timeout));
|
|
580
|
+
}
|
|
581
|
+
async buildInjectionsWithSkills(userInput, threadId, memoryPrefetch) {
|
|
582
|
+
const injections = [];
|
|
583
|
+
// Skills injection
|
|
584
|
+
const skillToolNames = [];
|
|
585
|
+
if (this.skillManager) {
|
|
586
|
+
const matchedSkills = await this.skillManager.match(userInput, { threadId });
|
|
587
|
+
for (const skill of matchedSkills) {
|
|
588
|
+
// Resolve instructions (dynamic getPrompt or static with arg substitution)
|
|
589
|
+
const rawArgs = skill.triggerPrefix && userInput.startsWith(skill.triggerPrefix)
|
|
590
|
+
? userInput.slice(skill.triggerPrefix.length).trim()
|
|
591
|
+
: skill.aliases?.reduce((acc, alias) => {
|
|
592
|
+
const prefix = alias.startsWith('/') ? alias : `/${alias}`;
|
|
593
|
+
return userInput.startsWith(prefix) ? userInput.slice(prefix.length).trim() : acc;
|
|
594
|
+
}, '')
|
|
595
|
+
?? '';
|
|
596
|
+
const resolved = await this.skillManager.resolveInstructions(skill, rawArgs, {
|
|
597
|
+
threadId,
|
|
598
|
+
traceId: 'pending', // traceId not yet available at injection time
|
|
599
|
+
skillDir: skill.skillDir,
|
|
600
|
+
});
|
|
601
|
+
const tokens = estimateTokens(resolved);
|
|
602
|
+
injections.push({ source: `skill:${skill.name}`, priority: 8, content: resolved, tokens });
|
|
603
|
+
// Register skill-scoped tools
|
|
604
|
+
if (skill.tools?.length) {
|
|
605
|
+
for (const tool of skill.tools) {
|
|
606
|
+
this.toolExecutor.register(tool);
|
|
607
|
+
skillToolNames.push(tool.name);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
// Track invocation
|
|
611
|
+
this.skillManager.markInvoked(skill.name);
|
|
612
|
+
}
|
|
613
|
+
// Skill listing + usage instructions for model discovery
|
|
614
|
+
if (this.config.skills?.modelDiscovery !== false) {
|
|
615
|
+
const budgetChars = Math.floor(this.config.maxContextTokens * 4 * 0.01); // ~1% of context
|
|
616
|
+
const listing = this.skillManager.buildSkillListing(budgetChars);
|
|
617
|
+
if (listing) {
|
|
618
|
+
const listContent = buildSkillToolPrompt(listing);
|
|
619
|
+
injections.push({
|
|
620
|
+
source: 'skill:listing',
|
|
621
|
+
priority: 9,
|
|
622
|
+
content: listContent,
|
|
623
|
+
tokens: estimateTokens(listContent),
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
// Knowledge injection
|
|
629
|
+
if (this.knowledgeManager) {
|
|
630
|
+
try {
|
|
631
|
+
const results = await this.knowledgeManager.search(userInput);
|
|
632
|
+
if (results.length > 0) {
|
|
633
|
+
const content = results.map(r => r.content).join('\n\n');
|
|
634
|
+
const tokens = estimateTokens(content);
|
|
635
|
+
injections.push({ source: 'knowledge', priority: 6, content: `Relevant knowledge:\n${content}`, tokens });
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
catch {
|
|
639
|
+
// Knowledge search failed — continue without it
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
// MCP server instructions injection
|
|
643
|
+
for (const conn of this.mcpAdapter.getConnections()) {
|
|
644
|
+
if (conn.instructions) {
|
|
645
|
+
const tokens = estimateTokens(conn.instructions);
|
|
646
|
+
injections.push({
|
|
647
|
+
source: `mcp:${conn.name}:instructions`,
|
|
648
|
+
priority: 5,
|
|
649
|
+
content: `[MCP Server "${conn.name}" instructions]\n${conn.instructions}`,
|
|
650
|
+
tokens,
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
// Memory injection — file-based system
|
|
655
|
+
if (this.fileMemorySystem) {
|
|
656
|
+
try {
|
|
657
|
+
// Behavioral instructions (types, when to save, verification rules)
|
|
658
|
+
const instructions = this.fileMemorySystem.getMemoryInstructions();
|
|
659
|
+
const instrTokens = estimateTokens(instructions);
|
|
660
|
+
injections.push({ source: 'memory:instructions', priority: 2, content: instructions, tokens: instrTokens });
|
|
661
|
+
// MEMORY.md index content
|
|
662
|
+
const indexContent = await this.fileMemorySystem.buildContextPrompt(threadId);
|
|
663
|
+
if (indexContent) {
|
|
664
|
+
const tokens = estimateTokens(indexContent);
|
|
665
|
+
injections.push({ source: 'memory:index', priority: 3, content: `## MEMORY.md\n${indexContent}`, tokens });
|
|
666
|
+
}
|
|
667
|
+
// LLM-selected relevant memories (from prefetch — already running in parallel)
|
|
668
|
+
const relevant = memoryPrefetch ? await memoryPrefetch : [];
|
|
669
|
+
if (relevant.length > 0) {
|
|
670
|
+
const content = relevant.map(m => {
|
|
671
|
+
const freshness = memoryFreshnessNote(m.mtimeMs);
|
|
672
|
+
const header = m.name ?? m.filename;
|
|
673
|
+
return `- ${header}:${freshness ? ` ${freshness}` : ''} ${m.content}`;
|
|
674
|
+
}).join('\n');
|
|
675
|
+
const tokens = estimateTokens(content);
|
|
676
|
+
injections.push({ source: 'memory:relevant', priority: 4, content: `Relevant memories:\n${content}`, tokens });
|
|
677
|
+
// Track surfaced filenames to avoid re-injection in subsequent turns
|
|
678
|
+
for (const m of relevant) {
|
|
679
|
+
this.surfacedMemories.add(m.filename);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
catch {
|
|
684
|
+
// Memory recall failed — continue without it
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
return { injections, skillToolNames };
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
//# sourceMappingURL=agent.js.map
|