@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,370 @@
1
+ import type { ToolExecutionContext } from "@/tools/types";
2
+ import { conversationRegistry } from "@/conversations/ConversationRegistry";
3
+ import { ConversationStore } from "@/conversations/ConversationStore";
4
+ import type { MessageMatch } from "@/conversations/search";
5
+ import type { AISdkTool } from "@/tools/types";
6
+ import { logger } from "@/utils/logger";
7
+ import { tool } from "ai";
8
+ import { z } from "zod";
9
+ import {
10
+ getConversationEmbeddingService,
11
+ type SemanticSearchResult,
12
+ } from "@/conversations/search/embeddings";
13
+
14
+ /**
15
+ * Search mode for conversation search
16
+ * - "keyword": Traditional title-based substring matching (fast, exact)
17
+ * - "semantic": Natural language semantic search using embeddings (slower, understands meaning)
18
+ * - "hybrid": Combines both approaches, semantic results first, then keyword fallback
19
+ * - "full-text": Full-text search across all message content (advanced)
20
+ */
21
+ const SearchMode = z.enum(["keyword", "semantic", "hybrid", "full-text"]).describe(
22
+ "Search mode: 'keyword' (fast title matching), 'semantic' (natural language), 'hybrid' (both), or 'full-text' (all messages)"
23
+ );
24
+
25
+ const conversationSearchSchema = z.object({
26
+ query: z.string().describe(
27
+ "Search query. For keyword mode: matches against conversation titles. " +
28
+ "For semantic mode: natural language query like 'conversations about debugging memory issues'. " +
29
+ "For full-text mode: matches against all message content and titles."
30
+ ),
31
+ mode: SearchMode.optional().describe(
32
+ "Search mode: 'keyword' (default, fast), 'semantic' (natural language), 'hybrid' (both), or 'full-text' (comprehensive)"
33
+ ),
34
+ filters: z
35
+ .object({
36
+ agents: z
37
+ .array(z.string())
38
+ .optional()
39
+ .describe("Filter by agent slugs or pubkeys - conversation must include at least one (exact match)"),
40
+ since: z
41
+ .union([z.string(), z.number()])
42
+ .optional()
43
+ .describe("Return conversations with activity since this date (Unix timestamp or ISO 8601)"),
44
+ after: z
45
+ .union([z.string(), z.number()])
46
+ .optional()
47
+ .describe("Alias for 'since' - return conversations with activity after this date"),
48
+ })
49
+ .optional()
50
+ .describe("Optional filters to narrow search results"),
51
+ limit: z
52
+ .number()
53
+ .optional()
54
+ .default(20)
55
+ .describe("Maximum number of results to return. Defaults to 20."),
56
+ minScore: z
57
+ .number()
58
+ .optional()
59
+ .describe("Minimum relevance score for semantic results (0-1). Defaults to 0.3."),
60
+ projectId: z
61
+ .string()
62
+ .optional()
63
+ .describe("Filter by project ID. Use 'ALL' for all projects."),
64
+ });
65
+
66
+ type ConversationSearchInput = z.infer<typeof conversationSearchSchema>;
67
+
68
+ interface ConversationSearchResult {
69
+ id: string;
70
+ projectId?: string;
71
+ title?: string;
72
+ summary?: string;
73
+ messageCount: number;
74
+ createdAt?: number;
75
+ lastActivity?: number;
76
+ matches?: MessageMatch[];
77
+ relevanceScore?: number; // Only present for semantic/hybrid results
78
+ }
79
+
80
+ interface ConversationSearchOutput {
81
+ success: boolean;
82
+ conversations: ConversationSearchResult[];
83
+ total: number;
84
+ query: string;
85
+ mode: string;
86
+ searchType?: "full-text" | "title-only" | "semantic" | "hybrid";
87
+ semanticAvailable: boolean;
88
+ error?: string;
89
+ warning?: string;
90
+ }
91
+
92
+ /**
93
+ * Fallback to legacy title-only search using in-memory stores.
94
+ */
95
+ function legacyTitleSearch(query: string, limit: number): ConversationSearchResult[] {
96
+ const stores = ConversationStore.search(query);
97
+ return stores.slice(0, limit).map((store) => {
98
+ const messages = store.getAllMessages();
99
+ const firstMessage = messages[0];
100
+ const lastMessage = messages[messages.length - 1];
101
+
102
+ return {
103
+ id: store.id,
104
+ title: store.title,
105
+ messageCount: messages.length,
106
+ createdAt: firstMessage?.timestamp,
107
+ lastActivity: lastMessage?.timestamp,
108
+ };
109
+ });
110
+ }
111
+
112
+ /**
113
+ * Perform keyword-based search on conversation titles
114
+ */
115
+ function keywordSearch(query: string, limit: number): ConversationSearchResult[] {
116
+ return legacyTitleSearch(query, limit);
117
+ }
118
+
119
+ /**
120
+ * Perform semantic search using embeddings
121
+ */
122
+ async function semanticSearch(
123
+ query: string,
124
+ limit: number,
125
+ minScore: number,
126
+ projectId?: string
127
+ ): Promise<ConversationSearchResult[]> {
128
+ const conversationEmbeddingService = getConversationEmbeddingService();
129
+ try {
130
+ const results = await conversationEmbeddingService.semanticSearch(query, {
131
+ limit,
132
+ minScore,
133
+ projectId,
134
+ });
135
+ return results.map((result: SemanticSearchResult) => ({
136
+ id: result.conversationId,
137
+ projectId: result.projectId,
138
+ title: result.title,
139
+ summary: result.summary,
140
+ messageCount: result.messageCount,
141
+ createdAt: result.createdAt,
142
+ lastActivity: result.lastActivity,
143
+ relevanceScore: result.relevanceScore,
144
+ }));
145
+ } catch (error) {
146
+ logger.error("Semantic search failed, falling back to empty results", { error });
147
+ return [];
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Perform hybrid search: semantic first, then keyword fallback
153
+ * Deduplicates results by conversation ID
154
+ */
155
+ async function hybridSearch(
156
+ query: string,
157
+ limit: number,
158
+ minScore: number,
159
+ projectId?: string
160
+ ): Promise<ConversationSearchResult[]> {
161
+ const seen = new Set<string>();
162
+ const combined: ConversationSearchResult[] = [];
163
+
164
+ // First: semantic results (higher quality matches)
165
+ const semanticResults = await semanticSearch(query, limit, minScore, projectId);
166
+ for (const result of semanticResults) {
167
+ if (!seen.has(result.id)) {
168
+ seen.add(result.id);
169
+ combined.push(result);
170
+ }
171
+ }
172
+
173
+ // Second: keyword results to fill up remaining slots
174
+ if (combined.length < limit) {
175
+ const keywordResults = keywordSearch(query, limit);
176
+ for (const result of keywordResults) {
177
+ if (!seen.has(result.id) && combined.length < limit) {
178
+ seen.add(result.id);
179
+ combined.push(result);
180
+ }
181
+ }
182
+ }
183
+
184
+ return combined.slice(0, limit);
185
+ }
186
+
187
+ /**
188
+ * Perform full-text search across all messages
189
+ */
190
+ async function fullTextSearch(
191
+ query: string,
192
+ filters: ConversationSearchInput["filters"],
193
+ limit: number
194
+ ): Promise<{ results: ConversationSearchResult[]; searchType: "full-text" | "title-only"; error?: string }> {
195
+ const advancedResult = conversationRegistry.searchAdvanced({ query, filters }, limit);
196
+
197
+ if (advancedResult.success) {
198
+ if (advancedResult.results.length > 0) {
199
+ return {
200
+ searchType: "full-text",
201
+ results: advancedResult.results.map((result) => ({
202
+ id: result.conversationId,
203
+ title: result.title,
204
+ messageCount: result.messageCount,
205
+ createdAt: result.createdAt,
206
+ lastActivity: result.lastActivity,
207
+ matches: result.matches,
208
+ })),
209
+ };
210
+ }
211
+
212
+ // Advanced search succeeded but no results - try legacy search too
213
+ const legacyResults = legacyTitleSearch(query, limit);
214
+ if (legacyResults.length > 0) {
215
+ return {
216
+ searchType: "title-only",
217
+ results: legacyResults,
218
+ };
219
+ }
220
+
221
+ // No results from either search
222
+ return {
223
+ searchType: "full-text",
224
+ results: [],
225
+ };
226
+ }
227
+
228
+ // Advanced search failed - try fallback
229
+ logger.warn("⚠️ Advanced search failed, falling back to title search", {
230
+ error: advancedResult.error,
231
+ query,
232
+ });
233
+
234
+ const legacyResults = legacyTitleSearch(query, limit);
235
+
236
+ return {
237
+ searchType: "title-only",
238
+ results: legacyResults,
239
+ error: advancedResult.error,
240
+ };
241
+ }
242
+
243
+ /**
244
+ * Check if semantic search is available
245
+ */
246
+ async function isSemanticSearchAvailable(): Promise<boolean> {
247
+ try {
248
+ const conversationEmbeddingService = getConversationEmbeddingService();
249
+ return await conversationEmbeddingService.hasIndexedConversations();
250
+ } catch {
251
+ return false;
252
+ }
253
+ }
254
+
255
+ async function executeConversationSearch(
256
+ input: ConversationSearchInput,
257
+ context: ToolExecutionContext
258
+ ): Promise<ConversationSearchOutput> {
259
+ const { query, mode = "keyword", filters, limit = 20, minScore = 0.3, projectId } = input;
260
+
261
+ logger.info("🔍 Searching conversations", {
262
+ query,
263
+ mode,
264
+ filters,
265
+ limit,
266
+ minScore,
267
+ projectId,
268
+ agent: context.agent.name,
269
+ });
270
+
271
+ // Check semantic availability
272
+ const semanticAvailable = await isSemanticSearchAvailable();
273
+
274
+ // Determine effective mode
275
+ let effectiveMode = mode;
276
+ if ((mode === "semantic" || mode === "hybrid") && !semanticAvailable) {
277
+ logger.info("Semantic search not available, falling back to keyword search");
278
+ effectiveMode = "keyword";
279
+ }
280
+
281
+ let conversations: ConversationSearchResult[];
282
+ let searchType: "full-text" | "title-only" | "semantic" | "hybrid";
283
+ let warning: string | undefined;
284
+
285
+ switch (effectiveMode) {
286
+ case "semantic":
287
+ conversations = await semanticSearch(query, limit, minScore, projectId);
288
+ searchType = "semantic";
289
+ break;
290
+
291
+ case "hybrid":
292
+ conversations = await hybridSearch(query, limit, minScore, projectId);
293
+ searchType = "hybrid";
294
+ break;
295
+
296
+ case "full-text": {
297
+ const fullTextResult = await fullTextSearch(query, filters, limit);
298
+ conversations = fullTextResult.results;
299
+ searchType = fullTextResult.searchType;
300
+ if (fullTextResult.error) {
301
+ warning = `Full-text search failed: ${fullTextResult.error}. Showing ${fullTextResult.searchType} results.`;
302
+ } else if (fullTextResult.searchType === "title-only" && fullTextResult.results.length > 0) {
303
+ warning = "Full-text search found no results; showing title-only matches";
304
+ }
305
+ break;
306
+ }
307
+
308
+ case "keyword":
309
+ default:
310
+ conversations = keywordSearch(query, limit);
311
+ searchType = "full-text";
312
+ break;
313
+ }
314
+
315
+ logger.info("✅ Conversation search complete", {
316
+ query,
317
+ mode: effectiveMode,
318
+ found: conversations.length,
319
+ semanticAvailable,
320
+ agent: context.agent.name,
321
+ });
322
+
323
+ return {
324
+ success: true,
325
+ conversations,
326
+ total: conversations.length,
327
+ query,
328
+ mode: effectiveMode,
329
+ searchType,
330
+ semanticAvailable,
331
+ warning,
332
+ };
333
+ }
334
+
335
+ export function createConversationSearchTool(context: ToolExecutionContext): AISdkTool {
336
+ const aiTool = tool({
337
+ description:
338
+ "Search conversations with multiple search modes. Returns matching conversations with summary information including ID, title, message count, and timestamps. " +
339
+ "Supports four search modes: 'keyword' (fast title matching), 'semantic' (natural language understanding), 'hybrid' (both), and 'full-text' (comprehensive message search). " +
340
+ "Use semantic or hybrid mode for natural language queries that understand meaning.",
341
+
342
+ inputSchema: conversationSearchSchema,
343
+
344
+ execute: async (input: ConversationSearchInput) => {
345
+ return await executeConversationSearch(input, context);
346
+ },
347
+ });
348
+
349
+ Object.defineProperty(aiTool, "getHumanReadableContent", {
350
+ value: ({ query, mode, filters, limit }: ConversationSearchInput) => {
351
+ const parts = [`Searching conversations for "${query}"`];
352
+ const modeStr = mode || "keyword";
353
+ parts.push(`mode=${modeStr}`);
354
+ if (filters?.agents?.length) {
355
+ parts.push(`agents: ${filters.agents.join(", ")}`);
356
+ }
357
+ if (filters?.since || filters?.after) {
358
+ parts.push(`since: ${filters.since || filters.after}`);
359
+ }
360
+ if (limit && limit !== 20) {
361
+ parts.push(`limit: ${limit}`);
362
+ }
363
+ return parts.join(", ");
364
+ },
365
+ enumerable: false,
366
+ configurable: true,
367
+ });
368
+
369
+ return aiTool as AISdkTool;
370
+ }
@@ -0,0 +1,327 @@
1
+ /**
2
+ * Delegation Tool Implementation
3
+ *
4
+ * Enables agents to delegate tasks to other agents in the system.
5
+ *
6
+ * ## Circular Delegation Handling
7
+ *
8
+ * The tool detects when a delegation would create a circular chain (A→B→C→A).
9
+ * By default, circular delegations return a soft warning with `success: false`.
10
+ * Set `force: true` on an individual delegation to bypass this check.
11
+ *
12
+ * @module delegate
13
+ */
14
+ import type { ToolExecutionContext } from "@/tools/types";
15
+ import { getProjectContext } from "@/services/projects";
16
+ import { RALRegistry } from "@/services/ral/RALRegistry";
17
+ import type { PendingDelegation } from "@/services/ral/types";
18
+ import type { AISdkTool } from "@/tools/types";
19
+ import { resolveAgentSlug } from "@/services/agents";
20
+ import { logger } from "@/utils/logger";
21
+ import { createEventContext } from "@/services/event-context";
22
+ import { shortenConversationId } from "@/utils/conversation-id";
23
+ import { wouldCreateCircularDelegation } from "@/utils/delegation-chain";
24
+ import { ConversationStore } from "@/conversations/ConversationStore";
25
+ import type { DelegationMarker } from "@/conversations/types";
26
+ import { AgentEventDecoder } from "@/nostr/AgentEventDecoder";
27
+ import { tool } from "ai";
28
+ import { z } from "zod";
29
+
30
+ const delegationItemSchema = z.object({
31
+ recipient: z
32
+ .string()
33
+ .describe(
34
+ "Agent slug (e.g., 'architect', 'claude-code', 'explore-agent'). Only agent slugs are accepted."
35
+ ),
36
+ prompt: z.string().describe("The request or task for this agent"),
37
+ branch: z
38
+ .string()
39
+ .optional()
40
+ .describe("Git branch name for worktree isolation"),
41
+ force: z
42
+ .boolean()
43
+ .optional()
44
+ .describe("Set to true to proceed even if circular delegation is detected"),
45
+ nudges: z
46
+ .array(z.string())
47
+ .optional()
48
+ .describe("Nudge event IDs to apply to this delegated agent. Nudges can modify tool availability and inject additional context."),
49
+ });
50
+
51
+ type DelegationItem = z.infer<typeof delegationItemSchema>;
52
+
53
+ interface DelegateInput {
54
+ delegations: DelegationItem[];
55
+ }
56
+
57
+ interface CircularDelegationWarning {
58
+ recipient: string;
59
+ chain: string;
60
+ message: string;
61
+ forced?: boolean;
62
+ }
63
+
64
+ interface DelegateOutput {
65
+ success: boolean;
66
+ message: string;
67
+ /** Truncated delegation conversation IDs (prefixes for compact display) */
68
+ delegationConversationIds: string[];
69
+ circularDelegationWarning?: CircularDelegationWarning;
70
+ circularDelegationWarnings?: CircularDelegationWarning[];
71
+ }
72
+
73
+ /**
74
+ * Check if the agent has created a todo list.
75
+ * Returns { hasTodos: boolean, hasConversation: boolean } to distinguish
76
+ * between "no todos" and "no conversation context" cases.
77
+ */
78
+ function checkTodoState(context: ToolExecutionContext): { hasTodos: boolean; hasConversation: boolean } {
79
+ const conversation = context.getConversation();
80
+ if (!conversation) {
81
+ // No conversation context available - skip enforcement
82
+ return { hasTodos: true, hasConversation: false };
83
+ }
84
+ const todos = conversation.getTodos(context.agent.pubkey);
85
+ return { hasTodos: todos.length > 0, hasConversation: true };
86
+ }
87
+
88
+ async function executeDelegate(
89
+ input: DelegateInput,
90
+ context: ToolExecutionContext
91
+ ): Promise<DelegateOutput> {
92
+ const { delegations } = input;
93
+
94
+ // ENFORCEMENT: Delegation requires a todo list
95
+ // Skip enforcement if no conversation context (e.g., MCP-only mode)
96
+ const todoState = checkTodoState(context);
97
+ if (todoState.hasConversation && !todoState.hasTodos) {
98
+ throw new Error(
99
+ "Delegation requires a todo list. Please use `todo_write()` to create a todo list before delegating tasks. " +
100
+ "This helps track work progress and ensures delegated tasks are properly documented."
101
+ );
102
+ }
103
+
104
+ if (!Array.isArray(delegations) || delegations.length === 0) {
105
+ throw new Error("At least one delegation is required");
106
+ }
107
+
108
+ const ralRegistry = RALRegistry.getInstance();
109
+ const pendingDelegations: PendingDelegation[] = [];
110
+ const circularWarnings: CircularDelegationWarning[] = [];
111
+
112
+ // Get the delegation chain from the current conversation for cycle detection
113
+ const conversationStore = ConversationStore.get(context.conversationId);
114
+ const delegationChain = conversationStore?.metadata?.delegationChain;
115
+
116
+ // Extract inherited nudges from the triggering event
117
+ // Nudge inheritance: any nudges on the current triggering event are automatically
118
+ // passed forward to delegated agents unless explicitly overridden
119
+ const inheritedNudges = AgentEventDecoder.extractNudgeEventIds(context.triggeringEvent);
120
+
121
+ for (const delegation of delegations) {
122
+ // Resolve slug to pubkey - throws if invalid
123
+ const resolution = resolveAgentSlug(delegation.recipient);
124
+ if (!resolution.pubkey) {
125
+ const availableSlugsStr = resolution.availableSlugs.length > 0
126
+ ? `Available agent slugs: ${resolution.availableSlugs.join(", ")}`
127
+ : "No agents available in the current project context.";
128
+ throw new Error(
129
+ `Invalid agent slug: "${delegation.recipient}". Only agent slugs are accepted. ${availableSlugsStr}`
130
+ );
131
+ }
132
+ const pubkey = resolution.pubkey;
133
+
134
+ // Check for circular delegation
135
+ if (delegationChain && wouldCreateCircularDelegation(delegationChain, pubkey)) {
136
+ const projectContext = getProjectContext();
137
+ const targetAgent = projectContext.getAgentByPubkey(pubkey);
138
+ const targetName = targetAgent?.slug || pubkey.substring(0, 8);
139
+ const chainDisplay = delegationChain.map(e => e.displayName).join(" → ");
140
+
141
+ const warning: CircularDelegationWarning = {
142
+ recipient: targetName,
143
+ chain: chainDisplay,
144
+ message: `"${targetName}" is already in the delegation chain (${chainDisplay}). Delegating would create a cycle.`,
145
+ };
146
+
147
+ if (!delegation.force) {
148
+ logger.info("[delegate] Circular delegation detected, skipping (no force flag)", {
149
+ recipient: delegation.recipient,
150
+ targetPubkey: pubkey.substring(0, 8),
151
+ chain: chainDisplay,
152
+ });
153
+ circularWarnings.push(warning);
154
+ continue;
155
+ }
156
+
157
+ // Force flag set - proceed with warning
158
+ logger.warn("[delegate] Circular delegation proceeding with force flag", {
159
+ recipient: delegation.recipient,
160
+ targetPubkey: pubkey.substring(0, 8),
161
+ chain: chainDisplay,
162
+ });
163
+ circularWarnings.push({ ...warning, forced: true });
164
+ }
165
+
166
+ // Publish delegation event
167
+ const eventContext = createEventContext(context);
168
+
169
+ // Combine inherited nudges with explicitly specified nudges
170
+ // Nudge inheritance: inherited nudges are always passed forward
171
+ // Explicit nudges are added to the inherited set (not replaced)
172
+ const combinedNudges = [
173
+ ...inheritedNudges,
174
+ ...(delegation.nudges || []),
175
+ ];
176
+ // Deduplicate nudges
177
+ const uniqueNudges = [...new Set(combinedNudges)];
178
+
179
+ const eventId = await context.agentPublisher.delegate({
180
+ recipient: pubkey,
181
+ content: delegation.prompt,
182
+ branch: delegation.branch,
183
+ nudges: uniqueNudges.length > 0 ? uniqueNudges : undefined,
184
+ }, eventContext);
185
+
186
+ const pendingDelegation: PendingDelegation = {
187
+ delegationConversationId: eventId,
188
+ recipientPubkey: pubkey,
189
+ senderPubkey: context.agent.pubkey,
190
+ prompt: delegation.prompt,
191
+ ralNumber: context.ralNumber,
192
+ };
193
+ pendingDelegations.push(pendingDelegation);
194
+
195
+ // Register immediately after publishing to prevent orphans
196
+ ralRegistry.mergePendingDelegations(
197
+ context.agent.pubkey,
198
+ context.conversationId,
199
+ context.ralNumber,
200
+ [pendingDelegation]
201
+ );
202
+
203
+ // Create pending marker immediately to show delegation in real-time
204
+ const parentStore = ConversationStore.get(context.conversationId);
205
+ if (parentStore) {
206
+ const initiatedAt = Math.floor(Date.now() / 1000);
207
+ const marker: DelegationMarker = {
208
+ delegationConversationId: eventId,
209
+ recipientPubkey: pubkey,
210
+ parentConversationId: context.conversationId,
211
+ initiatedAt,
212
+ status: "pending",
213
+ };
214
+
215
+ // Store marker locally
216
+ parentStore.addDelegationMarker(
217
+ marker,
218
+ context.agent.pubkey,
219
+ context.ralNumber
220
+ );
221
+ await parentStore.save();
222
+
223
+ // Publish marker to Nostr for verification by other agents
224
+ await context.agentPublisher.delegationMarker({
225
+ delegationConversationId: eventId,
226
+ recipientPubkey: pubkey,
227
+ parentConversationId: context.conversationId,
228
+ status: "pending",
229
+ initiatedAt,
230
+ });
231
+ }
232
+ }
233
+
234
+ const delegationConversationIds = pendingDelegations.map(d => shortenConversationId(d.delegationConversationId));
235
+ const unforcedWarnings = circularWarnings.filter(w => !w.forced);
236
+
237
+ // All delegations were circular (not forced) - return soft warning
238
+ if (pendingDelegations.length === 0 && unforcedWarnings.length > 0) {
239
+ const warningMessages = unforcedWarnings.map(w =>
240
+ `"${w.recipient}" is already in chain (${w.chain})`
241
+ ).join("; ");
242
+
243
+ return {
244
+ success: false,
245
+ message: `Circular delegation detected: ${warningMessages}. Add \`force: true\` to proceed anyway.`,
246
+ delegationConversationIds: [],
247
+ circularDelegationWarning: unforcedWarnings[0],
248
+ circularDelegationWarnings: unforcedWarnings,
249
+ };
250
+ }
251
+
252
+ if (pendingDelegations.length === 0) {
253
+ throw new Error("No delegations were published.");
254
+ }
255
+
256
+ logger.info("[delegate] Published delegations, agent continues without blocking", {
257
+ count: pendingDelegations.length,
258
+ delegationConversationIds,
259
+ circularWarningsCount: circularWarnings.length,
260
+ inheritedNudgesCount: inheritedNudges.length,
261
+ });
262
+
263
+ let message = `Delegated ${pendingDelegations.length} task(s). The agent(s) will wake you up when ready with the response(s).`;
264
+ if (unforcedWarnings.length > 0) {
265
+ const skipped = unforcedWarnings.map(w => w.recipient).join(", ");
266
+ message += ` Note: Skipped circular delegation(s) to: ${skipped}.`;
267
+ }
268
+
269
+ return {
270
+ success: true,
271
+ message,
272
+ delegationConversationIds,
273
+ ...(circularWarnings.length > 0 && {
274
+ circularDelegationWarning: circularWarnings[0],
275
+ circularDelegationWarnings: circularWarnings,
276
+ }),
277
+ };
278
+ }
279
+
280
+ export function createDelegateTool(context: ToolExecutionContext): AISdkTool {
281
+ const delegateSchema = z.object({
282
+ delegations: z
283
+ .array(delegationItemSchema)
284
+ .min(1)
285
+ .describe("Array of delegations to execute"),
286
+ });
287
+
288
+ const description = `Delegate tasks to one or more agents. Each delegation can have its own prompt and branch. IMPORTANT: Delegated agents ONLY see your prompt - they cannot see any prior conversation. Include ALL necessary context, requirements, and constraints in your prompt.
289
+
290
+ Circular delegation detection: The tool detects when a delegation would create a circular chain (A→B→C→A). By default, circular delegations are skipped with a soft warning. Set \`force: true\` on an individual delegation to bypass this check.
291
+
292
+ Nudge support: Pass nudge event IDs in the \`nudges\` array to apply behavioral nudges to delegated agents. Nudges can modify tool availability (only-tool, allow-tool, deny-tool) and inject additional context. Nudge inheritance: any nudges active on the current agent are automatically forwarded to all delegated agents.`;
293
+
294
+ const aiTool = tool({
295
+ description,
296
+ inputSchema: delegateSchema,
297
+ execute: async (input: unknown) => {
298
+ return await executeDelegate(input as DelegateInput, context);
299
+ },
300
+ });
301
+
302
+ Object.defineProperty(aiTool, "getHumanReadableContent", {
303
+ value: (args: unknown) => {
304
+ if (!args || typeof args !== "object" || !("delegations" in args)) {
305
+ return "Delegating to agent(s)";
306
+ }
307
+
308
+ const { delegations } = args as DelegateInput;
309
+
310
+ if (!delegations || !Array.isArray(delegations)) {
311
+ return "Delegating to agent(s)";
312
+ }
313
+
314
+ if (delegations.length === 1) {
315
+ const d = delegations[0];
316
+ return `Delegating to ${d.recipient}`;
317
+ }
318
+
319
+ const recipients = delegations.map((d) => d.recipient).join(", ");
320
+ return `Delegating ${delegations.length} tasks to: ${recipients}`;
321
+ },
322
+ enumerable: false,
323
+ configurable: true,
324
+ });
325
+
326
+ return aiTool as AISdkTool;
327
+ }