@zq-silk/yui 0.15.12 → 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 +3 -2
- package/ARCHITECTURE.zh-CN.md +3 -2
- 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 +57 -41
- 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 +7 -3
- package/dist/cli/invocationAuthority.js +64 -0
- package/dist/cli/managedDiagnostics.js +1 -1
- package/dist/cli/parseRepeatable.js +35 -0
- package/dist/cli/updatePorts.js +26 -5
- package/dist/cli.js +1280 -1243
- 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/globalRoleCommands.js +53 -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 +1 -10
- 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 +271 -710
- 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 +17 -57
- package/dist/commands/taskNextActionCommand.js +7 -6
- package/dist/commands/taskPublicationAdoptCommand.js +3 -3
- package/dist/commands/taskPublicationCommands.js +1 -1
- package/dist/commands/taskPublicationVerifyCommand.js +1 -1
- package/dist/commands/taskRemoteDeliveryCommand.js +2 -94
- 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 +2 -3
- package/dist/context/taskCatalog.js +3 -5
- package/dist/context/taskContext.js +139 -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 +9 -22
- package/dist/controller/fileSchedulerStoreAdapter.js +117 -212
- package/dist/controller/jobClient.js +4 -4
- package/dist/controller/jobControl.js +60 -25
- package/dist/controller/jobSupervisor.js +4 -4
- package/dist/controller/providerRetryDelivery.js +7 -7
- package/dist/controller/runtime.js +51 -44
- 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 +60 -84
- 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 +1 -9
- 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/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/projectMaintenanceLock.js +23 -64
- package/dist/repository/taskBaseFreshness.js +1 -1
- package/dist/repository/taskWorkspaceCoordinator.js +13 -22
- package/dist/repository/taskWorkspacePreparer.js +29 -25
- package/dist/repository/workspaceCleanupInspection.js +5 -5
- 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/agentFailureContext.js +43 -0
- package/dist/runtime/agentHost.js +15 -15
- package/dist/runtime/codexInteractiveHost.js +2 -2
- package/dist/runtime/index.js +0 -1
- package/dist/runtime/managedCaller.js +21 -1
- package/dist/runtime/providerErrors.js +38 -0
- package/dist/runtime/runtimeObservation.js +0 -5
- package/dist/runtime/runtimeSessionCandidate.js +2 -3
- package/dist/runtime/sessionOwnerIdentity.js +1 -1
- package/dist/runtime/structuredProviderHost.js +4 -41
- package/dist/runtime/tmuxAdapters.js +6 -8
- package/dist/scheduler/activeRoleRunDelivery.js +7 -7
- package/dist/scheduler/leaderWakeupProcessor.js +19 -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 +25 -33
- package/dist/scheduler/taskObservabilityProjection.js +0 -1
- 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 +142 -1
- package/dist/storage/sqliteStore.js +156 -162
- package/dist/storage/storageSchema.js +2 -14
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +15 -7
- package/dist/storage/taskStore.js +3 -7
- package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
- package/dist/task/completionReadiness.js +5 -24
- package/dist/task/draftPlan.js +0 -53
- package/dist/task/nextAction.js +7 -13
- 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 +1 -1
- package/dist/web/assets/client/taskSurface.js +4 -4
- package/dist/web/webServer.js +4 -6
- package/dist/web/webSnapshot.js +3 -83
- package/dist/web/webTaskSurface.js +17 -46
- package/dist/workItem/workItem.js +1 -12
- package/docs/architecture/capabilities-and-resources.md +14 -2
- package/docs/architecture/capabilities-and-resources.zh-CN.md +11 -1
- package/docs/managed-turn-and-session-runtime.md +83 -10
- package/docs/managed-turn-and-session-runtime.zh-CN.md +65 -11
- package/docs/plugin-sdk.md +4 -2
- 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 +91 -5
- package/docs/task-delivery.zh-CN.md +72 -4
- package/docs/task-discovery.md +10 -11
- package/docs/task-discovery.zh-CN.md +8 -9
- package/docs/testing/verification-levels.md +145 -9
- package/docs/testing/verification-levels.zh-CN.md +103 -8
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +5 -0
- package/skills/yui-leader/references/integration.md +9 -5
- package/skills/yui-leader/references/planning.md +9 -3
- package/skills/yui-operator/SKILL.md +15 -10
- package/skills/yui-runtime/SKILL.md +6 -6
- package/skills/yui-runtime/references/recovery.md +47 -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 -377
- 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,91 +1,87 @@
|
|
|
1
|
-
import { controlProviderRetry, providerRetryProjection, renderProviderRetry } from "../runtime/providerRetry.js";
|
|
2
|
-
import { roleLaunchEventPayload, saveTaskRoleUpdate } from "../role/taskRoleUpdate.js";
|
|
3
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
|
-
import { archiveDeliveryWarnings, archiveRetainedResources, renderArchiveDiagnostics, taskArchiveDiagnostics } from "../task/archiveDiagnostics.js";
|
|
5
|
-
import { archiveSettlementChecks } from "../task/archivePreflight.js";
|
|
6
|
-
import { CleanupInspectionError } from "../workspace/cleanupInspection.js";
|
|
7
2
|
import { isDeepStrictEqual } from "node:util";
|
|
8
|
-
import {
|
|
9
|
-
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";
|
|
10
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";
|
|
11
14
|
import { CliError, dataError, roleNotFound, runtimeError, taskNotFound, usageError } from "../errors/cliError.js";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import { assertTaskCompletionPublishedTreeProof } from "./taskCompletionGate.js";
|
|
18
|
-
import { createRoleSessionSet, roleAgentSessionResumeMode, updateTaskRoleProviderRuntime, taskRoleControlTarget } from "../executor/agentExecutor.js";
|
|
19
|
-
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";
|
|
20
20
|
import { resolveEffectiveLaunch } from "../executor/effectiveLaunch.js";
|
|
21
|
-
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
22
|
-
import { agentExecutionComponentLabel } from "../agent/executionComponents.js";
|
|
23
|
-
import { agentRunConfigurationLabel, renderAgentRunConfiguration } from "../output/agentRunConfigurationPresentation.js";
|
|
24
|
-
import { formatTimestamp } from "../output/timePresentation.js";
|
|
25
|
-
import { renderRoleDetails, renderRoleLaunchComparison } from "../output/rolePresentation.js";
|
|
26
|
-
import { createTaskMessage, expandTaskMessageResult, recordTaskMessageControlOutcome, taskMessageAuthorLabel, taskMessageInputControlState, updateDraftTaskMessage, withSubmissionReceipt, TASK_SUBMISSION_INTENTS } from "../message/message.js";
|
|
27
|
-
import { assertDraftTaskExecutionFree, validateDraftWorkItemEdit } from "../task/draftPlan.js";
|
|
28
|
-
import { TASK_PLANNING_ENTERED_EVENT, decideSubmissionRouting, describeSubmissionFeedback, draftActivationState, draftHasEnteredPlanning, normalizeSubmissionIntent, sameSubmissionTarget } from "../task/taskSubmission.js";
|
|
29
|
-
import { recordTaskActivationRequestInTransaction, taskActivationOperationRef } from "../task/taskActivationService.js";
|
|
30
21
|
import { cancelInputRequest } from "../input/inputRequest.js";
|
|
31
22
|
import { retireExactActiveRun, terminalizeExactTaskRun, validateExactRunReviewRound } from "../lifecycle/exactRunTerminalization.js";
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
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";
|
|
35
33
|
import { buildDeltaRecheckDispatchContext, verifyDeltaRecheckDiff } from "../review/deltaRecheck.js";
|
|
36
34
|
import { isCompletedTaskReviewEvidence } from "../review/reviewAcceptance.js";
|
|
37
35
|
import { projectReviewerAvailability } from "../review/reviewerAvailability.js";
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
43
|
-
import {
|
|
44
|
-
import {
|
|
45
|
-
import {
|
|
46
|
-
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";
|
|
47
45
|
import { projectProviderContinuations } from "../runtime/runtimeContinuationProjection.js";
|
|
48
46
|
import { runtimeObservationFromTaskEvent } from "../runtime/runtimeObservation.js";
|
|
49
|
-
import {
|
|
50
|
-
import {
|
|
51
|
-
import {
|
|
52
|
-
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";
|
|
53
52
|
import { planningRuntimeCwd } from "../storage/homeLayout.js";
|
|
54
|
-
import {
|
|
55
|
-
import {
|
|
56
|
-
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";
|
|
57
65
|
import { assertWorkItemDependenciesCompleted as assertWorkItemDependencyGate, WorkItemDependencyGateError } from "../workItem/dependencyGate.js";
|
|
58
|
-
import {
|
|
59
|
-
import {
|
|
60
|
-
import { dispatchReviewSynthesis, selectedReviewSynthesisProducers } from "../execution/reviewMainRun.js";
|
|
61
|
-
import { synthesisSourceRunIds } from "../context/runContextPack.js";
|
|
62
|
-
import { projectWorkItemExecution } from "../execution/workItemExecutionProjection.js";
|
|
63
|
-
import { sameTaskFinalReviewContract, taskFinalReviewConfig, validateTaskFinalReviewContract } from "../review/taskFinalReviewContract.js";
|
|
64
|
-
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";
|
|
65
68
|
import { managedWorkspaceKey } from "../worktree/managedWorkspace.js";
|
|
69
|
+
import { runGrantCommand } from "./grantCommands.js";
|
|
66
70
|
import { hasAgentConfigOptions, hasNoRoleMutation, parseRoleOptions, patchRoleAgentBinding, roleOptionSpecs, roleProfilePatch } from "./roleConfiguration.js";
|
|
67
|
-
import { hasRoleLaunchContextOptions, validateConfiguredRoleSkills } from "./roleSkillValidation.js";
|
|
68
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";
|
|
69
76
|
import { runTaskContextCommand } from "./taskContextCommand.js";
|
|
70
|
-
import {
|
|
71
|
-
import { createProjectResources } from "../resources/projectResourceService.js";
|
|
72
|
-
import { isGitArtifactRefString, parseGitArtifactRef } from "../artifacts/gitArtifactRef.js";
|
|
73
|
-
import { runTaskNextActionCommand } from "./taskNextActionCommand.js";
|
|
74
|
-
import { runDeliveryGuardPreflight, withGuardWarnings } from "./deliveryGuardPreflight.js";
|
|
75
|
-
import { inspectTaskRoleRuntimeStatuses, renderTaskRoleRuntimeStatus, taskRoleActiveWorkLabel, taskRoleLastRunLabel, taskRoleNativeSessionLabel, taskRoleOpenInputLabel, taskRoleTmuxLabel, withTaskRoleHostObservation, taskRoleHostDiagnostic } from "./taskRoleRuntimeStatus.js";
|
|
77
|
+
import { taskBriefCommand, taskDecisionCommand, taskEventCommand, taskMilestoneCommand } from "./taskFactCommands.js";
|
|
76
78
|
import { openInputRequestCount, runTaskInputCommand } from "./taskInputCommands.js";
|
|
77
|
-
import {
|
|
79
|
+
import { runTaskNextActionCommand } from "./taskNextActionCommand.js";
|
|
80
|
+
import { runPublicationCommand } from "./taskPublicationCommands.js";
|
|
81
|
+
import { renderTaskRemoteDelivery, runTaskRemoteDeliveryCommand } from "./taskRemoteDeliveryCommand.js";
|
|
82
|
+
import { inspectTaskRoleRuntimeStatuses, renderTaskRoleRuntimeStatus, taskRoleActiveWorkLabel, taskRoleHostDiagnostic, taskRoleLastRunLabel, taskRoleNativeSessionLabel, taskRoleOpenInputLabel, taskRoleTmuxLabel, withTaskRoleHostObservation } from "./taskRoleRuntimeStatus.js";
|
|
83
|
+
import { readCommandText } from "./textInput.js";
|
|
78
84
|
import { runWorkflowCommand } from "./workflowCommands.js";
|
|
79
|
-
import { taskLocalActor as resolveTaskLocalActor, assertTaskDeliveryAuthority, assertTaskInputControlAuthority } from "./taskActor.js";
|
|
80
|
-
import { currentManagedRuntime, resolveManagedTaskReader } from "../runtime/managedCaller.js";
|
|
81
|
-
import { parseTaskCatalogOptions, readTaskCatalog, renderTaskCatalog, taskCatalogScope } from "../context/taskCatalog.js";
|
|
82
|
-
import { resolveMessageRecipient, messageContinuationBlocker } from "../message/messageContinuation.js";
|
|
83
|
-
import { findTaskInterrupt, reserveTaskInterrupt, taskInterruptReceipt, taskInterruptWasRejected } from "../message/taskInterrupt.js";
|
|
84
|
-
import { resolveTaskInputControl } from "../message/inputControlResolution.js";
|
|
85
|
-
import { enqueueOperatorEvent } from "../scheduler/operatorEvent.js";
|
|
86
|
-
import { queueLeaderWakeup } from "../scheduler/wakeupQueue.js";
|
|
87
|
-
import { renderWakeReason, wakeReason } from "../scheduler/wakeReason.js";
|
|
88
|
-
import { buildTaskOverview, parseTaskListOptions, renderTaskOverview } from "./taskOverviewCommand.js";
|
|
89
85
|
const LEADER_ROLE = "leader";
|
|
90
86
|
/** Exact Task-final identity or evidence changed between queueing and dispatch. */
|
|
91
87
|
export class TaskFinalReviewDispatchDriftError extends CliError {
|
|
@@ -254,7 +250,7 @@ export function parseTaskCompletionRequest(args, summaryOverride) {
|
|
|
254
250
|
* terminalization precondition before attempting completion. All blockers
|
|
255
251
|
* are reported in one error instead of one per attempt.
|
|
256
252
|
*/
|
|
257
|
-
export function preflightTaskCompletion(taskId, store, options = {},
|
|
253
|
+
export function preflightTaskCompletion(taskId, store, options = {}, _request = {}) {
|
|
258
254
|
const task = requireTask(store, taskId);
|
|
259
255
|
const actor = taskActor(store, options, task.id);
|
|
260
256
|
assertTaskDeliveryAuthority(store, options.environment, task.id);
|
|
@@ -269,7 +265,7 @@ export function preflightTaskCompletion(taskId, store, options = {}, request = {
|
|
|
269
265
|
// Integration write. Historical control-plane identity is audit evidence,
|
|
270
266
|
// not a capability that every compatible CLI must reproduce.
|
|
271
267
|
const taskFinalReviewContract = taskFinalReviewContractForMutation(store, task.id, options);
|
|
272
|
-
const activeTaskReview = store.listReviewRounds(task.id).some((round) => (
|
|
268
|
+
const activeTaskReview = store.listReviewRounds(task.id).some((round) => (round.scope === "task"
|
|
273
269
|
&& (round.status === "pending" || round.status === "running")));
|
|
274
270
|
// Issue 06: one shared readiness projection enumerates every blocker.
|
|
275
271
|
const readinessFacts = store.readCompletionReadinessFacts(task.id);
|
|
@@ -307,7 +303,7 @@ export function runTaskCommand(args, store, options = {}) {
|
|
|
307
303
|
const delivery = command === "complete"
|
|
308
304
|
|| (command === "work" && ["dispatch", "synthesize", "review", "accept"].includes(rest[0] ?? ""))
|
|
309
305
|
|| (command === "review" && !["list", "show"].includes(rest[0] ?? ""))
|
|
310
|
-
|| (
|
|
306
|
+
|| (command === "run" && rest[0] === "retry");
|
|
311
307
|
if (delivery && options.environment?.YUI_SESSION_SCOPE === "task"
|
|
312
308
|
&& options.environment.YUI_TASK_ID !== undefined) {
|
|
313
309
|
assertTaskDeliveryAuthority(store, options.environment, options.environment.YUI_TASK_ID);
|
|
@@ -341,12 +337,11 @@ export function runTaskCommand(args, store, options = {}) {
|
|
|
341
337
|
case "role": return taskRoleCommand(rest, store, options);
|
|
342
338
|
case "work": return taskWorkCommand(rest, store, options);
|
|
343
339
|
case "review": return taskReviewCommand(rest, store, options);
|
|
344
|
-
case "run":
|
|
345
340
|
case "run": return taskRunCommand(rest, store, options);
|
|
346
341
|
case "brief": return taskBriefCommand(rest, store, options);
|
|
347
342
|
case "decision": return taskDecisionCommand(rest, store, options);
|
|
348
343
|
case "milestone": return taskMilestoneCommand(rest, store, options);
|
|
349
|
-
case "event": return taskEventCommand(rest, store);
|
|
344
|
+
case "event": return taskEventCommand(rest, store, options);
|
|
350
345
|
case "continuation": return taskContinuationCommand(rest, store);
|
|
351
346
|
default:
|
|
352
347
|
throw usageError(command === undefined
|
|
@@ -470,9 +465,9 @@ function taskProjectCommand(args, store, options) {
|
|
|
470
465
|
}
|
|
471
466
|
return next;
|
|
472
467
|
});
|
|
473
|
-
notifyMailbox(options.runtime, taskMailbox(updated.id)
|
|
468
|
+
notifyMailbox(options.runtime, taskMailbox(updated.id));
|
|
474
469
|
if (updated.status === "active") {
|
|
475
|
-
notifyMailbox(options.runtime, leaderMailbox(updated.id)
|
|
470
|
+
notifyMailbox(options.runtime, leaderMailbox(updated.id));
|
|
476
471
|
}
|
|
477
472
|
return output(`Added Project to ${updated.id}\n`, { task: updated });
|
|
478
473
|
}
|
|
@@ -527,7 +522,7 @@ function updateTaskCommand(args, store, options) {
|
|
|
527
522
|
}, options);
|
|
528
523
|
return `Updated task ${result.id}\n`;
|
|
529
524
|
}
|
|
530
|
-
/** Shared domain transaction for structured capabilities and the
|
|
525
|
+
/** Shared domain transaction for structured capabilities and the CLI.
|
|
531
526
|
* Parsing text flags must not become an alternate business write path. */
|
|
532
527
|
export function updateTaskMetadataCommand(store, taskId, patch, options = {}) {
|
|
533
528
|
if (Object.keys(patch).length === 0)
|
|
@@ -549,7 +544,7 @@ export function updateTaskMetadataCommand(store, taskId, patch, options = {}) {
|
|
|
549
544
|
enqueueWork(tx, taskMailbox(updated.id), "task-updated", now, [taskRef(updated.id)]);
|
|
550
545
|
return updated;
|
|
551
546
|
});
|
|
552
|
-
notifyMailbox(options.runtime, taskMailbox(result.id)
|
|
547
|
+
notifyMailbox(options.runtime, taskMailbox(result.id));
|
|
553
548
|
return result;
|
|
554
549
|
}
|
|
555
550
|
function routeUserSubmission(tx, task, actor, body, intent, now, submissionKey, target, inputControl) {
|
|
@@ -632,8 +627,7 @@ function routeUserSubmission(tx, task, actor, body, intent, now, submissionKey,
|
|
|
632
627
|
requestId: `submit-${message.id}`,
|
|
633
628
|
actorId: identity.actorId,
|
|
634
629
|
authorityRef: identity.authorityRef,
|
|
635
|
-
environmentPlan: routing.environmentPlan
|
|
636
|
-
origin: "submit-develop"
|
|
630
|
+
environmentPlan: routing.environmentPlan
|
|
637
631
|
}, now);
|
|
638
632
|
activationRef = taskActivationOperationRef(task.id, `submit-${message.id}`);
|
|
639
633
|
enqueueWork(tx, taskMailbox(task.id), "activation-requested", now, [taskRef(task.id)]);
|
|
@@ -685,7 +679,7 @@ function replayKeyedSubmission(task, prior, kind, body, intent, target) {
|
|
|
685
679
|
const receipt = prior.submissionReceipt;
|
|
686
680
|
if (prior.kind !== kind
|
|
687
681
|
|| prior.body !== body
|
|
688
|
-
||
|
|
682
|
+
|| prior.intent !== intent
|
|
689
683
|
|| receipt === undefined
|
|
690
684
|
|| (target !== undefined && !sameSubmissionTarget(receipt.target, target))) {
|
|
691
685
|
throw new StorageConflictError(`Submission key ${prior.submissionKey} was already used for a different submission on ${task.id}/${prior.id}.`);
|
|
@@ -747,7 +741,7 @@ const SUBMISSION_ACTIVATION_LABEL = {
|
|
|
747
741
|
function renderSubmissionNextStep(step) {
|
|
748
742
|
switch (step.kind) {
|
|
749
743
|
case "activate-manually":
|
|
750
|
-
return `
|
|
744
|
+
return `request activation with "yui task activation request ${step.taskId} --request-id <id> --environment <plan>".`;
|
|
751
745
|
case "start-execution":
|
|
752
746
|
return `start execution with "yui task execution start ${step.taskId}", then submit develop again.`;
|
|
753
747
|
case "await-pending-activation":
|
|
@@ -801,7 +795,7 @@ export function submitOperatorMessage(body, taskId, store, options = {}, intent,
|
|
|
801
795
|
const routed = routeUserSubmission(tx, createdAgg.task, "operator", body, effectiveIntent, now, submissionKey, { kind: "create" });
|
|
802
796
|
return { ...routed, task: createdAgg.task, created: true };
|
|
803
797
|
});
|
|
804
|
-
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));
|
|
805
799
|
const header = result.created
|
|
806
800
|
? `Created Draft task ${result.task.id}: ${result.task.title}\n`
|
|
807
801
|
: "";
|
|
@@ -828,7 +822,7 @@ function createTaskCommand(args, store, options) {
|
|
|
828
822
|
projectBindings: parsed.projectBindings,
|
|
829
823
|
...(parsed.type === undefined ? {} : { type: parsed.type })
|
|
830
824
|
}, now, parsed.defaultProjectIds));
|
|
831
|
-
notifyMailbox(options.runtime, taskMailbox(created.task.id)
|
|
825
|
+
notifyMailbox(options.runtime, taskMailbox(created.task.id));
|
|
832
826
|
return output(`Created Draft task ${created.task.id}: ${created.task.title}\n`
|
|
833
827
|
+ `Assigned role: ${created.leader.name}\n`
|
|
834
828
|
+ `Type: ${created.task.type ?? "unspecified"}\n`
|
|
@@ -909,18 +903,8 @@ function createTaskAggregate(store, title, metadata, now, defaultProjectIds = []
|
|
|
909
903
|
return { task, leader };
|
|
910
904
|
}
|
|
911
905
|
function listTaskCommand(args, store, environment = {}) {
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
return output(renderTaskCatalog(result), result);
|
|
915
|
-
}
|
|
916
|
-
const taskId = taskCatalogScope(store, environment);
|
|
917
|
-
const options = parseTaskListOptions(args);
|
|
918
|
-
const snapshot = store.transaction((reader) => ({
|
|
919
|
-
result: buildTaskOverview(reader, options, new Date(), taskId),
|
|
920
|
-
timeZone: reader.getConfig().timeZone
|
|
921
|
-
}));
|
|
922
|
-
const rendered = renderTaskOverview(snapshot.result, options, snapshot.timeZone, defaultTableWidth());
|
|
923
|
-
return output(rendered, snapshot.result);
|
|
906
|
+
const result = readTaskCatalog(store, parseTaskCatalogOptions(args), environment);
|
|
907
|
+
return output(renderTaskCatalog(result), result);
|
|
924
908
|
}
|
|
925
909
|
function showTaskCommand(args, store, currentTaskCandidate) {
|
|
926
910
|
const [taskId] = args;
|
|
@@ -1053,8 +1037,8 @@ function activateTaskCommand(args, store, options) {
|
|
|
1053
1037
|
throw usageError(`Task ${task.id} activation requires atomic workspace adoption through the CLI preflight.`);
|
|
1054
1038
|
});
|
|
1055
1039
|
if (result.changed) {
|
|
1056
|
-
notifyMailbox(options.runtime, taskMailbox(result.task.id)
|
|
1057
|
-
notifyMailbox(options.runtime, leaderMailbox(result.task.id)
|
|
1040
|
+
notifyMailbox(options.runtime, taskMailbox(result.task.id));
|
|
1041
|
+
notifyMailbox(options.runtime, leaderMailbox(result.task.id));
|
|
1058
1042
|
}
|
|
1059
1043
|
return result.changed
|
|
1060
1044
|
? `Activated task ${result.task.id}\n`
|
|
@@ -1188,7 +1172,7 @@ function completeTaskCommand(args, store, options) {
|
|
|
1188
1172
|
};
|
|
1189
1173
|
});
|
|
1190
1174
|
if (result.changed) {
|
|
1191
|
-
notifyMailbox(options.runtime, { kind: "operator" }
|
|
1175
|
+
notifyMailbox(options.runtime, { kind: "operator" });
|
|
1192
1176
|
}
|
|
1193
1177
|
if (result.finalReview !== undefined) {
|
|
1194
1178
|
const status = result.finalReview.status === "pending"
|
|
@@ -1249,9 +1233,9 @@ function reopenTaskCommand(args, store, options) {
|
|
|
1249
1233
|
return { task: active, changed: true, wakeLeader: actor !== "leader" };
|
|
1250
1234
|
});
|
|
1251
1235
|
if (result.changed) {
|
|
1252
|
-
notifyMailbox(options.runtime, taskMailbox(result.task.id)
|
|
1236
|
+
notifyMailbox(options.runtime, taskMailbox(result.task.id));
|
|
1253
1237
|
if (result.wakeLeader) {
|
|
1254
|
-
notifyMailbox(options.runtime, leaderMailbox(result.task.id)
|
|
1238
|
+
notifyMailbox(options.runtime, leaderMailbox(result.task.id));
|
|
1255
1239
|
}
|
|
1256
1240
|
}
|
|
1257
1241
|
return result.changed
|
|
@@ -1342,7 +1326,7 @@ function archiveTaskCommand(args, store, options) {
|
|
|
1342
1326
|
return { task: archived, changed: true };
|
|
1343
1327
|
});
|
|
1344
1328
|
if (result.changed && !request.force)
|
|
1345
|
-
options.runtime?.
|
|
1329
|
+
options.runtime?.notifyMailboxChanged({ kind: "task", taskId: result.task.id });
|
|
1346
1330
|
const diagnostics = taskArchiveDiagnostics(store, result.task);
|
|
1347
1331
|
return output((result.changed
|
|
1348
1332
|
? `Archived task ${result.task.id}\n`
|
|
@@ -1375,8 +1359,8 @@ function cancelTaskCommand(args, store, options) {
|
|
|
1375
1359
|
// AgentRuns, inputs, WorkItems and their results remain independently readable.
|
|
1376
1360
|
return cancelled;
|
|
1377
1361
|
});
|
|
1378
|
-
options.runtime?.
|
|
1379
|
-
notifyMailbox(options.runtime, { kind: "operator" }
|
|
1362
|
+
options.runtime?.notifyMailboxChanged({ kind: "task", taskId: result.id });
|
|
1363
|
+
notifyMailbox(options.runtime, { kind: "operator" });
|
|
1380
1364
|
return output(`Cancelled task ${result.id}\n`, { task: result });
|
|
1381
1365
|
}
|
|
1382
1366
|
/** Historical evidence only; these snapshots are never dispatch input. */
|
|
@@ -1477,8 +1461,8 @@ function retireTaskCommand(args, store, options) {
|
|
|
1477
1461
|
return { task: retired, changed: true };
|
|
1478
1462
|
});
|
|
1479
1463
|
if (result.changed) {
|
|
1480
|
-
options.runtime?.
|
|
1481
|
-
notifyMailbox(options.runtime, { kind: "operator" }
|
|
1464
|
+
options.runtime?.notifyMailboxChanged({ kind: "task", taskId: result.task.id });
|
|
1465
|
+
notifyMailbox(options.runtime, { kind: "operator" });
|
|
1482
1466
|
}
|
|
1483
1467
|
return output(result.changed
|
|
1484
1468
|
? `Retired task ${result.task.id}\n`
|
|
@@ -1590,7 +1574,7 @@ function taskMessageCommand(args, store, options) {
|
|
|
1590
1574
|
enqueueWork(tx, taskMailbox(ref.taskId), "message-continuation", now, [messageRef(ref.taskId, current.id)]);
|
|
1591
1575
|
return updated;
|
|
1592
1576
|
});
|
|
1593
|
-
notifyMailbox(options.runtime, taskMailbox(ref.taskId)
|
|
1577
|
+
notifyMailbox(options.runtime, taskMailbox(ref.taskId));
|
|
1594
1578
|
return output(`Handed off Message ${ref.localId} to ${message.recipient.roleName}.\n`, message);
|
|
1595
1579
|
}
|
|
1596
1580
|
if (command === "show") {
|
|
@@ -1608,22 +1592,11 @@ function taskMessageCommand(args, store, options) {
|
|
|
1608
1592
|
return { kind: "output", output: `${JSON.stringify(expanded, null, 2)}\n`, data: expanded };
|
|
1609
1593
|
}
|
|
1610
1594
|
if (command === "send") {
|
|
1611
|
-
const usage = "Task message send usage: yui task message send <id> (<body>|--body-file <path|->) [--intent record|discuss|develop] [--request-id <key>] [--
|
|
1612
|
-
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);
|
|
1613
1597
|
if (parsed.positionals.length < 1 || parsed.positionals.length > 2)
|
|
1614
1598
|
throw usageError(usage);
|
|
1615
1599
|
const body = readCommandText(parsed.positionals[1], parsed.options.get("--body-file"), "--body", usage);
|
|
1616
|
-
const wakePolicyRaw = parsed.options.get("--wake-policy");
|
|
1617
|
-
let wakePolicy;
|
|
1618
|
-
if (wakePolicyRaw === undefined) {
|
|
1619
|
-
wakePolicy = undefined;
|
|
1620
|
-
}
|
|
1621
|
-
else if (wakePolicyRaw === "leader" || wakePolicyRaw === "none") {
|
|
1622
|
-
wakePolicy = wakePolicyRaw;
|
|
1623
|
-
}
|
|
1624
|
-
else {
|
|
1625
|
-
throw usageError(`--wake-policy must be 'leader' or 'none': ${wakePolicyRaw}.`);
|
|
1626
|
-
}
|
|
1627
1600
|
const intent = parseSubmissionIntentOption(parsed.options.get("--intent"), usage);
|
|
1628
1601
|
const submissionKey = parsed.options.get("--request-id");
|
|
1629
1602
|
if (submissionKey !== undefined && submissionKey.trim().length === 0) {
|
|
@@ -1634,7 +1607,7 @@ function taskMessageCommand(args, store, options) {
|
|
|
1634
1607
|
const reviewRoundId = parsed.options.get("--review-round");
|
|
1635
1608
|
if (recipientRole === undefined && (workItemId !== undefined || reviewRoundId !== undefined))
|
|
1636
1609
|
throw usageError("--to is required for scoped Message delivery.");
|
|
1637
|
-
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);
|
|
1638
1611
|
// A user/operator submission returns the unified §2.5 feedback; render each
|
|
1639
1612
|
// facet on its own line and expose the structure to non-text callers.
|
|
1640
1613
|
if (result.feedback !== undefined) {
|
|
@@ -1713,6 +1686,13 @@ function taskMessageCommand(args, store, options) {
|
|
|
1713
1686
|
* reusing the requestId with different content is a conflict, never a second
|
|
1714
1687
|
* input (decision-3 §5/§6).
|
|
1715
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
|
+
}
|
|
1716
1696
|
function queueTaskMessage(args, store, options) {
|
|
1717
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>].";
|
|
1718
1698
|
const parsed = parseTail(args, new Set(["--body-file", "--request-id", "--to", "--work-item", "--review-round"]), usage);
|
|
@@ -1723,10 +1703,16 @@ function queueTaskMessage(args, store, options) {
|
|
|
1723
1703
|
const recipientRole = parsed.options.get("--to");
|
|
1724
1704
|
const workItemId = parsed.options.get("--work-item");
|
|
1725
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;
|
|
1726
1712
|
if (recipientRole === undefined && (workItemId !== undefined || reviewRoundId !== undefined)) {
|
|
1727
1713
|
throw usageError("--to is required for scoped Message delivery.");
|
|
1728
1714
|
}
|
|
1729
|
-
const result = sendTaskMessageCommand(store,
|
|
1715
|
+
const result = sendTaskMessageCommand(store, taskId, body, options, recipientRole === undefined ? undefined : { roleName: recipientRole, workItemId, reviewRoundId }, undefined, undefined, { action: "queue", requestId });
|
|
1730
1716
|
const reason = result.message.continuation?.notDeliveredReason;
|
|
1731
1717
|
const delivery = result.idempotentReplay ? { state: "idempotent-replay" }
|
|
1732
1718
|
: reason !== undefined ? { state: "not-delivered", reason }
|
|
@@ -1757,6 +1743,13 @@ function steerTaskMessage(args, store, options) {
|
|
|
1757
1743
|
if (recipientRole === undefined) {
|
|
1758
1744
|
throw usageError("steer requires --to <role>; it targets that Role's exact current Turn.");
|
|
1759
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;
|
|
1760
1753
|
// decision-3 §9: a Task Leader's current native turn (including a Draft's
|
|
1761
1754
|
// planning turn) is a real turn but NOT an implicit AgentRun, so steering it
|
|
1762
1755
|
// never establishes a Worker-style Assignment. It is persisted as an ordinary
|
|
@@ -1768,8 +1761,8 @@ function steerTaskMessage(args, store, options) {
|
|
|
1768
1761
|
throw usageError("Steering the Leader targets its current turn directly; it takes no --work-item/--review-round Assignment.");
|
|
1769
1762
|
}
|
|
1770
1763
|
const result = leaderTarget
|
|
1771
|
-
? sendTaskMessageCommand(store,
|
|
1772
|
-
: 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 });
|
|
1773
1766
|
const roleName = result.message.recipient?.roleName ?? recipientRole;
|
|
1774
1767
|
if (result.idempotentReplay) {
|
|
1775
1768
|
// A replay is a later reader (another CLI invocation): surface the durable
|
|
@@ -1864,12 +1857,9 @@ function steerInputDelivery(taskId, messageId, roleName, body) {
|
|
|
1864
1857
|
* execution is decided by the Controller from the Task's own status, so this
|
|
1865
1858
|
* function never names a purpose and no second planning path exists.
|
|
1866
1859
|
*/
|
|
1867
|
-
export function sendTaskMessageCommand(store, taskId, body,
|
|
1860
|
+
export function sendTaskMessageCommand(store, taskId, body, options = {}, recipient, intent, submissionKey, inputControl) {
|
|
1868
1861
|
if (!body.trim())
|
|
1869
1862
|
throw usageError("Message body is required.");
|
|
1870
|
-
if (recipient !== undefined && wakePolicy !== undefined) {
|
|
1871
|
-
throw usageError("--wake-policy applies only to unaddressed Leader Messages; an owner-directed Message uses its exact continuation boundary.");
|
|
1872
|
-
}
|
|
1873
1863
|
if (recipient !== undefined && intent !== undefined) {
|
|
1874
1864
|
throw usageError("A submission intent applies only to unaddressed Leader Messages; an owner-directed Message uses its exact continuation boundary.");
|
|
1875
1865
|
}
|
|
@@ -1924,7 +1914,7 @@ export function sendTaskMessageCommand(store, taskId, body, wakePolicy, options
|
|
|
1924
1914
|
// owner-directed continuations keep their exact existing boundary and never
|
|
1925
1915
|
// gain develop authority (task-32 §2.5).
|
|
1926
1916
|
if (recipient === undefined && (actor === "user" || actor === "operator")) {
|
|
1927
|
-
const effectiveIntent = normalizeSubmissionIntent(intent
|
|
1917
|
+
const effectiveIntent = inputControl?.action === "steer" ? "record" : normalizeSubmissionIntent(intent);
|
|
1928
1918
|
const routed = routeUserSubmission(tx, task, actor, body, effectiveIntent, now, submissionKey, { kind: "task", taskId: task.id }, inputControl);
|
|
1929
1919
|
return {
|
|
1930
1920
|
task: routed.task, message: routed.message, actor,
|
|
@@ -1979,11 +1969,11 @@ export function sendTaskMessageCommand(store, taskId, body, wakePolicy, options
|
|
|
1979
1969
|
if (result.idempotentReplay)
|
|
1980
1970
|
return result;
|
|
1981
1971
|
if (recipient !== undefined) {
|
|
1982
|
-
notifyMailbox(options.runtime, taskMailbox(result.task.id)
|
|
1972
|
+
notifyMailbox(options.runtime, taskMailbox(result.task.id));
|
|
1983
1973
|
}
|
|
1984
1974
|
else if (result.actor !== "leader") {
|
|
1985
1975
|
notifyMailbox(options.runtime, result.queuedForLeader
|
|
1986
|
-
? leaderMailbox(result.task.id) : taskMailbox(result.task.id)
|
|
1976
|
+
? leaderMailbox(result.task.id) : taskMailbox(result.task.id));
|
|
1987
1977
|
}
|
|
1988
1978
|
return result;
|
|
1989
1979
|
}
|
|
@@ -2004,19 +1994,11 @@ export function leaderWakingTaskStatus(status) {
|
|
|
2004
1994
|
return status === "active" || status === "draft";
|
|
2005
1995
|
}
|
|
2006
1996
|
function updateMessage(args, store, options) {
|
|
2007
|
-
const usage = "Task message update usage: yui task message update <task>/<message> (<body>|--body-file <path|->)
|
|
2008
|
-
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);
|
|
2009
1999
|
if (parsed.positionals.length < 1 || parsed.positionals.length > 2)
|
|
2010
2000
|
throw usageError(usage);
|
|
2011
2001
|
const body = readCommandText(parsed.positionals[1], parsed.options.get("--body-file"), "--body", usage);
|
|
2012
|
-
const wakePolicyRaw = parsed.options.get("--wake-policy");
|
|
2013
|
-
const wakePolicy = wakePolicyRaw === undefined
|
|
2014
|
-
? undefined
|
|
2015
|
-
: wakePolicyRaw === "leader" || wakePolicyRaw === "none"
|
|
2016
|
-
? wakePolicyRaw
|
|
2017
|
-
: (() => {
|
|
2018
|
-
throw usageError(`--wake-policy must be 'leader' or 'none': ${wakePolicyRaw}.`);
|
|
2019
|
-
})();
|
|
2020
2002
|
const reference = taskRecordReference(parsed.positionals[0], "message", "Message reference", options);
|
|
2021
2003
|
const now = clock(options);
|
|
2022
2004
|
const result = store.transaction((tx) => {
|
|
@@ -2037,24 +2019,42 @@ function updateMessage(args, store, options) {
|
|
|
2037
2019
|
throw usageError(`Task Message is retired: ${task.id}/${message.id}.`);
|
|
2038
2020
|
}
|
|
2039
2021
|
const updated = updateDraftTaskMessage(message, {
|
|
2040
|
-
body
|
|
2041
|
-
...(wakePolicy === undefined ? {} : { wakePolicy })
|
|
2022
|
+
body
|
|
2042
2023
|
});
|
|
2024
|
+
if (updated === message)
|
|
2025
|
+
return { task, message, queuedForLeader: false, changed: false };
|
|
2043
2026
|
tx.updateMessage(task.id, updated);
|
|
2044
2027
|
recordTaskEvent(tx, task.id, "message.updated", {
|
|
2045
2028
|
messageId: updated.id,
|
|
2046
|
-
updatedBy: actor
|
|
2047
|
-
...(wakePolicy === undefined ? {} : { wakePolicy })
|
|
2029
|
+
updatedBy: actor
|
|
2048
2030
|
}, now);
|
|
2049
|
-
|
|
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
|
+
}
|
|
2050
2048
|
if (queuedForLeader) {
|
|
2051
2049
|
enqueueWork(tx, leaderMailbox(task.id), actor === "operator" ? "operator-input" : "user-message", now, [messageRef(task.id, updated.id)], { source: actor });
|
|
2052
2050
|
}
|
|
2053
|
-
return { task, message: updated, queuedForLeader };
|
|
2051
|
+
return { task, message: updated, queuedForLeader, changed: true };
|
|
2054
2052
|
});
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
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`, {
|
|
2058
2058
|
message: result.message
|
|
2059
2059
|
});
|
|
2060
2060
|
}
|
|
@@ -2101,32 +2101,58 @@ function retireMessage(args, store, options) {
|
|
|
2101
2101
|
return { task, message, changed: true };
|
|
2102
2102
|
});
|
|
2103
2103
|
if (result.changed)
|
|
2104
|
-
options.runtime?.
|
|
2104
|
+
options.runtime?.notifyMailboxChanged({ kind: "task", taskId: result.task.id });
|
|
2105
2105
|
return `Retired Task Message ${result.task.id}/${result.message.id}\n`;
|
|
2106
2106
|
}
|
|
2107
|
-
/**
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
function taskWakeForceCommand(args, store, options) {
|
|
2113
|
-
const usage = "Task wake usage: yui task wake <id> --force --reason <text>.";
|
|
2114
|
-
const parsed = parseTail(args, new Set(["--reason"]), usage, new Set(["--force"]));
|
|
2115
|
-
exactPositionals(parsed.positionals, 1, usage);
|
|
2116
|
-
if (!parsed.options.has("--force")) {
|
|
2117
|
-
throw usageError("--force is required to wake a Task.", usage);
|
|
2118
|
-
}
|
|
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);
|
|
2119
2112
|
const reason = requiredOption(parsed.options, "--reason");
|
|
2120
2113
|
const now = clock(options);
|
|
2121
|
-
const
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
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" };
|
|
2127
2153
|
});
|
|
2128
|
-
notifyMailbox(options.runtime, leaderMailbox(
|
|
2129
|
-
return `
|
|
2154
|
+
notifyMailbox(options.runtime, leaderMailbox(result.taskId));
|
|
2155
|
+
return output(`Queued rejected ${result.wakeId} for retry; original input was preserved.\n`, result);
|
|
2130
2156
|
}
|
|
2131
2157
|
function taskRoleCommand(args, store, options) {
|
|
2132
2158
|
const [command, ...rest] = args;
|
|
@@ -2183,9 +2209,15 @@ function interruptTaskRole(args, store, options) {
|
|
|
2183
2209
|
const expectedTarget = requiredOption(parsed.options, "--expected-target");
|
|
2184
2210
|
const thenMessageRef = optionalNonEmptyOption(parsed.options, "--then-message");
|
|
2185
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;
|
|
2186
2218
|
const now = clock(options);
|
|
2187
|
-
const task = requireTask(store,
|
|
2188
|
-
const role = requireRole(store, task.id,
|
|
2219
|
+
const task = requireTask(store, taskId);
|
|
2220
|
+
const role = requireRole(store, task.id, input.role);
|
|
2189
2221
|
const fingerprint = createHash("sha256").update(JSON.stringify({
|
|
2190
2222
|
roleName: role.name, expectedTarget, thenMessageRef: thenMessageRef ?? null
|
|
2191
2223
|
})).digest("hex");
|
|
@@ -2362,7 +2394,7 @@ function registerInterruptThen(store, taskId, roleName, thenMessageRef, target,
|
|
|
2362
2394
|
// AgentRun's terminal — no extra wake is needed. A no-Run Leader turn owns no
|
|
2363
2395
|
// AgentRun and is never surfaced by that push path; it is delivered only by
|
|
2364
2396
|
// being woken to read its own Context. The interrupt itself (and, when the
|
|
2365
|
-
// handoff reuses a Leader self-steer
|
|
2397
|
+
// handoff reuses a Leader self-steer with record intent) enqueues no wake, so
|
|
2366
2398
|
// without this the claim would be structurally unreleasable: listPendingWakeups
|
|
2367
2399
|
// never selects the Task and the busy-gated leader mailbox is never consulted.
|
|
2368
2400
|
// Enqueue that wake now, keyed to the claimed Message so an idempotent repeat of
|
|
@@ -2483,7 +2515,7 @@ function taskRoleSessionCommand(args, store, options) {
|
|
|
2483
2515
|
enqueueWork(tx, target, RUNTIME_SESSION_REPLACE_REQUIRED_REASON, now, [{ type: "task", id: task.id }]);
|
|
2484
2516
|
return { taskId: task.id, roleName: role.name, target, alreadyRequested: false };
|
|
2485
2517
|
});
|
|
2486
|
-
notifyMailbox(options.runtime, result.target
|
|
2518
|
+
notifyMailbox(options.runtime, result.target);
|
|
2487
2519
|
return output(`Requested Session replacement for ${result.taskId}/${result.roleName}. `
|
|
2488
2520
|
+ "Yui will stop the old execution, retain its history and workspaces, and select a fresh Session. "
|
|
2489
2521
|
+ "If replacing your own Session, end this turn; the successor reads durable Task context.\n", result);
|
|
@@ -2602,7 +2634,7 @@ function addTaskRole(args, store, options) {
|
|
|
2602
2634
|
}, now);
|
|
2603
2635
|
return { role: created, binding };
|
|
2604
2636
|
});
|
|
2605
|
-
notifyMailbox(options.runtime, taskMailbox(result.role.taskId)
|
|
2637
|
+
notifyMailbox(options.runtime, taskMailbox(result.role.taskId));
|
|
2606
2638
|
const profileId = parsed.one("--profile");
|
|
2607
2639
|
const runtimeSource = profileId !== undefined
|
|
2608
2640
|
? `Agent Profile ${profileId}`
|
|
@@ -2742,7 +2774,7 @@ function updateTaskRole(args, store, options) {
|
|
|
2742
2774
|
});
|
|
2743
2775
|
return next;
|
|
2744
2776
|
});
|
|
2745
|
-
notifyMailbox(options.runtime, taskMailbox(updated.taskId)
|
|
2777
|
+
notifyMailbox(options.runtime, taskMailbox(updated.taskId));
|
|
2746
2778
|
const sessions = store.getTaskRoleSessionSet(updated.taskId, updated.name);
|
|
2747
2779
|
return output(renderRoleDetails(`Updated Task Role: ${updated.name}`, updated, {
|
|
2748
2780
|
kind: "task",
|
|
@@ -2784,7 +2816,7 @@ function removeTaskRole(args, store, options) {
|
|
|
2784
2816
|
enqueueWork(tx, taskMailbox(task.id), "role-removed", now, [taskRef(task.id)]);
|
|
2785
2817
|
return role;
|
|
2786
2818
|
});
|
|
2787
|
-
notifyMailbox(options.runtime, taskMailbox(removed.taskId)
|
|
2819
|
+
notifyMailbox(options.runtime, taskMailbox(removed.taskId));
|
|
2788
2820
|
return `Removed role ${removed.name} from ${removed.taskId}\n`;
|
|
2789
2821
|
}
|
|
2790
2822
|
function bindTaskRole(args, store, options) {
|
|
@@ -2843,7 +2875,7 @@ function bindTaskRole(args, store, options) {
|
|
|
2843
2875
|
}, now);
|
|
2844
2876
|
return { role: switched.role, mode: switched.mode };
|
|
2845
2877
|
});
|
|
2846
|
-
notifyMailbox(options.runtime, taskMailbox(result.role.taskId)
|
|
2878
|
+
notifyMailbox(options.runtime, taskMailbox(result.role.taskId));
|
|
2847
2879
|
return `Bound ${result.role.taskId}/${result.role.name} to ${result.role.activeAgentId} (${result.mode})\n`;
|
|
2848
2880
|
}
|
|
2849
2881
|
function unbindTaskRole(args, store, options) {
|
|
@@ -3113,7 +3145,7 @@ function editWork(args, store, options) {
|
|
|
3113
3145
|
}
|
|
3114
3146
|
return { task, item: updated };
|
|
3115
3147
|
});
|
|
3116
|
-
options.runtime?.
|
|
3148
|
+
options.runtime?.notifyMailboxChanged({ kind: "task", taskId: result.task.id });
|
|
3117
3149
|
return output(`Edited Work Item ${result.task.id}/${result.item.id}\n`, {
|
|
3118
3150
|
workItem: result.item
|
|
3119
3151
|
});
|
|
@@ -3153,15 +3185,6 @@ function createWork(args, store, options) {
|
|
|
3153
3185
|
if (new Set(baseRefs.map(({ projectId }) => projectId)).size !== baseRefs.length) {
|
|
3154
3186
|
throw usageError("Each Work Item Project may specify at most one base ref.");
|
|
3155
3187
|
}
|
|
3156
|
-
const guard = runDeliveryGuardPreflight(tx, task.id, {
|
|
3157
|
-
kind: "create-work-item",
|
|
3158
|
-
scope: {
|
|
3159
|
-
title: parsed.positionals[1],
|
|
3160
|
-
objective: parsed.objective ?? parsed.positionals[1],
|
|
3161
|
-
acceptance: parsed.acceptance,
|
|
3162
|
-
writeProjectIds
|
|
3163
|
-
}
|
|
3164
|
-
}, { environment: options.environment, budget: true });
|
|
3165
3188
|
const created = createWorkItem(tx.nextWorkItemId(task.id), task.id, {
|
|
3166
3189
|
title: parsed.positionals[1],
|
|
3167
3190
|
objective: parsed.objective ?? parsed.positionals[1],
|
|
@@ -3173,10 +3196,10 @@ function createWork(args, store, options) {
|
|
|
3173
3196
|
}, now);
|
|
3174
3197
|
tx.saveWorkItem(task.id, created);
|
|
3175
3198
|
enqueueWork(tx, taskMailbox(task.id), "work-created", now, [workItemRef(task.id, created.id)]);
|
|
3176
|
-
return
|
|
3199
|
+
return created;
|
|
3177
3200
|
});
|
|
3178
|
-
notifyMailbox(options.runtime, taskMailbox(item.
|
|
3179
|
-
return output(
|
|
3201
|
+
notifyMailbox(options.runtime, taskMailbox(item.taskId));
|
|
3202
|
+
return output(`Created work item ${item.id} for ${item.taskId}\n`, { workItem: item });
|
|
3180
3203
|
}
|
|
3181
3204
|
function updateWorkScope(args, store, options) {
|
|
3182
3205
|
const usage = "Task work scope usage: yui task work scope <task>/<work> [--project <project> ...].";
|
|
@@ -3213,7 +3236,7 @@ function updateWorkScope(args, store, options) {
|
|
|
3213
3236
|
return { item: next, changed: true };
|
|
3214
3237
|
});
|
|
3215
3238
|
if (updated.changed) {
|
|
3216
|
-
notifyMailbox(options.runtime, taskMailbox(updated.item.taskId)
|
|
3239
|
+
notifyMailbox(options.runtime, taskMailbox(updated.item.taskId));
|
|
3217
3240
|
}
|
|
3218
3241
|
return `${updated.changed ? "Updated" : "Unchanged"} Work Item Project scope ${updated.item.id}: ${updated.item.writeProjectIds.join(", ") || "read-only"}\n`;
|
|
3219
3242
|
}
|
|
@@ -3380,7 +3403,7 @@ function updateWork(args, store, options) {
|
|
|
3380
3403
|
reviewTrigger: candidatePolicy?.trigger ?? null
|
|
3381
3404
|
};
|
|
3382
3405
|
});
|
|
3383
|
-
notifyMailbox(options.runtime, taskMailbox(result.item.taskId)
|
|
3406
|
+
notifyMailbox(options.runtime, taskMailbox(result.item.taskId));
|
|
3384
3407
|
if ((result.item.status === "open" && result.item.candidates.length > 0) && status === "completed") {
|
|
3385
3408
|
const failure = result.reviewDispatch?.round.status === "failed"
|
|
3386
3409
|
? `Review could not start: ${result.reviewDispatch.round.failure?.message ?? result.reviewDispatch.round.id}\n`
|
|
@@ -3611,7 +3634,7 @@ function dispatchWork(args, store, options) {
|
|
|
3611
3634
|
return { kind: "replicated", runs };
|
|
3612
3635
|
});
|
|
3613
3636
|
for (const run of dispatch.runs) {
|
|
3614
|
-
notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName)
|
|
3637
|
+
notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName));
|
|
3615
3638
|
}
|
|
3616
3639
|
return dispatch.kind === "direct"
|
|
3617
3640
|
? `Direct WorkItem AgentRun queued as ${dispatch.runs[0].id}\n`
|
|
@@ -3855,7 +3878,7 @@ function retireWork(args, store, options) {
|
|
|
3855
3878
|
}
|
|
3856
3879
|
return next;
|
|
3857
3880
|
});
|
|
3858
|
-
options.runtime?.
|
|
3881
|
+
options.runtime?.notifyMailboxChanged({ kind: "task", taskId: retired.taskId });
|
|
3859
3882
|
return output(`Retired Work Item ${retired.id}\n`, { workItem: retired });
|
|
3860
3883
|
}
|
|
3861
3884
|
function listWork(args, store) {
|
|
@@ -4061,7 +4084,7 @@ function synthesizeRuns(args, kind, store, options) {
|
|
|
4061
4084
|
}, now);
|
|
4062
4085
|
return created;
|
|
4063
4086
|
});
|
|
4064
|
-
notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName)
|
|
4087
|
+
notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName));
|
|
4065
4088
|
return output(`Dispatched synthesis AgentRun ${run.taskId}/${run.id}\n`, { run });
|
|
4066
4089
|
}
|
|
4067
4090
|
function taskReviewCommand(args, store, options) {
|
|
@@ -4111,7 +4134,7 @@ function requestTaskReviewRound(args, store, options) {
|
|
|
4111
4134
|
throw usageError(producerCollision);
|
|
4112
4135
|
}
|
|
4113
4136
|
const taskRounds = reviewRoundsByIdentity(tx.listReviewRounds(task.id))
|
|
4114
|
-
.filter((entry) =>
|
|
4137
|
+
.filter((entry) => entry.scope === "task");
|
|
4115
4138
|
const exact = taskRounds.filter((entry) => (entry.reviewerRoleName === reviewerRoleName
|
|
4116
4139
|
&& (deltaRecheckRequested
|
|
4117
4140
|
? entry.deltaRecheck !== undefined
|
|
@@ -4305,7 +4328,7 @@ function retryFailedTaskReviewRound(args, store, options) {
|
|
|
4305
4328
|
if (task.status !== "active")
|
|
4306
4329
|
throw usageError(`Task is not active: ${task.id}.`);
|
|
4307
4330
|
const requestedBy = taskActor(tx, options, task.id);
|
|
4308
|
-
if (
|
|
4331
|
+
if (round.scope !== "task") {
|
|
4309
4332
|
throw usageError(`ReviewRound ${round.id} is not a failed Task-final ReviewRound.`);
|
|
4310
4333
|
}
|
|
4311
4334
|
if (round.reviewerRunId !== undefined) {
|
|
@@ -4392,11 +4415,11 @@ function taskRunCommand(args, store, options) {
|
|
|
4392
4415
|
if (command === "retire")
|
|
4393
4416
|
return retireRun(rest, store, options);
|
|
4394
4417
|
throw usageError(command === undefined
|
|
4395
|
-
? "Task
|
|
4418
|
+
? "Task run command is required."
|
|
4396
4419
|
: `Unknown command: task run ${command}`);
|
|
4397
4420
|
}
|
|
4398
4421
|
function settleRun(args, store, options) {
|
|
4399
|
-
exactPositionals(args, 1, "Task
|
|
4422
|
+
exactPositionals(args, 1, "Task run settle usage: yui task run settle <task>/<run>.");
|
|
4400
4423
|
const previous = store.transaction((tx) => requireRun(tx, args[0], options));
|
|
4401
4424
|
if (previous.purpose === "review") {
|
|
4402
4425
|
if (previous.executionGroupId !== undefined
|
|
@@ -4477,7 +4500,7 @@ function settleFailedReviewExecutionLaneRun(previous, store, options) {
|
|
|
4477
4500
|
};
|
|
4478
4501
|
});
|
|
4479
4502
|
for (const run of result.mainRuns) {
|
|
4480
|
-
notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName)
|
|
4503
|
+
notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName));
|
|
4481
4504
|
}
|
|
4482
4505
|
return output(result.changed
|
|
4483
4506
|
? `Settled failed Review Producer Lane from AgentRun ${result.run.id}\n`
|
|
@@ -4552,7 +4575,7 @@ function settleFailedExecutionLaneRun(previous, store, options) {
|
|
|
4552
4575
|
};
|
|
4553
4576
|
});
|
|
4554
4577
|
for (const run of result.mainRuns) {
|
|
4555
|
-
notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName)
|
|
4578
|
+
notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName));
|
|
4556
4579
|
}
|
|
4557
4580
|
return output(result.changed
|
|
4558
4581
|
? `Settled failed Execution Lane from AgentRun ${result.run.id}\n`
|
|
@@ -4563,7 +4586,7 @@ function settleFailedExecutionLaneRun(previous, store, options) {
|
|
|
4563
4586
|
});
|
|
4564
4587
|
}
|
|
4565
4588
|
function retireRun(args, store, options) {
|
|
4566
|
-
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>].";
|
|
4567
4590
|
const parsed = parseTail(args, new Set([
|
|
4568
4591
|
"--reason",
|
|
4569
4592
|
"--expected-progress-at",
|
|
@@ -4647,7 +4670,7 @@ function retireRun(args, store, options) {
|
|
|
4647
4670
|
return { task, run: run, changed: true };
|
|
4648
4671
|
});
|
|
4649
4672
|
if (result.changed)
|
|
4650
|
-
options.runtime?.
|
|
4673
|
+
options.runtime?.notifyMailboxChanged({ kind: "task", taskId: result.task.id });
|
|
4651
4674
|
return output(`Retired AgentRun ${result.task.id}/${result.run.id}\n`, {
|
|
4652
4675
|
run: result.run,
|
|
4653
4676
|
retired: true
|
|
@@ -4656,7 +4679,7 @@ function retireRun(args, store, options) {
|
|
|
4656
4679
|
function runContextCommand(args, store, options) {
|
|
4657
4680
|
const [first, ...rest] = args;
|
|
4658
4681
|
if (first === "expand") {
|
|
4659
|
-
const usage = "Task
|
|
4682
|
+
const usage = "Task run context expand usage: yui task run context expand <task>/<run> <ref-id> [--store <store>] [--mode full].";
|
|
4660
4683
|
const parsed = parseTail(rest, new Set(["--store", "--mode"]), usage);
|
|
4661
4684
|
exactPositionals(parsed.positionals, 2, usage);
|
|
4662
4685
|
const mode = parsed.options.get("--mode");
|
|
@@ -4670,7 +4693,7 @@ function runContextCommand(args, store, options) {
|
|
|
4670
4693
|
}
|
|
4671
4694
|
if (first === "delta") {
|
|
4672
4695
|
if (rest.length !== 3 || rest[1] !== "--after") {
|
|
4673
|
-
throw usageError("Task
|
|
4696
|
+
throw usageError("Task run context delta usage: yui task run context delta <task>/<run> --after <cursor>.");
|
|
4674
4697
|
}
|
|
4675
4698
|
const { taskId, runId } = parseRunContextReference(rest[0]);
|
|
4676
4699
|
authorizeRunContext(store, taskId, runId, options.environment);
|
|
@@ -4678,7 +4701,7 @@ function runContextCommand(args, store, options) {
|
|
|
4678
4701
|
return output(`${JSON.stringify(delta, null, 2)}\n`, { contextDelta: delta });
|
|
4679
4702
|
}
|
|
4680
4703
|
if (first === undefined || rest.length !== 0) {
|
|
4681
|
-
throw usageError("Task
|
|
4704
|
+
throw usageError("Task run context usage: yui task run context <task>/<run>.");
|
|
4682
4705
|
}
|
|
4683
4706
|
const { taskId, runId } = parseRunContextReference(first);
|
|
4684
4707
|
authorizeRunContext(store, taskId, runId, options.environment);
|
|
@@ -4719,7 +4742,7 @@ function authorizeRunContext(store, taskId, runId, environment) {
|
|
|
4719
4742
|
}
|
|
4720
4743
|
}
|
|
4721
4744
|
function listRuns(args, store, options) {
|
|
4722
|
-
const usage = "Task
|
|
4745
|
+
const usage = "Task run list usage: yui task run list <task|task/work>.";
|
|
4723
4746
|
exactPositionals(args, 1, usage);
|
|
4724
4747
|
const reference = args[0];
|
|
4725
4748
|
const task = store.getTask(reference);
|
|
@@ -4764,7 +4787,7 @@ function listRuns(args, store, options) {
|
|
|
4764
4787
|
* creates one fresh Round over the newer frozen Task heads.
|
|
4765
4788
|
*/
|
|
4766
4789
|
function settleStaleFinalReviewRun(args, store, options) {
|
|
4767
|
-
exactPositionals(args, 1, "Task
|
|
4790
|
+
exactPositionals(args, 1, "Task run settle usage: yui task run settle <task>/<run>.");
|
|
4768
4791
|
const now = clock(options);
|
|
4769
4792
|
const previous = store.transaction((tx) => requireRun(tx, args[0], options));
|
|
4770
4793
|
const result = store.transaction((tx) => {
|
|
@@ -4783,7 +4806,7 @@ function settleStaleFinalReviewRun(args, store, options) {
|
|
|
4783
4806
|
if (round === null) {
|
|
4784
4807
|
throw dataError(`ReviewRound not found for AgentRun ${run.id}: ${run.reviewRoundId}.`);
|
|
4785
4808
|
}
|
|
4786
|
-
if (
|
|
4809
|
+
if (round.scope !== "task") {
|
|
4787
4810
|
throw usageError(`Review AgentRun ${run.id} is not a Task-final review; request a new WorkItem review `
|
|
4788
4811
|
+ "for a new Candidate.");
|
|
4789
4812
|
}
|
|
@@ -4803,7 +4826,7 @@ function settleStaleFinalReviewRun(args, store, options) {
|
|
|
4803
4826
|
throw usageError(`${task.id}/${round.reviewerRoleName} already has active AgentRun ${activeRoleRun.id}.`);
|
|
4804
4827
|
}
|
|
4805
4828
|
const taskRounds = reviewRoundsByIdentity(tx.listReviewRounds(task.id)
|
|
4806
|
-
.filter((entry) =>
|
|
4829
|
+
.filter((entry) => entry.scope === "task"));
|
|
4807
4830
|
const roundIndex = taskRounds.findIndex(({ id }) => id === round.id);
|
|
4808
4831
|
if (roundIndex < 0) {
|
|
4809
4832
|
throw dataError(`Final ReviewRound is not in Task history: ${round.id}.`);
|
|
@@ -4883,7 +4906,7 @@ function retryRun(args, store, options) {
|
|
|
4883
4906
|
});
|
|
4884
4907
|
}
|
|
4885
4908
|
function retryRunOperation(args, store, options) {
|
|
4886
|
-
exactPositionals(args, 1, "Task
|
|
4909
|
+
exactPositionals(args, 1, "Task run retry usage: yui task run retry <task>/<run>.");
|
|
4887
4910
|
const now = clock(options);
|
|
4888
4911
|
const previous = store.transaction((tx) => requireRun(tx, args[0], options));
|
|
4889
4912
|
if (previous.purpose === "review") {
|
|
@@ -5147,10 +5170,10 @@ function retryRunOperation(args, store, options) {
|
|
|
5147
5170
|
}, now);
|
|
5148
5171
|
return { kind: "run", run: created };
|
|
5149
5172
|
});
|
|
5150
|
-
notifyMailbox(options.runtime, roleMailbox(retried.run.taskId, retried.run.roleName)
|
|
5173
|
+
notifyMailbox(options.runtime, roleMailbox(retried.run.taskId, retried.run.roleName));
|
|
5151
5174
|
return output(`Retry queued as ${retried.run.id} for ${retried.run.taskId}/${retried.run.roleName}\n`);
|
|
5152
5175
|
}
|
|
5153
|
-
function actualTaskReviewCandidateForMutation(
|
|
5176
|
+
function actualTaskReviewCandidateForMutation(_store, task, options) {
|
|
5154
5177
|
if (options.actualTaskReviewCandidate === undefined) {
|
|
5155
5178
|
throw usageError(`Actual Task Project heads were not verified for delivery: ${task.id}.`);
|
|
5156
5179
|
}
|
|
@@ -5286,7 +5309,7 @@ function isSameTaskReviewCandidate(left, right) {
|
|
|
5286
5309
|
function assertNoConflictingTaskReviewRound(rounds, reusableRoundIds = [], reviewerRoleName) {
|
|
5287
5310
|
const reusable = new Set(typeof reusableRoundIds === "string" ? [reusableRoundIds] : reusableRoundIds);
|
|
5288
5311
|
const conflicting = rounds.find((entry) => (!reusable.has(entry.id)
|
|
5289
|
-
&&
|
|
5312
|
+
&& entry.scope === "task"
|
|
5290
5313
|
&& (entry.status === "pending" || entry.status === "running")
|
|
5291
5314
|
&& (reviewerRoleName === undefined || entry.reviewerRoleName === reviewerRoleName)));
|
|
5292
5315
|
if (conflicting !== undefined) {
|
|
@@ -5341,7 +5364,7 @@ function prepareFinalTaskReview(store, task, now, taskFinalContract, options) {
|
|
|
5341
5364
|
if (taskFinalContract === undefined || task.projectBindings.length === 0)
|
|
5342
5365
|
return null;
|
|
5343
5366
|
const taskRounds = reviewRoundsByIdentity(store.listReviewRounds(task.id))
|
|
5344
|
-
.filter((round) => (
|
|
5367
|
+
.filter((round) => (round.scope === "task"
|
|
5345
5368
|
&& sameTaskFinalReviewContract(round.taskFinalReviewContract, taskFinalContract)));
|
|
5346
5369
|
const establishedRound = taskRounds.at(-1);
|
|
5347
5370
|
const config = taskFinalReviewConfig(taskFinalContract);
|
|
@@ -5368,7 +5391,7 @@ function prepareFinalTaskReview(store, task, now, taskFinalContract, options) {
|
|
|
5368
5391
|
}
|
|
5369
5392
|
return queueTaskReviewRound(store, task, config, taskCandidate, options, now, establishedRound?.requestedBy ?? "policy", taskFinalContract);
|
|
5370
5393
|
}
|
|
5371
|
-
function resumablePendingFinalTaskReview(store, task, round, config,
|
|
5394
|
+
function resumablePendingFinalTaskReview(store, task, round, config, _taskCandidate, taskFinalContract, _options) {
|
|
5372
5395
|
if (taskFinalContract === undefined || round.taskFinalReviewContract === undefined) {
|
|
5373
5396
|
throw usageError(`Final Task Review is still active: ${round.id}/${round.status}.`);
|
|
5374
5397
|
}
|
|
@@ -5447,7 +5470,7 @@ function retryFailedReviewRun(previous, store, options, now) {
|
|
|
5447
5470
|
if (round === null) {
|
|
5448
5471
|
throw dataError(`ReviewRound not found for run ${run.id}: ${run.reviewRoundId}.`);
|
|
5449
5472
|
}
|
|
5450
|
-
const taskScope =
|
|
5473
|
+
const taskScope = round.scope === "task";
|
|
5451
5474
|
const retryLane = run.executionLaneId === undefined
|
|
5452
5475
|
? undefined
|
|
5453
5476
|
: round.executionGroup?.lanes.find(({ id }) => id === run.executionLaneId);
|
|
@@ -5482,7 +5505,7 @@ function retryFailedReviewRun(previous, store, options, now) {
|
|
|
5482
5505
|
throw usageError(`Task-final ReviewRound ${round.id} no longer matches the frozen Task heads.`);
|
|
5483
5506
|
}
|
|
5484
5507
|
const taskRounds = reviewRoundsByIdentity(allRounds
|
|
5485
|
-
.filter((entry) =>
|
|
5508
|
+
.filter((entry) => entry.scope === "task"));
|
|
5486
5509
|
const roundIndex = taskRounds.findIndex(({ id }) => id === round.id);
|
|
5487
5510
|
if (roundIndex < 0) {
|
|
5488
5511
|
throw dataError(`Final ReviewRound is not in Task history: ${round.id}.`);
|
|
@@ -5650,7 +5673,7 @@ function retryFailedReviewRun(previous, store, options, now) {
|
|
|
5650
5673
|
return { round: resetRound, previousRun: run, created: true };
|
|
5651
5674
|
});
|
|
5652
5675
|
if ("run" in result && result.run !== undefined) {
|
|
5653
|
-
notifyMailbox(options.runtime, roleMailbox(result.run.taskId, result.run.roleName)
|
|
5676
|
+
notifyMailbox(options.runtime, roleMailbox(result.run.taskId, result.run.roleName));
|
|
5654
5677
|
}
|
|
5655
5678
|
return output(result.created
|
|
5656
5679
|
? `Review retry requested as ${result.round.id}\n`
|
|
@@ -5658,7 +5681,7 @@ function retryFailedReviewRun(previous, store, options, now) {
|
|
|
5658
5681
|
}
|
|
5659
5682
|
/** AgentRun details are retained audit evidence; continuation uses durable Task state. */
|
|
5660
5683
|
function showRun(args, store, options) {
|
|
5661
|
-
const usage = "Task
|
|
5684
|
+
const usage = "Task run show usage: yui task run show <task>/<run> [--json].";
|
|
5662
5685
|
const asJson = args.includes("--json");
|
|
5663
5686
|
const positionals = args.filter((arg) => arg !== "--json");
|
|
5664
5687
|
exactPositionals(positionals, 1, usage);
|
|
@@ -5710,7 +5733,7 @@ function renderRunShow(run, retirement) {
|
|
|
5710
5733
|
* Leader.
|
|
5711
5734
|
*/
|
|
5712
5735
|
function checkpointRun(args, store, options) {
|
|
5713
|
-
const usage = "Task
|
|
5736
|
+
const usage = "Task run checkpoint usage: yui task run checkpoint <run> (--note <text>|--note-file <path|->).";
|
|
5714
5737
|
const parsed = parseTail(args, new Set(["--note", "--note-file"]), usage);
|
|
5715
5738
|
exactPositionals(parsed.positionals, 1, usage);
|
|
5716
5739
|
const note = readCommandText(parsed.options.get("--note"), parsed.options.get("--note-file"), "--note", usage);
|
|
@@ -5876,12 +5899,12 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
|
|
|
5876
5899
|
throw usageError(`ReviewRound is not dispatchable: ${round.id}/${round.status}.`);
|
|
5877
5900
|
}
|
|
5878
5901
|
if (round.workspace === undefined) {
|
|
5879
|
-
if (
|
|
5902
|
+
if (round.scope === "task") {
|
|
5880
5903
|
throw new TaskFinalReviewDispatchDriftError(`ReviewRound workspace is not ready: ${round.id}.`);
|
|
5881
5904
|
}
|
|
5882
5905
|
throw usageError(`ReviewRound workspace is not ready: ${round.id}.`);
|
|
5883
5906
|
}
|
|
5884
|
-
const taskScope =
|
|
5907
|
+
const taskScope = round.scope === "task";
|
|
5885
5908
|
let item;
|
|
5886
5909
|
let candidate;
|
|
5887
5910
|
if (taskScope) {
|
|
@@ -5914,7 +5937,7 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
|
|
|
5914
5937
|
if (task.status !== "active")
|
|
5915
5938
|
throw usageError(`Task is not active: ${task.id}.`);
|
|
5916
5939
|
assertTaskExecutionEnabled(task, "dispatching a Review");
|
|
5917
|
-
if (
|
|
5940
|
+
if (round.scope === "task") {
|
|
5918
5941
|
let taskFinalContract;
|
|
5919
5942
|
try {
|
|
5920
5943
|
taskFinalContract = taskFinalReviewContractForMutation(tx, task.id, options);
|
|
@@ -5955,7 +5978,7 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
|
|
|
5955
5978
|
}
|
|
5956
5979
|
const reviewer = tx.getRole(taskId, round.reviewerRoleName);
|
|
5957
5980
|
if (reviewer === null) {
|
|
5958
|
-
if (
|
|
5981
|
+
if (round.scope === "task") {
|
|
5959
5982
|
throw new TaskFinalReviewDispatchDriftError(`Reviewer Role not found: ${taskId}/${round.reviewerRoleName}.`);
|
|
5960
5983
|
}
|
|
5961
5984
|
throw roleNotFound(round.reviewerRoleName);
|
|
@@ -5966,7 +5989,7 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
|
|
|
5966
5989
|
|| storedWorkspace.owner.type !== "review-round"
|
|
5967
5990
|
|| storedWorkspace.owner.reviewRoundId !== round.id) {
|
|
5968
5991
|
const message = `ReviewRound workspace ownership changed: ${round.id}.`;
|
|
5969
|
-
if (
|
|
5992
|
+
if (round.scope === "task") {
|
|
5970
5993
|
throw new TaskFinalReviewDispatchDriftError(message);
|
|
5971
5994
|
}
|
|
5972
5995
|
throw usageError(message);
|
|
@@ -5977,7 +6000,7 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
|
|
|
5977
6000
|
...(round.executionGroup?.lanes.map(({ roleName }) => roleName) ?? [])
|
|
5978
6001
|
]);
|
|
5979
6002
|
const conflicting = tx.listReviewRounds(task.id).find((entry) => (entry.id !== round.id
|
|
5980
|
-
&&
|
|
6003
|
+
&& entry.scope === "task"
|
|
5981
6004
|
&& (entry.status === "pending" || entry.status === "running")
|
|
5982
6005
|
&& (requestedReviewers.has(entry.reviewerRoleName)
|
|
5983
6006
|
|| entry.executionGroup?.lanes.some(({ roleName }) => (requestedReviewers.has(roleName))) === true)
|
|
@@ -6229,7 +6252,7 @@ export function dispatchPreparedReviewRound(taskId, reviewRoundId, store, option
|
|
|
6229
6252
|
return createdRuns;
|
|
6230
6253
|
});
|
|
6231
6254
|
for (const run of runs) {
|
|
6232
|
-
notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName)
|
|
6255
|
+
notifyMailbox(options.runtime, roleMailbox(run.taskId, run.roleName));
|
|
6233
6256
|
}
|
|
6234
6257
|
return runs[0] ?? null;
|
|
6235
6258
|
}
|
|
@@ -6256,7 +6279,7 @@ export function failPendingReviewRound(taskId, reviewRoundId, summary, store, op
|
|
|
6256
6279
|
]);
|
|
6257
6280
|
return terminal;
|
|
6258
6281
|
});
|
|
6259
|
-
notifyMailbox(options.runtime, leaderMailbox(taskId)
|
|
6282
|
+
notifyMailbox(options.runtime, leaderMailbox(taskId));
|
|
6260
6283
|
return failed;
|
|
6261
6284
|
}
|
|
6262
6285
|
export function preserveReviewRoundWorkspace(taskId, reviewRoundId, store, options = {}) {
|
|
@@ -6440,20 +6463,6 @@ function appendMessage(store, taskId, body, kind, author, now, context = {}) {
|
|
|
6440
6463
|
}, now);
|
|
6441
6464
|
return message;
|
|
6442
6465
|
}
|
|
6443
|
-
function recordTaskEvent(store, taskId, type, payload, now) {
|
|
6444
|
-
return recordTaskEventRecord(store, taskId, type, payload, now);
|
|
6445
|
-
}
|
|
6446
|
-
function leaderActionEventPayload(store, taskId, options) {
|
|
6447
|
-
const caller = currentManagedRuntime(store, options.environment, taskId, "leader");
|
|
6448
|
-
// A long-lived Session may be handling direct input while another request
|
|
6449
|
-
// awaits admission. The Role's active pointer cannot prove command origin.
|
|
6450
|
-
return caller === undefined ? {} : { leaderNativeSessionId: caller.nativeSessionId };
|
|
6451
|
-
}
|
|
6452
|
-
function recordTaskEventRecord(store, taskId, type, payload, now) {
|
|
6453
|
-
const event = createTaskEvent(store.nextEventId(taskId), taskId, type, payload, now);
|
|
6454
|
-
store.saveEvent(taskId, event);
|
|
6455
|
-
return event;
|
|
6456
|
-
}
|
|
6457
6466
|
/** Keeps a free-text AgentRun-fact note bounded so an event payload stays compact. */
|
|
6458
6467
|
function truncateEventNote(note) {
|
|
6459
6468
|
const normalized = note.trim();
|
|
@@ -6488,13 +6497,6 @@ function runLaunchEventPayload(run) {
|
|
|
6488
6497
|
})
|
|
6489
6498
|
};
|
|
6490
6499
|
}
|
|
6491
|
-
function requireTask(store, taskId) {
|
|
6492
|
-
const id = requiredText(taskId, "Task id");
|
|
6493
|
-
const task = store.getTask(id);
|
|
6494
|
-
if (task === null)
|
|
6495
|
-
throw taskNotFound(id);
|
|
6496
|
-
return task;
|
|
6497
|
-
}
|
|
6498
6500
|
function requireRole(store, taskId, roleName) {
|
|
6499
6501
|
const name = requiredText(roleName, "Role name");
|
|
6500
6502
|
const role = store.getRole(taskId, name);
|
|
@@ -6536,14 +6538,6 @@ function activeRunPointer(store, run) {
|
|
|
6536
6538
|
? store.getActiveExecutionLaneRun(run.taskId, run.executionGroupId, run.executionLaneId)
|
|
6537
6539
|
: store.getActiveRun(run.taskId, run.roleName);
|
|
6538
6540
|
}
|
|
6539
|
-
function requireReviewRound(store, reviewRoundId, options) {
|
|
6540
|
-
const reference = taskRecordReference(reviewRoundId, "reviewRound", "ReviewRound reference", options);
|
|
6541
|
-
const round = store.getReviewRound(reference.taskId, reference.localId);
|
|
6542
|
-
if (round === null) {
|
|
6543
|
-
throw usageError(`ReviewRound not found: ${reference.taskId}/${reference.localId}.`);
|
|
6544
|
-
}
|
|
6545
|
-
return round;
|
|
6546
|
-
}
|
|
6547
6541
|
function taskRecordReference(value, kind, label, options) {
|
|
6548
6542
|
try {
|
|
6549
6543
|
return resolveTaskRecordReference(requiredText(value, label), {
|
|
@@ -6576,23 +6570,11 @@ function chronologicalRuns(runs) {
|
|
|
6576
6570
|
function isTerminalWorkItemStatus(status) {
|
|
6577
6571
|
return ["accepted", "retired"].includes(status);
|
|
6578
6572
|
}
|
|
6579
|
-
function assertTaskOpen(task) {
|
|
6580
|
-
if (task.status === "completed") {
|
|
6581
|
-
throw usageError(`Task ${task.id} is completed; reopen it before continuing.`);
|
|
6582
|
-
}
|
|
6583
|
-
if (task.status === "archived")
|
|
6584
|
-
throw usageError(`Task is archived: ${task.id}.`);
|
|
6585
|
-
if (task.status === "cancelled")
|
|
6586
|
-
throw usageError(`Task is retired: ${task.id}.`);
|
|
6587
|
-
}
|
|
6588
6573
|
function assertTaskExecutionEnabled(task, action) {
|
|
6589
6574
|
if (task.executionGate.state === "enabled")
|
|
6590
6575
|
return;
|
|
6591
6576
|
throw usageError(`Task execution is stopped: ${task.id}. Run "yui task execution start ${task.id}" before ${action}.`);
|
|
6592
6577
|
}
|
|
6593
|
-
function taskActor(store, options, taskId) {
|
|
6594
|
-
return resolveTaskLocalActor(store, options.environment, taskId);
|
|
6595
|
-
}
|
|
6596
6578
|
function inactiveTaskMessage(task, action) {
|
|
6597
6579
|
if (task.status === "draft") {
|
|
6598
6580
|
return `Task ${task.id} is a Draft; activate it before ${action}.`;
|
|
@@ -6734,317 +6716,12 @@ function parseIsoTimestamp(value, label) {
|
|
|
6734
6716
|
}
|
|
6735
6717
|
return timestamp.toISOString();
|
|
6736
6718
|
}
|
|
6737
|
-
function taskBriefCommand(args, store, options) {
|
|
6738
|
-
const [command, ...rest] = args;
|
|
6739
|
-
if (command === "show") {
|
|
6740
|
-
exactPositionals(rest, 1, "Task brief show usage: yui task brief show <task>.");
|
|
6741
|
-
const task = requireTask(store, rest[0]);
|
|
6742
|
-
const brief = store.getTaskBrief(task.id);
|
|
6743
|
-
if (brief === null) {
|
|
6744
|
-
return output(`Task ${task.id} has no brief.\n`, { taskId: task.id, brief: null });
|
|
6745
|
-
}
|
|
6746
|
-
const timeZone = store.getConfig().timeZone;
|
|
6747
|
-
return output([
|
|
6748
|
-
`Task: ${task.id}`,
|
|
6749
|
-
`Objective: ${brief.objective}`,
|
|
6750
|
-
`Boundaries:`,
|
|
6751
|
-
...(brief.boundaries.length === 0 ? [" (none)"] : brief.boundaries.map((b) => ` - ${b}`)),
|
|
6752
|
-
`Technical approach: ${brief.technicalApproach || "(not defined)"}`,
|
|
6753
|
-
`Current focus: ${brief.currentFocus}`,
|
|
6754
|
-
`Leader summary: ${brief.leaderSummary}`,
|
|
6755
|
-
`Updated by: ${brief.updatedBy}`,
|
|
6756
|
-
`Updated at: ${presentTime(brief.updatedAt, timeZone)}`
|
|
6757
|
-
].join("\n").concat("\n"), { taskId: task.id, brief });
|
|
6758
|
-
}
|
|
6759
|
-
if (command === "update") {
|
|
6760
|
-
const usage = "Task brief update usage: yui task brief update <task> [--objective <text>] [--boundary <text> ...] [--approach <text>] [--focus <text>] [--leader-summary <text>].";
|
|
6761
|
-
const parsed = parseMultiValueTail(rest, new Set(["--objective", "--approach", "--focus", "--leader-summary"]), new Set(["--boundary"]), usage);
|
|
6762
|
-
exactPositionals(parsed.positionals, 1, usage);
|
|
6763
|
-
const hasObjective = parsed.options.has("--objective");
|
|
6764
|
-
const hasApproach = parsed.options.has("--approach");
|
|
6765
|
-
const hasFocus = parsed.options.has("--focus");
|
|
6766
|
-
const hasSummary = parsed.options.has("--leader-summary");
|
|
6767
|
-
const boundaries = parsed.multiOptions.get("--boundary") ?? [];
|
|
6768
|
-
if (!hasObjective && !hasApproach && !hasFocus && !hasSummary && boundaries.length === 0) {
|
|
6769
|
-
throw usageError("At least one brief field is required.", usage);
|
|
6770
|
-
}
|
|
6771
|
-
const now = clock(options);
|
|
6772
|
-
const result = store.transaction((tx) => {
|
|
6773
|
-
const task = requireTask(tx, parsed.positionals[0]);
|
|
6774
|
-
assertTaskOpen(task);
|
|
6775
|
-
const existing = tx.getTaskBrief(task.id);
|
|
6776
|
-
const updatedBy = taskActor(tx, options, task.id);
|
|
6777
|
-
const brief = existing === null
|
|
6778
|
-
? createTaskBrief({
|
|
6779
|
-
objective: requiredText(parsed.options.get("--objective"), "--objective"),
|
|
6780
|
-
boundaries,
|
|
6781
|
-
...(hasApproach
|
|
6782
|
-
? { technicalApproach: requiredText(parsed.options.get("--approach"), "--approach") }
|
|
6783
|
-
: {}),
|
|
6784
|
-
currentFocus: requiredText(parsed.options.get("--focus"), "--focus"),
|
|
6785
|
-
leaderSummary: requiredText(parsed.options.get("--leader-summary"), "--leader-summary"),
|
|
6786
|
-
updatedBy
|
|
6787
|
-
}, now)
|
|
6788
|
-
: updateTaskBrief(existing, {
|
|
6789
|
-
...(hasObjective ? { objective: parsed.options.get("--objective") } : {}),
|
|
6790
|
-
...(boundaries.length > 0 ? { boundaries } : {}),
|
|
6791
|
-
...(hasApproach
|
|
6792
|
-
? { technicalApproach: parsed.options.get("--approach") }
|
|
6793
|
-
: {}),
|
|
6794
|
-
...(hasFocus ? { currentFocus: parsed.options.get("--focus") } : {}),
|
|
6795
|
-
...(hasSummary ? { leaderSummary: parsed.options.get("--leader-summary") } : {})
|
|
6796
|
-
}, updatedBy, now);
|
|
6797
|
-
tx.saveTaskBrief(task.id, brief);
|
|
6798
|
-
recordTaskEvent(tx, task.id, "brief.updated", {
|
|
6799
|
-
updatedBy,
|
|
6800
|
-
previous: JSON.stringify(existing), current: JSON.stringify(brief)
|
|
6801
|
-
}, now);
|
|
6802
|
-
enqueueWork(tx, taskMailbox(task.id), "brief-updated", now, [taskRef(task.id)]);
|
|
6803
|
-
if (task.status === "active" && updatedBy !== "leader") {
|
|
6804
|
-
enqueueWork(tx, leaderMailbox(task.id), "brief-updated", now, [taskRef(task.id)]);
|
|
6805
|
-
}
|
|
6806
|
-
return { task, brief };
|
|
6807
|
-
});
|
|
6808
|
-
notifyMailbox(options.runtime, taskMailbox(result.task.id), result.task.id);
|
|
6809
|
-
if (result.task.status === "active") {
|
|
6810
|
-
notifyMailbox(options.runtime, leaderMailbox(result.task.id), result.task.id);
|
|
6811
|
-
}
|
|
6812
|
-
return output(`Updated brief for ${result.task.id}\n`, { taskId: result.task.id, brief: result.brief });
|
|
6813
|
-
}
|
|
6814
|
-
throw usageError(command === undefined
|
|
6815
|
-
? "Task brief command is required."
|
|
6816
|
-
: `Unknown command: task brief ${command}`);
|
|
6817
|
-
}
|
|
6818
6719
|
/** Persist only edited fields, not another copy of aggregate execution history.
|
|
6819
6720
|
* Null records an absent optional field so clearing it remains observable. */
|
|
6820
6721
|
function editedFieldValues(record, fields) {
|
|
6821
6722
|
const values = record;
|
|
6822
6723
|
return JSON.stringify(Object.fromEntries(fields.map((field) => [field, values[field] ?? null])));
|
|
6823
6724
|
}
|
|
6824
|
-
function taskDecisionCommand(args, store, options) {
|
|
6825
|
-
const [command, ...rest] = args;
|
|
6826
|
-
if (command === "record") {
|
|
6827
|
-
const usage = "Task decision record usage: yui task decision record <task> --title <text> --rationale <text>.";
|
|
6828
|
-
const parsed = parseTail(rest, new Set(["--title", "--rationale"]), usage);
|
|
6829
|
-
exactPositionals(parsed.positionals, 1, usage);
|
|
6830
|
-
const title = requiredOption(parsed.options, "--title");
|
|
6831
|
-
const rationale = requiredOption(parsed.options, "--rationale");
|
|
6832
|
-
const now = clock(options);
|
|
6833
|
-
const result = store.transaction((tx) => {
|
|
6834
|
-
const task = requireTask(tx, parsed.positionals[0]);
|
|
6835
|
-
assertTaskOpen(task);
|
|
6836
|
-
const actor = taskActor(tx, options, task.id);
|
|
6837
|
-
const decision = createDecision(tx.nextDecisionId(task.id), task.id, title, rationale, now);
|
|
6838
|
-
tx.saveDecision(task.id, decision);
|
|
6839
|
-
recordTaskEvent(tx, task.id, "decision.recorded", {
|
|
6840
|
-
decisionId: decision.id,
|
|
6841
|
-
title,
|
|
6842
|
-
...leaderActionEventPayload(tx, task.id, options)
|
|
6843
|
-
}, now);
|
|
6844
|
-
enqueueWork(tx, taskMailbox(task.id), "decision-recorded", now, [taskRef(task.id)]);
|
|
6845
|
-
if (task.status === "active" && actor !== "leader") {
|
|
6846
|
-
enqueueWork(tx, leaderMailbox(task.id), "decision-recorded", now, [taskRef(task.id)]);
|
|
6847
|
-
}
|
|
6848
|
-
return { task, decision };
|
|
6849
|
-
});
|
|
6850
|
-
notifyMailbox(options.runtime, taskMailbox(result.task.id), result.task.id);
|
|
6851
|
-
if (result.task.status === "active")
|
|
6852
|
-
notifyMailbox(options.runtime, leaderMailbox(result.task.id), result.task.id);
|
|
6853
|
-
return output(`Recorded decision ${result.decision.id} for ${result.task.id}\n`);
|
|
6854
|
-
}
|
|
6855
|
-
if (command === "list") {
|
|
6856
|
-
const usage = "Task decision list usage: yui task decision list <task> [--status active|superseded].";
|
|
6857
|
-
const parsed = parseTail(rest, new Set(["--status"]), usage);
|
|
6858
|
-
exactPositionals(parsed.positionals, 1, usage);
|
|
6859
|
-
const task = requireTask(store, parsed.positionals[0]);
|
|
6860
|
-
let decisions = store.listDecisions(task.id);
|
|
6861
|
-
const status = parsed.options.get("--status");
|
|
6862
|
-
if (status !== undefined) {
|
|
6863
|
-
if (status !== "active" && status !== "superseded") {
|
|
6864
|
-
throw usageError("--status must be active or superseded.", usage);
|
|
6865
|
-
}
|
|
6866
|
-
decisions = decisions.filter((d) => d.status === status);
|
|
6867
|
-
}
|
|
6868
|
-
if (decisions.length === 0) {
|
|
6869
|
-
return output(`No decisions found for ${task.id}.\n`, { taskId: task.id, decisions: [] });
|
|
6870
|
-
}
|
|
6871
|
-
const timeZone = store.getConfig().timeZone;
|
|
6872
|
-
return output(`${renderTable(`Decisions: ${task.id}`, [
|
|
6873
|
-
{ header: "Decision", minWidth: 8, maxWidth: 18 },
|
|
6874
|
-
{ header: "Status", minWidth: 6, maxWidth: 12 },
|
|
6875
|
-
{ header: "Title", minWidth: 8, maxWidth: 64 },
|
|
6876
|
-
{ header: "Created", minWidth: 10, maxWidth: 28 }
|
|
6877
|
-
], decisions.map((d) => [d.id, d.status, d.title, presentTime(d.createdAt, timeZone)]), defaultTableWidth())}\n`, { taskId: task.id, decisions });
|
|
6878
|
-
}
|
|
6879
|
-
if (command === "show") {
|
|
6880
|
-
exactPositionals(rest, 2, "Task decision show usage: yui task decision show <task> <decision>.");
|
|
6881
|
-
const task = requireTask(store, rest[0]);
|
|
6882
|
-
const decision = store.getDecision(task.id, rest[1]);
|
|
6883
|
-
if (decision === null)
|
|
6884
|
-
throw dataError(`Decision not found: ${rest[1]}.`);
|
|
6885
|
-
const timeZone = store.getConfig().timeZone;
|
|
6886
|
-
return output([
|
|
6887
|
-
`Decision: ${decision.id}`,
|
|
6888
|
-
`Task: ${task.id}`,
|
|
6889
|
-
`Title: ${decision.title}`,
|
|
6890
|
-
`Rationale: ${decision.rationale}`,
|
|
6891
|
-
`Status: ${decision.status}`,
|
|
6892
|
-
...(decision.supersededReason === undefined ? [] : [`Superseded reason: ${decision.supersededReason}`]),
|
|
6893
|
-
...(decision.supersededAt === undefined ? [] : [`Superseded at: ${presentTime(decision.supersededAt, timeZone)}`]),
|
|
6894
|
-
`Created: ${presentTime(decision.createdAt, timeZone)}`,
|
|
6895
|
-
`Updated: ${presentTime(decision.updatedAt, timeZone)}`
|
|
6896
|
-
].join("\n").concat("\n"), { taskId: task.id, decision });
|
|
6897
|
-
}
|
|
6898
|
-
if (command === "supersede") {
|
|
6899
|
-
const usage = "Task decision supersede usage: yui task decision supersede <task> <decision> --reason <text>.";
|
|
6900
|
-
const parsed = parseTail(rest, new Set(["--reason"]), usage);
|
|
6901
|
-
exactPositionals(parsed.positionals, 2, usage);
|
|
6902
|
-
const reason = requiredOption(parsed.options, "--reason");
|
|
6903
|
-
const now = clock(options);
|
|
6904
|
-
const result = store.transaction((tx) => {
|
|
6905
|
-
const task = requireTask(tx, parsed.positionals[0]);
|
|
6906
|
-
assertTaskOpen(task);
|
|
6907
|
-
const actor = taskActor(tx, options, task.id);
|
|
6908
|
-
const existing = tx.getDecision(task.id, parsed.positionals[1]);
|
|
6909
|
-
if (existing === null)
|
|
6910
|
-
throw dataError(`Decision not found: ${parsed.positionals[1]}.`);
|
|
6911
|
-
const decision = supersedeDecision(existing, reason, now);
|
|
6912
|
-
tx.saveDecision(task.id, decision);
|
|
6913
|
-
recordTaskEvent(tx, task.id, "decision.superseded", {
|
|
6914
|
-
decisionId: decision.id,
|
|
6915
|
-
reason,
|
|
6916
|
-
...leaderActionEventPayload(tx, task.id, options)
|
|
6917
|
-
}, now);
|
|
6918
|
-
enqueueWork(tx, taskMailbox(task.id), "decision-superseded", now, [taskRef(task.id)]);
|
|
6919
|
-
if (task.status === "active" && actor !== "leader") {
|
|
6920
|
-
enqueueWork(tx, leaderMailbox(task.id), "decision-superseded", now, [taskRef(task.id)]);
|
|
6921
|
-
}
|
|
6922
|
-
return { task, decision };
|
|
6923
|
-
});
|
|
6924
|
-
notifyMailbox(options.runtime, taskMailbox(result.task.id), result.task.id);
|
|
6925
|
-
if (result.task.status === "active")
|
|
6926
|
-
notifyMailbox(options.runtime, leaderMailbox(result.task.id), result.task.id);
|
|
6927
|
-
return output(`Superseded decision ${result.decision.id} for ${result.task.id}\n`);
|
|
6928
|
-
}
|
|
6929
|
-
throw usageError(command === undefined
|
|
6930
|
-
? "Task decision command is required."
|
|
6931
|
-
: `Unknown command: task decision ${command}`);
|
|
6932
|
-
}
|
|
6933
|
-
function taskMilestoneCommand(args, store, options) {
|
|
6934
|
-
const [command, ...rest] = args;
|
|
6935
|
-
if (command === "add") {
|
|
6936
|
-
const usage = "Task milestone add usage: yui task milestone add <task> --title <text> --summary <text>.";
|
|
6937
|
-
const parsed = parseTail(rest, new Set(["--title", "--summary"]), usage);
|
|
6938
|
-
exactPositionals(parsed.positionals, 1, usage);
|
|
6939
|
-
const title = requiredOption(parsed.options, "--title");
|
|
6940
|
-
const summary = requiredOption(parsed.options, "--summary");
|
|
6941
|
-
const now = clock(options);
|
|
6942
|
-
const result = store.transaction((tx) => {
|
|
6943
|
-
const task = requireTask(tx, parsed.positionals[0]);
|
|
6944
|
-
assertTaskOpen(task);
|
|
6945
|
-
const actor = taskActor(tx, options, task.id);
|
|
6946
|
-
const milestone = createMilestone(tx.nextMilestoneId(task.id), task.id, title, summary, actor, now);
|
|
6947
|
-
tx.saveMilestone(task.id, milestone);
|
|
6948
|
-
recordTaskEvent(tx, task.id, "milestone.added", {
|
|
6949
|
-
milestoneId: milestone.id,
|
|
6950
|
-
title,
|
|
6951
|
-
...leaderActionEventPayload(tx, task.id, options)
|
|
6952
|
-
}, now);
|
|
6953
|
-
enqueueWork(tx, taskMailbox(task.id), "milestone-added", now, [taskRef(task.id)]);
|
|
6954
|
-
return { task, milestone };
|
|
6955
|
-
});
|
|
6956
|
-
notifyMailbox(options.runtime, taskMailbox(result.task.id), result.task.id);
|
|
6957
|
-
return output(`Added milestone ${result.milestone.id} for ${result.task.id}\n`);
|
|
6958
|
-
}
|
|
6959
|
-
if (command === "list") {
|
|
6960
|
-
exactPositionals(rest, 1, "Task milestone list usage: yui task milestone list <task>.");
|
|
6961
|
-
const task = requireTask(store, rest[0]);
|
|
6962
|
-
const milestones = store.listMilestones(task.id);
|
|
6963
|
-
if (milestones.length === 0) {
|
|
6964
|
-
return output(`No milestones found for ${task.id}.\n`, { taskId: task.id, milestones: [] });
|
|
6965
|
-
}
|
|
6966
|
-
const timeZone = store.getConfig().timeZone;
|
|
6967
|
-
return output(`${renderTable(`Milestones: ${task.id}`, [
|
|
6968
|
-
{ header: "Milestone", minWidth: 9, maxWidth: 18 },
|
|
6969
|
-
{ header: "Title", minWidth: 8, maxWidth: 64 },
|
|
6970
|
-
{ header: "Created", minWidth: 10, maxWidth: 28 }
|
|
6971
|
-
], milestones.map((m) => [m.id, m.title, presentTime(m.createdAt, timeZone)]), defaultTableWidth())}\n`, { taskId: task.id, milestones });
|
|
6972
|
-
}
|
|
6973
|
-
if (command === "show") {
|
|
6974
|
-
exactPositionals(rest, 2, "Task milestone show usage: yui task milestone show <task> <milestone>.");
|
|
6975
|
-
const task = requireTask(store, rest[0]);
|
|
6976
|
-
const milestone = store.getMilestone(task.id, rest[1]);
|
|
6977
|
-
if (milestone === null)
|
|
6978
|
-
throw dataError(`Milestone not found: ${rest[1]}.`);
|
|
6979
|
-
const timeZone = store.getConfig().timeZone;
|
|
6980
|
-
return output([
|
|
6981
|
-
`Milestone: ${milestone.id}`,
|
|
6982
|
-
`Task: ${task.id}`,
|
|
6983
|
-
`Title: ${milestone.title}`,
|
|
6984
|
-
`Summary: ${milestone.summary}`,
|
|
6985
|
-
`Created by: ${milestone.createdBy}`,
|
|
6986
|
-
`Created: ${presentTime(milestone.createdAt, timeZone)}`
|
|
6987
|
-
].join("\n").concat("\n"), { taskId: task.id, milestone });
|
|
6988
|
-
}
|
|
6989
|
-
throw usageError(command === undefined
|
|
6990
|
-
? "Task milestone command is required."
|
|
6991
|
-
: `Unknown command: task milestone ${command}`);
|
|
6992
|
-
}
|
|
6993
|
-
function taskEventCommand(args, store) {
|
|
6994
|
-
const [command, ...rest] = args;
|
|
6995
|
-
if (command === "list") {
|
|
6996
|
-
const eventListUsage = "Task event list usage: yui task event list <task> [--after <timestamp>] [--limit <n>].";
|
|
6997
|
-
const parsed = parseTail(rest, new Set(["--after", "--limit"]), eventListUsage);
|
|
6998
|
-
exactPositionals(parsed.positionals, 1, eventListUsage);
|
|
6999
|
-
const task = requireTask(store, parsed.positionals[0]);
|
|
7000
|
-
let events = store.listEvents(task.id);
|
|
7001
|
-
const after = optionalNonEmptyOption(parsed.options, "--after");
|
|
7002
|
-
if (after !== undefined) {
|
|
7003
|
-
const afterMs = Date.parse(after);
|
|
7004
|
-
if (!Number.isFinite(afterMs))
|
|
7005
|
-
throw usageError("--after must be a valid timestamp.", eventListUsage);
|
|
7006
|
-
events = events.filter((e) => Date.parse(e.createdAt) > afterMs);
|
|
7007
|
-
}
|
|
7008
|
-
const limit = optionalNonEmptyOption(parsed.options, "--limit");
|
|
7009
|
-
if (limit !== undefined) {
|
|
7010
|
-
const n = Number(limit);
|
|
7011
|
-
if (!Number.isSafeInteger(n) || n <= 0)
|
|
7012
|
-
throw usageError("--limit must be a positive integer.", eventListUsage);
|
|
7013
|
-
events = events.slice(-n);
|
|
7014
|
-
}
|
|
7015
|
-
if (events.length === 0) {
|
|
7016
|
-
return output(`No events found for ${task.id}.\n`, { taskId: task.id, events: [] });
|
|
7017
|
-
}
|
|
7018
|
-
const timeZone = store.getConfig().timeZone;
|
|
7019
|
-
return output(`${renderTable(`Events: ${task.id}`, [
|
|
7020
|
-
{ header: "Event", minWidth: 8, maxWidth: 18 },
|
|
7021
|
-
{ header: "Type", minWidth: 8, maxWidth: 28 },
|
|
7022
|
-
{ header: "Created", minWidth: 10, maxWidth: 28 }
|
|
7023
|
-
], events.map((e) => [e.id, e.type, presentTime(e.createdAt, timeZone)]), defaultTableWidth())}\n`, { taskId: task.id, events });
|
|
7024
|
-
}
|
|
7025
|
-
if (command === "show") {
|
|
7026
|
-
exactPositionals(rest, 2, "Task event show usage: yui task event show <task> <event>.");
|
|
7027
|
-
const task = requireTask(store, rest[0]);
|
|
7028
|
-
const events = store.listEvents(task.id);
|
|
7029
|
-
const event = events.find((e) => e.id === rest[1]) ?? null;
|
|
7030
|
-
if (event === null)
|
|
7031
|
-
throw dataError(`Event not found: ${rest[1]}.`);
|
|
7032
|
-
const timeZone = store.getConfig().timeZone;
|
|
7033
|
-
return output([
|
|
7034
|
-
`Event: ${event.id}`,
|
|
7035
|
-
`Task: ${task.id}`,
|
|
7036
|
-
`Type: ${event.type}`,
|
|
7037
|
-
`Created: ${presentTime(event.createdAt, timeZone)}`,
|
|
7038
|
-
`Payload:`,
|
|
7039
|
-
...(Object.keys(event.payload).length === 0
|
|
7040
|
-
? [" (none)"]
|
|
7041
|
-
: Object.entries(event.payload).map(([k, v]) => ` ${k}: ${v}`))
|
|
7042
|
-
].join("\n").concat("\n"), { taskId: task.id, event });
|
|
7043
|
-
}
|
|
7044
|
-
throw usageError(command === undefined
|
|
7045
|
-
? "Task event command is required."
|
|
7046
|
-
: `Unknown command: task event ${command}`);
|
|
7047
|
-
}
|
|
7048
6725
|
/**
|
|
7049
6726
|
* Issue 13: native child durability visibility. A native Provider subagent is
|
|
7050
6727
|
* best-effort until Yui persists its result content; once a continuation
|
|
@@ -7137,6 +6814,8 @@ function continuationReportEvents(events) {
|
|
|
7137
6814
|
*/
|
|
7138
6815
|
function taskWakeDispatch(args, store, options) {
|
|
7139
6816
|
const [subcommand] = args;
|
|
6817
|
+
if (subcommand === "retry")
|
|
6818
|
+
return taskWakeRetryCommand(args.slice(1), store, options);
|
|
7140
6819
|
if (subcommand === "resolve") {
|
|
7141
6820
|
const usage = "Task wake resolve usage: yui task wake resolve <task> <wake> --reason <quiescence-evidence>.";
|
|
7142
6821
|
const parsed = parseTail(args.slice(1), new Set(["--reason"]), usage);
|
|
@@ -7176,13 +6855,15 @@ function taskWakeDispatch(args, store, options) {
|
|
|
7176
6855
|
tx.saveWorkMailbox(completeProcessing(mailbox, claim.batchId));
|
|
7177
6856
|
return { taskId: task.id, wakeId: wake.id, outcome: "released-without-replay" };
|
|
7178
6857
|
});
|
|
7179
|
-
notifyMailbox(options.runtime, leaderMailbox(result.taskId)
|
|
6858
|
+
notifyMailbox(options.runtime, leaderMailbox(result.taskId));
|
|
7180
6859
|
return output(`Released ${result.wakeId} without replay; original acceptance remains unknown.\n`, result);
|
|
7181
6860
|
}
|
|
7182
6861
|
if (subcommand === "list" || subcommand === "show") {
|
|
7183
6862
|
return taskWakeInspectionCommand(args, store);
|
|
7184
6863
|
}
|
|
7185
|
-
|
|
6864
|
+
throw usageError(subcommand === undefined
|
|
6865
|
+
? "Task wake command is required."
|
|
6866
|
+
: `Unknown command: task wake ${subcommand}`);
|
|
7186
6867
|
}
|
|
7187
6868
|
/**
|
|
7188
6869
|
* Issue 04 (long-term): the durable wake ledger. `wake list` shows the
|
|
@@ -7205,13 +6886,11 @@ function taskWakeInspectionCommand(args, store) {
|
|
|
7205
6886
|
{ header: "Wake", minWidth: 8, maxWidth: 18 },
|
|
7206
6887
|
{ header: "Status", minWidth: 8, maxWidth: 12 },
|
|
7207
6888
|
{ header: "Reasons", minWidth: 10, maxWidth: 40 },
|
|
7208
|
-
{ header: "AgentRun", minWidth: 10, maxWidth: 20 },
|
|
7209
6889
|
{ header: "Dispatched", minWidth: 10, maxWidth: 28 }
|
|
7210
6890
|
], wakes.map((wake) => [
|
|
7211
6891
|
wake.id,
|
|
7212
6892
|
wake.status,
|
|
7213
6893
|
wake.reasons.map(renderWakeReason).join(", "),
|
|
7214
|
-
wake.runId ?? "-",
|
|
7215
6894
|
presentTime(wake.createdAt, timeZone)
|
|
7216
6895
|
]), defaultTableWidth())}\n`, { taskId: task.id, wakes });
|
|
7217
6896
|
}
|
|
@@ -7233,6 +6912,8 @@ function taskWakeInspectionCommand(args, store) {
|
|
|
7233
6912
|
const deliveryEvents = allEvents.filter((event) => (event.type === "notification.delivery" || event.type === "notification.resolved")
|
|
7234
6913
|
&& (event.payload.wakeId === wake.id
|
|
7235
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;
|
|
7236
6917
|
const referenced = (type, id) => wake.refs?.some(ref => ref.type === type && ref.id === id
|
|
7237
6918
|
&& (!("taskId" in ref) || ref.taskId === task.id)) === true;
|
|
7238
6919
|
const events = allEvents.filter((e) => inWindow(e.createdAt) || referenced("event", e.id));
|
|
@@ -7250,10 +6931,18 @@ function taskWakeInspectionCommand(args, store) {
|
|
|
7250
6931
|
`Wake: ${wake.id}`,
|
|
7251
6932
|
`Task: ${task.id}`,
|
|
7252
6933
|
`Status: ${wake.status}`,
|
|
7253
|
-
`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
|
+
]),
|
|
7254
6944
|
`Reasons: ${wake.reasons.map(renderWakeReason).join(", ")}`,
|
|
7255
6945
|
`Delta window: ${wake.fromCursor} → ${wake.toCursor}`,
|
|
7256
|
-
...(wake.runId === undefined ? [] : [`AgentRun: ${wake.runId}`]),
|
|
7257
6946
|
`Dispatched: ${presentTime(wake.createdAt, timeZone)}`,
|
|
7258
6947
|
...(wake.consumedAt === undefined
|
|
7259
6948
|
? []
|
|
@@ -7282,130 +6971,13 @@ function taskWakeInspectionCommand(args, store) {
|
|
|
7282
6971
|
? "Task wake command is required."
|
|
7283
6972
|
: `Unknown command: task wake ${command}`);
|
|
7284
6973
|
}
|
|
7285
|
-
function parseMultiValueTail(args, valueOptions, repeatOptions, usage, flagOptions = new Set()) {
|
|
7286
|
-
const positionals = [];
|
|
7287
|
-
const options = new Map();
|
|
7288
|
-
const multiOptions = new Map();
|
|
7289
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
7290
|
-
const value = args[index];
|
|
7291
|
-
if (!value.startsWith("--")) {
|
|
7292
|
-
positionals.push(value);
|
|
7293
|
-
continue;
|
|
7294
|
-
}
|
|
7295
|
-
if (!valueOptions.has(value) && !repeatOptions.has(value) && !flagOptions.has(value)) {
|
|
7296
|
-
throw usageError(`Unsupported option: ${value}.`, usage);
|
|
7297
|
-
}
|
|
7298
|
-
if (flagOptions.has(value)) {
|
|
7299
|
-
if (options.has(value))
|
|
7300
|
-
throw usageError(`Option may only be specified once: ${value}.`, usage);
|
|
7301
|
-
options.set(value, "");
|
|
7302
|
-
continue;
|
|
7303
|
-
}
|
|
7304
|
-
if (repeatOptions.has(value)) {
|
|
7305
|
-
const optionValue = args[index + 1];
|
|
7306
|
-
if (optionValue === undefined || optionValue.startsWith("--")) {
|
|
7307
|
-
throw usageError(`${value} is required.`, usage);
|
|
7308
|
-
}
|
|
7309
|
-
const existing = multiOptions.get(value) ?? [];
|
|
7310
|
-
multiOptions.set(value, [...existing, optionValue]);
|
|
7311
|
-
index += 1;
|
|
7312
|
-
continue;
|
|
7313
|
-
}
|
|
7314
|
-
if (options.has(value))
|
|
7315
|
-
throw usageError(`Option may only be specified once: ${value}.`, usage);
|
|
7316
|
-
const optionValue = args[index + 1];
|
|
7317
|
-
if (optionValue === undefined || optionValue.startsWith("--")) {
|
|
7318
|
-
throw usageError(`${value} is required.`, usage);
|
|
7319
|
-
}
|
|
7320
|
-
options.set(value, optionValue);
|
|
7321
|
-
index += 1;
|
|
7322
|
-
}
|
|
7323
|
-
return { positionals, options, multiOptions };
|
|
7324
|
-
}
|
|
7325
|
-
function parseTail(args, valueOptions, usage, flagOptions = new Set()) {
|
|
7326
|
-
const positionals = [];
|
|
7327
|
-
const options = new Map();
|
|
7328
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
7329
|
-
const value = args[index];
|
|
7330
|
-
if (!value.startsWith("--")) {
|
|
7331
|
-
positionals.push(value);
|
|
7332
|
-
continue;
|
|
7333
|
-
}
|
|
7334
|
-
if (!valueOptions.has(value) && !flagOptions.has(value)) {
|
|
7335
|
-
throw usageError(`Unsupported option: ${value}.`, usage);
|
|
7336
|
-
}
|
|
7337
|
-
if (options.has(value))
|
|
7338
|
-
throw usageError(`Option may only be specified once: ${value}.`, usage);
|
|
7339
|
-
if (flagOptions.has(value)) {
|
|
7340
|
-
options.set(value, "");
|
|
7341
|
-
continue;
|
|
7342
|
-
}
|
|
7343
|
-
const optionValue = args[index + 1];
|
|
7344
|
-
if (optionValue === undefined || optionValue.startsWith("--")) {
|
|
7345
|
-
throw usageError(`${value} is required.`, usage);
|
|
7346
|
-
}
|
|
7347
|
-
options.set(value, optionValue);
|
|
7348
|
-
index += 1;
|
|
7349
|
-
}
|
|
7350
|
-
return { positionals, options };
|
|
7351
|
-
}
|
|
7352
|
-
function requiredOption(options, name) {
|
|
7353
|
-
return requiredText(options.get(name), name);
|
|
7354
|
-
}
|
|
7355
|
-
function optionalNonEmptyOption(options, name) {
|
|
7356
|
-
if (!options.has(name))
|
|
7357
|
-
return undefined;
|
|
7358
|
-
return requiredText(options.get(name), name);
|
|
7359
|
-
}
|
|
7360
|
-
function exactPositionals(values, count, usage) {
|
|
7361
|
-
if (values.length !== count || values.some((value) => value.trim().length === 0)) {
|
|
7362
|
-
throw usageError(usage);
|
|
7363
|
-
}
|
|
7364
|
-
}
|
|
7365
|
-
function assertNoArguments(args, usage) {
|
|
7366
|
-
if (args.length > 0)
|
|
7367
|
-
throw usageError(usage);
|
|
7368
|
-
}
|
|
7369
|
-
function requiredText(value, label) {
|
|
7370
|
-
const normalized = value?.trim();
|
|
7371
|
-
if (normalized === undefined || normalized.length === 0)
|
|
7372
|
-
throw usageError(`${label} is required.`);
|
|
7373
|
-
return normalized;
|
|
7374
|
-
}
|
|
7375
|
-
function trimmed(value) {
|
|
7376
|
-
const normalized = value?.trim();
|
|
7377
|
-
return normalized === undefined || normalized.length === 0 ? undefined : normalized;
|
|
7378
|
-
}
|
|
7379
6974
|
function titleFrom(body) {
|
|
7380
6975
|
const oneLine = requiredText(body, "Message body").replace(/\s+/g, " ");
|
|
7381
6976
|
return oneLine.length <= 80 ? oneLine : `${oneLine.slice(0, 77)}...`;
|
|
7382
6977
|
}
|
|
7383
|
-
function presentTime(value, timeZone) {
|
|
7384
|
-
return formatTimestamp(value, timeZone);
|
|
7385
|
-
}
|
|
7386
|
-
function output(value, data) {
|
|
7387
|
-
return data === undefined
|
|
7388
|
-
? { kind: "output", output: value }
|
|
7389
|
-
: { kind: "output", output: value, data };
|
|
7390
|
-
}
|
|
7391
|
-
function clock(options) {
|
|
7392
|
-
return options.now?.() ?? new Date();
|
|
7393
|
-
}
|
|
7394
6978
|
function messageOf(error) {
|
|
7395
6979
|
return error instanceof Error ? error.message : String(error);
|
|
7396
6980
|
}
|
|
7397
|
-
function taskMailbox(taskId) {
|
|
7398
|
-
return { kind: "task", taskId };
|
|
7399
|
-
}
|
|
7400
|
-
function roleMailbox(taskId, roleName) {
|
|
7401
|
-
return { kind: "role", taskId, roleName };
|
|
7402
|
-
}
|
|
7403
|
-
function leaderMailbox(taskId) {
|
|
7404
|
-
return roleMailbox(taskId, LEADER_ROLE);
|
|
7405
|
-
}
|
|
7406
|
-
function taskRef(id) {
|
|
7407
|
-
return { type: "task", id };
|
|
7408
|
-
}
|
|
7409
6981
|
function runRef(taskId, id) {
|
|
7410
6982
|
return { type: "run", taskId, id };
|
|
7411
6983
|
}
|
|
@@ -7418,14 +6990,3 @@ function messageRef(taskId, id) {
|
|
|
7418
6990
|
function eventRef(taskId, id) {
|
|
7419
6991
|
return { type: "event", taskId, id };
|
|
7420
6992
|
}
|
|
7421
|
-
function notifyMailbox(runtime, target, compatibilityTaskId) {
|
|
7422
|
-
if (runtime?.notifyMailboxChanged !== undefined) {
|
|
7423
|
-
runtime.notifyMailboxChanged(target);
|
|
7424
|
-
}
|
|
7425
|
-
else {
|
|
7426
|
-
runtime?.notifyStateChanged(compatibilityTaskId);
|
|
7427
|
-
}
|
|
7428
|
-
}
|
|
7429
|
-
function notifyReviewMailbox(_options, runtime, target, compatibilityTaskId) {
|
|
7430
|
-
notifyMailbox(runtime, target, compatibilityTaskId);
|
|
7431
|
-
}
|