mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-31 10:16:57 +00:00
fix(core): allow reading saved plan files without a confirmation prompt (#7678)
The default plans dir (~/.qwen/plans) sits outside the workspace and was in none of ReadFileTool's permission-free roots, so reading a saved plan back landed on an ask confirmation — popped at exactly the moment the user approved the plan and told the agent to start coding, and resolvable as a denial in non-interactive/ACP flows. With the approved-plan pointer (#7197) the saved file is the model's only recovery route for the plan text, so the read must not stall on a prompt. Adds config.getPlansDir() to ReadFileTool.getDefaultPermission's allowedRoots and to AcpAgent.buildAcpLocalReadRoots (per the SYNC comment). The dir holds only session plan files; sibling ~/.qwen files such as settings.json stay confirmation-gated, pinned by a new test. Refs #6237 Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
a76a0feedb
commit
331d58c652
4 changed files with 30 additions and 0 deletions
|
|
@ -2497,6 +2497,7 @@ describe('QwenAgent MCP SSE/HTTP support', () => {
|
|||
getSessionService: vi.fn(() => new SessionService('/tmp')),
|
||||
hasSessionWriteOwnership: vi.fn().mockReturnValue(false),
|
||||
getSessionRuntimeBaseDir: vi.fn().mockReturnValue('/runtime-a'),
|
||||
getPlansDir: vi.fn().mockReturnValue('/home/test/.qwen/plans'),
|
||||
setFileSystemService: vi.fn(),
|
||||
getHookSystem: vi.fn().mockReturnValue(undefined),
|
||||
getDisableAllHooks: vi.fn().mockReturnValue(true),
|
||||
|
|
@ -2515,6 +2516,7 @@ describe('QwenAgent MCP SSE/HTTP support', () => {
|
|||
'/tmp/user-memory',
|
||||
'/home/test/.qwen/skills',
|
||||
'/tmp/qwen-extensions',
|
||||
'/home/test/.qwen/plans',
|
||||
...(process.platform === 'win32' ? [] : ['/tmp']),
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -542,6 +542,9 @@ function buildAcpLocalReadRoots(config: Config): string[] {
|
|||
getUserAutoMemoryRoot(),
|
||||
...config.storage.getUserSkillsDirs(),
|
||||
Storage.getUserExtensionsDir(),
|
||||
// Saved plan files (see ReadFileTool.getDefaultPermission for why the
|
||||
// plans dir must be readable without a confirmation prompt).
|
||||
config.getPlansDir(),
|
||||
...defaultAcpOnlyLocalReadRoots(),
|
||||
...parseAcpLocalReadRootsEnv(),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ describe('ReadFileTool', () => {
|
|||
getProjectDir: () => path.join(tempRootDir, '.project'),
|
||||
getUserSkillsDirs: () => [path.join(os.homedir(), '.qwen', 'skills')],
|
||||
},
|
||||
getPlansDir: () => path.join(os.homedir(), '.qwen', 'plans'),
|
||||
getTruncateToolOutputThreshold: () => 2500,
|
||||
getTruncateToolOutputLines: () => 500,
|
||||
getContentGeneratorConfig: () => ({
|
||||
|
|
@ -324,6 +325,24 @@ describe('ReadFileTool', () => {
|
|||
expect(permission).toBe('allow');
|
||||
});
|
||||
|
||||
it('should return allow for saved plan files under the plans directory', async () => {
|
||||
const params: ReadFileToolParams = {
|
||||
file_path: path.join(os.homedir(), '.qwen', 'plans', 'session-1.md'),
|
||||
};
|
||||
const invocation = tool.build(params);
|
||||
const permission = await invocation.getDefaultPermission();
|
||||
expect(permission).toBe('allow');
|
||||
});
|
||||
|
||||
it('should still return ask for ~/.qwen files outside the plans directory', async () => {
|
||||
const params: ReadFileToolParams = {
|
||||
file_path: path.join(os.homedir(), '.qwen', 'settings.json'),
|
||||
};
|
||||
const invocation = tool.build(params);
|
||||
const permission = await invocation.getDefaultPermission();
|
||||
expect(permission).toBe('ask');
|
||||
});
|
||||
|
||||
it('should return ask for paths directly under the OS temp directory', async () => {
|
||||
const params: ReadFileToolParams = {
|
||||
file_path: path.join(os.tmpdir(), 'pr-review-context.md'),
|
||||
|
|
|
|||
|
|
@ -135,6 +135,12 @@ class ReadFileToolInvocation extends BaseToolInvocation<
|
|||
Storage.getGlobalTempDir(),
|
||||
...this.config.storage.getUserSkillsDirs(),
|
||||
Storage.getUserExtensionsDir(),
|
||||
// Approved plans are persisted here (default ~/.qwen/plans, outside
|
||||
// the workspace) and after approval nothing re-injects the plan text,
|
||||
// so the saved file is the model's only recovery route — reading it
|
||||
// back must not stall on a confirmation prompt. The dir holds only
|
||||
// session plan files, never credentials or settings.
|
||||
this.config.getPlansDir(),
|
||||
];
|
||||
|
||||
if (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue