@straiffi/archon 1.8.23 → 1.10.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 (219) hide show
  1. package/dist/client/assets/{ActivityView-BaIZ8eHC.js → ActivityView-Y5IaJiim.js} +1 -1
  2. package/dist/client/assets/GitHubActionsDialog-GWVxWGPK.js +1 -0
  3. package/dist/client/assets/VerificationDialog-C4RMuI4y.js +4 -0
  4. package/dist/client/assets/{architectureDiagram-3BPJPVTR-Dl5UF67P.js → architectureDiagram-3BPJPVTR-CeM6gbLN.js} +1 -1
  5. package/dist/client/assets/{blockDiagram-GPEHLZMM-DCcERfrZ.js → blockDiagram-GPEHLZMM-BP4hta96.js} +1 -1
  6. package/dist/client/assets/{c4Diagram-AAUBKEIU-BGhbASFm.js → c4Diagram-AAUBKEIU-5aJScvhn.js} +1 -1
  7. package/dist/client/assets/channel-B3DjZXbm.js +1 -0
  8. package/dist/client/assets/{chunk-2J33WTMH-C1ARozTV.js → chunk-2J33WTMH-BKc2IelV.js} +1 -1
  9. package/dist/client/assets/{chunk-3OPIFGDE-BEovLizF.js → chunk-3OPIFGDE-DCc15la5.js} +1 -1
  10. package/dist/client/assets/{chunk-5ZQYHXKU-C5MmlLa_.js → chunk-5ZQYHXKU-DSD9HbTp.js} +1 -1
  11. package/dist/client/assets/{chunk-727SXJPM-8y8jBk4n.js → chunk-727SXJPM-DrAjnKiA.js} +1 -1
  12. package/dist/client/assets/{chunk-AQP2D5EJ-q-mByAd3.js → chunk-AQP2D5EJ-DxKnpHfQ.js} +1 -1
  13. package/dist/client/assets/{chunk-CSCIHK7Q-BOx2mLSz.js → chunk-CSCIHK7Q-DLejrAB_.js} +1 -1
  14. package/dist/client/assets/{chunk-KSCS5N6A-zEwQFq7q.js → chunk-KSCS5N6A-BWRNmF8S.js} +1 -1
  15. package/dist/client/assets/{chunk-L5ZTLDWV-C80z6rY_.js → chunk-L5ZTLDWV-D48i966f.js} +1 -1
  16. package/dist/client/assets/{chunk-LZXEDZCA-BLaEsGPd.js → chunk-LZXEDZCA-DLhg6Bok.js} +2 -2
  17. package/dist/client/assets/{chunk-ND2GUHAM-x26BZ95L.js → chunk-ND2GUHAM-B4-7cOEq.js} +1 -1
  18. package/dist/client/assets/{chunk-NZK2D7GU-D2WAxr1p.js → chunk-NZK2D7GU-D2ttXA_d.js} +1 -1
  19. package/dist/client/assets/{chunk-O5CBEL6O-DDPCrzKh.js → chunk-O5CBEL6O-BrWrOVCq.js} +1 -1
  20. package/dist/client/assets/{chunk-WU5MYG2G-CE-JcvIc.js → chunk-WU5MYG2G-CLcPwxPe.js} +1 -1
  21. package/dist/client/assets/classDiagram-4FO5ZUOK-DproSkn2.js +1 -0
  22. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-JVMMzJxJ.js +1 -0
  23. package/dist/client/assets/{dagre-BM42HDAG-Bf7wpUk_.js → dagre-BM42HDAG-DIXN8_VX.js} +1 -1
  24. package/dist/client/assets/{diagram-2AECGRRQ-BQoKmc3B.js → diagram-2AECGRRQ-D2wjdCij.js} +1 -1
  25. package/dist/client/assets/{diagram-5GNKFQAL-D2jarPLn.js → diagram-5GNKFQAL-E1Og_PHE.js} +1 -1
  26. package/dist/client/assets/{diagram-KO2AKTUF-Bcbbjvsl.js → diagram-KO2AKTUF-B6P1Ud8a.js} +1 -1
  27. package/dist/client/assets/{diagram-LMA3HP47-t862RRBQ.js → diagram-LMA3HP47-C3Jxq56v.js} +1 -1
  28. package/dist/client/assets/{diagram-OG6HWLK6-CacbMcO0.js → diagram-OG6HWLK6-Do9LVhdL.js} +1 -1
  29. package/dist/client/assets/{erDiagram-TEJ5UH35-DRQ79baI.js → erDiagram-TEJ5UH35-DLb5E4-f.js} +1 -1
  30. package/dist/client/assets/{flowDiagram-I6XJVG4X-BfW4KEdc.js → flowDiagram-I6XJVG4X-CcTs_wU9.js} +1 -1
  31. package/dist/client/assets/{ganttDiagram-6RSMTGT7-BPIl0TAj.js → ganttDiagram-6RSMTGT7-q9ez5fri.js} +1 -1
  32. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-CRIusBMV.js → gitGraphDiagram-PVQCEYII-BcaKiz4c.js} +1 -1
  33. package/dist/client/assets/index-1CokwSFm.css +2 -0
  34. package/dist/client/assets/index-CSOKbu3w.js +276 -0
  35. package/dist/client/assets/{infoDiagram-5YYISTIA-DTKzK8EJ.js → infoDiagram-5YYISTIA-Cu0w4frE.js} +1 -1
  36. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-DVIHZLvL.js → ishikawaDiagram-YF4QCWOH-CJ47Z-ak.js} +1 -1
  37. package/dist/client/assets/{journeyDiagram-JHISSGLW-CQBZ4tCx.js → journeyDiagram-JHISSGLW-fAL_Zcvs.js} +1 -1
  38. package/dist/client/assets/{kanban-definition-UN3LZRKU-D7naegR0.js → kanban-definition-UN3LZRKU-BTNIybMC.js} +1 -1
  39. package/dist/client/assets/{line-CizZlaeV.js → line-BvsadeZR.js} +1 -1
  40. package/dist/client/assets/{mermaid-parser.core-NrDqgpZa.js → mermaid-parser.core-C0EkH2y_.js} +1 -1
  41. package/dist/client/assets/{mermaid.core-CwVO3oX0.js → mermaid.core-BU5QIIbY.js} +3 -3
  42. package/dist/client/assets/{mindmap-definition-RKZ34NQL-BWxy3Aa_.js → mindmap-definition-RKZ34NQL-CWbSYK8f.js} +1 -1
  43. package/dist/client/assets/{pieDiagram-4H26LBE5-B3gXpmBn.js → pieDiagram-4H26LBE5-t4w2cB-y.js} +1 -1
  44. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-Dpp4g8Al.js → quadrantDiagram-W4KKPZXB-Cqasvrpa.js} +1 -1
  45. package/dist/client/assets/{requirementDiagram-4Y6WPE33-lFKXnxml.js → requirementDiagram-4Y6WPE33-_l-mY-tb.js} +1 -1
  46. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-BBdtwqrS.js → sankeyDiagram-5OEKKPKP-Cr9YrZ-Y.js} +1 -1
  47. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-ANpVZzqm.js → sequenceDiagram-3UESZ5HK-DvzZhQpb.js} +1 -1
  48. package/dist/client/assets/{stateDiagram-AJRCARHV-D58ZxmKY.js → stateDiagram-AJRCARHV-cFjPpAO3.js} +1 -1
  49. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-btHMH5Cg.js +1 -0
  50. package/dist/client/assets/{timeline-definition-PNZ67QCA-B6FK8IQC.js → timeline-definition-PNZ67QCA-BjRNrEJY.js} +1 -1
  51. package/dist/client/assets/{vennDiagram-CIIHVFJN-lFWtZEEY.js → vennDiagram-CIIHVFJN-xs9afjrw.js} +1 -1
  52. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-DteAVp4l.js → wardleyDiagram-YWT4CUSO-DH68ZYti.js} +1 -1
  53. package/dist/client/assets/{xychartDiagram-2RQKCTM6-CNcDlRLN.js → xychartDiagram-2RQKCTM6-DLW5ktTU.js} +1 -1
  54. package/dist/client/index.html +2 -2
  55. package/dist/server/db.js +225 -12
  56. package/dist/server/db.js.map +1 -1
  57. package/dist/server/index.js +80 -26
  58. package/dist/server/index.js.map +1 -1
  59. package/dist/server/lib/agent.js +368 -143
  60. package/dist/server/lib/agent.js.map +1 -1
  61. package/dist/server/lib/agentProvider.js.map +1 -1
  62. package/dist/server/lib/approvalReconciliation.js +14 -2
  63. package/dist/server/lib/approvalReconciliation.js.map +1 -1
  64. package/dist/server/lib/buildExecutionEnvironment.js +251 -57
  65. package/dist/server/lib/buildExecutionEnvironment.js.map +1 -1
  66. package/dist/server/lib/chatOperations.js +125 -3
  67. package/dist/server/lib/chatOperations.js.map +1 -1
  68. package/dist/server/lib/chatPostBuildRuns.js +12 -6
  69. package/dist/server/lib/chatPostBuildRuns.js.map +1 -1
  70. package/dist/server/lib/chatTargets.js +2 -1
  71. package/dist/server/lib/chatTargets.js.map +1 -1
  72. package/dist/server/lib/checkDefinitionCwd.js +34 -0
  73. package/dist/server/lib/checkDefinitionCwd.js.map +1 -0
  74. package/dist/server/lib/checkExecutionEnvironment.js +95 -0
  75. package/dist/server/lib/checkExecutionEnvironment.js.map +1 -0
  76. package/dist/server/lib/checkPolicyDefinitionIds.js +41 -0
  77. package/dist/server/lib/checkPolicyDefinitionIds.js.map +1 -0
  78. package/dist/server/lib/checkRepairDispatch.js +2 -0
  79. package/dist/server/lib/checkRepairDispatch.js.map +1 -1
  80. package/dist/server/lib/checkRunner.js +371 -175
  81. package/dist/server/lib/checkRunner.js.map +1 -1
  82. package/dist/server/lib/checkRuns.js +99 -8
  83. package/dist/server/lib/checkRuns.js.map +1 -1
  84. package/dist/server/lib/claudeAgentProvider.js +3 -6
  85. package/dist/server/lib/claudeAgentProvider.js.map +1 -1
  86. package/dist/server/lib/claudeStdin.js +27 -0
  87. package/dist/server/lib/claudeStdin.js.map +1 -0
  88. package/dist/server/lib/completedAssistantOutput.js +38 -0
  89. package/dist/server/lib/completedAssistantOutput.js.map +1 -0
  90. package/dist/server/lib/consequentialActionAuthority.js +99 -8
  91. package/dist/server/lib/consequentialActionAuthority.js.map +1 -1
  92. package/dist/server/lib/consequentialActions.js +42 -2
  93. package/dist/server/lib/consequentialActions.js.map +1 -1
  94. package/dist/server/lib/dockerSandboxCredentials.js +10 -3
  95. package/dist/server/lib/dockerSandboxCredentials.js.map +1 -1
  96. package/dist/server/lib/dockerSandboxEnvironmentOperations.js +9 -8
  97. package/dist/server/lib/dockerSandboxEnvironmentOperations.js.map +1 -1
  98. package/dist/server/lib/dockerSandboxExecutionEnvironment.js +149 -74
  99. package/dist/server/lib/dockerSandboxExecutionEnvironment.js.map +1 -1
  100. package/dist/server/lib/dockerSandboxExecutionProvider.js +22 -14
  101. package/dist/server/lib/dockerSandboxExecutionProvider.js.map +1 -1
  102. package/dist/server/lib/dockerSandboxNetworkPolicy.js +23 -14
  103. package/dist/server/lib/dockerSandboxNetworkPolicy.js.map +1 -1
  104. package/dist/server/lib/dockerSandboxReleaseProfiles.js +91 -0
  105. package/dist/server/lib/dockerSandboxReleaseProfiles.js.map +1 -0
  106. package/dist/server/lib/dockerSandboxRuntimeController.js +16 -1
  107. package/dist/server/lib/dockerSandboxRuntimeController.js.map +1 -1
  108. package/dist/server/lib/executionCredentialCapabilities.js +9 -6
  109. package/dist/server/lib/executionCredentialCapabilities.js.map +1 -1
  110. package/dist/server/lib/executionEnvironment.js.map +1 -1
  111. package/dist/server/lib/executionEnvironmentLifecycle.js +7 -0
  112. package/dist/server/lib/executionEnvironmentLifecycle.js.map +1 -1
  113. package/dist/server/lib/executionEnvironmentOperations.js +241 -0
  114. package/dist/server/lib/executionEnvironmentOperations.js.map +1 -0
  115. package/dist/server/lib/executionEnvironmentReconciliation.js +98 -3
  116. package/dist/server/lib/executionEnvironmentReconciliation.js.map +1 -1
  117. package/dist/server/lib/executionEnvironmentRoutes.js +66 -27
  118. package/dist/server/lib/executionEnvironmentRoutes.js.map +1 -1
  119. package/dist/server/lib/executionEnvironmentValidation.js +2 -2
  120. package/dist/server/lib/executionEnvironmentValidation.js.map +1 -1
  121. package/dist/server/lib/executionEnvironmentValidationOperations.js +58 -31
  122. package/dist/server/lib/executionEnvironmentValidationOperations.js.map +1 -1
  123. package/dist/server/lib/executionEnvironments.js +187 -9
  124. package/dist/server/lib/executionEnvironments.js.map +1 -1
  125. package/dist/server/lib/executionProfileCheckPolicy.js +14 -0
  126. package/dist/server/lib/executionProfileCheckPolicy.js.map +1 -0
  127. package/dist/server/lib/executionProfileRoutes.js +9 -1
  128. package/dist/server/lib/executionProfileRoutes.js.map +1 -1
  129. package/dist/server/lib/executionProfileStore.js +5 -3
  130. package/dist/server/lib/executionProfileStore.js.map +1 -1
  131. package/dist/server/lib/executionProfiles.js +97 -15
  132. package/dist/server/lib/executionProfiles.js.map +1 -1
  133. package/dist/server/lib/executionReconciliation.js +43 -6
  134. package/dist/server/lib/executionReconciliation.js.map +1 -1
  135. package/dist/server/lib/executionRunAuthority.js +115 -24
  136. package/dist/server/lib/executionRunAuthority.js.map +1 -1
  137. package/dist/server/lib/executionTargets.js +30 -4
  138. package/dist/server/lib/executionTargets.js.map +1 -1
  139. package/dist/server/lib/executionWorkloads.js +75 -0
  140. package/dist/server/lib/executionWorkloads.js.map +1 -0
  141. package/dist/server/lib/git.js +10 -1
  142. package/dist/server/lib/git.js.map +1 -1
  143. package/dist/server/lib/opencodeAgentProvider.js +3 -1
  144. package/dist/server/lib/opencodeAgentProvider.js.map +1 -1
  145. package/dist/server/lib/opencodeSessionOperations.js +5 -3
  146. package/dist/server/lib/opencodeSessionOperations.js.map +1 -1
  147. package/dist/server/lib/planQuestions.js +2 -1
  148. package/dist/server/lib/planQuestions.js.map +1 -1
  149. package/dist/server/lib/postBuildCheckRuns.js +9 -5
  150. package/dist/server/lib/postBuildCheckRuns.js.map +1 -1
  151. package/dist/server/lib/preview.js +47 -6
  152. package/dist/server/lib/preview.js.map +1 -1
  153. package/dist/server/lib/previewProxy.js +11 -3
  154. package/dist/server/lib/previewProxy.js.map +1 -1
  155. package/dist/server/lib/productionCheckApprovals.js +97 -35
  156. package/dist/server/lib/productionCheckApprovals.js.map +1 -1
  157. package/dist/server/lib/projectMemorySuggestions.js +29 -6
  158. package/dist/server/lib/projectMemorySuggestions.js.map +1 -1
  159. package/dist/server/lib/requiredCheckPolicy.js +3 -40
  160. package/dist/server/lib/requiredCheckPolicy.js.map +1 -1
  161. package/dist/server/lib/reviewAgentWatchdog.js +75 -0
  162. package/dist/server/lib/reviewAgentWatchdog.js.map +1 -0
  163. package/dist/server/lib/reviewFindingReconciliation.js +176 -0
  164. package/dist/server/lib/reviewFindingReconciliation.js.map +1 -0
  165. package/dist/server/lib/reviewFindings.js +33 -11
  166. package/dist/server/lib/reviewFindings.js.map +1 -1
  167. package/dist/server/lib/run.js +370 -80
  168. package/dist/server/lib/run.js.map +1 -1
  169. package/dist/server/lib/serviceRuns.js +196 -0
  170. package/dist/server/lib/serviceRuns.js.map +1 -0
  171. package/dist/server/lib/sessionTargets.js +34 -0
  172. package/dist/server/lib/sessionTargets.js.map +1 -1
  173. package/dist/server/lib/shell.js +20 -0
  174. package/dist/server/lib/shell.js.map +1 -1
  175. package/dist/server/lib/specArtifact.js +36 -15
  176. package/dist/server/lib/specArtifact.js.map +1 -1
  177. package/dist/server/lib/specBundleGroups.js +4 -4
  178. package/dist/server/lib/specBundleGroups.js.map +1 -1
  179. package/dist/server/lib/specOperations.js +2 -0
  180. package/dist/server/lib/specOperations.js.map +1 -1
  181. package/dist/server/lib/specReviewOperations.js +2 -0
  182. package/dist/server/lib/specReviewOperations.js.map +1 -1
  183. package/dist/server/lib/specTicketMaterialization.js +37 -24
  184. package/dist/server/lib/specTicketMaterialization.js.map +1 -1
  185. package/dist/server/lib/ticketFollowUpOperations.js +9 -4
  186. package/dist/server/lib/ticketFollowUpOperations.js.map +1 -1
  187. package/dist/server/lib/ticketRunOperations.js +1 -0
  188. package/dist/server/lib/ticketRunOperations.js.map +1 -1
  189. package/dist/server/lib/types.js +1 -0
  190. package/dist/server/lib/types.js.map +1 -1
  191. package/dist/server/lib/workloadExecutionEnvironment.js +2 -0
  192. package/dist/server/lib/workloadExecutionEnvironment.js.map +1 -0
  193. package/dist/server/lib/workspaceIdentity.js +30 -6
  194. package/dist/server/lib/workspaceIdentity.js.map +1 -1
  195. package/dist/server/workers/build.js +39 -8
  196. package/dist/server/workers/build.js.map +1 -1
  197. package/dist/server/workers/chat.js +142 -15
  198. package/dist/server/workers/chat.js.map +1 -1
  199. package/dist/server/workers/followUp.js +77 -20
  200. package/dist/server/workers/followUp.js.map +1 -1
  201. package/dist/server/workers/plan.js +295 -48
  202. package/dist/server/workers/plan.js.map +1 -1
  203. package/dist/server/workers/planFollowUp.js +77 -7
  204. package/dist/server/workers/planFollowUp.js.map +1 -1
  205. package/dist/server/workers/review.js +149 -38
  206. package/dist/server/workers/review.js.map +1 -1
  207. package/dist/server/workers/specReview.js +43 -5
  208. package/dist/server/workers/specReview.js.map +1 -1
  209. package/dist/server/workers/specSession.js +156 -31
  210. package/dist/server/workers/specSession.js.map +1 -1
  211. package/package.json +1 -1
  212. package/dist/client/assets/GitHubActionsDialog-BFCqikDY.js +0 -1
  213. package/dist/client/assets/VerificationDialog-B1_6HMdt.js +0 -4
  214. package/dist/client/assets/channel-BfskvH2B.js +0 -1
  215. package/dist/client/assets/classDiagram-4FO5ZUOK-D4HPfxL3.js +0 -1
  216. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-DQQ_WqAU.js +0 -1
  217. package/dist/client/assets/index-CZ1z72fo.js +0 -276
  218. package/dist/client/assets/index-SMj5ZmMs.css +0 -2
  219. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Cv8cmDfz.js +0 -1
