@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.
Files changed (32) hide show
  1. package/README.md +43 -54
  2. package/dist/src/agents/codebase-investigator.js +12 -6
  3. package/dist/src/agents/codebase-investigator.js.map +1 -1
  4. package/dist/src/agents/executor.d.ts +3 -8
  5. package/dist/src/agents/executor.js +195 -94
  6. package/dist/src/agents/executor.js.map +1 -1
  7. package/dist/src/agents/executor.test.js +331 -287
  8. package/dist/src/agents/executor.test.js.map +1 -1
  9. package/dist/src/agents/types.d.ts +16 -4
  10. package/dist/src/generated/git-commit.d.ts +2 -2
  11. package/dist/src/generated/git-commit.js +2 -2
  12. package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +4 -2
  13. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +11 -0
  14. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
  15. package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +3 -2
  16. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +7 -2
  17. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
  18. package/dist/src/telemetry/constants.d.ts +2 -0
  19. package/dist/src/telemetry/constants.js +2 -0
  20. package/dist/src/telemetry/constants.js.map +1 -1
  21. package/dist/src/telemetry/loggers.d.ts +2 -1
  22. package/dist/src/telemetry/loggers.js +18 -2
  23. package/dist/src/telemetry/loggers.js.map +1 -1
  24. package/dist/src/telemetry/loggers.test.js +2 -2
  25. package/dist/src/telemetry/loggers.test.js.map +1 -1
  26. package/dist/src/telemetry/types.d.ts +6 -0
  27. package/dist/src/telemetry/types.js +10 -0
  28. package/dist/src/telemetry/types.js.map +1 -1
  29. package/dist/src/tools/smart-edit.js +7 -1
  30. package/dist/src/tools/smart-edit.js.map +1 -1
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. 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** with 60 requests/min and 1,000 requests/day
123
- - **Gemini 2.5 Pro and Flash** with 1M token context window
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 our latest models
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/cli/authentication.md).
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/cli/index.md) - Get up and running quickly
237
- - [**Authentication Setup**](./docs/cli/authentication.md) - Detailed auth configuration
238
- - [**Configuration Guide**](./docs/cli/configuration.md) - Settings and customization
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`, `/mcp`, etc.)
244
- - [**Checkpointing**](./docs/checkpointing.md) - Save and resume conversations
245
- - [**Memory Management**](./docs/tools/memory.md) - Using GEMINI.md context files
246
- - [**Token Caching**](./docs/cli/token-caching.md) - Optimize token usage
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
- - [Multi-file Operations](./docs/tools/multi-file.md)
255
- - [**MCP Server Integration**](./docs/tools/mcp-server.md) - Extend with custom tools
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
- - [**Architecture Overview**](./docs/architecture.md) - How Gemini CLI works
261
- - [**IDE Integration**](./docs/ide-integration.md) - VS Code companion
262
- - [**Sandboxing & Security**](./docs/sandbox.md) - Safe execution environments
263
- - [**Enterprise Deployment**](./docs/deployment.md) - Docker, system-wide config
264
- - [**Telemetry & Monitoring**](./docs/telemetry.md) - Usage tracking
265
- - [**Tools API Development**](./docs/core/tools-api.md) - Create custom tools
266
-
267
- ### Configuration & Customization
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/troubleshooting.md#frequently-asked-questions) - Quick answers
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/) for planned features and priorities.
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/Uninstall.md) for removal instructions.
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
- 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
+ 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
- completion_criteria: [
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;AAE3D,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,WAAW,EAAE;;;;EAIf,wBAAwB;CACzB;QACG,mBAAmB,EAAE;YACnB,2DAA2D;YAC3D,sGAAsG;YACtG,mIAAmI;SACpI;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"}
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 uses a simplified two-phase approach:
14
- * 1. **Work Phase:** The agent runs in a loop, calling tools until it has
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 to be added to the chat history.
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 uses a simplified two-phase approach:
26
- * 1. **Work Phase:** The agent runs in a loop, calling tools until it has
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.GOAL;
104
- // Phase 1: Work Phase
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 currentMessages = [
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, currentMessages, tools, signal, promptId);
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, the work phase is complete.
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
- currentMessages = await this.processFunctionCalls(functionCalls, signal, promptId);
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
- // If the work phase was terminated early, skip extraction and return.
137
- if (terminateReason !== AgentTerminateMode.GOAL) {
144
+ if (terminateReason === AgentTerminateMode.GOAL) {
138
145
  return {
139
- result: 'Agent execution was terminated before completion.',
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: textResponse || 'No response generated',
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, messages, tools, signal, promptId) {
165
+ async callModel(chat, message, tools, signal, promptId) {
169
166
  const messageParams = {
170
- message: messages[0]?.parts || [],
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 to be added to the chat history.
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
- // Filter out any tool calls that are not in the agent's allowed list.
245
- const validatedFunctionCalls = functionCalls.filter((call) => {
246
- if (!allowedToolNames.has(call.name)) {
247
- console.warn(`[AgentExecutor] Agent '${this.definition.name}' attempted to call ` +
248
- `unauthorized tool '${call.name}'. This call has been blocked.`);
249
- return false;
250
- }
251
- return true;
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: args,
349
+ args,
264
350
  isClientInitiated: true,
265
351
  prompt_id: promptId,
266
352
  };
267
- const toolResponse = await executeToolCall(this.runtimeContext, requestInfo, signal);
268
- if (toolResponse.error) {
269
- this.emitActivity('ERROR', {
270
- context: 'tool_call',
271
- name: functionCall.name,
272
- error: toolResponse.error.message,
273
- });
274
- }
275
- else {
276
- this.emitActivity('TOOL_CALL_END', {
277
- name: functionCall.name,
278
- output: toolResponse.resultDisplay,
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
- return toolResponse;
282
- });
283
- const toolResponses = await Promise.all(toolPromises);
284
- const toolResponseParts = toolResponses
285
- .flatMap((response) => response.responseParts)
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 [{ role: 'user', parts: toolResponseParts }];
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, outputConfig } = this.definition;
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
- * When you have completed your analysis and are ready to produce the final answer, stop calling tools.`;
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
  *