@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,144 @@
1
+ /**
2
+ * Unified Search Type Definitions
3
+ *
4
+ * Interfaces for the search provider pattern that enables querying
5
+ * across multiple RAG collections (reports, conversations, lessons).
6
+ */
7
+
8
+ /**
9
+ * A single search result from any collection.
10
+ * Contains enough metadata for the agent to:
11
+ * 1. Understand what the result is about
12
+ * 2. Retrieve the full document via the appropriate tool
13
+ * (report_read, lesson_get, conversation_get)
14
+ */
15
+ export interface SearchResult {
16
+ /** Source collection name (e.g., "reports", "conversations", "lessons") */
17
+ source: string;
18
+
19
+ /** Identifier for retrieving the full document.
20
+ * - Reports: slug
21
+ * - Lessons: nevent/note encoded ID
22
+ * - Conversations: conversation ID
23
+ */
24
+ id: string;
25
+
26
+ /** Project ID this result belongs to */
27
+ projectId: string;
28
+
29
+ /** Relevance score from vector search (0-1, higher is better) */
30
+ relevanceScore: number;
31
+
32
+ /** Human-readable title */
33
+ title: string;
34
+
35
+ /** Brief summary or snippet of the content */
36
+ summary: string;
37
+
38
+ /** Unix timestamp of when the content was created/published */
39
+ createdAt?: number;
40
+
41
+ /** Unix timestamp of last update/activity */
42
+ updatedAt?: number;
43
+
44
+ /** Author pubkey */
45
+ author?: string;
46
+
47
+ /** Author display name */
48
+ authorName?: string;
49
+
50
+ /** Hashtags/categories for additional context */
51
+ tags?: string[];
52
+
53
+ /** Which tool to use to retrieve full content */
54
+ retrievalTool: "report_read" | "lesson_get" | "conversation_get";
55
+
56
+ /** The argument to pass to the retrieval tool */
57
+ retrievalArg: string;
58
+ }
59
+
60
+ /**
61
+ * Options for performing a unified search.
62
+ */
63
+ export interface SearchOptions {
64
+ /** Natural language search query */
65
+ query: string;
66
+
67
+ /** Project ID for project-scoped isolation */
68
+ projectId: string;
69
+
70
+ /** Maximum results per collection */
71
+ limit?: number;
72
+
73
+ /** Minimum relevance score threshold (0-1) */
74
+ minScore?: number;
75
+
76
+ /** Optional prompt for LLM-based extraction/focusing.
77
+ * When provided, results are processed through a fast LLM to
78
+ * extract information relevant to this specific prompt.
79
+ */
80
+ prompt?: string;
81
+
82
+ /** Which collections to search (defaults to all) */
83
+ collections?: string[];
84
+ }
85
+
86
+ /**
87
+ * Interface that each search provider must implement.
88
+ * Each provider wraps a specific RAG collection.
89
+ */
90
+ export interface SearchProvider {
91
+ /** Unique name for this provider (matches collection concept) */
92
+ readonly name: string;
93
+
94
+ /** Human-readable description */
95
+ readonly description: string;
96
+
97
+ /**
98
+ * Perform semantic search within this provider's collection.
99
+ *
100
+ * @param query - Natural language search query
101
+ * @param projectId - Project ID for isolation
102
+ * @param limit - Maximum number of results
103
+ * @param minScore - Minimum relevance score threshold
104
+ * @returns Array of search results
105
+ */
106
+ search(
107
+ query: string,
108
+ projectId: string,
109
+ limit: number,
110
+ minScore: number
111
+ ): Promise<SearchResult[]>;
112
+ }
113
+
114
+ /**
115
+ * Output from the unified search tool.
116
+ */
117
+ export interface UnifiedSearchOutput {
118
+ /** Whether the search succeeded */
119
+ success: boolean;
120
+
121
+ /** Combined results from all collections, sorted by relevance */
122
+ results: SearchResult[];
123
+
124
+ /** Total number of results found */
125
+ totalResults: number;
126
+
127
+ /** The original query */
128
+ query: string;
129
+
130
+ /** Which collections were searched */
131
+ collectionsSearched: string[];
132
+
133
+ /** Which collections had errors (graceful degradation) */
134
+ collectionsErrored?: string[];
135
+
136
+ /** Warnings (e.g., collection failures) */
137
+ warnings?: string[];
138
+
139
+ /** Extracted/focused content when a prompt was provided */
140
+ extraction?: string;
141
+
142
+ /** Error message if the entire search failed */
143
+ error?: string;
144
+ }
@@ -0,0 +1,482 @@
1
+ import * as fs from "node:fs/promises";
2
+ import * as path from "node:path";
3
+ import * as crypto from "node:crypto";
4
+ import { getNDK } from "@/nostr";
5
+ import { NDKKind } from "@/nostr/kinds";
6
+ import { getTenexBasePath } from "@/constants";
7
+ import { ensureDirectory } from "@/lib/fs";
8
+ import { logger } from "@/utils/logger";
9
+ import type { NDKEvent } from "@nostr-dev-kit/ndk";
10
+ import { SpanStatusCode, context as otelContext, trace } from "@opentelemetry/api";
11
+ import type { SkillResult, SkillData, SkillFileInfo, SkillFileInstallResult } from "./types";
12
+
13
+ const tracer = trace.getTracer("tenex.skill-service");
14
+
15
+ const DOWNLOAD_TIMEOUT_MS = 30_000;
16
+ /** Maximum file download size: 10MB */
17
+ const MAX_DOWNLOAD_SIZE_BYTES = 10 * 1024 * 1024;
18
+
19
+ /**
20
+ * Service for fetching and processing Agent Skill events (kind:4202)
21
+ * Single Responsibility: Retrieve skill content, download attached files,
22
+ * and prepare for system prompt injection.
23
+ *
24
+ * Skills are stored in .tenex/skills/<short-id>/ directory structure.
25
+ */
26
+ export class SkillService {
27
+ private static instance: SkillService;
28
+
29
+ private constructor() {}
30
+
31
+ static getInstance(): SkillService {
32
+ if (!SkillService.instance) {
33
+ SkillService.instance = new SkillService();
34
+ }
35
+ return SkillService.instance;
36
+ }
37
+
38
+ /**
39
+ * Get the base directory for skill files
40
+ * @returns Path to .tenex/skills/
41
+ */
42
+ private async getSkillsBaseDir(): Promise<string> {
43
+ const basePath = getTenexBasePath();
44
+ const skillsDir = path.join(basePath, "skills");
45
+ await ensureDirectory(skillsDir);
46
+ return skillsDir;
47
+ }
48
+
49
+ /**
50
+ * Get the directory for a specific skill
51
+ * @param shortId Short event ID (first 12 chars)
52
+ * @returns Path to .tenex/skills/<short-id>/
53
+ */
54
+ private async getSkillDir(shortId: string): Promise<string> {
55
+ const baseDir = await this.getSkillsBaseDir();
56
+ const skillDir = path.join(baseDir, shortId);
57
+ await ensureDirectory(skillDir);
58
+ return skillDir;
59
+ }
60
+
61
+ /**
62
+ * Fetch skill events by IDs and process their content and attached files.
63
+ *
64
+ * @param eventIds Array of skill event IDs to fetch
65
+ * @returns SkillResult with skills data and concatenated content
66
+ */
67
+ async fetchSkills(eventIds: string[]): Promise<SkillResult> {
68
+ const emptyResult: SkillResult = {
69
+ skills: [],
70
+ content: "",
71
+ };
72
+
73
+ if (eventIds.length === 0) {
74
+ return emptyResult;
75
+ }
76
+
77
+ const span = tracer.startSpan("tenex.skill.fetch_skills", {
78
+ attributes: {
79
+ "skill.requested_count": eventIds.length,
80
+ },
81
+ }, otelContext.active());
82
+
83
+ return otelContext.with(trace.setSpan(otelContext.active(), span), async () => {
84
+ try {
85
+ const ndk = getNDK();
86
+ const skillEvents = await ndk.fetchEvents({
87
+ ids: eventIds,
88
+ });
89
+
90
+ const skills = Array.from(skillEvents);
91
+
92
+ // Filter to only kind:4202 events
93
+ const validSkills = skills.filter((event) => event.kind === NDKKind.AgentSkill);
94
+
95
+ // Process each skill and its attached files
96
+ const skillDataArray: SkillData[] = [];
97
+
98
+ for (const skill of validSkills) {
99
+ const skillData = await this.processSkillEvent(skill);
100
+ if (skillData) {
101
+ skillDataArray.push(skillData);
102
+ }
103
+ }
104
+
105
+ // Concatenate content for backward compatibility
106
+ const concatenated = skillDataArray
107
+ .map((data) => data.content)
108
+ .filter((content) => content.length > 0)
109
+ .join("\n\n");
110
+
111
+ const skillTitles = skillDataArray
112
+ .map((s) => s.title || s.name || "untitled")
113
+ .join(", ");
114
+
115
+ span.setAttributes({
116
+ "skill.fetched_count": validSkills.length,
117
+ "skill.content_length": concatenated.length,
118
+ "skill.titles": skillTitles,
119
+ "skill.total_files": skillDataArray.reduce(
120
+ (acc, s) => acc + s.installedFiles.length,
121
+ 0
122
+ ),
123
+ });
124
+
125
+ span.setStatus({ code: SpanStatusCode.OK });
126
+ span.end();
127
+
128
+ return {
129
+ skills: skillDataArray,
130
+ content: concatenated,
131
+ };
132
+ } catch (error) {
133
+ span.recordException(error as Error);
134
+ span.setStatus({
135
+ code: SpanStatusCode.ERROR,
136
+ message: (error as Error).message,
137
+ });
138
+ span.end();
139
+ logger.error("[SkillService] Failed to fetch skills", { error });
140
+ return emptyResult;
141
+ }
142
+ });
143
+ }
144
+
145
+ /**
146
+ * Process a single skill event: extract content, metadata, and download attached files.
147
+ *
148
+ * @param event The skill event (kind:4202)
149
+ * @returns SkillData or null if invalid
150
+ */
151
+ private async processSkillEvent(event: NDKEvent): Promise<SkillData | null> {
152
+ const content = event.content.trim();
153
+ const title = event.tagValue("title") || undefined;
154
+ const name = event.tagValue("name") || undefined;
155
+ const shortId = event.id.substring(0, 12);
156
+
157
+ // Extract e-tags that reference kind:1063 (NIP-94 file metadata) events
158
+ const fileETags = this.extractFileETags(event);
159
+
160
+ // Download and install attached files
161
+ const installedFiles = await this.installSkillFiles(fileETags, shortId);
162
+
163
+ return {
164
+ content,
165
+ title,
166
+ name,
167
+ shortId,
168
+ installedFiles,
169
+ };
170
+ }
171
+
172
+ /**
173
+ * Extract e-tags from a skill event that reference kind:1063 file metadata events.
174
+ * Format: ["e", "<event-id>", "<relay-url>?"]
175
+ *
176
+ * NOTE: Relay hints (tag[2]) are parsed but not currently used. NDK's fetchEvent
177
+ * handles relay discovery automatically through our connected relay pool. Adding
178
+ * explicit relay hints would require significant refactoring and the current approach
179
+ * works well for events that are available on commonly-connected relays.
180
+ *
181
+ * @param event The skill event
182
+ * @returns Array of event IDs to fetch
183
+ */
184
+ private extractFileETags(event: NDKEvent): string[] {
185
+ return event.tags
186
+ .filter((tag) => tag[0] === "e" && tag[1])
187
+ .map((tag) => tag[1]);
188
+ }
189
+
190
+ /**
191
+ * Fetch and install files referenced by e-tags in a skill event.
192
+ *
193
+ * @param fileEventIds Array of event IDs referencing kind:1063 events
194
+ * @param shortId Short skill ID for directory naming
195
+ * @returns Array of installation results
196
+ */
197
+ private async installSkillFiles(
198
+ fileEventIds: string[],
199
+ shortId: string
200
+ ): Promise<SkillFileInstallResult[]> {
201
+ if (fileEventIds.length === 0) {
202
+ return [];
203
+ }
204
+
205
+ const results: SkillFileInstallResult[] = [];
206
+ const ndk = getNDK();
207
+ const skillDir = await this.getSkillDir(shortId);
208
+
209
+ for (const eventId of fileEventIds) {
210
+ try {
211
+ // Fetch the kind:1063 file metadata event
212
+ const fileEvent = await ndk.fetchEvent(eventId, { groupable: false });
213
+
214
+ if (!fileEvent) {
215
+ results.push({
216
+ eventId,
217
+ relativePath: "unknown",
218
+ absolutePath: "unknown",
219
+ success: false,
220
+ error: `Could not fetch event ${eventId}`,
221
+ });
222
+ continue;
223
+ }
224
+
225
+ // Verify it's a kind:1063 event
226
+ if (fileEvent.kind !== 1063) {
227
+ results.push({
228
+ eventId,
229
+ relativePath: "unknown",
230
+ absolutePath: "unknown",
231
+ success: false,
232
+ error: `Event ${eventId} is not kind:1063 (got kind:${fileEvent.kind})`,
233
+ });
234
+ continue;
235
+ }
236
+
237
+ // Extract file info from the event
238
+ const fileInfo = this.extractFileInfo(fileEvent);
239
+ if (!fileInfo) {
240
+ results.push({
241
+ eventId,
242
+ relativePath: "unknown",
243
+ absolutePath: "unknown",
244
+ success: false,
245
+ error: "Missing required tags (url, name) in kind:1063 event",
246
+ });
247
+ continue;
248
+ }
249
+
250
+ // Download and install the file
251
+ const result = await this.installFile(fileInfo, skillDir);
252
+ results.push(result);
253
+ } catch (error) {
254
+ const errorMessage = error instanceof Error ? error.message : String(error);
255
+ results.push({
256
+ eventId,
257
+ relativePath: "unknown",
258
+ absolutePath: "unknown",
259
+ success: false,
260
+ error: errorMessage,
261
+ });
262
+ }
263
+ }
264
+
265
+ // Log summary
266
+ const successCount = results.filter((r) => r.success).length;
267
+ const failCount = results.filter((r) => !r.success).length;
268
+
269
+ if (failCount > 0) {
270
+ logger.warn(`[SkillService] Skill file installation completed with errors`, {
271
+ skillId: shortId,
272
+ success: successCount,
273
+ failed: failCount,
274
+ });
275
+ } else if (successCount > 0) {
276
+ logger.info(`[SkillService] All skill files installed successfully`, {
277
+ skillId: shortId,
278
+ count: successCount,
279
+ });
280
+ }
281
+
282
+ return results;
283
+ }
284
+
285
+ /**
286
+ * Extract file information from a kind:1063 (NIP-94) event.
287
+ *
288
+ * Expected tags:
289
+ * - ["url", "https://blossom.server/sha256"] - Required: Blossom download URL
290
+ * - ["name", "relative/path/file.ext"] - Required: Relative filepath
291
+ * - ["m", "text/plain"] - Optional: MIME type
292
+ * - ["x", "sha256hash"] - Optional: SHA-256 hash for verification
293
+ *
294
+ * @param event The kind:1063 event
295
+ * @returns SkillFileInfo or null if required tags are missing
296
+ */
297
+ private extractFileInfo(event: NDKEvent): SkillFileInfo | null {
298
+ const url = event.tagValue("url");
299
+ const relativePath = event.tagValue("name");
300
+
301
+ if (!url || !relativePath) {
302
+ logger.warn(`[SkillService] Kind 1063 event ${event.id} missing required tags`, {
303
+ hasUrl: !!url,
304
+ hasName: !!relativePath,
305
+ });
306
+ return null;
307
+ }
308
+
309
+ return {
310
+ eventId: event.id,
311
+ url,
312
+ relativePath,
313
+ mimeType: event.tagValue("m") || undefined,
314
+ sha256: event.tagValue("x") || undefined,
315
+ };
316
+ }
317
+
318
+ /**
319
+ * Download and install a single file to the skill directory.
320
+ *
321
+ * @param fileInfo Information about the file to install
322
+ * @param skillDir Base directory for this skill
323
+ * @returns Installation result
324
+ */
325
+ private async installFile(
326
+ fileInfo: SkillFileInfo,
327
+ skillDir: string
328
+ ): Promise<SkillFileInstallResult> {
329
+ // Resolve to absolute path, normalizing any ../ components
330
+ const resolvedSkillDir = path.resolve(skillDir);
331
+ const absolutePath = path.resolve(skillDir, fileInfo.relativePath);
332
+
333
+ try {
334
+ // Security check: ensure the resolved path stays within the skill directory
335
+ // Using path.relative and checking for ".." prefix is more robust than startsWith
336
+ const relativeToBoundary = path.relative(resolvedSkillDir, absolutePath);
337
+ if (relativeToBoundary.startsWith("..") || path.isAbsolute(relativeToBoundary)) {
338
+ throw new Error(
339
+ `Security violation: path "${fileInfo.relativePath}" would escape skill directory`
340
+ );
341
+ }
342
+
343
+ // Create parent directories
344
+ const parentDir = path.dirname(absolutePath);
345
+ await ensureDirectory(parentDir);
346
+
347
+ // Download the file with size limit
348
+ logger.debug(`[SkillService] Downloading file from ${fileInfo.url}`);
349
+ const content = await this.downloadFile(fileInfo.url);
350
+
351
+ // Verify SHA-256 hash if provided (NIP-94 "x" tag)
352
+ if (fileInfo.sha256) {
353
+ const actualHash = crypto.createHash("sha256").update(content).digest("hex");
354
+ if (actualHash.toLowerCase() !== fileInfo.sha256.toLowerCase()) {
355
+ throw new Error(
356
+ `SHA-256 hash mismatch: expected ${fileInfo.sha256}, got ${actualHash}`
357
+ );
358
+ }
359
+ logger.debug(`[SkillService] SHA-256 verification passed for ${fileInfo.relativePath}`);
360
+ }
361
+
362
+ // Write the file
363
+ await fs.writeFile(absolutePath, content);
364
+
365
+ logger.info(`[SkillService] Installed skill file: ${fileInfo.relativePath}`, {
366
+ eventId: fileInfo.eventId,
367
+ absolutePath,
368
+ size: content.length,
369
+ });
370
+
371
+ return {
372
+ eventId: fileInfo.eventId,
373
+ relativePath: fileInfo.relativePath,
374
+ absolutePath,
375
+ success: true,
376
+ };
377
+ } catch (error) {
378
+ const errorMessage = error instanceof Error ? error.message : String(error);
379
+ logger.error(`[SkillService] Failed to install skill file: ${fileInfo.relativePath}`, {
380
+ eventId: fileInfo.eventId,
381
+ error: errorMessage,
382
+ });
383
+
384
+ return {
385
+ eventId: fileInfo.eventId,
386
+ relativePath: fileInfo.relativePath,
387
+ absolutePath,
388
+ success: false,
389
+ error: errorMessage,
390
+ };
391
+ }
392
+ }
393
+
394
+ /**
395
+ * Download a file from a Blossom URL with size limit enforcement.
396
+ *
397
+ * @param url The Blossom URL to download from
398
+ * @returns The downloaded file content as a Buffer
399
+ * @throws Error if download exceeds MAX_DOWNLOAD_SIZE_BYTES
400
+ */
401
+ private async downloadFile(url: string): Promise<Buffer> {
402
+ const controller = new AbortController();
403
+ const timeout = setTimeout(() => controller.abort(), DOWNLOAD_TIMEOUT_MS);
404
+
405
+ try {
406
+ const response = await fetch(url, {
407
+ headers: {
408
+ "User-Agent": "TENEX/1.0 (Skill Service)",
409
+ },
410
+ signal: controller.signal,
411
+ });
412
+
413
+ if (!response.ok) {
414
+ throw new Error(`Failed to download: ${response.status} ${response.statusText}`);
415
+ }
416
+
417
+ // Check Content-Length header first if available
418
+ const contentLength = response.headers.get("Content-Length");
419
+ if (contentLength) {
420
+ const declaredSize = parseInt(contentLength, 10);
421
+ if (declaredSize > MAX_DOWNLOAD_SIZE_BYTES) {
422
+ throw new Error(
423
+ `File too large: ${declaredSize} bytes exceeds ${MAX_DOWNLOAD_SIZE_BYTES} byte limit`
424
+ );
425
+ }
426
+ }
427
+
428
+ // Stream the response and enforce size limit during download
429
+ const reader = response.body?.getReader();
430
+ if (!reader) {
431
+ throw new Error("Response body is not readable");
432
+ }
433
+
434
+ const chunks: Uint8Array[] = [];
435
+ let totalSize = 0;
436
+
437
+ while (true) {
438
+ const { done, value } = await reader.read();
439
+ if (done) break;
440
+
441
+ totalSize += value.length;
442
+ if (totalSize > MAX_DOWNLOAD_SIZE_BYTES) {
443
+ reader.cancel();
444
+ throw new Error(
445
+ `File too large: exceeded ${MAX_DOWNLOAD_SIZE_BYTES} byte limit during download`
446
+ );
447
+ }
448
+ chunks.push(value);
449
+ }
450
+
451
+ return Buffer.concat(chunks);
452
+ } catch (error) {
453
+ if (error instanceof Error && error.name === "AbortError") {
454
+ throw new Error(`Download timed out after ${DOWNLOAD_TIMEOUT_MS}ms`);
455
+ }
456
+ throw error;
457
+ } finally {
458
+ clearTimeout(timeout);
459
+ }
460
+ }
461
+
462
+ /**
463
+ * Fetch a single skill event by ID.
464
+ *
465
+ * @param eventId The skill event ID
466
+ * @returns The skill event or null if not found
467
+ */
468
+ async fetchSkill(eventId: string): Promise<NDKEvent | null> {
469
+ try {
470
+ const ndk = getNDK();
471
+ const events = await ndk.fetchEvents({
472
+ ids: [eventId],
473
+ });
474
+
475
+ const skill = Array.from(events).find((event) => event.kind === NDKKind.AgentSkill);
476
+ return skill || null;
477
+ } catch (error) {
478
+ logger.error("[SkillService] Failed to fetch skill", { error, eventId });
479
+ return null;
480
+ }
481
+ }
482
+ }
@@ -0,0 +1,2 @@
1
+ export { SkillService } from "./SkillService";
2
+ export type { SkillResult, SkillData, SkillFileInfo, SkillFileInstallResult } from "./types";
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Skill Event Types
3
+ *
4
+ * Defines the structure for skill events (kind:4202).
5
+ * Skills are transient capabilities that can be injected into agent system prompts.
6
+ *
7
+ * Unlike nudges, skills do NOT have tool permissions (only-tool, allow-tool, deny-tool).
8
+ * Skills are focused on providing additional context, instructions, and attached files.
9
+ */
10
+
11
+ /**
12
+ * Information about a file attached to a skill via NIP-94 (kind 1063) event reference
13
+ */
14
+ export interface SkillFileInfo {
15
+ /** Event ID of the kind:1063 event */
16
+ eventId: string;
17
+ /** Blossom URL to download the file */
18
+ url: string;
19
+ /** Relative path where the file should be stored (from NIP-94 "name" tag) */
20
+ relativePath: string;
21
+ /** Optional MIME type (from NIP-94 "m" tag) */
22
+ mimeType?: string;
23
+ /** Optional SHA-256 hash for verification (from NIP-94 "x" tag) */
24
+ sha256?: string;
25
+ }
26
+
27
+ /**
28
+ * Result of downloading and installing a skill file
29
+ */
30
+ export interface SkillFileInstallResult {
31
+ /** Event ID of the source kind:1063 event */
32
+ eventId: string;
33
+ /** Relative path within the skill directory */
34
+ relativePath: string;
35
+ /** Absolute path where the file was installed */
36
+ absolutePath: string;
37
+ /** Whether the installation succeeded */
38
+ success: boolean;
39
+ /** Error message if installation failed */
40
+ error?: string;
41
+ }
42
+
43
+ /**
44
+ * Individual skill data with content, title, and attached files
45
+ */
46
+ export interface SkillData {
47
+ /** The skill content/instructions */
48
+ content: string;
49
+ /** The skill title (from "title" tag) */
50
+ title?: string;
51
+ /** The skill name/identifier (from "name" tag) */
52
+ name?: string;
53
+ /** Short ID for directory naming (first 12 chars of event ID) */
54
+ shortId: string;
55
+ /** List of installed files for this skill */
56
+ installedFiles: SkillFileInstallResult[];
57
+ }
58
+
59
+ /**
60
+ * Result from fetching skills with their attached files.
61
+ * Contains both the concatenated content for system prompt injection
62
+ * and the individual skill data with file information.
63
+ */
64
+ export interface SkillResult {
65
+ /** Individual skill data for rendering in the fragment */
66
+ skills: SkillData[];
67
+
68
+ /** Concatenated content from all skills (for backward compatibility) */
69
+ content: string;
70
+ }