@unlinearity/cli-wechat-bridge 1.1.5 → 1.2.0

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 (89) hide show
  1. package/LICENSE.txt +661 -661
  2. package/README.md +434 -427
  3. package/bin/_run-entry.mjs +47 -9
  4. package/bin/wechat-check-update.mjs +6 -1
  5. package/bin/wechat-claude.mjs +6 -1
  6. package/bin/wechat-codex.mjs +6 -1
  7. package/bin/wechat-daemon.mjs +6 -1
  8. package/bin/wechat-opencode.mjs +6 -1
  9. package/bin/wechat-pi.mjs +6 -1
  10. package/bin/wechat-setup.mjs +6 -1
  11. package/bin/wecom-check-update.mjs +10 -0
  12. package/bin/wecom-claude.mjs +10 -0
  13. package/bin/wecom-codex.mjs +10 -0
  14. package/bin/wecom-daemon.mjs +10 -0
  15. package/bin/wecom-opencode.mjs +10 -0
  16. package/bin/wecom-pi.mjs +10 -0
  17. package/bin/wecom-setup.mjs +10 -0
  18. package/dist/bridge/adapter-control.js +58 -0
  19. package/dist/bridge/bridge-adapters.claude.js +163 -5
  20. package/dist/bridge/bridge-adapters.codex.js +331 -29
  21. package/dist/bridge/bridge-adapters.core.js +27 -2
  22. package/dist/bridge/bridge-adapters.opencode.js +156 -1
  23. package/dist/bridge/bridge-adapters.pi.js +46 -1
  24. package/dist/bridge/bridge-adapters.shared.js +8 -1
  25. package/dist/bridge/bridge-controller.js +6 -0
  26. package/dist/bridge/bridge-final-reply.js +8 -65
  27. package/dist/bridge/bridge-process-reaper.js +41 -2
  28. package/dist/bridge/bridge-state.js +17 -4
  29. package/dist/bridge/bridge-utils.js +10 -127
  30. package/dist/bridge/claude-hook.js +6 -0
  31. package/dist/bridge/claude-native-controls.js +133 -0
  32. package/dist/bridge/native-terminal-control.js +259 -0
  33. package/dist/bridge/wechat-bridge.js +520 -274
  34. package/dist/bridge/wechat-forwarding.js +8 -58
  35. package/dist/bridge/wechat-outbound-queue.js +8 -0
  36. package/dist/channels/wechat/bridge-final-reply.js +56 -0
  37. package/dist/channels/wechat/channel-message.js +24 -0
  38. package/dist/channels/wechat/index.js +11 -0
  39. package/dist/channels/wechat/wechat-channel-port.js +67 -0
  40. package/dist/channels/wechat/wechat-driver.js +191 -0
  41. package/dist/channels/wechat/wechat-forwarding.js +97 -0
  42. package/dist/channels/wechat/wechat-outbound-queue.js +118 -0
  43. package/dist/channels/wecom/bridge-final-reply.js +32 -0
  44. package/dist/channels/wecom/setup-cli.js +12 -0
  45. package/dist/channels/wecom/setup.js +182 -0
  46. package/dist/channels/wecom/wecom-channel-port.js +45 -0
  47. package/dist/channels/wecom/wecom-config.js +72 -0
  48. package/dist/channels/wecom/wecom-driver.js +106 -0
  49. package/dist/channels/wecom/wecom-media.js +147 -0
  50. package/dist/channels/wecom/wecom-message.js +149 -0
  51. package/dist/channels/wecom/wecom-transport.js +314 -0
  52. package/dist/commands/check-update.js +6 -0
  53. package/dist/companion/codex-remote-client.js +30 -3
  54. package/dist/companion/codex-visible-client-link.js +107 -0
  55. package/dist/companion/local-companion-link.js +6 -0
  56. package/dist/companion/local-companion-start.js +62 -11
  57. package/dist/companion/local-companion.js +25 -1
  58. package/dist/companion/opencode-tui-bridge-plugin.js +6 -0
  59. package/dist/companion/pi-tui-bridge-extension.js +62 -3
  60. package/dist/core/bridge-control.js +42 -0
  61. package/dist/core/bridge-defer.js +25 -0
  62. package/dist/core/bridge-event-forwarder.js +50 -0
  63. package/dist/core/bridge-message-router.js +32 -0
  64. package/dist/core/channel-driver.js +1 -0
  65. package/dist/core/channel-types.js +3 -0
  66. package/dist/core/conversation-routing.js +58 -0
  67. package/dist/core/direct-run.js +23 -0
  68. package/dist/core/inbound-message-claims.js +67 -0
  69. package/dist/core/outbound-target.js +32 -0
  70. package/dist/core/text-utils.js +69 -0
  71. package/dist/core/turn-coordinator.js +85 -0
  72. package/dist/daemon/daemon-link.js +9 -0
  73. package/dist/daemon/emoji-bindings.js +6 -0
  74. package/dist/daemon/wechat-daemon.js +827 -209
  75. package/dist/i18n/index.js +6 -0
  76. package/dist/i18n/messages-en.js +11 -3
  77. package/dist/i18n/messages-zh.js +11 -3
  78. package/dist/runtime/create-runtime-host.js +6 -0
  79. package/dist/runtime/legacy-adapter-runtime.js +15 -0
  80. package/dist/runtime/runtime-types.js +3 -0
  81. package/dist/utils/atomic-file.js +6 -0
  82. package/dist/utils/doctor.js +36 -10
  83. package/dist/utils/version-checker.js +17 -11
  84. package/dist/wechat/channel-config.js +6 -0
  85. package/dist/wechat/setup.js +119 -13
  86. package/dist/wechat/wechat-channel.js +6 -0
  87. package/dist/wechat/wechat-transport.js +169 -58
  88. package/package.json +14 -4
  89. package/scripts/ensure-node-pty-permissions.mjs +73 -73