@@ -7,8 +7,8 @@ import { GitHubApiError } from '../lib/integrations/github.js';
7
7
  import { formatGenericToolCall } from '../lib/genericToolCall.js';
8
8
  import { PlanningReferenceError, resolveExternalPlanningDescription } from '../lib/integrations/planning.js';
9
9
  import { resolveExistingWorktreePath } from '../lib/git.js';
10
- import { normalizeModelId } from '../lib/models.js';
11
- import { parseOpencodeTaskEnvelope } from '../lib/opencodeTaskSessions.js';
10
+ import { getModelProvider, normalizeModelId } from '../lib/models.js';
11
+ import { createOpencodeTaskSessionReader, parseOpencodeTaskEnvelope } from '../lib/opencodeTaskSessions.js';
12
12
  import { hostOpencodeSessionOperations } from '../lib/opencodeSessionOperations.js';
13
13
  import { markRetryableOpenCodeDatabaseLock, RetryableOpenCodeDatabaseLockError, retryOpenCodeDatabaseLocks } from '../lib/opencodeExecutionReliability.js';
14
14
  import { appendClaudeHeadlessPlanPrompt, appendPlanModeQuestionPromptRules, buildClaudeHeadlessPlanArgs } from '../lib/planModeQuestions.js';
@@ -25,9 +25,14 @@ import { createTicketMessage, upsertTicketDescriptionMessage } from '../lib/tick
25
25
  import { createAskQuestionMetadata, extractAskQuestionFromOutput, formatAskQuestionMessage } from '../lib/planQuestions.js';
