@kbediako/codex-orchestrator 0.1.37 → 0.2.0

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 (302) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/README.md +73 -291
  3. package/bin/codex-orchestrator.js +161 -0
  4. package/codex.orchestrator.json +149 -13
  5. package/dist/bin/codex-orchestrator.js +795 -1154
  6. package/dist/orchestrator/src/cli/adapters/CommandPlanner.js +22 -4
  7. package/dist/orchestrator/src/cli/adapters/CommandReviewer.js +3 -3
  8. package/dist/orchestrator/src/cli/adapters/CommandTester.js +2 -2
  9. package/dist/orchestrator/src/cli/adapters/cloudFailureDiagnostics.js +183 -11
  10. package/dist/orchestrator/src/cli/coStatusAttachCliShell.js +402 -0
  11. package/dist/orchestrator/src/cli/coStatusCliShell.js +429 -0
  12. package/dist/orchestrator/src/cli/coStatusOperatorAutopilotCliShell.js +120 -0
  13. package/dist/orchestrator/src/cli/codexCliShell.js +72 -0
  14. package/dist/orchestrator/src/cli/codexDefaultsSetup.js +49 -11
  15. package/dist/orchestrator/src/cli/config/delegationConfig.js +317 -5
  16. package/dist/orchestrator/src/cli/config/repoConfigPolicy.js +2 -3
  17. package/dist/orchestrator/src/cli/config/userConfig.js +28 -13
  18. package/dist/orchestrator/src/cli/control/authenticatedControlRouteGate.js +69 -0
  19. package/dist/orchestrator/src/cli/control/authenticatedRouteComposition.js +267 -0
  20. package/dist/orchestrator/src/cli/control/authenticatedRouteController.js +5 -0
  21. package/dist/orchestrator/src/cli/control/authenticatedRouteDispatcher.js +41 -0
  22. package/dist/orchestrator/src/cli/control/compatibilityIssuePresenter.js +1035 -0
  23. package/dist/orchestrator/src/cli/control/confirmationApproveController.js +62 -0
  24. package/dist/orchestrator/src/cli/control/confirmationCreateController.js +69 -0
  25. package/dist/orchestrator/src/cli/control/confirmationIssueConsumeController.js +43 -0
  26. package/dist/orchestrator/src/cli/control/confirmationListController.js +22 -0
  27. package/dist/orchestrator/src/cli/control/confirmationValidateController.js +58 -0
  28. package/dist/orchestrator/src/cli/control/confirmations.js +25 -3
  29. package/dist/orchestrator/src/cli/control/controlActionCancelConfirmation.js +65 -0
  30. package/dist/orchestrator/src/cli/control/controlActionController.js +77 -0
  31. package/dist/orchestrator/src/cli/control/controlActionControllerSequencing.js +161 -0
  32. package/dist/orchestrator/src/cli/control/controlActionExecution.js +142 -0
  33. package/dist/orchestrator/src/cli/control/controlActionFinalization.js +43 -0
  34. package/dist/orchestrator/src/cli/control/controlActionOutcome.js +60 -0
  35. package/dist/orchestrator/src/cli/control/controlActionPreflight.js +476 -0
  36. package/dist/orchestrator/src/cli/control/controlAuthenticatedRouteHandoff.js +57 -0
  37. package/dist/orchestrator/src/cli/control/controlBootstrapAssembly.js +39 -0
  38. package/dist/orchestrator/src/cli/control/controlBootstrapMetadataPersistence.js +16 -0
  39. package/dist/orchestrator/src/cli/control/controlEventTransport.js +49 -0
  40. package/dist/orchestrator/src/cli/control/controlExpiryLifecycle.js +102 -0
  41. package/dist/orchestrator/src/cli/control/controlHostOwnership.js +480 -0
  42. package/dist/orchestrator/src/cli/control/controlHostSupervision.js +608 -0
  43. package/dist/orchestrator/src/cli/control/controlOversightFacade.js +8 -0
  44. package/dist/orchestrator/src/cli/control/controlOversightReadContract.js +1 -0
  45. package/dist/orchestrator/src/cli/control/controlOversightReadService.js +16 -0
  46. package/dist/orchestrator/src/cli/control/controlOversightUpdateContract.js +1 -0
  47. package/dist/orchestrator/src/cli/control/controlPersistenceFiles.js +6 -0
  48. package/dist/orchestrator/src/cli/control/controlQuestionChildResolution.js +18 -0
  49. package/dist/orchestrator/src/cli/control/controlRequestContext.js +42 -0
  50. package/dist/orchestrator/src/cli/control/controlRequestController.js +9 -0
  51. package/dist/orchestrator/src/cli/control/controlRequestPredispatch.js +17 -0
  52. package/dist/orchestrator/src/cli/control/controlRequestRouteDispatch.js +44 -0
  53. package/dist/orchestrator/src/cli/control/controlRuntime.js +992 -0
  54. package/dist/orchestrator/src/cli/control/controlServer.js +23 -1456
  55. package/dist/orchestrator/src/cli/control/controlServerAuditAndErrorHelpers.js +115 -0
  56. package/dist/orchestrator/src/cli/control/controlServerAuthenticatedRouteBranch.js +29 -0
  57. package/dist/orchestrator/src/cli/control/controlServerBootstrapLifecycle.js +30 -0
  58. package/dist/orchestrator/src/cli/control/controlServerBootstrapStartSequence.js +21 -0
  59. package/dist/orchestrator/src/cli/control/controlServerOwnedRuntimeLifecycle.js +67 -0
  60. package/dist/orchestrator/src/cli/control/controlServerPublicLifecycle.js +756 -0
  61. package/dist/orchestrator/src/cli/control/controlServerPublicRouteHelpers.js +86 -0
  62. package/dist/orchestrator/src/cli/control/controlServerReadyInstanceLifecycle.js +25 -0
  63. package/dist/orchestrator/src/cli/control/controlServerReadyInstanceStartup.js +18 -0
  64. package/dist/orchestrator/src/cli/control/controlServerRequestBodyHelpers.js +37 -0
  65. package/dist/orchestrator/src/cli/control/controlServerRequestShell.js +40 -0
  66. package/dist/orchestrator/src/cli/control/controlServerRequestShellBinding.js +17 -0
  67. package/dist/orchestrator/src/cli/control/controlServerSeedLoading.js +27 -0
  68. package/dist/orchestrator/src/cli/control/controlServerSeededRuntimeAssembly.js +186 -0
  69. package/dist/orchestrator/src/cli/control/controlServerStartupInputPreparation.js +31 -0
  70. package/dist/orchestrator/src/cli/control/controlServerStartupSequence.js +49 -0
  71. package/dist/orchestrator/src/cli/control/controlState.js +233 -2
  72. package/dist/orchestrator/src/cli/control/controlStatusDashboard.js +1899 -0
  73. package/dist/orchestrator/src/cli/control/controlTelegramBridgeBootstrapLifecycle.js +22 -0
  74. package/dist/orchestrator/src/cli/control/controlTelegramBridgeLifecycle.js +67 -0
  75. package/dist/orchestrator/src/cli/control/controlTelegramBridgeOversightFacadeFactory.js +8 -0
  76. package/dist/orchestrator/src/cli/control/controlTelegramCommandController.js +49 -0
  77. package/dist/orchestrator/src/cli/control/controlTelegramDispatchRead.js +40 -0
  78. package/dist/orchestrator/src/cli/control/controlTelegramPollingController.js +89 -0
  79. package/dist/orchestrator/src/cli/control/controlTelegramProjectionNotificationController.js +29 -0
  80. package/dist/orchestrator/src/cli/control/controlTelegramPushState.js +63 -0
  81. package/dist/orchestrator/src/cli/control/controlTelegramQuestionRead.js +13 -0
  82. package/dist/orchestrator/src/cli/control/controlTelegramReadController.js +216 -0
  83. package/dist/orchestrator/src/cli/control/controlTelegramUpdateHandler.js +63 -0
  84. package/dist/orchestrator/src/cli/control/controlWatcher.js +73 -5
  85. package/dist/orchestrator/src/cli/control/delegationRegisterController.js +35 -0
  86. package/dist/orchestrator/src/cli/control/dynamicToolBridgePolicy.js +139 -0
  87. package/dist/orchestrator/src/cli/control/eventsSseController.js +12 -0
  88. package/dist/orchestrator/src/cli/control/linearBudgetState.js +1789 -0
  89. package/dist/orchestrator/src/cli/control/linearDispatchSource.js +1137 -0
  90. package/dist/orchestrator/src/cli/control/linearGraphqlClient.js +150 -0
  91. package/dist/orchestrator/src/cli/control/linearRateLimit.js +102 -0
  92. package/dist/orchestrator/src/cli/control/linearWebhookController.js +499 -0
  93. package/dist/orchestrator/src/cli/control/liveLinearAdvisoryRuntime.js +70 -0
  94. package/dist/orchestrator/src/cli/control/observabilityApiController.js +173 -0
  95. package/dist/orchestrator/src/cli/control/observabilityReadModel.js +500 -0
  96. package/dist/orchestrator/src/cli/control/observabilitySurface.js +284 -0
  97. package/dist/orchestrator/src/cli/control/observabilityUpdateNotifier.js +22 -0
  98. package/dist/orchestrator/src/cli/control/operatorDashboardPresenter.js +252 -0
  99. package/dist/orchestrator/src/cli/control/providerAgentCapacity.js +70 -0
  100. package/dist/orchestrator/src/cli/control/providerControlHostFreshnessGauge.js +1068 -0
  101. package/dist/orchestrator/src/cli/control/providerIntakeState.js +473 -0
  102. package/dist/orchestrator/src/cli/control/providerIssueHandoff.js +6811 -0
  103. package/dist/orchestrator/src/cli/control/providerIssueObservability.js +1348 -0
  104. package/dist/orchestrator/src/cli/control/providerIssueRetryQueue.js +84 -0
  105. package/dist/orchestrator/src/cli/control/providerLinearRuntimeProof.js +588 -0
  106. package/dist/orchestrator/src/cli/control/providerLinearScreenshotProof.js +473 -0
  107. package/dist/orchestrator/src/cli/control/providerLinearWorkerTruth.js +383 -0
  108. package/dist/orchestrator/src/cli/control/providerLinearWorkflowAudit.js +254 -0
  109. package/dist/orchestrator/src/cli/control/providerLinearWorkflowFacade.js +5573 -0
  110. package/dist/orchestrator/src/cli/control/providerLinearWorkflowStates.js +115 -0
  111. package/dist/orchestrator/src/cli/control/providerMergeCloseout.js +1868 -0
  112. package/dist/orchestrator/src/cli/control/providerOperatorAutopilot.js +1580 -0
  113. package/dist/orchestrator/src/cli/control/providerOperatorAutopilotLifecycle.js +154 -0
  114. package/dist/orchestrator/src/cli/control/providerOperatorAutopilotLocalRolloutExecution.js +1006 -0
  115. package/dist/orchestrator/src/cli/control/providerPollingHealth.js +435 -0
  116. package/dist/orchestrator/src/cli/control/providerTerminalCleanup.js +516 -0
  117. package/dist/orchestrator/src/cli/control/providerWorkerHosts.js +191 -0
  118. package/dist/orchestrator/src/cli/control/providerWorkflowConfigStore.js +515 -0
  119. package/dist/orchestrator/src/cli/control/questionChildResolutionAdapter.js +361 -0
  120. package/dist/orchestrator/src/cli/control/questionQueueController.js +181 -0
  121. package/dist/orchestrator/src/cli/control/questionReadRetryDeduplication.js +9 -0
  122. package/dist/orchestrator/src/cli/control/questionReadSequence.js +10 -0
  123. package/dist/orchestrator/src/cli/control/securityViolationController.js +27 -0
  124. package/dist/orchestrator/src/cli/control/selectedRunProjection.js +1838 -0
  125. package/dist/orchestrator/src/cli/control/telegramOversightApiClient.js +48 -0
  126. package/dist/orchestrator/src/cli/control/telegramOversightBridge.js +180 -0
  127. package/dist/orchestrator/src/cli/control/telegramOversightBridgeProjectionDeliveryQueue.js +25 -0
  128. package/dist/orchestrator/src/cli/control/telegramOversightBridgeRuntimeLifecycle.js +45 -0
  129. package/dist/orchestrator/src/cli/control/telegramOversightBridgeStateStore.js +77 -0
  130. package/dist/orchestrator/src/cli/control/telegramOversightControlActionApiClient.js +45 -0
  131. package/dist/orchestrator/src/cli/control/trackerDispatchPilot.js +439 -0
  132. package/dist/orchestrator/src/cli/control/uiDataController.js +34 -0
  133. package/dist/orchestrator/src/cli/control/uiSessionController.js +100 -0
  134. package/dist/orchestrator/src/cli/controlHostCliShell.js +860 -0
  135. package/dist/orchestrator/src/cli/controlHostFreshnessGaugeCliShell.js +129 -0
  136. package/dist/orchestrator/src/cli/controlHostSupervisionCliShell.js +2127 -0
  137. package/dist/orchestrator/src/cli/delegationCliShell.js +62 -0
  138. package/dist/orchestrator/src/cli/delegationServer.js +567 -678
  139. package/dist/orchestrator/src/cli/delegationServerCliShell.js +52 -0
  140. package/dist/orchestrator/src/cli/delegationServerQuestionFlowShell.js +228 -0
  141. package/dist/orchestrator/src/cli/delegationServerToolDispatchShell.js +411 -0
  142. package/dist/orchestrator/src/cli/delegationServerTransport.js +274 -0
  143. package/dist/orchestrator/src/cli/delegationSetup.js +51 -171
  144. package/dist/orchestrator/src/cli/devtoolsCliShell.js +34 -0
  145. package/dist/orchestrator/src/cli/doctor.js +542 -122
  146. package/dist/orchestrator/src/cli/doctorCliRequestShell.js +72 -0
  147. package/dist/orchestrator/src/cli/doctorCliShell.js +138 -0
  148. package/dist/orchestrator/src/cli/doctorUsage.js +136 -16
  149. package/dist/orchestrator/src/cli/exec/experience.js +16 -2
  150. package/dist/orchestrator/src/cli/exec/summary.js +3 -0
  151. package/dist/orchestrator/src/cli/execCliShell.js +51 -0
  152. package/dist/orchestrator/src/cli/flowCliRequestShell.js +44 -0
  153. package/dist/orchestrator/src/cli/flowCliShell.js +239 -0
  154. package/dist/orchestrator/src/cli/frontendTestCliRequestShell.js +80 -0
  155. package/dist/orchestrator/src/cli/frontendTestCliShell.js +41 -0
  156. package/dist/orchestrator/src/cli/init.js +1 -0
  157. package/dist/orchestrator/src/cli/initCliShell.js +50 -0
  158. package/dist/orchestrator/src/cli/linearCliShell.js +1200 -0
  159. package/dist/orchestrator/src/cli/mcpEnableCliShell.js +132 -0
  160. package/dist/orchestrator/src/cli/metrics/metricsAggregator.js +3 -2
  161. package/dist/orchestrator/src/cli/metrics/metricsRecorder.js +56 -0
  162. package/dist/orchestrator/src/cli/orchestrator.js +66 -1376
  163. package/dist/orchestrator/src/cli/planCliShell.js +19 -0
  164. package/dist/orchestrator/src/cli/prCliShell.js +41 -0
  165. package/dist/orchestrator/src/cli/providerLinearChildLanePhaseContract.js +204 -0
  166. package/dist/orchestrator/src/cli/providerLinearChildLaneRunner.js +1772 -0
  167. package/dist/orchestrator/src/cli/providerLinearChildLaneShell.js +2420 -0
  168. package/dist/orchestrator/src/cli/providerLinearChildStreamShell.js +385 -0
  169. package/dist/orchestrator/src/cli/providerLinearWorkerRunner.js +5738 -0
  170. package/dist/orchestrator/src/cli/resumeCliShell.js +14 -0
  171. package/dist/orchestrator/src/cli/reviewCliLaunchShell.js +72 -0
  172. package/dist/orchestrator/src/cli/rlm/alignment.js +3 -3
  173. package/dist/orchestrator/src/cli/rlm/context.js +94 -7
  174. package/dist/orchestrator/src/cli/rlm/rlmCodexRuntimeShell.js +546 -0
  175. package/dist/orchestrator/src/cli/rlm/symbolic.js +4 -2
  176. package/dist/orchestrator/src/cli/rlmCliRequestShell.js +42 -0
  177. package/dist/orchestrator/src/cli/rlmCompletionCliShell.js +46 -0
  178. package/dist/orchestrator/src/cli/rlmLaunchCliShell.js +51 -0
  179. package/dist/orchestrator/src/cli/rlmRunner.js +83 -523
  180. package/dist/orchestrator/src/cli/run/blockMemory.js +500 -0
  181. package/dist/orchestrator/src/cli/run/manifest.js +410 -73
  182. package/dist/orchestrator/src/cli/run/manifestPersister.js +45 -14
  183. package/dist/orchestrator/src/cli/run/runMemoryController.js +216 -0
  184. package/dist/orchestrator/src/cli/run/source0.js +690 -0
  185. package/dist/orchestrator/src/cli/run/workspacePath.js +101 -0
  186. package/dist/orchestrator/src/cli/runtime/mode.js +2 -1
  187. package/dist/orchestrator/src/cli/runtime/provider.js +39 -2
  188. package/dist/orchestrator/src/cli/selfCheckCliShell.js +12 -0
  189. package/dist/orchestrator/src/cli/services/commandRunner.js +668 -18
  190. package/dist/orchestrator/src/cli/services/execRuntime.js +66 -1
  191. package/dist/orchestrator/src/cli/services/orchestratorAutoScoutEvidenceRecorder.js +71 -0
  192. package/dist/orchestrator/src/cli/services/orchestratorCloudBranchResolution.js +8 -0
  193. package/dist/orchestrator/src/cli/services/orchestratorCloudEnvironmentResolution.js +22 -0
  194. package/dist/orchestrator/src/cli/services/orchestratorCloudExecutionLifecycleShell.js +39 -0
  195. package/dist/orchestrator/src/cli/services/orchestratorCloudPromptBuilder.js +37 -0
  196. package/dist/orchestrator/src/cli/services/orchestratorCloudRouteFallbackContract.js +45 -0
  197. package/dist/orchestrator/src/cli/services/orchestratorCloudRouteShell.js +36 -0
  198. package/dist/orchestrator/src/cli/services/orchestratorCloudTargetExecutor.js +277 -0
  199. package/dist/orchestrator/src/cli/services/orchestratorControlPlaneLifecycle.js +98 -0
  200. package/dist/orchestrator/src/cli/services/orchestratorControlPlaneLifecycleShell.js +54 -0
  201. package/dist/orchestrator/src/cli/services/orchestratorExecutionLifecycle.js +112 -0
  202. package/dist/orchestrator/src/cli/services/orchestratorExecutionModePolicy.js +27 -0
  203. package/dist/orchestrator/src/cli/services/orchestratorExecutionRouteAdapterShell.js +59 -0
  204. package/dist/orchestrator/src/cli/services/orchestratorExecutionRouteDecisionShell.js +57 -0
  205. package/dist/orchestrator/src/cli/services/orchestratorExecutionRouteState.js +21 -0
  206. package/dist/orchestrator/src/cli/services/orchestratorExecutionRouter.js +2 -0
  207. package/dist/orchestrator/src/cli/services/orchestratorLocalPipelineExecutor.js +149 -0
  208. package/dist/orchestrator/src/cli/services/orchestratorLocalRouteShell.js +63 -0
  209. package/dist/orchestrator/src/cli/services/orchestratorPlanShell.js +54 -0
  210. package/dist/orchestrator/src/cli/services/orchestratorPlanTargetTracker.js +16 -0
  211. package/dist/orchestrator/src/cli/services/orchestratorResumePreparationShell.js +84 -0
  212. package/dist/orchestrator/src/cli/services/orchestratorResumeTokenValidation.js +15 -0
  213. package/dist/orchestrator/src/cli/services/orchestratorRunLifecycleCompletion.js +31 -0
  214. package/dist/orchestrator/src/cli/services/orchestratorRunLifecycleExecutionRegistration.js +37 -0
  215. package/dist/orchestrator/src/cli/services/orchestratorRunLifecycleOrchestrationShell.js +83 -0
  216. package/dist/orchestrator/src/cli/services/orchestratorRunLifecycleTaskManagerShell.js +37 -0
  217. package/dist/orchestrator/src/cli/services/orchestratorRuntimeManifestMutation.js +20 -0
  218. package/dist/orchestrator/src/cli/services/orchestratorStartPreparationShell.js +56 -0
  219. package/dist/orchestrator/src/cli/services/orchestratorStatusShell.js +70 -0
  220. package/dist/orchestrator/src/cli/services/pipelineResolver.js +7 -3
  221. package/dist/orchestrator/src/cli/services/plannerMemory.js +119 -0
  222. package/dist/orchestrator/src/cli/services/runPreparation.js +7 -3
  223. package/dist/orchestrator/src/cli/services/runSummaryWriter.js +9 -0
  224. package/dist/orchestrator/src/cli/setupBootstrapShell.js +114 -0
  225. package/dist/orchestrator/src/cli/setupCliShell.js +51 -0
  226. package/dist/orchestrator/src/cli/skillsCliShell.js +56 -0
  227. package/dist/orchestrator/src/cli/startCliRequestShell.js +53 -0
  228. package/dist/orchestrator/src/cli/startCliShell.js +68 -0
  229. package/dist/orchestrator/src/cli/statusCliShell.js +22 -0
  230. package/dist/orchestrator/src/cli/utils/authProvenanceFingerprint.js +27 -0
  231. package/dist/orchestrator/src/cli/utils/cloudPreflight.js +83 -1
  232. package/dist/orchestrator/src/cli/utils/delegationConfigParser.js +250 -0
  233. package/dist/orchestrator/src/cli/utils/delegationMcpHealth.js +1382 -0
  234. package/dist/orchestrator/src/cli/utils/devtools.js +2 -54
  235. package/dist/orchestrator/src/cli/utils/mcpServerEntry.js +53 -0
  236. package/dist/orchestrator/src/cli/utils/packageProgramResolver.js +151 -0
  237. package/dist/orchestrator/src/cli/utils/providerOverrideEnv.js +71 -0
  238. package/dist/orchestrator/src/cli/utils/trailingJsonObject.js +59 -0
  239. package/dist/orchestrator/src/learning/crystalizer.js +2 -2
  240. package/dist/orchestrator/src/persistence/ExperienceStore.js +233 -49
  241. package/dist/orchestrator/src/persistence/TaskStateStore.js +6 -6
  242. package/dist/orchestrator/src/persistence/lockFile.js +70 -4
  243. package/dist/orchestrator/src/persistence/sanitizeIdentifier.js +39 -0
  244. package/dist/orchestrator/src/sync/createCloudSyncWorker.js +3 -2
  245. package/dist/orchestrator/src/utils/atomicWrite.js +17 -2
  246. package/dist/packages/orchestrator/src/exec/unified-exec.js +99 -6
  247. package/dist/packages/orchestrator/src/instructions/promptPacks.js +150 -19
  248. package/dist/packages/sdk-node/src/orchestrator.js +137 -13
  249. package/dist/packages/shared/config/designConfig.js +8 -1
  250. package/dist/packages/shared/streams/stdio.js +1 -1
  251. package/dist/scripts/design/pipeline/permit.js +15 -0
  252. package/dist/scripts/lib/docs-catalog.js +365 -0
  253. package/dist/scripts/lib/docs-helpers.js +87 -5
  254. package/dist/scripts/lib/pr-watch-merge.js +1088 -80
  255. package/dist/scripts/lib/provider-run-contract.js +26 -0
  256. package/dist/scripts/lib/review-command-intent-classification.js +532 -0
  257. package/dist/scripts/lib/review-command-probe-classification.js +385 -0
  258. package/dist/scripts/lib/review-execution-boundary-preflight.js +279 -0
  259. package/dist/scripts/lib/review-execution-runtime.js +753 -0
  260. package/dist/scripts/lib/review-execution-state.js +1144 -0
  261. package/dist/scripts/lib/review-execution-telemetry.js +215 -0
  262. package/dist/scripts/lib/review-inspection-target-parsing.js +78 -0
  263. package/dist/scripts/lib/review-launch-attempt.js +601 -0
  264. package/dist/scripts/lib/review-meta-surface-boundary-analysis.js +300 -0
  265. package/dist/scripts/lib/review-meta-surface-normalization.js +746 -0
  266. package/dist/scripts/lib/review-non-interactive-handoff.js +61 -0
  267. package/dist/scripts/lib/review-prompt-context.js +376 -0
  268. package/dist/scripts/lib/review-scope-advisory.js +286 -0
  269. package/dist/scripts/lib/review-scope-paths.js +123 -0
  270. package/dist/scripts/lib/review-shell-command-parser.js +389 -0
  271. package/dist/scripts/lib/review-shell-env-interpreter.js +340 -0
  272. package/dist/scripts/lib/run-manifests.js +192 -36
  273. package/dist/scripts/lib/spark-policy-classifier.js +593 -0
  274. package/dist/scripts/run-review.js +507 -1777
  275. package/docs/public/downstream-setup.md +106 -0
  276. package/docs/public/provider-onboarding.md +173 -0
  277. package/package.json +30 -11
  278. package/plugins/codex-orchestrator/.codex-plugin/plugin.json +30 -0
  279. package/plugins/codex-orchestrator/.mcp.json +13 -0
  280. package/plugins/codex-orchestrator/launcher.mjs +359 -0
  281. package/schemas/manifest.json +395 -0
  282. package/skills/chrome-devtools/SKILL.md +1 -1
  283. package/skills/codex-orchestrator/SKILL.md +83 -0
  284. package/skills/collab-subagents-first/SKILL.md +2 -1
  285. package/skills/delegation-usage/DELEGATION_GUIDE.md +24 -11
  286. package/skills/delegation-usage/SKILL.md +20 -13
  287. package/skills/land/SKILL.md +77 -0
  288. package/skills/linear/SKILL.md +255 -0
  289. package/skills/release/SKILL.md +47 -3
  290. package/skills/standalone-review/SKILL.md +6 -1
  291. package/templates/README.md +4 -2
  292. package/templates/codex/.codex/agents/awaiter-high.toml +2 -2
  293. package/templates/codex/.codex/agents/explorer-fast.toml +1 -0
  294. package/templates/codex/.codex/agents/worker-complex.toml +1 -1
  295. package/templates/codex/.codex/config.toml +3 -4
  296. package/templates/codex/.codex/providers/README.md +13 -0
  297. package/templates/codex/.codex/providers/control.example.json +18 -0
  298. package/templates/codex/.codex/providers/provider.env.example +15 -0
  299. package/templates/codex/AGENTS.md +12 -7
  300. package/templates/codex/mcp-client.json +5 -1
  301. package/docs/README.md +0 -307
  302. package/docs/assets/setup.gif +0 -0
