@google/gemini-cli-core 0.9.0-nightly.20251001.33269bdb → 0.9.0-nightly.20251002.0f465e88
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -54
- package/dist/src/agents/codebase-investigator.js +12 -6
- package/dist/src/agents/codebase-investigator.js.map +1 -1
- package/dist/src/agents/executor.d.ts +3 -8
- package/dist/src/agents/executor.js +195 -94
- package/dist/src/agents/executor.js.map +1 -1
- package/dist/src/agents/executor.test.js +331 -287
- package/dist/src/agents/executor.test.js.map +1 -1
- package/dist/src/agents/types.d.ts +16 -4
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +4 -2
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +11 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +3 -2
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +7 -2
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
- package/dist/src/telemetry/constants.d.ts +2 -0
- package/dist/src/telemetry/constants.js +2 -0
- package/dist/src/telemetry/constants.js.map +1 -1
- package/dist/src/telemetry/loggers.d.ts +2 -1
- package/dist/src/telemetry/loggers.js +18 -2
- package/dist/src/telemetry/loggers.js.map +1 -1
- package/dist/src/telemetry/loggers.test.js +2 -2
- package/dist/src/telemetry/loggers.test.js.map +1 -1
- package/dist/src/telemetry/types.d.ts +6 -0
- package/dist/src/telemetry/types.js +10 -0
- package/dist/src/telemetry/types.js.map +1 -1
- package/dist/src/tools/smart-edit.js +7 -1
- package/dist/src/tools/smart-edit.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,12 +11,12 @@ Gemini CLI is an open-source AI agent that brings the power of Gemini directly i
|
|
|
11
11
|
|
|
12
12
|
## 🚀 Why Gemini CLI?
|
|
13
13
|
|
|
14
|
-
- **🎯 Free tier**: 60 requests/min and 1,000 requests/day with personal Google account
|
|
15
|
-
- **🧠 Powerful Gemini 2.5 Pro**: Access to 1M token context window
|
|
16
|
-
- **🔧 Built-in tools**: Google Search grounding, file operations, shell commands, web fetching
|
|
17
|
-
- **🔌 Extensible**: MCP (Model Context Protocol) support for custom integrations
|
|
18
|
-
- **💻 Terminal-first**: Designed for developers who live in the command line
|
|
19
|
-
- **🛡️ Open source**: Apache 2.0 licensed
|
|
14
|
+
- **🎯 Free tier**: 60 requests/min and 1,000 requests/day with personal Google account.
|
|
15
|
+
- **🧠 Powerful Gemini 2.5 Pro**: Access to 1M token context window.
|
|
16
|
+
- **🔧 Built-in tools**: Google Search grounding, file operations, shell commands, web fetching.
|
|
17
|
+
- **🔌 Extensible**: MCP (Model Context Protocol) support for custom integrations.
|
|
18
|
+
- **💻 Terminal-first**: Designed for developers who live in the command line.
|
|
19
|
+
- **🛡️ Open source**: Apache 2.0 licensed.
|
|
20
20
|
|
|
21
21
|
## 📦 Installation
|
|
22
22
|
|
|
@@ -109,20 +109,14 @@ Choose the authentication method that best fits your needs:
|
|
|
109
109
|
|
|
110
110
|
### Option 1: Login with Google (OAuth login using your Google Account)
|
|
111
111
|
|
|
112
|
-
**✨ Best for:**
|
|
113
|
-
|
|
114
|
-
- Individual developers.
|
|
115
|
-
- Google AI Pro and AI Ultra subscribers.
|
|
116
|
-
- Anyone who has a Gemini Code Assist license.
|
|
117
|
-
|
|
118
|
-
_See [quota limits and terms of service](https://cloud.google.com/gemini/docs/quotas) for details._
|
|
112
|
+
**✨ Best for:** Individual developers as well as anyone who has a Gemini Code Assist License. (see [quota limits and terms of service](https://cloud.google.com/gemini/docs/quotas) for details)
|
|
119
113
|
|
|
120
114
|
**Benefits:**
|
|
121
115
|
|
|
122
|
-
- **Free tier
|
|
123
|
-
- **Gemini 2.5 Pro
|
|
116
|
+
- **Free tier**: 60 requests/min and 1,000 requests/day
|
|
117
|
+
- **Gemini 2.5 Pro** with 1M token context window
|
|
124
118
|
- **No API key management** - just sign in with your Google account
|
|
125
|
-
- **Automatic updates** to
|
|
119
|
+
- **Automatic updates** to latest models
|
|
126
120
|
|
|
127
121
|
#### Start Gemini CLI, then choose _Login with Google_ and follow the browser authentication flow when prompted
|
|
128
122
|
|
|
@@ -171,7 +165,7 @@ export GOOGLE_GENAI_USE_VERTEXAI=true
|
|
|
171
165
|
gemini
|
|
172
166
|
```
|
|
173
167
|
|
|
174
|
-
For Google Workspace accounts and other authentication methods, see the [authentication guide](./docs/
|
|
168
|
+
For Google Workspace accounts and other authentication methods, see the [authentication guide](./docs/get-started/authentication.md).
|
|
175
169
|
|
|
176
170
|
## 🚀 Getting Started
|
|
177
171
|
|
|
@@ -233,17 +227,18 @@ gemini
|
|
|
233
227
|
|
|
234
228
|
### Getting Started
|
|
235
229
|
|
|
236
|
-
- [**Quickstart Guide**](./docs/
|
|
237
|
-
- [**Authentication Setup**](./docs/
|
|
238
|
-
- [**Configuration Guide**](./docs/
|
|
239
|
-
- [**Keyboard Shortcuts**](./docs/keyboard-shortcuts.md) - Productivity tips
|
|
230
|
+
- [**Quickstart Guide**](./docs/get-started/index.md) - Get up and running quickly.
|
|
231
|
+
- [**Authentication Setup**](./docs/get-started/authentication.md) - Detailed auth configuration.
|
|
232
|
+
- [**Configuration Guide**](./docs/get-started/configuration.md) - Settings and customization.
|
|
233
|
+
- [**Keyboard Shortcuts**](./docs/cli/keyboard-shortcuts.md) - Productivity tips.
|
|
240
234
|
|
|
241
235
|
### Core Features
|
|
242
236
|
|
|
243
|
-
- [**Commands Reference**](./docs/cli/commands.md) - All slash commands (`/help`, `/chat`,
|
|
244
|
-
- [**
|
|
245
|
-
- [**
|
|
246
|
-
- [**
|
|
237
|
+
- [**Commands Reference**](./docs/cli/commands.md) - All slash commands (`/help`, `/chat`, etc).
|
|
238
|
+
- [**Custom Commands**](./docs/cli/custom-commands.md) - Create your own reusable commands.
|
|
239
|
+
- [**Context Files (GEMINI.md)**](./docs/cli/gemini-md.md) - Provide persistent context to Gemini CLI.
|
|
240
|
+
- [**Checkpointing**](./docs/cli/checkpointing.md) - Save and resume conversations.
|
|
241
|
+
- [**Token Caching**](./docs/cli/token-caching.md) - Optimize token usage.
|
|
247
242
|
|
|
248
243
|
### Tools & Extensions
|
|
249
244
|
|
|
@@ -251,31 +246,25 @@ gemini
|
|
|
251
246
|
- [File System Operations](./docs/tools/file-system.md)
|
|
252
247
|
- [Shell Commands](./docs/tools/shell.md)
|
|
253
248
|
- [Web Fetch & Search](./docs/tools/web-fetch.md)
|
|
254
|
-
|
|
255
|
-
- [**
|
|
256
|
-
- [**Custom Extensions**](./docs/extension.md) - Build your own commands
|
|
249
|
+
- [**MCP Server Integration**](./docs/tools/mcp-server.md) - Extend with custom tools.
|
|
250
|
+
- [**Custom Extensions**](./docs/extensions/index.md) - Build and share your own commands.
|
|
257
251
|
|
|
258
252
|
### Advanced Topics
|
|
259
253
|
|
|
260
|
-
- [**
|
|
261
|
-
- [**
|
|
262
|
-
- [**
|
|
263
|
-
- [**
|
|
264
|
-
- [**
|
|
265
|
-
- [**
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
- [**Settings Reference**](./docs/cli/configuration.md) - All configuration options
|
|
270
|
-
- [**Theme Customization**](./docs/cli/themes.md) - Visual customization
|
|
271
|
-
- [**.gemini Directory**](./docs/gemini-ignore.md) - Project-specific settings
|
|
272
|
-
- [**Environment Variables**](./docs/cli/configuration.md#environment-variables)
|
|
254
|
+
- [**Headless Mode (Scripting)**](./docs/cli/headless.md) - Use Gemini CLI in automated workflows.
|
|
255
|
+
- [**Architecture Overview**](./docs/architecture.md) - How Gemini CLI works.
|
|
256
|
+
- [**IDE Integration**](./docs/ide-integration/index.md) - VS Code companion.
|
|
257
|
+
- [**Sandboxing & Security**](./docs/cli/sandbox.md) - Safe execution environments.
|
|
258
|
+
- [**Trusted Folders**](./docs/cli/trusted-folders.md) - Control execution policies by folder.
|
|
259
|
+
- [**Enterprise Guide**](./docs/cli/enterprise.md) - Deploy and manage in a corporate environment.
|
|
260
|
+
- [**Telemetry & Monitoring**](./docs/cli/telemetry.md) - Usage tracking.
|
|
261
|
+
- [**Tools API Development**](./docs/core/tools-api.md) - Create custom tools.
|
|
273
262
|
|
|
274
263
|
### Troubleshooting & Support
|
|
275
264
|
|
|
276
|
-
- [**Troubleshooting Guide**](./docs/troubleshooting.md) - Common issues and solutions
|
|
277
|
-
- [**FAQ**](./docs/
|
|
278
|
-
- Use `/bug` command to report issues directly from the CLI
|
|
265
|
+
- [**Troubleshooting Guide**](./docs/troubleshooting.md) - Common issues and solutions.
|
|
266
|
+
- [**FAQ**](./docs/faq.md) - Frequently asked questions.
|
|
267
|
+
- Use `/bug` command to report issues directly from the CLI.
|
|
279
268
|
|
|
280
269
|
### Using MCP Servers
|
|
281
270
|
|
|
@@ -293,25 +282,25 @@ See the [MCP Server Integration guide](./docs/tools/mcp-server.md) for setup ins
|
|
|
293
282
|
|
|
294
283
|
We welcome contributions! Gemini CLI is fully open source (Apache 2.0), and we encourage the community to:
|
|
295
284
|
|
|
296
|
-
- Report bugs and suggest features
|
|
297
|
-
- Improve documentation
|
|
298
|
-
- Submit code improvements
|
|
299
|
-
- Share your MCP servers and extensions
|
|
285
|
+
- Report bugs and suggest features.
|
|
286
|
+
- Improve documentation.
|
|
287
|
+
- Submit code improvements.
|
|
288
|
+
- Share your MCP servers and extensions.
|
|
300
289
|
|
|
301
290
|
See our [Contributing Guide](./CONTRIBUTING.md) for development setup, coding standards, and how to submit pull requests.
|
|
302
291
|
|
|
303
|
-
Check our [Official Roadmap](https://github.com/orgs/google-gemini/projects/11
|
|
292
|
+
Check our [Official Roadmap](https://github.com/orgs/google-gemini/projects/11) for planned features and priorities.
|
|
304
293
|
|
|
305
294
|
## 📖 Resources
|
|
306
295
|
|
|
307
|
-
- **[Official Roadmap](./ROADMAP.md)** - See what's coming next
|
|
308
|
-
- **[NPM Package](https://www.npmjs.com/package/@google/gemini-cli)** - Package registry
|
|
309
|
-
- **[GitHub Issues](https://github.com/google-gemini/gemini-cli/issues)** - Report bugs or request features
|
|
310
|
-
- **[Security Advisories](https://github.com/google-gemini/gemini-cli/security/advisories)** - Security updates
|
|
296
|
+
- **[Official Roadmap](./ROADMAP.md)** - See what's coming next.
|
|
297
|
+
- **[NPM Package](https://www.npmjs.com/package/@google/gemini-cli)** - Package registry.
|
|
298
|
+
- **[GitHub Issues](https://github.com/google-gemini/gemini-cli/issues)** - Report bugs or request features.
|
|
299
|
+
- **[Security Advisories](https://github.com/google-gemini/gemini-cli/security/advisories)** - Security updates.
|
|
311
300
|
|
|
312
301
|
### Uninstall
|
|
313
302
|
|
|
314
|
-
See the [Uninstall Guide](docs/
|
|
303
|
+
See the [Uninstall Guide](docs/cli/uninstall.md) for removal instructions.
|
|
315
304
|
|
|
316
305
|
## 📄 Legal
|
|
317
306
|
|
|
@@ -8,6 +8,7 @@ import { ReadFileTool } from '../tools/read-file.js';
|
|
|
8
8
|
import { GLOB_TOOL_NAME } from '../tools/tool-names.js';
|
|
9
9
|
import { GrepTool } from '../tools/grep.js';
|
|
10
10
|
import { DEFAULT_GEMINI_MODEL } from '../config/models.js';
|
|
11
|
+
import { Type } from '@google/genai';
|
|
11
12
|
const CODEBASE_REPORT_MARKDOWN = `<CodebaseReport>
|
|
12
13
|
<SummaryOfFindings>
|
|
13
14
|
The user's objective is to remove an optional \`config\` property from the command context. The investigation identified that the \`CommandContext\` interface, defined in \`packages/cli/src/ui/commands/types.ts\`, contains a nullable \`config: Config | null\` property. A \`TODO\` comment confirms the intent to make this property non-nullable.
|
|
@@ -73,17 +74,22 @@ export const CodebaseInvestigatorAgent = {
|
|
|
73
74
|
},
|
|
74
75
|
},
|
|
75
76
|
outputConfig: {
|
|
76
|
-
|
|
77
|
+
outputName: 'report',
|
|
78
|
+
description: 'The final investigation report.',
|
|
79
|
+
schema: {
|
|
80
|
+
type: Type.STRING,
|
|
81
|
+
description: `A detailed markdown report summarizing the findings of the codebase investigation and insights that are the foundation for planning and executing any code modification related to the objective.
|
|
77
82
|
# Report Format
|
|
78
83
|
The final report should be structured markdown, clearly answering the investigation focus, citing the files, symbols, architectural patterns and how they relate to the given investigation focus.
|
|
79
84
|
The report should strictly follow a format like this example:
|
|
80
85
|
${CODEBASE_REPORT_MARKDOWN}
|
|
86
|
+
|
|
87
|
+
Completion Criteria:
|
|
88
|
+
- The report must directly address the initial \`objective\`.
|
|
89
|
+
- Cite specific files, functions, or configuration snippets and symbols as evidence for your findings.
|
|
90
|
+
- Conclude with a xml markdown summary of the key files, symbols, technologies, architectural patterns, and conventions discovered.
|
|
81
91
|
`,
|
|
82
|
-
|
|
83
|
-
'The report must directly address the initial `objective`.',
|
|
84
|
-
'Cite specific files, functions, or configuration snippets and symbols as evidence for your findings.',
|
|
85
|
-
'Conclude with a xml markdown summary of the key files, symbols, technologies, architectural patterns, and conventions discovered.',
|
|
86
|
-
],
|
|
92
|
+
},
|
|
87
93
|
},
|
|
88
94
|
modelConfig: {
|
|
89
95
|
model: DEFAULT_GEMINI_MODEL,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codebase-investigator.js","sourceRoot":"","sources":["../../../src/agents/codebase-investigator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"codebase-investigator.js","sourceRoot":"","sources":["../../../src/agents/codebase-investigator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAErC,MAAM,wBAAwB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA2Cf,CAAC;AAEnB;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAoB;IACxD,IAAI,EAAE,uBAAuB;IAC7B,WAAW,EAAE,6BAA6B;IAC1C,WAAW,EAAE;;kHAEmG;IAChH,WAAW,EAAE;QACX,MAAM,EAAE;YACN,SAAS,EAAE;gBACT,WAAW,EAAE;4HACuG;gBACpH,IAAI,EAAE,QAAQ;gBACd,QAAQ,EAAE,IAAI;aACf;SACF;KACF;IACD,YAAY,EAAE;QACZ,UAAU,EAAE,QAAQ;QACpB,WAAW,EAAE,iCAAiC;QAC9C,MAAM,EAAE;YACN,IAAI,EAAE,IAAI,CAAC,MAAM;YACjB,WAAW,EAAE;;;;EAIjB,wBAAwB;;;;;;CAMzB;SACI;KACF;IAED,WAAW,EAAE;QACX,KAAK,EAAE,oBAAoB;QAC3B,IAAI,EAAE,GAAG;QACT,KAAK,EAAE,IAAI;QACX,cAAc,EAAE,CAAC,CAAC;KACnB;IAED,SAAS,EAAE;QACT,gBAAgB,EAAE,CAAC;QACnB,SAAS,EAAE,EAAE;KACd;IAED,UAAU,EAAE;QACV,wCAAwC;QACxC,KAAK,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,IAAI,EAAE,cAAc,EAAE,QAAQ,CAAC,IAAI,CAAC;KACvE;IAED,YAAY,EAAE;QACZ,KAAK,EAAE;;;aAGE;QACT,YAAY,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqDhB,wBAAwB;CACzB;KACE;CACF,CAAC"}
|
|
@@ -10,11 +10,8 @@ export type ActivityCallback = (activity: SubagentActivityEvent) => void;
|
|
|
10
10
|
/**
|
|
11
11
|
* Executes an agent loop based on an {@link AgentDefinition}.
|
|
12
12
|
*
|
|
13
|
-
* This executor
|
|
14
|
-
*
|
|
15
|
-
* gathered all necessary information to fulfill its goal.
|
|
16
|
-
* 2. **Extraction Phase:** A final prompt is sent to the model to summarize
|
|
17
|
-
* the work and extract the final result in the desired format.
|
|
13
|
+
* This executor runs the agent in a loop, calling tools until it calls the
|
|
14
|
+
* mandatory `complete_task` tool to signal completion.
|
|
18
15
|
*/
|
|
19
16
|
export declare class AgentExecutor {
|
|
20
17
|
readonly definition: AgentDefinition;
|
|
@@ -60,7 +57,7 @@ export declare class AgentExecutor {
|
|
|
60
57
|
/**
|
|
61
58
|
* Executes function calls requested by the model and returns the results.
|
|
62
59
|
*
|
|
63
|
-
* @returns A new `Content` object
|
|
60
|
+
* @returns A new `Content` object for history, any submitted output, and completion status.
|
|
64
61
|
*/
|
|
65
62
|
private processFunctionCalls;
|
|
66
63
|
/**
|
|
@@ -69,8 +66,6 @@ export declare class AgentExecutor {
|
|
|
69
66
|
private prepareToolsList;
|
|
70
67
|
/** Builds the system prompt from the agent definition and inputs. */
|
|
71
68
|
private buildSystemPrompt;
|
|
72
|
-
/** Builds the final message for the extraction phase. */
|
|
73
|
-
private buildExtractionMessage;
|
|
74
69
|
/**
|
|
75
70
|
* Applies template strings to initial messages.
|
|
76
71
|
*
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { reportError } from '../utils/errorReporting.js';
|
|
7
7
|
import { GeminiChat, StreamEventType } from '../core/geminiChat.js';
|
|
8
|
+
import { Type } from '@google/genai';
|
|
8
9
|
import { executeToolCall } from '../core/nonInteractiveToolExecutor.js';
|
|
9
10
|
import { ToolRegistry } from '../tools/tool-registry.js';
|
|
10
11
|
import { getDirectoryContextString } from '../utils/environmentContext.js';
|
|
@@ -19,14 +20,12 @@ import { WebSearchTool } from '../tools/web-search.js';
|
|
|
19
20
|
import { AgentTerminateMode } from './types.js';
|
|
20
21
|
import { templateString } from './utils.js';
|
|
21
22
|
import { parseThought } from '../utils/thoughtUtils.js';
|
|
23
|
+
const TASK_COMPLETE_TOOL_NAME = 'complete_task';
|
|
22
24
|
/**
|
|
23
25
|
* Executes an agent loop based on an {@link AgentDefinition}.
|
|
24
26
|
*
|
|
25
|
-
* This executor
|
|
26
|
-
*
|
|
27
|
-
* gathered all necessary information to fulfill its goal.
|
|
28
|
-
* 2. **Extraction Phase:** A final prompt is sent to the model to summarize
|
|
29
|
-
* the work and extract the final result in the desired format.
|
|
27
|
+
* This executor runs the agent in a loop, calling tools until it calls the
|
|
28
|
+
* mandatory `complete_task` tool to signal completion.
|
|
30
29
|
*/
|
|
31
30
|
export class AgentExecutor {
|
|
32
31
|
definition;
|
|
@@ -100,15 +99,12 @@ export class AgentExecutor {
|
|
|
100
99
|
try {
|
|
101
100
|
const chat = await this.createChatObject(inputs);
|
|
102
101
|
const tools = this.prepareToolsList();
|
|
103
|
-
let terminateReason = AgentTerminateMode.
|
|
104
|
-
|
|
105
|
-
// The agent works in a loop until it stops calling tools.
|
|
102
|
+
let terminateReason = AgentTerminateMode.ERROR;
|
|
103
|
+
let finalResult = null;
|
|
106
104
|
const query = this.definition.promptConfig.query
|
|
107
105
|
? templateString(this.definition.promptConfig.query, inputs)
|
|
108
106
|
: 'Get Started!';
|
|
109
|
-
let
|
|
110
|
-
{ role: 'user', parts: [{ text: query }] },
|
|
111
|
-
];
|
|
107
|
+
let currentMessage = { role: 'user', parts: [{ text: query }] };
|
|
112
108
|
while (true) {
|
|
113
109
|
// Check for termination conditions like max turns or timeout.
|
|
114
110
|
const reason = this.checkTermination(startTime, turnCounter);
|
|
@@ -122,36 +118,37 @@ export class AgentExecutor {
|
|
|
122
118
|
}
|
|
123
119
|
// Call model
|
|
124
120
|
const promptId = `${this.runtimeContext.getSessionId()}#${this.agentId}#${turnCounter++}`;
|
|
125
|
-
const { functionCalls } = await this.callModel(chat,
|
|
121
|
+
const { functionCalls } = await this.callModel(chat, currentMessage, tools, signal, promptId);
|
|
126
122
|
if (signal.aborted) {
|
|
127
123
|
terminateReason = AgentTerminateMode.ABORTED;
|
|
128
124
|
break;
|
|
129
125
|
}
|
|
130
|
-
// If the model stops calling tools
|
|
126
|
+
// If the model stops calling tools without calling complete_task, it's an error.
|
|
131
127
|
if (functionCalls.length === 0) {
|
|
128
|
+
terminateReason = AgentTerminateMode.ERROR;
|
|
129
|
+
finalResult = `Agent stopped calling tools but did not call '${TASK_COMPLETE_TOOL_NAME}' to finalize the session.`;
|
|
130
|
+
this.emitActivity('ERROR', {
|
|
131
|
+
error: finalResult,
|
|
132
|
+
context: 'protocol_violation',
|
|
133
|
+
});
|
|
132
134
|
break;
|
|
133
135
|
}
|
|
134
|
-
|
|
136
|
+
const { nextMessage, submittedOutput, taskCompleted } = await this.processFunctionCalls(functionCalls, signal, promptId);
|
|
137
|
+
if (taskCompleted) {
|
|
138
|
+
finalResult = submittedOutput ?? 'Task completed successfully.';
|
|
139
|
+
terminateReason = AgentTerminateMode.GOAL;
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
currentMessage = nextMessage;
|
|
135
143
|
}
|
|
136
|
-
|
|
137
|
-
if (terminateReason !== AgentTerminateMode.GOAL) {
|
|
144
|
+
if (terminateReason === AgentTerminateMode.GOAL) {
|
|
138
145
|
return {
|
|
139
|
-
result:
|
|
146
|
+
result: finalResult || 'Task completed.',
|
|
140
147
|
terminate_reason: terminateReason,
|
|
141
148
|
};
|
|
142
149
|
}
|
|
143
|
-
// Phase 2: Extraction Phase
|
|
144
|
-
// A final message is sent to summarize findings and produce the output.
|
|
145
|
-
const extractionMessage = this.buildExtractionMessage();
|
|
146
|
-
const extractionMessages = [
|
|
147
|
-
{ role: 'user', parts: [{ text: extractionMessage }] },
|
|
148
|
-
];
|
|
149
|
-
const extractionPromptId = `${this.runtimeContext.getSessionId()}#${this.agentId}#extraction`;
|
|
150
|
-
// TODO: Consider if we should keep tools to avoid cache reset.
|
|
151
|
-
const { textResponse } = await this.callModel(chat, extractionMessages, [], // No tools are available in the extraction phase.
|
|
152
|
-
signal, extractionPromptId);
|
|
153
150
|
return {
|
|
154
|
-
result:
|
|
151
|
+
result: finalResult || 'Agent execution was terminated before completion.',
|
|
155
152
|
terminate_reason: terminateReason,
|
|
156
153
|
};
|
|
157
154
|
}
|
|
@@ -165,9 +162,9 @@ export class AgentExecutor {
|
|
|
165
162
|
*
|
|
166
163
|
* @returns The model's response, including any tool calls or text.
|
|
167
164
|
*/
|
|
168
|
-
async callModel(chat,
|
|
165
|
+
async callModel(chat, message, tools, signal, promptId) {
|
|
169
166
|
const messageParams = {
|
|
170
|
-
message:
|
|
167
|
+
message: message.parts || [],
|
|
171
168
|
config: {
|
|
172
169
|
abortSignal: signal,
|
|
173
170
|
tools: tools.length > 0 ? [{ functionDeclarations: tools }] : undefined,
|
|
@@ -237,68 +234,171 @@ export class AgentExecutor {
|
|
|
237
234
|
/**
|
|
238
235
|
* Executes function calls requested by the model and returns the results.
|
|
239
236
|
*
|
|
240
|
-
* @returns A new `Content` object
|
|
237
|
+
* @returns A new `Content` object for history, any submitted output, and completion status.
|
|
241
238
|
*/
|
|
242
239
|
async processFunctionCalls(functionCalls, signal, promptId) {
|
|
243
240
|
const allowedToolNames = new Set(this.toolRegistry.getAllToolNames());
|
|
244
|
-
//
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const toolPromises = validatedFunctionCalls.map(async (functionCall, index) => {
|
|
241
|
+
// Always allow the completion tool
|
|
242
|
+
allowedToolNames.add(TASK_COMPLETE_TOOL_NAME);
|
|
243
|
+
let submittedOutput = null;
|
|
244
|
+
let taskCompleted = false;
|
|
245
|
+
// We'll collect promises for the tool executions
|
|
246
|
+
const toolExecutionPromises = [];
|
|
247
|
+
// And we'll need a place to store the synchronous results (like complete_task or blocked calls)
|
|
248
|
+
const syncResponseParts = [];
|
|
249
|
+
for (const [index, functionCall] of functionCalls.entries()) {
|
|
254
250
|
const callId = functionCall.id ?? `${promptId}-${index}`;
|
|
255
|
-
const args = functionCall.args ?? {};
|
|
251
|
+
const args = (functionCall.args ?? {});
|
|
256
252
|
this.emitActivity('TOOL_CALL_START', {
|
|
257
253
|
name: functionCall.name,
|
|
258
254
|
args,
|
|
259
255
|
});
|
|
256
|
+
if (functionCall.name === TASK_COMPLETE_TOOL_NAME) {
|
|
257
|
+
if (taskCompleted) {
|
|
258
|
+
// We already have a completion from this turn. Ignore subsequent ones.
|
|
259
|
+
const error = 'Task already marked complete in this turn. Ignoring duplicate call.';
|
|
260
|
+
syncResponseParts.push({
|
|
261
|
+
functionResponse: {
|
|
262
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
263
|
+
response: { error },
|
|
264
|
+
id: callId,
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
this.emitActivity('ERROR', {
|
|
268
|
+
context: 'tool_call',
|
|
269
|
+
name: functionCall.name,
|
|
270
|
+
error,
|
|
271
|
+
});
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
const { outputConfig } = this.definition;
|
|
275
|
+
taskCompleted = true; // Signal completion regardless of output presence
|
|
276
|
+
if (outputConfig) {
|
|
277
|
+
const outputName = outputConfig.outputName;
|
|
278
|
+
if (args[outputName] !== undefined) {
|
|
279
|
+
submittedOutput = String(args[outputName]);
|
|
280
|
+
syncResponseParts.push({
|
|
281
|
+
functionResponse: {
|
|
282
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
283
|
+
response: { result: 'Output submitted and task completed.' },
|
|
284
|
+
id: callId,
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
this.emitActivity('TOOL_CALL_END', {
|
|
288
|
+
name: functionCall.name,
|
|
289
|
+
output: 'Output submitted and task completed.',
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
// Failed to provide required output.
|
|
294
|
+
taskCompleted = false; // Revoke completion status
|
|
295
|
+
const error = `Missing required argument '${outputName}' for completion.`;
|
|
296
|
+
syncResponseParts.push({
|
|
297
|
+
functionResponse: {
|
|
298
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
299
|
+
response: { error },
|
|
300
|
+
id: callId,
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
this.emitActivity('ERROR', {
|
|
304
|
+
context: 'tool_call',
|
|
305
|
+
name: functionCall.name,
|
|
306
|
+
error,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
// No output expected. Just signal completion.
|
|
312
|
+
submittedOutput = 'Task completed successfully.';
|
|
313
|
+
syncResponseParts.push({
|
|
314
|
+
functionResponse: {
|
|
315
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
316
|
+
response: { status: 'Task marked complete.' },
|
|
317
|
+
id: callId,
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
this.emitActivity('TOOL_CALL_END', {
|
|
321
|
+
name: functionCall.name,
|
|
322
|
+
output: 'Task marked complete.',
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
// Handle standard tools
|
|
328
|
+
if (!allowedToolNames.has(functionCall.name)) {
|
|
329
|
+
const error = `Unauthorized tool call: '${functionCall.name}' is not available to this agent.`;
|
|
330
|
+
console.warn(`[AgentExecutor] Blocked call: ${error}`);
|
|
331
|
+
syncResponseParts.push({
|
|
332
|
+
functionResponse: {
|
|
333
|
+
name: functionCall.name,
|
|
334
|
+
id: callId,
|
|
335
|
+
response: { error },
|
|
336
|
+
},
|
|
337
|
+
});
|
|
338
|
+
this.emitActivity('ERROR', {
|
|
339
|
+
context: 'tool_call_unauthorized',
|
|
340
|
+
name: functionCall.name,
|
|
341
|
+
callId,
|
|
342
|
+
error,
|
|
343
|
+
});
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
260
346
|
const requestInfo = {
|
|
261
347
|
callId,
|
|
262
348
|
name: functionCall.name,
|
|
263
|
-
args
|
|
349
|
+
args,
|
|
264
350
|
isClientInitiated: true,
|
|
265
351
|
prompt_id: promptId,
|
|
266
352
|
};
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
this.
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
353
|
+
// Create a promise for the tool execution
|
|
354
|
+
const executionPromise = (async () => {
|
|
355
|
+
const toolResponse = await executeToolCall(this.runtimeContext, requestInfo, signal);
|
|
356
|
+
if (toolResponse.error) {
|
|
357
|
+
this.emitActivity('ERROR', {
|
|
358
|
+
context: 'tool_call',
|
|
359
|
+
name: functionCall.name,
|
|
360
|
+
error: toolResponse.error.message,
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
this.emitActivity('TOOL_CALL_END', {
|
|
365
|
+
name: functionCall.name,
|
|
366
|
+
output: toolResponse.resultDisplay,
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
return toolResponse.responseParts;
|
|
370
|
+
})();
|
|
371
|
+
toolExecutionPromises.push(executionPromise);
|
|
372
|
+
}
|
|
373
|
+
// Wait for all tool executions to complete
|
|
374
|
+
const asyncResults = await Promise.all(toolExecutionPromises);
|
|
375
|
+
// Combine all response parts
|
|
376
|
+
const toolResponseParts = [...syncResponseParts];
|
|
377
|
+
for (const result of asyncResults) {
|
|
378
|
+
if (result) {
|
|
379
|
+
toolResponseParts.push(...result);
|
|
280
380
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
.filter((part) => part !== undefined);
|
|
287
|
-
// If all authorized tool calls failed, provide a generic error message
|
|
288
|
-
// to the model so it can try a different approach.
|
|
289
|
-
if (functionCalls.length > 0 && toolResponseParts.length === 0) {
|
|
381
|
+
}
|
|
382
|
+
// If all authorized tool calls failed (and task isn't complete), provide a generic error.
|
|
383
|
+
if (functionCalls.length > 0 &&
|
|
384
|
+
toolResponseParts.length === 0 &&
|
|
385
|
+
!taskCompleted) {
|
|
290
386
|
toolResponseParts.push({
|
|
291
|
-
text: 'All tool calls failed. Please analyze the errors and try an alternative approach.',
|
|
387
|
+
text: 'All tool calls failed or were unauthorized. Please analyze the errors and try an alternative approach.',
|
|
292
388
|
});
|
|
293
389
|
}
|
|
294
|
-
return
|
|
390
|
+
return {
|
|
391
|
+
nextMessage: { role: 'user', parts: toolResponseParts },
|
|
392
|
+
submittedOutput,
|
|
393
|
+
taskCompleted,
|
|
394
|
+
};
|
|
295
395
|
}
|
|
296
396
|
/**
|
|
297
397
|
* Prepares the list of tool function declarations to be sent to the model.
|
|
298
398
|
*/
|
|
299
399
|
prepareToolsList() {
|
|
300
400
|
const toolsList = [];
|
|
301
|
-
const { toolConfig } = this.definition;
|
|
401
|
+
const { toolConfig, outputConfig } = this.definition;
|
|
302
402
|
if (toolConfig) {
|
|
303
403
|
const toolNamesToLoad = [];
|
|
304
404
|
for (const toolRef of toolConfig.tools) {
|
|
@@ -317,11 +417,32 @@ export class AgentExecutor {
|
|
|
317
417
|
// Add schemas from tools that were registered by name.
|
|
318
418
|
toolsList.push(...this.toolRegistry.getFunctionDeclarationsFiltered(toolNamesToLoad));
|
|
319
419
|
}
|
|
420
|
+
// Always inject complete_task.
|
|
421
|
+
// Configure its schema based on whether output is expected.
|
|
422
|
+
const completeTool = {
|
|
423
|
+
name: TASK_COMPLETE_TOOL_NAME,
|
|
424
|
+
description: outputConfig
|
|
425
|
+
? 'Call this tool to submit your final answer and complete the task. This is the ONLY way to finish.'
|
|
426
|
+
: 'Call this tool to signal that you have completed your task. This is the ONLY way to finish.',
|
|
427
|
+
parameters: {
|
|
428
|
+
type: Type.OBJECT,
|
|
429
|
+
properties: {},
|
|
430
|
+
required: [],
|
|
431
|
+
},
|
|
432
|
+
};
|
|
433
|
+
if (outputConfig) {
|
|
434
|
+
completeTool.parameters.properties[outputConfig.outputName] = {
|
|
435
|
+
description: outputConfig.description,
|
|
436
|
+
...(outputConfig.schema ?? { type: Type.STRING }),
|
|
437
|
+
};
|
|
438
|
+
completeTool.parameters.required.push(outputConfig.outputName);
|
|
439
|
+
}
|
|
440
|
+
toolsList.push(completeTool);
|
|
320
441
|
return toolsList;
|
|
321
442
|
}
|
|
322
443
|
/** Builds the system prompt from the agent definition and inputs. */
|
|
323
444
|
async buildSystemPrompt(inputs) {
|
|
324
|
-
const { promptConfig
|
|
445
|
+
const { promptConfig } = this.definition;
|
|
325
446
|
if (!promptConfig.systemPrompt) {
|
|
326
447
|
return '';
|
|
327
448
|
}
|
|
@@ -330,38 +451,18 @@ export class AgentExecutor {
|
|
|
330
451
|
// Append environment context (CWD and folder structure).
|
|
331
452
|
const dirContext = await getDirectoryContextString(this.runtimeContext);
|
|
332
453
|
finalPrompt += `\n\n# Environment Context\n${dirContext}`;
|
|
333
|
-
// Append completion criteria to guide the model's output.
|
|
334
|
-
if (outputConfig?.completion_criteria) {
|
|
335
|
-
finalPrompt += '\n\nEnsure you complete the following:\n';
|
|
336
|
-
for (const criteria of outputConfig.completion_criteria) {
|
|
337
|
-
finalPrompt += `- ${criteria}\n`;
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
454
|
// Append standard rules for non-interactive execution.
|
|
341
455
|
finalPrompt += `
|
|
342
456
|
Important Rules:
|
|
343
457
|
* You are running in a non-interactive mode. You CANNOT ask the user for input or clarification.
|
|
344
458
|
* Work systematically using available tools to complete your task.
|
|
345
|
-
* Always use absolute paths for file operations. Construct them using the provided "Environment Context"
|
|
346
|
-
|
|
459
|
+
* Always use absolute paths for file operations. Construct them using the provided "Environment Context".`;
|
|
460
|
+
finalPrompt += `
|
|
461
|
+
* When you have completed your task, you MUST call the \`${TASK_COMPLETE_TOOL_NAME}\` tool.
|
|
462
|
+
* Do not call any other tools in the same turn as \`${TASK_COMPLETE_TOOL_NAME}\`.
|
|
463
|
+
* This is the ONLY way to complete your mission. If you stop calling tools without calling this, you have failed.`;
|
|
347
464
|
return finalPrompt;
|
|
348
465
|
}
|
|
349
|
-
/** Builds the final message for the extraction phase. */
|
|
350
|
-
buildExtractionMessage() {
|
|
351
|
-
const { outputConfig } = this.definition;
|
|
352
|
-
if (outputConfig?.description) {
|
|
353
|
-
let message = `Based on your work so far, provide: ${outputConfig.description}`;
|
|
354
|
-
if (outputConfig.completion_criteria?.length) {
|
|
355
|
-
message += `\n\nBe sure you have addressed:\n`;
|
|
356
|
-
for (const criteria of outputConfig.completion_criteria) {
|
|
357
|
-
message += `- ${criteria}\n`;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
return message;
|
|
361
|
-
}
|
|
362
|
-
// Fallback to a generic extraction message if no description is provided.
|
|
363
|
-
return 'Based on your work so far, provide a comprehensive summary of your analysis and findings. Do not perform any more function calls.';
|
|
364
|
-
}
|
|
365
466
|
/**
|
|
366
467
|
* Applies template strings to initial messages.
|
|
367
468
|
*
|