@routerhub/agent-rules 1.5.197 → 1.5.198

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.base.md CHANGED
@@ -695,6 +695,30 @@ agent-rules 生成的规则文件分两层,行为与归属不同,评审/发
695
695
  4. 一旦 `eval` / `snapshot` 返回的内容不是自己操作的页面(URL/内容不符),
696
696
  **第一反应是标签页被抢占**:切回自己的 tab id 后重试,禁止盲目重试同一条命令。
697
697
 
698
+ ## 🚨 agent-browser 内存收尾铁律(用完必须收摊)
699
+
700
+ **根因**:agent-browser CLI 本体很轻,但它背后复用的 Chrome Profile 会保留标签页、
701
+ Service Worker、登录态缓存和页面运行时。每次任务只打开不关闭,`9223` / `9226` 的
702
+ Chrome Helper 会越积越多,表现为“agent-browser 用着用着越来越卡”。
703
+
704
+ **必须遵守**:
705
+
706
+ 1. 每次 `agent-browser tab new` 后必须记录本次任务创建的 tab id;任务结束前,必须关闭
707
+ 本任务打开的所有 tab,禁止把 Cloud Console、GitHub、Slack、Google 登录页等任务页面
708
+ 长期留在 agent-browser Chrome 里。
709
+ 2. 每次浏览器自动化任务结束前,必须执行一次内存收尾检查:
710
+ `agent-browser-cleanup status`(或仓库源码内 `bash scripts/agent-browser-cleanup.sh status`)。
711
+ 3. `9226` 无头 Chrome 优先用于 agent 自动化;若 `9226` 的 tab 数 > 10,或 RSS > 1500MB,
712
+ 必须执行 `agent-browser-cleanup auto` 或 `agent-browser-cleanup hard` 释放内存。释放后下次
713
+ 需要浏览器时再重新启动,不要为了“可能等会用”长期占着内存。
714
+ 4. `9223` 有头 Chrome 只在需要用户手动登录、手动查看动态效果、或必须共享可见页面时启动;
715
+ 任务结束且用户不再需要查看时,必须关闭本任务 tab,并在确认没有人工操作中的页面后再
716
+ `agent-browser-cleanup hard`。
717
+ 5. 禁止长期同时保留 `9223` + `9226` 两套 agent-browser Chrome 实例,除非当前任务明确需要。
718
+ 多会话并行时也必须各自用 namespace 隔离,任务完成各自清理自己创建的 tab。
719
+ 6. 禁止用 `kill -9` / `pkill -9` 强杀 Chrome;清理只能使用 `agent-browser-cleanup`、
720
+ `chrome-bridge headless-stop`,或 `/bin/kill -TERM` 这类可让 Chrome 正常退出的方式。
721
+
698
722
  ## 优先级
699
723
 
700
724
  1. 项目私有规则(AGENTS.private.md)
package/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  所有对 @routerhub/agent-rules 的重大更改都会记录在这个文件中。
4
4
 
5
+ ## [1.5.198] - 2026-09-08
6
+
7
+ ### Added
8
+
9
+ - **新增「agent-browser 内存收尾铁律」与 `agent-browser-cleanup` 命令**:要求每次 browser 自动化任务结束关闭本任务 tab,并检查 `9223` / `9226` 的 tab 数与 RSS;`9226` 超过 10 个 tab 或 1500MB 时必须释放,避免 Chrome Profile 长期堆积导致 agent-browser 越用越卡。新增 `scripts/agent-browser-cleanup.sh`,支持 `status` / `soft` / `auto` / `hard` 四种模式,默认只读状态,释放时使用正常退出方式,禁止 `kill -9` 强杀 Chrome。
10
+
5
11
  ## [1.5.164] - 2026-08-31
6
12
 
7
13
  ### Changed
