pi: 档位判定 tool_call hook + 心跳报 native

- worker.mjs: mailContext 加 permissionMode 字段(从 SSE payload 读入)
- tool_call hook 增加档位判定:
  full → 不拦截任何工具(直接 return)
  plan → 被守卫工具(bash/write/edit)一律 block + 返回原因说明
  workspace → 走原有问人流程(不变)
- index.mjs 心跳上报 mode_enforcement: 'native'
- 导入 normalizeMode/MODE_FULL/MODE_PLAN 从 lib/permission-mode.js
- 377 测试全过
This commit is contained in:
2026-09-06 19:19:42 +08:00
parent 3bb419f2f2
commit 0c98fab4d5
2 changed files with 16 additions and 1 deletions

View File

@ -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 {

View File

@ -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);