codex-overleaf-link 2.3.0 → 2.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -23
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/src/background.js +20 -0
- package/extension/src/content/assetTransferBroker.js +169 -0
- package/extension/src/content/composerPanel.js +0 -2
- package/extension/src/content/contentRuntime.js +135 -111
- package/extension/src/content/generated/content.bundle.js +1066 -345
- package/extension/src/content/generated/content.bundle.meta.json +7 -3
- package/extension/src/content/legacyGlobalRegistry.js +3 -0
- package/extension/src/content/markdownText.js +12 -10
- package/extension/src/content/mathText.js +155 -9
- package/extension/src/content/modelPicker.js +2 -2
- package/extension/src/content/modelPickerSupport.js +3 -3
- package/extension/src/content/nativeCompatibilityController.js +2 -2
- package/extension/src/content/otWarmMirror.js +1 -1
- package/extension/src/content/pageBridgeClient.js +1 -0
- package/extension/src/content/runResultActions.js +114 -0
- package/extension/src/content/runTimelineView.js +23 -0
- package/extension/src/content/sessionForkController.js +120 -0
- package/extension/src/content/writebackController.js +3 -0
- package/extension/src/content/writebackOrchestrator.js +5 -50
- package/extension/src/page/binaryAssetUploader.js +343 -0
- package/extension/src/page/trackedChangesLifecycle.js +12 -4
- package/extension/src/page/writebackRouter.js +6 -2
- package/extension/src/pageBridge.js +30 -1
- package/extension/src/shared/agentTranscript.js +10 -3
- package/extension/src/shared/compatibility.js +4 -2
- package/extension/src/shared/i18n.js +18 -4
- package/extension/src/shared/models.js +13 -2
- package/extension/src/shared/pageRpcContract.js +6 -2
- package/extension/src/shared/runExecutionSnapshot.js +14 -4
- package/extension/src/shared/runExecutionSnapshotCodec.js +5 -2
- package/extension/src/shared/scopedPersistenceQueuePolicy.js +20 -1
- package/extension/src/shared/scopedPersistenceReceipt.js +60 -0
- package/extension/src/shared/scopedPersistenceTransaction.js +50 -47
- package/extension/src/shared/sessionState.js +58 -15
- package/extension/src/shared/storageDb.js +6 -5
- package/extension/src/shared/storageRunActions.js +11 -1
- package/extension/src/shared/writebackOperationEvidence.js +16 -6
- package/extension/src/shared/writebackSettlement.js +28 -6
- package/extension/styles/panel.css +79 -8
- package/native-host/src/codexModels.js +6 -2
- package/native-host/src/codexPromptAssembly.js +1 -1
- package/native-host/src/codexSessionRunner.js +5 -1
- package/native-host/src/codexThreadFork.js +103 -0
- package/native-host/src/mirrorWorkspace.js +5 -19
- package/native-host/src/nativeAssetTransfer.js +183 -0
- package/native-host/src/taskRunnerRuntime.js +33 -81
- package/native-host/src/updateManager.js +36 -1
- package/package.json +1 -1
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
'use strict';
|
|
25
25
|
|
|
26
26
|
const DEFAULT_PANEL_STATE = {
|
|
27
|
-
mode: '
|
|
27
|
+
mode: 'ask',
|
|
28
28
|
providerId: 'builtin',
|
|
29
29
|
model: 'gpt-5.4',
|
|
30
30
|
reasoningEffort: 'high',
|
|
@@ -47,13 +47,13 @@
|
|
|
47
47
|
customInstructionsByProject: {}
|
|
48
48
|
};
|
|
49
49
|
|
|
50
|
-
const VALID_MODES = new Set(['ask', '
|
|
50
|
+
const VALID_MODES = new Set(['ask', 'auto']);
|
|
51
51
|
// Keep the persisted/session-level contract aligned with the model picker
|
|
52
52
|
// and native provider bridge. Custom providers can explicitly disable
|
|
53
53
|
// reasoning (`none`) or expose the lightest Codex tier (`minimal`). Dropping
|
|
54
54
|
// either value here silently restores the global `high` default after the
|
|
55
55
|
// user has selected it in the composer.
|
|
56
|
-
const VALID_REASONING = new Set(['none', 'minimal', 'low', 'medium', 'high', 'xhigh']);
|
|
56
|
+
const VALID_REASONING = new Set(['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'ultra']);
|
|
57
57
|
const VALID_SPEED_TIERS = new Set(['standard', 'fast']);
|
|
58
58
|
const VALID_LOCALES = new Set(['en', 'zh']);
|
|
59
59
|
const VALID_EVENT_STATUSES = new Set(['info', 'running', 'completed', 'failed', 'warning', 'blocked', 'skipped', 'pending']);
|
|
@@ -127,12 +127,8 @@
|
|
|
127
127
|
...input
|
|
128
128
|
};
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
if (!VALID_REASONING.has(state.reasoningEffort)) {
|
|
134
|
-
state.reasoningEffort = DEFAULT_PANEL_STATE.reasoningEffort;
|
|
135
|
-
}
|
|
130
|
+
state.mode = normalizeMode(state.mode);
|
|
131
|
+
state.reasoningEffort = normalizeReasoning(state.reasoningEffort);
|
|
136
132
|
if (!VALID_SPEED_TIERS.has(state.speedTier)) {
|
|
137
133
|
state.speedTier = DEFAULT_PANEL_STATE.speedTier;
|
|
138
134
|
}
|
|
@@ -221,7 +217,7 @@
|
|
|
221
217
|
history,
|
|
222
218
|
runs,
|
|
223
219
|
task: sanitizeAssistantVisibleText(session.task),
|
|
224
|
-
mode:
|
|
220
|
+
mode: normalizeMode(session.mode, fallbackState.mode),
|
|
225
221
|
providerId: typeof session.providerId === 'string' && session.providerId.trim()
|
|
226
222
|
? session.providerId.trim()
|
|
227
223
|
: (fallbackState.providerId || 'builtin'),
|
|
@@ -234,7 +230,10 @@
|
|
|
234
230
|
: fallbackState.speedTier,
|
|
235
231
|
requireReviewing: session.requireReviewing !== false,
|
|
236
232
|
focusFiles: normalizeFocusFiles(session.focusFiles),
|
|
233
|
+
projectReferenceFiles: normalizeProjectReferenceFiles(session.projectReferenceFiles),
|
|
237
234
|
codexThreadId: typeof session.codexThreadId === 'string' ? session.codexThreadId : '',
|
|
235
|
+
forkedFromSessionId: typeof session.forkedFromSessionId === 'string' ? session.forkedFromSessionId : '',
|
|
236
|
+
forkedFromRunId: typeof session.forkedFromRunId === 'string' ? session.forkedFromRunId : '',
|
|
238
237
|
pendingInputs: normalizePendingInputs(
|
|
239
238
|
session.pendingInputs,
|
|
240
239
|
options.restoreRunningRuns === true,
|
|
@@ -311,13 +310,14 @@
|
|
|
311
310
|
id: active.id,
|
|
312
311
|
history: Array.isArray(active.history) ? active.history.slice(-10) : [],
|
|
313
312
|
focusFiles: normalizeFocusFiles(active.focusFiles),
|
|
313
|
+
projectReferenceFiles: normalizeProjectReferenceFiles(active.projectReferenceFiles),
|
|
314
314
|
codexThreadId: active.codexThreadId || '',
|
|
315
315
|
providerId: state.providerId || 'builtin'
|
|
316
316
|
};
|
|
317
317
|
state.runs = Array.isArray(active.runs) ? active.runs : [];
|
|
318
318
|
state.task = typeof active.task === 'string' ? active.task : '';
|
|
319
319
|
state.focusFiles = normalizeFocusFiles(active.focusFiles);
|
|
320
|
-
state.mode =
|
|
320
|
+
state.mode = normalizeMode(active.mode);
|
|
321
321
|
state.requireReviewing = active.requireReviewing !== false;
|
|
322
322
|
|
|
323
323
|
return state;
|
|
@@ -339,7 +339,7 @@
|
|
|
339
339
|
history: normalizeHistoryEntries(overrides.history),
|
|
340
340
|
runs,
|
|
341
341
|
task: sanitizeAssistantVisibleText(overrides.task),
|
|
342
|
-
mode:
|
|
342
|
+
mode: normalizeMode(overrides.mode),
|
|
343
343
|
providerId: typeof overrides.providerId === 'string' && overrides.providerId.trim()
|
|
344
344
|
? overrides.providerId.trim()
|
|
345
345
|
: 'builtin',
|
|
@@ -352,7 +352,10 @@
|
|
|
352
352
|
: DEFAULT_PANEL_STATE.speedTier,
|
|
353
353
|
requireReviewing: overrides.requireReviewing !== false,
|
|
354
354
|
focusFiles: normalizeFocusFiles(overrides.focusFiles),
|
|
355
|
+
projectReferenceFiles: normalizeProjectReferenceFiles(overrides.projectReferenceFiles),
|
|
355
356
|
codexThreadId: typeof overrides.codexThreadId === 'string' ? overrides.codexThreadId : '',
|
|
357
|
+
forkedFromSessionId: typeof overrides.forkedFromSessionId === 'string' ? overrides.forkedFromSessionId : '',
|
|
358
|
+
forkedFromRunId: typeof overrides.forkedFromRunId === 'string' ? overrides.forkedFromRunId : '',
|
|
356
359
|
pendingInputs: normalizePendingInputs(overrides.pendingInputs)
|
|
357
360
|
};
|
|
358
361
|
}
|
|
@@ -638,6 +641,8 @@
|
|
|
638
641
|
undoStatus: sanitizeAssistantVisibleText(run.undoStatus),
|
|
639
642
|
nativeRequestId: sanitizeAssistantVisibleText(run.nativeRequestId),
|
|
640
643
|
codexTurnId: sanitizeAssistantVisibleText(run.codexTurnId),
|
|
644
|
+
forkSnapshot: run.forkSnapshot === true,
|
|
645
|
+
forkSourceRunId: sanitizeAssistantVisibleText(run.forkSourceRunId),
|
|
641
646
|
nativeEventSeq: Number.isFinite(Number(run.nativeEventSeq)) ? Number(run.nativeEventSeq) : 0,
|
|
642
647
|
queueItemId: sanitizeAssistantVisibleText(run.queueItemId),
|
|
643
648
|
interruptedDraft: run.interruptedDraft ? sanitizeAssistantVisibleValue(run.interruptedDraft) : undefined
|
|
@@ -904,6 +909,33 @@
|
|
|
904
909
|
return files.slice(0, 5);
|
|
905
910
|
}
|
|
906
911
|
|
|
912
|
+
function normalizeProjectReferenceFiles(value) {
|
|
913
|
+
const seen = new Set();
|
|
914
|
+
const files = [];
|
|
915
|
+
for (const item of Array.isArray(value) ? value : []) {
|
|
916
|
+
const rawPath = typeof item === 'string' ? item : item?.path;
|
|
917
|
+
if (typeof rawPath !== 'string' || !rawPath.trim() || rawPath.length > 512) {
|
|
918
|
+
continue;
|
|
919
|
+
}
|
|
920
|
+
if (/^(?:[A-Za-z]:|[\\/])/.test(rawPath) || rawPath.includes('\0')) {
|
|
921
|
+
continue;
|
|
922
|
+
}
|
|
923
|
+
const path = rawPath.replace(/\\/g, '/').replace(/\/+/g, '/').replace(/^\.\//, '').trim();
|
|
924
|
+
if (!path || path.split('/').some(segment => !segment || segment === '.' || segment === '..') || seen.has(path)) {
|
|
925
|
+
continue;
|
|
926
|
+
}
|
|
927
|
+
seen.add(path);
|
|
928
|
+
files.push({
|
|
929
|
+
path,
|
|
930
|
+
kind: item?.kind === 'binary' ? 'binary' : 'text'
|
|
931
|
+
});
|
|
932
|
+
if (files.length >= 5000) {
|
|
933
|
+
break;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
return files;
|
|
937
|
+
}
|
|
938
|
+
|
|
907
939
|
function normalizeCustomInstructionsByProject(value) {
|
|
908
940
|
const result = {};
|
|
909
941
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
@@ -994,7 +1026,7 @@
|
|
|
994
1026
|
// Skills page was reopened.
|
|
995
1027
|
codexOverleafSkills: compactCodexOverleafSkillsForStorage(source.codexOverleafSkills),
|
|
996
1028
|
composerAttachments: normalizeComposerAttachmentsForPersistence(source.composerAttachments),
|
|
997
|
-
mode:
|
|
1029
|
+
mode: normalizeMode(active?.mode, source.mode),
|
|
998
1030
|
providerId: typeof source.providerId === 'string' && source.providerId.trim()
|
|
999
1031
|
? source.providerId.trim()
|
|
1000
1032
|
: 'builtin',
|
|
@@ -1092,7 +1124,10 @@
|
|
|
1092
1124
|
speedTier: normalizeSpeedTier(session.speedTier || fallbackState.speedTier),
|
|
1093
1125
|
requireReviewing: session.requireReviewing !== false,
|
|
1094
1126
|
focusFiles: normalizeFocusFiles(session.focusFiles),
|
|
1127
|
+
projectReferenceFiles: normalizeProjectReferenceFiles(session.projectReferenceFiles),
|
|
1095
1128
|
codexThreadId: typeof session.codexThreadId === 'string' ? session.codexThreadId : '',
|
|
1129
|
+
forkedFromSessionId: normalizeTextField(session.forkedFromSessionId, 160),
|
|
1130
|
+
forkedFromRunId: normalizeTextField(session.forkedFromRunId, 160),
|
|
1096
1131
|
pendingInputs: normalizePendingInputs(session.pendingInputs)
|
|
1097
1132
|
};
|
|
1098
1133
|
// Welcome-panel + write-guard: preserve the four
|
|
@@ -1185,6 +1220,8 @@
|
|
|
1185
1220
|
undoStatus: summarizeTextForStorage(run.undoStatus, 'undo status'),
|
|
1186
1221
|
nativeRequestId: normalizeTextField(run.nativeRequestId, 160),
|
|
1187
1222
|
codexTurnId: normalizeTextField(run.codexTurnId, 160),
|
|
1223
|
+
forkSnapshot: run.forkSnapshot === true,
|
|
1224
|
+
forkSourceRunId: normalizeTextField(run.forkSourceRunId, 160),
|
|
1188
1225
|
nativeEventSeq: Number.isFinite(Number(run.nativeEventSeq)) ? Number(run.nativeEventSeq) : 0,
|
|
1189
1226
|
queueItemId: normalizeTextField(run.queueItemId, 160)
|
|
1190
1227
|
};
|
|
@@ -1339,8 +1376,14 @@
|
|
|
1339
1376
|
};
|
|
1340
1377
|
}
|
|
1341
1378
|
|
|
1342
|
-
function normalizeMode(mode) {
|
|
1343
|
-
|
|
1379
|
+
function normalizeMode(mode, fallback = DEFAULT_PANEL_STATE.mode) {
|
|
1380
|
+
if (mode === 'confirm') {
|
|
1381
|
+
return 'ask';
|
|
1382
|
+
}
|
|
1383
|
+
if (VALID_MODES.has(mode)) {
|
|
1384
|
+
return mode;
|
|
1385
|
+
}
|
|
1386
|
+
return VALID_MODES.has(fallback) ? fallback : DEFAULT_PANEL_STATE.mode;
|
|
1344
1387
|
}
|
|
1345
1388
|
|
|
1346
1389
|
function normalizeReasoning(reasoningEffort) {
|
|
@@ -287,7 +287,7 @@
|
|
|
287
287
|
history: compactHistoryForStorage(input.history),
|
|
288
288
|
runs: compactRunsForStorage(input.runs, options),
|
|
289
289
|
task: normalizeDisplayTextForStorage(input.task, SESSION_STORAGE_LIMITS.taskChars),
|
|
290
|
-
mode:
|
|
290
|
+
mode: normalizeCurrentMode(input.mode), providerId: typeof input.providerId === 'string' && input.providerId ? input.providerId : 'builtin',
|
|
291
291
|
model: typeof input.model === 'string' ? input.model : '',
|
|
292
292
|
reasoningEffort: typeof input.reasoningEffort === 'string' ? input.reasoningEffort : '',
|
|
293
293
|
speedTier: typeof input.speedTier === 'string' ? input.speedTier : '',
|
|
@@ -435,6 +435,9 @@
|
|
|
435
435
|
}
|
|
436
436
|
|
|
437
437
|
// --- Helpers ---
|
|
438
|
+
function normalizeCurrentMode(mode) {
|
|
439
|
+
return mode === 'auto' ? 'auto' : 'ask';
|
|
440
|
+
}
|
|
438
441
|
|
|
439
442
|
function extractLightweightPrefs(state, projectId) {
|
|
440
443
|
var prefs = {
|
|
@@ -442,8 +445,9 @@
|
|
|
442
445
|
model: typeof state.model === 'string' ? state.model : '',
|
|
443
446
|
reasoningEffort: typeof state.reasoningEffort === 'string' ? state.reasoningEffort : '',
|
|
444
447
|
speedTier: typeof state.speedTier === 'string' ? state.speedTier : '',
|
|
445
|
-
mode:
|
|
448
|
+
mode: normalizeCurrentMode(state.mode),
|
|
446
449
|
locale: typeof state.locale === 'string' ? state.locale : '',
|
|
450
|
+
theme: state.theme === 'light' || state.theme === 'auto' ? state.theme : 'dark',
|
|
447
451
|
requireReviewing: state.requireReviewing !== false,
|
|
448
452
|
autoRecompile: state.autoRecompile !== false,
|
|
449
453
|
loadCodexLocalSkills: state.loadCodexLocalSkills !== false,
|
|
@@ -1314,11 +1318,9 @@
|
|
|
1314
1318
|
}
|
|
1315
1319
|
|
|
1316
1320
|
// --- Utilities ---
|
|
1317
|
-
|
|
1318
1321
|
function generateId(prefix) {
|
|
1319
1322
|
return prefix + '_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 8);
|
|
1320
1323
|
}
|
|
1321
|
-
|
|
1322
1324
|
function safeJsonStringify(value) {
|
|
1323
1325
|
try {
|
|
1324
1326
|
return JSON.stringify(value) || '';
|
|
@@ -1326,7 +1328,6 @@
|
|
|
1326
1328
|
return '[unserializable]';
|
|
1327
1329
|
}
|
|
1328
1330
|
}
|
|
1329
|
-
|
|
1330
1331
|
var hashString = StorageRunActions.hashString;
|
|
1331
1332
|
|
|
1332
1333
|
return {
|
|
@@ -38,8 +38,18 @@
|
|
|
38
38
|
&& Array.isArray(run.undoTrackedChanges) && run.undoTrackedChanges.length > 0
|
|
39
39
|
&& Array.isArray(run.undoExpectedFiles) && run.undoExpectedFiles.length > 0
|
|
40
40
|
&& Array.isArray(run.appliedOperations) && run.appliedOperations.length > 0;
|
|
41
|
+
// Overleaf can expose a working native editor-undo checkpoint before its
|
|
42
|
+
// Reviewing DOM markers are discoverable. Preserve that checkpoint too so
|
|
43
|
+
// a reload does not erase the only safe rollback path while the document
|
|
44
|
+
// still contains an unresolved tracked change.
|
|
45
|
+
var reviewingEditorUndo = trackedStatus !== 'accepted'
|
|
46
|
+
&& trackedStatus !== 'rejected'
|
|
47
|
+
&& run && run.undoStatus !== 'applied'
|
|
48
|
+
&& run.executionSnapshot && run.executionSnapshot.requireReviewing === true
|
|
49
|
+
&& Array.isArray(run.undoExpectedFiles) && run.undoExpectedFiles.length > 0
|
|
50
|
+
&& Array.isArray(run.appliedOperations) && run.appliedOperations.length > 0;
|
|
41
51
|
var legacyUndo = Array.isArray(run && run.undoOperations) && run.undoOperations.length > 0;
|
|
42
|
-
return trackedLifecycle || legacyUndo;
|
|
52
|
+
return trackedLifecycle || reviewingEditorUndo || legacyUndo;
|
|
43
53
|
}
|
|
44
54
|
|
|
45
55
|
function compactRunActionPayload(run, keepActionPayload) {
|
|
@@ -114,12 +114,22 @@
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
function normalizeTrackedChanges(value) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
const normalized = [];
|
|
118
|
+
const seen = new Set();
|
|
119
|
+
for (const item of Array.isArray(value) ? value : []) {
|
|
120
|
+
const change = {
|
|
121
|
+
key: normalizeText(item?.key),
|
|
122
|
+
id: normalizeText(item?.id),
|
|
123
|
+
path: normalizeText(item?.path),
|
|
124
|
+
label: normalizeText(item?.label)
|
|
125
|
+
};
|
|
126
|
+
if (!change.key && !change.id && !change.path) continue;
|
|
127
|
+
const identity = `${change.path}\u0000${change.key || change.id || 'path-only'}`;
|
|
128
|
+
if (seen.has(identity)) continue;
|
|
129
|
+
seen.add(identity);
|
|
130
|
+
normalized.push(change);
|
|
131
|
+
}
|
|
132
|
+
return normalized;
|
|
123
133
|
}
|
|
124
134
|
|
|
125
135
|
function normalizeStringArray(value) {
|
|
@@ -270,9 +270,17 @@
|
|
|
270
270
|
}
|
|
271
271
|
const successful = isSuccessfulTrackedChangeSettlement(input.result);
|
|
272
272
|
const reviewCodes = kind === 'accept' ? ACCEPT_NEEDS_REVIEW_CODES : REJECT_NEEDS_REVIEW_CODES;
|
|
273
|
-
|
|
273
|
+
// Reject/Undo is an all-target recovery transaction. A successful entry
|
|
274
|
+
// only proves that one tracked target was restored; any failed/skipped
|
|
275
|
+
// sibling can still leave this run's content in another file. Keep that
|
|
276
|
+
// recovery actionable instead of collapsing a partial multi-file reject
|
|
277
|
+
// into the terminal `rejected` state. Accept retains its established
|
|
278
|
+
// best-effort terminal policy.
|
|
279
|
+
const incompleteReject = kind === 'reject'
|
|
280
|
+
&& collectRunResultSkipped(input.result).length > 0;
|
|
281
|
+
const needsReview = incompleteReject || (!successful && failures.some(failure =>
|
|
274
282
|
failure.terminalState === 'needs_review' || reviewCodes.has(failure.code)
|
|
275
|
-
);
|
|
283
|
+
));
|
|
276
284
|
const status = needsReview ? 'needs_review' : kind === 'accept' ? 'accepted' : 'rejected';
|
|
277
285
|
return {
|
|
278
286
|
decision: status,
|
|
@@ -595,7 +603,12 @@
|
|
|
595
603
|
: operation;
|
|
596
604
|
}
|
|
597
605
|
|
|
598
|
-
function selectExpectedFilesForTrackedUndo(
|
|
606
|
+
function selectExpectedFilesForTrackedUndo(
|
|
607
|
+
project,
|
|
608
|
+
operations = [],
|
|
609
|
+
trackedChanges = [],
|
|
610
|
+
previousExpectedFiles = []
|
|
611
|
+
) {
|
|
599
612
|
const paths = new Set();
|
|
600
613
|
for (const change of trackedChanges || []) {
|
|
601
614
|
if (change?.path) paths.add(change.path);
|
|
@@ -604,9 +617,18 @@
|
|
|
604
617
|
if (operation?.path) paths.add(operation.path);
|
|
605
618
|
if (operation?.to) paths.add(operation.to);
|
|
606
619
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
.
|
|
620
|
+
const expectedByPath = new Map();
|
|
621
|
+
for (const file of previousExpectedFiles || []) {
|
|
622
|
+
if (paths.has(file?.path) && typeof file?.content === 'string' && !expectedByPath.has(file.path)) {
|
|
623
|
+
expectedByPath.set(file.path, { path: file.path, content: file.content });
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
for (const file of project?.files || []) {
|
|
627
|
+
if (paths.has(file?.path) && typeof file?.content === 'string' && !expectedByPath.has(file.path)) {
|
|
628
|
+
expectedByPath.set(file.path, { path: file.path, content: file.content });
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return Array.from(expectedByPath.values());
|
|
610
632
|
}
|
|
611
633
|
|
|
612
634
|
function acceptDiagnosticStatus(step, info) {
|
|
@@ -2359,6 +2359,67 @@
|
|
|
2359
2359
|
margin-left: auto;
|
|
2360
2360
|
}
|
|
2361
2361
|
|
|
2362
|
+
#codex-overleaf-panel .run-result-actions {
|
|
2363
|
+
display: flex;
|
|
2364
|
+
justify-content: flex-start;
|
|
2365
|
+
align-items: center;
|
|
2366
|
+
gap: 2px;
|
|
2367
|
+
min-height: 30px;
|
|
2368
|
+
margin: 10px 0 0;
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
#codex-overleaf-panel .run-result-actions[hidden] { display: none; }
|
|
2372
|
+
|
|
2373
|
+
#codex-overleaf-panel .run-copy-action,
|
|
2374
|
+
#codex-overleaf-panel .run-fork-action {
|
|
2375
|
+
display: inline-grid;
|
|
2376
|
+
place-items: center;
|
|
2377
|
+
width: 30px;
|
|
2378
|
+
height: 30px;
|
|
2379
|
+
border: 0;
|
|
2380
|
+
border-radius: 999px;
|
|
2381
|
+
background: transparent;
|
|
2382
|
+
color: var(--tl-fg-4);
|
|
2383
|
+
opacity: 1;
|
|
2384
|
+
transition: color 120ms ease, background 120ms ease, opacity 120ms ease;
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2387
|
+
#codex-overleaf-panel .run-copy-action:hover:not(:disabled),
|
|
2388
|
+
#codex-overleaf-panel .run-fork-action:hover:not(:disabled) {
|
|
2389
|
+
background: var(--tl-hover);
|
|
2390
|
+
color: var(--tl-accent);
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
#codex-overleaf-panel .run-copy-action:disabled,
|
|
2394
|
+
#codex-overleaf-panel .run-fork-action:disabled {
|
|
2395
|
+
cursor: not-allowed;
|
|
2396
|
+
opacity: 0.38;
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
#codex-overleaf-panel .run-copy-action svg,
|
|
2400
|
+
#codex-overleaf-panel .run-fork-action svg {
|
|
2401
|
+
width: 16px;
|
|
2402
|
+
height: 16px;
|
|
2403
|
+
fill: none;
|
|
2404
|
+
stroke: currentColor;
|
|
2405
|
+
stroke-width: 1.7;
|
|
2406
|
+
stroke-linecap: round;
|
|
2407
|
+
stroke-linejoin: round;
|
|
2408
|
+
}
|
|
2409
|
+
|
|
2410
|
+
#codex-overleaf-panel .run-copy-action[hidden],
|
|
2411
|
+
#codex-overleaf-panel .run-fork-action[hidden] { display: none; }
|
|
2412
|
+
|
|
2413
|
+
#codex-overleaf-panel .run-completed-time {
|
|
2414
|
+
margin-left: 5px;
|
|
2415
|
+
color: var(--tl-fg-4);
|
|
2416
|
+
font-size: 12px;
|
|
2417
|
+
font-variant-numeric: tabular-nums;
|
|
2418
|
+
line-height: 30px;
|
|
2419
|
+
}
|
|
2420
|
+
|
|
2421
|
+
#codex-overleaf-panel .run-completed-time[hidden] { display: none; }
|
|
2422
|
+
|
|
2362
2423
|
/* When Accept All is hidden, the Undo button takes the right-aligned slot. */
|
|
2363
2424
|
#codex-overleaf-panel .run-turn-meta [data-run-accept][hidden] + [data-run-undo] {
|
|
2364
2425
|
margin-left: auto;
|
|
@@ -2686,13 +2747,23 @@
|
|
|
2686
2747
|
}
|
|
2687
2748
|
|
|
2688
2749
|
#codex-overleaf-panel .run-math--fallback {
|
|
2689
|
-
border-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2750
|
+
border-bottom: 1px dotted var(--tl-fg-4);
|
|
2751
|
+
border-radius: 0;
|
|
2752
|
+
background: transparent;
|
|
2753
|
+
color: inherit;
|
|
2754
|
+
padding: 0 2px;
|
|
2693
2755
|
white-space: pre-wrap;
|
|
2694
2756
|
overflow-wrap: anywhere;
|
|
2695
|
-
font:
|
|
2757
|
+
font: inherit;
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
#codex-overleaf-panel .run-math--display.run-math--fallback {
|
|
2761
|
+
border: 1px solid var(--tl-border);
|
|
2762
|
+
border-left: 3px solid var(--tl-fg-4);
|
|
2763
|
+
border-radius: 6px;
|
|
2764
|
+
background: var(--tl-surface-2);
|
|
2765
|
+
padding: 8px 10px;
|
|
2766
|
+
text-align: left;
|
|
2696
2767
|
}
|
|
2697
2768
|
|
|
2698
2769
|
#codex-overleaf-panel .run-stream[data-stream-role="reasoning"] .run-stream-text {
|
|
@@ -3173,12 +3244,12 @@
|
|
|
3173
3244
|
white-space: nowrap;
|
|
3174
3245
|
}
|
|
3175
3246
|
|
|
3176
|
-
/* Mode switch (Ask/
|
|
3177
|
-
holding
|
|
3247
|
+
/* Mode switch (Ask/Auto) — a real segmented control: a bordered track
|
|
3248
|
+
holding two choices. Inactive choices are muted; hover is a light fill
|
|
3178
3249
|
(distinct from the active accent-wash fill) so hover never reads as active. */
|
|
3179
3250
|
#codex-overleaf-panel .codex-mode-switch {
|
|
3180
3251
|
display: grid;
|
|
3181
|
-
grid-template-columns: repeat(
|
|
3252
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
3182
3253
|
gap: 2px;
|
|
3183
3254
|
min-width: 0;
|
|
3184
3255
|
padding: 2px;
|
|
@@ -181,7 +181,7 @@ function buildModelEntry({
|
|
|
181
181
|
defaultSpeedTier = 'standard'
|
|
182
182
|
}) {
|
|
183
183
|
const normalizedEfforts = normalizeReasoningEfforts(reasoningEfforts);
|
|
184
|
-
const normalizedDefault =
|
|
184
|
+
const normalizedDefault = normalizeReasoningEffort(defaultReasoningEffort) || 'medium';
|
|
185
185
|
const normalizedSpeedTiers = normalizeSpeedTiers(speedTiers);
|
|
186
186
|
const normalizedDefaultSpeedTier = getString(defaultSpeedTier) || 'standard';
|
|
187
187
|
return {
|
|
@@ -202,7 +202,7 @@ function normalizeReasoningEfforts(rawEfforts) {
|
|
|
202
202
|
const seen = new Set();
|
|
203
203
|
const result = [];
|
|
204
204
|
for (const rawEffort of rawEfforts) {
|
|
205
|
-
const effort =
|
|
205
|
+
const effort = normalizeReasoningEffort(typeof rawEffort === 'string' ? rawEffort : rawEffort?.effort);
|
|
206
206
|
if (!effort || seen.has(effort)) {
|
|
207
207
|
continue;
|
|
208
208
|
}
|
|
@@ -212,6 +212,10 @@ function normalizeReasoningEfforts(rawEfforts) {
|
|
|
212
212
|
return result;
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
+
function normalizeReasoningEffort(rawEffort) {
|
|
216
|
+
return getString(rawEffort);
|
|
217
|
+
}
|
|
218
|
+
|
|
215
219
|
function normalizeSpeedTiers(rawTiers) {
|
|
216
220
|
const tiers = ['standard'];
|
|
217
221
|
if (!Array.isArray(rawTiers)) {
|
|
@@ -64,7 +64,7 @@ function buildCodexTurnPrompt(options = {}) {
|
|
|
64
64
|
'Mode for this turn:',
|
|
65
65
|
`- ${context.mode}`,
|
|
66
66
|
'- ask: inspect and explain only; do not edit files.',
|
|
67
|
-
'-
|
|
67
|
+
'- auto: edit the local workspace directly when the request calls for changes. The browser bridge handles authorization, deletion approval, and syncing back to Overleaf.',
|
|
68
68
|
'',
|
|
69
69
|
'Write expectation for this turn:',
|
|
70
70
|
formatWriteExpectation({
|
|
@@ -22,6 +22,7 @@ const {
|
|
|
22
22
|
loadSelectedProjectSkills
|
|
23
23
|
} = require('./localSkills');
|
|
24
24
|
const { createSubagentBroker } = require('./subagentBroker');
|
|
25
|
+
const { prepareBinaryAssetChanges } = require('./nativeAssetTransfer');
|
|
25
26
|
const { resolveCodexCommand, shouldUseShellForCommand } = require('./codexCommand');
|
|
26
27
|
const { applyProviderEnvironment, buildProviderConfigArgs, prepareProviderLaunch } = require('./codexProviderLaunch');
|
|
27
28
|
const { buildReadProgressRules, createReadProgressController } = require('./readProgressGuard');
|
|
@@ -259,6 +260,9 @@ async function runCodexSession({ params = {}, env = process.env, emit = () => {}
|
|
|
259
260
|
});
|
|
260
261
|
}
|
|
261
262
|
|
|
263
|
+
const preparedAssets = prepareBinaryAssetChanges({ projectId, rootDir, changes: rawSyncChanges });
|
|
264
|
+
rawSyncChanges = preparedAssets.changes;
|
|
265
|
+
unsupportedChanges.push(...preparedAssets.unsupportedChanges);
|
|
262
266
|
const syncChanges = rawSyncChanges.map(change => {
|
|
263
267
|
if (change.type === 'write' && typeof change.previousContent === 'string') {
|
|
264
268
|
return {
|
|
@@ -1461,7 +1465,7 @@ function extractCodexAppServerErrorMessage(params = {}) {
|
|
|
1461
1465
|
}
|
|
1462
1466
|
|
|
1463
1467
|
function normalizeReasoningEffort(value) {
|
|
1464
|
-
return ['none', 'minimal', 'low', 'medium', 'high', 'xhigh'].includes(value) ? value : null;
|
|
1468
|
+
return ['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'ultra'].includes(value) ? value : null;
|
|
1465
1469
|
}
|
|
1466
1470
|
|
|
1467
1471
|
function normalizeSpeedTier(value) {
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('node:child_process');
|
|
4
|
+
const { version: PACKAGE_VERSION } = require('../../package.json');
|
|
5
|
+
const { buildCodexHomeEnv } = require('./codexHome');
|
|
6
|
+
const { resolveCodexCommand, shouldUseShellForCommand } = require('./codexCommand');
|
|
7
|
+
const { buildCodexAppServerArgs } = require('./codexSessionRunner');
|
|
8
|
+
|
|
9
|
+
function forkCodexThread(params = {}, env = process.env) {
|
|
10
|
+
const threadId = cleanId(params.threadId, 'threadId');
|
|
11
|
+
const lastTurnId = cleanId(params.lastTurnId, 'lastTurnId');
|
|
12
|
+
const childEnv = buildCodexHomeEnv(env, {
|
|
13
|
+
loadCodexLocalSkills: params.loadCodexLocalSkills !== false,
|
|
14
|
+
loadCodexOverleafSkills: params.loadCodexOverleafSkills !== false
|
|
15
|
+
});
|
|
16
|
+
const command = resolveCodexCommand(childEnv);
|
|
17
|
+
if (!command) return Promise.reject(codedError('codex_not_found', 'Codex CLI was not found.'));
|
|
18
|
+
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const child = spawn(command, buildCodexAppServerArgs({
|
|
21
|
+
speedTier: params.speedTier || 'standard',
|
|
22
|
+
loadCodexLocalSkills: params.loadCodexLocalSkills !== false
|
|
23
|
+
}), {
|
|
24
|
+
env: childEnv,
|
|
25
|
+
shell: shouldUseShellForCommand(command, childEnv),
|
|
26
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
27
|
+
});
|
|
28
|
+
const pending = new Map();
|
|
29
|
+
let nextId = 1;
|
|
30
|
+
let buffer = '';
|
|
31
|
+
let stderr = '';
|
|
32
|
+
let settled = false;
|
|
33
|
+
const timeout = setTimeout(() => finish(codedError('codex_thread_fork_timeout', 'Codex thread fork timed out.')), 30000);
|
|
34
|
+
|
|
35
|
+
child.stdout.setEncoding('utf8');
|
|
36
|
+
child.stderr.setEncoding('utf8');
|
|
37
|
+
child.stdout.on('data', chunk => {
|
|
38
|
+
buffer += chunk;
|
|
39
|
+
const lines = buffer.split(/\r?\n/);
|
|
40
|
+
buffer = lines.pop() || '';
|
|
41
|
+
for (const line of lines) if (line.trim()) handleMessage(line);
|
|
42
|
+
});
|
|
43
|
+
child.stderr.on('data', chunk => { stderr += chunk; });
|
|
44
|
+
child.on('error', finish);
|
|
45
|
+
child.on('close', code => {
|
|
46
|
+
if (!settled) finish(codedError('codex_thread_fork_failed', stderr || `Codex app-server exited with code ${code}.`));
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
(async () => {
|
|
50
|
+
await request('initialize', {
|
|
51
|
+
clientInfo: { name: 'codex-overleaf-link', version: PACKAGE_VERSION },
|
|
52
|
+
capabilities: null
|
|
53
|
+
});
|
|
54
|
+
notify('initialized');
|
|
55
|
+
const result = await request('thread/fork', { threadId, lastTurnId });
|
|
56
|
+
const forkedThreadId = result?.thread?.id || result?.threadId || '';
|
|
57
|
+
if (!forkedThreadId || forkedThreadId === threadId) {
|
|
58
|
+
throw codedError('codex_thread_fork_invalid_response', 'Codex app-server did not return a new thread id.');
|
|
59
|
+
}
|
|
60
|
+
finish(null, { threadId: forkedThreadId, sourceThreadId: threadId, lastTurnId });
|
|
61
|
+
})().catch(finish);
|
|
62
|
+
|
|
63
|
+
function request(method, requestParams) {
|
|
64
|
+
const id = nextId++;
|
|
65
|
+
child.stdin.write(`${JSON.stringify({ id, method, params: requestParams })}\n`);
|
|
66
|
+
return new Promise((resolveRequest, rejectRequest) => pending.set(id, { resolve: resolveRequest, reject: rejectRequest }));
|
|
67
|
+
}
|
|
68
|
+
function notify(method, requestParams) {
|
|
69
|
+
child.stdin.write(`${JSON.stringify({ method, params: requestParams })}\n`);
|
|
70
|
+
}
|
|
71
|
+
function handleMessage(line) {
|
|
72
|
+
let message;
|
|
73
|
+
try { message = JSON.parse(line); } catch (_error) { return; }
|
|
74
|
+
if (Object.prototype.hasOwnProperty.call(message, 'id') && (message.result !== undefined || message.error)) {
|
|
75
|
+
const waiter = pending.get(message.id);
|
|
76
|
+
if (!waiter) return;
|
|
77
|
+
pending.delete(message.id);
|
|
78
|
+
if (message.error) waiter.reject(codedError('codex_thread_fork_failed', message.error.message || JSON.stringify(message.error)));
|
|
79
|
+
else waiter.resolve(message.result);
|
|
80
|
+
} else if (Object.prototype.hasOwnProperty.call(message, 'id') && message.method) {
|
|
81
|
+
child.stdin.write(`${JSON.stringify({ id: message.id, error: { code: -32601, message: 'Unsupported during thread fork' } })}\n`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function finish(error, result) {
|
|
85
|
+
if (settled) return;
|
|
86
|
+
settled = true;
|
|
87
|
+
clearTimeout(timeout);
|
|
88
|
+
for (const waiter of pending.values()) waiter.reject(error || codedError('codex_thread_fork_closed', 'Fork process closed.'));
|
|
89
|
+
pending.clear();
|
|
90
|
+
try { child.stdin.end(); } catch (_error) { /* best effort */ }
|
|
91
|
+
try { child.kill(); } catch (_error) { /* best effort */ }
|
|
92
|
+
if (error) reject(error); else resolve(result);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function cleanId(value, field) {
|
|
98
|
+
const id = String(value || '').trim();
|
|
99
|
+
if (!id || id.length > 160) throw codedError('codex_thread_fork_invalid', `${field} is required.`);
|
|
100
|
+
return id;
|
|
101
|
+
}
|
|
102
|
+
function codedError(code, message) { const error = new Error(message); error.code = code; return error; }
|
|
103
|
+
module.exports = { forkCodexThread };
|
|
@@ -152,34 +152,19 @@ async function collectMirrorChangesDetailed({ projectId, rootDir }) {
|
|
|
152
152
|
if (hashBytes(bytes) === previous.hash) {
|
|
153
153
|
continue;
|
|
154
154
|
}
|
|
155
|
-
if (stat.size > SAFE_INLINE_BINARY_CHANGE_BYTES) {
|
|
156
|
-
unsupportedChanges.push(buildOversizedBinaryPayloadChange({
|
|
157
|
-
filePath,
|
|
158
|
-
size: stat.size,
|
|
159
|
-
previous
|
|
160
|
-
}));
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
155
|
currentByPath.set(filePath, {
|
|
164
156
|
binary: true,
|
|
165
|
-
bytes,
|
|
157
|
+
sha256: hashBytes(bytes),
|
|
166
158
|
size: stat.size,
|
|
167
159
|
previous
|
|
168
160
|
});
|
|
169
161
|
continue;
|
|
170
162
|
}
|
|
171
163
|
if (!previous) {
|
|
172
|
-
|
|
173
|
-
unsupportedChanges.push(buildOversizedBinaryPayloadChange({
|
|
174
|
-
filePath,
|
|
175
|
-
size: stat.size,
|
|
176
|
-
previous
|
|
177
|
-
}));
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
164
|
+
const bytes = fs.readFileSync(target);
|
|
180
165
|
currentByPath.set(filePath, {
|
|
181
166
|
binary: true,
|
|
182
|
-
|
|
167
|
+
sha256: hashBytes(bytes),
|
|
183
168
|
size: stat.size,
|
|
184
169
|
previous
|
|
185
170
|
});
|
|
@@ -218,7 +203,8 @@ async function collectMirrorChangesDetailed({ projectId, rootDir }) {
|
|
|
218
203
|
changes.push({
|
|
219
204
|
type: previous ? 'overwrite-binary' : 'binary-create',
|
|
220
205
|
path: filePath,
|
|
221
|
-
|
|
206
|
+
assetSourcePath: filePath,
|
|
207
|
+
sha256: contentOrBinary.sha256,
|
|
222
208
|
previousExists: Boolean(previous),
|
|
223
209
|
previousKind: previous?.kind || '',
|
|
224
210
|
previousSize: previous?.size,
|