@zq-silk/yui 0.15.7 → 0.15.8

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 (281) hide show
  1. package/ARCHITECTURE.md +192 -399
  2. package/README.md +127 -1149
  3. package/dist/agent/adapterCatalog.js +15 -2
  4. package/dist/agent/agent.js +23 -3
  5. package/dist/agent/argumentPolicy.js +7 -1
  6. package/dist/agent/connectionPlan.js +62 -0
  7. package/dist/agent/executionComponents.js +158 -0
  8. package/dist/agent/launchEnvironment.js +31 -3
  9. package/dist/agent/managedRuntimeEnvironment.js +3 -5
  10. package/dist/{turn/turn.js → agentRun/agentRun.js} +166 -109
  11. package/dist/{turn/turnIdentity.js → agentRun/runIdentity.js} +4 -4
  12. package/dist/brief/taskBrief.js +12 -0
  13. package/dist/cli/agentConfigurationPicker.js +13 -0
  14. package/dist/cli/commandCatalog.js +157 -70
  15. package/dist/cli/interactionCandidates.js +5 -5
  16. package/dist/cli/interactionPolicy.js +38 -8
  17. package/dist/cli/invocationRouter.js +1 -1
  18. package/dist/cli/managedDiagnostics.js +28 -0
  19. package/dist/cli/operatorWizard.js +1 -7
  20. package/dist/cli/roleOptionOrder.js +27 -0
  21. package/dist/cli/roleWizard.js +50 -14
  22. package/dist/cli/updateOrchestrator.js +1 -1
  23. package/dist/cli/updatePorts.js +3 -4
  24. package/dist/cli.js +178 -95
  25. package/dist/commands/agentCommands.js +72 -14
  26. package/dist/commands/capabilityCommands.js +9 -6
  27. package/dist/commands/configCommands.js +20 -20
  28. package/dist/commands/deliveryGuardPreflight.js +2 -2
  29. package/dist/commands/executionAuditCommands.js +24 -24
  30. package/dist/commands/globalRoleCommands.js +1 -1
  31. package/dist/commands/grantCommands.js +4 -4
  32. package/dist/commands/operatorCommands.js +1 -7
  33. package/dist/commands/projectCommands.js +4 -4
  34. package/dist/commands/resourcesCommands.js +2 -2
  35. package/dist/commands/roleConfiguration.js +25 -5
  36. package/dist/commands/roleRuntimeGuard.js +4 -5
  37. package/dist/commands/sessionCommands.js +3 -7
  38. package/dist/commands/taskActivationCommands.js +259 -0
  39. package/dist/commands/taskActor.js +28 -49
  40. package/dist/commands/taskCommands.js +1188 -710
  41. package/dist/commands/taskContextCommand.js +39 -583
  42. package/dist/commands/taskExecutionCommands.js +32 -32
  43. package/dist/commands/taskInputCommands.js +40 -104
  44. package/dist/commands/taskIntegrationCommands.js +3 -2
  45. package/dist/commands/taskIntegrationQueueCommands.js +1 -1
  46. package/dist/commands/taskNextActionCommand.js +8 -8
  47. package/dist/commands/taskOverviewCommand.js +33 -45
  48. package/dist/commands/taskRemoteDeliveryCommand.js +2 -2
  49. package/dist/commands/taskRoleRuntimeStatus.js +133 -102
  50. package/dist/commands/telemetryCommands.js +36 -38
  51. package/dist/config/configCatalog.js +4 -4
  52. package/dist/config/yuiConfig.js +8 -8
  53. package/dist/context/contextSnapshot.js +10 -10
  54. package/dist/context/dispatchContext.js +11 -11
  55. package/dist/context/roleSessionContext.js +6 -3
  56. package/dist/context/{turnContextPack.js → runContextPack.js} +146 -81
  57. package/dist/context/{turnInputContract.js → runInputContract.js} +73 -60
  58. package/dist/context/sessionBootstrapManifest.js +21 -2
  59. package/dist/context/sourceRunContext.js +30 -0
  60. package/dist/context/taskContext.js +458 -0
  61. package/dist/context/wakeNotification.js +27 -27
  62. package/dist/controller/agentRuntimeObserver.js +21 -24
  63. package/dist/controller/capabilityBridge.js +17 -6
  64. package/dist/controller/clientRuntime.js +65 -92
  65. package/dist/controller/controller.js +114 -188
  66. package/dist/controller/fileSchedulerStoreAdapter.js +787 -887
  67. package/dist/controller/jobControl.js +54 -85
  68. package/dist/controller/resourceInventory.js +8 -27
  69. package/dist/controller/resourceInventoryLinux.js +12 -13
  70. package/dist/controller/runtime.js +529 -479
  71. package/dist/controller/runtimeEventInbox.js +55 -25
  72. package/dist/controller/runtimeEventProcessor.js +22 -31
  73. package/dist/controller/{runtimeHookTurnFence.js → runtimeHookRunFence.js} +91 -115
  74. package/dist/controller/runtimeLaunchCoordinator.js +80 -426
  75. package/dist/controller/runtimeObservationHook.js +14 -18
  76. package/dist/controller/sessionNotify.js +16 -24
  77. package/dist/controller/sessionOwnerReconciliation.js +168 -50
  78. package/dist/controller/structuredProviderObservation.js +138 -99
  79. package/dist/coordination/workMailbox.js +3 -3
  80. package/dist/coordination/workMailboxQueue.js +36 -33
  81. package/dist/core/boundedRpc.js +8 -1
  82. package/dist/core/controllerClient.js +20 -1
  83. package/dist/core/controllerServer.js +4 -4
  84. package/dist/doctor/doctor.js +13 -2
  85. package/dist/domain/agentResultTransport.js +9 -9
  86. package/dist/execution/codexThreadNaming.js +2 -8
  87. package/dist/execution/executionHealth.js +51 -63
  88. package/dist/execution/reviewMainRun.js +137 -0
  89. package/dist/execution/workItemExecution.js +28 -29
  90. package/dist/execution/workItemExecutionProjection.js +99 -107
  91. package/dist/execution/workItemMainRun.js +141 -0
  92. package/dist/executor/agentAdapter.js +227 -20
  93. package/dist/executor/agentConfigurationCatalog.js +126 -4
  94. package/dist/executor/agentConfigurationProbe.js +162 -4
  95. package/dist/executor/agentExecutor.js +79 -78
  96. package/dist/executor/effectiveLaunch.js +105 -18
  97. package/dist/executor/executorRegistry.js +29 -44
  98. package/dist/executor/fileRoleLaunchPlanner.js +229 -154
  99. package/dist/executor/workspacePreflightClassification.js +16 -16
  100. package/dist/grant/capabilityGrant.js +6 -3
  101. package/dist/input/inputRequest.js +12 -10
  102. package/dist/integration/gitIntegrationService.js +4 -11
  103. package/dist/integration/integrationQueueService.js +4 -4
  104. package/dist/interaction/operatorPresentation.js +1 -1
  105. package/dist/kernel/builtinCapabilities.js +247 -12
  106. package/dist/kernel/capabilityRegistry.js +64 -18
  107. package/dist/kernel/instanceHost.js +12 -1
  108. package/dist/kernel/kernelPorts.js +2 -2
  109. package/dist/lifecycle/canonicalLifecycleEvent.js +44 -49
  110. package/dist/lifecycle/exactRunTerminalization.js +449 -0
  111. package/dist/message/message.js +62 -6
  112. package/dist/message/messageContinuation.js +204 -0
  113. package/dist/observability/executionAudit.js +70 -72
  114. package/dist/observability/faultClassification.js +2 -2
  115. package/dist/observability/orchestrationMetrics.js +8 -8
  116. package/dist/operator/operatorSessionHistory.js +1 -7
  117. package/dist/output/agentConfigurationPresentation.js +8 -3
  118. package/dist/output/agentRunConfigurationPresentation.js +128 -0
  119. package/dist/output/rolePresentation.js +54 -3
  120. package/dist/plugins/pluginChild.js +104 -0
  121. package/dist/plugins/pluginIntent.js +26 -0
  122. package/dist/plugins/pluginInterpreter.js +43 -0
  123. package/dist/plugins/pluginPackage.js +101 -0
  124. package/dist/plugins/pluginProcess.js +112 -0
  125. package/dist/plugins/pluginService.js +380 -0
  126. package/dist/profile/agentProfile.js +1 -1
  127. package/dist/repository/gitWorkspace.js +26 -4
  128. package/dist/repository/project.js +19 -4
  129. package/dist/repository/taskBaseFreshness.js +13 -13
  130. package/dist/repository/taskWorkspaceCoordinator.js +20 -27
  131. package/dist/repository/taskWorkspacePreparer.js +344 -83
  132. package/dist/resources/autoResourceGc.js +3 -3
  133. package/dist/resources/liveReferences.js +3 -3
  134. package/dist/resources/projectResource.js +123 -0
  135. package/dist/resources/projectResourceService.js +421 -0
  136. package/dist/resources/resourceDiscovery.js +6 -6
  137. package/dist/resources/resourceGc.js +1 -1
  138. package/dist/resources/resourceRegistrar.js +1 -1
  139. package/dist/resources/resourceTypes.js +1 -1
  140. package/dist/review/deltaRecheck.js +3 -3
  141. package/dist/review/reviewAcceptance.js +16 -16
  142. package/dist/review/reviewDecision.js +7 -7
  143. package/dist/review/reviewRound.js +21 -20
  144. package/dist/review/reviewerAvailability.js +2 -2
  145. package/dist/role/role.js +51 -7
  146. package/dist/role/taskRoleUpdate.js +30 -0
  147. package/dist/runtime/acpProtocol.js +425 -0
  148. package/dist/runtime/acpSession.js +731 -0
  149. package/dist/runtime/acpSessionConfiguration.js +260 -0
  150. package/dist/runtime/agentDriver.js +30 -11
  151. package/dist/runtime/agentEndpoint.js +278 -0
  152. package/dist/runtime/agentEndpointIdentity.js +86 -0
  153. package/dist/runtime/agentEndpointOwnership.js +239 -0
  154. package/dist/runtime/agentError.js +2 -10
  155. package/dist/runtime/agentHost.js +565 -314
  156. package/dist/runtime/agentRunConfiguration.js +258 -0
  157. package/dist/runtime/builtinAgentDrivers.js +134 -18
  158. package/dist/runtime/builtinAgentErrorMappers.js +55 -3
  159. package/dist/runtime/builtinTranscriptUsage.js +1 -1
  160. package/dist/runtime/claude-process-owner +0 -0
  161. package/dist/runtime/codexAppServerRuntime.js +38 -30
  162. package/dist/runtime/codexInteractiveHost.js +41 -6
  163. package/dist/runtime/continuationManager.js +2 -6
  164. package/dist/runtime/executionEnvironment.js +30 -0
  165. package/dist/runtime/firstProgressAdvisory.js +11 -11
  166. package/dist/runtime/index.js +4 -3
  167. package/dist/runtime/jsonLineChannel.js +109 -0
  168. package/dist/runtime/launchBroker.js +91 -16
  169. package/dist/runtime/launchDiagnostics.js +2 -2
  170. package/dist/runtime/lifecycleReservation.js +10 -18
  171. package/dist/runtime/managedCaller.js +61 -17
  172. package/dist/runtime/nativeSessionControl.js +102 -0
  173. package/dist/runtime/ports.js +6 -21
  174. package/dist/runtime/processExitObservation.js +8 -7
  175. package/dist/runtime/promptEnvelope.js +17 -6
  176. package/dist/runtime/providerContinuation.js +3 -9
  177. package/dist/runtime/providerContinuationReconciliationService.js +4 -13
  178. package/dist/runtime/providerControl.js +2 -7
  179. package/dist/runtime/providerRuntimeIdentity.js +110 -222
  180. package/dist/runtime/providerRuntimeReconciler.js +5 -9
  181. package/dist/runtime/runtimeBinding.js +0 -1
  182. package/dist/runtime/runtimeContinuationProjection.js +4 -7
  183. package/dist/runtime/runtimeDeadlines.js +9 -0
  184. package/dist/runtime/runtimeHealthPolicy.js +1 -1
  185. package/dist/runtime/runtimeObservation.js +29 -65
  186. package/dist/runtime/runtimeProjection.js +43 -51
  187. package/dist/runtime/runtimeSessionCandidate.js +1 -3
  188. package/dist/runtime/sessionLaunchRequest.js +3 -7
  189. package/dist/runtime/sessionOwnerIdentity.js +7 -54
  190. package/dist/runtime/sessionOwnerRegistry.js +22 -17
  191. package/dist/runtime/sessionReconciliation.js +4 -8
  192. package/dist/runtime/sessionTerminationGuard.js +70 -259
  193. package/dist/runtime/sessionTokenMetrics.js +5 -16
  194. package/dist/runtime/structuredProviderHost.js +237 -117
  195. package/dist/runtime/taskRuntimeIsolation.js +39 -122
  196. package/dist/runtime/tmuxAdapters.js +39 -86
  197. package/dist/scheduler/activeRoleRunDelivery.js +354 -0
  198. package/dist/scheduler/leaderWakeupProcessor.js +75 -266
  199. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  200. package/dist/scheduler/ports.js +80 -9
  201. package/dist/scheduler/{roleTurnLiveness.js → roleRunLiveness.js} +26 -30
  202. package/dist/scheduler/{roleTurnStall.js → roleRunStall.js} +128 -139
  203. package/dist/scheduler/taskExecutionProjection.js +120 -124
  204. package/dist/scheduler/taskObservabilityProjection.js +29 -29
  205. package/dist/scheduler/taskWake.js +11 -4
  206. package/dist/scheduler/wakeReason.js +9 -1
  207. package/dist/setup/setupCommand.js +1 -0
  208. package/dist/storage/migrations/agentRunContract.js +159 -0
  209. package/dist/storage/migrations/removeRuntimeGeneration.js +207 -0
  210. package/dist/storage/sqliteSchema.js +431 -5
  211. package/dist/storage/sqliteStore.js +375 -220
  212. package/dist/storage/storageVersions.js +1 -1
  213. package/dist/storage/storeRpc.js +10 -5
  214. package/dist/storage/taskStore.js +13 -11
  215. package/dist/storage/upgrade/upgradeOrchestrator.js +5 -7
  216. package/dist/surface/surfaceContributions.js +102 -0
  217. package/dist/task/completionReadiness.js +32 -6
  218. package/dist/task/deliveryGuard.js +16 -16
  219. package/dist/task/draftPlan.js +72 -12
  220. package/dist/task/nextAction.js +144 -128
  221. package/dist/task/remoteDelivery.js +6 -6
  222. package/dist/task/task.js +184 -18
  223. package/dist/task/taskActivation.js +301 -0
  224. package/dist/task/taskActivationService.js +392 -0
  225. package/dist/task/taskRecordReference.js +5 -4
  226. package/dist/task/taskRecordRetirement.js +1 -1
  227. package/dist/telemetry/sqliteTelemetryStore.js +55 -68
  228. package/dist/telemetry/telemetryConfig.js +14 -14
  229. package/dist/telemetry/telemetryWiring.js +2 -2
  230. package/dist/web/assets/assetManifest.js +2 -0
  231. package/dist/web/assets/client/app.js +120 -20
  232. package/dist/web/assets/client/components.js +87 -54
  233. package/dist/web/assets/client/i18n.js +83 -41
  234. package/dist/web/assets/client/markdown.js +1 -1
  235. package/dist/web/assets/client/taskSurface.js +353 -0
  236. package/dist/web/assets/client/view.js +49 -44
  237. package/dist/web/assets/shell.js +1 -1
  238. package/dist/web/assets/styles/cards.js +22 -4
  239. package/dist/web/controllerWeb.js +60 -0
  240. package/dist/web/webMutation.js +28 -0
  241. package/dist/web/webServer.js +118 -8
  242. package/dist/web/webSnapshot.js +81 -74
  243. package/dist/web/webTaskSurface.js +64 -0
  244. package/dist/workItem/dependencyGate.js +1 -1
  245. package/dist/workItem/workItem.js +80 -48
  246. package/dist/workspace/workItemChangeSetManager.js +16 -9
  247. package/docs/agent-result-consumption.md +94 -0
  248. package/docs/agent-runtime-drivers.md +91 -0
  249. package/docs/architecture/README.md +38 -0
  250. package/docs/architecture/capabilities-and-resources.md +79 -0
  251. package/docs/managed-turn-and-session-runtime.md +222 -0
  252. package/docs/observability/README.md +81 -0
  253. package/docs/plugin-sdk.md +290 -0
  254. package/docs/provider-runtime.md +163 -0
  255. package/docs/release-workflow.md +303 -0
  256. package/docs/roles-and-configuration.md +113 -0
  257. package/docs/sqlite-control-plane-design.md +76 -0
  258. package/docs/task-dag-semantics.md +57 -0
  259. package/docs/task-delivery.md +103 -0
  260. package/docs/task-local-identity.md +6 -6
  261. package/docs/testing/verification-levels.md +86 -0
  262. package/i18n/README.zh-CN.md +98 -739
  263. package/package.json +2 -2
  264. package/skills/yui-leader/SKILL.md +130 -103
  265. package/skills/yui-leader/references/integration.md +39 -0
  266. package/skills/yui-leader/references/replicated-execution.md +42 -0
  267. package/skills/yui-leader/references/task-plugins.md +33 -0
  268. package/skills/yui-operator/SKILL.md +30 -59
  269. package/skills/yui-reviewer/SKILL.md +35 -36
  270. package/skills/yui-runtime/SKILL.md +88 -24
  271. package/skills/yui-runtime/references/publication.md +22 -0
  272. package/skills/yui-runtime/references/recovery.md +64 -0
  273. package/skills/yui-worker/SKILL.md +37 -39
  274. package/dist/cli/roleOptionCatalog.js +0 -68
  275. package/dist/context/sourceTurnContext.js +0 -30
  276. package/dist/execution/reviewMainTurn.js +0 -161
  277. package/dist/execution/workItemMainTurn.js +0 -164
  278. package/dist/lifecycle/exactTurnTerminalization.js +0 -407
  279. package/dist/runtime/preallocatedNativeSession.js +0 -13
  280. package/dist/runtime/runtimeStopReceipt.js +0 -42
  281. package/dist/scheduler/activeRoleTurnDelivery.js +0 -315
