@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,611 @@
1
+ import * as crypto from "node:crypto";
2
+ import * as fs from "node:fs/promises";
3
+ import * as path from "node:path";
4
+ import type { AISdkTool, ToolExecutionContext } from "@/tools/types";
5
+ import { getProjectContext } from "@/services/projects";
6
+ import { config } from "@/services/ConfigService";
7
+ import { NDKAgentDefinition } from "@/events/NDKAgentDefinition";
8
+ import { getNDK } from "@/nostr/ndkClient";
9
+ import { Nip46SigningService } from "@/services/nip46";
10
+ import { agentStorage } from "@/agents/AgentStorage";
11
+ import { logger } from "@/utils/logger";
12
+ import { NDKEvent, NDKNip46Signer, NDKPrivateKeySigner, type NDKSigner } from "@nostr-dev-kit/ndk";
13
+ import { tool } from "ai";
14
+ import { z } from "zod";
15
+
16
+ const MAX_FILE_SIZE_BYTES = 50 * 1024 * 1024; // 50 MB
17
+ const UPLOAD_TIMEOUT_MS = 60_000;
18
+ const NIP46_CONNECT_TIMEOUT_MS = 30_000; // 30 seconds for NIP-46 connection
19
+ const NIP46_SIGNING_TIMEOUT_MS = 120_000; // 2 minutes for NIP-46 remote signing
20
+
21
+ const fileReferenceSchema = z.object({
22
+ path: z.string().describe("Absolute path to the file on disk"),
23
+ name: z.string().describe("Filename/relative path for the agent's home directory (used in the kind:1063 name tag)"),
24
+ });
25
+
26
+ const agentsPublishSchema = z.object({
27
+ slug: z.string().describe("The slug identifier of the agent to publish"),
28
+ description: z.string().describe("Short one-line description of the agent definition"),
29
+ category: z.string().describe("Category for the agent (e.g., 'developer', 'analyst', 'assistant')"),
30
+ rich_description: z.string().describe("Comprehensive homepage-style description of what the agent definition is all about (markdown). This becomes the event content."),
31
+ image: z
32
+ .string()
33
+ .url("image must be a valid URL")
34
+ .optional()
35
+ .describe("Optional image URL for the agent definition. Published as an 'image' tag in the kind:4199 event."),
36
+ publish_as_user: z
37
+ .boolean()
38
+ .default(true)
39
+ .describe(
40
+ "When true (default), the kind:4199 event is signed by the project owner via NIP-46 remote signing " +
41
+ "(the agent sends the signing request). When false, the event is signed with the TENEX backend key."
42
+ ),
43
+ files: z
44
+ .array(fileReferenceSchema)
45
+ .optional()
46
+ .describe(
47
+ "Optional array of files to bundle with the agent. Each file will be uploaded to Blossom, " +
48
+ "a kind:1063 NIP-94 event created, and an e-tag added to the agent definition."
49
+ ),
50
+ });
51
+
52
+ type AgentsPublishInput = z.infer<typeof agentsPublishSchema>;
53
+
54
+ interface BlossomConfig {
55
+ serverUrl: string;
56
+ }
57
+
58
+ interface UploadResult {
59
+ url: string;
60
+ sha256: string;
61
+ size: number;
62
+ type?: string;
63
+ }
64
+
65
+ interface FileMetadataEvent {
66
+ eventId: string;
67
+ name: string;
68
+ url: string;
69
+ sha256: string;
70
+ }
71
+
72
+ /**
73
+ * Get Blossom server configuration from global config
74
+ */
75
+ async function getBlossomConfig(): Promise<BlossomConfig> {
76
+ try {
77
+ const tenexConfig = await config.loadTenexConfig(config.getGlobalPath());
78
+ return {
79
+ serverUrl: tenexConfig.blossomServerUrl || "https://blossom.primal.net",
80
+ };
81
+ } catch {
82
+ return {
83
+ serverUrl: "https://blossom.primal.net",
84
+ };
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Detect MIME type from file extension
90
+ */
91
+ function detectMimeType(filePath: string): string {
92
+ const ext = path.extname(filePath).toLowerCase();
93
+ const mimeTypes: Record<string, string> = {
94
+ ".jpg": "image/jpeg",
95
+ ".jpeg": "image/jpeg",
96
+ ".png": "image/png",
97
+ ".gif": "image/gif",
98
+ ".webp": "image/webp",
99
+ ".mp4": "video/mp4",
100
+ ".mov": "video/quicktime",
101
+ ".avi": "video/x-msvideo",
102
+ ".webm": "video/webm",
103
+ ".mp3": "audio/mpeg",
104
+ ".wav": "audio/wav",
105
+ ".pdf": "application/pdf",
106
+ ".json": "application/json",
107
+ ".txt": "text/plain",
108
+ ".md": "text/markdown",
109
+ ".js": "text/javascript",
110
+ ".ts": "text/typescript",
111
+ ".py": "text/x-python",
112
+ ".sh": "text/x-shellscript",
113
+ ".rb": "text/x-ruby",
114
+ ".pl": "text/x-perl",
115
+ };
116
+ return mimeTypes[ext] || "application/octet-stream";
117
+ }
118
+
119
+ /**
120
+ * Calculate SHA256 hash of data
121
+ */
122
+ function calculateSHA256(data: Buffer): string {
123
+ return crypto.createHash("sha256").update(data).digest("hex");
124
+ }
125
+
126
+ /**
127
+ * Create Blossom authorization event (kind 24242)
128
+ */
129
+ async function createBlossomAuthEvent(
130
+ sha256Hash: string,
131
+ description: string,
132
+ signer: NDKSigner
133
+ ): Promise<NDKEvent> {
134
+ const event = new NDKEvent();
135
+ event.kind = 24242;
136
+ event.content = description;
137
+ event.created_at = Math.floor(Date.now() / 1000);
138
+ event.tags = [
139
+ ["t", "upload"],
140
+ ["x", sha256Hash],
141
+ ["expiration", String(Math.floor(Date.now() / 1000) + 3600)], // 1 hour expiration
142
+ ];
143
+
144
+ await event.sign(signer);
145
+ return event;
146
+ }
147
+
148
+ /**
149
+ * Upload data to Blossom server
150
+ */
151
+ async function uploadToBlossomServer(
152
+ serverUrl: string,
153
+ data: Buffer,
154
+ mimeType: string,
155
+ authEvent: NDKEvent
156
+ ): Promise<UploadResult> {
157
+ const authHeader = `Nostr ${Buffer.from(JSON.stringify(authEvent.rawEvent())).toString("base64")}`;
158
+
159
+ const controller = new AbortController();
160
+ const timeout = setTimeout(() => controller.abort(), UPLOAD_TIMEOUT_MS);
161
+
162
+ try {
163
+ const response = await fetch(`${serverUrl}/upload`, {
164
+ method: "PUT",
165
+ headers: {
166
+ Authorization: authHeader,
167
+ "Content-Type": mimeType,
168
+ "Content-Length": String(data.length),
169
+ },
170
+ body: data,
171
+ signal: controller.signal,
172
+ });
173
+
174
+ if (!response.ok) {
175
+ let errorMessage = `Upload failed with status ${response.status}`;
176
+ try {
177
+ const errorData = (await response.json()) as { message?: string };
178
+ if (errorData.message) {
179
+ errorMessage = `Upload failed: ${errorData.message}`;
180
+ }
181
+ } catch {
182
+ // If parsing JSON fails, use the default error message
183
+ }
184
+ throw new Error(errorMessage);
185
+ }
186
+
187
+ const result = (await response.json()) as UploadResult;
188
+ return result;
189
+ } catch (error) {
190
+ if (error instanceof Error && error.name === "AbortError") {
191
+ throw new Error(`Upload timed out after ${UPLOAD_TIMEOUT_MS}ms`, { cause: error });
192
+ }
193
+ throw error;
194
+ } finally {
195
+ clearTimeout(timeout);
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Create a kind:1063 NIP-94 file metadata event
201
+ */
202
+ async function createFileMetadataEvent(
203
+ url: string,
204
+ sha256: string,
205
+ name: string,
206
+ mimeType: string,
207
+ size: number,
208
+ signer: NDKSigner,
209
+ ndk: ReturnType<typeof getNDK>
210
+ ): Promise<FileMetadataEvent> {
211
+ const event = new NDKEvent(ndk);
212
+ event.kind = 1063;
213
+ event.content = "";
214
+ event.created_at = Math.floor(Date.now() / 1000);
215
+ event.tags = [
216
+ ["url", url],
217
+ ["name", name],
218
+ ["m", mimeType],
219
+ ["x", sha256],
220
+ ["size", String(size)],
221
+ ];
222
+
223
+ await event.sign(signer);
224
+ await event.publish();
225
+
226
+ logger.info(`Published kind:1063 file metadata event`, {
227
+ eventId: event.id,
228
+ name,
229
+ url,
230
+ });
231
+
232
+ return {
233
+ eventId: event.id,
234
+ name,
235
+ url,
236
+ sha256,
237
+ };
238
+ }
239
+
240
+ /**
241
+ * Upload a file to Blossom and create the kind:1063 metadata event
242
+ */
243
+ async function uploadFileAndCreateMetadata(
244
+ filePath: string,
245
+ name: string,
246
+ signer: NDKSigner,
247
+ ndk: ReturnType<typeof getNDK>
248
+ ): Promise<FileMetadataEvent> {
249
+ // Validate file exists
250
+ try {
251
+ await fs.access(filePath);
252
+ } catch {
253
+ throw new Error(`File not found: ${filePath}`);
254
+ }
255
+
256
+ // Check file size
257
+ const stats = await fs.stat(filePath);
258
+ if (stats.size > MAX_FILE_SIZE_BYTES) {
259
+ throw new Error(
260
+ `File size ${stats.size} bytes exceeds limit of ${MAX_FILE_SIZE_BYTES} bytes`
261
+ );
262
+ }
263
+
264
+ // Read file
265
+ const data = await fs.readFile(filePath);
266
+ const mimeType = detectMimeType(filePath);
267
+ const sha256 = calculateSHA256(data);
268
+
269
+ logger.info(`Uploading file to Blossom`, {
270
+ path: filePath,
271
+ name,
272
+ size: data.length,
273
+ mimeType,
274
+ });
275
+
276
+ // Get Blossom config and upload
277
+ const blossomConfig = await getBlossomConfig();
278
+ const authEvent = await createBlossomAuthEvent(sha256, `Upload ${name}`, signer);
279
+ const uploadResult = await uploadToBlossomServer(
280
+ blossomConfig.serverUrl,
281
+ data,
282
+ mimeType,
283
+ authEvent
284
+ );
285
+
286
+ logger.info(`File uploaded to Blossom`, {
287
+ name,
288
+ url: uploadResult.url,
289
+ sha256: uploadResult.sha256,
290
+ });
291
+
292
+ // Create kind:1063 event
293
+ const metadataEvent = await createFileMetadataEvent(
294
+ uploadResult.url,
295
+ uploadResult.sha256,
296
+ name,
297
+ mimeType,
298
+ data.length,
299
+ signer,
300
+ ndk
301
+ );
302
+
303
+ return metadataEvent;
304
+ }
305
+
306
+ /**
307
+ * Create an NDKNip46Signer using the agent's private key as the local signer
308
+ * and the owner's pubkey as the remote signer target.
309
+ * Returns the signer after connecting (with timeout).
310
+ */
311
+ async function createAgentNip46Signer(
312
+ agentSigner: NDKPrivateKeySigner,
313
+ ownerPubkey: string,
314
+ ): Promise<NDKNip46Signer> {
315
+ const ndk = getNDK();
316
+ const nip46Service = Nip46SigningService.getInstance();
317
+ const bunkerUri = nip46Service.getBunkerUri(ownerPubkey);
318
+
319
+ if (!bunkerUri || !bunkerUri.startsWith("bunker://")) {
320
+ throw new Error(
321
+ `Invalid bunker URI for owner ${ownerPubkey.substring(0, 12)}: ` +
322
+ `expected a "bunker://" URI but got "${bunkerUri || "(empty)"}"`
323
+ );
324
+ }
325
+
326
+ logger.info("[agents_publish] Creating NIP-46 signer for remote signing", {
327
+ ownerPubkey: ownerPubkey.substring(0, 12),
328
+ bunkerUri: bunkerUri.substring(0, 60),
329
+ });
330
+
331
+ const signer = NDKNip46Signer.bunker(ndk, bunkerUri, agentSigner);
332
+
333
+ signer.on("authUrl", (url: string) => {
334
+ logger.info("[agents_publish] NIP-46 auth URL required", {
335
+ ownerPubkey: ownerPubkey.substring(0, 12),
336
+ url,
337
+ });
338
+ });
339
+
340
+ // Connect with timeout — cancel timer on success to avoid leaked handles
341
+ let connectTimer: ReturnType<typeof setTimeout> | undefined;
342
+ try {
343
+ await Promise.race([
344
+ signer.blockUntilReady(),
345
+ new Promise<never>((_, reject) => {
346
+ connectTimer = setTimeout(
347
+ () => reject(new Error(
348
+ `NIP-46 connect timed out after ${NIP46_CONNECT_TIMEOUT_MS / 1000}s`
349
+ )),
350
+ NIP46_CONNECT_TIMEOUT_MS,
351
+ );
352
+ }),
353
+ ]);
354
+ } catch (error) {
355
+ // On timeout or connection failure, close the signer to release relay subscriptions
356
+ try { signer.stop(); } catch { /* best-effort cleanup */ }
357
+ throw error;
358
+ } finally {
359
+ clearTimeout(connectTimer);
360
+ }
361
+
362
+ return signer;
363
+ }
364
+
365
+ /**
366
+ * Publishes an agent definition (kind 4199) to Nostr.
367
+ *
368
+ * When `publish_as_user` is true, uses NIP-46 remote signing so the event
369
+ * is signed by the project owner (the agent acts as the NIP-46 client).
370
+ * When false, signs with the TENEX backend key.
371
+ *
372
+ * Optionally uploads files and references them via e-tags.
373
+ * Returns the event ID on success.
374
+ */
375
+ async function executeAgentsPublish(
376
+ input: AgentsPublishInput,
377
+ context: ToolExecutionContext,
378
+ ): Promise<string> {
379
+ const { slug, description, category, rich_description, image, publish_as_user, files } = input;
380
+
381
+ if (!slug) {
382
+ throw new Error("Agent slug is required");
383
+ }
384
+
385
+ const projectCtx = getProjectContext();
386
+ const agent = projectCtx.getAgent(slug);
387
+
388
+ if (!agent) {
389
+ throw new Error(`Agent with slug "${slug}" not found in current project`);
390
+ }
391
+
392
+ const ndk = getNDK();
393
+
394
+ // Backend signer is only needed when NOT publishing as user (backend signs the 4199 event).
395
+ // File uploads are always signed by the agent's own signer when publish_as_user=true.
396
+ const backendSigner = !publish_as_user ? await config.getBackendSigner() : undefined;
397
+
398
+ // Choose the signer for file uploads: agent's own signer when publishing as user, backend signer otherwise
399
+ const fileSigner: NDKSigner = publish_as_user ? context.agent.signer : backendSigner!;
400
+
401
+ // Upload files and create kind:1063 events if provided
402
+ const fileMetadataEvents: FileMetadataEvent[] = [];
403
+ if (files && files.length > 0) {
404
+ logger.info(`Uploading ${files.length} file(s) for agent "${agent.name}"`);
405
+
406
+ for (const file of files) {
407
+ try {
408
+ const metadata = await uploadFileAndCreateMetadata(
409
+ file.path,
410
+ file.name,
411
+ fileSigner,
412
+ ndk
413
+ );
414
+ fileMetadataEvents.push(metadata);
415
+ } catch (error) {
416
+ const errorMessage = error instanceof Error ? error.message : String(error);
417
+ logger.error(`Failed to upload file: ${file.path}`, { error: errorMessage });
418
+ throw new Error(`Failed to upload file "${file.name}": ${errorMessage}`);
419
+ }
420
+ }
421
+ }
422
+
423
+ // Create the agent definition event
424
+ const agentDefinition = new NDKAgentDefinition(ndk);
425
+
426
+ agentDefinition.title = agent.name;
427
+ agentDefinition.role = agent.role;
428
+ agentDefinition.description = description;
429
+ agentDefinition.category = category;
430
+ // Rich description goes into event content, not a tag
431
+ agentDefinition.content = rich_description;
432
+
433
+ if (image) {
434
+ agentDefinition.removeTag("image");
435
+ agentDefinition.tags.push(["image", image]);
436
+ }
437
+
438
+ if (agent.instructions) {
439
+ agentDefinition.instructions = agent.instructions;
440
+ }
441
+
442
+ if (agent.useCriteria) {
443
+ agentDefinition.useCriteria = agent.useCriteria;
444
+ }
445
+
446
+ agentDefinition.version = 1;
447
+
448
+ agentDefinition.slug = slug;
449
+
450
+ // Add e-tags for each file metadata event
451
+ for (const fileMetadata of fileMetadataEvents) {
452
+ agentDefinition.tags.push(["e", fileMetadata.eventId]);
453
+ }
454
+
455
+ // Determine signing strategy
456
+ if (publish_as_user) {
457
+ // NIP-46 remote signing: the agent sends the signing request to the owner
458
+ const nip46Service = Nip46SigningService.getInstance();
459
+
460
+ if (!nip46Service.isEnabled()) {
461
+ throw new Error(
462
+ "Cannot publish as user: NIP-46 remote signing is not enabled in the configuration. " +
463
+ "Enable it by setting nip46.enabled=true in your TENEX config, or set publish_as_user=false " +
464
+ "to publish with the backend signer instead."
465
+ );
466
+ }
467
+
468
+ const ownerPubkey = projectCtx?.project?.pubkey;
469
+ if (!ownerPubkey) {
470
+ throw new Error(
471
+ "Cannot publish as user: no project owner pubkey found. " +
472
+ "Set publish_as_user=false to publish with the backend signer instead."
473
+ );
474
+ }
475
+
476
+ // Runtime type guard: verify agent signer is NDKPrivateKeySigner (required for NIP-46 local key)
477
+ const agentSigner: unknown = context.agent.signer;
478
+ if (!(agentSigner instanceof NDKPrivateKeySigner)) {
479
+ throw new Error(
480
+ `Expected agent signer to be NDKPrivateKeySigner for NIP-46 signing, ` +
481
+ `but got ${(agentSigner as { constructor?: { name?: string } })?.constructor?.name ?? "undefined"}. ` +
482
+ `Agent "${slug}" may have an incompatible signer configuration.`
483
+ );
484
+ }
485
+
486
+ logger.info(`Publishing agent definition as user via NIP-46`, {
487
+ slug,
488
+ ownerPubkey: ownerPubkey.substring(0, 12),
489
+ agentPubkey: context.agent.pubkey.substring(0, 12),
490
+ });
491
+
492
+ // Create NIP-46 signer using the agent's own signer as local key
493
+ const nip46Signer = await createAgentNip46Signer(
494
+ context.agent.signer,
495
+ ownerPubkey,
496
+ );
497
+
498
+ // Set pubkey to the owner's before signing
499
+ agentDefinition.pubkey = ownerPubkey;
500
+
501
+ // Sign with NIP-46 (with timeout) — cancel timer and close signer on failure
502
+ let signingTimer: ReturnType<typeof setTimeout> | undefined;
503
+ try {
504
+ await Promise.race([
505
+ agentDefinition.sign(nip46Signer, { pTags: false }),
506
+ new Promise<never>((_, reject) => {
507
+ signingTimer = setTimeout(
508
+ () => reject(new Error(
509
+ `NIP-46 remote signing timed out after ${NIP46_SIGNING_TIMEOUT_MS / 1000}s`
510
+ )),
511
+ NIP46_SIGNING_TIMEOUT_MS,
512
+ );
513
+ }),
514
+ ]);
515
+ } catch (error) {
516
+ try { nip46Signer.stop(); } catch { /* best-effort cleanup */ }
517
+ throw error;
518
+ } finally {
519
+ clearTimeout(signingTimer);
520
+ }
521
+
522
+ try {
523
+ await agentDefinition.publish();
524
+ } finally {
525
+ // Clean up the NIP-46 signer whether publish succeeds or fails
526
+ try { nip46Signer.stop(); } catch { /* best-effort cleanup */ }
527
+ }
528
+
529
+ logger.info(`Successfully published user-signed agent definition for "${agent.name}" (${slug})`, {
530
+ eventId: agentDefinition.id,
531
+ pubkey: ownerPubkey,
532
+ signerType: "nip46",
533
+ filesAttached: fileMetadataEvents.length,
534
+ });
535
+ } else {
536
+ // Backend signer (original behavior)
537
+ agentDefinition.pubkey = backendSigner!.pubkey;
538
+
539
+ await agentDefinition.sign(backendSigner!, { pTags: false });
540
+ await agentDefinition.publish();
541
+
542
+ logger.info(`Successfully published backend-signed agent definition for "${agent.name}" (${slug})`, {
543
+ eventId: agentDefinition.id,
544
+ pubkey: backendSigner!.pubkey,
545
+ signerType: "backend",
546
+ filesAttached: fileMetadataEvents.length,
547
+ });
548
+ }
549
+
550
+ // Sync published metadata back to local storage so the AgentDefinitionMonitor
551
+ // can track this agent for future updates (needs definitionDTag + definitionAuthor).
552
+ // Use the published agent's pubkey (resolved by slug), NOT context.agent.pubkey
553
+ // which is the *calling* agent — they may differ when one agent publishes another.
554
+ try {
555
+ const storedAgent = await agentStorage.loadAgent(agent.pubkey);
556
+ if (storedAgent) {
557
+ storedAgent.eventId = agentDefinition.id;
558
+ storedAgent.definitionDTag = agentDefinition.tagValue("d") ?? slug;
559
+ storedAgent.definitionAuthor = agentDefinition.pubkey;
560
+ storedAgent.definitionCreatedAt = agentDefinition.created_at;
561
+ await agentStorage.saveAgent(storedAgent);
562
+
563
+ logger.info("[agents_publish] Synced publish metadata to local storage", {
564
+ slug,
565
+ eventId: agentDefinition.id?.substring(0, 12),
566
+ definitionDTag: storedAgent.definitionDTag,
567
+ definitionAuthor: storedAgent.definitionAuthor?.substring(0, 12),
568
+ });
569
+ } else {
570
+ logger.warn("[agents_publish] Could not find stored agent to sync publish metadata", {
571
+ slug,
572
+ pubkey: agent.pubkey?.substring(0, 12),
573
+ });
574
+ }
575
+ } catch (error) {
576
+ // Never block publish success due to a storage sync failure
577
+ logger.error("[agents_publish] Failed to sync publish metadata to local storage", {
578
+ slug,
579
+ error: error instanceof Error ? error.message : String(error),
580
+ });
581
+ }
582
+
583
+ return agentDefinition.id;
584
+ }
585
+
586
+ /**
587
+ * Create an AI SDK tool for publishing agent definitions
588
+ */
589
+ export function createAgentsPublishTool(context: ToolExecutionContext): AISdkTool {
590
+ return tool({
591
+ description:
592
+ "Publish an agent definition (kind 4199) to Nostr. " +
593
+ "By default, the event is signed by the project owner via NIP-46 remote signing " +
594
+ "(the agent sends the signing request). Set publish_as_user=false to sign with the TENEX backend key instead. " +
595
+ "Optionally include an image URL and/or an array of files to bundle with the agent. " +
596
+ "Each file is uploaded to Blossom, a kind:1063 NIP-94 event is created, and an e-tag " +
597
+ "is added to the agent definition referencing the file. Returns the event ID on success.",
598
+ inputSchema: agentsPublishSchema,
599
+ execute: async (input: AgentsPublishInput) => {
600
+ try {
601
+ return await executeAgentsPublish(input, context);
602
+ } catch (error) {
603
+ logger.error("Failed to publish agent definition", { error });
604
+ throw new Error(
605
+ `Failed to publish agent definition: ${error instanceof Error ? error.message : String(error)}`,
606
+ { cause: error }
607
+ );
608
+ }
609
+ },
610
+ }) as AISdkTool;
611
+ }