@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,242 @@
1
+ /**
2
+ * PrefixKVStore - Centralized prefix-to-full-ID lookup store
3
+ *
4
+ * Provides O(1) lookups of event IDs and pubkeys by their 12-character hex prefix.
5
+ * Uses LMDB for fast, persistent storage.
6
+ *
7
+ * Storage location: ~/.tenex/data/prefix-kv/
8
+ *
9
+ * Key format: 12-char prefix -> 64-char full ID
10
+ * Collision policy: First write wins (collisions are statistically irrelevant)
11
+ *
12
+ * LIFECYCLE: This store is GLOBAL across all projects and uses reference counting.
13
+ * - Each call to initialize() increments the reference count
14
+ * - Each call to close() decrements the reference count (does NOT close the DB)
15
+ * - The DB remains open as long as at least one reference exists
16
+ * - Use forceClose() only during full daemon shutdown to actually close the DB
17
+ *
18
+ * Per-project runtimes SHOULD call:
19
+ * - initialize() during startup (increments refcount)
20
+ * - close() during shutdown (decrements refcount, keeps DB open for other runtimes)
21
+ *
22
+ * Migration scripts SHOULD call:
23
+ * - initialize() at start
24
+ * - forceClose() at end (since they run standalone, not within the daemon)
25
+ *
26
+ * CONCURRENCY NOTE: The first-write-wins policy uses check-then-put which is
27
+ * not strictly atomic under concurrent writers. In practice, this is acceptable
28
+ * because prefix collisions are extremely rare (1 in 2^48 for random IDs), and
29
+ * the worst case is that a later ID wins over an earlier one for the same prefix.
30
+ */
31
+
32
+ import { open, type RootDatabase } from "lmdb";
33
+ import { join } from "node:path";
34
+ import { getTenexBasePath } from "@/constants";
35
+ import { logger } from "@/utils/logger";
36
+
37
+ const PREFIX_LENGTH = 12;
38
+ const FULL_ID_LENGTH = 64;
39
+
40
+ export class PrefixKVStore {
41
+ private static instance: PrefixKVStore | null = null;
42
+ private db: RootDatabase<string, string> | null = null;
43
+ private initialized = false;
44
+ private initializationPromise: Promise<void> | null = null;
45
+ private referenceCount = 0;
46
+
47
+ private constructor() {}
48
+
49
+ static getInstance(): PrefixKVStore {
50
+ if (!PrefixKVStore.instance) {
51
+ PrefixKVStore.instance = new PrefixKVStore();
52
+ }
53
+ return PrefixKVStore.instance;
54
+ }
55
+
56
+ /**
57
+ * Initialize the LMDB store.
58
+ * Thread-safe: concurrent calls share a single initialization.
59
+ * Must be called before any operations.
60
+ */
61
+ async initialize(): Promise<void> {
62
+ // Fast path: already initialized
63
+ if (this.initialized) {
64
+ this.referenceCount++;
65
+ return;
66
+ }
67
+
68
+ // Concurrency guard: if initialization is in progress, wait for it
69
+ if (this.initializationPromise) {
70
+ await this.initializationPromise;
71
+ this.referenceCount++;
72
+ return;
73
+ }
74
+
75
+ // Start initialization
76
+ this.initializationPromise = this.doInitialize();
77
+
78
+ try {
79
+ await this.initializationPromise;
80
+ this.referenceCount++;
81
+ } finally {
82
+ // Clear the promise after completion (success or failure)
83
+ this.initializationPromise = null;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Internal initialization logic.
89
+ */
90
+ private async doInitialize(): Promise<void> {
91
+ const dataPath = join(getTenexBasePath(), "data", "prefix-kv");
92
+
93
+ try {
94
+ this.db = open<string, string>({
95
+ path: dataPath,
96
+ compression: false,
97
+ encoding: "string",
98
+ });
99
+ this.initialized = true;
100
+ logger.debug(`[PrefixKVStore] Initialized at ${dataPath}`);
101
+ } catch (error) {
102
+ logger.error("[PrefixKVStore] Failed to initialize LMDB", {
103
+ error: error instanceof Error ? error.message : String(error),
104
+ path: dataPath,
105
+ });
106
+ throw error;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Add an ID (event ID or pubkey) to the store.
112
+ * Extracts the 12-char prefix and stores the mapping.
113
+ * First write wins - if prefix already exists, this is a no-op.
114
+ */
115
+ async add(fullId: string): Promise<void> {
116
+ if (!this.db) {
117
+ throw new Error("[PrefixKVStore] Not initialized. Call initialize() first.");
118
+ }
119
+
120
+ if (!fullId || fullId.length !== FULL_ID_LENGTH) {
121
+ return; // Silently ignore invalid IDs
122
+ }
123
+
124
+ const prefix = fullId.substring(0, PREFIX_LENGTH);
125
+
126
+ // First write wins - only add if not exists
127
+ const existing = this.db.get(prefix);
128
+ if (existing) {
129
+ return;
130
+ }
131
+
132
+ await this.db.put(prefix, fullId);
133
+ }
134
+
135
+ /**
136
+ * Add multiple IDs in a batch operation.
137
+ * More efficient than calling add() multiple times.
138
+ */
139
+ async addBatch(fullIds: string[]): Promise<void> {
140
+ const db = this.db;
141
+ if (!db) {
142
+ throw new Error("[PrefixKVStore] Not initialized. Call initialize() first.");
143
+ }
144
+
145
+ const validIds = fullIds.filter((id) => id && id.length === FULL_ID_LENGTH);
146
+ if (validIds.length === 0) return;
147
+
148
+ await db.batch(() => {
149
+ for (const fullId of validIds) {
150
+ const prefix = fullId.substring(0, PREFIX_LENGTH);
151
+ const existing = db.get(prefix);
152
+ if (!existing) {
153
+ db.put(prefix, fullId);
154
+ }
155
+ }
156
+ });
157
+ }
158
+
159
+ /**
160
+ * Look up a full ID by its prefix.
161
+ * Returns the full 64-char ID or null if not found.
162
+ *
163
+ * @param prefix - Must be exactly 12 hex characters. Returns null otherwise.
164
+ */
165
+ lookup(prefix: string): string | null {
166
+ if (!this.db) {
167
+ throw new Error("[PrefixKVStore] Not initialized. Call initialize() first.");
168
+ }
169
+
170
+ // Require exactly 12 characters - no padding or truncation
171
+ // This prevents confusing behavior where short prefixes would
172
+ // be zero-padded and almost never match anything
173
+ if (!prefix || prefix.length !== PREFIX_LENGTH) {
174
+ return null;
175
+ }
176
+
177
+ const result = this.db.get(prefix);
178
+ return result ?? null;
179
+ }
180
+
181
+ /**
182
+ * Check if a prefix exists in the store.
183
+ */
184
+ has(prefix: string): boolean {
185
+ return this.lookup(prefix) !== null;
186
+ }
187
+
188
+ /**
189
+ * Get statistics about the store.
190
+ */
191
+ async getStats(): Promise<{ count: number }> {
192
+ if (!this.db) {
193
+ throw new Error("[PrefixKVStore] Not initialized. Call initialize() first.");
194
+ }
195
+
196
+ let count = 0;
197
+ for (const _entry of this.db.getRange()) {
198
+ count++;
199
+ }
200
+
201
+ return { count };
202
+ }
203
+
204
+ /**
205
+ * Release a reference to the store.
206
+ * For per-project runtimes - decrements reference count but does NOT close the store.
207
+ * Use forceClose() for daemon shutdown.
208
+ */
209
+ async close(): Promise<void> {
210
+ if (this.referenceCount > 0) {
211
+ this.referenceCount--;
212
+ logger.debug(`[PrefixKVStore] Reference released, remaining: ${this.referenceCount}`);
213
+ }
214
+ // Note: We intentionally do NOT close the DB here.
215
+ // Per-project runtimes call this on stop, but the store should
216
+ // remain open for other active runtimes.
217
+ }
218
+
219
+ /**
220
+ * Force close the database connection.
221
+ * Should ONLY be called during full daemon shutdown, not per-project stop.
222
+ */
223
+ async forceClose(): Promise<void> {
224
+ if (this.db) {
225
+ await this.db.close();
226
+ this.db = null;
227
+ this.initialized = false;
228
+ this.referenceCount = 0;
229
+ logger.debug("[PrefixKVStore] Force closed");
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Check if the store is initialized.
235
+ */
236
+ isInitialized(): boolean {
237
+ return this.initialized;
238
+ }
239
+ }
240
+
241
+ // Export singleton instance
242
+ export const prefixKVStore = PrefixKVStore.getInstance();
@@ -0,0 +1 @@
1
+ export { PrefixKVStore, prefixKVStore } from "./PrefixKVStore";
@@ -0,0 +1,96 @@
1
+ /**
2
+ * SystemReminderUtils - Utilities for wrapping and managing system reminder content
3
+ *
4
+ * This module provides shared utilities for creating consistent `<system-reminder>` XML tags
5
+ * that are used for behavioral nudges and context injections.
6
+ *
7
+ * The `<system-reminder>` format is used for:
8
+ * - Heuristic violations (todo nudge, delegation warnings, etc.)
9
+ * - Deferred injections (supervision messages for next turn)
10
+ * - Dynamic context (response context, todo state)
11
+ *
12
+ * NOT used for (these stay tool-bound):
13
+ * - AGENTS.md injections (remain in tool result output)
14
+ * - Pre-tool supervision blocks (remain in tool result)
15
+ */
16
+
17
+ /**
18
+ * Wrap content in `<system-reminder>` tags.
19
+ *
20
+ * @param content - The content to wrap (can be multi-line)
21
+ * @returns Content wrapped in system-reminder tags
22
+ */
23
+ export function wrapInSystemReminder(content: string): string {
24
+ if (!content || content.trim() === "") {
25
+ return "";
26
+ }
27
+ return `<system-reminder>\n${content.trim()}\n</system-reminder>`;
28
+ }
29
+
30
+ /**
31
+ * Combine multiple system reminder contents into a single wrapped block.
32
+ *
33
+ * @param contents - Array of content strings to combine
34
+ * @returns Single system-reminder block with all contents, or empty string if no content
35
+ */
36
+ export function combineSystemReminders(contents: string[]): string {
37
+ const nonEmpty = contents.filter((c) => c && c.trim() !== "");
38
+ if (nonEmpty.length === 0) {
39
+ return "";
40
+ }
41
+ return wrapInSystemReminder(nonEmpty.join("\n\n"));
42
+ }
43
+
44
+ /**
45
+ * Append system reminder content to an existing message.
46
+ *
47
+ * NOTE: This is a simple utility that appends the reminder to the end of the
48
+ * existing content with double newline separation. It does NOT merge with
49
+ * existing system-reminder blocks. For that use case, use MessageCompiler's
50
+ * appendEphemeralMessagesToLastUserMessage method which handles extraction
51
+ * and recombination.
52
+ *
53
+ * @param existingContent - The existing message content
54
+ * @param reminderContent - The system reminder content to append (already wrapped or raw)
55
+ * @returns The combined content with reminder appended
56
+ */
57
+ export function appendSystemReminderToMessage(
58
+ existingContent: string,
59
+ reminderContent: string
60
+ ): string {
61
+ if (!reminderContent || reminderContent.trim() === "") {
62
+ return existingContent;
63
+ }
64
+
65
+ // If reminderContent is not already wrapped, wrap it
66
+ const wrappedReminder = reminderContent.trim().startsWith("<system-reminder>")
67
+ ? reminderContent
68
+ : wrapInSystemReminder(reminderContent);
69
+
70
+ return `${existingContent}\n\n${wrappedReminder}`;
71
+ }
72
+
73
+ /**
74
+ * Check if a string contains system-reminder tags.
75
+ *
76
+ * @param content - The content to check
77
+ * @returns True if content contains system-reminder tags
78
+ */
79
+ export function hasSystemReminder(content: string): boolean {
80
+ return content.includes("<system-reminder>") && content.includes("</system-reminder>");
81
+ }
82
+
83
+ /**
84
+ * Extract the content from within the first system-reminder tag block.
85
+ * Returns empty string if no valid system-reminder block found.
86
+ *
87
+ * NOTE: This only extracts the first block. For extracting all blocks,
88
+ * use MessageCompiler's extractAllSystemReminderContents method.
89
+ *
90
+ * @param content - The content potentially containing system-reminder tags
91
+ * @returns The content inside the first tags, or empty string
92
+ */
93
+ export function extractSystemReminderContent(content: string): string {
94
+ const match = content.match(/<system-reminder>\n?([\s\S]*?)\n?<\/system-reminder>/);
95
+ return match ? match[1].trim() : "";
96
+ }
@@ -0,0 +1,7 @@
1
+ export {
2
+ wrapInSystemReminder,
3
+ combineSystemReminders,
4
+ appendSystemReminderToMessage,
5
+ hasSystemReminder,
6
+ extractSystemReminderContent,
7
+ } from "./SystemReminderUtils";
@@ -0,0 +1,325 @@
1
+ import { config } from "@/services/ConfigService";
2
+ import { projectContextStore } from "@/services/projects";
3
+ import { logger } from "@/utils/logger";
4
+ import type { Hexpubkey, NDKEvent } from "@nostr-dev-kit/ndk";
5
+
6
+ /**
7
+ * Reason why a pubkey is trusted
8
+ */
9
+ export type TrustReason = "whitelisted" | "backend" | "agent";
10
+
11
+ /**
12
+ * Result of a trust check
13
+ */
14
+ export interface TrustResult {
15
+ /** Whether the pubkey is trusted */
16
+ trusted: boolean;
17
+ /** Why the pubkey is trusted (only set if trusted is true) */
18
+ reason?: TrustReason;
19
+ }
20
+
21
+ /**
22
+ * TrustPubkeyService determines if a given pubkey should be heeded or ignored.
23
+ *
24
+ * A pubkey is trusted if it is:
25
+ * - In the whitelisted pubkeys from config
26
+ * - The backend's own pubkey
27
+ * - An agent in the system (registered in ProjectContext)
28
+ *
29
+ * Trust precedence (highest to lowest): whitelisted > backend > agent
30
+ */
31
+ export class TrustPubkeyService {
32
+ private static instance: TrustPubkeyService;
33
+
34
+ /** Cached backend pubkey to avoid repeated async calls */
35
+ private cachedBackendPubkey?: Hexpubkey;
36
+
37
+ /** Cached whitelist Set for O(1) lookups */
38
+ private cachedWhitelistSet?: Set<Hexpubkey>;
39
+
40
+ private constructor() {}
41
+
42
+ /**
43
+ * Get singleton instance
44
+ */
45
+ static getInstance(): TrustPubkeyService {
46
+ if (!TrustPubkeyService.instance) {
47
+ TrustPubkeyService.instance = new TrustPubkeyService();
48
+ }
49
+ return TrustPubkeyService.instance;
50
+ }
51
+
52
+ /**
53
+ * Check if a pubkey is trusted.
54
+ * Returns a TrustResult with the trust status and reason.
55
+ *
56
+ * @param pubkey The pubkey to check (hex format)
57
+ * @returns TrustResult indicating if trusted and why
58
+ */
59
+ async isTrusted(pubkey: Hexpubkey): Promise<TrustResult> {
60
+ // 1. Check whitelisted pubkeys from config
61
+ if (this.isWhitelisted(pubkey)) {
62
+ logger.debug("[TRUST_PUBKEY] Pubkey trusted: whitelisted", {
63
+ pubkey: pubkey.substring(0, 12),
64
+ });
65
+ return { trusted: true, reason: "whitelisted" };
66
+ }
67
+
68
+ // 2. Check if it's the backend's own pubkey (uses cache)
69
+ const backendPubkey = await this.getBackendPubkey();
70
+ if (backendPubkey && pubkey === backendPubkey) {
71
+ logger.debug("[TRUST_PUBKEY] Pubkey trusted: backend", {
72
+ pubkey: pubkey.substring(0, 12),
73
+ });
74
+ return { trusted: true, reason: "backend" };
75
+ }
76
+
77
+ // 3. Check if it's an agent in the system
78
+ if (this.isAgentPubkey(pubkey)) {
79
+ logger.debug("[TRUST_PUBKEY] Pubkey trusted: agent", {
80
+ pubkey: pubkey.substring(0, 12),
81
+ });
82
+ return { trusted: true, reason: "agent" };
83
+ }
84
+
85
+ // Not trusted
86
+ logger.debug("[TRUST_PUBKEY] Pubkey not trusted", {
87
+ pubkey: pubkey.substring(0, 12),
88
+ });
89
+ return { trusted: false };
90
+ }
91
+
92
+ /**
93
+ * Check if an NDKEvent is from a trusted source.
94
+ * Extracts the pubkey from the event and uses the existing pubkey-based trust logic.
95
+ *
96
+ * @param event The NDKEvent to check
97
+ * @returns TrustResult indicating if the event's author is trusted and why
98
+ */
99
+ async isTrustedEvent(event: NDKEvent): Promise<TrustResult> {
100
+ const pubkey = this.getEventPubkey(event);
101
+
102
+ if (!pubkey) {
103
+ return { trusted: false };
104
+ }
105
+
106
+ return this.isTrusted(pubkey);
107
+ }
108
+
109
+ /**
110
+ * Synchronous version of isTrustedEvent - uses cached backend pubkey.
111
+ * Note: May return false negative for backend pubkey if cache is not initialized.
112
+ * Use initializeBackendPubkeyCache() first if you need sync checks.
113
+ *
114
+ * @param event The NDKEvent to check
115
+ * @returns TrustResult indicating if the event's author is trusted and why
116
+ */
117
+ isTrustedEventSync(event: NDKEvent): TrustResult {
118
+ const pubkey = this.getEventPubkey(event);
119
+
120
+ if (!pubkey) {
121
+ return { trusted: false };
122
+ }
123
+
124
+ return this.isTrustedSync(pubkey);
125
+ }
126
+
127
+ /**
128
+ * Synchronous version of isTrusted - uses cached backend pubkey.
129
+ * Note: May return false negative for backend pubkey if cache is not initialized.
130
+ * Use initializeBackendPubkeyCache() first if you need sync checks.
131
+ *
132
+ * @param pubkey The pubkey to check (hex format)
133
+ * @returns TrustResult indicating if trusted and why
134
+ */
135
+ isTrustedSync(pubkey: Hexpubkey): TrustResult {
136
+ // 1. Check whitelisted pubkeys from config
137
+ if (this.isWhitelisted(pubkey)) {
138
+ return { trusted: true, reason: "whitelisted" };
139
+ }
140
+
141
+ // 2. Check cached backend pubkey
142
+ if (this.cachedBackendPubkey && pubkey === this.cachedBackendPubkey) {
143
+ return { trusted: true, reason: "backend" };
144
+ }
145
+
146
+ // 3. Check if it's an agent in the system
147
+ if (this.isAgentPubkey(pubkey)) {
148
+ return { trusted: true, reason: "agent" };
149
+ }
150
+
151
+ return { trusted: false };
152
+ }
153
+
154
+ /**
155
+ * Initialize the backend pubkey cache for sync operations.
156
+ * Call this during startup if you need to use isTrustedSync.
157
+ */
158
+ async initializeBackendPubkeyCache(): Promise<void> {
159
+ const pubkey = await this.getBackendPubkey();
160
+ if (pubkey) {
161
+ logger.debug("[TRUST_PUBKEY] Backend pubkey cache initialized", {
162
+ pubkey: pubkey.substring(0, 12),
163
+ });
164
+ }
165
+ // Note: getBackendPubkey already logs debug message on failure
166
+ }
167
+
168
+ /**
169
+ * Get all currently trusted pubkeys.
170
+ * Useful for debugging or displaying trust status.
171
+ *
172
+ * De-duplicates entries and enforces precedence (whitelisted > backend > agent).
173
+ * If a pubkey appears in multiple trust sources, only the highest precedence reason is returned.
174
+ *
175
+ * @returns Array of trusted pubkeys with their trust reasons
176
+ */
177
+ async getAllTrustedPubkeys(): Promise<Array<{ pubkey: Hexpubkey; reason: TrustReason }>> {
178
+ // Use a Map to de-duplicate, storing by pubkey with highest precedence reason
179
+ const trustedMap = new Map<Hexpubkey, TrustReason>();
180
+
181
+ // Priority order: whitelisted (1) > backend (2) > agent (3)
182
+ // Lower number = higher priority, so we only set if not already present (first wins)
183
+
184
+ // 1. Add whitelisted pubkeys (highest priority)
185
+ const whitelisted = this.getWhitelistedPubkeys();
186
+ for (const pubkey of whitelisted) {
187
+ if (!trustedMap.has(pubkey)) {
188
+ trustedMap.set(pubkey, "whitelisted");
189
+ }
190
+ }
191
+
192
+ // 2. Add backend pubkey (second priority, uses cache)
193
+ const backendPubkey = await this.getBackendPubkey();
194
+ if (backendPubkey && !trustedMap.has(backendPubkey)) {
195
+ trustedMap.set(backendPubkey, "backend");
196
+ }
197
+
198
+ // 3. Add agent pubkeys (lowest priority)
199
+ const projectCtx = projectContextStore.getContext();
200
+ if (projectCtx) {
201
+ for (const [_slug, agent] of projectCtx.agents) {
202
+ if (agent.pubkey && !trustedMap.has(agent.pubkey)) {
203
+ trustedMap.set(agent.pubkey, "agent");
204
+ }
205
+ }
206
+ }
207
+
208
+ // Convert map to array
209
+ return Array.from(trustedMap.entries()).map(([pubkey, reason]) => ({
210
+ pubkey,
211
+ reason,
212
+ }));
213
+ }
214
+
215
+ // =====================================================================================
216
+ // PRIVATE HELPERS
217
+ // =====================================================================================
218
+
219
+ /**
220
+ * Extract and validate pubkey from an NDKEvent.
221
+ * Returns undefined if the event has no pubkey or an empty pubkey.
222
+ *
223
+ * @param event The NDKEvent to extract pubkey from
224
+ * @returns The pubkey or undefined if not present/valid
225
+ */
226
+ private getEventPubkey(event: NDKEvent): Hexpubkey | undefined {
227
+ const pubkey = event.pubkey;
228
+
229
+ if (!pubkey) {
230
+ logger.debug("[TRUST_PUBKEY] Event has no pubkey", {
231
+ eventId: event.id?.substring(0, 12),
232
+ });
233
+ return undefined;
234
+ }
235
+
236
+ return pubkey;
237
+ }
238
+
239
+ /**
240
+ * Check if pubkey is in the whitelist using cached Set for O(1) lookups.
241
+ */
242
+ private isWhitelisted(pubkey: Hexpubkey): boolean {
243
+ return this.getWhitelistSet().has(pubkey);
244
+ }
245
+
246
+ /**
247
+ * Get the cached whitelist Set, building from config on first access.
248
+ * The set is cached until clearCache() is called; config changes
249
+ * are only reflected after an explicit cache clear.
250
+ */
251
+ private getWhitelistSet(): Set<Hexpubkey> {
252
+ if (!this.cachedWhitelistSet) {
253
+ this.cachedWhitelistSet = new Set(this.getWhitelistedPubkeys());
254
+ }
255
+ return this.cachedWhitelistSet;
256
+ }
257
+
258
+ /**
259
+ * Get whitelisted pubkeys from config
260
+ */
261
+ private getWhitelistedPubkeys(): string[] {
262
+ try {
263
+ return config.getWhitelistedPubkeys(undefined, config.getConfig());
264
+ } catch (error) {
265
+ logger.debug("[TRUST_PUBKEY] Failed to get whitelisted pubkeys from config", {
266
+ error: error instanceof Error ? error.message : String(error),
267
+ });
268
+ return [];
269
+ }
270
+ }
271
+
272
+ /**
273
+ * Get backend pubkey, using cache if available.
274
+ * This method ensures we only fetch from config.getBackendSigner() once,
275
+ * then use the cached value for subsequent calls.
276
+ *
277
+ * @returns The backend pubkey or undefined if not available
278
+ */
279
+ private async getBackendPubkey(): Promise<Hexpubkey | undefined> {
280
+ // Return cached value if available
281
+ if (this.cachedBackendPubkey) {
282
+ return this.cachedBackendPubkey;
283
+ }
284
+
285
+ // Fetch and cache
286
+ try {
287
+ const signer = await config.getBackendSigner();
288
+ this.cachedBackendPubkey = signer.pubkey;
289
+ return this.cachedBackendPubkey;
290
+ } catch (error) {
291
+ logger.debug("[TRUST_PUBKEY] Failed to get backend signer", {
292
+ error: error instanceof Error ? error.message : String(error),
293
+ });
294
+ return undefined;
295
+ }
296
+ }
297
+
298
+ /**
299
+ * Check if pubkey belongs to an agent in the system.
300
+ * Returns false if no project context is available (e.g., during daemon startup
301
+ * or when called outside of projectContextStore.run()).
302
+ */
303
+ private isAgentPubkey(pubkey: Hexpubkey): boolean {
304
+ const projectCtx = projectContextStore.getContext();
305
+ if (!projectCtx) {
306
+ return false;
307
+ }
308
+ return projectCtx.getAgentByPubkey(pubkey) !== undefined;
309
+ }
310
+
311
+ /**
312
+ * Clear all caches (useful for testing and config reloads)
313
+ */
314
+ clearCache(): void {
315
+ this.cachedBackendPubkey = undefined;
316
+ this.cachedWhitelistSet = undefined;
317
+ logger.debug("[TRUST_PUBKEY] Cache cleared");
318
+ }
319
+ }
320
+
321
+ /**
322
+ * Get the TrustPubkeyService singleton instance
323
+ */
324
+ export const getTrustPubkeyService = (): TrustPubkeyService =>
325
+ TrustPubkeyService.getInstance();
@@ -0,0 +1,2 @@
1
+ export { TrustPubkeyService, getTrustPubkeyService } from "./TrustPubkeyService";
2
+ export type { TrustResult, TrustReason } from "./TrustPubkeyService";