@unlinearity/cli-wechat-bridge 1.1.4 → 1.1.5

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.
@@ -12,11 +12,11 @@ import { forwardWechatFinalReply } from "../bridge/bridge-final-reply.js";
12
12
  import { readBridgeLockFile, } from "../bridge/bridge-state.js";
13
13
  import { getProcessRecordByPid, isWechatDaemonCommandLine, killProcessTreeSync, listWechatDaemonProcesses, reapOrphanedOpencodeProcesses, reapPeerBridgeProcesses, } from "../bridge/bridge-process-reaper.js";
14
14
  import { buildOneTimeCode, buildWechatInboundPrompt, formatApprovalMessage, formatDuration, formatMirroredUserInputMessage, formatPendingApprovalReminder, formatPendingUserInputReminder, formatSessionSwitchMessage, formatTaskFailedMessage, formatUserInputRequestMessage, MESSAGE_START_GRACE_MS, nowIso, OutputBatcher, parsePendingUserInputAnswerCommand, parseWechatControlCommand, truncatePreview, } from "../bridge/bridge-utils.js";
15
+ import { ResumeSessionCoordinator, isWechatResumeEnabled, shouldForwardSessionSwitchEvent, } from "../bridge/bridge-session-resume.js";
15
16
  import { WECHAT_SEND_MAX_ATTEMPTS, computeWechatSendRetryDelayMs, formatUserFacingBridgeFatalError, formatUserFacingInboundError, formatWechatContextTokenStaleLogEntry, formatWechatSendFailureLogEntry, isRetryableWechatSendError, shouldForwardBridgeEventToWechat, } from "../bridge/wechat-forwarding.js";
16
17
  import { BRIDGE_LOCK_FILE, BRIDGE_LOG_FILE, appendBoundedLog, ensureChannelDataDir, migrateLegacyChannelFiles, } from "../wechat/channel-config.js";
17
18
  import { ensureWechatCredentials } from "../wechat/setup.js";
18
19
  import { formatBindCommandUsage, formatBindingsListMessage, isBindCommandPrefix, listBindings, loadEmojiBindings, parseEmojiBindingsCommand, removeBinding, resolveEmojiCommand, setBinding, } from "./emoji-bindings.js";
19
- import { formatResumeSessionList } from "../bridge/bridge-utils.js";
20
20
  import { classifyWechatTransportError, DEFAULT_LONG_POLL_TIMEOUT_MS, describeWechatTransportError, isWechatContextTokenStaleError, WeChatTransport, } from "../wechat/wechat-transport.js";
21
21
  import { createRuntimeHost, } from "../runtime/create-runtime-host.js";
22
22
  import { clearLocalCompanionEndpoint, clearLocalCompanionOccupancy, readLocalCompanionEndpoint, } from "../companion/local-companion-link.js";
@@ -893,6 +893,10 @@ class WechatDaemon {
893
893
  }),
894
894
  pendingConfirmations: [],
895
895
  pendingUserInput: null,
896
+ resumeCoordinator: new ResumeSessionCoordinator({
897
+ adapter,
898
+ runtime,
899
+ }),
896
900
  activeTask: null,
897
901
  lastOutputAt: 0,
898
902
  };
@@ -1014,8 +1018,12 @@ class WechatDaemon {
1014
1018
  }
1015
1019
  break;
1016
1020
  case "session_switched":
1021
+ if (event.source === "local") {
1022
+ slot.resumeCoordinator.clear();
1023
+ }
1017
1024
  appendDaemonLog(`session_switched: adapter=${slot.adapter} session=${event.sessionId} source=${event.source} reason=${event.reason}`);
