@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,657 @@
1
+ import * as crypto from "node:crypto";
2
+ import { agentStorage } from "@/agents/AgentStorage";
3
+ import { NDKKind } from "@/nostr/kinds";
4
+ import { getNDK } from "@/nostr/ndkClient";
5
+ import { config } from "@/services/ConfigService";
6
+ import { Nip46SigningService, Nip46SigningLog } from "@/services/nip46";
7
+ import { logger } from "@/utils/logger";
8
+ import {
9
+ NDKEvent,
10
+ NDKPrivateKeySigner,
11
+ type NDKProject,
12
+ } from "@nostr-dev-kit/ndk";
13
+
14
+ /**
15
+ * Publishes Nostr events for agent profiles and creation.
16
+ * Separated from AgentPublisher to handle agent setup vs runtime publishing.
17
+ */
18
+ export class AgentProfilePublisher {
19
+ /** Timeout in milliseconds for publish operations */
20
+ private static readonly PUBLISH_TIMEOUT_MS = 5000;
21
+
22
+ /** Avatar style families for deterministic avatar selection */
23
+ private static readonly AVATAR_FAMILIES = [
24
+ "lorelei",
25
+ "miniavs",
26
+ "dylan",
27
+ "pixel-art",
28
+ "rings",
29
+ "avataaars",
30
+ ];
31
+
32
+ /**
33
+ * Builds a deterministic avatar URL based on the pubkey.
34
+ * Uses DiceBear API with a family selected based on pubkey hash.
35
+ */
36
+ private static buildAvatarUrl(pubkey: string): string {
37
+ const familyIndex =
38
+ Number.parseInt(pubkey.substring(0, 8), 16) % AgentProfilePublisher.AVATAR_FAMILIES.length;
39
+ const avatarStyle = AgentProfilePublisher.AVATAR_FAMILIES[familyIndex];
40
+ return `https://api.dicebear.com/7.x/${avatarStyle}/png?seed=${pubkey}`;
41
+ }
42
+ /** Per-project debounce timers for 14199 snapshot publishing */
43
+ private static snapshotDebounceTimers: Map<string, ReturnType<typeof setTimeout>> = new Map();
44
+ private static readonly SNAPSHOT_DEBOUNCE_MS = 5000;
45
+
46
+ /**
47
+ * Schedule a kind:14199 snapshot publish for a specific project.
48
+ * Debounced per-project: each project's agents are additively merged into
49
+ * the owner's single 14199 event without removing other projects' agents.
50
+ */
51
+ static publishProjectAgentSnapshot(projectDTag: string): void {
52
+ const existing = AgentProfilePublisher.snapshotDebounceTimers.get(projectDTag);
53
+ if (existing) {
54
+ clearTimeout(existing);
55
+ }
56
+
57
+ const timer = setTimeout(() => {
58
+ AgentProfilePublisher.snapshotDebounceTimers.delete(projectDTag);
59
+ AgentProfilePublisher.executeSnapshotPublish(projectDTag).catch((error) => {
60
+ logger.warn("Debounced 14199 snapshot publish failed", {
61
+ projectDTag,
62
+ error: error instanceof Error ? error.message : String(error),
63
+ });
64
+ });
65
+ }, AgentProfilePublisher.SNAPSHOT_DEBOUNCE_MS);
66
+
67
+ AgentProfilePublisher.snapshotDebounceTimers.set(projectDTag, timer);
68
+ }
69
+
70
+ /**
71
+ * Execute the actual 14199 snapshot publish for a specific project.
72
+ * Reads agents for this project only, then additively merges their pubkeys
73
+ * into the owner's existing 14199 event (preserving agents from other projects).
74
+ *
75
+ * When NIP-46 is enabled, each whitelisted owner gets their own 14199 event signed
76
+ * by that owner via NIP-46 remote signing. If signing fails, the event is simply
77
+ * not published — there is no fallback to backend key signing.
78
+ *
79
+ * When NIP-46 is disabled, the event is signed with the backend key.
80
+ */
81
+ private static async executeSnapshotPublish(projectDTag: string): Promise<void> {
82
+ const projectAgents = await agentStorage.getProjectAgents(projectDTag);
83
+ const whitelisted = config.getWhitelistedPubkeys(undefined, config.getConfig());
84
+
85
+ // Collect unique agent pubkeys for this project
86
+ const agentPubkeys: string[] = [];
87
+ const seen = new Set<string>();
88
+ for (const agent of projectAgents) {
89
+ const agentSigner = new NDKPrivateKeySigner(agent.nsec);
90
+ if (!seen.has(agentSigner.pubkey)) {
91
+ seen.add(agentSigner.pubkey);
92
+ agentPubkeys.push(agentSigner.pubkey);
93
+ }
94
+ }
95
+
96
+ logger.info("Publishing debounced 14199 snapshot", {
97
+ projectDTag,
98
+ agentCount: agentPubkeys.length,
99
+ });
100
+
101
+ const nip46Service = Nip46SigningService.getInstance();
102
+
103
+ if (nip46Service.isEnabled()) {
104
+ for (const ownerPubkey of whitelisted) {
105
+ await AgentProfilePublisher.publishSnapshotForOwner(
106
+ ownerPubkey,
107
+ agentPubkeys,
108
+ nip46Service,
109
+ );
110
+ }
111
+ } else {
112
+ await AgentProfilePublisher.publishSnapshotWithBackendKey(agentPubkeys);
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Publish a 14199 snapshot signed by a specific owner via NIP-46.
118
+ * Additively merges this project's agent pubkeys into the owner's existing 14199.
119
+ * If all project agents are already present, skips the publish entirely.
120
+ * If signing fails for any reason, the event is not published.
121
+ */
122
+ private static async publishSnapshotForOwner(
123
+ ownerPubkey: string,
124
+ projectAgentPubkeys: string[],
125
+ nip46Service: Nip46SigningService,
126
+ ): Promise<void> {
127
+ const existingPTags = await AgentProfilePublisher.fetchExistingPTags(ownerPubkey);
128
+ const existingSet = new Set(existingPTags);
129
+ const newPubkeys = projectAgentPubkeys.filter((pk) => !existingSet.has(pk));
130
+
131
+ if (newPubkeys.length === 0 && existingPTags.length > 0) {
132
+ logger.debug("[NIP-46] All project agents already in 14199, skipping publish", {
133
+ ownerPubkey: ownerPubkey.substring(0, 12),
134
+ existingCount: existingPTags.length,
135
+ });
136
+ return;
137
+ }
138
+
139
+ const mergedPubkeys = [...existingPTags, ...newPubkeys];
140
+ const ndk = getNDK();
141
+ const signingLog = Nip46SigningLog.getInstance();
142
+ const ev = AgentProfilePublisher.buildSnapshotEvent(ndk, mergedPubkeys);
143
+
144
+ const result = await nip46Service.signEvent(ownerPubkey, ev, "14199_snapshot");
145
+
146
+ if (result.outcome === "signed") {
147
+ try {
148
+ await ev.publish();
149
+ signingLog.log({
150
+ op: "event_published",
151
+ ownerPubkey: Nip46SigningLog.truncatePubkey(ownerPubkey),
152
+ eventKind: NDKKind.ProjectAgentSnapshot as number,
153
+ signerType: "nip46",
154
+ pTagCount: ev.tags.filter((t) => t[0] === "p").length,
155
+ eventId: ev.id,
156
+ });
157
+ logger.info("[NIP-46] Published owner-signed 14199", {
158
+ ownerPubkey: ownerPubkey.substring(0, 12),
159
+ eventId: ev.id?.substring(0, 12),
160
+ existingPTags: existingPTags.length,
161
+ newPubkeys: newPubkeys.length,
162
+ totalPTags: mergedPubkeys.length,
163
+ });
164
+ } catch (error) {
165
+ logger.warn("[NIP-46] Failed to publish owner-signed 14199", {
166
+ ownerPubkey: ownerPubkey.substring(0, 12),
167
+ error: error instanceof Error ? error.message : String(error),
168
+ });
169
+ }
170
+ return;
171
+ }
172
+
173
+ logger.warn("[NIP-46] Skipping 14199 publish — signing failed", {
174
+ ownerPubkey: ownerPubkey.substring(0, 12),
175
+ outcome: result.outcome,
176
+ reason: result.reason,
177
+ });
178
+ }
179
+
180
+ /**
181
+ * Publish a 14199 snapshot signed with the backend key.
182
+ * Additively merges this project's agent pubkeys into the existing 14199.
183
+ * If all project agents are already present, skips the publish entirely.
184
+ * Used when NIP-46 is not enabled.
185
+ */
186
+ private static async publishSnapshotWithBackendKey(
187
+ projectAgentPubkeys: string[],
188
+ ): Promise<void> {
189
+ const tenexNsec = await config.ensureBackendPrivateKey();
190
+ const signer = new NDKPrivateKeySigner(tenexNsec);
191
+
192
+ const existingPTags = await AgentProfilePublisher.fetchExistingPTags(signer.pubkey);
193
+ const existingSet = new Set(existingPTags);
194
+ const newPubkeys = projectAgentPubkeys.filter((pk) => !existingSet.has(pk));
195
+
196
+ if (newPubkeys.length === 0 && existingPTags.length > 0) {
197
+ logger.debug("All project agents already in 14199, skipping backend publish", {
198
+ existingCount: existingPTags.length,
199
+ });
200
+ return;
201
+ }
202
+
203
+ const mergedPubkeys = [...existingPTags, ...newPubkeys];
204
+ const ndk = getNDK();
205
+ const ev = AgentProfilePublisher.buildSnapshotEvent(ndk, mergedPubkeys);
206
+
207
+ await ev.sign(signer);
208
+ try {
209
+ await ev.publish();
210
+ logger.info("Published backend-signed 14199 snapshot", {
211
+ existingPTags: existingPTags.length,
212
+ newPubkeys: newPubkeys.length,
213
+ totalPTags: mergedPubkeys.length,
214
+ });
215
+ } catch (error) {
216
+ logger.warn("Failed to publish backend-signed 14199 snapshot", {
217
+ error: error instanceof Error ? error.message : String(error),
218
+ eventId: ev.id?.substring(0, 12),
219
+ });
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Build an unsigned 14199 snapshot event with p-tags for the given pubkeys.
225
+ */
226
+ private static buildSnapshotEvent(
227
+ ndk: ReturnType<typeof getNDK>,
228
+ allPubkeys: string[],
229
+ ): NDKEvent {
230
+ const ev = new NDKEvent(ndk, {
231
+ kind: NDKKind.ProjectAgentSnapshot,
232
+ });
233
+
234
+ for (const pk of allPubkeys) {
235
+ ev.tag(["p", pk]);
236
+ }
237
+
238
+ return ev;
239
+ }
240
+
241
+ /**
242
+ * Fetch existing p-tag pubkeys from the owner's latest 14199 event.
243
+ * Returns an empty array on fetch failure (safe: we only add, never remove).
244
+ */
245
+ private static async fetchExistingPTags(ownerPubkey: string): Promise<string[]> {
246
+ try {
247
+ const ndk = getNDK();
248
+ const events = await ndk.fetchEvents({
249
+ kinds: [NDKKind.ProjectAgentSnapshot as number],
250
+ authors: [ownerPubkey],
251
+ });
252
+
253
+ if (events.size === 0) {
254
+ return [];
255
+ }
256
+
257
+ // Get the latest event (highest created_at)
258
+ const latest = Array.from(events).sort(
259
+ (a, b) => (b.created_at ?? 0) - (a.created_at ?? 0),
260
+ )[0];
261
+
262
+ return latest.tags
263
+ .filter((t) => t[0] === "p" && t[1])
264
+ .map((t) => t[1]);
265
+ } catch (error) {
266
+ logger.warn("Failed to fetch existing 14199 event, proceeding with empty p-tags", {
267
+ ownerPubkey: ownerPubkey.substring(0, 12),
268
+ error: error instanceof Error ? error.message : String(error),
269
+ });
270
+ return [];
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Publishes a kind:0 profile event for an agent
276
+ */
277
+ static async publishAgentProfile(
278
+ signer: NDKPrivateKeySigner,
279
+ agentName: string,
280
+ agentRole: string,
281
+ projectTitle: string,
282
+ projectEvent: NDKProject,
283
+ agentDefinitionEventId?: string,
284
+ agentMetadata?: {
285
+ description?: string;
286
+ instructions?: string;
287
+ useCriteria?: string;
288
+ },
289
+ whitelistedPubkeys?: string[]
290
+ ): Promise<void> {
291
+ let profileEvent: NDKEvent;
292
+
293
+ try {
294
+ // Check if there are other agents with the same slug (name) in this project
295
+ // If so, append pubkey prefix for disambiguation
296
+ const projectDTag = projectEvent.dTag;
297
+ let displayName = agentName;
298
+
299
+ if (projectDTag) {
300
+ // Load the agent's slug from storage to check for conflicts
301
+ const agent = await agentStorage.loadAgent(signer.pubkey);
302
+ if (agent && agent.slug) {
303
+ // Check if this slug has conflicts (multiple pubkeys for same slug)
304
+ const conflictingAgent = await agentStorage.getAgentBySlugForProject(
305
+ agent.slug,
306
+ projectDTag
307
+ );
308
+
309
+ // If we found an agent with this slug but it's a different pubkey, there's a conflict
310
+ if (conflictingAgent && conflictingAgent.nsec !== agent.nsec) {
311
+ const otherSigner = new NDKPrivateKeySigner(conflictingAgent.nsec);
312
+ if (otherSigner.pubkey !== signer.pubkey) {
313
+ // Conflict exists - append pubkey prefix to both names
314
+ displayName = `${agentName} (${signer.pubkey.slice(0, 5)})`;
315
+ logger.info("Agent slug conflict detected, adding pubkey prefix to Kind 0 name", {
316
+ slug: agent.slug,
317
+ pubkey: signer.pubkey.substring(0, 8),
318
+ projectDTag,
319
+ displayName,
320
+ });
321
+ }
322
+ }
323
+ }
324
+ }
325
+
326
+ const avatarUrl = AgentProfilePublisher.buildAvatarUrl(signer.pubkey);
327
+
328
+ const profile = {
329
+ name: displayName,
330
+ description: `${agentRole} agent for ${projectTitle}`,
331
+ picture: avatarUrl,
332
+ };
333
+
334
+ profileEvent = new NDKEvent(getNDK(), {
335
+ kind: 0,
336
+ pubkey: signer.pubkey,
337
+ content: JSON.stringify(profile),
338
+ tags: [],
339
+ });
340
+
341
+ // Validate projectEvent has required fields before tagging
342
+ // Both pubkey and dTag are required for valid NIP-01 addressable coordinates:
343
+ // Format: <kind>:<pubkey>:<d-tag> (e.g., "31933:abc123:my-project")
344
+ // Note: projectDTag was already declared earlier for conflict detection
345
+ if (!projectEvent.pubkey) {
346
+ logger.warn("Project event missing pubkey, skipping a-tag", {
347
+ agentPubkey: signer.pubkey,
348
+ });
349
+ } else if (!projectDTag) {
350
+ logger.warn("Project event missing d-tag, skipping a-tag", {
351
+ agentPubkey: signer.pubkey,
352
+ projectPubkey: projectEvent.pubkey,
353
+ });
354
+ } else {
355
+ // Properly tag the project event (creates an "a" tag for kind:31933)
356
+ // Note: We only tag the CURRENT project. Each project publishes
357
+ // the agent's profile with its own a-tag when the agent boots there.
358
+ // This avoids the multi-owner problem where other projects may have
359
+ // different owner pubkeys that we don't have access to.
360
+ profileEvent.tag(projectEvent.tagReference());
361
+ }
362
+
363
+ // Add e-tag for the agent definition event if it exists and is valid
364
+ // OR add metadata tags as fallback for agents without a valid event ID
365
+ const trimmedEventId = agentDefinitionEventId?.trim() ?? "";
366
+ const isValidHexEventId = /^[a-f0-9]{64}$/i.test(trimmedEventId);
367
+
368
+ if (isValidHexEventId) {
369
+ profileEvent.tags.push(["e", trimmedEventId]);
370
+ } else {
371
+ // Log warning only if an event ID was provided but is invalid
372
+ if (trimmedEventId !== "") {
373
+ logger.warn(
374
+ "Invalid event ID format for agent definition in profile, using metadata tags instead",
375
+ {
376
+ eventId: agentDefinitionEventId,
377
+ }
378
+ );
379
+ }
380
+
381
+ // Add metadata tags for agents without a valid NDKAgentDefinition event ID
382
+ if (agentMetadata) {
383
+ if (agentMetadata.description) {
384
+ profileEvent.tags.push(["description", agentMetadata.description]);
385
+ }
386
+ if (agentMetadata.instructions) {
387
+ profileEvent.tags.push(["instructions", agentMetadata.instructions]);
388
+ }
389
+ if (agentMetadata.useCriteria) {
390
+ profileEvent.tags.push(["use-criteria", agentMetadata.useCriteria]);
391
+ }
392
+ }
393
+ }
394
+
395
+ // Add p-tags for all whitelisted pubkeys
396
+ if (whitelistedPubkeys && whitelistedPubkeys.length > 0) {
397
+ for (const pubkey of whitelistedPubkeys) {
398
+ if (pubkey && pubkey !== signer.pubkey) {
399
+ // Don't p-tag self
400
+ profileEvent.tags.push(["p", pubkey]);
401
+ }
402
+ }
403
+ }
404
+
405
+ // Add bot tag
406
+ profileEvent.tags.push(["bot"]);
407
+
408
+ // Add tenex tag
409
+ profileEvent.tags.push(["t", "tenex"]);
410
+
411
+ await profileEvent.sign(signer, { pTags: false });
412
+
413
+ try {
414
+ await profileEvent.publish();
415
+ } catch (publishError) {
416
+ logger.warn("Failed to publish agent profile (may already exist)", {
417
+ error: publishError,
418
+ agentName,
419
+ pubkey: signer.pubkey.substring(0, 8),
420
+ });
421
+ }
422
+
423
+ // Schedule debounced 14199 snapshot publish for this project
424
+ const projectTag = projectEvent.dTag;
425
+ if (projectTag) {
426
+ AgentProfilePublisher.publishProjectAgentSnapshot(projectTag);
427
+ }
428
+ } catch (error) {
429
+ logger.error("Failed to create agent profile", {
430
+ error,
431
+ agentName,
432
+ });
433
+ throw error;
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Publishes a kind:3 contact list for an agent
439
+ * This allows agents to follow other agents in the project and whitelisted pubkeys
440
+ */
441
+ static async publishContactList(
442
+ signer: NDKPrivateKeySigner,
443
+ contactPubkeys: string[]
444
+ ): Promise<void> {
445
+ try {
446
+ // Create a kind:3 event (contact list)
447
+ const contactListEvent = new NDKEvent(getNDK(), {
448
+ kind: 3,
449
+ pubkey: signer.pubkey,
450
+ content: "", // Contact list content is usually empty
451
+ tags: [],
452
+ });
453
+
454
+ // Add p-tags for each contact
455
+ for (const pubkey of contactPubkeys) {
456
+ if (pubkey && pubkey !== signer.pubkey) {
457
+ // Don't follow self
458
+ contactListEvent.tags.push(["p", pubkey]);
459
+ }
460
+ }
461
+
462
+ // Sign and publish the contact list
463
+ await contactListEvent.sign(signer, { pTags: false });
464
+
465
+ try {
466
+ await contactListEvent.publish();
467
+ } catch (publishError) {
468
+ logger.warn("Failed to publish contact list (may already exist)", {
469
+ error: publishError,
470
+ agentPubkey: signer.pubkey.substring(0, 8),
471
+ });
472
+ }
473
+ } catch (error) {
474
+ logger.error("Failed to create contact list", {
475
+ error,
476
+ agentPubkey: signer.pubkey.substring(0, 8),
477
+ });
478
+ // Don't throw - contact list is not critical
479
+ }
480
+ }
481
+
482
+ /**
483
+ * Publishes a kind:0 profile event for the TENEX backend daemon.
484
+ * This identifies the backend as an entity on nostr.
485
+ *
486
+ * @param signer The backend's NDKPrivateKeySigner
487
+ * @param backendName The name for the backend profile (default: "tenex backend")
488
+ * @param whitelistedPubkeys Array of pubkeys to include as contacts
489
+ */
490
+ static async publishBackendProfile(
491
+ signer: NDKPrivateKeySigner,
492
+ backendName: string = "tenex backend",
493
+ whitelistedPubkeys?: string[]
494
+ ): Promise<void> {
495
+ try {
496
+ const avatarUrl = AgentProfilePublisher.buildAvatarUrl(signer.pubkey);
497
+
498
+ const profile = {
499
+ name: backendName,
500
+ description: "TENEX Backend Daemon - Multi-agent orchestration system",
501
+ picture: avatarUrl,
502
+ };
503
+
504
+ const profileEvent = new NDKEvent(getNDK(), {
505
+ kind: 0,
506
+ pubkey: signer.pubkey,
507
+ content: JSON.stringify(profile),
508
+ tags: [],
509
+ });
510
+
511
+ // Add p-tags for all whitelisted pubkeys
512
+ if (whitelistedPubkeys && whitelistedPubkeys.length > 0) {
513
+ for (const pubkey of whitelistedPubkeys) {
514
+ if (pubkey && pubkey !== signer.pubkey) {
515
+ profileEvent.tags.push(["p", pubkey]);
516
+ }
517
+ }
518
+ }
519
+
520
+ // Add bot tag to indicate this is an automated system
521
+ profileEvent.tags.push(["bot"]);
522
+
523
+ // Add tenex tag for discoverability
524
+ profileEvent.tags.push(["t", "tenex"]);
525
+
526
+ // Add tenex-backend tag to distinguish from agents
527
+ profileEvent.tags.push(["t", "tenex-backend"]);
528
+
529
+ await profileEvent.sign(signer, { pTags: false });
530
+
531
+ try {
532
+ // Publish with timeout - don't block daemon startup if relays are slow
533
+ await Promise.race([
534
+ profileEvent.publish(),
535
+ new Promise((_, reject) =>
536
+ setTimeout(() => reject(new Error("Publish timeout")), AgentProfilePublisher.PUBLISH_TIMEOUT_MS)
537
+ ),
538
+ ]);
539
+ logger.info("Published TENEX backend profile", {
540
+ pubkey: signer.pubkey.substring(0, 8),
541
+ name: backendName,
542
+ });
543
+ } catch (publishError) {
544
+ logger.warn("Failed to publish backend profile (relay timeout or error)", {
545
+ error: publishError,
546
+ pubkey: signer.pubkey.substring(0, 8),
547
+ });
548
+ }
549
+ } catch (error) {
550
+ logger.error("Failed to create backend profile", {
551
+ error,
552
+ });
553
+ // Don't throw - backend profile is not critical for operation
554
+ }
555
+ }
556
+
557
+ /**
558
+ * In-memory cache of last published instruction hash per agent pubkey.
559
+ * Used for deduplication to avoid publishing duplicate kind:0 events
560
+ * when compiled instructions haven't changed.
561
+ */
562
+ private static lastPublishedInstructionHash: Map<string, string> = new Map();
563
+
564
+ /**
565
+ * Publishes a kind:0 profile event with compiled instructions for an agent.
566
+ * Uses hash-based deduplication to avoid publishing when instructions haven't changed.
567
+ * This is fire-and-forget - failures are logged but don't throw.
568
+ *
569
+ * Callers should use `void AgentProfilePublisher.publishCompiledInstructions(...)` to
570
+ * explicitly indicate the fire-and-forget intent.
571
+ *
572
+ * @param signer The agent's NDKPrivateKeySigner
573
+ * @param compiledInstructions The compiled effective instructions from PromptCompilerService
574
+ * @param agentName The agent's display name
575
+ * @param agentRole The agent's role description
576
+ * @param projectTitle The project title for the profile description
577
+ */
578
+ static async publishCompiledInstructions(
579
+ signer: NDKPrivateKeySigner,
580
+ compiledInstructions: string,
581
+ agentName: string,
582
+ agentRole: string,
583
+ projectTitle: string
584
+ ): Promise<void> {
585
+ try {
586
+ // Hash-based deduplication: skip if instructions haven't changed
587
+ const instructionHash = crypto
588
+ .createHash("sha256")
589
+ .update(compiledInstructions)
590
+ .digest("hex");
591
+
592
+ const lastHash = AgentProfilePublisher.lastPublishedInstructionHash.get(signer.pubkey);
593
+ if (lastHash === instructionHash) {
594
+ logger.debug("Skipping kind:0 publish - compiled instructions unchanged", {
595
+ agentPubkey: signer.pubkey.substring(0, 8),
596
+ });
597
+ return;
598
+ }
599
+
600
+ const avatarUrl = AgentProfilePublisher.buildAvatarUrl(signer.pubkey);
601
+
602
+ const profile = {
603
+ name: agentName,
604
+ description: `${agentRole} agent for ${projectTitle}`,
605
+ picture: avatarUrl,
606
+ };
607
+
608
+ const profileEvent = new NDKEvent(getNDK(), {
609
+ kind: 0,
610
+ pubkey: signer.pubkey,
611
+ content: JSON.stringify(profile),
612
+ tags: [],
613
+ });
614
+
615
+ // Add instruction tag with compiled instructions
616
+ profileEvent.tags.push(["instruction", compiledInstructions]);
617
+
618
+ // Add bot tag
619
+ profileEvent.tags.push(["bot"]);
620
+
621
+ // Add tenex tag for discoverability
622
+ profileEvent.tags.push(["t", "tenex"]);
623
+
624
+ await profileEvent.sign(signer, { pTags: false });
625
+
626
+ try {
627
+ // Publish with timeout to prevent blocking
628
+ await Promise.race([
629
+ profileEvent.publish(),
630
+ new Promise((_, reject) =>
631
+ setTimeout(() => reject(new Error("Publish timeout")), AgentProfilePublisher.PUBLISH_TIMEOUT_MS)
632
+ ),
633
+ ]);
634
+
635
+ // Update deduplication cache on successful publish
636
+ AgentProfilePublisher.lastPublishedInstructionHash.set(signer.pubkey, instructionHash);
637
+
638
+ logger.info("Published kind:0 with compiled instructions", {
639
+ agentPubkey: signer.pubkey.substring(0, 8),
640
+ agentName,
641
+ instructionsLength: compiledInstructions.length,
642
+ });
643
+ } catch (publishError) {
644
+ logger.warn("Failed to publish kind:0 with compiled instructions (relay timeout or error)", {
645
+ error: publishError,
646
+ agentPubkey: signer.pubkey.substring(0, 8),
647
+ });
648
+ }
649
+ } catch (error) {
650
+ logger.error("Failed to create kind:0 with compiled instructions", {
651
+ error,
652
+ agentPubkey: signer.pubkey.substring(0, 8),
653
+ });
654
+ // Don't throw - this is fire-and-forget
655
+ }
656
+ }
657
+ }