synabun 2026.4.21 → 2026.4.25-beta.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/CHANGELOG.md +161 -881
- package/README.md +1 -1
- package/hooks/claude-code/prompt-submit.mjs +39 -18
- package/hooks/claude-code/session-start.mjs +10 -3
- package/hooks/claude-code/shared.mjs +39 -2
- package/hooks/claude-code/stop.mjs +33 -6
- package/mcp-server/src/index.ts +9 -1
- package/mcp-server/src/services/neural-interface.ts +42 -14
- package/mcp-server/src/services/profiles.ts +8 -7
- package/mcp-server/src/tools/browser-advanced.ts +12 -10
- package/mcp-server/src/tools/browser-interact.ts +39 -38
- package/mcp-server/src/tools/browser-navigate.ts +12 -4
- package/mcp-server/src/tools/loop.ts +7 -3
- package/mcp-server/src/tools/profile.ts +19 -1
- package/neural-interface/lib/loop-logger.js +103 -0
- package/neural-interface/lib/mcp-installer.js +109 -0
- package/neural-interface/lib/plugin-installer.js +264 -0
- package/neural-interface/package.json +1 -1
- package/neural-interface/public/shared/api.js +66 -17
- package/neural-interface/public/shared/cdx/cdx-icons.js +5 -4
- package/neural-interface/public/shared/cdx/cdx-panel.js +44 -14
- package/neural-interface/public/shared/cdx/cdx-render.js +170 -43
- package/neural-interface/public/shared/cdx/cdx-styles.js +23 -10
- package/neural-interface/public/shared/cdx/cdx-tabs.js +384 -70
- package/neural-interface/public/shared/constants.js +3 -2
- package/neural-interface/public/shared/html-shell.js +32 -11
- package/neural-interface/public/shared/ocp/ocp-panel.js +45 -28
- package/neural-interface/public/shared/ocp/ocp-render.js +50 -43
- package/neural-interface/public/shared/ocp/ocp-tabs.js +693 -82
- package/neural-interface/public/shared/styles.css +635 -0
- package/neural-interface/public/shared/ui-automation-studio.js +38 -12
- package/neural-interface/public/shared/ui-claude-panel.js +216 -21
- package/neural-interface/public/shared/ui-file-explorer.js +38 -18
- package/neural-interface/public/shared/ui-menubar.js +34 -10
- package/neural-interface/public/shared/ui-navbar.js +8 -6
- package/neural-interface/public/shared/ui-schedules-studio.js +1464 -0
- package/neural-interface/public/shared/ui-settings.js +274 -50
- package/neural-interface/public/shared/ui-sidepanel-layout.js +18 -7
- package/neural-interface/public/shared/ui-sidepanel-tray.js +287 -0
- package/neural-interface/public/shared/ui-sync.js +9 -5
- package/neural-interface/public/shared/ui-terminal.js +76 -27
- package/neural-interface/public/shared/ui-tool-updates.js +49 -15
- package/neural-interface/public/shared/ui-tutorial-steps.js +4 -2
- package/neural-interface/public/shared/ui-tutorial.js +24 -18
- package/neural-interface/public/shared/ui-whiteboard.js +79 -31
- package/neural-interface/public/shared/vterm-buffer.js +9 -8
- package/neural-interface/public/variant/2d/main.js +9 -7
- package/neural-interface/public/variant/3d/main.js +10 -8
- package/neural-interface/server.js +1281 -252
- package/package.json +1 -1
- package/skills/leonardo/SKILL.md +5 -1
- package/skills/leonardo/modules/image-prompter.md +5 -1
- package/skills/leonardo/modules/video-prompter.md +5 -1
- package/skills/synabun/SKILL.md +44 -26
- package/skills/synabun/modules/audit.md +40 -36
- package/skills/synabun/modules/changelog.md +22 -18
- package/skills/synabun/modules/memorize.md +19 -15
- package/skills/synabun/modules/schedule.md +285 -0
- package/CLAUDE.md +0 -217
|
@@ -9,7 +9,7 @@ import { isClaudePanelOpen, toggleClaudePanel } from '../ui-claude-panel.js';
|
|
|
9
9
|
import { reserveRightPanelLayout, clearRightPanelLayout } from '../ui-sidepanel-layout.js';
|
|
10
10
|
import { notify, NOTIF_TYPE } from '../ui-notifications.js';
|
|
11
11
|
import {
|
|
12
|
-
STOR, EFFORT_LEVELS,
|
|
12
|
+
STOR, EFFORT_LEVELS, MAX_THREAD_SNAPSHOTS, MAX_THREAD_SNAPSHOT_CHARS,
|
|
13
13
|
CODEX_GREETING_MARKER_START, CODEX_GREETING_MARKER_END, CODEX_PLAN_MODE_PREFIXES,
|
|
14
14
|
STATUS_TONE, PANEL_OWNER, windowId, STALL_WARN_MS, STALL_KILL_MS,
|
|
15
15
|
OPENAI_ICON, ICON_SPARK, ICON_TERMINAL, ICON_TOOL, ICON_FILES, ICON_PLUS,
|
|
@@ -73,7 +73,12 @@ function scrollEnd() { _onScrollEnd?.(); }
|
|
|
73
73
|
function autosizeInput() { _onAutosizeInput?.(); }
|
|
74
74
|
function emit(...args) { _onEmit?.(...args); }
|
|
75
75
|
|
|
76
|
-
function sendSocket(msg) {
|
|
76
|
+
function sendSocket(msg) {
|
|
77
|
+
const packet = msg && _boundTab?.id && !msg.sessionId
|
|
78
|
+
? { sessionId: _boundTab.id, ...msg }
|
|
79
|
+
: msg;
|
|
80
|
+
return wsSendSocket(packet, _ws);
|
|
81
|
+
}
|
|
77
82
|
|
|
78
83
|
// ── connectTab / disconnectTab wrappers (build callbacks for cdx-ws) ──
|
|
79
84
|
|
|
@@ -111,8 +116,14 @@ function buildWsCallbacks(tab) {
|
|
|
111
116
|
onClose(tab, ws, { intentional }) {
|
|
112
117
|
_connected = false;
|
|
113
118
|
_bootstrapped = false;
|
|
119
|
+
_pendingReattach = !intentional;
|
|
114
120
|
_ws = null;
|
|
115
|
-
if (_boundTab) {
|
|
121
|
+
if (_boundTab) {
|
|
122
|
+
_boundTab.connected = false;
|
|
123
|
+
_boundTab.bootstrapped = false;
|
|
124
|
+
_boundTab.pendingReattach = _pendingReattach;
|
|
125
|
+
_boundTab.ws = null;
|
|
126
|
+
}
|
|
116
127
|
syncInputEnabled();
|
|
117
128
|
syncSessionControls();
|
|
118
129
|
if (!intentional && !tab.closed) {
|
|
@@ -124,6 +135,8 @@ function buildWsCallbacks(tab) {
|
|
|
124
135
|
},
|
|
125
136
|
|
|
126
137
|
onReattachResult(msg) {
|
|
138
|
+
_pendingReattach = false;
|
|
139
|
+
if (_boundTab) _boundTab.pendingReattach = false;
|
|
127
140
|
if (msg.ok) {
|
|
128
141
|
_threadId = msg.threadId || _threadId;
|
|
129
142
|
if (_boundTab) _boundTab.threadId = _threadId;
|
|
@@ -244,7 +257,14 @@ function buildWsCallbacks(tab) {
|
|
|
244
257
|
onInterruptAck(msg) {
|
|
245
258
|
_running = false;
|
|
246
259
|
_activeTurnId = null;
|
|
247
|
-
if (_boundTab) {
|
|
260
|
+
if (_boundTab) {
|
|
261
|
+
_boundTab.running = false;
|
|
262
|
+
_boundTab.activeTurnId = null;
|
|
263
|
+
if (_boundTab._abortRetry) {
|
|
264
|
+
clearTimeout(_boundTab._abortRetry);
|
|
265
|
+
_boundTab._abortRetry = null;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
248
268
|
clearInterruptTimer();
|
|
249
269
|
stopStallTimer();
|
|
250
270
|
setStatus('Interrupted', 'muted');
|
|
@@ -265,7 +285,7 @@ function buildWsCallbacks(tab) {
|
|
|
265
285
|
if (window.__cxpModelListResolve) {
|
|
266
286
|
window.__cxpModelListResolve(models);
|
|
267
287
|
} else {
|
|
268
|
-
populateModelDropdown(models
|
|
288
|
+
populateModelDropdown(mergeCodexModelList(models));
|
|
269
289
|
}
|
|
270
290
|
},
|
|
271
291
|
|
|
@@ -355,6 +375,9 @@ let _planContent = '';
|
|
|
355
375
|
let _editedPlanContent = '';
|
|
356
376
|
let _showPostPlanActions = false;
|
|
357
377
|
let _postPlanHeader = 'PLAN COMPLETE';
|
|
378
|
+
let _planTurnActive = false;
|
|
379
|
+
let _planApprovalPending = false;
|
|
380
|
+
let _lastPlanTurnId = '';
|
|
358
381
|
let _showPostCompactionPrompt = false;
|
|
359
382
|
let _postCompactionPending = false;
|
|
360
383
|
let _stallTimer = null;
|
|
@@ -746,6 +769,12 @@ export function flushThreadSnapshotSave(tab = _boundTab) {
|
|
|
746
769
|
writeThreadSnapshot(tab);
|
|
747
770
|
}
|
|
748
771
|
|
|
772
|
+
export function flushAllThreadSnapshots() {
|
|
773
|
+
for (const tab of _tabs) {
|
|
774
|
+
try { flushThreadSnapshotSave(tab); } catch {}
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
749
778
|
function normalizeTokenUsageBreakdown(value) {
|
|
750
779
|
if (!value || typeof value !== 'object') return null;
|
|
751
780
|
return {
|
|
@@ -822,18 +851,65 @@ let _hljs = null;
|
|
|
822
851
|
|
|
823
852
|
|
|
824
853
|
// ── Plan state functions (lines 886-938) ──
|
|
854
|
+
function isPlanModePromptText(text) {
|
|
855
|
+
const value = String(text || '').trim();
|
|
856
|
+
if (!value) return false;
|
|
857
|
+
return /^\[PLAN MODE\b/i.test(value)
|
|
858
|
+
|| CODEX_PLAN_MODE_PREFIXES.some((prefix) => value.startsWith(prefix));
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
function syncPlanHandoffFlags(tab = _boundTab) {
|
|
862
|
+
if (!tab || !isActiveTab(tab)) return;
|
|
863
|
+
_planTurnActive = !!tab.planTurnActive;
|
|
864
|
+
_planApprovalPending = !!tab.planApprovalPending;
|
|
865
|
+
_lastPlanTurnId = tab.lastPlanTurnId || '';
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
function setPlanTurnActive(tab = _boundTab, value, turnId = '') {
|
|
869
|
+
if (!tab) return;
|
|
870
|
+
tab.planTurnActive = !!value;
|
|
871
|
+
if (turnId !== undefined) tab.lastPlanTurnId = turnId || '';
|
|
872
|
+
syncPlanHandoffFlags(tab);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
function setPlanApprovalPending(tab = _boundTab, value) {
|
|
876
|
+
if (!tab) return;
|
|
877
|
+
tab.planApprovalPending = !!value;
|
|
878
|
+
syncPlanHandoffFlags(tab);
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
function shouldHoldForPlanApproval(tab = activeTab()) {
|
|
882
|
+
return !!(tab?.showPostPlanActions || tab?.planApprovalPending);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
function captureCompletedPlanItem(tab = _boundTab, item = null) {
|
|
886
|
+
if (!tab || !tab.planTurnActive || !item) return '';
|
|
887
|
+
const itemType = item.type || '';
|
|
888
|
+
if (itemType !== 'plan' && itemType !== 'agentMessage') return '';
|
|
889
|
+
const state = item.id ? tab.items?.get?.(item.id) : null;
|
|
890
|
+
const text = String(state?.buffer || item.text || '').trim();
|
|
891
|
+
if (text.length <= 80) return '';
|
|
892
|
+
return capturePlanContent(tab, text);
|
|
893
|
+
}
|
|
894
|
+
|
|
825
895
|
export function clearPlanHandoffState(tab = _boundTab, { keepFilePath = false } = {}) {
|
|
826
896
|
if (!tab) return;
|
|
827
897
|
tab.planContent = '';
|
|
828
898
|
tab.editedPlanContent = '';
|
|
829
899
|
tab.showPostPlanActions = false;
|
|
830
900
|
tab.postPlanHeader = 'PLAN COMPLETE';
|
|
901
|
+
tab.planTurnActive = false;
|
|
902
|
+
tab.planApprovalPending = false;
|
|
903
|
+
tab.lastPlanTurnId = '';
|
|
831
904
|
if (!keepFilePath) tab.planFilePath = '';
|
|
832
905
|
if (isActiveTab(tab)) {
|
|
833
906
|
_planContent = '';
|
|
834
907
|
_editedPlanContent = '';
|
|
835
908
|
_showPostPlanActions = false;
|
|
836
909
|
_postPlanHeader = 'PLAN COMPLETE';
|
|
910
|
+
_planTurnActive = false;
|
|
911
|
+
_planApprovalPending = false;
|
|
912
|
+
_lastPlanTurnId = '';
|
|
837
913
|
if (!keepFilePath) _planFilePath = '';
|
|
838
914
|
}
|
|
839
915
|
removePostPlanCards(tab.messagesEl);
|
|
@@ -914,6 +990,39 @@ export function capturePlanContent(tab = _boundTab, explicitText = '') {
|
|
|
914
990
|
return next;
|
|
915
991
|
}
|
|
916
992
|
|
|
993
|
+
export async function ensurePlanFile(tab = _boundTab) {
|
|
994
|
+
if (!tab) return '';
|
|
995
|
+
if (tab.planFilePath) return tab.planFilePath;
|
|
996
|
+
if (tab._planFilePromise) return tab._planFilePromise;
|
|
997
|
+
const planText = capturePlanContent(tab);
|
|
998
|
+
if (!planText) return '';
|
|
999
|
+
|
|
1000
|
+
tab._planFilePromise = fetch('/api/create-plan', {
|
|
1001
|
+
method: 'POST',
|
|
1002
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1003
|
+
body: JSON.stringify({ content: planText, cwd: tab.project || _project || '' }),
|
|
1004
|
+
})
|
|
1005
|
+
.then(async (res) => {
|
|
1006
|
+
const result = await res.json().catch(() => ({}));
|
|
1007
|
+
if (!res.ok || !result?.ok || !result.path) {
|
|
1008
|
+
throw new Error(result?.error || 'create-plan failed');
|
|
1009
|
+
}
|
|
1010
|
+
tab.planFilePath = result.path;
|
|
1011
|
+
if (isActiveTab(tab)) _planFilePath = result.path;
|
|
1012
|
+
saveTabs();
|
|
1013
|
+
return result.path;
|
|
1014
|
+
})
|
|
1015
|
+
.catch((err) => {
|
|
1016
|
+
console.warn('[codex-panel] create-plan failed:', err);
|
|
1017
|
+
return '';
|
|
1018
|
+
})
|
|
1019
|
+
.finally(() => {
|
|
1020
|
+
tab._planFilePromise = null;
|
|
1021
|
+
});
|
|
1022
|
+
|
|
1023
|
+
return tab._planFilePromise;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
917
1026
|
// ── Formatting & helpers (lines 1148-1421) ──
|
|
918
1027
|
export function formatStatus(status) {
|
|
919
1028
|
if (!status) return 'pending';
|
|
@@ -966,6 +1075,7 @@ function formatContextTrackerState(meta) {
|
|
|
966
1075
|
}
|
|
967
1076
|
|
|
968
1077
|
const CODEX_MODEL_PRICING = {
|
|
1078
|
+
'gpt-5.5': { input: 5.00, cachedInput: 5.00, output: 30.00 },
|
|
969
1079
|
'gpt-5.4': { input: 2.50, cachedInput: 0.25, output: 15.00 },
|
|
970
1080
|
'gpt-5.4-mini': { input: 0.75, cachedInput: 0.075, output: 4.50 },
|
|
971
1081
|
};
|
|
@@ -1000,10 +1110,15 @@ function itemHeadline(item) {
|
|
|
1000
1110
|
: 'Preparing file changes',
|
|
1001
1111
|
};
|
|
1002
1112
|
case 'reasoning':
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1113
|
+
{
|
|
1114
|
+
const summaryText = Array.isArray(item.summary)
|
|
1115
|
+
? item.summary.map((part) => typeof part === 'string' ? part : (part?.text || part?.summaryText || '')).filter(Boolean).join(' ')
|
|
1116
|
+
: (item.summaryText || item.summary_text || item.text || '');
|
|
1117
|
+
return {
|
|
1118
|
+
title: 'Reasoning summary',
|
|
1119
|
+
detail: cleanPreview(summaryText || 'Working through the next step'),
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1007
1122
|
case 'plan':
|
|
1008
1123
|
return {
|
|
1009
1124
|
title: 'Updating plan',
|
|
@@ -1269,6 +1384,9 @@ export function snapshotBoundState() {
|
|
|
1269
1384
|
editedPlanContent: _editedPlanContent,
|
|
1270
1385
|
showPostPlanActions: _showPostPlanActions,
|
|
1271
1386
|
postPlanHeader: _postPlanHeader,
|
|
1387
|
+
planTurnActive: _planTurnActive,
|
|
1388
|
+
planApprovalPending: _planApprovalPending,
|
|
1389
|
+
lastPlanTurnId: _lastPlanTurnId,
|
|
1272
1390
|
showPostCompactionPrompt: _showPostCompactionPrompt,
|
|
1273
1391
|
postCompactionPending: _postCompactionPending,
|
|
1274
1392
|
blockingServerRequests: _blockingServerRequests,
|
|
@@ -1319,6 +1437,9 @@ export function restoreBoundState(state) {
|
|
|
1319
1437
|
_editedPlanContent = state.editedPlanContent || '';
|
|
1320
1438
|
_showPostPlanActions = !!state.showPostPlanActions;
|
|
1321
1439
|
_postPlanHeader = state.postPlanHeader || 'PLAN COMPLETE';
|
|
1440
|
+
_planTurnActive = !!state.planTurnActive;
|
|
1441
|
+
_planApprovalPending = !!state.planApprovalPending;
|
|
1442
|
+
_lastPlanTurnId = state.lastPlanTurnId || '';
|
|
1322
1443
|
_showPostCompactionPrompt = !!state.showPostCompactionPrompt;
|
|
1323
1444
|
_postCompactionPending = !!state.postCompactionPending;
|
|
1324
1445
|
_blockingServerRequests = state.blockingServerRequests || new Map();
|
|
@@ -1368,6 +1489,9 @@ export function commitBoundState() {
|
|
|
1368
1489
|
_boundTab.editedPlanContent = _editedPlanContent;
|
|
1369
1490
|
_boundTab.showPostPlanActions = _showPostPlanActions;
|
|
1370
1491
|
_boundTab.postPlanHeader = _postPlanHeader;
|
|
1492
|
+
_boundTab.planTurnActive = _planTurnActive;
|
|
1493
|
+
_boundTab.planApprovalPending = _planApprovalPending;
|
|
1494
|
+
_boundTab.lastPlanTurnId = _lastPlanTurnId;
|
|
1371
1495
|
_boundTab.showPostCompactionPrompt = _showPostCompactionPrompt;
|
|
1372
1496
|
_boundTab.postCompactionPending = _postCompactionPending;
|
|
1373
1497
|
_boundTab.blockingServerRequests = _blockingServerRequests;
|
|
@@ -1417,6 +1541,9 @@ export function bindTabState(tab) {
|
|
|
1417
1541
|
_editedPlanContent = tab?.editedPlanContent || '';
|
|
1418
1542
|
_showPostPlanActions = !!tab?.showPostPlanActions;
|
|
1419
1543
|
_postPlanHeader = tab?.postPlanHeader || 'PLAN COMPLETE';
|
|
1544
|
+
_planTurnActive = !!tab?.planTurnActive;
|
|
1545
|
+
_planApprovalPending = !!tab?.planApprovalPending;
|
|
1546
|
+
_lastPlanTurnId = tab?.lastPlanTurnId || '';
|
|
1420
1547
|
_showPostCompactionPrompt = !!tab?.showPostCompactionPrompt;
|
|
1421
1548
|
_postCompactionPending = !!tab?.postCompactionPending;
|
|
1422
1549
|
_blockingServerRequests = tab?.blockingServerRequests || new Map();
|
|
@@ -1484,6 +1611,7 @@ function hasMeaningfulTrayState(tab) {
|
|
|
1484
1611
|
const draft = normalizeSessionLabel(tab.draft || '');
|
|
1485
1612
|
return !!(
|
|
1486
1613
|
tab.threadId
|
|
1614
|
+
|| hasCustomSessionLabel(tab)
|
|
1487
1615
|
|| tab.running
|
|
1488
1616
|
|| tab.startingThread
|
|
1489
1617
|
|| draft
|
|
@@ -1493,6 +1621,76 @@ function hasMeaningfulTrayState(tab) {
|
|
|
1493
1621
|
);
|
|
1494
1622
|
}
|
|
1495
1623
|
|
|
1624
|
+
function hasCustomSessionLabel(tab) {
|
|
1625
|
+
const label = normalizeSessionLabel(tab?.pendingSessionLabel || tab?.sessionLabel || '');
|
|
1626
|
+
if (!label) return false;
|
|
1627
|
+
return !['new session', 'saved session', 'untitled session', 'codex'].includes(label.toLowerCase());
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
function hasMeaningfulTabState(tab) {
|
|
1631
|
+
if (!tab) return false;
|
|
1632
|
+
return !!(
|
|
1633
|
+
hasMeaningfulTrayState(tab)
|
|
1634
|
+
|| tab.planContent
|
|
1635
|
+
|| tab.editedPlanContent
|
|
1636
|
+
|| tab.showPostPlanActions
|
|
1637
|
+
|| tab.planApprovalPending
|
|
1638
|
+
|| tab.postCompactionPending
|
|
1639
|
+
|| tab.items?.size
|
|
1640
|
+
|| tab.activeItems?.size
|
|
1641
|
+
|| tab.blockingServerRequests?.size
|
|
1642
|
+
);
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
function isDiscardableBlankTab(tab) {
|
|
1646
|
+
return !!tab && !hasMeaningfulTabState(tab);
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
function disposeDiscardedTab(tab) {
|
|
1650
|
+
if (!tab) return;
|
|
1651
|
+
flushThreadSnapshotSave(tab);
|
|
1652
|
+
disconnectTab(tab);
|
|
1653
|
+
tab.closed = true;
|
|
1654
|
+
if (tab.snapshotTimer) {
|
|
1655
|
+
clearTimeout(tab.snapshotTimer);
|
|
1656
|
+
tab.snapshotTimer = null;
|
|
1657
|
+
}
|
|
1658
|
+
tab.messagesEl?.remove();
|
|
1659
|
+
tab.pillEl?.remove();
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
function pruneDiscardableTabs({ keepTab = activeTab(), persist = false } = {}) {
|
|
1663
|
+
// Blank tabs are provisional composer state; inactive blanks have no tray affordance.
|
|
1664
|
+
let pruned = false;
|
|
1665
|
+
for (let idx = _tabs.length - 1; idx >= 0; idx -= 1) {
|
|
1666
|
+
const tab = _tabs[idx];
|
|
1667
|
+
if (tab === keepTab || !isDiscardableBlankTab(tab)) continue;
|
|
1668
|
+
disposeDiscardedTab(tab);
|
|
1669
|
+
_tabs.splice(idx, 1);
|
|
1670
|
+
if (_activeTabIdx > idx) _activeTabIdx -= 1;
|
|
1671
|
+
else if (_activeTabIdx >= _tabs.length) _activeTabIdx = _tabs.length - 1;
|
|
1672
|
+
pruned = true;
|
|
1673
|
+
}
|
|
1674
|
+
if (_activeTabIdx < 0 && _tabs.length) _activeTabIdx = 0;
|
|
1675
|
+
if (pruned) {
|
|
1676
|
+
renderPills();
|
|
1677
|
+
if (persist) saveTabs();
|
|
1678
|
+
}
|
|
1679
|
+
return pruned;
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
function isBlankTabRequest(saved = {}) {
|
|
1683
|
+
return !saved.threadId
|
|
1684
|
+
&& !normalizeSessionLabel(saved.title || '')
|
|
1685
|
+
&& !normalizeSessionLabel(saved.draft || '')
|
|
1686
|
+
&& !saved.planContent
|
|
1687
|
+
&& !saved.editedPlanContent
|
|
1688
|
+
&& !saved.showPostPlanActions
|
|
1689
|
+
&& !saved.planApprovalPending
|
|
1690
|
+
&& !saved.showPostCompactionPrompt
|
|
1691
|
+
&& !saved.postCompactionPending;
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1496
1694
|
function createTrayPill(tab) {
|
|
1497
1695
|
const tray = document.getElementById('term-minimized-tray');
|
|
1498
1696
|
if (!tray) return null;
|
|
@@ -1588,7 +1786,10 @@ export function switchTab(idx) {
|
|
|
1588
1786
|
}
|
|
1589
1787
|
|
|
1590
1788
|
export function createTab(saved = {}, { autoSwitch = true } = {}) {
|
|
1591
|
-
if (
|
|
1789
|
+
if (autoSwitch && isBlankTabRequest(saved) && isDiscardableBlankTab(activeTab())) {
|
|
1790
|
+
return activeTab();
|
|
1791
|
+
}
|
|
1792
|
+
pruneDiscardableTabs({ keepTab: activeTab(), persist: false });
|
|
1592
1793
|
const inheritedProject = saved.project ?? activeTab()?.project ?? (storage.getItem(STOR.project) || '');
|
|
1593
1794
|
const threadId = saved.threadId || null;
|
|
1594
1795
|
const title = normalizeSessionLabel(saved.title || '');
|
|
@@ -1653,6 +1854,9 @@ export function createTab(saved = {}, { autoSwitch = true } = {}) {
|
|
|
1653
1854
|
editedPlanContent: saved.editedPlanContent || '',
|
|
1654
1855
|
showPostPlanActions: !!saved.showPostPlanActions,
|
|
1655
1856
|
postPlanHeader: saved.postPlanHeader || 'PLAN COMPLETE',
|
|
1857
|
+
planTurnActive: !!saved.planTurnActive,
|
|
1858
|
+
planApprovalPending: !!saved.planApprovalPending,
|
|
1859
|
+
lastPlanTurnId: saved.lastPlanTurnId || '',
|
|
1656
1860
|
pendingSessionLabel: saved.pendingSessionLabel || null, // user-named before thread exists; sent via thread_rename once ready
|
|
1657
1861
|
showPostCompactionPrompt: !!saved.showPostCompactionPrompt,
|
|
1658
1862
|
postCompactionPending: !!saved.postCompactionPending,
|
|
@@ -1736,6 +1940,7 @@ export function closeTab(idx) {
|
|
|
1736
1940
|
export function saveTabs() {
|
|
1737
1941
|
try {
|
|
1738
1942
|
commitBoundState();
|
|
1943
|
+
pruneDiscardableTabs({ keepTab: activeTab(), persist: false });
|
|
1739
1944
|
const payload = JSON.stringify({
|
|
1740
1945
|
tabs: _tabs.map((tab) => {
|
|
1741
1946
|
const persistedTokenUsage = normalizeThreadTokenUsage(tab.threadTokenUsage);
|
|
@@ -1760,6 +1965,9 @@ export function saveTabs() {
|
|
|
1760
1965
|
editedPlanContent: tab.editedPlanContent || '',
|
|
1761
1966
|
showPostPlanActions: !!tab.showPostPlanActions,
|
|
1762
1967
|
postPlanHeader: tab.postPlanHeader || 'PLAN COMPLETE',
|
|
1968
|
+
planTurnActive: !!tab.planTurnActive,
|
|
1969
|
+
planApprovalPending: !!tab.planApprovalPending,
|
|
1970
|
+
lastPlanTurnId: tab.lastPlanTurnId || '',
|
|
1763
1971
|
showPostCompactionPrompt: !!tab.showPostCompactionPrompt,
|
|
1764
1972
|
postCompactionPending: !!tab.postCompactionPending,
|
|
1765
1973
|
};
|
|
@@ -1779,6 +1987,8 @@ export function restoreTabs() {
|
|
|
1779
1987
|
if (Array.isArray(data?.tabs) && data.tabs.length) {
|
|
1780
1988
|
for (const saved of data.tabs) createTab(saved, { autoSwitch: false });
|
|
1781
1989
|
_activeTabIdx = Math.min(Math.max(Number(data.activeIdx) || 0, 0), _tabs.length - 1);
|
|
1990
|
+
pruneDiscardableTabs({ keepTab: activeTab(), persist: false });
|
|
1991
|
+
if (!_tabs.length) throw new Error('No restorable Codex tabs');
|
|
1782
1992
|
updateActiveTabView();
|
|
1783
1993
|
saveTabs();
|
|
1784
1994
|
return;
|
|
@@ -3062,14 +3272,16 @@ export function syncInputEnabled() {
|
|
|
3062
3272
|
const blockedByRequest = hasBlockingServerRequests();
|
|
3063
3273
|
if (input) input.disabled = !_connected || !_bootstrapped || _startingThread || blockedByRequest;
|
|
3064
3274
|
if (send) {
|
|
3065
|
-
|
|
3066
|
-
// abort the whole panel regardless of which tab is focused.
|
|
3067
|
-
const isStopMode = _running || _tabs.some((t) => t?.running);
|
|
3275
|
+
const isStopMode = !!(tab?.running || _running);
|
|
3068
3276
|
send.classList.toggle('cxp-send-stopping', !!isStopMode);
|
|
3069
3277
|
send.title = isStopMode ? 'Stop' : 'Send';
|
|
3070
3278
|
send.disabled = isStopMode ? false : (!_connected || !_bootstrapped || _running || _startingThread || blockedByRequest || (!hasText && !hasImages && !hasPaths));
|
|
3071
3279
|
}
|
|
3072
|
-
if (fresh)
|
|
3280
|
+
if (fresh) {
|
|
3281
|
+
pruneDiscardableTabs({ keepTab: tab, persist: true });
|
|
3282
|
+
fresh.disabled = false;
|
|
3283
|
+
fresh.title = 'New Codex tab';
|
|
3284
|
+
}
|
|
3073
3285
|
if (close) close.disabled = false;
|
|
3074
3286
|
if (compact) {
|
|
3075
3287
|
compact.disabled = !_connected || !_bootstrapped || !_threadId || _running || _startingThread || _compacting;
|
|
@@ -3108,7 +3320,9 @@ export function dispatchPrompt(prompt, { tab = activeTab(), images = null, paths
|
|
|
3108
3320
|
finalPrompt += `${finalPrompt ? '\n\n' : ''}Referenced files:\n${pathList.map((path) => `- ${path}`).join('\n')}`;
|
|
3109
3321
|
}
|
|
3110
3322
|
|
|
3323
|
+
const explicitPlanPrompt = isPlanModePromptText(finalPrompt);
|
|
3111
3324
|
const usePlanModePrefix = forcePlanModePrefix == null ? !!tab?.planMode : !!forcePlanModePrefix;
|
|
3325
|
+
const sentAsPlanMode = !!usePlanModePrefix || explicitPlanPrompt;
|
|
3112
3326
|
if (usePlanModePrefix) {
|
|
3113
3327
|
finalPrompt = `${CODEX_PLAN_MODE_PREFIXES[0]}\n\n${finalPrompt}`;
|
|
3114
3328
|
}
|
|
@@ -3117,6 +3331,8 @@ export function dispatchPrompt(prompt, { tab = activeTab(), images = null, paths
|
|
|
3117
3331
|
type: 'query',
|
|
3118
3332
|
requestId: crypto.randomUUID(),
|
|
3119
3333
|
prompt: finalPrompt,
|
|
3334
|
+
planMode: sentAsPlanMode,
|
|
3335
|
+
autoAccept: !!tab?.autoAccept,
|
|
3120
3336
|
threadId: _threadId || null,
|
|
3121
3337
|
fresh: _freshThread || !_threadId,
|
|
3122
3338
|
cwd: _project || null,
|
|
@@ -3133,6 +3349,16 @@ export function dispatchPrompt(prompt, { tab = activeTab(), images = null, paths
|
|
|
3133
3349
|
setStatus('Codex is not connected', 'error');
|
|
3134
3350
|
return false;
|
|
3135
3351
|
}
|
|
3352
|
+
if (tab) {
|
|
3353
|
+
tab.planTurnActive = sentAsPlanMode;
|
|
3354
|
+
tab.planApprovalPending = false;
|
|
3355
|
+
tab.lastPlanTurnId = msg.requestId;
|
|
3356
|
+
if (isActiveTab(tab)) {
|
|
3357
|
+
_planTurnActive = sentAsPlanMode;
|
|
3358
|
+
_planApprovalPending = false;
|
|
3359
|
+
_lastPlanTurnId = msg.requestId;
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3136
3362
|
clearPostCompactionState(tab, { updateTranscript: !_freshThread });
|
|
3137
3363
|
if (_freshThread) clearTranscript('Starting fresh Codex thread…');
|
|
3138
3364
|
_freshThread = false;
|
|
@@ -3194,29 +3420,47 @@ export function handleNotify(method, params) {
|
|
|
3194
3420
|
break;
|
|
3195
3421
|
case 'turn/completed':
|
|
3196
3422
|
if (_compacting) setCompactingUI(false);
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
_boundTab.
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3423
|
+
{
|
|
3424
|
+
const tab = _boundTab;
|
|
3425
|
+
const wasPlanTurn = !!(tab?.planTurnActive || _planTurnActive || tab?.planMode);
|
|
3426
|
+
setRunning(false);
|
|
3427
|
+
if (params.turn?.error?.message) {
|
|
3428
|
+
setPlanTurnActive(_boundTab, false, '');
|
|
3429
|
+
setPlanApprovalPending(_boundTab, false);
|
|
3430
|
+
appendSystem(params.turn.error.message, 'error');
|
|
3431
|
+
setStatus(params.turn.error.message, 'error');
|
|
3432
|
+
notifyCodexTurnOutcome(NOTIF_TYPE.ERROR, _boundTab, params.turn?.id || _activeTurnId);
|
|
3433
|
+
} else if (wasPlanTurn && tab) {
|
|
3434
|
+
const capturedPlan = capturePlanContent(tab);
|
|
3435
|
+
if (capturedPlan) {
|
|
3436
|
+
tab.planMode = false;
|
|
3437
|
+
tab.showPostPlanActions = true;
|
|
3438
|
+
tab.postPlanHeader = 'PLAN COMPLETE';
|
|
3439
|
+
tab.planApprovalPending = true;
|
|
3440
|
+
tab.planTurnActive = false;
|
|
3441
|
+
_showPostPlanActions = true;
|
|
3442
|
+
_postPlanHeader = 'PLAN COMPLETE';
|
|
3443
|
+
_planApprovalPending = true;
|
|
3444
|
+
_planTurnActive = false;
|
|
3445
|
+
_lastPlanTurnId = '';
|
|
3446
|
+
tab.lastPlanTurnId = '';
|
|
3447
|
+
ensurePlanFile(tab);
|
|
3448
|
+
renderPostPlanActions(tab);
|
|
3449
|
+
syncToolbarState();
|
|
3450
|
+
saveTabs();
|
|
3451
|
+
} else {
|
|
3452
|
+
setPlanTurnActive(tab, false, '');
|
|
3453
|
+
setPlanApprovalPending(tab, false);
|
|
3454
|
+
}
|
|
3455
|
+
notifyCodexTurnOutcome(NOTIF_TYPE.DONE, tab, params.turn?.id || _activeTurnId);
|
|
3456
|
+
} else {
|
|
3457
|
+
notifyCodexTurnOutcome(NOTIF_TYPE.DONE, _boundTab, params.turn?.id || _activeTurnId);
|
|
3211
3458
|
}
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
if (_boundTab?.postCompactionPending && !_boundTab?.showPostPlanActions) {
|
|
3217
|
-
flushPostCompactionPrompt(_boundTab);
|
|
3459
|
+
if (_boundTab?.postCompactionPending && !_boundTab?.showPostPlanActions) {
|
|
3460
|
+
flushPostCompactionPrompt(_boundTab);
|
|
3461
|
+
}
|
|
3462
|
+
if (!shouldHoldForPlanApproval(_boundTab)) advanceQueue();
|
|
3218
3463
|
}
|
|
3219
|
-
advanceQueue();
|
|
3220
3464
|
break;
|
|
3221
3465
|
case 'thread/compacted':
|
|
3222
3466
|
setCompactingUI(false);
|
|
@@ -3266,25 +3510,30 @@ export function handleNotify(method, params) {
|
|
|
3266
3510
|
case 'item/autoApprovalReview/completed':
|
|
3267
3511
|
setGuardianReviewState(params.targetItemId, params.review);
|
|
3268
3512
|
break;
|
|
3269
|
-
case 'item/completed':
|
|
3513
|
+
case 'item/completed': {
|
|
3514
|
+
const completedItem = params.item?.type === 'reasoning' && params.item.status == null
|
|
3515
|
+
? { ...params.item, status: 'complete' }
|
|
3516
|
+
: params.item;
|
|
3270
3517
|
// Inject pending images into userMessage if not consumed at item/started
|
|
3271
|
-
if (
|
|
3518
|
+
if (completedItem?.type === 'userMessage' && _boundTab?._pendingSendImages?.length) {
|
|
3272
3519
|
const imgItems = _boundTab._pendingSendImages.map((img) => ({
|
|
3273
3520
|
type: 'localImage',
|
|
3274
3521
|
name: img.name || 'image',
|
|
3275
3522
|
dataUrl: img.dataUrl,
|
|
3276
3523
|
}));
|
|
3277
|
-
|
|
3524
|
+
completedItem.content = [...imgItems, ...(completedItem.content || [])];
|
|
3278
3525
|
_boundTab._pendingSendImages = null;
|
|
3279
3526
|
}
|
|
3280
|
-
updateItemFromData(
|
|
3281
|
-
trackActiveItemCompletion(
|
|
3282
|
-
|
|
3527
|
+
updateItemFromData(completedItem);
|
|
3528
|
+
trackActiveItemCompletion(completedItem);
|
|
3529
|
+
captureCompletedPlanItem(_boundTab, completedItem);
|
|
3530
|
+
if (completedItem?.type === 'contextCompaction') {
|
|
3283
3531
|
markPostCompactionPrompt(_boundTab, { defer: _running });
|
|
3284
3532
|
}
|
|
3285
3533
|
// Re-show thinking — Codex is processing between items
|
|
3286
3534
|
if (_running) showThinking();
|
|
3287
3535
|
break;
|
|
3536
|
+
}
|
|
3288
3537
|
case 'item/agentMessage/delta':
|
|
3289
3538
|
appendAgentDelta(params.itemId, params.delta);
|
|
3290
3539
|
repositionThinking();
|
|
@@ -3294,14 +3543,19 @@ export function handleNotify(method, params) {
|
|
|
3294
3543
|
repositionThinking();
|
|
3295
3544
|
break;
|
|
3296
3545
|
case 'item/reasoning/textDelta':
|
|
3546
|
+
hideThinking();
|
|
3297
3547
|
appendReasoningDelta(params.itemId, params.delta, false);
|
|
3298
|
-
|
|
3548
|
+
syncWorkStatus();
|
|
3299
3549
|
break;
|
|
3300
3550
|
case 'item/reasoning/summaryTextDelta':
|
|
3551
|
+
hideThinking();
|
|
3301
3552
|
appendReasoningDelta(params.itemId, params.delta, true, params.summaryIndex);
|
|
3553
|
+
syncWorkStatus();
|
|
3302
3554
|
break;
|
|
3303
3555
|
case 'item/reasoning/summaryPartAdded':
|
|
3556
|
+
hideThinking();
|
|
3304
3557
|
appendReasoningSummaryPart(params.itemId, params.summaryIndex);
|
|
3558
|
+
syncWorkStatus();
|
|
3305
3559
|
break;
|
|
3306
3560
|
case 'item/commandExecution/outputDelta':
|
|
3307
3561
|
appendOutputDelta(params.itemId, params.delta, 'commandExecution');
|
|
@@ -3451,7 +3705,7 @@ export function syncToolbarState() {
|
|
|
3451
3705
|
effortBtn.title = `Effort level: ${effort}`;
|
|
3452
3706
|
effortBtn.classList.toggle('active', effort !== 'off');
|
|
3453
3707
|
const dots = effortBtn.querySelectorAll('.cxp-effort-dots i');
|
|
3454
|
-
const litCount =
|
|
3708
|
+
const litCount = Math.max(0, EFFORT_LEVELS.indexOf(effort));
|
|
3455
3709
|
dots.forEach((d, i) => d.classList.toggle('lit', i < litCount));
|
|
3456
3710
|
}
|
|
3457
3711
|
if (planBtn) {
|
|
@@ -3472,24 +3726,42 @@ export function syncToolbarState() {
|
|
|
3472
3726
|
}
|
|
3473
3727
|
}
|
|
3474
3728
|
|
|
3475
|
-
const CODEX_DEFAULT_MODELS = ['gpt-5.4', 'gpt-5.4-mini', 'gpt-5.3-codex'];
|
|
3729
|
+
const CODEX_DEFAULT_MODELS = ['gpt-5.5', 'gpt-5.4', 'gpt-5.4-mini', 'gpt-5.3-codex'];
|
|
3476
3730
|
let _modelListCache = null;
|
|
3477
3731
|
let _modelListRequest = null;
|
|
3478
3732
|
|
|
3733
|
+
function getCodexModelName(model) {
|
|
3734
|
+
const name = typeof model === 'string' ? model : (model?.id || model?.model || model?.name || '');
|
|
3735
|
+
return String(name || '').trim();
|
|
3736
|
+
}
|
|
3737
|
+
|
|
3738
|
+
function mergeCodexModelList(models) {
|
|
3739
|
+
const merged = [];
|
|
3740
|
+
const seen = new Set();
|
|
3741
|
+
for (const model of [...CODEX_DEFAULT_MODELS, ...(Array.isArray(models) ? models : [])]) {
|
|
3742
|
+
const name = getCodexModelName(model);
|
|
3743
|
+
const key = name.toLowerCase();
|
|
3744
|
+
if (!name || seen.has(key)) continue;
|
|
3745
|
+
seen.add(key);
|
|
3746
|
+
merged.push(name);
|
|
3747
|
+
}
|
|
3748
|
+
return merged;
|
|
3749
|
+
}
|
|
3750
|
+
|
|
3479
3751
|
export function requestModelList() {
|
|
3480
3752
|
if (_modelListCache) return Promise.resolve(_modelListCache);
|
|
3481
3753
|
if (_modelListRequest) return _modelListRequest;
|
|
3482
3754
|
_modelListRequest = new Promise((resolve) => {
|
|
3483
3755
|
const requestId = crypto.randomUUID();
|
|
3484
3756
|
if (!sendSocket({ type: 'model_list', requestId })) {
|
|
3485
|
-
_modelListCache =
|
|
3757
|
+
_modelListCache = mergeCodexModelList([]);
|
|
3486
3758
|
populateModelDropdown(_modelListCache);
|
|
3487
3759
|
resolve(_modelListCache);
|
|
3488
3760
|
_modelListRequest = null;
|
|
3489
3761
|
return;
|
|
3490
3762
|
}
|
|
3491
3763
|
window.__cxpModelListResolve = (models) => {
|
|
3492
|
-
_modelListCache = models
|
|
3764
|
+
_modelListCache = mergeCodexModelList(models);
|
|
3493
3765
|
populateModelDropdown(_modelListCache);
|
|
3494
3766
|
resolve(_modelListCache);
|
|
3495
3767
|
_modelListRequest = null;
|
|
@@ -3511,7 +3783,7 @@ export function populateModelDropdown(models) {
|
|
|
3511
3783
|
if (!menu) return;
|
|
3512
3784
|
menu.innerHTML = '';
|
|
3513
3785
|
for (const m of models) {
|
|
3514
|
-
const name =
|
|
3786
|
+
const name = getCodexModelName(m);
|
|
3515
3787
|
if (!name) continue;
|
|
3516
3788
|
const item = document.createElement('div');
|
|
3517
3789
|
item.className = 'cxp-dd-item';
|
|
@@ -3576,22 +3848,35 @@ export function stopStallTimer() {
|
|
|
3576
3848
|
_stallTimer = null;
|
|
3577
3849
|
}
|
|
3578
3850
|
|
|
3579
|
-
export function
|
|
3580
|
-
if (!
|
|
3851
|
+
export function interruptTab(tab = activeTab()) {
|
|
3852
|
+
if (!tab?.running) return false;
|
|
3581
3853
|
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3854
|
+
return withTab(tab, () => {
|
|
3855
|
+
const threadId = _threadId || tab.threadId || null;
|
|
3856
|
+
|
|
3857
|
+
// Reset UI immediately; the server interrupt/kill is best-effort cleanup.
|
|
3858
|
+
setRunning(false);
|
|
3859
|
+
setStatus('Stopped', 'info');
|
|
3860
|
+
appendSystem('Stopped.', 'muted');
|
|
3861
|
+
|
|
3862
|
+
clearInterruptTimer();
|
|
3863
|
+
if (tab._abortRetry) {
|
|
3864
|
+
clearTimeout(tab._abortRetry);
|
|
3865
|
+
tab._abortRetry = null;
|
|
3866
|
+
}
|
|
3867
|
+
sendSocket({ type: 'interrupt', sessionId: tab.id, threadId });
|
|
3868
|
+
tab._abortRetry = setTimeout(() => {
|
|
3869
|
+
tab._abortRetry = null;
|
|
3870
|
+
const ws = tab.ws;
|
|
3871
|
+
if (ws?.readyState !== 1) return;
|
|
3872
|
+
try { ws.send(JSON.stringify({ type: 'force_kill', sessionId: tab.id, threadId })); } catch {}
|
|
3873
|
+
}, 2500);
|
|
3874
|
+
return true;
|
|
3875
|
+
});
|
|
3876
|
+
}
|
|
3586
3877
|
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
_interruptTimer = setTimeout(() => {
|
|
3590
|
-
_interruptTimer = null;
|
|
3591
|
-
if (_interruptRetried) return;
|
|
3592
|
-
_interruptRetried = true;
|
|
3593
|
-
sendSocket({ type: 'force_kill', threadId: _threadId || null });
|
|
3594
|
-
}, 2500);
|
|
3878
|
+
export function interruptTurn() {
|
|
3879
|
+
return interruptTab(activeTab());
|
|
3595
3880
|
}
|
|
3596
3881
|
|
|
3597
3882
|
export function interruptAllTabs() {
|
|
@@ -3602,12 +3887,12 @@ export function interruptAllTabs() {
|
|
|
3602
3887
|
const ws = tab.ws;
|
|
3603
3888
|
const threadId = tab.threadId || null;
|
|
3604
3889
|
if (ws && ws.readyState === 1) {
|
|
3605
|
-
try { ws.send(JSON.stringify({ type: 'interrupt', threadId })); } catch {}
|
|
3890
|
+
try { ws.send(JSON.stringify({ type: 'interrupt', sessionId: tab.id, threadId })); } catch {}
|
|
3606
3891
|
if (tab._abortRetry) clearTimeout(tab._abortRetry);
|
|
3607
3892
|
tab._abortRetry = setTimeout(() => {
|
|
3608
3893
|
tab._abortRetry = null;
|
|
3609
3894
|
try {
|
|
3610
|
-
if (ws.readyState === 1) ws.send(JSON.stringify({ type: 'force_kill', threadId }));
|
|
3895
|
+
if (ws.readyState === 1) ws.send(JSON.stringify({ type: 'force_kill', sessionId: tab.id, threadId }));
|
|
3611
3896
|
} catch {}
|
|
3612
3897
|
}, 2000);
|
|
3613
3898
|
}
|
|
@@ -3778,6 +4063,8 @@ function formatStatusSnapshotMarkdown(snapshot, tab = activeTab()) {
|
|
|
3778
4063
|
`- Thread: \`${runtime.activeThreadId || tab?.threadId || 'none'}\`${runtime.activeTurnId ? ` (active turn \`${runtime.activeTurnId}\`)` : ''}`,
|
|
3779
4064
|
`- Model: \`${tab?.model || effective.model || 'default'}\``,
|
|
3780
4065
|
`- Reasoning effort: \`${tab?.effort && tab.effort !== 'off' ? tab.effort : (effective.model_reasoning_effort || 'default')}\``,
|
|
4066
|
+
`- Reasoning summary: \`${effective.model_reasoning_summary || 'default'}\``,
|
|
4067
|
+
`- Response verbosity: \`${effective.model_verbosity || 'default'}\``,
|
|
3781
4068
|
`- Personality: \`${effective.personality || 'pragmatic'}\``,
|
|
3782
4069
|
`- Fast mode: \`${effective.service_tier || 'default'}\``,
|
|
3783
4070
|
`- Approval policy: \`${effective.approval_policy || 'never'}\``,
|
|
@@ -3911,11 +4198,18 @@ function setModelForTab(tab, model) {
|
|
|
3911
4198
|
}
|
|
3912
4199
|
|
|
3913
4200
|
function setEffortForTab(tab, effort) {
|
|
3914
|
-
if (!tab
|
|
3915
|
-
|
|
3916
|
-
|
|
4201
|
+
if (!tab) return false;
|
|
4202
|
+
const normalized = String(effort || '').trim().toLowerCase();
|
|
4203
|
+
const nextEffort = normalized === 'default' || normalized === 'none' ? 'off' : normalized;
|
|
4204
|
+
if (!EFFORT_LEVELS.includes(nextEffort)) {
|
|
4205
|
+
appendSystem(`Unknown reasoning effort: ${effort}. Use off, minimal, low, medium, high, or xhigh.`, 'error');
|
|
4206
|
+
return false;
|
|
4207
|
+
}
|
|
4208
|
+
tab.effort = nextEffort;
|
|
4209
|
+
storage.setItem(STOR.effort, nextEffort);
|
|
3917
4210
|
syncToolbarState();
|
|
3918
4211
|
saveTabs();
|
|
4212
|
+
return true;
|
|
3919
4213
|
}
|
|
3920
4214
|
|
|
3921
4215
|
async function handleLocalSlashCommand(payload = currentComposerPayload()) {
|
|
@@ -4093,8 +4387,7 @@ async function handleLocalSlashCommand(payload = currentComposerPayload()) {
|
|
|
4093
4387
|
cycleEffort();
|
|
4094
4388
|
break;
|
|
4095
4389
|
}
|
|
4096
|
-
setEffortForTab(tab, args);
|
|
4097
|
-
appendMarkdown(`Reasoning effort set to \`${args}\`.`);
|
|
4390
|
+
if (setEffortForTab(tab, args)) appendMarkdown(`Reasoning effort set to \`${tab.effort || 'off'}\`.`);
|
|
4098
4391
|
break;
|
|
4099
4392
|
case 'mcp':
|
|
4100
4393
|
consumeComposer(input, tab, { clearAttachments: false });
|
|
@@ -4242,6 +4535,10 @@ export function sendPrompt() {
|
|
|
4242
4535
|
consumeComposer(input, tab);
|
|
4243
4536
|
return;
|
|
4244
4537
|
}
|
|
4538
|
+
if (shouldHoldForPlanApproval(tab)) {
|
|
4539
|
+
appendSystem('Choose Continue with implementation, Compact context, or Edit plan before sending another message.', 'working');
|
|
4540
|
+
return;
|
|
4541
|
+
}
|
|
4245
4542
|
if (tab?.planMode) {
|
|
4246
4543
|
preparePlanModeTurn(tab);
|
|
4247
4544
|
}
|
|
@@ -4382,7 +4679,7 @@ export function clearQueue() {
|
|
|
4382
4679
|
|
|
4383
4680
|
export function advanceQueue() {
|
|
4384
4681
|
const tab = activeTab();
|
|
4385
|
-
if (!tab || _running || tab.queuePaused || !tab.queue.length || hasBlockingServerRequests()) return;
|
|
4682
|
+
if (!tab || _running || tab.queuePaused || !tab.queue.length || hasBlockingServerRequests() || shouldHoldForPlanApproval(tab)) return;
|
|
4386
4683
|
const next = tab.queue.shift();
|
|
4387
4684
|
if (!next) return;
|
|
4388
4685
|
if (tab.planMode) {
|
|
@@ -4903,7 +5200,7 @@ export async function openSettingsPanel() {
|
|
|
4903
5200
|
// Get dynamic model list (fall back to hardcoded defaults)
|
|
4904
5201
|
let models;
|
|
4905
5202
|
try { models = await requestModelList(); } catch { models = null; }
|
|
4906
|
-
const modelList = models
|
|
5203
|
+
const modelList = mergeCodexModelList(models);
|
|
4907
5204
|
|
|
4908
5205
|
configEl.innerHTML = '';
|
|
4909
5206
|
const fieldDefs = [
|
|
@@ -4938,6 +5235,19 @@ export async function openSettingsPanel() {
|
|
|
4938
5235
|
{ value: 'high', label: 'High' },
|
|
4939
5236
|
{ value: 'xhigh', label: 'Extra High' },
|
|
4940
5237
|
]},
|
|
5238
|
+
{ key: 'model_reasoning_summary', label: 'Reasoning Summary', type: 'select', options: [
|
|
5239
|
+
{ value: '', label: '(default)' },
|
|
5240
|
+
{ value: 'auto', label: 'Auto' },
|
|
5241
|
+
{ value: 'concise', label: 'Concise' },
|
|
5242
|
+
{ value: 'detailed', label: 'Detailed' },
|
|
5243
|
+
{ value: 'none', label: 'None' },
|
|
5244
|
+
]},
|
|
5245
|
+
{ key: 'model_verbosity', label: 'Response Verbosity', type: 'select', options: [
|
|
5246
|
+
{ value: '', label: '(default)' },
|
|
5247
|
+
{ value: 'low', label: 'Low' },
|
|
5248
|
+
{ value: 'medium', label: 'Medium' },
|
|
5249
|
+
{ value: 'high', label: 'High' },
|
|
5250
|
+
]},
|
|
4941
5251
|
{ key: 'model_context_window', label: 'Context Window', type: 'number', placeholder: 'tokens' },
|
|
4942
5252
|
{ key: 'model_auto_compact_token_limit', label: 'Auto-Compact', type: 'number', placeholder: 'token limit' },
|
|
4943
5253
|
{ key: 'web_search', label: 'Web Search', type: 'checkbox' },
|
|
@@ -5063,6 +5373,7 @@ export function initContextBridge() {
|
|
|
5063
5373
|
isActiveTab,
|
|
5064
5374
|
dispatchPrompt,
|
|
5065
5375
|
capturePlanContent,
|
|
5376
|
+
ensurePlanFile,
|
|
5066
5377
|
saveTabs,
|
|
5067
5378
|
hideEmpty,
|
|
5068
5379
|
showEmpty,
|
|
@@ -5101,6 +5412,9 @@ export function initContextBridge() {
|
|
|
5101
5412
|
onBlockingServerRequestAnswered: (requestId) => clearBlockingServerRequest(requestId),
|
|
5102
5413
|
setPostPlanHeader: (v) => { _postPlanHeader = v; },
|
|
5103
5414
|
setShowPostPlanActions: (v) => { _showPostPlanActions = v; },
|
|
5415
|
+
setPlanTurnActive: (v) => { _planTurnActive = !!v; },
|
|
5416
|
+
setPlanApprovalPending: (v) => { _planApprovalPending = !!v; },
|
|
5417
|
+
setLastPlanTurnId: (v) => { _lastPlanTurnId = v || ''; },
|
|
5104
5418
|
setShowPostCompactionPrompt: (v) => { _showPostCompactionPrompt = !!v; },
|
|
5105
5419
|
setPostCompactionPending: (v) => { _postCompactionPending = !!v; },
|
|
5106
5420
|
setPlanFilePath: (v) => { _planFilePath = v; },
|