@kbediako/codex-orchestrator 0.1.38 → 0.2.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/.agents/plugins/marketplace.json +20 -0
- package/README.md +46 -317
- package/bin/codex-orchestrator.js +161 -0
- package/codex.orchestrator.json +149 -13
- package/dist/bin/codex-orchestrator.js +797 -1154
- package/dist/orchestrator/src/cli/adapters/CommandBuilder.js +50 -0
- package/dist/orchestrator/src/cli/adapters/CommandPlanner.js +22 -4
- package/dist/orchestrator/src/cli/adapters/CommandReviewer.js +3 -3
- package/dist/orchestrator/src/cli/adapters/CommandTester.js +2 -2
- package/dist/orchestrator/src/cli/adapters/cloudFailureDiagnostics.js +295 -11
- package/dist/orchestrator/src/cli/coStatusAttachCliShell.js +402 -0
- package/dist/orchestrator/src/cli/coStatusCliShell.js +451 -0
- package/dist/orchestrator/src/cli/coStatusOperatorAutopilotCliShell.js +120 -0
- package/dist/orchestrator/src/cli/codexCliShell.js +119 -0
- package/dist/orchestrator/src/cli/codexDefaultsSetup.js +265 -36
- package/dist/orchestrator/src/cli/config/delegationConfig.js +317 -5
- package/dist/orchestrator/src/cli/config/repoConfigPolicy.js +2 -3
- package/dist/orchestrator/src/cli/config/userConfig.js +28 -13
- package/dist/orchestrator/src/cli/control/authenticatedControlRouteGate.js +69 -0
- package/dist/orchestrator/src/cli/control/authenticatedRouteComposition.js +267 -0
- package/dist/orchestrator/src/cli/control/authenticatedRouteController.js +5 -0
- package/dist/orchestrator/src/cli/control/authenticatedRouteDispatcher.js +41 -0
- package/dist/orchestrator/src/cli/control/compatibilityIssuePresenter.js +1035 -0
- package/dist/orchestrator/src/cli/control/confirmationApproveController.js +62 -0
- package/dist/orchestrator/src/cli/control/confirmationCreateController.js +69 -0
- package/dist/orchestrator/src/cli/control/confirmationIssueConsumeController.js +43 -0
- package/dist/orchestrator/src/cli/control/confirmationListController.js +22 -0
- package/dist/orchestrator/src/cli/control/confirmationValidateController.js +58 -0
- package/dist/orchestrator/src/cli/control/confirmations.js +25 -3
- package/dist/orchestrator/src/cli/control/controlActionCancelConfirmation.js +65 -0
- package/dist/orchestrator/src/cli/control/controlActionController.js +77 -0
- package/dist/orchestrator/src/cli/control/controlActionControllerSequencing.js +161 -0
- package/dist/orchestrator/src/cli/control/controlActionExecution.js +142 -0
- package/dist/orchestrator/src/cli/control/controlActionFinalization.js +43 -0
- package/dist/orchestrator/src/cli/control/controlActionOutcome.js +60 -0
- package/dist/orchestrator/src/cli/control/controlActionPreflight.js +476 -0
- package/dist/orchestrator/src/cli/control/controlAuthenticatedRouteHandoff.js +57 -0
- package/dist/orchestrator/src/cli/control/controlBootstrapAssembly.js +39 -0
- package/dist/orchestrator/src/cli/control/controlBootstrapMetadataPersistence.js +16 -0
- package/dist/orchestrator/src/cli/control/controlEventTransport.js +49 -0
- package/dist/orchestrator/src/cli/control/controlExpiryLifecycle.js +102 -0
- package/dist/orchestrator/src/cli/control/controlHostOwnership.js +480 -0
- package/dist/orchestrator/src/cli/control/controlHostSupervision.js +630 -0
- package/dist/orchestrator/src/cli/control/controlOversightFacade.js +8 -0
- package/dist/orchestrator/src/cli/control/controlOversightReadContract.js +1 -0
- package/dist/orchestrator/src/cli/control/controlOversightReadService.js +16 -0
- package/dist/orchestrator/src/cli/control/controlOversightUpdateContract.js +1 -0
- package/dist/orchestrator/src/cli/control/controlPersistenceFiles.js +6 -0
- package/dist/orchestrator/src/cli/control/controlQuestionChildResolution.js +18 -0
- package/dist/orchestrator/src/cli/control/controlRequestContext.js +42 -0
- package/dist/orchestrator/src/cli/control/controlRequestController.js +9 -0
- package/dist/orchestrator/src/cli/control/controlRequestPredispatch.js +17 -0
- package/dist/orchestrator/src/cli/control/controlRequestRouteDispatch.js +44 -0
- package/dist/orchestrator/src/cli/control/controlRuntime.js +1003 -0
- package/dist/orchestrator/src/cli/control/controlServer.js +23 -1456
- package/dist/orchestrator/src/cli/control/controlServerAuditAndErrorHelpers.js +115 -0
- package/dist/orchestrator/src/cli/control/controlServerAuthenticatedRouteBranch.js +29 -0
- package/dist/orchestrator/src/cli/control/controlServerBootstrapLifecycle.js +30 -0
- package/dist/orchestrator/src/cli/control/controlServerBootstrapStartSequence.js +21 -0
- package/dist/orchestrator/src/cli/control/controlServerOwnedRuntimeLifecycle.js +67 -0
- package/dist/orchestrator/src/cli/control/controlServerPublicLifecycle.js +756 -0
- package/dist/orchestrator/src/cli/control/controlServerPublicRouteHelpers.js +86 -0
- package/dist/orchestrator/src/cli/control/controlServerReadyInstanceLifecycle.js +25 -0
- package/dist/orchestrator/src/cli/control/controlServerReadyInstanceStartup.js +18 -0
- package/dist/orchestrator/src/cli/control/controlServerRequestBodyHelpers.js +37 -0
- package/dist/orchestrator/src/cli/control/controlServerRequestShell.js +40 -0
- package/dist/orchestrator/src/cli/control/controlServerRequestShellBinding.js +17 -0
- package/dist/orchestrator/src/cli/control/controlServerSeedLoading.js +27 -0
- package/dist/orchestrator/src/cli/control/controlServerSeededRuntimeAssembly.js +186 -0
- package/dist/orchestrator/src/cli/control/controlServerStartupInputPreparation.js +31 -0
- package/dist/orchestrator/src/cli/control/controlServerStartupSequence.js +49 -0
- package/dist/orchestrator/src/cli/control/controlState.js +233 -2
- package/dist/orchestrator/src/cli/control/controlStatusDashboard.js +1904 -0
- package/dist/orchestrator/src/cli/control/controlTelegramBridgeBootstrapLifecycle.js +22 -0
- package/dist/orchestrator/src/cli/control/controlTelegramBridgeLifecycle.js +67 -0
- package/dist/orchestrator/src/cli/control/controlTelegramBridgeOversightFacadeFactory.js +8 -0
- package/dist/orchestrator/src/cli/control/controlTelegramCommandController.js +49 -0
- package/dist/orchestrator/src/cli/control/controlTelegramDispatchRead.js +40 -0
- package/dist/orchestrator/src/cli/control/controlTelegramPollingController.js +89 -0
- package/dist/orchestrator/src/cli/control/controlTelegramProjectionNotificationController.js +29 -0
- package/dist/orchestrator/src/cli/control/controlTelegramPushState.js +63 -0
- package/dist/orchestrator/src/cli/control/controlTelegramQuestionRead.js +13 -0
- package/dist/orchestrator/src/cli/control/controlTelegramReadController.js +216 -0
- package/dist/orchestrator/src/cli/control/controlTelegramUpdateHandler.js +63 -0
- package/dist/orchestrator/src/cli/control/controlWatcher.js +73 -5
- package/dist/orchestrator/src/cli/control/delegationRegisterController.js +35 -0
- package/dist/orchestrator/src/cli/control/dynamicToolBridgePolicy.js +139 -0
- package/dist/orchestrator/src/cli/control/eventsSseController.js +12 -0
- package/dist/orchestrator/src/cli/control/linearBudgetState.js +1789 -0
- package/dist/orchestrator/src/cli/control/linearDispatchSource.js +1137 -0
- package/dist/orchestrator/src/cli/control/linearGraphqlClient.js +150 -0
- package/dist/orchestrator/src/cli/control/linearRateLimit.js +102 -0
- package/dist/orchestrator/src/cli/control/linearWebhookController.js +499 -0
- package/dist/orchestrator/src/cli/control/liveLinearAdvisoryRuntime.js +70 -0
- package/dist/orchestrator/src/cli/control/observabilityApiController.js +173 -0
- package/dist/orchestrator/src/cli/control/observabilityReadModel.js +500 -0
- package/dist/orchestrator/src/cli/control/observabilitySurface.js +284 -0
- package/dist/orchestrator/src/cli/control/observabilityUpdateNotifier.js +22 -0
- package/dist/orchestrator/src/cli/control/operatorDashboardPresenter.js +252 -0
- package/dist/orchestrator/src/cli/control/providerAgentCapacity.js +70 -0
- package/dist/orchestrator/src/cli/control/providerControlHostFreshnessGauge.js +1068 -0
- package/dist/orchestrator/src/cli/control/providerIntakeState.js +473 -0
- package/dist/orchestrator/src/cli/control/providerIssueHandoff.js +6811 -0
- package/dist/orchestrator/src/cli/control/providerIssueObservability.js +1348 -0
- package/dist/orchestrator/src/cli/control/providerIssueRetryQueue.js +84 -0
- package/dist/orchestrator/src/cli/control/providerLinearRuntimeProof.js +588 -0
- package/dist/orchestrator/src/cli/control/providerLinearScreenshotProof.js +473 -0
- package/dist/orchestrator/src/cli/control/providerLinearWorkerTruth.js +383 -0
- package/dist/orchestrator/src/cli/control/providerLinearWorkflowAudit.js +254 -0
- package/dist/orchestrator/src/cli/control/providerLinearWorkflowFacade.js +5573 -0
- package/dist/orchestrator/src/cli/control/providerLinearWorkflowStates.js +115 -0
- package/dist/orchestrator/src/cli/control/providerMergeCloseout.js +1868 -0
- package/dist/orchestrator/src/cli/control/providerOperatorAutopilot.js +1580 -0
- package/dist/orchestrator/src/cli/control/providerOperatorAutopilotLifecycle.js +154 -0
- package/dist/orchestrator/src/cli/control/providerOperatorAutopilotLocalRolloutExecution.js +1006 -0
- package/dist/orchestrator/src/cli/control/providerPollingHealth.js +435 -0
- package/dist/orchestrator/src/cli/control/providerTerminalCleanup.js +516 -0
- package/dist/orchestrator/src/cli/control/providerWorkerHosts.js +191 -0
- package/dist/orchestrator/src/cli/control/providerWorkflowConfigStore.js +515 -0
- package/dist/orchestrator/src/cli/control/questionChildResolutionAdapter.js +361 -0
- package/dist/orchestrator/src/cli/control/questionQueueController.js +181 -0
- package/dist/orchestrator/src/cli/control/questionReadRetryDeduplication.js +9 -0
- package/dist/orchestrator/src/cli/control/questionReadSequence.js +10 -0
- package/dist/orchestrator/src/cli/control/securityViolationController.js +27 -0
- package/dist/orchestrator/src/cli/control/selectedRunProjection.js +1885 -0
- package/dist/orchestrator/src/cli/control/telegramOversightApiClient.js +48 -0
- package/dist/orchestrator/src/cli/control/telegramOversightBridge.js +180 -0
- package/dist/orchestrator/src/cli/control/telegramOversightBridgeProjectionDeliveryQueue.js +25 -0
- package/dist/orchestrator/src/cli/control/telegramOversightBridgeRuntimeLifecycle.js +45 -0
- package/dist/orchestrator/src/cli/control/telegramOversightBridgeStateStore.js +77 -0
- package/dist/orchestrator/src/cli/control/telegramOversightControlActionApiClient.js +45 -0
- package/dist/orchestrator/src/cli/control/trackerDispatchPilot.js +439 -0
- package/dist/orchestrator/src/cli/control/uiDataController.js +34 -0
- package/dist/orchestrator/src/cli/control/uiSessionController.js +100 -0
- package/dist/orchestrator/src/cli/controlHostCliShell.js +860 -0
- package/dist/orchestrator/src/cli/controlHostFreshnessGaugeCliShell.js +129 -0
- package/dist/orchestrator/src/cli/controlHostSupervisionCliShell.js +2127 -0
- package/dist/orchestrator/src/cli/delegationCliShell.js +62 -0
- package/dist/orchestrator/src/cli/delegationServer.js +567 -678
- package/dist/orchestrator/src/cli/delegationServerCliShell.js +52 -0
- package/dist/orchestrator/src/cli/delegationServerQuestionFlowShell.js +228 -0
- package/dist/orchestrator/src/cli/delegationServerToolDispatchShell.js +411 -0
- package/dist/orchestrator/src/cli/delegationServerTransport.js +274 -0
- package/dist/orchestrator/src/cli/delegationSetup.js +51 -171
- package/dist/orchestrator/src/cli/devtoolsCliShell.js +34 -0
- package/dist/orchestrator/src/cli/doctor.js +678 -164
- package/dist/orchestrator/src/cli/doctorCliRequestShell.js +72 -0
- package/dist/orchestrator/src/cli/doctorCliShell.js +138 -0
- package/dist/orchestrator/src/cli/doctorUsage.js +119 -15
- package/dist/orchestrator/src/cli/exec/experience.js +16 -2
- package/dist/orchestrator/src/cli/exec/summary.js +3 -0
- package/dist/orchestrator/src/cli/execCliShell.js +51 -0
- package/dist/orchestrator/src/cli/flowCliRequestShell.js +44 -0
- package/dist/orchestrator/src/cli/flowCliShell.js +239 -0
- package/dist/orchestrator/src/cli/frontendTestCliRequestShell.js +80 -0
- package/dist/orchestrator/src/cli/frontendTestCliShell.js +41 -0
- package/dist/orchestrator/src/cli/init.js +95 -1
- package/dist/orchestrator/src/cli/initCliShell.js +50 -0
- package/dist/orchestrator/src/cli/linearCliShell.js +1200 -0
- package/dist/orchestrator/src/cli/mcpEnableCliShell.js +132 -0
- package/dist/orchestrator/src/cli/metrics/metricsAggregator.js +3 -2
- package/dist/orchestrator/src/cli/metrics/metricsRecorder.js +56 -0
- package/dist/orchestrator/src/cli/orchestrator.js +66 -1376
- package/dist/orchestrator/src/cli/planCliShell.js +19 -0
- package/dist/orchestrator/src/cli/prCliShell.js +41 -0
- package/dist/orchestrator/src/cli/providerLinearChildLanePhaseContract.js +204 -0
- package/dist/orchestrator/src/cli/providerLinearChildLaneRunner.js +1835 -0
- package/dist/orchestrator/src/cli/providerLinearChildLaneShell.js +2420 -0
- package/dist/orchestrator/src/cli/providerLinearChildStreamShell.js +385 -0
- package/dist/orchestrator/src/cli/providerLinearWorkerRunner.js +6834 -0
- package/dist/orchestrator/src/cli/resumeCliShell.js +14 -0
- package/dist/orchestrator/src/cli/reviewCliLaunchShell.js +72 -0
- package/dist/orchestrator/src/cli/rlm/alignment.js +3 -3
- package/dist/orchestrator/src/cli/rlm/context.js +94 -7
- package/dist/orchestrator/src/cli/rlm/rlmCodexRuntimeShell.js +546 -0
- package/dist/orchestrator/src/cli/rlm/symbolic.js +4 -2
- package/dist/orchestrator/src/cli/rlmCliRequestShell.js +42 -0
- package/dist/orchestrator/src/cli/rlmCompletionCliShell.js +46 -0
- package/dist/orchestrator/src/cli/rlmLaunchCliShell.js +51 -0
- package/dist/orchestrator/src/cli/rlmRunner.js +83 -523
- package/dist/orchestrator/src/cli/run/blockMemory.js +500 -0
- package/dist/orchestrator/src/cli/run/manifest.js +410 -73
- package/dist/orchestrator/src/cli/run/manifestPersister.js +45 -14
- package/dist/orchestrator/src/cli/run/runMemoryController.js +216 -0
- package/dist/orchestrator/src/cli/run/source0.js +690 -0
- package/dist/orchestrator/src/cli/run/workspacePath.js +101 -0
- package/dist/orchestrator/src/cli/runtime/mode.js +2 -1
- package/dist/orchestrator/src/cli/runtime/provider.js +39 -2
- package/dist/orchestrator/src/cli/selfCheckCliShell.js +12 -0
- package/dist/orchestrator/src/cli/services/commandRunner.js +698 -18
- package/dist/orchestrator/src/cli/services/execRuntime.js +66 -1
- package/dist/orchestrator/src/cli/services/orchestratorAutoScoutEvidenceRecorder.js +71 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudBranchResolution.js +8 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudEnvironmentResolution.js +22 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudExecutionLifecycleShell.js +39 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudPromptBuilder.js +37 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudRouteFallbackContract.js +45 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudRouteShell.js +36 -0
- package/dist/orchestrator/src/cli/services/orchestratorCloudTargetExecutor.js +277 -0
- package/dist/orchestrator/src/cli/services/orchestratorControlPlaneLifecycle.js +98 -0
- package/dist/orchestrator/src/cli/services/orchestratorControlPlaneLifecycleShell.js +54 -0
- package/dist/orchestrator/src/cli/services/orchestratorExecutionLifecycle.js +112 -0
- package/dist/orchestrator/src/cli/services/orchestratorExecutionModePolicy.js +27 -0
- package/dist/orchestrator/src/cli/services/orchestratorExecutionRouteAdapterShell.js +59 -0
- package/dist/orchestrator/src/cli/services/orchestratorExecutionRouteDecisionShell.js +57 -0
- package/dist/orchestrator/src/cli/services/orchestratorExecutionRouteState.js +21 -0
- package/dist/orchestrator/src/cli/services/orchestratorExecutionRouter.js +2 -0
- package/dist/orchestrator/src/cli/services/orchestratorLocalPipelineExecutor.js +149 -0
- package/dist/orchestrator/src/cli/services/orchestratorLocalRouteShell.js +63 -0
- package/dist/orchestrator/src/cli/services/orchestratorPlanShell.js +54 -0
- package/dist/orchestrator/src/cli/services/orchestratorPlanTargetTracker.js +16 -0
- package/dist/orchestrator/src/cli/services/orchestratorResumePreparationShell.js +84 -0
- package/dist/orchestrator/src/cli/services/orchestratorResumeTokenValidation.js +15 -0
- package/dist/orchestrator/src/cli/services/orchestratorRunLifecycleCompletion.js +31 -0
- package/dist/orchestrator/src/cli/services/orchestratorRunLifecycleExecutionRegistration.js +37 -0
- package/dist/orchestrator/src/cli/services/orchestratorRunLifecycleOrchestrationShell.js +83 -0
- package/dist/orchestrator/src/cli/services/orchestratorRunLifecycleTaskManagerShell.js +37 -0
- package/dist/orchestrator/src/cli/services/orchestratorRuntimeManifestMutation.js +20 -0
- package/dist/orchestrator/src/cli/services/orchestratorStartPreparationShell.js +56 -0
- package/dist/orchestrator/src/cli/services/orchestratorStatusShell.js +70 -0
- package/dist/orchestrator/src/cli/services/pipelineResolver.js +7 -3
- package/dist/orchestrator/src/cli/services/plannerMemory.js +119 -0
- package/dist/orchestrator/src/cli/services/runPreparation.js +7 -3
- package/dist/orchestrator/src/cli/services/runSummaryWriter.js +9 -0
- package/dist/orchestrator/src/cli/setupBootstrapShell.js +114 -0
- package/dist/orchestrator/src/cli/setupCliShell.js +51 -0
- package/dist/orchestrator/src/cli/skillsCliShell.js +56 -0
- package/dist/orchestrator/src/cli/startCliRequestShell.js +53 -0
- package/dist/orchestrator/src/cli/startCliShell.js +68 -0
- package/dist/orchestrator/src/cli/statusCliShell.js +22 -0
- package/dist/orchestrator/src/cli/utils/authProvenanceFingerprint.js +27 -0
- package/dist/orchestrator/src/cli/utils/cloudPreflight.js +285 -7
- package/dist/orchestrator/src/cli/utils/codexFeatures.js +60 -0
- package/dist/orchestrator/src/cli/utils/delegationConfigParser.js +250 -0
- package/dist/orchestrator/src/cli/utils/delegationMcpHealth.js +1382 -0
- package/dist/orchestrator/src/cli/utils/devtools.js +2 -54
- package/dist/orchestrator/src/cli/utils/mcpServerEntry.js +53 -0
- package/dist/orchestrator/src/cli/utils/packageProgramResolver.js +151 -0
- package/dist/orchestrator/src/cli/utils/providerOverrideEnv.js +71 -0
- package/dist/orchestrator/src/cli/utils/trailingJsonObject.js +59 -0
- package/dist/orchestrator/src/learning/crystalizer.js +2 -2
- package/dist/orchestrator/src/manager.js +74 -4
- package/dist/orchestrator/src/persistence/ExperienceStore.js +233 -49
- package/dist/orchestrator/src/persistence/TaskStateStore.js +6 -6
- package/dist/orchestrator/src/persistence/lockFile.js +70 -4
- package/dist/orchestrator/src/persistence/sanitizeIdentifier.js +39 -0
- package/dist/orchestrator/src/sync/createCloudSyncWorker.js +3 -2
- package/dist/orchestrator/src/utils/atomicWrite.js +17 -2
- package/dist/packages/orchestrator/src/exec/unified-exec.js +99 -6
- package/dist/packages/orchestrator/src/instructions/promptPacks.js +150 -19
- package/dist/packages/sdk-node/src/orchestrator.js +137 -13
- package/dist/packages/shared/config/designConfig.js +8 -1
- package/dist/packages/shared/streams/stdio.js +1 -1
- package/dist/scripts/design/pipeline/permit.js +15 -0
- package/dist/scripts/lib/docs-catalog.js +399 -0
- package/dist/scripts/lib/docs-helpers.js +87 -5
- package/dist/scripts/lib/pr-watch-merge.js +1088 -80
- package/dist/scripts/lib/provider-run-contract.js +26 -0
- package/dist/scripts/lib/review-command-intent-classification.js +532 -0
- package/dist/scripts/lib/review-command-probe-classification.js +385 -0
- package/dist/scripts/lib/review-execution-boundary-preflight.js +279 -0
- package/dist/scripts/lib/review-execution-runtime.js +753 -0
- package/dist/scripts/lib/review-execution-state.js +1144 -0
- package/dist/scripts/lib/review-execution-telemetry.js +215 -0
- package/dist/scripts/lib/review-inspection-target-parsing.js +78 -0
- package/dist/scripts/lib/review-launch-attempt.js +601 -0
- package/dist/scripts/lib/review-meta-surface-boundary-analysis.js +300 -0
- package/dist/scripts/lib/review-meta-surface-normalization.js +746 -0
- package/dist/scripts/lib/review-non-interactive-handoff.js +61 -0
- package/dist/scripts/lib/review-prompt-context.js +376 -0
- package/dist/scripts/lib/review-scope-advisory.js +286 -0
- package/dist/scripts/lib/review-scope-paths.js +123 -0
- package/dist/scripts/lib/review-shell-command-parser.js +389 -0
- package/dist/scripts/lib/review-shell-env-interpreter.js +340 -0
- package/dist/scripts/lib/run-manifests.js +192 -36
- package/dist/scripts/lib/spark-policy-classifier.js +593 -0
- package/dist/scripts/run-review.js +507 -1777
- package/docs/README.md +43 -20
- package/docs/book/README.md +19 -0
- package/docs/book/codex-cli-0124-adoption.md +68 -0
- package/docs/book/local-hook-impact.md +73 -0
- package/docs/book/operations.md +60 -0
- package/docs/book/public-posture.md +34 -0
- package/docs/book/setup.md +91 -0
- package/docs/book/skills.md +11 -0
- package/docs/guides/codex-version-policy.md +104 -0
- package/docs/public/downstream-setup.md +113 -0
- package/docs/public/provider-onboarding.md +173 -0
- package/package.json +23 -10
- package/plugins/codex-orchestrator/.codex-plugin/plugin.json +30 -0
- package/plugins/codex-orchestrator/.mcp.json +13 -0
- package/plugins/codex-orchestrator/launcher.mjs +361 -0
- package/schemas/manifest.json +411 -0
- package/skills/README.md +26 -0
- package/skills/collab-subagents-first/SKILL.md +1 -1
- package/skills/delegation-usage/DELEGATION_GUIDE.md +30 -12
- package/skills/delegation-usage/SKILL.md +25 -14
- package/skills/land/SKILL.md +77 -0
- package/skills/linear/SKILL.md +255 -0
- package/skills/release/SKILL.md +47 -3
- package/skills/standalone-review/SKILL.md +6 -1
- package/templates/README.md +4 -2
- package/templates/codex/.codex/agents/awaiter-high.toml +2 -2
- package/templates/codex/.codex/agents/worker-complex.toml +1 -1
- package/templates/codex/.codex/config.toml +3 -4
- package/templates/codex/.codex/providers/README.md +13 -0
- package/templates/codex/.codex/providers/control.example.json +18 -0
- package/templates/codex/.codex/providers/provider.env.example +15 -0
- package/templates/codex/AGENTS.md +15 -8
- package/templates/codex/mcp-client.json +5 -1
- package/docs/assets/setup.gif +0 -0
|
@@ -0,0 +1,756 @@
|
|
|
1
|
+
import { isoTimestamp } from '../utils/time.js';
|
|
2
|
+
import { readSharedLinearBudgetStatus, resolveLinearPollingInterval } from './linearBudgetState.js';
|
|
3
|
+
import { resolveLiveLinearTrackedIssues, } from './linearDispatchSource.js';
|
|
4
|
+
import { resolveLinearWebhookSourceSetup } from './linearWebhookController.js';
|
|
5
|
+
import { initializeProviderPollingHealth, isProviderPollingStuck, flushProviderPollingHealthUpdates, markProviderPollingStuck, markProviderPollingCompleted, markProviderPollingStarted, noteProviderPollingRequest, readProviderPollingHealth, scheduleProviderPolling } from './providerPollingHealth.js';
|
|
6
|
+
import { beginClosingControlServerHttpServer, closeControlServerOwnedRuntime, startControlServerReadyInstanceLifecycle } from './controlServerReadyInstanceLifecycle.js';
|
|
7
|
+
import { prepareControlServerStartupInputs } from './controlServerStartupInputPreparation.js';
|
|
8
|
+
import { acquireControlHostOwnership } from './controlHostOwnership.js';
|
|
9
|
+
const EXPIRY_INTERVAL_MS = 15_000;
|
|
10
|
+
const PROVIDER_REFRESH_INTERVAL_MS = 15_000;
|
|
11
|
+
const PROVIDER_REFRESH_STUCK_AFTER_MS = 45_000;
|
|
12
|
+
const PROVIDER_CLOSE_STUCK_DRAIN_GRACE_MS = 1_000;
|
|
13
|
+
const PROVIDER_FULL_RECOVERY_SWEEP_INTERVAL_MS = 10 * 60 * 1000;
|
|
14
|
+
const SESSION_TTL_MS = 15 * 60 * 1000;
|
|
15
|
+
const providerIssueHandoffOperations = new WeakMap();
|
|
16
|
+
export async function startControlServerPublicLifecycle(options) {
|
|
17
|
+
let controlHostOwnership = null;
|
|
18
|
+
try {
|
|
19
|
+
controlHostOwnership =
|
|
20
|
+
!options.controlHostOwnership
|
|
21
|
+
? null
|
|
22
|
+
: await acquireControlHostOwnership({
|
|
23
|
+
paths: options.paths,
|
|
24
|
+
runId: options.runId,
|
|
25
|
+
repoRoot: options.controlHostOwnership?.repoRoot,
|
|
26
|
+
taskId: options.controlHostOwnership?.taskId,
|
|
27
|
+
pipelineId: options.controlHostOwnership?.pipelineId
|
|
28
|
+
});
|
|
29
|
+
const startupInputs = await prepareControlServerStartupInputs({
|
|
30
|
+
paths: options.paths,
|
|
31
|
+
config: options.config,
|
|
32
|
+
eventStream: options.eventStream,
|
|
33
|
+
runId: options.runId,
|
|
34
|
+
sessionTtlMs: SESSION_TTL_MS,
|
|
35
|
+
providerWorkflowConfigStore: options.providerWorkflowConfigStore,
|
|
36
|
+
createProviderIssueHandoff: options.createProviderIssueHandoff
|
|
37
|
+
});
|
|
38
|
+
const readyInstance = await startControlServerReadyInstanceLifecycle({
|
|
39
|
+
requestContextShared: startupInputs.requestContextShared,
|
|
40
|
+
host: startupInputs.host,
|
|
41
|
+
controlToken: startupInputs.controlToken,
|
|
42
|
+
intervalMs: EXPIRY_INTERVAL_MS
|
|
43
|
+
});
|
|
44
|
+
const providerRefreshCoordinator = startupInputs.requestContextShared.providerIssueHandoff
|
|
45
|
+
? createProviderRefreshCoordinator(startupInputs.requestContextShared.providerIssueHandoff, {
|
|
46
|
+
readFeatureToggles: startupInputs.requestContextShared.controlStore
|
|
47
|
+
? () => startupInputs.requestContextShared.controlStore.snapshot().feature_toggles
|
|
48
|
+
: null
|
|
49
|
+
})
|
|
50
|
+
: null;
|
|
51
|
+
if (startupInputs.requestContextShared.providerIssueHandoff) {
|
|
52
|
+
const persistProviderIntakePolling = startupInputs.requestContextShared.persist?.providerIntakePolling ?? null;
|
|
53
|
+
const persistedPollingSnapshot = startupInputs.requestContextShared.providerIntakeState?.polling ?? null;
|
|
54
|
+
initializeProviderPollingHealth(startupInputs.requestContextShared.providerIssueHandoff, {
|
|
55
|
+
intervalMs: PROVIDER_REFRESH_INTERVAL_MS,
|
|
56
|
+
stuckAfterMs: PROVIDER_REFRESH_STUCK_AFTER_MS,
|
|
57
|
+
controlHostOwner: controlHostOwnership?.polling ?? null,
|
|
58
|
+
skipInitialUpdate: persistedPollingSnapshot !== null,
|
|
59
|
+
onUpdate: startupInputs.requestContextShared.providerIntakeState && persistProviderIntakePolling
|
|
60
|
+
? async (polling) => {
|
|
61
|
+
const pollingUpdatedAt = typeof polling.updated_at === 'string' && polling.updated_at.trim().length > 0
|
|
62
|
+
? polling.updated_at
|
|
63
|
+
: isoTimestamp();
|
|
64
|
+
const stateUpdatedAt = pickLatestTimestamp(startupInputs.requestContextShared.providerIntakeState.updated_at, pollingUpdatedAt);
|
|
65
|
+
const nextPolling = {
|
|
66
|
+
...polling,
|
|
67
|
+
updated_at: pollingUpdatedAt
|
|
68
|
+
};
|
|
69
|
+
startupInputs.requestContextShared.providerIntakeState.polling = nextPolling;
|
|
70
|
+
startupInputs.requestContextShared.providerIntakeState.updated_at = stateUpdatedAt;
|
|
71
|
+
await persistProviderIntakePolling(nextPolling, stateUpdatedAt);
|
|
72
|
+
}
|
|
73
|
+
: null
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
const providerRefreshStartupTrigger = providerRefreshCoordinator
|
|
77
|
+
? scheduleStartupProviderRefresh(providerRefreshCoordinator.trigger)
|
|
78
|
+
: null;
|
|
79
|
+
return {
|
|
80
|
+
server: readyInstance.server,
|
|
81
|
+
requestContextShared: startupInputs.requestContextShared,
|
|
82
|
+
lifecycleState: readyInstance.lifecycleState,
|
|
83
|
+
...(controlHostOwnership ? { controlHostOwnership } : {}),
|
|
84
|
+
...(providerRefreshCoordinator
|
|
85
|
+
? {
|
|
86
|
+
providerRefreshTimer: providerRefreshCoordinator.timer,
|
|
87
|
+
providerRefreshStartupTrigger,
|
|
88
|
+
triggerProviderRefresh: providerRefreshCoordinator.trigger
|
|
89
|
+
}
|
|
90
|
+
: {}),
|
|
91
|
+
baseUrl: readyInstance.baseUrl
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
await controlHostOwnership?.release().catch(() => undefined);
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
export async function closeControlServerPublicLifecycle(state) {
|
|
100
|
+
if (state.providerRefreshStartupTrigger) {
|
|
101
|
+
clearTimeout(state.providerRefreshStartupTrigger);
|
|
102
|
+
}
|
|
103
|
+
if (state.providerRefreshTimer) {
|
|
104
|
+
state.providerRefreshTimer.cancel();
|
|
105
|
+
}
|
|
106
|
+
const serverClosePromise = beginClosingControlServerHttpServer(state.server);
|
|
107
|
+
void serverClosePromise.catch(() => undefined);
|
|
108
|
+
const providerIssueHandoff = state.requestContextShared.providerIssueHandoff ?? null;
|
|
109
|
+
let closeError = null;
|
|
110
|
+
const captureCloseError = (error) => {
|
|
111
|
+
if (closeError === null) {
|
|
112
|
+
closeError = error;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
if (providerIssueHandoff) {
|
|
116
|
+
try {
|
|
117
|
+
await waitForProviderIssueHandoffQueueToDrain(providerIssueHandoff, () => resolveProviderIssueHandoffWatchdogDelayMs(providerIssueHandoff), { settleStuckPending: true });
|
|
118
|
+
await flushProviderPollingHealthUpdates(providerIssueHandoff);
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
captureCloseError(error);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
await closeControlServerOwnedRuntime({
|
|
126
|
+
server: state.server,
|
|
127
|
+
requestContextShared: state.requestContextShared,
|
|
128
|
+
lifecycleState: state.lifecycleState,
|
|
129
|
+
serverClosePromise
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
captureCloseError(error);
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
await serverClosePromise;
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
captureCloseError(error);
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
await state.controlHostOwnership?.release();
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
captureCloseError(error);
|
|
146
|
+
}
|
|
147
|
+
if (closeError !== null) {
|
|
148
|
+
throw closeError;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
export function runProviderIssueHandoffRefresh(providerIssueHandoff, options) {
|
|
152
|
+
const state = getProviderIssueHandoffOperationState(providerIssueHandoff);
|
|
153
|
+
const acknowledgeAccepted = options?.acknowledgeAccepted === true;
|
|
154
|
+
const allowIdleRestartRequiredRetry = options?.allowIdleRestartRequiredRetry === true;
|
|
155
|
+
if (state.active) {
|
|
156
|
+
const continueWhileBusy = () => {
|
|
157
|
+
if (!options?.queueIfBusy) {
|
|
158
|
+
return mapProviderIssueHandoffRefreshOutcome(providerIssueHandoff, waitForProviderIssueHandoffPending(providerIssueHandoff, state.active), {
|
|
159
|
+
queued: true,
|
|
160
|
+
coalesced: true
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
noteProviderPollingRequest(providerIssueHandoff, {
|
|
164
|
+
mode: 'refresh',
|
|
165
|
+
queued: true,
|
|
166
|
+
preserveActiveMode: true
|
|
167
|
+
});
|
|
168
|
+
if (state.queuedRefresh) {
|
|
169
|
+
const queuedOutcome = {
|
|
170
|
+
queued: true,
|
|
171
|
+
coalesced: true
|
|
172
|
+
};
|
|
173
|
+
return acknowledgeAccepted
|
|
174
|
+
? acknowledgeProviderIssueHandoffAccepted(state.queuedRefresh, queuedOutcome)
|
|
175
|
+
: mapProviderIssueHandoffRefreshOutcome(providerIssueHandoff, state.queuedRefresh, queuedOutcome);
|
|
176
|
+
}
|
|
177
|
+
const queuedRefresh = queueProviderIssueHandoffRefresh(providerIssueHandoff, state, () => providerIssueHandoff.refresh(), {
|
|
178
|
+
mode: 'refresh'
|
|
179
|
+
});
|
|
180
|
+
const queuedOutcome = {
|
|
181
|
+
queued: true,
|
|
182
|
+
coalesced: false
|
|
183
|
+
};
|
|
184
|
+
return acknowledgeAccepted
|
|
185
|
+
? acknowledgeProviderIssueHandoffAccepted(queuedRefresh, queuedOutcome)
|
|
186
|
+
: mapProviderIssueHandoffRefreshOutcome(providerIssueHandoff, queuedRefresh, queuedOutcome);
|
|
187
|
+
};
|
|
188
|
+
if (isProviderPollingStuck(providerIssueHandoff)) {
|
|
189
|
+
return (async () => {
|
|
190
|
+
const stuckOutcome = await resolveProviderIssueHandoffStuckOutcome(providerIssueHandoff);
|
|
191
|
+
if (!stuckOutcome) {
|
|
192
|
+
return continueWhileBusy();
|
|
193
|
+
}
|
|
194
|
+
if (allowIdleRestartRequiredRetry) {
|
|
195
|
+
detachProviderIssueHandoffPending(state.active);
|
|
196
|
+
detachProviderIssueHandoffPending(state.queuedRefresh);
|
|
197
|
+
state.active = null;
|
|
198
|
+
state.queuedRefresh = null;
|
|
199
|
+
providerIssueHandoff.resetStuckRefreshLifecycle?.();
|
|
200
|
+
const activeRefresh = waitForProviderIssueHandoffPending(providerIssueHandoff, startProviderIssueHandoffOperation(providerIssueHandoff, state, () => providerIssueHandoff.refresh(), {
|
|
201
|
+
mode: 'refresh'
|
|
202
|
+
}));
|
|
203
|
+
const activeOutcome = {
|
|
204
|
+
queued: true,
|
|
205
|
+
coalesced: false
|
|
206
|
+
};
|
|
207
|
+
return acknowledgeAccepted
|
|
208
|
+
? acknowledgeProviderIssueHandoffAccepted(activeRefresh, activeOutcome)
|
|
209
|
+
: mapProviderIssueHandoffRefreshOutcome(providerIssueHandoff, activeRefresh, activeOutcome);
|
|
210
|
+
}
|
|
211
|
+
noteProviderPollingRequest(providerIssueHandoff, {
|
|
212
|
+
mode: 'refresh',
|
|
213
|
+
queued: state.queuedRefresh !== null,
|
|
214
|
+
replaceQueued: true,
|
|
215
|
+
preserveActiveMode: true
|
|
216
|
+
});
|
|
217
|
+
return stuckOutcome;
|
|
218
|
+
})();
|
|
219
|
+
}
|
|
220
|
+
return continueWhileBusy();
|
|
221
|
+
}
|
|
222
|
+
const idleStuckError = buildProviderIssueHandoffRestartRequiredError(providerIssueHandoff);
|
|
223
|
+
if (idleStuckError && !allowIdleRestartRequiredRetry) {
|
|
224
|
+
clearProviderIssueHandoffOperationState(providerIssueHandoff, state);
|
|
225
|
+
return mapProviderIssueHandoffRefreshOutcome(providerIssueHandoff, Promise.reject(idleStuckError), {
|
|
226
|
+
queued: true,
|
|
227
|
+
coalesced: true
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
const activeRefresh = waitForProviderIssueHandoffPending(providerIssueHandoff, startProviderIssueHandoffOperation(providerIssueHandoff, state, () => providerIssueHandoff.refresh(), {
|
|
231
|
+
mode: 'refresh'
|
|
232
|
+
}));
|
|
233
|
+
const activeOutcome = {
|
|
234
|
+
queued: true,
|
|
235
|
+
coalesced: false
|
|
236
|
+
};
|
|
237
|
+
return acknowledgeAccepted
|
|
238
|
+
? acknowledgeProviderIssueHandoffAccepted(activeRefresh, activeOutcome)
|
|
239
|
+
: mapProviderIssueHandoffRefreshOutcome(providerIssueHandoff, activeRefresh, activeOutcome);
|
|
240
|
+
}
|
|
241
|
+
export function runProviderIssueHandoffRehydrate(providerIssueHandoff) {
|
|
242
|
+
return runProviderIssueHandoffOperation(providerIssueHandoff, () => providerIssueHandoff.rehydrate());
|
|
243
|
+
}
|
|
244
|
+
export function runProviderIssueHandoffPoll(providerIssueHandoff, input, options) {
|
|
245
|
+
return runProviderIssueHandoffOperation(providerIssueHandoff, () => providerIssueHandoff.poll
|
|
246
|
+
? providerIssueHandoff.poll(input)
|
|
247
|
+
: providerIssueHandoff.refresh(), options, {
|
|
248
|
+
mode: providerIssueHandoff.poll ? 'poll' : 'refresh'
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
function createProviderRefreshCoordinator(providerIssueHandoff, context) {
|
|
252
|
+
let stopped = false;
|
|
253
|
+
let timer = null;
|
|
254
|
+
let rescheduleGeneration = 0;
|
|
255
|
+
let lastSuccessfulFullRecoverySweepAtMs = null;
|
|
256
|
+
const clearScheduledTrigger = () => {
|
|
257
|
+
if (!timer) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
clearTimeout(timer);
|
|
261
|
+
timer = null;
|
|
262
|
+
};
|
|
263
|
+
const scheduleNextTriggerAsync = async () => {
|
|
264
|
+
if (stopped || timer) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const schedule = await resolveProviderRefreshSchedule().catch(() => ({
|
|
268
|
+
interval_ms: PROVIDER_REFRESH_INTERVAL_MS,
|
|
269
|
+
reason: null,
|
|
270
|
+
linear_budget: null
|
|
271
|
+
}));
|
|
272
|
+
if (stopped || timer) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
scheduleProviderPolling(providerIssueHandoff, {
|
|
276
|
+
intervalMs: schedule.interval_ms,
|
|
277
|
+
reason: schedule.reason,
|
|
278
|
+
linearBudget: schedule.linear_budget
|
|
279
|
+
});
|
|
280
|
+
timer = setTimeout(() => {
|
|
281
|
+
timer = null;
|
|
282
|
+
void trigger();
|
|
283
|
+
}, schedule.interval_ms);
|
|
284
|
+
timer.unref?.();
|
|
285
|
+
};
|
|
286
|
+
const resolveProviderRefreshSchedule = async () => resolveLinearPollingInterval({
|
|
287
|
+
budget: await readSharedLinearBudgetStatus(process.env, {
|
|
288
|
+
operation: 'dispatch_source_tracked_issues'
|
|
289
|
+
}).catch(() => null),
|
|
290
|
+
default_interval_ms: PROVIDER_REFRESH_INTERVAL_MS,
|
|
291
|
+
operation: 'dispatch_source_tracked_issues'
|
|
292
|
+
});
|
|
293
|
+
const resolveWatchdogDelayMs = () => resolveProviderIssueHandoffWatchdogDelayMs(providerIssueHandoff);
|
|
294
|
+
const shouldRunFullRecoverySweep = (nowMs) => lastSuccessfulFullRecoverySweepAtMs === null ||
|
|
295
|
+
nowMs - lastSuccessfulFullRecoverySweepAtMs >= PROVIDER_FULL_RECOVERY_SWEEP_INTERVAL_MS;
|
|
296
|
+
const trigger = async () => {
|
|
297
|
+
if (stopped) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const preflightSchedule = await resolveProviderRefreshSchedule().catch(() => null);
|
|
301
|
+
if (stopped) {
|
|
302
|
+
clearScheduledTrigger();
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (preflightSchedule?.linear_budget?.cooldown_active) {
|
|
306
|
+
clearScheduledTrigger();
|
|
307
|
+
await scheduleNextTriggerAsync();
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
if (isProviderPollingStuck(providerIssueHandoff)) {
|
|
311
|
+
await resolveProviderIssueHandoffStuckOutcome(providerIssueHandoff);
|
|
312
|
+
await scheduleNextTriggerAsync();
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
clearScheduledTrigger();
|
|
316
|
+
const generation = ++rescheduleGeneration;
|
|
317
|
+
try {
|
|
318
|
+
const operation = runProviderIssueHandoffOperation(providerIssueHandoff, async () => {
|
|
319
|
+
if (!providerIssueHandoff.poll || !context.readFeatureToggles) {
|
|
320
|
+
await providerIssueHandoff.refresh();
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const refetchTrackedIssues = async (input) => await resolveProviderPollTrackedIssues(context, input);
|
|
324
|
+
if (shouldRunFullRecoverySweep(Date.now())) {
|
|
325
|
+
const pollResolution = await refetchTrackedIssues({
|
|
326
|
+
mode: 'recovery_sweep'
|
|
327
|
+
});
|
|
328
|
+
if (pollResolution.kind === 'ready') {
|
|
329
|
+
await providerIssueHandoff.poll({
|
|
330
|
+
trackedIssues: pollResolution.trackedIssues,
|
|
331
|
+
refetchTrackedIssues,
|
|
332
|
+
allowPollFailClosed: true
|
|
333
|
+
});
|
|
334
|
+
lastSuccessfulFullRecoverySweepAtMs = Date.now();
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
if (pollResolution.reason === 'dispatch_source_provider_rate_limited') {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
noteProviderPollingRequest(providerIssueHandoff, {
|
|
341
|
+
mode: 'refresh',
|
|
342
|
+
queued: getProviderIssueHandoffOperationState(providerIssueHandoff).queuedRefresh !== null,
|
|
343
|
+
replaceQueued: true
|
|
344
|
+
});
|
|
345
|
+
await providerIssueHandoff.refresh();
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
await providerIssueHandoff.poll({
|
|
349
|
+
trackedIssues: [],
|
|
350
|
+
refetchTrackedIssues,
|
|
351
|
+
deferFreshDiscovery: true
|
|
352
|
+
});
|
|
353
|
+
return;
|
|
354
|
+
}, undefined, {
|
|
355
|
+
mode: providerIssueHandoff.poll && context.readFeatureToggles ? 'poll' : 'refresh'
|
|
356
|
+
});
|
|
357
|
+
await waitForProviderIssueHandoffPendingWithWatchdog(providerIssueHandoff, operation, resolveWatchdogDelayMs);
|
|
358
|
+
}
|
|
359
|
+
catch {
|
|
360
|
+
// Best-effort provider refreshes should not crash the public lifecycle.
|
|
361
|
+
}
|
|
362
|
+
await waitForProviderIssueHandoffQueueToDrain(providerIssueHandoff, resolveWatchdogDelayMs);
|
|
363
|
+
if (stopped || generation !== rescheduleGeneration) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
await scheduleNextTriggerAsync();
|
|
367
|
+
};
|
|
368
|
+
return {
|
|
369
|
+
timer: {
|
|
370
|
+
cancel: () => {
|
|
371
|
+
stopped = true;
|
|
372
|
+
rescheduleGeneration += 1;
|
|
373
|
+
clearScheduledTrigger();
|
|
374
|
+
}
|
|
375
|
+
},
|
|
376
|
+
trigger
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
function scheduleStartupProviderRefresh(trigger) {
|
|
380
|
+
const startupTrigger = setTimeout(() => {
|
|
381
|
+
void trigger();
|
|
382
|
+
}, 0);
|
|
383
|
+
startupTrigger.unref?.();
|
|
384
|
+
return startupTrigger;
|
|
385
|
+
}
|
|
386
|
+
function resolveProviderIssueHandoffWatchdogDelayMs(providerIssueHandoff) {
|
|
387
|
+
const health = readProviderPollingHealth(providerIssueHandoff);
|
|
388
|
+
if (!health?.checking || health.operation_elapsed_ms === null) {
|
|
389
|
+
return PROVIDER_REFRESH_STUCK_AFTER_MS;
|
|
390
|
+
}
|
|
391
|
+
return Math.max(0, PROVIDER_REFRESH_STUCK_AFTER_MS - health.operation_elapsed_ms);
|
|
392
|
+
}
|
|
393
|
+
async function resolveProviderPollTrackedIssues(context, input) {
|
|
394
|
+
if (!context.readFeatureToggles) {
|
|
395
|
+
return {
|
|
396
|
+
kind: 'skip',
|
|
397
|
+
reason: 'dispatch_source_unavailable'
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
const sourceSetup = resolveLinearWebhookSourceSetup(context.readFeatureToggles(), process.env);
|
|
401
|
+
if ('error' in sourceSetup) {
|
|
402
|
+
return {
|
|
403
|
+
kind: 'skip',
|
|
404
|
+
reason: sourceSetup.error
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
const resolution = await resolveLiveLinearTrackedIssues({
|
|
408
|
+
sourceSetup: sourceSetup.sourceSetup,
|
|
409
|
+
env: process.env,
|
|
410
|
+
queryMode: input?.mode,
|
|
411
|
+
eligibleIssueTargetCount: input?.eligibleTargetCount,
|
|
412
|
+
eligibleStateSlotCounts: input?.eligibleStateSlotCounts,
|
|
413
|
+
excludedIssueIds: input?.excludedIssueIds
|
|
414
|
+
});
|
|
415
|
+
if (resolution.kind !== 'ready') {
|
|
416
|
+
return {
|
|
417
|
+
kind: 'skip',
|
|
418
|
+
reason: resolution.reason
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
kind: 'ready',
|
|
423
|
+
trackedIssues: dedupeProviderPollTrackedIssues(resolution.tracked_issues)
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
function dedupeProviderPollTrackedIssues(trackedIssues) {
|
|
427
|
+
const seenIssueIds = new Set();
|
|
428
|
+
const deduped = [];
|
|
429
|
+
for (const trackedIssue of trackedIssues) {
|
|
430
|
+
if (seenIssueIds.has(trackedIssue.id)) {
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
seenIssueIds.add(trackedIssue.id);
|
|
434
|
+
deduped.push(trackedIssue);
|
|
435
|
+
}
|
|
436
|
+
return deduped;
|
|
437
|
+
}
|
|
438
|
+
function pickLatestTimestamp(currentIso, candidateIso) {
|
|
439
|
+
const currentMs = Date.parse(currentIso ?? '');
|
|
440
|
+
const candidateMs = Date.parse(candidateIso);
|
|
441
|
+
if (!Number.isFinite(currentMs)) {
|
|
442
|
+
return candidateIso;
|
|
443
|
+
}
|
|
444
|
+
if (!Number.isFinite(candidateMs)) {
|
|
445
|
+
return currentIso ?? candidateIso;
|
|
446
|
+
}
|
|
447
|
+
return candidateMs >= currentMs ? candidateIso : currentIso ?? candidateIso;
|
|
448
|
+
}
|
|
449
|
+
function runProviderIssueHandoffOperation(providerIssueHandoff, operation, options, healthContext) {
|
|
450
|
+
const state = getProviderIssueHandoffOperationState(providerIssueHandoff);
|
|
451
|
+
if (state.active) {
|
|
452
|
+
const continueWhileBusy = () => {
|
|
453
|
+
if (!options?.queueIfBusy) {
|
|
454
|
+
return waitForProviderIssueHandoffPending(providerIssueHandoff, state.active);
|
|
455
|
+
}
|
|
456
|
+
if (healthContext) {
|
|
457
|
+
noteProviderPollingRequest(providerIssueHandoff, {
|
|
458
|
+
mode: healthContext.mode,
|
|
459
|
+
queued: true,
|
|
460
|
+
preserveActiveMode: true
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
return queueProviderIssueHandoffRefresh(providerIssueHandoff, state, operation, healthContext);
|
|
464
|
+
};
|
|
465
|
+
if (isProviderPollingStuck(providerIssueHandoff)) {
|
|
466
|
+
return (async () => {
|
|
467
|
+
const stuckOutcome = await resolveProviderIssueHandoffStuckOutcome(providerIssueHandoff);
|
|
468
|
+
if (!stuckOutcome) {
|
|
469
|
+
return continueWhileBusy();
|
|
470
|
+
}
|
|
471
|
+
if (healthContext) {
|
|
472
|
+
noteProviderPollingRequest(providerIssueHandoff, {
|
|
473
|
+
mode: healthContext.mode,
|
|
474
|
+
queued: state.queuedRefresh !== null,
|
|
475
|
+
replaceQueued: true,
|
|
476
|
+
preserveActiveMode: true
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
throw new Error(stuckOutcome?.reason ?? 'provider_refresh_lifecycle_stuck');
|
|
480
|
+
})();
|
|
481
|
+
}
|
|
482
|
+
return continueWhileBusy();
|
|
483
|
+
}
|
|
484
|
+
const idleStuckError = buildProviderIssueHandoffRestartRequiredError(providerIssueHandoff);
|
|
485
|
+
if (idleStuckError) {
|
|
486
|
+
clearProviderIssueHandoffOperationState(providerIssueHandoff, state);
|
|
487
|
+
return Promise.reject(idleStuckError);
|
|
488
|
+
}
|
|
489
|
+
return waitForProviderIssueHandoffPending(providerIssueHandoff, startProviderIssueHandoffOperation(providerIssueHandoff, state, operation, healthContext));
|
|
490
|
+
}
|
|
491
|
+
function buildProviderIssueHandoffRestartRequiredError(providerIssueHandoff) {
|
|
492
|
+
if (!isProviderPollingStuck(providerIssueHandoff)) {
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
return new Error(readProviderPollingHealth(providerIssueHandoff)?.reason ?? 'provider_refresh_lifecycle_stuck');
|
|
496
|
+
}
|
|
497
|
+
function startProviderIssueHandoffOperation(providerIssueHandoff, state, operation, healthContext) {
|
|
498
|
+
resetProviderIssueHandoffStuckSignal(state);
|
|
499
|
+
if (healthContext) {
|
|
500
|
+
markProviderPollingStarted(providerIssueHandoff, {
|
|
501
|
+
mode: healthContext.mode
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
let operationResult;
|
|
505
|
+
try {
|
|
506
|
+
operationResult = operation();
|
|
507
|
+
}
|
|
508
|
+
catch (error) {
|
|
509
|
+
operationResult = Promise.reject(error);
|
|
510
|
+
}
|
|
511
|
+
const operationPromise = operationResult
|
|
512
|
+
.then((value) => {
|
|
513
|
+
if (healthContext && state.active === operationPromise) {
|
|
514
|
+
markProviderPollingCompleted(providerIssueHandoff);
|
|
515
|
+
}
|
|
516
|
+
return value;
|
|
517
|
+
}, (error) => {
|
|
518
|
+
if (healthContext && state.active === operationPromise) {
|
|
519
|
+
markProviderPollingCompleted(providerIssueHandoff, {
|
|
520
|
+
error
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
throw error;
|
|
524
|
+
})
|
|
525
|
+
.finally(() => {
|
|
526
|
+
if (state.active === operationPromise) {
|
|
527
|
+
state.active = null;
|
|
528
|
+
clearProviderIssueHandoffOperationState(providerIssueHandoff, state);
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
state.active = operationPromise;
|
|
532
|
+
return operationPromise;
|
|
533
|
+
}
|
|
534
|
+
function queueProviderIssueHandoffRefresh(providerIssueHandoff, state, operation, healthContext) {
|
|
535
|
+
if (state.queuedRefresh) {
|
|
536
|
+
return state.queuedRefresh;
|
|
537
|
+
}
|
|
538
|
+
const queuedRefresh = waitForProviderIssueHandoffPending(providerIssueHandoff, state.active)
|
|
539
|
+
.catch(async (error) => {
|
|
540
|
+
const stuckReason = resolveProviderIssueHandoffStuckErrorReason(error);
|
|
541
|
+
if (stuckReason) {
|
|
542
|
+
throw new Error(stuckReason);
|
|
543
|
+
}
|
|
544
|
+
if (await resolveProviderIssueHandoffStuckOutcome(providerIssueHandoff)) {
|
|
545
|
+
throw new Error(readProviderPollingHealth(providerIssueHandoff)?.reason ??
|
|
546
|
+
'provider_refresh_lifecycle_stuck');
|
|
547
|
+
}
|
|
548
|
+
})
|
|
549
|
+
.then(async () => {
|
|
550
|
+
if (await resolveProviderIssueHandoffStuckOutcome(providerIssueHandoff)) {
|
|
551
|
+
throw new Error(readProviderPollingHealth(providerIssueHandoff)?.reason ??
|
|
552
|
+
'provider_refresh_lifecycle_stuck');
|
|
553
|
+
}
|
|
554
|
+
if (state.queuedRefresh !== queuedRefresh) {
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
state.queuedRefresh = null;
|
|
558
|
+
if (state.active) {
|
|
559
|
+
return queueProviderIssueHandoffRefresh(providerIssueHandoff, state, operation, healthContext);
|
|
560
|
+
}
|
|
561
|
+
return waitForProviderIssueHandoffPending(providerIssueHandoff, startProviderIssueHandoffOperation(providerIssueHandoff, state, operation, healthContext));
|
|
562
|
+
})
|
|
563
|
+
.finally(() => {
|
|
564
|
+
if (state.queuedRefresh === queuedRefresh) {
|
|
565
|
+
state.queuedRefresh = null;
|
|
566
|
+
}
|
|
567
|
+
clearProviderIssueHandoffOperationState(providerIssueHandoff, state);
|
|
568
|
+
});
|
|
569
|
+
state.queuedRefresh = queuedRefresh;
|
|
570
|
+
return queuedRefresh;
|
|
571
|
+
}
|
|
572
|
+
function detachProviderIssueHandoffPending(pending) {
|
|
573
|
+
if (!pending) {
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
void pending.catch(() => undefined);
|
|
577
|
+
}
|
|
578
|
+
async function waitForProviderIssueHandoffQueueToDrain(providerIssueHandoff, resolveWatchdogDelayMs, options = {}) {
|
|
579
|
+
for (;;) {
|
|
580
|
+
const state = getProviderIssueHandoffOperationState(providerIssueHandoff);
|
|
581
|
+
const pending = state.active ?? state.queuedRefresh;
|
|
582
|
+
if (!pending) {
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
if (await resolveProviderIssueHandoffStuckOutcome(providerIssueHandoff)) {
|
|
586
|
+
if (options.settleStuckPending) {
|
|
587
|
+
await settleProviderIssueHandoffStuckPending(providerIssueHandoff, state, pending, options);
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
try {
|
|
593
|
+
await waitForProviderIssueHandoffPendingWithWatchdog(providerIssueHandoff, pending, resolveWatchdogDelayMs);
|
|
594
|
+
}
|
|
595
|
+
catch {
|
|
596
|
+
if (await resolveProviderIssueHandoffStuckOutcome(providerIssueHandoff)) {
|
|
597
|
+
if (options.settleStuckPending) {
|
|
598
|
+
await settleProviderIssueHandoffStuckPending(providerIssueHandoff, state, pending, options);
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
async function settleProviderIssueHandoffStuckPending(providerIssueHandoff, state, pending, options) {
|
|
607
|
+
let timeout = null;
|
|
608
|
+
const graceMs = Math.max(0, options.stuckPendingGraceMs ?? PROVIDER_CLOSE_STUCK_DRAIN_GRACE_MS);
|
|
609
|
+
const outcome = await Promise.race([
|
|
610
|
+
pending.then(() => 'settled', () => 'settled'),
|
|
611
|
+
new Promise((resolve) => {
|
|
612
|
+
timeout = setTimeout(() => resolve('timed_out'), graceMs);
|
|
613
|
+
timeout.unref?.();
|
|
614
|
+
})
|
|
615
|
+
]);
|
|
616
|
+
if (timeout) {
|
|
617
|
+
clearTimeout(timeout);
|
|
618
|
+
}
|
|
619
|
+
if (outcome === 'settled') {
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
detachProviderIssueHandoffPending(pending);
|
|
623
|
+
providerIssueHandoff.resetStuckRefreshLifecycle?.();
|
|
624
|
+
if (state.active === pending) {
|
|
625
|
+
state.active = null;
|
|
626
|
+
}
|
|
627
|
+
if (state.queuedRefresh === pending) {
|
|
628
|
+
state.queuedRefresh = null;
|
|
629
|
+
}
|
|
630
|
+
clearProviderIssueHandoffOperationState(providerIssueHandoff, state);
|
|
631
|
+
}
|
|
632
|
+
function getProviderIssueHandoffOperationState(providerIssueHandoff) {
|
|
633
|
+
const existingState = providerIssueHandoffOperations.get(providerIssueHandoff);
|
|
634
|
+
if (existingState) {
|
|
635
|
+
return existingState;
|
|
636
|
+
}
|
|
637
|
+
const nextState = {
|
|
638
|
+
active: null,
|
|
639
|
+
queuedRefresh: null,
|
|
640
|
+
...createProviderIssueHandoffStuckSignal()
|
|
641
|
+
};
|
|
642
|
+
providerIssueHandoffOperations.set(providerIssueHandoff, nextState);
|
|
643
|
+
return nextState;
|
|
644
|
+
}
|
|
645
|
+
function clearProviderIssueHandoffOperationState(providerIssueHandoff, state) {
|
|
646
|
+
if (!state.active && !state.queuedRefresh) {
|
|
647
|
+
providerIssueHandoffOperations.delete(providerIssueHandoff);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
async function resolveProviderIssueHandoffStuckOutcome(providerIssueHandoff) {
|
|
651
|
+
if (!isProviderPollingStuck(providerIssueHandoff)) {
|
|
652
|
+
return null;
|
|
653
|
+
}
|
|
654
|
+
await markProviderPollingStuck(providerIssueHandoff);
|
|
655
|
+
const outcome = {
|
|
656
|
+
queued: true,
|
|
657
|
+
coalesced: true,
|
|
658
|
+
stuck: true,
|
|
659
|
+
restart_required: true,
|
|
660
|
+
reason: readProviderPollingHealth(providerIssueHandoff)?.reason ?? 'provider_refresh_lifecycle_stuck'
|
|
661
|
+
};
|
|
662
|
+
signalProviderIssueHandoffStuck(providerIssueHandoff, outcome);
|
|
663
|
+
return outcome;
|
|
664
|
+
}
|
|
665
|
+
function mapProviderIssueHandoffRefreshOutcome(providerIssueHandoff, pending, successOutcome) {
|
|
666
|
+
return pending.then(() => successOutcome, async (error) => {
|
|
667
|
+
const stuckOutcome = await resolveProviderIssueHandoffStuckOutcome(providerIssueHandoff);
|
|
668
|
+
if (stuckOutcome) {
|
|
669
|
+
return stuckOutcome;
|
|
670
|
+
}
|
|
671
|
+
const stuckReason = resolveProviderIssueHandoffStuckErrorReason(error);
|
|
672
|
+
if (stuckReason) {
|
|
673
|
+
return {
|
|
674
|
+
queued: true,
|
|
675
|
+
coalesced: true,
|
|
676
|
+
stuck: true,
|
|
677
|
+
restart_required: true,
|
|
678
|
+
reason: stuckReason
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
throw error;
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
function resolveProviderIssueHandoffStuckErrorReason(error) {
|
|
685
|
+
if (!(error instanceof Error)) {
|
|
686
|
+
return null;
|
|
687
|
+
}
|
|
688
|
+
if (error.name === 'ProviderRefreshLifecycleStuckError' ||
|
|
689
|
+
error.message === 'provider_refresh_lifecycle_stuck') {
|
|
690
|
+
return 'provider_refresh_lifecycle_stuck';
|
|
691
|
+
}
|
|
692
|
+
if (error.name === 'ProviderPollLifecycleStuckError' ||
|
|
693
|
+
error.message === 'provider_poll_lifecycle_stuck') {
|
|
694
|
+
return 'provider_poll_lifecycle_stuck';
|
|
695
|
+
}
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
function acknowledgeProviderIssueHandoffAccepted(pending, successOutcome) {
|
|
699
|
+
// Public refresh routes return as soon as the lifecycle accepts the work, so
|
|
700
|
+
// keep the detached refresh promise from surfacing later failures as
|
|
701
|
+
// unhandled rejections.
|
|
702
|
+
void pending.catch(() => undefined);
|
|
703
|
+
return Promise.resolve(successOutcome);
|
|
704
|
+
}
|
|
705
|
+
function waitForProviderIssueHandoffPending(providerIssueHandoff, pending) {
|
|
706
|
+
const state = getProviderIssueHandoffOperationState(providerIssueHandoff);
|
|
707
|
+
return Promise.race([
|
|
708
|
+
pending,
|
|
709
|
+
state.stuckSignal.then((outcome) => {
|
|
710
|
+
throw new Error(outcome.reason ?? 'provider_refresh_lifecycle_stuck');
|
|
711
|
+
})
|
|
712
|
+
]);
|
|
713
|
+
}
|
|
714
|
+
async function waitForProviderIssueHandoffPendingWithWatchdog(providerIssueHandoff, pending, resolveWatchdogDelayMs) {
|
|
715
|
+
let stuckWatchdog = null;
|
|
716
|
+
try {
|
|
717
|
+
await Promise.race([
|
|
718
|
+
waitForProviderIssueHandoffPending(providerIssueHandoff, pending),
|
|
719
|
+
new Promise((resolve) => {
|
|
720
|
+
const watchdogDelayMs = resolveWatchdogDelayMs();
|
|
721
|
+
stuckWatchdog = setTimeout(() => {
|
|
722
|
+
void resolveProviderIssueHandoffStuckOutcome(providerIssueHandoff).finally(resolve);
|
|
723
|
+
}, watchdogDelayMs);
|
|
724
|
+
stuckWatchdog.unref?.();
|
|
725
|
+
})
|
|
726
|
+
]);
|
|
727
|
+
}
|
|
728
|
+
finally {
|
|
729
|
+
if (stuckWatchdog) {
|
|
730
|
+
clearTimeout(stuckWatchdog);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
function createProviderIssueHandoffStuckSignal() {
|
|
735
|
+
let resolveStuckSignal = null;
|
|
736
|
+
const stuckSignal = new Promise((resolve) => {
|
|
737
|
+
resolveStuckSignal = resolve;
|
|
738
|
+
});
|
|
739
|
+
return {
|
|
740
|
+
stuckSignal,
|
|
741
|
+
resolveStuckSignal
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
function resetProviderIssueHandoffStuckSignal(state) {
|
|
745
|
+
const nextSignal = createProviderIssueHandoffStuckSignal();
|
|
746
|
+
state.stuckSignal = nextSignal.stuckSignal;
|
|
747
|
+
state.resolveStuckSignal = nextSignal.resolveStuckSignal;
|
|
748
|
+
}
|
|
749
|
+
function signalProviderIssueHandoffStuck(providerIssueHandoff, outcome) {
|
|
750
|
+
const state = providerIssueHandoffOperations.get(providerIssueHandoff);
|
|
751
|
+
if (!state?.resolveStuckSignal) {
|
|
752
|
+
return;
|
|
753
|
+
}
|
|
754
|
+
state.resolveStuckSignal(outcome);
|
|
755
|
+
state.resolveStuckSignal = null;
|
|
756
|
+
}
|