@unlinearity/cli-wechat-bridge 1.0.2 → 1.0.3
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/dist/bridge/bridge-adapters.codex.js +8 -1
- package/dist/bridge/bridge-adapters.core.js +5 -0
- package/dist/bridge/bridge-adapters.opencode.js +176 -22
- package/dist/bridge/bridge-utils.js +24 -1
- package/dist/bridge/wechat-bridge.js +15 -1
- package/dist/companion/local-companion.js +8 -0
- package/dist/runtime/legacy-adapter-runtime.js +6 -0
- package/package.json +1 -1
|
@@ -314,6 +314,13 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
314
314
|
}
|
|
315
315
|
return this.isNativePanelMode() ? Boolean(this.nativeProcess) : Boolean(this.pty);
|
|
316
316
|
}
|
|
317
|
+
shouldPollSessionLog() {
|
|
318
|
+
return (this.isCodexClientRunning() ||
|
|
319
|
+
this.pendingTurnStart ||
|
|
320
|
+
Boolean(this.activeTurn) ||
|
|
321
|
+
Boolean(this.state.activeTurnId) ||
|
|
322
|
+
Boolean(this.sessionFilePath));
|
|
323
|
+
}
|
|
317
324
|
async startNativeClient() {
|
|
318
325
|
this.setStatus("starting", `Starting ${this.options.kind} adapter...`);
|
|
319
326
|
let spawnTarget = null;
|
|
@@ -450,7 +457,7 @@ export class CodexPtyAdapter extends AbstractPtyAdapter {
|
|
|
450
457
|
this.nextSessionFallbackScanAtMs = 0;
|
|
451
458
|
}
|
|
452
459
|
async pollSessionLog() {
|
|
453
|
-
if (!this.
|
|
460
|
+
if (!this.shouldPollSessionLog()) {
|
|
454
461
|
return;
|
|
455
462
|
}
|
|
456
463
|
this.maybeApplyRecentSessionFallback();
|
|
@@ -98,6 +98,11 @@ export class LocalCompanionProxyAdapter {
|
|
|
98
98
|
sessionId,
|
|
99
99
|
});
|
|
100
100
|
}
|
|
101
|
+
async createSession() {
|
|
102
|
+
await this.sendRequest({
|
|
103
|
+
command: "create_session",
|
|
104
|
+
});
|
|
105
|
+
}
|
|
101
106
|
async interrupt() {
|
|
102
107
|
const result = await this.sendRequest({
|
|
103
108
|
command: "interrupt",
|
|
@@ -6,6 +6,7 @@ const OPENCODE_DEBUG_ENABLED = /^(1|true|yes|on)$/i.test(process.env.WECHAT_OPEN
|
|
|
6
6
|
const OPENCODE_DUPLICATE_EVENT_TTL_MS = 150;
|
|
7
7
|
const OPENCODE_WECHAT_MIRROR_SUPPRESSION_TTL_MS = 30_000;
|
|
8
8
|
const OPENCODE_RECENT_LOCAL_PROMPT_TTL_MS = 10_000;
|
|
9
|
+
const OPENCODE_LOCAL_SESSION_CREATE_FOLLOW_TTL_MS = 5_000;
|
|
9
10
|
/* ------------------------------------------------------------------ */
|
|
10
11
|
/* Adapter */
|
|
11
12
|
/* ------------------------------------------------------------------ */
|
|
@@ -37,9 +38,11 @@ export class OpenCodeServerAdapter {
|
|
|
37
38
|
observedUserTextByPartId = new Map();
|
|
38
39
|
observedUserMessagePartIds = new Map();
|
|
39
40
|
pendingWechatPromptMirrorSuppressions = [];
|
|
41
|
+
recentWechatPromptMirrorSuppressions = [];
|
|
40
42
|
recentSdkEventObservations = new Map();
|
|
41
43
|
suppressedTuiSessionSelectId = null;
|
|
42
44
|
lastMirroredLocalPrompt = null;
|
|
45
|
+
pendingLocalSessionCreateFollowUntilMs = 0;
|
|
43
46
|
pendingPermission = null;
|
|
44
47
|
constructor(options) {
|
|
45
48
|
this.options = options;
|
|
@@ -118,6 +121,8 @@ export class OpenCodeServerAdapter {
|
|
|
118
121
|
syncVisible: true,
|
|
119
122
|
forceVisibleSync: true,
|
|
120
123
|
});
|
|
124
|
+
this.recordPendingWechatPromptMirrorSuppression(session.id, normalized);
|
|
125
|
+
this.beginTrackedTurn(normalized, "wechat");
|
|
121
126
|
try {
|
|
122
127
|
const result = await this.client.session.promptAsync({
|
|
123
128
|
sessionID: session.id,
|
|
@@ -130,10 +135,11 @@ export class OpenCodeServerAdapter {
|
|
|
130
135
|
}
|
|
131
136
|
}
|
|
132
137
|
catch (err) {
|
|
138
|
+
this.settleTurnState();
|
|
139
|
+
this.clearObservedMessageTracking();
|
|
140
|
+
this.setStatus("idle");
|
|
133
141
|
throw new Error(`Failed to send prompt: ${describeUnknownError(err)}`);
|
|
134
142
|
}
|
|
135
|
-
this.recordPendingWechatPromptMirrorSuppression(session.id, normalized);
|
|
136
|
-
this.beginTrackedTurn(normalized, "wechat");
|
|
137
143
|
}
|
|
138
144
|
async listResumeSessions(limit = 10) {
|
|
139
145
|
void limit;
|
|
@@ -143,6 +149,31 @@ export class OpenCodeServerAdapter {
|
|
|
143
149
|
void sessionId;
|
|
144
150
|
throw new Error('WeChat /resume is disabled in opencode mode. Use /session directly inside "wechat-opencode"; WeChat will follow the active local session.');
|
|
145
151
|
}
|
|
152
|
+
async createSession() {
|
|
153
|
+
if (!this.client) {
|
|
154
|
+
throw new Error("OpenCode adapter is not running.");
|
|
155
|
+
}
|
|
156
|
+
if (this.state.status === "busy") {
|
|
157
|
+
throw new Error("OpenCode is still working. Wait for the current reply or use /stop.");
|
|
158
|
+
}
|
|
159
|
+
if (this.pendingPermission) {
|
|
160
|
+
throw new Error("An OpenCode approval request is pending. Reply with /confirm <code> or /deny.");
|
|
161
|
+
}
|
|
162
|
+
this.outputBatcher.clear();
|
|
163
|
+
this.clearStreamedPartState();
|
|
164
|
+
const session = this.unwrapOrThrow(await this.client.session.create({
|
|
165
|
+
directory: this.options.cwd,
|
|
166
|
+
workspace: this.activeWorkspaceId ?? undefined,
|
|
167
|
+
}));
|
|
168
|
+
this.switchSharedSession(session, {
|
|
169
|
+
source: "wechat",
|
|
170
|
+
reason: "wechat_resume",
|
|
171
|
+
notify: true,
|
|
172
|
+
clearTrackedTurn: true,
|
|
173
|
+
syncVisible: true,
|
|
174
|
+
forceVisibleSync: true,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
146
177
|
async interrupt() {
|
|
147
178
|
if (!this.client || !this.activeSessionId) {
|
|
148
179
|
return false;
|
|
@@ -167,6 +198,7 @@ export class OpenCodeServerAdapter {
|
|
|
167
198
|
this.clearWechatWorkingNotice(true);
|
|
168
199
|
this.pendingLocalPrompt = "";
|
|
169
200
|
this.localPromptNoticeSent = false;
|
|
201
|
+
this.pendingLocalSessionCreateFollowUntilMs = 0;
|
|
170
202
|
this.clearObservedMessageTracking();
|
|
171
203
|
this.recentSdkEventObservations.clear();
|
|
172
204
|
this.clearPendingPermissionState();
|
|
@@ -223,6 +255,7 @@ export class OpenCodeServerAdapter {
|
|
|
223
255
|
this.clearWechatWorkingNotice(true);
|
|
224
256
|
this.pendingLocalPrompt = "";
|
|
225
257
|
this.localPromptNoticeSent = false;
|
|
258
|
+
this.pendingLocalSessionCreateFollowUntilMs = 0;
|
|
226
259
|
this.clearObservedMessageTracking();
|
|
227
260
|
this.recentSdkEventObservations.clear();
|
|
228
261
|
this.outputBatcher.clear();
|
|
@@ -573,12 +606,15 @@ export class OpenCodeServerAdapter {
|
|
|
573
606
|
if (this.isImplicitLocalCompanionUiEvent(type)) {
|
|
574
607
|
return true;
|
|
575
608
|
}
|
|
576
|
-
return this.matchesCurrentDirectoryEvent(event)
|
|
609
|
+
return (this.matchesCurrentDirectoryEvent(event) ||
|
|
610
|
+
this.shouldAcceptUnscopedLocalSessionCreatedEvent(event));
|
|
577
611
|
}
|
|
578
612
|
if (type === "session.created" ||
|
|
579
613
|
type === "session.updated" ||
|
|
580
614
|
type === "session.deleted") {
|
|
581
|
-
return this.matchesCurrentDirectoryEvent(event)
|
|
615
|
+
return (this.matchesCurrentDirectoryEvent(event) ||
|
|
616
|
+
(type === "session.created" &&
|
|
617
|
+
this.shouldAcceptUnscopedLocalSessionCreatedEvent(event)));
|
|
582
618
|
}
|
|
583
619
|
return false;
|
|
584
620
|
}
|
|
@@ -831,9 +867,21 @@ export class OpenCodeServerAdapter {
|
|
|
831
867
|
return;
|
|
832
868
|
}
|
|
833
869
|
const session = this.extractSessionReference(properties);
|
|
834
|
-
if (
|
|
870
|
+
if (this.syncTrackedSessionFromEvent(session)) {
|
|
835
871
|
return;
|
|
836
872
|
}
|
|
873
|
+
if (!this.shouldFollowCreatedLocalSession(session)) {
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
this.pendingLocalSessionCreateFollowUntilMs = 0;
|
|
877
|
+
this.logDebug(`[opencode-adapter:local-follow] session.created ${session.id}`);
|
|
878
|
+
this.switchSharedSession(session, {
|
|
879
|
+
source: "local",
|
|
880
|
+
reason: "local_follow",
|
|
881
|
+
notify: true,
|
|
882
|
+
clearTrackedTurn: true,
|
|
883
|
+
syncVisible: true,
|
|
884
|
+
});
|
|
837
885
|
}
|
|
838
886
|
handleSessionUpdated(properties) {
|
|
839
887
|
if (!isRecord(properties)) {
|
|
@@ -907,6 +955,14 @@ export class OpenCodeServerAdapter {
|
|
|
907
955
|
: delta
|
|
908
956
|
? this.consumeVisiblePartDelta(partId, delta)
|
|
909
957
|
: "";
|
|
958
|
+
const observedText = typeof part.messageID === "string"
|
|
959
|
+
? this.observedOpenCodeMessages.get(part.messageID)?.text
|
|
960
|
+
: undefined;
|
|
961
|
+
if (this.matchesRecentWechatPromptMirror(part.sessionID, observedText || partText || delta || text, {
|
|
962
|
+
allowPrefix: true,
|
|
963
|
+
})) {
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
910
966
|
this.pushVisibleOutput(text);
|
|
911
967
|
}
|
|
912
968
|
handleMessagePartDelta(properties) {
|
|
@@ -953,6 +1009,14 @@ export class OpenCodeServerAdapter {
|
|
|
953
1009
|
return;
|
|
954
1010
|
}
|
|
955
1011
|
const text = this.consumeVisiblePartDelta(partId, delta);
|
|
1012
|
+
const observedText = typeof properties.messageID === "string"
|
|
1013
|
+
? this.observedOpenCodeMessages.get(properties.messageID)?.text
|
|
1014
|
+
: undefined;
|
|
1015
|
+
if (this.matchesRecentWechatPromptMirror(sessionId, observedText || delta || text, {
|
|
1016
|
+
allowPrefix: true,
|
|
1017
|
+
})) {
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
956
1020
|
this.pushVisibleOutput(text);
|
|
957
1021
|
}
|
|
958
1022
|
handleMessagePartRemoved(properties) {
|
|
@@ -993,6 +1057,9 @@ export class OpenCodeServerAdapter {
|
|
|
993
1057
|
this.handleLocalPromptSubmit();
|
|
994
1058
|
return;
|
|
995
1059
|
default:
|
|
1060
|
+
if (this.isLocalSessionNavigationCommand(command)) {
|
|
1061
|
+
this.markPendingLocalSessionCreateFollow();
|
|
1062
|
+
}
|
|
996
1063
|
return;
|
|
997
1064
|
}
|
|
998
1065
|
}
|
|
@@ -1000,7 +1067,7 @@ export class OpenCodeServerAdapter {
|
|
|
1000
1067
|
if (!isRecord(properties)) {
|
|
1001
1068
|
return;
|
|
1002
1069
|
}
|
|
1003
|
-
const sessionId =
|
|
1070
|
+
const sessionId = this.extractSessionId(properties) ?? undefined;
|
|
1004
1071
|
if (!sessionId) {
|
|
1005
1072
|
return;
|
|
1006
1073
|
}
|
|
@@ -1010,6 +1077,7 @@ export class OpenCodeServerAdapter {
|
|
|
1010
1077
|
}
|
|
1011
1078
|
this.pendingLocalPrompt = "";
|
|
1012
1079
|
this.localPromptNoticeSent = false;
|
|
1080
|
+
this.pendingLocalSessionCreateFollowUntilMs = 0;
|
|
1013
1081
|
this.logDebug(`[opencode-adapter:local-follow] tui.session.select ${sessionId}`);
|
|
1014
1082
|
this.switchSharedSession({
|
|
1015
1083
|
id: sessionId,
|
|
@@ -1043,7 +1111,9 @@ export class OpenCodeServerAdapter {
|
|
|
1043
1111
|
clearTrackedTurn: true,
|
|
1044
1112
|
syncVisible: true,
|
|
1045
1113
|
});
|
|
1114
|
+
return;
|
|
1046
1115
|
}
|
|
1116
|
+
this.markPendingLocalSessionCreateFollow();
|
|
1047
1117
|
}
|
|
1048
1118
|
handleSessionError(properties) {
|
|
1049
1119
|
if (!isRecord(properties)) {
|
|
@@ -1138,7 +1208,8 @@ export class OpenCodeServerAdapter {
|
|
|
1138
1208
|
return;
|
|
1139
1209
|
}
|
|
1140
1210
|
observed.emitted = true;
|
|
1141
|
-
if (this.shouldSuppressWechatMirroredPrompt(observed.sessionId, text)
|
|
1211
|
+
if (this.shouldSuppressWechatMirroredPrompt(observed.sessionId, text) ||
|
|
1212
|
+
this.matchesRecentWechatPromptMirror(observed.sessionId, text)) {
|
|
1142
1213
|
this.cleanupObservedOpenCodeMessage(messageId);
|
|
1143
1214
|
return;
|
|
1144
1215
|
}
|
|
@@ -1159,30 +1230,23 @@ export class OpenCodeServerAdapter {
|
|
|
1159
1230
|
if (!sessionId || !normalizedText) {
|
|
1160
1231
|
return;
|
|
1161
1232
|
}
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
if (this.pendingWechatPromptMirrorSuppressions[index].createdAtMs < cutoff) {
|
|
1165
|
-
this.pendingWechatPromptMirrorSuppressions.splice(index, 1);
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
this.pendingWechatPromptMirrorSuppressions.push({
|
|
1233
|
+
this.pruneWechatPromptMirrorSuppressions();
|
|
1234
|
+
const suppression = {
|
|
1169
1235
|
sessionId,
|
|
1170
1236
|
text: normalizedText,
|
|
1171
1237
|
createdAtMs: Date.now(),
|
|
1172
|
-
}
|
|
1238
|
+
};
|
|
1239
|
+
this.pendingWechatPromptMirrorSuppressions.push(suppression);
|
|
1240
|
+
this.recentWechatPromptMirrorSuppressions.push(suppression);
|
|
1173
1241
|
}
|
|
1174
1242
|
shouldSuppressWechatMirroredPrompt(sessionId, text) {
|
|
1175
1243
|
const normalizedText = normalizeOutput(text).trim();
|
|
1176
1244
|
if (!sessionId || !normalizedText) {
|
|
1177
1245
|
return false;
|
|
1178
1246
|
}
|
|
1179
|
-
|
|
1247
|
+
this.pruneWechatPromptMirrorSuppressions();
|
|
1180
1248
|
for (let index = this.pendingWechatPromptMirrorSuppressions.length - 1; index >= 0; index -= 1) {
|
|
1181
1249
|
const pending = this.pendingWechatPromptMirrorSuppressions[index];
|
|
1182
|
-
if (pending.createdAtMs < cutoff) {
|
|
1183
|
-
this.pendingWechatPromptMirrorSuppressions.splice(index, 1);
|
|
1184
|
-
continue;
|
|
1185
|
-
}
|
|
1186
1250
|
if (pending.sessionId === sessionId && pending.text === normalizedText) {
|
|
1187
1251
|
this.pendingWechatPromptMirrorSuppressions.splice(index, 1);
|
|
1188
1252
|
return true;
|
|
@@ -1190,6 +1254,35 @@ export class OpenCodeServerAdapter {
|
|
|
1190
1254
|
}
|
|
1191
1255
|
return false;
|
|
1192
1256
|
}
|
|
1257
|
+
matchesRecentWechatPromptMirror(sessionId, text, options = {}) {
|
|
1258
|
+
const normalizedText = normalizeOutput(text ?? "").trim();
|
|
1259
|
+
if (!sessionId || !normalizedText) {
|
|
1260
|
+
return false;
|
|
1261
|
+
}
|
|
1262
|
+
this.pruneWechatPromptMirrorSuppressions();
|
|
1263
|
+
return this.recentWechatPromptMirrorSuppressions.some((pending) => {
|
|
1264
|
+
if (pending.sessionId !== sessionId) {
|
|
1265
|
+
return false;
|
|
1266
|
+
}
|
|
1267
|
+
if (pending.text === normalizedText) {
|
|
1268
|
+
return true;
|
|
1269
|
+
}
|
|
1270
|
+
return options.allowPrefix === true && pending.text.startsWith(normalizedText);
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1273
|
+
pruneWechatPromptMirrorSuppressions() {
|
|
1274
|
+
const cutoff = Date.now() - OPENCODE_WECHAT_MIRROR_SUPPRESSION_TTL_MS;
|
|
1275
|
+
for (let index = this.pendingWechatPromptMirrorSuppressions.length - 1; index >= 0; index -= 1) {
|
|
1276
|
+
if (this.pendingWechatPromptMirrorSuppressions[index].createdAtMs < cutoff) {
|
|
1277
|
+
this.pendingWechatPromptMirrorSuppressions.splice(index, 1);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
for (let index = this.recentWechatPromptMirrorSuppressions.length - 1; index >= 0; index -= 1) {
|
|
1281
|
+
if (this.recentWechatPromptMirrorSuppressions[index].createdAtMs < cutoff) {
|
|
1282
|
+
this.recentWechatPromptMirrorSuppressions.splice(index, 1);
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1193
1286
|
wasRecentlyMirroredLocalPrompt(text) {
|
|
1194
1287
|
const normalizedText = normalizeOutput(text).trim();
|
|
1195
1288
|
if (!normalizedText) {
|
|
@@ -1276,6 +1369,7 @@ export class OpenCodeServerAdapter {
|
|
|
1276
1369
|
this.observedUserTextByPartId.clear();
|
|
1277
1370
|
this.observedUserMessagePartIds.clear();
|
|
1278
1371
|
this.pendingWechatPromptMirrorSuppressions.length = 0;
|
|
1372
|
+
this.recentWechatPromptMirrorSuppressions.length = 0;
|
|
1279
1373
|
this.lastMirroredLocalPrompt = null;
|
|
1280
1374
|
}
|
|
1281
1375
|
clearTrackedTurnForLocalSessionSwitch() {
|
|
@@ -1400,12 +1494,22 @@ export class OpenCodeServerAdapter {
|
|
|
1400
1494
|
return false;
|
|
1401
1495
|
}
|
|
1402
1496
|
extractSessionReference(properties) {
|
|
1403
|
-
if (typeof properties.sessionID === "string") {
|
|
1497
|
+
if (typeof properties.sessionID === "string" || typeof properties.sessionId === "string") {
|
|
1498
|
+
const id = typeof properties.sessionID === "string"
|
|
1499
|
+
? properties.sessionID
|
|
1500
|
+
: properties.sessionId;
|
|
1404
1501
|
return {
|
|
1405
|
-
id
|
|
1502
|
+
id,
|
|
1406
1503
|
workspaceID: this.extractWorkspaceId(properties) ?? undefined,
|
|
1407
1504
|
};
|
|
1408
1505
|
}
|
|
1506
|
+
const session = isRecord(properties.session) ? properties.session : undefined;
|
|
1507
|
+
if (typeof session?.id === "string") {
|
|
1508
|
+
return {
|
|
1509
|
+
id: session.id,
|
|
1510
|
+
workspaceID: this.extractWorkspaceId(session) ?? this.extractWorkspaceId(properties) ?? undefined,
|
|
1511
|
+
};
|
|
1512
|
+
}
|
|
1409
1513
|
const info = isRecord(properties.info) ? properties.info : undefined;
|
|
1410
1514
|
if (typeof info?.id === "string") {
|
|
1411
1515
|
return {
|
|
@@ -1422,6 +1526,10 @@ export class OpenCodeServerAdapter {
|
|
|
1422
1526
|
if (typeof properties.workspaceID === "string") {
|
|
1423
1527
|
return properties.workspaceID;
|
|
1424
1528
|
}
|
|
1529
|
+
const session = isRecord(properties.session) ? properties.session : undefined;
|
|
1530
|
+
if (typeof session?.workspaceID === "string") {
|
|
1531
|
+
return session.workspaceID;
|
|
1532
|
+
}
|
|
1425
1533
|
const info = isRecord(properties.info) ? properties.info : undefined;
|
|
1426
1534
|
if (typeof info?.workspaceID === "string") {
|
|
1427
1535
|
return info.workspaceID;
|
|
@@ -1433,6 +1541,46 @@ export class OpenCodeServerAdapter {
|
|
|
1433
1541
|
this.state.activeTurnOrigin === "local" &&
|
|
1434
1542
|
this.hasTrackedTurnState());
|
|
1435
1543
|
}
|
|
1544
|
+
markPendingLocalSessionCreateFollow() {
|
|
1545
|
+
this.pendingLocalSessionCreateFollowUntilMs =
|
|
1546
|
+
Date.now() + OPENCODE_LOCAL_SESSION_CREATE_FOLLOW_TTL_MS;
|
|
1547
|
+
}
|
|
1548
|
+
hasPendingLocalSessionCreateFollow() {
|
|
1549
|
+
return Date.now() <= this.pendingLocalSessionCreateFollowUntilMs;
|
|
1550
|
+
}
|
|
1551
|
+
shouldAcceptUnscopedLocalSessionCreatedEvent(event) {
|
|
1552
|
+
if (this.normalizeEventType(event.type) !== "session.created") {
|
|
1553
|
+
return false;
|
|
1554
|
+
}
|
|
1555
|
+
if (this.options.renderMode !== "companion") {
|
|
1556
|
+
return false;
|
|
1557
|
+
}
|
|
1558
|
+
const payload = this.extractEventPayload(event);
|
|
1559
|
+
if (!isRecord(payload)) {
|
|
1560
|
+
return false;
|
|
1561
|
+
}
|
|
1562
|
+
const session = this.extractSessionReference(payload);
|
|
1563
|
+
if (!session?.id || session.id === this.activeSessionId) {
|
|
1564
|
+
return false;
|
|
1565
|
+
}
|
|
1566
|
+
if (typeof event.directory === "string" ||
|
|
1567
|
+
this.extractSessionDirectory(payload) ||
|
|
1568
|
+
this.extractWorkspaceId(payload)) {
|
|
1569
|
+
return false;
|
|
1570
|
+
}
|
|
1571
|
+
return this.hasPendingLocalSessionCreateFollow() || Boolean(this.activeSessionId);
|
|
1572
|
+
}
|
|
1573
|
+
shouldFollowCreatedLocalSession(session) {
|
|
1574
|
+
if (!session?.id || session.id === this.activeSessionId) {
|
|
1575
|
+
return false;
|
|
1576
|
+
}
|
|
1577
|
+
if (this.hasPendingLocalSessionCreateFollow()) {
|
|
1578
|
+
this.pendingLocalSessionCreateFollowUntilMs = 0;
|
|
1579
|
+
return true;
|
|
1580
|
+
}
|
|
1581
|
+
this.pendingLocalSessionCreateFollowUntilMs = 0;
|
|
1582
|
+
return Boolean(this.activeSessionId);
|
|
1583
|
+
}
|
|
1436
1584
|
switchSharedSession(session, options) {
|
|
1437
1585
|
const sessionId = typeof session === "string" ? session : session.id;
|
|
1438
1586
|
if (!sessionId) {
|
|
@@ -1490,7 +1638,9 @@ export class OpenCodeServerAdapter {
|
|
|
1490
1638
|
}
|
|
1491
1639
|
const normalized = command.trim().toLowerCase();
|
|
1492
1640
|
return (normalized === "session" ||
|
|
1641
|
+
normalized === "new" ||
|
|
1493
1642
|
normalized === "/session" ||
|
|
1643
|
+
normalized === "/new" ||
|
|
1494
1644
|
normalized.startsWith("session.") ||
|
|
1495
1645
|
normalized.startsWith("/session "));
|
|
1496
1646
|
}
|
|
@@ -1538,6 +1688,10 @@ export class OpenCodeServerAdapter {
|
|
|
1538
1688
|
if (typeof properties.directory === "string") {
|
|
1539
1689
|
return properties.directory;
|
|
1540
1690
|
}
|
|
1691
|
+
const session = isRecord(properties.session) ? properties.session : undefined;
|
|
1692
|
+
if (typeof session?.directory === "string") {
|
|
1693
|
+
return session.directory;
|
|
1694
|
+
}
|
|
1541
1695
|
const info = isRecord(properties.info) ? properties.info : undefined;
|
|
1542
1696
|
if (typeof info?.directory === "string") {
|
|
1543
1697
|
return info.directory;
|
|
@@ -139,6 +139,9 @@ export function parseSystemCommand(text) {
|
|
|
139
139
|
return { type: "status" };
|
|
140
140
|
case "/resume":
|
|
141
141
|
return argument ? { type: "resume", target: argument } : { type: "resume" };
|
|
142
|
+
case "/new":
|
|
143
|
+
case "/new-session":
|
|
144
|
+
return { type: "new_session" };
|
|
142
145
|
case "/stop":
|
|
143
146
|
return { type: "stop" };
|
|
144
147
|
case "/reset":
|
|
@@ -694,6 +697,7 @@ const OPENCODE_REASONING_LINE_RES = [
|
|
|
694
697
|
/\bI need to (?:respond|reply|answer|tell the user)\b/i,
|
|
695
698
|
/\bWe need to (?:respond|reply|answer)\b/i,
|
|
696
699
|
/\bI should\b/i,
|
|
700
|
+
/\bI(?:'ll| will) (?:respond|reply|answer|tell the user|provide)\b/i,
|
|
697
701
|
/\bI'll provide\b/i,
|
|
698
702
|
/^Let me (?:directly )?(?:answer|respond)\b/i,
|
|
699
703
|
/根据系统提示/i,
|
|
@@ -704,8 +708,27 @@ const OPENCODE_REASONING_LINE_RES = [
|
|
|
704
708
|
/^我要直接(?:回答|回复)/,
|
|
705
709
|
/^用户(?:说|问)了/,
|
|
706
710
|
];
|
|
711
|
+
const OPENCODE_INLINE_REASONING_MARKER_RE = /\b(?:The user\b|I need to\b|I should\b|I(?:'ll| will)\b|We need to\b|Let me\b)/i;
|
|
712
|
+
const OPENCODE_INLINE_REASONING_SENTENCE_RE = /^(?:The user\b|I need to\b|I should\b|I(?:'ll| will)\b|We need to\b|Let me\b)[^.!?\n]*(?:[.!?]+)\s*/i;
|
|
713
|
+
function stripInlineOpenCodeReasoningPrefix(text) {
|
|
714
|
+
let current = text.trim();
|
|
715
|
+
const markerIndex = current.search(OPENCODE_INLINE_REASONING_MARKER_RE);
|
|
716
|
+
if (markerIndex > 0 && markerIndex <= 80) {
|
|
717
|
+
current = current.slice(markerIndex).trimStart();
|
|
718
|
+
}
|
|
719
|
+
for (let index = 0; index < 6; index += 1) {
|
|
720
|
+
const match = current.match(OPENCODE_INLINE_REASONING_SENTENCE_RE);
|
|
721
|
+
if (!match) {
|
|
722
|
+
break;
|
|
723
|
+
}
|
|
724
|
+
current = current.slice(match[0].length).trimStart();
|
|
725
|
+
}
|
|
726
|
+
return current;
|
|
727
|
+
}
|
|
707
728
|
export function sanitizeWechatFinalReplyText(adapter, text) {
|
|
708
|
-
const normalized =
|
|
729
|
+
const normalized = adapter === "opencode"
|
|
730
|
+
? cleanupVisibleWechatReplyText(stripInlineOpenCodeReasoningPrefix(text))
|
|
731
|
+
: cleanupVisibleWechatReplyText(text);
|
|
709
732
|
if (!normalized || adapter !== "opencode") {
|
|
710
733
|
return normalized;
|
|
711
734
|
}
|
|
@@ -641,12 +641,13 @@ function wireAdapterEvents(params) {
|
|
|
641
641
|
}
|
|
642
642
|
break;
|
|
643
643
|
case "final_reply":
|
|
644
|
+
stateStore.appendLog(`final_reply: ${truncatePreview(event.text)}`);
|
|
644
645
|
void outputBatcher.flushNow().then(async () => {
|
|
645
646
|
await forwardWechatFinalReply({
|
|
646
647
|
adapter: options.adapter,
|
|
647
648
|
rawText: event.text,
|
|
648
649
|
sender: {
|
|
649
|
-
sendText: (text) => queueWechatMessage(authorizedUserId, text),
|
|
650
|
+
sendText: (text) => queueWechatMessage(authorizedUserId, text, "final_reply"),
|
|
650
651
|
sendImage: (imagePath) => queueWechatAttachmentAction(() => transport.sendImage(imagePath, { recipientId: authorizedUserId })),
|
|
651
652
|
sendFile: (filePath) => queueWechatAttachmentAction(() => transport.sendFile(filePath, { recipientId: authorizedUserId })),
|
|
652
653
|
sendVoice: (voicePath) => queueWechatAttachmentAction(() => transport.sendVoice(voicePath, authorizedUserId)),
|
|
@@ -798,6 +799,19 @@ async function handleInboundMessage(params) {
|
|
|
798
799
|
await queueWechatMessage(message.senderId, `/resume is not available in ${options.adapter} mode.`);
|
|
799
800
|
return null;
|
|
800
801
|
}
|
|
802
|
+
case "new_session": {
|
|
803
|
+
if (!adapter.createSession) {
|
|
804
|
+
await queueWechatMessage(message.senderId, `/new is not available in ${options.adapter} mode.`);
|
|
805
|
+
return null;
|
|
806
|
+
}
|
|
807
|
+
await outputBatcher.flushNow();
|
|
808
|
+
outputBatcher.clear();
|
|
809
|
+
stateStore.clearPendingConfirmation();
|
|
810
|
+
stateStore.clearSharedSessionId();
|
|
811
|
+
await adapter.createSession();
|
|
812
|
+
stateStore.appendLog(`New ${options.adapter} session requested by owner.`);
|
|
813
|
+
return null;
|
|
814
|
+
}
|
|
801
815
|
case "stop": {
|
|
802
816
|
const interrupted = await adapter.interrupt();
|
|
803
817
|
await queueWechatMessage(message.senderId, interrupted
|
|
@@ -199,6 +199,14 @@ export async function runLocalCompanion(options) {
|
|
|
199
199
|
publishState();
|
|
200
200
|
sendResponse(socket, message.id, true);
|
|
201
201
|
break;
|
|
202
|
+
case "create_session":
|
|
203
|
+
if (!adapter.createSession) {
|
|
204
|
+
throw new Error(`/${adapter.getState().kind} does not support creating sessions from WeChat.`);
|
|
205
|
+
}
|
|
206
|
+
await adapter.createSession();
|
|
207
|
+
publishState();
|
|
208
|
+
sendResponse(socket, message.id, true);
|
|
209
|
+
break;
|
|
202
210
|
case "interrupt":
|
|
203
211
|
sendResponse(socket, message.id, true, await adapter.interrupt());
|
|
204
212
|
break;
|
|
@@ -19,6 +19,12 @@ export class LegacyAdapterRuntime {
|
|
|
19
19
|
async resumeSession(sessionId) {
|
|
20
20
|
await this.adapter.resumeSession(sessionId);
|
|
21
21
|
}
|
|
22
|
+
async createSession() {
|
|
23
|
+
if (!this.adapter.createSession) {
|
|
24
|
+
throw new Error(`/${this.adapter.getState().kind} does not support creating sessions from WeChat.`);
|
|
25
|
+
}
|
|
26
|
+
await this.adapter.createSession();
|
|
27
|
+
}
|
|
22
28
|
async interrupt() {
|
|
23
29
|
return await this.adapter.interrupt();
|
|
24
30
|
}
|