codex-overleaf-link 2.4.0 → 2.5.0

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 (327) hide show
  1. package/README.md +272 -311
  2. package/README.zh-CN.md +287 -326
  3. package/extension/bootstrap/background.js +47 -10
  4. package/extension/bootstrap/manifest.template.json +9 -4
  5. package/extension/bootstrap/popup.html +10 -16
  6. package/extension/bootstrap/update.js +25 -2
  7. package/extension/runtime-manifest.json +3 -1
  8. package/extension/src/background.js +28 -3
  9. package/extension/src/backgroundSharedSessions.js +380 -0
  10. package/extension/src/backgroundUpdateCoordinator.js +442 -379
  11. package/extension/src/content/activeTurnControl.js +8 -0
  12. package/extension/src/content/assetTransferBroker.js +186 -12
  13. package/extension/src/content/changeHistoryView.js +175 -0
  14. package/extension/src/content/composerAttachments.js +24 -22
  15. package/extension/src/content/composerPanel.js +23 -8
  16. package/extension/src/content/contentRuntime.js +572 -571
  17. package/extension/src/content/diagnosticsController.js +1 -2
  18. package/extension/src/content/diagnosticsExport.js +222 -0
  19. package/extension/src/content/diagnosticsPanel.js +48 -19
  20. package/extension/src/content/diffReviewPanel.js +8 -5
  21. package/extension/src/content/generated/content.bundle.js +8974 -1781
  22. package/extension/src/content/generated/content.bundle.meta.json +22 -3
  23. package/extension/src/content/legacyGlobalRegistry.js +19 -1
  24. package/extension/src/content/localSkillsPanel.js +42 -7
  25. package/extension/src/content/markdownDomRenderer.js +9 -4
  26. package/extension/src/content/modelPicker.js +1 -1
  27. package/extension/src/content/pageBridgeClient.js +18 -3
  28. package/extension/src/content/panelMaintenance.js +51 -120
  29. package/extension/src/content/panelRenderer.js +4 -0
  30. package/extension/src/content/pendingInputView.js +109 -76
  31. package/extension/src/content/providerSettingsCoordinator.js +17 -6
  32. package/extension/src/content/providerSettingsDialog.js +661 -108
  33. package/extension/src/content/recentProjects.js +364 -356
  34. package/extension/src/content/runActivitySummary.js +631 -0
  35. package/extension/src/content/runController.js +3 -1
  36. package/extension/src/content/runFailureNotice.js +14 -1
  37. package/extension/src/content/runGuidanceController.js +4 -3
  38. package/extension/src/content/runPresence.js +306 -0
  39. package/extension/src/content/runQueueScheduler.js +2 -1
  40. package/extension/src/content/runResultActions.js +123 -3
  41. package/extension/src/content/runTimelineView.js +194 -164
  42. package/extension/src/content/scopedPersistenceCoordinator.js +2 -1
  43. package/extension/src/content/scopedPersistencePanelState.js +45 -2
  44. package/extension/src/content/selectionContextView.js +300 -0
  45. package/extension/src/content/sessionPersistence.js +10 -2
  46. package/extension/src/content/settingsPanel.js +93 -54
  47. package/extension/src/content/settingsWorkbench.js +374 -0
  48. package/extension/src/content/sharedSessionViewSync.js +72 -0
  49. package/extension/src/content/subagentActivityView.js +195 -0
  50. package/extension/src/content/trackedChangeCaptureController.js +7 -3
  51. package/extension/src/content/updateNotice.js +121 -50
  52. package/extension/src/content/writebackController.js +257 -0
  53. package/extension/src/content/writebackOrchestrator.js +146 -141
  54. package/extension/src/content/writebackPlan.js +231 -0
  55. package/extension/src/content/writebackRecovery.js +122 -0
  56. package/extension/src/content/writingStyleSettings.js +576 -0
  57. package/extension/src/content/writtenChangesView.js +152 -0
  58. package/extension/src/page/binaryAssetUploader.js +97 -19
  59. package/extension/src/page/overleafProjectSnapshot.js +1 -0
  60. package/extension/src/page/referenceProjects.js +148 -0
  61. package/extension/src/page/saveState.js +58 -3
  62. package/extension/src/page/snapshotRouter.js +44 -13
  63. package/extension/src/page/textCoordinates.js +102 -0
  64. package/extension/src/page/textFileCreator.js +350 -59
  65. package/extension/src/page/trackedChangeCapture.js +42 -56
  66. package/extension/src/page/trackedChangeOwnership.js +198 -0
  67. package/extension/src/page/trackedChangeReplay.js +164 -0
  68. package/extension/src/page/trackedChangesLifecycle.js +139 -150
  69. package/extension/src/page/treeOperations.js +36 -29
  70. package/extension/src/page/writebackReceiptJournal.js +112 -0
  71. package/extension/src/page/writebackRouter.js +176 -136
  72. package/extension/src/pageBridge.js +59 -101
  73. package/extension/src/popup.js +203 -59
  74. package/extension/src/shared/agentTranscript.js +18 -4
  75. package/extension/src/shared/auditRecords.js +71 -0
  76. package/extension/src/shared/changeHistoryModel.js +94 -0
  77. package/extension/src/shared/compatibility.js +1 -1
  78. package/extension/src/shared/compileAdapter.js +42 -0
  79. package/extension/src/shared/failureReasons.js +20 -0
  80. package/extension/src/shared/globalPreferences.js +1 -1
  81. package/extension/src/shared/i18n.js +187 -65
  82. package/extension/src/shared/managedUpdateProjection.js +6 -3
  83. package/extension/src/shared/pageRpcContract.js +7 -1
  84. package/extension/src/shared/providerProfiles.js +109 -0
  85. package/extension/src/shared/runActivityModel.js +361 -0
  86. package/extension/src/shared/runExecutionSnapshot.js +8 -2
  87. package/extension/src/shared/runExecutionSnapshotCodec.js +16 -4
  88. package/extension/src/shared/scopedPersistenceBrowserAdapter.js +6 -3
  89. package/extension/src/shared/selectionContext.js +28 -0
  90. package/extension/src/shared/sessionState.js +97 -6
  91. package/extension/src/shared/sharedSessionBridge.js +288 -0
  92. package/extension/src/shared/storageDb.js +23 -55
  93. package/extension/src/shared/storageMigration.js +3 -0
  94. package/extension/src/shared/storageRunActions.js +54 -4
  95. package/extension/src/shared/storageValuePruning.js +40 -0
  96. package/extension/src/shared/subagentActivityModel.js +174 -0
  97. package/extension/src/shared/undoOperations.js +87 -0
  98. package/extension/src/shared/updateConsent.js +37 -8
  99. package/extension/src/shared/writebackIntent.js +62 -0
  100. package/extension/src/shared/writebackSettlement.js +29 -10
  101. package/extension/src/shared/writingStyle.js +33 -0
  102. package/extension/styles/panel.css +2136 -744
  103. package/extension/styles/popup.css +110 -0
  104. package/native-host/src/anthropicBridgeRequest.js +2 -1
  105. package/native-host/src/codexModelRecovery.js +107 -0
  106. package/native-host/src/codexPromptAssembly.js +2 -0
  107. package/native-host/src/codexProviderLaunch.js +3 -3
  108. package/native-host/src/codexRuntimeIdentity.js +1 -0
  109. package/native-host/src/codexSessionRunner.js +146 -177
  110. package/native-host/src/codexSessionTiming.js +71 -0
  111. package/native-host/src/managedInstall.js +5 -4
  112. package/native-host/src/nativeEnvironment.js +70 -0
  113. package/native-host/src/nativeQuotas.js +15 -8
  114. package/native-host/src/overleafWorkspaceRules.js +16 -0
  115. package/native-host/src/processStopBarrier.js +80 -0
  116. package/native-host/src/providerModelCatalog.js +4 -3
  117. package/native-host/src/providerProfile.js +13 -1
  118. package/native-host/src/providerReasoning.js +9 -5
  119. package/native-host/src/providerStore.js +4 -0
  120. package/native-host/src/readProgressGuard.js +84 -17
  121. package/native-host/src/releaseTransport.js +14 -5
  122. package/native-host/src/selectionScope.js +58 -0
  123. package/native-host/src/skills/parallel-subagents/SKILL.md +31 -3
  124. package/native-host/src/skills/writing-style-builder/SKILL.md +146 -0
  125. package/native-host/src/subagentBroker.js +102 -98
  126. package/native-host/src/subagentTelemetry.js +316 -0
  127. package/native-host/src/subagentWorkspace.js +103 -0
  128. package/native-host/src/subagentWorkspacePath.js +31 -0
  129. package/native-host/src/taskRunnerRuntime.js +55 -5
  130. package/native-host/src/turnAttachments.js +113 -0
  131. package/native-host/src/updateManager.js +122 -12
  132. package/native-host/src/updateNetworkControl.js +75 -0
  133. package/native-host/src/updateTrust.js +21 -2
  134. package/native-host/src/vendor/pdfjs/LICENSE +177 -0
  135. package/native-host/src/vendor/pdfjs/SOURCE.txt +3 -0
  136. package/native-host/src/vendor/pdfjs/cmaps/78-EUC-H.bcmap +0 -0
  137. package/native-host/src/vendor/pdfjs/cmaps/78-EUC-V.bcmap +0 -0
  138. package/native-host/src/vendor/pdfjs/cmaps/78-H.bcmap +0 -0
  139. package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-H.bcmap +0 -0
  140. package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-V.bcmap +0 -0
  141. package/native-host/src/vendor/pdfjs/cmaps/78-V.bcmap +0 -0
  142. package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-H.bcmap +0 -0
  143. package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-V.bcmap +0 -0
  144. package/native-host/src/vendor/pdfjs/cmaps/83pv-RKSJ-H.bcmap +0 -0
  145. package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-H.bcmap +0 -0
  146. package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-V.bcmap +0 -0
  147. package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-H.bcmap +0 -0
  148. package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-V.bcmap +0 -0
  149. package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-H.bcmap +0 -0
  150. package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-V.bcmap +0 -0
  151. package/native-host/src/vendor/pdfjs/cmaps/Add-H.bcmap +0 -0
  152. package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-H.bcmap +0 -0
  153. package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-V.bcmap +0 -0
  154. package/native-host/src/vendor/pdfjs/cmaps/Add-V.bcmap +0 -0
  155. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-0.bcmap +0 -0
  156. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-1.bcmap +0 -0
  157. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-2.bcmap +0 -0
  158. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-3.bcmap +0 -0
  159. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-4.bcmap +0 -0
  160. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-5.bcmap +0 -0
  161. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-6.bcmap +0 -0
  162. package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
  163. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-0.bcmap +0 -0
  164. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-1.bcmap +0 -0
  165. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-2.bcmap +0 -0
  166. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-3.bcmap +0 -0
  167. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-4.bcmap +0 -0
  168. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-5.bcmap +0 -0
  169. package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
  170. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-0.bcmap +0 -0
  171. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-1.bcmap +0 -0
  172. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-2.bcmap +0 -0
  173. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-3.bcmap +0 -0
  174. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-4.bcmap +0 -0
  175. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-5.bcmap +0 -0
  176. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-6.bcmap +0 -0
  177. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
  178. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-0.bcmap +0 -0
  179. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-1.bcmap +0 -0
  180. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-2.bcmap +0 -0
  181. package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
  182. package/native-host/src/vendor/pdfjs/cmaps/B5-H.bcmap +0 -0
  183. package/native-host/src/vendor/pdfjs/cmaps/B5-V.bcmap +0 -0
  184. package/native-host/src/vendor/pdfjs/cmaps/B5pc-H.bcmap +0 -0
  185. package/native-host/src/vendor/pdfjs/cmaps/B5pc-V.bcmap +0 -0
  186. package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-H.bcmap +0 -0
  187. package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-V.bcmap +0 -0
  188. package/native-host/src/vendor/pdfjs/cmaps/CNS1-H.bcmap +0 -0
  189. package/native-host/src/vendor/pdfjs/cmaps/CNS1-V.bcmap +0 -0
  190. package/native-host/src/vendor/pdfjs/cmaps/CNS2-H.bcmap +0 -0
  191. package/native-host/src/vendor/pdfjs/cmaps/CNS2-V.bcmap +3 -0
  192. package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-H.bcmap +0 -0
  193. package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-V.bcmap +0 -0
  194. package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-H.bcmap +0 -0
  195. package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-V.bcmap +0 -0
  196. package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-H.bcmap +3 -0
  197. package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-V.bcmap +0 -0
  198. package/native-host/src/vendor/pdfjs/cmaps/EUC-H.bcmap +0 -0
  199. package/native-host/src/vendor/pdfjs/cmaps/EUC-V.bcmap +0 -0
  200. package/native-host/src/vendor/pdfjs/cmaps/Ext-H.bcmap +0 -0
  201. package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-H.bcmap +0 -0
  202. package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-V.bcmap +0 -0
  203. package/native-host/src/vendor/pdfjs/cmaps/Ext-V.bcmap +0 -0
  204. package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-H.bcmap +0 -0
  205. package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-V.bcmap +0 -0
  206. package/native-host/src/vendor/pdfjs/cmaps/GB-H.bcmap +4 -0
  207. package/native-host/src/vendor/pdfjs/cmaps/GB-V.bcmap +0 -0
  208. package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-H.bcmap +0 -0
  209. package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-V.bcmap +0 -0
  210. package/native-host/src/vendor/pdfjs/cmaps/GBK2K-H.bcmap +0 -0
  211. package/native-host/src/vendor/pdfjs/cmaps/GBK2K-V.bcmap +0 -0
  212. package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-H.bcmap +0 -0
  213. package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-V.bcmap +0 -0
  214. package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-H.bcmap +0 -0
  215. package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-V.bcmap +0 -0
  216. package/native-host/src/vendor/pdfjs/cmaps/GBT-H.bcmap +0 -0
  217. package/native-host/src/vendor/pdfjs/cmaps/GBT-V.bcmap +0 -0
  218. package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-H.bcmap +0 -0
  219. package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-V.bcmap +0 -0
  220. package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-H.bcmap +0 -0
  221. package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-V.bcmap +0 -0
  222. package/native-host/src/vendor/pdfjs/cmaps/H.bcmap +0 -0
  223. package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-H.bcmap +0 -0
  224. package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-V.bcmap +0 -0
  225. package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-H.bcmap +0 -0
  226. package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-V.bcmap +0 -0
  227. package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-H.bcmap +0 -0
  228. package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-V.bcmap +0 -0
  229. package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-H.bcmap +0 -0
  230. package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-V.bcmap +0 -0
  231. package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-H.bcmap +0 -0
  232. package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-V.bcmap +0 -0
  233. package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-H.bcmap +0 -0
  234. package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-V.bcmap +0 -0
  235. package/native-host/src/vendor/pdfjs/cmaps/Hankaku.bcmap +0 -0
  236. package/native-host/src/vendor/pdfjs/cmaps/Hiragana.bcmap +0 -0
  237. package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-H.bcmap +0 -0
  238. package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-V.bcmap +0 -0
  239. package/native-host/src/vendor/pdfjs/cmaps/KSC-H.bcmap +0 -0
  240. package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-H.bcmap +0 -0
  241. package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-V.bcmap +0 -0
  242. package/native-host/src/vendor/pdfjs/cmaps/KSC-V.bcmap +0 -0
  243. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-H.bcmap +0 -0
  244. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
  245. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
  246. package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-V.bcmap +0 -0
  247. package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-H.bcmap +0 -0
  248. package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-V.bcmap +0 -0
  249. package/native-host/src/vendor/pdfjs/cmaps/Katakana.bcmap +0 -0
  250. package/native-host/src/vendor/pdfjs/cmaps/LICENSE +36 -0
  251. package/native-host/src/vendor/pdfjs/cmaps/NWP-H.bcmap +0 -0
  252. package/native-host/src/vendor/pdfjs/cmaps/NWP-V.bcmap +0 -0
  253. package/native-host/src/vendor/pdfjs/cmaps/RKSJ-H.bcmap +0 -0
  254. package/native-host/src/vendor/pdfjs/cmaps/RKSJ-V.bcmap +0 -0
  255. package/native-host/src/vendor/pdfjs/cmaps/Roman.bcmap +0 -0
  256. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-H.bcmap +0 -0
  257. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-V.bcmap +0 -0
  258. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-H.bcmap +0 -0
  259. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-V.bcmap +0 -0
  260. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-H.bcmap +0 -0
  261. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-V.bcmap +0 -0
  262. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-H.bcmap +0 -0
  263. package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-V.bcmap +0 -0
  264. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-H.bcmap +0 -0
  265. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-V.bcmap +0 -0
  266. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-H.bcmap +0 -0
  267. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-V.bcmap +0 -0
  268. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-H.bcmap +0 -0
  269. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-V.bcmap +0 -0
  270. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-H.bcmap +0 -0
  271. package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-V.bcmap +0 -0
  272. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-H.bcmap +0 -0
  273. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
  274. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
  275. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-V.bcmap +0 -0
  276. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-H.bcmap +0 -0
  277. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-V.bcmap +0 -0
  278. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-H.bcmap +0 -0
  279. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-V.bcmap +0 -0
  280. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-H.bcmap +0 -0
  281. package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-V.bcmap +0 -0
  282. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
  283. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
  284. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
  285. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
  286. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
  287. package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
  288. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
  289. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
  290. package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
  291. package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
  292. package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
  293. package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
  294. package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
  295. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-H.bcmap +0 -0
  296. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-V.bcmap +0 -0
  297. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-H.bcmap +0 -0
  298. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-V.bcmap +0 -0
  299. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-H.bcmap +0 -0
  300. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-V.bcmap +0 -0
  301. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-H.bcmap +0 -0
  302. package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-V.bcmap +0 -0
  303. package/native-host/src/vendor/pdfjs/cmaps/V.bcmap +0 -0
  304. package/native-host/src/vendor/pdfjs/cmaps/WP-Symbol.bcmap +0 -0
  305. package/native-host/src/vendor/pdfjs/pdf.mjs +26740 -0
  306. package/native-host/src/vendor/pdfjs/pdf.worker.mjs +62201 -0
  307. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitDingbats.pfb +0 -0
  308. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixed.pfb +0 -0
  309. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBold.pfb +0 -0
  310. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
  311. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedItalic.pfb +0 -0
  312. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerif.pfb +0 -0
  313. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBold.pfb +0 -0
  314. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
  315. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifItalic.pfb +0 -0
  316. package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSymbol.pfb +0 -0
  317. package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_FOXIT +27 -0
  318. package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_LIBERATION +102 -0
  319. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Bold.ttf +0 -0
  320. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
  321. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Italic.ttf +0 -0
  322. package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Regular.ttf +0 -0
  323. package/native-host/src/writingStyleEvidence.js +102 -0
  324. package/native-host/src/writingStylePdfWorker.js +35 -0
  325. package/native-host/src/writingStyleProfile.js +165 -0
  326. package/native-host/src/writingStyleRuntime.js +447 -0
  327. package/package.json +1 -1
