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
|
@@ -19,6 +19,10 @@ const fs = require('node:fs');
|
|
|
19
19
|
const path = require('node:path');
|
|
20
20
|
|
|
21
21
|
const { SUBAGENT_QUEUE_DIR } = require('./mirrorWorkspace');
|
|
22
|
+
const { safeWorkspaceRelativePath } = require('./subagentWorkspacePath');
|
|
23
|
+
const { estimateWorkerWaves } = require('./subagentAdmission');
|
|
24
|
+
const { compactEvent, publicText } = require('./subagentTelemetry');
|
|
25
|
+
const { createSubagentWorkspace } = require('./subagentWorkspace');
|
|
22
26
|
|
|
23
27
|
const PROTOCOL_VERSION = 1;
|
|
24
28
|
const JOB_ID_PATTERN = /^[a-z0-9-]{1,32}$/;
|
|
@@ -28,8 +32,8 @@ const SUMMARY_LIMIT_CHARS = 2048;
|
|
|
28
32
|
const DEFAULT_LIMITS = Object.freeze({
|
|
29
33
|
maxWorkers: 3,
|
|
30
34
|
maxJobsPerRun: 8,
|
|
31
|
-
perWorkerTimeoutMs:
|
|
32
|
-
brokerBudgetMs:
|
|
35
|
+
perWorkerTimeoutMs: 600000,
|
|
36
|
+
brokerBudgetMs: 1800000,
|
|
33
37
|
pollIntervalMs: 500,
|
|
34
38
|
drainGraceMs: 30000
|
|
35
39
|
});
|
|
@@ -43,15 +47,31 @@ function createSubagentBroker(options = {}) {
|
|
|
43
47
|
if (typeof runWorkerTask !== 'function') {
|
|
44
48
|
throw new Error('subagent broker requires a runWorkerTask function');
|
|
45
49
|
}
|
|
46
|
-
const emit =
|
|
50
|
+
const emit = (...args) => {
|
|
51
|
+
try { if (typeof options.emit === 'function') options.emit(...args); }
|
|
52
|
+
catch (_error) { /* Observability cannot prevent worker cleanup. */ }
|
|
53
|
+
};
|
|
47
54
|
const onMirrorDirty = typeof options.onMirrorDirty === 'function' ? options.onMirrorDirty : () => {};
|
|
48
55
|
const parentSignal = options.signal || null;
|
|
49
|
-
const
|
|
56
|
+
const env = options.env || process.env;
|
|
57
|
+
const boundedMs = (value, fallback, max) => Number.isFinite(Number(value)) && Number(value) > 0
|
|
58
|
+
? Math.min(max, Math.max(60000, Number(value))) : fallback;
|
|
59
|
+
const limits = { ...DEFAULT_LIMITS,
|
|
60
|
+
perWorkerTimeoutMs: boundedMs(env.CODEX_OVERLEAF_SUBAGENT_TIMEOUT_MS, DEFAULT_LIMITS.perWorkerTimeoutMs, 900000),
|
|
61
|
+
brokerBudgetMs: boundedMs(env.CODEX_OVERLEAF_SUBAGENT_BUDGET_MS, DEFAULT_LIMITS.brokerBudgetMs, 3600000),
|
|
62
|
+
...(options.limits || {}) };
|
|
50
63
|
|
|
51
64
|
const queueRoot = path.join(workspacePath, SUBAGENT_QUEUE_DIR);
|
|
52
65
|
const jobsDir = path.join(queueRoot, 'jobs');
|
|
53
66
|
const resultsDir = path.join(queueRoot, 'results');
|
|
54
67
|
const logsDir = path.join(queueRoot, 'logs');
|
|
68
|
+
const { atomicWrite, appendLog, adoptionHash, hashWorkspace, hashPaths, buildWorkerPrompt } =
|
|
69
|
+
createSubagentWorkspace({ workspacePath, logsDir });
|
|
70
|
+
const adoptionsDir = path.join(queueRoot, 'adoptions');
|
|
71
|
+
const adoptionResultsDir = path.join(queueRoot, 'adoption-results');
|
|
72
|
+
const quarantined = new Map();
|
|
73
|
+
const seenAdoptions = new Set();
|
|
74
|
+
let adoptionToken = '';
|
|
55
75
|
|
|
56
76
|
const jobs = new Map(); // id -> { job, status, startedAt, ownedBefore }
|
|
57
77
|
const seenFiles = new Set();
|
|
@@ -68,6 +88,8 @@ function createSubagentBroker(options = {}) {
|
|
|
68
88
|
let anyWorkerStarted = false;
|
|
69
89
|
let cancelled = false;
|
|
70
90
|
let closed = false;
|
|
91
|
+
let brokerError = null;
|
|
92
|
+
let stopping = null;
|
|
71
93
|
|
|
72
94
|
function start() {
|
|
73
95
|
// Stale queue dirs from a previous run carry old results/logs — clear
|
|
@@ -76,6 +98,8 @@ function createSubagentBroker(options = {}) {
|
|
|
76
98
|
fs.mkdirSync(jobsDir, { recursive: true });
|
|
77
99
|
fs.mkdirSync(resultsDir, { recursive: true });
|
|
78
100
|
fs.mkdirSync(logsDir, { recursive: true });
|
|
101
|
+
fs.mkdirSync(adoptionsDir, { recursive: true });
|
|
102
|
+
fs.mkdirSync(adoptionResultsDir, { recursive: true });
|
|
79
103
|
fs.mkdirSync(path.join(queueRoot, 'work'), { recursive: true });
|
|
80
104
|
startedAt = Date.now();
|
|
81
105
|
accepting = true;
|
|
@@ -94,16 +118,14 @@ function createSubagentBroker(options = {}) {
|
|
|
94
118
|
maxJobsPerRun: limits.maxJobsPerRun,
|
|
95
119
|
perWorkerTimeoutMs: limits.perWorkerTimeoutMs,
|
|
96
120
|
brokerBudgetMs: limits.brokerBudgetMs,
|
|
121
|
+
activeWorkers: running.size,
|
|
122
|
+
adoptionToken,
|
|
123
|
+
adoptionDirectory: 'adoptions',
|
|
97
124
|
acceptedUntil: new Date(startedAt + limits.brokerBudgetMs - limits.perWorkerTimeoutMs).toISOString()
|
|
98
125
|
};
|
|
99
126
|
atomicWrite(path.join(queueRoot, 'broker.json'), JSON.stringify(payload, null, 2));
|
|
100
127
|
}
|
|
101
128
|
|
|
102
|
-
function atomicWrite(target, text) {
|
|
103
|
-
const tmp = path.join(path.dirname(target), `.tmp-${crypto.randomUUID()}`);
|
|
104
|
-
fs.writeFileSync(tmp, text);
|
|
105
|
-
fs.renameSync(tmp, target);
|
|
106
|
-
}
|
|
107
129
|
|
|
108
130
|
function pollOnce() {
|
|
109
131
|
if (closed || cancelled) {
|
|
@@ -112,11 +134,23 @@ function createSubagentBroker(options = {}) {
|
|
|
112
134
|
try {
|
|
113
135
|
intakeJobs();
|
|
114
136
|
fillSlots();
|
|
115
|
-
|
|
116
|
-
|
|
137
|
+
processAdoptions();
|
|
138
|
+
} catch (error) {
|
|
139
|
+
recordFailure(error);
|
|
117
140
|
}
|
|
118
141
|
}
|
|
119
142
|
|
|
143
|
+
function recordFailure(error, jobId) {
|
|
144
|
+
accepting = false;
|
|
145
|
+
if (brokerError) return;
|
|
146
|
+
brokerError = Object.assign(new Error('Subagent broker results or cleanup could not be persisted.'), {
|
|
147
|
+
code: 'subagent_io_failed', cause: error
|
|
148
|
+
});
|
|
149
|
+
emit('codex.subagent.failed', 'Subagent result storage failed', {
|
|
150
|
+
jobId, status: 'failed', reason: brokerError.message, code: brokerError.code
|
|
151
|
+
}, 'warning');
|
|
152
|
+
}
|
|
153
|
+
|
|
120
154
|
function intakeJobs() {
|
|
121
155
|
if (!accepting || !fs.existsSync(jobsDir)) {
|
|
122
156
|
return;
|
|
@@ -154,15 +188,18 @@ function createSubagentBroker(options = {}) {
|
|
|
154
188
|
acceptedCount += 1;
|
|
155
189
|
jobs.set(job.id, { job, status: 'queued' });
|
|
156
190
|
queued.push(job.id);
|
|
157
|
-
emit('codex.subagent.queued', job.title || job.id,
|
|
191
|
+
emit('codex.subagent.queued', publicText(job.title || job.id, 160), {
|
|
192
|
+
jobId: job.id, files: job.files, task: publicText(job.task, 4096)
|
|
193
|
+
}, 'running');
|
|
158
194
|
}
|
|
159
195
|
|
|
160
196
|
function idFromFileName(name) {
|
|
161
|
-
|
|
197
|
+
const id = name.replace(/\.json$/, '');
|
|
198
|
+
return JOB_ID_PATTERN.test(id) ? id : `rejected-${crypto.randomBytes(11).toString('hex')}`;
|
|
162
199
|
}
|
|
163
200
|
|
|
164
201
|
function jobIdOrFallback(job, name) {
|
|
165
|
-
return typeof job?.id === 'string' && job.id ? job.id
|
|
202
|
+
return typeof job?.id === 'string' && JOB_ID_PATTERN.test(job.id) ? job.id : idFromFileName(name);
|
|
166
203
|
}
|
|
167
204
|
|
|
168
205
|
function validateJob(job) {
|
|
@@ -195,13 +232,11 @@ function createSubagentBroker(options = {}) {
|
|
|
195
232
|
}
|
|
196
233
|
owned.push(safe);
|
|
197
234
|
}
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
// so the broker enforces its own wall-clock envelope across ALL waves.
|
|
204
|
-
const projectedWaves = Math.ceil((queued.length + running.size + 1) / limits.maxWorkers);
|
|
235
|
+
// Reserve serial waves for overlapping ownership, matching fillSlots.
|
|
236
|
+
// The budget covers worker admission; parent/OS cleanup has separate limits.
|
|
237
|
+
const projectedWaves = estimateWorkerWaves(
|
|
238
|
+
[...running.keys()].map(id => jobs.get(id).job.files),
|
|
239
|
+
[...queued.map(id => jobs.get(id).job.files), owned], limits.maxWorkers);
|
|
205
240
|
if (Date.now() + projectedWaves * limits.perWorkerTimeoutMs > startedAt + limits.brokerBudgetMs) {
|
|
206
241
|
return { reason: 'insufficient_time' };
|
|
207
242
|
}
|
|
@@ -212,30 +247,6 @@ function createSubagentBroker(options = {}) {
|
|
|
212
247
|
return null;
|
|
213
248
|
}
|
|
214
249
|
|
|
215
|
-
function safeWorkspaceRelativePath(value) {
|
|
216
|
-
if (typeof value !== 'string' || !value.trim()) {
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
const normalized = value.trim().replace(/\\/g, '/').replace(/^\.\//, '');
|
|
220
|
-
if (!normalized || path.isAbsolute(normalized)) {
|
|
221
|
-
return null;
|
|
222
|
-
}
|
|
223
|
-
const segments = normalized.split('/');
|
|
224
|
-
if (segments.some(segment => segment === '..' || segment === '')) {
|
|
225
|
-
return null;
|
|
226
|
-
}
|
|
227
|
-
if (segments[0] === SUBAGENT_QUEUE_DIR) {
|
|
228
|
-
// The queue control plane (jobs/results/logs/broker.json) is never
|
|
229
|
-
// ownable — but the work/ scratch zone IS: single-file fan-out slices
|
|
230
|
-
// live there (v1.6.1 scatter-gather), excluded from writeback by the
|
|
231
|
-
// mirror-scan rule yet fully owned/hashed like any other job file.
|
|
232
|
-
if (segments[1] === 'work' && segments.length >= 3) {
|
|
233
|
-
return segments.join('/');
|
|
234
|
-
}
|
|
235
|
-
return null;
|
|
236
|
-
}
|
|
237
|
-
return segments.join('/');
|
|
238
|
-
}
|
|
239
250
|
|
|
240
251
|
function writeRejection(input) {
|
|
241
252
|
const result = {
|
|
@@ -250,6 +261,10 @@ function createSubagentBroker(options = {}) {
|
|
|
250
261
|
}
|
|
251
262
|
|
|
252
263
|
function writeResult(result) {
|
|
264
|
+
// Rejection receipts cross the same filesystem boundary as accepted jobs.
|
|
265
|
+
if (typeof result.id !== 'string' || !JOB_ID_PATTERN.test(result.id)) {
|
|
266
|
+
throw new Error('Invalid subagent result ID');
|
|
267
|
+
}
|
|
253
268
|
atomicWrite(path.join(resultsDir, `${result.id}.json`), JSON.stringify(result, null, 2));
|
|
254
269
|
}
|
|
255
270
|
|
|
@@ -270,6 +285,11 @@ function createSubagentBroker(options = {}) {
|
|
|
270
285
|
}
|
|
271
286
|
const [jobId] = queued.splice(index, 1);
|
|
272
287
|
const entry = jobs.get(jobId);
|
|
288
|
+
if (Date.now() + limits.perWorkerTimeoutMs > startedAt + limits.brokerBudgetMs) {
|
|
289
|
+
entry.status = 'rejected';
|
|
290
|
+
writeRejection({ id: jobId, reason: 'insufficient_time' });
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
273
293
|
try {
|
|
274
294
|
startWorker(entry);
|
|
275
295
|
} catch (error) {
|
|
@@ -283,6 +303,9 @@ function createSubagentBroker(options = {}) {
|
|
|
283
303
|
try {
|
|
284
304
|
writeResult({ id: jobId, status: 'failed', reason: error?.message || 'subagent failed to start' });
|
|
285
305
|
} catch (_writeError) { /* result dir may be gone */ }
|
|
306
|
+
emit('codex.subagent.failed', publicText(entry?.job?.title || jobId, 160), {
|
|
307
|
+
jobId, status: 'failed', reason: publicText(error?.message || 'subagent failed to start', 1200)
|
|
308
|
+
}, 'warning');
|
|
286
309
|
}
|
|
287
310
|
}
|
|
288
311
|
}
|
|
@@ -313,8 +336,9 @@ function createSubagentBroker(options = {}) {
|
|
|
313
336
|
controller.abort(new Error('subagent timeout'));
|
|
314
337
|
}, limits.perWorkerTimeoutMs);
|
|
315
338
|
|
|
316
|
-
emit('codex.subagent.started', job.title || job.id, {
|
|
339
|
+
emit('codex.subagent.started', publicText(job.title || job.id, 160), {
|
|
317
340
|
jobId: job.id,
|
|
341
|
+
task: publicText(job.task, 4096),
|
|
318
342
|
files: job.files,
|
|
319
343
|
activeWorkers: running.size + 1,
|
|
320
344
|
maxWorkers: limits.maxWorkers
|
|
@@ -323,8 +347,23 @@ function createSubagentBroker(options = {}) {
|
|
|
323
347
|
const promise = runWorkerTask({
|
|
324
348
|
jobId: job.id,
|
|
325
349
|
prompt: buildWorkerPrompt(job),
|
|
326
|
-
signal: controller.signal
|
|
350
|
+
signal: controller.signal,
|
|
351
|
+
onEvent: event => {
|
|
352
|
+
if (closed || entry.status !== 'running') return;
|
|
353
|
+
try {
|
|
354
|
+
const bounded = compactEvent(event);
|
|
355
|
+
if (!bounded) return;
|
|
356
|
+
const params = bounded.detail?.params || {};
|
|
357
|
+
entry.threadId = bounded.detail?.threadId || params.threadId || params.thread?.id || entry.threadId || '';
|
|
358
|
+
entry.sequence = (entry.sequence || 0) + 1;
|
|
359
|
+
emit('codex.subagent.event', publicText(job.title || job.id, 160), {
|
|
360
|
+
source: 'broker', jobId: job.id, threadId: entry.threadId,
|
|
361
|
+
sequence: entry.sequence, event: bounded
|
|
362
|
+
}, 'running');
|
|
363
|
+
} catch (_error) { /* Observability must not fail or stop the worker. */ }
|
|
364
|
+
}
|
|
327
365
|
}).then(workerResult => {
|
|
366
|
+
if (controller.signal.aborted) throw controller.signal.reason || new Error('Subagent aborted');
|
|
328
367
|
finishWorker(entry, {
|
|
329
368
|
status: 'completed',
|
|
330
369
|
summary: String(workerResult?.assistantMessage || '').slice(0, SUMMARY_LIMIT_CHARS),
|
|
@@ -332,21 +371,22 @@ function createSubagentBroker(options = {}) {
|
|
|
332
371
|
}, workerResult);
|
|
333
372
|
}).catch(error => {
|
|
334
373
|
// Any broker-initiated abort that is not the wall-clock deadline
|
|
335
|
-
// (parent cancel, drain)
|
|
336
|
-
|
|
337
|
-
const status = timedOut ? 'timeout' : controller.signal.aborted ? 'cancelled' : 'failed';
|
|
374
|
+
// (parent cancel, drain), or an interrupted worker turn, is cancelled.
|
|
375
|
+
const status = timedOut ? 'timeout' : controller.signal.aborted || error?.code === 'codex_cancelled' ? 'cancelled' : 'failed';
|
|
338
376
|
appendLog(job.id, `${status}: ${error?.stack || error?.message || String(error)}`);
|
|
339
377
|
finishWorker(entry, { status, reason: error?.message || String(error) });
|
|
340
|
-
}).finally(() => {
|
|
378
|
+
}).catch(error => recordFailure(error, job.id)).finally(() => {
|
|
341
379
|
clearTimeout(deadlineTimer);
|
|
342
380
|
running.delete(job.id);
|
|
343
381
|
if (running.size === 0) {
|
|
344
382
|
endWave();
|
|
345
383
|
}
|
|
384
|
+
writeBrokerFile(closed ? 'closed' : 'ready');
|
|
346
385
|
fillSlots();
|
|
347
|
-
});
|
|
386
|
+
}).catch(error => recordFailure(error, job.id));
|
|
348
387
|
|
|
349
388
|
running.set(job.id, { controller, deadlineTimer, promise });
|
|
389
|
+
try { writeBrokerFile('ready'); } catch (error) { recordFailure(error, job.id); }
|
|
350
390
|
}
|
|
351
391
|
|
|
352
392
|
function finishWorker(entry, resultFields, workerResult) {
|
|
@@ -362,12 +402,13 @@ function createSubagentBroker(options = {}) {
|
|
|
362
402
|
// demotion reads getViolationPaths() and drops these from writeback
|
|
363
403
|
// (spec S8 safety extension, v1.6.2).
|
|
364
404
|
for (const file of changedFiles) {
|
|
365
|
-
|
|
405
|
+
quarantined.set(file, { jobId: job.id, status: resultFields.status, adoptedHash: '' });
|
|
366
406
|
}
|
|
367
407
|
}
|
|
368
408
|
const result = {
|
|
369
409
|
id: job.id,
|
|
370
410
|
...resultFields,
|
|
411
|
+
threadId: workerResult?.threadId || entry.threadId || '',
|
|
371
412
|
changedFiles,
|
|
372
413
|
durationMs: Date.now() - entry.startedAt
|
|
373
414
|
};
|
|
@@ -377,24 +418,23 @@ function createSubagentBroker(options = {}) {
|
|
|
377
418
|
atomicWrite(path.join(resultsDir, `${job.id}.last-message.md`), String(workerResult.assistantMessage));
|
|
378
419
|
}
|
|
379
420
|
const eventType = resultFields.status === 'completed' ? 'codex.subagent.completed' : 'codex.subagent.failed';
|
|
380
|
-
emit(eventType, job.title || job.id, {
|
|
421
|
+
emit(eventType, publicText(job.title || job.id, 160), {
|
|
381
422
|
jobId: job.id,
|
|
423
|
+
threadId: result.threadId,
|
|
382
424
|
status: resultFields.status,
|
|
383
|
-
reason: resultFields.reason,
|
|
425
|
+
reason: publicText(resultFields.reason, 1200),
|
|
426
|
+
summary: publicText(workerResult?.assistantMessage || resultFields.summary, 24000),
|
|
384
427
|
changedFiles,
|
|
385
428
|
durationMs: result.durationMs
|
|
386
429
|
}, resultFields.status === 'completed' ? 'completed' : 'warning');
|
|
387
430
|
}
|
|
388
431
|
|
|
389
|
-
function appendLog(jobId, text) {
|
|
390
|
-
try {
|
|
391
|
-
fs.appendFileSync(path.join(logsDir, `${jobId}.log`), `${new Date().toISOString()} ${text}\n`);
|
|
392
|
-
} catch (_error) { /* logs are best-effort */ }
|
|
393
|
-
}
|
|
394
432
|
|
|
395
433
|
// ---- wave tracking (spec S5): owned attribution is per worker; unowned
|
|
396
434
|
// changes can only be attributed to the wave's set of suspects.
|
|
397
435
|
function beginWave() {
|
|
436
|
+
adoptionToken = '';
|
|
437
|
+
writeBrokerFile('ready');
|
|
398
438
|
waveIndex += 1;
|
|
399
439
|
waveSnapshot = {
|
|
400
440
|
hashes: hashWorkspace(),
|
|
@@ -434,68 +474,48 @@ function createSubagentBroker(options = {}) {
|
|
|
434
474
|
emit('codex.subagent.violation', file, violation, 'warning');
|
|
435
475
|
}
|
|
436
476
|
waveSnapshot = null;
|
|
477
|
+
if (!closed && !cancelled && accepting && running.size === 0) {
|
|
478
|
+
// Workers have exited before this capability is published. A worker
|
|
479
|
+
// cannot pre-authorize its own incomplete output while still running.
|
|
480
|
+
adoptionToken = crypto.randomBytes(24).toString('hex');
|
|
481
|
+
}
|
|
437
482
|
}
|
|
438
483
|
|
|
439
|
-
function hashWorkspace() {
|
|
440
|
-
const hashes = new Map();
|
|
441
|
-
walk(workspacePath, '');
|
|
442
|
-
return hashes;
|
|
443
484
|
|
|
444
|
-
|
|
445
|
-
|
|
485
|
+
function processAdoptions() {
|
|
486
|
+
if (closed || cancelled || running.size || !adoptionToken || !fs.existsSync(adoptionsDir)) return;
|
|
487
|
+
for (const name of fs.readdirSync(adoptionsDir).filter(name => /^[a-z0-9-]{1,32}\.json$/.test(name)).slice(0, 64)) {
|
|
488
|
+
if (seenAdoptions.has(name)) continue;
|
|
489
|
+
seenAdoptions.add(name);
|
|
490
|
+
let result = { ok: false, reason: 'invalid_adoption' };
|
|
446
491
|
try {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
const absolute = path.join(dir, entry.name);
|
|
461
|
-
if (entry.isDirectory()) {
|
|
462
|
-
walk(absolute, relative);
|
|
463
|
-
} else if (entry.isFile()) {
|
|
464
|
-
hashes.set(relative, hashFile(absolute));
|
|
492
|
+
const requestPath = path.join(adoptionsDir, name), stat = fs.lstatSync(requestPath);
|
|
493
|
+
if (!stat.isFile() || stat.size > MAX_JOB_FILE_BYTES) throw new Error('invalid_adoption_file');
|
|
494
|
+
const request = JSON.parse(fs.readFileSync(requestPath, 'utf8'));
|
|
495
|
+
if (request.token !== adoptionToken || request.reviewed !== true || !Array.isArray(request.files)
|
|
496
|
+
|| !request.files.length || request.files.length > 100) throw new Error('adoption_not_authorized');
|
|
497
|
+
const approved = [];
|
|
498
|
+
for (const item of request.files) {
|
|
499
|
+
const safe = safeWorkspaceRelativePath(item?.path), held = quarantined.get(safe);
|
|
500
|
+
const entry = held && jobs.get(held.jobId);
|
|
501
|
+
if (!safe || !held || held.jobId !== item.jobId || !entry?.job?.files.includes(safe)
|
|
502
|
+
|| violationPaths.has(safe) || !/^[a-f0-9]{64}$/i.test(item.sha256 || '')
|
|
503
|
+
|| adoptionHash(safe) !== item.sha256.toLowerCase()) throw new Error('adoption_content_or_owner_mismatch');
|
|
504
|
+
approved.push({ path: safe, sha256: item.sha256.toLowerCase(), held });
|
|
465
505
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
function hashPaths(files) {
|
|
471
|
-
const hashes = new Map();
|
|
472
|
-
for (const file of files) {
|
|
473
|
-
hashes.set(file, hashFile(path.join(workspacePath, file)));
|
|
506
|
+
for (const item of approved) item.held.adoptedHash = item.sha256;
|
|
507
|
+
result = { ok: true, files: approved.map(item => ({ path: item.path, sha256: item.sha256 })) };
|
|
508
|
+
} catch (error) { result = { ok: false, reason: String(error?.message || error).slice(0, 200) }; }
|
|
509
|
+
atomicWrite(path.join(adoptionResultsDir, name), JSON.stringify(result, null, 2));
|
|
474
510
|
}
|
|
475
|
-
return hashes;
|
|
476
511
|
}
|
|
477
512
|
|
|
478
|
-
function
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
return 'missing';
|
|
513
|
+
function blockedPaths() {
|
|
514
|
+
const blocked = new Set(violationPaths);
|
|
515
|
+
for (const [file, held] of quarantined) {
|
|
516
|
+
if (!held.adoptedHash || adoptionHash(file) !== held.adoptedHash) blocked.add(file);
|
|
483
517
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
function buildWorkerPrompt(job) {
|
|
487
|
-
const readOnly = job.readOnlyContext.length
|
|
488
|
-
? `- You may read these for context but must not modify them: ${job.readOnlyContext.join(', ')}.\n`
|
|
489
|
-
: '';
|
|
490
|
-
return [
|
|
491
|
-
'You are a subagent working on one slice of a larger task.',
|
|
492
|
-
'HARD CONSTRAINTS:',
|
|
493
|
-
`- You may modify ONLY these files: ${job.files.join(', ')}.`,
|
|
494
|
-
readOnly + '- Do not modify, create, or delete any other file. Inside .codex-overleaf-subagents/ you may touch ONLY the slice files listed above (if any).',
|
|
495
|
-
'- Work fully autonomously; nobody can answer questions.',
|
|
496
|
-
'',
|
|
497
|
-
job.task
|
|
498
|
-
].join('\n');
|
|
518
|
+
return blocked;
|
|
499
519
|
}
|
|
500
520
|
|
|
501
521
|
function onParentAbort() {
|
|
@@ -504,62 +524,56 @@ function createSubagentBroker(options = {}) {
|
|
|
504
524
|
for (const { controller } of running.values()) {
|
|
505
525
|
controller.abort(new Error('run cancelled'));
|
|
506
526
|
}
|
|
507
|
-
for (const jobId of queued.splice(0)) {
|
|
508
|
-
const entry = jobs.get(jobId);
|
|
509
|
-
entry.status = 'cancelled';
|
|
510
|
-
writeResult({ id: jobId, status: 'cancelled', reason: 'run cancelled' });
|
|
511
|
-
}
|
|
512
527
|
if (anyWorkerStarted) {
|
|
513
|
-
|
|
514
|
-
// already skips writeback, and the dirty mark forces the next run's
|
|
515
|
-
// mirror sync to rebuild the workspace from Overleaf (spec P1-3 fix).
|
|
516
|
-
onMirrorDirty();
|
|
528
|
+
try { onMirrorDirty(); } catch (error) { recordFailure(error); }
|
|
517
529
|
}
|
|
518
|
-
|
|
530
|
+
// Event listeners cannot expose an async rejection. The same sticky stop
|
|
531
|
+
// promise is still awaited by the parent runner and reports any I/O error.
|
|
532
|
+
stop({ drain: false }).catch(() => {});
|
|
519
533
|
}
|
|
520
534
|
|
|
521
|
-
|
|
522
|
-
accepting = false;
|
|
523
|
-
// Queued-but-unstarted jobs would otherwise vanish with no result file,
|
|
524
|
-
// hanging a lead still polling for its job count. Settle them first
|
|
525
|
-
// (mirrors onParentAbort's queue handling).
|
|
535
|
+
function cancelQueued(reason) {
|
|
526
536
|
for (const jobId of queued.splice(0)) {
|
|
527
537
|
const entry = jobs.get(jobId);
|
|
528
|
-
if (entry)
|
|
529
|
-
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
let graceTimer = null;
|
|
535
|
-
const grace = new Promise(resolve => {
|
|
536
|
-
graceTimer = setTimeout(resolve, limits.drainGraceMs);
|
|
537
|
-
});
|
|
538
|
-
await Promise.race([
|
|
539
|
-
Promise.allSettled([...running.values()].map(worker => worker.promise)),
|
|
540
|
-
grace
|
|
541
|
-
]);
|
|
542
|
-
// The grace timer gates an awaited race; if the workers settled first it
|
|
543
|
-
// must be cleared or it keeps the event loop alive for drainGraceMs
|
|
544
|
-
// (the "keep timers gated, then clear" discipline from c3cd357).
|
|
545
|
-
if (graceTimer) {
|
|
546
|
-
clearTimeout(graceTimer);
|
|
547
|
-
}
|
|
538
|
+
if (entry) entry.status = 'cancelled';
|
|
539
|
+
try { writeResult({ id: jobId, status: 'cancelled', reason }); }
|
|
540
|
+
catch (error) { recordFailure(error, jobId); }
|
|
541
|
+
emit('codex.subagent.failed', publicText(entry?.job?.title || jobId, 160), {
|
|
542
|
+
jobId, status: 'cancelled', reason
|
|
543
|
+
}, 'warning');
|
|
548
544
|
}
|
|
549
|
-
|
|
550
|
-
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function stop({ drain = true } = {}) {
|
|
548
|
+
accepting = false;
|
|
549
|
+
if (!stopping) stopping = Promise.resolve().then(() => stopWorkers(drain));
|
|
550
|
+
return stopping.then(() => { if (brokerError) throw brokerError; });
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
async function stopWorkers(drain) {
|
|
554
|
+
accepting = false;
|
|
555
|
+
try {
|
|
556
|
+
try { processAdoptions(); } catch (error) { recordFailure(error); }
|
|
557
|
+
cancelQueued(cancelled ? 'run cancelled' : 'The run ended before this subagent started.');
|
|
558
|
+
if (drain && !cancelled && !brokerError) await settleRunningWithin(limits.drainGraceMs);
|
|
559
|
+
} finally {
|
|
560
|
+
for (const { controller } of running.values()) controller.abort(new Error('broker drained'));
|
|
561
|
+
// Keep bounded waiting for workers that ignore abort; their own deadline
|
|
562
|
+
// remains active until settlement. OS exit proof belongs to the runner.
|
|
563
|
+
try { await settleRunningWithin(limits.drainGraceMs); } finally {
|
|
564
|
+
for (const jobId of running.keys()) for (const file of jobs.get(jobId).job.files) {
|
|
565
|
+
quarantined.set(file, { jobId, status: 'cancelled', adoptedHash: '' });
|
|
566
|
+
}
|
|
567
|
+
close();
|
|
568
|
+
}
|
|
551
569
|
}
|
|
552
|
-
// Bounded final settle: a worker that ignores its abort must not hang
|
|
553
|
-
// stop() forever — that strands codex.run and leaks the project lock (the
|
|
554
|
-
// very zombie-lock case handleCodexCancel exists to recover from).
|
|
555
|
-
await settleRunningWithin(limits.drainGraceMs);
|
|
556
570
|
if (jobs.size) {
|
|
557
571
|
emit('codex.subagent.drained', 'subagents drained', {
|
|
558
572
|
jobs: jobs.size,
|
|
559
573
|
violations: violations.length
|
|
560
|
-
}, 'completed');
|
|
574
|
+
}, brokerError ? 'warning' : 'completed');
|
|
561
575
|
}
|
|
562
|
-
|
|
576
|
+
if (brokerError) throw brokerError;
|
|
563
577
|
}
|
|
564
578
|
|
|
565
579
|
async function settleRunningWithin(timeoutMs) {
|
|
@@ -590,7 +604,7 @@ function createSubagentBroker(options = {}) {
|
|
|
590
604
|
parentSignal?.removeEventListener?.('abort', onParentAbort);
|
|
591
605
|
try {
|
|
592
606
|
writeBrokerFile('closed');
|
|
593
|
-
} catch (
|
|
607
|
+
} catch (error) { recordFailure(error); }
|
|
594
608
|
}
|
|
595
609
|
|
|
596
610
|
return {
|
|
@@ -599,7 +613,8 @@ function createSubagentBroker(options = {}) {
|
|
|
599
613
|
pollOnce,
|
|
600
614
|
hasActiveWorkers: () => running.size > 0,
|
|
601
615
|
hasAcceptedJobs: () => jobs.size > 0,
|
|
602
|
-
getViolationPaths:
|
|
616
|
+
getViolationPaths: blockedPaths,
|
|
617
|
+
getBlockedReason: file => violationPaths.has(file) ? 'subagent_unauthorized_edit' : 'subagent_unfinished_output',
|
|
603
618
|
getAuditSummary: () => ({
|
|
604
619
|
jobs: [...jobs.values()].map(entry => ({
|
|
605
620
|
id: entry.job?.id,
|
|
@@ -609,7 +624,9 @@ function createSubagentBroker(options = {}) {
|
|
|
609
624
|
reason: entry.result?.reason,
|
|
610
625
|
durationMs: entry.result?.durationMs
|
|
611
626
|
})),
|
|
612
|
-
violations: violations.slice()
|
|
627
|
+
violations: violations.slice(),
|
|
628
|
+
quarantined: [...quarantined].map(([file, item]) => ({ path: file, jobId: item.jobId, status: item.status,
|
|
629
|
+
adopted: Boolean(item.adoptedHash && adoptionHash(file) === item.adoptedHash) }))
|
|
613
630
|
})
|
|
614
631
|
};
|
|
615
632
|
}
|