@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 fs from "node:fs/promises";
2
+ import { config } from "@/services/ConfigService";
3
+ import * as path from "node:path";
4
+ import { AgentRegistry } from "@/agents/AgentRegistry";
5
+ import { checkSupervisionHealth, registerDefaultHeuristics } from "@/agents/supervision";
6
+ import { ConversationStore } from "@/conversations/ConversationStore";
7
+ import { EventHandler } from "@/event-handler";
8
+ import { NDKMCPTool } from "@/events/NDKMCPTool";
9
+ import { getNDK } from "@/nostr";
10
+ import { ProjectContext } from "@/services/projects";
11
+ import { projectContextStore } from "@/services/projects";
12
+ import { MCPManager } from "@/services/mcp/MCPManager";
13
+ import { McpSubscriptionService } from "@/services/mcp/McpSubscriptionService";
14
+ import { deliverMcpNotification } from "@/services/mcp/McpNotificationDelivery";
15
+ import { installMCPServerFromEvent } from "@/services/mcp/mcpInstaller";
16
+ import { createLocalReportStore, LocalReportStore } from "@/services/reports";
17
+ import { ProjectStatusService } from "@/services/status/ProjectStatusService";
18
+ import { OperationsStatusService } from "@/services/status/OperationsStatusService";
19
+ import { prefixKVStore } from "@/services/storage";
20
+ import { RALRegistry } from "@/services/ral";
21
+ import { getPubkeyService } from "@/services/PubkeyService";
22
+ import { getTrustPubkeyService } from "@/services/trust-pubkeys";
23
+ import { cloneGitRepository, initializeGitRepository } from "@/utils/git";
24
+ import { logger } from "@/utils/logger";
25
+ import type { NDKEvent } from "@nostr-dev-kit/ndk";
26
+ import type { NDKProject } from "@nostr-dev-kit/ndk";
27
+ import { trace, SpanStatusCode } from "@opentelemetry/api";
28
+ import chalk from "chalk";
29
+
30
+ /**
31
+ * Self-contained runtime for a single project.
32
+ * Manages its own lifecycle, status publishing, and event handling.
33
+ */
34
+ export class ProjectRuntime {
35
+ public readonly projectId: string;
36
+ /**
37
+ * Project directory (normal git repository root).
38
+ * Example: ~/tenex/{dTag}
39
+ * The default branch is checked out here directly.
40
+ * Worktrees go in .worktrees/ subdirectory.
41
+ */
42
+ public readonly projectBasePath: string;
43
+ private readonly metadataPath: string; // TENEX metadata path
44
+ private readonly dTag: string;
45
+
46
+ private project: NDKProject;
47
+ private context: ProjectContext | null = null;
48
+ private eventHandler: EventHandler | null = null;
49
+ private statusPublisher: ProjectStatusService | null = null;
50
+ private operationsStatusPublisher: OperationsStatusService | null = null;
51
+ private mcpManager: MCPManager = new MCPManager();
52
+ private localReportStore: LocalReportStore = createLocalReportStore();
53
+
54
+ private isRunning = false;
55
+ private startTime: Date | null = null;
56
+ private lastEventTime: Date | null = null;
57
+ private eventCount = 0;
58
+ private prefixStoreInitialized = false;
59
+
60
+ constructor(project: NDKProject, projectsBase: string) {
61
+ this.project = project;
62
+
63
+ // Build project ID: "31933:authorPubkey:dTag"
64
+ const dTag = project.tagValue("d");
65
+ if (!dTag) {
66
+ throw new Error("Project missing required d tag");
67
+ }
68
+ this.dTag = dTag;
69
+ this.projectId = `31933:${project.pubkey}:${dTag}`;
70
+
71
+ // Project directory: {projectsBase}/{dTag}
72
+ // Normal git repo with default branch checked out.
73
+ // Worktrees go in .worktrees/ subdirectory.
74
+ this.projectBasePath = path.join(projectsBase, dTag);
75
+
76
+ // TENEX metadata (hidden): ~/.tenex/projects/{dTag}
77
+ this.metadataPath = path.join(config.getConfigPath("projects"), dTag);
78
+ }
79
+
80
+ /**
81
+ * Start the project runtime
82
+ */
83
+ async start(): Promise<void> {
84
+ if (this.isRunning) {
85
+ logger.warn(`Project runtime already running: ${this.projectId}`);
86
+ return;
87
+ }
88
+
89
+ const projectTitle = this.project.tagValue("title") || "Untitled";
90
+ console.log(chalk.yellow(`🚀 Starting project: ${chalk.bold(projectTitle)}`));
91
+
92
+ trace.getActiveSpan()?.addEvent("project_runtime.starting", {
93
+ "project.id": this.projectId,
94
+ "project.title": this.project.tagValue("title") ?? "",
95
+ });
96
+
97
+ try {
98
+ // Create TENEX metadata directories: ~/.tenex/projects/<dTag>/{conversations,logs}
99
+ await fs.mkdir(path.join(this.metadataPath, "conversations"), { recursive: true });
100
+ await fs.mkdir(path.join(this.metadataPath, "logs"), { recursive: true });
101
+
102
+ // Clone or init git repository at ~/tenex/<dTag>/
103
+ const repoUrl = this.project.repo;
104
+
105
+ if (repoUrl) {
106
+ trace.getActiveSpan()?.addEvent("project_runtime.cloning_repo", {
107
+ "repo.url": repoUrl,
108
+ });
109
+ const result = await cloneGitRepository(repoUrl, this.projectBasePath);
110
+ if (!result) {
111
+ throw new Error(`Failed to clone repository: ${repoUrl}`);
112
+ }
113
+ } else {
114
+ trace.getActiveSpan()?.addEvent("project_runtime.init_repo");
115
+ await initializeGitRepository(this.projectBasePath);
116
+ }
117
+
118
+ trace.getActiveSpan()?.addEvent("project_runtime.repo_ready", {
119
+ "project.path": this.projectBasePath,
120
+ });
121
+
122
+ // Initialize components
123
+ const agentRegistry = new AgentRegistry(this.projectBasePath, this.metadataPath);
124
+ await agentRegistry.loadFromProject(this.project);
125
+
126
+ // Verify supervision system health (fail-fast if misconfigured)
127
+ await this.verifySupervisionHealth();
128
+
129
+ // Create project context directly (don't use global singleton)
130
+ this.context = new ProjectContext(this.project, agentRegistry);
131
+
132
+ // Initialize prefix KV store and index agent pubkeys
133
+ // This is best-effort - indexing failures don't block project startup
134
+ await prefixKVStore.initialize();
135
+ this.prefixStoreInitialized = true;
136
+ const agentPubkeysForIndex = Array.from(this.context.agents.values()).map(a => a.pubkey);
137
+ try {
138
+ await prefixKVStore.addBatch(agentPubkeysForIndex);
139
+ } catch (error) {
140
+ logger.warn("[ProjectRuntime] Failed to index agent pubkeys for prefix lookup", {
141
+ projectId: this.projectId,
142
+ error: error instanceof Error ? error.message : String(error),
143
+ });
144
+ }
145
+
146
+ // Load MCP tools from project event
147
+ await this.initializeMCPTools();
148
+
149
+ // Set mcpManager on context for use by tools and services
150
+ this.context.mcpManager = this.mcpManager;
151
+
152
+ // Initialize MCP subscription service for resource notification subscriptions
153
+ // Must be done after mcpManager is set on context (subscriptions need MCP access).
154
+ // The notification handler is wrapped in projectContextStore.run() because MCP
155
+ // push notifications fire from SDK transport callbacks outside AsyncLocalStorage scope.
156
+ const capturedContext = this.context;
157
+ await projectContextStore.run(this.context, async () => {
158
+ const mcpSubService = McpSubscriptionService.getInstance();
159
+ mcpSubService.setNotificationHandler(async (subscription, content) => {
160
+ await projectContextStore.run(capturedContext, async () => {
161
+ await deliverMcpNotification(subscription, content);
162
+ });
163
+ });
164
+ await mcpSubService.initialize();
165
+ });
166
+
167
+ // Initialize and set local report store on context for project-scoped storage
168
+ this.localReportStore.initialize(this.metadataPath);
169
+ this.context.localReportStore = this.localReportStore;
170
+
171
+ // NOTE: Nudge whitelist is initialized at daemon level (user-scoped, not project-scoped).
172
+ // See Daemon.ts step 6d.
173
+
174
+ // Initialize conversation store with project path and agent pubkeys
175
+ const agentPubkeys = Array.from(this.context.agents.values()).map(a => a.pubkey);
176
+ ConversationStore.initialize(this.metadataPath, agentPubkeys);
177
+
178
+ // Reconcile any orphaned RALs from a previous daemon run
179
+ await this.reconcileOrphanedRals();
180
+
181
+ // Warm user profile cache for whitelisted pubkeys and project owner
182
+ // This ensures getNameSync() returns real names instead of shortened pubkeys
183
+ // for message attribution in delegations.
184
+ // Must run within projectContextStore.run() since PubkeyService.getAgentSlug
185
+ // needs project context to filter out agent pubkeys.
186
+ await projectContextStore.run(this.context, async () => {
187
+ await this.warmUserProfileCache();
188
+ });
189
+
190
+ // Initialize backend pubkey cache for the pubkey gate.
191
+ // Must happen before EventHandler is initialized so that
192
+ // isTrustedEventSync() can recognize backend-signed events
193
+ // without an async fallback (fail-closed gate).
194
+ await getTrustPubkeyService().initializeBackendPubkeyCache();
195
+
196
+ // Initialize event handler
197
+ this.eventHandler = new EventHandler();
198
+ await this.eventHandler.initialize();
199
+
200
+ // Start status publisher
201
+ this.statusPublisher = new ProjectStatusService();
202
+ const context = this.context;
203
+ context.statusPublisher = this.statusPublisher;
204
+ await projectContextStore.run(context, async () => {
205
+ await this.statusPublisher?.startPublishing(
206
+ this.projectBasePath,
207
+ context
208
+ );
209
+ });
210
+
211
+ // Start operations status publisher (uses RALRegistry for streaming-only semantics)
212
+ // Pass projectId and context for multi-project isolation in daemon mode
213
+ this.operationsStatusPublisher = new OperationsStatusService(
214
+ RALRegistry.getInstance(),
215
+ this.projectId,
216
+ this.context
217
+ );
218
+ this.operationsStatusPublisher.start();
219
+
220
+ this.isRunning = true;
221
+ this.startTime = new Date();
222
+
223
+ logger.info(`Project runtime started successfully: ${this.projectId}`, {
224
+ agentCount: this.context.agents.size,
225
+ pmPubkey: this.context.projectManager?.pubkey?.slice(0, 8),
226
+ });
227
+
228
+ console.log(chalk.green(`✅ Project started: ${chalk.bold(projectTitle)}`));
229
+ console.log(
230
+ chalk.gray(` Agents: ${this.context.agents.size} | Path: ${this.projectBasePath}`)
231
+ );
232
+ console.log();
233
+ } catch (error) {
234
+ // Release prefix store reference if we acquired one during startup
235
+ if (this.prefixStoreInitialized) {
236
+ await prefixKVStore.close();
237
+ this.prefixStoreInitialized = false;
238
+ }
239
+
240
+ const errorMessage = error instanceof Error ? error.message : String(error);
241
+ console.error(chalk.red(`❌ Failed to start project: ${chalk.bold(projectTitle)}`));
242
+ console.error(chalk.red(` ${errorMessage}`));
243
+
244
+ logger.error(`Failed to start project runtime: ${this.projectId}`, {
245
+ error: errorMessage,
246
+ stack: error instanceof Error ? error.stack : undefined,
247
+ });
248
+ throw error;
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Handle an incoming event
254
+ */
255
+ async handleEvent(event: NDKEvent): Promise<void> {
256
+ if (!this.isRunning) {
257
+ throw new Error(`Project runtime not running: ${this.projectId}`);
258
+ }
259
+
260
+ if (!this.context) {
261
+ throw new Error(`Project context not initialized: ${this.projectId}`);
262
+ }
263
+
264
+ // Update stats
265
+ this.lastEventTime = new Date();
266
+ this.eventCount++;
267
+
268
+ // Set project.dtag on active span for trace filtering
269
+ const activeSpan = trace.getActiveSpan();
270
+ if (activeSpan) {
271
+ activeSpan.setAttribute("project.dtag", this.dTag);
272
+ }
273
+
274
+ // Run event handler with the project context
275
+ // AsyncLocalStorage ensures all async operations within this scope
276
+ // have access to the correct project context
277
+ await projectContextStore.run(this.context, async () => {
278
+ if (this.eventHandler) {
279
+ await this.eventHandler.handleEvent(event);
280
+ }
281
+ });
282
+ }
283
+
284
+ /**
285
+ * Stop the project runtime
286
+ */
287
+ async stop(): Promise<void> {
288
+ if (!this.isRunning) {
289
+ logger.warn(`Project runtime already stopped: ${this.projectId}`);
290
+ return;
291
+ }
292
+
293
+ const projectTitle = this.project.tagValue("title") || "Untitled";
294
+ console.log(chalk.yellow(`🛑 Stopping project: ${chalk.bold(projectTitle)}`));
295
+
296
+ trace.getActiveSpan()?.addEvent("project_runtime.stopping", {
297
+ "project.id": this.projectId,
298
+ "uptime_ms": this.startTime ? Date.now() - this.startTime.getTime() : 0,
299
+ "events.processed": this.eventCount,
300
+ });
301
+
302
+ // Stop status publisher
303
+ if (this.statusPublisher) {
304
+ process.stdout.write(chalk.gray(" Stopping status publisher..."));
305
+ await this.statusPublisher.stopPublishing();
306
+ this.statusPublisher = null;
307
+ console.log(chalk.gray(" done"));
308
+ }
309
+
310
+ // Stop operations status publisher
311
+ if (this.operationsStatusPublisher) {
312
+ process.stdout.write(chalk.gray(" Stopping operations status..."));
313
+ this.operationsStatusPublisher.stop();
314
+ this.operationsStatusPublisher = null;
315
+ console.log(chalk.gray(" done"));
316
+ }
317
+
318
+ // Cleanup event handler
319
+ if (this.eventHandler) {
320
+ process.stdout.write(chalk.gray(" Cleaning up event handler..."));
321
+ await this.eventHandler.cleanup();
322
+ this.eventHandler = null;
323
+ console.log(chalk.gray(" done"));
324
+ }
325
+
326
+ // Shutdown MCP subscription service
327
+ try {
328
+ const mcpSubService = McpSubscriptionService.getInstance();
329
+ await mcpSubService.shutdown();
330
+ } catch {
331
+ // Service may not be initialized
332
+ }
333
+
334
+ // Save conversation state
335
+ process.stdout.write(chalk.gray(" Saving conversations..."));
336
+ await ConversationStore.cleanup();
337
+ console.log(chalk.gray(" done"));
338
+
339
+ // Reset local report store
340
+ process.stdout.write(chalk.gray(" Resetting report store..."));
341
+ this.localReportStore.reset();
342
+ console.log(chalk.gray(" done"));
343
+
344
+ // Release our reference to the prefix KV store (but don't close it -
345
+ // it's a daemon-global resource that outlives individual project runtimes)
346
+ process.stdout.write(chalk.gray(" Releasing storage..."));
347
+ await prefixKVStore.close();
348
+ console.log(chalk.gray(" done"));
349
+
350
+ // Clear context
351
+ this.context = null;
352
+
353
+ this.isRunning = false;
354
+
355
+ logger.info(`Project runtime stopped: ${this.projectId}`);
356
+
357
+ const uptime = this.startTime
358
+ ? Math.round((Date.now() - this.startTime.getTime()) / 1000)
359
+ : 0;
360
+ console.log(chalk.green(`✅ Project stopped: ${chalk.bold(projectTitle)}`));
361
+ console.log(chalk.gray(` Uptime: ${uptime}s | Events processed: ${this.eventCount}`));
362
+ }
363
+
364
+ /**
365
+ * Get runtime status
366
+ */
367
+ getStatus(): {
368
+ isRunning: boolean;
369
+ projectId: string;
370
+ title: string;
371
+ startTime: Date | null;
372
+ lastEventTime: Date | null;
373
+ eventCount: number;
374
+ agentCount: number;
375
+ } {
376
+ return {
377
+ isRunning: this.isRunning,
378
+ projectId: this.projectId,
379
+ title: this.project.tagValue("title") || "Untitled",
380
+ startTime: this.startTime,
381
+ lastEventTime: this.lastEventTime,
382
+ eventCount: this.eventCount,
383
+ agentCount: this.context?.agents.size || 0,
384
+ };
385
+ }
386
+
387
+ /**
388
+ * Get the project context (if running)
389
+ */
390
+ getContext(): ProjectContext | null {
391
+ return this.context;
392
+ }
393
+
394
+ /**
395
+ * Check if runtime is running
396
+ */
397
+ isActive(): boolean {
398
+ return this.isRunning;
399
+ }
400
+
401
+ /**
402
+ * Initialize MCP tools from the project event
403
+ * Extracts MCP tool event IDs from "mcp" tags, fetches and installs them
404
+ */
405
+ private async initializeMCPTools(): Promise<void> {
406
+ try {
407
+ // Extract MCP tool event IDs from the project
408
+ const mcpEventIds = this.project.tags
409
+ .filter((tag) => tag[0] === "mcp" && tag[1])
410
+ .map((tag) => tag[1])
411
+ .filter((id): id is string => typeof id === "string");
412
+
413
+ trace.getActiveSpan()?.addEvent("project_runtime.mcp_tools_found", {
414
+ "mcp.count": mcpEventIds.length,
415
+ });
416
+
417
+ if (mcpEventIds.length === 0) {
418
+ return;
419
+ }
420
+
421
+ const ndk = getNDK();
422
+ const installedCount = { success: 0, failed: 0 };
423
+
424
+ // Fetch and install each MCP tool
425
+ for (const eventId of mcpEventIds) {
426
+ try {
427
+ trace.getActiveSpan()?.addEvent("project_runtime.mcp_fetching", {
428
+ "mcp.event_id": eventId.substring(0, 12),
429
+ });
430
+ const mcpEvent = await ndk.fetchEvent(eventId);
431
+
432
+ if (!mcpEvent) {
433
+ logger.warn(
434
+ `[ProjectRuntime] MCP tool event not found: ${eventId.substring(0, 12)}`
435
+ );
436
+ installedCount.failed++;
437
+ continue;
438
+ }
439
+
440
+ const mcpTool = NDKMCPTool.from(mcpEvent);
441
+ trace.getActiveSpan()?.addEvent("project_runtime.mcp_installing", {
442
+ "mcp.name": mcpTool.name ?? "unnamed",
443
+ "mcp.event_id": eventId.substring(0, 12),
444
+ });
445
+
446
+ await installMCPServerFromEvent(this.metadataPath, mcpTool);
447
+ installedCount.success++;
448
+
449
+ trace.getActiveSpan()?.addEvent("project_runtime.mcp_installed", {
450
+ "mcp.name": mcpTool.name ?? "",
451
+ "mcp.slug": mcpTool.slug ?? "",
452
+ });
453
+ } catch (error) {
454
+ logger.error("[ProjectRuntime] Failed to install MCP tool", {
455
+ eventId: eventId.substring(0, 12),
456
+ error: error instanceof Error ? error.message : String(error),
457
+ });
458
+ installedCount.failed++;
459
+ }
460
+ }
461
+
462
+ trace.getActiveSpan()?.addEvent("project_runtime.mcp_installation_complete", {
463
+ "mcp.total": mcpEventIds.length,
464
+ "mcp.success": installedCount.success,
465
+ "mcp.failed": installedCount.failed,
466
+ });
467
+
468
+ // Initialize MCP service if any tools were installed
469
+ if (installedCount.success > 0) {
470
+ await this.mcpManager.initialize(this.metadataPath, this.projectBasePath);
471
+
472
+ const runningServers = this.mcpManager.getRunningServers();
473
+ const availableTools = Object.keys(this.mcpManager.getCachedTools());
474
+
475
+ trace.getActiveSpan()?.addEvent("project_runtime.mcp_service_initialized", {
476
+ "mcp.running_servers": runningServers.length,
477
+ "mcp.available_tools": availableTools.length,
478
+ });
479
+ } else {
480
+ logger.warn(
481
+ "[ProjectRuntime] No MCP tools were successfully installed, skipping MCP service initialization"
482
+ );
483
+ }
484
+ } catch (error) {
485
+ logger.error("[ProjectRuntime] Failed to initialize MCP tools", {
486
+ error: error instanceof Error ? error.message : String(error),
487
+ stack: error instanceof Error ? error.stack : undefined,
488
+ });
489
+ // Don't throw - allow project to start even if MCP initialization fails
490
+ }
491
+ }
492
+
493
+ /**
494
+ * Warm the user profile cache for whitelisted pubkeys and project owner.
495
+ * This ensures that getNameSync() returns real user names instead of shortened pubkeys
496
+ * when attributing messages in delegations.
497
+ */
498
+ private async warmUserProfileCache(): Promise<void> {
499
+ try {
500
+ const { config: loadedConfig } = await config.loadConfig();
501
+ const pubkeysToWarm: Set<string> = new Set();
502
+
503
+ // Add whitelisted pubkeys
504
+ const whitelistedPubkeys = loadedConfig.whitelistedPubkeys ?? [];
505
+ for (const pk of whitelistedPubkeys) {
506
+ if (pk) pubkeysToWarm.add(pk);
507
+ }
508
+
509
+ // Add project owner pubkey
510
+ if (this.project.pubkey) {
511
+ pubkeysToWarm.add(this.project.pubkey);
512
+ }
513
+
514
+ if (pubkeysToWarm.size === 0) {
515
+ logger.debug("[ProjectRuntime] No user pubkeys to warm");
516
+ return;
517
+ }
518
+
519
+ trace.getActiveSpan()?.addEvent("project_runtime.warming_user_profiles", {
520
+ "profiles.count": pubkeysToWarm.size,
521
+ });
522
+
523
+ const pubkeyService = getPubkeyService();
524
+ const results = await pubkeyService.warmUserProfiles(Array.from(pubkeysToWarm));
525
+
526
+ logger.debug("[ProjectRuntime] Warmed user profile cache", {
527
+ projectId: this.projectId,
528
+ count: results.size,
529
+ });
530
+
531
+ trace.getActiveSpan()?.addEvent("project_runtime.user_profiles_warmed", {
532
+ "profiles.warmed": results.size,
533
+ });
534
+ } catch (error) {
535
+ // Don't block startup if profile warming fails
536
+ logger.warn("[ProjectRuntime] Failed to warm user profile cache", {
537
+ projectId: this.projectId,
538
+ error: error instanceof Error ? error.message : String(error),
539
+ });
540
+ }
541
+ }
542
+
543
+ /**
544
+ * Scan all persisted conversations and reconcile orphaned RALs.
545
+ * An orphaned RAL is one that's marked as "active" in ConversationStore
546
+ * but doesn't exist in RALRegistry (because daemon was restarted).
547
+ */
548
+ private async reconcileOrphanedRals(): Promise<void> {
549
+ const conversationsDir = path.join(this.metadataPath, "conversations");
550
+
551
+ let files: string[];
552
+ try {
553
+ files = await fs.readdir(conversationsDir);
554
+ } catch {
555
+ return; // No conversations directory yet
556
+ }
557
+
558
+ const ralRegistry = RALRegistry.getInstance();
559
+ let totalReconciled = 0;
560
+
561
+ for (const file of files) {
562
+ if (!file.endsWith(".json")) continue;
563
+
564
+ const conversationId = file.replace(".json", "");
565
+ const store = ConversationStore.getOrLoad(conversationId);
566
+
567
+ const allActiveRals = store.getAllActiveRals();
568
+ let modified = false;
569
+
570
+ for (const [agentPubkey, ralNumbers] of allActiveRals) {
571
+ for (const ralNumber of ralNumbers) {
572
+ // RALRegistry is empty after restart - any active RAL in ConversationStore is orphaned
573
+ const exists = ralRegistry.getRAL(agentPubkey, conversationId, ralNumber);
574
+ if (!exists) {
575
+ logger.info(`[ProjectRuntime] Reconciling orphaned RAL #${ralNumber}`, {
576
+ conversationId: conversationId.substring(0, 8),
577
+ agentPubkey: agentPubkey.substring(0, 8),
578
+ });
579
+
580
+ store.addMessage({
581
+ pubkey: agentPubkey,
582
+ ral: ralNumber,
583
+ content: `⚠️ RAL #${ralNumber} was interrupted (daemon restart). Work may be incomplete.`,
584
+ messageType: "text",
585
+ });
586
+ store.completeRal(agentPubkey, ralNumber);
587
+ modified = true;
588
+ totalReconciled++;
589
+ }
590
+ }
591
+ }
592
+
593
+ if (modified) {
594
+ await store.save();
595
+ }
596
+ }
597
+
598
+ if (totalReconciled > 0) {
599
+ logger.info(`[ProjectRuntime] Reconciled ${totalReconciled} orphaned RAL(s)`);
600
+ }
601
+ }
602
+
603
+ /**
604
+ * Verify supervision system health at startup.
605
+ * Ensures heuristics are registered and the supervision system is properly configured.
606
+ * This is a fail-fast check that prevents the daemon from running without supervision.
607
+ *
608
+ * Uses centralized health check for consistent fail-closed semantics across all entry points.
609
+ */
610
+ private async verifySupervisionHealth(): Promise<void> {
611
+ const tracer = trace.getTracer("tenex.project-runtime");
612
+
613
+ return tracer.startActiveSpan("tenex.supervision.health_check", async (span) => {
614
+ span.setAttribute("project.id", this.projectId);
615
+
616
+ // Ensure heuristics are registered before checking health
617
+ registerDefaultHeuristics();
618
+
619
+ // Use centralized health check for consistent validation
620
+ const healthResult = checkSupervisionHealth();
621
+
622
+ span.setAttributes({
623
+ "supervision.registry_size": healthResult.registrySize,
624
+ "supervision.heuristic_ids": healthResult.heuristicIds.join(","),
625
+ "supervision.post_completion_count": healthResult.postCompletionCount,
626
+ "supervision.healthy": healthResult.healthy,
627
+ });
628
+
629
+ if (!healthResult.healthy) {
630
+ const errorMessage = `[ProjectRuntime] ${healthResult.errorMessage}`;
631
+
632
+ logger.error(errorMessage);
633
+ span.recordException(new Error(errorMessage));
634
+ span.setStatus({ code: SpanStatusCode.ERROR, message: healthResult.errorMessage });
635
+ span.end();
636
+
637
+ throw new Error(errorMessage);
638
+ }
639
+
640
+ span.addEvent("supervision.health_check_passed", {
641
+ "heuristics.count": healthResult.registrySize,
642
+ "heuristics.post_completion_count": healthResult.postCompletionCount,
643
+ });
644
+
645
+ span.setStatus({ code: SpanStatusCode.OK });
646
+
647
+ logger.info("[ProjectRuntime] Supervision system health check passed", {
648
+ projectId: this.projectId,
649
+ heuristicCount: healthResult.registrySize,
650
+ heuristicIds: healthResult.heuristicIds,
651
+ postCompletionCount: healthResult.postCompletionCount,
652
+ });
653
+
654
+ span.end();
655
+ });
656
+ }
657
+ }