@zq-silk/yui 0.15.7 → 0.15.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. package/ARCHITECTURE.md +192 -399
  2. package/README.md +127 -1149
  3. package/dist/agent/adapterCatalog.js +15 -2
  4. package/dist/agent/agent.js +23 -3
  5. package/dist/agent/argumentPolicy.js +7 -1
  6. package/dist/agent/connectionPlan.js +62 -0
  7. package/dist/agent/executionComponents.js +158 -0
  8. package/dist/agent/launchEnvironment.js +31 -3
  9. package/dist/agent/managedRuntimeEnvironment.js +3 -5
  10. package/dist/{turn/turn.js → agentRun/agentRun.js} +166 -109
  11. package/dist/{turn/turnIdentity.js → agentRun/runIdentity.js} +4 -4
  12. package/dist/brief/taskBrief.js +12 -0
  13. package/dist/cli/agentConfigurationPicker.js +13 -0
  14. package/dist/cli/commandCatalog.js +157 -70
  15. package/dist/cli/interactionCandidates.js +5 -5
  16. package/dist/cli/interactionPolicy.js +38 -8
  17. package/dist/cli/invocationRouter.js +1 -1
  18. package/dist/cli/managedDiagnostics.js +28 -0
  19. package/dist/cli/operatorWizard.js +1 -7
  20. package/dist/cli/roleOptionOrder.js +27 -0
  21. package/dist/cli/roleWizard.js +50 -14
  22. package/dist/cli/updateOrchestrator.js +1 -1
  23. package/dist/cli/updatePorts.js +3 -4
  24. package/dist/cli.js +178 -95
  25. package/dist/commands/agentCommands.js +72 -14
  26. package/dist/commands/capabilityCommands.js +9 -6
  27. package/dist/commands/configCommands.js +20 -20
  28. package/dist/commands/deliveryGuardPreflight.js +2 -2
  29. package/dist/commands/executionAuditCommands.js +24 -24
  30. package/dist/commands/globalRoleCommands.js +1 -1
  31. package/dist/commands/grantCommands.js +4 -4
  32. package/dist/commands/operatorCommands.js +1 -7
  33. package/dist/commands/projectCommands.js +4 -4
  34. package/dist/commands/resourcesCommands.js +2 -2
  35. package/dist/commands/roleConfiguration.js +25 -5
  36. package/dist/commands/roleRuntimeGuard.js +4 -5
  37. package/dist/commands/sessionCommands.js +3 -7
  38. package/dist/commands/taskActivationCommands.js +259 -0
  39. package/dist/commands/taskActor.js +28 -49
  40. package/dist/commands/taskCommands.js +1188 -710
  41. package/dist/commands/taskContextCommand.js +39 -583
  42. package/dist/commands/taskExecutionCommands.js +32 -32
  43. package/dist/commands/taskInputCommands.js +40 -104
  44. package/dist/commands/taskIntegrationCommands.js +3 -2
  45. package/dist/commands/taskIntegrationQueueCommands.js +1 -1
  46. package/dist/commands/taskNextActionCommand.js +8 -8
  47. package/dist/commands/taskOverviewCommand.js +33 -45
  48. package/dist/commands/taskRemoteDeliveryCommand.js +2 -2
  49. package/dist/commands/taskRoleRuntimeStatus.js +133 -102
  50. package/dist/commands/telemetryCommands.js +36 -38
  51. package/dist/config/configCatalog.js +4 -4
  52. package/dist/config/yuiConfig.js +8 -8
  53. package/dist/context/contextSnapshot.js +10 -10
  54. package/dist/context/dispatchContext.js +11 -11
  55. package/dist/context/roleSessionContext.js +6 -3
  56. package/dist/context/{turnContextPack.js → runContextPack.js} +146 -81
  57. package/dist/context/{turnInputContract.js → runInputContract.js} +73 -60
  58. package/dist/context/sessionBootstrapManifest.js +21 -2
  59. package/dist/context/sourceRunContext.js +30 -0
  60. package/dist/context/taskContext.js +458 -0
  61. package/dist/context/wakeNotification.js +27 -27
  62. package/dist/controller/agentRuntimeObserver.js +21 -24
  63. package/dist/controller/capabilityBridge.js +17 -6
  64. package/dist/controller/clientRuntime.js +65 -92
  65. package/dist/controller/controller.js +114 -188
  66. package/dist/controller/fileSchedulerStoreAdapter.js +787 -887
  67. package/dist/controller/jobControl.js +54 -85
  68. package/dist/controller/resourceInventory.js +8 -27
  69. package/dist/controller/resourceInventoryLinux.js +12 -13
  70. package/dist/controller/runtime.js +529 -479
  71. package/dist/controller/runtimeEventInbox.js +55 -25
  72. package/dist/controller/runtimeEventProcessor.js +22 -31
  73. package/dist/controller/{runtimeHookTurnFence.js → runtimeHookRunFence.js} +91 -115
  74. package/dist/controller/runtimeLaunchCoordinator.js +80 -426
  75. package/dist/controller/runtimeObservationHook.js +14 -18
  76. package/dist/controller/sessionNotify.js +16 -24
  77. package/dist/controller/sessionOwnerReconciliation.js +168 -50
  78. package/dist/controller/structuredProviderObservation.js +138 -99
  79. package/dist/coordination/workMailbox.js +3 -3
  80. package/dist/coordination/workMailboxQueue.js +36 -33
  81. package/dist/core/boundedRpc.js +8 -1
  82. package/dist/core/controllerClient.js +20 -1
  83. package/dist/core/controllerServer.js +4 -4
  84. package/dist/doctor/doctor.js +13 -2
  85. package/dist/domain/agentResultTransport.js +9 -9
  86. package/dist/execution/codexThreadNaming.js +2 -8
  87. package/dist/execution/executionHealth.js +51 -63
  88. package/dist/execution/reviewMainRun.js +137 -0
  89. package/dist/execution/workItemExecution.js +28 -29
  90. package/dist/execution/workItemExecutionProjection.js +99 -107
  91. package/dist/execution/workItemMainRun.js +141 -0
  92. package/dist/executor/agentAdapter.js +227 -20
  93. package/dist/executor/agentConfigurationCatalog.js +126 -4
  94. package/dist/executor/agentConfigurationProbe.js +162 -4
  95. package/dist/executor/agentExecutor.js +79 -78
  96. package/dist/executor/effectiveLaunch.js +105 -18
  97. package/dist/executor/executorRegistry.js +29 -44
  98. package/dist/executor/fileRoleLaunchPlanner.js +229 -154
  99. package/dist/executor/workspacePreflightClassification.js +16 -16
  100. package/dist/grant/capabilityGrant.js +6 -3
  101. package/dist/input/inputRequest.js +12 -10
  102. package/dist/integration/gitIntegrationService.js +4 -11
  103. package/dist/integration/integrationQueueService.js +4 -4
  104. package/dist/interaction/operatorPresentation.js +1 -1
  105. package/dist/kernel/builtinCapabilities.js +247 -12
  106. package/dist/kernel/capabilityRegistry.js +64 -18
  107. package/dist/kernel/instanceHost.js +12 -1
  108. package/dist/kernel/kernelPorts.js +2 -2
  109. package/dist/lifecycle/canonicalLifecycleEvent.js +44 -49
  110. package/dist/lifecycle/exactRunTerminalization.js +449 -0
  111. package/dist/message/message.js +62 -6
  112. package/dist/message/messageContinuation.js +204 -0
  113. package/dist/observability/executionAudit.js +70 -72
  114. package/dist/observability/faultClassification.js +2 -2
  115. package/dist/observability/orchestrationMetrics.js +8 -8
  116. package/dist/operator/operatorSessionHistory.js +1 -7
  117. package/dist/output/agentConfigurationPresentation.js +8 -3
  118. package/dist/output/agentRunConfigurationPresentation.js +128 -0
  119. package/dist/output/rolePresentation.js +54 -3
  120. package/dist/plugins/pluginChild.js +104 -0
  121. package/dist/plugins/pluginIntent.js +26 -0
  122. package/dist/plugins/pluginInterpreter.js +43 -0
  123. package/dist/plugins/pluginPackage.js +101 -0
  124. package/dist/plugins/pluginProcess.js +112 -0
  125. package/dist/plugins/pluginService.js +380 -0
  126. package/dist/profile/agentProfile.js +1 -1
  127. package/dist/repository/gitWorkspace.js +26 -4
  128. package/dist/repository/project.js +19 -4
  129. package/dist/repository/taskBaseFreshness.js +13 -13
  130. package/dist/repository/taskWorkspaceCoordinator.js +20 -27
  131. package/dist/repository/taskWorkspacePreparer.js +344 -83
  132. package/dist/resources/autoResourceGc.js +3 -3
  133. package/dist/resources/liveReferences.js +3 -3
  134. package/dist/resources/projectResource.js +123 -0
  135. package/dist/resources/projectResourceService.js +421 -0
  136. package/dist/resources/resourceDiscovery.js +6 -6
  137. package/dist/resources/resourceGc.js +1 -1
  138. package/dist/resources/resourceRegistrar.js +1 -1
  139. package/dist/resources/resourceTypes.js +1 -1
  140. package/dist/review/deltaRecheck.js +3 -3
  141. package/dist/review/reviewAcceptance.js +16 -16
  142. package/dist/review/reviewDecision.js +7 -7
  143. package/dist/review/reviewRound.js +21 -20
  144. package/dist/review/reviewerAvailability.js +2 -2
  145. package/dist/role/role.js +51 -7
  146. package/dist/role/taskRoleUpdate.js +30 -0
  147. package/dist/runtime/acpProtocol.js +425 -0
  148. package/dist/runtime/acpSession.js +731 -0
  149. package/dist/runtime/acpSessionConfiguration.js +260 -0
  150. package/dist/runtime/agentDriver.js +30 -11
  151. package/dist/runtime/agentEndpoint.js +278 -0
  152. package/dist/runtime/agentEndpointIdentity.js +86 -0
  153. package/dist/runtime/agentEndpointOwnership.js +239 -0
  154. package/dist/runtime/agentError.js +2 -10
  155. package/dist/runtime/agentHost.js +565 -314
  156. package/dist/runtime/agentRunConfiguration.js +258 -0
  157. package/dist/runtime/builtinAgentDrivers.js +134 -18
  158. package/dist/runtime/builtinAgentErrorMappers.js +55 -3
  159. package/dist/runtime/builtinTranscriptUsage.js +1 -1
  160. package/dist/runtime/claude-process-owner +0 -0
  161. package/dist/runtime/codexAppServerRuntime.js +38 -30
  162. package/dist/runtime/codexInteractiveHost.js +41 -6
  163. package/dist/runtime/continuationManager.js +2 -6
  164. package/dist/runtime/executionEnvironment.js +30 -0
  165. package/dist/runtime/firstProgressAdvisory.js +11 -11
  166. package/dist/runtime/index.js +4 -3
  167. package/dist/runtime/jsonLineChannel.js +109 -0
  168. package/dist/runtime/launchBroker.js +91 -16
  169. package/dist/runtime/launchDiagnostics.js +2 -2
  170. package/dist/runtime/lifecycleReservation.js +10 -18
  171. package/dist/runtime/managedCaller.js +61 -17
  172. package/dist/runtime/nativeSessionControl.js +102 -0
  173. package/dist/runtime/ports.js +6 -21
  174. package/dist/runtime/processExitObservation.js +8 -7
  175. package/dist/runtime/promptEnvelope.js +17 -6
  176. package/dist/runtime/providerContinuation.js +3 -9
  177. package/dist/runtime/providerContinuationReconciliationService.js +4 -13
  178. package/dist/runtime/providerControl.js +2 -7
  179. package/dist/runtime/providerRuntimeIdentity.js +110 -222
  180. package/dist/runtime/providerRuntimeReconciler.js +5 -9
  181. package/dist/runtime/runtimeBinding.js +0 -1
  182. package/dist/runtime/runtimeContinuationProjection.js +4 -7
  183. package/dist/runtime/runtimeDeadlines.js +9 -0
  184. package/dist/runtime/runtimeHealthPolicy.js +1 -1
  185. package/dist/runtime/runtimeObservation.js +29 -65
  186. package/dist/runtime/runtimeProjection.js +43 -51
  187. package/dist/runtime/runtimeSessionCandidate.js +1 -3
  188. package/dist/runtime/sessionLaunchRequest.js +3 -7
  189. package/dist/runtime/sessionOwnerIdentity.js +7 -54
  190. package/dist/runtime/sessionOwnerRegistry.js +22 -17
  191. package/dist/runtime/sessionReconciliation.js +4 -8
  192. package/dist/runtime/sessionTerminationGuard.js +70 -259
  193. package/dist/runtime/sessionTokenMetrics.js +5 -16
  194. package/dist/runtime/structuredProviderHost.js +237 -117
  195. package/dist/runtime/taskRuntimeIsolation.js +39 -122
  196. package/dist/runtime/tmuxAdapters.js +39 -86
  197. package/dist/scheduler/activeRoleRunDelivery.js +354 -0
  198. package/dist/scheduler/leaderWakeupProcessor.js +75 -266
  199. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  200. package/dist/scheduler/ports.js +80 -9
  201. package/dist/scheduler/{roleTurnLiveness.js → roleRunLiveness.js} +26 -30
  202. package/dist/scheduler/{roleTurnStall.js → roleRunStall.js} +128 -139
  203. package/dist/scheduler/taskExecutionProjection.js +120 -124
  204. package/dist/scheduler/taskObservabilityProjection.js +29 -29
  205. package/dist/scheduler/taskWake.js +11 -4
  206. package/dist/scheduler/wakeReason.js +9 -1
  207. package/dist/setup/setupCommand.js +1 -0
  208. package/dist/storage/migrations/agentRunContract.js +159 -0
  209. package/dist/storage/migrations/removeRuntimeGeneration.js +207 -0
  210. package/dist/storage/sqliteSchema.js +431 -5
  211. package/dist/storage/sqliteStore.js +375 -220
  212. package/dist/storage/storageVersions.js +1 -1
  213. package/dist/storage/storeRpc.js +10 -5
  214. package/dist/storage/taskStore.js +13 -11
  215. package/dist/storage/upgrade/upgradeOrchestrator.js +5 -7
  216. package/dist/surface/surfaceContributions.js +102 -0
  217. package/dist/task/completionReadiness.js +32 -6
  218. package/dist/task/deliveryGuard.js +16 -16
  219. package/dist/task/draftPlan.js +72 -12
  220. package/dist/task/nextAction.js +144 -128
  221. package/dist/task/remoteDelivery.js +6 -6
  222. package/dist/task/task.js +184 -18
  223. package/dist/task/taskActivation.js +301 -0
  224. package/dist/task/taskActivationService.js +392 -0
  225. package/dist/task/taskRecordReference.js +5 -4
  226. package/dist/task/taskRecordRetirement.js +1 -1
  227. package/dist/telemetry/sqliteTelemetryStore.js +55 -68
  228. package/dist/telemetry/telemetryConfig.js +14 -14
  229. package/dist/telemetry/telemetryWiring.js +2 -2
  230. package/dist/web/assets/assetManifest.js +2 -0
  231. package/dist/web/assets/client/app.js +120 -20
  232. package/dist/web/assets/client/components.js +87 -54
  233. package/dist/web/assets/client/i18n.js +83 -41
  234. package/dist/web/assets/client/markdown.js +1 -1
  235. package/dist/web/assets/client/taskSurface.js +353 -0
  236. package/dist/web/assets/client/view.js +49 -44
  237. package/dist/web/assets/shell.js +1 -1
  238. package/dist/web/assets/styles/cards.js +22 -4
  239. package/dist/web/controllerWeb.js +60 -0
  240. package/dist/web/webMutation.js +28 -0
  241. package/dist/web/webServer.js +118 -8
  242. package/dist/web/webSnapshot.js +81 -74
  243. package/dist/web/webTaskSurface.js +64 -0
  244. package/dist/workItem/dependencyGate.js +1 -1
  245. package/dist/workItem/workItem.js +80 -48
  246. package/dist/workspace/workItemChangeSetManager.js +16 -9
  247. package/docs/agent-result-consumption.md +94 -0
  248. package/docs/agent-runtime-drivers.md +91 -0
  249. package/docs/architecture/README.md +38 -0
  250. package/docs/architecture/capabilities-and-resources.md +79 -0
  251. package/docs/managed-turn-and-session-runtime.md +222 -0
  252. package/docs/observability/README.md +81 -0
  253. package/docs/plugin-sdk.md +290 -0
  254. package/docs/provider-runtime.md +163 -0
  255. package/docs/release-workflow.md +303 -0
  256. package/docs/roles-and-configuration.md +113 -0
  257. package/docs/sqlite-control-plane-design.md +76 -0
  258. package/docs/task-dag-semantics.md +57 -0
  259. package/docs/task-delivery.md +103 -0
  260. package/docs/task-local-identity.md +6 -6
  261. package/docs/testing/verification-levels.md +86 -0
  262. package/i18n/README.zh-CN.md +98 -739
  263. package/package.json +2 -2
  264. package/skills/yui-leader/SKILL.md +130 -103
  265. package/skills/yui-leader/references/integration.md +39 -0
  266. package/skills/yui-leader/references/replicated-execution.md +42 -0
  267. package/skills/yui-leader/references/task-plugins.md +33 -0
  268. package/skills/yui-operator/SKILL.md +30 -59
  269. package/skills/yui-reviewer/SKILL.md +35 -36
  270. package/skills/yui-runtime/SKILL.md +88 -24
  271. package/skills/yui-runtime/references/publication.md +22 -0
  272. package/skills/yui-runtime/references/recovery.md +64 -0
  273. package/skills/yui-worker/SKILL.md +37 -39
  274. package/dist/cli/roleOptionCatalog.js +0 -68
  275. package/dist/context/sourceTurnContext.js +0 -30
  276. package/dist/execution/reviewMainTurn.js +0 -161
  277. package/dist/execution/workItemMainTurn.js +0 -164
  278. package/dist/lifecycle/exactTurnTerminalization.js +0 -407
  279. package/dist/runtime/preallocatedNativeSession.js +0 -13
  280. package/dist/runtime/runtimeStopReceipt.js +0 -42
  281. package/dist/scheduler/activeRoleTurnDelivery.js +0 -315
