codex-overleaf-link 2.2.1 → 2.3.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/README.md +25 -23
- package/extension/bootstrap/manifest.template.json +1 -1
- package/extension/runtime-manifest.json +5 -71
- package/extension/src/background.js +67 -63
- package/extension/src/backgroundUpdateCoordinator.js +128 -66
- package/extension/src/content/applyResultFormatters.js +2 -3
- package/extension/src/content/composerPanel.js +0 -2
- package/extension/src/content/contentRuntime.js +757 -1558
- package/extension/src/content/contextTray.js +4 -6
- package/extension/src/content/diagnosticsController.js +1 -2
- package/extension/src/content/diffReviewPanel.js +14 -13
- package/extension/src/content/generated/content.bundle.js +41605 -0
- package/extension/src/content/generated/content.bundle.meta.json +103 -0
- package/extension/src/content/legacyGlobalRegistry.js +129 -0
- package/extension/src/content/markdownText.js +17 -17
- package/extension/src/content/mathText.js +155 -9
- package/extension/src/content/modelPicker.js +1 -1
- package/extension/src/content/modelPickerSupport.js +10 -5
- package/extension/src/content/moduleRegistryKernel.js +125 -0
- package/extension/src/content/nativeCompatibilityController.js +313 -0
- package/extension/src/content/otWarmMirror.js +8 -6
- package/extension/src/content/pageBridgeClient.js +224 -0
- package/extension/src/content/panelMaintenance.js +8 -3
- package/extension/src/content/postNavigationSettlementPersistence.js +100 -0
- package/extension/src/content/projectProviderSelection.js +4 -12
- package/extension/src/content/projectSessionCleanup.js +3 -3
- package/extension/src/content/providerSettingsCoordinator.js +59 -26
- package/extension/src/content/providerSettingsDialog.js +30 -18
- package/extension/src/content/recentProjects.js +15 -23
- package/extension/src/content/runGuidanceController.js +1 -1
- package/extension/src/content/runQueueScheduler.js +129 -29
- package/extension/src/content/runSettlementPersistence.js +51 -0
- package/extension/src/content/runTimelineView.js +17 -8
- package/extension/src/content/scopedPersistenceCoordinator.js +100 -0
- package/extension/src/content/scopedPersistenceDetachedCommit.js +47 -0
- package/extension/src/content/scopedPersistencePanelState.js +111 -0
- package/extension/src/content/sessionManager.js +15 -12
- package/extension/src/content/sessionPersistence.js +67 -8
- package/extension/src/content/updateNotice.js +10 -4
- package/extension/src/content/writebackOrchestrator.js +89 -68
- package/extension/src/contentScript.js +5 -1
- package/extension/src/page/trackedChangesLifecycle.js +16 -5
- package/extension/src/page/writebackRouter.js +6 -2
- package/extension/src/pageBridge.js +47 -98
- package/extension/src/shared/agentTranscript.js +18 -8
- package/extension/src/shared/compatibility.js +1 -1
- package/extension/src/shared/governanceRules.js +1 -1
- package/extension/src/shared/i18n.js +2 -4
- package/extension/src/shared/managedUpdateProjection.js +337 -0
- package/extension/src/shared/nativeRequestIdentity.js +60 -0
- package/extension/src/shared/otText.js +0 -19
- package/extension/src/shared/pageRpcContract.js +136 -0
- package/extension/src/shared/runExecutionSnapshot.js +187 -0
- package/extension/src/shared/runExecutionSnapshotCodec.js +171 -0
- package/extension/src/shared/runInputQueue.js +81 -34
- package/extension/src/shared/scopedPersistenceBrowserAdapter.js +79 -0
- package/extension/src/shared/scopedPersistenceQueuePolicy.js +200 -0
- package/extension/src/shared/scopedPersistenceReceipt.js +60 -0
- package/extension/src/shared/scopedPersistenceTransaction.js +241 -0
- package/extension/src/shared/sessionState.js +110 -59
- package/extension/src/shared/settlementFacts.js +351 -0
- package/extension/src/shared/storageDb.js +25 -10
- package/extension/src/shared/storageMigration.js +275 -61
- package/extension/src/shared/storageSessionClaims.js +60 -0
- package/extension/src/shared/updateConsent.js +4 -0
- package/extension/src/shared/updateRevocationIntent.js +109 -0
- package/extension/src/shared/writebackEvidenceProjection.js +150 -0
- package/extension/src/shared/writebackOperationEvidence.js +153 -0
- package/extension/src/shared/writebackSettlement.js +727 -0
- package/extension/styles/panel.css +18 -8
- package/native-host/src/codexPromptAssembly.js +1 -1
- package/native-host/src/codexRuntimeIdentity.js +1 -1
- package/native-host/src/codexSessionRunner.js +7 -13
- package/native-host/src/index.js +3 -69
- package/native-host/src/managedInstall.js +50 -8
- package/native-host/src/nativeTransportEnvelope.js +148 -0
- package/native-host/src/taskRunnerRuntime.js +14 -84
- package/native-host/src/updateManager.js +36 -1
- package/package.json +13 -5
|
@@ -25,7 +25,6 @@
|
|
|
25
25
|
let appendOperationsPreview;
|
|
26
26
|
let appendPartialWritebackWarning;
|
|
27
27
|
let appendApplyResult;
|
|
28
|
-
let renderDiffReview;
|
|
29
28
|
let renderReadOnlyDiffReview;
|
|
30
29
|
let showPluginConfirm;
|
|
31
30
|
let callPageBridge;
|
|
@@ -53,11 +52,21 @@
|
|
|
53
52
|
let buildReviewingBlockedApplyResult;
|
|
54
53
|
let ensureReviewingBeforeWrite;
|
|
55
54
|
let confirmBinaryOperations;
|
|
55
|
+
let compileAdapter;
|
|
56
56
|
let filterSyncChangesByOperations;
|
|
57
57
|
let writebackController;
|
|
58
58
|
let RUN_SNAPSHOT_ZIP_TIMEOUT_MS;
|
|
59
59
|
let getState;
|
|
60
60
|
let getCurrentRunView;
|
|
61
|
+
let onMirrorRefreshSettled;
|
|
62
|
+
let writebackSettlement;
|
|
63
|
+
let injectedWritebackSettlement;
|
|
64
|
+
|
|
65
|
+
function buildSettlement(input = {}) {
|
|
66
|
+
return writebackSettlement?.settle instanceof Function
|
|
67
|
+
? writebackSettlement.settle(input)
|
|
68
|
+
: null;
|
|
69
|
+
}
|
|
61
70
|
|
|
62
71
|
async function applySyncChangesToOverleaf(syncChanges = [], project = {}, options = {}) {
|
|
63
72
|
const runMode = options.mode || getState().mode;
|
|
@@ -86,13 +95,17 @@
|
|
|
86
95
|
applyResults: [],
|
|
87
96
|
mode: runMode,
|
|
88
97
|
nextStep: tx(
|
|
89
|
-
'Switch to
|
|
90
|
-
'只有希望 Codex
|
|
98
|
+
'Switch to Auto only when you want Codex to edit files.',
|
|
99
|
+
'只有希望 Codex 修改文件时,才需要切换到自动写入。'
|
|
91
100
|
)
|
|
92
101
|
});
|
|
93
102
|
return {
|
|
94
103
|
summaryLine: assistantMessage || tx('Ask mode completed without Overleaf changes', 'Ask 模式已完成,未修改 Overleaf'),
|
|
95
104
|
hasSkippedOperations: false,
|
|
105
|
+
settlement: buildSettlement({
|
|
106
|
+
operations: [],
|
|
107
|
+
applyResult: { ok: true, applied: [], skipped: [] }
|
|
108
|
+
}),
|
|
96
109
|
audit: buildAuditSummaryFromApply({
|
|
97
110
|
operations: [],
|
|
98
111
|
resultStatus: 'ask_ignored_local_changes',
|
|
@@ -138,16 +151,23 @@
|
|
|
138
151
|
conclusion: assistantMessage || tx('Codex finished locally. There are no changes to sync back to Overleaf.', 'Codex 已完成本地处理,没有需要同步回 Overleaf 的改动。'),
|
|
139
152
|
status: runMode === 'ask' ? tr('modeAsk') : 'completed',
|
|
140
153
|
operations: [],
|
|
141
|
-
applyResults: [],
|
|
142
|
-
unchangedReason:
|
|
143
|
-
? tx('No file changes need to sync back to Overleaf.', '没有产生需要同步回 Overleaf 的文件改动。')
|
|
144
|
-
: formatUnsupportedLocalChangeSummary(unsupportedChanges),
|
|
154
|
+
applyResults: additionalSkippedEntries.length ? [{ ok: false, applied: [], skipped: additionalSkippedEntries }] : [],
|
|
155
|
+
unchangedReason: additionalSkippedEntries.find(item => item?.result?.reason)?.result.reason
|
|
156
|
+
|| (assistantMessage ? tx('No file changes need to sync back to Overleaf.', '没有产生需要同步回 Overleaf 的文件改动。') : formatUnsupportedLocalChangeSummary(unsupportedChanges)),
|
|
145
157
|
mode: runMode,
|
|
146
|
-
nextStep: tx('You can continue the conversation, or adjust @context and run again.', '可以继续追问,或调整 @context 后重新运行。')
|
|
158
|
+
nextStep: additionalSkippedEntries.length ? formatWritebackSkippedNextStep({ ok: false, applied: [], skipped: additionalSkippedEntries }) : tx('You can continue the conversation, or adjust @context and run again.', '可以继续追问,或调整 @context 后重新运行。')
|
|
147
159
|
});
|
|
148
160
|
return {
|
|
149
161
|
summaryLine: assistantMessage || tx('No changes to sync', '没有需要同步的改动'),
|
|
150
162
|
hasSkippedOperations: additionalSkippedEntries.length > 0,
|
|
163
|
+
settlement: buildSettlement({
|
|
164
|
+
operations: buildSyncApplyOperations(syncChanges, project),
|
|
165
|
+
applyResult: {
|
|
166
|
+
ok: additionalSkippedEntries.length === 0,
|
|
167
|
+
applied: [],
|
|
168
|
+
skipped: additionalSkippedEntries
|
|
169
|
+
}
|
|
170
|
+
}),
|
|
151
171
|
audit: buildAuditSummaryFromApply({
|
|
152
172
|
operations: buildSyncApplyOperations(syncChanges, project),
|
|
153
173
|
applyResults: additionalSkippedEntries.length ? [{ ok: false, applied: [], skipped: additionalSkippedEntries }] : [],
|
|
@@ -157,47 +177,6 @@
|
|
|
157
177
|
};
|
|
158
178
|
}
|
|
159
179
|
|
|
160
|
-
if (runMode === 'confirm') {
|
|
161
|
-
appendRunEvent({
|
|
162
|
-
title: tx(
|
|
163
|
-
`Local Codex produced ${operations.length} change(s). Review the diff before applying.`,
|
|
164
|
-
`本地 Codex 产生了 ${operations.length} 项改动,请查看差异后确认。`
|
|
165
|
-
),
|
|
166
|
-
status: 'running'
|
|
167
|
-
});
|
|
168
|
-
const accepted = await renderDiffReview(visibleSyncChanges);
|
|
169
|
-
if (!accepted.length) {
|
|
170
|
-
appendRunEvent({
|
|
171
|
-
title: tx('Sync cancelled: Overleaf was not modified.', '已取消同步:Overleaf 没有被修改。'),
|
|
172
|
-
status: 'completed'
|
|
173
|
-
});
|
|
174
|
-
appendCompletionReport({
|
|
175
|
-
conclusion: tx('You cancelled syncing. Local Codex changes were not written back to Overleaf.', '你取消了同步,本地 Codex 改动没有写回 Overleaf。'),
|
|
176
|
-
status: 'rejected',
|
|
177
|
-
operations: [],
|
|
178
|
-
applyResults: [],
|
|
179
|
-
mode: runMode,
|
|
180
|
-
nextStep: tx('Run the task again, or switch to Auto if you want changes synced directly.', '可以重新运行任务,或切换到自动写入后再同步。')
|
|
181
|
-
});
|
|
182
|
-
return {
|
|
183
|
-
summaryLine: tx('Sync cancelled', '已取消同步'),
|
|
184
|
-
hasSkippedOperations: additionalSkippedEntries.length > 0,
|
|
185
|
-
audit: buildAuditSummaryFromApply({
|
|
186
|
-
operations,
|
|
187
|
-
applyResults: additionalSkippedEntries.length ? [{ ok: false, applied: [], skipped: additionalSkippedEntries }] : [],
|
|
188
|
-
blockedFiles: additionalSkippedEntries.map(item => summarizeOperationForAudit(item.operation, item.result, 'blocked')),
|
|
189
|
-
resultStatus: 'rejected'
|
|
190
|
-
})
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
operations = buildSyncApplyOperations(accepted, project);
|
|
194
|
-
const acceptedPathSafety = partitionUnsafeProjectPathOperations(operations);
|
|
195
|
-
if (acceptedPathSafety.skipped.length) {
|
|
196
|
-
additionalSkippedEntries.push(...acceptedPathSafety.skipped);
|
|
197
|
-
operations = acceptedPathSafety.safe;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
180
|
const deleteOperations = operations.filter(operation => operation.type === 'delete');
|
|
202
181
|
if (deleteOperations.length) {
|
|
203
182
|
const approved = await showPluginConfirm({
|
|
@@ -239,14 +218,23 @@
|
|
|
239
218
|
conclusion: assistantMessage || tx('No approved changes were written back to Overleaf.', '没有已确认的改动写回 Overleaf。'),
|
|
240
219
|
status: runMode === 'ask' ? tr('modeAsk') : 'completed',
|
|
241
220
|
operations: [],
|
|
242
|
-
applyResults: [],
|
|
243
|
-
unchangedReason:
|
|
221
|
+
applyResults: additionalSkippedEntries.length ? [{ ok: false, applied: [], skipped: additionalSkippedEntries }] : [],
|
|
222
|
+
unchangedReason: additionalSkippedEntries.find(item => item?.result?.reason)?.result.reason
|
|
223
|
+
|| tx('No approved file changes remain to sync back to Overleaf.', '没有剩余已确认的文件改动需要同步回 Overleaf。'),
|
|
244
224
|
mode: runMode,
|
|
245
|
-
nextStep: tx('You can continue the conversation, or adjust @context and run again.', '可以继续追问,或调整 @context 后重新运行。')
|
|
225
|
+
nextStep: additionalSkippedEntries.length ? formatWritebackSkippedNextStep({ ok: false, applied: [], skipped: additionalSkippedEntries }) : tx('You can continue the conversation, or adjust @context and run again.', '可以继续追问,或调整 @context 后重新运行。')
|
|
246
226
|
});
|
|
247
227
|
return {
|
|
248
228
|
summaryLine: tx('No changes applied', '没有应用改动'),
|
|
249
229
|
hasSkippedOperations: additionalSkippedEntries.length > 0,
|
|
230
|
+
settlement: buildSettlement({
|
|
231
|
+
operations: buildSyncApplyOperations(syncChanges, project),
|
|
232
|
+
applyResult: {
|
|
233
|
+
ok: additionalSkippedEntries.length === 0,
|
|
234
|
+
applied: [],
|
|
235
|
+
skipped: additionalSkippedEntries
|
|
236
|
+
}
|
|
237
|
+
}),
|
|
250
238
|
audit: buildAuditSummaryFromApply({
|
|
251
239
|
operations: buildSyncApplyOperations(syncChanges, project),
|
|
252
240
|
applyResults: additionalSkippedEntries.length ? [{ ok: false, applied: [], skipped: additionalSkippedEntries }] : [],
|
|
@@ -283,6 +271,7 @@
|
|
|
283
271
|
'已阻止写入:未确认 Overleaf Reviewing/Track Changes'
|
|
284
272
|
),
|
|
285
273
|
hasSkippedOperations: true,
|
|
274
|
+
settlement: buildSettlement({ operations, applyResult: blocked }),
|
|
286
275
|
audit: buildAuditSummaryFromApply({
|
|
287
276
|
operations,
|
|
288
277
|
applyResults: [blocked],
|
|
@@ -342,6 +331,7 @@
|
|
|
342
331
|
// pending promise is exposed so undoRun and the next runTask barrier on
|
|
343
332
|
// it — a mirror.sync landing AFTER an undo would otherwise push the
|
|
344
333
|
// pre-undo snapshot as the local baseline.
|
|
334
|
+
const mirrorSettlementTarget = getCurrentRunView?.();
|
|
345
335
|
pendingMirrorRefresh = refreshProjectMirrorAfterWriteback(project, applied, saveVerification)
|
|
346
336
|
.catch(error => {
|
|
347
337
|
appendRunEvent({
|
|
@@ -351,12 +341,25 @@
|
|
|
351
341
|
),
|
|
352
342
|
status: 'failed'
|
|
353
343
|
});
|
|
344
|
+
return 'failed';
|
|
345
|
+
})
|
|
346
|
+
.then(state => {
|
|
347
|
+
if (typeof onMirrorRefreshSettled !== 'function') return;
|
|
348
|
+
return onMirrorRefreshSettled({
|
|
349
|
+
recordId: mirrorSettlementTarget?.recordId || '',
|
|
350
|
+
sessionId: mirrorSettlementTarget?.sessionId || '',
|
|
351
|
+
runProjectId: mirrorSettlementTarget?.runProjectId || '',
|
|
352
|
+
state: state || 'not-attempted'
|
|
353
|
+
});
|
|
354
354
|
})
|
|
355
355
|
.finally(() => {
|
|
356
356
|
pendingMirrorRefresh = null;
|
|
357
357
|
});
|
|
358
358
|
const compileSummary = appliedPaths.length
|
|
359
|
-
? await autoRecompileAfterWriteback(appliedPaths, saveVerification
|
|
359
|
+
? await autoRecompileAfterWriteback(appliedPaths, saveVerification, {
|
|
360
|
+
autoRecompile: options.autoRecompile,
|
|
361
|
+
mode: runMode
|
|
362
|
+
}).catch(error => {
|
|
360
363
|
appendRunEvent({
|
|
361
364
|
title: tx(`Post-write compile failed: ${error.message}`, `写后编译出错:${error.message}`),
|
|
362
365
|
status: 'failed'
|
|
@@ -419,6 +422,17 @@
|
|
|
419
422
|
// Existing call sites that only read `summaryLine` / `hasSkippedOperations`
|
|
420
423
|
// / `audit` are unaffected.
|
|
421
424
|
applied,
|
|
425
|
+
settlement: buildSettlement({
|
|
426
|
+
operations,
|
|
427
|
+
applyResult: applied,
|
|
428
|
+
saveVerification,
|
|
429
|
+
mirror: {
|
|
430
|
+
state: appliedPaths.length && saveVerification?.state === 'verified_saved'
|
|
431
|
+
? 'pending'
|
|
432
|
+
: 'not_started'
|
|
433
|
+
},
|
|
434
|
+
compile: compileSummary
|
|
435
|
+
}),
|
|
422
436
|
audit: buildAuditSummaryFromApply({
|
|
423
437
|
operations,
|
|
424
438
|
applyResults: [applied],
|
|
@@ -447,8 +461,8 @@
|
|
|
447
461
|
if (result?.ok === true && !result?.state) {
|
|
448
462
|
return {
|
|
449
463
|
...result,
|
|
450
|
-
ok:
|
|
451
|
-
state: '
|
|
464
|
+
ok: false,
|
|
465
|
+
state: 'verified_quiet'
|
|
452
466
|
};
|
|
453
467
|
}
|
|
454
468
|
if (result?.state === 'unknown_timeout' || result?.state === 'unavailable') {
|
|
@@ -522,10 +536,10 @@
|
|
|
522
536
|
|
|
523
537
|
async function refreshProjectMirrorAfterWriteback(project = {}, applied = {}, saveVerification = {}) {
|
|
524
538
|
if (saveVerification?.state !== 'verified_saved') {
|
|
525
|
-
return;
|
|
539
|
+
return 'not-attempted';
|
|
526
540
|
}
|
|
527
541
|
if (!Array.isArray(applied?.applied) || !applied.applied.length) {
|
|
528
|
-
return;
|
|
542
|
+
return 'not-attempted';
|
|
529
543
|
}
|
|
530
544
|
|
|
531
545
|
if (await tryConfirmMirrorWriteback(applied)) {
|
|
@@ -536,7 +550,7 @@
|
|
|
536
550
|
),
|
|
537
551
|
status: 'completed'
|
|
538
552
|
});
|
|
539
|
-
return;
|
|
553
|
+
return 'complete';
|
|
540
554
|
}
|
|
541
555
|
|
|
542
556
|
appendRunEvent({
|
|
@@ -567,7 +581,7 @@
|
|
|
567
581
|
),
|
|
568
582
|
status: 'failed'
|
|
569
583
|
});
|
|
570
|
-
return;
|
|
584
|
+
return 'failed';
|
|
571
585
|
}
|
|
572
586
|
|
|
573
587
|
const syncedProject = mergeVerifiedAppliedFiles(freshProject, project, applied);
|
|
@@ -584,7 +598,7 @@
|
|
|
584
598
|
title: tx('Local Codex workspace refreshed. The next run will start from the latest Overleaf content.', '已刷新本地 Codex workspace,下一轮会从最新 Overleaf 内容开始。'),
|
|
585
599
|
status: 'completed'
|
|
586
600
|
});
|
|
587
|
-
return;
|
|
601
|
+
return 'complete';
|
|
588
602
|
}
|
|
589
603
|
|
|
590
604
|
const message = response?.error?.message || tx('native host did not respond', 'native host 没有响应');
|
|
@@ -595,6 +609,7 @@
|
|
|
595
609
|
),
|
|
596
610
|
status: 'failed'
|
|
597
611
|
});
|
|
612
|
+
return 'failed';
|
|
598
613
|
}
|
|
599
614
|
|
|
600
615
|
function mergeVerifiedAppliedFiles(freshProject = {}, originalProject = {}, applied = {}) {
|
|
@@ -615,11 +630,12 @@
|
|
|
615
630
|
return writebackController.formatUnsupportedLocalChangeSummary(changes, getLocale());
|
|
616
631
|
}
|
|
617
632
|
|
|
618
|
-
async function autoRecompileAfterWriteback(writtenPaths = [], saveVerification = {}) {
|
|
619
|
-
if (
|
|
620
|
-
|
|
633
|
+
async function autoRecompileAfterWriteback(writtenPaths = [], saveVerification = {}, options = {}) {
|
|
634
|
+
if (options.autoRecompile === false
|
|
635
|
+
|| (options.autoRecompile === undefined && getState().autoRecompile === false)) return null;
|
|
636
|
+
if ((options.mode || getState().mode) === 'ask') return null;
|
|
621
637
|
|
|
622
|
-
const CompileAdapter =
|
|
638
|
+
const CompileAdapter = compileAdapter;
|
|
623
639
|
if (!CompileAdapter) return null;
|
|
624
640
|
|
|
625
641
|
const hasCompilableFile = writtenPaths.some(filePath => CompileAdapter.isCompilableFile(filePath));
|
|
@@ -642,7 +658,8 @@
|
|
|
642
658
|
const result = await callPageBridge('triggerCompile', {
|
|
643
659
|
preferUiClick: true,
|
|
644
660
|
waitForSaveMs: 5000,
|
|
645
|
-
requireVerifiedSave: saveVerification?.state === 'verified_saved'
|
|
661
|
+
requireVerifiedSave: saveVerification?.state === 'verified_saved',
|
|
662
|
+
runProjectId: getCurrentRunView()?.runProjectId || getCurrentProjectId()
|
|
646
663
|
});
|
|
647
664
|
if (result?.ok) {
|
|
648
665
|
let logResult = null;
|
|
@@ -650,7 +667,8 @@
|
|
|
650
667
|
logResult = await callPageBridge('getCompileLog', {
|
|
651
668
|
triggerIfStale: false,
|
|
652
669
|
maxAgeMs: 30000,
|
|
653
|
-
waitForSaveMs: 0
|
|
670
|
+
waitForSaveMs: 0,
|
|
671
|
+
runProjectId: getCurrentRunView()?.runProjectId || getCurrentProjectId()
|
|
654
672
|
});
|
|
655
673
|
} catch (_error) {
|
|
656
674
|
logResult = null;
|
|
@@ -775,7 +793,8 @@
|
|
|
775
793
|
const result = await callPageBridge('getCompileLog', {
|
|
776
794
|
triggerIfStale: true,
|
|
777
795
|
maxAgeMs: 30000,
|
|
778
|
-
waitForSaveMs: 5000
|
|
796
|
+
waitForSaveMs: 5000,
|
|
797
|
+
runProjectId: getCurrentRunView()?.runProjectId || getCurrentProjectId()
|
|
779
798
|
});
|
|
780
799
|
|
|
781
800
|
if (!result?.ok) {
|
|
@@ -807,7 +826,6 @@
|
|
|
807
826
|
appendOperationsPreview,
|
|
808
827
|
appendPartialWritebackWarning,
|
|
809
828
|
appendApplyResult,
|
|
810
|
-
renderDiffReview,
|
|
811
829
|
renderReadOnlyDiffReview,
|
|
812
830
|
showPluginConfirm,
|
|
813
831
|
callPageBridge,
|
|
@@ -841,6 +859,9 @@
|
|
|
841
859
|
getState,
|
|
842
860
|
getCurrentRunView,
|
|
843
861
|
} = deps);
|
|
862
|
+
onMirrorRefreshSettled = deps.onMirrorRefreshSettled;
|
|
863
|
+
writebackSettlement = deps.writebackSettlement;
|
|
864
|
+
compileAdapter = deps.compileAdapter;
|
|
844
865
|
return {
|
|
845
866
|
applySyncChangesToOverleaf,
|
|
846
867
|
resolveCompileLogContext,
|
|
@@ -4,5 +4,9 @@
|
|
|
4
4
|
if (!window.CodexOverleafContentRuntime || typeof window.CodexOverleafContentRuntime.init !== 'function') {
|
|
5
5
|
throw new Error('Codex Overleaf content runtime did not load.');
|
|
6
6
|
}
|
|
7
|
-
window.
|
|
7
|
+
const registry = window.CodexOverleafLegacyGlobalRegistry;
|
|
8
|
+
if (!registry || typeof registry.create !== 'function') {
|
|
9
|
+
throw new Error('Codex Overleaf dependency registry did not load.');
|
|
10
|
+
}
|
|
11
|
+
window.CodexOverleafContentRuntime.init(registry.create(window));
|
|
8
12
|
})();
|
|
@@ -971,8 +971,15 @@
|
|
|
971
971
|
}
|
|
972
972
|
|
|
973
973
|
for (const path of paths) {
|
|
974
|
-
|
|
975
|
-
|
|
974
|
+
const postContent = postByPath.get(path);
|
|
975
|
+
const activePathMatches = getActiveFilePath() === path;
|
|
976
|
+
const activeContentMatches = readActiveEditorText() === postContent;
|
|
977
|
+
// Overleaf can update the selected tree path before CodeMirror swaps its
|
|
978
|
+
// document. In that state getActiveFilePath() already names `path`, while
|
|
979
|
+
// readActiveEditorText() still belongs to the previously viewed file.
|
|
980
|
+
// Force-reopen the target whenever either identity signal disagrees.
|
|
981
|
+
if (path && (!activePathMatches || !activeContentMatches)) {
|
|
982
|
+
const opened = await openFileByPath(path, { force: true });
|
|
976
983
|
if (!opened.ok) {
|
|
977
984
|
return {
|
|
978
985
|
ok: false,
|
|
@@ -983,8 +990,7 @@
|
|
|
983
990
|
}
|
|
984
991
|
}
|
|
985
992
|
|
|
986
|
-
const
|
|
987
|
-
const postReady = await waitForActiveEditorExpectedText(path, postContent, 2500);
|
|
993
|
+
const postReady = await waitForActiveEditorExpectedText(path, postContent, 9000);
|
|
988
994
|
if (!postReady.ok) {
|
|
989
995
|
return {
|
|
990
996
|
ok: false,
|
|
@@ -1008,7 +1014,12 @@
|
|
|
1008
1014
|
result: {
|
|
1009
1015
|
ok: true,
|
|
1010
1016
|
method: 'overleaf-editor-undo',
|
|
1011
|
-
undoClicks: result.clicks
|
|
1017
|
+
undoClicks: result.clicks,
|
|
1018
|
+
// undoEditorHistoryUntilContent returns ok only after the active
|
|
1019
|
+
// editor exactly matches this run's pre-write checkpoint. Preserve
|
|
1020
|
+
// that proof for the content-side lifecycle settlement.
|
|
1021
|
+
verified: true,
|
|
1022
|
+
verifiedContent: expectedByPath.get(path)
|
|
1012
1023
|
}
|
|
1013
1024
|
});
|
|
1014
1025
|
}
|
|
@@ -1667,13 +1667,17 @@
|
|
|
1667
1667
|
continue;
|
|
1668
1668
|
}
|
|
1669
1669
|
const key = typeof ref.key === 'string' ? ref.key : '';
|
|
1670
|
-
if (!key
|
|
1670
|
+
if (!key) {
|
|
1671
1671
|
continue;
|
|
1672
1672
|
}
|
|
1673
|
-
seen.add(key);
|
|
1674
1673
|
const hasPath = typeof ref.path === 'string';
|
|
1675
1674
|
const hasExplicitPath = hasPath && ref.path.trim() !== '';
|
|
1676
1675
|
const path = hasPath ? normalizeSafeProjectPath(ref.path) : '';
|
|
1676
|
+
const identity = `${path}\u0000${key}`;
|
|
1677
|
+
if (seen.has(identity)) {
|
|
1678
|
+
continue;
|
|
1679
|
+
}
|
|
1680
|
+
seen.add(identity);
|
|
1677
1681
|
normalized.push({
|
|
1678
1682
|
key,
|
|
1679
1683
|
id: typeof ref.id === 'string' ? ref.id : '',
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const PAGE_BRIDGE_INSTALL_VERSION = window.CodexOverleafCompatibility?.BUILD_TARGET_VERSION || 'dev';
|
|
5
|
-
const
|
|
5
|
+
const pageRpcContract = window.CodexOverleafPageRpcContract;
|
|
6
|
+
const PAGE_BRIDGE_INSTALL_REVISION = pageRpcContract.REVISION;
|
|
6
7
|
if (window.__codexOverleafPageBridgeInstalledVersion === PAGE_BRIDGE_INSTALL_VERSION
|
|
7
8
|
&& window.__codexOverleafPageBridgeInstalledRevision === PAGE_BRIDGE_INSTALL_REVISION) {
|
|
8
9
|
return;
|
|
@@ -152,10 +153,7 @@
|
|
|
152
153
|
result = await dispatch(method, params || {});
|
|
153
154
|
}
|
|
154
155
|
} catch (error) {
|
|
155
|
-
result =
|
|
156
|
-
ok: false,
|
|
157
|
-
error: error.message
|
|
158
|
-
};
|
|
156
|
+
result = pageRpcContract.normalizeFailure(method, error);
|
|
159
157
|
}
|
|
160
158
|
|
|
161
159
|
window.postMessage({
|
|
@@ -187,111 +185,62 @@
|
|
|
187
185
|
return writeCancellationSequence;
|
|
188
186
|
}
|
|
189
187
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
if (method === 'cancelActiveWrite') {
|
|
188
|
+
const pageRpcHandlers = {
|
|
189
|
+
probe,
|
|
190
|
+
cancelActiveWrite() {
|
|
195
191
|
const sequence = bumpWriteCancellationSequence();
|
|
196
192
|
return { ok: true, cancelled: true, sequence };
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
if (method === 'getProjectFileList') {
|
|
202
|
-
return projectSnapshotBridge.getProjectFileList(params);
|
|
203
|
-
}
|
|
204
|
-
if (method === 'invalidateProjectSnapshot') {
|
|
193
|
+
},
|
|
194
|
+
getProjectSnapshot: params => projectSnapshotBridge.getProjectSnapshot(withSnapshotCacheIdentity(params)),
|
|
195
|
+
getProjectFileList: params => projectSnapshotBridge.getProjectFileList(params),
|
|
196
|
+
invalidateProjectSnapshot(params) {
|
|
205
197
|
projectSnapshotBridge.invalidateProjectSnapshot(params);
|
|
206
198
|
return { ok: true };
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
// Gate the Reviewing-mode toggle with the same writeGuard the per-write
|
|
213
|
-
// path uses. Without this, a mid-flight SPA navigation could leave the
|
|
214
|
-
// pre-flight toggle flipping Track Changes in a different Overleaf
|
|
215
|
-
// project than the run was bound to. params.runProjectId is set by
|
|
216
|
-
// the content-side caller (preflightWriteSafety / ensureReviewingBeforeWrite);
|
|
217
|
-
// when absent (defensive), the guard short-circuits via its own
|
|
218
|
-
// missing-runProjectId branch.
|
|
219
|
-
if (typeof params?.runProjectId === 'string' && params.runProjectId) {
|
|
220
|
-
const blocked = await writeGuard.runWriteGuard(params);
|
|
221
|
-
if (blocked) return blocked;
|
|
222
|
-
}
|
|
223
|
-
return ensureReviewing(params);
|
|
224
|
-
}
|
|
225
|
-
if (method === 'ensureEditing') {
|
|
226
|
-
if (typeof params?.runProjectId === 'string' && params.runProjectId) {
|
|
227
|
-
const blocked = await writeGuard.runWriteGuard(params);
|
|
228
|
-
if (blocked) return blocked;
|
|
229
|
-
}
|
|
230
|
-
return ensureEditing(params);
|
|
231
|
-
}
|
|
232
|
-
if (method === 'applyOperations') {
|
|
233
|
-
// Welcome-panel + write-guard:
|
|
234
|
-
// First gate: runProjectId vs page-side editorProjectId. Runs before
|
|
235
|
-
// any reviewing / save-state / open-file readiness check so a
|
|
236
|
-
// mid-run navigation cannot write into a different project. runWriteGuard
|
|
237
|
-
// is async because it retries the page-side editorProjectId reader with
|
|
238
|
-
// backoff to ride out Overleaf's hydration window.
|
|
239
|
-
const blocked = await writeGuard.runWriteGuard(params);
|
|
240
|
-
if (blocked) return blocked;
|
|
241
|
-
return applyOperations(params.operations || [], {
|
|
199
|
+
},
|
|
200
|
+
createCheckpoint: params => createCheckpoint(params.label),
|
|
201
|
+
ensureReviewing,
|
|
202
|
+
ensureEditing,
|
|
203
|
+
applyOperations: params => applyOperations(params.operations || [], {
|
|
242
204
|
baseFiles: params.baseFiles || null,
|
|
243
205
|
reviewingPolicy: params.reviewingPolicy || '',
|
|
244
206
|
requireReviewing: params.requireReviewing === true,
|
|
245
207
|
requireEditing: params.requireEditing === true,
|
|
246
|
-
// Propagate runProjectId so the writeback router's defense-in-depth
|
|
247
|
-
// missing-runProjectId check sees it on this in-bridge dispatch path.
|
|
248
208
|
runProjectId: typeof params.runProjectId === 'string' ? params.runProjectId : ''
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
209
|
+
}),
|
|
210
|
+
jumpToPosition,
|
|
211
|
+
rejectTrackedChanges,
|
|
212
|
+
acceptTrackedChanges,
|
|
213
|
+
triggerCompile: params => compileBridge.triggerCompile(params),
|
|
214
|
+
getCompileLog: params => compileBridge.getCompileLog(params),
|
|
215
|
+
getCompileState: () => compileBridge.getCompileState(),
|
|
216
|
+
waitForSaveState,
|
|
217
|
+
startOtObserver: params => otObserver.start(params),
|
|
218
|
+
stopOtObserver: () => otObserver.stop(),
|
|
219
|
+
getOtStatus: () => otObserver.getStatus(),
|
|
220
|
+
drainOtEvents() {
|
|
221
|
+
return { ok: true, events: otObserver.drainEvents().map(sanitizeOtEventForContent) };
|
|
258
222
|
}
|
|
259
|
-
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
async function dispatch(method, params) {
|
|
226
|
+
const contract = pageRpcContract.getMethod(method);
|
|
227
|
+
if (!contract || method === PAGE_BRIDGE_CAPABILITY_METHOD) {
|
|
228
|
+
return { ok: false, error: `Unknown page bridge method: ${method}` };
|
|
229
|
+
}
|
|
230
|
+
const handler = pageRpcHandlers[method];
|
|
231
|
+
if (typeof handler !== 'function') {
|
|
232
|
+
return { ok: false, error: `Page bridge handler unavailable: ${method}` };
|
|
233
|
+
}
|
|
234
|
+
const hasRunProjectId = typeof params?.runProjectId === 'string'
|
|
235
|
+
&& params.runProjectId.trim().length > 0;
|
|
236
|
+
if (
|
|
237
|
+
contract.projectIdentity === 'required'
|
|
238
|
+
|| (contract.projectIdentity === 'optional' && hasRunProjectId)
|
|
239
|
+
) {
|
|
260
240
|
const blocked = await writeGuard.runWriteGuard(params);
|
|
261
241
|
if (blocked) return blocked;
|
|
262
|
-
return acceptTrackedChanges(params);
|
|
263
|
-
}
|
|
264
|
-
if (method === 'triggerCompile') {
|
|
265
|
-
return compileBridge.triggerCompile(params);
|
|
266
|
-
}
|
|
267
|
-
if (method === 'getCompileLog') {
|
|
268
|
-
return compileBridge.getCompileLog(params);
|
|
269
|
-
}
|
|
270
|
-
if (method === 'getCompileState') {
|
|
271
|
-
return compileBridge.getCompileState();
|
|
272
|
-
}
|
|
273
|
-
if (method === 'waitForSaveState') {
|
|
274
|
-
return waitForSaveState(params);
|
|
275
242
|
}
|
|
276
|
-
|
|
277
|
-
return otObserver.start(params);
|
|
278
|
-
}
|
|
279
|
-
if (method === 'stopOtObserver') {
|
|
280
|
-
return otObserver.stop();
|
|
281
|
-
}
|
|
282
|
-
if (method === 'getOtStatus') {
|
|
283
|
-
return otObserver.getStatus();
|
|
284
|
-
}
|
|
285
|
-
if (method === 'drainOtEvents') {
|
|
286
|
-
return {
|
|
287
|
-
ok: true,
|
|
288
|
-
events: otObserver.drainEvents().map(sanitizeOtEventForContent)
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
return {
|
|
292
|
-
ok: false,
|
|
293
|
-
error: `Unknown page bridge method: ${method}`
|
|
294
|
-
};
|
|
243
|
+
return handler(params);
|
|
295
244
|
}
|
|
296
245
|
|
|
297
246
|
function sanitizeOtEventForContent(event = {}) {
|
|
@@ -468,7 +417,7 @@
|
|
|
468
417
|
reviewing: window.CodexOverleafReviewing.detectReviewingFromSignals(reviewingSignals),
|
|
469
418
|
reviewingDiagnostics: reviewingSignals.diagnostics,
|
|
470
419
|
editor,
|
|
471
|
-
capabilities: collectPageCapabilities(editor),
|
|
420
|
+
capabilities: pageRpcContract.withCapabilityReport(collectPageCapabilities(editor)),
|
|
472
421
|
editorDiagnostics: buildEditorDiagnostics(editor),
|
|
473
422
|
projectDiagnostics: {
|
|
474
423
|
internalRootKeys: collectInternalRootKeys(),
|
|
@@ -5,11 +5,14 @@
|
|
|
5
5
|
getI18n: function getI18nCjs() { return require('./i18n.js'); }
|
|
6
6
|
});
|
|
7
7
|
} else {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
root.CodexOverleafModuleRegistry.define('AgentTranscript', [
|
|
9
|
+
'FailureReasons',
|
|
10
|
+
'I18n'
|
|
11
|
+
], function createAgentTranscript(FailureReasons, I18n) {
|
|
12
|
+
return factory({
|
|
13
|
+
getFailureReasons: function getFailureReasonsWindow() { return FailureReasons; },
|
|
14
|
+
getI18n: function getI18nWindow() { return I18n; }
|
|
15
|
+
});
|
|
13
16
|
});
|
|
14
17
|
}
|
|
15
18
|
})(typeof globalThis !== 'undefined' ? globalThis : window, function agentTranscriptFactory(deps) {
|
|
@@ -724,10 +727,17 @@
|
|
|
724
727
|
if (providerError) {
|
|
725
728
|
return providerError;
|
|
726
729
|
}
|
|
727
|
-
if (/
|
|
730
|
+
if (/suggest_mode_removed|Suggest mode has been removed/i.test(text)) {
|
|
731
|
+
return {
|
|
732
|
+
conclusion: textFor(locale, '建议修改模式已经移除。', 'Suggest mode has been removed.'),
|
|
733
|
+
nextStep: textFor(locale, '请刷新扩展,然后选择“只问不改”或“自动写入”。', 'Reload the extension, then choose Ask or Auto.'),
|
|
734
|
+
failureCode: 'suggest_mode_removed'
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
if (/Mode must be "(?:confirm|ask)" or "auto"/i.test(text)) {
|
|
728
738
|
return {
|
|
729
739
|
conclusion: textFor(locale, '这轮没有写入:当前是“只问不改”,但这个任务需要写入权限。', 'No files were written: this task needs write access, but the current mode is Ask.'),
|
|
730
|
-
nextStep: textFor(locale, '
|
|
740
|
+
nextStep: textFor(locale, '请切换到“自动写入”后重新运行。', 'Switch to Auto and run the task again.'),
|
|
731
741
|
failureCode: null // no direct catalog match — preflight / governance
|
|
732
742
|
};
|
|
733
743
|
}
|
|
@@ -794,7 +804,7 @@
|
|
|
794
804
|
if (/checkpoint/i.test(text)) {
|
|
795
805
|
return {
|
|
796
806
|
conclusion: textFor(locale, '这轮没有自动写入:Codex 没有拿到可恢复版本。', 'This run did not auto-write: Codex did not get a recoverable version.'),
|
|
797
|
-
nextStep: textFor(locale, '
|
|
807
|
+
nextStep: textFor(locale, '请确认 Overleaf Reviewing 已开启后再用“自动写入”。', 'Confirm that Overleaf Reviewing is enabled before using Auto.'),
|
|
798
808
|
failureCode: null // no current catalog code; preflight reviewing issue
|
|
799
809
|
};
|
|
800
810
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
const MIN_NATIVE_VERSION = '1.0.0';
|
|
13
13
|
const MIN_COMPATIBLE_NATIVE_VERSION = '1.0.0';
|
|
14
14
|
const MIN_COMPATIBLE_EXTENSION_VERSION = '1.0.0';
|
|
15
|
-
const BUILD_TARGET_VERSION = '2.
|
|
15
|
+
const BUILD_TARGET_VERSION = '2.3.1';
|
|
16
16
|
const DEFAULT_CHROME_EXTENSION_ID = 'illdpneeeopfffmiepaejglgmhpmdhdc';
|
|
17
17
|
const REQUIRED_CAPABILITIES = Object.freeze([
|
|
18
18
|
'bridgePing',
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
}
|
|
9
9
|
module.exports = factory(projectFiles);
|
|
10
10
|
} else {
|
|
11
|
-
root.
|
|
11
|
+
root.CodexOverleafModuleRegistry.define('GovernanceRules', ['ProjectFiles'], factory);
|
|
12
12
|
}
|
|
13
13
|
})(typeof globalThis !== 'undefined' ? globalThis : window, function governanceRulesFactory(projectFiles) {
|
|
14
14
|
'use strict';
|