@ccpocket/bridge 1.68.2 → 1.69.1
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 +25 -0
- package/dist/codex-process.js +191 -27
- package/dist/codex-process.js.map +1 -1
- package/dist/parser.d.ts +5 -0
- package/dist/parser.js.map +1 -1
- package/dist/sessions-index.js +4 -0
- package/dist/sessions-index.js.map +1 -1
- package/dist/websocket.d.ts +5 -0
- package/dist/websocket.js +178 -68
- package/dist/websocket.js.map +1 -1
- package/package.json +1 -1
package/dist/websocket.d.ts
CHANGED
|
@@ -52,6 +52,8 @@ export declare class BridgeWebSocketServer {
|
|
|
52
52
|
private archiveStore;
|
|
53
53
|
private codexProfiles;
|
|
54
54
|
private defaultCodexProfile;
|
|
55
|
+
private codexAutoReviewDisabled;
|
|
56
|
+
private codexAutoReviewPolicyLoaded;
|
|
55
57
|
private codexMetadataRequest;
|
|
56
58
|
private lastConnectMetadataRefreshAt;
|
|
57
59
|
private claudeModels;
|
|
@@ -161,12 +163,14 @@ export declare class BridgeWebSocketServer {
|
|
|
161
163
|
private applyClaudeModels;
|
|
162
164
|
private applyFallbackClaudeModels;
|
|
163
165
|
private readCodexModels;
|
|
166
|
+
private readCodexAutoReviewDisabled;
|
|
164
167
|
private applyCodexModels;
|
|
165
168
|
private applyFallbackCodexModels;
|
|
166
169
|
private loadCodexProfiles;
|
|
167
170
|
private validateCodexProfile;
|
|
168
171
|
private resolveCodexResumeProfile;
|
|
169
172
|
private getActiveCodexProcess;
|
|
173
|
+
private withCodexAutoReviewPolicy;
|
|
170
174
|
private getActiveClaudeProcess;
|
|
171
175
|
private listRecentCodexThreads;
|
|
172
176
|
private createStandaloneCodexProcess;
|
|
@@ -180,6 +184,7 @@ export declare class BridgeWebSocketServer {
|
|
|
180
184
|
private sessionLabel;
|
|
181
185
|
private maybeSendPushNotification;
|
|
182
186
|
private broadcast;
|
|
187
|
+
private prepareServerMessageForClient;
|
|
183
188
|
private shouldSendToClient;
|
|
184
189
|
private hasInputConflictSince;
|
|
185
190
|
private sendCodexQueueState;
|
package/dist/websocket.js
CHANGED
|
@@ -7,7 +7,7 @@ import { promisify } from "node:util";
|
|
|
7
7
|
import { WebSocketServer, WebSocket } from "ws";
|
|
8
8
|
import { SessionManager, MAX_HISTORY_PER_SESSION, } from "./session.js";
|
|
9
9
|
import { SdkProcess, listAvailableClaudeModels, } from "./sdk-process.js";
|
|
10
|
-
import { CodexProcess, } from "./codex-process.js";
|
|
10
|
+
import { codexErrorMessage, CodexRpcError, CodexProcess, } from "./codex-process.js";
|
|
11
11
|
import { stopManagedCodexAppServers } from "./codex-transport.js";
|
|
12
12
|
import { parseClientMessage, } from "./parser.js";
|
|
13
13
|
import { getAllRecentSessions, getCodexSessionHistory, getSessionHistory, codexUserTurnUuid, codexThreadToSessionHistory, findSessionsByClaudeIds, extractMessageImages, getClaudeSessionName, getCodexSessionIndexMetadata, loadCodexSessionNames, renameClaudeSession, renameCodexSession, saveCodexSessionProfile, } from "./sessions-index.js";
|
|
@@ -90,8 +90,12 @@ const CODEX_USER_TURN_UUID_RE = /^codex:user-turn:(\d+)$/;
|
|
|
90
90
|
const OPT_IN_SERVER_MESSAGES = new Set([
|
|
91
91
|
"conversation_queue",
|
|
92
92
|
"goal_state",
|
|
93
|
+
"guardian_approval",
|
|
93
94
|
"prompt_history_status",
|
|
94
95
|
]);
|
|
96
|
+
function isRecord(value) {
|
|
97
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
98
|
+
}
|
|
95
99
|
function parseCodexUserTurnOrdinal(uuid) {
|
|
96
100
|
if (!uuid)
|
|
97
101
|
return null;
|
|
@@ -239,6 +243,9 @@ function normalizeCodexApprovalPolicy(value) {
|
|
|
239
243
|
return "on-request";
|
|
240
244
|
}
|
|
241
245
|
}
|
|
246
|
+
function isCodexAutoReviewApprovalsReviewer(value) {
|
|
247
|
+
return value === "auto_review" || value === "guardian_subagent";
|
|
248
|
+
}
|
|
242
249
|
function sanitizeCodexModel(model) {
|
|
243
250
|
if (typeof model !== "string")
|
|
244
251
|
return undefined;
|
|
@@ -453,6 +460,8 @@ export class BridgeWebSocketServer {
|
|
|
453
460
|
archiveStore;
|
|
454
461
|
codexProfiles = [];
|
|
455
462
|
defaultCodexProfile;
|
|
463
|
+
codexAutoReviewDisabled = false;
|
|
464
|
+
codexAutoReviewPolicyLoaded = false;
|
|
456
465
|
codexMetadataRequest = null;
|
|
457
466
|
lastConnectMetadataRefreshAt = null;
|
|
458
467
|
claudeModels = FALLBACK_CLAUDE_MODELS;
|
|
@@ -805,10 +814,10 @@ export class BridgeWebSocketServer {
|
|
|
805
814
|
fallback: buildCodexHistoryPrefix(session, targetOrdinal - 1),
|
|
806
815
|
});
|
|
807
816
|
this.destroySession(sessionId);
|
|
808
|
-
const newSessionId = this.sessionManager.create(projectPath, undefined, pastMessages, worktreeOpts, "codex", {
|
|
817
|
+
const newSessionId = this.sessionManager.create(projectPath, undefined, pastMessages, worktreeOpts, "codex", this.withCodexAutoReviewPolicy({
|
|
809
818
|
...(codexSettings ?? {}),
|
|
810
819
|
threadId,
|
|
811
|
-
});
|
|
820
|
+
}));
|
|
812
821
|
const newSession = this.sessionManager.get(newSessionId);
|
|
813
822
|
this.send(ws, {
|
|
814
823
|
type: "rewind_result",
|
|
@@ -820,8 +829,8 @@ export class BridgeWebSocketServer {
|
|
|
820
829
|
provider: "codex",
|
|
821
830
|
projectPath,
|
|
822
831
|
session: newSession,
|
|
823
|
-
approvalsReviewer: codexSettings?.approvalsReviewer,
|
|
824
|
-
sandboxMode: codexSettings?.sandboxMode,
|
|
832
|
+
approvalsReviewer: newSession?.codexSettings?.approvalsReviewer,
|
|
833
|
+
sandboxMode: newSession?.codexSettings?.sandboxMode,
|
|
825
834
|
sourceSessionId: sessionId,
|
|
826
835
|
}));
|
|
827
836
|
this.sendSessionList(ws);
|
|
@@ -892,18 +901,18 @@ export class BridgeWebSocketServer {
|
|
|
892
901
|
expectedUserTurns: targetOrdinal,
|
|
893
902
|
fallback: buildCodexHistoryPrefix(session, targetOrdinal),
|
|
894
903
|
});
|
|
895
|
-
const newSessionId = this.sessionManager.create(projectPath, undefined, pastMessages, worktreeOpts, "codex", {
|
|
904
|
+
const newSessionId = this.sessionManager.create(projectPath, undefined, pastMessages, worktreeOpts, "codex", this.withCodexAutoReviewPolicy({
|
|
896
905
|
...(codexSettings ?? {}),
|
|
897
906
|
threadId: forkedThreadId,
|
|
898
|
-
});
|
|
907
|
+
}));
|
|
899
908
|
const newSession = this.sessionManager.get(newSessionId);
|
|
900
909
|
this.send(ws, this.buildSessionCreatedMessage({
|
|
901
910
|
sessionId: newSessionId,
|
|
902
911
|
provider: "codex",
|
|
903
912
|
projectPath,
|
|
904
913
|
session: newSession,
|
|
905
|
-
approvalsReviewer: codexSettings?.approvalsReviewer,
|
|
906
|
-
sandboxMode: codexSettings?.sandboxMode,
|
|
914
|
+
approvalsReviewer: newSession?.codexSettings?.approvalsReviewer,
|
|
915
|
+
sandboxMode: newSession?.codexSettings?.sandboxMode,
|
|
907
916
|
sourceSessionId: sessionId,
|
|
908
917
|
}));
|
|
909
918
|
this.sendSessionList(ws);
|
|
@@ -1641,9 +1650,13 @@ export class BridgeWebSocketServer {
|
|
|
1641
1650
|
}
|
|
1642
1651
|
try {
|
|
1643
1652
|
const provider = msg.provider ?? "claude";
|
|
1644
|
-
const
|
|
1653
|
+
const normalizedCodexPermissionsMode = provider === "codex"
|
|
1645
1654
|
? normalizeCodexPermissionsMode(msg.codexPermissionsMode)
|
|
1646
1655
|
: undefined;
|
|
1656
|
+
const requestedCodexPermissionsMode = this.codexAutoReviewDisabled &&
|
|
1657
|
+
normalizedCodexPermissionsMode === "autoReview"
|
|
1658
|
+
? "default"
|
|
1659
|
+
: normalizedCodexPermissionsMode;
|
|
1647
1660
|
const codexPermissionSettings = requestedCodexPermissionsMode
|
|
1648
1661
|
? codexSettingsFromPermissionsMode(requestedCodexPermissionsMode)
|
|
1649
1662
|
: undefined;
|
|
@@ -1719,7 +1732,7 @@ export class BridgeWebSocketServer {
|
|
|
1719
1732
|
useWorktree: msg.useWorktree,
|
|
1720
1733
|
worktreeBranch: msg.worktreeBranch,
|
|
1721
1734
|
existingWorktreePath: msg.existingWorktreePath,
|
|
1722
|
-
}, provider, {
|
|
1735
|
+
}, provider, this.withCodexAutoReviewPolicy({
|
|
1723
1736
|
profile: msg.profile,
|
|
1724
1737
|
approvalPolicy: codexPermissionSettings
|
|
1725
1738
|
? codexPermissionSettings.approvalPolicy
|
|
@@ -1727,9 +1740,8 @@ export class BridgeWebSocketServer {
|
|
|
1727
1740
|
normalizeCodexApprovalPolicy(executionMode === "fullAccess"
|
|
1728
1741
|
? "never"
|
|
1729
1742
|
: "on-request")),
|
|
1730
|
-
approvalsReviewer: codexPermissionSettings
|
|
1731
|
-
|
|
1732
|
-
: msg.approvalsReviewer,
|
|
1743
|
+
approvalsReviewer: codexPermissionSettings?.approvalsReviewer ??
|
|
1744
|
+
msg.approvalsReviewer,
|
|
1733
1745
|
codexPermissionsMode: codexPermissionSettings?.codexPermissionsMode,
|
|
1734
1746
|
sandboxMode: codexPermissionSettings
|
|
1735
1747
|
? codexPermissionSettings.sandboxMode
|
|
@@ -1746,7 +1758,7 @@ export class BridgeWebSocketServer {
|
|
|
1746
1758
|
collaborationMode: planMode
|
|
1747
1759
|
? "plan"
|
|
1748
1760
|
: "default",
|
|
1749
|
-
}),
|
|
1761
|
+
})),
|
|
1750
1762
|
permissionMode: claudePermissionMode,
|
|
1751
1763
|
executionMode,
|
|
1752
1764
|
planMode,
|
|
@@ -2286,7 +2298,15 @@ export class BridgeWebSocketServer {
|
|
|
2286
2298
|
// Permission mode for Codex requires a session restart (like sandbox mode).
|
|
2287
2299
|
// approvalPolicy and collaborationMode are thread-level settings that
|
|
2288
2300
|
// only take effect reliably at thread/start or thread/resume time.
|
|
2289
|
-
const
|
|
2301
|
+
const normalizedCodexPermissionsMode = normalizeCodexPermissionsMode(msg.codexPermissionsMode);
|
|
2302
|
+
const requestedCodexPermissionsMode = this.codexAutoReviewDisabled &&
|
|
2303
|
+
normalizedCodexPermissionsMode === "autoReview"
|
|
2304
|
+
? "default"
|
|
2305
|
+
: normalizedCodexPermissionsMode;
|
|
2306
|
+
const requestedApprovalsReviewer = this.codexAutoReviewDisabled &&
|
|
2307
|
+
isCodexAutoReviewApprovalsReviewer(msg.approvalsReviewer)
|
|
2308
|
+
? "user"
|
|
2309
|
+
: msg.approvalsReviewer;
|
|
2290
2310
|
const codexPermissionSettings = requestedCodexPermissionsMode
|
|
2291
2311
|
? codexSettingsFromPermissionsMode(requestedCodexPermissionsMode)
|
|
2292
2312
|
: undefined;
|
|
@@ -2324,23 +2344,28 @@ export class BridgeWebSocketServer {
|
|
|
2324
2344
|
const newSandboxMode = codexPermissionSettings
|
|
2325
2345
|
? codexPermissionSettings.sandboxMode
|
|
2326
2346
|
: currentSandboxMode;
|
|
2327
|
-
const
|
|
2347
|
+
const configuredReviewer = requestedCodexPermissionsMode === "custom"
|
|
2348
|
+
? undefined
|
|
2349
|
+
: (codexPermissionSettings?.approvalsReviewer ??
|
|
2350
|
+
requestedApprovalsReviewer ??
|
|
2351
|
+
currentReviewer);
|
|
2352
|
+
const newReviewer = this.codexAutoReviewDisabled
|
|
2353
|
+
? "user"
|
|
2354
|
+
: configuredReviewer;
|
|
2355
|
+
const derivedPermissionsMode = codexPermissionSettings?.codexPermissionsMode ??
|
|
2328
2356
|
(collaborationOnlyChange ? currentPermissionsMode : undefined) ??
|
|
2329
2357
|
deriveCodexPermissionsMode({
|
|
2330
2358
|
approvalPolicy: newApproval,
|
|
2331
|
-
approvalsReviewer:
|
|
2332
|
-
msg.approvalsReviewer ??
|
|
2333
|
-
currentReviewer,
|
|
2359
|
+
approvalsReviewer: newReviewer,
|
|
2334
2360
|
sandboxMode: newSandboxMode,
|
|
2335
2361
|
});
|
|
2362
|
+
const newPermissionsMode = this.codexAutoReviewDisabled &&
|
|
2363
|
+
derivedPermissionsMode === "autoReview"
|
|
2364
|
+
? "default"
|
|
2365
|
+
: derivedPermissionsMode;
|
|
2336
2366
|
const newCollaboration = planMode
|
|
2337
2367
|
? "plan"
|
|
2338
2368
|
: "default";
|
|
2339
|
-
const newReviewer = requestedCodexPermissionsMode === "custom"
|
|
2340
|
-
? undefined
|
|
2341
|
-
: (codexPermissionSettings?.approvalsReviewer ??
|
|
2342
|
-
msg.approvalsReviewer ??
|
|
2343
|
-
currentReviewer);
|
|
2344
2369
|
const currentCollaboration = process.collaborationMode;
|
|
2345
2370
|
if (newApproval === currentApproval &&
|
|
2346
2371
|
newReviewer === currentReviewer &&
|
|
@@ -2351,7 +2376,9 @@ export class BridgeWebSocketServer {
|
|
|
2351
2376
|
}
|
|
2352
2377
|
const canApplyModeInPlace = session.status === "idle" &&
|
|
2353
2378
|
requestedCodexPermissionsMode !== "custom" &&
|
|
2354
|
-
newSandboxMode === currentSandboxMode
|
|
2379
|
+
newSandboxMode === currentSandboxMode &&
|
|
2380
|
+
(this.codexAutoReviewPolicyLoaded ||
|
|
2381
|
+
!isCodexAutoReviewApprovalsReviewer(newReviewer));
|
|
2355
2382
|
if (canApplyModeInPlace) {
|
|
2356
2383
|
const process = session.process;
|
|
2357
2384
|
if (newApproval && newApproval !== currentApproval) {
|
|
@@ -2407,7 +2434,7 @@ export class BridgeWebSocketServer {
|
|
|
2407
2434
|
if (!threadId || !hasUserMessages) {
|
|
2408
2435
|
const newId = this.sessionManager.create(projectPath, undefined, undefined, worktreePath
|
|
2409
2436
|
? { existingWorktreePath: worktreePath, worktreeBranch }
|
|
2410
|
-
: undefined, "codex", {
|
|
2437
|
+
: undefined, "codex", this.withCodexAutoReviewPolicy({
|
|
2411
2438
|
approvalPolicy: newApproval,
|
|
2412
2439
|
approvalsReviewer: newReviewer,
|
|
2413
2440
|
codexPermissionsMode: newPermissionsMode,
|
|
@@ -2418,7 +2445,7 @@ export class BridgeWebSocketServer {
|
|
|
2418
2445
|
networkAccessEnabled: oldSettings.networkAccessEnabled,
|
|
2419
2446
|
webSearchMode: oldSettings.webSearchMode,
|
|
2420
2447
|
collaborationMode: newCollaboration,
|
|
2421
|
-
});
|
|
2448
|
+
}));
|
|
2422
2449
|
const newSession = this.sessionManager.get(newId);
|
|
2423
2450
|
if (newSession && sessionName)
|
|
2424
2451
|
newSession.name = sessionName;
|
|
@@ -2467,7 +2494,7 @@ export class BridgeWebSocketServer {
|
|
|
2467
2494
|
}
|
|
2468
2495
|
this.getCodexThreadHistory(threadId, effectiveProjectPath)
|
|
2469
2496
|
.then((pastMessages) => {
|
|
2470
|
-
const newId = this.sessionManager.create(effectiveProjectPath, undefined, pastMessages, worktreeOpts, "codex", {
|
|
2497
|
+
const newId = this.sessionManager.create(effectiveProjectPath, undefined, pastMessages, worktreeOpts, "codex", this.withCodexAutoReviewPolicy({
|
|
2471
2498
|
threadId,
|
|
2472
2499
|
approvalPolicy: newApproval,
|
|
2473
2500
|
approvalsReviewer: newReviewer,
|
|
@@ -2479,7 +2506,7 @@ export class BridgeWebSocketServer {
|
|
|
2479
2506
|
networkAccessEnabled: oldSettings.networkAccessEnabled,
|
|
2480
2507
|
webSearchMode: oldSettings.webSearchMode,
|
|
2481
2508
|
collaborationMode: newCollaboration,
|
|
2482
|
-
});
|
|
2509
|
+
}));
|
|
2483
2510
|
const newSession = this.sessionManager.get(newId);
|
|
2484
2511
|
if (newSession && sessionName) {
|
|
2485
2512
|
newSession.name = sessionName;
|
|
@@ -2818,8 +2845,10 @@ export class BridgeWebSocketServer {
|
|
|
2818
2845
|
// "no rollout found for thread id".)
|
|
2819
2846
|
const newId = this.sessionManager.create(projectPath, undefined, undefined, worktreePath
|
|
2820
2847
|
? { existingWorktreePath: worktreePath, worktreeBranch }
|
|
2821
|
-
: undefined, "codex", {
|
|
2848
|
+
: undefined, "codex", this.withCodexAutoReviewPolicy({
|
|
2822
2849
|
approvalPolicy: oldSettings.approvalPolicy,
|
|
2850
|
+
approvalsReviewer: oldSettings.approvalsReviewer,
|
|
2851
|
+
codexPermissionsMode: oldSettings.codexPermissionsMode,
|
|
2823
2852
|
sandboxMode: newSandboxMode,
|
|
2824
2853
|
model: oldSettings.model,
|
|
2825
2854
|
modelReasoningEffort: oldSettings.modelReasoningEffort,
|
|
@@ -2827,7 +2856,7 @@ export class BridgeWebSocketServer {
|
|
|
2827
2856
|
networkAccessEnabled: oldSettings.networkAccessEnabled,
|
|
2828
2857
|
webSearchMode: oldSettings.webSearchMode,
|
|
2829
2858
|
collaborationMode,
|
|
2830
|
-
});
|
|
2859
|
+
}));
|
|
2831
2860
|
const newSession = this.sessionManager.get(newId);
|
|
2832
2861
|
if (newSession && sessionName)
|
|
2833
2862
|
newSession.name = sessionName;
|
|
@@ -2869,9 +2898,11 @@ export class BridgeWebSocketServer {
|
|
|
2869
2898
|
}
|
|
2870
2899
|
this.getCodexThreadHistory(threadId, effectiveProjectPath)
|
|
2871
2900
|
.then((pastMessages) => {
|
|
2872
|
-
const newId = this.sessionManager.create(effectiveProjectPath, undefined, pastMessages, worktreeOpts, "codex", {
|
|
2901
|
+
const newId = this.sessionManager.create(effectiveProjectPath, undefined, pastMessages, worktreeOpts, "codex", this.withCodexAutoReviewPolicy({
|
|
2873
2902
|
threadId,
|
|
2874
2903
|
approvalPolicy: oldSettings.approvalPolicy,
|
|
2904
|
+
approvalsReviewer: oldSettings.approvalsReviewer,
|
|
2905
|
+
codexPermissionsMode: oldSettings.codexPermissionsMode,
|
|
2875
2906
|
sandboxMode: newSandboxMode,
|
|
2876
2907
|
model: oldSettings.model,
|
|
2877
2908
|
modelReasoningEffort: oldSettings.modelReasoningEffort,
|
|
@@ -2879,7 +2910,7 @@ export class BridgeWebSocketServer {
|
|
|
2879
2910
|
networkAccessEnabled: oldSettings.networkAccessEnabled,
|
|
2880
2911
|
webSearchMode: oldSettings.webSearchMode,
|
|
2881
2912
|
collaborationMode,
|
|
2882
|
-
});
|
|
2913
|
+
}));
|
|
2883
2914
|
// Restore session name
|
|
2884
2915
|
const newSession = this.sessionManager.get(newId);
|
|
2885
2916
|
if (newSession && sessionName) {
|
|
@@ -3316,25 +3347,39 @@ export class BridgeWebSocketServer {
|
|
|
3316
3347
|
}
|
|
3317
3348
|
case "archive_session": {
|
|
3318
3349
|
const { sessionId, provider, projectPath } = msg;
|
|
3319
|
-
this.
|
|
3320
|
-
|
|
3321
|
-
.
|
|
3322
|
-
|
|
3323
|
-
|
|
3350
|
+
const archiveProjectPath = resolvePlatformPath(projectPath, this.platform);
|
|
3351
|
+
if (!this.isPathAllowed(archiveProjectPath)) {
|
|
3352
|
+
const pathError = this.buildPathNotAllowedError(projectPath);
|
|
3353
|
+
this.send(ws, {
|
|
3354
|
+
type: "archive_result",
|
|
3355
|
+
sessionId,
|
|
3356
|
+
success: false,
|
|
3357
|
+
error: pathError.message,
|
|
3358
|
+
});
|
|
3359
|
+
break;
|
|
3360
|
+
}
|
|
3361
|
+
void (async () => {
|
|
3324
3362
|
if (provider === "codex") {
|
|
3325
|
-
const
|
|
3326
|
-
const
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
});
|
|
3363
|
+
const activeProcess = this.getActiveCodexProcess();
|
|
3364
|
+
const codexProcess = activeProcess ??
|
|
3365
|
+
(await this.createStandaloneCodexProcess(archiveProjectPath));
|
|
3366
|
+
try {
|
|
3367
|
+
await codexProcess.archiveThread(sessionId);
|
|
3368
|
+
}
|
|
3369
|
+
catch (err) {
|
|
3370
|
+
if (!(err instanceof CodexRpcError && err.code === -32601)) {
|
|
3371
|
+
throw err;
|
|
3335
3372
|
}
|
|
3373
|
+
console.warn("[ws] thread/archive unsupported; using local archive marker");
|
|
3374
|
+
}
|
|
3375
|
+
finally {
|
|
3376
|
+
if (!activeProcess)
|
|
3377
|
+
codexProcess.stop();
|
|
3336
3378
|
}
|
|
3337
3379
|
}
|
|
3380
|
+
await this.archiveStore.archive(sessionId, provider, archiveProjectPath);
|
|
3381
|
+
})()
|
|
3382
|
+
.then(() => {
|
|
3338
3383
|
this.send(ws, {
|
|
3339
3384
|
type: "archive_result",
|
|
3340
3385
|
sessionId,
|
|
@@ -3346,7 +3391,7 @@ export class BridgeWebSocketServer {
|
|
|
3346
3391
|
type: "archive_result",
|
|
3347
3392
|
sessionId,
|
|
3348
3393
|
success: false,
|
|
3349
|
-
error:
|
|
3394
|
+
error: codexErrorMessage(err),
|
|
3350
3395
|
});
|
|
3351
3396
|
});
|
|
3352
3397
|
break;
|
|
@@ -3359,9 +3404,13 @@ export class BridgeWebSocketServer {
|
|
|
3359
3404
|
break;
|
|
3360
3405
|
}
|
|
3361
3406
|
const provider = msg.provider ?? "claude";
|
|
3362
|
-
const
|
|
3407
|
+
const normalizedCodexPermissionsMode = provider === "codex"
|
|
3363
3408
|
? normalizeCodexPermissionsMode(msg.codexPermissionsMode)
|
|
3364
3409
|
: undefined;
|
|
3410
|
+
const requestedCodexPermissionsMode = this.codexAutoReviewDisabled &&
|
|
3411
|
+
normalizedCodexPermissionsMode === "autoReview"
|
|
3412
|
+
? "default"
|
|
3413
|
+
: normalizedCodexPermissionsMode;
|
|
3365
3414
|
const codexPermissionSettings = requestedCodexPermissionsMode
|
|
3366
3415
|
? codexSettingsFromPermissionsMode(requestedCodexPermissionsMode)
|
|
3367
3416
|
: undefined;
|
|
@@ -3420,16 +3469,15 @@ export class BridgeWebSocketServer {
|
|
|
3420
3469
|
}
|
|
3421
3470
|
try {
|
|
3422
3471
|
const pastMessages = await this.getCodexThreadHistory(sessionRefId, effectiveProjectPath);
|
|
3423
|
-
const sessionId = this.sessionManager.create(effectiveProjectPath, undefined, pastMessages, worktreeOpts, "codex", {
|
|
3472
|
+
const sessionId = this.sessionManager.create(effectiveProjectPath, undefined, pastMessages, worktreeOpts, "codex", this.withCodexAutoReviewPolicy({
|
|
3424
3473
|
threadId: sessionRefId,
|
|
3425
3474
|
profile: effectiveProfile,
|
|
3426
3475
|
approvalPolicy: codexPermissionSettings
|
|
3427
3476
|
? codexPermissionSettings.approvalPolicy
|
|
3428
3477
|
: (codexApprovalPolicy ??
|
|
3429
3478
|
normalizeCodexApprovalPolicy(executionMode === "fullAccess" ? "never" : "on-request")),
|
|
3430
|
-
approvalsReviewer: codexPermissionSettings
|
|
3431
|
-
|
|
3432
|
-
: msg.approvalsReviewer,
|
|
3479
|
+
approvalsReviewer: codexPermissionSettings?.approvalsReviewer ??
|
|
3480
|
+
msg.approvalsReviewer,
|
|
3433
3481
|
codexPermissionsMode: codexPermissionSettings?.codexPermissionsMode,
|
|
3434
3482
|
sandboxMode: codexPermissionSettings
|
|
3435
3483
|
? codexPermissionSettings.sandboxMode
|
|
@@ -3445,7 +3493,7 @@ export class BridgeWebSocketServer {
|
|
|
3445
3493
|
collaborationMode: planMode
|
|
3446
3494
|
? "plan"
|
|
3447
3495
|
: "default",
|
|
3448
|
-
});
|
|
3496
|
+
}));
|
|
3449
3497
|
const createdSession = this.sessionManager.get(sessionId);
|
|
3450
3498
|
const cached = this.sessionManager.getCachedCommands("codex", createdSession?.worktreePath ?? effectiveProjectPath);
|
|
3451
3499
|
await this.loadAndSetSessionName(createdSession, "codex", effectiveProjectPath, sessionRefId);
|
|
@@ -5029,6 +5077,7 @@ export class BridgeWebSocketServer {
|
|
|
5029
5077
|
codexModelServiceTiers: this.codexModelServiceTiers,
|
|
5030
5078
|
codexProfiles: this.codexProfiles,
|
|
5031
5079
|
defaultCodexProfile: this.defaultCodexProfile,
|
|
5080
|
+
codexAutoReviewDisabled: this.codexAutoReviewDisabled,
|
|
5032
5081
|
bridgeVersion: getPackageVersion(),
|
|
5033
5082
|
});
|
|
5034
5083
|
}
|
|
@@ -5060,6 +5109,7 @@ export class BridgeWebSocketServer {
|
|
|
5060
5109
|
codexModelServiceTiers: this.codexModelServiceTiers,
|
|
5061
5110
|
codexProfiles: this.codexProfiles,
|
|
5062
5111
|
defaultCodexProfile: this.defaultCodexProfile,
|
|
5112
|
+
codexAutoReviewDisabled: this.codexAutoReviewDisabled,
|
|
5063
5113
|
bridgeVersion: getPackageVersion(),
|
|
5064
5114
|
});
|
|
5065
5115
|
}
|
|
@@ -5216,14 +5266,18 @@ export class BridgeWebSocketServer {
|
|
|
5216
5266
|
}
|
|
5217
5267
|
}
|
|
5218
5268
|
broadcastSessionMessageNow(sessionId, msg, exclude) {
|
|
5219
|
-
const data = JSON.stringify({ ...msg, sessionId });
|
|
5220
5269
|
for (const client of this.wss.clients) {
|
|
5221
5270
|
if (client === exclude)
|
|
5222
5271
|
continue;
|
|
5223
5272
|
if (client.readyState === WebSocket.OPEN) {
|
|
5224
|
-
|
|
5273
|
+
const outboundMsg = {
|
|
5274
|
+
...msg,
|
|
5275
|
+
sessionId,
|
|
5276
|
+
};
|
|
5277
|
+
const compatibleMsg = this.prepareServerMessageForClient(client, outboundMsg);
|
|
5278
|
+
if (!compatibleMsg)
|
|
5225
5279
|
continue;
|
|
5226
|
-
client.send(
|
|
5280
|
+
client.send(JSON.stringify(compatibleMsg));
|
|
5227
5281
|
}
|
|
5228
5282
|
}
|
|
5229
5283
|
}
|
|
@@ -5304,6 +5358,7 @@ export class BridgeWebSocketServer {
|
|
|
5304
5358
|
console.warn(`[ws] Failed to load Codex metadata: ${err}`);
|
|
5305
5359
|
this.codexProfiles = [];
|
|
5306
5360
|
this.defaultCodexProfile = undefined;
|
|
5361
|
+
this.codexAutoReviewPolicyLoaded = false;
|
|
5307
5362
|
this.applyFallbackCodexModels();
|
|
5308
5363
|
this.broadcastSessionList();
|
|
5309
5364
|
})
|
|
@@ -5316,9 +5371,10 @@ export class BridgeWebSocketServer {
|
|
|
5316
5371
|
const activeProcess = this.getActiveCodexProcess();
|
|
5317
5372
|
const codexProcess = activeProcess ?? (await this.createStandaloneCodexProcess(projectPath));
|
|
5318
5373
|
try {
|
|
5319
|
-
const [profileResult, modelResult] = await Promise.allSettled([
|
|
5374
|
+
const [profileResult, modelResult, requirementsResult] = await Promise.allSettled([
|
|
5320
5375
|
codexProcess.readProfileConfig(projectPath),
|
|
5321
5376
|
this.readCodexModels(codexProcess),
|
|
5377
|
+
this.readCodexAutoReviewDisabled(codexProcess),
|
|
5322
5378
|
]);
|
|
5323
5379
|
if (profileResult.status === "fulfilled") {
|
|
5324
5380
|
this.codexProfiles = profileResult.value.profiles;
|
|
@@ -5338,6 +5394,14 @@ export class BridgeWebSocketServer {
|
|
|
5338
5394
|
}
|
|
5339
5395
|
this.applyFallbackCodexModels();
|
|
5340
5396
|
}
|
|
5397
|
+
if (requirementsResult.status === "fulfilled") {
|
|
5398
|
+
this.codexAutoReviewDisabled = requirementsResult.value;
|
|
5399
|
+
this.codexAutoReviewPolicyLoaded = true;
|
|
5400
|
+
}
|
|
5401
|
+
else {
|
|
5402
|
+
console.warn(`[ws] Failed to load Codex config requirements: ${requirementsResult.reason}`);
|
|
5403
|
+
this.codexAutoReviewPolicyLoaded = false;
|
|
5404
|
+
}
|
|
5341
5405
|
this.broadcastSessionList();
|
|
5342
5406
|
}
|
|
5343
5407
|
finally {
|
|
@@ -5401,6 +5465,14 @@ export class BridgeWebSocketServer {
|
|
|
5401
5465
|
supportedServiceTiers: fallbackCodexServiceTiers(model),
|
|
5402
5466
|
}));
|
|
5403
5467
|
}
|
|
5468
|
+
async readCodexAutoReviewDisabled(codexProcess) {
|
|
5469
|
+
const requirementsSource = codexProcess;
|
|
5470
|
+
if (typeof requirementsSource.readConfigRequirements !== "function") {
|
|
5471
|
+
return false;
|
|
5472
|
+
}
|
|
5473
|
+
return (await requirementsSource.readConfigRequirements())
|
|
5474
|
+
.autoReviewDisabled;
|
|
5475
|
+
}
|
|
5404
5476
|
applyCodexModels(models) {
|
|
5405
5477
|
this.codexModels = models.map((model) => model.model);
|
|
5406
5478
|
this.codexModelReasoningEfforts = Object.fromEntries(models.map((model) => [
|
|
@@ -5479,6 +5551,19 @@ export class BridgeWebSocketServer {
|
|
|
5479
5551
|
? session.process
|
|
5480
5552
|
: null;
|
|
5481
5553
|
}
|
|
5554
|
+
withCodexAutoReviewPolicy(options) {
|
|
5555
|
+
const disableAutoReview = this.codexAutoReviewDisabled;
|
|
5556
|
+
return {
|
|
5557
|
+
...options,
|
|
5558
|
+
...(disableAutoReview ? { approvalsReviewer: "user" } : {}),
|
|
5559
|
+
...(disableAutoReview && options.codexPermissionsMode === "autoReview"
|
|
5560
|
+
? { codexPermissionsMode: "default" }
|
|
5561
|
+
: {}),
|
|
5562
|
+
autoReviewDisabledByPolicy: this.codexAutoReviewPolicyLoaded
|
|
5563
|
+
? disableAutoReview
|
|
5564
|
+
: null,
|
|
5565
|
+
};
|
|
5566
|
+
}
|
|
5482
5567
|
getActiveClaudeProcess() {
|
|
5483
5568
|
const summary = this.sessionManager
|
|
5484
5569
|
.list()
|
|
@@ -5726,15 +5811,39 @@ export class BridgeWebSocketServer {
|
|
|
5726
5811
|
}
|
|
5727
5812
|
}
|
|
5728
5813
|
broadcast(msg) {
|
|
5729
|
-
const data = JSON.stringify(msg);
|
|
5730
5814
|
for (const client of this.wss.clients) {
|
|
5731
5815
|
if (client.readyState === WebSocket.OPEN) {
|
|
5732
|
-
|
|
5816
|
+
const compatibleMsg = this.prepareServerMessageForClient(client, msg);
|
|
5817
|
+
if (!compatibleMsg)
|
|
5733
5818
|
continue;
|
|
5734
|
-
client.send(
|
|
5819
|
+
client.send(JSON.stringify(compatibleMsg));
|
|
5735
5820
|
}
|
|
5736
5821
|
}
|
|
5737
5822
|
}
|
|
5823
|
+
prepareServerMessageForClient(ws, msg) {
|
|
5824
|
+
if (!this.shouldSendToClient(ws, msg))
|
|
5825
|
+
return null;
|
|
5826
|
+
if (!("messages" in msg) || !Array.isArray(msg.messages))
|
|
5827
|
+
return msg;
|
|
5828
|
+
const messages = msg.messages;
|
|
5829
|
+
if (msg.type === "history") {
|
|
5830
|
+
return {
|
|
5831
|
+
...msg,
|
|
5832
|
+
messages: messages.filter((message) => isRecord(message) && this.shouldSendToClient(ws, message)),
|
|
5833
|
+
};
|
|
5834
|
+
}
|
|
5835
|
+
if (msg.type === "history_delta" || msg.type === "history_snapshot") {
|
|
5836
|
+
return {
|
|
5837
|
+
...msg,
|
|
5838
|
+
messages: messages.filter((entry) => {
|
|
5839
|
+
if (!isRecord(entry) || !isRecord(entry.message))
|
|
5840
|
+
return true;
|
|
5841
|
+
return this.shouldSendToClient(ws, entry.message);
|
|
5842
|
+
}),
|
|
5843
|
+
};
|
|
5844
|
+
}
|
|
5845
|
+
return msg;
|
|
5846
|
+
}
|
|
5738
5847
|
shouldSendToClient(ws, msg) {
|
|
5739
5848
|
const type = typeof msg.type === "string" ? msg.type : "";
|
|
5740
5849
|
if (!OPT_IN_SERVER_MESSAGES.has(type))
|
|
@@ -5828,19 +5937,20 @@ export class BridgeWebSocketServer {
|
|
|
5828
5937
|
this.send(ws, msg);
|
|
5829
5938
|
}
|
|
5830
5939
|
send(ws, msg) {
|
|
5831
|
-
|
|
5940
|
+
const compatibleMsg = this.prepareServerMessageForClient(ws, msg);
|
|
5941
|
+
if (!compatibleMsg)
|
|
5832
5942
|
return;
|
|
5833
|
-
const sessionId = this.extractSessionIdFromServerMessage(
|
|
5943
|
+
const sessionId = this.extractSessionIdFromServerMessage(compatibleMsg);
|
|
5834
5944
|
if (sessionId) {
|
|
5835
5945
|
this.recordDebugEvent(sessionId, {
|
|
5836
5946
|
direction: "outgoing",
|
|
5837
5947
|
channel: "ws",
|
|
5838
|
-
type: String(
|
|
5839
|
-
detail: this.summarizeOutboundMessage(
|
|
5948
|
+
type: String(compatibleMsg.type ?? "unknown"),
|
|
5949
|
+
detail: this.summarizeOutboundMessage(compatibleMsg),
|
|
5840
5950
|
});
|
|
5841
5951
|
}
|
|
5842
5952
|
if (ws.readyState === WebSocket.OPEN) {
|
|
5843
|
-
ws.send(JSON.stringify(
|
|
5953
|
+
ws.send(JSON.stringify(compatibleMsg));
|
|
5844
5954
|
}
|
|
5845
5955
|
}
|
|
5846
5956
|
/** Broadcast a gallery_new_image message to all connected clients. */
|