@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.
- package/ARCHITECTURE.md +11 -6
- package/ARCHITECTURE.zh-CN.md +8 -4
- package/README.md +13 -5
- package/dist/agentRun/agentRun.js +3 -0
- package/dist/artifacts/artifactCommitLock.js +16 -55
- package/dist/artifacts/taskArtifactRepository.js +3 -3
- package/dist/cli/agentConfigurationPicker.js +1 -1
- package/dist/cli/commandCatalog.js +92 -48
- package/dist/cli/completion.js +18 -18
- package/dist/cli/completionWizard.js +1 -1
- package/dist/cli/dynamicCompletion.js +1 -1
- package/dist/cli/interactionPolicy.js +11 -3
- package/dist/cli/invocationAuthority.js +64 -0
- package/dist/cli/managedDiagnostics.js +2 -2
- package/dist/cli/parseRepeatable.js +35 -0
- package/dist/cli/updatePorts.js +26 -5
- package/dist/cli.js +1303 -1235
- package/dist/commands/agentCommands.js +4 -4
- package/dist/commands/capabilityCommands.js +1 -1
- package/dist/commands/configCommands.js +6 -57
- package/dist/commands/configOverview.js +2 -2
- package/dist/commands/durableJobCommands.js +12 -6
- package/dist/commands/executionAuditCommands.js +10 -0
- package/dist/commands/globalRoleCommands.js +78 -29
- package/dist/commands/grantCommands.js +0 -3
- package/dist/commands/jobCommands.js +1 -4
- package/dist/commands/operatorCommands.js +3 -3
- package/dist/commands/projectCommands.js +45 -25
- package/dist/commands/resourcesCommands.js +10 -40
- package/dist/commands/roleConfiguration.js +2 -6
- package/dist/commands/taskActivationCommands.js +4 -8
- package/dist/commands/taskAgentCapabilities.js +9 -0
- package/dist/commands/taskCommandSupport.js +155 -0
- package/dist/commands/taskCommandTypes.js +1 -0
- package/dist/commands/taskCommands.js +355 -732
- package/dist/commands/taskCompletionGate.js +1 -1
- package/dist/commands/taskExecutionCommands.js +1 -1
- package/dist/commands/taskFactCommands.js +325 -0
- package/dist/commands/taskInputCommands.js +12 -42
- package/dist/commands/taskIntegrationCommands.js +20 -58
- package/dist/commands/taskNextActionCommand.js +7 -6
- package/dist/commands/taskPublicationAdoptCommand.js +127 -0
- package/dist/commands/taskPublicationCommands.js +12 -3
- package/dist/commands/taskPublicationVerifyCommand.js +24 -40
- package/dist/commands/taskRemoteDeliveryCommand.js +5 -86
- package/dist/commands/taskUpstreamCommands.js +19 -8
- package/dist/commands/workflowCommands.js +6 -1
- package/dist/completion/completionInstaller.js +26 -26
- package/dist/completion/completionState.js +26 -26
- package/dist/completion/fileCompletionManager.js +6 -11
- package/dist/config/configCatalog.js +0 -2
- package/dist/config/timeZone.js +14 -0
- package/dist/config/yuiConfig.js +0 -29
- package/dist/context/runContextPack.js +5 -3
- package/dist/context/taskCatalog.js +185 -0
- package/dist/context/taskContext.js +152 -151
- package/dist/context/wakeRunReferences.js +11 -0
- package/dist/controller/agentCapabilities.js +58 -0
- package/dist/controller/agentRuntimeObserver.js +6 -11
- package/dist/controller/clientRuntime.js +22 -14
- package/dist/controller/controller.js +20 -24
- package/dist/controller/fileSchedulerStoreAdapter.js +191 -225
- package/dist/controller/globalInputDelivery.js +13 -0
- package/dist/controller/jobClient.js +4 -4
- package/dist/controller/jobControl.js +60 -25
- package/dist/controller/jobSupervisor.js +4 -4
- package/dist/controller/providerRetryAdmission.js +100 -0
- package/dist/controller/providerRetryDelivery.js +218 -0
- package/dist/controller/runtime.js +85 -43
- package/dist/controller/runtimeEventProcessor.js +0 -5
- package/dist/controller/sessionOwnerReconciliation.js +5 -0
- package/dist/controller/taskAgentError.js +35 -0
- package/dist/coordination/workMailboxQueue.js +2 -24
- package/dist/core/controllerClient.js +2 -2
- package/dist/core/fileLockOwner.js +74 -0
- package/dist/decision/decision.js +17 -0
- package/dist/doctor/doctor.js +18 -9
- package/dist/event/taskEvent.js +12 -0
- package/dist/execution/workItemExecutionProjection.js +2 -2
- package/dist/executor/agentCapabilityConfig.js +43 -0
- package/dist/executor/agentConfigurationCatalog.js +49 -11
- package/dist/executor/agentExecutor.js +5 -8
- package/dist/executor/fileRoleLaunchPlanner.js +11 -53
- package/dist/executor/taskAgentCapabilities.js +64 -0
- package/dist/integration/deliveryObligation.js +1 -69
- package/dist/integration/gitIntegrationService.js +79 -90
- package/dist/integration/integrationAttempt.js +3 -14
- package/dist/integration/integrationSourceApplication.js +4 -12
- package/dist/integration/manifestTags.js +2 -2
- package/dist/job/durableJob.js +4 -25
- package/dist/job/jobAssignmentScope.js +22 -0
- package/dist/job/jobOperation.js +16 -0
- package/dist/job/jobRunner.js +2 -1
- package/dist/job/stepDirectory.js +15 -0
- package/dist/kernel/builtinCapabilities.js +15 -11
- package/dist/kernel/kernelPorts.js +1 -17
- package/dist/lifecycle/exactRunTerminalization.js +5 -10
- package/dist/message/globalProviderRetry.js +15 -0
- package/dist/message/inputControlResolution.js +3 -4
- package/dist/message/message.js +17 -20
- package/dist/message/messageContinuation.js +1 -1
- package/dist/milestone/milestone.js +11 -0
- package/dist/observability/executionAudit.js +19 -0
- package/dist/observability/orchestrationMetrics.js +24 -1
- package/dist/observability/runtimeIdentity.js +1 -17
- package/dist/output/agentConfigurationPresentation.js +2 -0
- package/dist/output/timePresentation.js +1 -14
- package/dist/plugins/pluginService.js +12 -4
- package/dist/release/releaseWorkflowPorts.js +45 -96
- package/dist/release/runtimeRelease.js +9 -15
- package/dist/repository/gitWorkspace.js +371 -105
- package/dist/repository/projectMaintenanceLock.js +98 -82
- package/dist/repository/taskBaseFreshness.js +1 -1
- package/dist/repository/taskWorkspaceCoordinator.js +82 -144
- package/dist/repository/taskWorkspacePreparer.js +113 -47
- package/dist/repository/workspaceCleanupInspection.js +187 -0
- package/dist/resources/autoResourceGc.js +4 -77
- package/dist/resources/liveReferences.js +7 -54
- package/dist/resources/resourceDiscovery.js +5 -118
- package/dist/resources/resourceGc.js +233 -181
- package/dist/resources/resourceRegistrar.js +5 -4
- package/dist/resources/resourceRegistry.js +3 -37
- package/dist/resources/resourceRegistryStore.js +13 -36
- package/dist/resources/sqliteResourceRegistry.js +43 -26
- package/dist/review/deltaRecheck.js +1 -1
- package/dist/review/reviewAcceptance.js +1 -1
- package/dist/review/reviewDecision.js +1 -1
- package/dist/review/reviewRound.js +8 -7
- package/dist/review/taskFinalReviewContractResolution.js +1 -1
- package/dist/runtime/acpProtocol.js +4 -51
- package/dist/runtime/acpSession.js +10 -75
- package/dist/runtime/acpSessionConfiguration.js +1 -8
- package/dist/runtime/agentEndpoint.js +3 -2
- package/dist/runtime/agentError.js +5 -3
- package/dist/runtime/agentFailureContext.js +43 -0
- package/dist/runtime/agentHost.js +42 -25
- package/dist/runtime/builtinAgentErrorMappers.js +91 -0
- package/dist/runtime/codexAppServerRuntime.js +34 -3
- package/dist/runtime/codexInteractiveHost.js +2 -2
- package/dist/runtime/index.js +0 -1
- package/dist/runtime/managedCaller.js +21 -1
- package/dist/runtime/providerControl.js +5 -1
- package/dist/runtime/providerErrors.js +38 -0
- package/dist/runtime/providerRetry.js +198 -0
- package/dist/runtime/providerRuntimeIdentity.js +28 -2
- package/dist/runtime/runtimeObservation.js +0 -5
- package/dist/runtime/runtimeSessionCandidate.js +2 -3
- package/dist/runtime/sessionOwnerIdentity.js +1 -1
- package/dist/runtime/sessionTokenMetrics.js +15 -5
- package/dist/runtime/structuredProviderHost.js +10 -43
- package/dist/runtime/taskUsageMetrics.js +275 -0
- package/dist/runtime/tmuxAdapters.js +6 -8
- package/dist/scheduler/activeRoleRunDelivery.js +19 -7
- package/dist/scheduler/leaderWakeupProcessor.js +24 -6
- package/dist/scheduler/operatorEvent.js +12 -20
- package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
- package/dist/scheduler/ports.js +5 -8
- package/dist/scheduler/roleRunLiveness.js +4 -4
- package/dist/scheduler/roleRunStall.js +16 -23
- package/dist/scheduler/taskExecutionProjection.js +49 -36
- package/dist/scheduler/taskObservabilityProjection.js +6 -45
- package/dist/scheduler/taskWake.js +5 -10
- package/dist/scheduler/wakeReason.js +2 -0
- package/dist/scheduler/wakeupQueue.js +2 -4
- package/dist/setup/setupCommand.js +1 -1
- package/dist/storage/contextRecords.js +106 -0
- package/dist/storage/currentTaskStore.js +1 -1
- package/dist/storage/homeLayout.js +13 -17
- package/dist/storage/migrations/agentFailureContext.js +22 -0
- package/dist/storage/migrations/currentInputContract.js +86 -0
- package/dist/storage/migrations/currentRuntimeContract.js +228 -0
- package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
- package/dist/storage/migrations/integrationContinuation.js +5 -4
- package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
- package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
- package/dist/storage/migrations/verificationPlanV1.js +162 -0
- package/dist/storage/migrations/verificationPolicy.js +74 -0
- package/dist/storage/migrations/workItemHistory.js +46 -0
- package/dist/storage/persistenceWorker.js +12 -4
- package/dist/storage/recordValidation.js +87 -0
- package/dist/storage/sqliteSchema.js +173 -1
- package/dist/storage/sqliteStore.js +172 -161
- package/dist/storage/storageSchema.js +2 -14
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +16 -7
- package/dist/storage/taskCatalog.js +123 -0
- package/dist/storage/taskStore.js +5 -7
- package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
- package/dist/task/archiveDiagnostics.js +1 -0
- package/dist/task/archivePreflight.js +124 -0
- package/dist/task/completionReadiness.js +5 -24
- package/dist/task/draftPlan.js +0 -53
- package/dist/task/nextAction.js +7 -13
- package/dist/task/publicationAdoption.js +56 -0
- package/dist/task/publicationReference.js +10 -0
- package/dist/task/remoteDelivery.js +31 -16
- package/dist/task/remoteDeliveryService.js +89 -0
- package/dist/task/taskActivation.js +2 -25
- package/dist/task/taskActivationService.js +0 -2
- package/dist/task/taskRecordReference.js +0 -1
- package/dist/task/taskSubmission.js +3 -14
- package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
- package/dist/telemetry/sqliteTelemetryStore.js +49 -49
- package/dist/telemetry/telemetryWiring.js +4 -3
- package/dist/tmux/tmuxManager.js +29 -20
- package/dist/verification/gateArtifact.js +15 -24
- package/dist/verification/gateArtifactStore.js +14 -7
- package/dist/verification/verificationGateService.js +29 -88
- package/dist/verification/verificationPlan.js +27 -72
- package/dist/web/assets/client/app.js +92 -17
- package/dist/web/assets/client/components.js +55 -13
- package/dist/web/assets/client/i18n.js +72 -4
- package/dist/web/assets/client/taskSurface.js +6 -5
- package/dist/web/assets/client/view.js +35 -7
- package/dist/web/assets/shell.js +6 -0
- package/dist/web/assets/styles/layout.js +7 -0
- package/dist/web/webServer.js +12 -3
- package/dist/web/webSnapshot.js +14 -85
- package/dist/web/webTaskSurface.js +17 -46
- package/dist/workItem/workItem.js +1 -12
- package/dist/workspace/cleanupInspection.js +63 -0
- package/dist/workspace/workItemChangeSetManager.js +110 -50
- package/docs/agent-result-consumption.md +4 -0
- package/docs/agent-result-consumption.zh-CN.md +3 -0
- package/docs/agent-runtime-drivers.md +7 -0
- package/docs/agent-runtime-drivers.zh-CN.md +5 -0
- package/docs/architecture/README.md +2 -0
- package/docs/architecture/README.zh-CN.md +3 -1
- package/docs/architecture/capabilities-and-resources.md +42 -5
- package/docs/architecture/capabilities-and-resources.zh-CN.md +33 -3
- package/docs/managed-turn-and-session-runtime.md +121 -1
- package/docs/managed-turn-and-session-runtime.zh-CN.md +96 -2
- package/docs/observability/README.md +62 -0
- package/docs/observability/README.zh-CN.md +47 -0
- package/docs/plugin-sdk.md +4 -2
- package/docs/project-refresh.md +77 -0
- package/docs/project-refresh.zh-CN.md +59 -0
- package/docs/provider-retry.md +70 -0
- package/docs/provider-runtime.md +3 -1
- package/docs/provider-runtime.zh-CN.md +4 -2
- package/docs/release-workflow.md +190 -7
- package/docs/release-workflow.zh-CN.md +139 -5
- package/docs/roles-and-configuration.md +29 -0
- package/docs/roles-and-configuration.zh-CN.md +21 -0
- package/docs/sqlite-control-plane-design.md +16 -3
- package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
- package/docs/task-dag-semantics.md +3 -3
- package/docs/task-dag-semantics.zh-CN.md +2 -2
- package/docs/task-delivery.md +223 -17
- package/docs/task-delivery.zh-CN.md +171 -14
- package/docs/task-discovery.md +101 -0
- package/docs/task-discovery.zh-CN.md +85 -0
- package/docs/testing/verification-levels.md +161 -9
- package/docs/testing/verification-levels.zh-CN.md +116 -9
- package/i18n/README.zh-CN.md +13 -7
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +5 -0
- package/skills/yui-leader/references/execution.md +3 -2
- package/skills/yui-leader/references/integration.md +9 -5
- package/skills/yui-leader/references/planning.md +9 -3
- package/skills/yui-operator/SKILL.md +24 -8
- package/skills/yui-reviewer/SKILL.md +4 -0
- package/skills/yui-runtime/SKILL.md +17 -2
- package/skills/yui-runtime/references/publication.md +26 -4
- package/skills/yui-runtime/references/recovery.md +71 -0
- package/dist/agent/agentRegistry.js +0 -10
- package/dist/agentRun/runIdentity.js +0 -22
- package/dist/commands/deliveryGuardPreflight.js +0 -30
- package/dist/commands/taskIntegrationQueueCommands.js +0 -226
- package/dist/commands/taskOverviewCommand.js +0 -376
- package/dist/completion/completionWizard.js +0 -125
- package/dist/context/dispatchContext.js +0 -110
- package/dist/context/wakeNotification.js +0 -144
- package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
- package/dist/integration/integrationQueueEntry.js +0 -191
- package/dist/integration/integrationQueueService.js +0 -810
- package/dist/release/fakeReleasePorts.js +0 -55
- package/dist/runtime/sessionOwnerRegistry.js +0 -137
- package/dist/task/deliveryGuard.js +0 -226
- /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 {
|
|
6
|
-
import {
|
|
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 {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
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 {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
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 {
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
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 {
|
|
47
|
-
import {
|
|
48
|
-
import {
|
|
49
|
-
import {
|
|
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 {
|
|
52
|
-
import {
|
|
53
|
-
import {
|
|
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 {
|
|
56
|
-
import {
|
|
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 {
|
|
68
|
-
import {
|
|
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 {
|
|
72
|
-
import {
|
|
73
|
-
import {
|
|
74
|
-
import {
|
|
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 = {},
|
|
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) => (
|
|
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
|
-
|| (
|
|
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)
|
|
468
|
+
notifyMailbox(options.runtime, taskMailbox(updated.id));
|
|
470
469
|
if (updated.status === "active") {
|
|
471
|
-
notifyMailbox(options.runtime, leaderMailbox(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
|
|
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)
|
|
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
|
-
||
|
|
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 `
|
|
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)
|
|
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)
|
|
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
|
|
909
|
-
|
|
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)
|
|
1048
|
-
notifyMailbox(options.runtime, leaderMailbox(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" }
|
|
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)
|
|
1236
|
+
notifyMailbox(options.runtime, taskMailbox(result.task.id));
|
|
1244
1237
|
if (result.wakeLeader) {
|
|
1245
|
-
notifyMailbox(options.runtime, leaderMailbox(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
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
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?.
|
|
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?.
|
|
1385
|
-
notifyMailbox(options.runtime, { kind: "operator" }
|
|
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?.
|
|
1487
|
-
notifyMailbox(options.runtime, { kind: "operator" }
|
|
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
|
-
+
|
|
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
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
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)
|
|
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>] [--
|
|
1623
|
-
const parsed = parseTail(rest, new Set(["--body-file", "--intent", "--request-id", "--
|
|
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,
|
|
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,
|
|
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,
|
|
1783
|
-
: sendTaskMessageCommand(store,
|
|
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,
|
|
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
|
|
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)
|
|
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)
|
|
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|->)
|
|
2019
|
-
const parsed = parseTail(args, new Set(["--body-file"
|
|
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
|
-
|
|
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
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
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?.
|
|
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
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
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
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
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(
|
|
2140
|
-
return `
|
|
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,
|
|
2199
|
-
const role = requireRole(store, task.id,
|
|
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
|
|
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
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
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?.
|
|
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
|
|
3199
|
+
return created;
|
|
3154
3200
|
});
|
|
3155
|
-
notifyMailbox(options.runtime, taskMailbox(item.
|
|
3156
|
-
return output(
|
|
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)
|
|
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)
|
|
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)
|
|
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?.
|
|
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)
|
|
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) =>
|
|
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 (
|
|
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
|
|
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
|
|
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)
|
|
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)
|
|
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
|
|
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?.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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) =>
|
|
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
|
-
|
|
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)
|
|
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(
|
|
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
|
-
&&
|
|
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) => (
|
|
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,
|
|
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 =
|
|
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) =>
|
|
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)
|
|
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
|
|
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
|
|
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 (
|
|
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 =
|
|
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 (
|
|
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 (
|
|
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 (
|
|
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
|
-
&&
|
|
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)
|
|
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)
|
|
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)
|
|
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
|
-
|
|
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: ${
|
|
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
|
-
}
|