Revert background agent commits (#4479)
This commit is contained in:
parent
5b7b6fe608
commit
4dbd9f30b6
|
@ -916,10 +916,6 @@
|
|||
"resolved": "packages/core",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@google/gemini-cli-examples": {
|
||||
"resolved": "packages/examples",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@google/genai": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.9.0.tgz",
|
||||
|
@ -5661,19 +5657,6 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-tsconfig": {
|
||||
"version": "4.10.1",
|
||||
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz",
|
||||
"integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"resolve-pkg-maps": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "10.4.5",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
||||
|
@ -9207,16 +9190,6 @@
|
|||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-pkg-maps": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
||||
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/restore-cursor": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
|
||||
|
@ -10517,26 +10490,6 @@
|
|||
"dev": true,
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.20.3",
|
||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz",
|
||||
"integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"esbuild": "~0.25.0",
|
||||
"get-tsconfig": "^4.7.5"
|
||||
},
|
||||
"bin": {
|
||||
"tsx": "dist/cli.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.3"
|
||||
}
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
|
@ -11907,17 +11860,6 @@
|
|||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"packages/examples": {
|
||||
"name": "@google/gemini-cli-examples",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "1.15.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tsx": "^4.16.2"
|
||||
}
|
||||
},
|
||||
"packages/vscode-ide-companion": {
|
||||
"name": "gemini-cli-vscode-ide-companion",
|
||||
"version": "0.0.1",
|
||||
|
|
|
@ -382,7 +382,6 @@ export async function loadCliConfig(
|
|||
toolCallCommand: settings.toolCallCommand,
|
||||
mcpServerCommand: settings.mcpServerCommand,
|
||||
mcpServers,
|
||||
backgroundAgents: settings.backgroundAgents,
|
||||
userMemory: memoryContent,
|
||||
geminiMdFileCount: fileCount,
|
||||
approvalMode: argv.yolo || false ? ApprovalMode.YOLO : ApprovalMode.DEFAULT,
|
||||
|
|
|
@ -64,7 +64,6 @@ export interface Settings {
|
|||
toolCallCommand?: string;
|
||||
mcpServerCommand?: string;
|
||||
mcpServers?: Record<string, MCPServerConfig>;
|
||||
backgroundAgents?: Record<string, MCPServerConfig>;
|
||||
allowMCPServers?: string[];
|
||||
excludeMCPServers?: string[];
|
||||
showMemoryUsage?: boolean;
|
||||
|
|
|
@ -96,7 +96,6 @@ describe('CommandService', () => {
|
|||
mockConfig = {
|
||||
getIdeMode: vi.fn(),
|
||||
getCheckpointingEnabled: vi.fn(),
|
||||
getBackgroundAgentManager: vi.fn(),
|
||||
} as unknown as Mocked<Config>;
|
||||
vi.mocked(ideCommand).mockReturnValue(null);
|
||||
vi.mocked(restoreCommand).mockReturnValue(null);
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
import { Config } from '@google/gemini-cli-core';
|
||||
import { SlashCommand } from '../ui/commands/types.js';
|
||||
import { memoryCommand } from '../ui/commands/memoryCommand.js';
|
||||
import { backgroundCommand } from '../ui/commands/backgroundCommand.js';
|
||||
import { helpCommand } from '../ui/commands/helpCommand.js';
|
||||
import { clearCommand } from '../ui/commands/clearCommand.js';
|
||||
import { corgiCommand } from '../ui/commands/corgiCommand.js';
|
||||
|
@ -34,7 +33,6 @@ const loadBuiltInCommands = async (
|
|||
const allCommands = [
|
||||
aboutCommand,
|
||||
authCommand,
|
||||
backgroundCommand(config),
|
||||
bugCommand,
|
||||
chatCommand,
|
||||
clearCommand,
|
||||
|
|
|
@ -9,7 +9,6 @@ import { render } from 'ink-testing-library';
|
|||
import { AppWrapper as App } from './App.js';
|
||||
import {
|
||||
Config as ServerConfig,
|
||||
BackgroundAgentManager,
|
||||
MCPServerConfig,
|
||||
ApprovalMode,
|
||||
ToolRegistry,
|
||||
|
@ -52,7 +51,6 @@ interface MockServerConfig {
|
|||
getSandbox: Mock<() => SandboxConfig | undefined>;
|
||||
getTargetDir: Mock<() => string>;
|
||||
getToolRegistry: Mock<() => ToolRegistry>; // Use imported ToolRegistry type
|
||||
getBackgroundAgentManager: Mock<() => BackgroundAgentManager>;
|
||||
getDebugMode: Mock<() => boolean>;
|
||||
getQuestion: Mock<() => string | undefined>;
|
||||
getFullContext: Mock<() => boolean>;
|
||||
|
@ -119,7 +117,6 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
|||
getSandbox: vi.fn(() => opts.sandbox),
|
||||
getTargetDir: vi.fn(() => opts.targetDir || '/test/dir'),
|
||||
getToolRegistry: vi.fn(() => ({}) as ToolRegistry), // Simple mock
|
||||
getBackgroundAgentManager: vi.fn(() => new BackgroundAgentManager([])),
|
||||
getDebugMode: vi.fn(() => opts.debugMode || false),
|
||||
getQuestion: vi.fn(() => opts.question),
|
||||
getFullContext: vi.fn(() => opts.fullContext ?? false),
|
||||
|
|
|
@ -1,262 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { SlashCommand, CommandContext } from './types.js';
|
||||
import {
|
||||
Config,
|
||||
BackgroundAgentMessage,
|
||||
partListUnionToString,
|
||||
} from '@google/gemini-cli-core';
|
||||
|
||||
const MAX_STATUS_MESSAGE_LENGTH = 100;
|
||||
|
||||
function toMessageString(message?: BackgroundAgentMessage): string {
|
||||
return partListUnionToString(message?.parts ?? []).trim();
|
||||
}
|
||||
|
||||
function toOneliner(input: string, maxlength: number) {
|
||||
let output = input.replace(/\r?\n|\r/g, ' ');
|
||||
if (output.length > maxlength) {
|
||||
output = output.substring(0, maxlength) + '...';
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
function getActiveAgent(context: CommandContext) {
|
||||
const agent =
|
||||
context.services.config?.getBackgroundAgentManager()?.activeAgent;
|
||||
if (!agent) {
|
||||
throw Error('There is no active background agent.');
|
||||
}
|
||||
return agent;
|
||||
}
|
||||
|
||||
function addClientHistory(context: CommandContext, text: string) {
|
||||
context.services.config!.getGeminiClient().addHistory({
|
||||
role: 'user',
|
||||
parts: [{ text }],
|
||||
});
|
||||
|
||||
context.services.config!.getGeminiClient().addHistory({
|
||||
role: 'model',
|
||||
parts: [{ text: 'Got it.' }],
|
||||
});
|
||||
}
|
||||
|
||||
const startSubcommand: SlashCommand = {
|
||||
name: 'start',
|
||||
description:
|
||||
'Start a new task with the provided prompt. Usage: /bg start <prompt>',
|
||||
action: async (context, args) => {
|
||||
if (!args || args.trim() === '') {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'The `start` command requires a prompt.',
|
||||
};
|
||||
}
|
||||
|
||||
const agent = getActiveAgent(context);
|
||||
const task = await agent.startTask(args);
|
||||
|
||||
addClientHistory(
|
||||
context,
|
||||
`I started a background task with id '${task.id}' and prompt:\n${args}`,
|
||||
);
|
||||
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: `Started background task with id '${task.id}' and prompt:\n${args}`,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const stopSubcommand: SlashCommand = {
|
||||
name: 'stop',
|
||||
description: 'Stops a running task. Usage: /bg stop <task_id>',
|
||||
action: async (context, args) => {
|
||||
if (!args || args.trim() === '') {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'The `stop` command requires a task id.',
|
||||
};
|
||||
}
|
||||
const agent = getActiveAgent(context);
|
||||
await agent.cancelTask(args);
|
||||
addClientHistory(context, `I canceled the background task with id ${args}`);
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: `Stopped background task with id ${args}.`,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const listSubcommand: SlashCommand = {
|
||||
name: 'list',
|
||||
description: 'List all tasks',
|
||||
action: async (context, args) => {
|
||||
if (args && args.trim() !== '') {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'The `list` command takes no arguments.',
|
||||
};
|
||||
}
|
||||
|
||||
const agent = getActiveAgent(context);
|
||||
const tasks = await agent.listTasks();
|
||||
let content: string;
|
||||
if (tasks.length === 0) {
|
||||
content = 'No background tasks found.';
|
||||
} else {
|
||||
const taskList = tasks
|
||||
.map((task) => {
|
||||
const shortStatus = toOneliner(
|
||||
toMessageString(task.status.message),
|
||||
MAX_STATUS_MESSAGE_LENGTH,
|
||||
);
|
||||
return ` - ${task.id}: (${task.status.state}) ${shortStatus}`;
|
||||
})
|
||||
.join('\n');
|
||||
content = `Background tasks:\n${taskList}`;
|
||||
}
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const getSubcommand: SlashCommand = {
|
||||
name: 'get',
|
||||
description: 'View a task. Usage: /bg get <task_id>',
|
||||
action: async (context, args) => {
|
||||
if (!args || args.trim() === '') {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'The `get` command requires a task id.',
|
||||
};
|
||||
}
|
||||
const agent = getActiveAgent(context);
|
||||
const task = await agent.getTask(args);
|
||||
const content = `Task Details for ${task.id}:
|
||||
Status: (${task.status.state}) ${toMessageString(task.status.message)}}`;
|
||||
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const logsSubcommand: SlashCommand = {
|
||||
name: 'logs',
|
||||
description: "View a task's recent logs. Usage: /bg log <task_id>",
|
||||
action: async (context, args) => {
|
||||
if (!args || args.trim() === '') {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'The `log` command requires a task id.',
|
||||
};
|
||||
}
|
||||
const agent = getActiveAgent(context);
|
||||
const task = await agent.getTask(args, 5);
|
||||
const contents = [
|
||||
`Task logs for ${task.id}. status: (${task.status.state})`,
|
||||
];
|
||||
(task.history ?? []).forEach((message) => {
|
||||
contents.push(toMessageString(message));
|
||||
});
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: contents.join('\n\n'),
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const messageSubcommand: SlashCommand = {
|
||||
name: 'message',
|
||||
description:
|
||||
'Send a message to a task. Usage: /bg message <task_id> <message>',
|
||||
action: async (context, args) => {
|
||||
if (!args || args.trim() === '' || !args.trim().includes(' ')) {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'The `message` command requires a task id and a message.',
|
||||
};
|
||||
}
|
||||
|
||||
const firstSpaceIndex = args.indexOf(' ');
|
||||
const id = args.substring(0, firstSpaceIndex);
|
||||
const message = args.substring(firstSpaceIndex + 1);
|
||||
|
||||
const agent = getActiveAgent(context);
|
||||
await agent.messageTask(id, message);
|
||||
addClientHistory(
|
||||
context,
|
||||
`I sent a message to the background task with id '${id}':\n${message}`,
|
||||
);
|
||||
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: `Sent a message to the background task with id '${id}':\n${message}`,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const deleteSubcommand: SlashCommand = {
|
||||
name: 'delete',
|
||||
description: 'Deletes a task. Usage: /bg delete <task_id>',
|
||||
action: async (context, args) => {
|
||||
if (!args) {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'The `delete` command requires a task id.',
|
||||
};
|
||||
}
|
||||
const agent = getActiveAgent(context);
|
||||
await agent.deleteTask(args);
|
||||
addClientHistory(context, `I deleted the background task with id ${args}`);
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'info',
|
||||
content: `Task ${args} deleted.`,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
export const backgroundCommand = (
|
||||
config: Config | null,
|
||||
): SlashCommand | null => {
|
||||
if (!config?.getBackgroundAgentManager()?.activeAgent) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
name: 'background',
|
||||
altName: 'bg',
|
||||
description: "Commands for managing the background agent's tasks",
|
||||
subCommands: [
|
||||
startSubcommand,
|
||||
stopSubcommand,
|
||||
listSubcommand,
|
||||
getSubcommand,
|
||||
logsSubcommand,
|
||||
messageSubcommand,
|
||||
deleteSubcommand,
|
||||
],
|
||||
};
|
||||
};
|
|
@ -1,126 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { MCPServerConfig } from '../config/config.js';
|
||||
import { connectToMcpServer, discoverTools } from '../tools/mcp-client.js';
|
||||
import { DiscoveredMCPTool } from '../tools/mcp-tool.js';
|
||||
import {
|
||||
BackgroundAgentTasksResponseSchema,
|
||||
BackgroundAgentTaskResponseSchema,
|
||||
BackgroundAgentTask,
|
||||
} from './types.js';
|
||||
|
||||
export async function loadBackgroundAgent(
|
||||
name: string,
|
||||
config: MCPServerConfig,
|
||||
debugMode: boolean,
|
||||
): Promise<BackgroundAgent> {
|
||||
const server = await connectToMcpServer(name, config, debugMode);
|
||||
try {
|
||||
const tools = await discoverTools(name, config, server);
|
||||
return new BackgroundAgent(name, tools);
|
||||
} catch (error) {
|
||||
await server.close();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export class BackgroundAgent {
|
||||
readonly startTaskTool: DiscoveredMCPTool;
|
||||
readonly getTaskTool: DiscoveredMCPTool;
|
||||
readonly listTasksTool: DiscoveredMCPTool;
|
||||
readonly messageTaskTool: DiscoveredMCPTool;
|
||||
readonly deleteTaskTool: DiscoveredMCPTool;
|
||||
readonly cancelTaskTool: DiscoveredMCPTool;
|
||||
|
||||
constructor(
|
||||
readonly serverName: string,
|
||||
tools: DiscoveredMCPTool[],
|
||||
) {
|
||||
const getToolOrFail = (name: string): DiscoveredMCPTool => {
|
||||
for (const tool of tools) {
|
||||
if (tool.serverToolName === name) {
|
||||
return tool;
|
||||
}
|
||||
}
|
||||
throw new Error(`missing expected tool: ${name}`);
|
||||
};
|
||||
|
||||
this.startTaskTool = getToolOrFail('startTask');
|
||||
this.getTaskTool = getToolOrFail('getTask');
|
||||
this.listTasksTool = getToolOrFail('listTasks');
|
||||
this.messageTaskTool = getToolOrFail('messageTask');
|
||||
this.deleteTaskTool = getToolOrFail('deleteTask');
|
||||
this.cancelTaskTool = getToolOrFail('cancelTask');
|
||||
}
|
||||
|
||||
async startTask(prompt: string): Promise<BackgroundAgentTask> {
|
||||
const resp = await this.callTool(this.startTaskTool, {
|
||||
prompt: {
|
||||
role: 'user',
|
||||
parts: [{ text: prompt }],
|
||||
},
|
||||
});
|
||||
const taskResp = await BackgroundAgentTaskResponseSchema.parseAsync(resp);
|
||||
return taskResp.structuredContent;
|
||||
}
|
||||
|
||||
async getTask(
|
||||
id: string,
|
||||
historyLength?: number,
|
||||
): Promise<BackgroundAgentTask> {
|
||||
const resp = await this.callTool(this.getTaskTool, {
|
||||
id,
|
||||
historyLength,
|
||||
});
|
||||
const taskResp = await BackgroundAgentTaskResponseSchema.parseAsync(resp);
|
||||
return taskResp.structuredContent;
|
||||
}
|
||||
|
||||
async listTasks(): Promise<BackgroundAgentTask[]> {
|
||||
const resp = await this.callTool(this.listTasksTool, {});
|
||||
const tasksResp = await BackgroundAgentTasksResponseSchema.parseAsync(resp);
|
||||
return tasksResp.structuredContent;
|
||||
}
|
||||
|
||||
async messageTask(id: string, message: string) {
|
||||
await this.callTool(this.messageTaskTool, {
|
||||
id,
|
||||
message: {
|
||||
role: 'user',
|
||||
parts: [{ text: message }],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async deleteTask(id: string) {
|
||||
await this.callTool(this.deleteTaskTool, { id });
|
||||
}
|
||||
|
||||
async cancelTask(id: string) {
|
||||
await this.callTool(this.cancelTaskTool, { id });
|
||||
}
|
||||
|
||||
private async callTool(
|
||||
tool: DiscoveredMCPTool,
|
||||
params: Record<string, unknown>,
|
||||
): Promise<Record<string, unknown>> {
|
||||
const { llmContent: parts } = await tool.execute(params);
|
||||
if (
|
||||
!Array.isArray(parts) ||
|
||||
parts.length !== 1 ||
|
||||
typeof parts[0] !== 'object' ||
|
||||
parts[0]?.functionResponse?.response === undefined
|
||||
) {
|
||||
throw new Error('Expected exactly one part with a functionResponse');
|
||||
}
|
||||
const resp = parts[0].functionResponse.response;
|
||||
if ('isError' in resp && resp.isError) {
|
||||
throw new Error(`Error calling ${tool.displayName}: ${resp}`);
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { MCPServerConfig } from '../config/config.js';
|
||||
import { BackgroundAgent, loadBackgroundAgent } from './backgroundAgent.js';
|
||||
|
||||
export async function loadBackgroundAgentManager(
|
||||
backgroundAgentConfigs: Record<string, MCPServerConfig> | undefined,
|
||||
debugMode: boolean,
|
||||
): Promise<BackgroundAgentManager> {
|
||||
const agents = await Promise.all(
|
||||
Object.entries(backgroundAgentConfigs ?? {}).map(([name, config]) =>
|
||||
loadBackgroundAgent(name, config, debugMode).catch((error) => {
|
||||
console.error(`Error loading background agent '${name}': ${error}`);
|
||||
return null;
|
||||
}),
|
||||
),
|
||||
).then((agents) => agents.filter((agent) => agent !== null));
|
||||
return new BackgroundAgentManager(agents);
|
||||
}
|
||||
|
||||
export class BackgroundAgentManager {
|
||||
// The active agent. May be empty if none are confgured.
|
||||
activeAgent?: BackgroundAgent;
|
||||
|
||||
constructor(readonly backgroundAgents: BackgroundAgent[]) {
|
||||
if (backgroundAgents.length !== 0) {
|
||||
this.activeAgent = backgroundAgents[0];
|
||||
}
|
||||
}
|
||||
|
||||
setActiveAgentByName(name: string) {
|
||||
this.activeAgent = this.backgroundAgents.find(
|
||||
(agent) => agent.serverName === name,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
import { Outcome, Language, FunctionResponseScheduling } from '@google/genai';
|
||||
|
||||
// Should conform to Part in @google/genai
|
||||
export const PartSchema = z.object({
|
||||
videoMetadata: z
|
||||
.object({
|
||||
fps: z.number().optional(),
|
||||
endOffset: z.string().optional(),
|
||||
startOffset: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
thought: z.boolean().optional(),
|
||||
inlineData: z
|
||||
.object({
|
||||
displayName: z.string().optional(),
|
||||
data: z.string(),
|
||||
mimeType: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
fileData: z
|
||||
.object({
|
||||
displayName: z.string().optional(),
|
||||
fileUri: z.string(),
|
||||
mimeType: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
thoughtSignature: z.string().optional(),
|
||||
codeExecutionResult: z
|
||||
.object({
|
||||
outcome: z.nativeEnum(Outcome).optional(),
|
||||
output: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
executableCode: z
|
||||
.object({
|
||||
code: z.string().optional(),
|
||||
language: z.nativeEnum(Language).optional(),
|
||||
})
|
||||
.optional(),
|
||||
functionCall: z
|
||||
.object({
|
||||
id: z.string().optional(),
|
||||
args: z.record(z.unknown()).optional(),
|
||||
name: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
functionResponse: z
|
||||
.object({
|
||||
willContinue: z.boolean().optional(),
|
||||
scheduling: z.nativeEnum(FunctionResponseScheduling).optional(),
|
||||
id: z.string().optional(),
|
||||
name: z.string(),
|
||||
response: z.record(z.unknown()).optional(),
|
||||
})
|
||||
.optional(),
|
||||
text: z.string().optional(),
|
||||
});
|
||||
|
||||
export const BackgroundAgentMessageSchema = z.object({
|
||||
role: z.enum(['user', 'agent']).describe('The role of the sender.'),
|
||||
parts: z.array(PartSchema).describe('The parts of the message.'),
|
||||
});
|
||||
|
||||
export const BackgroundAgentTaskStatusSchema = z.object({
|
||||
state: z.enum([
|
||||
'submitted',
|
||||
'working',
|
||||
'input-required',
|
||||
'completed',
|
||||
'failed',
|
||||
]),
|
||||
message: BackgroundAgentMessageSchema.describe(
|
||||
'Message describing the state of the task.',
|
||||
).optional(),
|
||||
});
|
||||
|
||||
export const BackgroundAgentTaskSchema = z.object({
|
||||
id: z.string().describe('The id of the task. Must match `[a-zA-Z0-9.-_]+`'),
|
||||
status: BackgroundAgentTaskStatusSchema.describe(
|
||||
'The current status of the task.',
|
||||
),
|
||||
history: z
|
||||
.array(BackgroundAgentMessageSchema)
|
||||
.describe('Recent history of messages associated with this task')
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export type BackgroundAgentMessage = z.infer<
|
||||
typeof BackgroundAgentMessageSchema
|
||||
>;
|
||||
|
||||
export type BackgroundAgentTask = z.infer<typeof BackgroundAgentTaskSchema>;
|
||||
|
||||
export const BackgroundAgentTaskResponseSchema = z.object({
|
||||
structuredContent: BackgroundAgentTaskSchema,
|
||||
});
|
||||
|
||||
export const BackgroundAgentTasksResponseSchema = z.object({
|
||||
structuredContent: z.array(BackgroundAgentTaskSchema),
|
||||
});
|
|
@ -45,10 +45,6 @@ import {
|
|||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
} from './models.js';
|
||||
import { ClearcutLogger } from '../telemetry/clearcut-logger/clearcut-logger.js';
|
||||
import {
|
||||
BackgroundAgentManager,
|
||||
loadBackgroundAgentManager,
|
||||
} from '../background/backgroundManager.js';
|
||||
|
||||
export enum ApprovalMode {
|
||||
DEFAULT = 'default',
|
||||
|
@ -131,7 +127,6 @@ export interface ConfigParameters {
|
|||
toolCallCommand?: string;
|
||||
mcpServerCommand?: string;
|
||||
mcpServers?: Record<string, MCPServerConfig>;
|
||||
backgroundAgents?: Record<string, MCPServerConfig>;
|
||||
userMemory?: string;
|
||||
geminiMdFileCount?: number;
|
||||
approvalMode?: ApprovalMode;
|
||||
|
@ -163,7 +158,6 @@ export interface ConfigParameters {
|
|||
|
||||
export class Config {
|
||||
private toolRegistry!: ToolRegistry;
|
||||
private backgroundAgentManager?: BackgroundAgentManager;
|
||||
private readonly sessionId: string;
|
||||
private contentGeneratorConfig!: ContentGeneratorConfig;
|
||||
private readonly embeddingModel: string;
|
||||
|
@ -178,7 +172,6 @@ export class Config {
|
|||
private readonly toolCallCommand: string | undefined;
|
||||
private readonly mcpServerCommand: string | undefined;
|
||||
private readonly mcpServers: Record<string, MCPServerConfig> | undefined;
|
||||
private readonly backgroundAgents?: Record<string, MCPServerConfig>;
|
||||
private userMemory: string;
|
||||
private geminiMdFileCount: number;
|
||||
private approvalMode: ApprovalMode;
|
||||
|
@ -231,7 +224,6 @@ export class Config {
|
|||
this.toolCallCommand = params.toolCallCommand;
|
||||
this.mcpServerCommand = params.mcpServerCommand;
|
||||
this.mcpServers = params.mcpServers;
|
||||
this.backgroundAgents = params.backgroundAgents;
|
||||
this.userMemory = params.userMemory ?? '';
|
||||
this.geminiMdFileCount = params.geminiMdFileCount ?? 0;
|
||||
this.approvalMode = params.approvalMode ?? ApprovalMode.DEFAULT;
|
||||
|
@ -289,10 +281,6 @@ export class Config {
|
|||
if (this.getCheckpointingEnabled()) {
|
||||
await this.getGitService();
|
||||
}
|
||||
this.backgroundAgentManager = await loadBackgroundAgentManager(
|
||||
this.backgroundAgents,
|
||||
this.debugMode,
|
||||
);
|
||||
this.toolRegistry = await this.createToolRegistry();
|
||||
}
|
||||
|
||||
|
@ -418,10 +406,6 @@ export class Config {
|
|||
return this.mcpServers;
|
||||
}
|
||||
|
||||
getBackgroundAgentManager(): BackgroundAgentManager | undefined {
|
||||
return this.backgroundAgentManager;
|
||||
}
|
||||
|
||||
getUserMemory(): string {
|
||||
return this.userMemory;
|
||||
}
|
||||
|
|
|
@ -24,9 +24,6 @@ export * from './code_assist/oauth2.js';
|
|||
export * from './code_assist/server.js';
|
||||
export * from './code_assist/types.js';
|
||||
|
||||
export * from './background/types.js';
|
||||
export * from './background/backgroundManager.js';
|
||||
|
||||
// Export utilities
|
||||
export * from './utils/paths.js';
|
||||
export * from './utils/schemaValidator.js';
|
||||
|
|
|
@ -113,7 +113,9 @@ export class DiscoveredMCPTool extends BaseTool<ToolParams, ToolResult> {
|
|||
args: params,
|
||||
},
|
||||
];
|
||||
const responseParts = await this.mcpTool.callTool(functionCalls);
|
||||
|
||||
const responseParts: Part[] = await this.mcpTool.callTool(functionCalls);
|
||||
|
||||
return {
|
||||
llmContent: responseParts,
|
||||
returnDisplay: getStringifiedResultForDisplay(responseParts),
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
# Demo Background Agent
|
||||
|
||||
A pretend background agent that does not actually process tasks in the background. Configure in your settings.json with:
|
||||
|
||||
```javascript
|
||||
"backgroundAgents": {
|
||||
"demo-background-agent": {
|
||||
"command": "npm",
|
||||
"args": [
|
||||
"run",
|
||||
"start:demo-background-agent",
|
||||
"--workspace=@google/gemini-cli-examples"
|
||||
]
|
||||
}
|
||||
},
|
||||
```
|
|
@ -1,217 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||
import { z } from 'zod';
|
||||
|
||||
const BackgroundAgentMessageSchema = z.object({
|
||||
role: z.enum(['user', 'agent']),
|
||||
parts: z.array(z.any()),
|
||||
});
|
||||
|
||||
const BackgroundAgentTaskStatusSchema = z.object({
|
||||
state: z.enum([
|
||||
'submitted',
|
||||
'working',
|
||||
'input-required',
|
||||
'completed',
|
||||
'canceled',
|
||||
'failed',
|
||||
]),
|
||||
message: BackgroundAgentMessageSchema.optional(),
|
||||
});
|
||||
|
||||
const BackgroundAgentTaskSchema = z.object({
|
||||
id: z.string(),
|
||||
status: BackgroundAgentTaskStatusSchema,
|
||||
history: z.array(BackgroundAgentMessageSchema).optional(),
|
||||
});
|
||||
type BackgroundAgentTask = z.infer<typeof BackgroundAgentTaskSchema>;
|
||||
|
||||
const server = new McpServer({
|
||||
name: 'demo-background-agent',
|
||||
version: '1.0.0',
|
||||
});
|
||||
|
||||
const idToTask = new Map<string, BackgroundAgentTask>();
|
||||
|
||||
server.registerTool(
|
||||
'startTask',
|
||||
{
|
||||
title: 'Start a new task',
|
||||
description: 'Launches a new task asynchronously.',
|
||||
inputSchema: { prompt: BackgroundAgentMessageSchema },
|
||||
outputSchema: BackgroundAgentTaskSchema.shape,
|
||||
},
|
||||
({ prompt }) => {
|
||||
const task: BackgroundAgentTask = {
|
||||
id: crypto.randomUUID(),
|
||||
status: {
|
||||
state: 'submitted',
|
||||
message: prompt,
|
||||
},
|
||||
history: [],
|
||||
};
|
||||
|
||||
idToTask.set(task.id, task);
|
||||
|
||||
return {
|
||||
content: [],
|
||||
structuredContent: task,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
server.registerTool(
|
||||
'getTask',
|
||||
{
|
||||
title: 'Get a task',
|
||||
inputSchema: { id: z.string() },
|
||||
outputSchema: BackgroundAgentTaskSchema.shape,
|
||||
},
|
||||
({ id }) => {
|
||||
const task = idToTask.get(id);
|
||||
if (!task) {
|
||||
return {
|
||||
isError: true,
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'No such task',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
content: [],
|
||||
structuredContent: task,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
server.registerTool(
|
||||
'listTasks',
|
||||
{
|
||||
title: 'Lists tasks',
|
||||
outputSchema: {
|
||||
tasks: z.array(BackgroundAgentTaskSchema),
|
||||
},
|
||||
},
|
||||
() => {
|
||||
const out = {
|
||||
tasks: Array.from(idToTask.values()),
|
||||
};
|
||||
return {
|
||||
content: [],
|
||||
structuredContent: out,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
server.registerTool(
|
||||
'messageTask',
|
||||
{
|
||||
title: 'Send a message to a task',
|
||||
inputSchema: {
|
||||
id: z.string(),
|
||||
message: BackgroundAgentMessageSchema,
|
||||
},
|
||||
},
|
||||
({ id, message }) => {
|
||||
const task = idToTask.get(id);
|
||||
if (!task) {
|
||||
return {
|
||||
isError: true,
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'No such task',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
task.history?.push(message);
|
||||
task.status.message = message;
|
||||
|
||||
const statuses = BackgroundAgentTaskStatusSchema.shape.state.options;
|
||||
const randomStatus = statuses[Math.floor(Math.random() * statuses.length)];
|
||||
task.status.state = randomStatus;
|
||||
|
||||
return {
|
||||
content: [],
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
server.registerTool(
|
||||
'deleteTask',
|
||||
{
|
||||
title: 'Delete a task',
|
||||
inputSchema: { id: z.string() },
|
||||
},
|
||||
({ id }) => {
|
||||
const task = idToTask.get(id);
|
||||
if (!task) {
|
||||
return {
|
||||
isError: true,
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'No such task',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
idToTask.delete(id);
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'Task deleted',
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
server.registerTool(
|
||||
'cancelTask',
|
||||
{
|
||||
title: 'Cancels a task',
|
||||
inputSchema: { id: z.string() },
|
||||
},
|
||||
({ id }) => {
|
||||
const task = idToTask.get(id);
|
||||
if (!task) {
|
||||
return {
|
||||
isError: true,
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'No such task',
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
task.status.state = 'canceled';
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: 'Task cancelled',
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
|
@ -1,17 +0,0 @@
|
|||
{
|
||||
"name": "@google/gemini-cli-examples",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start:demo-background-agent": "tsx background_agent/demo-background-agent.ts",
|
||||
"build": "echo 'nothing to build'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "1.15.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tsx": "^4.16.2"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue