@ccpocket/bridge 1.26.0 → 1.27.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/README.md +4 -0
- package/dist/codex-process.d.ts +1 -0
- package/dist/codex-process.js +210 -59
- package/dist/codex-process.js.map +1 -1
- package/dist/parser.d.ts +13 -0
- package/dist/parser.js +15 -0
- package/dist/parser.js.map +1 -1
- package/dist/session.d.ts +2 -0
- package/dist/session.js +96 -24
- package/dist/session.js.map +1 -1
- package/dist/websocket.d.ts +3 -0
- package/dist/websocket.js +300 -96
- package/dist/websocket.js.map +1 -1
- package/package.json +1 -1
package/dist/websocket.js
CHANGED
|
@@ -38,6 +38,39 @@ const CODEX_MODELS = [
|
|
|
38
38
|
function permissionModeToApprovalPolicy(mode) {
|
|
39
39
|
return mode === "bypassPermissions" ? "never" : "on-request";
|
|
40
40
|
}
|
|
41
|
+
function deriveExecutionMode(params) {
|
|
42
|
+
if (params.executionMode === "default" ||
|
|
43
|
+
params.executionMode === "acceptEdits" ||
|
|
44
|
+
params.executionMode === "fullAccess") {
|
|
45
|
+
return params.executionMode;
|
|
46
|
+
}
|
|
47
|
+
if (params.permissionMode === "bypassPermissions" ||
|
|
48
|
+
params.approvalPolicy === "never") {
|
|
49
|
+
return "fullAccess";
|
|
50
|
+
}
|
|
51
|
+
if (params.permissionMode === "acceptEdits") {
|
|
52
|
+
return params.provider === "codex" ? "default" : "acceptEdits";
|
|
53
|
+
}
|
|
54
|
+
return "default";
|
|
55
|
+
}
|
|
56
|
+
function derivePlanMode(params) {
|
|
57
|
+
return params.planMode ??
|
|
58
|
+
((params.permissionMode === "plan") ||
|
|
59
|
+
(params.collaborationMode === "plan"));
|
|
60
|
+
}
|
|
61
|
+
function modesToLegacyPermissionMode(provider, executionMode, planMode) {
|
|
62
|
+
if (planMode)
|
|
63
|
+
return "plan";
|
|
64
|
+
switch (executionMode) {
|
|
65
|
+
case "fullAccess":
|
|
66
|
+
return "bypassPermissions";
|
|
67
|
+
case "acceptEdits":
|
|
68
|
+
return "acceptEdits";
|
|
69
|
+
case "default":
|
|
70
|
+
default:
|
|
71
|
+
return provider === "codex" ? "acceptEdits" : "default";
|
|
72
|
+
}
|
|
73
|
+
}
|
|
41
74
|
/** Map simplified SandboxMode (on/off) to Codex internal sandbox mode. */
|
|
42
75
|
function sandboxModeToInternal(mode) {
|
|
43
76
|
switch (mode) {
|
|
@@ -58,6 +91,10 @@ function sandboxModeToExternal(mode) {
|
|
|
58
91
|
function threadTimestampToIso(value) {
|
|
59
92
|
return value > 0 ? new Date(value * 1000).toISOString() : "";
|
|
60
93
|
}
|
|
94
|
+
function envFlagEnabled(name) {
|
|
95
|
+
const value = process.env[name]?.trim().toLowerCase();
|
|
96
|
+
return value === "1" || value === "true" || value === "yes" || value === "on";
|
|
97
|
+
}
|
|
61
98
|
function codexThreadToRecentSession(thread, indexed) {
|
|
62
99
|
return {
|
|
63
100
|
sessionId: thread.id,
|
|
@@ -98,6 +135,8 @@ export class BridgeWebSocketServer {
|
|
|
98
135
|
/** FCM token → push notification locale */
|
|
99
136
|
tokenLocales = new Map();
|
|
100
137
|
tokenPrivacyMode = new Map();
|
|
138
|
+
failSetPermissionMode = envFlagEnabled("BRIDGE_FAIL_SET_PERMISSION_MODE");
|
|
139
|
+
failSetSandboxMode = envFlagEnabled("BRIDGE_FAIL_SET_SANDBOX_MODE");
|
|
101
140
|
constructor(options) {
|
|
102
141
|
const { server, apiKey, allowedDirs, imageStore, galleryStore, projectHistory, debugTraceStore, recordingStore, firebaseAuth, promptHistoryBackup } = options;
|
|
103
142
|
this.apiKey = apiKey ?? null;
|
|
@@ -176,6 +215,91 @@ export class BridgeWebSocketServer {
|
|
|
176
215
|
errorCode: "path_not_allowed",
|
|
177
216
|
};
|
|
178
217
|
}
|
|
218
|
+
buildSessionCreatedMessage(params) {
|
|
219
|
+
const { sessionId, provider, projectPath, session, permissionMode, executionMode, planMode, sandboxMode, slashCommands, skills, skillMetadata, sourceSessionId, } = params;
|
|
220
|
+
const msg = {
|
|
221
|
+
type: "system",
|
|
222
|
+
subtype: "session_created",
|
|
223
|
+
sessionId,
|
|
224
|
+
provider,
|
|
225
|
+
projectPath,
|
|
226
|
+
...(permissionMode ? { permissionMode: permissionMode } : {}),
|
|
227
|
+
...((executionMode ?? (session?.process instanceof SdkProcess
|
|
228
|
+
? session.process.permissionMode === "bypassPermissions"
|
|
229
|
+
? "fullAccess"
|
|
230
|
+
: session.process.permissionMode === "acceptEdits"
|
|
231
|
+
? "acceptEdits"
|
|
232
|
+
: "default"
|
|
233
|
+
: session?.process instanceof CodexProcess
|
|
234
|
+
? session.process.approvalPolicy === "never"
|
|
235
|
+
? "fullAccess"
|
|
236
|
+
: "default"
|
|
237
|
+
: undefined))
|
|
238
|
+
? {
|
|
239
|
+
executionMode: (executionMode ?? (session?.process instanceof SdkProcess
|
|
240
|
+
? session.process.permissionMode === "bypassPermissions"
|
|
241
|
+
? "fullAccess"
|
|
242
|
+
: session.process.permissionMode === "acceptEdits"
|
|
243
|
+
? "acceptEdits"
|
|
244
|
+
: "default"
|
|
245
|
+
: session?.process instanceof CodexProcess
|
|
246
|
+
? session.process.approvalPolicy === "never"
|
|
247
|
+
? "fullAccess"
|
|
248
|
+
: "default"
|
|
249
|
+
: undefined)),
|
|
250
|
+
}
|
|
251
|
+
: {}),
|
|
252
|
+
...((planMode ??
|
|
253
|
+
(session?.process instanceof SdkProcess
|
|
254
|
+
? session.process.permissionMode === "plan"
|
|
255
|
+
: session?.process instanceof CodexProcess
|
|
256
|
+
? session.process.collaborationMode === "plan"
|
|
257
|
+
: undefined)) !=
|
|
258
|
+
null
|
|
259
|
+
? {
|
|
260
|
+
planMode: planMode ??
|
|
261
|
+
(session?.process instanceof SdkProcess
|
|
262
|
+
? session.process.permissionMode === "plan"
|
|
263
|
+
: session?.process instanceof CodexProcess
|
|
264
|
+
? session.process.collaborationMode === "plan"
|
|
265
|
+
: false),
|
|
266
|
+
}
|
|
267
|
+
: {}),
|
|
268
|
+
...(sandboxMode ? { sandboxMode } : {}),
|
|
269
|
+
...(slashCommands ? { slashCommands } : {}),
|
|
270
|
+
...(skills ? { skills } : {}),
|
|
271
|
+
...(skillMetadata
|
|
272
|
+
? {
|
|
273
|
+
skillMetadata: skillMetadata,
|
|
274
|
+
}
|
|
275
|
+
: {}),
|
|
276
|
+
...(session?.worktreePath
|
|
277
|
+
? {
|
|
278
|
+
worktreePath: session.worktreePath,
|
|
279
|
+
worktreeBranch: session.worktreeBranch,
|
|
280
|
+
}
|
|
281
|
+
: {}),
|
|
282
|
+
...(sourceSessionId ? { sourceSessionId } : {}),
|
|
283
|
+
};
|
|
284
|
+
if (provider === "codex" && session?.codexSettings) {
|
|
285
|
+
if (session.codexSettings.model !== undefined) {
|
|
286
|
+
msg.model = session.codexSettings.model;
|
|
287
|
+
}
|
|
288
|
+
if (session.codexSettings.approvalPolicy !== undefined) {
|
|
289
|
+
msg.approvalPolicy = session.codexSettings.approvalPolicy;
|
|
290
|
+
}
|
|
291
|
+
if (session.codexSettings.modelReasoningEffort !== undefined) {
|
|
292
|
+
msg.modelReasoningEffort = session.codexSettings.modelReasoningEffort;
|
|
293
|
+
}
|
|
294
|
+
if (session.codexSettings.networkAccessEnabled !== undefined) {
|
|
295
|
+
msg.networkAccessEnabled = session.codexSettings.networkAccessEnabled;
|
|
296
|
+
}
|
|
297
|
+
if (session.codexSettings.webSearchMode !== undefined) {
|
|
298
|
+
msg.webSearchMode = session.codexSettings.webSearchMode;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return msg;
|
|
302
|
+
}
|
|
179
303
|
close() {
|
|
180
304
|
console.log("[ws] Shutting down...");
|
|
181
305
|
this.sessionManager.destroyAll();
|
|
@@ -244,14 +368,24 @@ export class BridgeWebSocketServer {
|
|
|
244
368
|
}
|
|
245
369
|
try {
|
|
246
370
|
const provider = msg.provider ?? "claude";
|
|
371
|
+
const executionMode = deriveExecutionMode({
|
|
372
|
+
provider,
|
|
373
|
+
permissionMode: msg.permissionMode,
|
|
374
|
+
executionMode: msg.executionMode,
|
|
375
|
+
});
|
|
376
|
+
const planMode = derivePlanMode({
|
|
377
|
+
permissionMode: msg.permissionMode,
|
|
378
|
+
planMode: msg.planMode,
|
|
379
|
+
});
|
|
380
|
+
const legacyPermissionMode = modesToLegacyPermissionMode(provider, executionMode, planMode);
|
|
247
381
|
if (provider === "codex") {
|
|
248
|
-
console.log(`[ws] start(codex):
|
|
382
|
+
console.log(`[ws] start(codex): execution=${executionMode} plan=${planMode}`);
|
|
249
383
|
}
|
|
250
384
|
const cached = provider === "claude" ? this.sessionManager.getCachedCommands(msg.projectPath) : undefined;
|
|
251
385
|
const sessionId = this.sessionManager.create(msg.projectPath, {
|
|
252
386
|
sessionId: msg.sessionId,
|
|
253
387
|
continueMode: msg.continue,
|
|
254
|
-
permissionMode:
|
|
388
|
+
permissionMode: legacyPermissionMode,
|
|
255
389
|
model: msg.model,
|
|
256
390
|
effort: msg.effort,
|
|
257
391
|
maxTurns: msg.maxTurns,
|
|
@@ -269,33 +403,38 @@ export class BridgeWebSocketServer {
|
|
|
269
403
|
existingWorktreePath: msg.existingWorktreePath,
|
|
270
404
|
}, provider, provider === "codex"
|
|
271
405
|
? {
|
|
272
|
-
approvalPolicy:
|
|
406
|
+
approvalPolicy: executionMode === "fullAccess" ? "never" : "on-request",
|
|
273
407
|
sandboxMode: sandboxModeToInternal(msg.sandboxMode),
|
|
274
408
|
model: msg.model,
|
|
275
409
|
modelReasoningEffort: msg.modelReasoningEffort ?? undefined,
|
|
276
410
|
networkAccessEnabled: msg.networkAccessEnabled,
|
|
277
411
|
webSearchMode: msg.webSearchMode ?? undefined,
|
|
278
412
|
threadId: msg.sessionId,
|
|
279
|
-
collaborationMode:
|
|
413
|
+
collaborationMode: planMode ? "plan" : "default",
|
|
280
414
|
}
|
|
281
415
|
: undefined);
|
|
282
416
|
const createdSession = this.sessionManager.get(sessionId);
|
|
283
417
|
// Load saved session name from CLI storage (for resumed sessions)
|
|
284
418
|
void this.loadAndSetSessionName(createdSession, provider, msg.projectPath, msg.sessionId).then(() => {
|
|
285
|
-
this.send(ws, {
|
|
286
|
-
type: "system",
|
|
287
|
-
subtype: "session_created",
|
|
419
|
+
this.send(ws, this.buildSessionCreatedMessage({
|
|
288
420
|
sessionId,
|
|
289
421
|
provider,
|
|
290
422
|
projectPath: msg.projectPath,
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
423
|
+
session: createdSession,
|
|
424
|
+
permissionMode: legacyPermissionMode,
|
|
425
|
+
executionMode,
|
|
426
|
+
planMode,
|
|
427
|
+
sandboxMode: msg.sandboxMode,
|
|
428
|
+
...(cached
|
|
429
|
+
? {
|
|
430
|
+
slashCommands: cached.slashCommands,
|
|
431
|
+
skills: cached.skills,
|
|
432
|
+
...(cached.skillMetadata
|
|
433
|
+
? { skillMetadata: cached.skillMetadata }
|
|
434
|
+
: {}),
|
|
435
|
+
}
|
|
436
|
+
: {}),
|
|
437
|
+
}));
|
|
299
438
|
this.broadcastSessionList();
|
|
300
439
|
// Send a gentle tip when the project is not a git repository
|
|
301
440
|
if (createdSession && !createdSession.gitBranch) {
|
|
@@ -510,6 +649,14 @@ export class BridgeWebSocketServer {
|
|
|
510
649
|
break;
|
|
511
650
|
}
|
|
512
651
|
case "set_permission_mode": {
|
|
652
|
+
if (this.failSetPermissionMode) {
|
|
653
|
+
this.send(ws, {
|
|
654
|
+
type: "error",
|
|
655
|
+
message: "Failed to set permission mode: forced test failure",
|
|
656
|
+
errorCode: "set_permission_mode_rejected",
|
|
657
|
+
});
|
|
658
|
+
break;
|
|
659
|
+
}
|
|
513
660
|
const session = this.resolveSession(msg.sessionId);
|
|
514
661
|
if (!session) {
|
|
515
662
|
this.send(ws, { type: "error", message: "No active session." });
|
|
@@ -519,14 +666,52 @@ export class BridgeWebSocketServer {
|
|
|
519
666
|
// Permission mode for Codex requires a session restart (like sandbox mode).
|
|
520
667
|
// approvalPolicy and collaborationMode are thread-level settings that
|
|
521
668
|
// only take effect reliably at thread/start or thread/resume time.
|
|
522
|
-
const
|
|
523
|
-
|
|
669
|
+
const executionMode = deriveExecutionMode({
|
|
670
|
+
provider: "codex",
|
|
671
|
+
permissionMode: msg.mode,
|
|
672
|
+
executionMode: msg.executionMode,
|
|
673
|
+
});
|
|
674
|
+
const planMode = derivePlanMode({
|
|
675
|
+
permissionMode: msg.mode,
|
|
676
|
+
planMode: msg.planMode,
|
|
677
|
+
});
|
|
678
|
+
const legacyPermissionMode = modesToLegacyPermissionMode("codex", executionMode, planMode);
|
|
679
|
+
const newApproval = executionMode === "fullAccess" ? "never" : "on-request";
|
|
680
|
+
const newCollaboration = planMode ? "plan" : "default";
|
|
524
681
|
const currentApproval = session.process.approvalPolicy;
|
|
525
682
|
const currentCollaboration = session.process.collaborationMode;
|
|
526
683
|
if (newApproval === currentApproval && newCollaboration === currentCollaboration) {
|
|
527
684
|
break; // No change needed
|
|
528
685
|
}
|
|
529
|
-
|
|
686
|
+
const canApplyModeInPlace = session.status === "idle";
|
|
687
|
+
if (canApplyModeInPlace) {
|
|
688
|
+
const process = session.process;
|
|
689
|
+
if (newApproval !== currentApproval) {
|
|
690
|
+
process.setApprovalPolicy(newApproval);
|
|
691
|
+
}
|
|
692
|
+
if (newCollaboration !== currentCollaboration) {
|
|
693
|
+
process.setCollaborationMode(newCollaboration);
|
|
694
|
+
}
|
|
695
|
+
session.lastActivityAt = new Date();
|
|
696
|
+
this.broadcast({
|
|
697
|
+
type: "system",
|
|
698
|
+
subtype: "set_permission_mode",
|
|
699
|
+
sessionId: session.id,
|
|
700
|
+
permissionMode: legacyPermissionMode,
|
|
701
|
+
executionMode,
|
|
702
|
+
planMode,
|
|
703
|
+
});
|
|
704
|
+
this.broadcastSessionList();
|
|
705
|
+
this.recordDebugEvent(session.id, {
|
|
706
|
+
direction: "internal",
|
|
707
|
+
channel: "bridge",
|
|
708
|
+
type: "permission_mode_changed",
|
|
709
|
+
detail: `mode=${msg.mode} approval=${newApproval} collaboration=${newCollaboration} applied=in-place`,
|
|
710
|
+
});
|
|
711
|
+
console.log(`[ws] set_permission_mode(codex): execution=${executionMode} plan=${planMode} → approval=${newApproval}, collaboration=${newCollaboration} (in-place)`);
|
|
712
|
+
break;
|
|
713
|
+
}
|
|
714
|
+
console.log(`[ws] set_permission_mode(codex): execution=${executionMode} plan=${planMode} → approval=${newApproval}, collaboration=${newCollaboration} (restart)`);
|
|
530
715
|
const oldSessionId = session.id;
|
|
531
716
|
const threadId = session.claudeSessionId;
|
|
532
717
|
const projectPath = session.projectPath;
|
|
@@ -550,17 +735,19 @@ export class BridgeWebSocketServer {
|
|
|
550
735
|
const newSession = this.sessionManager.get(newId);
|
|
551
736
|
if (newSession && sessionName)
|
|
552
737
|
newSession.name = sessionName;
|
|
553
|
-
this.broadcast({
|
|
554
|
-
type: "system",
|
|
555
|
-
subtype: "session_created",
|
|
738
|
+
this.broadcast(this.buildSessionCreatedMessage({
|
|
556
739
|
sessionId: newId,
|
|
557
740
|
provider: "codex",
|
|
558
741
|
projectPath,
|
|
559
|
-
|
|
560
|
-
|
|
742
|
+
session: newSession,
|
|
743
|
+
permissionMode: legacyPermissionMode,
|
|
744
|
+
executionMode,
|
|
745
|
+
planMode,
|
|
746
|
+
sandboxMode: oldSettings.sandboxMode
|
|
747
|
+
? sandboxModeToExternal(oldSettings.sandboxMode)
|
|
748
|
+
: undefined,
|
|
561
749
|
sourceSessionId: oldSessionId,
|
|
562
|
-
|
|
563
|
-
});
|
|
750
|
+
}));
|
|
564
751
|
this.broadcastSessionList();
|
|
565
752
|
console.log(`[ws] Permission mode change (no thread): created new session ${newId} (mode=${msg.mode})`);
|
|
566
753
|
break;
|
|
@@ -596,20 +783,19 @@ export class BridgeWebSocketServer {
|
|
|
596
783
|
newSession.name = sessionName;
|
|
597
784
|
}
|
|
598
785
|
void this.loadAndSetSessionName(newSession, "codex", effectiveProjectPath, threadId).then(() => {
|
|
599
|
-
this.broadcast({
|
|
600
|
-
type: "system",
|
|
601
|
-
subtype: "session_created",
|
|
786
|
+
this.broadcast(this.buildSessionCreatedMessage({
|
|
602
787
|
sessionId: newId,
|
|
603
788
|
provider: "codex",
|
|
604
789
|
projectPath: effectiveProjectPath,
|
|
605
|
-
|
|
606
|
-
|
|
790
|
+
session: newSession,
|
|
791
|
+
permissionMode: legacyPermissionMode,
|
|
792
|
+
executionMode,
|
|
793
|
+
planMode,
|
|
794
|
+
sandboxMode: oldSettings.sandboxMode
|
|
795
|
+
? sandboxModeToExternal(oldSettings.sandboxMode)
|
|
796
|
+
: undefined,
|
|
607
797
|
sourceSessionId: oldSessionId,
|
|
608
|
-
|
|
609
|
-
worktreePath: newSession.worktreePath,
|
|
610
|
-
worktreeBranch: newSession.worktreeBranch,
|
|
611
|
-
} : {}),
|
|
612
|
-
});
|
|
798
|
+
}));
|
|
613
799
|
this.broadcastSessionList();
|
|
614
800
|
});
|
|
615
801
|
this.debugEvents.set(newId, []);
|
|
@@ -634,6 +820,14 @@ export class BridgeWebSocketServer {
|
|
|
634
820
|
break;
|
|
635
821
|
}
|
|
636
822
|
case "set_sandbox_mode": {
|
|
823
|
+
if (this.failSetSandboxMode) {
|
|
824
|
+
this.send(ws, {
|
|
825
|
+
type: "error",
|
|
826
|
+
message: "Failed to set sandbox mode: forced test failure",
|
|
827
|
+
errorCode: "set_sandbox_mode_rejected",
|
|
828
|
+
});
|
|
829
|
+
break;
|
|
830
|
+
}
|
|
637
831
|
const session = this.resolveSession(msg.sessionId);
|
|
638
832
|
if (!session) {
|
|
639
833
|
this.send(ws, { type: "error", message: "No active session." });
|
|
@@ -670,19 +864,14 @@ export class BridgeWebSocketServer {
|
|
|
670
864
|
if (newSession && sessionName)
|
|
671
865
|
newSession.name = sessionName;
|
|
672
866
|
void this.loadAndSetSessionName(newSession, "claude", projectPath, claudeSessionId).then(() => {
|
|
673
|
-
this.broadcast({
|
|
674
|
-
type: "system",
|
|
675
|
-
subtype: "session_created",
|
|
867
|
+
this.broadcast(this.buildSessionCreatedMessage({
|
|
676
868
|
sessionId: newId,
|
|
677
869
|
provider: "claude",
|
|
678
870
|
projectPath,
|
|
871
|
+
session: newSession,
|
|
679
872
|
sandboxMode: msg.sandboxMode,
|
|
680
873
|
sourceSessionId: oldSessionId,
|
|
681
|
-
|
|
682
|
-
worktreePath: newSession.worktreePath,
|
|
683
|
-
worktreeBranch: newSession.worktreeBranch,
|
|
684
|
-
} : {}),
|
|
685
|
-
});
|
|
874
|
+
}));
|
|
686
875
|
this.broadcastSessionList();
|
|
687
876
|
});
|
|
688
877
|
this.debugEvents.set(newId, []);
|
|
@@ -713,6 +902,9 @@ export class BridgeWebSocketServer {
|
|
|
713
902
|
const worktreeBranch = session.worktreeBranch;
|
|
714
903
|
const sessionName = session.name;
|
|
715
904
|
const collaborationMode = session.process.collaborationMode;
|
|
905
|
+
const executionMode = oldSettings.approvalPolicy === "never" ? "fullAccess" : "default";
|
|
906
|
+
const planMode = collaborationMode === "plan";
|
|
907
|
+
const legacyPermissionMode = modesToLegacyPermissionMode("codex", executionMode, planMode);
|
|
716
908
|
this.sessionManager.destroy(oldSessionId);
|
|
717
909
|
console.log(`[ws] Sandbox mode change: destroyed session ${oldSessionId}`);
|
|
718
910
|
// Check if the user actually exchanged messages in this session.
|
|
@@ -737,16 +929,17 @@ export class BridgeWebSocketServer {
|
|
|
737
929
|
const newSession = this.sessionManager.get(newId);
|
|
738
930
|
if (newSession && sessionName)
|
|
739
931
|
newSession.name = sessionName;
|
|
740
|
-
this.broadcast({
|
|
741
|
-
type: "system",
|
|
742
|
-
subtype: "session_created",
|
|
932
|
+
this.broadcast(this.buildSessionCreatedMessage({
|
|
743
933
|
sessionId: newId,
|
|
744
934
|
provider: "codex",
|
|
745
935
|
projectPath,
|
|
936
|
+
session: newSession,
|
|
937
|
+
permissionMode: legacyPermissionMode,
|
|
938
|
+
executionMode,
|
|
939
|
+
planMode,
|
|
746
940
|
sandboxMode: sandboxModeToExternal(newSandboxMode),
|
|
747
941
|
sourceSessionId: oldSessionId,
|
|
748
|
-
|
|
749
|
-
});
|
|
942
|
+
}));
|
|
750
943
|
this.broadcastSessionList();
|
|
751
944
|
console.log(`[ws] Sandbox mode change (no thread): created new session ${newId} (sandbox=${newSandboxMode})`);
|
|
752
945
|
break;
|
|
@@ -783,19 +976,17 @@ export class BridgeWebSocketServer {
|
|
|
783
976
|
newSession.name = sessionName;
|
|
784
977
|
}
|
|
785
978
|
void this.loadAndSetSessionName(newSession, "codex", effectiveProjectPath, threadId).then(() => {
|
|
786
|
-
this.broadcast({
|
|
787
|
-
type: "system",
|
|
788
|
-
subtype: "session_created",
|
|
979
|
+
this.broadcast(this.buildSessionCreatedMessage({
|
|
789
980
|
sessionId: newId,
|
|
790
981
|
provider: "codex",
|
|
791
982
|
projectPath: effectiveProjectPath,
|
|
983
|
+
session: newSession,
|
|
984
|
+
permissionMode: legacyPermissionMode,
|
|
985
|
+
executionMode,
|
|
986
|
+
planMode,
|
|
792
987
|
sandboxMode: sandboxModeToExternal(newSandboxMode),
|
|
793
988
|
sourceSessionId: oldSessionId,
|
|
794
|
-
|
|
795
|
-
worktreePath: newSession.worktreePath,
|
|
796
|
-
worktreeBranch: newSession.worktreeBranch,
|
|
797
|
-
} : {}),
|
|
798
|
-
});
|
|
989
|
+
}));
|
|
799
990
|
this.broadcastSessionList();
|
|
800
991
|
});
|
|
801
992
|
this.debugEvents.set(newId, []);
|
|
@@ -856,16 +1047,15 @@ export class BridgeWebSocketServer {
|
|
|
856
1047
|
console.log(`[ws] Clear context: created new session ${newId} (CLI session: ${claudeSessionId ?? "new"})`);
|
|
857
1048
|
// Notify all clients. Broadcast is used so reconnecting clients also receive it.
|
|
858
1049
|
const newSession = this.sessionManager.get(newId);
|
|
859
|
-
this.
|
|
860
|
-
type: "system",
|
|
861
|
-
subtype: "session_created",
|
|
1050
|
+
const createdMsg = this.buildSessionCreatedMessage({
|
|
862
1051
|
sessionId: newId,
|
|
863
1052
|
provider: newSession?.provider ?? "claude",
|
|
864
1053
|
projectPath,
|
|
865
|
-
|
|
866
|
-
|
|
1054
|
+
session: newSession,
|
|
1055
|
+
permissionMode,
|
|
867
1056
|
sourceSessionId: sessionId,
|
|
868
1057
|
});
|
|
1058
|
+
this.broadcast({ ...createdMsg, clearContext: true });
|
|
869
1059
|
this.broadcastSessionList();
|
|
870
1060
|
}
|
|
871
1061
|
else {
|
|
@@ -1109,6 +1299,16 @@ export class BridgeWebSocketServer {
|
|
|
1109
1299
|
break;
|
|
1110
1300
|
}
|
|
1111
1301
|
const provider = msg.provider ?? "claude";
|
|
1302
|
+
const executionMode = deriveExecutionMode({
|
|
1303
|
+
provider,
|
|
1304
|
+
permissionMode: msg.permissionMode,
|
|
1305
|
+
executionMode: msg.executionMode,
|
|
1306
|
+
});
|
|
1307
|
+
const planMode = derivePlanMode({
|
|
1308
|
+
permissionMode: msg.permissionMode,
|
|
1309
|
+
planMode: msg.planMode,
|
|
1310
|
+
});
|
|
1311
|
+
const legacyPermissionMode = modesToLegacyPermissionMode(provider, executionMode, planMode);
|
|
1112
1312
|
const sessionRefId = msg.sessionId;
|
|
1113
1313
|
// Resume flow: keep past history in SessionInfo and deliver it only
|
|
1114
1314
|
// via get_history(sessionId) to avoid duplicate/missed replay races.
|
|
@@ -1133,29 +1333,28 @@ export class BridgeWebSocketServer {
|
|
|
1133
1333
|
getCodexSessionHistory(sessionRefId).then((pastMessages) => {
|
|
1134
1334
|
const sessionId = this.sessionManager.create(effectiveProjectPath, undefined, pastMessages, worktreeOpts, "codex", {
|
|
1135
1335
|
threadId: sessionRefId,
|
|
1136
|
-
approvalPolicy:
|
|
1336
|
+
approvalPolicy: executionMode === "fullAccess" ? "never" : "on-request",
|
|
1137
1337
|
sandboxMode: sandboxModeToInternal(msg.sandboxMode),
|
|
1138
1338
|
model: msg.model,
|
|
1139
1339
|
modelReasoningEffort: msg.modelReasoningEffort ?? undefined,
|
|
1140
1340
|
networkAccessEnabled: msg.networkAccessEnabled,
|
|
1141
1341
|
webSearchMode: msg.webSearchMode ?? undefined,
|
|
1142
|
-
collaborationMode:
|
|
1342
|
+
collaborationMode: planMode ? "plan" : "default",
|
|
1143
1343
|
});
|
|
1144
1344
|
const createdSession = this.sessionManager.get(sessionId);
|
|
1145
1345
|
void this.loadAndSetSessionName(createdSession, "codex", effectiveProjectPath, sessionRefId).then(() => {
|
|
1146
|
-
this.send(ws, {
|
|
1147
|
-
type: "system",
|
|
1148
|
-
subtype: "session_created",
|
|
1346
|
+
this.send(ws, this.buildSessionCreatedMessage({
|
|
1149
1347
|
sessionId,
|
|
1150
1348
|
provider: "codex",
|
|
1151
1349
|
projectPath: effectiveProjectPath,
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1350
|
+
session: createdSession,
|
|
1351
|
+
sandboxMode: createdSession?.codexSettings?.sandboxMode
|
|
1352
|
+
? sandboxModeToExternal(createdSession.codexSettings.sandboxMode)
|
|
1353
|
+
: undefined,
|
|
1354
|
+
permissionMode: legacyPermissionMode,
|
|
1355
|
+
executionMode,
|
|
1356
|
+
planMode,
|
|
1357
|
+
}));
|
|
1159
1358
|
this.broadcastSessionList();
|
|
1160
1359
|
});
|
|
1161
1360
|
this.debugEvents.set(sessionId, []);
|
|
@@ -1192,7 +1391,7 @@ export class BridgeWebSocketServer {
|
|
|
1192
1391
|
getSessionHistory(claudeSessionId).then((pastMessages) => {
|
|
1193
1392
|
const sessionId = this.sessionManager.create(msg.projectPath, {
|
|
1194
1393
|
sessionId: claudeSessionId,
|
|
1195
|
-
permissionMode:
|
|
1394
|
+
permissionMode: legacyPermissionMode,
|
|
1196
1395
|
model: msg.model,
|
|
1197
1396
|
effort: msg.effort,
|
|
1198
1397
|
maxTurns: msg.maxTurns,
|
|
@@ -1205,19 +1404,26 @@ export class BridgeWebSocketServer {
|
|
|
1205
1404
|
const createdSession = this.sessionManager.get(sessionId);
|
|
1206
1405
|
void this.loadAndSetSessionName(createdSession, "claude", msg.projectPath, claudeSessionId).then(() => {
|
|
1207
1406
|
this.send(ws, {
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1407
|
+
...this.buildSessionCreatedMessage({
|
|
1408
|
+
sessionId,
|
|
1409
|
+
provider: "claude",
|
|
1410
|
+
projectPath: msg.projectPath,
|
|
1411
|
+
session: createdSession,
|
|
1412
|
+
permissionMode: legacyPermissionMode,
|
|
1413
|
+
executionMode,
|
|
1414
|
+
planMode,
|
|
1415
|
+
sandboxMode: msg.sandboxMode,
|
|
1416
|
+
...(cached
|
|
1417
|
+
? {
|
|
1418
|
+
slashCommands: cached.slashCommands,
|
|
1419
|
+
skills: cached.skills,
|
|
1420
|
+
...(cached.skillMetadata
|
|
1421
|
+
? { skillMetadata: cached.skillMetadata }
|
|
1422
|
+
: {}),
|
|
1423
|
+
}
|
|
1424
|
+
: {}),
|
|
1425
|
+
}),
|
|
1211
1426
|
claudeSessionId,
|
|
1212
|
-
provider: "claude",
|
|
1213
|
-
projectPath: msg.projectPath,
|
|
1214
|
-
...(msg.permissionMode ? { permissionMode: msg.permissionMode } : {}),
|
|
1215
|
-
...(msg.sandboxMode ? { sandboxMode: msg.sandboxMode } : {}),
|
|
1216
|
-
...(cached ? { slashCommands: cached.slashCommands, skills: cached.skills, ...(cached.skillMetadata ? { skillMetadata: cached.skillMetadata } : {}) } : {}),
|
|
1217
|
-
...(createdSession?.worktreePath ? {
|
|
1218
|
-
worktreePath: createdSession.worktreePath,
|
|
1219
|
-
worktreeBranch: createdSession.worktreeBranch,
|
|
1220
|
-
} : {}),
|
|
1221
1427
|
});
|
|
1222
1428
|
this.broadcastSessionList();
|
|
1223
1429
|
});
|
|
@@ -1527,15 +1733,14 @@ export class BridgeWebSocketServer {
|
|
|
1527
1733
|
// Notify the new session ID
|
|
1528
1734
|
const newSession = this.sessionManager.get(newSessionId);
|
|
1529
1735
|
const rewindPermMode = newSession?.process instanceof SdkProcess ? newSession.process.permissionMode : undefined;
|
|
1530
|
-
this.send(ws, {
|
|
1531
|
-
type: "system",
|
|
1532
|
-
subtype: "session_created",
|
|
1736
|
+
this.send(ws, this.buildSessionCreatedMessage({
|
|
1533
1737
|
sessionId: newSessionId,
|
|
1534
1738
|
provider: newSession?.provider ?? "claude",
|
|
1535
1739
|
projectPath: newSession?.projectPath ?? "",
|
|
1536
|
-
|
|
1740
|
+
session: newSession,
|
|
1741
|
+
permissionMode: rewindPermMode,
|
|
1537
1742
|
sourceSessionId: msg.sessionId,
|
|
1538
|
-
});
|
|
1743
|
+
}));
|
|
1539
1744
|
this.sendSessionList(ws);
|
|
1540
1745
|
});
|
|
1541
1746
|
}
|
|
@@ -1555,15 +1760,14 @@ export class BridgeWebSocketServer {
|
|
|
1555
1760
|
this.send(ws, { type: "rewind_result", success: true, mode: "both" });
|
|
1556
1761
|
const newSession = this.sessionManager.get(newSessionId);
|
|
1557
1762
|
const rewindPermMode2 = newSession?.process instanceof SdkProcess ? newSession.process.permissionMode : undefined;
|
|
1558
|
-
this.send(ws, {
|
|
1559
|
-
type: "system",
|
|
1560
|
-
subtype: "session_created",
|
|
1763
|
+
this.send(ws, this.buildSessionCreatedMessage({
|
|
1561
1764
|
sessionId: newSessionId,
|
|
1562
1765
|
provider: newSession?.provider ?? "claude",
|
|
1563
1766
|
projectPath: newSession?.projectPath ?? "",
|
|
1564
|
-
|
|
1767
|
+
session: newSession,
|
|
1768
|
+
permissionMode: rewindPermMode2,
|
|
1565
1769
|
sourceSessionId: msg.sessionId,
|
|
1566
|
-
});
|
|
1770
|
+
}));
|
|
1567
1771
|
this.sendSessionList(ws);
|
|
1568
1772
|
});
|
|
1569
1773
|
}
|