@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,587 @@
|
|
|
1
|
+
import type { CompiledMessage } from "@/agents/execution/MessageCompiler";
|
|
2
|
+
import type { ConversationEntry } from "@/conversations/types";
|
|
3
|
+
import type {
|
|
4
|
+
CompressionSegment,
|
|
5
|
+
CompressionRange,
|
|
6
|
+
ValidationResult,
|
|
7
|
+
} from "./compression-types.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Estimate token count for messages using rough heuristic (chars/4).
|
|
11
|
+
* This is faster than actual tokenization and sufficient for compression decisions.
|
|
12
|
+
*
|
|
13
|
+
* @param messages - Messages to estimate
|
|
14
|
+
* @returns Estimated token count
|
|
15
|
+
*/
|
|
16
|
+
export function estimateTokens(
|
|
17
|
+
messages: CompiledMessage[]
|
|
18
|
+
): number {
|
|
19
|
+
const totalChars = messages.reduce((sum, msg) => {
|
|
20
|
+
const contentLength = typeof msg.content === "string"
|
|
21
|
+
? msg.content.length
|
|
22
|
+
: JSON.stringify(msg.content).length;
|
|
23
|
+
return sum + contentLength;
|
|
24
|
+
}, 0);
|
|
25
|
+
|
|
26
|
+
// Rough estimate: 1 token ≈ 4 characters
|
|
27
|
+
return Math.ceil(totalChars / 4);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Select a range of messages to compress.
|
|
32
|
+
* Strategy: Skip recent messages (keep context fresh), compress older middle section.
|
|
33
|
+
*
|
|
34
|
+
* @param messages - All messages in conversation
|
|
35
|
+
* @param lastSegment - Most recent compression segment (to avoid re-compressing)
|
|
36
|
+
* @returns Range to compress, or null if no suitable range found
|
|
37
|
+
*/
|
|
38
|
+
export function selectCandidateRange(
|
|
39
|
+
messages: CompiledMessage[],
|
|
40
|
+
lastSegment: CompressionSegment | null
|
|
41
|
+
): CompressionRange | null {
|
|
42
|
+
if (messages.length < 10) {
|
|
43
|
+
// Too few messages to compress meaningfully
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Keep last 20% of messages uncompressed (fresh context)
|
|
48
|
+
const keepRecentCount = Math.max(5, Math.floor(messages.length * 0.2));
|
|
49
|
+
const endIndex = messages.length - keepRecentCount;
|
|
50
|
+
|
|
51
|
+
// Find start index (after last compressed segment, or from beginning)
|
|
52
|
+
let startIndex = 0;
|
|
53
|
+
if (lastSegment) {
|
|
54
|
+
const lastCompressedIndex = messages.findIndex(
|
|
55
|
+
(m) => m.eventId === lastSegment.toEventId
|
|
56
|
+
);
|
|
57
|
+
if (lastCompressedIndex >= 0) {
|
|
58
|
+
startIndex = lastCompressedIndex + 1;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Need at least 5 messages to make compression worthwhile
|
|
63
|
+
if (endIndex - startIndex < 5) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return { startIndex, endIndex };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Validate that LLM-generated segments are valid for the given message range.
|
|
72
|
+
*
|
|
73
|
+
* Checks:
|
|
74
|
+
* 1. All event IDs exist in the message range
|
|
75
|
+
* 2. Segments are in chronological order
|
|
76
|
+
* 3. No gaps or overlaps between segments
|
|
77
|
+
* 4. First segment starts at range beginning
|
|
78
|
+
* 5. Last segment ends at range end
|
|
79
|
+
*
|
|
80
|
+
* @param segments - LLM-generated segments
|
|
81
|
+
* @param messages - All messages
|
|
82
|
+
* @param range - The range that was compressed
|
|
83
|
+
* @returns Validation result
|
|
84
|
+
*/
|
|
85
|
+
export function validateSegments(
|
|
86
|
+
segments: CompressionSegment[],
|
|
87
|
+
messages: CompiledMessage[],
|
|
88
|
+
range: CompressionRange
|
|
89
|
+
): ValidationResult {
|
|
90
|
+
if (segments.length === 0) {
|
|
91
|
+
return { valid: false, error: "No segments provided" };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const rangeMessages = messages.slice(range.startIndex, range.endIndex);
|
|
95
|
+
const messagesWithEventIds = rangeMessages.filter((m) => m.eventId);
|
|
96
|
+
|
|
97
|
+
if (messagesWithEventIds.length === 0) {
|
|
98
|
+
return { valid: false, error: "No messages with eventIds in range" };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const eventIds = new Set(messagesWithEventIds.map((m) => m.eventId!));
|
|
102
|
+
|
|
103
|
+
// Check all event IDs exist
|
|
104
|
+
for (const segment of segments) {
|
|
105
|
+
if (!eventIds.has(segment.fromEventId)) {
|
|
106
|
+
return {
|
|
107
|
+
valid: false,
|
|
108
|
+
error: `fromEventId ${segment.fromEventId} not found in range`,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (!eventIds.has(segment.toEventId)) {
|
|
112
|
+
return {
|
|
113
|
+
valid: false,
|
|
114
|
+
error: `toEventId ${segment.toEventId} not found in range`,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Check that first segment starts at range start
|
|
120
|
+
const firstMessageWithEventId = messagesWithEventIds[0];
|
|
121
|
+
const firstSegment = segments[0];
|
|
122
|
+
if (firstSegment.fromEventId !== firstMessageWithEventId.eventId) {
|
|
123
|
+
return {
|
|
124
|
+
valid: false,
|
|
125
|
+
error: `First segment must start at range beginning (expected ${firstMessageWithEventId.eventId}, got ${firstSegment.fromEventId})`,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Check that last segment ends at range end
|
|
130
|
+
const lastMessageWithEventId = messagesWithEventIds[messagesWithEventIds.length - 1];
|
|
131
|
+
const lastSegment = segments[segments.length - 1];
|
|
132
|
+
if (lastSegment.toEventId !== lastMessageWithEventId.eventId) {
|
|
133
|
+
return {
|
|
134
|
+
valid: false,
|
|
135
|
+
error: `Last segment must end at range end (expected ${lastMessageWithEventId.eventId}, got ${lastSegment.toEventId})`,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Check chronological order and no gaps
|
|
140
|
+
for (let i = 0; i < segments.length; i++) {
|
|
141
|
+
const segment = segments[i];
|
|
142
|
+
const fromIndex = rangeMessages.findIndex(
|
|
143
|
+
(m) => m.eventId === segment.fromEventId
|
|
144
|
+
);
|
|
145
|
+
const toIndex = rangeMessages.findIndex(
|
|
146
|
+
(m) => m.eventId === segment.toEventId
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
if (fromIndex > toIndex) {
|
|
150
|
+
return {
|
|
151
|
+
valid: false,
|
|
152
|
+
error: `Segment ${i}: fromEventId comes after toEventId`,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Check for gaps between segments
|
|
157
|
+
if (i > 0) {
|
|
158
|
+
const prevSegment = segments[i - 1];
|
|
159
|
+
const prevToIndex = rangeMessages.findIndex(
|
|
160
|
+
(m) => m.eventId === prevSegment.toEventId
|
|
161
|
+
);
|
|
162
|
+
if (fromIndex !== prevToIndex + 1) {
|
|
163
|
+
return {
|
|
164
|
+
valid: false,
|
|
165
|
+
error: `Gap between segment ${i - 1} and ${i}`,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return { valid: true };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Validate that LLM-generated segments are valid for the given entry range.
|
|
176
|
+
* This is the ConversationEntry version of validateSegments.
|
|
177
|
+
*
|
|
178
|
+
* Checks:
|
|
179
|
+
* 1. All event IDs exist in the entry range
|
|
180
|
+
* 2. Segments are in chronological order
|
|
181
|
+
* 3. No gaps or overlaps between segments
|
|
182
|
+
* 4. First segment starts at range beginning
|
|
183
|
+
* 5. Last segment ends at range end
|
|
184
|
+
*
|
|
185
|
+
* @param segments - LLM-generated segments
|
|
186
|
+
* @param entries - All entries
|
|
187
|
+
* @param range - The range that was compressed
|
|
188
|
+
* @returns Validation result
|
|
189
|
+
*/
|
|
190
|
+
export function validateSegmentsForEntries(
|
|
191
|
+
segments: CompressionSegment[],
|
|
192
|
+
entries: ConversationEntry[],
|
|
193
|
+
range: CompressionRange
|
|
194
|
+
): ValidationResult {
|
|
195
|
+
if (segments.length === 0) {
|
|
196
|
+
return { valid: false, error: "No segments provided" };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const rangeEntries = entries.slice(range.startIndex, range.endIndex);
|
|
200
|
+
const entriesWithEventIds = rangeEntries.filter((e) => e.eventId);
|
|
201
|
+
|
|
202
|
+
if (entriesWithEventIds.length === 0) {
|
|
203
|
+
return { valid: false, error: "No entries with eventIds in range" };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const eventIds = new Set(entriesWithEventIds.map((e) => e.eventId!));
|
|
207
|
+
|
|
208
|
+
// Check all event IDs exist
|
|
209
|
+
for (const segment of segments) {
|
|
210
|
+
if (!eventIds.has(segment.fromEventId)) {
|
|
211
|
+
return {
|
|
212
|
+
valid: false,
|
|
213
|
+
error: `fromEventId ${segment.fromEventId} not found in range`,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
if (!eventIds.has(segment.toEventId)) {
|
|
217
|
+
return {
|
|
218
|
+
valid: false,
|
|
219
|
+
error: `toEventId ${segment.toEventId} not found in range`,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Check that first segment starts at range start
|
|
225
|
+
const firstEntryWithEventId = entriesWithEventIds[0];
|
|
226
|
+
const firstSegment = segments[0];
|
|
227
|
+
if (firstSegment.fromEventId !== firstEntryWithEventId.eventId) {
|
|
228
|
+
return {
|
|
229
|
+
valid: false,
|
|
230
|
+
error: `First segment must start at range beginning (expected ${firstEntryWithEventId.eventId}, got ${firstSegment.fromEventId})`,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Check that last segment ends at range end
|
|
235
|
+
const lastEntryWithEventId = entriesWithEventIds[entriesWithEventIds.length - 1];
|
|
236
|
+
const lastSegment = segments[segments.length - 1];
|
|
237
|
+
if (lastSegment.toEventId !== lastEntryWithEventId.eventId) {
|
|
238
|
+
return {
|
|
239
|
+
valid: false,
|
|
240
|
+
error: `Last segment must end at range end (expected ${lastEntryWithEventId.eventId}, got ${lastSegment.toEventId})`,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Check chronological order and no gaps
|
|
245
|
+
for (let i = 0; i < segments.length; i++) {
|
|
246
|
+
const segment = segments[i];
|
|
247
|
+
const fromIndex = rangeEntries.findIndex(
|
|
248
|
+
(e) => e.eventId === segment.fromEventId
|
|
249
|
+
);
|
|
250
|
+
const toIndex = rangeEntries.findIndex(
|
|
251
|
+
(e) => e.eventId === segment.toEventId
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
if (fromIndex > toIndex) {
|
|
255
|
+
return {
|
|
256
|
+
valid: false,
|
|
257
|
+
error: `Segment ${i}: fromEventId comes after toEventId`,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Check for gaps between segments
|
|
262
|
+
if (i > 0) {
|
|
263
|
+
const prevSegment = segments[i - 1];
|
|
264
|
+
const prevToIndex = rangeEntries.findIndex(
|
|
265
|
+
(e) => e.eventId === prevSegment.toEventId
|
|
266
|
+
);
|
|
267
|
+
if (fromIndex !== prevToIndex + 1) {
|
|
268
|
+
return {
|
|
269
|
+
valid: false,
|
|
270
|
+
error: `Gap between segment ${i - 1} and ${i}`,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return { valid: true };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Apply compression segments to messages, replacing ranges with summaries.
|
|
281
|
+
*
|
|
282
|
+
* Creates new "system" role messages with compressed content.
|
|
283
|
+
* Preserves all other messages unchanged.
|
|
284
|
+
*
|
|
285
|
+
* @param messages - Original messages
|
|
286
|
+
* @param segments - Compression segments to apply
|
|
287
|
+
* @returns New message array with compressions applied
|
|
288
|
+
*/
|
|
289
|
+
export function applySegments(
|
|
290
|
+
messages: CompiledMessage[],
|
|
291
|
+
segments: CompressionSegment[]
|
|
292
|
+
): CompiledMessage[] {
|
|
293
|
+
if (segments.length === 0) {
|
|
294
|
+
return messages;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const result: CompiledMessage[] = [];
|
|
298
|
+
let currentIndex = 0;
|
|
299
|
+
|
|
300
|
+
for (const segment of segments) {
|
|
301
|
+
const fromIndex = messages.findIndex(
|
|
302
|
+
(m) => m.eventId === segment.fromEventId
|
|
303
|
+
);
|
|
304
|
+
const toIndex = messages.findIndex((m) => m.eventId === segment.toEventId);
|
|
305
|
+
|
|
306
|
+
if (fromIndex < 0 || toIndex < 0) {
|
|
307
|
+
// Segment doesn't match current messages, skip it
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Add messages before this segment
|
|
312
|
+
while (currentIndex < fromIndex) {
|
|
313
|
+
result.push(messages[currentIndex]);
|
|
314
|
+
currentIndex++;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Add compressed summary as system message
|
|
318
|
+
result.push({
|
|
319
|
+
role: "system",
|
|
320
|
+
content: `[Compressed history]\n${segment.compressed}`,
|
|
321
|
+
eventId: `compressed-${segment.fromEventId}-${segment.toEventId}`,
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
// Skip the compressed range
|
|
325
|
+
currentIndex = toIndex + 1;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Add remaining messages after last segment
|
|
329
|
+
while (currentIndex < messages.length) {
|
|
330
|
+
result.push(messages[currentIndex]);
|
|
331
|
+
currentIndex++;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return result;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Emergency fallback: truncate to sliding window of recent messages.
|
|
339
|
+
* Used when LLM compression fails or is unavailable.
|
|
340
|
+
*
|
|
341
|
+
* NOTE: This utility is currently unused in production but maintained as part of
|
|
342
|
+
* the public API for future use cases (e.g., client-side compression, direct
|
|
343
|
+
* truncation without segment creation). See createFallbackSegmentForEntries for
|
|
344
|
+
* the production fallback implementation.
|
|
345
|
+
*
|
|
346
|
+
* @param messages - All messages
|
|
347
|
+
* @param windowSize - Number of recent messages to keep
|
|
348
|
+
* @param currentTimestamp - Current timestamp (for deterministic testing)
|
|
349
|
+
* @returns Truncated message array
|
|
350
|
+
*/
|
|
351
|
+
export function truncateSlidingWindow(
|
|
352
|
+
messages: CompiledMessage[],
|
|
353
|
+
windowSize: number,
|
|
354
|
+
currentTimestamp: number
|
|
355
|
+
): CompiledMessage[] {
|
|
356
|
+
if (messages.length <= windowSize) {
|
|
357
|
+
return messages;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const kept = messages.slice(-windowSize);
|
|
361
|
+
|
|
362
|
+
// Add a system message at the start indicating truncation
|
|
363
|
+
return [
|
|
364
|
+
{
|
|
365
|
+
role: "system",
|
|
366
|
+
content: `[Earlier messages truncated. Showing last ${windowSize} messages.]`,
|
|
367
|
+
eventId: `truncated-${currentTimestamp}`,
|
|
368
|
+
},
|
|
369
|
+
...kept,
|
|
370
|
+
];
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Apply compression segments to conversation entries, replacing ranges with summaries.
|
|
375
|
+
* This is the ConversationEntry version of applySegments.
|
|
376
|
+
*
|
|
377
|
+
* Creates new "system" entries with compressed content.
|
|
378
|
+
* Preserves all other entries unchanged.
|
|
379
|
+
*
|
|
380
|
+
* @param entries - Original entries
|
|
381
|
+
* @param segments - Compression segments to apply
|
|
382
|
+
* @returns New entry array with compressions applied
|
|
383
|
+
*/
|
|
384
|
+
export function applySegmentsToEntries(
|
|
385
|
+
entries: ConversationEntry[],
|
|
386
|
+
segments: CompressionSegment[]
|
|
387
|
+
): ConversationEntry[] {
|
|
388
|
+
if (segments.length === 0) {
|
|
389
|
+
return entries;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const result: ConversationEntry[] = [];
|
|
393
|
+
let currentIndex = 0;
|
|
394
|
+
|
|
395
|
+
for (const segment of segments) {
|
|
396
|
+
const fromIndex = entries.findIndex((e) => e.eventId === segment.fromEventId);
|
|
397
|
+
const toIndex = entries.findIndex((e) => e.eventId === segment.toEventId);
|
|
398
|
+
|
|
399
|
+
if (fromIndex < 0 || toIndex < 0) {
|
|
400
|
+
// Segment doesn't match current entries, skip it
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Add entries before this segment
|
|
405
|
+
while (currentIndex < fromIndex) {
|
|
406
|
+
result.push(entries[currentIndex]);
|
|
407
|
+
currentIndex++;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Add compressed summary as system entry with explicit role
|
|
411
|
+
// CRITICAL: role field prevents deriveRole from misclassifying as "user"
|
|
412
|
+
result.push({
|
|
413
|
+
pubkey: "system",
|
|
414
|
+
content: `[Compressed history]\n${segment.compressed}`,
|
|
415
|
+
messageType: "text",
|
|
416
|
+
eventId: `compressed-${segment.fromEventId}-${segment.toEventId}`,
|
|
417
|
+
timestamp: segment.createdAt / 1000,
|
|
418
|
+
role: "system",
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
// Skip the compressed range
|
|
422
|
+
currentIndex = toIndex + 1;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Add remaining entries after last segment
|
|
426
|
+
while (currentIndex < entries.length) {
|
|
427
|
+
result.push(entries[currentIndex]);
|
|
428
|
+
currentIndex++;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return result;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Estimate token count for conversation entries using rough heuristic (chars/4).
|
|
436
|
+
* This is faster than actual tokenization and sufficient for compression decisions.
|
|
437
|
+
*
|
|
438
|
+
* Accounts for both text content and tool payloads (toolData) to prevent
|
|
439
|
+
* tool-heavy conversations from bypassing compression thresholds.
|
|
440
|
+
*
|
|
441
|
+
* @param entries - Entries to estimate
|
|
442
|
+
* @returns Estimated token count
|
|
443
|
+
*/
|
|
444
|
+
export function estimateTokensFromEntries(entries: ConversationEntry[]): number {
|
|
445
|
+
const totalChars = entries.reduce((sum, entry) => {
|
|
446
|
+
let entryChars = entry.content.length;
|
|
447
|
+
|
|
448
|
+
// Account for tool payloads (tool-call/tool-result)
|
|
449
|
+
if (entry.toolData && entry.toolData.length > 0) {
|
|
450
|
+
const toolDataSize = JSON.stringify(entry.toolData).length;
|
|
451
|
+
entryChars += toolDataSize;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return sum + entryChars;
|
|
455
|
+
}, 0);
|
|
456
|
+
return Math.ceil(totalChars / 4);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Select a range of entries to compress.
|
|
461
|
+
* Strategy: Skip recent entries (keep context fresh), compress older middle section.
|
|
462
|
+
*
|
|
463
|
+
* @param entries - All entries in conversation
|
|
464
|
+
* @param lastSegment - Most recent compression segment (to avoid re-compressing)
|
|
465
|
+
* @returns Range to compress, or null if no suitable range found
|
|
466
|
+
*/
|
|
467
|
+
export function selectCandidateRangeFromEntries(
|
|
468
|
+
entries: ConversationEntry[],
|
|
469
|
+
lastSegment: CompressionSegment | null
|
|
470
|
+
): CompressionRange | null {
|
|
471
|
+
if (entries.length < 10) {
|
|
472
|
+
// Too few entries to compress meaningfully
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Keep last 20% of entries uncompressed (fresh context)
|
|
477
|
+
const keepRecentCount = Math.max(5, Math.floor(entries.length * 0.2));
|
|
478
|
+
const endIndex = entries.length - keepRecentCount;
|
|
479
|
+
|
|
480
|
+
// Find start index (after last compressed segment, or from beginning)
|
|
481
|
+
let startIndex = 0;
|
|
482
|
+
if (lastSegment) {
|
|
483
|
+
const lastCompressedIndex = entries.findIndex(
|
|
484
|
+
(e) => e.eventId === lastSegment.toEventId
|
|
485
|
+
);
|
|
486
|
+
if (lastCompressedIndex >= 0) {
|
|
487
|
+
startIndex = lastCompressedIndex + 1;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Need at least 5 entries to make compression worthwhile
|
|
492
|
+
if (endIndex - startIndex < 5) {
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return { startIndex, endIndex };
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Emergency fallback: truncate entries to sliding window.
|
|
501
|
+
* This is the ConversationEntry version of truncateSlidingWindow.
|
|
502
|
+
*
|
|
503
|
+
* NOTE: This utility is currently unused in production but maintained as part of
|
|
504
|
+
* the public API for future use cases (e.g., direct truncation without segment
|
|
505
|
+
* creation). See createFallbackSegmentForEntries for the production fallback.
|
|
506
|
+
*
|
|
507
|
+
* @param entries - All entries
|
|
508
|
+
* @param windowSize - Number of recent entries to keep
|
|
509
|
+
* @param currentTimestamp - Current timestamp (for deterministic testing)
|
|
510
|
+
* @returns Truncated entry array
|
|
511
|
+
*/
|
|
512
|
+
export function truncateSlidingWindowEntries(
|
|
513
|
+
entries: ConversationEntry[],
|
|
514
|
+
windowSize: number,
|
|
515
|
+
currentTimestamp: number
|
|
516
|
+
): ConversationEntry[] {
|
|
517
|
+
if (entries.length <= windowSize) {
|
|
518
|
+
return entries;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
const kept = entries.slice(-windowSize);
|
|
522
|
+
|
|
523
|
+
// Add a synthetic entry indicating truncation
|
|
524
|
+
const truncationEntry: ConversationEntry = {
|
|
525
|
+
pubkey: "system",
|
|
526
|
+
content: `[Earlier messages truncated. Showing last ${windowSize} messages.]`,
|
|
527
|
+
messageType: "text",
|
|
528
|
+
timestamp: currentTimestamp / 1000,
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
return [truncationEntry, ...kept];
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Create a fallback compression segment for entries using index-based approach.
|
|
536
|
+
* Works even when entries lack eventIds by finding the nearest entries with eventIds.
|
|
537
|
+
*
|
|
538
|
+
* @param entries - All entries in conversation
|
|
539
|
+
* @param windowSize - Number of recent entries to keep uncompressed
|
|
540
|
+
* @returns Fallback segment or null if cannot create one
|
|
541
|
+
*/
|
|
542
|
+
export function createFallbackSegmentForEntries(
|
|
543
|
+
entries: ConversationEntry[],
|
|
544
|
+
windowSize: number
|
|
545
|
+
): CompressionSegment | null {
|
|
546
|
+
if (entries.length <= windowSize) {
|
|
547
|
+
// Nothing to compress
|
|
548
|
+
return null;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Find entries with eventIds
|
|
552
|
+
const entriesWithEventIds = entries
|
|
553
|
+
.map((e, index) => ({ entry: e, index }))
|
|
554
|
+
.filter((item) => item.entry.eventId);
|
|
555
|
+
|
|
556
|
+
if (entriesWithEventIds.length < 2) {
|
|
557
|
+
// Need at least 2 event IDs to create a valid segment
|
|
558
|
+
return null;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// Calculate how many entries to compress (before sliding window)
|
|
562
|
+
const truncateCount = entries.length - windowSize;
|
|
563
|
+
|
|
564
|
+
// Find the first entry with an eventId
|
|
565
|
+
const fromEventId = entriesWithEventIds[0].entry.eventId!;
|
|
566
|
+
|
|
567
|
+
// Find the last entry with eventId in the range to compress
|
|
568
|
+
// Use the entry at or before truncateCount-1
|
|
569
|
+
let toEntry = entriesWithEventIds[0];
|
|
570
|
+
for (const item of entriesWithEventIds) {
|
|
571
|
+
if (item.index < truncateCount) {
|
|
572
|
+
toEntry = item;
|
|
573
|
+
} else {
|
|
574
|
+
break;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const toEventId = toEntry.entry.eventId!;
|
|
579
|
+
|
|
580
|
+
return {
|
|
581
|
+
fromEventId,
|
|
582
|
+
toEventId,
|
|
583
|
+
compressed: `[Truncated ${truncateCount} earlier messages due to compression failure]`,
|
|
584
|
+
createdAt: Date.now(),
|
|
585
|
+
model: "fallback-truncation",
|
|
586
|
+
};
|
|
587
|
+
}
|