@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.
- package/README.md +194 -0
- package/dist/backend-wrapper.cjs +3 -0
- package/dist/src/index.js +331928 -0
- package/package.json +103 -0
- package/src/agents/AgentRegistry.ts +418 -0
- package/src/agents/AgentStorage.ts +1133 -0
- package/src/agents/ConfigResolver.ts +229 -0
- package/src/agents/agent-installer.ts +236 -0
- package/src/agents/agent-loader.ts +241 -0
- package/src/agents/constants.ts +82 -0
- package/src/agents/errors.ts +48 -0
- package/src/agents/execution/AgentExecutor.ts +561 -0
- package/src/agents/execution/ExecutionContextFactory.ts +112 -0
- package/src/agents/execution/MessageCompiler.ts +597 -0
- package/src/agents/execution/MessageSyncer.ts +100 -0
- package/src/agents/execution/PostCompletionChecker.ts +278 -0
- package/src/agents/execution/ProgressMonitor.ts +50 -0
- package/src/agents/execution/RALResolver.ts +177 -0
- package/src/agents/execution/SessionManager.ts +181 -0
- package/src/agents/execution/StreamCallbacks.ts +312 -0
- package/src/agents/execution/StreamExecutionHandler.ts +579 -0
- package/src/agents/execution/StreamSetup.ts +313 -0
- package/src/agents/execution/ToolEventHandlers.ts +239 -0
- package/src/agents/execution/ToolExecutionTracker.ts +498 -0
- package/src/agents/execution/ToolResultUtils.ts +97 -0
- package/src/agents/execution/ToolSupervisionWrapper.ts +174 -0
- package/src/agents/execution/constants.ts +16 -0
- package/src/agents/execution/index.ts +3 -0
- package/src/agents/execution/types.ts +96 -0
- package/src/agents/execution/utils.ts +26 -0
- package/src/agents/index.ts +4 -0
- package/src/agents/script-installer.ts +266 -0
- package/src/agents/supervision/SupervisorLLMService.ts +253 -0
- package/src/agents/supervision/SupervisorOrchestrator.ts +471 -0
- package/src/agents/supervision/heuristics/ConsecutiveToolsWithoutTodoHeuristic.ts +73 -0
- package/src/agents/supervision/heuristics/DelegationClaimHeuristic.ts +80 -0
- package/src/agents/supervision/heuristics/HeuristicRegistry.ts +114 -0
- package/src/agents/supervision/heuristics/PendingTodosHeuristic.ts +93 -0
- package/src/agents/supervision/heuristics/SilentAgentHeuristic.ts +54 -0
- package/src/agents/supervision/heuristics/index.ts +5 -0
- package/src/agents/supervision/index.ts +28 -0
- package/src/agents/supervision/registerHeuristics.ts +110 -0
- package/src/agents/supervision/supervisionHealthCheck.ts +123 -0
- package/src/agents/supervision/types.ts +171 -0
- package/src/agents/tool-names.ts +46 -0
- package/src/agents/tool-normalization.ts +184 -0
- package/src/agents/types/index.ts +2 -0
- package/src/agents/types/runtime.ts +74 -0
- package/src/agents/types/storage.ts +145 -0
- package/src/commands/agent/import/index.ts +6 -0
- package/src/commands/agent/import/openclaw-distiller.ts +57 -0
- package/src/commands/agent/import/openclaw-reader.ts +141 -0
- package/src/commands/agent/import/openclaw.ts +154 -0
- package/src/commands/agent/index.ts +6 -0
- package/src/commands/agent.ts +215 -0
- package/src/commands/daemon.ts +198 -0
- package/src/commands/doctor.ts +134 -0
- package/src/commands/setup/embed.ts +228 -0
- package/src/commands/setup/global-system-prompt.ts +223 -0
- package/src/commands/setup/image.ts +179 -0
- package/src/commands/setup/index.ts +16 -0
- package/src/commands/setup/interactive.ts +95 -0
- package/src/commands/setup/llm.ts +38 -0
- package/src/commands/setup/onboarding.ts +294 -0
- package/src/commands/setup/providers.ts +27 -0
- package/src/constants.ts +34 -0
- package/src/conversations/ConversationDiskReader.ts +148 -0
- package/src/conversations/ConversationRegistry.ts +728 -0
- package/src/conversations/ConversationStore.ts +868 -0
- package/src/conversations/MessageBuilder.ts +866 -0
- package/src/conversations/executionTime.ts +62 -0
- package/src/conversations/formatters/DelegationXmlFormatter.ts +64 -0
- package/src/conversations/formatters/ThreadedConversationFormatter.ts +303 -0
- package/src/conversations/formatters/index.ts +9 -0
- package/src/conversations/formatters/utils/MessageFormatter.ts +46 -0
- package/src/conversations/formatters/utils/TimestampFormatter.ts +56 -0
- package/src/conversations/formatters/utils/TreeBuilder.ts +131 -0
- package/src/conversations/formatters/utils/TreeRenderer.ts +49 -0
- package/src/conversations/index.ts +2 -0
- package/src/conversations/persistence/ToolMessageStorage.ts +143 -0
- package/src/conversations/search/ConversationIndexManager.ts +393 -0
- package/src/conversations/search/QueryParser.ts +114 -0
- package/src/conversations/search/SearchEngine.ts +175 -0
- package/src/conversations/search/SnippetExtractor.ts +345 -0
- package/src/conversations/search/embeddings/ConversationEmbeddingService.ts +484 -0
- package/src/conversations/search/embeddings/ConversationIndexingJob.ts +320 -0
- package/src/conversations/search/embeddings/IndexingStateManager.ts +338 -0
- package/src/conversations/search/embeddings/index.ts +18 -0
- package/src/conversations/search/index.ts +49 -0
- package/src/conversations/search/types.ts +124 -0
- package/src/conversations/services/CategoryManager.ts +160 -0
- package/src/conversations/services/ConversationResolver.ts +296 -0
- package/src/conversations/services/ConversationSummarizer.ts +234 -0
- package/src/conversations/services/MetadataDebounceManager.ts +188 -0
- package/src/conversations/services/index.ts +2 -0
- package/src/conversations/types.ts +148 -0
- package/src/conversations/utils/content-utils.ts +69 -0
- package/src/conversations/utils/image-placeholder.ts +281 -0
- package/src/conversations/utils/image-url-utils.ts +171 -0
- package/src/conversations/utils/multimodal-content.ts +90 -0
- package/src/conversations/utils/tool-result-truncator.ts +159 -0
- package/src/daemon/Daemon.ts +1883 -0
- package/src/daemon/ProjectRuntime.ts +657 -0
- package/src/daemon/RestartState.ts +152 -0
- package/src/daemon/RuntimeLifecycle.ts +268 -0
- package/src/daemon/SubscriptionManager.ts +305 -0
- package/src/daemon/UnixSocketTransport.ts +318 -0
- package/src/daemon/filters/SubscriptionFilterBuilder.ts +119 -0
- package/src/daemon/index.ts +9 -0
- package/src/daemon/routing/DaemonRouter.ts +491 -0
- package/src/daemon/types.ts +150 -0
- package/src/daemon/utils/routing-log.ts +76 -0
- package/src/daemon/utils/telemetry.ts +173 -0
- package/src/event-handler/agentDeletion.ts +383 -0
- package/src/event-handler/index.ts +749 -0
- package/src/event-handler/newConversation.ts +165 -0
- package/src/event-handler/project.ts +166 -0
- package/src/event-handler/reply.ts +18 -0
- package/src/events/NDKAgentDefinition.ts +292 -0
- package/src/events/NDKAgentLesson.ts +106 -0
- package/src/events/NDKEventMetadata.ts +34 -0
- package/src/events/NDKMCPTool.ts +60 -0
- package/src/events/NDKProjectStatus.ts +384 -0
- package/src/events/index.ts +4 -0
- package/src/index.ts +126 -0
- package/src/lib/agent-home.ts +334 -0
- package/src/lib/error-formatter.ts +200 -0
- package/src/lib/fs/filesystem.ts +128 -0
- package/src/lib/fs/index.ts +1 -0
- package/src/lib/json-parser.ts +30 -0
- package/src/lib/string.ts +15 -0
- package/src/lib/time.ts +74 -0
- package/src/llm/ChunkHandler.ts +277 -0
- package/src/llm/FinishHandler.ts +250 -0
- package/src/llm/LLMConfigEditor.ts +154 -0
- package/src/llm/LLMServiceFactory.ts +230 -0
- package/src/llm/MessageProcessor.ts +90 -0
- package/src/llm/RecordingState.ts +37 -0
- package/src/llm/StreamPublisher.ts +40 -0
- package/src/llm/TracingUtils.ts +77 -0
- package/src/llm/chunk-validators.ts +57 -0
- package/src/llm/constants.ts +6 -0
- package/src/llm/index.ts +12 -0
- package/src/llm/meta/MetaModelResolver.ts +352 -0
- package/src/llm/meta/index.ts +11 -0
- package/src/llm/middleware/flight-recorder.ts +188 -0
- package/src/llm/providers/MockProvider.ts +332 -0
- package/src/llm/providers/agent/ClaudeCodeProvider.ts +343 -0
- package/src/llm/providers/agent/ClaudeCodeToolsAdapter.ts +203 -0
- package/src/llm/providers/agent/CodexAppServerProvider.ts +214 -0
- package/src/llm/providers/agent/CodexAppServerToolsAdapter.ts +91 -0
- package/src/llm/providers/agent/index.ts +10 -0
- package/src/llm/providers/base/AgentProvider.ts +107 -0
- package/src/llm/providers/base/BaseProvider.ts +114 -0
- package/src/llm/providers/base/StandardProvider.ts +38 -0
- package/src/llm/providers/base/index.ts +9 -0
- package/src/llm/providers/index.ts +106 -0
- package/src/llm/providers/key-manager.ts +238 -0
- package/src/llm/providers/ollama-models.ts +105 -0
- package/src/llm/providers/openrouter-models.ts +102 -0
- package/src/llm/providers/provider-ids.ts +18 -0
- package/src/llm/providers/registry/ProviderRegistry.ts +414 -0
- package/src/llm/providers/registry/index.ts +7 -0
- package/src/llm/providers/standard/AnthropicProvider.ts +71 -0
- package/src/llm/providers/standard/OllamaProvider.ts +59 -0
- package/src/llm/providers/standard/OpenAIProvider.ts +44 -0
- package/src/llm/providers/standard/OpenRouterProvider.ts +103 -0
- package/src/llm/providers/standard/index.ts +10 -0
- package/src/llm/providers/types.ts +194 -0
- package/src/llm/providers/usage-metadata.ts +78 -0
- package/src/llm/service.ts +713 -0
- package/src/llm/types.ts +167 -0
- package/src/llm/utils/ConfigurationManager.ts +650 -0
- package/src/llm/utils/ConfigurationTester.ts +229 -0
- package/src/llm/utils/ModelSelector.ts +212 -0
- package/src/llm/utils/ProviderConfigUI.ts +177 -0
- package/src/llm/utils/claudeCodePromptCompiler.ts +141 -0
- package/src/llm/utils/codex-models.ts +53 -0
- package/src/llm/utils/context-window-cache.ts +30 -0
- package/src/llm/utils/models-dev-cache.ts +267 -0
- package/src/llm/utils/provider-setup.ts +50 -0
- package/src/llm/utils/tool-errors.ts +78 -0
- package/src/llm/utils/usage.ts +74 -0
- package/src/logging/EventRoutingLogger.ts +205 -0
- package/src/nostr/AgentEventDecoder.ts +357 -0
- package/src/nostr/AgentEventEncoder.ts +677 -0
- package/src/nostr/AgentProfilePublisher.ts +657 -0
- package/src/nostr/AgentPublisher.ts +437 -0
- package/src/nostr/BlossomService.ts +226 -0
- package/src/nostr/InterventionPublisher.ts +132 -0
- package/src/nostr/TagExtractor.ts +228 -0
- package/src/nostr/collectEvents.ts +83 -0
- package/src/nostr/constants.ts +38 -0
- package/src/nostr/encryption.ts +26 -0
- package/src/nostr/index.ts +31 -0
- package/src/nostr/keys.ts +17 -0
- package/src/nostr/kinds.ts +37 -0
- package/src/nostr/ndkClient.ts +72 -0
- package/src/nostr/relays.ts +43 -0
- package/src/nostr/trace-context.ts +39 -0
- package/src/nostr/types.ts +227 -0
- package/src/nostr/utils.ts +84 -0
- package/src/prompts/core/FragmentRegistry.ts +30 -0
- package/src/prompts/core/PromptBuilder.ts +98 -0
- package/src/prompts/core/index.ts +3 -0
- package/src/prompts/core/types.ts +13 -0
- package/src/prompts/fragments/00-global-system-prompt.ts +44 -0
- package/src/prompts/fragments/01-agent-identity.ts +69 -0
- package/src/prompts/fragments/02-agent-home-directory.ts +114 -0
- package/src/prompts/fragments/03-system-reminders-explanation.ts +14 -0
- package/src/prompts/fragments/04-relay-configuration.ts +38 -0
- package/src/prompts/fragments/05-delegation-chain.ts +45 -0
- package/src/prompts/fragments/06-agent-todos.ts +74 -0
- package/src/prompts/fragments/06-todo-usage-guidance.ts +34 -0
- package/src/prompts/fragments/07-meta-project-context.ts +234 -0
- package/src/prompts/fragments/08-active-conversations.ts +382 -0
- package/src/prompts/fragments/09-recent-conversations.ts +153 -0
- package/src/prompts/fragments/10-referenced-article.ts +21 -0
- package/src/prompts/fragments/11-nudges.ts +134 -0
- package/src/prompts/fragments/12-skills.ts +127 -0
- package/src/prompts/fragments/13-available-nudges.ts +122 -0
- package/src/prompts/fragments/15-available-agents.ts +53 -0
- package/src/prompts/fragments/16-stay-in-your-lane.ts +41 -0
- package/src/prompts/fragments/17-todo-before-delegation.ts +39 -0
- package/src/prompts/fragments/20-voice-mode.ts +62 -0
- package/src/prompts/fragments/22-scheduled-tasks.ts +175 -0
- package/src/prompts/fragments/24-retrieved-lessons.ts +26 -0
- package/src/prompts/fragments/25-rag-instructions.ts +333 -0
- package/src/prompts/fragments/26-mcp-resources.ts +237 -0
- package/src/prompts/fragments/27-memorized-reports.ts +77 -0
- package/src/prompts/fragments/28-agent-directed-monitoring.ts +32 -0
- package/src/prompts/fragments/29-rag-collections.ts +50 -0
- package/src/prompts/fragments/30-worktree-context.ts +98 -0
- package/src/prompts/fragments/31-agents-md-guidance.ts +96 -0
- package/src/prompts/fragments/32-process-metrics.ts +72 -0
- package/src/prompts/fragments/debug-mode.ts +48 -0
- package/src/prompts/fragments/delegation-completion.ts +44 -0
- package/src/prompts/fragments/index.ts +91 -0
- package/src/prompts/index.ts +21 -0
- package/src/prompts/utils/systemPromptBuilder.ts +777 -0
- package/src/scripts/migrate-prefix-index.ts +157 -0
- package/src/services/AgentDefinitionMonitor.ts +701 -0
- package/src/services/ConfigService.ts +723 -0
- package/src/services/CooldownRegistry.ts +199 -0
- package/src/services/LLMOperationsRegistry.ts +424 -0
- package/src/services/OwnerAgentListService.ts +354 -0
- package/src/services/PubkeyService.ts +308 -0
- package/src/services/agents/AgentMetadataStore.ts +72 -0
- package/src/services/agents/AgentResolution.ts +59 -0
- package/src/services/agents/EscalationService.ts +281 -0
- package/src/services/agents/NDKAgentDiscovery.ts +95 -0
- package/src/services/agents/index.ts +7 -0
- package/src/services/agents-md/AgentsMdService.ts +184 -0
- package/src/services/agents-md/SystemReminderInjector.ts +238 -0
- package/src/services/agents-md/index.ts +11 -0
- package/src/services/apns/APNsClient.ts +203 -0
- package/src/services/apns/APNsService.ts +358 -0
- package/src/services/apns/index.ts +11 -0
- package/src/services/apns/types.ts +80 -0
- package/src/services/compression/CompressionService.ts +445 -0
- package/src/services/compression/compression-schema.ts +28 -0
- package/src/services/compression/compression-types.ts +74 -0
- package/src/services/compression/compression-utils.ts +587 -0
- package/src/services/config/types.ts +394 -0
- package/src/services/dispatch/AgentDispatchService.ts +937 -0
- package/src/services/dispatch/AgentRouter.ts +181 -0
- package/src/services/dispatch/DelegationCompletionHandler.ts +232 -0
- package/src/services/embedding/EmbeddingProvider.ts +188 -0
- package/src/services/embedding/index.ts +5 -0
- package/src/services/event-context/EventContextService.ts +108 -0
- package/src/services/event-context/index.ts +2 -0
- package/src/services/heuristics/ContextBuilder.ts +106 -0
- package/src/services/heuristics/HeuristicEngine.ts +200 -0
- package/src/services/heuristics/formatters.ts +58 -0
- package/src/services/heuristics/index.ts +12 -0
- package/src/services/heuristics/rules/index.ts +25 -0
- package/src/services/heuristics/rules/todoBeforeDelegation.ts +69 -0
- package/src/services/heuristics/rules/todoReminderOnToolUse.ts +63 -0
- package/src/services/heuristics/types.ts +144 -0
- package/src/services/image/ImageGenerationService.ts +389 -0
- package/src/services/image/index.ts +12 -0
- package/src/services/intervention/InterventionService.ts +1352 -0
- package/src/services/intervention/index.ts +7 -0
- package/src/services/mcp/MCPManager.ts +683 -0
- package/src/services/mcp/McpNotificationDelivery.ts +139 -0
- package/src/services/mcp/McpSubscriptionService.ts +653 -0
- package/src/services/mcp/mcpInstaller.ts +130 -0
- package/src/services/nip46/Nip46SigningLog.ts +81 -0
- package/src/services/nip46/Nip46SigningService.ts +467 -0
- package/src/services/nip46/index.ts +4 -0
- package/src/services/nudge/NudgeService.ts +224 -0
- package/src/services/nudge/NudgeWhitelistService.ts +382 -0
- package/src/services/nudge/index.ts +5 -0
- package/src/services/nudge/types.ts +83 -0
- package/src/services/projects/ProjectContext.ts +672 -0
- package/src/services/projects/ProjectContextStore.ts +102 -0
- package/src/services/projects/index.ts +6 -0
- package/src/services/prompt-compiler/index.ts +15 -0
- package/src/services/prompt-compiler/prompt-compiler-service.ts +1143 -0
- package/src/services/pubkey-gate/PubkeyGateService.ts +93 -0
- package/src/services/pubkey-gate/index.ts +1 -0
- package/src/services/rag/EmbeddingProviderFactory.ts +292 -0
- package/src/services/rag/LanceDBMaintenanceService.ts +211 -0
- package/src/services/rag/RAGDatabaseService.ts +173 -0
- package/src/services/rag/RAGOperations.ts +682 -0
- package/src/services/rag/RAGService.ts +240 -0
- package/src/services/rag/RagSubscriptionService.ts +618 -0
- package/src/services/rag/rag-utils.ts +174 -0
- package/src/services/ral/PendingDelegationsRegistry.ts +168 -0
- package/src/services/ral/RALRegistry.ts +2782 -0
- package/src/services/ral/index.ts +4 -0
- package/src/services/ral/types.ts +292 -0
- package/src/services/reports/LocalReportStore.ts +380 -0
- package/src/services/reports/ReportEmbeddingService.ts +430 -0
- package/src/services/reports/ReportService.ts +440 -0
- package/src/services/reports/articleUtils.ts +52 -0
- package/src/services/reports/index.ts +7 -0
- package/src/services/scheduling/SchedulerService.ts +1057 -0
- package/src/services/scheduling/errors.ts +14 -0
- package/src/services/scheduling/index.ts +7 -0
- package/src/services/scheduling/utils.ts +77 -0
- package/src/services/search/SearchProviderRegistry.ts +78 -0
- package/src/services/search/UnifiedSearchService.ts +218 -0
- package/src/services/search/index.ts +47 -0
- package/src/services/search/projectFilter.ts +22 -0
- package/src/services/search/providers/ConversationSearchProvider.ts +48 -0
- package/src/services/search/providers/LessonSearchProvider.ts +75 -0
- package/src/services/search/providers/ReportSearchProvider.ts +49 -0
- package/src/services/search/types.ts +144 -0
- package/src/services/skill/SkillService.ts +482 -0
- package/src/services/skill/index.ts +2 -0
- package/src/services/skill/types.ts +70 -0
- package/src/services/status/OperationsStatusService.ts +276 -0
- package/src/services/status/ProjectStatusService.ts +522 -0
- package/src/services/status/index.ts +11 -0
- package/src/services/storage/PrefixKVStore.ts +242 -0
- package/src/services/storage/index.ts +1 -0
- package/src/services/system-reminder/SystemReminderUtils.ts +96 -0
- package/src/services/system-reminder/index.ts +7 -0
- package/src/services/trust-pubkeys/TrustPubkeyService.ts +325 -0
- package/src/services/trust-pubkeys/index.ts +2 -0
- package/src/telemetry/ConversationSpanManager.ts +111 -0
- package/src/telemetry/EventLoopMonitor.ts +206 -0
- package/src/telemetry/LLMSpanRegistry.ts +20 -0
- package/src/telemetry/NostrSpanProcessor.ts +89 -0
- package/src/telemetry/ToolCallSpanProcessor.ts +66 -0
- package/src/telemetry/diagnostics.ts +27 -0
- package/src/telemetry/setup.ts +120 -0
- package/src/tools/implementations/agents_discover.ts +121 -0
- package/src/tools/implementations/agents_hire.ts +127 -0
- package/src/tools/implementations/agents_list.ts +96 -0
- package/src/tools/implementations/agents_publish.ts +611 -0
- package/src/tools/implementations/agents_read.ts +173 -0
- package/src/tools/implementations/agents_write.ts +200 -0
- package/src/tools/implementations/ask.ts +411 -0
- package/src/tools/implementations/change_model.ts +141 -0
- package/src/tools/implementations/conversation_get.ts +661 -0
- package/src/tools/implementations/conversation_list.ts +377 -0
- package/src/tools/implementations/conversation_search.ts +370 -0
- package/src/tools/implementations/delegate.ts +327 -0
- package/src/tools/implementations/delegate_crossproject.ts +209 -0
- package/src/tools/implementations/delegate_followup.ts +300 -0
- package/src/tools/implementations/fs_edit.ts +162 -0
- package/src/tools/implementations/fs_glob.ts +182 -0
- package/src/tools/implementations/fs_grep.ts +513 -0
- package/src/tools/implementations/fs_read.ts +332 -0
- package/src/tools/implementations/fs_write.ts +113 -0
- package/src/tools/implementations/generate_image.ts +259 -0
- package/src/tools/implementations/home_fs.ts +515 -0
- package/src/tools/implementations/kill.ts +651 -0
- package/src/tools/implementations/learn.ts +166 -0
- package/src/tools/implementations/lesson-formatter.ts +38 -0
- package/src/tools/implementations/lesson_delete.ts +164 -0
- package/src/tools/implementations/lesson_get.ts +105 -0
- package/src/tools/implementations/lessons_list.ts +153 -0
- package/src/tools/implementations/mcp_resource_read.ts +161 -0
- package/src/tools/implementations/mcp_subscribe.ts +158 -0
- package/src/tools/implementations/mcp_subscription_stop.ts +85 -0
- package/src/tools/implementations/nostr_fetch.ts +149 -0
- package/src/tools/implementations/nostr_publish_as_user.ts +353 -0
- package/src/tools/implementations/project_list.ts +146 -0
- package/src/tools/implementations/rag_add_documents.ts +573 -0
- package/src/tools/implementations/rag_create_collection.ts +65 -0
- package/src/tools/implementations/rag_delete_collection.ts +68 -0
- package/src/tools/implementations/rag_list_collections.ts +77 -0
- package/src/tools/implementations/rag_query.ts +107 -0
- package/src/tools/implementations/rag_subscription_create.ts +105 -0
- package/src/tools/implementations/rag_subscription_delete.ts +80 -0
- package/src/tools/implementations/rag_subscription_get.ts +123 -0
- package/src/tools/implementations/rag_subscription_list.ts +128 -0
- package/src/tools/implementations/report_delete.ts +79 -0
- package/src/tools/implementations/report_read.ts +160 -0
- package/src/tools/implementations/report_write.ts +278 -0
- package/src/tools/implementations/reports_list.ts +77 -0
- package/src/tools/implementations/schedule_task.ts +104 -0
- package/src/tools/implementations/schedule_task_cancel.ts +62 -0
- package/src/tools/implementations/schedule_task_once.ts +128 -0
- package/src/tools/implementations/schedule_tasks_list.ts +79 -0
- package/src/tools/implementations/search.ts +160 -0
- package/src/tools/implementations/shell.ts +553 -0
- package/src/tools/implementations/todo.ts +260 -0
- package/src/tools/implementations/upload_blob.ts +381 -0
- package/src/tools/implementations/web_fetch.ts +153 -0
- package/src/tools/implementations/web_search.ts +250 -0
- package/src/tools/registry.ts +670 -0
- package/src/tools/types.ts +177 -0
- package/src/tools/utils.ts +256 -0
- package/src/types/event-ids.ts +320 -0
- package/src/types/index.ts +46 -0
- package/src/utils/agentFetcher.ts +107 -0
- package/src/utils/cli-error.ts +29 -0
- package/src/utils/conversation-id.ts +27 -0
- package/src/utils/conversation-utils.ts +1 -0
- package/src/utils/delegation-chain.ts +357 -0
- package/src/utils/error-handler.ts +42 -0
- package/src/utils/git/gitignore.ts +69 -0
- package/src/utils/git/index.ts +2 -0
- package/src/utils/git/initializeGitRepo.ts +204 -0
- package/src/utils/git/worktree.ts +260 -0
- package/src/utils/lessonFormatter.ts +70 -0
- package/src/utils/lessonTrust.ts +24 -0
- package/src/utils/lockfile.ts +123 -0
- package/src/utils/logger.ts +149 -0
- package/src/utils/nostr-entity-parser.ts +365 -0
- package/src/utils/process.ts +49 -0
- package/src/wrapper.ts +262 -0
- package/tsconfig.json +41 -0
|
@@ -0,0 +1,713 @@
|
|
|
1
|
+
import { ProgressMonitor } from "@/agents/execution/ProgressMonitor";
|
|
2
|
+
import type { AISdkTool } from "@/tools/types";
|
|
3
|
+
import { logger } from "@/utils/logger";
|
|
4
|
+
import { trace } from "@opentelemetry/api";
|
|
5
|
+
import {
|
|
6
|
+
type LanguageModel,
|
|
7
|
+
type LanguageModelMiddleware,
|
|
8
|
+
type LanguageModelUsage,
|
|
9
|
+
type ProviderRegistryProvider,
|
|
10
|
+
type StepResult,
|
|
11
|
+
type TextStreamPart,
|
|
12
|
+
extractReasoningMiddleware,
|
|
13
|
+
generateObject,
|
|
14
|
+
generateText,
|
|
15
|
+
streamText,
|
|
16
|
+
wrapLanguageModel,
|
|
17
|
+
} from "ai";
|
|
18
|
+
import type { ModelMessage } from "ai";
|
|
19
|
+
import type { ClaudeCodeSettings } from "ai-sdk-provider-claude-code";
|
|
20
|
+
import { EventEmitter } from "tseep";
|
|
21
|
+
import type { z } from "zod";
|
|
22
|
+
import { ChunkHandler, type ChunkHandlerState } from "./ChunkHandler";
|
|
23
|
+
import { createFinishHandler, type FinishHandlerConfig, type FinishHandlerState } from "./FinishHandler";
|
|
24
|
+
import { extractLastUserMessage, extractSystemContent, prepareMessagesForRequest } from "./MessageProcessor";
|
|
25
|
+
import { createFlightRecorderMiddleware } from "./middleware/flight-recorder";
|
|
26
|
+
import { PROVIDER_IDS } from "./providers/provider-ids";
|
|
27
|
+
import { getFullTelemetryConfig, getOpenRouterMetadata, getTraceCorrelationId } from "./TracingUtils";
|
|
28
|
+
import type { ProviderCapabilities } from "./providers/types";
|
|
29
|
+
import type { LanguageModelUsageWithCostUsd, LLMServiceEventMap } from "./types";
|
|
30
|
+
import { getContextWindow, resolveContextWindow } from "./utils/context-window-cache";
|
|
31
|
+
import { calculateCumulativeUsage } from "./utils/usage";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* LLM Service for runtime execution with AI SDK providers
|
|
35
|
+
* Pure runtime concerns - no configuration management
|
|
36
|
+
*/
|
|
37
|
+
export class LLMService extends EventEmitter<LLMServiceEventMap> {
|
|
38
|
+
public readonly provider: string;
|
|
39
|
+
public readonly model: string;
|
|
40
|
+
private readonly capabilities: ProviderCapabilities;
|
|
41
|
+
private readonly temperature?: number;
|
|
42
|
+
private readonly maxTokens?: number;
|
|
43
|
+
private previousChunkType?: string;
|
|
44
|
+
private readonly claudeCodeProviderFunction?: (
|
|
45
|
+
model: string,
|
|
46
|
+
options?: ClaudeCodeSettings
|
|
47
|
+
) => LanguageModel;
|
|
48
|
+
private readonly claudeCodeBaseSettings?: ClaudeCodeSettings;
|
|
49
|
+
private readonly sessionId?: string;
|
|
50
|
+
private readonly agentSlug?: string;
|
|
51
|
+
private readonly conversationId?: string;
|
|
52
|
+
private cachedContentForComplete = "";
|
|
53
|
+
/** Cumulative usage from previous steps, set via setCurrentStepUsage */
|
|
54
|
+
private currentStepUsage?: LanguageModelUsageWithCostUsd;
|
|
55
|
+
/** Last user message - stored before streaming for logging in onFinish */
|
|
56
|
+
private lastUserMessage?: string;
|
|
57
|
+
/** Chunk handler instance */
|
|
58
|
+
private chunkHandler: ChunkHandler;
|
|
59
|
+
|
|
60
|
+
constructor(
|
|
61
|
+
private readonly registry: ProviderRegistryProvider | null,
|
|
62
|
+
provider: string,
|
|
63
|
+
model: string,
|
|
64
|
+
capabilities: ProviderCapabilities,
|
|
65
|
+
temperature?: number,
|
|
66
|
+
maxTokens?: number,
|
|
67
|
+
claudeCodeProviderFunction?: (model: string, options?: ClaudeCodeSettings) => LanguageModel,
|
|
68
|
+
claudeCodeBaseSettings?: ClaudeCodeSettings,
|
|
69
|
+
sessionId?: string,
|
|
70
|
+
agentSlug?: string,
|
|
71
|
+
conversationId?: string
|
|
72
|
+
) {
|
|
73
|
+
super();
|
|
74
|
+
this.provider = provider;
|
|
75
|
+
this.model = model;
|
|
76
|
+
this.capabilities = capabilities;
|
|
77
|
+
this.temperature = temperature;
|
|
78
|
+
this.maxTokens = maxTokens;
|
|
79
|
+
this.claudeCodeProviderFunction = claudeCodeProviderFunction;
|
|
80
|
+
this.claudeCodeBaseSettings = claudeCodeBaseSettings;
|
|
81
|
+
this.sessionId = sessionId;
|
|
82
|
+
this.agentSlug = agentSlug;
|
|
83
|
+
this.conversationId = conversationId;
|
|
84
|
+
|
|
85
|
+
if (!registry && !claudeCodeProviderFunction) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
"LLMService requires either a registry or Claude Code provider function"
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Initialize chunk handler with state accessors that sync to LLMService
|
|
92
|
+
const getService = (): LLMService => this;
|
|
93
|
+
const chunkHandlerState: ChunkHandlerState = {
|
|
94
|
+
get previousChunkType() {
|
|
95
|
+
return getService().previousChunkType;
|
|
96
|
+
},
|
|
97
|
+
set previousChunkType(value: string | undefined) {
|
|
98
|
+
getService().previousChunkType = value;
|
|
99
|
+
},
|
|
100
|
+
get cachedContentForComplete() {
|
|
101
|
+
return getService().cachedContentForComplete;
|
|
102
|
+
},
|
|
103
|
+
set cachedContentForComplete(value: string) {
|
|
104
|
+
getService().cachedContentForComplete = value;
|
|
105
|
+
},
|
|
106
|
+
getCurrentStepUsage: () => getService().currentStepUsage,
|
|
107
|
+
getModelContextWindow: () => getService().getModelContextWindow(),
|
|
108
|
+
};
|
|
109
|
+
this.chunkHandler = new ChunkHandler(this, chunkHandlerState);
|
|
110
|
+
|
|
111
|
+
// Fire-and-forget: start resolving context window
|
|
112
|
+
resolveContextWindow(this.provider, this.model).catch(() => {
|
|
113
|
+
// Silently ignore - context window will be undefined if fetch fails
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Update cumulative usage from completed steps.
|
|
119
|
+
* Called from prepareStep to make usage available for tool-will-execute events.
|
|
120
|
+
* Extracts accurate usage from providerMetadata.openrouter.usage when available.
|
|
121
|
+
*/
|
|
122
|
+
updateUsageFromSteps(steps: Array<{ usage?: { inputTokens?: number; outputTokens?: number } }>): void {
|
|
123
|
+
this.currentStepUsage = calculateCumulativeUsage(steps);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Get cumulative usage from previous completed steps.
|
|
128
|
+
* Returns undefined if no steps have completed yet.
|
|
129
|
+
*/
|
|
130
|
+
getCurrentStepUsage(): LanguageModelUsageWithCostUsd | undefined {
|
|
131
|
+
return this.currentStepUsage;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get context window for current model
|
|
136
|
+
*/
|
|
137
|
+
getModelContextWindow(): number | undefined {
|
|
138
|
+
return getContextWindow(this.provider, this.model);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Get full telemetry configuration for the current service instance.
|
|
143
|
+
*/
|
|
144
|
+
private getTelemetryConfig(): ReturnType<typeof getFullTelemetryConfig> {
|
|
145
|
+
return getFullTelemetryConfig({
|
|
146
|
+
agentSlug: this.agentSlug,
|
|
147
|
+
provider: this.provider,
|
|
148
|
+
model: this.model,
|
|
149
|
+
temperature: this.temperature,
|
|
150
|
+
maxTokens: this.maxTokens,
|
|
151
|
+
sessionId: this.sessionId,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Get a language model instance.
|
|
157
|
+
* For Claude Code: Creates model with system prompt from messages.
|
|
158
|
+
* For standard providers: Gets model from registry.
|
|
159
|
+
* Wraps all models with extract-reasoning-middleware.
|
|
160
|
+
*/
|
|
161
|
+
private getLanguageModel(messages?: ModelMessage[]): LanguageModel {
|
|
162
|
+
let baseModel: LanguageModel;
|
|
163
|
+
|
|
164
|
+
// Extract system content for ALL providers (provider-agnostic telemetry)
|
|
165
|
+
const systemContent = messages ? extractSystemContent(messages) : null;
|
|
166
|
+
|
|
167
|
+
// Capture system prompt as event for ALL providers when system content exists
|
|
168
|
+
// Using event instead of attribute avoids OTel issues with unbounded/high-cardinality data
|
|
169
|
+
if (systemContent) {
|
|
170
|
+
const maxLength = 10000;
|
|
171
|
+
const truncated = systemContent.length > maxLength;
|
|
172
|
+
trace.getActiveSpan()?.addEvent("llm.system_prompt", {
|
|
173
|
+
"system_prompt.text": truncated ? systemContent.slice(0, maxLength) : systemContent,
|
|
174
|
+
"system_prompt.full_length": systemContent.length,
|
|
175
|
+
"system_prompt.truncated": truncated,
|
|
176
|
+
"system_prompt.provider": this.provider,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (this.claudeCodeProviderFunction) {
|
|
181
|
+
// Claude Code or Codex CLI provider
|
|
182
|
+
// Start with base settings (cwd, env, mcpServers, etc.) from createAgentSettings
|
|
183
|
+
const options: ClaudeCodeSettings = { ...this.claudeCodeBaseSettings };
|
|
184
|
+
|
|
185
|
+
// Pass system prompt to Claude Code provider (Claude Code-specific requirement)
|
|
186
|
+
// Using plain string (not preset+append) gives full control over agent identity
|
|
187
|
+
if (systemContent && this.provider === PROVIDER_IDS.CLAUDE_CODE) {
|
|
188
|
+
options.systemPrompt = systemContent;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
baseModel = this.claudeCodeProviderFunction(this.model, options);
|
|
192
|
+
} else if (this.registry) {
|
|
193
|
+
// Standard providers use registry
|
|
194
|
+
baseModel = this.registry.languageModel(`${this.provider}:${this.model}`);
|
|
195
|
+
} else {
|
|
196
|
+
throw new Error("No provider available for model creation");
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Build middleware chain
|
|
200
|
+
const middlewares: LanguageModelMiddleware[] = [];
|
|
201
|
+
|
|
202
|
+
// Flight recorder - records LLM interactions when enabled via 'r' key
|
|
203
|
+
middlewares.push(createFlightRecorderMiddleware());
|
|
204
|
+
|
|
205
|
+
// Extract reasoning from thinking tags
|
|
206
|
+
middlewares.push(
|
|
207
|
+
extractReasoningMiddleware({
|
|
208
|
+
tagName: "thinking",
|
|
209
|
+
separator: "\n",
|
|
210
|
+
startWithReasoning: false,
|
|
211
|
+
})
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
// Wrap with middlewares
|
|
215
|
+
// Note: Type assertion needed because AI SDK v6 beta uses LanguageModelV3 internally
|
|
216
|
+
// but stable providers export LanguageModel (union type). This is a beta compatibility issue.
|
|
217
|
+
const wrappedModel = wrapLanguageModel({
|
|
218
|
+
model: baseModel as Parameters<typeof wrapLanguageModel>[0]["model"],
|
|
219
|
+
middleware: middlewares,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
return wrappedModel;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async stream(
|
|
226
|
+
messages: ModelMessage[],
|
|
227
|
+
tools: Record<string, AISdkTool>,
|
|
228
|
+
options?: {
|
|
229
|
+
abortSignal?: AbortSignal;
|
|
230
|
+
prepareStep?: (step: {
|
|
231
|
+
messages: ModelMessage[];
|
|
232
|
+
stepNumber: number;
|
|
233
|
+
steps: StepResult<Record<string, AISdkTool>>[];
|
|
234
|
+
}) =>
|
|
235
|
+
| PromiseLike<{ model?: LanguageModel; messages?: ModelMessage[] } | undefined>
|
|
236
|
+
| { model?: LanguageModel; messages?: ModelMessage[] }
|
|
237
|
+
| undefined;
|
|
238
|
+
/** Custom stopWhen callback that wraps the default progress monitor check */
|
|
239
|
+
onStopCheck?: (steps: StepResult<Record<string, AISdkTool>>[]) => Promise<boolean>;
|
|
240
|
+
}
|
|
241
|
+
): Promise<void> {
|
|
242
|
+
const model = this.getLanguageModel(messages);
|
|
243
|
+
|
|
244
|
+
const processedMessages = prepareMessagesForRequest(messages, this.provider);
|
|
245
|
+
|
|
246
|
+
// Extract last user message for logging
|
|
247
|
+
this.lastUserMessage = extractLastUserMessage(messages);
|
|
248
|
+
|
|
249
|
+
const startTime = Date.now();
|
|
250
|
+
|
|
251
|
+
// ProgressMonitor is only used for providers without built-in tools
|
|
252
|
+
let progressMonitor: ProgressMonitor | undefined;
|
|
253
|
+
if (!this.capabilities.builtInTools) {
|
|
254
|
+
const reviewModel = this.getLanguageModel();
|
|
255
|
+
progressMonitor = new ProgressMonitor(reviewModel);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const stopWhen = async ({
|
|
259
|
+
steps,
|
|
260
|
+
}: { steps: StepResult<Record<string, AISdkTool>>[] }): Promise<boolean> => {
|
|
261
|
+
// First check custom stop condition
|
|
262
|
+
if (options?.onStopCheck) {
|
|
263
|
+
const shouldStop = await options.onStopCheck(steps);
|
|
264
|
+
if (shouldStop) {
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Then check default progress monitor (only for standard providers)
|
|
270
|
+
if (progressMonitor) {
|
|
271
|
+
const toolNames: string[] = [];
|
|
272
|
+
for (const step of steps) {
|
|
273
|
+
if (step.toolCalls) {
|
|
274
|
+
for (const tc of step.toolCalls) {
|
|
275
|
+
toolNames.push(tc.toolName);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const shouldContinue = await progressMonitor.check(toolNames);
|
|
280
|
+
return !shouldContinue;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return false;
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
// Create finish handler config and state
|
|
287
|
+
const finishConfig: FinishHandlerConfig = {
|
|
288
|
+
provider: this.provider,
|
|
289
|
+
model: this.model,
|
|
290
|
+
getModelContextWindow: () => this.getModelContextWindow(),
|
|
291
|
+
};
|
|
292
|
+
const finishState: FinishHandlerState = {
|
|
293
|
+
getCachedContent: () => this.cachedContentForComplete,
|
|
294
|
+
clearCachedContent: () => {
|
|
295
|
+
this.cachedContentForComplete = "";
|
|
296
|
+
},
|
|
297
|
+
getLastUserMessage: () => this.lastUserMessage,
|
|
298
|
+
clearLastUserMessage: () => {
|
|
299
|
+
this.lastUserMessage = undefined;
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// DIAGNOSTIC: Track state before streamText call
|
|
304
|
+
const activeSpan = trace.getActiveSpan();
|
|
305
|
+
activeSpan?.addEvent("llm.streamText_preparing", {
|
|
306
|
+
"stream.messages_count": processedMessages.length,
|
|
307
|
+
"stream.tools_count": this.capabilities.builtInTools ? 0 : Object.keys(tools).length,
|
|
308
|
+
"stream.abort_signal_present": !!options?.abortSignal,
|
|
309
|
+
"stream.abort_signal_aborted": options?.abortSignal?.aborted ?? false,
|
|
310
|
+
"stream.provider": this.provider,
|
|
311
|
+
"stream.model": this.model,
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// Track chunk statistics for post-mortem analysis
|
|
315
|
+
let chunkCount = 0;
|
|
316
|
+
let lastChunkType: string | undefined;
|
|
317
|
+
let lastChunkTime = startTime;
|
|
318
|
+
let toolInputStartCount = 0;
|
|
319
|
+
let toolCallCount = 0;
|
|
320
|
+
let toolResultCount = 0;
|
|
321
|
+
let textDeltaCount = 0;
|
|
322
|
+
let finishPartSeen = false;
|
|
323
|
+
let stepFinishCount = 0;
|
|
324
|
+
|
|
325
|
+
// DIAGNOSTIC: Track inter-chunk timing for concurrent streaming bottleneck analysis
|
|
326
|
+
let maxInterChunkDelayMs = 0;
|
|
327
|
+
let totalInterChunkDelayMs = 0;
|
|
328
|
+
const interChunkDelays: number[] = []; // For percentile analysis
|
|
329
|
+
const SLOW_CHUNK_THRESHOLD_MS = 500; // Log chunks taking >500ms
|
|
330
|
+
|
|
331
|
+
const { fullStream } = streamText({
|
|
332
|
+
model,
|
|
333
|
+
messages: processedMessages,
|
|
334
|
+
// Don't pass tools for providers with built-in tools
|
|
335
|
+
...(!this.capabilities.builtInTools && { tools }),
|
|
336
|
+
temperature: this.temperature,
|
|
337
|
+
maxOutputTokens: this.maxTokens,
|
|
338
|
+
stopWhen,
|
|
339
|
+
prepareStep: options?.prepareStep,
|
|
340
|
+
abortSignal: options?.abortSignal,
|
|
341
|
+
|
|
342
|
+
// ✨ Enable full AI SDK telemetry
|
|
343
|
+
experimental_telemetry: this.getTelemetryConfig(),
|
|
344
|
+
|
|
345
|
+
providerOptions: {
|
|
346
|
+
openrouter: {
|
|
347
|
+
usage: { include: true },
|
|
348
|
+
user: getTraceCorrelationId(),
|
|
349
|
+
metadata: getOpenRouterMetadata(this.agentSlug, this.conversationId),
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
|
|
353
|
+
onChunk: (event) => this.chunkHandler.handleChunk(event),
|
|
354
|
+
onFinish: createFinishHandler(this, finishConfig, finishState),
|
|
355
|
+
onError: ({ error }) => {
|
|
356
|
+
// Emit stream-error event for the executor to handle and publish to user
|
|
357
|
+
activeSpan?.addEvent("llm.onError_called", {
|
|
358
|
+
"error.message": error instanceof Error ? error.message : String(error),
|
|
359
|
+
"error.type": error instanceof Error ? error.constructor.name : typeof error,
|
|
360
|
+
"stream.chunk_count_at_error": chunkCount,
|
|
361
|
+
"stream.last_chunk_type": lastChunkType ?? "none",
|
|
362
|
+
});
|
|
363
|
+
this.emit("stream-error", { error });
|
|
364
|
+
},
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
// Consume the stream (this is what triggers everything!)
|
|
368
|
+
try {
|
|
369
|
+
for await (const part of fullStream) {
|
|
370
|
+
const chunkReceivedTime = Date.now();
|
|
371
|
+
const interChunkDelay = chunkReceivedTime - lastChunkTime;
|
|
372
|
+
|
|
373
|
+
chunkCount++;
|
|
374
|
+
lastChunkType = part.type;
|
|
375
|
+
|
|
376
|
+
// DIAGNOSTIC: Track inter-chunk timing
|
|
377
|
+
if (chunkCount > 1) { // Skip first chunk (no previous to compare)
|
|
378
|
+
totalInterChunkDelayMs += interChunkDelay;
|
|
379
|
+
interChunkDelays.push(interChunkDelay);
|
|
380
|
+
if (interChunkDelay > maxInterChunkDelayMs) {
|
|
381
|
+
maxInterChunkDelayMs = interChunkDelay;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Log slow chunks that might indicate blocking
|
|
385
|
+
if (interChunkDelay > SLOW_CHUNK_THRESHOLD_MS) {
|
|
386
|
+
activeSpan?.addEvent("llm.slow_chunk_detected", {
|
|
387
|
+
"chunk.number": chunkCount,
|
|
388
|
+
"chunk.type": part.type,
|
|
389
|
+
"chunk.inter_delay_ms": interChunkDelay,
|
|
390
|
+
"chunk.threshold_ms": SLOW_CHUNK_THRESHOLD_MS,
|
|
391
|
+
"process.memory_heap_used_mb": Math.round(
|
|
392
|
+
process.memoryUsage().heapUsed / 1024 / 1024
|
|
393
|
+
),
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
lastChunkTime = chunkReceivedTime;
|
|
399
|
+
|
|
400
|
+
// Track specific part types for diagnostics
|
|
401
|
+
switch (part.type) {
|
|
402
|
+
case "tool-input-start":
|
|
403
|
+
toolInputStartCount++;
|
|
404
|
+
break;
|
|
405
|
+
case "tool-call":
|
|
406
|
+
toolCallCount++;
|
|
407
|
+
break;
|
|
408
|
+
case "tool-result":
|
|
409
|
+
toolResultCount++;
|
|
410
|
+
break;
|
|
411
|
+
case "text-delta":
|
|
412
|
+
textDeltaCount++;
|
|
413
|
+
break;
|
|
414
|
+
case "finish":
|
|
415
|
+
finishPartSeen = true;
|
|
416
|
+
activeSpan?.addEvent("llm.finish_part_received", {
|
|
417
|
+
"finish.reason": (part as { finishReason?: string }).finishReason ?? "unknown",
|
|
418
|
+
"stream.chunk_count": chunkCount,
|
|
419
|
+
"stream.ms_since_start": lastChunkTime - startTime,
|
|
420
|
+
});
|
|
421
|
+
break;
|
|
422
|
+
case "finish-step":
|
|
423
|
+
stepFinishCount++;
|
|
424
|
+
activeSpan?.addEvent("llm.step_finish_received", {
|
|
425
|
+
"step.number": stepFinishCount,
|
|
426
|
+
"step.finish_reason": (part as { finishReason?: string }).finishReason ?? "unknown",
|
|
427
|
+
"stream.chunk_count": chunkCount,
|
|
428
|
+
});
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Handle events that don't come through onChunk callback.
|
|
433
|
+
// AI SDK's onChunk only fires for: text-delta, reasoning-delta, source,
|
|
434
|
+
// tool-call, tool-input-start, tool-input-delta, tool-result, raw.
|
|
435
|
+
// Events like "finish" and "tool-error" must be forwarded explicitly.
|
|
436
|
+
if (part.type === "finish") {
|
|
437
|
+
// Emit raw-chunk directly to reach StreamPublisher (for HTTP wrapper SSE)
|
|
438
|
+
// WITHOUT going through ChunkHandler which would trigger chunk-type-change
|
|
439
|
+
// and cause AgentExecutor to publish a duplicate kind:1 event.
|
|
440
|
+
this.emit("raw-chunk", { chunk: part as TextStreamPart<Record<string, AISdkTool>> });
|
|
441
|
+
} else if (part.type === "tool-error") {
|
|
442
|
+
this.chunkHandler.handleChunk({ chunk: part as TextStreamPart<Record<string, AISdkTool>> });
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// DIAGNOSTIC: Track when for-await loop completes with detailed stats
|
|
447
|
+
const loopCompleteTime = Date.now();
|
|
448
|
+
const loopDuration = loopCompleteTime - startTime;
|
|
449
|
+
const timeSinceLastChunk = loopCompleteTime - lastChunkTime;
|
|
450
|
+
|
|
451
|
+
// Calculate inter-chunk timing percentiles for bottleneck analysis
|
|
452
|
+
const sortedDelays = [...interChunkDelays].sort((a, b) => a - b);
|
|
453
|
+
const p50Delay = sortedDelays.length > 0
|
|
454
|
+
? sortedDelays[Math.floor(sortedDelays.length * 0.5)]
|
|
455
|
+
: 0;
|
|
456
|
+
const p95Delay = sortedDelays.length > 0
|
|
457
|
+
? sortedDelays[Math.floor(sortedDelays.length * 0.95)]
|
|
458
|
+
: 0;
|
|
459
|
+
const p99Delay = sortedDelays.length > 0
|
|
460
|
+
? sortedDelays[Math.floor(sortedDelays.length * 0.99)]
|
|
461
|
+
: 0;
|
|
462
|
+
const avgInterChunkDelay = interChunkDelays.length > 0
|
|
463
|
+
? totalInterChunkDelayMs / interChunkDelays.length
|
|
464
|
+
: 0;
|
|
465
|
+
|
|
466
|
+
activeSpan?.addEvent("llm.stream_loop_complete", {
|
|
467
|
+
"stream.loop_complete_time": loopCompleteTime,
|
|
468
|
+
"stream.loop_duration_ms": loopDuration,
|
|
469
|
+
"stream.cached_content_length": this.cachedContentForComplete.length,
|
|
470
|
+
"stream.total_chunk_count": chunkCount,
|
|
471
|
+
"stream.last_chunk_type": lastChunkType ?? "none",
|
|
472
|
+
"stream.ms_since_last_chunk": timeSinceLastChunk,
|
|
473
|
+
"stream.finish_part_seen": finishPartSeen,
|
|
474
|
+
"stream.step_finish_count": stepFinishCount,
|
|
475
|
+
"stream.tool_input_start_count": toolInputStartCount,
|
|
476
|
+
"stream.tool_call_count": toolCallCount,
|
|
477
|
+
"stream.tool_result_count": toolResultCount,
|
|
478
|
+
"stream.text_delta_count": textDeltaCount,
|
|
479
|
+
"stream.abort_signal_aborted": options?.abortSignal?.aborted ?? false,
|
|
480
|
+
// DIAGNOSTIC: Inter-chunk timing metrics for bottleneck analysis
|
|
481
|
+
"stream.inter_chunk_max_delay_ms": maxInterChunkDelayMs,
|
|
482
|
+
"stream.inter_chunk_avg_delay_ms": Math.round(avgInterChunkDelay * 100) / 100,
|
|
483
|
+
"stream.inter_chunk_p50_delay_ms": p50Delay,
|
|
484
|
+
"stream.inter_chunk_p95_delay_ms": p95Delay,
|
|
485
|
+
"stream.inter_chunk_p99_delay_ms": p99Delay,
|
|
486
|
+
"stream.slow_chunks_count": interChunkDelays.filter(d => d > SLOW_CHUNK_THRESHOLD_MS).length,
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
// CRITICAL DIAGNOSTIC: If loop completed but no finish part was seen, something is very wrong
|
|
490
|
+
if (!finishPartSeen && chunkCount > 0) {
|
|
491
|
+
activeSpan?.addEvent("llm.stream_incomplete_warning", {
|
|
492
|
+
"warning.message": "Stream loop completed without seeing finish part",
|
|
493
|
+
"stream.chunk_count": chunkCount,
|
|
494
|
+
"stream.last_chunk_type": lastChunkType ?? "none",
|
|
495
|
+
"stream.tool_input_start_count": toolInputStartCount,
|
|
496
|
+
"stream.tool_call_count": toolCallCount,
|
|
497
|
+
"stream.tool_result_count": toolResultCount,
|
|
498
|
+
});
|
|
499
|
+
logger.warn("[LLMService] Stream loop completed without finish part", {
|
|
500
|
+
chunkCount,
|
|
501
|
+
lastChunkType,
|
|
502
|
+
toolInputStartCount,
|
|
503
|
+
toolCallCount,
|
|
504
|
+
toolResultCount,
|
|
505
|
+
provider: this.provider,
|
|
506
|
+
model: this.model,
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
} catch (error) {
|
|
510
|
+
// DIAGNOSTIC: Track error with stream state
|
|
511
|
+
activeSpan?.addEvent("llm.stream_loop_error", {
|
|
512
|
+
"error.message": error instanceof Error ? error.message : String(error),
|
|
513
|
+
"error.type": error instanceof Error ? error.constructor.name : typeof error,
|
|
514
|
+
"stream.chunk_count_at_error": chunkCount,
|
|
515
|
+
"stream.last_chunk_type": lastChunkType ?? "none",
|
|
516
|
+
"stream.finish_part_seen": finishPartSeen,
|
|
517
|
+
"stream.abort_signal_aborted": options?.abortSignal?.aborted ?? false,
|
|
518
|
+
});
|
|
519
|
+
await this.handleStreamError(error, startTime);
|
|
520
|
+
throw error;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
private async handleStreamError(error: unknown, startTime: number): Promise<void> {
|
|
525
|
+
const duration = Date.now() - startTime;
|
|
526
|
+
|
|
527
|
+
// Format stack trace for better readability
|
|
528
|
+
const stackLines =
|
|
529
|
+
error instanceof Error && error.stack
|
|
530
|
+
? error.stack
|
|
531
|
+
.split("\n")
|
|
532
|
+
.map((line) => line.trim())
|
|
533
|
+
.filter(Boolean)
|
|
534
|
+
: undefined;
|
|
535
|
+
|
|
536
|
+
logger.error("[LLMService] Stream failed", {
|
|
537
|
+
model: `${this.provider}:${this.model}`,
|
|
538
|
+
duration,
|
|
539
|
+
error: error instanceof Error ? error.message : String(error),
|
|
540
|
+
stack: stackLines,
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Get the language model for use with AI SDK's generateObject and other functions
|
|
546
|
+
*/
|
|
547
|
+
getModel(): LanguageModel {
|
|
548
|
+
return this.getLanguageModel();
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Create a language model instance for dynamic model switching.
|
|
553
|
+
* Used by AgentExecutor when the change_model tool switches variants mid-run.
|
|
554
|
+
*/
|
|
555
|
+
static createLanguageModelFromRegistry(
|
|
556
|
+
provider: string,
|
|
557
|
+
model: string,
|
|
558
|
+
registry: ProviderRegistryProvider
|
|
559
|
+
): LanguageModel {
|
|
560
|
+
return registry.languageModel(`${provider}:${model}`);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Execute object generation
|
|
565
|
+
*/
|
|
566
|
+
private async executeObjectGeneration<T>(
|
|
567
|
+
languageModel: LanguageModel,
|
|
568
|
+
messages: ModelMessage[],
|
|
569
|
+
schema: z.ZodSchema<T>,
|
|
570
|
+
tools: Record<string, AISdkTool> | undefined
|
|
571
|
+
): Promise<{ object: T; usage: LanguageModelUsage }> {
|
|
572
|
+
return await generateObject({
|
|
573
|
+
model: languageModel,
|
|
574
|
+
messages,
|
|
575
|
+
schema,
|
|
576
|
+
temperature: this.temperature,
|
|
577
|
+
maxOutputTokens: this.maxTokens,
|
|
578
|
+
...(tools && Object.keys(tools).length > 0 ? { tools } : {}),
|
|
579
|
+
|
|
580
|
+
// ✨ Enable full AI SDK telemetry
|
|
581
|
+
experimental_telemetry: this.getTelemetryConfig(),
|
|
582
|
+
|
|
583
|
+
providerOptions: {
|
|
584
|
+
openrouter: {
|
|
585
|
+
usage: { include: true },
|
|
586
|
+
user: getTraceCorrelationId(),
|
|
587
|
+
metadata: getOpenRouterMetadata(this.agentSlug, this.conversationId),
|
|
588
|
+
},
|
|
589
|
+
},
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Generate a structured object using AI SDK's generateObject
|
|
595
|
+
*/
|
|
596
|
+
async generateObject<T>(
|
|
597
|
+
messages: ModelMessage[],
|
|
598
|
+
schema: z.ZodSchema<T>,
|
|
599
|
+
tools?: Record<string, AISdkTool>
|
|
600
|
+
): Promise<{ object: T; usage: LanguageModelUsageWithCostUsd }> {
|
|
601
|
+
const startTime = Date.now();
|
|
602
|
+
|
|
603
|
+
return this.withErrorHandling(
|
|
604
|
+
async () => {
|
|
605
|
+
trace.getActiveSpan()?.addEvent("llm.generate_object_start", {
|
|
606
|
+
"llm.provider": this.provider,
|
|
607
|
+
"llm.model": this.model,
|
|
608
|
+
"messages.count": messages.length,
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
const languageModel = this.getLanguageModel();
|
|
612
|
+
const result = await this.executeObjectGeneration(
|
|
613
|
+
languageModel,
|
|
614
|
+
messages,
|
|
615
|
+
schema,
|
|
616
|
+
tools
|
|
617
|
+
);
|
|
618
|
+
|
|
619
|
+
const duration = Date.now() - startTime;
|
|
620
|
+
|
|
621
|
+
trace.getActiveSpan()?.addEvent("llm.generate_object_complete", {
|
|
622
|
+
"llm.provider": this.provider,
|
|
623
|
+
"llm.model": this.model,
|
|
624
|
+
"llm.duration_ms": duration,
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
return {
|
|
628
|
+
object: result.object,
|
|
629
|
+
usage: result.usage,
|
|
630
|
+
};
|
|
631
|
+
},
|
|
632
|
+
"Generate structured object",
|
|
633
|
+
startTime
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Generate plain text output using AI SDK's generateText.
|
|
639
|
+
* Unlike generateObject, this returns unstructured text directly from the model.
|
|
640
|
+
*/
|
|
641
|
+
async generateText(
|
|
642
|
+
messages: ModelMessage[]
|
|
643
|
+
): Promise<{ text: string; usage: LanguageModelUsageWithCostUsd }> {
|
|
644
|
+
const startTime = Date.now();
|
|
645
|
+
|
|
646
|
+
return this.withErrorHandling(
|
|
647
|
+
async () => {
|
|
648
|
+
trace.getActiveSpan()?.addEvent("llm.generate_text_start", {
|
|
649
|
+
"llm.provider": this.provider,
|
|
650
|
+
"llm.model": this.model,
|
|
651
|
+
"messages.count": messages.length,
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
const languageModel = this.getLanguageModel();
|
|
655
|
+
const result = await generateText({
|
|
656
|
+
model: languageModel,
|
|
657
|
+
messages,
|
|
658
|
+
temperature: this.temperature,
|
|
659
|
+
maxOutputTokens: this.maxTokens,
|
|
660
|
+
|
|
661
|
+
// ✨ Enable full AI SDK telemetry
|
|
662
|
+
experimental_telemetry: this.getTelemetryConfig(),
|
|
663
|
+
|
|
664
|
+
providerOptions: {
|
|
665
|
+
openrouter: {
|
|
666
|
+
usage: { include: true },
|
|
667
|
+
user: getTraceCorrelationId(),
|
|
668
|
+
metadata: getOpenRouterMetadata(this.agentSlug, this.conversationId),
|
|
669
|
+
},
|
|
670
|
+
},
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
const duration = Date.now() - startTime;
|
|
674
|
+
|
|
675
|
+
trace.getActiveSpan()?.addEvent("llm.generate_text_complete", {
|
|
676
|
+
"llm.provider": this.provider,
|
|
677
|
+
"llm.model": this.model,
|
|
678
|
+
"llm.duration_ms": duration,
|
|
679
|
+
"text.length": result.text.length,
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
return {
|
|
683
|
+
text: result.text,
|
|
684
|
+
usage: result.usage,
|
|
685
|
+
};
|
|
686
|
+
},
|
|
687
|
+
"Generate text",
|
|
688
|
+
startTime
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Higher-order function for centralized error handling
|
|
694
|
+
*/
|
|
695
|
+
private async withErrorHandling<T>(
|
|
696
|
+
operation: () => Promise<T>,
|
|
697
|
+
operationName: string,
|
|
698
|
+
startTime: number
|
|
699
|
+
): Promise<T> {
|
|
700
|
+
try {
|
|
701
|
+
return await operation();
|
|
702
|
+
} catch (error) {
|
|
703
|
+
const duration = Date.now() - startTime;
|
|
704
|
+
logger.error(`[LLMService] ${operationName} failed`, {
|
|
705
|
+
provider: this.provider,
|
|
706
|
+
model: this.model,
|
|
707
|
+
duration,
|
|
708
|
+
error: error instanceof Error ? error.message : String(error),
|
|
709
|
+
});
|
|
710
|
+
throw error;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|