@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
@@ -5,11 +5,24 @@ import { join } from "node:path";
5
5
  import { createInterface } from "node:readline";
6
6
  import { parse } from "smol-toml";
7
7
  import { configuredAgentLaunchEnvironment } from "../agent/launchEnvironment.js";
8
+ import { codexClientInitialization } from "../runtime/codexAppServerRuntime.js";
9
+ import { acpInitializeRequest, readAcpInitializeResult } from "../runtime/acpProtocol.js";
10
+ import { handshakeObservationFrom } from "../runtime/agentRunConfiguration.js";
8
11
  import { YUI_VERSION } from "../version.js";
12
+ /**
13
+ * Codex and Claude Code are configured by flags and their own files; neither
14
+ * plan exchanges capabilities on connect. Saying so explicitly keeps a caller
15
+ * from reading a missing handshake as an Agent that answered with nothing.
16
+ */
17
+ const NO_HANDSHAKE = Object.freeze({
18
+ status: "unsupported",
19
+ reason: "This connection plan has no capability handshake; support is static."
20
+ });
9
21
  const MAX_OUTPUT_BYTES = 1024 * 1024;
10
22
  const PROCESS_TERMINATION_GRACE_MS = 100;
11
23
  const CODEX_SANDBOXES = ["read-only", "workspace-write", "danger-full-access"];
12
24
  const CODEX_APPROVALS = ["untrusted", "on-request", "never"];
