@ycodium-ai/plugin-im 0.2.3216 → 0.2.3220

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.
Files changed (2) hide show
  1. package/dist/index.js +383 -191
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -126258,67 +126258,96 @@ var keepNewest = (record4, limit) => {
126258
126258
  return entries.length <= limit ? record4 : Object.fromEntries(entries.slice(entries.length - limit));
126259
126259
  };
126260
126260
 
126261
- // src/boundedMap.ts
126262
- var makeBoundedMap = (limit) => {
126263
- const entries = /* @__PURE__ */ new Map();
126264
- return {
126265
- get: (key) => entries.get(key),
126266
- set: (key, value2) => {
126267
- entries.delete(key);
126268
- entries.set(key, value2);
126269
- if (entries.size > limit) {
126270
- const oldest = entries.keys().next().value;
126271
- if (oldest !== void 0) entries.delete(oldest);
126272
- }
126273
- },
126274
- /** 忘掉键满足条件的那些。 */
126275
- forget: (matches) => {
126276
- for (const key of [...entries.keys()]) if (matches(key)) entries.delete(key);
126261
+ // src/executionTurn.ts
126262
+ var indexExecution = (execution) => {
126263
+ const intentsByMessageId = /* @__PURE__ */ new Map();
126264
+ const intentsById = /* @__PURE__ */ new Map();
126265
+ const turnsById = /* @__PURE__ */ new Map();
126266
+ for (const intent of execution.turnIntents) {
126267
+ if (!intentsByMessageId.has(intent.messageId)) intentsByMessageId.set(intent.messageId, intent);
126268
+ if (!intentsById.has(intent.intentId)) intentsById.set(intent.intentId, intent);
126269
+ if (intent.turn?.turnId != null && !turnsById.has(intent.turn.turnId)) {
126270
+ turnsById.set(intent.turn.turnId, intent.turn);
126277
126271
  }
126278
- };
126272
+ }
126273
+ if (execution.latestTurn != null && !turnsById.has(execution.latestTurn.turnId)) {
126274
+ turnsById.set(execution.latestTurn.turnId, execution.latestTurn);
126275
+ }
126276
+ return { execution, intentsByMessageId, intentsById, turnsById };
126279
126277
  };
126278
+ var executionTurnFor = (snapshot, turnId) => snapshot.turnsById.get(turnId);
126280
126279
 
126281
- // src/replyAnchors.ts
126282
- var THREAD_LIMIT = 256;
126283
- var TURNS_PER_THREAD = 16;
126284
- var anchorOfMessage = (adapter, message) => adapter?.quotesReplies === true ? { messageId: message.messageId, inThread: message.replyInThread === true } : void 0;
126285
- var userMessageIdOfTurn = (execution, turnId) => {
126286
- const intent = execution.turnIntents.find((candidate) => candidate.turn?.turnId === turnId);
126287
- const latest = execution.latestTurn;
126288
- return intent?.messageId ?? (latest?.turnId === turnId ? latest.userMessageId : void 0);
126280
+ // src/intentStatus.ts
126281
+ var isLiveIntentStatus = (status) => status === "queued" || status === "preparing" || status === "dispatching" || status === "running" || status === "reconciling" || status === "blocked";
126282
+
126283
+ // src/conclusionTurn.ts
126284
+ var conclusionOf = (turn) => turn.state === "completed" || turn.state === "interrupted" || turn.state === "error" ? { kind: "settled", ...turn, state: turn.state } : { kind: "pending" };
126285
+ var conclusionTurnFor = (snapshot, userMessageId) => {
126286
+ const { execution } = snapshot;
126287
+ const initial = snapshot.intentsByMessageId.get(userMessageId);
126288
+ if (initial === void 0) {
126289
+ const latest = execution.latestTurn;
126290
+ return latest?.userMessageId === userMessageId ? conclusionOf(latest) : { kind: "gone" };
126291
+ }
126292
+ let intent = initial;
126293
+ const visited = /* @__PURE__ */ new Set();
126294
+ while (intent.status === "cancelled" || intent.status === "abandoned") {
126295
+ if (visited.has(intent.intentId)) return { kind: "gone" };
126296
+ visited.add(intent.intentId);
126297
+ const replacementId = intent.replacementIntentId;
126298
+ const replacement = replacementId == null ? void 0 : snapshot.intentsById.get(replacementId);
126299
+ if (replacement === void 0) return { kind: "gone" };
126300
+ intent = replacement;
126301
+ }
126302
+ const action = intent.action;
126303
+ const turn = intent.turn;
126304
+ if (action?.kind === "steer" && intent.deliveredTurnId === action.turnId) {
126305
+ return {
126306
+ kind: "merged",
126307
+ turnId: action.turnId,
126308
+ turn: executionTurnFor(snapshot, action.turnId)
126309
+ };
126310
+ }
126311
+ if (turn !== null) return conclusionOf(turn);
126312
+ if (action?.kind === "steer") return { kind: "pending" };
126313
+ return { kind: isLiveIntentStatus(intent.status) ? "pending" : "gone" };
126289
126314
  };
126290
- var makeReplyAnchors = (host) => {
126291
- const byThread = makeBoundedMap(THREAD_LIMIT);
126292
- const userMessageOfTurn = async (threadId, turnId) => {
126293
- const execution = await host.projections.getThreadExecution(threadId).catch(() => null);
126294
- if (execution === null) return void 0;
126295
- const wanted = turnId ?? execution.session?.activeTurnId ?? execution.latestTurn?.turnId;
126296
- return wanted == null ? void 0 : userMessageIdOfTurn(execution, wanted);
126297
- };
126315
+ var groupConclusionDebts = (snapshot, threadId, debts) => {
126316
+ const resolved = new Map(
126317
+ debts.filter((debt) => debt.threadId === threadId).map((debt) => [
126318
+ debt.userMessageId,
126319
+ { debt, result: conclusionTurnFor(snapshot, debt.userMessageId) }
126320
+ ])
126321
+ );
126322
+ const byTurn = /* @__PURE__ */ new Map();
126323
+ for (const { debt, result: result2 } of resolved.values()) {
126324
+ if (result2.kind !== "settled" && result2.kind !== "merged") continue;
126325
+ const group = byTurn.get(result2.turnId) ?? [];
126326
+ group.push(debt);
126327
+ byTurn.set(result2.turnId, group);
126328
+ }
126329
+ for (const [turnId, candidates] of byTurn) {
126330
+ const ownerId = turnId === null ? void 0 : executionTurnFor(snapshot, turnId)?.userMessageId;
126331
+ const owner = ownerId === void 0 ? void 0 : resolved.get(ownerId);
126332
+ if (owner !== void 0 && owner.result.kind !== "gone" && !candidates.includes(owner.debt))
126333
+ candidates.push(owner.debt);
126334
+ }
126335
+ return { resolved, byTurn };
126336
+ };
126337
+ var conclusionDebtForTurn = (snapshot, turnId, candidates, isDelivered) => {
126338
+ const turn = turnId === null ? void 0 : executionTurnFor(snapshot, turnId);
126339
+ const own = candidates.find((debt2) => debt2.userMessageId === turn?.userMessageId);
126340
+ const handled = turn?.userMessageId !== void 0 && isDelivered(turn.userMessageId) || candidates.length === 0 && snapshot.execution.turnIntents.some((intent) => {
126341
+ if (!isDelivered(intent.messageId)) return false;
126342
+ const result2 = conclusionTurnFor(snapshot, intent.messageId);
126343
+ return (result2.kind === "merged" || result2.kind === "settled") && result2.turnId === turnId;
126344
+ });
126345
+ const debt = handled ? void 0 : own ?? candidates[0];
126298
126346
  return {
126299
- /**
126300
- * 开轮时记一笔:`userMessageId` 是派给宿主的那条用户消息,`binding` 是收到聊天消息的那个
126301
- * 聊天(带着它的锚点)。没有锚点(平台不引用)就不记。
126302
- */
126303
- note: (threadId, userMessageId, binding) => {
126304
- if (binding.reply === void 0) return;
126305
- const turns = byThread.get(threadId) ?? makeBoundedMap(TURNS_PER_THREAD);
126306
- byThread.set(threadId, turns);
126307
- turns.set(userMessageId, { chatKey: chatKeyOf(binding), anchor: binding.reply });
126308
- },
126309
- /**
126310
- * 给发往这条会话的聊天挂上 `turnId` 那一轮的锚点(null 的含义见上);取不到就原样返回。
126311
- *
126312
- * 锚点只配给它那条消息所在的聊天:会话可以被另一个聊天接走(`/resume`),而回复接口只按被
126313
- * 回复的消息寻址,挂错了答复就发回原来的聊天。
126314
- */
126315
- anchored: async (binding, threadId, turnId) => {
126316
- const turns = byThread.get(threadId);
126317
- if (turns === void 0) return binding;
126318
- const asked = await userMessageOfTurn(threadId, turnId);
126319
- const noted = asked === void 0 ? void 0 : turns.get(asked);
126320
- return noted?.chatKey === chatKeyOf(binding) ? { ...binding, reply: noted.anchor } : binding;
126321
- }
126347
+ debt,
126348
+ handled,
126349
+ userMessageId: turn?.userMessageId,
126350
+ redundant: candidates.filter((candidate) => candidate !== debt)
126322
126351
  };
126323
126352
  };
126324
126353
 
@@ -126336,11 +126365,14 @@ var foldConclusionEvent = (state, name, data) => {
126336
126365
  const pending = conclusionDebtsOf(state);
126337
126366
  if (name === "conclusion-delivered") {
126338
126367
  const { [key]: _delivered, ...rest } = pending;
126368
+ const turnUserMessageId = data["turnUserMessageId"];
126369
+ const turnKey2 = typeof turnUserMessageId === "string" && turnUserMessageId !== "" ? keyOf(threadId, turnUserMessageId) : key;
126370
+ const { [turnKey2]: _turnDelivered, ...remaining } = rest;
126339
126371
  return {
126340
126372
  ...state,
126341
- conclusions: rest,
126373
+ conclusions: remaining,
126342
126374
  deliveredConclusions: keepNewest(
126343
- { ...state.deliveredConclusions, [key]: true },
126375
+ { ...state.deliveredConclusions, [key]: true, [turnKey2]: true },
126344
126376
  CONCLUSION_DEBT_LIMIT
126345
126377
  )
126346
126378
  };
@@ -126368,13 +126400,35 @@ var makeConclusionLedger = (core) => {
126368
126400
  core.host.onDispose(() => {
126369
126401
  disposed = true;
126370
126402
  });
126371
- const settle = (debt) => {
126403
+ const settle = ({ debt, turnUserMessageId }) => {
126372
126404
  core.commit("conclusion-delivered", {
126373
126405
  threadId: debt.threadId,
126374
- userMessageId: debt.userMessageId
126406
+ userMessageId: debt.userMessageId,
126407
+ ...turnUserMessageId !== void 0 && turnUserMessageId !== debt.userMessageId ? { turnUserMessageId } : {}
126375
126408
  });
126376
126409
  };
126410
+ const forExecutionTurn = (snapshot, threadId, turnId, grouped) => {
126411
+ if (disposed) return { selection: void 0, handled: true };
126412
+ const pending = conclusionDebtsOf(core.state);
126413
+ const candidates = (grouped ?? groupConclusionDebts(snapshot, threadId, Object.values(pending))).byTurn.get(turnId);
126414
+ const currentCandidates = candidates?.filter(
126415
+ (debt) => pending[keyOf(threadId, debt.userMessageId)] !== void 0
126416
+ );
126417
+ if (currentCandidates?.length === 0) return { selection: void 0, handled: true };
126418
+ const selected = conclusionDebtForTurn(
126419
+ snapshot,
126420
+ turnId,
126421
+ currentCandidates ?? [],
126422
+ (messageId) => delivered(keyOf(threadId, messageId))
126423
+ );
126424
+ for (const debt of selected.redundant) settle({ debt, turnUserMessageId: void 0 });
126425
+ return {
126426
+ selection: selected.debt === void 0 ? void 0 : { debt: selected.debt, turnUserMessageId: selected.userMessageId },
126427
+ handled: selected.handled || selected.debt !== void 0 && inFlight.has(keyOf(threadId, selected.debt.userMessageId))
126428
+ };
126429
+ };
126377
126430
  return {
126431
+ forExecutionTurn,
126378
126432
  starting: (threadId) => {
126379
126433
  const gate = Promise.withResolvers();
126380
126434
  const pending = registering.get(threadId) ?? /* @__PURE__ */ new Set();
@@ -126409,15 +126463,17 @@ var makeConclusionLedger = (core) => {
126409
126463
  forTurn: async (threadId, turnId = null) => {
126410
126464
  await Promise.all(registering.get(threadId) ?? []);
126411
126465
  const execution = await core.host.projections.getThreadExecution(threadId);
126412
- const userMessageId = execution === null ? void 0 : turnId === null ? execution.latestTurn?.userMessageId : userMessageIdOfTurn(execution, turnId);
126413
- const key = userMessageId === void 0 ? void 0 : keyOf(threadId, userMessageId);
126414
126466
  return {
126415
- debt: key === void 0 ? void 0 : conclusionDebtsOf(core.state)[key],
126416
- handled: key !== void 0 && (delivered(key) || inFlight.has(key)),
126417
- execution
126467
+ execution,
126468
+ ...execution === null ? { selection: void 0, handled: false } : forExecutionTurn(
126469
+ indexExecution(execution),
126470
+ threadId,
126471
+ turnId ?? execution.latestTurn?.turnId ?? null
126472
+ )
126418
126473
  };
126419
126474
  },
126420
- deliver: async (debt, send) => {
126475
+ deliver: async (selection, send) => {
126476
+ const { debt } = selection;
126421
126477
  const key = keyOf(debt.threadId, debt.userMessageId);
126422
126478
  if (disposed || delivered(key) || inFlight.has(key) || conclusionDebtsOf(core.state)[key] === void 0)
126423
126479
  return false;
@@ -126426,16 +126482,17 @@ var makeConclusionLedger = (core) => {
126426
126482
  const sent = await send(
126427
126483
  () => !delivered(key) && conclusionDebtsOf(core.state)[key] !== void 0
126428
126484
  );
126429
- if (sent) settle(debt);
126485
+ if (sent) settle(selection);
126430
126486
  return sent;
126431
126487
  } finally {
126432
126488
  inFlight.delete(key);
126433
126489
  }
126434
126490
  },
126435
- discard: (debt) => {
126491
+ discard: (selection) => {
126492
+ const { debt } = selection;
126436
126493
  const key = keyOf(debt.threadId, debt.userMessageId);
126437
126494
  if (!disposed && !delivered(key) && !inFlight.has(key) && conclusionDebtsOf(core.state)[key] !== void 0) {
126438
- settle(debt);
126495
+ settle(selection);
126439
126496
  }
126440
126497
  }
126441
126498
  };
@@ -127450,6 +127507,26 @@ var makeChatQueue = (core) => {
127450
127507
  // src/chatSetup.ts
127451
127508
  import { threadOrNull } from "@ycodium-ai/plugin-api";
127452
127509
 
127510
+ // src/boundedMap.ts
127511
+ var makeBoundedMap = (limit) => {
127512
+ const entries = /* @__PURE__ */ new Map();
127513
+ return {
127514
+ get: (key) => entries.get(key),
127515
+ set: (key, value2) => {
127516
+ entries.delete(key);
127517
+ entries.set(key, value2);
127518
+ if (entries.size > limit) {
127519
+ const oldest = entries.keys().next().value;
127520
+ if (oldest !== void 0) entries.delete(oldest);
127521
+ }
127522
+ },
127523
+ /** 忘掉键满足条件的那些。 */
127524
+ forget: (matches) => {
127525
+ for (const key of [...entries.keys()]) if (matches(key)) entries.delete(key);
127526
+ }
127527
+ };
127528
+ };
127529
+
127453
127530
  // src/cooldown.ts
127454
127531
  var makeCooldown = (windowMs, now, maxKeys = Number.POSITIVE_INFINITY) => {
127455
127532
  const lastAt = /* @__PURE__ */ new Map();
@@ -133604,18 +133681,14 @@ var makeInboundStore = (input) => {
133604
133681
  write(rowId, row);
133605
133682
  return result2.promise;
133606
133683
  };
133607
- const claim = (rowId, id, message, receivedAt = 0) => {
133684
+ const claimReceipt = (rowId, id, message, receivedAt = 0) => {
133608
133685
  const row = rowOf(rowId);
133609
- if (!row.loaded) return load(rowId).then(() => claim(rowId, id, message, receivedAt));
133610
- try {
133611
- assertReadable(row);
133612
- } catch (error) {
133613
- return Promise.reject(error);
133614
- }
133686
+ assertReadable(row);
133615
133687
  const pending = row.claims.get(id);
133616
- if (pending !== void 0) return pending.then(() => false);
133688
+ if (pending !== void 0) return { first: false, persisted: pending };
133617
133689
  const seen = row.data.seenIds.includes(id);
133618
- if (seen && (row.claimRevisions.get(id) ?? 0) <= row.persisted) return Promise.resolve(false);
133690
+ if (seen && (row.claimRevisions.get(id) ?? 0) <= row.persisted)
133691
+ return { first: false, persisted: Promise.resolve() };
133619
133692
  if (!seen) {
133620
133693
  if (message !== void 0) {
133621
133694
  const inbox = row.data.inbox ?? [];
@@ -133659,7 +133732,17 @@ var makeInboundStore = (input) => {
133659
133732
  row.claims.delete(id);
133660
133733
  };
133661
133734
  void result2.then(forget, forget);
133662
- return result2.then(() => !seen);
133735
+ return { first: !seen, persisted: result2 };
133736
+ };
133737
+ const claim = (rowId, id, message, receivedAt = 0) => {
133738
+ const row = rowOf(rowId);
133739
+ if (!row.loaded) return load(rowId).then(() => claim(rowId, id, message, receivedAt));
133740
+ try {
133741
+ const receipt = claimReceipt(rowId, id, message, receivedAt);
133742
+ return receipt.persisted.then(() => receipt.first);
133743
+ } catch (error) {
133744
+ return Promise.reject(error);
133745
+ }
133663
133746
  };
133664
133747
  const removeEntry = (rowId, key, failure2) => {
133665
133748
  const row = rowOf(rowId);
@@ -133785,6 +133868,7 @@ var makeInboundStore = (input) => {
133785
133868
  return {
133786
133869
  load,
133787
133870
  claim,
133871
+ claimReceipt,
133788
133872
  get: (rowId, key) => {
133789
133873
  const row = rowOf(rowId);
133790
133874
  assertReadable(row);
@@ -133822,6 +133906,8 @@ var makeInboundStore = (input) => {
133822
133906
  assertReadable(row);
133823
133907
  if (row.data.inbox !== void 0)
133824
133908
  row.data.inbox = row.data.inbox.filter((e) => e.key !== key);
133909
+ row.data.seenIds = row.data.seenIds.filter((id) => id !== key);
133910
+ row.claimRevisions.delete(key);
133825
133911
  row.revision++;
133826
133912
  },
133827
133913
  reset,
@@ -133962,7 +134048,9 @@ var startConnections = (core, inboundHandlersFor, inboundState, recovery) => {
133962
134048
  ...inbound,
133963
134049
  // 停用先封接收,保持发送连接直到已接收工作 drain 完成。
133964
134050
  onMessage: (message) => {
133965
- if (!closed && !disposed) inbound.onMessage(message);
134051
+ if (closed || disposed)
134052
+ return Promise.reject(new Error("inbound connection is closed"));
134053
+ return inbound.onMessage(message);
133966
134054
  },
133967
134055
  onButton: (...args2) => closed || disposed ? null : inbound.onButton(...args2),
133968
134056
  onPanelClick: (click) => closed || disposed ? null : inbound.onPanelClick?.(click) ?? null,
@@ -135109,6 +135197,45 @@ var makeGreetings = (core, enqueueForChat, now) => {
135109
135197
  };
135110
135198
  };
135111
135199
 
135200
+ // src/replyAnchors.ts
135201
+ var THREAD_LIMIT = 256;
135202
+ var TURNS_PER_THREAD = 16;
135203
+ var anchorOfMessage = (adapter, message) => adapter?.quotesReplies === true ? { messageId: message.messageId, inThread: message.replyInThread === true } : void 0;
135204
+ var makeReplyAnchors = (host) => {
135205
+ const byThread = makeBoundedMap(THREAD_LIMIT);
135206
+ const userMessageOfTurn = async (threadId, turnId) => {
135207
+ const execution = await host.projections.getThreadExecution(threadId).catch(() => null);
135208
+ if (execution === null) return void 0;
135209
+ const wanted = turnId ?? execution.session?.activeTurnId ?? execution.latestTurn?.turnId;
135210
+ return wanted == null ? void 0 : executionTurnFor(indexExecution(execution), wanted)?.userMessageId;
135211
+ };
135212
+ return {
135213
+ /**
135214
+ * 开轮时记一笔:`userMessageId` 是派给宿主的那条用户消息,`binding` 是收到聊天消息的那个
135215
+ * 聊天(带着它的锚点)。没有锚点(平台不引用)就不记。
135216
+ */
135217
+ note: (threadId, userMessageId, binding) => {
135218
+ if (binding.reply === void 0) return;
135219
+ const turns = byThread.get(threadId) ?? makeBoundedMap(TURNS_PER_THREAD);
135220
+ byThread.set(threadId, turns);
135221
+ turns.set(userMessageId, { chatKey: chatKeyOf(binding), anchor: binding.reply });
135222
+ },
135223
+ /**
135224
+ * 给发往这条会话的聊天挂上 `turnId` 那一轮的锚点(null 的含义见上);取不到就原样返回。
135225
+ *
135226
+ * 锚点只配给它那条消息所在的聊天:会话可以被另一个聊天接走(`/resume`),而回复接口只按被
135227
+ * 回复的消息寻址,挂错了答复就发回原来的聊天。
135228
+ */
135229
+ anchored: async (binding, threadId, turnId) => {
135230
+ const turns = byThread.get(threadId);
135231
+ if (turns === void 0) return binding;
135232
+ const asked = await userMessageOfTurn(threadId, turnId);
135233
+ const noted = asked === void 0 ? void 0 : turns.get(asked);
135234
+ return noted?.chatKey === chatKeyOf(binding) ? { ...binding, reply: noted.anchor } : binding;
135235
+ }
135236
+ };
135237
+ };
135238
+
135112
135239
  // src/inbound.ts
135113
135240
  var ARCHIVED_REPLY_COOLDOWN_MS = 10 * 6e4;
135114
135241
  var UNSUPPORTED_REPLY_COOLDOWN_MS = 6e4;
@@ -135336,40 +135463,44 @@ var makeInbound = (core, deps) => {
135336
135463
  noteChatAlias: (alias, chatId) => noteChatAlias(bot, alias, chatId),
135337
135464
  knowsChatAlias: (alias) => chatAliasOf(core.state, bot.rowId, alias) !== void 0,
135338
135465
  onGreeting: (chatId, senderId) => greet(bot.rowId, chatId, senderId),
135339
- onMessage: (received) => {
135466
+ onMessage: async (received) => {
135340
135467
  const current = currentBot(bot.rowId);
135341
- if (!accepting || current === void 0) return;
135468
+ if (!accepting || current === void 0) throw new Error("inbound is not accepting messages");
135342
135469
  const message = settleChat(current, received);
135343
135470
  const work = workForMessage(message);
135344
- if (inboundState.hasSeen(current.rowId, work.key)) return;
135471
+ const adapter = adapterFor(current.platform);
135345
135472
  let saved;
135346
135473
  try {
135347
- saved = persistInbound(adapterFor(current.platform), message);
135474
+ saved = persistInbound(adapter, message);
135348
135475
  } catch (error) {
135349
135476
  host.log.warn("chat could not serialize inbound; processing without restart protection", {
135350
135477
  rowId: current.rowId,
135351
135478
  error: describeError(error)
135352
135479
  });
135353
135480
  }
135481
+ const claim = inboundState.claimReceipt(current.rowId, work.key, saved, now());
135482
+ if (!claim.first) return claim.persisted;
135483
+ const receipt = claim.persisted.catch((error) => {
135484
+ inboundState.abandon(current.rowId, work.key);
135485
+ host.log.warn(
135486
+ adapter?.redeliversUnacknowledged ? "chat could not save inbound state; waiting for platform redelivery" : "chat could not save inbound state; processing without restart protection",
135487
+ { rowId: current.rowId, error: describeError(error) }
135488
+ );
135489
+ throw error;
135490
+ });
135491
+ const enqueue = () => {
135492
+ const arrived = bindingOf2(current, message.chatId);
135493
+ if (message.chatKind !== void 0 && chatOf(core.state, chatKeyOf(arrived)).chatKind !== message.chatKind) {
135494
+ commit("chat-updated", { chatKey: chatKeyOf(arrived), chatKind: message.chatKind });
135495
+ }
135496
+ enqueueForChat(chatKeyOf(arrived), () => resumeMessage(current, message, work));
135497
+ };
135354
135498
  track(
135355
- inboundState.claim(current.rowId, work.key, saved, now()).then(
135356
- () => {
135357
- },
135358
- (error) => {
135359
- inboundState.abandon(current.rowId, work.key);
135360
- host.log.warn(
135361
- "chat could not save inbound state; processing without restart protection",
135362
- { rowId: current.rowId, error: describeError(error) }
135363
- );
135364
- }
135365
- ).then(() => {
135366
- const arrived = bindingOf2(current, message.chatId);
135367
- if (message.chatKind !== void 0 && chatOf(core.state, chatKeyOf(arrived)).chatKind !== message.chatKind) {
135368
- commit("chat-updated", { chatKey: chatKeyOf(arrived), chatKind: message.chatKind });
135369
- }
135370
- enqueueForChat(chatKeyOf(arrived), () => resumeMessage(current, message, work));
135499
+ receipt.then(enqueue, () => {
135500
+ if (!adapter?.redeliversUnacknowledged) enqueue();
135371
135501
  })
135372
135502
  );
135503
+ return receipt;
135373
135504
  }
135374
135505
  });
135375
135506
  const replay = makeInboxReplay(
@@ -135803,14 +135934,6 @@ var queueCardOf = (record4, view, note, t) => {
135803
135934
  var queueViewKey = (view, note) => JSON.stringify([view, note]);
135804
135935
 
135805
135936
  // src/queueFlow.ts
135806
- var LIVE_STATUSES = /* @__PURE__ */ new Set([
135807
- "queued",
135808
- "preparing",
135809
- "dispatching",
135810
- "running",
135811
- "reconciling",
135812
- "blocked"
135813
- ]);
135814
135937
  var makeQueueFlow = (host, deps) => {
135815
135938
  const { t } = host;
135816
135939
  const shown = /* @__PURE__ */ new Map();
@@ -135834,7 +135957,7 @@ var makeQueueFlow = (host, deps) => {
135834
135957
  const intent = execution?.intents.find((entry) => entry.messageId === messageId);
135835
135958
  if (execution === null || intent === void 0 || intent.status !== "queued") return;
135836
135959
  const waiting = execution.activeTurnId !== null || execution.queuePaused || execution.intents.some(
135837
- (other) => other.intentId !== intent.intentId && LIVE_STATUSES.has(other.status) && other.sequence < intent.sequence
135960
+ (other) => other.intentId !== intent.intentId && isLiveIntentStatus(other.status) && other.sequence < intent.sequence
135838
135961
  );
135839
135962
  if (!waiting) return;
135840
135963
  const cards = target2.adapter.choiceCards;
@@ -144071,7 +144194,7 @@ var cardActionOperator = (event) => {
144071
144194
  };
144072
144195
  var startLarkInbound = (input) => {
144073
144196
  const dispatcher = new import_node_sdk.EventDispatcher({}).register({
144074
- "im.message.receive_v1": (event) => {
144197
+ "im.message.receive_v1": async (event) => {
144075
144198
  const mode = input.groupMention ?? "always";
144076
144199
  const parse = (groupMention) => parseInboundMessage(
144077
144200
  event,
@@ -144081,17 +144204,16 @@ var startLarkInbound = (input) => {
144081
144204
  );
144082
144205
  const parsed = parse(mode);
144083
144206
  if (parsed !== null) {
144084
- input.onMessage(parsed);
144207
+ await input.onMessage(parsed);
144085
144208
  return {};
144086
144209
  }
144087
144210
  const chat = event.message;
144088
144211
  if (mode === "always" && input.isSoloGroup !== void 0 && chat?.chat_type !== "p2p") {
144089
144212
  const chatId = chat?.chat_id;
144090
144213
  if (typeof chatId === "string") {
144091
- void input.isSoloGroup(chatId).then((solo) => {
144092
- const relaxed = solo ? parse("never") : null;
144093
- if (relaxed !== null) input.onMessage(relaxed);
144094
- });
144214
+ const solo = await input.isSoloGroup(chatId).catch(() => false);
144215
+ const relaxed = solo ? parse("never") : null;
144216
+ if (relaxed !== null) await input.onMessage(relaxed);
144095
144217
  }
144096
144218
  }
144097
144219
  return {};
@@ -144670,10 +144792,12 @@ var makeLarkAdapter = (deps) => {
144670
144792
  };
144671
144793
  const soloGroupFor = (context2) => async (chatId) => {
144672
144794
  const info = await context2.chatInfo(chatId);
144673
- return info?.userCount !== void 0 && info.botCount !== void 0 && info.userCount <= 1 && info.botCount <= 1;
144795
+ if (info?.userCount === void 0 || info.botCount === void 0) return false;
144796
+ return info.userCount <= 1 && info.botCount <= 1;
144674
144797
  };
144675
144798
  return {
144676
144799
  platform: "lark",
144800
+ redeliversUnacknowledged: true,
144677
144801
  ...larkInboundPersistence((rowId) => messageContexts.get(rowId)?.context),
144678
144802
  botIdentity: (bot) => larkCredentials(bot).appId,
144679
144803
  icon: "lark",
@@ -144781,15 +144905,27 @@ var makeLarkAdapter = (deps) => {
144781
144905
  const key = JSON.stringify([message.chatId, topic?.threadId]);
144782
144906
  const earlier = pendingDeliveries.get(key);
144783
144907
  if (earlier === void 0 && pending === null && reference2 === null && !(prepared instanceof Promise) && !(source instanceof Promise)) {
144784
- deliver(prepared, source);
144908
+ return deliver(prepared, source);
144785
144909
  } else {
144786
- const completed = Promise.all([earlier, pending, reference2, source, prepared]).then(([, , replyTo, names, ready]) => {
144910
+ const completed = Promise.all([
144911
+ earlier?.catch(() => {
144912
+ }),
144913
+ pending,
144914
+ reference2,
144915
+ source,
144916
+ prepared
144917
+ ]).then(([, , replyTo, names, ready]) => {
144787
144918
  if (pendingDeliveries.get(key) === completed) pendingDeliveries.delete(key);
144788
- if (open) deliver(ready, names, replyTo ?? void 0);
144789
- }).catch((error) => handlers.onError(describeError(error))).finally(() => {
144919
+ if (!open) throw new Error("lark inbound connection is closed");
144920
+ return deliver(ready, names, replyTo ?? void 0);
144921
+ }).catch((error) => {
144922
+ handlers.onError(describeError(error));
144923
+ throw error;
144924
+ }).finally(() => {
144790
144925
  if (pendingDeliveries.get(key) === completed) pendingDeliveries.delete(key);
144791
144926
  });
144792
144927
  pendingDeliveries.set(key, completed);
144928
+ return completed;
144793
144929
  }
144794
144930
  }
144795
144931
  });
@@ -146643,13 +146779,13 @@ var makeModelPlan = (core, deps) => {
146643
146779
  import { readThreadFailed, threadOrNull as threadOrNull5 } from "@ycodium-ai/plugin-api";
146644
146780
 
146645
146781
  // src/conclusionOutbox.ts
146646
- var enqueueConclusion = (core, ledger, binding, debt, send) => core.outbound(
146782
+ var enqueueConclusion = (core, ledger, binding, selection, send) => core.outbound(
146647
146783
  chatKeyOf(binding),
146648
146784
  async () => {
146649
- if (debt === void 0) await send();
146650
- else await ledger.deliver(debt, send);
146785
+ if (selection === void 0) await send();
146786
+ else await ledger.deliver(selection, send);
146651
146787
  },
146652
- { droppable: debt !== void 0 }
146788
+ { droppable: selection !== void 0 }
146653
146789
  );
146654
146790
 
146655
146791
  // src/conclusionRecovery.ts
@@ -146665,60 +146801,100 @@ var makeConclusionRecovery = (core, ledger, delivery, failures) => {
146665
146801
  error: describeError(error)
146666
146802
  });
146667
146803
  };
146668
- const recoverConclusion = async (debt) => {
146804
+ const recoverThread = async (threadId, debts, requestedMessageId) => {
146669
146805
  await core.ready;
146670
146806
  if (disposed) return;
146671
146807
  try {
146672
- const execution = await core.host.projections.getThreadExecution(debt.threadId);
146808
+ const execution = await core.host.projections.getThreadExecution(threadId);
146673
146809
  if (disposed) return;
146674
146810
  if (execution === null) {
146675
- ledger.discard(debt);
146676
- return;
146677
- }
146678
- const latest = execution.latestTurn;
146679
- if (latest?.userMessageId !== debt.userMessageId) {
146680
- core.host.log.info("chat discarded a conclusion whose turn is no longer latest", {
146681
- ...debt
146682
- });
146683
- ledger.discard(debt);
146684
- return;
146685
- }
146686
- if (latest.state === "running" || execution.session?.activeTurnId != null) return;
146687
- if (latest.completedAt !== null && Date.parse(latest.completedAt) < Date.now() - MAX_AGE_MS) {
146688
- core.host.log.info("chat discarded a conclusion older than seven days", { ...debt });
146689
- ledger.discard(debt);
146811
+ for (const debt of debts) {
146812
+ if (requestedMessageId === void 0 || debt.userMessageId === requestedMessageId)
146813
+ ledger.discard({ debt, turnUserMessageId: void 0 });
146814
+ }
146690
146815
  return;
146691
146816
  }
146692
- const binding = core.bindingForChatKey(debt.chatKey);
146693
- if (binding === void 0) return;
146694
- enqueueConclusion(core, ledger, binding, debt, async (owed) => {
146695
- try {
146696
- if (latest.state === "error") {
146697
- return delivery.sendText(
146698
- binding,
146699
- debt.threadId,
146700
- failures.forFailure(debt.threadId, execution.session?.lastError),
146701
- true,
146702
- latest.turnId,
146703
- owed
146704
- );
146817
+ const snapshot = indexExecution(execution);
146818
+ const grouped = groupConclusionDebts(snapshot, threadId, debts);
146819
+ const { resolved, byTurn } = grouped;
146820
+ for (const { debt, result: result2 } of resolved.values()) {
146821
+ if (result2.kind === "gone") {
146822
+ if (requestedMessageId === void 0 || debt.userMessageId === requestedMessageId) {
146823
+ core.host.log.info("chat discarded a conclusion whose message has no turn", {
146824
+ ...debt
146825
+ });
146826
+ ledger.discard({ debt, turnUserMessageId: void 0 });
146705
146827
  }
146706
- const send = await delivery.prepareConclusion(debt.threadId, latest.turnId);
146707
- return send(binding, { recovered: true, streaming: false, owed });
146708
- } catch (error) {
146709
- couldNotRecover(debt, error);
146710
- return false;
146711
146828
  }
146712
- });
146829
+ }
146830
+ const requested = requestedMessageId === void 0 ? void 0 : resolved.get(requestedMessageId)?.result;
146831
+ for (const turnId of byTurn.keys()) {
146832
+ if (requestedMessageId !== void 0 && (requested?.kind !== "settled" && requested?.kind !== "merged" || requested.turnId !== turnId))
146833
+ continue;
146834
+ const { selection, handled } = ledger.forExecutionTurn(snapshot, threadId, turnId, grouped);
146835
+ if (handled || selection === void 0 || requestedMessageId !== void 0 && selection.debt.userMessageId !== requestedMessageId)
146836
+ continue;
146837
+ const result2 = resolved.get(selection.debt.userMessageId).result;
146838
+ if (result2.kind === "pending" || result2.kind === "gone") continue;
146839
+ recoverTurn(selection, result2.kind === "settled" ? result2 : result2.turn, execution);
146840
+ }
146713
146841
  } catch (error) {
146714
- couldNotRecover(debt, error);
146842
+ for (const debt of debts) couldNotRecover(debt, error);
146715
146843
  }
146716
146844
  };
146845
+ const recoverTurn = (selection, turn, execution) => {
146846
+ const { debt } = selection;
146847
+ if (turn === void 0 || turn.state === "pending" || turn.state === "running" || execution.session?.activeTurnId === turn.turnId)
146848
+ return;
146849
+ if (turn.completedAt !== null && Date.parse(turn.completedAt) < Date.now() - MAX_AGE_MS) {
146850
+ core.host.log.info("chat discarded a conclusion older than seven days", { ...debt });
146851
+ ledger.discard(selection);
146852
+ return;
146853
+ }
146854
+ const binding = core.bindingForChatKey(selection.debt.chatKey);
146855
+ if (binding === void 0) return;
146856
+ enqueueConclusion(core, ledger, binding, selection, async (owed) => {
146857
+ try {
146858
+ if (turn.state === "error") {
146859
+ const detail = execution.latestTurn?.state === "error" && execution.latestTurn.turnId === turn.turnId ? execution.session?.lastError : void 0;
146860
+ return delivery.sendText(
146861
+ binding,
146862
+ debt.threadId,
146863
+ failures.forFailure(debt.threadId, detail),
146864
+ true,
146865
+ turn.turnId,
146866
+ owed
146867
+ );
146868
+ }
146869
+ const send = await delivery.prepareConclusion(debt.threadId, turn.turnId);
146870
+ return send(binding, { recovered: true, streaming: false, owed });
146871
+ } catch (error) {
146872
+ couldNotRecover(debt, error);
146873
+ return false;
146874
+ }
146875
+ });
146876
+ };
146877
+ const recoverConclusion = async (debt) => {
146878
+ await core.ready;
146879
+ await recoverThread(
146880
+ debt.threadId,
146881
+ Object.values(conclusionDebtsOf(core.state)).filter(
146882
+ (candidate) => candidate.threadId === debt.threadId
146883
+ ),
146884
+ debt.userMessageId
146885
+ );
146886
+ };
146717
146887
  const recoverConclusions = async () => {
146718
146888
  await core.ready;
146889
+ const byThread = /* @__PURE__ */ new Map();
146719
146890
  for (const debt of Object.values(conclusionDebtsOf(core.state))) {
146891
+ const group = byThread.get(debt.threadId) ?? [];
146892
+ group.push(debt);
146893
+ byThread.set(debt.threadId, group);
146894
+ }
146895
+ for (const [threadId, debts] of byThread) {
146720
146896
  if (disposed) return;
146721
- await recoverConclusion(debt);
146897
+ await recoverThread(threadId, debts);
146722
146898
  }
146723
146899
  };
146724
146900
  return { recoverConclusion, recoverConclusions };
@@ -146799,14 +146975,14 @@ var makeNotifications = (core, deps) => {
146799
146975
  await ready;
146800
146976
  const targets = targetsForProject(threadId, await projectOfThread(threadId));
146801
146977
  const owes = Object.values(conclusionDebtsOf(core.state)).some(
146802
- (debt2) => debt2.threadId === threadId
146978
+ (debt) => debt.threadId === threadId
146803
146979
  );
146804
- const { debt, handled, execution } = said || owes || targets.length > 0 ? await conclusions.forTurn(threadId) : { debt: void 0, handled: false, execution: null };
146805
- if (debt !== void 0) {
146806
- const binding = bindingForChatKey(debt.chatKey);
146980
+ const { selection, handled, execution } = said || owes || targets.length > 0 ? await conclusions.forTurn(threadId) : { selection: void 0, handled: false, execution: null };
146981
+ if (selection !== void 0) {
146982
+ const binding = bindingForChatKey(selection.debt.chatKey);
146807
146983
  if (binding !== void 0 && !streamsTo(binding, threadId) && execution?.session?.activeTurnId == null && execution?.latestTurn?.state !== "running" && execution?.latestTurn?.state !== "error" && payload.session?.status !== "error") {
146808
146984
  const turnId = execution?.latestTurn?.turnId ?? null;
146809
- enqueueConclusion(core, conclusions, binding, debt, async (owed) => {
146985
+ enqueueConclusion(core, conclusions, binding, selection, async (owed) => {
146810
146986
  const send = await delivery.prepareConclusion(threadId, turnId);
146811
146987
  return send(binding, { recovered: false, streaming: false, owed });
146812
146988
  });
@@ -146841,9 +147017,9 @@ var makeNotifications = (core, deps) => {
146841
147017
  threadsWithSomethingToReport.delete(threadId);
146842
147018
  await ready;
146843
147019
  const targets = targetsForProject(threadId, await projectOfThread(threadId));
146844
- const { debt, handled, execution } = await conclusions.forTurn(threadId);
146845
- const debtBinding = debt === void 0 ? void 0 : bindingForChatKey(debt.chatKey);
146846
- const recipients = debt !== void 0 ? debtBinding === void 0 ? [] : [debtBinding] : targets;
147020
+ const { selection, handled, execution } = await conclusions.forTurn(threadId);
147021
+ const debtBinding = selection === void 0 ? void 0 : bindingForChatKey(selection.debt.chatKey);
147022
+ const recipients = selection !== void 0 ? debtBinding === void 0 ? [] : [debtBinding] : targets;
146847
147023
  const textTargets = handled ? [] : recipients.filter(
146848
147024
  (binding) => !(streamsTo(binding, threadId) && turnCards.covers(threadId))
146849
147025
  );
@@ -146854,7 +147030,7 @@ var makeNotifications = (core, deps) => {
146854
147030
  if (notice !== null) {
146855
147031
  for (const binding of textTargets) {
146856
147032
  const send = (owed) => delivery.sendText(binding, threadId, notice, false, null, owed);
146857
- enqueueConclusion(core, conclusions, binding, debt, send);
147033
+ enqueueConclusion(core, conclusions, binding, selection, send);
146858
147034
  }
146859
147035
  }
146860
147036
  for (const binding of targets) {
@@ -147844,10 +148020,10 @@ var makeReplyCards = (core, failures, anchors, conclusions, delivery) => {
147844
148020
  onOpened: (card) => commit("turn-card-opened", card),
147845
148021
  onClosed: (cardId) => commit("turn-card-closed", { cardId }),
147846
148022
  finalize: async (threadId, turnId, chatKey, send) => {
147847
- const { debt, handled } = await conclusions.forTurn(threadId, turnId);
148023
+ const { selection, handled } = await conclusions.forTurn(threadId, turnId);
147848
148024
  if (handled) return true;
147849
- if (debt === void 0) return send();
147850
- if (!await conclusions.deliver(debt, send)) return false;
148025
+ if (selection === void 0) return send();
148026
+ if (!await conclusions.deliver(selection, send)) return false;
147851
148027
  const binding = bindingForChatKey(chatKey);
147852
148028
  if (binding === void 0) return true;
147853
148029
  const sendConclusion = await delivery.prepareConclusion(threadId, turnId);
@@ -147855,7 +148031,7 @@ var makeReplyCards = (core, failures, anchors, conclusions, delivery) => {
147855
148031
  return true;
147856
148032
  },
147857
148033
  fallback: async (threadId, turnId, chatKey, view) => {
147858
- const { debt, handled } = await conclusions.forTurn(threadId, turnId);
148034
+ const { selection, handled } = await conclusions.forTurn(threadId, turnId);
147859
148035
  if (handled) return;
147860
148036
  const binding = bindingForChatKey(chatKey);
147861
148037
  if (binding === void 0) return;
@@ -147864,7 +148040,7 @@ var makeReplyCards = (core, failures, anchors, conclusions, delivery) => {
147864
148040
  core,
147865
148041
  conclusions,
147866
148042
  binding,
147867
- debt,
148043
+ selection,
147868
148044
  (owed) => delivery.sendText(binding, threadId, text4, false, turnId, owed)
147869
148045
  );
147870
148046
  },
@@ -148996,6 +149172,7 @@ var makeTelegramAdapter = (deps) => {
148996
149172
  });
148997
149173
  return {
148998
149174
  platform: "telegram",
149175
+ redeliversUnacknowledged: true,
148999
149176
  botIdentity: tokenFingerprint,
149000
149177
  icon: "telegram",
149001
149178
  label: "Telegram",
@@ -149107,14 +149284,16 @@ var makeTelegramAdapter = (deps) => {
149107
149284
  }
149108
149285
  }
149109
149286
  if (!running) break;
149110
- advanceOffset(update);
149111
149287
  const senderId = message.from === void 0 ? null : String(message.from.id);
149112
149288
  const senderName2 = telegramDisplayName(message.from);
149113
149289
  const chatKind = message.chat.type === "private" ? "private" : "group";
149114
149290
  if (message.voice !== void 0 || text4 === "" && resource === null) {
149115
149291
  const unsupported = unsupportedTypeOf(message);
149116
- if (unsupported === null) continue;
149117
- handlers.onMessage({
149292
+ if (unsupported === null) {
149293
+ advanceOffset(update);
149294
+ continue;
149295
+ }
149296
+ await handlers.onMessage({
149118
149297
  messageId: String(message.message_id),
149119
149298
  chatId: chatIdOf(message),
149120
149299
  senderId,
@@ -149123,11 +149302,13 @@ var makeTelegramAdapter = (deps) => {
149123
149302
  resources: [],
149124
149303
  unsupportedType: unsupported
149125
149304
  });
149305
+ if (!running) break;
149306
+ advanceOffset(update);
149126
149307
  continue;
149127
149308
  }
149128
149309
  const parentChatId = parentChatIdOf(message);
149129
149310
  const replyTo = telegramReplyReference(message, botUsername, botId);
149130
- handlers.onMessage({
149311
+ await handlers.onMessage({
149131
149312
  messageId: String(message.message_id),
149132
149313
  chatId: chatIdOf(message),
149133
149314
  ...parentChatId === void 0 ? {} : { parentChatId },
@@ -149147,6 +149328,8 @@ var makeTelegramAdapter = (deps) => {
149147
149328
  }
149148
149329
  ]
149149
149330
  });
149331
+ if (!running) break;
149332
+ advanceOffset(update);
149150
149333
  }
149151
149334
  } catch (error) {
149152
149335
  if (!running) return;
@@ -149727,12 +149910,10 @@ var makeTurns = (core, deps) => {
149727
149910
  );
149728
149911
  if (!dispatched) return null;
149729
149912
  topicRoot?.confirmConsumed();
149730
- if (intentAction?.kind !== "steer") {
149731
- await deps.conclusions.owe(
149732
- { chatKey, threadId, userMessageId, recordedAt: (/* @__PURE__ */ new Date()).toISOString() },
149733
- replayed ? deps.recoverConclusion : void 0
149734
- );
149735
- }
149913
+ await deps.conclusions.owe(
149914
+ { chatKey, threadId, userMessageId, recordedAt: (/* @__PURE__ */ new Date()).toISOString() },
149915
+ replayed ? deps.recoverConclusion : void 0
149916
+ );
149736
149917
  } finally {
149737
149918
  registered();
149738
149919
  }
@@ -151167,7 +151348,7 @@ ${kept}`;
151167
151348
  if (message.chatType !== "single") return;
151168
151349
  client.rememberRequest(message.chatId, message.requestId);
151169
151350
  client.rememberConversation(message.chatId, message.conversation);
151170
- handlers.onMessage({
151351
+ void handlers.onMessage({
151171
151352
  // 不保存短效 req_id:重启后的回话走新连接的 aibot_send_msg 主动发送,
151172
151353
  // 不冒充原消息的回答,也不复用旧连接的 rememberRequest / 流状态。
151173
151354
  messageId: message.messageId,
@@ -151184,6 +151365,10 @@ ${kept}`;
151184
151365
  mimeType: resource.mimeType,
151185
151366
  kind: resource.kind
151186
151367
  }))
151368
+ }).catch((error) => {
151369
+ deps.log.warn("wecom could not persist inbound message", {
151370
+ error: describeError(error)
151371
+ });
151187
151372
  });
151188
151373
  }
151189
151374
  }
@@ -152279,12 +152464,19 @@ var makeWecomAppAdapter = (deps) => {
152279
152464
  }
152280
152465
  const messageId = message.msgid ?? `${chatId}:${message.createTime ?? Date.now()}`;
152281
152466
  const base2 = { messageId, chatId, senderId: chatId };
152467
+ const receive = (inbound) => {
152468
+ void handlers.onMessage(inbound).catch((error) => {
152469
+ deps.log.warn("wecom-app could not persist inbound message", {
152470
+ error: describeError(error)
152471
+ });
152472
+ });
152473
+ };
152282
152474
  if (message.msgtype === "text") {
152283
- handlers.onMessage({ ...base2, text: message.text?.content?.trim() ?? "", resources: [] });
152475
+ receive({ ...base2, text: message.text?.content?.trim() ?? "", resources: [] });
152284
152476
  return;
152285
152477
  }
152286
152478
  if (message.msgtype === "image" && message.mediaId !== void 0 && message.mediaId !== "") {
152287
- handlers.onMessage({
152479
+ receive({
152288
152480
  ...base2,
152289
152481
  text: "",
152290
152482
  resources: [
@@ -152293,7 +152485,7 @@ var makeWecomAppAdapter = (deps) => {
152293
152485
  });
152294
152486
  return;
152295
152487
  }
152296
- handlers.onMessage({
152488
+ receive({
152297
152489
  ...base2,
152298
152490
  text: "",
152299
152491
  resources: [],
@@ -152503,7 +152695,7 @@ var makeWecomAppAdapter = (deps) => {
152503
152695
  // src/index.ts
152504
152696
  var definition = {
152505
152697
  id: "im",
152506
- apiVersion: 22,
152698
+ apiVersion: 23,
152507
152699
  // 第二个参数只给插件自己的测试夹具:换掉文件 IO 与派发重试的等待。宿主只传 ctx。
152508
152700
  activate: async (ctx, testing = {}) => {
152509
152701
  const { dispatchSleep } = testing;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ycodium-ai/plugin-im",
3
- "version": "0.2.3216",
3
+ "version": "0.2.3220",
4
4
  "description": "Talk to your agents from Telegram, WeCom, Lark, or WeChat (experimental).",
5
5
  "keywords": [
6
6
  "im",
@@ -32,7 +32,8 @@
32
32
  "markdown-it": "^15.0.2",
33
33
  "react": "19.2.6",
34
34
  "vite-plus": "0.2.2",
35
- "@ycodium-ai/plugin-api": "0.2.3216"
35
+ "ycodium-react-test-harness": "0.0.0",
36
+ "@ycodium-ai/plugin-api": "0.2.3220"
36
37
  },
37
38
  "ycodium": {
38
39
  "plugin": "./dist/index.js",