@zq-silk/yui 0.15.7 → 0.15.9

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 (308) hide show
  1. package/ARCHITECTURE.md +194 -399
  2. package/ARCHITECTURE.zh-CN.md +151 -0
  3. package/README.md +306 -1131
  4. package/dist/agent/adapterCatalog.js +15 -2
  5. package/dist/agent/agent.js +23 -3
  6. package/dist/agent/argumentPolicy.js +7 -1
  7. package/dist/agent/connectionPlan.js +62 -0
  8. package/dist/agent/executionComponents.js +158 -0
  9. package/dist/agent/launchEnvironment.js +31 -3
  10. package/dist/agent/managedRuntimeEnvironment.js +3 -5
  11. package/dist/{turn/turn.js → agentRun/agentRun.js} +166 -109
  12. package/dist/{turn/turnIdentity.js → agentRun/runIdentity.js} +4 -4
  13. package/dist/artifacts/artifactCapability.js +74 -0
  14. package/dist/artifacts/artifactCommitLock.js +249 -0
  15. package/dist/artifacts/artifactPaths.js +151 -0
  16. package/dist/artifacts/gitArtifactRef.js +146 -0
  17. package/dist/artifacts/managedGit.js +332 -0
  18. package/dist/artifacts/taskArtifactRepository.js +277 -0
  19. package/dist/brief/taskBrief.js +12 -0
  20. package/dist/cli/agentConfigurationPicker.js +13 -0
  21. package/dist/cli/commandCatalog.js +165 -74
  22. package/dist/cli/interactionCandidates.js +5 -5
  23. package/dist/cli/interactionPolicy.js +38 -8
  24. package/dist/cli/invocationRouter.js +1 -1
  25. package/dist/cli/managedDiagnostics.js +28 -0
  26. package/dist/cli/operatorWizard.js +1 -7
  27. package/dist/cli/roleOptionOrder.js +27 -0
  28. package/dist/cli/roleWizard.js +50 -14
  29. package/dist/cli/updateOrchestrator.js +1 -1
  30. package/dist/cli/updatePorts.js +3 -4
  31. package/dist/cli.js +245 -95
  32. package/dist/commands/agentCommands.js +72 -14
  33. package/dist/commands/capabilityCommands.js +9 -6
  34. package/dist/commands/configCommands.js +20 -20
  35. package/dist/commands/deliveryGuardPreflight.js +2 -2
  36. package/dist/commands/executionAuditCommands.js +24 -24
  37. package/dist/commands/globalRoleCommands.js +1 -1
  38. package/dist/commands/grantCommands.js +4 -4
  39. package/dist/commands/operatorCommands.js +34 -9
  40. package/dist/commands/projectCommands.js +4 -4
  41. package/dist/commands/resourcesCommands.js +2 -2
  42. package/dist/commands/roleConfiguration.js +25 -5
  43. package/dist/commands/roleRuntimeGuard.js +4 -5
  44. package/dist/commands/sessionCommands.js +3 -7
  45. package/dist/commands/taskActivationCommands.js +281 -0
  46. package/dist/commands/taskActor.js +28 -49
  47. package/dist/commands/taskCommands.js +1461 -720
  48. package/dist/commands/taskContextCommand.js +39 -583
  49. package/dist/commands/taskExecutionCommands.js +32 -32
  50. package/dist/commands/taskInputCommands.js +40 -104
  51. package/dist/commands/taskIntegrationCommands.js +3 -2
  52. package/dist/commands/taskIntegrationQueueCommands.js +1 -1
  53. package/dist/commands/taskNextActionCommand.js +8 -8
  54. package/dist/commands/taskOverviewCommand.js +33 -45
  55. package/dist/commands/taskRemoteDeliveryCommand.js +2 -2
  56. package/dist/commands/taskRoleRuntimeStatus.js +133 -102
  57. package/dist/commands/telemetryCommands.js +36 -38
  58. package/dist/config/configCatalog.js +4 -4
  59. package/dist/config/yuiConfig.js +8 -8
  60. package/dist/context/contextSnapshot.js +10 -10
  61. package/dist/context/dispatchContext.js +11 -11
  62. package/dist/context/roleSessionContext.js +6 -3
  63. package/dist/context/{turnContextPack.js → runContextPack.js} +158 -81
  64. package/dist/context/{turnInputContract.js → runInputContract.js} +73 -60
  65. package/dist/context/sessionBootstrapManifest.js +21 -2
  66. package/dist/context/sourceRunContext.js +30 -0
  67. package/dist/context/taskContext.js +481 -0
  68. package/dist/context/wakeNotification.js +27 -27
  69. package/dist/controller/agentRuntimeObserver.js +21 -24
  70. package/dist/controller/capabilityBridge.js +17 -6
  71. package/dist/controller/clientRuntime.js +65 -92
  72. package/dist/controller/controller.js +120 -188
  73. package/dist/controller/fileSchedulerStoreAdapter.js +787 -887
  74. package/dist/controller/jobControl.js +54 -85
  75. package/dist/controller/resourceInventory.js +8 -27
  76. package/dist/controller/resourceInventoryLinux.js +12 -13
  77. package/dist/controller/runtime.js +529 -479
  78. package/dist/controller/runtimeEventInbox.js +55 -25
  79. package/dist/controller/runtimeEventProcessor.js +22 -31
  80. package/dist/controller/{runtimeHookTurnFence.js → runtimeHookRunFence.js} +91 -115
  81. package/dist/controller/runtimeLaunchCoordinator.js +80 -426
  82. package/dist/controller/runtimeObservationHook.js +14 -18
  83. package/dist/controller/sessionNotify.js +16 -24
  84. package/dist/controller/sessionOwnerReconciliation.js +168 -50
  85. package/dist/controller/structuredProviderObservation.js +138 -99
  86. package/dist/coordination/workMailbox.js +3 -3
  87. package/dist/coordination/workMailboxQueue.js +36 -33
  88. package/dist/core/boundedRpc.js +8 -1
  89. package/dist/core/controllerClient.js +20 -1
  90. package/dist/core/controllerServer.js +4 -4
  91. package/dist/doctor/doctor.js +13 -2
  92. package/dist/domain/agentResultTransport.js +9 -9
  93. package/dist/execution/codexThreadNaming.js +2 -8
  94. package/dist/execution/executionHealth.js +51 -63
  95. package/dist/execution/reviewMainRun.js +137 -0
  96. package/dist/execution/workItemExecution.js +28 -29
  97. package/dist/execution/workItemExecutionProjection.js +99 -107
  98. package/dist/execution/workItemMainRun.js +141 -0
  99. package/dist/executor/agentAdapter.js +227 -20
  100. package/dist/executor/agentConfigurationCatalog.js +126 -4
  101. package/dist/executor/agentConfigurationProbe.js +162 -4
  102. package/dist/executor/agentExecutor.js +79 -78
  103. package/dist/executor/effectiveLaunch.js +105 -18
  104. package/dist/executor/executorRegistry.js +29 -44
  105. package/dist/executor/fileRoleLaunchPlanner.js +229 -154
  106. package/dist/executor/workspacePreflightClassification.js +16 -16
  107. package/dist/grant/capabilityGrant.js +6 -3
  108. package/dist/input/inputRequest.js +12 -10
  109. package/dist/integration/gitIntegrationService.js +4 -11
  110. package/dist/integration/integrationQueueService.js +4 -4
  111. package/dist/interaction/operatorPresentation.js +1 -1
  112. package/dist/kernel/builtinCapabilities.js +255 -12
  113. package/dist/kernel/capabilityRegistry.js +64 -18
  114. package/dist/kernel/instanceHost.js +12 -1
  115. package/dist/kernel/kernelPorts.js +2 -2
  116. package/dist/lifecycle/canonicalLifecycleEvent.js +44 -49
  117. package/dist/lifecycle/exactRunTerminalization.js +449 -0
  118. package/dist/message/message.js +118 -6
  119. package/dist/message/messageContinuation.js +204 -0
  120. package/dist/observability/executionAudit.js +70 -72
  121. package/dist/observability/faultClassification.js +2 -2
  122. package/dist/observability/orchestrationMetrics.js +8 -8
  123. package/dist/operator/operatorSessionHistory.js +1 -7
  124. package/dist/output/agentConfigurationPresentation.js +8 -3
  125. package/dist/output/agentRunConfigurationPresentation.js +128 -0
  126. package/dist/output/rolePresentation.js +54 -3
  127. package/dist/plugins/pluginChild.js +104 -0
  128. package/dist/plugins/pluginIntent.js +26 -0
  129. package/dist/plugins/pluginInterpreter.js +43 -0
  130. package/dist/plugins/pluginPackage.js +101 -0
  131. package/dist/plugins/pluginProcess.js +112 -0
  132. package/dist/plugins/pluginService.js +388 -0
  133. package/dist/profile/agentProfile.js +1 -1
  134. package/dist/repository/gitWorkspace.js +26 -4
  135. package/dist/repository/project.js +19 -4
  136. package/dist/repository/taskBaseFreshness.js +13 -13
  137. package/dist/repository/taskWorkspaceCoordinator.js +20 -27
  138. package/dist/repository/taskWorkspacePreparer.js +344 -83
  139. package/dist/resources/autoResourceGc.js +3 -3
  140. package/dist/resources/liveReferences.js +3 -3
  141. package/dist/resources/projectResource.js +75 -0
  142. package/dist/resources/projectResourceService.js +343 -0
  143. package/dist/resources/resourceDiscovery.js +6 -6
  144. package/dist/resources/resourceGc.js +1 -1
  145. package/dist/resources/resourceRegistrar.js +1 -1
  146. package/dist/resources/resourceTypes.js +1 -1
  147. package/dist/review/deltaRecheck.js +3 -3
  148. package/dist/review/reviewAcceptance.js +16 -16
  149. package/dist/review/reviewDecision.js +7 -7
  150. package/dist/review/reviewRound.js +21 -20
  151. package/dist/review/reviewerAvailability.js +2 -2
  152. package/dist/role/role.js +51 -7
  153. package/dist/role/taskRoleUpdate.js +30 -0
  154. package/dist/runtime/acpProtocol.js +425 -0
  155. package/dist/runtime/acpSession.js +731 -0
  156. package/dist/runtime/acpSessionConfiguration.js +260 -0
  157. package/dist/runtime/agentDriver.js +30 -11
  158. package/dist/runtime/agentEndpoint.js +278 -0
  159. package/dist/runtime/agentEndpointIdentity.js +86 -0
  160. package/dist/runtime/agentEndpointOwnership.js +239 -0
  161. package/dist/runtime/agentError.js +2 -10
  162. package/dist/runtime/agentHost.js +565 -314
  163. package/dist/runtime/agentRunConfiguration.js +258 -0
  164. package/dist/runtime/builtinAgentDrivers.js +134 -18
  165. package/dist/runtime/builtinAgentErrorMappers.js +55 -3
  166. package/dist/runtime/builtinTranscriptUsage.js +1 -1
  167. package/dist/runtime/claude-process-owner +0 -0
  168. package/dist/runtime/codexAppServerRuntime.js +38 -30
  169. package/dist/runtime/codexInteractiveHost.js +41 -6
  170. package/dist/runtime/continuationManager.js +2 -6
  171. package/dist/runtime/executionEnvironment.js +30 -0
  172. package/dist/runtime/firstProgressAdvisory.js +11 -11
  173. package/dist/runtime/index.js +4 -3
  174. package/dist/runtime/jsonLineChannel.js +109 -0
  175. package/dist/runtime/launchBroker.js +91 -16
  176. package/dist/runtime/launchDiagnostics.js +2 -2
  177. package/dist/runtime/lifecycleReservation.js +10 -18
  178. package/dist/runtime/managedCaller.js +61 -17
  179. package/dist/runtime/nativeSessionControl.js +102 -0
  180. package/dist/runtime/ports.js +6 -21
  181. package/dist/runtime/processExitObservation.js +8 -7
  182. package/dist/runtime/promptEnvelope.js +17 -6
  183. package/dist/runtime/providerContinuation.js +3 -9
  184. package/dist/runtime/providerContinuationReconciliationService.js +4 -13
  185. package/dist/runtime/providerControl.js +2 -7
  186. package/dist/runtime/providerRuntimeIdentity.js +110 -222
  187. package/dist/runtime/providerRuntimeReconciler.js +5 -9
  188. package/dist/runtime/runtimeBinding.js +0 -1
  189. package/dist/runtime/runtimeContinuationProjection.js +4 -7
  190. package/dist/runtime/runtimeDeadlines.js +9 -0
  191. package/dist/runtime/runtimeHealthPolicy.js +1 -1
  192. package/dist/runtime/runtimeObservation.js +29 -65
  193. package/dist/runtime/runtimeProjection.js +43 -51
  194. package/dist/runtime/runtimeSessionCandidate.js +1 -3
  195. package/dist/runtime/sessionLaunchRequest.js +3 -7
  196. package/dist/runtime/sessionOwnerIdentity.js +7 -54
  197. package/dist/runtime/sessionOwnerRegistry.js +22 -17
  198. package/dist/runtime/sessionReconciliation.js +4 -8
  199. package/dist/runtime/sessionTerminationGuard.js +70 -259
  200. package/dist/runtime/sessionTokenMetrics.js +5 -16
  201. package/dist/runtime/structuredProviderHost.js +237 -117
  202. package/dist/runtime/taskRuntimeIsolation.js +39 -122
  203. package/dist/runtime/tmuxAdapters.js +39 -86
  204. package/dist/scheduler/activeRoleRunDelivery.js +354 -0
  205. package/dist/scheduler/leaderWakeupProcessor.js +75 -266
  206. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  207. package/dist/scheduler/ports.js +80 -9
  208. package/dist/scheduler/{roleTurnLiveness.js → roleRunLiveness.js} +26 -30
  209. package/dist/scheduler/{roleTurnStall.js → roleRunStall.js} +128 -139
  210. package/dist/scheduler/taskExecutionProjection.js +120 -124
  211. package/dist/scheduler/taskObservabilityProjection.js +29 -29
  212. package/dist/scheduler/taskWake.js +11 -4
  213. package/dist/scheduler/wakeReason.js +9 -1
  214. package/dist/setup/setupCommand.js +3 -7
  215. package/dist/storage/migrations/agentRunContract.js +159 -0
  216. package/dist/storage/migrations/artifactsToGit.js +338 -0
  217. package/dist/storage/migrations/removeRuntimeGeneration.js +207 -0
  218. package/dist/storage/migrations/submitIntent.js +126 -0
  219. package/dist/storage/sqliteSchema.js +467 -7
  220. package/dist/storage/sqliteStore.js +355 -220
  221. package/dist/storage/storageVersions.js +1 -1
  222. package/dist/storage/storeRpc.js +10 -5
  223. package/dist/storage/taskStore.js +13 -11
  224. package/dist/storage/upgrade/upgradeOrchestrator.js +5 -7
  225. package/dist/surface/surfaceContributions.js +102 -0
  226. package/dist/task/completionReadiness.js +32 -6
  227. package/dist/task/deliveryGuard.js +16 -16
  228. package/dist/task/draftPlan.js +72 -12
  229. package/dist/task/nextAction.js +144 -128
  230. package/dist/task/remoteDelivery.js +6 -6
  231. package/dist/task/task.js +184 -18
  232. package/dist/task/taskActivation.js +327 -0
  233. package/dist/task/taskActivationService.js +408 -0
  234. package/dist/task/taskRecordReference.js +5 -4
  235. package/dist/task/taskRecordRetirement.js +1 -1
  236. package/dist/task/taskSubmission.js +236 -0
  237. package/dist/telemetry/sqliteTelemetryStore.js +55 -68
  238. package/dist/telemetry/telemetryConfig.js +14 -14
  239. package/dist/telemetry/telemetryWiring.js +2 -2
  240. package/dist/web/assets/assetManifest.js +2 -0
  241. package/dist/web/assets/client/app.js +121 -20
  242. package/dist/web/assets/client/components.js +87 -54
  243. package/dist/web/assets/client/i18n.js +83 -41
  244. package/dist/web/assets/client/markdown.js +1 -1
  245. package/dist/web/assets/client/taskSurface.js +442 -0
  246. package/dist/web/assets/client/view.js +49 -44
  247. package/dist/web/assets/shell.js +1 -1
  248. package/dist/web/assets/styles/cards.js +22 -4
  249. package/dist/web/controllerWeb.js +60 -0
  250. package/dist/web/webMutation.js +28 -0
  251. package/dist/web/webServer.js +133 -8
  252. package/dist/web/webSnapshot.js +81 -74
  253. package/dist/web/webTaskSurface.js +64 -0
  254. package/dist/workItem/dependencyGate.js +1 -1
  255. package/dist/workItem/workItem.js +84 -48
  256. package/dist/workspace/workItemChangeSetManager.js +16 -9
  257. package/docs/agent-result-consumption.md +96 -0
  258. package/docs/agent-result-consumption.zh-CN.md +81 -0
  259. package/docs/agent-runtime-drivers.md +93 -0
  260. package/docs/agent-runtime-drivers.zh-CN.md +77 -0
  261. package/docs/architecture/README.md +50 -0
  262. package/docs/architecture/README.zh-CN.md +43 -0
  263. package/docs/architecture/capabilities-and-resources.md +118 -0
  264. package/docs/architecture/capabilities-and-resources.zh-CN.md +83 -0
  265. package/docs/managed-turn-and-session-runtime.md +224 -0
  266. package/docs/managed-turn-and-session-runtime.zh-CN.md +180 -0
  267. package/docs/observability/README.md +83 -0
  268. package/docs/observability/README.zh-CN.md +71 -0
  269. package/docs/plugin-sdk.md +393 -0
  270. package/docs/plugin-sdk.zh-CN.md +293 -0
  271. package/docs/provider-runtime.md +165 -0
  272. package/docs/provider-runtime.zh-CN.md +132 -0
  273. package/docs/release-workflow.md +305 -0
  274. package/docs/release-workflow.zh-CN.md +237 -0
  275. package/docs/roles-and-configuration.md +115 -0
  276. package/docs/roles-and-configuration.zh-CN.md +96 -0
  277. package/docs/sqlite-control-plane-design.md +78 -0
  278. package/docs/sqlite-control-plane-design.zh-CN.md +62 -0
  279. package/docs/task-dag-semantics.md +80 -0
  280. package/docs/task-dag-semantics.zh-CN.md +59 -0
  281. package/docs/task-delivery.md +105 -0
  282. package/docs/task-delivery.zh-CN.md +82 -0
  283. package/docs/task-local-identity.md +8 -6
  284. package/docs/task-local-identity.zh-CN.md +58 -0
  285. package/docs/testing/verification-levels.md +88 -0
  286. package/docs/testing/verification-levels.zh-CN.md +69 -0
  287. package/i18n/README.zh-CN.md +270 -722
  288. package/package.json +3 -2
  289. package/skills/yui-leader/SKILL.md +88 -304
  290. package/skills/yui-leader/references/execution.md +303 -0
  291. package/skills/yui-leader/references/integration.md +39 -0
  292. package/skills/yui-leader/references/planning.md +109 -0
  293. package/skills/yui-leader/references/replicated-execution.md +42 -0
  294. package/skills/yui-leader/references/task-plugins.md +37 -0
  295. package/skills/yui-operator/SKILL.md +46 -62
  296. package/skills/yui-reviewer/SKILL.md +35 -36
  297. package/skills/yui-runtime/SKILL.md +88 -24
  298. package/skills/yui-runtime/references/publication.md +22 -0
  299. package/skills/yui-runtime/references/recovery.md +64 -0
  300. package/skills/yui-worker/SKILL.md +37 -39
  301. package/dist/cli/roleOptionCatalog.js +0 -68
  302. package/dist/context/sourceTurnContext.js +0 -30
  303. package/dist/execution/reviewMainTurn.js +0 -161
  304. package/dist/execution/workItemMainTurn.js +0 -164
  305. package/dist/lifecycle/exactTurnTerminalization.js +0 -407
  306. package/dist/runtime/preallocatedNativeSession.js +0 -13
  307. package/dist/runtime/runtimeStopReceipt.js +0 -42
  308. package/dist/scheduler/activeRoleTurnDelivery.js +0 -315
