chore(hooks): 输出门禁三脚本更新 + 新增技能加载闸门 skill-load-guard.py

This commit is contained in:
admin committed 2026-09-16 11:28:51 +08:00
1 parent c8b514832f
commit bda9b10161
4 files changed
+385 -10

No files matched your search

+29 -2
View File
@@ -78,8 +78,26 @@ def lock_owner() -> str:
def out(obj: dict) -> None:
sys.stdout.write(json.dumps(obj, ensure_ascii=False))
sys.stdout.flush()
# ⚠️ 2026-09-16 修复(**根因**):必须走 **`sys.stdout.buffer` 写 bytes**,⛔ 不能走文本层
# (`sys.stdout.write(str)`)。
# 实证:宿主 spawn 本脚本时 stdout 编码**不保证是 UTF-8**(本机 `PYTHONUTF8=1` 在 hook 环境下
# 不保证生效),而 deny 文案里含 `⛔` / `🔐` 等字符 ⇒ 文本层写抛 `UnicodeEncodeError` ⇒
# 异常冒泡、**stdout 为空** ⇒ 宿主**收不到 deny** ⇒ **静默放行**。
# 而 `hook_log()` 已经把 `PreToolUse-deny` 写进日志 ⇒ 表现成 **「日志里有 DENY,但拦不住」**
# (2026-09-16 受控自测:无锁时 Write 文档库 ⇒ 文件真被创建)。
# 对照:`bash-output-guard.py` 用 `sys.stdout.buffer.write(bytes)` ⇒ **实测拦得住**。
# 📌 教训:本脚本的 `stdin` 早前已按 buffer 加固(见 main 顶部注释),**stdout 漏了** ——
# 「读写要同时加固」,只修一半等于没修。
data = json.dumps(obj, ensure_ascii=False).encode('utf-8')
try:
sys.stdout.buffer.write(data)
sys.stdout.buffer.flush()
except Exception:
try:
sys.stdout.write(data.decode('utf-8', 'replace'))
sys.stdout.flush()
except Exception:
pass
LOG_FILE = os.environ.get(
@@ -120,6 +138,15 @@ def main() -> None:
tool = payload.get("tool_name")
event = payload.get("hook_event_name") or payload.get("hook_event") or ""
# ★ 入口即留痕(2026-09-16 加)—— 与 stop-dialog-guard.py / skill-load-guard.py 同款。
# 为什么不只记 SessionStart 与 deny:2026-09-16 实测 —— 重启后本钩子**一行都没写**,
# 当时无法区分"没被宿主调用"与"被静默放行",只能靠猜(最后查明:SessionStart 的
# `matcher: "startup"` 不匹配"恢复会话",钩子压根没被执行)。
# 留痕里带上**实际收到的字段名**(keys):下次宿主字段一变,日志里立刻能看出来,
# 而不是等某天发现守卫早已失效。低频:每次调用一行。
hook_log("entry", "event=%s|tool=%s|keys=%s" % (
event or "?", tool or "-", ",".join(sorted(payload.keys()))[:220]))
# ── SessionStart:只提示(该事件的输出只给用户看,不会进 Agent 上下文)──
if event == "SessionStart" or (tool is None and "source" in payload):
src = payload.get("source", "?")