codex-overleaf-link 2.4.1 → 2.5.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.
Files changed (397) hide show
  1. package/README.md +275 -305
  2. package/README.zh-CN.md +290 -320
  3. package/extension/bootstrap/background.js +99 -19
  4. package/extension/bootstrap/manifest.template.json +9 -4
  5. package/extension/bootstrap/popup.html +11 -16
  6. package/extension/bootstrap/popupFallback.js +55 -0
  7. package/extension/bootstrap/update.js +25 -2
  8. package/extension/bootstrap/updateStatus.js +1 -0
  9. package/extension/runtime-manifest.json +3 -1
  10. package/extension/src/background.js +234 -56
  11. package/extension/src/backgroundBootstrapMigration.js +181 -0
  12. package/extension/src/backgroundSharedSessions.js +440 -0
  13. package/extension/src/backgroundUpdateCoordinator.js +541 -381
  14. package/extension/src/backgroundUpdateSafety.js +173 -0
  15. package/extension/src/content/activeTurnControl.js +16 -2
  16. package/extension/src/content/applyResultFormatters.js +46 -73
  17. package/extension/src/content/assetTransferBroker.js +244 -12
  18. package/extension/src/content/changeHistoryView.js +175 -0
  19. package/extension/src/content/composerAttachments.js +117 -67
  20. package/extension/src/content/composerPanel.js +41 -8
  21. package/extension/src/content/contentRuntime.js +1084 -1099
  22. package/extension/src/content/diagnosticsController.js +122 -97
  23. package/extension/src/content/diagnosticsExport.js +236 -0
  24. package/extension/src/content/diagnosticsPanel.js +78 -19
  25. package/extension/src/content/diffReviewPanel.js +8 -5
  26. package/extension/src/content/directoryCleanupRecovery.js +54 -0
  27. package/extension/src/content/generated/content.bundle.js +14680 -3606
  28. package/extension/src/content/generated/content.bundle.meta.json +30 -3
  29. package/extension/src/content/globalPreferencesController.js +1 -1
  30. package/extension/src/content/legacyGlobalRegistry.js +25 -1
  31. package/extension/src/content/localSkillsPanel.js +141 -16
  32. package/extension/src/content/markdownDomRenderer.js +9 -4
  33. package/extension/src/content/markdownText.js +5 -6
  34. package/extension/src/content/modelPicker.js +8 -5
  35. package/extension/src/content/nativeCompatibilityController.js +45 -20
  36. package/extension/src/content/otWarmMirror.js +4 -5
  37. package/extension/src/content/otWarmMirrorController.js +17 -20
  38. package/extension/src/content/pageBridgeClient.js +33 -5
  39. package/extension/src/content/panelMaintenance.js +87 -132
  40. package/extension/src/content/panelRenderer.js +10 -0
  41. package/extension/src/content/pendingInputView.js +109 -76
  42. package/extension/src/content/projectProviderSelection.js +13 -4
  43. package/extension/src/content/projectSessionCleanup.js +109 -62
  44. package/extension/src/content/projectSettingsCoordinator.js +39 -2
  45. package/extension/src/content/providerSettingsCoordinator.js +63 -14
  46. package/extension/src/content/providerSettingsDialog.js +724 -113
  47. package/extension/src/content/recentProjects.js +21 -6
  48. package/extension/src/content/runActivitySummary.js +665 -0
  49. package/extension/src/content/runCompletionMeta.js +344 -0
  50. package/extension/src/content/runController.js +40 -15
  51. package/extension/src/content/runFailureNotice.js +34 -12
  52. package/extension/src/content/runGuidanceController.js +76 -30
  53. package/extension/src/content/runGuidanceView.js +59 -10
  54. package/extension/src/content/runPresence.js +306 -0
  55. package/extension/src/content/runQueueScheduler.js +150 -43
  56. package/extension/src/content/runRecoveryActions.js +146 -0
  57. package/extension/src/content/runResultActions.js +11 -8
  58. package/extension/src/content/runReviewController.js +574 -0
  59. package/extension/src/content/runScrollLayout.js +21 -2
  60. package/extension/src/content/runTimelineView.js +315 -290
  61. package/extension/src/content/scopedPersistencePanelState.js +51 -6
  62. package/extension/src/content/selectionContextView.js +300 -0
  63. package/extension/src/content/sensitivePreflight.js +140 -0
  64. package/extension/src/content/sessionForkController.js +80 -17
  65. package/extension/src/content/sessionManager.js +1 -1
  66. package/extension/src/content/sessionPersistence.js +6 -1
  67. package/extension/src/content/settingsPanel.js +84 -55
  68. package/extension/src/content/settingsWorkbench.js +379 -0
  69. package/extension/src/content/sharedSessionViewSync.js +109 -0
  70. package/extension/src/content/subagentActivityView.js +195 -0
  71. package/extension/src/content/trackedChangeCaptureController.js +7 -3
  72. package/extension/src/content/updateIdle.js +4 -1
  73. package/extension/src/content/updateNotice.js +179 -56
  74. package/extension/src/content/updateSafetyReceiver.js +58 -0
  75. package/extension/src/content/writebackController.js +308 -2
  76. package/extension/src/content/writebackOrchestrator.js +213 -199
  77. package/extension/src/content/writebackPlan.js +231 -0
  78. package/extension/src/content/writebackRecovery.js +122 -0
  79. package/extension/src/content/writingStyleSettings.js +600 -0
  80. package/extension/src/content/writtenChangesView.js +152 -0
  81. package/extension/src/page/binaryAssetUploader.js +223 -23
  82. package/extension/src/page/fileMover.js +256 -0
  83. package/extension/src/page/overleafProjectSnapshot.js +1 -0
  84. package/extension/src/page/ownedDirectoryCleanup.js +94 -0
  85. package/extension/src/page/referenceProjects.js +149 -0
  86. package/extension/src/page/reviewContext.js +113 -0
  87. package/extension/src/page/saveState.js +20 -6
  88. package/extension/src/page/snapshotRouter.js +124 -48
  89. package/extension/src/page/textCoordinates.js +102 -0
  90. package/extension/src/page/textFileCreator.js +442 -71
  91. package/extension/src/page/trackedChangeCapture.js +53 -57
  92. package/extension/src/page/trackedChangeOwnership.js +216 -0
  93. package/extension/src/page/trackedChangeReplay.js +184 -0
  94. package/extension/src/page/trackedChangesLifecycle.js +343 -339
  95. package/extension/src/page/treeOperations.js +33 -12
  96. package/extension/src/page/updateSafetyProbe.js +41 -0
  97. package/extension/src/page/writebackReceiptJournal.js +114 -0
  98. package/extension/src/page/writebackRouter.js +242 -201
  99. package/extension/src/pageBridge.js +73 -102
  100. package/extension/src/popup.js +261 -65
  101. package/extension/src/shared/agentTranscript.js +41 -4
  102. package/extension/src/shared/auditRecords.js +73 -0
  103. package/extension/src/shared/changeHistoryModel.js +94 -0
  104. package/extension/src/shared/compatibility.js +22 -6
  105. package/extension/src/shared/compileAdapter.js +101 -37
  106. package/extension/src/shared/composerAttachmentDraft.js +151 -0
  107. package/extension/src/shared/directoryCleanupProof.js +43 -0
  108. package/extension/src/shared/failureReasons.js +329 -3
  109. package/extension/src/shared/globalPreferences.js +31 -14
  110. package/extension/src/shared/governanceRules.js +4 -37
  111. package/extension/src/shared/i18n.js +389 -25
  112. package/extension/src/shared/managedUpdateProjection.js +8 -5
  113. package/extension/src/shared/nativeRequestIdentity.js +1 -0
  114. package/extension/src/shared/pageRpcContract.js +9 -2
  115. package/extension/src/shared/projectFiles.js +21 -44
  116. package/extension/src/shared/providerProfiles.js +114 -3
  117. package/extension/src/shared/runActivityModel.js +361 -0
  118. package/extension/src/shared/runExecutionSnapshot.js +8 -2
  119. package/extension/src/shared/runExecutionSnapshotCodec.js +16 -4
  120. package/extension/src/shared/runInputQueue.js +48 -5
  121. package/extension/src/shared/scopedPersistenceBrowserAdapter.js +6 -3
  122. package/extension/src/shared/selectionContext.js +37 -0
  123. package/extension/src/shared/sessionState.js +166 -30
  124. package/extension/src/shared/settlementFacts.js +10 -2
  125. package/extension/src/shared/sharedSessionBridge.js +311 -0
  126. package/extension/src/shared/sharedSessionTransfer.js +140 -0
  127. package/extension/src/shared/staleGuard.js +118 -13
  128. package/extension/src/shared/storageDb.js +77 -78
  129. package/extension/src/shared/storageMigration.js +74 -111
  130. package/extension/src/shared/storageRunActions.js +211 -22
  131. package/extension/src/shared/storageValuePruning.js +40 -0
  132. package/extension/src/shared/subagentActivityModel.js +174 -0
  133. package/extension/src/shared/undoOperations.js +216 -6
  134. package/extension/src/shared/updateConsent.js +42 -11
  135. package/extension/src/shared/updateRuntimeIdentity.js +1 -1
  136. package/extension/src/shared/writebackIntent.js +78 -0
  137. package/extension/src/shared/writebackOperationEvidence.js +4 -15
  138. package/extension/src/shared/writebackSettlement.js +67 -48
  139. package/extension/src/shared/writingStyle.js +33 -0
  140. package/extension/styles/panel.css +1987 -385
  141. package/extension/styles/popup.css +110 -0
  142. package/native-host/src/anthropicBridgeRequest.js +6 -3
  143. package/native-host/src/anthropicBridgeResponse.js +6 -2
  144. package/native-host/src/anthropicMessagesBridge.js +24 -16
  145. package/native-host/src/bootstrapMigration.js +296 -0
  146. package/native-host/src/bootstrapMigrationContract.js +101 -0
  147. package/native-host/src/bootstrapUpdate.js +233 -0
  148. package/native-host/src/bootstrapUpdateWatchdog.js +44 -0
  149. package/native-host/src/chatBridgeResponse.js +10 -0
  150. package/native-host/src/chatCompletionsBridge.js +21 -15
  151. package/native-host/src/codexConfigPaths.js +106 -0
  152. package/native-host/src/codexHome.js +66 -73
  153. package/native-host/src/codexHomeScope.js +124 -0
  154. package/native-host/src/codexInheritedAuth.js +90 -0
  155. package/native-host/src/codexLocalSkills.js +301 -0
  156. package/native-host/src/codexModelRecovery.js +107 -0
  157. package/native-host/src/codexPromptAssembly.js +33 -12
  158. package/native-host/src/codexProviderLaunch.js +3 -3
  159. package/native-host/src/codexProviderTest.js +24 -26
  160. package/native-host/src/codexRuntimeIdentity.js +1 -0
  161. package/native-host/src/codexSessionRunner.js +211 -318
  162. package/native-host/src/codexSessionTiming.js +21 -8
  163. package/native-host/src/codexSkillIsolation.js +170 -0
  164. package/native-host/src/codexThreadFork.js +19 -3
  165. package/native-host/src/index.js +19 -7
  166. package/native-host/src/managedInstall.js +115 -39
  167. package/native-host/src/managedInstallRecovery.js +140 -0
  168. package/native-host/src/managedRuntimeEvidence.js +57 -0
  169. package/native-host/src/mirrorWorkspace.js +92 -22
  170. package/native-host/src/nativeDoctor.js +15 -0
  171. package/native-host/src/nativeEnvironment.js +70 -0
  172. package/native-host/src/nativeHelperLifecycle.js +21 -0
  173. package/native-host/src/nativeHostRegistration.js +33 -0
  174. package/native-host/src/nativeQuotas.js +15 -8
  175. package/native-host/src/nativeRunLifecycle.js +71 -0
  176. package/native-host/src/overleafWorkspaceRules.js +16 -0
  177. package/native-host/src/processStopBarrier.js +115 -0
  178. package/native-host/src/projectPathPlatform.js +30 -0
  179. package/native-host/src/providerHttp.js +80 -0
  180. package/native-host/src/providerModelCatalog.js +4 -3
  181. package/native-host/src/providerProfile.js +17 -3
  182. package/native-host/src/providerReasoning.js +10 -11
  183. package/native-host/src/providerRuntime.js +1 -0
  184. package/native-host/src/providerStore.js +76 -14
  185. package/native-host/src/releaseTransport.js +14 -5
  186. package/native-host/src/responsesPassthroughBridge.js +266 -52
  187. package/native-host/src/runtimeInstaller.js +21 -1
  188. package/native-host/src/selectionScope.js +86 -0
  189. package/native-host/src/skills/parallel-subagents/SKILL.md +31 -3
  190. package/native-host/src/skills/writing-style-builder/SKILL.md +146 -0
  191. package/native-host/src/subagentAdmission.js +26 -0
  192. package/native-host/src/subagentBroker.js +176 -159
  193. package/native-host/src/subagentTelemetry.js +325 -0
  194. package/native-host/src/subagentWorkspace.js +103 -0
  195. package/native-host/src/subagentWorkspacePath.js +34 -0
  196. package/native-host/src/taskRunnerRuntime.js +130 -57
  197. package/native-host/src/tomlStatements.js +140 -0
  198. package/native-host/src/turnAttachments.js +113 -0
  199. package/native-host/src/updateManager.js +406 -38
  200. package/native-host/src/updateNetworkControl.js +75 -0
  201. package/native-host/src/updateTrust.js +21 -2
  202. package/native-host/src/vendor/pdfjs/LICENSE +177 -0
  203. package/native-host/src/vendor/pdfjs/SOURCE.txt +3 -0
  204. package/native-host/src/vendor/pdfjs/cmaps/78-EUC-H.bcmap +0 -0
  205. package/native-host/src/vendor/pdfjs/cmaps/78-EUC-V.bcmap +0 -0
  206. package/native-host/src/vendor/pdfjs/cmaps/78-H.bcmap +0 -0
  207. package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-H.bcmap +0 -0
  208. package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-V.bcmap +0 -0
  209. package/native-host/src/vendor/pdfjs/cmaps/78-V.bcmap +0 -0
  210. package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-H.bcmap +0 -0
  211. package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-V.bcmap +0 -0
  212. package/native-host/src/vendor/pdfjs/cmaps/83pv-RKSJ-H.bcmap +0 -0
  213. package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-H.bcmap +0 -0
  214. package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-V.bcmap +0 -0
  215. package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-H.bcmap +0 -0
  216. package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-V.bcmap +0 -0
  217. package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-H.bcmap +0 -0
  218. package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-V.bcmap +0 -0
  219. package/native-host/src/vendor/pdfjs/cmaps/Add-H.bcmap +0 -0
  220. package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-H.bcmap +0 -0
  221. package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-V.bcmap +0 -0
  222. package/native-host/src/vendor/pdfjs/cmaps/Add-V.bcmap +0 -0
  223. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-0.bcmap +0 -0
  224. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-1.bcmap +0 -0
  225. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-2.bcmap +0 -0
  226. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-3.bcmap +0 -0
  227. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-4.bcmap +0 -0
  228. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-5.bcmap +0 -0
  229. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-6.bcmap +0 -0
  230. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
  231. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-0.bcmap +0 -0
  232. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-1.bcmap +0 -0
  233. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-2.bcmap +0 -0
  234. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-3.bcmap +0 -0
  235. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-4.bcmap +0 -0
  236. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-5.bcmap +0 -0
  237. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
  238. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-0.bcmap +0 -0
  239. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-1.bcmap +0 -0
  240. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-2.bcmap +0 -0
  241. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-3.bcmap +0 -0
  242. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-4.bcmap +0 -0
  243. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-5.bcmap +0 -0
  244. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-6.bcmap +0 -0
  245. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
  246. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-0.bcmap +0 -0
  247. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-1.bcmap +0 -0
  248. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-2.bcmap +0 -0
  249. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
  250. package/native-host/src/vendor/pdfjs/cmaps/B5-H.bcmap +0 -0
  251. package/native-host/src/vendor/pdfjs/cmaps/B5-V.bcmap +0 -0
  252. package/native-host/src/vendor/pdfjs/cmaps/B5pc-H.bcmap +0 -0
  253. package/native-host/src/vendor/pdfjs/cmaps/B5pc-V.bcmap +0 -0
  254. package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-H.bcmap +0 -0
  255. package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-V.bcmap +0 -0
  256. package/native-host/src/vendor/pdfjs/cmaps/CNS1-H.bcmap +0 -0
  257. package/native-host/src/vendor/pdfjs/cmaps/CNS1-V.bcmap +0 -0
  258. package/native-host/src/vendor/pdfjs/cmaps/CNS2-H.bcmap +0 -0
  259. package/native-host/src/vendor/pdfjs/cmaps/CNS2-V.bcmap +3 -0
  260. package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-H.bcmap +0 -0
  261. package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-V.bcmap +0 -0
  262. package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-H.bcmap +0 -0
  263. package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-V.bcmap +0 -0
  264. package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-H.bcmap +3 -0
  265. package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-V.bcmap +0 -0
  266. package/native-host/src/vendor/pdfjs/cmaps/EUC-H.bcmap +0 -0
  267. package/native-host/src/vendor/pdfjs/cmaps/EUC-V.bcmap +0 -0
  268. package/native-host/src/vendor/pdfjs/cmaps/Ext-H.bcmap +0 -0
  269. package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-H.bcmap +0 -0
  270. package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-V.bcmap +0 -0
  271. package/native-host/src/vendor/pdfjs/cmaps/Ext-V.bcmap +0 -0
  272. package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-H.bcmap +0 -0
  273. package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-V.bcmap +0 -0
  274. package/native-host/src/vendor/pdfjs/cmaps/GB-H.bcmap +4 -0
  275. package/native-host/src/vendor/pdfjs/cmaps/GB-V.bcmap +0 -0
  276. package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-H.bcmap +0 -0
  277. package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-V.bcmap +0 -0
  278. package/native-host/src/vendor/pdfjs/cmaps/GBK2K-H.bcmap +0 -0
  279. package/native-host/src/vendor/pdfjs/cmaps/GBK2K-V.bcmap +0 -0
  280. package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-H.bcmap +0 -0
  281. package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-V.bcmap +0 -0
  282. package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-H.bcmap +0 -0
  283. package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-V.bcmap +0 -0
  284. package/native-host/src/vendor/pdfjs/cmaps/GBT-H.bcmap +0 -0
  285. package/native-host/src/vendor/pdfjs/cmaps/GBT-V.bcmap +0 -0
  286. package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-H.bcmap +0 -0
  287. package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-V.bcmap +0 -0
  288. package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-H.bcmap +0 -0
  289. package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-V.bcmap +0 -0
  290. package/native-host/src/vendor/pdfjs/cmaps/H.bcmap +0 -0
  291. package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-H.bcmap +0 -0
  292. package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-V.bcmap +0 -0
  293. package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-H.bcmap +0 -0
  294. package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-V.bcmap +0 -0
  295. package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-H.bcmap +0 -0
  296. package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-V.bcmap +0 -0
  297. package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-H.bcmap +0 -0
  298. package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-V.bcmap +0 -0
  299. package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-H.bcmap +0 -0
  300. package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-V.bcmap +0 -0
  301. package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-H.bcmap +0 -0
  302. package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-V.bcmap +0 -0
  303. package/native-host/src/vendor/pdfjs/cmaps/Hankaku.bcmap +0 -0
  304. package/native-host/src/vendor/pdfjs/cmaps/Hiragana.bcmap +0 -0
  305. package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-H.bcmap +0 -0
  306. package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-V.bcmap +0 -0
  307. package/native-host/src/vendor/pdfjs/cmaps/KSC-H.bcmap +0 -0
  308. package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-H.bcmap +0 -0
  309. package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-V.bcmap +0 -0
  310. package/native-host/src/vendor/pdfjs/cmaps/KSC-V.bcmap +0 -0
  311. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-H.bcmap +0 -0
  312. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
  313. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
  314. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-V.bcmap +0 -0
  315. package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-H.bcmap +0 -0
  316. package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-V.bcmap +0 -0
  317. package/native-host/src/vendor/pdfjs/cmaps/Katakana.bcmap +0 -0
  318. package/native-host/src/vendor/pdfjs/cmaps/LICENSE +36 -0
  319. package/native-host/src/vendor/pdfjs/cmaps/NWP-H.bcmap +0 -0
  320. package/native-host/src/vendor/pdfjs/cmaps/NWP-V.bcmap +0 -0
  321. package/native-host/src/vendor/pdfjs/cmaps/RKSJ-H.bcmap +0 -0
  322. package/native-host/src/vendor/pdfjs/cmaps/RKSJ-V.bcmap +0 -0
  323. package/native-host/src/vendor/pdfjs/cmaps/Roman.bcmap +0 -0
  324. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-H.bcmap +0 -0
  325. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-V.bcmap +0 -0
  326. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-H.bcmap +0 -0
  327. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-V.bcmap +0 -0
  328. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-H.bcmap +0 -0
  329. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-V.bcmap +0 -0
  330. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-H.bcmap +0 -0
  331. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-V.bcmap +0 -0
  332. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-H.bcmap +0 -0
  333. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-V.bcmap +0 -0
  334. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-H.bcmap +0 -0
  335. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-V.bcmap +0 -0
  336. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-H.bcmap +0 -0
  337. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-V.bcmap +0 -0
  338. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-H.bcmap +0 -0
  339. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-V.bcmap +0 -0
  340. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-H.bcmap +0 -0
  341. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
  342. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
  343. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-V.bcmap +0 -0
  344. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-H.bcmap +0 -0
  345. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-V.bcmap +0 -0
  346. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-H.bcmap +0 -0
  347. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-V.bcmap +0 -0
  348. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-H.bcmap +0 -0
  349. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-V.bcmap +0 -0
  350. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
  351. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
  352. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
  353. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
  354. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
  355. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
  356. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
  357. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
  358. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
  359. package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
  360. package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
  361. package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
  362. package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
  363. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-H.bcmap +0 -0
  364. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-V.bcmap +0 -0
  365. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-H.bcmap +0 -0
  366. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-V.bcmap +0 -0
  367. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-H.bcmap +0 -0
  368. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-V.bcmap +0 -0
  369. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-H.bcmap +0 -0
  370. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-V.bcmap +0 -0
  371. package/native-host/src/vendor/pdfjs/cmaps/V.bcmap +0 -0
  372. package/native-host/src/vendor/pdfjs/cmaps/WP-Symbol.bcmap +0 -0
  373. package/native-host/src/vendor/pdfjs/pdf.mjs +26740 -0
  374. package/native-host/src/vendor/pdfjs/pdf.worker.mjs +62201 -0
  375. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitDingbats.pfb +0 -0
  376. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixed.pfb +0 -0
  377. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBold.pfb +0 -0
  378. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
  379. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedItalic.pfb +0 -0
  380. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerif.pfb +0 -0
  381. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBold.pfb +0 -0
  382. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
  383. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifItalic.pfb +0 -0
  384. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSymbol.pfb +0 -0
  385. package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_FOXIT +27 -0
  386. package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_LIBERATION +102 -0
  387. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Bold.ttf +0 -0
  388. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
  389. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Italic.ttf +0 -0
  390. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Regular.ttf +0 -0
  391. package/native-host/src/writingStyleEvidence.js +102 -0
  392. package/native-host/src/writingStylePdfWorker.js +35 -0
  393. package/native-host/src/writingStyleProfile.js +165 -0
  394. package/native-host/src/writingStyleRuntime.js +475 -0
  395. package/native-host/src/writingStyleStore.js +73 -0
  396. package/package.json +1 -1
  397. package/scripts/install-managed.mjs +1 -0
