@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,141 @@
1
+ import type { ModelMessage, TextPart, ImagePart } from "ai";
2
+
3
+ /**
4
+ * Extract text content from a message content field.
5
+ * Handles both string content and multimodal content (TextPart + ImagePart arrays).
6
+ *
7
+ * For multimodal content, extracts the text part and notes any images.
8
+ */
9
+ function extractTextContent(content: ModelMessage["content"]): string {
10
+ if (typeof content === "string") {
11
+ return content;
12
+ }
13
+
14
+ if (!Array.isArray(content)) {
15
+ return JSON.stringify(content);
16
+ }
17
+
18
+ // Handle multimodal content (arrays of parts)
19
+ const parts: string[] = [];
20
+ let imageCount = 0;
21
+
22
+ for (const part of content) {
23
+ if ((part as TextPart).type === "text") {
24
+ parts.push((part as TextPart).text);
25
+ } else if ((part as ImagePart).type === "image") {
26
+ imageCount++;
27
+ }
28
+ }
29
+
30
+ // If there were images, note them at the end
31
+ if (imageCount > 0) {
32
+ parts.push(`[${imageCount} image${imageCount > 1 ? "s" : ""} attached]`);
33
+ }
34
+
35
+ return parts.join("\n");
36
+ }
37
+
38
+ /**
39
+ * System prompt type for Claude Code.
40
+ * Can be a string for custom system prompt, or use preset with append for
41
+ * Claude Code's built-in instructions plus custom content.
42
+ */
43
+ export type ClaudeCodeSystemPrompt = string | {
44
+ type: "preset";
45
+ preset: "claude_code";
46
+ append?: string;
47
+ };
48
+
49
+ /**
50
+ * Compiles system messages for Claude Code's systemPrompt.append.
51
+ * Uses the new systemPrompt API (replacing deprecated customSystemPrompt/appendSystemPrompt).
52
+ *
53
+ * IMPORTANT: Only system messages are included in systemPrompt.append.
54
+ * User/assistant messages are passed separately via the messages array to streamText().
55
+ * This separation is critical for session resumption to work correctly - if conversation
56
+ * history is duplicated in both systemPrompt.append AND messages, it creates a session
57
+ * state that can't be reconstructed on resume.
58
+ *
59
+ * Note: Multimodal content (images) is converted to text descriptions since
60
+ * Claude Code uses a text-based prompt compilation approach.
61
+ */
62
+ export function compileMessagesForClaudeCode(messages: ModelMessage[]): {
63
+ systemPrompt?: ClaudeCodeSystemPrompt;
64
+ } {
65
+ if (messages.length === 0) {
66
+ return { systemPrompt: undefined };
67
+ }
68
+
69
+ // Extract only system messages - user/assistant go in messages array
70
+ const systemMessages = messages.filter((m) => m.role === "system");
71
+
72
+ if (systemMessages.length === 0) {
73
+ return { systemPrompt: undefined };
74
+ }
75
+
76
+ // Combine all system messages into the append content
77
+ const appendParts: string[] = [];
78
+
79
+ for (const msg of systemMessages) {
80
+ const content =
81
+ typeof msg.content === "string"
82
+ ? msg.content
83
+ : extractTextContent(msg.content);
84
+ appendParts.push(content);
85
+ }
86
+
87
+ const appendContent = appendParts.join("\n\n");
88
+
89
+ // Use Claude Code's built-in preset with our system content appended
90
+ return {
91
+ systemPrompt: {
92
+ type: "preset",
93
+ preset: "claude_code",
94
+ append: appendContent,
95
+ },
96
+ };
97
+ }
98
+
99
+ /**
100
+ * Converts system messages to user messages for active Claude Code sessions.
101
+ * When resuming a session, Claude Code doesn't receive new system messages,
102
+ * so we convert them to user messages to ensure they're delivered.
103
+ *
104
+ * Note: Multimodal content is preserved for user messages (they support images),
105
+ * but system messages with multimodal content are converted to text descriptions.
106
+ */
107
+ export function convertSystemMessagesForResume(messages: ModelMessage[]): ModelMessage[] {
108
+ // For resuming sessions, we need to convert system messages that appear
109
+ // after the conversation started into user messages
110
+
111
+ // Find the first non-system message (start of conversation)
112
+ const conversationStartIndex = messages.findIndex((m) => m.role !== "system");
113
+
114
+ if (conversationStartIndex === -1) {
115
+ // All messages are system messages, no conversion needed
116
+ return messages;
117
+ }
118
+
119
+ // Convert messages, preserving order
120
+ const convertedMessages = messages.map((msg, index) => {
121
+ // Keep initial system messages as-is (they were part of initial prompt)
122
+ if (index < conversationStartIndex) {
123
+ return msg;
124
+ }
125
+
126
+ // Convert subsequent system messages to user messages with clear marker
127
+ if (msg.role === "system") {
128
+ // Use extractTextContent to handle multimodal content gracefully
129
+ const content = extractTextContent(msg.content);
130
+ return {
131
+ role: "user" as const,
132
+ content: `[System Context]: ${content}`,
133
+ };
134
+ }
135
+
136
+ // Keep user and assistant messages as-is (preserving multimodal content)
137
+ return msg;
138
+ });
139
+
140
+ return convertedMessages as ModelMessage[];
141
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Codex Model Discovery Utilities
3
+ *
4
+ * Provides functions to discover available Codex models and their capabilities,
5
+ * including supported reasoning effort levels.
6
+ */
7
+
8
+ import { listModels } from "ai-sdk-provider-codex-app-server";
9
+ import type { ModelInfo } from "ai-sdk-provider-codex-app-server";
10
+
11
+ export interface CodexModelOption {
12
+ id: string;
13
+ displayName: string;
14
+ description: string;
15
+ supportedReasoningEfforts: string[];
16
+ defaultReasoningEffort: string;
17
+ isDefault: boolean;
18
+ }
19
+
20
+ /**
21
+ * List available Codex models with their reasoning effort options
22
+ */
23
+ export async function listCodexModels(): Promise<CodexModelOption[]> {
24
+ const { models } = await listModels();
25
+
26
+ return models.map((model: ModelInfo) => ({
27
+ id: model.id,
28
+ displayName: model.displayName,
29
+ description: model.description,
30
+ supportedReasoningEfforts: model.supportedReasoningEfforts.map(
31
+ (e) => e.reasoningEffort
32
+ ),
33
+ defaultReasoningEffort: model.defaultReasoningEffort,
34
+ isDefault: model.isDefault,
35
+ }));
36
+ }
37
+
38
+ /**
39
+ * Get the default Codex model
40
+ */
41
+ export async function getDefaultCodexModel(): Promise<CodexModelOption | undefined> {
42
+ const models = await listCodexModels();
43
+ return models.find((m) => m.isDefault);
44
+ }
45
+
46
+ /**
47
+ * Format model info for display
48
+ */
49
+ export function formatCodexModel(model: CodexModelOption): string {
50
+ const efforts = model.supportedReasoningEfforts.join(", ");
51
+ const defaultMark = model.isDefault ? " (default)" : "";
52
+ return `${model.id}${defaultMark}\n ${model.description}\n Reasoning: ${efforts} (default: ${model.defaultReasoningEffort})`;
53
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Context window cache for LLM models
3
+ * Delegates to models-dev-cache for model metadata
4
+ */
5
+
6
+ import { getContextWindowFromModelsdev, clearModelsDevCache } from "./models-dev-cache";
7
+
8
+ /**
9
+ * Get context window for a model
10
+ * Returns undefined if not found
11
+ */
12
+ export function getContextWindow(provider: string, model: string): number | undefined {
13
+ return getContextWindowFromModelsdev(provider, model);
14
+ }
15
+
16
+ /**
17
+ * Resolve context window for a model (async)
18
+ * No-op since models-dev-cache handles loading
19
+ */
20
+ export async function resolveContextWindow(_provider: string, _model: string): Promise<void> {
21
+ // models-dev-cache loads data at startup via ensureCacheLoaded()
22
+ // Nothing to do here
23
+ }
24
+
25
+ /**
26
+ * Clear the cache (for testing)
27
+ */
28
+ export function clearCache(): void {
29
+ clearModelsDevCache();
30
+ }
@@ -0,0 +1,267 @@
1
+ /**
2
+ * models.dev cache for LLM model metadata
3
+ *
4
+ * Fetches model information from https://models.dev/api.json and caches to disk.
5
+ * Uses stale-while-revalidate: serves cached data immediately, refreshes in background if stale.
6
+ */
7
+
8
+ import * as path from "node:path";
9
+ import { ensureDirectory, fileExists, readJsonFile, writeJsonFile, getFileStats } from "@/lib/fs";
10
+ import { config } from "@/services/ConfigService";
11
+ import { logger } from "@/utils/logger";
12
+
13
+ const MODELS_DEV_API_URL = "https://models.dev/api.json";
14
+ const CACHE_FILE_NAME = "models-dev.json";
15
+ const STALE_THRESHOLD_MS = 24 * 60 * 60 * 1000; // 24 hours
16
+
17
+ /**
18
+ * Model limits from models.dev
19
+ */
20
+ export interface ModelLimits {
21
+ /** Context window size in tokens */
22
+ context: number;
23
+ /** Max output tokens */
24
+ output: number;
25
+ }
26
+
27
+ /**
28
+ * models.dev API response structure
29
+ */
30
+ interface ModelsDevResponse {
31
+ [provider: string]: {
32
+ models: {
33
+ [modelId: string]: {
34
+ limit?: {
35
+ context?: number;
36
+ output?: number;
37
+ };
38
+ };
39
+ };
40
+ };
41
+ }
42
+
43
+ /**
44
+ * Cached data structure
45
+ */
46
+ interface CacheData {
47
+ fetchedAt: number;
48
+ data: ModelsDevResponse;
49
+ }
50
+
51
+ // In-memory cache (loaded from disk)
52
+ let cache: ModelsDevResponse | null = null;
53
+ let cacheLoadPromise: Promise<void> | null = null;
54
+
55
+ /**
56
+ * Provider ID mapping from our providers to models.dev providers
57
+ */
58
+ const PROVIDER_MAPPING: Record<string, string | null> = {
59
+ anthropic: "anthropic",
60
+ openai: "openai",
61
+ openrouter: "openrouter",
62
+ // These providers are not in models.dev (local/custom)
63
+ ollama: null,
64
+ "claude-code": null,
65
+ "codex-app-server": null,
66
+ };
67
+
68
+ /**
69
+ * Get the cache file path
70
+ */
71
+ function getCacheFilePath(): string {
72
+ return path.join(config.getConfigPath("cache"), CACHE_FILE_NAME);
73
+ }
74
+
75
+ /**
76
+ * Fetch data from models.dev API
77
+ */
78
+ async function fetchFromApi(): Promise<ModelsDevResponse | null> {
79
+ try {
80
+ const response = await fetch(MODELS_DEV_API_URL);
81
+ if (!response.ok) {
82
+ logger.warn("Failed to fetch models.dev API", {
83
+ status: response.status,
84
+ statusText: response.statusText,
85
+ });
86
+ return null;
87
+ }
88
+ return (await response.json()) as ModelsDevResponse;
89
+ } catch (error) {
90
+ logger.warn("Error fetching models.dev API", { error });
91
+ return null;
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Load cache from disk
97
+ */
98
+ async function loadFromDisk(): Promise<CacheData | null> {
99
+ const cacheFile = getCacheFilePath();
100
+ if (!(await fileExists(cacheFile))) {
101
+ return null;
102
+ }
103
+ return readJsonFile<CacheData>(cacheFile);
104
+ }
105
+
106
+ /**
107
+ * Save cache to disk
108
+ */
109
+ async function saveToDisk(data: ModelsDevResponse): Promise<void> {
110
+ const cacheFile = getCacheFilePath();
111
+ await ensureDirectory(path.dirname(cacheFile));
112
+ const cacheData: CacheData = {
113
+ fetchedAt: Date.now(),
114
+ data,
115
+ };
116
+ await writeJsonFile(cacheFile, cacheData);
117
+ }
118
+
119
+ /**
120
+ * Check if cache is stale (older than 24 hours)
121
+ */
122
+ async function isCacheStale(): Promise<boolean> {
123
+ const cacheFile = getCacheFilePath();
124
+ const stats = await getFileStats(cacheFile);
125
+ if (!stats) return true;
126
+
127
+ const age = Date.now() - stats.mtimeMs;
128
+ return age > STALE_THRESHOLD_MS;
129
+ }
130
+
131
+ /**
132
+ * Refresh cache from API (runs in background)
133
+ */
134
+ async function refreshInBackground(): Promise<void> {
135
+ const freshData = await fetchFromApi();
136
+ if (freshData) {
137
+ cache = freshData;
138
+ await saveToDisk(freshData);
139
+ logger.debug("models.dev cache refreshed");
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Ensure cache is loaded (call at startup)
145
+ * Blocks until cache is ready. Fetches from API if no cached data exists.
146
+ */
147
+ export async function ensureCacheLoaded(): Promise<void> {
148
+ // If already loading, wait for that
149
+ if (cacheLoadPromise) {
150
+ await cacheLoadPromise;
151
+ return;
152
+ }
153
+
154
+ // If already loaded, check for background refresh
155
+ if (cache) {
156
+ if (await isCacheStale()) {
157
+ // Trigger background refresh, don't wait
158
+ refreshInBackground().catch((err) =>
159
+ logger.warn("Background cache refresh failed", { error: err })
160
+ );
161
+ }
162
+ return;
163
+ }
164
+
165
+ // Start loading
166
+ cacheLoadPromise = (async () => {
167
+ // Try to load from disk first
168
+ const diskCache = await loadFromDisk();
169
+ if (diskCache?.data) {
170
+ cache = diskCache.data;
171
+ logger.debug("models.dev cache loaded from disk");
172
+
173
+ // Check if stale and trigger background refresh
174
+ if (await isCacheStale()) {
175
+ refreshInBackground().catch((err) =>
176
+ logger.warn("Background cache refresh failed", { error: err })
177
+ );
178
+ }
179
+ return;
180
+ }
181
+
182
+ // No cache on disk, fetch from API
183
+ logger.debug("models.dev cache not found, fetching from API");
184
+ const freshData = await fetchFromApi();
185
+ if (freshData) {
186
+ cache = freshData;
187
+ await saveToDisk(freshData);
188
+ logger.debug("models.dev cache fetched and saved");
189
+ } else {
190
+ logger.warn("Could not load models.dev data - model limits will be unavailable");
191
+ }
192
+ })();
193
+
194
+ try {
195
+ await cacheLoadPromise;
196
+ } finally {
197
+ cacheLoadPromise = null;
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Force refresh cache from API
203
+ */
204
+ export async function refreshCache(): Promise<void> {
205
+ const freshData = await fetchFromApi();
206
+ if (freshData) {
207
+ cache = freshData;
208
+ await saveToDisk(freshData);
209
+ logger.info("models.dev cache force refreshed");
210
+ } else {
211
+ throw new Error("Failed to refresh models.dev cache");
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Get model limits for a specific provider and model
217
+ *
218
+ * @param provider Our provider ID (e.g., "anthropic", "openai", "openrouter")
219
+ * @param model Model ID (e.g., "claude-opus-4-5-20251101", "gpt-4o")
220
+ * @returns Model limits or undefined if not found/unsupported
221
+ */
222
+ export function getModelLimits(provider: string, model: string): ModelLimits | undefined {
223
+ if (!cache) {
224
+ return undefined;
225
+ }
226
+
227
+ // Map our provider ID to models.dev provider ID
228
+ const modelsDevProvider = PROVIDER_MAPPING[provider];
229
+ if (modelsDevProvider === null || modelsDevProvider === undefined) {
230
+ // Provider not supported by models.dev
231
+ return undefined;
232
+ }
233
+
234
+ const providerData = cache[modelsDevProvider];
235
+ if (!providerData?.models) {
236
+ return undefined;
237
+ }
238
+
239
+ const modelData = providerData.models[model];
240
+ if (!modelData?.limit) {
241
+ return undefined;
242
+ }
243
+
244
+ const { context, output } = modelData.limit;
245
+ if (context === undefined || output === undefined) {
246
+ return undefined;
247
+ }
248
+
249
+ return { context, output };
250
+ }
251
+
252
+ /**
253
+ * Get just the context window for a model
254
+ * Convenience function for backwards compatibility with context-window-cache
255
+ */
256
+ export function getContextWindowFromModelsdev(provider: string, model: string): number | undefined {
257
+ const limits = getModelLimits(provider, model);
258
+ return limits?.context;
259
+ }
260
+
261
+ /**
262
+ * Clear in-memory cache (for testing)
263
+ */
264
+ export function clearModelsDevCache(): void {
265
+ cache = null;
266
+ cacheLoadPromise = null;
267
+ }
@@ -0,0 +1,50 @@
1
+ import inquirer from "inquirer";
2
+
3
+ import { AI_SDK_PROVIDERS } from "@/llm/types";
4
+ import type { ProviderCredentials, TenexProviders } from "@/services/config/types";
5
+ import { hasApiKey } from "@/llm/providers/key-manager";
6
+ import { ProviderConfigUI } from "@/llm/utils/ProviderConfigUI";
7
+
8
+ /**
9
+ * Interactive flow for configuring provider credentials.
10
+ * Returns an updated TenexProviders object (merges into the existing map).
11
+ */
12
+ export async function runProviderSetup(
13
+ existingProviders: TenexProviders
14
+ ): Promise<TenexProviders> {
15
+ const providers: Record<string, ProviderCredentials> = {
16
+ ...existingProviders.providers,
17
+ };
18
+
19
+ const choices = AI_SDK_PROVIDERS.map((provider) => {
20
+ const isConfigured = hasApiKey(providers[provider]?.apiKey);
21
+ const name = ProviderConfigUI.getProviderDisplayName(provider);
22
+ return {
23
+ name: isConfigured ? `${name} (configured)` : name,
24
+ value: provider,
25
+ };
26
+ });
27
+
28
+ const { selected } = await inquirer.prompt([
29
+ {
30
+ type: "checkbox",
31
+ name: "selected",
32
+ message: "Select providers to configure:",
33
+ choices,
34
+ },
35
+ ]);
36
+
37
+ if (!selected || selected.length === 0) {
38
+ return existingProviders;
39
+ }
40
+
41
+ for (const provider of selected) {
42
+ const providerConfig = await ProviderConfigUI.configureProvider(provider, providers);
43
+ providers[provider] = {
44
+ ...providers[provider],
45
+ apiKey: providerConfig.apiKey,
46
+ };
47
+ }
48
+
49
+ return { providers };
50
+ }
@@ -0,0 +1,78 @@
1
+ import type { AISdkTool } from "@/tools/types";
2
+ import type { StepResult } from "ai";
3
+
4
+ /**
5
+ * Extract invalid tool calls from step results.
6
+ * Used to detect and report dynamic tool validation errors.
7
+ */
8
+ export function getInvalidToolCalls(
9
+ steps: StepResult<Record<string, AISdkTool>>[]
10
+ ): Array<{ toolName: string; error: string }> {
11
+ const invalidToolCalls: Array<{ toolName: string; error: string }> = [];
12
+
13
+ for (const step of steps) {
14
+ if (step.toolCalls) {
15
+ for (const toolCall of step.toolCalls) {
16
+ // Check if this is a dynamic tool call that's invalid
17
+ if (
18
+ "dynamic" in toolCall &&
19
+ toolCall.dynamic === true &&
20
+ toolCall.invalid === true &&
21
+ toolCall.error
22
+ ) {
23
+ const error =
24
+ typeof toolCall.error === "object" &&
25
+ toolCall.error !== null &&
26
+ "name" in toolCall.error
27
+ ? (toolCall.error as { name: string }).name
28
+ : "Unknown error";
29
+ invalidToolCalls.push({
30
+ toolName: toolCall.toolName,
31
+ error,
32
+ });
33
+ }
34
+ }
35
+ }
36
+ }
37
+
38
+ return invalidToolCalls;
39
+ }
40
+
41
+ /**
42
+ * Check if a tool result indicates an error.
43
+ * AI SDK wraps tool execution errors in error-text or error-json formats.
44
+ */
45
+ export function isToolResultError(result: unknown): boolean {
46
+ if (typeof result !== "object" || result === null) {
47
+ return false;
48
+ }
49
+ const res = result as Record<string, unknown>;
50
+ // Check for AI SDK's known error formats
51
+ return (
52
+ (res.type === "error-text" && typeof res.text === "string") ||
53
+ (res.type === "error-json" && typeof res.json === "object")
54
+ );
55
+ }
56
+
57
+ /**
58
+ * Extract error details from tool result for better logging.
59
+ * Returns null if the result is not an error format.
60
+ */
61
+ export function extractErrorDetails(result: unknown): { message: string; type: string } | null {
62
+ if (typeof result !== "object" || result === null) {
63
+ return null;
64
+ }
65
+ const res = result as Record<string, unknown>;
66
+
67
+ if (res.type === "error-text" && typeof res.text === "string") {
68
+ return { message: res.text, type: "error-text" };
69
+ }
70
+
71
+ if (res.type === "error-json" && typeof res.json === "object") {
72
+ const errorJson = res.json as Record<string, unknown>;
73
+ const message = errorJson.message || errorJson.error || JSON.stringify(errorJson);
74
+ return { message: String(message), type: "error-json" };
75
+ }
76
+
77
+ return null;
78
+ }
@@ -0,0 +1,74 @@
1
+ import type { LanguageModelUsage } from "ai";
2
+ import type { LanguageModelUsageWithCostUsd } from "../types";
3
+
4
+ interface OpenRouterUsage {
5
+ promptTokens?: number;
6
+ completionTokens?: number;
7
+ totalTokens?: number;
8
+ cost?: number;
9
+ promptTokensDetails?: {
10
+ cachedTokens?: number;
11
+ };
12
+ completionTokensDetails?: {
13
+ reasoningTokens?: number;
14
+ };
15
+ }
16
+
17
+ interface StepProviderMetadata {
18
+ openrouter?: {
19
+ usage?: OpenRouterUsage;
20
+ };
21
+ [key: string]: unknown;
22
+ }
23
+
24
+ type StepUsage = Partial<Pick<LanguageModelUsage, "inputTokens" | "outputTokens">>;
25
+
26
+ export interface StepWithProviderMetadata {
27
+ usage?: StepUsage;
28
+ providerMetadata?: StepProviderMetadata;
29
+ }
30
+
31
+ /**
32
+ * Calculate cumulative usage from an array of steps.
33
+ * Returns undefined if no steps provided.
34
+ * Extracts token counts, cost, and detailed token breakdowns from OpenRouter providerMetadata.
35
+ */
36
+ export function calculateCumulativeUsage(
37
+ steps: StepWithProviderMetadata[]
38
+ ): LanguageModelUsageWithCostUsd | undefined {
39
+ if (steps.length === 0) {
40
+ return undefined;
41
+ }
42
+
43
+ const inputTokens = steps.reduce((sum, step) => {
44
+ const openrouterUsage = step.providerMetadata?.openrouter?.usage;
45
+ return sum + (openrouterUsage?.promptTokens ?? step.usage?.inputTokens ?? 0);
46
+ }, 0);
47
+ const outputTokens = steps.reduce((sum, step) => {
48
+ const openrouterUsage = step.providerMetadata?.openrouter?.usage;
49
+ return sum + (openrouterUsage?.completionTokens ?? step.usage?.outputTokens ?? 0);
50
+ }, 0);
51
+ const costUsd = steps.reduce((sum, step) => {
52
+ const openrouterUsage = step.providerMetadata?.openrouter?.usage;
53
+ return sum + (openrouterUsage?.cost ?? 0);
54
+ }, 0);
55
+ const cachedInputTokens = steps.reduce((sum, step) => {
56
+ const openrouterUsage = step.providerMetadata?.openrouter?.usage;
57
+ return sum + (openrouterUsage?.promptTokensDetails?.cachedTokens ?? 0);
58
+ }, 0);
59
+ const reasoningTokens = steps.reduce((sum, step) => {
60
+ const openrouterUsage = step.providerMetadata?.openrouter?.usage;
61
+ return sum + (openrouterUsage?.completionTokensDetails?.reasoningTokens ?? 0);
62
+ }, 0);
63
+
64
+ return {
65
+ inputTokens,
66
+ outputTokens,
67
+ totalTokens: inputTokens + outputTokens,
68
+ costUsd: costUsd > 0 ? costUsd : undefined,
69
+ cachedInputTokens: cachedInputTokens > 0 ? cachedInputTokens : undefined,
70
+ reasoningTokens: reasoningTokens > 0 ? reasoningTokens : undefined,
71
+ inputTokenDetails: { noCacheTokens: undefined, cacheReadTokens: undefined, cacheWriteTokens: undefined },
72
+ outputTokenDetails: { textTokens: undefined, reasoningTokens: undefined },
73
+ } as LanguageModelUsageWithCostUsd;
74
+ }