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.
- package/README.md +275 -305
- package/README.zh-CN.md +290 -320
- package/extension/bootstrap/background.js +99 -19
- package/extension/bootstrap/manifest.template.json +9 -4
- package/extension/bootstrap/popup.html +11 -16
- package/extension/bootstrap/popupFallback.js +55 -0
- package/extension/bootstrap/update.js +25 -2
- package/extension/bootstrap/updateStatus.js +1 -0
- package/extension/runtime-manifest.json +3 -1
- package/extension/src/background.js +234 -56
- package/extension/src/backgroundBootstrapMigration.js +181 -0
- package/extension/src/backgroundSharedSessions.js +440 -0
- package/extension/src/backgroundUpdateCoordinator.js +541 -381
- package/extension/src/backgroundUpdateSafety.js +173 -0
- package/extension/src/content/activeTurnControl.js +16 -2
- package/extension/src/content/applyResultFormatters.js +46 -73
- package/extension/src/content/assetTransferBroker.js +244 -12
- package/extension/src/content/changeHistoryView.js +175 -0
- package/extension/src/content/composerAttachments.js +117 -67
- package/extension/src/content/composerPanel.js +41 -8
- package/extension/src/content/contentRuntime.js +1084 -1099
- package/extension/src/content/diagnosticsController.js +122 -97
- package/extension/src/content/diagnosticsExport.js +236 -0
- package/extension/src/content/diagnosticsPanel.js +78 -19
- package/extension/src/content/diffReviewPanel.js +8 -5
- package/extension/src/content/directoryCleanupRecovery.js +54 -0
- package/extension/src/content/generated/content.bundle.js +14680 -3606
- package/extension/src/content/generated/content.bundle.meta.json +30 -3
- package/extension/src/content/globalPreferencesController.js +1 -1
- package/extension/src/content/legacyGlobalRegistry.js +25 -1
- package/extension/src/content/localSkillsPanel.js +141 -16
- package/extension/src/content/markdownDomRenderer.js +9 -4
- package/extension/src/content/markdownText.js +5 -6
- package/extension/src/content/modelPicker.js +8 -5
- package/extension/src/content/nativeCompatibilityController.js +45 -20
- package/extension/src/content/otWarmMirror.js +4 -5
- package/extension/src/content/otWarmMirrorController.js +17 -20
- package/extension/src/content/pageBridgeClient.js +33 -5
- package/extension/src/content/panelMaintenance.js +87 -132
- package/extension/src/content/panelRenderer.js +10 -0
- package/extension/src/content/pendingInputView.js +109 -76
- package/extension/src/content/projectProviderSelection.js +13 -4
- package/extension/src/content/projectSessionCleanup.js +109 -62
- package/extension/src/content/projectSettingsCoordinator.js +39 -2
- package/extension/src/content/providerSettingsCoordinator.js +63 -14
- package/extension/src/content/providerSettingsDialog.js +724 -113
- package/extension/src/content/recentProjects.js +21 -6
- package/extension/src/content/runActivitySummary.js +665 -0
- package/extension/src/content/runCompletionMeta.js +344 -0
- package/extension/src/content/runController.js +40 -15
- package/extension/src/content/runFailureNotice.js +34 -12
- package/extension/src/content/runGuidanceController.js +76 -30
- package/extension/src/content/runGuidanceView.js +59 -10
- package/extension/src/content/runPresence.js +306 -0
- package/extension/src/content/runQueueScheduler.js +150 -43
- package/extension/src/content/runRecoveryActions.js +146 -0
- package/extension/src/content/runResultActions.js +11 -8
- package/extension/src/content/runReviewController.js +574 -0
- package/extension/src/content/runScrollLayout.js +21 -2
- package/extension/src/content/runTimelineView.js +315 -290
- package/extension/src/content/scopedPersistencePanelState.js +51 -6
- package/extension/src/content/selectionContextView.js +300 -0
- package/extension/src/content/sensitivePreflight.js +140 -0
- package/extension/src/content/sessionForkController.js +80 -17
- package/extension/src/content/sessionManager.js +1 -1
- package/extension/src/content/sessionPersistence.js +6 -1
- package/extension/src/content/settingsPanel.js +84 -55
- package/extension/src/content/settingsWorkbench.js +379 -0
- package/extension/src/content/sharedSessionViewSync.js +109 -0
- package/extension/src/content/subagentActivityView.js +195 -0
- package/extension/src/content/trackedChangeCaptureController.js +7 -3
- package/extension/src/content/updateIdle.js +4 -1
- package/extension/src/content/updateNotice.js +179 -56
- package/extension/src/content/updateSafetyReceiver.js +58 -0
- package/extension/src/content/writebackController.js +308 -2
- package/extension/src/content/writebackOrchestrator.js +213 -199
- package/extension/src/content/writebackPlan.js +231 -0
- package/extension/src/content/writebackRecovery.js +122 -0
- package/extension/src/content/writingStyleSettings.js +600 -0
- package/extension/src/content/writtenChangesView.js +152 -0
- package/extension/src/page/binaryAssetUploader.js +223 -23
- package/extension/src/page/fileMover.js +256 -0
- package/extension/src/page/overleafProjectSnapshot.js +1 -0
- package/extension/src/page/ownedDirectoryCleanup.js +94 -0
- package/extension/src/page/referenceProjects.js +149 -0
- package/extension/src/page/reviewContext.js +113 -0
- package/extension/src/page/saveState.js +20 -6
- package/extension/src/page/snapshotRouter.js +124 -48
- package/extension/src/page/textCoordinates.js +102 -0
- package/extension/src/page/textFileCreator.js +442 -71
- package/extension/src/page/trackedChangeCapture.js +53 -57
- package/extension/src/page/trackedChangeOwnership.js +216 -0
- package/extension/src/page/trackedChangeReplay.js +184 -0
- package/extension/src/page/trackedChangesLifecycle.js +343 -339
- package/extension/src/page/treeOperations.js +33 -12
- package/extension/src/page/updateSafetyProbe.js +41 -0
- package/extension/src/page/writebackReceiptJournal.js +114 -0
- package/extension/src/page/writebackRouter.js +242 -201
- package/extension/src/pageBridge.js +73 -102
- package/extension/src/popup.js +261 -65
- package/extension/src/shared/agentTranscript.js +41 -4
- package/extension/src/shared/auditRecords.js +73 -0
- package/extension/src/shared/changeHistoryModel.js +94 -0
- package/extension/src/shared/compatibility.js +22 -6
- package/extension/src/shared/compileAdapter.js +101 -37
- package/extension/src/shared/composerAttachmentDraft.js +151 -0
- package/extension/src/shared/directoryCleanupProof.js +43 -0
- package/extension/src/shared/failureReasons.js +329 -3
- package/extension/src/shared/globalPreferences.js +31 -14
- package/extension/src/shared/governanceRules.js +4 -37
- package/extension/src/shared/i18n.js +389 -25
- package/extension/src/shared/managedUpdateProjection.js +8 -5
- package/extension/src/shared/nativeRequestIdentity.js +1 -0
- package/extension/src/shared/pageRpcContract.js +9 -2
- package/extension/src/shared/projectFiles.js +21 -44
- package/extension/src/shared/providerProfiles.js +114 -3
- package/extension/src/shared/runActivityModel.js +361 -0
- package/extension/src/shared/runExecutionSnapshot.js +8 -2
- package/extension/src/shared/runExecutionSnapshotCodec.js +16 -4
- package/extension/src/shared/runInputQueue.js +48 -5
- package/extension/src/shared/scopedPersistenceBrowserAdapter.js +6 -3
- package/extension/src/shared/selectionContext.js +37 -0
- package/extension/src/shared/sessionState.js +166 -30
- package/extension/src/shared/settlementFacts.js +10 -2
- package/extension/src/shared/sharedSessionBridge.js +311 -0
- package/extension/src/shared/sharedSessionTransfer.js +140 -0
- package/extension/src/shared/staleGuard.js +118 -13
- package/extension/src/shared/storageDb.js +77 -78
- package/extension/src/shared/storageMigration.js +74 -111
- package/extension/src/shared/storageRunActions.js +211 -22
- package/extension/src/shared/storageValuePruning.js +40 -0
- package/extension/src/shared/subagentActivityModel.js +174 -0
- package/extension/src/shared/undoOperations.js +216 -6
- package/extension/src/shared/updateConsent.js +42 -11
- package/extension/src/shared/updateRuntimeIdentity.js +1 -1
- package/extension/src/shared/writebackIntent.js +78 -0
- package/extension/src/shared/writebackOperationEvidence.js +4 -15
- package/extension/src/shared/writebackSettlement.js +67 -48
- package/extension/src/shared/writingStyle.js +33 -0
- package/extension/styles/panel.css +1987 -385
- package/extension/styles/popup.css +110 -0
- package/native-host/src/anthropicBridgeRequest.js +6 -3
- package/native-host/src/anthropicBridgeResponse.js +6 -2
- package/native-host/src/anthropicMessagesBridge.js +24 -16
- package/native-host/src/bootstrapMigration.js +296 -0
- package/native-host/src/bootstrapMigrationContract.js +101 -0
- package/native-host/src/bootstrapUpdate.js +233 -0
- package/native-host/src/bootstrapUpdateWatchdog.js +44 -0
- package/native-host/src/chatBridgeResponse.js +10 -0
- package/native-host/src/chatCompletionsBridge.js +21 -15
- package/native-host/src/codexConfigPaths.js +106 -0
- package/native-host/src/codexHome.js +66 -73
- package/native-host/src/codexHomeScope.js +124 -0
- package/native-host/src/codexInheritedAuth.js +90 -0
- package/native-host/src/codexLocalSkills.js +301 -0
- package/native-host/src/codexModelRecovery.js +107 -0
- package/native-host/src/codexPromptAssembly.js +33 -12
- package/native-host/src/codexProviderLaunch.js +3 -3
- package/native-host/src/codexProviderTest.js +24 -26
- package/native-host/src/codexRuntimeIdentity.js +1 -0
- package/native-host/src/codexSessionRunner.js +211 -318
- package/native-host/src/codexSessionTiming.js +21 -8
- package/native-host/src/codexSkillIsolation.js +170 -0
- package/native-host/src/codexThreadFork.js +19 -3
- package/native-host/src/index.js +19 -7
- package/native-host/src/managedInstall.js +115 -39
- package/native-host/src/managedInstallRecovery.js +140 -0
- package/native-host/src/managedRuntimeEvidence.js +57 -0
- package/native-host/src/mirrorWorkspace.js +92 -22
- package/native-host/src/nativeDoctor.js +15 -0
- package/native-host/src/nativeEnvironment.js +70 -0
- package/native-host/src/nativeHelperLifecycle.js +21 -0
- package/native-host/src/nativeHostRegistration.js +33 -0
- package/native-host/src/nativeQuotas.js +15 -8
- package/native-host/src/nativeRunLifecycle.js +71 -0
- package/native-host/src/overleafWorkspaceRules.js +16 -0
- package/native-host/src/processStopBarrier.js +115 -0
- package/native-host/src/projectPathPlatform.js +30 -0
- package/native-host/src/providerHttp.js +80 -0
- package/native-host/src/providerModelCatalog.js +4 -3
- package/native-host/src/providerProfile.js +17 -3
- package/native-host/src/providerReasoning.js +10 -11
- package/native-host/src/providerRuntime.js +1 -0
- package/native-host/src/providerStore.js +76 -14
- package/native-host/src/releaseTransport.js +14 -5
- package/native-host/src/responsesPassthroughBridge.js +266 -52
- package/native-host/src/runtimeInstaller.js +21 -1
- package/native-host/src/selectionScope.js +86 -0
- package/native-host/src/skills/parallel-subagents/SKILL.md +31 -3
- package/native-host/src/skills/writing-style-builder/SKILL.md +146 -0
- package/native-host/src/subagentAdmission.js +26 -0
- package/native-host/src/subagentBroker.js +176 -159
- package/native-host/src/subagentTelemetry.js +325 -0
- package/native-host/src/subagentWorkspace.js +103 -0
- package/native-host/src/subagentWorkspacePath.js +34 -0
- package/native-host/src/taskRunnerRuntime.js +130 -57
- package/native-host/src/tomlStatements.js +140 -0
- package/native-host/src/turnAttachments.js +113 -0
- package/native-host/src/updateManager.js +406 -38
- package/native-host/src/updateNetworkControl.js +75 -0
- package/native-host/src/updateTrust.js +21 -2
- package/native-host/src/vendor/pdfjs/LICENSE +177 -0
- package/native-host/src/vendor/pdfjs/SOURCE.txt +3 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/78ms-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/83pv-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90ms-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90msp-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/90pv-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Add-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Add-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Add-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-0.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-1.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-3.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-4.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-5.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-6.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-0.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-1.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-3.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-4.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-5.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-0.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-1.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-3.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-4.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-5.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-6.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-0.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-1.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/B5pc-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/B5pc-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS1-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS1-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/CNS2-V.bcmap +3 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETHK-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETen-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-H.bcmap +3 -0
- package/native-host/src/vendor/pdfjs/cmaps/ETenms-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Ext-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Ext-RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Ext-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GB-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GB-H.bcmap +4 -0
- package/native-host/src/vendor/pdfjs/cmaps/GB-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBK-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBK2K-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBK2K-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBKp-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBT-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBT-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBT-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBTpc-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/GBpc-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKdla-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKdlb-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKgccs-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKm314-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKm471-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/HKscs-B5-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Hankaku.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Hiragana.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-Johab-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCms-UHC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/KSCpc-EUC-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Katakana.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/LICENSE +36 -0
- package/native-host/src/vendor/pdfjs/cmaps/NWP-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/NWP-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/RKSJ-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/RKSJ-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/Roman.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniCNS-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniGB-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UCS2-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF16-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF32-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-H.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/UniKS-UTF8-V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/V.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/cmaps/WP-Symbol.bcmap +0 -0
- package/native-host/src/vendor/pdfjs/pdf.mjs +26740 -0
- package/native-host/src/vendor/pdfjs/pdf.worker.mjs +62201 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitDingbats.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixed.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBold.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitFixedItalic.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerif.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBold.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSerifItalic.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/FoxitSymbol.pfb +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_FOXIT +27 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LICENSE_LIBERATION +102 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Bold.ttf +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Italic.ttf +0 -0
- package/native-host/src/vendor/pdfjs/standard_fonts/LiberationSans-Regular.ttf +0 -0
- package/native-host/src/writingStyleEvidence.js +102 -0
- package/native-host/src/writingStylePdfWorker.js +35 -0
- package/native-host/src/writingStyleProfile.js +165 -0
- package/native-host/src/writingStyleRuntime.js +475 -0
- package/native-host/src/writingStyleStore.js +73 -0
- package/package.json +1 -1
- 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
|
|
373
|
-
const
|
|
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
|
|
378
|
-
previousSecrets
|
|
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,
|
|
388
|
-
atomicWriteJson(previous.paths.publicFile,
|
|
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
|
-
|
|
412
|
-
|
|
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
|
|
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,
|
|
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 >
|
|
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
|
|
99
|
-
|
|
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(
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
421
|
-
const
|
|
422
|
-
if (
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
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
|
|
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
|
-
|
|
544
|
+
forward(observer.buffer);
|
|
445
545
|
observer.buffer = '';
|
|
446
546
|
}
|
|
447
547
|
}
|
|
448
548
|
|
|
449
|
-
function
|
|
450
|
-
|
|
451
|
-
.
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
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
|
-
|
|
458
|
-
|
|
459
|
-
|
|
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
|
-
|
|
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
|
-
|
|
465
|
-
|
|
466
|
-
|
|
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
|
-
|
|
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
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|