diff --git a/plugins/pi-mail-bridge/src/index.mjs b/plugins/pi-mail-bridge/src/index.mjs index eb083ae..e38ff57 100644 --- a/plugins/pi-mail-bridge/src/index.mjs +++ b/plugins/pi-mail-bridge/src/index.mjs @@ -322,6 +322,7 @@ async function main() { const beat = async () => { const [platform_sessions, models] = await Promise.all([reportSessions(), reportModels()]); const body = {}; + body.mode_enforcement = 'native'; // tool_call hook 能 block bash/write/edit if (platform_sessions) body.platform_sessions = platform_sessions; if (models) body.models = models; try { diff --git a/plugins/pi-mail-bridge/src/worker.mjs b/plugins/pi-mail-bridge/src/worker.mjs index 60eb2af..75c1f27 100644 --- a/plugins/pi-mail-bridge/src/worker.mjs +++ b/plugins/pi-mail-bridge/src/worker.mjs @@ -54,6 +54,7 @@ import { explicitSends, shouldSkipAutoRelay } from '../lib/relay-dedup.js'; import { autoRelayDecision } from '../lib/relay-policy.js'; import { adoptedSessionID, adoptMissingMessage } from '../lib/adopt.js'; import { isApproval, isAlwaysDecision } from '../lib/permission-grants.js'; +import { normalizeMode, MODE_FULL, MODE_PLAN } from '../lib/permission-mode.js'; import { clampRelayKey, isPermanentFailure } from '../lib/relay-key.js'; // ─── 与主进程的通道 ─── @@ -90,7 +91,7 @@ let job = null; let client = null; let modelRuntime = null; let piSessionId = ''; -let mailContext = { replyTo: '', subject: '', mailID: '' }; +let mailContext = { replyTo: '', subject: '', mailID: '', permissionMode: 'workspace' }; let lastSyncedName = ''; let relayedKey = ''; let finished = false; @@ -110,6 +111,18 @@ function permissionExtension() { return (pi) => { pi.on('tool_call', async (event, ctx) => { + // ── 档位判定 ── + // plan: 被守卫的工具一律直接拒绝(该档语义是「不动手」,没什么可问人的) + // full: 不拦截(已声明全权) + // workspace: 走原有问人流程 + const mode = normalizeMode(mailContext.permissionMode); + if (mode === MODE_FULL) return; // full 档不拦任何工具 + if (mode === MODE_PLAN && GUARDED.has(event.toolName)) { + return { + block: true, + reason: `plan 档下不允许执行 ${event.toolName}。本档只允许读与查,请把方案写在回信里。如需动手请让发件人把档位改成 workspace。`, + }; + } if (!GUARDED.has(event.toolName)) return; const sid = ctx?.sessionManager?.getSessionId?.() || ''; @@ -559,6 +572,7 @@ process.on('message', (msg) => { replyTo: msg.data?.from_name || '', subject: msg.data?.subject || '', mailID: msg.data?.mail_id || '', + permissionMode: msg.data?.permission_mode || 'workspace', }; lastSyncedName = msg.lastSyncedName || ''; for (const t of msg.grants || []) grants.add(t);