package/package.json CHANGED
@@ -1,15 +1,17 @@
1
1
  {
2
2
  "name": "@routerhub/agent-rules",
3
- "version": "1.5.197",
3
+ "version": "1.5.198",
4
4
  "description": "Shared Copilot agent rules and guidelines for RouterHub projects",
5
5
  "main": "AGENTS.base.md",
6
6
  "bin": {
7
- "agent-rules": "merge.js"
7
+ "agent-rules": "merge.js",
8
+ "agent-browser-cleanup": "scripts/agent-browser-cleanup.sh"
8
9
  },
9
10
  "files": [
10
11
  "AGENTS.base.md",
11
12
  "rules/",
12
13
  "skills/",
14
+ "scripts/agent-browser-cleanup.sh",
13
15
  "merge.js",
14
16
  "AGENTS.private.example.md",
15
17
  "PULL_REQUEST_TEMPLATE.md",
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ MODE="${1:-status}"
5
+ TAB_LIMIT="${AGENT_BROWSER_TAB_LIMIT:-10}"
6
+ RSS_LIMIT_MB="${AGENT_BROWSER_RSS_LIMIT_MB:-1500}"
7
+
8
+ profileForPort() {
9
+ case "$1" in
10
+ 9223) printf '%s\n' "$HOME/.agent-browser-profile" ;;
11
+ 9226) printf '%s\n' "$HOME/.agent-browser-profile-headless" ;;
12
+ *) printf '' ;;
13
+ esac
14
+ }
15
+
16
+ usage() {
17
+ cat <<'USAGE'
18
+ Usage: scripts/agent-browser-cleanup.sh [status|soft|hard|auto]
19
+
20
+ Modes:
21
+ status 只查看 9223/9226 的标签页数量与内存占用
22
+ soft 关闭 about:blank、newtab、devtools 等无业务标签页
23
+ hard 优雅退出 agent-browser 专用 Chrome(9223/9226),释放内存
24
+ auto tab 数或 RSS 超阈值时自动执行 hard,否则只输出状态
25
+
26
+ Env:
27
+ AGENT_BROWSER_TAB_LIMIT 默认 10
28
+ AGENT_BROWSER_RSS_LIMIT_MB 默认 1500
29
+ USAGE
30
+ }
31
+
32
+ if [[ "$MODE" == "-h" || "$MODE" == "--help" ]]; then
33
+ usage
34
+ exit 0
35
+ fi
36
+
37
+ if [[ "$MODE" != "status" && "$MODE" != "soft" && "$MODE" != "hard" && "$MODE" != "auto" ]]; then
38
+ usage >&2
39
+ exit 2
40
+ fi
41
+
42
+ portStatus() {
43
+ local portNumber="$1"
44
+ local profileDir=""
45
+ local tabCount="0"
46
+ local rssMb="0"
47
+ local profileArg=""
48
+
49
+ profileDir=$(profileForPort "$portNumber")
50
+ profileArg="--user-data-dir=${profileDir}"
51
+ rssMb=$(ps -axo rss=,command= | awk -v profileArg="$profileArg" '
52
+ {
53
+ for (fieldIndex = 2; fieldIndex <= NF; fieldIndex++) {
54
+ if ($fieldIndex == profileArg) {
55
+ sum += $1
56
+ break
57
+ }
58
+ }
59
+ }
60
+ END {printf "%d", sum / 1024}
61
+ ')
62
+
63
+ if curl -sf "http://127.0.0.1:${portNumber}/json/list" >/tmp/agent-browser-tabs-${portNumber}.json 2>/dev/null; then
64
+ tabCount=$(jq 'length' "/tmp/agent-browser-tabs-${portNumber}.json" 2>/dev/null || echo "0")
65
+ fi
66
+
67
+ rm -f "/tmp/agent-browser-tabs-${portNumber}.json"
68
+ printf '%s %s %s\n' "$portNumber" "$tabCount" "$rssMb"
69
+ }
70
+
71
+ printStatus() {
72
+ printf 'port\ttabs\trssMB\n'
73
+ portStatus 9223 | awk '{printf "%s\t%s\t%s\n", $1, $2, $3}'
74
+ portStatus 9226 | awk '{printf "%s\t%s\t%s\n", $1, $2, $3}'
75
+ }
76
+
77
+ closeChromeByPort() {
78
+ local portNumber="$1"
79
+ local profileDir=""
80
+ local processIds=""
81
+ profileDir=$(profileForPort "$portNumber")
82
+ processIds=$(ps -axo pid=,command= | awk -v portArg="--remote-debugging-port=${portNumber}" -v profileArg="--user-data-dir=${profileDir}" '
83
+ {
84
+ hasPort = 0
85
+ hasProfile = 0
86
+ for (fieldIndex = 2; fieldIndex <= NF; fieldIndex++) {
87
+ if ($fieldIndex == portArg) hasPort = 1
88
+ if ($fieldIndex == profileArg) hasProfile = 1
89
+ }
90
+ if (hasPort && hasProfile) print $1
91
+ }
92
+ ')
93
+ if [[ -z "$processIds" ]]; then
94
+ echo "port ${portNumber}: no agent-browser Chrome process"
95
+ return
96
+ fi
97
+
98
+ echo "port ${portNumber}: graceful quit"
99
+ printf '%s\n' "$processIds" | xargs -n1 /bin/kill -TERM
100
+ }
101
+
102
+ softCleanup() {
103
+ local portNumber="$1"
104
+ if ! curl -sf "http://127.0.0.1:${portNumber}/json/list" >/tmp/agent-browser-tabs-${portNumber}.json 2>/dev/null; then
105
+ echo "port ${portNumber}: not reachable"
106
+ return
107
+ fi
108
+
109
+ jq -r '.[] | select(.url | test("^(about:blank|chrome://newtab|devtools://|chrome://)")).id' "/tmp/agent-browser-tabs-${portNumber}.json" |
110
+ while IFS= read -r tabId; do
111
+ [[ -z "$tabId" ]] && continue
112
+ curl -sf "http://127.0.0.1:${portNumber}/json/close/${tabId}" >/dev/null || true
113
+ echo "port ${portNumber}: closed idle tab ${tabId}"
114
+ done
115
+
116
+ rm -f "/tmp/agent-browser-tabs-${portNumber}.json"
117
+ }
118
+
119
+ needsHardCleanup() {
120
+ local overLimit="0"
121
+ while read -r portNumber tabCount rssMb; do
122
+ if (( tabCount > TAB_LIMIT || rssMb > RSS_LIMIT_MB )); then
123
+ echo "port ${portNumber}: over limit tabs=${tabCount}/${TAB_LIMIT} rssMB=${rssMb}/${RSS_LIMIT_MB}"
124
+ overLimit="1"
125
+ fi
126
+ done < <(portStatus 9223 && portStatus 9226)
127
+ [[ "$overLimit" == "1" ]]
128
+ }
129
+
130
+ case "$MODE" in
131
+ status)
132
+ printStatus
133
+ ;;
134
+ soft)
135
+ softCleanup 9223
136
+ softCleanup 9226
137
+ printStatus
138
+ ;;
139
+ hard)
140
+ closeChromeByPort 9223
141
+ closeChromeByPort 9226
142
+ ;;
143
+ auto)
144
+ printStatus
145
+ if needsHardCleanup; then
146
+ closeChromeByPort 9223
147
+ closeChromeByPort 9226
148
+ else
149
+ echo "agent-browser Chrome is below cleanup thresholds"
150
+ fi
151
+ ;;
152
+ esac