@gonzih/cc-discord 0.2.45 → 0.2.47

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.
@@ -155,6 +155,8 @@ function wireStdoutToRedis(proc, ns, wire) {
155
155
  const rawRedis = wire._redis;
156
156
  const streamCh = metaStreamChannel(ns);
157
157
  const logKey = metaLogKey(ns);
158
+ // Deduplicate rate_limit_event: Claude emits multiple per turn; only notify once per session.
159
+ let rateLimitNotified = false;
158
160
  const forwardEventToRedis = (eventJson) => {
159
161
  rawRedis.publish(streamCh, eventJson).catch((err) => {
160
162
  console.warn(`[meta-agent-manager] stream publish failed (ns=${ns}):`, err.message);
@@ -246,9 +248,9 @@ function wireStdoutToRedis(proc, ns, wire) {
246
248
  const rl = parsed.rate_limit_info;
247
249
  structuredEvent = parsed;
248
250
  // Throttled: five-hour window exhausted and no overage credits.
249
- // Claude will hang on API calls until the window resets notify Discord immediately
250
- // and signal done so the live message finalizes, rather than silently timing out.
251
- if (rl?.overageStatus === "rejected") {
251
+ // Claude emits multiple rate_limit_events per turn only act on the first.
252
+ if (rl?.overageStatus === "rejected" && !rateLimitNotified) {
253
+ rateLimitNotified = true;
252
254
  const resetsAt = rl.resetsAt ? new Date(rl.resetsAt * 1000).toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit", timeZoneName: "short" }) : "soon";
253
255
  const alertMsg = {
254
256
  id: crypto.randomUUID(),
package/dist/notifier.js CHANGED
@@ -274,7 +274,7 @@ export function startNotifier(bot, notifyChannelId, namespace, redis, handleUser
274
274
  function getLiveState(ns, targetChannelId) {
275
275
  let state = liveStates.get(ns);
276
276
  if (!state) {
277
- state = { text: "", toolStatus: "", targetChannelId, starting: false, liveStarted: false, finalTimer: null };
277
+ state = { text: "", toolStatus: "", targetChannelId, starting: false, liveStarted: false, finalTimer: null, pendingDone: false };
278
278
  liveStates.set(ns, state);
279
279
  }
280
280
  return state;
@@ -354,6 +354,13 @@ export function startNotifier(bot, notifyChannelId, namespace, redis, handleUser
354
354
  }
355
355
  // done: immediate finalization (fired by meta-agent-manager after result event)
356
356
  if (event === "done") {
357
+ if (state.starting) {
358
+ // startOrGetLiveMessage is still in flight — defer finalization to its .then() callback
359
+ // so we don't race: finalizeState runs before the Discord message exists → fallback
360
+ // sends a second plain message, leaving the live message orphaned with ▋.
361
+ state.pendingDone = true;
362
+ return;
363
+ }
357
364
  finalizeState(ns, state).catch((err) => {
358
365
  log("warn", `meta-agent done finalize failed (ns=${ns}):`, err.message);
359
366
  });
@@ -376,6 +383,12 @@ export function startNotifier(bot, notifyChannelId, namespace, redis, handleUser
376
383
  state.liveStarted = true;
377
384
  bot.updateLiveMessage(deliverTo, textDisplay);
378
385
  }
386
+ // done arrived while we were creating the message — finalize now
387
+ if (state.pendingDone) {
388
+ finalizeState(ns, state).catch((err) => {
389
+ log("warn", `meta-agent deferred done finalize failed (ns=${ns}):`, err.message);
390
+ });
391
+ }
379
392
  }).catch(() => { state.starting = false; });
380
393
  }
381
394
  // Text is buffered in state.text; applied when startOrGetLiveMessage resolves
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gonzih/cc-discord",
3
- "version": "0.2.45",
3
+ "version": "0.2.47",
4
4
  "description": "Claude Code Discord bot — chat with Claude Code via Discord",
5
5
  "type": "module",
6
6
  "bin": {