wechat-claude-sessions 1.0.1-dev → 1.0.1-dev.usage-limit-detection.4.76a0129

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/README.en.md CHANGED
@@ -85,6 +85,7 @@ Send these from WeChat to control routing:
85
85
  | `/runs` | List running `/run` task sessions |
86
86
  | `/stop <name>` | Kill a `/run` task session (names start with `wc-`) |
87
87
  | `/close <number\|name\|pid>` | Close a Claude session remotely — terminates its Claude process (and MCP server) and removes it from the list. Unsaved work in that session is lost. If a name matches several sessions it lists them instead of guessing; `/close <name> all` closes all matches, `/close idle` cleans up every unmonitored session idle for 2+ hours. Every close reply ends with a remaining-session summary |
88
+ | `/usage` | Check whether Claude's usage limit is what's blocking replies. Runs one tiny headless probe (Haiku by default — see below) |
88
89
  | `/help` | Show command help |
89
90
  | *(no prefix)* | Send to the most recently active **monitoring** session (falls back to most recently active overall) |
90
91
 
@@ -92,6 +93,47 @@ Delivery feedback: if a message lands in a session that isn't monitoring its
92
93
  inbox, the daemon warns you immediately; if a delivered message is still
93
94
  unread after 2 minutes, it sends a reminder.
94
95
 
96
+ ### Usage limits (when every session goes quiet)
97
+
98
+ When the Claude account hits its usage limit, **every** session goes silent at
99
+ once: model calls are rejected, so nothing is read and nothing is answered —
100
+ from WeChat that is indistinguishable from a dead daemon. So the daemon works
101
+ it out itself:
102
+
103
+ 1. A delivered message with no reply for 90 seconds, **and** a target session
104
+ whose *own* transcript (`~/.claude/projects/….jsonl` — the MCP server records
105
+ the exact path, so a busy neighbour sharing the directory can't mask it)
106
+ hasn't been written for 60 seconds — i.e. it isn't busy, it is stuck —
107
+ triggers one headless probe (`claude -p ok`, with no MCP servers loaded).
108
+ 2. If the probe confirms a usage limit, the daemon says so on WeChat with the
109
+ expected reset time, and suppresses the misleading "that session may not be
110
+ monitoring" reminder. Messages you send meanwhile are still delivered, with
111
+ a note attached.
112
+ 3. When the limit lifts, the daemon sends a recovery message and gets the
113
+ unanswered work moving: messages still in an inbox only need their watcher
114
+ poked (the original announcement is long gone and never repeats); messages
115
+ Claude *read* but never got to answer are put back in the inbox first, since
116
+ there is nothing left to announce. Only deliveries with no reply are
117
+ replayed, so nothing is handled twice.
118
+
119
+ Probes cost a little quota, so: only one runs at a time; at most one every 5
120
+ minutes while things look healthy; once a limit is confirmed probes are free
121
+ (the request is rejected outright), and a known reset time is waited out.
122
+
123
+ Optional config (`~/.claude/wechat/config.json`):
124
+
125
+ ```json
126
+ { "probeModel": "claude-haiku-4-5-20251001", "claudeBin": "~/.local/bin/claude" }
127
+ ```
128
+
129
+ - `probeModel` — model used for the probe. Haiku by default: cheap, and
130
+ subscription limits are account-wide, so it is rejected by the same limit.
131
+ Set `"default"` to probe with your own default model — needed if the limit you
132
+ care about is an Opus-only weekly cap.
133
+ - `claudeBin` — path to the `claude` executable, only needed when launchd's
134
+ minimal PATH can't find it (`~/.local/bin`, `~/.claude/local`, Homebrew and
135
+ `/usr/local/bin` are tried).
136
+
95
137
  Incoming images are downloaded and decrypted automatically to
96
138
  `~/.claude/wechat/media/` (cleaned up after 7 days); the routed message text
97
139
  contains the local path (`[图片: /path/to/file.png]`) so the receiving Claude
@@ -151,6 +193,11 @@ Use `wechat_set_session_name` to set a custom name.
151
193
  ├── daemon.log # daemon output
152
194
  ├── cursor.txt # message polling cursor
153
195
  ├── expired.flag # present when the WeChat login has expired
