fix(cost): 修门禁三处误伤 + 新增「自动发现并上报」
用户两问:① 机制会不会影响 AI 会话?② 后续能否自动发现并上报?——① 自查出**三处真误伤**并修掉,
② 新增两条自动上报。
**修误伤(都是实测出来的,不是推测)**
1. `ls -la` 被拦 —— 正则 `-[a-zA-Z]*[lR]` 里的 `l` 命中日常 `-la` ⇒ 改为**只拦 `-R`**(递归全树)
2. `cat 小文件` 被拦 —— 盲拦一切 `cat *.txt` ⇒ 改为**按目标文件大小**(`> cat_bigfile()`:>200 KB 才拦,
取不到大小一律放行)
3. **"提到"也拦** —— 原按**整条命令文本**匹配 ⇒ `printf 'ls -laR /x'`、测试脚本、写文档全被拦(阻塞面过大)
⇒ 改为**按 shell 分段、只看每段开头**(`segments()` + `re.match`):
`cd /tmp && ls -laR /x` 照样拦(第二段以 `ls` 开头),而"引号里提到"放行
实测 8/8:`ls -la` 放行 · `ls -laR` 拦 · `cd x && ls -laR` 拦 · `cat` 小/大 分别放行/拦 ·
引号内提到放行 · `grep -rn` 拦 · `grep -rn | head` 放行
**新增「自动发现并上报」**(用户第 2 问的落地)
- `stop-dialog-guard.py` → `guard_health()`:读 `bash-guard.log`,**同一规则重复命中 ≥3 次** ⇒
经 `UserPromptSubmit` 主动报告「疑似误伤,不是你操作有问题」,并给出两条处置(换写法 / `off` + 上报用户)
- 两个钩子脚本仍保持**入口即留痕**(`entry|event=…`)⇒ "钩子有没有被调用"永远可判
⚠️ 副作用须知:门禁**已生效**(重启已加载)。默认 `soft` 只拦 6 类;急停 = env `DSH_OUTPUT_GUARD_OFF=1`
或 `<工作区>/.workbuddy/bash-guard-mode` 写 `off`;`hard` 才启用 `rg`/`git log`/`du·tree` 三条。
This commit is contained in:
1 parent
3efd68517f
commit
3f141c7b4d
2 files changed
+80
-14
No files matched your search
@@ -308,6 +308,37 @@ def budget_note(tokens, ncalls, prev=None):
|
||||
)
|
||||
|
||||
|
||||
def guard_health(root):
|
||||
"""**自动发现并上报**:读 `bash-guard.log`,同一规则重复命中 ≥3 次 ⇒ 提示"可能误伤"。
|
||||
|
||||
为什么要有:门禁自己不会喊疼 —— 只有"写日志"没人看。让每轮都跑的脚本顺带体检,
|
||||
机制问题才能在**下一次用户发言时**浮出来(而不是等人发现"AI 怎么老做不成事")。
|
||||
"""
|
||||
try:
|
||||
p = os.path.join(root, '.workbuddy', 'bash-guard.log')
|
||||
rows = [l for l in io.open(p, encoding='utf-8').read().split('\n') if 'DENY|' in l][-40:]
|
||||
except OSError:
|
||||
return ''
|
||||
if len(rows) < 3:
|
||||
return ''
|
||||
cnt = {}
|
||||
for l in rows:
|
||||
try:
|
||||
k = l.split('DENY|')[1].split('|')[0].strip()
|
||||
except IndexError:
|
||||
continue
|
||||
cnt[k] = cnt.get(k, 0) + 1
|
||||
if not cnt:
|
||||
return ''
|
||||
why, n = max(cnt.items(), key=lambda kv: kv[1])
|
||||
if n < 3:
|
||||
return ''
|
||||
return ('⚠️【门禁自检】最近 %d 次 Bash 里有 **%d 次**因「%s」被拦(同一规则重复命中)⇒ 先怀疑**误伤**,'
|
||||
'不是谁的操作有问题。处置:① 换等价限流写法;② 若确认误伤 ⇒ 把 `off` 写进 '
|
||||
'`.workbuddy/bash-guard-mode`(或 env `DSH_OUTPUT_GUARD_OFF=1`),并**主动上报用户**'
|
||||
'(规则该不该收窄是人的决定)。' % (len(rows), n, why))
|
||||
|
||||
|
||||
def mode_of(root):
|
||||
try:
|
||||
m = io.open(os.path.join(root or '.', '.workbuddy', 'stop-guard-mode'), encoding='utf-8').read()
|
||||
@@ -332,15 +363,15 @@ def user_prompt_mode(payload):
|
||||
hit = bool(tail) and not RE_QUOTE.search(tail) and bool(RE_BAN.search(tail))
|
||||
toks, ncalls, prev = session_budget(tp)
|
||||
note = budget_note(toks, ncalls, prev)
|
||||
log(root0 or '.', 'invoked(user-prompt)|mode=%s|上轮收尾=征询句:%s|上下文=%s tok(+%s)|工具=%s 次|预算告警=%s|%s'
|
||||
% (mode, hit, toks, (toks - prev) if (toks and prev) else '-', ncalls, bool(note),
|
||||
gh = guard_health(root0 or '.')
|
||||
log(root0 or '.', 'invoked(user-prompt)|mode=%s|上轮收尾=征询句:%s|上下文=%s tok(+%s)|工具=%s 次|预算告警=%s|门禁自检=%s|%s'
|
||||
% (mode, hit, toks, (toks - prev) if (toks and prev) else '-', ncalls, bool(note), bool(gh),
|
||||
(tail.replace('\n', ' ')[:60] if tail else '(取不到上一轮文本)')))
|
||||
if mode == 'inject' and (hit or note):
|
||||
if mode == 'inject' and (hit or note or gh):
|
||||
ctx = ''
|
||||
if hit:
|
||||
ctx += CONTEXT
|
||||
if note:
|
||||
ctx += ('\n\n' + note) if ctx else note
|
||||
for seg in (CONTEXT if hit else '', note, gh):
|
||||
if seg:
|
||||
ctx += ('\n\n' + seg) if ctx else seg
|
||||
_emit({'hookSpecificOutput': {'hookEventName': 'UserPromptSubmit',
|
||||
'additionalContext': ctx}})
|
||||
|
||||
|
||||
Reference in new issue
Block a user