@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.
Files changed (311) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/README.md +46 -317
  3. package/bin/codex-orchestrator.js +161 -0
  4. package/codex.orchestrator.json +149 -13
  5. package/dist/bin/codex-orchestrator.js +797 -1154
  6. package/dist/orchestrator/src/cli/adapters/CommandBuilder.js +50 -0
  7. package/dist/orchestrator/src/cli/adapters/CommandPlanner.js +22 -4
  8. package/dist/orchestrator/src/cli/adapters/CommandReviewer.js +3 -3
  9. package/dist/orchestrator/src/cli/adapters/CommandTester.js +2 -2
  10. package/dist/orchestrator/src/cli/adapters/cloudFailureDiagnostics.js +295 -11
  11. package/dist/orchestrator/src/cli/coStatusAttachCliShell.js +402 -0
  12. package/dist/orchestrator/src/cli/coStatusCliShell.js +451 -0
  13. package/dist/orchestrator/src/cli/coStatusOperatorAutopilotCliShell.js +120 -0
  14. package/dist/orchestrator/src/cli/codexCliShell.js +119 -0
  15. package/dist/orchestrator/src/cli/codexDefaultsSetup.js +265 -36
  16. package/dist/orchestrator/src/cli/config/delegationConfig.js +317 -5
  17. package/dist/orchestrator/src/cli/config/repoConfigPolicy.js +2 -3
  18. package/dist/orchestrator/src/cli/config/userConfig.js +28 -13
  19. package/dist/orchestrator/src/cli/control/authenticatedControlRouteGate.js +69 -0
  20. package/dist/orchestrator/src/cli/control/authenticatedRouteComposition.js +267 -0
  21. package/dist/orchestrator/src/cli/control/authenticatedRouteController.js +5 -0
  22. package/dist/orchestrator/src/cli/control/authenticatedRouteDispatcher.js +41 -0
  23. package/dist/orchestrator/src/cli/control/compatibilityIssuePresenter.js +1035 -0
  24. package/dist/orchestrator/src/cli/control/confirmationApproveController.js +62 -0
  25. package/dist/orchestrator/src/cli/control/confirmationCreateController.js +69 -0
  26. package/dist/orchestrator/src/cli/control/confirmationIssueConsumeController.js +43 -0
  27. package/dist/orchestrator/src/cli/control/confirmationListController.js +22 -0
  28. package/dist/orchestrator/src/cli/control/confirmationValidateController.js +58 -0
  29. package/dist/orchestrator/src/cli/control/confirmations.js +25 -3
  30. package/dist/orchestrator/src/cli/control/controlActionCancelConfirmation.js +65 -0
  31. package/dist/orchestrator/src/cli/control/controlActionController.js +77 -0
  32. package/dist/orchestrator/src/cli/control/controlActionControllerSequencing.js +161 -0
  33. package/dist/orchestrator/src/cli/control/controlActionExecution.js +142 -0
  34. package/dist/orchestrator/src/cli/control/controlActionFinalization.js +43 -0
  35. package/dist/orchestrator/src/cli/control/controlActionOutcome.js +60 -0
  36. package/dist/orchestrator/src/cli/control/controlActionPreflight.js +476 -0
  37. package/dist/orchestrator/src/cli/control/controlAuthenticatedRouteHandoff.js +57 -0
  38. package/dist/orchestrator/src/cli/control/controlBootstrapAssembly.js +39 -0
  39. package/dist/orchestrator/src/cli/control/controlBootstrapMetadataPersistence.js +16 -0
  40. package/dist/orchestrator/src/cli/control/controlEventTransport.js +49 -0
  41. package/dist/orchestrator/src/cli/control/controlExpiryLifecycle.js +102 -0
  42. package/dist/orchestrator/src/cli/control/controlHostOwnership.js +480 -0
  43. package/dist/orchestrator/src/cli/control/controlHostSupervision.js +630 -0
  44. package/dist/orchestrator/src/cli/control/controlOversightFacade.js +8 -0
  45. package/dist/orchestrator/src/cli/control/controlOversightReadContract.js +1 -0
  46. package/dist/orchestrator/src/cli/control/controlOversightReadService.js +16 -0
  47. package/dist/orchestrator/src/cli/control/controlOversightUpdateContract.js +1 -0
  48. package/dist/orchestrator/src/cli/control/controlPersistenceFiles.js +6 -0
  49. package/dist/orchestrator/src/cli/control/controlQuestionChildResolution.js +18 -0
  50. package/dist/orchestrator/src/cli/control/controlRequestContext.js +42 -0
  51. package/dist/orchestrator/src/cli/control/controlRequestController.js +9 -0
  52. package/dist/orchestrator/src/cli/control/controlRequestPredispatch.js +17 -0
  53. package/dist/orchestrator/src/cli/control/controlRequestRouteDispatch.js +44 -0
  54. package/dist/orchestrator/src/cli/control/controlRuntime.js +1003 -0
  55. package/dist/orchestrator/src/cli/control/controlServer.js +23 -1456
  56. package/dist/orchestrator/src/cli/control/controlServerAuditAndErrorHelpers.js +115 -0
  57. package/dist/orchestrator/src/cli/control/controlServerAuthenticatedRouteBranch.js +29 -0
  58. package/dist/orchestrator/src/cli/control/controlServerBootstrapLifecycle.js +30 -0
  59. package/dist/orchestrator/src/cli/control/controlServerBootstrapStartSequence.js +21 -0
  60. package/dist/orchestrator/src/cli/control/controlServerOwnedRuntimeLifecycle.js +67 -0
  61. package/dist/orchestrator/src/cli/control/controlServerPublicLifecycle.js +756 -0
  62. package/dist/orchestrator/src/cli/control/controlServerPublicRouteHelpers.js +86 -0
  63. package/dist/orchestrator/src/cli/control/controlServerReadyInstanceLifecycle.js +25 -0
  64. package/dist/orchestrator/src/cli/control/controlServerReadyInstanceStartup.js +18 -0
  65. package/dist/orchestrator/src/cli/control/controlServerRequestBodyHelpers.js +37 -0
  66. package/dist/orchestrator/src/cli/control/controlServerRequestShell.js +40 -0
  67. package/dist/orchestrator/src/cli/control/controlServerRequestShellBinding.js +17 -0
  68. package/dist/orchestrator/src/cli/control/controlServerSeedLoading.js +27 -0
  69. package/dist/orchestrator/src/cli/control/controlServerSeededRuntimeAssembly.js +186 -0
  70. package/dist/orchestrator/src/cli/control/controlServerStartupInputPreparation.js +31 -0
  71. package/dist/orchestrator/src/cli/control/controlServerStartupSequence.js +49 -0
  72. package/dist/orchestrator/src/cli/control/controlState.js +233 -2
  73. package/dist/orchestrator/src/cli/control/controlStatusDashboard.js +1904 -0
  74. package/dist/orchestrator/src/cli/control/controlTelegramBridgeBootstrapLifecycle.js +22 -0
  75. package/dist/orchestrator/src/cli/control/controlTelegramBridgeLifecycle.js +67 -0
  76. package/dist/orchestrator/src/cli/control/controlTelegramBridgeOversightFacadeFactory.js +8 -0
  77. package/dist/orchestrator/src/cli/control/controlTelegramCommandController.js +49 -0
  78. package/dist/orchestrator/src/cli/control/controlTelegramDispatchRead.js +40 -0
  79. package/dist/orchestrator/src/cli/control/controlTelegramPollingController.js +89 -0
  80. package/dist/orchestrator/src/cli/control/controlTelegramProjectionNotificationController.js +29 -0
  81. package/dist/orchestrator/src/cli/control/controlTelegramPushState.js +63 -0
  82. package/dist/orchestrator/src/cli/control/controlTelegramQuestionRead.js +13 -0
  83. package/dist/orchestrator/src/cli/control/controlTelegramReadController.js +216 -0
  84. package/dist/orchestrator/src/cli/control/controlTelegramUpdateHandler.js +63 -0
  85. package/dist/orchestrator/src/cli/control/controlWatcher.js +73 -5
  86. package/dist/orchestrator/src/cli/control/delegationRegisterController.js +35 -0
  87. package/dist/orchestrator/src/cli/control/dynamicToolBridgePolicy.js +139 -0
  88. package/dist/orchestrator/src/cli/control/eventsSseController.js +12 -0
  89. package/dist/orchestrator/src/cli/control/linearBudgetState.js +1789 -0
  90. package/dist/orchestrator/src/cli/control/linearDispatchSource.js +1137 -0
  91. package/dist/orchestrator/src/cli/control/linearGraphqlClient.js +150 -0
  92. package/dist/orchestrator/src/cli/control/linearRateLimit.js +102 -0
  93. package/dist/orchestrator/src/cli/control/linearWebhookController.js +499 -0
  94. package/dist/orchestrator/src/cli/control/liveLinearAdvisoryRuntime.js +70 -0
  95. package/dist/orchestrator/src/cli/control/observabilityApiController.js +173 -0
  96. package/dist/orchestrator/src/cli/control/observabilityReadModel.js +500 -0
  97. package/dist/orchestrator/src/cli/control/observabilitySurface.js +284 -0
  98. package/dist/orchestrator/src/cli/control/observabilityUpdateNotifier.js +22 -0
  99. package/dist/orchestrator/src/cli/control/operatorDashboardPresenter.js +252 -0
  100. package/dist/orchestrator/src/cli/control/providerAgentCapacity.js +70 -0
  101. package/dist/orchestrator/src/cli/control/providerControlHostFreshnessGauge.js +1068 -0
  102. package/dist/orchestrator/src/cli/control/providerIntakeState.js +473 -0
  103. package/dist/orchestrator/src/cli/control/providerIssueHandoff.js +6811 -0
  104. package/dist/orchestrator/src/cli/control/providerIssueObservability.js +1348 -0
  105. package/dist/orchestrator/src/cli/control/providerIssueRetryQueue.js +84 -0
  106. package/dist/orchestrator/src/cli/control/providerLinearRuntimeProof.js +588 -0
  107. package/dist/orchestrator/src/cli/control/providerLinearScreenshotProof.js +473 -0
  108. package/dist/orchestrator/src/cli/control/providerLinearWorkerTruth.js +383 -0
  109. package/dist/orchestrator/src/cli/control/providerLinearWorkflowAudit.js +254 -0
  110. package/dist/orchestrator/src/cli/control/providerLinearWorkflowFacade.js +5573 -0
  111. package/dist/orchestrator/src/cli/control/providerLinearWorkflowStates.js +115 -0
  112. package/dist/orchestrator/src/cli/control/providerMergeCloseout.js +1868 -0
  113. package/dist/orchestrator/src/cli/control/providerOperatorAutopilot.js +1580 -0
  114. package/dist/orchestrator/src/cli/control/providerOperatorAutopilotLifecycle.js +154 -0
  115. package/dist/orchestrator/src/cli/control/providerOperatorAutopilotLocalRolloutExecution.js +1006 -0
  116. package/dist/orchestrator/src/cli/control/providerPollingHealth.js +435 -0
  117. package/dist/orchestrator/src/cli/control/providerTerminalCleanup.js +516 -0
  118. package/dist/orchestrator/src/cli/control/providerWorkerHosts.js +191 -0
  119. package/dist/orchestrator/src/cli/control/providerWorkflowConfigStore.js +515 -0
  120. package/dist/orchestrator/src/cli/control/questionChildResolutionAdapter.js +361 -0
  121. package/dist/orchestrator/src/cli/control/questionQueueController.js +181 -0
  122. package/dist/orchestrator/src/cli/control/questionReadRetryDeduplication.js +9 -0
  123. package/dist/orchestrator/src/cli/control/questionReadSequence.js +10 -0
  124. package/dist/orchestrator/src/cli/control/securityViolationController.js +27 -0
  125. package/dist/orchestrator/src/cli/control/selectedRunProjection.js +1885 -0
  126. package/dist/orchestrator/src/cli/control/telegramOversightApiClient.js +48 -0
  127. package/dist/orchestrator/src/cli/control/telegramOversightBridge.js +180 -0
  128. package/dist/orchestrator/src/cli/control/telegramOversightBridgeProjectionDeliveryQueue.js +25 -0
  129. package/dist/orchestrator/src/cli/control/telegramOversightBridgeRuntimeLifecycle.js +45 -0
  130. package/dist/orchestrator/src/cli/control/telegramOversightBridgeStateStore.js +77 -0
  131. package/dist/orchestrator/src/cli/control/telegramOversightControlActionApiClient.js +45 -0
  132. package/dist/orchestrator/src/cli/control/trackerDispatchPilot.js +439 -0
  133. package/dist/orchestrator/src/cli/control/uiDataController.js +34 -0
  134. package/dist/orchestrator/src/cli/control/uiSessionController.js +100 -0
  135. package/dist/orchestrator/src/cli/controlHostCliShell.js +860 -0
  136. package/dist/orchestrator/src/cli/controlHostFreshnessGaugeCliShell.js +129 -0
  137. package/dist/orchestrator/src/cli/controlHostSupervisionCliShell.js +2127 -0
  138. package/dist/orchestrator/src/cli/delegationCliShell.js +62 -0
  139. package/dist/orchestrator/src/cli/delegationServer.js +567 -678
  140. package/dist/orchestrator/src/cli/delegationServerCliShell.js +52 -0
  141. package/dist/orchestrator/src/cli/delegationServerQuestionFlowShell.js +228 -0
  142. package/dist/orchestrator/src/cli/delegationServerToolDispatchShell.js +411 -0
  143. package/dist/orchestrator/src/cli/delegationServerTransport.js +274 -0
  144. package/dist/orchestrator/src/cli/delegationSetup.js +51 -171
  145. package/dist/orchestrator/src/cli/devtoolsCliShell.js +34 -0
  146. package/dist/orchestrator/src/cli/doctor.js +678 -164
  147. package/dist/orchestrator/src/cli/doctorCliRequestShell.js +72 -0
  148. package/dist/orchestrator/src/cli/doctorCliShell.js +138 -0
  149. package/dist/orchestrator/src/cli/doctorUsage.js +119 -15
  150. package/dist/orchestrator/src/cli/exec/experience.js +16 -2
  151. package/dist/orchestrator/src/cli/exec/summary.js +3 -0
  152. package/dist/orchestrator/src/cli/execCliShell.js +51 -0
  153. package/dist/orchestrator/src/cli/flowCliRequestShell.js +44 -0
  154. package/dist/orchestrator/src/cli/flowCliShell.js +239 -0
  155. package/dist/orchestrator/src/cli/frontendTestCliRequestShell.js +80 -0
  156. package/dist/orchestrator/src/cli/frontendTestCliShell.js +41 -0
  157. package/dist/orchestrator/src/cli/init.js +95 -1
  158. package/dist/orchestrator/src/cli/initCliShell.js +50 -0
  159. package/dist/orchestrator/src/cli/linearCliShell.js +1200 -0
  160. package/dist/orchestrator/src/cli/mcpEnableCliShell.js +132 -0
  161. package/dist/orchestrator/src/cli/metrics/metricsAggregator.js +3 -2
  162. package/dist/orchestrator/src/cli/metrics/metricsRecorder.js +56 -0
  163. package/dist/orchestrator/src/cli/orchestrator.js +66 -1376
  164. package/dist/orchestrator/src/cli/planCliShell.js +19 -0
  165. package/dist/orchestrator/src/cli/prCliShell.js +41 -0
  166. package/dist/orchestrator/src/cli/providerLinearChildLanePhaseContract.js +204 -0
  167. package/dist/orchestrator/src/cli/providerLinearChildLaneRunner.js +1835 -0
  168. package/dist/orchestrator/src/cli/providerLinearChildLaneShell.js +2420 -0
  169. package/dist/orchestrator/src/cli/providerLinearChildStreamShell.js +385 -0
  170. package/dist/orchestrator/src/cli/providerLinearWorkerRunner.js +6834 -0
  171. package/dist/orchestrator/src/cli/resumeCliShell.js +14 -0
  172. package/dist/orchestrator/src/cli/reviewCliLaunchShell.js +72 -0
  173. package/dist/orchestrator/src/cli/rlm/alignment.js +3 -3
  174. package/dist/orchestrator/src/cli/rlm/context.js +94 -7
  175. package/dist/orchestrator/src/cli/rlm/rlmCodexRuntimeShell.js +546 -0
  176. package/dist/orchestrator/src/cli/rlm/symbolic.js +4 -2
  177. package/dist/orchestrator/src/cli/rlmCliRequestShell.js +42 -0
  178. package/dist/orchestrator/src/cli/rlmCompletionCliShell.js +46 -0
  179. package/dist/orchestrator/src/cli/rlmLaunchCliShell.js +51 -0
  180. package/dist/orchestrator/src/cli/rlmRunner.js +83 -523
  181. package/dist/orchestrator/src/cli/run/blockMemory.js +500 -0
  182. package/dist/orchestrator/src/cli/run/manifest.js +410 -73
  183. package/dist/orchestrator/src/cli/run/manifestPersister.js +45 -14
  184. package/dist/orchestrator/src/cli/run/runMemoryController.js +216 -0
  185. package/dist/orchestrator/src/cli/run/source0.js +690 -0
  186. package/dist/orchestrator/src/cli/run/workspacePath.js +101 -0
  187. package/dist/orchestrator/src/cli/runtime/mode.js +2 -1
  188. package/dist/orchestrator/src/cli/runtime/provider.js +39 -2
  189. package/dist/orchestrator/src/cli/selfCheckCliShell.js +12 -0
  190. package/dist/orchestrator/src/cli/services/commandRunner.js +698 -18
  191. package/dist/orchestrator/src/cli/services/execRuntime.js +66 -1
  192. package/dist/orchestrator/src/cli/services/orchestratorAutoScoutEvidenceRecorder.js +71 -0
  193. package/dist/orchestrator/src/cli/services/orchestratorCloudBranchResolution.js +8 -0
  194. package/dist/orchestrator/src/cli/services/orchestratorCloudEnvironmentResolution.js +22 -0
  195. package/dist/orchestrator/src/cli/services/orchestratorCloudExecutionLifecycleShell.js +39 -0
  196. package/dist/orchestrator/src/cli/services/orchestratorCloudPromptBuilder.js +37 -0
  197. package/dist/orchestrator/src/cli/services/orchestratorCloudRouteFallbackContract.js +45 -0
  198. package/dist/orchestrator/src/cli/services/orchestratorCloudRouteShell.js +36 -0
  199. package/dist/orchestrator/src/cli/services/orchestratorCloudTargetExecutor.js +277 -0
  200. package/dist/orchestrator/src/cli/services/orchestratorControlPlaneLifecycle.js +98 -0
  201. package/dist/orchestrator/src/cli/services/orchestratorControlPlaneLifecycleShell.js +54 -0
  202. package/dist/orchestrator/src/cli/services/orchestratorExecutionLifecycle.js +112 -0
  203. package/dist/orchestrator/src/cli/services/orchestratorExecutionModePolicy.js +27 -0
  204. package/dist/orchestrator/src/cli/services/orchestratorExecutionRouteAdapterShell.js +59 -0
  205. package/dist/orchestrator/src/cli/services/orchestratorExecutionRouteDecisionShell.js +57 -0
  206. package/dist/orchestrator/src/cli/services/orchestratorExecutionRouteState.js +21 -0
  207. package/dist/orchestrator/src/cli/services/orchestratorExecutionRouter.js +2 -0
  208. package/dist/orchestrator/src/cli/services/orchestratorLocalPipelineExecutor.js +149 -0
  209. package/dist/orchestrator/src/cli/services/orchestratorLocalRouteShell.js +63 -0
  210. package/dist/orchestrator/src/cli/services/orchestratorPlanShell.js +54 -0
  211. package/dist/orchestrator/src/cli/services/orchestratorPlanTargetTracker.js +16 -0
  212. package/dist/orchestrator/src/cli/services/orchestratorResumePreparationShell.js +84 -0
  213. package/dist/orchestrator/src/cli/services/orchestratorResumeTokenValidation.js +15 -0
  214. package/dist/orchestrator/src/cli/services/orchestratorRunLifecycleCompletion.js +31 -0
  215. package/dist/orchestrator/src/cli/services/orchestratorRunLifecycleExecutionRegistration.js +37 -0
  216. package/dist/orchestrator/src/cli/services/orchestratorRunLifecycleOrchestrationShell.js +83 -0
  217. package/dist/orchestrator/src/cli/services/orchestratorRunLifecycleTaskManagerShell.js +37 -0
  218. package/dist/orchestrator/src/cli/services/orchestratorRuntimeManifestMutation.js +20 -0
  219. package/dist/orchestrator/src/cli/services/orchestratorStartPreparationShell.js +56 -0
  220. package/dist/orchestrator/src/cli/services/orchestratorStatusShell.js +70 -0
  221. package/dist/orchestrator/src/cli/services/pipelineResolver.js +7 -3
  222. package/dist/orchestrator/src/cli/services/plannerMemory.js +119 -0
  223. package/dist/orchestrator/src/cli/services/runPreparation.js +7 -3
  224. package/dist/orchestrator/src/cli/services/runSummaryWriter.js +9 -0
  225. package/dist/orchestrator/src/cli/setupBootstrapShell.js +114 -0
  226. package/dist/orchestrator/src/cli/setupCliShell.js +51 -0
  227. package/dist/orchestrator/src/cli/skillsCliShell.js +56 -0
  228. package/dist/orchestrator/src/cli/startCliRequestShell.js +53 -0
  229. package/dist/orchestrator/src/cli/startCliShell.js +68 -0
  230. package/dist/orchestrator/src/cli/statusCliShell.js +22 -0
  231. package/dist/orchestrator/src/cli/utils/authProvenanceFingerprint.js +27 -0
  232. package/dist/orchestrator/src/cli/utils/cloudPreflight.js +285 -7
  233. package/dist/orchestrator/src/cli/utils/codexFeatures.js +60 -0
  234. package/dist/orchestrator/src/cli/utils/delegationConfigParser.js +250 -0
  235. package/dist/orchestrator/src/cli/utils/delegationMcpHealth.js +1382 -0
  236. package/dist/orchestrator/src/cli/utils/devtools.js +2 -54
  237. package/dist/orchestrator/src/cli/utils/mcpServerEntry.js +53 -0
  238. package/dist/orchestrator/src/cli/utils/packageProgramResolver.js +151 -0
  239. package/dist/orchestrator/src/cli/utils/providerOverrideEnv.js +71 -0
  240. package/dist/orchestrator/src/cli/utils/trailingJsonObject.js +59 -0
  241. package/dist/orchestrator/src/learning/crystalizer.js +2 -2
  242. package/dist/orchestrator/src/manager.js +74 -4
  243. package/dist/orchestrator/src/persistence/ExperienceStore.js +233 -49
  244. package/dist/orchestrator/src/persistence/TaskStateStore.js +6 -6
  245. package/dist/orchestrator/src/persistence/lockFile.js +70 -4
  246. package/dist/orchestrator/src/persistence/sanitizeIdentifier.js +39 -0
  247. package/dist/orchestrator/src/sync/createCloudSyncWorker.js +3 -2
  248. package/dist/orchestrator/src/utils/atomicWrite.js +17 -2
  249. package/dist/packages/orchestrator/src/exec/unified-exec.js +99 -6
  250. package/dist/packages/orchestrator/src/instructions/promptPacks.js +150 -19
  251. package/dist/packages/sdk-node/src/orchestrator.js +137 -13
  252. package/dist/packages/shared/config/designConfig.js +8 -1
  253. package/dist/packages/shared/streams/stdio.js +1 -1
  254. package/dist/scripts/design/pipeline/permit.js +15 -0
  255. package/dist/scripts/lib/docs-catalog.js +399 -0
  256. package/dist/scripts/lib/docs-helpers.js +87 -5
  257. package/dist/scripts/lib/pr-watch-merge.js +1088 -80
  258. package/dist/scripts/lib/provider-run-contract.js +26 -0
  259. package/dist/scripts/lib/review-command-intent-classification.js +532 -0
  260. package/dist/scripts/lib/review-command-probe-classification.js +385 -0
  261. package/dist/scripts/lib/review-execution-boundary-preflight.js +279 -0
  262. package/dist/scripts/lib/review-execution-runtime.js +753 -0
  263. package/dist/scripts/lib/review-execution-state.js +1144 -0
  264. package/dist/scripts/lib/review-execution-telemetry.js +215 -0
  265. package/dist/scripts/lib/review-inspection-target-parsing.js +78 -0
  266. package/dist/scripts/lib/review-launch-attempt.js +601 -0
  267. package/dist/scripts/lib/review-meta-surface-boundary-analysis.js +300 -0
  268. package/dist/scripts/lib/review-meta-surface-normalization.js +746 -0
  269. package/dist/scripts/lib/review-non-interactive-handoff.js +61 -0
  270. package/dist/scripts/lib/review-prompt-context.js +376 -0
  271. package/dist/scripts/lib/review-scope-advisory.js +286 -0
  272. package/dist/scripts/lib/review-scope-paths.js +123 -0
  273. package/dist/scripts/lib/review-shell-command-parser.js +389 -0
  274. package/dist/scripts/lib/review-shell-env-interpreter.js +340 -0
  275. package/dist/scripts/lib/run-manifests.js +192 -36
  276. package/dist/scripts/lib/spark-policy-classifier.js +593 -0
  277. package/dist/scripts/run-review.js +507 -1777
  278. package/docs/README.md +43 -20
  279. package/docs/book/README.md +19 -0
  280. package/docs/book/codex-cli-0124-adoption.md +68 -0
  281. package/docs/book/local-hook-impact.md +73 -0
  282. package/docs/book/operations.md +60 -0
  283. package/docs/book/public-posture.md +34 -0
  284. package/docs/book/setup.md +91 -0
  285. package/docs/book/skills.md +11 -0
  286. package/docs/guides/codex-version-policy.md +104 -0
  287. package/docs/public/downstream-setup.md +113 -0
  288. package/docs/public/provider-onboarding.md +173 -0
  289. package/package.json +23 -10
  290. package/plugins/codex-orchestrator/.codex-plugin/plugin.json +30 -0
  291. package/plugins/codex-orchestrator/.mcp.json +13 -0
  292. package/plugins/codex-orchestrator/launcher.mjs +361 -0
  293. package/schemas/manifest.json +411 -0
  294. package/skills/README.md +26 -0
  295. package/skills/collab-subagents-first/SKILL.md +1 -1
  296. package/skills/delegation-usage/DELEGATION_GUIDE.md +30 -12
  297. package/skills/delegation-usage/SKILL.md +25 -14
  298. package/skills/land/SKILL.md +77 -0
  299. package/skills/linear/SKILL.md +255 -0
  300. package/skills/release/SKILL.md +47 -3
  301. package/skills/standalone-review/SKILL.md +6 -1
  302. package/templates/README.md +4 -2
  303. package/templates/codex/.codex/agents/awaiter-high.toml +2 -2
  304. package/templates/codex/.codex/agents/worker-complex.toml +1 -1
  305. package/templates/codex/.codex/config.toml +3 -4
  306. package/templates/codex/.codex/providers/README.md +13 -0
  307. package/templates/codex/.codex/providers/control.example.json +18 -0
  308. package/templates/codex/.codex/providers/provider.env.example +15 -0
  309. package/templates/codex/AGENTS.md +15 -8
  310. package/templates/codex/mcp-client.json +5 -1
  311. 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 MAX_MCP_MESSAGE_BYTES = 1024 * 1024;
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 = async (request) => {
60
- switch (request.method) {
61
- case 'initialize':
62
- return {
63
- protocolVersion: PROTOCOL_VERSION,
64
- serverInfo: { name: 'codex-delegation', version: '0.1.0' },
65
- capabilities: { tools: {} }
66
- };
67
- case 'tools/list':
68
- return { tools };
69
- case 'tools/call':
70
- return await handleToolCall(request, {
71
- repoRoot,
72
- mode,
73
- allowNested,
74
- githubEnabled,
75
- allowedGithubOps,
76
- allowedRoots,
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
- const params = asRecord(request.params);
220
- const toolName = readStringValue(params, 'name');
221
- if (!toolName) {
222
- throw new Error('Invalid tool call: missing name');
223
- }
224
- const input = asRecord(params.arguments);
225
- if (context.mode === 'question_only' && isRestrictedTool(toolName)) {
226
- await reportSecurityViolation('delegate_mode_violation', `Tool ${toolName} blocked in question_only mode.`, toolName, context.allowedHosts);
227
- throw new Error('delegate_mode_forbidden');
228
- }
229
- if (containsSecret(input, 'confirm_nonce') || containsSecret(input, 'confirmNonce')) {
230
- await reportSecurityViolation('confirm_nonce_present', 'Model supplied confirm_nonce.', toolName, context.allowedHosts);
231
- throw new Error('confirm_nonce must be injected by the runner');
232
- }
233
- if (containsSecret(input, 'delegation_token') || containsSecret(input, 'delegationToken')) {
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 raw = await readFile(manifestPath, 'utf8');
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
- return await callControlEndpoint(manifestPath, '/control/action', {
293
- action: paused ? 'pause' : 'resume',
294
- requested_by: 'delegate'
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
- return await callControlEndpoint(manifestPath, '/confirmations/create', {
190
+ const confirmation = await callControlEndpoint(manifestPath, '/confirmations/create', {
302
191
  action: 'cancel',
303
192
  tool: 'delegate.cancel',
304
- params: { manifest_path: manifestPath }
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
- return await callControlEndpoint(manifestPath, '/control/action', {
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: { manifest_path: manifestPath }
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
- return await callControlEndpoint(manifestPath, '/confirmations/create', {
283
+ const confirmation = await callControlEndpoint(manifestPath, '/confirmations/create', {
321
284
  action: 'cancel',
322
285
  tool: 'delegate.cancel',
323
- params: { manifest_path: manifestPath }
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 = allowNested && requestedMode === 'full' ? 'full' : 'question_only';
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
- const parentManifestPath = resolveParentManifestPath(input, allowedRoots);
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
- const parentManifestPath = resolveParentManifestPath(input, allowedRoots);
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
- const trimmed = stdout.trim();
1105
- if (!trimmed) {
1106
- return {};
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 direct = safeJsonParse(trimmed);
1109
- if (direct && typeof direct === 'object' && !Array.isArray(direct)) {
1110
- return direct;
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
- const lines = trimmed.split(/\r?\n/);
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
- const privateToken = request.codex_private?.delegation_token;
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
- assertRunManifestPath(resolved, label);
1583
- if (allowedRoots && !isPathWithinRoots(resolved, allowedRoots)) {
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 resolved;
1514
+ return canonicalPath;
1587
1515
  }
1588
1516
  function assertRunManifestPath(pathname, label) {
1589
- if (basename(pathname) !== 'manifest.json') {
1517
+ const resolvedPath = resolve(pathname);
1518
+ if (basename(resolvedPath) !== 'manifest.json') {
1590
1519
  throw new Error(`${label} invalid`);
1591
1520
  }
1592
- const runDir = dirname(pathname);
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) || !basename(runsDir)) {
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
- assertRunManifestPath(resolved, 'manifest_path');
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
- const manifestPath = process.env.CODEX_ORCHESTRATOR_MANIFEST_PATH;
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
  }