196
+ ├── usage-limit.json # known Claude usage-limit state (incl. notified users)
197
+ ├── replies/ # last time each session replied to each WeChat user
198
+ │ └── <pid>--<userId>
199
+ ├── nudge/ # signal to re-announce a backlog (after a limit lifts)
200
+ │ └── <pid>
154
201
  ├── sessions/ # registered Claude Code sessions
155
202
  │ └── <pid>.json
156
203
  ├── inbox/ # per-session message queues
@@ -204,6 +251,9 @@ server's pid.
204
251
  4. **MCP Server** reads from its inbox when Claude calls `wechat_get_messages`
205
252
  5. Claude processes the message and replies via `wechat_send_text`
206
253
  6. The MCP server clears the typing indicator file; the daemon detects this and stops typing
254
+ 7. If a message goes unread *and* unanswered for too long, the daemon checks
255
+ whether Claude's usage limit is the cause and explains it on WeChat (see
256
+ [Usage limits](#usage-limits-when-every-session-goes-quiet))
207
257
 
208
258
  ## Language
209
259
 
package/README.md CHANGED
@@ -78,12 +78,42 @@ wechat-claude daemon # 前台运行 / 交给你自己的进程管理
78
78
  | `/runs` | 列出运行中的 `/run` 任务 session |
79
79
  | `/stop <名字>` | 终止一个 `/run` 任务 session(名字以 `wc-` 开头) |
80
80
  | `/close <编号\|名字\|pid>` | 远程关闭一个 Claude session —— 终结它的 Claude 进程(以及 MCP server)并从列表移除。该 session 里未保存的工作会丢失。如果一个名字匹配到多个,会列出来而不是瞎猜;`/close <名字> all` 关闭全部匹配项,`/close idle` 清理所有闲置 2 小时以上且未被监控的 session。每次关闭的回复末尾都会附上剩余 session 概览 |
81
+ | `/usage` | 查询 Claude 用量是否已达上限(session 集体不回复时用)。会跑一次极小的 headless 探测(默认用 Haiku,见下) |
81
82
  | `/help` | 显示命令帮助 |
82
83
  | *(无前缀)* | 发给最近活跃的**监控中** session(没有的话退回到最近活跃的那个) |
83
84
 
84
85
  **投递反馈**:如果消息落到了一个没在监控 inbox 的 session,daemon 会立刻警告你;如果一条
85
86
  已投递的消息 2 分钟后仍未被读取,它会再发一条提醒。
86
87
 
88
+ ### 用量上限(session 集体不回复)
89
+
90
+ Claude 账号一旦触达用量上限,**所有** session 会同时哑掉:模型调用被拒绝,消息既没人读、
91
+ 也没人回,从微信看和 daemon 挂了一模一样。为此 daemon 会自己判断:
92
+
93
+ 1. 一条已投递的消息 90 秒没有得到回复,**并且**目标 session 自己的 transcript
94
+ (`~/.claude/projects/…jsonl`,由 MCP server 记下确切路径,不会被同目录下别的 session
95
+ 干扰)也 60 秒没有任何写入 —— 即它不是在忙,而是真的停住了 —— daemon 会跑一次 headless
96
+ 探测(`claude -p ok`,不加载任何 MCP server)。
97
+ 2. 探测确认是用量上限,daemon 会主动在微信里说明情况,附上预计恢复时间,并且不再发那条
98
+ 容易误导人的"session 可能没在监控"提醒。期间新发的消息照常投递,但会附一条提示。
99
+ 3. 上限解除后,daemon 会再发一条"已恢复",并把没被回答的消息重新推起来:还留在 inbox 里的,
100
+ 戳一下让 watcher 重新播报(否则那条播报早过去了);已经被读走、但还没来得及回复的(inbox
101
+ 已经被清空,没东西可播报),会先塞回 inbox 再播报。只有确实没得到回复的才会重来,不会重复处理。
102
+
103
+ 探测本身要花一点点额度,所以:同一时间只跑一个;正常状态下最多 5 分钟一次;确认受限之后
104
+ 探测是免费的(请求被直接拒绝),有明确恢复时间时会等到那个时间点再查。
105
+
106
+ 可选配置(`~/.claude/wechat/config.json`):
107
+
108
+ ```json
109
+ { "probeModel": "claude-haiku-4-5-20251001", "claudeBin": "~/.local/bin/claude" }
110
+ ```
111
+
112
+ - `probeModel` —— 探测用的模型,默认 Haiku(便宜,且订阅的用量上限是账号级的,拒绝同样会
113
+ 发生)。设成 `"default"` 用你自己的默认模型 —— 如果你想抓的是 Opus 专属的周上限,就得这么设。
114
+ - `claudeBin` —— `claude` 可执行文件路径,launchd 环境下 PATH 很干净时才需要(默认会依次
115
+ 找 `~/.local/bin`、`~/.claude/local`、Homebrew、`/usr/local/bin`)。
116
+
87
117
  **图片**会被自动下载解密到 `~/.claude/wechat/media/`(保留 7 天);路由后的消息文本里带
88
118
  本地路径(`[图片: /path/to/file.png]`),接收方的 Claude session 可以直接打开该文件。
89
119
 
@@ -138,6 +168,11 @@ Session 会根据工作目录自动命名:
138
168
  ├── daemon.log # daemon 输出
139
169
  ├── cursor.txt # 消息轮询游标
140
170
  ├── expired.flag # 微信登录过期时存在
171
+ ├── usage-limit.json # 已知的 Claude 用量上限状态(含已通知的用户)
172
+ ├── replies/ # 各 session 最近一次回复各微信用户的时间戳
173
+ │ └── <pid>--<userId>
174
+ ├── nudge/ # 让 watcher 重播积压 inbox 的信号(上限解除后)
175
+ │ └── <pid>
141
176
  ├── sessions/ # 已注册的 Claude Code session
142
177
  │ └── <pid>.json
143
178
  ├── inbox/ # 各 session 的消息队列
@@ -189,6 +224,8 @@ wechat-claude daemon restart
189
224
  4. **MCP Server** 在 Claude 调用 `wechat_get_messages` 时读取自己的 inbox
190
225
  5. Claude 处理消息并通过 `wechat_send_text` 回复
191
226
  6. MCP server 清除输入状态文件,daemon 检测到后停止"正在输入"
227
+ 7. 如果长时间既没被读也没被回,daemon 会检查是不是撞上了 Claude 用量上限,并在微信里说明
228
+ (见[用量上限](#用量上限session-集体不回复))
192
229
 
193
230
  ## 语言
194
231
 
package/dist/cli.js CHANGED
@@ -13,6 +13,7 @@ import { fileURLToPath } from "node:url";
13
13
  import { ILinkClient } from "./ilink.js";
14
14
  import { pkgFile } from "./pkg-root.js";
15
15
  import { DAEMON_PID_FILE, ensureDirs, WECHAT_DIR } from "./paths.js";
16
+ import { classifyMcpRegistration } from "./utils.js";
16
17
  import * as launchd from "./launchd.js";
17
18
  const DAEMON_JS = fileURLToPath(new URL("./daemon.js", import.meta.url));
18
19
  const SERVER_JS = fileURLToPath(new URL("./server.js", import.meta.url));
@@ -26,18 +27,18 @@ function mcpAdd() {
26
27
  stdio: ["ignore", "pipe", "pipe"],
27
28
  }).status === 0);
28
29
  }
