@zq-silk/yui 0.15.11 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +11 -6
- package/ARCHITECTURE.zh-CN.md +8 -4
- package/README.md +13 -5
- package/dist/agentRun/agentRun.js +3 -0
- package/dist/artifacts/artifactCommitLock.js +16 -55
- package/dist/artifacts/taskArtifactRepository.js +3 -3
- package/dist/cli/agentConfigurationPicker.js +1 -1
- package/dist/cli/commandCatalog.js +92 -48
- package/dist/cli/completion.js +18 -18
- package/dist/cli/completionWizard.js +1 -1
- package/dist/cli/dynamicCompletion.js +1 -1
- package/dist/cli/interactionPolicy.js +11 -3
- package/dist/cli/invocationAuthority.js +64 -0
- package/dist/cli/managedDiagnostics.js +2 -2
- package/dist/cli/parseRepeatable.js +35 -0
- package/dist/cli/updatePorts.js +26 -5
- package/dist/cli.js +1303 -1235
- package/dist/commands/agentCommands.js +4 -4
- package/dist/commands/capabilityCommands.js +1 -1
- package/dist/commands/configCommands.js +6 -57
- package/dist/commands/configOverview.js +2 -2
- package/dist/commands/durableJobCommands.js +12 -6
- package/dist/commands/executionAuditCommands.js +10 -0
- package/dist/commands/globalRoleCommands.js +78 -29
- package/dist/commands/grantCommands.js +0 -3
- package/dist/commands/jobCommands.js +1 -4
- package/dist/commands/operatorCommands.js +3 -3
- package/dist/commands/projectCommands.js +45 -25
- package/dist/commands/resourcesCommands.js +10 -40
- package/dist/commands/roleConfiguration.js +2 -6
- package/dist/commands/taskActivationCommands.js +4 -8
- package/dist/commands/taskAgentCapabilities.js +9 -0
- package/dist/commands/taskCommandSupport.js +155 -0
- package/dist/commands/taskCommandTypes.js +1 -0
- package/dist/commands/taskCommands.js +355 -732
- package/dist/commands/taskCompletionGate.js +1 -1
- package/dist/commands/taskExecutionCommands.js +1 -1
- package/dist/commands/taskFactCommands.js +325 -0
- package/dist/commands/taskInputCommands.js +12 -42
- package/dist/commands/taskIntegrationCommands.js +20 -58
- package/dist/commands/taskNextActionCommand.js +7 -6
- package/dist/commands/taskPublicationAdoptCommand.js +127 -0
- package/dist/commands/taskPublicationCommands.js +12 -3
- package/dist/commands/taskPublicationVerifyCommand.js +24 -40
- package/dist/commands/taskRemoteDeliveryCommand.js +5 -86
- package/dist/commands/taskUpstreamCommands.js +19 -8
- package/dist/commands/workflowCommands.js +6 -1
- package/dist/completion/completionInstaller.js +26 -26
- package/dist/completion/completionState.js +26 -26
- package/dist/completion/fileCompletionManager.js +6 -11
- package/dist/config/configCatalog.js +0 -2
- package/dist/config/timeZone.js +14 -0
- package/dist/config/yuiConfig.js +0 -29
- package/dist/context/runContextPack.js +5 -3
- package/dist/context/taskCatalog.js +185 -0
- package/dist/context/taskContext.js +152 -151
- package/dist/context/wakeRunReferences.js +11 -0
- package/dist/controller/agentCapabilities.js +58 -0
- package/dist/controller/agentRuntimeObserver.js +6 -11
- package/dist/controller/clientRuntime.js +22 -14
- package/dist/controller/controller.js +20 -24
- package/dist/controller/fileSchedulerStoreAdapter.js +191 -225
- package/dist/controller/globalInputDelivery.js +13 -0
- package/dist/controller/jobClient.js +4 -4
- package/dist/controller/jobControl.js +60 -25
- package/dist/controller/jobSupervisor.js +4 -4
- package/dist/controller/providerRetryAdmission.js +100 -0
- package/dist/controller/providerRetryDelivery.js +218 -0
- package/dist/controller/runtime.js +85 -43
- package/dist/controller/runtimeEventProcessor.js +0 -5
- package/dist/controller/sessionOwnerReconciliation.js +5 -0
- package/dist/controller/taskAgentError.js +35 -0
- package/dist/coordination/workMailboxQueue.js +2 -24
- package/dist/core/controllerClient.js +2 -2
- package/dist/core/fileLockOwner.js +74 -0
- package/dist/decision/decision.js +17 -0
- package/dist/doctor/doctor.js +18 -9
- package/dist/event/taskEvent.js +12 -0
- package/dist/execution/workItemExecutionProjection.js +2 -2
- package/dist/executor/agentCapabilityConfig.js +43 -0
- package/dist/executor/agentConfigurationCatalog.js +49 -11
- package/dist/executor/agentExecutor.js +5 -8
- package/dist/executor/fileRoleLaunchPlanner.js +11 -53
- package/dist/executor/taskAgentCapabilities.js +64 -0
- package/dist/integration/deliveryObligation.js +1 -69
- package/dist/integration/gitIntegrationService.js +79 -90
- package/dist/integration/integrationAttempt.js +3 -14
- package/dist/integration/integrationSourceApplication.js +4 -12
- package/dist/integration/manifestTags.js +2 -2
- package/dist/job/durableJob.js +4 -25
- package/dist/job/jobAssignmentScope.js +22 -0
- package/dist/job/jobOperation.js +16 -0
- package/dist/job/jobRunner.js +2 -1
- package/dist/job/stepDirectory.js +15 -0
- package/dist/kernel/builtinCapabilities.js +15 -11
- package/dist/kernel/kernelPorts.js +1 -17
- package/dist/lifecycle/exactRunTerminalization.js +5 -10
- package/dist/message/globalProviderRetry.js +15 -0
- package/dist/message/inputControlResolution.js +3 -4
- package/dist/message/message.js +17 -20
- package/dist/message/messageContinuation.js +1 -1
- package/dist/milestone/milestone.js +11 -0
- package/dist/observability/executionAudit.js +19 -0
- package/dist/observability/orchestrationMetrics.js +24 -1
- package/dist/observability/runtimeIdentity.js +1 -17
- package/dist/output/agentConfigurationPresentation.js +2 -0
- package/dist/output/timePresentation.js +1 -14
- package/dist/plugins/pluginService.js +12 -4
- package/dist/release/releaseWorkflowPorts.js +45 -96
- package/dist/release/runtimeRelease.js +9 -15
- package/dist/repository/gitWorkspace.js +371 -105
- package/dist/repository/projectMaintenanceLock.js +98 -82
- package/dist/repository/taskBaseFreshness.js +1 -1
- package/dist/repository/taskWorkspaceCoordinator.js +82 -144
- package/dist/repository/taskWorkspacePreparer.js +113 -47
- package/dist/repository/workspaceCleanupInspection.js +187 -0
- package/dist/resources/autoResourceGc.js +4 -77
- package/dist/resources/liveReferences.js +7 -54
- package/dist/resources/resourceDiscovery.js +5 -118
- package/dist/resources/resourceGc.js +233 -181
- package/dist/resources/resourceRegistrar.js +5 -4
- package/dist/resources/resourceRegistry.js +3 -37
- package/dist/resources/resourceRegistryStore.js +13 -36
- package/dist/resources/sqliteResourceRegistry.js +43 -26
- package/dist/review/deltaRecheck.js +1 -1
- package/dist/review/reviewAcceptance.js +1 -1
- package/dist/review/reviewDecision.js +1 -1
- package/dist/review/reviewRound.js +8 -7
- package/dist/review/taskFinalReviewContractResolution.js +1 -1
- package/dist/runtime/acpProtocol.js +4 -51
- package/dist/runtime/acpSession.js +10 -75
- package/dist/runtime/acpSessionConfiguration.js +1 -8
- package/dist/runtime/agentEndpoint.js +3 -2
- package/dist/runtime/agentError.js +5 -3
- package/dist/runtime/agentFailureContext.js +43 -0
- package/dist/runtime/agentHost.js +42 -25
- package/dist/runtime/builtinAgentErrorMappers.js +91 -0
- package/dist/runtime/codexAppServerRuntime.js +34 -3
- package/dist/runtime/codexInteractiveHost.js +2 -2
- package/dist/runtime/index.js +0 -1
- package/dist/runtime/managedCaller.js +21 -1
- package/dist/runtime/providerControl.js +5 -1
- package/dist/runtime/providerErrors.js +38 -0
- package/dist/runtime/providerRetry.js +198 -0
- package/dist/runtime/providerRuntimeIdentity.js +28 -2
- package/dist/runtime/runtimeObservation.js +0 -5
- package/dist/runtime/runtimeSessionCandidate.js +2 -3
- package/dist/runtime/sessionOwnerIdentity.js +1 -1
- package/dist/runtime/sessionTokenMetrics.js +15 -5
- package/dist/runtime/structuredProviderHost.js +10 -43
- package/dist/runtime/taskUsageMetrics.js +275 -0
- package/dist/runtime/tmuxAdapters.js +6 -8
- package/dist/scheduler/activeRoleRunDelivery.js +19 -7
- package/dist/scheduler/leaderWakeupProcessor.js +24 -6
- package/dist/scheduler/operatorEvent.js +12 -20
- package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
- package/dist/scheduler/ports.js +5 -8
- package/dist/scheduler/roleRunLiveness.js +4 -4
- package/dist/scheduler/roleRunStall.js +16 -23
- package/dist/scheduler/taskExecutionProjection.js +49 -36
- package/dist/scheduler/taskObservabilityProjection.js +6 -45
- package/dist/scheduler/taskWake.js +5 -10
- package/dist/scheduler/wakeReason.js +2 -0
- package/dist/scheduler/wakeupQueue.js +2 -4
- package/dist/setup/setupCommand.js +1 -1
- package/dist/storage/contextRecords.js +106 -0
- package/dist/storage/currentTaskStore.js +1 -1
- package/dist/storage/homeLayout.js +13 -17
- package/dist/storage/migrations/agentFailureContext.js +22 -0
- package/dist/storage/migrations/currentInputContract.js +86 -0
- package/dist/storage/migrations/currentRuntimeContract.js +228 -0
- package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
- package/dist/storage/migrations/integrationContinuation.js +5 -4
- package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
- package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
- package/dist/storage/migrations/verificationPlanV1.js +162 -0
- package/dist/storage/migrations/verificationPolicy.js +74 -0
- package/dist/storage/migrations/workItemHistory.js +46 -0
- package/dist/storage/persistenceWorker.js +12 -4
- package/dist/storage/recordValidation.js +87 -0
- package/dist/storage/sqliteSchema.js +173 -1
- package/dist/storage/sqliteStore.js +172 -161
- package/dist/storage/storageSchema.js +2 -14
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +16 -7
- package/dist/storage/taskCatalog.js +123 -0
- package/dist/storage/taskStore.js +5 -7
- package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
- package/dist/task/archiveDiagnostics.js +1 -0
- package/dist/task/archivePreflight.js +124 -0
- package/dist/task/completionReadiness.js +5 -24
- package/dist/task/draftPlan.js +0 -53
- package/dist/task/nextAction.js +7 -13
- package/dist/task/publicationAdoption.js +56 -0
- package/dist/task/publicationReference.js +10 -0
- package/dist/task/remoteDelivery.js +31 -16
- package/dist/task/remoteDeliveryService.js +89 -0
- package/dist/task/taskActivation.js +2 -25
- package/dist/task/taskActivationService.js +0 -2
- package/dist/task/taskRecordReference.js +0 -1
- package/dist/task/taskSubmission.js +3 -14
- package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
- package/dist/telemetry/sqliteTelemetryStore.js +49 -49
- package/dist/telemetry/telemetryWiring.js +4 -3
- package/dist/tmux/tmuxManager.js +29 -20
- package/dist/verification/gateArtifact.js +15 -24
- package/dist/verification/gateArtifactStore.js +14 -7
- package/dist/verification/verificationGateService.js +29 -88
- package/dist/verification/verificationPlan.js +27 -72
- package/dist/web/assets/client/app.js +92 -17
- package/dist/web/assets/client/components.js +55 -13
- package/dist/web/assets/client/i18n.js +72 -4
- package/dist/web/assets/client/taskSurface.js +6 -5
- package/dist/web/assets/client/view.js +35 -7
- package/dist/web/assets/shell.js +6 -0
- package/dist/web/assets/styles/layout.js +7 -0
- package/dist/web/webServer.js +12 -3
- package/dist/web/webSnapshot.js +14 -85
- package/dist/web/webTaskSurface.js +17 -46
- package/dist/workItem/workItem.js +1 -12
- package/dist/workspace/cleanupInspection.js +63 -0
- package/dist/workspace/workItemChangeSetManager.js +110 -50
- package/docs/agent-result-consumption.md +4 -0
- package/docs/agent-result-consumption.zh-CN.md +3 -0
- package/docs/agent-runtime-drivers.md +7 -0
- package/docs/agent-runtime-drivers.zh-CN.md +5 -0
- package/docs/architecture/README.md +2 -0
- package/docs/architecture/README.zh-CN.md +3 -1
- package/docs/architecture/capabilities-and-resources.md +42 -5
- package/docs/architecture/capabilities-and-resources.zh-CN.md +33 -3
- package/docs/managed-turn-and-session-runtime.md +121 -1
- package/docs/managed-turn-and-session-runtime.zh-CN.md +96 -2
- package/docs/observability/README.md +62 -0
- package/docs/observability/README.zh-CN.md +47 -0
- package/docs/plugin-sdk.md +4 -2
- package/docs/project-refresh.md +77 -0
- package/docs/project-refresh.zh-CN.md +59 -0
- package/docs/provider-retry.md +70 -0
- package/docs/provider-runtime.md +3 -1
- package/docs/provider-runtime.zh-CN.md +4 -2
- package/docs/release-workflow.md +190 -7
- package/docs/release-workflow.zh-CN.md +139 -5
- package/docs/roles-and-configuration.md +29 -0
- package/docs/roles-and-configuration.zh-CN.md +21 -0
- package/docs/sqlite-control-plane-design.md +16 -3
- package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
- package/docs/task-dag-semantics.md +3 -3
- package/docs/task-dag-semantics.zh-CN.md +2 -2
- package/docs/task-delivery.md +223 -17
- package/docs/task-delivery.zh-CN.md +171 -14
- package/docs/task-discovery.md +101 -0
- package/docs/task-discovery.zh-CN.md +85 -0
- package/docs/testing/verification-levels.md +161 -9
- package/docs/testing/verification-levels.zh-CN.md +116 -9
- package/i18n/README.zh-CN.md +13 -7
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +5 -0
- package/skills/yui-leader/references/execution.md +3 -2
- package/skills/yui-leader/references/integration.md +9 -5
- package/skills/yui-leader/references/planning.md +9 -3
- package/skills/yui-operator/SKILL.md +24 -8
- package/skills/yui-reviewer/SKILL.md +4 -0
- package/skills/yui-runtime/SKILL.md +17 -2
- package/skills/yui-runtime/references/publication.md +26 -4
- package/skills/yui-runtime/references/recovery.md +71 -0
- package/dist/agent/agentRegistry.js +0 -10
- package/dist/agentRun/runIdentity.js +0 -22
- package/dist/commands/deliveryGuardPreflight.js +0 -30
- package/dist/commands/taskIntegrationQueueCommands.js +0 -226
- package/dist/commands/taskOverviewCommand.js +0 -376
- package/dist/completion/completionWizard.js +0 -125
- package/dist/context/dispatchContext.js +0 -110
- package/dist/context/wakeNotification.js +0 -144
- package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
- package/dist/integration/integrationQueueEntry.js +0 -191
- package/dist/integration/integrationQueueService.js +0 -810
- package/dist/release/fakeReleasePorts.js +0 -55
- package/dist/runtime/sessionOwnerRegistry.js +0 -137
- package/dist/task/deliveryGuard.js +0 -226
- /package/dist/{commands/taskActor.js → task/taskAuthority.js} +0 -0
|
@@ -2,8 +2,8 @@ import { usageError } from "../errors/cliError.js";
|
|
|
2
2
|
import { GitIntegrationService } from "../integration/gitIntegrationService.js";
|
|
3
3
|
import { createIntegrationAttempt } from "../integration/integrationAttempt.js";
|
|
4
4
|
import { NodeGitWorkspace } from "../repository/gitWorkspace.js";
|
|
5
|
-
import { publicationExternalKey } from "../task/publicationReference.js";
|
|
6
5
|
import { isCompletedTaskReviewEvidence } from "../review/reviewAcceptance.js";
|
|
6
|
+
import { publicationExternalKey } from "../task/publicationReference.js";
|
|
7
7
|
import { workspaceProjectEntry } from "../worktree/managedWorkspace.js";
|
|
8
8
|
/**
|
|
9
9
|
* Verify the one supported ancestry waiver before `task complete` mutates any
|
|
@@ -6,7 +6,7 @@ import { finishReviewRound, updateReviewExecutionGroup } from "../review/reviewR
|
|
|
6
6
|
import { failRun } from "../agentRun/agentRun.js";
|
|
7
7
|
import { queueLeaderWakeup } from "../scheduler/wakeupQueue.js";
|
|
8
8
|
import { startTaskExecution, stopTaskExecution } from "../task/task.js";
|
|
9
|
-
import { taskActor } from "
|
|
9
|
+
import { taskActor } from "../task/taskAuthority.js";
|
|
10
10
|
export function parseTaskExecutionStopRequest(args) {
|
|
11
11
|
const taskId = args[0];
|
|
12
12
|
if (taskId === undefined || taskId.startsWith("--")) {
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { createTaskBrief, updateTaskBrief } from "../brief/taskBrief.js";
|
|
2
|
+
import { assertContextRecordReadable, contextRecordReader } from "../context/taskContext.js";
|
|
3
|
+
import { enqueueWork } from "../coordination/workMailboxQueue.js";
|
|
4
|
+
import { createDecision, supersedeDecision } from "../decision/decision.js";
|
|
5
|
+
import { dataError, usageError } from "../errors/cliError.js";
|
|
6
|
+
import { createMilestone } from "../milestone/milestone.js";
|
|
7
|
+
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
8
|
+
import { assertTaskOpen, clock, exactPositionals, leaderActionEventPayload, leaderMailbox, notifyMailbox, optionalNonEmptyOption, output, parseMultiValueTail, parseTail, presentTime, recordTaskEvent, requiredOption, requiredText, requireTask, taskActor, taskMailbox, taskRef } from "./taskCommandSupport.js";
|
|
9
|
+
import { failureCapabilitiesCommand } from "../runtime/agentFailureContext.js";
|
|
10
|
+
export function taskBriefCommand(args, store, options) {
|
|
11
|
+
const [command, ...rest] = args;
|
|
12
|
+
if (command === "show") {
|
|
13
|
+
exactPositionals(rest, 1, "Task brief show usage: yui task brief show <task>.");
|
|
14
|
+
const task = requireTask(store, rest[0]);
|
|
15
|
+
assertContextRecordReadable(store, task.id, "task-brief", task.id, options.environment);
|
|
16
|
+
const brief = store.getTaskBrief(task.id);
|
|
17
|
+
if (brief === null) {
|
|
18
|
+
return output(`Task ${task.id} has no brief.\n`, { taskId: task.id, brief: null });
|
|
19
|
+
}
|
|
20
|
+
const timeZone = store.getConfig().timeZone;
|
|
21
|
+
return output([
|
|
22
|
+
`Task: ${task.id}`,
|
|
23
|
+
`Objective: ${brief.objective}`,
|
|
24
|
+
`Boundaries:`,
|
|
25
|
+
...(brief.boundaries.length === 0 ? [" (none)"] : brief.boundaries.map((b) => ` - ${b}`)),
|
|
26
|
+
`Technical approach: ${brief.technicalApproach || "(not defined)"}`,
|
|
27
|
+
`Current focus: ${brief.currentFocus}`,
|
|
28
|
+
`Leader summary: ${brief.leaderSummary}`,
|
|
29
|
+
`Updated by: ${brief.updatedBy}`,
|
|
30
|
+
`Updated at: ${presentTime(brief.updatedAt, timeZone)}`
|
|
31
|
+
].join("\n").concat("\n"), { taskId: task.id, brief });
|
|
32
|
+
}
|
|
33
|
+
if (command === "update") {
|
|
34
|
+
const usage = "Task brief update usage: yui task brief update <task> [--objective <text>] [--boundary <text> ...] [--approach <text>] [--focus <text>] [--leader-summary <text>].";
|
|
35
|
+
const parsed = parseMultiValueTail(rest, new Set(["--objective", "--approach", "--focus", "--leader-summary"]), new Set(["--boundary"]), usage);
|
|
36
|
+
exactPositionals(parsed.positionals, 1, usage);
|
|
37
|
+
const hasObjective = parsed.options.has("--objective");
|
|
38
|
+
const hasApproach = parsed.options.has("--approach");
|
|
39
|
+
const hasFocus = parsed.options.has("--focus");
|
|
40
|
+
const hasSummary = parsed.options.has("--leader-summary");
|
|
41
|
+
const boundaries = parsed.multiOptions.get("--boundary") ?? [];
|
|
42
|
+
if (!hasObjective && !hasApproach && !hasFocus && !hasSummary && boundaries.length === 0) {
|
|
43
|
+
throw usageError("At least one brief field is required.", usage);
|
|
44
|
+
}
|
|
45
|
+
const now = clock(options);
|
|
46
|
+
const result = store.transaction((tx) => {
|
|
47
|
+
const task = requireTask(tx, parsed.positionals[0]);
|
|
48
|
+
assertTaskOpen(task);
|
|
49
|
+
const existing = tx.getTaskBrief(task.id);
|
|
50
|
+
const updatedBy = taskActor(tx, options, task.id);
|
|
51
|
+
const brief = existing === null
|
|
52
|
+
? createTaskBrief({
|
|
53
|
+
objective: requiredText(parsed.options.get("--objective"), "--objective"),
|
|
54
|
+
boundaries,
|
|
55
|
+
...(hasApproach
|
|
56
|
+
? { technicalApproach: requiredText(parsed.options.get("--approach"), "--approach") }
|
|
57
|
+
: {}),
|
|
58
|
+
currentFocus: requiredText(parsed.options.get("--focus"), "--focus"),
|
|
59
|
+
leaderSummary: requiredText(parsed.options.get("--leader-summary"), "--leader-summary"),
|
|
60
|
+
updatedBy
|
|
61
|
+
}, now)
|
|
62
|
+
: updateTaskBrief(existing, {
|
|
63
|
+
...(hasObjective ? { objective: parsed.options.get("--objective") } : {}),
|
|
64
|
+
...(boundaries.length > 0 ? { boundaries } : {}),
|
|
65
|
+
...(hasApproach
|
|
66
|
+
? { technicalApproach: parsed.options.get("--approach") }
|
|
67
|
+
: {}),
|
|
68
|
+
...(hasFocus ? { currentFocus: parsed.options.get("--focus") } : {}),
|
|
69
|
+
...(hasSummary ? { leaderSummary: parsed.options.get("--leader-summary") } : {})
|
|
70
|
+
}, updatedBy, now);
|
|
71
|
+
tx.saveTaskBrief(task.id, brief);
|
|
72
|
+
recordTaskEvent(tx, task.id, "brief.updated", {
|
|
73
|
+
updatedBy,
|
|
74
|
+
previous: JSON.stringify(existing), current: JSON.stringify(brief)
|
|
75
|
+
}, now);
|
|
76
|
+
enqueueWork(tx, taskMailbox(task.id), "brief-updated", now, [taskRef(task.id)]);
|
|
77
|
+
if (task.status === "active" && updatedBy !== "leader") {
|
|
78
|
+
enqueueWork(tx, leaderMailbox(task.id), "brief-updated", now, [taskRef(task.id)]);
|
|
79
|
+
}
|
|
80
|
+
return { task, brief };
|
|
81
|
+
});
|
|
82
|
+
notifyMailbox(options.runtime, taskMailbox(result.task.id));
|
|
83
|
+
if (result.task.status === "active") {
|
|
84
|
+
notifyMailbox(options.runtime, leaderMailbox(result.task.id));
|
|
85
|
+
}
|
|
86
|
+
return output(`Updated brief for ${result.task.id}\n`, { taskId: result.task.id, brief: result.brief });
|
|
87
|
+
}
|
|
88
|
+
throw usageError(command === undefined
|
|
89
|
+
? "Task brief command is required."
|
|
90
|
+
: `Unknown command: task brief ${command}`);
|
|
91
|
+
}
|
|
92
|
+
export function taskDecisionCommand(args, store, options) {
|
|
93
|
+
const [command, ...rest] = args;
|
|
94
|
+
if (command === "record") {
|
|
95
|
+
const usage = "Task decision record usage: yui task decision record <task> --title <text> --rationale <text>.";
|
|
96
|
+
const parsed = parseTail(rest, new Set(["--title", "--rationale"]), usage);
|
|
97
|
+
exactPositionals(parsed.positionals, 1, usage);
|
|
98
|
+
const title = requiredOption(parsed.options, "--title");
|
|
99
|
+
const rationale = requiredOption(parsed.options, "--rationale");
|
|
100
|
+
const now = clock(options);
|
|
101
|
+
const result = store.transaction((tx) => {
|
|
102
|
+
const task = requireTask(tx, parsed.positionals[0]);
|
|
103
|
+
assertTaskOpen(task);
|
|
104
|
+
const actor = taskActor(tx, options, task.id);
|
|
105
|
+
const decision = createDecision(tx.nextDecisionId(task.id), task.id, title, rationale, now);
|
|
106
|
+
tx.saveDecision(task.id, decision);
|
|
107
|
+
recordTaskEvent(tx, task.id, "decision.recorded", {
|
|
108
|
+
decisionId: decision.id,
|
|
109
|
+
title,
|
|
110
|
+
...leaderActionEventPayload(tx, task.id, options)
|
|
111
|
+
}, now);
|
|
112
|
+
enqueueWork(tx, taskMailbox(task.id), "decision-recorded", now, [taskRef(task.id)]);
|
|
113
|
+
if (task.status === "active" && actor !== "leader") {
|
|
114
|
+
enqueueWork(tx, leaderMailbox(task.id), "decision-recorded", now, [taskRef(task.id)]);
|
|
115
|
+
}
|
|
116
|
+
return { task, decision };
|
|
117
|
+
});
|
|
118
|
+
notifyMailbox(options.runtime, taskMailbox(result.task.id));
|
|
119
|
+
if (result.task.status === "active")
|
|
120
|
+
notifyMailbox(options.runtime, leaderMailbox(result.task.id));
|
|
121
|
+
return output(`Recorded decision ${result.decision.id} for ${result.task.id}\n`);
|
|
122
|
+
}
|
|
123
|
+
if (command === "list") {
|
|
124
|
+
const usage = "Task decision list usage: yui task decision list <task> [--status active|superseded].";
|
|
125
|
+
const parsed = parseTail(rest, new Set(["--status"]), usage);
|
|
126
|
+
exactPositionals(parsed.positionals, 1, usage);
|
|
127
|
+
const task = requireTask(store, parsed.positionals[0]);
|
|
128
|
+
const readable = contextRecordReader(store, task.id, options.environment);
|
|
129
|
+
let decisions = store.listDecisions(task.id).filter(record => readable("task-decision", record.id));
|
|
130
|
+
const status = parsed.options.get("--status");
|
|
131
|
+
if (status !== undefined) {
|
|
132
|
+
if (status !== "active" && status !== "superseded") {
|
|
133
|
+
throw usageError("--status must be active or superseded.", usage);
|
|
134
|
+
}
|
|
135
|
+
decisions = decisions.filter((d) => d.status === status);
|
|
136
|
+
}
|
|
137
|
+
if (decisions.length === 0) {
|
|
138
|
+
return output(`No decisions found for ${task.id}.\n`, { taskId: task.id, decisions: [] });
|
|
139
|
+
}
|
|
140
|
+
const timeZone = store.getConfig().timeZone;
|
|
141
|
+
return output(`${renderTable(`Decisions: ${task.id}`, [
|
|
142
|
+
{ header: "Decision", minWidth: 8, maxWidth: 18 },
|
|
143
|
+
{ header: "Status", minWidth: 6, maxWidth: 12 },
|
|
144
|
+
{ header: "Title", minWidth: 8, maxWidth: 64 },
|
|
145
|
+
{ header: "Created", minWidth: 10, maxWidth: 28 }
|
|
146
|
+
], decisions.map((d) => [d.id, d.status, d.title, presentTime(d.createdAt, timeZone)]), defaultTableWidth())}\n`, { taskId: task.id, decisions });
|
|
147
|
+
}
|
|
148
|
+
if (command === "show") {
|
|
149
|
+
exactPositionals(rest, 2, "Task decision show usage: yui task decision show <task> <decision>.");
|
|
150
|
+
const task = requireTask(store, rest[0]);
|
|
151
|
+
assertContextRecordReadable(store, task.id, "task-decision", rest[1], options.environment);
|
|
152
|
+
const decision = store.getDecision(task.id, rest[1]);
|
|
153
|
+
if (decision === null)
|
|
154
|
+
throw dataError(`Decision not found: ${rest[1]}.`);
|
|
155
|
+
const timeZone = store.getConfig().timeZone;
|
|
156
|
+
return output([
|
|
157
|
+
`Decision: ${decision.id}`,
|
|
158
|
+
`Task: ${task.id}`,
|
|
159
|
+
`Title: ${decision.title}`,
|
|
160
|
+
`Rationale: ${decision.rationale}`,
|
|
161
|
+
`Status: ${decision.status}`,
|
|
162
|
+
...(decision.supersededReason === undefined ? [] : [`Superseded reason: ${decision.supersededReason}`]),
|
|
163
|
+
...(decision.supersededAt === undefined ? [] : [`Superseded at: ${presentTime(decision.supersededAt, timeZone)}`]),
|
|
164
|
+
`Created: ${presentTime(decision.createdAt, timeZone)}`,
|
|
165
|
+
`Updated: ${presentTime(decision.updatedAt, timeZone)}`
|
|
166
|
+
].join("\n").concat("\n"), { taskId: task.id, decision });
|
|
167
|
+
}
|
|
168
|
+
if (command === "supersede") {
|
|
169
|
+
const usage = "Task decision supersede usage: yui task decision supersede <task> <decision> --reason <text>.";
|
|
170
|
+
const parsed = parseTail(rest, new Set(["--reason"]), usage);
|
|
171
|
+
exactPositionals(parsed.positionals, 2, usage);
|
|
172
|
+
const reason = requiredOption(parsed.options, "--reason");
|
|
173
|
+
const now = clock(options);
|
|
174
|
+
const result = store.transaction((tx) => {
|
|
175
|
+
const task = requireTask(tx, parsed.positionals[0]);
|
|
176
|
+
assertTaskOpen(task);
|
|
177
|
+
const actor = taskActor(tx, options, task.id);
|
|
178
|
+
const existing = tx.getDecision(task.id, parsed.positionals[1]);
|
|
179
|
+
if (existing === null)
|
|
180
|
+
throw dataError(`Decision not found: ${parsed.positionals[1]}.`);
|
|
181
|
+
const decision = supersedeDecision(existing, reason, now);
|
|
182
|
+
tx.saveDecision(task.id, decision);
|
|
183
|
+
recordTaskEvent(tx, task.id, "decision.superseded", {
|
|
184
|
+
decisionId: decision.id,
|
|
185
|
+
reason,
|
|
186
|
+
...leaderActionEventPayload(tx, task.id, options)
|
|
187
|
+
}, now);
|
|
188
|
+
enqueueWork(tx, taskMailbox(task.id), "decision-superseded", now, [taskRef(task.id)]);
|
|
189
|
+
if (task.status === "active" && actor !== "leader") {
|
|
190
|
+
enqueueWork(tx, leaderMailbox(task.id), "decision-superseded", now, [taskRef(task.id)]);
|
|
191
|
+
}
|
|
192
|
+
return { task, decision };
|
|
193
|
+
});
|
|
194
|
+
notifyMailbox(options.runtime, taskMailbox(result.task.id));
|
|
195
|
+
if (result.task.status === "active")
|
|
196
|
+
notifyMailbox(options.runtime, leaderMailbox(result.task.id));
|
|
197
|
+
return output(`Superseded decision ${result.decision.id} for ${result.task.id}\n`);
|
|
198
|
+
}
|
|
199
|
+
throw usageError(command === undefined
|
|
200
|
+
? "Task decision command is required."
|
|
201
|
+
: `Unknown command: task decision ${command}`);
|
|
202
|
+
}
|
|
203
|
+
export function taskMilestoneCommand(args, store, options) {
|
|
204
|
+
const [command, ...rest] = args;
|
|
205
|
+
if (command === "add") {
|
|
206
|
+
const usage = "Task milestone add usage: yui task milestone add <task> --title <text> --summary <text>.";
|
|
207
|
+
const parsed = parseTail(rest, new Set(["--title", "--summary"]), usage);
|
|
208
|
+
exactPositionals(parsed.positionals, 1, usage);
|
|
209
|
+
const title = requiredOption(parsed.options, "--title");
|
|
210
|
+
const summary = requiredOption(parsed.options, "--summary");
|
|
211
|
+
const now = clock(options);
|
|
212
|
+
const result = store.transaction((tx) => {
|
|
213
|
+
const task = requireTask(tx, parsed.positionals[0]);
|
|
214
|
+
assertTaskOpen(task);
|
|
215
|
+
const actor = taskActor(tx, options, task.id);
|
|
216
|
+
const milestone = createMilestone(tx.nextMilestoneId(task.id), task.id, title, summary, actor, now);
|
|
217
|
+
tx.saveMilestone(task.id, milestone);
|
|
218
|
+
recordTaskEvent(tx, task.id, "milestone.added", {
|
|
219
|
+
milestoneId: milestone.id,
|
|
220
|
+
title,
|
|
221
|
+
...leaderActionEventPayload(tx, task.id, options)
|
|
222
|
+
}, now);
|
|
223
|
+
enqueueWork(tx, taskMailbox(task.id), "milestone-added", now, [taskRef(task.id)]);
|
|
224
|
+
return { task, milestone };
|
|
225
|
+
});
|
|
226
|
+
notifyMailbox(options.runtime, taskMailbox(result.task.id));
|
|
227
|
+
return output(`Added milestone ${result.milestone.id} for ${result.task.id}\n`);
|
|
228
|
+
}
|
|
229
|
+
if (command === "list") {
|
|
230
|
+
exactPositionals(rest, 1, "Task milestone list usage: yui task milestone list <task>.");
|
|
231
|
+
const task = requireTask(store, rest[0]);
|
|
232
|
+
const readable = contextRecordReader(store, task.id, options.environment);
|
|
233
|
+
const milestones = store.listMilestones(task.id).filter(record => readable("task-milestone", record.id));
|
|
234
|
+
if (milestones.length === 0) {
|
|
235
|
+
return output(`No milestones found for ${task.id}.\n`, { taskId: task.id, milestones: [] });
|
|
236
|
+
}
|
|
237
|
+
const timeZone = store.getConfig().timeZone;
|
|
238
|
+
return output(`${renderTable(`Milestones: ${task.id}`, [
|
|
239
|
+
{ header: "Milestone", minWidth: 9, maxWidth: 18 },
|
|
240
|
+
{ header: "Title", minWidth: 8, maxWidth: 64 },
|
|
241
|
+
{ header: "Created", minWidth: 10, maxWidth: 28 }
|
|
242
|
+
], milestones.map((m) => [m.id, m.title, presentTime(m.createdAt, timeZone)]), defaultTableWidth())}\n`, { taskId: task.id, milestones });
|
|
243
|
+
}
|
|
244
|
+
if (command === "show") {
|
|
245
|
+
exactPositionals(rest, 2, "Task milestone show usage: yui task milestone show <task> <milestone>.");
|
|
246
|
+
const task = requireTask(store, rest[0]);
|
|
247
|
+
assertContextRecordReadable(store, task.id, "task-milestone", rest[1], options.environment);
|
|
248
|
+
const milestone = store.getMilestone(task.id, rest[1]);
|
|
249
|
+
if (milestone === null)
|
|
250
|
+
throw dataError(`Milestone not found: ${rest[1]}.`);
|
|
251
|
+
const timeZone = store.getConfig().timeZone;
|
|
252
|
+
return output([
|
|
253
|
+
`Milestone: ${milestone.id}`,
|
|
254
|
+
`Task: ${task.id}`,
|
|
255
|
+
`Title: ${milestone.title}`,
|
|
256
|
+
`Summary: ${milestone.summary}`,
|
|
257
|
+
`Created by: ${milestone.createdBy}`,
|
|
258
|
+
`Created: ${presentTime(milestone.createdAt, timeZone)}`
|
|
259
|
+
].join("\n").concat("\n"), { taskId: task.id, milestone });
|
|
260
|
+
}
|
|
261
|
+
throw usageError(command === undefined
|
|
262
|
+
? "Task milestone command is required."
|
|
263
|
+
: `Unknown command: task milestone ${command}`);
|
|
264
|
+
}
|
|
265
|
+
export function taskEventCommand(args, store, options = {}) {
|
|
266
|
+
const [command, ...rest] = args;
|
|
267
|
+
if (command === "list") {
|
|
268
|
+
const eventListUsage = "Task event list usage: yui task event list <task> [--after <timestamp>] [--limit <n>].";
|
|
269
|
+
const parsed = parseTail(rest, new Set(["--after", "--limit"]), eventListUsage);
|
|
270
|
+
exactPositionals(parsed.positionals, 1, eventListUsage);
|
|
271
|
+
const task = requireTask(store, parsed.positionals[0]);
|
|
272
|
+
const readable = contextRecordReader(store, task.id, options.environment);
|
|
273
|
+
let events = store.listEvents(task.id).filter(record => readable("task-event", record.id));
|
|
274
|
+
const after = optionalNonEmptyOption(parsed.options, "--after");
|
|
275
|
+
if (after !== undefined) {
|
|
276
|
+
const afterMs = Date.parse(after);
|
|
277
|
+
if (!Number.isFinite(afterMs))
|
|
278
|
+
throw usageError("--after must be a valid timestamp.", eventListUsage);
|
|
279
|
+
events = events.filter((e) => Date.parse(e.createdAt) > afterMs);
|
|
280
|
+
}
|
|
281
|
+
const limit = optionalNonEmptyOption(parsed.options, "--limit");
|
|
282
|
+
if (limit !== undefined) {
|
|
283
|
+
const n = Number(limit);
|
|
284
|
+
if (!Number.isSafeInteger(n) || n <= 0)
|
|
285
|
+
throw usageError("--limit must be a positive integer.", eventListUsage);
|
|
286
|
+
events = events.slice(-n);
|
|
287
|
+
}
|
|
288
|
+
if (events.length === 0) {
|
|
289
|
+
return output(`No events found for ${task.id}.\n`, { taskId: task.id, events: [] });
|
|
290
|
+
}
|
|
291
|
+
const timeZone = store.getConfig().timeZone;
|
|
292
|
+
return output(`${renderTable(`Events: ${task.id}`, [
|
|
293
|
+
{ header: "Event", minWidth: 8, maxWidth: 18 },
|
|
294
|
+
{ header: "Type", minWidth: 8, maxWidth: 28 },
|
|
295
|
+
{ header: "Created", minWidth: 10, maxWidth: 28 }
|
|
296
|
+
], events.map((e) => [e.id, e.type, presentTime(e.createdAt, timeZone)]), defaultTableWidth())}\n`, { taskId: task.id, events });
|
|
297
|
+
}
|
|
298
|
+
if (command === "show") {
|
|
299
|
+
exactPositionals(rest, 2, "Task event show usage: yui task event show <task> <event>.");
|
|
300
|
+
const task = requireTask(store, rest[0]);
|
|
301
|
+
assertContextRecordReadable(store, task.id, "task-event", rest[1], options.environment);
|
|
302
|
+
const events = store.listEvents(task.id);
|
|
303
|
+
const event = events.find((e) => e.id === rest[1]) ?? null;
|
|
304
|
+
if (event === null)
|
|
305
|
+
throw dataError(`Event not found: ${rest[1]}.`);
|
|
306
|
+
const timeZone = store.getConfig().timeZone;
|
|
307
|
+
const capabilitiesCommand = event.type === "runtime.agent-error" && event.payload.roleName
|
|
308
|
+
? failureCapabilitiesCommand(task.id, event.payload.roleName, event.id) : undefined;
|
|
309
|
+
return output([
|
|
310
|
+
`Event: ${event.id}`,
|
|
311
|
+
`Task: ${task.id}`,
|
|
312
|
+
`Type: ${event.type}`,
|
|
313
|
+
`Created: ${presentTime(event.createdAt, timeZone)}`,
|
|
314
|
+
...(capabilitiesCommand === undefined ? [] : [`Inspect capabilities: ${capabilitiesCommand}`]),
|
|
315
|
+
`Payload:`,
|
|
316
|
+
...(Object.keys(event.payload).length === 0
|
|
317
|
+
? [" (none)"]
|
|
318
|
+
: Object.entries(event.payload).map(([k, v]) => ` ${k}: ${v}`))
|
|
319
|
+
].join("\n").concat("\n"), { taskId: task.id, event,
|
|
320
|
+
...(capabilitiesCommand === undefined ? {} : { diagnostics: { capabilitiesCommand } }) });
|
|
321
|
+
}
|
|
322
|
+
throw usageError(command === undefined
|
|
323
|
+
? "Task event command is required."
|
|
324
|
+
: `Unknown command: task event ${command}`);
|
|
325
|
+
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
+
import { enqueueWork } from "../coordination/workMailboxQueue.js";
|
|
1
2
|
import { dataError, roleNotFound, taskNotFound, usageError } from "../errors/cliError.js";
|
|
2
3
|
import { createTaskEvent } from "../event/taskEvent.js";
|
|
3
|
-
import { activeLiveRoleAgentSession } from "../executor/agentExecutor.js";
|
|
4
4
|
import { answerInputRequest, cancelInputRequest, createInputRequest } from "../input/inputRequest.js";
|
|
5
5
|
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
6
6
|
import { formatTimestamp } from "../output/timePresentation.js";
|
|
7
|
-
import {
|
|
7
|
+
import { requireManagedTaskCaller, requireManagedGlobalCaller } from "../runtime/managedCaller.js";
|
|
8
8
|
import { isRoleRunStalled, RUN_RECOVERED_EVENT } from "../scheduler/roleRunStall.js";
|
|
9
|
-
import { requireManagedTaskCaller } from "../runtime/managedCaller.js";
|
|
10
9
|
import { resolveTaskRecordReference } from "../task/taskRecordReference.js";
|
|
11
10
|
const LEADER_ROLE = "leader";
|
|
12
11
|
export function runTaskInputCommand(args, store, options) {
|
|
@@ -84,7 +83,7 @@ function createRequest(args, store, options) {
|
|
|
84
83
|
}, now);
|
|
85
84
|
return created;
|
|
86
85
|
});
|
|
87
|
-
notifyMailbox(options, { kind: "operator" }
|
|
86
|
+
notifyMailbox(options, { kind: "operator" });
|
|
88
87
|
return output(`Created input request ${request.id} for ${request.taskId}\n`, { request });
|
|
89
88
|
}
|
|
90
89
|
function listRequests(args, store) {
|
|
@@ -165,7 +164,7 @@ function answerRequest(args, store, options) {
|
|
|
165
164
|
enqueueWork(tx, { kind: "role", taskId: task.id, roleName: LEADER_ROLE }, `input-answered:${answered.id}`, now, [{ type: "input", taskId: task.id, id: answered.id }]);
|
|
166
165
|
return answered;
|
|
167
166
|
});
|
|
168
|
-
notifyMailbox(options, { kind: "role", taskId: request.taskId, roleName: LEADER_ROLE }
|
|
167
|
+
notifyMailbox(options, { kind: "role", taskId: request.taskId, roleName: LEADER_ROLE });
|
|
169
168
|
return output(`Answered input request ${request.id} for ${request.taskId}\n`, { request });
|
|
170
169
|
}
|
|
171
170
|
function cancelRequest(args, store, options) {
|
|
@@ -195,7 +194,7 @@ function cancelRequest(args, store, options) {
|
|
|
195
194
|
enqueueWork(tx, { kind: "role", taskId: task.id, roleName: LEADER_ROLE }, `input-cancelled:${cancelled.id}`, now, [{ type: "input", taskId: task.id, id: cancelled.id }]);
|
|
196
195
|
return cancelled;
|
|
197
196
|
});
|
|
198
|
-
notifyMailbox(options, { kind: "role", taskId: request.taskId, roleName: LEADER_ROLE }
|
|
197
|
+
notifyMailbox(options, { kind: "role", taskId: request.taskId, roleName: LEADER_ROLE });
|
|
199
198
|
return output(`Cancelled input request ${request.id} for ${request.taskId}\n`, { request });
|
|
200
199
|
}
|
|
201
200
|
function requireLeaderInputOrigin(store, taskId, environment) {
|
|
@@ -237,27 +236,13 @@ export function isCurrentGlobalOperator(store, environment) {
|
|
|
237
236
|
|| (environment.YUI_ROLE !== undefined && environment.YUI_ROLE !== "operator")
|
|
238
237
|
|| environment.YUI_TASK_ID !== undefined)
|
|
239
238
|
return false;
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
return
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
return false;
|
|
246
|
-
const sessions = store.getGlobalRoleSessionSet(role.name);
|
|
247
|
-
const session = activeLiveRoleAgentSession(sessions);
|
|
248
|
-
if (sessions === null || session === null || sessions.activeAgentId !== role.activeAgentId) {
|
|
239
|
+
try {
|
|
240
|
+
requireManagedGlobalCaller(store, { ...environment, YUI_SESSION_SCOPE: "global", YUI_ROLE: "operator" });
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
249
244
|
return false;
|
|
250
245
|
}
|
|
251
|
-
// The provider's conversation survives Host restarts and entry-point changes.
|
|
252
|
-
// Prefer its command-time identity over a launch-time environment snapshot.
|
|
253
|
-
// A launch reservation alone is not a registered Operator conversation.
|
|
254
|
-
const nativeSessionId = exactIdentity(binding.adapterId === "codex"
|
|
255
|
-
? environment.CODEX_THREAD_ID ?? environment.YUI_NATIVE_SESSION_ID
|
|
256
|
-
: environment.YUI_NATIVE_SESSION_ID);
|
|
257
|
-
return binding.agentId === session.agentId
|
|
258
|
-
&& binding.adapterId === session.adapterId
|
|
259
|
-
&& nativeSessionId !== undefined
|
|
260
|
-
&& nativeSessionId === session.nativeSessionId;
|
|
261
246
|
}
|
|
262
247
|
function inputAnswerer(environment) {
|
|
263
248
|
const env = environment ?? {};
|
|
@@ -400,16 +385,6 @@ function requiredText(value, label) {
|
|
|
400
385
|
throw usageError(`${label} is required.`);
|
|
401
386
|
return normalized;
|
|
402
387
|
}
|
|
403
|
-
function trimmed(value) {
|
|
404
|
-
const normalized = value?.trim();
|
|
405
|
-
return normalized === undefined || normalized.length === 0 ? undefined : normalized;
|
|
406
|
-
}
|
|
407
|
-
function exactIdentity(value) {
|
|
408
|
-
if (value === undefined || value.includes("\0"))
|
|
409
|
-
return undefined;
|
|
410
|
-
const normalized = value.trim();
|
|
411
|
-
return normalized.length === 0 || normalized !== value ? undefined : normalized;
|
|
412
|
-
}
|
|
413
388
|
function timeoutAfter(now, value) {
|
|
414
389
|
if (!/^[1-9][0-9]*$/.test(value)) {
|
|
415
390
|
throw usageError("--timeout-seconds must be a positive integer.");
|
|
@@ -487,11 +462,6 @@ function parseMultiValueTail(args, valueOptions, repeatOptions, usage) {
|
|
|
487
462
|
}
|
|
488
463
|
return { positionals, options, multiOptions };
|
|
489
464
|
}
|
|
490
|
-
function notifyMailbox(options, target
|
|
491
|
-
|
|
492
|
-
options.runtime.notifyMailboxChanged(target);
|
|
493
|
-
}
|
|
494
|
-
else {
|
|
495
|
-
options.runtime?.notifyStateChanged(compatibilityTaskId);
|
|
496
|
-
}
|
|
465
|
+
function notifyMailbox(options, target) {
|
|
466
|
+
options.runtime?.notifyMailboxChanged(target);
|
|
497
467
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { assertProjectActive, resolveProject } from "../repository/project.js";
|
|
3
|
-
import { workspaceProjectEntry } from "../worktree/managedWorkspace.js";
|
|
1
|
+
import { parseRepeatable } from "../cli/parseRepeatable.js";
|
|
4
2
|
import { usageError } from "../errors/cliError.js";
|
|
5
|
-
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
6
|
-
import { createIntegrationAttempt, recordResolutionDecision, supersedeIntegration } from "../integration/integrationAttempt.js";
|
|
7
3
|
import { GitIntegrationService } from "../integration/gitIntegrationService.js";
|
|
4
|
+
import { createIntegrationAttempt, recordResolutionDecision, supersedeIntegration } from "../integration/integrationAttempt.js";
|
|
5
|
+
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
6
|
+
import { NodeGitWorkspace } from "../repository/gitWorkspace.js";
|
|
7
|
+
import { assertProjectActive, resolveProject } from "../repository/project.js";
|
|
8
8
|
import { FileTaskWorkspacePreparer } from "../repository/taskWorkspacePreparer.js";
|
|
9
|
-
import {
|
|
10
|
-
import { assertTaskDeliveryAuthority as taskLocalActor } from "./taskActor.js";
|
|
9
|
+
import { assertTaskDeliveryAuthority as taskLocalActor } from "../task/taskAuthority.js";
|
|
11
10
|
import { resolveTaskRecordReference } from "../task/taskRecordReference.js";
|
|
12
11
|
import { governingWorkItemCandidate } from "../workItem/workItem.js";
|
|
12
|
+
import { workspaceProjectEntry } from "../worktree/managedWorkspace.js";
|
|
13
13
|
export async function runTaskIntegrationCommand(args, store, home, options = {}) {
|
|
14
14
|
const now = options.now ?? (() => new Date());
|
|
15
15
|
const [command, ...rest] = args;
|
|
@@ -33,9 +33,6 @@ export async function runTaskIntegrationCommand(args, store, home, options = {})
|
|
|
33
33
|
return list(rest, store);
|
|
34
34
|
if (command === "show")
|
|
35
35
|
return show(rest, store, options.environment);
|
|
36
|
-
if (command === "queue") {
|
|
37
|
-
return runTaskIntegrationQueueCommand(rest, store, home, options);
|
|
38
|
-
}
|
|
39
36
|
throw usageError(command === undefined
|
|
40
37
|
? "Task Integration command is required."
|
|
41
38
|
: `Unknown command: task integration ${command}`);
|
|
@@ -64,7 +61,9 @@ async function cleanupIntegration(args, store, home, environment) {
|
|
|
64
61
|
+ `${activeIntegrationJob.id}/${activeIntegrationJob.status}. `
|
|
65
62
|
+ "Cancel or acknowledge it before cleanup.");
|
|
66
63
|
}
|
|
67
|
-
const result = await new GitIntegrationService(home, store).cleanup(integration
|
|
64
|
+
const result = await new GitIntegrationService(home, store).cleanup(integration, {
|
|
65
|
+
authorize: () => taskLocalActor(store, environment, integration.taskId)
|
|
66
|
+
});
|
|
68
67
|
if (result === "dirty") {
|
|
69
68
|
throw usageError(`Integration worktree contains unresolved or uncommitted changes: ${integration.id}. Inspect it before cleanup.`);
|
|
70
69
|
}
|
|
@@ -76,8 +75,8 @@ async function cleanupIntegration(args, store, home, environment) {
|
|
|
76
75
|
};
|
|
77
76
|
}
|
|
78
77
|
async function start(args, store, home, now, options) {
|
|
79
|
-
const usage = "Task Integration start usage: yui task integration start <task> --work-item <id> --strategy <ff|cherry-pick|merge|manual> [--project <project>] [--target <ref>] [--check <command> ...].";
|
|
80
|
-
const parsed = parseRepeatable(args, new Set(["--check"]), new Set(["--work-item", "--strategy", "--project", "--target"]), usage);
|
|
78
|
+
const usage = "Task Integration start usage: yui task integration start <task> --work-item <id> --strategy <ff|cherry-pick|merge|manual> [--project <project>] [--target <ref>] [--check <command> ...] [--rerun-checks].";
|
|
79
|
+
const parsed = parseRepeatable(args, new Set(["--check"]), new Set(["--work-item", "--strategy", "--project", "--target"]), usage, new Set(["--rerun-checks"]));
|
|
81
80
|
if (parsed.positionals.length !== 1)
|
|
82
81
|
throw usageError(usage);
|
|
83
82
|
let task = store.getTask(parsed.positionals[0]);
|
|
@@ -154,7 +153,8 @@ async function start(args, store, home, now, options) {
|
|
|
154
153
|
resultCommit,
|
|
155
154
|
strategy
|
|
156
155
|
},
|
|
157
|
-
checkCommands: parsed.many.get("--check") ?? []
|
|
156
|
+
checkCommands: parsed.many.get("--check") ?? [],
|
|
157
|
+
rerunChecks: parsed.one.has("--rerun-checks")
|
|
158
158
|
}, now());
|
|
159
159
|
tx.saveIntegrationAttempt(task.id, created);
|
|
160
160
|
return created;
|
|
@@ -191,7 +191,7 @@ async function runIntegration(store, home, integration, now, options) {
|
|
|
191
191
|
: `Integration ${result.attempt.id} failed; inspect its target and preserved evidence\n`;
|
|
192
192
|
return { output, data: result };
|
|
193
193
|
}
|
|
194
|
-
function resolveDecision(args, store, now, environment,
|
|
194
|
+
function resolveDecision(args, store, now, environment, _home) {
|
|
195
195
|
const usage = "Task Integration resolve usage: yui task integration resolve <task>/<integration> --option <manual-resolution|reject> --rationale <text>.";
|
|
196
196
|
const parsed = parseRepeatable(args, new Set(), new Set(["--option", "--rationale"]), usage);
|
|
197
197
|
if (parsed.positionals.length !== 1)
|
|
@@ -237,7 +237,7 @@ async function abortIntegration(args, store, now, options, home) {
|
|
|
237
237
|
data: { integration: settled }
|
|
238
238
|
};
|
|
239
239
|
}
|
|
240
|
-
function supersedeIntegrationCommand(args, store, now, environment,
|
|
240
|
+
function supersedeIntegrationCommand(args, store, now, environment, _home) {
|
|
241
241
|
const usage = "Task Integration supersede usage: yui task integration supersede <task>/<integration> --reason <text>.";
|
|
242
242
|
const parsed = parseRepeatable(args, new Set(), new Set(["--reason"]), usage);
|
|
243
243
|
if (parsed.positionals.length !== 1)
|
|
@@ -253,17 +253,6 @@ function supersedeIntegrationCommand(args, store, now, environment, home) {
|
|
|
253
253
|
// Superseding a committed Integration rewrites delivery-baseline evidence
|
|
254
254
|
// and audit history, so it remains an explicit Task-control decision.
|
|
255
255
|
taskLocalActor(store, environment, integration.taskId);
|
|
256
|
-
// A queue-backed committed Attempt cannot be superseded: the queue entry
|
|
257
|
-
// would remain in its current status while its Attempt becomes "superseded",
|
|
258
|
-
// leaving contradictory terminal records that never converge. This covers
|
|
259
|
-
// the crash window where the entry is still "running" or "conflicted" after
|
|
260
|
-
// the Attempt committed.
|
|
261
|
-
const queueBacked = store.listIntegrationQueueEntries(integration.taskId)
|
|
262
|
-
.some((entry) => entry.integrationAttemptId === integration.id);
|
|
263
|
-
if (queueBacked) {
|
|
264
|
-
throw usageError(`Integration ${integration.id} is backed by a queue entry; `
|
|
265
|
-
+ "reconcile the queue entry instead of superseding its Attempt.");
|
|
266
|
-
}
|
|
267
256
|
return store.transaction((tx) => {
|
|
268
257
|
const current = tx.getIntegrationAttempt(integration.taskId, integration.id);
|
|
269
258
|
if (current === null) {
|
|
@@ -328,6 +317,9 @@ function show(args, store, environment) {
|
|
|
328
317
|
`Candidate: ${integration.candidateCommit ?? "-"}`,
|
|
329
318
|
`After commit: ${integration.afterCommit ?? "-"}`,
|
|
330
319
|
`Job: ${integration.jobId ?? "-"}`,
|
|
320
|
+
`Check execution: ${integration.rerunChecks ? "explicit rerun"
|
|
321
|
+
: integration.checkCommands.length > 0 ? "explicit check commands"
|
|
322
|
+
: "reuse exact successful evidence when available"}`,
|
|
331
323
|
`Source: ${integrationSourceLabel(integration)}`,
|
|
332
324
|
`Status: ${integration.status}`,
|
|
333
325
|
`Summary: ${integration.summary ?? "-"}`,
|
|
@@ -351,9 +343,7 @@ function integrationSourceLabel(integration) {
|
|
|
351
343
|
if (integration.source.kind === "work-item") {
|
|
352
344
|
return `work-item:${integration.source.workItemId}@${integration.source.resultCommit.slice(0, 12)}`;
|
|
353
345
|
}
|
|
354
|
-
return integration.source.
|
|
355
|
-
? `upstream:${integration.source.branch}@${integration.source.remoteCommit.slice(0, 12)}`
|
|
356
|
-
: `historical:${integration.source.changeSetIds.join(",")}`;
|
|
346
|
+
return `upstream:${integration.source.branch}@${integration.source.remoteCommit.slice(0, 12)}`;
|
|
357
347
|
}
|
|
358
348
|
function requireIntegration(store, value, environment) {
|
|
359
349
|
let reference;
|
|
@@ -373,31 +363,3 @@ function requireIntegration(store, value, environment) {
|
|
|
373
363
|
}
|
|
374
364
|
return attempt;
|
|
375
365
|
}
|
|
376
|
-
export function parseRepeatable(args, repeatable, singular, usage) {
|
|
377
|
-
const positionals = [];
|
|
378
|
-
const many = new Map();
|
|
379
|
-
const one = new Map();
|
|
380
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
381
|
-
const value = args[index];
|
|
382
|
-
if (!value.startsWith("--")) {
|
|
383
|
-
positionals.push(value);
|
|
384
|
-
continue;
|
|
385
|
-
}
|
|
386
|
-
if (!repeatable.has(value) && !singular.has(value)) {
|
|
387
|
-
throw usageError(`Unsupported option: ${value}.`, usage);
|
|
388
|
-
}
|
|
389
|
-
if (singular.has(value) && one.has(value)) {
|
|
390
|
-
throw usageError(`Option may only be specified once: ${value}.`, usage);
|
|
391
|
-
}
|
|
392
|
-
const optionValue = args[index + 1];
|
|
393
|
-
if (optionValue === undefined || optionValue.startsWith("--")) {
|
|
394
|
-
throw usageError(`${value} is required.`, usage);
|
|
395
|
-
}
|
|
396
|
-
if (repeatable.has(value))
|
|
397
|
-
many.set(value, [...(many.get(value) ?? []), optionValue]);
|
|
398
|
-
else
|
|
399
|
-
one.set(value, optionValue);
|
|
400
|
-
index += 1;
|
|
401
|
-
}
|
|
402
|
-
return { positionals, many, one };
|
|
403
|
-
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { taskNotFound, usageError } from "../errors/cliError.js";
|
|
2
|
-
import { projectNextAction } from "../task/nextAction.js";
|
|
3
|
-
import { projectCompletionReadiness } from "../task/completionReadiness.js";
|
|
4
2
|
import { projectTaskOrchestration } from "../observability/orchestrationMetrics.js";
|
|
5
|
-
import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
|
|
6
|
-
import { buildTaskExecutionProjection } from "../scheduler/taskExecutionProjection.js";
|
|
7
3
|
import { projectReviewDecision } from "../review/reviewDecision.js";
|
|
8
|
-
import {
|
|
4
|
+
import { buildTaskExecutionProjection } from "../scheduler/taskExecutionProjection.js";
|
|
5
|
+
import { projectCompletionReadiness } from "../task/completionReadiness.js";
|
|
6
|
+
import { projectNextAction } from "../task/nextAction.js";
|
|
7
|
+
import { projectTaskRemoteDeliveryFromStore } from "../task/remoteDeliveryService.js";
|
|
8
|
+
import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
|
|
9
|
+
import { renderTaskRemoteDelivery } from "./taskRemoteDeliveryCommand.js";
|
|
9
10
|
/**
|
|
10
11
|
* Issue 07 (Leader convergence): read-only `yui task next-action <task>`.
|
|
11
12
|
* Folds the existing durable records into exactly one protocol-level next
|
|
@@ -34,7 +35,7 @@ export function runTaskNextActionCommand(args, store, currentTaskReviewCandidate
|
|
|
34
35
|
const facts = reader.readNextActionFacts(taskId);
|
|
35
36
|
if (facts === null)
|
|
36
37
|
throw taskNotFound(taskId);
|
|
37
|
-
const execution = buildTaskExecutionProjection(reader, taskId,
|
|
38
|
+
const execution = buildTaskExecutionProjection(reader, taskId, now);
|
|
38
39
|
if (execution === null)
|
|
39
40
|
throw taskNotFound(taskId);
|
|
40
41
|
const actionFacts = {
|