@@ -1,9 +1,12 @@
1
- import { createHash, randomBytes, randomUUID } from "node:crypto";
1
+ import { randomUUID } from "node:crypto";
2
2
  import { spawnSync } from "node:child_process";
3
- import { realpathSync } from "node:fs";
3
+ import { mkdirSync, realpathSync } from "node:fs";
4
4
  import { join, resolve } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { isDeepStrictEqual } from "node:util";
7
+ import { assertExecutionEnvironmentCurrent } from "../runtime/executionEnvironment.js";
8
+ import { runPurposeAdmitsTaskState } from "../agentRun/agentRun.js";
9
+ import { taskOwnsManagedWorkspace } from "../task/task.js";
7
10
  import { configuredAgentToDefinition, resolveAgentEnvironment } from "../agent/agent.js";
8
11
  import { NATIVE_AGENT_ENVIRONMENT_NAMES, nativeAgentEnvironmentNames, operationalAgentEnvironment, selectEnvironment } from "../agent/launchEnvironment.js";
9
12
  import { activeRoleAgentBinding } from "../role/role.js";
@@ -12,15 +15,15 @@ import { compileRoleSessionContext, roleSessionKind } from "../context/roleSessi
12
15
  import { materializeSessionBootstrap } from "../context/sessionBootstrapManifest.js";
