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.
Files changed (318) hide show
  1. package/README.md +561 -0
  2. package/dist/agent.d.ts +105 -0
  3. package/dist/agent.d.ts.map +1 -0
  4. package/dist/agent.js +690 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/config/config.d.ts +346 -0
  7. package/dist/config/config.d.ts.map +1 -0
  8. package/dist/config/config.js +93 -0
  9. package/dist/config/config.js.map +1 -0
  10. package/dist/contracts/entities/agent-event.d.ts +97 -0
  11. package/dist/contracts/entities/agent-event.d.ts.map +1 -0
  12. package/dist/contracts/entities/agent-event.js +2 -0
  13. package/dist/contracts/entities/agent-event.js.map +1 -0
  14. package/dist/contracts/entities/agent-skill.d.ts +59 -0
  15. package/dist/contracts/entities/agent-skill.d.ts.map +1 -0
  16. package/dist/contracts/entities/agent-skill.js +2 -0
  17. package/dist/contracts/entities/agent-skill.js.map +1 -0
  18. package/dist/contracts/entities/agent-tool.d.ts +42 -0
  19. package/dist/contracts/entities/agent-tool.d.ts.map +1 -0
  20. package/dist/contracts/entities/agent-tool.js +2 -0
  21. package/dist/contracts/entities/agent-tool.js.map +1 -0
  22. package/dist/contracts/entities/chat-message.d.ts +13 -0
  23. package/dist/contracts/entities/chat-message.d.ts.map +1 -0
  24. package/dist/contracts/entities/chat-message.js +2 -0
  25. package/dist/contracts/entities/chat-message.js.map +1 -0
  26. package/dist/contracts/entities/content-part.d.ts +16 -0
  27. package/dist/contracts/entities/content-part.d.ts.map +1 -0
  28. package/dist/contracts/entities/content-part.js +2 -0
  29. package/dist/contracts/entities/content-part.js.map +1 -0
  30. package/dist/contracts/entities/execution-context.d.ts +9 -0
  31. package/dist/contracts/entities/execution-context.d.ts.map +1 -0
  32. package/dist/contracts/entities/execution-context.js +2 -0
  33. package/dist/contracts/entities/execution-context.js.map +1 -0
  34. package/dist/contracts/entities/index.d.ts +11 -0
  35. package/dist/contracts/entities/index.d.ts.map +1 -0
  36. package/dist/contracts/entities/index.js +2 -0
  37. package/dist/contracts/entities/index.js.map +1 -0
  38. package/dist/contracts/entities/knowledge.d.ts +21 -0
  39. package/dist/contracts/entities/knowledge.d.ts.map +1 -0
  40. package/dist/contracts/entities/knowledge.js +2 -0
  41. package/dist/contracts/entities/knowledge.js.map +1 -0
  42. package/dist/contracts/entities/stores.d.ts +18 -0
  43. package/dist/contracts/entities/stores.d.ts.map +1 -0
  44. package/dist/contracts/entities/stores.js +2 -0
  45. package/dist/contracts/entities/stores.js.map +1 -0
  46. package/dist/contracts/entities/token-usage.d.ts +7 -0
  47. package/dist/contracts/entities/token-usage.d.ts.map +1 -0
  48. package/dist/contracts/entities/token-usage.js +2 -0
  49. package/dist/contracts/entities/token-usage.js.map +1 -0
  50. package/dist/contracts/entities/tool-call.d.ts +16 -0
  51. package/dist/contracts/entities/tool-call.d.ts.map +1 -0
  52. package/dist/contracts/entities/tool-call.js +2 -0
  53. package/dist/contracts/entities/tool-call.js.map +1 -0
  54. package/dist/contracts/enums/index.d.ts +21 -0
  55. package/dist/contracts/enums/index.d.ts.map +1 -0
  56. package/dist/contracts/enums/index.js +8 -0
  57. package/dist/contracts/enums/index.js.map +1 -0
  58. package/dist/contracts/index.d.ts +3 -0
  59. package/dist/contracts/index.d.ts.map +1 -0
  60. package/dist/contracts/index.js +3 -0
  61. package/dist/contracts/index.js.map +1 -0
  62. package/dist/core/compaction/autocompact.d.ts +18 -0
  63. package/dist/core/compaction/autocompact.d.ts.map +1 -0
  64. package/dist/core/compaction/autocompact.js +68 -0
  65. package/dist/core/compaction/autocompact.js.map +1 -0
  66. package/dist/core/compaction/microcompact.d.ts +20 -0
  67. package/dist/core/compaction/microcompact.d.ts.map +1 -0
  68. package/dist/core/compaction/microcompact.js +38 -0
  69. package/dist/core/compaction/microcompact.js.map +1 -0
  70. package/dist/core/compaction/snip-compact.d.ts +22 -0
  71. package/dist/core/compaction/snip-compact.d.ts.map +1 -0
  72. package/dist/core/compaction/snip-compact.js +61 -0
  73. package/dist/core/compaction/snip-compact.js.map +1 -0
  74. package/dist/core/compaction/tool-result-budget.d.ts +24 -0
  75. package/dist/core/compaction/tool-result-budget.d.ts.map +1 -0
  76. package/dist/core/compaction/tool-result-budget.js +67 -0
  77. package/dist/core/compaction/tool-result-budget.js.map +1 -0
  78. package/dist/core/context-analysis.d.ts +24 -0
  79. package/dist/core/context-analysis.d.ts.map +1 -0
  80. package/dist/core/context-analysis.js +37 -0
  81. package/dist/core/context-analysis.js.map +1 -0
  82. package/dist/core/context-builder.d.ts +25 -0
  83. package/dist/core/context-builder.d.ts.map +1 -0
  84. package/dist/core/context-builder.js +108 -0
  85. package/dist/core/context-builder.js.map +1 -0
  86. package/dist/core/conversation-manager.d.ts +19 -0
  87. package/dist/core/conversation-manager.d.ts.map +1 -0
  88. package/dist/core/conversation-manager.js +62 -0
  89. package/dist/core/conversation-manager.js.map +1 -0
  90. package/dist/core/execution-context.d.ts +6 -0
  91. package/dist/core/execution-context.d.ts.map +1 -0
  92. package/dist/core/execution-context.js +14 -0
  93. package/dist/core/execution-context.js.map +1 -0
  94. package/dist/core/loop-deps.d.ts +15 -0
  95. package/dist/core/loop-deps.d.ts.map +1 -0
  96. package/dist/core/loop-deps.js +8 -0
  97. package/dist/core/loop-deps.js.map +1 -0
  98. package/dist/core/loop-types.d.ts +34 -0
  99. package/dist/core/loop-types.d.ts.map +1 -0
  100. package/dist/core/loop-types.js +15 -0
  101. package/dist/core/loop-types.js.map +1 -0
  102. package/dist/core/message-normalize.d.ts +18 -0
  103. package/dist/core/message-normalize.d.ts.map +1 -0
  104. package/dist/core/message-normalize.js +69 -0
  105. package/dist/core/message-normalize.js.map +1 -0
  106. package/dist/core/prompt-builders.d.ts +36 -0
  107. package/dist/core/prompt-builders.d.ts.map +1 -0
  108. package/dist/core/prompt-builders.js +89 -0
  109. package/dist/core/prompt-builders.js.map +1 -0
  110. package/dist/core/prompt-cache.d.ts +25 -0
  111. package/dist/core/prompt-cache.d.ts.map +1 -0
  112. package/dist/core/prompt-cache.js +34 -0
  113. package/dist/core/prompt-cache.js.map +1 -0
  114. package/dist/core/react-loop.d.ts +43 -0
  115. package/dist/core/react-loop.d.ts.map +1 -0
  116. package/dist/core/react-loop.js +403 -0
  117. package/dist/core/react-loop.js.map +1 -0
  118. package/dist/core/stop-hooks.d.ts +18 -0
  119. package/dist/core/stop-hooks.d.ts.map +1 -0
  120. package/dist/core/stop-hooks.js +18 -0
  121. package/dist/core/stop-hooks.js.map +1 -0
  122. package/dist/core/stream-emitter.d.ts +24 -0
  123. package/dist/core/stream-emitter.d.ts.map +1 -0
  124. package/dist/core/stream-emitter.js +65 -0
  125. package/dist/core/stream-emitter.js.map +1 -0
  126. package/dist/core/streaming-tool-executor.d.ts +54 -0
  127. package/dist/core/streaming-tool-executor.d.ts.map +1 -0
  128. package/dist/core/streaming-tool-executor.js +164 -0
  129. package/dist/core/streaming-tool-executor.js.map +1 -0
  130. package/dist/core/turn-end-hooks.d.ts +39 -0
  131. package/dist/core/turn-end-hooks.d.ts.map +1 -0
  132. package/dist/core/turn-end-hooks.js +36 -0
  133. package/dist/core/turn-end-hooks.js.map +1 -0
  134. package/dist/index.d.ts +39 -0
  135. package/dist/index.d.ts.map +1 -0
  136. package/dist/index.js +45 -0
  137. package/dist/index.js.map +1 -0
  138. package/dist/knowledge/chunking.d.ts +9 -0
  139. package/dist/knowledge/chunking.d.ts.map +1 -0
  140. package/dist/knowledge/chunking.js +49 -0
  141. package/dist/knowledge/chunking.js.map +1 -0
  142. package/dist/knowledge/embedding-service.d.ts +16 -0
  143. package/dist/knowledge/embedding-service.d.ts.map +1 -0
  144. package/dist/knowledge/embedding-service.js +43 -0
  145. package/dist/knowledge/embedding-service.js.map +1 -0
  146. package/dist/knowledge/knowledge-manager.d.ts +33 -0
  147. package/dist/knowledge/knowledge-manager.d.ts.map +1 -0
  148. package/dist/knowledge/knowledge-manager.js +62 -0
  149. package/dist/knowledge/knowledge-manager.js.map +1 -0
  150. package/dist/knowledge/sqlite-vector-store.d.ts +16 -0
  151. package/dist/knowledge/sqlite-vector-store.d.ts.map +1 -0
  152. package/dist/knowledge/sqlite-vector-store.js +56 -0
  153. package/dist/knowledge/sqlite-vector-store.js.map +1 -0
  154. package/dist/knowledge/vector-store.d.ts +2 -0
  155. package/dist/knowledge/vector-store.d.ts.map +1 -0
  156. package/dist/knowledge/vector-store.js +2 -0
  157. package/dist/knowledge/vector-store.js.map +1 -0
  158. package/dist/llm/errors.d.ts +15 -0
  159. package/dist/llm/errors.d.ts.map +1 -0
  160. package/dist/llm/errors.js +39 -0
  161. package/dist/llm/errors.js.map +1 -0
  162. package/dist/llm/message-types.d.ts +80 -0
  163. package/dist/llm/message-types.d.ts.map +1 -0
  164. package/dist/llm/message-types.js +2 -0
  165. package/dist/llm/message-types.js.map +1 -0
  166. package/dist/llm/openrouter-client.d.ts +18 -0
  167. package/dist/llm/openrouter-client.d.ts.map +1 -0
  168. package/dist/llm/openrouter-client.js +215 -0
  169. package/dist/llm/openrouter-client.js.map +1 -0
  170. package/dist/llm/reasoning.d.ts +10 -0
  171. package/dist/llm/reasoning.d.ts.map +1 -0
  172. package/dist/llm/reasoning.js +18 -0
  173. package/dist/llm/reasoning.js.map +1 -0
  174. package/dist/memory/file-memory-system.d.ts +98 -0
  175. package/dist/memory/file-memory-system.d.ts.map +1 -0
  176. package/dist/memory/file-memory-system.js +310 -0
  177. package/dist/memory/file-memory-system.js.map +1 -0
  178. package/dist/memory/memory-age.d.ts +22 -0
  179. package/dist/memory/memory-age.d.ts.map +1 -0
  180. package/dist/memory/memory-age.js +44 -0
  181. package/dist/memory/memory-age.js.map +1 -0
  182. package/dist/memory/memory-extractor.d.ts +56 -0
  183. package/dist/memory/memory-extractor.d.ts.map +1 -0
  184. package/dist/memory/memory-extractor.js +91 -0
  185. package/dist/memory/memory-extractor.js.map +1 -0
  186. package/dist/memory/memory-paths.d.ts +45 -0
  187. package/dist/memory/memory-paths.d.ts.map +1 -0
  188. package/dist/memory/memory-paths.js +121 -0
  189. package/dist/memory/memory-paths.js.map +1 -0
  190. package/dist/memory/memory-prompts.d.ts +41 -0
  191. package/dist/memory/memory-prompts.d.ts.map +1 -0
  192. package/dist/memory/memory-prompts.js +279 -0
  193. package/dist/memory/memory-prompts.js.map +1 -0
  194. package/dist/memory/memory-relevance.d.ts +16 -0
  195. package/dist/memory/memory-relevance.d.ts.map +1 -0
  196. package/dist/memory/memory-relevance.js +46 -0
  197. package/dist/memory/memory-relevance.js.map +1 -0
  198. package/dist/memory/memory-scanner.d.ts +22 -0
  199. package/dist/memory/memory-scanner.d.ts.map +1 -0
  200. package/dist/memory/memory-scanner.js +99 -0
  201. package/dist/memory/memory-scanner.js.map +1 -0
  202. package/dist/memory/memory-tools.d.ts +16 -0
  203. package/dist/memory/memory-tools.d.ts.map +1 -0
  204. package/dist/memory/memory-tools.js +196 -0
  205. package/dist/memory/memory-tools.js.map +1 -0
  206. package/dist/memory/memory-types.d.ts +47 -0
  207. package/dist/memory/memory-types.d.ts.map +1 -0
  208. package/dist/memory/memory-types.js +24 -0
  209. package/dist/memory/memory-types.js.map +1 -0
  210. package/dist/skills/skill-args.d.ts +23 -0
  211. package/dist/skills/skill-args.d.ts.map +1 -0
  212. package/dist/skills/skill-args.js +77 -0
  213. package/dist/skills/skill-args.js.map +1 -0
  214. package/dist/skills/skill-glob.d.ts +24 -0
  215. package/dist/skills/skill-glob.d.ts.map +1 -0
  216. package/dist/skills/skill-glob.js +60 -0
  217. package/dist/skills/skill-glob.js.map +1 -0
  218. package/dist/skills/skill-loader.d.ts +49 -0
  219. package/dist/skills/skill-loader.d.ts.map +1 -0
  220. package/dist/skills/skill-loader.js +197 -0
  221. package/dist/skills/skill-loader.js.map +1 -0
  222. package/dist/skills/skill-manager.d.ts +83 -0
  223. package/dist/skills/skill-manager.d.ts.map +1 -0
  224. package/dist/skills/skill-manager.js +338 -0
  225. package/dist/skills/skill-manager.js.map +1 -0
  226. package/dist/storage/sqlite-conversation-store.d.ts +15 -0
  227. package/dist/storage/sqlite-conversation-store.d.ts.map +1 -0
  228. package/dist/storage/sqlite-conversation-store.js +45 -0
  229. package/dist/storage/sqlite-conversation-store.js.map +1 -0
  230. package/dist/storage/sqlite-database.d.ts +14 -0
  231. package/dist/storage/sqlite-database.d.ts.map +1 -0
  232. package/dist/storage/sqlite-database.js +95 -0
  233. package/dist/storage/sqlite-database.js.map +1 -0
  234. package/dist/tools/builtin/ask-user.d.ts +7 -0
  235. package/dist/tools/builtin/ask-user.d.ts.map +1 -0
  236. package/dist/tools/builtin/ask-user.js +23 -0
  237. package/dist/tools/builtin/ask-user.js.map +1 -0
  238. package/dist/tools/builtin/bash.d.ts +3 -0
  239. package/dist/tools/builtin/bash.d.ts.map +1 -0
  240. package/dist/tools/builtin/bash.js +54 -0
  241. package/dist/tools/builtin/bash.js.map +1 -0
  242. package/dist/tools/builtin/file-edit.d.ts +3 -0
  243. package/dist/tools/builtin/file-edit.d.ts.map +1 -0
  244. package/dist/tools/builtin/file-edit.js +50 -0
  245. package/dist/tools/builtin/file-edit.js.map +1 -0
  246. package/dist/tools/builtin/file-read.d.ts +3 -0
  247. package/dist/tools/builtin/file-read.d.ts.map +1 -0
  248. package/dist/tools/builtin/file-read.js +47 -0
  249. package/dist/tools/builtin/file-read.js.map +1 -0
  250. package/dist/tools/builtin/file-write.d.ts +3 -0
  251. package/dist/tools/builtin/file-write.d.ts.map +1 -0
  252. package/dist/tools/builtin/file-write.js +29 -0
  253. package/dist/tools/builtin/file-write.js.map +1 -0
  254. package/dist/tools/builtin/glob.d.ts +3 -0
  255. package/dist/tools/builtin/glob.d.ts.map +1 -0
  256. package/dist/tools/builtin/glob.js +67 -0
  257. package/dist/tools/builtin/glob.js.map +1 -0
  258. package/dist/tools/builtin/grep.d.ts +3 -0
  259. package/dist/tools/builtin/grep.d.ts.map +1 -0
  260. package/dist/tools/builtin/grep.js +94 -0
  261. package/dist/tools/builtin/grep.js.map +1 -0
  262. package/dist/tools/builtin/index.d.ts +49 -0
  263. package/dist/tools/builtin/index.d.ts.map +1 -0
  264. package/dist/tools/builtin/index.js +65 -0
  265. package/dist/tools/builtin/index.js.map +1 -0
  266. package/dist/tools/builtin/web-fetch.d.ts +3 -0
  267. package/dist/tools/builtin/web-fetch.d.ts.map +1 -0
  268. package/dist/tools/builtin/web-fetch.js +56 -0
  269. package/dist/tools/builtin/web-fetch.js.map +1 -0
  270. package/dist/tools/json-schema-to-zod.d.ts +30 -0
  271. package/dist/tools/json-schema-to-zod.d.ts.map +1 -0
  272. package/dist/tools/json-schema-to-zod.js +123 -0
  273. package/dist/tools/json-schema-to-zod.js.map +1 -0
  274. package/dist/tools/mcp-adapter.d.ts +80 -0
  275. package/dist/tools/mcp-adapter.d.ts.map +1 -0
  276. package/dist/tools/mcp-adapter.js +326 -0
  277. package/dist/tools/mcp-adapter.js.map +1 -0
  278. package/dist/tools/skill-tool.d.ts +41 -0
  279. package/dist/tools/skill-tool.d.ts.map +1 -0
  280. package/dist/tools/skill-tool.js +149 -0
  281. package/dist/tools/skill-tool.js.map +1 -0
  282. package/dist/tools/sql/index.d.ts +4 -0
  283. package/dist/tools/sql/index.d.ts.map +1 -0
  284. package/dist/tools/sql/index.js +2 -0
  285. package/dist/tools/sql/index.js.map +1 -0
  286. package/dist/tools/sql/sql-query-def.d.ts +22 -0
  287. package/dist/tools/sql/sql-query-def.d.ts.map +1 -0
  288. package/dist/tools/sql/sql-query-def.js +2 -0
  289. package/dist/tools/sql/sql-query-def.js.map +1 -0
  290. package/dist/tools/sql/sql-tool-factory.d.ts +28 -0
  291. package/dist/tools/sql/sql-tool-factory.d.ts.map +1 -0
  292. package/dist/tools/sql/sql-tool-factory.js +136 -0
  293. package/dist/tools/sql/sql-tool-factory.js.map +1 -0
  294. package/dist/tools/tool-executor.d.ts +67 -0
  295. package/dist/tools/tool-executor.d.ts.map +1 -0
  296. package/dist/tools/tool-executor.js +232 -0
  297. package/dist/tools/tool-executor.js.map +1 -0
  298. package/dist/utils/cache.d.ts +22 -0
  299. package/dist/utils/cache.d.ts.map +1 -0
  300. package/dist/utils/cache.js +61 -0
  301. package/dist/utils/cache.js.map +1 -0
  302. package/dist/utils/logger.d.ts +15 -0
  303. package/dist/utils/logger.d.ts.map +1 -0
  304. package/dist/utils/logger.js +46 -0
  305. package/dist/utils/logger.js.map +1 -0
  306. package/dist/utils/model-context.d.ts +14 -0
  307. package/dist/utils/model-context.d.ts.map +1 -0
  308. package/dist/utils/model-context.js +52 -0
  309. package/dist/utils/model-context.js.map +1 -0
  310. package/dist/utils/retry.d.ts +13 -0
  311. package/dist/utils/retry.d.ts.map +1 -0
  312. package/dist/utils/retry.js +41 -0
  313. package/dist/utils/retry.js.map +1 -0
  314. package/dist/utils/token-counter.d.ts +6 -0
  315. package/dist/utils/token-counter.d.ts.map +1 -0
  316. package/dist/utils/token-counter.js +19 -0
  317. package/dist/utils/token-counter.js.map +1 -0
  318. 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