@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,561 @@
1
+ /**
2
+ * AgentExecutor - Orchestrates agent execution with LLM streaming
3
+ *
4
+ * This is the main entry point for executing agent tasks. It coordinates:
5
+ * - RAL (Request/Assignement/Loop) lifecycle management
6
+ * - Stream setup and execution via extracted modules
7
+ * - Post-completion supervision checks
8
+ * - Event publishing
9
+ *
10
+ * The heavy lifting is delegated to:
11
+ * - StreamSetup: Pre-stream configuration (tools, messages, injections)
12
+ * - StreamExecutionHandler: LLM streaming with event processing
13
+ * - PostCompletionChecker: Supervision heuristics
14
+ * - RALResolver: RAL lifecycle resolution
15
+ */
16
+
17
+ import { assertSupervisionHealth } from "@/agents/supervision";
18
+ import { checkPostCompletion } from "./PostCompletionChecker";
19
+ import { resolveRAL } from "./RALResolver";
20
+ import { ConversationStore } from "@/conversations/ConversationStore";
21
+ import { startExecutionTime, stopExecutionTime } from "@/conversations/executionTime";
22
+ import { formatAnyError } from "@/lib/error-formatter";
23
+ import { shortenConversationId } from "@/utils/conversation-id";
24
+ import { AgentPublisher } from "@/nostr/AgentPublisher";
25
+ import { INJECTION_ABORT_REASON } from "@/services/LLMOperationsRegistry";
26
+ import { getProjectContext } from "@/services/projects";
27
+ import { RALRegistry } from "@/services/ral";
28
+ import { getPubkeyService } from "@/services/PubkeyService";
29
+ import { getToolsObject } from "@/tools/registry";
30
+ import type { ToolRegistryContext } from "@/tools/types";
31
+ import { logger } from "@/utils/logger";
32
+ import { createEventContext } from "@/services/event-context";
33
+ import type { NDKEvent } from "@nostr-dev-kit/ndk";
34
+ import { SpanStatusCode, context as otelContext, trace } from "@opentelemetry/api";
35
+ import type { ModelMessage } from "ai";
36
+ import { ToolExecutionTracker } from "./ToolExecutionTracker";
37
+ import { setupStreamExecution } from "./StreamSetup";
38
+ import { StreamExecutionHandler } from "./StreamExecutionHandler";
39
+ import type {
40
+ ExecutionContext,
41
+ FullRuntimeContext,
42
+ LLMCompletionRequest,
43
+ StreamExecutionResult,
44
+ } from "./types";
45
+
46
+ const tracer = trace.getTracer("tenex.agent-executor");
47
+
48
+ export class AgentExecutor {
49
+ constructor() {
50
+ // Centralized supervision health check - ensures both total registry size AND
51
+ // post-completion heuristic count are validated (fail-closed semantics)
52
+ assertSupervisionHealth("AgentExecutor");
53
+ }
54
+
55
+ /**
56
+ * Warm user profile cache for injection sender pubkeys (best-effort, non-blocking).
57
+ */
58
+ private warmSenderPubkeys(injections: Array<{ senderPubkey?: string }>): void {
59
+ const senderPubkeys = injections
60
+ .map((i) => i.senderPubkey)
61
+ .filter((pk): pk is string => !!pk);
62
+
63
+ if (senderPubkeys.length > 0) {
64
+ const pubkeyService = getPubkeyService();
65
+ void pubkeyService.warmUserProfiles(senderPubkeys).catch((error) => {
66
+ logger.debug("[AgentExecutor] Best-effort profile warming failed", {
67
+ error: error instanceof Error ? error.message : String(error),
68
+ });
69
+ });
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Prepare an LLM request without executing it.
75
+ * Creates stub context for tool schema extraction - runtime deps are never called.
76
+ */
77
+ async prepareLLMRequest(
78
+ agent: { slug: string; tools?: string[] },
79
+ initialPrompt: string,
80
+ originalEvent: NDKEvent,
81
+ conversationHistory: ModelMessage[] = [],
82
+ projectPath?: string
83
+ ): Promise<LLMCompletionRequest> {
84
+ const context: ToolRegistryContext = {
85
+ agent: agent as ToolRegistryContext["agent"],
86
+ triggeringEvent: originalEvent,
87
+ conversationId: originalEvent.id,
88
+ projectBasePath: projectPath || "",
89
+ workingDirectory: projectPath || "",
90
+ currentBranch: "main",
91
+ agentPublisher: {} as AgentPublisher,
92
+ ralNumber: 0,
93
+ conversationStore: {} as ConversationStore,
94
+ getConversation: () => ({} as ConversationStore),
95
+ };
96
+
97
+ const messages: ModelMessage[] = conversationHistory.length > 0
98
+ ? [...conversationHistory]
99
+ : [{ role: "user", content: initialPrompt }];
100
+
101
+ const toolNames = agent.tools || [];
102
+ const tools = toolNames.length > 0 ? getToolsObject(toolNames, context) : {};
103
+
104
+ return { messages, tools };
105
+ }
106
+
107
+ /**
108
+ * Execute an agent's assignment for a conversation with streaming
109
+ */
110
+ async execute(context: ExecutionContext): Promise<NDKEvent | undefined> {
111
+ const span = tracer.startSpan("tenex.agent.execute", {
112
+ attributes: {
113
+ "agent.slug": context.agent.slug,
114
+ "agent.pubkey": context.agent.pubkey,
115
+ "agent.role": context.agent.role || "worker",
116
+ "conversation.id": shortenConversationId(context.conversationId),
117
+ "triggering_event.id": context.triggeringEvent.id,
118
+ "triggering_event.kind": context.triggeringEvent.kind || 0,
119
+ },
120
+ }, otelContext.active());
121
+
122
+ return otelContext.with(trace.setSpan(otelContext.active(), span), async () => {
123
+ try {
124
+ // Get project ID for multi-project isolation in daemon mode
125
+ const projectCtx = getProjectContext();
126
+ const projectId = projectCtx.project.tagId();
127
+
128
+ const { ralNumber, isResumption, markersToPublish } = await resolveRAL({
129
+ agentPubkey: context.agent.pubkey,
130
+ conversationId: context.conversationId,
131
+ projectId,
132
+ triggeringEventId: context.triggeringEvent.id,
133
+ span,
134
+ });
135
+
136
+ // RACE CONDITION FIX: Early kill check
137
+ // If this conversation was killed before the agent started (or during RAL resolution),
138
+ // abort immediately without spending compute resources.
139
+ const ralRegistry = RALRegistry.getInstance();
140
+ if (ralRegistry.isAgentConversationKilled(context.agent.pubkey, context.conversationId)) {
141
+ span.addEvent("executor.aborted_early_kill", {
142
+ "ral.number": ralNumber,
143
+ "agent.pubkey": context.agent.pubkey.substring(0, 12),
144
+ "conversation.id": shortenConversationId(context.conversationId),
145
+ });
146
+
147
+ logger.info("[AgentExecutor] Execution aborted - conversation was killed before agent started", {
148
+ agent: context.agent.slug,
149
+ conversationId: shortenConversationId(context.conversationId),
150
+ ralNumber,
151
+ });
152
+
153
+ // Clean up the RAL we just created since we're not going to use it
154
+ ralRegistry.clear(context.agent.pubkey, context.conversationId);
155
+
156
+ span.setStatus({ code: SpanStatusCode.OK, message: "aborted_early_kill" });
157
+ return undefined;
158
+ }
159
+
160
+ const contextWithRal = { ...context, ralNumber };
161
+ const { fullContext, toolTracker, agentPublisher, cleanup } =
162
+ this.prepareExecution(contextWithRal);
163
+
164
+ // Publish delegation marker updates to Nostr
165
+ // This happens after RAL resolution when delegations have completed
166
+ if (markersToPublish && markersToPublish.length > 0) {
167
+ span.addEvent("executor.publishing_delegation_markers", {
168
+ "marker.count": markersToPublish.length,
169
+ });
170
+
171
+ for (const marker of markersToPublish) {
172
+ try {
173
+ await agentPublisher.delegationMarker(marker);
174
+ } catch (error) {
175
+ logger.warn("Failed to publish delegation marker", {
176
+ delegationConversationId: marker.delegationConversationId.substring(0, 12),
177
+ status: marker.status,
178
+ error: formatAnyError(error),
179
+ });
180
+ }
181
+ }
182
+ }
183
+
184
+ const conversation = fullContext.getConversation();
185
+ if (conversation) {
186
+ span.setAttributes({
187
+ "conversation.message_count": conversation.getMessageCount(),
188
+ });
189
+ }
190
+
191
+ span.addEvent("executor.started", {
192
+ ral_number: ralNumber,
193
+ is_resumption: isResumption,
194
+ });
195
+
196
+ try {
197
+ const result = await this.executeOnce(
198
+ fullContext,
199
+ toolTracker,
200
+ agentPublisher,
201
+ ralNumber
202
+ );
203
+
204
+ span.setStatus({ code: SpanStatusCode.OK });
205
+ return result;
206
+ } finally {
207
+ await cleanup();
208
+ }
209
+ } catch (error) {
210
+ span.recordException(error as Error);
211
+ span.setStatus({ code: SpanStatusCode.ERROR });
212
+
213
+ const errorMessage = formatAnyError(error);
214
+ const isCreditsError =
215
+ errorMessage.includes("Insufficient credits") || errorMessage.includes("402");
216
+
217
+ const displayMessage = isCreditsError
218
+ ? "Unable to process your request: Insufficient credits. Please add more credits at https://openrouter.ai/settings/credits to continue."
219
+ : `Unable to process your request due to an error: ${errorMessage}`;
220
+
221
+ const conversation = context.getConversation();
222
+ if (conversation) {
223
+ const agentPublisher = new AgentPublisher(context.agent);
224
+ try {
225
+ await agentPublisher.error(
226
+ {
227
+ message: displayMessage,
228
+ errorType: isCreditsError ? "insufficient_credits" : "execution_error",
229
+ },
230
+ {
231
+ triggeringEvent: context.triggeringEvent,
232
+ rootEvent: { id: conversation.getRootEventId() },
233
+ conversationId: conversation.id,
234
+ ralNumber: 0,
235
+ }
236
+ );
237
+ } catch (publishError) {
238
+ logger.error("Failed to publish execution error event", {
239
+ error: formatAnyError(publishError),
240
+ });
241
+ }
242
+ }
243
+
244
+ logger.error(
245
+ isCreditsError
246
+ ? "[AgentExecutor] Execution failed due to insufficient credits"
247
+ : "[AgentExecutor] Execution failed",
248
+ {
249
+ agent: context.agent.slug,
250
+ error: errorMessage,
251
+ conversationId: context.conversationId,
252
+ }
253
+ );
254
+
255
+ throw error;
256
+ } finally {
257
+ span.end();
258
+ }
259
+ });
260
+ }
261
+
262
+ /**
263
+ * Prepare execution context with all necessary components.
264
+ */
265
+ private prepareExecution(
266
+ context: ExecutionContext & { ralNumber: number }
267
+ ): {
268
+ fullContext: FullRuntimeContext;
269
+ toolTracker: ToolExecutionTracker;
270
+ agentPublisher: AgentPublisher;
271
+ cleanup: () => Promise<void>;
272
+ } {
273
+ const toolTracker = new ToolExecutionTracker();
274
+ const agentPublisher = new AgentPublisher(context.agent);
275
+ const conversationStore = ConversationStore.getOrLoad(context.conversationId);
276
+ const projectContext = getProjectContext();
277
+
278
+ const fullContext: FullRuntimeContext = {
279
+ agent: context.agent,
280
+ conversationId: context.conversationId,
281
+ projectBasePath: context.projectBasePath,
282
+ workingDirectory: context.workingDirectory,
283
+ currentBranch: context.currentBranch,
284
+ triggeringEvent: context.triggeringEvent,
285
+ agentPublisher,
286
+ ralNumber: context.ralNumber,
287
+ conversationStore,
288
+ getConversation: () => conversationStore,
289
+ mcpManager: projectContext.mcpManager,
290
+ isDelegationCompletion: context.isDelegationCompletion,
291
+ hasPendingDelegations: context.hasPendingDelegations,
292
+ };
293
+
294
+ const conversation = fullContext.getConversation();
295
+ startExecutionTime(conversation);
296
+
297
+ const cleanup = async (): Promise<void> => {
298
+ stopExecutionTime(conversation);
299
+ toolTracker.clear();
300
+ };
301
+
302
+ return { fullContext, toolTracker, agentPublisher, cleanup };
303
+ }
304
+
305
+ /**
306
+ * Execute streaming and publish result
307
+ */
308
+ private async executeOnce(
309
+ context: FullRuntimeContext,
310
+ toolTracker: ToolExecutionTracker,
311
+ agentPublisher: AgentPublisher,
312
+ ralNumber: number
313
+ ): Promise<NDKEvent | undefined> {
314
+ let result: StreamExecutionResult;
315
+
316
+ try {
317
+ result = await this.executeStreaming(context, toolTracker, ralNumber);
318
+ } catch (streamError) {
319
+ logger.error("[AgentExecutor] Streaming failed", {
320
+ agent: context.agent.slug,
321
+ error: formatAnyError(streamError),
322
+ });
323
+ throw streamError;
324
+ }
325
+
326
+ if (result.kind === "error-handled") {
327
+ return undefined;
328
+ }
329
+
330
+ if (result.aborted) {
331
+ if (result.abortReason === INJECTION_ABORT_REASON) {
332
+ trace.getActiveSpan()?.addEvent("executor.aborted_for_injection", {
333
+ "ral.number": ralNumber,
334
+ "agent.slug": context.agent.slug,
335
+ });
336
+ logger.info("[AgentExecutor] Execution aborted for injection - silent return", {
337
+ agent: context.agent.slug,
338
+ ralNumber,
339
+ });
340
+ return undefined;
341
+ }
342
+
343
+ const eventContext = createEventContext(context);
344
+ const responseEvent = await agentPublisher.complete(
345
+ { content: "Manually stopped by user" },
346
+ eventContext
347
+ );
348
+ // Handle case where completion was skipped (conversation was killed)
349
+ if (responseEvent) {
350
+ await ConversationStore.addEvent(context.conversationId, responseEvent);
351
+ }
352
+ return responseEvent;
353
+ }
354
+
355
+ const completionEvent = result.event;
356
+ const ralRegistry = RALRegistry.getInstance();
357
+
358
+ // =====================================================================
359
+ // RACE CONDITION FIX: Check for ANY outstanding work, not just pending delegations
360
+ // =====================================================================
361
+ // This is the key guard against the race condition where delegation results arrive
362
+ // (via debounce in AgentDispatchService) after the last prepareStep but before
363
+ // the executor finalizes. The debounce state queues injections that would be
364
+ // invisible if we only checked pendingDelegations.
365
+ //
366
+ // hasOutstandingWork() consolidates checking for:
367
+ // 1. Queued injections (messages/delegation results waiting for next LLM step)
368
+ // 2. Pending delegations (delegations that haven't completed yet)
369
+ // =====================================================================
370
+ const outstandingWork = ralRegistry.hasOutstandingWork(
371
+ context.agent.pubkey,
372
+ context.conversationId,
373
+ ralNumber
374
+ );
375
+
376
+ // NOTE: startedWithPendingDelegations is a snapshot from dispatch time, used ONLY for
377
+ // conservative RAL lifetime management (line ~395). It should NOT be used for the publish
378
+ // mode decision because delegations may have completed during execution.
379
+ const startedWithPendingDelegations = Boolean(
380
+ context.isDelegationCompletion && context.hasPendingDelegations
381
+ );
382
+
383
+ // DIAGNOSTIC: Trace the exact values used in outstanding work decision
384
+ trace.getActiveSpan()?.addEvent("executor.outstanding_work_decision", {
385
+ "context.isDelegationCompletion": context.isDelegationCompletion ?? false,
386
+ "context.hasPendingDelegations_snapshot": context.hasPendingDelegations ?? false,
387
+ "startedWithPendingDelegations_for_ral_cleanup": startedWithPendingDelegations,
388
+ "outstanding.has_work": outstandingWork.hasWork,
389
+ "outstanding.queued_injections": outstandingWork.details.queuedInjections,
390
+ "outstanding.pending_delegations": outstandingWork.details.pendingDelegations,
391
+ "fix_applied": "uses hasOutstandingWork() to check both injections and delegations",
392
+ });
393
+
394
+ // INVARIANT GUARD: If there's outstanding work (queued injections OR pending delegations),
395
+ // we should NOT finalize. The executor should continue to allow the work to be processed.
396
+ const hasMessageContent = completionEvent?.message && completionEvent.message.length > 0;
397
+ if (!hasMessageContent && outstandingWork.hasWork) {
398
+ // This is the expected path when delegation results arrive via debounce.
399
+ // The executor returns undefined to allow the dispatch loop to continue
400
+ // and process the queued injections in the next iteration.
401
+ trace.getActiveSpan()?.addEvent("executor.awaiting_outstanding_work", {
402
+ "ral.number": ralNumber,
403
+ "outstanding.queued_injections": outstandingWork.details.queuedInjections,
404
+ "outstanding.pending_delegations": outstandingWork.details.pendingDelegations,
405
+ "completion_event_exists": Boolean(completionEvent),
406
+ "scenario": "injection_debounce_await",
407
+ });
408
+ logger.debug("[AgentExecutor] Deferring finalization due to outstanding work", {
409
+ agent: context.agent.slug,
410
+ ralNumber,
411
+ queuedInjections: outstandingWork.details.queuedInjections,
412
+ pendingDelegations: outstandingWork.details.pendingDelegations,
413
+ });
414
+ return undefined;
415
+ }
416
+
417
+ if (!completionEvent) {
418
+ // This is an unexpected state: no completion event AND no outstanding work.
419
+ // The LLM stream should always produce a completion event if it completes normally.
420
+ // Log extensively before throwing to aid debugging.
421
+ logger.error("[AgentExecutor] Missing completion event with no outstanding work", {
422
+ agent: context.agent.slug,
423
+ ralNumber,
424
+ conversationId: context.conversationId.substring(0, 12),
425
+ hasOutstandingWork: outstandingWork.hasWork,
426
+ });
427
+ trace.getActiveSpan()?.addEvent("executor.missing_completion_event_error", {
428
+ "ral.number": ralNumber,
429
+ "outstanding.has_work": outstandingWork.hasWork,
430
+ "scenario": "unexpected_missing_completion",
431
+ });
432
+ throw new Error("LLM execution completed without producing a completion event");
433
+ }
434
+
435
+ // Post-completion supervision check
436
+ const supervisionCheckResult = await checkPostCompletion({
437
+ agent: context.agent,
438
+ context,
439
+ conversationStore: context.conversationStore,
440
+ ralNumber,
441
+ completionEvent,
442
+ });
443
+
444
+ if (supervisionCheckResult.shouldReEngage) {
445
+ return this.executeOnce(context, toolTracker, agentPublisher, ralNumber);
446
+ }
447
+
448
+ // RAL cleanup - use hasOutstandingWork for comprehensive check
449
+ const conversationStore = context.conversationStore;
450
+ const cleanupOutstandingWork = ralRegistry.hasOutstandingWork(
451
+ context.agent.pubkey,
452
+ context.conversationId,
453
+ ralNumber
454
+ );
455
+
456
+ if (!cleanupOutstandingWork.hasWork && !startedWithPendingDelegations) {
457
+ ralRegistry.clearRAL(context.agent.pubkey, context.conversationId, ralNumber);
458
+ conversationStore.completeRal(context.agent.pubkey, ralNumber);
459
+ await conversationStore.save();
460
+
461
+ trace.getActiveSpan()?.addEvent("executor.ral_cleared_post_supervision_check", {
462
+ "ral.number": ralNumber,
463
+ "supervision.executed": true,
464
+ "supervision.had_violation": supervisionCheckResult.shouldReEngage,
465
+ });
466
+ } else if (!cleanupOutstandingWork.hasWork && startedWithPendingDelegations) {
467
+ trace.getActiveSpan()?.addEvent("executor.ral_clear_skipped_pending_at_start", {
468
+ "ral.number": ralNumber,
469
+ "supervision.executed": true,
470
+ });
471
+ }
472
+
473
+ const eventContext = createEventContext(context, {
474
+ model: completionEvent?.usage?.model,
475
+ });
476
+
477
+ // Re-check outstanding work for final publish decision (state may have changed after supervision)
478
+ const finalOutstandingWork = ralRegistry.hasOutstandingWork(
479
+ context.agent.pubkey,
480
+ context.conversationId,
481
+ ralNumber
482
+ );
483
+
484
+ trace.getActiveSpan()?.addEvent("executor.publish", {
485
+ "message.length": completionEvent?.message?.length || 0,
486
+ "outstanding.has_work": finalOutstandingWork.hasWork,
487
+ "outstanding.queued_injections": finalOutstandingWork.details.queuedInjections,
488
+ "outstanding.pending_delegations": finalOutstandingWork.details.pendingDelegations,
489
+ });
490
+
491
+ let responseEvent: NDKEvent | undefined;
492
+
493
+ if (finalOutstandingWork.hasWork) {
494
+ // If there's outstanding work, publish as conversation (not completion)
495
+ if (completionEvent.message.trim().length > 0) {
496
+ responseEvent = await agentPublisher.conversation(
497
+ { content: completionEvent.message, usage: completionEvent.usage },
498
+ eventContext
499
+ );
500
+ }
501
+ } else {
502
+ // No outstanding work - safe to publish as complete
503
+ responseEvent = await agentPublisher.complete(
504
+ { content: completionEvent.message, usage: completionEvent.usage },
505
+ eventContext
506
+ );
507
+ }
508
+
509
+ if (responseEvent) {
510
+ await ConversationStore.addEvent(context.conversationId, responseEvent);
511
+
512
+ trace.getActiveSpan()?.addEvent("executor.published", {
513
+ "event.id": responseEvent.id || "",
514
+ is_completion: !finalOutstandingWork.hasWork,
515
+ });
516
+
517
+ result.messageCompiler.advanceCursor();
518
+ }
519
+
520
+ return responseEvent;
521
+ }
522
+
523
+ /**
524
+ * Execute streaming and return the result.
525
+ * Delegates to StreamSetup for configuration and StreamExecutionHandler for execution.
526
+ */
527
+ private async executeStreaming(
528
+ context: FullRuntimeContext,
529
+ toolTracker: ToolExecutionTracker,
530
+ ralNumber: number
531
+ ): Promise<StreamExecutionResult> {
532
+ // Setup stream execution (tools, messages, injections, meta model)
533
+ const setup = await setupStreamExecution(
534
+ context,
535
+ toolTracker,
536
+ ralNumber,
537
+ { warmSenderPubkeys: this.warmSenderPubkeys.bind(this) }
538
+ );
539
+
540
+ // Create and execute stream handler
541
+ const handler = new StreamExecutionHandler({
542
+ context,
543
+ toolTracker,
544
+ ralNumber,
545
+ toolsObject: setup.toolsObject,
546
+ sessionManager: setup.sessionManager,
547
+ llmService: setup.llmService,
548
+ messageCompiler: setup.messageCompiler,
549
+ nudgeContent: setup.nudgeContent,
550
+ skillContent: setup.skillContent,
551
+ skills: setup.skills,
552
+ ephemeralMessages: setup.ephemeralMessages,
553
+ abortSignal: setup.abortSignal,
554
+ metaModelSystemPrompt: setup.metaModelSystemPrompt,
555
+ variantSystemPrompt: setup.variantSystemPrompt,
556
+ compressionLlmService: setup.compressionLlmService,
557
+ });
558
+
559
+ return handler.execute(setup.messages);
560
+ }
561
+ }
@@ -0,0 +1,112 @@
1
+ import type { AgentInstance } from "@/agents/types";
2
+ import { ConversationStore } from "@/conversations/ConversationStore";
3
+ import type { AgentPublisher } from "@/nostr/AgentPublisher";
4
+ import type { MCPManager } from "@/services/mcp/MCPManager";
5
+ import type { NDKEvent } from "@nostr-dev-kit/ndk";
6
+ import { listWorktrees, createWorktree } from "@/utils/git/worktree";
7
+ import { getCurrentBranchWithFallback } from "@/utils/git/initializeGitRepo";
8
+ import { logger } from "@/utils/logger";
9
+ import type { ExecutionContext } from "./types";
10
+
11
+ /**
12
+ * Create an ExecutionContext with environment resolution from event
13
+ *
14
+ * This factory resolves the working directory based on the triggering event's branch tag:
15
+ * - No branch tag: Uses projectBasePath directly (main repo with default branch)
16
+ * - With branch tag: Uses .worktrees/{sanitized_branch}/ directory
17
+ *
18
+ * @param params Context creation parameters
19
+ * @returns Complete ExecutionContext with resolved environment
20
+ */
21
+ export async function createExecutionContext(params: {
22
+ agent: AgentInstance;
23
+ conversationId: string;
24
+ /**
25
+ * Project directory (normal git repository root).
26
+ * Example: ~/tenex/{dTag}
27
+ */
28
+ projectBasePath: string;
29
+ triggeringEvent: NDKEvent;
30
+ agentPublisher?: AgentPublisher;
31
+ isDelegationCompletion?: boolean;
32
+ hasPendingDelegations?: boolean;
33
+ debug?: boolean;
34
+ /**
35
+ * MCP manager for this project's MCP tool access.
36
+ * Required for agents to use MCP tools at execution time.
37
+ */
38
+ mcpManager?: MCPManager;
39
+ }): Promise<ExecutionContext> {
40
+ // Extract branch tag from event
41
+ const branchTag = params.triggeringEvent.tags.find(t => t[0] === "branch")?.[1];
42
+
43
+ // Resolve execution environment
44
+ let workingDirectory: string;
45
+ let currentBranch: string;
46
+
47
+ if (branchTag) {
48
+ // Branch specified in event - check if it's in a worktree
49
+ const worktrees = await listWorktrees(params.projectBasePath);
50
+ const matchingWorktree = worktrees.find(wt => wt.branch === branchTag);
51
+
52
+ if (matchingWorktree) {
53
+ // Found the worktree
54
+ workingDirectory = matchingWorktree.path;
55
+ currentBranch = branchTag;
56
+ logger.info("Using worktree from branch tag", {
57
+ branch: branchTag,
58
+ path: matchingWorktree.path
59
+ });
60
+ } else {
61
+ // Worktree not found - create it now
62
+ const baseBranch = await getCurrentBranchWithFallback(params.projectBasePath);
63
+
64
+ logger.info("Branch tag specified but worktree not found, creating it", {
65
+ branch: branchTag,
66
+ baseBranch,
67
+ });
68
+
69
+ try {
70
+ workingDirectory = await createWorktree(params.projectBasePath, branchTag, baseBranch);
71
+ currentBranch = branchTag;
72
+
73
+ logger.info("Created worktree for delegation", {
74
+ branch: branchTag,
75
+ path: workingDirectory,
76
+ baseBranch,
77
+ });
78
+ } catch (error) {
79
+ // If worktree creation fails, fall back to project root with a warning
80
+ logger.error("Failed to create worktree, falling back to project root", {
81
+ branch: branchTag,
82
+ error: error instanceof Error ? error.message : String(error),
83
+ });
84
+ workingDirectory = params.projectBasePath;
85
+ currentBranch = baseBranch;
86
+ }
87
+ }
88
+ } else {
89
+ // No branch tag - use project root (default branch)
90
+ workingDirectory = params.projectBasePath;
91
+ currentBranch = await getCurrentBranchWithFallback(params.projectBasePath);
92
+ logger.info("Using project root as working directory", {
93
+ path: workingDirectory,
94
+ branch: currentBranch
95
+ });
96
+ }
97
+
98
+ return {
99
+ agent: params.agent,
100
+ conversationId: params.conversationId,
101
+ projectBasePath: params.projectBasePath,
102
+ workingDirectory,
103
+ currentBranch,
104
+ triggeringEvent: params.triggeringEvent,
105
+ agentPublisher: params.agentPublisher,
106
+ isDelegationCompletion: params.isDelegationCompletion,
107
+ hasPendingDelegations: params.hasPendingDelegations,
108
+ debug: params.debug,
109
+ mcpManager: params.mcpManager,
110
+ getConversation: () => ConversationStore.get(params.conversationId),
111
+ };
112
+ }