@umsai/ums-code 0.3.0-v1 → 0.5.0-v1
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/dist/package.json +4 -4
- package/dist/src/{zed-integration → acp-integration}/acp.d.ts +7 -0
- package/dist/src/{zed-integration → acp-integration}/acp.js +25 -0
- package/dist/src/acp-integration/acp.js.map +1 -0
- package/dist/src/acp-integration/acpAgent.d.ts +10 -0
- package/dist/src/acp-integration/acpAgent.js +238 -0
- package/dist/src/acp-integration/acpAgent.js.map +1 -0
- package/dist/src/{zed-integration → acp-integration}/schema.d.ts +1030 -43
- package/dist/src/{zed-integration → acp-integration}/schema.js +81 -2
- package/dist/src/acp-integration/schema.js.map +1 -0
- package/dist/src/{zed-integration/fileSystemService.d.ts → acp-integration/service/filesystem.d.ts} +1 -1
- package/dist/src/{zed-integration/fileSystemService.js → acp-integration/service/filesystem.js} +14 -1
- package/dist/src/acp-integration/service/filesystem.js.map +1 -0
- package/dist/src/acp-integration/service/filesystem.test.d.ts +6 -0
- package/dist/src/acp-integration/service/filesystem.test.js +39 -0
- package/dist/src/acp-integration/service/filesystem.test.js.map +1 -0
- package/dist/src/acp-integration/session/HistoryReplayer.d.ts +51 -0
- package/dist/src/acp-integration/session/HistoryReplayer.js +164 -0
- package/dist/src/acp-integration/session/HistoryReplayer.js.map +1 -0
- package/dist/src/acp-integration/session/HistoryReplayer.test.d.ts +6 -0
- package/dist/src/acp-integration/session/HistoryReplayer.test.js +374 -0
- package/dist/src/acp-integration/session/HistoryReplayer.test.js.map +1 -0
- package/dist/src/acp-integration/session/Session.d.ts +66 -0
- package/dist/src/acp-integration/session/Session.js +760 -0
- package/dist/src/acp-integration/session/Session.js.map +1 -0
- package/dist/src/acp-integration/session/SubAgentTracker.d.ts +51 -0
- package/dist/src/acp-integration/session/SubAgentTracker.js +257 -0
- package/dist/src/acp-integration/session/SubAgentTracker.js.map +1 -0
- package/dist/src/acp-integration/session/SubAgentTracker.test.d.ts +6 -0
- package/dist/src/acp-integration/session/SubAgentTracker.test.js +369 -0
- package/dist/src/acp-integration/session/SubAgentTracker.test.js.map +1 -0
- package/dist/src/acp-integration/session/emitters/BaseEmitter.d.ts +27 -0
- package/dist/src/acp-integration/session/emitters/BaseEmitter.js +34 -0
- package/dist/src/acp-integration/session/emitters/BaseEmitter.js.map +1 -0
- package/dist/src/acp-integration/session/emitters/MessageEmitter.d.ts +41 -0
- package/dist/src/acp-integration/session/emitters/MessageEmitter.js +77 -0
- package/dist/src/acp-integration/session/emitters/MessageEmitter.js.map +1 -0
- package/dist/src/acp-integration/session/emitters/MessageEmitter.test.d.ts +6 -0
- package/dist/src/acp-integration/session/emitters/MessageEmitter.test.js +174 -0
- package/dist/src/acp-integration/session/emitters/MessageEmitter.test.js.map +1 -0
- package/dist/src/acp-integration/session/emitters/PlanEmitter.d.ts +39 -0
- package/dist/src/acp-integration/session/emitters/PlanEmitter.js +83 -0
- package/dist/src/acp-integration/session/emitters/PlanEmitter.js.map +1 -0
- package/dist/src/acp-integration/session/emitters/PlanEmitter.test.d.ts +6 -0
- package/dist/src/acp-integration/session/emitters/PlanEmitter.test.js +176 -0
- package/dist/src/acp-integration/session/emitters/PlanEmitter.test.js.map +1 -0
- package/dist/src/acp-integration/session/emitters/ToolCallEmitter.d.ts +80 -0
- package/dist/src/acp-integration/session/emitters/ToolCallEmitter.js +248 -0
- package/dist/src/acp-integration/session/emitters/ToolCallEmitter.js.map +1 -0
- package/dist/src/acp-integration/session/emitters/ToolCallEmitter.test.d.ts +6 -0
- package/dist/src/acp-integration/session/emitters/ToolCallEmitter.test.js +561 -0
- package/dist/src/acp-integration/session/emitters/ToolCallEmitter.test.js.map +1 -0
- package/dist/src/acp-integration/session/emitters/index.d.ts +9 -0
- package/dist/src/acp-integration/session/emitters/index.js +10 -0
- package/dist/src/acp-integration/session/emitters/index.js.map +1 -0
- package/dist/src/acp-integration/session/index.d.ts +24 -0
- package/dist/src/acp-integration/session/index.js +16 -0
- package/dist/src/acp-integration/session/index.js.map +1 -0
- package/dist/src/acp-integration/session/types.d.ts +71 -0
- package/dist/src/acp-integration/session/types.js +7 -0
- package/dist/src/acp-integration/session/types.js.map +1 -0
- package/dist/src/config/auth.d.ts +1 -0
- package/dist/src/config/auth.js +3 -0
- package/dist/src/config/auth.js.map +1 -1
- package/dist/src/config/config.d.ts +11 -3
- package/dist/src/config/config.js +84 -8
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/extension.js +0 -2
- package/dist/src/config/extension.js.map +1 -1
- package/dist/src/config/settingsSchema.d.ts +12 -0
- package/dist/src/config/settingsSchema.js +10 -0
- package/dist/src/config/settingsSchema.js.map +1 -1
- package/dist/src/core/auth.d.ts +1 -1
- package/dist/src/core/auth.js +3 -2
- package/dist/src/core/auth.js.map +1 -1
- package/dist/src/gemini.js +38 -18
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/gemini.test.js +7 -0
- package/dist/src/gemini.test.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/i18n/index.d.ts +1 -1
- package/dist/src/i18n/index.js +4 -0
- package/dist/src/i18n/index.js.map +1 -1
- package/dist/src/i18n/locales/en.js +1 -13
- package/dist/src/i18n/locales/ru.js +1121 -0
- package/dist/src/i18n/locales/zh.js +1 -10
- package/dist/src/nonInteractive/control/ControlDispatcher.d.ts +24 -1
- package/dist/src/nonInteractive/control/ControlDispatcher.js +46 -17
- package/dist/src/nonInteractive/control/ControlDispatcher.js.map +1 -1
- package/dist/src/nonInteractive/control/ControlService.d.ts +2 -1
- package/dist/src/nonInteractive/control/ControlService.js +22 -37
- package/dist/src/nonInteractive/control/ControlService.js.map +1 -1
- package/dist/src/nonInteractive/control/controllers/baseController.d.ts +2 -1
- package/dist/src/nonInteractive/control/controllers/baseController.js +38 -5
- package/dist/src/nonInteractive/control/controllers/baseController.js.map +1 -1
- package/dist/src/nonInteractive/control/controllers/permissionController.d.ts +1 -22
- package/dist/src/nonInteractive/control/controllers/permissionController.js +38 -38
- package/dist/src/nonInteractive/control/controllers/permissionController.js.map +1 -1
- package/dist/src/nonInteractive/control/controllers/sdkMcpController.d.ts +54 -0
- package/dist/src/nonInteractive/control/controllers/sdkMcpController.js +84 -0
- package/dist/src/nonInteractive/control/controllers/sdkMcpController.js.map +1 -0
- package/dist/src/nonInteractive/control/controllers/systemController.d.ts +16 -6
- package/dist/src/nonInteractive/control/controllers/systemController.js +189 -44
- package/dist/src/nonInteractive/control/controllers/systemController.js.map +1 -1
- package/dist/src/nonInteractive/control/types/serviceAPIs.d.ts +1 -16
- package/dist/src/nonInteractive/io/BaseJsonOutputAdapter.d.ts +11 -0
- package/dist/src/nonInteractive/io/BaseJsonOutputAdapter.js +54 -2
- package/dist/src/nonInteractive/io/BaseJsonOutputAdapter.js.map +1 -1
- package/dist/src/nonInteractive/session.d.ts +0 -16
- package/dist/src/nonInteractive/session.js +317 -321
- package/dist/src/nonInteractive/session.js.map +1 -1
- package/dist/src/nonInteractive/session.test.js +6 -0
- package/dist/src/nonInteractive/session.test.js.map +1 -1
- package/dist/src/nonInteractive/types.d.ts +57 -3
- package/dist/src/nonInteractive/types.js +0 -1
- package/dist/src/nonInteractive/types.js.map +1 -1
- package/dist/src/nonInteractiveCli.js +25 -46
- package/dist/src/nonInteractiveCli.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.js +5 -6
- package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.test.js +0 -3
- package/dist/src/services/BuiltinCommandLoader.test.js.map +1 -1
- package/dist/src/test-utils/mockCommandContext.js +11 -2
- package/dist/src/test-utils/mockCommandContext.js.map +1 -1
- package/dist/src/ui/AppContainer.js +39 -36
- package/dist/src/ui/AppContainer.js.map +1 -1
- package/dist/src/ui/auth/useAuth.js +20 -1
- package/dist/src/ui/auth/useAuth.js.map +1 -1
- package/dist/src/ui/commands/clearCommand.js +22 -10
- package/dist/src/ui/commands/clearCommand.js.map +1 -1
- package/dist/src/ui/commands/languageCommand.js +41 -8
- package/dist/src/ui/commands/languageCommand.js.map +1 -1
- package/dist/src/ui/commands/languageCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/languageCommand.test.js +463 -0
- package/dist/src/ui/commands/languageCommand.test.js.map +1 -0
- package/dist/src/ui/commands/modelCommand.js +4 -2
- package/dist/src/ui/commands/modelCommand.js.map +1 -1
- package/dist/src/ui/commands/quitCommand.d.ts +0 -1
- package/dist/src/ui/commands/quitCommand.js +0 -27
- package/dist/src/ui/commands/quitCommand.js.map +1 -1
- package/dist/src/ui/commands/reviewCommand.d.ts +9 -0
- package/dist/src/ui/commands/reviewCommand.js +1267 -0
- package/dist/src/ui/commands/reviewCommand.js.map +1 -0
- package/dist/src/ui/commands/reviewCommand.test.d.ts +6 -0
- package/dist/src/ui/commands/reviewCommand.test.js +545 -0
- package/dist/src/ui/commands/reviewCommand.test.js.map +1 -0
- package/dist/src/ui/commands/setupGithubCommand.js +11 -10
- package/dist/src/ui/commands/setupGithubCommand.js.map +1 -1
- package/dist/src/ui/commands/setupGithubCommand.test.js +14 -14
- package/dist/src/ui/commands/setupGithubCommand.test.js.map +1 -1
- package/dist/src/ui/commands/types.d.ts +4 -9
- package/dist/src/ui/commands/types.js.map +1 -1
- package/dist/src/ui/commands/ums/unittestCommand.d.ts +9 -0
- package/dist/src/ui/commands/ums/unittestCommand.js +387 -0
- package/dist/src/ui/commands/ums/unittestCommand.js.map +1 -0
- package/dist/src/ui/components/Composer.test.js +1 -2
- package/dist/src/ui/components/Composer.test.js.map +1 -1
- package/dist/src/ui/components/ContextUsageDisplay.d.ts +3 -2
- package/dist/src/ui/components/ContextUsageDisplay.js +8 -2
- package/dist/src/ui/components/ContextUsageDisplay.js.map +1 -1
- package/dist/src/ui/components/DialogManager.js +3 -19
- package/dist/src/ui/components/DialogManager.js.map +1 -1
- package/dist/src/ui/components/Footer.js +2 -3
- package/dist/src/ui/components/Footer.js.map +1 -1
- package/dist/src/ui/components/Help.js +13 -2
- package/dist/src/ui/components/Help.js.map +1 -1
- package/dist/src/ui/components/Help.test.js +6 -0
- package/dist/src/ui/components/Help.test.js.map +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.js +3 -1
- package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.js +6 -4
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/ModelDialog.d.ts +3 -1
- package/dist/src/ui/components/ModelDialog.js +25 -5
- package/dist/src/ui/components/ModelDialog.js.map +1 -1
- package/dist/src/ui/components/OpenAIKeyPrompt.d.ts +3 -0
- package/dist/src/ui/components/OpenAIKeyPrompt.js +1 -0
- package/dist/src/ui/components/OpenAIKeyPrompt.js.map +1 -1
- package/dist/src/ui/components/ResumeSessionPicker.d.ts +10 -0
- package/dist/src/ui/components/ResumeSessionPicker.js +249 -0
- package/dist/src/ui/components/ResumeSessionPicker.js.map +1 -0
- package/dist/src/ui/components/SessionSummaryDisplay.js +10 -2
- package/dist/src/ui/components/SessionSummaryDisplay.js.map +1 -1
- package/dist/src/ui/components/SettingsDialog.test.js +2 -2
- package/dist/src/ui/components/SuggestionsDisplay.js +3 -2
- package/dist/src/ui/components/SuggestionsDisplay.js.map +1 -1
- package/dist/src/ui/components/messages/GeminiThoughtMessage.d.ts +18 -0
- package/dist/src/ui/components/messages/GeminiThoughtMessage.js +14 -0
- package/dist/src/ui/components/messages/GeminiThoughtMessage.js.map +1 -0
- package/dist/src/ui/components/messages/GeminiThoughtMessageContent.d.ts +18 -0
- package/dist/src/ui/components/messages/GeminiThoughtMessageContent.js +14 -0
- package/dist/src/ui/components/messages/GeminiThoughtMessageContent.js.map +1 -0
- package/dist/src/ui/components/subagents/manage/AgentEditStep.js +4 -1
- package/dist/src/ui/components/subagents/manage/AgentEditStep.js.map +1 -1
- package/dist/src/ui/components/subagents/manage/AgentSelectionStep.js +2 -2
- package/dist/src/ui/components/subagents/manage/AgentSelectionStep.js.map +1 -1
- package/dist/src/ui/components/ums/UMSKeyPrompt.d.ts +1 -1
- package/dist/src/ui/components/ums/UMSKeyPrompt.js +23 -3
- package/dist/src/ui/components/ums/UMSKeyPrompt.js.map +1 -1
- package/dist/src/ui/components/ums/umsStartupWarnings.d.ts +6 -0
- package/dist/src/ui/components/ums/umsStartupWarnings.js +47 -0
- package/dist/src/ui/components/ums/umsStartupWarnings.js.map +1 -0
- package/dist/src/ui/contexts/SessionContext.d.ts +2 -0
- package/dist/src/ui/contexts/SessionContext.js +18 -10
- package/dist/src/ui/contexts/SessionContext.js.map +1 -1
- package/dist/src/ui/contexts/UIStateContext.d.ts +1 -3
- package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.d.ts +2 -8
- package/dist/src/ui/hooks/slashCommandProcessor.js +68 -101
- package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/useAttentionNotifications.d.ts +3 -1
- package/dist/src/ui/hooks/useAttentionNotifications.js +10 -5
- package/dist/src/ui/hooks/useAttentionNotifications.js.map +1 -1
- package/dist/src/ui/hooks/useAttentionNotifications.test.js +39 -2
- package/dist/src/ui/hooks/useAttentionNotifications.test.js.map +1 -1
- package/dist/src/ui/hooks/useDialogClose.d.ts +0 -3
- package/dist/src/ui/hooks/useDialogClose.js +0 -2
- package/dist/src/ui/hooks/useDialogClose.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.js +49 -9
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/useLogger.d.ts +1 -1
- package/dist/src/ui/hooks/useLogger.js +6 -3
- package/dist/src/ui/hooks/useLogger.js.map +1 -1
- package/dist/src/ui/hooks/useReactToolScheduler.js +2 -2
- package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
- package/dist/src/ui/hooks/useSlashCompletion.js +9 -1
- package/dist/src/ui/hooks/useSlashCompletion.js.map +1 -1
- package/dist/src/ui/hooks/useSlashCompletion.test.js +36 -31
- package/dist/src/ui/hooks/useSlashCompletion.test.js.map +1 -1
- package/dist/src/ui/hooks/useToolScheduler.test.js +1 -0
- package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
- package/dist/src/ui/models/availableModels.d.ts +8 -2
- package/dist/src/ui/models/availableModels.js +24 -35
- package/dist/src/ui/models/availableModels.js.map +1 -1
- package/dist/src/ui/noninteractive/nonInteractiveUi.js +0 -1
- package/dist/src/ui/noninteractive/nonInteractiveUi.js.map +1 -1
- package/dist/src/ui/types.d.ts +9 -14
- package/dist/src/ui/types.js +0 -1
- package/dist/src/ui/types.js.map +1 -1
- package/dist/src/ui/utils/InlineMarkdownRenderer.d.ts +1 -0
- package/dist/src/ui/utils/InlineMarkdownRenderer.js +2 -2
- package/dist/src/ui/utils/InlineMarkdownRenderer.js.map +1 -1
- package/dist/src/ui/utils/MarkdownDisplay.d.ts +1 -0
- package/dist/src/ui/utils/MarkdownDisplay.js +13 -13
- package/dist/src/ui/utils/MarkdownDisplay.js.map +1 -1
- package/dist/src/ui/utils/formatters.d.ts +6 -0
- package/dist/src/ui/utils/formatters.js +31 -0
- package/dist/src/ui/utils/formatters.js.map +1 -1
- package/dist/src/ui/utils/formatters.test.js +51 -2
- package/dist/src/ui/utils/formatters.test.js.map +1 -1
- package/dist/src/ui/utils/resumeHistoryUtils.d.ts +19 -0
- package/dist/src/ui/utils/resumeHistoryUtils.js +263 -0
- package/dist/src/ui/utils/resumeHistoryUtils.js.map +1 -0
- package/dist/src/ui/utils/resumeHistoryUtils.test.d.ts +6 -0
- package/dist/src/ui/utils/resumeHistoryUtils.test.js +248 -0
- package/dist/src/ui/utils/resumeHistoryUtils.test.js.map +1 -0
- package/dist/src/utils/attentionNotification.d.ts +1 -0
- package/dist/src/utils/attentionNotification.js +4 -0
- package/dist/src/utils/attentionNotification.js.map +1 -1
- package/dist/src/utils/gitUtils.js +3 -3
- package/dist/src/utils/gitUtils.js.map +1 -1
- package/dist/src/utils/nonInteractiveHelpers.js +1 -1
- package/dist/src/utils/nonInteractiveHelpers.js.map +1 -1
- package/dist/src/utils/nonInteractiveHelpers.test.js +1 -1
- package/dist/src/utils/nonInteractiveHelpers.test.js.map +1 -1
- package/dist/src/validateNonInterActiveAuth.js +1 -1
- package/dist/src/validateNonInterActiveAuth.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/dist/src/nonInteractive/control/controllers/mcpController.d.ts +0 -42
- package/dist/src/nonInteractive/control/controllers/mcpController.js +0 -205
- package/dist/src/nonInteractive/control/controllers/mcpController.js.map +0 -1
- package/dist/src/ui/commands/chatCommand.d.ts +0 -9
- package/dist/src/ui/commands/chatCommand.js +0 -351
- package/dist/src/ui/commands/chatCommand.js.map +0 -1
- package/dist/src/ui/commands/corgiCommand.d.ts +0 -7
- package/dist/src/ui/commands/corgiCommand.js +0 -16
- package/dist/src/ui/commands/corgiCommand.js.map +0 -1
- package/dist/src/ui/components/QuitConfirmationDialog.d.ts +0 -17
- package/dist/src/ui/components/QuitConfirmationDialog.js +0 -49
- package/dist/src/ui/components/QuitConfirmationDialog.js.map +0 -1
- package/dist/src/ui/hooks/usePromptCompletion.d.ts +0 -23
- package/dist/src/ui/hooks/usePromptCompletion.js +0 -177
- package/dist/src/ui/hooks/usePromptCompletion.js.map +0 -1
- package/dist/src/ui/hooks/useQuitConfirmation.d.ts +0 -14
- package/dist/src/ui/hooks/useQuitConfirmation.js +0 -36
- package/dist/src/ui/hooks/useQuitConfirmation.js.map +0 -1
- package/dist/src/zed-integration/acp.js.map +0 -1
- package/dist/src/zed-integration/fileSystemService.js.map +0 -1
- package/dist/src/zed-integration/schema.js.map +0 -1
- package/dist/src/zed-integration/zedIntegration.d.ts +0 -17
- package/dist/src/zed-integration/zedIntegration.js +0 -1135
- package/dist/src/zed-integration/zedIntegration.js.map +0 -1
|
@@ -1,1135 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license
|
|
3
|
-
* Copyright 2025 Google LLC
|
|
4
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
-
*/
|
|
6
|
-
import { AuthType, clearCachedCredentialFile, convertToFunctionResponse, DiscoveredMCPTool, StreamEventType, DEFAULT_GEMINI_MODEL, DEFAULT_GEMINI_MODEL_AUTO, DEFAULT_GEMINI_FLASH_MODEL, MCPServerConfig, ToolConfirmationOutcome, logToolCall, logUserPrompt, getErrorStatus, isWithinRoot, isNodeError, SubAgentEventType, TaskTool, Kind, TodoWriteTool, UserPromptEvent, } from '@umsai/ums-code-core';
|
|
7
|
-
import * as acp from './acp.js';
|
|
8
|
-
import { AcpFileSystemService } from './fileSystemService.js';
|
|
9
|
-
import { Readable, Writable } from 'node:stream';
|
|
10
|
-
import { SettingScope } from '../config/settings.js';
|
|
11
|
-
import * as fs from 'node:fs/promises';
|
|
12
|
-
import * as path from 'node:path';
|
|
13
|
-
import { z } from 'zod';
|
|
14
|
-
import { randomUUID } from 'node:crypto';
|
|
15
|
-
import { getErrorMessage } from '../utils/errors.js';
|
|
16
|
-
import { ExtensionStorage } from '../config/extension.js';
|
|
17
|
-
import { loadCliConfig } from '../config/config.js';
|
|
18
|
-
import { ExtensionEnablementManager } from '../config/extensions/extensionEnablement.js';
|
|
19
|
-
import { handleSlashCommand, getAvailableCommands, } from '../nonInteractiveCliCommands.js';
|
|
20
|
-
import { isSlashCommand } from '../ui/utils/commandUtils.js';
|
|
21
|
-
/**
|
|
22
|
-
* Built-in commands that are allowed in ACP integration mode.
|
|
23
|
-
* Only these commands will be available when using handleSlashCommand
|
|
24
|
-
* or getAvailableCommands in ACP integration.
|
|
25
|
-
*
|
|
26
|
-
* Currently, only "init" is supported because `handleSlashCommand` in
|
|
27
|
-
* nonInteractiveCliCommands.ts only supports handling results where
|
|
28
|
-
* result.type is "submit_prompt". Other result types are either coupled
|
|
29
|
-
* to the UI or cannot send notifications to the client via ACP.
|
|
30
|
-
*
|
|
31
|
-
* If you have a good idea to add support for more commands, PRs are welcome!
|
|
32
|
-
*/
|
|
33
|
-
const ALLOWED_BUILTIN_COMMANDS_FOR_ACP = ['init'];
|
|
34
|
-
/**
|
|
35
|
-
* Resolves the model to use based on the current configuration.
|
|
36
|
-
*
|
|
37
|
-
* If the model is set to "auto", it will use the flash model if in fallback
|
|
38
|
-
* mode, otherwise it will use the default model.
|
|
39
|
-
*/
|
|
40
|
-
export function resolveModel(model, isInFallbackMode) {
|
|
41
|
-
if (model === DEFAULT_GEMINI_MODEL_AUTO) {
|
|
42
|
-
return isInFallbackMode ? DEFAULT_GEMINI_FLASH_MODEL : DEFAULT_GEMINI_MODEL;
|
|
43
|
-
}
|
|
44
|
-
return model;
|
|
45
|
-
}
|
|
46
|
-
export async function runZedIntegration(config, settings, extensions, argv) {
|
|
47
|
-
const stdout = Writable.toWeb(process.stdout);
|
|
48
|
-
const stdin = Readable.toWeb(process.stdin);
|
|
49
|
-
// Stdout is used to send messages to the client, so console.log/console.info
|
|
50
|
-
// messages to stderr so that they don't interfere with ACP.
|
|
51
|
-
console.log = console.error;
|
|
52
|
-
console.info = console.error;
|
|
53
|
-
console.debug = console.error;
|
|
54
|
-
new acp.AgentSideConnection((client) => new GeminiAgent(config, settings, extensions, argv, client), stdout, stdin);
|
|
55
|
-
}
|
|
56
|
-
class GeminiAgent {
|
|
57
|
-
config;
|
|
58
|
-
settings;
|
|
59
|
-
extensions;
|
|
60
|
-
argv;
|
|
61
|
-
client;
|
|
62
|
-
sessions = new Map();
|
|
63
|
-
clientCapabilities;
|
|
64
|
-
constructor(config, settings, extensions, argv, client) {
|
|
65
|
-
this.config = config;
|
|
66
|
-
this.settings = settings;
|
|
67
|
-
this.extensions = extensions;
|
|
68
|
-
this.argv = argv;
|
|
69
|
-
this.client = client;
|
|
70
|
-
}
|
|
71
|
-
async initialize(args) {
|
|
72
|
-
this.clientCapabilities = args.clientCapabilities;
|
|
73
|
-
const authMethods = [
|
|
74
|
-
{
|
|
75
|
-
id: AuthType.USE_OPENAI,
|
|
76
|
-
name: 'Use OpenAI API key',
|
|
77
|
-
description: 'Requires setting the `OPENAI_API_KEY` environment variable',
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
id: AuthType.QWEN_OAUTH,
|
|
81
|
-
name: 'Qwen OAuth',
|
|
82
|
-
description: 'OAuth authentication for Qwen models with 2000 daily requests',
|
|
83
|
-
},
|
|
84
|
-
];
|
|
85
|
-
return {
|
|
86
|
-
protocolVersion: acp.PROTOCOL_VERSION,
|
|
87
|
-
authMethods,
|
|
88
|
-
agentCapabilities: {
|
|
89
|
-
loadSession: false,
|
|
90
|
-
promptCapabilities: {
|
|
91
|
-
image: true,
|
|
92
|
-
audio: true,
|
|
93
|
-
embeddedContext: true,
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
async authenticate({ methodId }) {
|
|
99
|
-
const method = z.nativeEnum(AuthType).parse(methodId);
|
|
100
|
-
await clearCachedCredentialFile();
|
|
101
|
-
await this.config.refreshAuth(method);
|
|
102
|
-
this.settings.setValue(SettingScope.User, 'security.auth.selectedType', method);
|
|
103
|
-
}
|
|
104
|
-
async newSession({ cwd, mcpServers, }) {
|
|
105
|
-
const sessionId = this.config.getSessionId() || randomUUID();
|
|
106
|
-
const config = await this.newSessionConfig(sessionId, cwd, mcpServers);
|
|
107
|
-
let isAuthenticated = false;
|
|
108
|
-
if (this.settings.merged.security?.auth?.selectedType) {
|
|
109
|
-
try {
|
|
110
|
-
await config.refreshAuth(this.settings.merged.security.auth.selectedType);
|
|
111
|
-
isAuthenticated = true;
|
|
112
|
-
}
|
|
113
|
-
catch (e) {
|
|
114
|
-
console.error(`Authentication failed: ${e}`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
if (!isAuthenticated) {
|
|
118
|
-
throw acp.RequestError.authRequired();
|
|
119
|
-
}
|
|
120
|
-
if (this.clientCapabilities?.fs) {
|
|
121
|
-
const acpFileSystemService = new AcpFileSystemService(this.client, sessionId, this.clientCapabilities.fs, config.getFileSystemService());
|
|
122
|
-
config.setFileSystemService(acpFileSystemService);
|
|
123
|
-
}
|
|
124
|
-
const geminiClient = config.getGeminiClient();
|
|
125
|
-
const chat = await geminiClient.startChat();
|
|
126
|
-
const session = new Session(sessionId, chat, config, this.client, this.settings);
|
|
127
|
-
this.sessions.set(sessionId, session);
|
|
128
|
-
// Send available commands update as the first session update
|
|
129
|
-
setTimeout(async () => {
|
|
130
|
-
await session.sendAvailableCommandsUpdate();
|
|
131
|
-
}, 0);
|
|
132
|
-
return {
|
|
133
|
-
sessionId,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
async newSessionConfig(sessionId, cwd, mcpServers) {
|
|
137
|
-
const mergedMcpServers = { ...this.settings.merged.mcpServers };
|
|
138
|
-
for (const { command, args, env: rawEnv, name } of mcpServers) {
|
|
139
|
-
const env = {};
|
|
140
|
-
for (const { name: envName, value } of rawEnv) {
|
|
141
|
-
env[envName] = value;
|
|
142
|
-
}
|
|
143
|
-
mergedMcpServers[name] = new MCPServerConfig(command, args, env, cwd);
|
|
144
|
-
}
|
|
145
|
-
const settings = { ...this.settings.merged, mcpServers: mergedMcpServers };
|
|
146
|
-
const config = await loadCliConfig(settings, this.extensions, new ExtensionEnablementManager(ExtensionStorage.getUserExtensionsDir(), this.argv.extensions), sessionId, this.argv, cwd);
|
|
147
|
-
await config.initialize();
|
|
148
|
-
return config;
|
|
149
|
-
}
|
|
150
|
-
async cancel(params) {
|
|
151
|
-
const session = this.sessions.get(params.sessionId);
|
|
152
|
-
if (!session) {
|
|
153
|
-
throw new Error(`Session not found: ${params.sessionId}`);
|
|
154
|
-
}
|
|
155
|
-
await session.cancelPendingPrompt();
|
|
156
|
-
}
|
|
157
|
-
async prompt(params) {
|
|
158
|
-
const session = this.sessions.get(params.sessionId);
|
|
159
|
-
if (!session) {
|
|
160
|
-
throw new Error(`Session not found: ${params.sessionId}`);
|
|
161
|
-
}
|
|
162
|
-
return session.prompt(params);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
class Session {
|
|
166
|
-
id;
|
|
167
|
-
chat;
|
|
168
|
-
config;
|
|
169
|
-
client;
|
|
170
|
-
settings;
|
|
171
|
-
pendingPrompt = null;
|
|
172
|
-
turn = 0;
|
|
173
|
-
constructor(id, chat, config, client, settings) {
|
|
174
|
-
this.id = id;
|
|
175
|
-
this.chat = chat;
|
|
176
|
-
this.config = config;
|
|
177
|
-
this.client = client;
|
|
178
|
-
this.settings = settings;
|
|
179
|
-
}
|
|
180
|
-
async cancelPendingPrompt() {
|
|
181
|
-
if (!this.pendingPrompt) {
|
|
182
|
-
throw new Error('Not currently generating');
|
|
183
|
-
}
|
|
184
|
-
this.pendingPrompt.abort();
|
|
185
|
-
this.pendingPrompt = null;
|
|
186
|
-
}
|
|
187
|
-
async prompt(params) {
|
|
188
|
-
this.pendingPrompt?.abort();
|
|
189
|
-
const pendingSend = new AbortController();
|
|
190
|
-
this.pendingPrompt = pendingSend;
|
|
191
|
-
// Increment turn counter for each user prompt
|
|
192
|
-
this.turn += 1;
|
|
193
|
-
const chat = this.chat;
|
|
194
|
-
const promptId = this.config.getSessionId() + '########' + this.turn;
|
|
195
|
-
// Extract text from all text blocks to construct the full prompt text for logging
|
|
196
|
-
const promptText = params.prompt
|
|
197
|
-
.filter((block) => block.type === 'text')
|
|
198
|
-
.map((block) => (block.type === 'text' ? block.text : ''))
|
|
199
|
-
.join(' ');
|
|
200
|
-
// Log user prompt
|
|
201
|
-
logUserPrompt(this.config, new UserPromptEvent(promptText.length, promptId, this.config.getContentGeneratorConfig()?.authType, promptText));
|
|
202
|
-
// Check if the input contains a slash command
|
|
203
|
-
// Extract text from the first text block if present
|
|
204
|
-
const firstTextBlock = params.prompt.find((block) => block.type === 'text');
|
|
205
|
-
const inputText = firstTextBlock?.text || '';
|
|
206
|
-
let parts;
|
|
207
|
-
if (isSlashCommand(inputText)) {
|
|
208
|
-
// Handle slash command - allow specific built-in commands for ACP integration
|
|
209
|
-
const slashCommandResult = await handleSlashCommand(inputText, pendingSend, this.config, this.settings, ALLOWED_BUILTIN_COMMANDS_FOR_ACP);
|
|
210
|
-
if (slashCommandResult) {
|
|
211
|
-
// Use the result from the slash command
|
|
212
|
-
parts = slashCommandResult;
|
|
213
|
-
}
|
|
214
|
-
else {
|
|
215
|
-
// Slash command didn't return a prompt, continue with normal processing
|
|
216
|
-
parts = await this.#resolvePrompt(params.prompt, pendingSend.signal);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
else {
|
|
220
|
-
// Normal processing for non-slash commands
|
|
221
|
-
parts = await this.#resolvePrompt(params.prompt, pendingSend.signal);
|
|
222
|
-
}
|
|
223
|
-
let nextMessage = { role: 'user', parts };
|
|
224
|
-
while (nextMessage !== null) {
|
|
225
|
-
if (pendingSend.signal.aborted) {
|
|
226
|
-
chat.addHistory(nextMessage);
|
|
227
|
-
return { stopReason: 'cancelled' };
|
|
228
|
-
}
|
|
229
|
-
const functionCalls = [];
|
|
230
|
-
try {
|
|
231
|
-
const responseStream = await chat.sendMessageStream(resolveModel(this.config.getModel(), this.config.isInFallbackMode()), {
|
|
232
|
-
message: nextMessage?.parts ?? [],
|
|
233
|
-
config: {
|
|
234
|
-
abortSignal: pendingSend.signal,
|
|
235
|
-
},
|
|
236
|
-
}, promptId);
|
|
237
|
-
nextMessage = null;
|
|
238
|
-
for await (const resp of responseStream) {
|
|
239
|
-
if (pendingSend.signal.aborted) {
|
|
240
|
-
return { stopReason: 'cancelled' };
|
|
241
|
-
}
|
|
242
|
-
if (resp.type === StreamEventType.CHUNK &&
|
|
243
|
-
resp.value.candidates &&
|
|
244
|
-
resp.value.candidates.length > 0) {
|
|
245
|
-
const candidate = resp.value.candidates[0];
|
|
246
|
-
for (const part of candidate.content?.parts ?? []) {
|
|
247
|
-
if (!part.text) {
|
|
248
|
-
continue;
|
|
249
|
-
}
|
|
250
|
-
const content = {
|
|
251
|
-
type: 'text',
|
|
252
|
-
text: part.text,
|
|
253
|
-
};
|
|
254
|
-
this.sendUpdate({
|
|
255
|
-
sessionUpdate: part.thought
|
|
256
|
-
? 'agent_thought_chunk'
|
|
257
|
-
: 'agent_message_chunk',
|
|
258
|
-
content,
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
if (resp.type === StreamEventType.CHUNK && resp.value.functionCalls) {
|
|
263
|
-
functionCalls.push(...resp.value.functionCalls);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
catch (error) {
|
|
268
|
-
if (getErrorStatus(error) === 429) {
|
|
269
|
-
throw new acp.RequestError(429, 'Rate limit exceeded. Try again later.');
|
|
270
|
-
}
|
|
271
|
-
throw error;
|
|
272
|
-
}
|
|
273
|
-
if (functionCalls.length > 0) {
|
|
274
|
-
const toolResponseParts = [];
|
|
275
|
-
for (const fc of functionCalls) {
|
|
276
|
-
const response = await this.runTool(pendingSend.signal, promptId, fc);
|
|
277
|
-
toolResponseParts.push(...response);
|
|
278
|
-
}
|
|
279
|
-
nextMessage = { role: 'user', parts: toolResponseParts };
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return { stopReason: 'end_turn' };
|
|
283
|
-
}
|
|
284
|
-
async sendUpdate(update) {
|
|
285
|
-
const params = {
|
|
286
|
-
sessionId: this.id,
|
|
287
|
-
update,
|
|
288
|
-
};
|
|
289
|
-
await this.client.sessionUpdate(params);
|
|
290
|
-
}
|
|
291
|
-
async sendAvailableCommandsUpdate() {
|
|
292
|
-
const abortController = new AbortController();
|
|
293
|
-
try {
|
|
294
|
-
const slashCommands = await getAvailableCommands(this.config, this.settings, abortController.signal, ALLOWED_BUILTIN_COMMANDS_FOR_ACP);
|
|
295
|
-
// Convert SlashCommand[] to AvailableCommand[] format for ACP protocol
|
|
296
|
-
const availableCommands = slashCommands.map((cmd) => ({
|
|
297
|
-
name: cmd.name,
|
|
298
|
-
description: cmd.description,
|
|
299
|
-
input: null,
|
|
300
|
-
}));
|
|
301
|
-
const update = {
|
|
302
|
-
sessionUpdate: 'available_commands_update',
|
|
303
|
-
availableCommands,
|
|
304
|
-
};
|
|
305
|
-
await this.sendUpdate(update);
|
|
306
|
-
}
|
|
307
|
-
catch (error) {
|
|
308
|
-
// Log error but don't fail session creation
|
|
309
|
-
console.error('Error sending available commands update:', error);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
async runTool(abortSignal, promptId, fc) {
|
|
313
|
-
const callId = fc.id ?? `${fc.name}-${Date.now()}`;
|
|
314
|
-
const args = (fc.args ?? {});
|
|
315
|
-
const startTime = Date.now();
|
|
316
|
-
const errorResponse = (error) => {
|
|
317
|
-
const durationMs = Date.now() - startTime;
|
|
318
|
-
logToolCall(this.config, {
|
|
319
|
-
'event.name': 'tool_call',
|
|
320
|
-
'event.timestamp': new Date().toISOString(),
|
|
321
|
-
prompt_id: promptId,
|
|
322
|
-
function_name: fc.name ?? '',
|
|
323
|
-
function_args: args,
|
|
324
|
-
duration_ms: durationMs,
|
|
325
|
-
status: 'error',
|
|
326
|
-
success: false,
|
|
327
|
-
error: error.message,
|
|
328
|
-
tool_type: typeof tool !== 'undefined' && tool instanceof DiscoveredMCPTool
|
|
329
|
-
? 'mcp'
|
|
330
|
-
: 'native',
|
|
331
|
-
});
|
|
332
|
-
return [
|
|
333
|
-
{
|
|
334
|
-
functionResponse: {
|
|
335
|
-
id: callId,
|
|
336
|
-
name: fc.name ?? '',
|
|
337
|
-
response: { error: error.message },
|
|
338
|
-
},
|
|
339
|
-
},
|
|
340
|
-
];
|
|
341
|
-
};
|
|
342
|
-
if (!fc.name) {
|
|
343
|
-
return errorResponse(new Error('Missing function name'));
|
|
344
|
-
}
|
|
345
|
-
const toolRegistry = this.config.getToolRegistry();
|
|
346
|
-
const tool = toolRegistry.getTool(fc.name);
|
|
347
|
-
if (!tool) {
|
|
348
|
-
return errorResponse(new Error(`Tool "${fc.name}" not found in registry.`));
|
|
349
|
-
}
|
|
350
|
-
// Detect TodoWriteTool early - route to plan updates instead of tool_call events
|
|
351
|
-
const isTodoWriteTool = fc.name === TodoWriteTool.Name || tool.name === TodoWriteTool.Name;
|
|
352
|
-
// Declare subAgentToolEventListeners outside try block for cleanup in catch
|
|
353
|
-
let subAgentToolEventListeners = [];
|
|
354
|
-
try {
|
|
355
|
-
const invocation = tool.build(args);
|
|
356
|
-
// Detect TaskTool and set up sub-agent tool tracking
|
|
357
|
-
const isTaskTool = tool.name === TaskTool.Name;
|
|
358
|
-
if (isTaskTool && 'eventEmitter' in invocation) {
|
|
359
|
-
// Access eventEmitter from TaskTool invocation
|
|
360
|
-
const taskEventEmitter = invocation.eventEmitter;
|
|
361
|
-
// Set up sub-agent tool tracking
|
|
362
|
-
subAgentToolEventListeners = this.setupSubAgentToolTracking(taskEventEmitter, abortSignal);
|
|
363
|
-
}
|
|
364
|
-
const confirmationDetails = await invocation.shouldConfirmExecute(abortSignal);
|
|
365
|
-
if (confirmationDetails) {
|
|
366
|
-
const content = [];
|
|
367
|
-
if (confirmationDetails.type === 'edit') {
|
|
368
|
-
content.push({
|
|
369
|
-
type: 'diff',
|
|
370
|
-
path: confirmationDetails.fileName,
|
|
371
|
-
oldText: confirmationDetails.originalContent,
|
|
372
|
-
newText: confirmationDetails.newContent,
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
const params = {
|
|
376
|
-
sessionId: this.id,
|
|
377
|
-
options: toPermissionOptions(confirmationDetails),
|
|
378
|
-
toolCall: {
|
|
379
|
-
toolCallId: callId,
|
|
380
|
-
status: 'pending',
|
|
381
|
-
title: invocation.getDescription(),
|
|
382
|
-
content,
|
|
383
|
-
locations: invocation.toolLocations(),
|
|
384
|
-
kind: tool.kind,
|
|
385
|
-
},
|
|
386
|
-
};
|
|
387
|
-
const output = await this.client.requestPermission(params);
|
|
388
|
-
const outcome = output.outcome.outcome === 'cancelled'
|
|
389
|
-
? ToolConfirmationOutcome.Cancel
|
|
390
|
-
: z
|
|
391
|
-
.nativeEnum(ToolConfirmationOutcome)
|
|
392
|
-
.parse(output.outcome.optionId);
|
|
393
|
-
await confirmationDetails.onConfirm(outcome);
|
|
394
|
-
switch (outcome) {
|
|
395
|
-
case ToolConfirmationOutcome.Cancel:
|
|
396
|
-
return errorResponse(new Error(`Tool "${fc.name}" was canceled by the user.`));
|
|
397
|
-
case ToolConfirmationOutcome.ProceedOnce:
|
|
398
|
-
case ToolConfirmationOutcome.ProceedAlways:
|
|
399
|
-
case ToolConfirmationOutcome.ProceedAlwaysServer:
|
|
400
|
-
case ToolConfirmationOutcome.ProceedAlwaysTool:
|
|
401
|
-
case ToolConfirmationOutcome.ModifyWithEditor:
|
|
402
|
-
break;
|
|
403
|
-
default: {
|
|
404
|
-
const resultOutcome = outcome;
|
|
405
|
-
throw new Error(`Unexpected: ${resultOutcome}`);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
else if (!isTodoWriteTool) {
|
|
410
|
-
// Skip tool_call event for TodoWriteTool
|
|
411
|
-
await this.sendUpdate({
|
|
412
|
-
sessionUpdate: 'tool_call',
|
|
413
|
-
toolCallId: callId,
|
|
414
|
-
status: 'in_progress',
|
|
415
|
-
title: invocation.getDescription(),
|
|
416
|
-
content: [],
|
|
417
|
-
locations: invocation.toolLocations(),
|
|
418
|
-
kind: tool.kind,
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
|
-
const toolResult = await invocation.execute(abortSignal);
|
|
422
|
-
// Clean up event listeners
|
|
423
|
-
subAgentToolEventListeners.forEach((cleanup) => cleanup());
|
|
424
|
-
// Handle TodoWriteTool: extract todos and send plan update
|
|
425
|
-
if (isTodoWriteTool) {
|
|
426
|
-
// Extract todos from args (initial state)
|
|
427
|
-
let todos = [];
|
|
428
|
-
if (Array.isArray(args['todos'])) {
|
|
429
|
-
todos = args['todos'];
|
|
430
|
-
}
|
|
431
|
-
// If returnDisplay has todos (e.g., modified by user), use those instead
|
|
432
|
-
if (toolResult.returnDisplay &&
|
|
433
|
-
typeof toolResult.returnDisplay === 'object' &&
|
|
434
|
-
'type' in toolResult.returnDisplay &&
|
|
435
|
-
toolResult.returnDisplay.type === 'todo_list' &&
|
|
436
|
-
'todos' in toolResult.returnDisplay &&
|
|
437
|
-
Array.isArray(toolResult.returnDisplay.todos)) {
|
|
438
|
-
todos = toolResult.returnDisplay.todos;
|
|
439
|
-
}
|
|
440
|
-
// Convert todos to plan entries and send plan update
|
|
441
|
-
if (todos.length > 0 || Array.isArray(args['todos'])) {
|
|
442
|
-
const planEntries = convertTodosToPlanEntries(todos);
|
|
443
|
-
await this.sendUpdate({
|
|
444
|
-
sessionUpdate: 'plan',
|
|
445
|
-
entries: planEntries,
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
// Skip tool_call_update event for TodoWriteTool
|
|
449
|
-
// Still log and return function response for LLM
|
|
450
|
-
}
|
|
451
|
-
else {
|
|
452
|
-
// Normal tool handling: send tool_call_update
|
|
453
|
-
const content = toToolCallContent(toolResult);
|
|
454
|
-
await this.sendUpdate({
|
|
455
|
-
sessionUpdate: 'tool_call_update',
|
|
456
|
-
toolCallId: callId,
|
|
457
|
-
status: 'completed',
|
|
458
|
-
content: content ? [content] : [],
|
|
459
|
-
});
|
|
460
|
-
}
|
|
461
|
-
const durationMs = Date.now() - startTime;
|
|
462
|
-
logToolCall(this.config, {
|
|
463
|
-
'event.name': 'tool_call',
|
|
464
|
-
'event.timestamp': new Date().toISOString(),
|
|
465
|
-
function_name: fc.name,
|
|
466
|
-
function_args: args,
|
|
467
|
-
duration_ms: durationMs,
|
|
468
|
-
status: 'success',
|
|
469
|
-
success: true,
|
|
470
|
-
prompt_id: promptId,
|
|
471
|
-
tool_type: typeof tool !== 'undefined' && tool instanceof DiscoveredMCPTool
|
|
472
|
-
? 'mcp'
|
|
473
|
-
: 'native',
|
|
474
|
-
});
|
|
475
|
-
return convertToFunctionResponse(fc.name, callId, toolResult.llmContent);
|
|
476
|
-
}
|
|
477
|
-
catch (e) {
|
|
478
|
-
// Ensure cleanup on error
|
|
479
|
-
subAgentToolEventListeners.forEach((cleanup) => cleanup());
|
|
480
|
-
const error = e instanceof Error ? e : new Error(String(e));
|
|
481
|
-
await this.sendUpdate({
|
|
482
|
-
sessionUpdate: 'tool_call_update',
|
|
483
|
-
toolCallId: callId,
|
|
484
|
-
status: 'failed',
|
|
485
|
-
content: [
|
|
486
|
-
{ type: 'content', content: { type: 'text', text: error.message } },
|
|
487
|
-
],
|
|
488
|
-
});
|
|
489
|
-
return errorResponse(error);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
/**
|
|
493
|
-
* Sets up event listeners to track sub-agent tool calls within a TaskTool execution.
|
|
494
|
-
* Converts subagent tool call events into zedIntegration session updates.
|
|
495
|
-
*
|
|
496
|
-
* @param eventEmitter - The SubAgentEventEmitter from TaskTool
|
|
497
|
-
* @param abortSignal - Signal to abort tracking if parent is cancelled
|
|
498
|
-
* @returns Array of cleanup functions to remove event listeners
|
|
499
|
-
*/
|
|
500
|
-
setupSubAgentToolTracking(eventEmitter, abortSignal) {
|
|
501
|
-
const cleanupFunctions = [];
|
|
502
|
-
const toolRegistry = this.config.getToolRegistry();
|
|
503
|
-
// Track subagent tool call states
|
|
504
|
-
const subAgentToolStates = new Map();
|
|
505
|
-
// Listen for tool call start
|
|
506
|
-
const onToolCall = (...args) => {
|
|
507
|
-
const event = args[0];
|
|
508
|
-
if (abortSignal.aborted)
|
|
509
|
-
return;
|
|
510
|
-
const subAgentTool = toolRegistry.getTool(event.name);
|
|
511
|
-
let subAgentInvocation;
|
|
512
|
-
let toolKind = 'other';
|
|
513
|
-
let locations = [];
|
|
514
|
-
if (subAgentTool) {
|
|
515
|
-
try {
|
|
516
|
-
subAgentInvocation = subAgentTool.build(event.args);
|
|
517
|
-
toolKind = this.mapToolKind(subAgentTool.kind);
|
|
518
|
-
locations = subAgentInvocation.toolLocations().map((loc) => ({
|
|
519
|
-
path: loc.path,
|
|
520
|
-
line: loc.line ?? null,
|
|
521
|
-
}));
|
|
522
|
-
}
|
|
523
|
-
catch (e) {
|
|
524
|
-
// If building fails, continue with defaults
|
|
525
|
-
console.warn(`Failed to build subagent tool ${event.name}:`, e);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
// Save state for subsequent updates
|
|
529
|
-
subAgentToolStates.set(event.callId, {
|
|
530
|
-
tool: subAgentTool,
|
|
531
|
-
invocation: subAgentInvocation,
|
|
532
|
-
args: event.args,
|
|
533
|
-
});
|
|
534
|
-
// Check if this is TodoWriteTool - if so, skip sending tool_call event
|
|
535
|
-
// Plan update will be sent in onToolResult when we have the final state
|
|
536
|
-
if (event.name === TodoWriteTool.Name) {
|
|
537
|
-
return;
|
|
538
|
-
}
|
|
539
|
-
// Send tool call start update with rawInput
|
|
540
|
-
void this.sendUpdate({
|
|
541
|
-
sessionUpdate: 'tool_call',
|
|
542
|
-
toolCallId: event.callId,
|
|
543
|
-
status: 'in_progress',
|
|
544
|
-
title: event.description || event.name,
|
|
545
|
-
content: [],
|
|
546
|
-
locations,
|
|
547
|
-
kind: toolKind,
|
|
548
|
-
rawInput: event.args,
|
|
549
|
-
});
|
|
550
|
-
};
|
|
551
|
-
// Listen for tool call result
|
|
552
|
-
const onToolResult = (...args) => {
|
|
553
|
-
const event = args[0];
|
|
554
|
-
if (abortSignal.aborted)
|
|
555
|
-
return;
|
|
556
|
-
const state = subAgentToolStates.get(event.callId);
|
|
557
|
-
// Check if this is TodoWriteTool - if so, route to plan updates
|
|
558
|
-
if (event.name === TodoWriteTool.Name) {
|
|
559
|
-
let todos;
|
|
560
|
-
// Try to extract todos from resultDisplay first (final state)
|
|
561
|
-
if (event.resultDisplay) {
|
|
562
|
-
try {
|
|
563
|
-
// resultDisplay might be a JSON stringified object
|
|
564
|
-
const parsed = typeof event.resultDisplay === 'string'
|
|
565
|
-
? JSON.parse(event.resultDisplay)
|
|
566
|
-
: event.resultDisplay;
|
|
567
|
-
if (typeof parsed === 'object' &&
|
|
568
|
-
parsed !== null &&
|
|
569
|
-
'type' in parsed &&
|
|
570
|
-
parsed.type === 'todo_list' &&
|
|
571
|
-
'todos' in parsed &&
|
|
572
|
-
Array.isArray(parsed.todos)) {
|
|
573
|
-
todos = parsed.todos;
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
catch {
|
|
577
|
-
// If parsing fails, ignore - resultDisplay might not be JSON
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
// Fallback to args if resultDisplay doesn't have todos
|
|
581
|
-
if (!todos && state?.args && Array.isArray(state.args['todos'])) {
|
|
582
|
-
todos = state.args['todos'];
|
|
583
|
-
}
|
|
584
|
-
// Send plan update if we have todos
|
|
585
|
-
if (todos) {
|
|
586
|
-
const planEntries = convertTodosToPlanEntries(todos);
|
|
587
|
-
void this.sendUpdate({
|
|
588
|
-
sessionUpdate: 'plan',
|
|
589
|
-
entries: planEntries,
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
// Skip sending tool_call_update event for TodoWriteTool
|
|
593
|
-
// Clean up state
|
|
594
|
-
subAgentToolStates.delete(event.callId);
|
|
595
|
-
return;
|
|
596
|
-
}
|
|
597
|
-
let content = [];
|
|
598
|
-
// If there's a result display, try to convert to ToolCallContent
|
|
599
|
-
if (event.resultDisplay && state?.invocation) {
|
|
600
|
-
// resultDisplay is typically a string
|
|
601
|
-
if (typeof event.resultDisplay === 'string') {
|
|
602
|
-
content = [
|
|
603
|
-
{
|
|
604
|
-
type: 'content',
|
|
605
|
-
content: {
|
|
606
|
-
type: 'text',
|
|
607
|
-
text: event.resultDisplay,
|
|
608
|
-
},
|
|
609
|
-
},
|
|
610
|
-
];
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
// Send tool call completion update
|
|
614
|
-
void this.sendUpdate({
|
|
615
|
-
sessionUpdate: 'tool_call_update',
|
|
616
|
-
toolCallId: event.callId,
|
|
617
|
-
status: event.success ? 'completed' : 'failed',
|
|
618
|
-
content: content.length > 0 ? content : [],
|
|
619
|
-
title: state?.invocation?.getDescription() ?? event.name,
|
|
620
|
-
kind: state?.tool ? this.mapToolKind(state.tool.kind) : null,
|
|
621
|
-
locations: state?.invocation?.toolLocations().map((loc) => ({
|
|
622
|
-
path: loc.path,
|
|
623
|
-
line: loc.line ?? null,
|
|
624
|
-
})) ?? null,
|
|
625
|
-
rawInput: state?.args,
|
|
626
|
-
});
|
|
627
|
-
// Clean up state
|
|
628
|
-
subAgentToolStates.delete(event.callId);
|
|
629
|
-
};
|
|
630
|
-
// Listen for permission requests
|
|
631
|
-
const onToolWaitingApproval = async (...args) => {
|
|
632
|
-
const event = args[0];
|
|
633
|
-
if (abortSignal.aborted)
|
|
634
|
-
return;
|
|
635
|
-
const state = subAgentToolStates.get(event.callId);
|
|
636
|
-
const content = [];
|
|
637
|
-
// Handle different confirmation types
|
|
638
|
-
if (event.confirmationDetails.type === 'edit') {
|
|
639
|
-
const editDetails = event.confirmationDetails;
|
|
640
|
-
content.push({
|
|
641
|
-
type: 'diff',
|
|
642
|
-
path: editDetails.fileName,
|
|
643
|
-
oldText: editDetails.originalContent ?? '',
|
|
644
|
-
newText: editDetails.newContent,
|
|
645
|
-
});
|
|
646
|
-
}
|
|
647
|
-
// Build permission request options from confirmation details
|
|
648
|
-
// event.confirmationDetails already contains all fields except onConfirm,
|
|
649
|
-
// which we add here to satisfy the type requirement for toPermissionOptions
|
|
650
|
-
const fullConfirmationDetails = {
|
|
651
|
-
...event.confirmationDetails,
|
|
652
|
-
onConfirm: async () => {
|
|
653
|
-
// This is a placeholder - the actual response is handled via event.respond
|
|
654
|
-
},
|
|
655
|
-
};
|
|
656
|
-
const params = {
|
|
657
|
-
sessionId: this.id,
|
|
658
|
-
options: toPermissionOptions(fullConfirmationDetails),
|
|
659
|
-
toolCall: {
|
|
660
|
-
toolCallId: event.callId,
|
|
661
|
-
status: 'pending',
|
|
662
|
-
title: event.description || event.name,
|
|
663
|
-
content,
|
|
664
|
-
locations: state?.invocation?.toolLocations().map((loc) => ({
|
|
665
|
-
path: loc.path,
|
|
666
|
-
line: loc.line ?? null,
|
|
667
|
-
})) ?? [],
|
|
668
|
-
kind: state?.tool ? this.mapToolKind(state.tool.kind) : 'other',
|
|
669
|
-
rawInput: state?.args,
|
|
670
|
-
},
|
|
671
|
-
};
|
|
672
|
-
try {
|
|
673
|
-
// Request permission from zed client
|
|
674
|
-
const output = await this.client.requestPermission(params);
|
|
675
|
-
const outcome = output.outcome.outcome === 'cancelled'
|
|
676
|
-
? ToolConfirmationOutcome.Cancel
|
|
677
|
-
: z
|
|
678
|
-
.nativeEnum(ToolConfirmationOutcome)
|
|
679
|
-
.parse(output.outcome.optionId);
|
|
680
|
-
// Respond to subagent with the outcome
|
|
681
|
-
await event.respond(outcome);
|
|
682
|
-
}
|
|
683
|
-
catch (error) {
|
|
684
|
-
// If permission request fails, cancel the tool call
|
|
685
|
-
console.error(`Permission request failed for subagent tool ${event.name}:`, error);
|
|
686
|
-
await event.respond(ToolConfirmationOutcome.Cancel);
|
|
687
|
-
}
|
|
688
|
-
};
|
|
689
|
-
// Register event listeners
|
|
690
|
-
eventEmitter.on(SubAgentEventType.TOOL_CALL, onToolCall);
|
|
691
|
-
eventEmitter.on(SubAgentEventType.TOOL_RESULT, onToolResult);
|
|
692
|
-
eventEmitter.on(SubAgentEventType.TOOL_WAITING_APPROVAL, onToolWaitingApproval);
|
|
693
|
-
// Return cleanup functions
|
|
694
|
-
cleanupFunctions.push(() => {
|
|
695
|
-
eventEmitter.off(SubAgentEventType.TOOL_CALL, onToolCall);
|
|
696
|
-
eventEmitter.off(SubAgentEventType.TOOL_RESULT, onToolResult);
|
|
697
|
-
eventEmitter.off(SubAgentEventType.TOOL_WAITING_APPROVAL, onToolWaitingApproval);
|
|
698
|
-
});
|
|
699
|
-
return cleanupFunctions;
|
|
700
|
-
}
|
|
701
|
-
/**
|
|
702
|
-
* Maps core Tool Kind enum to ACP ToolKind string literals.
|
|
703
|
-
*
|
|
704
|
-
* @param kind - The core Kind enum value
|
|
705
|
-
* @returns The corresponding ACP ToolKind string literal
|
|
706
|
-
*/
|
|
707
|
-
mapToolKind(kind) {
|
|
708
|
-
const kindMap = {
|
|
709
|
-
[Kind.Read]: 'read',
|
|
710
|
-
[Kind.Edit]: 'edit',
|
|
711
|
-
[Kind.Delete]: 'delete',
|
|
712
|
-
[Kind.Move]: 'move',
|
|
713
|
-
[Kind.Search]: 'search',
|
|
714
|
-
[Kind.Execute]: 'execute',
|
|
715
|
-
[Kind.Think]: 'think',
|
|
716
|
-
[Kind.Fetch]: 'fetch',
|
|
717
|
-
[Kind.Other]: 'other',
|
|
718
|
-
};
|
|
719
|
-
return kindMap[kind] ?? 'other';
|
|
720
|
-
}
|
|
721
|
-
async #resolvePrompt(message, abortSignal) {
|
|
722
|
-
const FILE_URI_SCHEME = 'file://';
|
|
723
|
-
const embeddedContext = [];
|
|
724
|
-
const parts = message.map((part) => {
|
|
725
|
-
switch (part.type) {
|
|
726
|
-
case 'text':
|
|
727
|
-
return { text: part.text };
|
|
728
|
-
case 'image':
|
|
729
|
-
case 'audio':
|
|
730
|
-
return {
|
|
731
|
-
inlineData: {
|
|
732
|
-
mimeType: part.mimeType,
|
|
733
|
-
data: part.data,
|
|
734
|
-
},
|
|
735
|
-
};
|
|
736
|
-
case 'resource_link': {
|
|
737
|
-
if (part.uri.startsWith(FILE_URI_SCHEME)) {
|
|
738
|
-
return {
|
|
739
|
-
fileData: {
|
|
740
|
-
mimeData: part.mimeType,
|
|
741
|
-
name: part.name,
|
|
742
|
-
fileUri: part.uri.slice(FILE_URI_SCHEME.length),
|
|
743
|
-
},
|
|
744
|
-
};
|
|
745
|
-
}
|
|
746
|
-
else {
|
|
747
|
-
return { text: `@${part.uri}` };
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
case 'resource': {
|
|
751
|
-
embeddedContext.push(part.resource);
|
|
752
|
-
return { text: `@${part.resource.uri}` };
|
|
753
|
-
}
|
|
754
|
-
default: {
|
|
755
|
-
const unreachable = part;
|
|
756
|
-
throw new Error(`Unexpected chunk type: '${unreachable}'`);
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
});
|
|
760
|
-
const atPathCommandParts = parts.filter((part) => 'fileData' in part);
|
|
761
|
-
if (atPathCommandParts.length === 0 && embeddedContext.length === 0) {
|
|
762
|
-
return parts;
|
|
763
|
-
}
|
|
764
|
-
const atPathToResolvedSpecMap = new Map();
|
|
765
|
-
// Get centralized file discovery service
|
|
766
|
-
const fileDiscovery = this.config.getFileService();
|
|
767
|
-
const respectGitIgnore = this.config.getFileFilteringRespectGitIgnore();
|
|
768
|
-
const pathSpecsToRead = [];
|
|
769
|
-
const contentLabelsForDisplay = [];
|
|
770
|
-
const ignoredPaths = [];
|
|
771
|
-
const toolRegistry = this.config.getToolRegistry();
|
|
772
|
-
const readManyFilesTool = toolRegistry.getTool('read_many_files');
|
|
773
|
-
const globTool = toolRegistry.getTool('glob');
|
|
774
|
-
if (!readManyFilesTool) {
|
|
775
|
-
throw new Error('Error: read_many_files tool not found.');
|
|
776
|
-
}
|
|
777
|
-
for (const atPathPart of atPathCommandParts) {
|
|
778
|
-
const pathName = atPathPart.fileData.fileUri;
|
|
779
|
-
// Check if path should be ignored by git
|
|
780
|
-
if (fileDiscovery.shouldGitIgnoreFile(pathName)) {
|
|
781
|
-
ignoredPaths.push(pathName);
|
|
782
|
-
const reason = respectGitIgnore
|
|
783
|
-
? 'git-ignored and will be skipped'
|
|
784
|
-
: 'ignored by custom patterns';
|
|
785
|
-
console.warn(`Path ${pathName} is ${reason}.`);
|
|
786
|
-
continue;
|
|
787
|
-
}
|
|
788
|
-
let currentPathSpec = pathName;
|
|
789
|
-
let resolvedSuccessfully = false;
|
|
790
|
-
try {
|
|
791
|
-
const absolutePath = path.resolve(this.config.getTargetDir(), pathName);
|
|
792
|
-
if (isWithinRoot(absolutePath, this.config.getTargetDir())) {
|
|
793
|
-
const stats = await fs.stat(absolutePath);
|
|
794
|
-
if (stats.isDirectory()) {
|
|
795
|
-
currentPathSpec = pathName.endsWith('/')
|
|
796
|
-
? `${pathName}**`
|
|
797
|
-
: `${pathName}/**`;
|
|
798
|
-
this.debug(`Path ${pathName} resolved to directory, using glob: ${currentPathSpec}`);
|
|
799
|
-
}
|
|
800
|
-
else {
|
|
801
|
-
this.debug(`Path ${pathName} resolved to file: ${currentPathSpec}`);
|
|
802
|
-
}
|
|
803
|
-
resolvedSuccessfully = true;
|
|
804
|
-
}
|
|
805
|
-
else {
|
|
806
|
-
this.debug(`Path ${pathName} is outside the project directory. Skipping.`);
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
catch (error) {
|
|
810
|
-
if (isNodeError(error) && error.code === 'ENOENT') {
|
|
811
|
-
if (this.config.getEnableRecursiveFileSearch() && globTool) {
|
|
812
|
-
this.debug(`Path ${pathName} not found directly, attempting glob search.`);
|
|
813
|
-
try {
|
|
814
|
-
const globResult = await globTool.buildAndExecute({
|
|
815
|
-
pattern: `**/*${pathName}*`,
|
|
816
|
-
path: this.config.getTargetDir(),
|
|
817
|
-
}, abortSignal);
|
|
818
|
-
if (globResult.llmContent &&
|
|
819
|
-
typeof globResult.llmContent === 'string' &&
|
|
820
|
-
!globResult.llmContent.startsWith('No files found') &&
|
|
821
|
-
!globResult.llmContent.startsWith('Error:')) {
|
|
822
|
-
const lines = globResult.llmContent.split('\n');
|
|
823
|
-
if (lines.length > 1 && lines[1]) {
|
|
824
|
-
const firstMatchAbsolute = lines[1].trim();
|
|
825
|
-
currentPathSpec = path.relative(this.config.getTargetDir(), firstMatchAbsolute);
|
|
826
|
-
this.debug(`Glob search for ${pathName} found ${firstMatchAbsolute}, using relative path: ${currentPathSpec}`);
|
|
827
|
-
resolvedSuccessfully = true;
|
|
828
|
-
}
|
|
829
|
-
else {
|
|
830
|
-
this.debug(`Glob search for '**/*${pathName}*' did not return a usable path. Path ${pathName} will be skipped.`);
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
|
-
else {
|
|
834
|
-
this.debug(`Glob search for '**/*${pathName}*' found no files or an error. Path ${pathName} will be skipped.`);
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
catch (globError) {
|
|
838
|
-
console.error(`Error during glob search for ${pathName}: ${getErrorMessage(globError)}`);
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
else {
|
|
842
|
-
this.debug(`Glob tool not found. Path ${pathName} will be skipped.`);
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
else {
|
|
846
|
-
console.error(`Error stating path ${pathName}. Path ${pathName} will be skipped.`);
|
|
847
|
-
}
|
|
848
|
-
}
|
|
849
|
-
if (resolvedSuccessfully) {
|
|
850
|
-
pathSpecsToRead.push(currentPathSpec);
|
|
851
|
-
atPathToResolvedSpecMap.set(pathName, currentPathSpec);
|
|
852
|
-
contentLabelsForDisplay.push(pathName);
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
// Construct the initial part of the query for the LLM
|
|
856
|
-
let initialQueryText = '';
|
|
857
|
-
for (let i = 0; i < parts.length; i++) {
|
|
858
|
-
const chunk = parts[i];
|
|
859
|
-
if ('text' in chunk) {
|
|
860
|
-
initialQueryText += chunk.text;
|
|
861
|
-
}
|
|
862
|
-
else {
|
|
863
|
-
// type === 'atPath'
|
|
864
|
-
const resolvedSpec = chunk.fileData && atPathToResolvedSpecMap.get(chunk.fileData.fileUri);
|
|
865
|
-
if (i > 0 &&
|
|
866
|
-
initialQueryText.length > 0 &&
|
|
867
|
-
!initialQueryText.endsWith(' ') &&
|
|
868
|
-
resolvedSpec) {
|
|
869
|
-
// Add space if previous part was text and didn't end with space, or if previous was @path
|
|
870
|
-
const prevPart = parts[i - 1];
|
|
871
|
-
if ('text' in prevPart ||
|
|
872
|
-
('fileData' in prevPart &&
|
|
873
|
-
atPathToResolvedSpecMap.has(prevPart.fileData.fileUri))) {
|
|
874
|
-
initialQueryText += ' ';
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
if (resolvedSpec) {
|
|
878
|
-
initialQueryText += `@${resolvedSpec}`;
|
|
879
|
-
}
|
|
880
|
-
else {
|
|
881
|
-
// If not resolved for reading (e.g. lone @ or invalid path that was skipped),
|
|
882
|
-
// add the original @-string back, ensuring spacing if it's not the first element.
|
|
883
|
-
if (i > 0 &&
|
|
884
|
-
initialQueryText.length > 0 &&
|
|
885
|
-
!initialQueryText.endsWith(' ') &&
|
|
886
|
-
!chunk.fileData?.fileUri.startsWith(' ')) {
|
|
887
|
-
initialQueryText += ' ';
|
|
888
|
-
}
|
|
889
|
-
if (chunk.fileData?.fileUri) {
|
|
890
|
-
initialQueryText += `@${chunk.fileData.fileUri}`;
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
initialQueryText = initialQueryText.trim();
|
|
896
|
-
// Inform user about ignored paths
|
|
897
|
-
if (ignoredPaths.length > 0) {
|
|
898
|
-
const ignoreType = respectGitIgnore ? 'git-ignored' : 'custom-ignored';
|
|
899
|
-
this.debug(`Ignored ${ignoredPaths.length} ${ignoreType} files: ${ignoredPaths.join(', ')}`);
|
|
900
|
-
}
|
|
901
|
-
const processedQueryParts = [{ text: initialQueryText }];
|
|
902
|
-
if (pathSpecsToRead.length === 0 && embeddedContext.length === 0) {
|
|
903
|
-
// Fallback for lone "@" or completely invalid @-commands resulting in empty initialQueryText
|
|
904
|
-
console.warn('No valid file paths found in @ commands to read.');
|
|
905
|
-
return [{ text: initialQueryText }];
|
|
906
|
-
}
|
|
907
|
-
if (pathSpecsToRead.length > 0) {
|
|
908
|
-
const toolArgs = {
|
|
909
|
-
paths: pathSpecsToRead,
|
|
910
|
-
respectGitIgnore, // Use configuration setting
|
|
911
|
-
};
|
|
912
|
-
const callId = `${readManyFilesTool.name}-${Date.now()}`;
|
|
913
|
-
try {
|
|
914
|
-
const invocation = readManyFilesTool.build(toolArgs);
|
|
915
|
-
await this.sendUpdate({
|
|
916
|
-
sessionUpdate: 'tool_call',
|
|
917
|
-
toolCallId: callId,
|
|
918
|
-
status: 'in_progress',
|
|
919
|
-
title: invocation.getDescription(),
|
|
920
|
-
content: [],
|
|
921
|
-
locations: invocation.toolLocations(),
|
|
922
|
-
kind: readManyFilesTool.kind,
|
|
923
|
-
});
|
|
924
|
-
const result = await invocation.execute(abortSignal);
|
|
925
|
-
const content = toToolCallContent(result) || {
|
|
926
|
-
type: 'content',
|
|
927
|
-
content: {
|
|
928
|
-
type: 'text',
|
|
929
|
-
text: `Successfully read: ${contentLabelsForDisplay.join(', ')}`,
|
|
930
|
-
},
|
|
931
|
-
};
|
|
932
|
-
await this.sendUpdate({
|
|
933
|
-
sessionUpdate: 'tool_call_update',
|
|
934
|
-
toolCallId: callId,
|
|
935
|
-
status: 'completed',
|
|
936
|
-
content: content ? [content] : [],
|
|
937
|
-
});
|
|
938
|
-
if (Array.isArray(result.llmContent)) {
|
|
939
|
-
const fileContentRegex = /^--- (.*?) ---\n\n([\s\S]*?)\n\n$/;
|
|
940
|
-
processedQueryParts.push({
|
|
941
|
-
text: '\n--- Content from referenced files ---',
|
|
942
|
-
});
|
|
943
|
-
for (const part of result.llmContent) {
|
|
944
|
-
if (typeof part === 'string') {
|
|
945
|
-
const match = fileContentRegex.exec(part);
|
|
946
|
-
if (match) {
|
|
947
|
-
const filePathSpecInContent = match[1]; // This is a resolved pathSpec
|
|
948
|
-
const fileActualContent = match[2].trim();
|
|
949
|
-
processedQueryParts.push({
|
|
950
|
-
text: `\nContent from @${filePathSpecInContent}:\n`,
|
|
951
|
-
});
|
|
952
|
-
processedQueryParts.push({ text: fileActualContent });
|
|
953
|
-
}
|
|
954
|
-
else {
|
|
955
|
-
processedQueryParts.push({ text: part });
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
else {
|
|
959
|
-
// part is a Part object.
|
|
960
|
-
processedQueryParts.push(part);
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
else {
|
|
965
|
-
console.warn('read_many_files tool returned no content or empty content.');
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
catch (error) {
|
|
969
|
-
await this.sendUpdate({
|
|
970
|
-
sessionUpdate: 'tool_call_update',
|
|
971
|
-
toolCallId: callId,
|
|
972
|
-
status: 'failed',
|
|
973
|
-
content: [
|
|
974
|
-
{
|
|
975
|
-
type: 'content',
|
|
976
|
-
content: {
|
|
977
|
-
type: 'text',
|
|
978
|
-
text: `Error reading files (${contentLabelsForDisplay.join(', ')}): ${getErrorMessage(error)}`,
|
|
979
|
-
},
|
|
980
|
-
},
|
|
981
|
-
],
|
|
982
|
-
});
|
|
983
|
-
throw error;
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
if (embeddedContext.length > 0) {
|
|
987
|
-
processedQueryParts.push({
|
|
988
|
-
text: '\n--- Content from referenced context ---',
|
|
989
|
-
});
|
|
990
|
-
for (const contextPart of embeddedContext) {
|
|
991
|
-
processedQueryParts.push({
|
|
992
|
-
text: `\nContent from @${contextPart.uri}:\n`,
|
|
993
|
-
});
|
|
994
|
-
if ('text' in contextPart) {
|
|
995
|
-
processedQueryParts.push({
|
|
996
|
-
text: contextPart.text,
|
|
997
|
-
});
|
|
998
|
-
}
|
|
999
|
-
else {
|
|
1000
|
-
processedQueryParts.push({
|
|
1001
|
-
inlineData: {
|
|
1002
|
-
mimeType: contextPart.mimeType ?? 'application/octet-stream',
|
|
1003
|
-
data: contextPart.blob,
|
|
1004
|
-
},
|
|
1005
|
-
});
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
return processedQueryParts;
|
|
1010
|
-
}
|
|
1011
|
-
debug(msg) {
|
|
1012
|
-
if (this.config.getDebugMode()) {
|
|
1013
|
-
console.warn(msg);
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
/**
|
|
1018
|
-
* Converts todo items to plan entries format for zed integration.
|
|
1019
|
-
* Maps todo status to plan status and assigns a default priority.
|
|
1020
|
-
*
|
|
1021
|
-
* @param todos - Array of todo items with id, content, and status
|
|
1022
|
-
* @returns Array of plan entries with content, priority, and status
|
|
1023
|
-
*/
|
|
1024
|
-
function convertTodosToPlanEntries(todos) {
|
|
1025
|
-
return todos.map((todo) => ({
|
|
1026
|
-
content: todo.content,
|
|
1027
|
-
priority: 'medium', // Default priority since todos don't have priority
|
|
1028
|
-
status: todo.status,
|
|
1029
|
-
}));
|
|
1030
|
-
}
|
|
1031
|
-
function toToolCallContent(toolResult) {
|
|
1032
|
-
if (toolResult.error?.message) {
|
|
1033
|
-
throw new Error(toolResult.error.message);
|
|
1034
|
-
}
|
|
1035
|
-
if (toolResult.returnDisplay) {
|
|
1036
|
-
if (typeof toolResult.returnDisplay === 'string') {
|
|
1037
|
-
return {
|
|
1038
|
-
type: 'content',
|
|
1039
|
-
content: { type: 'text', text: toolResult.returnDisplay },
|
|
1040
|
-
};
|
|
1041
|
-
}
|
|
1042
|
-
else if ('type' in toolResult.returnDisplay &&
|
|
1043
|
-
toolResult.returnDisplay.type === 'plan_summary') {
|
|
1044
|
-
const planDisplay = toolResult.returnDisplay;
|
|
1045
|
-
const planText = `${planDisplay.message}\n\n${planDisplay.plan}`;
|
|
1046
|
-
return {
|
|
1047
|
-
type: 'content',
|
|
1048
|
-
content: { type: 'text', text: planText },
|
|
1049
|
-
};
|
|
1050
|
-
}
|
|
1051
|
-
else {
|
|
1052
|
-
if ('fileName' in toolResult.returnDisplay) {
|
|
1053
|
-
return {
|
|
1054
|
-
type: 'diff',
|
|
1055
|
-
path: toolResult.returnDisplay.fileName,
|
|
1056
|
-
oldText: toolResult.returnDisplay.originalContent,
|
|
1057
|
-
newText: toolResult.returnDisplay.newContent,
|
|
1058
|
-
};
|
|
1059
|
-
}
|
|
1060
|
-
return null;
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
return null;
|
|
1064
|
-
}
|
|
1065
|
-
const basicPermissionOptions = [
|
|
1066
|
-
{
|
|
1067
|
-
optionId: ToolConfirmationOutcome.ProceedOnce,
|
|
1068
|
-
name: 'Allow',
|
|
1069
|
-
kind: 'allow_once',
|
|
1070
|
-
},
|
|
1071
|
-
{
|
|
1072
|
-
optionId: ToolConfirmationOutcome.Cancel,
|
|
1073
|
-
name: 'Reject',
|
|
1074
|
-
kind: 'reject_once',
|
|
1075
|
-
},
|
|
1076
|
-
];
|
|
1077
|
-
function toPermissionOptions(confirmation) {
|
|
1078
|
-
switch (confirmation.type) {
|
|
1079
|
-
case 'edit':
|
|
1080
|
-
return [
|
|
1081
|
-
{
|
|
1082
|
-
optionId: ToolConfirmationOutcome.ProceedAlways,
|
|
1083
|
-
name: 'Allow All Edits',
|
|
1084
|
-
kind: 'allow_always',
|
|
1085
|
-
},
|
|
1086
|
-
...basicPermissionOptions,
|
|
1087
|
-
];
|
|
1088
|
-
case 'exec':
|
|
1089
|
-
return [
|
|
1090
|
-
{
|
|
1091
|
-
optionId: ToolConfirmationOutcome.ProceedAlways,
|
|
1092
|
-
name: `Always Allow ${confirmation.rootCommand}`,
|
|
1093
|
-
kind: 'allow_always',
|
|
1094
|
-
},
|
|
1095
|
-
...basicPermissionOptions,
|
|
1096
|
-
];
|
|
1097
|
-
case 'mcp':
|
|
1098
|
-
return [
|
|
1099
|
-
{
|
|
1100
|
-
optionId: ToolConfirmationOutcome.ProceedAlwaysServer,
|
|
1101
|
-
name: `Always Allow ${confirmation.serverName}`,
|
|
1102
|
-
kind: 'allow_always',
|
|
1103
|
-
},
|
|
1104
|
-
{
|
|
1105
|
-
optionId: ToolConfirmationOutcome.ProceedAlwaysTool,
|
|
1106
|
-
name: `Always Allow ${confirmation.toolName}`,
|
|
1107
|
-
kind: 'allow_always',
|
|
1108
|
-
},
|
|
1109
|
-
...basicPermissionOptions,
|
|
1110
|
-
];
|
|
1111
|
-
case 'info':
|
|
1112
|
-
return [
|
|
1113
|
-
{
|
|
1114
|
-
optionId: ToolConfirmationOutcome.ProceedAlways,
|
|
1115
|
-
name: `Always Allow`,
|
|
1116
|
-
kind: 'allow_always',
|
|
1117
|
-
},
|
|
1118
|
-
...basicPermissionOptions,
|
|
1119
|
-
];
|
|
1120
|
-
case 'plan':
|
|
1121
|
-
return [
|
|
1122
|
-
{
|
|
1123
|
-
optionId: ToolConfirmationOutcome.ProceedAlways,
|
|
1124
|
-
name: `Always Allow Plans`,
|
|
1125
|
-
kind: 'allow_always',
|
|
1126
|
-
},
|
|
1127
|
-
...basicPermissionOptions,
|
|
1128
|
-
];
|
|
1129
|
-
default: {
|
|
1130
|
-
const unreachable = confirmation;
|
|
1131
|
-
throw new Error(`Unexpected: ${unreachable}`);
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
}
|
|
1135
|
-
//# sourceMappingURL=zedIntegration.js.map
|