@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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
3
3
|
import { realpathSync } from 'node:fs';
|
|
4
4
|
import { access, chmod, readFile, readdir, stat } from 'node:fs/promises';
|
|
5
5
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
@@ -7,9 +7,12 @@ import process from 'node:process';
|
|
|
7
7
|
import { loadDelegationConfigFiles, computeEffectiveDelegationConfig, parseDelegationConfigOverride, splitDelegationConfigOverrides } from './config/delegationConfig.js';
|
|
8
8
|
import { logger } from '../logger.js';
|
|
9
9
|
import { writeJsonAtomic } from './utils/fs.js';
|
|
10
|
+
import { parseTrailingJsonObject } from './utils/trailingJsonObject.js';
|
|
11
|
+
import { evaluateDynamicToolBridgeRequest } from './control/dynamicToolBridgePolicy.js';
|
|
12
|
+
import { buildToolList, createDelegationServerRpcHandler, handleDelegationServerToolCall } from './delegationServerToolDispatchShell.js';
|
|
13
|
+
import { applyDelegationQuestionFallback, clampQuestionPollWaitMs, handleDelegationServerQuestionEnqueue, handleDelegationServerQuestionPoll, MAX_QUESTION_POLL_WAIT_MS, QUESTION_POLL_INTERVAL_MS, resolveDelegationTokenValue } from './delegationServerQuestionFlowShell.js';
|
|
14
|
+
import { MAX_MCP_HEADER_BYTES, MAX_MCP_MESSAGE_BYTES, parseContentLengthHeader, runJsonRpcServer } from './delegationServerTransport.js';
|
|
10
15
|
const PROTOCOL_VERSION = '2024-11-05';
|
|
11
|
-
const QUESTION_POLL_INTERVAL_MS = 500;
|
|
12
|
-
const MAX_QUESTION_POLL_WAIT_MS = 10_000;
|
|
13
16
|
const DEFAULT_SPAWN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
14
17
|
const DEFAULT_SPAWN_START_TIMEOUT_MS = 10_000;
|
|
15
18
|
const DEFAULT_SPAWN_START_POLL_INTERVAL_MS = 200;
|
|
@@ -17,17 +20,30 @@ const DEFAULT_GH_TIMEOUT_MS = 60_000;
|
|
|
17
20
|
const DEFAULT_DELEGATION_TOKEN_RETRY_MS = 2000;
|
|
18
21
|
const DEFAULT_DELEGATION_TOKEN_RETRY_INTERVAL_MS = 200;
|
|
19
22
|
const DEFAULT_CONTROL_ENDPOINT_TIMEOUT_MS = 15_000;
|
|
20
|
-
const
|
|
21
|
-
const MAX_MCP_HEADER_BYTES = 16 * 1024;
|
|
22
|
-
const MCP_HEADER_DELIMITER = '\r\n\r\n';
|
|
23
|
-
const MCP_HEADER_DELIMITER_BYTES = MCP_HEADER_DELIMITER.length;
|
|
24
|
-
const MCP_HEADER_DELIMITER_BUFFER = Buffer.from(MCP_HEADER_DELIMITER, 'utf8');
|
|
25
|
-
const MAX_MCP_BUFFER_BYTES = (MAX_MCP_MESSAGE_BYTES + MAX_MCP_HEADER_BYTES + MCP_HEADER_DELIMITER_BYTES) * 2;
|
|
23
|
+
const DEFAULT_DELEGATION_IDLE_TIMEOUT_MS = 10 * 60 * 1000;
|
|
26
24
|
const DELEGATION_TOKEN_HEADER = 'x-codex-delegation-token';
|
|
27
25
|
const DELEGATION_RUN_HEADER = 'x-codex-delegation-run-id';
|
|
28
26
|
const DELEGATION_TOKEN_FILE = 'delegation_token.json';
|
|
29
27
|
const CSRF_HEADER = 'x-csrf-token';
|
|
30
28
|
const LOOPBACK_HOSTS = new Set(['127.0.0.1', 'localhost', '::1']);
|
|
29
|
+
const DYNAMIC_TOOL_BRIDGE_PUBLIC_TOKEN_KEYS = [
|
|
30
|
+
'dynamic_tool_bridge_token',
|
|
31
|
+
'dynamicToolBridgeToken',
|
|
32
|
+
'bridge_token',
|
|
33
|
+
'bridgeToken'
|
|
34
|
+
];
|
|
35
|
+
const DYNAMIC_TOOL_BRIDGE_PUBLIC_ATTESTATION_KEYS = [
|
|
36
|
+
'dynamic_tool_bridge_attestation',
|
|
37
|
+
'dynamicToolBridgeAttestation',
|
|
38
|
+
'bridge_attestation',
|
|
39
|
+
'bridgeAttestation'
|
|
40
|
+
];
|
|
41
|
+
const DYNAMIC_TOOL_BRIDGE_PRIVATE_ATTESTATION_KEYS = [
|
|
42
|
+
'dynamic_tool_bridge_attestation',
|
|
43
|
+
'dynamicToolBridgeAttestation',
|
|
44
|
+
'bridge_attestation',
|
|
45
|
+
'bridgeAttestation'
|
|
46
|
+
];
|
|
31
47
|
const CONFIG_OVERRIDE_ENV_KEYS = ['CODEX_CONFIG_OVERRIDES', 'CODEX_MCP_CONFIG_OVERRIDES'];
|
|
32
48
|
const CONFIRMATION_ERROR_CODES = new Set([
|
|
33
49
|
'confirmation_required',
|
|
@@ -56,226 +72,52 @@ export async function startDelegationServer(options) {
|
|
|
56
72
|
const allowedHosts = effectiveConfig.ui.allowedBindHosts;
|
|
57
73
|
const toolProfile = effectiveConfig.delegate.toolProfile;
|
|
58
74
|
const tools = buildToolList({ mode, githubEnabled, allowedGithubOps });
|
|
59
|
-
const handler =
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
allowedHosts,
|
|
78
|
-
toolProfile,
|
|
79
|
-
expiryFallback: effectiveConfig.delegate.expiryFallback
|
|
80
|
-
});
|
|
81
|
-
default:
|
|
82
|
-
throw new Error(`Unsupported method: ${request.method}`);
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
await runJsonRpcServer(handler);
|
|
86
|
-
}
|
|
87
|
-
function buildToolList(options) {
|
|
88
|
-
const tools = [];
|
|
89
|
-
const includeFull = options.mode !== 'question_only';
|
|
90
|
-
if (includeFull) {
|
|
91
|
-
tools.push(toolDefinition('delegate.spawn', 'Spawn a delegated run', {
|
|
92
|
-
type: 'object',
|
|
93
|
-
properties: {
|
|
94
|
-
task_id: { type: 'string' },
|
|
95
|
-
pipeline: { type: 'string' },
|
|
96
|
-
repo: { type: 'string' },
|
|
97
|
-
parent_run_id: { type: 'string' },
|
|
98
|
-
parent_manifest_path: { type: 'string' },
|
|
99
|
-
env: { type: 'object', additionalProperties: { type: 'string' } },
|
|
100
|
-
delegate_mode: { type: 'string', enum: ['full', 'question_only'] },
|
|
101
|
-
start_only: { type: 'boolean' }
|
|
102
|
-
},
|
|
103
|
-
required: ['pipeline', 'repo']
|
|
104
|
-
}));
|
|
105
|
-
tools.push(toolDefinition('delegate.pause', 'Pause or resume a run', {
|
|
106
|
-
type: 'object',
|
|
107
|
-
properties: {
|
|
108
|
-
manifest_path: { type: 'string' },
|
|
109
|
-
paused: { type: 'boolean' }
|
|
110
|
-
},
|
|
111
|
-
required: ['manifest_path', 'paused']
|
|
112
|
-
}));
|
|
113
|
-
tools.push(toolDefinition('delegate.cancel', 'Cancel a run (confirmation required)', {
|
|
114
|
-
type: 'object',
|
|
115
|
-
properties: {
|
|
116
|
-
manifest_path: { type: 'string' }
|
|
117
|
-
},
|
|
118
|
-
required: ['manifest_path']
|
|
119
|
-
}));
|
|
120
|
-
}
|
|
121
|
-
tools.push(toolDefinition('delegate.status', 'Fetch run status', {
|
|
122
|
-
type: 'object',
|
|
123
|
-
properties: {
|
|
124
|
-
manifest_path: { type: 'string' }
|
|
125
|
-
},
|
|
126
|
-
required: ['manifest_path']
|
|
127
|
-
}));
|
|
128
|
-
tools.push(toolDefinition('delegate.question.enqueue', 'Enqueue a question to the parent run', {
|
|
129
|
-
type: 'object',
|
|
130
|
-
properties: {
|
|
131
|
-
parent_manifest_path: { type: 'string' },
|
|
132
|
-
parent_run_id: { type: 'string' },
|
|
133
|
-
parent_task_id: { type: 'string' },
|
|
134
|
-
from_manifest_path: { type: 'string' },
|
|
135
|
-
prompt: { type: 'string' },
|
|
136
|
-
urgency: { type: 'string', enum: ['low', 'med', 'high'] },
|
|
137
|
-
expires_in_ms: { type: 'number' },
|
|
138
|
-
auto_pause: { type: 'boolean' }
|
|
139
|
-
},
|
|
140
|
-
required: ['parent_manifest_path', 'prompt']
|
|
141
|
-
}));
|
|
142
|
-
tools.push(toolDefinition('delegate.question.poll', 'Poll for a question answer', {
|
|
143
|
-
type: 'object',
|
|
144
|
-
properties: {
|
|
145
|
-
parent_manifest_path: { type: 'string' },
|
|
146
|
-
question_id: { type: 'string' },
|
|
147
|
-
wait_ms: { type: 'number' }
|
|
148
|
-
},
|
|
149
|
-
required: ['parent_manifest_path', 'question_id']
|
|
150
|
-
}));
|
|
151
|
-
if (options.githubEnabled) {
|
|
152
|
-
if (options.allowedGithubOps.has('open_pr')) {
|
|
153
|
-
tools.push(toolDefinition('github.open_pr', 'Open a pull request', {
|
|
154
|
-
type: 'object',
|
|
155
|
-
properties: {
|
|
156
|
-
repo: { type: 'string' },
|
|
157
|
-
title: { type: 'string' },
|
|
158
|
-
body: { type: 'string' },
|
|
159
|
-
base: { type: 'string' },
|
|
160
|
-
head: { type: 'string' },
|
|
161
|
-
draft: { type: 'boolean' }
|
|
162
|
-
},
|
|
163
|
-
required: ['title']
|
|
164
|
-
}));
|
|
165
|
-
}
|
|
166
|
-
if (options.allowedGithubOps.has('comment')) {
|
|
167
|
-
tools.push(toolDefinition('github.comment', 'Create a PR/issue comment', {
|
|
168
|
-
type: 'object',
|
|
169
|
-
properties: {
|
|
170
|
-
repo: { type: 'string' },
|
|
171
|
-
issue_number: { type: 'number' },
|
|
172
|
-
body: { type: 'string' }
|
|
173
|
-
},
|
|
174
|
-
required: ['issue_number', 'body']
|
|
175
|
-
}));
|
|
176
|
-
}
|
|
177
|
-
if (options.allowedGithubOps.has('review')) {
|
|
178
|
-
tools.push(toolDefinition('github.review', 'Submit a PR review', {
|
|
179
|
-
type: 'object',
|
|
180
|
-
properties: {
|
|
181
|
-
repo: { type: 'string' },
|
|
182
|
-
pull_number: { type: 'number' },
|
|
183
|
-
event: { type: 'string', enum: ['APPROVE', 'REQUEST_CHANGES', 'COMMENT'] },
|
|
184
|
-
body: { type: 'string' }
|
|
185
|
-
},
|
|
186
|
-
required: ['pull_number', 'event']
|
|
187
|
-
}));
|
|
188
|
-
}
|
|
189
|
-
if (options.allowedGithubOps.has('get_checks')) {
|
|
190
|
-
tools.push(toolDefinition('github.get_checks', 'Fetch PR checks', {
|
|
191
|
-
type: 'object',
|
|
192
|
-
properties: {
|
|
193
|
-
repo: { type: 'string' },
|
|
194
|
-
pull_number: { type: 'number' }
|
|
195
|
-
},
|
|
196
|
-
required: ['pull_number']
|
|
197
|
-
}));
|
|
198
|
-
}
|
|
199
|
-
if (options.allowedGithubOps.has('merge')) {
|
|
200
|
-
tools.push(toolDefinition('github.merge', 'Merge a PR', {
|
|
201
|
-
type: 'object',
|
|
202
|
-
properties: {
|
|
203
|
-
manifest_path: { type: 'string' },
|
|
204
|
-
repo: { type: 'string' },
|
|
205
|
-
pull_number: { type: 'number' },
|
|
206
|
-
method: { type: 'string', enum: ['merge', 'squash', 'rebase'] },
|
|
207
|
-
delete_branch: { type: 'boolean' }
|
|
208
|
-
},
|
|
209
|
-
required: ['pull_number']
|
|
210
|
-
}));
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
return tools;
|
|
214
|
-
}
|
|
215
|
-
function toolDefinition(name, description, inputSchema) {
|
|
216
|
-
return { name, description, inputSchema };
|
|
75
|
+
const handler = createDelegationServerRpcHandler({
|
|
76
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
77
|
+
tools,
|
|
78
|
+
handleToolCall: (request) => handleToolCall(request, {
|
|
79
|
+
repoRoot,
|
|
80
|
+
mode,
|
|
81
|
+
allowNested,
|
|
82
|
+
githubEnabled,
|
|
83
|
+
allowedGithubOps,
|
|
84
|
+
allowedRoots,
|
|
85
|
+
allowedHosts,
|
|
86
|
+
toolProfile,
|
|
87
|
+
expiryFallback: effectiveConfig.delegate.expiryFallback
|
|
88
|
+
})
|
|
89
|
+
});
|
|
90
|
+
await runJsonRpcServer(handler, {
|
|
91
|
+
idleTimeoutMs: resolveDelegationIdleTimeoutMs(process.env)
|
|
92
|
+
});
|
|
217
93
|
}
|
|
218
94
|
async function handleToolCall(request, context) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
await reportSecurityViolation('delegation_token_present', 'Model supplied delegation_token.', toolName, context.allowedHosts);
|
|
235
|
-
throw new Error('delegation_token must be injected by the runner');
|
|
236
|
-
}
|
|
237
|
-
switch (toolName) {
|
|
238
|
-
case 'delegate.status':
|
|
239
|
-
return wrapResult(await handleDelegateStatus(input, context.allowedRoots, context.allowedHosts));
|
|
240
|
-
case 'delegate.pause':
|
|
241
|
-
return wrapResult(await handleDelegatePause(input, context.allowedRoots, context.allowedHosts));
|
|
242
|
-
case 'delegate.cancel':
|
|
243
|
-
return wrapResult(await handleDelegateCancel(input, request, context.allowedRoots, context.allowedHosts));
|
|
244
|
-
case 'delegate.spawn':
|
|
245
|
-
return wrapResult(await handleDelegateSpawn(input, context.repoRoot, context.allowNested, context.allowedRoots, context.allowedHosts, context.toolProfile));
|
|
246
|
-
case 'delegate.question.enqueue':
|
|
247
|
-
return wrapResult(await handleQuestionEnqueue(input, request, context.allowedRoots, context.allowedHosts, context.expiryFallback));
|
|
248
|
-
case 'delegate.question.poll':
|
|
249
|
-
return wrapResult(await handleQuestionPoll(input, request, context.allowedRoots, context.allowedHosts, context.expiryFallback));
|
|
250
|
-
case 'github.open_pr':
|
|
251
|
-
case 'github.comment':
|
|
252
|
-
case 'github.review':
|
|
253
|
-
case 'github.get_checks':
|
|
254
|
-
case 'github.merge':
|
|
255
|
-
return wrapResult(await handleGithubCall(toolName, input, request, context));
|
|
256
|
-
default:
|
|
257
|
-
throw new Error(`Unknown tool: ${toolName}`);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
function wrapResult(payload) {
|
|
261
|
-
return {
|
|
262
|
-
content: [
|
|
263
|
-
{
|
|
264
|
-
type: 'text',
|
|
265
|
-
text: typeof payload === 'string' ? payload : JSON.stringify(payload, null, 2)
|
|
266
|
-
}
|
|
267
|
-
],
|
|
268
|
-
isError: false
|
|
269
|
-
};
|
|
95
|
+
return handleDelegationServerToolCall(request, context, {
|
|
96
|
+
asRecord,
|
|
97
|
+
readStringValue,
|
|
98
|
+
containsSecret,
|
|
99
|
+
reportSecurityViolation,
|
|
100
|
+
getDelegateModeViolationMessage,
|
|
101
|
+
handleDelegateStatus,
|
|
102
|
+
handleDelegatePause,
|
|
103
|
+
handleDelegateCancel,
|
|
104
|
+
handleDelegateSpawn,
|
|
105
|
+
handleQuestionEnqueue,
|
|
106
|
+
handleQuestionPoll,
|
|
107
|
+
handleCoordinatorDynamicToolCall,
|
|
108
|
+
handleGithubCall
|
|
109
|
+
});
|
|
270
110
|
}
|
|
271
111
|
async function handleDelegateStatus(input, allowedRoots, allowedHosts) {
|
|
272
112
|
const manifestPath = resolveManifestPath(readStringValue(input, 'manifest_path', 'manifestPath'), allowedRoots);
|
|
273
|
-
const
|
|
274
|
-
const manifest = JSON.parse(raw);
|
|
113
|
+
const manifest = await loadRunManifest(manifestPath);
|
|
275
114
|
const eventsPath = resolve(dirname(manifestPath), 'events.jsonl');
|
|
115
|
+
const intentId = readStringValue(input, 'intent_id', 'intentId') ?? null;
|
|
116
|
+
const requestId = readStringValue(input, 'request_id', 'requestId') ?? null;
|
|
276
117
|
if (!TERMINAL_RUN_STATUSES.has(manifest.status)) {
|
|
277
118
|
await assertControlEndpoint(manifestPath, allowedHosts);
|
|
278
119
|
}
|
|
120
|
+
const control = await loadControlSnapshot(manifestPath);
|
|
279
121
|
return {
|
|
280
122
|
run_id: manifest.run_id,
|
|
281
123
|
task_id: manifest.task_id,
|
|
@@ -283,47 +125,405 @@ async function handleDelegateStatus(input, allowedRoots, allowedHosts) {
|
|
|
283
125
|
status_detail: manifest.status_detail ?? null,
|
|
284
126
|
manifest_path: manifestPath,
|
|
285
127
|
events_path: eventsPath,
|
|
286
|
-
log_path: manifest.log_path ?? null
|
|
128
|
+
log_path: manifest.log_path ?? null,
|
|
129
|
+
control,
|
|
130
|
+
traceability: {
|
|
131
|
+
intent_id: intentId,
|
|
132
|
+
task_id: manifest.task_id ?? null,
|
|
133
|
+
run_id: manifest.run_id ?? null,
|
|
134
|
+
manifest_path: manifestPath,
|
|
135
|
+
request_id: requestId
|
|
136
|
+
}
|
|
287
137
|
};
|
|
288
138
|
}
|
|
289
139
|
async function handleDelegatePause(input, allowedRoots, allowedHosts) {
|
|
290
140
|
const manifestPath = resolveManifestPath(readStringValue(input, 'manifest_path', 'manifestPath'), allowedRoots);
|
|
141
|
+
const manifest = await loadRunManifest(manifestPath);
|
|
291
142
|
const paused = readBooleanValue(input, 'paused') ?? false;
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
143
|
+
const action = paused ? 'pause' : 'resume';
|
|
144
|
+
const intentId = readStringValue(input, 'intent_id', 'intentId') ?? null;
|
|
145
|
+
const requestId = readStringValue(input, 'request_id', 'requestId') ??
|
|
146
|
+
deriveIdempotentRequestId(action, intentId, manifestPath);
|
|
147
|
+
const transportMetadata = readTransportMutationMetadata(input);
|
|
148
|
+
const result = await callControlEndpoint(manifestPath, '/control/action', {
|
|
149
|
+
action,
|
|
150
|
+
requested_by: 'delegate',
|
|
151
|
+
intent_id: intentId,
|
|
152
|
+
request_id: requestId,
|
|
153
|
+
...(transportMetadata ?? {})
|
|
295
154
|
}, undefined, { allowedHosts });
|
|
155
|
+
const resultRecord = asRecord(result);
|
|
156
|
+
const latestAction = asRecord(resultRecord.latest_action);
|
|
157
|
+
const resultRequestId = readStringValue(resultRecord, 'request_id', 'requestId') ??
|
|
158
|
+
readStringValue(latestAction, 'request_id', 'requestId') ??
|
|
159
|
+
requestId;
|
|
160
|
+
const canonicalTraceability = readCanonicalTraceability(resultRecord);
|
|
161
|
+
return {
|
|
162
|
+
...resultRecord,
|
|
163
|
+
traceability: canonicalTraceability ??
|
|
164
|
+
{
|
|
165
|
+
intent_id: intentId,
|
|
166
|
+
task_id: manifest.task_id ?? null,
|
|
167
|
+
run_id: manifest.run_id ?? null,
|
|
168
|
+
manifest_path: manifestPath,
|
|
169
|
+
request_id: resultRequestId,
|
|
170
|
+
...(transportMetadata
|
|
171
|
+
? {
|
|
172
|
+
transport: transportMetadata.transport,
|
|
173
|
+
actor_id: transportMetadata.actor_id,
|
|
174
|
+
actor_source: transportMetadata.actor_source,
|
|
175
|
+
transport_principal: transportMetadata.transport_principal
|
|
176
|
+
}
|
|
177
|
+
: {})
|
|
178
|
+
}
|
|
179
|
+
};
|
|
296
180
|
}
|
|
297
181
|
async function handleDelegateCancel(input, request, allowedRoots, allowedHosts) {
|
|
298
182
|
const manifestPath = resolveManifestPath(readStringValue(input, 'manifest_path', 'manifestPath'), allowedRoots);
|
|
183
|
+
const manifest = await loadRunManifest(manifestPath);
|
|
184
|
+
const intentId = readStringValue(input, 'intent_id', 'intentId') ?? null;
|
|
185
|
+
const requestId = readStringValue(input, 'request_id', 'requestId') ??
|
|
186
|
+
deriveIdempotentRequestId('cancel', intentId, manifestPath);
|
|
187
|
+
const transportMetadata = readTransportMutationMetadata(input);
|
|
299
188
|
const privateNonce = request.codex_private?.confirm_nonce;
|
|
300
189
|
if (!privateNonce) {
|
|
301
|
-
|
|
190
|
+
const confirmation = await callControlEndpoint(manifestPath, '/confirmations/create', {
|
|
302
191
|
action: 'cancel',
|
|
303
192
|
tool: 'delegate.cancel',
|
|
304
|
-
params: {
|
|
193
|
+
params: {
|
|
194
|
+
manifest_path: manifestPath,
|
|
195
|
+
intent_id: intentId,
|
|
196
|
+
request_id: requestId,
|
|
197
|
+
...(transportMetadata
|
|
198
|
+
? {
|
|
199
|
+
transport: transportMetadata.transport,
|
|
200
|
+
actor_id: transportMetadata.actor_id,
|
|
201
|
+
actor_source: transportMetadata.actor_source,
|
|
202
|
+
transport_principal: transportMetadata.transport_principal
|
|
203
|
+
}
|
|
204
|
+
: {})
|
|
205
|
+
}
|
|
305
206
|
}, undefined, { allowedHosts });
|
|
207
|
+
const confirmationRequestId = readStringValue(confirmation, 'request_id', 'requestId') ?? requestId;
|
|
208
|
+
const confirmationRecord = asRecord(confirmation);
|
|
209
|
+
const canonicalTraceability = readCanonicalTraceability(confirmationRecord);
|
|
210
|
+
return {
|
|
211
|
+
...confirmationRecord,
|
|
212
|
+
traceability: canonicalTraceability ??
|
|
213
|
+
{
|
|
214
|
+
intent_id: intentId,
|
|
215
|
+
task_id: manifest.task_id ?? null,
|
|
216
|
+
run_id: manifest.run_id ?? null,
|
|
217
|
+
manifest_path: manifestPath,
|
|
218
|
+
request_id: confirmationRequestId,
|
|
219
|
+
...(transportMetadata
|
|
220
|
+
? {
|
|
221
|
+
transport: transportMetadata.transport,
|
|
222
|
+
actor_id: transportMetadata.actor_id,
|
|
223
|
+
actor_source: transportMetadata.actor_source,
|
|
224
|
+
transport_principal: transportMetadata.transport_principal
|
|
225
|
+
}
|
|
226
|
+
: {})
|
|
227
|
+
}
|
|
228
|
+
};
|
|
306
229
|
}
|
|
307
230
|
try {
|
|
308
|
-
|
|
231
|
+
const result = await callControlEndpoint(manifestPath, '/control/action', {
|
|
309
232
|
action: 'cancel',
|
|
310
233
|
requested_by: 'delegate',
|
|
234
|
+
intent_id: intentId,
|
|
235
|
+
request_id: requestId,
|
|
311
236
|
confirm_nonce: String(privateNonce),
|
|
312
237
|
tool: 'delegate.cancel',
|
|
313
|
-
params: {
|
|
238
|
+
params: {
|
|
239
|
+
manifest_path: manifestPath,
|
|
240
|
+
intent_id: intentId,
|
|
241
|
+
request_id: requestId,
|
|
242
|
+
...(transportMetadata
|
|
243
|
+
? {
|
|
244
|
+
transport: transportMetadata.transport,
|
|
245
|
+
actor_id: transportMetadata.actor_id,
|
|
246
|
+
actor_source: transportMetadata.actor_source,
|
|
247
|
+
transport_principal: transportMetadata.transport_principal
|
|
248
|
+
}
|
|
249
|
+
: {})
|
|
250
|
+
},
|
|
251
|
+
...(transportMetadata ?? {})
|
|
314
252
|
}, undefined, { allowedHosts });
|
|
253
|
+
const resultRecord = asRecord(result);
|
|
254
|
+
const latestAction = asRecord(resultRecord.latest_action);
|
|
255
|
+
const resultRequestId = readStringValue(resultRecord, 'request_id', 'requestId') ??
|
|
256
|
+
readStringValue(latestAction, 'request_id', 'requestId') ??
|
|
257
|
+
requestId;
|
|
258
|
+
const canonicalTraceability = readCanonicalTraceability(resultRecord);
|
|
259
|
+
return {
|
|
260
|
+
...resultRecord,
|
|
261
|
+
traceability: canonicalTraceability ??
|
|
262
|
+
{
|
|
263
|
+
intent_id: intentId,
|
|
264
|
+
task_id: manifest.task_id ?? null,
|
|
265
|
+
run_id: manifest.run_id ?? null,
|
|
266
|
+
manifest_path: manifestPath,
|
|
267
|
+
request_id: resultRequestId,
|
|
268
|
+
...(transportMetadata
|
|
269
|
+
? {
|
|
270
|
+
transport: transportMetadata.transport,
|
|
271
|
+
actor_id: transportMetadata.actor_id,
|
|
272
|
+
actor_source: transportMetadata.actor_source,
|
|
273
|
+
transport_principal: transportMetadata.transport_principal
|
|
274
|
+
}
|
|
275
|
+
: {})
|
|
276
|
+
}
|
|
277
|
+
};
|
|
315
278
|
}
|
|
316
279
|
catch (error) {
|
|
317
280
|
if (!isConfirmationError(error)) {
|
|
318
281
|
throw error;
|
|
319
282
|
}
|
|
320
|
-
|
|
283
|
+
const confirmation = await callControlEndpoint(manifestPath, '/confirmations/create', {
|
|
321
284
|
action: 'cancel',
|
|
322
285
|
tool: 'delegate.cancel',
|
|
323
|
-
params: {
|
|
286
|
+
params: {
|
|
287
|
+
manifest_path: manifestPath,
|
|
288
|
+
intent_id: intentId,
|
|
289
|
+
request_id: requestId,
|
|
290
|
+
...(transportMetadata
|
|
291
|
+
? {
|
|
292
|
+
transport: transportMetadata.transport,
|
|
293
|
+
actor_id: transportMetadata.actor_id,
|
|
294
|
+
actor_source: transportMetadata.actor_source,
|
|
295
|
+
transport_principal: transportMetadata.transport_principal
|
|
296
|
+
}
|
|
297
|
+
: {})
|
|
298
|
+
}
|
|
324
299
|
}, undefined, { allowedHosts });
|
|
300
|
+
const confirmationRequestId = readStringValue(confirmation, 'request_id', 'requestId') ?? requestId;
|
|
301
|
+
const confirmationRecord = asRecord(confirmation);
|
|
302
|
+
const canonicalTraceability = readCanonicalTraceability(confirmationRecord);
|
|
303
|
+
return {
|
|
304
|
+
...confirmationRecord,
|
|
305
|
+
traceability: canonicalTraceability ??
|
|
306
|
+
{
|
|
307
|
+
intent_id: intentId,
|
|
308
|
+
task_id: manifest.task_id ?? null,
|
|
309
|
+
run_id: manifest.run_id ?? null,
|
|
310
|
+
manifest_path: manifestPath,
|
|
311
|
+
request_id: confirmationRequestId,
|
|
312
|
+
...(transportMetadata
|
|
313
|
+
? {
|
|
314
|
+
transport: transportMetadata.transport,
|
|
315
|
+
actor_id: transportMetadata.actor_id,
|
|
316
|
+
actor_source: transportMetadata.actor_source,
|
|
317
|
+
transport_principal: transportMetadata.transport_principal
|
|
318
|
+
}
|
|
319
|
+
: {})
|
|
320
|
+
}
|
|
321
|
+
};
|
|
325
322
|
}
|
|
326
323
|
}
|
|
324
|
+
async function handleCoordinatorDynamicToolCall(toolName, input, request, allowedRoots, allowedHosts) {
|
|
325
|
+
if (containsAnySecret(input, DYNAMIC_TOOL_BRIDGE_PUBLIC_TOKEN_KEYS)) {
|
|
326
|
+
await reportSecurityViolation('dynamic_tool_bridge_token_present', 'Model supplied dynamic tool bridge token.', toolName, allowedHosts);
|
|
327
|
+
throw new Error('dynamic_tool_bridge_token_missing');
|
|
328
|
+
}
|
|
329
|
+
if (containsAnySecret(input, DYNAMIC_TOOL_BRIDGE_PUBLIC_ATTESTATION_KEYS)) {
|
|
330
|
+
await reportSecurityViolation('dynamic_tool_bridge_attestation_present', 'Model supplied dynamic tool bridge attestation.', toolName, allowedHosts);
|
|
331
|
+
throw new Error('dynamic_tool_bridge_attestation_missing');
|
|
332
|
+
}
|
|
333
|
+
const manifestPath = resolveManifestPath(readStringValue(input, 'manifest_path', 'manifestPath'), allowedRoots);
|
|
334
|
+
const controlSnapshot = await loadControlSnapshot(manifestPath);
|
|
335
|
+
const featureToggles = asRecord(controlSnapshot?.feature_toggles);
|
|
336
|
+
const bridgeAction = resolveCoordinatorDynamicToolAction(toolName);
|
|
337
|
+
const bridgeEvaluation = evaluateDynamicToolBridgeRequest({
|
|
338
|
+
featureToggles,
|
|
339
|
+
action: bridgeAction,
|
|
340
|
+
args: input
|
|
341
|
+
});
|
|
342
|
+
if (!bridgeEvaluation.ok) {
|
|
343
|
+
throw new Error(bridgeEvaluation.error ?? 'dynamic_tool_bridge_disabled');
|
|
344
|
+
}
|
|
345
|
+
verifyDynamicToolBridgeAttestation(request, featureToggles, bridgeEvaluation.sourceId ?? 'appserver_dynamic_tool');
|
|
346
|
+
let result;
|
|
347
|
+
if (bridgeAction === 'status') {
|
|
348
|
+
result = await handleDelegateStatus({
|
|
349
|
+
...input,
|
|
350
|
+
manifest_path: manifestPath
|
|
351
|
+
}, allowedRoots, allowedHosts);
|
|
352
|
+
}
|
|
353
|
+
else if (bridgeAction === 'pause' || bridgeAction === 'resume') {
|
|
354
|
+
result = await handleDelegatePause({
|
|
355
|
+
...input,
|
|
356
|
+
manifest_path: manifestPath,
|
|
357
|
+
paused: bridgeAction === 'pause'
|
|
358
|
+
}, allowedRoots, allowedHosts);
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
result = await handleDelegateCancel({
|
|
362
|
+
...input,
|
|
363
|
+
manifest_path: manifestPath
|
|
364
|
+
}, request, allowedRoots, allowedHosts);
|
|
365
|
+
}
|
|
366
|
+
return appendCoordinatorBridgeTraceability(result, {
|
|
367
|
+
action: bridgeAction,
|
|
368
|
+
sourceId: bridgeEvaluation.sourceId ?? 'appserver_dynamic_tool'
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
function resolveCoordinatorDynamicToolAction(toolName) {
|
|
372
|
+
switch (toolName) {
|
|
373
|
+
case 'coordinator.status':
|
|
374
|
+
return 'status';
|
|
375
|
+
case 'coordinator.pause':
|
|
376
|
+
return 'pause';
|
|
377
|
+
case 'coordinator.resume':
|
|
378
|
+
return 'resume';
|
|
379
|
+
case 'coordinator.cancel':
|
|
380
|
+
return 'cancel';
|
|
381
|
+
default:
|
|
382
|
+
throw new Error('dynamic_tool_bridge_action_not_allowed');
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
function appendCoordinatorBridgeTraceability(payload, input) {
|
|
386
|
+
const record = asRecord(payload);
|
|
387
|
+
const traceability = asRecord(record.traceability);
|
|
388
|
+
return {
|
|
389
|
+
...record,
|
|
390
|
+
traceability: {
|
|
391
|
+
...traceability,
|
|
392
|
+
bridge_surface: 'coordinator_dynamic_tool',
|
|
393
|
+
bridge_action: input.action,
|
|
394
|
+
bridge_source_id: input.sourceId,
|
|
395
|
+
bridge_token_validated: true
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
function verifyDynamicToolBridgeAttestation(request, featureToggles, sourceId) {
|
|
400
|
+
const hiddenAttestation = readDynamicToolBridgePrivateAttestation(request);
|
|
401
|
+
if (!hiddenAttestation) {
|
|
402
|
+
throw new Error('dynamic_tool_bridge_attestation_missing');
|
|
403
|
+
}
|
|
404
|
+
const expectedAttestation = readDynamicToolBridgeExpectedAttestation(featureToggles);
|
|
405
|
+
if (!expectedAttestation) {
|
|
406
|
+
throw new Error('dynamic_tool_bridge_attestation_invalid');
|
|
407
|
+
}
|
|
408
|
+
if (expectedAttestation.revoked) {
|
|
409
|
+
throw new Error('dynamic_tool_bridge_attestation_revoked');
|
|
410
|
+
}
|
|
411
|
+
if (expectedAttestation.expiresAt) {
|
|
412
|
+
const expiresAtMs = Date.parse(expectedAttestation.expiresAt);
|
|
413
|
+
if (!Number.isFinite(expiresAtMs)) {
|
|
414
|
+
throw new Error('dynamic_tool_bridge_attestation_malformed');
|
|
415
|
+
}
|
|
416
|
+
if (expiresAtMs <= Date.now()) {
|
|
417
|
+
throw new Error('dynamic_tool_bridge_attestation_expired');
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
const normalizedSourceId = normalizeDynamicToolBridgeSourceId(sourceId);
|
|
421
|
+
if (hiddenAttestation.sourceId !== normalizedSourceId ||
|
|
422
|
+
hiddenAttestation.sourceId !== expectedAttestation.sourceId ||
|
|
423
|
+
expectedAttestation.sourceId !== normalizedSourceId) {
|
|
424
|
+
throw new Error('dynamic_tool_bridge_attestation_source_mismatch');
|
|
425
|
+
}
|
|
426
|
+
if (hiddenAttestation.principal !== expectedAttestation.principal) {
|
|
427
|
+
throw new Error('dynamic_tool_bridge_attestation_principal_mismatch');
|
|
428
|
+
}
|
|
429
|
+
const tokenSha256 = createHash('sha256').update(hiddenAttestation.token).digest('hex');
|
|
430
|
+
if (tokenSha256 !== expectedAttestation.tokenSha256) {
|
|
431
|
+
throw new Error('dynamic_tool_bridge_attestation_invalid');
|
|
432
|
+
}
|
|
433
|
+
return hiddenAttestation;
|
|
434
|
+
}
|
|
435
|
+
function readDynamicToolBridgePrivateAttestation(request) {
|
|
436
|
+
const privateRecord = request.codex_private;
|
|
437
|
+
if (!privateRecord) {
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
for (const key of DYNAMIC_TOOL_BRIDGE_PRIVATE_ATTESTATION_KEYS) {
|
|
441
|
+
if (!Object.prototype.hasOwnProperty.call(privateRecord, key)) {
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
const attestation = asRecord(privateRecord[key]);
|
|
445
|
+
const token = readStringValue(attestation, 'token', 'bridge_token', 'bridgeToken');
|
|
446
|
+
const sourceId = readStringValue(attestation, 'source_id', 'sourceId');
|
|
447
|
+
const principal = readStringValue(attestation, 'principal');
|
|
448
|
+
if (!token || !sourceId || !principal) {
|
|
449
|
+
throw new Error('dynamic_tool_bridge_attestation_malformed');
|
|
450
|
+
}
|
|
451
|
+
return {
|
|
452
|
+
token,
|
|
453
|
+
sourceId: normalizeDynamicToolBridgeSourceId(sourceId),
|
|
454
|
+
principal
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
function readDynamicToolBridgeExpectedAttestation(featureToggles) {
|
|
460
|
+
const bridgePolicy = readDynamicToolBridgePolicyRecord(featureToggles);
|
|
461
|
+
const rawAttestation = bridgePolicy.attestation;
|
|
462
|
+
if (rawAttestation === undefined) {
|
|
463
|
+
return null;
|
|
464
|
+
}
|
|
465
|
+
const attestation = asRecord(rawAttestation);
|
|
466
|
+
const tokenSha256 = readStringValue(attestation, 'token_sha256', 'tokenSha256');
|
|
467
|
+
const sourceId = readStringValue(attestation, 'source_id', 'sourceId');
|
|
468
|
+
const principal = readStringValue(attestation, 'principal');
|
|
469
|
+
const expiresAt = readStringValue(attestation, 'expires_at', 'expiresAt');
|
|
470
|
+
const revoked = readBooleanValue(attestation, 'revoked') ?? false;
|
|
471
|
+
const revokedAt = readStringValue(attestation, 'revoked_at', 'revokedAt');
|
|
472
|
+
if (!tokenSha256 || !sourceId || !principal) {
|
|
473
|
+
throw new Error('dynamic_tool_bridge_attestation_malformed');
|
|
474
|
+
}
|
|
475
|
+
if (!/^[a-f0-9]{64}$/i.test(tokenSha256)) {
|
|
476
|
+
throw new Error('dynamic_tool_bridge_attestation_malformed');
|
|
477
|
+
}
|
|
478
|
+
return {
|
|
479
|
+
tokenSha256: tokenSha256.toLowerCase(),
|
|
480
|
+
sourceId: normalizeDynamicToolBridgeSourceId(sourceId),
|
|
481
|
+
principal,
|
|
482
|
+
expiresAt: expiresAt ?? null,
|
|
483
|
+
revoked: revoked || Boolean(revokedAt)
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
function readDynamicToolBridgePolicyRecord(featureToggles) {
|
|
487
|
+
const coordinatorPolicy = asRecord(asRecord(featureToggles.coordinator).dynamic_tool_bridge);
|
|
488
|
+
if (Object.keys(coordinatorPolicy).length > 0) {
|
|
489
|
+
return coordinatorPolicy;
|
|
490
|
+
}
|
|
491
|
+
return asRecord(featureToggles.dynamic_tool_bridge);
|
|
492
|
+
}
|
|
493
|
+
function normalizeDynamicToolBridgeSourceId(sourceId) {
|
|
494
|
+
return sourceId.trim().toLowerCase();
|
|
495
|
+
}
|
|
496
|
+
async function loadRunManifest(manifestPath) {
|
|
497
|
+
const raw = await readFile(manifestPath, 'utf8');
|
|
498
|
+
return JSON.parse(raw);
|
|
499
|
+
}
|
|
500
|
+
async function loadControlSnapshot(manifestPath) {
|
|
501
|
+
const controlPath = resolve(dirname(manifestPath), 'control.json');
|
|
502
|
+
try {
|
|
503
|
+
const raw = await readFile(controlPath, 'utf8');
|
|
504
|
+
const parsed = safeJsonParse(raw);
|
|
505
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
return parsed;
|
|
509
|
+
}
|
|
510
|
+
catch (error) {
|
|
511
|
+
if (error.code === 'ENOENT') {
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
function deriveIdempotentRequestId(action, intentId, manifestPath) {
|
|
518
|
+
if (!intentId) {
|
|
519
|
+
return undefined;
|
|
520
|
+
}
|
|
521
|
+
const digest = createHash('sha256')
|
|
522
|
+
.update(`${action}:${intentId}:${manifestPath}`)
|
|
523
|
+
.digest('hex')
|
|
524
|
+
.slice(0, 24);
|
|
525
|
+
return `intent-${digest}`;
|
|
526
|
+
}
|
|
327
527
|
async function handleDelegateSpawn(input, repoRoot, allowNested, allowedRoots, allowedHosts, toolProfile) {
|
|
328
528
|
const pipeline = requireString(readStringValue(input, 'pipeline'), 'pipeline');
|
|
329
529
|
const repo = readStringValue(input, 'repo') ?? repoRoot ?? process.cwd();
|
|
@@ -347,7 +547,7 @@ async function handleDelegateSpawn(input, repoRoot, allowNested, allowedRoots, a
|
|
|
347
547
|
args.push('--parent-run', parentRunId);
|
|
348
548
|
}
|
|
349
549
|
const requestedMode = readStringValue(input, 'delegate_mode', 'delegateMode') ?? 'question_only';
|
|
350
|
-
const childMode =
|
|
550
|
+
const childMode = resolveChildDelegateMode(requestedMode, allowNested);
|
|
351
551
|
const envOverrides = readStringMap(input, 'env');
|
|
352
552
|
const delegationToken = randomBytes(32).toString('hex');
|
|
353
553
|
const parentManifestPath = resolveParentManifestPath(input, allowedRoots);
|
|
@@ -498,114 +698,10 @@ async function handleDelegateSpawn(input, repoRoot, allowNested, allowedRoots, a
|
|
|
498
698
|
};
|
|
499
699
|
}
|
|
500
700
|
async function handleQuestionEnqueue(input, request, allowedRoots, allowedHosts, expiryFallback) {
|
|
501
|
-
|
|
502
|
-
if (!parentManifestPath) {
|
|
503
|
-
throw new Error('parent_manifest_path is required');
|
|
504
|
-
}
|
|
505
|
-
const delegationToken = await resolveDelegationToken(request, allowedRoots, {
|
|
506
|
-
retryMs: DEFAULT_DELEGATION_TOKEN_RETRY_MS,
|
|
507
|
-
intervalMs: DEFAULT_DELEGATION_TOKEN_RETRY_INTERVAL_MS
|
|
508
|
-
});
|
|
509
|
-
const childRunId = process.env.CODEX_ORCHESTRATOR_RUN_ID ?? readStringValue(input, 'from_run_id', 'fromRunId') ?? '';
|
|
510
|
-
if (!delegationToken) {
|
|
511
|
-
throw new Error('delegation_token missing');
|
|
512
|
-
}
|
|
513
|
-
const autoPause = readBooleanValue(input, 'auto_pause', 'autoPause') ?? true;
|
|
514
|
-
const manifestFromEnv = process.env.CODEX_ORCHESTRATOR_MANIFEST_PATH;
|
|
515
|
-
const manifestFromInput = readStringValue(input, 'from_manifest_path', 'fromManifestPath');
|
|
516
|
-
const childManifestPath = manifestFromEnv ?? manifestFromInput;
|
|
517
|
-
const result = await callControlEndpointWithRetry(parentManifestPath, '/questions/enqueue', {
|
|
518
|
-
parent_run_id: readStringValue(input, 'parent_run_id', 'parentRunId') ?? '',
|
|
519
|
-
parent_task_id: readStringValue(input, 'parent_task_id', 'parentTaskId') ?? null,
|
|
520
|
-
from_run_id: childRunId,
|
|
521
|
-
from_manifest_path: childManifestPath ?? null,
|
|
522
|
-
prompt: requireString(readStringValue(input, 'prompt'), 'prompt'),
|
|
523
|
-
urgency: readStringValue(input, 'urgency') ?? 'med',
|
|
524
|
-
expires_in_ms: readNumberValue(input, 'expires_in_ms', 'expiresInMs'),
|
|
525
|
-
auto_pause: autoPause,
|
|
526
|
-
expiry_fallback: expiryFallback
|
|
527
|
-
}, {
|
|
528
|
-
[DELEGATION_TOKEN_HEADER]: delegationToken,
|
|
529
|
-
[DELEGATION_RUN_HEADER]: childRunId
|
|
530
|
-
}, {
|
|
531
|
-
allowedHosts,
|
|
532
|
-
allowedRoots,
|
|
533
|
-
retryMs: DEFAULT_DELEGATION_TOKEN_RETRY_MS,
|
|
534
|
-
retryIntervalMs: DEFAULT_DELEGATION_TOKEN_RETRY_INTERVAL_MS
|
|
535
|
-
});
|
|
536
|
-
if (autoPause && manifestFromEnv) {
|
|
537
|
-
const resolvedManifest = resolveRunManifestPath(manifestFromEnv, allowedRoots, 'manifest_path');
|
|
538
|
-
await callControlEndpoint(resolvedManifest, '/control/action', {
|
|
539
|
-
action: 'pause',
|
|
540
|
-
requested_by: 'delegate',
|
|
541
|
-
reason: 'awaiting_question_answer'
|
|
542
|
-
}, undefined, { allowedHosts, allowedRoots });
|
|
543
|
-
}
|
|
544
|
-
return {
|
|
545
|
-
...result,
|
|
546
|
-
fallback_action: expiryFallback
|
|
547
|
-
};
|
|
701
|
+
return handleDelegationServerQuestionEnqueue(input, request, allowedRoots, allowedHosts, expiryFallback, questionFlowDeps);
|
|
548
702
|
}
|
|
549
703
|
async function handleQuestionPoll(input, request, allowedRoots, allowedHosts, expiryFallback) {
|
|
550
|
-
|
|
551
|
-
if (!parentManifestPath) {
|
|
552
|
-
throw new Error('parent_manifest_path is required');
|
|
553
|
-
}
|
|
554
|
-
const delegationToken = await resolveDelegationToken(request, allowedRoots, {
|
|
555
|
-
retryMs: DEFAULT_DELEGATION_TOKEN_RETRY_MS,
|
|
556
|
-
intervalMs: DEFAULT_DELEGATION_TOKEN_RETRY_INTERVAL_MS
|
|
557
|
-
});
|
|
558
|
-
const childRunId = process.env.CODEX_ORCHESTRATOR_RUN_ID ?? readStringValue(input, 'from_run_id', 'fromRunId') ?? '';
|
|
559
|
-
if (!delegationToken) {
|
|
560
|
-
throw new Error('delegation_token missing');
|
|
561
|
-
}
|
|
562
|
-
const questionId = requireString(readStringValue(input, 'question_id', 'questionId'), 'question_id');
|
|
563
|
-
const requestedWaitMs = readNumberValue(input, 'wait_ms', 'waitMs') ?? 0;
|
|
564
|
-
const waitMs = clampQuestionPollWaitMs(requestedWaitMs);
|
|
565
|
-
const deadline = Date.now() + waitMs;
|
|
566
|
-
const maxIterations = waitMs > 0 ? Math.max(1, Math.ceil(waitMs / QUESTION_POLL_INTERVAL_MS)) : 1;
|
|
567
|
-
for (let iteration = 0; iteration < maxIterations; iteration += 1) {
|
|
568
|
-
const remainingMs = waitMs > 0 ? Math.max(0, deadline - Date.now()) : null;
|
|
569
|
-
const timeoutMs = remainingMs === null ? undefined : Math.max(1, Math.min(DEFAULT_CONTROL_ENDPOINT_TIMEOUT_MS, remainingMs));
|
|
570
|
-
const retryMs = remainingMs === null ? DEFAULT_DELEGATION_TOKEN_RETRY_MS : Math.min(DEFAULT_DELEGATION_TOKEN_RETRY_MS, remainingMs);
|
|
571
|
-
const record = await callControlEndpointWithRetry(parentManifestPath, `/questions/${questionId}`, null, {
|
|
572
|
-
[DELEGATION_TOKEN_HEADER]: delegationToken,
|
|
573
|
-
[DELEGATION_RUN_HEADER]: childRunId
|
|
574
|
-
}, {
|
|
575
|
-
allowedHosts,
|
|
576
|
-
allowedRoots,
|
|
577
|
-
retryMs,
|
|
578
|
-
retryIntervalMs: DEFAULT_DELEGATION_TOKEN_RETRY_INTERVAL_MS,
|
|
579
|
-
...(timeoutMs !== undefined ? { timeoutMs } : {})
|
|
580
|
-
});
|
|
581
|
-
const status = readStringValue(record, 'status');
|
|
582
|
-
if (status !== 'queued' || waitMs <= 0 || Date.now() >= deadline) {
|
|
583
|
-
const expiresAt = readStringValue(record, 'expires_at', 'expiresAt');
|
|
584
|
-
if (status === 'expired') {
|
|
585
|
-
await applyQuestionFallback(expiryFallback, allowedHosts, allowedRoots);
|
|
586
|
-
}
|
|
587
|
-
return {
|
|
588
|
-
...record,
|
|
589
|
-
expired_at: status === 'expired' ? expiresAt ?? null : null,
|
|
590
|
-
fallback_action: status === 'expired' ? expiryFallback : null
|
|
591
|
-
};
|
|
592
|
-
}
|
|
593
|
-
await delay(QUESTION_POLL_INTERVAL_MS);
|
|
594
|
-
}
|
|
595
|
-
const remainingMs = waitMs > 0 ? Math.max(0, deadline - Date.now()) : null;
|
|
596
|
-
const timeoutMs = remainingMs === null ? undefined : Math.max(1, Math.min(DEFAULT_CONTROL_ENDPOINT_TIMEOUT_MS, remainingMs));
|
|
597
|
-
const record = await callControlEndpoint(parentManifestPath, `/questions/${questionId}`, null, {
|
|
598
|
-
[DELEGATION_TOKEN_HEADER]: delegationToken,
|
|
599
|
-
[DELEGATION_RUN_HEADER]: childRunId
|
|
600
|
-
}, {
|
|
601
|
-
allowedHosts,
|
|
602
|
-
...(timeoutMs !== undefined ? { timeoutMs } : {})
|
|
603
|
-
});
|
|
604
|
-
return {
|
|
605
|
-
...record,
|
|
606
|
-
expired_at: null,
|
|
607
|
-
fallback_action: null
|
|
608
|
-
};
|
|
704
|
+
return handleDelegationServerQuestionPoll(input, request, allowedRoots, allowedHosts, expiryFallback, questionFlowDeps);
|
|
609
705
|
}
|
|
610
706
|
async function handleGithubCall(toolName, input, request, context) {
|
|
611
707
|
const op = toolName.replace('github.', '');
|
|
@@ -892,206 +988,6 @@ async function runGh(args, timeoutMs = DEFAULT_GH_TIMEOUT_MS) {
|
|
|
892
988
|
});
|
|
893
989
|
});
|
|
894
990
|
}
|
|
895
|
-
async function runJsonRpcServer(handler, options = {}) {
|
|
896
|
-
let buffer = Buffer.alloc(0);
|
|
897
|
-
let expectedLength = null;
|
|
898
|
-
let processing = Promise.resolve();
|
|
899
|
-
let halted = false;
|
|
900
|
-
const input = options.stdin ?? process.stdin;
|
|
901
|
-
const output = options.stdout ?? process.stdout;
|
|
902
|
-
const handleProtocolViolation = (message) => {
|
|
903
|
-
if (halted) {
|
|
904
|
-
return;
|
|
905
|
-
}
|
|
906
|
-
halted = true;
|
|
907
|
-
logger.warn(message);
|
|
908
|
-
process.exitCode = 1;
|
|
909
|
-
buffer = Buffer.alloc(0);
|
|
910
|
-
expectedLength = null;
|
|
911
|
-
if (typeof input.pause === 'function') {
|
|
912
|
-
input.pause();
|
|
913
|
-
}
|
|
914
|
-
};
|
|
915
|
-
input.on('data', (chunk) => {
|
|
916
|
-
if (halted) {
|
|
917
|
-
return;
|
|
918
|
-
}
|
|
919
|
-
buffer = Buffer.concat([buffer, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)]);
|
|
920
|
-
if (buffer.length > MAX_MCP_BUFFER_BYTES) {
|
|
921
|
-
handleProtocolViolation(`Rejecting MCP buffer larger than ${MAX_MCP_BUFFER_BYTES} bytes`);
|
|
922
|
-
return;
|
|
923
|
-
}
|
|
924
|
-
processing = processing
|
|
925
|
-
.then(() => processBuffer())
|
|
926
|
-
.catch((error) => {
|
|
927
|
-
logger.error(`Failed to process MCP buffer: ${error?.message ?? error}`);
|
|
928
|
-
});
|
|
929
|
-
});
|
|
930
|
-
async function processBuffer() {
|
|
931
|
-
while (buffer.length > 0) {
|
|
932
|
-
if (halted) {
|
|
933
|
-
return;
|
|
934
|
-
}
|
|
935
|
-
if (expectedLength !== null) {
|
|
936
|
-
if (buffer.length < expectedLength) {
|
|
937
|
-
return;
|
|
938
|
-
}
|
|
939
|
-
const body = buffer.slice(0, expectedLength);
|
|
940
|
-
buffer = buffer.slice(expectedLength);
|
|
941
|
-
expectedLength = null;
|
|
942
|
-
await handleMessage(body.toString('utf8'), 'framed');
|
|
943
|
-
continue;
|
|
944
|
-
}
|
|
945
|
-
const headerEnd = buffer.indexOf('\r\n\r\n');
|
|
946
|
-
if (headerEnd === -1) {
|
|
947
|
-
const newlineIndex = buffer.indexOf('\n');
|
|
948
|
-
if (newlineIndex !== -1) {
|
|
949
|
-
const lineBuffer = buffer.slice(0, newlineIndex);
|
|
950
|
-
const line = lineBuffer.toString('utf8').trim();
|
|
951
|
-
buffer = buffer.slice(newlineIndex + 1);
|
|
952
|
-
if (!line) {
|
|
953
|
-
continue;
|
|
954
|
-
}
|
|
955
|
-
const normalizedLine = line.trimStart();
|
|
956
|
-
const looksLikeHeaderLine = /^[A-Za-z0-9-]+:/.test(normalizedLine);
|
|
957
|
-
const looksLikeJson = normalizedLine.startsWith('{') || normalizedLine.startsWith('[');
|
|
958
|
-
const isContentLength = normalizedLine.toLowerCase().startsWith('content-length:');
|
|
959
|
-
let restoredHeader = false;
|
|
960
|
-
if (!looksLikeJson && looksLikeHeaderLine) {
|
|
961
|
-
// Fall through to header-size checks for partial Content-Length frames (and other header lines).
|
|
962
|
-
buffer = Buffer.concat([Buffer.from(lineBuffer), Buffer.from('\n'), buffer]);
|
|
963
|
-
restoredHeader = true;
|
|
964
|
-
}
|
|
965
|
-
else if (!isContentLength) {
|
|
966
|
-
const lineBytes = Buffer.byteLength(line, 'utf8');
|
|
967
|
-
if (lineBytes > MAX_MCP_MESSAGE_BYTES) {
|
|
968
|
-
handleProtocolViolation(`Rejecting MCP payload (${lineBytes} bytes) larger than ${MAX_MCP_MESSAGE_BYTES}`);
|
|
969
|
-
return;
|
|
970
|
-
}
|
|
971
|
-
await handleMessage(line, 'jsonl');
|
|
972
|
-
continue;
|
|
973
|
-
}
|
|
974
|
-
if (!restoredHeader && isContentLength) {
|
|
975
|
-
// Fall through to header-size checks for partial Content-Length frames.
|
|
976
|
-
buffer = Buffer.concat([Buffer.from(lineBuffer), Buffer.from('\n'), buffer]);
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
else if (buffer.length > MAX_MCP_MESSAGE_BYTES) {
|
|
980
|
-
handleProtocolViolation(`Rejecting MCP payload (${buffer.length} bytes) larger than ${MAX_MCP_MESSAGE_BYTES}`);
|
|
981
|
-
return;
|
|
982
|
-
}
|
|
983
|
-
if (buffer.length > MAX_MCP_HEADER_BYTES) {
|
|
984
|
-
const overflow = buffer.slice(MAX_MCP_HEADER_BYTES);
|
|
985
|
-
const allowedPrefix = MCP_HEADER_DELIMITER_BUFFER.subarray(0, overflow.length);
|
|
986
|
-
if (overflow.length > MCP_HEADER_DELIMITER_BYTES || !overflow.equals(allowedPrefix)) {
|
|
987
|
-
handleProtocolViolation(`Rejecting MCP header larger than ${MAX_MCP_HEADER_BYTES} bytes`);
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
return;
|
|
991
|
-
}
|
|
992
|
-
if (headerEnd > MAX_MCP_HEADER_BYTES) {
|
|
993
|
-
handleProtocolViolation(`Rejecting MCP header larger than ${MAX_MCP_HEADER_BYTES} bytes`);
|
|
994
|
-
return;
|
|
995
|
-
}
|
|
996
|
-
const header = buffer.slice(0, headerEnd).toString('utf8');
|
|
997
|
-
const parsed = parseContentLengthHeader(header);
|
|
998
|
-
if (parsed.error) {
|
|
999
|
-
handleProtocolViolation(parsed.error);
|
|
1000
|
-
return;
|
|
1001
|
-
}
|
|
1002
|
-
if (parsed.length === null) {
|
|
1003
|
-
const lines = header.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
1004
|
-
if (lines.length === 0) {
|
|
1005
|
-
buffer = buffer.slice(headerEnd + 4);
|
|
1006
|
-
continue;
|
|
1007
|
-
}
|
|
1008
|
-
const allJsonLike = lines.every((line) => line.startsWith('{') || line.startsWith('['));
|
|
1009
|
-
if (allJsonLike) {
|
|
1010
|
-
buffer = buffer.slice(headerEnd + 4);
|
|
1011
|
-
for (const line of lines) {
|
|
1012
|
-
await handleMessage(line, 'jsonl');
|
|
1013
|
-
}
|
|
1014
|
-
continue;
|
|
1015
|
-
}
|
|
1016
|
-
handleProtocolViolation('Missing Content-Length header in MCP message');
|
|
1017
|
-
return;
|
|
1018
|
-
}
|
|
1019
|
-
const length = parsed.length;
|
|
1020
|
-
if (!Number.isFinite(length) || length < 0) {
|
|
1021
|
-
handleProtocolViolation('Invalid Content-Length for MCP payload');
|
|
1022
|
-
return;
|
|
1023
|
-
}
|
|
1024
|
-
if (length > MAX_MCP_MESSAGE_BYTES) {
|
|
1025
|
-
handleProtocolViolation(`Rejecting MCP payload (${length} bytes) larger than ${MAX_MCP_MESSAGE_BYTES}`);
|
|
1026
|
-
return;
|
|
1027
|
-
}
|
|
1028
|
-
expectedLength = length;
|
|
1029
|
-
buffer = buffer.slice(headerEnd + 4);
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
async function handleMessage(raw, format) {
|
|
1033
|
-
let request;
|
|
1034
|
-
try {
|
|
1035
|
-
request = JSON.parse(raw);
|
|
1036
|
-
}
|
|
1037
|
-
catch (error) {
|
|
1038
|
-
logger.error(`Failed to parse MCP message: ${error?.message ?? error}`);
|
|
1039
|
-
return;
|
|
1040
|
-
}
|
|
1041
|
-
if (typeof request.method !== 'string') {
|
|
1042
|
-
return;
|
|
1043
|
-
}
|
|
1044
|
-
const id = request.id ?? null;
|
|
1045
|
-
try {
|
|
1046
|
-
const result = await handler(request);
|
|
1047
|
-
if (id !== null && typeof id !== 'undefined') {
|
|
1048
|
-
sendResponse({ jsonrpc: '2.0', id, result }, output, format);
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
catch (error) {
|
|
1052
|
-
if (id !== null && typeof id !== 'undefined') {
|
|
1053
|
-
sendResponse({
|
|
1054
|
-
jsonrpc: '2.0',
|
|
1055
|
-
id,
|
|
1056
|
-
error: { code: -32603, message: error?.message ?? String(error) }
|
|
1057
|
-
}, output, format);
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
function parseContentLengthHeader(header) {
|
|
1063
|
-
const lines = header.split(/\r?\n/);
|
|
1064
|
-
let contentLength = null;
|
|
1065
|
-
for (const line of lines) {
|
|
1066
|
-
const separator = line.indexOf(':');
|
|
1067
|
-
if (separator === -1) {
|
|
1068
|
-
continue;
|
|
1069
|
-
}
|
|
1070
|
-
const name = line.slice(0, separator).trim().toLowerCase();
|
|
1071
|
-
if (name !== 'content-length') {
|
|
1072
|
-
continue;
|
|
1073
|
-
}
|
|
1074
|
-
if (contentLength !== null) {
|
|
1075
|
-
return { length: null, error: 'Multiple Content-Length headers in MCP message' };
|
|
1076
|
-
}
|
|
1077
|
-
const value = line.slice(separator + 1).trim();
|
|
1078
|
-
if (!/^\d+$/.test(value)) {
|
|
1079
|
-
return { length: null, error: 'Invalid Content-Length header in MCP message' };
|
|
1080
|
-
}
|
|
1081
|
-
contentLength = Number(value);
|
|
1082
|
-
}
|
|
1083
|
-
return { length: contentLength };
|
|
1084
|
-
}
|
|
1085
|
-
function sendResponse(response, output = process.stdout, format = 'framed') {
|
|
1086
|
-
const payload = JSON.stringify(response);
|
|
1087
|
-
if (format === 'jsonl') {
|
|
1088
|
-
output.write(`${payload}\n`);
|
|
1089
|
-
return;
|
|
1090
|
-
}
|
|
1091
|
-
const buffer = Buffer.from(payload, 'utf8');
|
|
1092
|
-
const header = Buffer.from(`Content-Length: ${buffer.length}\r\n\r\n`, 'utf8');
|
|
1093
|
-
output.write(Buffer.concat([header, buffer]));
|
|
1094
|
-
}
|
|
1095
991
|
function safeJsonParse(text) {
|
|
1096
992
|
try {
|
|
1097
993
|
return JSON.parse(text);
|
|
@@ -1101,35 +997,25 @@ function safeJsonParse(text) {
|
|
|
1101
997
|
}
|
|
1102
998
|
}
|
|
1103
999
|
function parseSpawnOutput(stdout) {
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1000
|
+
return parseTrailingJsonObject(stdout, { allowTrailingTextAfterJson: true }) ?? {};
|
|
1001
|
+
}
|
|
1002
|
+
function resolveDelegationIdleTimeoutMs(env = process.env) {
|
|
1003
|
+
const raw = env.CODEX_DELEGATION_IDLE_TIMEOUT_MS;
|
|
1004
|
+
if (typeof raw !== 'string' || raw.trim().length === 0) {
|
|
1005
|
+
return DEFAULT_DELEGATION_IDLE_TIMEOUT_MS;
|
|
1107
1006
|
}
|
|
1108
|
-
const
|
|
1109
|
-
if (
|
|
1110
|
-
return
|
|
1007
|
+
const parsed = Number.parseInt(raw.trim(), 10);
|
|
1008
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
1009
|
+
return DEFAULT_DELEGATION_IDLE_TIMEOUT_MS;
|
|
1111
1010
|
}
|
|
1112
|
-
|
|
1113
|
-
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
1114
|
-
if (!lines[i].trim().startsWith('{')) {
|
|
1115
|
-
continue;
|
|
1116
|
-
}
|
|
1117
|
-
for (let j = lines.length - 1; j >= i; j -= 1) {
|
|
1118
|
-
if (!lines[j].includes('}')) {
|
|
1119
|
-
continue;
|
|
1120
|
-
}
|
|
1121
|
-
const candidate = lines.slice(i, j + 1).join('\n');
|
|
1122
|
-
const parsed = safeJsonParse(candidate);
|
|
1123
|
-
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
1124
|
-
return parsed;
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
return {};
|
|
1011
|
+
return parsed;
|
|
1129
1012
|
}
|
|
1130
1013
|
export const __test__ = {
|
|
1131
1014
|
runJsonRpcServer,
|
|
1132
1015
|
handleToolCall,
|
|
1016
|
+
buildToolList,
|
|
1017
|
+
createDelegationServerRpcHandler,
|
|
1018
|
+
resolveChildDelegateMode,
|
|
1133
1019
|
parseContentLengthHeader,
|
|
1134
1020
|
parseSpawnOutput,
|
|
1135
1021
|
handleDelegateSpawn,
|
|
@@ -1139,15 +1025,28 @@ export const __test__ = {
|
|
|
1139
1025
|
QUESTION_POLL_INTERVAL_MS,
|
|
1140
1026
|
clampQuestionPollWaitMs
|
|
1141
1027
|
};
|
|
1142
|
-
function clampQuestionPollWaitMs(value) {
|
|
1143
|
-
if (!Number.isFinite(value) || value <= 0) {
|
|
1144
|
-
return 0;
|
|
1145
|
-
}
|
|
1146
|
-
return Math.min(value, MAX_QUESTION_POLL_WAIT_MS);
|
|
1147
|
-
}
|
|
1148
1028
|
function delay(ms) {
|
|
1149
1029
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1150
1030
|
}
|
|
1031
|
+
const questionFlowDeps = {
|
|
1032
|
+
resolveParentManifestPath,
|
|
1033
|
+
readStringValue,
|
|
1034
|
+
readNumberValue,
|
|
1035
|
+
readBooleanValue,
|
|
1036
|
+
requireString,
|
|
1037
|
+
callControlEndpoint,
|
|
1038
|
+
callControlEndpointWithRetry,
|
|
1039
|
+
resolveRunManifestPath,
|
|
1040
|
+
isPathWithinRoots,
|
|
1041
|
+
safeJsonParse,
|
|
1042
|
+
delay,
|
|
1043
|
+
defaultDelegationTokenRetryMs: DEFAULT_DELEGATION_TOKEN_RETRY_MS,
|
|
1044
|
+
defaultDelegationTokenRetryIntervalMs: DEFAULT_DELEGATION_TOKEN_RETRY_INTERVAL_MS,
|
|
1045
|
+
defaultControlEndpointTimeoutMs: DEFAULT_CONTROL_ENDPOINT_TIMEOUT_MS,
|
|
1046
|
+
delegationTokenHeader: DELEGATION_TOKEN_HEADER,
|
|
1047
|
+
delegationRunHeader: DELEGATION_RUN_HEADER,
|
|
1048
|
+
delegationTokenFile: DELEGATION_TOKEN_FILE
|
|
1049
|
+
};
|
|
1151
1050
|
function resolveRepoRootForRuns(repoRoot, env) {
|
|
1152
1051
|
const configured = env.CODEX_ORCHESTRATOR_ROOT?.trim();
|
|
1153
1052
|
if (!configured) {
|
|
@@ -1367,6 +1266,62 @@ function asRecord(value) {
|
|
|
1367
1266
|
}
|
|
1368
1267
|
return value;
|
|
1369
1268
|
}
|
|
1269
|
+
function readCanonicalTraceability(record) {
|
|
1270
|
+
const traceability = asRecord(record.traceability);
|
|
1271
|
+
return Object.keys(traceability).length > 0 ? traceability : null;
|
|
1272
|
+
}
|
|
1273
|
+
const TRANSPORT_MUTATION_METADATA_KEYS = [
|
|
1274
|
+
'actor_id',
|
|
1275
|
+
'actorId',
|
|
1276
|
+
'actor_source',
|
|
1277
|
+
'actorSource',
|
|
1278
|
+
'transport_principal',
|
|
1279
|
+
'transportPrincipal',
|
|
1280
|
+
'principal',
|
|
1281
|
+
'transport_nonce',
|
|
1282
|
+
'transportNonce',
|
|
1283
|
+
'nonce',
|
|
1284
|
+
'transport_nonce_expires_at',
|
|
1285
|
+
'transportNonceExpiresAt',
|
|
1286
|
+
'nonce_expires_at',
|
|
1287
|
+
'nonceExpiresAt'
|
|
1288
|
+
];
|
|
1289
|
+
function hasTransportMutationMetadata(input) {
|
|
1290
|
+
return TRANSPORT_MUTATION_METADATA_KEYS.some((key) => Object.prototype.hasOwnProperty.call(input, key));
|
|
1291
|
+
}
|
|
1292
|
+
function readTransportMutationMetadata(input) {
|
|
1293
|
+
if (!Object.prototype.hasOwnProperty.call(input, 'transport')) {
|
|
1294
|
+
if (hasTransportMutationMetadata(input)) {
|
|
1295
|
+
throw new Error('transport_required');
|
|
1296
|
+
}
|
|
1297
|
+
return null;
|
|
1298
|
+
}
|
|
1299
|
+
const rawTransport = input.transport;
|
|
1300
|
+
if (typeof rawTransport !== 'string') {
|
|
1301
|
+
throw new Error('transport_invalid');
|
|
1302
|
+
}
|
|
1303
|
+
const normalizedTransport = rawTransport.trim();
|
|
1304
|
+
if (!normalizedTransport) {
|
|
1305
|
+
throw new Error('transport_invalid');
|
|
1306
|
+
}
|
|
1307
|
+
if (normalizedTransport !== 'discord' && normalizedTransport !== 'telegram') {
|
|
1308
|
+
throw new Error('transport_unsupported');
|
|
1309
|
+
}
|
|
1310
|
+
const transport = normalizedTransport;
|
|
1311
|
+
const actorId = readStringValue(input, 'actor_id', 'actorId');
|
|
1312
|
+
const actorSource = readStringValue(input, 'actor_source', 'actorSource');
|
|
1313
|
+
const principal = readStringValue(input, 'transport_principal', 'transportPrincipal', 'principal');
|
|
1314
|
+
const nonce = readStringValue(input, 'transport_nonce', 'transportNonce', 'nonce');
|
|
1315
|
+
const nonceExpiresAt = readStringValue(input, 'transport_nonce_expires_at', 'transportNonceExpiresAt', 'nonce_expires_at', 'nonceExpiresAt');
|
|
1316
|
+
return {
|
|
1317
|
+
transport,
|
|
1318
|
+
...(actorId ? { actor_id: actorId } : {}),
|
|
1319
|
+
...(actorSource ? { actor_source: actorSource } : {}),
|
|
1320
|
+
...(principal ? { transport_principal: principal } : {}),
|
|
1321
|
+
...(nonce ? { transport_nonce: nonce } : {}),
|
|
1322
|
+
...(nonceExpiresAt ? { transport_nonce_expires_at: nonceExpiresAt } : {})
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1370
1325
|
function readStringValue(record, ...keys) {
|
|
1371
1326
|
for (const key of keys) {
|
|
1372
1327
|
const value = record[key];
|
|
@@ -1425,12 +1380,47 @@ function requireNumber(value, field) {
|
|
|
1425
1380
|
}
|
|
1426
1381
|
return value;
|
|
1427
1382
|
}
|
|
1383
|
+
function getDelegateModeViolationMessage(mode, toolName) {
|
|
1384
|
+
if (toolName.startsWith('coordinator.') && mode !== 'full') {
|
|
1385
|
+
if (mode === 'status_only') {
|
|
1386
|
+
return `Tool ${toolName} blocked in status_only mode; only delegate.status is allowed.`;
|
|
1387
|
+
}
|
|
1388
|
+
return `Tool ${toolName} blocked in question_only mode.`;
|
|
1389
|
+
}
|
|
1390
|
+
if (mode === 'question_only' && isRestrictedTool(toolName)) {
|
|
1391
|
+
return `Tool ${toolName} blocked in question_only mode.`;
|
|
1392
|
+
}
|
|
1393
|
+
if (mode === 'status_only' && toolName !== 'delegate.status' && isDelegationOrGithubTool(toolName)) {
|
|
1394
|
+
return `Tool ${toolName} blocked in status_only mode; only delegate.status is allowed.`;
|
|
1395
|
+
}
|
|
1396
|
+
return null;
|
|
1397
|
+
}
|
|
1428
1398
|
function isRestrictedTool(toolName) {
|
|
1429
1399
|
return toolName === 'delegate.spawn' || toolName === 'delegate.pause' || toolName === 'delegate.cancel';
|
|
1430
1400
|
}
|
|
1401
|
+
function isDelegationOrGithubTool(toolName) {
|
|
1402
|
+
return toolName.startsWith('delegate.') || toolName.startsWith('github.') || toolName.startsWith('coordinator.');
|
|
1403
|
+
}
|
|
1404
|
+
function resolveChildDelegateMode(requestedMode, allowNested) {
|
|
1405
|
+
if (requestedMode === 'status_only') {
|
|
1406
|
+
return 'status_only';
|
|
1407
|
+
}
|
|
1408
|
+
if (allowNested && requestedMode === 'full') {
|
|
1409
|
+
return 'full';
|
|
1410
|
+
}
|
|
1411
|
+
return 'question_only';
|
|
1412
|
+
}
|
|
1431
1413
|
function containsSecret(record, key) {
|
|
1432
1414
|
return Object.prototype.hasOwnProperty.call(record, key);
|
|
1433
1415
|
}
|
|
1416
|
+
function containsAnySecret(record, keys) {
|
|
1417
|
+
for (const key of keys) {
|
|
1418
|
+
if (containsSecret(record, key)) {
|
|
1419
|
+
return true;
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
return false;
|
|
1423
|
+
}
|
|
1434
1424
|
async function reportSecurityViolation(kind, summary, toolName, allowedHosts) {
|
|
1435
1425
|
const manifestPath = process.env.CODEX_ORCHESTRATOR_MANIFEST_PATH;
|
|
1436
1426
|
if (!manifestPath) {
|
|
@@ -1448,70 +1438,7 @@ async function reportSecurityViolation(kind, summary, toolName, allowedHosts) {
|
|
|
1448
1438
|
}
|
|
1449
1439
|
}
|
|
1450
1440
|
export async function resolveDelegationToken(request, allowedRoots, options = {}) {
|
|
1451
|
-
|
|
1452
|
-
if (privateToken) {
|
|
1453
|
-
return String(privateToken);
|
|
1454
|
-
}
|
|
1455
|
-
const tokenPath = resolveDelegationTokenPath(allowedRoots);
|
|
1456
|
-
if (!tokenPath) {
|
|
1457
|
-
return null;
|
|
1458
|
-
}
|
|
1459
|
-
const retryMs = options.retryMs ?? 0;
|
|
1460
|
-
const intervalMs = options.intervalMs ?? DEFAULT_DELEGATION_TOKEN_RETRY_INTERVAL_MS;
|
|
1461
|
-
const deadline = Date.now() + retryMs;
|
|
1462
|
-
let token = await readDelegationTokenFile(tokenPath);
|
|
1463
|
-
while (!token && Date.now() < deadline) {
|
|
1464
|
-
await delay(intervalMs);
|
|
1465
|
-
token = await readDelegationTokenFile(tokenPath);
|
|
1466
|
-
}
|
|
1467
|
-
return token;
|
|
1468
|
-
}
|
|
1469
|
-
function resolveDelegationTokenPath(allowedRoots) {
|
|
1470
|
-
const explicit = process.env.CODEX_DELEGATION_TOKEN_PATH?.trim();
|
|
1471
|
-
const manifestPath = process.env.CODEX_ORCHESTRATOR_MANIFEST_PATH?.trim();
|
|
1472
|
-
let runDir = null;
|
|
1473
|
-
if (manifestPath) {
|
|
1474
|
-
try {
|
|
1475
|
-
const resolvedManifest = resolveRunManifestPath(manifestPath, allowedRoots, 'manifest_path');
|
|
1476
|
-
runDir = dirname(resolvedManifest);
|
|
1477
|
-
}
|
|
1478
|
-
catch {
|
|
1479
|
-
return null;
|
|
1480
|
-
}
|
|
1481
|
-
}
|
|
1482
|
-
if (explicit) {
|
|
1483
|
-
if (!runDir && !isAbsolute(explicit)) {
|
|
1484
|
-
return null;
|
|
1485
|
-
}
|
|
1486
|
-
const resolvedToken = runDir && !isAbsolute(explicit) ? resolve(runDir, explicit) : resolve(explicit);
|
|
1487
|
-
if (runDir) {
|
|
1488
|
-
if (!isPathWithinRoots(resolvedToken, [runDir])) {
|
|
1489
|
-
return null;
|
|
1490
|
-
}
|
|
1491
|
-
}
|
|
1492
|
-
else if (allowedRoots && allowedRoots.length > 0 && !isPathWithinRoots(resolvedToken, allowedRoots)) {
|
|
1493
|
-
return null;
|
|
1494
|
-
}
|
|
1495
|
-
return resolvedToken;
|
|
1496
|
-
}
|
|
1497
|
-
if (runDir) {
|
|
1498
|
-
return resolve(runDir, DELEGATION_TOKEN_FILE);
|
|
1499
|
-
}
|
|
1500
|
-
return null;
|
|
1501
|
-
}
|
|
1502
|
-
async function readDelegationTokenFile(tokenPath) {
|
|
1503
|
-
try {
|
|
1504
|
-
const raw = await readFile(tokenPath, 'utf8');
|
|
1505
|
-
const parsed = safeJsonParse(raw);
|
|
1506
|
-
const tokenValue = parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
1507
|
-
? parsed.token
|
|
1508
|
-
: null;
|
|
1509
|
-
const token = typeof tokenValue === 'string' && tokenValue.trim().length > 0 ? tokenValue.trim() : raw.trim();
|
|
1510
|
-
return token || null;
|
|
1511
|
-
}
|
|
1512
|
-
catch {
|
|
1513
|
-
return null;
|
|
1514
|
-
}
|
|
1441
|
+
return resolveDelegationTokenValue(request, allowedRoots, options, questionFlowDeps);
|
|
1515
1442
|
}
|
|
1516
1443
|
function buildDelegateMcpOverrides(toolProfile) {
|
|
1517
1444
|
const overrides = ['mcp_servers.delegation.enabled=true'];
|
|
@@ -1579,24 +1506,26 @@ function resolveManifestPath(value, allowedRoots) {
|
|
|
1579
1506
|
}
|
|
1580
1507
|
export function resolveRunManifestPath(rawPath, allowedRoots, label = 'manifest_path') {
|
|
1581
1508
|
const resolved = resolve(rawPath);
|
|
1582
|
-
|
|
1583
|
-
|
|
1509
|
+
const canonicalPath = realpathSafe(resolved);
|
|
1510
|
+
assertRunManifestPath(canonicalPath, label);
|
|
1511
|
+
if (allowedRoots && !isPathWithinRoots(canonicalPath, allowedRoots)) {
|
|
1584
1512
|
throw new Error(`${label} not permitted`);
|
|
1585
1513
|
}
|
|
1586
|
-
return
|
|
1514
|
+
return canonicalPath;
|
|
1587
1515
|
}
|
|
1588
1516
|
function assertRunManifestPath(pathname, label) {
|
|
1589
|
-
|
|
1517
|
+
const resolvedPath = resolve(pathname);
|
|
1518
|
+
if (basename(resolvedPath) !== 'manifest.json') {
|
|
1590
1519
|
throw new Error(`${label} invalid`);
|
|
1591
1520
|
}
|
|
1592
|
-
const runDir = dirname(
|
|
1521
|
+
const runDir = dirname(resolvedPath);
|
|
1593
1522
|
const cliDir = dirname(runDir);
|
|
1594
1523
|
if (basename(cliDir) !== 'cli') {
|
|
1595
1524
|
throw new Error(`${label} invalid`);
|
|
1596
1525
|
}
|
|
1597
1526
|
const taskDir = dirname(cliDir);
|
|
1598
1527
|
const runsDir = dirname(taskDir);
|
|
1599
|
-
if (!basename(runDir) || !basename(taskDir) ||
|
|
1528
|
+
if (!basename(runDir) || !basename(taskDir) || dirname(runsDir) === runsDir) {
|
|
1600
1529
|
throw new Error(`${label} invalid`);
|
|
1601
1530
|
}
|
|
1602
1531
|
}
|
|
@@ -1634,11 +1563,7 @@ function resolveSpawnManifestPath(manifestPath, repoRoot, allowedRoots) {
|
|
|
1634
1563
|
}
|
|
1635
1564
|
const resolved = isAbsolute(manifestPath) ? manifestPath : resolve(repoRoot, manifestPath);
|
|
1636
1565
|
try {
|
|
1637
|
-
|
|
1638
|
-
if (allowedRoots && !isPathWithinRoots(resolved, allowedRoots)) {
|
|
1639
|
-
return null;
|
|
1640
|
-
}
|
|
1641
|
-
return resolved;
|
|
1566
|
+
return resolveRunManifestPath(resolved, allowedRoots, 'manifest_path');
|
|
1642
1567
|
}
|
|
1643
1568
|
catch {
|
|
1644
1569
|
return null;
|
|
@@ -1659,42 +1584,6 @@ async function persistDelegationToken(manifestPath, token, info) {
|
|
|
1659
1584
|
logger.warn(`Failed to persist delegation token: ${error?.message ?? error}`);
|
|
1660
1585
|
}
|
|
1661
1586
|
}
|
|
1662
|
-
async function isRunAwaitingQuestion(manifestPath, allowedRoots) {
|
|
1663
|
-
try {
|
|
1664
|
-
const resolvedManifest = resolveRunManifestPath(manifestPath, allowedRoots, 'manifest_path');
|
|
1665
|
-
const controlPath = resolve(dirname(resolvedManifest), 'control.json');
|
|
1666
|
-
const raw = await readFile(controlPath, 'utf8');
|
|
1667
|
-
const snapshot = safeJsonParse(raw);
|
|
1668
|
-
const latest = snapshot && snapshot.latest_action && typeof snapshot.latest_action === 'object'
|
|
1669
|
-
? snapshot.latest_action
|
|
1670
|
-
: null;
|
|
1671
|
-
if (!latest) {
|
|
1672
|
-
return false;
|
|
1673
|
-
}
|
|
1674
|
-
return latest.action === 'pause' && latest.reason === 'awaiting_question_answer';
|
|
1675
|
-
}
|
|
1676
|
-
catch {
|
|
1677
|
-
return false;
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
1587
|
export async function applyQuestionFallback(fallback, allowedHosts, allowedRoots) {
|
|
1681
|
-
|
|
1682
|
-
if (!manifestPath) {
|
|
1683
|
-
return;
|
|
1684
|
-
}
|
|
1685
|
-
const shouldResolve = await isRunAwaitingQuestion(manifestPath, allowedRoots);
|
|
1686
|
-
if (!shouldResolve) {
|
|
1687
|
-
return;
|
|
1688
|
-
}
|
|
1689
|
-
const action = fallback === 'pause' ? 'pause' : fallback === 'resume' ? 'resume' : 'fail';
|
|
1690
|
-
try {
|
|
1691
|
-
await callControlEndpoint(resolveRunManifestPath(manifestPath, allowedRoots, 'manifest_path'), '/control/action', {
|
|
1692
|
-
action,
|
|
1693
|
-
requested_by: 'delegate',
|
|
1694
|
-
reason: 'question_expired'
|
|
1695
|
-
}, undefined, { allowedHosts, allowedRoots });
|
|
1696
|
-
}
|
|
1697
|
-
catch {
|
|
1698
|
-
// ignore
|
|
1699
|
-
}
|
|
1588
|
+
return applyDelegationQuestionFallback(fallback, allowedHosts, allowedRoots, questionFlowDeps);
|
|
1700
1589
|
}
|