25
+ const ACP_PROBE_REQUEST_ID = 1;
13
26
  export async function discoverCodexConfiguration(input) {
14
27
  const environment = configuredAgentLaunchEnvironment(input.agent, input.environment);
15
28
  const profile = input.config?.adapterId === "codex" ? input.config.profile : undefined;
@@ -22,10 +35,8 @@ export async function discoverCodexConfiguration(input) {
22
35
  const [version, help] = await Promise.all([
23
36
  runTextProcess(input.agent.command, [...input.agent.baseArgs, "--version"], input.cwd, environment, input.signal),
24
37
  runTextProcess(input.agent.command, [...input.agent.baseArgs, "--help"], input.cwd, environment, input.signal),
25
- client.request("initialize", {
26
- clientInfo: { name: "yui", title: "Yui", version: YUI_VERSION },
27
- capabilities: { experimentalApi: true, requestAttestation: false }
28
- }).then(() => { client.notify("initialized", {}); })
38
+ client.request("initialize", codexClientInitialization())
39
+ .then(() => { client.notify("initialized", {}); })
29
40
  ]);
30
41
  const [models, requirementsResult, providerCapabilities] = await Promise.all([
31
42
  listCodexModels(client),
@@ -48,6 +59,7 @@ export async function discoverCodexConfiguration(input) {
48
59
  agentId: input.agent.id,
49
60
  adapterId: "codex",
50
61
  ...(semanticVersion(version) === undefined ? {} : { cliVersion: semanticVersion(version) }),
62
+ handshake: NO_HANDSHAKE,
51
63
  models,
52
64
  fields: [
53
65
  field("model", [], true),
@@ -101,6 +113,7 @@ export async function discoverClaudeConfiguration(input) {
101
113
  agentId: input.agent.id,
102
114
  adapterId: "claude",
103
115
  ...(semanticVersion(version) === undefined ? {} : { cliVersion: semanticVersion(version) }),
116
+ handshake: NO_HANDSHAKE,
104
117
  models,
105
118
  fields: [
106
119
  field("model", [], true),
@@ -259,6 +272,151 @@ async function requestClaudeInitialization(command, args, cwd, environment, sign
259
272
  })}\n`);
260
273
  });
261
274
  }
275
+ /**
276
+ * Discover an ACP Agent's capabilities from the protocol itself.
277
+ *
278
+ * `initialize` is the only method used: it is the one exchange ACP guarantees
279
+ * before any Session exists, and it requires no authentication, so discovery
280
+ * never touches a model or spends quota. Everything reported here is what the
281
+ * Agent actually advertised — no field is inferred from which product it is.
282
+ */
283
+ export async function discoverAcpConfiguration(input) {
284
+ const environment = configuredAgentLaunchEnvironment(input.agent, input.environment);
285
+ const negotiated = await requestAcpInitialization(input.agent.command, [...input.agent.baseArgs], input.cwd, environment, input.signal);
286
+ const warnings = [];
287
+ if (!negotiated.capabilities.loadSession) {
288
+ warnings.push("This ACP Agent does not support `session/load`, so a managed Yui Turn "
289
+ + "cannot survive a Provider restart.");
290
+ }
291
+ if (negotiated.authMethods.length > 0) {
292
+ // Authenticating is the operator's decision, made outside Yui with the
293
+ // product's own tooling. Reporting the requirement is the honest action.
294
+ warnings.push("This ACP Agent advertises authentication methods "
295
+ + `(${negotiated.authMethods.map((method) => method.id).join(", ")}); `
296
+ + "this handshake does not establish whether authentication is required "
297
+ + "or already satisfied.");
298
+ }
299
+ return {
300
+ schemaVersion: 1,
301
+ agentId: input.agent.id,
302
+ adapterId: "acp",
303
+ ...(negotiated.agentVersion === undefined
304
+ ? {}
305
+ : semanticVersion(negotiated.agentVersion) === undefined
306
+ ? {}
307
+ : { cliVersion: semanticVersion(negotiated.agentVersion) }),
308
+ // What this Agent actually agreed to, kept separate from what Yui's client
309
+ // statically supports. Projected by the same function a live Session uses,
310
+ // so this query and a Session inspect cannot describe one Agent's handshake
311
+ // differently. An Agent that reports no name stays `unknown` here and is
312
+ // never resolved into a product by its command line.
313
+ handshake: handshakeObservationFrom(negotiated),
314
+ // Deliberately empty, and not because ACP cannot select a model. ACP
315
+ // enumerates a Session's models in the `configOptions` returned by
316
+ // `session/new` — which means listing them requires creating a real Session
317
+ // on the Agent, and for a hosted product that is a billable remote effect
318
+ // triggered by what the user asked to be a capability query. So this probe
319
+ // stops at `initialize`: it reports that the axis is configurable and that
320
+ // its values are negotiated per Session, rather than opening a Session to
321
+ // populate a menu. The values are checked where they are actually known, at
322
+ // launch, against the option list that Session returns.
323
+ models: [],
324
+ fields: [
325
+ field("model", [], true, true, "ACP selects a model with session/set_config_option. The values one Agent "
326
+ + "accepts are enumerated per Session at session/new, so Yui does not list "
327
+ + "them here: creating a Session to populate the list would be a real, "
328
+ + "possibly billed remote effect for what is only a capability query. A "
329
+ + "configured model is checked against the Agent's own list at launch and "
330
+ + "the launch fails if it is not offered."),
331
+ field("effort", [], true, true, "ACP selects reasoning effort with session/set_config_option, under the "
332
+ + "`thought_level` category. As with the model, the accepted values are "
333
+ + "negotiated per Session and verified at launch rather than listed here."),
334
+ field("permission.strategy", [choice("default"), choice("bypass"), choice("configured")], false, true, "`default` sends no mode, so the Agent's own default stands. `configured` "
335
+ + "selects one exact mode the Agent offers. `bypass` applies the mode that "
336
+ + "grants unattended action, and only for an execution component Yui can "
337
+ + "identify — it is never guessed from a mode's name. None of these change "
338
+ + "how Yui answers session/request_permission: this client holds no "
339
+ + "interactive consent and always declines."),
340
+ field("permission.mode", [], true, true, "The mode ids belong to the Agent and are enumerated per Session, so Yui "
341
+ + "verifies a configured mode against that list at launch instead of "
342
+ + "listing candidates here."),
343
+ field("additionalDirectories", [], true, negotiated.capabilities.additionalDirectories, negotiated.capabilities.additionalDirectories
344
+ ? undefined
345
+ : "This ACP Agent does not advertise "
346
+ + "`sessionCapabilities.additionalDirectories`, so extra workspace roots "
347
+ + "cannot be sent to it and only the launch `cwd` is in scope.")
348
+ ],
349
+ warnings
350
+ };
351
+ }
352
+ /**
353
+ * Run one `initialize` exchange and stop. The Agent is spawned, asked what it
354
+ * supports, and terminated without creating a Session.
355
+ */
356
+ async function requestAcpInitialization(command, args, cwd, environment, signal) {
357
+ const child = spawn(command, args, { cwd, env: environment, stdio: ["pipe", "pipe", "pipe"] });
358
+ return new Promise((resolvePromise, reject) => {
359
+ let settled = false;
360
+ let bytes = 0;
361
+ const output = createInterface({ input: child.stdout });
362
+ const finish = (error, value) => {
363
+ if (settled)
364
+ return;
365
+ settled = true;
366
+ output.close();
367
+ terminateProcess(child);
368
+ signal.removeEventListener("abort", abort);
369
+ if (error !== undefined)
370
+ reject(error);
371
+ else
372
+ resolvePromise(value);
373
+ };
374
+ const abort = () => finish(abortError());
375
+ signal.addEventListener("abort", abort, { once: true });
376
+ child.on("error", (error) => finish(error));
377
+ child.on("exit", (code, exitSignal) => {
378
+ if (!settled)
379
+ finish(new Error(`ACP configuration probe exited before initialize (${code ?? exitSignal ?? "unknown"}).`));
380
+ });
381
+ output.on("line", (line) => {
382
+ bytes += Buffer.byteLength(line);
383
+ if (bytes > MAX_OUTPUT_BYTES) {
384
+ finish(new Error("ACP configuration probe exceeded the output limit."));
385
+ return;
386
+ }
387
+ let message;
388
+ try {
389
+ message = JSON.parse(line);
390
+ }
391
+ catch {
392
+ return;
393
+ }
394
+ const envelope = object(message);
395
+ if (envelope === undefined || envelope.id !== ACP_PROBE_REQUEST_ID)
396
+ return;
397
+ const failure = object(envelope.error);
398
+ if (failure !== undefined) {
399
+ finish(new Error(typeof failure.message === "string"
400
+ ? `ACP initialize failed: ${failure.message}`
401
+ : "ACP initialize failed."));
402
+ return;
403
+ }
404
+ try {
405
+ finish(undefined, readAcpInitializeResult(envelope.result));
406
+ }
407
+ catch (error) {
408
+ finish(error instanceof Error ? error : new Error(String(error)));
409
+ }
410
+ });
411
+ child.stdin.on("error", (error) => finish(error));
412
+ child.stdin.end(`${JSON.stringify({
413
+ jsonrpc: "2.0",
414
+ id: ACP_PROBE_REQUEST_ID,
415
+ method: "initialize",
416
+ params: acpInitializeRequest(YUI_VERSION)
417
+ })}\n`);
418
+ });
419
+ }
262
420
  async function runTextProcess(command, args, cwd, environment, signal) {
263
421
  const child = spawn(command, args, {
264
422
  cwd,
@@ -1,8 +1,30 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { hasRecentTurnId, rememberRecentTurnId, validateRecentTurnIds } from "../runtime/recentTurnIds.js";
3
3
  import { roleSessionMayContinue, validateEffectiveLaunchSnapshot } from "./effectiveLaunch.js";
4
- import { currentProviderActivation, endProviderActivation, validateProviderRuntimeBinding } from "../runtime/providerRuntimeIdentity.js";
5
- import { builtinDriverIdForAdapter } from "../runtime/builtinAgentDrivers.js";
4
+ import { validateProviderRuntimeBinding, currentProviderConversation } from "../runtime/providerRuntimeIdentity.js";
5
+ import { builtinDriverIdForAdapter, builtinAgentDriverRegistry } from "../runtime/builtinAgentDrivers.js";
6
+ import { builtinAgentEndpointImplementation, validateAgentEndpointImplementation } from "../runtime/agentEndpointIdentity.js";
7
+ /** Recovery addresses retained execution identity, not a live Session grant.
8
+ * A released Session cache can be absent while its native binding still needs
9
+ * to be stopped. No active Session record is fabricated by this projection. */
10
+ export function taskRoleControlTarget(set) {
11
+ if (set == null)
12
+ return undefined;
13
+ const current = set.sessions[set.activeAgentId];
14
+ const binding = set.providerBinding;
15
+ const fromBinding = binding !== null && (current === undefined
16
+ || (binding.run !== null && ["submitting", "accepted", "delivery-unknown"].includes(binding.run.status))
17
+ || (binding.goal !== null && binding.goal.status !== "complete"));
18
+ const nativeSessionId = fromBinding ? currentProviderConversation(binding).conversationId : current?.nativeSessionId;
19
+ const agentId = fromBinding ? binding.accountScope : current?.agentId;
20
+ if (nativeSessionId === undefined || agentId === undefined)
21
+ return undefined;
22
+ const session = [...Object.values(set.sessions), ...(set.history ?? [])]
23
+ .find(entry => entry.nativeSessionId === nativeSessionId && entry.agentId === agentId);
24
+ const adapterId = session?.adapterId ?? (binding === null ? undefined : builtinAgentDriverRegistry().find(binding.providerNamespace)?.adapterId);
25
+ return { nativeSessionId, agentId, adapterId, status: session?.status,
26
+ updatedAt: session?.updatedAt ?? set.updatedAt, fromBinding };
27
+ }
6
28
  export function createRoleSessionSet(owner, activeAgentId, now) {
7
29
  const base = {
8
30
  owner: normalizeOwner(owner),
@@ -45,8 +67,7 @@ export function recordRoleAgentSession(set, input, now) {
45
67
  const adapterId = requireText(input.adapterId, "Agent adapter id");
46
68
  const nativeSessionId = requireText(input.nativeSessionId, "Native session id");
47
69
  if (set.owner.scope === "task") {
48
- const historical = (set.history ?? []).find((entry) => (entry.nativeSessionId === nativeSessionId
49
- || (input.runtimeGenerationId !== undefined && entry.runtimeGenerationId === input.runtimeGenerationId)));
70
+ const historical = (set.history ?? []).find((entry) => (entry.nativeSessionId === nativeSessionId));
50
71
  if (historical !== undefined) {
51
72
  throw new Error("A new Task Role Session cannot reuse a historical native identity.");
52
73
  }
@@ -68,7 +89,7 @@ export function recordRoleAgentSession(set, input, now) {
68
89
  if (existing !== undefined && existing.nativeSessionId === nativeSessionId
69
90
  && !roleSessionMayContinue(existing.effective, effective)) {
70
91
  throw new Error(`Role Agent session cannot continue under this launch: ${agentId}. `
71
- + "Its Agent, adapter or physical workspace changed.");
92
+ + "Its Agent, execution component, connection plan or physical workspace changed.");
72
93
  }
73
94
  if (existing !== undefined && existing.nativeSessionId !== nativeSessionId
74
95
  && existing.status === "active") {
@@ -76,18 +97,25 @@ export function recordRoleAgentSession(set, input, now) {
76
97
  }
77
98
  const timestamp = now.toISOString();
78
99
  const continuing = existing?.nativeSessionId === nativeSessionId ? existing : undefined;
100
+ if (continuing !== undefined && input.endpointImplementation !== undefined
101
+ && (continuing.endpointImplementation.id !== input.endpointImplementation.id
102
+ || continuing.endpointImplementation.generation !== input.endpointImplementation.generation)) {
103
+ throw new Error("A native Session cannot change its Endpoint implementation in place.");
104
+ }
79
105
  const session = {
80
- schemaVersion: 5,
106
+ schemaVersion: 6,
81
107
  agentId,
82
108
  adapterId,
83
109
  nativeSessionId,
84
- ...(input.runtimeGenerationId === undefined
85
- ? continuing?.runtimeGenerationId === undefined ? {} : { runtimeGenerationId: continuing.runtimeGenerationId }
86
- : { runtimeGenerationId: requireText(input.runtimeGenerationId, "Runtime generation id") }),
87
110
  ...optionalSessionText("title", input.title ?? continuing?.title),
88
111
  ...optionalSessionText("preview", input.preview ?? continuing?.preview),
89
112
  policy: input.policy,
90
113
  effective: continuing?.effective ?? effective,
114
+ endpointImplementation: {
115
+ ...(continuing?.endpointImplementation
116
+ ?? input.endpointImplementation
117
+ ?? builtinAgentEndpointImplementation(adapterId))
118
+ },
91
119
  status: input.status,
92
120
  ...(input.status === "ended"
93
121
  ? { endReason: input.endReason ?? continuing?.endReason ?? "stopped" }
@@ -192,38 +220,34 @@ export function retireTaskRoleSessionsForWorkspace(set, now) {
192
220
  updatedAt: timestamp
193
221
  });
194
222
  }
195
- /**
196
- * Terminalizes only the aggregate-16 Claude placeholder shape after the
197
- * caller has fenced the Task store and proved that the exact Role has no live
198
- * native pane. All other nonterminal durable Sessions remain blockers for the
199
- * ordinary workspace-retirement path above.
200
- */
201
- export function retireConfirmedAbsentInactiveTaskRolePlaceholders(set, now) {
223
+ /** Explicit fresh-session selection after physical stop. Keep every original
224
+ * Run/result and the old native identity as history; never invent a new ID. */
225
+ export function selectNewTaskRoleSession(set, agentId, now) {
202
226
  validateRoleSessionSet(set);
203
- const timestamp = now.toISOString();
204
- let changed = false;
205
- const sessions = Object.fromEntries(Object.entries(set.sessions).map(([agentId, session]) => {
206
- const isNeverStartedInactiveClaude = agentId !== set.activeAgentId
207
- && session.adapterId === "claude"
208
- && session.status === "active"
209
- && session.runtimeGenerationId === undefined
210
- && session.recentCompletedTurnIds.length === 0;
211
- if (!isNeverStartedInactiveClaude)
212
- return [agentId, session];
213
- changed = true;
214
- return [agentId, {
215
- ...session,
216
- status: "ended",
217
- endReason: "failed",
218
- updatedAt: timestamp
219
- }];
220
- }));
221
- if (!changed)
222
- return set;
227
+ const session = set.sessions[requireSafeIdentity(agentId, "Agent id")];
228
+ if (session !== undefined && session.status !== "ended") {
229
+ throw new Error("Stop the current Session before selecting a new one.");
230
+ }
231
+ if (set.activeAgentId === agentId && set.providerBinding !== null) {
232
+ if (set.providerBinding.run !== null
233
+ && ["submitting", "accepted", "delivery-unknown"].includes(set.providerBinding.run.status)
234
+ || set.providerBinding.goal !== null && set.providerBinding.goal.status !== "complete") {
235
+ throw new Error("Settle the exact native input and Goal before selecting a new Session.");
236
+ }
237
+ }
238
+ if (session === undefined)
239
+ return validateRoleSessionSet({
240
+ ...set, ...(set.activeAgentId === agentId ? { providerBinding: null } : {}),
241
+ updatedAt: now.toISOString()
242
+ });
243
+ const sessions = { ...set.sessions };
244
+ delete sessions[agentId];
223
245
  return validateRoleSessionSet({
224
246
  ...set,
225
247
  sessions,
226
- updatedAt: timestamp
248
+ history: [...(set.history ?? []), session],
249
+ ...(set.activeAgentId === agentId ? { providerBinding: null } : {}),
250
+ updatedAt: now.toISOString()
227
251
  });
228
252
  }
229
253
  export function rememberRoleAgentCompletedTurn(set, agentId, nativeSessionId, turnId, now) {
@@ -279,7 +303,10 @@ export function roleAgentSessionResumeMode(set, agentId, desired) {
279
303
  : undefined;
280
304
  if (conversation?.recoverability === "unrecoverable") {
281
305
  throw new Error(`Role Agent native Session is not recoverable: ${agentId}/${session.nativeSessionId}. `
282
- + "Explicitly select a new Session to continue; existing input attempts are not replayed.");
306
+ + (set.owner.scope === "task"
307
+ ? `After stopping the exact idle Session, use yui task role session new ${set.owner.taskId} ${set.owner.roleName} --reason <evidence>, then retry the failed AgentRun. `
308
+ : "Explicitly select a new Session to continue. ")
309
+ + "Existing input attempts are not replayed.");
283
310
  }
284
311
  if (roleSessionMayContinue(session.effective, desired))
285
312
  return "resume";
@@ -317,7 +344,7 @@ export function updateTaskRoleProviderRuntime(set, binding, updatedAt) {
317
344
  }
318
345
  /**
319
346
  * Detaches a confirmed-dead local Host without ending its resumable native
320
- * Session. The Host launch and Provider Activation are disposable execution
347
+ * Session. Host connections are disposable execution
321
348
  * facts; the native Session remains the durable continuation identity.
322
349
  */
323
350
  export function detachRoleAgentSessionHost(set, now) {
@@ -326,7 +353,7 @@ export function detachRoleAgentSessionHost(set, now) {
326
353
  if (active === undefined || active.status === "ended")
327
354
  return set;
328
355
  const timestamp = requireDate(now, "Role Host detach timestamp");
329
- const { runtimeGenerationId: _runtimeGenerationId, endReason: _endReason, ...session } = active;
356
+ const { endReason: _endReason, ...session } = active;
330
357
  const updated = validateRoleSessionSet({
331
358
  ...set,
332
359
  sessions: {
@@ -339,24 +366,10 @@ export function detachRoleAgentSessionHost(set, now) {
339
366
  },
340
367
  updatedAt: timestamp
341
368
  });
342
- if (updated.owner.scope !== "task")
343
- return updated;
344
- let taskSet = updated;
345
- const binding = taskSet.providerBinding;
346
369
  // Losing the attachment proves neither cancellation nor non-submission.
347
370
  // Preserve the exact input and its acceptance/unknown facts. Only a native
348
371
  // terminal or an explicit submission resolution may settle that attempt.
349
- const activation = binding === null
350
- ? null
351
- : currentProviderActivation(binding);
352
- if (activation !== null) {
353
- taskSet = updateTaskRoleProviderRuntime(taskSet, endProviderActivation(binding, activation.activationId, {
354
- status: "ended",
355
- endedAt: now.toISOString(),
356
- reason: "runtime-physical-exit"
357
- }), now);
358
- }
359
- return taskSet;
372
+ return updated;
360
373
  }
361
374
  /**
362
375
  * Records a Provider activity boundary without changing the durable Yui Turn.
@@ -367,7 +380,7 @@ export function detachRoleAgentSessionHost(set, now) {
367
380
  * Turn fence; this transition merely makes the native Session available for a
368
381
  * subsequent input and remembers the provider Turn idempotently.
369
382
  */
370
- export function recordTaskRoleTurnBoundary(set, input, completedAt) {
383
+ export function recordTaskRoleNativeTurnBoundary(set, input, completedAt) {
371
384
  validateRoleSessionSet(set);
372
385
  assertTaskRoleSessionSet(set);
373
386
  const agentId = requireSafeIdentity(input.agentId, "Agent id");
@@ -463,11 +476,12 @@ export function validateRoleSessionSet(set) {
463
476
  ? null
464
477
  : validateProviderRuntimeBinding(taskSet.providerBinding);
465
478
  if (providerBinding !== null) {
466
- const session = taskSet.sessions[set.activeAgentId];
467
- if (session === undefined) {
468
- throw new Error("Provider Runtime Binding has no active Role Agent session.");
469
- }
470
- if (providerBinding.providerNamespace !== builtinDriverIdForAdapter(session.adapterId)) {
479
+ const conversationId = currentProviderConversation(providerBinding).conversationId;
480
+ const session = [...Object.values(taskSet.sessions), ...(taskSet.history ?? [])]
481
+ .find(entry => entry.agentId === providerBinding.accountScope && entry.nativeSessionId === conversationId);
482
+ // Execution control evidence may outlive the disposable Session cache.
483
+ // It is not a live Session grant; recovery still needs native/OS proof.
484
+ if (session !== undefined && providerBinding.providerNamespace !== builtinDriverIdForAdapter(session.adapterId)) {
471
485
  throw new Error("Provider Runtime Binding namespace does not match the Agent adapter.");
472
486
  }
473
487
  }
@@ -476,7 +490,7 @@ export function validateRoleSessionSet(set) {
476
490
  return set;
477
491
  }
478
492
  export function validateRoleAgentSession(session, expectedAgentId = session.agentId) {
479
- if (session.schemaVersion !== 5) {
493
+ if (session.schemaVersion !== 6) {
480
494
  throw new Error(`Role Agent session schema version is invalid: ${expectedAgentId}.`);
481
495
  }
482
496
  const agentId = requireSafeIdentity(session.agentId, "Agent id");
@@ -484,22 +498,12 @@ export function validateRoleAgentSession(session, expectedAgentId = session.agen
484
498
  throw new Error(`Role Agent session identity is inconsistent: ${expectedAgentId}.`);
485
499
  }
486
500
  requireText(session.adapterId, "Agent adapter id");
501
+ validateAgentEndpointImplementation(session.endpointImplementation);
487
502
  validateEffectiveLaunchSnapshot(session.effective);
488
503
  if (session.effective.agentId !== agentId || session.effective.adapterId !== session.adapterId) {
489
504
  throw new Error(`Role Agent session effective identity is inconsistent: ${agentId}.`);
490
505
  }
491
- // A restored opaque host may have no provider-native identity; retain its
492
- // launch fence so it can be inspected or stopped without inventing identity.
493
- if (session.nativeSessionId === undefined) {
494
- if (session.runtimeGenerationId === undefined) {
495
- throw new Error("Role Agent session requires a native Session or runtime generation id.");
496
- }
497
- }
498
- else {
499
- requireText(session.nativeSessionId, "Native session id");
500
- }
501
- if (session.runtimeGenerationId !== undefined)
502
- requireSafeIdentity(session.runtimeGenerationId, "Runtime generation id");
506
+ requireText(session.nativeSessionId, "Native session id");
503
507
  if (session.title !== undefined
504
508
  && optionalSessionText("title", session.title).title !== session.title) {
505
509
  throw new Error("Role Agent session title is invalid.");
@@ -531,10 +535,7 @@ function taskRoleSessionIdentity(session) {
531
535
  if (session.nativeSessionId !== undefined) {
532
536
  return `${session.agentId}\0native\0${session.nativeSessionId}`;
533
537
  }
534
- if (session.runtimeGenerationId === undefined) {
535
- throw new Error("Opaque Task Role session requires a runtime generation identity.");
536
- }
537
- return `${session.agentId}\0runtime-generation\0${session.runtimeGenerationId}`;
538
+ throw new Error("Task Role session requires a native session identity.");
538
539
  }
539
540
  function optionalSessionText(field, value) {
540
541
  if (value === undefined)