UX for topic narration tool (#24079)

This commit is contained in:
Christian Gunderman
2026-03-28 21:33:38 +00:00
committed by GitHub
parent 3eebb75b7a
commit b7c86b5497
13 changed files with 271 additions and 44 deletions
@@ -48,6 +48,7 @@ interface HistoryItemDisplayProps {
isExpandable?: boolean; isExpandable?: boolean;
isFirstThinking?: boolean; isFirstThinking?: boolean;
isFirstAfterThinking?: boolean; isFirstAfterThinking?: boolean;
suppressNarration?: boolean;
} }
export const HistoryItemDisplay: React.FC<HistoryItemDisplayProps> = ({ export const HistoryItemDisplay: React.FC<HistoryItemDisplayProps> = ({
@@ -60,6 +61,7 @@ export const HistoryItemDisplay: React.FC<HistoryItemDisplayProps> = ({
isExpandable, isExpandable,
isFirstThinking = false, isFirstThinking = false,
isFirstAfterThinking = false, isFirstAfterThinking = false,
suppressNarration = false,
}) => { }) => {
const settings = useSettings(); const settings = useSettings();
const inlineThinkingMode = getInlineThinkingMode(settings); const inlineThinkingMode = getInlineThinkingMode(settings);
@@ -68,6 +70,17 @@ export const HistoryItemDisplay: React.FC<HistoryItemDisplayProps> = ({
const needsTopMarginAfterThinking = const needsTopMarginAfterThinking =
isFirstAfterThinking && inlineThinkingMode !== 'off'; isFirstAfterThinking && inlineThinkingMode !== 'off';
// If there's a topic update in this turn, we suppress the regular narration
// and thoughts as they are being "replaced" by the update_topic tool.
if (
suppressNarration &&
(itemForDisplay.type === 'thinking' ||
itemForDisplay.type === 'gemini' ||
itemForDisplay.type === 'gemini_content')
) {
return null;
}
return ( return (
<Box <Box
flexDirection="column" flexDirection="column"
+59 -8
View File
@@ -7,8 +7,10 @@
import { Box, Static } from 'ink'; import { Box, Static } from 'ink';
import { HistoryItemDisplay } from './HistoryItemDisplay.js'; import { HistoryItemDisplay } from './HistoryItemDisplay.js';
import { useUIState } from '../contexts/UIStateContext.js'; import { useUIState } from '../contexts/UIStateContext.js';
import { useSettings } from '../contexts/SettingsContext.js';
import { useAppContext } from '../contexts/AppContext.js'; import { useAppContext } from '../contexts/AppContext.js';
import { AppHeader } from './AppHeader.js'; import { AppHeader } from './AppHeader.js';
import { useAlternateBuffer } from '../hooks/useAlternateBuffer.js'; import { useAlternateBuffer } from '../hooks/useAlternateBuffer.js';
import { import {
SCROLL_TO_ITEM_END, SCROLL_TO_ITEM_END,
@@ -19,6 +21,7 @@ import { useMemo, memo, useCallback, useEffect, useRef } from 'react';
import { MAX_GEMINI_MESSAGE_LINES } from '../constants.js'; import { MAX_GEMINI_MESSAGE_LINES } from '../constants.js';
import { useConfirmingTool } from '../hooks/useConfirmingTool.js'; import { useConfirmingTool } from '../hooks/useConfirmingTool.js';
import { ToolConfirmationQueue } from './ToolConfirmationQueue.js'; import { ToolConfirmationQueue } from './ToolConfirmationQueue.js';
import { isTopicTool } from './messages/TopicMessage.js';
const MemoizedHistoryItemDisplay = memo(HistoryItemDisplay); const MemoizedHistoryItemDisplay = memo(HistoryItemDisplay);
const MemoizedAppHeader = memo(AppHeader); const MemoizedAppHeader = memo(AppHeader);
@@ -63,12 +66,39 @@ export const MainContent = () => {
return -1; return -1;
}, [uiState.history]); }, [uiState.history]);
const settings = useSettings();
const topicUpdateNarrationEnabled =
settings.merged.experimental?.topicUpdateNarration === true;
const suppressNarrationFlags = useMemo(() => {
const combinedHistory = [...uiState.history, ...pendingHistoryItems];
const flags = new Array<boolean>(combinedHistory.length).fill(false);
if (topicUpdateNarrationEnabled) {
let toolGroupInTurn = false;
for (let i = combinedHistory.length - 1; i >= 0; i--) {
const item = combinedHistory[i];
if (item.type === 'user' || item.type === 'user_shell') {
toolGroupInTurn = false;
} else if (item.type === 'tool_group') {
toolGroupInTurn = item.tools.some((t) => isTopicTool(t.name));
} else if (
(item.type === 'thinking' ||
item.type === 'gemini' ||
item.type === 'gemini_content') &&
toolGroupInTurn
) {
flags[i] = true;
}
}
}
return flags;
}, [uiState.history, pendingHistoryItems, topicUpdateNarrationEnabled]);
const augmentedHistory = useMemo( const augmentedHistory = useMemo(
() => () =>
uiState.history.map((item, index) => { uiState.history.map((item, i) => {
const isExpandable = index > lastUserPromptIndex; const prevType = i > 0 ? uiState.history[i - 1]?.type : undefined;
const prevType =
index > 0 ? uiState.history[index - 1]?.type : undefined;
const isFirstThinking = const isFirstThinking =
item.type === 'thinking' && prevType !== 'thinking'; item.type === 'thinking' && prevType !== 'thinking';
const isFirstAfterThinking = const isFirstAfterThinking =
@@ -76,18 +106,25 @@ export const MainContent = () => {
return { return {
item, item,
isExpandable, isExpandable: i > lastUserPromptIndex,
isFirstThinking, isFirstThinking,
isFirstAfterThinking, isFirstAfterThinking,
suppressNarration: suppressNarrationFlags[i] ?? false,
}; };
}), }),
[uiState.history, lastUserPromptIndex], [uiState.history, lastUserPromptIndex, suppressNarrationFlags],
); );
const historyItems = useMemo( const historyItems = useMemo(
() => () =>
augmentedHistory.map( augmentedHistory.map(
({ item, isExpandable, isFirstThinking, isFirstAfterThinking }) => ( ({
item,
isExpandable,
isFirstThinking,
isFirstAfterThinking,
suppressNarration,
}) => (
<MemoizedHistoryItemDisplay <MemoizedHistoryItemDisplay
terminalWidth={mainAreaWidth} terminalWidth={mainAreaWidth}
availableTerminalHeight={ availableTerminalHeight={
@@ -103,6 +140,7 @@ export const MainContent = () => {
isExpandable={isExpandable} isExpandable={isExpandable}
isFirstThinking={isFirstThinking} isFirstThinking={isFirstThinking}
isFirstAfterThinking={isFirstAfterThinking} isFirstAfterThinking={isFirstAfterThinking}
suppressNarration={suppressNarration}
/> />
), ),
), ),
@@ -138,6 +176,9 @@ export const MainContent = () => {
const isFirstAfterThinking = const isFirstAfterThinking =
item.type !== 'thinking' && prevType === 'thinking'; item.type !== 'thinking' && prevType === 'thinking';
const suppressNarration =
suppressNarrationFlags[uiState.history.length + i] ?? false;
return ( return (
<HistoryItemDisplay <HistoryItemDisplay
key={`pending-${i}`} key={`pending-${i}`}
@@ -150,6 +191,7 @@ export const MainContent = () => {
isExpandable={true} isExpandable={true}
isFirstThinking={isFirstThinking} isFirstThinking={isFirstThinking}
isFirstAfterThinking={isFirstAfterThinking} isFirstAfterThinking={isFirstAfterThinking}
suppressNarration={suppressNarration}
/> />
); );
})} })}
@@ -169,6 +211,7 @@ export const MainContent = () => {
showConfirmationQueue, showConfirmationQueue,
confirmingTool, confirmingTool,
uiState.history, uiState.history,
suppressNarrationFlags,
], ],
); );
@@ -176,12 +219,19 @@ export const MainContent = () => {
() => [ () => [
{ type: 'header' as const }, { type: 'header' as const },
...augmentedHistory.map( ...augmentedHistory.map(
({ item, isExpandable, isFirstThinking, isFirstAfterThinking }) => ({ ({
item,
isExpandable,
isFirstThinking,
isFirstAfterThinking,
suppressNarration,
}) => ({
type: 'history' as const, type: 'history' as const,
item, item,
isExpandable, isExpandable,
isFirstThinking, isFirstThinking,
isFirstAfterThinking, isFirstAfterThinking,
suppressNarration,
}), }),
), ),
{ type: 'pending' as const }, { type: 'pending' as const },
@@ -216,6 +266,7 @@ export const MainContent = () => {
isExpandable={item.isExpandable} isExpandable={item.isExpandable}
isFirstThinking={item.isFirstThinking} isFirstThinking={item.isFirstThinking}
isFirstAfterThinking={item.isFirstAfterThinking} isFirstAfterThinking={item.isFirstAfterThinking}
suppressNarration={item.suppressNarration}
/> />
); );
} else { } else {
@@ -7,13 +7,10 @@
import { renderWithProviders } from '../../../test-utils/render.js'; import { renderWithProviders } from '../../../test-utils/render.js';
import { describe, it, expect, vi, afterEach } from 'vitest'; import { describe, it, expect, vi, afterEach } from 'vitest';
import { ToolGroupMessage } from './ToolGroupMessage.js'; import { ToolGroupMessage } from './ToolGroupMessage.js';
import type {
HistoryItem,
HistoryItemWithoutId,
IndividualToolCallDisplay,
} from '../../types.js';
import { Scrollable } from '../shared/Scrollable.js';
import { import {
UPDATE_TOPIC_TOOL_NAME,
TOPIC_PARAM_TITLE,
TOPIC_PARAM_STRATEGIC_INTENT,
makeFakeConfig, makeFakeConfig,
CoreToolCallStatus, CoreToolCallStatus,
ApprovalMode, ApprovalMode,
@@ -23,6 +20,12 @@ import {
READ_FILE_DISPLAY_NAME, READ_FILE_DISPLAY_NAME,
GLOB_DISPLAY_NAME, GLOB_DISPLAY_NAME,
} from '@google/gemini-cli-core'; } from '@google/gemini-cli-core';
import type {
HistoryItem,
HistoryItemWithoutId,
IndividualToolCallDisplay,
} from '../../types.js';
import { Scrollable } from '../shared/Scrollable.js';
import os from 'node:os'; import os from 'node:os';
import { createMockSettings } from '../../../test-utils/settings.js'; import { createMockSettings } from '../../../test-utils/settings.js';
@@ -36,6 +39,7 @@ describe('<ToolGroupMessage />', () => {
): IndividualToolCallDisplay => ({ ): IndividualToolCallDisplay => ({
callId: 'tool-123', callId: 'tool-123',
name: 'test-tool', name: 'test-tool',
args: {},
description: 'A tool for testing', description: 'A tool for testing',
resultDisplay: 'Test result', resultDisplay: 'Test result',
status: CoreToolCallStatus.Success, status: CoreToolCallStatus.Success,
@@ -253,8 +257,71 @@ describe('<ToolGroupMessage />', () => {
unmount(); unmount();
}); });
it('renders mixed tool calls including shell command', async () => { it('renders update_topic tool call using TopicMessage', async () => {
const toolCalls = [ const toolCalls = [
createToolCall({
callId: 'topic-tool',
name: UPDATE_TOPIC_TOOL_NAME,
args: {
[TOPIC_PARAM_TITLE]: 'Testing Topic',
[TOPIC_PARAM_STRATEGIC_INTENT]: 'This is the description',
},
}),
];
const item = createItem(toolCalls);
const { lastFrame, unmount } = await renderWithProviders(
<ToolGroupMessage {...baseProps} item={item} toolCalls={toolCalls} />,
{
config: baseMockConfig,
settings: fullVerbositySettings,
},
);
const output = lastFrame();
expect(output).toContain('Testing Topic');
expect(output).toContain('— This is the description');
expect(output).toMatchSnapshot('update_topic_tool');
unmount();
});
it('renders update_topic tool call with summary instead of strategic_intent', async () => {
const toolCalls = [
createToolCall({
callId: 'topic-tool-summary',
name: UPDATE_TOPIC_TOOL_NAME,
args: {
[TOPIC_PARAM_TITLE]: 'Testing Topic',
summary: 'This is the summary',
},
}),
];
const item = createItem(toolCalls);
const { lastFrame, unmount } = await renderWithProviders(
<ToolGroupMessage {...baseProps} item={item} toolCalls={toolCalls} />,
{
config: baseMockConfig,
settings: fullVerbositySettings,
},
);
const output = lastFrame();
expect(output).toContain('Testing Topic');
expect(output).toContain('— This is the summary');
unmount();
});
it('renders mixed tool calls including update_topic', async () => {
const toolCalls = [
createToolCall({
callId: 'topic-tool-mixed',
name: UPDATE_TOPIC_TOOL_NAME,
args: {
[TOPIC_PARAM_TITLE]: 'Testing Topic',
[TOPIC_PARAM_STRATEGIC_INTENT]: 'This is the description',
},
}),
createToolCall({ createToolCall({
callId: 'tool-1', callId: 'tool-1',
name: 'read_file', name: 'read_file',
@@ -15,6 +15,7 @@ import type {
import { ToolCallStatus, mapCoreStatusToDisplayStatus } from '../../types.js'; import { ToolCallStatus, mapCoreStatusToDisplayStatus } from '../../types.js';
import { ToolMessage } from './ToolMessage.js'; import { ToolMessage } from './ToolMessage.js';
import { ShellToolMessage } from './ShellToolMessage.js'; import { ShellToolMessage } from './ShellToolMessage.js';
import { TopicMessage, isTopicTool } from './TopicMessage.js';
import { SubagentGroupDisplay } from './SubagentGroupDisplay.js'; import { SubagentGroupDisplay } from './SubagentGroupDisplay.js';
import { theme } from '../../semantic-colors.js'; import { theme } from '../../semantic-colors.js';
import { useConfig } from '../../contexts/ConfigContext.js'; import { useConfig } from '../../contexts/ConfigContext.js';
@@ -192,7 +193,20 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
paddingRight={TOOL_MESSAGE_HORIZONTAL_MARGIN} paddingRight={TOOL_MESSAGE_HORIZONTAL_MARGIN}
> >
{groupedTools.map((group, index) => { {groupedTools.map((group, index) => {
const isFirst = index === 0; let isFirst = index === 0;
if (!isFirst) {
// Check if all previous tools were topics
let allPreviousWereTopics = true;
for (let i = 0; i < index; i++) {
const prevGroup = groupedTools[i];
if (Array.isArray(prevGroup) || !isTopicTool(prevGroup.name)) {
allPreviousWereTopics = false;
break;
}
}
isFirst = allPreviousWereTopics;
}
const resolvedIsFirst = const resolvedIsFirst =
borderTopOverride !== undefined borderTopOverride !== undefined
? borderTopOverride && isFirst ? borderTopOverride && isFirst
@@ -215,6 +229,7 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
const tool = group; const tool = group;
const isShellToolCall = isShellTool(tool.name); const isShellToolCall = isShellTool(tool.name);
const isTopicToolCall = isTopicTool(tool.name);
const commonProps = { const commonProps = {
...tool, ...tool,
@@ -234,7 +249,9 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
minHeight={1} minHeight={1}
width={contentWidth} width={contentWidth}
> >
{isShellToolCall ? ( {isTopicToolCall ? (
<TopicMessage {...commonProps} />
) : isShellToolCall ? (
<ShellToolMessage {...commonProps} config={config} /> <ShellToolMessage {...commonProps} config={config} />
) : ( ) : (
<ToolMessage {...commonProps} /> <ToolMessage {...commonProps} />
@@ -262,26 +279,26 @@ export const ToolGroupMessage: React.FC<ToolGroupMessageProps> = ({
</Box> </Box>
); );
})} })}
{ {/*
/* We have to keep the bottom border separate so it doesn't get
We have to keep the bottom border separate so it doesn't get drawn over by the sticky header directly inside it.
drawn over by the sticky header directly inside it. */}
*/ {(visibleToolCalls.length > 0 || borderBottomOverride !== undefined) &&
(visibleToolCalls.length > 0 || borderBottomOverride !== undefined) && borderBottomOverride !== false &&
borderBottomOverride !== false && ( (visibleToolCalls.length === 0 ||
<Box !visibleToolCalls.every((tool) => isTopicTool(tool.name))) && (
height={0} <Box
width={contentWidth} height={0}
borderLeft={true} width={contentWidth}
borderRight={true} borderLeft={true}
borderTop={false} borderRight={true}
borderBottom={borderBottomOverride ?? true} borderTop={false}
borderColor={borderColor} borderBottom={borderBottomOverride ?? true}
borderDimColor={borderDimColor} borderColor={borderColor}
borderStyle="round" borderDimColor={borderDimColor}
/> borderStyle="round"
) />
} )}
</Box> </Box>
); );
@@ -0,0 +1,41 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import type React from 'react';
import { Box, Text } from 'ink';
import {
UPDATE_TOPIC_TOOL_NAME,
UPDATE_TOPIC_DISPLAY_NAME,
TOPIC_PARAM_TITLE,
TOPIC_PARAM_SUMMARY,
TOPIC_PARAM_STRATEGIC_INTENT,
} from '@google/gemini-cli-core';
import type { IndividualToolCallDisplay } from '../../types.js';
import { theme } from '../../semantic-colors.js';
interface TopicMessageProps extends IndividualToolCallDisplay {
terminalWidth: number;
}
export const isTopicTool = (name: string): boolean =>
name === UPDATE_TOPIC_TOOL_NAME || name === UPDATE_TOPIC_DISPLAY_NAME;
export const TopicMessage: React.FC<TopicMessageProps> = ({ args }) => {
const rawTitle = args?.[TOPIC_PARAM_TITLE];
const title = typeof rawTitle === 'string' ? rawTitle : undefined;
const rawIntent =
args?.[TOPIC_PARAM_STRATEGIC_INTENT] || args?.[TOPIC_PARAM_SUMMARY];
const intent = typeof rawIntent === 'string' ? rawIntent : undefined;
return (
<Box flexDirection="row" marginLeft={2}>
<Text color={theme.text.primary} bold>
{title || 'Topic'}
</Text>
{intent && <Text color={theme.text.secondary}> {intent}</Text>}
</Box>
);
};
@@ -74,8 +74,9 @@ exports[`<ToolGroupMessage /> > Golden Snapshots > renders header when scrolled
" "
`; `;
exports[`<ToolGroupMessage /> > Golden Snapshots > renders mixed tool calls including shell command 1`] = ` exports[`<ToolGroupMessage /> > Golden Snapshots > renders mixed tool calls including update_topic 1`] = `
"╭──────────────────────────────────────────────────────────────────────────╮ " Testing Topic — This is the description
╭──────────────────────────────────────────────────────────────────────────╮
│ ✓ read_file Read a file │ │ ✓ read_file Read a file │
│ │ │ │
│ Test result │ │ Test result │
@@ -137,6 +138,11 @@ exports[`<ToolGroupMessage /> > Golden Snapshots > renders two tool groups where
" "
`; `;
exports[`<ToolGroupMessage /> > Golden Snapshots > renders update_topic tool call using TopicMessage > update_topic_tool 1`] = `
" Testing Topic — This is the description
"
`;
exports[`<ToolGroupMessage /> > Golden Snapshots > renders with limited terminal height 1`] = ` exports[`<ToolGroupMessage /> > Golden Snapshots > renders with limited terminal height 1`] = `
"╭──────────────────────────────────────────────────────────────────────────╮ "╭──────────────────────────────────────────────────────────────────────────╮
│ ✓ tool-with-result Tool with output │ │ ✓ tool-with-result Tool with output │
+1
View File
@@ -50,6 +50,7 @@ export function mapToDisplay(
callId: call.request.callId, callId: call.request.callId,
parentCallId: call.request.parentCallId, parentCallId: call.request.parentCallId,
name: displayName, name: displayName,
args: call.request.args,
description, description,
renderOutputAsMarkdown, renderOutputAsMarkdown,
}; };
+29 -4
View File
@@ -40,6 +40,8 @@ import {
Kind, Kind,
ACTIVATE_SKILL_TOOL_NAME, ACTIVATE_SKILL_TOOL_NAME,
shouldHideToolCall, shouldHideToolCall,
UPDATE_TOPIC_TOOL_NAME,
UPDATE_TOPIC_DISPLAY_NAME,
} from '@google/gemini-cli-core'; } from '@google/gemini-cli-core';
import type { import type {
Config, Config,
@@ -108,6 +110,9 @@ interface BackgroundedToolInfo {
initialOutput: string; initialOutput: string;
} }
const isTopicTool = (name: string): boolean =>
name === UPDATE_TOPIC_TOOL_NAME || name === UPDATE_TOPIC_DISPLAY_NAME;
enum StreamProcessingStatus { enum StreamProcessingStatus {
Completed, Completed,
UserCancelled, UserCancelled,
@@ -489,7 +494,17 @@ export const useGeminiStream = (
addItem(historyItem); addItem(historyItem);
setPushedToolCallIds(newPushed); setPushedToolCallIds(newPushed);
setIsFirstToolInGroup(false);
// If this batch ONLY contains topics, and we were the first in the group,
// the NEXT batch is still effectively the first VISIBLE bordered tool in the group.
if (
isFirstToolInGroupRef.current &&
toolsToPush.every((tc) => isTopicTool(tc.request.name))
) {
// Keep it true!
} else {
setIsFirstToolInGroup(false);
}
} }
}, [ }, [
toolCalls, toolCalls,
@@ -502,7 +517,6 @@ export const useGeminiStream = (
isShellFocused, isShellFocused,
backgroundTasks, backgroundTasks,
]); ]);
const pendingToolGroupItems = useMemo((): HistoryItemWithoutId[] => { const pendingToolGroupItems = useMemo((): HistoryItemWithoutId[] => {
const remainingTools = toolCalls.filter( const remainingTools = toolCalls.filter(
(tc) => !pushedToolCallIds.has(tc.request.callId), (tc) => !pushedToolCallIds.has(tc.request.callId),
@@ -519,15 +533,26 @@ export const useGeminiStream = (
); );
if (remainingTools.length > 0) { if (remainingTools.length > 0) {
// Should we draw a top border? Yes if NO previous tools were drawn,
// OR if ALL previously drawn tools were topics (which don't draw top borders).
let needsTopBorder = pushedToolCallIds.size === 0;
if (!needsTopBorder) {
const allPushedWereTopics = toolCalls
.filter((tc) => pushedToolCallIds.has(tc.request.callId))
.every((tc) => isTopicTool(tc.request.name));
if (allPushedWereTopics) {
needsTopBorder = true;
}
}
items.push( items.push(
mapTrackedToolCallsToDisplay(remainingTools, { mapTrackedToolCallsToDisplay(remainingTools, {
borderTop: pushedToolCallIds.size === 0, borderTop: needsTopBorder,
borderBottom: false, // Stay open to connect with the slice below borderBottom: false, // Stay open to connect with the slice below
...appearance, ...appearance,
}), }),
); );
} }
// Always show a bottom border slice if we have ANY tools in the batch // Always show a bottom border slice if we have ANY tools in the batch
// and we haven't finished pushing the whole batch to history yet. // and we haven't finished pushing the whole batch to history yet.
// Once all tools are terminal and pushed, the last history item handles the closing border. // Once all tools are terminal and pushed, the last history item handles the closing border.
+1
View File
@@ -118,6 +118,7 @@ export interface IndividualToolCallDisplay {
callId: string; callId: string;
parentCallId?: string; parentCallId?: string;
name: string; name: string;
args?: Record<string, unknown>;
description: string; description: string;
resultDisplay: ToolResultDisplay | undefined; resultDisplay: ToolResultDisplay | undefined;
status: CoreToolCallStatus; status: CoreToolCallStatus;
@@ -128,6 +128,7 @@ export const PARAM_ADDITIONAL_PERMISSIONS = 'additional_permissions';
// -- update_topic -- // -- update_topic --
export const UPDATE_TOPIC_TOOL_NAME = 'update_topic'; export const UPDATE_TOPIC_TOOL_NAME = 'update_topic';
export const UPDATE_TOPIC_DISPLAY_NAME = 'Update Topic Context';
export const TOPIC_PARAM_TITLE = 'title'; export const TOPIC_PARAM_TITLE = 'title';
export const TOPIC_PARAM_SUMMARY = 'summary'; export const TOPIC_PARAM_SUMMARY = 'summary';
export const TOPIC_PARAM_STRATEGIC_INTENT = 'strategic_intent'; export const TOPIC_PARAM_STRATEGIC_INTENT = 'strategic_intent';
@@ -40,6 +40,7 @@ export {
EXIT_PLAN_MODE_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME,
ENTER_PLAN_MODE_TOOL_NAME, ENTER_PLAN_MODE_TOOL_NAME,
UPDATE_TOPIC_TOOL_NAME, UPDATE_TOPIC_TOOL_NAME,
UPDATE_TOPIC_DISPLAY_NAME,
// Shared parameter names // Shared parameter names
PARAM_FILE_PATH, PARAM_FILE_PATH,
PARAM_DIR_PATH, PARAM_DIR_PATH,
+2
View File
@@ -76,6 +76,7 @@ import {
EXIT_PLAN_PARAM_PLAN_FILENAME, EXIT_PLAN_PARAM_PLAN_FILENAME,
SKILL_PARAM_NAME, SKILL_PARAM_NAME,
UPDATE_TOPIC_TOOL_NAME, UPDATE_TOPIC_TOOL_NAME,
UPDATE_TOPIC_DISPLAY_NAME,
TOPIC_PARAM_TITLE, TOPIC_PARAM_TITLE,
TOPIC_PARAM_SUMMARY, TOPIC_PARAM_SUMMARY,
TOPIC_PARAM_STRATEGIC_INTENT, TOPIC_PARAM_STRATEGIC_INTENT,
@@ -100,6 +101,7 @@ export {
EXIT_PLAN_MODE_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME,
ENTER_PLAN_MODE_TOOL_NAME, ENTER_PLAN_MODE_TOOL_NAME,
UPDATE_TOPIC_TOOL_NAME, UPDATE_TOPIC_TOOL_NAME,
UPDATE_TOPIC_DISPLAY_NAME,
// Shared parameter names // Shared parameter names
PARAM_FILE_PATH, PARAM_FILE_PATH,
PARAM_DIR_PATH, PARAM_DIR_PATH,
+2 -1
View File
@@ -6,6 +6,7 @@
import { import {
UPDATE_TOPIC_TOOL_NAME, UPDATE_TOPIC_TOOL_NAME,
UPDATE_TOPIC_DISPLAY_NAME,
TOPIC_PARAM_TITLE, TOPIC_PARAM_TITLE,
TOPIC_PARAM_SUMMARY, TOPIC_PARAM_SUMMARY,
TOPIC_PARAM_STRATEGIC_INTENT, TOPIC_PARAM_STRATEGIC_INTENT,
@@ -110,7 +111,7 @@ export class UpdateTopicTool extends BaseDeclarativeTool<
const declaration = getUpdateTopicDeclaration(); const declaration = getUpdateTopicDeclaration();
super( super(
UPDATE_TOPIC_TOOL_NAME, UPDATE_TOPIC_TOOL_NAME,
'Update Topic Context', UPDATE_TOPIC_DISPLAY_NAME,
declaration.description ?? '', declaration.description ?? '',
Kind.Think, Kind.Think,
declaration.parametersJsonSchema, declaration.parametersJsonSchema,