13
16
  import { resolveAgentAdapter } from "./agentAdapter.js";
14
17
  import { resolveTaskRoleSessionTitle } from "../runtime/sessionTitle.js";
15
- import { nativeSessionIdForLaunch } from "../runtime/preallocatedNativeSession.js";
16
18
  import { classifyWorkspacePreflight, formatWorkspacePreflightError } from "./workspacePreflightClassification.js";
17
- import { activeLiveRoleAgentSession } from "./agentExecutor.js";
19
+ import { activeLiveRoleAgentSession, taskRoleControlTarget } from "./agentExecutor.js";
18
20
  import { roleSessionMayContinue, effectiveRoleForLaunch, resolveEffectiveLaunch } from "./effectiveLaunch.js";
19
21
  import { parseTaskRuntimeIsolationDescriptor, taskRuntimeIsolationEnvironment } from "../runtime/taskRuntimeIsolation.js";
20
22
  import { ResourceRegistrar } from "../resources/resourceRegistrar.js";
21
23
  import { builtinAgentDriverRegistry, builtinDriverIdForAdapter } from "../runtime/builtinAgentDrivers.js";
22
24
  import { managedRuntimeAdmission } from "../runtime/agentDriver.js";
23
- import { assertProviderConversationReplaceable, currentProviderActivation } from "../runtime/providerRuntimeIdentity.js";
25
+ import { builtinAgentEndpointImplementation, validateAgentEndpointImplementation } from "../runtime/agentEndpointIdentity.js";
26
+ import { assertProviderConversationReplaceable } from "../runtime/providerRuntimeIdentity.js";
24
27
  import { assertCodexLaunchOverridesAvailable, inspectCodexLaunchConfig } from "./codexConfigConflict.js";
25
28
  /** Builds managed native Agent launches from the authoritative Task records. */
