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
@@ -0,0 +1,475 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const crypto = require('node:crypto');
6
+ const { Worker } = require('node:worker_threads');
7
+ const { getCodexOverleafHome } = require('./nativeHostPlatform');
8
+ const Shared = require('../../extension/src/shared/writingStyle');
9
+ const REFERENCE_LIMITS = Shared.REFERENCE_LIMITS;
10
+ const StyleProfile = require('./writingStyleProfile');
11
+ const { writeJson, withConfigLock } = require('./writingStyleStore');
12
+ const sha = value => crypto.createHash('sha256').update(value).digest('hex');
13
+ const fail = (code, message) => Object.assign(new Error(message), { code });
14
+ const checkAbort = signal => { if (signal?.aborted) throw signal.reason || fail('codex_cancelled', 'Writing-style generation was cancelled.'); };
15
+
16
+ function scopeOf(params, env) {
17
+ const accountScopeId = String(params.accountScopeId || '');
18
+ const projectId = String(params.projectId || '');
19
+ if (!accountScopeId || accountScopeId.length > 256 || !/^[a-zA-Z0-9_-]{1,80}$/.test(projectId)) {
20
+ throw fail('writing_style_scope_invalid', 'A current account and project are required.');
21
+ }
22
+ const base = path.resolve(env.CODEX_OVERLEAF_WRITING_STYLE_ROOT || path.join(getCodexOverleafHome({ env }), 'writing-styles'));
23
+ const root = path.join(base, sha(accountScopeId).slice(0, 32), projectId);
24
+ fs.mkdirSync(base, { recursive: true, mode: 0o700 });
25
+ let cursor = base;
26
+ for (const part of path.relative(base, root).split(path.sep)) {
27
+ cursor = path.join(cursor, part);
28
+ try { if (fs.lstatSync(cursor).isSymbolicLink()) throw fail('writing_style_path_invalid', 'Writing-style storage cannot use symlinks.'); }
29
+ catch (error) { if (error.code !== 'ENOENT') throw error; }
30
+ fs.mkdirSync(cursor, { recursive: true, mode: 0o700 });
31
+ }
32
+ return { root, accountScopeId, projectId };
33
+ }
34
+
35
+ function readJson(file, fallback, max = 2 * 1024 * 1024) {
36
+ try {
37
+ if (fs.statSync(file).size > max) throw fail('writing_style_data_too_large', 'Writing-style data exceeds its limit.');
38
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
39
+ } catch (error) { if (error.code === 'ENOENT') return fallback; throw error; }
40
+ }
41
+
42
+ function config(scope) { return readJson(path.join(scope.root, 'config.json'), { revision: 0, enabled: false, version: '' }); }
43
+ function saveConfig(scope, value) { const next = { ...value, revision: (Number(value.revision) || 0) + 1 }; writeJson(path.join(scope.root, 'config.json'), next); return next; }
44
+ function alive(pid) { try { process.kill(Number(pid), 0); return true; } catch (error) { return error.code !== 'ESRCH'; } }
45
+
46
+ function versionRoot(scope, version) {
47
+ if (!/^[a-f0-9-]{36}$/i.test(version || '')) throw fail('writing_style_version_invalid', 'Writing-style version is invalid.');
48
+ const target = path.join(scope.root, 'versions', version);
49
+ if (fs.lstatSync(target).isSymbolicLink()) throw fail('writing_style_path_invalid', 'Writing-style versions cannot be symlinks.');
50
+ return target;
51
+ }
52
+
53
+ function readBundle(scope, version, bundleHash) {
54
+ const root = versionRoot(scope, version);
55
+ return readBundleAtRoot(scope, version, root, bundleHash);
56
+ }
57
+
58
+ function readBundleAtRoot(scope, version, root, bundleHash) {
59
+ const raw = fs.readFileSync(path.join(root, 'manifest.json'), 'utf8');
60
+ if (raw.length > 100000 || (bundleHash && sha(raw) !== bundleHash)) throw fail('writing_style_changed', 'The saved writing skill changed. Rebuild it in General settings.');
61
+ const manifest = JSON.parse(raw);
62
+ if (manifest.projectId !== scope.projectId || manifest.accountHash !== sha(scope.accountScopeId) || manifest.version !== version) {
63
+ throw fail('writing_style_scope_invalid', 'The writing skill does not belong to this project and account.');
64
+ }
65
+ return { root, manifest, bundleHash: sha(raw) };
66
+ }
67
+
68
+ function publicBundle(scope, bundle) {
69
+ const { manifest, bundleHash } = bundle;
70
+ return { label: manifest.label, createdAt: manifest.createdAt,
71
+ model: manifest.model, reasoningEffort: manifest.reasoningEffort,
72
+ sources: manifest.sources, sections: manifest.sections,
73
+ traits: Array.isArray(manifest.traits) ? manifest.traits : [],
74
+ snapshot: Shared.normalizeSnapshot({ accountScopeId: scope.accountScopeId, projectId: scope.projectId,
75
+ version: manifest.version, bundleHash, label: manifest.label }) };
76
+ }
77
+
78
+ function get(params, env, isRequestActive) {
79
+ const scope = scopeOf(params, env);
80
+ let state = config(scope);
81
+ const interrupted = value => value.building && (!alive(value.building.pid)
82
+ || (value.building.pid === process.pid && isRequestActive && !isRequestActive(value.building.requestId)));
83
+ if (interrupted(state)) {
84
+ state = withConfigLock(scope, () => {
85
+ const latest = config(scope);
86
+ return interrupted(latest) ? saveConfig(scope, { ...latest, building: null, error: 'The previous style-generation task was interrupted.' }) : latest;
87
+ });
88
+ }
89
+ let bundle = null, unavailable = '';
90
+ if (state.version) {
91
+ try { bundle = readBundle(scope, state.version); }
92
+ catch (error) {
93
+ if (state.enabled === true || params.includePreview) throw error;
94
+ unavailable = 'The saved writing skill changed. Rebuild it in General settings.';
95
+ }
96
+ }
97
+ const result = { enabled: state.enabled === true, ready: Boolean(bundle),
98
+ revision: state.revision, building: state.building || null, error: state.error || unavailable,
99
+ errorCode: !state.error && unavailable ? 'writing_style_changed' : '',
100
+ ...(bundle ? publicBundle(scope, bundle) : { sources: [], sections: [], snapshot: null }) };
101
+ if (params.includePreview && bundle) {
102
+ result.preview = ['SKILL.md', 'references/writing-style.md', ...bundle.manifest.sections.map(item => item.file)]
103
+ .map(file => ({ file, content: safeBundleText(bundle, file) }));
104
+ }
105
+ return result;
106
+ }
107
+
108
+ function set(params, env) {
109
+ const scope = scopeOf(params, env);
110
+ withConfigLock(scope, () => {
111
+ const state = config(scope);
112
+ if (params.enabled === true && !state.version) throw fail('writing_style_not_ready', 'Generate the writing skill before enabling it.');
113
+ if (params.enabled === true) readBundle(scope, state.version);
114
+ saveConfig(scope, { ...state, enabled: params.enabled === true });
115
+ });
116
+ return get(params, env);
117
+ }
118
+
119
+ function safeBundleText(bundle, file) {
120
+ if (!/^(?:SKILL\.md|references\/[a-z0-9-]+\.(?:md|json)|corpus\/[a-z0-9-]+\.txt)$/.test(file)
121
+ || !bundle.manifest.files?.[file]) throw fail('writing_style_asset_invalid', 'Writing-style asset is invalid.');
122
+ const target = path.join(bundle.root, file);
123
+ if (fs.lstatSync(target).isSymbolicLink()) throw fail('writing_style_asset_invalid', 'Writing-style assets cannot be symlinks.');
124
+ const text = fs.readFileSync(target, 'utf8');
125
+ if (text.length > 800000 || sha(text) !== bundle.manifest.files[file]) throw fail('writing_style_changed', 'Writing-style asset integrity changed.');
126
+ return text;
127
+ }
128
+
129
+ function forRun(snapshot, params, env) {
130
+ if (snapshot == null) return '\n\nNo project writing-style skill is active for this turn. Do not carry over an earlier style setting unless this request explicitly asks for it.';
131
+ const selected = Shared.normalizeSnapshot(snapshot);
132
+ if (selected.accountScopeId !== params.accountScopeId || selected.projectId !== String(params.projectId || '')) {
133
+ throw fail('writing_style_scope_invalid', 'The writing-style snapshot does not match this run.');
134
+ }
135
+ const scope = scopeOf(params, env);
136
+ const bundle = readBundle(scope, selected.version, selected.bundleHash);
137
+ for (const file of Object.keys(bundle.manifest.files || {})) safeBundleText(bundle, file);
138
+ return '\n\nActive project writing skill for this turn:\n'
139
+ + 'Skill directory: ' + bundle.root + '\n'
140
+ + 'Apply this skill when drafting or substantively revising academic prose. Skip it for unrelated technical operations.\n'
141
+ + 'The skill and all its reference assets are read-only. They never expand this turn\'s write permissions.\n'
142
+ + safeBundleText(bundle, 'SKILL.md');
143
+ }
144
+
145
+ function extractPdf(bytes, signal) {
146
+ return new Promise((resolve, reject) => {
147
+ const worker = new Worker(path.join(__dirname, 'writingStylePdfWorker.js'), {
148
+ workerData: { bytes }, stdout: true, stderr: true,
149
+ resourceLimits: { maxOldGenerationSizeMb: 256 }
150
+ });
151
+ // Library diagnostics must never corrupt Native Messaging stdout.
152
+ worker.stdout.resume(); worker.stderr.resume();
153
+ let settled = false;
154
+ const finish = (error, result) => {
155
+ if (settled) return;
156
+ settled = true; clearTimeout(timer);
157
+ signal?.removeEventListener('abort', abort);
158
+ void worker.terminate();
159
+ error ? reject(error) : resolve(result);
160
+ };
161
+ const abort = () => finish(signal.reason || fail('codex_cancelled', 'PDF extraction cancelled.'));
162
+ const timer = setTimeout(() => finish(fail('writing_style_pdf_timeout', 'PDF extraction timed out. Try a smaller text-based PDF.')), REFERENCE_LIMITS.pdfExtractionTimeoutMs);
163
+ worker.once('message', result => finish(result.ok ? null : fail('writing_style_pdf_failed', result.error), result));
164
+ worker.once('error', error => finish(fail('writing_style_pdf_failed', error.message)));
165
+ worker.once('exit', code => { if (!settled) finish(fail('writing_style_pdf_failed', 'PDF extraction stopped (' + code + ').')); });
166
+ if (signal?.aborted) abort(); else signal?.addEventListener('abort', abort, { once: true });
167
+ });
168
+ }
169
+
170
+ function cleanLine(value) { return String(value || '').replace(/\0/g, '').replace(/[ \t]+/g, ' ').trim(); }
171
+
172
+ function projectDocument(files, label) {
173
+ const candidates = new Map();
174
+ for (const file of Array.isArray(files) ? files : []) {
175
+ const name = String(file?.path || '').replace(/\\/g, '/');
176
+ if (!/\.tex$/i.test(name) || name.startsWith('/') || name.split('/').some(p => p === '..' || p === '.')
177
+ || typeof file.content !== 'string') continue;
178
+ candidates.set(name, file.content);
179
+ }
180
+ const score = ([name, text]) => (/^(?:main|thesis)\.tex$/i.test(name) ? 100 : 0)
181
+ + (/\\(?:documentclass|documentstyle)/.test(text) ? 40 : 0)
182
+ + (/\\begin\{document\}/.test(text) ? 30 : 0)
183
+ - (/\\documentclass(?:\[[^\]]*\])?\{beamer\}/.test(text) ? 100 : 0)
184
+ + Math.min(text.length / 10000, 10);
185
+ const main = [...candidates].sort((a, b) => score(b) - score(a))[0];
186
+ if (!main) throw fail('writing_style_reference_empty', 'No readable TeX document was found in ' + label + '.');
187
+ const lines = [], origins = [], visited = new Set();
188
+ let chars = 0, truncated = false;
189
+ function visit(name) {
190
+ if (visited.has(name)) return;
191
+ if (visited.size >= 80) { truncated = true; return; }
192
+ const text = candidates.get(name);
193
+ if (text === undefined) throw fail('writing_style_reference_incomplete', 'A referenced TeX component is missing: ' + name);
194
+ visited.add(name);
195
+ let inDocument = !/\\begin\{document\}/.test(text), inComment = false, inBibliography = false;
196
+ for (const [index, original] of text.split(/\r?\n/).entries()) {
197
+ checkLimit: {
198
+ if (chars >= 60000) { truncated = true; return; }
199
+ if (/\\begin\{comment\}/.test(original)) inComment = true;
200
+ if (/\\end\{comment\}/.test(original)) { inComment = false; break checkLimit; }
201
+ if (inComment || /^\s*%/.test(original)) break checkLimit;
202
+ const line = cleanLine(original.replace(/(^|[^\\])%.*/, '$1'));
203
+ if (/\\begin\{document\}/.test(line)) { inDocument = true; break checkLimit; }
204
+ if (!inDocument) break checkLimit;
205
+ if (/\\begin\{thebibliography\}/.test(line)) inBibliography = true;
206
+ if (/\\end\{thebibliography\}/.test(line)) { inBibliography = false; break checkLimit; }
207
+ if (inBibliography || /\\(?:bibliography|bibliographystyle|maketitle|end\{document\})/.test(line)) break checkLimit;
208
+ const includes = [...line.matchAll(/\\(?:input|include)\s*\{([^}]+)\}/g)];
209
+ if (includes.length) {
210
+ for (const match of includes) {
211
+ const relative = path.posix.normalize(path.posix.join(path.posix.dirname(name), match[1].trim()));
212
+ if (relative.startsWith('../') || path.posix.isAbsolute(relative)) throw fail('writing_style_reference_path', 'A TeX include leaves its reference project.');
213
+ visit(candidates.has(relative) ? relative : relative + '.tex');
214
+ }
215
+ break checkLimit;
216
+ }
217
+ if (!line) break checkLimit;
218
+ lines.push(line); origins.push({ path: name, line: index + 1 }); chars += line.length;
219
+ }
220
+ }
221
+ }
222
+ visit(main[0]);
223
+ return { lines, origins, mainPath: main[0], truncated };
224
+ }
225
+
226
+ function readReferenceProjectAttachment(item, params) {
227
+ const attachment = (params.attachments || []).find(value => value.id === item.attachmentId);
228
+ if (!attachment || typeof attachment.contentBase64 !== 'string'
229
+ || attachment.contentBase64.length > Math.ceil(4 * 1024 * 1024 / 3) * 4
230
+ || !/^[A-Za-z0-9+/]*={0,2}$/.test(attachment.contentBase64)) {
231
+ throw fail('writing_style_reference_invalid', 'The selected project text is missing or exceeds 4 MiB.');
232
+ }
233
+ const bytes = Buffer.from(attachment.contentBase64, 'base64');
234
+ if (bytes.length > 4 * 1024 * 1024) throw fail('writing_style_reference_invalid', 'The selected project text exceeds 4 MiB.');
235
+ let snapshot;
236
+ try { snapshot = JSON.parse(bytes.toString('utf8')); }
237
+ catch (_) { throw fail('writing_style_reference_invalid', 'The selected project snapshot is unreadable.'); }
238
+ if (snapshot?.projectId !== item.projectId || snapshot?.capabilities?.referenceTextComplete !== true
239
+ || !Array.isArray(snapshot.files) || !snapshot.files.length || snapshot.files.length > 1000
240
+ || !Number.isFinite(Date.parse(snapshot.capturedAt))) {
241
+ throw fail('writing_style_reference_invalid', 'A complete, identified project reference is required. Reload the reference and retry.');
242
+ }
243
+ let textBytes = 0;
244
+ for (const file of snapshot.files) {
245
+ const name = String(file?.path || '').replace(/\\/g, '/');
246
+ if (!/\.tex$/i.test(name) || name.startsWith('/') || name.split('/').some(part => !part || part === '.' || part === '..')
247
+ || typeof file.content !== 'string') {
248
+ throw fail('writing_style_reference_invalid', 'The reference project contains an invalid TeX file entry.');
249
+ }
250
+ textBytes += Buffer.byteLength(file.content, 'utf8');
251
+ if (textBytes > 4 * 1024 * 1024) throw fail('writing_style_reference_invalid', 'The selected project text exceeds 4 MiB.');
252
+ }
253
+ return snapshot;
254
+ }
255
+
256
+ async function prepareSources(input, params, scope, env, signal) {
257
+ const selected = input.sources;
258
+ if (!Array.isArray(selected) || selected.length < 1 || selected.length > 3) throw fail('writing_style_sources_invalid', 'Select one to three reference sources.');
259
+ let cached = [];
260
+ if (input.reuseSnapshot) {
261
+ const snapshot = Shared.normalizeSnapshot(input.reuseSnapshot);
262
+ if (snapshot.projectId !== scope.projectId || snapshot.accountScopeId !== scope.accountScopeId) throw fail('writing_style_scope_invalid', 'Cached references belong to another scope.');
263
+ const bundle = readBundle(scope, snapshot.version, snapshot.bundleHash);
264
+ cached = JSON.parse(safeBundleText(bundle, 'references/source-index.json')).documents || [];
265
+ }
266
+ const documents = [], hashes = new Set();
267
+ let pdfBytes = 0;
268
+ for (const item of selected) {
269
+ checkAbort(signal);
270
+ let doc;
271
+ if (item.kind === 'project') {
272
+ const id = String(item.projectId || '');
273
+ if (!/^[a-zA-Z0-9_-]{1,80}$/.test(id)) throw fail('writing_style_project_invalid', 'Reference project identity is invalid.');
274
+ let files, capturedAt;
275
+ if (item.attachmentId) {
276
+ const snapshot = readReferenceProjectAttachment(item, params);
277
+ files = snapshot.files; capturedAt = snapshot.capturedAt;
278
+ } else if (id === scope.projectId && params.project?.capabilities?.fullProjectSnapshot === true) {
279
+ files = params.project.files; capturedAt = new Date().toISOString();
280
+ } else {
281
+ throw fail('writing_style_reference_required', 'Read the latest reference project from Overleaf before generating the style.');
282
+ }
283
+ doc = { kind: 'project', projectId: id, name: String(item.name || id).slice(0, 160),
284
+ capturedAt, ...projectDocument(files, item.name || id) };
285
+ } else if (item.kind === 'pdf') {
286
+ const attachment = (params.attachments || []).find(value => value.id === item.attachmentId);
287
+ if (!attachment || typeof attachment.contentBase64 !== 'string'
288
+ || attachment.contentBase64.length > Math.ceil(REFERENCE_LIMITS.maxPdfBytes / 3) * 4) {
289
+ throw fail('writing_style_pdf_invalid', 'The selected PDF is missing or too large.');
290
+ }
291
+ const bytes = Buffer.from(attachment.contentBase64, 'base64');
292
+ pdfBytes += bytes.length;
293
+ if (bytes.length > REFERENCE_LIMITS.maxPdfBytes || pdfBytes > REFERENCE_LIMITS.maxPdfTotalBytes
294
+ || bytes.subarray(0, 1024).indexOf('%PDF-') < 0) {
295
+ throw fail('writing_style_pdf_invalid', 'Use PDF files up to 20 MiB each, 40 MiB in total.');
296
+ }
297
+ const result = await extractPdf(bytes, signal);
298
+ const lines = [], origins = [];
299
+ const marginCounts = new Map();
300
+ for (const page of result.pages) {
301
+ for (const line of new Set([...page.lines.slice(0, 2), ...page.lines.slice(-2)].map(cleanLine))) {
302
+ if (line.length > 2 && line.length < 140) marginCounts.set(line, (marginCounts.get(line) || 0) + 1);
303
+ }
304
+ }
305
+ let count = 0;
306
+ for (const page of result.pages) {
307
+ for (const raw of page.lines) {
308
+ const line = cleanLine(raw);
309
+ if (!line || /^\d+$/.test(line) || (result.pages.length >= 3 && marginCounts.get(line) >= Math.ceil(result.pages.length * .6))) continue;
310
+ if (count >= 60000) break;
311
+ lines.push(line); origins.push({ page: page.number }); count += line.length;
312
+ }
313
+ if (count >= 60000) break;
314
+ }
315
+ doc = { kind: 'pdf', name: String(attachment.name || 'reference.pdf').slice(0, 160),
316
+ lines, origins, fileHash: sha(bytes), totalPages: result.totalPages,
317
+ pagesRead: result.pages.length, truncated: count >= 60000 || result.pages.length < result.totalPages };
318
+ } else if (item.kind === 'saved') {
319
+ const existing = cached.find(value => value.id === item.sourceId);
320
+ if (!existing) throw fail('writing_style_source_missing', 'A saved reference is unavailable. Add it again.');
321
+ doc = { ...existing };
322
+ } else throw fail('writing_style_sources_invalid', 'Unsupported writing reference.');
323
+ const text = doc.lines.join('\n');
324
+ if (text.length < 400 || (text.match(/\p{L}/gu) || []).length < 150
325
+ || (text.match(/\ufffd/g) || []).length > text.length * .02) {
326
+ throw fail('writing_style_reference_unreadable', 'Not enough readable prose in ' + doc.name + '. Use a text-based PDF or a fuller TeX document.');
327
+ }
328
+ const contentHash = sha(text);
329
+ if (hashes.has(contentHash)) continue;
330
+ hashes.add(contentHash);
331
+ documents.push({ ...doc, id: 'source-' + (documents.length + 1), contentHash });
332
+ }
333
+ if (!documents.length) throw fail('writing_style_reference_empty', 'No usable writing references were selected.');
334
+ return documents;
335
+ }
336
+
337
+ function parseResult(text, documents) {
338
+ const raw = String(text || '').trim().replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/, '');
339
+ const begin = raw.indexOf('{'), end = raw.lastIndexOf('}');
340
+ let result;
341
+ try { result = JSON.parse(raw.slice(begin, end + 1)); } catch (_) { throw fail('writing_style_generation_invalid', 'The agent did not return a valid writing-skill result. The previous skill was kept.'); }
342
+ return StyleProfile.validate(result, documents);
343
+ }
344
+
345
+ async function build({ params, env, signal, execute, providerLaunch, onControlReady, onStopUnconfirmed }) {
346
+ const scope = scopeOf(params, env), input = params.writingStyleBuild;
347
+ const id = crypto.randomUUID();
348
+ const staging = path.join(scope.root, '.build-' + id);
349
+ let keepStaging = false, registered = false;
350
+ try {
351
+ fs.mkdirSync(path.join(staging, 'corpus'), { recursive: true, mode: 0o700 });
352
+ const before = withConfigLock(scope, () => {
353
+ const state = config(scope);
354
+ saveConfig(scope, { ...state, error: '', building: { id, pid: process.pid,
355
+ requestId: String(input.requestId || ''), startedAt: new Date().toISOString() } });
356
+ return state;
357
+ });
358
+ registered = true;
359
+ const documents = await prepareSources(input, params, scope, env, signal);
360
+ checkAbort(signal);
361
+ for (const doc of documents) {
362
+ const text = doc.lines.map((line, index) => String(index + 1).padStart(5, '0') + ' | ' + line).join('\n');
363
+ fs.writeFileSync(path.join(staging, 'corpus', doc.id + '.txt'), text, { mode: 0o600 });
364
+ }
365
+ const index = documents.map(doc => ({ id: doc.id, name: doc.name, kind: doc.kind,
366
+ file: 'corpus/' + doc.id + '.txt', lines: doc.lines.length, truncated: doc.truncated === true }));
367
+ fs.writeFileSync(path.join(staging, 'source-index.json'), JSON.stringify(index, null, 2), { mode: 0o600 });
368
+ const instructions = fs.readFileSync(path.join(__dirname, 'skills', 'writing-style-builder', 'SKILL.md'), 'utf8');
369
+ const execution = {
370
+ workspacePath: staging, task: instructions + '\n\nAvailable references:\n' + JSON.stringify(index)
371
+ + '\nRespond in ' + (params.locale === 'zh' ? 'Chinese' : 'English') + ' for guidance; preserve source-example language.',
372
+ userTask: 'Build the project writing-style skill from the supplied corpus.',
373
+ session: null, threadId: '', mode: 'ask', model: params.model || '',
374
+ reasoningEffort: params.reasoningEffort || '', speedTier: params.speedTier || 'standard',
375
+ sandboxMode: 'read-only', approvalPolicy: 'never',
376
+ loadCodexLocalSkills: false, loadCodexOverleafSkills: false,
377
+ disableCodexOverleafSkillIds: ['annotated-rewrite', 'parallel-subagents'],
378
+ skillInvocation: null, projectLocalSkills: null, providerLaunch, env, signal,
379
+ onControlReady, emit: () => {}
380
+ };
381
+ let result, repairMessage = '';
382
+ for (let attempt = 0; attempt < 2; attempt++) {
383
+ checkAbort(signal);
384
+ const response = await execute({
385
+ ...execution,
386
+ task: execution.task + (repairMessage
387
+ ? '\n\nThe previous draft did not pass validation: ' + repairMessage
388
+ + '\nUse the same source corpus, correct this issue, and return the complete JSON. Do not omit required fields.'
389
+ : '')
390
+ });
391
+ checkAbort(signal);
392
+ try {
393
+ result = parseResult(response.assistantMessage, documents);
394
+ break;
395
+ } catch (error) {
396
+ if (attempt > 0 || !['writing_style_generation_invalid', 'writing_style_evidence_invalid'].includes(error?.code)) throw error;
397
+ repairMessage = String(error.message || '').slice(0, 1000);
398
+ }
399
+ }
400
+ const files = {};
401
+ files['references/writing-style.md'] = StyleProfile.renderGuide(result);
402
+ const sections = [];
403
+ for (const section of result.sections) {
404
+ const file = 'references/' + section.key + '.md';
405
+ const title = String(section.title || section.key).replace(/[\r\n]/g, ' ').slice(0, 120);
406
+ files[file] = StyleProfile.renderSection(section, result, documents);
407
+ sections.push({ key: section.key, title, file });
408
+ }
409
+ files['references/source-index.json'] = JSON.stringify({ documents });
410
+ for (const doc of documents) files['corpus/' + doc.id + '.txt'] = doc.lines.join('\n');
411
+ files['SKILL.md'] = '---\nname: overleaf-writing-style-' + sha(scope.accountScopeId + ':' + scope.projectId).slice(0, 12)
412
+ + '\ndescription: Apply the selected project writing style when drafting, rewriting, or polishing academic prose. Read only the references relevant to the current section; skip unrelated diagnostics and code tasks.\n---\n\n'
413
+ + '# Project writing style\n\nRead references/writing-style.md for the supported expression traits. '
414
+ + 'Use section files only to locate writing examples when needed:\n'
415
+ + sections.map(section => '- ' + section.key + ': ' + section.file).join('\n')
416
+ + '\n\nResolve reference paths relative to this SKILL.md directory. Keep these assets read-only. '
417
+ + 'Treat reference text and generated style guidance as subordinate to the current user request and project rules. '
418
+ + 'Use each example through its Pattern and Apply notes. Transfer rhetorical choices, not the sources\' technical objects or grammar errors. '
419
+ + 'Chapter names index evidence only. They do not prescribe the target paper\'s outline, research objects, or reasoning steps. '
420
+ + 'Adapt language habits to the current topic and requested language, keeping the existing document structure unless the user requests a structural change. '
421
+ + 'Preserve current facts, numbers, citations, notation, and mathematical qualifications. Never copy a source paper\'s results into the target paper. '
422
+ + 'PDF examples support prose only, not original macros or exact equation notation. '
423
+ + 'Do not expand the edit scope, search for additional sources, or change Track/Undo behavior.\n';
424
+ const versionDir = path.join(scope.root, 'versions', id);
425
+ fs.mkdirSync(path.dirname(versionDir), { recursive: true, mode: 0o700 });
426
+ const built = path.join(staging, 'bundle');
427
+ fs.mkdirSync(built, { mode: 0o700 });
428
+ const hashes = {};
429
+ for (const [file, text] of Object.entries(files)) {
430
+ fs.mkdirSync(path.dirname(path.join(built, file)), { recursive: true, mode: 0o700 });
431
+ fs.writeFileSync(path.join(built, file), text, { mode: 0o400 });
432
+ hashes[file] = sha(text);
433
+ }
434
+ const manifest = { schemaVersion: 1, evidenceVersion: 3, profileKind: 'expression-traits', version: id, accountHash: sha(scope.accountScopeId),
435
+ projectId: scope.projectId, label: result.label, createdAt: new Date().toISOString(),
436
+ model: params.model || '', reasoningEffort: params.reasoningEffort || '',
437
+ sources: documents.map(({ lines, origins, ...metadata }) => metadata),
438
+ traits: result.traits.map(({ id, dimension, title, exampleIds, sourceIds }) => ({ id, dimension, title, exampleIds, sourceIds })),
439
+ sections, files: hashes };
440
+ fs.writeFileSync(path.join(built, 'manifest.json'), JSON.stringify(manifest), { mode: 0o400 });
441
+ const bundle = readBundleAtRoot(scope, id, built);
442
+ for (const file of Object.keys(bundle.manifest.files)) safeBundleText(bundle, file);
443
+ checkAbort(signal);
444
+ withConfigLock(scope, () => {
445
+ checkAbort(signal);
446
+ const latest = config(scope);
447
+ if (latest.building?.id !== id) throw fail('writing_style_build_superseded', 'A newer style-generation task replaced this one.');
448
+ fs.renameSync(built, versionDir);
449
+ const unchangedPreference = latest.revision === before.revision + 1;
450
+ saveConfig(scope, { ...latest, version: id, building: null, error: '',
451
+ enabled: unchangedPreference ? input.enableAfterBuild !== false : latest.enabled === true });
452
+ });
453
+ return { status: 'completed', assistantMessage: 'Writing skill generated: ' + result.label,
454
+ syncChanges: [], unsupportedChanges: [], writingStyle: get({ ...params }, env) };
455
+ } catch (error) {
456
+ if (error?.code === 'codex_process_stop_unconfirmed') {
457
+ keepStaging = true;
458
+ onStopUnconfirmed?.(error);
459
+ }
460
+ if (registered) {
461
+ try {
462
+ withConfigLock(scope, () => {
463
+ const state = config(scope);
464
+ if (state.building?.id === id) saveConfig(scope, { ...state, building: null,
465
+ error: error?.code === 'codex_cancelled' ? 'Style generation cancelled.' : String(error.message || 'Style generation failed.').replace(/\/Users\/\S+|\/home\/\S+|[A-Z]:\\\S+/g, '[local path]').slice(0, 300) });
466
+ });
467
+ } catch (_) { /* Preserve the original failure if config is unavailable. */ }
468
+ }
469
+ throw error;
470
+ } finally {
471
+ if (!keepStaging) fs.rmSync(staging, { recursive: true, force: true });
472
+ }
473
+ }
474
+
475
+ module.exports = { get, set, build, forRun };
@@ -0,0 +1,73 @@
1
+ 'use strict';
2
+ const fs = require('node:fs');
3
+ const path = require('node:path');
4
+ const crypto = require('node:crypto');
5
+
6
+ function writeJson(file, value) {
7
+ const temp = file + '.' + crypto.randomUUID() + '.tmp';
8
+ let created = false;
9
+ try {
10
+ const fd = fs.openSync(temp, 'wx', 0o600);
11
+ created = true;
12
+ try { fs.writeFileSync(fd, JSON.stringify(value)); }
13
+ finally { fs.closeSync(fd); }
14
+ fs.renameSync(temp, file);
15
+ } finally {
16
+ if (created) fs.rmSync(temp, { force: true });
17
+ }
18
+ }
19
+
20
+ function withConfigLock(scope, action) {
21
+ const lock = path.join(scope.root, '.config-lock');
22
+ const owner = process.pid + '-' + crypto.randomUUID() + '.owner';
23
+ const pending = path.join(scope.root, '.config-lock-' + owner);
24
+ let acquired = false, prepared = false;
25
+ try {
26
+ // Publish a nonempty directory atomically, so a reaper can only remove an
27
+ // empty old lock; it cannot remove a replacement owner's marker.
28
+ fs.mkdirSync(pending, { mode: 0o700 });
29
+ prepared = true;
30
+ fs.writeFileSync(path.join(pending, owner), '', { mode: 0o600 });
31
+ const deadline = Date.now() + 2000;
32
+ while (!acquired) {
33
+ try { fs.renameSync(pending, lock); acquired = true; }
34
+ catch (error) {
35
+ if (!['EEXIST', 'ENOTEMPTY', 'EPERM', 'EACCES'].includes(error.code)) throw error;
36
+ const stat = fs.lstatSync(lock, { throwIfNoEntry: false });
37
+ if (!stat && ['EPERM', 'EACCES'].includes(error.code)) throw error;
38
+ if (stat && (!stat.isDirectory() || stat.isSymbolicLink())) throw error;
39
+ if (stat) reapDeadOwner(lock);
40
+ if (Date.now() >= deadline) throw Object.assign(new Error('Writing-style settings are being updated by another Native Host. Retry shortly.'), { code: 'writing_style_busy' });
41
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 20);
42
+ }
43
+ }
44
+ return action();
45
+ } finally {
46
+ if (acquired) removeOwner(lock, owner);
47
+ if (prepared) fs.rmSync(pending, { recursive: true, force: true });
48
+ }
49
+ }
50
+
51
+ function reapDeadOwner(lock) {
52
+ let entries;
53
+ try { entries = fs.readdirSync(lock); } catch (error) { if (error.code === 'ENOENT') return; throw error; }
54
+ if (!entries.length) { removeEmptyLock(lock); return; }
55
+ if (entries.length !== 1) return;
56
+ const match = /^([1-9]\d*)-[a-f0-9-]{36}\.owner$/.exec(entries[0]);
57
+ if (!match || !Number.isSafeInteger(Number(match[1]))) return;
58
+ try { process.kill(Number(match[1]), 0); }
59
+ catch (error) { if (error.code === 'ESRCH') removeOwner(lock, entries[0]); }
60
+ }
61
+
62
+ function removeOwner(lock, owner) {
63
+ try { fs.unlinkSync(path.join(lock, owner)); }
64
+ catch (error) { if (error.code !== 'ENOENT') throw error; }
65
+ removeEmptyLock(lock);
66
+ }
67
+
68
+ function removeEmptyLock(lock) {
69
+ try { fs.rmdirSync(lock); }
70
+ catch (error) { if (!['ENOENT', 'ENOTEMPTY', 'EEXIST'].includes(error.code)) throw error; }
71
+ }
72
+
73
+ module.exports = { writeJson, withConfigLock };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-overleaf-link",
3
- "version": "2.4.1",
3
+ "version": "2.5.1",
4
4
  "description": "Cross-platform Chrome bridge that connects Codex to the active Overleaf project.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -22,6 +22,7 @@ export function formatInstallManagedHuman(result) {
22
22
  '2. Remove the previous Codex Overleaf Link entry if Chrome shows a duplicate.',
23
23
  '3. Choose Load unpacked and select: ' + result.loadUnpackedPath,
24
24
  'Future signed stable updates are checked automatically. Choose Update now to authorize a version; installation waits until Overleaf is saved and idle.',
25
+ ...(result.warning ? ['Warning: ' + result.warning] : []),
25
26
  ''
26
27
  ].join('\n');
27
28
  }