29
- // What the registration currently points at, or undefined if there is none.
30
- // `claude mcp get` prints a human-readable block; we only need our own path
31
- // out of it, so look for it rather than parsing the format.
32
- function mcpRegisteredPath() {
30
+ // What the registration currently points at. `claude mcp get` prints a
31
+ // human-readable block; classifyMcpRegistration looks for this install's
32
+ // exact path in it (robust to spaces in the path) and treats any successful
33
+ // output that lacks it as an existing registration to replace — a get that
34
+ // succeeds must never be read as "no registration", or the stale entry
35
+ // survives and `claude mcp add` keeps no-opping against it.
36
+ function mcpRegistration() {
33
37
  const r = spawnSync("claude", ["mcp", "get", MCP_NAME], {
34
38
  stdio: ["ignore", "pipe", "ignore"],
35
39
  });
36
- if (r.status !== 0 || !r.stdout)
37
- return undefined;
38
- const text = r.stdout.toString();
39
- const match = text.match(/\S*dist[/\\]server\.js/);
40
- return match?.[0];
40
+ const stdout = r.status === 0 && r.stdout ? r.stdout.toString() : undefined;
41
+ return classifyMcpRegistration(stdout, SERVER_JS);
41
42
  }
42
43
  function registerMcpServer() {
43
44
  out(`→ Registering MCP server (claude mcp add ${MCP_NAME})…`);
@@ -46,24 +47,24 @@ function registerMcpServer() {
46
47
  // install, which breaks as soon as that copy is gone. So replace an existing
47
48
  // registration rather than trusting the exit code.
48
49
  mcpAdd();
49
- let registered = mcpRegisteredPath();
50
- if (registered !== undefined && registered !== SERVER_JS) {
51
- out(` • replacing a registration that points at ${registered}`);
50
+ let registered = mcpRegistration();
51
+ if (registered.kind === "other") {
52
+ out(` • replacing a registration that points at ${registered.target ?? "another install"}`);
52
53
  spawnSync("claude", ["mcp", "remove", "--scope", "user", MCP_NAME], {
53
54
  stdio: ["ignore", "pipe", "pipe"],
54
55
  });
55
56
  mcpAdd();
56
- registered = mcpRegisteredPath();
57
+ registered = mcpRegistration();
57
58
  }
58
- if (registered === SERVER_JS) {
59
+ if (registered.kind === "current") {
59
60
  out(" ✓ registered");
60
61
  }
61
- else if (registered === undefined) {
62
+ else if (registered.kind === "none") {
62
63
  out(" ! failed — is the `claude` CLI on your PATH?");
63
64
  out(` Register it manually: claude mcp add --scope user ${MCP_NAME} node ${SERVER_JS}`);
64
65
  }
65
66
  else {
66
- out(` ! still registered to ${registered}, not this install`);
67
+ out(` ! still registered to ${registered.target ?? "another install"}, not this install`);
67
68
  out(` Fix it with: claude mcp remove --scope user ${MCP_NAME}`);
68
69
  }
69
70
  }
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
package/dist/daemon.js CHANGED
@@ -6,10 +6,14 @@ import { spawnSync } from "node:child_process";
6
6
  import { ILinkClient } from "./ilink.js";
7
7
  import { clearBinding, clearBindingsToSession, getBinding, setBinding, } from "./bindings.js";
8
8
  import { clearHeartbeat, isMonitoring } from "./monitoring.js";
9
+ import { nudgeSession } from "./nudge.js";
10
+ import { lastReplyAt, pruneReplies } from "./replies.js";
11
+ import { looksStalled } from "./transcripts.js";
12
+ import { probeUsageLimit, readUsageState, resetHint, shouldProbe, writeUsageState, } from "./usage.js";
9
13
  import { CONFIG_FILE, CURSOR_FILE, DAEMON_PID_FILE, EXPIRED_FLAG_FILE, INBOX_DIR, MEDIA_DIR, SESSIONS_DIR, TYPING_DIR, WECHAT_DIR, ensureDirs as ensureWechatDirs, isProcessAlive, } from "./paths.js";
10
14
  import { assignSessionNumbers } from "./session-numbers.js";
11
15
  import { findSession, getDefaultTarget, listSessions, matchSessions, sessionLabel, sortedSessions, } from "./sessions.js";
12
- import { writeToInbox } from "./inbox.js";
16
+ import { peekInbox, writeToInbox } from "./inbox.js";
13
17
  import { CLAUDE_CONFIG_FILE, ensureBypassAccepted } from "./claude-config.js";
14
18
  import { formatAgo, getLang, marker, t } from "./i18n.js";
15
19
  import { hasTmux, isSafeSessionName, killTmuxSession, listTmuxSessions, newTmuxSession, sanitizeForSessionName, tmuxSessionExists, } from "./tmux.js";
@@ -21,6 +25,12 @@ const MEDIA_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
21
25
  const UNREAD_WARN_AFTER_MS = 120_000;
22
26
  const DELIVERY_EXPIRE_MS = 600_000;
23
27
  const IDLE_MS = 2 * 60 * 60 * 1000;
28
+ // A delivery with no answer for this long is a candidate for a usage-limit
29
+ // check — but only if the session also looks stalled (see sessionStalled).
30
+ const SILENT_PROBE_AFTER_MS = 90_000;
31
+ // A session that is genuinely working writes to its transcript constantly; one
32
+ // whose model calls are being rejected writes nothing at all.
33
+ const TRANSCRIPT_IDLE_MS = 60_000;
24
34
  function cleanOldMedia() {
25
35
  try {
26
36
  const now = Date.now();
@@ -141,42 +151,234 @@ function isTypingActive(userId) {
141
151
  }
142
152
  const extractText = sharedExtractText;
143
153
  const deliveries = [];
144
- function trackDelivery(msgId, sessionId, targetName, fromUserId) {
154
+ function trackDelivery(msg, sessionId, targetName) {
145
155
  deliveries.push({
146
- msgId,
156
+ msg,
147
157
  sessionId,
148
158
  targetName,
149
- fromUserId,
150
159
  deliveredAt: Date.now(),
151
160
  warned: false,
161
+ probed: false,
152
162
  });
153
163
  if (deliveries.length > 200)
154
164
  deliveries.shift();
155
165
  }
166
+ function inboxHas(sessionId, msgId) {
167
+ try {
168
+ const inbox = JSON.parse(fs.readFileSync(path.join(INBOX_DIR, `${sessionId}.json`), "utf-8"));
169
+ return inbox.some((m) => m.id === msgId);
170
+ }
171
+ catch {
172
+ return false;
173
+ }
174
+ }
175
+ // Has the session gone quiet, or is it just busy? Claude Code appends to its
176
+ // transcript on every step, so a still-working session is never "stalled" —
177
+ // which keeps long tasks from triggering (and paying for) usage probes.
178
+ function sessionStalled(sessionId) {
179
+ const session = listSessions().find((s) => s.id === sessionId);
180
+ if (!session)
181
+ return false;
182
+ return looksStalled(session, TRANSCRIPT_IDLE_MS);
183
+ }
184
+ // Deliveries this user is still waiting on an answer for.
185
+ function pendingFor(userId) {
186
+ return deliveries.filter((d) => d.msg.fromUserId === userId);
187
+ }
156
188
  function startDeliveryWatcher(client) {
157
189
  setInterval(() => {
158
190
  const now = Date.now();
159
191
  for (let i = deliveries.length - 1; i >= 0; i--) {
160
192
  const d = deliveries[i];
161
- let stillPending = false;
162
- try {
163
- const inbox = JSON.parse(fs.readFileSync(path.join(INBOX_DIR, `${d.sessionId}.json`), "utf-8"));
164
- stillPending = inbox.some((m) => m.id === d.msgId);
193
+ // That session answered this user after the delivery — done watching.
194
+ if (lastReplyAt(d.sessionId, d.msg.fromUserId) > d.deliveredAt) {
195
+ deliveries.splice(i, 1);
196
+ continue;
165
197
  }
166
- catch { }
167
- if (!stillPending || now - d.deliveredAt > DELIVERY_EXPIRE_MS) {
198
+ if (now - d.deliveredAt > DELIVERY_EXPIRE_MS) {
168
199
  deliveries.splice(i, 1);
169
200
  continue;
170
201
  }
171
- if (!d.warned && now - d.deliveredAt > UNREAD_WARN_AFTER_MS) {
202
+ const stillPending = inboxHas(d.sessionId, d.msg.id);
203
+ // Silence can mean "unread" (never picked up) or "read but no reply"
204
+ // (the model call was rejected mid-turn). Both look like a usage limit,
205
+ // so check it before blaming the session.
206
+ if (!d.probed &&
207
+ now - d.deliveredAt > SILENT_PROBE_AFTER_MS &&
208
+ sessionStalled(d.sessionId)) {
209
+ d.probed = true;
210
+ void checkUsage(client);
211
+ }
212
+ if (!d.warned && stillPending && now - d.deliveredAt > UNREAD_WARN_AFTER_MS) {
172
213
  d.warned = true;
214
+ // A confirmed usage limit already explains the silence, and does it
215
+ // better than "that session may not be monitoring".
216
+ if (isLimitedNow())
217
+ continue;
173
218
  client
174
- .sendText(d.fromUserId, t().unreadWarn(d.targetName))
219
+ .sendText(d.msg.fromUserId, t().unreadWarn(d.targetName))
175
220
  .catch((err) => log(`Unread warning failed: ${err}`));
176
221
  }
177
222
  }
178
223
  }, 20_000);
179
224
  }
225
+ // ---------------------------------------------------------------------------
226
+ // Claude usage limit
227
+ //
228
+ // When the account's limit is reached every session stops answering at once,
229
+ // which from WeChat is indistinguishable from a dead daemon. The daemon probes
230
+ // for it, says so, and follows up when the limit lifts.
231
+ // ---------------------------------------------------------------------------
232
+ let usageState = readUsageState();
233
+ function saveUsageState() {
234
+ writeUsageState(usageState);
235
+ }
236
+ // Limited, as far as we know right now. Once the reported reset time has
237
+ // passed the state is stale — the watcher re-probes rather than keep claiming
238
+ // a limit that may already be over.
239
+ function isLimitedNow(now = Date.now()) {
240
+ if (!usageState.limited)
241
+ return false;
242
+ return usageState.resetAt === undefined || now < usageState.resetAt;
243
+ }
244
+ function hintFor(now = Date.now()) {
245
+ return resetHint(usageState, now) ?? t().usageResetUnknown;
246
+ }
247
+ function waitingSummary(userId) {
248
+ const pending = pendingFor(userId);
249
+ if (pending.length === 0)
250
+ return "";
251
+ const body = pending
252
+ .map((d) => `• ${d.targetName}: ${d.msg.text.replace(/\s+/g, " ").slice(0, 40)}`)
253
+ .join("\n");
254
+ return t().usageWaitingList(body);
255
+ }
256
+ function notifyLimited(client, userId) {
257
+ if (usageState.notified.includes(userId))
258
+ return;
259
+ usageState.notified.push(userId);
260
+ saveUsageState();
261
+ client
262
+ .sendText(userId, t().usageLimited(hintFor(), waitingSummary(userId)))
263
+ .catch((err) => log(`Usage-limit notice failed: ${err}`));
264
+ }
265
+ // Tell everyone who is currently waiting on a session, not just the user whose
266
+ // message triggered the probe.
267
+ function announceLimited(client) {
268
+ for (const userId of new Set(deliveries.map((d) => d.msg.fromUserId))) {
269
+ notifyLimited(client, userId);
270
+ }
271
+ }
272
+ // Get unanswered work moving again after a limit lifts.
273
+ //
274
+ // Two shapes to repair. A message still in the inbox only needs its watcher
275
+ // poked: the announcement it made while Claude could not react never repeats on
276
+ // its own. A message Claude *read* and then failed to answer is worse — the
277
+ // inbox was already drained, so there is nothing left to announce and nothing
278
+ // to poke; it has to go back in the inbox first. Only deliveries with no reply
279
+ // are requeued, so nothing is handled twice.
280
+ //
281
+ // Deliveries are in-memory, so a daemon restart during a limit loses the
282
+ // read-but-unanswered ones; the still-in-inbox case survives on disk.
283
+ function resumePendingDeliveries() {
284
+ const live = new Set(listSessions().map((s) => s.id));
285
+ let requeued = 0;
286
+ for (const d of deliveries) {
287
+ if (!live.has(d.sessionId))
288
+ continue;
289
+ if (inboxHas(d.sessionId, d.msg.id))
290
+ continue;
291
+ writeToInbox(d.sessionId, d.msg);
292
+ requeued += 1;
293
+ }
294
+ let nudged = 0;
295
+ for (const session of listSessions()) {
296
+ if (peekInbox(session.id) === 0)
297
+ continue;
298
+ nudgeSession(session.id);
299
+ nudged += 1;
300
+ }
301
+ return { nudged, requeued };
302
+ }
303
+ function applyProbe(client, probe) {
304
+ const now = Date.now();
305
+ if (probe.state === "limited") {
306
+ if (!usageState.limited) {
307
+ log(`Claude usage limit detected: ${probe.detail}`);
308
+ usageState = {
309
+ limited: true,
310
+ since: now,
311
+ resetAt: probe.resetAt,
312
+ resetText: probe.resetText,
313
+ lastProbeAt: now,
314
+ notified: [],
315
+ };
316
+ notifyMacOS(t().usageMacNotice(hintFor(now)));
317
+ }
318
+ else {
319
+ usageState.lastProbeAt = now;
320
+ usageState.resetAt = probe.resetAt ?? usageState.resetAt;
321
+ usageState.resetText = probe.resetText ?? usageState.resetText;
322
+ }
323
+ saveUsageState();
324
+ announceLimited(client);
325
+ return;
326
+ }
327
+ if (probe.state === "unknown") {
328
+ // Some other failure (CLI missing, network, timeout). Don't invent a limit
329
+ // — just record the attempt so probes stay throttled.
330
+ log(`Usage probe inconclusive: ${probe.detail}`);
331
+ usageState.lastProbeAt = now;
332
+ saveUsageState();
333
+ return;
334
+ }
335
+ if (!usageState.limited) {
336
+ usageState.lastProbeAt = now;
337
+ saveUsageState();
338
+ return;
339
+ }
340
+ const mins = Math.round((now - (usageState.since ?? now)) / 60_000);
341
+ const audience = new Set([
342
+ ...usageState.notified,
343
+ ...deliveries.map((d) => d.msg.fromUserId),
344
+ ]);
345
+ usageState = { limited: false, lastProbeAt: now, notified: [] };
346
+ saveUsageState();
347
+ const { nudged, requeued } = resumePendingDeliveries();
348
+ log(`Claude usage limit lifted after ${mins}m; requeued ${requeued} message(s), nudged ${nudged} session(s)`);
349
+ const tail = nudged > 0 ? t().usagePendingNudged(nudged) : t().usagePendingNone;
350
+ for (const userId of audience) {
351
+ client
352
+ .sendText(userId, t().usageRecovered(mins, tail))
353
+ .catch((err) => log(`Usage-recovery notice failed: ${err}`));
354
+ }
355
+ }
356
+ // One probe at a time, throttled unless forced (the /usage command).
357
+ let probeInFlight;
358
+ async function checkUsage(client, force = false) {
359
+ if (probeInFlight)
360
+ return probeInFlight;
361
+ if (!force && !shouldProbe(usageState, Date.now()))
362
+ return undefined;
363
+ probeInFlight = probeUsageLimit();
364
+ try {
365
+ const probe = await probeInFlight;
366
+ applyProbe(client, probe);
367
+ return probe;
368
+ }
369
+ finally {
370
+ probeInFlight = undefined;
371
+ }
372
+ }
373
+ // While limited, keep checking so recovery is announced without the user
374
+ // having to poke it — rejected probes are instant and cost nothing.
375
+ function startUsageWatcher(client) {
376
+ setInterval(() => {
377
+ if (!usageState.limited)
378
+ return;
379
+ void checkUsage(client);
380
+ }, 30_000);
381
+ }
180
382
  function notifyMacOS(message) {
181
383
  if (process.platform !== "darwin")
182
384
  return;
@@ -422,6 +624,18 @@ function handleRunCommand(client, msg, args) {
422
624
  log(`Started tmux session "${sessionName}" in ${cwd}: ${task}`);
423
625
  sendReply(m.runStarted(path.basename(cwd), task, auto ? m.permSkip : m.permSafe, sessionName) + (bypass === "accepted" ? `\n\n${m.bypassAutoAccepted}` : ""));
424
626
  }
627
+ // A message delivered during a known limit episode would otherwise vanish into
628
+ // silence, so say so immediately instead of waiting for the watchdog.
629
+ function noteUsageLimit(client, userId) {
630
+ if (!isLimitedNow())
631
+ return;
632
+ if (!usageState.notified.includes(userId))
633
+ usageState.notified.push(userId);
634
+ saveUsageState();
635
+ client
636
+ .sendText(userId, t().usageNoteOnSend(hintFor()))
637
+ .catch((err) => log(`Usage-limit note failed: ${err}`));
638
+ }
425
639
  function routeMessage(client, msg) {
426
640
  const text = msg.text.trim();
427
641
  const lang = getLang();
@@ -431,6 +645,28 @@ function routeMessage(client, msg) {
431
645
  log(`Reply failed: ${err}`);
432
646
  });
433
647
  };
648
+ if (text === "/usage" || text === "/limit" || text === "/用量") {
649
+ sendReply(m.usageChecking);
650
+ void (async () => {
651
+ const probe = await checkUsage(client, true);
652
+ if (!probe)
653
+ return;
654
+ if (probe.state === "limited") {
655
+ const now = Date.now();
656
+ const mins = Math.round((now - (usageState.since ?? now)) / 60_000);
657
+ // Answering here counts as telling this user about the episode, so the
658
+ // watcher does not send them the same news a second time.
659
+ if (!usageState.notified.includes(msg.fromUserId)) {
660
+ usageState.notified.push(msg.fromUserId);
661
+ saveUsageState();
662
+ }
663
+ sendReply(m.usageStatusLimited(hintFor(now), mins));
664
+ return;
665
+ }
666
+ sendReply(probe.state === "ok" ? m.usageOk : m.usageUnknown(probe.detail));
667
+ })();
668
+ return;
669
+ }
434
670
  const runMatch = text.match(/^\/run(?:\s+([\s\S]*))?$/);
435
671
  if (runMatch) {
436
672
  handleRunCommand(client, msg, runMatch[1] ?? "");
@@ -627,11 +863,13 @@ function routeMessage(client, msg) {
627
863
  sendReply(m.notFound(selector));
628
864
  return;
629
865
  }
630
- writeToInbox(target.id, { ...msg, text: message });
631
- trackDelivery(msg.id, target.id, target.name, msg.fromUserId);
866
+ const routed = { ...msg, text: message };
867
+ writeToInbox(target.id, routed);
868
+ trackDelivery(routed, target.id, target.name);
632
869
  if (!isMonitoring(target.id)) {
633
870
  sendReply(m.deliveredUnmonitored(target.name));
634
871
  }
872
+ noteUsageLimit(client, msg.fromUserId);
635
873
  markTyping(msg.fromUserId);
636
874
  client.startTypingKeepAlive(msg.fromUserId, () => isTypingActive(msg.fromUserId));
637
875
  log(`Routed to ${target.name} (${target.id})`);
@@ -654,10 +892,11 @@ function routeMessage(client, msg) {
654
892
  return;
655
893
  }
656
894
  writeToInbox(target.id, msg);
657
- trackDelivery(msg.id, target.id, target.name, msg.fromUserId);
895
+ trackDelivery(msg, target.id, target.name);
658
896
  if (!isMonitoring(target.id)) {
659
897
  sendReply(m.deliveredNoneMonitored(target.name));
660
898
  }
899
+ noteUsageLimit(client, msg.fromUserId);
661
900
  markTyping(msg.fromUserId);
662
901
  client.startTypingKeepAlive(msg.fromUserId);
663
902
  log(`Routed to ${target.name} (${target.id})`);
@@ -714,6 +953,9 @@ async function main() {
714
953
  startTypingWatcher(client);
715
954
  startDeliveryWatcher(client);
716
955
  startRunWatcher(client);
956
+ startUsageWatcher(client);
957
+ pruneReplies(listSessions().map((s) => s.id));
958
+ setInterval(() => pruneReplies(listSessions().map((s) => s.id)), 30 * 60_000);
717
959
  log(`Daemon started (pid ${process.pid}). Polling WeChat...`);
718
960
  let clearedExpiredFlag = false;
719
961
  while (true) {
@@ -756,8 +998,17 @@ async function main() {
756
998
  fs.writeFileSync(EXPIRED_FLAG_FILE, String(Date.now()));
757
999
  }
758
1000
  catch { }
1001
+ // The cursor belongs to the dead login session; a fresh login must
1002
+ // start from a fresh cursor or getupdates may keep rejecting it.
1003
+ try {
1004
+ fs.unlinkSync(CURSOR_FILE);
1005
+ }
1006
+ catch { }
759
1007
  notifyMacOS(t().loginExpired);
760
- break;
1008
+ // The watcher setIntervals keep the event loop alive, so breaking out
1009
+ // of the loop is not enough — without an explicit exit the process
1010
+ // lingers, holds the pid file, and blocks launchd from restarting.
1011
+ process.exit(1);
761
1012
  }
762
1013
  log(`Poll error: ${errMsg}`);
763
1014
  await new Promise((r) => setTimeout(r, 5000));