Repository navigation
[Bug]: CLI ignores permission.nativeDelete - deletions still go to the Recycle Bin while the Desktop app permanently deletes (Windows, 0.6.13) #529
Description
Product or interface
CLI - headless (mcode exec) and CLI - interactive TUI.
The Desktop app (Electron, same 0.6.13 runtime) behaves differently from the CLI while reading the exact same config.yaml, so the two surfaces disagree. This report is about the CLI side.
Version
- CLI:
@minimax-ai/code@0.6.13(mcode --version→0.6.13) - Node.js v24.14.1, npm 11.13.0
- Desktop app for comparison: same
0.6.13runtime. - Note: the open-source
mainbranch of this repo is still at0.6.5and does not containpermission.nativeDeleteat all (it was introduced in the published0.6.9). This report is therefore based on the published npm artifact; the fix will need to land on top of0.6.13.
Platform
Windows
OS version and architectu
Windows 11 專業版, 10.0.26100, x64. PowerShell 5.1.26100.9711.
Issue area
Tool call / permission / sandbox
Steps to reproduce
- Install the CLI:
npm install -g @minimax-ai/code(0.6.13). - Enable native deletion in the shared runtime config. Both surfaces use the same data directory, so edit
<data-dir>/config.yaml(default~/.minimax/config.yaml):permission: nativeDelete: true
- Create a probe file:
Set-Content -LiteralPath 'D:\MiniMax\_ndtest\cli_rm.txt' -Value 'probe' - Run one headless task that deletes it:
mcode exec --output-format json --permission full --cwd 'D:\MiniMax\_ndtest' \ "使用 bash 工具執行這一行(不要改寫指令):rm -- 'D:\MiniMax\_ndtest\cli_rm.txt'" - Inspect the Windows Recycle Bin (e.g.
Shell.Application→Namespace(10)).
Expected and actual behavior
Expected: per the 0.6.9 changelog entry that introduced the flag, permission.nativeDelete: true means "use the selected shell's native deletion instead of enforcing recoverable deletion". The file should be permanently removed, and the CLI should behave the same as the Desktop app, which does honor the flag.
Actual: the file is deleted but recoverably — it lands in the Windows Recycle Bin with its original path preserved (orig=D:\MiniMax\_ndtest), instead of being permanently removed.
The same config.yaml produces different results on the two surfaces:
| Surface | Command | Result |
|---|---|---|
| Desktop app | Remove-Item 'D:\MiniMax\_ndtest\app_literal.txt' -Force |
permanently deleted, not in Recycle Bin |
| CLI | rm -- 'D:\MiniMax\_ndtest\cli_rm.txt' |
deleted, present in Recycle Bin |
The key observation is that this is not merely a difference in which delete command each surface prefers. In the published CLI bundle, the recoverable-rewrite of rm is explicitly gated on the flag being off:
if (u) return s("recoverable-delete-rewrite", { ... }); // recoverable windows delete
if (!n?.nativeDelete && bs(t)) return s("rm-rewrite", { ... }); // POSIX rm -> trashSo the fact that the CLI rewrote rm into the trash launcher proves that nativeDelete was read as false inside the permission check, even though the same file sets it to true.
Redacted error summary
No error is raised. The behavior is a silent fallback to the recoverable-delete path, so there is nothing in the logs to quote.
Suspected root cause
The flag is parsed correctly and then lost during the config merge, so it never reaches the permission check.
parsePermissionConfig() in packages/config/src/permission-config.ts does parse it (obj.nativeDelete === true), and PERMISSION_CONFIG_DEFAULTS includes nativeDelete: false, so the value enters the config correctly:
const nativeDelete = obj.nativeDelete === true;
return { policyOwner, storageWriteVersion, classifierTimeoutMs, userConfirmationEnabled, nativeDelete };However, the config-merge step rebuilds the permission object from four fields only and drops nativeDelete. In main this shape lives in packages/local-runtime/src/permissions/service.ts → toRuntimeConfig() (lines 225-241):
permission: {
policyOwner: local.permission?.policyOwner ?? getConfig().permission?.policyOwner ?? 'core',
storageWriteVersion:
local.permission?.storageWriteVersion ?? getConfig().permission?.storageWriteVersion ?? 2,
classifierTimeoutMs:
local.permission?.classifierTimeoutMs ??
getConfig().permission?.classifierTimeoutMs ??
AUTO_CLASSIFIER_TIMEOUT_MS_DEFAULT,
userConfirmationEnabled:
local.permission?.userConfirmationEnabled ??
getConfig().permission?.userConfirmationEnabled ??
true,
},The same four-field rebuild is present in the published CLI bundle (IMr() in chunks/chunk-H6JEREKX.js).
Every downstream consumer then reads the rebuilt object rather than the raw config, e.g.:
const nativeDelete = unresolvedParams.nativeDeleteSnapshot ?? config.permission?.nativeDelete === true;Because nativeDelete was never copied into the rebuilt object, this resolves to undefined → false, and the permission checker keeps enforcing recoverable deletion.
Two things would make this concrete for whoever picks it up:
- Confirm whether the
0.6.13config-merge path is the same four-field rebuild as the one inmain(the flag does not exist inmain, so it cannot be observed there directly). - If so,
nativeDeleteneeds to be carried through the merge alongside the other four fields.
A secondary, Windows-specific limitation worth considering while fixing this: the CLI's Bash tool runs a POSIX shell on Windows, so the native-delete command subset the runtime recognizes (del/erase/rd/rmdir for cmd, remove-item/ri for PowerShell) is not directly expressible — Remove-Item is simply command not found in the CLI's shell. Even with the flag correctly threaded through, the CLI would still fall back to rm, so the POSIX rm path needs to honor nativeDelete too.
Screenshots
N/A — reproducible from the CLI alone.
Before submitting
- I have searched existing issues.
- I have included my version and removed sensitive information.
Activity
yujiachen-y commentedon Oct 24, 2026
Hi @HowHaoChih , I cannot reproduce your issue. Can check again for your version? Or you can submit a feedback vim
/feedbackcommand.