@cjhyy/code-shell-chat 0.9.4 → 0.9.6

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.
@@ -110,7 +110,9 @@ export class DeliveryQueue {
110
110
  if (this.stopped)
111
111
  throw new Error("Chat Gateway inbox is stopped");
112
112
  const persistent = Boolean(this.config.path && (!message.attachments?.length || spooled !== undefined));
113
- const safeMessage = persistent ? serializableMessage(message, spooled) : message;
113
+ const safeMessage = persistent
114
+ ? serializableMessage(message, this.rehydrateAttachments(spooled))
115
+ : message;
114
116
  record = {
115
117
  id,
116
118
  ...(dedupeKey ? { dedupeKey } : {}),
@@ -501,7 +503,7 @@ function deliveryDedupeKey(message) {
501
503
  function targetKey(message) {
502
504
  return `${message.channel}\0${message.target}`;
503
505
  }
504
- function serializableMessage(message, spooled) {
506
+ function serializableMessage(message, acceptedAttachments) {
505
507
  // Drop `attachments` before serializing: each carries a `load()` closure that
506
508
  // JSON silently discards, which would leave a persisted message claiming
507
509
  // attachments it can no longer produce. Spooled metadata is stored on the
@@ -512,10 +514,11 @@ function serializableMessage(message, spooled) {
512
514
  throw new Error("Chat Gateway message is not persistable or exceeds 1 MiB");
513
515
  }
514
516
  const parsed = JSON.parse(serialized);
515
- // Keep the live attachments for THIS process; the spool only matters after a
516
- // restart, and re-reading from disk while the originals are in hand is waste.
517
- if (message.attachments?.length && spooled) {
518
- return { ...parsed, attachments: message.attachments };
517
+ // Use the bytes already accepted into the durable inbox. The original load()
518
+ // usually fetches a short-lived remote URL, so keeping it here would download
519
+ // twice and could lose a successfully spooled image before Mimi sees it.
520
+ if (acceptedAttachments?.length) {
521
+ return { ...parsed, attachments: acceptedAttachments };
519
522
  }
520
523
  return parsed;
521
524
  }
package/dist/gateway.js CHANGED
@@ -116,8 +116,6 @@ export function createMimiPetChat(options) {
116
116
  const entry = {
117
117
  outgoing: pending,
118
118
  nextSendIndex: 0,
119
- activeSends: 0,
120
- sendFailed: false,
121
119
  };
122
120
  replies.set(cacheKey, entry);
123
121
  cacheEntry = entry;
@@ -151,12 +149,7 @@ export function createMimiPetChat(options) {
151
149
  }
152
150
  // Adapter failures must escape to DeliveryQueue. Catching them above would
153
151
  // turn a failed real reply into a successful generic error and lose it.
154
- if (cacheEntry) {
155
- if (cacheEntry.activeSends === 0)
156
- cacheEntry.sendFailed = false;
157
- cacheEntry.activeSends += 1;
158
- }
159
- try {
152
+ const sendOutgoing = async () => {
160
153
  let index = cacheEntry?.nextSendIndex ?? 0;
161
154
  while (index < outgoing.length) {
162
155
  await reply(outgoing[index]);
@@ -164,24 +157,23 @@ export function createMimiPetChat(options) {
164
157
  if (cacheEntry)
165
158
  cacheEntry.nextSendIndex = index;
166
159
  }
167
- }
168
- catch (error) {
169
- if (cacheEntry)
170
- cacheEntry.sendFailed = true;
171
- throw error;
160
+ };
161
+ // Sharing model work alone is insufficient: overlapping deliveries can
162
+ // both observe nextSendIndex=0 while the first platform send is in flight.
163
+ // Share that send too, including its failure, then let a later queue retry
164
+ // resume from the first unfinished chunk.
165
+ const sendTask = cacheEntry ? (cacheEntry.sendTask ??= sendOutgoing()) : sendOutgoing();
166
+ try {
167
+ await sendTask;
168
+ // DeliveryQueue now owns a durable completed-message dedupe record.
169
+ // Keep memory proportional to failed/in-flight sends, not successful chat.
170
+ if (cacheKey && cacheEntry && replies.get(cacheKey) === cacheEntry) {
171
+ replies.delete(cacheKey);
172
+ }
172
173
  }
173
174
  finally {
174
- if (cacheEntry) {
175
- cacheEntry.activeSends -= 1;
176
- // DeliveryQueue now owns a durable completed-message dedupe record.
177
- // Keep memory proportional to failed/in-flight sends, not successful chat.
178
- if (cacheKey &&
179
- cacheEntry.activeSends === 0 &&
180
- !cacheEntry.sendFailed &&
181
- replies.get(cacheKey) === cacheEntry) {
182
- replies.delete(cacheKey);
183
- }
184
- }
175
+ if (cacheEntry?.sendTask === sendTask)
176
+ cacheEntry.sendTask = undefined;
185
177
  }
186
178
  };
187
179
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cjhyy/code-shell-chat",
3
- "version": "0.9.4",
3
+ "version": "0.9.6",
4
4
  "description": "A standalone, transport-agnostic multi-channel chat gateway for Node.js and Bun.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",