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
@@ -4,6 +4,7 @@ const crypto = require('node:crypto');
4
4
  const fs = require('node:fs');
5
5
  const os = require('node:os');
6
6
  const path = require('node:path');
7
+ const { isDeepStrictEqual } = require('node:util');
7
8
  const {
8
9
  computeConnectionFingerprint,
9
10
  computeDraftFingerprint,
@@ -16,6 +17,9 @@ const {
16
17
  } = require('./providerProfile');
17
18
 
18
19
  const SCHEMA_VERSION = 1;
20
+ const STORE_FILE_MAX_BYTES = 1024 * 1024;
21
+ // A compact journal contains two bounded Store files plus revision/schema keys.
22
+ const STORE_TRANSACTION_MAX_BYTES = 2 * STORE_FILE_MAX_BYTES + 1024;
19
23
  const STORE_LOCK_TIMEOUT_MS = 2500;
20
24
  const STORE_LOCK_STALE_MS = 30000;
21
25
  const STORE_LOCK_RETRY_MS = 25;
@@ -261,6 +265,10 @@ function recordProviderDiagnosticUnlocked(params = {}, env = process.env) {
261
265
  return { recorded: false };
262
266
  }
263
267
  const modelId = String(params.modelId || '').trim();
268
+ // Draft tests may cover models that are not saved yet; only saved models carry diagnostics.
269
+ if (!profile.models.some(model => model.id === modelId)) {
270
+ return { recorded: false };
271
+ }
264
272
  const secret = state.secrets.secrets[providerId] || '';
265
273
  const connectionFingerprint = computeConnectionFingerprint(profile, secret, modelId);
266
274
  if (connectionFingerprint !== String(params.connectionFingerprint || '') || !isResolvedWireApi(params.wireApi)) {
@@ -353,6 +361,7 @@ function loadProviderStateUnlocked(env = process.env) {
353
361
  : [];
354
362
  return {
355
363
  paths,
364
+ stored: { public: publicStore, secrets: secretStore },
356
365
  public: {
357
366
  schemaVersion: SCHEMA_VERSION,
358
367
  storeRevision: normalizeRevision(publicStore.storeRevision),
@@ -369,23 +378,35 @@ function loadProviderStateUnlocked(env = process.env) {
369
378
 
370
379
  function commitProviderState(previous, next) {
371
380
  const revision = previous.public.storeRevision + 1;
372
- const nextPublic = { ...next.public, schemaVersion: SCHEMA_VERSION, storeRevision: revision };
373
- const nextSecrets = { ...next.secrets, schemaVersion: SCHEMA_VERSION, storeRevision: revision };
381
+ const previousPublic = previous.stored.public;
382
+ const previousSecrets = previous.stored.secrets;
383
+ const nextPublic = { ...mergeStoredChanges(previousPublic, previous.public, next.public), schemaVersion: SCHEMA_VERSION, storeRevision: revision };
384
+ const nextSecrets = { ...mergeStoredChanges(previousSecrets, previous.secrets, next.secrets), schemaVersion: SCHEMA_VERSION, storeRevision: revision };
385
+ const compactPublic = Buffer.byteLength(`${JSON.stringify(previousPublic, null, 2)}\n`) > STORE_FILE_MAX_BYTES;
386
+ const compactSecrets = Buffer.byteLength(`${JSON.stringify(previousSecrets, null, 2)}\n`) > STORE_FILE_MAX_BYTES;
374
387
  const journal = {
375
388
  schemaVersion: SCHEMA_VERSION,
376
389
  nextRevision: revision,
377
- previousPublic: previous.public,
378
- previousSecrets: previous.secrets
390
+ previousPublic,
391
+ previousSecrets,
392
+ compactPublic,
393
+ compactSecrets
379
394
  };
395
+ // Validate every write, including rollback copies, before replacing any file.
396
+ serializeStoreJson(previous.paths.publicFile, nextPublic, compactPublic);
397
+ serializeStoreJson(previous.paths.secretFile, nextSecrets, compactSecrets);
398
+ serializeStoreJson(previous.paths.publicFile, previousPublic, compactPublic);
399
+ serializeStoreJson(previous.paths.secretFile, previousSecrets, compactSecrets);
400
+ serializeStoreJson(previous.paths.transactionFile, journal);
380
401
  atomicWriteJson(previous.paths.transactionFile, journal);
381
402
  try {
382
- atomicWriteJson(previous.paths.secretFile, nextSecrets);
383
- atomicWriteJson(previous.paths.publicFile, nextPublic);
403
+ atomicWriteJson(previous.paths.secretFile, nextSecrets, compactSecrets);
404
+ atomicWriteJson(previous.paths.publicFile, nextPublic, compactPublic);
384
405
  fs.rmSync(previous.paths.transactionFile, { force: true });
385
406
  } catch (error) {
386
407
  try {
387
- atomicWriteJson(previous.paths.secretFile, previous.secrets);
388
- atomicWriteJson(previous.paths.publicFile, previous.public);
408
+ atomicWriteJson(previous.paths.secretFile, previousSecrets, compactSecrets);
409
+ atomicWriteJson(previous.paths.publicFile, previousPublic, compactPublic);
389
410
  fs.rmSync(previous.paths.transactionFile, { force: true });
390
411
  } catch (_restoreError) {
391
412
  // The journal remains the recovery source on the next Native Host start.
@@ -394,6 +415,26 @@ function commitProviderState(previous, next) {
394
415
  }
395
416
  }
396
417
 
418
+ function mergeStoredChanges(stored, before, after) {
419
+ if (isDeepStrictEqual(before, after)) return stored;
420
+ if (!stored || !before || !after || typeof stored !== 'object' || typeof before !== 'object' || typeof after !== 'object') return after;
421
+ if (Array.isArray(before) || Array.isArray(after)) {
422
+ // Profiles and models carry stable IDs; changed membership must not persist
423
+ // normalization defaults into unrelated records at the storage boundary.
424
+ if (!Array.isArray(stored) || !Array.isArray(before) || !Array.isArray(after)
425
+ || [...before, ...after].some(item => !item?.id)) return after;
426
+ const oldById = new Map(before.map(item => [item.id, item]));
427
+ const storedById = new Map(stored.map(item => [typeof item === 'string' ? item : item?.id, item]));
428
+ return after.map(item => mergeStoredChanges(storedById.get(item.id), oldById.get(item.id), item));
429
+ }
430
+ const result = { ...stored };
431
+ for (const key of new Set([...Object.keys(before), ...Object.keys(after)])) {
432
+ if (!Object.prototype.hasOwnProperty.call(after, key)) delete result[key];
433
+ else if (!isDeepStrictEqual(before[key], after[key])) result[key] = mergeStoredChanges(stored[key], before[key], after[key]);
434
+ }
435
+ return result;
436
+ }
437
+
397
438
  function recoverInterruptedTransaction(paths) {
398
439
  if (!fs.existsSync(paths.transactionFile)) {
399
440
  return;
@@ -408,8 +449,10 @@ function recoverInterruptedTransaction(paths) {
408
449
  fs.rmSync(paths.transactionFile, { force: true });
409
450
  return;
410
451
  }
411
- atomicWriteJson(paths.secretFile, journal.previousSecrets);
412
- atomicWriteJson(paths.publicFile, journal.previousPublic);
452
+ serializeStoreJson(paths.secretFile, journal.previousSecrets, journal.compactSecrets === true);
453
+ serializeStoreJson(paths.publicFile, journal.previousPublic, journal.compactPublic === true);
454
+ atomicWriteJson(paths.secretFile, journal.previousSecrets, journal.compactSecrets === true);
455
+ atomicWriteJson(paths.publicFile, journal.previousPublic, journal.compactPublic === true);
413
456
  fs.rmSync(paths.transactionFile, { force: true });
414
457
  }
415
458
 
@@ -498,11 +541,16 @@ function normalizeStoredProfile(value) {
498
541
  if (!value || typeof value !== 'object' || !normalizeId(value.id)) {
499
542
  throw providerError('provider_store_corrupt', 'Provider store contains an invalid profile.');
500
543
  }
501
- const normalized = normalizeProviderDraft(value);
544
+ const legacyOutputBudget = value.outputBudgetVersion !== 1
545
+ && [8192, 32768].includes(Number(value.maxOutputTokens));
546
+ const normalized = normalizeProviderDraft(legacyOutputBudget ? { ...value, maxOutputTokens: 65536 } : value);
502
547
  return {
503
548
  id: normalizeId(value.id),
504
549
  revision: Math.max(1, normalizeRevision(value.revision)),
505
550
  ...normalized,
551
+ // Keep the old provenance until an explicit upsert persists the new value.
552
+ // Unrelated mutations must not mark an unmigrated raw record as current.
553
+ outputBudgetVersion: value.outputBudgetVersion === 1 ? 1 : 0,
506
554
  resolvedWireApi: isResolvedWireApi(value.resolvedWireApi) ? value.resolvedWireApi : '',
507
555
  endpointDisclosureHost: String(value.endpointDisclosureHost || ''),
508
556
  endpointDisclosureBaseUrl: String(value.endpointDisclosureBaseUrl || ''),
@@ -630,11 +678,12 @@ function ensurePrivateDirectory(directory) {
630
678
  }
631
679
  }
632
680
 
633
- function atomicWriteJson(target, value) {
681
+ function atomicWriteJson(target, value, compact = false) {
682
+ const serialized = serializeStoreJson(target, value, compact);
634
683
  const temp = `${target}.tmp-${process.pid}-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;
635
684
  const fd = fs.openSync(temp, 'w', 0o600);
636
685
  try {
637
- fs.writeFileSync(fd, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
686
+ fs.writeFileSync(fd, serialized, 'utf8');
638
687
  fs.fsyncSync(fd);
639
688
  } finally {
640
689
  fs.closeSync(fd);
@@ -645,13 +694,26 @@ function atomicWriteJson(target, value) {
645
694
  fs.renameSync(temp, target);
646
695
  }
647
696
 
697
+ function getStoreFileMaxBytes(target) {
698
+ return path.basename(target) === 'provider-transaction.json' ? STORE_TRANSACTION_MAX_BYTES : STORE_FILE_MAX_BYTES;
699
+ }
700
+
701
+ function serializeStoreJson(target, value, compact = false) {
702
+ const serialized = `${JSON.stringify(value, null, compact || path.basename(target) === 'provider-transaction.json' ? undefined : 2)}\n`;
703
+ const maxBytes = getStoreFileMaxBytes(target);
704
+ if (Buffer.byteLength(serialized, 'utf8') > maxBytes) {
705
+ throw providerError('provider_store_too_large', `Provider settings exceed the ${maxBytes}-byte limit for ${path.basename(target)}. Reduce the configuration and retry.`);
706
+ }
707
+ return serialized;
708
+ }
709
+
648
710
  function readJson(target, fallback) {
649
711
  if (!fs.existsSync(target)) {
650
712
  return fallback;
651
713
  }
652
714
  try {
653
715
  const stat = fs.statSync(target);
654
- if (!stat.isFile() || stat.size > 1024 * 1024) {
716
+ if (!stat.isFile() || stat.size > getStoreFileMaxBytes(target)) {
655
717
  throw new Error('invalid provider store file');
656
718
  }
657
719
  return JSON.parse(fs.readFileSync(target, 'utf8'));
@@ -22,7 +22,7 @@ function terminalError(code, message) {
22
22
  return Object.assign(updateError(code, message), { retryable: false });
23
23
  }
24
24
 
25
- async function readBoundedBody(response, limit, signal) {
25
+ async function readBoundedBody(response, limit, signal, onProgress = () => {}) {
26
26
  const declared = Number(response.headers?.get?.('content-length') || 0);
27
27
  if (declared > limit) throw terminalError('update_download_limit', 'Update response exceeds its size limit.');
28
28
  const reader = response.body?.getReader?.();
@@ -39,6 +39,7 @@ async function readBoundedBody(response, limit, signal) {
39
39
  if (chunk.done) break;
40
40
  const bytes = Buffer.from(chunk.value);
41
41
  length += bytes.length;
42
+ if (bytes.length) onProgress();
42
43
  if (length > limit) throw terminalError('update_download_limit', 'Update response exceeds its size limit.');
43
44
  chunks.push(bytes);
44
45
  }
@@ -95,8 +96,14 @@ async function requestRelease(url, options = {}) {
95
96
  const remaining = deadline - Date.now();
96
97
  if (remaining <= 0) break;
97
98
  const controller = new AbortController();
98
- const timer = setTimeout(() => controller.abort(new DOMException('Update request timed out.', 'TimeoutError')),
99
- Math.min(remaining, options.timeoutMs ?? 12000));
99
+ const timeout = () => controller.abort(new DOMException('Update request timed out.', 'TimeoutError'));
100
+ const totalTimer = setTimeout(timeout, remaining);
101
+ let phaseTimer = setTimeout(timeout, Math.min(remaining, options.timeoutMs ?? 12000));
102
+ const receivedBytes = () => {
103
+ clearTimeout(phaseTimer);
104
+ phaseTimer = setTimeout(timeout, Math.min(Math.max(1, deadline - Date.now()),
105
+ options.idleTimeoutMs ?? options.timeoutMs ?? 12000));
106
+ };
100
107
  const cancel = () => controller.abort(options.signal.reason);
101
108
  if (options.signal?.aborted) cancel();
102
109
  else options.signal?.addEventListener('abort', cancel, { once: true });
@@ -108,6 +115,7 @@ async function requestRelease(url, options = {}) {
108
115
  method: options.method || 'GET', headers: options.headers,
109
116
  redirect: 'follow', signal: controller.signal
110
117
  }), controller.signal);
118
+ receivedBytes();
111
119
  const finalUrl = new URL(response.url || url);
112
120
  if (options.allowedHosts && (finalUrl.protocol !== 'https:' || !options.allowedHosts.has(finalUrl.hostname))) {
113
121
  throw terminalError('update_asset_redirect_forbidden', 'Release request redirected to an untrusted host.');
@@ -117,7 +125,7 @@ async function requestRelease(url, options = {}) {
117
125
  'Update request failed with HTTP ' + response.status + '.'), { retryable: RETRYABLE_HTTP.has(response.status) });
118
126
  }
119
127
  const bytes = options.method === 'HEAD' || response.status === 304
120
- ? null : await readBoundedBody(response, options.limit ?? 32 * 1024 * 1024, controller.signal);
128
+ ? null : await readBoundedBody(response, options.limit ?? 32 * 1024 * 1024, controller.signal, receivedBytes);
121
129
  return { ok: response.ok, status: response.status, url: finalUrl.href, headers: response.headers,
122
130
  bytes, attempts: attempt, elapsedMs: Date.now() - started };
123
131
  } catch (error) {
@@ -140,7 +148,8 @@ async function requestRelease(url, options = {}) {
140
148
  if (!classified.retryable || attempt === maxAttempts) throw failure;
141
149
  retryAfter = retryDelay(response, (options.delayMs ?? 500) * attempt);
142
150
  } finally {
143
- clearTimeout(timer);
151
+ clearTimeout(totalTimer);
152
+ clearTimeout(phaseTimer);
144
153
  options.signal?.removeEventListener('abort', cancel);
145
154
  controller.abort();
146
155
  if (response?.body && !response.body.locked) void response.body.cancel().catch(() => {});
@@ -4,6 +4,7 @@ const crypto = require('node:crypto');
4
4
  const http = require('node:http');
5
5
  const { classifyResponsesRoute } = require('./providerBridgeRoutes');
6
6
  const { providerError } = require('./providerProfile');
7
+ const { fetchProvider, readProviderText } = require('./providerHttp');
7
8
  const { sanitizeProviderMessage } = require('./providerRedaction');
8
9
  const { logDebug } = require('./debugLog');
9
10
  const {
@@ -19,6 +20,7 @@ const MAX_HISTORY_ITEMS = 4096;
19
20
  const MAX_HISTORY_BYTES = 32 * 1024 * 1024;
20
21
  const MAX_DUPLICATE_CONTINUATIONS = 2;
21
22
  const MAX_STREAM_BUFFER_BYTES = 8 * 1024 * 1024;
23
+ const MAX_COMPACT_RESPONSE_BYTES = 32 * 1024 * 1024;
22
24
  const DEFAULT_MAX_OUTPUT_TOKENS = 65536;
23
25
  const CONTINUATION_MARKER = '[codex-overleaf-provider-reasoning-continuation]';
24
26
 
@@ -102,7 +104,7 @@ async function handleRequest({ req, res, launch, clientToken, activeRequests, hi
102
104
  const prepared = route === 'responses'
103
105
  ? prepareResponsesRequest({ requestBody, launch, history, previous, originalPreviousResponseId })
104
106
  : {
105
- body: { ...requestBody },
107
+ body: { ...(launch.bodyOverrides || {}), ...requestBody, previous_response_id: requestBody.previous_response_id },
106
108
  currentInput: normalizeInputItems(requestBody.input),
107
109
  replayed: false
108
110
  };
@@ -140,19 +142,19 @@ async function handleRequest({ req, res, launch, clientToken, activeRequests, hi
140
142
  res.on('close', onClientClose);
141
143
  lifecycle.start();
142
144
  try {
143
- const upstream = await fetch(buildResponsesUrl(launch.baseUrl, launch, route), {
145
+ const upstream = await fetchProvider(buildResponsesUrl(launch.baseUrl, launch, route), {
144
146
  method: 'POST',
145
147
  headers: buildUpstreamHeaders(launch),
146
148
  body: JSON.stringify(prepared.body),
147
149
  signal: controller.signal
148
- });
150
+ }, launch);
149
151
  lifecycle.touch();
150
152
  if (!upstream.ok) {
151
- await forwardUpstreamError(upstream, res, launch.apiKey);
153
+ await forwardUpstreamError(upstream, res, launch.apiKey, lifecycle.touch);
152
154
  return;
153
155
  }
154
156
  if (route === 'compact') {
155
- await forwardOpaqueResponse(upstream, res, lifecycle.touch);
157
+ await forwardCompactResponse(upstream, res, lifecycle.touch, launch.apiKey);
156
158
  return;
157
159
  }
158
160
 
@@ -165,11 +167,18 @@ async function handleRequest({ req, res, launch, clientToken, activeRequests, hi
165
167
  'The provider returned a non-streaming payload for a streaming Responses request.'
166
168
  );
167
169
  }
168
- response = await forwardResponsesSse(upstream, res, lifecycle.touch);
170
+ response = await forwardResponsesSse(upstream, res, lifecycle.touch, launch.apiKey);
169
171
  } else {
170
- response = await upstream.json();
172
+ const text = await readProviderText(upstream, lifecycle.touch);
173
+ try {
174
+ response = JSON.parse(text);
175
+ } catch (_error) {
176
+ throw providerError('provider_response_invalid', 'The provider returned an invalid Responses JSON response.');
177
+ }
171
178
  lifecycle.touch();
172
- sendJson(res, 200, response);
179
+ sanitizeProtocolErrors(response, launch.apiKey, hasDuplicateJsonKeys(text));
180
+ if (requestBody.stream !== false) writeBufferedResponseAsSse(res, response);
181
+ else sendJson(res, 200, response);
173
182
  }
174
183
  if (response?.id) {
175
184
  rememberResponse(history, {
@@ -228,7 +237,10 @@ async function handleRequest({ req, res, launch, clientToken, activeRequests, hi
228
237
  function prepareResponsesRequest({ requestBody, launch, history, previous, originalPreviousResponseId }) {
229
238
  const currentInput = normalizeInputItems(requestBody.input);
230
239
  const body = {
240
+ ...(launch.bodyOverrides || {}),
231
241
  ...requestBody,
242
+ previous_response_id: requestBody.previous_response_id,
243
+ stream: requestBody.stream !== false && launch.upstreamResponseMode !== 'buffered',
232
244
  max_output_tokens: resolveMaxOutputTokens(launch.maxOutputTokens, requestBody.max_output_tokens)
233
245
  };
234
246
  if (!previous?.continuationRequired) {
@@ -405,7 +417,75 @@ function resolveMaxOutputTokens(configured, requested) {
405
417
  return Math.max(1, Math.floor(value));
406
418
  }
407
419
 
408
- async function forwardResponsesSse(upstream, res, onActivity) {
420
+ function writeBufferedResponseAsSse(res, response) {
421
+ if (!response || typeof response !== 'object' || Array.isArray(response)) {
422
+ throw providerError('provider_response_invalid', 'The provider returned an invalid Responses JSON response.');
423
+ }
424
+ if (!response.error && !['completed', 'incomplete', 'failed'].includes(response.status)) {
425
+ throw providerError('provider_response_invalid', 'The buffered Responses request did not return a terminal response.');
426
+ }
427
+ res.writeHead(200, { 'content-type': 'text/event-stream; charset=utf-8', 'cache-control': 'no-cache, no-store' });
428
+ let sequence = 0;
429
+ const emit = (type, detail) => {
430
+ const frame = `event: ${type}\ndata: ${JSON.stringify({ type, sequence_number: sequence++, ...detail })}\n\n`;
431
+ if (Buffer.byteLength(frame, 'utf8') > MAX_STREAM_BUFFER_BYTES) {
432
+ throw providerError('provider_stream_frame_too_large', 'Provider stream frame exceeded the local bridge limit.');
433
+ }
434
+ res.write(frame);
435
+ };
436
+ if (response.error && !Array.isArray(response.output)) {
437
+ emit('error', { ...response, type: 'error' });
438
+ res.end();
439
+ return;
440
+ }
441
+ const pending = { ...response, status: 'in_progress', output: [], usage: null, error: null, incomplete_details: null };
442
+ emit('response.created', { response: pending });
443
+ emit('response.in_progress', { response: pending });
444
+ for (const [output_index, item] of (response.output || []).entries()) {
445
+ const identity = { item_id: item.id, output_index };
446
+ const added = { ...item, status: 'in_progress' };
447
+ if (item.type === 'message') added.content = [];
448
+ if (item.type === 'reasoning') added.summary = [];
449
+ if (item.type === 'function_call') added.arguments = '';
450
+ if (item.type === 'custom_tool_call') added.input = '';
451
+ emit('response.output_item.added', { output_index, item: added });
452
+ for (const [content_index, part] of (item.content || []).entries()) {
453
+ const detail = { ...identity, content_index };
454
+ const emptyPart = part.type === 'refusal' ? { ...part, refusal: '' }
455
+ : { ...part, ...(typeof part.text === 'string' ? { text: '' } : {}) };
456
+ emit('response.content_part.added', { ...detail, part: emptyPart });
457
+ if (part.type === 'output_text') {
458
+ if (part.text) emit('response.output_text.delta', { ...detail, delta: part.text, logprobs: [] });
459
+ emit('response.output_text.done', { ...detail, text: part.text || '', logprobs: [] });
460
+ } else if (part.type === 'refusal') {
461
+ if (part.refusal) emit('response.refusal.delta', { ...detail, delta: part.refusal });
462
+ emit('response.refusal.done', { ...detail, refusal: part.refusal || '' });
463
+ }
464
+ emit('response.content_part.done', { ...detail, part });
465
+ }
466
+ for (const [summary_index, part] of (item.summary || []).entries()) {
467
+ const detail = { ...identity, summary_index };
468
+ emit('response.reasoning_summary_part.added', { ...detail, part: { ...part, text: '' } });
469
+ if (part.text) emit('response.reasoning_summary_text.delta', { ...detail, delta: part.text });
470
+ emit('response.reasoning_summary_text.done', { ...detail, text: part.text || '' });
471
+ emit('response.reasoning_summary_part.done', { ...detail, part });
472
+ }
473
+ if (item.type === 'function_call' || item.type === 'custom_tool_call') {
474
+ const custom = item.type === 'custom_tool_call';
475
+ const value = custom ? item.input : item.arguments;
476
+ const event = custom ? 'response.custom_tool_call_input' : 'response.function_call_arguments';
477
+ if (value) emit(event + '.delta', { ...identity, delta: value });
478
+ emit(event + '.done', { ...identity, [custom ? 'input' : 'arguments']: value || (custom ? '' : '{}') });
479
+ }
480
+ emit('response.output_item.done', { output_index, item });
481
+ }
482
+ const status = response.error || response.status === 'failed' ? 'failed'
483
+ : response.status === 'incomplete' ? 'incomplete' : 'completed';
484
+ emit('response.' + status, { response });
485
+ res.end();
486
+ }
487
+
488
+ async function forwardResponsesSse(upstream, res, onActivity, apiKey) {
409
489
  res.writeHead(200, {
410
490
  'content-type': 'text/event-stream; charset=utf-8',
411
491
  'cache-control': 'no-cache, no-store',
@@ -415,67 +495,199 @@ async function forwardResponsesSse(upstream, res, onActivity) {
415
495
  throw providerError('provider_response_invalid', 'The provider returned an empty Responses stream.');
416
496
  }
417
497
  const reader = upstream.body.getReader();
418
- const decoder = new TextDecoder();
419
- const observer = { buffer: '', terminalResponse: null };
420
- while (true) {
421
- const { done, value } = await reader.read();
422
- if (done) break;
423
- onActivity();
424
- observeSseText(observer, decoder.decode(value, { stream: true }));
425
- if (!res.writableEnded) res.write(Buffer.from(value));
426
- }
427
- observeSseText(observer, decoder.decode(), true);
498
+ const decoder = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true });
499
+ const observer = { buffer: '', position: 0, lineStart: 0, terminalResponse: null };
500
+ const forward = frame => {
501
+ const safeFrame = inspectSseFrame(observer, frame, apiKey);
502
+ if (!res.writableEnded) res.write(safeFrame);
503
+ };
504
+ let complete = false;
505
+ try {
506
+ while (true) {
507
+ const { done, value } = await reader.read();
508
+ if (done) break;
509
+ onActivity();
510
+ forwardSseText(observer, decoder.decode(value, { stream: true }), forward);
511
+ }
512
+ forwardSseText(observer, decoder.decode(), forward, true);
513
+ complete = true;
514
+ } finally {
515
+ if (!complete) await reader.cancel().catch(() => {});
516
+ reader.releaseLock();
517
+ }
428
518
  if (!res.writableEnded) res.end();
429
519
  return observer.terminalResponse;
430
520
  }
431
521
 
432
- function observeSseText(observer, text, flush = false) {
522
+ function forwardSseText(observer, text, forward, flush = false) {
433
523
  observer.buffer += text;
524
+ // Hold each event until its diagnostics can be inspected. Line endings and
525
+ // successful frames remain intact, including CRLF split across HTTP chunks.
526
+ while (observer.position < observer.buffer.length) {
527
+ const position = observer.position;
528
+ const char = observer.buffer[position];
529
+ if (char !== '\r' && char !== '\n') { observer.position += 1; continue; }
530
+ if (char === '\r' && position + 1 === observer.buffer.length && !flush) break;
531
+ const end = position + (char === '\r' && observer.buffer[position + 1] === '\n' ? 2 : 1);
532
+ if (position === observer.lineStart) {
533
+ forward(observer.buffer.slice(0, end));
534
+ observer.buffer = observer.buffer.slice(end);
535
+ observer.position = observer.lineStart = 0;
536
+ } else {
537
+ observer.position = observer.lineStart = end;
538
+ }
539
+ }
434
540
  if (Buffer.byteLength(observer.buffer, 'utf8') > MAX_STREAM_BUFFER_BYTES) {
435
541
  throw providerError('provider_stream_frame_too_large', 'Provider stream frame exceeded the local bridge limit.');
436
542
  }
437
- let match;
438
- while ((match = /\r?\n\r?\n/.exec(observer.buffer))) {
439
- const block = observer.buffer.slice(0, match.index);
440
- observer.buffer = observer.buffer.slice(match.index + match[0].length);
441
- observeSseBlock(observer, block);
442
- }
443
543
  if (flush && observer.buffer.trim()) {
444
- observeSseBlock(observer, observer.buffer);
544
+ forward(observer.buffer);
445
545
  observer.buffer = '';
446
546
  }
447
547
  }
448
548
 
449
- function observeSseBlock(observer, block) {
450
- const data = String(block || '')
451
- .split(/\r?\n/)
452
- .filter(line => line.startsWith('data:'))
453
- .map(line => line.slice(5).trimStart())
454
- .join('\n');
455
- if (!data || data === '[DONE]') return;
549
+ function inspectSseFrame(observer, frame, apiKey) {
550
+ if (Buffer.byteLength(frame, 'utf8') > MAX_STREAM_BUFFER_BYTES) {
551
+ throw providerError('provider_stream_frame_too_large', 'Provider stream frame exceeded the local bridge limit.');
552
+ }
553
+ const parts = frame.split(/(\r\n|\r|\n)/);
554
+ const dataIndexes = [];
555
+ let event = '';
556
+ for (let index = 0; index < parts.length; index += 2) {
557
+ const line = parts[index].replace(/^\uFEFF/, '');
558
+ if (/^data(?::|$)/.test(line)) dataIndexes.push(index);
559
+ if (line.startsWith('event:')) event = line.slice(6).replace(/^ /, '');
560
+ }
561
+ const failedEvent = ['error', 'response.failed', 'response.error'].includes(event);
562
+ if (!dataIndexes.length && !failedEvent) return frame;
563
+ const data = dataIndexes.map(index => parts[index].replace(/^\uFEFF/, '').replace(/^data:? ?/, '')).join('\n');
564
+ if ((!data || data === '[DONE]') && !failedEvent) return frame;
565
+ let payload;
456
566
  try {
457
- const payload = JSON.parse(data);
458
- if (['response.completed', 'response.incomplete', 'response.failed'].includes(payload?.type) && payload.response) {
459
- observer.terminalResponse = payload.response;
567
+ payload = JSON.parse(data);
568
+ } catch (_error) {
569
+ throw providerError('provider_response_invalid', 'The provider returned an invalid Responses stream event.');
570
+ }
571
+ if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
572
+ throw providerError('provider_response_invalid', 'The provider returned an invalid Responses stream event.');
573
+ }
574
+ const changed = sanitizeProtocolErrors(payload, apiKey, failedEvent || hasDuplicateJsonKeys(data));
575
+ if (['response.completed', 'response.incomplete', 'response.failed'].includes(payload.type || event) && payload.response) {
576
+ observer.terminalResponse = payload.response;
577
+ }
578
+ if (!changed) return frame;
579
+ parts[dataIndexes[0]] = `data: ${JSON.stringify(payload)}`;
580
+ for (const index of dataIndexes.slice(1)) parts[index] = parts[index + 1] = '';
581
+ return parts.join('');
582
+ }
583
+
584
+ function sanitizeProtocolErrors(payload, apiKey, failedEvent = false) {
585
+ let changed = false;
586
+ const clean = value => {
587
+ if (typeof value === 'string' && value) {
588
+ const safe = sanitizeProviderMessage(value, [apiKey], MAX_ERROR_BYTES);
589
+ changed ||= safe !== value;
590
+ return safe;
460
591
  }
461
- } catch (_error) {}
592
+ if (value && typeof value === 'object') {
593
+ for (const key of Object.keys(value)) value[key] = clean(value[key]);
594
+ }
595
+ return value;
596
+ };
597
+ const visit = (value, inheritedFailure) => {
598
+ if (!value || typeof value !== 'object') return;
599
+ if (value.error != null) { changed = true; value.error = clean(value.error); }
600
+ const failed = inheritedFailure || ['error', 'response.failed', 'response.error'].includes(value.type)
601
+ || ['failed', 'error'].includes(value.status);
602
+ if (failed) {
603
+ // Re-encode failures even when the surviving values need no replacement:
604
+ // duplicate JSON keys must not retain an earlier raw diagnostic value.
605
+ changed = true;
606
+ for (const key of ['message', 'code', 'param', 'detail', 'details', 'reason', 'description']) {
607
+ if (value[key] != null) value[key] = clean(value[key]);
608
+ }
609
+ }
610
+ // Response envelopes carry diagnostics; model output and tool arguments
611
+ // retain their original protocol semantics, even if they resemble secrets.
612
+ visit(value.response, failed);
613
+ };
614
+ visit(payload, failedEvent);
615
+ return changed;
462
616
  }
463
617
 
464
- async function forwardOpaqueResponse(upstream, res, onActivity) {
465
- const contentType = upstream.headers.get('content-type') || 'application/json; charset=utf-8';
466
- res.writeHead(upstream.status, { 'content-type': contentType, 'cache-control': 'no-store' });
618
+ function hasDuplicateJsonKeys(text) {
619
+ // Called only after JSON.parse succeeds. Track keys per object and skip
620
+ // entire string values, including JSON-looking tool arguments. A duplicate
621
+ // can hide both an error value and an earlier failed status, so its protocol
622
+ // diagnostics require cleaning even when the surviving status is completed.
623
+ const stack = [];
624
+ for (let index = 0; index < text.length; index += 1) {
625
+ const char = text[index];
626
+ if (char === '{') stack.push({ keys: new Set(), expectsKey: true });
627
+ else if (char === '[') stack.push(null);
628
+ else if (char === '}' || char === ']') stack.pop();
629
+ else if (char === ',' && stack.at(-1)) stack.at(-1).expectsKey = true;
630
+ else if (char === '"') {
631
+ let end = index;
632
+ while (true) {
633
+ end = text.indexOf('"', end + 1);
634
+ let backslashes = 0;
635
+ for (let at = end - 1; text[at] === '\\'; at -= 1) backslashes += 1;
636
+ if (backslashes % 2 === 0) break;
637
+ }
638
+ const object = stack.at(-1);
639
+ if (object?.expectsKey) {
640
+ const key = JSON.parse(text.slice(index, end + 1));
641
+ if (object.keys.has(key)) return true;
642
+ object.keys.add(key);
643
+ object.expectsKey = false;
644
+ }
645
+ index = end;
646
+ }
647
+ }
648
+ return false;
649
+ }
650
+
651
+ async function forwardCompactResponse(upstream, res, onActivity, apiKey) {
467
652
  if (!upstream.body) {
468
- res.end();
469
- return;
653
+ throw providerError('provider_response_invalid', 'The provider returned an empty compact response.');
470
654
  }
471
655
  const reader = upstream.body.getReader();
472
- while (true) {
473
- const { done, value } = await reader.read();
474
- if (done) break;
475
- onActivity();
476
- if (!res.writableEnded) res.write(Buffer.from(value));
656
+ const chunks = [];
657
+ let bytes = 0;
658
+ let complete = false;
659
+ try {
660
+ while (true) {
661
+ const { done, value } = await reader.read();
662
+ if (done) break;
663
+ onActivity();
664
+ bytes += value.byteLength;
665
+ if (bytes > MAX_COMPACT_RESPONSE_BYTES) {
666
+ throw providerError('provider_response_too_large', 'The compact response exceeded the 32 MiB local bridge limit.');
667
+ }
668
+ chunks.push(Buffer.from(value));
669
+ }
670
+ complete = true;
671
+ } finally {
672
+ if (!complete) await reader.cancel().catch(() => {});
673
+ reader.releaseLock();
477
674
  }
478
- if (!res.writableEnded) res.end();
675
+ const body = Buffer.concat(chunks, bytes);
676
+ let text;
677
+ let payload;
678
+ try {
679
+ text = new TextDecoder('utf-8', { fatal: true }).decode(body);
680
+ payload = JSON.parse(text);
681
+ } catch (_error) {
682
+ throw providerError('provider_response_invalid', 'The provider returned an invalid compact JSON response.');
683
+ }
684
+ if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
685
+ throw providerError('provider_response_invalid', 'The provider returned an invalid compact JSON response.');
686
+ }
687
+ const changed = sanitizeProtocolErrors(payload, apiKey, hasDuplicateJsonKeys(text));
688
+ const contentType = upstream.headers.get('content-type') || 'application/json; charset=utf-8';
689
+ res.writeHead(upstream.status, { 'content-type': contentType, 'cache-control': 'no-store' });
690
+ if (!res.writableEnded) res.end(changed ? JSON.stringify(payload) : body);
479
691
  }
480
692
 
481
693
  function buildResponsesUrl(baseUrl, launch = {}, route = 'responses') {
@@ -521,8 +733,8 @@ function buildUpstreamHeaders(launch = {}) {
521
733
  return headers;
522
734
  }
523
735
 
524
- async function forwardUpstreamError(upstream, res, apiKey) {
525
- const text = (await upstream.text()).slice(0, MAX_ERROR_BYTES);
736
+ async function forwardUpstreamError(upstream, res, apiKey, onActivity) {
737
+ const text = (await readProviderText(upstream, onActivity)).slice(0, MAX_ERROR_BYTES);
526
738
  let message = `Provider returned HTTP ${upstream.status}.`;
527
739
  try {
528
740
  const parsed = JSON.parse(text);
@@ -555,12 +767,14 @@ function normalizeBridgeErrorCode(value) {
555
767
  return [
556
768
  'provider_connection_timeout',
557
769
  'provider_response_invalid',
770
+ 'provider_response_too_large',
558
771
  'provider_protocol_incompatible',
559
772
  'provider_request_invalid',
560
773
  'provider_request_too_large',
561
774
  'provider_responses_continuation_repeated',
562
775
  'provider_responses_continuation_unrecoverable',
563
- 'provider_continuation_history_limit'
776
+ 'provider_continuation_history_limit',
777
+ 'provider_upstream_error'
564
778
  ].includes(value) ? value : 'provider_bridge_failed';
565
779
  }
566
780