mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-22 02:54:31 -07:00
feat(core): implement tool-based topic grouping (Chapters) (#23150)
Co-authored-by: Christian Gunderman <gundermanc@google.com>
This commit is contained in:
@@ -125,3 +125,9 @@ export const PLAN_MODE_PARAM_REASON = 'reason';
|
||||
|
||||
// -- sandbox --
|
||||
export const PARAM_ADDITIONAL_PERMISSIONS = 'additional_permissions';
|
||||
|
||||
// -- update_topic --
|
||||
export const UPDATE_TOPIC_TOOL_NAME = 'update_topic';
|
||||
export const TOPIC_PARAM_TITLE = 'title';
|
||||
export const TOPIC_PARAM_SUMMARY = 'summary';
|
||||
export const TOPIC_PARAM_STRATEGIC_INTENT = 'strategic_intent';
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
getShellDeclaration,
|
||||
getExitPlanModeDeclaration,
|
||||
getActivateSkillDeclaration,
|
||||
getUpdateTopicDeclaration,
|
||||
} from './dynamic-declaration-helpers.js';
|
||||
|
||||
// Re-export names for compatibility
|
||||
@@ -38,6 +39,7 @@ export {
|
||||
ASK_USER_TOOL_NAME,
|
||||
EXIT_PLAN_MODE_TOOL_NAME,
|
||||
ENTER_PLAN_MODE_TOOL_NAME,
|
||||
UPDATE_TOPIC_TOOL_NAME,
|
||||
// Shared parameter names
|
||||
PARAM_FILE_PATH,
|
||||
PARAM_DIR_PATH,
|
||||
@@ -91,6 +93,9 @@ export {
|
||||
PLAN_MODE_PARAM_REASON,
|
||||
EXIT_PLAN_PARAM_PLAN_FILENAME,
|
||||
SKILL_PARAM_NAME,
|
||||
TOPIC_PARAM_TITLE,
|
||||
TOPIC_PARAM_SUMMARY,
|
||||
TOPIC_PARAM_STRATEGIC_INTENT,
|
||||
} from './base-declarations.js';
|
||||
|
||||
// Re-export sets for compatibility
|
||||
@@ -221,6 +226,13 @@ export const ENTER_PLAN_MODE_DEFINITION: ToolDefinition = {
|
||||
overrides: (modelId) => getToolSet(modelId).enter_plan_mode,
|
||||
};
|
||||
|
||||
export const UPDATE_TOPIC_DEFINITION: ToolDefinition = {
|
||||
get base() {
|
||||
return getUpdateTopicDeclaration();
|
||||
},
|
||||
overrides: (modelId) => getToolSet(modelId).update_topic,
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// DYNAMIC TOOL DEFINITIONS (LEGACY EXPORTS)
|
||||
// ============================================================================
|
||||
|
||||
@@ -24,6 +24,10 @@ import {
|
||||
EXIT_PLAN_PARAM_PLAN_FILENAME,
|
||||
SKILL_PARAM_NAME,
|
||||
PARAM_ADDITIONAL_PERMISSIONS,
|
||||
UPDATE_TOPIC_TOOL_NAME,
|
||||
TOPIC_PARAM_TITLE,
|
||||
TOPIC_PARAM_SUMMARY,
|
||||
TOPIC_PARAM_STRATEGIC_INTENT,
|
||||
} from './base-declarations.js';
|
||||
|
||||
/**
|
||||
@@ -204,3 +208,34 @@ export function getActivateSkillDeclaration(
|
||||
parametersJsonSchema: zodToJsonSchema(schema),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the FunctionDeclaration for updating the topic context.
|
||||
*/
|
||||
export function getUpdateTopicDeclaration(): FunctionDeclaration {
|
||||
return {
|
||||
name: UPDATE_TOPIC_TOOL_NAME,
|
||||
description:
|
||||
'Manages your narrative flow. Include `title` and `summary` only when starting a new Chapter (logical phase) or shifting strategic intent.',
|
||||
parametersJsonSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
[TOPIC_PARAM_TITLE]: {
|
||||
type: 'string',
|
||||
description: 'The title of the new topic or chapter.',
|
||||
},
|
||||
[TOPIC_PARAM_SUMMARY]: {
|
||||
type: 'string',
|
||||
description:
|
||||
'(OPTIONAL) A detailed summary (5-10 sentences) covering both the work completed in the previous topic and the strategic intent of the new topic. This is required when transitioning between topics to maintain continuity.',
|
||||
},
|
||||
[TOPIC_PARAM_STRATEGIC_INTENT]: {
|
||||
type: 'string',
|
||||
description:
|
||||
'A mandatory one-sentence statement of your immediate intent.',
|
||||
},
|
||||
},
|
||||
required: [TOPIC_PARAM_STRATEGIC_INTENT],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ import {
|
||||
getShellDeclaration,
|
||||
getExitPlanModeDeclaration,
|
||||
getActivateSkillDeclaration,
|
||||
getUpdateTopicDeclaration,
|
||||
} from '../dynamic-declaration-helpers.js';
|
||||
import {
|
||||
DEFAULT_MAX_LINES_TEXT_FILE,
|
||||
@@ -724,4 +725,5 @@ The agent did not use the todo list because this task could be completed by a ti
|
||||
|
||||
exit_plan_mode: () => getExitPlanModeDeclaration(),
|
||||
activate_skill: (skillNames) => getActivateSkillDeclaration(skillNames),
|
||||
update_topic: getUpdateTopicDeclaration(),
|
||||
};
|
||||
|
||||
@@ -50,4 +50,5 @@ export interface CoreToolSet {
|
||||
enter_plan_mode: FunctionDeclaration;
|
||||
exit_plan_mode: () => FunctionDeclaration;
|
||||
activate_skill: (skillNames: string[]) => FunctionDeclaration;
|
||||
update_topic?: FunctionDeclaration;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,10 @@ import {
|
||||
PLAN_MODE_PARAM_REASON,
|
||||
EXIT_PLAN_PARAM_PLAN_FILENAME,
|
||||
SKILL_PARAM_NAME,
|
||||
UPDATE_TOPIC_TOOL_NAME,
|
||||
TOPIC_PARAM_TITLE,
|
||||
TOPIC_PARAM_SUMMARY,
|
||||
TOPIC_PARAM_STRATEGIC_INTENT,
|
||||
} from './definitions/coreTools.js';
|
||||
|
||||
export {
|
||||
@@ -95,6 +99,7 @@ export {
|
||||
ASK_USER_TOOL_NAME,
|
||||
EXIT_PLAN_MODE_TOOL_NAME,
|
||||
ENTER_PLAN_MODE_TOOL_NAME,
|
||||
UPDATE_TOPIC_TOOL_NAME,
|
||||
// Shared parameter names
|
||||
PARAM_FILE_PATH,
|
||||
PARAM_DIR_PATH,
|
||||
@@ -148,6 +153,9 @@ export {
|
||||
PLAN_MODE_PARAM_REASON,
|
||||
EXIT_PLAN_PARAM_PLAN_FILENAME,
|
||||
SKILL_PARAM_NAME,
|
||||
TOPIC_PARAM_TITLE,
|
||||
TOPIC_PARAM_SUMMARY,
|
||||
TOPIC_PARAM_STRATEGIC_INTENT,
|
||||
};
|
||||
|
||||
export const EDIT_TOOL_NAMES = new Set([EDIT_TOOL_NAME, WRITE_FILE_TOOL_NAME]);
|
||||
@@ -253,6 +261,7 @@ export const ALL_BUILTIN_TOOL_NAMES = [
|
||||
GET_INTERNAL_DOCS_TOOL_NAME,
|
||||
ENTER_PLAN_MODE_TOOL_NAME,
|
||||
EXIT_PLAN_MODE_TOOL_NAME,
|
||||
UPDATE_TOPIC_TOOL_NAME,
|
||||
] as const;
|
||||
|
||||
/**
|
||||
@@ -269,6 +278,7 @@ export const PLAN_MODE_TOOLS = [
|
||||
ASK_USER_TOOL_NAME,
|
||||
ACTIVATE_SKILL_TOOL_NAME,
|
||||
GET_INTERNAL_DOCS_TOOL_NAME,
|
||||
UPDATE_TOPIC_TOOL_NAME,
|
||||
'codebase_investigator',
|
||||
'cli_help',
|
||||
] as const;
|
||||
|
||||
@@ -19,7 +19,10 @@ import { Config, type ConfigParameters } from '../config/config.js';
|
||||
import { ApprovalMode } from '../policy/types.js';
|
||||
|
||||
import { ToolRegistry, DiscoveredTool } from './tool-registry.js';
|
||||
import { DISCOVERED_TOOL_PREFIX } from './tool-names.js';
|
||||
import {
|
||||
DISCOVERED_TOOL_PREFIX,
|
||||
UPDATE_TOPIC_TOOL_NAME,
|
||||
} from './tool-names.js';
|
||||
import { DiscoveredMCPTool } from './mcp-tool.js';
|
||||
import {
|
||||
mcpToTool,
|
||||
@@ -800,6 +803,36 @@ describe('ToolRegistry', () => {
|
||||
const toolNames = allTools.map((t) => t.name);
|
||||
expect(toolNames).not.toContain('mcp_test-server_write-mcp-tool');
|
||||
});
|
||||
|
||||
it('should exclude topic tool when narration is disabled in config', () => {
|
||||
const topicTool = new MockTool({
|
||||
name: UPDATE_TOPIC_TOOL_NAME,
|
||||
displayName: 'Topic Tool',
|
||||
});
|
||||
toolRegistry.registerTool(topicTool);
|
||||
|
||||
vi.spyOn(config, 'isTopicUpdateNarrationEnabled').mockReturnValue(false);
|
||||
mockConfigGetExcludedTools.mockReturnValue(new Set());
|
||||
|
||||
expect(toolRegistry.getAllToolNames()).not.toContain(
|
||||
UPDATE_TOPIC_TOOL_NAME,
|
||||
);
|
||||
expect(toolRegistry.getTool(UPDATE_TOPIC_TOOL_NAME)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should NOT exclude topic tool when narration is enabled in config', () => {
|
||||
const topicTool = new MockTool({
|
||||
name: UPDATE_TOPIC_TOOL_NAME,
|
||||
displayName: 'Topic Tool',
|
||||
});
|
||||
toolRegistry.registerTool(topicTool);
|
||||
|
||||
vi.spyOn(config, 'isTopicUpdateNarrationEnabled').mockReturnValue(true);
|
||||
mockConfigGetExcludedTools.mockReturnValue(new Set());
|
||||
|
||||
expect(toolRegistry.getAllToolNames()).toContain(UPDATE_TOPIC_TOOL_NAME);
|
||||
expect(toolRegistry.getTool(UPDATE_TOPIC_TOOL_NAME)).toBe(topicTool);
|
||||
});
|
||||
});
|
||||
|
||||
describe('DiscoveredToolInvocation', () => {
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
getToolAliases,
|
||||
WRITE_FILE_TOOL_NAME,
|
||||
EDIT_TOOL_NAME,
|
||||
UPDATE_TOPIC_TOOL_NAME,
|
||||
} from './tool-names.js';
|
||||
|
||||
type ToolParams = Record<string, unknown>;
|
||||
@@ -576,6 +577,12 @@ export class ToolRegistry {
|
||||
),
|
||||
) ?? new Set([]);
|
||||
|
||||
if (tool.name === UPDATE_TOPIC_TOOL_NAME) {
|
||||
if (!this.config.isTopicUpdateNarrationEnabled()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const normalizedClassName = tool.constructor.name.replace(/^_+/, '');
|
||||
const possibleNames = [tool.name, normalizedClassName];
|
||||
if (tool instanceof DiscoveredMCPTool) {
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { TopicState, UpdateTopicTool } from './topicTool.js';
|
||||
import { MessageBus } from '../confirmation-bus/message-bus.js';
|
||||
import type { PolicyEngine } from '../policy/policy-engine.js';
|
||||
import {
|
||||
UPDATE_TOPIC_TOOL_NAME,
|
||||
TOPIC_PARAM_TITLE,
|
||||
TOPIC_PARAM_SUMMARY,
|
||||
TOPIC_PARAM_STRATEGIC_INTENT,
|
||||
} from './definitions/base-declarations.js';
|
||||
import type { Config } from '../config/config.js';
|
||||
|
||||
describe('TopicState', () => {
|
||||
let state: TopicState;
|
||||
|
||||
beforeEach(() => {
|
||||
state = new TopicState();
|
||||
});
|
||||
|
||||
it('should store and retrieve topic title and intent', () => {
|
||||
expect(state.getTopic()).toBeUndefined();
|
||||
expect(state.getIntent()).toBeUndefined();
|
||||
const success = state.setTopic('Test Topic', 'Test Intent');
|
||||
expect(success).toBe(true);
|
||||
expect(state.getTopic()).toBe('Test Topic');
|
||||
expect(state.getIntent()).toBe('Test Intent');
|
||||
});
|
||||
|
||||
it('should sanitize newlines and carriage returns', () => {
|
||||
state.setTopic('Topic\nWith\r\nLines', 'Intent\nWith\r\nLines');
|
||||
expect(state.getTopic()).toBe('Topic With Lines');
|
||||
expect(state.getIntent()).toBe('Intent With Lines');
|
||||
});
|
||||
|
||||
it('should trim whitespace', () => {
|
||||
state.setTopic(' Spaced Topic ', ' Spaced Intent ');
|
||||
expect(state.getTopic()).toBe('Spaced Topic');
|
||||
expect(state.getIntent()).toBe('Spaced Intent');
|
||||
});
|
||||
|
||||
it('should reject empty or whitespace-only inputs', () => {
|
||||
expect(state.setTopic('', '')).toBe(false);
|
||||
});
|
||||
|
||||
it('should reset topic and intent', () => {
|
||||
state.setTopic('Test Topic', 'Test Intent');
|
||||
state.reset();
|
||||
expect(state.getTopic()).toBeUndefined();
|
||||
expect(state.getIntent()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('UpdateTopicTool', () => {
|
||||
let tool: UpdateTopicTool;
|
||||
let mockMessageBus: MessageBus;
|
||||
let mockConfig: Config;
|
||||
|
||||
beforeEach(() => {
|
||||
mockMessageBus = new MessageBus(vi.mocked({} as PolicyEngine));
|
||||
// Mock enough of Config to satisfy the tool
|
||||
mockConfig = {
|
||||
topicState: new TopicState(),
|
||||
} as unknown as Config;
|
||||
tool = new UpdateTopicTool(mockConfig, mockMessageBus);
|
||||
});
|
||||
|
||||
it('should have correct name and display name', () => {
|
||||
expect(tool.name).toBe(UPDATE_TOPIC_TOOL_NAME);
|
||||
expect(tool.displayName).toBe('Update Topic Context');
|
||||
});
|
||||
|
||||
it('should update TopicState and include strategic intent on execute', async () => {
|
||||
const invocation = tool.build({
|
||||
[TOPIC_PARAM_TITLE]: 'New Chapter',
|
||||
[TOPIC_PARAM_SUMMARY]: 'The goal is to implement X. Previously we did Y.',
|
||||
[TOPIC_PARAM_STRATEGIC_INTENT]: 'Initial Move',
|
||||
});
|
||||
const result = await invocation.execute(new AbortController().signal);
|
||||
|
||||
expect(result.llmContent).toContain('Current topic: "New Chapter"');
|
||||
expect(result.llmContent).toContain(
|
||||
'Topic summary: The goal is to implement X. Previously we did Y.',
|
||||
);
|
||||
expect(result.llmContent).toContain('Strategic Intent: Initial Move');
|
||||
expect(mockConfig.topicState.getTopic()).toBe('New Chapter');
|
||||
expect(mockConfig.topicState.getIntent()).toBe('Initial Move');
|
||||
expect(result.returnDisplay).toContain('## 📂 Topic: **New Chapter**');
|
||||
expect(result.returnDisplay).toContain('**Summary:**');
|
||||
expect(result.returnDisplay).toContain(
|
||||
'> [!STRATEGY]\n> **Intent:** Initial Move',
|
||||
);
|
||||
});
|
||||
|
||||
it('should render only intent for tactical updates (same topic)', async () => {
|
||||
mockConfig.topicState.setTopic('New Chapter');
|
||||
|
||||
const invocation = tool.build({
|
||||
[TOPIC_PARAM_TITLE]: 'New Chapter',
|
||||
[TOPIC_PARAM_STRATEGIC_INTENT]: 'Subsequent Move',
|
||||
});
|
||||
const result = await invocation.execute(new AbortController().signal);
|
||||
|
||||
expect(result.returnDisplay).not.toContain('## 📂 Topic:');
|
||||
expect(result.returnDisplay).toBe(
|
||||
'> [!STRATEGY]\n> **Intent:** Subsequent Move',
|
||||
);
|
||||
expect(result.llmContent).toBe('Strategic Intent: Subsequent Move');
|
||||
});
|
||||
|
||||
it('should return error if strategic_intent is missing', async () => {
|
||||
try {
|
||||
tool.build({
|
||||
[TOPIC_PARAM_TITLE]: 'Title',
|
||||
});
|
||||
expect.fail('Should have thrown validation error');
|
||||
} catch (e: unknown) {
|
||||
if (e instanceof Error) {
|
||||
expect(e.message).toContain(
|
||||
"must have required property 'strategic_intent'",
|
||||
);
|
||||
} else {
|
||||
expect.fail('Expected Error instance');
|
||||
}
|
||||
}
|
||||
expect(mockConfig.topicState.getTopic()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {
|
||||
UPDATE_TOPIC_TOOL_NAME,
|
||||
TOPIC_PARAM_TITLE,
|
||||
TOPIC_PARAM_SUMMARY,
|
||||
TOPIC_PARAM_STRATEGIC_INTENT,
|
||||
} from './definitions/coreTools.js';
|
||||
import {
|
||||
BaseDeclarativeTool,
|
||||
BaseToolInvocation,
|
||||
Kind,
|
||||
type ToolResult,
|
||||
} from './tools.js';
|
||||
import type { MessageBus } from '../confirmation-bus/message-bus.js';
|
||||
import { debugLogger } from '../utils/debugLogger.js';
|
||||
import { getUpdateTopicDeclaration } from './definitions/dynamic-declaration-helpers.js';
|
||||
import type { Config } from '../config/config.js';
|
||||
|
||||
/**
|
||||
* Manages the current active topic title and tactical intent for a session.
|
||||
* Hosted within the Config instance for session-scoping.
|
||||
*/
|
||||
export class TopicState {
|
||||
private activeTopicTitle?: string;
|
||||
private activeIntent?: string;
|
||||
|
||||
/**
|
||||
* Sanitizes and sets the topic title and/or intent.
|
||||
* @returns true if the input was valid and set, false otherwise.
|
||||
*/
|
||||
setTopic(title?: string, intent?: string): boolean {
|
||||
const sanitizedTitle = title?.trim().replace(/[\r\n]+/g, ' ');
|
||||
const sanitizedIntent = intent?.trim().replace(/[\r\n]+/g, ' ');
|
||||
|
||||
if (!sanitizedTitle && !sanitizedIntent) return false;
|
||||
|
||||
if (sanitizedTitle) {
|
||||
this.activeTopicTitle = sanitizedTitle;
|
||||
}
|
||||
|
||||
if (sanitizedIntent) {
|
||||
this.activeIntent = sanitizedIntent;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getTopic(): string | undefined {
|
||||
return this.activeTopicTitle;
|
||||
}
|
||||
|
||||
getIntent(): string | undefined {
|
||||
return this.activeIntent;
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this.activeTopicTitle = undefined;
|
||||
this.activeIntent = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
interface UpdateTopicParams {
|
||||
[TOPIC_PARAM_TITLE]?: string;
|
||||
[TOPIC_PARAM_SUMMARY]?: string;
|
||||
[TOPIC_PARAM_STRATEGIC_INTENT]?: string;
|
||||
}
|
||||
|
||||
class UpdateTopicInvocation extends BaseToolInvocation<
|
||||
UpdateTopicParams,
|
||||
ToolResult
|
||||
> {
|
||||
constructor(
|
||||
params: UpdateTopicParams,
|
||||
messageBus: MessageBus,
|
||||
toolName: string,
|
||||
private readonly config: Config,
|
||||
) {
|
||||
super(params, messageBus, toolName);
|
||||
}
|
||||
|
||||
getDescription(): string {
|
||||
const title = this.params[TOPIC_PARAM_TITLE];
|
||||
const intent = this.params[TOPIC_PARAM_STRATEGIC_INTENT];
|
||||
if (title) {
|
||||
return `Update topic to: "${title}"`;
|
||||
}
|
||||
return `Update tactical intent: "${intent || '...'}"`;
|
||||
}
|
||||
|
||||
async execute(): Promise<ToolResult> {
|
||||
const title = this.params[TOPIC_PARAM_TITLE];
|
||||
const summary = this.params[TOPIC_PARAM_SUMMARY];
|
||||
const strategicIntent = this.params[TOPIC_PARAM_STRATEGIC_INTENT];
|
||||
|
||||
const activeTopic = this.config.topicState.getTopic();
|
||||
const isNewTopic = !!(
|
||||
title &&
|
||||
title.trim() !== '' &&
|
||||
title.trim() !== activeTopic
|
||||
);
|
||||
|
||||
this.config.topicState.setTopic(title, strategicIntent);
|
||||
|
||||
const currentTopic = this.config.topicState.getTopic() || '...';
|
||||
const currentIntent =
|
||||
strategicIntent || this.config.topicState.getIntent() || '...';
|
||||
|
||||
debugLogger.log(
|
||||
`[TopicTool] Update: Topic="${currentTopic}", Intent="${currentIntent}", isNew=${isNewTopic}`,
|
||||
);
|
||||
|
||||
let llmContent = '';
|
||||
let returnDisplay = '';
|
||||
|
||||
if (isNewTopic) {
|
||||
// Handle New Topic Header & Summary
|
||||
llmContent = `Current topic: "${currentTopic}"\nTopic summary: ${summary || '...'}`;
|
||||
returnDisplay = `## 📂 Topic: **${currentTopic}**\n\n**Summary:**\n${summary || '...'}`;
|
||||
|
||||
if (strategicIntent && strategicIntent.trim()) {
|
||||
llmContent += `\n\nStrategic Intent: ${strategicIntent.trim()}`;
|
||||
returnDisplay += `\n\n> [!STRATEGY]\n> **Intent:** ${strategicIntent.trim()}`;
|
||||
}
|
||||
} else {
|
||||
// Tactical update only
|
||||
llmContent = `Strategic Intent: ${currentIntent}`;
|
||||
returnDisplay = `> [!STRATEGY]\n> **Intent:** ${currentIntent}`;
|
||||
}
|
||||
|
||||
return {
|
||||
llmContent,
|
||||
returnDisplay,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tool to update semantic topic context and tactical intent for UI grouping and model focus.
|
||||
*/
|
||||
export class UpdateTopicTool extends BaseDeclarativeTool<
|
||||
UpdateTopicParams,
|
||||
ToolResult
|
||||
> {
|
||||
constructor(
|
||||
private readonly config: Config,
|
||||
messageBus: MessageBus,
|
||||
) {
|
||||
const declaration = getUpdateTopicDeclaration();
|
||||
super(
|
||||
UPDATE_TOPIC_TOOL_NAME,
|
||||
'Update Topic Context',
|
||||
declaration.description ?? '',
|
||||
Kind.Think,
|
||||
declaration.parametersJsonSchema,
|
||||
messageBus,
|
||||
);
|
||||
}
|
||||
|
||||
protected createInvocation(
|
||||
params: UpdateTopicParams,
|
||||
messageBus: MessageBus,
|
||||
): UpdateTopicInvocation {
|
||||
return new UpdateTopicInvocation(
|
||||
params,
|
||||
messageBus,
|
||||
this.name,
|
||||
this.config,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user