@zq-silk/yui 0.12.4 → 0.13.1
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/dist/cli/updatePorts.js +18 -2
- package/dist/cli.js +3 -6
- package/dist/commands/durableJobCommands.js +5 -17
- package/dist/commands/executionAuditCommands.js +1 -1
- package/dist/commands/taskActor.js +17 -61
- package/dist/commands/taskCommands.js +44 -166
- package/dist/commands/taskIntegrationCommands.js +7 -9
- package/dist/commands/taskIntegrationQueueCommands.js +9 -11
- package/dist/commands/taskPublicationCommands.js +0 -4
- package/dist/commands/taskWorkspaceCommands.js +1 -1
- package/dist/controller/jobClient.js +5 -8
- package/dist/controller/jobControl.js +59 -105
- package/dist/integration/gitIntegrationService.js +3 -3
- package/dist/integration/integrationAttempt.js +13 -9
- package/dist/integration/integrationQueueService.js +7 -7
- package/dist/milestone/milestone.js +9 -3
- package/dist/review/deltaRecheck.js +1 -1
- package/dist/review/reviewFinding.js +3 -3
- package/dist/review/reviewRound.js +11 -8
- package/dist/storage/migration/productionRegistry.js +9 -0
- package/dist/storage/taskStore.js +9 -8
- package/dist/storage/upgrade/upgradeOrchestrator.js +2 -2
- package/dist/storage/upgradeCoordination.js +6 -5
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +4 -0
- package/skills/yui-operator/SKILL.md +19 -13
package/dist/cli/updatePorts.js
CHANGED
|
@@ -765,9 +765,25 @@ function runControllerCommandOutput(home, environment, spawn, method, cliBinary)
|
|
|
765
765
|
const args = cliBinary === undefined
|
|
766
766
|
? [UPDATE_CLI_PATH, "--json", "controller", method]
|
|
767
767
|
: ["--json", "controller", method];
|
|
768
|
-
const result = spawn(command, args, {
|
|
768
|
+
const result = spawn(command, args, {
|
|
769
|
+
cwd: process.cwd(),
|
|
770
|
+
env: {
|
|
771
|
+
...environment,
|
|
772
|
+
YUI_HOME: home,
|
|
773
|
+
// The restart child participates in the handover owned by this parent
|
|
774
|
+
// update process. Without the owner PID it waits on its parent's live
|
|
775
|
+
// lock as though a foreign update owned the Home, then times out.
|
|
776
|
+
YUI_UPDATE_HANDOVER_OWNER_PID: String(process.pid)
|
|
777
|
+
},
|
|
778
|
+
shell: false
|
|
779
|
+
});
|
|
769
780
|
if (result.error !== undefined || result.status !== 0) {
|
|
770
|
-
|
|
781
|
+
const detail = structuredErrorMessage(result) ?? result.stderr.toString("utf8").trim();
|
|
782
|
+
const error = new Error(`Controller ${method} failed (exit ${result.status ?? "null"})${detail.length === 0 ? "." : `: ${detail}`}`);
|
|
783
|
+
const code = controllerErrorCodeFromResult(result);
|
|
784
|
+
if (code !== undefined)
|
|
785
|
+
Object.assign(error, { code });
|
|
786
|
+
throw error;
|
|
771
787
|
}
|
|
772
788
|
const parsed = JSON.parse(result.stdout.toString("utf8"));
|
|
773
789
|
if (!isRecord(parsed) || parsed.ok !== true) {
|
package/dist/cli.js
CHANGED
|
@@ -807,7 +807,7 @@ export async function main() {
|
|
|
807
807
|
if (resolved[1] === "integration") {
|
|
808
808
|
const result = await runTaskIntegrationCommand(resolved.slice(2), store, home, {
|
|
809
809
|
environment: process.env,
|
|
810
|
-
jobPort: createControllerIntegrationJobPort(home, { environment: process.env
|
|
810
|
+
jobPort: createControllerIntegrationJobPort(home, { environment: process.env })
|
|
811
811
|
});
|
|
812
812
|
emit(result.output, false, result.data);
|
|
813
813
|
return;
|
|
@@ -896,10 +896,7 @@ export async function main() {
|
|
|
896
896
|
}
|
|
897
897
|
const reference = cliWorkItemReference(workItemId, process.env);
|
|
898
898
|
const qualified = `${reference.taskId}/${reference.localId}`;
|
|
899
|
-
|
|
900
|
-
if (actor === "operator") {
|
|
901
|
-
throw usageError("Only the Task Leader may clean a WorkItem from a managed Session.");
|
|
902
|
-
}
|
|
899
|
+
taskActor(process.env, reference.taskId);
|
|
903
900
|
if (disposition === "--runtime-only") {
|
|
904
901
|
let runtimeCleanup;
|
|
905
902
|
try {
|
|
@@ -1117,7 +1114,7 @@ export async function main() {
|
|
|
1117
1114
|
// Keep completion offline by default. An explicit refresh is the only
|
|
1118
1115
|
// path that may fetch and reconcile a moved remote baseline.
|
|
1119
1116
|
if (refreshRemote) {
|
|
1120
|
-
await reconcileTaskRemoteBaselines(resolved[2], store, home, { environment: process.env, jobPort: createControllerIntegrationJobPort(home, { environment: process.env
|
|
1117
|
+
await reconcileTaskRemoteBaselines(resolved[2], store, home, { environment: process.env, jobPort: createControllerIntegrationJobPort(home, { environment: process.env }) });
|
|
1121
1118
|
}
|
|
1122
1119
|
}
|
|
1123
1120
|
}
|
|
@@ -34,10 +34,7 @@ export async function runDurableJobCommand(args, options) {
|
|
|
34
34
|
async function startJob(args, options) {
|
|
35
35
|
const parsed = parseStartArgs(args);
|
|
36
36
|
await ensureFileTaskController(options.home, { environment: options.environment });
|
|
37
|
-
|
|
38
|
-
// Controller rejects a Reviewer, constrains a Worker to its own Work Item,
|
|
39
|
-
// and requires a verified Leader assertion for user scope.
|
|
40
|
-
const caller = resolveJobCaller(options.environment, parsed.taskId, options.store);
|
|
37
|
+
const caller = resolveJobCaller(options.environment, parsed.taskId);
|
|
41
38
|
const params = {
|
|
42
39
|
taskId: parsed.taskId,
|
|
43
40
|
owner: parsed.owner,
|
|
@@ -89,7 +86,7 @@ async function cancelJob(args, options) {
|
|
|
89
86
|
const ref = parseRefArgs(args, "cancel");
|
|
90
87
|
await ensureFileTaskController(options.home, { environment: options.environment });
|
|
91
88
|
// rr8/rr12: Bind the cancel request to the caller's managed identity.
|
|
92
|
-
const caller = resolveJobCaller(options.environment, ref.taskId
|
|
89
|
+
const caller = resolveJobCaller(options.environment, ref.taskId);
|
|
93
90
|
const result = await cancelDurableJob(options.home, ref.taskId, ref.jobId, caller);
|
|
94
91
|
if (options.json === true)
|
|
95
92
|
return `${JSON.stringify(result, null, 2)}\n`;
|
|
@@ -99,20 +96,11 @@ async function cancelJob(args, options) {
|
|
|
99
96
|
}
|
|
100
97
|
async function acknowledgeJob(args, options) {
|
|
101
98
|
const ref = parseRefArgs(args, "acknowledge");
|
|
102
|
-
// rr5/f5(a): only the Task Leader may acknowledge an
|
|
103
|
-
// unknown-needs-attention job. A non-Leader (Worker, Operator, or plain
|
|
104
|
-
// user) is rejected at the CLI boundary.
|
|
105
99
|
if (options.store === undefined) {
|
|
106
|
-
throw usageError("job acknowledge requires a Task store to resolve the
|
|
100
|
+
throw usageError("job acknowledge requires a Task store to resolve the caller.");
|
|
107
101
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
throw usageError("Only the Task Leader may acknowledge a DurableJob: "
|
|
111
|
-
+ `${ref.taskId}.`);
|
|
112
|
-
}
|
|
113
|
-
// Carry the full managed task caller, including its ephemeral launch key.
|
|
114
|
-
// A durable leaderAssertion by itself is intentionally not sufficient.
|
|
115
|
-
const caller = resolveJobCaller(options.environment, ref.taskId, options.store);
|
|
102
|
+
taskLocalActor(options.store, options.environment, ref.taskId, options.home);
|
|
103
|
+
const caller = resolveJobCaller(options.environment, ref.taskId);
|
|
116
104
|
await ensureFileTaskController(options.home, { environment: options.environment });
|
|
117
105
|
const result = await acknowledgeDurableJob(options.home, ref.taskId, ref.jobId, caller);
|
|
118
106
|
if (options.json === true)
|
|
@@ -156,7 +156,7 @@ export function renderExecutionAudit(report, width = defaultTableWidth()) {
|
|
|
156
156
|
`Delta-rechecks: ${reviews.deltaRechecks.total} total · `
|
|
157
157
|
+ `${reviews.deltaRechecks.equivalentAndAccepted} accepted · `
|
|
158
158
|
+ `${reviews.deltaRechecks.finding} finding · `
|
|
159
|
-
+ `${reviews.deltaRechecks.requiresFullReview} requiring
|
|
159
|
+
+ `${reviews.deltaRechecks.requiresFullReview} requiring Task Agent decision`
|
|
160
160
|
]));
|
|
161
161
|
}
|
|
162
162
|
else {
|
|
@@ -67,29 +67,26 @@ export function projectActor(environment) {
|
|
|
67
67
|
}
|
|
68
68
|
/**
|
|
69
69
|
* rr8: Resolve the caller identity for a `job.start`/`job.cancel` request from
|
|
70
|
-
* the managed Session environment. The Controller
|
|
71
|
-
*
|
|
72
|
-
* its own Work Item's jobs, and a Leader or plain user retains full access.
|
|
70
|
+
* the managed Session environment. The Controller verifies the Agent Session
|
|
71
|
+
* and its scope; Role does not narrow Task control authority.
|
|
73
72
|
*
|
|
74
73
|
* rr12: The identity is now Controller-verified rather than self-reported:
|
|
75
74
|
* - A managed Task Session (`YUI_SESSION_SCOPE=task`) returns `scope: "task"`
|
|
76
|
-
* with its Role and Run.
|
|
77
|
-
*
|
|
78
|
-
* a possibly-stale `YUI_RUN_ID`), which the Controller verifies through the
|
|
79
|
-
* same active-Run + in-flight + Session check as `job.acknowledge`.
|
|
75
|
+
* with its Role and Run. For a Leader, the explicit current-Turn Run id is
|
|
76
|
+
* preferred over a possibly stale `YUI_RUN_ID`.
|
|
80
77
|
*
|
|
81
78
|
* rr13: A managed Task Session also carries `callerKey` — the
|
|
82
79
|
* `YUI_JOB_CALLER_KEY` injected at its native Session launch. The Controller
|
|
83
80
|
* hashes it and compares against the durable `jobCallerKeyHashes` map, so a
|
|
84
81
|
* client that reads durable state cannot replay the caller. A `user`-scope
|
|
85
|
-
* caller is rejected outright for job.start/job.cancel (fail-closed);
|
|
86
|
-
*
|
|
82
|
+
* caller is rejected outright for job.start/job.cancel (fail-closed); a
|
|
83
|
+
* managed global Agent carries its own durable Session identity.
|
|
87
84
|
*
|
|
88
85
|
* A managed Task Session may only start jobs for its own Task. An incomplete
|
|
89
86
|
* managed identity (role/agent/run/session vars without a scope) is rejected
|
|
90
87
|
* rather than silently downgraded to user authority.
|
|
91
88
|
*/
|
|
92
|
-
export function resolveJobCaller(environment, taskId
|
|
89
|
+
export function resolveJobCaller(environment, taskId) {
|
|
93
90
|
const env = environment ?? {};
|
|
94
91
|
if (env.YUI_SESSION_SCOPE === "task") {
|
|
95
92
|
if (env.YUI_TASK_ID !== taskId) {
|
|
@@ -101,9 +98,7 @@ export function resolveJobCaller(environment, taskId, store) {
|
|
|
101
98
|
// Controller boundary.
|
|
102
99
|
const callerKey = env[JOB_CALLER_KEY_ENV];
|
|
103
100
|
if (role === LEADER_ROLE) {
|
|
104
|
-
// Prefer the explicit current-
|
|
105
|
-
// stale YUI_RUN_ID/launch. The Controller verifies it against the
|
|
106
|
-
// active in-flight Leader Run.
|
|
101
|
+
// Prefer the explicit current-Turn Run over a possibly stale YUI_RUN_ID.
|
|
107
102
|
const assertion = leaderActionAssertion(env);
|
|
108
103
|
if (assertion !== undefined && assertion !== "invalid") {
|
|
109
104
|
return {
|
|
@@ -111,7 +106,6 @@ export function resolveJobCaller(environment, taskId, store) {
|
|
|
111
106
|
taskId,
|
|
112
107
|
role,
|
|
113
108
|
runId: assertion.runId,
|
|
114
|
-
receiptId: assertion.receiptId,
|
|
115
109
|
...(callerKey === undefined ? {} : { callerKey })
|
|
116
110
|
};
|
|
117
111
|
}
|
|
@@ -125,7 +119,14 @@ export function resolveJobCaller(environment, taskId, store) {
|
|
|
125
119
|
};
|
|
126
120
|
}
|
|
127
121
|
if (env.YUI_SESSION_SCOPE === "global") {
|
|
128
|
-
return
|
|
122
|
+
return {
|
|
123
|
+
scope: "global",
|
|
124
|
+
role: env.YUI_ROLE,
|
|
125
|
+
agentId: env.YUI_AGENT_ID,
|
|
126
|
+
adapterId: env.YUI_ADAPTER_ID,
|
|
127
|
+
launchId: env.YUI_LAUNCH_ID,
|
|
128
|
+
nativeSessionId: env.YUI_NATIVE_SESSION_ID
|
|
129
|
+
};
|
|
129
130
|
}
|
|
130
131
|
if (env.YUI_ROLE !== undefined
|
|
131
132
|
|| env.YUI_AGENT_ID !== undefined
|
|
@@ -133,52 +134,7 @@ export function resolveJobCaller(environment, taskId, store) {
|
|
|
133
134
|
|| env.YUI_NATIVE_SESSION_ID !== undefined) {
|
|
134
135
|
throw usageError("Managed Agent identity is incomplete; refusing to infer user authority.");
|
|
135
136
|
}
|
|
136
|
-
return
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* rr12: Build a `scope: "user"` caller. When a store is available, attach the
|
|
140
|
-
* active in-flight Leader assertion so the Controller can verify the request
|
|
141
|
-
* acts under real Leader authority. Without a store, return the bare
|
|
142
|
-
* `{scope: "user"}` which the Controller rejects (fail-closed).
|
|
143
|
-
*/
|
|
144
|
-
function userCaller(store, taskId) {
|
|
145
|
-
if (store === undefined)
|
|
146
|
-
return { scope: "user" };
|
|
147
|
-
const runId = activeLeaderRunId(store, taskId);
|
|
148
|
-
if (runId === undefined) {
|
|
149
|
-
throw usageError("job.start/job.cancel user scope requires an active in-flight Task Leader: "
|
|
150
|
-
+ `${taskId}.`);
|
|
151
|
-
}
|
|
152
|
-
return {
|
|
153
|
-
scope: "user",
|
|
154
|
-
leaderAssertion: {
|
|
155
|
-
runId,
|
|
156
|
-
receiptId: formatAgentRunReceiptId(taskId, runId)
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* rr12: Resolve the current in-flight Task Leader Run for a non-managed
|
|
162
|
-
* (operator/bare-shell) caller. Unlike `taskLeaderActionRunId`, this does not
|
|
163
|
-
* require managed Leader environment variables — it verifies the durable
|
|
164
|
-
* state directly: an active Leader Run whose Role Session is currently
|
|
165
|
-
* in-flight with the matching receipt. Returns undefined when no Leader is
|
|
166
|
-
* active or in flight.
|
|
167
|
-
*/
|
|
168
|
-
function activeLeaderRunId(store, taskId) {
|
|
169
|
-
const run = store.getActiveAgentRun(taskId, LEADER_ROLE);
|
|
170
|
-
if (run === null || run.status !== "active" || run.roleName !== LEADER_ROLE) {
|
|
171
|
-
return undefined;
|
|
172
|
-
}
|
|
173
|
-
const sessions = store.getTaskRoleSessionSet(taskId, LEADER_ROLE);
|
|
174
|
-
const expectedReceipt = agentRunDeliveryReceiptId(run);
|
|
175
|
-
if (sessions === null
|
|
176
|
-
|| sessions.inFlight === null
|
|
177
|
-
|| sessions.inFlight.runId !== run.id
|
|
178
|
-
|| sessions.inFlight.receiptId !== expectedReceipt) {
|
|
179
|
-
return undefined;
|
|
180
|
-
}
|
|
181
|
-
return run.id;
|
|
137
|
+
return { scope: "user" };
|
|
182
138
|
}
|
|
183
139
|
/**
|
|
184
140
|
* Resolve an exact current Task Leader Run for event attribution.
|