@zq-silk/yui 0.15.11 → 0.16.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 (280) hide show
  1. package/ARCHITECTURE.md +11 -6
  2. package/ARCHITECTURE.zh-CN.md +8 -4
  3. package/README.md +13 -5
  4. package/dist/agentRun/agentRun.js +3 -0
  5. package/dist/artifacts/artifactCommitLock.js +16 -55
  6. package/dist/artifacts/taskArtifactRepository.js +3 -3
  7. package/dist/cli/agentConfigurationPicker.js +1 -1
  8. package/dist/cli/commandCatalog.js +92 -48
  9. package/dist/cli/completion.js +18 -18
  10. package/dist/cli/completionWizard.js +1 -1
  11. package/dist/cli/dynamicCompletion.js +1 -1
  12. package/dist/cli/interactionPolicy.js +11 -3
  13. package/dist/cli/invocationAuthority.js +64 -0
  14. package/dist/cli/managedDiagnostics.js +2 -2
  15. package/dist/cli/parseRepeatable.js +35 -0
  16. package/dist/cli/updatePorts.js +26 -5
  17. package/dist/cli.js +1303 -1235
  18. package/dist/commands/agentCommands.js +4 -4
  19. package/dist/commands/capabilityCommands.js +1 -1
  20. package/dist/commands/configCommands.js +6 -57
  21. package/dist/commands/configOverview.js +2 -2
  22. package/dist/commands/durableJobCommands.js +12 -6
  23. package/dist/commands/executionAuditCommands.js +10 -0
  24. package/dist/commands/globalRoleCommands.js +78 -29
  25. package/dist/commands/grantCommands.js +0 -3
  26. package/dist/commands/jobCommands.js +1 -4
  27. package/dist/commands/operatorCommands.js +3 -3
  28. package/dist/commands/projectCommands.js +45 -25
  29. package/dist/commands/resourcesCommands.js +10 -40
  30. package/dist/commands/roleConfiguration.js +2 -6
  31. package/dist/commands/taskActivationCommands.js +4 -8
  32. package/dist/commands/taskAgentCapabilities.js +9 -0
  33. package/dist/commands/taskCommandSupport.js +155 -0
  34. package/dist/commands/taskCommandTypes.js +1 -0
  35. package/dist/commands/taskCommands.js +355 -732
  36. package/dist/commands/taskCompletionGate.js +1 -1
  37. package/dist/commands/taskExecutionCommands.js +1 -1
  38. package/dist/commands/taskFactCommands.js +325 -0
  39. package/dist/commands/taskInputCommands.js +12 -42
  40. package/dist/commands/taskIntegrationCommands.js +20 -58
  41. package/dist/commands/taskNextActionCommand.js +7 -6
  42. package/dist/commands/taskPublicationAdoptCommand.js +127 -0
  43. package/dist/commands/taskPublicationCommands.js +12 -3
  44. package/dist/commands/taskPublicationVerifyCommand.js +24 -40
  45. package/dist/commands/taskRemoteDeliveryCommand.js +5 -86
  46. package/dist/commands/taskUpstreamCommands.js +19 -8
  47. package/dist/commands/workflowCommands.js +6 -1
  48. package/dist/completion/completionInstaller.js +26 -26
  49. package/dist/completion/completionState.js +26 -26
  50. package/dist/completion/fileCompletionManager.js +6 -11
  51. package/dist/config/configCatalog.js +0 -2
  52. package/dist/config/timeZone.js +14 -0
  53. package/dist/config/yuiConfig.js +0 -29
  54. package/dist/context/runContextPack.js +5 -3
  55. package/dist/context/taskCatalog.js +185 -0
  56. package/dist/context/taskContext.js +152 -151
  57. package/dist/context/wakeRunReferences.js +11 -0
  58. package/dist/controller/agentCapabilities.js +58 -0
  59. package/dist/controller/agentRuntimeObserver.js +6 -11
  60. package/dist/controller/clientRuntime.js +22 -14
  61. package/dist/controller/controller.js +20 -24
  62. package/dist/controller/fileSchedulerStoreAdapter.js +191 -225
  63. package/dist/controller/globalInputDelivery.js +13 -0
  64. package/dist/controller/jobClient.js +4 -4
  65. package/dist/controller/jobControl.js +60 -25
  66. package/dist/controller/jobSupervisor.js +4 -4
  67. package/dist/controller/providerRetryAdmission.js +100 -0
  68. package/dist/controller/providerRetryDelivery.js +218 -0
  69. package/dist/controller/runtime.js +85 -43
  70. package/dist/controller/runtimeEventProcessor.js +0 -5
  71. package/dist/controller/sessionOwnerReconciliation.js +5 -0
  72. package/dist/controller/taskAgentError.js +35 -0
  73. package/dist/coordination/workMailboxQueue.js +2 -24
  74. package/dist/core/controllerClient.js +2 -2
  75. package/dist/core/fileLockOwner.js +74 -0
  76. package/dist/decision/decision.js +17 -0
  77. package/dist/doctor/doctor.js +18 -9
  78. package/dist/event/taskEvent.js +12 -0
  79. package/dist/execution/workItemExecutionProjection.js +2 -2
  80. package/dist/executor/agentCapabilityConfig.js +43 -0
  81. package/dist/executor/agentConfigurationCatalog.js +49 -11
  82. package/dist/executor/agentExecutor.js +5 -8
  83. package/dist/executor/fileRoleLaunchPlanner.js +11 -53
  84. package/dist/executor/taskAgentCapabilities.js +64 -0
  85. package/dist/integration/deliveryObligation.js +1 -69
  86. package/dist/integration/gitIntegrationService.js +79 -90
  87. package/dist/integration/integrationAttempt.js +3 -14
  88. package/dist/integration/integrationSourceApplication.js +4 -12
  89. package/dist/integration/manifestTags.js +2 -2
  90. package/dist/job/durableJob.js +4 -25
  91. package/dist/job/jobAssignmentScope.js +22 -0
  92. package/dist/job/jobOperation.js +16 -0
  93. package/dist/job/jobRunner.js +2 -1
  94. package/dist/job/stepDirectory.js +15 -0
  95. package/dist/kernel/builtinCapabilities.js +15 -11
  96. package/dist/kernel/kernelPorts.js +1 -17
  97. package/dist/lifecycle/exactRunTerminalization.js +5 -10
  98. package/dist/message/globalProviderRetry.js +15 -0
  99. package/dist/message/inputControlResolution.js +3 -4
  100. package/dist/message/message.js +17 -20
  101. package/dist/message/messageContinuation.js +1 -1
  102. package/dist/milestone/milestone.js +11 -0
  103. package/dist/observability/executionAudit.js +19 -0
  104. package/dist/observability/orchestrationMetrics.js +24 -1
  105. package/dist/observability/runtimeIdentity.js +1 -17
  106. package/dist/output/agentConfigurationPresentation.js +2 -0
  107. package/dist/output/timePresentation.js +1 -14
  108. package/dist/plugins/pluginService.js +12 -4
  109. package/dist/release/releaseWorkflowPorts.js +45 -96
  110. package/dist/release/runtimeRelease.js +9 -15
  111. package/dist/repository/gitWorkspace.js +371 -105
  112. package/dist/repository/projectMaintenanceLock.js +98 -82
  113. package/dist/repository/taskBaseFreshness.js +1 -1
  114. package/dist/repository/taskWorkspaceCoordinator.js +82 -144
  115. package/dist/repository/taskWorkspacePreparer.js +113 -47
  116. package/dist/repository/workspaceCleanupInspection.js +187 -0
  117. package/dist/resources/autoResourceGc.js +4 -77
  118. package/dist/resources/liveReferences.js +7 -54
  119. package/dist/resources/resourceDiscovery.js +5 -118
  120. package/dist/resources/resourceGc.js +233 -181
  121. package/dist/resources/resourceRegistrar.js +5 -4
  122. package/dist/resources/resourceRegistry.js +3 -37
  123. package/dist/resources/resourceRegistryStore.js +13 -36
  124. package/dist/resources/sqliteResourceRegistry.js +43 -26
  125. package/dist/review/deltaRecheck.js +1 -1
  126. package/dist/review/reviewAcceptance.js +1 -1
  127. package/dist/review/reviewDecision.js +1 -1
  128. package/dist/review/reviewRound.js +8 -7
  129. package/dist/review/taskFinalReviewContractResolution.js +1 -1
  130. package/dist/runtime/acpProtocol.js +4 -51
  131. package/dist/runtime/acpSession.js +10 -75
  132. package/dist/runtime/acpSessionConfiguration.js +1 -8
  133. package/dist/runtime/agentEndpoint.js +3 -2
  134. package/dist/runtime/agentError.js +5 -3
  135. package/dist/runtime/agentFailureContext.js +43 -0
  136. package/dist/runtime/agentHost.js +42 -25
  137. package/dist/runtime/builtinAgentErrorMappers.js +91 -0
  138. package/dist/runtime/codexAppServerRuntime.js +34 -3
  139. package/dist/runtime/codexInteractiveHost.js +2 -2
  140. package/dist/runtime/index.js +0 -1
  141. package/dist/runtime/managedCaller.js +21 -1
  142. package/dist/runtime/providerControl.js +5 -1
  143. package/dist/runtime/providerErrors.js +38 -0
  144. package/dist/runtime/providerRetry.js +198 -0
  145. package/dist/runtime/providerRuntimeIdentity.js +28 -2
  146. package/dist/runtime/runtimeObservation.js +0 -5
  147. package/dist/runtime/runtimeSessionCandidate.js +2 -3
  148. package/dist/runtime/sessionOwnerIdentity.js +1 -1
  149. package/dist/runtime/sessionTokenMetrics.js +15 -5
  150. package/dist/runtime/structuredProviderHost.js +10 -43
  151. package/dist/runtime/taskUsageMetrics.js +275 -0
  152. package/dist/runtime/tmuxAdapters.js +6 -8
  153. package/dist/scheduler/activeRoleRunDelivery.js +19 -7
  154. package/dist/scheduler/leaderWakeupProcessor.js +24 -6
  155. package/dist/scheduler/operatorEvent.js +12 -20
  156. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  157. package/dist/scheduler/ports.js +5 -8
  158. package/dist/scheduler/roleRunLiveness.js +4 -4
  159. package/dist/scheduler/roleRunStall.js +16 -23
  160. package/dist/scheduler/taskExecutionProjection.js +49 -36
  161. package/dist/scheduler/taskObservabilityProjection.js +6 -45
  162. package/dist/scheduler/taskWake.js +5 -10
  163. package/dist/scheduler/wakeReason.js +2 -0
  164. package/dist/scheduler/wakeupQueue.js +2 -4
  165. package/dist/setup/setupCommand.js +1 -1
  166. package/dist/storage/contextRecords.js +106 -0
  167. package/dist/storage/currentTaskStore.js +1 -1
  168. package/dist/storage/homeLayout.js +13 -17
  169. package/dist/storage/migrations/agentFailureContext.js +22 -0
  170. package/dist/storage/migrations/currentInputContract.js +86 -0
  171. package/dist/storage/migrations/currentRuntimeContract.js +228 -0
  172. package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
  173. package/dist/storage/migrations/integrationContinuation.js +5 -4
  174. package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
  175. package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
  176. package/dist/storage/migrations/verificationPlanV1.js +162 -0
  177. package/dist/storage/migrations/verificationPolicy.js +74 -0
  178. package/dist/storage/migrations/workItemHistory.js +46 -0
  179. package/dist/storage/persistenceWorker.js +12 -4
  180. package/dist/storage/recordValidation.js +87 -0
  181. package/dist/storage/sqliteSchema.js +173 -1
  182. package/dist/storage/sqliteStore.js +172 -161
  183. package/dist/storage/storageSchema.js +2 -14
  184. package/dist/storage/storageVersions.js +1 -1
  185. package/dist/storage/storeRpc.js +16 -7
  186. package/dist/storage/taskCatalog.js +123 -0
  187. package/dist/storage/taskStore.js +5 -7
  188. package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
  189. package/dist/task/archiveDiagnostics.js +1 -0
  190. package/dist/task/archivePreflight.js +124 -0
  191. package/dist/task/completionReadiness.js +5 -24
  192. package/dist/task/draftPlan.js +0 -53
  193. package/dist/task/nextAction.js +7 -13
  194. package/dist/task/publicationAdoption.js +56 -0
  195. package/dist/task/publicationReference.js +10 -0
  196. package/dist/task/remoteDelivery.js +31 -16
  197. package/dist/task/remoteDeliveryService.js +89 -0
  198. package/dist/task/taskActivation.js +2 -25
  199. package/dist/task/taskActivationService.js +0 -2
  200. package/dist/task/taskRecordReference.js +0 -1
  201. package/dist/task/taskSubmission.js +3 -14
  202. package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
  203. package/dist/telemetry/sqliteTelemetryStore.js +49 -49
  204. package/dist/telemetry/telemetryWiring.js +4 -3
  205. package/dist/tmux/tmuxManager.js +29 -20
  206. package/dist/verification/gateArtifact.js +15 -24
  207. package/dist/verification/gateArtifactStore.js +14 -7
  208. package/dist/verification/verificationGateService.js +29 -88
  209. package/dist/verification/verificationPlan.js +27 -72
  210. package/dist/web/assets/client/app.js +92 -17
  211. package/dist/web/assets/client/components.js +55 -13
  212. package/dist/web/assets/client/i18n.js +72 -4
  213. package/dist/web/assets/client/taskSurface.js +6 -5
  214. package/dist/web/assets/client/view.js +35 -7
  215. package/dist/web/assets/shell.js +6 -0
  216. package/dist/web/assets/styles/layout.js +7 -0
  217. package/dist/web/webServer.js +12 -3
  218. package/dist/web/webSnapshot.js +14 -85
  219. package/dist/web/webTaskSurface.js +17 -46
  220. package/dist/workItem/workItem.js +1 -12
  221. package/dist/workspace/cleanupInspection.js +63 -0
  222. package/dist/workspace/workItemChangeSetManager.js +110 -50
  223. package/docs/agent-result-consumption.md +4 -0
  224. package/docs/agent-result-consumption.zh-CN.md +3 -0
  225. package/docs/agent-runtime-drivers.md +7 -0
  226. package/docs/agent-runtime-drivers.zh-CN.md +5 -0
  227. package/docs/architecture/README.md +2 -0
  228. package/docs/architecture/README.zh-CN.md +3 -1
  229. package/docs/architecture/capabilities-and-resources.md +42 -5
  230. package/docs/architecture/capabilities-and-resources.zh-CN.md +33 -3
  231. package/docs/managed-turn-and-session-runtime.md +121 -1
  232. package/docs/managed-turn-and-session-runtime.zh-CN.md +96 -2
  233. package/docs/observability/README.md +62 -0
  234. package/docs/observability/README.zh-CN.md +47 -0
  235. package/docs/plugin-sdk.md +4 -2
  236. package/docs/project-refresh.md +77 -0
  237. package/docs/project-refresh.zh-CN.md +59 -0
  238. package/docs/provider-retry.md +70 -0
  239. package/docs/provider-runtime.md +3 -1
  240. package/docs/provider-runtime.zh-CN.md +4 -2
  241. package/docs/release-workflow.md +190 -7
  242. package/docs/release-workflow.zh-CN.md +139 -5
  243. package/docs/roles-and-configuration.md +29 -0
  244. package/docs/roles-and-configuration.zh-CN.md +21 -0
  245. package/docs/sqlite-control-plane-design.md +16 -3
  246. package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
  247. package/docs/task-dag-semantics.md +3 -3
  248. package/docs/task-dag-semantics.zh-CN.md +2 -2
  249. package/docs/task-delivery.md +223 -17
  250. package/docs/task-delivery.zh-CN.md +171 -14
  251. package/docs/task-discovery.md +101 -0
  252. package/docs/task-discovery.zh-CN.md +85 -0
  253. package/docs/testing/verification-levels.md +161 -9
  254. package/docs/testing/verification-levels.zh-CN.md +116 -9
  255. package/i18n/README.zh-CN.md +13 -7
  256. package/package.json +1 -1
  257. package/skills/yui-leader/SKILL.md +5 -0
  258. package/skills/yui-leader/references/execution.md +3 -2
  259. package/skills/yui-leader/references/integration.md +9 -5
  260. package/skills/yui-leader/references/planning.md +9 -3
  261. package/skills/yui-operator/SKILL.md +24 -8
  262. package/skills/yui-reviewer/SKILL.md +4 -0
  263. package/skills/yui-runtime/SKILL.md +17 -2
  264. package/skills/yui-runtime/references/publication.md +26 -4
  265. package/skills/yui-runtime/references/recovery.md +71 -0
  266. package/dist/agent/agentRegistry.js +0 -10
  267. package/dist/agentRun/runIdentity.js +0 -22
  268. package/dist/commands/deliveryGuardPreflight.js +0 -30
  269. package/dist/commands/taskIntegrationQueueCommands.js +0 -226
  270. package/dist/commands/taskOverviewCommand.js +0 -376
  271. package/dist/completion/completionWizard.js +0 -125
  272. package/dist/context/dispatchContext.js +0 -110
  273. package/dist/context/wakeNotification.js +0 -144
  274. package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
  275. package/dist/integration/integrationQueueEntry.js +0 -191
  276. package/dist/integration/integrationQueueService.js +0 -810
  277. package/dist/release/fakeReleasePorts.js +0 -55
  278. package/dist/runtime/sessionOwnerRegistry.js +0 -137
  279. package/dist/task/deliveryGuard.js +0 -226
  280. /package/dist/{commands/taskActor.js → task/taskAuthority.js} +0 -0
@@ -1,87 +1,87 @@
1
- import { roleLaunchEventPayload, saveTaskRoleUpdate } from "../role/taskRoleUpdate.js";
2
1
  import { createHash, randomUUID } from "node:crypto";
3
- import { archiveDeliveryWarnings, archiveRetainedResources, renderArchiveDiagnostics, taskArchiveDiagnostics } from "../task/archiveDiagnostics.js";
4
2
  import { isDeepStrictEqual } from "node:util";
