polygram 0.38.1 → 0.38.3

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.
@@ -605,19 +605,19 @@ function createHandleConfigCallback({
605
605
  for (const { scope, value } of cleared) scope[configField] = value;
606
606
  };
607
607
  // The user tapped one card; topics they never touched can change value
608
- // with it. Record which ones and what they held, so a config that moved
609
- // on its own is still explicable afterwards. Logged only once the sweep
610
- // is persisted — an event for a rolled-back sweep is worse than none.
608
+ // with it. Record WHICH ones, by id, so a config that moved on its own
609
+ // is still explicable afterwards — the values they held are
610
+ // configuration content and the ids answer the question without them.
611
+ // Logged only once the sweep is persisted — an event for a rolled-back
612
+ // sweep is worse than none.
611
613
  const logTopicRichTextSweep = (cleared) => {
612
614
  if (!cleared.length) return;
613
615
  dbWrite(() => {
614
616
  db.logEvent('config-topic-override-swept', {
615
617
  chat_id: chatId,
616
618
  field: configField,
617
- topics: cleared.map(({ threadId, value }) => ({
618
- thread_id: threadId,
619
- old_value: value,
620
- })),
619
+ topic_ids: cleared.map(({ threadId }) => String(threadId)),
620
+ topics_cleared: cleared.length,
621
621
  });
622
622
  }, 'log topic-override sweep');
623
623
  };
@@ -77,6 +77,7 @@ function createDispatcher({
77
77
  // Per-session in-flight handler count.
78
78
  const inFlightHandlers = new Map();
79
79
  const activeHandlers = new Set();
80
+ const activeHandlerTargets = new Set();
80
81
 
81
82
  function trackTask(task) {
82
83
  activeHandlers.add(task);
@@ -265,6 +266,15 @@ function createDispatcher({
265
266
  }
266
267
 
267
268
  function dispatchHandleMessage(sessionKey, chatId, msg, bot) {
269
+ const activeHandlerTarget = Object.freeze({
270
+ sessionKey: String(sessionKey),
271
+ chatId: String(chatId),
272
+ threadId: msg?.message_thread_id == null
273
+ ? null
274
+ : String(msg.message_thread_id),
275
+ telegramMessageId: String(msg?.message_id),
276
+ });
277
+ activeHandlerTargets.add(activeHandlerTarget);
268
278
  const count = (inFlightHandlers.get(sessionKey) || 0) + 1;
269
279
  inFlightHandlers.set(sessionKey, count);
270
280
  const warnAt = queueWarnThreshold();
@@ -305,13 +315,14 @@ function createDispatcher({
305
315
  logEvent('handler-error', {
306
316
  chat_id: chatId, session_key: sessionKey,
307
317
  msg_id: msg?.message_id,
308
- error: err.message?.slice(0, 500),
318
+ error_class: err.name || 'Error',
319
+ error_len: err.message?.length ?? 0,
309
320
  code: err.code || undefined,
310
321
  cause_code: err.cause?.code || undefined,
311
- stderr_tail: err.code === 'TMUX_SPAWN_FAILED' && typeof err.stderr === 'string'
312
- ? err.stderr.replace(/[\x00-\x08\x0b-\x1f\x7f]/g, '').slice(-500)
313
- : undefined,
314
- stack: err.stack?.split('\n').slice(0, 5).join('\n'),
322
+ // A spawn failure's stderr is process output, not a field: its size
323
+ // says whether the child said anything at all, which is the signal
324
+ // that distinguishes "tmux refused" from "tmux said nothing".
325
+ stderr_len: typeof err.stderr === 'string' ? err.stderr.length : undefined,
315
326
  aborted: wasAborted || undefined,
316
327
  replay: isReplay || undefined,
317
328
  oom_shutdown: oomShutdown || undefined,
@@ -332,7 +343,7 @@ function createDispatcher({
332
343
  logger.error?.(`[${sessionKey}] failed to clear lost session: ${resetErr.message}`);
333
344
  logEvent('session-reset-after-process-loss-failed', {
334
345
  chat_id: chatId, session_key: sessionKey, msg_id: msg?.message_id,
335
- error: resetErr.message?.slice(0, 200),
346
+ error_class: resetErr.name || 'Error',
336
347
  });
337
348
  }
338
349
  }
@@ -368,7 +379,8 @@ function createDispatcher({
368
379
  logger.error?.(`[auth] (${botName}) Claude access DISABLED by Anthropic — turn rejected immediately instead of wedging; check the account/billing.`);
369
380
  logEvent('auth-disabled', {
370
381
  chat_id: chatId, session_key: sessionKey, msg_id: msg?.message_id,
371
- error: err.message?.slice(0, 500),
382
+ error_class: err.name || 'Error',
383
+ error_len: err.message?.length ?? 0,
372
384
  });
373
385
  // Never let the gate itself become a new failure mode — AUTH_DISABLED
374
386
  // is account-wide, so a bad gate would throw on every concurrent
@@ -486,7 +498,7 @@ function createDispatcher({
486
498
  autoResumeTracker.markAttempt(sessionKey);
487
499
  logEvent('auto-resume-attempted', {
488
500
  chat_id: chatId, session_key: sessionKey, msg_id: msg.message_id,
489
- original_error: err.message?.slice(0, 200),
501
+ error_class: err.name || 'Error',
490
502
  });
491
503
  return attemptAutoResume(sessionKey, chatId, msg, bot, {
492
504
  provider,
@@ -502,7 +514,7 @@ function createDispatcher({
502
514
  logger.error?.(`[${sessionKey}] auto-resume failed: ${resumeErr?.message}`);
503
515
  logEvent('auto-resume-failed', {
504
516
  chat_id: chatId, session_key: sessionKey, msg_id: msg.message_id,
505
- error: resumeErr?.message?.slice(0, 200),
517
+ error_class: resumeErr?.name || 'Error',
506
518
  });
507
519
  // Music topic incident (2026-06-01): a channels session whose
508
520
  // context grew large enough to auto-/compact on resume loses its
@@ -562,6 +574,7 @@ function createDispatcher({
562
574
  }
563
575
  }
564
576
  }).finally(() => {
577
+ activeHandlerTargets.delete(activeHandlerTarget);
565
578
  const n = (inFlightHandlers.get(sessionKey) || 1) - 1;
566
579
  if (n <= 0) inFlightHandlers.delete(sessionKey);
567
580
  else inFlightHandlers.set(sessionKey, n);
@@ -575,6 +588,8 @@ function createDispatcher({
575
588
  errorReplyText,
576
589
  queueWarnThreshold,
577
590
  inFlightHandlers, // exposed so polygram.js can introspect for shutdown drain
591
+ getActiveHandlerCount: () => activeHandlers.size,
592
+ getActiveHandlerTargets: () => [...activeHandlerTargets],
578
593
  awaitSettlement,
579
594
  trackTask,
580
595
  };