polygram 0.33.5 → 0.34.0

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://anthropic.com/claude-code/plugin.schema.json",
3
3
  "name": "polygram",
4
- "version": "0.33.5",
4
+ "version": "0.34.0",
5
5
  "description": "Telegram integration for Claude Code that preserves the OpenClaw per-chat session model. Migration target for OpenClaw users. Multi-bot, multi-chat, per-topic isolation; SQLite transcripts; inline-keyboard approvals. Bundles /polygram:status|logs|pair-code|approvals admin commands plus history (transcript queries) and polygram-send (out-of-turn IPC sends with file-upload validation) skills.",
6
6
  "keywords": [
7
7
  "telegram",
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ function parseSystemdInvocationId(env = process.env) {
4
+ const value = env.INVOCATION_ID;
5
+ if (value == null || value === '') return null;
6
+ if (!/^[0-9a-f]{32}$/.test(value)) {
7
+ throw new Error('invalid systemd invocation identity');
8
+ }
9
+ return value;
10
+ }
11
+
12
+ function lifecycleDetail(detail, invocationId) {
13
+ const value = detail && typeof detail === 'object' ? detail : {};
14
+ return invocationId == null
15
+ ? value
16
+ : { ...value, invocation_id: invocationId };
17
+ }
18
+
19
+ module.exports = {
20
+ parseSystemdInvocationId,
21
+ lifecycleDetail,
22
+ };
package/ops/README.md CHANGED
@@ -109,6 +109,19 @@ canonical absolute directory. Temporary paths and unsafe aliases are rejected;
109
109
  there is no `/tmp` fallback. If the data-directory path is too long for a
110
110
  portable Unix socket, set `POLYGRAM_IPC_DIR` to a shorter owner-only path.
111
111
 
112
+ Deployment automation can query the aggregate in-flight handler count without
113
+ printing session identifiers or the socket path:
114
+
115
+ ```sh
116
+ POLYGRAM_IPC_DIR=/absolute/data-dir/.ipc polygram-ipc admin-bot busy
117
+ # {"bot":"admin-bot","in_flight":0}
118
+ ```
119
+
120
+ This is an advisory quiet-window check. The daemon's shutdown admission,
121
+ delivery, and handler-settlement barriers remain the correctness boundary.
122
+ Invoking `polygram-ipc admin-bot` without a subcommand retains the existing
123
+ path-and-ping health output.
124
+
112
125
  Allowed methods: `sendMessage`, `sendPhoto`, `sendDocument`, `sendSticker`,
113
126
  `sendChatAction`, `editMessageText`, `setMessageReaction`. Other methods
114
127
  are rejected server-side.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polygram",
3
- "version": "0.33.5",
3
+ "version": "0.34.0",
4
4
  "description": "Telegram daemon for Claude Code that preserves the OpenClaw per-chat session model. Migration path for OpenClaw users moving to Claude Code.",
5
5
  "main": "lib/ipc/client.js",
6
6
  "bin": {
package/polygram.js CHANGED
@@ -32,6 +32,10 @@ const { buildPrompt, resolvePromptBackend } = require('./lib/prompt');
32
32
  const { countInFlight } = require('./lib/queue-utils');
33
33
  const { createIpcHandlers } = require('./lib/ipc/handlers');
34
34
  const { classifyOrphanSweep } = require('./lib/ops/tmux-preflight');
35
+ const {
36
+ parseSystemdInvocationId,
37
+ lifecycleDetail,
38
+ } = require('./lib/ops/systemd-invocation');
35
39
  const { filterAttachments, resolveFileCaps, resolveMaxFileOverride, MAX_TOTAL_BYTES } = require('./lib/attachments');
36
40
  // 0.9.0: SDK ProcessManager is the only pm. CLI pm
37
41
  // (lib/process-manager.js) deleted in commit 6.
@@ -3391,6 +3395,7 @@ let startPollWatchdog = null;
3391
3395
 
3392
3396
  async function main() {
3393
3397
  const cgroupOomObserver = createCgroupOomObserver();
3398
+ const invocationId = parseSystemdInvocationId();
3394
3399
  let codexManagerOptions = Object.freeze({
3395
3400
  codexHostIdentity: null,
3396
3401
  codexBootSessionIdentity: null,
@@ -3628,7 +3633,10 @@ async function main() {
3628
3633
  console.log(`[inbox] swept ${swept.swept} files (${(swept.bytes / 1_048_576).toFixed(1)} MiB) older than ${inboxRetentionMs / 86_400_000}d`);
3629
3634
  db.logEvent('inbox-swept', { files: swept.swept, bytes: swept.bytes, retention_days: inboxRetentionMs / 86_400_000 });
3630
3635
  }
3631
- db.logEvent('polygram-start', { migration: migration.reason, imported: migration.imported });
3636
+ db.logEvent('polygram-start', lifecycleDetail({
3637
+ migration: migration.reason,
3638
+ imported: migration.imported,
3639
+ }, invocationId));
3632
3640
  if (cgroupOomObserver.startup.status === 'unavailable') {
3633
3641
  console.warn(`[oom-observer] unavailable (${cgroupOomObserver.startup.reason}); handled shutdown signals will retain clean-restart behavior`);
3634
3642
  db.logEvent('oom-observer-unavailable', { reason: cgroupOomObserver.startup.reason });
@@ -4534,7 +4542,7 @@ async function main() {
4534
4542
  });
4535
4543
  }
4536
4544
  }
4537
- logEvent('shutdown-drain', {
4545
+ logEvent('shutdown-drain', lifecycleDetail({
4538
4546
  bot: BOT_NAME,
4539
4547
  in_flight: remaining,
4540
4548
  in_flight_at_signal: inFlightAtSignal,
@@ -4543,7 +4551,7 @@ async function main() {
4543
4551
  clean: res.clean,
4544
4552
  shutdown_reason: res.shutdownReason,
4545
4553
  ...(res.oomKillDelta != null ? { oom_kill_delta: res.oomKillDelta } : {}),
4546
- });
4554
+ }, invocationId));
4547
4555
  console.log(`[shutdown] ${res.clean ? 'clean' : 'crash-like'} shutdown recorded (${res.shutdownReason}); ${inFlightAtSignal} in-flight at signal, drained ${drainElapsed}ms, ${remaining} still in-flight, ${res.replayMarked} marked replay-pending`);
4548
4556
  } catch (err) {
4549
4557
  console.error(`[shutdown] persistence failed: ${err.message}`);
@@ -4576,7 +4584,10 @@ async function main() {
4576
4584
  if (cancelAllWaiters) cancelAllWaiters('cancelled', 'polygram shutting down');
4577
4585
  if (pm && !pmRetired) await pm.shutdown().catch(() => {});
4578
4586
  if (db) {
4579
- try { db.logEvent('polygram-stop'); db.raw.close(); } catch {}
4587
+ try {
4588
+ db.logEvent('polygram-stop', lifecycleDetail({}, invocationId));
4589
+ db.raw.close();
4590
+ } catch {}
4580
4591
  }
4581
4592
  // rc.50: release our PID file claim so the next boot doesn't try
4582
4593
  // to kill us. releasePidFile is idempotent and only deletes the
@@ -1,19 +1,44 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * Quick IPC round-trip probe.
4
- * Usage: node scripts/ipc-smoke.js <bot-name>
4
+ * Usage: node scripts/ipc-smoke.js <bot-name> [busy]
5
5
  */
6
6
 
7
- const { call, socketPathFor } = require('../lib/ipc/client');
7
+ const { call, readSecret, socketPathFor } = require('../lib/ipc/client');
8
8
 
9
9
  (async () => {
10
10
  const bot = process.argv[2] || 'shumabit';
11
+ const command = process.argv[3] || 'ping';
11
12
  const path = socketPathFor(bot);
12
13
 
14
+ if (command === 'busy') {
15
+ const result = await call({
16
+ path,
17
+ op: 'busy',
18
+ secret: readSecret(bot),
19
+ });
20
+ if (
21
+ result.ok !== true
22
+ || result.bot !== bot
23
+ || !Number.isSafeInteger(result.in_flight)
24
+ || result.in_flight < 0
25
+ ) {
26
+ throw new Error('invalid busy response');
27
+ }
28
+ console.log(JSON.stringify({
29
+ bot: result.bot,
30
+ in_flight: result.in_flight,
31
+ }));
32
+ return;
33
+ }
34
+
13
35
  console.log('path:', path);
14
36
  console.log('ping:', JSON.stringify(await call({ path, op: 'ping' })));
15
37
  console.log('DONE');
16
38
  })().catch((err) => {
17
- console.error('ERR:', err.message);
39
+ const message = process.argv[3] === 'busy'
40
+ ? 'busy request failed'
41
+ : err.message;
42
+ console.error('ERR:', message);
18
43
  process.exit(1);
19
44
  });