mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-06-01 06:32:48 -07:00
feat(core): always allow topic tool and refine reasoning mandate
- Added explicit policy rules to always allow 'create_new_topic' in all modes. - Updated topic tool output to use 'Current topic' phrasing for clarity. - Replaced the 'Thinking Protocol' with a mandate for internal reasoning to prevent literal thought block output in the UI. - Added and updated unit tests to verify policy and tool behavior.
This commit is contained in:
@@ -101,6 +101,13 @@ decision = "allow"
|
||||
priority = 70
|
||||
modes = ["plan"]
|
||||
|
||||
# Topic grouping tool is innocuous and used for UI organization.
|
||||
[[rule]]
|
||||
toolName = "create_new_topic"
|
||||
decision = "allow"
|
||||
priority = 70
|
||||
modes = ["plan"]
|
||||
|
||||
[[rule]]
|
||||
toolName = ["ask_user", "save_memory"]
|
||||
decision = "ask_user"
|
||||
|
||||
@@ -55,4 +55,10 @@ priority = 50
|
||||
[[rule]]
|
||||
toolName = ["codebase_investigator", "cli_help", "get_internal_docs"]
|
||||
decision = "allow"
|
||||
priority = 50
|
||||
|
||||
# Topic grouping tool is innocuous and used for UI organization.
|
||||
[[rule]]
|
||||
toolName = "create_new_topic"
|
||||
decision = "allow"
|
||||
priority = 50
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import * as path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { loadPoliciesFromToml } from './toml-loader.js';
|
||||
import { PolicyEngine } from './policy-engine.js';
|
||||
import { ApprovalMode, PolicyDecision } from './types.js';
|
||||
import { CREATE_NEW_TOPIC_TOOL_NAME } from '../tools/tool-names.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
describe('Topic Tool Policy', () => {
|
||||
async function loadDefaultPolicies() {
|
||||
const policiesDir = path.resolve(__dirname, 'policies');
|
||||
const getPolicyTier = () => 1; // Default tier
|
||||
const result = await loadPoliciesFromToml([policiesDir], getPolicyTier);
|
||||
return result.rules;
|
||||
}
|
||||
|
||||
it('should allow create_new_topic in DEFAULT mode', async () => {
|
||||
const rules = await loadDefaultPolicies();
|
||||
const engine = new PolicyEngine({
|
||||
rules,
|
||||
approvalMode: ApprovalMode.DEFAULT,
|
||||
});
|
||||
|
||||
const result = await engine.check(
|
||||
{ name: CREATE_NEW_TOPIC_TOOL_NAME },
|
||||
undefined,
|
||||
);
|
||||
expect(result.decision).toBe(PolicyDecision.ALLOW);
|
||||
});
|
||||
|
||||
it('should allow create_new_topic in PLAN mode', async () => {
|
||||
const rules = await loadDefaultPolicies();
|
||||
const engine = new PolicyEngine({
|
||||
rules,
|
||||
approvalMode: ApprovalMode.PLAN,
|
||||
});
|
||||
|
||||
const result = await engine.check(
|
||||
{ name: CREATE_NEW_TOPIC_TOOL_NAME },
|
||||
undefined,
|
||||
);
|
||||
expect(result.decision).toBe(PolicyDecision.ALLOW);
|
||||
});
|
||||
|
||||
it('should allow create_new_topic in YOLO mode', async () => {
|
||||
const rules = await loadDefaultPolicies();
|
||||
const engine = new PolicyEngine({
|
||||
rules,
|
||||
approvalMode: ApprovalMode.YOLO,
|
||||
});
|
||||
|
||||
const result = await engine.check(
|
||||
{ name: CREATE_NEW_TOPIC_TOOL_NAME },
|
||||
undefined,
|
||||
);
|
||||
expect(result.decision).toBe(PolicyDecision.ALLOW);
|
||||
});
|
||||
});
|
||||
@@ -591,10 +591,10 @@ function mandateTopicUpdateModel(): string {
|
||||
- **Silent Mode:** No conversational filler, no "I will now...", and no summaries between tools.
|
||||
- Only the tool execution is permitted to define the state. Everything in between must be silent.
|
||||
|
||||
- **3. Thinking Protocol:**
|
||||
- Use internal thought blocks to keep track of what tools you have called, plan your next steps, and reason about the task.
|
||||
- Without reasoning and tracking in thought blocks, you may lose context.
|
||||
- Always use the required syntax for thought blocks to ensure they remain hidden from the user interface.
|
||||
- **3. Internal Reasoning:**
|
||||
- You MUST reason about your plan, track tool calls, and strategize internally before executing tools.
|
||||
- This reasoning process must remain internal. You are strictly FORBIDDEN from including your reasoning, "thoughts," or explanations in your text response.
|
||||
- Between tool calls, your text output MUST remain completely empty (Zero-Noise).
|
||||
|
||||
- **4. Completion:**
|
||||
- Only when the entire task is finalized do you provide a **Final Summary**.
|
||||
|
||||
@@ -59,7 +59,7 @@ describe('CreateNewTopicTool', () => {
|
||||
const invocation = tool.build({ [TOPIC_PARAM_TITLE]: 'New Chapter' });
|
||||
const result = await invocation.execute(new AbortController().signal);
|
||||
|
||||
expect(result.llmContent).toContain('New Chapter');
|
||||
expect(result.llmContent).toBe('Current topic: "New Chapter"');
|
||||
expect(TopicManager.getInstance().getTopic()).toBe('New Chapter');
|
||||
});
|
||||
|
||||
|
||||
@@ -78,8 +78,8 @@ class CreateNewTopicInvocation extends BaseToolInvocation<
|
||||
TopicManager.getInstance().setTopic(title.trim());
|
||||
|
||||
return {
|
||||
llmContent: `Topic changed to: "${title.trim()}"`,
|
||||
returnDisplay: `Topic changed to: **${title.trim()}**`,
|
||||
llmContent: `Current topic: "${title.trim()}"`,
|
||||
returnDisplay: `Current topic: **${title.trim()}**`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user