@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.
- package/LICENSE.txt +661 -661
- package/README.md +434 -427
- package/bin/_run-entry.mjs +47 -9
- package/bin/wechat-check-update.mjs +6 -1
- package/bin/wechat-claude.mjs +6 -1
- package/bin/wechat-codex.mjs +6 -1
- package/bin/wechat-daemon.mjs +6 -1
- package/bin/wechat-opencode.mjs +6 -1
- package/bin/wechat-pi.mjs +6 -1
- package/bin/wechat-setup.mjs +6 -1
- package/bin/wecom-check-update.mjs +10 -0
- package/bin/wecom-claude.mjs +10 -0
- package/bin/wecom-codex.mjs +10 -0
- package/bin/wecom-daemon.mjs +10 -0
- package/bin/wecom-opencode.mjs +10 -0
- package/bin/wecom-pi.mjs +10 -0
- package/bin/wecom-setup.mjs +10 -0
- package/dist/bridge/adapter-control.js +58 -0
- package/dist/bridge/bridge-adapters.claude.js +163 -5
- package/dist/bridge/bridge-adapters.codex.js +331 -29
- package/dist/bridge/bridge-adapters.core.js +27 -2
- package/dist/bridge/bridge-adapters.opencode.js +156 -1
- package/dist/bridge/bridge-adapters.pi.js +46 -1
- package/dist/bridge/bridge-adapters.shared.js +8 -1
- package/dist/bridge/bridge-controller.js +6 -0
- package/dist/bridge/bridge-final-reply.js +8 -65
- package/dist/bridge/bridge-process-reaper.js +41 -2
- package/dist/bridge/bridge-state.js +17 -4
- package/dist/bridge/bridge-utils.js +10 -127
- package/dist/bridge/claude-hook.js +6 -0
- package/dist/bridge/claude-native-controls.js +133 -0
- package/dist/bridge/native-terminal-control.js +259 -0
- package/dist/bridge/wechat-bridge.js +520 -274
- package/dist/bridge/wechat-forwarding.js +8 -58
- package/dist/bridge/wechat-outbound-queue.js +8 -0
- package/dist/channels/wechat/bridge-final-reply.js +56 -0
- package/dist/channels/wechat/channel-message.js +24 -0
- package/dist/channels/wechat/index.js +11 -0
- package/dist/channels/wechat/wechat-channel-port.js +67 -0
- package/dist/channels/wechat/wechat-driver.js +191 -0
- package/dist/channels/wechat/wechat-forwarding.js +97 -0
- package/dist/channels/wechat/wechat-outbound-queue.js +118 -0
- package/dist/channels/wecom/bridge-final-reply.js +32 -0
- package/dist/channels/wecom/setup-cli.js +12 -0
- package/dist/channels/wecom/setup.js +182 -0
- package/dist/channels/wecom/wecom-channel-port.js +45 -0
- package/dist/channels/wecom/wecom-config.js +72 -0
- package/dist/channels/wecom/wecom-driver.js +106 -0
- package/dist/channels/wecom/wecom-media.js +147 -0
- package/dist/channels/wecom/wecom-message.js +149 -0
- package/dist/channels/wecom/wecom-transport.js +314 -0
- package/dist/commands/check-update.js +6 -0
- package/dist/companion/codex-remote-client.js +30 -3
- package/dist/companion/codex-visible-client-link.js +107 -0
- package/dist/companion/local-companion-link.js +6 -0
- package/dist/companion/local-companion-start.js +62 -11
- package/dist/companion/local-companion.js +25 -1
- package/dist/companion/opencode-tui-bridge-plugin.js +6 -0
- package/dist/companion/pi-tui-bridge-extension.js +62 -3
- package/dist/core/bridge-control.js +42 -0
- package/dist/core/bridge-defer.js +25 -0
- package/dist/core/bridge-event-forwarder.js +50 -0
- package/dist/core/bridge-message-router.js +32 -0
- package/dist/core/channel-driver.js +1 -0
- package/dist/core/channel-types.js +3 -0
- package/dist/core/conversation-routing.js +58 -0
- package/dist/core/direct-run.js +23 -0
- package/dist/core/inbound-message-claims.js +67 -0
- package/dist/core/outbound-target.js +32 -0
- package/dist/core/text-utils.js +69 -0
- package/dist/core/turn-coordinator.js +85 -0
- package/dist/daemon/daemon-link.js +9 -0
- package/dist/daemon/emoji-bindings.js +6 -0
- package/dist/daemon/wechat-daemon.js +827 -209
- package/dist/i18n/index.js +6 -0
- package/dist/i18n/messages-en.js +11 -3
- package/dist/i18n/messages-zh.js +11 -3
- package/dist/runtime/create-runtime-host.js +6 -0
- package/dist/runtime/legacy-adapter-runtime.js +15 -0
- package/dist/runtime/runtime-types.js +3 -0
- package/dist/utils/atomic-file.js +6 -0
- package/dist/utils/doctor.js +36 -10
- package/dist/utils/version-checker.js +17 -11
- package/dist/wechat/channel-config.js +6 -0
- package/dist/wechat/setup.js +119 -13
- package/dist/wechat/wechat-channel.js +6 -0
- package/dist/wechat/wechat-transport.js +169 -58
- package/package.json +14 -4
- 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,
|
|
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
|
|
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
|
|
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
|
|
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;
|