5
- import { createRunInput } from "../context/runInputContract.js";
6
- import { buildRunContextPack, buildRunContextDelta, contextSnapshotDeltaRefIds, expandRunContextRef, freezeWorkItemExecutionAssignmentContextSnapshot, freezeReviewStageContextSnapshot, freezeRunContextSnapshot } from "../context/runContextPack.js";
3
+ import { agentExecutionComponentLabel } from "../agent/executionComponents.js";
4
+ import { createRun, runExecutionObservation, runPurposeAdmitsTaskState, withRunContextSnapshot } from "../agentRun/agentRun.js";
5
+ import { isGitArtifactRefString, parseGitArtifactRef } from "../artifacts/gitArtifactRef.js";
7
6
  import { contextSnapshotRef } from "../context/contextSnapshot.js";
7
+ import { buildRunContextDelta, buildRunContextPack, contextSnapshotDeltaRefIds, expandRunContextRef, freezeReviewStageContextSnapshot, freezeRunContextSnapshot, freezeWorkItemExecutionAssignmentContextSnapshot, synthesisSourceRunIds } from "../context/runContextPack.js";
8
+ import { createRunInput } from "../context/runInputContract.js";
9
+ import { parseTaskCatalogOptions, readTaskCatalog, renderTaskCatalog } from "../context/taskCatalog.js";
10
+ import { listContextMessages } from "../context/taskContext.js";
11
+ import { referencedWakeRunIds } from "../context/wakeRunReferences.js";
12
+ import { completeProcessing, releaseProcessing } from "../coordination/workMailbox.js";
13
+ import { enqueueRoleRunDispatch, enqueueWork, settleExactWorkExecution } from "../coordination/workMailboxQueue.js";
8
14
  import { CliError, dataError, roleNotFound, runtimeError, taskNotFound, usageError } from "../errors/cliError.js";
9
- import { createTaskEvent } from "../event/taskEvent.js";
10
- import { createTaskRecordRetirement, isTaskRecordRetired, operationalTaskRecords, taskRecordRetirement } from "../task/taskRecordRetirement.js";
11
- import { referencedWakeRunIds } from "../context/wakeNotification.js";
12
- import { isRoleRunStalled, RUN_PROGRESS_EVENT, RUN_RECOVERED_EVENT } from "../scheduler/roleRunStall.js";
13
- import { readCommandText } from "./textInput.js";
14
- import { assertTaskCompletionPublishedTreeProof } from "./taskCompletionGate.js";
15
- import { createRoleSessionSet, roleAgentSessionResumeMode, updateTaskRoleProviderRuntime, taskRoleControlTarget } from "../executor/agentExecutor.js";
16
- import { transferProviderAuthority, currentProviderConversation } from "../runtime/providerRuntimeIdentity.js";
15
+ import { dispatchReviewSynthesis, selectedReviewSynthesisProducers } from "../execution/reviewMainRun.js";
16
+ import { createExecutionGroup, createReviewExecutionAssignment, createWorkItemExecutionAssignment, createWorkItemExecutionGroup, updateExecutionLane as updateUnifiedExecutionLane, updateWorkItemExecutionLane, workItemExecutionGroupSettled } from "../execution/workItemExecution.js";
17
+ import { projectWorkItemExecution } from "../execution/workItemExecutionProjection.js";
18
+ import { dispatchWorkItemSynthesis, selectedWorkItemSynthesisProducers } from "../execution/workItemMainRun.js";
19
+ import { createRoleSessionSet, roleAgentSessionResumeMode, taskRoleControlTarget, updateTaskRoleProviderRuntime } from "../executor/agentExecutor.js";
17
20
  import { resolveEffectiveLaunch } from "../executor/effectiveLaunch.js";
18
- import { defaultTableWidth, renderTable } from "../output/table.js";
19
- import { agentExecutionComponentLabel } from "../agent/executionComponents.js";
20
- import { agentRunConfigurationLabel, renderAgentRunConfiguration } from "../output/agentRunConfigurationPresentation.js";
21
- import { formatTimestamp } from "../output/timePresentation.js";
22
- import { renderRoleDetails, renderRoleLaunchComparison } from "../output/rolePresentation.js";
23
- import { createTaskMessage, expandTaskMessageResult, recordTaskMessageControlOutcome, taskMessageAuthorLabel, taskMessageInputControlState, updateDraftTaskMessage, withSubmissionReceipt, TASK_SUBMISSION_INTENTS } from "../message/message.js";
24
- import { assertDraftTaskExecutionFree, validateDraftWorkItemEdit } from "../task/draftPlan.js";
25
- import { TASK_PLANNING_ENTERED_EVENT, decideSubmissionRouting, describeSubmissionFeedback, draftActivationState, draftHasEnteredPlanning, normalizeSubmissionIntent, sameSubmissionTarget } from "../task/taskSubmission.js";
26
- import { recordTaskActivationRequestInTransaction, taskActivationOperationRef } from "../task/taskActivationService.js";
27
21
  import { cancelInputRequest } from "../input/inputRequest.js";
28
22
  import { retireExactActiveRun, terminalizeExactTaskRun, validateExactRunReviewRound } from "../lifecycle/exactRunTerminalization.js";
29
- import { copyGlobalRoleToTaskRole, createRole, createRoleAgentBinding, switchActiveRoleAgent, unbindRoleAgent, updateRole } from "../role/role.js";
30
- import { createRun, runPurposeAdmitsTaskState, runExecutionObservation, withRunContextSnapshot } from "../agentRun/agentRun.js";
31
- import { createReviewRound, createTaskReviewRound, createTaskDeltaReviewRound, attachReviewExecutionGroup, finishReviewRound, recordReviewWorkspaceDisposition, retryReviewRound, retryRunningReviewExecutionLane, retryTaskReviewRound, startReplicatedReviewRound, startReviewRound, updateReviewExecutionGroup, validateTaskReviewCandidate } from "../review/reviewRound.js";
23
+ import { resolveTaskInputControl } from "../message/inputControlResolution.js";
24
+ import { createTaskMessage, expandTaskMessageResult, recordTaskMessageControlOutcome, TASK_SUBMISSION_INTENTS, taskMessageAuthorLabel, taskMessageInputControlState, updateDraftTaskMessage, withSubmissionReceipt } from "../message/message.js";
25
+ import { messageContinuationBlocker, resolveMessageRecipient } from "../message/messageContinuation.js";
26
+ import { findTaskInterrupt, reserveTaskInterrupt, taskInterruptReceipt, taskInterruptWasRejected } from "../message/taskInterrupt.js";
27
+ import { agentRunConfigurationLabel, renderAgentRunConfiguration } from "../output/agentRunConfigurationPresentation.js";
28
+ import { renderRoleDetails, renderRoleLaunchComparison } from "../output/rolePresentation.js";
29
+ import { defaultTableWidth, renderTable } from "../output/table.js";
30
+ import { requireResolvedAgentProfileRuntime } from "../profile/agentProfileRuntime.js";
31
+ import { assertProjectActive, resolveProject } from "../repository/project.js";
32
+ import { createProjectResources } from "../resources/projectResourceService.js";
32
33
  import { buildDeltaRecheckDispatchContext, verifyDeltaRecheckDiff } from "../review/deltaRecheck.js";
33
34
  import { isCompletedTaskReviewEvidence } from "../review/reviewAcceptance.js";
34
35
  import { projectReviewerAvailability } from "../review/reviewerAvailability.js";