@@ -5,7 +5,7 @@ const DEFAULT_MAX_CONCURRENT_SAMPLES = 8;
5
5
  const MAX_CONCURRENT_SAMPLES = 64;
6
6
  /**
7
7
  * Controller-owned, provider-independent sampler. Drivers own source parsing;
8
- * this component owns active-Turn discovery, cursor lifetime, canonical event
8
+ * this component owns active-AgentRun discovery, cursor lifetime, canonical event
9
9
  * creation, and low-latency mailbox wakes.
10
10
  */
11
11
  export class AgentRuntimeObserver {
@@ -72,8 +72,8 @@ export class AgentRuntimeObserver {
72
72
  let usages = sample.usages ?? [];
73
73
  if (state.usage === undefined) {
74
74
  // Cumulative counters need a lower bound. An exact initial sample
75
- // proves the complete Session-generation history even when the
76
- // current Turn resumed that Session, so preserve every occurrence and
75
+ // proves the complete Session history even when the
76
+ // current AgentRun resumed that Session, so preserve every occurrence and
77
77
  // anchor it at zero. A partial sample cannot prove the omitted request
78
78
  // boundaries; retain its latest total as a partial baseline, plus the
79
79
  // smallest ordered witness if the visible counter already rolled back.
@@ -251,43 +251,43 @@ export class AgentRuntimeObserver {
251
251
  && task.executionGate.state === "enabled"));
252
252
  for (const task of activeTasks) {
253
253
  // A Task still incurs one O(E) event projection. Group those observations
254
- // by Turn and sort each group once so every active Turn can reuse the same
254
+ // by AgentRun and sort each group once so every active AgentRun can reuse the same
255
255
  // ordered slice instead of repeatedly filtering/sorting all E events.
256
- const observationsByTurnId = new Map();
256
+ const observationsByRunId = new Map();
257
257
  const taskObservations = [];
258
258
  for (const event of this.store.listEvents(task.id)) {
259
259
  const observation = runtimeObservationFromTaskEvent(event);
260
- const turnId = observation?.fence.turnId;
261
- if (observation === null || turnId === undefined)
260
+ const runId = observation?.fence.runId;
261
+ if (observation === null || runId === undefined)
262
262
  continue;
263
263
  taskObservations.push(observation);
264
- const grouped = observationsByTurnId.get(turnId);
264
+ const grouped = observationsByRunId.get(runId);
265
265
  if (grouped === undefined) {
266
- observationsByTurnId.set(turnId, [observation]);
266
+ observationsByRunId.set(runId, [observation]);
267
267
  }
268
268
  else {
269
269
  grouped.push(observation);
270
270
  }
271
271
  }
272
- for (const observations of observationsByTurnId.values()) {
272
+ for (const observations of observationsByRunId.values()) {
273
273
  observations.sort(compareObservations);
274
274
  }
275
275
  taskObservations.sort(compareObservations);
276
- for (const run of this.store.listTurns(task.id)) {
276
+ for (const run of this.store.listRuns(task.id)) {
277
277
  if (run.status !== "active"
278
- || this.store.getActiveTurn(task.id, run.roleName)?.id !== run.id)
278
+ || this.store.getActiveRun(task.id, run.roleName)?.id !== run.id)
279
279
  continue;
280
- const observations = observationsByTurnId.get(run.id) ?? [];
280
+ const observations = observationsByRunId.get(run.id) ?? [];
281
281
  const accepted = observations
282
282
  .filter((observation) => observation.kind === "turn.accepted"
283
- && observation.fence.turnId === run.id
283
+ && observation.fence.runId === run.id
284
284
  && observation.fence.roleName === run.roleName
285
285
  && observation.fence.agentId === run.effective.agentId
286
286
  && observation.payload.observerSource !== undefined)
287
287
  .at(-1);
288
288
  const source = accepted?.payload.observerSource;
289
289
  if (accepted === undefined || source === undefined
290
- || accepted.fence.taskId === undefined || accepted.fence.turnId === undefined)
290
+ || accepted.fence.taskId === undefined || accepted.fence.runId === undefined)
291
291
  continue;
292
292
  try {
293
293
  if (this.drivers.require(accepted.fence.driverId).runtime.observer === undefined)
@@ -297,14 +297,14 @@ export class AgentRuntimeObserver {
297
297
  continue;
298
298
  }
299
299
  const fence = accepted.fence;
300
- const generation = taskObservations.filter((observation) => (sessionRuntimeGenerationFenceMatches(fence, observation.fence)
300
+ const sessionObservations = taskObservations.filter((observation) => (sessionRuntimeFenceMatches(fence, observation.fence)
301
301
  && observation.payload.sourceId === source.sourceId));
302
- const persistedUsage = generation.filter((observation) => (observation.kind === "activity.observed"
302
+ const persistedUsage = sessionObservations.filter((observation) => (observation.kind === "activity.observed"
303
303
  && observation.payload.usage !== undefined)).at(-1);
304
- const persistedActivity = generation.filter((observation) => (observation.kind === "activity.observed"
304
+ const persistedActivity = sessionObservations.filter((observation) => (observation.kind === "activity.observed"
305
305
  && observation.payload.activityId !== undefined
306
306
  && observation.payload.usage === undefined)).at(-1);
307
- const persistedHealth = generation.filter((observation) => (observation.kind === "observer.health"
307
+ const persistedHealth = sessionObservations.filter((observation) => (observation.kind === "observer.health"
308
308
  && observation.payload.sourceId === source.sourceId)).at(-1);
309
309
  const persistedUsageResume = persistedUsage === undefined
310
310
  ? undefined
@@ -313,10 +313,9 @@ export class AgentRuntimeObserver {
313
313
  key: JSON.stringify([
314
314
  fence.taskId,
315
315
  fence.roleName,
316
- fence.turnId,
316
+ fence.runId,
317
317
  fence.agentId,
318
318
  fence.driverId,
319
- fence.runtimeGenerationId,
320
319
  fence.nativeSessionId,
321
320
  source.sourceId
322
321
  ]),
@@ -462,19 +461,17 @@ function tokenObservationId(kind, fence, sourceId, value) {
462
461
  fence.roleName,
463
462
  fence.agentId,
464
463
  fence.driverId,
465
- fence.runtimeGenerationId,
466
464
  fence.nativeSessionId ?? null,
467
465
  sourceId,
468
466
  value
469
467
  ])).digest("hex")}`;
470
468
  }
471
- function sessionRuntimeGenerationFenceMatches(expected, actual) {
469
+ function sessionRuntimeFenceMatches(expected, actual) {
472
470
  for (const field of [
473
471
  "taskId",
474
472
  "roleName",
475
473
  "agentId",
476
474
  "driverId",
477
- "runtimeGenerationId",
478
475
  "nativeSessionId"
479
476
  ]) {
480
477
  if (expected[field] !== actual[field])
@@ -1,7 +1,9 @@
1
1
  import { capabilitySchemaError } from "../kernel/capabilitySchema.js";
2
+ import { SurfaceContributions } from "../surface/surfaceContributions.js";
2
3
  /** The same transport method is usable by CLI, Agent and an authenticated
3
4
  * Surface adapter. Credentials are an ingress envelope, never capability input. */
4
5
  export function createCapabilityDispatcher(capabilities) {
6
+ const surfaces = new SurfaceContributions(capabilities.registry);
5
7
  return async (method, value) => {
6
8
  const error = capabilitySchemaError({
7
9
  type: "object", required: ["taskId", "caller"],
@@ -12,12 +14,12 @@ export function createCapabilityDispatcher(capabilities) {
12
14
  type: "object", required: ["scope"], additionalProperties: false,
13
15
  properties: {
14
16
  scope: { enum: ["user", "global", "task"] }, taskId: { type: "string" },
15
- role: { type: "string" }, agentId: { type: "string" }, adapterId: { type: "string" },
16
- runtimeGenerationId: { type: "string" }, nativeSessionId: { type: "string" },
17
- turnId: { type: "string" }, callerKey: { type: "string" }
17
+ role: { type: "string" }, agentId: { type: "string" }, adapterId: { type: "string" }, nativeSessionId: { type: "string" },
18
+ runId: { type: "string" }
18
19
  }
19
20
  },
20
21
  query: { type: "string" },
22
+ surface: { enum: ["commands", "panels"] },
21
23
  request: {
22
24
  type: "object", required: ["name"], additionalProperties: false,
23
25
  properties: {
@@ -37,11 +39,20 @@ export function createCapabilityDispatcher(capabilities) {
37
39
  if (method === "capability.search") {
38
40
  if (params.request !== undefined)
39
41
  throw invalidParams("search accepts query, not request.");
40
- result = { capabilities: capabilities.registry.search(context, params.query) };
42
+ if (params.surface !== undefined) {
43
+ if (params.query !== undefined)
44
+ throw invalidParams("Surface listing does not accept query.");
45
+ result = params.surface === "commands"
46
+ ? { commands: surfaces.listCommands(context) }
47
+ : { panels: surfaces.listPanels(context) };
48
+ }
49
+ else
50
+ result = { capabilities: capabilities.registry.search(context, params.query) };
41
51
  }
42
52
  else {
43
- if (params.query !== undefined || params.request === undefined)
44
- throw invalidParams("describe/call requires request.");
53
+ if (params.query !== undefined || params.surface !== undefined || params.request === undefined) {
54
+ throw invalidParams("describe/call requires request, not query or surface.");
55
+ }
45
56
  if (method === "capability.describe")
46
57
  result = capabilities.registry.describe(context, params.request);
47
58
  else if (method === "capability.call") {
@@ -1,11 +1,11 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { fileURLToPath } from "node:url";
3
+ import { findLiveControllerProcessForHome } from "../core/controllerProcessIdentity.js";
4
+ import { readHomeFilesystemId } from "../core/homeFilesystemIdentity.js";
3
5
  import { callController, ControllerClientError, readControllerDiscovery, stopOrphanedFileTaskController } from "../core/controllerClient.js";
4
- import { controllerSocketPath } from "../core/controllerEndpoint.js";
5
6
  import { FILE_TASK_CONTROLLER_PROTOCOL_VERSION } from "../core/protocol.js";
6
7
  import { AGENT_OPERATIONAL_ENVIRONMENT_NAMES, nativeAgentEnvironmentNames, operationalAgentEnvironment, selectEnvironment, YUI_MANAGED_RUNTIME_ENVIRONMENT_NAMES } from "../agent/launchEnvironment.js";
7
8
  import { openCurrentTaskStore } from "../storage/currentTaskStore.js";
8
- import { yuiTmuxServerName } from "../tmux/tmuxManager.js";
9
9
  import { hasRuntimeLifecycleWork } from "../runtime/lifecycleReservation.js";
10
10
  import { assertControllerStatusIdentity } from "../runtime/runtimeCoherence.js";
11
11
  import { EPHEMERAL_DOMAIN_ENVIRONMENT_NAMES } from "./domainIdentity.js";
@@ -13,7 +13,6 @@ import { yuiVersionIdentity } from "../version.js";
13
13
  import { SessionOwnerReconciliation } from "./sessionOwnerReconciliation.js";
14
14
  import { WorkspaceCleanupBlockedError } from "../repository/taskWorkspacePreparer.js";
15
15
  import { CONTROLLER_SHUTDOWN_TIMEOUT_MS, LIFECYCLE_REQUEST_TIMEOUT_MS } from "../runtime/runtimeDeadlines.js";
16
- import { FileTaskRuntimeIsolation } from "../runtime/taskRuntimeIsolation.js";
17
16
  import { isForeignHandoverLockHeld } from "../release/runtimeRelease.js";
18
17
  const STARTUP_TIMEOUT_MS = 5_000;
19
18
  const POLL_INTERVAL_MS = 50;
@@ -24,7 +23,7 @@ const CONTROLLER_OPERATIONAL_ENVIRONMENT = [
24
23
  // The worker toggle changes process placement, not storage authority.
25
24
  "YUI_STORE_WORKER",
26
25
  // rr13/test: Forward the liveness seam so an integration test's Controller
27
- // subprocess does not reap a saved active Leader Turn without a real tmux role.
26
+ // subprocess does not reap a saved active Leader AgentRun without a real tmux role.
28
27
  "YUI_TEST_ROLE_LIVENESS_PRESENT",
29
28
  ...EPHEMERAL_DOMAIN_ENVIRONMENT_NAMES
30
29
  ];
@@ -68,7 +67,13 @@ export async function ensureFileTaskController(home, options = {}) {
68
67
  const timeoutMs = positive(options.startupTimeoutMs, STARTUP_TIMEOUT_MS, "startupTimeoutMs");
69
68
  const pollMs = positive(options.pollIntervalMs, POLL_INTERVAL_MS, "pollIntervalMs");
70
69
  const spawnController = options.spawnController ?? spawnDetachedFileTaskController;
71
- spawnController(home, controllerSpawnEnvironment(home, options.environment ?? process.env));
70
+ // A readiness timeout is not exit evidence. Reuse the observable startup
71
+ // process for this exact Home rather than multiplying unready Controllers.
72
+ const existing = options.spawnController === undefined && options.call === undefined
73
+ ? findLiveControllerProcessForHome(readHomeFilesystemId(home))
74
+ : undefined;
75
+ const startupPid = existing?.pid
76
+ ?? spawnController(home, controllerSpawnEnvironment(home, options.environment ?? process.env));
72
77
  const deadline = Date.now() + timeoutMs;
73
78
  for (;;) {
74
79
  try {
@@ -80,7 +85,9 @@ export async function ensureFileTaskController(home, options = {}) {
80
85
  if (!isUnavailable(error))
81
86
  throw error;
82
87
  if (Date.now() >= deadline) {
83
- throw new Error(`Controller did not become ready within ${timeoutMs} ms.`, { cause: error });
88
+ throw new Error(`Controller did not become ready within ${timeoutMs} ms.`
89
+ + (startupPid === undefined ? "" : ` Startup PID: ${startupPid}.`)
90
+ + " Read current process state before retrying; timeout does not prove it stopped.", { cause: error });
84
91
  }
85
92
  await delay(pollMs);
86
93
  }
@@ -88,7 +95,7 @@ export async function ensureFileTaskController(home, options = {}) {
88
95
  }
89
96
  /** Start and await readiness for a captured Controller identity. */
90
97
  export async function ensureFileTaskControllerIdentity(home, identity, options = {}) {
91
- assertExpectedControllerRuntimeGenerationIdentity(identity);
98
+ assertExpectedControllerRuntimeProcessIdentity(identity);
92
99
  const status = await ensureFileTaskController(home, {
93
100
  ...options,
94
101
  expectedVersion: identity.version
@@ -102,36 +109,36 @@ export async function ensureFileTaskControllerIdentity(home, identity, options =
102
109
  const actual = await call(home, "controller.identity", {}, {
103
110
  timeoutMs: options.requestTimeoutMs
104
111
  });
105
- assertControllerRuntimeGenerationIdentity(actual, identity);
112
+ assertControllerRuntimeProcessIdentity(actual, identity);
106
113
  return status;
107
114
  }
108
- function assertExpectedControllerRuntimeGenerationIdentity(identity) {
115
+ function assertExpectedControllerRuntimeProcessIdentity(identity) {
109
116
  if (typeof identity.executablePath !== "string"
110
117
  || identity.executablePath.length === 0
111
118
  || !Array.isArray(identity.args)
112
119
  || identity.args.some((arg) => typeof arg !== "string")
113
120
  || typeof identity.version !== "string"
114
121
  || identity.version.length === 0) {
115
- throw new Error("Expected Controller runtime generation identity is malformed; refusing to start or accept a Controller.");
122
+ throw new Error("Expected Controller runtime identity is malformed; refusing to start or accept a Controller.");
116
123
  }
117
124
  }
118
- function assertControllerRuntimeGenerationIdentity(actual, expected) {
125
+ function assertControllerRuntimeProcessIdentity(actual, expected) {
119
126
  if (!isJsonRecord(actual)) {
120
- throw new Error("Authenticated Controller runtime generation identity is malformed; refusing to accept readiness.");
127
+ throw new Error("Authenticated Controller runtime identity is malformed; refusing to accept readiness.");
121
128
  }
122
129
  const actualArgs = actual.args;
123
130
  if (typeof actual.executablePath !== "string"
124
131
  || !Array.isArray(actualArgs)
125
132
  || actualArgs.some((arg) => typeof arg !== "string")
126
133
  || typeof actual.version !== "string") {
127
- throw new Error("Authenticated Controller runtime generation identity is malformed; refusing to accept readiness.");
134
+ throw new Error("Authenticated Controller runtime identity is malformed; refusing to accept readiness.");
128
135
  }
129
136
  const argsMatch = actualArgs.length === expected.args.length
130
137
  && actualArgs.every((arg, index) => arg === expected.args[index]);
131
138
  if (actual.executablePath !== expected.executablePath
132
139
  || !argsMatch
133
140
  || actual.version !== expected.version) {
134
- throw new Error("Authenticated Controller runtime generation identity does not match the captured executable, argv, and version; refusing readiness.");
141
+ throw new Error("Authenticated Controller runtime identity does not match the captured executable, argv, and version; refusing readiness.");
135
142
  }
136
143
  }
137
144
  function assertCompatibleControllerStatus(status, expectedVersion) {
@@ -166,10 +173,33 @@ function spawnDetachedFileTaskController(home, environment) {
166
173
  stdio: "ignore"
167
174
  });
168
175
  child.unref();
176
+ return child.pid;
169
177
  }
170
178
  /** Stops the per-home Controller and waits until its owned discovery is gone. */
171
179
  export async function stopFileTaskController(home, options = {}) {
172
- const call = options.call ?? callController;
180
+ await waitForForeignHandover(home, options);
181
+ const physical = options.call === undefined
182
+ ? findLiveControllerProcessForHome(readHomeFilesystemId(home)) : undefined;
183
+ const expectedPid = options.expectedPid ?? physical?.pid;
184
+ const bounded = { ...options, requestTimeoutMs: options.requestTimeoutMs ?? 2000,
185
+ ...(expectedPid === undefined ? {} : { expectedPid }) };
186
+ try {
187
+ const result = await stopControllerGracefully(home, bounded);
188
+ if (result.stopped || options.call !== undefined)
189
+ return result;
190
+ }
191
+ catch (error) {
192
+ if (options.call !== undefined || !controllerRecoveryError(error))
193
+ throw error;
194
+ }
195
+ const stopped = await stopOrphanedFileTaskController(home, options.shutdownTimeoutMs ?? CONTROLLER_SHUTDOWN_TIMEOUT_MS, { force: true, ...(expectedPid === undefined ? {} : { expectedPid }),
196
+ ...(physical === undefined ? {} : { expectedProcessStartIdentity: physical.processStartIdentity }) });
197
+ return stopped === undefined ? { stopped: false, alreadyStopped: true } : { stopped: true, pid: stopped.pid };
198
+ }
199
+ async function stopControllerGracefully(home, options) {
200
+ const call = options.call ?? ((h, method, params) => callController(h, method, params, {
201
+ timeoutMs: options.requestTimeoutMs
202
+ }));
173
203
  const shutdownTimeoutMs = positive(options.shutdownTimeoutMs, CONTROLLER_SHUTDOWN_TIMEOUT_MS, "shutdownTimeoutMs");
174
204
  const pollMs = positive(options.pollIntervalMs, POLL_INTERVAL_MS, "pollIntervalMs");
175
205
  const expectedPid = options.expectedPid === undefined
@@ -178,6 +208,8 @@ export async function stopFileTaskController(home, options = {}) {
178
208
  const current = await readOptionalControllerStatus(home, call);
179
209
  const pid = controllerPid(current);
180
210
  if (expectedPid !== undefined && pid !== expectedPid) {
211
+ if (pid === undefined)
212
+ throw new ControllerClientError("CONTROLLER_UNAVAILABLE", `Controller ownership could not be queried for expected PID ${expectedPid}.`);
181
213
  throw new Error(`Controller ownership changed before fenced stop (expected PID ${expectedPid}, `
182
214
  + `found ${pid === undefined ? "none" : pid}).`);
183
215
  }
@@ -214,7 +246,7 @@ export async function stopFileTaskController(home, options = {}) {
214
246
  break;
215
247
  }
216
248
  if (Date.now() >= deadline) {
217
- throw new Error(`Controller did not stop within ${shutdownTimeoutMs} ms.`);
249
+ throw new ControllerClientError("CONTROLLER_TIMEOUT", `Controller did not stop within ${shutdownTimeoutMs} ms.`);
218
250
  }
219
251
  await delay(pollMs);
220
252
  }
@@ -225,41 +257,8 @@ export async function stopFileTaskController(home, options = {}) {
225
257
  }
226
258
  /** Restarts only the per-home Controller process; managed tmux sessions remain untouched. */
227
259
  export async function restartFileTaskController(home, options = {}) {
228
- const call = options.call ?? callController;
229
- const shutdownTimeoutMs = positive(options.shutdownTimeoutMs, CONTROLLER_SHUTDOWN_TIMEOUT_MS, "shutdownTimeoutMs");
230
- const pollMs = positive(options.pollIntervalMs, POLL_INTERVAL_MS, "pollIntervalMs");
231
- let current = null;
232
- let previousPid;
233
- try {
234
- current = await readOptionalControllerStatus(home, call);
235
- previousPid = controllerPid(current);
236
- }
237
- catch (error) {
238
- if (options.call !== undefined || !isInvalidDiscovery(error))
239
- throw error;
240
- // A malformed current discovery record cannot authorize a protocol call.
241
- // Exact process identity below may still fence and stop an orphaned owner.
242
- }
243
- if (!controllerRunning(current) && options.call === undefined) {
244
- const orphan = await stopOrphanedFileTaskController(home, shutdownTimeoutMs);
245
- if (orphan !== undefined)
246
- previousPid = orphan.pid;
247
- }
248
- if (controllerRunning(current)) {
249
- await callFileTaskController(home, "controller.stop", {}, options);
250
- const deadline = Date.now() + shutdownTimeoutMs;
251
- for (;;) {
252
- const stillOwned = options.call === undefined
253
- ? await ownedControllerDiscoveryExists(home, previousPid)
254
- : controllerPid(await readOptionalControllerStatus(home, call)) === previousPid;
255
- if (!stillOwned)
256
- break;
257
- if (Date.now() >= deadline) {
258
- throw new Error(`Controller did not stop within ${shutdownTimeoutMs} ms.`);
259
- }
260
- await delay(pollMs);
261
- }
262
- }
260
+ const stopped = await stopFileTaskController(home, options);
261
+ const previousPid = stopped.pid;
263
262
  const started = await ensureFileTaskController(home, options);
264
263
  const pid = controllerPid(started);
265
264
  return {
@@ -268,6 +267,12 @@ export async function restartFileTaskController(home, options = {}) {
268
267
  ...(pid === undefined ? {} : { pid })
269
268
  };
270
269
  }
270
+ function controllerRecoveryError(error) {
271
+ return error instanceof ControllerClientError && [
272
+ "CONTROLLER_TIMEOUT", "CONTROLLER_UNAVAILABLE", "CONTROLLER_DELIVERY_UNKNOWN",
273
+ "CONTROLLER_DISCOVERY_INVALID", "INVALID_RESPONSE", "CONTROLLER_NOT_RUNNING"
274
+ ].includes(error.code);
275
+ }
271
276
  function controllerSpawnEnvironment(home, source) {
272
277
  const allowed = new Set(CONTROLLER_OPERATIONAL_ENVIRONMENT);
273
278
  try {
@@ -433,8 +438,8 @@ export class FileTaskWorkflowRuntime {
433
438
  async stopTaskRoleSessions(taskId, roleNames) {
434
439
  const targets = [];
435
440
  for (const roleName of [...new Set(roleNames)]) {
436
- if (this.store.getActiveTurn(taskId, roleName) !== null) {
437
- throw new Error(`Role has an active Turn: ${taskId}/${roleName}.`);
441
+ if (this.store.getActiveRun(taskId, roleName) !== null) {
442
+ throw new Error(`Role has an active AgentRun: ${taskId}/${roleName}.`);
438
443
  }
439
444
  const target = this.schedulerStore.enqueueRuntimeCleanup({
440
445
  scope: "task",
@@ -464,11 +469,8 @@ export class FileTaskWorkflowRuntime {
464
469
  }
465
470
  }
466
471
  }
467
- /** Stops only the exact idle Session observed by an Agent command. */
472
+ /** Explicitly stops the exact Session observed by an Agent command. */
468
473
  async stopExactTaskRoleSession(input) {
469
- if (this.store.getActiveTurn(input.taskId, input.roleName) !== null) {
470
- throw new Error(`Role has an active Turn: ${input.taskId}/${input.roleName}.`);
471
- }
472
474
  const owner = {
473
475
  scope: "task",
474
476
  taskId: input.taskId,
@@ -479,9 +481,8 @@ export class FileTaskWorkflowRuntime {
479
481
  agentId: input.agentId,
480
482
  adapterId: input.adapterId,
481
483
  nativeSessionId: input.nativeSessionId,
482
- ...(input.runtimeGenerationId === undefined ? {} : { runtimeGenerationId: input.runtimeGenerationId }),
483
484
  sessionUpdatedAt: input.sessionUpdatedAt
484
- });
485
+ }, true);
485
486
  if (target === null) {
486
487
  throw new Error(`Role Session changed before its exact stop was reserved: ${input.taskId}/${input.roleName}.`);
487
488
  }
@@ -492,14 +493,9 @@ export class FileTaskWorkflowRuntime {
492
493
  if (hasRuntimeLifecycleWork(this.store.getWorkMailbox(target))) {
493
494
  throw new Error(`Role runtime did not stop: ${input.taskId}/${input.roleName}.`);
494
495
  }
495
- const session = this.store.getRoleSession(input.taskId, input.roleName);
496
- if (session !== null
497
- && session.status !== "ended"
498
- && session.agentId === input.agentId
499
- && session.adapterId === input.adapterId
500
- && session.nativeSessionId === input.nativeSessionId) {
501
- throw new Error(`Role runtime session is still active: ${input.taskId}/${input.roleName}.`);
502
- }
496
+ // Cleanup completion proves the requested stop. A retained Message may
497
+ // already have started a successor on the same reusable conversation;
498
+ // its new active state must not retroactively turn this stop into failure.
503
499
  }
504
500
  /** Wait until every cancellation requested by Task execution stop is physically settled. */
505
501
  async stopTaskDurableJobs(taskId) {
@@ -563,7 +559,7 @@ export class FileTaskWorkflowRuntime {
563
559
  if (blockers.length === 0)
564
560
  return;
565
561
  throw new WorkspaceCleanupBlockedError("physical-resource-live", `task:${taskId}`, true, `Task archive blocked: ${blockers.length} owned physical resource(s) still live: `
566
- + blockers.map((entry) => (`${entry.owner.roleName}/${entry.runtimeGenerationId}`
562
+ + blockers.map((entry) => (`${entry.owner.roleName}/${entry.nativeSessionId ?? "unknown-session"}`
567
563
  + ` (pid ${entry.physical?.alive === true ? entry.physical.pid : "?"})`)).join("; "));
568
564
  }
569
565
  async stopGlobalRoleSession(roleName) {
@@ -622,27 +618,9 @@ export class FileTaskWorkflowRuntime {
622
618
  if (termination.outcome !== "stop-confirmed") {
623
619
  throw new Error(`Role runtime cleanup could not prove physical exit: ${runtimeOwnerLabel(candidate.owner)}; `
624
620
  + termination.remaining
625
- .map(({ record, detail }) => `${record.runtimeGenerationId}: ${detail}`)
621
+ .map(({ record, detail }) => `PID ${record.providerRoot.pid}: ${detail}`)
626
622
  .join("; "));
627
623
  }
628
- if (candidate.owner.scope === "task" && candidate.runtimeGenerationId !== undefined) {
629
- const isolation = new FileTaskRuntimeIsolation({
630
- runtimeRoot: `${this.home}.task-runtimes`,
631
- controlPlane: {
632
- yuiHome: this.home,
633
- controllerSocketPath: controllerSocketPath(this.store.getHomeIdentity().homeId),
634
- tmuxNamespace: yuiTmuxServerName(this.home),
635
- globalInstallPaths: [process.execPath]
636
- }
637
- });
638
- isolation.cleanupTaskLaunch({
639
- taskId: candidate.owner.taskId,
640
- runtimeGenerationId: candidate.runtimeGenerationId,
641
- reason: this.store.getTask(candidate.owner.taskId)?.status === "completed"
642
- ? "completion"
643
- : "interruption"
644
- });
645
- }
646
624
  if (!this.schedulerStore.completeRuntimeCleanup(target, new Date())) {
647
625
  throw new Error(`Role runtime cleanup state changed before completion: ${runtimeOwnerLabel(candidate.owner)}.`);
648
626
  }
@@ -746,11 +724,6 @@ function isDefinitelyNotRunning(error) {
746
724
  const code = error.code;
747
725
  return code === "CONTROLLER_NOT_RUNNING";
748
726
  }
749
- function isInvalidDiscovery(error) {
750
- if (typeof error !== "object" || error === null || !("code" in error))
751
- return false;
752
- return error.code === "CONTROLLER_DISCOVERY_INVALID";
753
- }
754
727
  async function readOptionalControllerStatus(home, call) {
755
728
  try {
756
729
  return await call(home, "controller.status", {});