@@ -0,0 +1,1772 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { mkdir, mkdtemp, open, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises';
3
+ import { createRequire } from 'node:module';
4
+ import { tmpdir } from 'node:os';
5
+ import { basename, dirname, isAbsolute, join, parse as parsePath, relative, resolve } from 'node:path';
6
+ import process from 'node:process';
7
+ import { promisify } from 'node:util';
8
+ import { fileURLToPath } from 'node:url';
9
+ import { logger } from '../logger.js';
10
+ import { acquireLockWithRetry } from '../persistence/lockFile.js';
11
+ import { writeAtomicFile } from '../utils/atomicWrite.js';
12
+ import { buildEmptyProviderLinearWorkerTokenUsage, defaultExecRunner, parseProviderLinearWorkerJsonl } from './providerLinearWorkerRunner.js';
13
+ import { createRuntimeCodexCommandContext, formatRuntimeSelectionSummary, parseRuntimeMode, resolveRuntimeCodexCommand } from './runtime/index.js';
14
+ import { buildRunMemoryPromptLines, selectRunMemoryForRole } from './run/runMemoryController.js';
15
+ import { resolveProviderLinearChildLaneScopeContract } from './providerLinearChildLanePhaseContract.js';
16
+ import { resolveCodexHome } from './utils/codexPaths.js';
17
+ const execFileAsync = promisify(execFile);
18
+ const require = createRequire(import.meta.url);
19
+ const PROVIDER_LINEAR_CHILD_LANE_APPSERVER_STARTUP_TIMEOUT_MS = 90 * 1000;
20
+ const PROVIDER_LINEAR_CHILD_LANE_APPSERVER_STARTUP_POLL_INTERVAL_MS = 250;
21
+ const PROVIDER_LINEAR_CHILD_LANE_SCOPE_DRIFT_POLL_INTERVAL_MS = 250;
22
+ const PROVIDER_LINEAR_CHILD_LANE_SESSION_LOG_DISCOVERY_WINDOW_MS = 10 * 60 * 1000;
23
+ const PROVIDER_LINEAR_CHILD_LANE_SESSION_LOG_HEADER_BYTES = 256 * 1024;
24
+ const PROVIDER_LINEAR_CHILD_LANE_SESSION_LOG_MTIME_SKEW_MS = 1000;
25
+ let tomlParser;
26
+ const PROVIDER_LINEAR_CHILD_LANE_TRUSTED_PROJECT_CONFIG_LOCK_RETRY = {
27
+ maxAttempts: 50,
28
+ initialDelayMs: 10,
29
+ backoffFactor: 1.5,
30
+ maxDelayMs: 250,
31
+ staleMs: 30_000
32
+ };
33
+ export const PROVIDER_LINEAR_CHILD_LANE_PROOF_FILENAME = 'provider-linear-child-lane-proof.json';
34
+ export const PROVIDER_LINEAR_CHILD_LANE_STREAM_ENV = 'CODEX_PROVIDER_LINEAR_CHILD_LANE_STREAM';
35
+ export const PROVIDER_LINEAR_CHILD_LANE_PURPOSE_ENV = 'CODEX_PROVIDER_LINEAR_CHILD_LANE_PURPOSE';
36
+ export const PROVIDER_LINEAR_CHILD_LANE_INSTRUCTIONS_ENV = 'CODEX_PROVIDER_LINEAR_CHILD_LANE_INSTRUCTIONS';
37
+ export const PROVIDER_LINEAR_CHILD_LANE_FILES_ENV = 'CODEX_PROVIDER_LINEAR_CHILD_LANE_FILES';
38
+ export const PROVIDER_LINEAR_CHILD_LANE_PHASES_ENV = 'CODEX_PROVIDER_LINEAR_CHILD_LANE_PHASES';
39
+ export const PROVIDER_LINEAR_CHILD_LANE_PARENT_WORKSPACE_PATH_ENV = 'CODEX_PROVIDER_LINEAR_CHILD_LANE_PARENT_WORKSPACE_PATH';
40
+ export const PROVIDER_LINEAR_CHILD_LANE_PARENT_SNAPSHOT_BASE_SHA_ENV = 'CODEX_PROVIDER_LINEAR_CHILD_LANE_PARENT_SNAPSHOT_BASE_SHA';
41
+ export const PROVIDER_LINEAR_CHILD_LANE_PARENT_SNAPSHOT_ISSUE_UPDATED_AT_ENV = 'CODEX_PROVIDER_LINEAR_CHILD_LANE_PARENT_SNAPSHOT_ISSUE_UPDATED_AT';
42
+ export const PROVIDER_LINEAR_CHILD_LANE_PARENT_SNAPSHOT_ISSUE_STATE_ENV = 'CODEX_PROVIDER_LINEAR_CHILD_LANE_PARENT_SNAPSHOT_ISSUE_STATE';
43
+ export const PROVIDER_LINEAR_CHILD_LANE_PARENT_SNAPSHOT_ISSUE_STATE_TYPE_ENV = 'CODEX_PROVIDER_LINEAR_CHILD_LANE_PARENT_SNAPSHOT_ISSUE_STATE_TYPE';
44
+ export const PROVIDER_LINEAR_CHILD_LANE_PARENT_SNAPSHOT_CAPTURED_AT_ENV = 'CODEX_PROVIDER_LINEAR_CHILD_LANE_PARENT_SNAPSHOT_CAPTURED_AT';
45
+ function normalizeOptionalString(value) {
46
+ if (typeof value !== 'string') {
47
+ return null;
48
+ }
49
+ const trimmed = value.trim();
50
+ return trimmed.length > 0 ? trimmed : null;
51
+ }
52
+ function isRecord(value) {
53
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
54
+ }
55
+ function getTomlParser() {
56
+ if (tomlParser) {
57
+ return tomlParser;
58
+ }
59
+ if (tomlParser === null) {
60
+ throw new Error('Failed to load @iarna/toml.');
61
+ }
62
+ try {
63
+ tomlParser = require('@iarna/toml');
64
+ return tomlParser;
65
+ }
66
+ catch (error) {
67
+ tomlParser = null;
68
+ throw error;
69
+ }
70
+ }
71
+ function normalizeProjectPath(value) {
72
+ const resolved = resolve(value);
73
+ const root = parsePath(resolved).root;
74
+ let normalized = resolved;
75
+ while (normalized.length > root.length && /[\\/]$/u.test(normalized)) {
76
+ normalized = normalized.slice(0, -1);
77
+ }
78
+ return normalized;
79
+ }
80
+ function isPathAncestorOf(ancestor, candidate) {
81
+ const relativePath = relative(ancestor, candidate);
82
+ return relativePath === '' || (!relativePath.startsWith('..') && !isAbsolute(relativePath));
83
+ }
84
+ function pathContainsSegment(path, segment) {
85
+ return normalizeProjectPath(path).split(/[\\/]+/u).includes(segment);
86
+ }
87
+ function isTrustedProjectEntry(value) {
88
+ return isRecord(value) && normalizeOptionalString(value.trust_level)?.toLowerCase() === 'trusted';
89
+ }
90
+ function decodeTomlQuotedString(raw) {
91
+ if (raw.startsWith('"') && raw.endsWith('"')) {
92
+ return JSON.parse(raw);
93
+ }
94
+ return raw.slice(1, -1);
95
+ }
96
+ function isProjectsTableKeyPath(keyPath) {
97
+ return keyPath === 'projects' || keyPath === '"projects"' || keyPath === '\'projects\'';
98
+ }
99
+ function parseProjectTableHeader(line) {
100
+ const keyPath = parseTomlTableKeyPath(line);
101
+ if (!keyPath) {
102
+ return null;
103
+ }
104
+ const match = keyPath.match(/^(?:"projects"|'projects'|projects)\s*\.\s*("(?:[^"\\]|\\.)*"|'[^']*')\s*$/u);
105
+ if (!match) {
106
+ return null;
107
+ }
108
+ return normalizeProjectPath(decodeTomlQuotedString(match[1]));
109
+ }
110
+ function parseTomlTableKeyPath(line) {
111
+ const match = line
112
+ .trim()
113
+ .match(/^(?:\[\[((?:[^"'\\\]]+|"(?:[^"\\]|\\.)*"|'[^']*')+)\]\]|\[((?:[^"'\\\]]+|"(?:[^"\\]|\\.)*"|'[^']*')+)\])\s*(?:#.*)?$/u);
114
+ return (match?.[1] ?? match?.[2] ?? null)?.trim() ?? null;
115
+ }
116
+ function parseProjectNamespaceHeader(line) {
117
+ const keyPath = parseTomlTableKeyPath(line);
118
+ if (!keyPath) {
119
+ return null;
120
+ }
121
+ const match = keyPath.match(/^(?:"projects"|'projects'|projects)\s*\.\s*("(?:[^"\\]|\\.)*"|'[^']*')(?:\s*\.\s*|$)/u);
122
+ if (!match) {
123
+ return null;
124
+ }
125
+ return normalizeProjectPath(decodeTomlQuotedString(match[1]));
126
+ }
127
+ function parseInlineProjectEntry(line, currentTableKeyPath) {
128
+ const trimmed = line.trim();
129
+ const inlineKeyPattern = /^("(?:[^"\\]|\\.)*"|'[^']*')\s*=\s*\{.*\}\s*(?:#.*)?$/u;
130
+ const dottedInlineKeyPattern = /^(?:"projects"|'projects'|projects)\s*\.\s*("(?:[^"\\]|\\.)*"|'[^']*')\s*=\s*\{.*\}\s*(?:#.*)?$/u;
131
+ const inlineMatch = isProjectsTableKeyPath(currentTableKeyPath)
132
+ ? trimmed.match(inlineKeyPattern)
133
+ : currentTableKeyPath === null
134
+ ? trimmed.match(dottedInlineKeyPattern)
135
+ : null;
136
+ if (inlineMatch) {
137
+ return normalizeProjectPath(decodeTomlQuotedString(inlineMatch[1]));
138
+ }
139
+ const dottedAssignmentPattern = /^("(?:[^"\\]|\\.)*"|'[^']*')\s*\.\s*.+?=\s*(.+)$/u;
140
+ const topLevelDottedAssignmentPattern = /^(?:"projects"|'projects'|projects)\s*\.\s*("(?:[^"\\]|\\.)*"|'[^']*')\s*\.\s*.+?=\s*(.+)$/u;
141
+ const dottedMatch = isProjectsTableKeyPath(currentTableKeyPath)
142
+ ? trimmed.match(dottedAssignmentPattern)
143
+ : currentTableKeyPath === null
144
+ ? trimmed.match(topLevelDottedAssignmentPattern)
145
+ : null;
146
+ if (!dottedMatch) {
147
+ return null;
148
+ }
149
+ const value = dottedMatch[2].trim();
150
+ if (advanceTomlMultilineStringState(value, null) !== null || advanceTomlArrayDepth(value) > 0) {
151
+ return null;
152
+ }
153
+ return normalizeProjectPath(decodeTomlQuotedString(dottedMatch[1]));
154
+ }
155
+ function isBackslashEscaped(line, index) {
156
+ let backslashCount = 0;
157
+ for (let cursor = index - 1; cursor >= 0 && line[cursor] === '\\'; cursor -= 1) {
158
+ backslashCount += 1;
159
+ }
160
+ return backslashCount % 2 === 1;
161
+ }
162
+ function advanceTomlArrayDepth(line, initialDepth = 0, multilineStringState = null) {
163
+ let arrayDepth = initialDepth;
164
+ let stringState = multilineStringState;
165
+ for (let index = 0; index < line.length; index += 1) {
166
+ if (stringState === 'basic') {
167
+ if (line.startsWith('"""', index) && !isBackslashEscaped(line, index)) {
168
+ stringState = null;
169
+ index += 2;
170
+ }
171
+ continue;
172
+ }
173
+ if (stringState === 'literal') {
174
+ if (line.startsWith("'''", index)) {
175
+ stringState = null;
176
+ index += 2;
177
+ }
178
+ continue;
179
+ }
180
+ const character = line[index];
181
+ if (character === '#')
182
+ break;
183
+ if (line.startsWith('"""', index) && !isBackslashEscaped(line, index)) {
184
+ stringState = 'basic';
185
+ index += 2;
186
+ continue;
187
+ }
188
+ if (line.startsWith("'''", index)) {
189
+ stringState = 'literal';
190
+ index += 2;
191
+ continue;
192
+ }
193
+ if (character === '"' && !isBackslashEscaped(line, index)) {
194
+ for (index += 1; index < line.length && (line[index] !== '"' || isBackslashEscaped(line, index)); index += line[index] === '\\' ? 2 : 1)
195
+ ;
196
+ continue;
197
+ }
198
+ if (character === '\'') {
199
+ for (index += 1; index < line.length && line[index] !== '\''; index += 1)
200
+ ;
201
+ continue;
202
+ }
203
+ if (character === '[')
204
+ arrayDepth += 1;
205
+ if (character === ']')
206
+ arrayDepth = Math.max(0, arrayDepth - 1);
207
+ }
208
+ return arrayDepth;
209
+ }
210
+ function advanceTomlMultilineStringState(line, state) {
211
+ let nextState = state;
212
+ let index = 0;
213
+ while (index < line.length) {
214
+ if (nextState === 'basic') {
215
+ if (line.startsWith('"""', index) && !isBackslashEscaped(line, index)) {
216
+ nextState = null;
217
+ index += 3;
218
+ continue;
219
+ }
220
+ index += line[index] === '\\' ? 2 : 1;
221
+ continue;
222
+ }
223
+ if (nextState === 'literal') {
224
+ if (line.startsWith("'''", index)) {
225
+ nextState = null;
226
+ index += 3;
227
+ continue;
228
+ }
229
+ index += 1;
230
+ continue;
231
+ }
232
+ if (line[index] === '#') {
233
+ break;
234
+ }
235
+ if (line.startsWith('"""', index) && !isBackslashEscaped(line, index)) {
236
+ nextState = 'basic';
237
+ index += 3;
238
+ continue;
239
+ }
240
+ if (line.startsWith("'''", index)) {
241
+ nextState = 'literal';
242
+ index += 3;
243
+ continue;
244
+ }
245
+ if (line[index] === '"' && !isBackslashEscaped(line, index)) {
246
+ index += 1;
247
+ while (index < line.length) {
248
+ if (line[index] === '\\') {
249
+ index += 2;
250
+ continue;
251
+ }
252
+ if (line[index] === '"') {
253
+ index += 1;
254
+ break;
255
+ }
256
+ index += 1;
257
+ }
258
+ continue;
259
+ }
260
+ if (line[index] === '\'') {
261
+ const literalEnd = line.indexOf('\'', index + 1);
262
+ index = literalEnd === -1 ? line.length : literalEnd + 1;
263
+ continue;
264
+ }
265
+ index += 1;
266
+ }
267
+ return nextState;
268
+ }
269
+ function findTrustedAncestorProject(projects, laneWorkspacePath) {
270
+ const normalizedLaneWorkspacePath = normalizeProjectPath(laneWorkspacePath);
271
+ return Object.entries(projects)
272
+ .map(([path, value]) => ({ path: normalizeProjectPath(path), value }))
273
+ .filter(({ path, value }) => path !== normalizedLaneWorkspacePath
274
+ && isTrustedProjectEntry(value)
275
+ && !pathContainsSegment(path, '.child-lanes')
276
+ && isPathAncestorOf(path, normalizedLaneWorkspacePath))
277
+ .sort((left, right) => right.path.length - left.path.length)
278
+ .map(({ path }) => path)[0] ?? null;
279
+ }
280
+ function hasTrustedProjectEntry(projects, candidatePath) {
281
+ const normalizedCandidatePath = normalizeProjectPath(candidatePath);
282
+ return Object.entries(projects).some(([path, value]) => normalizeProjectPath(path) === normalizedCandidatePath && isTrustedProjectEntry(value));
283
+ }
284
+ function removeProjectTablesFromRawConfig(rawConfig, removableProjects) {
285
+ const lines = rawConfig.split(/\r?\n/u);
286
+ const keptLines = [];
287
+ let skippingProjectTable = null;
288
+ let currentTableKeyPath = null;
289
+ let multilineStringState = null;
290
+ let multilineArrayDepth = 0;
291
+ for (const line of lines) {
292
+ const tableHeaderPath = multilineStringState || multilineArrayDepth > 0 ? null : parseTomlTableKeyPath(line);
293
+ if (skippingProjectTable) {
294
+ if (!tableHeaderPath || parseProjectNamespaceHeader(line) === skippingProjectTable) {
295
+ multilineArrayDepth = advanceTomlArrayDepth(line, multilineArrayDepth, multilineStringState);
296
+ multilineStringState = advanceTomlMultilineStringState(line, multilineStringState);
297
+ continue;
298
+ }
299
+ skippingProjectTable = null;
300
+ }
301
+ const projectHeaderPath = tableHeaderPath ? parseProjectTableHeader(line) : null;
302
+ if (projectHeaderPath && removableProjects.has(projectHeaderPath)) {
303
+ skippingProjectTable = projectHeaderPath;
304
+ multilineStringState = advanceTomlMultilineStringState(line, multilineStringState);
305
+ continue;
306
+ }
307
+ const inlineProjectPath = tableHeaderPath || multilineStringState
308
+ ? null
309
+ : parseInlineProjectEntry(line, currentTableKeyPath);
310
+ if (inlineProjectPath && removableProjects.has(inlineProjectPath)) {
311
+ multilineArrayDepth = advanceTomlArrayDepth(line, multilineArrayDepth, multilineStringState);
312
+ multilineStringState = advanceTomlMultilineStringState(line, multilineStringState);
313
+ continue;
314
+ }
315
+ keptLines.push(line);
316
+ multilineArrayDepth = advanceTomlArrayDepth(line, multilineArrayDepth, multilineStringState);
317
+ multilineStringState = advanceTomlMultilineStringState(line, multilineStringState);
318
+ if (tableHeaderPath) {
319
+ currentTableKeyPath = tableHeaderPath.trim();
320
+ }
321
+ }
322
+ const nextConfig = keptLines.join('\n');
323
+ return rawConfig.endsWith('\n') && !nextConfig.endsWith('\n') ? `${nextConfig}\n` : nextConfig;
324
+ }
325
+ function planTrustedProjectCleanup(input) {
326
+ const parsed = getTomlParser().parse(input.rawConfig);
327
+ if (!isRecord(parsed) || !isRecord(parsed.projects)) {
328
+ return {
329
+ configPath: input.configPath,
330
+ anchorProject: null,
331
+ removedProjects: [],
332
+ changed: false,
333
+ nextConfig: null
334
+ };
335
+ }
336
+ const projects = parsed.projects;
337
+ const anchorProject = findTrustedAncestorProject(projects, input.laneWorkspacePath);
338
+ if (!anchorProject) {
339
+ return {
340
+ configPath: input.configPath,
341
+ anchorProject: null,
342
+ removedProjects: [],
343
+ changed: false,
344
+ nextConfig: null
345
+ };
346
+ }
347
+ const normalizedLaneWorkspacePath = normalizeProjectPath(input.laneWorkspacePath);
348
+ const removedProjects = hasTrustedProjectEntry(projects, normalizedLaneWorkspacePath)
349
+ ? [normalizedLaneWorkspacePath]
350
+ : [];
351
+ if (removedProjects.length === 0) {
352
+ return {
353
+ configPath: input.configPath,
354
+ anchorProject,
355
+ removedProjects: [],
356
+ changed: false,
357
+ nextConfig: null
358
+ };
359
+ }
360
+ const nextConfig = removeProjectTablesFromRawConfig(input.rawConfig, new Set(removedProjects));
361
+ return {
362
+ configPath: input.configPath,
363
+ anchorProject,
364
+ removedProjects,
365
+ changed: nextConfig !== input.rawConfig,
366
+ nextConfig
367
+ };
368
+ }
369
+ async function compactRedundantTrustedChildLaneProjects(input) {
370
+ const configPath = join(resolveCodexHome(input.env), 'config.toml');
371
+ const lockPath = `${configPath}.lock`;
372
+ const lock = await acquireLockWithRetry({
373
+ taskId: configPath,
374
+ lockPath,
375
+ retry: PROVIDER_LINEAR_CHILD_LANE_TRUSTED_PROJECT_CONFIG_LOCK_RETRY,
376
+ ensureDirectory: async () => {
377
+ await mkdir(dirname(lockPath), { recursive: true });
378
+ },
379
+ createError: (_taskId, attempts) => new Error(`Failed to acquire provider-linear-child-lane trusted-project config lock after ${attempts} attempts.`)
380
+ });
381
+ try {
382
+ let rawConfig;
383
+ try {
384
+ rawConfig = await readFile(configPath, 'utf8');
385
+ }
386
+ catch (error) {
387
+ if (error.code === 'ENOENT') {
388
+ return {
389
+ configPath,
390
+ anchorProject: null,
391
+ removedProjects: [],
392
+ changed: false,
393
+ nextConfig: null
394
+ };
395
+ }
396
+ throw error;
397
+ }
398
+ const plan = planTrustedProjectCleanup({
399
+ rawConfig,
400
+ laneWorkspacePath: input.laneWorkspacePath,
401
+ configPath
402
+ });
403
+ if (plan.changed && plan.nextConfig !== null) {
404
+ await writeAtomicFile(configPath, plan.nextConfig, { ensureDir: true, encoding: 'utf8' });
405
+ }
406
+ return plan;
407
+ }
408
+ finally {
409
+ await lock.release();
410
+ }
411
+ }
412
+ async function compactRedundantTrustedChildLaneProjectsBestEffort(input) {
413
+ try {
414
+ const cleanup = await compactRedundantTrustedChildLaneProjects(input);
415
+ if (cleanup.removedProjects.length > 0) {
416
+ logger.info(`[provider-linear-child-lane-trust] removed ${cleanup.removedProjects.length} child-lane trusted project entr${cleanup.removedProjects.length === 1 ? 'y' : 'ies'} from ${cleanup.configPath} using ancestor ${cleanup.anchorProject}.`);
417
+ }
418
+ }
419
+ catch (error) {
420
+ logger.warn(`provider-linear-child-lane warning: failed to compact redundant trusted project entries in ${join(resolveCodexHome(input.env), 'config.toml')}: ${error instanceof Error ? error.message : String(error)}`);
421
+ }
422
+ }
423
+ function normalizeStringArrayFromEnv(value) {
424
+ if (!value) {
425
+ return [];
426
+ }
427
+ try {
428
+ const parsed = JSON.parse(value);
429
+ if (!Array.isArray(parsed)) {
430
+ return [];
431
+ }
432
+ return parsed
433
+ .map((entry) => normalizeOptionalString(entry))
434
+ .filter((entry) => entry !== null);
435
+ }
436
+ catch {
437
+ return [];
438
+ }
439
+ }
440
+ function ensurePathWithinRoot(root, candidate) {
441
+ const resolvedRoot = resolve(root);
442
+ const resolvedCandidate = resolve(candidate);
443
+ const relativePath = relative(resolvedRoot, resolvedCandidate);
444
+ if (relativePath.startsWith('..') || isAbsolute(relativePath)) {
445
+ throw new Error(`Path escapes parent workspace boundary: ${candidate}`);
446
+ }
447
+ return resolvedCandidate;
448
+ }
449
+ function deriveLatestTurnSessionId(input) {
450
+ if (!input.threadId || !input.turnId) {
451
+ return { sessionId: null, source: null };
452
+ }
453
+ return {
454
+ sessionId: `${input.threadId}-${input.turnId}`,
455
+ source: 'derived_from_thread_and_turn'
456
+ };
457
+ }
458
+ function buildChildLanePromptHeader(issueIdentifier) {
459
+ return `You are a bounded same-issue child lane for Linear issue ${issueIdentifier}.`;
460
+ }
461
+ function buildChildLanePrompt(context) {
462
+ const scopeLines = [
463
+ context.scope.files.length > 0
464
+ ? `- File scope: ${context.scope.files.join(', ')}`
465
+ : '- File scope: none declared',
466
+ context.scope.phases.length > 0
467
+ ? `- Phase scope: ${context.scope.phases.join(', ')}`
468
+ : '- Phase scope: none declared'
469
+ ];
470
+ return [
471
+ buildChildLanePromptHeader(context.issueIdentifier),
472
+ '',
473
+ 'Constraints:',
474
+ '- Work only inside this lane workspace. The parent lane owns the authoritative issue workspace, Linear state, workpad, and PR lifecycle.',
475
+ '- Stay strictly inside the declared scope below. Do not edit files outside the declared file or phase scope.',
476
+ '- If the change cannot be completed within that scope, stop and report back to the parent lane so it can relaunch with widened ownership.',
477
+ '- Do not call Linear mutation helpers. Parent-owned integration happens by patch artifact only.',
478
+ '- Do not run full repo validation suites. Keep checks tightly scoped to the touched area when needed.',
479
+ '- If the lane is advisory/read-only and no file changes are needed, finish with a concise evidence summary. The parent will classify a zero-byte patch as no-output advisory evidence, not as an applicable patch.',
480
+ '',
481
+ `Purpose: ${context.purpose}`,
482
+ ...scopeLines,
483
+ ...(context.runMemoryPromptLines.length > 0 ? ['', ...context.runMemoryPromptLines] : []),
484
+ '',
485
+ context.instructions ? `Additional instructions:\n${context.instructions}` : 'Additional instructions: none.',
486
+ '',
487
+ 'Finish by leaving the lane workspace changes in place for patch export. Do not commit.'
488
+ ].join('\n');
489
+ }
490
+ function buildProviderLinearChildLaneSessionPromptNeedles(context) {
491
+ return [buildChildLanePromptHeader(context.issueIdentifier)];
492
+ }
493
+ function buildProviderLinearChildLaneRecentSessionDayDirs(sessionRoot, referenceDates) {
494
+ const results = [];
495
+ const seen = new Set();
496
+ for (const referenceDate of referenceDates) {
497
+ for (const dayOffset of [0, -1]) {
498
+ const current = new Date(referenceDate.getTime());
499
+ current.setDate(current.getDate() + dayOffset);
500
+ const dir = join(sessionRoot, String(current.getFullYear()), String(current.getMonth() + 1).padStart(2, '0'), String(current.getDate()).padStart(2, '0'));
501
+ if (!seen.has(dir)) {
502
+ seen.add(dir);
503
+ results.push(dir);
504
+ }
505
+ }
506
+ }
507
+ return results;
508
+ }
509
+ function providerLinearChildLaneSessionLogPathMatchesThreadId(path, threadId) {
510
+ return basename(path, '.jsonl').endsWith(`-${threadId}`);
511
+ }
512
+ async function readProviderLinearChildLaneFilePrefix(path, maxBytes) {
513
+ const handle = await open(path, 'r');
514
+ try {
515
+ const buffer = Buffer.alloc(maxBytes);
516
+ const { bytesRead } = await handle.read(buffer, 0, maxBytes, 0);
517
+ return buffer.toString('utf8', 0, bytesRead);
518
+ }
519
+ finally {
520
+ await handle.close();
521
+ }
522
+ }
523
+ function parseProviderLinearChildLaneSessionJsonlLine(line) {
524
+ const trimmed = line.trim();
525
+ if (!trimmed.startsWith('{')) {
526
+ return null;
527
+ }
528
+ try {
529
+ const parsed = JSON.parse(trimmed);
530
+ return typeof parsed === 'object' && parsed !== null ? parsed : null;
531
+ }
532
+ catch {
533
+ return null;
534
+ }
535
+ }
536
+ function tokenizeShellCommandForScopeDrift(command) {
537
+ const normalized = stripShellCommandHeredocBodies(command);
538
+ return normalized.match(/"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\n|&&|\|\||[;&|]|[^\s;&|]+/gu) ?? [];
539
+ }
540
+ function stripShellCommandHeredocBodies(command) {
541
+ const normalized = command.replace(/\r\n/gu, '\n');
542
+ const lines = normalized.split('\n');
543
+ const heredocDelimiters = [];
544
+ const output = [];
545
+ for (const line of lines) {
546
+ if (heredocDelimiters.length > 0) {
547
+ const active = heredocDelimiters[0];
548
+ if (active) {
549
+ const candidate = active.stripLeadingTabs ? line.replace(/^\t+/u, '') : line;
550
+ if (lineClosesShellCommandHeredoc(candidate, active.delimiter)) {
551
+ output.push(line);
552
+ heredocDelimiters.shift();
553
+ }
554
+ else {
555
+ output.push('');
556
+ }
557
+ continue;
558
+ }
559
+ }
560
+ output.push(line);
561
+ for (const match of line.matchAll(/<<(-)?\s*(?:(['"])(.*?)\2|([^\s<>&|;]+))/gu)) {
562
+ const delimiter = match[3] ?? match[4];
563
+ if (!delimiter) {
564
+ continue;
565
+ }
566
+ heredocDelimiters.push({
567
+ delimiter,
568
+ stripLeadingTabs: match[1] === '-'
569
+ });
570
+ }
571
+ }
572
+ return output.join('\n');
573
+ }
574
+ function lineClosesShellCommandHeredoc(line, delimiter) {
575
+ if (line === delimiter) {
576
+ return true;
577
+ }
578
+ if (!line.startsWith(delimiter)) {
579
+ return false;
580
+ }
581
+ const remainder = line.slice(delimiter.length);
582
+ return /^["']+(?:\s*(?:&&|\|\||[;&|]).*)?$/u.test(remainder);
583
+ }
584
+ function stripShellCommandTokenQuotes(token) {
585
+ return token.replace(/^['"]|['"]$/gu, '');
586
+ }
587
+ function shellOptionConsumesNextValue(token, optionsWithValues) {
588
+ return optionsWithValues.has(token) && !token.includes('=');
589
+ }
590
+ function advancePastShellCommandEnvWrappers(tokens, segmentStart, segmentEnd) {
591
+ const envAssignmentPattern = /^[A-Za-z_][A-Za-z0-9_]*=.*/u;
592
+ const envOptionsWithValues = new Set([
593
+ '-u',
594
+ '--unset',
595
+ '-C',
596
+ '--chdir',
597
+ '-S',
598
+ '--split-string',
599
+ '--default-signal',
600
+ '--ignore-signal',
601
+ '--block-signal'
602
+ ]);
603
+ let index = segmentStart;
604
+ while (index < segmentEnd) {
605
+ const token = stripShellCommandTokenQuotes(tokens[index] ?? '');
606
+ if (!token) {
607
+ index += 1;
608
+ continue;
609
+ }
610
+ if (envAssignmentPattern.test(token)) {
611
+ index += 1;
612
+ continue;
613
+ }
614
+ if (basename(token) === 'env') {
615
+ index += 1;
616
+ while (index < segmentEnd) {
617
+ const envToken = stripShellCommandTokenQuotes(tokens[index] ?? '');
618
+ if (!envToken) {
619
+ index += 1;
620
+ continue;
621
+ }
622
+ if (envToken === '--') {
623
+ index += 1;
624
+ break;
625
+ }
626
+ if (envAssignmentPattern.test(envToken)) {
627
+ index += 1;
628
+ continue;
629
+ }
630
+ if (envToken.startsWith('-')) {
631
+ if (shellOptionConsumesNextValue(envToken, envOptionsWithValues)) {
632
+ index += 1;
633
+ }
634
+ index += 1;
635
+ continue;
636
+ }
637
+ break;
638
+ }
639
+ continue;
640
+ }
641
+ break;
642
+ }
643
+ return index;
644
+ }
645
+ function advancePastShellCommandExecutionWrappers(tokens, segmentStart, segmentEnd) {
646
+ let index = advancePastShellCommandEnvWrappers(tokens, segmentStart, segmentEnd);
647
+ while (index < segmentEnd) {
648
+ const token = stripShellCommandTokenQuotes(tokens[index] ?? '');
649
+ if (!token) {
650
+ index += 1;
651
+ continue;
652
+ }
653
+ if (basename(token) !== 'command') {
654
+ break;
655
+ }
656
+ index += 1;
657
+ while (index < segmentEnd) {
658
+ const commandToken = stripShellCommandTokenQuotes(tokens[index] ?? '');
659
+ if (!commandToken) {
660
+ index += 1;
661
+ continue;
662
+ }
663
+ if (commandToken === '--') {
664
+ index += 1;
665
+ break;
666
+ }
667
+ if (commandToken === '-p') {
668
+ index += 1;
669
+ continue;
670
+ }
671
+ if (commandToken.startsWith('-')) {
672
+ return segmentEnd;
673
+ }
674
+ break;
675
+ }
676
+ }
677
+ return index;
678
+ }
679
+ function findShellCommandSegmentEnd(tokens, segmentStart, commandSeparators) {
680
+ let index = segmentStart;
681
+ while (index < tokens.length) {
682
+ const token = stripShellCommandTokenQuotes(tokens[index] ?? '');
683
+ if (token && commandSeparators.has(token)) {
684
+ break;
685
+ }
686
+ index += 1;
687
+ }
688
+ return index;
689
+ }
690
+ function segmentShowsParentOwnedOrchestratorScopeDrift(tokens, start, end) {
691
+ for (let index = start; index < end; index += 1) {
692
+ const token = stripShellCommandTokenQuotes(tokens[index] ?? '');
693
+ if (!token) {
694
+ continue;
695
+ }
696
+ if (token === 'linear' || token === 'pr' || token === 'review' || token.startsWith('provider-linear-')) {
697
+ return true;
698
+ }
699
+ }
700
+ return false;
701
+ }
702
+ function tokenInvokesCodexOrchestrator(token) {
703
+ return /^(?:codex-orchestrator(?:\.js)?|codex-orchestrator@.+)$/u.test(basename(token));
704
+ }
705
+ function packageExecSegmentShowsParentOwnedScopeDrift(tokens, commandStart, segmentEnd) {
706
+ const packageExecOptionsWithValues = new Set(['-c', '--call', '-p', '--package', '--shell', '--node-options']);
707
+ for (let index = commandStart; index < segmentEnd; index += 1) {
708
+ const token = stripShellCommandTokenQuotes(tokens[index] ?? '');
709
+ if (!token) {
710
+ continue;
711
+ }
712
+ if (token === '--') {
713
+ continue;
714
+ }
715
+ if (token === '-c' || token === '--call') {
716
+ const nestedCommand = stripShellCommandTokenQuotes(tokens[index + 1] ?? '');
717
+ return nestedCommand ? commandShowsParentOwnedScopeDrift(nestedCommand) : false;
718
+ }
719
+ if (token.startsWith('-')) {
720
+ if (shellOptionConsumesNextValue(token, packageExecOptionsWithValues)) {
721
+ index += 1;
722
+ }
723
+ continue;
724
+ }
725
+ if (!tokenInvokesCodexOrchestrator(token)) {
726
+ return false;
727
+ }
728
+ return segmentShowsParentOwnedOrchestratorScopeDrift(tokens, index + 1, segmentEnd);
729
+ }
730
+ return false;
731
+ }
732
+ function npmExecSegmentShowsParentOwnedScopeDrift(tokens, commandStart, segmentEnd) {
733
+ const npmOptionsWithValues = new Set(['-C', '--prefix', '-w', '--workspace', '--userconfig', '--cache', '--loglevel']);
734
+ for (let index = commandStart; index < segmentEnd; index += 1) {
735
+ const token = stripShellCommandTokenQuotes(tokens[index] ?? '');
736
+ if (!token) {
737
+ continue;
738
+ }
739
+ if (token === '--') {
740
+ continue;
741
+ }
742
+ if (token === 'exec' || token === 'x') {
743
+ return packageExecSegmentShowsParentOwnedScopeDrift(tokens, index + 1, segmentEnd);
744
+ }
745
+ if (token.startsWith('-')) {
746
+ if (shellOptionConsumesNextValue(token, npmOptionsWithValues)) {
747
+ index += 1;
748
+ }
749
+ continue;
750
+ }
751
+ return false;
752
+ }
753
+ return false;
754
+ }
755
+ function commandSegmentShowsParentOwnedScopeDrift(tokens, segmentStart, segmentEnd) {
756
+ const shellCommandWrappers = new Set(['bash', 'sh', 'zsh']);
757
+ const gitOptionsWithValues = new Set([
758
+ '-c',
759
+ '-C',
760
+ '--exec-path',
761
+ '--git-dir',
762
+ '--work-tree',
763
+ '--namespace',
764
+ '--super-prefix',
765
+ '--config-env'
766
+ ]);
767
+ const commandIndex = advancePastShellCommandExecutionWrappers(tokens, segmentStart, segmentEnd);
768
+ if (commandIndex >= segmentEnd) {
769
+ return false;
770
+ }
771
+ const commandToken = stripShellCommandTokenQuotes(tokens[commandIndex] ?? '');
772
+ const commandBase = basename(commandToken);
773
+ if (commandBase === 'eval') {
774
+ const nestedCommandTokens = [];
775
+ for (let index = commandIndex + 1; index < segmentEnd; index += 1) {
776
+ const token = stripShellCommandTokenQuotes(tokens[index] ?? '');
777
+ if (!token) {
778
+ continue;
779
+ }
780
+ if (nestedCommandTokens.length === 0 && token === '--') {
781
+ continue;
782
+ }
783
+ nestedCommandTokens.push(token);
784
+ }
785
+ return nestedCommandTokens.length > 0 ? commandShowsParentOwnedScopeDrift(nestedCommandTokens.join(' ')) : false;
786
+ }
787
+ if (shellCommandWrappers.has(commandBase)) {
788
+ const shellOptionsWithValues = new Set(['-o', '-O', '--rcfile', '--init-file']);
789
+ for (let index = commandIndex + 1; index < segmentEnd; index += 1) {
790
+ const token = stripShellCommandTokenQuotes(tokens[index] ?? '');
791
+ if (!token) {
792
+ continue;
793
+ }
794
+ if (/^-[^-]*c[^-]*$/u.test(token) || token === '-c') {
795
+ const nestedCommand = stripShellCommandTokenQuotes(tokens[index + 1] ?? '');
796
+ return nestedCommand ? commandShowsParentOwnedScopeDrift(nestedCommand) : false;
797
+ }
798
+ if (shellOptionConsumesNextValue(token, shellOptionsWithValues)) {
799
+ index += 1;
800
+ continue;
801
+ }
802
+ if (!token.startsWith('-')) {
803
+ return false;
804
+ }
805
+ }
806
+ return false;
807
+ }
808
+ if (commandBase === 'gh') {
809
+ return true;
810
+ }
811
+ if (commandBase === 'npx') {
812
+ return packageExecSegmentShowsParentOwnedScopeDrift(tokens, commandIndex + 1, segmentEnd);
813
+ }
814
+ if (commandBase === 'npm') {
815
+ return npmExecSegmentShowsParentOwnedScopeDrift(tokens, commandIndex + 1, segmentEnd);
816
+ }
817
+ if (tokenInvokesCodexOrchestrator(commandToken)) {
818
+ return segmentShowsParentOwnedOrchestratorScopeDrift(tokens, commandIndex + 1, segmentEnd);
819
+ }
820
+ if (commandBase === 'node' || commandBase === 'bun') {
821
+ const runtimeOptionsWithValues = new Set([
822
+ '-r',
823
+ '--require',
824
+ '--import',
825
+ '--loader',
826
+ '--experimental-loader',
827
+ '--conditions'
828
+ ]);
829
+ for (let index = commandIndex + 1; index < segmentEnd; index += 1) {
830
+ const token = stripShellCommandTokenQuotes(tokens[index] ?? '');
831
+ if (!token) {
832
+ continue;
833
+ }
834
+ if (token.startsWith('-')) {
835
+ if (shellOptionConsumesNextValue(token, runtimeOptionsWithValues)) {
836
+ index += 1;
837
+ }
838
+ continue;
839
+ }
840
+ if (!tokenInvokesCodexOrchestrator(token)) {
841
+ return false;
842
+ }
843
+ return segmentShowsParentOwnedOrchestratorScopeDrift(tokens, index + 1, segmentEnd);
844
+ }
845
+ return false;
846
+ }
847
+ if (commandBase !== 'git') {
848
+ return false;
849
+ }
850
+ for (let index = commandIndex + 1; index < segmentEnd; index += 1) {
851
+ const token = stripShellCommandTokenQuotes(tokens[index] ?? '');
852
+ if (!token) {
853
+ continue;
854
+ }
855
+ if (token === 'commit' || token === 'push') {
856
+ return true;
857
+ }
858
+ if (!token.startsWith('-')) {
859
+ return false;
860
+ }
861
+ if (shellOptionConsumesNextValue(token, gitOptionsWithValues)) {
862
+ index += 1;
863
+ }
864
+ }
865
+ return false;
866
+ }
867
+ function commandShowsParentOwnedScopeDrift(command) {
868
+ const tokens = tokenizeShellCommandForScopeDrift(command);
869
+ const commandSeparators = new Set(['\n', '&&', '||', ';', '|', '&']);
870
+ let segmentStart = 0;
871
+ while (segmentStart < tokens.length) {
872
+ const segmentEnd = findShellCommandSegmentEnd(tokens, segmentStart, commandSeparators);
873
+ if (commandSegmentShowsParentOwnedScopeDrift(tokens, segmentStart, segmentEnd)) {
874
+ return true;
875
+ }
876
+ segmentStart = segmentEnd + 1;
877
+ }
878
+ return false;
879
+ }
880
+ function queryShowsParentOwnedScopeDrift(query) {
881
+ if (/\bpull requests?\b/iu.test(query) &&
882
+ /\b(?:open|opened|close|closed|merge|merged|comment|comments|review|reviews|check|checks|status|list|view|repo|repository)\b/iu.test(query)) {
883
+ return true;
884
+ }
885
+ if (/\bgithub\b/iu.test(query) &&
886
+ /\b(?:prs?\b|issue|issues|comment|comments|review|reviews|check|checks|status|merge|merged|open|opened|close|closed)\b/iu.test(query)) {
887
+ return true;
888
+ }
889
+ if (/\bprs?\b/iu.test(query) &&
890
+ (/\bpr\s*#?\d+\b/iu.test(query) ||
891
+ /\b(?:comment|comments|review|reviews|check|checks|status|merge|merged|open|opened|close|closed)\b/iu.test(query))) {
892
+ return true;
893
+ }
894
+ return (/\blinear\b/iu.test(query) &&
895
+ /\b(?:issue|issues|ticket|tickets|project|projects|comment|comments|status|state|workflow)\b/iu.test(query));
896
+ }
897
+ function functionNameShowsParentOwnedScopeDrift(functionName) {
898
+ const tokens = functionName
899
+ .toLowerCase()
900
+ .split(/[^a-z0-9]+/u)
901
+ .filter((token) => token.length > 0);
902
+ if (tokens.includes('github') || tokens.includes('linear')) {
903
+ return true;
904
+ }
905
+ if (tokens.includes('pull') && tokens.includes('request')) {
906
+ return true;
907
+ }
908
+ return (tokens.includes('pr') &&
909
+ tokens.some((token) => ['open', 'make', 'create', 'update', 'close', 'merge', 'view', 'list', 'comment', 'review'].includes(token)));
910
+ }
911
+ function formatProviderLinearChildLaneScopeDriftEvidence(timestamp, detail) {
912
+ return timestamp ? `${timestamp} ${detail}` : detail;
913
+ }
914
+ function truncateProviderLinearChildLaneScopeDriftText(value, maxLength = 140) {
915
+ const normalized = value.replace(/\s+/gu, ' ').trim();
916
+ if (normalized.length <= maxLength) {
917
+ return normalized;
918
+ }
919
+ return `${normalized.slice(0, Math.max(0, maxLength - 1)).trimEnd()}...`;
920
+ }
921
+ function extractProviderLinearChildLaneScopeDriftEvidenceFromRecord(record) {
922
+ const timestamp = normalizeOptionalString(record.timestamp);
923
+ if (normalizeOptionalString(record.type) !== 'response_item') {
924
+ return [];
925
+ }
926
+ const payload = typeof record.payload === 'object' && record.payload !== null
927
+ ? record.payload
928
+ : null;
929
+ if (!payload) {
930
+ return [];
931
+ }
932
+ const payloadType = normalizeOptionalString(payload.type);
933
+ if (payloadType === 'tool_search_call') {
934
+ const argumentsValue = typeof payload.arguments === 'object' && payload.arguments !== null
935
+ ? payload.arguments
936
+ : null;
937
+ const query = normalizeOptionalString(argumentsValue?.query);
938
+ if (query && queryShowsParentOwnedScopeDrift(query)) {
939
+ return [
940
+ formatProviderLinearChildLaneScopeDriftEvidence(timestamp, `tool_search ${truncateProviderLinearChildLaneScopeDriftText(query)}`)
941
+ ];
942
+ }
943
+ return [];
944
+ }
945
+ if (payloadType !== 'function_call') {
946
+ return [];
947
+ }
948
+ const functionName = normalizeOptionalString(payload.name);
949
+ if (!functionName) {
950
+ return [];
951
+ }
952
+ if (functionNameShowsParentOwnedScopeDrift(functionName)) {
953
+ return [
954
+ formatProviderLinearChildLaneScopeDriftEvidence(timestamp, `function_call ${functionName}`)
955
+ ];
956
+ }
957
+ if (functionName !== 'exec_command') {
958
+ return [];
959
+ }
960
+ const rawArguments = normalizeOptionalString(payload.arguments);
961
+ if (!rawArguments) {
962
+ return [];
963
+ }
964
+ try {
965
+ const parsed = JSON.parse(rawArguments);
966
+ const command = normalizeOptionalString(parsed.cmd);
967
+ if (!command || !commandShowsParentOwnedScopeDrift(command)) {
968
+ return [];
969
+ }
970
+ return [
971
+ formatProviderLinearChildLaneScopeDriftEvidence(timestamp, `exec_command ${truncateProviderLinearChildLaneScopeDriftText(command)}`)
972
+ ];
973
+ }
974
+ catch {
975
+ return [];
976
+ }
977
+ }
978
+ async function scanProviderLinearChildLaneSessionLogForParentScopeDrift(sessionLogPath, startedAt = null) {
979
+ const raw = await readFile(sessionLogPath, 'utf8');
980
+ const evidence = new Set();
981
+ const startedAtMs = startedAt ? Date.parse(startedAt) : Number.NaN;
982
+ let withinLaunchWindow = !Number.isFinite(startedAtMs);
983
+ for (const line of raw.split(/\r?\n/u)) {
984
+ const parsed = parseProviderLinearChildLaneSessionJsonlLine(line);
985
+ if (!parsed) {
986
+ continue;
987
+ }
988
+ if (!withinLaunchWindow) {
989
+ const timestampMs = typeof parsed.timestamp === 'string' ? Date.parse(parsed.timestamp) : Number.NaN;
990
+ if (!Number.isFinite(timestampMs) || timestampMs < startedAtMs) {
991
+ continue;
992
+ }
993
+ withinLaunchWindow = true;
994
+ }
995
+ for (const detail of extractProviderLinearChildLaneScopeDriftEvidenceFromRecord(parsed)) {
996
+ evidence.add(detail);
997
+ if (evidence.size >= 3) {
998
+ return [...evidence];
999
+ }
1000
+ }
1001
+ }
1002
+ return [...evidence];
1003
+ }
1004
+ function buildProviderLinearChildLaneScopeDriftMessage(input) {
1005
+ const renderedEvidence = input.evidence.map((entry) => `"${entry}"`).join('; ');
1006
+ return `Appserver child lane drifted into parent-owned GitHub/Linear/PR lifecycle work for ${input.context.issueIdentifier}. Session log ${basename(input.sessionLogPath)} captured ${renderedEvidence}. Invalidate the lane and relaunch with tighter bounded scope; parent-owned Linear/GitHub/PR handling must stay in the provider worker.`;
1007
+ }
1008
+ async function waitForProviderLinearChildLaneScopeDrift(input) {
1009
+ const readScopeDriftMessage = async () => {
1010
+ const evidence = await scanProviderLinearChildLaneSessionLogForParentScopeDrift(input.sessionLogPath, input.startedAt ?? null).catch((error) => [
1011
+ formatProviderLinearChildLaneScopeDriftEvidence(null, `drift_monitor_unreadable ${basename(input.sessionLogPath)}: ${error instanceof Error ? error.message : String(error)}`)
1012
+ ]);
1013
+ if (evidence.length === 0) {
1014
+ return null;
1015
+ }
1016
+ return buildProviderLinearChildLaneScopeDriftMessage({
1017
+ context: input.context,
1018
+ sessionLogPath: input.sessionLogPath,
1019
+ evidence
1020
+ });
1021
+ };
1022
+ while (!input.isExecSettled()) {
1023
+ const driftMessage = await readScopeDriftMessage();
1024
+ if (driftMessage) {
1025
+ return driftMessage;
1026
+ }
1027
+ await input.deps.sleep(PROVIDER_LINEAR_CHILD_LANE_SCOPE_DRIFT_POLL_INTERVAL_MS);
1028
+ }
1029
+ return await readScopeDriftMessage();
1030
+ }
1031
+ function valueContainsProviderLinearChildLaneSessionNeedle(value, needle) {
1032
+ if (typeof value === 'string') {
1033
+ return value.includes(needle);
1034
+ }
1035
+ if (Array.isArray(value)) {
1036
+ return value.some((item) => valueContainsProviderLinearChildLaneSessionNeedle(item, needle));
1037
+ }
1038
+ if (typeof value !== 'object' || value === null) {
1039
+ return false;
1040
+ }
1041
+ return Object.values(value).some((item) => valueContainsProviderLinearChildLaneSessionNeedle(item, needle));
1042
+ }
1043
+ function valueEqualsProviderLinearChildLaneSessionNeedle(value, needle) {
1044
+ if (typeof value === 'string') {
1045
+ return value === needle;
1046
+ }
1047
+ if (Array.isArray(value)) {
1048
+ return value.some((item) => valueEqualsProviderLinearChildLaneSessionNeedle(item, needle));
1049
+ }
1050
+ if (typeof value !== 'object' || value === null) {
1051
+ return false;
1052
+ }
1053
+ return Object.values(value).some((item) => valueEqualsProviderLinearChildLaneSessionNeedle(item, needle));
1054
+ }
1055
+ function prefixContainsProviderLinearChildLaneSessionHeader(prefix, workspacePath, promptNeedles) {
1056
+ let hasExactWorkspacePath = false;
1057
+ let hasPromptNeedle = promptNeedles.some((needle) => prefix.includes(needle));
1058
+ for (const line of prefix.split(/\r?\n/u)) {
1059
+ const parsed = parseProviderLinearChildLaneSessionJsonlLine(line);
1060
+ const payload = parsed?.type === 'session_meta' && parsed.payload && typeof parsed.payload === 'object'
1061
+ ? parsed.payload
1062
+ : parsed?.payload && typeof parsed.payload === 'object'
1063
+ ? parsed.payload
1064
+ : null;
1065
+ if (!payload) {
1066
+ continue;
1067
+ }
1068
+ hasExactWorkspacePath =
1069
+ hasExactWorkspacePath || valueEqualsProviderLinearChildLaneSessionNeedle(payload, workspacePath);
1070
+ if (!hasPromptNeedle) {
1071
+ hasPromptNeedle = promptNeedles.some((needle) => valueContainsProviderLinearChildLaneSessionNeedle(payload, needle));
1072
+ }
1073
+ if (hasExactWorkspacePath && hasPromptNeedle) {
1074
+ return true;
1075
+ }
1076
+ }
1077
+ return false;
1078
+ }
1079
+ function prefixHasProviderLinearChildLaneSessionTimestampAtOrAfter(prefix, startedAtMs) {
1080
+ if (!Number.isFinite(startedAtMs)) {
1081
+ return false;
1082
+ }
1083
+ for (const line of prefix.split(/\r?\n/u)) {
1084
+ const parsed = parseProviderLinearChildLaneSessionJsonlLine(line);
1085
+ if (parsed?.type !== 'session_meta' && parsed?.type !== 'turn_context') {
1086
+ continue;
1087
+ }
1088
+ const timestamp = typeof parsed?.timestamp === 'string' ? Date.parse(parsed.timestamp) : Number.NaN;
1089
+ if (Number.isFinite(timestamp) && timestamp >= startedAtMs) {
1090
+ return true;
1091
+ }
1092
+ }
1093
+ return false;
1094
+ }
1095
+ async function discoverProviderLinearChildLaneSessionLogPath(input) {
1096
+ const sessionRoot = join(resolveCodexHome(input.env), 'sessions');
1097
+ const startedAtMs = Date.parse(input.startedAt);
1098
+ const referenceDate = Number.isFinite(startedAtMs) ? new Date(startedAtMs) : new Date();
1099
+ const currentDate = new Date();
1100
+ const cutoffMs = Number.isFinite(startedAtMs)
1101
+ ? startedAtMs - PROVIDER_LINEAR_CHILD_LANE_SESSION_LOG_DISCOVERY_WINDOW_MS
1102
+ : Date.now() - PROVIDER_LINEAR_CHILD_LANE_SESSION_LOG_DISCOVERY_WINDOW_MS;
1103
+ const threadIdHint = normalizeOptionalString(input.env.CODEX_THREAD_ID);
1104
+ const sessionMetaNeedle = threadIdHint ? `"id":"${threadIdHint}"` : null;
1105
+ const candidates = [];
1106
+ for (const dayDir of buildProviderLinearChildLaneRecentSessionDayDirs(sessionRoot, [
1107
+ currentDate,
1108
+ referenceDate
1109
+ ])) {
1110
+ let entries;
1111
+ try {
1112
+ entries = await readdir(dayDir);
1113
+ }
1114
+ catch {
1115
+ continue;
1116
+ }
1117
+ for (const entry of entries) {
1118
+ if (!entry.startsWith('rollout-') || !entry.endsWith('.jsonl')) {
1119
+ continue;
1120
+ }
1121
+ const candidatePath = join(dayDir, entry);
1122
+ let fileStat;
1123
+ try {
1124
+ fileStat = await stat(candidatePath);
1125
+ }
1126
+ catch {
1127
+ continue;
1128
+ }
1129
+ if (!fileStat.isFile() || fileStat.mtimeMs < cutoffMs) {
1130
+ continue;
1131
+ }
1132
+ candidates.push({ path: candidatePath, mtimeMs: fileStat.mtimeMs });
1133
+ }
1134
+ }
1135
+ candidates.sort((left, right) => right.mtimeMs - left.mtimeMs);
1136
+ for (const requireThreadHint of threadIdHint ? [true, false] : [false]) {
1137
+ for (const candidate of candidates) {
1138
+ let prefix;
1139
+ try {
1140
+ prefix = await readProviderLinearChildLaneFilePrefix(candidate.path, PROVIDER_LINEAR_CHILD_LANE_SESSION_LOG_HEADER_BYTES);
1141
+ }
1142
+ catch {
1143
+ continue;
1144
+ }
1145
+ const matchesThreadHint = !threadIdHint ||
1146
+ providerLinearChildLaneSessionLogPathMatchesThreadId(candidate.path, threadIdHint) ||
1147
+ (sessionMetaNeedle !== null && prefix.includes(sessionMetaNeedle));
1148
+ if (requireThreadHint && !matchesThreadHint) {
1149
+ continue;
1150
+ }
1151
+ if (!requireThreadHint && Number.isFinite(startedAtMs) && candidate.mtimeMs < startedAtMs) {
1152
+ const mtimeWithinSkewWindow = candidate.mtimeMs + PROVIDER_LINEAR_CHILD_LANE_SESSION_LOG_MTIME_SKEW_MS >= startedAtMs;
1153
+ if (!mtimeWithinSkewWindow ||
1154
+ !prefixHasProviderLinearChildLaneSessionTimestampAtOrAfter(prefix, startedAtMs)) {
1155
+ continue;
1156
+ }
1157
+ }
1158
+ if (prefixContainsProviderLinearChildLaneSessionHeader(prefix, input.workspacePath, input.promptNeedles)) {
1159
+ return candidate.path;
1160
+ }
1161
+ }
1162
+ }
1163
+ return null;
1164
+ }
1165
+ function buildProviderLinearChildLaneAppserverStartupTimeoutMessage(context, timeoutMs) {
1166
+ const timeoutSeconds = Math.max(1, Math.floor(timeoutMs / 1000));
1167
+ return `Appserver child lane startup stalled after runtime selection for ${timeoutSeconds}s without matching session-log startup evidence. Invalidate the lane and relaunch under CLI, or inspect appserver session startup for ${context.issueIdentifier}.`;
1168
+ }
1169
+ async function waitForProviderLinearChildLaneAppserverStartup(input) {
1170
+ const promptNeedles = buildProviderLinearChildLaneSessionPromptNeedles(input.context);
1171
+ const currentClockMs = () => {
1172
+ const parsed = Date.parse(input.deps.now());
1173
+ return Number.isFinite(parsed) ? parsed : Date.now();
1174
+ };
1175
+ const startClockMs = currentClockMs();
1176
+ const startupDeadlineMs = startClockMs + PROVIDER_LINEAR_CHILD_LANE_APPSERVER_STARTUP_TIMEOUT_MS;
1177
+ for (;;) {
1178
+ if (input.isExecSettled()) {
1179
+ return null;
1180
+ }
1181
+ const remainingBeforeDiscoveryMs = startupDeadlineMs - currentClockMs();
1182
+ if (remainingBeforeDiscoveryMs < 0) {
1183
+ break;
1184
+ }
1185
+ const sessionLogPath = await input.deps.discoverStartupSessionLogPath({
1186
+ env: input.env,
1187
+ workspacePath: input.laneWorkspacePath,
1188
+ promptNeedles,
1189
+ startedAt: input.startedAt
1190
+ });
1191
+ if (sessionLogPath) {
1192
+ return sessionLogPath;
1193
+ }
1194
+ const remainingAfterDiscoveryMs = startupDeadlineMs - currentClockMs();
1195
+ if (remainingAfterDiscoveryMs <= 0) {
1196
+ break;
1197
+ }
1198
+ const nextSleepMs = Math.min(PROVIDER_LINEAR_CHILD_LANE_APPSERVER_STARTUP_POLL_INTERVAL_MS, remainingAfterDiscoveryMs);
1199
+ await input.deps.sleep(nextSleepMs);
1200
+ }
1201
+ throw new Error(buildProviderLinearChildLaneAppserverStartupTimeoutMessage(input.context, PROVIDER_LINEAR_CHILD_LANE_APPSERVER_STARTUP_TIMEOUT_MS));
1202
+ }
1203
+ async function recoverProviderLinearChildLaneExecResultAfterAbort(input) {
1204
+ if (!input.execSettled) {
1205
+ input.abortController.abort(input.error);
1206
+ }
1207
+ if (!input.execPromise) {
1208
+ return null;
1209
+ }
1210
+ try {
1211
+ return await input.execPromise;
1212
+ }
1213
+ catch {
1214
+ return null;
1215
+ }
1216
+ }
1217
+ async function resolveChildLaneRuntimeContext(env, repoRoot, runId) {
1218
+ const requestedMode = parseRuntimeMode(env.CODEX_ORCHESTRATOR_RUNTIME_MODE_ACTIVE ?? env.CODEX_ORCHESTRATOR_RUNTIME_MODE ?? null);
1219
+ return await createRuntimeCodexCommandContext({
1220
+ requestedMode,
1221
+ executionMode: 'mcp',
1222
+ repoRoot,
1223
+ env: { ...process.env, ...env },
1224
+ runId
1225
+ });
1226
+ }
1227
+ async function loadProviderLinearChildLaneContext(env = process.env) {
1228
+ const manifestPath = normalizeOptionalString(env.CODEX_ORCHESTRATOR_MANIFEST_PATH);
1229
+ if (!manifestPath) {
1230
+ throw new Error('CODEX_ORCHESTRATOR_MANIFEST_PATH is required for provider-linear-child-lane.');
1231
+ }
1232
+ const rawManifest = JSON.parse(await readFile(resolve(manifestPath), 'utf8'));
1233
+ const runDir = dirname(resolve(manifestPath));
1234
+ const repoRoot = normalizeOptionalString(env.CODEX_ORCHESTRATOR_ROOT) ?? process.cwd();
1235
+ const runId = normalizeOptionalString(rawManifest.run_id) ?? normalizeOptionalString(env.CODEX_ORCHESTRATOR_RUN_ID);
1236
+ const taskId = normalizeOptionalString(rawManifest.task_id) ?? normalizeOptionalString(env.CODEX_ORCHESTRATOR_TASK_ID);
1237
+ const parentRunId = normalizeOptionalString(rawManifest.parent_run_id);
1238
+ const issueId = normalizeOptionalString(rawManifest.issue_id);
1239
+ const issueIdentifier = normalizeOptionalString(rawManifest.issue_identifier);
1240
+ const stream = normalizeOptionalString(env[PROVIDER_LINEAR_CHILD_LANE_STREAM_ENV]);
1241
+ const purpose = normalizeOptionalString(env[PROVIDER_LINEAR_CHILD_LANE_PURPOSE_ENV]);
1242
+ const parentWorkspacePath = normalizeOptionalString(env[PROVIDER_LINEAR_CHILD_LANE_PARENT_WORKSPACE_PATH_ENV]);
1243
+ const rawScope = {
1244
+ files: normalizeStringArrayFromEnv(env[PROVIDER_LINEAR_CHILD_LANE_FILES_ENV]),
1245
+ phases: normalizeStringArrayFromEnv(env[PROVIDER_LINEAR_CHILD_LANE_PHASES_ENV])
1246
+ };
1247
+ let scope;
1248
+ try {
1249
+ scope = resolveProviderLinearChildLaneScopeContract(rawScope);
1250
+ }
1251
+ catch (error) {
1252
+ throw new Error(`provider-linear-child-lane scope is invalid: ${error instanceof Error ? error.message : String(error)}`);
1253
+ }
1254
+ if (!runId ||
1255
+ !taskId ||
1256
+ !parentRunId ||
1257
+ !issueId ||
1258
+ !issueIdentifier ||
1259
+ !stream ||
1260
+ !purpose ||
1261
+ !parentWorkspacePath ||
1262
+ (rawScope.files.length === 0 && rawScope.phases.length === 0)) {
1263
+ throw new Error('provider-linear-child-lane context is missing required manifest or env fields.');
1264
+ }
1265
+ const capturedAt = new Date().toISOString();
1266
+ return {
1267
+ manifestPath: resolve(manifestPath),
1268
+ runDir,
1269
+ repoRoot: resolve(repoRoot),
1270
+ runId,
1271
+ taskId,
1272
+ parentRunId,
1273
+ issueId,
1274
+ issueIdentifier,
1275
+ stream,
1276
+ purpose,
1277
+ instructions: normalizeOptionalString(env[PROVIDER_LINEAR_CHILD_LANE_INSTRUCTIONS_ENV]),
1278
+ scope,
1279
+ runMemoryPromptLines: buildRunMemoryPromptLines(selectRunMemoryForRole({
1280
+ role: 'delegate',
1281
+ manifest: rawManifest,
1282
+ hints: [purpose, ...scope.files, ...scope.phases]
1283
+ })),
1284
+ parentWorkspacePath: resolve(parentWorkspacePath),
1285
+ parentSnapshot: {
1286
+ base_sha: normalizeOptionalString(env[PROVIDER_LINEAR_CHILD_LANE_PARENT_SNAPSHOT_BASE_SHA_ENV]),
1287
+ issue_updated_at: normalizeOptionalString(env[PROVIDER_LINEAR_CHILD_LANE_PARENT_SNAPSHOT_ISSUE_UPDATED_AT_ENV]),
1288
+ issue_state: normalizeOptionalString(env[PROVIDER_LINEAR_CHILD_LANE_PARENT_SNAPSHOT_ISSUE_STATE_ENV]),
1289
+ issue_state_type: normalizeOptionalString(env[PROVIDER_LINEAR_CHILD_LANE_PARENT_SNAPSHOT_ISSUE_STATE_TYPE_ENV]),
1290
+ captured_at: normalizeOptionalString(env[PROVIDER_LINEAR_CHILD_LANE_PARENT_SNAPSHOT_CAPTURED_AT_ENV]) ?? capturedAt
1291
+ }
1292
+ };
1293
+ }
1294
+ function sanitizeChildLaneStreamSegment(stream) {
1295
+ const collapsed = stream.trim().replaceAll('\\', '/').split('/').map((segment) => segment.trim()).filter(Boolean).join('-');
1296
+ const sanitized = collapsed
1297
+ .replace(/[^A-Za-z0-9._-]+/gu, '-')
1298
+ .replace(/\.{2,}/gu, '-')
1299
+ .replace(/^-+/u, '')
1300
+ .replace(/-+$/u, '')
1301
+ .replace(/^\.+/u, '')
1302
+ .replace(/\.+$/u, '');
1303
+ return sanitized.length > 0 ? sanitized : 'lane';
1304
+ }
1305
+ async function prepareLaneWorkspace(context) {
1306
+ const safeStream = sanitizeChildLaneStreamSegment(context.stream);
1307
+ const laneWorkspacePath = ensurePathWithinRoot(context.parentWorkspacePath, join(context.parentWorkspacePath, '.child-lanes', `${safeStream}-${context.runId}`));
1308
+ const laneBranch = `child-lane/${safeStream}-${context.runId}`.slice(0, 120);
1309
+ await rm(laneWorkspacePath, { recursive: true, force: true });
1310
+ await mkdir(dirname(laneWorkspacePath), { recursive: true });
1311
+ await execFileAsync('git', ['clone', '--local', context.parentWorkspacePath, laneWorkspacePath]);
1312
+ const baseSha = context.parentSnapshot.base_sha ?? 'HEAD';
1313
+ await execFileAsync('git', ['-C', laneWorkspacePath, 'checkout', '--detach', baseSha]);
1314
+ await execFileAsync('git', ['-C', laneWorkspacePath, 'switch', '-c', laneBranch]);
1315
+ const remotes = (await execFileAsync('git', ['-C', laneWorkspacePath, 'remote'], {
1316
+ maxBuffer: 1024 * 1024
1317
+ })).stdout
1318
+ .split(/\r?\n/u)
1319
+ .map((entry) => normalizeOptionalString(entry))
1320
+ .filter((entry) => entry !== null);
1321
+ for (const remote of remotes) {
1322
+ await execFileAsync('git', [
1323
+ '-C',
1324
+ laneWorkspacePath,
1325
+ 'remote',
1326
+ 'set-url',
1327
+ '--push',
1328
+ remote,
1329
+ 'no_push://provider-linear-child-lane'
1330
+ ]);
1331
+ }
1332
+ return { laneWorkspacePath, laneBranch };
1333
+ }
1334
+ async function createPatchArtifact(laneWorkspacePath, runDir, baselineRef = 'HEAD', targetRef = null, currentHeadSha = null) {
1335
+ await execFileAsync('git', ['-C', laneWorkspacePath, 'add', '-N', '.']);
1336
+ const workspaceDiff = await execFileAsync('git', ['-C', laneWorkspacePath, 'diff', '--binary', '--no-ext-diff', baselineRef, '--', '.'], {
1337
+ maxBuffer: 20 * 1024 * 1024
1338
+ });
1339
+ const committedDiff = targetRef !== null
1340
+ ? await execFileAsync('git', ['-C', laneWorkspacePath, 'diff', '--binary', '--no-ext-diff', baselineRef, targetRef, '--', '.'], {
1341
+ maxBuffer: 20 * 1024 * 1024
1342
+ })
1343
+ : null;
1344
+ const shouldComposeCommittedAndWorkspaceDiff = committedDiff !== null &&
1345
+ committedDiff.stdout.length > 0 &&
1346
+ workspaceDiff.stdout.length > 0 &&
1347
+ (currentHeadSha === baselineRef || !currentHeadSha);
1348
+ let diffOutput;
1349
+ if (shouldComposeCommittedAndWorkspaceDiff) {
1350
+ if (!targetRef) {
1351
+ throw new Error(`Child-lane patch export failed closed while composing ${baselineRef}: missing transient child commit ref`);
1352
+ }
1353
+ const compositionRoot = await mkdtemp(join(tmpdir(), 'provider-linear-child-lane-compose-'));
1354
+ const compositionWorkspacePath = join(compositionRoot, 'workspace');
1355
+ const workspacePatchPath = join(compositionRoot, 'workspace.patch');
1356
+ let compositionWorktreeCreated = false;
1357
+ try {
1358
+ await execFileAsync('git', ['-C', laneWorkspacePath, 'worktree', 'add', '--detach', compositionWorkspacePath, baselineRef], {
1359
+ maxBuffer: 20 * 1024 * 1024
1360
+ });
1361
+ compositionWorktreeCreated = true;
1362
+ await writeFile(workspacePatchPath, workspaceDiff.stdout, 'utf8');
1363
+ await execFileAsync('git', ['-C', compositionWorkspacePath, 'apply', '--whitespace=nowarn', workspacePatchPath], {
1364
+ maxBuffer: 20 * 1024 * 1024
1365
+ });
1366
+ await execFileAsync('git', ['-C', compositionWorkspacePath, 'add', '-A'], {
1367
+ maxBuffer: 20 * 1024 * 1024
1368
+ });
1369
+ const workspaceSnapshotTree = normalizeOptionalString((await execFileAsync('git', ['-C', compositionWorkspacePath, 'write-tree'], {
1370
+ maxBuffer: 20 * 1024 * 1024
1371
+ })).stdout);
1372
+ if (!workspaceSnapshotTree) {
1373
+ throw new Error('missing temporary workspace snapshot tree');
1374
+ }
1375
+ const workspaceSnapshotCommit = normalizeOptionalString((await execFileAsync('git', [
1376
+ '-C',
1377
+ compositionWorkspacePath,
1378
+ 'commit-tree',
1379
+ workspaceSnapshotTree,
1380
+ '-p',
1381
+ baselineRef,
1382
+ '-m',
1383
+ 'provider-linear-child-lane workspace snapshot'
1384
+ ], {
1385
+ maxBuffer: 20 * 1024 * 1024,
1386
+ env: {
1387
+ ...process.env,
1388
+ GIT_AUTHOR_NAME: 'Codex',
1389
+ GIT_AUTHOR_EMAIL: 'codex@example.com',
1390
+ GIT_COMMITTER_NAME: 'Codex',
1391
+ GIT_COMMITTER_EMAIL: 'codex@example.com'
1392
+ }
1393
+ })).stdout);
1394
+ if (!workspaceSnapshotCommit) {
1395
+ throw new Error('missing temporary workspace snapshot commit');
1396
+ }
1397
+ await execFileAsync('git', ['-C', compositionWorkspacePath, 'checkout', '--detach', workspaceSnapshotCommit], {
1398
+ maxBuffer: 20 * 1024 * 1024
1399
+ });
1400
+ await execFileAsync('git', ['-C', compositionWorkspacePath, 'merge', '--no-commit', '--no-ff', targetRef], {
1401
+ maxBuffer: 20 * 1024 * 1024
1402
+ });
1403
+ await execFileAsync('git', ['-C', compositionWorkspacePath, 'add', '-N', '.'], {
1404
+ maxBuffer: 20 * 1024 * 1024
1405
+ });
1406
+ const composedDiff = await execFileAsync('git', ['-C', compositionWorkspacePath, 'diff', '--binary', '--no-ext-diff', baselineRef, '--', '.'], {
1407
+ maxBuffer: 20 * 1024 * 1024
1408
+ });
1409
+ diffOutput = composedDiff.stdout;
1410
+ }
1411
+ catch (error) {
1412
+ const detail = error instanceof Error ? error.message : String(error);
1413
+ throw new Error(`Child-lane patch export failed closed while composing ${baselineRef}${targetRef ? ` with transient child commit ${targetRef}` : ''} and live workspace changes: ${detail}`);
1414
+ }
1415
+ finally {
1416
+ if (compositionWorktreeCreated) {
1417
+ await execFileAsync('git', ['-C', laneWorkspacePath, 'worktree', 'remove', '--force', compositionWorkspacePath], {
1418
+ maxBuffer: 20 * 1024 * 1024
1419
+ }).catch(() => undefined);
1420
+ }
1421
+ await rm(compositionRoot, { recursive: true, force: true }).catch(() => undefined);
1422
+ }
1423
+ }
1424
+ else {
1425
+ const diffChunks = [];
1426
+ if (committedDiff && (currentHeadSha === baselineRef || !currentHeadSha)) {
1427
+ diffChunks.push(committedDiff.stdout);
1428
+ }
1429
+ if (workspaceDiff.stdout.length > 0 || diffChunks.length === 0) {
1430
+ diffChunks.push(workspaceDiff.stdout);
1431
+ }
1432
+ diffOutput = diffChunks.join('');
1433
+ }
1434
+ const patchArtifactPath = join(runDir, 'provider-linear-child-lane.patch');
1435
+ await writeFile(patchArtifactPath, diffOutput, 'utf8');
1436
+ return {
1437
+ patchArtifactPath,
1438
+ patchBytes: Buffer.byteLength(diffOutput, 'utf8')
1439
+ };
1440
+ }
1441
+ async function readProviderLinearChildLaneHeadSha(laneWorkspacePath) {
1442
+ const result = await execFileAsync('git', ['-C', laneWorkspacePath, 'rev-parse', 'HEAD'], {
1443
+ maxBuffer: 1024 * 1024
1444
+ });
1445
+ return normalizeOptionalString(result.stdout);
1446
+ }
1447
+ async function countProviderLinearChildLaneReflogEntries(laneWorkspacePath) {
1448
+ const result = await execFileAsync('git', ['-C', laneWorkspacePath, 'reflog', '--format=%H'], {
1449
+ maxBuffer: 1024 * 1024
1450
+ });
1451
+ return result.stdout
1452
+ .split(/\r?\n/u)
1453
+ .map((line) => normalizeOptionalString(line))
1454
+ .filter((line) => line !== null).length;
1455
+ }
1456
+ async function detectProviderLinearChildLaneCreatedCommitShas(laneWorkspacePath, startingHeadSha, startingReflogEntryCount = 0) {
1457
+ if (!startingHeadSha) {
1458
+ return [];
1459
+ }
1460
+ const result = await execFileAsync('git', ['-C', laneWorkspacePath, 'reflog', '--format=%H%x09%gs'], {
1461
+ maxBuffer: 1024 * 1024
1462
+ });
1463
+ const reflogEntries = result.stdout.split(/\r?\n/u).filter((line) => line.trim().length > 0);
1464
+ const newEntryCount = Math.max(0, reflogEntries.length - Math.max(0, startingReflogEntryCount));
1465
+ const createdCommitShas = [];
1466
+ const seen = new Set();
1467
+ for (const line of reflogEntries.slice(0, newEntryCount)) {
1468
+ const [rawSha, rawSummary = ''] = line.split('\t', 2);
1469
+ const sha = normalizeOptionalString(rawSha);
1470
+ const summary = normalizeOptionalString(rawSummary);
1471
+ if (!sha ||
1472
+ !summary ||
1473
+ sha === startingHeadSha ||
1474
+ seen.has(sha) ||
1475
+ !/^(?:am|commit(?:\s+\([^)]*\))?|cherry-pick|merge(?:\s+[^:]*)?|revert|rebase(?:\s+-i)?\s+\((?:pick|reword|edit|squash|fixup|continue|finish)\)):/u.test(summary)) {
1476
+ continue;
1477
+ }
1478
+ seen.add(sha);
1479
+ createdCommitShas.push(sha);
1480
+ }
1481
+ return createdCommitShas;
1482
+ }
1483
+ function resolveProviderLinearChildLaneUnauthorizedCommitMessage(input) {
1484
+ if (input.createdCommitShas.length === 0) {
1485
+ if (!input.expectedBaseSha || !input.currentHeadSha || input.expectedBaseSha === input.currentHeadSha) {
1486
+ return null;
1487
+ }
1488
+ return `Child lane created commit ${input.currentHeadSha} from parent base ${input.expectedBaseSha} for ${input.context.issueIdentifier} instead of returning an uncommitted patch artifact. Invalidate the lane and relaunch; parent acceptance owns integration and branch updates.`;
1489
+ }
1490
+ const renderedCreatedCommitShas = input.createdCommitShas.slice(0, 3).join(', ');
1491
+ const currentHeadSuffix = input.expectedBaseSha && input.currentHeadSha === input.expectedBaseSha
1492
+ ? ' and reset HEAD back to the parent base'
1493
+ : input.currentHeadSha
1494
+ ? ` and left HEAD at ${input.currentHeadSha}`
1495
+ : '';
1496
+ if (!input.expectedBaseSha) {
1497
+ return `Child lane created commit(s) ${renderedCreatedCommitShas} for ${input.context.issueIdentifier} instead of returning an uncommitted patch artifact${currentHeadSuffix}. Invalidate the lane and relaunch; parent acceptance owns integration and branch updates.`;
1498
+ }
1499
+ return `Child lane created commit(s) ${renderedCreatedCommitShas} from parent base ${input.expectedBaseSha} for ${input.context.issueIdentifier} instead of returning an uncommitted patch artifact${currentHeadSuffix}. Invalidate the lane and relaunch; parent acceptance owns integration and branch updates.`;
1500
+ }
1501
+ async function writeChildLaneProof(runDir, proof) {
1502
+ await writeFile(join(runDir, PROVIDER_LINEAR_CHILD_LANE_PROOF_FILENAME), `${JSON.stringify(proof, null, 2)}\n`, 'utf8');
1503
+ }
1504
+ function buildFailedChildLaneProof(input) {
1505
+ return {
1506
+ issue_id: input.context.issueId,
1507
+ issue_identifier: input.context.issueIdentifier,
1508
+ task_id: input.context.taskId,
1509
+ run_id: input.context.runId,
1510
+ parent_run_id: input.context.parentRunId,
1511
+ stream: input.context.stream,
1512
+ purpose: input.context.purpose,
1513
+ instructions: input.context.instructions,
1514
+ scope: input.context.scope,
1515
+ parent_snapshot: input.context.parentSnapshot,
1516
+ lane_workspace_path: input.laneWorkspacePath,
1517
+ lane_branch: input.laneBranch,
1518
+ patch_artifact_path: input.patchArtifactPath ?? join(input.context.runDir, 'provider-linear-child-lane.patch'),
1519
+ patch_bytes: input.patchBytes ?? 0,
1520
+ thread_id: input.parsed?.threadId ?? null,
1521
+ latest_turn_id: input.parsed?.turnId ?? null,
1522
+ latest_session_id: input.session?.sessionId ?? null,
1523
+ latest_session_id_source: input.session?.source ?? null,
1524
+ last_event: input.parsed?.lastEvent ?? null,
1525
+ last_message: input.lastMessage,
1526
+ last_event_at: input.parsed?.lastEventAt ?? null,
1527
+ tokens: input.parsed?.tokens ?? buildEmptyProviderLinearWorkerTokenUsage(),
1528
+ rate_limits: input.parsed?.rateLimits ?? null,
1529
+ status: 'failed',
1530
+ updated_at: input.updatedAt
1531
+ };
1532
+ }
1533
+ export async function runProviderLinearChildLane(env = process.env, dependencyOverrides = {}) {
1534
+ const deps = {
1535
+ now: () => new Date().toISOString(),
1536
+ sleep: async (ms) => {
1537
+ await new Promise((resolvePromise) => {
1538
+ setTimeout(resolvePromise, ms);
1539
+ });
1540
+ },
1541
+ execRunner: defaultExecRunner,
1542
+ discoverStartupSessionLogPath: discoverProviderLinearChildLaneSessionLogPath,
1543
+ ...dependencyOverrides
1544
+ };
1545
+ const context = await loadProviderLinearChildLaneContext(env);
1546
+ const { laneWorkspacePath, laneBranch } = await prepareLaneWorkspace(context);
1547
+ const startingHeadSha = await readProviderLinearChildLaneHeadSha(laneWorkspacePath).catch(() => null);
1548
+ const startingReflogEntryCount = await countProviderLinearChildLaneReflogEntries(laneWorkspacePath).catch(() => 0);
1549
+ try {
1550
+ const runtimeContext = await resolveChildLaneRuntimeContext(env, laneWorkspacePath, context.runId);
1551
+ logger.info(`[provider-linear-child-lane-runtime] ${formatRuntimeSelectionSummary(runtimeContext.runtime)}`);
1552
+ const childEnv = { ...process.env, ...env, ...runtimeContext.env };
1553
+ childEnv.CODEX_NON_INTERACTIVE = '1';
1554
+ childEnv.CODEX_NO_INTERACTIVE = '1';
1555
+ childEnv.CODEX_INTERACTIVE = '0';
1556
+ delete childEnv.CODEX_THREAD_ID;
1557
+ const prompt = buildChildLanePrompt(context);
1558
+ const { command, args } = resolveRuntimeCodexCommand(['exec', '--json', prompt], runtimeContext);
1559
+ const startedAt = deps.now();
1560
+ const execAbortController = new AbortController();
1561
+ let execSettled = false;
1562
+ let execPromise = null;
1563
+ let scopeDriftMessage = null;
1564
+ let execResult = null;
1565
+ try {
1566
+ execPromise = deps.execRunner({
1567
+ command,
1568
+ args,
1569
+ cwd: laneWorkspacePath,
1570
+ env: childEnv,
1571
+ mirrorOutput: false,
1572
+ abortSignal: execAbortController.signal
1573
+ });
1574
+ void execPromise.then(() => {
1575
+ execSettled = true;
1576
+ }, () => {
1577
+ execSettled = true;
1578
+ });
1579
+ if (runtimeContext.runtime.selected_mode === 'appserver') {
1580
+ const startupRace = await Promise.race([
1581
+ execPromise.then((result) => ({ kind: 'exec', result })),
1582
+ waitForProviderLinearChildLaneAppserverStartup({
1583
+ context,
1584
+ laneWorkspacePath,
1585
+ env: childEnv,
1586
+ startedAt,
1587
+ isExecSettled: () => execSettled,
1588
+ deps
1589
+ }).then((sessionLogPath) => ({ kind: 'startup', sessionLogPath }))
1590
+ ]);
1591
+ if (startupRace.kind === 'exec') {
1592
+ execResult = startupRace.result;
1593
+ }
1594
+ else {
1595
+ if (startupRace.sessionLogPath) {
1596
+ logger.info(`[provider-linear-child-lane-runtime] appserver startup observed via session log ${basename(startupRace.sessionLogPath)}`);
1597
+ }
1598
+ const execOrDriftRace = startupRace.sessionLogPath
1599
+ ? await Promise.race([
1600
+ execPromise.then((result) => ({ kind: 'exec', result })),
1601
+ waitForProviderLinearChildLaneScopeDrift({
1602
+ context,
1603
+ sessionLogPath: startupRace.sessionLogPath,
1604
+ startedAt,
1605
+ isExecSettled: () => execSettled,
1606
+ deps
1607
+ }).then((message) => ({ kind: 'drift', message }))
1608
+ ])
1609
+ : ({ kind: 'exec', result: await execPromise });
1610
+ if (execOrDriftRace.kind === 'exec') {
1611
+ execResult = execOrDriftRace.result;
1612
+ }
1613
+ else {
1614
+ scopeDriftMessage = execOrDriftRace.message;
1615
+ if (scopeDriftMessage) {
1616
+ execResult = await recoverProviderLinearChildLaneExecResultAfterAbort({
1617
+ abortController: execAbortController,
1618
+ error: new Error(scopeDriftMessage),
1619
+ execPromise,
1620
+ execSettled
1621
+ });
1622
+ if (!execResult) {
1623
+ throw new Error(scopeDriftMessage);
1624
+ }
1625
+ }
1626
+ else {
1627
+ execResult = await execPromise;
1628
+ }
1629
+ }
1630
+ }
1631
+ }
1632
+ else {
1633
+ execResult = await execPromise;
1634
+ }
1635
+ }
1636
+ catch (error) {
1637
+ const normalizedError = error instanceof Error ? error : new Error(String(error));
1638
+ execResult = await recoverProviderLinearChildLaneExecResultAfterAbort({
1639
+ abortController: execAbortController,
1640
+ error: normalizedError,
1641
+ execPromise,
1642
+ execSettled
1643
+ });
1644
+ if (execResult) {
1645
+ execSettled = true;
1646
+ }
1647
+ else {
1648
+ const failedProof = buildFailedChildLaneProof({
1649
+ context,
1650
+ laneWorkspacePath,
1651
+ laneBranch,
1652
+ lastMessage: normalizedError.message,
1653
+ updatedAt: deps.now()
1654
+ });
1655
+ await writeChildLaneProof(context.runDir, failedProof);
1656
+ throw normalizedError;
1657
+ }
1658
+ }
1659
+ if (!execResult) {
1660
+ throw new Error('provider-linear-child-lane completed without an exec result');
1661
+ }
1662
+ const parsed = parseProviderLinearWorkerJsonl(execResult.stdout);
1663
+ const session = deriveLatestTurnSessionId({
1664
+ threadId: parsed.threadId,
1665
+ turnId: parsed.turnId
1666
+ });
1667
+ const currentHeadSha = await readProviderLinearChildLaneHeadSha(laneWorkspacePath).catch(() => null);
1668
+ const createdCommitShas = await detectProviderLinearChildLaneCreatedCommitShas(laneWorkspacePath, startingHeadSha, startingReflogEntryCount).catch(() => []);
1669
+ const unauthorizedCommitMessage = resolveProviderLinearChildLaneUnauthorizedCommitMessage({
1670
+ context,
1671
+ expectedBaseSha: context.parentSnapshot.base_sha,
1672
+ currentHeadSha,
1673
+ createdCommitShas
1674
+ });
1675
+ const patchTargetRef = createdCommitShas[0] ?? null;
1676
+ const patchBaselineRef = context.parentSnapshot.base_sha &&
1677
+ (patchTargetRef !== null || (currentHeadSha !== null && context.parentSnapshot.base_sha !== currentHeadSha))
1678
+ ? context.parentSnapshot.base_sha
1679
+ : 'HEAD';
1680
+ const forcedFailureMessage = scopeDriftMessage ?? unauthorizedCommitMessage;
1681
+ let patchArtifactPath = join(context.runDir, 'provider-linear-child-lane.patch');
1682
+ let patchBytes = 0;
1683
+ let proof;
1684
+ try {
1685
+ ({ patchArtifactPath, patchBytes } = await createPatchArtifact(laneWorkspacePath, context.runDir, patchBaselineRef, patchTargetRef, currentHeadSha));
1686
+ proof = {
1687
+ issue_id: context.issueId,
1688
+ issue_identifier: context.issueIdentifier,
1689
+ task_id: context.taskId,
1690
+ run_id: context.runId,
1691
+ parent_run_id: context.parentRunId,
1692
+ stream: context.stream,
1693
+ purpose: context.purpose,
1694
+ instructions: context.instructions,
1695
+ scope: context.scope,
1696
+ parent_snapshot: context.parentSnapshot,
1697
+ lane_workspace_path: laneWorkspacePath,
1698
+ lane_branch: laneBranch,
1699
+ patch_artifact_path: patchArtifactPath,
1700
+ patch_bytes: patchBytes,
1701
+ thread_id: parsed.threadId,
1702
+ latest_turn_id: parsed.turnId,
1703
+ latest_session_id: session.sessionId,
1704
+ latest_session_id_source: session.source,
1705
+ last_event: parsed.lastEvent,
1706
+ last_message: forcedFailureMessage ?? parsed.finalMessage,
1707
+ last_event_at: parsed.lastEventAt,
1708
+ tokens: parsed.tokens,
1709
+ rate_limits: parsed.rateLimits,
1710
+ status: execResult.exitCode === 0 && !forcedFailureMessage ? 'succeeded' : 'failed',
1711
+ updated_at: deps.now()
1712
+ };
1713
+ await writeChildLaneProof(context.runDir, proof);
1714
+ }
1715
+ catch (error) {
1716
+ const normalizedError = error instanceof Error ? error : new Error(String(error));
1717
+ const failedProof = buildFailedChildLaneProof({
1718
+ context,
1719
+ laneWorkspacePath,
1720
+ laneBranch,
1721
+ lastMessage: normalizedError.message,
1722
+ updatedAt: deps.now(),
1723
+ parsed,
1724
+ session,
1725
+ patchArtifactPath,
1726
+ patchBytes
1727
+ });
1728
+ try {
1729
+ await writeChildLaneProof(context.runDir, failedProof);
1730
+ }
1731
+ catch (proofWriteError) {
1732
+ logger.warn(`[provider-linear-child-lane-proof] failed to persist failure proof after post-exec export error: ${proofWriteError instanceof Error ? proofWriteError.message : String(proofWriteError)}`);
1733
+ }
1734
+ throw normalizedError;
1735
+ }
1736
+ if (proof.status !== 'succeeded') {
1737
+ throw new Error(proof.last_message ?? `provider-linear-child-lane exited with code ${execResult.exitCode ?? 'unknown'}`);
1738
+ }
1739
+ return proof;
1740
+ }
1741
+ finally {
1742
+ await compactRedundantTrustedChildLaneProjectsBestEffort({
1743
+ env,
1744
+ laneWorkspacePath
1745
+ });
1746
+ }
1747
+ }
1748
+ async function main() {
1749
+ await runProviderLinearChildLane();
1750
+ }
1751
+ const entry = process.argv[1] ? resolve(process.argv[1]) : null;
1752
+ const self = resolve(fileURLToPath(import.meta.url));
1753
+ if (entry && entry === self) {
1754
+ main().catch((error) => {
1755
+ logger.error(error instanceof Error ? error.message : String(error));
1756
+ process.exitCode = 1;
1757
+ });
1758
+ }
1759
+ export const __test__ = {
1760
+ buildChildLanePrompt,
1761
+ buildProviderLinearChildLaneAppserverStartupTimeoutMessage,
1762
+ createPatchArtifact,
1763
+ detectProviderLinearChildLaneCreatedCommitShas,
1764
+ discoverProviderLinearChildLaneSessionLogPath,
1765
+ planTrustedProjectCleanup,
1766
+ extractProviderLinearChildLaneScopeDriftEvidenceFromRecord,
1767
+ recoverProviderLinearChildLaneExecResultAfterAbort,
1768
+ resolveProviderLinearChildLaneUnauthorizedCommitMessage,
1769
+ scanProviderLinearChildLaneSessionLogForParentScopeDrift,
1770
+ waitForProviderLinearChildLaneAppserverStartup,
1771
+ waitForProviderLinearChildLaneScopeDrift
1772
+ };