mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-20 08:47:44 +00:00
fix: type errors
This commit is contained in:
@@ -416,6 +416,7 @@ export class Task {
|
|||||||
|
|
||||||
private createScheduler(): CoreToolScheduler {
|
private createScheduler(): CoreToolScheduler {
|
||||||
const scheduler = new CoreToolScheduler({
|
const scheduler = new CoreToolScheduler({
|
||||||
|
/* @ts-expect-error */
|
||||||
outputUpdateHandler: this._schedulerOutputUpdate.bind(this),
|
outputUpdateHandler: this._schedulerOutputUpdate.bind(this),
|
||||||
onAllToolCallsComplete: this._schedulerAllToolCallsComplete.bind(this),
|
onAllToolCallsComplete: this._schedulerAllToolCallsComplete.bind(this),
|
||||||
onToolCallsUpdate: this._schedulerToolCallsUpdate.bind(this),
|
onToolCallsUpdate: this._schedulerToolCallsUpdate.bind(this),
|
||||||
|
|||||||
@@ -4,7 +4,11 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* 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 = {
|
export const agentsCommand: SlashCommand = {
|
||||||
name: 'agents',
|
name: 'agents',
|
||||||
|
|||||||
@@ -7,12 +7,12 @@
|
|||||||
import * as fsPromises from 'fs/promises';
|
import * as fsPromises from 'fs/promises';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import {
|
import {
|
||||||
SlashCommand,
|
type SlashCommand,
|
||||||
CommandKind,
|
CommandKind,
|
||||||
SlashCommandActionReturn,
|
type SlashCommandActionReturn,
|
||||||
} from './types.js';
|
} from './types.js';
|
||||||
import { getProjectSummaryPrompt } from '@qwen-code/qwen-code-core';
|
import { getProjectSummaryPrompt } from '@qwen-code/qwen-code-core';
|
||||||
import { HistoryItemSummary } from '../types.js';
|
import type { HistoryItemSummary } from '../types.js';
|
||||||
|
|
||||||
export const summaryCommand: SlashCommand = {
|
export const summaryCommand: SlashCommand = {
|
||||||
name: 'summary',
|
name: 'summary',
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import React from 'react';
|
|||||||
import { Colors } from '../colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import {
|
import {
|
||||||
RadioButtonSelect,
|
RadioButtonSelect,
|
||||||
RadioSelectItem,
|
type RadioSelectItem,
|
||||||
} from './shared/RadioButtonSelect.js';
|
} from './shared/RadioButtonSelect.js';
|
||||||
import { useKeypress } from '../hooks/useKeypress.js';
|
import { useKeypress } from '../hooks/useKeypress.js';
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,10 @@
|
|||||||
|
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { Colors } from '../colors.js';
|
import { Colors } from '../colors.js';
|
||||||
import { ProjectSummaryInfo } from '@qwen-code/qwen-code-core';
|
import { type ProjectSummaryInfo } from '@qwen-code/qwen-code-core';
|
||||||
import {
|
import {
|
||||||
RadioButtonSelect,
|
RadioButtonSelect,
|
||||||
RadioSelectItem,
|
type RadioSelectItem,
|
||||||
} from './shared/RadioButtonSelect.js';
|
} from './shared/RadioButtonSelect.js';
|
||||||
import { useKeypress } from '../hooks/useKeypress.js';
|
import { useKeypress } from '../hooks/useKeypress.js';
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { SummaryProps } from '../../types.js';
|
import type { SummaryProps } from '../../types.js';
|
||||||
import Spinner from 'ink-spinner';
|
import Spinner from 'ink-spinner';
|
||||||
import { Colors } from '../../colors.js';
|
import { Colors } from '../../colors.js';
|
||||||
|
|
||||||
|
|||||||
@@ -38,260 +38,260 @@ export const ToolConfirmationMessage: React.FC<
|
|||||||
availableTerminalHeight,
|
availableTerminalHeight,
|
||||||
terminalWidth,
|
terminalWidth,
|
||||||
}) => {
|
}) => {
|
||||||
const { onConfirm } = confirmationDetails;
|
const { onConfirm } = confirmationDetails;
|
||||||
const childWidth = terminalWidth - 2; // 2 for padding
|
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') {
|
if (confirmationDetails.type === 'edit') {
|
||||||
const ideClient = config.getIdeClient();
|
if (confirmationDetails.isModifying) {
|
||||||
if (config.getIdeMode()) {
|
return (
|
||||||
const cliOutcome =
|
<Box
|
||||||
outcome === ToolConfirmationOutcome.Cancel ? 'rejected' : 'accepted';
|
minWidth="90%"
|
||||||
await ideClient?.resolveDiffFromCli(
|
borderStyle="round"
|
||||||
confirmationDetails.filePath,
|
borderColor={Colors.Gray}
|
||||||
cliOutcome,
|
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;
|
question = `Apply this change?`;
|
||||||
|
|
||||||
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) {
|
|
||||||
options.push({
|
options.push({
|
||||||
label: 'Yes, allow always',
|
label: 'Yes, allow once',
|
||||||
value: ToolConfirmationOutcome.ProceedAlways,
|
value: ToolConfirmationOutcome.ProceedOnce,
|
||||||
});
|
});
|
||||||
}
|
if (isTrustedFolder) {
|
||||||
if (config.getIdeMode()) {
|
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({
|
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,
|
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({
|
options.push({
|
||||||
label: 'Modify with external editor',
|
label: 'Yes, allow once',
|
||||||
value: ToolConfirmationOutcome.ModifyWithEditor,
|
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({
|
options.push({
|
||||||
label: 'No, suggest changes (esc)',
|
label: 'No, suggest changes (esc)',
|
||||||
value: ToolConfirmationOutcome.Cancel,
|
value: ToolConfirmationOutcome.Cancel,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bodyContent = (
|
return (
|
||||||
<DiffRenderer
|
<Box flexDirection="column" padding={1} width={childWidth}>
|
||||||
diffContent={confirmationDetails.fileDiff}
|
{/* Body Content (Diff Renderer or Command Info) */}
|
||||||
filename={confirmationDetails.fileName}
|
{/* No separate context display here anymore for edits */}
|
||||||
availableTerminalHeight={availableBodyContentHeight()}
|
<Box flexGrow={1} flexShrink={1} overflow="hidden" marginBottom={1}>
|
||||||
terminalWidth={childWidth}
|
{bodyContent}
|
||||||
/>
|
</Box>
|
||||||
);
|
|
||||||
} else if (confirmationDetails.type === 'exec') {
|
|
||||||
const executionProps =
|
|
||||||
confirmationDetails as ToolExecuteConfirmationDetails;
|
|
||||||
|
|
||||||
question = `Allow execution of: '${executionProps.rootCommand}'?`;
|
{/* Confirmation Question */}
|
||||||
options.push({
|
<Box marginBottom={1} flexShrink={0}>
|
||||||
label: 'Yes, allow once',
|
<Text wrap="truncate">{question}</Text>
|
||||||
value: ToolConfirmationOutcome.ProceedOnce,
|
</Box>
|
||||||
});
|
|
||||||
if (isTrustedFolder) {
|
|
||||||
options.push({
|
|
||||||
label: `Yes, allow always ...`,
|
|
||||||
value: ToolConfirmationOutcome.ProceedAlways,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
options.push({
|
|
||||||
label: 'No, suggest changes (esc)',
|
|
||||||
value: ToolConfirmationOutcome.Cancel,
|
|
||||||
});
|
|
||||||
|
|
||||||
let bodyContentHeight = availableBodyContentHeight();
|
{/* Select Input for Options */}
|
||||||
if (bodyContentHeight !== undefined) {
|
<Box flexShrink={0}>
|
||||||
bodyContentHeight -= 2; // Account for padding;
|
<RadioButtonSelect
|
||||||
}
|
items={options}
|
||||||
bodyContent = (
|
onSelect={handleSelect}
|
||||||
<Box flexDirection="column">
|
isFocused={isFocused}
|
||||||
<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>
|
||||||
</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>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -60,12 +60,12 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
|
|||||||
const countOneLineToolCalls = toolCalls.length - countToolCallsWithResults;
|
const countOneLineToolCalls = toolCalls.length - countToolCallsWithResults;
|
||||||
const availableTerminalHeightPerToolMessage = availableTerminalHeight
|
const availableTerminalHeightPerToolMessage = availableTerminalHeight
|
||||||
? Math.max(
|
? Math.max(
|
||||||
Math.floor(
|
Math.floor(
|
||||||
(availableTerminalHeight - staticHeight - countOneLineToolCalls) /
|
(availableTerminalHeight - staticHeight - countOneLineToolCalls) /
|
||||||
Math.max(1, countToolCallsWithResults),
|
Math.max(1, countToolCallsWithResults),
|
||||||
),
|
),
|
||||||
1,
|
1,
|
||||||
)
|
)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -189,9 +189,9 @@ export const ToolMessage: React.FC<ToolMessageProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const availableHeight = availableTerminalHeight
|
const availableHeight = availableTerminalHeight
|
||||||
? Math.max(
|
? Math.max(
|
||||||
availableTerminalHeight - STATIC_HEIGHT - RESERVED_LINE_COUNT,
|
availableTerminalHeight - STATIC_HEIGHT - RESERVED_LINE_COUNT,
|
||||||
MIN_LINES_SHOWN + 1, // enforce minimum lines shown
|
MIN_LINES_SHOWN + 1, // enforce minimum lines shown
|
||||||
)
|
)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
// Long tool call response in MarkdownDisplay doesn't respect availableTerminalHeight properly,
|
// Long tool call response in MarkdownDisplay doesn't respect availableTerminalHeight properly,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { DescriptionInput } from './DescriptionInput.js';
|
|||||||
import { ToolSelector } from './ToolSelector.js';
|
import { ToolSelector } from './ToolSelector.js';
|
||||||
import { ColorSelector } from './ColorSelector.js';
|
import { ColorSelector } from './ColorSelector.js';
|
||||||
import { CreationSummary } from './CreationSummary.js';
|
import { CreationSummary } from './CreationSummary.js';
|
||||||
import { WizardStepProps } from '../types.js';
|
import { type WizardStepProps } from '../types.js';
|
||||||
import { WIZARD_STEPS } from '../constants.js';
|
import { WIZARD_STEPS } from '../constants.js';
|
||||||
import { getStepKind } from '../utils.js';
|
import { getStepKind } from '../utils.js';
|
||||||
import { Config } from '@qwen-code/qwen-code-core';
|
import { Config } from '@qwen-code/qwen-code-core';
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { RadioButtonSelect } from '../../shared/RadioButtonSelect.js';
|
import { RadioButtonSelect } from '../../shared/RadioButtonSelect.js';
|
||||||
import { ColorOption } from '../types.js';
|
import { type ColorOption } from '../types.js';
|
||||||
import { Colors } from '../../../colors.js';
|
import { Colors } from '../../../colors.js';
|
||||||
import { COLOR_OPTIONS } from '../constants.js';
|
import { COLOR_OPTIONS } from '../constants.js';
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
import { useCallback, useState, useEffect } from 'react';
|
import { useCallback, useState, useEffect } from 'react';
|
||||||
import { Box, Text, useInput } from 'ink';
|
import { Box, Text, useInput } from 'ink';
|
||||||
import { WizardStepProps } from '../types.js';
|
import { type WizardStepProps } from '../types.js';
|
||||||
import { SubagentManager, SubagentConfig } from '@qwen-code/qwen-code-core';
|
import { SubagentManager, type SubagentConfig } from '@qwen-code/qwen-code-core';
|
||||||
import { theme } from '../../../semantic-colors.js';
|
import { theme } from '../../../semantic-colors.js';
|
||||||
import { shouldShowColor, getColorForDisplay } from '../utils.js';
|
import { shouldShowColor, getColorForDisplay } from '../utils.js';
|
||||||
import { useLaunchEditor } from '../../../hooks/useLaunchEditor.js';
|
import { useLaunchEditor } from '../../../hooks/useLaunchEditor.js';
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
import { useCallback, useRef } from 'react';
|
import { useCallback, useRef } from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import Spinner from 'ink-spinner';
|
import Spinner from 'ink-spinner';
|
||||||
import { WizardStepProps, WizardAction } from '../types.js';
|
import type { WizardStepProps, WizardAction } from '../types.js';
|
||||||
import { sanitizeInput } from '../utils.js';
|
import { sanitizeInput } from '../utils.js';
|
||||||
import { Config, subagentGenerator } from '@qwen-code/qwen-code-core';
|
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 { keyMatchers, Command } from '../../../keyMatchers.js';
|
||||||
import { theme } from '../../../semantic-colors.js';
|
import { theme } from '../../../semantic-colors.js';
|
||||||
import { Colors } from '../../../colors.js';
|
import { Colors } from '../../../colors.js';
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
import { Box } from 'ink';
|
import { Box } from 'ink';
|
||||||
import { RadioButtonSelect } from '../../shared/RadioButtonSelect.js';
|
import { RadioButtonSelect } from '../../shared/RadioButtonSelect.js';
|
||||||
import { WizardStepProps } from '../types.js';
|
import type { WizardStepProps } from '../types.js';
|
||||||
|
|
||||||
interface GenerationOption {
|
interface GenerationOption {
|
||||||
label: string;
|
label: string;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
import { Box } from 'ink';
|
import { Box } from 'ink';
|
||||||
import { RadioButtonSelect } from '../../shared/RadioButtonSelect.js';
|
import { RadioButtonSelect } from '../../shared/RadioButtonSelect.js';
|
||||||
import { WizardStepProps } from '../types.js';
|
import type { WizardStepProps } from '../types.js';
|
||||||
|
|
||||||
interface LocationOption {
|
interface LocationOption {
|
||||||
label: string;
|
label: string;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { WizardStepProps } from '../types.js';
|
import type { WizardStepProps } from '../types.js';
|
||||||
import { Colors } from '../../../colors.js';
|
import { Colors } from '../../../colors.js';
|
||||||
import { TextInput } from '../../shared/TextInput.js';
|
import { TextInput } from '../../shared/TextInput.js';
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import { useState, useMemo, useEffect } from 'react';
|
import { useState, useMemo, useEffect } from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { RadioButtonSelect } from '../../shared/RadioButtonSelect.js';
|
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 { Kind, Config } from '@qwen-code/qwen-code-core';
|
||||||
import { Colors } from '../../../colors.js';
|
import { Colors } from '../../../colors.js';
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { useState } from 'react';
|
|||||||
import { Box } from 'ink';
|
import { Box } from 'ink';
|
||||||
import { RadioButtonSelect } from '../../shared/RadioButtonSelect.js';
|
import { RadioButtonSelect } from '../../shared/RadioButtonSelect.js';
|
||||||
import { MANAGEMENT_STEPS } from '../types.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 {
|
interface ActionSelectionStepProps {
|
||||||
selectedAgent: SubagentConfig | null;
|
selectedAgent: SubagentConfig | null;
|
||||||
@@ -35,8 +35,8 @@ export const ActionSelectionStep = ({
|
|||||||
|
|
||||||
const actions = selectedAgent?.isBuiltin
|
const actions = selectedAgent?.isBuiltin
|
||||||
? allActions.filter(
|
? allActions.filter(
|
||||||
(action) => action.value === 'view' || action.value === 'back',
|
(action) => action.value === 'view' || action.value === 'back',
|
||||||
)
|
)
|
||||||
: allActions;
|
: allActions;
|
||||||
|
|
||||||
const handleActionSelect = (value: 'view' | 'edit' | 'delete' | 'back') => {
|
const handleActionSelect = (value: 'view' | 'edit' | 'delete' | 'back') => {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { SubagentConfig } from '@qwen-code/qwen-code-core';
|
import { type SubagentConfig } from '@qwen-code/qwen-code-core';
|
||||||
import { StepNavigationProps } from '../types.js';
|
import type { StepNavigationProps } from '../types.js';
|
||||||
import { theme } from '../../../semantic-colors.js';
|
import { theme } from '../../../semantic-colors.js';
|
||||||
import { useKeypress } from '../../../hooks/useKeypress.js';
|
import { useKeypress } from '../../../hooks/useKeypress.js';
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { RadioButtonSelect } from '../../shared/RadioButtonSelect.js';
|
|||||||
import { MANAGEMENT_STEPS } from '../types.js';
|
import { MANAGEMENT_STEPS } from '../types.js';
|
||||||
import { theme } from '../../../semantic-colors.js';
|
import { theme } from '../../../semantic-colors.js';
|
||||||
import { useLaunchEditor } from '../../../hooks/useLaunchEditor.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 {
|
interface EditOption {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { Box, Text } from 'ink';
|
|||||||
import { theme } from '../../../semantic-colors.js';
|
import { theme } from '../../../semantic-colors.js';
|
||||||
import { Colors } from '../../../colors.js';
|
import { Colors } from '../../../colors.js';
|
||||||
import { useKeypress } from '../../../hooks/useKeypress.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 {
|
interface NavigationState {
|
||||||
currentBlock: 'project' | 'user' | 'builtin';
|
currentBlock: 'project' | 'user' | 'builtin';
|
||||||
@@ -318,12 +318,12 @@ export const AgentSelectionStep = ({
|
|||||||
{(projectAgents.length > 0 ||
|
{(projectAgents.length > 0 ||
|
||||||
userAgents.length > 0 ||
|
userAgents.length > 0 ||
|
||||||
builtinAgents.length > 0) && (
|
builtinAgents.length > 0) && (
|
||||||
<Box marginTop={1}>
|
<Box marginTop={1}>
|
||||||
<Text color={theme.text.secondary}>
|
<Text color={theme.text.secondary}>
|
||||||
Using: {enabledAgentsCount} agents
|
Using: {enabledAgentsCount} agents
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../../../semantic-colors.js';
|
import { theme } from '../../../semantic-colors.js';
|
||||||
import { shouldShowColor, getColorForDisplay } from '../utils.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 {
|
interface AgentViewerStepProps {
|
||||||
selectedAgent: SubagentConfig | null;
|
selectedAgent: SubagentConfig | null;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { MANAGEMENT_STEPS } from '../types.js';
|
|||||||
import { Colors } from '../../../colors.js';
|
import { Colors } from '../../../colors.js';
|
||||||
import { theme } from '../../../semantic-colors.js';
|
import { theme } from '../../../semantic-colors.js';
|
||||||
import { getColorForDisplay, shouldShowColor } from '../utils.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 {
|
interface AgentsManagerDialogProps {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@@ -170,8 +170,8 @@ export function AgentsManagerDialog({
|
|||||||
// Use agent color for the Agent Viewer header
|
// Use agent color for the Agent Viewer header
|
||||||
const headerColor =
|
const headerColor =
|
||||||
currentStep === MANAGEMENT_STEPS.AGENT_VIEWER &&
|
currentStep === MANAGEMENT_STEPS.AGENT_VIEWER &&
|
||||||
selectedAgent &&
|
selectedAgent &&
|
||||||
shouldShowColor(selectedAgent.color)
|
shouldShowColor(selectedAgent.color)
|
||||||
? getColorForDisplay(selectedAgent.color)
|
? getColorForDisplay(selectedAgent.color)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* 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 { WIZARD_STEPS } from './constants.js';
|
||||||
import { getStepKind, getTotalSteps } from './utils.js';
|
import { getStepKind, getTotalSteps } from './utils.js';
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { Colors } from '../../../colors.js';
|
import { Colors } from '../../../colors.js';
|
||||||
import {
|
import type {
|
||||||
TaskResultDisplay,
|
TaskResultDisplay,
|
||||||
SubagentStatsSummary,
|
SubagentStatsSummary,
|
||||||
} from '@qwen-code/qwen-code-core';
|
} from '@qwen-code/qwen-code-core';
|
||||||
@@ -163,8 +163,8 @@ export const AgentExecutionDisplay: React.FC<AgentExecutionDisplayProps> = ({
|
|||||||
{(data.status === 'completed' ||
|
{(data.status === 'completed' ||
|
||||||
data.status === 'failed' ||
|
data.status === 'failed' ||
|
||||||
data.status === 'cancelled') && (
|
data.status === 'cancelled') && (
|
||||||
<ResultsSection data={data} displayMode={displayMode} />
|
<ResultsSection data={data} displayMode={displayMode} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Footer with keyboard shortcuts */}
|
{/* Footer with keyboard shortcuts */}
|
||||||
{footerText && (
|
{footerText && (
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* 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.
|
* State management for the subagent creation wizard.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useStdin } from 'ink';
|
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 { spawnSync } from 'child_process';
|
||||||
import { useSettings } from '../contexts/SettingsContext.js';
|
import { useSettings } from '../contexts/SettingsContext.js';
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ export function useLaunchEditor() {
|
|||||||
|
|
||||||
const launchEditor = useCallback(
|
const launchEditor = useCallback(
|
||||||
async (filePath: string): Promise<void> => {
|
async (filePath: string): Promise<void> => {
|
||||||
const preferredEditor = settings.merged.preferredEditor as
|
const preferredEditor = settings.merged.general?.preferredEditor as
|
||||||
| EditorType
|
| EditorType
|
||||||
| undefined;
|
| undefined;
|
||||||
const editor = getEditorCommand(preferredEditor);
|
const editor = getEditorCommand(preferredEditor);
|
||||||
@@ -75,7 +75,7 @@ export function useLaunchEditor() {
|
|||||||
if (wasRaw) setRawMode?.(true);
|
if (wasRaw) setRawMode?.(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[settings.merged.preferredEditor, setRawMode, stdin],
|
[settings.merged.general?.preferredEditor, setRawMode, stdin],
|
||||||
);
|
);
|
||||||
|
|
||||||
return launchEditor;
|
return launchEditor;
|
||||||
|
|||||||
@@ -794,11 +794,11 @@ describe('CoreToolScheduler cancellation during executing with live output', ()
|
|||||||
model: 'test-model',
|
model: 'test-model',
|
||||||
authType: 'oauth-personal',
|
authType: 'oauth-personal',
|
||||||
}),
|
}),
|
||||||
|
getToolRegistry: () => mockToolRegistry,
|
||||||
} as unknown as Config;
|
} as unknown as Config;
|
||||||
|
|
||||||
const scheduler = new CoreToolScheduler({
|
const scheduler = new CoreToolScheduler({
|
||||||
config: mockConfig,
|
config: mockConfig,
|
||||||
toolRegistry: mockToolRegistry,
|
|
||||||
onAllToolCallsComplete,
|
onAllToolCallsComplete,
|
||||||
onToolCallsUpdate,
|
onToolCallsUpdate,
|
||||||
getPreferredEditor: () => 'vscode',
|
getPreferredEditor: () => 'vscode',
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import { isGitRepository } from '../utils/gitUtils.js';
|
|||||||
import { MemoryTool, GEMINI_CONFIG_DIR } from '../tools/memoryTool.js';
|
import { MemoryTool, GEMINI_CONFIG_DIR } from '../tools/memoryTool.js';
|
||||||
import { TodoWriteTool } from '../tools/todoWrite.js';
|
import { TodoWriteTool } from '../tools/todoWrite.js';
|
||||||
import { TaskTool } from '../tools/task.js';
|
import { TaskTool } from '../tools/task.js';
|
||||||
import { GenerateContentConfig } from '@google/genai';
|
import type { GenerateContentConfig } from '@google/genai';
|
||||||
|
|
||||||
export interface ModelTemplateMapping {
|
export interface ModelTemplateMapping {
|
||||||
baseUrls?: string[];
|
baseUrls?: string[];
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* 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.
|
* Registry of built-in subagents that are always available to all users.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import {
|
import {
|
||||||
ToolCallConfirmationDetails,
|
type ToolCallConfirmationDetails,
|
||||||
ToolConfirmationOutcome,
|
ToolConfirmationOutcome,
|
||||||
} from '../tools/tools.js';
|
} from '../tools/tools.js';
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import * as fs from 'fs/promises';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import { SubagentManager } from './subagent-manager.js';
|
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 { ToolRegistry } from '../tools/tool-registry.js';
|
||||||
import { Config } from '../config/config.js';
|
import { Config } from '../config/config.js';
|
||||||
import { makeFakeConfig } from '../test-utils/config.js';
|
import { makeFakeConfig } from '../test-utils/config.js';
|
||||||
|
|||||||
@@ -13,19 +13,18 @@ import {
|
|||||||
parse as parseYaml,
|
parse as parseYaml,
|
||||||
stringify as stringifyYaml,
|
stringify as stringifyYaml,
|
||||||
} from '../utils/yaml-parser.js';
|
} from '../utils/yaml-parser.js';
|
||||||
import {
|
import type {
|
||||||
SubagentConfig,
|
SubagentConfig,
|
||||||
SubagentRuntimeConfig,
|
SubagentRuntimeConfig,
|
||||||
SubagentLevel,
|
SubagentLevel,
|
||||||
ListSubagentsOptions,
|
ListSubagentsOptions,
|
||||||
CreateSubagentOptions,
|
CreateSubagentOptions,
|
||||||
SubagentError,
|
|
||||||
SubagentErrorCode,
|
|
||||||
PromptConfig,
|
PromptConfig,
|
||||||
ModelConfig,
|
ModelConfig,
|
||||||
RunConfig,
|
RunConfig,
|
||||||
ToolConfig,
|
ToolConfig,
|
||||||
} from './types.js';
|
} from './types.js';
|
||||||
|
import { SubagentError, SubagentErrorCode } from './types.js';
|
||||||
import { SubagentValidator } from './validation.js';
|
import { SubagentValidator } from './validation.js';
|
||||||
import { SubAgentScope } from './subagent.js';
|
import { SubAgentScope } from './subagent.js';
|
||||||
import { Config } from '../config/config.js';
|
import { Config } from '../config/config.js';
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ describe('subagent.ts', () => {
|
|||||||
// Default mock for executeToolCall
|
// Default mock for executeToolCall
|
||||||
vi.mocked(executeToolCall).mockResolvedValue({
|
vi.mocked(executeToolCall).mockResolvedValue({
|
||||||
callId: 'default-call',
|
callId: 'default-call',
|
||||||
responseParts: 'default response',
|
responseParts: [{ text: 'default response' }],
|
||||||
resultDisplay: 'Default tool result',
|
resultDisplay: 'Default tool result',
|
||||||
error: undefined,
|
error: undefined,
|
||||||
errorType: undefined,
|
errorType: undefined,
|
||||||
|
|||||||
@@ -6,11 +6,11 @@
|
|||||||
|
|
||||||
import { reportError } from '../utils/errorReporting.js';
|
import { reportError } from '../utils/errorReporting.js';
|
||||||
import { Config } from '../config/config.js';
|
import { Config } from '../config/config.js';
|
||||||
import { ToolCallRequestInfo } from '../core/turn.js';
|
import { type ToolCallRequestInfo } from '../core/turn.js';
|
||||||
import {
|
import {
|
||||||
CoreToolScheduler,
|
CoreToolScheduler,
|
||||||
ToolCall,
|
type ToolCall,
|
||||||
WaitingToolCall,
|
type WaitingToolCall,
|
||||||
} from '../core/coreToolScheduler.js';
|
} from '../core/coreToolScheduler.js';
|
||||||
import type {
|
import type {
|
||||||
ToolConfirmationOutcome,
|
ToolConfirmationOutcome,
|
||||||
@@ -18,7 +18,7 @@ import type {
|
|||||||
} from '../tools/tools.js';
|
} from '../tools/tools.js';
|
||||||
import { createContentGenerator } from '../core/contentGenerator.js';
|
import { createContentGenerator } from '../core/contentGenerator.js';
|
||||||
import { getEnvironmentContext } from '../utils/environmentContext.js';
|
import { getEnvironmentContext } from '../utils/environmentContext.js';
|
||||||
import {
|
import type {
|
||||||
Content,
|
Content,
|
||||||
Part,
|
Part,
|
||||||
FunctionCall,
|
FunctionCall,
|
||||||
@@ -27,16 +27,14 @@ import {
|
|||||||
GenerateContentResponseUsageMetadata,
|
GenerateContentResponseUsageMetadata,
|
||||||
} from '@google/genai';
|
} from '@google/genai';
|
||||||
import { GeminiChat } from '../core/geminiChat.js';
|
import { GeminiChat } from '../core/geminiChat.js';
|
||||||
import {
|
import type {
|
||||||
SubagentTerminateMode,
|
|
||||||
PromptConfig,
|
PromptConfig,
|
||||||
ModelConfig,
|
ModelConfig,
|
||||||
RunConfig,
|
RunConfig,
|
||||||
ToolConfig,
|
ToolConfig,
|
||||||
} from './types.js';
|
} from './types.js';
|
||||||
import {
|
import { SubagentTerminateMode } from './types.js';
|
||||||
SubAgentEventEmitter,
|
import type {
|
||||||
SubAgentEventType,
|
|
||||||
SubAgentFinishEvent,
|
SubAgentFinishEvent,
|
||||||
SubAgentRoundEvent,
|
SubAgentRoundEvent,
|
||||||
SubAgentStartEvent,
|
SubAgentStartEvent,
|
||||||
@@ -45,11 +43,12 @@ import {
|
|||||||
SubAgentStreamTextEvent,
|
SubAgentStreamTextEvent,
|
||||||
SubAgentErrorEvent,
|
SubAgentErrorEvent,
|
||||||
} from './subagent-events.js';
|
} from './subagent-events.js';
|
||||||
|
import { SubAgentEventEmitter, SubAgentEventType } from './subagent-events.js';
|
||||||
import {
|
import {
|
||||||
SubagentStatistics,
|
SubagentStatistics,
|
||||||
SubagentStatsSummary,
|
type SubagentStatsSummary,
|
||||||
} from './subagent-statistics.js';
|
} from './subagent-statistics.js';
|
||||||
import { SubagentHooks } from './subagent-hooks.js';
|
import type { SubagentHooks } from './subagent-hooks.js';
|
||||||
import { logSubagentExecution } from '../telemetry/loggers.js';
|
import { logSubagentExecution } from '../telemetry/loggers.js';
|
||||||
import { SubagentExecutionEvent } from '../telemetry/types.js';
|
import { SubagentExecutionEvent } from '../telemetry/types.js';
|
||||||
import { TaskTool } from '../tools/task.js';
|
import { TaskTool } from '../tools/task.js';
|
||||||
@@ -379,26 +378,36 @@ export class SubAgentScope {
|
|||||||
let roundText = '';
|
let roundText = '';
|
||||||
let lastUsage: GenerateContentResponseUsageMetadata | undefined =
|
let lastUsage: GenerateContentResponseUsageMetadata | undefined =
|
||||||
undefined;
|
undefined;
|
||||||
for await (const resp of responseStream) {
|
for await (const streamEvent of responseStream) {
|
||||||
if (abortController.signal.aborted) {
|
if (abortController.signal.aborted) {
|
||||||
this.terminateMode = SubagentTerminateMode.CANCELLED;
|
this.terminateMode = SubagentTerminateMode.CANCELLED;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (resp.functionCalls) functionCalls.push(...resp.functionCalls);
|
|
||||||
const content = resp.candidates?.[0]?.content;
|
// Handle retry events
|
||||||
const parts = content?.parts || [];
|
if (streamEvent.type === 'retry') {
|
||||||
for (const p of parts) {
|
continue;
|
||||||
const txt = (p as Part & { text?: string }).text;
|
}
|
||||||
if (txt) roundText += txt;
|
|
||||||
if (txt)
|
// Handle chunk events
|
||||||
this.eventEmitter?.emit(SubAgentEventType.STREAM_TEXT, {
|
if (streamEvent.type === 'chunk') {
|
||||||
subagentId: this.subagentId,
|
const resp = streamEvent.value;
|
||||||
round: turnCounter,
|
if (resp.functionCalls) functionCalls.push(...resp.functionCalls);
|
||||||
text: txt,
|
const content = resp.candidates?.[0]?.content;
|
||||||
timestamp: Date.now(),
|
const parts = content?.parts || [];
|
||||||
} as SubAgentStreamTextEvent);
|
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.executionStats.rounds = turnCounter;
|
||||||
this.stats.setRounds(turnCounter);
|
this.stats.setRounds(turnCounter);
|
||||||
@@ -546,7 +555,6 @@ export class SubAgentScope {
|
|||||||
const responded = new Set<string>();
|
const responded = new Set<string>();
|
||||||
let resolveBatch: (() => void) | null = null;
|
let resolveBatch: (() => void) | null = null;
|
||||||
const scheduler = new CoreToolScheduler({
|
const scheduler = new CoreToolScheduler({
|
||||||
toolRegistry: this.runtimeContext.getToolRegistry(),
|
|
||||||
outputUpdateHandler: undefined,
|
outputUpdateHandler: undefined,
|
||||||
onAllToolCallsComplete: async (completedCalls) => {
|
onAllToolCallsComplete: async (completedCalls) => {
|
||||||
for (const call of completedCalls) {
|
for (const call of completedCalls) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* 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.
|
* Represents the storage level for a subagent configuration.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
import { describe, it, expect, beforeEach } from 'vitest';
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
import { SubagentValidator } from './validation.js';
|
import { SubagentValidator } from './validation.js';
|
||||||
import { SubagentConfig, SubagentError } from './types.js';
|
import { type SubagentConfig, SubagentError } from './types.js';
|
||||||
|
|
||||||
describe('SubagentValidator', () => {
|
describe('SubagentValidator', () => {
|
||||||
let validator: SubagentValidator;
|
let validator: SubagentValidator;
|
||||||
|
|||||||
@@ -4,13 +4,12 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import { SubagentError, SubagentErrorCode } from './types.js';
|
||||||
SubagentConfig,
|
import type {
|
||||||
ValidationResult,
|
|
||||||
SubagentError,
|
|
||||||
SubagentErrorCode,
|
|
||||||
ModelConfig,
|
ModelConfig,
|
||||||
RunConfig,
|
RunConfig,
|
||||||
|
SubagentConfig,
|
||||||
|
ValidationResult,
|
||||||
} from './types.js';
|
} from './types.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -5,12 +5,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
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 { PartListUnion } from '@google/genai';
|
||||||
import type { ToolResultDisplay, TaskResultDisplay } from './tools.js';
|
import type { ToolResultDisplay, TaskResultDisplay } from './tools.js';
|
||||||
import { Config } from '../config/config.js';
|
import { Config } from '../config/config.js';
|
||||||
import { SubagentManager } from '../subagents/subagent-manager.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 { SubAgentScope, ContextState } from '../subagents/subagent.js';
|
||||||
import { partToString } from '../utils/partUtils.js';
|
import { partToString } from '../utils/partUtils.js';
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,8 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import { BaseDeclarativeTool, BaseToolInvocation, Kind } from './tools.js';
|
||||||
BaseDeclarativeTool,
|
import type {
|
||||||
BaseToolInvocation,
|
|
||||||
Kind,
|
|
||||||
ToolResult,
|
ToolResult,
|
||||||
ToolResultDisplay,
|
ToolResultDisplay,
|
||||||
TaskResultDisplay,
|
TaskResultDisplay,
|
||||||
@@ -19,14 +17,19 @@ import type {
|
|||||||
} from './tools.js';
|
} from './tools.js';
|
||||||
import { Config } from '../config/config.js';
|
import { Config } from '../config/config.js';
|
||||||
import { SubagentManager } from '../subagents/subagent-manager.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 { ContextState } from '../subagents/subagent.js';
|
||||||
import {
|
import {
|
||||||
SubAgentEventEmitter,
|
SubAgentEventEmitter,
|
||||||
|
SubAgentEventType,
|
||||||
|
} from '../subagents/subagent-events.js';
|
||||||
|
import type {
|
||||||
SubAgentToolCallEvent,
|
SubAgentToolCallEvent,
|
||||||
SubAgentToolResultEvent,
|
SubAgentToolResultEvent,
|
||||||
SubAgentFinishEvent,
|
SubAgentFinishEvent,
|
||||||
SubAgentEventType,
|
|
||||||
SubAgentErrorEvent,
|
SubAgentErrorEvent,
|
||||||
SubAgentApprovalRequestEvent,
|
SubAgentApprovalRequestEvent,
|
||||||
} from '../subagents/subagent-events.js';
|
} from '../subagents/subagent-events.js';
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import type { FunctionDeclaration, PartListUnion } from '@google/genai';
|
|||||||
import { ToolErrorType } from './tool-error.js';
|
import { ToolErrorType } from './tool-error.js';
|
||||||
import type { DiffUpdateResult } from '../ide/ideContext.js';
|
import type { DiffUpdateResult } from '../ide/ideContext.js';
|
||||||
import { SchemaValidator } from '../utils/schemaValidator.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.
|
* Represents a validated and ready-to-execute tool call.
|
||||||
|
|||||||
@@ -4,14 +4,22 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, it, expect, vi, beforeEach, Mock, afterEach } from 'vitest';
|
import {
|
||||||
import { Content, GoogleGenAI, Models } from '@google/genai';
|
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 { DEFAULT_QWEN_MODEL } from '../config/models.js';
|
||||||
import { GeminiClient } from '../core/client.js';
|
import { GeminiClient } from '../core/client.js';
|
||||||
import { Config } from '../config/config.js';
|
import { Config } from '../config/config.js';
|
||||||
import {
|
import {
|
||||||
subagentGenerator,
|
subagentGenerator,
|
||||||
SubagentGeneratedContent,
|
type SubagentGeneratedContent,
|
||||||
} from './subagentGenerator.js';
|
} from './subagentGenerator.js';
|
||||||
|
|
||||||
// Mock GeminiClient and Config constructor
|
// Mock GeminiClient and Config constructor
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* 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 { DEFAULT_QWEN_MODEL } from '../config/models.js';
|
||||||
import { GeminiClient } from '../core/client.js';
|
import { GeminiClient } from '../core/client.js';
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user