@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,365 @@
1
+ import type NDK from "@nostr-dev-kit/ndk";
2
+ import { type NDKEvent, NDKUser } from "@nostr-dev-kit/ndk";
3
+ import { prefixKVStore } from "@/services/storage";
4
+ import { nip19 } from "nostr-tools";
5
+ import type { NDKAgentLesson } from "@/events/NDKAgentLesson";
6
+ import type { FullEventId, ShortEventId } from "@/types/event-ids";
7
+ import { isFullEventId, isShortEventId } from "@/types/event-ids";
8
+
9
+ /**
10
+ * The standard prefix length used for shortened hex IDs throughout the system.
11
+ * Used for delegation IDs, conversation IDs, and other nostr identifiers.
12
+ */
13
+ export const PREFIX_LENGTH = 12;
14
+
15
+ /**
16
+ * Parses various Nostr user identifier formats into a pubkey
17
+ * Handles: npub, nprofile, hex pubkey, with or without "nostr:" prefix
18
+ *
19
+ * @param input - The user identifier in various formats
20
+ * @param ndk - NDK instance for validation
21
+ * @returns The parsed pubkey or null if invalid
22
+ */
23
+ export function parseNostrUser(input: string | undefined): string | null {
24
+ if (!input) return null;
25
+
26
+ try {
27
+ // Strip nostr: prefix if present
28
+ let cleaned = input.trim();
29
+ if (cleaned.startsWith("nostr:")) {
30
+ cleaned = cleaned.substring(6);
31
+ }
32
+
33
+ // Handle npub format
34
+ if (cleaned.startsWith("npub1")) {
35
+ const user = new NDKUser({ npub: cleaned });
36
+ return user.pubkey;
37
+ }
38
+
39
+ // Handle nprofile format
40
+ if (cleaned.startsWith("nprofile1")) {
41
+ const user = new NDKUser({ nprofile: cleaned });
42
+ return user.pubkey;
43
+ }
44
+
45
+ // Assume it's a hex pubkey - validate format
46
+ if (/^[0-9a-fA-F]{64}$/.test(cleaned)) {
47
+ return cleaned.toLowerCase();
48
+ }
49
+
50
+ // Try to create user anyway in case it's a valid format we didn't check
51
+ try {
52
+ const user = new NDKUser({ pubkey: cleaned });
53
+ if (user.pubkey && /^[0-9a-fA-F]{64}$/.test(user.pubkey)) {
54
+ return user.pubkey;
55
+ }
56
+ } catch {
57
+ // Ignore and return null
58
+ }
59
+
60
+ return null;
61
+ } catch (error) {
62
+ console.debug("Failed to parse Nostr user identifier:", input, error);
63
+ return null;
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Parses various Nostr event identifier formats and fetches the event
69
+ * Handles: nevent, note, naddr, hex event id, with or without "nostr:" prefix
70
+ *
71
+ * @param input - The event identifier in various formats
72
+ * @param ndk - NDK instance for fetching
73
+ * @returns The fetched event or null if not found/invalid
74
+ */
75
+ export async function parseNostrEvent(
76
+ input: string | undefined,
77
+ ndk: NDK
78
+ ): Promise<NDKEvent | null> {
79
+ if (!input) return null;
80
+
81
+ try {
82
+ // Strip nostr: prefix if present
83
+ let cleaned = input.trim();
84
+ if (cleaned.startsWith("nostr:")) {
85
+ cleaned = cleaned.substring(6);
86
+ }
87
+
88
+ // Try to fetch directly - NDK handles various formats
89
+ if (
90
+ cleaned.startsWith("nevent1") ||
91
+ cleaned.startsWith("note1") ||
92
+ cleaned.startsWith("naddr1")
93
+ ) {
94
+ const event = await ndk.fetchEvent(cleaned);
95
+ return event;
96
+ }
97
+
98
+ // Try as hex event ID
99
+ if (/^[0-9a-fA-F]{64}$/.test(cleaned)) {
100
+ const event = await ndk.fetchEvent(cleaned);
101
+ return event;
102
+ }
103
+
104
+ // Last attempt - try to fetch as-is
105
+ const event = await ndk.fetchEvent(cleaned);
106
+ return event;
107
+ } catch (error) {
108
+ console.debug("Failed to parse/fetch Nostr event:", input, error);
109
+ return null;
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Validates and normalizes a Nostr identifier, removing prefixes
115
+ * Returns the cleaned identifier or null if invalid
116
+ */
117
+ export function normalizeNostrIdentifier(input: string | undefined): string | null {
118
+ if (!input) return null;
119
+
120
+ let cleaned = input.trim();
121
+ if (cleaned.startsWith("nostr:")) {
122
+ cleaned = cleaned.substring(6);
123
+ }
124
+
125
+ // Basic validation - should be bech32 or hex
126
+ if (
127
+ cleaned.match(/^(npub1|nprofile1|nevent1|note1|nsec1|naddr1)[0-9a-z]+$/i) ||
128
+ cleaned.match(/^[0-9a-fA-F]{64}$/)
129
+ ) {
130
+ return cleaned;
131
+ }
132
+
133
+ return null;
134
+ }
135
+
136
+ /**
137
+ * Checks if a string looks like a 12-char hex prefix (potential shorthand ID).
138
+ * Note: This is a pure format check - it doesn't do any lookup.
139
+ * For resolving prefixes to actual IDs, use the appropriate service.
140
+ */
141
+ export function isHexPrefix(input: string | undefined): boolean {
142
+ if (!input) return false;
143
+ return /^[0-9a-fA-F]{12}$/.test(input.trim());
144
+ }
145
+
146
+ /**
147
+ * Resolves a 12-character hex prefix to a full 64-character ID using PrefixKVStore.
148
+ * This enables shorthand references to event IDs and pubkeys.
149
+ *
150
+ * IMPORTANT: This function is TYPE-AGNOSTIC - it returns any matching ID without
151
+ * validating whether it's an event ID or pubkey. For resolving specifically to
152
+ * agent pubkeys, use `resolveAgentSlug` from the AgentResolution service.
153
+ *
154
+ * @param prefix - A 12-character hex string prefix
155
+ * @returns The full 64-character ID, or null if not found or invalid input
156
+ */
157
+ export function resolvePrefixToId(prefix: string | undefined): string | null {
158
+ if (!prefix) return null;
159
+
160
+ const cleaned = prefix.trim().toLowerCase();
161
+
162
+ // Must be exactly 12 hex characters
163
+ if (!/^[0-9a-f]{12}$/.test(cleaned)) {
164
+ return null;
165
+ }
166
+
167
+ // Check if store is initialized (best-effort lookup)
168
+ if (!prefixKVStore.isInitialized()) {
169
+ console.debug("[resolvePrefixToId] PrefixKVStore not initialized, cannot resolve prefix");
170
+ return null;
171
+ }
172
+
173
+ // Wrap lookup in try/catch - LMDB can throw
174
+ try {
175
+ return prefixKVStore.lookup(cleaned);
176
+ } catch (error) {
177
+ const message = error instanceof Error ? error.message : String(error);
178
+ console.debug("[resolvePrefixToId] Prefix lookup failed:", cleaned, message);
179
+ return null;
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Resolves various event ID formats to a typed FullEventId.
185
+ *
186
+ * Accepts:
187
+ * - Full 64-character hex IDs (returns as-is after validation)
188
+ * - 12-character hex prefixes (resolved via PrefixKVStore)
189
+ * - NIP-19 formats: note1..., nevent1...
190
+ * - nostr: prefixed versions of all the above
191
+ *
192
+ * @param input - The event ID in any supported format
193
+ * @returns A typed FullEventId, or null if resolution failed
194
+ */
195
+ export function resolveToFullEventId(input: string | undefined): FullEventId | null {
196
+ if (!input) return null;
197
+
198
+ const trimmed = input.trim();
199
+
200
+ // Strip nostr: prefix if present
201
+ const cleaned = trimmed.startsWith("nostr:") ? trimmed.slice(6) : trimmed;
202
+ const normalized = cleaned.toLowerCase();
203
+
204
+ // 1. Check for full 64-char hex ID
205
+ if (isFullEventId(normalized)) {
206
+ return normalized;
207
+ }
208
+
209
+ // 2. Check for 12-char hex prefix
210
+ if (isShortEventId(normalized)) {
211
+ const resolved = resolvePrefixToId(normalized);
212
+ if (resolved && isFullEventId(resolved)) {
213
+ return resolved;
214
+ }
215
+ return null;
216
+ }
217
+
218
+ // 3. Try NIP-19 decoding (note1..., nevent1...)
219
+ try {
220
+ const decoded = nip19.decode(cleaned);
221
+ if (decoded.type === "note" && typeof decoded.data === "string") {
222
+ const eventId = decoded.data.toLowerCase();
223
+ if (isFullEventId(eventId)) {
224
+ return eventId;
225
+ }
226
+ }
227
+ if (decoded.type === "nevent" && typeof decoded.data === "object" && decoded.data !== null) {
228
+ const data = decoded.data as { id: string };
229
+ const eventId = data.id.toLowerCase();
230
+ if (isFullEventId(eventId)) {
231
+ return eventId;
232
+ }
233
+ }
234
+ } catch {
235
+ // Not a valid NIP-19 format
236
+ }
237
+
238
+ return null;
239
+ }
240
+
241
+ /**
242
+ * Type-safe version of resolvePrefixToId that returns a typed FullEventId
243
+ *
244
+ * @param prefix - A ShortEventId (12-char hex prefix)
245
+ * @returns A typed FullEventId, or null if not found
246
+ */
247
+ export function resolvePrefixToFullEventId(prefix: ShortEventId): FullEventId | null {
248
+ const resolved = resolvePrefixToId(prefix);
249
+ if (resolved && isFullEventId(resolved)) {
250
+ return resolved;
251
+ }
252
+ return null;
253
+ }
254
+
255
+ /**
256
+ * Result type for normalizeLessonEventId - either success with eventId or error with message
257
+ */
258
+ export type NormalizeLessonEventIdResult =
259
+ | { success: true; eventId: string }
260
+ | { success: false; error: string; errorType: "invalid_format" | "prefix_not_found" | "store_not_initialized" };
261
+
262
+ /**
263
+ * Normalizes various lesson event ID formats to a canonical 64-char lowercase hex ID.
264
+ *
265
+ * Accepts:
266
+ * - Full 64-character hex IDs
267
+ * - 12-character hex prefixes (resolved via PrefixKVStore or in-memory fallback)
268
+ * - NIP-19 formats: note1..., nevent1...
269
+ * - nostr: prefixed versions of all the above
270
+ *
271
+ * This bridges the contract mismatch between lesson_learn (which emits NIP-19 encoded IDs)
272
+ * and lesson_get (which needs hex IDs for lookup).
273
+ *
274
+ * @param input - The event ID in any supported format
275
+ * @param allLessons - Optional array of all lessons for in-memory prefix fallback
276
+ * @returns Result object with either normalized eventId or error details
277
+ */
278
+ export function normalizeLessonEventId(
279
+ input: string,
280
+ allLessons?: NDKAgentLesson[]
281
+ ): NormalizeLessonEventIdResult {
282
+ const trimmed = input.trim();
283
+
284
+ // Strip nostr: prefix if present
285
+ const cleaned = trimmed.startsWith("nostr:") ? trimmed.slice(6) : trimmed;
286
+
287
+ // 1. Check for full 64-char hex ID
288
+ if (/^[0-9a-f]{64}$/i.test(cleaned)) {
289
+ return { success: true, eventId: cleaned.toLowerCase() };
290
+ }
291
+
292
+ // 2. Check for 12-char hex prefix
293
+ if (isHexPrefix(cleaned)) {
294
+ const prefix = cleaned.toLowerCase();
295
+
296
+ // Try PrefixKVStore first
297
+ if (prefixKVStore.isInitialized()) {
298
+ try {
299
+ const resolved = prefixKVStore.lookup(prefix);
300
+ if (resolved) {
301
+ return { success: true, eventId: resolved };
302
+ }
303
+ } catch (error) {
304
+ console.debug("[normalizeLessonEventId] PrefixKVStore lookup error:", error);
305
+ // Fall through to in-memory fallback
306
+ }
307
+ }
308
+
309
+ // In-memory fallback: scan lessons for unique prefix match
310
+ if (allLessons && allLessons.length > 0) {
311
+ const matches = allLessons.filter((l) => l.id?.toLowerCase().startsWith(prefix));
312
+ if (matches.length === 1 && matches[0].id) {
313
+ return { success: true, eventId: matches[0].id.toLowerCase() };
314
+ }
315
+ if (matches.length > 1) {
316
+ return {
317
+ success: false,
318
+ error: `Prefix "${input}" is ambiguous - matches ${matches.length} lessons. Use a longer prefix or full event ID.`,
319
+ errorType: "prefix_not_found",
320
+ };
321
+ }
322
+ }
323
+
324
+ // Distinguish between "store not initialized" vs "not found"
325
+ if (!prefixKVStore.isInitialized()) {
326
+ return {
327
+ success: false,
328
+ error: `Could not resolve prefix "${input}" - PrefixKVStore is not initialized and no in-memory matches found.`,
329
+ errorType: "store_not_initialized",
330
+ };
331
+ }
332
+
333
+ return {
334
+ success: false,
335
+ error: `Could not resolve prefix "${input}" to a full event ID. No matching lesson found.`,
336
+ errorType: "prefix_not_found",
337
+ };
338
+ }
339
+
340
+ // 3. Try NIP-19 decoding (note1..., nevent1...)
341
+ try {
342
+ const decoded = nip19.decode(cleaned);
343
+ if (decoded.type === "note") {
344
+ return { success: true, eventId: (decoded.data as string).toLowerCase() };
345
+ }
346
+ if (decoded.type === "nevent") {
347
+ return { success: true, eventId: (decoded.data as { id: string }).id.toLowerCase() };
348
+ }
349
+ // Other NIP-19 types (npub, nprofile, naddr) are not valid event IDs
350
+ return {
351
+ success: false,
352
+ error: `Invalid lesson event ID format: "${input}". Got NIP-19 type "${decoded.type}" but expected "note" or "nevent".`,
353
+ errorType: "invalid_format",
354
+ };
355
+ } catch {
356
+ // Not a valid NIP-19 format, fall through to error
357
+ }
358
+
359
+ // 4. Invalid format
360
+ return {
361
+ success: false,
362
+ error: `Invalid lesson event ID format: "${input}". Expected 64-char hex, 12-char hex prefix, or NIP-19 (note1.../nevent1...).`,
363
+ errorType: "invalid_format",
364
+ };
365
+ }
@@ -0,0 +1,49 @@
1
+ import { logger } from "@/utils/logger";
2
+
3
+ /**
4
+ * Handler function called during graceful shutdown
5
+ */
6
+ export type ShutdownHandler = (signal: string) => Promise<void>;
7
+
8
+ /**
9
+ * Sets up graceful shutdown handlers for various termination signals
10
+ * @param shutdownHandler - Async function to handle cleanup during shutdown
11
+ * @description Handles SIGTERM, SIGINT, SIGHUP signals and uncaught exceptions/rejections
12
+ */
13
+ export function setupGracefulShutdown(shutdownHandler: ShutdownHandler): void {
14
+ let isShuttingDown = false;
15
+
16
+ const shutdown = async (signal: string): Promise<void> => {
17
+ if (isShuttingDown) return;
18
+ isShuttingDown = true;
19
+
20
+ logger.info(`Received ${signal}, shutting down gracefully...`);
21
+
22
+ try {
23
+ await shutdownHandler(signal);
24
+ logger.info("Shutdown complete");
25
+ process.exit(0);
26
+ } catch (error) {
27
+ logger.error("Error during shutdown", { error });
28
+ process.exit(1);
29
+ }
30
+ };
31
+
32
+ // Handle various termination signals
33
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
34
+ process.on("SIGINT", () => shutdown("SIGINT"));
35
+ process.on("SIGHUP", () => shutdown("SIGHUP"));
36
+
37
+ // Handle uncaught errors
38
+ process.on("uncaughtException", (error) => {
39
+ logger.error("Uncaught exception", { error });
40
+ shutdown("uncaughtException");
41
+ });
42
+
43
+ process.on("unhandledRejection", (reason, promise) => {
44
+ logger.error("Unhandled rejection", { reason, promise });
45
+ // Don't shutdown for unhandled rejections - they're usually not critical
46
+ // e.g., relay rejections like "replaced: have newer event"
47
+ // Let the daemon's handler deal with these instead
48
+ });
49
+ }
package/src/wrapper.ts ADDED
@@ -0,0 +1,262 @@
1
+ #!/usr/bin/env bun
2
+
3
+ /**
4
+ * TENEX Daemon Wrapper Process
5
+ *
6
+ * A supervisor process that manages the TENEX daemon lifecycle.
7
+ * Features:
8
+ * - Spawns the daemon as a child process with --supervised flag
9
+ * - Forwards SIGHUP to child to trigger graceful restart
10
+ * - Respawns daemon on clean exit (exit code 0)
11
+ * - Crash loop detection to prevent rapid respawns
12
+ *
13
+ * Usage:
14
+ * bun src/wrapper.ts [daemon options]
15
+ *
16
+ * The wrapper passes all arguments to the daemon command.
17
+ */
18
+
19
+ import { spawn, type ChildProcess } from "node:child_process";
20
+ import { existsSync } from "node:fs";
21
+ import * as path from "node:path";
22
+ import { fileURLToPath } from "node:url";
23
+
24
+ // Get __filename and __dirname equivalent in ESM
25
+ const __filename = fileURLToPath(import.meta.url);
26
+ const __dirname = path.dirname(__filename);
27
+
28
+ /**
29
+ * Resolve the daemon entry point dynamically.
30
+ * Handles both development (src/index.ts) and production (dist/index.js) scenarios.
31
+ *
32
+ * Resolution order:
33
+ * 1. If running from src/, use src/index.ts (sibling of wrapper.ts)
34
+ * 2. If running from dist/, use dist/index.js
35
+ * 3. Fallback: look for index.ts/index.js in the same directory as wrapper
36
+ */
37
+ function resolveEntryPoint(): string {
38
+ // Check if we're in src/ directory (development)
39
+ const srcIndex = path.join(__dirname, "index.ts");
40
+ if (existsSync(srcIndex)) {
41
+ return srcIndex;
42
+ }
43
+
44
+ // Check if we're in dist/ directory (production)
45
+ const distIndex = path.join(__dirname, "index.js");
46
+ if (existsSync(distIndex)) {
47
+ return distIndex;
48
+ }
49
+
50
+ // Fallback: try parent directories (in case wrapper is in a subdirectory)
51
+ const parentSrcIndex = path.join(path.dirname(__dirname), "src", "index.ts");
52
+ if (existsSync(parentSrcIndex)) {
53
+ return parentSrcIndex;
54
+ }
55
+
56
+ const parentDistIndex = path.join(path.dirname(__dirname), "dist", "index.js");
57
+ if (existsSync(parentDistIndex)) {
58
+ return parentDistIndex;
59
+ }
60
+
61
+ // Last resort: assume src/index.ts relative to current directory
62
+ // This maintains backward compatibility with existing behavior
63
+ return srcIndex;
64
+ }
65
+
66
+ // Configuration
67
+ const MIN_UPTIME_MS = 5000; // Minimum uptime to reset crash counter
68
+ const MAX_CRASHES = 5; // Maximum crashes before giving up
69
+ const CRASH_WINDOW_MS = 60000; // Time window for crash counting
70
+
71
+ interface CrashRecord {
72
+ timestamp: number;
73
+ }
74
+
75
+ class DaemonWrapper {
76
+ private child: ChildProcess | null = null;
77
+ private crashHistory: CrashRecord[] = [];
78
+ private startTime: number = 0;
79
+ private isShuttingDown = false;
80
+
81
+ /**
82
+ * Start the daemon wrapper
83
+ */
84
+ async start(args: string[]): Promise<void> {
85
+ console.log("[Wrapper] Starting TENEX daemon supervisor");
86
+ console.log("[Wrapper] Daemon arguments:", args.join(" ") || "(none)");
87
+
88
+ // Setup signal handlers
89
+ this.setupSignalHandlers();
90
+
91
+ // Start the daemon loop
92
+ await this.runDaemonLoop(args);
93
+ }
94
+
95
+ /**
96
+ * Setup signal handlers for the wrapper process
97
+ */
98
+ private setupSignalHandlers(): void {
99
+ // Forward SIGHUP to child process
100
+ process.on("SIGHUP", () => {
101
+ if (this.child && this.child.pid) {
102
+ console.log("[Wrapper] SIGHUP received - forwarding to daemon");
103
+ this.child.kill("SIGHUP");
104
+ }
105
+ });
106
+
107
+ // Handle SIGTERM/SIGINT - shutdown gracefully
108
+ const handleTermination = (signal: string) => {
109
+ if (this.isShuttingDown) {
110
+ console.log(`[Wrapper] ${signal} received during shutdown - forcing exit`);
111
+ process.exit(1);
112
+ }
113
+
114
+ console.log(`[Wrapper] ${signal} received - shutting down`);
115
+ this.isShuttingDown = true;
116
+
117
+ if (this.child && this.child.pid) {
118
+ // Forward the signal to child
119
+ this.child.kill(signal === "SIGTERM" ? "SIGTERM" : "SIGINT");
120
+
121
+ // Give child some time to shutdown, then force kill
122
+ setTimeout(() => {
123
+ if (this.child && this.child.pid) {
124
+ console.log("[Wrapper] Child still running after timeout - forcing kill");
125
+ this.child.kill("SIGKILL");
126
+ }
127
+ process.exit(1);
128
+ }, 30000);
129
+ } else {
130
+ process.exit(0);
131
+ }
132
+ };
133
+
134
+ process.on("SIGTERM", () => handleTermination("SIGTERM"));
135
+ process.on("SIGINT", () => handleTermination("SIGINT"));
136
+ }
137
+
138
+ /**
139
+ * Main daemon loop - spawn daemon and respawn on clean exit
140
+ */
141
+ private async runDaemonLoop(args: string[]): Promise<void> {
142
+ while (!this.isShuttingDown) {
143
+ // Check crash loop
144
+ if (this.isInCrashLoop()) {
145
+ console.error("[Wrapper] Crash loop detected - too many crashes in short period");
146
+ console.error(`[Wrapper] ${this.crashHistory.length} crashes in last ${CRASH_WINDOW_MS / 1000}s`);
147
+ console.error("[Wrapper] Giving up - please check daemon logs");
148
+ process.exit(1);
149
+ }
150
+
151
+ // Spawn daemon
152
+ this.startTime = Date.now();
153
+ const exitCode = await this.spawnDaemon(args);
154
+ const uptime = Date.now() - this.startTime;
155
+
156
+ console.log(`[Wrapper] Daemon exited with code ${exitCode} after ${Math.round(uptime / 1000)}s`);
157
+
158
+ if (this.isShuttingDown) {
159
+ // Wrapper is shutting down, don't respawn
160
+ console.log("[Wrapper] Wrapper shutting down, not respawning");
161
+ break;
162
+ }
163
+
164
+ if (exitCode === 0) {
165
+ // Clean exit - this is a graceful restart
166
+ console.log("[Wrapper] Daemon exited cleanly - respawning for graceful restart");
167
+
168
+ // Reset crash counter on clean exit after sufficient uptime
169
+ if (uptime >= MIN_UPTIME_MS) {
170
+ this.crashHistory = [];
171
+ }
172
+
173
+ // Small delay to prevent tight loop in edge cases
174
+ await this.sleep(100);
175
+ } else {
176
+ // Non-zero exit - this is a crash
177
+ console.error(`[Wrapper] Daemon crashed with exit code ${exitCode}`);
178
+ this.recordCrash();
179
+
180
+ // Exponential backoff on crashes
181
+ const backoffMs = Math.min(1000 * Math.pow(2, this.crashHistory.length - 1), 30000);
182
+ console.log(`[Wrapper] Waiting ${backoffMs / 1000}s before respawn...`);
183
+ await this.sleep(backoffMs);
184
+ }
185
+ }
186
+
187
+ console.log("[Wrapper] Exiting");
188
+ process.exit(0);
189
+ }
190
+
191
+ /**
192
+ * Spawn the daemon process and wait for it to exit
193
+ */
194
+ private spawnDaemon(args: string[]): Promise<number> {
195
+ return new Promise((resolve) => {
196
+ // Build command: bun <entry-point> daemon --supervised [args]
197
+ const indexPath = resolveEntryPoint();
198
+ const daemonArgs = [indexPath, "daemon", "--supervised", ...args];
199
+
200
+ console.log(`[Wrapper] Spawning: bun ${daemonArgs.join(" ")}`);
201
+
202
+ this.child = spawn("bun", daemonArgs, {
203
+ stdio: "inherit",
204
+ env: process.env,
205
+ });
206
+
207
+ this.child.on("exit", (code) => {
208
+ this.child = null;
209
+ resolve(code ?? 1);
210
+ });
211
+
212
+ this.child.on("error", (error) => {
213
+ console.error("[Wrapper] Failed to spawn daemon:", error.message);
214
+ this.child = null;
215
+ resolve(1);
216
+ });
217
+ });
218
+ }
219
+
220
+ /**
221
+ * Record a crash for crash loop detection
222
+ */
223
+ private recordCrash(): void {
224
+ const now = Date.now();
225
+ this.crashHistory.push({ timestamp: now });
226
+
227
+ // Prune old crashes outside the window
228
+ this.crashHistory = this.crashHistory.filter(
229
+ (c) => now - c.timestamp < CRASH_WINDOW_MS
230
+ );
231
+ }
232
+
233
+ /**
234
+ * Check if we're in a crash loop
235
+ */
236
+ private isInCrashLoop(): boolean {
237
+ const now = Date.now();
238
+ const recentCrashes = this.crashHistory.filter(
239
+ (c) => now - c.timestamp < CRASH_WINDOW_MS
240
+ );
241
+ return recentCrashes.length >= MAX_CRASHES;
242
+ }
243
+
244
+ /**
245
+ * Sleep for a given duration
246
+ */
247
+ private sleep(ms: number): Promise<void> {
248
+ return new Promise((resolve) => setTimeout(resolve, ms));
249
+ }
250
+ }
251
+
252
+ // Main entry point
253
+ const wrapper = new DaemonWrapper();
254
+
255
+ // Get arguments after wrapper.ts
256
+ // Process args are: ["bun", "wrapper.ts", ...args]
257
+ const args = process.argv.slice(2);
258
+
259
+ wrapper.start(args).catch((error) => {
260
+ console.error("[Wrapper] Fatal error:", error);
261
+ process.exit(1);
262
+ });