@@ -0,0 +1,447 @@
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 sha = value => crypto.createHash('sha256').update(value).digest('hex');
12
+ const fail = (code, message) => Object.assign(new Error(message), { code });
13
+ const checkAbort = signal => { if (signal?.aborted) throw signal.reason || fail('codex_cancelled', 'Writing-style generation was cancelled.'); };
14
+
15
+ function scopeOf(params, env) {
16
+ const accountScopeId = String(params.accountScopeId || '');
17
+ const projectId = String(params.projectId || '');
18
+ if (!accountScopeId || accountScopeId.length > 256 || !/^[a-zA-Z0-9_-]{1,80}$/.test(projectId)) {
19
+ throw fail('writing_style_scope_invalid', 'A current account and project are required.');
20
+ }
21
+ const base = path.resolve(env.CODEX_OVERLEAF_WRITING_STYLE_ROOT || path.join(getCodexOverleafHome({ env }), 'writing-styles'));
22
+ const root = path.join(base, sha(accountScopeId).slice(0, 32), projectId);
23
+ fs.mkdirSync(base, { recursive: true, mode: 0o700 });
24
+ let cursor = base;
25
+ for (const part of path.relative(base, root).split(path.sep)) {
26
+ cursor = path.join(cursor, part);
27
+ try { if (fs.lstatSync(cursor).isSymbolicLink()) throw fail('writing_style_path_invalid', 'Writing-style storage cannot use symlinks.'); }
28
+ catch (error) { if (error.code !== 'ENOENT') throw error; }
29
+ fs.mkdirSync(cursor, { recursive: true, mode: 0o700 });
30
+ }
31
+ return { root, accountScopeId, projectId };
32
+ }
33
+
34
+ function readJson(file, fallback, max = 2 * 1024 * 1024) {
35
+ try {
36
+ if (fs.statSync(file).size > max) throw fail('writing_style_data_too_large', 'Writing-style data exceeds its limit.');
37
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
38
+ } catch (error) { if (error.code === 'ENOENT') return fallback; throw error; }
39
+ }
40
+
41
+ function writeJson(file, value) {
42
+ const temp = file + '.' + crypto.randomUUID() + '.tmp';
43
+ fs.writeFileSync(temp, JSON.stringify(value), { mode: 0o600 });
44
+ fs.renameSync(temp, file);
45
+ }
46
+
47
+ function config(scope) { return readJson(path.join(scope.root, 'config.json'), { revision: 0, enabled: false, version: '' }); }
48
+ function saveConfig(scope, value) { const next = { ...value, revision: (Number(value.revision) || 0) + 1 }; writeJson(path.join(scope.root, 'config.json'), next); return next; }
49
+ function alive(pid) { try { process.kill(Number(pid), 0); return true; } catch (error) { return error.code !== 'ESRCH'; } }
50
+
51
+ function versionRoot(scope, version) {
52
+ if (!/^[a-f0-9-]{36}$/i.test(version || '')) throw fail('writing_style_version_invalid', 'Writing-style version is invalid.');
53
+ const target = path.join(scope.root, 'versions', version);
54
+ if (fs.lstatSync(target).isSymbolicLink()) throw fail('writing_style_path_invalid', 'Writing-style versions cannot be symlinks.');
55
+ return target;
56
+ }
57
+
58
+ function readBundle(scope, version, bundleHash) {
59
+ const root = versionRoot(scope, version);
60
+ const raw = fs.readFileSync(path.join(root, 'manifest.json'), 'utf8');
61
+ if (raw.length > 100000 || (bundleHash && sha(raw) !== bundleHash)) throw fail('writing_style_changed', 'The saved writing skill changed. Rebuild it in General settings.');
62
+ const manifest = JSON.parse(raw);
63
+ if (manifest.projectId !== scope.projectId || manifest.accountHash !== sha(scope.accountScopeId) || manifest.version !== version) {
64
+ throw fail('writing_style_scope_invalid', 'The writing skill does not belong to this project and account.');
65
+ }
66
+ return { root, manifest, bundleHash: sha(raw) };
67
+ }
68
+
69
+ function publicBundle(scope, bundle) {
70
+ const { manifest, bundleHash } = bundle;
71
+ return { label: manifest.label, createdAt: manifest.createdAt,
72
+ model: manifest.model, reasoningEffort: manifest.reasoningEffort,
73
+ sources: manifest.sources, sections: manifest.sections,
74
+ traits: Array.isArray(manifest.traits) ? manifest.traits : [],
75
+ snapshot: Shared.normalizeSnapshot({ accountScopeId: scope.accountScopeId, projectId: scope.projectId,
76
+ version: manifest.version, bundleHash, label: manifest.label }) };
77
+ }
78
+
79
+ function get(params, env, isRequestActive) {
80
+ const scope = scopeOf(params, env);
81
+ let state = config(scope);
82
+ if (state.building && (!alive(state.building.pid)
83
+ || (state.building.pid === process.pid && isRequestActive && !isRequestActive(state.building.requestId)))) {
84
+ state = saveConfig(scope, { ...state, building: null, error: 'The previous style-generation task was interrupted.' });
85
+ }
86
+ const bundle = state.version ? readBundle(scope, state.version) : null;
87
+ const result = { enabled: state.enabled === true, ready: Boolean(bundle),
88
+ revision: state.revision, building: state.building || null, error: state.error || '',
89
+ ...(bundle ? publicBundle(scope, bundle) : { sources: [], sections: [], snapshot: null }) };
90
+ if (params.includePreview && bundle) {
91
+ result.preview = ['SKILL.md', 'references/writing-style.md', ...bundle.manifest.sections.map(item => item.file)]
92
+ .map(file => ({ file, content: safeBundleText(bundle, file) }));
93
+ }
94
+ return result;
95
+ }
96
+
97
+ function set(params, env) {
98
+ const scope = scopeOf(params, env);
99
+ const state = config(scope);
100
+ if (params.enabled === true && !state.version) throw fail('writing_style_not_ready', 'Generate the writing skill before enabling it.');
101
+ if (params.enabled === true) readBundle(scope, state.version);
102
+ saveConfig(scope, { ...state, enabled: params.enabled === true });
103
+ return get(params, env);
104
+ }
105
+
106
+ function safeBundleText(bundle, file) {
107
+ if (!/^(?:SKILL\.md|references\/[a-z0-9-]+\.(?:md|json)|corpus\/[a-z0-9-]+\.txt)$/.test(file)
108
+ || !bundle.manifest.files?.[file]) throw fail('writing_style_asset_invalid', 'Writing-style asset is invalid.');
109
+ const target = path.join(bundle.root, file);
110
+ if (fs.lstatSync(target).isSymbolicLink()) throw fail('writing_style_asset_invalid', 'Writing-style assets cannot be symlinks.');
111
+ const text = fs.readFileSync(target, 'utf8');
112
+ if (text.length > 800000 || sha(text) !== bundle.manifest.files[file]) throw fail('writing_style_changed', 'Writing-style asset integrity changed.');
113
+ return text;
114
+ }
115
+
116
+ function forRun(snapshot, params, env) {
117
+ 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.';
118
+ const selected = Shared.normalizeSnapshot(snapshot);
119
+ if (selected.accountScopeId !== params.accountScopeId || selected.projectId !== String(params.projectId || '')) {
120
+ throw fail('writing_style_scope_invalid', 'The writing-style snapshot does not match this run.');
121
+ }
122
+ const scope = scopeOf(params, env);
123
+ const bundle = readBundle(scope, selected.version, selected.bundleHash);
124
+ for (const file of Object.keys(bundle.manifest.files || {})) safeBundleText(bundle, file);
125
+ return '\n\nActive project writing skill for this turn:\n'
126
+ + 'Skill directory: ' + bundle.root + '\n'
127
+ + 'Apply this skill when drafting or substantively revising academic prose. Skip it for unrelated technical operations.\n'
128
+ + 'The skill and all its reference assets are read-only. They never expand this turn\'s write permissions.\n'
129
+ + safeBundleText(bundle, 'SKILL.md');
130
+ }
131
+
132
+ function extractPdf(bytes, signal) {
133
+ return new Promise((resolve, reject) => {
134
+ const worker = new Worker(path.join(__dirname, 'writingStylePdfWorker.js'), {
135
+ workerData: { bytes }, stdout: true, stderr: true,
136
+ resourceLimits: { maxOldGenerationSizeMb: 256 }
137
+ });
138
+ // Library diagnostics must never corrupt Native Messaging stdout.
139
+ worker.stdout.resume(); worker.stderr.resume();
140
+ let settled = false;
141
+ const finish = (error, result) => {
142
+ if (settled) return;
143
+ settled = true; clearTimeout(timer);
144
+ signal?.removeEventListener('abort', abort);
145
+ void worker.terminate();
146
+ error ? reject(error) : resolve(result);
147
+ };
148
+ const abort = () => finish(signal.reason || fail('codex_cancelled', 'PDF extraction cancelled.'));
149
+ const timer = setTimeout(() => finish(fail('writing_style_pdf_timeout', 'PDF extraction timed out. Try a smaller text-based PDF.')), REFERENCE_LIMITS.pdfExtractionTimeoutMs);
150
+ worker.once('message', result => finish(result.ok ? null : fail('writing_style_pdf_failed', result.error), result));
151
+ worker.once('error', error => finish(fail('writing_style_pdf_failed', error.message)));
152
+ worker.once('exit', code => { if (!settled) finish(fail('writing_style_pdf_failed', 'PDF extraction stopped (' + code + ').')); });
153
+ if (signal?.aborted) abort(); else signal?.addEventListener('abort', abort, { once: true });
154
+ });
155
+ }
156
+
157
+ function cleanLine(value) { return String(value || '').replace(/\0/g, '').replace(/[ \t]+/g, ' ').trim(); }
158
+
159
+ function projectDocument(files, label) {
160
+ const candidates = new Map();
161
+ for (const file of Array.isArray(files) ? files : []) {
162
+ const name = String(file?.path || '').replace(/\\/g, '/');
163
+ if (!/\.tex$/i.test(name) || name.startsWith('/') || name.split('/').some(p => p === '..' || p === '.')
164
+ || typeof file.content !== 'string') continue;
165
+ candidates.set(name, file.content);
166
+ }
167
+ const score = ([name, text]) => (/^(?:main|thesis)\.tex$/i.test(name) ? 100 : 0)
168
+ + (/\\(?:documentclass|documentstyle)/.test(text) ? 40 : 0)
169
+ + (/\\begin\{document\}/.test(text) ? 30 : 0)
170
+ - (/\\documentclass(?:\[[^\]]*\])?\{beamer\}/.test(text) ? 100 : 0)
171
+ + Math.min(text.length / 10000, 10);
172
+ const main = [...candidates].sort((a, b) => score(b) - score(a))[0];
173
+ if (!main) throw fail('writing_style_reference_empty', 'No readable TeX document was found in ' + label + '.');
174
+ const lines = [], origins = [], visited = new Set();
175
+ let chars = 0, truncated = false;
176
+ function visit(name) {
177
+ if (visited.has(name)) return;
178
+ if (visited.size >= 80) { truncated = true; return; }
179
+ const text = candidates.get(name);
180
+ if (text === undefined) throw fail('writing_style_reference_incomplete', 'A referenced TeX component is missing: ' + name);
181
+ visited.add(name);
182
+ let inDocument = !/\\begin\{document\}/.test(text), inComment = false, inBibliography = false;
183
+ for (const [index, original] of text.split(/\r?\n/).entries()) {
184
+ checkLimit: {
185
+ if (chars >= 60000) { truncated = true; return; }
186
+ if (/\\begin\{comment\}/.test(original)) inComment = true;
187
+ if (/\\end\{comment\}/.test(original)) { inComment = false; break checkLimit; }
188
+ if (inComment || /^\s*%/.test(original)) break checkLimit;
189
+ const line = cleanLine(original.replace(/(^|[^\\])%.*/, '$1'));
190
+ if (/\\begin\{document\}/.test(line)) { inDocument = true; break checkLimit; }
191
+ if (!inDocument) break checkLimit;
192
+ if (/\\begin\{thebibliography\}/.test(line)) inBibliography = true;
193
+ if (/\\end\{thebibliography\}/.test(line)) { inBibliography = false; break checkLimit; }
194
+ if (inBibliography || /\\(?:bibliography|bibliographystyle|maketitle|end\{document\})/.test(line)) break checkLimit;
195
+ const includes = [...line.matchAll(/\\(?:input|include)\s*\{([^}]+)\}/g)];
196
+ if (includes.length) {
197
+ for (const match of includes) {
198
+ const relative = path.posix.normalize(path.posix.join(path.posix.dirname(name), match[1].trim()));
199
+ if (relative.startsWith('../') || path.posix.isAbsolute(relative)) throw fail('writing_style_reference_path', 'A TeX include leaves its reference project.');
200
+ visit(candidates.has(relative) ? relative : relative + '.tex');
201
+ }
202
+ break checkLimit;
203
+ }
204
+ if (!line) break checkLimit;
205
+ lines.push(line); origins.push({ path: name, line: index + 1 }); chars += line.length;
206
+ }
207
+ }
208
+ }
209
+ visit(main[0]);
210
+ return { lines, origins, mainPath: main[0], truncated };
211
+ }
212
+
213
+ function readReferenceProjectAttachment(item, params) {
214
+ const attachment = (params.attachments || []).find(value => value.id === item.attachmentId);
215
+ if (!attachment || typeof attachment.contentBase64 !== 'string'
216
+ || attachment.contentBase64.length > Math.ceil(4 * 1024 * 1024 / 3) * 4
217
+ || !/^[A-Za-z0-9+/]*={0,2}$/.test(attachment.contentBase64)) {
218
+ throw fail('writing_style_reference_invalid', 'The selected project text is missing or exceeds 4 MiB.');
219
+ }
220
+ const bytes = Buffer.from(attachment.contentBase64, 'base64');
221
+ if (bytes.length > 4 * 1024 * 1024) throw fail('writing_style_reference_invalid', 'The selected project text exceeds 4 MiB.');
222
+ let snapshot;
223
+ try { snapshot = JSON.parse(bytes.toString('utf8')); }
224
+ catch (_) { throw fail('writing_style_reference_invalid', 'The selected project snapshot is unreadable.'); }
225
+ if (snapshot?.projectId !== item.projectId || snapshot?.capabilities?.referenceTextComplete !== true
226
+ || !Array.isArray(snapshot.files) || !snapshot.files.length || snapshot.files.length > 1000
227
+ || !Number.isFinite(Date.parse(snapshot.capturedAt))) {
228
+ throw fail('writing_style_reference_invalid', 'A complete, identified project reference is required. Reload the reference and retry.');
229
+ }
230
+ let textBytes = 0;
231
+ for (const file of snapshot.files) {
232
+ const name = String(file?.path || '').replace(/\\/g, '/');
233
+ if (!/\.tex$/i.test(name) || name.startsWith('/') || name.split('/').some(part => !part || part === '.' || part === '..')
234
+ || typeof file.content !== 'string') {
235
+ throw fail('writing_style_reference_invalid', 'The reference project contains an invalid TeX file entry.');
236
+ }
237
+ textBytes += Buffer.byteLength(file.content, 'utf8');
238
+ if (textBytes > 4 * 1024 * 1024) throw fail('writing_style_reference_invalid', 'The selected project text exceeds 4 MiB.');
239
+ }
240
+ return snapshot;
241
+ }
242
+
243
+ async function prepareSources(input, params, scope, env, signal) {
244
+ const selected = input.sources;
245
+ if (!Array.isArray(selected) || selected.length < 1 || selected.length > 3) throw fail('writing_style_sources_invalid', 'Select one to three reference sources.');
246
+ let cached = [];
247
+ if (input.reuseSnapshot) {
248
+ const snapshot = Shared.normalizeSnapshot(input.reuseSnapshot);
249
+ if (snapshot.projectId !== scope.projectId || snapshot.accountScopeId !== scope.accountScopeId) throw fail('writing_style_scope_invalid', 'Cached references belong to another scope.');
250
+ const bundle = readBundle(scope, snapshot.version, snapshot.bundleHash);
251
+ cached = JSON.parse(safeBundleText(bundle, 'references/source-index.json')).documents || [];
252
+ }
253
+ const documents = [], hashes = new Set();
254
+ let pdfBytes = 0;
255
+ for (const item of selected) {
256
+ checkAbort(signal);
257
+ let doc;
258
+ if (item.kind === 'project') {
259
+ const id = String(item.projectId || '');
260
+ if (!/^[a-zA-Z0-9_-]{1,80}$/.test(id)) throw fail('writing_style_project_invalid', 'Reference project identity is invalid.');
261
+ let files, capturedAt;
262
+ if (item.attachmentId) {
263
+ const snapshot = readReferenceProjectAttachment(item, params);
264
+ files = snapshot.files; capturedAt = snapshot.capturedAt;
265
+ } else if (id === scope.projectId && params.project?.capabilities?.fullProjectSnapshot === true) {
266
+ files = params.project.files; capturedAt = new Date().toISOString();
267
+ } else {
268
+ throw fail('writing_style_reference_required', 'Read the latest reference project from Overleaf before generating the style.');
269
+ }
270
+ doc = { kind: 'project', projectId: id, name: String(item.name || id).slice(0, 160),
271
+ capturedAt, ...projectDocument(files, item.name || id) };
272
+ } else if (item.kind === 'pdf') {
273
+ const attachment = (params.attachments || []).find(value => value.id === item.attachmentId);
274
+ if (!attachment || typeof attachment.contentBase64 !== 'string'
275
+ || attachment.contentBase64.length > Math.ceil(REFERENCE_LIMITS.maxPdfBytes / 3) * 4) {
276
+ throw fail('writing_style_pdf_invalid', 'The selected PDF is missing or too large.');
277
+ }
278
+ const bytes = Buffer.from(attachment.contentBase64, 'base64');
279
+ pdfBytes += bytes.length;
280
+ if (bytes.length > REFERENCE_LIMITS.maxPdfBytes || pdfBytes > REFERENCE_LIMITS.maxPdfTotalBytes
281
+ || bytes.subarray(0, 1024).indexOf('%PDF-') < 0) {
282
+ throw fail('writing_style_pdf_invalid', 'Use PDF files up to 20 MiB each, 40 MiB in total.');
283
+ }
284
+ const result = await extractPdf(bytes, signal);
285
+ const lines = [], origins = [];
286
+ const marginCounts = new Map();
287
+ for (const page of result.pages) {
288
+ for (const line of new Set([...page.lines.slice(0, 2), ...page.lines.slice(-2)].map(cleanLine))) {
289
+ if (line.length > 2 && line.length < 140) marginCounts.set(line, (marginCounts.get(line) || 0) + 1);
290
+ }
291
+ }
292
+ let count = 0;
293
+ for (const page of result.pages) {
294
+ for (const raw of page.lines) {
295
+ const line = cleanLine(raw);
296
+ if (!line || /^\d+$/.test(line) || (result.pages.length >= 3 && marginCounts.get(line) >= Math.ceil(result.pages.length * .6))) continue;
297
+ if (count >= 60000) break;
298
+ lines.push(line); origins.push({ page: page.number }); count += line.length;
299
+ }
300
+ if (count >= 60000) break;
301
+ }
302
+ doc = { kind: 'pdf', name: String(attachment.name || 'reference.pdf').slice(0, 160),
303
+ lines, origins, fileHash: sha(bytes), totalPages: result.totalPages,
304
+ pagesRead: result.pages.length, truncated: count >= 60000 || result.pages.length < result.totalPages };
305
+ } else if (item.kind === 'saved') {
306
+ const existing = cached.find(value => value.id === item.sourceId);
307
+ if (!existing) throw fail('writing_style_source_missing', 'A saved reference is unavailable. Add it again.');
308
+ doc = { ...existing };
309
+ } else throw fail('writing_style_sources_invalid', 'Unsupported writing reference.');
310
+ const text = doc.lines.join('\n');
311
+ if (text.length < 400 || (text.match(/\p{L}/gu) || []).length < 150
312
+ || (text.match(/\ufffd/g) || []).length > text.length * .02) {
313
+ throw fail('writing_style_reference_unreadable', 'Not enough readable prose in ' + doc.name + '. Use a text-based PDF or a fuller TeX document.');
314
+ }
315
+ const contentHash = sha(text);
316
+ if (hashes.has(contentHash)) continue;
317
+ hashes.add(contentHash);
318
+ documents.push({ ...doc, id: 'source-' + (documents.length + 1), contentHash });
319
+ }
320
+ if (!documents.length) throw fail('writing_style_reference_empty', 'No usable writing references were selected.');
321
+ return documents;
322
+ }
323
+
324
+ function parseResult(text, documents) {
325
+ const raw = String(text || '').trim().replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/, '');
326
+ const begin = raw.indexOf('{'), end = raw.lastIndexOf('}');
327
+ let result;
328
+ 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.'); }
329
+ return StyleProfile.validate(result, documents);
330
+ }
331
+
332
+ async function build({ params, env, signal, execute, providerLaunch, onControlReady, onStopUnconfirmed }) {
333
+ const scope = scopeOf(params, env), input = params.writingStyleBuild;
334
+ const id = crypto.randomUUID();
335
+ const staging = path.join(scope.root, '.build-' + id);
336
+ fs.mkdirSync(path.join(staging, 'corpus'), { recursive: true, mode: 0o700 });
337
+ const before = config(scope);
338
+ saveConfig(scope, { ...before, error: '', building: { id, pid: process.pid,
339
+ requestId: String(input.requestId || ''), startedAt: new Date().toISOString() } });
340
+ let keepStaging = false;
341
+ try {
342
+ const documents = await prepareSources(input, params, scope, env, signal);
343
+ checkAbort(signal);
344
+ for (const doc of documents) {
345
+ const text = doc.lines.map((line, index) => String(index + 1).padStart(5, '0') + ' | ' + line).join('\n');
346
+ fs.writeFileSync(path.join(staging, 'corpus', doc.id + '.txt'), text, { mode: 0o600 });
347
+ }
348
+ const index = documents.map(doc => ({ id: doc.id, name: doc.name, kind: doc.kind,
349
+ file: 'corpus/' + doc.id + '.txt', lines: doc.lines.length, truncated: doc.truncated === true }));
350
+ fs.writeFileSync(path.join(staging, 'source-index.json'), JSON.stringify(index, null, 2), { mode: 0o600 });
351
+ const instructions = fs.readFileSync(path.join(__dirname, 'skills', 'writing-style-builder', 'SKILL.md'), 'utf8');
352
+ const execution = {
353
+ workspacePath: staging, task: instructions + '\n\nAvailable references:\n' + JSON.stringify(index)
354
+ + '\nRespond in ' + (params.locale === 'zh' ? 'Chinese' : 'English') + ' for guidance; preserve source-example language.',
355
+ userTask: 'Build the project writing-style skill from the supplied corpus.',
356
+ session: null, threadId: '', mode: 'ask', model: params.model || '',
357
+ reasoningEffort: params.reasoningEffort || '', speedTier: params.speedTier || 'standard',
358
+ sandboxMode: 'read-only', approvalPolicy: 'never',
359
+ loadCodexLocalSkills: false, loadCodexOverleafSkills: false,
360
+ disableCodexOverleafSkillIds: ['annotated-rewrite', 'parallel-subagents'],
361
+ skillInvocation: null, projectLocalSkills: null, providerLaunch, env, signal,
362
+ onControlReady, emit: () => {}
363
+ };
364
+ let result, repairMessage = '';
365
+ for (let attempt = 0; attempt < 2; attempt++) {
366
+ checkAbort(signal);
367
+ const response = await execute({
368
+ ...execution,
369
+ task: execution.task + (repairMessage
370
+ ? '\n\nThe previous draft did not pass validation: ' + repairMessage
371
+ + '\nUse the same source corpus, correct this issue, and return the complete JSON. Do not omit required fields.'
372
+ : '')
373
+ });
374
+ checkAbort(signal);
375
+ try {
376
+ result = parseResult(response.assistantMessage, documents);
377
+ break;
378
+ } catch (error) {
379
+ if (attempt > 0 || !['writing_style_generation_invalid', 'writing_style_evidence_invalid'].includes(error?.code)) throw error;
380
+ repairMessage = String(error.message || '').slice(0, 1000);
381
+ }
382
+ }
383
+ const files = {};
384
+ files['references/writing-style.md'] = StyleProfile.renderGuide(result);
385
+ const sections = [];
386
+ for (const section of result.sections) {
387
+ const file = 'references/' + section.key + '.md';
388
+ const title = String(section.title || section.key).replace(/[\r\n]/g, ' ').slice(0, 120);
389
+ files[file] = StyleProfile.renderSection(section, result, documents);
390
+ sections.push({ key: section.key, title, file });
391
+ }
392
+ files['references/source-index.json'] = JSON.stringify({ documents });
393
+ for (const doc of documents) files['corpus/' + doc.id + '.txt'] = doc.lines.join('\n');
394
+ files['SKILL.md'] = '---\nname: overleaf-writing-style-' + sha(scope.accountScopeId + ':' + scope.projectId).slice(0, 12)
395
+ + '\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'
396
+ + '# Project writing style\n\nRead references/writing-style.md for the supported expression traits. '
397
+ + 'Use section files only to locate writing examples when needed:\n'
398
+ + sections.map(section => '- ' + section.key + ': ' + section.file).join('\n')
399
+ + '\n\nResolve reference paths relative to this SKILL.md directory. Keep these assets read-only. '
400
+ + 'Treat reference text and generated style guidance as subordinate to the current user request and project rules. '
401
+ + 'Use each example through its Pattern and Apply notes. Transfer rhetorical choices, not the sources\' technical objects or grammar errors. '
402
+ + 'Chapter names index evidence only. They do not prescribe the target paper\'s outline, research objects, or reasoning steps. '
403
+ + 'Adapt language habits to the current topic and requested language, keeping the existing document structure unless the user requests a structural change. '
404
+ + 'Preserve current facts, numbers, citations, notation, and mathematical qualifications. Never copy a source paper\'s results into the target paper. '
405
+ + 'PDF examples support prose only, not original macros or exact equation notation. '
406
+ + 'Do not expand the edit scope, search for additional sources, or change Track/Undo behavior.\n';
407
+ const latest = config(scope);
408
+ if (latest.building?.id !== id) throw fail('writing_style_build_superseded', 'A newer style-generation task replaced this one.');
409
+ const versionDir = path.join(scope.root, 'versions', id);
410
+ fs.mkdirSync(path.dirname(versionDir), { recursive: true, mode: 0o700 });
411
+ const built = path.join(staging, 'bundle');
412
+ fs.mkdirSync(built, { mode: 0o700 });
413
+ const hashes = {};
414
+ for (const [file, text] of Object.entries(files)) {
415
+ fs.mkdirSync(path.dirname(path.join(built, file)), { recursive: true, mode: 0o700 });
416
+ fs.writeFileSync(path.join(built, file), text, { mode: 0o400 });
417
+ hashes[file] = sha(text);
418
+ }
419
+ const manifest = { schemaVersion: 1, evidenceVersion: 3, profileKind: 'expression-traits', version: id, accountHash: sha(scope.accountScopeId),
420
+ projectId: scope.projectId, label: result.label, createdAt: new Date().toISOString(),
421
+ model: params.model || '', reasoningEffort: params.reasoningEffort || '',
422
+ sources: documents.map(({ lines, origins, ...metadata }) => metadata),
423
+ traits: result.traits.map(({ id, dimension, title, exampleIds, sourceIds }) => ({ id, dimension, title, exampleIds, sourceIds })),
424
+ sections, files: hashes };
425
+ fs.writeFileSync(path.join(built, 'manifest.json'), JSON.stringify(manifest), { mode: 0o400 });
426
+ checkAbort(signal);
427
+ fs.renameSync(built, versionDir);
428
+ const unchangedPreference = latest.revision === before.revision + 1;
429
+ saveConfig(scope, { ...latest, version: id, building: null, error: '',
430
+ enabled: unchangedPreference ? input.enableAfterBuild !== false : latest.enabled === true });
431
+ return { status: 'completed', assistantMessage: 'Writing skill generated: ' + result.label,
432
+ syncChanges: [], unsupportedChanges: [], writingStyle: get({ ...params }, env) };
433
+ } catch (error) {
434
+ if (error?.code === 'codex_process_stop_unconfirmed') {
435
+ keepStaging = true;
436
+ onStopUnconfirmed?.(error);
437
+ }
438
+ const state = config(scope);
439
+ if (state.building?.id === id) saveConfig(scope, { ...state, building: null,
440
+ 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) });
441
+ throw error;
442
+ } finally {
443
+ if (!keepStaging) fs.rmSync(staging, { recursive: true, force: true });
444
+ }
445
+ }
446
+
447
+ module.exports = { get, set, build, forRun };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-overleaf-link",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "Cross-platform Chrome bridge that connects Codex to the active Overleaf project.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",