@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,651 @@
1
+ /**
2
+ * Unified kill tool - Abort agents or background shell processes
3
+ *
4
+ * This tool provides a unified interface for killing:
5
+ * 1. Agent executions (by conversation_id) - with cascading abort support
6
+ * 2. Background shell processes (by shell_id)
7
+ *
8
+ * When killing an agent by conversation_id, this tool will:
9
+ * - Abort the agent's execution in that conversation
10
+ * - Cascade the abort to all nested delegations (agents this agent delegated to)
11
+ * - Add aborted tuples to cooldown registry to prevent immediate re-routing
12
+ * - Block until all cascade aborts complete
13
+ */
14
+
15
+ import type { AISdkTool, ToolExecutionContext } from "@/tools/types";
16
+ import { killBackgroundTask, getBackgroundTaskInfo, getAllBackgroundTasks } from "./shell";
17
+ import { RALRegistry } from "@/services/ral";
18
+ import { CooldownRegistry } from "@/services/CooldownRegistry";
19
+ import { ConversationStore } from "@/conversations/ConversationStore";
20
+ import { tool } from "ai";
21
+ import { z } from "zod";
22
+ import { logger } from "@/utils/logger";
23
+ import { shortenConversationId } from "@/utils/conversation-id";
24
+ import { trace } from "@opentelemetry/api";
25
+ import { resolvePrefixToId, normalizeNostrIdentifier } from "@/utils/nostr-entity-parser";
26
+ import { nip19 } from "nostr-tools";
27
+ import { isFullEventId, isShortEventId, isShellTaskId } from "@/types/event-ids";
28
+
29
+ const killSchema = z.object({
30
+ target: z
31
+ .string()
32
+ .min(1, "target is required")
33
+ .describe(
34
+ "The target to kill. Can be either:\n" +
35
+ "- A conversation ID (to abort an agent and cascade to nested delegations)\n" +
36
+ "- A shell task ID (to terminate a background shell process)"
37
+ ),
38
+ reason: z
39
+ .string()
40
+ .optional()
41
+ .describe("Optional reason for the kill (used for agent aborts)"),
42
+ });
43
+
44
+ type KillInput = z.infer<typeof killSchema>;
45
+
46
+ /**
47
+ * Resolve a target ID from various formats to a canonical full ID.
48
+ *
49
+ * Supports:
50
+ * - 64-char hex: Already a full ID, returned as-is
51
+ * - 12-char hex: Resolve via PrefixKVStore or RALRegistry fallback
52
+ * - 7-char alphanumeric: Shell task ID (returned as-is for separate handling)
53
+ * - NIP-19 formats (nevent, note): Decode to 64-char hex
54
+ *
55
+ * @param input - The target identifier in any supported format
56
+ * @returns Object with resolved ID and type, or null if resolution failed
57
+ */
58
+ async function resolveTargetId(input: string): Promise<{
59
+ id: string;
60
+ type: 'conversation' | 'shell' | 'unknown';
61
+ wasResolved: boolean;
62
+ } | null> {
63
+ const trimmed = input.trim().toLowerCase();
64
+
65
+ // 64-char hex: already a full ID (use typed guard)
66
+ if (isFullEventId(trimmed)) {
67
+ return { id: trimmed, type: 'conversation', wasResolved: false };
68
+ }
69
+
70
+ // 12-char hex: resolve via prefix store or RALRegistry fallback (use typed guard)
71
+ if (isShortEventId(trimmed)) {
72
+ // Try PrefixKVStore first (O(1) lookup)
73
+ const resolved = resolvePrefixToId(trimmed);
74
+ if (resolved) {
75
+ logger.debug("[kill.resolveTargetId] Resolved 12-char prefix via PrefixKVStore", {
76
+ prefix: trimmed,
77
+ fullId: resolved.substring(0, 12),
78
+ });
79
+ return { id: resolved, type: 'conversation', wasResolved: true };
80
+ }
81
+
82
+ // Fallback: scan RALRegistry for active delegations
83
+ const ralRegistry = RALRegistry.getInstance();
84
+ const ralResolved = ralRegistry.resolveDelegationPrefix(trimmed);
85
+ if (ralResolved) {
86
+ logger.debug("[kill.resolveTargetId] Resolved 12-char prefix via RALRegistry", {
87
+ prefix: trimmed,
88
+ fullId: ralResolved.substring(0, 12),
89
+ });
90
+ return { id: ralResolved, type: 'conversation', wasResolved: true };
91
+ }
92
+
93
+ // Prefix not found - could still be valid but unknown
94
+ logger.debug("[kill.resolveTargetId] Could not resolve 12-char prefix", {
95
+ prefix: trimmed,
96
+ });
97
+ return null;
98
+ }
99
+
100
+ // 7-char alphanumeric: shell task ID (different ID space, use typed guard)
101
+ if (isShellTaskId(trimmed)) {
102
+ return { id: trimmed, type: 'shell', wasResolved: false };
103
+ }
104
+
105
+ // NIP-19 formats (nevent, note)
106
+ const normalized = normalizeNostrIdentifier(input);
107
+ if (normalized && (normalized.startsWith("nevent1") || normalized.startsWith("note1"))) {
108
+ try {
109
+ const decoded = nip19.decode(normalized);
110
+ if (decoded.type === "note" && typeof decoded.data === "string") {
111
+ const eventId = decoded.data.toLowerCase();
112
+ return { id: eventId, type: 'conversation', wasResolved: true };
113
+ }
114
+ if (decoded.type === "nevent" && typeof decoded.data === "object" && decoded.data !== null) {
115
+ const data = decoded.data as { id: string };
116
+ const eventId = data.id.toLowerCase();
117
+ return { id: eventId, type: 'conversation', wasResolved: true };
118
+ }
119
+ } catch (error) {
120
+ logger.debug("[kill.resolveTargetId] Failed to decode NIP-19 identifier", {
121
+ input,
122
+ error: error instanceof Error ? error.message : String(error),
123
+ });
124
+ }
125
+ }
126
+
127
+ // UUID format (legacy shell task IDs) - check as fallback
128
+ if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(trimmed)) {
129
+ return { id: trimmed, type: 'shell', wasResolved: false };
130
+ }
131
+
132
+ return null;
133
+ }
134
+
135
+ interface KillOutput {
136
+ success: boolean;
137
+ message: string;
138
+ target: string;
139
+ targetType: "agent" | "shell";
140
+ /** For agent kills: number of agents aborted in cascade */
141
+ cascadeAbortCount?: number;
142
+ /** For agent kills: list of aborted conversation:agent tuples */
143
+ abortedTuples?: Array<{ conversationId: string; agentPubkey: string }>;
144
+ /** For shell kills: process info */
145
+ pid?: number;
146
+ taskInfo?: {
147
+ command: string;
148
+ description: string | null;
149
+ outputFile: string;
150
+ startTime: string;
151
+ };
152
+ }
153
+
154
+ /**
155
+ * Core implementation of the unified kill functionality
156
+ */
157
+ async function executeKill(input: KillInput, context: ToolExecutionContext): Promise<KillOutput> {
158
+ const { target, reason } = input;
159
+
160
+ // Normalize target once for consistent matching throughout
161
+ const normalizedTarget = target.trim().toLowerCase();
162
+
163
+ // Get caller's project ID for filtering (prevents cross-project metadata leakage)
164
+ const callerConversation = context.getConversation?.();
165
+ const callerProjectId = callerConversation?.getProjectId();
166
+
167
+ // First, try to resolve the target ID to a canonical format
168
+ const resolved = await resolveTargetId(target);
169
+
170
+ if (resolved) {
171
+ // Successfully resolved to a known format
172
+ if (resolved.type === 'shell') {
173
+ // Shell task ID - check if it exists and handle
174
+ const taskInfo = getBackgroundTaskInfo(resolved.id);
175
+ if (taskInfo) {
176
+ return killShellTask(resolved.id, context);
177
+ }
178
+ // Shell ID format but task not found - fall through to error
179
+ } else if (resolved.type === 'conversation') {
180
+ // Conversation ID (64-char hex) - check if it exists
181
+ const isConversationId = ConversationStore.has(resolved.id);
182
+ if (isConversationId) {
183
+ return await killAgent(resolved.id, reason, context);
184
+ }
185
+ // Also try prefix match on the resolved ID (in case it's a prefix itself)
186
+ const allConversations = ConversationStore.getAll();
187
+ const matchingConv = allConversations.find((c) => c.id.startsWith(resolved.id));
188
+ if (matchingConv) {
189
+ return await killAgent(matchingConv.id, reason, context);
190
+ }
191
+ // Resolved ID but not found in store - fall through to error
192
+ }
193
+ }
194
+
195
+ // Resolution failed or target not found
196
+ // Legacy fallback: try direct lookup with normalized target (handles edge cases)
197
+ const isDirectConversationId = ConversationStore.has(normalizedTarget);
198
+ // Note: getBackgroundTaskInfo returns undefined for non-existent tasks, so use truthiness check
199
+ const isDirectShellTaskId = !isDirectConversationId && !!getBackgroundTaskInfo(normalizedTarget);
200
+
201
+ if (isDirectConversationId) {
202
+ return await killAgent(normalizedTarget, reason, context);
203
+ }
204
+
205
+ if (isDirectShellTaskId) {
206
+ return killShellTask(normalizedTarget, context);
207
+ }
208
+
209
+ // Try partial match for conversation ID (prefix lookup - legacy fallback)
210
+ // Use normalized target to handle uppercase input
211
+ const allConversations = ConversationStore.getAll();
212
+ const matchingConv = allConversations.find((c) => c.id.startsWith(normalizedTarget));
213
+
214
+ if (matchingConv) {
215
+ // Found a conversation by prefix - use it
216
+ return await killAgent(matchingConv.id, reason, context);
217
+ }
218
+
219
+ // Not found - provide helpful error with project-filtered listings
220
+ // SECURITY: Only show tasks/conversations from caller's project to prevent metadata leakage
221
+ let errorMessage = `Target '${target}' not found.`;
222
+
223
+ if (callerProjectId) {
224
+ // Filter by caller's project to prevent cross-project information disclosure
225
+ const projectTasks = getAllBackgroundTasks().filter(
226
+ (t) => t.projectId === callerProjectId
227
+ );
228
+ const projectConversations = allConversations.filter(
229
+ (c) => c.getProjectId() === callerProjectId
230
+ );
231
+
232
+ if (projectTasks.length > 0) {
233
+ errorMessage += ` Available background tasks in this project: ${projectTasks
234
+ .map((t) => t.taskId)
235
+ .join(", ")}.`;
236
+ }
237
+
238
+ if (projectConversations.length > 0) {
239
+ errorMessage += ` Active conversations in this project: ${projectConversations
240
+ .map((c) => c.id.substring(0, 12))
241
+ .join(", ")}.`;
242
+ } else if (projectTasks.length === 0) {
243
+ errorMessage += " No active tasks or conversations found in this project.";
244
+ }
245
+ } else {
246
+ // No project context - return generic error without enumeration
247
+ errorMessage += " Unable to list available targets without project context.";
248
+ }
249
+
250
+ return {
251
+ success: false,
252
+ message: errorMessage,
253
+ target,
254
+ targetType: "agent",
255
+ };
256
+ }
257
+
258
+ /**
259
+ * Kill an agent execution with cascading abort
260
+ */
261
+ async function killAgent(
262
+ conversationId: string,
263
+ reason: string | undefined,
264
+ context: ToolExecutionContext
265
+ ): Promise<KillOutput> {
266
+ const ralRegistry = RALRegistry.getInstance();
267
+ const cooldownRegistry = CooldownRegistry.getInstance();
268
+ const conversation = ConversationStore.get(conversationId);
269
+
270
+ if (!conversation) {
271
+ return {
272
+ success: false,
273
+ message: `Conversation ${conversationId.substring(0, 12)} not found`,
274
+ target: conversationId,
275
+ targetType: "agent",
276
+ };
277
+ }
278
+
279
+ // Find the agent executing in this conversation
280
+ // Check BOTH ConversationStore (where RALs are registered during stream setup)
281
+ // AND RALRegistry (where RALs are created earlier during RAL resolution)
282
+ // This prevents a race condition where kill is called after RAL creation but before stream setup
283
+ const activeRals = conversation.getAllActiveRals();
284
+ const ralRegistryEntries = ralRegistry.getConversationEntries(conversationId);
285
+
286
+ // Build a combined set of agent pubkeys that have active RALs
287
+ const activeAgentPubkeys = new Set<string>();
288
+ for (const [pubkey] of activeRals) {
289
+ activeAgentPubkeys.add(pubkey);
290
+ }
291
+ for (const entry of ralRegistryEntries) {
292
+ activeAgentPubkeys.add(entry.agentPubkey);
293
+ }
294
+
295
+ // Get projectId for proper cooldown isolation (needed for both active and pre-emptive kills)
296
+ const projectId = conversation.getProjectId();
297
+ if (!projectId) {
298
+ return {
299
+ success: false,
300
+ message: `Cannot abort: conversation ${conversationId.substring(0, 12)} has no project ID`,
301
+ target: conversationId,
302
+ targetType: "agent",
303
+ };
304
+ }
305
+
306
+ // === AUTHORIZATION: Project Isolation Check (BEFORE pre-emptive or active kill) ===
307
+ // This check MUST happen before any kill operation to prevent cross-project kills.
308
+ // Moving this check before the pre-emptive kill branch fixes the security bug where
309
+ // a caller with no matching project context could kill a pending delegation.
310
+ const callerConversation = context.getConversation?.();
311
+ const callerProjectId = callerConversation?.getProjectId();
312
+
313
+ if (!callerProjectId) {
314
+ logger.warn("[kill] Authorization check failed: caller has no project context", {
315
+ callerAgent: context.agent.slug,
316
+ targetConversationId: conversationId.substring(0, 12),
317
+ targetProjectId: projectId.substring(0, 12),
318
+ });
319
+
320
+ trace.getActiveSpan()?.addEvent("kill.authorization_failed", {
321
+ "kill.reason": "caller_no_project",
322
+ "kill.caller_agent": context.agent.slug,
323
+ "kill.target_conversation_id": shortenConversationId(conversationId),
324
+ });
325
+
326
+ return {
327
+ success: false,
328
+ message: `Authorization failed: cannot kill agents without project context`,
329
+ target: conversationId,
330
+ targetType: "agent",
331
+ };
332
+ }
333
+
334
+ if (callerProjectId !== projectId) {
335
+ logger.warn("[kill] Authorization check failed: cross-project kill attempt blocked", {
336
+ callerAgent: context.agent.slug,
337
+ callerProjectId: callerProjectId.substring(0, 12),
338
+ targetConversationId: conversationId.substring(0, 12),
339
+ targetProjectId: projectId.substring(0, 12),
340
+ });
341
+
342
+ trace.getActiveSpan()?.addEvent("kill.authorization_failed", {
343
+ "kill.reason": "cross_project_kill_blocked",
344
+ "kill.caller_agent": context.agent.slug,
345
+ "kill.caller_project_id": callerProjectId.substring(0, 12),
346
+ "kill.target_project_id": projectId.substring(0, 12),
347
+ });
348
+
349
+ return {
350
+ success: false,
351
+ message: `Authorization failed: cannot kill agents in other projects (target: ${projectId.substring(0, 12)}, caller: ${callerProjectId.substring(0, 12)})`,
352
+ target: conversationId,
353
+ targetType: "agent",
354
+ };
355
+ }
356
+
357
+ // If no active agents found, try PRE-EMPTIVE KILL:
358
+ // The conversation may be a delegation that hasn't started executing yet.
359
+ // Look up the recipient agent from the delegation info and mark it as killed
360
+ // so when it eventually starts, it will abort immediately.
361
+ if (activeAgentPubkeys.size === 0) {
362
+ const delegationRecipient = ralRegistry.getDelegationRecipientPubkey(conversationId);
363
+
364
+ if (delegationRecipient) {
365
+ // Pre-emptive kill: Mark the agent+conversation as killed before it starts
366
+ ralRegistry.markAgentConversationKilled(delegationRecipient, conversationId);
367
+
368
+ trace.getActiveSpan()?.addEvent("kill.pre_emptive_kill", {
369
+ "kill.conversation_id": shortenConversationId(conversationId),
370
+ "kill.recipient_pubkey": delegationRecipient.substring(0, 12),
371
+ "kill.reason": reason ?? "pre-emptive kill via kill tool",
372
+ });
373
+
374
+ logger.info("[kill] Pre-emptive kill: agent will be aborted when it starts", {
375
+ conversationId: shortenConversationId(conversationId),
376
+ recipientPubkey: delegationRecipient.substring(0, 12),
377
+ reason,
378
+ });
379
+
380
+ // Also mark the parent delegation as killed
381
+ ralRegistry.markParentDelegationKilled(conversationId);
382
+
383
+ // Add to cooldown registry
384
+ const cooldownRegistry = CooldownRegistry.getInstance();
385
+ cooldownRegistry.add(projectId, conversationId, delegationRecipient, reason);
386
+
387
+ return {
388
+ success: true,
389
+ message: `Pre-emptive kill: agent will be aborted when it starts in conversation ${conversationId.substring(0, 12)}`,
390
+ target: conversationId,
391
+ targetType: "agent",
392
+ cascadeAbortCount: 1,
393
+ abortedTuples: [{ conversationId, agentPubkey: delegationRecipient }],
394
+ };
395
+ }
396
+
397
+ // No active agents and not a known delegation - nothing to kill
398
+ return {
399
+ success: false,
400
+ message: `No active agents found in conversation ${conversationId.substring(0, 12)}`,
401
+ target: conversationId,
402
+ targetType: "agent",
403
+ };
404
+ }
405
+
406
+ // NOTE: Authorization check was already performed at the top of killAgent(),
407
+ // before the pre-emptive kill branch. No duplicate check needed here.
408
+
409
+ // Get the first active agent (in multi-agent conversations, we abort all)
410
+ // Use the combined set that includes both ConversationStore and RALRegistry
411
+ const agentPubkey = Array.from(activeAgentPubkeys)[0];
412
+
413
+ trace.getActiveSpan()?.addEvent("kill.agent_abort_starting", {
414
+ "kill.project_id": projectId.substring(0, 12),
415
+ "kill.conversation_id": shortenConversationId(conversationId),
416
+ "kill.agent_pubkey": agentPubkey.substring(0, 12),
417
+ "kill.reason": reason ?? "manual kill",
418
+ "kill.cascade_enabled": true,
419
+ });
420
+
421
+ logger.info("[kill] Aborting agent with cascade", {
422
+ projectId: projectId.substring(0, 12),
423
+ conversationId: shortenConversationId(conversationId),
424
+ agentPubkey: agentPubkey.substring(0, 12),
425
+ reason,
426
+ });
427
+
428
+ // Perform cascading abort (blocks until all descendants are aborted)
429
+ const result = await ralRegistry.abortWithCascade(
430
+ agentPubkey,
431
+ conversationId,
432
+ projectId,
433
+ reason ?? "manual kill via kill tool",
434
+ cooldownRegistry
435
+ );
436
+
437
+ trace.getActiveSpan()?.addEvent("kill.agent_abort_completed", {
438
+ "kill.conversation_id": shortenConversationId(conversationId),
439
+ "kill.agent_pubkey": agentPubkey.substring(0, 12),
440
+ "kill.direct_aborted": result.abortedCount,
441
+ "kill.cascade_aborted": result.descendantConversations.length,
442
+ "kill.total_aborted": result.abortedCount + result.descendantConversations.length,
443
+ });
444
+
445
+ const totalAborted = result.abortedCount + result.descendantConversations.length;
446
+
447
+ return {
448
+ success: true,
449
+ message: `Aborted agent in conversation ${conversationId.substring(0, 12)} with ${result.descendantConversations.length} cascaded aborts`,
450
+ target: conversationId,
451
+ targetType: "agent",
452
+ cascadeAbortCount: totalAborted,
453
+ abortedTuples: [
454
+ { conversationId, agentPubkey },
455
+ ...result.descendantConversations,
456
+ ],
457
+ };
458
+ }
459
+
460
+ /**
461
+ * Kill a background shell task
462
+ */
463
+ function killShellTask(taskId: string, context: ToolExecutionContext): KillOutput {
464
+ // Get task info before killing (for reporting)
465
+ const taskInfo = getBackgroundTaskInfo(taskId);
466
+
467
+ // === AUTHORIZATION: Shell Task Project Isolation ===
468
+ // Shell tasks are bound to projectId at creation time.
469
+ // Verify caller's projectId matches task's projectId before allowing kill operation.
470
+ const callerConversation = context.getConversation?.();
471
+ const callerProjectId = callerConversation?.getProjectId();
472
+
473
+ if (!callerProjectId) {
474
+ logger.warn("[kill] Authorization check failed: caller has no project context for shell kill", {
475
+ callerAgent: context.agent?.slug ?? "unknown",
476
+ targetTaskId: taskId,
477
+ });
478
+
479
+ trace.getActiveSpan()?.addEvent("kill.shell_authorization_failed", {
480
+ "kill.reason": "caller_no_project",
481
+ "kill.caller_agent": context.agent?.slug ?? "unknown",
482
+ "kill.target_task_id": taskId,
483
+ });
484
+
485
+ return {
486
+ success: false,
487
+ message: `Authorization failed: cannot kill shell tasks without project context`,
488
+ target: taskId,
489
+ targetType: "shell",
490
+ };
491
+ }
492
+
493
+ // Verify task exists and belongs to caller's project
494
+ if (!taskInfo) {
495
+ logger.warn("[kill] Task not found", {
496
+ callerAgent: context.agent?.slug ?? "unknown",
497
+ targetTaskId: taskId,
498
+ });
499
+
500
+ return {
501
+ success: false,
502
+ message: `Task ${taskId} not found`,
503
+ target: taskId,
504
+ targetType: "shell",
505
+ };
506
+ }
507
+
508
+ // CRITICAL: Enforce project isolation - deny kill if projectId doesn't match
509
+ if (taskInfo.projectId !== callerProjectId) {
510
+ logger.warn("[kill] Authorization check failed: project isolation violation", {
511
+ callerAgent: context.agent?.slug ?? "unknown",
512
+ callerProjectId: callerProjectId.substring(0, 12),
513
+ taskProjectId: taskInfo.projectId.substring(0, 12),
514
+ targetTaskId: taskId,
515
+ });
516
+
517
+ trace.getActiveSpan()?.addEvent("kill.shell_authorization_failed", {
518
+ "kill.reason": "project_mismatch",
519
+ "kill.caller_agent": context.agent?.slug ?? "unknown",
520
+ "kill.caller_project_id": callerProjectId.substring(0, 12),
521
+ "kill.task_project_id": taskInfo.projectId.substring(0, 12),
522
+ "kill.target_task_id": taskId,
523
+ });
524
+
525
+ return {
526
+ success: false,
527
+ message: `Authorization failed: task ${taskId} belongs to a different project`,
528
+ target: taskId,
529
+ targetType: "shell",
530
+ };
531
+ }
532
+
533
+ // Log audit trail for shell kill
534
+ logger.info("[kill] Shell task kill requested", {
535
+ callerAgent: context.agent?.slug ?? "unknown",
536
+ callerConversationId: context.conversationId?.substring(0, 12) ?? "unknown",
537
+ callerProjectId: callerProjectId.substring(0, 12),
538
+ targetTaskId: taskId,
539
+ taskCommand: taskInfo?.command,
540
+ });
541
+
542
+ trace.getActiveSpan()?.addEvent("kill.shell_task_killing", {
543
+ "kill.caller_agent": context.agent?.slug ?? "unknown",
544
+ "kill.caller_project_id": callerProjectId.substring(0, 12),
545
+ "kill.target_task_id": taskId,
546
+ });
547
+
548
+ // Attempt to kill the task
549
+ const result = killBackgroundTask(taskId);
550
+
551
+ const output: KillOutput = {
552
+ success: result.success,
553
+ message: result.message,
554
+ target: taskId,
555
+ targetType: "shell",
556
+ pid: result.pid,
557
+ };
558
+
559
+ // Include task info if it was found
560
+ if (taskInfo) {
561
+ output.taskInfo = {
562
+ command: taskInfo.command,
563
+ description: taskInfo.description,
564
+ outputFile: taskInfo.outputFile,
565
+ startTime: taskInfo.startTime.toISOString(),
566
+ };
567
+ }
568
+
569
+ // If task not found, suggest listing tasks (filtered by project to prevent metadata leakage)
570
+ if (!result.success && !taskInfo && callerProjectId) {
571
+ // SECURITY: Only show tasks from caller's project to prevent cross-project information disclosure
572
+ const projectTasks = getAllBackgroundTasks().filter(
573
+ (t) => t.projectId === callerProjectId
574
+ );
575
+ if (projectTasks.length > 0) {
576
+ output.message += `\n\nAvailable background tasks in this project: ${projectTasks
577
+ .map((t) => t.taskId)
578
+ .join(", ")}`;
579
+ } else {
580
+ output.message += "\n\nNo background tasks are currently running in this project.";
581
+ }
582
+ }
583
+
584
+ // Log audit trail for result
585
+ if (result.success) {
586
+ logger.info("[kill] Shell task killed successfully", {
587
+ taskId,
588
+ pid: result.pid,
589
+ callerAgent: context.agent?.slug ?? "unknown",
590
+ projectId: callerProjectId.substring(0, 12),
591
+ });
592
+ }
593
+
594
+ return output;
595
+ }
596
+
597
+ /**
598
+ * Create an AI SDK tool for the unified kill command
599
+ */
600
+ export function createKillTool(context: ToolExecutionContext): AISdkTool {
601
+ const aiTool = tool({
602
+ description:
603
+ "Terminate an agent execution or background shell process. " +
604
+ "For agents: aborts the agent and all nested delegations with 15s cooldown. " +
605
+ "For shells: terminates the background process. " +
606
+ "IMPORTANT: This tool blocks until all cascade aborts complete.",
607
+
608
+ inputSchema: killSchema,
609
+
610
+ execute: async (input: KillInput) => {
611
+ return await executeKill(input, context);
612
+ },
613
+ });
614
+
615
+ Object.defineProperty(aiTool, "getHumanReadableContent", {
616
+ value: ({ target }: KillInput) => {
617
+ // Use the same detection logic as resolveTargetId for consistent classification
618
+ const trimmed = target.trim().toLowerCase();
619
+
620
+ // Check ID format to determine type
621
+ // Shell task IDs are 7-char alphanumeric
622
+ if (isShellTaskId(trimmed)) {
623
+ return `Killing background task ${target}`;
624
+ }
625
+
626
+ // Full event IDs (64-char hex) or short prefixes (12-char hex)
627
+ // are conversation/agent targets
628
+ if (isFullEventId(trimmed) || isShortEventId(trimmed)) {
629
+ return `Killing agent in conversation ${trimmed.substring(0, 12)} (with cascade)`;
630
+ }
631
+
632
+ // NIP-19 formats (nevent, note) are also agent targets
633
+ const normalized = normalizeNostrIdentifier(target);
634
+ if (normalized && (normalized.startsWith("nevent1") || normalized.startsWith("note1"))) {
635
+ return `Killing agent from ${target.substring(0, 20)}... (with cascade)`;
636
+ }
637
+
638
+ // UUID format is shell task (legacy)
639
+ if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(trimmed)) {
640
+ return `Killing background task ${target}`;
641
+ }
642
+
643
+ // Unknown format - let executeKill determine and error appropriately
644
+ return `Killing target ${target.substring(0, 12)}...`;
645
+ },
646
+ enumerable: false,
647
+ configurable: true,
648
+ });
649
+
650
+ return aiTool as AISdkTool;
651
+ }