@zq-silk/yui 0.13.9 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/ARCHITECTURE.md +69 -428
  2. package/README.md +108 -298
  3. package/dist/agent/managedRuntimeEnvironment.js +2 -2
  4. package/dist/cli/commandCatalog.js +39 -77
  5. package/dist/cli/interactionCandidates.js +4 -4
  6. package/dist/cli/interactionPolicy.js +7 -10
  7. package/dist/cli/invocationRouter.js +1 -1
  8. package/dist/cli/updateCommand.js +6 -27
  9. package/dist/cli/updateOrchestrator.js +63 -484
  10. package/dist/cli/updatePorts.js +15 -333
  11. package/dist/cli/upgradeCommand.js +8 -65
  12. package/dist/cli.js +57 -242
  13. package/dist/commands/configCommands.js +11 -62
  14. package/dist/commands/executionAuditCommands.js +23 -23
  15. package/dist/commands/globalRoleCommands.js +2 -7
  16. package/dist/commands/jobCommands.js +1 -4
  17. package/dist/commands/projectCommands.js +5 -5
  18. package/dist/commands/releaseCommands.js +3 -10
  19. package/dist/commands/resourcesCommands.js +2 -2
  20. package/dist/commands/sessionCommands.js +2 -2
  21. package/dist/commands/taskActor.js +31 -39
  22. package/dist/commands/taskChangeSetCommands.js +5 -12
  23. package/dist/commands/taskCommands.js +464 -1249
  24. package/dist/commands/taskContextCommand.js +35 -36
  25. package/dist/commands/taskExecutionCommands.js +33 -46
  26. package/dist/commands/taskInputCommands.js +20 -42
  27. package/dist/commands/taskNextActionCommand.js +9 -9
  28. package/dist/commands/taskOverviewCommand.js +56 -95
  29. package/dist/commands/taskRoleRuntimeStatus.js +77 -107
  30. package/dist/commands/taskUpstreamCommands.js +3 -3
  31. package/dist/commands/taskWorkspaceCommands.js +7 -79
  32. package/dist/commands/telemetryCommands.js +32 -124
  33. package/dist/commands/workflowCommands.js +3 -3
  34. package/dist/completion/fileCompletionManager.js +1 -1
  35. package/dist/config/configCatalog.js +4 -6
  36. package/dist/config/yuiConfig.js +6 -43
  37. package/dist/context/dispatchContext.js +14 -30
  38. package/dist/context/roleSessionContext.js +2 -2
  39. package/dist/context/sessionBootstrapManifest.js +2 -2
  40. package/dist/context/{runContextPack.js → turnContextPack.js} +58 -73
  41. package/dist/context/turnInputContract.js +204 -0
  42. package/dist/context/wakeNotification.js +2 -2
  43. package/dist/controller/agentRuntimeObserver.js +18 -18
  44. package/dist/controller/clientRuntime.js +13 -20
  45. package/dist/controller/controller.js +89 -116
  46. package/dist/controller/fileSchedulerStoreAdapter.js +511 -742
  47. package/dist/controller/jobControl.js +17 -17
  48. package/dist/controller/resourceInventory.js +6 -6
  49. package/dist/controller/resourceInventoryLinux.js +38 -59
  50. package/dist/controller/resourceInventoryRpc.js +3 -3
  51. package/dist/controller/resourceInventoryWorker.js +1 -1
  52. package/dist/controller/runtime.js +46 -53
  53. package/dist/controller/runtimeEventInbox.js +23 -31
  54. package/dist/controller/runtimeEventProcessor.js +15 -15
  55. package/dist/controller/{runtimeHookRunFence.js → runtimeHookTurnFence.js} +87 -81
  56. package/dist/controller/runtimeLaunchCoordinator.js +3 -3
  57. package/dist/controller/runtimeObservationHook.js +11 -8
  58. package/dist/controller/sessionNotify.js +11 -11
  59. package/dist/controller/structuredProviderObservation.js +89 -16
  60. package/dist/coordination/workMailbox.js +309 -579
  61. package/dist/coordination/workMailboxQueue.js +5 -6
  62. package/dist/core/controllerClient.js +1 -91
  63. package/dist/core/controllerServer.js +3 -6
  64. package/dist/doctor/doctor.js +51 -314
  65. package/dist/execution/candidateConvergence.js +3 -3
  66. package/dist/execution/executionGroup.js +32 -33
  67. package/dist/execution/executionHealth.js +36 -38
  68. package/dist/execution/resourceBroker.js +12 -12
  69. package/dist/executor/agentExecutor.js +30 -265
  70. package/dist/executor/effectiveLaunch.js +9 -19
  71. package/dist/executor/executorRegistry.js +43 -17
  72. package/dist/executor/fileRoleLaunchPlanner.js +56 -56
  73. package/dist/executor/launchPlan.js +3 -3
  74. package/dist/executor/workspacePreflightClassification.js +16 -16
  75. package/dist/grant/capabilityGrant.js +20 -25
  76. package/dist/input/inputRequest.js +12 -12
  77. package/dist/integration/changeSet.js +9 -13
  78. package/dist/integration/changeSetManifest.js +1 -1
  79. package/dist/integration/gitIntegrationService.js +2 -2
  80. package/dist/integration/integrationQueueService.js +5 -21
  81. package/dist/integration/overlapDiagnostics.js +2 -2
  82. package/dist/interaction/operatorPresentation.js +1 -1
  83. package/dist/job/jobRunner.js +1 -1
  84. package/dist/lifecycle/canonicalLifecycleEvent.js +25 -25
  85. package/dist/lifecycle/{exactRunTerminalization.js → exactTurnTerminalization.js} +116 -232
  86. package/dist/message/message.js +6 -6
  87. package/dist/observability/executionAudit.js +74 -86
  88. package/dist/observability/faultClassification.js +7 -24
  89. package/dist/observability/orchestrationMetrics.js +5 -5
  90. package/dist/observability/runtimeIdentity.js +34 -101
  91. package/dist/profile/agentProfile.js +1 -1
  92. package/dist/release/cliHomeReleaseFence.js +3 -3
  93. package/dist/release/releaseWorkflowEngine.js +6 -10
  94. package/dist/release/releaseWorkflowPorts.js +2 -2
  95. package/dist/release/workflowFileLock.js +1 -1
  96. package/dist/repository/checkoutSwap.js +1 -1
  97. package/dist/repository/gitWorkspace.js +0 -13
  98. package/dist/repository/project.js +1 -1
  99. package/dist/repository/projectMaintenanceLock.js +1 -2
  100. package/dist/repository/taskBaseFreshness.js +13 -13
  101. package/dist/repository/taskWorkspaceCoordinator.js +21 -25
  102. package/dist/repository/taskWorkspaceIdentity.js +8 -21
  103. package/dist/repository/taskWorkspacePreparer.js +63 -620
  104. package/dist/resources/autoResourceGc.js +3 -3
  105. package/dist/resources/liveReferences.js +1 -1
  106. package/dist/resources/resourceGc.js +1 -1
  107. package/dist/resources/sqliteResourceRegistry.js +1 -1
  108. package/dist/review/reviewDecision.js +7 -7
  109. package/dist/review/reviewFindingLedger.js +1 -1
  110. package/dist/review/reviewOutcomeClassifier.js +35 -50
  111. package/dist/review/reviewRound.js +14 -25
  112. package/dist/review/reviewerAvailability.js +4 -4
  113. package/dist/review/taskFinalReviewContractResolution.js +41 -0
  114. package/dist/role/role.js +6 -3
  115. package/dist/runtime/agentDriver.js +4 -0
  116. package/dist/runtime/agentHost.js +151 -36
  117. package/dist/runtime/builtinAgentDrivers.js +79 -2
  118. package/dist/runtime/codexAppServerRuntime.js +122 -0
  119. package/dist/runtime/continuationManager.js +3 -5
  120. package/dist/runtime/exactControlPlane.js +17 -42
  121. package/dist/runtime/firstProgressAdvisory.js +1 -1
  122. package/dist/runtime/index.js +2 -2
  123. package/dist/runtime/launchDiagnostics.js +2 -2
  124. package/dist/runtime/lifecycleReservation.js +4 -4
  125. package/dist/runtime/processExitObservation.js +1 -1
  126. package/dist/runtime/processExitOutbox.js +0 -32
  127. package/dist/runtime/promptEnvelope.js +9 -9
  128. package/dist/runtime/providerContinuation.js +2 -2
  129. package/dist/runtime/providerContinuationReconciliationService.js +2 -2
  130. package/dist/runtime/providerRuntimeIdentity.js +76 -24
  131. package/dist/runtime/recentTurnIds.js +38 -0
  132. package/dist/runtime/runtimeContinuationProjection.js +2 -2
  133. package/dist/runtime/runtimeHealthPolicy.js +1 -1
  134. package/dist/runtime/runtimeObservation.js +58 -21
  135. package/dist/runtime/runtimeProjection.js +19 -27
  136. package/dist/runtime/sessionLaunchRequest.js +2 -2
  137. package/dist/runtime/structuredProviderHost.js +177 -12
  138. package/dist/runtime/tmuxAdapters.js +46 -11
  139. package/dist/scheduler/activeRoleTurnDelivery.js +245 -0
  140. package/dist/scheduler/leaderWakeupProcessor.js +147 -445
  141. package/dist/scheduler/operatorEvent.js +4 -6
  142. package/dist/scheduler/resourceQueueProjection.js +71 -0
  143. package/dist/scheduler/{roleRunLiveness.js → roleTurnLiveness.js} +24 -28
  144. package/dist/scheduler/{roleRunStall.js → roleTurnStall.js} +162 -247
  145. package/dist/scheduler/taskExecutionProjection.js +99 -99
  146. package/dist/scheduler/taskObservabilityProjection.js +10 -10
  147. package/dist/scheduler/taskWake.js +4 -4
  148. package/dist/scheduler/wakeReason.js +6 -10
  149. package/dist/scheduler/wakeupQueue.js +0 -9
  150. package/dist/setup/setupCommand.js +2 -2
  151. package/dist/storage/currentTaskStore.js +46 -0
  152. package/dist/storage/persistenceWorker.js +1 -1
  153. package/dist/storage/sqliteSchema.js +81 -485
  154. package/dist/storage/sqliteStore.js +206 -365
  155. package/dist/storage/storageSchema.js +1 -15
  156. package/dist/storage/storageVersions.js +8 -9
  157. package/dist/storage/storeRpc.js +10 -34
  158. package/dist/storage/taskStore.js +63 -3415
  159. package/dist/storage/upgrade/recordVersions.js +55 -149
  160. package/dist/storage/upgrade/upgradeOrchestrator.js +89 -1752
  161. package/dist/task/completionReadiness.js +5 -5
  162. package/dist/task/deliveryGuard.js +10 -10
  163. package/dist/task/nextAction.js +74 -77
  164. package/dist/task/task.js +2 -4
  165. package/dist/task/taskRecordReference.js +3 -3
  166. package/dist/task/taskRecordRetirement.js +1 -1
  167. package/dist/telemetry/sqliteTelemetryStore.js +53 -53
  168. package/dist/telemetry/telemetryConfig.js +14 -14
  169. package/dist/telemetry/telemetryWiring.js +8 -14
  170. package/dist/turn/turn.js +356 -0
  171. package/dist/{run/runIdentity.js → turn/turnIdentity.js} +4 -4
  172. package/dist/verification/gateArtifactStore.js +2 -2
  173. package/dist/web/assets/client/app.js +1 -1
  174. package/dist/web/assets/client/components.js +29 -54
  175. package/dist/web/assets/client/i18n.js +17 -37
  176. package/dist/web/assets/client/view.js +37 -37
  177. package/dist/web/assets/shell.js +1 -1
  178. package/dist/web/assets/styles/cards.js +5 -5
  179. package/dist/web/assets/styles/widgets.js +1 -2
  180. package/dist/web/webSnapshot.js +65 -60
  181. package/dist/workItem/workItem.js +11 -11
  182. package/dist/workspace/workItemChangeSetManager.js +1 -1
  183. package/docs/task-local-identity.md +5 -5
  184. package/i18n/README.zh-CN.md +41 -54
  185. package/package.json +1 -1
  186. package/skills/yui-leader/SKILL.md +85 -83
  187. package/skills/yui-operator/SKILL.md +16 -16
  188. package/skills/yui-reviewer/SKILL.md +10 -10
  189. package/skills/yui-runtime/SKILL.md +17 -18
  190. package/skills/yui-worker/SKILL.md +16 -18
  191. package/dist/context/runContextContract.js +0 -162
  192. package/dist/executor/turnCompletion.js +0 -126
  193. package/dist/review/taskFinalReviewContractEvent.js +0 -1
  194. package/dist/review/taskFinalReviewContractRebind.js +0 -367
  195. package/dist/run/agentRun.js +0 -356
  196. package/dist/run/rejectedYieldAttempt.js +0 -221
  197. package/dist/run/runControlRequest.js +0 -50
  198. package/dist/run/yieldReceipt.js +0 -65
  199. package/dist/runtime/turnCompletion.js +0 -3
  200. package/dist/scheduler/actionability.js +0 -323
  201. package/dist/scheduler/activeRoleRunDelivery.js +0 -868
  202. package/dist/scheduler/activeTaskProgress.js +0 -126
  203. package/dist/storage/compatibleTaskStore.js +0 -248
  204. package/dist/storage/migration/baseline.js +0 -78
  205. package/dist/storage/migration/classifier.js +0 -51
  206. package/dist/storage/migration/compatibleCodec.js +0 -53
  207. package/dist/storage/migration/engine.js +0 -147
  208. package/dist/storage/migration/index.js +0 -33
  209. package/dist/storage/migration/planner.js +0 -154
  210. package/dist/storage/migration/productionRegistry.js +0 -3931
  211. package/dist/storage/migration/registry.js +0 -169
  212. package/dist/storage/migration/report.js +0 -54
  213. package/dist/storage/migration/types.js +0 -31
  214. package/dist/storage/upgrade/homeClassification.js +0 -353
  215. package/dist/storage/upgrade/homeMigrationTarget.js +0 -595
  216. package/dist/storage/upgrade/migrationReceipt.js +0 -67
  217. package/dist/storage/upgrade/offlineUpgradeInventory.js +0 -453
  218. package/dist/storage/upgrade/productionMigrationRegistry.js +0 -6
  219. package/dist/storage/upgrade/pseudoLayoutRepair.js +0 -241
  220. package/dist/storage/upgrade/recordVersionScan.js +0 -176
  221. package/dist/storage/upgrade/sqliteFileSet.js +0 -50
  222. package/dist/storage/upgrade/sqliteMigrationTarget.js +0 -375
  223. package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +0 -305
  224. package/dist/storage/upgrade/sqliteStateMigration.js +0 -918
  225. package/dist/storage/upgrade/switchProgress.js +0 -80
  226. package/dist/storage/upgrade/upgradeReceipt.js +0 -161
  227. package/dist/storage/upgradeCoordination.js +0 -187
  228. package/dist/storage/upgradeFence.js +0 -366
  229. package/dist/telemetry/telemetryCompaction.js +0 -252
