@ccpocket/bridge 1.68.0 → 1.68.2
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/codex-process.d.ts +4 -0
- package/dist/codex-process.js +111 -89
- package/dist/codex-process.js.map +1 -1
- package/dist/sdk-process.d.ts +1 -0
- package/dist/sdk-process.js +4 -1
- package/dist/sdk-process.js.map +1 -1
- package/dist/session.d.ts +14 -2
- package/dist/session.js +20 -14
- package/dist/session.js.map +1 -1
- package/dist/websocket.d.ts +2 -0
- package/dist/websocket.js +161 -74
- package/dist/websocket.js.map +1 -1
- package/package.json +1 -1
package/dist/websocket.js
CHANGED
|
@@ -5,7 +5,7 @@ import { lstat, readFile, readlink, stat, unlink } from "node:fs/promises";
|
|
|
5
5
|
import { resolve, extname } from "node:path";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
7
|
import { WebSocketServer, WebSocket } from "ws";
|
|
8
|
-
import { SessionManager, } from "./session.js";
|
|
8
|
+
import { SessionManager, MAX_HISTORY_PER_SESSION, } from "./session.js";
|
|
9
9
|
import { SdkProcess, listAvailableClaudeModels, } from "./sdk-process.js";
|
|
10
10
|
import { CodexProcess, } from "./codex-process.js";
|
|
11
11
|
import { stopManagedCodexAppServers } from "./codex-transport.js";
|
|
@@ -979,89 +979,110 @@ export class BridgeWebSocketServer {
|
|
|
979
979
|
message,
|
|
980
980
|
}));
|
|
981
981
|
this.applyCodexCanonicalHistoryBaseline(session, history, entries);
|
|
982
|
-
return
|
|
982
|
+
return entries;
|
|
983
983
|
}
|
|
984
984
|
applyCodexCanonicalHistoryBaseline(session, history, canonicalEntries) {
|
|
985
|
-
const
|
|
985
|
+
const orderedRevision = session.codexOrderedHistoryRevision ?? 0;
|
|
986
|
+
const liveEntries = [
|
|
987
|
+
...(session.codexOrderedHistoryEntries ?? []),
|
|
988
|
+
...session.historyEntries.filter((entry) => entry.seq > orderedRevision),
|
|
989
|
+
].map((entry) => ({
|
|
986
990
|
seq: entry.seq,
|
|
987
991
|
message: entry.message,
|
|
988
992
|
}));
|
|
989
|
-
const
|
|
993
|
+
const latestUserInput = session.codexLatestUserInput;
|
|
994
|
+
const latestUserKeys = latestUserInput
|
|
995
|
+
? this.codexHistoryMessageIdentityKeys(latestUserInput)
|
|
996
|
+
: [];
|
|
997
|
+
if (latestUserInput &&
|
|
998
|
+
!liveEntries.some((entry) => entry.message === latestUserInput ||
|
|
999
|
+
(entry.message.type === "user_input" &&
|
|
1000
|
+
this.codexHistoryMessageIdentityKeys(entry.message).some((key) => latestUserKeys.includes(key))))) {
|
|
1001
|
+
const historySeq = latestUserInput
|
|
1002
|
+
.historySeq;
|
|
1003
|
+
liveEntries.unshift({
|
|
1004
|
+
seq: typeof historySeq === "number" ? historySeq : 0,
|
|
1005
|
+
message: latestUserInput,
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
990
1008
|
const canonicalUserUuids = new Set();
|
|
991
1009
|
for (const entry of canonicalEntries) {
|
|
992
|
-
for (const key of this.codexHistoryMessageIdentityKeys(entry.message)) {
|
|
993
|
-
canonicalKeys.add(key);
|
|
994
|
-
}
|
|
995
1010
|
const message = entry.message;
|
|
996
1011
|
if (message.type === "user_input" && message.userMessageUuid) {
|
|
997
1012
|
canonicalUserUuids.add(message.userMessageUuid);
|
|
998
1013
|
}
|
|
999
1014
|
}
|
|
1000
1015
|
this.seedCodexCanonicalUserTurnUuidMap(session, history);
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
let
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1016
|
+
const merged = [];
|
|
1017
|
+
let canonicalCursor = 0;
|
|
1018
|
+
let hasCanonicalAnchor = false;
|
|
1019
|
+
let canMatchAssistantContent = false;
|
|
1020
|
+
const appendCanonicalUntil = (endExclusive) => {
|
|
1021
|
+
while (canonicalCursor < endExclusive) {
|
|
1022
|
+
const message = canonicalEntries[canonicalCursor].message;
|
|
1023
|
+
merged.push({ message, retained: false });
|
|
1024
|
+
canonicalCursor += 1;
|
|
1025
|
+
hasCanonicalAnchor = true;
|
|
1026
|
+
if (message.type === "user_input") {
|
|
1027
|
+
canMatchAssistantContent = true;
|
|
1028
|
+
}
|
|
1010
1029
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
const
|
|
1014
|
-
const
|
|
1015
|
-
if (
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
const retainedMessages = [];
|
|
1022
|
-
const retainedEntries = [];
|
|
1023
|
-
for (const entry of liveEntries) {
|
|
1024
|
-
if (!this.shouldRetainCodexLiveHistoryMessage(entry.message))
|
|
1025
|
-
continue;
|
|
1026
|
-
const keys = this.codexHistoryMessageIdentityKeys(entry.message);
|
|
1027
|
-
const matchesCanonical = keys.some((key) => canonicalKeys.has(key));
|
|
1028
|
-
if (entry.message.type === "user_input") {
|
|
1029
|
-
const userKey = entry.message.userMessageUuid
|
|
1030
|
-
? `user:${entry.message.userMessageUuid}`
|
|
1031
|
-
: null;
|
|
1032
|
-
pendingCanonicalAssistantContents =
|
|
1033
|
-
matchesCanonical && userKey
|
|
1034
|
-
? [...(canonicalAssistantContentsByUser.get(userKey) ?? [])]
|
|
1035
|
-
: [];
|
|
1036
|
-
}
|
|
1037
|
-
if (entry.message.type === "assistant") {
|
|
1038
|
-
const contentKey = this.historyValueKey(entry.message.message.content);
|
|
1039
|
-
const contentIndex = pendingCanonicalAssistantContents.indexOf(contentKey);
|
|
1040
|
-
if (matchesCanonical) {
|
|
1041
|
-
if (contentIndex !== -1) {
|
|
1042
|
-
pendingCanonicalAssistantContents.splice(contentIndex, 1);
|
|
1043
|
-
}
|
|
1044
|
-
continue;
|
|
1030
|
+
};
|
|
1031
|
+
for (let liveIndex = 0; liveIndex < liveEntries.length; liveIndex++) {
|
|
1032
|
+
const liveEntry = liveEntries[liveIndex];
|
|
1033
|
+
const matchIndex = this.findCodexCanonicalMatchIndex(canonicalEntries, liveEntry.message, canonicalCursor, canMatchAssistantContent);
|
|
1034
|
+
if (matchIndex === -1) {
|
|
1035
|
+
let nextMatchIndex = -1;
|
|
1036
|
+
for (let nextLiveIndex = liveIndex + 1; nextLiveIndex < liveEntries.length; nextLiveIndex++) {
|
|
1037
|
+
nextMatchIndex = this.findCodexCanonicalMatchIndex(canonicalEntries, liveEntries[nextLiveIndex].message, canonicalCursor, canMatchAssistantContent);
|
|
1038
|
+
if (nextMatchIndex !== -1)
|
|
1039
|
+
break;
|
|
1045
1040
|
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1041
|
+
if (nextMatchIndex !== -1) {
|
|
1042
|
+
appendCanonicalUntil(nextMatchIndex);
|
|
1043
|
+
}
|
|
1044
|
+
else if (!hasCanonicalAnchor) {
|
|
1045
|
+
appendCanonicalUntil(canonicalEntries.length);
|
|
1046
|
+
}
|
|
1047
|
+
if (this.shouldRetainCodexLiveHistoryMessage(liveEntry.message)) {
|
|
1048
|
+
merged.push({ message: liveEntry.message, retained: true });
|
|
1049
1049
|
}
|
|
1050
|
-
if (matchesCanonicalTurnAssistant)
|
|
1051
|
-
continue;
|
|
1052
|
-
}
|
|
1053
|
-
if (matchesCanonical)
|
|
1054
1050
|
continue;
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
retainedMessages.push(entry.message);
|
|
1058
|
-
retainedEntries.push({ seq, message: entry.message });
|
|
1051
|
+
}
|
|
1052
|
+
appendCanonicalUntil(matchIndex + 1);
|
|
1059
1053
|
}
|
|
1054
|
+
appendCanonicalUntil(canonicalEntries.length);
|
|
1055
|
+
const retainedMessages = [];
|
|
1056
|
+
const retainedEntries = [];
|
|
1057
|
+
let canonicalRevision = 0;
|
|
1058
|
+
const previousRevision = session.historyRevision;
|
|
1059
|
+
const requiresNewBaselineRevision = previousRevision > 0;
|
|
1060
|
+
const targetRevision = requiresNewBaselineRevision
|
|
1061
|
+
? Math.max(merged.length, previousRevision + 1)
|
|
1062
|
+
: merged.length;
|
|
1063
|
+
const sequenceOffset = targetRevision - merged.length;
|
|
1064
|
+
const mergedEntries = merged.map(({ message, retained }, index) => {
|
|
1065
|
+
const seq = sequenceOffset + index + 1;
|
|
1066
|
+
if (retained) {
|
|
1067
|
+
message.historySeq = seq;
|
|
1068
|
+
retainedMessages.push(message);
|
|
1069
|
+
retainedEntries.push({ seq, message });
|
|
1070
|
+
}
|
|
1071
|
+
else {
|
|
1072
|
+
canonicalRevision = seq;
|
|
1073
|
+
}
|
|
1074
|
+
return { seq, message };
|
|
1075
|
+
});
|
|
1076
|
+
canonicalEntries.splice(0, canonicalEntries.length, ...mergedEntries);
|
|
1077
|
+
session.codexOrderedHistoryEntries =
|
|
1078
|
+
this.buildCodexOrderedHistoryWindow(mergedEntries);
|
|
1079
|
+
session.codexOrderedHistoryRevision = targetRevision;
|
|
1060
1080
|
session.pastMessages = history;
|
|
1061
1081
|
session.history = retainedMessages;
|
|
1062
1082
|
session.historyEntries = retainedEntries;
|
|
1063
|
-
session.historyRevision =
|
|
1064
|
-
session.codexCanonicalHistoryRevision =
|
|
1083
|
+
session.historyRevision = targetRevision;
|
|
1084
|
+
session.codexCanonicalHistoryRevision = canonicalRevision;
|
|
1085
|
+
session.codexHistoryResetRevision = targetRevision;
|
|
1065
1086
|
session.historyLowWatermark =
|
|
1066
1087
|
retainedEntries[0]?.seq ?? session.historyRevision + 1;
|
|
1067
1088
|
if (session.pendingCodexUserEchoUuids) {
|
|
@@ -1075,6 +1096,52 @@ export class BridgeWebSocketServer {
|
|
|
1075
1096
|
}
|
|
1076
1097
|
}
|
|
1077
1098
|
}
|
|
1099
|
+
buildCodexOrderedHistoryWindow(entries) {
|
|
1100
|
+
if (entries.length <= MAX_HISTORY_PER_SESSION) {
|
|
1101
|
+
return entries.map((entry) => ({
|
|
1102
|
+
seq: entry.seq,
|
|
1103
|
+
message: entry.message,
|
|
1104
|
+
}));
|
|
1105
|
+
}
|
|
1106
|
+
const tail = entries.slice(-MAX_HISTORY_PER_SESSION);
|
|
1107
|
+
if (tail.some((entry) => entry.message.type === "user_input")) {
|
|
1108
|
+
return tail;
|
|
1109
|
+
}
|
|
1110
|
+
let latestUser;
|
|
1111
|
+
for (let index = entries.length - 1; index >= 0; index--) {
|
|
1112
|
+
if (entries[index].message.type === "user_input") {
|
|
1113
|
+
latestUser = entries[index];
|
|
1114
|
+
break;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
return latestUser
|
|
1118
|
+
? [latestUser, ...entries.slice(-(MAX_HISTORY_PER_SESSION - 1))]
|
|
1119
|
+
: tail;
|
|
1120
|
+
}
|
|
1121
|
+
findCodexCanonicalMatchIndex(canonicalEntries, liveMessage, start, allowAssistantContentMatch) {
|
|
1122
|
+
const liveKeys = this.codexHistoryMessageIdentityKeys(liveMessage);
|
|
1123
|
+
if (liveKeys.length > 0) {
|
|
1124
|
+
for (let index = start; index < canonicalEntries.length; index++) {
|
|
1125
|
+
const canonicalKeys = this.codexHistoryMessageIdentityKeys(canonicalEntries[index].message);
|
|
1126
|
+
if (liveKeys.some((key) => canonicalKeys.includes(key)))
|
|
1127
|
+
return index;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
if (!allowAssistantContentMatch || liveMessage.type !== "assistant") {
|
|
1131
|
+
return -1;
|
|
1132
|
+
}
|
|
1133
|
+
const liveContent = this.historyValueKey(liveMessage.message.content);
|
|
1134
|
+
for (let index = start; index < canonicalEntries.length; index++) {
|
|
1135
|
+
const canonicalMessage = canonicalEntries[index].message;
|
|
1136
|
+
if (canonicalMessage.type === "user_input")
|
|
1137
|
+
break;
|
|
1138
|
+
if (canonicalMessage.type === "assistant" &&
|
|
1139
|
+
this.historyValueKey(canonicalMessage.message.content) === liveContent) {
|
|
1140
|
+
return index;
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
return -1;
|
|
1144
|
+
}
|
|
1078
1145
|
seedCodexCanonicalUserTurnUuidMap(session, history) {
|
|
1079
1146
|
if (session.provider !== "codex")
|
|
1080
1147
|
return;
|
|
@@ -1151,8 +1218,8 @@ export class BridgeWebSocketServer {
|
|
|
1151
1218
|
this.send(ws, {
|
|
1152
1219
|
type: "history_snapshot",
|
|
1153
1220
|
sessionId,
|
|
1154
|
-
fromSeq: entries[0]?.seq ?? 1,
|
|
1155
|
-
toSeq:
|
|
1221
|
+
fromSeq: entries[0]?.seq ?? session.historyRevision + 1,
|
|
1222
|
+
toSeq: session.historyRevision,
|
|
1156
1223
|
messages: entries,
|
|
1157
1224
|
status: session.status,
|
|
1158
1225
|
reason: "reset",
|
|
@@ -1205,9 +1272,11 @@ export class BridgeWebSocketServer {
|
|
|
1205
1272
|
shouldResetCodexHistoryDelta(session, sinceSeq, resultKind) {
|
|
1206
1273
|
if (!this.codexThreadIdForSession(session))
|
|
1207
1274
|
return false;
|
|
1208
|
-
|
|
1275
|
+
const resetRevision = session.codexHistoryResetRevision ??
|
|
1276
|
+
session.codexCanonicalHistoryRevision;
|
|
1277
|
+
if (typeof resetRevision !== "number")
|
|
1209
1278
|
return true;
|
|
1210
|
-
if (sinceSeq <
|
|
1279
|
+
if (sinceSeq < resetRevision)
|
|
1211
1280
|
return true;
|
|
1212
1281
|
return resultKind === "snapshot";
|
|
1213
1282
|
}
|
|
@@ -1620,9 +1689,6 @@ export class BridgeWebSocketServer {
|
|
|
1620
1689
|
this.send(ws, this.buildPathNotAllowedError(additionalWritableRoots.deniedRoot));
|
|
1621
1690
|
break;
|
|
1622
1691
|
}
|
|
1623
|
-
const cached = provider === "claude"
|
|
1624
|
-
? this.sessionManager.getCachedCommands(projectPath)
|
|
1625
|
-
: undefined;
|
|
1626
1692
|
const { sessionId, permissionMode: effectivePermissionMode, executionMode: effectiveExecutionMode, planMode: effectivePlanMode, usedFallback: autoFallbackUsed, } = provider === "claude"
|
|
1627
1693
|
? this.createClaudeSessionWithFallback({
|
|
1628
1694
|
projectPath,
|
|
@@ -1687,6 +1753,7 @@ export class BridgeWebSocketServer {
|
|
|
1687
1753
|
usedFallback: false,
|
|
1688
1754
|
};
|
|
1689
1755
|
const createdSession = this.sessionManager.get(sessionId);
|
|
1756
|
+
const cached = this.sessionManager.getCachedCommands(provider, createdSession?.worktreePath ?? projectPath);
|
|
1690
1757
|
// Load saved session name from CLI storage (for resumed sessions)
|
|
1691
1758
|
void this.loadAndSetSessionName(createdSession, provider, projectPath, msg.sessionId).then(() => {
|
|
1692
1759
|
this.send(ws, this.buildSessionCreatedMessage({
|
|
@@ -3380,6 +3447,7 @@ export class BridgeWebSocketServer {
|
|
|
3380
3447
|
: "default",
|
|
3381
3448
|
});
|
|
3382
3449
|
const createdSession = this.sessionManager.get(sessionId);
|
|
3450
|
+
const cached = this.sessionManager.getCachedCommands("codex", createdSession?.worktreePath ?? effectiveProjectPath);
|
|
3383
3451
|
await this.loadAndSetSessionName(createdSession, "codex", effectiveProjectPath, sessionRefId);
|
|
3384
3452
|
this.send(ws, this.buildSessionCreatedMessage({
|
|
3385
3453
|
sessionId,
|
|
@@ -3394,6 +3462,23 @@ export class BridgeWebSocketServer {
|
|
|
3394
3462
|
permissionMode: legacyPermissionMode,
|
|
3395
3463
|
executionMode,
|
|
3396
3464
|
planMode,
|
|
3465
|
+
...(cached
|
|
3466
|
+
? {
|
|
3467
|
+
slashCommands: cached.slashCommands,
|
|
3468
|
+
skills: cached.skills,
|
|
3469
|
+
...(cached.skillMetadata
|
|
3470
|
+
? { skillMetadata: cached.skillMetadata }
|
|
3471
|
+
: {}),
|
|
3472
|
+
apps: cached.apps,
|
|
3473
|
+
...(cached.appMetadata
|
|
3474
|
+
? { appMetadata: cached.appMetadata }
|
|
3475
|
+
: {}),
|
|
3476
|
+
plugins: cached.plugins,
|
|
3477
|
+
...(cached.pluginMetadata
|
|
3478
|
+
? { pluginMetadata: cached.pluginMetadata }
|
|
3479
|
+
: {}),
|
|
3480
|
+
}
|
|
3481
|
+
: {}),
|
|
3397
3482
|
}));
|
|
3398
3483
|
this.broadcastSessionList();
|
|
3399
3484
|
this.debugEvents.set(sessionId, []);
|
|
@@ -3414,7 +3499,6 @@ export class BridgeWebSocketServer {
|
|
|
3414
3499
|
break;
|
|
3415
3500
|
}
|
|
3416
3501
|
const claudeSessionId = sessionRefId;
|
|
3417
|
-
const cached = this.sessionManager.getCachedCommands(resumeProjectPath);
|
|
3418
3502
|
let pendingResumes = this.pendingClaudeResumeInputs.get(ws);
|
|
3419
3503
|
if (!pendingResumes) {
|
|
3420
3504
|
pendingResumes = new Map();
|
|
@@ -3469,6 +3553,7 @@ export class BridgeWebSocketServer {
|
|
|
3469
3553
|
worktreeOptions: worktreeOpts,
|
|
3470
3554
|
});
|
|
3471
3555
|
const createdSession = this.sessionManager.get(sessionId);
|
|
3556
|
+
const cached = this.sessionManager.getCachedCommands("claude", createdSession?.worktreePath ?? resumeProjectPath);
|
|
3472
3557
|
const finishResume = () => {
|
|
3473
3558
|
this.send(ws, {
|
|
3474
3559
|
...this.buildSessionCreatedMessage({
|
|
@@ -5657,9 +5742,11 @@ export class BridgeWebSocketServer {
|
|
|
5657
5742
|
return (this.clientSupportedServerMessages.get(ws)?.has(type) ?? false);
|
|
5658
5743
|
}
|
|
5659
5744
|
hasInputConflictSince(session, baseSeq) {
|
|
5745
|
+
const codexResetRevision = session.codexHistoryResetRevision ??
|
|
5746
|
+
session.codexCanonicalHistoryRevision;
|
|
5660
5747
|
if (session.provider === "codex" &&
|
|
5661
|
-
typeof
|
|
5662
|
-
baseSeq <
|
|
5748
|
+
typeof codexResetRevision === "number" &&
|
|
5749
|
+
baseSeq < codexResetRevision) {
|
|
5663
5750
|
return true;
|
|
5664
5751
|
}
|
|
5665
5752
|
const delta = this.sessionManager.getHistorySince(session.id, baseSeq);
|
|
@@ -5714,7 +5801,7 @@ export class BridgeWebSocketServer {
|
|
|
5714
5801
|
sendCachedCommands(ws, sessionId, session) {
|
|
5715
5802
|
// Restore command metadata when the original init/supported_commands event
|
|
5716
5803
|
// has fallen out of the bounded in-memory history.
|
|
5717
|
-
const cached = this.sessionManager.getCachedCommands(session.projectPath);
|
|
5804
|
+
const cached = this.sessionManager.getCachedCommands(session.provider, session.worktreePath ?? session.projectPath);
|
|
5718
5805
|
if (!cached ||
|
|
5719
5806
|
(cached.slashCommands.length === 0 &&
|
|
5720
5807
|
cached.skills.length === 0 &&
|