@umsai/ums-code 0.1.4-v2 → 0.3.0-v2
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 +11 -3
- package/dist/src/config/config.d.ts +6 -0
- package/dist/src/config/config.js +102 -11
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/settings.d.ts +5 -0
- package/dist/src/config/settings.js +15 -1
- package/dist/src/config/settings.js.map +1 -1
- package/dist/src/config/settingsSchema.d.ts +75 -17
- package/dist/src/config/settingsSchema.js +57 -17
- package/dist/src/config/settingsSchema.js.map +1 -1
- package/dist/src/config/webSearch.d.ts +35 -0
- package/dist/src/config/webSearch.js +87 -0
- package/dist/src/config/webSearch.js.map +1 -0
- package/dist/src/core/auth.d.ts +1 -1
- package/dist/src/core/auth.js +19 -3
- package/dist/src/core/auth.js.map +1 -1
- package/dist/src/core/initializer.js +13 -2
- package/dist/src/core/initializer.js.map +1 -1
- package/dist/src/core/theme.js +4 -1
- package/dist/src/core/theme.js.map +1 -1
- package/dist/src/gemini.d.ts +1 -1
- package/dist/src/gemini.js +53 -44
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/gemini.test.js +117 -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 +18 -0
- package/dist/src/i18n/index.js +184 -0
- package/dist/src/i18n/index.js.map +1 -0
- package/dist/src/i18n/locales/en.js +1129 -0
- package/dist/src/i18n/locales/zh.js +1052 -0
- package/dist/src/nonInteractive/control/ControlContext.d.ts +63 -0
- package/dist/src/nonInteractive/control/ControlContext.js +31 -0
- package/dist/src/nonInteractive/control/ControlContext.js.map +1 -0
- package/dist/src/nonInteractive/control/ControlDispatcher.d.ts +86 -0
- package/dist/src/nonInteractive/control/ControlDispatcher.js +238 -0
- package/dist/src/nonInteractive/control/ControlDispatcher.js.map +1 -0
- package/dist/src/nonInteractive/control/ControlDispatcher.test.d.ts +6 -0
- package/dist/src/nonInteractive/control/ControlDispatcher.test.js +549 -0
- package/dist/src/nonInteractive/control/ControlDispatcher.test.js.map +1 -0
- package/dist/src/nonInteractive/control/ControlService.d.ts +78 -0
- package/dist/src/nonInteractive/control/ControlService.js +154 -0
- package/dist/src/nonInteractive/control/ControlService.js.map +1 -0
- package/dist/src/nonInteractive/control/controllers/baseController.d.ts +50 -0
- package/dist/src/nonInteractive/control/controllers/baseController.js +102 -0
- package/dist/src/nonInteractive/control/controllers/baseController.js.map +1 -0
- package/dist/src/nonInteractive/control/controllers/hookController.d.ts +25 -0
- package/dist/src/nonInteractive/control/controllers/hookController.js +42 -0
- package/dist/src/nonInteractive/control/controllers/hookController.js.map +1 -0
- package/dist/src/nonInteractive/control/controllers/mcpController.d.ts +42 -0
- package/dist/src/nonInteractive/control/controllers/mcpController.js +205 -0
- package/dist/src/nonInteractive/control/controllers/mcpController.js.map +1 -0
- package/dist/src/nonInteractive/control/controllers/permissionController.d.ts +78 -0
- package/dist/src/nonInteractive/control/controllers/permissionController.js +358 -0
- package/dist/src/nonInteractive/control/controllers/permissionController.js.map +1 -0
- package/dist/src/nonInteractive/control/controllers/systemController.d.ts +56 -0
- package/dist/src/nonInteractive/control/controllers/systemController.js +166 -0
- package/dist/src/nonInteractive/control/controllers/systemController.js.map +1 -0
- package/dist/src/nonInteractive/control/types/serviceAPIs.d.ts +120 -0
- package/dist/src/nonInteractive/control/types/serviceAPIs.js +7 -0
- package/dist/src/nonInteractive/control/types/serviceAPIs.js.map +1 -0
- package/dist/src/nonInteractive/io/BaseJsonOutputAdapter.d.ts +446 -0
- package/dist/src/nonInteractive/io/BaseJsonOutputAdapter.js +891 -0
- package/dist/src/nonInteractive/io/BaseJsonOutputAdapter.js.map +1 -0
- package/dist/src/nonInteractive/io/BaseJsonOutputAdapter.test.d.ts +6 -0
- package/dist/src/nonInteractive/io/BaseJsonOutputAdapter.test.js +1197 -0
- package/dist/src/nonInteractive/io/BaseJsonOutputAdapter.test.js.map +1 -0
- package/dist/src/nonInteractive/io/JsonOutputAdapter.d.ts +29 -0
- package/dist/src/nonInteractive/io/JsonOutputAdapter.js +56 -0
- package/dist/src/nonInteractive/io/JsonOutputAdapter.js.map +1 -0
- package/dist/src/nonInteractive/io/JsonOutputAdapter.test.d.ts +6 -0
- package/dist/src/nonInteractive/io/JsonOutputAdapter.test.js +624 -0
- package/dist/src/nonInteractive/io/JsonOutputAdapter.test.js.map +1 -0
- package/dist/src/nonInteractive/io/StreamJsonInputReader.d.ts +16 -0
- package/dist/src/nonInteractive/io/StreamJsonInputReader.js +54 -0
- package/dist/src/nonInteractive/io/StreamJsonInputReader.js.map +1 -0
- package/dist/src/nonInteractive/io/StreamJsonInputReader.test.d.ts +6 -0
- package/dist/src/nonInteractive/io/StreamJsonInputReader.test.js +178 -0
- package/dist/src/nonInteractive/io/StreamJsonInputReader.test.js.map +1 -0
- package/dist/src/nonInteractive/io/StreamJsonOutputAdapter.d.ts +69 -0
- package/dist/src/nonInteractive/io/StreamJsonOutputAdapter.js +185 -0
- package/dist/src/nonInteractive/io/StreamJsonOutputAdapter.js.map +1 -0
- package/dist/src/nonInteractive/io/StreamJsonOutputAdapter.test.d.ts +6 -0
- package/dist/src/nonInteractive/io/StreamJsonOutputAdapter.test.js +808 -0
- package/dist/src/nonInteractive/io/StreamJsonOutputAdapter.test.js.map +1 -0
- package/dist/src/nonInteractive/session.d.ts +23 -0
- package/dist/src/nonInteractive/session.js +549 -0
- package/dist/src/nonInteractive/session.js.map +1 -0
- package/dist/src/nonInteractive/session.test.d.ts +6 -0
- package/dist/src/nonInteractive/session.test.js +407 -0
- package/dist/src/nonInteractive/session.test.js.map +1 -0
- package/dist/src/nonInteractive/types.d.ts +344 -0
- package/dist/src/nonInteractive/types.js +78 -0
- package/dist/src/nonInteractive/types.js.map +1 -0
- package/dist/src/nonInteractiveCli.d.ts +21 -1
- package/dist/src/nonInteractiveCli.js +204 -57
- package/dist/src/nonInteractiveCli.js.map +1 -1
- package/dist/src/nonInteractiveCliCommands.d.ts +19 -1
- package/dist/src/nonInteractiveCliCommands.js +69 -4
- package/dist/src/nonInteractiveCliCommands.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.js +2 -0
- package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
- package/dist/src/services/FileCommandLoader.js +4 -1
- package/dist/src/services/FileCommandLoader.js.map +1 -1
- package/dist/src/ui/AppContainer.js +41 -38
- package/dist/src/ui/AppContainer.js.map +1 -1
- package/dist/src/ui/auth/AuthDialog.d.ts +1 -13
- package/dist/src/ui/auth/AuthDialog.js +41 -111
- package/dist/src/ui/auth/AuthDialog.js.map +1 -1
- package/dist/src/ui/auth/AuthDialog.test.js +49 -19
- package/dist/src/ui/auth/AuthDialog.test.js.map +1 -1
- package/dist/src/ui/auth/AuthInProgress.js +2 -1
- package/dist/src/ui/auth/AuthInProgress.js.map +1 -1
- package/dist/src/ui/auth/useAuth.d.ts +9 -8
- package/dist/src/ui/auth/useAuth.js +135 -63
- package/dist/src/ui/auth/useAuth.js.map +1 -1
- package/dist/src/ui/commands/aboutCommand.js +7 -31
- package/dist/src/ui/commands/aboutCommand.js.map +1 -1
- package/dist/src/ui/commands/agentsCommand.js +10 -3
- package/dist/src/ui/commands/agentsCommand.js.map +1 -1
- package/dist/src/ui/commands/approvalModeCommand.js +8 -329
- package/dist/src/ui/commands/approvalModeCommand.js.map +1 -1
- package/dist/src/ui/commands/approvalModeCommand.test.js +19 -263
- package/dist/src/ui/commands/approvalModeCommand.test.js.map +1 -1
- package/dist/src/ui/commands/authCommand.js +4 -1
- package/dist/src/ui/commands/authCommand.js.map +1 -1
- package/dist/src/ui/commands/bugCommand.js +13 -47
- package/dist/src/ui/commands/bugCommand.js.map +1 -1
- package/dist/src/ui/commands/chatCommand.js +51 -25
- package/dist/src/ui/commands/chatCommand.js.map +1 -1
- package/dist/src/ui/commands/clearCommand.js +6 -3
- package/dist/src/ui/commands/clearCommand.js.map +1 -1
- package/dist/src/ui/commands/compressCommand.js +9 -4
- package/dist/src/ui/commands/compressCommand.js.map +1 -1
- package/dist/src/ui/commands/copyCommand.js +4 -1
- package/dist/src/ui/commands/copyCommand.js.map +1 -1
- package/dist/src/ui/commands/directoryCommand.js +30 -12
- package/dist/src/ui/commands/directoryCommand.js.map +1 -1
- package/dist/src/ui/commands/docsCommand.js +9 -3
- package/dist/src/ui/commands/docsCommand.js.map +1 -1
- package/dist/src/ui/commands/editorCommand.js +4 -1
- package/dist/src/ui/commands/editorCommand.js.map +1 -1
- package/dist/src/ui/commands/extensionsCommand.js +10 -3
- package/dist/src/ui/commands/extensionsCommand.js.map +1 -1
- package/dist/src/ui/commands/helpCommand.js +4 -1
- package/dist/src/ui/commands/helpCommand.js.map +1 -1
- package/dist/src/ui/commands/ideCommand.js +23 -7
- package/dist/src/ui/commands/ideCommand.js.map +1 -1
- package/dist/src/ui/commands/initCommand.js +5 -3
- package/dist/src/ui/commands/initCommand.js.map +1 -1
- package/dist/src/ui/commands/languageCommand.d.ts +7 -0
- package/dist/src/ui/commands/languageCommand.js +386 -0
- package/dist/src/ui/commands/languageCommand.js.map +1 -0
- package/dist/src/ui/commands/mcpCommand.js +38 -18
- package/dist/src/ui/commands/mcpCommand.js.map +1 -1
- package/dist/src/ui/commands/memoryCommand.js +54 -25
- package/dist/src/ui/commands/memoryCommand.js.map +1 -1
- package/dist/src/ui/commands/modelCommand.js +9 -4
- package/dist/src/ui/commands/modelCommand.js.map +1 -1
- package/dist/src/ui/commands/permissionsCommand.js +4 -1
- package/dist/src/ui/commands/permissionsCommand.js.map +1 -1
- package/dist/src/ui/commands/quitCommand.js +7 -2
- package/dist/src/ui/commands/quitCommand.js.map +1 -1
- package/dist/src/ui/commands/settingsCommand.js +4 -1
- package/dist/src/ui/commands/settingsCommand.js.map +1 -1
- package/dist/src/ui/commands/setupGithubCommand.js +4 -1
- package/dist/src/ui/commands/setupGithubCommand.js.map +1 -1
- package/dist/src/ui/commands/statsCommand.js +11 -4
- package/dist/src/ui/commands/statsCommand.js.map +1 -1
- package/dist/src/ui/commands/summaryCommand.js +15 -8
- package/dist/src/ui/commands/summaryCommand.js.map +1 -1
- package/dist/src/ui/commands/terminalSetupCommand.js +9 -3
- package/dist/src/ui/commands/terminalSetupCommand.js.map +1 -1
- package/dist/src/ui/commands/themeCommand.js +4 -1
- package/dist/src/ui/commands/themeCommand.js.map +1 -1
- package/dist/src/ui/commands/toolsCommand.js +5 -2
- package/dist/src/ui/commands/toolsCommand.js.map +1 -1
- package/dist/src/ui/commands/types.d.ts +1 -1
- package/dist/src/ui/commands/types.js.map +1 -1
- package/dist/src/ui/commands/vimCommand.js +4 -1
- package/dist/src/ui/commands/vimCommand.js.map +1 -1
- package/dist/src/ui/components/AboutBox.d.ts +2 -9
- package/dist/src/ui/components/AboutBox.js +6 -2
- package/dist/src/ui/components/AboutBox.js.map +1 -1
- package/dist/src/ui/components/ApprovalModeDialog.d.ts +21 -0
- package/dist/src/ui/components/ApprovalModeDialog.js +68 -0
- package/dist/src/ui/components/ApprovalModeDialog.js.map +1 -0
- package/dist/src/ui/components/AutoAcceptIndicator.js +7 -6
- package/dist/src/ui/components/AutoAcceptIndicator.js.map +1 -1
- package/dist/src/ui/components/Composer.js +4 -3
- package/dist/src/ui/components/Composer.js.map +1 -1
- package/dist/src/ui/components/ConfigInitDisplay.js +7 -3
- package/dist/src/ui/components/ConfigInitDisplay.js.map +1 -1
- package/dist/src/ui/components/ContextSummaryDisplay.js +33 -9
- package/dist/src/ui/components/ContextSummaryDisplay.js.map +1 -1
- package/dist/src/ui/components/DialogManager.js +60 -13
- package/dist/src/ui/components/DialogManager.js.map +1 -1
- package/dist/src/ui/components/EditorSettingsDialog.js +8 -3
- package/dist/src/ui/components/EditorSettingsDialog.js.map +1 -1
- package/dist/src/ui/components/Help.js +14 -4
- package/dist/src/ui/components/Help.js.map +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.js +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.js.map +1 -1
- package/dist/src/ui/components/HistoryItemDisplay.test.js +17 -8
- package/dist/src/ui/components/HistoryItemDisplay.test.js.map +1 -1
- package/dist/src/ui/components/InputPrompt.js +10 -136
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/LoadingIndicator.js +6 -1
- package/dist/src/ui/components/LoadingIndicator.js.map +1 -1
- package/dist/src/ui/components/ModelDialog.js +2 -1
- package/dist/src/ui/components/ModelDialog.js.map +1 -1
- package/dist/src/ui/components/ModelStatsDisplay.js +6 -5
- package/dist/src/ui/components/ModelStatsDisplay.js.map +1 -1
- package/dist/src/ui/components/OpenAIKeyPrompt.d.ts +19 -1
- package/dist/src/ui/components/OpenAIKeyPrompt.js +38 -6
- package/dist/src/ui/components/OpenAIKeyPrompt.js.map +1 -1
- package/dist/src/ui/components/ProQuotaDialog.js +5 -4
- package/dist/src/ui/components/ProQuotaDialog.js.map +1 -1
- package/dist/src/ui/components/QuitConfirmationDialog.js +6 -5
- package/dist/src/ui/components/QuitConfirmationDialog.js.map +1 -1
- package/dist/src/ui/components/QwenOAuthProgress.d.ts +2 -2
- package/dist/src/ui/components/QwenOAuthProgress.js +14 -7
- package/dist/src/ui/components/QwenOAuthProgress.js.map +1 -1
- package/dist/src/ui/components/QwenOAuthProgress.test.js +1 -0
- package/dist/src/ui/components/QwenOAuthProgress.test.js.map +1 -1
- package/dist/src/ui/components/SessionSummaryDisplay.js +2 -1
- package/dist/src/ui/components/SessionSummaryDisplay.js.map +1 -1
- package/dist/src/ui/components/SettingsDialog.d.ts +3 -1
- package/dist/src/ui/components/SettingsDialog.js +35 -12
- package/dist/src/ui/components/SettingsDialog.js.map +1 -1
- package/dist/src/ui/components/SettingsDialog.test.js +5 -4
- package/dist/src/ui/components/SettingsDialog.test.js.map +1 -1
- package/dist/src/ui/components/ShellConfirmationDialog.js +5 -4
- package/dist/src/ui/components/ShellConfirmationDialog.js.map +1 -1
- package/dist/src/ui/components/StatsDisplay.js +5 -4
- package/dist/src/ui/components/StatsDisplay.js.map +1 -1
- package/dist/src/ui/components/ThemeDialog.js +5 -2
- package/dist/src/ui/components/ThemeDialog.js.map +1 -1
- package/dist/src/ui/components/ToolStatsDisplay.js +3 -2
- package/dist/src/ui/components/ToolStatsDisplay.js.map +1 -1
- package/dist/src/ui/components/WelcomeBackDialog.js +13 -4
- package/dist/src/ui/components/WelcomeBackDialog.js.map +1 -1
- package/dist/src/ui/components/messages/CompressionMessage.js +10 -6
- package/dist/src/ui/components/messages/CompressionMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolConfirmationMessage.js +40 -29
- package/dist/src/ui/components/messages/ToolConfirmationMessage.js.map +1 -1
- package/dist/src/ui/components/shared/BaseSelectionList.test.js +1 -1
- package/dist/src/ui/components/shared/BaseSelectionList.test.js.map +1 -1
- package/dist/src/ui/components/shared/ScopeSelector.js +3 -1
- package/dist/src/ui/components/shared/ScopeSelector.js.map +1 -1
- package/dist/src/ui/components/subagents/create/AgentCreationWizard.js +39 -26
- package/dist/src/ui/components/subagents/create/AgentCreationWizard.js.map +1 -1
- package/dist/src/ui/components/subagents/create/CreationSummary.js +20 -10
- package/dist/src/ui/components/subagents/create/CreationSummary.js.map +1 -1
- package/dist/src/ui/components/subagents/create/DescriptionInput.js +6 -3
- package/dist/src/ui/components/subagents/create/DescriptionInput.js.map +1 -1
- package/dist/src/ui/components/subagents/create/GenerationMethodSelector.js +7 -2
- package/dist/src/ui/components/subagents/create/GenerationMethodSelector.js.map +1 -1
- package/dist/src/ui/components/subagents/create/LocationSelector.js +7 -2
- package/dist/src/ui/components/subagents/create/LocationSelector.js.map +1 -1
- package/dist/src/ui/components/subagents/create/ToolSelector.js +8 -7
- package/dist/src/ui/components/subagents/create/ToolSelector.js.map +1 -1
- package/dist/src/ui/components/subagents/manage/ActionSelectionStep.js +29 -4
- package/dist/src/ui/components/subagents/manage/ActionSelectionStep.js.map +1 -1
- package/dist/src/ui/components/subagents/manage/AgentDeleteStep.js +6 -3
- package/dist/src/ui/components/subagents/manage/AgentDeleteStep.js.map +1 -1
- package/dist/src/ui/components/subagents/manage/AgentEditStep.js +14 -5
- package/dist/src/ui/components/subagents/manage/AgentEditStep.js.map +1 -1
- package/dist/src/ui/components/subagents/manage/AgentSelectionStep.js +13 -6
- package/dist/src/ui/components/subagents/manage/AgentSelectionStep.js.map +1 -1
- package/dist/src/ui/components/subagents/manage/AgentViewerStep.js +3 -2
- package/dist/src/ui/components/subagents/manage/AgentViewerStep.js.map +1 -1
- package/dist/src/ui/components/subagents/manage/AgentsManagerDialog.js +14 -13
- package/dist/src/ui/components/subagents/manage/AgentsManagerDialog.js.map +1 -1
- package/dist/src/ui/components/subagents/runtime/AgentExecutionDisplay.js +8 -8
- package/dist/src/ui/components/views/McpStatus.js +28 -15
- package/dist/src/ui/components/views/McpStatus.js.map +1 -1
- package/dist/src/ui/components/views/ToolsList.js +2 -1
- package/dist/src/ui/components/views/ToolsList.js.map +1 -1
- package/dist/src/ui/contexts/UIActionsContext.d.ts +5 -4
- package/dist/src/ui/contexts/UIActionsContext.js +1 -0
- package/dist/src/ui/contexts/UIActionsContext.js.map +1 -1
- package/dist/src/ui/contexts/UIStateContext.d.ts +5 -7
- package/dist/src/ui/contexts/UIStateContext.js.map +1 -1
- package/dist/src/ui/editors/editorSettingsManager.js +1 -0
- package/dist/src/ui/editors/editorSettingsManager.js.map +1 -1
- package/dist/src/ui/hooks/atCommandProcessor.test.js +2 -0
- package/dist/src/ui/hooks/atCommandProcessor.test.js.map +1 -1
- package/dist/src/ui/hooks/slashCommandProcessor.d.ts +1 -0
- package/dist/src/ui/hooks/slashCommandProcessor.js +4 -7
- package/dist/src/ui/hooks/slashCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/useApprovalModeCommand.d.ts +14 -0
- package/dist/src/ui/hooks/useApprovalModeCommand.js +33 -0
- package/dist/src/ui/hooks/useApprovalModeCommand.js.map +1 -0
- package/dist/src/ui/hooks/useAttentionNotifications.d.ts +14 -0
- package/dist/src/ui/hooks/useAttentionNotifications.js +41 -0
- package/dist/src/ui/hooks/useAttentionNotifications.js.map +1 -0
- package/dist/src/ui/hooks/useAttentionNotifications.test.d.ts +6 -0
- package/dist/src/ui/hooks/useAttentionNotifications.test.js +113 -0
- package/dist/src/ui/hooks/useAttentionNotifications.test.js.map +1 -0
- package/dist/src/ui/hooks/useCommandCompletion.d.ts +1 -4
- package/dist/src/ui/hooks/useCommandCompletion.js +1 -23
- package/dist/src/ui/hooks/useCommandCompletion.js.map +1 -1
- package/dist/src/ui/hooks/useDialogClose.d.ts +6 -3
- package/dist/src/ui/hooks/useDialogClose.js +5 -0
- package/dist/src/ui/hooks/useDialogClose.js.map +1 -1
- package/dist/src/ui/hooks/useGitBranchName.js +6 -3
- package/dist/src/ui/hooks/useGitBranchName.js.map +1 -1
- package/dist/src/ui/hooks/useGitBranchName.test.js +39 -21
- package/dist/src/ui/hooks/useGitBranchName.test.js.map +1 -1
- package/dist/src/ui/hooks/useInitializationAuthError.d.ts +22 -0
- package/dist/src/ui/hooks/useInitializationAuthError.js +40 -0
- package/dist/src/ui/hooks/useInitializationAuthError.js.map +1 -0
- package/dist/src/ui/hooks/usePhraseCycler.js +6 -4
- package/dist/src/ui/hooks/usePhraseCycler.js.map +1 -1
- package/dist/src/ui/hooks/useQwenAuth.d.ts +7 -12
- package/dist/src/ui/hooks/useQwenAuth.js +4 -8
- package/dist/src/ui/hooks/useQwenAuth.js.map +1 -1
- package/dist/src/ui/hooks/useQwenAuth.test.js +72 -98
- package/dist/src/ui/hooks/useQwenAuth.test.js.map +1 -1
- package/dist/src/ui/hooks/useThemeCommand.js +8 -3
- package/dist/src/ui/hooks/useThemeCommand.js.map +1 -1
- package/dist/src/ui/models/availableModels.js +7 -2
- package/dist/src/ui/models/availableModels.js.map +1 -1
- package/dist/src/ui/types.d.ts +32 -14
- package/dist/src/ui/types.js.map +1 -1
- package/dist/src/ui/utils/clipboardUtils.js +3 -3
- package/dist/src/ui/utils/clipboardUtils.js.map +1 -1
- package/dist/src/ui/utils/commandUtils.d.ts +16 -0
- package/dist/src/ui/utils/commandUtils.js +16 -1
- package/dist/src/ui/utils/commandUtils.js.map +1 -1
- package/dist/src/ui/utils/commandUtils.test.js +5 -2
- package/dist/src/ui/utils/commandUtils.test.js.map +1 -1
- package/dist/src/ui/utils/terminalSetup.js +45 -18
- package/dist/src/ui/utils/terminalSetup.js.map +1 -1
- package/dist/src/utils/attentionNotification.d.ts +20 -0
- package/dist/src/utils/attentionNotification.js +34 -0
- package/dist/src/utils/attentionNotification.js.map +1 -0
- package/dist/src/utils/attentionNotification.test.d.ts +6 -0
- package/dist/src/utils/attentionNotification.test.js +46 -0
- package/dist/src/utils/attentionNotification.test.js.map +1 -0
- package/dist/src/utils/dialogScopeUtils.d.ts +0 -4
- package/dist/src/utils/dialogScopeUtils.js +5 -2
- package/dist/src/utils/dialogScopeUtils.js.map +1 -1
- package/dist/src/utils/errors.d.ts +10 -2
- package/dist/src/utils/errors.js +14 -13
- package/dist/src/utils/errors.js.map +1 -1
- package/dist/src/utils/errors.test.js +91 -54
- package/dist/src/utils/errors.test.js.map +1 -1
- package/dist/src/utils/nonInteractiveHelpers.d.ts +88 -0
- package/dist/src/utils/nonInteractiveHelpers.js +470 -0
- package/dist/src/utils/nonInteractiveHelpers.js.map +1 -0
- package/dist/src/utils/nonInteractiveHelpers.test.d.ts +6 -0
- package/dist/src/utils/nonInteractiveHelpers.test.js +945 -0
- package/dist/src/utils/nonInteractiveHelpers.test.js.map +1 -0
- package/dist/src/utils/sandbox.js +1 -1
- package/dist/src/utils/sandbox.js.map +1 -1
- package/dist/src/utils/settingsUtils.js +7 -1
- package/dist/src/utils/settingsUtils.js.map +1 -1
- package/dist/src/utils/systemInfo.d.ts +66 -0
- package/dist/src/utils/systemInfo.js +125 -0
- package/dist/src/utils/systemInfo.js.map +1 -0
- package/dist/src/utils/systemInfo.test.d.ts +6 -0
- package/dist/src/utils/systemInfo.test.js +259 -0
- package/dist/src/utils/systemInfo.test.js.map +1 -0
- package/dist/src/utils/systemInfoFields.d.ts +22 -0
- package/dist/src/utils/systemInfoFields.js +96 -0
- package/dist/src/utils/systemInfoFields.js.map +1 -0
- package/dist/src/utils/userStartupWarnings.js +9 -4
- package/dist/src/utils/userStartupWarnings.js.map +1 -1
- package/dist/src/validateNonInterActiveAuth.js +27 -6
- package/dist/src/validateNonInterActiveAuth.js.map +1 -1
- package/dist/src/zed-integration/acp.js +1 -2
- package/dist/src/zed-integration/acp.js.map +1 -1
- package/dist/src/zed-integration/schema.d.ts +506 -264
- package/dist/src/zed-integration/schema.js +13 -0
- package/dist/src/zed-integration/schema.js.map +1 -1
- package/dist/src/zed-integration/zedIntegration.js +376 -32
- package/dist/src/zed-integration/zedIntegration.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +12 -4
|
@@ -0,0 +1,1197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Qwen Team
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
7
|
+
import { GeminiEventType, } from '@umsai/ums-code-core';
|
|
8
|
+
import { BaseJsonOutputAdapter, partsToString, partsToContentBlock, toolResultContent, extractTextFromBlocks, createExtendedUsage, } from './BaseJsonOutputAdapter.js';
|
|
9
|
+
/**
|
|
10
|
+
* Test implementation of BaseJsonOutputAdapter for unit testing.
|
|
11
|
+
* Captures emitted messages for verification.
|
|
12
|
+
*/
|
|
13
|
+
class TestJsonOutputAdapter extends BaseJsonOutputAdapter {
|
|
14
|
+
emittedMessages = [];
|
|
15
|
+
emitMessageImpl(message) {
|
|
16
|
+
this.emittedMessages.push(message);
|
|
17
|
+
}
|
|
18
|
+
shouldEmitStreamEvents() {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
finalizeAssistantMessage() {
|
|
22
|
+
return this.finalizeAssistantMessageInternal(this.mainAgentMessageState, null);
|
|
23
|
+
}
|
|
24
|
+
emitResult(options) {
|
|
25
|
+
const resultMessage = this.buildResultMessage(options, this.lastAssistantMessage);
|
|
26
|
+
this.emitMessageImpl(resultMessage);
|
|
27
|
+
}
|
|
28
|
+
// Expose protected methods for testing
|
|
29
|
+
exposeGetMessageState(parentToolUseId) {
|
|
30
|
+
return this.getMessageState(parentToolUseId);
|
|
31
|
+
}
|
|
32
|
+
exposeCreateMessageState() {
|
|
33
|
+
return this.createMessageState();
|
|
34
|
+
}
|
|
35
|
+
exposeCreateUsage(metadata) {
|
|
36
|
+
return this.createUsage(metadata);
|
|
37
|
+
}
|
|
38
|
+
exposeBuildMessage(parentToolUseId) {
|
|
39
|
+
return this.buildMessage(parentToolUseId);
|
|
40
|
+
}
|
|
41
|
+
exposeFinalizePendingBlocks(state, parentToolUseId) {
|
|
42
|
+
this.finalizePendingBlocks(state, parentToolUseId);
|
|
43
|
+
}
|
|
44
|
+
exposeOpenBlock(state, index, block) {
|
|
45
|
+
this.openBlock(state, index, block);
|
|
46
|
+
}
|
|
47
|
+
exposeCloseBlock(state, index) {
|
|
48
|
+
this.closeBlock(state, index);
|
|
49
|
+
}
|
|
50
|
+
exposeEnsureBlockTypeConsistency(state, targetType, parentToolUseId) {
|
|
51
|
+
this.ensureBlockTypeConsistency(state, targetType, parentToolUseId);
|
|
52
|
+
}
|
|
53
|
+
exposeStartAssistantMessageInternal(state) {
|
|
54
|
+
this.startAssistantMessageInternal(state);
|
|
55
|
+
}
|
|
56
|
+
exposeFinalizeAssistantMessageInternal(state, parentToolUseId) {
|
|
57
|
+
return this.finalizeAssistantMessageInternal(state, parentToolUseId);
|
|
58
|
+
}
|
|
59
|
+
exposeAppendText(state, fragment, parentToolUseId) {
|
|
60
|
+
this.appendText(state, fragment, parentToolUseId);
|
|
61
|
+
}
|
|
62
|
+
exposeAppendThinking(state, subject, description, parentToolUseId) {
|
|
63
|
+
this.appendThinking(state, subject, description, parentToolUseId);
|
|
64
|
+
}
|
|
65
|
+
exposeAppendToolUse(state, request, parentToolUseId) {
|
|
66
|
+
this.appendToolUse(state, request, parentToolUseId);
|
|
67
|
+
}
|
|
68
|
+
exposeEnsureMessageStarted(state, parentToolUseId) {
|
|
69
|
+
this.ensureMessageStarted(state, parentToolUseId);
|
|
70
|
+
}
|
|
71
|
+
exposeCreateSubagentToolUseBlock(state, toolCall, parentToolUseId) {
|
|
72
|
+
return this.createSubagentToolUseBlock(state, toolCall, parentToolUseId);
|
|
73
|
+
}
|
|
74
|
+
exposeBuildResultMessage(options) {
|
|
75
|
+
return this.buildResultMessage(options, this.lastAssistantMessage);
|
|
76
|
+
}
|
|
77
|
+
exposeBuildSubagentErrorResult(errorMessage, numTurns) {
|
|
78
|
+
return this.buildSubagentErrorResult(errorMessage, numTurns);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function createMockConfig() {
|
|
82
|
+
return {
|
|
83
|
+
getSessionId: vi.fn().mockReturnValue('test-session-id'),
|
|
84
|
+
getModel: vi.fn().mockReturnValue('test-model'),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
describe('BaseJsonOutputAdapter', () => {
|
|
88
|
+
let adapter;
|
|
89
|
+
let mockConfig;
|
|
90
|
+
beforeEach(() => {
|
|
91
|
+
mockConfig = createMockConfig();
|
|
92
|
+
adapter = new TestJsonOutputAdapter(mockConfig);
|
|
93
|
+
});
|
|
94
|
+
describe('createMessageState', () => {
|
|
95
|
+
it('should create a new message state with default values', () => {
|
|
96
|
+
const state = adapter.exposeCreateMessageState();
|
|
97
|
+
expect(state.messageId).toBeNull();
|
|
98
|
+
expect(state.blocks).toEqual([]);
|
|
99
|
+
expect(state.openBlocks).toBeInstanceOf(Set);
|
|
100
|
+
expect(state.openBlocks.size).toBe(0);
|
|
101
|
+
expect(state.usage).toEqual({
|
|
102
|
+
input_tokens: 0,
|
|
103
|
+
output_tokens: 0,
|
|
104
|
+
});
|
|
105
|
+
expect(state.messageStarted).toBe(false);
|
|
106
|
+
expect(state.finalized).toBe(false);
|
|
107
|
+
expect(state.currentBlockType).toBeNull();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
describe('getMessageState', () => {
|
|
111
|
+
it('should return main agent state for null parentToolUseId', () => {
|
|
112
|
+
const state = adapter.exposeGetMessageState(null);
|
|
113
|
+
expect(state).toBe(adapter['mainAgentMessageState']);
|
|
114
|
+
});
|
|
115
|
+
it('should create and return subagent state for non-null parentToolUseId', () => {
|
|
116
|
+
const parentToolUseId = 'parent-tool-1';
|
|
117
|
+
const state1 = adapter.exposeGetMessageState(parentToolUseId);
|
|
118
|
+
const state2 = adapter.exposeGetMessageState(parentToolUseId);
|
|
119
|
+
expect(state1).toBe(state2);
|
|
120
|
+
expect(state1).not.toBe(adapter['mainAgentMessageState']);
|
|
121
|
+
expect(adapter['subagentMessageStates'].has(parentToolUseId)).toBe(true);
|
|
122
|
+
});
|
|
123
|
+
it('should create separate states for different parentToolUseIds', () => {
|
|
124
|
+
const state1 = adapter.exposeGetMessageState('parent-1');
|
|
125
|
+
const state2 = adapter.exposeGetMessageState('parent-2');
|
|
126
|
+
expect(state1).not.toBe(state2);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
describe('createUsage', () => {
|
|
130
|
+
it('should create usage with default values when metadata is not provided', () => {
|
|
131
|
+
const usage = adapter.exposeCreateUsage();
|
|
132
|
+
expect(usage).toEqual({
|
|
133
|
+
input_tokens: 0,
|
|
134
|
+
output_tokens: 0,
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
it('should create usage with null metadata', () => {
|
|
138
|
+
const usage = adapter.exposeCreateUsage(null);
|
|
139
|
+
expect(usage).toEqual({
|
|
140
|
+
input_tokens: 0,
|
|
141
|
+
output_tokens: 0,
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
it('should extract usage from metadata', () => {
|
|
145
|
+
const metadata = {
|
|
146
|
+
promptTokenCount: 100,
|
|
147
|
+
candidatesTokenCount: 50,
|
|
148
|
+
cachedContentTokenCount: 10,
|
|
149
|
+
totalTokenCount: 160,
|
|
150
|
+
};
|
|
151
|
+
const usage = adapter.exposeCreateUsage(metadata);
|
|
152
|
+
expect(usage).toEqual({
|
|
153
|
+
input_tokens: 100,
|
|
154
|
+
output_tokens: 50,
|
|
155
|
+
cache_read_input_tokens: 10,
|
|
156
|
+
total_tokens: 160,
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
it('should handle partial metadata', () => {
|
|
160
|
+
const metadata = {
|
|
161
|
+
promptTokenCount: 100,
|
|
162
|
+
// candidatesTokenCount missing
|
|
163
|
+
};
|
|
164
|
+
const usage = adapter.exposeCreateUsage(metadata);
|
|
165
|
+
expect(usage).toEqual({
|
|
166
|
+
input_tokens: 100,
|
|
167
|
+
output_tokens: 0,
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
describe('buildMessage', () => {
|
|
172
|
+
beforeEach(() => {
|
|
173
|
+
adapter.startAssistantMessage();
|
|
174
|
+
});
|
|
175
|
+
it('should throw error if message not started', () => {
|
|
176
|
+
// Manipulate the actual main agent state used by buildMessage
|
|
177
|
+
const state = adapter['mainAgentMessageState'];
|
|
178
|
+
state.messageId = null; // Explicitly set to null to test error case
|
|
179
|
+
state.blocks = [{ type: 'text', text: 'test' }];
|
|
180
|
+
expect(() => adapter.exposeBuildMessage(null)).toThrow('Message not started');
|
|
181
|
+
});
|
|
182
|
+
it('should build message with text blocks', () => {
|
|
183
|
+
adapter.startAssistantMessage();
|
|
184
|
+
adapter.processEvent({
|
|
185
|
+
type: GeminiEventType.Content,
|
|
186
|
+
value: 'Hello world',
|
|
187
|
+
});
|
|
188
|
+
const message = adapter.exposeBuildMessage(null);
|
|
189
|
+
expect(message.type).toBe('assistant');
|
|
190
|
+
expect(message.uuid).toBeTruthy();
|
|
191
|
+
expect(message.session_id).toBe('test-session-id');
|
|
192
|
+
expect(message.parent_tool_use_id).toBeNull();
|
|
193
|
+
expect(message.message.role).toBe('assistant');
|
|
194
|
+
expect(message.message.model).toBe('test-model');
|
|
195
|
+
expect(message.message.content).toHaveLength(1);
|
|
196
|
+
expect(message.message.content[0]).toMatchObject({
|
|
197
|
+
type: 'text',
|
|
198
|
+
text: 'Hello world',
|
|
199
|
+
});
|
|
200
|
+
expect(message.message.stop_reason).toBeNull();
|
|
201
|
+
});
|
|
202
|
+
it('should set stop_reason to tool_use when message contains only tool_use blocks', () => {
|
|
203
|
+
adapter.startAssistantMessage();
|
|
204
|
+
adapter.processEvent({
|
|
205
|
+
type: GeminiEventType.ToolCallRequest,
|
|
206
|
+
value: {
|
|
207
|
+
callId: 'tool-1',
|
|
208
|
+
name: 'test_tool',
|
|
209
|
+
args: {},
|
|
210
|
+
isClientInitiated: false,
|
|
211
|
+
prompt_id: 'prompt-1',
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
const message = adapter.exposeBuildMessage(null);
|
|
215
|
+
expect(message.message.stop_reason).toBe('tool_use');
|
|
216
|
+
});
|
|
217
|
+
it('should enforce single block type constraint', () => {
|
|
218
|
+
adapter.startAssistantMessage();
|
|
219
|
+
const state = adapter['mainAgentMessageState'];
|
|
220
|
+
state.messageId = 'test-id';
|
|
221
|
+
state.blocks = [
|
|
222
|
+
{ type: 'text', text: 'text' },
|
|
223
|
+
{ type: 'thinking', thinking: 'thinking', signature: 'sig' },
|
|
224
|
+
];
|
|
225
|
+
expect(() => adapter.exposeBuildMessage(null)).toThrow('Assistant message must contain only one type of ContentBlock');
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
describe('finalizePendingBlocks', () => {
|
|
229
|
+
it('should finalize text blocks', () => {
|
|
230
|
+
const state = adapter.exposeCreateMessageState();
|
|
231
|
+
state.blocks = [{ type: 'text', text: 'test' }];
|
|
232
|
+
const index = 0;
|
|
233
|
+
adapter.exposeOpenBlock(state, index, state.blocks[0]);
|
|
234
|
+
adapter.exposeFinalizePendingBlocks(state);
|
|
235
|
+
expect(state.openBlocks.has(index)).toBe(false);
|
|
236
|
+
});
|
|
237
|
+
it('should finalize thinking blocks', () => {
|
|
238
|
+
const state = adapter.exposeCreateMessageState();
|
|
239
|
+
state.blocks = [{ type: 'thinking', thinking: 'test', signature: 'sig' }];
|
|
240
|
+
const index = 0;
|
|
241
|
+
adapter.exposeOpenBlock(state, index, state.blocks[0]);
|
|
242
|
+
adapter.exposeFinalizePendingBlocks(state);
|
|
243
|
+
expect(state.openBlocks.has(index)).toBe(false);
|
|
244
|
+
});
|
|
245
|
+
it('should do nothing if no blocks', () => {
|
|
246
|
+
const state = adapter.exposeCreateMessageState();
|
|
247
|
+
expect(() => adapter.exposeFinalizePendingBlocks(state)).not.toThrow();
|
|
248
|
+
});
|
|
249
|
+
it('should do nothing if last block is not text or thinking', () => {
|
|
250
|
+
const state = adapter.exposeCreateMessageState();
|
|
251
|
+
state.blocks = [
|
|
252
|
+
{
|
|
253
|
+
type: 'tool_use',
|
|
254
|
+
id: 'tool-1',
|
|
255
|
+
name: 'test',
|
|
256
|
+
input: {},
|
|
257
|
+
},
|
|
258
|
+
];
|
|
259
|
+
expect(() => adapter.exposeFinalizePendingBlocks(state)).not.toThrow();
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
describe('openBlock and closeBlock', () => {
|
|
263
|
+
it('should add block index to openBlocks', () => {
|
|
264
|
+
const state = adapter.exposeCreateMessageState();
|
|
265
|
+
const block = { type: 'text', text: 'test' };
|
|
266
|
+
adapter.exposeOpenBlock(state, 0, block);
|
|
267
|
+
expect(state.openBlocks.has(0)).toBe(true);
|
|
268
|
+
});
|
|
269
|
+
it('should remove block index from openBlocks', () => {
|
|
270
|
+
const state = adapter.exposeCreateMessageState();
|
|
271
|
+
const block = { type: 'text', text: 'test' };
|
|
272
|
+
adapter.exposeOpenBlock(state, 0, block);
|
|
273
|
+
adapter.exposeCloseBlock(state, 0);
|
|
274
|
+
expect(state.openBlocks.has(0)).toBe(false);
|
|
275
|
+
});
|
|
276
|
+
it('should not throw when closing non-existent block', () => {
|
|
277
|
+
const state = adapter.exposeCreateMessageState();
|
|
278
|
+
expect(() => adapter.exposeCloseBlock(state, 0)).not.toThrow();
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
describe('ensureBlockTypeConsistency', () => {
|
|
282
|
+
it('should set currentBlockType if null', () => {
|
|
283
|
+
const state = adapter.exposeCreateMessageState();
|
|
284
|
+
state.currentBlockType = null;
|
|
285
|
+
adapter.exposeEnsureBlockTypeConsistency(state, 'text', null);
|
|
286
|
+
expect(state.currentBlockType).toBe('text');
|
|
287
|
+
});
|
|
288
|
+
it('should do nothing if currentBlockType matches target', () => {
|
|
289
|
+
const state = adapter.exposeCreateMessageState();
|
|
290
|
+
state.currentBlockType = 'text';
|
|
291
|
+
state.messageId = 'test-id';
|
|
292
|
+
state.blocks = [{ type: 'text', text: 'test' }];
|
|
293
|
+
adapter.exposeEnsureBlockTypeConsistency(state, 'text', null);
|
|
294
|
+
expect(state.currentBlockType).toBe('text');
|
|
295
|
+
expect(state.blocks).toHaveLength(1);
|
|
296
|
+
});
|
|
297
|
+
it('should finalize and start new message when block type changes', () => {
|
|
298
|
+
adapter.startAssistantMessage();
|
|
299
|
+
const state = adapter['mainAgentMessageState'];
|
|
300
|
+
adapter.processEvent({
|
|
301
|
+
type: GeminiEventType.Content,
|
|
302
|
+
value: 'text',
|
|
303
|
+
});
|
|
304
|
+
adapter.exposeEnsureBlockTypeConsistency(state, 'thinking', null);
|
|
305
|
+
expect(state.currentBlockType).toBe('thinking');
|
|
306
|
+
expect(state.blocks.length).toBe(0);
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
describe('startAssistantMessageInternal', () => {
|
|
310
|
+
it('should reset message state', () => {
|
|
311
|
+
const state = adapter.exposeCreateMessageState();
|
|
312
|
+
state.messageId = 'old-id';
|
|
313
|
+
state.blocks = [{ type: 'text', text: 'old' }];
|
|
314
|
+
state.openBlocks.add(0);
|
|
315
|
+
state.usage = { input_tokens: 100, output_tokens: 50 };
|
|
316
|
+
state.messageStarted = true;
|
|
317
|
+
state.finalized = true;
|
|
318
|
+
state.currentBlockType = 'text';
|
|
319
|
+
adapter.exposeStartAssistantMessageInternal(state);
|
|
320
|
+
expect(state.messageId).toBeTruthy();
|
|
321
|
+
expect(state.messageId).not.toBe('old-id');
|
|
322
|
+
expect(state.blocks).toEqual([]);
|
|
323
|
+
expect(state.openBlocks.size).toBe(0);
|
|
324
|
+
expect(state.usage).toEqual({ input_tokens: 0, output_tokens: 0 });
|
|
325
|
+
expect(state.messageStarted).toBe(false);
|
|
326
|
+
expect(state.finalized).toBe(false);
|
|
327
|
+
expect(state.currentBlockType).toBeNull();
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
describe('finalizeAssistantMessageInternal', () => {
|
|
331
|
+
it('should return same message if already finalized', () => {
|
|
332
|
+
adapter.startAssistantMessage();
|
|
333
|
+
const state = adapter['mainAgentMessageState'];
|
|
334
|
+
adapter.processEvent({
|
|
335
|
+
type: GeminiEventType.Content,
|
|
336
|
+
value: 'test',
|
|
337
|
+
});
|
|
338
|
+
const message1 = adapter.exposeFinalizeAssistantMessageInternal(state, null);
|
|
339
|
+
const message2 = adapter.exposeFinalizeAssistantMessageInternal(state, null);
|
|
340
|
+
expect(message1).toEqual(message2);
|
|
341
|
+
expect(state.finalized).toBe(true);
|
|
342
|
+
});
|
|
343
|
+
it('should finalize pending blocks and emit message', () => {
|
|
344
|
+
adapter.startAssistantMessage();
|
|
345
|
+
const state = adapter['mainAgentMessageState'];
|
|
346
|
+
adapter.processEvent({
|
|
347
|
+
type: GeminiEventType.Content,
|
|
348
|
+
value: 'test',
|
|
349
|
+
});
|
|
350
|
+
const message = adapter.exposeFinalizeAssistantMessageInternal(state, null);
|
|
351
|
+
expect(message).toBeDefined();
|
|
352
|
+
expect(state.finalized).toBe(true);
|
|
353
|
+
expect(adapter.emittedMessages).toContain(message);
|
|
354
|
+
});
|
|
355
|
+
it('should close all open blocks', () => {
|
|
356
|
+
adapter.startAssistantMessage();
|
|
357
|
+
const state = adapter['mainAgentMessageState'];
|
|
358
|
+
adapter.processEvent({
|
|
359
|
+
type: GeminiEventType.Content,
|
|
360
|
+
value: 'test',
|
|
361
|
+
});
|
|
362
|
+
state.openBlocks.add(0);
|
|
363
|
+
adapter.exposeFinalizeAssistantMessageInternal(state, null);
|
|
364
|
+
expect(state.openBlocks.size).toBe(0);
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
describe('appendText', () => {
|
|
368
|
+
it('should create new text block if none exists', () => {
|
|
369
|
+
const state = adapter.exposeCreateMessageState();
|
|
370
|
+
adapter.startAssistantMessage();
|
|
371
|
+
adapter.exposeAppendText(state, 'Hello', null);
|
|
372
|
+
expect(state.blocks).toHaveLength(1);
|
|
373
|
+
expect(state.blocks[0]).toMatchObject({
|
|
374
|
+
type: 'text',
|
|
375
|
+
text: 'Hello',
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
it('should append to existing text block', () => {
|
|
379
|
+
const state = adapter.exposeCreateMessageState();
|
|
380
|
+
adapter.startAssistantMessage();
|
|
381
|
+
adapter.exposeAppendText(state, 'Hello', null);
|
|
382
|
+
adapter.exposeAppendText(state, ' World', null);
|
|
383
|
+
expect(state.blocks).toHaveLength(1);
|
|
384
|
+
expect(state.blocks[0]).toMatchObject({
|
|
385
|
+
type: 'text',
|
|
386
|
+
text: 'Hello World',
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
it('should ignore empty fragments', () => {
|
|
390
|
+
const state = adapter.exposeCreateMessageState();
|
|
391
|
+
adapter.startAssistantMessage();
|
|
392
|
+
adapter.exposeAppendText(state, '', null);
|
|
393
|
+
expect(state.blocks).toHaveLength(0);
|
|
394
|
+
});
|
|
395
|
+
it('should ensure message is started', () => {
|
|
396
|
+
const state = adapter.exposeCreateMessageState();
|
|
397
|
+
adapter.startAssistantMessage();
|
|
398
|
+
adapter.exposeAppendText(state, 'test', null);
|
|
399
|
+
expect(state.messageStarted).toBe(true);
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
describe('appendThinking', () => {
|
|
403
|
+
it('should create new thinking block', () => {
|
|
404
|
+
const state = adapter.exposeCreateMessageState();
|
|
405
|
+
adapter.startAssistantMessage();
|
|
406
|
+
adapter.exposeAppendThinking(state, 'Planning', 'Thinking about task', null);
|
|
407
|
+
expect(state.blocks).toHaveLength(1);
|
|
408
|
+
expect(state.blocks[0]).toMatchObject({
|
|
409
|
+
type: 'thinking',
|
|
410
|
+
thinking: 'Planning: Thinking about task',
|
|
411
|
+
signature: 'Planning',
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
it('should append to existing thinking block', () => {
|
|
415
|
+
const state = adapter.exposeCreateMessageState();
|
|
416
|
+
adapter.startAssistantMessage();
|
|
417
|
+
adapter.exposeAppendThinking(state, 'Planning', 'First thought', null);
|
|
418
|
+
adapter.exposeAppendThinking(state, 'Planning', 'Second thought', null);
|
|
419
|
+
expect(state.blocks).toHaveLength(1);
|
|
420
|
+
expect(state.blocks[0].type).toBe('thinking');
|
|
421
|
+
const block = state.blocks[0];
|
|
422
|
+
expect(block.thinking).toContain('First thought');
|
|
423
|
+
expect(block.thinking).toContain('Second thought');
|
|
424
|
+
});
|
|
425
|
+
it('should handle only subject', () => {
|
|
426
|
+
const state = adapter.exposeCreateMessageState();
|
|
427
|
+
adapter.startAssistantMessage();
|
|
428
|
+
adapter.exposeAppendThinking(state, 'Planning', '', null);
|
|
429
|
+
expect(state.blocks).toHaveLength(1);
|
|
430
|
+
expect(state.blocks[0]).toMatchObject({
|
|
431
|
+
type: 'thinking',
|
|
432
|
+
signature: 'Planning',
|
|
433
|
+
});
|
|
434
|
+
});
|
|
435
|
+
it('should ignore empty fragments', () => {
|
|
436
|
+
const state = adapter.exposeCreateMessageState();
|
|
437
|
+
adapter.startAssistantMessage();
|
|
438
|
+
adapter.exposeAppendThinking(state, '', '', null);
|
|
439
|
+
expect(state.blocks).toHaveLength(0);
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
describe('appendToolUse', () => {
|
|
443
|
+
it('should create tool_use block', () => {
|
|
444
|
+
const state = adapter.exposeCreateMessageState();
|
|
445
|
+
adapter.startAssistantMessage();
|
|
446
|
+
adapter.exposeAppendToolUse(state, {
|
|
447
|
+
callId: 'tool-1',
|
|
448
|
+
name: 'test_tool',
|
|
449
|
+
args: { param: 'value' },
|
|
450
|
+
}, null);
|
|
451
|
+
expect(state.blocks).toHaveLength(1);
|
|
452
|
+
expect(state.blocks[0]).toMatchObject({
|
|
453
|
+
type: 'tool_use',
|
|
454
|
+
id: 'tool-1',
|
|
455
|
+
name: 'test_tool',
|
|
456
|
+
input: { param: 'value' },
|
|
457
|
+
});
|
|
458
|
+
});
|
|
459
|
+
it('should finalize pending blocks before appending tool_use', () => {
|
|
460
|
+
const state = adapter.exposeCreateMessageState();
|
|
461
|
+
adapter.startAssistantMessage();
|
|
462
|
+
adapter.exposeAppendText(state, 'text', null);
|
|
463
|
+
adapter.exposeAppendToolUse(state, {
|
|
464
|
+
callId: 'tool-1',
|
|
465
|
+
name: 'test_tool',
|
|
466
|
+
args: {},
|
|
467
|
+
}, null);
|
|
468
|
+
expect(state.blocks.length).toBeGreaterThan(0);
|
|
469
|
+
const toolUseBlock = state.blocks.find((b) => b.type === 'tool_use');
|
|
470
|
+
expect(toolUseBlock).toBeDefined();
|
|
471
|
+
});
|
|
472
|
+
});
|
|
473
|
+
describe('ensureMessageStarted', () => {
|
|
474
|
+
it('should set messageStarted to true', () => {
|
|
475
|
+
const state = adapter.exposeCreateMessageState();
|
|
476
|
+
adapter.startAssistantMessage();
|
|
477
|
+
adapter.exposeEnsureMessageStarted(state, null);
|
|
478
|
+
expect(state.messageStarted).toBe(true);
|
|
479
|
+
});
|
|
480
|
+
it('should do nothing if already started', () => {
|
|
481
|
+
const state = adapter.exposeCreateMessageState();
|
|
482
|
+
adapter.startAssistantMessage();
|
|
483
|
+
state.messageStarted = true;
|
|
484
|
+
adapter.exposeEnsureMessageStarted(state, null);
|
|
485
|
+
expect(state.messageStarted).toBe(true);
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
describe('startAssistantMessage', () => {
|
|
489
|
+
it('should reset main agent message state', () => {
|
|
490
|
+
adapter.startAssistantMessage();
|
|
491
|
+
adapter.processEvent({
|
|
492
|
+
type: GeminiEventType.Content,
|
|
493
|
+
value: 'test',
|
|
494
|
+
});
|
|
495
|
+
adapter.startAssistantMessage();
|
|
496
|
+
const state = adapter['mainAgentMessageState'];
|
|
497
|
+
expect(state.blocks).toHaveLength(0);
|
|
498
|
+
expect(state.messageStarted).toBe(false);
|
|
499
|
+
});
|
|
500
|
+
});
|
|
501
|
+
describe('processEvent', () => {
|
|
502
|
+
beforeEach(() => {
|
|
503
|
+
adapter.startAssistantMessage();
|
|
504
|
+
});
|
|
505
|
+
it('should process Content events', () => {
|
|
506
|
+
adapter.processEvent({
|
|
507
|
+
type: GeminiEventType.Content,
|
|
508
|
+
value: 'Hello',
|
|
509
|
+
});
|
|
510
|
+
const state = adapter['mainAgentMessageState'];
|
|
511
|
+
expect(state.blocks).toHaveLength(1);
|
|
512
|
+
expect(state.blocks[0]).toMatchObject({
|
|
513
|
+
type: 'text',
|
|
514
|
+
text: 'Hello',
|
|
515
|
+
});
|
|
516
|
+
});
|
|
517
|
+
it('should process Citation events', () => {
|
|
518
|
+
adapter.processEvent({
|
|
519
|
+
type: GeminiEventType.Citation,
|
|
520
|
+
value: 'Citation text',
|
|
521
|
+
});
|
|
522
|
+
const state = adapter['mainAgentMessageState'];
|
|
523
|
+
expect(state.blocks[0].type).toBe('text');
|
|
524
|
+
const block = state.blocks[0];
|
|
525
|
+
expect(block.text).toContain('Citation text');
|
|
526
|
+
});
|
|
527
|
+
it('should ignore non-string Citation values', () => {
|
|
528
|
+
adapter.processEvent({
|
|
529
|
+
type: GeminiEventType.Citation,
|
|
530
|
+
value: 123,
|
|
531
|
+
});
|
|
532
|
+
const state = adapter['mainAgentMessageState'];
|
|
533
|
+
expect(state.blocks).toHaveLength(0);
|
|
534
|
+
});
|
|
535
|
+
it('should process Thought events', () => {
|
|
536
|
+
adapter.processEvent({
|
|
537
|
+
type: GeminiEventType.Thought,
|
|
538
|
+
value: {
|
|
539
|
+
subject: 'Planning',
|
|
540
|
+
description: 'Thinking',
|
|
541
|
+
},
|
|
542
|
+
});
|
|
543
|
+
const state = adapter['mainAgentMessageState'];
|
|
544
|
+
expect(state.blocks).toHaveLength(1);
|
|
545
|
+
expect(state.blocks[0]).toMatchObject({
|
|
546
|
+
type: 'thinking',
|
|
547
|
+
thinking: 'Planning: Thinking',
|
|
548
|
+
signature: 'Planning',
|
|
549
|
+
});
|
|
550
|
+
});
|
|
551
|
+
it('should process ToolCallRequest events', () => {
|
|
552
|
+
adapter.processEvent({
|
|
553
|
+
type: GeminiEventType.ToolCallRequest,
|
|
554
|
+
value: {
|
|
555
|
+
callId: 'tool-1',
|
|
556
|
+
name: 'test_tool',
|
|
557
|
+
args: { param: 'value' },
|
|
558
|
+
isClientInitiated: false,
|
|
559
|
+
prompt_id: 'prompt-1',
|
|
560
|
+
},
|
|
561
|
+
});
|
|
562
|
+
const state = adapter['mainAgentMessageState'];
|
|
563
|
+
expect(state.blocks).toHaveLength(1);
|
|
564
|
+
expect(state.blocks[0]).toMatchObject({
|
|
565
|
+
type: 'tool_use',
|
|
566
|
+
id: 'tool-1',
|
|
567
|
+
name: 'test_tool',
|
|
568
|
+
input: { param: 'value' },
|
|
569
|
+
});
|
|
570
|
+
});
|
|
571
|
+
it('should process Finished events with usage metadata', () => {
|
|
572
|
+
adapter.processEvent({
|
|
573
|
+
type: GeminiEventType.Finished,
|
|
574
|
+
value: {
|
|
575
|
+
reason: undefined,
|
|
576
|
+
usageMetadata: {
|
|
577
|
+
promptTokenCount: 100,
|
|
578
|
+
candidatesTokenCount: 50,
|
|
579
|
+
},
|
|
580
|
+
},
|
|
581
|
+
});
|
|
582
|
+
const state = adapter['mainAgentMessageState'];
|
|
583
|
+
expect(state.usage).toEqual({
|
|
584
|
+
input_tokens: 100,
|
|
585
|
+
output_tokens: 50,
|
|
586
|
+
});
|
|
587
|
+
});
|
|
588
|
+
it('should ignore events after finalization', () => {
|
|
589
|
+
adapter.processEvent({
|
|
590
|
+
type: GeminiEventType.Content,
|
|
591
|
+
value: 'First',
|
|
592
|
+
});
|
|
593
|
+
adapter.finalizeAssistantMessage();
|
|
594
|
+
adapter.processEvent({
|
|
595
|
+
type: GeminiEventType.Content,
|
|
596
|
+
value: 'Second',
|
|
597
|
+
});
|
|
598
|
+
const state = adapter['mainAgentMessageState'];
|
|
599
|
+
expect(state.blocks[0]).toMatchObject({
|
|
600
|
+
type: 'text',
|
|
601
|
+
text: 'First',
|
|
602
|
+
});
|
|
603
|
+
});
|
|
604
|
+
});
|
|
605
|
+
describe('finalizeAssistantMessage', () => {
|
|
606
|
+
beforeEach(() => {
|
|
607
|
+
adapter.startAssistantMessage();
|
|
608
|
+
});
|
|
609
|
+
it('should build and return assistant message', () => {
|
|
610
|
+
adapter.processEvent({
|
|
611
|
+
type: GeminiEventType.Content,
|
|
612
|
+
value: 'Test response',
|
|
613
|
+
});
|
|
614
|
+
const message = adapter.finalizeAssistantMessage();
|
|
615
|
+
expect(message.type).toBe('assistant');
|
|
616
|
+
expect(message.message.content).toHaveLength(1);
|
|
617
|
+
expect(adapter.emittedMessages).toContain(message);
|
|
618
|
+
});
|
|
619
|
+
});
|
|
620
|
+
describe('emitUserMessage', () => {
|
|
621
|
+
it('should emit user message with ContentBlock array', () => {
|
|
622
|
+
const parts = [{ text: 'Hello user' }];
|
|
623
|
+
adapter.emitUserMessage(parts);
|
|
624
|
+
expect(adapter.emittedMessages).toHaveLength(1);
|
|
625
|
+
const message = adapter.emittedMessages[0];
|
|
626
|
+
expect(message.type).toBe('user');
|
|
627
|
+
if (message.type === 'user') {
|
|
628
|
+
expect(Array.isArray(message.message.content)).toBe(true);
|
|
629
|
+
if (Array.isArray(message.message.content)) {
|
|
630
|
+
expect(message.message.content).toHaveLength(1);
|
|
631
|
+
expect(message.message.content[0]).toEqual({
|
|
632
|
+
type: 'text',
|
|
633
|
+
text: 'Hello user',
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
expect(message.parent_tool_use_id).toBeNull();
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
it('should handle multiple parts and merge into single text block', () => {
|
|
640
|
+
const parts = [{ text: 'Hello' }, { text: ' World' }];
|
|
641
|
+
adapter.emitUserMessage(parts);
|
|
642
|
+
const message = adapter.emittedMessages[0];
|
|
643
|
+
if (message.type === 'user' && Array.isArray(message.message.content)) {
|
|
644
|
+
expect(message.message.content).toHaveLength(1);
|
|
645
|
+
expect(message.message.content[0]).toEqual({
|
|
646
|
+
type: 'text',
|
|
647
|
+
text: 'Hello World',
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
it('should handle non-text parts by converting to text blocks', () => {
|
|
652
|
+
const parts = [
|
|
653
|
+
{ text: 'Hello' },
|
|
654
|
+
{ functionCall: { name: 'test' } },
|
|
655
|
+
];
|
|
656
|
+
adapter.emitUserMessage(parts);
|
|
657
|
+
const message = adapter.emittedMessages[0];
|
|
658
|
+
if (message.type === 'user' && Array.isArray(message.message.content)) {
|
|
659
|
+
expect(message.message.content.length).toBeGreaterThan(0);
|
|
660
|
+
const textBlock = message.message.content.find((block) => block.type === 'text');
|
|
661
|
+
expect(textBlock).toBeDefined();
|
|
662
|
+
if (textBlock && textBlock.type === 'text') {
|
|
663
|
+
expect(textBlock.text).toContain('Hello');
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
});
|
|
668
|
+
describe('emitToolResult', () => {
|
|
669
|
+
it('should emit tool result message with content', () => {
|
|
670
|
+
const request = {
|
|
671
|
+
callId: 'tool-1',
|
|
672
|
+
name: 'test_tool',
|
|
673
|
+
args: {},
|
|
674
|
+
isClientInitiated: false,
|
|
675
|
+
prompt_id: 'prompt-1',
|
|
676
|
+
};
|
|
677
|
+
const response = {
|
|
678
|
+
callId: 'tool-1',
|
|
679
|
+
responseParts: [],
|
|
680
|
+
resultDisplay: 'Tool executed successfully',
|
|
681
|
+
error: undefined,
|
|
682
|
+
errorType: undefined,
|
|
683
|
+
};
|
|
684
|
+
adapter.emitToolResult(request, response);
|
|
685
|
+
expect(adapter.emittedMessages).toHaveLength(1);
|
|
686
|
+
const message = adapter.emittedMessages[0];
|
|
687
|
+
expect(message.type).toBe('user');
|
|
688
|
+
if (message.type === 'user') {
|
|
689
|
+
expect(message.message.content).toHaveLength(1);
|
|
690
|
+
const block = message.message.content[0];
|
|
691
|
+
if (typeof block === 'object' && block !== null && 'type' in block) {
|
|
692
|
+
expect(block.type).toBe('tool_result');
|
|
693
|
+
if (block.type === 'tool_result') {
|
|
694
|
+
expect(block.tool_use_id).toBe('tool-1');
|
|
695
|
+
expect(block.content).toBe('Tool executed successfully');
|
|
696
|
+
expect(block.is_error).toBe(false);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
it('should mark error tool results', () => {
|
|
702
|
+
const request = {
|
|
703
|
+
callId: 'tool-1',
|
|
704
|
+
name: 'test_tool',
|
|
705
|
+
args: {},
|
|
706
|
+
isClientInitiated: false,
|
|
707
|
+
prompt_id: 'prompt-1',
|
|
708
|
+
};
|
|
709
|
+
const response = {
|
|
710
|
+
callId: 'tool-1',
|
|
711
|
+
responseParts: [],
|
|
712
|
+
resultDisplay: undefined,
|
|
713
|
+
error: new Error('Tool failed'),
|
|
714
|
+
errorType: undefined,
|
|
715
|
+
};
|
|
716
|
+
adapter.emitToolResult(request, response);
|
|
717
|
+
const message = adapter.emittedMessages[0];
|
|
718
|
+
if (message.type === 'user') {
|
|
719
|
+
const block = message.message.content[0];
|
|
720
|
+
if (typeof block === 'object' && block !== null && 'type' in block) {
|
|
721
|
+
if (block.type === 'tool_result') {
|
|
722
|
+
expect(block.is_error).toBe(true);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
it('should handle parentToolUseId', () => {
|
|
728
|
+
const request = {
|
|
729
|
+
callId: 'tool-1',
|
|
730
|
+
name: 'test_tool',
|
|
731
|
+
args: {},
|
|
732
|
+
isClientInitiated: false,
|
|
733
|
+
prompt_id: 'prompt-1',
|
|
734
|
+
};
|
|
735
|
+
const response = {
|
|
736
|
+
callId: 'tool-1',
|
|
737
|
+
responseParts: [],
|
|
738
|
+
resultDisplay: 'Result',
|
|
739
|
+
error: undefined,
|
|
740
|
+
errorType: undefined,
|
|
741
|
+
};
|
|
742
|
+
adapter.emitToolResult(request, response, 'parent-tool-1');
|
|
743
|
+
const message = adapter.emittedMessages[0];
|
|
744
|
+
if (message.type === 'user') {
|
|
745
|
+
expect(message.parent_tool_use_id).toBe('parent-tool-1');
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
});
|
|
749
|
+
describe('emitSystemMessage', () => {
|
|
750
|
+
it('should emit system message', () => {
|
|
751
|
+
adapter.emitSystemMessage('test_subtype', { data: 'value' });
|
|
752
|
+
expect(adapter.emittedMessages).toHaveLength(1);
|
|
753
|
+
const message = adapter.emittedMessages[0];
|
|
754
|
+
expect(message.type).toBe('system');
|
|
755
|
+
if (message.type === 'system') {
|
|
756
|
+
expect(message.subtype).toBe('test_subtype');
|
|
757
|
+
expect(message.data).toEqual({ data: 'value' });
|
|
758
|
+
}
|
|
759
|
+
});
|
|
760
|
+
it('should handle system message without data', () => {
|
|
761
|
+
adapter.emitSystemMessage('test_subtype');
|
|
762
|
+
const message = adapter.emittedMessages[0];
|
|
763
|
+
if (message.type === 'system') {
|
|
764
|
+
expect(message.subtype).toBe('test_subtype');
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
});
|
|
768
|
+
describe('buildResultMessage', () => {
|
|
769
|
+
beforeEach(() => {
|
|
770
|
+
adapter.startAssistantMessage();
|
|
771
|
+
adapter.processEvent({
|
|
772
|
+
type: GeminiEventType.Content,
|
|
773
|
+
value: 'Response text',
|
|
774
|
+
});
|
|
775
|
+
const message = adapter.finalizeAssistantMessage();
|
|
776
|
+
// Update lastAssistantMessage manually since test adapter doesn't do it automatically
|
|
777
|
+
adapter['lastAssistantMessage'] = message;
|
|
778
|
+
});
|
|
779
|
+
it('should build success result message', () => {
|
|
780
|
+
const options = {
|
|
781
|
+
isError: false,
|
|
782
|
+
durationMs: 1000,
|
|
783
|
+
apiDurationMs: 800,
|
|
784
|
+
numTurns: 1,
|
|
785
|
+
};
|
|
786
|
+
const result = adapter.exposeBuildResultMessage(options);
|
|
787
|
+
expect(result.type).toBe('result');
|
|
788
|
+
expect(result.is_error).toBe(false);
|
|
789
|
+
if (!result.is_error) {
|
|
790
|
+
expect(result.subtype).toBe('success');
|
|
791
|
+
expect(result.result).toBe('Response text');
|
|
792
|
+
expect(result.duration_ms).toBe(1000);
|
|
793
|
+
expect(result.duration_api_ms).toBe(800);
|
|
794
|
+
expect(result.num_turns).toBe(1);
|
|
795
|
+
}
|
|
796
|
+
});
|
|
797
|
+
it('should build error result message', () => {
|
|
798
|
+
const options = {
|
|
799
|
+
isError: true,
|
|
800
|
+
errorMessage: 'Test error',
|
|
801
|
+
durationMs: 500,
|
|
802
|
+
apiDurationMs: 300,
|
|
803
|
+
numTurns: 1,
|
|
804
|
+
};
|
|
805
|
+
const result = adapter.exposeBuildResultMessage(options);
|
|
806
|
+
expect(result.type).toBe('result');
|
|
807
|
+
expect(result.is_error).toBe(true);
|
|
808
|
+
if (result.is_error) {
|
|
809
|
+
expect(result.subtype).toBe('error_during_execution');
|
|
810
|
+
expect(result.error?.message).toBe('Test error');
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
it('should use provided summary over extracted text', () => {
|
|
814
|
+
const options = {
|
|
815
|
+
isError: false,
|
|
816
|
+
summary: 'Custom summary',
|
|
817
|
+
durationMs: 1000,
|
|
818
|
+
apiDurationMs: 800,
|
|
819
|
+
numTurns: 1,
|
|
820
|
+
};
|
|
821
|
+
const result = adapter.exposeBuildResultMessage(options);
|
|
822
|
+
if (!result.is_error) {
|
|
823
|
+
expect(result.result).toBe('Custom summary');
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
it('should include usage information', () => {
|
|
827
|
+
const usage = {
|
|
828
|
+
input_tokens: 100,
|
|
829
|
+
output_tokens: 50,
|
|
830
|
+
total_tokens: 150,
|
|
831
|
+
};
|
|
832
|
+
const options = {
|
|
833
|
+
isError: false,
|
|
834
|
+
usage,
|
|
835
|
+
durationMs: 1000,
|
|
836
|
+
apiDurationMs: 800,
|
|
837
|
+
numTurns: 1,
|
|
838
|
+
};
|
|
839
|
+
const result = adapter.exposeBuildResultMessage(options);
|
|
840
|
+
expect(result.usage).toEqual(usage);
|
|
841
|
+
});
|
|
842
|
+
it('should include stats when provided', () => {
|
|
843
|
+
const stats = {
|
|
844
|
+
models: {},
|
|
845
|
+
tools: {
|
|
846
|
+
totalCalls: 5,
|
|
847
|
+
totalSuccess: 4,
|
|
848
|
+
totalFail: 1,
|
|
849
|
+
totalDurationMs: 1000,
|
|
850
|
+
totalDecisions: {
|
|
851
|
+
accept: 3,
|
|
852
|
+
reject: 1,
|
|
853
|
+
modify: 0,
|
|
854
|
+
auto_accept: 1,
|
|
855
|
+
},
|
|
856
|
+
byName: {},
|
|
857
|
+
},
|
|
858
|
+
files: {
|
|
859
|
+
totalLinesAdded: 10,
|
|
860
|
+
totalLinesRemoved: 5,
|
|
861
|
+
},
|
|
862
|
+
};
|
|
863
|
+
const options = {
|
|
864
|
+
isError: false,
|
|
865
|
+
stats,
|
|
866
|
+
durationMs: 1000,
|
|
867
|
+
apiDurationMs: 800,
|
|
868
|
+
numTurns: 1,
|
|
869
|
+
};
|
|
870
|
+
const result = adapter.exposeBuildResultMessage(options);
|
|
871
|
+
if (!result.is_error && 'stats' in result) {
|
|
872
|
+
expect(result['stats']).toEqual(stats);
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
it('should handle result without assistant message', () => {
|
|
876
|
+
adapter = new TestJsonOutputAdapter(mockConfig);
|
|
877
|
+
const options = {
|
|
878
|
+
isError: false,
|
|
879
|
+
durationMs: 1000,
|
|
880
|
+
apiDurationMs: 800,
|
|
881
|
+
numTurns: 1,
|
|
882
|
+
};
|
|
883
|
+
const result = adapter.exposeBuildResultMessage(options);
|
|
884
|
+
if (!result.is_error) {
|
|
885
|
+
expect(result.result).toBe('');
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
});
|
|
889
|
+
describe('startSubagentAssistantMessage', () => {
|
|
890
|
+
it('should start subagent message', () => {
|
|
891
|
+
const parentToolUseId = 'parent-tool-1';
|
|
892
|
+
adapter.startSubagentAssistantMessage(parentToolUseId);
|
|
893
|
+
const state = adapter.exposeGetMessageState(parentToolUseId);
|
|
894
|
+
expect(state.messageId).toBeTruthy();
|
|
895
|
+
expect(state.blocks).toEqual([]);
|
|
896
|
+
});
|
|
897
|
+
});
|
|
898
|
+
describe('finalizeSubagentAssistantMessage', () => {
|
|
899
|
+
it('should finalize and return subagent message', () => {
|
|
900
|
+
const parentToolUseId = 'parent-tool-1';
|
|
901
|
+
adapter.startSubagentAssistantMessage(parentToolUseId);
|
|
902
|
+
const state = adapter.exposeGetMessageState(parentToolUseId);
|
|
903
|
+
adapter.exposeAppendText(state, 'Subagent response', parentToolUseId);
|
|
904
|
+
const message = adapter.finalizeSubagentAssistantMessage(parentToolUseId);
|
|
905
|
+
expect(message.type).toBe('assistant');
|
|
906
|
+
expect(message.parent_tool_use_id).toBe(parentToolUseId);
|
|
907
|
+
expect(message.message.content).toHaveLength(1);
|
|
908
|
+
});
|
|
909
|
+
});
|
|
910
|
+
describe('emitSubagentErrorResult', () => {
|
|
911
|
+
it('should emit subagent error result', () => {
|
|
912
|
+
const parentToolUseId = 'parent-tool-1';
|
|
913
|
+
adapter.startSubagentAssistantMessage(parentToolUseId);
|
|
914
|
+
adapter.emitSubagentErrorResult('Error occurred', 5, parentToolUseId);
|
|
915
|
+
expect(adapter.emittedMessages.length).toBeGreaterThan(0);
|
|
916
|
+
const errorResult = adapter.emittedMessages.find((msg) => msg.type === 'result' && msg.is_error === true);
|
|
917
|
+
expect(errorResult).toBeDefined();
|
|
918
|
+
if (errorResult &&
|
|
919
|
+
errorResult.type === 'result' &&
|
|
920
|
+
errorResult.is_error) {
|
|
921
|
+
expect(errorResult.error?.message).toBe('Error occurred');
|
|
922
|
+
expect(errorResult.num_turns).toBe(5);
|
|
923
|
+
}
|
|
924
|
+
});
|
|
925
|
+
it('should finalize pending assistant message before emitting error', () => {
|
|
926
|
+
const parentToolUseId = 'parent-tool-1';
|
|
927
|
+
adapter.startSubagentAssistantMessage(parentToolUseId);
|
|
928
|
+
const state = adapter.exposeGetMessageState(parentToolUseId);
|
|
929
|
+
adapter.exposeAppendText(state, 'Partial response', parentToolUseId);
|
|
930
|
+
adapter.emitSubagentErrorResult('Error', 1, parentToolUseId);
|
|
931
|
+
const assistantMessage = adapter.emittedMessages.find((msg) => msg.type === 'assistant');
|
|
932
|
+
expect(assistantMessage).toBeDefined();
|
|
933
|
+
});
|
|
934
|
+
});
|
|
935
|
+
describe('processSubagentToolCall', () => {
|
|
936
|
+
it('should process subagent tool call', () => {
|
|
937
|
+
const parentToolUseId = 'parent-tool-1';
|
|
938
|
+
adapter.startSubagentAssistantMessage(parentToolUseId);
|
|
939
|
+
const toolCall = {
|
|
940
|
+
callId: 'tool-1',
|
|
941
|
+
name: 'test_tool',
|
|
942
|
+
args: { param: 'value' },
|
|
943
|
+
status: 'success',
|
|
944
|
+
resultDisplay: 'Result',
|
|
945
|
+
};
|
|
946
|
+
adapter.processSubagentToolCall(toolCall, parentToolUseId);
|
|
947
|
+
// processSubagentToolCall finalizes the message and starts a new one,
|
|
948
|
+
// so we should check the emitted messages instead of the state
|
|
949
|
+
const assistantMessages = adapter.emittedMessages.filter((msg) => msg.type === 'assistant' &&
|
|
950
|
+
msg.parent_tool_use_id === parentToolUseId);
|
|
951
|
+
expect(assistantMessages.length).toBeGreaterThan(0);
|
|
952
|
+
const toolUseMessage = assistantMessages.find((msg) => msg.type === 'assistant' &&
|
|
953
|
+
msg.message.content.some((block) => block.type === 'tool_use'));
|
|
954
|
+
expect(toolUseMessage).toBeDefined();
|
|
955
|
+
});
|
|
956
|
+
it('should finalize text message before tool_use', () => {
|
|
957
|
+
const parentToolUseId = 'parent-tool-1';
|
|
958
|
+
adapter.startSubagentAssistantMessage(parentToolUseId);
|
|
959
|
+
const state = adapter.exposeGetMessageState(parentToolUseId);
|
|
960
|
+
adapter.exposeAppendText(state, 'Text', parentToolUseId);
|
|
961
|
+
const toolCall = {
|
|
962
|
+
callId: 'tool-1',
|
|
963
|
+
name: 'test_tool',
|
|
964
|
+
args: {},
|
|
965
|
+
status: 'success',
|
|
966
|
+
resultDisplay: 'Result',
|
|
967
|
+
};
|
|
968
|
+
adapter.processSubagentToolCall(toolCall, parentToolUseId);
|
|
969
|
+
const assistantMessages = adapter.emittedMessages.filter((msg) => msg.type === 'assistant');
|
|
970
|
+
expect(assistantMessages.length).toBeGreaterThan(0);
|
|
971
|
+
});
|
|
972
|
+
});
|
|
973
|
+
describe('createSubagentToolUseBlock', () => {
|
|
974
|
+
it('should create tool_use block for subagent', () => {
|
|
975
|
+
const state = adapter.exposeCreateMessageState();
|
|
976
|
+
adapter.startAssistantMessage();
|
|
977
|
+
const toolCall = {
|
|
978
|
+
callId: 'tool-1',
|
|
979
|
+
name: 'test_tool',
|
|
980
|
+
args: { param: 'value' },
|
|
981
|
+
status: 'success',
|
|
982
|
+
resultDisplay: 'Result',
|
|
983
|
+
};
|
|
984
|
+
const { block, index } = adapter.exposeCreateSubagentToolUseBlock(state, toolCall, 'parent-tool-1');
|
|
985
|
+
expect(block).toMatchObject({
|
|
986
|
+
type: 'tool_use',
|
|
987
|
+
id: 'tool-1',
|
|
988
|
+
name: 'test_tool',
|
|
989
|
+
input: { param: 'value' },
|
|
990
|
+
});
|
|
991
|
+
expect(state.blocks[index]).toBe(block);
|
|
992
|
+
expect(state.openBlocks.has(index)).toBe(true);
|
|
993
|
+
});
|
|
994
|
+
});
|
|
995
|
+
describe('buildSubagentErrorResult', () => {
|
|
996
|
+
it('should build subagent error result', () => {
|
|
997
|
+
const errorResult = adapter.exposeBuildSubagentErrorResult('Error message', 3);
|
|
998
|
+
expect(errorResult.type).toBe('result');
|
|
999
|
+
expect(errorResult.is_error).toBe(true);
|
|
1000
|
+
expect(errorResult.subtype).toBe('error_during_execution');
|
|
1001
|
+
expect(errorResult.error?.message).toBe('Error message');
|
|
1002
|
+
expect(errorResult.num_turns).toBe(3);
|
|
1003
|
+
expect(errorResult.usage).toEqual({
|
|
1004
|
+
input_tokens: 0,
|
|
1005
|
+
output_tokens: 0,
|
|
1006
|
+
});
|
|
1007
|
+
});
|
|
1008
|
+
});
|
|
1009
|
+
describe('getSessionId and getModel', () => {
|
|
1010
|
+
it('should return session ID from config', () => {
|
|
1011
|
+
expect(adapter.getSessionId()).toBe('test-session-id');
|
|
1012
|
+
expect(mockConfig.getSessionId).toHaveBeenCalled();
|
|
1013
|
+
});
|
|
1014
|
+
it('should return model from config', () => {
|
|
1015
|
+
expect(adapter.getModel()).toBe('test-model');
|
|
1016
|
+
expect(mockConfig.getModel).toHaveBeenCalled();
|
|
1017
|
+
});
|
|
1018
|
+
});
|
|
1019
|
+
describe('helper functions', () => {
|
|
1020
|
+
describe('partsToContentBlock', () => {
|
|
1021
|
+
it('should convert text parts to TextBlock array', () => {
|
|
1022
|
+
const parts = [{ text: 'Hello' }, { text: ' World' }];
|
|
1023
|
+
const result = partsToContentBlock(parts);
|
|
1024
|
+
expect(result).toHaveLength(1);
|
|
1025
|
+
expect(result[0]).toEqual({
|
|
1026
|
+
type: 'text',
|
|
1027
|
+
text: 'Hello World',
|
|
1028
|
+
});
|
|
1029
|
+
});
|
|
1030
|
+
it('should handle functionResponse parts by extracting output', () => {
|
|
1031
|
+
const parts = [
|
|
1032
|
+
{ text: 'Result: ' },
|
|
1033
|
+
{
|
|
1034
|
+
functionResponse: {
|
|
1035
|
+
name: 'test',
|
|
1036
|
+
response: { output: 'function output' },
|
|
1037
|
+
},
|
|
1038
|
+
},
|
|
1039
|
+
];
|
|
1040
|
+
const result = partsToContentBlock(parts);
|
|
1041
|
+
expect(result).toHaveLength(1);
|
|
1042
|
+
expect(result[0].type).toBe('text');
|
|
1043
|
+
if (result[0].type === 'text') {
|
|
1044
|
+
expect(result[0].text).toBe('Result: function output');
|
|
1045
|
+
}
|
|
1046
|
+
});
|
|
1047
|
+
it('should handle non-text parts by converting to JSON string', () => {
|
|
1048
|
+
const parts = [
|
|
1049
|
+
{ text: 'Hello' },
|
|
1050
|
+
{ functionCall: { name: 'test' } },
|
|
1051
|
+
];
|
|
1052
|
+
const result = partsToContentBlock(parts);
|
|
1053
|
+
expect(result.length).toBeGreaterThan(0);
|
|
1054
|
+
const textBlock = result.find((block) => block.type === 'text');
|
|
1055
|
+
expect(textBlock).toBeDefined();
|
|
1056
|
+
if (textBlock && textBlock.type === 'text') {
|
|
1057
|
+
expect(textBlock.text).toContain('Hello');
|
|
1058
|
+
expect(textBlock.text).toContain('functionCall');
|
|
1059
|
+
}
|
|
1060
|
+
});
|
|
1061
|
+
it('should handle empty array', () => {
|
|
1062
|
+
const result = partsToContentBlock([]);
|
|
1063
|
+
expect(result).toEqual([]);
|
|
1064
|
+
});
|
|
1065
|
+
it('should merge consecutive text parts into single block', () => {
|
|
1066
|
+
const parts = [
|
|
1067
|
+
{ text: 'Part 1' },
|
|
1068
|
+
{ text: 'Part 2' },
|
|
1069
|
+
{ text: 'Part 3' },
|
|
1070
|
+
];
|
|
1071
|
+
const result = partsToContentBlock(parts);
|
|
1072
|
+
expect(result).toHaveLength(1);
|
|
1073
|
+
expect(result[0]).toEqual({
|
|
1074
|
+
type: 'text',
|
|
1075
|
+
text: 'Part 1Part 2Part 3',
|
|
1076
|
+
});
|
|
1077
|
+
});
|
|
1078
|
+
});
|
|
1079
|
+
describe('partsToString', () => {
|
|
1080
|
+
it('should convert text parts to string', () => {
|
|
1081
|
+
const parts = [{ text: 'Hello' }, { text: ' World' }];
|
|
1082
|
+
const result = partsToString(parts);
|
|
1083
|
+
expect(result).toBe('Hello World');
|
|
1084
|
+
});
|
|
1085
|
+
it('should handle non-text parts', () => {
|
|
1086
|
+
const parts = [
|
|
1087
|
+
{ text: 'Hello' },
|
|
1088
|
+
{ functionCall: { name: 'test' } },
|
|
1089
|
+
];
|
|
1090
|
+
const result = partsToString(parts);
|
|
1091
|
+
expect(result).toContain('Hello');
|
|
1092
|
+
expect(result).toContain('functionCall');
|
|
1093
|
+
});
|
|
1094
|
+
it('should handle empty array', () => {
|
|
1095
|
+
const result = partsToString([]);
|
|
1096
|
+
expect(result).toBe('');
|
|
1097
|
+
});
|
|
1098
|
+
});
|
|
1099
|
+
describe('toolResultContent', () => {
|
|
1100
|
+
it('should extract content from resultDisplay', () => {
|
|
1101
|
+
const response = {
|
|
1102
|
+
callId: 'tool-1',
|
|
1103
|
+
resultDisplay: 'Tool result',
|
|
1104
|
+
responseParts: [],
|
|
1105
|
+
error: undefined,
|
|
1106
|
+
errorType: undefined,
|
|
1107
|
+
};
|
|
1108
|
+
const result = toolResultContent(response);
|
|
1109
|
+
expect(result).toBe('Tool result');
|
|
1110
|
+
});
|
|
1111
|
+
it('should extract content from responseParts', () => {
|
|
1112
|
+
const response = {
|
|
1113
|
+
callId: 'tool-1',
|
|
1114
|
+
resultDisplay: undefined,
|
|
1115
|
+
responseParts: [{ text: 'Result' }],
|
|
1116
|
+
error: undefined,
|
|
1117
|
+
errorType: undefined,
|
|
1118
|
+
};
|
|
1119
|
+
const result = toolResultContent(response);
|
|
1120
|
+
expect(result).toBeTruthy();
|
|
1121
|
+
});
|
|
1122
|
+
it('should extract error message', () => {
|
|
1123
|
+
const response = {
|
|
1124
|
+
callId: 'tool-1',
|
|
1125
|
+
resultDisplay: undefined,
|
|
1126
|
+
responseParts: [],
|
|
1127
|
+
error: new Error('Tool failed'),
|
|
1128
|
+
errorType: undefined,
|
|
1129
|
+
};
|
|
1130
|
+
const result = toolResultContent(response);
|
|
1131
|
+
expect(result).toBe('Tool failed');
|
|
1132
|
+
});
|
|
1133
|
+
it('should return undefined if no content', () => {
|
|
1134
|
+
const response = {
|
|
1135
|
+
callId: 'tool-1',
|
|
1136
|
+
resultDisplay: undefined,
|
|
1137
|
+
responseParts: [],
|
|
1138
|
+
error: undefined,
|
|
1139
|
+
errorType: undefined,
|
|
1140
|
+
};
|
|
1141
|
+
const result = toolResultContent(response);
|
|
1142
|
+
expect(result).toBeUndefined();
|
|
1143
|
+
});
|
|
1144
|
+
it('should ignore empty resultDisplay', () => {
|
|
1145
|
+
const response = {
|
|
1146
|
+
callId: 'tool-1',
|
|
1147
|
+
resultDisplay: ' ',
|
|
1148
|
+
responseParts: [{ text: 'Result' }],
|
|
1149
|
+
error: undefined,
|
|
1150
|
+
errorType: undefined,
|
|
1151
|
+
};
|
|
1152
|
+
const result = toolResultContent(response);
|
|
1153
|
+
expect(result).toBeTruthy();
|
|
1154
|
+
expect(result).not.toBe(' ');
|
|
1155
|
+
});
|
|
1156
|
+
});
|
|
1157
|
+
describe('extractTextFromBlocks', () => {
|
|
1158
|
+
it('should extract text from text blocks', () => {
|
|
1159
|
+
const blocks = [
|
|
1160
|
+
{ type: 'text', text: 'Hello' },
|
|
1161
|
+
{ type: 'text', text: ' World' },
|
|
1162
|
+
];
|
|
1163
|
+
const result = extractTextFromBlocks(blocks);
|
|
1164
|
+
expect(result).toBe('Hello World');
|
|
1165
|
+
});
|
|
1166
|
+
it('should ignore non-text blocks', () => {
|
|
1167
|
+
const blocks = [
|
|
1168
|
+
{ type: 'text', text: 'Hello' },
|
|
1169
|
+
{ type: 'tool_use', id: 'tool-1', name: 'test', input: {} },
|
|
1170
|
+
];
|
|
1171
|
+
const result = extractTextFromBlocks(blocks);
|
|
1172
|
+
expect(result).toBe('Hello');
|
|
1173
|
+
});
|
|
1174
|
+
it('should handle empty array', () => {
|
|
1175
|
+
const result = extractTextFromBlocks([]);
|
|
1176
|
+
expect(result).toBe('');
|
|
1177
|
+
});
|
|
1178
|
+
it('should handle array with no text blocks', () => {
|
|
1179
|
+
const blocks = [
|
|
1180
|
+
{ type: 'tool_use', id: 'tool-1', name: 'test', input: {} },
|
|
1181
|
+
];
|
|
1182
|
+
const result = extractTextFromBlocks(blocks);
|
|
1183
|
+
expect(result).toBe('');
|
|
1184
|
+
});
|
|
1185
|
+
});
|
|
1186
|
+
describe('createExtendedUsage', () => {
|
|
1187
|
+
it('should create extended usage with default values', () => {
|
|
1188
|
+
const usage = createExtendedUsage();
|
|
1189
|
+
expect(usage).toEqual({
|
|
1190
|
+
input_tokens: 0,
|
|
1191
|
+
output_tokens: 0,
|
|
1192
|
+
});
|
|
1193
|
+
});
|
|
1194
|
+
});
|
|
1195
|
+
});
|
|
1196
|
+
});
|
|
1197
|
+
//# sourceMappingURL=BaseJsonOutputAdapter.test.js.map
|