mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-06-13 12:57:12 -07:00
Merge branch 'main' into fix/headless-log
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Preview release: v0.34.0-preview.2
|
||||
# Preview release: v0.34.0-preview.3
|
||||
|
||||
Released: March 12, 2026
|
||||
Released: March 13, 2026
|
||||
|
||||
Our preview release includes the latest, new, and experimental features. This
|
||||
release may not be as stable as our [latest weekly release](latest.md).
|
||||
@@ -28,6 +28,10 @@ npm install -g @google/gemini-cli@preview
|
||||
|
||||
## What's Changed
|
||||
|
||||
- fix(patch): cherry-pick 24adacd to release/v0.34.0-preview.2-pr-22332 to patch
|
||||
version v0.34.0-preview.2 and create version 0.34.0-preview.3 by
|
||||
@gemini-cli-robot in
|
||||
[#22391](https://github.com/google-gemini/gemini-cli/pull/22391)
|
||||
- fix(patch): cherry-pick 8432bce to release/v0.34.0-preview.1-pr-22069 to patch
|
||||
version v0.34.0-preview.1 and create version 0.34.0-preview.2 by
|
||||
@gemini-cli-robot in
|
||||
@@ -472,4 +476,4 @@ npm install -g @google/gemini-cli@preview
|
||||
[#21938](https://github.com/google-gemini/gemini-cli/pull/21938)
|
||||
|
||||
**Full Changelog**:
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.33.0-preview.15...v0.34.0-preview.2
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.33.0-preview.15...v0.34.0-preview.3
|
||||
|
||||
@@ -120,7 +120,8 @@ These are the only allowed tools:
|
||||
[`list_directory`](../tools/file-system.md#1-list_directory-readfolder),
|
||||
[`glob`](../tools/file-system.md#4-glob-findfiles)
|
||||
- **Search:** [`grep_search`](../tools/file-system.md#5-grep_search-searchtext),
|
||||
[`google_web_search`](../tools/web-search.md)
|
||||
[`google_web_search`](../tools/web-search.md),
|
||||
[`get_internal_docs`](../tools/internal-docs.md)
|
||||
- **Research Subagents:**
|
||||
[`codebase_investigator`](../core/subagents.md#codebase-investigator),
|
||||
[`cli_help`](../core/subagents.md#cli-help-agent)
|
||||
|
||||
@@ -1190,7 +1190,7 @@ their corresponding top-level category object in your `settings.json` file.
|
||||
|
||||
- **`experimental.jitContext`** (boolean):
|
||||
- **Description:** Enable Just-In-Time (JIT) context loading.
|
||||
- **Default:** `false`
|
||||
- **Default:** `true`
|
||||
- **Requires restart:** Yes
|
||||
|
||||
- **`experimental.useOSC52Paste`** (boolean):
|
||||
|
||||
Generated
+1
-25
@@ -2195,7 +2195,6 @@
|
||||
"integrity": "sha512-t54CUOsFMappY1Jbzb7fetWeO0n6K0k/4+/ZpkS+3Joz8I4VcvY9OiEBFRYISqaI2fq5sCiPtAjRDOzVYG8m+Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": "^6.0.0",
|
||||
"@octokit/graphql": "^9.0.2",
|
||||
@@ -2376,7 +2375,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
|
||||
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
@@ -2426,7 +2424,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.5.0.tgz",
|
||||
"integrity": "sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/semantic-conventions": "^1.29.0"
|
||||
},
|
||||
@@ -2801,7 +2798,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.5.0.tgz",
|
||||
"integrity": "sha512-F8W52ApePshpoSrfsSk1H2yJn9aKjCrbpQF1M9Qii0GHzbfVeFUB+rc3X4aggyZD8x9Gu3Slua+s6krmq6Dt8g==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "2.5.0",
|
||||
"@opentelemetry/semantic-conventions": "^1.29.0"
|
||||
@@ -2835,7 +2831,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.5.0.tgz",
|
||||
"integrity": "sha512-BeJLtU+f5Gf905cJX9vXFQorAr6TAfK3SPvTFqP+scfIpDQEJfRaGJWta7sJgP+m4dNtBf9y3yvBKVAZZtJQVA==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "2.5.0",
|
||||
"@opentelemetry/resources": "2.5.0"
|
||||
@@ -2890,7 +2885,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.5.0.tgz",
|
||||
"integrity": "sha512-VzRf8LzotASEyNDUxTdaJ9IRJ1/h692WyArDBInf5puLCjxbICD6XkHgpuudis56EndyS7LYFmtTMny6UABNdQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "2.5.0",
|
||||
"@opentelemetry/resources": "2.5.0",
|
||||
@@ -4127,7 +4121,6 @@
|
||||
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
@@ -4402,7 +4395,6 @@
|
||||
"integrity": "sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.35.0",
|
||||
"@typescript-eslint/types": "8.35.0",
|
||||
@@ -5276,7 +5268,6 @@
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -7995,7 +7986,6 @@
|
||||
"integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -8513,7 +8503,6 @@
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
|
||||
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"accepts": "^2.0.0",
|
||||
"body-parser": "^2.2.1",
|
||||
@@ -9826,7 +9815,6 @@
|
||||
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.7.tgz",
|
||||
"integrity": "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
}
|
||||
@@ -10105,7 +10093,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@jrichman/ink/-/ink-6.4.11.tgz",
|
||||
"integrity": "sha512-93LQlzT7vvZ1XJcmOMwN4s+6W334QegendeHOMnEJBlhnpIzr8bws6/aOEHG8ZCuVD/vNeeea5m1msHIdAY6ig==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@alcalzone/ansi-tokenize": "^0.2.1",
|
||||
"ansi-escapes": "^7.0.0",
|
||||
@@ -13863,7 +13850,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
|
||||
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -13874,7 +13860,6 @@
|
||||
"integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"shell-quote": "^1.6.1",
|
||||
"ws": "^7"
|
||||
@@ -16024,7 +16009,6 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -16248,8 +16232,7 @@
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"dev": true,
|
||||
"license": "0BSD",
|
||||
"peer": true
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tsx": {
|
||||
"version": "4.20.3",
|
||||
@@ -16257,7 +16240,6 @@
|
||||
"integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "~0.25.0",
|
||||
"get-tsconfig": "^4.7.5"
|
||||
@@ -16423,7 +16405,6 @@
|
||||
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -16646,7 +16627,6 @@
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz",
|
||||
"integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.5.0",
|
||||
@@ -16760,7 +16740,6 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -16773,7 +16752,6 @@
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz",
|
||||
"integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/chai": "^5.2.2",
|
||||
"@vitest/expect": "3.2.4",
|
||||
@@ -17421,7 +17399,6 @@
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
@@ -17968,7 +17945,6 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
||||
@@ -177,10 +177,13 @@ describe('a2a-server memory commands', () => {
|
||||
expect.any(AbortSignal),
|
||||
undefined,
|
||||
{
|
||||
sanitizationConfig: {
|
||||
allowedEnvironmentVariables: [],
|
||||
blockedEnvironmentVariables: [],
|
||||
enableEnvironmentVariableRedaction: false,
|
||||
shellExecutionConfig: {
|
||||
sanitizationConfig: {
|
||||
allowedEnvironmentVariables: [],
|
||||
blockedEnvironmentVariables: [],
|
||||
enableEnvironmentVariableRedaction: false,
|
||||
},
|
||||
sandboxManager: undefined,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
@@ -103,8 +103,10 @@ export class AddMemoryCommand implements Command {
|
||||
const abortController = new AbortController();
|
||||
const signal = abortController.signal;
|
||||
await tool.buildAndExecute(result.toolArgs, signal, undefined, {
|
||||
sanitizationConfig: DEFAULT_SANITIZATION_CONFIG,
|
||||
sandboxManager: loopContext.sandboxManager,
|
||||
shellExecutionConfig: {
|
||||
sanitizationConfig: DEFAULT_SANITIZATION_CONFIG,
|
||||
sandboxManager: loopContext.sandboxManager,
|
||||
},
|
||||
});
|
||||
await refreshMemory(context.config);
|
||||
return {
|
||||
|
||||
@@ -104,8 +104,10 @@ export class AddMemoryCommand implements Command {
|
||||
await context.sendMessage(`Saving memory via ${result.toolName}...`);
|
||||
|
||||
await tool.buildAndExecute(result.toolArgs, signal, undefined, {
|
||||
sanitizationConfig: DEFAULT_SANITIZATION_CONFIG,
|
||||
sandboxManager: context.config.sandboxManager,
|
||||
shellExecutionConfig: {
|
||||
sanitizationConfig: DEFAULT_SANITIZATION_CONFIG,
|
||||
sandboxManager: context.config.sandboxManager,
|
||||
},
|
||||
});
|
||||
await refreshMemory(context.config);
|
||||
return {
|
||||
|
||||
@@ -814,7 +814,9 @@ describe('Hierarchical Memory Loading (config.ts) - Placeholder Suite', () => {
|
||||
|
||||
it('should pass extension context file paths to loadServerHierarchicalMemory', async () => {
|
||||
process.argv = ['node', 'script.js'];
|
||||
const settings = createTestMergedSettings();
|
||||
const settings = createTestMergedSettings({
|
||||
experimental: { jitContext: false },
|
||||
});
|
||||
vi.spyOn(ExtensionManager.prototype, 'getExtensions').mockReturnValue([
|
||||
{
|
||||
path: '/path/to/ext1',
|
||||
@@ -865,6 +867,7 @@ describe('Hierarchical Memory Loading (config.ts) - Placeholder Suite', () => {
|
||||
process.argv = ['node', 'script.js'];
|
||||
const includeDir = path.resolve(path.sep, 'path', 'to', 'include');
|
||||
const settings = createTestMergedSettings({
|
||||
experimental: { jitContext: false },
|
||||
context: {
|
||||
includeDirectories: [includeDir],
|
||||
loadMemoryFromIncludeDirectories: true,
|
||||
@@ -892,6 +895,7 @@ describe('Hierarchical Memory Loading (config.ts) - Placeholder Suite', () => {
|
||||
it('should NOT pass includeDirectories to loadServerHierarchicalMemory when loadMemoryFromIncludeDirectories is false', async () => {
|
||||
process.argv = ['node', 'script.js'];
|
||||
const settings = createTestMergedSettings({
|
||||
experimental: { jitContext: false },
|
||||
context: {
|
||||
includeDirectories: ['/path/to/include'],
|
||||
loadMemoryFromIncludeDirectories: false,
|
||||
|
||||
@@ -494,7 +494,7 @@ export async function loadCliConfig(
|
||||
.getExtensions()
|
||||
.find((ext) => ext.isActive && ext.plan?.directory)?.plan;
|
||||
|
||||
const experimentalJitContext = settings.experimental?.jitContext ?? false;
|
||||
const experimentalJitContext = settings.experimental.jitContext;
|
||||
|
||||
let extensionRegistryURI =
|
||||
process.env['GEMINI_CLI_EXTENSION_REGISTRY_URI'] ??
|
||||
@@ -737,6 +737,8 @@ export async function loadCliConfig(
|
||||
includeDirectories,
|
||||
loadMemoryFromIncludeDirectories:
|
||||
settings.context?.loadMemoryFromIncludeDirectories || false,
|
||||
discoveryMaxDirs: settings.context?.discoveryMaxDirs,
|
||||
importFormat: settings.context?.importFormat,
|
||||
debugMode,
|
||||
question,
|
||||
|
||||
|
||||
@@ -346,6 +346,12 @@ describe('Policy Engine Integration Tests', () => {
|
||||
expect(
|
||||
(await engine.check({ name: 'list_directory' }, undefined)).decision,
|
||||
).toBe(PolicyDecision.ALLOW);
|
||||
expect(
|
||||
(await engine.check({ name: 'get_internal_docs' }, undefined)).decision,
|
||||
).toBe(PolicyDecision.ALLOW);
|
||||
expect(
|
||||
(await engine.check({ name: 'cli_help' }, undefined)).decision,
|
||||
).toBe(PolicyDecision.ALLOW);
|
||||
|
||||
// Other tools should be denied via catch all
|
||||
expect(
|
||||
|
||||
@@ -1894,7 +1894,7 @@ const SETTINGS_SCHEMA = {
|
||||
label: 'JIT Context Loading',
|
||||
category: 'Experimental',
|
||||
requiresRestart: true,
|
||||
default: false,
|
||||
default: true,
|
||||
description: 'Enable Just-In-Time (JIT) context loading.',
|
||||
showInDialog: false,
|
||||
},
|
||||
|
||||
@@ -22,6 +22,25 @@ describe('NewAgentsNotification', () => {
|
||||
{
|
||||
name: 'Agent B',
|
||||
description: 'Description B',
|
||||
kind: 'local' as const,
|
||||
inputConfig: { inputSchema: {} },
|
||||
promptConfig: {},
|
||||
modelConfig: {},
|
||||
runConfig: {},
|
||||
mcpServers: {
|
||||
github: {
|
||||
command: 'npx',
|
||||
args: ['-y', '@modelcontextprotocol/server-github'],
|
||||
},
|
||||
postgres: {
|
||||
command: 'npx',
|
||||
args: ['-y', '@modelcontextprotocol/server-postgres'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Agent C',
|
||||
description: 'Description C',
|
||||
kind: 'remote' as const,
|
||||
agentCardUrl: '',
|
||||
inputConfig: { inputSchema: {} },
|
||||
|
||||
@@ -80,16 +80,35 @@ export const NewAgentsNotification = ({
|
||||
borderStyle="single"
|
||||
padding={1}
|
||||
>
|
||||
{displayAgents.map((agent) => (
|
||||
<Box key={agent.name}>
|
||||
<Box flexShrink={0}>
|
||||
<Text bold color={theme.text.primary}>
|
||||
- {agent.name}:{' '}
|
||||
</Text>
|
||||
{displayAgents.map((agent) => {
|
||||
const mcpServers =
|
||||
agent.kind === 'local' ? agent.mcpServers : undefined;
|
||||
const hasMcpServers =
|
||||
mcpServers && Object.keys(mcpServers).length > 0;
|
||||
return (
|
||||
<Box key={agent.name} flexDirection="column">
|
||||
<Box>
|
||||
<Box flexShrink={0}>
|
||||
<Text bold color={theme.text.primary}>
|
||||
- {agent.name}:{' '}
|
||||
</Text>
|
||||
</Box>
|
||||
<Text color={theme.text.secondary}>
|
||||
{' '}
|
||||
{agent.description}
|
||||
</Text>
|
||||
</Box>
|
||||
{hasMcpServers && (
|
||||
<Box marginLeft={2}>
|
||||
<Text color={theme.text.secondary}>
|
||||
(Includes MCP servers:{' '}
|
||||
{Object.keys(mcpServers).join(', ')})
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
<Text color={theme.text.secondary}> {agent.description}</Text>
|
||||
</Box>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
{remaining > 0 && (
|
||||
<Text color={theme.text.secondary}>
|
||||
... and {remaining} more.
|
||||
|
||||
@@ -10,6 +10,8 @@ exports[`NewAgentsNotification > renders agent list 1`] = `
|
||||
│ │ │ │
|
||||
│ │ - Agent A: Description A │ │
|
||||
│ │ - Agent B: Description B │ │
|
||||
│ │ (Includes MCP servers: github, postgres) │ │
|
||||
│ │ - Agent C: Description C │ │
|
||||
│ │ │ │
|
||||
│ └────────────────────────────────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
|
||||
@@ -81,6 +81,33 @@ System prompt content.`);
|
||||
});
|
||||
});
|
||||
|
||||
it('should parse frontmatter with mcp_servers', async () => {
|
||||
const filePath = await writeAgentMarkdown(`---
|
||||
name: mcp-agent
|
||||
description: An agent with MCP servers
|
||||
mcp_servers:
|
||||
test-server:
|
||||
command: node
|
||||
args: [server.js]
|
||||
include_tools: [tool1, tool2]
|
||||
---
|
||||
System prompt content.`);
|
||||
|
||||
const result = await parseAgentMarkdown(filePath);
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toMatchObject({
|
||||
name: 'mcp-agent',
|
||||
description: 'An agent with MCP servers',
|
||||
mcp_servers: {
|
||||
'test-server': {
|
||||
command: 'node',
|
||||
args: ['server.js'],
|
||||
include_tools: ['tool1', 'tool2'],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw AgentLoadError if frontmatter is missing', async () => {
|
||||
const filePath = await writeAgentMarkdown(`Just some markdown content.`);
|
||||
await expect(parseAgentMarkdown(filePath)).rejects.toThrow(
|
||||
@@ -274,6 +301,33 @@ Body`);
|
||||
expect(result.modelConfig.model).toBe(GEMINI_MODEL_ALIAS_PRO);
|
||||
});
|
||||
|
||||
it('should convert mcp_servers in local agent', () => {
|
||||
const markdown = {
|
||||
kind: 'local' as const,
|
||||
name: 'mcp-agent',
|
||||
description: 'An agent with MCP servers',
|
||||
mcp_servers: {
|
||||
'test-server': {
|
||||
command: 'node',
|
||||
args: ['server.js'],
|
||||
include_tools: ['tool1'],
|
||||
},
|
||||
},
|
||||
system_prompt: 'prompt',
|
||||
};
|
||||
|
||||
const result = markdownToAgentDefinition(
|
||||
markdown,
|
||||
) as LocalAgentDefinition;
|
||||
expect(result.kind).toBe('local');
|
||||
expect(result.mcpServers).toBeDefined();
|
||||
expect(result.mcpServers!['test-server']).toMatchObject({
|
||||
command: 'node',
|
||||
args: ['server.js'],
|
||||
includeTools: ['tool1'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should pass through unknown model names (e.g. auto)', () => {
|
||||
const markdown = {
|
||||
kind: 'local' as const,
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
DEFAULT_MAX_TIME_MINUTES,
|
||||
} from './types.js';
|
||||
import type { A2AAuthConfig } from './auth-provider/types.js';
|
||||
import { MCPServerConfig } from '../config/config.js';
|
||||
import { isValidToolName } from '../tools/tool-names.js';
|
||||
import { FRONTMATTER_REGEX } from '../skills/skillLoader.js';
|
||||
import { getErrorMessage } from '../utils/errors.js';
|
||||
@@ -28,11 +29,29 @@ interface FrontmatterBaseAgentDefinition {
|
||||
display_name?: string;
|
||||
}
|
||||
|
||||
interface FrontmatterMCPServerConfig {
|
||||
command?: string;
|
||||
args?: string[];
|
||||
env?: Record<string, string>;
|
||||
cwd?: string;
|
||||
url?: string;
|
||||
http_url?: string;
|
||||
headers?: Record<string, string>;
|
||||
tcp?: string;
|
||||
type?: 'sse' | 'http';
|
||||
timeout?: number;
|
||||
trust?: boolean;
|
||||
description?: string;
|
||||
include_tools?: string[];
|
||||
exclude_tools?: string[];
|
||||
}
|
||||
|
||||
interface FrontmatterLocalAgentDefinition
|
||||
extends FrontmatterBaseAgentDefinition {
|
||||
kind: 'local';
|
||||
description: string;
|
||||
tools?: string[];
|
||||
mcp_servers?: Record<string, FrontmatterMCPServerConfig>;
|
||||
system_prompt: string;
|
||||
model?: string;
|
||||
temperature?: number;
|
||||
@@ -100,6 +119,23 @@ const nameSchema = z
|
||||
.string()
|
||||
.regex(/^[a-z0-9-_]+$/, 'Name must be a valid slug');
|
||||
|
||||
const mcpServerSchema = z.object({
|
||||
command: z.string().optional(),
|
||||
args: z.array(z.string()).optional(),
|
||||
env: z.record(z.string()).optional(),
|
||||
cwd: z.string().optional(),
|
||||
url: z.string().optional(),
|
||||
http_url: z.string().optional(),
|
||||
headers: z.record(z.string()).optional(),
|
||||
tcp: z.string().optional(),
|
||||
type: z.enum(['sse', 'http']).optional(),
|
||||
timeout: z.number().optional(),
|
||||
trust: z.boolean().optional(),
|
||||
description: z.string().optional(),
|
||||
include_tools: z.array(z.string()).optional(),
|
||||
exclude_tools: z.array(z.string()).optional(),
|
||||
});
|
||||
|
||||
const localAgentSchema = z
|
||||
.object({
|
||||
kind: z.literal('local').optional().default('local'),
|
||||
@@ -115,6 +151,7 @@ const localAgentSchema = z
|
||||
}),
|
||||
)
|
||||
.optional(),
|
||||
mcp_servers: z.record(mcpServerSchema).optional(),
|
||||
model: z.string().optional(),
|
||||
temperature: z.number().optional(),
|
||||
max_turns: z.number().int().positive().optional(),
|
||||
@@ -495,6 +532,28 @@ export function markdownToAgentDefinition(
|
||||
// If a model is specified, use it. Otherwise, inherit
|
||||
const modelName = markdown.model || 'inherit';
|
||||
|
||||
const mcpServers: Record<string, MCPServerConfig> = {};
|
||||
if (markdown.kind === 'local' && markdown.mcp_servers) {
|
||||
for (const [name, config] of Object.entries(markdown.mcp_servers)) {
|
||||
mcpServers[name] = new MCPServerConfig(
|
||||
config.command,
|
||||
config.args,
|
||||
config.env,
|
||||
config.cwd,
|
||||
config.url,
|
||||
config.http_url,
|
||||
config.headers,
|
||||
config.tcp,
|
||||
config.type,
|
||||
config.timeout,
|
||||
config.trust,
|
||||
config.description,
|
||||
config.include_tools,
|
||||
config.exclude_tools,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
kind: 'local',
|
||||
name: markdown.name,
|
||||
@@ -520,6 +579,7 @@ export function markdownToAgentDefinition(
|
||||
tools: markdown.tools,
|
||||
}
|
||||
: undefined,
|
||||
mcpServers: Object.keys(mcpServers).length > 0 ? mcpServers : undefined,
|
||||
inputConfig,
|
||||
metadata,
|
||||
};
|
||||
|
||||
@@ -570,6 +570,19 @@ export class AgentRegistry {
|
||||
},
|
||||
};
|
||||
|
||||
if (overrides.tools) {
|
||||
merged.toolConfig = {
|
||||
tools: overrides.tools,
|
||||
};
|
||||
}
|
||||
|
||||
if (overrides.mcpServers) {
|
||||
merged.mcpServers = {
|
||||
...definition.mcpServers,
|
||||
...overrides.mcpServers,
|
||||
};
|
||||
}
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import { type z } from 'zod';
|
||||
import type { ModelConfig } from '../services/modelConfigService.js';
|
||||
import type { AnySchema } from 'ajv';
|
||||
import type { A2AAuthConfig } from './auth-provider/types.js';
|
||||
import type { MCPServerConfig } from '../config/config.js';
|
||||
|
||||
/**
|
||||
* Describes the possible termination modes for an agent.
|
||||
@@ -130,6 +131,11 @@ export interface LocalAgentDefinition<
|
||||
// Optional configs
|
||||
toolConfig?: ToolConfig;
|
||||
|
||||
/**
|
||||
* Optional inline MCP servers for this agent.
|
||||
*/
|
||||
mcpServers?: Record<string, MCPServerConfig>;
|
||||
|
||||
/**
|
||||
* An optional function to process the raw output from the agent's final tool
|
||||
* call into a string format.
|
||||
|
||||
@@ -240,6 +240,8 @@ export interface AgentOverride {
|
||||
modelConfig?: ModelConfig;
|
||||
runConfig?: AgentRunConfig;
|
||||
enabled?: boolean;
|
||||
tools?: string[];
|
||||
mcpServers?: Record<string, MCPServerConfig>;
|
||||
}
|
||||
|
||||
export interface AgentSettings {
|
||||
@@ -994,7 +996,7 @@ export class Config implements McpContext, AgentLoopContext {
|
||||
modelConfigServiceConfig ?? DEFAULT_MODEL_CONFIGS,
|
||||
);
|
||||
|
||||
this.experimentalJitContext = params.experimentalJitContext ?? false;
|
||||
this.experimentalJitContext = params.experimentalJitContext ?? true;
|
||||
this.topicUpdateNarration = params.topicUpdateNarration ?? false;
|
||||
this.modelSteering = params.modelSteering ?? false;
|
||||
this.injectionService = new InjectionService(() =>
|
||||
|
||||
@@ -51,10 +51,9 @@ class MockBackgroundableInvocation extends BaseToolInvocation<
|
||||
async execute(
|
||||
_signal: AbortSignal,
|
||||
_updateOutput?: (output: ToolLiveOutput) => void,
|
||||
_shellExecutionConfig?: unknown,
|
||||
setExecutionIdCallback?: (executionId: number) => void,
|
||||
options?: { setExecutionIdCallback?: (executionId: number) => void },
|
||||
) {
|
||||
setExecutionIdCallback?.(4242);
|
||||
options?.setExecutionIdCallback?.(4242);
|
||||
return {
|
||||
llmContent: 'pid',
|
||||
returnDisplay: 'pid',
|
||||
@@ -111,7 +110,6 @@ describe('executeToolWithHooks', () => {
|
||||
mockTool,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
mockConfig,
|
||||
);
|
||||
|
||||
@@ -136,7 +134,6 @@ describe('executeToolWithHooks', () => {
|
||||
mockTool,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
mockConfig,
|
||||
);
|
||||
|
||||
@@ -168,7 +165,6 @@ describe('executeToolWithHooks', () => {
|
||||
mockTool,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
mockConfig,
|
||||
);
|
||||
|
||||
@@ -200,7 +196,6 @@ describe('executeToolWithHooks', () => {
|
||||
mockTool,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
mockConfig,
|
||||
);
|
||||
|
||||
@@ -234,7 +229,6 @@ describe('executeToolWithHooks', () => {
|
||||
mockTool,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
mockConfig,
|
||||
);
|
||||
|
||||
@@ -275,7 +269,6 @@ describe('executeToolWithHooks', () => {
|
||||
mockTool,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
mockConfig,
|
||||
);
|
||||
|
||||
@@ -298,8 +291,7 @@ describe('executeToolWithHooks', () => {
|
||||
abortSignal,
|
||||
mockTool,
|
||||
undefined,
|
||||
undefined,
|
||||
setExecutionIdCallback,
|
||||
{ setExecutionIdCallback },
|
||||
mockConfig,
|
||||
);
|
||||
|
||||
|
||||
@@ -11,10 +11,10 @@ import type {
|
||||
AnyDeclarativeTool,
|
||||
AnyToolInvocation,
|
||||
ToolLiveOutput,
|
||||
ExecuteOptions,
|
||||
} from '../tools/tools.js';
|
||||
import { ToolErrorType } from '../tools/tool-error.js';
|
||||
import { debugLogger } from '../utils/debugLogger.js';
|
||||
import type { ShellExecutionConfig } from '../index.js';
|
||||
import { DiscoveredMCPToolInvocation } from '../tools/mcp-tool.js';
|
||||
|
||||
/**
|
||||
@@ -61,8 +61,7 @@ function extractMcpContext(
|
||||
* @param toolName The name of the tool
|
||||
* @param signal Abort signal for cancellation
|
||||
* @param liveOutputCallback Optional callback for live output updates
|
||||
* @param shellExecutionConfig Optional shell execution config
|
||||
* @param setExecutionIdCallback Optional callback to set an execution ID for backgroundable invocations
|
||||
* @param options Optional execution options (shell config, execution ID callback, etc.)
|
||||
* @param config Config to look up MCP server details for hook context
|
||||
* @returns The tool result
|
||||
*/
|
||||
@@ -72,8 +71,7 @@ export async function executeToolWithHooks(
|
||||
signal: AbortSignal,
|
||||
tool: AnyDeclarativeTool,
|
||||
liveOutputCallback?: (outputChunk: ToolLiveOutput) => void,
|
||||
shellExecutionConfig?: ShellExecutionConfig,
|
||||
setExecutionIdCallback?: (executionId: number) => void,
|
||||
options?: ExecuteOptions,
|
||||
config?: Config,
|
||||
originalRequestName?: string,
|
||||
): Promise<ToolResult> {
|
||||
@@ -158,8 +156,7 @@ export async function executeToolWithHooks(
|
||||
const toolResult: ToolResult = await invocation.execute(
|
||||
signal,
|
||||
liveOutputCallback,
|
||||
shellExecutionConfig,
|
||||
setExecutionIdCallback,
|
||||
options,
|
||||
);
|
||||
|
||||
// Append notification if parameters were modified
|
||||
|
||||
@@ -156,6 +156,12 @@ export * from './services/executionLifecycleService.js';
|
||||
// Export Injection Service
|
||||
export * from './config/injectionService.js';
|
||||
|
||||
// Export Execution Lifecycle Service
|
||||
export * from './services/executionLifecycleService.js';
|
||||
|
||||
// Export Injection Service
|
||||
export * from './config/injectionService.js';
|
||||
|
||||
// Export base tool definitions
|
||||
export * from './tools/tools.js';
|
||||
export * from './tools/tool-error.js';
|
||||
|
||||
@@ -80,7 +80,8 @@ toolName = [
|
||||
"google_web_search",
|
||||
"activate_skill",
|
||||
"codebase_investigator",
|
||||
"cli_help"
|
||||
"cli_help",
|
||||
"get_internal_docs"
|
||||
]
|
||||
decision = "allow"
|
||||
priority = 70
|
||||
|
||||
@@ -53,6 +53,6 @@ decision = "allow"
|
||||
priority = 50
|
||||
|
||||
[[rule]]
|
||||
toolName = ["codebase_investigator", "cli_help"]
|
||||
toolName = ["codebase_investigator", "cli_help", "get_internal_docs"]
|
||||
decision = "allow"
|
||||
priority = 50
|
||||
@@ -570,14 +570,13 @@ describe('ToolExecutor', () => {
|
||||
_sig,
|
||||
_tool,
|
||||
_liveCb,
|
||||
_shellCfg,
|
||||
setExecutionIdCallback,
|
||||
options,
|
||||
_config,
|
||||
_originalRequestName,
|
||||
) => {
|
||||
// Simulate the tool reporting an execution ID
|
||||
if (setExecutionIdCallback) {
|
||||
setExecutionIdCallback(testPid);
|
||||
if (options?.setExecutionIdCallback) {
|
||||
options.setExecutionIdCallback(testPid);
|
||||
}
|
||||
return { llmContent: 'done', returnDisplay: 'done' };
|
||||
},
|
||||
@@ -624,16 +623,8 @@ describe('ToolExecutor', () => {
|
||||
|
||||
const testExecutionId = 67890;
|
||||
vi.mocked(coreToolHookTriggers.executeToolWithHooks).mockImplementation(
|
||||
async (
|
||||
_inv,
|
||||
_name,
|
||||
_sig,
|
||||
_tool,
|
||||
_liveCb,
|
||||
_shellCfg,
|
||||
setExecutionIdCallback,
|
||||
) => {
|
||||
setExecutionIdCallback?.(testExecutionId);
|
||||
async (_inv, _name, _sig, _tool, _liveCb, options) => {
|
||||
options?.setExecutionIdCallback?.(testExecutionId);
|
||||
return { llmContent: 'done', returnDisplay: 'done' };
|
||||
},
|
||||
);
|
||||
|
||||
@@ -112,8 +112,7 @@ export class ToolExecutor {
|
||||
signal,
|
||||
tool,
|
||||
liveOutputCallback,
|
||||
shellExecutionConfig,
|
||||
setExecutionIdCallback,
|
||||
{ shellExecutionConfig, setExecutionIdCallback },
|
||||
this.config,
|
||||
request.originalRequestName,
|
||||
);
|
||||
|
||||
@@ -22,13 +22,13 @@ import {
|
||||
type ToolExecuteConfirmationDetails,
|
||||
type PolicyUpdateOptions,
|
||||
type ToolLiveOutput,
|
||||
type ExecuteOptions,
|
||||
} from './tools.js';
|
||||
|
||||
import { getErrorMessage } from '../utils/errors.js';
|
||||
import { summarizeToolOutput } from '../utils/summarizer.js';
|
||||
import {
|
||||
ShellExecutionService,
|
||||
type ShellExecutionConfig,
|
||||
type ShellOutputEvent,
|
||||
} from '../services/shellExecutionService.js';
|
||||
import { formatBytes } from '../utils/formatters.js';
|
||||
@@ -150,9 +150,9 @@ export class ShellToolInvocation extends BaseToolInvocation<
|
||||
async execute(
|
||||
signal: AbortSignal,
|
||||
updateOutput?: (output: ToolLiveOutput) => void,
|
||||
shellExecutionConfig?: ShellExecutionConfig,
|
||||
setExecutionIdCallback?: (executionId: number) => void,
|
||||
options?: ExecuteOptions,
|
||||
): Promise<ToolResult> {
|
||||
const { shellExecutionConfig, setExecutionIdCallback } = options ?? {};
|
||||
const strippedCommand = stripShellWrapper(this.params.command);
|
||||
|
||||
if (signal.aborted) {
|
||||
|
||||
@@ -266,6 +266,9 @@ export const PLAN_MODE_TOOLS = [
|
||||
WEB_SEARCH_TOOL_NAME,
|
||||
ASK_USER_TOOL_NAME,
|
||||
ACTIVATE_SKILL_TOOL_NAME,
|
||||
GET_INTERNAL_DOCS_TOOL_NAME,
|
||||
'codebase_investigator',
|
||||
'cli_help',
|
||||
] as const;
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,15 @@ import {
|
||||
import { type ApprovalMode } from '../policy/types.js';
|
||||
import type { SubagentProgress } from '../agents/types.js';
|
||||
|
||||
/**
|
||||
* Options bag for tool execution, replacing positional parameters that are
|
||||
* only relevant to specific tool types.
|
||||
*/
|
||||
export interface ExecuteOptions {
|
||||
shellExecutionConfig?: ShellExecutionConfig;
|
||||
setExecutionIdCallback?: (executionId: number) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a validated and ready-to-execute tool call.
|
||||
* An instance of this is created by a `ToolBuilder`.
|
||||
@@ -68,8 +77,7 @@ export interface ToolInvocation<
|
||||
execute(
|
||||
signal: AbortSignal,
|
||||
updateOutput?: (output: ToolLiveOutput) => void,
|
||||
shellExecutionConfig?: ShellExecutionConfig,
|
||||
setExecutionIdCallback?: (executionId: number) => void,
|
||||
options?: ExecuteOptions,
|
||||
): Promise<TResult>;
|
||||
|
||||
/**
|
||||
@@ -325,7 +333,7 @@ export abstract class BaseToolInvocation<
|
||||
abstract execute(
|
||||
signal: AbortSignal,
|
||||
updateOutput?: (output: ToolLiveOutput) => void,
|
||||
shellExecutionConfig?: ShellExecutionConfig,
|
||||
options?: ExecuteOptions,
|
||||
): Promise<TResult>;
|
||||
}
|
||||
|
||||
@@ -427,6 +435,25 @@ export abstract class DeclarativeTool<
|
||||
readonly extensionId?: string,
|
||||
) {}
|
||||
|
||||
clone(messageBus?: MessageBus): this {
|
||||
// Note: we cannot use structuredClone() here because it does not preserve
|
||||
// prototype chains or handle non-serializable properties (like functions).
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const cloned = Object.assign(
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
Object.create(Object.getPrototypeOf(this)),
|
||||
this,
|
||||
) as this;
|
||||
if (messageBus) {
|
||||
Object.defineProperty(cloned, 'messageBus', {
|
||||
value: messageBus,
|
||||
writable: false,
|
||||
configurable: true,
|
||||
});
|
||||
}
|
||||
return cloned;
|
||||
}
|
||||
|
||||
get isReadOnly(): boolean {
|
||||
return READ_ONLY_KINDS.includes(this.kind);
|
||||
}
|
||||
@@ -522,10 +549,10 @@ export abstract class DeclarativeTool<
|
||||
params: TParams,
|
||||
signal: AbortSignal,
|
||||
updateOutput?: (output: ToolLiveOutput) => void,
|
||||
shellExecutionConfig?: ShellExecutionConfig,
|
||||
options?: ExecuteOptions,
|
||||
): Promise<TResult> {
|
||||
const invocation = this.build(params);
|
||||
return invocation.execute(signal, updateOutput, shellExecutionConfig);
|
||||
return invocation.execute(signal, updateOutput, options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2013,8 +2013,8 @@
|
||||
"jitContext": {
|
||||
"title": "JIT Context Loading",
|
||||
"description": "Enable Just-In-Time (JIT) context loading.",
|
||||
"markdownDescription": "Enable Just-In-Time (JIT) context loading.\n\n- Category: `Experimental`\n- Requires restart: `yes`\n- Default: `false`",
|
||||
"default": false,
|
||||
"markdownDescription": "Enable Just-In-Time (JIT) context loading.\n\n- Category: `Experimental`\n- Requires restart: `yes`\n- Default: `true`",
|
||||
"default": true,
|
||||
"type": "boolean"
|
||||
},
|
||||
"useOSC52Paste": {
|
||||
|
||||
Reference in New Issue
Block a user