Revert background agent commits (#4479)
This commit is contained in:
parent
5b7b6fe608
commit
4dbd9f30b6
|
@ -916,10 +916,6 @@
|
||||||
"resolved": "packages/core",
|
"resolved": "packages/core",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
"node_modules/@google/gemini-cli-examples": {
|
|
||||||
"resolved": "packages/examples",
|
|
||||||
"link": true
|
|
||||||
},
|
|
||||||
"node_modules/@google/genai": {
|
"node_modules/@google/genai": {
|
||||||
"version": "1.9.0",
|
"version": "1.9.0",
|
||||||
"resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.9.0.tgz",
|
"resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.9.0.tgz",
|
||||||
|
@ -5661,19 +5657,6 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/glob": {
|
||||||
"version": "10.4.5",
|
"version": "10.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
|
||||||
|
@ -9207,16 +9190,6 @@
|
||||||
"node": ">=4"
|
"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": {
|
"node_modules/restore-cursor": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
|
||||||
|
@ -10517,26 +10490,6 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "0BSD"
|
"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": {
|
"node_modules/type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||||
|
@ -11907,17 +11860,6 @@
|
||||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
||||||
"license": "MIT"
|
"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": {
|
"packages/vscode-ide-companion": {
|
||||||
"name": "gemini-cli-vscode-ide-companion",
|
"name": "gemini-cli-vscode-ide-companion",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
|
|
|
@ -382,7 +382,6 @@ export async function loadCliConfig(
|
||||||
toolCallCommand: settings.toolCallCommand,
|
toolCallCommand: settings.toolCallCommand,
|
||||||
mcpServerCommand: settings.mcpServerCommand,
|
mcpServerCommand: settings.mcpServerCommand,
|
||||||
mcpServers,
|
mcpServers,
|
||||||
backgroundAgents: settings.backgroundAgents,
|
|
||||||
userMemory: memoryContent,
|
userMemory: memoryContent,
|
||||||
geminiMdFileCount: fileCount,
|
geminiMdFileCount: fileCount,
|
||||||
approvalMode: argv.yolo || false ? ApprovalMode.YOLO : ApprovalMode.DEFAULT,
|
approvalMode: argv.yolo || false ? ApprovalMode.YOLO : ApprovalMode.DEFAULT,
|
||||||
|
|
|
@ -64,7 +64,6 @@ export interface Settings {
|
||||||
toolCallCommand?: string;
|
toolCallCommand?: string;
|
||||||
mcpServerCommand?: string;
|
mcpServerCommand?: string;
|
||||||
mcpServers?: Record<string, MCPServerConfig>;
|
mcpServers?: Record<string, MCPServerConfig>;
|
||||||
backgroundAgents?: Record<string, MCPServerConfig>;
|
|
||||||
allowMCPServers?: string[];
|
allowMCPServers?: string[];
|
||||||
excludeMCPServers?: string[];
|
excludeMCPServers?: string[];
|
||||||
showMemoryUsage?: boolean;
|
showMemoryUsage?: boolean;
|
||||||
|
|
|
@ -96,7 +96,6 @@ describe('CommandService', () => {
|
||||||
mockConfig = {
|
mockConfig = {
|
||||||
getIdeMode: vi.fn(),
|
getIdeMode: vi.fn(),
|
||||||
getCheckpointingEnabled: vi.fn(),
|
getCheckpointingEnabled: vi.fn(),
|
||||||
getBackgroundAgentManager: vi.fn(),
|
|
||||||
} as unknown as Mocked<Config>;
|
} as unknown as Mocked<Config>;
|
||||||
vi.mocked(ideCommand).mockReturnValue(null);
|
vi.mocked(ideCommand).mockReturnValue(null);
|
||||||
vi.mocked(restoreCommand).mockReturnValue(null);
|
vi.mocked(restoreCommand).mockReturnValue(null);
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
import { Config } from '@google/gemini-cli-core';
|
import { Config } from '@google/gemini-cli-core';
|
||||||
import { SlashCommand } from '../ui/commands/types.js';
|
import { SlashCommand } from '../ui/commands/types.js';
|
||||||
import { memoryCommand } from '../ui/commands/memoryCommand.js';
|
import { memoryCommand } from '../ui/commands/memoryCommand.js';
|
||||||
import { backgroundCommand } from '../ui/commands/backgroundCommand.js';
|
|
||||||
import { helpCommand } from '../ui/commands/helpCommand.js';
|
import { helpCommand } from '../ui/commands/helpCommand.js';
|
||||||
import { clearCommand } from '../ui/commands/clearCommand.js';
|
import { clearCommand } from '../ui/commands/clearCommand.js';
|
||||||
import { corgiCommand } from '../ui/commands/corgiCommand.js';
|
import { corgiCommand } from '../ui/commands/corgiCommand.js';
|
||||||
|
@ -34,7 +33,6 @@ const loadBuiltInCommands = async (
|
||||||
const allCommands = [
|
const allCommands = [
|
||||||
aboutCommand,
|
aboutCommand,
|
||||||
authCommand,
|
authCommand,
|
||||||
backgroundCommand(config),
|
|
||||||
bugCommand,
|
bugCommand,
|
||||||
chatCommand,
|
chatCommand,
|
||||||
clearCommand,
|
clearCommand,
|
||||||
|
|
|
@ -9,7 +9,6 @@ import { render } from 'ink-testing-library';
|
||||||
import { AppWrapper as App } from './App.js';
|
import { AppWrapper as App } from './App.js';
|
||||||
import {
|
import {
|
||||||
Config as ServerConfig,
|
Config as ServerConfig,
|
||||||
BackgroundAgentManager,
|
|
||||||
MCPServerConfig,
|
MCPServerConfig,
|
||||||
ApprovalMode,
|
ApprovalMode,
|
||||||
ToolRegistry,
|
ToolRegistry,
|
||||||
|
@ -52,7 +51,6 @@ interface MockServerConfig {
|
||||||
getSandbox: Mock<() => SandboxConfig | undefined>;
|
getSandbox: Mock<() => SandboxConfig | undefined>;
|
||||||
getTargetDir: Mock<() => string>;
|
getTargetDir: Mock<() => string>;
|
||||||
getToolRegistry: Mock<() => ToolRegistry>; // Use imported ToolRegistry type
|
getToolRegistry: Mock<() => ToolRegistry>; // Use imported ToolRegistry type
|
||||||
getBackgroundAgentManager: Mock<() => BackgroundAgentManager>;
|
|
||||||
getDebugMode: Mock<() => boolean>;
|
getDebugMode: Mock<() => boolean>;
|
||||||
getQuestion: Mock<() => string | undefined>;
|
getQuestion: Mock<() => string | undefined>;
|
||||||
getFullContext: Mock<() => boolean>;
|
getFullContext: Mock<() => boolean>;
|
||||||
|
@ -119,7 +117,6 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => {
|
||||||
getSandbox: vi.fn(() => opts.sandbox),
|
getSandbox: vi.fn(() => opts.sandbox),
|
||||||
getTargetDir: vi.fn(() => opts.targetDir || '/test/dir'),
|
getTargetDir: vi.fn(() => opts.targetDir || '/test/dir'),
|
||||||
getToolRegistry: vi.fn(() => ({}) as ToolRegistry), // Simple mock
|
getToolRegistry: vi.fn(() => ({}) as ToolRegistry), // Simple mock
|
||||||
getBackgroundAgentManager: vi.fn(() => new BackgroundAgentManager([])),
|
|
||||||
getDebugMode: vi.fn(() => opts.debugMode || false),
|
getDebugMode: vi.fn(() => opts.debugMode || false),
|
||||||
getQuestion: vi.fn(() => opts.question),
|
getQuestion: vi.fn(() => opts.question),
|
||||||
getFullContext: vi.fn(() => opts.fullContext ?? false),
|
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,
|
DEFAULT_GEMINI_FLASH_MODEL,
|
||||||
} from './models.js';
|
} from './models.js';
|
||||||
import { ClearcutLogger } from '../telemetry/clearcut-logger/clearcut-logger.js';
|
import { ClearcutLogger } from '../telemetry/clearcut-logger/clearcut-logger.js';
|
||||||
import {
|
|
||||||
BackgroundAgentManager,
|
|
||||||
loadBackgroundAgentManager,
|
|
||||||
} from '../background/backgroundManager.js';
|
|
||||||
|
|
||||||
export enum ApprovalMode {
|
export enum ApprovalMode {
|
||||||
DEFAULT = 'default',
|
DEFAULT = 'default',
|
||||||
|
@ -131,7 +127,6 @@ export interface ConfigParameters {
|
||||||
toolCallCommand?: string;
|
toolCallCommand?: string;
|
||||||
mcpServerCommand?: string;
|
mcpServerCommand?: string;
|
||||||
mcpServers?: Record<string, MCPServerConfig>;
|
mcpServers?: Record<string, MCPServerConfig>;
|
||||||
backgroundAgents?: Record<string, MCPServerConfig>;
|
|
||||||
userMemory?: string;
|
userMemory?: string;
|
||||||
geminiMdFileCount?: number;
|
geminiMdFileCount?: number;
|
||||||
approvalMode?: ApprovalMode;
|
approvalMode?: ApprovalMode;
|
||||||
|
@ -163,7 +158,6 @@ export interface ConfigParameters {
|
||||||
|
|
||||||
export class Config {
|
export class Config {
|
||||||
private toolRegistry!: ToolRegistry;
|
private toolRegistry!: ToolRegistry;
|
||||||
private backgroundAgentManager?: BackgroundAgentManager;
|
|
||||||
private readonly sessionId: string;
|
private readonly sessionId: string;
|
||||||
private contentGeneratorConfig!: ContentGeneratorConfig;
|
private contentGeneratorConfig!: ContentGeneratorConfig;
|
||||||
private readonly embeddingModel: string;
|
private readonly embeddingModel: string;
|
||||||
|
@ -178,7 +172,6 @@ export class Config {
|
||||||
private readonly toolCallCommand: string | undefined;
|
private readonly toolCallCommand: string | undefined;
|
||||||
private readonly mcpServerCommand: string | undefined;
|
private readonly mcpServerCommand: string | undefined;
|
||||||
private readonly mcpServers: Record<string, MCPServerConfig> | undefined;
|
private readonly mcpServers: Record<string, MCPServerConfig> | undefined;
|
||||||
private readonly backgroundAgents?: Record<string, MCPServerConfig>;
|
|
||||||
private userMemory: string;
|
private userMemory: string;
|
||||||
private geminiMdFileCount: number;
|
private geminiMdFileCount: number;
|
||||||
private approvalMode: ApprovalMode;
|
private approvalMode: ApprovalMode;
|
||||||
|
@ -231,7 +224,6 @@ export class Config {
|
||||||
this.toolCallCommand = params.toolCallCommand;
|
this.toolCallCommand = params.toolCallCommand;
|
||||||
this.mcpServerCommand = params.mcpServerCommand;
|
this.mcpServerCommand = params.mcpServerCommand;
|
||||||
this.mcpServers = params.mcpServers;
|
this.mcpServers = params.mcpServers;
|
||||||
this.backgroundAgents = params.backgroundAgents;
|
|
||||||
this.userMemory = params.userMemory ?? '';
|
this.userMemory = params.userMemory ?? '';
|
||||||
this.geminiMdFileCount = params.geminiMdFileCount ?? 0;
|
this.geminiMdFileCount = params.geminiMdFileCount ?? 0;
|
||||||
this.approvalMode = params.approvalMode ?? ApprovalMode.DEFAULT;
|
this.approvalMode = params.approvalMode ?? ApprovalMode.DEFAULT;
|
||||||
|
@ -289,10 +281,6 @@ export class Config {
|
||||||
if (this.getCheckpointingEnabled()) {
|
if (this.getCheckpointingEnabled()) {
|
||||||
await this.getGitService();
|
await this.getGitService();
|
||||||
}
|
}
|
||||||
this.backgroundAgentManager = await loadBackgroundAgentManager(
|
|
||||||
this.backgroundAgents,
|
|
||||||
this.debugMode,
|
|
||||||
);
|
|
||||||
this.toolRegistry = await this.createToolRegistry();
|
this.toolRegistry = await this.createToolRegistry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,10 +406,6 @@ export class Config {
|
||||||
return this.mcpServers;
|
return this.mcpServers;
|
||||||
}
|
}
|
||||||
|
|
||||||
getBackgroundAgentManager(): BackgroundAgentManager | undefined {
|
|
||||||
return this.backgroundAgentManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
getUserMemory(): string {
|
getUserMemory(): string {
|
||||||
return this.userMemory;
|
return this.userMemory;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,9 +24,6 @@ export * from './code_assist/oauth2.js';
|
||||||
export * from './code_assist/server.js';
|
export * from './code_assist/server.js';
|
||||||
export * from './code_assist/types.js';
|
export * from './code_assist/types.js';
|
||||||
|
|
||||||
export * from './background/types.js';
|
|
||||||
export * from './background/backgroundManager.js';
|
|
||||||
|
|
||||||
// Export utilities
|
// Export utilities
|
||||||
export * from './utils/paths.js';
|
export * from './utils/paths.js';
|
||||||
export * from './utils/schemaValidator.js';
|
export * from './utils/schemaValidator.js';
|
||||||
|
|
|
@ -113,7 +113,9 @@ export class DiscoveredMCPTool extends BaseTool<ToolParams, ToolResult> {
|
||||||
args: params,
|
args: params,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const responseParts = await this.mcpTool.callTool(functionCalls);
|
|
||||||
|
const responseParts: Part[] = await this.mcpTool.callTool(functionCalls);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
llmContent: responseParts,
|
llmContent: responseParts,
|
||||||
returnDisplay: getStringifiedResultForDisplay(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