@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,515 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Home-scoped filesystem tools for restricted agents.
|
|
3
|
+
*
|
|
4
|
+
* These tools provide filesystem access ONLY within the agent's home directory.
|
|
5
|
+
* They are auto-injected for agents that don't have fs_* tools configured.
|
|
6
|
+
*
|
|
7
|
+
* Security: All paths are validated via resolveHomeScopedPath() which:
|
|
8
|
+
* - Resolves symlinks to prevent escape attacks
|
|
9
|
+
* - Validates path traversal (../) attempts
|
|
10
|
+
* - Supports both relative and absolute path inputs
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { exec } from "node:child_process";
|
|
14
|
+
import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
15
|
+
import { dirname, relative } from "node:path";
|
|
16
|
+
import { promisify } from "node:util";
|
|
17
|
+
import {
|
|
18
|
+
getAgentHomeDirectory,
|
|
19
|
+
HomeScopeViolationError,
|
|
20
|
+
resolveHomeScopedPath,
|
|
21
|
+
} from "@/lib/agent-home";
|
|
22
|
+
import { formatAnyError } from "@/lib/error-formatter";
|
|
23
|
+
import {
|
|
24
|
+
createExpectedError,
|
|
25
|
+
getFsErrorDescription,
|
|
26
|
+
isExpectedFsError,
|
|
27
|
+
} from "@/tools/utils";
|
|
28
|
+
import { tool } from "ai";
|
|
29
|
+
import { z } from "zod";
|
|
30
|
+
import type { AISdkTool, ToolExecutionContext } from "../types";
|
|
31
|
+
|
|
32
|
+
const execAsync = promisify(exec);
|
|
33
|
+
|
|
34
|
+
// Constants
|
|
35
|
+
const DEFAULT_LINE_LIMIT = 2000;
|
|
36
|
+
const MAX_LINE_LENGTH = 2000;
|
|
37
|
+
const MAX_GREP_CONTENT_SIZE = 50_000; // 50KB threshold
|
|
38
|
+
|
|
39
|
+
// ============================================================================
|
|
40
|
+
// home_fs_read
|
|
41
|
+
// ============================================================================
|
|
42
|
+
|
|
43
|
+
const homeReadSchema = z.object({
|
|
44
|
+
path: z
|
|
45
|
+
.string()
|
|
46
|
+
.describe(
|
|
47
|
+
"Path to the file or directory to read. Can be relative (to your home) or absolute (must be within your home). Example: 'notes.txt' or '+REMINDERS.md'"
|
|
48
|
+
),
|
|
49
|
+
description: z
|
|
50
|
+
.string()
|
|
51
|
+
.min(1, "Description is required and cannot be empty")
|
|
52
|
+
.describe(
|
|
53
|
+
"REQUIRED: A clear, concise description of why you're reading this file (5-10 words). Helps provide human-readable context for the operation."
|
|
54
|
+
),
|
|
55
|
+
offset: z
|
|
56
|
+
.number()
|
|
57
|
+
.min(1)
|
|
58
|
+
.optional()
|
|
59
|
+
.describe("Line number to start reading from (1-based). Defaults to line 1."),
|
|
60
|
+
limit: z
|
|
61
|
+
.number()
|
|
62
|
+
.min(1)
|
|
63
|
+
.optional()
|
|
64
|
+
.describe(`Maximum number of lines to read. Defaults to ${DEFAULT_LINE_LIMIT}.`),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
async function executeHomeRead(
|
|
68
|
+
path: string,
|
|
69
|
+
agentPubkey: string,
|
|
70
|
+
offset?: number,
|
|
71
|
+
limit?: number
|
|
72
|
+
): Promise<string> {
|
|
73
|
+
// Resolve and validate path is within home
|
|
74
|
+
const resolvedPath = resolveHomeScopedPath(path, agentPubkey);
|
|
75
|
+
|
|
76
|
+
const stats = await stat(resolvedPath);
|
|
77
|
+
|
|
78
|
+
if (stats.isDirectory()) {
|
|
79
|
+
const files = await readdir(resolvedPath);
|
|
80
|
+
const fileList = files.map((file) => ` - ${file}`).join("\n");
|
|
81
|
+
return `Directory listing for ${path}:\n${fileList}\n\nTo read a specific file, specify the filename.`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const rawContent = await readFile(resolvedPath, "utf-8");
|
|
85
|
+
const lines = rawContent.split("\n");
|
|
86
|
+
const totalLines = lines.length;
|
|
87
|
+
|
|
88
|
+
// 1-based offset, default to line 1
|
|
89
|
+
const startLine = offset ?? 1;
|
|
90
|
+
const startIndex = startLine - 1;
|
|
91
|
+
|
|
92
|
+
if (startIndex >= totalLines) {
|
|
93
|
+
return `File has only ${totalLines} line(s), but offset ${offset} was requested.`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Apply limit
|
|
97
|
+
const effectiveLimit = limit ?? DEFAULT_LINE_LIMIT;
|
|
98
|
+
const endIndex = Math.min(startIndex + effectiveLimit, totalLines);
|
|
99
|
+
const selectedLines = lines.slice(startIndex, endIndex);
|
|
100
|
+
|
|
101
|
+
// Format with line numbers and truncate long lines
|
|
102
|
+
const numberedLines = selectedLines
|
|
103
|
+
.map((line, idx) => {
|
|
104
|
+
const lineNum = startIndex + idx + 1;
|
|
105
|
+
const truncatedLine =
|
|
106
|
+
line.length > MAX_LINE_LENGTH ? line.slice(0, MAX_LINE_LENGTH) + "..." : line;
|
|
107
|
+
return `${lineNum.toString().padStart(6)}\t${truncatedLine}`;
|
|
108
|
+
})
|
|
109
|
+
.join("\n");
|
|
110
|
+
|
|
111
|
+
// Add info about truncation if we didn't read the whole file
|
|
112
|
+
const remainingLines = totalLines - endIndex;
|
|
113
|
+
if (remainingLines > 0) {
|
|
114
|
+
return `${numberedLines}\n\n[Showing lines ${startLine}-${endIndex} of ${totalLines}. ${remainingLines} more lines available. Use offset=${endIndex + 1} to continue.]`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return numberedLines;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function createHomeFsReadTool(context: ToolExecutionContext): AISdkTool {
|
|
121
|
+
const toolInstance = tool({
|
|
122
|
+
description:
|
|
123
|
+
`Read a file or directory from your home directory. This tool ONLY operates within your home directory. You cannot access files outside your home. ` +
|
|
124
|
+
`Returns file contents with line numbers (up to ${DEFAULT_LINE_LIMIT} lines). Use offset (1-based) and limit to paginate large files. ` +
|
|
125
|
+
`Paths can be relative (resolved against your home) or absolute (must be within your home).`,
|
|
126
|
+
|
|
127
|
+
inputSchema: homeReadSchema,
|
|
128
|
+
|
|
129
|
+
execute: async ({
|
|
130
|
+
path,
|
|
131
|
+
description: _description,
|
|
132
|
+
offset,
|
|
133
|
+
limit,
|
|
134
|
+
}: {
|
|
135
|
+
path: string;
|
|
136
|
+
description: string;
|
|
137
|
+
offset?: number;
|
|
138
|
+
limit?: number;
|
|
139
|
+
}) => {
|
|
140
|
+
try {
|
|
141
|
+
return await executeHomeRead(path, context.agent.pubkey, offset, limit);
|
|
142
|
+
} catch (error: unknown) {
|
|
143
|
+
// Home scope violations return friendly error message
|
|
144
|
+
if (error instanceof HomeScopeViolationError) {
|
|
145
|
+
return createExpectedError(error.message);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Expected FS errors (file not found, permission denied, etc.)
|
|
149
|
+
if (isExpectedFsError(error)) {
|
|
150
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
151
|
+
const description = getFsErrorDescription(code);
|
|
152
|
+
return createExpectedError(`${description}: ${path}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
throw new Error(`Failed to read ${path}: ${formatAnyError(error)}`, {
|
|
156
|
+
cause: error,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
Object.defineProperty(toolInstance, "getHumanReadableContent", {
|
|
163
|
+
value: ({ path, description }: { path: string; description: string }) => {
|
|
164
|
+
return `Reading ${path} (${description})`;
|
|
165
|
+
},
|
|
166
|
+
enumerable: false,
|
|
167
|
+
configurable: true,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
return toolInstance as AISdkTool;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ============================================================================
|
|
174
|
+
// home_fs_write
|
|
175
|
+
// ============================================================================
|
|
176
|
+
|
|
177
|
+
const homeWriteSchema = z.object({
|
|
178
|
+
path: z
|
|
179
|
+
.string()
|
|
180
|
+
.describe(
|
|
181
|
+
"Path to the file to write. Can be relative (to your home) or absolute (must be within your home). Parent directories are created automatically."
|
|
182
|
+
),
|
|
183
|
+
content: z.string().describe("The content to write to the file."),
|
|
184
|
+
description: z
|
|
185
|
+
.string()
|
|
186
|
+
.min(1, "Description is required and cannot be empty")
|
|
187
|
+
.describe(
|
|
188
|
+
"REQUIRED: A clear, concise description of why you're writing this file (5-10 words). Helps provide human-readable context for the operation."
|
|
189
|
+
),
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
async function executeHomeWrite(
|
|
193
|
+
path: string,
|
|
194
|
+
content: string,
|
|
195
|
+
agentPubkey: string
|
|
196
|
+
): Promise<string> {
|
|
197
|
+
// Resolve and validate path is within home
|
|
198
|
+
const resolvedPath = resolveHomeScopedPath(path, agentPubkey);
|
|
199
|
+
|
|
200
|
+
// Create parent directories if they don't exist
|
|
201
|
+
const parentDir = dirname(resolvedPath);
|
|
202
|
+
await mkdir(parentDir, { recursive: true });
|
|
203
|
+
|
|
204
|
+
// Write the file
|
|
205
|
+
await writeFile(resolvedPath, content, "utf-8");
|
|
206
|
+
|
|
207
|
+
return `Successfully wrote ${content.length} bytes to ${path}`;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function createHomeFsWriteTool(context: ToolExecutionContext): AISdkTool {
|
|
211
|
+
const toolInstance = tool({
|
|
212
|
+
description:
|
|
213
|
+
"Write content to a file in your home directory. This tool ONLY operates within your home directory. You cannot write files outside your home. " +
|
|
214
|
+
"Creates parent directories automatically if they don't exist. Overwrites existing files. " +
|
|
215
|
+
"Paths can be relative (resolved against your home) or absolute (must be within your home).",
|
|
216
|
+
|
|
217
|
+
inputSchema: homeWriteSchema,
|
|
218
|
+
|
|
219
|
+
execute: async ({ path, content, description: _description }: { path: string; content: string; description: string }) => {
|
|
220
|
+
try {
|
|
221
|
+
return await executeHomeWrite(path, content, context.agent.pubkey);
|
|
222
|
+
} catch (error: unknown) {
|
|
223
|
+
// Home scope violations return friendly error message
|
|
224
|
+
if (error instanceof HomeScopeViolationError) {
|
|
225
|
+
return createExpectedError(error.message);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Expected FS errors
|
|
229
|
+
if (isExpectedFsError(error)) {
|
|
230
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
231
|
+
const description = getFsErrorDescription(code);
|
|
232
|
+
return createExpectedError(`${description}: ${path}`);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
throw new Error(`Failed to write ${path}: ${formatAnyError(error)}`, {
|
|
236
|
+
cause: error,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
Object.defineProperty(toolInstance, "getHumanReadableContent", {
|
|
243
|
+
value: ({ path, description }: { path: string; description: string }) => {
|
|
244
|
+
return `Writing ${path} (${description})`;
|
|
245
|
+
},
|
|
246
|
+
enumerable: false,
|
|
247
|
+
configurable: true,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
return toolInstance as AISdkTool;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// ============================================================================
|
|
254
|
+
// home_fs_grep
|
|
255
|
+
// ============================================================================
|
|
256
|
+
|
|
257
|
+
const homeGrepSchema = z.object({
|
|
258
|
+
pattern: z
|
|
259
|
+
.string()
|
|
260
|
+
.describe("Regex pattern to search for in file contents (e.g., 'TODO', 'function\\s+\\w+')."),
|
|
261
|
+
description: z
|
|
262
|
+
.string()
|
|
263
|
+
.min(1, "Description is required and cannot be empty")
|
|
264
|
+
.describe(
|
|
265
|
+
"REQUIRED: A clear, concise description of why you're searching for this pattern (5-10 words). Helps provide human-readable context for the operation."
|
|
266
|
+
),
|
|
267
|
+
path: z
|
|
268
|
+
.string()
|
|
269
|
+
.optional()
|
|
270
|
+
.describe(
|
|
271
|
+
"Path within your home to search. Can be relative or absolute (must be within home). Defaults to your entire home directory."
|
|
272
|
+
),
|
|
273
|
+
output_mode: z
|
|
274
|
+
.enum(["files_with_matches", "content", "count"])
|
|
275
|
+
.default("files_with_matches")
|
|
276
|
+
.describe(
|
|
277
|
+
"Output mode: 'files_with_matches' (file paths only), 'content' (matching lines), 'count' (match counts per file)."
|
|
278
|
+
),
|
|
279
|
+
"-i": z.boolean().optional().describe("Case-insensitive search."),
|
|
280
|
+
head_limit: z
|
|
281
|
+
.number()
|
|
282
|
+
.default(100)
|
|
283
|
+
.describe("Limit output to first N entries. Use 0 for unlimited."),
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
type HomeGrepInput = z.infer<typeof homeGrepSchema>;
|
|
287
|
+
|
|
288
|
+
async function isRipgrepAvailable(): Promise<boolean> {
|
|
289
|
+
try {
|
|
290
|
+
await execAsync("which rg", { timeout: 1000 });
|
|
291
|
+
return true;
|
|
292
|
+
} catch {
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function buildHomeGrepCommand(
|
|
298
|
+
input: HomeGrepInput,
|
|
299
|
+
searchPath: string,
|
|
300
|
+
useRipgrep: boolean
|
|
301
|
+
): string {
|
|
302
|
+
const { pattern, output_mode, "-i": caseInsensitive } = input;
|
|
303
|
+
|
|
304
|
+
const parts: string[] = [];
|
|
305
|
+
|
|
306
|
+
if (useRipgrep) {
|
|
307
|
+
parts.push("rg");
|
|
308
|
+
if (output_mode === "files_with_matches") {
|
|
309
|
+
parts.push("-l");
|
|
310
|
+
} else if (output_mode === "count") {
|
|
311
|
+
parts.push("-c");
|
|
312
|
+
} else {
|
|
313
|
+
parts.push("-n"); // Line numbers for content mode
|
|
314
|
+
}
|
|
315
|
+
if (caseInsensitive) {
|
|
316
|
+
parts.push("-i");
|
|
317
|
+
}
|
|
318
|
+
} else {
|
|
319
|
+
parts.push("grep", "-r", "-E");
|
|
320
|
+
if (output_mode === "files_with_matches") {
|
|
321
|
+
parts.push("-l");
|
|
322
|
+
} else if (output_mode === "count") {
|
|
323
|
+
parts.push("-c");
|
|
324
|
+
} else {
|
|
325
|
+
parts.push("-n"); // Line numbers for content mode
|
|
326
|
+
}
|
|
327
|
+
if (caseInsensitive) {
|
|
328
|
+
parts.push("-i");
|
|
329
|
+
}
|
|
330
|
+
parts.push("--binary-files=without-match");
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Pattern (escape for shell)
|
|
334
|
+
parts.push(`'${pattern.replace(/'/g, "'\\''")}'`);
|
|
335
|
+
|
|
336
|
+
// Search path (escape single quotes to prevent shell injection)
|
|
337
|
+
parts.push(`'${searchPath.replace(/'/g, "'\\''")}'`);
|
|
338
|
+
|
|
339
|
+
return parts.join(" ");
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function applyPagination(lines: string[], limit: number): string[] {
|
|
343
|
+
return limit > 0 ? lines.slice(0, limit) : lines;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
async function runHomeGrepCommand(
|
|
347
|
+
input: HomeGrepInput,
|
|
348
|
+
homeDir: string,
|
|
349
|
+
searchPath: string
|
|
350
|
+
): Promise<string[]> {
|
|
351
|
+
const useRipgrep = await isRipgrepAvailable();
|
|
352
|
+
const command = buildHomeGrepCommand(input, searchPath, useRipgrep);
|
|
353
|
+
|
|
354
|
+
try {
|
|
355
|
+
const { stdout } = await execAsync(command, {
|
|
356
|
+
cwd: homeDir,
|
|
357
|
+
timeout: 30000,
|
|
358
|
+
maxBuffer: 1024 * 1024 * 10, // 10MB
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
if (!stdout.trim()) {
|
|
362
|
+
return [];
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return stdout.trim().split("\n").filter(Boolean);
|
|
366
|
+
} catch (error) {
|
|
367
|
+
// Exit code 1 from grep/rg means no matches - not an error
|
|
368
|
+
if (error && typeof error === "object" && "code" in error && error.code === 1) {
|
|
369
|
+
return [];
|
|
370
|
+
}
|
|
371
|
+
// maxBuffer error - rethrow to trigger fallback
|
|
372
|
+
if (error && typeof error === "object" && "message" in error &&
|
|
373
|
+
typeof error.message === "string" && error.message.includes("maxBuffer")) {
|
|
374
|
+
throw error;
|
|
375
|
+
}
|
|
376
|
+
throw error;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function processGrepLines(lines: string[], outputMode: string, homeDir: string): string[] {
|
|
381
|
+
return lines.map((line) => {
|
|
382
|
+
if (outputMode === "files_with_matches") {
|
|
383
|
+
return relative(homeDir, line);
|
|
384
|
+
} else if (outputMode === "count") {
|
|
385
|
+
const colonIdx = line.lastIndexOf(":");
|
|
386
|
+
if (colonIdx > 0) {
|
|
387
|
+
const filePath = line.substring(0, colonIdx);
|
|
388
|
+
const count = line.substring(colonIdx + 1);
|
|
389
|
+
return `${relative(homeDir, filePath)}:${count}`;
|
|
390
|
+
}
|
|
391
|
+
} else {
|
|
392
|
+
// Content mode: /path/to/file:line:content
|
|
393
|
+
const firstColon = line.indexOf(":");
|
|
394
|
+
if (firstColon > 0) {
|
|
395
|
+
const filePath = line.substring(0, firstColon);
|
|
396
|
+
const rest = line.substring(firstColon);
|
|
397
|
+
return `${relative(homeDir, filePath)}${rest}`;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return line;
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
async function executeHomeGrep(input: HomeGrepInput, agentPubkey: string): Promise<string> {
|
|
405
|
+
const { pattern, path: inputPath, output_mode, head_limit } = input;
|
|
406
|
+
const homeDir = getAgentHomeDirectory(agentPubkey);
|
|
407
|
+
|
|
408
|
+
if (!pattern) {
|
|
409
|
+
return "Error: pattern is required";
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Determine and validate search path
|
|
413
|
+
const searchPath = inputPath ? resolveHomeScopedPath(inputPath, agentPubkey) : homeDir;
|
|
414
|
+
|
|
415
|
+
try {
|
|
416
|
+
let lines: string[];
|
|
417
|
+
|
|
418
|
+
try {
|
|
419
|
+
lines = await runHomeGrepCommand(input, homeDir, searchPath);
|
|
420
|
+
} catch (error) {
|
|
421
|
+
// Handle maxBuffer error by falling back to files_with_matches mode
|
|
422
|
+
if (
|
|
423
|
+
output_mode === "content" &&
|
|
424
|
+
error &&
|
|
425
|
+
typeof error === "object" &&
|
|
426
|
+
"message" in error &&
|
|
427
|
+
typeof error.message === "string" &&
|
|
428
|
+
error.message.includes("maxBuffer")
|
|
429
|
+
) {
|
|
430
|
+
// Retry with files_with_matches mode
|
|
431
|
+
const fallbackInput = { ...input, output_mode: "files_with_matches" as const };
|
|
432
|
+
const fallbackLines = await runHomeGrepCommand(fallbackInput, homeDir, searchPath);
|
|
433
|
+
const fallbackProcessed = processGrepLines(fallbackLines, "files_with_matches", homeDir);
|
|
434
|
+
const fallbackPaginated = applyPagination(fallbackProcessed, head_limit);
|
|
435
|
+
|
|
436
|
+
return `Output exceeded buffer limit. Showing ${fallbackPaginated.length} matching files instead:\n\n${fallbackPaginated.join("\n")}`;
|
|
437
|
+
}
|
|
438
|
+
throw error;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (lines.length === 0) {
|
|
442
|
+
return `No matches found for pattern: ${pattern}`;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Convert absolute paths to relative (to home)
|
|
446
|
+
lines = processGrepLines(lines, output_mode, homeDir);
|
|
447
|
+
|
|
448
|
+
// Apply pagination
|
|
449
|
+
const paginatedLines = applyPagination(lines, head_limit);
|
|
450
|
+
const result = paginatedLines.join("\n");
|
|
451
|
+
|
|
452
|
+
// Check content size
|
|
453
|
+
if (output_mode === "content" && Buffer.byteLength(result, "utf8") > MAX_GREP_CONTENT_SIZE) {
|
|
454
|
+
// Fall back to files_with_matches
|
|
455
|
+
const uniquePaths = new Set<string>();
|
|
456
|
+
for (const line of lines) {
|
|
457
|
+
const firstColon = line.indexOf(":");
|
|
458
|
+
if (firstColon > 0) {
|
|
459
|
+
uniquePaths.add(line.substring(0, firstColon));
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
const fileList = Array.from(uniquePaths).slice(0, head_limit).join("\n");
|
|
463
|
+
return `Content output exceeded 50KB limit. Showing ${uniquePaths.size} matching files:\n\n${fileList}`;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const truncated = paginatedLines.length < lines.length;
|
|
467
|
+
if (truncated) {
|
|
468
|
+
return `${result}\n\n[Truncated: showing ${paginatedLines.length} of ${lines.length} results]`;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
return result;
|
|
472
|
+
} catch (error) {
|
|
473
|
+
// Exit code 1 from grep/rg means no matches - not an error
|
|
474
|
+
if (error && typeof error === "object" && "code" in error && error.code === 1) {
|
|
475
|
+
return `No matches found for pattern: ${pattern}`;
|
|
476
|
+
}
|
|
477
|
+
throw error;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
export function createHomeFsGrepTool(context: ToolExecutionContext): AISdkTool {
|
|
482
|
+
const toolInstance = tool({
|
|
483
|
+
description:
|
|
484
|
+
"Search for patterns in files within your home directory. This tool ONLY operates within your home directory. You cannot search files outside your home. " +
|
|
485
|
+
"Uses ripgrep (with grep fallback). Supports regex patterns. " +
|
|
486
|
+
"Output modes: 'files_with_matches' (default), 'content' (matching lines), 'count' (match counts).",
|
|
487
|
+
|
|
488
|
+
inputSchema: homeGrepSchema,
|
|
489
|
+
|
|
490
|
+
execute: async (input: HomeGrepInput) => {
|
|
491
|
+
try {
|
|
492
|
+
return await executeHomeGrep(input, context.agent.pubkey);
|
|
493
|
+
} catch (error: unknown) {
|
|
494
|
+
// Home scope violations return friendly error message
|
|
495
|
+
if (error instanceof HomeScopeViolationError) {
|
|
496
|
+
return createExpectedError(error.message);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
500
|
+
return `Search error: ${message}`;
|
|
501
|
+
}
|
|
502
|
+
},
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
Object.defineProperty(toolInstance, "getHumanReadableContent", {
|
|
506
|
+
value: (input: HomeGrepInput) => {
|
|
507
|
+
const pathInfo = input.path ? ` in ${input.path}` : " in home";
|
|
508
|
+
return `Searching for '${input.pattern}'${pathInfo} (${input.description})`;
|
|
509
|
+
},
|
|
510
|
+
enumerable: false,
|
|
511
|
+
configurable: true,
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
return toolInstance as AISdkTool;
|
|
515
|
+
}
|