@tenex-chat/backend 0.9.1

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 (427) hide show
  1. package/README.md +194 -0
  2. package/dist/backend-wrapper.cjs +3 -0
  3. package/dist/src/index.js +331928 -0
  4. package/package.json +103 -0
  5. package/src/agents/AgentRegistry.ts +418 -0
  6. package/src/agents/AgentStorage.ts +1133 -0
  7. package/src/agents/ConfigResolver.ts +229 -0
  8. package/src/agents/agent-installer.ts +236 -0
  9. package/src/agents/agent-loader.ts +241 -0
  10. package/src/agents/constants.ts +82 -0
  11. package/src/agents/errors.ts +48 -0
  12. package/src/agents/execution/AgentExecutor.ts +561 -0
  13. package/src/agents/execution/ExecutionContextFactory.ts +112 -0
  14. package/src/agents/execution/MessageCompiler.ts +597 -0
  15. package/src/agents/execution/MessageSyncer.ts +100 -0
  16. package/src/agents/execution/PostCompletionChecker.ts +278 -0
  17. package/src/agents/execution/ProgressMonitor.ts +50 -0
  18. package/src/agents/execution/RALResolver.ts +177 -0
  19. package/src/agents/execution/SessionManager.ts +181 -0
  20. package/src/agents/execution/StreamCallbacks.ts +312 -0
  21. package/src/agents/execution/StreamExecutionHandler.ts +579 -0
  22. package/src/agents/execution/StreamSetup.ts +313 -0
  23. package/src/agents/execution/ToolEventHandlers.ts +239 -0
  24. package/src/agents/execution/ToolExecutionTracker.ts +498 -0
  25. package/src/agents/execution/ToolResultUtils.ts +97 -0
  26. package/src/agents/execution/ToolSupervisionWrapper.ts +174 -0
  27. package/src/agents/execution/constants.ts +16 -0
  28. package/src/agents/execution/index.ts +3 -0
  29. package/src/agents/execution/types.ts +96 -0
  30. package/src/agents/execution/utils.ts +26 -0
  31. package/src/agents/index.ts +4 -0
  32. package/src/agents/script-installer.ts +266 -0
  33. package/src/agents/supervision/SupervisorLLMService.ts +253 -0
  34. package/src/agents/supervision/SupervisorOrchestrator.ts +471 -0
  35. package/src/agents/supervision/heuristics/ConsecutiveToolsWithoutTodoHeuristic.ts +73 -0
  36. package/src/agents/supervision/heuristics/DelegationClaimHeuristic.ts +80 -0
  37. package/src/agents/supervision/heuristics/HeuristicRegistry.ts +114 -0
  38. package/src/agents/supervision/heuristics/PendingTodosHeuristic.ts +93 -0
  39. package/src/agents/supervision/heuristics/SilentAgentHeuristic.ts +54 -0
  40. package/src/agents/supervision/heuristics/index.ts +5 -0
  41. package/src/agents/supervision/index.ts +28 -0
  42. package/src/agents/supervision/registerHeuristics.ts +110 -0
  43. package/src/agents/supervision/supervisionHealthCheck.ts +123 -0
  44. package/src/agents/supervision/types.ts +171 -0
  45. package/src/agents/tool-names.ts +46 -0
  46. package/src/agents/tool-normalization.ts +184 -0
  47. package/src/agents/types/index.ts +2 -0
  48. package/src/agents/types/runtime.ts +74 -0
  49. package/src/agents/types/storage.ts +145 -0
  50. package/src/commands/agent/import/index.ts +6 -0
  51. package/src/commands/agent/import/openclaw-distiller.ts +57 -0
  52. package/src/commands/agent/import/openclaw-reader.ts +141 -0
  53. package/src/commands/agent/import/openclaw.ts +154 -0
  54. package/src/commands/agent/index.ts +6 -0
  55. package/src/commands/agent.ts +215 -0
  56. package/src/commands/daemon.ts +198 -0
  57. package/src/commands/doctor.ts +134 -0
  58. package/src/commands/setup/embed.ts +228 -0
  59. package/src/commands/setup/global-system-prompt.ts +223 -0
  60. package/src/commands/setup/image.ts +179 -0
  61. package/src/commands/setup/index.ts +16 -0
  62. package/src/commands/setup/interactive.ts +95 -0
  63. package/src/commands/setup/llm.ts +38 -0
  64. package/src/commands/setup/onboarding.ts +294 -0
  65. package/src/commands/setup/providers.ts +27 -0
  66. package/src/constants.ts +34 -0
  67. package/src/conversations/ConversationDiskReader.ts +148 -0
  68. package/src/conversations/ConversationRegistry.ts +728 -0
  69. package/src/conversations/ConversationStore.ts +868 -0
  70. package/src/conversations/MessageBuilder.ts +866 -0
  71. package/src/conversations/executionTime.ts +62 -0
  72. package/src/conversations/formatters/DelegationXmlFormatter.ts +64 -0
  73. package/src/conversations/formatters/ThreadedConversationFormatter.ts +303 -0
  74. package/src/conversations/formatters/index.ts +9 -0
  75. package/src/conversations/formatters/utils/MessageFormatter.ts +46 -0
  76. package/src/conversations/formatters/utils/TimestampFormatter.ts +56 -0
  77. package/src/conversations/formatters/utils/TreeBuilder.ts +131 -0
  78. package/src/conversations/formatters/utils/TreeRenderer.ts +49 -0
  79. package/src/conversations/index.ts +2 -0
  80. package/src/conversations/persistence/ToolMessageStorage.ts +143 -0
  81. package/src/conversations/search/ConversationIndexManager.ts +393 -0
  82. package/src/conversations/search/QueryParser.ts +114 -0
  83. package/src/conversations/search/SearchEngine.ts +175 -0
  84. package/src/conversations/search/SnippetExtractor.ts +345 -0
  85. package/src/conversations/search/embeddings/ConversationEmbeddingService.ts +484 -0
  86. package/src/conversations/search/embeddings/ConversationIndexingJob.ts +320 -0
  87. package/src/conversations/search/embeddings/IndexingStateManager.ts +338 -0
  88. package/src/conversations/search/embeddings/index.ts +18 -0
  89. package/src/conversations/search/index.ts +49 -0
  90. package/src/conversations/search/types.ts +124 -0
  91. package/src/conversations/services/CategoryManager.ts +160 -0
  92. package/src/conversations/services/ConversationResolver.ts +296 -0
  93. package/src/conversations/services/ConversationSummarizer.ts +234 -0
  94. package/src/conversations/services/MetadataDebounceManager.ts +188 -0
  95. package/src/conversations/services/index.ts +2 -0
  96. package/src/conversations/types.ts +148 -0
  97. package/src/conversations/utils/content-utils.ts +69 -0
  98. package/src/conversations/utils/image-placeholder.ts +281 -0
  99. package/src/conversations/utils/image-url-utils.ts +171 -0
  100. package/src/conversations/utils/multimodal-content.ts +90 -0
  101. package/src/conversations/utils/tool-result-truncator.ts +159 -0
  102. package/src/daemon/Daemon.ts +1883 -0
  103. package/src/daemon/ProjectRuntime.ts +657 -0
  104. package/src/daemon/RestartState.ts +152 -0
  105. package/src/daemon/RuntimeLifecycle.ts +268 -0
  106. package/src/daemon/SubscriptionManager.ts +305 -0
  107. package/src/daemon/UnixSocketTransport.ts +318 -0
  108. package/src/daemon/filters/SubscriptionFilterBuilder.ts +119 -0
  109. package/src/daemon/index.ts +9 -0
  110. package/src/daemon/routing/DaemonRouter.ts +491 -0
  111. package/src/daemon/types.ts +150 -0
  112. package/src/daemon/utils/routing-log.ts +76 -0
  113. package/src/daemon/utils/telemetry.ts +173 -0
  114. package/src/event-handler/agentDeletion.ts +383 -0
  115. package/src/event-handler/index.ts +749 -0
  116. package/src/event-handler/newConversation.ts +165 -0
  117. package/src/event-handler/project.ts +166 -0
  118. package/src/event-handler/reply.ts +18 -0
  119. package/src/events/NDKAgentDefinition.ts +292 -0
  120. package/src/events/NDKAgentLesson.ts +106 -0
  121. package/src/events/NDKEventMetadata.ts +34 -0
  122. package/src/events/NDKMCPTool.ts +60 -0
  123. package/src/events/NDKProjectStatus.ts +384 -0
  124. package/src/events/index.ts +4 -0
  125. package/src/index.ts +126 -0
  126. package/src/lib/agent-home.ts +334 -0
  127. package/src/lib/error-formatter.ts +200 -0
  128. package/src/lib/fs/filesystem.ts +128 -0
  129. package/src/lib/fs/index.ts +1 -0
  130. package/src/lib/json-parser.ts +30 -0
  131. package/src/lib/string.ts +15 -0
  132. package/src/lib/time.ts +74 -0
  133. package/src/llm/ChunkHandler.ts +277 -0
  134. package/src/llm/FinishHandler.ts +250 -0
  135. package/src/llm/LLMConfigEditor.ts +154 -0
  136. package/src/llm/LLMServiceFactory.ts +230 -0
  137. package/src/llm/MessageProcessor.ts +90 -0
  138. package/src/llm/RecordingState.ts +37 -0
  139. package/src/llm/StreamPublisher.ts +40 -0
  140. package/src/llm/TracingUtils.ts +77 -0
  141. package/src/llm/chunk-validators.ts +57 -0
  142. package/src/llm/constants.ts +6 -0
  143. package/src/llm/index.ts +12 -0
  144. package/src/llm/meta/MetaModelResolver.ts +352 -0
  145. package/src/llm/meta/index.ts +11 -0
  146. package/src/llm/middleware/flight-recorder.ts +188 -0
  147. package/src/llm/providers/MockProvider.ts +332 -0
  148. package/src/llm/providers/agent/ClaudeCodeProvider.ts +343 -0
  149. package/src/llm/providers/agent/ClaudeCodeToolsAdapter.ts +203 -0
  150. package/src/llm/providers/agent/CodexAppServerProvider.ts +214 -0
  151. package/src/llm/providers/agent/CodexAppServerToolsAdapter.ts +91 -0
  152. package/src/llm/providers/agent/index.ts +10 -0
  153. package/src/llm/providers/base/AgentProvider.ts +107 -0
  154. package/src/llm/providers/base/BaseProvider.ts +114 -0
  155. package/src/llm/providers/base/StandardProvider.ts +38 -0
  156. package/src/llm/providers/base/index.ts +9 -0
  157. package/src/llm/providers/index.ts +106 -0
  158. package/src/llm/providers/key-manager.ts +238 -0
  159. package/src/llm/providers/ollama-models.ts +105 -0
  160. package/src/llm/providers/openrouter-models.ts +102 -0
  161. package/src/llm/providers/provider-ids.ts +18 -0
  162. package/src/llm/providers/registry/ProviderRegistry.ts +414 -0
  163. package/src/llm/providers/registry/index.ts +7 -0
  164. package/src/llm/providers/standard/AnthropicProvider.ts +71 -0
  165. package/src/llm/providers/standard/OllamaProvider.ts +59 -0
  166. package/src/llm/providers/standard/OpenAIProvider.ts +44 -0
  167. package/src/llm/providers/standard/OpenRouterProvider.ts +103 -0
  168. package/src/llm/providers/standard/index.ts +10 -0
  169. package/src/llm/providers/types.ts +194 -0
  170. package/src/llm/providers/usage-metadata.ts +78 -0
  171. package/src/llm/service.ts +713 -0
  172. package/src/llm/types.ts +167 -0
  173. package/src/llm/utils/ConfigurationManager.ts +650 -0
  174. package/src/llm/utils/ConfigurationTester.ts +229 -0
  175. package/src/llm/utils/ModelSelector.ts +212 -0
  176. package/src/llm/utils/ProviderConfigUI.ts +177 -0
  177. package/src/llm/utils/claudeCodePromptCompiler.ts +141 -0
  178. package/src/llm/utils/codex-models.ts +53 -0
  179. package/src/llm/utils/context-window-cache.ts +30 -0
  180. package/src/llm/utils/models-dev-cache.ts +267 -0
  181. package/src/llm/utils/provider-setup.ts +50 -0
  182. package/src/llm/utils/tool-errors.ts +78 -0
  183. package/src/llm/utils/usage.ts +74 -0
  184. package/src/logging/EventRoutingLogger.ts +205 -0
  185. package/src/nostr/AgentEventDecoder.ts +357 -0
  186. package/src/nostr/AgentEventEncoder.ts +677 -0
  187. package/src/nostr/AgentProfilePublisher.ts +657 -0
  188. package/src/nostr/AgentPublisher.ts +437 -0
  189. package/src/nostr/BlossomService.ts +226 -0
  190. package/src/nostr/InterventionPublisher.ts +132 -0
  191. package/src/nostr/TagExtractor.ts +228 -0
  192. package/src/nostr/collectEvents.ts +83 -0
  193. package/src/nostr/constants.ts +38 -0
  194. package/src/nostr/encryption.ts +26 -0
  195. package/src/nostr/index.ts +31 -0
  196. package/src/nostr/keys.ts +17 -0
  197. package/src/nostr/kinds.ts +37 -0
  198. package/src/nostr/ndkClient.ts +72 -0
  199. package/src/nostr/relays.ts +43 -0
  200. package/src/nostr/trace-context.ts +39 -0
  201. package/src/nostr/types.ts +227 -0
  202. package/src/nostr/utils.ts +84 -0
  203. package/src/prompts/core/FragmentRegistry.ts +30 -0
  204. package/src/prompts/core/PromptBuilder.ts +98 -0
  205. package/src/prompts/core/index.ts +3 -0
  206. package/src/prompts/core/types.ts +13 -0
  207. package/src/prompts/fragments/00-global-system-prompt.ts +44 -0
  208. package/src/prompts/fragments/01-agent-identity.ts +69 -0
  209. package/src/prompts/fragments/02-agent-home-directory.ts +114 -0
  210. package/src/prompts/fragments/03-system-reminders-explanation.ts +14 -0
  211. package/src/prompts/fragments/04-relay-configuration.ts +38 -0
  212. package/src/prompts/fragments/05-delegation-chain.ts +45 -0
  213. package/src/prompts/fragments/06-agent-todos.ts +74 -0
  214. package/src/prompts/fragments/06-todo-usage-guidance.ts +34 -0
  215. package/src/prompts/fragments/07-meta-project-context.ts +234 -0
  216. package/src/prompts/fragments/08-active-conversations.ts +382 -0
  217. package/src/prompts/fragments/09-recent-conversations.ts +153 -0
  218. package/src/prompts/fragments/10-referenced-article.ts +21 -0
  219. package/src/prompts/fragments/11-nudges.ts +134 -0
  220. package/src/prompts/fragments/12-skills.ts +127 -0
  221. package/src/prompts/fragments/13-available-nudges.ts +122 -0
  222. package/src/prompts/fragments/15-available-agents.ts +53 -0
  223. package/src/prompts/fragments/16-stay-in-your-lane.ts +41 -0
  224. package/src/prompts/fragments/17-todo-before-delegation.ts +39 -0
  225. package/src/prompts/fragments/20-voice-mode.ts +62 -0
  226. package/src/prompts/fragments/22-scheduled-tasks.ts +175 -0
  227. package/src/prompts/fragments/24-retrieved-lessons.ts +26 -0
  228. package/src/prompts/fragments/25-rag-instructions.ts +333 -0
  229. package/src/prompts/fragments/26-mcp-resources.ts +237 -0
  230. package/src/prompts/fragments/27-memorized-reports.ts +77 -0
  231. package/src/prompts/fragments/28-agent-directed-monitoring.ts +32 -0
  232. package/src/prompts/fragments/29-rag-collections.ts +50 -0
  233. package/src/prompts/fragments/30-worktree-context.ts +98 -0
  234. package/src/prompts/fragments/31-agents-md-guidance.ts +96 -0
  235. package/src/prompts/fragments/32-process-metrics.ts +72 -0
  236. package/src/prompts/fragments/debug-mode.ts +48 -0
  237. package/src/prompts/fragments/delegation-completion.ts +44 -0
  238. package/src/prompts/fragments/index.ts +91 -0
  239. package/src/prompts/index.ts +21 -0
  240. package/src/prompts/utils/systemPromptBuilder.ts +777 -0
  241. package/src/scripts/migrate-prefix-index.ts +157 -0
  242. package/src/services/AgentDefinitionMonitor.ts +701 -0
  243. package/src/services/ConfigService.ts +723 -0
  244. package/src/services/CooldownRegistry.ts +199 -0
  245. package/src/services/LLMOperationsRegistry.ts +424 -0
  246. package/src/services/OwnerAgentListService.ts +354 -0
  247. package/src/services/PubkeyService.ts +308 -0
  248. package/src/services/agents/AgentMetadataStore.ts +72 -0
  249. package/src/services/agents/AgentResolution.ts +59 -0
  250. package/src/services/agents/EscalationService.ts +281 -0
  251. package/src/services/agents/NDKAgentDiscovery.ts +95 -0
  252. package/src/services/agents/index.ts +7 -0
  253. package/src/services/agents-md/AgentsMdService.ts +184 -0
  254. package/src/services/agents-md/SystemReminderInjector.ts +238 -0
  255. package/src/services/agents-md/index.ts +11 -0
  256. package/src/services/apns/APNsClient.ts +203 -0
  257. package/src/services/apns/APNsService.ts +358 -0
  258. package/src/services/apns/index.ts +11 -0
  259. package/src/services/apns/types.ts +80 -0
  260. package/src/services/compression/CompressionService.ts +445 -0
  261. package/src/services/compression/compression-schema.ts +28 -0
  262. package/src/services/compression/compression-types.ts +74 -0
  263. package/src/services/compression/compression-utils.ts +587 -0
  264. package/src/services/config/types.ts +394 -0
  265. package/src/services/dispatch/AgentDispatchService.ts +937 -0
  266. package/src/services/dispatch/AgentRouter.ts +181 -0
  267. package/src/services/dispatch/DelegationCompletionHandler.ts +232 -0
  268. package/src/services/embedding/EmbeddingProvider.ts +188 -0
  269. package/src/services/embedding/index.ts +5 -0
  270. package/src/services/event-context/EventContextService.ts +108 -0
  271. package/src/services/event-context/index.ts +2 -0
  272. package/src/services/heuristics/ContextBuilder.ts +106 -0
  273. package/src/services/heuristics/HeuristicEngine.ts +200 -0
  274. package/src/services/heuristics/formatters.ts +58 -0
  275. package/src/services/heuristics/index.ts +12 -0
  276. package/src/services/heuristics/rules/index.ts +25 -0
  277. package/src/services/heuristics/rules/todoBeforeDelegation.ts +69 -0
  278. package/src/services/heuristics/rules/todoReminderOnToolUse.ts +63 -0
  279. package/src/services/heuristics/types.ts +144 -0
  280. package/src/services/image/ImageGenerationService.ts +389 -0
  281. package/src/services/image/index.ts +12 -0
  282. package/src/services/intervention/InterventionService.ts +1352 -0
  283. package/src/services/intervention/index.ts +7 -0
  284. package/src/services/mcp/MCPManager.ts +683 -0
  285. package/src/services/mcp/McpNotificationDelivery.ts +139 -0
  286. package/src/services/mcp/McpSubscriptionService.ts +653 -0
  287. package/src/services/mcp/mcpInstaller.ts +130 -0
  288. package/src/services/nip46/Nip46SigningLog.ts +81 -0
  289. package/src/services/nip46/Nip46SigningService.ts +467 -0
  290. package/src/services/nip46/index.ts +4 -0
  291. package/src/services/nudge/NudgeService.ts +224 -0
  292. package/src/services/nudge/NudgeWhitelistService.ts +382 -0
  293. package/src/services/nudge/index.ts +5 -0
  294. package/src/services/nudge/types.ts +83 -0
  295. package/src/services/projects/ProjectContext.ts +672 -0
  296. package/src/services/projects/ProjectContextStore.ts +102 -0
  297. package/src/services/projects/index.ts +6 -0
  298. package/src/services/prompt-compiler/index.ts +15 -0
  299. package/src/services/prompt-compiler/prompt-compiler-service.ts +1143 -0
  300. package/src/services/pubkey-gate/PubkeyGateService.ts +93 -0
  301. package/src/services/pubkey-gate/index.ts +1 -0
  302. package/src/services/rag/EmbeddingProviderFactory.ts +292 -0
  303. package/src/services/rag/LanceDBMaintenanceService.ts +211 -0
  304. package/src/services/rag/RAGDatabaseService.ts +173 -0
  305. package/src/services/rag/RAGOperations.ts +682 -0
  306. package/src/services/rag/RAGService.ts +240 -0
  307. package/src/services/rag/RagSubscriptionService.ts +618 -0
  308. package/src/services/rag/rag-utils.ts +174 -0
  309. package/src/services/ral/PendingDelegationsRegistry.ts +168 -0
  310. package/src/services/ral/RALRegistry.ts +2782 -0
  311. package/src/services/ral/index.ts +4 -0
  312. package/src/services/ral/types.ts +292 -0
  313. package/src/services/reports/LocalReportStore.ts +380 -0
  314. package/src/services/reports/ReportEmbeddingService.ts +430 -0
  315. package/src/services/reports/ReportService.ts +440 -0
  316. package/src/services/reports/articleUtils.ts +52 -0
  317. package/src/services/reports/index.ts +7 -0
  318. package/src/services/scheduling/SchedulerService.ts +1057 -0
  319. package/src/services/scheduling/errors.ts +14 -0
  320. package/src/services/scheduling/index.ts +7 -0
  321. package/src/services/scheduling/utils.ts +77 -0
  322. package/src/services/search/SearchProviderRegistry.ts +78 -0
  323. package/src/services/search/UnifiedSearchService.ts +218 -0
  324. package/src/services/search/index.ts +47 -0
  325. package/src/services/search/projectFilter.ts +22 -0
  326. package/src/services/search/providers/ConversationSearchProvider.ts +48 -0
  327. package/src/services/search/providers/LessonSearchProvider.ts +75 -0
  328. package/src/services/search/providers/ReportSearchProvider.ts +49 -0
  329. package/src/services/search/types.ts +144 -0
  330. package/src/services/skill/SkillService.ts +482 -0
  331. package/src/services/skill/index.ts +2 -0
  332. package/src/services/skill/types.ts +70 -0
  333. package/src/services/status/OperationsStatusService.ts +276 -0
  334. package/src/services/status/ProjectStatusService.ts +522 -0
  335. package/src/services/status/index.ts +11 -0
  336. package/src/services/storage/PrefixKVStore.ts +242 -0
  337. package/src/services/storage/index.ts +1 -0
  338. package/src/services/system-reminder/SystemReminderUtils.ts +96 -0
  339. package/src/services/system-reminder/index.ts +7 -0
  340. package/src/services/trust-pubkeys/TrustPubkeyService.ts +325 -0
  341. package/src/services/trust-pubkeys/index.ts +2 -0
  342. package/src/telemetry/ConversationSpanManager.ts +111 -0
  343. package/src/telemetry/EventLoopMonitor.ts +206 -0
  344. package/src/telemetry/LLMSpanRegistry.ts +20 -0
  345. package/src/telemetry/NostrSpanProcessor.ts +89 -0
  346. package/src/telemetry/ToolCallSpanProcessor.ts +66 -0
  347. package/src/telemetry/diagnostics.ts +27 -0
  348. package/src/telemetry/setup.ts +120 -0
  349. package/src/tools/implementations/agents_discover.ts +121 -0
  350. package/src/tools/implementations/agents_hire.ts +127 -0
  351. package/src/tools/implementations/agents_list.ts +96 -0
  352. package/src/tools/implementations/agents_publish.ts +611 -0
  353. package/src/tools/implementations/agents_read.ts +173 -0
  354. package/src/tools/implementations/agents_write.ts +200 -0
  355. package/src/tools/implementations/ask.ts +411 -0
  356. package/src/tools/implementations/change_model.ts +141 -0
  357. package/src/tools/implementations/conversation_get.ts +661 -0
  358. package/src/tools/implementations/conversation_list.ts +377 -0
  359. package/src/tools/implementations/conversation_search.ts +370 -0
  360. package/src/tools/implementations/delegate.ts +327 -0
  361. package/src/tools/implementations/delegate_crossproject.ts +209 -0
  362. package/src/tools/implementations/delegate_followup.ts +300 -0
  363. package/src/tools/implementations/fs_edit.ts +162 -0
  364. package/src/tools/implementations/fs_glob.ts +182 -0
  365. package/src/tools/implementations/fs_grep.ts +513 -0
  366. package/src/tools/implementations/fs_read.ts +332 -0
  367. package/src/tools/implementations/fs_write.ts +113 -0
  368. package/src/tools/implementations/generate_image.ts +259 -0
  369. package/src/tools/implementations/home_fs.ts +515 -0
  370. package/src/tools/implementations/kill.ts +651 -0
  371. package/src/tools/implementations/learn.ts +166 -0
  372. package/src/tools/implementations/lesson-formatter.ts +38 -0
  373. package/src/tools/implementations/lesson_delete.ts +164 -0
  374. package/src/tools/implementations/lesson_get.ts +105 -0
  375. package/src/tools/implementations/lessons_list.ts +153 -0
  376. package/src/tools/implementations/mcp_resource_read.ts +161 -0
  377. package/src/tools/implementations/mcp_subscribe.ts +158 -0
  378. package/src/tools/implementations/mcp_subscription_stop.ts +85 -0
  379. package/src/tools/implementations/nostr_fetch.ts +149 -0
  380. package/src/tools/implementations/nostr_publish_as_user.ts +353 -0
  381. package/src/tools/implementations/project_list.ts +146 -0
  382. package/src/tools/implementations/rag_add_documents.ts +573 -0
  383. package/src/tools/implementations/rag_create_collection.ts +65 -0
  384. package/src/tools/implementations/rag_delete_collection.ts +68 -0
  385. package/src/tools/implementations/rag_list_collections.ts +77 -0
  386. package/src/tools/implementations/rag_query.ts +107 -0
  387. package/src/tools/implementations/rag_subscription_create.ts +105 -0
  388. package/src/tools/implementations/rag_subscription_delete.ts +80 -0
  389. package/src/tools/implementations/rag_subscription_get.ts +123 -0
  390. package/src/tools/implementations/rag_subscription_list.ts +128 -0
  391. package/src/tools/implementations/report_delete.ts +79 -0
  392. package/src/tools/implementations/report_read.ts +160 -0
  393. package/src/tools/implementations/report_write.ts +278 -0
  394. package/src/tools/implementations/reports_list.ts +77 -0
  395. package/src/tools/implementations/schedule_task.ts +104 -0
  396. package/src/tools/implementations/schedule_task_cancel.ts +62 -0
  397. package/src/tools/implementations/schedule_task_once.ts +128 -0
  398. package/src/tools/implementations/schedule_tasks_list.ts +79 -0
  399. package/src/tools/implementations/search.ts +160 -0
  400. package/src/tools/implementations/shell.ts +553 -0
  401. package/src/tools/implementations/todo.ts +260 -0
  402. package/src/tools/implementations/upload_blob.ts +381 -0
  403. package/src/tools/implementations/web_fetch.ts +153 -0
  404. package/src/tools/implementations/web_search.ts +250 -0
  405. package/src/tools/registry.ts +670 -0
  406. package/src/tools/types.ts +177 -0
  407. package/src/tools/utils.ts +256 -0
  408. package/src/types/event-ids.ts +320 -0
  409. package/src/types/index.ts +46 -0
  410. package/src/utils/agentFetcher.ts +107 -0
  411. package/src/utils/cli-error.ts +29 -0
  412. package/src/utils/conversation-id.ts +27 -0
  413. package/src/utils/conversation-utils.ts +1 -0
  414. package/src/utils/delegation-chain.ts +357 -0
  415. package/src/utils/error-handler.ts +42 -0
  416. package/src/utils/git/gitignore.ts +69 -0
  417. package/src/utils/git/index.ts +2 -0
  418. package/src/utils/git/initializeGitRepo.ts +204 -0
  419. package/src/utils/git/worktree.ts +260 -0
  420. package/src/utils/lessonFormatter.ts +70 -0
  421. package/src/utils/lessonTrust.ts +24 -0
  422. package/src/utils/lockfile.ts +123 -0
  423. package/src/utils/logger.ts +149 -0
  424. package/src/utils/nostr-entity-parser.ts +365 -0
  425. package/src/utils/process.ts +49 -0
  426. package/src/wrapper.ts +262 -0
  427. package/tsconfig.json +41 -0