@@ -1,3 +1,11 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // Copyright (c) 2026 UNLINEARITY <unlinearity@gmail.com>
3
+ // Source: https://github.com/UNLINEARITY/CLI-WeChat-Bridge
4
+ // This file is part of CLI-WeChat-Bridge. Modifications and derivative works
5
+ // must be released under AGPL-3.0-or-later with full source code; see
6
+ // LICENSE.txt. Network services built on it must offer source to users.
7
+ import { NativeTerminalControl, readClaudePermissionMode } from "./native-terminal-control.js";
8
+ import { ClaudeNativeControls } from "./claude-native-controls.js";
1
9
  import fs from "node:fs";
2
10
  import net from "node:net";
3
11
  import os from "node:os";
@@ -7,7 +15,8 @@ import { buildLocalCompanionToken } from "../companion/local-companion-link.js";
7
15
  import { t } from "../i18n/index.js";
8
16
  import { ensureWorkspaceChannelDir } from "../wechat/channel-config.js";
9
17
  import { buildClaudeFailureMessage, buildClaudeHookScript, buildClaudeHookSettings, buildClaudePermissionDecisionHookOutput, buildClaudePermissionApprovalRequest, extractClaudeAssistantMessageText, extractClaudeResumeConversationId, extractClaudeTranscriptFinalReply, findInjectedClaudePromptIndex, getClaudePermissionAutoResponse, getClaudeWechatOutboundAttachmentDenyMessage, normalizeClaudeAssistantMessage, parseClaudeHookPayload, } from "./claude-hooks.js";
10
- import { detectCliApproval, isThinkingForwardEnabled, normalizeOutput, nowIso, truncatePreview, } from "./bridge-utils.js";
18
+ import { detectCliApproval, isThinkingForwardEnabled, } from "./bridge-utils.js";
19
+ import { normalizeOutput, nowIso, truncatePreview } from "../core/text-utils.js";
11
20
  import { AbstractPtyAdapter } from "./bridge-adapters.core.js";
12
21
  import * as shared from "./bridge-adapters.shared.js";
13
22
  const { CLAUDE_HOOK_LISTEN_HOST, CLAUDE_WECHAT_WORKING_NOTICE_DELAY_MS, DEFAULT_COLS, DEFAULT_ROWS, MODULE_DIR, buildClaudeCliArgs, delay, isClaudeInvalidResumeError, quotePosixCommandArg, quoteWindowsCommandArg, shouldIncludeClaudeNoAltScreen, } = shared;
@@ -17,6 +26,8 @@ const CLAUDE_COMPACT_DEDUP_MS = 2_000;
17
26
  const CLAUDE_BRACKETED_PASTE_START = "\u001b[200~";
