@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,868 +0,0 @@
1
- import { selectedSchedulerRoles, selectedActiveSchedulerTasks } from "./ports.js";
2
- import { isSchedulerTaskWorkspaceReady } from "./ports.js";
3
- import { formatAgentRunReceiptId } from "../task/taskRecordReference.js";
4
- import { agentRunDeliveryReceiptId } from "../run/agentRun.js";
5
- import { serializeRunBootstrapEnvelope, serializeRunHostRecoveryEnvelope } from "../context/runContextContract.js";
6
- import { serializeWorkflowOutcomeRequestEnvelope } from "../run/runControlRequest.js";
7
- import { effectiveLaunchSnapshotsCompatible, effectiveLaunchSnapshotsCompatibleForTaskSession } from "../executor/effectiveLaunch.js";
8
- import { RuntimeLaunchError } from "../runtime/ports.js";
9
- import { RuntimeLaunchFailure } from "../runtime/launchDiagnostics.js";
10
- import { mailboxHasWork, nextPendingBatch } from "../coordination/workMailbox.js";
11
- import { runtimeObservationFromTaskEvent } from "../runtime/runtimeObservation.js";
12
- import { projectProviderContinuations } from "../runtime/runtimeContinuationProjection.js";
13
- import { RuntimeLifecycleBusyError } from "../runtime/lifecycleReservation.js";
14
- import { serializeAgentErrorRaw } from "../runtime/agentError.js";
15
- /**
16
- * Delivers durable Work AgentRuns before liveness reconciliation. Task command
17
- * handlers only record intent; this Controller path is the sole automated
18
- * route into the Agent terminal, through tmux receipt-backed delivery.
19
- */
20
- export async function processActiveRoleRunDeliveries(store, delivery, now, selection, inputDeliveryRecoveryCutoff) {
21
- const results = [];
22
- for (const task of selectedActiveSchedulerTasks(store, selection)) {
23
- for (const role of selectedSchedulerRoles(store, task.id, selection)) {
24
- const run = store.getActiveAgentRun(task.id, role.name);
25
- if (run !== null && run.pushedAt !== undefined
26
- && run.controlRequest?.state !== "dispatching") {
27
- const continuation = await processActiveRunContinuation(store, delivery, task, role, run, now, inputDeliveryRecoveryCutoff);
28
- if (continuation !== null)
29
- results.push(continuation);
30
- continue;
31
- }
32
- // A crash after a Leader wake is durably claimed but before tmux input
33
- // is recoverable through the same receipt-backed delivery path. The
34
- // re-push guard keys on pushedAt (transport), not deliveredAt (provider
35
- // acceptance): a pushed-but-unaccepted Run must never be pushed twice —
36
- // no duplicate Enter while acceptance is still pending.
37
- if (run === null
38
- || (run.pushedAt !== undefined
39
- && run.controlRequest?.state !== "dispatching"))
40
- continue;
41
- const taskWorkspace = store.getTaskWorkspace(task.id);
42
- if (!isSchedulerTaskWorkspaceReady(task, taskWorkspace)) {
43
- results.push({
44
- taskId: task.id,
45
- roleName: role.name,
46
- runId: run.id,
47
- status: "skipped",
48
- reason: "workspace-not-ready"
49
- });
50
- continue;
51
- }
52
- // Recovery can expose an unpushed Run while an older Provider Turn is
53
- // still settling. Preserve the Run and its mailbox claim without even
54
- // touching the Host; terminal observation will wake the next pass.
55
- if (providerTurnIsUnsettled(store, task.id, role.name)) {
56
- results.push({
57
- taskId: task.id,
58
- roleName: role.name,
59
- runId: run.id,
60
- status: "skipped",
61
- reason: "not-ready"
62
- });
63
- continue;
64
- }
65
- const existingSession = store.getRoleSession(task.id, role.name, run.effective.agentId);
66
- const receiptId = agentRunDeliveryReceiptId(run);
67
- const target = { kind: "role", taskId: task.id, roleName: role.name };
68
- const claim = store.claimWorkMailbox({
69
- target,
70
- batchId: receiptId,
71
- owner: "controller",
72
- now,
73
- executionRef: { type: "run", taskId: task.id, id: run.id }
74
- });
75
- if (claim.status === "empty") {
76
- results.push({
77
- taskId: task.id,
78
- roleName: role.name,
79
- runId: run.id,
80
- status: "skipped",
81
- reason: "mailbox-empty"
82
- });
83
- continue;
84
- }
85
- const processing = claim.processing;
86
- if (processing.executionRef?.type !== "run"
87
- || processing.executionRef.taskId !== task.id
88
- || processing.executionRef.id !== run.id) {
89
- results.push({
90
- taskId: task.id,
91
- roleName: role.name,
92
- runId: run.id,
93
- status: "skipped",
94
- reason: "mailbox-busy"
95
- });
96
- continue;
97
- }
98
- let prepared;
99
- let preStartFencePersisted = false;
100
- let deliveryAttempted = false;
101
- const launchMode = run.controlRequest?.state === "dispatching"
102
- ? "resume"
103
- : run.mode;
104
- try {
105
- // Workflow-outcome control is always another Turn on the already-bound
106
- // Session. A fresh Session is selected only by the Run created after
107
- // the prior Session was explicitly ended.
108
- const nativeSessionId = launchMode === "resume"
109
- ? requireResumeSession(role, run.effective, existingSession)
110
- : undefined;
111
- prepared = await delivery.prepareRoleSession({
112
- taskId: task.id,
113
- roleName: role.name,
114
- agentId: run.effective.agentId,
115
- adapterId: run.effective.adapterId,
116
- effective: run.effective,
117
- workspace: run.effective.workspace.root,
118
- ...(run.workspace === undefined
119
- ? {}
120
- : { managedWorkspace: run.workspace }),
121
- mode: launchMode,
122
- runId: run.id,
123
- ...(nativeSessionId === undefined ? {} : { nativeSessionId }),
124
- ...(launchMode !== "resume" || existingSession?.launchId === undefined
125
- ? {}
126
- : { hostActivationId: existingSession.launchId }),
127
- beforeHostStart: (preflight) => {
128
- persistPreStartFence(store, task, role, run, existingSession, preflight, now);
129
- preflightSession(role, run.effective, existingSession, launchMode, preflight);
130
- preStartFencePersisted = true;
131
- }
132
- });
133
- // Persist the Session identity before submitting any Turn. Host start
134
- // and Session restore are complete lifecycle operations; neither owns
135
- // nor implies the Run's first Provider write.
136
- if (!preStartFencePersisted) {
137
- const preparedFenceSession = validateLaunchSubmittedRecoverySession(role, run.effective, existingSession, launchMode, prepared.session);
138
- store.saveRoleRunPrepared({
139
- task,
140
- role,
141
- run,
142
- session: preparedFenceSession,
143
- ...(prepared.launchId === undefined ? {} : { launchId: prepared.launchId }),
144
- now
145
- });
146
- }
147
- const ready = await delivery.waitUntilReady(prepared);
148
- const session = validateLaunchSubmittedRecoverySession(role, run.effective, existingSession, launchMode, ready.session);
149
- store.saveRoleRunPrepared({
150
- task,
151
- role,
152
- run,
153
- session,
154
- ...(ready.prepared.launchId === undefined
155
- ? {}
156
- : { launchId: ready.prepared.launchId }),
157
- now
158
- });
159
- deliveryAttempted = true;
160
- const outcome = await delivery.sendOnce({
161
- delivery: ready,
162
- receiptId,
163
- text: initialRunTurnInput(run)
164
- });
165
- if (outcome === "busy" || outcome === "unavailable") {
166
- delivery.forgetPrepared?.({
167
- taskId: task.id,
168
- roleName: role.name,
169
- runId: run.id,
170
- ...(ready.prepared.launchId === undefined
171
- ? {}
172
- : { launchId: ready.prepared.launchId })
173
- });
174
- results.push({
175
- taskId: task.id,
176
- roleName: role.name,
177
- runId: run.id,
178
- status: "skipped",
179
- reason: outcome === "busy" ? "not-ready" : "runtime-unavailable"
180
- });
181
- continue;
182
- }
183
- if (outcome === "rejected") {
184
- const terminal = store.saveRoleRunDeliveryFailure({
185
- taskId: task.id,
186
- roleName: role.name,
187
- agentId: run.effective.agentId,
188
- adapterId: run.effective.adapterId,
189
- runId: run.id,
190
- mailboxBatchId: processing.batchId,
191
- ...(session?.nativeSessionId === undefined
192
- ? {}
193
- : { nativeSessionId: session.nativeSessionId }),
194
- ...(ready.prepared.launchId === undefined
195
- ? {}
196
- : { launchId: ready.prepared.launchId }),
197
- summary: "Provider rejected the Turn before acceptance; Agent recovery is required.",
198
- cleanupRequired: false,
199
- now
200
- });
201
- delivery.forgetPrepared?.({
202
- taskId: task.id,
203
- roleName: role.name,
204
- runId: run.id,
205
- ...(ready.prepared.launchId === undefined
206
- ? {}
207
- : { launchId: ready.prepared.launchId })
208
- });
209
- results.push({
210
- taskId: task.id,
211
- roleName: role.name,
212
- runId: run.id,
213
- status: terminal === "failed" ? "failed" : "skipped",
214
- reason: "provider-rejected",
215
- ...(terminal === "failed" ? { terminalized: true } : {})
216
- });
217
- continue;
218
- }
219
- if (outcome === "delivery-unknown") {
220
- store.saveRoleRunDelivery({
221
- task,
222
- role,
223
- run,
224
- session,
225
- ...(ready.prepared.launchId === undefined
226
- ? {}
227
- : { launchId: ready.prepared.launchId }),
228
- now
229
- });
230
- delivery.forgetPrepared?.({
231
- taskId: task.id,
232
- roleName: role.name,
233
- runId: run.id,
234
- ...(ready.prepared.launchId === undefined
235
- ? {}
236
- : { launchId: ready.prepared.launchId })
237
- });
238
- results.push({
239
- taskId: task.id,
240
- roleName: role.name,
241
- runId: run.id,
242
- status: "skipped",
243
- reason: "delivery-uncertain"
244
- });
245
- continue;
246
- }
247
- store.saveRoleRunDelivery({
248
- task,
249
- role,
250
- run,
251
- session,
252
- ...(ready.prepared.launchId === undefined
253
- ? {}
254
- : { launchId: ready.prepared.launchId }),
255
- now
256
- });
257
- delivery.forgetPrepared?.({
258
- taskId: task.id,
259
- roleName: role.name,
260
- runId: run.id,
261
- ...(ready.prepared.launchId === undefined
262
- ? {}
263
- : { launchId: ready.prepared.launchId })
264
- });
265
- results.push({
266
- taskId: task.id,
267
- roleName: role.name,
268
- runId: run.id,
269
- status: outcome === "sent" ? "delivered" : "already-delivered"
270
- });
271
- continue;
272
- }
273
- catch (error) {
274
- const message = error instanceof Error ? error.message : String(error);
275
- // Scheduler single-flight backpressure: the Role runtime lifecycle
276
- // lane was busy when the launch was reserved. The Run stays
277
- // active-unpushed and its mailbox claim is retained; the next pass
278
- // retries after the lane settles. This is contention before any
279
- // semantic launch, never a Run failure.
280
- if (error instanceof RuntimeLifecycleBusyError) {
281
- delivery.forgetPrepared?.({
282
- taskId: task.id,
283
- roleName: role.name,
284
- runId: run.id
285
- });
286
- results.push({
287
- taskId: task.id,
288
- roleName: role.name,
289
- runId: run.id,
290
- status: "skipped",
291
- reason: "runtime-unavailable",
292
- error: message
293
- });
294
- continue;
295
- }
296
- store.recordAgentError?.({
297
- taskId: task.id,
298
- roleName: role.name,
299
- runId: run.id,
300
- source: error instanceof RuntimeLaunchError || error instanceof RuntimeLaunchFailure
301
- ? "host"
302
- : "yui",
303
- phase: deliveryAttempted
304
- ? "turn-submit"
305
- : launchMode === "new" ? "session-start" : "session-restore",
306
- message,
307
- raw: serializeAgentErrorRaw(error),
308
- inputDisposition: deliveryAttempted ? "unknown" : "not-accepted"
309
- }, now);
310
- if (!deliveryAttempted) {
311
- const failedSession = store.getRoleSession(task.id, role.name, run.effective.agentId);
312
- const terminal = store.saveRoleRunDeliveryFailure({
313
- taskId: task.id,
314
- roleName: role.name,
315
- agentId: run.effective.agentId,
316
- adapterId: run.effective.adapterId,
317
- runId: run.id,
318
- mailboxBatchId: processing.batchId,
319
- ...(failedSession?.nativeSessionId === undefined
320
- ? {}
321
- : { nativeSessionId: failedSession.nativeSessionId }),
322
- ...(prepared?.launchId === undefined && failedSession?.launchId === undefined
323
- ? {}
324
- : { launchId: prepared?.launchId ?? failedSession.launchId }),
325
- summary: `Agent Session preparation failed before Turn acceptance: ${message}`,
326
- cleanupRequired: false,
327
- now
328
- });
329
- if (terminal === "failed") {
330
- delivery.forgetPrepared?.({
331
- taskId: task.id,
332
- roleName: role.name,
333
- runId: run.id,
334
- ...(prepared?.launchId === undefined
335
- ? {}
336
- : { launchId: prepared.launchId })
337
- });
338
- results.push({
339
- taskId: task.id,
340
- roleName: role.name,
341
- runId: run.id,
342
- status: "failed",
343
- reason: "launch-failed",
344
- error: message,
345
- terminalized: true
346
- });
347
- continue;
348
- }
349
- }
350
- if (error instanceof RuntimeLaunchFailure) {
351
- if (!deliveryAttempted) {
352
- delivery.forgetPrepared?.({
353
- taskId: task.id,
354
- roleName: role.name,
355
- runId: run.id,
356
- ...(prepared?.launchId === undefined
357
- ? {}
358
- : { launchId: prepared.launchId })
359
- });
360
- results.push({
361
- taskId: task.id,
362
- roleName: role.name,
363
- runId: run.id,
364
- status: "skipped",
365
- reason: "launch-failed",
366
- error: message
367
- });
368
- continue;
369
- }
370
- }
371
- if (error instanceof RuntimeLaunchError) {
372
- const writerAttached = error.reason === "writable-client";
373
- results.push({
374
- taskId: task.id,
375
- roleName: role.name,
376
- runId: run.id,
377
- status: "skipped",
378
- reason: writerAttached ? "writer-attached" : "runtime-unavailable",
379
- error: message
380
- });
381
- continue;
382
- }
383
- if (!deliveryAttempted) {
384
- delivery.forgetPrepared?.({
385
- taskId: task.id,
386
- roleName: role.name,
387
- runId: run.id,
388
- ...(prepared?.launchId === undefined
389
- ? {}
390
- : { launchId: prepared.launchId })
391
- });
392
- results.push({
393
- taskId: task.id,
394
- roleName: role.name,
395
- runId: run.id,
396
- status: "skipped",
397
- reason: "runtime-unavailable",
398
- error: message
399
- });
400
- continue;
401
- }
402
- results.push({
403
- taskId: task.id,
404
- roleName: role.name,
405
- runId: run.id,
406
- status: "failed",
407
- reason: "delivery-uncertain",
408
- error: message
409
- });
410
- }
411
- }
412
- }
413
- return results;
414
- }
415
- function providerTurnIsUnsettled(store, taskId, roleName) {
416
- if (store.getRoleSession(taskId, roleName)?.status !== "active")
417
- return false;
418
- const turn = store.getTaskRoleSessionSet?.(taskId, roleName)?.providerBinding?.turn;
419
- return turn !== null
420
- && turn !== undefined
421
- && ["submitting", "accepted", "running", "delivery-unknown"].includes(turn.status);
422
- }
423
- async function processActiveRunContinuation(store, delivery, task, role, run, now, inputDeliveryRecoveryCutoff) {
424
- const target = { kind: "role", taskId: task.id, roleName: role.name };
425
- const mailbox = store.getWorkMailbox(target);
426
- if (mailbox === null || !mailboxHasWork(mailbox))
427
- return null;
428
- const blockedBy = latestContinuationPreparationFailure(store, task.id, run);
429
- if (blockedBy !== null) {
430
- return {
431
- taskId: task.id,
432
- roleName: role.name,
433
- runId: run.id,
434
- status: "skipped",
435
- reason: "runtime-unavailable",
436
- error: blockedBy.payload.message
437
- };
438
- }
439
- const session = store.getRoleSession(task.id, role.name, run.effective.agentId);
440
- if (session === null || session.launchId === undefined || !hasText(session.nativeSessionId)) {
441
- return {
442
- taskId: task.id,
443
- roleName: role.name,
444
- runId: run.id,
445
- status: "skipped",
446
- reason: "not-ready"
447
- };
448
- }
449
- const writer = store.getProviderAuthorityFence?.({
450
- taskId: task.id,
451
- roleName: role.name,
452
- runId: run.id,
453
- agentId: run.effective.agentId,
454
- launchId: session.launchId,
455
- nativeSessionId: session.nativeSessionId
456
- }) ?? null;
457
- if (writer !== null && writer.owner !== "controller") {
458
- return {
459
- taskId: task.id,
460
- roleName: role.name,
461
- runId: run.id,
462
- status: "skipped",
463
- reason: "writer-attached"
464
- };
465
- }
466
- const originalReceipt = formatAgentRunReceiptId(task.id, run.id);
467
- if (mailbox.processing !== null && (mailbox.processing.executionRef?.type !== "run"
468
- || mailbox.processing.executionRef.taskId !== task.id
469
- || mailbox.processing.executionRef.id !== run.id
470
- || mailbox.processing.batchId !== originalReceipt)) {
471
- return {
472
- taskId: task.id,
473
- roleName: role.name,
474
- runId: run.id,
475
- status: "skipped",
476
- reason: "mailbox-busy"
477
- };
478
- }
479
- if (mailbox.inputDelivery !== null) {
480
- const existing = mailbox.inputDelivery;
481
- if ((existing.status === "delivery-unknown"
482
- || (existing.status === "dispatching"
483
- && inputDeliveryRecoveryCutoff !== undefined
484
- && Date.parse(existing.startedAt) < inputDeliveryRecoveryCutoff.getTime()))) {
485
- return reconcileStrandedInputDelivery(store, task, role, run, existing, now);
486
- }
487
- return {
488
- taskId: task.id,
489
- roleName: role.name,
490
- runId: run.id,
491
- status: "skipped",
492
- reason: "delivery-uncertain"
493
- };
494
- }
495
- const pending = nextPendingBatch(mailbox);
496
- const lane = mailbox.pending.userCorrection === pending ? "user-correction" : "normal";
497
- const requestedBatchId = pending === null
498
- ? originalReceipt
499
- : `agent-input:${task.id}/${run.id}/${lane}:${pending.fromSequence}-${pending.toSequence}`;
500
- if (requestedBatchId === originalReceipt) {
501
- // Initial provider acceptance is still outstanding. It owns this claim;
502
- // never reinterpret the original Run prompt as a continuation.
503
- return {
504
- taskId: task.id,
505
- roleName: role.name,
506
- runId: run.id,
507
- status: "skipped",
508
- reason: "delivery-uncertain"
509
- };
510
- }
511
- if (pending === null || session.launchId === undefined)
512
- return null;
513
- if (session.status === "ended") {
514
- return {
515
- taskId: task.id,
516
- roleName: role.name,
517
- runId: run.id,
518
- status: "skipped",
519
- reason: "runtime-unavailable"
520
- };
521
- }
522
- // Managed input has one write path: a new structured Turn at a settled
523
- // boundary. AgentHost owns Provider serialization; a busy send releases the
524
- // claim and leaves the durable correction pending for the next pass.
525
- const mode = "followup";
526
- if (writer === null) {
527
- return {
528
- taskId: task.id,
529
- roleName: role.name,
530
- runId: run.id,
531
- status: "skipped",
532
- reason: "not-ready"
533
- };
534
- }
535
- let deliveryAttempted = false;
536
- let inputDelivery;
537
- try {
538
- const prepared = await delivery.prepareRoleSession({
539
- taskId: task.id,
540
- roleName: role.name,
541
- agentId: run.effective.agentId,
542
- adapterId: run.effective.adapterId,
543
- effective: run.effective,
544
- workspace: run.effective.workspace.root,
545
- ...(run.workspace === undefined ? {} : { managedWorkspace: run.workspace }),
546
- mode: "resume",
547
- runId: run.id,
548
- nativeSessionId: session.nativeSessionId,
549
- hostActivationId: session.launchId
550
- });
551
- const ready = await delivery.waitUntilReady(prepared);
552
- const readyLaunchId = ready.prepared.launchId
553
- ?? ready.session?.launchId
554
- ?? session.launchId;
555
- const readyNativeSessionId = ready.session?.nativeSessionId
556
- ?? session.nativeSessionId;
557
- if (readyLaunchId === undefined || !hasText(readyNativeSessionId)) {
558
- throw new Error("Continuation delivery has no Provider Activation fence.");
559
- }
560
- const providerFence = {
561
- conversationId: writer.conversationId,
562
- activationId: writer.activationId
563
- };
564
- const claim = store.claimInputDelivery({
565
- target,
566
- attemptId: requestedBatchId,
567
- lane,
568
- mode,
569
- owner: "controller",
570
- now,
571
- executionRef: { type: "run", taskId: task.id, id: run.id },
572
- providerFence
573
- });
574
- if (claim.status === "empty")
575
- return null;
576
- if (claim.status === "busy") {
577
- return {
578
- taskId: task.id,
579
- roleName: role.name,
580
- runId: run.id,
581
- status: "skipped",
582
- reason: "not-ready"
583
- };
584
- }
585
- if (claim.delivery.attemptId !== requestedBatchId || claim.status === "delivery") {
586
- return {
587
- taskId: task.id,
588
- roleName: role.name,
589
- runId: run.id,
590
- status: "skipped",
591
- reason: "delivery-uncertain"
592
- };
593
- }
594
- inputDelivery = claim.delivery;
595
- deliveryAttempted = true;
596
- const text = continuationInput(task, role, run, inputDelivery.attemptId, inputDelivery.batch, boundedContinuationSummaries(store, task.id, inputDelivery.batch.refs));
597
- const outcome = await delivery.sendOnce({
598
- delivery: ready,
599
- receiptId: inputDelivery.attemptId,
600
- text
601
- });
602
- if (outcome === "busy" || outcome === "unavailable") {
603
- store.releaseInputDelivery(target, inputDelivery.attemptId, now);
604
- return {
605
- taskId: task.id,
606
- roleName: role.name,
607
- runId: run.id,
608
- status: "skipped",
609
- reason: outcome === "busy" ? "not-ready" : "runtime-unavailable"
610
- };
611
- }
612
- if (outcome === "rejected") {
613
- store.resolveInputDeliveryNotAccepted(target, inputDelivery.attemptId, now);
614
- return {
615
- taskId: task.id,
616
- roleName: role.name,
617
- runId: run.id,
618
- status: "skipped",
619
- reason: "not-ready"
620
- };
621
- }
622
- if (outcome === "delivery-unknown") {
623
- store.markInputDeliveryUnknown(target, inputDelivery.attemptId, "Provider Turn delivery is ambiguous; automatic retry is fenced.", now);
624
- return {
625
- taskId: task.id,
626
- roleName: role.name,
627
- runId: run.id,
628
- status: "skipped",
629
- reason: "delivery-uncertain"
630
- };
631
- }
632
- store.markInputDeliveryPushed(target, inputDelivery.attemptId, now);
633
- // Keep the exact claim until the matching structured provider acceptance
634
- // observation folds it. An interrupted acknowledgement becomes
635
- // delivery-unknown and cannot be resubmitted automatically.
636
- return {
637
- taskId: task.id,
638
- roleName: role.name,
639
- runId: run.id,
640
- status: outcome === "sent" ? "delivered" : "already-delivered"
641
- };
642
- }
643
- catch (error) {
644
- store.recordAgentError?.({
645
- taskId: task.id,
646
- roleName: role.name,
647
- runId: run.id,
648
- source: error instanceof RuntimeLaunchError || error instanceof RuntimeLaunchFailure
649
- ? "host"
650
- : "yui",
651
- phase: deliveryAttempted ? "turn-submit" : "session-restore",
652
- message: error instanceof Error ? error.message : String(error),
653
- raw: serializeAgentErrorRaw(error),
654
- inputDisposition: deliveryAttempted ? "unknown" : "not-accepted"
655
- }, now);
656
- if (inputDelivery === undefined) {
657
- // No durable input intent existed and no provider input method was
658
- // called. Session preparation may be retried safely.
659
- }
660
- else if (!deliveryAttempted)
661
- store.releaseInputDelivery(target, inputDelivery.attemptId, now);
662
- else
663
- store.markInputDeliveryUnknown(target, inputDelivery.attemptId, error instanceof Error ? error.message : String(error), now);
664
- return {
665
- taskId: task.id,
666
- roleName: role.name,
667
- runId: run.id,
668
- status: "skipped",
669
- reason: deliveryAttempted ? "delivery-uncertain" : "runtime-unavailable",
670
- error: error instanceof Error ? error.message : String(error)
671
- };
672
- }
673
- }
674
- /**
675
- * A persisted, conclusively unaccepted continuation failure is the recovery
676
- * fence for this exact Run. Periodic scheduler passes must not reinterpret it
677
- * as permission to start another Host or submit the same pending input. A new
678
- * Run has a new identity and therefore makes the Agent's explicit retry
679
- * observable without adding a second writable recovery state.
680
- */
681
- function latestContinuationPreparationFailure(store, taskId, run) {
682
- if (run.deliveredAt === undefined)
683
- return null;
684
- const deliveredAt = Date.parse(run.deliveredAt);
685
- const events = store.listEvents?.(taskId) ?? [];
686
- return [...events].reverse().find((event) => (event.type === "runtime.agent-error"
687
- && event.payload.runId === run.id
688
- && ["session-restore", "turn-submit"].includes(event.payload.phase ?? "")
689
- && event.payload.inputDisposition === "not-accepted"
690
- && Date.parse(event.createdAt) >= deliveredAt)) ?? null;
691
- }
692
- async function reconcileStrandedInputDelivery(store, task, role, run, input, now) {
693
- const target = { kind: "role", taskId: task.id, roleName: role.name };
694
- const uncertain = (reason) => {
695
- store.markInputDeliveryUnknown(target, input.attemptId, reason, now);
696
- return {
697
- taskId: task.id,
698
- roleName: role.name,
699
- runId: run.id,
700
- status: "skipped",
701
- reason: "delivery-uncertain"
702
- };
703
- };
704
- return uncertain("Controller restarted with unsettled Provider delivery; automatic resubmission is fenced.");
705
- }
706
- function continuationInput(task, role, run, attemptId, batch, resultSummaries) {
707
- const references = batch.refs.map((ref) => ("taskId" in ref
708
- ? `${ref.type}:${ref.taskId}/${ref.id}`
709
- : `${ref.type}:${ref.id}`));
710
- return [
711
- `Yui Task Event Batch: ${attemptId}.`,
712
- "New durable task events are available for the current Yui Run.",
713
- "Read the referenced shared context through the Yui CLI, incorporate it, and decide whether to continue work or wait for more results.",
714
- "Do not create a new Yui Run merely because this is a new Provider Turn.",
715
- `Reasons: ${batch.reasons.join(", ")}.`,
716
- ...(batch.sources.length === 0 ? [] : [`Sources: ${batch.sources.join(", ")}.`]),
717
- ...(references.length === 0 ? [] : [`References: ${references.join(", ")}.`]),
718
- ...(resultSummaries.length === 0
719
- ? []
720
- : [
721
- "Native child results (bounded excerpts; read the referenced event for the full content):",
722
- ...resultSummaries
723
- ])
724
- ].join("\n");
725
- }
726
- /**
727
- * Issue 13: the parent prompt only ever sees a bounded excerpt of a native
728
- * child result plus its durable event reference. The full content stays in
729
- * the Task event log and is read on demand through `yui task event show`.
730
- * This prevents an unbounded provider result from being re-injected into the
731
- * parent Session on every continuation wake.
732
- */
733
- const MAX_RESULT_SUMMARY_CHARS = 512;
734
- const MAX_RESULT_SUMMARY_LINES = 8;
735
- function boundedContinuationSummaries(store, taskId, refs) {
736
- if (store.listEvents === undefined)
737
- return [];
738
- const events = store.listEvents(taskId);
739
- const continuations = projectProviderContinuations(events);
740
- const byKey = new Map(continuations.map((entry) => [
741
- `${entry.identity.continuationId}\u0000${entry.identity.generation}`,
742
- entry
743
- ]));
744
- const summaries = [];
745
- for (const ref of refs) {
746
- if (!("taskId" in ref) || ref.type !== "event")
747
- continue;
748
- const event = events.find((entry) => entry.id === ref.id);
749
- if (event === undefined)
750
- continue;
751
- const observation = runtimeObservationFromTaskEvent(event);
752
- if (observation === null || observation.kind !== "continuation.reported")
753
- continue;
754
- const summary = observation.payload?.summary;
755
- if (summary === undefined || summary.trim().length === 0)
756
- continue;
757
- const continuation = byKey.get(`${observation.fence.continuationId}\u0000${observation.fence.continuationGeneration}`);
758
- const digest = continuation?.reports
759
- .find((report) => report.reportId === observation.payload?.reportId)
760
- ?.resultDigest;
761
- summaries.push([
762
- `- child ${observation.fence.continuationId}`,
763
- ...(continuation?.durability === "durable-result"
764
- ? [" (durable-result; full content: "
765
- + `yui task event show ${taskId} ${ref.id}`
766
- + (digest === undefined ? "" : `; digest ${digest.slice(0, 12)}`)
767
- + ")"]
768
- : [" (best-effort; rerun if the parent Session was lost)"]),
769
- ` ${boundedExcerpt(summary)}`
770
- ].join("\n"));
771
- }
772
- return summaries;
773
- }
774
- function boundedExcerpt(text) {
775
- const lines = text.split("\n").slice(0, MAX_RESULT_SUMMARY_LINES);
776
- const joined = lines.join("\n");
777
- if (joined.length <= MAX_RESULT_SUMMARY_CHARS && text.length === joined.length)
778
- return joined;
779
- return `${joined.slice(0, MAX_RESULT_SUMMARY_CHARS)}… [truncated]`;
780
- }
781
- function requireResumeSession(role, effective, session) {
782
- if (session === null || !hasText(session.nativeSessionId)) {
783
- throw new Error(`Role resume has no fixed native session: ${role.taskId}/${role.name}.`);
784
- }
785
- if (!effectiveLaunchSnapshotsCompatibleForTaskSession(session.effective, effective)) {
786
- throw new Error(`Role resume effective snapshot drifted: ${role.taskId}/${role.name}.`);
787
- }
788
- return session.nativeSessionId;
789
- }
790
- function persistPreStartFence(store, task, role, run, existingSession, preflight, now) {
791
- if (preflight.owner.scope !== "task"
792
- || preflight.owner.taskId !== task.id
793
- || preflight.owner.roleName !== role.name
794
- || preflight.runId !== run.id
795
- || preflight.launchId.trim().length === 0) {
796
- throw new Error(`Pre-start launch fence changed the active Role Run: ${task.id}/${role.name}.`);
797
- }
798
- const session = preflightSession(role, run.effective, existingSession, run.mode, preflight);
799
- store.saveRoleRunPrepared({
800
- task,
801
- role,
802
- run,
803
- session,
804
- launchId: preflight.launchId,
805
- now
806
- });
807
- }
808
- function preflightSession(role, effective, existing, mode, preflight) {
809
- const session = preflight.nativeSessionId === undefined
810
- ? null
811
- : {
812
- agentId: preflight.agentId,
813
- adapterId: preflight.adapterId,
814
- nativeSessionId: preflight.nativeSessionId,
815
- launchId: preflight.launchId,
816
- ...(preflight.sessionTitle === undefined ? {} : { title: preflight.sessionTitle }),
817
- status: "active",
818
- effective: preflight.effective
819
- };
820
- return validateRoleSession(role, effective, existing, mode, session);
821
- }
822
- function validateLaunchSubmittedRecoverySession(role, effective, existing, mode, session) {
823
- return session === undefined
824
- ? existing
825
- : validateRoleSession(role, effective, existing, mode, session);
826
- }
827
- function initialRunTurnInput(run) {
828
- if (run.controlRequest?.state === "dispatching") {
829
- return serializeWorkflowOutcomeRequestEnvelope({
830
- taskId: run.taskId,
831
- runId: run.id,
832
- roleName: run.roleName,
833
- request: run.controlRequest
834
- });
835
- }
836
- return run.pushedAt === undefined
837
- ? serializeRunBootstrapEnvelope(run.bootstrapEnvelope)
838
- : serializeRunHostRecoveryEnvelope(run.bootstrapEnvelope);
839
- }
840
- function validateRoleSession(role, effective, existing, mode, session) {
841
- if (mode === "new" && session === null)
842
- return null;
843
- if (session === null || !hasText(session.nativeSessionId)) {
844
- throw new Error(`Ready Role session has no native session id: ${role.taskId}/${role.name}.`);
845
- }
846
- if (session.agentId !== effective.agentId || session.adapterId !== effective.adapterId) {
847
- throw new Error(`Ready Role session identity changed: ${role.taskId}/${role.name}.`);
848
- }
849
- const compatible = mode === "resume"
850
- ? effectiveLaunchSnapshotsCompatibleForTaskSession(session.effective, effective)
851
- : effectiveLaunchSnapshotsCompatible(session.effective, effective);
852
- if (!compatible) {
853
- throw new Error(`Ready Role session effective snapshot changed: ${role.taskId}/${role.name}.`);
854
- }
855
- if (mode === "resume" && session.nativeSessionId !== existing?.nativeSessionId) {
856
- throw new Error(`Role resume changed the fixed native session id: ${role.taskId}/${role.name}.`);
857
- }
858
- return {
859
- ...session,
860
- status: "active",
861
- ...(mode === "resume" && existing !== null
862
- ? { effective: existing.effective }
863
- : {})
864
- };
865
- }
866
- function hasText(value) {
867
- return typeof value === "string" && value.trim().length > 0;
868
- }