26
26
  import { getTicket, resolveTicketBranch, resolveTicketTool, resolveTicketWorkerKey } from '../lib/tickets.js';
27
27
  import { createOpencodeSubagentMonitor, createSubagentRunTracker, formatAgentErrorForTranscript, getAgentErrorTranscriptRuns, readOpencodeSessionTranscript, storeTicketUsageSnapshot, storeTicketUsageSnapshotDirect, mergeUsageFromJsonLine, EMPTY_CHAT_USAGE_SNAPSHOT } from '../lib/agent.js';
28
- import { createAgentRun, emitAgentRunsUpdated, finalizeAgentRun, getNextAgentRunIndex, terminalizeAgentRunsForExecutionRun } from '../lib/agentRuns.js';
28
+ import { createAgentRun, emitAgentRunsUpdated, finalizeAgentRun, getAgentRun, getNextAgentRunIndex, terminalizeAgentRunsForExecutionRun, updateAgentRunSessionId } from '../lib/agentRuns.js';
29
29
  import { addExecutionRunLink, createExecutionRun, transitionExecutionRun } from '../lib/executionRuns.js';
30
+ import { assertProviderSessionAuthorityMatches, createExecutionProfileSelectionMetadata } from '../lib/executionRunAuthority.js';
31
+ import { classifyTicketExecutionTarget, resolveExecutionTarget, resolveProjectRootExecutionTarget } from '../lib/executionTargets.js';
32
+ import { resolveWorkloadExecutionEnvironment } from '../lib/workloadExecutionEnvironment.js';
30
33
  import { createWorkspaceProcessEnvironment } from '../lib/workspaceProcessEnvironment.js';