@@ -1,3 +1,16 @@
1
+ import { YUI_VERSION } from "../version.js";
2
+ /**
3
+ * Preserve the native non-interactive Codex identity used by our historical
4
+ * execution adapter. A proxy's environment cannot set the daemon's originator;
5
+ * the daemon derives it from initialization (and can retain its first identity).
6
+ * Keep Yui's title/version explicit and do not opt into unsupported attestation.
7
+ */
8
+ export function codexClientInitialization() {
9
+ return {
10
+ clientInfo: { name: "codex_exec", title: "Yui", version: YUI_VERSION },
11
+ capabilities: { experimentalApi: true, requestAttestation: false }
12
+ };
13
+ }
1
14
  export class CodexAppServerRequestError extends Error {
2
15
  code;
3
16
  data;
@@ -8,6 +21,10 @@ export class CodexAppServerRequestError extends Error {
8
21
  this.data = data;
9
22
  }
10
23
  }
24
+ /** The pre-submit read failed; no mutation request was issued. */
25
+ export class CodexPreSubmissionError extends Error {
26
+ name = "CodexPreSubmissionError";
27
+ }
11
28
  /**
12
29
  * Continuable Codex integration over App Server. A transport connection is
13
30
  * deliberately not an Activation identity: callers supply the persisted
@@ -41,11 +58,11 @@ export class CodexAppServerRuntime {
41
58
  name: text(input.name, "Codex thread name")
42
59
  });
43
60
  }
44
- async readConversation(conversationId) {
61
+ async readConversation(conversationId, options = {}) {
45
62
  const id = text(conversationId, "Codex thread id");
46
63
  const result = await this.transport.request("thread/read", {
47
64
  threadId: id,
48
- includeTurns: true
65
+ includeTurns: options.includeTurns ?? true
49
66
  });
50
67
  return parseThreadSnapshot(result, id, "unknown");
51
68
  }
@@ -84,22 +101,26 @@ export class CodexAppServerRuntime {
84
101
  const requestedThreadId = text(input.conversationId, "Codex thread id");
85
102
  let threadId = requestedThreadId;
86
103
  try {
87
- const snapshot = await this.readConversation(requestedThreadId);
104
+ // Submission needs current availability, not historical Turns. Some
105
+ // App Server transports expose status without supporting history reads.
106
+ const snapshot = await this.readConversation(requestedThreadId, { includeTurns: false });
88
107
  threadId = snapshot.threadId;
89
- if (input.expectedNoActiveTurn && snapshot.activeTurnId !== undefined) {
108
+ if (input.expectedNoActiveTurn
109
+ && (snapshot.status === "active" || snapshot.activeTurnId !== undefined)) {
90
110
  return {
91
111
  status: "busy",
92
- activeTurnId: snapshot.activeTurnId,
93
- reason: `active-turn:${snapshot.activeTurnId}`
112
+ ...(snapshot.activeTurnId === undefined ? {} : { activeTurnId: snapshot.activeTurnId }),
113
+ reason: snapshot.activeTurnId === undefined
114
+ ? "Provider Conversation has an active Turn."
115
+ : `active-turn:${snapshot.activeTurnId}`
94
116
  };
95
117
  }
118
+ if (snapshot.status !== "idle" && snapshot.status !== "active") {
119
+ throw new Error(`Codex Session availability is ${snapshot.status}; no input was submitted.`);
120
+ }
96
121
  }
97
122
  catch (error) {
98
- // A new App Server thread has no materialized Turn history yet. This
99
- // exact response proves there cannot be an active Turn, so its first
100
- // mutation can proceed without weakening unknown-delivery handling.
101
- if (!codexAppServerErrorIsUnmaterialized(error))
102
- throw error;
123
+ throw new CodexPreSubmissionError("Codex Session inspection failed before input submission.", { cause: error });
103
124
  }
104
125
  try {
105
126
  const result = await this.transport.request("turn/start", {
@@ -126,7 +147,9 @@ export class CodexAppServerRuntime {
126
147
  async steerTurn(input) {
127
148
  const threadId = text(input.conversationId, "Codex thread id");
128
149
  const expectedTurnId = text(input.expectedTurnId, "Codex expected Turn id");
129
- const snapshot = await this.readConversation(threadId);
150
+ const snapshot = await this.readConversation(threadId).catch((error) => {
151
+ throw new CodexPreSubmissionError("Codex Session inspection failed before native steer.", { cause: error });
152
+ });
130
153
  if (snapshot.activeTurnId !== expectedTurnId) {
131
154
  return {
132
155
  status: "not-accepted",
@@ -215,9 +238,7 @@ export class CodexAppServerRuntime {
215
238
  input.providerNamespace,
216
239
  input.accountScope,
217
240
  input.conversationId,
218
- input.activationId,
219
- continuation.continuationId,
220
- continuation.generation
241
+ continuation.continuationId
221
242
  ].join("\u0000"),
222
243
  ...state
223
244
  });
@@ -233,14 +254,14 @@ export class CodexAppServerRuntime {
233
254
  return { quality: "exact", continuations: Object.freeze(observed) };
234
255
  }
235
256
  }
236
- /** thread/closed means the loaded Activation ended; the durable thread remains resumable. */
257
+ /** thread/closed unloads the client attachment; the durable thread remains resumable. */
237
258
  export function codexNotificationBoundary(input) {
238
259
  const conversationId = optionalId(input.params.threadId)
239
260
  ?? optionalId(objectMember(input.params, "thread")?.id);
240
261
  const turnId = optionalId(input.params.turnId)
241
262
  ?? optionalId(objectMember(input.params, "turn")?.id);
242
263
  if (input.method === "thread/closed")
243
- return { kind: "activation-ended", conversationId };
264
+ return { kind: "attachment-closed", conversationId };
244
265
  if (input.method === "thread/goal/updated")
245
266
  return { kind: "goal-updated", conversationId, turnId };
246
267
  if (input.method === "thread/goal/cleared")
@@ -481,30 +502,17 @@ function optionalTurnStatus(value) {
481
502
  }
482
503
  function classifyMutationError(error) {
483
504
  if (error instanceof CodexAppServerRequestError) {
484
- if (codexAppServerErrorIsBusy(error)) {
485
- return { status: "busy", reason: error.message };
486
- }
487
505
  if (["INVALID_PARAMS", "NOT_FOUND", "TURN_NOT_ACTIVE", -32602].includes(error.code)) {
488
506
  return { status: "not-accepted", reason: error.message };
489
507
  }
490
508
  }
491
509
  return { status: "unknown", reason: error instanceof Error ? error.message : String(error) };
492
510
  }
493
- function codexAppServerErrorIsBusy(error) {
494
- return /\b(active turn|turn (?:is )?(?:already )?(?:active|in progress|running)|already has an active)\b/iu
495
- .test(error.message);
496
- }
497
511
  function isNotLoaded(error) {
498
512
  return error instanceof CodexAppServerRequestError
499
513
  && (String(error.code).toLowerCase().includes("not_loaded")
500
514
  || error.message.toLowerCase().includes("not loaded"));
501
515
  }
502
- function codexAppServerErrorIsUnmaterialized(error) {
503
- if (!(error instanceof CodexAppServerRequestError) || Number(error.code) !== -32600) {
504
- return false;
505
- }
506
- return /\bthread\b.*\bnot materialized yet\b.*\bbefore first user message\b/iu.test(error.message);
507
- }
508
516
  export function codexAppServerErrorIsMissing(error) {
509
517
  if (!(error instanceof CodexAppServerRequestError))
510
518
  return false;
@@ -7,7 +7,7 @@ import { openCodexInteractiveConnection } from "./structuredProviderHost.js";
7
7
  /**
8
8
  * Keep the native TUI and its transparent App Server attachment in one pane.
9
9
  * The existing Host acknowledgement carries the ID from that TUI's exact
10
- * thread/start or thread/resume response, before any user/model Turn.
10
+ * thread/start or thread/resume response, before any user/model AgentRun.
11
11
  *
12
12
  * Codex 0.150.1 cannot resume a pre-created empty Thread: no rollout exists
13
13
  * until its first message. Observing the TUI's own startup avoids creating a
@@ -29,8 +29,7 @@ export async function runCodexInteractiveHost(home, payload) {
29
29
  }
30
30
  const expectedId = environment.YUI_NATIVE_SESSION_ID;
31
31
  let snapshot = {
32
- schemaVersion: 2, state: "starting", adapterId: "codex",
33
- runtimeGenerationId: payload.runtimeGenerationId, updatedAt: new Date().toISOString()
32
+ schemaVersion: 2, state: "starting", adapterId: "codex", updatedAt: new Date().toISOString()
34
33
  };
35
34
  const control = await openAgentHostControl(home, payload, () => snapshot, async (request) => {
36
35
  if (request.type !== "status") {
@@ -59,8 +58,8 @@ export async function runCodexInteractiveHost(home, payload) {
59
58
  stop();
60
59
  };
61
60
  try {
62
- // This proxy is a disposable client, not the daemon. No Yui initialization
63
- // or Thread request is injected; all requests below belong to the TUI.
61
+ // This proxy is a disposable client, not the daemon. All requests belong
62
+ // to the TUI; its startup carries the reserved launch's per-Thread options.
64
63
  connection = await openCodexInteractiveConnection({
65
64
  command: payload.command, args: [...baseArgs, "app-server", "proxy"],
66
65
  environment, cwd: payload.cwd
@@ -105,6 +104,7 @@ export async function runCodexInteractiveHost(home, payload) {
105
104
  throw new Error("Codex TUI startup does not match the reserved Session.");
106
105
  }
107
106
  startupRequestId = message.id;
107
+ message.params = codexInteractiveStartupParameters(payload, params);
108
108
  }
109
109
  void connection.send(message).catch(fail);
110
110
  }
@@ -159,7 +159,7 @@ export async function runCodexInteractiveHost(home, payload) {
159
159
  args.splice(remoteIndex + 2, 0, "--remote-auth-token-env", "YUI_CODEX_REMOTE_AUTH_TOKEN");
160
160
  child = spawn(payload.command, args, {
161
161
  cwd: payload.cwd,
162
- env: { ...environment, YUI_CODEX_REMOTE_AUTH_TOKEN: token },
162
+ env: { ...localCodexTuiEnvironment(environment), YUI_CODEX_REMOTE_AUTH_TOKEN: token },
163
163
  stdio: "inherit"
164
164
  });
165
165
  for (const signal of signals)
@@ -183,6 +183,41 @@ export async function runCodexInteractiveHost(home, payload) {
183
183
  await control.close();
184
184
  }
185
185
  }
186
+ /** The remote TUI can omit local launch configuration. Apply the reserved
187
+ * workspace and managed identity on its actual startup, never by starting a
188
+ * second Thread or changing daemon-wide configuration. */
189
+ export function codexInteractiveStartupParameters(payload, params) {
190
+ const options = payload.interactiveCodexThread;
191
+ if (options === undefined)
192
+ throw new Error("Global Codex launch is missing its per-Thread options.");
193
+ const config = params.config === undefined || params.config === null ? {} : jsonObject(params.config);
194
+ const shell = config.shell_environment_policy === undefined ? {} : jsonObject(config.shell_environment_policy);
195
+ const set = shell.set === undefined ? {} : jsonObject(shell.set);
196
+ return {
197
+ ...params,
198
+ ...options,
199
+ cwd: payload.cwd,
200
+ config: {
201
+ ...config,
202
+ ...options.config,
203
+ shell_environment_policy: {
204
+ ...shell,
205
+ set: {
206
+ ...set,
207
+ ...Object.fromEntries(Object.entries(payload.environment).filter(([name]) => name.startsWith("YUI_")))
208
+ }
209
+ }
210
+ }
211
+ };
212
+ }
213
+ /** Our loopback relay must stay local, even when the TUI inherits HTTP proxies. */
214
+ export function localCodexTuiEnvironment(environment) {
215
+ const bypass = [
216
+ "127.0.0.1", "localhost", "::1",
217
+ environment.NO_PROXY, environment.no_proxy
218
+ ].filter(Boolean).join(",");
219
+ return { ...environment, NO_PROXY: bypass, no_proxy: bypass };
220
+ }
186
221
  function jsonObject(value) {
187
222
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
188
223
  throw new Error("Invalid Codex App Server object.");
@@ -11,7 +11,7 @@ export function foldContinuationObservation(existing, raw) {
11
11
  const base = existing ?? createProviderContinuation({
12
12
  taskId: observation.fence.taskId,
13
13
  roleName: observation.fence.roleName,
14
- turnId: observation.fence.turnId,
14
+ runId: observation.fence.runId,
15
15
  identity,
16
16
  ...(observation.fence.parentContinuationId === undefined
17
17
  ? {}
@@ -116,17 +116,13 @@ export function applyContinuationObservationAtomically(store, raw, factRef) {
116
116
  }
117
117
  function continuationIdentity(observation) {
118
118
  const fence = observation.fence;
119
- if (fence.taskId === undefined || fence.turnId === undefined
120
- || fence.conversationId === undefined || fence.activationId === undefined
121
- || fence.continuationId === undefined) {
119
+ if (fence.taskId === undefined || fence.runId === undefined || fence.conversationId === undefined || fence.continuationId === undefined) {
122
120
  throw new Error("Continuation observation fence is incomplete.");
123
121
  }
124
122
  return {
125
123
  providerNamespace: fence.driverId,
126
124
  accountScope: fence.agentId,
127
125
  conversationId: fence.conversationId,
128
- activationId: fence.activationId,
129
126
  continuationId: fence.continuationId,
130
- generation: fence.continuationGeneration
131
127
  };
132
128
  }
@@ -0,0 +1,30 @@
1
+ import { isDeepStrictEqual } from "node:util";
2
+ import { openCurrentTaskStore } from "../storage/currentTaskStore.js";
3
+ import { createProjectResources } from "../resources/projectResourceService.js";
4
+ /** Revalidate the adopted owner; a saved path alone never authorizes execution. */
5
+ export function assertExecutionEnvironmentCurrent(store, taskId, snapshot) {
6
+ if (snapshot.taskId !== taskId)
7
+ throw new Error("Execution environment belongs to another Task.");
8
+ const current = createProjectResources(store).resolveExecutionEnvironment(taskId, snapshot.preparationId);
9
+ if (!isDeepStrictEqual(current, snapshot)) {
10
+ throw new Error("Execution environment changed; select an adopted environment and start a new Session.");
11
+ }
12
+ }
13
+ /** Last local check before native process creation or another Provider input. */
14
+ export function assertAgentExecutionEnvironment(home, payload) {
15
+ const snapshot = payload.executionEnvironment;
16
+ if (snapshot === undefined)
17
+ return;
18
+ if (payload.environment.YUI_SESSION_SCOPE !== "task"
19
+ || payload.environment.YUI_TASK_ID !== snapshot.taskId
20
+ || payload.cwd !== snapshot.directory.path) {
21
+ throw new Error("Agent launch does not match its adopted execution environment.");
22
+ }
23
+ const store = openCurrentTaskStore(home);
24
+ try {
25
+ assertExecutionEnvironmentCurrent(store, snapshot.taskId, snapshot);
26
+ }
27
+ finally {
28
+ store.close();
29
+ }
30
+ }
@@ -4,15 +4,15 @@ export function projectFirstProgressAdvisory(input) {
4
4
  : [...(input.sessions.history ?? []), ...Object.values(input.sessions.sessions)]
5
5
  .sort((left, right) => left.createdAt.localeCompare(right.createdAt));
6
6
  const unique = [...new Map(sessions.map((session) => [
7
- `${session.nativeSessionId}\0${session.runtimeGenerationId ?? ""}`,
7
+ session.nativeSessionId,
8
8
  session
9
9
  ])).values()];
10
- const firstGenerationAt = unique[0]?.createdAt;
11
- const progress = firstGenerationAt === undefined
10
+ const firstSessionAt = unique[0]?.createdAt;
11
+ const progress = firstSessionAt === undefined
12
12
  ? []
13
13
  : [
14
14
  ...input.events
15
- .filter((event) => typeof event.payload.leaderTurnId === "string")
15
+ .filter((event) => typeof event.payload.leaderRunId === "string")
16
16
  .map((event) => ({ at: event.createdAt, ref: `event:${event.id}` })),
17
17
  ...input.workItems
18
18
  .filter((item) => item.status !== "retired")
@@ -20,21 +20,21 @@ export function projectFirstProgressAdvisory(input) {
20
20
  ...input.reviewRounds.map((round) => ({ at: round.createdAt, ref: `review-round:${round.id}` })),
21
21
  ...input.integrations.map((attempt) => ({ at: attempt.createdAt, ref: `integration-attempt:${attempt.id}` }))
22
22
  ]
23
- .filter(({ at }) => at >= firstGenerationAt)
23
+ .filter(({ at }) => at >= firstSessionAt)
24
24
  .sort((left, right) => left.at.localeCompare(right.at) || left.ref.localeCompare(right.ref));
25
25
  const firstProgressAt = progress[0]?.at;
26
- const generationsBeforeFirstProgress = unique.filter((session) => (firstProgressAt === undefined || session.createdAt <= firstProgressAt)).length;
26
+ const sessionsBeforeFirstProgress = unique.filter((session) => (firstProgressAt === undefined || session.createdAt <= firstProgressAt)).length;
27
27
  const attentionRecommended = firstProgressAt === undefined
28
- && generationsBeforeFirstProgress >= 2;
28
+ && sessionsBeforeFirstProgress >= 2;
29
29
  return Object.freeze({
30
30
  attentionRecommended,
31
- generationsBeforeFirstProgress,
32
- ...(firstGenerationAt === undefined ? {} : { firstGenerationAt }),
31
+ sessionsBeforeFirstProgress,
32
+ ...(firstSessionAt === undefined ? {} : { firstSessionAt }),
33
33
  ...(firstProgressAt === undefined ? {} : { firstProgressAt }),
34
34
  reason: attentionRecommended
35
- ? `${generationsBeforeFirstProgress} fresh Leader generations produced no first durable progress; Operator attention may be useful before another generation.`
35
+ ? `${sessionsBeforeFirstProgress} fresh Leader Sessions produced no first durable progress; Operator attention may be useful before another Session.`
36
36
  : firstProgressAt !== undefined
37
37
  ? `First durable progress was recorded at ${firstProgressAt}.`
38
- : "Fewer than two Leader generations exist before first durable progress."
38
+ : "Fewer than two Leader Sessions exist before first durable progress."
39
39
  });
40
40
  }
@@ -5,16 +5,17 @@ export { createRuntimeBinding } from "./runtimeBinding.js";
5
5
  export { normalizeRuntimeOwner } from "./runtimeOwner.js";
6
6
  export { createSessionLaunchRequest } from "./sessionLaunchRequest.js";
7
7
  export { DEFAULT_RECENT_TURN_ID_LIMIT, hasRecentTurnId, rememberRecentTurnId, validateRecentTurnIds } from "./recentTurnIds.js";
8
- export { promptPushOutcome, RuntimeGenerationMismatchError, RuntimeHostContentionError, RuntimeHostUnavailableError, RuntimeLaunchError } from "./ports.js";
8
+ export { promptPushOutcome, RuntimeHostContentionError, RuntimeHostUnavailableError, RuntimeLaunchError } from "./ports.js";
9
9
  export { AgentHostPromptPushAdapter, TmuxSessionHost } from "./tmuxAdapters.js";
10
10
  export { FileTaskRuntimeIsolation, YUI_TASK_RUNTIME_ISOLATION_DESCRIPTOR, YUI_TASK_RUNTIME_SERVICE_NAMESPACE, assertTaskRuntimeIsolationPreflight, createTaskRuntimeIsolationDescriptor, parseTaskRuntimeIsolationDescriptor, planTaskRuntimeCleanup, taskRuntimeIsolationEnvironment, taskRuntimeIsolationFingerprint } from "./taskRuntimeIsolation.js";
11
- export { createSessionOwnerIdentity, discoverProviderRootByLaunchEnv, isLinuxProcessLive, listLaunchFencedProcesses, listOwnedProcessTree, readLinuxProcessIdentity } from "./sessionOwnerIdentity.js";
11
+ export { createSessionOwnerIdentity, isLinuxProcessLive, listOwnedProcessTree, readLinuxProcessIdentity } from "./sessionOwnerIdentity.js";
12
12
  export { FileSessionOwnerRegistry } from "./sessionOwnerRegistry.js";
13
13
  export { formatRuntimeLaunchDiagnostic, redactLaunchArgument, redactLaunchText, RuntimeLaunchFailure, toRuntimeLaunchFailure } from "./launchDiagnostics.js";
14
14
  export { DEFAULT_FORCED_GRACE_MS, DEFAULT_GRACEFUL_GRACE_MS, terminateSessionOwners } from "./sessionTerminationGuard.js";
15
15
  export { ProviderContinuationReconciliationService } from "./providerContinuationReconciliationService.js";
16
16
  export { codexNotificationBoundary, codexAppServerErrorIsMissing, CodexAppServerRequestError, CodexAppServerRuntime } from "./codexAppServerRuntime.js";
17
- export { createProviderRuntimeBinding, acceptProviderTurn, beginProviderTurn, currentProviderActivation, currentProviderAuthority, currentProviderConversation, endProviderActivation, markProviderTurnDeliveryUnknown, rejectProviderTurn, settleProviderTurnSubmission, settleProviderTurn, managedProviderTurnId, startProviderActivation, supersedeProviderConversation, transferProviderAuthority, updateProviderConversationRecoverability, validateProviderRuntimeBinding } from "./providerRuntimeIdentity.js";
17
+ export { createProviderRuntimeBinding, acceptProviderTurn, beginProviderTurn, currentProviderAuthority, currentProviderConversation, markProviderTurnDeliveryUnknown, rejectProviderTurn, settleProviderTurnSubmission, settleProviderTurn, managedProviderTurnId, supersedeProviderConversation, transferProviderAuthority, updateProviderConversationRecoverability, validateProviderRuntimeBinding } from "./providerRuntimeIdentity.js";
18
18
  export { FencedProviderControl } from "./providerControl.js";
19
19
  export { sameProviderAuthorityFence, validateProviderAuthorityFence } from "./providerAuthorityFence.js";
20
20
  export { reconcileSessionOwners } from "./sessionReconciliation.js";
21
+ export { createAgentEndpointFactory, builtinAgentEndpointImplementation } from "./agentEndpoint.js";
@@ -0,0 +1,109 @@
1
+ export const PROVIDER_MESSAGE_MAX_BYTES = 16 * 1024 * 1024;
2
+ /**
3
+ * Newline-delimited JSON over a child process's stdio. This is a plain shared
4
+ * transport: it carries whole JSON objects and never interprets them, so any
5
+ * line-framed Provider protocol can sit on top of it.
6
+ */
7
+ export class JsonLineChannel {
8
+ child;
9
+ mirror;
10
+ #listeners = new Set();
11
+ #closeListeners = new Set();
12
+ #buffer = "";
13
+ #closedError;
14
+ constructor(child, mirror) {
15
+ this.child = child;
16
+ this.mirror = mirror;
17
+ child.stdout.setEncoding("utf8");
18
+ child.stdout.on("data", (chunk) => this.#receive(chunk));
19
+ child.once("error", (error) => this.#close(error));
20
+ child.once("close", (code, signal) => this.#close(new Error(`Provider process exited (code=${code ?? "none"}, signal=${signal ?? "none"}).`)));
21
+ }
22
+ /** The transport's terminal failure, once it has one. */
23
+ get closedError() {
24
+ return this.#closedError;
25
+ }
26
+ onMessage(listener) {
27
+ this.#listeners.add(listener);
28
+ return () => this.#listeners.delete(listener);
29
+ }
30
+ /**
31
+ * Observe transport closure. A listener registered after the channel already
32
+ * closed is called immediately, so a caller cannot lose the loss of the pipe
33
+ * to a registration race and then wait forever for a reply that cannot come.
34
+ */
35
+ onClose(listener) {
36
+ if (this.#closedError !== undefined) {
37
+ listener(this.#closedError);
38
+ return () => { };
39
+ }
40
+ this.#closeListeners.add(listener);
41
+ return () => this.#closeListeners.delete(listener);
42
+ }
43
+ async send(message) {
44
+ if (this.#closedError !== undefined)
45
+ throw this.#closedError;
46
+ const line = `${JSON.stringify(message)}\n`;
47
+ if (Buffer.byteLength(line, "utf8") > PROVIDER_MESSAGE_MAX_BYTES) {
48
+ throw new Error("Provider request exceeds its message bound.");
49
+ }
50
+ await new Promise((resolvePromise, reject) => {
51
+ this.child.stdin.write(line, "utf8", (error) => {
52
+ if (error === null || error === undefined)
53
+ resolvePromise();
54
+ else
55
+ reject(error);
56
+ });
57
+ });
58
+ }
59
+ #receive(chunk) {
60
+ this.mirror("stdout", chunk);
61
+ this.#buffer += chunk;
62
+ if (Buffer.byteLength(this.#buffer, "utf8") > PROVIDER_MESSAGE_MAX_BYTES) {
63
+ this.#close(new Error("Provider response line exceeds its message bound."));
64
+ terminateProcessGroup(this.child, "SIGTERM");
65
+ return;
66
+ }
67
+ for (;;) {
68
+ const newline = this.#buffer.indexOf("\n");
69
+ if (newline < 0)
70
+ return;
71
+ const line = this.#buffer.slice(0, newline).trim();
72
+ this.#buffer = this.#buffer.slice(newline + 1);
73
+ if (line.length === 0)
74
+ continue;
75
+ try {
76
+ const parsed = JSON.parse(line);
77
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed))
78
+ continue;
79
+ for (const listener of this.#listeners)
80
+ listener(parsed);
81
+ }
82
+ catch {
83
+ continue;
84
+ }
85
+ }
86
+ }
87
+ #close(error) {
88
+ if (this.#closedError !== undefined)
89
+ return;
90
+ this.#closedError = error;
91
+ for (const listener of this.#closeListeners)
92
+ listener(error);
93
+ }
94
+ }
95
+ export function terminateProcessGroup(child, signal) {
96
+ if (child.pid === undefined)
97
+ return;
98
+ // Managed Providers are spawned detached and therefore own a process group.
99
+ // Kill that exact group so a CLI helper cannot outlive the Agent Host. The
100
+ // direct-child fallback covers embedded runtimes that cannot create setsid.
101
+ try {
102
+ process.kill(-child.pid, signal);
103
+ }
104
+ catch (error) {
105
+ if (error.code !== "ESRCH")
106
+ throw error;
107
+ child.kill(signal);
108
+ }
109
+ }
@@ -1,7 +1,13 @@
1
1
  import { randomBytes } from "node:crypto";
2
- import { resolve } from "node:path";
2
+ import { resolve, isAbsolute } from "node:path";
3
3
  import { validateProviderAuthorityFence } from "./providerAuthorityFence.js";
4
4
  import { AGENT_HOST_LAUNCH_TICKET_TTL_MS } from "./runtimeDeadlines.js";
5
+ import { validateAgentEndpointImplementation } from "./agentEndpointIdentity.js";
6
+ import { validateExecutionEnvironmentSnapshot } from "../resources/projectResource.js";
7
+ import { isAgentAdapterId } from "../agent/adapterCatalog.js";
8
+ import { adapterIdForExecutionComponent, isAgentExecutionComponentId } from "../agent/executionComponents.js";
9
+ import { agentTransportForAdapter } from "../agent/connectionPlan.js";
10
+ import { resolveAgentAdapter } from "../executor/agentAdapter.js";
5
11
  const brokers = new Map();
6
12
  /** One Controller-process broker per canonical Home. Payloads never hit disk or tmux. */
7
13
  export function launchBrokerForHome(home) {
@@ -17,30 +23,27 @@ export class LaunchBroker {
17
23
  #reservations = new Map();
18
24
  reserve(payload) {
19
25
  validatePayload(payload);
20
- if (this.#reservations.has(payload.runtimeGenerationId)) {
21
- throw new Error(`Launch payload is already reserved: ${payload.runtimeGenerationId}.`);
22
- }
23
26
  const ticket = randomBytes(32).toString("hex");
24
- this.#reservations.set(payload.runtimeGenerationId, Object.freeze({
27
+ this.#reservations.set(ticket, Object.freeze({
25
28
  ticket,
26
29
  payload,
27
30
  createdAt: Date.now()
28
31
  }));
29
- return Object.freeze({ runtimeGenerationId: payload.runtimeGenerationId, ticket });
32
+ return Object.freeze({ ticket });
30
33
  }
31
- redeem(runtimeGenerationId, ticket) {
32
- const reservation = this.#reservations.get(runtimeGenerationId);
34
+ redeem(ticket) {
35
+ const reservation = this.#reservations.get(ticket);
33
36
  if (reservation === undefined || reservation.ticket !== ticket) {
34
37
  throw new Error("Launch ticket is invalid or already consumed.");
35
38
  }
36
- this.#reservations.delete(runtimeGenerationId);
39
+ this.#reservations.delete(ticket);
37
40
  if (Date.now() - reservation.createdAt > AGENT_HOST_LAUNCH_TICKET_TTL_MS) {
38
41
  throw new Error("Launch ticket expired before redemption.");
39
42
  }
40
43
  return reservation.payload;
41
44
  }
42
- revoke(runtimeGenerationId) {
43
- this.#reservations.delete(runtimeGenerationId);
45
+ revoke(ticket) {
46
+ this.#reservations.delete(ticket);
44
47
  }
45
48
  pendingCount() {
46
49
  return this.#reservations.size;
@@ -55,7 +58,6 @@ export function validateAgentHostLaunchPayload(value) {
55
58
  function validatePayload(payload) {
56
59
  if (payload.schemaVersion !== 2)
57
60
  throw new Error("Agent Host launch payload version is invalid.");
58
- text(payload.runtimeGenerationId, "runtimeGenerationId");
59
61
  text(payload.command, "command");
60
62
  text(payload.cwd, "cwd");
61
63
  if (!Array.isArray(payload.args))
@@ -70,6 +72,14 @@ function validatePayload(payload) {
70
72
  throw new Error("Agent Host launch environment value is invalid.");
71
73
  }
72
74
  }
75
+ if (payload.executionEnvironment !== undefined) {
76
+ const adopted = validateExecutionEnvironmentSnapshot(payload.executionEnvironment);
77
+ if (payload.environment.YUI_SESSION_SCOPE !== "task"
78
+ || payload.environment.YUI_TASK_ID !== adopted.taskId
79
+ || payload.cwd !== adopted.directory.path) {
80
+ throw new Error("Agent Host execution environment does not match its Task and cwd.");
81
+ }
82
+ }
73
83
  if (payload.childLifecycle !== "persistent" && payload.childLifecycle !== "per-turn") {
74
84
  throw new Error("Agent Host child lifecycle is invalid.");
75
85
  }
@@ -83,18 +93,37 @@ function validatePayload(payload) {
83
93
  function validateProviderControl(control) {
84
94
  if (control.schemaVersion !== 1)
85
95
  throw new Error("Agent Host Provider control version is invalid.");
86
- if (control.adapterId !== "codex" && control.adapterId !== "claude") {
96
+ if (control.endpointImplementation !== undefined)
97
+ validateAgentEndpointImplementation(control.endpointImplementation);
98
+ if (!isAgentAdapterId(control.adapterId)) {
87
99
  throw new Error("Agent Host Provider control adapter is invalid.");
88
100
  }
89
- if ((control.adapterId === "codex" && control.transport !== "codex-app-server-proxy")
90
- || (control.adapterId === "claude" && control.transport !== "claude-stream-json")) {
101
+ // The connection plan owns the protocol/transport pair. Re-deriving it here
102
+ // is what keeps a control from naming a carrier its adapter does not speak.
103
+ if (control.transport !== agentTransportForAdapter(control.adapterId)) {
91
104
  throw new Error("Agent Host Provider control transport does not match its adapter.");
92
105
  }
106
+ // The component determines its plan, so a control naming both must have them
107
+ // agree. Letting them drift would let a launch apply one product's
108
+ // configuration decisions to another product's Session.
109
+ if (control.component !== undefined) {
110
+ if (!isAgentExecutionComponentId(control.component)) {
111
+ throw new Error("Agent Host Provider control execution component is invalid.");
112
+ }
113
+ if (adapterIdForExecutionComponent(control.component) !== control.adapterId) {
114
+ throw new Error("Agent Host Provider control execution component does not match its adapter.");
115
+ }
116
+ }
93
117
  if ((control.adapterId === "codex") !== (control.codexThread !== undefined)) {
94
118
  throw new Error("Agent Host Provider thread settings do not match its adapter.");
95
119
  }
96
120
  if (control.codexThread !== undefined)
97
121
  validateCodexThreadOptions(control.codexThread);
122
+ if (control.adapterId !== "acp" && control.acpSession !== undefined) {
123
+ throw new Error("Agent Host ACP session settings do not match its adapter.");
124
+ }
125
+ if (control.acpSession !== undefined)
126
+ validateAcpSessionOptions(control.acpSession);
98
127
  if (control.mode !== "new" && control.mode !== "resume") {
99
128
  throw new Error("Agent Host Provider control mode is invalid.");
100
129
  }
@@ -114,7 +143,11 @@ function validateProviderControl(control) {
114
143
  text(control.ownedTurn.attemptId, "owned Provider input attemptId");
115
144
  text(control.ownedTurn.turnId, "owned Provider Turn id");
116
145
  }
117
- const requiresNativeSessionId = control.mode === "resume" || control.adapterId === "claude";
146
+ // Resume always needs the id being resumed. A new launch needs one only from
147
+ // an Agent that accepts a caller-chosen Session id; the others report theirs
148
+ // once they answer, so demanding it up front rejects a valid launch.
149
+ const requiresNativeSessionId = control.mode === "resume"
150
+ || resolveAgentAdapter(control.adapterId).capabilities.nativeSessionDiscovery === "preallocated";
118
151
  if (requiresNativeSessionId !== (control.nativeSessionId !== undefined)) {
119
152
  throw new Error("Agent Host Provider resume identity is inconsistent.");
120
153
  }
@@ -155,6 +188,48 @@ function validateCodexThreadOptions(options) {
155
188
  throw new Error("Agent Host Codex thread config is invalid.");
156
189
  }
157
190
  }
191
+ function validateAcpSessionOptions(options) {
192
+ if (options === null || typeof options !== "object" || Array.isArray(options)) {
193
+ throw new Error("Agent Host ACP session settings are invalid.");
194
+ }
195
+ if (options.additionalDirectories !== undefined) {
196
+ if (!Array.isArray(options.additionalDirectories)) {
197
+ throw new Error("Agent Host ACP additional workspace roots are invalid.");
198
+ }
199
+ // ACP requires each additional root to be absolute. Rejecting a relative
200
+ // path here keeps an invalid request from reaching the Agent at all.
201
+ for (const root of options.additionalDirectories) {
202
+ if (!isAbsolute(text(root, "ACP additional workspace root"))) {
203
+ throw new Error("Agent Host ACP additional workspace root must be absolute.");
204
+ }
205
+ }
206
+ }
207
+ if (options.sessionBootstrap !== undefined) {
208
+ text(options.sessionBootstrap, "ACP session bootstrap");
209
+ }
210
+ const desired = options.desiredConfiguration;
211
+ if (desired !== undefined) {
212
+ if (desired === null || typeof desired !== "object" || Array.isArray(desired)) {
213
+ throw new Error("Agent Host ACP session configuration is invalid.");
214
+ }
215
+ if (desired.model !== undefined)
216
+ text(desired.model, "ACP session model");
217
+ if (desired.effort !== undefined)
218
+ text(desired.effort, "ACP session effort");
219
+ if (desired.permissionMode !== undefined) {
220
+ text(desired.permissionMode, "ACP session permission mode");
221
+ }
222
+ if (desired.permissionBypass !== undefined && typeof desired.permissionBypass !== "boolean") {
223
+ throw new Error("Agent Host ACP session permission bypass is invalid.");
224
+ }
225
+ // Naming an exact mode and asking for bypass are two different requests, and
226
+ // a payload carrying both leaves it ambiguous which one the user made.
227
+ if (desired.permissionMode !== undefined && desired.permissionBypass === true) {
228
+ throw new Error("Agent Host ACP session configuration cannot request both a named permission "
229
+ + "mode and the bypass strategy.");
230
+ }
231
+ }
232
+ }
158
233
  function text(value, label) {
159
234
  if (typeof value !== "string" || value.length === 0 || value.includes("\0")) {
160
235
  throw new Error(`Agent Host ${label} is invalid.`);