35
- import { createTaskBrief, updateTaskBrief } from "../brief/taskBrief.js";
36
- import { createDecision, supersedeDecision } from "../decision/decision.js";
37
- import { createMilestone } from "../milestone/milestone.js";
38
- import { runPublicationCommand } from "./taskPublicationCommands.js";
39
- import { runTaskActivationCommand, nonLeaderActivationIdentity } from "./taskActivationCommands.js";
40
- import { assertTaskRemoteDeliveryProof, projectTaskRemoteDeliveryFromStore, renderTaskRemoteDelivery, runTaskRemoteDeliveryCommand } from "./taskRemoteDeliveryCommand.js";
41
- import { enqueueRoleRunDispatch, enqueueWork, settleExactWorkExecution } from "../coordination/workMailboxQueue.js";
42
- import { completeProcessing } from "../coordination/workMailbox.js";
43
- import { runtimeLifecycleTarget, RUNTIME_SESSION_REPLACE_REQUIRED_REASON } from "../runtime/lifecycleReservation.js";
36
+ import { attachReviewExecutionGroup, createReviewRound, createTaskDeltaReviewRound, createTaskReviewRound, finishReviewRound, recordReviewWorkspaceDisposition, retryReviewRound, retryRunningReviewExecutionLane, retryTaskReviewRound, startReplicatedReviewRound, startReviewRound, updateReviewExecutionGroup, validateTaskReviewCandidate } from "../review/reviewRound.js";
37
+ import { sameTaskFinalReviewContract, taskFinalReviewConfig, validateTaskFinalReviewContract } from "../review/taskFinalReviewContract.js";
38
+ import { resolveRecordedTaskFinalReviewContract } from "../review/taskFinalReviewContractResolution.js";
39
+ import { copyGlobalRoleToTaskRole, createRole, createRoleAgentBinding, switchActiveRoleAgent, unbindRoleAgent, updateRole } from "../role/role.js";
40
+ import { roleLaunchEventPayload, saveTaskRoleUpdate } from "../role/taskRoleUpdate.js";
41
+ import { RUNTIME_SESSION_REPLACE_REQUIRED_REASON, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
42
+ import { currentManagedRuntime, resolveManagedTaskReader } from "../runtime/managedCaller.js";
43
+ import { controlProviderRetry, providerRetryProjection, renderProviderRetry } from "../runtime/providerRetry.js";
44
+ import { currentProviderConversation, transferProviderAuthority } from "../runtime/providerRuntimeIdentity.js";
44
45
  import { projectProviderContinuations } from "../runtime/runtimeContinuationProjection.js";
45
46
  import { runtimeObservationFromTaskEvent } from "../runtime/runtimeObservation.js";
46
- import { addTaskProjectBinding, archiveTask, completeTask, createTask, retireTask, reopenTask, taskOwnsManagedWorkspace, updateTaskMetadata } from "../task/task.js";
47
- import { resolveTaskRecordReference } from "../task/taskRecordReference.js";
48
- import { projectCompletionReadiness } from "../task/completionReadiness.js";
49
- import { StorageConflictError } from "../storage/taskStore.js";
47
+ import { failureCapabilitiesCommand } from "../runtime/agentFailureContext.js";
48
+ import { enqueueOperatorEvent } from "../scheduler/operatorEvent.js";
49
+ import { isRoleRunStalled, RUN_PROGRESS_EVENT, RUN_RECOVERED_EVENT } from "../scheduler/roleRunStall.js";
50
+ import { renderWakeReason, wakeReason } from "../scheduler/wakeReason.js";
51
+ import { queueLeaderWakeup } from "../scheduler/wakeupQueue.js";
50
52
  import { planningRuntimeCwd } from "../storage/homeLayout.js";
51
- import { requireResolvedAgentProfileRuntime } from "../profile/agentProfileRuntime.js";
52
- import { assertProjectActive, resolveProject } from "../repository/project.js";
53
- import { currentWorkItemCandidate, currentWorkItemExecutionGroup, workItemExecutionGroupById, createWorkItem, editWorkItemDefinition, attachWorkItemExecutionGroup, updateWorkItemExecutionGroup, retireWorkItem, prepareWorkItemDispatch, submitWorkItemCandidate, updateWorkItemWriteProjects, updateWorkItemStatus } from "../workItem/workItem.js";
53
+ import { StorageConflictError } from "../storage/taskStore.js";
54
+ import { archiveDeliveryWarnings, archiveRetainedResources, renderArchiveDiagnostics, taskArchiveDiagnostics } from "../task/archiveDiagnostics.js";
55
+ import { archiveSettlementChecks } from "../task/archivePreflight.js";
56
+ import { projectCompletionReadiness } from "../task/completionReadiness.js";
57
+ import { assertDraftTaskExecutionFree, validateDraftWorkItemEdit } from "../task/draftPlan.js";
58
+ import { assertTaskRemoteDeliveryProof, projectTaskRemoteDeliveryFromStore } from "../task/remoteDeliveryService.js";
59
+ import { addTaskProjectBinding, archiveTask, completeTask, createTask, reopenTask, retireTask, taskOwnsManagedWorkspace, updateTaskMetadata } from "../task/task.js";
60
+ import { recordTaskActivationRequestInTransaction, taskActivationOperationRef } from "../task/taskActivationService.js";
61
+ import { assertTaskDeliveryAuthority, assertTaskInputControlAuthority } from "../task/taskAuthority.js";
62
+ import { resolveTaskRecordReference } from "../task/taskRecordReference.js";
63
+ import { createTaskRecordRetirement, isTaskRecordRetired, operationalTaskRecords, taskRecordRetirement } from "../task/taskRecordRetirement.js";
64
+ import { decideSubmissionRouting, describeSubmissionFeedback, draftActivationState, draftHasEnteredPlanning, normalizeSubmissionIntent, sameSubmissionTarget, TASK_PLANNING_ENTERED_EVENT } from "../task/taskSubmission.js";
54
65
  import { assertWorkItemDependenciesCompleted as assertWorkItemDependencyGate, WorkItemDependencyGateError } from "../workItem/dependencyGate.js";
55
- import { createExecutionGroup, createReviewExecutionAssignment, createWorkItemExecutionAssignment, createWorkItemExecutionGroup, updateExecutionLane as updateUnifiedExecutionLane, updateWorkItemExecutionLane, workItemExecutionGroupSettled } from "../execution/workItemExecution.js";
56
- import { dispatchWorkItemSynthesis, selectedWorkItemSynthesisProducers } from "../execution/workItemMainRun.js";
57
- import { dispatchReviewSynthesis, selectedReviewSynthesisProducers } from "../execution/reviewMainRun.js";
58
- import { synthesisSourceRunIds } from "../context/runContextPack.js";
59
- import { projectWorkItemExecution } from "../execution/workItemExecutionProjection.js";
60
- import { sameTaskFinalReviewContract, taskFinalReviewConfig, validateTaskFinalReviewContract } from "../review/taskFinalReviewContract.js";
61
- import { resolveRecordedTaskFinalReviewContract } from "../review/taskFinalReviewContractResolution.js";
66
+ import { attachWorkItemExecutionGroup, createWorkItem, currentWorkItemCandidate, currentWorkItemExecutionGroup, editWorkItemDefinition, prepareWorkItemDispatch, retireWorkItem, submitWorkItemCandidate, updateWorkItemExecutionGroup, updateWorkItemStatus, updateWorkItemWriteProjects, workItemExecutionGroupById } from "../workItem/workItem.js";
67
+ import { CleanupInspectionError } from "../workspace/cleanupInspection.js";
62
68
  import { managedWorkspaceKey } from "../worktree/managedWorkspace.js";
69
+ import { runGrantCommand } from "./grantCommands.js";
63
70
  import { hasAgentConfigOptions, hasNoRoleMutation, parseRoleOptions, patchRoleAgentBinding, roleOptionSpecs, roleProfilePatch } from "./roleConfiguration.js";
64
- import { hasRoleLaunchContextOptions, validateConfiguredRoleSkills } from "./roleSkillValidation.js";
65
71
  import { assertRoleRuntimeMutationAllowed } from "./roleRuntimeGuard.js";
72
+ import { hasRoleLaunchContextOptions, validateConfiguredRoleSkills } from "./roleSkillValidation.js";
73
+ import { nonLeaderActivationIdentity, runTaskActivationCommand } from "./taskActivationCommands.js";
74
+ import { assertTaskOpen, clock, exactPositionals, leaderActionEventPayload, leaderMailbox, notifyMailbox, optionalNonEmptyOption, output, parseMultiValueTail, parseTail, presentTime, recordTaskEvent, recordTaskEventRecord, requiredOption, requiredText, requireTask, roleMailbox, taskActor, taskMailbox, taskRef, trimmed } from "./taskCommandSupport.js";
75
+ import { assertTaskCompletionPublishedTreeProof } from "./taskCompletionGate.js";
66
76
  import { runTaskContextCommand } from "./taskContextCommand.js";
67
- import { listContextMessages } from "../context/taskContext.js";
68
- import { createProjectResources } from "../resources/projectResourceService.js";
69
- import { isGitArtifactRefString, parseGitArtifactRef } from "../artifacts/gitArtifactRef.js";
77
+ import { taskBriefCommand, taskDecisionCommand, taskEventCommand, taskMilestoneCommand } from "./taskFactCommands.js";
78
+ import { openInputRequestCount, runTaskInputCommand } from "./taskInputCommands.js";
70
79
  import { runTaskNextActionCommand } from "./taskNextActionCommand.js";
71
- import { runDeliveryGuardPreflight, withGuardWarnings } from "./deliveryGuardPreflight.js";
72
- import { inspectTaskRoleRuntimeStatuses, renderTaskRoleRuntimeStatus, taskRoleActiveWorkLabel, taskRoleLastRunLabel, taskRoleNativeSessionLabel, taskRoleOpenInputLabel, taskRoleTmuxLabel, withTaskRoleHostObservation, taskRoleHostDiagnostic } from "./taskRoleRuntimeStatus.js";
73
- import { assertNoOpenInputRequests, openInputRequestCount, runTaskInputCommand } from "./taskInputCommands.js";
74
- import { runGrantCommand } from "./grantCommands.js";
80
+ import { runPublicationCommand } from "./taskPublicationCommands.js";
81
+ import { renderTaskRemoteDelivery, runTaskRemoteDeliveryCommand } from "./taskRemoteDeliveryCommand.js";
82
+ import { inspectTaskRoleRuntimeStatuses, renderTaskRoleRuntimeStatus, taskRoleActiveWorkLabel, taskRoleHostDiagnostic, taskRoleLastRunLabel, taskRoleNativeSessionLabel, taskRoleOpenInputLabel, taskRoleTmuxLabel, withTaskRoleHostObservation } from "./taskRoleRuntimeStatus.js";
83
+ import { readCommandText } from "./textInput.js";
75
84
  import { runWorkflowCommand } from "./workflowCommands.js";
76
- import { taskLocalActor as resolveTaskLocalActor, assertTaskDeliveryAuthority, assertTaskInputControlAuthority } from "./taskActor.js";
77
- import { currentManagedRuntime, resolveManagedTaskReader } from "../runtime/managedCaller.js";
78
- import { resolveMessageRecipient, messageContinuationBlocker } from "../message/messageContinuation.js";
79
- import { findTaskInterrupt, reserveTaskInterrupt, taskInterruptReceipt, taskInterruptWasRejected } from "../message/taskInterrupt.js";
80
- import { resolveTaskInputControl } from "../message/inputControlResolution.js";
81
- import { enqueueOperatorEvent } from "../scheduler/operatorEvent.js";
82
- import { queueLeaderWakeup } from "../scheduler/wakeupQueue.js";
83
- import { renderWakeReason, wakeReason } from "../scheduler/wakeReason.js";
84
- import { buildTaskOverview, parseTaskListOptions, renderTaskOverview } from "./taskOverviewCommand.js";
85
85
  const LEADER_ROLE = "leader";
86
86
  /** Exact Task-final identity or evidence changed between queueing and dispatch. */
87
87
  export class TaskFinalReviewDispatchDriftError extends CliError {
@@ -250,7 +250,7 @@ export function parseTaskCompletionRequest(args, summaryOverride) {
250
250
  * terminalization precondition before attempting completion. All blockers
251
251
  * are reported in one error instead of one per attempt.
252
252
  */
253
- export function preflightTaskCompletion(taskId, store, options = {}, request = {}) {
253
+ export function preflightTaskCompletion(taskId, store, options = {}, _request = {}) {
254
254
  const task = requireTask(store, taskId);
255
255
  const actor = taskActor(store, options, task.id);
256
256
  assertTaskDeliveryAuthority(store, options.environment, task.id);
@@ -265,7 +265,7 @@ export function preflightTaskCompletion(taskId, store, options = {}, request = {
265
265
  // Integration write. Historical control-plane identity is audit evidence,
266
266
  // not a capability that every compatible CLI must reproduce.
267
267
  const taskFinalReviewContract = taskFinalReviewContractForMutation(store, task.id, options);
268
- const activeTaskReview = store.listReviewRounds(task.id).some((round) => ((round.scope ?? "work-item") === "task"
268
+ const activeTaskReview = store.listReviewRounds(task.id).some((round) => (round.scope === "task"
269
269
  && (round.status === "pending" || round.status === "running")));
270
270
  // Issue 06: one shared readiness projection enumerates every blocker.
271
271
  const readinessFacts = store.readCompletionReadinessFacts(task.id);
@@ -303,7 +303,7 @@ export function runTaskCommand(args, store, options = {}) {
303
303
  const delivery = command === "complete"
304
304
  || (command === "work" && ["dispatch", "synthesize", "review", "accept"].includes(rest[0] ?? ""))
305
305
  || (command === "review" && !["list", "show"].includes(rest[0] ?? ""))
306
- || ((command === "run" || command === "run") && rest[0] === "retry");
306
+ || (command === "run" && rest[0] === "retry");
307
307
  if (delivery && options.environment?.YUI_SESSION_SCOPE === "task"
308
308
  && options.environment.YUI_TASK_ID !== undefined) {
309
309
  assertTaskDeliveryAuthority(store, options.environment, options.environment.YUI_TASK_ID);
@@ -311,7 +311,7 @@ export function runTaskCommand(args, store, options = {}) {
311
311
  switch (command) {
312
312
  case "create": return createTaskCommand(rest, store, options);
313
313
  case "update": return output(updateTaskCommand(rest, store, options));
314
- case "list": return listTaskCommand(rest, store);
314
+ case "list": return listTaskCommand(rest, store, options.environment);
315
315
  case "show": return showTaskCommand(rest, store, options.actualTaskReviewCandidate ?? null);
316
316
  case "context": return runTaskContextCommand(rest, store, options.environment);
317
317
  case "next-action": return runTaskNextActionCommand(rest, store, options.actualTaskReviewCandidate ?? null);
@@ -337,12 +337,11 @@ export function runTaskCommand(args, store, options = {}) {
337
337
  case "role": return taskRoleCommand(rest, store, options);
338
338
  case "work": return taskWorkCommand(rest, store, options);
339
339
  case "review": return taskReviewCommand(rest, store, options);
340
- case "run":
341
340
  case "run": return taskRunCommand(rest, store, options);
342
341
  case "brief": return taskBriefCommand(rest, store, options);
343
342
  case "decision": return taskDecisionCommand(rest, store, options);
344
343
  case "milestone": return taskMilestoneCommand(rest, store, options);
345
- case "event": return taskEventCommand(rest, store);
344
+ case "event": return taskEventCommand(rest, store, options);
346
345
  case "continuation": return taskContinuationCommand(rest, store);
347
346
  default:
348
347
  throw usageError(command === undefined
@@ -466,9 +465,9 @@ function taskProjectCommand(args, store, options) {
466
465
  }
467
466
  return next;
468
467
  });
469
- notifyMailbox(options.runtime, taskMailbox(updated.id), updated.id);
468
+ notifyMailbox(options.runtime, taskMailbox(updated.id));
470
469
  if (updated.status === "active") {
471
- notifyMailbox(options.runtime, leaderMailbox(updated.id), updated.id);
470
+ notifyMailbox(options.runtime, leaderMailbox(updated.id));
472
471
  }
473
472
  return output(`Added Project to ${updated.id}\n`, { task: updated });
474
473
  }
@@ -523,7 +522,7 @@ function updateTaskCommand(args, store, options) {
523
522
  }, options);
524
523
  return `Updated task ${result.id}\n`;
525
524
  }
526
- /** Shared domain transaction for structured capabilities and the legacy CLI.
525
+ /** Shared domain transaction for structured capabilities and the CLI.
527
526
  * Parsing text flags must not become an alternate business write path. */
528
527
  export function updateTaskMetadataCommand(store, taskId, patch, options = {}) {
529
528
  if (Object.keys(patch).length === 0)
@@ -545,7 +544,7 @@ export function updateTaskMetadataCommand(store, taskId, patch, options = {}) {
545
544
  enqueueWork(tx, taskMailbox(updated.id), "task-updated", now, [taskRef(updated.id)]);
546
545
  return updated;
547
546
  });
548
- notifyMailbox(options.runtime, taskMailbox(result.id), result.id);
547
+ notifyMailbox(options.runtime, taskMailbox(result.id));
549
548
  return result;
550
549
  }
551
550
  function routeUserSubmission(tx, task, actor, body, intent, now, submissionKey, target, inputControl) {
@@ -628,8 +627,7 @@ function routeUserSubmission(tx, task, actor, body, intent, now, submissionKey,
628
627
  requestId: `submit-${message.id}`,
629
628
  actorId: identity.actorId,
630
629
  authorityRef: identity.authorityRef,
631
- environmentPlan: routing.environmentPlan,
632
- origin: "submit-develop"
630
+ environmentPlan: routing.environmentPlan
633
631
  }, now);
634
632
  activationRef = taskActivationOperationRef(task.id, `submit-${message.id}`);
635
633
  enqueueWork(tx, taskMailbox(task.id), "activation-requested", now, [taskRef(task.id)]);
@@ -681,7 +679,7 @@ function replayKeyedSubmission(task, prior, kind, body, intent, target) {
681
679
  const receipt = prior.submissionReceipt;
682
680
  if (prior.kind !== kind
683
681
  || prior.body !== body
684
- || normalizeSubmissionIntent(prior.intent) !== intent
682
+ || prior.intent !== intent
685
683
  || receipt === undefined
686
684
  || (target !== undefined && !sameSubmissionTarget(receipt.target, target))) {
687
685
  throw new StorageConflictError(`Submission key ${prior.submissionKey} was already used for a different submission on ${task.id}/${prior.id}.`);
@@ -743,7 +741,7 @@ const SUBMISSION_ACTIVATION_LABEL = {
743
741
  function renderSubmissionNextStep(step) {
744
742
  switch (step.kind) {
745
743
  case "activate-manually":
746
- return `activate it explicitly with "yui task activate ${step.taskId}".`;
744
+ return `request activation with "yui task activation request ${step.taskId} --request-id <id> --environment <plan>".`;
747
745
  case "start-execution":
748
746
  return `start execution with "yui task execution start ${step.taskId}", then submit develop again.`;
749
747
  case "await-pending-activation":
@@ -797,7 +795,7 @@ export function submitOperatorMessage(body, taskId, store, options = {}, intent,
797
795
  const routed = routeUserSubmission(tx, createdAgg.task, "operator", body, effectiveIntent, now, submissionKey, { kind: "create" });
798
796
  return { ...routed, task: createdAgg.task, created: true };
799
797
  });
800
- notifyMailbox(options.runtime, result.queuedForLeader ? leaderMailbox(result.task.id) : taskMailbox(result.task.id), result.task.id);
798
+ notifyMailbox(options.runtime, result.queuedForLeader ? leaderMailbox(result.task.id) : taskMailbox(result.task.id));
801
799
  const header = result.created
802
800
  ? `Created Draft task ${result.task.id}: ${result.task.title}\n`
803
801
  : "";
@@ -824,7 +822,7 @@ function createTaskCommand(args, store, options) {
824
822
  projectBindings: parsed.projectBindings,
825
823
  ...(parsed.type === undefined ? {} : { type: parsed.type })
826
824
  }, now, parsed.defaultProjectIds));
827
- notifyMailbox(options.runtime, taskMailbox(created.task.id), created.task.id);
825
+ notifyMailbox(options.runtime, taskMailbox(created.task.id));
828
826
  return output(`Created Draft task ${created.task.id}: ${created.task.title}\n`
829
827
  + `Assigned role: ${created.leader.name}\n`
830
828
  + `Type: ${created.task.type ?? "unspecified"}\n`
@@ -904,14 +902,9 @@ function createTaskAggregate(store, title, metadata, now, defaultProjectIds = []
904
902
  enqueueWork(store, taskMailbox(task.id), "task-created", now, [taskRef(task.id)]);
905
903
  return { task, leader };
906
904
  }
907
- function listTaskCommand(args, store) {
908
- const options = parseTaskListOptions(args);
909
- const snapshot = store.transaction((reader) => ({
910
- result: buildTaskOverview(reader, options),
911
- timeZone: reader.getConfig().timeZone
912
- }));
913
- const rendered = renderTaskOverview(snapshot.result, options, snapshot.timeZone, defaultTableWidth());
914
- return output(rendered, snapshot.result);
905
+ function listTaskCommand(args, store, environment = {}) {
906
+ const result = readTaskCatalog(store, parseTaskCatalogOptions(args), environment);
907
+ return output(renderTaskCatalog(result), result);
915
908
  }
916
909
  function showTaskCommand(args, store, currentTaskCandidate) {
917
910
  const [taskId] = args;
@@ -1044,8 +1037,8 @@ function activateTaskCommand(args, store, options) {
1044
1037
  throw usageError(`Task ${task.id} activation requires atomic workspace adoption through the CLI preflight.`);
1045
1038
  });
1046
1039
  if (result.changed) {
1047
- notifyMailbox(options.runtime, taskMailbox(result.task.id), result.task.id);
1048
- notifyMailbox(options.runtime, leaderMailbox(result.task.id), result.task.id);
1040
+ notifyMailbox(options.runtime, taskMailbox(result.task.id));
1041
+ notifyMailbox(options.runtime, leaderMailbox(result.task.id));
1049
1042
  }
1050
1043
  return result.changed
1051
1044
  ? `Activated task ${result.task.id}\n`
@@ -1179,7 +1172,7 @@ function completeTaskCommand(args, store, options) {
1179
1172
  };
1180
1173
  });
1181
1174
  if (result.changed) {
1182
- notifyMailbox(options.runtime, { kind: "operator" }, result.task.id);
1175
+ notifyMailbox(options.runtime, { kind: "operator" });
1183
1176
  }
1184
1177
  if (result.finalReview !== undefined) {
1185
1178
  const status = result.finalReview.status === "pending"
@@ -1240,9 +1233,9 @@ function reopenTaskCommand(args, store, options) {
1240
1233
  return { task: active, changed: true, wakeLeader: actor !== "leader" };
1241
1234
  });
1242
1235
  if (result.changed) {
1243
- notifyMailbox(options.runtime, taskMailbox(result.task.id), result.task.id);
1236
+ notifyMailbox(options.runtime, taskMailbox(result.task.id));
1244
1237
  if (result.wakeLeader) {
1245
- notifyMailbox(options.runtime, leaderMailbox(result.task.id), result.task.id);
1238
+ notifyMailbox(options.runtime, leaderMailbox(result.task.id));
1246
1239
  }
1247
1240
  }
1248
1241
  return result.changed
@@ -1267,24 +1260,9 @@ function archiveTaskCommand(args, store, options) {
1267
1260
  ? assertTaskRemoteDeliveryProof(tx, task, options.archiveRemoteDeliveryProof)
1268
1261
  : undefined;
1269
1262
  if (!request.force) {
1270
- assertNoOpenInputRequests(tx, task.id, "archiving the Task");
1271
- const unsettledWork = tx.listWorkItems(task.id).find((item) => item.status === "open");
1272
- if (unsettledWork !== undefined) {
1273
- throw usageError(`Work Item ${unsettledWork.id} must be accepted or explicitly retired before archive.`);
1274
- }
1275
- const unresolvedIntegration = tx.listIntegrationAttempts(task.id).find((integration) => (integration.status === "running"
1276
- || integration.status === "blocked"
1277
- || integration.status === "conflicted"
1278
- || integration.status === "validating"));
1279
- if (unresolvedIntegration !== undefined) {
1280
- throw usageError(`Task ${task.id} has an unresolved Integration Attempt: ${unresolvedIntegration.id}.`);
1281
- }
1282
- const activeArchiveJob = tx.listDurableJobs(task.id).find((job) => (job.status === "queued"
1283
- || job.status === "running"
1284
- || (job.status === "unknown-needs-attention" && job.acknowledgedAt === undefined)));
1285
- if (activeArchiveJob !== undefined) {
1286
- throw usageError(`Task ${task.id} has an active DurableJob: ${activeArchiveJob.id}/${activeArchiveJob.status}.`);
1287
- }
1263
+ const checks = archiveSettlementChecks(tx, task);
1264
+ if (checks.length > 0)
1265
+ throw new CleanupInspectionError(checks);
1288
1266
  if (task.cwd !== undefined || tx.listManagedWorkspaces(task.id).length > 0) {
1289
1267
  throw usageError(`Task ${task.id} still has managed worktrees; clean them before archiving.`);
1290
1268
  }
@@ -1348,7 +1326,7 @@ function archiveTaskCommand(args, store, options) {
1348
1326
  return { task: archived, changed: true };
1349
1327
  });
1350
1328
  if (result.changed && !request.force)
1351
- options.runtime?.notifyStateChanged(result.task.id);
1329
+ options.runtime?.notifyMailboxChanged({ kind: "task", taskId: result.task.id });
1352
1330
  const diagnostics = taskArchiveDiagnostics(store, result.task);
1353
1331
  return output((result.changed
1354
1332
  ? `Archived task ${result.task.id}\n`
@@ -1381,8 +1359,8 @@ function cancelTaskCommand(args, store, options) {
1381
1359
  // AgentRuns, inputs, WorkItems and their results remain independently readable.
1382
1360
  return cancelled;
1383
1361
  });
1384
- options.runtime?.notifyStateChanged(result.id);
1385
- notifyMailbox(options.runtime, { kind: "operator" }, result.id);
1362
+ options.runtime?.notifyMailboxChanged({ kind: "task", taskId: result.id });
1363
+ notifyMailbox(options.runtime, { kind: "operator" });
1386
1364
  return output(`Cancelled task ${result.id}\n`, { task: result });
1387
1365
  }
1388
1366
  /** Historical evidence only; these snapshots are never dispatch input. */
@@ -1483,8 +1461,8 @@ function retireTaskCommand(args, store, options) {
1483
1461
  return { task: retired, changed: true };
1484
1462
  });
1485
1463
  if (result.changed) {
1486
- options.runtime?.notifyStateChanged(result.task.id);
1487
- notifyMailbox(options.runtime, { kind: "operator" }, result.task.id);
1464
+ options.runtime?.notifyMailboxChanged({ kind: "task", taskId: result.task.id });
1465
+ notifyMailbox(options.runtime, { kind: "operator" });
1488
1466
  }
1489
1467
  return output(result.changed
1490
1468
  ? `Retired task ${result.task.id}\n`
@@ -1512,9 +1490,9 @@ function assertTaskRetirementProof(store, task, proof) {
1512
1490
  throw usageError(`Task workspaces changed after retirement preflight: ${task.id}.`);
1513
1491
  }
1514
1492
  }
1515
- export function parseTaskArchiveArguments(args) {
1493
+ export function parseTaskArchiveArguments(args, command = "archive") {
1516
1494
  const usage = "Task archive usage: "
1517
- + "yui task archive <id> (--integrated|--abandon) [--force].";
1495
+ + `yui task ${command} <id> (--integrated|--abandon) [--force].`;
1518
1496
  const taskId = args[0]?.trim();
1519
1497
  const flags = args.slice(1);
1520
1498
  if (taskId === undefined
@@ -1552,14 +1530,9 @@ export function validateTaskArchiveRequest(args, store, options = {}) {
1552
1530
  throw usageError(`Task ${task.id} must be completed or retired before it can be archived.`);
1553
1531
  }
1554
1532
  if (task.status !== "archived" && !request.force) {
1555
- assertNoOpenInputRequests(store, task.id, "archiving the Task");
1556
- const unresolvedIntegration = store.listIntegrationAttempts(task.id).find((integration) => (integration.status === "running"
1557
- || integration.status === "blocked"
1558
- || integration.status === "conflicted"
1559
- || integration.status === "validating"));
1560
- if (unresolvedIntegration !== undefined) {
1561
- throw usageError(`Task ${task.id} has an unresolved Integration Attempt: ${unresolvedIntegration.id}.`);
1562
- }
1533
+ const checks = archiveSettlementChecks(store, task);
1534
+ if (checks.length > 0)
1535
+ throw new CleanupInspectionError(checks);
1563
1536
  }
1564
1537
  return request;
1565
1538
  }
@@ -1601,7 +1574,7 @@ function taskMessageCommand(args, store, options) {
1601
1574
  enqueueWork(tx, taskMailbox(ref.taskId), "message-continuation", now, [messageRef(ref.taskId, current.id)]);
1602
1575
  return updated;
1603
1576
  });
1604
- notifyMailbox(options.runtime, taskMailbox(ref.taskId), ref.taskId);
1577
+ notifyMailbox(options.runtime, taskMailbox(ref.taskId));
1605
1578
  return output(`Handed off Message ${ref.localId} to ${message.recipient.roleName}.\n`, message);
1606
1579
  }
1607
1580
  if (command === "show") {
@@ -1619,22 +1592,11 @@ function taskMessageCommand(args, store, options) {
1619
1592
  return { kind: "output", output: `${JSON.stringify(expanded, null, 2)}\n`, data: expanded };
1620
1593
  }
1621
1594
  if (command === "send") {
1622
- const usage = "Task message send usage: yui task message send <id> (<body>|--body-file <path|->) [--intent record|discuss|develop] [--request-id <key>] [--wake-policy leader|none] [--to <role> --work-item <id>|--review-round <id>].";
1623
- const parsed = parseTail(rest, new Set(["--body-file", "--intent", "--request-id", "--wake-policy", "--to", "--work-item", "--review-round"]), usage);
1595
+ const usage = "Task message send usage: yui task message send <id> (<body>|--body-file <path|->) [--intent record|discuss|develop] [--request-id <key>] [--to <role> --work-item <id>|--review-round <id>].";
1596
+ const parsed = parseTail(rest, new Set(["--body-file", "--intent", "--request-id", "--to", "--work-item", "--review-round"]), usage);
1624
1597
  if (parsed.positionals.length < 1 || parsed.positionals.length > 2)
1625
1598
  throw usageError(usage);
1626
1599
  const body = readCommandText(parsed.positionals[1], parsed.options.get("--body-file"), "--body", usage);
1627
- const wakePolicyRaw = parsed.options.get("--wake-policy");
1628
- let wakePolicy;
1629
- if (wakePolicyRaw === undefined) {
1630
- wakePolicy = undefined;
1631
- }
1632
- else if (wakePolicyRaw === "leader" || wakePolicyRaw === "none") {
1633
- wakePolicy = wakePolicyRaw;
1634
- }
1635
- else {
1636
- throw usageError(`--wake-policy must be 'leader' or 'none': ${wakePolicyRaw}.`);
1637
- }
1638
1600
  const intent = parseSubmissionIntentOption(parsed.options.get("--intent"), usage);
1639
1601
  const submissionKey = parsed.options.get("--request-id");
1640
1602
  if (submissionKey !== undefined && submissionKey.trim().length === 0) {
@@ -1645,7 +1607,7 @@ function taskMessageCommand(args, store, options) {
1645
1607
  const reviewRoundId = parsed.options.get("--review-round");
1646
1608
  if (recipientRole === undefined && (workItemId !== undefined || reviewRoundId !== undefined))
1647
1609
  throw usageError("--to is required for scoped Message delivery.");
1648
- const result = sendTaskMessageCommand(store, parsed.positionals[0], body, wakePolicy, options, recipientRole === undefined ? undefined : { roleName: recipientRole, workItemId, reviewRoundId }, intent, submissionKey);
1610
+ const result = sendTaskMessageCommand(store, parsed.positionals[0], body, options, recipientRole === undefined ? undefined : { roleName: recipientRole, workItemId, reviewRoundId }, intent, submissionKey);
1649
1611
  // A user/operator submission returns the unified §2.5 feedback; render each
1650
1612
  // facet on its own line and expose the structure to non-text callers.
1651
1613
  if (result.feedback !== undefined) {
@@ -1724,6 +1686,13 @@ function taskMessageCommand(args, store, options) {
1724
1686
  * reusing the requestId with different content is a conflict, never a second
1725
1687
  * input (decision-3 §5/§6).
1726
1688
  */
1689
+ export function applyTaskInputControl(taskId, input, store, options) {
1690
+ if (input.action === "queue")
1691
+ return queueTaskInput(taskId, input, store, options);
1692
+ if (input.action === "steer")
1693
+ return steerTaskInput(taskId, input, store, options);
1694
+ return interruptTaskInput(taskId, input, store, options);
1695
+ }
1727
1696
  function queueTaskMessage(args, store, options) {
1728
1697
  const usage = "Task message queue usage: yui task message queue <id> (<body>|--body-file <path|->) --request-id <id> [--to <role> --work-item <id>|--review-round <id>].";
1729
1698
  const parsed = parseTail(args, new Set(["--body-file", "--request-id", "--to", "--work-item", "--review-round"]), usage);
@@ -1734,10 +1703,16 @@ function queueTaskMessage(args, store, options) {
1734
1703
  const recipientRole = parsed.options.get("--to");
1735
1704
  const workItemId = parsed.options.get("--work-item");
1736
1705
  const reviewRoundId = parsed.options.get("--review-round");
1706
+ return applyTaskInputControl(parsed.positionals[0], {
1707
+ action: "queue", body, requestId, to: recipientRole, workItem: workItemId, reviewRound: reviewRoundId
1708
+ }, store, options);
1709
+ }
1710
+ function queueTaskInput(taskId, input, store, options) {
1711
+ const { body, requestId, to: recipientRole, workItem: workItemId, reviewRound: reviewRoundId } = input;
1737
1712
  if (recipientRole === undefined && (workItemId !== undefined || reviewRoundId !== undefined)) {
1738
1713
  throw usageError("--to is required for scoped Message delivery.");
1739
1714
  }
1740
- const result = sendTaskMessageCommand(store, parsed.positionals[0], body, undefined, options, recipientRole === undefined ? undefined : { roleName: recipientRole, workItemId, reviewRoundId }, undefined, undefined, { action: "queue", requestId });
1715
+ const result = sendTaskMessageCommand(store, taskId, body, options, recipientRole === undefined ? undefined : { roleName: recipientRole, workItemId, reviewRoundId }, undefined, undefined, { action: "queue", requestId });
1741
1716
  const reason = result.message.continuation?.notDeliveredReason;
1742
1717
  const delivery = result.idempotentReplay ? { state: "idempotent-replay" }
1743
1718
  : reason !== undefined ? { state: "not-delivered", reason }
@@ -1768,6 +1743,13 @@ function steerTaskMessage(args, store, options) {
1768
1743
  if (recipientRole === undefined) {
1769
1744
  throw usageError("steer requires --to <role>; it targets that Role's exact current Turn.");
1770
1745
  }
1746
+ return applyTaskInputControl(parsed.positionals[0], {
1747
+ action: "steer", body, requestId, expectedTarget, to: recipientRole,
1748
+ workItem: workItemId, reviewRound: reviewRoundId
1749
+ }, store, options);
1750
+ }
1751
+ function steerTaskInput(taskId, input, store, options) {
1752
+ const { body, requestId, expectedTarget, to: recipientRole, workItem: workItemId, reviewRound: reviewRoundId } = input;
1771
1753
  // decision-3 §9: a Task Leader's current native turn (including a Draft's
1772
1754
  // planning turn) is a real turn but NOT an implicit AgentRun, so steering it
1773
1755
  // never establishes a Worker-style Assignment. It is persisted as an ordinary
@@ -1779,8 +1761,8 @@ function steerTaskMessage(args, store, options) {
1779
1761
  throw usageError("Steering the Leader targets its current turn directly; it takes no --work-item/--review-round Assignment.");
1780
1762
  }
1781
1763
  const result = leaderTarget
1782
- ? sendTaskMessageCommand(store, parsed.positionals[0], body, "none", options, undefined, undefined, undefined, { action: "steer", requestId, expectedTarget })
1783
- : sendTaskMessageCommand(store, parsed.positionals[0], body, undefined, options, { roleName: recipientRole, workItemId, reviewRoundId }, undefined, undefined, { action: "steer", requestId, expectedTarget });
1764
+ ? sendTaskMessageCommand(store, taskId, body, options, undefined, undefined, undefined, { action: "steer", requestId, expectedTarget })
1765
+ : sendTaskMessageCommand(store, taskId, body, options, { roleName: recipientRole, workItemId, reviewRoundId }, undefined, undefined, { action: "steer", requestId, expectedTarget });
1784
1766
  const roleName = result.message.recipient?.roleName ?? recipientRole;
1785
1767
  if (result.idempotentReplay) {
1786
1768
  // A replay is a later reader (another CLI invocation): surface the durable
@@ -1875,12 +1857,9 @@ function steerInputDelivery(taskId, messageId, roleName, body) {
1875
1857
  * execution is decided by the Controller from the Task's own status, so this
1876
1858
  * function never names a purpose and no second planning path exists.
1877
1859
  */
1878
- export function sendTaskMessageCommand(store, taskId, body, wakePolicy, options = {}, recipient, intent, submissionKey, inputControl) {
1860
+ export function sendTaskMessageCommand(store, taskId, body, options = {}, recipient, intent, submissionKey, inputControl) {
1879
1861
  if (!body.trim())
1880
1862
  throw usageError("Message body is required.");
1881
- if (recipient !== undefined && wakePolicy !== undefined) {
1882
- throw usageError("--wake-policy applies only to unaddressed Leader Messages; an owner-directed Message uses its exact continuation boundary.");
1883
- }
1884
1863
  if (recipient !== undefined && intent !== undefined) {
1885
1864
  throw usageError("A submission intent applies only to unaddressed Leader Messages; an owner-directed Message uses its exact continuation boundary.");
1886
1865
  }
@@ -1935,7 +1914,7 @@ export function sendTaskMessageCommand(store, taskId, body, wakePolicy, options
1935
1914
  // owner-directed continuations keep their exact existing boundary and never
1936
1915
  // gain develop authority (task-32 §2.5).
1937
1916
  if (recipient === undefined && (actor === "user" || actor === "operator")) {
1938
- const effectiveIntent = normalizeSubmissionIntent(intent, wakePolicy);
1917
+ const effectiveIntent = inputControl?.action === "steer" ? "record" : normalizeSubmissionIntent(intent);
1939
1918
  const routed = routeUserSubmission(tx, task, actor, body, effectiveIntent, now, submissionKey, { kind: "task", taskId: task.id }, inputControl);
1940
1919
  return {
1941
1920
  task: routed.task, message: routed.message, actor,
@@ -1990,11 +1969,11 @@ export function sendTaskMessageCommand(store, taskId, body, wakePolicy, options
1990
1969
  if (result.idempotentReplay)
1991
1970
  return result;
1992
1971
  if (recipient !== undefined) {
1993
- notifyMailbox(options.runtime, taskMailbox(result.task.id), result.task.id);
1972
+ notifyMailbox(options.runtime, taskMailbox(result.task.id));
1994
1973
  }
1995
1974
  else if (result.actor !== "leader") {
1996
1975
  notifyMailbox(options.runtime, result.queuedForLeader
1997
- ? leaderMailbox(result.task.id) : taskMailbox(result.task.id), result.task.id);
1976
+ ? leaderMailbox(result.task.id) : taskMailbox(result.task.id));
1998
1977
  }
1999
1978
  return result;
2000
1979
  }
@@ -2015,19 +1994,11 @@ export function leaderWakingTaskStatus(status) {
2015
1994
  return status === "active" || status === "draft";
2016
1995
  }
2017
1996
  function updateMessage(args, store, options) {
2018
- const usage = "Task message update usage: yui task message update <task>/<message> (<body>|--body-file <path|->) [--wake-policy leader|none].";
2019
- const parsed = parseTail(args, new Set(["--body-file", "--wake-policy"]), usage);
1997
+ const usage = "Task message update usage: yui task message update <task>/<message> (<body>|--body-file <path|->).";
1998
+ const parsed = parseTail(args, new Set(["--body-file"]), usage);
2020
1999
  if (parsed.positionals.length < 1 || parsed.positionals.length > 2)
2021
2000
  throw usageError(usage);
2022
2001
  const body = readCommandText(parsed.positionals[1], parsed.options.get("--body-file"), "--body", usage);
2023
- const wakePolicyRaw = parsed.options.get("--wake-policy");
2024
- const wakePolicy = wakePolicyRaw === undefined
2025
- ? undefined
2026
- : wakePolicyRaw === "leader" || wakePolicyRaw === "none"
2027
- ? wakePolicyRaw
2028
- : (() => {
2029
- throw usageError(`--wake-policy must be 'leader' or 'none': ${wakePolicyRaw}.`);
2030
- })();
2031
2002
  const reference = taskRecordReference(parsed.positionals[0], "message", "Message reference", options);
2032
2003
  const now = clock(options);
2033
2004
  const result = store.transaction((tx) => {
@@ -2048,24 +2019,42 @@ function updateMessage(args, store, options) {
2048
2019
  throw usageError(`Task Message is retired: ${task.id}/${message.id}.`);
2049
2020
  }
2050
2021
  const updated = updateDraftTaskMessage(message, {
2051
- body,
2052
- ...(wakePolicy === undefined ? {} : { wakePolicy })
2022
+ body
2053
2023
  });
2024
+ if (updated === message)
2025
+ return { task, message, queuedForLeader: false, changed: false };
2054
2026
  tx.updateMessage(task.id, updated);
2055
2027
  recordTaskEvent(tx, task.id, "message.updated", {
2056
2028
  messageId: updated.id,
2057
- updatedBy: actor,
2058
- ...(wakePolicy === undefined ? {} : { wakePolicy })
2029
+ updatedBy: actor
2059
2030
  }, now);
2060
- const queuedForLeader = updated.wakePolicy !== "none";
2031
+ // An edit can continue a discussion, but it is not a new develop request.
2032
+ // Use the submission router's current activation/planning facts so pending
2033
+ // or failed activation cannot be turned into a planning wake by an edit.
2034
+ const routing = decideSubmissionRouting({
2035
+ intent: updated.intent,
2036
+ status: task.status,
2037
+ enteredPlanning: draftHasEnteredPlanning(tx, task),
2038
+ activation: draftActivationState(task.activationRequest),
2039
+ executionEnabled: task.executionGate.state === "enabled",
2040
+ developEnvironmentPlan: { kind: "empty" }
2041
+ });
2042
+ const queuedForLeader = routing.kind === "enter-planning" || routing.kind === "continue-planning";
2043
+ if (routing.kind === "enter-planning") {
2044
+ recordTaskEvent(tx, task.id, TASK_PLANNING_ENTERED_EVENT, {
2045
+ messageId: updated.id, intent: updated.intent
2046
+ }, now);
2047
+ }
2061
2048
  if (queuedForLeader) {
2062
2049
  enqueueWork(tx, leaderMailbox(task.id), actor === "operator" ? "operator-input" : "user-message", now, [messageRef(task.id, updated.id)], { source: actor });
2063
2050
  }
2064
- return { task, message: updated, queuedForLeader };
2051
+ return { task, message: updated, queuedForLeader, changed: true };
2065
2052
  });
2066
- notifyMailbox(options.runtime, result.queuedForLeader
2067
- ? leaderMailbox(result.task.id) : taskMailbox(result.task.id), result.task.id);
2068
- return output(`Updated Task Message ${result.task.id}/${result.message.id}\n`, {
2053
+ if (result.changed) {
2054
+ notifyMailbox(options.runtime, result.queuedForLeader
2055
+ ? leaderMailbox(result.task.id) : taskMailbox(result.task.id));
2056
+ }
2057
+ return output(`${result.changed ? "Updated" : "Unchanged"} Task Message ${result.task.id}/${result.message.id}\n`, {
2069
2058
  message: result.message
2070
2059
  });
2071
2060
  }
@@ -2112,32 +2101,58 @@ function retireMessage(args, store, options) {
2112
2101
  return { task, message, changed: true };
2113
2102
  });
2114
2103
  if (result.changed)
2115
- options.runtime?.notifyStateChanged(result.task.id);
2104
+ options.runtime?.notifyMailboxChanged({ kind: "task", taskId: result.task.id });
2116
2105
  return `Retired Task Message ${result.task.id}/${result.message.id}\n`;
2117
2106
  }
2118
- /**
2119
- * Issue 05: force-wake escape hatch. Bypasses the actionability digest and
2120
- * enqueues exactly one Leader wakeup with an auditable reason. The reason is
2121
- * truncated to keep the event payload compact.
2122
- */
2123
- function taskWakeForceCommand(args, store, options) {
2124
- const usage = "Task wake usage: yui task wake <id> --force --reason <text>.";
2125
- const parsed = parseTail(args, new Set(["--reason"]), usage, new Set(["--force"]));
2126
- exactPositionals(parsed.positionals, 1, usage);
2127
- if (!parsed.options.has("--force")) {
2128
- throw usageError("--force is required to wake a Task.", usage);
2129
- }
2107
+ /** Explicitly retry one proven-rejected notification, preserving its inputs. */
2108
+ function taskWakeRetryCommand(args, store, options) {
2109
+ const usage = "Task wake retry usage: yui task wake retry <task> <wake> --reason <correction>.";
2110
+ const parsed = parseTail(args, new Set(["--reason"]), usage);
2111
+ exactPositionals(parsed.positionals, 2, usage);
2130
2112
  const reason = requiredOption(parsed.options, "--reason");
2131
2113
  const now = clock(options);
2132
- const task = requireTask(store, parsed.positionals[0]);
2133
- assertTaskOpen(task);
2134
- const wakeReasonTag = wakeReason("force-wake", truncateEventNote(reason));
2135
- store.transaction((tx) => {
2136
- queueLeaderWakeup(tx, task.id, wakeReasonTag, now);
2137
- recordTaskEvent(tx, task.id, "task.wake-forced", { reason: wakeReasonTag }, now);
2114
+ const result = store.transaction((tx) => {
2115
+ const task = requireTask(tx, parsed.positionals[0]);
2116
+ assertTaskOpen(task);
2117
+ taskActor(tx, options, task.id);
2118
+ const wakeId = parsed.positionals[1];
2119
+ const wake = tx.getTaskWake(task.id, wakeId);
2120
+ if (wake === null)
2121
+ throw dataError(`Wake not found: ${wakeId}.`);
2122
+ if (wake.status === "consumed")
2123
+ throw usageError("An accepted notification cannot be retried.");
2124
+ const mailbox = tx.getWorkMailbox(leaderMailbox(task.id));
2125
+ const claim = mailbox?.processing;
2126
+ if (claim == null || claim.owner !== `leader-notification:${wakeId}`) {
2127
+ throw usageError("This wake has no rejected notification claim.");
2128
+ }
2129
+ const events = tx.listEvents(task.id);
2130
+ const previous = events.filter(event => event.type === "notification.delivery" && event.payload.attemptId === claim.batchId).at(-1);
2131
+ if (previous?.payload.outcome !== "rejected") {
2132
+ throw usageError("Only a rejected notification can be retried; unknown acceptance must not be replayed.");
2133
+ }
2134
+ const provider = tx.getTaskRoleSessionSet(task.id, "leader")?.providerBinding;
2135
+ const accepted = events.some(event => {
2136
+ const observation = runtimeObservationFromTaskEvent(event);
2137
+ return observation?.fence.roleName === "leader" && observation.fence.receiptId === claim.batchId
2138
+ && ((observation.kind === "turn.accepted" && observation.authority !== "transport")
2139
+ || ["turn.completed", "turn.failed", "turn.cancelled"].includes(observation.kind));
2140
+ });
2141
+ if (accepted || tx.getActiveRun(task.id, "leader") !== null
2142
+ || provider?.authority.owner === "human" || provider?.authority.owner === "unknown"
2143
+ || (provider?.run != null && (["submitting", "accepted", "delivery-unknown"].includes(provider.run.status)
2144
+ || provider.run.attemptId === claim.batchId && provider.run.status !== "rejected"))) {
2145
+ throw usageError("Native execution is not proven unaccepted and quiescent; this notification cannot be replayed.");
2146
+ }
2147
+ tx.saveWorkMailbox(releaseProcessing(mailbox, claim.batchId));
2148
+ queueLeaderWakeup(tx, task.id, wakeReason("notification-retry", wakeId), now);
2149
+ recordTaskEvent(tx, task.id, "notification.retry-requested", {
2150
+ wakeId, attemptId: claim.batchId, reason
2151
+ }, now);
2152
+ return { taskId: task.id, wakeId, outcome: "queued" };
2138
2153
  });
2139
- notifyMailbox(options.runtime, leaderMailbox(task.id), task.id);
2140
- return `Woke ${task.id} (${wakeReasonTag})\n`;
2154
+ notifyMailbox(options.runtime, leaderMailbox(result.taskId));
2155
+ return output(`Queued rejected ${result.wakeId} for retry; original input was preserved.\n`, result);
2141
2156
  }
2142
2157
  function taskRoleCommand(args, store, options) {
2143
2158
  const [command, ...rest] = args;
@@ -2194,9 +2209,15 @@ function interruptTaskRole(args, store, options) {
2194
2209
  const expectedTarget = requiredOption(parsed.options, "--expected-target");
2195
2210
  const thenMessageRef = optionalNonEmptyOption(parsed.options, "--then-message");
2196
2211
  const requestId = optionalNonEmptyOption(parsed.options, "--request-id");
2212
+ return applyTaskInputControl(parsed.positionals[0], {
2213
+ action: "interrupt", expectedTarget, role: parsed.positionals[1], thenMessage: thenMessageRef, requestId
2214
+ }, store, options);
2215
+ }
2216
+ function interruptTaskInput(taskId, input, store, options) {
2217
+ const { expectedTarget, thenMessage: thenMessageRef, requestId } = input;
2197
2218
  const now = clock(options);
2198
- const task = requireTask(store, parsed.positionals[0]);
2199
- const role = requireRole(store, task.id, parsed.positionals[1]);
2219
+ const task = requireTask(store, taskId);
2220
+ const role = requireRole(store, task.id, input.role);
2200
2221
  const fingerprint = createHash("sha256").update(JSON.stringify({
2201
2222
  roleName: role.name, expectedTarget, thenMessageRef: thenMessageRef ?? null
2202
2223
  })).digest("hex");
@@ -2373,7 +2394,7 @@ function registerInterruptThen(store, taskId, roleName, thenMessageRef, target,
2373
2394
  // AgentRun's terminal — no extra wake is needed. A no-Run Leader turn owns no
2374
2395
  // AgentRun and is never surfaced by that push path; it is delivered only by
2375
2396
  // being woken to read its own Context. The interrupt itself (and, when the
2376
- // handoff reuses a Leader self-steer, wakePolicy "none") enqueues no wake, so
2397
+ // handoff reuses a Leader self-steer with record intent) enqueues no wake, so
2377
2398
  // without this the claim would be structurally unreleasable: listPendingWakeups
2378
2399
  // never selects the Task and the busy-gated leader mailbox is never consulted.
2379
2400
  // Enqueue that wake now, keyed to the claimed Message so an idempotent repeat of
@@ -2387,6 +2408,38 @@ function registerInterruptThen(store, taskId, roleName, thenMessageRef, target,
2387
2408
  }
2388
2409
  function taskRoleSessionCommand(args, store, options) {
2389
2410
  const [command, ...rest] = args;
2411
+ if (command === "retry") {
2412
+ const [taskId, roleName, action = "show", ...extra] = rest;
2413
+ if (extra.length > 0 || !["show", "cancel", "disable", "enable"].includes(action)) {
2414
+ throw usageError("Usage: yui task role session retry <task> <role> [show|cancel|disable|enable]");
2415
+ }
2416
+ return store.transaction(tx => {
2417
+ const task = requireTask(tx, taskId);
2418
+ const role = requireRole(tx, task.id, roleName);
2419
+ if (action !== "show")
2420
+ taskActor(tx, options, task.id);
2421
+ else {
2422
+ const reader = resolveManagedTaskReader(tx, options.environment);
2423
+ if (reader === undefined)
2424
+ taskActor(tx, options, task.id);
2425
+ else if (reader.taskId !== task.id || reader.roleName !== "leader" && reader.roleName !== role.name) {
2426
+ throw usageError("Provider retry read is outside the caller's Task/Role.");
2427
+ }
2428
+ }
2429
+ const sessions = tx.getTaskRoleSessionSet(task.id, role.name);
2430
+ let binding = sessions?.providerBinding ?? null;
2431
+ if (action !== "show") {
2432
+ if (task.status === "archived" || sessions === null || binding === null)
2433
+ throw usageError("No writable Provider Session.");
2434
+ binding = controlProviderRetry(binding, action, clock(options).getTime());
2435
+ tx.saveTaskRoleSessionSet({ ...sessions, providerBinding: binding });
2436
+ recordTaskEvent(tx, task.id, "provider.retry-control", { roleName: role.name, action }, clock(options));
2437
+ }
2438
+ return output(`${renderProviderRetry(binding)}\n`, {
2439
+ roleName: role.name, retry: providerRetryProjection(binding), disabled: binding?.retryDisabled ?? false
2440
+ });
2441
+ });
2442
+ }
2390
2443
  if (command === "inspect") {
2391
2444
  exactPositionals(rest, 2, "Task Role Session inspect usage: yui task role session inspect <task> <role>.");
2392
2445
  const task = requireTask(store, rest[0]);
@@ -2420,6 +2473,7 @@ function taskRoleSessionCommand(args, store, options) {
2420
2473
  `Native id: ${active.nativeSessionId}`,
2421
2474
  `Session: ${active.status}${active.endReason === undefined ? "" : `/${active.endReason}`}`,
2422
2475
  `AgentRun: ${binding?.run?.status ?? "none"}`,
2476
+ renderProviderRetry(binding),
2423
2477
  ...(host === undefined ? [] : [`Host reporting: ${taskRoleHostDiagnostic(host)}`]),
2424
2478
  `Run configuration: ${agentRunConfigurationLabel(runConfiguration)}`
2425
2479
  ].join("\n") + "\n"
@@ -2429,6 +2483,7 @@ function taskRoleSessionCommand(args, store, options) {
2429
2483
  role,
2430
2484
  session: active,
2431
2485
  providerBinding: binding,
2486
+ retry: providerRetryProjection(binding),
2432
2487
  ...(host === undefined ? {} : { host }),
2433
2488
  ...(runConfiguration === undefined ? {} : { runConfiguration })
2434
2489
  });
@@ -2460,7 +2515,7 @@ function taskRoleSessionCommand(args, store, options) {
2460
2515
  enqueueWork(tx, target, RUNTIME_SESSION_REPLACE_REQUIRED_REASON, now, [{ type: "task", id: task.id }]);
2461
2516
  return { taskId: task.id, roleName: role.name, target, alreadyRequested: false };
2462
2517
  });
2463
- notifyMailbox(options.runtime, result.target, result.taskId);
2518
+ notifyMailbox(options.runtime, result.target);
2464
2519
  return output(`Requested Session replacement for ${result.taskId}/${result.roleName}. `
2465
2520
  + "Yui will stop the old execution, retain its history and workspaces, and select a fresh Session. "
2466
2521
  + "If replacing your own Session, end this turn; the successor reads durable Task context.\n", result);
@@ -2579,7 +2634,7 @@ function addTaskRole(args, store, options) {
2579
2634
  }, now);
2580
2635
  return { role: created, binding };
2581
2636
  });
2582
- notifyMailbox(options.runtime, taskMailbox(result.role.taskId), result.role.taskId);
2637
+ notifyMailbox(options.runtime, taskMailbox(result.role.taskId));
2583
2638
  const profileId = parsed.one("--profile");
2584
2639
  const runtimeSource = profileId !== undefined
2585
2640
  ? `Agent Profile ${profileId}`
@@ -2719,7 +2774,7 @@ function updateTaskRole(args, store, options) {
2719
2774
  });
2720
2775
  return next;
2721
2776
  });
2722
- notifyMailbox(options.runtime, taskMailbox(updated.taskId), updated.taskId);
2777
+ notifyMailbox(options.runtime, taskMailbox(updated.taskId));
2723
2778
  const sessions = store.getTaskRoleSessionSet(updated.taskId, updated.name);
2724
2779
  return output(renderRoleDetails(`Updated Task Role: ${updated.name}`, updated, {
2725
2780
  kind: "task",
@@ -2761,7 +2816,7 @@ function removeTaskRole(args, store, options) {
2761
2816
  enqueueWork(tx, taskMailbox(task.id), "role-removed", now, [taskRef(task.id)]);
2762
2817
  return role;
2763
2818
  });
2764
- notifyMailbox(options.runtime, taskMailbox(removed.taskId), removed.taskId);
2819
+ notifyMailbox(options.runtime, taskMailbox(removed.taskId));
2765
2820
  return `Removed role ${removed.name} from ${removed.taskId}\n`;
2766
2821
  }
2767
2822
  function bindTaskRole(args, store, options) {
@@ -2820,7 +2875,7 @@ function bindTaskRole(args, store, options) {
2820
2875
  }, now);
2821
2876
  return { role: switched.role, mode: switched.mode };
2822
2877
  });
2823
- notifyMailbox(options.runtime, taskMailbox(result.role.taskId), result.role.taskId);
2878
+ notifyMailbox(options.runtime, taskMailbox(result.role.taskId));
2824
2879
  return `Bound ${result.role.taskId}/${result.role.name} to ${result.role.activeAgentId} (${result.mode})\n`;
2825
2880
  }
2826
2881
  function unbindTaskRole(args, store, options) {
@@ -3090,7 +3145,7 @@ function editWork(args, store, options) {
3090
3145
  }
3091
3146
  return { task, item: updated };
3092
3147
  });
3093
- options.runtime?.notifyStateChanged(result.task.id);
3148
+ options.runtime?.notifyMailboxChanged({ kind: "task", taskId: result.task.id });
3094
3149
  return output(`Edited Work Item ${result.task.id}/${result.item.id}\n`, {
3095
3150
  workItem: result.item
3096
3151
  });
@@ -3130,15 +3185,6 @@ function createWork(args, store, options) {
3130
3185
  if (new Set(baseRefs.map(({ projectId }) => projectId)).size !== baseRefs.length) {
3131
3186
  throw usageError("Each Work Item Project may specify at most one base ref.");
3132
3187
  }
3133
- const guard = runDeliveryGuardPreflight(tx, task.id, {
3134
- kind: "create-work-item",
3135
- scope: {
3136
- title: parsed.positionals[1],
3137
- objective: parsed.objective ?? parsed.positionals[1],
3138
- acceptance: parsed.acceptance,
3139
- writeProjectIds
3140
- }
3141
- }, { environment: options.environment, budget: true });
3142
3188
  const created = createWorkItem(tx.nextWorkItemId(task.id), task.id, {
3143
3189
  title: parsed.positionals[1],
3144
3190
  objective: parsed.objective ?? parsed.positionals[1],
@@ -3150,10 +3196,10 @@ function createWork(args, store, options) {
3150
3196
  }, now);
3151
3197
  tx.saveWorkItem(task.id, created);
3152
3198
  enqueueWork(tx, taskMailbox(task.id), "work-created", now, [workItemRef(task.id, created.id)]);
3153
- return { item: created, guard };
3199
+ return created;
3154
3200
  });
3155
- notifyMailbox(options.runtime, taskMailbox(item.item.taskId), item.item.taskId);
3156
- return output(withGuardWarnings(item.guard, `Created work item ${item.item.id} for ${item.item.taskId}\n`), { workItem: item.item });
3201
+ notifyMailbox(options.runtime, taskMailbox(item.taskId));
3202
+ return output(`Created work item ${item.id} for ${item.taskId}\n`, { workItem: item });
3157
3203
  }
3158
3204
  function updateWorkScope(args, store, options) {
3159
3205
  const usage = "Task work scope usage: yui task work scope <task>/<work> [--project <project> ...].";
@@ -3190,7 +3236,7 @@ function updateWorkScope(args, store, options) {
3190
3236
  return { item: next, changed: true };
3191
3237
  });
3192
3238
  if (updated.changed) {
3193
- notifyMailbox(options.runtime, taskMailbox(updated.item.taskId), updated.item.taskId);
3239
+ notifyMailbox(options.runtime, taskMailbox(updated.item.taskId));
3194
3240
  }
3195
3241
  return `${updated.changed ? "Updated" : "Unchanged"} Work Item Project scope ${updated.item.id}: ${updated.item.writeProjectIds.join(", ") || "read-only"}\n`;
3196
3242
  }
@@ -3357,7 +3403,7 @@ function updateWork(args, store, options) {
3357
3403
  reviewTrigger: candidatePolicy?.trigger ?? null
3358
3404
  };
3359
3405
  });
3360
- notifyMailbox(options.runtime, taskMailbox(result.item.taskId), result.item.taskId);
3406
+ notifyMailbox(options.runtime, taskMailbox(result.item.taskId));
3361
3407
  if ((result.item.status === "open" && result.item.candidates.length > 0) && status === "completed") {
3362
3408
  const failure = result.reviewDispatch?.round.status === "failed"
3363
3409
  ? `Review could not start: ${result.reviewDispatch.round.failure?.message ?? result.reviewDispatch.round.id}\n`
@@ -3588,7 +3634,7 @@ function dispatchWork(args, store, options) {
3588
3634
  return { kind: "replicated", runs };
3589
3635
  });
3590
3636
  for (const run of dispatch.runs) {
3591
- notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName), run.taskId);
3637
+ notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName));
3592
3638
  }
3593
3639
  return dispatch.kind === "direct"
3594
3640
  ? `Direct WorkItem AgentRun queued as ${dispatch.runs[0].id}\n`
@@ -3832,7 +3878,7 @@ function retireWork(args, store, options) {
3832
3878
  }
3833
3879
  return next;
3834
3880
  });
3835
- options.runtime?.notifyStateChanged(retired.taskId);
3881
+ options.runtime?.notifyMailboxChanged({ kind: "task", taskId: retired.taskId });
3836
3882
  return output(`Retired Work Item ${retired.id}\n`, { workItem: retired });
3837
3883
  }
3838
3884
  function listWork(args, store) {
@@ -4038,7 +4084,7 @@ function synthesizeRuns(args, kind, store, options) {
4038
4084
  }, now);
4039
4085
  return created;
4040
4086
  });
4041
- notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName), run.taskId);
4087
+ notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName));
4042
4088
  return output(`Dispatched synthesis AgentRun ${run.taskId}/${run.id}\n`, { run });
4043
4089
  }
4044
4090
  function taskReviewCommand(args, store, options) {
@@ -4088,7 +4134,7 @@ function requestTaskReviewRound(args, store, options) {
4088
4134
  throw usageError(producerCollision);
4089
4135
  }
4090
4136
  const taskRounds = reviewRoundsByIdentity(tx.listReviewRounds(task.id))
4091
- .filter((entry) => (entry.scope ?? "work-item") === "task");
4137
+ .filter((entry) => entry.scope === "task");
4092
4138
  const exact = taskRounds.filter((entry) => (entry.reviewerRoleName === reviewerRoleName
4093
4139
  && (deltaRecheckRequested
4094
4140
  ? entry.deltaRecheck !== undefined
@@ -4282,7 +4328,7 @@ function retryFailedTaskReviewRound(args, store, options) {
4282
4328
  if (task.status !== "active")
4283
4329
  throw usageError(`Task is not active: ${task.id}.`);
4284
4330
  const requestedBy = taskActor(tx, options, task.id);
4285
- if ((round.scope ?? "work-item") !== "task") {
4331
+ if (round.scope !== "task") {
4286
4332
  throw usageError(`ReviewRound ${round.id} is not a failed Task-final ReviewRound.`);
4287
4333
  }
4288
4334
  if (round.reviewerRunId !== undefined) {
@@ -4369,11 +4415,11 @@ function taskRunCommand(args, store, options) {
4369
4415
  if (command === "retire")
4370
4416
  return retireRun(rest, store, options);
4371
4417
  throw usageError(command === undefined
4372
- ? "Task turn command is required."
4418
+ ? "Task run command is required."
4373
4419
  : `Unknown command: task run ${command}`);
4374
4420
  }
4375
4421
  function settleRun(args, store, options) {
4376
- exactPositionals(args, 1, "Task turn settle usage: yui task run settle <task>/<run>.");
4422
+ exactPositionals(args, 1, "Task run settle usage: yui task run settle <task>/<run>.");
4377
4423
  const previous = store.transaction((tx) => requireRun(tx, args[0], options));
4378
4424
  if (previous.purpose === "review") {
4379
4425
  if (previous.executionGroupId !== undefined
@@ -4454,7 +4500,7 @@ function settleFailedReviewExecutionLaneRun(previous, store, options) {
4454
4500
  };
4455
4501
  });
4456
4502
  for (const run of result.mainRuns) {
4457
- notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName), run.taskId);
4503
+ notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName));
4458
4504
  }
4459
4505
  return output(result.changed
4460
4506
  ? `Settled failed Review Producer Lane from AgentRun ${result.run.id}\n`
@@ -4529,7 +4575,7 @@ function settleFailedExecutionLaneRun(previous, store, options) {
4529
4575
  };
4530
4576
  });
4531
4577
  for (const run of result.mainRuns) {
4532
- notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName), run.taskId);
4578
+ notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName));
4533
4579
  }
4534
4580
  return output(result.changed
4535
4581
  ? `Settled failed Execution Lane from AgentRun ${result.run.id}\n`
@@ -4540,7 +4586,7 @@ function settleFailedExecutionLaneRun(previous, store, options) {
4540
4586
  });
4541
4587
  }
4542
4588
  function retireRun(args, store, options) {
4543
- const usage = "Task turn retire usage: yui task run retire <task>/<run> --reason <text> [--expected-progress-at <timestamp>] [--agent-id <id>] [--adapter-id <id>] [--native-session-id <id>].";
4589
+ const usage = "Task run retire usage: yui task run retire <task>/<run> --reason <text> [--expected-progress-at <timestamp>] [--agent-id <id>] [--adapter-id <id>] [--native-session-id <id>].";
4544
4590
  const parsed = parseTail(args, new Set([
4545
4591
  "--reason",
4546
4592
  "--expected-progress-at",
@@ -4624,7 +4670,7 @@ function retireRun(args, store, options) {
4624
4670
  return { task, run: run, changed: true };
4625
4671
  });
4626
4672
  if (result.changed)
4627
- options.runtime?.notifyStateChanged(result.task.id);
4673
+ options.runtime?.notifyMailboxChanged({ kind: "task", taskId: result.task.id });
4628
4674
  return output(`Retired AgentRun ${result.task.id}/${result.run.id}\n`, {
4629
4675
  run: result.run,
4630
4676
  retired: true
@@ -4633,7 +4679,7 @@ function retireRun(args, store, options) {
4633
4679
  function runContextCommand(args, store, options) {
4634
4680
  const [first, ...rest] = args;
4635
4681
  if (first === "expand") {
4636
- const usage = "Task turn context expand usage: yui task run context expand <task>/<run> <ref-id> [--store <store>] [--mode full].";
4682
+ const usage = "Task run context expand usage: yui task run context expand <task>/<run> <ref-id> [--store <store>] [--mode full].";
4637
4683
  const parsed = parseTail(rest, new Set(["--store", "--mode"]), usage);
4638
4684
  exactPositionals(parsed.positionals, 2, usage);
4639
4685
  const mode = parsed.options.get("--mode");
@@ -4647,7 +4693,7 @@ function runContextCommand(args, store, options) {
4647
4693
  }
4648
4694
  if (first === "delta") {
4649
4695
  if (rest.length !== 3 || rest[1] !== "--after") {
4650
- throw usageError("Task turn context delta usage: yui task run context delta <task>/<run> --after <cursor>.");
4696
+ throw usageError("Task run context delta usage: yui task run context delta <task>/<run> --after <cursor>.");
4651
4697
  }
4652
4698
  const { taskId, runId } = parseRunContextReference(rest[0]);
4653
4699
  authorizeRunContext(store, taskId, runId, options.environment);
@@ -4655,7 +4701,7 @@ function runContextCommand(args, store, options) {
4655
4701
  return output(`${JSON.stringify(delta, null, 2)}\n`, { contextDelta: delta });
4656
4702
  }
4657
4703
  if (first === undefined || rest.length !== 0) {
4658
- throw usageError("Task turn context usage: yui task run context <task>/<run>.");
4704
+ throw usageError("Task run context usage: yui task run context <task>/<run>.");
4659
4705
  }
4660
4706
  const { taskId, runId } = parseRunContextReference(first);
4661
4707
  authorizeRunContext(store, taskId, runId, options.environment);
@@ -4696,7 +4742,7 @@ function authorizeRunContext(store, taskId, runId, environment) {
4696
4742
  }
4697
4743
  }
4698
4744
  function listRuns(args, store, options) {
4699
- const usage = "Task turn list usage: yui task run list <task|task/work>.";
4745
+ const usage = "Task run list usage: yui task run list <task|task/work>.";
4700
4746
  exactPositionals(args, 1, usage);
4701
4747
  const reference = args[0];
4702
4748
  const task = store.getTask(reference);
@@ -4741,7 +4787,7 @@ function listRuns(args, store, options) {
4741
4787
  * creates one fresh Round over the newer frozen Task heads.
4742
4788
  */
4743
4789
  function settleStaleFinalReviewRun(args, store, options) {
4744
- exactPositionals(args, 1, "Task turn settle usage: yui task run settle <task>/<run>.");
4790
+ exactPositionals(args, 1, "Task run settle usage: yui task run settle <task>/<run>.");
4745
4791
  const now = clock(options);
4746
4792
  const previous = store.transaction((tx) => requireRun(tx, args[0], options));
4747
4793
  const result = store.transaction((tx) => {
@@ -4760,7 +4806,7 @@ function settleStaleFinalReviewRun(args, store, options) {
4760
4806
  if (round === null) {
4761
4807
  throw dataError(`ReviewRound not found for AgentRun ${run.id}: ${run.reviewRoundId}.`);
4762
4808
  }
4763
- if ((round.scope ?? "work-item") !== "task") {
4809
+ if (round.scope !== "task") {
4764
4810
  throw usageError(`Review AgentRun ${run.id} is not a Task-final review; request a new WorkItem review `
4765
4811
  + "for a new Candidate.");
4766
4812
  }
@@ -4780,7 +4826,7 @@ function settleStaleFinalReviewRun(args, store, options) {
4780
4826
  throw usageError(`${task.id}/${round.reviewerRoleName} already has active AgentRun ${activeRoleRun.id}.`);
4781
4827
  }
4782
4828
  const taskRounds = reviewRoundsByIdentity(tx.listReviewRounds(task.id)
4783
- .filter((entry) => (entry.scope ?? "work-item") === "task"));
4829
+ .filter((entry) => entry.scope === "task"));
4784
4830
  const roundIndex = taskRounds.findIndex(({ id }) => id === round.id);
4785
4831
  if (roundIndex < 0) {
4786
4832
  throw dataError(`Final ReviewRound is not in Task history: ${round.id}.`);
@@ -4821,7 +4867,46 @@ function settleStaleFinalReviewRun(args, store, options) {
4821
4867
  : `Obsolete final Review already settled: ${result.round.id}/${result.run.id}\n`, { reviewRound: result.round, reviewRun: result.run });
4822
4868
  }
4823
4869
  function retryRun(args, store, options) {
4824
- exactPositionals(args, 1, "Task turn retry usage: yui task run retry <task>/<run>.");
4870
+ return store.transaction(tx => {
4871
+ const previous = requireRun(tx, args[0], options);
4872
+ const existing = tx.listEvents(previous.taskId).find(event => event.type === "run.retried"
4873
+ && event.payload.previousRunId === previous.id);
4874
+ if (existing !== undefined) {
4875
+ return output(`Retry already recorded as ${existing.payload.runId}; retry that exact successor if needed.\n`);
4876
+ }
4877
+ const sessions = tx.getTaskRoleSessionSet(previous.taskId, previous.roleName);
4878
+ const retry = sessions?.providerBinding?.retry;
4879
+ if (options.providerRetryChainId !== undefined
4880
+ && (retry?.chainId !== options.providerRetryChainId || retry.status !== "waiting"
4881
+ || retry.previousRunId !== previous.id || clock(options).getTime() < Date.parse(retry.nextEligibleAt)
4882
+ || clock(options).getTime() >= Date.parse(retry.deadline))) {
4883
+ throw usageError("Provider recovery intent is no longer eligible.");
4884
+ }
4885
+ const result = retryRunOperation(args, tx, options);
4886
+ if (retry !== undefined && retry.status !== "recovered" && retry.previousRunId === previous.id && sessions?.providerBinding != null) {
4887
+ const successor = tx.getActiveRun(previous.taskId, previous.roleName);
4888
+ tx.saveTaskRoleSessionSet({
4889
+ ...sessions, providerBinding: {
4890
+ ...sessions.providerBinding, retry: {
4891
+ ...retry, status: "waiting", successorAutomatic: options.providerRetryChainId !== undefined,
4892
+ ...(options.providerRetryChainId === undefined ? {
4893
+ intentAt: clock(options).toISOString(),
4894
+ // An explicit manual attempt does not inherit automatic jitter,
4895
+ // but cannot shorten a trusted server minimum.
4896
+ nextEligibleAt: new Date(Math.max(clock(options).getTime(), retry.error.retryAfterMs === undefined ? 0
4897
+ : Date.parse(sessions.providerBinding.run.updatedAt) + retry.error.retryAfterMs + 1)).toISOString()
4898
+ } : {}),
4899
+ ...(successor === null ? {} : { successorRunId: successor.id }),
4900
+ ...(previous.reviewRoundId === undefined ? {} : { successorReviewRoundId: previous.reviewRoundId })
4901
+ }
4902
+ }
4903
+ });
4904
+ }
4905
+ return result;
4906
+ });
4907
+ }
4908
+ function retryRunOperation(args, store, options) {
4909
+ exactPositionals(args, 1, "Task run retry usage: yui task run retry <task>/<run>.");
4825
4910
  const now = clock(options);
4826
4911
  const previous = store.transaction((tx) => requireRun(tx, args[0], options));
4827
4912
  if (previous.purpose === "review") {
@@ -5085,10 +5170,10 @@ function retryRun(args, store, options) {
5085
5170
  }, now);
5086
5171
  return { kind: "run", run: created };
5087
5172
  });
5088
- notifyMailbox(options.runtime, roleMailbox(retried.run.taskId, retried.run.roleName), retried.run.taskId);
5173
+ notifyMailbox(options.runtime, roleMailbox(retried.run.taskId, retried.run.roleName));
5089
5174
  return output(`Retry queued as ${retried.run.id} for ${retried.run.taskId}/${retried.run.roleName}\n`);
5090
5175
  }
5091
- function actualTaskReviewCandidateForMutation(store, task, options) {
5176
+ function actualTaskReviewCandidateForMutation(_store, task, options) {
5092
5177
  if (options.actualTaskReviewCandidate === undefined) {
5093
5178
  throw usageError(`Actual Task Project heads were not verified for delivery: ${task.id}.`);
5094
5179
  }
@@ -5224,7 +5309,7 @@ function isSameTaskReviewCandidate(left, right) {
5224
5309
  function assertNoConflictingTaskReviewRound(rounds, reusableRoundIds = [], reviewerRoleName) {
5225
5310
  const reusable = new Set(typeof reusableRoundIds === "string" ? [reusableRoundIds] : reusableRoundIds);
5226
5311
  const conflicting = rounds.find((entry) => (!reusable.has(entry.id)
5227
- && (entry.scope ?? "work-item") === "task"
5312
+ && entry.scope === "task"
5228
5313
  && (entry.status === "pending" || entry.status === "running")
5229
5314
  && (reviewerRoleName === undefined || entry.reviewerRoleName === reviewerRoleName)));
5230
5315
  if (conflicting !== undefined) {
@@ -5279,7 +5364,7 @@ function prepareFinalTaskReview(store, task, now, taskFinalContract, options) {
5279
5364
  if (taskFinalContract === undefined || task.projectBindings.length === 0)
5280
5365
  return null;
5281
5366
  const taskRounds = reviewRoundsByIdentity(store.listReviewRounds(task.id))
5282
- .filter((round) => ((round.scope ?? "work-item") === "task"
5367
+ .filter((round) => (round.scope === "task"
5283
5368
  && sameTaskFinalReviewContract(round.taskFinalReviewContract, taskFinalContract)));
5284
5369
  const establishedRound = taskRounds.at(-1);
5285
5370
  const config = taskFinalReviewConfig(taskFinalContract);
@@ -5306,7 +5391,7 @@ function prepareFinalTaskReview(store, task, now, taskFinalContract, options) {
5306
5391
  }
5307
5392
  return queueTaskReviewRound(store, task, config, taskCandidate, options, now, establishedRound?.requestedBy ?? "policy", taskFinalContract);
5308
5393
  }
5309
- function resumablePendingFinalTaskReview(store, task, round, config, taskCandidate, taskFinalContract, options) {
5394
+ function resumablePendingFinalTaskReview(store, task, round, config, _taskCandidate, taskFinalContract, _options) {
5310
5395
  if (taskFinalContract === undefined || round.taskFinalReviewContract === undefined) {
5311
5396
  throw usageError(`Final Task Review is still active: ${round.id}/${round.status}.`);
5312
5397
  }
@@ -5380,12 +5465,12 @@ function retryFailedReviewRun(previous, store, options, now) {
5380
5465
  const task = requireTask(tx, run.taskId);
5381
5466
  if (task.status !== "active")
5382
5467
  throw usageError(`Task is not active: ${task.id}.`);
5383
- const requestedBy = taskActor(tx, options, task.id);
5468
+ const requestedBy = options.providerRetryChainId === undefined ? taskActor(tx, options, task.id) : "policy";
5384
5469
  const round = tx.getReviewRound(task.id, run.reviewRoundId);
5385
5470
  if (round === null) {
5386
5471
  throw dataError(`ReviewRound not found for run ${run.id}: ${run.reviewRoundId}.`);
5387
5472
  }
5388
- const taskScope = (round.scope ?? "work-item") === "task";
5473
+ const taskScope = round.scope === "task";
5389
5474
  const retryLane = run.executionLaneId === undefined
5390
5475
  ? undefined
5391
5476
  : round.executionGroup?.lanes.find(({ id }) => id === run.executionLaneId);
@@ -5420,7 +5505,7 @@ function retryFailedReviewRun(previous, store, options, now) {
5420
5505
  throw usageError(`Task-final ReviewRound ${round.id} no longer matches the frozen Task heads.`);
5421
5506
  }
5422
5507
  const taskRounds = reviewRoundsByIdentity(allRounds
5423
- .filter((entry) => (entry.scope ?? "work-item") === "task"));
5508
+ .filter((entry) => entry.scope === "task"));
5424
5509
  const roundIndex = taskRounds.findIndex(({ id }) => id === round.id);
5425
5510
  if (roundIndex < 0) {
5426
5511
  throw dataError(`Final ReviewRound is not in Task history: ${round.id}.`);
@@ -5588,7 +5673,7 @@ function retryFailedReviewRun(previous, store, options, now) {
5588
5673
  return { round: resetRound, previousRun: run, created: true };
5589
5674
  });
5590
5675
  if ("run" in result && result.run !== undefined) {
5591
- notifyMailbox(options.runtime, roleMailbox(result.run.taskId, result.run.roleName), result.run.taskId);
5676
+ notifyMailbox(options.runtime, roleMailbox(result.run.taskId, result.run.roleName));
5592
5677
  }
5593
5678
  return output(result.created
5594
5679
  ? `Review retry requested as ${result.round.id}\n`
@@ -5596,7 +5681,7 @@ function retryFailedReviewRun(previous, store, options, now) {
5596
5681
  }
5597
5682
  /** AgentRun details are retained audit evidence; continuation uses durable Task state. */
5598
5683
  function showRun(args, store, options) {
5599
- const usage = "Task turn show usage: yui task run show <task>/<run> [--json].";
5684
+ const usage = "Task run show usage: yui task run show <task>/<run> [--json].";
5600
5685
  const asJson = args.includes("--json");
5601
5686
  const positionals = args.filter((arg) => arg !== "--json");
5602
5687
  exactPositionals(positionals, 1, usage);
@@ -5648,7 +5733,7 @@ function renderRunShow(run, retirement) {
5648
5733
  * Leader.
5649
5734
  */
5650
5735
  function checkpointRun(args, store, options) {
5651
- const usage = "Task turn checkpoint usage: yui task run checkpoint <run> (--note <text>|--note-file <path|->).";
5736
+ const usage = "Task run checkpoint usage: yui task run checkpoint <run> (--note <text>|--note-file <path|->).";
5652
5737
  const parsed = parseTail(args, new Set(["--note", "--note-file"]), usage);
5653
5738
  exactPositionals(parsed.positionals, 1, usage);
5654
5739
  const note = readCommandText(parsed.options.get("--note"), parsed.options.get("--note-file"), "--note", usage);
@@ -5814,12 +5899,12 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
5814
5899
  throw usageError(`ReviewRound is not dispatchable: ${round.id}/${round.status}.`);
5815
5900
  }
5816
5901
  if (round.workspace === undefined) {
5817
- if ((round.scope ?? "work-item") === "task") {
5902
+ if (round.scope === "task") {
5818
5903
  throw new TaskFinalReviewDispatchDriftError(`ReviewRound workspace is not ready: ${round.id}.`);
5819
5904
  }
5820
5905
  throw usageError(`ReviewRound workspace is not ready: ${round.id}.`);
5821
5906
  }
5822
- const taskScope = (round.scope ?? "work-item") === "task";
5907
+ const taskScope = round.scope === "task";
5823
5908
  let item;
5824
5909
  let candidate;
5825
5910
  if (taskScope) {
@@ -5852,7 +5937,7 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
5852
5937
  if (task.status !== "active")
5853
5938
  throw usageError(`Task is not active: ${task.id}.`);
5854
5939
  assertTaskExecutionEnabled(task, "dispatching a Review");
5855
- if ((round.scope ?? "work-item") === "task") {
5940
+ if (round.scope === "task") {
5856
5941
  let taskFinalContract;
5857
5942
  try {
5858
5943
  taskFinalContract = taskFinalReviewContractForMutation(tx, task.id, options);
@@ -5893,7 +5978,7 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
5893
5978
  }
5894
5979
  const reviewer = tx.getRole(taskId, round.reviewerRoleName);
5895
5980
  if (reviewer === null) {
5896
- if ((round.scope ?? "work-item") === "task") {
5981
+ if (round.scope === "task") {
5897
5982
  throw new TaskFinalReviewDispatchDriftError(`Reviewer Role not found: ${taskId}/${round.reviewerRoleName}.`);
5898
5983
  }
5899
5984
  throw roleNotFound(round.reviewerRoleName);
@@ -5904,7 +5989,7 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
5904
5989
  || storedWorkspace.owner.type !== "review-round"
5905
5990
  || storedWorkspace.owner.reviewRoundId !== round.id) {
5906
5991
  const message = `ReviewRound workspace ownership changed: ${round.id}.`;
5907
- if ((round.scope ?? "work-item") === "task") {
5992
+ if (round.scope === "task") {
5908
5993
  throw new TaskFinalReviewDispatchDriftError(message);
5909
5994
  }
5910
5995
  throw usageError(message);
@@ -5915,7 +6000,7 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
5915
6000
  ...(round.executionGroup?.lanes.map(({ roleName }) => roleName) ?? [])
5916
6001
  ]);
5917
6002
  const conflicting = tx.listReviewRounds(task.id).find((entry) => (entry.id !== round.id
5918
- && (entry.scope ?? "work-item") === "task"
6003
+ && entry.scope === "task"
5919
6004
  && (entry.status === "pending" || entry.status === "running")
5920
6005
  && (requestedReviewers.has(entry.reviewerRoleName)
5921
6006
  || entry.executionGroup?.lanes.some(({ roleName }) => (requestedReviewers.has(roleName))) === true)
@@ -6167,7 +6252,7 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
6167
6252
  return createdRuns;
6168
6253
  });
6169
6254
  for (const run of runs) {
6170
- notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName), run.taskId);
6255
+ notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName));
6171
6256
  }
6172
6257
  return runs[0] ?? null;
6173
6258
  }
@@ -6194,7 +6279,7 @@ export function failPendingReviewRound(taskId, reviewRoundId, summary, store, op
6194
6279
  ]);
6195
6280
  return terminal;
6196
6281
  });
6197
- notifyMailbox(options.runtime, leaderMailbox(taskId), taskId);
6282
+ notifyMailbox(options.runtime, leaderMailbox(taskId));
6198
6283
  return failed;
6199
6284
  }
6200
6285
  export function preserveReviewRoundWorkspace(taskId, reviewRoundId, store, options = {}) {
@@ -6378,20 +6463,6 @@ function appendMessage(store, taskId, body, kind, author, now, context = {}) {
6378
6463
  }, now);
6379
6464
  return message;
6380
6465
  }
6381
- function recordTaskEvent(store, taskId, type, payload, now) {
6382
- return recordTaskEventRecord(store, taskId, type, payload, now);
6383
- }
6384
- function leaderActionEventPayload(store, taskId, options) {
6385
- const caller = currentManagedRuntime(store, options.environment, taskId, "leader");
6386
- // A long-lived Session may be handling direct input while another request
6387
- // awaits admission. The Role's active pointer cannot prove command origin.
6388
- return caller === undefined ? {} : { leaderNativeSessionId: caller.nativeSessionId };
6389
- }
6390
- function recordTaskEventRecord(store, taskId, type, payload, now) {
6391
- const event = createTaskEvent(store.nextEventId(taskId), taskId, type, payload, now);
6392
- store.saveEvent(taskId, event);
6393
- return event;
6394
- }
6395
6466
  /** Keeps a free-text AgentRun-fact note bounded so an event payload stays compact. */
6396
6467
  function truncateEventNote(note) {
6397
6468
  const normalized = note.trim();
@@ -6426,13 +6497,6 @@ function runLaunchEventPayload(run) {
6426
6497
  })
6427
6498
  };
6428
6499
  }
6429
- function requireTask(store, taskId) {
6430
- const id = requiredText(taskId, "Task id");
6431
- const task = store.getTask(id);
6432
- if (task === null)
6433
- throw taskNotFound(id);
6434
- return task;
6435
- }
6436
6500
  function requireRole(store, taskId, roleName) {
6437
6501
  const name = requiredText(roleName, "Role name");
6438
6502
  const role = store.getRole(taskId, name);
@@ -6474,14 +6538,6 @@ function activeRunPointer(store, run) {
6474
6538
  ? store.getActiveExecutionLaneRun(run.taskId, run.executionGroupId, run.executionLaneId)
6475
6539
  : store.getActiveRun(run.taskId, run.roleName);
6476
6540
  }
6477
- function requireReviewRound(store, reviewRoundId, options) {
6478
- const reference = taskRecordReference(reviewRoundId, "reviewRound", "ReviewRound reference", options);
6479
- const round = store.getReviewRound(reference.taskId, reference.localId);
6480
- if (round === null) {
6481
- throw usageError(`ReviewRound not found: ${reference.taskId}/${reference.localId}.`);
6482
- }
6483
- return round;
6484
- }
6485
6541
  function taskRecordReference(value, kind, label, options) {
6486
6542
  try {
6487
6543
  return resolveTaskRecordReference(requiredText(value, label), {
@@ -6514,23 +6570,11 @@ function chronologicalRuns(runs) {
6514
6570
  function isTerminalWorkItemStatus(status) {
6515
6571
  return ["accepted", "retired"].includes(status);
6516
6572
  }
6517
- function assertTaskOpen(task) {
6518
- if (task.status === "completed") {
6519
- throw usageError(`Task ${task.id} is completed; reopen it before continuing.`);
6520
- }
6521
- if (task.status === "archived")
6522
- throw usageError(`Task is archived: ${task.id}.`);
6523
- if (task.status === "cancelled")
6524
- throw usageError(`Task is retired: ${task.id}.`);
6525
- }
6526
6573
  function assertTaskExecutionEnabled(task, action) {
6527
6574
  if (task.executionGate.state === "enabled")
6528
6575
  return;
6529
6576
  throw usageError(`Task execution is stopped: ${task.id}. Run "yui task execution start ${task.id}" before ${action}.`);
6530
6577
  }
6531
- function taskActor(store, options, taskId) {
6532
- return resolveTaskLocalActor(store, options.environment, taskId);
6533
- }
6534
6578
  function inactiveTaskMessage(task, action) {
6535
6579
  if (task.status === "draft") {
6536
6580
  return `Task ${task.id} is a Draft; activate it before ${action}.`;
@@ -6672,317 +6716,12 @@ function parseIsoTimestamp(value, label) {
6672
6716
  }
6673
6717
  return timestamp.toISOString();
6674
6718
  }
6675
- function taskBriefCommand(args, store, options) {
6676
- const [command, ...rest] = args;
6677
- if (command === "show") {
6678
- exactPositionals(rest, 1, "Task brief show usage: yui task brief show <task>.");
6679
- const task = requireTask(store, rest[0]);
6680
- const brief = store.getTaskBrief(task.id);
6681
- if (brief === null) {
6682
- return output(`Task ${task.id} has no brief.\n`, { taskId: task.id, brief: null });
6683
- }
6684
- const timeZone = store.getConfig().timeZone;
6685
- return output([
6686
- `Task: ${task.id}`,
6687
- `Objective: ${brief.objective}`,
6688
- `Boundaries:`,
6689
- ...(brief.boundaries.length === 0 ? [" (none)"] : brief.boundaries.map((b) => ` - ${b}`)),
6690
- `Technical approach: ${brief.technicalApproach || "(not defined)"}`,
6691
- `Current focus: ${brief.currentFocus}`,
6692
- `Leader summary: ${brief.leaderSummary}`,
6693
- `Updated by: ${brief.updatedBy}`,
6694
- `Updated at: ${presentTime(brief.updatedAt, timeZone)}`
6695
- ].join("\n").concat("\n"), { taskId: task.id, brief });
6696
- }
6697
- if (command === "update") {
6698
- const usage = "Task brief update usage: yui task brief update <task> [--objective <text>] [--boundary <text> ...] [--approach <text>] [--focus <text>] [--leader-summary <text>].";
6699
- const parsed = parseMultiValueTail(rest, new Set(["--objective", "--approach", "--focus", "--leader-summary"]), new Set(["--boundary"]), usage);
6700
- exactPositionals(parsed.positionals, 1, usage);
6701
- const hasObjective = parsed.options.has("--objective");
6702
- const hasApproach = parsed.options.has("--approach");
6703
- const hasFocus = parsed.options.has("--focus");
6704
- const hasSummary = parsed.options.has("--leader-summary");
6705
- const boundaries = parsed.multiOptions.get("--boundary") ?? [];
6706
- if (!hasObjective && !hasApproach && !hasFocus && !hasSummary && boundaries.length === 0) {
6707
- throw usageError("At least one brief field is required.", usage);
6708
- }
6709
- const now = clock(options);
6710
- const result = store.transaction((tx) => {
6711
- const task = requireTask(tx, parsed.positionals[0]);
6712
- assertTaskOpen(task);
6713
- const existing = tx.getTaskBrief(task.id);
6714
- const updatedBy = taskActor(tx, options, task.id);
6715
- const brief = existing === null
6716
- ? createTaskBrief({
6717
- objective: requiredText(parsed.options.get("--objective"), "--objective"),
6718
- boundaries,
6719
- ...(hasApproach
6720
- ? { technicalApproach: requiredText(parsed.options.get("--approach"), "--approach") }
6721
- : {}),
6722
- currentFocus: requiredText(parsed.options.get("--focus"), "--focus"),
6723
- leaderSummary: requiredText(parsed.options.get("--leader-summary"), "--leader-summary"),
6724
- updatedBy
6725
- }, now)
6726
- : updateTaskBrief(existing, {
6727
- ...(hasObjective ? { objective: parsed.options.get("--objective") } : {}),
6728
- ...(boundaries.length > 0 ? { boundaries } : {}),
6729
- ...(hasApproach
6730
- ? { technicalApproach: parsed.options.get("--approach") }
6731
- : {}),
6732
- ...(hasFocus ? { currentFocus: parsed.options.get("--focus") } : {}),
6733
- ...(hasSummary ? { leaderSummary: parsed.options.get("--leader-summary") } : {})
6734
- }, updatedBy, now);
6735
- tx.saveTaskBrief(task.id, brief);
6736
- recordTaskEvent(tx, task.id, "brief.updated", {
6737
- updatedBy,
6738
- previous: JSON.stringify(existing), current: JSON.stringify(brief)
6739
- }, now);
6740
- enqueueWork(tx, taskMailbox(task.id), "brief-updated", now, [taskRef(task.id)]);
6741
- if (task.status === "active" && updatedBy !== "leader") {
6742
- enqueueWork(tx, leaderMailbox(task.id), "brief-updated", now, [taskRef(task.id)]);
6743
- }
6744
- return { task, brief };
6745
- });
6746
- notifyMailbox(options.runtime, taskMailbox(result.task.id), result.task.id);
6747
- if (result.task.status === "active") {
6748
- notifyMailbox(options.runtime, leaderMailbox(result.task.id), result.task.id);
6749
- }
6750
- return output(`Updated brief for ${result.task.id}\n`, { taskId: result.task.id, brief: result.brief });
6751
- }
6752
- throw usageError(command === undefined
6753
- ? "Task brief command is required."
6754
- : `Unknown command: task brief ${command}`);
6755
- }
6756
6719
  /** Persist only edited fields, not another copy of aggregate execution history.
6757
6720
  * Null records an absent optional field so clearing it remains observable. */
6758
6721
  function editedFieldValues(record, fields) {
6759
6722
  const values = record;
6760
6723
  return JSON.stringify(Object.fromEntries(fields.map((field) => [field, values[field] ?? null])));
6761
6724
  }
6762
- function taskDecisionCommand(args, store, options) {
6763
- const [command, ...rest] = args;
6764
- if (command === "record") {
6765
- const usage = "Task decision record usage: yui task decision record <task> --title <text> --rationale <text>.";
6766
- const parsed = parseTail(rest, new Set(["--title", "--rationale"]), usage);
6767
- exactPositionals(parsed.positionals, 1, usage);
6768
- const title = requiredOption(parsed.options, "--title");
6769
- const rationale = requiredOption(parsed.options, "--rationale");
6770
- const now = clock(options);
6771
- const result = store.transaction((tx) => {
6772
- const task = requireTask(tx, parsed.positionals[0]);
6773
- assertTaskOpen(task);
6774
- const actor = taskActor(tx, options, task.id);
6775
- const decision = createDecision(tx.nextDecisionId(task.id), task.id, title, rationale, now);
6776
- tx.saveDecision(task.id, decision);
6777
- recordTaskEvent(tx, task.id, "decision.recorded", {
6778
- decisionId: decision.id,
6779
- title,
6780
- ...leaderActionEventPayload(tx, task.id, options)
6781
- }, now);
6782
- enqueueWork(tx, taskMailbox(task.id), "decision-recorded", now, [taskRef(task.id)]);
6783
- if (task.status === "active" && actor !== "leader") {
6784
- enqueueWork(tx, leaderMailbox(task.id), "decision-recorded", now, [taskRef(task.id)]);
6785
- }
6786
- return { task, decision };
6787
- });
6788
- notifyMailbox(options.runtime, taskMailbox(result.task.id), result.task.id);
6789
- if (result.task.status === "active")
6790
- notifyMailbox(options.runtime, leaderMailbox(result.task.id), result.task.id);
6791
- return output(`Recorded decision ${result.decision.id} for ${result.task.id}\n`);
6792
- }
6793
- if (command === "list") {
6794
- const usage = "Task decision list usage: yui task decision list <task> [--status active|superseded].";
6795
- const parsed = parseTail(rest, new Set(["--status"]), usage);
6796
- exactPositionals(parsed.positionals, 1, usage);
6797
- const task = requireTask(store, parsed.positionals[0]);
6798
- let decisions = store.listDecisions(task.id);
6799
- const status = parsed.options.get("--status");
6800
- if (status !== undefined) {
6801
- if (status !== "active" && status !== "superseded") {
6802
- throw usageError("--status must be active or superseded.", usage);
6803
- }
6804
- decisions = decisions.filter((d) => d.status === status);
6805
- }
6806
- if (decisions.length === 0) {
6807
- return output(`No decisions found for ${task.id}.\n`, { taskId: task.id, decisions: [] });
6808
- }
6809
- const timeZone = store.getConfig().timeZone;
6810
- return output(`${renderTable(`Decisions: ${task.id}`, [
6811
- { header: "Decision", minWidth: 8, maxWidth: 18 },
6812
- { header: "Status", minWidth: 6, maxWidth: 12 },
6813
- { header: "Title", minWidth: 8, maxWidth: 64 },
6814
- { header: "Created", minWidth: 10, maxWidth: 28 }
6815
- ], decisions.map((d) => [d.id, d.status, d.title, presentTime(d.createdAt, timeZone)]), defaultTableWidth())}\n`, { taskId: task.id, decisions });
6816
- }
6817
- if (command === "show") {
6818
- exactPositionals(rest, 2, "Task decision show usage: yui task decision show <task> <decision>.");
6819
- const task = requireTask(store, rest[0]);
6820
- const decision = store.getDecision(task.id, rest[1]);
6821
- if (decision === null)
6822
- throw dataError(`Decision not found: ${rest[1]}.`);
6823
- const timeZone = store.getConfig().timeZone;
6824
- return output([
6825
- `Decision: ${decision.id}`,
6826
- `Task: ${task.id}`,
6827
- `Title: ${decision.title}`,
6828
- `Rationale: ${decision.rationale}`,
6829
- `Status: ${decision.status}`,
6830
- ...(decision.supersededReason === undefined ? [] : [`Superseded reason: ${decision.supersededReason}`]),
6831
- ...(decision.supersededAt === undefined ? [] : [`Superseded at: ${presentTime(decision.supersededAt, timeZone)}`]),
6832
- `Created: ${presentTime(decision.createdAt, timeZone)}`,
6833
- `Updated: ${presentTime(decision.updatedAt, timeZone)}`
6834
- ].join("\n").concat("\n"), { taskId: task.id, decision });
6835
- }
6836
- if (command === "supersede") {
6837
- const usage = "Task decision supersede usage: yui task decision supersede <task> <decision> --reason <text>.";
6838
- const parsed = parseTail(rest, new Set(["--reason"]), usage);
6839
- exactPositionals(parsed.positionals, 2, usage);
6840
- const reason = requiredOption(parsed.options, "--reason");
6841
- const now = clock(options);
6842
- const result = store.transaction((tx) => {
6843
- const task = requireTask(tx, parsed.positionals[0]);
6844
- assertTaskOpen(task);
6845
- const actor = taskActor(tx, options, task.id);
6846
- const existing = tx.getDecision(task.id, parsed.positionals[1]);
6847
- if (existing === null)
6848
- throw dataError(`Decision not found: ${parsed.positionals[1]}.`);
6849
- const decision = supersedeDecision(existing, reason, now);
6850
- tx.saveDecision(task.id, decision);
6851
- recordTaskEvent(tx, task.id, "decision.superseded", {
6852
- decisionId: decision.id,
6853
- reason,
6854
- ...leaderActionEventPayload(tx, task.id, options)
6855
- }, now);
6856
- enqueueWork(tx, taskMailbox(task.id), "decision-superseded", now, [taskRef(task.id)]);
6857
- if (task.status === "active" && actor !== "leader") {
6858
- enqueueWork(tx, leaderMailbox(task.id), "decision-superseded", now, [taskRef(task.id)]);
6859
- }
6860
- return { task, decision };
6861
- });
6862
- notifyMailbox(options.runtime, taskMailbox(result.task.id), result.task.id);
6863
- if (result.task.status === "active")
6864
- notifyMailbox(options.runtime, leaderMailbox(result.task.id), result.task.id);
6865
- return output(`Superseded decision ${result.decision.id} for ${result.task.id}\n`);
6866
- }
6867
- throw usageError(command === undefined
6868
- ? "Task decision command is required."
6869
- : `Unknown command: task decision ${command}`);
6870
- }
6871
- function taskMilestoneCommand(args, store, options) {
6872
- const [command, ...rest] = args;
6873
- if (command === "add") {
6874
- const usage = "Task milestone add usage: yui task milestone add <task> --title <text> --summary <text>.";
6875
- const parsed = parseTail(rest, new Set(["--title", "--summary"]), usage);
6876
- exactPositionals(parsed.positionals, 1, usage);
6877
- const title = requiredOption(parsed.options, "--title");
6878
- const summary = requiredOption(parsed.options, "--summary");
6879
- const now = clock(options);
6880
- const result = store.transaction((tx) => {
6881
- const task = requireTask(tx, parsed.positionals[0]);
6882
- assertTaskOpen(task);
6883
- const actor = taskActor(tx, options, task.id);
6884
- const milestone = createMilestone(tx.nextMilestoneId(task.id), task.id, title, summary, actor, now);
6885
- tx.saveMilestone(task.id, milestone);
6886
- recordTaskEvent(tx, task.id, "milestone.added", {
6887
- milestoneId: milestone.id,
6888
- title,
6889
- ...leaderActionEventPayload(tx, task.id, options)
6890
- }, now);
6891
- enqueueWork(tx, taskMailbox(task.id), "milestone-added", now, [taskRef(task.id)]);
6892
- return { task, milestone };
6893
- });
6894
- notifyMailbox(options.runtime, taskMailbox(result.task.id), result.task.id);
6895
- return output(`Added milestone ${result.milestone.id} for ${result.task.id}\n`);
6896
- }
6897
- if (command === "list") {
6898
- exactPositionals(rest, 1, "Task milestone list usage: yui task milestone list <task>.");
6899
- const task = requireTask(store, rest[0]);
6900
- const milestones = store.listMilestones(task.id);
6901
- if (milestones.length === 0) {
6902
- return output(`No milestones found for ${task.id}.\n`, { taskId: task.id, milestones: [] });
6903
- }
6904
- const timeZone = store.getConfig().timeZone;
6905
- return output(`${renderTable(`Milestones: ${task.id}`, [
6906
- { header: "Milestone", minWidth: 9, maxWidth: 18 },
6907
- { header: "Title", minWidth: 8, maxWidth: 64 },
6908
- { header: "Created", minWidth: 10, maxWidth: 28 }
6909
- ], milestones.map((m) => [m.id, m.title, presentTime(m.createdAt, timeZone)]), defaultTableWidth())}\n`, { taskId: task.id, milestones });
6910
- }
6911
- if (command === "show") {
6912
- exactPositionals(rest, 2, "Task milestone show usage: yui task milestone show <task> <milestone>.");
6913
- const task = requireTask(store, rest[0]);
6914
- const milestone = store.getMilestone(task.id, rest[1]);
6915
- if (milestone === null)
6916
- throw dataError(`Milestone not found: ${rest[1]}.`);
6917
- const timeZone = store.getConfig().timeZone;
6918
- return output([
6919
- `Milestone: ${milestone.id}`,
6920
- `Task: ${task.id}`,
6921
- `Title: ${milestone.title}`,
6922
- `Summary: ${milestone.summary}`,
6923
- `Created by: ${milestone.createdBy}`,
6924
- `Created: ${presentTime(milestone.createdAt, timeZone)}`
6925
- ].join("\n").concat("\n"), { taskId: task.id, milestone });
6926
- }
6927
- throw usageError(command === undefined
6928
- ? "Task milestone command is required."
6929
- : `Unknown command: task milestone ${command}`);
6930
- }
6931
- function taskEventCommand(args, store) {
6932
- const [command, ...rest] = args;
6933
- if (command === "list") {
6934
- const eventListUsage = "Task event list usage: yui task event list <task> [--after <timestamp>] [--limit <n>].";
6935
- const parsed = parseTail(rest, new Set(["--after", "--limit"]), eventListUsage);
6936
- exactPositionals(parsed.positionals, 1, eventListUsage);
6937
- const task = requireTask(store, parsed.positionals[0]);
6938
- let events = store.listEvents(task.id);
6939
- const after = optionalNonEmptyOption(parsed.options, "--after");
6940
- if (after !== undefined) {
6941
- const afterMs = Date.parse(after);
6942
- if (!Number.isFinite(afterMs))
6943
- throw usageError("--after must be a valid timestamp.", eventListUsage);
6944
- events = events.filter((e) => Date.parse(e.createdAt) > afterMs);
6945
- }
6946
- const limit = optionalNonEmptyOption(parsed.options, "--limit");
6947
- if (limit !== undefined) {
6948
- const n = Number(limit);
6949
- if (!Number.isSafeInteger(n) || n <= 0)
6950
- throw usageError("--limit must be a positive integer.", eventListUsage);
6951
- events = events.slice(-n);
6952
- }
6953
- if (events.length === 0) {
6954
- return output(`No events found for ${task.id}.\n`, { taskId: task.id, events: [] });
6955
- }
6956
- const timeZone = store.getConfig().timeZone;
6957
- return output(`${renderTable(`Events: ${task.id}`, [
6958
- { header: "Event", minWidth: 8, maxWidth: 18 },
6959
- { header: "Type", minWidth: 8, maxWidth: 28 },
6960
- { header: "Created", minWidth: 10, maxWidth: 28 }
6961
- ], events.map((e) => [e.id, e.type, presentTime(e.createdAt, timeZone)]), defaultTableWidth())}\n`, { taskId: task.id, events });
6962
- }
6963
- if (command === "show") {
6964
- exactPositionals(rest, 2, "Task event show usage: yui task event show <task> <event>.");
6965
- const task = requireTask(store, rest[0]);
6966
- const events = store.listEvents(task.id);
6967
- const event = events.find((e) => e.id === rest[1]) ?? null;
6968
- if (event === null)
6969
- throw dataError(`Event not found: ${rest[1]}.`);
6970
- const timeZone = store.getConfig().timeZone;
6971
- return output([
6972
- `Event: ${event.id}`,
6973
- `Task: ${task.id}`,
6974
- `Type: ${event.type}`,
6975
- `Created: ${presentTime(event.createdAt, timeZone)}`,
6976
- `Payload:`,
6977
- ...(Object.keys(event.payload).length === 0
6978
- ? [" (none)"]
6979
- : Object.entries(event.payload).map(([k, v]) => ` ${k}: ${v}`))
6980
- ].join("\n").concat("\n"), { taskId: task.id, event });
6981
- }
6982
- throw usageError(command === undefined
6983
- ? "Task event command is required."
6984
- : `Unknown command: task event ${command}`);
6985
- }
6986
6725
  /**
6987
6726
  * Issue 13: native child durability visibility. A native Provider subagent is
6988
6727
  * best-effort until Yui persists its result content; once a continuation
@@ -7075,6 +6814,8 @@ function continuationReportEvents(events) {
7075
6814
  */
7076
6815
  function taskWakeDispatch(args, store, options) {
7077
6816
  const [subcommand] = args;
6817
+ if (subcommand === "retry")
6818
+ return taskWakeRetryCommand(args.slice(1), store, options);
7078
6819
  if (subcommand === "resolve") {
7079
6820
  const usage = "Task wake resolve usage: yui task wake resolve <task> <wake> --reason <quiescence-evidence>.";
7080
6821
  const parsed = parseTail(args.slice(1), new Set(["--reason"]), usage);
@@ -7114,13 +6855,15 @@ function taskWakeDispatch(args, store, options) {
7114
6855
  tx.saveWorkMailbox(completeProcessing(mailbox, claim.batchId));
7115
6856
  return { taskId: task.id, wakeId: wake.id, outcome: "released-without-replay" };
7116
6857
  });
7117
- notifyMailbox(options.runtime, leaderMailbox(result.taskId), result.taskId);
6858
+ notifyMailbox(options.runtime, leaderMailbox(result.taskId));
7118
6859
  return output(`Released ${result.wakeId} without replay; original acceptance remains unknown.\n`, result);
7119
6860
  }
7120
6861
  if (subcommand === "list" || subcommand === "show") {
7121
6862
  return taskWakeInspectionCommand(args, store);
7122
6863
  }
7123
- return output(taskWakeForceCommand(args, store, options));
6864
+ throw usageError(subcommand === undefined
6865
+ ? "Task wake command is required."
6866
+ : `Unknown command: task wake ${subcommand}`);
7124
6867
  }
7125
6868
  /**
7126
6869
  * Issue 04 (long-term): the durable wake ledger. `wake list` shows the
@@ -7143,13 +6886,11 @@ function taskWakeInspectionCommand(args, store) {
7143
6886
  { header: "Wake", minWidth: 8, maxWidth: 18 },
7144
6887
  { header: "Status", minWidth: 8, maxWidth: 12 },
7145
6888
  { header: "Reasons", minWidth: 10, maxWidth: 40 },
7146
- { header: "AgentRun", minWidth: 10, maxWidth: 20 },
7147
6889
  { header: "Dispatched", minWidth: 10, maxWidth: 28 }
7148
6890
  ], wakes.map((wake) => [
7149
6891
  wake.id,
7150
6892
  wake.status,
7151
6893
  wake.reasons.map(renderWakeReason).join(", "),
7152
- wake.runId ?? "-",
7153
6894
  presentTime(wake.createdAt, timeZone)
7154
6895
  ]), defaultTableWidth())}\n`, { taskId: task.id, wakes });
7155
6896
  }
@@ -7171,6 +6912,8 @@ function taskWakeInspectionCommand(args, store) {
7171
6912
  const deliveryEvents = allEvents.filter((event) => (event.type === "notification.delivery" || event.type === "notification.resolved")
7172
6913
  && (event.payload.wakeId === wake.id
7173
6914
  || event.payload.attemptId?.startsWith(`notification:${task.id}/${wake.id}/`)));
6915
+ const lastDelivery = deliveryEvents.at(-1);
6916
+ const currentClaim = store.getWorkMailbox(leaderMailbox(task.id))?.processing;
7174
6917
  const referenced = (type, id) => wake.refs?.some(ref => ref.type === type && ref.id === id
7175
6918
  && (!("taskId" in ref) || ref.taskId === task.id)) === true;
7176
6919
  const events = allEvents.filter((e) => inWindow(e.createdAt) || referenced("event", e.id));
@@ -7188,10 +6931,18 @@ function taskWakeInspectionCommand(args, store) {
7188
6931
  `Wake: ${wake.id}`,
7189
6932
  `Task: ${task.id}`,
7190
6933
  `Status: ${wake.status}`,
7191
- `Notification: ${deliveryEvents.at(-1)?.payload.outcome ?? "unobserved"}`,
6934
+ `Notification: ${lastDelivery?.payload.outcome ?? "unobserved"}`,
6935
+ ...(lastDelivery?.payload.detail === undefined ? [] : [`Failure: ${lastDelivery.payload.detail}`]),
6936
+ ...(lastDelivery?.payload.errorEventId === undefined ? [] : [
6937
+ `Failure record: ${task.id}/${lastDelivery.payload.errorEventId}`,
6938
+ `Inspect capabilities: ${failureCapabilitiesCommand(task.id, "leader", lastDelivery.payload.errorEventId)}`
6939
+ ]),
6940
+ ...(lastDelivery?.payload.outcome !== "rejected"
6941
+ || currentClaim?.batchId !== lastDelivery.payload.attemptId ? [] : [
6942
+ `Recovery: correct the cause, then yui task wake retry ${task.id} ${wake.id} --reason <correction>.`
6943
+ ]),
7192
6944
  `Reasons: ${wake.reasons.map(renderWakeReason).join(", ")}`,
7193
6945
  `Delta window: ${wake.fromCursor} → ${wake.toCursor}`,
7194
- ...(wake.runId === undefined ? [] : [`AgentRun: ${wake.runId}`]),
7195
6946
  `Dispatched: ${presentTime(wake.createdAt, timeZone)}`,
7196
6947
  ...(wake.consumedAt === undefined
7197
6948
  ? []
@@ -7220,130 +6971,13 @@ function taskWakeInspectionCommand(args, store) {
7220
6971
  ? "Task wake command is required."
7221
6972
  : `Unknown command: task wake ${command}`);
7222
6973
  }
7223
- function parseMultiValueTail(args, valueOptions, repeatOptions, usage, flagOptions = new Set()) {
7224
- const positionals = [];
7225
- const options = new Map();
7226
- const multiOptions = new Map();
7227
- for (let index = 0; index < args.length; index += 1) {
7228
- const value = args[index];
7229
- if (!value.startsWith("--")) {
7230
- positionals.push(value);
7231
- continue;
7232
- }
7233
- if (!valueOptions.has(value) && !repeatOptions.has(value) && !flagOptions.has(value)) {
7234
- throw usageError(`Unsupported option: ${value}.`, usage);
7235
- }
7236
- if (flagOptions.has(value)) {
7237
- if (options.has(value))
7238
- throw usageError(`Option may only be specified once: ${value}.`, usage);
7239
- options.set(value, "");
7240
- continue;
7241
- }
7242
- if (repeatOptions.has(value)) {
7243
- const optionValue = args[index + 1];
7244
- if (optionValue === undefined || optionValue.startsWith("--")) {
7245
- throw usageError(`${value} is required.`, usage);
7246
- }
7247
- const existing = multiOptions.get(value) ?? [];
7248
- multiOptions.set(value, [...existing, optionValue]);
7249
- index += 1;
7250
- continue;
7251
- }
7252
- if (options.has(value))
7253
- throw usageError(`Option may only be specified once: ${value}.`, usage);
7254
- const optionValue = args[index + 1];
7255
- if (optionValue === undefined || optionValue.startsWith("--")) {
7256
- throw usageError(`${value} is required.`, usage);
7257
- }
7258
- options.set(value, optionValue);
7259
- index += 1;
7260
- }
7261
- return { positionals, options, multiOptions };
7262
- }
7263
- function parseTail(args, valueOptions, usage, flagOptions = new Set()) {
7264
- const positionals = [];
7265
- const options = new Map();
7266
- for (let index = 0; index < args.length; index += 1) {
7267
- const value = args[index];
7268
- if (!value.startsWith("--")) {
7269
- positionals.push(value);
7270
- continue;
7271
- }
7272
- if (!valueOptions.has(value) && !flagOptions.has(value)) {
7273
- throw usageError(`Unsupported option: ${value}.`, usage);
7274
- }
7275
- if (options.has(value))
7276
- throw usageError(`Option may only be specified once: ${value}.`, usage);
7277
- if (flagOptions.has(value)) {
7278
- options.set(value, "");
7279
- continue;
7280
- }
7281
- const optionValue = args[index + 1];
7282
- if (optionValue === undefined || optionValue.startsWith("--")) {
7283
- throw usageError(`${value} is required.`, usage);
7284
- }
7285
- options.set(value, optionValue);
7286
- index += 1;
7287
- }
7288
- return { positionals, options };
7289
- }
7290
- function requiredOption(options, name) {
7291
- return requiredText(options.get(name), name);
7292
- }
7293
- function optionalNonEmptyOption(options, name) {
7294
- if (!options.has(name))
7295
- return undefined;
7296
- return requiredText(options.get(name), name);
7297
- }
7298
- function exactPositionals(values, count, usage) {
7299
- if (values.length !== count || values.some((value) => value.trim().length === 0)) {
7300
- throw usageError(usage);
7301
- }
7302
- }
7303
- function assertNoArguments(args, usage) {
7304
- if (args.length > 0)
7305
- throw usageError(usage);
7306
- }
7307
- function requiredText(value, label) {
7308
- const normalized = value?.trim();
7309
- if (normalized === undefined || normalized.length === 0)
7310
- throw usageError(`${label} is required.`);
7311
- return normalized;
7312
- }
7313
- function trimmed(value) {
7314
- const normalized = value?.trim();
7315
- return normalized === undefined || normalized.length === 0 ? undefined : normalized;
7316
- }
7317
6974
  function titleFrom(body) {
7318
6975
  const oneLine = requiredText(body, "Message body").replace(/\s+/g, " ");
7319
6976
  return oneLine.length <= 80 ? oneLine : `${oneLine.slice(0, 77)}...`;
7320
6977
  }
7321
- function presentTime(value, timeZone) {
7322
- return formatTimestamp(value, timeZone);
7323
- }
7324
- function output(value, data) {
7325
- return data === undefined
7326
- ? { kind: "output", output: value }
7327
- : { kind: "output", output: value, data };
7328
- }
7329
- function clock(options) {
7330
- return options.now?.() ?? new Date();
7331
- }
7332
6978
  function messageOf(error) {
7333
6979
  return error instanceof Error ? error.message : String(error);
7334
6980
  }
7335
- function taskMailbox(taskId) {
7336
- return { kind: "task", taskId };
7337
- }
7338
- function roleMailbox(taskId, roleName) {
7339
- return { kind: "role", taskId, roleName };
7340
- }
7341
- function leaderMailbox(taskId) {
7342
- return roleMailbox(taskId, LEADER_ROLE);
7343
- }
7344
- function taskRef(id) {
7345
- return { type: "task", id };
7346
- }
7347
6981
  function runRef(taskId, id) {
7348
6982
  return { type: "run", taskId, id };
7349
6983
  }
@@ -7356,14 +6990,3 @@ function messageRef(taskId, id) {
7356
6990
  function eventRef(taskId, id) {
7357
6991
  return { type: "event", taskId, id };
7358
6992
  }
7359
- function notifyMailbox(runtime, target, compatibilityTaskId) {
7360
- if (runtime?.notifyMailboxChanged !== undefined) {
7361
- runtime.notifyMailboxChanged(target);
7362
- }
7363
- else {
7364
- runtime?.notifyStateChanged(compatibilityTaskId);
7365
- }
7366
- }
7367
- function notifyReviewMailbox(_options, runtime, target, compatibilityTaskId) {
7368
- notifyMailbox(runtime, target, compatibilityTaskId);
7369
- }