34
+ import { createHostLauncherEnvironment, createLegacyHostRuntimeEnvironment } from '../lib/workspaceProcessEnvironment.js';
35
+ import { hostExecutionEnvironment } from '../lib/hostExecutionEnvironment.js';
31
36
  const resolvePlannerAgentRunParent = (options) => {
32
37
  if (options.agentRunParent) {
33
38
  return options.agentRunParent;
@@ -37,6 +42,12 @@ const resolvePlannerAgentRunParent = (options) => {
37
42
  }
38
43
  return undefined;
39
44
  };
45
+ const plannerExecutionOptions = (options) => ({
46
+ executionProvider: options.executionProvider,
47
+ executionTarget: options.executionTarget,
48
+ executionLauncherEnvironment: options.executionLauncherEnvironment,
49
+ executionRuntimeEnvironment: options.executionRuntimeEnvironment,
50
+ });
40
51
  const createPlanningInvocation = (options, label, role) => {
41
52
  const parentType = options.agentRunParent?.parentType ?? (options.ticketId ? 'ticket' : options.projectId ? 'planning' : null);
42
53
  const parentId = options.agentRunParent?.parentId ?? options.ticketId ?? options.projectId ?? null;
@@ -939,13 +950,18 @@ const resolvePlannerCwd = (project, branch) => {
939
950
  }
940
951
  return resolveExistingWorktreePath(branch, project) ?? project.repo_path;
941
952
  };
942
- export const updatePlanningSessionId = (ticketId, sessionId, io) => {
953
+ export const updatePlanningSessionId = (ticketId, sessionId, io, sessionOperations) => {
943
954
  const existing = getTicket(ticketId);
944
955
  const resolvedTool = resolveTicketTool(existing, config.tool ?? 'opencode');
945
956
  db.prepare('UPDATE tickets SET planning_session_id = ?, tool = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?').run(sessionId, resolvedTool, ticketId);
946
957
  const ticket = getTicket(ticketId);
947
958
  if (ticket && resolveTicketTool(ticket, config.tool ?? 'opencode') === 'opencode') {
948
- void storeTicketUsageSnapshot(ticket.id, sessionId);
959
+ if (sessionOperations) {
960
+ void storeTicketUsageSnapshot(ticket.id, sessionId, sessionOperations);
961
+ }
962
+ else {
963
+ void storeTicketUsageSnapshot(ticket.id, sessionId);
964
+ }
949
965
  }
950
966
  const freshTicket = getTicket(ticketId) ?? ticket;
951
967
  if (freshTicket) {
@@ -996,15 +1012,15 @@ export const stopPlannerProcesses = async (children) => {
996
1012
  killPlannerProcess(child, 'SIGKILL');
997
1013
  }
998
1014
  };
999
- const opencodeSessionHasCompletedAssistantSince = (sessionId, sinceMs) => {
1000
- return hostOpencodeSessionOperations.hasCompletedAssistantSince(sessionId, sinceMs);
1001
- };
1002
1015
  const runPlannerPromptOnce = (tool, model, variant, prompt, cwd, options = {}) => {
1003
1016
  const resolvedModel = normalizeModelId(model);
1004
1017
  const finalPrompt = tool === 'claude'
1005
1018
  ? appendClaudeHeadlessPlanPrompt(prompt)
1006
1019
  : prompt;
1007
1020
  const sessionTracking = options.sessionTracking;
1021
+ const opencodeSessionOperations = options.executionProvider
1022
+ ? options.executionProvider.sessionOperations.opencode
1023
+ : hostOpencodeSessionOperations;
1008
1024
  let sessionId = null;
1009
1025
  let spawnTimestamp = null;
1010
1026
  let sessionLookupTimer = null;
@@ -1090,26 +1106,29 @@ const runPlannerPromptOnce = (tool, model, variant, prompt, cwd, options = {}) =
1090
1106
  watchdogTimer = null;
1091
1107
  }
1092
1108
  };
1093
- const bindResolvedOpencodeSession = (resolvedId) => {
1109
+ const bindResolvedProviderSession = (resolvedId) => {
1094
1110
  const normalized = resolvedId?.trim();
1095
1111
  if (!normalized) {
1096
1112
  return;
1097
1113
  }
1098
1114
  resolvedSessionId = normalized;
1099
1115
  opencodeSubagentMonitor?.setParentSessionId(normalized);
1116
+ if (options.mainRun) {
1117
+ updateAgentRunSessionId(options.mainRun.id, normalized);
1118
+ }
1100
1119
  if (sessionTracking && !hasResolvedSessionId) {
1101
1120
  hasResolvedSessionId = true;
1102
- updatePlanningSessionId(sessionTracking.ticketId, normalized, sessionTracking.io);
1121
+ updatePlanningSessionId(sessionTracking.ticketId, normalized, sessionTracking.io, opencodeSessionOperations ?? undefined);
1103
1122
  }
1104
1123
  };
1105
1124
  const attemptOpencodeSessionLookup = async () => {
1106
- if (!sessionTracking || tool !== 'opencode' || hasResolvedSessionId) {
1125
+ if (!sessionTracking || tool !== 'opencode' || hasResolvedSessionId || !opencodeSessionOperations) {
1107
1126
  return false;
1108
1127
  }
1109
1128
  try {
1110
- const session = await hostOpencodeSessionOperations.findLatestSessionAfter(spawnTimestamp ?? 0, cwd);
1129
+ const session = await opencodeSessionOperations.findLatestSessionAfter(spawnTimestamp ?? 0, cwd);
1111
1130
  if (!hasResolvedSessionId && session?.id) {
1112
- bindResolvedOpencodeSession(session.id);
1131
+ bindResolvedProviderSession(session.id);
1113
1132
  }
1114
1133
  }
1115
1134
  catch {
@@ -1121,15 +1140,36 @@ const runPlannerPromptOnce = (tool, model, variant, prompt, cwd, options = {}) =
1121
1140
  spawnTimestamp = Date.now();
1122
1141
  }
1123
1142
  let child;
1143
+ let placementFailure = Promise.resolve(null);
1124
1144
  try {
1125
- child = spawn(cmd, args, {
1126
- stdio: ['ignore', 'pipe', 'pipe'],
1127
- env: cwd
1128
- ? createWorkspaceProcessEnvironment(cwd, { PWD: cwd })
1129
- : process.env,
1130
- detached: process.platform !== 'win32',
1131
- ...(cwd ? { cwd } : {}),
1132
- });
1145
+ if (options.executionProvider) {
1146
+ const handle = options.executionProvider.execute({
1147
+ phase: 'agent',
1148
+ target: options.executionTarget ?? null,
1149
+ command: cmd,
1150
+ args,
1151
+ stdio: ['ignore', 'pipe', 'pipe'],
1152
+ launcherEnvironment: options.executionLauncherEnvironment ?? createHostLauncherEnvironment(),
1153
+ runtimeEnvironment: options.executionRuntimeEnvironment ?? createLegacyHostRuntimeEnvironment(cwd ?? process.cwd(), cwd ? { PWD: cwd } : {}),
1154
+ detached: process.platform !== 'win32',
1155
+ cwd: cwd ?? process.cwd(),
1156
+ });
1157
+ child = handle.child;
1158
+ placementFailure = handle.placementFailure;
1159
+ void placementFailure.then(failure => {
1160
+ if (failure) {
1161
+ rejectOnce(failure);
1162
+ }
1163
+ });
1164
+ }
1165
+ else {
1166
+ child = spawn(cmd, args, {
1167
+ stdio: ['ignore', 'pipe', 'pipe'],
1168
+ env: cwd ? createWorkspaceProcessEnvironment(cwd, { PWD: cwd }) : process.env,
1169
+ detached: process.platform !== 'win32',
1170
+ ...(cwd ? { cwd } : {}),
1171
+ });
1172
+ }
1133
1173
  }
1134
1174
  catch (error) {
1135
1175
  const resolvedError = error instanceof Error ? error : new Error(String(error));
@@ -1144,9 +1184,11 @@ const runPlannerPromptOnce = (tool, model, variant, prompt, cwd, options = {}) =
1144
1184
  killPlannerProcess(child, 'SIGTERM');
1145
1185
  }
1146
1186
  }
1147
- if (sessionTracking && tool === 'claude' && sessionId) {
1148
- hasResolvedSessionId = true;
1149
- updatePlanningSessionId(sessionTracking.ticketId, sessionId, sessionTracking.io);
1187
+ if (sessionTracking?.resumeSessionId) {
1188
+ bindResolvedProviderSession(sessionTracking.resumeSessionId);
1189
+ }
1190
+ else if (sessionTracking && tool === 'claude' && sessionId) {
1191
+ bindResolvedProviderSession(sessionId);
1150
1192
  }
1151
1193
  if (sessionTracking && tool === 'opencode') {
1152
1194
  if (!sessionTracking.resumeSessionId) {
@@ -1166,8 +1208,11 @@ const runPlannerPromptOnce = (tool, model, variant, prompt, cwd, options = {}) =
1166
1208
  tracker: subagentTracker,
1167
1209
  discoverParentSession: false,
1168
1210
  onParentSessionId: discoveredSessionId => {
1169
- bindResolvedOpencodeSession(discoveredSessionId);
1211
+ bindResolvedProviderSession(discoveredSessionId);
1170
1212
  },
1213
+ taskSessionReader: opencodeSessionOperations
1214
+ ? createOpencodeTaskSessionReader(opencodeSessionOperations)
1215
+ : null,
1171
1216
  });
1172
1217
  opencodeSubagentMonitor.start();
1173
1218
  }
@@ -1214,7 +1259,7 @@ const runPlannerPromptOnce = (tool, model, variant, prompt, cwd, options = {}) =
1214
1259
  }
1215
1260
  subagentTracker?.handle(subagentEvents);
1216
1261
  if (providerSessionId) {
1217
- bindResolvedOpencodeSession(providerSessionId);
1262
+ bindResolvedProviderSession(providerSessionId);
1218
1263
  }
1219
1264
  if (isHiddenOpenTag.test(line)) {
1220
1265
  insideHiddenBlock = true;
@@ -1239,6 +1284,11 @@ const runPlannerPromptOnce = (tool, model, variant, prompt, cwd, options = {}) =
1239
1284
  activePlannerProcesses.delete(child);
1240
1285
  options.cancellationState?.researchChildren.delete(child);
1241
1286
  clearWatchdogTimer();
1287
+ const placementError = await placementFailure;
1288
+ if (placementError) {
1289
+ rejectOnce(placementError);
1290
+ return;
1291
+ }
1242
1292
  if (remainder.trim()) {
1243
1293
  if (tool === 'claude') {
1244
1294
  usageSnapshot = mergeUsageFromJsonLine(usageSnapshot, remainder);
@@ -1256,7 +1306,7 @@ const runPlannerPromptOnce = (tool, model, variant, prompt, cwd, options = {}) =
1256
1306
  }
1257
1307
  subagentTracker?.handle(subagentEvents);
1258
1308
  if (providerSessionId) {
1259
- bindResolvedOpencodeSession(providerSessionId);
1309
+ bindResolvedProviderSession(providerSessionId);
1260
1310
  }
1261
1311
  if (logFragment !== null) {
1262
1312
  logLines.push(logFragment);
@@ -1284,11 +1334,16 @@ const runPlannerPromptOnce = (tool, model, variant, prompt, cwd, options = {}) =
1284
1334
  sessionLookupTimer = null;
1285
1335
  }
1286
1336
  await opencodeSubagentMonitor?.refresh();
1287
- const hasPersistedCompletion = tool === 'opencode' && resolvedSessionId && spawnTimestamp
1288
- ? await opencodeSessionHasCompletedAssistantSince(resolvedSessionId, spawnTimestamp)
1337
+ const hasPersistedCompletion = tool === 'opencode' && resolvedSessionId && spawnTimestamp && opencodeSessionOperations
1338
+ ? await opencodeSessionOperations.hasCompletedAssistantSince(resolvedSessionId, spawnTimestamp)
1289
1339
  : false;
1290
1340
  const persistedTranscript = hasPersistedCompletion && resolvedSessionId
1291
- ? await readOpencodeSessionTranscript({ sessionId: resolvedSessionId, since: spawnTimestamp ?? undefined, cwd: cwd ?? process.cwd() })
1341
+ ? await readOpencodeSessionTranscript({
1342
+ sessionId: resolvedSessionId,
1343
+ since: spawnTimestamp ?? undefined,
1344
+ cwd: cwd ?? process.cwd(),
1345
+ ...(opencodeSessionOperations ? { sessionOperations: opencodeSessionOperations } : {}),
1346
+ })
1292
1347
  : null;
1293
1348
  const persistedResponse = persistedTranscript?.response?.trim() ?? '';
1294
1349
  const output = tool === 'opencode' && persistedResponse && (completionForced || hardTimeoutForced || !streamedOutput)
@@ -1370,7 +1425,10 @@ const runPlannerPromptOnce = (tool, model, variant, prompt, cwd, options = {}) =
1370
1425
  return;
1371
1426
  }
1372
1427
  watchdogReadInFlight = true;
1373
- const completed = await opencodeSessionHasCompletedAssistantSince(resolvedSessionId, spawnTimestamp)
1428
+ if (!opencodeSessionOperations) {
1429
+ return;
1430
+ }
1431
+ const completed = await opencodeSessionOperations.hasCompletedAssistantSince(resolvedSessionId, spawnTimestamp)
1374
1432
  .catch(() => false);
1375
1433
  watchdogReadInFlight = false;
1376
1434
  if (settled) {
@@ -1652,6 +1710,7 @@ export const prepareLinkedPlanningResearch = async (taskDescription, options = {
1652
1710
  agentRunParent: resolvePlannerAgentRunParent(options),
1653
1711
  executionContext: options.executionContext,
1654
1712
  cancellationState: options.cancellationState,
1713
+ ...plannerExecutionOptions(options),
1655
1714
  ...(selectionInvocation ? { mainRun: selectionInvocation } : {}),
1656
1715
  });
1657
1716
  decision = extractFirstJsonObject(output);
@@ -1674,24 +1733,145 @@ export const prepareLinkedPlanningResearch = async (taskDescription, options = {
1674
1733
  if (!linkedProject) {
1675
1734
  throw new Error(`Linked project ${request.project_id} is no longer available`);
1676
1735
  }
1677
- const invocation = createPlanningInvocation(options, `Linked Research: ${linkedProject.name}`, 'research');
1736
+ const parentContext = options.executionContext;
1737
+ if (!parentContext) {
1738
+ const legacyInvocation = createPlanningInvocation(options, `Linked Research: ${linkedProject.name}`, 'research');
1739
+ try {
1740
+ const { output } = await runPlannerPrompt(tool, options.model ?? null, options.variant ?? null, prependSkillPromptBlock(buildLinkedProjectResearchPrompt(project, linkedProject, request), options.skills ?? []), linkedProject.repo_path, {
1741
+ agentRunParent: resolvePlannerAgentRunParent(options),
1742
+ cancellationState: options.cancellationState,
1743
+ ...(legacyInvocation ? { mainRun: legacyInvocation } : {}),
1744
+ });
1745
+ const result = normalizeLinkedResearchResponse(extractFirstJsonObject(output), linkedProject, request);
1746
+ finalizePlanningInvocation(legacyInvocation, 'done', output);
1747
+ return result;
1748
+ }
1749
+ catch (error) {
1750
+ finalizePlanningInvocation(legacyInvocation, options.cancellationState?.stopRequested ? 'stopped' : 'error', error instanceof Error ? error.message : String(error));
1751
+ throw error;
1752
+ }
1753
+ }
1754
+ const childRun = createExecutionRun({
1755
+ projectId: linkedProject.id,
1756
+ kind: 'planning_agent',
1757
+ domainStatus: 'queued',
1758
+ sourceReference: { kind: 'project', id: linkedProject.id, label: linkedProject.name },
1759
+ initiatedBy: 'automation',
1760
+ metadata: {
1761
+ linked_research_origin_project_id: project.id,
1762
+ linked_research_origin_run_id: parentContext?.runId ?? null,
1763
+ linked_research_origin_correlation_id: parentContext?.correlationId ?? null,
1764
+ linked_research_question: request.question,
1765
+ retry_tool: tool,
1766
+ retry_model: options.model ?? null,
1767
+ retry_variant: options.variant ?? null,
1768
+ retry_skills: options.skills ?? [],
1769
+ ...createExecutionProfileSelectionMetadata(linkedProject.id),
1770
+ },
1771
+ eventType: 'linked_research_created',
1772
+ }, options.agentRunParent?.io);
1773
+ const childContext = {
1774
+ runId: childRun.id,
1775
+ correlationId: childRun.correlation_id,
1776
+ projectId: childRun.project_id,
1777
+ kind: childRun.kind,
1778
+ };
1779
+ if (parentContext) {
1780
+ addExecutionRunLink({
1781
+ projectId: parentContext.projectId,
1782
+ runId: parentContext.runId,
1783
+ link: { entityType: 'project', entityId: linkedProject.id, relationship: 'context', label: linkedProject.name },
1784
+ });
1785
+ }
1786
+ let childEnvironment = null;
1787
+ let invocation = null;
1678
1788
  try {
1679
- const { output } = await runPlannerPrompt(tool, options.model ?? null, options.variant ?? null, prependSkillPromptBlock(buildLinkedProjectResearchPrompt(project, linkedProject, request), options.skills ?? []), linkedProject.repo_path, {
1680
- agentRunParent: resolvePlannerAgentRunParent(options),
1681
- executionContext: options.executionContext,
1789
+ transitionExecutionRun({
1790
+ projectId: childContext.projectId,
1791
+ runId: childContext.runId,
1792
+ status: 'running',
1793
+ domainStatus: 'running',
1794
+ eventType: 'linked_research_started',
1795
+ });
1796
+ const resolvedTarget = await resolveProjectRootExecutionTarget(linkedProject);
1797
+ const executionTarget = {
1798
+ identity: resolvedTarget.identity,
1799
+ workspacePath: resolvedTarget.workspacePath,
1800
+ workspaceEvidence: resolvedTarget.workspaceEvidence,
1801
+ };
1802
+ childEnvironment = await resolveWorkloadExecutionEnvironment({
1803
+ project: linkedProject,
1804
+ executionRunId: childContext.runId,
1805
+ agent: tool,
1806
+ model: options.model ?? null,
1807
+ skills: options.skills ?? [],
1808
+ target: executionTarget,
1809
+ workload: 'plan',
1810
+ });
1811
+ const invocationId = createAgentRun({
1812
+ executionRunId: childContext.runId,
1813
+ parentType: 'planning',
1814
+ parentId: linkedProject.id,
1815
+ label: `Linked Research: ${linkedProject.name}`,
1816
+ role: 'research',
1817
+ });
1818
+ const invocationRun = getAgentRun(invocationId);
1819
+ if (!invocationRun) {
1820
+ throw new Error('Unable to create the linked research agent run');
1821
+ }
1822
+ invocation = {
1823
+ parentType: 'planning',
1824
+ parentId: linkedProject.id,
1825
+ id: invocationRun.id,
1826
+ runIndex: invocationRun.run_index,
1827
+ label: invocationRun.label,
1828
+ role: invocationRun.role,
1829
+ };
1830
+ const { output } = await runPlannerPrompt(tool, options.model ?? null, options.variant ?? null, prependSkillPromptBlock(buildLinkedProjectResearchPrompt(project, linkedProject, request), options.skills ?? []), resolvedTarget.workspacePath, {
1831
+ agentRunParent: options.agentRunParent
1832
+ ? { parentType: 'planning', parentId: linkedProject.id, io: options.agentRunParent.io }
1833
+ : undefined,
1834
+ executionContext: childContext,
1682
1835
  cancellationState: options.cancellationState,
1836
+ executionProvider: childEnvironment.provider,
1837
+ executionTarget,
1838
+ executionLauncherEnvironment: childEnvironment.launcherEnvironment,
1839
+ executionRuntimeEnvironment: childEnvironment.runtimeEnvironment,
1683
1840
  ...(invocation ? { mainRun: invocation } : {}),
1684
1841
  });
1685
1842
  const result = normalizeLinkedResearchResponse(extractFirstJsonObject(output), linkedProject, request);
1686
1843
  finalizePlanningInvocation(invocation, 'done', output);
1844
+ transitionExecutionRun({
1845
+ projectId: childContext.projectId,
1846
+ runId: childContext.runId,
1847
+ status: 'completed',
1848
+ domainStatus: 'done',
1849
+ eventType: 'linked_research_completed',
1850
+ });
1687
1851
  return result;
1688
1852
  }
1689
1853
  catch (error) {
1690
1854
  finalizePlanningInvocation(invocation, options.cancellationState?.stopRequested ? 'stopped' : 'error', error instanceof Error ? error.message : String(error));
1691
- throw error;
1855
+ transitionExecutionRun({
1856
+ projectId: childContext.projectId,
1857
+ runId: childContext.runId,
1858
+ status: options.cancellationState?.stopRequested ? 'cancelled' : 'failed',
1859
+ domainStatus: options.cancellationState?.stopRequested ? 'stopped' : 'error',
1860
+ ...(options.cancellationState?.stopRequested ? {} : {
1861
+ failure: { code: 'linked_research_failed', message: error instanceof Error ? error.message : String(error) },
1862
+ }),
1863
+ eventType: options.cancellationState?.stopRequested ? 'linked_research_cancelled' : 'linked_research_failed',
1864
+ });
1865
+ if (options.cancellationState?.stopRequested) {
1866
+ throw error;
1867
+ }
1868
+ return null;
1869
+ }
1870
+ finally {
1871
+ await childEnvironment?.release();
1692
1872
  }
1693
1873
  }));
1694
- return linkedResearch;
1874
+ return linkedResearch.filter((result) => result !== null);
1695
1875
  };
1696
1876
  export const generatePlanText = (title, description, options = {}) => {
1697
1877
  const tool = options.tool ?? config.tool ?? 'opencode';
@@ -1707,7 +1887,7 @@ export const generatePlanText = (title, description, options = {}) => {
1707
1887
  description,
1708
1888
  ...(invocation ? { invocation: { parentType: invocation.parentType, parentId: invocation.parentId, agentRunId: invocation.id } } : {}),
1709
1889
  });
1710
- const { output: plannerOutput, responseTiming: plannerTiming, usageSnapshot, transcriptRuns } = await runPlannerPrompt(tool, options.model ?? null, options.variant ?? null, prependSkillPromptBlock(buildSingleTicketPrompt(title, description, linkedResearchContext, projectMemoryBlock, Boolean(project?.memory_enabled), Boolean(options.ticketId)), options.skills ?? []), resolvePlannerCwd(project, options.branch), {
1890
+ const { output: plannerOutput, responseTiming: plannerTiming, usageSnapshot, transcriptRuns } = await runPlannerPrompt(tool, options.model ?? null, options.variant ?? null, prependSkillPromptBlock(buildSingleTicketPrompt(title, description, linkedResearchContext, projectMemoryBlock, Boolean(project?.memory_enabled), Boolean(options.ticketId)), options.skills ?? []), options.cwd ?? resolvePlannerCwd(project, options.branch), {
1711
1891
  onProgress: options.onProgress,
1712
1892
  ...(options.ticketId && options.io
1713
1893
  ? {
@@ -1725,6 +1905,7 @@ export const generatePlanText = (title, description, options = {}) => {
1725
1905
  : {}),
1726
1906
  executionContext: options.executionContext,
1727
1907
  cancellationState: options.cancellationState,
1908
+ ...plannerExecutionOptions(options),
1728
1909
  ...(invocation ? { mainRun: invocation } : {}),
1729
1910
  });
1730
1911
  try {
@@ -1788,9 +1969,10 @@ export const generatePlanningTicketOutline = async (description, options = {}) =
1788
1969
  description,
1789
1970
  ...(invocation ? { invocation: { parentType: invocation.parentType, parentId: invocation.parentId, agentRunId: invocation.id } } : {}),
1790
1971
  });
1791
- const { output } = await runPlannerPrompt(tool, options.model ?? null, options.variant ?? null, prependSkillPromptBlock(buildPlanningAgentOutlinePrompt(description, linkedResearchContext, projectMemoryBlock), options.skills ?? []), resolvePlannerCwd(project, options.branch), {
1972
+ const { output } = await runPlannerPrompt(tool, options.model ?? null, options.variant ?? null, prependSkillPromptBlock(buildPlanningAgentOutlinePrompt(description, linkedResearchContext, projectMemoryBlock), options.skills ?? []), options.cwd ?? resolvePlannerCwd(project, options.branch), {
1792
1973
  agentRunParent: resolvePlannerAgentRunParent(options),
1793
1974
  executionContext: options.executionContext,
1975
+ ...plannerExecutionOptions(options),
1794
1976
  ...(invocation ? { mainRun: invocation } : {}),
1795
1977
  });
1796
1978
  const result = extractFirstJsonObject(output);
@@ -1816,9 +1998,10 @@ export const expandPlanningTicketOutline = async (description, outline, options
1816
1998
  description,
1817
1999
  ...(invocation ? { invocation: { parentType: invocation.parentType, parentId: invocation.parentId, agentRunId: invocation.id } } : {}),
1818
2000
  });
1819
- const { output } = await runPlannerPrompt(tool, options.model ?? null, options.variant ?? null, prependSkillPromptBlock(buildPlanningAgentExpansionPrompt(description, outline, linkedResearchContext, projectMemoryBlock), options.skills ?? []), resolvePlannerCwd(project, options.branch), {
2001
+ const { output } = await runPlannerPrompt(tool, options.model ?? null, options.variant ?? null, prependSkillPromptBlock(buildPlanningAgentExpansionPrompt(description, outline, linkedResearchContext, projectMemoryBlock), options.skills ?? []), options.cwd ?? resolvePlannerCwd(project, options.branch), {
1820
2002
  agentRunParent: resolvePlannerAgentRunParent(options),
1821
2003
  executionContext: options.executionContext,
2004
+ ...plannerExecutionOptions(options),
1822
2005
  ...(invocation ? { mainRun: invocation } : {}),
1823
2006
  });
1824
2007
  const result = extractFirstJsonObject(output);
@@ -1880,6 +2063,8 @@ export const startPlan = (ticket, io, options = {}) => {
1880
2063
  };
1881
2064
  const targetBundleId = options.targetBundleId === undefined ? ticket.worktree_bundle_id : options.targetBundleId;
1882
2065
  const targetBranch = options.branch === undefined ? resolveTicketBranch(ticket) : options.branch;
2066
+ const runTool = resolveTicketTool(ticket, config.tool ?? 'opencode');
2067
+ const runSkills = options.skills ?? ticket.skills ?? [];
1883
2068
  const executionRun = createExecutionRun({
1884
2069
  projectId: project.id,
1885
2070
  kind: 'plan',
@@ -1895,6 +2080,11 @@ export const startPlan = (ticket, io, options = {}) => {
1895
2080
  ...(initiation.workflowContext ?? {}),
1896
2081
  plan_target_bundle_id: targetBundleId,
1897
2082
  plan_target_branch: targetBranch,
2083
+ retry_tool: runTool,
2084
+ retry_model: ticket.model,
2085
+ retry_variant: ticket.variant,
2086
+ retry_skills: runSkills,
2087
+ ...createExecutionProfileSelectionMetadata(project.id),
1898
2088
  },
1899
2089
  eventType: initiation.retrySourceRunId ? 'retry_started' : 'plan_created',
1900
2090
  }, io);
@@ -1911,6 +2101,7 @@ export const startPlan = (ticket, io, options = {}) => {
1911
2101
  queuedJob = enqueue(jobKey, async () => {
1912
2102
  let callbackStarted = false;
1913
2103
  let activityRegistered = false;
2104
+ let workloadEnvironment = null;
1914
2105
  try {
1915
2106
  registerBundleActivity(ticket, 'plan');
1916
2107
  activityRegistered = true;
@@ -1946,18 +2137,60 @@ export const startPlan = (ticket, io, options = {}) => {
1946
2137
  }
1947
2138
  const planningDescription = await resolveExternalPlanningDescription(project.id, ticket.description);
1948
2139
  const initiativePrompt = ticket.planning_context?.initiative_prompt?.trim() || planningDescription;
2140
+ const resolvedTarget = targetBranch
2141
+ ? await resolveExecutionTarget({
2142
+ project,
2143
+ descriptor: classifyTicketExecutionTarget({
2144
+ branch: targetBranch,
2145
+ bundleId: targetBundleId,
2146
+ bundleKind: currentTicket.bundle?.kind ?? null,
2147
+ }),
2148
+ })
2149
+ : await resolveProjectRootExecutionTarget(project);
2150
+ const executionTarget = {
2151
+ identity: resolvedTarget.identity,
2152
+ workspacePath: resolvedTarget.workspacePath,
2153
+ workspaceEvidence: resolvedTarget.workspaceEvidence,
2154
+ };
2155
+ workloadEnvironment = await resolveWorkloadExecutionEnvironment({
2156
+ project,
2157
+ executionRunId: executionContext.runId,
2158
+ agent: runTool,
2159
+ model: ticket.model,
2160
+ skills: runSkills,
2161
+ target: executionTarget,
2162
+ workload: 'plan',
2163
+ });
2164
+ const planningSessionId = options.providerContinuation === 'fresh' ? null : ticket.planning_session_id;
2165
+ if (planningSessionId) {
2166
+ assertProviderSessionAuthorityMatches({
2167
+ projectId: project.id,
2168
+ currentExecutionRunId: executionContext.runId,
2169
+ parentType: 'ticket',
2170
+ parentId: ticket.id,
2171
+ sessionId: planningSessionId,
2172
+ agent: runTool,
2173
+ modelProvider: runTool === 'claude' ? 'anthropic' : getModelProvider(ticket.model),
2174
+ authority: workloadEnvironment.runAuthority,
2175
+ });
2176
+ }
1949
2177
  const planned = await generatePlanText(ticket.title, planningDescription, {
1950
- tool: resolveTicketTool(ticket, config.tool ?? 'opencode'),
2178
+ tool: runTool,
1951
2179
  model: ticket.model,
1952
2180
  variant: ticket.variant,
1953
- skills: options.skills ?? ticket.skills ?? [],
2181
+ skills: runSkills,
1954
2182
  projectId: project.id,
1955
2183
  branch: targetBranch,
1956
- planningSessionId: options.providerContinuation === 'fresh' ? null : ticket.planning_session_id,
2184
+ cwd: resolvedTarget.workspacePath,
2185
+ planningSessionId,
1957
2186
  ticketId: ticket.id,
1958
2187
  io,
1959
2188
  executionContext,
1960
2189
  cancellationState,
2190
+ executionProvider: workloadEnvironment.provider,
2191
+ executionTarget,
2192
+ executionLauncherEnvironment: workloadEnvironment.launcherEnvironment,
2193
+ executionRuntimeEnvironment: workloadEnvironment.runtimeEnvironment,
1961
2194
  onProgress: progress => {
1962
2195
  updatePlanningProgress(ticket.id, progress.log, progress.response, io);
1963
2196
  },
@@ -1980,7 +2213,13 @@ export const startPlan = (ticket, io, options = {}) => {
1980
2213
  if (resolvedTool === 'opencode') {
1981
2214
  const freshTicket = getTicket(ticket.id);
1982
2215
  if (freshTicket?.planning_session_id) {
1983
- await storeTicketUsageSnapshot(freshTicket.id, freshTicket.planning_session_id);
2216
+ const sessionOperations = workloadEnvironment.provider?.sessionOperations?.opencode;
2217
+ if (sessionOperations) {
2218
+ await storeTicketUsageSnapshot(freshTicket.id, freshTicket.planning_session_id, sessionOperations);
2219
+ }
2220
+ else {
2221
+ await storeTicketUsageSnapshot(freshTicket.id, freshTicket.planning_session_id);
2222
+ }
1984
2223
  }
1985
2224
  }
1986
2225
  else if (resolvedTool === 'claude' && planned.usageSnapshot) {
@@ -2136,11 +2375,19 @@ export const startPlan = (ticket, io, options = {}) => {
2136
2375
  }, io);
2137
2376
  }
2138
2377
  finally {
2139
- if (activityRegistered) {
2140
- unregisterBundleActivity(ticket, 'plan');
2378
+ try {
2379
+ await workloadEnvironment?.release();
2380
+ }
2381
+ catch {
2382
+ // Cleanup failure must not replace the durable Plan outcome.
2141
2383
  }
2142
- if (activePlanProcesses.get(ticket.id) === cancellationState) {
2143
- activePlanProcesses.delete(ticket.id);
2384
+ finally {
2385
+ if (activityRegistered) {
2386
+ unregisterBundleActivity(ticket, 'plan');
2387
+ }
2388
+ if (activePlanProcesses.get(ticket.id) === cancellationState) {
2389
+ activePlanProcesses.delete(ticket.id);
2390
+ }
2144
2391
  }
2145
2392
  }
2146
2393
  });