@zq-silk/yui 0.8.9 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +48 -46
- package/README.md +72 -42
- package/dist/cli/commandCatalog.js +16 -8
- package/dist/cli.js +27 -32
- package/dist/commands/executionAuditCommands.js +2 -2
- package/dist/commands/globalRoleCommands.js +0 -12
- package/dist/commands/sessionCommands.js +116 -0
- package/dist/commands/taskBaseCommands.js +1 -11
- package/dist/commands/taskCommands.js +123 -340
- package/dist/commands/taskCompletionGate.js +15 -12
- package/dist/commands/taskContextCommand.js +18 -11
- package/dist/commands/taskNextActionCommand.js +4 -5
- package/dist/commands/taskWorkspaceCommands.js +2 -2
- package/dist/controller/clientRuntime.js +65 -0
- package/dist/controller/fileSchedulerStoreAdapter.js +2 -49
- package/dist/doctor/doctor.js +16 -12
- package/dist/execution/executionGroup.js +0 -3
- package/dist/executor/agentAdapter.js +39 -42
- package/dist/executor/agentExecutor.js +4 -2
- package/dist/executor/codexConfigConflict.js +40 -16
- package/dist/executor/effectiveLaunch.js +33 -3
- package/dist/executor/fileRoleLaunchPlanner.js +15 -24
- package/dist/integration/deliveryObligation.js +72 -0
- package/dist/integration/gitIntegrationService.js +1 -1
- package/dist/lifecycle/exactRunTerminalization.js +12 -8
- package/dist/observability/orchestrationMetrics.js +12 -26
- package/dist/profile/agentProfile.js +1 -1
- package/dist/repository/taskBaseFreshness.js +5 -11
- package/dist/repository/taskWorkspaceCoordinator.js +2 -0
- package/dist/repository/taskWorkspacePreparer.js +173 -26
- package/dist/review/reviewRound.js +41 -24
- package/dist/role/role.js +0 -9
- package/dist/runtime/{firstProgressStopLoss.js → firstProgressAdvisory.js} +7 -21
- package/dist/scheduler/leaderWakeupProcessor.js +0 -25
- package/dist/setup/setupCommand.js +0 -5
- package/dist/storage/migration/productionRegistry.js +138 -0
- package/dist/storage/sqliteStore.js +2 -1
- package/dist/storage/taskStore.js +27 -27
- package/dist/storage/upgrade/upgradeOrchestrator.js +35 -7
- package/dist/task/completionReadiness.js +35 -25
- package/dist/task/nextAction.js +70 -78
- package/dist/task/task.js +12 -19
- package/dist/web/assets/client/components.js +3 -1
- package/dist/web/assets/client/i18n.js +6 -0
- package/dist/web/webSnapshot.js +0 -3
- package/i18n/README.zh-CN.md +33 -24
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +55 -52
- package/skills/yui-operator/SKILL.md +31 -40
- package/skills/yui-reviewer/SKILL.md +18 -12
- package/skills/yui-worker/SKILL.md +5 -3
|
@@ -1,5 +1,121 @@
|
|
|
1
1
|
import { usageError } from "../errors/cliError.js";
|
|
2
2
|
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
3
|
+
export function parseSessionStopOptions(args) {
|
|
4
|
+
if (args.length !== 1 || args[0] !== "--all") {
|
|
5
|
+
throw usageError("Session stop usage: yui session stop --all.");
|
|
6
|
+
}
|
|
7
|
+
return { all: true };
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Stop every current managed Session only when all of them are idle. The
|
|
11
|
+
* all-or-nothing preflight prevents a maintenance command from silently
|
|
12
|
+
* stopping half the topology while another Role is still working.
|
|
13
|
+
*/
|
|
14
|
+
export async function runSessionStopCommand(input) {
|
|
15
|
+
void input.options;
|
|
16
|
+
if (input.environment?.YUI_SESSION_SCOPE !== undefined) {
|
|
17
|
+
throw usageError("Session stop must be run by the user from a normal shell, outside a managed Yui Session.");
|
|
18
|
+
}
|
|
19
|
+
const maintenance = input.runtime.beginMaintenance();
|
|
20
|
+
let completed = false;
|
|
21
|
+
try {
|
|
22
|
+
const initial = input.runtime.snapshot();
|
|
23
|
+
const initiallyBlocked = blockedSessions(initial);
|
|
24
|
+
if (initiallyBlocked.length > 0)
|
|
25
|
+
return blockedStopResult(initiallyBlocked);
|
|
26
|
+
// Stop and drain the Controller while the handover fence prevents a new
|
|
27
|
+
// Leader dispatch. The explicit full pass settles pending runtime inbox and
|
|
28
|
+
// lifecycle work that may exist without a current Session candidate.
|
|
29
|
+
await input.runtime.drainController();
|
|
30
|
+
await input.runtime.stopController();
|
|
31
|
+
// Re-read the authoritative facts after the drain because a Run that was
|
|
32
|
+
// already inside a pass may have changed state meanwhile.
|
|
33
|
+
const settled = input.runtime.snapshot();
|
|
34
|
+
const blockedAfterDrain = blockedSessions(settled);
|
|
35
|
+
if (blockedAfterDrain.length > 0) {
|
|
36
|
+
await input.runtime.startController();
|
|
37
|
+
return blockedStopResult(blockedAfterDrain);
|
|
38
|
+
}
|
|
39
|
+
for (const session of settled.candidates) {
|
|
40
|
+
const candidate = settled.dormant.find((entry) => sameStopCandidate(session, entry));
|
|
41
|
+
if (candidate === undefined) {
|
|
42
|
+
throw new Error(`Dormant Session changed during maintenance: ${renderSessionOwner(session)}.`);
|
|
43
|
+
}
|
|
44
|
+
await input.runtime.stopDormantSession(candidate);
|
|
45
|
+
}
|
|
46
|
+
const count = settled.candidates.length;
|
|
47
|
+
completed = true;
|
|
48
|
+
return {
|
|
49
|
+
output: count === 0
|
|
50
|
+
? "No managed Sessions were running. The Controller remains stopped; run `yui update` now.\n"
|
|
51
|
+
: `Stopped ${count} managed Sessions. The Controller remains stopped; run \`yui update\` now.\n`,
|
|
52
|
+
data: { stopped: settled.candidates, blocked: [] },
|
|
53
|
+
exitCode: 0
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
if (!completed) {
|
|
58
|
+
try {
|
|
59
|
+
await input.runtime.startController();
|
|
60
|
+
}
|
|
61
|
+
catch (restartError) {
|
|
62
|
+
throw new AggregateError([error, restartError], "Session maintenance failed and the Controller could not be restarted.");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
finally {
|
|
68
|
+
maintenance.release();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function blockedSessions(input) {
|
|
72
|
+
return input.candidates.flatMap((session) => {
|
|
73
|
+
const idle = input.dormant.some((candidate) => sameStopCandidate(session, candidate));
|
|
74
|
+
if (idle)
|
|
75
|
+
return [];
|
|
76
|
+
return [{
|
|
77
|
+
session,
|
|
78
|
+
reason: session.status === "running"
|
|
79
|
+
? "running"
|
|
80
|
+
: "runtime-work-pending"
|
|
81
|
+
}];
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function blockedStopResult(blocked) {
|
|
85
|
+
const details = blocked.map(({ session, reason }) => (`- ${renderSessionOwner(session)} (${reason === "running"
|
|
86
|
+
? "a Turn or Run is still running"
|
|
87
|
+
: "an active Run or lifecycle operation is still pending"})`));
|
|
88
|
+
return {
|
|
89
|
+
output: [
|
|
90
|
+
`Cannot stop managed Sessions: ${blocked.length} Session(s) are still busy.`,
|
|
91
|
+
...details,
|
|
92
|
+
"No Session was stopped. Let the listed work finish, then run `yui session stop --all` again."
|
|
93
|
+
].join("\n") + "\n",
|
|
94
|
+
data: { stopped: [], blocked },
|
|
95
|
+
exitCode: 5
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function sameStopCandidate(session, candidate) {
|
|
99
|
+
if (session.owner.scope !== candidate.owner.scope)
|
|
100
|
+
return false;
|
|
101
|
+
if (session.owner.roleName !== candidate.owner.roleName)
|
|
102
|
+
return false;
|
|
103
|
+
if (session.owner.scope === "task"
|
|
104
|
+
&& candidate.owner.scope === "task"
|
|
105
|
+
&& session.owner.taskId !== candidate.owner.taskId) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
return session.agentId === candidate.agentId
|
|
109
|
+
&& session.adapterId === candidate.adapterId
|
|
110
|
+
&& session.nativeSessionId === candidate.nativeSessionId
|
|
111
|
+
&& session.launchId === candidate.launchId
|
|
112
|
+
&& session.sessionUpdatedAt === candidate.sessionUpdatedAt;
|
|
113
|
+
}
|
|
114
|
+
function renderSessionOwner(session) {
|
|
115
|
+
return session.owner.scope === "task"
|
|
116
|
+
? `${session.owner.taskId}/${session.owner.roleName}`
|
|
117
|
+
: `global/${session.owner.roleName}`;
|
|
118
|
+
}
|
|
3
119
|
export function parseSessionReconcileOptions(args) {
|
|
4
120
|
const allowed = new Set(["--report", "--cleanup"]);
|
|
5
121
|
if (args.some((argument) => !allowed.has(argument))) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { usageError } from "../errors/cliError.js";
|
|
2
|
-
import {
|
|
2
|
+
import { inspectTaskBaseFreshness, renderTaskBaseFreshnessReport } from "../repository/taskBaseFreshness.js";
|
|
3
3
|
export async function runTaskBaseStatusCommand(args, store, options = {}) {
|
|
4
4
|
const usage = "Task base status usage: yui task base status <task> [--refresh].";
|
|
5
5
|
const refresh = args.includes("--refresh");
|
|
@@ -11,19 +11,9 @@ export async function runTaskBaseStatusCommand(args, store, options = {}) {
|
|
|
11
11
|
git: options.git,
|
|
12
12
|
refresh
|
|
13
13
|
});
|
|
14
|
-
assertReportIsDeliverySafe(report);
|
|
15
14
|
return {
|
|
16
15
|
kind: "output",
|
|
17
16
|
output: renderTaskBaseFreshnessReport(report),
|
|
18
17
|
data: report
|
|
19
18
|
};
|
|
20
19
|
}
|
|
21
|
-
function assertReportIsDeliverySafe(report) {
|
|
22
|
-
try {
|
|
23
|
-
assertTaskBaseFreshnessForCompletion(report);
|
|
24
|
-
}
|
|
25
|
-
catch (error) {
|
|
26
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
27
|
-
throw usageError(`${renderTaskBaseFreshnessReport(report)}${message}`);
|
|
28
|
-
}
|
|
29
|
-
}
|