18
27
  const CLAUDE_BRACKETED_PASTE_END = "\u001b[201~";
19
28
  const CLAUDE_REMOTE_ENTER_DELAY_MS = 40;
29
+ const CLAUDE_REMOTE_ENTER_RETRY_DELAYS_MS = [200, 500, 1_000];
30
+ const CLAUDE_INTERACTIVE_READY_TIMEOUT_MS = 10_000;
20
31
  const CLAUDE_STARTUP_OUTPUT_BUFFER_LIMIT = 4_000;
21
32
  const CLAUDE_SESSION_METADATA_READ_BYTES = 64 * 1024;
22
33
  const CLAUDE_MAX_SANITIZED_PROJECT_PATH = 200;
@@ -336,6 +347,26 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
336
347
  polledTranscriptSessionId = null;
337
348
  pendingResume = null;
338
349
  localResumeSessionEndObserved = false;
350
+ interactiveReady = true;
351
+ interactiveReadyTimer = null;
352
+ interactiveReadyWaiters = new Set();
353
+ remoteSubmitRetryTimer = null;
354
+ remoteSubmitRetryIndex = 0;
355
+ remoteSubmitText = null;
356
+ terminalControl = new NativeTerminalControl({
357
+ write: (text) => this.writeToPty(text),
358
+ identity: () => JSON.stringify([this.state.pid, this.runtimeSessionId, this.resumeConversationId]),
359
+ isPromptEmpty: () => readClaudePermissionMode(this.terminalControl.text) !== null,
360
+ assertReady: () => {
361
+ if (!this.pty || this.state.status !== "idle" || this.pendingApproval || this.pendingResume) {
362
+ throw new Error("Claude must be idle with no pending approval before using remote controls.");
363
+ }
364
+ },
365
+ });
366
+ nativeControls = new ClaudeNativeControls(this.terminalControl, () => this.resumeConversationId ?? this.runtimeSessionId ?? "");
367
+ async listModels() { return this.nativeControls.listModels(); }
368
+ async selectModel(modelId) { return this.nativeControls.selectModel(modelId); }
369
+ async setPlanMode(enabled) { return this.nativeControls.setPlanMode(enabled); }
339
370
  constructor(options) {
340
371
  super(options);
341
372
  const shouldRestoreInitialSession = options.sessionStartMode !== "new";
@@ -363,6 +394,7 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
363
394
  if (this.pty) {
364
395
  return;
365
396
  }
397
+ this.beginInteractiveReadinessWait();
366
398
  this.startupOutputBuffer = "";
367
399
  this.hasAutoConfirmedWorkspaceTrustPrompt = false;
368
400
  ensureClaudeWorkspaceTrustAccepted(this.options.cwd);
@@ -384,16 +416,22 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
384
416
  this.state.activeRuntimeSessionId = undefined;
385
417
  }
386
418
  }
387
- await this.startHookServer();
388
419
  try {
420
+ await this.startHookServer();
389
421
  await super.start();
422
+ if (!this.interactiveReady) {
423
+ this.setStatus("starting", "Waiting for Claude interactive session...");
424
+ }
390
425
  }
391
426
  catch (error) {
427
+ this.rejectInteractiveReadiness(error instanceof Error ? error : new Error(String(error)));
392
428
  await this.stopHookServer();
393
429
  throw error;
394
430
  }
395
431
  }
