@zq-silk/yui 0.14.1 → 0.14.2
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 +64 -35
- package/README.md +254 -124
- package/dist/agent/managedRuntimeEnvironment.js +5 -8
- package/dist/cli/commandCatalog.js +74 -109
- package/dist/cli/helpRenderer.js +1 -2
- package/dist/cli/interactionCandidates.js +10 -0
- package/dist/cli/interactionPolicy.js +76 -37
- package/dist/cli/roleWizard.js +5 -6
- package/dist/cli/updateOrchestrator.js +10 -8
- package/dist/cli/updatePorts.js +17 -13
- package/dist/cli/upgradeCommand.js +2 -5
- package/dist/cli.js +268 -231
- package/dist/commands/agentCommands.js +13 -0
- package/dist/commands/configCommands.js +7 -19
- package/dist/commands/configOverview.js +3 -2
- package/dist/commands/durableJobCommands.js +1 -1
- package/dist/commands/executionAuditCommands.js +4 -9
- package/dist/commands/globalRoleCommands.js +17 -0
- package/dist/commands/profileCommands.js +200 -30
- package/dist/commands/sessionCommands.js +1 -1
- package/dist/commands/taskActor.js +29 -127
- package/dist/commands/taskChangeSetCommands.js +1 -3
- package/dist/commands/taskCommands.js +1771 -2107
- package/dist/commands/taskCompletionGate.js +27 -103
- package/dist/commands/taskContextCommand.js +47 -80
- package/dist/commands/taskExecutionCommands.js +9 -27
- package/dist/commands/taskInputCommands.js +10 -10
- package/dist/commands/taskIntegrationCommands.js +69 -61
- package/dist/commands/taskIntegrationQueueCommands.js +1 -1
- package/dist/commands/taskNextActionCommand.js +8 -29
- package/dist/commands/taskOverviewCommand.js +25 -3
- package/dist/commands/taskPublicationCommands.js +151 -85
- package/dist/commands/taskPublicationVerifyCommand.js +181 -0
- package/dist/commands/taskRemoteDeliveryCommand.js +134 -0
- package/dist/commands/taskRoleRuntimeStatus.js +5 -6
- package/dist/commands/taskUpstreamCommands.js +79 -95
- package/dist/config/configCatalog.js +1 -1
- package/dist/context/contextSnapshot.js +17 -2
- package/dist/context/sourceTurnContext.js +30 -0
- package/dist/context/turnContextPack.js +107 -48
- package/dist/context/turnInputContract.js +11 -4
- package/dist/context/wakeNotification.js +84 -22
- package/dist/controller/agentRuntimeObserver.js +9 -12
- package/dist/controller/clientRuntime.js +13 -13
- package/dist/controller/controller.js +11 -11
- package/dist/controller/fileSchedulerStoreAdapter.js +262 -219
- package/dist/controller/jobControl.js +11 -10
- package/dist/controller/resourceInventory.js +6 -6
- package/dist/controller/resourceInventoryLinux.js +2 -2
- package/dist/controller/runtime.js +18 -61
- package/dist/controller/runtimeEventInbox.js +57 -34
- package/dist/controller/runtimeEventProcessor.js +36 -96
- package/dist/controller/runtimeHookTurnFence.js +33 -57
- package/dist/controller/runtimeLaunchCoordinator.js +44 -40
- package/dist/controller/runtimeObservationHook.js +18 -12
- package/dist/controller/sessionNotify.js +43 -28
- package/dist/controller/sessionOwnerReconciliation.js +21 -21
- package/dist/controller/structuredProviderObservation.js +13 -16
- package/dist/coordination/workMailboxQueue.js +132 -0
- package/dist/core/controllerServer.js +1 -0
- package/dist/domain/agentResultTransport.js +60 -0
- package/dist/execution/executionHealth.js +111 -118
- package/dist/execution/reviewMainTurn.js +161 -0
- package/dist/execution/workItemExecution.js +454 -0
- package/dist/execution/workItemExecutionProjection.js +309 -0
- package/dist/execution/workItemMainTurn.js +164 -0
- package/dist/executor/agentExecutor.js +19 -19
- package/dist/executor/executorRegistry.js +7 -28
- package/dist/executor/fileRoleLaunchPlanner.js +16 -82
- package/dist/external/githubPublicationVerifier.js +114 -0
- package/dist/external/gitlabPublicationVerifier.js +158 -0
- package/dist/external/pinnedCommandRunner.js +73 -0
- package/dist/integration/changeSetManifest.js +0 -3
- package/dist/integration/deliveryObligation.js +43 -6
- package/dist/integration/gitIntegrationService.js +284 -101
- package/dist/integration/integrationAttempt.js +64 -14
- package/dist/integration/integrationQueueEntry.js +5 -35
- package/dist/integration/integrationQueueService.js +65 -194
- package/dist/lifecycle/canonicalLifecycleEvent.js +16 -15
- package/dist/lifecycle/exactTurnTerminalization.js +100 -97
- package/dist/message/message.js +16 -0
- package/dist/observability/executionAudit.js +4 -25
- package/dist/observability/faultClassification.js +58 -70
- package/dist/observability/orchestrationMetrics.js +8 -50
- package/dist/profile/agentProfile.js +60 -15
- package/dist/profile/agentProfileRuntime.js +100 -0
- package/dist/release/releaseWorkflowPorts.js +5 -68
- package/dist/repository/executionLaneGitSnapshot.js +80 -0
- package/dist/repository/gitWorkspace.js +9 -67
- package/dist/repository/taskBaseFreshness.js +3 -1
- package/dist/repository/taskWorkspaceCoordinator.js +32 -4
- package/dist/repository/taskWorkspacePreparer.js +252 -411
- package/dist/resources/liveReferences.js +6 -55
- package/dist/resources/resourceDiscovery.js +1 -30
- package/dist/resources/resourceRegistrar.js +0 -23
- package/dist/review/deltaRecheck.js +8 -65
- package/dist/review/reviewAcceptance.js +22 -19
- package/dist/review/reviewConfig.js +0 -10
- package/dist/review/reviewDecision.js +2 -2
- package/dist/review/reviewRound.js +147 -273
- package/dist/review/reviewerAvailability.js +13 -23
- package/dist/role/role.js +3 -0
- package/dist/runtime/agentDriverObservation.js +1 -1
- package/dist/runtime/agentHost.js +51 -51
- package/dist/runtime/builtinAgentDrivers.js +9 -2
- package/dist/runtime/codexAppServerRuntime.js +23 -5
- package/dist/runtime/exactControlPlane.js +2 -267
- package/dist/runtime/firstProgressAdvisory.js +1 -1
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/launchBroker.js +11 -11
- package/dist/runtime/lifecycleReservation.js +2 -2
- package/dist/runtime/managedCaller.js +111 -0
- package/dist/runtime/ports.js +16 -3
- package/dist/runtime/preallocatedNativeSession.js +2 -2
- package/dist/runtime/processExitObservation.js +3 -2
- package/dist/runtime/providerContinuationReconciliationService.js +1 -1
- package/dist/runtime/runtimeBinding.js +1 -1
- package/dist/runtime/runtimeObservation.js +33 -16
- package/dist/runtime/runtimeSessionCandidate.js +2 -2
- package/dist/runtime/runtimeStopReceipt.js +11 -11
- package/dist/runtime/sessionLaunchRequest.js +2 -2
- package/dist/runtime/sessionOwnerIdentity.js +9 -9
- package/dist/runtime/sessionOwnerRegistry.js +13 -13
- package/dist/runtime/sessionReconciliation.js +4 -4
- package/dist/runtime/sessionTerminationGuard.js +9 -9
- package/dist/runtime/sessionTokenMetrics.js +5 -7
- package/dist/runtime/structuredProviderHost.js +5 -5
- package/dist/runtime/taskRuntimeIsolation.js +24 -24
- package/dist/runtime/tmuxAdapters.js +32 -30
- package/dist/scheduler/activeRoleTurnDelivery.js +37 -15
- package/dist/scheduler/leaderWakeupProcessor.js +78 -55
- package/dist/scheduler/roleTurnLiveness.js +6 -6
- package/dist/scheduler/roleTurnStall.js +8 -9
- package/dist/scheduler/taskExecutionProjection.js +19 -41
- package/dist/scheduler/taskObservabilityProjection.js +75 -122
- package/dist/storage/sqliteSchema.js +1 -21
- package/dist/storage/sqliteStore.js +62 -47
- package/dist/storage/storageSchema.js +1 -8
- package/dist/storage/storageVersions.js +7 -4
- package/dist/storage/taskStore.js +8 -8
- package/dist/storage/upgrade/upgradeOrchestrator.js +112 -83
- package/dist/task/completionReadiness.js +17 -98
- package/dist/task/deliveryGuard.js +9 -11
- package/dist/task/draftPlan.js +142 -0
- package/dist/task/nextAction.js +179 -477
- package/dist/task/publicationVerification.js +1 -0
- package/dist/task/remoteDelivery.js +233 -0
- package/dist/task/task.js +108 -4
- package/dist/task/taskRecordReference.js +0 -1
- package/dist/turn/turn.js +109 -16
- package/dist/verification/verificationGateService.js +0 -26
- package/dist/web/assets/client/components.js +99 -121
- package/dist/web/assets/client/i18n.js +136 -58
- package/dist/web/assets/client/view.js +44 -0
- package/dist/web/assets/styles/cards.js +3 -15
- package/dist/web/assets/styles/widgets.js +4 -0
- package/dist/web/webSnapshot.js +29 -14
- package/dist/workItem/dependencyGate.js +34 -0
- package/dist/workItem/workItem.js +99 -266
- package/dist/workspace/workItemChangeSetManager.js +15 -59
- package/docs/task-local-identity.md +1 -1
- package/i18n/README.zh-CN.md +164 -58
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +272 -782
- package/skills/yui-operator/SKILL.md +190 -365
- package/skills/yui-reviewer/SKILL.md +40 -33
- package/skills/yui-runtime/SKILL.md +22 -0
- package/skills/yui-worker/SKILL.md +19 -6
- package/dist/execution/candidateConvergence.js +0 -623
- package/dist/execution/executionGroup.js +0 -854
- package/dist/execution/resourceBroker.js +0 -426
- package/dist/executor/launchPlan.js +0 -16
- package/dist/review/reviewFinding.js +0 -240
- package/dist/review/reviewFindingLedger.js +0 -550
- package/dist/review/reviewOutcomeClassifier.js +0 -255
- package/dist/scheduler/resourceQueueProjection.js +0 -71
- package/dist/task/repairWave.js +0 -150
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import { readFileSync } from "node:fs";
|
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
5
|
import { createInterface } from "node:readline/promises";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { isDeepStrictEqual } from "node:util";
|
|
7
8
|
import { renderCommandHelp } from "./cli/helpRenderer.js";
|
|
8
9
|
import { describeCommandTree, findCommandNode } from "./cli/commandCatalog.js";
|
|
9
10
|
import { routeInvocation } from "./cli/invocationRouter.js";
|
|
@@ -35,8 +36,12 @@ import { runTelemetryCommand } from "./commands/telemetryCommands.js";
|
|
|
35
36
|
import { runResourcesCommand } from "./commands/resourcesCommands.js";
|
|
36
37
|
import { applyOperatorSessionControl, runOperatorCommand } from "./commands/operatorCommands.js";
|
|
37
38
|
import { runProjectCommand } from "./commands/projectCommands.js";
|
|
38
|
-
import { runProfileCommand } from "./commands/profileCommands.js";
|
|
39
|
-
import { dispatchPreparedReviewRound, failPendingReviewRound, preserveReviewRoundWorkspace, parseTaskCompletionRequest, preflightTaskCompletion, runTaskCommand,
|
|
39
|
+
import { previewProfileAgentConfigurationMutation, runProfileCommand } from "./commands/profileCommands.js";
|
|
40
|
+
import { assertWorkItemDependenciesCompletedForCommand, dispatchPreparedReviewRound, failPendingReviewRound, preserveReviewRoundWorkspace, parseTaskCompletionRequest, previewTaskRoleAgentConfigurationMutation, preflightTaskCompletion, runTaskCommand, planReplicatedWorkItemLanes, validateTaskArchiveRequest } from "./commands/taskCommands.js";
|
|
41
|
+
import { assertTaskRemoteDeliveryIntegrated, createTaskRemoteDeliveryProof } from "./commands/taskRemoteDeliveryCommand.js";
|
|
42
|
+
import { runTaskPublicationVerifyCommand } from "./commands/taskPublicationVerifyCommand.js";
|
|
43
|
+
import { createGitHubCliPublicationVerifier } from "./external/githubPublicationVerifier.js";
|
|
44
|
+
import { createGitLabCliPublicationVerifier } from "./external/gitlabPublicationVerifier.js";
|
|
40
45
|
import { taskActor } from "./commands/taskActor.js";
|
|
41
46
|
import { parseTaskExecutionStartRequest, parseTaskExecutionStopRequest, finalizeStoppedTaskExecution, startTaskExecutionCommand, stopTaskExecutionCommand } from "./commands/taskExecutionCommands.js";
|
|
42
47
|
import { runTaskIntegrationCommand } from "./commands/taskIntegrationCommands.js";
|
|
@@ -78,21 +83,23 @@ import { runSetupCommand, validateSetupInvocation } from "./setup/setupCommand.j
|
|
|
78
83
|
import { NodeCommandExecutor } from "./tmux/commandExecutor.js";
|
|
79
84
|
import { TmuxManager } from "./tmux/tmuxManager.js";
|
|
80
85
|
import { WorkItemChangeSetManager } from "./workspace/workItemChangeSetManager.js";
|
|
86
|
+
import { workspaceProjectEntry } from "./worktree/managedWorkspace.js";
|
|
81
87
|
import { parseWebCommandOptions, startYuiWebServer } from "./web/webServer.js";
|
|
82
|
-
import { AgentConfigurationCatalogService } from "./executor/agentConfigurationCatalog.js";
|
|
88
|
+
import { AgentConfigurationCatalogService, validateAgentLaunchConfiguration } from "./executor/agentConfigurationCatalog.js";
|
|
89
|
+
import { resolveAgentProfileView } from "./profile/agentProfileRuntime.js";
|
|
83
90
|
import { TmuxWebTerminalService } from "./web/tmuxWebTerminal.js";
|
|
84
91
|
import { listOperatorSessions, operatorSessionRef } from "./operator/operatorSessionHistory.js";
|
|
85
92
|
import { YUI_VERSION, yuiVersionIdentity } from "./version.js";
|
|
86
93
|
import { SqliteSchemaMigrationError } from "./storage/sqliteSchema.js";
|
|
87
|
-
import { YUI_CONTROL_PLANE_DESCRIPTOR,
|
|
94
|
+
import { YUI_CONTROL_PLANE_DESCRIPTOR, assertCompatibleControlPlanePreflight, assertExactControlPlanePreflight, exactControlPlaneDigest, extractExactControlArgument, createExactControlPlaneDescriptor, parseExactControlPlaneDescriptor } from "./runtime/exactControlPlane.js";
|
|
95
|
+
import { requireManagedTaskCaller } from "./runtime/managedCaller.js";
|
|
88
96
|
import { readSessionBootstrapManifest, refreshManagedSessionCliWrappers } from "./context/sessionBootstrapManifest.js";
|
|
89
|
-
import { builtinAgentDriverRegistry } from "./runtime/builtinAgentDrivers.js";
|
|
90
97
|
import { createTaskFinalReviewContract, extractTaskFinalReviewRequest } from "./review/taskFinalReviewContract.js";
|
|
91
98
|
import { resolveRecordedTaskFinalReviewContract } from "./review/taskFinalReviewContractResolution.js";
|
|
92
99
|
import { assessDeltaRecheck } from "./review/deltaRecheck.js";
|
|
93
|
-
import {
|
|
100
|
+
import { isCompletedTaskReviewEvidence } from "./review/reviewAcceptance.js";
|
|
94
101
|
import { NodeGitWorkspace } from "./repository/gitWorkspace.js";
|
|
95
|
-
import { currentWorkItemExecutionGroup
|
|
102
|
+
import { currentWorkItemExecutionGroup } from "./workItem/workItem.js";
|
|
96
103
|
const VERSION = YUI_VERSION;
|
|
97
104
|
const exactControlInvocation = extractExactControlArgument(process.argv.slice(2));
|
|
98
105
|
const taskFinalReviewInvocation = extractTaskFinalReviewRequest(exactControlInvocation.args);
|
|
@@ -134,8 +141,7 @@ export async function main() {
|
|
|
134
141
|
const routedForFence = args.length === 0 ? undefined : routeInvocation(args);
|
|
135
142
|
const managedInvocation = process.env.YUI_SESSION_SCOPE === "task"
|
|
136
143
|
|| process.env.YUI_SESSION_SCOPE === "global"
|
|
137
|
-
|| process.env[YUI_CONTROL_PLANE_DESCRIPTOR] !== undefined
|
|
138
|
-
|| process.env[YUI_TASK_RUNTIME_DESCRIPTOR] !== undefined;
|
|
144
|
+
|| process.env[YUI_CONTROL_PLANE_DESCRIPTOR] !== undefined;
|
|
139
145
|
const homeFreeInvocation = args.length === 0
|
|
140
146
|
|| (args[0] === "version" && args.length === 1)
|
|
141
147
|
|| routedForFence?.kind === "help"
|
|
@@ -298,7 +304,6 @@ export async function main() {
|
|
|
298
304
|
return;
|
|
299
305
|
}
|
|
300
306
|
if (args[0] === "upgrade") {
|
|
301
|
-
// Admission diagnostic only: never quiesces, repairs, or switches storage.
|
|
302
307
|
const result = await runUpgradeCommand(args.slice(1), home);
|
|
303
308
|
process.exitCode = result.exitCode;
|
|
304
309
|
emit(result.output, false, result.data);
|
|
@@ -318,7 +323,7 @@ export async function main() {
|
|
|
318
323
|
if (args[1] === "agent-host" && args.length === 4) {
|
|
319
324
|
process.exitCode = await runAgentHost({
|
|
320
325
|
home,
|
|
321
|
-
|
|
326
|
+
runtimeGenerationId: args[2],
|
|
322
327
|
ticket: args[3]
|
|
323
328
|
});
|
|
324
329
|
return;
|
|
@@ -553,7 +558,7 @@ export async function main() {
|
|
|
553
558
|
emit("Cancelled.");
|
|
554
559
|
return;
|
|
555
560
|
}
|
|
556
|
-
await preflightAgentConfigurationMutation(resolved, store, catalogs);
|
|
561
|
+
const validateAgentConfiguration = await preflightAgentConfigurationMutation(resolved, store, catalogs);
|
|
557
562
|
const executor = new NodeCommandExecutor();
|
|
558
563
|
const tmux = new TmuxManager(resolveTmuxBin(store.getConfig().tmuxBin), executor, {
|
|
559
564
|
yuiHome: home,
|
|
@@ -682,7 +687,7 @@ export async function main() {
|
|
|
682
687
|
return;
|
|
683
688
|
}
|
|
684
689
|
if (domain === "profile") {
|
|
685
|
-
const result = runProfileCommand(resolved.slice(2), store);
|
|
690
|
+
const result = runProfileCommand(resolved.slice(2), store, () => new Date(), validateAgentConfiguration === undefined ? {} : { validateAgentConfiguration });
|
|
686
691
|
emit(result.output, false, result.data);
|
|
687
692
|
return;
|
|
688
693
|
}
|
|
@@ -820,6 +825,27 @@ export async function main() {
|
|
|
820
825
|
emit(result.output, false, result.data);
|
|
821
826
|
return;
|
|
822
827
|
}
|
|
828
|
+
if (resolved[1] === "publication" && resolved[2] === "verify") {
|
|
829
|
+
const result = await runTaskPublicationVerifyCommand(resolved.slice(3), store, {
|
|
830
|
+
verifiers: {
|
|
831
|
+
github: createGitHubCliPublicationVerifier({
|
|
832
|
+
environmentPath: process.env.PATH
|
|
833
|
+
}),
|
|
834
|
+
gitlab: createGitLabCliPublicationVerifier({
|
|
835
|
+
environmentPath: process.env.PATH
|
|
836
|
+
})
|
|
837
|
+
},
|
|
838
|
+
candidateForTask: async (taskId) => {
|
|
839
|
+
const status = store.getTask(taskId)?.status;
|
|
840
|
+
return status === "active" || status === "retired"
|
|
841
|
+
? snapshotActualTaskReviewCandidate(taskId, store, workspacePreparer)
|
|
842
|
+
: null;
|
|
843
|
+
},
|
|
844
|
+
environment: process.env
|
|
845
|
+
});
|
|
846
|
+
emit(result.output, false, result.data);
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
823
849
|
if (resolved[1] === "overlap") {
|
|
824
850
|
const result = await runTaskOverlapCommand(resolved.slice(2), store);
|
|
825
851
|
emit(result.output, false, result.data);
|
|
@@ -877,6 +903,7 @@ export async function main() {
|
|
|
877
903
|
throw usageError("Task work capture usage: yui task work capture <task>/<work>.");
|
|
878
904
|
}
|
|
879
905
|
const reference = cliWorkItemReference(workItemId, process.env);
|
|
906
|
+
taskActor(process.env, reference.taskId);
|
|
880
907
|
const changeSets = await new WorkItemChangeSetManager(store).capture(reference.taskId, reference.localId, taskFinalReviewContract === undefined
|
|
881
908
|
? {}
|
|
882
909
|
: { taskFinalReviewContract });
|
|
@@ -968,12 +995,23 @@ export async function main() {
|
|
|
968
995
|
});
|
|
969
996
|
return;
|
|
970
997
|
}
|
|
998
|
+
let archiveRemoteDeliveryProof;
|
|
999
|
+
let archiveTaskReviewCandidate;
|
|
971
1000
|
if (resolved[1] === "archive") {
|
|
972
|
-
const { taskId, disposition } = validateTaskArchiveRequest(resolved.slice(2), store, {
|
|
1001
|
+
const { taskId, disposition, forceUnverified } = validateTaskArchiveRequest(resolved.slice(2), store, {
|
|
1002
|
+
runtime,
|
|
1003
|
+
environment: process.env,
|
|
1004
|
+
yuiHome: home
|
|
1005
|
+
});
|
|
973
1006
|
const task = store.getTask(taskId);
|
|
974
1007
|
if (task === null)
|
|
975
1008
|
throw new Error(`Task disappeared after archive validation: ${taskId}.`);
|
|
976
1009
|
if (task.status !== "archived") {
|
|
1010
|
+
if (disposition === "integrated") {
|
|
1011
|
+
archiveTaskReviewCandidate = await actualTaskReviewCandidateForTaskCommand(resolved, store, workspacePreparer, process.env);
|
|
1012
|
+
archiveRemoteDeliveryProof = createTaskRemoteDeliveryProof(store, task, archiveTaskReviewCandidate ?? null);
|
|
1013
|
+
assertTaskRemoteDeliveryIntegrated(archiveRemoteDeliveryProof.delivery, { forceUnverified });
|
|
1014
|
+
}
|
|
977
1015
|
const workItemIds = store.listManagedWorkspaces(task.id)
|
|
978
1016
|
.flatMap(({ owner }) => owner.type === "work-item" ? [owner.workItemId] : []);
|
|
979
1017
|
for (const workItemId of workItemIds) {
|
|
@@ -1012,6 +1050,7 @@ export async function main() {
|
|
|
1012
1050
|
}
|
|
1013
1051
|
}
|
|
1014
1052
|
}
|
|
1053
|
+
assertWorkItemExecutionDependenciesForCommand(resolved, store, process.env);
|
|
1015
1054
|
if (resolved[1] === "work" && resolved[2] === "dispatch") {
|
|
1016
1055
|
const workItemId = resolved[3];
|
|
1017
1056
|
const reference = workItemId === undefined
|
|
@@ -1021,11 +1060,19 @@ export async function main() {
|
|
|
1021
1060
|
? null
|
|
1022
1061
|
: store.getWorkItem(reference.taskId, reference.localId);
|
|
1023
1062
|
const task = item === null ? null : store.getTask(item.taskId);
|
|
1063
|
+
if (item !== null && task !== null) {
|
|
1064
|
+
// Authority and pure Lane-shape checks precede every physical or
|
|
1065
|
+
// durable workspace preparation performed for dispatch.
|
|
1066
|
+
taskActor(process.env, task.id);
|
|
1067
|
+
if (item.assignee !== undefined) {
|
|
1068
|
+
workItemDispatchLanePlan(resolved, store, item);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1024
1071
|
// A rejected Candidate starts a new execution iteration. Release every
|
|
1025
1072
|
// terminal Lane Role runtime before preparing the new Lane workspaces;
|
|
1026
1073
|
// durable Turns, Groups, Candidates, and workspace owners remain intact.
|
|
1027
1074
|
if (item?.status === "failed"
|
|
1028
|
-
&& currentWorkItemExecutionGroup(item)?.
|
|
1075
|
+
&& currentWorkItemExecutionGroup(item)?.lanes.every(({ disposition }) => disposition !== "open")) {
|
|
1029
1076
|
await workspaceCoordinator.cleanupWorkItemRuntime(item.taskId, item.id);
|
|
1030
1077
|
}
|
|
1031
1078
|
// Every Task needs an authoritative runtime owner before dispatch. A
|
|
@@ -1078,7 +1125,9 @@ export async function main() {
|
|
|
1078
1125
|
return;
|
|
1079
1126
|
}
|
|
1080
1127
|
if (resolved[1] === "upstream") {
|
|
1081
|
-
const result = await runTaskUpstreamCommand(resolved.slice(2), store
|
|
1128
|
+
const result = await runTaskUpstreamCommand(resolved.slice(2), store, home, {
|
|
1129
|
+
environment: process.env
|
|
1130
|
+
});
|
|
1082
1131
|
emit(result.output, false, result.data);
|
|
1083
1132
|
return;
|
|
1084
1133
|
}
|
|
@@ -1116,19 +1165,24 @@ export async function main() {
|
|
|
1116
1165
|
// Keep completion offline by default. An explicit refresh is the only
|
|
1117
1166
|
// path that may fetch and reconcile a moved remote baseline.
|
|
1118
1167
|
if (refreshRemote) {
|
|
1119
|
-
await reconcileTaskRemoteBaselines(resolved[2], store, home, { environment: process.env, jobPort: createControllerIntegrationJobPort(home, { environment: process.env }) });
|
|
1168
|
+
const reconciled = await reconcileTaskRemoteBaselines(resolved[2], store, home, { environment: process.env, jobPort: createControllerIntegrationJobPort(home, { environment: process.env }) });
|
|
1169
|
+
if (reconciled.length > 0) {
|
|
1170
|
+
const updates = reconciled.map((entry) => (`${entry.projectId}: ${entry.fromCommit} -> ${entry.toCommit} `
|
|
1171
|
+
+ `(Integration ${entry.integrationId})`)).join("; ");
|
|
1172
|
+
throw usageError(`Remote baseline reconciliation advanced Task ${resolved[2]} (${updates}). `
|
|
1173
|
+
+ "The Task remains active so the Leader can inspect the new authoritative head, "
|
|
1174
|
+
+ "decide how prior Review evidence applies, and retry task complete.");
|
|
1175
|
+
}
|
|
1120
1176
|
}
|
|
1121
1177
|
}
|
|
1122
1178
|
}
|
|
1123
1179
|
let releaseReviewHandoverLock;
|
|
1124
1180
|
if ((resolved[1] === "review"
|
|
1125
|
-
&& ["request", "
|
|
1181
|
+
&& ["request", "retry"].includes(resolved[2] ?? ""))
|
|
1126
1182
|
|| resolved[1] === "complete") {
|
|
1127
1183
|
const handoverLock = acquireHandoverLock(home);
|
|
1128
1184
|
releaseReviewHandoverLock = handoverLock.release;
|
|
1129
1185
|
}
|
|
1130
|
-
let candidateMaterialization;
|
|
1131
|
-
let candidateMaterializationCommitted = false;
|
|
1132
1186
|
try {
|
|
1133
1187
|
const preparedLanes = await prepareExecutionLaneWorkspacesForCommand(resolved, store, workspacePreparer, process.env);
|
|
1134
1188
|
if (preparedLanes !== undefined) {
|
|
@@ -1136,12 +1190,11 @@ export async function main() {
|
|
|
1136
1190
|
laneDispatchRelease = preparedLanes.release;
|
|
1137
1191
|
laneDispatchProjectPaths = preparedLanes.projectPaths;
|
|
1138
1192
|
}
|
|
1139
|
-
|
|
1140
|
-
const candidateGitSnapshot = candidateMaterialization === undefined
|
|
1141
|
-
? await candidateSnapshotForTaskCommand(resolved, store, workspacePreparer, process.env, taskFinalReviewContract)
|
|
1142
|
-
: candidateMaterialization.snapshot;
|
|
1193
|
+
const candidateGitSnapshot = await candidateSnapshotForTaskCommand(resolved, store, workspacePreparer, process.env, taskFinalReviewContract);
|
|
1143
1194
|
const directTaskMainSnapshot = await directTaskMainSnapshotForTaskCommand(resolved, store, workspacePreparer, process.env, taskFinalReviewContract);
|
|
1144
|
-
const actualTaskReviewCandidate =
|
|
1195
|
+
const actualTaskReviewCandidate = archiveRemoteDeliveryProof === undefined
|
|
1196
|
+
? await actualTaskReviewCandidateForTaskCommand(resolved, store, workspacePreparer, process.env)
|
|
1197
|
+
: archiveTaskReviewCandidate;
|
|
1145
1198
|
const deltaRecheckPreflight = await deltaRecheckPreflightForTaskCommand(resolved.slice(1), store, actualTaskReviewCandidate);
|
|
1146
1199
|
// Physical preparation may precede the durable write, but Task status,
|
|
1147
1200
|
// workspace identity/cwd, and ManagedWorkspace ownership are adopted by
|
|
@@ -1168,9 +1221,6 @@ export async function main() {
|
|
|
1168
1221
|
: { completionPublishedTreeProof }),
|
|
1169
1222
|
...(workItemIntegrationProof === undefined ? {} : { workItemIntegrationProof }),
|
|
1170
1223
|
...(candidateGitSnapshot === undefined ? {} : { candidateGitSnapshot }),
|
|
1171
|
-
...(candidateMaterialization === undefined
|
|
1172
|
-
? {}
|
|
1173
|
-
: { candidateWorkspace: candidateMaterialization.workspace ?? null }),
|
|
1174
1224
|
...(executionLaneWorkspaces === undefined ? {} : { executionLaneWorkspaces }),
|
|
1175
1225
|
...(taskWorkspaceActivation === undefined ? {} : { taskWorkspaceActivation }),
|
|
1176
1226
|
...(laneDispatchProjectPaths === undefined ? {} : { laneDispatchProjectPaths }),
|
|
@@ -1178,10 +1228,16 @@ export async function main() {
|
|
|
1178
1228
|
...(actualTaskReviewCandidate === undefined
|
|
1179
1229
|
? {}
|
|
1180
1230
|
: { actualTaskReviewCandidate }),
|
|
1231
|
+
...(archiveRemoteDeliveryProof === undefined
|
|
1232
|
+
? {}
|
|
1233
|
+
: { archiveRemoteDeliveryProof }),
|
|
1181
1234
|
...(deltaRecheckPreflight === undefined
|
|
1182
1235
|
? {}
|
|
1183
1236
|
: { deltaRecheckPreflight }),
|
|
1184
|
-
...(taskRetirementProof === undefined ? {} : { taskRetirementProof })
|
|
1237
|
+
...(taskRetirementProof === undefined ? {} : { taskRetirementProof }),
|
|
1238
|
+
...(validateAgentConfiguration === undefined
|
|
1239
|
+
? {}
|
|
1240
|
+
: { validateAgentConfiguration })
|
|
1185
1241
|
});
|
|
1186
1242
|
// The dispatch transaction has now adopted (or rejected) the prepared
|
|
1187
1243
|
// Lane workspaces. Release the held fence so later output/review
|
|
@@ -1190,9 +1246,6 @@ export async function main() {
|
|
|
1190
1246
|
laneDispatchRelease();
|
|
1191
1247
|
laneDispatchRelease = undefined;
|
|
1192
1248
|
}
|
|
1193
|
-
// The command transaction has now durably submitted the Candidate. Any
|
|
1194
|
-
// later output/review handling must not roll back its Git snapshot.
|
|
1195
|
-
candidateMaterializationCommitted = candidateMaterialization !== undefined;
|
|
1196
1249
|
if (result.kind === "output") {
|
|
1197
1250
|
const requestedRound = reviewRoundFromCommandData(result.data);
|
|
1198
1251
|
const persistedRequestedRound = requestedRound === undefined
|
|
@@ -1206,7 +1259,9 @@ export async function main() {
|
|
|
1206
1259
|
const reviewDispatchNeeded = requestedRound?.status === "pending"
|
|
1207
1260
|
|| (requestedRound?.status === "running"
|
|
1208
1261
|
&& resumesReviewDispatch
|
|
1209
|
-
&& persistedRequestedRound?.executionGroup?.lanes.some((lane) => (lane.
|
|
1262
|
+
&& persistedRequestedRound?.executionGroup?.lanes.some((lane) => (lane.disposition === "open"
|
|
1263
|
+
&& (lane.currentTurnId === undefined
|
|
1264
|
+
|| store.getTurn(requestedRound.taskId, lane.currentTurnId)?.status === "failed"))) === true);
|
|
1210
1265
|
if (reviewDispatchNeeded) {
|
|
1211
1266
|
try {
|
|
1212
1267
|
const workspace = requestedRound.status === "running"
|
|
@@ -1242,17 +1297,15 @@ export async function main() {
|
|
|
1242
1297
|
})
|
|
1243
1298
|
});
|
|
1244
1299
|
reviewOutput = run === null
|
|
1245
|
-
? `Review ${requestedRound.id}
|
|
1300
|
+
? `Review ${requestedRound.id} remains running\n`
|
|
1246
1301
|
: `Review queued as ${requestedRound.id} (${run.id})\n`;
|
|
1247
1302
|
reviewData = {
|
|
1248
1303
|
reviewRequest: run === null
|
|
1249
1304
|
? {
|
|
1250
|
-
kind: "
|
|
1305
|
+
kind: "running",
|
|
1251
1306
|
reviewerRoleName: requestedRound.reviewerRoleName,
|
|
1252
|
-
phase: "resource-broker",
|
|
1253
1307
|
activeReviewRoundId: requestedRound.id,
|
|
1254
|
-
retryable:
|
|
1255
|
-
retryAfterSeconds: 5
|
|
1308
|
+
retryable: false
|
|
1256
1309
|
}
|
|
1257
1310
|
: {
|
|
1258
1311
|
kind: "started",
|
|
@@ -1311,7 +1364,7 @@ export async function main() {
|
|
|
1311
1364
|
agentId: result.agentId,
|
|
1312
1365
|
adapterId: result.adapterId,
|
|
1313
1366
|
nativeSessionId: result.nativeSessionId,
|
|
1314
|
-
...(result.
|
|
1367
|
+
...(result.runtimeGenerationId === undefined ? {} : { runtimeGenerationId: result.runtimeGenerationId }),
|
|
1315
1368
|
sessionUpdatedAt: result.sessionUpdatedAt
|
|
1316
1369
|
});
|
|
1317
1370
|
}
|
|
@@ -1405,12 +1458,7 @@ export async function main() {
|
|
|
1405
1458
|
return;
|
|
1406
1459
|
}
|
|
1407
1460
|
catch (error) {
|
|
1408
|
-
|
|
1409
|
-
await workspacePreparer.restoreExecutionGroupCandidateMaterialization(candidateMaterialization);
|
|
1410
|
-
}
|
|
1411
|
-
if (!candidateMaterializationCommitted) {
|
|
1412
|
-
await workspacePreparer.discardUnadoptedExecutionLaneWorkspaces(executionLaneWorkspaces);
|
|
1413
|
-
}
|
|
1461
|
+
await workspacePreparer.discardUnadoptedExecutionLaneWorkspaces(executionLaneWorkspaces);
|
|
1414
1462
|
if (laneDispatchRelease !== undefined) {
|
|
1415
1463
|
laneDispatchRelease();
|
|
1416
1464
|
laneDispatchRelease = undefined;
|
|
@@ -1465,10 +1513,9 @@ async function preflightManagedTaskControlPlane() {
|
|
|
1465
1513
|
throw new Error(taskFinalReviewInvocation.error);
|
|
1466
1514
|
}
|
|
1467
1515
|
const serializedControl = process.env[YUI_CONTROL_PLANE_DESCRIPTOR];
|
|
1468
|
-
const
|
|
1469
|
-
const exactAgentRuntime = serializedControl !== undefined || serializedRuntime !== undefined;
|
|
1516
|
+
const exactAgentRuntime = serializedControl !== undefined;
|
|
1470
1517
|
if (process.env.YUI_SESSION_SCOPE === "task" && !exactAgentRuntime) {
|
|
1471
|
-
throw new Error("Exact control-plane invocation requires
|
|
1518
|
+
throw new Error("Exact control-plane invocation requires its frozen descriptor in a managed Task runtime.");
|
|
1472
1519
|
}
|
|
1473
1520
|
if (!exactAgentRuntime) {
|
|
1474
1521
|
if (exactControlInvocation.digest !== undefined
|
|
@@ -1483,7 +1530,7 @@ async function preflightManagedTaskControlPlane() {
|
|
|
1483
1530
|
if (process.env.YUI_SESSION_SCOPE !== "task") {
|
|
1484
1531
|
throw new Error("Exact Task control-plane invocation requires a managed Task runtime.");
|
|
1485
1532
|
}
|
|
1486
|
-
if (serializedControl === undefined
|
|
1533
|
+
if (serializedControl === undefined) {
|
|
1487
1534
|
throw new Error("Exact control-plane invocation is required for this managed Task runtime.");
|
|
1488
1535
|
}
|
|
1489
1536
|
const control = parseExactControlPlaneDescriptor(serializedControl);
|
|
@@ -1522,20 +1569,16 @@ async function preflightManagedTaskControlPlane() {
|
|
|
1522
1569
|
commandControl = control;
|
|
1523
1570
|
digest = frozenDigest;
|
|
1524
1571
|
}
|
|
1525
|
-
const runtime = assertExactTaskRuntimeEnvironment(serializedRuntime, process.env, frozenDigest, control.yuiHome);
|
|
1526
|
-
const runtimeDriverCallback = args[0] === "internal"
|
|
1527
|
-
&& args[1] === "runtime-hook"
|
|
1528
|
-
&& process.env.YUI_DRIVER_ID !== undefined
|
|
1529
|
-
? builtinAgentDriverRegistry().require(process.env.YUI_DRIVER_ID)
|
|
1530
|
-
: undefined;
|
|
1531
1572
|
const verifiedStore = openCurrentTaskStore(control.yuiHome);
|
|
1532
|
-
//
|
|
1533
|
-
//
|
|
1534
|
-
//
|
|
1535
|
-
// current
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1573
|
+
// One authority for "may this process act as this Task Role?". Yui's own
|
|
1574
|
+
// internal callbacks run in the Host process and authenticate through the
|
|
1575
|
+
// exact control-plane digest verified above; an Agent command proves it is
|
|
1576
|
+
// the Role's current runtime with its per-Session caller key. Nothing here
|
|
1577
|
+
// gates on the current Turn: which Turn is active is durable state that the
|
|
1578
|
+
// command needing it reads, never a fact frozen into a process environment.
|
|
1579
|
+
const runtime = internalCallback
|
|
1580
|
+
? undefined
|
|
1581
|
+
: requireManagedTaskCaller(verifiedStore, process.env);
|
|
1539
1582
|
const request = taskFinalReviewInvocation.request;
|
|
1540
1583
|
if (request === undefined) {
|
|
1541
1584
|
return {
|
|
@@ -1544,7 +1587,7 @@ async function preflightManagedTaskControlPlane() {
|
|
|
1544
1587
|
verifiedStore
|
|
1545
1588
|
};
|
|
1546
1589
|
}
|
|
1547
|
-
if (runtime.roleName !== "leader") {
|
|
1590
|
+
if (runtime === undefined || runtime.roleName !== "leader") {
|
|
1548
1591
|
throw new Error("Only the exact Task Leader invocation may establish a final-review contract.");
|
|
1549
1592
|
}
|
|
1550
1593
|
if (request.taskId !== runtime.taskId) {
|
|
@@ -1690,6 +1733,25 @@ function cliTaskRecordReference(value, kind, environment) {
|
|
|
1690
1733
|
throw usageError(error instanceof Error ? error.message : String(error));
|
|
1691
1734
|
}
|
|
1692
1735
|
}
|
|
1736
|
+
function assertWorkItemExecutionDependenciesForCommand(args, store, environment) {
|
|
1737
|
+
let item;
|
|
1738
|
+
if (args[0] === "task" && args[1] === "work" && args[2] === "dispatch"
|
|
1739
|
+
&& args[3] !== undefined) {
|
|
1740
|
+
const reference = cliWorkItemReference(args[3], environment);
|
|
1741
|
+
item = store.getWorkItem(reference.taskId, reference.localId) ?? undefined;
|
|
1742
|
+
}
|
|
1743
|
+
else if (args[0] === "task" && args[1] === "turn" && args[2] === "retry"
|
|
1744
|
+
&& args[3] !== undefined) {
|
|
1745
|
+
const reference = cliTaskRecordReference(args[3], "turn", environment);
|
|
1746
|
+
const turn = store.getTurn(reference.taskId, reference.localId);
|
|
1747
|
+
item = turn?.purpose === "execution" && turn.workItemId !== undefined
|
|
1748
|
+
? store.getWorkItem(turn.taskId, turn.workItemId) ?? undefined
|
|
1749
|
+
: undefined;
|
|
1750
|
+
}
|
|
1751
|
+
if (item === undefined)
|
|
1752
|
+
return;
|
|
1753
|
+
assertWorkItemDependenciesCompletedForCommand(store, item);
|
|
1754
|
+
}
|
|
1693
1755
|
async function candidateSnapshotForTaskCommand(args, store, preparer, environment, taskFinalReviewContract) {
|
|
1694
1756
|
if (args[0] !== "task")
|
|
1695
1757
|
return undefined;
|
|
@@ -1718,179 +1780,79 @@ async function candidateSnapshotForTaskCommand(args, store, preparer, environmen
|
|
|
1718
1780
|
}
|
|
1719
1781
|
return preparer.snapshotCandidateWorkspace(workspace);
|
|
1720
1782
|
}
|
|
1721
|
-
if (args[1] === "work" && args[2] === "group" && args[3] === "resolve"
|
|
1722
|
-
&& args[4] !== undefined) {
|
|
1723
|
-
// The grouped accept path snapshots only after all selected Lane outputs
|
|
1724
|
-
// have been merged by candidateMaterializationForTaskCommand.
|
|
1725
|
-
return undefined;
|
|
1726
|
-
}
|
|
1727
1783
|
return undefined;
|
|
1728
1784
|
}
|
|
1729
|
-
async function candidateMaterializationForTaskCommand(args, store, preparer, environment, taskFinalReviewContract) {
|
|
1730
|
-
if (args[0] !== "task" || args[1] !== "work" || args[2] !== "group"
|
|
1731
|
-
|| args[3] !== "resolve" || args[4] === undefined
|
|
1732
|
-
|| !args.includes("--decision")
|
|
1733
|
-
|| args[args.indexOf("--decision") + 1] !== "accept")
|
|
1734
|
-
return undefined;
|
|
1735
|
-
const reference = cliWorkItemReference(args[4], environment);
|
|
1736
|
-
const item = store.getWorkItem(reference.taskId, reference.localId);
|
|
1737
|
-
const group = item === null || item === undefined
|
|
1738
|
-
? undefined
|
|
1739
|
-
: currentWorkItemExecutionGroup(item);
|
|
1740
|
-
if (item === null || item === undefined || group === undefined)
|
|
1741
|
-
return undefined;
|
|
1742
|
-
if (group.stage !== undefined && group.stage.stage !== "resolve")
|
|
1743
|
-
return undefined;
|
|
1744
|
-
// Early termination first stops never-started spend. Active stragglers are
|
|
1745
|
-
// deliberately retained, so the command records that stop and leaves the
|
|
1746
|
-
// group unresolved. Do not merge Lane output into the WorkItem Candidate
|
|
1747
|
-
// until those active Lanes settle and the Leader resolves the group again.
|
|
1748
|
-
if (args.includes("--early-stop")
|
|
1749
|
-
&& group.lanes.some(({ status }) => status === "running"))
|
|
1750
|
-
return undefined;
|
|
1751
|
-
const selected = args.flatMap((value, index) => value === "--lane" && args[index + 1] !== undefined ? [args[index + 1]] : []);
|
|
1752
|
-
const materializedLaneIds = selected.length === 0
|
|
1753
|
-
? group.lanes
|
|
1754
|
-
.filter((lane) => lane.status === "completed")
|
|
1755
|
-
.map(({ id }) => id)
|
|
1756
|
-
: selected;
|
|
1757
|
-
if (group.stage?.convergence !== undefined
|
|
1758
|
-
&& group.stage.stage === "resolve"
|
|
1759
|
-
&& materializedLaneIds.length !== 1) {
|
|
1760
|
-
throw usageError("Candidate convergence Resolve must select exactly one Lane before materialization.");
|
|
1761
|
-
}
|
|
1762
|
-
try {
|
|
1763
|
-
return await preparer.materializeExecutionGroupCandidate(item.taskId, item.id, group.id, materializedLaneIds);
|
|
1764
|
-
}
|
|
1765
|
-
catch (error) {
|
|
1766
|
-
throw usageError(error instanceof Error ? error.message : String(error));
|
|
1767
|
-
}
|
|
1768
|
-
}
|
|
1769
1785
|
async function prepareExecutionLaneWorkspacesForCommand(args, store, preparer, environment) {
|
|
1770
|
-
const isDispatch = args[0] === "task"
|
|
1771
|
-
|
|
1772
|
-
|
|
1786
|
+
const isDispatch = args[0] === "task"
|
|
1787
|
+
&& args[1] === "work"
|
|
1788
|
+
&& args[2] === "dispatch"
|
|
1789
|
+
&& args[3] !== undefined;
|
|
1790
|
+
if (!isDispatch)
|
|
1773
1791
|
return undefined;
|
|
1774
|
-
const itemRef =
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
const item = itemRef === null
|
|
1778
|
-
? (() => {
|
|
1779
|
-
const runRef = cliTaskRecordReference(args[3], "turn", environment);
|
|
1780
|
-
const run = store.getTurn(runRef.taskId, runRef.localId);
|
|
1781
|
-
return run?.workItemId === undefined ? null : store.getWorkItem(run.taskId, run.workItemId);
|
|
1782
|
-
})()
|
|
1783
|
-
: store.getWorkItem(itemRef.taskId, itemRef.localId);
|
|
1784
|
-
if (item === null)
|
|
1792
|
+
const itemRef = cliWorkItemReference(args[3], environment);
|
|
1793
|
+
const item = store.getWorkItem(itemRef.taskId, itemRef.localId);
|
|
1794
|
+
if (item === null || item.assignee === undefined)
|
|
1785
1795
|
return undefined;
|
|
1786
|
-
const
|
|
1787
|
-
|
|
1788
|
-
: null;
|
|
1789
|
-
const currentGroup = currentWorkItemExecutionGroup(item);
|
|
1790
|
-
const group = retryRun?.executionGroupId === undefined
|
|
1791
|
-
? (isDispatch && currentGroup?.resolution !== undefined ? undefined : currentGroup)
|
|
1792
|
-
: workItemExecutionGroupById(item, retryRun.executionGroupId);
|
|
1793
|
-
const roles = args.flatMap((value, index) => (value === "--lane-role" && args[index + 1] !== undefined
|
|
1794
|
-
? [args[index + 1]]
|
|
1795
|
-
: []));
|
|
1796
|
-
const requestedStrategy = (() => {
|
|
1797
|
-
const index = args.indexOf("--strategy");
|
|
1798
|
-
const value = index < 0 ? undefined : args[index + 1];
|
|
1799
|
-
if (value === undefined)
|
|
1800
|
-
return undefined;
|
|
1801
|
-
const fixed = /^fixed:([1-9]\d*)$/u.exec(value);
|
|
1802
|
-
if (fixed !== null)
|
|
1803
|
-
return { mode: "fixed", count: Number(fixed[1]) };
|
|
1804
|
-
const adaptive = /^adaptive:([1-9]\d*)$/u.exec(value);
|
|
1805
|
-
if (adaptive !== null)
|
|
1806
|
-
return { mode: "adaptive", max: Number(adaptive[1]) };
|
|
1807
|
-
throw usageError(`Invalid execution strategy: ${value}.`);
|
|
1808
|
-
})();
|
|
1809
|
-
const retryLaneId = isRetry
|
|
1810
|
-
? store.getTurn(item.taskId, cliTaskRecordReference(args[3], "turn", environment).localId)?.executionLaneId
|
|
1811
|
-
: undefined;
|
|
1812
|
-
const plan = normalizedExecutionLanePlan({
|
|
1813
|
-
assignee: item.assignee ?? "",
|
|
1814
|
-
requestedRoles: roles,
|
|
1815
|
-
requestedStrategy,
|
|
1816
|
-
existingGroup: group === undefined ? undefined : group,
|
|
1817
|
-
resolvedRetryGroup: isDispatch
|
|
1818
|
-
? resolvedExecutionStageRetryGroup(currentGroup)
|
|
1819
|
-
: undefined,
|
|
1820
|
-
status: item.status,
|
|
1821
|
-
nextGroupId: `execution-group-${store.peekNextTurnId(item.taskId)}`,
|
|
1822
|
-
retryLaneId,
|
|
1823
|
-
phase: isRetry ? "retry" : "dispatch"
|
|
1824
|
-
});
|
|
1825
|
-
const laneRoles = plan.roles;
|
|
1826
|
-
if (!isRetry && laneRoles.length === 0) {
|
|
1827
|
-
throw usageError("At least one --lane-role is required when expanding an ExecutionGroup.");
|
|
1828
|
-
}
|
|
1829
|
-
if (group !== undefined && requestedStrategy !== undefined) {
|
|
1830
|
-
const same = group.strategy.mode === requestedStrategy.mode
|
|
1831
|
-
&& (group.strategy.mode === "fixed"
|
|
1832
|
-
? requestedStrategy.mode === "fixed" && group.strategy.count === requestedStrategy.count
|
|
1833
|
-
: requestedStrategy.mode === "adaptive" && group.strategy.max === requestedStrategy.max);
|
|
1834
|
-
if (!same)
|
|
1835
|
-
throw usageError(`ExecutionGroup strategy is frozen: ${group.id}.`);
|
|
1836
|
-
}
|
|
1837
|
-
const laneCount = plan.requestedCount;
|
|
1838
|
-
const adaptive = plan.strategy.mode === "adaptive";
|
|
1839
|
-
const needsIsolation = adaptive || laneCount > 1 || (group?.lanes.length ?? 0) > 1;
|
|
1840
|
-
if (!needsIsolation)
|
|
1796
|
+
const plan = workItemDispatchLanePlan(args, store, item);
|
|
1797
|
+
if (plan.roles.length === 0)
|
|
1841
1798
|
return undefined;
|
|
1842
|
-
const
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
const held =
|
|
1851
|
-
? preparer.acquireTaskProjectMaintenanceLocks(item.taskId)
|
|
1852
|
-
: undefined;
|
|
1799
|
+
for (const roleName of plan.roles) {
|
|
1800
|
+
if (store.getRole(item.taskId, roleName) === null) {
|
|
1801
|
+
throw usageError(`Task Role not found: ${item.taskId}/${roleName}.`);
|
|
1802
|
+
}
|
|
1803
|
+
if (store.getActiveTurn(item.taskId, roleName) !== null) {
|
|
1804
|
+
throw usageError(`${item.taskId}/${roleName} already has an active turn.`);
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
const held = preparer.acquireTaskProjectMaintenanceLocks(item.taskId);
|
|
1853
1808
|
const map = new Map();
|
|
1854
1809
|
try {
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
const
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
throw new Error(`Project not found: ${projectId}.`);
|
|
1862
|
-
paths.set(projectId, project.path);
|
|
1863
|
-
}
|
|
1864
|
-
projectPaths = paths;
|
|
1810
|
+
const projectPaths = new Map();
|
|
1811
|
+
for (const { projectId } of held.current.projectBindings) {
|
|
1812
|
+
const project = store.getProject(projectId);
|
|
1813
|
+
if (project === null)
|
|
1814
|
+
throw new Error(`Project not found: ${projectId}.`);
|
|
1815
|
+
projectPaths.set(projectId, project.path);
|
|
1865
1816
|
}
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1817
|
+
const source = item.assignee === "leader"
|
|
1818
|
+
? store.getTaskWorkspace(item.taskId)
|
|
1819
|
+
: store.getWorkItemWorkspace(item.taskId, item.id);
|
|
1820
|
+
if (source === null) {
|
|
1821
|
+
throw usageError(`Execution Lane source workspace is not ready: ${item.id}.`);
|
|
1871
1822
|
}
|
|
1872
|
-
|
|
1823
|
+
const inputHeads = await preparer.snapshotExecutionLaneInputHeads(source, item.writeProjectIds);
|
|
1824
|
+
for (const laneId of plan.laneIds) {
|
|
1825
|
+
map.set(laneId, await preparer.prepareExecutionLaneWorkspace(item.taskId, plan.groupId, laneId, { purpose: "execution", workItemId: item.id, inputHeads }, { current: held.current }));
|
|
1826
|
+
}
|
|
1827
|
+
return { workspaces: map, release: held.release, projectPaths };
|
|
1873
1828
|
}
|
|
1874
1829
|
catch (error) {
|
|
1875
|
-
// Compensate (discard unadopted Lane worktrees) BEFORE releasing the
|
|
1876
|
-
// fence: a concurrent project migrate must not switch the catalog while
|
|
1877
|
-
// external-backed worktrees are still identifiable for removal.
|
|
1878
1830
|
await preparer.discardUnadoptedExecutionLaneWorkspaces(map);
|
|
1879
|
-
|
|
1880
|
-
held.release();
|
|
1831
|
+
held.release();
|
|
1881
1832
|
throw error;
|
|
1882
1833
|
}
|
|
1883
1834
|
}
|
|
1835
|
+
function workItemDispatchLanePlan(args, store, item) {
|
|
1836
|
+
if (item.assignee === undefined) {
|
|
1837
|
+
throw usageError(`Work Item has no Task Role assignee: ${item.id}.`);
|
|
1838
|
+
}
|
|
1839
|
+
const roles = args.flatMap((value, index) => (value === "--lane-role" && args[index + 1] !== undefined
|
|
1840
|
+
? [args[index + 1]]
|
|
1841
|
+
: []));
|
|
1842
|
+
const groupId = `execution-group-${store.peekNextTurnId(item.taskId)}`;
|
|
1843
|
+
return {
|
|
1844
|
+
groupId,
|
|
1845
|
+
...planReplicatedWorkItemLanes(item.assignee, roles, groupId)
|
|
1846
|
+
};
|
|
1847
|
+
}
|
|
1884
1848
|
async function prepareReviewLaneWorkspaces(taskId, reviewRoundId, store, preparer) {
|
|
1885
1849
|
const round = store.getReviewRound(taskId, reviewRoundId);
|
|
1886
1850
|
const group = round?.executionGroup;
|
|
1887
1851
|
if (round === null || round === undefined || group === undefined)
|
|
1888
1852
|
return undefined;
|
|
1889
|
-
if (group.lanes.length < 2 && group.strategy.mode !== "adaptive")
|
|
1890
|
-
return undefined;
|
|
1891
1853
|
const map = new Map();
|
|
1892
1854
|
try {
|
|
1893
|
-
for (const lane of group.lanes.filter((
|
|
1855
|
+
for (const lane of group.lanes.filter(({ disposition }) => disposition === "open")) {
|
|
1894
1856
|
map.set(lane.id, await preparer.prepareExecutionLaneWorkspace(taskId, group.id, lane.id, {
|
|
1895
1857
|
purpose: "review",
|
|
1896
1858
|
reviewRoundId
|
|
@@ -1963,15 +1925,6 @@ async function actualTaskReviewCandidateForTaskCommand(args, store, preparer, en
|
|
|
1963
1925
|
taskId = reference.taskId;
|
|
1964
1926
|
}
|
|
1965
1927
|
}
|
|
1966
|
-
else if (args[1] === "review"
|
|
1967
|
-
&& args[2] === "force-fresh"
|
|
1968
|
-
&& args[3] !== undefined) {
|
|
1969
|
-
const reference = cliTaskRecordReference(args[3], "reviewRound", environment);
|
|
1970
|
-
const round = store.getReviewRound(reference.taskId, reference.localId);
|
|
1971
|
-
if (round !== null && (round.scope ?? "work-item") === "task") {
|
|
1972
|
-
taskId = reference.taskId;
|
|
1973
|
-
}
|
|
1974
|
-
}
|
|
1975
1928
|
else if (args[1] === "work"
|
|
1976
1929
|
&& args[2] === "review"
|
|
1977
1930
|
&& args[3] === "retry"
|
|
@@ -1996,12 +1949,21 @@ async function actualTaskReviewCandidateForTaskCommand(args, store, preparer, en
|
|
|
1996
1949
|
taskId = reference.taskId;
|
|
1997
1950
|
}
|
|
1998
1951
|
}
|
|
1999
|
-
else if (
|
|
1952
|
+
else if (args[1] === "archive"
|
|
1953
|
+
&& args[2] !== undefined
|
|
1954
|
+
&& args.includes("--integrated")) {
|
|
1955
|
+
taskId = store.getTask(args[2])?.id;
|
|
1956
|
+
}
|
|
1957
|
+
else if ((args[1] === "show"
|
|
1958
|
+
|| args[1] === "context"
|
|
1959
|
+
|| args[1] === "next-action"
|
|
1960
|
+
|| args[1] === "remote-delivery")
|
|
2000
1961
|
&& args[2] !== undefined) {
|
|
2001
1962
|
taskId = store.getTask(args[2])?.id;
|
|
2002
1963
|
decisionSupportRead = true;
|
|
2003
1964
|
}
|
|
2004
|
-
|
|
1965
|
+
const status = taskId === undefined ? undefined : store.getTask(taskId)?.status;
|
|
1966
|
+
if (taskId === undefined || (status !== "active" && status !== "retired"))
|
|
2005
1967
|
return undefined;
|
|
2006
1968
|
try {
|
|
2007
1969
|
return await snapshotActualTaskReviewCandidate(taskId, store, preparer);
|
|
@@ -2019,6 +1981,9 @@ async function snapshotActualTaskReviewCandidate(taskId, store, preparer) {
|
|
|
2019
1981
|
if (task.projectBindings.length === 0) {
|
|
2020
1982
|
throw usageError(`Final Task Review requires a Project-backed Task: ${task.id}.`);
|
|
2021
1983
|
}
|
|
1984
|
+
// Reconcile the durable currentCommit facts from the authoritative Task
|
|
1985
|
+
// clones before freezing a completion/review candidate.
|
|
1986
|
+
await preparer.prepareTaskWorkspace(task.id);
|
|
2022
1987
|
const workspace = store.getTaskWorkspace(task.id);
|
|
2023
1988
|
if (workspace === null
|
|
2024
1989
|
|| workspace.owner.type !== "task"
|
|
@@ -2068,19 +2033,23 @@ async function deltaRecheckPreflightForTaskCommand(args, store, actualTaskReview
|
|
|
2068
2033
|
// a head (a full Review or an equivalent-and-accepted delta). A
|
|
2069
2034
|
// non-accepted delta cannot be the base for a new delta.
|
|
2070
2035
|
const previous = [...store.listReviewRounds(task.id)]
|
|
2071
|
-
.filter((round) =>
|
|
2036
|
+
.filter((round) => isCompletedTaskReviewEvidence(store, round))
|
|
2072
2037
|
.sort((left, right) => left.id.localeCompare(right.id, undefined, { numeric: true }))
|
|
2073
2038
|
.at(-1);
|
|
2074
2039
|
if (previous === undefined) {
|
|
2075
2040
|
throw usageError("Delta-recheck requires a previous completed Task-final Review that accepted a head.");
|
|
2076
2041
|
}
|
|
2077
2042
|
const repositoryPaths = {};
|
|
2043
|
+
const taskWorkspace = store.getTaskWorkspace(task.id);
|
|
2044
|
+
if (taskWorkspace === null || taskWorkspace.owner.type !== "task") {
|
|
2045
|
+
throw usageError(`Task has no authoritative main workspace: ${task.id}.`);
|
|
2046
|
+
}
|
|
2078
2047
|
for (const candidateProject of actualTaskReviewCandidate.projects) {
|
|
2079
|
-
const
|
|
2080
|
-
if (
|
|
2081
|
-
throw usageError(`Delta-recheck Project not found: ${candidateProject.projectId}.`);
|
|
2048
|
+
const entry = workspaceProjectEntry(taskWorkspace, candidateProject.projectId);
|
|
2049
|
+
if (entry === undefined) {
|
|
2050
|
+
throw usageError(`Delta-recheck Task workspace Project not found: ${candidateProject.projectId}.`);
|
|
2082
2051
|
}
|
|
2083
|
-
repositoryPaths[candidateProject.projectId] =
|
|
2052
|
+
repositoryPaths[candidateProject.projectId] = entry.path;
|
|
2084
2053
|
}
|
|
2085
2054
|
const assessment = await assessDeltaRecheck({
|
|
2086
2055
|
repositoryPaths,
|
|
@@ -2317,6 +2286,51 @@ function selectionPorts(store, catalogs) {
|
|
|
2317
2286
|
};
|
|
2318
2287
|
}
|
|
2319
2288
|
async function preflightAgentConfigurationMutation(commandArgs, store, catalogs) {
|
|
2289
|
+
const mutation = profileAgentConfigurationMutation(commandArgs, store)
|
|
2290
|
+
?? taskRoleAgentConfigurationMutation(commandArgs, store);
|
|
2291
|
+
if (mutation === undefined) {
|
|
2292
|
+
await warmLegacyRoleConfigurationMutation(commandArgs, store, catalogs);
|
|
2293
|
+
return undefined;
|
|
2294
|
+
}
|
|
2295
|
+
const agent = store.getConfiguredAgent(mutation.agentId);
|
|
2296
|
+
if (agent === null)
|
|
2297
|
+
throw agentNotFound(mutation.agentId);
|
|
2298
|
+
const resolved = await catalogs.resolve({
|
|
2299
|
+
agent,
|
|
2300
|
+
cwd: mutation.cwd,
|
|
2301
|
+
config: mutation.config
|
|
2302
|
+
});
|
|
2303
|
+
try {
|
|
2304
|
+
validateAgentLaunchConfiguration(resolved.catalog, mutation.config);
|
|
2305
|
+
}
|
|
2306
|
+
catch (error) {
|
|
2307
|
+
throw usageError(error instanceof Error ? error.message : String(error));
|
|
2308
|
+
}
|
|
2309
|
+
return (candidate) => {
|
|
2310
|
+
if (candidate.agentId !== mutation.agentId
|
|
2311
|
+
|| resolve(candidate.cwd) !== resolve(mutation.cwd)
|
|
2312
|
+
|| !isDeepStrictEqual(candidate.config, mutation.config)) {
|
|
2313
|
+
throw usageError("Agent configuration changed after capability preflight; retry the command.");
|
|
2314
|
+
}
|
|
2315
|
+
try {
|
|
2316
|
+
validateAgentLaunchConfiguration(resolved.catalog, candidate.config);
|
|
2317
|
+
}
|
|
2318
|
+
catch (error) {
|
|
2319
|
+
throw usageError(error instanceof Error ? error.message : String(error));
|
|
2320
|
+
}
|
|
2321
|
+
};
|
|
2322
|
+
}
|
|
2323
|
+
function profileAgentConfigurationMutation(args, store) {
|
|
2324
|
+
return args[0] === "config" && args[1] === "profile"
|
|
2325
|
+
? previewProfileAgentConfigurationMutation(args.slice(2), store)
|
|
2326
|
+
: undefined;
|
|
2327
|
+
}
|
|
2328
|
+
function taskRoleAgentConfigurationMutation(args, store) {
|
|
2329
|
+
return args[0] === "task"
|
|
2330
|
+
? previewTaskRoleAgentConfigurationMutation(args.slice(1), store)
|
|
2331
|
+
: undefined;
|
|
2332
|
+
}
|
|
2333
|
+
async function warmLegacyRoleConfigurationMutation(commandArgs, store, catalogs) {
|
|
2320
2334
|
if (!hasModelOrEffortMutation(commandArgs))
|
|
2321
2335
|
return;
|
|
2322
2336
|
const agentId = configurationMutationAgentId(commandArgs, store);
|
|
@@ -2380,8 +2394,11 @@ function selectionCall(store, catalogs, method, params) {
|
|
|
2380
2394
|
});
|
|
2381
2395
|
}
|
|
2382
2396
|
case "config.get": return store.getConfig();
|
|
2383
|
-
case "profile.list": return store.listAgentProfiles();
|
|
2384
|
-
case "profile.show":
|
|
2397
|
+
case "profile.list": return store.listAgentProfiles().map((profile) => profileSelectionRecord(profile, store));
|
|
2398
|
+
case "profile.show": {
|
|
2399
|
+
const profile = store.getAgentProfile(String(params.id ?? ""));
|
|
2400
|
+
return profile === null ? null : profileSelectionRecord(profile, store);
|
|
2401
|
+
}
|
|
2385
2402
|
case "role.list": return store.listGlobalRoles();
|
|
2386
2403
|
case "role.show": return store.getGlobalRole(String(params.name ?? ""));
|
|
2387
2404
|
case "project.list": return callOptional(reader, "listProjects");
|
|
@@ -2391,6 +2408,7 @@ function selectionCall(store, catalogs, method, params) {
|
|
|
2391
2408
|
case "task.role.list": return callOptional(reader, "listRoles", [params.taskId]);
|
|
2392
2409
|
case "task.role.show": return callOptional(reader, "getRole", [params.taskId, params.roleName]);
|
|
2393
2410
|
case "task.work.list": return callOptional(reader, "listWorkItems", [params.taskId]);
|
|
2411
|
+
case "task.message.list": return callOptional(reader, "listMessages", [params.taskId]);
|
|
2394
2412
|
case "task.input.list": {
|
|
2395
2413
|
const taskId = typeof params.taskId === "string" ? params.taskId : undefined;
|
|
2396
2414
|
const requests = taskId === undefined
|
|
@@ -2406,6 +2424,25 @@ function selectionCall(store, catalogs, method, params) {
|
|
|
2406
2424
|
default: return [];
|
|
2407
2425
|
}
|
|
2408
2426
|
}
|
|
2427
|
+
function profileSelectionRecord(profile, store) {
|
|
2428
|
+
const view = resolveAgentProfileView(profile, store);
|
|
2429
|
+
return {
|
|
2430
|
+
...view.profile,
|
|
2431
|
+
runtimeSource: view.runtime.source,
|
|
2432
|
+
...(view.runtime.source === "global-worker"
|
|
2433
|
+
? { workerRevision: view.runtime.workerRevision }
|
|
2434
|
+
: {}),
|
|
2435
|
+
effectiveRuntime: view.runtime,
|
|
2436
|
+
...(view.runtime.status === "resolved"
|
|
2437
|
+
? {
|
|
2438
|
+
agentId: view.runtime.binding.agentId,
|
|
2439
|
+
adapterId: view.runtime.binding.adapterId,
|
|
2440
|
+
model: view.runtime.binding.config.model,
|
|
2441
|
+
effort: view.runtime.binding.config.effort
|
|
2442
|
+
}
|
|
2443
|
+
: {})
|
|
2444
|
+
};
|
|
2445
|
+
}
|
|
2409
2446
|
function presentSelectionTimes(value, store) {
|
|
2410
2447
|
if (!Array.isArray(value))
|
|
2411
2448
|
return value;
|