@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,597 @@
|
|
|
1
|
+
import type { AgentInstance } from "@/agents/types";
|
|
2
|
+
import type { ConversationStore } from "@/conversations/ConversationStore";
|
|
3
|
+
import type { NDKAgentLesson } from "@/events/NDKAgentLesson";
|
|
4
|
+
import { providerRegistry } from "@/llm/providers";
|
|
5
|
+
import type { ProviderCapabilities } from "@/llm/providers/types";
|
|
6
|
+
import { shortenConversationId } from "@/utils/conversation-id";
|
|
7
|
+
import { agentTodosFragment } from "@/prompts/fragments/06-agent-todos";
|
|
8
|
+
import { buildSystemPromptMessages } from "@/prompts/utils/systemPromptBuilder";
|
|
9
|
+
import { getPubkeyService } from "@/services/PubkeyService";
|
|
10
|
+
import type { CompletedDelegation, PendingDelegation } from "@/services/ral/types";
|
|
11
|
+
import type { MCPManager } from "@/services/mcp/MCPManager";
|
|
12
|
+
import type { NudgeToolPermissions, NudgeData, WhitelistItem } from "@/services/nudge";
|
|
13
|
+
import type { LessonComment } from "@/services/prompt-compiler";
|
|
14
|
+
import type { SkillData } from "@/services/skill";
|
|
15
|
+
import { combineSystemReminders } from "@/services/system-reminder";
|
|
16
|
+
import type { NDKProject } from "@nostr-dev-kit/ndk";
|
|
17
|
+
import type { ModelMessage, TextPart } from "ai";
|
|
18
|
+
import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
19
|
+
import type { SessionManager } from "./SessionManager";
|
|
20
|
+
import type { LLMService } from "@/llm/service";
|
|
21
|
+
import { createCompressionService } from "@/services/compression/CompressionService.js";
|
|
22
|
+
import { logger } from "@/utils/logger";
|
|
23
|
+
|
|
24
|
+
const tracer = trace.getTracer("tenex.message-compiler");
|
|
25
|
+
|
|
26
|
+
type CompilationMode = "full" | "delta";
|
|
27
|
+
|
|
28
|
+
interface MessageCompilationPlan {
|
|
29
|
+
mode: CompilationMode;
|
|
30
|
+
cursor?: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Ephemeral message to include in LLM context but NOT persist */
|
|
34
|
+
export interface EphemeralMessage {
|
|
35
|
+
role: "user" | "system";
|
|
36
|
+
content: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* CompiledMessage - A message with event ID for compression tracking.
|
|
41
|
+
* Combines ModelMessage with optional eventId field.
|
|
42
|
+
*/
|
|
43
|
+
export type CompiledMessage = ModelMessage & {
|
|
44
|
+
eventId?: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export interface MessageCompilerContext {
|
|
48
|
+
agent: AgentInstance;
|
|
49
|
+
project: NDKProject;
|
|
50
|
+
conversation: ConversationStore;
|
|
51
|
+
projectBasePath?: string;
|
|
52
|
+
workingDirectory?: string;
|
|
53
|
+
currentBranch?: string;
|
|
54
|
+
availableAgents?: AgentInstance[];
|
|
55
|
+
agentLessons?: Map<string, NDKAgentLesson[]>;
|
|
56
|
+
/** Comments on agent lessons (kind 1111 NIP-22 comments) */
|
|
57
|
+
agentComments?: Map<string, LessonComment[]>;
|
|
58
|
+
mcpManager?: MCPManager;
|
|
59
|
+
nudgeContent?: string;
|
|
60
|
+
/** Individual nudge data for rendering in fragments */
|
|
61
|
+
nudges?: NudgeData[];
|
|
62
|
+
/** Tool permissions extracted from nudge events */
|
|
63
|
+
nudgeToolPermissions?: NudgeToolPermissions;
|
|
64
|
+
/** Concatenated skill content */
|
|
65
|
+
skillContent?: string;
|
|
66
|
+
/** Individual skill data for rendering in fragments */
|
|
67
|
+
skills?: SkillData[];
|
|
68
|
+
respondingToPubkey: string;
|
|
69
|
+
pendingDelegations: PendingDelegation[];
|
|
70
|
+
completedDelegations: CompletedDelegation[];
|
|
71
|
+
ralNumber: number;
|
|
72
|
+
/** System prompt fragment describing available meta model variants */
|
|
73
|
+
metaModelSystemPrompt?: string;
|
|
74
|
+
/** Variant-specific system prompt to inject when a meta model variant is active */
|
|
75
|
+
variantSystemPrompt?: string;
|
|
76
|
+
/** Ephemeral messages to include in this compilation only (not persisted) */
|
|
77
|
+
ephemeralMessages?: EphemeralMessage[];
|
|
78
|
+
/** Available whitelisted nudges for delegation */
|
|
79
|
+
availableNudges?: WhitelistItem[];
|
|
80
|
+
/** Available whitelisted skills */
|
|
81
|
+
availableSkills?: WhitelistItem[];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface CompiledMessages {
|
|
85
|
+
messages: ModelMessage[];
|
|
86
|
+
mode: CompilationMode;
|
|
87
|
+
counts: {
|
|
88
|
+
systemPrompt: number;
|
|
89
|
+
conversation: number;
|
|
90
|
+
dynamicContext: number;
|
|
91
|
+
total: number;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export class MessageCompiler {
|
|
96
|
+
private readonly plan: MessageCompilationPlan;
|
|
97
|
+
private currentCursor: number;
|
|
98
|
+
|
|
99
|
+
constructor(
|
|
100
|
+
private providerId: string,
|
|
101
|
+
private sessionManager: SessionManager,
|
|
102
|
+
private conversationStore: ConversationStore,
|
|
103
|
+
private llmService?: LLMService,
|
|
104
|
+
private compressionLlmService?: LLMService
|
|
105
|
+
) {
|
|
106
|
+
this.plan = this.buildPlan();
|
|
107
|
+
this.currentCursor = this.plan.cursor ?? -1;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async compile(context: MessageCompilerContext): Promise<CompiledMessages> {
|
|
111
|
+
return tracer.startActiveSpan("tenex.message.compile", async (span) => {
|
|
112
|
+
try {
|
|
113
|
+
// Validate conversation source - context.conversation must match conversationStore
|
|
114
|
+
if (context.conversation.id !== this.conversationStore.getId()) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
`Conversation mismatch: context.conversation.id (${context.conversation.id}) ` +
|
|
117
|
+
`does not match conversationStore.id (${this.conversationStore.getId()})`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
span.setAttribute("agent.slug", context.agent.slug);
|
|
122
|
+
span.setAttribute("conversation.id", shortenConversationId(context.conversation.id));
|
|
123
|
+
span.setAttribute("ral.number", context.ralNumber);
|
|
124
|
+
span.setAttribute("compilation.mode", this.plan.mode);
|
|
125
|
+
|
|
126
|
+
// Consume any deferred injections from previous turns (e.g., supervision nudges).
|
|
127
|
+
// These are added to ephemeral messages so they appear in context but aren't persisted.
|
|
128
|
+
const deferredInjections = this.conversationStore.consumeDeferredInjections(context.agent.pubkey);
|
|
129
|
+
if (deferredInjections.length > 0) {
|
|
130
|
+
span.setAttribute("deferred_injections.count", deferredInjections.length);
|
|
131
|
+
logger.debug("[MessageCompiler] Consuming deferred injections", {
|
|
132
|
+
agent: context.agent.slug,
|
|
133
|
+
count: deferredInjections.length,
|
|
134
|
+
sources: deferredInjections.map(d => d.source).filter(Boolean),
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Initialize ephemeralMessages if not present
|
|
138
|
+
if (!context.ephemeralMessages) {
|
|
139
|
+
context.ephemeralMessages = [];
|
|
140
|
+
}
|
|
141
|
+
// Add deferred injections as ephemeral system messages
|
|
142
|
+
for (const injection of deferredInjections) {
|
|
143
|
+
context.ephemeralMessages.push({
|
|
144
|
+
role: injection.role,
|
|
145
|
+
content: injection.content,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
// Save the store to persist the consumption
|
|
149
|
+
await this.conversationStore.save();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const cursor = this.currentCursor;
|
|
153
|
+
const messages: ModelMessage[] = [];
|
|
154
|
+
let systemPromptCount = 0;
|
|
155
|
+
let dynamicContextCount = 0;
|
|
156
|
+
|
|
157
|
+
if (this.plan.mode === "full") {
|
|
158
|
+
// Build system prompt messages (sub-span removed - parent compile span is sufficient)
|
|
159
|
+
const systemPromptMessages = await buildSystemPromptMessages(context);
|
|
160
|
+
|
|
161
|
+
// Apply compression: load existing segments and apply to conversation history
|
|
162
|
+
await this.applyCompression(context);
|
|
163
|
+
|
|
164
|
+
// Build conversation history (sub-span removed - parent compile span is sufficient)
|
|
165
|
+
const conversationMessages = await this.conversationStore.buildMessagesForRal(
|
|
166
|
+
context.agent.pubkey,
|
|
167
|
+
context.ralNumber,
|
|
168
|
+
context.projectBasePath
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
// Build dynamic context content (todo state, response context)
|
|
172
|
+
const dynamicContextContent = await this.buildDynamicContextContent(context);
|
|
173
|
+
|
|
174
|
+
messages.push(...systemPromptMessages.map((sm) => sm.message));
|
|
175
|
+
|
|
176
|
+
// Inject meta model system prompts if present
|
|
177
|
+
// These describe available model variants and variant-specific instructions
|
|
178
|
+
if (context.metaModelSystemPrompt) {
|
|
179
|
+
messages.push({
|
|
180
|
+
role: "system",
|
|
181
|
+
content: context.metaModelSystemPrompt,
|
|
182
|
+
});
|
|
183
|
+
systemPromptCount++;
|
|
184
|
+
}
|
|
185
|
+
if (context.variantSystemPrompt) {
|
|
186
|
+
messages.push({
|
|
187
|
+
role: "system",
|
|
188
|
+
content: context.variantSystemPrompt,
|
|
189
|
+
});
|
|
190
|
+
systemPromptCount++;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
messages.push(...conversationMessages);
|
|
194
|
+
|
|
195
|
+
// Collect all ephemeral content: dynamic context + any queued ephemeral messages.
|
|
196
|
+
// These are appended to the last user message as <system-reminder> tags.
|
|
197
|
+
// This unifies behavioral nudges (heuristic violations, deferred injections,
|
|
198
|
+
// todo state, response context) into a consistent format.
|
|
199
|
+
// AGENTS.md injections remain tool-bound (in tool result output).
|
|
200
|
+
//
|
|
201
|
+
// NOTE: dynamicContextCount is NOT incremented here because ephemeral content
|
|
202
|
+
// is appended to an existing user message (not added as separate messages).
|
|
203
|
+
// The count tracks messages in the array, not ephemeral injections.
|
|
204
|
+
const allEphemeralMessages: EphemeralMessage[] = [];
|
|
205
|
+
|
|
206
|
+
// Add dynamic context as ephemeral
|
|
207
|
+
if (dynamicContextContent) {
|
|
208
|
+
allEphemeralMessages.push({
|
|
209
|
+
role: "system",
|
|
210
|
+
content: dynamicContextContent,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Add queued ephemeral messages (heuristic violations, deferred injections, etc.)
|
|
215
|
+
if (context.ephemeralMessages?.length) {
|
|
216
|
+
allEphemeralMessages.push(...context.ephemeralMessages);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Append all ephemeral content to the last user message
|
|
220
|
+
if (allEphemeralMessages.length > 0) {
|
|
221
|
+
this.appendEphemeralMessagesToLastUserMessage(messages, allEphemeralMessages);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
systemPromptCount += systemPromptMessages.length;
|
|
225
|
+
// NOTE: dynamicContextCount stays 0 because ephemeral content is appended to
|
|
226
|
+
// existing user messages, not added as separate messages. The count is retained
|
|
227
|
+
// in the interface for backwards compatibility with telemetry consumers.
|
|
228
|
+
} else {
|
|
229
|
+
// In delta mode, only send new conversation messages.
|
|
230
|
+
// The session already has full context from initial compilation.
|
|
231
|
+
// However, dynamic context (todos, response routing) must still be included
|
|
232
|
+
// since it can change between turns in a stateful session.
|
|
233
|
+
const conversationMessages = await this.conversationStore.buildMessagesForRalAfterIndex(
|
|
234
|
+
context.agent.pubkey,
|
|
235
|
+
context.ralNumber,
|
|
236
|
+
cursor,
|
|
237
|
+
context.projectBasePath
|
|
238
|
+
);
|
|
239
|
+
messages.push(...conversationMessages);
|
|
240
|
+
|
|
241
|
+
// Build dynamic context for delta mode (todo state, response context)
|
|
242
|
+
const dynamicContextContent = await this.buildDynamicContextContent(context);
|
|
243
|
+
|
|
244
|
+
// Collect all ephemeral content for delta mode
|
|
245
|
+
const allEphemeralMessages: EphemeralMessage[] = [];
|
|
246
|
+
|
|
247
|
+
if (dynamicContextContent) {
|
|
248
|
+
allEphemeralMessages.push({
|
|
249
|
+
role: "system",
|
|
250
|
+
content: dynamicContextContent,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (context.ephemeralMessages?.length) {
|
|
255
|
+
allEphemeralMessages.push(...context.ephemeralMessages);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Append all ephemeral content to the last user message
|
|
259
|
+
if (allEphemeralMessages.length > 0) {
|
|
260
|
+
this.appendEphemeralMessagesToLastUserMessage(messages, allEphemeralMessages);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const conversationCount = this.plan.mode === "full"
|
|
265
|
+
? messages.length - systemPromptCount - dynamicContextCount
|
|
266
|
+
: messages.length;
|
|
267
|
+
|
|
268
|
+
const counts = {
|
|
269
|
+
systemPrompt: systemPromptCount,
|
|
270
|
+
conversation: conversationCount,
|
|
271
|
+
dynamicContext: dynamicContextCount,
|
|
272
|
+
total: messages.length,
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
span.setAttribute("message.count", messages.length);
|
|
276
|
+
span.setAttribute("counts.system_prompt", counts.systemPrompt);
|
|
277
|
+
span.setAttribute("counts.conversation", counts.conversation);
|
|
278
|
+
span.setAttribute("counts.dynamic_context", counts.dynamicContext);
|
|
279
|
+
span.setAttribute("counts.total", counts.total);
|
|
280
|
+
|
|
281
|
+
// Update cursor for subsequent prepareStep calls within same execution.
|
|
282
|
+
// This prevents resending the same messages during streaming.
|
|
283
|
+
// CRITICAL: Use compressed count, not raw count. Cursor must be in compressed space
|
|
284
|
+
// to match the space used by buildMessagesForRalAfterIndex.
|
|
285
|
+
this.currentCursor = Math.max(this.conversationStore.getCompressedMessageCount() - 1, cursor);
|
|
286
|
+
|
|
287
|
+
return { messages, mode: this.plan.mode, counts };
|
|
288
|
+
} catch (error) {
|
|
289
|
+
span.recordException(error as Error);
|
|
290
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
291
|
+
throw error;
|
|
292
|
+
} finally {
|
|
293
|
+
span.end();
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
advanceCursor(): void {
|
|
299
|
+
const sessionCapabilities = this.getProviderCapabilities();
|
|
300
|
+
const isStateful = sessionCapabilities?.sessionResumption === true;
|
|
301
|
+
|
|
302
|
+
if (!isStateful) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Use current message count (includes agent's response), not compile-time cursor.
|
|
307
|
+
// advanceCursor is called after agent responds, so we want the cursor to point
|
|
308
|
+
// to the last message the agent knows about (its own response).
|
|
309
|
+
// CRITICAL: Use compressed count to save cursor in compressed space.
|
|
310
|
+
const messageCount = this.conversationStore.getCompressedMessageCount();
|
|
311
|
+
const cursorToSave = messageCount - 1;
|
|
312
|
+
this.sessionManager.saveLastSentMessageIndex(cursorToSave);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private buildPlan(): MessageCompilationPlan {
|
|
316
|
+
const session = this.sessionManager.getSession();
|
|
317
|
+
const capabilities = this.getProviderCapabilities();
|
|
318
|
+
const isStateful = capabilities?.sessionResumption === true;
|
|
319
|
+
const hasSession = Boolean(session.sessionId);
|
|
320
|
+
const cursor = session.lastSentMessageIndex;
|
|
321
|
+
const cursorIsValid =
|
|
322
|
+
typeof cursor === "number" && cursor < this.conversationStore.getCompressedMessageCount();
|
|
323
|
+
|
|
324
|
+
if (isStateful && hasSession && cursorIsValid) {
|
|
325
|
+
return { mode: "delta", cursor };
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return { mode: "full" };
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
private getProviderCapabilities(): ProviderCapabilities | undefined {
|
|
332
|
+
const normalized = this.normalizeProviderId(this.providerId);
|
|
333
|
+
const provider = providerRegistry.getProvider(normalized);
|
|
334
|
+
if (provider) {
|
|
335
|
+
return provider.metadata.capabilities;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const registered = providerRegistry
|
|
339
|
+
.getRegisteredProviders()
|
|
340
|
+
.find((metadata) => metadata.id === normalized);
|
|
341
|
+
return registered?.capabilities;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
private normalizeProviderId(providerId: string): string {
|
|
345
|
+
const normalized = providerId.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
346
|
+
const registered = providerRegistry.getRegisteredProviders();
|
|
347
|
+
const matches = registered.some((metadata) => metadata.id === normalized);
|
|
348
|
+
|
|
349
|
+
return matches ? normalized : providerId;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Build dynamic context content for injection into the last user message.
|
|
354
|
+
* This includes todo state and response context.
|
|
355
|
+
*
|
|
356
|
+
* @returns Combined content string, or empty string if no content
|
|
357
|
+
*/
|
|
358
|
+
private async buildDynamicContextContent(
|
|
359
|
+
context: MessageCompilerContext
|
|
360
|
+
): Promise<string> {
|
|
361
|
+
const parts: string[] = [];
|
|
362
|
+
|
|
363
|
+
// Add todo content if present
|
|
364
|
+
const todoContent = await agentTodosFragment.template({
|
|
365
|
+
conversation: context.conversation,
|
|
366
|
+
agentPubkey: context.agent.pubkey,
|
|
367
|
+
});
|
|
368
|
+
if (todoContent) {
|
|
369
|
+
parts.push(todoContent);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Always add response context
|
|
373
|
+
const responseContextContent = await this.buildResponseContext(context);
|
|
374
|
+
parts.push(responseContextContent);
|
|
375
|
+
|
|
376
|
+
return parts.join("\n\n");
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
private async buildResponseContext(context: MessageCompilerContext): Promise<string> {
|
|
380
|
+
const pubkeyService = getPubkeyService();
|
|
381
|
+
const respondingToName = await pubkeyService.getName(context.respondingToPubkey);
|
|
382
|
+
let responseContextContent = `Your response will be sent to @${respondingToName}.`;
|
|
383
|
+
|
|
384
|
+
const allDelegatedPubkeys = [
|
|
385
|
+
...context.pendingDelegations.map((d) => d.recipientPubkey),
|
|
386
|
+
...context.completedDelegations.map((d) => d.recipientPubkey),
|
|
387
|
+
];
|
|
388
|
+
|
|
389
|
+
if (allDelegatedPubkeys.length > 0) {
|
|
390
|
+
const delegatedAgentNames = await Promise.all(
|
|
391
|
+
allDelegatedPubkeys.map((pk) => pubkeyService.getName(pk))
|
|
392
|
+
);
|
|
393
|
+
const uniqueNames = [...new Set(delegatedAgentNames)];
|
|
394
|
+
responseContextContent +=
|
|
395
|
+
`\nYou have delegations to: ${uniqueNames.map((n) => `@${n}`).join(", ")}.`;
|
|
396
|
+
responseContextContent +=
|
|
397
|
+
"\nIf you want to follow up with a delegated agent, use delegate_followup with the delegation ID. Do NOT address them directly in your response - they won't see it.";
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return responseContextContent;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Append ephemeral messages to the last user message as <system-reminder> tags.
|
|
405
|
+
*
|
|
406
|
+
* This method finds the last user message in the array and appends all ephemeral
|
|
407
|
+
* messages as system-reminder blocks. This unifies behavioral nudges (heuristic
|
|
408
|
+
* violations, deferred injections, supervision corrections) into a consistent format.
|
|
409
|
+
*
|
|
410
|
+
* Handles both string content and multimodal content (TextPart + ImagePart arrays).
|
|
411
|
+
* For multimodal messages, the reminder is appended to the last text part.
|
|
412
|
+
*
|
|
413
|
+
* If no user message exists, the ephemeral messages are added as a new user message.
|
|
414
|
+
*
|
|
415
|
+
* @param messages - The message array to modify in place
|
|
416
|
+
* @param ephemeralMessages - The ephemeral messages to append
|
|
417
|
+
*/
|
|
418
|
+
private appendEphemeralMessagesToLastUserMessage(
|
|
419
|
+
messages: ModelMessage[],
|
|
420
|
+
ephemeralMessages: EphemeralMessage[]
|
|
421
|
+
): void {
|
|
422
|
+
if (ephemeralMessages.length === 0) {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Collect all ephemeral content, extracting inner content from already-wrapped reminders
|
|
427
|
+
const reminderContents: string[] = [];
|
|
428
|
+
for (const ephemeral of ephemeralMessages) {
|
|
429
|
+
const content = ephemeral.content.trim();
|
|
430
|
+
if (content) {
|
|
431
|
+
// If content already has system-reminder tags, extract the inner content
|
|
432
|
+
// to avoid double-wrapping when we combine them
|
|
433
|
+
if (content.startsWith("<system-reminder>")) {
|
|
434
|
+
// Extract all system-reminder blocks from the content
|
|
435
|
+
const extracted = this.extractAllSystemReminderContents(content);
|
|
436
|
+
if (extracted.length > 0) {
|
|
437
|
+
reminderContents.push(...extracted);
|
|
438
|
+
} else {
|
|
439
|
+
// Fallback: use raw content if extraction fails
|
|
440
|
+
reminderContents.push(content);
|
|
441
|
+
}
|
|
442
|
+
} else {
|
|
443
|
+
reminderContents.push(content);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
if (reminderContents.length === 0) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Combine all contents into a single system-reminder block
|
|
453
|
+
const combinedReminder = combineSystemReminders(reminderContents);
|
|
454
|
+
|
|
455
|
+
// Find the last user message (searching from the end)
|
|
456
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
457
|
+
const msg = messages[i];
|
|
458
|
+
if (msg.role === "user") {
|
|
459
|
+
if (typeof msg.content === "string") {
|
|
460
|
+
// String content: append directly
|
|
461
|
+
messages[i] = {
|
|
462
|
+
...msg,
|
|
463
|
+
content: `${msg.content}\n\n${combinedReminder}`,
|
|
464
|
+
};
|
|
465
|
+
return;
|
|
466
|
+
} else if (Array.isArray(msg.content)) {
|
|
467
|
+
// Multimodal content: find the last text part and append to it
|
|
468
|
+
// We need to work with the array generically since content can be various part types
|
|
469
|
+
const contentArray = msg.content;
|
|
470
|
+
let lastTextIndex = -1;
|
|
471
|
+
|
|
472
|
+
// Find the last text part
|
|
473
|
+
for (let j = contentArray.length - 1; j >= 0; j--) {
|
|
474
|
+
const part = contentArray[j];
|
|
475
|
+
if (typeof part === "object" && part !== null && "type" in part &&
|
|
476
|
+
part.type === "text" && "text" in part && typeof part.text === "string") {
|
|
477
|
+
lastTextIndex = j;
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (lastTextIndex >= 0) {
|
|
483
|
+
// Append to the last text part
|
|
484
|
+
const textPart = contentArray[lastTextIndex] as TextPart;
|
|
485
|
+
const newTextPart: TextPart = {
|
|
486
|
+
type: "text",
|
|
487
|
+
text: `${textPart.text}\n\n${combinedReminder}`,
|
|
488
|
+
};
|
|
489
|
+
// Create new array with modified text part
|
|
490
|
+
const newContent = contentArray.map((part, idx) =>
|
|
491
|
+
idx === lastTextIndex ? newTextPart : part
|
|
492
|
+
);
|
|
493
|
+
messages[i] = {
|
|
494
|
+
...msg,
|
|
495
|
+
content: newContent,
|
|
496
|
+
};
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
// No text part found in multimodal - add a text part
|
|
500
|
+
const newTextPart: TextPart = { type: "text", text: combinedReminder };
|
|
501
|
+
messages[i] = {
|
|
502
|
+
...msg,
|
|
503
|
+
content: [...contentArray, newTextPart],
|
|
504
|
+
};
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// No user message found - add as a new user message
|
|
511
|
+
// This is a fallback; in practice there should always be a user message
|
|
512
|
+
logger.warn("[MessageCompiler] No user message found for ephemeral injection, adding as new message");
|
|
513
|
+
messages.push({
|
|
514
|
+
role: "user",
|
|
515
|
+
content: combinedReminder,
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Extract all content blocks from system-reminder tags.
|
|
521
|
+
* Handles multiple consecutive system-reminder blocks and trailing text.
|
|
522
|
+
*
|
|
523
|
+
* @param content - String potentially containing system-reminder tags
|
|
524
|
+
* @returns Array of extracted content blocks
|
|
525
|
+
*/
|
|
526
|
+
private extractAllSystemReminderContents(content: string): string[] {
|
|
527
|
+
const results: string[] = [];
|
|
528
|
+
const regex = /<system-reminder>\n?([\s\S]*?)\n?<\/system-reminder>/g;
|
|
529
|
+
let match: RegExpExecArray | null;
|
|
530
|
+
let lastIndex = 0;
|
|
531
|
+
|
|
532
|
+
while ((match = regex.exec(content)) !== null) {
|
|
533
|
+
// Check for text before this match (after the last match)
|
|
534
|
+
const beforeText = content.substring(lastIndex, match.index).trim();
|
|
535
|
+
if (beforeText) {
|
|
536
|
+
results.push(beforeText);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Add the inner content of this system-reminder block
|
|
540
|
+
const innerContent = match[1].trim();
|
|
541
|
+
if (innerContent) {
|
|
542
|
+
results.push(innerContent);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
lastIndex = regex.lastIndex;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Check for text after the last match
|
|
549
|
+
const afterText = content.substring(lastIndex).trim();
|
|
550
|
+
if (afterText) {
|
|
551
|
+
results.push(afterText);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
return results;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Apply reactive compression if needed.
|
|
559
|
+
* This ensures conversation history fits within the token budget.
|
|
560
|
+
*/
|
|
561
|
+
private async applyCompression(context: MessageCompilerContext): Promise<void> {
|
|
562
|
+
// Skip compression if LLMService not available
|
|
563
|
+
if (!this.llmService) {
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const compressionService = createCompressionService(
|
|
568
|
+
this.conversationStore,
|
|
569
|
+
this.llmService!,
|
|
570
|
+
this.compressionLlmService
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
// Get compression config with proper defaults (enabled: true by default)
|
|
574
|
+
// CRITICAL: Don't check config.compression?.enabled directly - it bypasses
|
|
575
|
+
// CompressionService defaults. Let CompressionService handle the enabled check.
|
|
576
|
+
const compressionConfig = compressionService.getCompressionConfig();
|
|
577
|
+
if (!compressionConfig.enabled) {
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// Apply reactive compression (sub-span removed - CompressionService has its own span when work is done)
|
|
582
|
+
try {
|
|
583
|
+
const tokenBudget = compressionConfig.tokenBudget;
|
|
584
|
+
await compressionService.ensureUnderLimit(
|
|
585
|
+
context.conversation.id,
|
|
586
|
+
tokenBudget
|
|
587
|
+
);
|
|
588
|
+
} catch (error) {
|
|
589
|
+
// Non-critical - log and continue without compression
|
|
590
|
+
logger.warn("Reactive compression failed", {
|
|
591
|
+
conversationId: context.conversation.id,
|
|
592
|
+
ralNumber: context.ralNumber,
|
|
593
|
+
error: error instanceof Error ? error.message : String(error),
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|