fix: type errors

This commit is contained in:
mingholy.lmh
2025-09-15 14:11:31 +08:00
parent 1976837eda
commit acb93b1e1b
43 changed files with 367 additions and 342 deletions

View File

@@ -416,6 +416,7 @@ export class Task {
private createScheduler(): CoreToolScheduler {
const scheduler = new CoreToolScheduler({
/* @ts-expect-error */
outputUpdateHandler: this._schedulerOutputUpdate.bind(this),
onAllToolCallsComplete: this._schedulerAllToolCallsComplete.bind(this),
onToolCallsUpdate: this._schedulerToolCallsUpdate.bind(this),

View File

@@ -4,7 +4,11 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { CommandKind, SlashCommand, OpenDialogActionReturn } from './types.js';
import {
CommandKind,
type SlashCommand,
type OpenDialogActionReturn,
} from './types.js';
export const agentsCommand: SlashCommand = {
name: 'agents',

View File

@@ -7,12 +7,12 @@
import * as fsPromises from 'fs/promises';
import path from 'path';
import {
SlashCommand,
type SlashCommand,
CommandKind,
SlashCommandActionReturn,
type SlashCommandActionReturn,
} from './types.js';
import { getProjectSummaryPrompt } from '@qwen-code/qwen-code-core';
import { HistoryItemSummary } from '../types.js';
import type { HistoryItemSummary } from '../types.js';
export const summaryCommand: SlashCommand = {
name: 'summary',

View File

@@ -9,7 +9,7 @@ import React from 'react';
import { Colors } from '../colors.js';
import {
RadioButtonSelect,
RadioSelectItem,
type RadioSelectItem,
} from './shared/RadioButtonSelect.js';
import { useKeypress } from '../hooks/useKeypress.js';

View File

@@ -6,10 +6,10 @@
import { Box, Text } from 'ink';
import { Colors } from '../colors.js';
import { ProjectSummaryInfo } from '@qwen-code/qwen-code-core';
import { type ProjectSummaryInfo } from '@qwen-code/qwen-code-core';
import {
RadioButtonSelect,
RadioSelectItem,
type RadioSelectItem,
} from './shared/RadioButtonSelect.js';
import { useKeypress } from '../hooks/useKeypress.js';

View File

@@ -6,7 +6,7 @@
import React from 'react';
import { Box, Text } from 'ink';
import { SummaryProps } from '../../types.js';
import type { SummaryProps } from '../../types.js';
import Spinner from 'ink-spinner';
import { Colors } from '../../colors.js';

View File

@@ -38,260 +38,260 @@ export const ToolConfirmationMessage: React.FC<
availableTerminalHeight,
terminalWidth,
}) => {
const { onConfirm } = confirmationDetails;
const childWidth = terminalWidth - 2; // 2 for padding
const { onConfirm } = confirmationDetails;
const childWidth = terminalWidth - 2; // 2 for padding
const handleConfirm = async (outcome: ToolConfirmationOutcome) => {
if (confirmationDetails.type === 'edit') {
const ideClient = config.getIdeClient();
if (config.getIdeMode()) {
const cliOutcome =
outcome === ToolConfirmationOutcome.Cancel ? 'rejected' : 'accepted';
await ideClient?.resolveDiffFromCli(
confirmationDetails.filePath,
cliOutcome,
);
}
}
onConfirm(outcome);
};
const isTrustedFolder = config.isTrustedFolder() !== false;
useKeypress(
(key) => {
if (!isFocused) return;
if (key.name === 'escape' || (key.ctrl && key.name === 'c')) {
handleConfirm(ToolConfirmationOutcome.Cancel);
}
},
{ isActive: isFocused },
);
const handleSelect = (item: ToolConfirmationOutcome) => handleConfirm(item);
let bodyContent: React.ReactNode | null = null; // Removed contextDisplay here
let question: string;
const options: Array<RadioSelectItem<ToolConfirmationOutcome>> = new Array<
RadioSelectItem<ToolConfirmationOutcome>
>();
// Body content is now the DiffRenderer, passing filename to it
// The bordered box is removed from here and handled within DiffRenderer
function availableBodyContentHeight() {
if (options.length === 0) {
// This should not happen in practice as options are always added before this is called.
throw new Error('Options not provided for confirmation message');
}
if (availableTerminalHeight === undefined) {
return undefined;
}
// Calculate the vertical space (in lines) consumed by UI elements
// surrounding the main body content.
const PADDING_OUTER_Y = 2; // Main container has `padding={1}` (top & bottom).
const MARGIN_BODY_BOTTOM = 1; // margin on the body container.
const HEIGHT_QUESTION = 1; // The question text is one line.
const MARGIN_QUESTION_BOTTOM = 1; // Margin on the question container.
const HEIGHT_OPTIONS = options.length; // Each option in the radio select takes one line.
const surroundingElementsHeight =
PADDING_OUTER_Y +
MARGIN_BODY_BOTTOM +
HEIGHT_QUESTION +
MARGIN_QUESTION_BOTTOM +
HEIGHT_OPTIONS;
return Math.max(availableTerminalHeight - surroundingElementsHeight, 1);
}
const handleConfirm = async (outcome: ToolConfirmationOutcome) => {
if (confirmationDetails.type === 'edit') {
const ideClient = config.getIdeClient();
if (config.getIdeMode()) {
const cliOutcome =
outcome === ToolConfirmationOutcome.Cancel ? 'rejected' : 'accepted';
await ideClient?.resolveDiffFromCli(
confirmationDetails.filePath,
cliOutcome,
if (confirmationDetails.isModifying) {
return (
<Box
minWidth="90%"
borderStyle="round"
borderColor={Colors.Gray}
justifyContent="space-around"
padding={1}
overflow="hidden"
>
<Text>Modify in progress: </Text>
<Text color={Colors.AccentGreen}>
Save and close external editor to continue
</Text>
</Box>
);
}
}
onConfirm(outcome);
};
const isTrustedFolder = config.isTrustedFolder() !== false;
useKeypress(
(key) => {
if (!isFocused) return;
if (key.name === 'escape' || (key.ctrl && key.name === 'c')) {
handleConfirm(ToolConfirmationOutcome.Cancel);
}
},
{ isActive: isFocused },
);
const handleSelect = (item: ToolConfirmationOutcome) => handleConfirm(item);
let bodyContent: React.ReactNode | null = null; // Removed contextDisplay here
let question: string;
const options: Array<RadioSelectItem<ToolConfirmationOutcome>> = new Array<
RadioSelectItem<ToolConfirmationOutcome>
>();
// Body content is now the DiffRenderer, passing filename to it
// The bordered box is removed from here and handled within DiffRenderer
function availableBodyContentHeight() {
if (options.length === 0) {
// This should not happen in practice as options are always added before this is called.
throw new Error('Options not provided for confirmation message');
}
if (availableTerminalHeight === undefined) {
return undefined;
}
// Calculate the vertical space (in lines) consumed by UI elements
// surrounding the main body content.
const PADDING_OUTER_Y = 2; // Main container has `padding={1}` (top & bottom).
const MARGIN_BODY_BOTTOM = 1; // margin on the body container.
const HEIGHT_QUESTION = 1; // The question text is one line.
const MARGIN_QUESTION_BOTTOM = 1; // Margin on the question container.
const HEIGHT_OPTIONS = options.length; // Each option in the radio select takes one line.
const surroundingElementsHeight =
PADDING_OUTER_Y +
MARGIN_BODY_BOTTOM +
HEIGHT_QUESTION +
MARGIN_QUESTION_BOTTOM +
HEIGHT_OPTIONS;
return Math.max(availableTerminalHeight - surroundingElementsHeight, 1);
}
if (confirmationDetails.type === 'edit') {
if (confirmationDetails.isModifying) {
return (
<Box
minWidth="90%"
borderStyle="round"
borderColor={Colors.Gray}
justifyContent="space-around"
padding={1}
overflow="hidden"
>
<Text>Modify in progress: </Text>
<Text color={Colors.AccentGreen}>
Save and close external editor to continue
</Text>
</Box>
);
}
question = `Apply this change?`;
options.push({
label: 'Yes, allow once',
value: ToolConfirmationOutcome.ProceedOnce,
});
if (isTrustedFolder) {
question = `Apply this change?`;
options.push({
label: 'Yes, allow always',
value: ToolConfirmationOutcome.ProceedAlways,
label: 'Yes, allow once',
value: ToolConfirmationOutcome.ProceedOnce,
});
}
if (config.getIdeMode()) {
if (isTrustedFolder) {
options.push({
label: 'Yes, allow always',
value: ToolConfirmationOutcome.ProceedAlways,
});
}
if (config.getIdeMode()) {
options.push({
label: 'No (esc)',
value: ToolConfirmationOutcome.Cancel,
});
} else {
options.push({
label: 'Modify with external editor',
value: ToolConfirmationOutcome.ModifyWithEditor,
});
options.push({
label: 'No, suggest changes (esc)',
value: ToolConfirmationOutcome.Cancel,
});
}
bodyContent = (
<DiffRenderer
diffContent={confirmationDetails.fileDiff}
filename={confirmationDetails.fileName}
availableTerminalHeight={availableBodyContentHeight()}
terminalWidth={childWidth}
/>
);
} else if (confirmationDetails.type === 'exec') {
const executionProps =
confirmationDetails as ToolExecuteConfirmationDetails;
question = `Allow execution of: '${executionProps.rootCommand}'?`;
options.push({
label: 'No (esc)',
label: 'Yes, allow once',
value: ToolConfirmationOutcome.ProceedOnce,
});
if (isTrustedFolder) {
options.push({
label: `Yes, allow always ...`,
value: ToolConfirmationOutcome.ProceedAlways,
});
}
options.push({
label: 'No, suggest changes (esc)',
value: ToolConfirmationOutcome.Cancel,
});
} else {
let bodyContentHeight = availableBodyContentHeight();
if (bodyContentHeight !== undefined) {
bodyContentHeight -= 2; // Account for padding;
}
bodyContent = (
<Box flexDirection="column">
<Box paddingX={1} marginLeft={1}>
<MaxSizedBox
maxHeight={bodyContentHeight}
maxWidth={Math.max(childWidth - 4, 1)}
>
<Box>
<Text color={Colors.AccentCyan}>{executionProps.command}</Text>
</Box>
</MaxSizedBox>
</Box>
</Box>
);
} else if (confirmationDetails.type === 'info') {
const infoProps = confirmationDetails;
const displayUrls =
infoProps.urls &&
!(infoProps.urls.length === 1 && infoProps.urls[0] === infoProps.prompt);
question = `Do you want to proceed?`;
options.push({
label: 'Modify with external editor',
value: ToolConfirmationOutcome.ModifyWithEditor,
label: 'Yes, allow once',
value: ToolConfirmationOutcome.ProceedOnce,
});
if (isTrustedFolder) {
options.push({
label: 'Yes, allow always',
value: ToolConfirmationOutcome.ProceedAlways,
});
}
options.push({
label: 'No, suggest changes (esc)',
value: ToolConfirmationOutcome.Cancel,
});
bodyContent = (
<Box flexDirection="column" paddingX={1} marginLeft={1}>
<Text color={Colors.AccentCyan}>
<RenderInline text={infoProps.prompt} />
</Text>
{displayUrls && infoProps.urls && infoProps.urls.length > 0 && (
<Box flexDirection="column" marginTop={1}>
<Text>URLs to fetch:</Text>
{infoProps.urls.map((url) => (
<Text key={url}>
{' '}
- <RenderInline text={url} />
</Text>
))}
</Box>
)}
</Box>
);
} else {
// mcp tool confirmation
const mcpProps = confirmationDetails as ToolMcpConfirmationDetails;
bodyContent = (
<Box flexDirection="column" paddingX={1} marginLeft={1}>
<Text color={Colors.AccentCyan}>MCP Server: {mcpProps.serverName}</Text>
<Text color={Colors.AccentCyan}>Tool: {mcpProps.toolName}</Text>
</Box>
);
question = `Allow execution of MCP tool "${mcpProps.toolName}" from server "${mcpProps.serverName}"?`;
options.push({
label: 'Yes, allow once',
value: ToolConfirmationOutcome.ProceedOnce,
});
if (isTrustedFolder) {
options.push({
label: `Yes, always allow tool "${mcpProps.toolName}" from server "${mcpProps.serverName}"`,
value: ToolConfirmationOutcome.ProceedAlwaysTool, // Cast until types are updated
});
options.push({
label: `Yes, always allow all tools from server "${mcpProps.serverName}"`,
value: ToolConfirmationOutcome.ProceedAlwaysServer,
});
}
options.push({
label: 'No, suggest changes (esc)',
value: ToolConfirmationOutcome.Cancel,
});
}
bodyContent = (
<DiffRenderer
diffContent={confirmationDetails.fileDiff}
filename={confirmationDetails.fileName}
availableTerminalHeight={availableBodyContentHeight()}
terminalWidth={childWidth}
/>
);
} else if (confirmationDetails.type === 'exec') {
const executionProps =
confirmationDetails as ToolExecuteConfirmationDetails;
return (
<Box flexDirection="column" padding={1} width={childWidth}>
{/* Body Content (Diff Renderer or Command Info) */}
{/* No separate context display here anymore for edits */}
<Box flexGrow={1} flexShrink={1} overflow="hidden" marginBottom={1}>
{bodyContent}
</Box>
question = `Allow execution of: '${executionProps.rootCommand}'?`;
options.push({
label: 'Yes, allow once',
value: ToolConfirmationOutcome.ProceedOnce,
});
if (isTrustedFolder) {
options.push({
label: `Yes, allow always ...`,
value: ToolConfirmationOutcome.ProceedAlways,
});
}
options.push({
label: 'No, suggest changes (esc)',
value: ToolConfirmationOutcome.Cancel,
});
{/* Confirmation Question */}
<Box marginBottom={1} flexShrink={0}>
<Text wrap="truncate">{question}</Text>
</Box>
let bodyContentHeight = availableBodyContentHeight();
if (bodyContentHeight !== undefined) {
bodyContentHeight -= 2; // Account for padding;
}
bodyContent = (
<Box flexDirection="column">
<Box paddingX={1} marginLeft={1}>
<MaxSizedBox
maxHeight={bodyContentHeight}
maxWidth={Math.max(childWidth - 4, 1)}
>
<Box>
<Text color={Colors.AccentCyan}>{executionProps.command}</Text>
</Box>
</MaxSizedBox>
{/* Select Input for Options */}
<Box flexShrink={0}>
<RadioButtonSelect
items={options}
onSelect={handleSelect}
isFocused={isFocused}
/>
</Box>
</Box>
);
} else if (confirmationDetails.type === 'info') {
const infoProps = confirmationDetails;
const displayUrls =
infoProps.urls &&
!(infoProps.urls.length === 1 && infoProps.urls[0] === infoProps.prompt);
question = `Do you want to proceed?`;
options.push({
label: 'Yes, allow once',
value: ToolConfirmationOutcome.ProceedOnce,
});
if (isTrustedFolder) {
options.push({
label: 'Yes, allow always',
value: ToolConfirmationOutcome.ProceedAlways,
});
}
options.push({
label: 'No, suggest changes (esc)',
value: ToolConfirmationOutcome.Cancel,
});
bodyContent = (
<Box flexDirection="column" paddingX={1} marginLeft={1}>
<Text color={Colors.AccentCyan}>
<RenderInline text={infoProps.prompt} />
</Text>
{displayUrls && infoProps.urls && infoProps.urls.length > 0 && (
<Box flexDirection="column" marginTop={1}>
<Text>URLs to fetch:</Text>
{infoProps.urls.map((url) => (
<Text key={url}>
{' '}
- <RenderInline text={url} />
</Text>
))}
</Box>
)}
</Box>
);
} else {
// mcp tool confirmation
const mcpProps = confirmationDetails as ToolMcpConfirmationDetails;
bodyContent = (
<Box flexDirection="column" paddingX={1} marginLeft={1}>
<Text color={Colors.AccentCyan}>MCP Server: {mcpProps.serverName}</Text>
<Text color={Colors.AccentCyan}>Tool: {mcpProps.toolName}</Text>
</Box>
);
question = `Allow execution of MCP tool "${mcpProps.toolName}" from server "${mcpProps.serverName}"?`;
options.push({
label: 'Yes, allow once',
value: ToolConfirmationOutcome.ProceedOnce,
});
if (isTrustedFolder) {
options.push({
label: `Yes, always allow tool "${mcpProps.toolName}" from server "${mcpProps.serverName}"`,
value: ToolConfirmationOutcome.ProceedAlwaysTool, // Cast until types are updated
});
options.push({
label: `Yes, always allow all tools from server "${mcpProps.serverName}"`,
value: ToolConfirmationOutcome.ProceedAlwaysServer,
});
}
options.push({
label: 'No, suggest changes (esc)',
value: ToolConfirmationOutcome.Cancel,
});
}
return (
<Box flexDirection="column" padding={1} width={childWidth}>
{/* Body Content (Diff Renderer or Command Info) */}
{/* No separate context display here anymore for edits */}
<Box flexGrow={1} flexShrink={1} overflow="hidden" marginBottom={1}>
{bodyContent}
</Box>
{/* Confirmation Question */}
<Box marginBottom={1} flexShrink={0}>
<Text wrap="truncate">{question}</Text>
</Box>
{/* Select Input for Options */}
<Box flexShrink={0}>
<RadioButtonSelect
items={options}
onSelect={handleSelect}
isFocused={isFocused}
/>
</Box>
</Box>
);
};
};

View File

@@ -60,12 +60,12 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
const countOneLineToolCalls = toolCalls.length - countToolCallsWithResults;
const availableTerminalHeightPerToolMessage = availableTerminalHeight
? Math.max(
Math.floor(
(availableTerminalHeight - staticHeight - countOneLineToolCalls) /
Math.max(1, countToolCallsWithResults),
),
1,
)
Math.floor(
(availableTerminalHeight - staticHeight - countOneLineToolCalls) /
Math.max(1, countToolCallsWithResults),
),
1,
)
: undefined;
return (

View File

@@ -189,9 +189,9 @@ export const ToolMessage: React.FC<ToolMessageProps> = ({
}) => {
const availableHeight = availableTerminalHeight
? Math.max(
availableTerminalHeight - STATIC_HEIGHT - RESERVED_LINE_COUNT,
MIN_LINES_SHOWN + 1, // enforce minimum lines shown
)
availableTerminalHeight - STATIC_HEIGHT - RESERVED_LINE_COUNT,
MIN_LINES_SHOWN + 1, // enforce minimum lines shown
)
: undefined;
// Long tool call response in MarkdownDisplay doesn't respect availableTerminalHeight properly,

View File

@@ -13,7 +13,7 @@ import { DescriptionInput } from './DescriptionInput.js';
import { ToolSelector } from './ToolSelector.js';
import { ColorSelector } from './ColorSelector.js';
import { CreationSummary } from './CreationSummary.js';
import { WizardStepProps } from '../types.js';
import { type WizardStepProps } from '../types.js';
import { WIZARD_STEPS } from '../constants.js';
import { getStepKind } from '../utils.js';
import { Config } from '@qwen-code/qwen-code-core';

View File

@@ -7,7 +7,7 @@
import { useState, useEffect } from 'react';
import { Box, Text } from 'ink';
import { RadioButtonSelect } from '../../shared/RadioButtonSelect.js';
import { ColorOption } from '../types.js';
import { type ColorOption } from '../types.js';
import { Colors } from '../../../colors.js';
import { COLOR_OPTIONS } from '../constants.js';

View File

@@ -6,8 +6,8 @@
import { useCallback, useState, useEffect } from 'react';
import { Box, Text, useInput } from 'ink';
import { WizardStepProps } from '../types.js';
import { SubagentManager, SubagentConfig } from '@qwen-code/qwen-code-core';
import { type WizardStepProps } from '../types.js';
import { SubagentManager, type SubagentConfig } from '@qwen-code/qwen-code-core';
import { theme } from '../../../semantic-colors.js';
import { shouldShowColor, getColorForDisplay } from '../utils.js';
import { useLaunchEditor } from '../../../hooks/useLaunchEditor.js';

View File

@@ -7,10 +7,10 @@
import { useCallback, useRef } from 'react';
import { Box, Text } from 'ink';
import Spinner from 'ink-spinner';
import { WizardStepProps, WizardAction } from '../types.js';
import type { WizardStepProps, WizardAction } from '../types.js';
import { sanitizeInput } from '../utils.js';
import { Config, subagentGenerator } from '@qwen-code/qwen-code-core';
import { useKeypress, Key } from '../../../hooks/useKeypress.js';
import { useKeypress, type Key } from '../../../hooks/useKeypress.js';
import { keyMatchers, Command } from '../../../keyMatchers.js';
import { theme } from '../../../semantic-colors.js';
import { Colors } from '../../../colors.js';

View File

@@ -6,7 +6,7 @@
import { Box } from 'ink';
import { RadioButtonSelect } from '../../shared/RadioButtonSelect.js';
import { WizardStepProps } from '../types.js';
import type { WizardStepProps } from '../types.js';
interface GenerationOption {
label: string;

View File

@@ -6,7 +6,7 @@
import { Box } from 'ink';
import { RadioButtonSelect } from '../../shared/RadioButtonSelect.js';
import { WizardStepProps } from '../types.js';
import type { WizardStepProps } from '../types.js';
interface LocationOption {
label: string;

View File

@@ -6,7 +6,7 @@
import { useCallback } from 'react';
import { Box, Text } from 'ink';
import { WizardStepProps } from '../types.js';
import type { WizardStepProps } from '../types.js';
import { Colors } from '../../../colors.js';
import { TextInput } from '../../shared/TextInput.js';

View File

@@ -7,7 +7,7 @@
import { useState, useMemo, useEffect } from 'react';
import { Box, Text } from 'ink';
import { RadioButtonSelect } from '../../shared/RadioButtonSelect.js';
import { ToolCategory } from '../types.js';
import type { ToolCategory } from '../types.js';
import { Kind, Config } from '@qwen-code/qwen-code-core';
import { Colors } from '../../../colors.js';

View File

@@ -8,7 +8,7 @@ import { useState } from 'react';
import { Box } from 'ink';
import { RadioButtonSelect } from '../../shared/RadioButtonSelect.js';
import { MANAGEMENT_STEPS } from '../types.js';
import { SubagentConfig } from '@qwen-code/qwen-code-core';
import { type SubagentConfig } from '@qwen-code/qwen-code-core';
interface ActionSelectionStepProps {
selectedAgent: SubagentConfig | null;
@@ -35,8 +35,8 @@ export const ActionSelectionStep = ({
const actions = selectedAgent?.isBuiltin
? allActions.filter(
(action) => action.value === 'view' || action.value === 'back',
)
(action) => action.value === 'view' || action.value === 'back',
)
: allActions;
const handleActionSelect = (value: 'view' | 'edit' | 'delete' | 'back') => {

View File

@@ -5,8 +5,8 @@
*/
import { Box, Text } from 'ink';
import { SubagentConfig } from '@qwen-code/qwen-code-core';
import { StepNavigationProps } from '../types.js';
import { type SubagentConfig } from '@qwen-code/qwen-code-core';
import type { StepNavigationProps } from '../types.js';
import { theme } from '../../../semantic-colors.js';
import { useKeypress } from '../../../hooks/useKeypress.js';

View File

@@ -10,7 +10,7 @@ import { RadioButtonSelect } from '../../shared/RadioButtonSelect.js';
import { MANAGEMENT_STEPS } from '../types.js';
import { theme } from '../../../semantic-colors.js';
import { useLaunchEditor } from '../../../hooks/useLaunchEditor.js';
import { SubagentConfig } from '@qwen-code/qwen-code-core';
import { type SubagentConfig } from '@qwen-code/qwen-code-core';
interface EditOption {
id: string;

View File

@@ -9,7 +9,7 @@ import { Box, Text } from 'ink';
import { theme } from '../../../semantic-colors.js';
import { Colors } from '../../../colors.js';
import { useKeypress } from '../../../hooks/useKeypress.js';
import { SubagentConfig } from '@qwen-code/qwen-code-core';
import { type SubagentConfig } from '@qwen-code/qwen-code-core';
interface NavigationState {
currentBlock: 'project' | 'user' | 'builtin';
@@ -318,12 +318,12 @@ export const AgentSelectionStep = ({
{(projectAgents.length > 0 ||
userAgents.length > 0 ||
builtinAgents.length > 0) && (
<Box marginTop={1}>
<Text color={theme.text.secondary}>
Using: {enabledAgentsCount} agents
</Text>
</Box>
)}
<Box marginTop={1}>
<Text color={theme.text.secondary}>
Using: {enabledAgentsCount} agents
</Text>
</Box>
)}
</Box>
);
};

View File

@@ -7,7 +7,7 @@
import { Box, Text } from 'ink';
import { theme } from '../../../semantic-colors.js';
import { shouldShowColor, getColorForDisplay } from '../utils.js';
import { SubagentConfig } from '@qwen-code/qwen-code-core';
import { type SubagentConfig } from '@qwen-code/qwen-code-core';
interface AgentViewerStepProps {
selectedAgent: SubagentConfig | null;

View File

@@ -17,7 +17,7 @@ import { MANAGEMENT_STEPS } from '../types.js';
import { Colors } from '../../../colors.js';
import { theme } from '../../../semantic-colors.js';
import { getColorForDisplay, shouldShowColor } from '../utils.js';
import { Config, SubagentConfig } from '@qwen-code/qwen-code-core';
import { Config, type SubagentConfig } from '@qwen-code/qwen-code-core';
interface AgentsManagerDialogProps {
onClose: () => void;
@@ -170,8 +170,8 @@ export function AgentsManagerDialog({
// Use agent color for the Agent Viewer header
const headerColor =
currentStep === MANAGEMENT_STEPS.AGENT_VIEWER &&
selectedAgent &&
shouldShowColor(selectedAgent.color)
selectedAgent &&
shouldShowColor(selectedAgent.color)
? getColorForDisplay(selectedAgent.color)
: undefined;

View File

@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { CreationWizardState, WizardAction } from './types.js';
import { type CreationWizardState, type WizardAction } from './types.js';
import { WIZARD_STEPS } from './constants.js';
import { getStepKind, getTotalSteps } from './utils.js';

View File

@@ -7,7 +7,7 @@
import React, { useMemo } from 'react';
import { Box, Text } from 'ink';
import { Colors } from '../../../colors.js';
import {
import type {
TaskResultDisplay,
SubagentStatsSummary,
} from '@qwen-code/qwen-code-core';
@@ -163,8 +163,8 @@ export const AgentExecutionDisplay: React.FC<AgentExecutionDisplayProps> = ({
{(data.status === 'completed' ||
data.status === 'failed' ||
data.status === 'cancelled') && (
<ResultsSection data={data} displayMode={displayMode} />
)}
<ResultsSection data={data} displayMode={displayMode} />
)}
{/* Footer with keyboard shortcuts */}
{footerText && (

View File

@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { SubagentLevel, Config } from '@qwen-code/qwen-code-core';
import { type SubagentLevel, Config } from '@qwen-code/qwen-code-core';
/**
* State management for the subagent creation wizard.

View File

@@ -6,7 +6,7 @@
import { useCallback } from 'react';
import { useStdin } from 'ink';
import { EditorType } from '@qwen-code/qwen-code-core';
import type { EditorType } from '@qwen-code/qwen-code-core';
import { spawnSync } from 'child_process';
import { useSettings } from '../contexts/SettingsContext.js';
@@ -39,7 +39,7 @@ export function useLaunchEditor() {
const launchEditor = useCallback(
async (filePath: string): Promise<void> => {
const preferredEditor = settings.merged.preferredEditor as
const preferredEditor = settings.merged.general?.preferredEditor as
| EditorType
| undefined;
const editor = getEditorCommand(preferredEditor);
@@ -75,7 +75,7 @@ export function useLaunchEditor() {
if (wasRaw) setRawMode?.(true);
}
},
[settings.merged.preferredEditor, setRawMode, stdin],
[settings.merged.general?.preferredEditor, setRawMode, stdin],
);
return launchEditor;

View File

@@ -794,11 +794,11 @@ describe('CoreToolScheduler cancellation during executing with live output', ()
model: 'test-model',
authType: 'oauth-personal',
}),
getToolRegistry: () => mockToolRegistry,
} as unknown as Config;
const scheduler = new CoreToolScheduler({
config: mockConfig,
toolRegistry: mockToolRegistry,
onAllToolCallsComplete,
onToolCallsUpdate,
getPreferredEditor: () => 'vscode',

View File

@@ -19,7 +19,7 @@ import { isGitRepository } from '../utils/gitUtils.js';
import { MemoryTool, GEMINI_CONFIG_DIR } from '../tools/memoryTool.js';
import { TodoWriteTool } from '../tools/todoWrite.js';
import { TaskTool } from '../tools/task.js';
import { GenerateContentConfig } from '@google/genai';
import type { GenerateContentConfig } from '@google/genai';
export interface ModelTemplateMapping {
baseUrls?: string[];

View File

@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { SubagentConfig } from './types.js';
import type { SubagentConfig } from './types.js';
/**
* Registry of built-in subagents that are always available to all users.

View File

@@ -6,7 +6,7 @@
import { EventEmitter } from 'events';
import {
ToolCallConfirmationDetails,
type ToolCallConfirmationDetails,
ToolConfirmationOutcome,
} from '../tools/tools.js';

View File

@@ -9,7 +9,7 @@ import * as fs from 'fs/promises';
import * as path from 'path';
import * as os from 'os';
import { SubagentManager } from './subagent-manager.js';
import { SubagentConfig, SubagentError } from './types.js';
import { type SubagentConfig, SubagentError } from './types.js';
import { ToolRegistry } from '../tools/tool-registry.js';
import { Config } from '../config/config.js';
import { makeFakeConfig } from '../test-utils/config.js';

View File

@@ -13,19 +13,18 @@ import {
parse as parseYaml,
stringify as stringifyYaml,
} from '../utils/yaml-parser.js';
import {
import type {
SubagentConfig,
SubagentRuntimeConfig,
SubagentLevel,
ListSubagentsOptions,
CreateSubagentOptions,
SubagentError,
SubagentErrorCode,
PromptConfig,
ModelConfig,
RunConfig,
ToolConfig,
} from './types.js';
import { SubagentError, SubagentErrorCode } from './types.js';
import { SubagentValidator } from './validation.js';
import { SubAgentScope } from './subagent.js';
import { Config } from '../config/config.js';

View File

@@ -165,7 +165,7 @@ describe('subagent.ts', () => {
// Default mock for executeToolCall
vi.mocked(executeToolCall).mockResolvedValue({
callId: 'default-call',
responseParts: 'default response',
responseParts: [{ text: 'default response' }],
resultDisplay: 'Default tool result',
error: undefined,
errorType: undefined,

View File

@@ -6,11 +6,11 @@
import { reportError } from '../utils/errorReporting.js';
import { Config } from '../config/config.js';
import { ToolCallRequestInfo } from '../core/turn.js';
import { type ToolCallRequestInfo } from '../core/turn.js';
import {
CoreToolScheduler,
ToolCall,
WaitingToolCall,
type ToolCall,
type WaitingToolCall,
} from '../core/coreToolScheduler.js';
import type {
ToolConfirmationOutcome,
@@ -18,7 +18,7 @@ import type {
} from '../tools/tools.js';
import { createContentGenerator } from '../core/contentGenerator.js';
import { getEnvironmentContext } from '../utils/environmentContext.js';
import {
import type {
Content,
Part,
FunctionCall,
@@ -27,16 +27,14 @@ import {
GenerateContentResponseUsageMetadata,
} from '@google/genai';
import { GeminiChat } from '../core/geminiChat.js';
import {
SubagentTerminateMode,
import type {
PromptConfig,
ModelConfig,
RunConfig,
ToolConfig,
} from './types.js';
import {
SubAgentEventEmitter,
SubAgentEventType,
import { SubagentTerminateMode } from './types.js';
import type {
SubAgentFinishEvent,
SubAgentRoundEvent,
SubAgentStartEvent,
@@ -45,11 +43,12 @@ import {
SubAgentStreamTextEvent,
SubAgentErrorEvent,
} from './subagent-events.js';
import { SubAgentEventEmitter, SubAgentEventType } from './subagent-events.js';
import {
SubagentStatistics,
SubagentStatsSummary,
type SubagentStatsSummary,
} from './subagent-statistics.js';
import { SubagentHooks } from './subagent-hooks.js';
import type { SubagentHooks } from './subagent-hooks.js';
import { logSubagentExecution } from '../telemetry/loggers.js';
import { SubagentExecutionEvent } from '../telemetry/types.js';
import { TaskTool } from '../tools/task.js';
@@ -379,26 +378,36 @@ export class SubAgentScope {
let roundText = '';
let lastUsage: GenerateContentResponseUsageMetadata | undefined =
undefined;
for await (const resp of responseStream) {
for await (const streamEvent of responseStream) {
if (abortController.signal.aborted) {
this.terminateMode = SubagentTerminateMode.CANCELLED;
return;
}
if (resp.functionCalls) functionCalls.push(...resp.functionCalls);
const content = resp.candidates?.[0]?.content;
const parts = content?.parts || [];
for (const p of parts) {
const txt = (p as Part & { text?: string }).text;
if (txt) roundText += txt;
if (txt)
this.eventEmitter?.emit(SubAgentEventType.STREAM_TEXT, {
subagentId: this.subagentId,
round: turnCounter,
text: txt,
timestamp: Date.now(),
} as SubAgentStreamTextEvent);
// Handle retry events
if (streamEvent.type === 'retry') {
continue;
}
// Handle chunk events
if (streamEvent.type === 'chunk') {
const resp = streamEvent.value;
if (resp.functionCalls) functionCalls.push(...resp.functionCalls);
const content = resp.candidates?.[0]?.content;
const parts = content?.parts || [];
for (const p of parts) {
const txt = (p as Part & { text?: string }).text;
if (txt) roundText += txt;
if (txt)
this.eventEmitter?.emit(SubAgentEventType.STREAM_TEXT, {
subagentId: this.subagentId,
round: turnCounter,
text: txt,
timestamp: Date.now(),
} as SubAgentStreamTextEvent);
}
if (resp.usageMetadata) lastUsage = resp.usageMetadata;
}
if (resp.usageMetadata) lastUsage = resp.usageMetadata;
}
this.executionStats.rounds = turnCounter;
this.stats.setRounds(turnCounter);
@@ -546,7 +555,6 @@ export class SubAgentScope {
const responded = new Set<string>();
let resolveBatch: (() => void) | null = null;
const scheduler = new CoreToolScheduler({
toolRegistry: this.runtimeContext.getToolRegistry(),
outputUpdateHandler: undefined,
onAllToolCallsComplete: async (completedCalls) => {
for (const call of completedCalls) {

View File

@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { Content, FunctionDeclaration } from '@google/genai';
import type { Content, FunctionDeclaration } from '@google/genai';
/**
* Represents the storage level for a subagent configuration.

View File

@@ -6,7 +6,7 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { SubagentValidator } from './validation.js';
import { SubagentConfig, SubagentError } from './types.js';
import { type SubagentConfig, SubagentError } from './types.js';
describe('SubagentValidator', () => {
let validator: SubagentValidator;

View File

@@ -4,13 +4,12 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {
SubagentConfig,
ValidationResult,
SubagentError,
SubagentErrorCode,
import { SubagentError, SubagentErrorCode } from './types.js';
import type {
ModelConfig,
RunConfig,
SubagentConfig,
ValidationResult,
} from './types.js';
/**

View File

@@ -5,12 +5,15 @@
*/
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { TaskTool, TaskParams } from './task.js';
import { TaskTool, type TaskParams } from './task.js';
import type { PartListUnion } from '@google/genai';
import type { ToolResultDisplay, TaskResultDisplay } from './tools.js';
import { Config } from '../config/config.js';
import { SubagentManager } from '../subagents/subagent-manager.js';
import { SubagentConfig, SubagentTerminateMode } from '../subagents/types.js';
import {
type SubagentConfig,
SubagentTerminateMode,
} from '../subagents/types.js';
import { SubAgentScope, ContextState } from '../subagents/subagent.js';
import { partToString } from '../utils/partUtils.js';

View File

@@ -4,10 +4,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
import {
BaseDeclarativeTool,
BaseToolInvocation,
Kind,
import { BaseDeclarativeTool, BaseToolInvocation, Kind } from './tools.js';
import type {
ToolResult,
ToolResultDisplay,
TaskResultDisplay,
@@ -19,14 +17,19 @@ import type {
} from './tools.js';
import { Config } from '../config/config.js';
import { SubagentManager } from '../subagents/subagent-manager.js';
import { SubagentConfig, SubagentTerminateMode } from '../subagents/types.js';
import {
type SubagentConfig,
SubagentTerminateMode,
} from '../subagents/types.js';
import { ContextState } from '../subagents/subagent.js';
import {
SubAgentEventEmitter,
SubAgentEventType,
} from '../subagents/subagent-events.js';
import type {
SubAgentToolCallEvent,
SubAgentToolResultEvent,
SubAgentFinishEvent,
SubAgentEventType,
SubAgentErrorEvent,
SubAgentApprovalRequestEvent,
} from '../subagents/subagent-events.js';

View File

@@ -8,7 +8,7 @@ import type { FunctionDeclaration, PartListUnion } from '@google/genai';
import { ToolErrorType } from './tool-error.js';
import type { DiffUpdateResult } from '../ide/ideContext.js';
import { SchemaValidator } from '../utils/schemaValidator.js';
import { SubagentStatsSummary } from '../subagents/subagent-statistics.js';
import { type SubagentStatsSummary } from '../subagents/subagent-statistics.js';
/**
* Represents a validated and ready-to-execute tool call.

View File

@@ -4,14 +4,22 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi, beforeEach, Mock, afterEach } from 'vitest';
import { Content, GoogleGenAI, Models } from '@google/genai';
import {
describe,
it,
expect,
vi,
beforeEach,
type Mock,
afterEach,
} from 'vitest';
import { type Content, GoogleGenAI, Models } from '@google/genai';
import { DEFAULT_QWEN_MODEL } from '../config/models.js';
import { GeminiClient } from '../core/client.js';
import { Config } from '../config/config.js';
import {
subagentGenerator,
SubagentGeneratedContent,
type SubagentGeneratedContent,
} from './subagentGenerator.js';
// Mock GeminiClient and Config constructor

View File

@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { Content } from '@google/genai';
import { type Content } from '@google/genai';
import { DEFAULT_QWEN_MODEL } from '../config/models.js';
import { GeminiClient } from '../core/client.js';