@@ -1,11 +1,12 @@
1
1
  (function initCodexOverleafWritebackRouter(root, factory) {
2
2
  if (typeof module === 'object' && module.exports) {
3
- module.exports = factory();
3
+ module.exports = factory(require('./treeOperations'));
4
4
  } else {
5
- root.CodexOverleafWritebackRouter = factory();
5
+ root.CodexOverleafWritebackRouter = factory(root.CodexOverleafTreeOperations);
6
6
  }
7
- })(typeof window !== 'undefined' ? window : globalThis, function writebackRouterFactory() {
7
+ })(typeof window !== 'undefined' ? window : globalThis, function writebackRouterFactory(TreeOperations) {
8
8
  'use strict';
9
+ const { fileTreeMethodNames } = TreeOperations;
9
10
 
10
11
  function create(deps = {}) {
11
12
  const window = deps.window || (typeof globalThis !== 'undefined' ? globalThis : {});
@@ -17,6 +18,7 @@
17
18
  ? Math.max(0, Number(deps.writebackOpenSettleMs))
18
19
  : 1200;
19
20
  const diagnosticsRevision = String(deps.diagnosticsRevision || '');
21
+ const now = deps.now || Date.now;
20
22
  // Cross-world cancel signal reader. Returns the current page-bridge
21
23
  // sequence number, which monotonically increments when content-side
22
24
  // calls pageBridge.cancelActiveWrite. applyOperationsCore captures the
@@ -269,6 +271,11 @@
269
271
  fallbackUserMessage: 'The edit anchor no longer matches current Overleaf content.',
270
272
  fallbackNextAction: 'Rerun the task against the current document.'
271
273
  },
274
+ write_operation_failed: {
275
+ stage: 'write', severity: 'error', defaultRetryable: true,
276
+ fallbackUserMessage: 'Editor write call failed.',
277
+ fallbackNextAction: 'Retry after the editor is stable.'
278
+ },
272
279
  write_observed_mismatch: {
273
280
  stage: 'verify', severity: 'error', defaultRetryable: false,
274
281
  fallbackUserMessage: 'Codex attempted to write, but the content read back from Overleaf did not match the approved change.',
@@ -352,7 +359,9 @@
352
359
 
353
360
  async function applyOperations(operations, options = {}) {
354
361
  if (options.reviewingPolicy === 'no-trace-undo') {
355
- return applyOperationsWithNoTraceUndo(operations, options);
362
+ const Context = window.CodexOverleafReviewContext || (typeof module === 'object' && module.exports ? require('./reviewContext') : null);
363
+ return Context.create({ window, now, readWriteCancellationSequence, getProjectId: () => treeOperations.getProjectId?.() }, options).run(
364
+ (scoped, input) => applyOperationsWithNoTraceUndo(operations, { ...input, reviewGuard: scoped.reviewGuard, reviewAwait: scoped.reviewAwait }));
356
365
  }
357
366
  const hasOperations = (operations || []).length > 0;
358
367
  const trackReviewingChanges = options.requireReviewing === true && hasOperations;
@@ -382,6 +391,7 @@
382
391
  }
383
392
  const result = await applyOperationsCore(operations, {
384
393
  ...options,
394
+ noTraceUndo: false,
385
395
  trackReviewingChanges
386
396
  });
387
397
  return reviewingPolicy
@@ -392,13 +402,16 @@
392
402
  : result;
393
403
  }
394
404
 
395
- async function confirmNoTraceUndoEditing() {
405
+ async function confirmNoTraceUndoEditing(options = {}) {
396
406
  let changed = false;
397
407
  let toggle = { ok: true, changed: false, enabled: false };
398
408
  for (let attempt = 0; attempt < 2; attempt++) {
409
+ const blocked = options.reviewGuard?.();
410
+ if (blocked) return blocked;
399
411
  if (!isEditingConfirmedForNoTraceUndo(getReviewingState({}))) {
400
412
  toggle = await setReviewingEnabled(false, { waitMs: 1800 });
401
- if (!toggle.ok) return toggle;
413
+ if (!toggle.ok && (!['editing_not_confirmed', 'reviewing_disable_failed'].includes(toggle.code)
414
+ || !isEditingConfirmedForNoTraceUndo(getReviewingState({})))) return toggle;
402
415
  changed = changed || toggle.changed === true;
403
416
  }
404
417
  await delay(180);
@@ -411,8 +424,13 @@
411
424
  reason: 'Overleaf Editing mode did not remain confirmed before undo; no undo write was started.' };
412
425
  }
413
426
 
427
+ async function awaitReview(options, work) {
428
+ try { return options.reviewAwait ? await options.reviewAwait(work) : await work(); }
429
+ catch (error) { if (error?.reviewFailure) return error.reviewFailure; throw error; }
430
+ }
431
+
414
432
  async function applyOperationsWithNoTraceUndo(operations, options = {}) {
415
- const disabled = await confirmNoTraceUndoEditing();
433
+ const disabled = await awaitReview(options, () => confirmNoTraceUndoEditing(options));
416
434
  if (!disabled.ok) return buildNoTraceUndoBlockedResult(operations, disabled);
417
435
  const result = await applyOperationsCore(operations, {
418
436
  ...options, noTraceUndo: true, trackReviewingChanges: false
@@ -434,33 +452,15 @@
434
452
  const safeBaseFiles = normalizeBaseFilesForSafety(options.baseFiles);
435
453
  const baseFileLookup = window.CodexOverleafStaleGuard?.buildBaseFileLookup(safeBaseFiles);
436
454
  const baseBinaryFileLookup = buildBaseBinaryFileLookup(safeBaseFiles);
437
- // Mid-run project-change guard. The pageBridge wrapper already ran the
438
- // writeGuard once at entry, but each per-op step here can take 0.5-5s
439
- // (openFileByPath, waitForActiveEditorText, save-state verification),
440
- // so a multi-op write can span 10-30 seconds total. If the user SPA-
441
- // navigates to a different project mid-flight, the remaining operations
442
- // would land in the wrong project. Re-check the editor project before
443
- // each op and short-circuit the rest of the queue when it diverges.
455
+ // Recheck project identity between awaited operations and before each write.
444
456
  const runProjectId = typeof options.runProjectId === 'string' ? options.runProjectId : '';
445
457
  const writeGuardSurface = window.CodexOverleafWriteGuard?.create({
446
458
  window,
447
459
  document: window.document,
448
460
  treeOperations
449
461
  });
450
- // Cross-world cancel baseline. The user clicks cancel during writeback →
451
- // content-side calls pageBridge.cancelActiveWrite → the sequence number
452
- // bumps. Two layers of responsiveness:
453
- // 1. Between-ops check — short-circuits the next iteration so the
454
- // remaining ops never start.
455
- // 2. Race the in-flight op against a cancellation poller (50ms
456
- // interval) so a click DURING a slow op (e.g. inside a 5s
457
- // waitForActiveFile poll) is observed within ~50ms instead of
458
- // having to wait out the op's internal timeout. The op promise
459
- // keeps running in the page world after the race resolves — its
460
- // eventual result is ignored. This is "user-perceived instant
461
- // cancel": the spinner stops, the run settles to cancelled, and
462
- // any DOM mutation already in flight on the op cannot be undone
463
- // from here anyway.
462
+ // Preserve the actual result of an in-flight operation so its readback and
463
+ // undo evidence survive cancellation. The checkpoints stop undispatched work.
464
464
  const cancelBaselineSequence = readWriteCancellationSequence();
465
465
  const cancelledSkipResult = {
466
466
  ok: false,
@@ -477,103 +477,78 @@
477
477
  changedDocument: false
478
478
  }
479
479
  };
480
- const cancelledInFlightResult = {
481
- ...cancelledSkipResult,
482
- reason: 'The Codex run was cancelled while an Overleaf write operation was already in flight. The editor may have changed; review the current file before starting a new run.',
483
- failure: {
484
- ...cancelledSkipResult.failure,
485
- severity: 'warning',
486
- userMessage: 'The Codex run was cancelled during an in-flight write. The editor may already contain part of that write.',
487
- nextAction: 'Review the current Overleaf file before starting a new run.',
488
- changedDocument: true
480
+ const createCandidates = operations.map(normalizeOperationPaths).filter(operation => operation.type === 'create'
481
+ && operations.filter(other => [other.path, other.to].includes(operation.path)).length === 1);
482
+ const createBatch = createCandidates.length && typeof deps.beginTextCreateBatch === 'function'
483
+ ? await deps.beginTextCreateBatch(createCandidates, { isCurrent: () =>
484
+ treeOperations.getProjectId?.() === runProjectId && readWriteCancellationSequence() === cancelBaselineSequence }) : null;
485
+ const deferredCreates = [];
486
+ let haltRemainingOperations = false;
487
+ const skipTail = (operation, result) => operations.slice(operations.indexOf(operation) + 1)
488
+ .forEach(tail => skipped.push({ operation: normalizeOperationPaths(tail), result }));
489
+ const reopenQueue = [];
490
+ const deadlineFailure = () => Number.isFinite(options.deadlineAt) && now() >= options.deadlineAt
491
+ ? { ok: false, code: 'writeback_deadline_exceeded', changedDocument: false,
492
+ reason: 'The undo time budget expired before this operation could start.' } : null;
493
+ const editOptions = () => ({ baseFileLookup, runProjectId, deadlineAt: options.deadlineAt,
494
+ reviewGuard: options.reviewGuard, reviewAwait: options.reviewAwait,
495
+ recheckWriteProject: writeGuardSurface ? () => {
496
+ const expired = options.reviewGuard?.() || deadlineFailure();
497
+ return expired ? { applied: [], skipped: [{ operation: {}, result: expired }] }
498
+ : writeGuardSurface.runWriteGuard({ runProjectId });
499
+ } : null,
500
+ trackReviewingChanges: options.trackReviewingChanges === true, noTraceUndo: options.noTraceUndo === true });
501
+ const record = (operation, result) => {
502
+ if (result.trackedChangeCapture) trackedChangeCaptures.push(result.trackedChangeCapture);
503
+ if (operation.type === 'edit' && result.ok && Array.isArray(result.trackedChanges)) {
504
+ trackedChanges.push(...result.trackedChanges);
489
505
  }
506
+ (result.ok ? applied : skipped).push({ operation, result });
507
+ options.onOperationResult?.({ operation, result });
490
508
  };
491
- // 50ms-poller racer using recursive setTimeout. setInterval is NOT
492
- // exposed in the test VM context that hosts pageBridge (only setTimeout /
493
- // clearTimeout are wired), so this implementation must avoid it. Each
494
- // tick re-schedules itself only while the racer has not resolved;
495
- // dispose() flips a flag so any tick that fires after dispose is a no-op.
496
- // Resolves the first time the cancellation sequence bumps past baseline.
497
- function createCancellationRacer() {
498
- let resolved = false;
499
- let resolveFn;
500
- const promise = new Promise(resolve => { resolveFn = resolve; });
501
- let timer = null;
502
- function tick() {
503
- if (resolved) return;
504
- if (readWriteCancellationSequence() !== cancelBaselineSequence) {
505
- resolved = true;
506
- resolveFn();
507
- return;
508
- }
509
- timer = window.setTimeout(tick, 50);
510
- }
511
- timer = window.setTimeout(tick, 50);
512
- return {
513
- promise,
514
- dispose() {
515
- resolved = true;
516
- if (timer) window.clearTimeout(timer);
517
- }
518
- };
519
- }
520
- const CANCELLED_RACE_SENTINEL = Symbol('writebackRouter.cancelled');
521
- async function raceOpAgainstCancellation(opPromise) {
522
- // Once an editor operation starts, preserve its actual result and undo
523
- // metadata. The loop's pre-operation cancellation check stops the tail.
524
- return await opPromise;
525
- }
526
509
 
527
510
  for (const rawOperation of operations) {
528
- // Cross-world cancel check. Cheap (synchronous read of a counter),
529
- // runs before the writeGuard re-check so a click-cancel that arrived
530
- // during the previous op's await microtasks short-circuits before we
531
- // burn time on guard / open-file work for the next op.
511
+ const reviewBlock = options.reviewGuard?.();
512
+ if (reviewBlock) {
513
+ skipped.push({ operation: normalizeOperationPaths(rawOperation), result: reviewBlock });
514
+ skipTail(rawOperation, reviewBlock); break;
515
+ }
516
+ // Stop undispatched work when cancellation arrives during an awaited operation.
532
517
  if (readWriteCancellationSequence() !== cancelBaselineSequence) {
533
518
  skipped.push({ operation: normalizeOperationPaths(rawOperation), result: cancelledSkipResult });
534
- const remainingForCancel = operations.slice(operations.indexOf(rawOperation) + 1);
535
- for (const tailOperation of remainingForCancel) {
536
- skipped.push({ operation: normalizeOperationPaths(tailOperation), result: cancelledSkipResult });
537
- }
519
+ skipTail(rawOperation, cancelledSkipResult);
538
520
  break;
539
521
  }
540
- // Per-op re-check: if a runProjectId was supplied and the page-side
541
- // guard is available, verify the editor still shows the same project
542
- // before committing the next write. Mismatch → push aborted_project_changed
543
- // for THIS op and every remaining op in the queue, then return.
522
+ // Re-check project ownership before this operation and preserve every skipped tail entry.
544
523
  if (runProjectId && writeGuardSurface) {
545
524
  const guardBlock = await writeGuardSurface.runWriteGuard({ runProjectId });
546
525
  if (guardBlock) {
547
- // First push the guard's structured skip for the current op so the
548
- // caller can attribute the abort to a real operation path, then
549
- // pad the remaining ops with the same failure shape.
526
+ // Keep the current path and the remaining operations attributable.
550
527
  const guardFailure = guardBlock.skipped[0].result;
551
528
  skipped.push({ operation: normalizeOperationPaths(rawOperation), result: guardFailure });
552
- const remaining = operations.slice(operations.indexOf(rawOperation) + 1);
553
- for (const tailOperation of remaining) {
554
- skipped.push({ operation: normalizeOperationPaths(tailOperation), result: guardFailure });
555
- }
529
+ skipTail(rawOperation, guardFailure);
556
530
  break;
557
531
  }
558
532
  }
559
533
  const operation = normalizeOperationPaths(rawOperation);
560
- const pathSafety = validateOperationProjectPaths(operation);
534
+ const pathSafety = deadlineFailure() || validateOperationProjectPaths(operation);
561
535
  if (!pathSafety.ok) {
562
536
  skipped.push({ operation, result: pathSafety });
563
537
  continue;
564
538
  }
565
- let raceResult;
539
+ let result;
566
540
  if (operation.type === 'edit') {
567
- raceResult = await raceOpAgainstCancellation(applyEditOperation(operation, {
568
- baseFileLookup, runProjectId,
569
- recheckWriteProject: writeGuardSurface ? () => writeGuardSurface.runWriteGuard({ runProjectId }) : null,
570
- trackReviewingChanges: options.trackReviewingChanges === true,
571
- noTraceUndo: options.noTraceUndo === true
572
- }));
541
+ result = await applyEditOperation(operation, editOptions());
542
+ if (isUntouchedOpenFailure(result)) {
543
+ reopenQueue.push({ operation, result });
544
+ continue;
545
+ }
573
546
  } else if (['binary-create', 'overwrite-binary'].includes(operation.type)) {
574
- raceResult = await raceOpAgainstCancellation(applyBinaryAssetOperation(operation, { baseFileLookup, baseBinaryFileLookup }));
547
+ result = await applyBinaryAssetOperation(operation, { baseFileLookup, baseBinaryFileLookup });
575
548
  } else if (['create', 'rename', 'move', 'delete'].includes(operation.type)) {
576
- raceResult = await raceOpAgainstCancellation(applyFileTreeOperation(operation, { baseFileLookup }));
549
+ result = await applyFileTreeOperation(operation, { baseFileLookup, runProjectId, deadlineAt: options.deadlineAt, reviewGuard: options.reviewGuard, noTraceUndo: options.noTraceUndo === true,
550
+ createBatch: createBatch?.targets.has(operation.path) ? createBatch : null,
551
+ retryCreates: options.retryCreates === true });
577
552
  } else {
578
553
  skipped.push({
579
554
  operation,
@@ -584,27 +559,42 @@
584
559
  });
585
560
  continue;
586
561
  }
587
- // Cancellation race winner: skip current op + the rest of the queue.
588
- // The op promise above keeps running in background; its eventual
589
- // result is discarded. The user perceives instant cancel because we
590
- // settle the loop here instead of waiting for the op to finish.
591
- if (raceResult === CANCELLED_RACE_SENTINEL) {
592
- skipped.push({ operation, result: cancelledInFlightResult });
593
- const remainingAfterCancel = operations.slice(operations.indexOf(rawOperation) + 1);
594
- for (const tailOperation of remainingAfterCancel) {
595
- skipped.push({ operation: normalizeOperationPaths(tailOperation), result: cancelledSkipResult });
596
- }
597
- break;
562
+ if (operation.type === 'create' && createBatch?.pending.has(operation.path)
563
+ && !createBatch.blocked.has(operation.path)) {
564
+ deferredCreates.push({ operation, result });
565
+ continue;
598
566
  }
599
- const result = raceResult;
600
- if (result.trackedChangeCapture) trackedChangeCaptures.push(result.trackedChangeCapture);
601
- if (operation.type === 'edit' && result.ok && Array.isArray(result.trackedChanges)) {
602
- trackedChanges.push(...result.trackedChanges);
567
+ record(operation, result);
568
+ if (operation.type === 'move' && operation.moveProof && !result.ok) {
569
+ haltRemainingOperations = true;
570
+ skipTail(rawOperation, { ok: false, code: 'file_plan_not_completed', changedDocument: false,
571
+ reason: 'The file move did not finish; the remaining file plan was not started.' }); break;
603
572
  }
604
- (result.ok ? applied : skipped).push({ operation, result });
605
573
  }
606
574
 
607
- if (applied.length > 0) {
575
+ // A file that could not be opened mid-batch usually opens once the
576
+ // previous document has settled: give untouched open failures one pass.
577
+ if (!haltRemainingOperations && reopenQueue.length && readWriteCancellationSequence() === cancelBaselineSequence
578
+ && !skipped.some(entry => entry.result?.code === 'aborted_project_changed')) {
579
+ await delay(deps.reopenRetryDelayMs ?? 1200);
580
+ }
581
+ for (const entry of reopenQueue) {
582
+ const blocked = options.reviewGuard?.() || haltRemainingOperations || readWriteCancellationSequence() !== cancelBaselineSequence
583
+ || skipped.some(item => item.result?.code === 'aborted_project_changed')
584
+ || Boolean(runProjectId && writeGuardSurface && await writeGuardSurface.runWriteGuard({ runProjectId }));
585
+ const result = blocked ? entry.result : await applyEditOperation(entry.operation, editOptions());
586
+ record(entry.operation, result);
587
+ }
588
+
589
+ if (deferredCreates.length) {
590
+ const confirmed = await deps.finishTextCreateBatch(createBatch, deferredCreates, applied);
591
+ for (const entry of confirmed) {
592
+ (entry.result.ok ? applied : skipped).push(entry);
593
+ if (entry.result.ok) recordFileTreeOperationSuccess(entry.operation, baseFileLookup);
594
+ options.onOperationResult?.(entry);
595
+ }
596
+ }
597
+ if (applied.some(entry => entry.result?.changedDocument !== false)) {
608
598
  compileBridge.markSourceEdited();
609
599
  }
610
600
 
@@ -612,44 +602,26 @@
612
602
  ok: skipped.length === 0,
613
603
  applied,
614
604
  skipped,
605
+ ...(haltRemainingOperations ? { haltRemainingOperations: true } : {}),
615
606
  trackedChanges: trackedChangeCaptures.some(capture => capture.state !== 'observed') ? [] : mergeTrackedChangeRefs(trackedChanges),
616
607
  trackedChangeCaptures
617
608
  };
618
609
  }
619
610
 
620
-
621
-
622
-
623
-
624
-
625
-
626
-
627
-
628
-
629
-
630
-
631
-
632
-
633
-
634
-
635
-
636
-
637
-
638
-
639
-
640
-
641
-
642
-
611
+ const isUntouchedOpenFailure = result => result?.ok === false && result.failure?.code === 'target_file_open_failed'
612
+ && result.failure.changedDocument === false;
643
613
 
644
614
  async function applyEditOperation(operation, options = {}) {
615
+ const reviewBlock = options.reviewGuard?.();
616
+ if (reviewBlock) return reviewBlock;
645
617
  const currentPath = getActiveFilePath();
646
- const editorReady = await ensureEditorReadyForOperation(operation, options.baseFileLookup, currentPath);
618
+ const editorReady = await awaitReview(options, () => ensureEditorReadyForOperation(operation, options.baseFileLookup, currentPath));
647
619
  if (!editorReady.ok) {
648
620
  return editorReady;
649
621
  }
650
622
 
651
623
  if (options.noTraceUndo === true) {
652
- const editing = await confirmNoTraceUndoEditing();
624
+ const editing = await awaitReview(options, () => confirmNoTraceUndoEditing(options));
653
625
  if (!editing.ok) return buildNoTraceUndoBlockedResult([operation], editing).skipped[0].result;
654
626
  }
655
627
  const trackReviewingChanges = options.trackReviewingChanges === true;
@@ -667,7 +639,8 @@
667
639
  options.baseFileLookup
668
640
  ) || { ok: true };
669
641
  if (!freshness.ok) {
670
- const ready = await waitForFreshEditorTextForOperation(operation, options.baseFileLookup, 1500);
642
+ const ready = await awaitReview(options, () => waitForFreshEditorTextForOperation(operation, options.baseFileLookup, 1500));
643
+ if (options.reviewGuard?.()) return options.reviewGuard();
671
644
  if (!ready.ok) {
672
645
  const decorated = attachWritebackDebug(freshness, 'stale_guard', operation, options.baseFileLookup, current, {
673
646
  initialActivePath: currentPath,
@@ -707,8 +680,11 @@
707
680
  }
708
681
 
709
682
  let nextContent = null;
683
+ let preparedPatches;
710
684
  if (Array.isArray(operation.patches) && operation.patches.length) {
711
- const patched = applyTextPatches(current, operation.patches);
685
+ preparedPatches = window.CodexOverleafStaleGuard?.prepareOperationPatches(operation, current, options.baseFileLookup)
686
+ || { ok: true, patches: operation.patches };
687
+ const patched = preparedPatches.ok ? applyTextPatches(current, preparedPatches.patches) : preparedPatches;
712
688
  if (!patched.ok) {
713
689
  const decorated = attachWritebackDebug(patched, 'apply_text_patches', operation, options.baseFileLookup, current, {
714
690
  initialActivePath: currentPath,
@@ -771,19 +747,34 @@
771
747
  reason: 'Overleaf left Editing mode before the undo write.' }).skipped[0].result;
772
748
  }
773
749
 
750
+ const finalReviewBlock = options.reviewGuard?.();
751
+ if (finalReviewBlock) return finalReviewBlock;
774
752
  const result = Array.isArray(operation.patches) && operation.patches.length
775
- ? replaceActiveEditorPatches(operation.patches, nextContent)
753
+ ? replaceActiveEditorPatches(preparedPatches.patches, nextContent)
776
754
  : replaceActiveEditorText(nextContent);
777
755
  if (!result.ok) {
778
756
  return result;
779
757
  }
780
758
 
781
- const verified = await verifyActiveEditorText(nextContent, operation.path);
759
+ const verifyWaitMs = Math.min(deps.writeVerifyWaitMs ?? 2500, Number.isFinite(options.deadlineAt)
760
+ ? Math.max(0, options.deadlineAt - now()) : Infinity);
761
+ const verified = await verifyActiveEditorText(nextContent, operation.path, verifyWaitMs, current);
782
762
  if (!verified.ok) {
783
763
  const decorated = attachWritebackDebug(verified, 'write_verification', operation, options.baseFileLookup, readActiveEditorText(), {
784
764
  initialActivePath: currentPath,
785
765
  editorReadyDebug: editorReady.debug || null
786
766
  });
767
+ const evidence = { originalCode: verified.code || '', writeMethod: result.method || '', writeStarted: true,
768
+ ...verified.evidence };
769
+ if (verified.evidence?.observedIsBefore && verified.evidence.activeMatchesTarget) {
770
+ // The editor still holds exactly the pre-write text: the write never landed, so a retry is safe.
771
+ decorated.failure = buildPageFailure('write_operation_failed', {
772
+ file: operation?.path || '', operationType: operation?.type || 'edit', changedDocument: false,
773
+ userMessage: `Overleaf did not accept the edit to ${operation?.path || 'the target file'}; the document was left unchanged.`,
774
+ technicalMessage: verified.reason || '', evidence
775
+ });
776
+ return decorated;
777
+ }
787
778
  // §9.3 write_observed_mismatch: the write call landed but the readback
788
779
  // does not match. changedDocument:true — the editor state moved, just
789
780
  // not as Codex expected.
@@ -793,12 +784,7 @@
793
784
  changedDocument: true,
794
785
  userMessage: `${operation?.path || 'The target file'} did not read back the content Codex wrote; the document state may differ from what was approved.`,
795
786
  technicalMessage: verified.reason || '',
796
- evidence: {
797
- originalCode: verified.code || '',
798
- expectedLength: verified.expectedLength,
799
- actualLength: verified.actualLength,
800
- writeStarted: true
801
- }
787
+ evidence
802
788
  });
803
789
  return decorated;
804
790
  }
@@ -808,12 +794,12 @@
808
794
  // Overleaf renders review markers asynchronously after the editor text
809
795
  // has already changed. A single 120 ms snapshot intermittently missed
810
796
  // those markers, leaving a Reviewing write with Undo but no Accept.
811
- // Poll both paths: normal Reviewing writes get a shorter capture window,
812
- // while Accept replay keeps the longer safety window used to prove that
813
- // it did not create fresh tracked changes.
797
+ // Complete normal Track attribution while this file is still active.
798
+ // Slow batches are covered by request receipts; Accept replay retains
799
+ // its independent safety window proving that no new tracking was added.
814
800
  const trackedDiff = trackReviewingChanges
815
- ? await captureTrackedWrite({ trackedBefore, captureBaseline, operation, beforeContent: current,
816
- postContent: nextContent, runProjectId: options.runProjectId })
801
+ ? await captureTrackedWrite({ trackedBefore, captureBaseline, operation: { ...operation, patches: preparedPatches?.patches }, beforeContent: current,
802
+ postContent: nextContent, runProjectId: options.runProjectId, waitMs: deps.trackCaptureWaitMs ?? 35000 })
817
803
  : await waitForTrackedChangeDiff(trackedBefore, operationPaths, {
818
804
  waitMs: 3600, intervalMs: 180, stopOnFirst: true
819
805
  });
@@ -848,7 +834,7 @@
848
834
  window.CodexOverleafStaleGuard?.updateExpectedFileContent(
849
835
  options.baseFileLookup,
850
836
  operation.path,
851
- nextContent
837
+ preparedPatches?.sourceContent ?? nextContent
852
838
  );
853
839
  return {
854
840
  ...result,
@@ -859,8 +845,7 @@
859
845
  };
860
846
  }
861
847
 
862
-
863
- async function verifyActiveEditorText(expected, filePath, waitMs = 1000) {
848
+ async function verifyActiveEditorText(expected, filePath, waitMs = 1000, before) {
864
849
  const deadline = Date.now() + waitMs;
865
850
  let actual = readActiveEditorText();
866
851
  while (actual !== expected && Date.now() < deadline) {
@@ -877,10 +862,37 @@
877
862
  code: 'write_verification_failed',
878
863
  reason: `${filePath || '当前文件'} 写入后读回内容和 Codex 预期不一致,已停止把这次操作标记为成功。请刷新 Overleaf 后重试。`,
879
864
  expectedLength: String(expected || '').length,
880
- actualLength: String(actual || '').length
865
+ actualLength: String(actual || '').length,
866
+ evidence: describeReadbackMismatch(expected, actual, before, filePath)
881
867
  };
882
868
  }
883
869
 
870
+ // Content-free mismatch evidence: offsets, lengths and short hashes only.
871
+ function describeReadbackMismatch(expected, actual, before, filePath) {
872
+ const left = String(expected ?? ''), right = String(actual ?? '');
873
+ let firstDiff = 0;
874
+ while (firstDiff < left.length && left[firstDiff] === right[firstDiff]) firstDiff += 1;
875
+ let tail = 0;
876
+ while (tail < left.length - firstDiff && tail < right.length - firstDiff
877
+ && left[left.length - 1 - tail] === right[right.length - 1 - tail]) tail += 1;
878
+ const activePath = getActiveFilePath() || '';
879
+ return { expectedLength: left.length, actualLength: right.length, firstDiffOffset: firstDiff,
880
+ expectedDiffLength: left.length - firstDiff - tail, actualDiffLength: right.length - firstDiff - tail,
881
+ expectedHash: shortHash(left), actualHash: shortHash(right),
882
+ beforeHash: typeof before === 'string' ? shortHash(before) : '',
883
+ observedIsBefore: typeof before === 'string' && right === before,
884
+ activePath, activeMatchesTarget: !filePath || activePath === filePath };
885
+ }
886
+
887
+ function shortHash(value) {
888
+ let hash = 0x811c9dc5;
889
+ for (let index = 0; index < value.length; index += 1) {
890
+ hash ^= value.charCodeAt(index);
891
+ hash = Math.imul(hash, 0x01000193) >>> 0;
892
+ }
893
+ return hash.toString(16).padStart(8, '0');
894
+ }
895
+
884
896
  async function ensureEditorReadyForOperation(operation, baseFileLookup, initialActivePath) {
885
897
  if (!operation?.path) {
886
898
  return {
@@ -1158,7 +1170,6 @@
1158
1170
  };
1159
1171
  }
1160
1172
 
1161
-
1162
1173
  async function applyBinaryAssetOperation(operation, options = {}) {
1163
1174
  const freshness = await checkBinaryAssetFreshness(operation, options);
1164
1175
  if (!freshness.ok) {
@@ -1328,9 +1339,7 @@
1328
1339
  return baseFile.size == null || currentSize == null || baseFile.size === currentSize;
1329
1340
  }
1330
1341
 
1331
- function normalizeBase64(value) {
1332
- return String(value || '').replace(/\s+/g, '');
1333
- }
1342
+ const normalizeBase64 = value => String(value || '').replace(/\s+/g, '');
1334
1343
 
1335
1344
  function decodeBase64Bytes(contentBase64) {
1336
1345
  try {
@@ -1376,13 +1385,37 @@
1376
1385
  }
1377
1386
 
1378
1387
  async function applyFileTreeOperation(operation, options = {}) {
1388
+ if (operation.type === 'delete' && operation.undoCreatedFile) {
1389
+ return applyCreatedFileUndo(operation, options);
1390
+ }
1379
1391
  const initialProjectId = treeOperations.getProjectId?.();
1380
1392
  const initialCancellationSequence = readWriteCancellationSequence();
1393
+ if (operation.type === 'move' && operation.moveProof) {
1394
+ if (options.noTraceUndo !== true) return { ok: false, code: 'file_tree_controls_unavailable', changedDocument: false,
1395
+ reason: 'Native file moves are available only to undo a previously recorded move.' };
1396
+ if (typeof deps.moveFile !== 'function') return { ok: false, code: 'file_move_unavailable', changedDocument: false,
1397
+ reason: 'Verified native file moves are unavailable. Reload the extension and Overleaf.' };
1398
+ const result = await deps.moveFile(operation, { noTraceUndo: true, runProjectId: options.runProjectId, deadlineAt: options.deadlineAt,
1399
+ isCurrent: () => treeOperations.getProjectId?.() === initialProjectId
1400
+ && readWriteCancellationSequence() === initialCancellationSequence && !options.reviewGuard?.() });
1401
+ if (result.ok) recordFileTreeOperationSuccess(operation, options.baseFileLookup);
1402
+ return result;
1403
+ }
1404
+ if (operation.type === 'create' && options.createBatch && typeof deps.createTextFile === 'function') {
1405
+ if (options.baseFileLookup?.has(operation.path) && options.retryCreates !== true) {
1406
+ return checkFileTreeOperationFreshness(operation, options.baseFileLookup);
1407
+ }
1408
+ return deps.createTextFile(operation, { batch: options.createBatch,
1409
+ allowExistingMatches: options.retryCreates === true,
1410
+ isCurrent: () => treeOperations.getProjectId?.() === initialProjectId
1411
+ && readWriteCancellationSequence() === initialCancellationSequence && !options.reviewGuard?.() });
1412
+ }
1381
1413
  const freshness = await checkFileTreeOperationFreshness(operation, options.baseFileLookup);
1382
1414
  if (!freshness.ok) {
1383
1415
  return freshness;
1384
1416
  }
1385
1417
 
1418
+ const ownerBlocked = options.reviewGuard?.(); if (ownerBlocked) return ownerBlocked;
1386
1419
  const manager = findFileTreeManager();
1387
1420
  const methodNames = fileTreeMethodNames(operation.type);
1388
1421
 
@@ -1429,14 +1462,14 @@
1429
1462
  const nativeOperation = { create: deps.createTextFile, delete: deps.deleteTextFile }[operation.type];
1430
1463
  if (typeof nativeOperation === 'function' && (operation.type === 'delete' || typeof operation.content === 'string')) {
1431
1464
  const isCurrent = () => Boolean(initialProjectId) && treeOperations.getProjectId?.() === initialProjectId
1432
- && readWriteCancellationSequence() === initialCancellationSequence;
1465
+ && readWriteCancellationSequence() === initialCancellationSequence && !options.reviewGuard?.();
1433
1466
  const created = await nativeOperation(operation, { isCurrent, expectedContent: options.baseFileLookup?.get(operation.path) });
1434
1467
  if (!created.ok) return created;
1435
1468
  if (!isCurrent() || created.verified !== true || created.verification !== 'overleaf-zip') {
1436
1469
  return { ...fileTreeVerificationFailed(operation, 'Server receipt is missing or stale.'), changedDocument: true };
1437
1470
  }
1438
1471
  recordFileTreeOperationSuccess(operation, options.baseFileLookup);
1439
- return { ok: true, method: created.method, verified: true };
1472
+ return { ...created, ok: true, method: created.method, verified: true };
1440
1473
  }
1441
1474
  return {
1442
1475
  ok: false,
@@ -1445,6 +1478,43 @@
1445
1478
  };
1446
1479
  }
1447
1480
 
1481
+ async function applyCreatedFileUndo(operation, options = {}) {
1482
+ const projectId = treeOperations.getProjectId?.();
1483
+ const cancellation = readWriteCancellationSequence();
1484
+ const proof = operation.undoCreatedFile;
1485
+ const expected = options.baseFileLookup?.get(operation.path);
1486
+ const text = proof?.v === 1 && proof.kind === 'text' && typeof expected === 'string';
1487
+ const binary = proof?.v === 1 && proof.kind === 'binary' && /^[a-f0-9]{64}$/i.test(proof.sha256 || '');
1488
+ const isCurrent = () => Boolean(projectId) && projectId === options.runProjectId
1489
+ && treeOperations.getProjectId?.() === projectId && readWriteCancellationSequence() === cancellation && !options.reviewGuard?.();
1490
+ if (!isCurrent() || (!text && !binary) || typeof deps.deleteTextFile !== 'function') {
1491
+ return { ok: false, code: 'undo_operation_failed', changedDocument: false,
1492
+ reason: 'Verified created-file deletion is unavailable for this run.' };
1493
+ }
1494
+ if (text && projectPathExists(operation.path)) {
1495
+ const current = await readCurrentTextFileForFreshness(operation.path);
1496
+ if (!current.ok || current.text !== expected || !isCurrent()) {
1497
+ return { ok: false, code: 'undo_operation_failed', changedDocument: false,
1498
+ reason: 'The created file changed after this run and was left untouched.' };
1499
+ }
1500
+ }
1501
+ const result = await deps.deleteTextFile(operation, {
1502
+ isCurrent, undoCreatedFile: true, deadlineAt: options.deadlineAt,
1503
+ expectedContent: text ? expected : undefined,
1504
+ prepareTextPreimage: text ? () => readCurrentTextFileForFreshness(operation.path) : undefined,
1505
+ expectedSha256: binary ? proof.sha256.toLowerCase() : undefined,
1506
+ canDelete: () => isCurrent() && (binary || (treeOperations.getActiveFilePath?.() === operation.path
1507
+ && readActiveEditorText() === expected))
1508
+ });
1509
+ if (!result.ok) return result;
1510
+ if (!isCurrent() || result.verified !== true || result.verification !== 'overleaf-zip') {
1511
+ return { ok: false, code: 'undo_operation_failed', changedDocument: result.changedDocument !== false,
1512
+ reason: 'The created file has no current server deletion receipt.' };
1513
+ }
1514
+ recordFileTreeOperationSuccess(operation, options.baseFileLookup);
1515
+ return result;
1516
+ }
1517
+
1448
1518
  async function verifyFileTreeOperation(operation) {
1449
1519
  await delay(120);
1450
1520
  const sourcePath = operation.path;
@@ -1613,15 +1683,6 @@
1613
1683
  }
1614
1684
  }
1615
1685
 
1616
- function fileTreeMethodNames(type) {
1617
- return {
1618
- create: ['createDoc', 'createFile', 'addDoc', 'addFile'],
1619
- rename: ['renameEntity', 'renameFile', 'renameDoc'],
1620
- move: ['moveEntity', 'moveFile', 'moveDoc'],
1621
- delete: ['deleteEntity', 'deleteFile', 'deleteDoc', 'removeEntity']
1622
- }[type] || [];
1623
- }
1624
-
1625
1686
  function normalizeOperationPaths(operation) {
1626
1687
  if (!operation || typeof operation !== 'object') {
1627
1688
  return operation;
@@ -1694,10 +1755,6 @@
1694
1755
  }
1695
1756
 
1696
1757
 
1697
-
1698
-
1699
-
1700
-
1701
1758
  function normalizeTrackedChangeRefs(refs = []) {
1702
1759
  const seen = new Set();
1703
1760
  const normalized = [];
@@ -1756,23 +1813,12 @@
1756
1813
 
1757
1814
  async function applyOperationsForBridge(operationsOrOptions, options = {}) {
1758
1815
  if (Array.isArray(operationsOrOptions)) {
1759
- // Welcome-panel + write-guard: defense-in-depth
1760
- // also runs on the array-shaped entry. T2 only guarded the
1761
- // payload-object branch; if any future caller goes straight to the
1762
- // array entry without `options.runProjectId`, the router would have
1763
- // silently dispatched. Mirror the same `editor_project_id_unavailable`
1764
- // failure shape the payload branch and the page-side guard already
1765
- // emit.
1816
+ // Both public call shapes require the original project identity.
1766
1817
  const writeGuardBlock = checkWritebackRunProjectId(options);
1767
1818
  if (writeGuardBlock) return writeGuardBlock;
1768
1819
  return applyOperations(operationsOrOptions, options);
1769
1820
  }
1770
1821
  const payload = operationsOrOptions || {};
1771
- // Welcome-panel + write-guard: defense-in-depth.
1772
- // The pageBridge wrapper already runs the runProjectId guard, but a
1773
- // future caller could reach the router directly. A missing or empty
1774
- // `runProjectId` on the payload-shaped call blocks the write with the
1775
- // same `editor_project_id_unavailable` shape the page-side guard uses.
1776
1822
  const writeGuardBlock = checkWritebackRunProjectId(payload);
1777
1823
  if (writeGuardBlock) return writeGuardBlock;
1778
1824
  return applyOperations(payload.operations || [], payload);
@@ -1781,10 +1827,7 @@
1781
1827
  function checkWritebackRunProjectId(params) {
1782
1828
  const runProjectId = params && typeof params.runProjectId === 'string' ? params.runProjectId : '';
1783
1829
  if (runProjectId) return null;
1784
- // operation: {} (not null) — batch-level guard fired before any per-op
1785
- // dispatch; matches the abortDispatchResult shape in pageBridge.js and
1786
- // avoids crashing downstream audit / transcript code that reads
1787
- // operation.path without a null guard.
1830
+ // Keep operation.path safe for downstream readers of batch-level failures.
1788
1831
  return {
1789
1832
  ok: false,
1790
1833
  applied: [],
@@ -1876,9 +1919,7 @@
1876
1919
  };
1877
1920
  }
1878
1921
 
1879
- // Tracked-changes lifecycle — carved to trackedChangesLifecycle.js in
1880
- // v1.8.0 (structural-debt phase 7, #117). Loaded just before this file
1881
- // in the manifest's page-script list; the Node test path requires it.
1922
+ // Tracked lifecycle is loaded by the Page script list or required by Node tests.
1882
1923
  const trackedChangesLifecycleModule = (typeof window !== 'undefined' && window.CodexOverleafTrackedChangesLifecycle)
1883
1924
  || (typeof module === 'object' && module.exports ? require('./trackedChangesLifecycle.js') : null)
1884
1925
  || (typeof globalThis !== 'undefined' ? globalThis.CodexOverleafTrackedChangesLifecycle : null);
@@ -1890,7 +1931,7 @@
1890
1931
  captureTrackedWrite, reconcileTrackedChangeCapture,
1891
1932
  waitForTrackedChangeDiff
1892
1933
  } = trackedChangesLifecycleModule.create({
1893
- window, confirmReviewWriteback: deps.confirmReviewWriteback,
1934
+ window, now, readWriteCancellationSequence, confirmReviewWriteback: deps.confirmReviewWriteback,
1894
1935
  checkWritebackRunProjectId,
1895
1936
  buildNoTraceUndoBlockedResult,
1896
1937
  buildReviewingRequiredBlockedResult,