codex-to-im 1.0.47 → 1.0.49

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/daemon.mjs +39 -58
  2. package/package.json +1 -1
package/dist/daemon.mjs CHANGED
@@ -359,6 +359,10 @@ function getTerminalDrainTimeoutMs() {
359
359
  function isAbortError(error) {
360
360
  return error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
361
361
  }
362
+ function isWindowsProcessTerminationParseNoise(message) {
363
+ const normalized = message.replace(/\s+/g, " ").trim().toLowerCase();
364
+ return normalized.startsWith("failed to parse item: success:") && normalized.includes("the process with pid") && normalized.includes("has been terminated");
365
+ }
362
366
  function normalizeTaskText(value) {
363
367
  return typeof value === "string" ? value.trim() : "";
364
368
  }
@@ -505,6 +509,7 @@ var init_codex_provider = __esm({
505
509
  }
506
510
  let sawAnyEvent = false;
507
511
  let sawTerminalEvent = false;
512
+ let sawCompletedAssistantContent = false;
508
513
  const runAbortController = new AbortController();
509
514
  let terminalDrainTimer = null;
510
515
  const clearTerminalDrainTimer = () => {
@@ -555,10 +560,14 @@ var init_codex_provider = __esm({
555
560
  case "item.updated":
556
561
  case "item.completed": {
557
562
  const item = event.item;
563
+ const phase = event.type === "item.started" ? "started" : event.type === "item.updated" ? "updated" : "completed";
564
+ if (phase === "completed" && item.type === "agent_message" && typeof item.text === "string" && item.text.trim()) {
565
+ sawCompletedAssistantContent = true;
566
+ }
558
567
  self.handleItemEvent(
559
568
  controller,
560
569
  item,
561
- event.type === "item.started" ? "started" : event.type === "item.updated" ? "updated" : "completed",
570
+ phase,
562
571
  params.sessionId,
563
572
  emittedToolStarts
564
573
  );
@@ -618,6 +627,10 @@ var init_codex_provider = __esm({
618
627
  if (sawTerminalEvent && (runAbortController.signal.aborted || isAbortError(err)) && !userAborted) {
619
628
  break;
620
629
  }
630
+ if ((sawTerminalEvent || sawCompletedAssistantContent) && isWindowsProcessTerminationParseNoise(message)) {
631
+ console.warn("[codex-provider] Suppressed Codex SDK Windows process cleanup parse noise:", message);
632
+ break;
633
+ }
621
634
  if (savedThreadId && !retryFresh && !sawAnyEvent && shouldRetryFreshThread(message)) {
622
635
  console.warn("[codex-provider] Resume failed, retrying with a fresh thread:", message);
623
636
  self.clearCachedThreadId(params.sessionId);
@@ -1402,7 +1415,8 @@ function buildPermissionButtonCard(text2, permissionRequestId, chatId) {
1402
1415
  // src/lib/bridge/adapters/feishu-adapter.ts
1403
1416
  var DEDUP_MAX = 1e3;
1404
1417
  var MAX_FILE_SIZE = 20 * 1024 * 1024;
1405
- var TYPING_EMOJI = "Typing";
1418
+ var COMPLETED_EMOJI = "DONE";
1419
+ var ERROR_EMOJI = "ERROR";
1406
1420
  var CARD_THROTTLE_MS = 1e3;
1407
1421
  var CARD_REQUEST_TIMEOUT_MS = 15e3;
1408
1422
  var CARD_FINALIZE_FLUSH_WAIT_EXTRA_MS = 1e3;
@@ -1471,14 +1485,8 @@ var FeishuAdapter = class extends BaseChannelAdapter {
1471
1485
  botOpenId = null;
1472
1486
  /** All known bot IDs (open_id, user_id, union_id) for mention matching. */
1473
1487
  botIds = /* @__PURE__ */ new Set();
1474
- /** Track last incoming message ID per chat for typing indicator. */
1488
+ /** Track last incoming message ID per chat for replying with streaming cards. */
1475
1489
  lastIncomingMessageId = /* @__PURE__ */ new Map();
1476
- /** Track active typing reaction IDs per stream key for cleanup. */
1477
- typingReactions = /* @__PURE__ */ new Map();
1478
- /** Track in-flight typing reaction creates so repeated status updates stay idempotent. */
1479
- typingReactionCreatePromises = /* @__PURE__ */ new Map();
1480
- /** Track streams that ended before the async reaction create finished. */
1481
- typingReactionCleanupRequested = /* @__PURE__ */ new Set();
1482
1490
  /** Active streaming card state per stream key. */
1483
1491
  activeCards = /* @__PURE__ */ new Map();
1484
1492
  /** In-flight card creation promises per stream key — prevents duplicate creation. */
@@ -1585,9 +1593,6 @@ var FeishuAdapter = class extends BaseChannelAdapter {
1585
1593
  this.cardCreatePromises.clear();
1586
1594
  this.seenMessageIds.clear();
1587
1595
  this.lastIncomingMessageId.clear();
1588
- this.typingReactions.clear();
1589
- this.typingReactionCreatePromises.clear();
1590
- this.typingReactionCleanupRequested.clear();
1591
1596
  console.log("[feishu-adapter] Stopped");
1592
1597
  }
1593
1598
  isRunning() {
@@ -1597,67 +1602,24 @@ var FeishuAdapter = class extends BaseChannelAdapter {
1597
1602
  consumeOne() {
1598
1603
  return this.consumeInboundMessage(this.running);
1599
1604
  }
1600
- // ── Typing indicator (Openclaw-style reaction) ─────────────
1605
+ // ── Streaming lifecycle hooks ──────────────────────────────
1601
1606
  /**
1602
- * Add a "Typing" emoji reaction to the user's message and create streaming card.
1607
+ * Create the streaming card as early as possible.
1603
1608
  * Called by bridge-manager via onMessageStart().
1604
1609
  */
1605
1610
  onMessageStart(chatId, streamKey) {
1606
1611
  const messageId = this.lastIncomingMessageId.get(chatId);
1607
- const reactionKey = this.resolveStreamKey(chatId, streamKey);
1608
1612
  if (messageId && this.isStreamingEnabled()) {
1609
1613
  this.createStreamingCard(chatId, messageId, streamKey).catch(() => {
1610
1614
  });
1611
1615
  }
1612
- if (!messageId || !this.restClient) return;
1613
- if (this.typingReactions.has(reactionKey) || this.typingReactionCreatePromises.has(reactionKey)) {
1614
- return;
1615
- }
1616
- const createPromise = this.restClient.im.messageReaction.create({
1617
- path: { message_id: messageId },
1618
- data: { reaction_type: { emoji_type: TYPING_EMOJI } }
1619
- }).then((res) => {
1620
- const reactionId = res?.data?.reaction_id;
1621
- if (reactionId) {
1622
- this.typingReactions.set(reactionKey, { messageId, reactionId });
1623
- if (this.typingReactionCleanupRequested.delete(reactionKey)) {
1624
- this.removeTypingReaction(reactionKey);
1625
- }
1626
- }
1627
- }).catch((err) => {
1628
- const code2 = err?.code;
1629
- if (code2 !== 99991400 && code2 !== 99991403) {
1630
- console.warn("[feishu-adapter] Typing indicator failed:", err instanceof Error ? err.message : err);
1631
- }
1632
- }).finally(() => {
1633
- this.typingReactionCreatePromises.delete(reactionKey);
1634
- if (!this.typingReactions.has(reactionKey)) {
1635
- this.typingReactionCleanupRequested.delete(reactionKey);
1636
- }
1637
- });
1638
- this.typingReactionCreatePromises.set(reactionKey, createPromise);
1639
1616
  }
1640
1617
  /**
1641
- * Remove the "Typing" emoji reaction and clean up card state.
1618
+ * Clean up card state.
1642
1619
  * Called by bridge-manager via onMessageEnd().
1643
1620
  */
1644
1621
  onMessageEnd(chatId, streamKey) {
1645
1622
  this.cleanupCard(chatId, streamKey);
1646
- const reactionKey = this.resolveStreamKey(chatId, streamKey);
1647
- if (this.typingReactionCreatePromises.has(reactionKey) && !this.typingReactions.has(reactionKey)) {
1648
- this.typingReactionCleanupRequested.add(reactionKey);
1649
- return;
1650
- }
1651
- this.removeTypingReaction(reactionKey);
1652
- }
1653
- removeTypingReaction(reactionKey) {
1654
- const reaction = this.typingReactions.get(reactionKey);
1655
- if (!reaction || !this.restClient) return;
1656
- this.typingReactions.delete(reactionKey);
1657
- this.restClient.im.messageReaction.delete({
1658
- path: { message_id: reaction.messageId, reaction_id: reaction.reactionId }
1659
- }).catch(() => {
1660
- });
1661
1623
  }
1662
1624
  // ── Card Action Handler ─────────────────────────────────────
1663
1625
  /**
@@ -2042,6 +2004,10 @@ ${trimmedResponse}`;
2042
2004
  sequence: state.sequence
2043
2005
  }
2044
2006
  }));
2007
+ const terminalReactionEmoji = status === "completed" ? COMPLETED_EMOJI : status === "error" ? ERROR_EMOJI : null;
2008
+ if (terminalReactionEmoji) {
2009
+ await this.addTerminalReaction(cardKey, state.messageId, terminalReactionEmoji);
2010
+ }
2045
2011
  console.log(`[feishu-adapter] Card finalized: streamKey=${cardKey}, cardId=${state.cardId}, status=${status}, elapsed=${formatElapsed(elapsedMs)}`);
2046
2012
  return true;
2047
2013
  } catch (err) {
@@ -2051,6 +2017,21 @@ ${trimmedResponse}`;
2051
2017
  this.activeCards.delete(cardKey);
2052
2018
  }
2053
2019
  }
2020
+ async addTerminalReaction(streamKey, messageId, emojiType) {
2021
+ const messageReaction = this.restClient?.im?.messageReaction;
2022
+ if (typeof messageReaction?.create !== "function") return;
2023
+ try {
2024
+ await this.withFeishuRequestTimeout(streamKey, `im.messageReaction.create:${emojiType}`, () => messageReaction.create({
2025
+ path: { message_id: messageId },
2026
+ data: { reaction_type: { emoji_type: emojiType } }
2027
+ }));
2028
+ } catch (err) {
2029
+ const code2 = err?.code;
2030
+ if (code2 !== 99991400 && code2 !== 99991403) {
2031
+ console.warn("[feishu-adapter] Terminal reaction failed:", err instanceof Error ? err.message : err);
2032
+ }
2033
+ }
2034
+ }
2054
2035
  /**
2055
2036
  * Clean up card state without finalizing (e.g. on unexpected errors).
2056
2037
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-to-im",
3
- "version": "1.0.47",
3
+ "version": "1.0.49",
4
4
  "description": "Installable Codex-to-IM bridge with local setup UI and background service",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/zhangle1987/codex-to-im#readme",