@@ -1,5 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
- import { hasRecentTurnId, rememberRecentTurnId, validatePendingTurnCompletion, validateRecentTurnIds } from "./turnCompletion.js";
2
+ import { hasRecentTurnId, rememberRecentTurnId, validateRecentTurnIds } from "../runtime/recentTurnIds.js";
3
3
  import { effectiveLaunchSnapshotsCompatible, effectiveLaunchSnapshotsCompatibleForTaskSession, validateEffectiveLaunchSnapshot } from "./effectiveLaunch.js";
4
4
  import { currentProviderActivation, endProviderActivation, settleProviderTurn, settleProviderTurnSubmission, validateProviderRuntimeBinding } from "../runtime/providerRuntimeIdentity.js";
5
5
  import { builtinDriverIdForAdapter } from "../runtime/builtinAgentDrivers.js";
@@ -14,8 +14,7 @@ export function createRoleSessionSet(owner, activeAgentId, now) {
14
14
  ? { ...base, schemaVersion: 4 }
15
15
  : {
16
16
  ...base,
17
- schemaVersion: 8,
18
- inFlight: null,
17
+ schemaVersion: 11,
19
18
  providerBinding: null
20
19
  };
21
20
  }
@@ -179,9 +178,6 @@ export function updateRoleAgentSessionStatus(set, agentId, status, now, endReaso
179
178
  }
