@zq-silk/yui 0.8.3 → 0.8.7
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 +40 -22
- package/README.md +66 -19
- package/dist/cli/commandCatalog.js +43 -14
- package/dist/cli/operatorWizard.js +10 -20
- package/dist/cli/updatePorts.js +6 -0
- package/dist/cli.js +252 -37
- package/dist/commands/executionAuditCommands.js +30 -0
- package/dist/commands/globalRoleCommands.js +8 -4
- package/dist/commands/operatorCommands.js +42 -1
- package/dist/commands/taskCommands.js +527 -147
- package/dist/commands/taskCompletionGate.js +36 -24
- package/dist/commands/taskContextCommand.js +11 -4
- package/dist/commands/taskInputCommands.js +48 -10
- package/dist/commands/taskNextActionCommand.js +38 -3
- package/dist/commands/taskOverviewCommand.js +2 -1
- package/dist/commands/taskRoleRuntimeStatus.js +2 -1
- package/dist/context/runContextPack.js +9 -5
- package/dist/context/sessionBootstrapManifest.js +158 -11
- package/dist/context/wakeNotification.js +5 -3
- package/dist/controller/clientRuntime.js +15 -15
- package/dist/controller/controller.js +16 -8
- package/dist/controller/fileSchedulerStoreAdapter.js +67 -7
- package/dist/controller/handoverCandidate.js +10 -3
- package/dist/controller/sessionNotify.js +4 -22
- package/dist/executor/agentAdapter.js +2 -2
- package/dist/executor/agentExecutor.js +29 -9
- package/dist/executor/fileRoleLaunchPlanner.js +37 -45
- package/dist/integration/gitIntegrationService.js +50 -2
- package/dist/integration/integrationCheckEvidenceReuse.js +53 -0
- package/dist/observability/executionAudit.js +47 -1
- package/dist/observability/faultClassification.js +6 -4
- package/dist/observability/orchestrationMetrics.js +196 -0
- package/dist/operator/operatorSessionHistory.js +36 -0
- package/dist/release/releaseHandover.js +7 -5
- package/dist/release/runtimeRelease.js +15 -0
- package/dist/repository/taskWorkspaceCoordinator.js +13 -10
- package/dist/review/deltaRecheck.js +3 -2
- package/dist/review/reviewFindingLedger.js +5 -4
- package/dist/review/reviewOutcomeClassifier.js +263 -54
- package/dist/review/taskFinalReviewContractEvent.js +1 -0
- package/dist/review/taskFinalReviewContractRebind.js +367 -0
- package/dist/run/runIdentity.js +10 -70
- package/dist/runtime/agentHost.js +3 -4
- package/dist/runtime/codexAppServerRuntime.js +6 -0
- package/dist/runtime/exactControlPlane.js +47 -37
- package/dist/runtime/firstProgressStopLoss.js +54 -0
- package/dist/runtime/launchBroker.js +10 -2
- package/dist/runtime/runtimeDeadlines.js +14 -0
- package/dist/runtime/sessionTitle.js +24 -12
- package/dist/runtime/structuredProviderHost.js +7 -1
- package/dist/runtime/tmuxAdapters.js +10 -3
- package/dist/scheduler/actionability.js +4 -2
- package/dist/scheduler/activeRoleRunDelivery.js +20 -18
- package/dist/scheduler/activeTaskProgress.js +2 -1
- package/dist/scheduler/leaderWakeupProcessor.js +33 -2
- package/dist/scheduler/taskExecutionProjection.js +13 -4
- package/dist/scheduler/wakeReason.js +1 -0
- package/dist/storage/sqliteStore.js +18 -3
- package/dist/storage/taskStore.js +14 -3
- package/dist/task/completionReadiness.js +48 -19
- package/dist/task/deliveryGuard.js +3 -1
- package/dist/task/nextAction.js +153 -55
- package/dist/task/repairWave.js +14 -1
- package/dist/task/task.js +10 -0
- package/dist/task/taskRecordRetirement.js +72 -0
- package/dist/web/webSnapshot.js +7 -1
- package/dist/workItem/workItem.js +6 -4
- package/i18n/README.zh-CN.md +48 -9
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +73 -31
- package/skills/yui-operator/SKILL.md +58 -10
- package/skills/yui-reviewer/SKILL.md +23 -0
- package/skills/yui-runtime/SKILL.md +6 -6
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
|
-
import { resolve } from "node:path";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
4
|
import { createInterface } from "node:readline/promises";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { isDeepStrictEqual } from "node:util";
|
|
@@ -36,8 +36,9 @@ import { runResourcesCommand } from "./commands/resourcesCommands.js";
|
|
|
36
36
|
import { applyOperatorSessionControl, runOperatorCommand } from "./commands/operatorCommands.js";
|
|
37
37
|
import { runProjectCommand } from "./commands/projectCommands.js";
|
|
38
38
|
import { runProfileCommand } from "./commands/profileCommands.js";
|
|
39
|
-
import { dispatchPreparedReviewRound, failPendingReviewRound, RESUMED_PENDING_FINAL_REVIEW, TERMINALIZED_LEADER_BEFORE_FINAL_REVIEW, TaskFinalReviewDispatchDriftError, preserveReviewRoundWorkspace, parseTaskCompletionRequest, preflightTaskCompletion, runTaskCommand, normalizedExecutionLanePlan, validateTaskArchiveRequest } from "./commands/taskCommands.js";
|
|
39
|
+
import { dispatchPreparedReviewRound, failPendingReviewRound, RESUMED_PENDING_FINAL_REVIEW, TERMINALIZED_LEADER_BEFORE_FINAL_REVIEW, TaskFinalReviewDispatchDriftError, preserveReviewRoundWorkspace, parseTaskCompletionRequest, parseTaskFinalReviewContractRebindRequest, preflightTaskCompletion, runTaskCommand, normalizedExecutionLanePlan, validateTaskArchiveRequest } from "./commands/taskCommands.js";
|
|
40
40
|
import { taskActor } from "./commands/taskActor.js";
|
|
41
|
+
import { isCurrentGlobalOperator } from "./commands/taskInputCommands.js";
|
|
41
42
|
import { runTaskIntegrationCommand } from "./commands/taskIntegrationCommands.js";
|
|
42
43
|
import { runTaskChangeSetCommand } from "./commands/taskChangeSetCommands.js";
|
|
43
44
|
import { runTaskOverlapCommand } from "./commands/taskOverlapCommands.js";
|
|
@@ -46,7 +47,7 @@ import { runTaskWorkspaceCommand } from "./commands/taskWorkspaceCommands.js";
|
|
|
46
47
|
import { runWorkflowCommandAsync } from "./commands/workflowCommands.js";
|
|
47
48
|
import { createUpdatePorts } from "./cli/updatePorts.js";
|
|
48
49
|
import { createReleaseWorkflowPorts } from "./release/releaseWorkflowPorts.js";
|
|
49
|
-
import { readRuntimeIdentity } from "./release/runtimeRelease.js";
|
|
50
|
+
import { acquireHandoverLock, readRuntimeIdentity } from "./release/runtimeRelease.js";
|
|
50
51
|
import { renderReleaseActivateResult, renderReleaseInstallResult, renderReleaseList, runReleaseActivate, runReleaseInstall, runReleaseList } from "./commands/releaseCommands.js";
|
|
51
52
|
import { reconcileTaskRemoteBaselines, verifyTaskCompletionPublishedTree } from "./commands/taskCompletionGate.js";
|
|
52
53
|
import { runTaskBaseStatusCommand } from "./commands/taskBaseCommands.js";
|
|
@@ -80,9 +81,11 @@ import { AgentConfigurationCatalogService } from "./executor/agentConfigurationC
|
|
|
80
81
|
import { TmuxWebTerminalService } from "./web/tmuxWebTerminal.js";
|
|
81
82
|
import { listOperatorSessions, operatorSessionRef } from "./operator/operatorSessionHistory.js";
|
|
82
83
|
import { YUI_VERSION, yuiVersionIdentity } from "./version.js";
|
|
83
|
-
import { YUI_CONTROL_PLANE_DESCRIPTOR, YUI_TASK_RUNTIME_DESCRIPTOR, assertExactControlPlanePreflight, assertExactTaskRuntimeEnvironment, assertExactTaskRuntimeState, exactControlPlaneDigest, extractExactControlArgument, parseExactControlPlaneDescriptor } from "./runtime/exactControlPlane.js";
|
|
84
|
+
import { YUI_CONTROL_PLANE_DESCRIPTOR, YUI_TASK_RUNTIME_DESCRIPTOR, assertCompatibleControlPlanePreflight, assertExactControlPlanePreflight, assertExactTaskRuntimeEnvironment, assertExactTaskRuntimeState, exactControlPlaneDigest, extractExactControlArgument, createExactControlPlaneDescriptor, parseExactControlPlaneDescriptor } from "./runtime/exactControlPlane.js";
|
|
85
|
+
import { readSessionBootstrapManifest, refreshManagedSessionCliWrappers } from "./context/sessionBootstrapManifest.js";
|
|
84
86
|
import { builtinAgentDriverRegistry } from "./runtime/builtinAgentDrivers.js";
|
|
85
87
|
import { createTaskFinalReviewContract, extractTaskFinalReviewRequest } from "./review/taskFinalReviewContract.js";
|
|
88
|
+
import { prepareTaskFinalReviewContractRebindProof, resolveRecordedTaskFinalReviewContract } from "./review/taskFinalReviewContractRebind.js";
|
|
86
89
|
import { assessDeltaRecheck } from "./review/deltaRecheck.js";
|
|
87
90
|
import { deltaRecheckEnabled } from "./review/reviewConfig.js";
|
|
88
91
|
import { NodeGitWorkspace } from "./repository/gitWorkspace.js";
|
|
@@ -109,7 +112,7 @@ void main().catch((error) => {
|
|
|
109
112
|
process.exitCode = 5;
|
|
110
113
|
});
|
|
111
114
|
export async function main() {
|
|
112
|
-
const { contract: taskFinalReviewContract, verifiedStore } = await preflightManagedTaskControlPlane();
|
|
115
|
+
const { contract: taskFinalReviewContract, verifiedStore, controlPlane: exactCurrentControlPlane } = await preflightManagedTaskControlPlane();
|
|
113
116
|
if (args.length === 0) {
|
|
114
117
|
emit(renderCommandHelp((await import("./cli/commandCatalog.js")).ROOT_COMMAND, VERSION));
|
|
115
118
|
return;
|
|
@@ -261,6 +264,16 @@ export async function main() {
|
|
|
261
264
|
return;
|
|
262
265
|
}
|
|
263
266
|
if (args[0] === "internal") {
|
|
267
|
+
if (args[1] === "session-cli-refresh" && args.length === 2) {
|
|
268
|
+
if (process.env.YUI_SESSION_SCOPE === "task"
|
|
269
|
+
|| (process.env.YUI_SESSION_SCOPE === "global" && process.env.YUI_ROLE !== "operator")) {
|
|
270
|
+
throw usageError("Managed Session CLI refresh may be run only by the user or global Operator.");
|
|
271
|
+
}
|
|
272
|
+
const result = refreshManagedSessionCliWrappers(home);
|
|
273
|
+
emit(`Refreshed ${result.refreshed} legacy Session CLI wrapper(s); `
|
|
274
|
+
+ `${result.current} already current, ${result.skipped} skipped.`, false, result);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
264
277
|
if (args[1] === "agent-host" && args.length === 4) {
|
|
265
278
|
process.exitCode = await runAgentHost({
|
|
266
279
|
home,
|
|
@@ -988,6 +1001,59 @@ export async function main() {
|
|
|
988
1001
|
}
|
|
989
1002
|
}
|
|
990
1003
|
}
|
|
1004
|
+
let taskFinalReviewRebindProof;
|
|
1005
|
+
let taskFinalReviewRebindRequest;
|
|
1006
|
+
let releaseTaskFinalReviewHandoverLock;
|
|
1007
|
+
if (resolved[1] === "review" && resolved[2] === "rebind") {
|
|
1008
|
+
if (!isCurrentGlobalOperator(store, process.env)) {
|
|
1009
|
+
throw usageError("Task-final Review contract rebind requires the authenticated global Operator session.");
|
|
1010
|
+
}
|
|
1011
|
+
if (exactCurrentControlPlane === undefined) {
|
|
1012
|
+
throw usageError("Task-final Review contract rebind requires the exact current global Session CLI.");
|
|
1013
|
+
}
|
|
1014
|
+
taskFinalReviewRebindRequest = parseTaskFinalReviewContractRebindRequest(resolved.slice(3));
|
|
1015
|
+
}
|
|
1016
|
+
if ((resolved[1] === "review"
|
|
1017
|
+
&& ["request", "force-fresh", "retry", "rebind"].includes(resolved[2] ?? ""))
|
|
1018
|
+
|| resolved[1] === "complete") {
|
|
1019
|
+
const handoverLock = acquireHandoverLock(home);
|
|
1020
|
+
releaseTaskFinalReviewHandoverLock = handoverLock.release;
|
|
1021
|
+
}
|
|
1022
|
+
if (taskFinalReviewRebindRequest !== undefined) {
|
|
1023
|
+
const request = taskFinalReviewRebindRequest;
|
|
1024
|
+
try {
|
|
1025
|
+
taskFinalReviewRebindProof = prepareTaskFinalReviewContractRebindProof({
|
|
1026
|
+
home,
|
|
1027
|
+
taskId: request.taskId,
|
|
1028
|
+
fromControlPlaneDigest: request.fromControlPlaneDigest,
|
|
1029
|
+
toControlPlaneDigest: request.toControlPlaneDigest,
|
|
1030
|
+
fromReleaseId: request.fromReleaseId,
|
|
1031
|
+
toReleaseId: request.toReleaseId,
|
|
1032
|
+
currentControlPlane: exactCurrentControlPlane.descriptor
|
|
1033
|
+
});
|
|
1034
|
+
const workItems = store.listWorkItems(request.taskId);
|
|
1035
|
+
const reviewRounds = store.listReviewRounds(request.taskId);
|
|
1036
|
+
const resolution = resolveRecordedTaskFinalReviewContract(request.taskId, workItems, reviewRounds, store.listEvents(request.taskId));
|
|
1037
|
+
const activeRound = reviewRounds.find((round) => ((round.scope ?? "work-item") === "task"
|
|
1038
|
+
&& (round.status === "pending" || round.status === "running")));
|
|
1039
|
+
// An exact replay is a true no-op, even when the target Leader is live.
|
|
1040
|
+
// Invalid/stale source tuples also reach the command unchanged so its
|
|
1041
|
+
// transactional diagnosis cannot stop a healthy target runtime.
|
|
1042
|
+
if (resolution?.effective.controlPlaneDigest === request.fromControlPlaneDigest
|
|
1043
|
+
&& activeRound === undefined) {
|
|
1044
|
+
// The contract event and wake must never target a Provider
|
|
1045
|
+
// Conversation launched by the source release. Active Runs fail
|
|
1046
|
+
// closed; otherwise stop and retire that runtime under the handover
|
|
1047
|
+
// lock before the transactional event+wake commit.
|
|
1048
|
+
await workspaceCoordinator.cleanupTaskRoleRuntime(request.taskId, "leader");
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
catch (error) {
|
|
1052
|
+
releaseTaskFinalReviewHandoverLock();
|
|
1053
|
+
releaseTaskFinalReviewHandoverLock = undefined;
|
|
1054
|
+
throw error;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
991
1057
|
let candidateMaterialization;
|
|
992
1058
|
let candidateMaterializationCommitted = false;
|
|
993
1059
|
try {
|
|
@@ -1002,7 +1068,7 @@ export async function main() {
|
|
|
1002
1068
|
? await candidateSnapshotForTaskCommand(resolved, store, workspacePreparer, process.env, taskFinalReviewContract)
|
|
1003
1069
|
: candidateMaterialization.snapshot;
|
|
1004
1070
|
const directTaskMainSnapshot = await directTaskMainSnapshotForTaskCommand(resolved, store, workspacePreparer, process.env, taskFinalReviewContract);
|
|
1005
|
-
const actualTaskReviewCandidate = await actualTaskReviewCandidateForTaskCommand(resolved, store, workspacePreparer, process.env
|
|
1071
|
+
const actualTaskReviewCandidate = await actualTaskReviewCandidateForTaskCommand(resolved, store, workspacePreparer, process.env);
|
|
1006
1072
|
const deltaRecheckPreflight = await deltaRecheckPreflightForTaskCommand(resolved.slice(1), store, actualTaskReviewCandidate);
|
|
1007
1073
|
const reviewWorkspaceResult = await reviewWorkspaceResultForTaskCommand(resolved, store, workspacePreparer, process.env);
|
|
1008
1074
|
const executionLaneGitSnapshot = await executionLaneGitSnapshotForTaskCommand(resolved, store, workspacePreparer, process.env);
|
|
@@ -1016,6 +1082,9 @@ export async function main() {
|
|
|
1016
1082
|
...(taskFinalReviewContract === undefined
|
|
1017
1083
|
? {}
|
|
1018
1084
|
: { taskFinalReviewContract }),
|
|
1085
|
+
...(taskFinalReviewRebindProof === undefined
|
|
1086
|
+
? {}
|
|
1087
|
+
: { taskFinalReviewRebindProof }),
|
|
1019
1088
|
...(completionSummary === undefined ? {} : { completionSummary }),
|
|
1020
1089
|
...(completionPublishedTreeProof === undefined
|
|
1021
1090
|
? {}
|
|
@@ -1260,6 +1329,12 @@ export async function main() {
|
|
|
1260
1329
|
}
|
|
1261
1330
|
throw error;
|
|
1262
1331
|
}
|
|
1332
|
+
finally {
|
|
1333
|
+
if (releaseTaskFinalReviewHandoverLock !== undefined) {
|
|
1334
|
+
releaseTaskFinalReviewHandoverLock();
|
|
1335
|
+
releaseTaskFinalReviewHandoverLock = undefined;
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1263
1338
|
}
|
|
1264
1339
|
if (resolved[0] === "jobs") {
|
|
1265
1340
|
emit(runJobCommand(resolved.slice(1), store, { runtime }));
|
|
@@ -1299,13 +1374,14 @@ async function preflightManagedTaskControlPlane() {
|
|
|
1299
1374
|
throw new Error("Exact control-plane invocation requires both frozen descriptors in a managed Task runtime.");
|
|
1300
1375
|
}
|
|
1301
1376
|
if (!exactRuntime) {
|
|
1302
|
-
if (exactControlInvocation.digest !== undefined
|
|
1303
|
-
|
|
1377
|
+
if (exactControlInvocation.digest !== undefined
|
|
1378
|
+
|| process.env.YUI_SESSION_SCOPE === "global") {
|
|
1379
|
+
return await preflightManagedGlobalControlPlane(exactControlInvocation.digest);
|
|
1304
1380
|
}
|
|
1305
1381
|
if (taskFinalReviewInvocation.request !== undefined) {
|
|
1306
1382
|
throw new Error("Task final-review contract requires a verified exact Task control-plane invocation.");
|
|
1307
1383
|
}
|
|
1308
|
-
return { contract: undefined, verifiedStore: undefined };
|
|
1384
|
+
return { contract: undefined, controlPlane: undefined, verifiedStore: undefined };
|
|
1309
1385
|
}
|
|
1310
1386
|
if (process.env.YUI_SESSION_SCOPE !== "task") {
|
|
1311
1387
|
throw new Error("Exact Task control-plane invocation requires a managed Task runtime.");
|
|
@@ -1314,24 +1390,42 @@ async function preflightManagedTaskControlPlane() {
|
|
|
1314
1390
|
throw new Error("Exact control-plane invocation is required for this managed Task runtime.");
|
|
1315
1391
|
}
|
|
1316
1392
|
const control = parseExactControlPlaneDescriptor(serializedControl);
|
|
1317
|
-
const internalCallback = args[0] === "internal"
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1393
|
+
const internalCallback = args[0] === "internal"
|
|
1394
|
+
&& ["agent-host", "session-notify", "runtime-hook"].includes(args[1] ?? "");
|
|
1395
|
+
const home = resolveYuiHome(process.env);
|
|
1396
|
+
assertManagedSessionManifest(home, "task");
|
|
1397
|
+
if (resolve(control.yuiHome) !== resolve(home)) {
|
|
1398
|
+
throw new Error("Managed Task control-plane descriptor belongs to another YUI_HOME.");
|
|
1399
|
+
}
|
|
1400
|
+
const frozenDigest = exactControlPlaneDigest(control);
|
|
1401
|
+
const exactCommand = internalCallback || exactControlInvocation.digest !== undefined;
|
|
1402
|
+
let commandControl;
|
|
1403
|
+
let digest;
|
|
1404
|
+
if (exactCommand) {
|
|
1405
|
+
digest = exactControlInvocation.digest ?? frozenDigest;
|
|
1406
|
+
await assertExactControlPlanePreflight({
|
|
1407
|
+
serializedDescriptor: serializedControl,
|
|
1408
|
+
digest,
|
|
1409
|
+
actualExecutable: process.execPath,
|
|
1410
|
+
actualCliEntry: fileURLToPath(import.meta.url),
|
|
1411
|
+
actualHome: home
|
|
1412
|
+
}, {
|
|
1413
|
+
// Provider callbacks must remain able to append their immutable inbox fact
|
|
1414
|
+
// while the Controller is offline. They still validate executable, CLI,
|
|
1415
|
+
// Home, schema, and the exact Task runtime envelope first.
|
|
1416
|
+
checkController: !internalCallback
|
|
1417
|
+
});
|
|
1418
|
+
commandControl = control;
|
|
1419
|
+
}
|
|
1420
|
+
else {
|
|
1421
|
+
await assertCompatibleControlPlanePreflight({ actualHome: home });
|
|
1422
|
+
// Package/build identity is deliberately not part of ordinary Session
|
|
1423
|
+
// continuity. Keep contracts on the Session's frozen digest while the
|
|
1424
|
+
// current CLI is proven protocol/storage compatible.
|
|
1425
|
+
commandControl = control;
|
|
1426
|
+
digest = frozenDigest;
|
|
1427
|
+
}
|
|
1428
|
+
const runtime = assertExactTaskRuntimeEnvironment(serializedRuntime, process.env, frozenDigest, control.yuiHome);
|
|
1335
1429
|
const runtimeDriverCallback = args[0] === "internal"
|
|
1336
1430
|
&& args[1] === "runtime-hook"
|
|
1337
1431
|
&& process.env.YUI_DRIVER_ID !== undefined
|
|
@@ -1349,23 +1443,117 @@ async function preflightManagedTaskControlPlane() {
|
|
|
1349
1443
|
}
|
|
1350
1444
|
: {});
|
|
1351
1445
|
const request = taskFinalReviewInvocation.request;
|
|
1352
|
-
if (request === undefined)
|
|
1353
|
-
return {
|
|
1446
|
+
if (request === undefined) {
|
|
1447
|
+
return {
|
|
1448
|
+
contract: undefined,
|
|
1449
|
+
controlPlane: { digest, descriptor: commandControl },
|
|
1450
|
+
verifiedStore
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1354
1453
|
if (runtime.roleName !== "leader") {
|
|
1355
1454
|
throw new Error("Only the exact Task Leader invocation may establish a final-review contract.");
|
|
1356
1455
|
}
|
|
1357
1456
|
if (request.taskId !== runtime.taskId) {
|
|
1358
1457
|
throw new Error(`Task final-review contract Task id mismatch: expected ${runtime.taskId}, found ${request.taskId}.`);
|
|
1359
1458
|
}
|
|
1459
|
+
const recordedContract = resolveRecordedTaskFinalReviewContract(runtime.taskId, verifiedStore.listWorkItems(runtime.taskId), verifiedStore.listReviewRounds(runtime.taskId), verifiedStore.listEvents(runtime.taskId))?.effective;
|
|
1460
|
+
if (recordedContract !== undefined
|
|
1461
|
+
&& recordedContract.reviewerRoleName !== request.reviewerRoleName) {
|
|
1462
|
+
throw new Error(`Task final-review Reviewer mismatch: expected ${recordedContract.reviewerRoleName}, `
|
|
1463
|
+
+ `found ${request.reviewerRoleName}.`);
|
|
1464
|
+
}
|
|
1360
1465
|
return {
|
|
1361
1466
|
contract: createTaskFinalReviewContract({
|
|
1362
1467
|
taskId: runtime.taskId,
|
|
1363
1468
|
reviewerRoleName: request.reviewerRoleName,
|
|
1364
|
-
|
|
1469
|
+
// Once Task evidence establishes a contract, a compatible Session or
|
|
1470
|
+
// CLI replacement presents that same capability. Package/build identity
|
|
1471
|
+
// must not force a release rebind or invalidate delivery evidence.
|
|
1472
|
+
controlPlaneDigest: recordedContract?.controlPlaneDigest ?? digest
|
|
1365
1473
|
}),
|
|
1474
|
+
controlPlane: { digest, descriptor: commandControl },
|
|
1366
1475
|
verifiedStore
|
|
1367
1476
|
};
|
|
1368
1477
|
}
|
|
1478
|
+
async function preflightManagedGlobalControlPlane(digest) {
|
|
1479
|
+
if (process.env.YUI_SESSION_SCOPE !== "global") {
|
|
1480
|
+
throw new Error("Exact Task control-plane invocation requires its frozen runtime descriptors.");
|
|
1481
|
+
}
|
|
1482
|
+
if (taskFinalReviewInvocation.request !== undefined) {
|
|
1483
|
+
throw new Error("Task final-review contract establishment requires a verified exact Task control-plane invocation.");
|
|
1484
|
+
}
|
|
1485
|
+
const manifestPath = process.env.YUI_SESSION_MANIFEST;
|
|
1486
|
+
if (manifestPath === undefined) {
|
|
1487
|
+
throw new Error("Managed global control-plane invocation requires its Session Manifest.");
|
|
1488
|
+
}
|
|
1489
|
+
const home = resolveYuiHome(process.env);
|
|
1490
|
+
const manifest = assertManagedSessionManifest(home, "global");
|
|
1491
|
+
const expectedRoleKind = process.env.YUI_ROLE === "operator" ? "operator" : "global";
|
|
1492
|
+
if (manifest.owner.scope !== "global"
|
|
1493
|
+
|| manifest.roleKind !== expectedRoleKind) {
|
|
1494
|
+
throw new Error("Managed global invocation does not match its Session Manifest.");
|
|
1495
|
+
}
|
|
1496
|
+
const serializedDescriptor = readFileSync(manifest.controlPlane.descriptorPath, "utf8");
|
|
1497
|
+
const descriptor = parseExactControlPlaneDescriptor(serializedDescriptor);
|
|
1498
|
+
if (resolve(descriptor.yuiHome) !== resolve(home)) {
|
|
1499
|
+
throw new Error("Managed global control-plane descriptor belongs to another YUI_HOME.");
|
|
1500
|
+
}
|
|
1501
|
+
const frozenDigest = exactControlPlaneDigest(descriptor);
|
|
1502
|
+
if (frozenDigest !== manifest.controlPlane.digest
|
|
1503
|
+
|| resolve(manifest.controlPlane.descriptorPath) !== resolve(join(descriptor.yuiHome, "runtime", "control-plane", `${frozenDigest}.json`))
|
|
1504
|
+
|| resolve(manifestPath) !== resolve(join(descriptor.yuiHome, "runtime", "session-manifests", `${manifest.digest}.json`))) {
|
|
1505
|
+
throw new Error("Exact global Session Manifest does not match its control-plane descriptor.");
|
|
1506
|
+
}
|
|
1507
|
+
let commandControl;
|
|
1508
|
+
let commandDigest;
|
|
1509
|
+
if (digest !== undefined) {
|
|
1510
|
+
await assertExactControlPlanePreflight({
|
|
1511
|
+
serializedDescriptor,
|
|
1512
|
+
digest,
|
|
1513
|
+
actualExecutable: process.execPath,
|
|
1514
|
+
actualCliEntry: fileURLToPath(import.meta.url),
|
|
1515
|
+
actualHome: home
|
|
1516
|
+
});
|
|
1517
|
+
commandControl = descriptor;
|
|
1518
|
+
commandDigest = digest;
|
|
1519
|
+
}
|
|
1520
|
+
else {
|
|
1521
|
+
await assertCompatibleControlPlanePreflight({ actualHome: home });
|
|
1522
|
+
commandControl = currentInvocationControlPlane(home);
|
|
1523
|
+
commandDigest = exactControlPlaneDigest(commandControl);
|
|
1524
|
+
}
|
|
1525
|
+
return {
|
|
1526
|
+
contract: undefined,
|
|
1527
|
+
controlPlane: { digest: commandDigest, descriptor: commandControl },
|
|
1528
|
+
verifiedStore: openCompatibleFileTaskStore(home)
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
function currentInvocationControlPlane(home) {
|
|
1532
|
+
return createExactControlPlaneDescriptor({
|
|
1533
|
+
executable: process.execPath,
|
|
1534
|
+
cliEntry: fileURLToPath(import.meta.url),
|
|
1535
|
+
yuiHome: home
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
function assertManagedSessionManifest(home, scope) {
|
|
1539
|
+
const manifestPath = process.env.YUI_SESSION_MANIFEST;
|
|
1540
|
+
if (manifestPath === undefined) {
|
|
1541
|
+
throw new Error("Managed control-plane invocation requires its Session Manifest.");
|
|
1542
|
+
}
|
|
1543
|
+
const manifest = readSessionBootstrapManifest(manifestPath);
|
|
1544
|
+
if (manifest.owner.scope !== scope) {
|
|
1545
|
+
throw new Error("Managed invocation scope does not match its Session Manifest.");
|
|
1546
|
+
}
|
|
1547
|
+
if (scope === "task" && (manifest.owner.scope !== "task"
|
|
1548
|
+
|| manifest.owner.taskId !== process.env.YUI_TASK_ID)) {
|
|
1549
|
+
throw new Error("Managed Task invocation does not match its Session Manifest owner.");
|
|
1550
|
+
}
|
|
1551
|
+
const expectedPath = resolve(home, "runtime", "session-manifests", `${manifest.digest}.json`);
|
|
1552
|
+
if (resolve(manifestPath) !== expectedPath) {
|
|
1553
|
+
throw new Error("Managed Session Manifest path is outside this YUI_HOME.");
|
|
1554
|
+
}
|
|
1555
|
+
return manifest;
|
|
1556
|
+
}
|
|
1369
1557
|
function cleanupCliError(error, fallbackResource) {
|
|
1370
1558
|
if (error instanceof WorkspaceCleanupBlockedError) {
|
|
1371
1559
|
return usageError(error.message, undefined, cleanupBlockedDetails(error.reason, error.resource, error.retryable));
|
|
@@ -1630,6 +1818,36 @@ async function prepareReviewLaneWorkspaces(taskId, reviewRoundId, store, prepare
|
|
|
1630
1818
|
return map;
|
|
1631
1819
|
}
|
|
1632
1820
|
async function directTaskMainSnapshotForTaskCommand(args, store, preparer, environment, taskFinalReviewContract) {
|
|
1821
|
+
const deliveryOption = args.indexOf("--delivery", 3);
|
|
1822
|
+
const deliveryPromotion = args[0] === "task"
|
|
1823
|
+
&& args[1] === "update"
|
|
1824
|
+
&& args[2] !== undefined
|
|
1825
|
+
&& (args.includes("--require-integration", 3)
|
|
1826
|
+
|| (deliveryOption >= 0 && args[deliveryOption + 1] === "integrated"));
|
|
1827
|
+
if (deliveryPromotion) {
|
|
1828
|
+
const task = store.getTask(args[2]);
|
|
1829
|
+
if (task === null
|
|
1830
|
+
|| (task.status !== "active" && task.status !== "draft")
|
|
1831
|
+
|| task.requireIntegration === true
|
|
1832
|
+
|| task.projectBindings.length === 0) {
|
|
1833
|
+
return undefined;
|
|
1834
|
+
}
|
|
1835
|
+
const workspace = store.getTaskWorkspace(task.id);
|
|
1836
|
+
if (task.status === "draft" && workspace === null)
|
|
1837
|
+
return undefined;
|
|
1838
|
+
if (workspace === null
|
|
1839
|
+
|| workspace.owner.type !== "task"
|
|
1840
|
+
|| workspace.owner.taskId !== task.id) {
|
|
1841
|
+
throw usageError(`Task has no authoritative main workspace: ${task.id}.`);
|
|
1842
|
+
}
|
|
1843
|
+
try {
|
|
1844
|
+
return await preparer.snapshotDirectTaskMain(workspace, task.projectBindings.map(({ projectId }) => projectId));
|
|
1845
|
+
}
|
|
1846
|
+
catch (error) {
|
|
1847
|
+
throw usageError(`Delivery promotion Task-main verification failed for ${task.id}: `
|
|
1848
|
+
+ `${error instanceof Error ? error.message : String(error)}`);
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1633
1851
|
if (taskFinalReviewContract === undefined
|
|
1634
1852
|
|| args[0] !== "task"
|
|
1635
1853
|
|| args[1] !== "work"
|
|
@@ -1660,7 +1878,7 @@ async function directTaskMainSnapshotForTaskCommand(args, store, preparer, envir
|
|
|
1660
1878
|
throw usageError(error instanceof Error ? error.message : String(error));
|
|
1661
1879
|
}
|
|
1662
1880
|
}
|
|
1663
|
-
async function actualTaskReviewCandidateForTaskCommand(args, store, preparer, environment
|
|
1881
|
+
async function actualTaskReviewCandidateForTaskCommand(args, store, preparer, environment) {
|
|
1664
1882
|
if (args[0] !== "task")
|
|
1665
1883
|
return undefined;
|
|
1666
1884
|
let taskId;
|
|
@@ -1669,12 +1887,9 @@ async function actualTaskReviewCandidateForTaskCommand(args, store, preparer, en
|
|
|
1669
1887
|
if (task === null || task.status !== "active" || task.projectBindings.length === 0) {
|
|
1670
1888
|
return undefined;
|
|
1671
1889
|
}
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
&& !establishedFinalRound) {
|
|
1676
|
-
return undefined;
|
|
1677
|
-
}
|
|
1890
|
+
// Every Project-backed completion, including direct delivery, must freeze
|
|
1891
|
+
// a clean committed Task-main snapshot. Review policy only decides whether
|
|
1892
|
+
// that head also needs an independent ReviewRound.
|
|
1678
1893
|
taskId = task.id;
|
|
1679
1894
|
}
|
|
1680
1895
|
else if (args[1] === "review"
|
|
@@ -213,6 +213,36 @@ export function renderExecutionAudit(report, width = defaultTableWidth()) {
|
|
|
213
213
|
else {
|
|
214
214
|
lines.push("", ...sectionError("workItems", report));
|
|
215
215
|
}
|
|
216
|
+
if (report.orchestration.status === "ok" && report.orchestration.data !== undefined) {
|
|
217
|
+
const orchestration = report.orchestration.data;
|
|
218
|
+
lines.push("", `Orchestration: ${orchestration.tasks.length} Task(s) · ${orchestration.advisoryCount} advisory finding(s)`);
|
|
219
|
+
if (orchestration.tasks.length > 0) {
|
|
220
|
+
lines.push(renderTable("Task orchestration metrics", [
|
|
221
|
+
{ header: "Task", minWidth: 7, maxWidth: 14 },
|
|
222
|
+
{ header: "Delivery", minWidth: 8, maxWidth: 10 },
|
|
223
|
+
{ header: "Runs", minWidth: 4, maxWidth: 6 },
|
|
224
|
+
{ header: "WIs", minWidth: 3, maxWidth: 5 },
|
|
225
|
+
{ header: "Review F/D/N", minWidth: 12, maxWidth: 16 },
|
|
226
|
+
{ header: "Integration A/F/R", minWidth: 17, maxWidth: 20 },
|
|
227
|
+
{ header: "Pre-progress gen", minWidth: 12, maxWidth: 16 },
|
|
228
|
+
{ header: "Terminal ws", minWidth: 10, maxWidth: 12 },
|
|
229
|
+
{ header: "Advice", minWidth: 6, maxWidth: 8 }
|
|
230
|
+
], orchestration.tasks.map((task) => [
|
|
231
|
+
task.taskId,
|
|
232
|
+
task.deliveryPath,
|
|
233
|
+
String(task.runs.total),
|
|
234
|
+
String(task.workItems),
|
|
235
|
+
`${task.reviews.full}/${task.reviews.delta}/${task.reviews.nonSemantic}`,
|
|
236
|
+
`${task.integrations.attempts}/${task.integrations.failed}/${task.integrations.repeatedIdentities}`,
|
|
237
|
+
String(task.providerGenerationsBeforeFirstProgress),
|
|
238
|
+
String(task.terminalWorkspaceCount),
|
|
239
|
+
String(task.advisories.length)
|
|
240
|
+
]), width));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
lines.push("", ...sectionError("orchestration", report));
|
|
245
|
+
}
|
|
216
246
|
if (report.storage.status === "ok" && report.storage.data !== undefined) {
|
|
217
247
|
const storage = report.storage.data;
|
|
218
248
|
lines.push("", `Storage: backend ${storage.backend} · state.json ${formatBytes(storage.stateJsonBytes)} · yui.db ${formatBytes(storage.databaseBytes)}`
|
|
@@ -36,9 +36,8 @@ function roleContext(args, store, options) {
|
|
|
36
36
|
if (environment.YUI_SESSION_SCOPE !== "global" || environment.YUI_ROLE !== name) {
|
|
37
37
|
throw usageError("Managed GlobalRole context is outside the exact Session authority.");
|
|
38
38
|
}
|
|
39
|
-
if (environment.YUI_SESSION_MANIFEST === undefined
|
|
40
|
-
|
|
41
|
-
throw usageError("Managed GlobalRole context requires the exact Session Manifest and CLI.");
|
|
39
|
+
if (environment.YUI_SESSION_MANIFEST === undefined) {
|
|
40
|
+
throw usageError("Managed GlobalRole context requires its Session Manifest.");
|
|
42
41
|
}
|
|
43
42
|
}
|
|
44
43
|
const role = requireRole(name, store);
|
|
@@ -81,7 +80,12 @@ function roleContext(args, store, options) {
|
|
|
81
80
|
: ["perform-global-role-request"]
|
|
82
81
|
},
|
|
83
82
|
sessionManifestPath: environment.YUI_SESSION_MANIFEST,
|
|
84
|
-
|
|
83
|
+
cliCommand: "yui",
|
|
84
|
+
// Compatibility hint for already-running Sessions. New instructions use
|
|
85
|
+
// the stable command name and do not bind behavior to this wrapper path.
|
|
86
|
+
...(environment.YUI_SESSION_CLI === undefined
|
|
87
|
+
? {}
|
|
88
|
+
: { legacySessionCliPath: environment.YUI_SESSION_CLI })
|
|
85
89
|
});
|
|
86
90
|
if (options.jsonOutput === true)
|
|
87
91
|
return `${JSON.stringify(context)}\n`;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { usageError } from "../errors/cliError.js";
|
|
2
2
|
import { createRoleSessionSet } from "../executor/agentExecutor.js";
|
|
3
|
-
import { listOperatorSessions, prepareOperatorNewSession, prepareOperatorResumeSession } from "../operator/operatorSessionHistory.js";
|
|
3
|
+
import { listOperatorSessions, projectOperatorStatus, prepareOperatorNewSession, prepareOperatorResumeSession } from "../operator/operatorSessionHistory.js";
|
|
4
4
|
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
5
5
|
import { formatRelativeTimestamp } from "../output/timePresentation.js";
|
|
6
6
|
import { updateGlobalRole } from "../role/role.js";
|
|
@@ -11,6 +11,7 @@ export function runOperatorCommand(args, store, options = {}) {
|
|
|
11
11
|
const [command, ...rest] = args;
|
|
12
12
|
switch (command) {
|
|
13
13
|
case "list": return listSessions(rest, store, options);
|
|
14
|
+
case "status": return status(rest, store, options);
|
|
14
15
|
case "new": return newSession(rest, store);
|
|
15
16
|
case "resume": return resumeSession(rest, store);
|
|
16
17
|
case "submit": return submit(rest, store, options);
|
|
@@ -20,6 +21,46 @@ export function runOperatorCommand(args, store, options = {}) {
|
|
|
20
21
|
: `Unknown command: operator ${command}`);
|
|
21
22
|
}
|
|
22
23
|
}
|
|
24
|
+
function status(args, store, options) {
|
|
25
|
+
if (args.length !== 0)
|
|
26
|
+
throw usageError("Operator status usage: yui operator status.");
|
|
27
|
+
const role = requireOperator(store);
|
|
28
|
+
const activeBinding = role.agentBindings[role.activeAgentId];
|
|
29
|
+
if (activeBinding === undefined) {
|
|
30
|
+
throw usageError(`Operator active Agent is not bound: ${role.activeAgentId}.`);
|
|
31
|
+
}
|
|
32
|
+
const projection = projectOperatorStatus(store.getGlobalRoleSessionSet(role.name), role.activeAgentId, activeBinding.adapterId);
|
|
33
|
+
const writer = projection.writer;
|
|
34
|
+
const writerLines = [
|
|
35
|
+
"Operator status",
|
|
36
|
+
"Active writer:",
|
|
37
|
+
` State: ${writer.state}`,
|
|
38
|
+
` Agent: ${writer.agentId}`,
|
|
39
|
+
` Adapter: ${adapterLabel(writer.adapterId)}`,
|
|
40
|
+
` Conversation: ${writer.sessionRef ?? "-"}`,
|
|
41
|
+
` Native session: ${writer.nativeSessionId ?? "-"}`,
|
|
42
|
+
` Session state: ${writer.sessionStatus ?? "-"}`
|
|
43
|
+
];
|
|
44
|
+
const history = projection.historicalConversations;
|
|
45
|
+
const historyOutput = history.length === 0
|
|
46
|
+
? "Historical conversations: none"
|
|
47
|
+
: renderTable("Historical conversations", [
|
|
48
|
+
{ header: "Updated", minWidth: 7, maxWidth: 12 },
|
|
49
|
+
{ header: "Agent", minWidth: 6, maxWidth: 16 },
|
|
50
|
+
{ header: "Conversation", minWidth: 20, maxWidth: 72 },
|
|
51
|
+
{ header: "State", minWidth: 7, maxWidth: 10 }
|
|
52
|
+
], history.map((session) => [
|
|
53
|
+
formatRelativeTimestamp(session.updatedAt, options.now?.() ?? new Date()),
|
|
54
|
+
adapterLabel(session.adapterId),
|
|
55
|
+
session.displayTitle,
|
|
56
|
+
session.state
|
|
57
|
+
]), options.width ?? defaultTableWidth());
|
|
58
|
+
return {
|
|
59
|
+
kind: "output",
|
|
60
|
+
output: `${writerLines.join("\n")}\n\n${historyOutput}\n`,
|
|
61
|
+
data: projection
|
|
62
|
+
};
|
|
63
|
+
}
|
|
23
64
|
function submit(rest, store, options) {
|
|
24
65
|
const usage = "Operator submit usage: yui operator submit (<body>|--body-file <path|->) [--task <id>].";
|
|
25
66
|
const positionals = [];
|