396
432
  async sendInput(text) {
433
+ if (this.terminalControl.active)
434
+ throw new Error("A Claude control operation is in progress.");
397
435
  if (!this.pty) {
398
436
  throw new Error("claude adapter is not running.");
399
437
  }
@@ -403,7 +441,18 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
403
441
  if (this.pendingApproval) {
404
442
  throw new Error("A Claude approval request is pending. Reply with /confirm or /deny.");
405
443
  }
444
+ await this.waitForInteractiveReadiness();
445
+ if (!this.pty) {
446
+ throw new Error("claude adapter is not running.");
447
+ }
448
+ if (this.getState().status === "busy") {
449
+ throw new Error("claude is still working. Wait for the current reply or use /stop.");
450
+ }
451
+ if (this.pendingApproval) {
452
+ throw new Error("A Claude approval request is pending. Reply with /confirm or /deny.");
453
+ }
406
454
  const normalizedText = normalizeOutput(text).trim();
455
+ this.clearRemoteSubmitRetry();
407
456
  this.pendingInjectedInputs.push({
408
457
  normalizedText,
409
458
  createdAtMs: Date.now(),
@@ -420,12 +469,15 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
420
469
  this.writeToPty(this.buildRemoteInputPayload(text));
421
470
  await delay(CLAUDE_REMOTE_ENTER_DELAY_MS);
422
471
  this.writeToPty("\r");
472
+ this.armRemoteSubmitRetry(normalizedText);
423
473
  this.armWechatWorkingNotice();
424
474
  }
425
475
  async listResumeSessions(limit = 10) {
426
476
  return listClaudeResumeSessions(this.options.cwd, limit);
427
477
  }
428
478
  async resumeSession(sessionId) {
479
+ if (this.terminalControl.active)
480
+ throw new Error("A CLI control operation is in progress. Wait for it to finish before switching sessions.");
429
481
  if (!this.pty) {
430
482
  throw new Error("Claude is not running yet.");
431
483
  }
@@ -522,6 +574,7 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
522
574
  return findClaudeResumeSessionFile(this.options.cwd, sessionId);
523
575
  }
524
576
  async interrupt() {
577
+ this.terminalControl.cancel("The remote control operation was interrupted.");
525
578
  if (!this.pty) {
526
579
  return false;
527
580
  }
@@ -529,6 +582,7 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
529
582
  return false;
530
583
  }
531
584
  this.clearWechatWorkingNotice(true);
585
+ this.clearRemoteSubmitRetry();
532
586
  this.pendingCliApprovalHints = null;
533
587
  this.flushPendingClaudeHookApprovals();
534
588
  if (this.pendingApproval) {
@@ -551,6 +605,8 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
551
605
  return true;
552
606
  }
553
607
  async reset() {
608
+ if (this.terminalControl.active)
609
+ throw new Error("A CLI control operation is in progress. Wait for it to finish before switching sessions.");
554
610
  this.rejectPendingResume(new Error("Claude was reset during session switching."));
555
611
  this.clearWechatWorkingNotice(true);
556
612
  this.pendingCliApprovalHints = null;
@@ -565,7 +621,8 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
565
621
  this.state.lastSessionSwitchAt = undefined;
566
622
  this.state.lastSessionSwitchSource = undefined;
567
623
  this.state.lastSessionSwitchReason = undefined;
568
- await super.reset();
624
+ await this.dispose();
625
+ await this.start();
569
626
  }
570
627
  async resolveApproval(action) {
571
628
  if (!this.pendingApproval) {
@@ -619,9 +676,12 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
619
676
  return ok ? 1 : 0;
620
677
  }
621
678
  async dispose() {
679
+ this.terminalControl.cancel("Claude is shutting down.");
622
680
  this.rejectPendingResume(new Error("Claude is shutting down during session switching."));
623
681
  this.detachLocalTerminal();
624
682
  this.clearWechatWorkingNotice(true);
683
+ this.clearRemoteSubmitRetry();
684
+ this.rejectInteractiveReadiness(new Error("Claude is shutting down."));
625
685
  this.pendingCliApprovalHints = null;
626
686
  this.flushPendingClaudeHookApprovals();
627
687
  this.stopTranscriptThinkingWatch();
@@ -646,6 +706,7 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
646
706
  this.startHookHealthCheck();
647
707
  }
648
708
  handleData(rawText) {
709
+ this.terminalControl.feed(rawText);
649
710
  this.renderLocalOutput(rawText);
650
711
  const text = normalizeOutput(rawText);
651
712
  if (!text) {
@@ -717,10 +778,15 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
717
778
  return true;
718
779
  }
719
780
  handleExit(exitCode) {
781
+ this.terminalControl.cancel("Claude exited during the control operation.");
720
782
  this.detachLocalTerminal();
721
783
  this.clearWechatWorkingNotice(true);
784
+ this.clearRemoteSubmitRetry();
722
785
  this.pendingCliApprovalHints = null;
723
786
  this.rejectPendingResume(new Error("Claude exited before the requested session switch completed."));
787
+ if (!this.recoveringInvalidResume) {
788
+ this.rejectInteractiveReadiness(new Error("Claude exited before its interactive session became ready."));
789
+ }
724
790
  void this.stopHookServer();
725
791
  if (this.recoveringInvalidResume && !this.shuttingDown) {
726
792
  this.clearCompletionTimer();
@@ -869,6 +935,7 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
869
935
  }
870
936
  this.localTerminalInputListener = (chunk) => {
871
937
  const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
938
+ this.terminalControl.localInput(text);
872
939
  this.writeToPty(text);
873
940
  };
874
941
  process.stdin.on("data", this.localTerminalInputListener);
@@ -897,10 +964,11 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
897
964
  }
898
965
  }
899
966
  resizePtyToTerminal() {
900
- if (!this.pty || !process.stdout.isTTY) {
967
+ if (!this.pty) {
901
968
  return;
902
969
  }
903
970
  try {
971
+ this.terminalControl.resize(process.stdout.columns || DEFAULT_COLS, process.stdout.rows || DEFAULT_ROWS);
904
972
  this.pty.resize?.(process.stdout.columns || DEFAULT_COLS, process.stdout.rows || DEFAULT_ROWS);
905
973
  }
906
974
  catch {
@@ -995,6 +1063,91 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
995
1063
  }
996
1064
  return `${CLAUDE_BRACKETED_PASTE_START}${normalizedText}${CLAUDE_BRACKETED_PASTE_END}`;
997
1065
  }
1066
+ beginInteractiveReadinessWait() {
1067
+ this.clearInteractiveReadinessTimer();
1068
+ this.interactiveReady = false;
1069
+ this.interactiveReadyTimer = setTimeout(() => {
1070
+ this.interactiveReadyTimer = null;
1071
+ this.markInteractiveReady();
1072
+ }, CLAUDE_INTERACTIVE_READY_TIMEOUT_MS);
1073
+ this.interactiveReadyTimer.unref?.();
1074
+ }
1075
+ waitForInteractiveReadiness() {
1076
+ if (this.interactiveReady) {
1077
+ return Promise.resolve();
1078
+ }
1079
+ return new Promise((resolve, reject) => {
1080
+ this.interactiveReadyWaiters.add({ resolve, reject });
1081
+ });
1082
+ }
1083
+ markInteractiveReady() {
1084
+ if (this.interactiveReady) {
1085
+ return;
1086
+ }
1087
+ this.interactiveReady = true;
1088
+ this.clearInteractiveReadinessTimer();
1089
+ for (const waiter of this.interactiveReadyWaiters) {
1090
+ waiter.resolve();
1091
+ }
1092
+ this.interactiveReadyWaiters.clear();
1093
+ if (this.state.status === "starting" && !this.hasAcceptedInput) {
1094
+ this.setStatus("idle", "Claude interactive session is ready.");
1095
+ }
1096
+ }
1097
+ rejectInteractiveReadiness(error) {
1098
+ this.clearInteractiveReadinessTimer();
1099
+ for (const waiter of this.interactiveReadyWaiters) {
1100
+ waiter.reject(error);
1101
+ }
1102
+ this.interactiveReadyWaiters.clear();
1103
+ this.interactiveReady = true;
1104
+ }
1105
+ clearInteractiveReadinessTimer() {
1106
+ if (!this.interactiveReadyTimer) {
1107
+ return;
1108
+ }
1109
+ clearTimeout(this.interactiveReadyTimer);
1110
+ this.interactiveReadyTimer = null;
1111
+ }
1112
+ armRemoteSubmitRetry(normalizedText) {
1113
+ this.clearRemoteSubmitRetry();
1114
+ this.remoteSubmitText = normalizedText;
1115
+ this.remoteSubmitRetryIndex = 0;
1116
+ this.scheduleRemoteSubmitRetry();
1117
+ }
1118
+ scheduleRemoteSubmitRetry() {
1119
+ const delayMs = CLAUDE_REMOTE_ENTER_RETRY_DELAYS_MS[this.remoteSubmitRetryIndex];
1120
+ if (delayMs === undefined || !this.remoteSubmitText) {
1121
+ return;
1122
+ }
1123
+ this.remoteSubmitRetryTimer = setTimeout(() => {
1124
+ this.remoteSubmitRetryTimer = null;
1125
+ if (!this.remoteSubmitText ||
1126
+ !this.pty ||
1127
+ this.state.status !== "busy" ||
1128
+ !this.pendingInjectedInputs.some((input) => input.normalizedText === this.remoteSubmitText)) {
1129
+ this.clearRemoteSubmitRetry();
1130
+ return;
1131
+ }
1132
+ this.writeToPty("\r");
1133
+ this.remoteSubmitRetryIndex += 1;
1134
+ if (this.remoteSubmitRetryIndex >= CLAUDE_REMOTE_ENTER_RETRY_DELAYS_MS.length) {
1135
+ this.clearRemoteSubmitRetry();
1136
+ this.emitClaudeNotice("Claude did not confirm the WeChat message submission. Press Enter in the local Claude terminal to retry.", "warning");
1137
+ return;
1138
+ }
1139
+ this.scheduleRemoteSubmitRetry();
1140
+ }, delayMs);
1141
+ this.remoteSubmitRetryTimer.unref?.();
1142
+ }
1143
+ clearRemoteSubmitRetry() {
1144
+ if (this.remoteSubmitRetryTimer) {
1145
+ clearTimeout(this.remoteSubmitRetryTimer);
1146
+ this.remoteSubmitRetryTimer = null;
1147
+ }
1148
+ this.remoteSubmitRetryIndex = 0;
1149
+ this.remoteSubmitText = null;
1150
+ }
998
1151
  // Fallback heuristic for older Claude Code versions that lack PostCompact hooks.
999
1152
  // The structured PostCompact hook event (handled in handleClaudeHookEnvelope) is
1000
1153
  // the reliable signal; this regex match serves as a best-effort fallback.
@@ -1043,6 +1196,7 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
1043
1196
  }
1044
1197
  this.clearCompletionTimer();
1045
1198
  this.clearWechatWorkingNotice(true);
1199
+ this.clearRemoteSubmitRetry();
1046
1200
  this.pendingCliApprovalHints = null;
1047
1201
  this.flushPendingClaudeHookApprovals();
1048
1202
  this.pendingApproval = null;
@@ -1078,6 +1232,7 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
1078
1232
  const completedPreview = this.currentPreview;
1079
1233
  this.clearCompletionTimer();
1080
1234
  this.clearWechatWorkingNotice(true);
1235
+ this.clearRemoteSubmitRetry();
1081
1236
  this.pendingCliApprovalHints = null;
1082
1237
  this.flushPendingClaudeHookApprovals();
1083
1238
  this.pendingApproval = null;
@@ -1167,6 +1322,7 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
1167
1322
  }
1168
1323
  }
1169
1324
  handleClaudeSessionStart(payload) {
1325
+ this.markInteractiveReady();
1170
1326
  if (!payload.session_id) {
1171
1327
  return;
1172
1328
  }
@@ -1289,6 +1445,7 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
1289
1445
  }
1290
1446
  const injectedIndex = findInjectedClaudePromptIndex(prompt, this.pendingInjectedInputs);
1291
1447
  if (injectedIndex >= 0) {
1448
+ this.clearRemoteSubmitRetry();
1292
1449
  this.pendingInjectedInputs.splice(injectedIndex, 1);
1293
1450
  return;
1294
1451
  }
@@ -1331,7 +1488,7 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
1331
1488
  this.state.lastSessionSwitchReason = undefined;
1332
1489
  this.emitClaudeNotice(`Saved Claude conversation ${failedResumeConversationId} is no longer available. Starting a fresh Claude session.`, "warning");
1333
1490
  try {
1334
- await super.reset();
1491
+ await this.reset();
1335
1492
  }
1336
1493
  catch (error) {
1337
1494
  // recoverFromInvalidResume is invoked fire-and-forget (void) and there is
@@ -1440,6 +1597,7 @@ export class ClaudeCompanionAdapter extends AbstractPtyAdapter {
1440
1597
  this.currentPreview === "(idle)") {
1441
1598
  return;
1442
1599
  }
1600
+ this.clearRemoteSubmitRetry();
1443
1601
  this.clearWechatWorkingNotice(true);
1444
1602
  this.pendingCliApprovalHints = null;
1445
1603
  this.lastAutoApprovedPayload = null;