clawgram 2.4.0 → 2.4.2

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/dist/channel.js CHANGED
@@ -536,6 +536,10 @@ const createChannelPlugin = (runtimes) => {
536
536
  channel: "clawgram",
537
537
  accountId: route.accountId ?? accountId,
538
538
  });
539
+ // Boundary for the transcript fallback below: only replies
540
+ // written after this instant may be salvaged. Same clock as
541
+ // the transcript writer — both live in this process.
542
+ const dispatchStartedAt = Date.now();
539
543
  const dispatchResult = await dispatchBase.dispatchReplyWithBufferedBlockDispatcher({
540
544
  ctx: ctxPayload,
541
545
  cfg,
@@ -604,7 +608,7 @@ const createChannelPlugin = (runtimes) => {
604
608
  (dispatchCounts.block ?? 0) === 0 &&
605
609
  (dispatchCounts.final ?? 0) === 0;
606
610
  if (nothingDelivered) {
607
- const fallbackText = (0, helpers_1.readLatestAssistantFallbackFromTranscript)(route.sessionKey, storePath);
611
+ const fallbackText = (0, helpers_1.readLatestAssistantFallbackFromTranscript)(route.sessionKey, storePath, dispatchStartedAt);
608
612
  // A suppressed silent reply legitimately delivers nothing, so
609
613
  // this fallback fires right after it. Without the same check
610
614
  // the token would be read back from the transcript and sent.
@@ -1288,21 +1292,38 @@ const createChannelPlugin = (runtimes) => {
1288
1292
  },
1289
1293
  },
1290
1294
  outbound: {
1295
+ // Core's agent-delivery path (`--deliver`, subagent announces) calls this
1296
+ // with `to: undefined` whenever a delivery has no explicit target and the
1297
+ // session route yielded none — and it does not catch a rejection from
1298
+ // here. A throw is therefore an unhandled rejection that takes down the
1299
+ // entire gateway process, which is exactly what happened on 2026-08-06
1300
+ // (systemd: Main process exited, status=1). The contract, read from
1301
+ // core's resolveOutboundTargetWithPlugin: answer { ok: false, error } for
1302
+ // anything unresolvable. Never throw.
1291
1303
  async resolveTarget(ctx) {
1292
- actionLog.info("clawgram outbound resolveTarget", {
1293
- accountId: ctx.accountId,
1294
- rawTo: ctx.to,
1295
- });
1296
- const gram = runtimes.get(ctx.accountId);
1297
- if (!gram) {
1298
- throw new Error(`clawgram: runtime not found for account ${ctx.accountId}`);
1304
+ try {
1305
+ const raw = typeof ctx.to === "string" ? ctx.to.trim() : "";
1306
+ actionLog.info("clawgram outbound resolveTarget", {
1307
+ accountId: ctx.accountId,
1308
+ rawTo: raw || null,
1309
+ });
1310
+ if (!raw) {
1311
+ return { ok: false, error: "clawgram: no delivery target — pass `to` or use a session with a bound chat" };
1312
+ }
1313
+ const gram = runtimes.get(ctx.accountId);
1314
+ if (!gram) {
1315
+ return { ok: false, error: `clawgram: runtime not found for account ${ctx.accountId}` };
1316
+ }
1317
+ const targetKind = (0, helpers_1.inferOutboundTargetKind)(raw);
1318
+ const target = (0, helpers_1.normalizeOutboundTarget)(raw);
1319
+ return {
1320
+ ok: true,
1321
+ to: (await gram.resolvePeer(target, { kind: targetKind })).chatId ?? target,
1322
+ };
1323
+ }
1324
+ catch (err) {
1325
+ return { ok: false, error: `clawgram: target resolution failed: ${String(err)}` };
1299
1326
  }
1300
- const targetKind = (0, helpers_1.inferOutboundTargetKind)(ctx.to);
1301
- const target = (0, helpers_1.normalizeOutboundTarget)(ctx.to);
1302
- return {
1303
- ok: true,
1304
- to: (await gram.resolvePeer(target, { kind: targetKind })).chatId ?? target,
1305
- };
1306
1327
  },
1307
1328
  async sendText(ctx) {
1308
1329
  // Never log `text`: outbound bodies are private correspondence and the
package/dist/helpers.js CHANGED
@@ -110,7 +110,17 @@ function resolveTranscriptPathFromStoreEntry(input) {
110
110
  return undefined;
111
111
  }
112
112
  }
113
- function readLatestAssistantFallbackFromTranscript(sessionKey, storePath) {
113
+ /**
114
+ * Salvages a reply that reached the transcript but not stdout.
115
+ *
116
+ * `notBeforeMs` is the start of the current dispatch. Only entries stamped at
117
+ * or after it count: on a turn that aborted with zero output, the newest
118
+ * assistant entry is by definition from an earlier turn, and re-sending it
119
+ * addresses an old answer to a new question (the 2026-08-06 duplicates,
120
+ * note 0054 in the control repo). An entry with no parseable timestamp cannot
121
+ * prove it is fresh, so it does not count either.
122
+ */
123
+ function readLatestAssistantFallbackFromTranscript(sessionKey, storePath, notBeforeMs) {
114
124
  if (!storePath?.trim()) {
115
125
  return undefined;
116
126
  }
@@ -135,6 +145,14 @@ function readLatestAssistantFallbackFromTranscript(sessionKey, storePath) {
135
145
  if (entry?.type !== "message" || entry?.message?.role !== "assistant" || !Array.isArray(entry.message.content)) {
136
146
  continue;
137
147
  }
148
+ if (notBeforeMs !== undefined) {
149
+ const stamped = typeof entry.timestamp === "string" ? Date.parse(entry.timestamp) : NaN;
150
+ if (!Number.isFinite(stamped) || stamped < notBeforeMs) {
151
+ // Entries are appended in order; everything below this one is
152
+ // older still. Stop instead of walking further into the past.
153
+ return undefined;
154
+ }
155
+ }
138
156
  const textPart = entry.message.content.find((part) => part?.type === "text" && typeof part.text === "string" && part.text.trim());
139
157
  if (!textPart?.text) {
140
158
  continue;
@@ -2,7 +2,7 @@
2
2
  "id": "clawgram",
3
3
  "name": "Clawgram",
4
4
  "description": "Clawgram — personal Telegram (MTProto userbot) channel for OpenClaw. Your AI assistant reads and responds as you.",
5
- "version": "2.4.0",
5
+ "version": "2.4.2",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawgram",
3
- "version": "2.4.0",
3
+ "version": "2.4.2",
4
4
  "description": "Clawgram — personal Telegram (MTProto userbot) channel for OpenClaw. Your AI assistant reads and responds as you.",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {