codex-overleaf-link 2.5.1 → 2.6.5

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.
Files changed (111) hide show
  1. package/README.md +149 -398
  2. package/README.zh-CN.md +153 -404
  3. package/extension/bootstrap/manifest.template.json +1 -1
  4. package/extension/src/background.js +148 -3
  5. package/extension/src/backgroundSharedSessions.js +55 -2
  6. package/extension/src/backgroundUpdateCoordinator.js +128 -12
  7. package/extension/src/content/activeTurnControl.js +164 -7
  8. package/extension/src/content/composerAttachments.js +46 -10
  9. package/extension/src/content/composerPanel.js +92 -12
  10. package/extension/src/content/contentRuntime.js +1114 -321
  11. package/extension/src/content/contextTray.js +8 -2
  12. package/extension/src/content/contextUsageView.js +267 -0
  13. package/extension/src/content/diagnosticsController.js +77 -34
  14. package/extension/src/content/diagnosticsExport.js +1 -1
  15. package/extension/src/content/diagnosticsPanel.js +147 -42
  16. package/extension/src/content/generated/content.bundle.js +5761 -1237
  17. package/extension/src/content/generated/content.bundle.meta.json +6 -3
  18. package/extension/src/content/legacyGlobalRegistry.js +4 -0
  19. package/extension/src/content/localSkillsPanel.js +6 -4
  20. package/extension/src/content/markdownDomRenderer.js +5 -3
  21. package/extension/src/content/markdownText.js +53 -14
  22. package/extension/src/content/modelPicker.js +29 -19
  23. package/extension/src/content/nativeCompatibilityController.js +52 -19
  24. package/extension/src/content/otWarmMirror.js +1 -1
  25. package/extension/src/content/pageBridgeClient.js +7 -7
  26. package/extension/src/content/panelMaintenance.js +10 -2
  27. package/extension/src/content/panelRenderer.js +55 -28
  28. package/extension/src/content/pendingInputView.js +1 -1
  29. package/extension/src/content/projectSettingsCoordinator.js +30 -6
  30. package/extension/src/content/providerSettingsDialog.js +28 -19
  31. package/extension/src/content/recentProjects.js +30 -19
  32. package/extension/src/content/runActivitySummary.js +122 -14
  33. package/extension/src/content/runCompletionMeta.js +194 -6
  34. package/extension/src/content/runController.js +2 -1
  35. package/extension/src/content/runFailureNotice.js +8 -1
  36. package/extension/src/content/runPresence.js +18 -8
  37. package/extension/src/content/runQueueScheduler.js +27 -13
  38. package/extension/src/content/runRecoveryActions.js +2 -1
  39. package/extension/src/content/runResultActions.js +73 -24
  40. package/extension/src/content/runReviewController.js +102 -37
  41. package/extension/src/content/runTimelineView.js +224 -25
  42. package/extension/src/content/scopedPersistencePanelState.js +124 -46
  43. package/extension/src/content/selectionContextView.js +30 -3
  44. package/extension/src/content/sensitivePreflight.js +14 -1
  45. package/extension/src/content/sessionManager.js +49 -3
  46. package/extension/src/content/sessionMenuView.js +54 -18
  47. package/extension/src/content/sessionPersistence.js +25 -5
  48. package/extension/src/content/settingsPanel.js +15 -2
  49. package/extension/src/content/settingsWorkbench.js +127 -89
  50. package/extension/src/content/sharedSessionViewSync.js +13 -4
  51. package/extension/src/content/subagentActivityView.js +23 -7
  52. package/extension/src/content/updateNotice.js +60 -6
  53. package/extension/src/content/writebackOrchestrator.js +301 -72
  54. package/extension/src/content/writingStyleSettings.js +70 -17
  55. package/extension/src/page/compileBridge.js +193 -180
  56. package/extension/src/page/overleafCapabilities.js +4 -1
  57. package/extension/src/page/overleafEditor.js +12 -3
  58. package/extension/src/page/overleafProjectSnapshot.js +56 -3
  59. package/extension/src/page/snapshotRouter.js +75 -66
  60. package/extension/src/page/textCoordinates.js +17 -1
  61. package/extension/src/page/textFileCreator.js +9 -1
  62. package/extension/src/page/treeOperations.js +200 -19
  63. package/extension/src/page/writebackRouter.js +172 -113
  64. package/extension/src/pageBridge.js +124 -73
  65. package/extension/src/popup.js +1 -1
  66. package/extension/src/shared/agentTranscript.js +372 -18
  67. package/extension/src/shared/auditRecords.js +3 -0
  68. package/extension/src/shared/compatibility.js +1 -1
  69. package/extension/src/shared/compileAdapter.js +58 -17
  70. package/extension/src/shared/composerAttachmentDraft.js +27 -7
  71. package/extension/src/shared/contextUsage.js +125 -0
  72. package/extension/src/shared/failureReasons.js +57 -3
  73. package/extension/src/shared/i18n.js +315 -29
  74. package/extension/src/shared/lineReferences.js +38 -31
  75. package/extension/src/shared/pageRpcContract.js +1 -1
  76. package/extension/src/shared/panelIcons.js +39 -0
  77. package/extension/src/shared/projectFiles.js +2 -1
  78. package/extension/src/shared/reviewing.js +130 -1
  79. package/extension/src/shared/runActivityModel.js +70 -4
  80. package/extension/src/shared/runExecutionSnapshot.js +13 -0
  81. package/extension/src/shared/scopedPersistenceBrowserAdapter.js +34 -0
  82. package/extension/src/shared/sessionState.js +63 -4
  83. package/extension/src/shared/sessionTitle.js +14 -1
  84. package/extension/src/shared/sharedSessionBridge.js +81 -13
  85. package/extension/src/shared/staleGuard.js +130 -63
  86. package/extension/src/shared/storageDb.js +35 -3
  87. package/extension/src/shared/storageMigration.js +111 -2
  88. package/extension/src/shared/storageRunActions.js +62 -8
  89. package/extension/src/shared/undoOperations.js +126 -28
  90. package/extension/src/shared/updateRuntimeIdentity.js +16 -2
  91. package/extension/src/shared/writebackSettlement.js +21 -3
  92. package/extension/styles/panel.css +1293 -549
  93. package/native-host/src/bootstrapUpdate.js +52 -16
  94. package/native-host/src/chatBridgeRequest.js +60 -6
  95. package/native-host/src/chatBridgeResponse.js +10 -1
  96. package/native-host/src/chatCompletionsBridge.js +29 -2
  97. package/native-host/src/codexPromptAssembly.js +86 -118
  98. package/native-host/src/codexSessionRunner.js +127 -17
  99. package/native-host/src/debugLog.js +27 -3
  100. package/native-host/src/index.js +42 -11
  101. package/native-host/src/localSkills.js +1 -1
  102. package/native-host/src/mirrorWorkspace.js +47 -3
  103. package/native-host/src/nativeEnvironment.js +27 -5
  104. package/native-host/src/providerContextError.js +20 -0
  105. package/native-host/src/providerRuntime.js +1 -0
  106. package/native-host/src/skills/reviewer-response/SKILL.md +65 -0
  107. package/native-host/src/taskRunnerRuntime.js +28 -8
  108. package/native-host/src/textPatch.js +473 -377
  109. package/native-host/src/updateManager.js +144 -21
  110. package/native-host/src/writingStyleRuntime.js +1 -1
  111. package/package.json +5 -8
@@ -35,6 +35,11 @@ const ACTIVATION_DELAY_MS = 125;
35
35
  const APPLYING_TRANSACTION_STATES = new Set(['activation_pending', 'applying', 'awaiting_health',
36
36
  ...BootstrapUpdate.BOOTSTRAP_STATES]);
37
37
  const CONSENT_MIGRATION_TARGET_VERSION = '1.9.4';
38
+ // Pure reads of user-owned stores that do not depend on update files. They stay
39
+ // available while a migration or its recovery is unsettled.
40
+ const READ_ONLY_DURING_UPDATE = new Set(['codex.providers.list']);
41
+ // A user-requested return to the source version of an applied update.
42
+ const USER_ROLLBACK_REASON = 'update_user_rollback';
38
43
  const UPDATE_METHODS = new Set([
39
44
  'update.status',
40
45
  'update.bootstrap',
@@ -107,7 +112,12 @@ async function handleUpdateRequest(request, options = {}) {
107
112
  case 'update.confirm':
108
113
  return okResponse(request.id, await withUpdateMutex(context, () => confirmUpdate(context, request.params || {})));
109
114
  case 'update.rollback':
110
- return okResponse(request.id, await withUpdateMutex(context, () => rollbackUpdate(context, request.params || {})));
115
+ return okResponse(request.id, await withUpdateMutex(context, () => {
116
+ if (request.params?.reasonCode === USER_ROLLBACK_REASON && !getApplyGate(options).idle) {
117
+ throw updateError('update_native_busy', 'Native host is still processing a Codex task.');
118
+ }
119
+ return rollbackUpdate(context, request.params || {});
120
+ }));
111
121
  case 'update.cancel':
112
122
  return okResponse(request.id, await cancelNetworkUpdate(context, request.params || {}));
113
123
  case 'update.revoke': {
@@ -264,7 +274,8 @@ async function recoverBootstrapTransaction(options, transactionId, token) {
264
274
  }
265
275
 
266
276
  function assertBootstrapBusinessAvailable(request, options = {}) {
267
- if (isUpdateMethod(request?.method) || ['bridge.ping', 'codex.cancel'].includes(request?.method)) return;
277
+ if (isUpdateMethod(request?.method) || ['bridge.ping', 'codex.cancel'].includes(request?.method)
278
+ || READ_ONLY_DURING_UPDATE.has(request?.method)) return;
268
279
  const context = getManagedContext(options);
269
280
  if (!context.managed) return;
270
281
  const journal = readJournal(context);
@@ -352,9 +363,14 @@ async function checkForUpdate(context, params = {}, options = {}) {
352
363
  };
353
364
  }
354
365
  const cooldowns = readJsonSafe(path.join(context.updatesRoot, COOLDOWN_FILE), {});
355
- if (Number(cooldowns[latestVersion]?.until || 0) > Date.now()) {
366
+ const cooldown = cooldowns[latestVersion];
367
+ // Read legacy user-rollback cooldowns, allowing explicit checks to bypass
368
+ // them. New user rollbacks defer browser prompts without a Native cooldown.
369
+ if (Number(cooldown?.until || 0) > Date.now()
370
+ && !(cooldown.reasonCode === USER_ROLLBACK_REASON && params.manual === true)) {
356
371
  return { managed: true, available: false, reason: 'cooldown', currentVersion, latestVersion,
357
- cooldownUntil: Number(cooldowns[latestVersion].until), etag: releaseResponse.headers.get('etag') || '' };
372
+ cooldownUntil: Number(cooldown.until), cooldownReason: cooldown.reasonCode || '',
373
+ etag: releaseResponse.headers.get('etag') || '' };
358
374
  }
359
375
 
360
376
  const manifestBytes = await fetchReleaseAsset(
@@ -393,6 +409,19 @@ async function checkForUpdate(context, params = {}, options = {}) {
393
409
  });
394
410
  return manual;
395
411
  }
412
+ try {
413
+ BootstrapUpdate.assertMigrationSource(context, manifest, currentVersion);
414
+ } catch (error) {
415
+ if (error.code !== 'update_bootstrap_source_mismatch') throw error;
416
+ // Never cached as an installable candidate: only a reinstall can replace
417
+ // startup files this release cannot migrate.
418
+ const manual = { managed: true, available: false, reason: 'manual_install_required', code: error.code,
419
+ currentVersion, latestVersion, manualInstall: {}, etag: releaseResponse.headers.get('etag') || '' };
420
+ atomicWriteJson(path.join(context.updatesRoot, MANUAL_CANDIDATE_FILE), { ...manual,
421
+ checkedAt: new Date().toISOString(), manifestBase64: manifestBytes.toString('base64'),
422
+ signatureBase64: signatureBytes.toString('base64') });
423
+ return manual;
424
+ }
396
425
  const candidate = {
397
426
  checkedAt: new Date().toISOString(),
398
427
  currentVersion,
@@ -416,8 +445,15 @@ function readManualCandidate(context, currentVersion, etag) {
416
445
  Buffer.from(manual.signatureBase64, 'base64'),
417
446
  { allowBootstrapMismatch: true }
418
447
  );
419
- if (!isNewerStableVersion(manifest.version, currentVersion)
420
- || manifest.bootstrapProtocol === BOOTSTRAP_PROTOCOL) return null;
448
+ if (!isNewerStableVersion(manifest.version, currentVersion)) return null;
449
+ if (manifest.bootstrapProtocol === BOOTSTRAP_PROTOCOL) {
450
+ // Same protocol: a hint only while the installed startup files still
451
+ // cannot be migrated by this signed release.
452
+ try { BootstrapUpdate.assertMigrationSource(context, manifest, currentVersion); return null; }
453
+ catch (error) { if (error.code !== 'update_bootstrap_source_mismatch') return null; }
454
+ return { managed: true, available: false, reason: 'manual_install_required', code: 'update_bootstrap_source_mismatch',
455
+ currentVersion, latestVersion: manifest.version, manualInstall: {}, etag };
456
+ }
421
457
  return {
422
458
  managed: true, available: false, reason: 'manual_install_required',
423
459
  currentVersion, latestVersion: manifest.version,
@@ -494,6 +530,7 @@ function authorizeUpdate(context, params = {}) {
494
530
  !isNewerStableVersion(targetVersion, activeVersion)) {
495
531
  throw updateError('update_consent_mismatch', 'Update authorization does not match the active signed candidate.');
496
532
  }
533
+ BootstrapUpdate.assertMigrationSource(context, manifest, activeVersion);
497
534
  const existing = readAuthorization(context);
498
535
  if (existing?.id === authorizationId &&
499
536
  existing.targetVersion === targetVersion &&
@@ -536,6 +573,11 @@ async function stageCandidate(context, options = {}) {
536
573
  throw updateError('update_candidate_stale', 'The staged update is no longer newer than the active version.');
537
574
  }
538
575
  const authorization = requireAuthorization(context, manifest.version, ['authorized', 'bound']);
576
+ try { BootstrapUpdate.assertMigrationSource(context, manifest, activeVersion); }
577
+ catch (error) {
578
+ if (authorization.state === 'authorized') settleAuthorization(context, authorization.id, 'revoked');
579
+ throw error;
580
+ }
539
581
  const existingJournal = readJournal(context);
540
582
  if (existingJournal?.state === 'staged' && existingJournal.targetVersion === manifest.version) {
541
583
  if (existingJournal.authorizationId !== authorization.id ||
@@ -832,14 +874,40 @@ function rollbackUpdate(context, params = {}) {
832
874
  if (journal.bootstrapMigration && !params.transactionId && journal.bootstrapMigration.phase !== 'pending') {
833
875
  throw updateError('update_transaction_mismatch', 'Bootstrap rollback must identify its update transaction.');
834
876
  }
877
+ const userRequested = params.reasonCode === USER_ROLLBACK_REASON;
878
+ // Check every precondition before writing the rolling-back state, so an
879
+ // unavailable request never turns a working installation into a recovery.
880
+ if (userRequested && (!params.transactionId
881
+ || (journal.state === 'committed' && !userRollbackAvailable(context, journal)))) {
882
+ throw updateError('update_rollback_unavailable', 'The previous version is no longer available for rollback.');
883
+ }
835
884
  const migration = Boolean(journal.bootstrapMigration);
836
885
  if (migration) writeJournal(context, { ...journal, state: 'bootstrap_rolling_back' });
837
- const rollbackEvidence = rollbackFiles(context, journal, { deferSourceActivation: migration });
886
+ let rollbackEvidence;
887
+ try {
888
+ rollbackEvidence = rollbackFiles(context, journal, { deferSourceActivation: migration });
889
+ } catch (error) {
890
+ // The incomplete state remains. The failure record bounds automatic retries
891
+ // and lets status report the reason; explicit rollback requests still run.
892
+ const failed = readJournal(context);
893
+ if (failed?.id === journal.id) {
894
+ writeJournal(context, { ...failed, updatedAt: new Date().toISOString(), rollbackFailure: {
895
+ code: safeErrorCode(error), attempts: Number(failed.rollbackFailure?.attempts || 0) + 1,
896
+ at: new Date().toISOString() } });
897
+ }
898
+ throw error;
899
+ }
838
900
  const cooldowns = readJsonSafe(path.join(context.updatesRoot, COOLDOWN_FILE), {});
839
- cooldowns[journal.targetVersion] = {
840
- until: Date.now() + 24 * 60 * 60 * 1000,
841
- reasonCode: normalizeReasonCode(params.reasonCode)
842
- };
901
+ if (userRequested) {
902
+ // The restored Native may predate manual-check exemptions. Keep its
903
+ // original check usable; the coordinator snoozes automatic prompts.
904
+ delete cooldowns[journal.targetVersion];
905
+ } else {
906
+ cooldowns[journal.targetVersion] = {
907
+ until: Date.now() + 24 * 60 * 60 * 1000,
908
+ reasonCode: normalizeReasonCode(params.reasonCode)
909
+ };
910
+ }
843
911
  atomicWriteJson(path.join(context.updatesRoot, COOLDOWN_FILE), cooldowns);
844
912
  const restoredJournal = {
845
913
  ...journal,
@@ -848,6 +916,7 @@ function rollbackUpdate(context, params = {}) {
848
916
  rolledBackAt: new Date().toISOString(),
849
917
  updatedAt: new Date().toISOString(),
850
918
  previousManifest: undefined,
919
+ rollbackFailure: undefined,
851
920
  rollbackEvidence,
852
921
  ...(migration ? { bootstrapMigration: { ...journal.bootstrapMigration, phase: 'restored',
853
922
  sourceUnchanged: rollbackEvidence.bootstrapSourceUnchanged === true },
@@ -867,6 +936,27 @@ function rollbackUpdate(context, params = {}) {
867
936
  return { transactionId: journal.id, version: journal.sourceVersion, state: 'rolled_back', restoredPair: rollbackEvidence };
868
937
  }
869
938
 
939
+ // A committed update can be returned to its source pair only while every
940
+ // source layer is still on disk: the previous runtime slot, the source Native
941
+ // version, the saved manifest and, for a Bootstrap migration, its backups.
942
+ function userRollbackAvailable(context, journal) {
943
+ if (journal?.state !== 'committed' || !parseSemver(journal.sourceVersion) || !journal.previousManifest
944
+ || readVersionPointer(context.nativeRoot, 'active-version') !== journal.targetVersion) return false;
945
+ if (journal.bootstrapMigration
946
+ && !fs.existsSync(path.join(context.updatesRoot, 'bootstrap-migrations', journal.id, 'state.json'))) return false;
947
+ // Status runs often, so this reads only the version identities. rollbackFiles
948
+ // still verifies both full source trees before and after restoring them.
949
+ const previousRuntime = path.join(context.extensionRoot, 'slots', 'previous', 'runtime');
950
+ try {
951
+ const compatibility = fs.readFileSync(path.join(previousRuntime, 'src', 'shared', 'compatibility.js'), 'utf8');
952
+ const nativePackage = readJsonSafe(path.join(context.nativeRoot, 'versions', journal.sourceVersion, 'package.json'), null);
953
+ return compatibility.match(/BUILD_TARGET_VERSION\s*=\s*['"]([^'"]+)['"]/)?.[1] === journal.sourceVersion
954
+ && nativePackage?.version === journal.sourceVersion;
955
+ } catch (_) {
956
+ return false;
957
+ }
958
+ }
959
+
870
960
  function revokeUpdate(context, params = {}) {
871
961
  const authorizationId = String(params.authorizationId || '');
872
962
  const targetVersion = String(params.targetVersion || '');
@@ -909,12 +999,19 @@ function rollbackFiles(context, journal, options = {}) {
909
999
  const runtime = path.join(context.extensionRoot, 'runtime');
910
1000
  const previousRuntime = path.join(context.extensionRoot, 'slots', 'previous', 'runtime');
911
1001
  // An early interrupted apply may still have the source in the current slot.
912
- // A missing previous slot is recoverable only when these actual bytes match
913
- // the source pair; the source manifest and markers alone are insufficient.
914
- const source = readManagedRuntimeEvidence(context, journal.sourceVersion,
915
- fs.existsSync(previousRuntime) ? previousRuntime : runtime);
1002
+ // A missing or stale previous slot is recoverable only when these actual
1003
+ // bytes match the source pair; manifest and markers alone are insufficient.
1004
+ let sourceRuntime = fs.existsSync(previousRuntime) ? previousRuntime : runtime;
1005
+ let source;
1006
+ try {
1007
+ source = readManagedRuntimeEvidence(context, journal.sourceVersion, sourceRuntime);
1008
+ } catch (error) {
1009
+ if (journal.state !== 'applying' || sourceRuntime !== previousRuntime) throw error;
1010
+ source = readManagedRuntimeEvidence(context, journal.sourceVersion, runtime);
1011
+ sourceRuntime = runtime;
1012
+ }
916
1013
  if (journal.bootstrapMigration) BootstrapUpdate.restoreBootstrap(context, journal);
917
- if (fs.existsSync(previousRuntime)) {
1014
+ if (sourceRuntime === previousRuntime) {
918
1015
  fs.rmSync(runtime, { recursive: true, force: true });
919
1016
  fs.renameSync(previousRuntime, runtime);
920
1017
  fs.rmSync(path.dirname(previousRuntime), { recursive: true, force: true });
@@ -1003,7 +1100,12 @@ function recoverAndReadStatus(context, params = {}) {
1003
1100
  }
1004
1101
  journal = BootstrapUpdate.bindBootstrapMigration(context, journal, bootstrapDependencies(context));
1005
1102
  if (BootstrapUpdate.bootstrapExpired(journal, { recoveryRequested: params.bootstrapRecovery === true, context })) {
1006
- rollbackUpdate(context, { transactionId: journal.id, expectedState: journal.state, reasonCode: 'update_bootstrap_health_timeout' });
1103
+ try {
1104
+ rollbackUpdate(context, { transactionId: journal.id, expectedState: journal.state, reasonCode: 'update_bootstrap_health_timeout' });
1105
+ } catch (_error) {
1106
+ // Recorded as rollbackFailure on the journal; status still answers so
1107
+ // the browser can show the reason instead of an unreadable state.
1108
+ }
1007
1109
  journal = readJournal(context);
1008
1110
  }
1009
1111
  if (journal?.state === 'rolled_back' || journal?.state === 'superseded') {
@@ -1027,7 +1129,18 @@ function recoverAndReadStatus(context, params = {}) {
1027
1129
  cleanupStage(journal.stageRoot);
1028
1130
  }
1029
1131
  }
1030
- return currentStatus(context, journal);
1132
+ const status = currentStatus(context, journal);
1133
+ if (journal?.state === 'rolled_back' && !journal.bootstrapMigration && status.restoredPair) {
1134
+ const authorization = readAuthorization(context);
1135
+ if (['bound', 'consumed'].includes(authorization?.state) && authorization.id === journal.authorizationId
1136
+ && authorization.transactionId === journal.id && authorization.sourceVersion === journal.sourceVersion
1137
+ && authorization.targetVersion === journal.targetVersion) {
1138
+ settleAuthorization(context, journal.authorizationId, 'revoked');
1139
+ status.authorization = publicAuthorization(readAuthorization(context));
1140
+ }
1141
+ cleanupStage(journal.stageRoot);
1142
+ }
1143
+ return status;
1031
1144
  }
1032
1145
 
1033
1146
  function finishSourceActivation(context, journal) {
@@ -1035,7 +1148,11 @@ function finishSourceActivation(context, journal) {
1035
1148
  if (!sameRuntimeEvidence(actual, journal.rollbackEvidence)) {
1036
1149
  throw updateError('update_rollback_unconfirmed', 'The recovered source files changed before activation.');
1037
1150
  }
1038
- BootstrapUpdate.bootstrapEvidence(context, journal, 'source');
1151
+ const bootstrap = BootstrapUpdate.bootstrapEvidence(context, journal, 'source');
1152
+ if (journal.bootstrapMigration
1153
+ && (!bootstrap?.digest || bootstrap.digest !== journal.rollbackEvidence?.bootstrapSha256)) {
1154
+ throw updateError('update_bootstrap_source_mismatch', 'The recovered Bootstrap bytes changed before activation.');
1155
+ }
1039
1156
  settleAuthorization(context, journal.authorizationId, 'revoked');
1040
1157
  atomicWriteText(path.join(context.nativeRoot, 'active-version'), journal.sourceVersion + '\n');
1041
1158
  const finished = { ...journal, sourceActivationPending: false };
@@ -1059,10 +1176,13 @@ function settleReinstalledUpdate(context, version, expected) {
1059
1176
  }
1060
1177
  const reinstalledPair = readManagedRuntimeEvidence(context, version);
1061
1178
  const timestamp = new Date().toISOString();
1179
+ // The installer already activated `version`; a pending switch back to the
1180
+ // source would block business requests and could repoint the launcher.
1062
1181
  writeJournal(context, {
1063
1182
  ...journal, state: 'superseded', supersededFromState: journal.state,
1064
1183
  supersededByVersion: version, supersededAt: timestamp,
1065
- updatedAt: timestamp, reasonCode: 'update_reinstalled', reinstalledPair
1184
+ updatedAt: timestamp, reasonCode: 'update_reinstalled', reinstalledPair,
1185
+ sourceActivationPending: false
1066
1186
  });
1067
1187
  const authorization = readAuthorization(context);
1068
1188
  if (authorization?.state === 'bound' && authorization.id === journal.authorizationId
@@ -1104,6 +1224,8 @@ function currentStatus(context, journal = readJournal(context)) {
1104
1224
  ...(journal.state === 'awaiting_health' && BootstrapUpdate.migrationHint(context, journal)
1105
1225
  && journal.bootstrapMigration?.phase !== 'loaded' ? { state: 'bootstrap_pending' } : {}) } : null,
1106
1226
  authorization: publicAuthorization(readAuthorization(context)),
1227
+ rollbackAvailable: userRollbackAvailable(context, journal)
1228
+ ? { transactionId: journal.id, sourceVersion: journal.sourceVersion, targetVersion: journal.targetVersion } : null,
1107
1229
  restoredPair,
1108
1230
  reinstalledPair
1109
1231
  };
@@ -1347,7 +1469,8 @@ function publicTransaction(journal) {
1347
1469
  supersededAt: journal.supersededAt,
1348
1470
  supersededByVersion: journal.supersededByVersion,
1349
1471
  supersededFromState: journal.supersededFromState,
1350
- reasonCode: journal.reasonCode || ''
1472
+ reasonCode: journal.reasonCode || '',
1473
+ rollbackFailureCode: journal.rollbackFailure?.code || ''
1351
1474
  };
1352
1475
  }
1353
1476
 
@@ -374,7 +374,7 @@ async function build({ params, env, signal, execute, providerLaunch, onControlRe
374
374
  reasoningEffort: params.reasoningEffort || '', speedTier: params.speedTier || 'standard',
375
375
  sandboxMode: 'read-only', approvalPolicy: 'never',
376
376
  loadCodexLocalSkills: false, loadCodexOverleafSkills: false,
377
- disableCodexOverleafSkillIds: ['annotated-rewrite', 'parallel-subagents'],
377
+ disableCodexOverleafSkillIds: ['annotated-rewrite', 'parallel-subagents', 'reviewer-response'],
378
378
  skillInvocation: null, projectLocalSkills: null, providerLaunch, env, signal,
379
379
  onControlReady, emit: () => {}
380
380
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-overleaf-link",
3
- "version": "2.5.1",
3
+ "version": "2.6.5",
4
4
  "description": "Cross-platform Chrome bridge that connects Codex to the active Overleaf project.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -16,13 +16,9 @@
16
16
  "agent:codex": "node scripts/codex-json-agent.mjs",
17
17
  "build:content": "node scripts/build-content-bundle.mjs",
18
18
  "check:module-graph": "node scripts/check-content-module-graph.mjs",
19
- "test": "npm run check:module-graph && node scripts/run-tests.mjs",
20
- "test:fast": "npm run check:module-graph && node scripts/run-tests.mjs --fast",
21
- "test:focus": "npm run check:module-graph && node scripts/run-tests.mjs",
22
- "check:architecture": "node scripts/check-architecture-budget.mjs --enforce-target",
23
- "benchmark:large": "node scripts/benchmark-large-project.mjs --gate",
19
+ "test": "npm run build:content && node scripts/run-tests.mjs",
24
20
  "smoke:extension": "npm run build:content && node scripts/smoke-extension.mjs",
25
- "smoke:prepare:chrome": "npm run build:content && npm run test:fast && npm run install:managed",
21
+ "smoke:prepare:chrome": "npm test && npm run install:managed",
26
22
  "verify:release": "node scripts/verify-release.mjs",
27
23
  "verify:source": "node scripts/verify-release.mjs --source-hygiene-only",
28
24
  "verify:release-artifacts": "node scripts/verify-release-artifacts.mjs",
@@ -38,7 +34,8 @@
38
34
  "uninstall:native": "node scripts/uninstall-native-host.mjs",
39
35
  "bridge": "node native-host/src/index.js",
40
36
  "verify:npm-package": "node scripts/verify-npm-package.mjs",
41
- "pack:npm": "npm run build:content && node scripts/verify-npm-package.mjs --pack"
37
+ "pack:npm": "npm run build:content && node scripts/verify-npm-package.mjs --pack",
38
+ "test:platform": "npm run build:content && node scripts/run-tests.mjs --platform"
42
39
  },
43
40
  "engines": {
44
41
  "node": ">=20"