1018
- if (shouldForwardBridgeEventToWechat(slot.adapter, event.type)) {
1025
+ if (shouldForwardSessionSwitchEvent(event.reason) &&
1026
+ shouldForwardBridgeEventToWechat(slot.adapter, event.type)) {
1019
1027
  this.trackWechatForwardTask(slot.outputBatcher.flushNow().then(async () => {
1020
1028
  await this.queueWechatMessage(this.authorizedUserId, prefixDaemonAdapterMessage(slot.adapter, formatSessionSwitchMessage({
1021
1029
  adapter: slot.adapter,
@@ -1027,8 +1035,12 @@ class WechatDaemon {
1027
1035
  }
1028
1036
  break;
1029
1037
  case "thread_switched":
1038
+ if (event.source === "local") {
1039
+ slot.resumeCoordinator.clear();
1040
+ }
1030
1041
  appendDaemonLog(`thread_switched: adapter=${slot.adapter} thread=${event.threadId} source=${event.source} reason=${event.reason}`);
1031
- if (shouldForwardBridgeEventToWechat(slot.adapter, event.type)) {
1042
+ if (shouldForwardSessionSwitchEvent(event.reason) &&
1043
+ shouldForwardBridgeEventToWechat(slot.adapter, event.type)) {
1032
1044
  this.trackWechatForwardTask(slot.outputBatcher.flushNow().then(async () => {
1033
1045
  await this.queueWechatMessage(this.authorizedUserId, prefixDaemonAdapterMessage(slot.adapter, formatSessionSwitchMessage({
1034
1046
  adapter: slot.adapter,
@@ -1207,29 +1219,33 @@ class WechatDaemon {
1207
1219
  await this.queueWechatMessage(message.senderId, formatDaemonStatus(this.getStatus()));
1208
1220
  return;
1209
1221
  case "resume": {
1210
- if (activeSlot.adapter !== "opencode") {
1222
+ if (!isWechatResumeEnabled(activeSlot.adapter)) {
1211
1223
  await this.queueWechatMessage(message.senderId, `WeChat /resume is disabled for ${activeSlot.adapter} in daemon mode. Use /resume directly inside the visible terminal; WeChat will follow that local session.`);
1212
1224
  return;
1213
1225
  }
1214
- if (command.type !== "resume") {
1215
- return;
1216
- }
1217
1226
  try {
1227
+ let resumeSlot = activeSlot;
1228
+ if (activeSlot.adapter === "codex" && command.target) {
1229
+ const visible = await this.ensureSlot("codex", {
1230
+ openVisible: true,
1231
+ reuseExistingVisible: true,
1232
+ });
1233
+ if (!visible.activated) {
1234
+ throw new Error(formatDaemonSwitchResultDetail(visible));
1235
+ }
1236
+ resumeSlot = this.slots.get("codex") ?? activeSlot;
1237
+ }
1218
1238
  if (command.target) {
1219
- await activeSlot.runtime.resumeSession(command.target);
1220
- await this.queueWechatMessage(message.senderId, prefixDaemonAdapterMessage(activeSlot.adapter, `Resumed OpenCode session ${command.target}.`));
1239
+ await resumeSlot.outputBatcher.flushNow();
1221
1240
  }
1222
- else {
1223
- const candidates = await activeSlot.runtime.listResumeSessions(8);
1224
- await this.queueWechatMessage(message.senderId, prefixDaemonAdapterMessage(activeSlot.adapter, formatResumeSessionList({
1225
- adapter: activeSlot.adapter,
1226
- candidates,
1227
- currentSessionId: activeSlot.runtime.getState().sharedSessionId,
1228
- })));
1241
+ const result = await resumeSlot.resumeCoordinator.execute(command.target);
1242
+ if (result.kind === "resumed") {
1243
+ resumeSlot.activeTask = null;
1229
1244
  }
1245
+ await this.queueWechatMessage(message.senderId, prefixDaemonAdapterMessage(resumeSlot.adapter, result.message));
1230
1246
  }
1231
1247
  catch (error) {
1232
- await this.queueWechatMessage(message.senderId, prefixDaemonAdapterMessage(activeSlot.adapter, `Failed to resume OpenCode session: ${error instanceof Error ? error.message : String(error)}`));
1248
+ await this.queueWechatMessage(message.senderId, prefixDaemonAdapterMessage(activeSlot.adapter, error instanceof Error ? error.message : String(error)));
1233
1249
  }
1234
1250
  return;
1235
1251
  }
@@ -1242,6 +1258,7 @@ class WechatDaemon {
1242
1258
  activeSlot.outputBatcher.clear();
1243
1259
  activeSlot.pendingConfirmations = [];
1244
1260
  activeSlot.pendingUserInput = null;
1261
+ activeSlot.resumeCoordinator.clear();
1245
1262
  await activeSlot.runtime.createSession();
1246
1263
  appendDaemonLog(`new_session: adapter=${activeSlot.adapter}`);
1247
1264
  return;
@@ -1257,6 +1274,7 @@ class WechatDaemon {
1257
1274
  activeSlot.outputBatcher.clear();
1258
1275
  activeSlot.pendingConfirmations = [];
1259
1276
  activeSlot.pendingUserInput = null;
1277
+ activeSlot.resumeCoordinator.clear();
1260
1278
  await activeSlot.runtime.reset();
1261
1279
  appendDaemonLog(`reset: adapter=${activeSlot.adapter}`);
1262
1280
  await this.queueWechatMessage(message.senderId, prefixDaemonAdapterMessage(activeSlot.adapter, "Worker session has been reset."));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unlinearity/cli-wechat-bridge",
3
- "version": "1.1.4",
3
+ "version": "1.1.5",
4
4
  "description": "CLI WeChat Bridge for Codex, Claude Code, OpenCode, and Pi over WeChat",
5
5
  "type": "module",
6
6
  "files": [
@@ -35,13 +35,9 @@
35
35
  },
36
36
  "bin": {
37
37
  "wechat-codex": "bin/wechat-codex.mjs",
38
- "wechat-codex-start": "bin/wechat-codex-start.mjs",
39
38
  "wechat-claude": "bin/wechat-claude.mjs",
40
- "wechat-claude-start": "bin/wechat-claude-start.mjs",
41
39
  "wechat-opencode": "bin/wechat-opencode.mjs",
42
- "wechat-opencode-start": "bin/wechat-opencode-start.mjs",
43
40
  "wechat-pi": "bin/wechat-pi.mjs",
44
- "wechat-pi-start": "bin/wechat-pi-start.mjs",
45
41
  "wechat-daemon": "bin/wechat-daemon.mjs",
46
42
  "wechat-setup": "bin/wechat-setup.mjs",
47
43
  "wechat-check-update": "bin/wechat-check-update.mjs"
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { runDeprecatedJsEntry } from "./_run-entry.mjs";
4
-
5
- runDeprecatedJsEntry(
6
- "wechat-claude-start",
7
- "wechat-claude",
8
- "dist/companion/local-companion-start.js",
9
- ["--adapter", "claude"],
10
- );
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { runDeprecatedJsEntry } from "./_run-entry.mjs";
4
-
5
- runDeprecatedJsEntry(
6
- "wechat-codex-start",
7
- "wechat-codex",
8
- "dist/companion/local-companion-start.js",
9
- ["--adapter", "codex"],
10
- );
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { runDeprecatedJsEntry } from "./_run-entry.mjs";
4
-
5
- runDeprecatedJsEntry(
6
- "wechat-opencode-start",
7
- "wechat-opencode",
8
- "dist/companion/local-companion-start.js",
9
- ["--adapter", "opencode"],
10
- );
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { runDeprecatedJsEntry } from "./_run-entry.mjs";
4
-
5
- runDeprecatedJsEntry(
6
- "wechat-pi-start",
7
- "wechat-pi",
8
- "dist/companion/local-companion-start.js",
9
- ["--adapter", "pi"],
10
- );