26
29
  export class FileRoleLaunchPlanner {
@@ -64,42 +67,83 @@ export class FileRoleLaunchPlanner {
64
67
  this.#agentEnvironment = patchEnvironment(this.#agentEnvironment, refresh.sourceNames, refresh.sources);
65
68
  this.#nativeAgentEnvironment = patchEnvironment(this.#nativeAgentEnvironment, refresh.nativeNames, refresh.nativeSources);
66
69
  }
67
- /**
68
- * Commit the caller key for a task Session only after the runtime host has
69
- * confirmed that it created a new native process. An ensure/resume request
70
- * that reuses a live host must keep the old durable hash because its process
71
- * still carries the old plaintext key.
72
- */
73
- commitTaskCallerKey(input) {
74
- const hash = createHash("sha256").update(input.callerKey).digest("hex");
75
- this.store.setJobCallerKeyHash(input.taskId, input.roleName, input.agentId, hash);
70
+ /** Recovery addresses the recorded Agent/Session, independent of delivery
71
+ * admission, a missing worktree, or the old Endpoint code generation. */
72
+ planNativeControl(taskId, roleName) {
73
+ const set = this.store.getTaskRoleSessionSet(taskId, roleName);
74
+ const session = taskRoleControlTarget(set);
75
+ if (session?.adapterId !== "codex")
76
+ throw new Error("Native metadata control requires a recorded Codex Session.");
77
+ const configured = this.store.getConfiguredAgent(session.agentId);
78
+ if (configured === null || configured.adapterId !== session.adapterId) {
79
+ throw new Error("The recorded native Agent connection is unavailable.");
80
+ }
81
+ const agent = configuredAgentToDefinition(configured);
82
+ const connection = this.store.listEvents(taskId).find(event => event.type === "runtime.native-connection-bound"
83
+ && event.payload.roleName === roleName && event.payload.agentId === session.agentId
84
+ && event.payload.nativeSessionId === session.nativeSessionId)?.payload;
85
+ return {
86
+ command: configured.command, args: [...agent.baseArgs, "app-server", "proxy"], cwd: this.home,
87
+ expectedAccountHome: connection?.nativeAccountHome,
88
+ environment: {
89
+ ...operationalAgentEnvironment("codex", { ...this.#operationalEnvironment, ...this.#nativeAgentEnvironment }),
90
+ ...resolveAgentEnvironment(agent, this.#agentEnvironment),
91
+ ...(connection === undefined ? {} : { HOME: connection.home, CODEX_HOME: connection.codexHome })
92
+ }
93
+ };
76
94
  }
77
95
  plan(input) {
78
96
  const task = this.store.getTask(input.taskId);
79
97
  if (task === null)
80
98
  throw new Error(`Task not found: ${input.taskId}.`);
81
- if (task.status !== "active" || task.executionGate.state !== "enabled") {
82
- throw new Error(`Task execution is not enabled: ${input.taskId}.`);
83
- }
84
99
  const role = this.store.getRole(input.taskId, input.roleName);
85
100
  if (role === null)
86
101
  throw new Error(`Role not found: ${input.taskId}/${input.roleName}.`);
87
- const activeTurn = this.store.getActiveTurn(task.id, role.name);
88
- if (input.turnId !== undefined && activeTurn?.id !== input.turnId) {
89
- throw new Error(`Role Turn is no longer current: ${input.turnId}.`);
102
+ const activeRun = this.store.getActiveRun(task.id, role.name);
103
+ if (input.runId !== undefined && activeRun?.id !== input.runId) {
104
+ throw new Error(`Role AgentRun is no longer current: ${input.runId}.`);
90
105
  }
91
- const runWorkspace = activeTurn?.workspace;
106
+ const purpose = activeRun?.purpose
107
+ ?? (task.status === "draft" && role.name === "leader" ? "planning" : "execution");
108
+ if (!runPurposeAdmitsTaskState(purpose, task)) {
109
+ throw new Error(`Task execution is not enabled: ${input.taskId}.`);
110
+ }
111
+ // A launch owns no managed workspace in two cases: a Draft planning
112
+ // conversation, and a Task activated with an empty environment plan. Both
113
+ // run with no Project entries, so there is nothing to preflight and no
114
+ // worktree to wait for. Every other fence — live Session, replaceable
115
+ // Conversation, configured Agent, Context protocol identity, adopted
116
+ // execution environment — still applies.
117
+ const planningDraft = purpose === "planning" && task.status === "draft";
118
+ // A planning cwd is a disposable per-Task runtime resource, not a delivery
119
+ // workspace. Materialize only the exact directory selected at creation.
120
+ if (planningDraft && resolve(role.workspace) === resolve(`${this.home}.task-runtimes`, "planning", task.id)) {
121
+ mkdirSync(role.workspace, { recursive: true, mode: 0o700 });
122
+ }
123
+ const workspaceFree = planningDraft || !taskOwnsManagedWorkspace(task);
124
+ // An empty resource plan is a legal Task shape, but it does not make a
125
+ // shared directory a legal cwd. Once such a Task is active it can name the
126
+ // directory it means through the existing environment plan, so require that
127
+ // instead of silently running in whatever workspace the Role inherited with
128
+ // isolation skipped.
129
+ if (workspaceFree && !planningDraft && role.executionEnvironment === undefined) {
130
+ throw new Error(`Task ${task.id} owns no workspace and no adopted execution environment, so Role `
131
+ + `${role.name} has no directory of its own to run in. Request activation with a `
132
+ + "`scratch` or `local` environment plan, or bind an adopted environment with "
133
+ + "`environment.bind`, instead of inheriting a shared workspace.");
134
+ }
135
+ const runWorkspace = activeRun?.workspace;
92
136
  const main = this.store.getTaskWorkspace(task.id);
93
137
  // Quick Win (EXE-04/EXE-08): classify workspace preflight failures so
94
138
  // split-brain state is never reported as a transient Provider failure.
95
- const preflight = classifyWorkspacePreflight(this.store, task, input.roleName, activeTurn === null ? null : { id: activeTurn.id, workspace: activeTurn.workspace }, this.#inspectWorkspacePhysicalState);
139
+ const preflight = workspaceFree ? null : classifyWorkspacePreflight(this.store, task, input.roleName, activeRun === null ? null : { id: activeRun.id, workspace: activeRun.workspace }, this.#inspectWorkspacePhysicalState);
96
140
  if (preflight !== null) {
97
141
  throw new Error(formatWorkspacePreflightError(preflight));
98
142
  }
99
143
  const assignedWorkItem = this.store.listWorkItems(task.id).find((item) => item.assignee === role.name
100
- && !["completed", "failed", "retired"].includes(item.status));
101
- // A Turn snapshot is authoritative for the live launch. In particular,
102
- // a Reviewer Turn must launch from its ReviewRound-owned workspace rather
144
+ && !["accepted", "retired"].includes(item.status));
145
+ // A AgentRun snapshot is authoritative for the live launch. In particular,
146
+ // a Reviewer AgentRun must launch from its ReviewRound-owned workspace rather
103
147
  // than falling back to the WorkItem Develop workspace. Without an
104
148
  // active snapshot, resolve the normal Role/WorkItem assignment.
105
149
  const workspace = runWorkspace !== undefined
@@ -109,7 +153,12 @@ export class FileRoleLaunchPlanner {
109
153
  : assignedWorkItem === undefined
110
154
  ? main
111
155
  : this.store.getWorkItemWorkspace(task.id, assignedWorkItem.id);
112
- if (task.projectBindings.length === 0) {
156
+ if (workspaceFree) {
157
+ if (workspace !== null && !isDeepStrictEqual(workspace, main)) {
158
+ throw new Error(`Role workspace is not ready: ${input.taskId}/${input.roleName}.`);
159
+ }
160
+ }
161
+ else if (task.projectBindings.length === 0) {
113
162
  if (workspace === null || !isDeepStrictEqual(workspace, main)) {
114
163
  throw new Error(`Role workspace is not ready: ${input.taskId}/${input.roleName}.`);
115
164
  }
@@ -126,9 +175,9 @@ export class FileRoleLaunchPlanner {
126
175
  && workspace.owner.type === "work-item"
127
176
  && isolatedWorkItem !== null
128
177
  && (isolatedWorkItem.assignee === undefined || isolatedWorkItem.assignee === role.name)
129
- && !["completed", "failed", "retired"].includes(isolatedWorkItem.status)
130
- && (activeTurn === null
131
- || activeTurn.workItemId === workspace.owner.workItemId)
178
+ && !["accepted", "retired"].includes(isolatedWorkItem.status)
179
+ && (activeRun === null
180
+ || activeRun.workItemId === workspace.owner.workItemId)
132
181
  && sameWorkspaceProjects(workspace, task.projectBindings.map(({ projectId }) => projectId))
133
182
  && sameWritableProjects(workspace, isolatedWorkItem.writeProjectIds);
134
183
  const runScoped = runWorkspace !== undefined
@@ -136,29 +185,29 @@ export class FileRoleLaunchPlanner {
136
185
  && sameWorkspaceProjects(runWorkspace, task.projectBindings.map(({ projectId }) => projectId))
137
186
  && (runWorkspace.owner.type === "task"
138
187
  || (runWorkspace.owner.type === "work-item"
139
- && runWorkspace.owner.workItemId === activeTurn?.workItemId)
188
+ && runWorkspace.owner.workItemId === activeRun?.workItemId)
140
189
  || (runWorkspace.owner.type === "review-round"
141
- && activeTurn?.purpose === "review"
142
- && runWorkspace.owner.reviewRoundId === activeTurn.reviewRoundId)
190
+ && activeRun?.purpose === "review"
191
+ && runWorkspace.owner.reviewRoundId === activeRun.reviewRoundId)
143
192
  || (runWorkspace.owner.type === "execution-lane"
144
- && runWorkspace.owner.executionGroupId === activeTurn?.executionGroupId
145
- && runWorkspace.owner.executionLaneId === activeTurn?.executionLaneId
146
- && ((runWorkspace.owner.purpose === "review" && activeTurn?.purpose === "review")
147
- || (runWorkspace.owner.purpose === "execution" && activeTurn?.purpose === "execution"))));
193
+ && runWorkspace.owner.executionGroupId === activeRun?.executionGroupId
194
+ && runWorkspace.owner.executionLaneId === activeRun?.executionLaneId
195
+ && ((runWorkspace.owner.purpose === "review" && activeRun?.purpose === "review")
196
+ || (runWorkspace.owner.purpose === "execution" && activeRun?.purpose === "execution"))));
148
197
  if (!runScoped && !sharedMain && !isolated) {
149
198
  throw new Error(`Role workspace is not ready: ${input.taskId}/${input.roleName}.`);
150
199
  }
151
200
  }
152
201
  const sessionSet = this.store.getTaskRoleSessionSet(task.id, role.name);
153
- const resolvedEffective = activeTurn?.effective
202
+ const resolvedEffective = activeRun?.effective
154
203
  ?? activeLiveRoleAgentSession(sessionSet)?.effective
155
- ?? resolveTaskRoleEffectiveLaunch(this.store, role);
204
+ ?? resolveTaskRoleEffectiveLaunch(this.store, role, planningDraft ? "planning" : "execution");
156
205
  if (input.effective !== undefined
157
206
  && !isDeepStrictEqual(resolvedEffective, input.effective)) {
158
- throw new Error(`Role launch effective Turn snapshot changed: ${input.taskId}/${input.roleName}.`);
207
+ throw new Error(`Role launch effective AgentRun snapshot changed: ${input.taskId}/${input.roleName}.`);
159
208
  }
160
- if (activeTurn !== null && input.effective === undefined) {
161
- throw new Error(`Role launch is missing the effective Turn snapshot: ${input.taskId}/${input.roleName}.`);
209
+ if (activeRun !== null && input.effective === undefined) {
210
+ throw new Error(`Role launch is missing the effective AgentRun snapshot: ${input.taskId}/${input.roleName}.`);
162
211
  }
163
212
  const effective = input.effective ?? resolvedEffective;
164
213
  const existing = sessionSet?.sessions[effective.agentId];
@@ -179,7 +228,7 @@ export class FileRoleLaunchPlanner {
179
228
  assertProviderConversationReplaceable(sessionSet.providerBinding);
180
229
  }
181
230
  return this.#compile(role, input, { scope: "task", taskId: task.id }, resolveTaskRoleSessionTitle(input.mode === "resume" ? existing?.title : undefined, task, role.name), input.mode === "resume" && compatibleExisting ? existing.nativeSessionId : undefined, runWorkspace, effective, {
182
- purpose: activeTurn?.purpose ?? "execution"
231
+ purpose
183
232
  });
184
233
  }
185
234
  planGlobalRole(input) {
@@ -206,14 +255,40 @@ export class FileRoleLaunchPlanner {
206
255
  const launchRole = effectiveRoleForLaunch(role, effective);
207
256
  const binding = activeRoleAgentBinding(launchRole);
208
257
  if (binding.agentId !== input.agentId || binding.adapterId !== input.adapterId) {
209
- throw new Error(`Role runtime generation identity changed: ${role.name}.`);
210
- }
258
+ throw new Error(`Role runtime identity changed: ${role.name}.`);
259
+ }
260
+ const existingSession = owner.scope === "task"
261
+ ? this.store.getTaskRoleSessionSet(owner.taskId, role.name)?.sessions[input.agentId]
262
+ : this.store.getGlobalRoleSessionSet(role.name)?.sessions[input.agentId];
263
+ // A resume carries the Session's recorded generation forward unchanged. This
264
+ // planner runs in the Controller, which after an upgrade is already the new
265
+ // code, so it cannot speak for the still-running Agent Host that owns the
266
+ // Session: rejecting here would end live Sessions on Controller upgrade,
267
+ // while rewriting to current code would silently move a Session onto code it
268
+ // never started on. The Host that actually executes the implementation makes
269
+ // the decision — a reused live Host accepts its own generation, and a newly
270
+ // started Host running different code fails closed
271
+ // (`agentEndpointOwnership.pin` -> `requireBuiltinAgentEndpointImplementation`).
272
+ const endpointImplementation = input.mode === "resume"
273
+ ? validateAgentEndpointImplementation(existingSession.endpointImplementation)
274
+ : builtinAgentEndpointImplementation(binding.adapterId);
211
275
  const configured = this.store.getConfiguredAgent(input.agentId);
212
276
  if (configured === null)
213
277
  throw new Error(`Configured Agent not found: ${input.agentId}.`);
214
278
  if (configured.adapterId !== binding.adapterId) {
215
279
  throw new Error(`Configured Agent adapter changed: ${input.agentId}.`);
216
280
  }
281
+ // The command below comes from the Agent record while the product identity
282
+ // stamped onto this Session comes from the pinned snapshot. Checking only
283
+ // the adapter lets those disagree whenever two products share a plan, which
284
+ // is exactly the ACP case: the launch would run the newly configured
285
+ // executable and label it with the product the Session was pinned to. The
286
+ // update path refuses this change on a referenced Agent, so reaching here
287
+ // means the record was altered some other way — still not something to
288
+ // launch through.
289
+ if (configured.component !== binding.component) {
290
+ throw new Error(`Configured Agent execution component changed: ${input.agentId}.`);
291
+ }
217
292
  const agent = configuredAgentToDefinition(configured);
218
293
  const agentSourceEnvironment = input.environment ?? this.#agentEnvironment;
219
294
  const operationalSourceEnvironment = input.environment ?? {
@@ -228,8 +303,20 @@ export class FileRoleLaunchPlanner {
228
303
  const launchEnvironment = { ...inheritedLaunchEnvironment };
229
304
  const adapter = resolveAgentAdapter(binding.adapterId);
230
305
  const effectiveWorkspace = effective.workspace.root;
231
- const agentWorkspace = nativeAgentWorkspace(effective.workspace);
232
- if (adapter.id === "codex" && (owner.scope !== "task" || input.turnId === undefined)) {
306
+ if (effective.executionEnvironment !== undefined) {
307
+ if (owner.scope !== "task")
308
+ throw new Error("Global Roles cannot adopt a Task execution environment.");
309
+ assertExecutionEnvironmentCurrent(this.store, owner.taskId, effective.executionEnvironment);
310
+ if ((binding.config.advanced?.rawArgs?.length ?? 0) > 0) {
311
+ throw new Error("Adopted environments do not accept raw Agent arguments; use structured configuration.");
312
+ }
313
+ if ((binding.config.additionalDirectories?.length ?? 0) > 0) {
314
+ throw new Error("Adopted environments cannot implicitly include additional directories.");
315
+ }
316
+ }
317
+ const agentWorkspace = effective.executionEnvironment?.directory.path
318
+ ?? nativeAgentWorkspace(effective.workspace);
319
+ if (adapter.id === "codex" && (owner.scope !== "task" || input.runId === undefined)) {
233
320
  const codexConfig = inspectCodexLaunchConfig({
234
321
  environment: launchEnvironment,
235
322
  workspace: agentWorkspace,
@@ -245,10 +332,7 @@ export class FileRoleLaunchPlanner {
245
332
  const runtimeIsolation = input.runtimeIsolation === undefined
246
333
  ? undefined
247
334
  : parseTaskRuntimeIsolationDescriptor(JSON.stringify(input.runtimeIsolation));
248
- if (runtimeIsolation !== undefined && (owner.scope !== "task"
249
- || runtimeIsolation.taskId !== owner.taskId
250
- || runtimeIsolation.workspace.root !== effectiveWorkspace
251
- || runtimeIsolation.generation.runtimeGenerationId !== input.runtimeGenerationId)) {
335
+ if (runtimeIsolation !== undefined && (owner.scope !== "task" || runtimeIsolation.taskId !== owner.taskId || runtimeIsolation.workspace.root !== effectiveWorkspace)) {
252
336
  throw new Error("Role launch does not match its Task runtime isolation descriptor.");
253
337
  }
254
338
  Object.assign(launchEnvironment, runtimeIsolation === undefined
@@ -276,23 +360,25 @@ export class FileRoleLaunchPlanner {
276
360
  sessionManifestDigest: bootstrap.manifest.digest,
277
361
  sessionCliPath: bootstrap.sessionCliPath
278
362
  };
279
- const managedRun = owner.scope === "task" && input.turnId !== undefined
280
- ? this.store.getTurn(owner.taskId, input.turnId)
363
+ const managedRun = owner.scope === "task" && input.runId !== undefined
364
+ ? this.store.getRun(owner.taskId, input.runId)
281
365
  : null;
282
366
  const driver = builtinAgentDriverRegistry().require(builtinDriverIdForAdapter(configured.adapterId));
283
- if (owner.scope === "task" && input.turnId !== undefined) {
367
+ if (owner.scope === "task" && input.runId !== undefined) {
284
368
  const admission = managedRuntimeAdmission(driver.capabilities);
285
369
  if (!admission.admitted) {
286
- throw new Error(`Agent Driver ${driver.id} cannot host managed Turns; missing capabilities: `
370
+ throw new Error(`Agent Driver ${driver.id} cannot host managed AgentRuns; missing capabilities: `
287
371
  + admission.missing.join(", "));
288
372
  }
289
373
  }
290
374
  const roleConfig = binding.config.adapterId === "claude"
291
375
  && owner.scope === "task"
292
- && input.turnId !== undefined
293
- ? managedClaudeControlPlaneConfig(binding.config, owner.taskId, managedRun?.workItemId, input.turnId)
376
+ && input.runId !== undefined
377
+ ? managedClaudeControlPlaneConfig(binding.config, owner.taskId, managedRun?.workItemId, input.runId)
294
378
  : binding.config;
295
- const effectiveConfig = withNativeProjectDirectories(roleConfig, nativeAdditionalDirectories(effective.workspace, agentWorkspace));
379
+ const effectiveConfig = withNativeProjectDirectories(roleConfig, effective.executionEnvironment === undefined
380
+ ? nativeAdditionalDirectories(effective.workspace, agentWorkspace)
381
+ : []);
296
382
  const compileInput = {
297
383
  agent,
298
384
  config: effectiveConfig,
@@ -307,27 +393,20 @@ export class FileRoleLaunchPlanner {
307
393
  }
308
394
  // A previous attempt may have persisted a preallocated/discovered ID
309
395
  // before its receipt was committed. Reuse that fixed session rather than
310
- // allocating a second native session for the same durable Turn.
396
+ // allocating a second native session for the same durable AgentRun.
311
397
  const resumeNativeSessionId = input.mode === "resume"
312
398
  ? requireText(input.nativeSessionId, "Native session id")
313
399
  : knownNativeSessionId;
314
400
  const launchMode = resumeNativeSessionId === undefined
315
401
  ? "new"
316
402
  : "resume";
317
- const managedControl = owner.scope === "task" && input.turnId !== undefined;
318
- const managedProviderEnvironment = managedControl
319
- && configured.adapterId === "codex"
320
- ? {
321
- // Managed Codex Turns are non-interactive. Use the Codex execution
322
- // identity for provider requests while clientInfo still identifies Yui.
323
- CODEX_INTERNAL_ORIGINATOR_OVERRIDE: "codex_exec"
324
- }
325
- : {};
326
- const preallocatedNativeSessionId = binding.adapterId === "claude"
403
+ const managedControl = owner.scope === "task";
404
+ // Only an Agent that accepts a caller-chosen Session id can have one
405
+ // preallocated. Keying this on the adapter name instead of the declared
406
+ // capability meant every non-Claude adapter was assumed to accept one.
407
+ const preallocatedNativeSessionId = adapter.capabilities.nativeSessionDiscovery === "preallocated"
327
408
  && resumeNativeSessionId === undefined
328
- ? requireText(input.runtimeGenerationId === undefined
329
- ? this.#createNativeSessionId()
330
- : nativeSessionIdForLaunch(this.home, input.runtimeGenerationId, input.agentId, input.adapterId), "Native session id")
409
+ ? requireText(this.#createNativeSessionId(), "Native session id")
331
410
  : resumeNativeSessionId;
332
411
  const managedCompiled = managedControl
333
412
  ? adapter.compileManagedControl(compileInput, launchMode, preallocatedNativeSessionId)
@@ -363,17 +442,14 @@ export class FileRoleLaunchPlanner {
363
442
  }
364
443
  if (binding.adapterId === "codex") {
365
444
  // Interactive Task sessions may use notify for presentation.
366
- // Managed Turns receive lifecycle facts through their ordinary App Server
367
- // subscription, avoiding a second Hook channel for the same Turn.
368
- if (owner.scope === "task" && input.turnId === undefined) {
369
- args = addCodexSessionNotify(args, launchMode, this.#cliPath);
370
- }
445
+ // Managed AgentRuns receive lifecycle facts through their ordinary App Server
446
+ // subscription, avoiding a second Hook channel for the same AgentRun.
371
447
  // Managed Codex Turns use disposable proxy clients against the shared
372
448
  // native App Server. Their Session Manifest points at the Yui Skills;
373
449
  // no Yui Hook config is installed.
374
- if (owner.scope === "task" && input.turnId !== undefined) {
450
+ if (owner.scope === "task" && input.runId !== undefined) {
375
451
  if (managedRun === null || managedRun.status !== "active") {
376
- throw new Error(`Managed Codex Turn is no longer active: ${input.turnId}.`);
452
+ throw new Error(`Managed Codex AgentRun is no longer active: ${input.runId}.`);
377
453
  }
378
454
  }
379
455
  session = launchMode === "resume"
@@ -381,41 +457,41 @@ export class FileRoleLaunchPlanner {
381
457
  : null;
382
458
  }
383
459
  else if (launchMode === "new") {
384
- const nativeSessionId = requireText(preallocatedNativeSessionId, "Native session id");
385
- if (!managedControl)
386
- args.push("--session-id", nativeSessionId);
387
- else if (!args.includes("--session-id"))
388
- args.push("--session-id", nativeSessionId);
389
- session = readySession(input.agentId, binding.adapterId, nativeSessionId, effective);
460
+ if (adapter.capabilities.nativeSessionDiscovery === "preallocated") {
461
+ const nativeSessionId = requireText(preallocatedNativeSessionId, "Native session id");
462
+ if (!managedControl)
463
+ args.push("--session-id", nativeSessionId);
464
+ else if (!args.includes("--session-id"))
465
+ args.push("--session-id", nativeSessionId);
466
+ session = readySession(input.agentId, binding.adapterId, nativeSessionId, effective);
467
+ }
468
+ else {
469
+ // A runtime-discovered Session id does not exist until the Agent
470
+ // answers, so there is no ready Session to record at plan time and no
471
+ // id to pass on the command line. Same shape as a new Codex launch.
472
+ session = null;
473
+ }
390
474
  }
391
475
  else {
392
476
  session = readySession(input.agentId, binding.adapterId, resumeNativeSessionId, effective);
393
477
  }
394
478
  const managedClaudeRun = binding.adapterId === "claude"
395
479
  && owner.scope === "task"
396
- && input.turnId !== undefined;
480
+ && input.runId !== undefined;
397
481
  if (managedClaudeRun && (managedRun === null || managedRun.status !== "active")) {
398
- throw new Error(`Managed Claude Turn is no longer active: ${input.turnId}.`);
482
+ throw new Error(`Managed Claude AgentRun is no longer active: ${input.runId}.`);
399
483
  }
400
- // Generate a candidate per-Provider-process DurableJob caller key for
401
- // every task-scope launch. A reused live Conversation keeps the key that
402
- // its process inherited; a new Host or a Conversation replacement commits
403
- // the fresh candidate only after Provider dispatch is observed.
404
- let jobCallerKey;
405
- if (owner.scope === "task" && (input.mode === "new" || input.mode === "resume")) {
406
- jobCallerKey = randomBytes(32).toString("hex");
407
- }
408
- // Session lifecycle and Turn submission are separate atomic operations.
484
+ // Session lifecycle and AgentRun submission are separate atomic operations.
409
485
  // Planning only starts or restores the exact native Session; delivery
410
- // submits the Turn input after that Session fact is durable.
411
- if (managedControl && (managedRun === null || managedRun.status !== "active")) {
412
- throw new Error(`Managed Turn is no longer active: ${input.turnId}.`);
486
+ // submits the AgentRun input after that Session fact is durable.
487
+ if (managedControl && input.runId !== undefined && (managedRun === null || managedRun.status !== "active")) {
488
+ throw new Error(`Managed AgentRun is no longer active: ${input.runId}.`);
413
489
  }
414
490
  const providerAuthority = managedControl
415
- ? this.#providerAuthorityForLaunch(owner.taskId, role.name, input.runtimeGenerationId, input.mode)
491
+ ? this.#providerAuthorityForLaunch(owner.taskId, role.name, input.mode)
416
492
  : undefined;
417
- const providerOwnedTurn = managedControl && input.mode === "resume"
418
- ? this.#providerOwnedTurnForLaunch(owner.taskId, role.name, input.turnId)
493
+ const providerOwnedRun = managedControl && input.mode === "resume" && input.runId !== undefined
494
+ ? this.#providerOwnedRunForLaunch(owner.taskId, role.name, input.runId)
419
495
  : undefined;
420
496
  const providerNativeSessionId = binding.adapterId === "claude"
421
497
  ? preallocatedNativeSessionId
@@ -426,22 +502,35 @@ export class FileRoleLaunchPlanner {
426
502
  ? {
427
503
  schemaVersion: 1,
428
504
  adapterId: binding.adapterId,
505
+ // The product identity is pinned by the snapshot this launch was
506
+ // resolved from, and the check above proves the Agent record still
507
+ // names it. Carrying only the adapter would leave the runtime with
508
+ // two products sharing one plan and no way to tell them apart.
509
+ component: binding.component,
429
510
  transport: managedCompiled.transport,
511
+ endpointImplementation,
430
512
  kind: "restore",
513
+ sessionOnly: input.runId === undefined,
431
514
  mode: "resume",
432
515
  nativeSessionId: requireText(providerNativeSessionId, "Managed Provider restore native session id"),
433
516
  ...(sessionTitle === undefined ? {} : { sessionTitle }),
434
517
  ...(managedCompiled.codexThread === undefined
435
518
  ? {}
436
519
  : { codexThread: managedCompiled.codexThread }),
437
- ...(providerOwnedTurn === undefined ? {} : { ownedTurn: providerOwnedTurn }),
520
+ ...(providerOwnedRun === undefined ? {} : { ownedTurn: providerOwnedRun }),
521
+ ...(managedCompiled.acpSession === undefined
522
+ ? {}
523
+ : { acpSession: managedCompiled.acpSession }),
438
524
  authority: providerAuthority
439
525
  }
440
526
  : {
441
527
  schemaVersion: 1,
442
528
  adapterId: binding.adapterId,
529
+ component: binding.component,
443
530
  transport: managedCompiled.transport,
531
+ endpointImplementation,
444
532
  kind: "start",
533
+ sessionOnly: input.runId === undefined,
445
534
  mode: "new",
446
535
  ...(providerNativeSessionId === undefined
447
536
  ? {}
@@ -450,15 +539,23 @@ export class FileRoleLaunchPlanner {
450
539
  ...(managedCompiled.codexThread === undefined
451
540
  ? {}
452
541
  : { codexThread: managedCompiled.codexThread }),
542
+ ...(managedCompiled.acpSession === undefined
543
+ ? {}
544
+ : { acpSession: managedCompiled.acpSession }),
453
545
  authority: providerAuthority
454
546
  };
455
547
  const launch = {
456
548
  command,
457
549
  args,
550
+ ...(effective.executionEnvironment === undefined ? {} : {
551
+ executionEnvironment: structuredClone(effective.executionEnvironment)
552
+ }),
458
553
  ...(providerControl === undefined ? {} : { providerControl }),
554
+ ...(owner.scope === "global" && binding.adapterId === "codex"
555
+ ? { interactiveCodexThread: compiled.codexThread }
556
+ : {}),
459
557
  env: {
460
558
  ...launchEnvironment,
461
- ...managedProviderEnvironment,
462
559
  YUI_HOME: resolve(this.home),
463
560
  YUI_SESSION_SCOPE: owner.scope,
464
561
  ...(owner.scope === "task" ? { YUI_TASK_ID: owner.taskId } : {}),
@@ -469,10 +566,9 @@ export class FileRoleLaunchPlanner {
469
566
  YUI_WORKSPACE: effectiveWorkspace,
470
567
  YUI_SESSION_MANIFEST: sessionContext.sessionManifestPath,
471
568
  YUI_SESSION_CLI: sessionContext.sessionCliPath,
472
- ...(owner.scope === "global" && configured.adapterId === "codex"
569
+ ...(configured.adapterId === "codex"
473
570
  ? { YUI_AGENT_BASE_ARGS: JSON.stringify(configured.baseArgs) }
474
571
  : {}),
475
- ...(jobCallerKey === undefined ? {} : { YUI_JOB_CALLER_KEY: jobCallerKey }),
476
572
  ...(sessionTitle === undefined
477
573
  ? {}
478
574
  : {
@@ -484,13 +580,10 @@ export class FileRoleLaunchPlanner {
484
580
  }
485
581
  : {})
486
582
  }),
487
- ...(input.runtimeGenerationId === undefined
488
- ? {}
489
- : { YUI_RUNTIME_GENERATION_ID: input.runtimeGenerationId }),
490
- // No Turn is exported into the Session environment. A native pane
491
- // outlives its Turn, so a Turn id frozen here would be stale for every
492
- // later Turn. The Agent Host sets it explicitly per spawned Provider
493
- // Turn, and every durable decision reads the active Turn from state.
583
+ // No AgentRun is exported into the Session environment. A native pane
584
+ // outlives its AgentRun, so a AgentRun id frozen here would be stale for every
585
+ // later AgentRun. The Agent Host sets it explicitly per spawned Provider
586
+ // AgentRun, and every durable decision reads the active AgentRun from state.
494
587
  ...(session === null
495
588
  ? {}
496
589
  : { YUI_NATIVE_SESSION_ID: session.nativeSessionId })
@@ -518,24 +611,17 @@ export class FileRoleLaunchPlanner {
518
611
  ...(sessionTitle === undefined ? {} : { sessionTitle })
519
612
  };
520
613
  }
521
- #providerAuthorityForLaunch(taskId, roleName, runtimeGenerationId, mode) {
522
- const activationId = requireText(runtimeGenerationId, "Managed Provider Activation id");
614
+ #providerAuthorityForLaunch(taskId, roleName, mode) {
523
615
  const binding = this.store.getTaskRoleSessionSet(taskId, roleName)?.providerBinding;
524
616
  if (binding === null || binding === undefined) {
525
- return { epoch: 1, owner: "controller", holderId: activationId };
617
+ return { epoch: 1, owner: "controller", holderId: "controller" };
526
618
  }
527
619
  if (binding.authority.owner === "controller") {
528
620
  if (mode === "new") {
529
- const activation = currentProviderActivation(binding);
530
- if (activation === null || binding.authority.holderId !== activation.activationId) {
531
- throw new Error(`Provider Activation is not exact: ${taskId}/${roleName}.`);
532
- }
533
- // An actor-requested switch first ends the idle old Activation (+1)
534
- // and then binds the replacement Activation (+1).
535
621
  return {
536
- epoch: binding.authority.epoch + 2,
622
+ epoch: binding.authority.epoch + 1,
537
623
  owner: "controller",
538
- holderId: activationId
624
+ holderId: "controller"
539
625
  };
540
626
  }
541
627
  return {
@@ -551,32 +637,32 @@ export class FileRoleLaunchPlanner {
551
637
  return {
552
638
  epoch: binding.authority.epoch + 1,
553
639
  owner: "controller",
554
- holderId: activationId
640
+ holderId: "controller"
555
641
  };
556
642
  }
557
643
  throw new Error(`Provider writer authority is unknown: ${taskId}/${roleName}.`);
558
644
  }
559
- #providerOwnedTurnForLaunch(taskId, roleName, turnId) {
645
+ #providerOwnedRunForLaunch(taskId, roleName, runId) {
560
646
  const binding = this.store.getTaskRoleSessionSet(taskId, roleName)?.providerBinding;
561
647
  if (binding === null || binding === undefined
562
- || binding.turn?.turnId !== turnId)
648
+ || binding.run?.runId !== runId)
563
649
  return undefined;
564
- const turn = binding.turn;
565
- if (turn === null || turn.status !== "accepted")
650
+ const run = binding.run;
651
+ if (run === null || run.status !== "accepted")
566
652
  return undefined;
567
- if (turn.nativeTurnId === undefined) {
568
- throw new Error(`Active Provider Turn has no native identity: ${taskId}/${roleName}.`);
653
+ if (run.nativeTurnId === undefined) {
654
+ throw new Error(`Active Provider AgentRun has no native identity: ${taskId}/${roleName}.`);
569
655
  }
570
- return { attemptId: turn.attemptId, turnId: turn.nativeTurnId };
656
+ return { attemptId: run.attemptId, turnId: run.nativeTurnId };
571
657
  }
572
658
  #applyWorkspaceScope(taskId, role, launch, workspaceOverride) {
573
659
  const workspace = workspaceOverride
574
660
  ?? (role.name === "leader"
575
661
  ? this.store.getTaskWorkspace(taskId)
576
662
  : this.store.listWorkItems(taskId)
577
- .find((item) => item.assignee === role.name && item.status === "running") === undefined
663
+ .find((item) => item.assignee === role.name && item.status === "open") === undefined
578
664
  ? this.store.getTaskWorkspace(taskId)
579
- : this.store.getWorkItemWorkspace(taskId, this.store.listWorkItems(taskId).find((item) => item.assignee === role.name && item.status === "running").id));
665
+ : this.store.getWorkItemWorkspace(taskId, this.store.listWorkItems(taskId).find((item) => item.assignee === role.name && item.status === "open").id));
580
666
  if (workspace === null || workspace === undefined)
581
667
  return launch;
582
668
  return {
@@ -622,9 +708,14 @@ export function withNativeProjectDirectories(config, projectDirectories) {
622
708
  ])]
623
709
  };
624
710
  }
625
- function resolveTaskRoleEffectiveLaunch(store, role) {
711
+ function resolveTaskRoleEffectiveLaunch(store, role, purpose = "execution") {
712
+ if (purpose === "planning") {
713
+ // Planning has no WorkItem assignment and no managed workspace, so it
714
+ // resolves the Role's own configured workspace with no writable Project.
715
+ return resolveEffectiveLaunch({ role, purpose: "planning" });
716
+ }
626
717
  const item = store.listWorkItems(role.taskId).find((candidate) => (candidate.assignee === role.name
627
- && !["completed", "failed", "retired"].includes(candidate.status))) ?? null;
718
+ && !["accepted", "retired"].includes(candidate.status))) ?? null;
628
719
  const workspace = (item === null
629
720
  ? store.getTaskWorkspace(role.taskId)
630
721
  : store.getWorkItemWorkspace(role.taskId, item.id))
@@ -684,11 +775,11 @@ function ensureClaudeLifecyclePlugin(home, cliPath) {
684
775
  }, null, 2)}\n`);
685
776
  return root;
686
777
  }
687
- function managedClaudeControlPlaneConfig(config, taskId, workItemId, turnId) {
778
+ function managedClaudeControlPlaneConfig(config, taskId, workItemId, runId) {
688
779
  if (config.permission.strategy !== "configured")
689
780
  return config;
690
781
  const managed = [
691
- `Bash(yui task turn context ${taskId}/${turnId}:*)`,
782
+ `Bash(yui task run context ${taskId}/${runId}:*)`,
692
783
  `Bash(yui --json task context ${taskId})`,
693
784
  `Bash(yui --json task work list ${taskId})`,
694
785
  ...(workItemId === undefined
@@ -751,22 +842,6 @@ function patchEnvironment(current, names, values) {
751
842
  next[name] = value;
752
843
  return next;
753
844
  }
754
- /**
755
- * Codex invokes this argv after each completed turn and appends one JSON
756
- * payload. JSON arrays are valid TOML arrays for the managed config override.
757
- */
758
- export function codexSessionNotifyConfig(cliPath) {
759
- return `notify=${JSON.stringify([process.execPath, cliPath, "internal", "session-notify"])}`;
760
- }
761
- function addCodexSessionNotify(args, mode, cliPath) {
762
- const managed = ["--config", codexSessionNotifyConfig(cliPath)];
763
- if (mode === "new")
764
- return [...args, ...managed];
765
- if (args.length < 2 || args.at(-2) !== "resume") {
766
- throw new Error("Codex resume launch shape is invalid.");
767
- }
768
- return [...args.slice(0, -2), ...managed, ...args.slice(-2)];
769
- }
770
845
  /**
771
846
  * Global Codex keeps its native TUI, but the TUI is only a client attachment
772
847
  * to the user's shared App Server. The daemon owns the Thread and its writer,
@@ -798,7 +873,7 @@ function codexShellEnvironmentConfig(environment) {
798
873
  /**
799
874
  * Codex 0.145 discovers lifecycle hooks from its effective config. Keep Yui's
800
875
  * two handlers invocation-local: this avoids mutating CODEX_HOME or the Task
801
- * workspace, while the exact launch environment supplies the durable Turn fence.
876
+ * workspace, while the exact launch environment supplies the durable AgentRun fence.
802
877
  */
803
878
  function codexLifecycleHooksConfig(cliPath) {
804
879
  const command = [