180
179
  export function retireTaskRoleSessionsForWorkspace(set, now) {
181
180
  validateRoleSessionSet(set);
182
- if (set.inFlight !== null) {
183
- throw new Error("Cannot retire a Task Role session with unsettled Run state.");
184
- }
185
181
  const live = Object.values(set.sessions).find(({ status }) => status === "active");
186
182
  if (live !== undefined) {
187
183
  throw new Error(`Task Role session must be stopped before workspace migration: ${live.agentId}.`);
@@ -205,9 +201,6 @@ export function retireTaskRoleSessionsForWorkspace(set, now) {
205
201
  */
206
202
  export function retireConfirmedAbsentInactiveTaskRolePlaceholders(set, now) {
207
203
  validateRoleSessionSet(set);
208
- if (set.inFlight !== null) {
209
- throw new Error("Cannot retire a Task Role placeholder with unsettled Run state.");
210
- }
211
204
  const timestamp = now.toISOString();
212
205
  let changed = false;
213
206
  const sessions = Object.fromEntries(Object.entries(set.sessions).map(([agentId, session]) => {
@@ -295,35 +288,6 @@ export function roleAgentSessionResumeMode(set, agentId, desired) {
295
288
  throw new Error(`Role Agent session is incompatible with the next effective launch: ${agentId}. `
296
289
  + "Stop the existing native process before starting a fresh Session.");
297
290
  }
298
- export function bindTaskRoleRun(set, fence, preparedAt, mode) {
299
- validateRoleSessionSet(set);
300
- assertTaskRoleSessionSet(set);
301
- const normalized = normalizeTaskRoleRunFence(fence);
302
- if (normalized.agentId !== set.activeAgentId) {
303
- throw new Error("Task Role Run Agent does not match the active Agent.");
304
- }
305
- if (set.inFlight !== null) {
306
- if (sameRunFence(set.inFlight, normalized))
307
- return set;
308
- throw new Error("Task Role session set already has an in-flight Run.");
309
- }
310
- const timestamp = requireDate(preparedAt, "Task Role Run preparedAt");
311
- // A fresh Session follows physical cleanup and does not inherit runtime
312
- // evidence from the disposable old Conversation. A resumed Session keeps
313
- // that evidence unchanged here: claiming a durable Run is a workflow
314
- // mutation, not a Provider operation. AgentHost binds the Conversation to
315
- // this Run only when it can actually submit the next serialized Turn.
316
- const providerBinding = mode === "new"
317
- ? null
318
- : set.providerBinding;
319
- const updated = {
320
- ...set,
321
- inFlight: { ...normalized, preparedAt: timestamp },
322
- providerBinding,
323
- updatedAt: timestamp
324
- };
325
- return validateRoleSessionSet(updated);
326
- }
327
291
  export function bindTaskRoleProviderRuntime(set, binding, updatedAt) {
328
292
  validateRoleSessionSet(set);
329
293
  const normalized = validateProviderRuntimeBinding(binding);
@@ -382,9 +346,9 @@ export function detachRoleAgentSessionHost(set, now) {
382
346
  let binding = taskSet.providerBinding;
383
347
  const turn = binding?.turn;
384
348
  if (binding !== null && binding !== undefined && turn !== null && turn !== undefined
385
- && ["accepted", "running"].includes(turn.status)) {
349
+ && turn.status === "accepted") {
386
350
  binding = settleProviderTurn(binding, {
387
- turnId: turn.turnId,
351
+ nativeTurnId: turn.nativeTurnId,
388
352
  status: "cancelled",
389
353
  settledAt: timestamp,
390
354
  reason: "runtime-physical-exit"
@@ -413,51 +377,13 @@ export function detachRoleAgentSessionHost(set, now) {
413
377
  }
414
378
  return taskSet;
415
379
  }
416
- export function markTaskRoleRunPushed(set, fence, pushedAt) {
417
- validateRoleSessionSet(set);
418
- assertTaskRoleSessionSet(set);
419
- const inFlight = requireMatchingInFlight(set, fence);
420
- if (inFlight.pushedAt !== undefined)
421
- return set;
422
- const timestamp = requireDate(pushedAt, "Task Role Run pushedAt");
423
- if (Date.parse(timestamp) < Date.parse(inFlight.preparedAt)) {
424
- throw new Error("Task Role Run pushedAt must not be earlier than preparedAt.");
425
- }
426
- const updated = {
427
- ...set,
428
- inFlight: { ...inFlight, pushedAt: timestamp },
429
- updatedAt: timestamp
430
- };
431
- return validateRoleSessionSet(updated);
432
- }
433
- export function markTaskRoleRunDelivered(set, fence, deliveredAt) {
434
- validateRoleSessionSet(set);
435
- assertTaskRoleSessionSet(set);
436
- const inFlight = requireMatchingInFlight(set, fence);
437
- if (inFlight.deliveredAt !== undefined)
438
- return set;
439
- const timestamp = requireDate(deliveredAt, "Task Role Run deliveredAt");
440
- if (Date.parse(timestamp) < Date.parse(inFlight.preparedAt)) {
441
- throw new Error("Task Role Run deliveredAt must not be earlier than preparedAt.");
442
- }
443
- const updated = {
444
- ...set,
445
- inFlight: {
446
- ...inFlight,
447
- ...(inFlight.pushedAt === undefined ? { pushedAt: timestamp } : {}),
448
- deliveredAt: timestamp
449
- },
450
- updatedAt: timestamp
451
- };
452
- return validateRoleSessionSet(updated);
453
- }
454
380
  /**
455
- * Records a provider Turn boundary without changing the durable Yui Run.
381
+ * Records a Provider activity boundary without changing the durable Yui Turn.
456
382
  *
457
383
  * A native session may finish one foreground Turn while provider-owned
458
384
  * subagents, mailbox work, or later user corrections still belong to the same
459
- * application-level Run. Only an explicit Yui workflow outcome may clear the
460
- * Run fence; this transition merely makes the native session available for a
385
+ * application-level Turn. Only the native Provider terminal may clear the
386
+ * Turn fence; this transition merely makes the native Session available for a
461
387
  * subsequent input and remembers the provider Turn idempotently.
462
388
  */
463
389
  export function recordTaskRoleTurnBoundary(set, input, completedAt) {
@@ -472,9 +398,6 @@ export function recordTaskRoleTurnBoundary(set, input, completedAt) {
472
398
  }
473
399
  if (session.recentCompletedTurnIds.includes(turnId))
474
400
  return set;
475
- if (set.inFlight !== null && set.inFlight.agentId !== agentId) {
476
- throw new Error("Completed Turn Agent does not match the in-flight Run.");
477
- }
478
401
  const timestamp = requireDate(completedAt, "Turn completedAt");
479
402
  return validateRoleSessionSet({
480
403
  ...set,
@@ -489,94 +412,19 @@ export function recordTaskRoleTurnBoundary(set, input, completedAt) {
489
412
  updatedAt: timestamp
490
413
  });
491
414
  }
492
- export function recordObservedTaskRoleCompletion(set, completion) {
493
- const observed = validatePendingTurnCompletion(completion);
494
- return recordTaskRoleTurnBoundary(set, {
495
- agentId: observed.agentId,
496
- nativeSessionId: observed.nativeSessionId,
497
- turnId: observed.turnId
498
- }, new Date(observed.observedAt));
499
- }
500
- /**
501
- * Rebinds the same active Run to a bounded control-input receipt while keeping
502
- * its observed native Turn completion unsettled. No new Run fence is created.
503
- */
504
- export function rebindTaskRoleRunControlReceipt(set, input, now) {
505
- validateRoleSessionSet(set);
506
- assertTaskRoleSessionSet(set);
507
- const inFlight = set.inFlight;
508
- if (inFlight === null
509
- || inFlight.agentId !== input.agentId
510
- || inFlight.runId !== input.runId) {
511
- throw new Error("Workflow outcome control request does not match the unsettled Run.");
512
- }
513
- const timestamp = requireDate(now, "Workflow outcome control receipt timestamp");
514
- return validateRoleSessionSet({
515
- ...set,
516
- inFlight: {
517
- ...inFlight,
518
- receiptId: requireSafeIdentity(input.receiptId, "Workflow outcome control receipt id")
519
- },
520
- updatedAt: timestamp
521
- });
522
- }
523
- export function clearTaskRoleRun(set, fence, clearedAt) {
524
- validateRoleSessionSet(set);
525
- assertTaskRoleSessionSet(set);
526
- const inFlight = requireMatchingInFlight(set, fence);
527
- const timestamp = requireDate(clearedAt, "Task Role Run clearedAt");
528
- if (Date.parse(timestamp) < Date.parse(inFlight.preparedAt)) {
529
- throw new Error("Task Role Run clearedAt must not be earlier than preparedAt.");
530
- }
531
- const updated = {
532
- ...set,
533
- inFlight: null,
534
- updatedAt: timestamp
535
- };
536
- return validateRoleSessionSet(updated);
537
- }
538
- /**
539
- * Applies an authoritative application-level Run terminal fact to its exact
540
- * delivery receipt. Provider lifecycle remains independent: only the matching
541
- * native Turn boundary may move the Session from running to ready. AgentHost
542
- * serializes a retained next delivery while that Turn is still active.
543
- */
544
- export function terminalizeTaskRoleRunSession(set, fence, terminalAt) {
545
- validateRoleSessionSet(set);
546
- const inFlight = set.inFlight;
547
- return inFlight === null ? set : clearTaskRoleRun(set, fence, terminalAt);
548
- }
549
- export function settleTaskRoleCompletion(set, expected, settledAt) {
550
- validateRoleSessionSet(set);
551
- assertTaskRoleSessionSet(set);
552
- const agentId = requireSafeIdentity(expected.agentId, "Agent id");
553
- const runId = requireSafeIdentity(expected.runId, "Run id");
554
- const turnId = requireText(expected.turnId, "Turn id");
555
- const inFlight = set.inFlight;
556
- if (inFlight === null || inFlight.agentId !== agentId || inFlight.runId !== runId) {
557
- throw new Error("Pending Turn completion does not match the in-flight Run.");
558
- }
559
- const session = set.sessions[agentId];
560
- if (session === undefined || !session.recentCompletedTurnIds.includes(turnId)) {
561
- throw new Error("Turn completion has not been observed for the Role Agent session.");
562
- }
563
- const timestamp = requireDate(settledAt, "Turn settledAt");
564
- const updated = {
565
- ...set,
566
- sessions: {
567
- ...set.sessions,
568
- [agentId]: {
569
- ...session,
570
- recentCompletedTurnIds: session.recentCompletedTurnIds,
571
- updatedAt: timestamp
572
- }
573
- },
574
- inFlight: null,
575
- updatedAt: timestamp
576
- };
577
- return validateRoleSessionSet(updated);
578
- }
579
415
  export function validateRoleSessionSet(set) {
416
+ const ownerScope = set.owner?.scope;
417
+ rejectUnknownFields(set, ownerScope === "global"
418
+ ? ["schemaVersion", "owner", "activeAgentId", "sessions", "updatedAt", "history"]
419
+ : [
420
+ "schemaVersion",
421
+ "owner",
422
+ "activeAgentId",
423
+ "sessions",
424
+ "updatedAt",
425
+ "history",
426
+ "providerBinding"
427
+ ], "Role session set");
580
428
  normalizeOwner(set.owner);
581
429
  requireSafeIdentity(set.activeAgentId, "Active Agent id");
582
430
  for (const [agentId, session] of Object.entries(set.sessions)) {
@@ -586,8 +434,7 @@ export function validateRoleSessionSet(set) {
586
434
  if (set.schemaVersion !== 4) {
587
435
  throw new Error("Global Role session set schema version is invalid.");
588
436
  }
589
- if (Object.hasOwn(set, "inFlight")
590
- || Object.hasOwn(set, "providerBinding")) {
437
+ if (Object.hasOwn(set, "providerBinding")) {
591
438
  throw new Error("Global Role session set must not contain Task Role lifecycle fields.");
592
439
  }
593
440
  const history = set.history;
@@ -602,12 +449,11 @@ export function validateRoleSessionSet(set) {
602
449
  }
603
450
  }
604
451
  else {
605
- if (set.schemaVersion !== 8) {
452
+ if (set.schemaVersion !== 11) {
606
453
  throw new Error("Task Role session set schema version is invalid.");
607
454
  }
608
- if (!Object.hasOwn(set, "inFlight")
609
- || !Object.hasOwn(set, "providerBinding")) {
610
- throw new Error("Task Role session set must contain its Turn fence.");
455
+ if (!Object.hasOwn(set, "providerBinding")) {
456
+ throw new Error("Task Role session set must contain its Provider Runtime Binding.");
611
457
  }
612
458
  const taskSet = set;
613
459
  if (taskSet.history !== undefined) {
@@ -632,17 +478,11 @@ export function validateRoleSessionSet(set) {
632
478
  }
633
479
  }
634
480
  }
635
- const inFlight = taskSet.inFlight === null
636
- ? null
637
- : validateTaskRoleInFlight(taskSet.inFlight);
638
481
  const providerBinding = taskSet.providerBinding === null
639
482
  ? null
640
483
  : validateProviderRuntimeBinding(taskSet.providerBinding);
641
- if (inFlight !== null && inFlight.agentId !== set.activeAgentId) {
642
- throw new Error("Task Role in-flight Run Agent must be active.");
643
- }
644
484
  if (providerBinding !== null) {
645
- const session = taskSet.sessions[inFlight?.agentId ?? set.activeAgentId];
485
+ const session = taskSet.sessions[set.activeAgentId];
646
486
  if (session === undefined) {
647
487
  throw new Error("Provider Runtime Binding has no active Role Agent session.");
648
488
  }
@@ -732,93 +572,12 @@ function assertTaskRoleSessionSet(set) {
732
572
  throw new Error("Turn fences require a Task Role session set.");
733
573
  }
734
574
  }
735
- function normalizeTaskRoleRunFence(fence) {
736
- return {
737
- agentId: requireSafeIdentity(fence.agentId, "Agent id"),
738
- runId: requireSafeIdentity(fence.runId, "Run id"),
739
- receiptId: requireText(fence.receiptId, "Delivery receipt id")
740
- };
741
- }
742
- function requireMatchingInFlight(set, fence) {
743
- const normalized = normalizeTaskRoleRunFence(fence);
744
- const inFlight = set.inFlight;
745
- if (inFlight === null)
746
- throw new Error("Task Role session set has no in-flight Run.");
747
- if (inFlight.agentId !== normalized.agentId) {
748
- throw new Error("Task Role Run Agent does not match the in-flight Run.");
749
- }
750
- if (inFlight.runId !== normalized.runId) {
751
- throw new Error("Task Role Run does not match the in-flight Run.");
752
- }
753
- if (inFlight.receiptId !== normalized.receiptId) {
754
- throw new Error("Task Role Run receipt does not match the in-flight Run.");
755
- }
756
- return inFlight;
757
- }
758
- function validateTaskRoleInFlight(value) {
759
- const fields = Object.keys(value);
760
- const unknown = fields.find((field) => !["agentId", "runId", "receiptId", "preparedAt", "pushedAt", "deliveredAt"].includes(field));
761
- if (unknown !== undefined) {
762
- throw new Error(`Task Role in-flight Run has unknown field: ${unknown}.`);
763
- }
764
- for (const required of ["agentId", "runId", "receiptId", "preparedAt"]) {
765
- if (!Object.hasOwn(value, required)) {
766
- throw new Error(`Task Role in-flight Run is missing field: ${required}.`);
767
- }
768
- }
769
- const preparedAt = requirePersistedTimestamp(value.preparedAt, "Task Role Run preparedAt");
770
- if (value.pushedAt !== undefined) {
771
- const pushedAt = requirePersistedTimestamp(value.pushedAt, "Task Role Run pushedAt");
772
- if (Date.parse(pushedAt) < Date.parse(preparedAt)) {
773
- throw new Error("Task Role Run pushedAt must not be earlier than preparedAt.");
774
- }
775
- }
776
- if (value.deliveredAt !== undefined) {
777
- const deliveredAt = requirePersistedTimestamp(value.deliveredAt, "Task Role Run deliveredAt");
778
- if (Date.parse(deliveredAt) < Date.parse(preparedAt)) {
779
- throw new Error("Task Role Run deliveredAt must not be earlier than preparedAt.");
780
- }
781
- if (value.pushedAt === undefined) {
782
- throw new Error("Task Role Run deliveredAt requires a prior pushedAt.");
783
- }
784
- }
785
- requireSafeIdentity(value.agentId, "Agent id");
786
- requireSafeIdentity(value.runId, "Run id");
787
- requireText(value.receiptId, "Delivery receipt id");
788
- return value;
789
- }
790
- function assertCompletionOwner(set, completion) {
791
- if (completion.taskId !== set.owner.taskId) {
792
- throw new Error("Observed Turn Task does not match the Task Role session set.");
793
- }
794
- if (completion.roleName !== set.owner.roleName) {
795
- throw new Error("Observed Turn Role does not match the Task Role session set.");
796
- }
797
- if (completion.agentId !== set.activeAgentId) {
798
- throw new Error("Observed Turn Agent does not match the active Agent.");
799
- }
800
- }
801
- function sameRunFence(inFlight, fence) {
802
- return inFlight.agentId === fence.agentId
803
- && inFlight.runId === fence.runId
804
- && inFlight.receiptId === fence.receiptId;
805
- }
806
- function samePendingTurnCompletion(left, right) {
807
- return JSON.stringify(left) === JSON.stringify(right);
808
- }
809
575
  function requireDate(value, label) {
810
576
  if (!(value instanceof Date) || !Number.isFinite(value.getTime())) {
811
577
  throw new Error(`${label} must be a valid date.`);
812
578
  }
813
579
  return value.toISOString();
814
580
  }
815
- function requirePersistedTimestamp(value, label) {
816
- const timestamp = Date.parse(value);
817
- if (!Number.isFinite(timestamp) || new Date(timestamp).toISOString() !== value) {
818
- throw new Error(`${label} must be a canonical timestamp.`);
819
- }
820
- return value;
821
- }
822
581
  function normalizeOwner(owner) {
823
582
  const roleName = requireSafeIdentity(owner.roleName, "Role name");
824
583
  return owner.scope === "global"
@@ -840,6 +599,12 @@ function requireSafeIdentity(value, label) {
840
599
  }
841
600
  return normalized;
842
601
  }
602
+ function rejectUnknownFields(value, fields, label) {
603
+ const allowed = new Set(fields);
604
+ const unknown = Object.keys(value).find((field) => !allowed.has(field));
605
+ if (unknown !== undefined)
606
+ throw new Error(`${label} has unknown field: ${unknown}.`);
607
+ }
843
608
  function requireText(value, label) {
844
609
  if (typeof value !== "string" || value.includes("\0"))
845
610
  throw new Error(`${label} is invalid.`);
@@ -79,7 +79,7 @@ export function effectiveLaunchSnapshotsCompatible(existing, desired) {
79
79
  return isDeepStrictEqual(withoutDesiredRevision(existing), withoutDesiredRevision(desired));
80
80
  }
81
81
  /**
82
- * Task Role Sessions keep one physical workspace while Run-scoped facts move.
82
+ * Task Role Sessions keep one physical workspace while Turn-scoped facts move.
83
83
  * Candidate commits, ReviewRound identity and desired-revision bookkeeping do
84
84
  * not define a native Session. Agent, adapter, permission, model, sandbox,
85
85
  * manifest, Role context and physical workspace identity still do.
@@ -99,7 +99,7 @@ export function effectiveLaunchWithTaskMainWorkspace(existing, workspace) {
99
99
  validateEffectiveLaunchSnapshot(existing);
100
100
  validateManagedWorkspace(workspace);
101
101
  if (workspace.owner.type !== "task") {
102
- throw new Error("Only a Task-owned main workspace may refresh fixed Session Run evidence.");
102
+ throw new Error("Only a Task-owned main workspace may refresh fixed Session Turn evidence.");
103
103
  }
104
104
  return validateEffectiveLaunchSnapshot({
105
105
  ...existing,
@@ -120,8 +120,8 @@ function taskSessionCompatibleSnapshot(snapshot) {
120
120
  };
121
121
  }
122
122
  export function validateEffectiveLaunchSnapshot(snapshot) {
123
- if (snapshot.schemaVersion !== 2) {
124
- throw new Error("Effective launch snapshot must use schemaVersion 2.");
123
+ if (snapshot.schemaVersion !== 3) {
124
+ throw new Error("Effective launch snapshot must use schemaVersion 3.");
125
125
  }
126
126
  positiveInteger(snapshot.sourceDesiredRevision, "Source desired revision");
127
127
  identity(snapshot.agentId, "Effective Agent id");
@@ -148,18 +148,12 @@ export function validateEffectiveLaunchSnapshot(snapshot) {
148
148
  identity(snapshot.reviewRoundId, "Effective ReviewRound id");
149
149
  commit(snapshot.reviewBaseCommit, "Effective review base commit");
150
150
  }
151
- if (snapshot.contextProtocolVersion !== undefined
152
- && snapshot.contextProtocolVersion !== SESSION_BOOTSTRAP_MANIFEST_SCHEMA_VERSION) {
151
+ if (snapshot.contextProtocolVersion !== SESSION_BOOTSTRAP_MANIFEST_SCHEMA_VERSION) {
153
152
  throw new Error("Effective launch context protocol version is unsupported.");
154
153
  }
155
- if (snapshot.sessionManifestCompatibilityDigest !== undefined
156
- && !/^[a-f0-9]{64}$/u.test(snapshot.sessionManifestCompatibilityDigest)) {
154
+ if (!/^[a-f0-9]{64}$/u.test(snapshot.sessionManifestCompatibilityDigest)) {
157
155
  throw new Error("Effective launch Session Manifest compatibility digest is invalid.");
158
156
  }
159
- if ((snapshot.contextProtocolVersion === undefined)
160
- !== (snapshot.sessionManifestCompatibilityDigest === undefined)) {
161
- throw new Error("Effective launch Context protocol compatibility identity is incomplete.");
162
- }
163
157
  validateWorkspace(snapshot.workspace);
164
158
  cloneContext(snapshot.context);
165
159
  const config = effectiveLaunchConfigUnchecked(snapshot);
@@ -198,7 +192,7 @@ function snapshotFromConfig(input) {
198
192
  reviewBaseCommit: commit(input.reviewBaseCommit ?? "", "Review base commit")
199
193
  };
200
194
  const common = {
201
- schemaVersion: 2,
195
+ schemaVersion: 3,
202
196
  sourceDesiredRevision: positiveInteger(input.sourceDesiredRevision, "Source desired revision"),
203
197
  agentId: identity(input.agentId, "Effective Agent id"),
204
198
  profileAccess: input.profileAccess,
@@ -212,12 +206,8 @@ function snapshotFromConfig(input) {
212
206
  writeProjectIds: [...input.writeProjectIds],
213
207
  workspace: cloneWorkspace(input.workspace),
214
208
  context: cloneContext(input.context),
215
- ...(input.contextProtocolVersion === undefined
216
- ? {}
217
- : { contextProtocolVersion: input.contextProtocolVersion }),
218
- ...(input.sessionManifestCompatibilityDigest === undefined
219
- ? {}
220
- : { sessionManifestCompatibilityDigest: input.sessionManifestCompatibilityDigest }),
209
+ contextProtocolVersion: input.contextProtocolVersion,
210
+ sessionManifestCompatibilityDigest: input.sessionManifestCompatibilityDigest,
221
211
  ...review
222
212
  };
223
213
  const snapshot = config.adapterId === "codex"
@@ -1,10 +1,10 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { createPromptEnvelope, createSessionLaunchRequest } from "../runtime/index.js";
3
- import { formatAgentRunReceiptId } from "../task/taskRecordReference.js";
3
+ import { formatTurnReceiptId } from "../task/taskRecordReference.js";
4
4
  /**
5
5
  * rr13/test: Test-only liveness seam. Integration tests that spawn a real
6
6
  * Controller subprocess cannot inject a fake TmuxDeliveryPort, and a saved
7
- * active Leader Run would be reaped by the startup liveness pass without a
7
+ * active Leader Turn would be reaped by the startup liveness pass without a
8
8
  * real tmux role. When this env var is "1", every role reads "present"
9
9
  * without probing tmux. The Controller subprocess inherits it from the
10
10
  * test's CLI env. Never set in production.
@@ -12,7 +12,7 @@ import { formatAgentRunReceiptId } from "../task/taskRecordReference.js";
12
12
  const TEST_ROLE_LIVENESS_PRESENT = process.env.YUI_TEST_ROLE_LIVENESS_PRESENT === "1";
13
13
  /**
14
14
  * Scheduler-to-tmux adapter. It retains only in-process prepared launch data;
15
- * durable session identity remains owned by FileTaskStore.
15
+ * durable session identity remains owned by TaskStore.
16
16
  */
17
17
  export class ExecutorRegistry {
18
18
  planner;
@@ -38,7 +38,7 @@ export class ExecutorRegistry {
38
38
  agentId: input.agentId,
39
39
  adapterId: input.adapterId,
40
40
  mode: input.mode,
41
- ...(input.runId === undefined ? {} : { runId: input.runId })
41
+ ...(input.turnId === undefined ? {} : { turnId: input.turnId })
42
42
  };
43
43
  const cached = this.#prepared.get(deliveryBase.deliveryId);
44
44
  if (cached !== undefined)
@@ -74,7 +74,7 @@ export class ExecutorRegistry {
74
74
  ...(input.runtimePolicy === undefined
75
75
  ? {}
76
76
  : { runtimePolicy: input.runtimePolicy }),
77
- ...(input.runId === undefined ? {} : { runId: input.runId })
77
+ ...(input.turnId === undefined ? {} : { turnId: input.turnId })
78
78
  };
79
79
  if (this.runtimePorts.launchCoordinator !== undefined) {
80
80
  binding = await this.runtimePorts.launchCoordinator.prepare(input.mode === "new"
@@ -141,13 +141,13 @@ export class ExecutorRegistry {
141
141
  async sendOnce(input) {
142
142
  const prepared = this.requirePrepared(input.delivery.prepared);
143
143
  if (prepared.binding !== undefined && this.runtimePorts !== undefined) {
144
- const runId = input.delivery.prepared.runId;
145
- if (runId === undefined) {
146
- throw new Error("Runtime prompt delivery requires a Task-local Run id.");
144
+ const turnId = input.delivery.prepared.turnId;
145
+ if (turnId === undefined) {
146
+ throw new Error("Runtime prompt delivery requires a Task-local Turn id.");
147
147
  }
148
148
  // A reused native process retains the stable descriptor path from its
149
149
  // original control plane. Publish only the current-control source after
150
- // the Run/Session fence is durable and immediately before provider input;
150
+ // the Turn/Session fence is durable and immediately before Provider input;
151
151
  // the reused Hook self-refreshes its own source before the volatile
152
152
  // fence instead of the Controller scanning history to keep it fresh.
153
153
  if (prepared.binding.hostCreated === false
@@ -158,7 +158,7 @@ export class ExecutorRegistry {
158
158
  this.planner.refreshTaskRuntimeDescriptor({
159
159
  taskId: input.delivery.prepared.taskId,
160
160
  roleName: input.delivery.prepared.roleName,
161
- runId,
161
+ turnId,
162
162
  launchId: prepared.binding.launchId,
163
163
  nativeSessionId: prepared.binding.nativeSessionId,
164
164
  agentId: prepared.binding.agentId,
@@ -171,9 +171,9 @@ export class ExecutorRegistry {
171
171
  envelope: createPromptEnvelope({
172
172
  id: input.receiptId,
173
173
  source: {
174
- kind: input.receiptId === formatAgentRunReceiptId(input.delivery.prepared.taskId, runId) ? "agent-run" : "run-input",
174
+ kind: input.receiptId === formatTurnReceiptId(input.delivery.prepared.taskId, turnId) ? "turn" : "turn-input",
175
175
  taskId: input.delivery.prepared.taskId,
176
- localId: runId
176
+ localId: turnId
177
177
  },
178
178
  text: input.text,
179
179
  createdAt: new Date()
@@ -190,6 +190,26 @@ export class ExecutorRegistry {
190
190
  }
191
191
  return outcome;
192
192
  }
193
+ async steerOnce(input) {
194
+ if (this.runtimePorts === undefined)
195
+ return "unavailable";
196
+ const outcome = await this.runtimePorts.promptPush.trySteer({
197
+ owner: { scope: "task", taskId: input.taskId, roleName: input.roleName },
198
+ launchId: input.launchId,
199
+ agentId: input.agentId,
200
+ adapterId: input.adapterId,
201
+ nativeSessionId: input.nativeSessionId,
202
+ nativeTurnId: input.nativeTurnId,
203
+ providerAuthority: input.authority,
204
+ envelope: createPromptEnvelope({
205
+ id: input.receiptId,
206
+ source: { kind: "turn-input", taskId: input.taskId, localId: activeTurnId(input.receiptId) },
207
+ text: input.text,
208
+ createdAt: new Date()
209
+ })
210
+ });
211
+ return outcome === "delivered" ? "sent" : outcome;
212
+ }
193
213
  async notifyOperatorInputOnce(input) {
194
214
  const probe = this.readiness(input.adapterId, "operator");
195
215
  return this.tmux.sendRoleInputOnceIfReadyAsync === undefined
@@ -201,8 +221,8 @@ export class ExecutorRegistry {
201
221
  const delivery = prepared.delivery;
202
222
  if (delivery.taskId !== input.taskId
203
223
  || delivery.roleName !== input.roleName
204
- || (input.runId !== undefined
205
- && delivery.runId !== input.runId)
224
+ || (input.turnId !== undefined
225
+ && delivery.turnId !== input.turnId)
206
226
  || (input.launchId !== undefined
207
227
  && delivery.launchId !== input.launchId)) {
208
228
  continue;
@@ -261,7 +281,7 @@ export class ExecutorRegistry {
261
281
  const requested = resourceInputs ?? inputs.map((input) => ({
262
282
  taskId: input.taskId,
263
283
  roleName: input.roleName,
264
- ...(input.runId === undefined ? {} : { runId: input.runId }),
284
+ ...(input.turnId === undefined ? {} : { turnId: input.turnId }),
265
285
  agentId: input.agentId,
266
286
  adapterId: input.adapterId,
267
287
  ...(input.nativeSessionId === undefined
@@ -329,11 +349,17 @@ export class ExecutorRegistry {
329
349
  return planned;
330
350
  }
331
351
  }
352
+ function activeTurnId(receiptId) {
353
+ const match = /^turn-input:[^/]+\/([^/]+)\/[1-9]\d*$/u.exec(receiptId);
354
+ if (match === null)
355
+ throw new Error("Turn steer receipt is invalid.");
356
+ return decodeURIComponent(match[1]);
357
+ }
332
358
  export function agentProcessReadinessProbe(adapterId, _surface = "role") {
333
359
  if (adapterId !== "codex" && adapterId !== "claude") {
334
360
  throw new Error(`No tmux readiness probe is registered for Agent adapter: ${adapterId}.`);
335
361
  }
336
- // Run state and receipt fences decide whether delivery is allowed. Provider
362
+ // Turn state and receipt fences decide whether delivery is allowed. Provider
337
363
  // terminal contents are display-only evidence and never lifecycle input.
338
364
  return livePane;
339
365
  }
@@ -348,7 +374,7 @@ function preparedDeliveryId(input) {
348
374
  input.adapterId,
349
375
  input.effective,
350
376
  input.mode,
351
- input.runId ?? null,
377
+ input.turnId ?? null,
352
378
  input.nativeSessionId ?? null,
353
379
  input.hostActivationId ?? null
354
380
  ])).digest("hex");