@@ -0,0 +1,166 @@
1
+ import type { AISdkTool, ToolExecutionContext } from "@/tools/types";
2
+ import type { EventContext, LessonIntent } from "@/nostr/types";
3
+ import { getProjectContext, isProjectContextInitialized } from "@/services/projects";
4
+ import { RAGService } from "@/services/rag/RAGService";
5
+ import { logger } from "@/utils/logger";
6
+ import { tool } from "ai";
7
+ import { z } from "zod";
8
+
9
+ const lessonLearnSchema = z.object({
10
+ title: z.string().describe("Brief title/description of what this lesson is about"),
11
+ lesson: z.string().describe("The key insight or lesson learned - be concise and actionable"),
12
+ detailed: z
13
+ .string()
14
+ .optional()
15
+ .describe("Detailed version with richer explanation when deeper context is needed"),
16
+ category: z
17
+ .string()
18
+ .optional()
19
+ .describe(
20
+ "Single category for filing this lesson (e.g., 'architecture', 'debugging', 'user-preferences')"
21
+ ),
22
+ hashtags: z
23
+ .array(z.string())
24
+ .default([])
25
+ .describe("Hashtags for easier sorting and discovery (e.g., ['async', 'error-handling'])"),
26
+ });
27
+
28
+ type LessonLearnInput = z.infer<typeof lessonLearnSchema>;
29
+ type LessonLearnOutput = {
30
+ message: string;
31
+ eventId: string;
32
+ title: string;
33
+ hasDetailed: boolean;
34
+ };
35
+
36
+ // Core implementation - extracted from existing execute function
37
+ async function executeLessonLearn(
38
+ input: LessonLearnInput,
39
+ context: ToolExecutionContext
40
+ ): Promise<LessonLearnOutput> {
41
+ const { title, lesson, detailed, category, hashtags } = input;
42
+
43
+ logger.info("Agent recording new lesson", {
44
+ agent: context.agent.name,
45
+ agentPubkey: context.agent.pubkey,
46
+ title,
47
+ lessonLength: lesson.length,
48
+ conversationId: context.conversationId,
49
+ });
50
+
51
+ // Create lesson intent
52
+ const intent: LessonIntent = {
53
+ title,
54
+ lesson,
55
+ detailed,
56
+ category,
57
+ hashtags,
58
+ };
59
+
60
+ // Create event context
61
+ // Note: encodeLesson only uses addStandardTags which needs model/ralNumber
62
+ // rootEvent.id is not used by lesson encoding
63
+ const eventContext: EventContext = {
64
+ triggeringEvent: context.triggeringEvent,
65
+ rootEvent: {},
66
+ conversationId: context.conversationId,
67
+ model: context.agent.llmConfig,
68
+ ralNumber: context.ralNumber,
69
+ };
70
+
71
+ // Use shared AgentPublisher instance from context to create and publish the lesson
72
+ const lessonEvent = await context.agentPublisher.lesson(intent, eventContext);
73
+
74
+ // Add lesson to RAG collection for semantic search
75
+ try {
76
+ const ragService = RAGService.getInstance();
77
+
78
+ // Ensure the lessons collection exists
79
+ try {
80
+ await ragService.createCollection("lessons");
81
+ } catch (error) {
82
+ // Collection might already exist, which is fine
83
+ logger.debug("Lessons collection might already exist", { error });
84
+ }
85
+
86
+ // Add the lesson to the RAG collection
87
+ const lessonContent = detailed || lesson;
88
+
89
+ // Get projectId for project-scoped search isolation
90
+ let projectId: string | undefined;
91
+ if (isProjectContextInitialized()) {
92
+ try {
93
+ projectId = getProjectContext().project.tagId();
94
+ } catch {
95
+ // Project context not available - lesson will lack project scoping
96
+ }
97
+ }
98
+
99
+ await ragService.addDocuments("lessons", [
100
+ {
101
+ id: lessonEvent.encode(),
102
+ content: lessonContent,
103
+ metadata: {
104
+ title,
105
+ category,
106
+ hashtags: hashtags.length > 0 ? hashtags : undefined,
107
+ agentPubkey: context.agent.pubkey,
108
+ agentName: context.agent.name,
109
+ timestamp: Date.now(),
110
+ hasDetailed: !!detailed,
111
+ type: "lesson",
112
+ ...(projectId && { projectId }),
113
+ },
114
+ },
115
+ ]);
116
+
117
+ logger.info("Lesson added to RAG collection", {
118
+ title,
119
+ eventId: lessonEvent.encode(),
120
+ agentName: context.agent.name,
121
+ });
122
+ } catch (error) {
123
+ // Don't fail the tool if RAG integration fails
124
+ logger.warn("Failed to add lesson to RAG collection", { error, title });
125
+ }
126
+
127
+ const message = `Lesson recorded: "${title}"${detailed ? " (with detailed version)" : ""}\n\nThis lesson will be available in future conversations to help avoid similar issues.`;
128
+
129
+ return {
130
+ message,
131
+ eventId: lessonEvent.encode(),
132
+ title,
133
+ hasDetailed: !!detailed,
134
+ };
135
+ }
136
+
137
+ // AI SDK tool factory
138
+ export function createLessonLearnTool(context: ToolExecutionContext): AISdkTool {
139
+ return tool({
140
+ description: `Record new lessons and insights for future reference. Use for content ABOUT YOUR BEHAVIOR, such as:
141
+ - Patterns in how you approach tasks
142
+ - Debugging workflows that work well
143
+ - User preferences and communication styles
144
+ - Behavioral adjustments when user requests changes
145
+ - Performance patterns in your tool usage
146
+ - Things to do differently in future work
147
+
148
+ Use when the user instructs you to remember something about YOUR BEHAVIOR or PREFERENCES, or when the user instructs you to change some behavior. Lessons persist across conversations and help build institutional memory. Include both concise lesson and detailed explanation when complexity warrants it. Categorize and tag appropriately for future discovery. Lessons become immediately available via lesson_get.
149
+
150
+ **CRITICAL:** Only use this for content ABOUT YOUR BEHAVIOR. For content about the project itself, use report_write instead.
151
+
152
+ **NEVER use for:**
153
+ - Project conventions or patterns
154
+ - Architecture documentation
155
+ - Technical specifications
156
+ - Design decisions
157
+ - API documentation
158
+ - Any content that would help others understand the project
159
+
160
+ See also: report_write (for project documentation)`,
161
+ inputSchema: lessonLearnSchema,
162
+ execute: async (input: LessonLearnInput) => {
163
+ return await executeLessonLearn(input, context);
164
+ },
165
+ }) as AISdkTool;
166
+ }
@@ -0,0 +1,38 @@
1
+ import type { NDKAgentLesson } from "@/events/NDKAgentLesson";
2
+
3
+ /**
4
+ * Standard output shape for lesson retrieval operations.
5
+ * Consolidates the lesson data structure to avoid duplication between lesson_get and lessons_list.
6
+ */
7
+ export type FormattedLessonOutput = {
8
+ eventId: string;
9
+ title: string;
10
+ lesson: string;
11
+ detailed?: string;
12
+ category?: string;
13
+ hashtags?: string[];
14
+ hasDetailed: boolean;
15
+ };
16
+
17
+ /**
18
+ * Formats a lesson into the standard output shape.
19
+ * Used by both lesson_get and lessons_list to ensure consistent formatting.
20
+ *
21
+ * @param lesson - The NDKAgentLesson to format
22
+ * @param eventId - The event ID (may differ from lesson.id if normalized)
23
+ * @returns Formatted lesson output
24
+ */
25
+ export function formatLessonOutput(
26
+ lesson: NDKAgentLesson,
27
+ eventId: string
28
+ ): FormattedLessonOutput {
29
+ return {
30
+ eventId,
31
+ title: lesson.title || "Untitled",
32
+ lesson: lesson.lesson || lesson.content,
33
+ detailed: lesson.detailed,
34
+ category: lesson.category,
35
+ hashtags: lesson.hashtags,
36
+ hasDetailed: !!lesson.detailed,
37
+ };
38
+ }
@@ -0,0 +1,164 @@
1
+ import type { AISdkTool, ToolExecutionContext } from "@/tools/types";
2
+ import { getProjectContext } from "@/services/projects";
3
+ import { RAGService } from "@/services/rag/RAGService";
4
+ import { createExpectedError } from "@/tools/utils";
5
+ import { logger } from "@/utils/logger";
6
+ import { normalizeLessonEventId } from "@/utils/nostr-entity-parser";
7
+ import { getNDK } from "@/nostr/ndkClient";
8
+ import { NDKEvent } from "@nostr-dev-kit/ndk";
9
+ import { tool } from "ai";
10
+ import { z } from "zod";
11
+
12
+ const lessonDeleteSchema = z.object({
13
+ eventId: z.string().describe(
14
+ "Nostr event ID of the lesson to delete. Supports full 64-char hex ID, 12-char hex prefix, or NIP-19 formats (note1.../nevent1...)."
15
+ ),
16
+ reason: z.string().optional().describe(
17
+ "Optional reason for deleting the lesson"
18
+ ),
19
+ });
20
+
21
+ type LessonDeleteInput = z.infer<typeof lessonDeleteSchema>;
22
+
23
+ interface LessonDeleteOutput {
24
+ success: boolean;
25
+ eventId: string;
26
+ title: string;
27
+ message: string;
28
+ }
29
+
30
+ // Type for expected error results
31
+ type LessonDeleteResult = LessonDeleteOutput | { type: "error-text"; text: string };
32
+
33
+ /**
34
+ * Core implementation of lesson deletion functionality.
35
+ * Uses NIP-09 event deletion to mark the lesson as deleted.
36
+ */
37
+ async function executeLessonDelete(
38
+ input: LessonDeleteInput,
39
+ context: ToolExecutionContext
40
+ ): Promise<LessonDeleteResult> {
41
+ const { eventId: inputEventId, reason } = input;
42
+
43
+ const projectContext = getProjectContext();
44
+
45
+ // Get all lessons for normalization and lookup
46
+ const allLessons = projectContext.getAllLessons();
47
+
48
+ // Normalize the input to a canonical 64-char hex ID
49
+ const normalizeResult = normalizeLessonEventId(inputEventId, allLessons);
50
+ if (!normalizeResult.success) {
51
+ return createExpectedError(normalizeResult.error);
52
+ }
53
+
54
+ const eventId = normalizeResult.eventId;
55
+
56
+ logger.info("🗑️ Agent deleting lesson", {
57
+ agent: context.agent.name,
58
+ agentPubkey: context.agent.pubkey,
59
+ inputEventId,
60
+ resolvedEventId: eventId,
61
+ reason,
62
+ conversationId: context.conversationId,
63
+ });
64
+
65
+ // Find the lesson by event ID - only allow deleting own lessons
66
+ const agentLessons = projectContext.getLessonsForAgent(context.agent.pubkey);
67
+ const lesson = agentLessons.find((l) => l.id === eventId);
68
+
69
+ if (!lesson) {
70
+ // Check if the lesson exists but belongs to another agent
71
+ const globalLesson = allLessons.find((l) => l.id === eventId);
72
+ if (globalLesson) {
73
+ return createExpectedError(
74
+ `Cannot delete lesson "${eventId}": You can only delete your own lessons.`
75
+ );
76
+ }
77
+ return createExpectedError(`No lesson found with event ID: "${eventId}"`);
78
+ }
79
+
80
+ const lessonTitle = lesson.title || "Untitled";
81
+
82
+ // Create NIP-09 deletion event manually and sign with agent's signer
83
+ // We can't use lesson.delete() directly because the lesson's NDK instance
84
+ // doesn't have the agent's signer attached
85
+ const ndk = getNDK();
86
+ const deleteEvent = new NDKEvent(ndk);
87
+ deleteEvent.kind = 5; // NIP-09 deletion event kind
88
+ deleteEvent.tags = [["e", eventId]]; // Reference the event being deleted
89
+ deleteEvent.content = reason || "";
90
+
91
+ // Sign with the agent's signer and publish
92
+ // Per error handling contract: unexpected failures should throw
93
+ await context.agent.sign(deleteEvent);
94
+ await deleteEvent.publish();
95
+
96
+ logger.info("✅ Lesson deletion event published", {
97
+ agent: context.agent.name,
98
+ agentPubkey: context.agent.pubkey,
99
+ eventId,
100
+ title: lessonTitle,
101
+ reason,
102
+ conversationId: context.conversationId,
103
+ });
104
+
105
+ // Remove from RAG collection
106
+ try {
107
+ const ragService = RAGService.getInstance();
108
+ await ragService.deleteDocumentById("lessons", lesson.encode());
109
+
110
+ logger.info("📚 Lesson removed from RAG collection", {
111
+ eventId,
112
+ title: lessonTitle,
113
+ agent: context.agent.name,
114
+ });
115
+ } catch (error) {
116
+ // Don't fail the tool if RAG cleanup fails - the lesson is already deleted
117
+ logger.warn("Failed to remove lesson from RAG collection", {
118
+ error,
119
+ eventId,
120
+ title: lessonTitle,
121
+ });
122
+ }
123
+
124
+ // Remove from project context cache (also triggers prompt recompilation)
125
+ projectContext.removeLesson(context.agent.pubkey, eventId);
126
+
127
+ return {
128
+ success: true,
129
+ eventId,
130
+ title: lessonTitle,
131
+ message: `Lesson "${lessonTitle}" has been deleted${reason ? ` (reason: ${reason})` : ""}.`,
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Create an AI SDK tool for deleting lessons
137
+ */
138
+ export function createLessonDeleteTool(context: ToolExecutionContext): AISdkTool {
139
+ const aiTool = tool({
140
+ description:
141
+ "Delete a lesson by its Nostr event ID. This creates a deletion event (NIP-09) " +
142
+ "to mark the lesson as deleted. Only lessons created by the calling agent can be deleted.\n\n" +
143
+ "Supports multiple ID formats:\n" +
144
+ "- Full 64-character hex IDs\n" +
145
+ "- 12-character hex prefixes\n" +
146
+ "- NIP-19 formats: note1..., nevent1...\n" +
147
+ "- nostr: prefixed versions of all the above\n\n" +
148
+ "Use when a lesson is no longer relevant, contains outdated information, or was recorded in error.",
149
+ inputSchema: lessonDeleteSchema,
150
+ execute: async (input: LessonDeleteInput) => {
151
+ return await executeLessonDelete(input, context);
152
+ },
153
+ });
154
+
155
+ Object.defineProperty(aiTool, "getHumanReadableContent", {
156
+ value: ({ eventId }: LessonDeleteInput) => {
157
+ return `Deleting lesson: ${eventId}`;
158
+ },
159
+ enumerable: false,
160
+ configurable: true,
161
+ });
162
+
163
+ return aiTool as AISdkTool;
164
+ }
@@ -0,0 +1,105 @@
1
+ import type { ToolExecutionContext } from "@/tools/types";
2
+ import { getProjectContext } from "@/services/projects";
3
+ import type { AISdkTool } from "@/tools/types";
4
+ import { createExpectedError } from "@/tools/utils";
5
+ import { logger } from "@/utils/logger";
6
+ import { normalizeLessonEventId } from "@/utils/nostr-entity-parser";
7
+ import { formatLessonOutput, type FormattedLessonOutput } from "./lesson-formatter";
8
+ import { tool } from "ai";
9
+ import { z } from "zod";
10
+
11
+ const lessonGetSchema = z.object({
12
+ eventId: z.string().describe("Nostr event ID of the lesson to retrieve. Supports full 64-char hex ID, 12-char hex prefix, or NIP-19 formats (note1.../nevent1...)."),
13
+ });
14
+
15
+ type LessonGetInput = z.infer<typeof lessonGetSchema>;
16
+
17
+ // Type for expected error results
18
+ type LessonGetResult = FormattedLessonOutput | { type: "error-text"; text: string };
19
+
20
+ // Core implementation - fetches lesson by event ID
21
+ // Returns error-text for expected "not found" conditions
22
+ async function executeLessonGet(
23
+ input: LessonGetInput,
24
+ context: ToolExecutionContext
25
+ ): Promise<LessonGetResult> {
26
+ const { eventId: inputEventId } = input;
27
+
28
+ const projectContext = getProjectContext();
29
+
30
+ // Get all lessons upfront for both normalization fallback and lookup
31
+ const allLessons = projectContext.getAllLessons();
32
+
33
+ // Normalize the input to a canonical 64-char hex ID
34
+ // Supports: hex IDs, hex prefixes, note1..., nevent1..., nostr: prefixed versions
35
+ const normalizeResult = normalizeLessonEventId(inputEventId, allLessons);
36
+ if (!normalizeResult.success) {
37
+ // Invalid event ID format is an expected user error - return error-text
38
+ return createExpectedError(normalizeResult.error);
39
+ }
40
+
41
+ const eventId = normalizeResult.eventId;
42
+
43
+ logger.info("📖 Agent retrieving lesson by event ID", {
44
+ agent: context.agent.name,
45
+ agentPubkey: context.agent.pubkey,
46
+ inputEventId,
47
+ resolvedEventId: eventId,
48
+ conversationId: context.conversationId,
49
+ });
50
+
51
+ // Find lesson by event ID - first try agent-specific, then global
52
+ const agentLessons = projectContext.getLessonsForAgent(context.agent.pubkey);
53
+ let lesson = agentLessons.find((l) => l.id === eventId);
54
+ let isGlobalMatch = false;
55
+
56
+ if (!lesson) {
57
+ // Fall back to global search (in case agent pubkey doesn't match exactly)
58
+ lesson = allLessons.find((l) => l.id === eventId);
59
+ isGlobalMatch = true;
60
+ }
61
+
62
+ if (!lesson) {
63
+ // "Lesson not found" is an expected condition - return error-text
64
+ return createExpectedError(`No lesson found with event ID: "${eventId}"`);
65
+ }
66
+
67
+ logger.info("✅ Successfully retrieved lesson", {
68
+ agent: context.agent.name,
69
+ agentPubkey: context.agent.pubkey,
70
+ eventId,
71
+ title: lesson.title,
72
+ hasDetailed: !!lesson.detailed,
73
+ source: isGlobalMatch ? "global" : "agent",
74
+ conversationId: context.conversationId,
75
+ });
76
+
77
+ return formatLessonOutput(lesson, eventId);
78
+ }
79
+
80
+ // AI SDK tool factory
81
+ export function createLessonGetTool(context: ToolExecutionContext): AISdkTool {
82
+ const aiTool = tool({
83
+ description:
84
+ "Retrieve a lesson by its Nostr event ID. Supports multiple formats:\n" +
85
+ "- Full 64-character hex IDs\n" +
86
+ "- 12-character hex prefixes\n" +
87
+ "- NIP-19 formats: note1..., nevent1...\n" +
88
+ "- nostr: prefixed versions of all the above\n\n" +
89
+ "Returns full lesson content including detailed explanations if available. Use when you need to recall specific knowledge or patterns that have been previously documented.",
90
+ inputSchema: lessonGetSchema,
91
+ execute: async (input: LessonGetInput) => {
92
+ return await executeLessonGet(input, context);
93
+ },
94
+ });
95
+
96
+ Object.defineProperty(aiTool, "getHumanReadableContent", {
97
+ value: ({ eventId }: LessonGetInput) => {
98
+ return `Reading lesson: ${eventId}`;
99
+ },
100
+ enumerable: false,
101
+ configurable: true,
102
+ });
103
+
104
+ return aiTool as AISdkTool;
105
+ }
@@ -0,0 +1,153 @@
1
+ import type { AISdkTool, ToolExecutionContext } from "@/tools/types";
2
+ import { getProjectContext } from "@/services/projects";
3
+ import { logger } from "@/utils/logger";
4
+ import { createExpectedError } from "@/tools/utils";
5
+ import { formatLessonOutput } from "./lesson-formatter";
6
+ import { tool } from "ai";
7
+ import { z } from "zod";
8
+
9
+ const lessonsListSchema = z.object({
10
+ agentPubkey: z
11
+ .string()
12
+ .optional()
13
+ .describe(
14
+ "Optional agent pubkey to filter lessons by. If not provided, returns only your own lessons."
15
+ ),
16
+ });
17
+
18
+ type LessonsListInput = z.infer<typeof lessonsListSchema>;
19
+
20
+ type LessonSummary = {
21
+ eventId: string;
22
+ title: string;
23
+ lesson: string;
24
+ category?: string;
25
+ hashtags?: string[];
26
+ author: string; // Agent slug or pubkey fallback
27
+ hasDetailed: boolean;
28
+ createdAt?: number;
29
+ };
30
+
31
+ type LessonsListOutput = {
32
+ success: boolean;
33
+ lessons: LessonSummary[];
34
+ totalCount: number;
35
+ agentFilter?: string;
36
+ };
37
+
38
+ type LessonsListResult = LessonsListOutput | { type: "error-text"; text: string };
39
+
40
+ /**
41
+ * Normalizes a pubkey string: trims whitespace and lowercases.
42
+ * Returns the normalized value, or undefined if input is empty/whitespace.
43
+ */
44
+ function normalizePubkey(pubkey: string | undefined): string | undefined {
45
+ if (!pubkey) return undefined;
46
+ const trimmed = pubkey.trim();
47
+ if (trimmed === "") return undefined;
48
+ return trimmed.toLowerCase();
49
+ }
50
+
51
+ /**
52
+ * Validates that a string is a valid hex pubkey (64 hex characters).
53
+ * Returns null if valid, error message if invalid.
54
+ * Expects pre-normalized input (trimmed, lowercased).
55
+ */
56
+ function validateAgentPubkey(pubkey: string, originalInput: string): string | null {
57
+ // Must be exactly 64 hex characters (already lowercased)
58
+ if (!/^[0-9a-f]{64}$/.test(pubkey)) {
59
+ return `Invalid agent pubkey format: "${originalInput}". Expected 64-character hex string`;
60
+ }
61
+
62
+ return null;
63
+ }
64
+
65
+ /**
66
+ * Core implementation - list lessons for the project
67
+ */
68
+ async function executeLessonsList(
69
+ input: LessonsListInput,
70
+ context: ToolExecutionContext
71
+ ): Promise<LessonsListResult> {
72
+ const { agentPubkey } = input;
73
+
74
+ // Normalize the input pubkey once - trim whitespace and lowercase
75
+ const normalizedInputPubkey = normalizePubkey(agentPubkey);
76
+
77
+ // Validate agentPubkey if provided (after normalization)
78
+ if (agentPubkey !== undefined) {
79
+ // Check if normalization resulted in empty (whitespace-only input)
80
+ if (normalizedInputPubkey === undefined) {
81
+ return createExpectedError("Agent pubkey cannot be empty");
82
+ }
83
+ const validationError = validateAgentPubkey(normalizedInputPubkey, agentPubkey);
84
+ if (validationError) {
85
+ return createExpectedError(validationError);
86
+ }
87
+ }
88
+
89
+ // Determine effective pubkey: use normalized input or default to calling agent
90
+ const effectivePubkey = normalizedInputPubkey ?? context.agent.pubkey;
91
+
92
+ logger.info("📚 Listing lessons", {
93
+ agent: context.agent.name,
94
+ agentFilter: effectivePubkey,
95
+ conversationId: context.conversationId,
96
+ });
97
+
98
+ const projectContext = getProjectContext();
99
+
100
+ // Get lessons based on filter
101
+ const rawLessons = projectContext.getLessonsForAgent(effectivePubkey);
102
+
103
+ // Transform lessons to summary format
104
+ const lessons: LessonSummary[] = rawLessons.map((lesson) => {
105
+ // Try to resolve author pubkey to agent slug (matching reports_list pattern)
106
+ const agent = projectContext.getAgentByPubkey(lesson.pubkey);
107
+ const authorSlug = agent?.slug ?? lesson.pubkey;
108
+
109
+ // Use shared formatter for consistent lesson formatting
110
+ const formatted = formatLessonOutput(lesson, lesson.id);
111
+
112
+ return {
113
+ ...formatted,
114
+ author: authorSlug,
115
+ createdAt: lesson.created_at,
116
+ };
117
+ });
118
+
119
+ // Sort by creation date, most recent first
120
+ lessons.sort((a, b) => (b.createdAt || 0) - (a.createdAt || 0));
121
+
122
+ logger.info("✅ Lessons listed successfully", {
123
+ total: lessons.length,
124
+ agent: context.agent.name,
125
+ agentFilter: effectivePubkey,
126
+ conversationId: context.conversationId,
127
+ });
128
+
129
+ return {
130
+ success: true,
131
+ lessons,
132
+ totalCount: lessons.length,
133
+ agentFilter: effectivePubkey,
134
+ };
135
+ }
136
+
137
+ /**
138
+ * AI SDK tool factory
139
+ */
140
+ export function createLessonsListTool(context: ToolExecutionContext): AISdkTool {
141
+ return tool({
142
+ description:
143
+ "List lessons learned by agents in the project. Returns only your own lessons by default, " +
144
+ "including title, full lesson content, category, hashtags, and author (agent slug). " +
145
+ "Optionally filter by agent pubkey to see lessons from a specific agent. " +
146
+ "Results are sorted by creation date (most recent first). Use this to discover what lessons are available. " +
147
+ "For lessons with detailed explanations, use lesson_get to retrieve the full detailed content.",
148
+ inputSchema: lessonsListSchema,
149
+ execute: async (input: LessonsListInput) => {
150
+ return await executeLessonsList(input, context);
151
+ },
152
+ }) as AISdkTool;
153
+ }