@zq-silk/yui 0.6.8 → 0.6.9
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/commands/taskCommands.js +1 -1
- package/dist/controller/fileSchedulerStoreAdapter.js +27 -4
- package/dist/runtime/runtimeObservation.js +22 -0
- package/dist/runtime/runtimeProjection.js +10 -4
- package/dist/scheduler/leaderWakeupProcessor.js +2 -1
- package/dist/task/nextAction.js +1 -1
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +27 -23
- package/skills/yui-operator/SKILL.md +7 -5
|
@@ -1802,7 +1802,7 @@ function dispatchWork(args, store, options) {
|
|
|
1802
1802
|
if (item.assignee === undefined) {
|
|
1803
1803
|
throw usageError(`Work Item has no Task Role assignee: ${item.id}. `
|
|
1804
1804
|
+ `The Task Leader must run "yui task work update ${item.id} running", `
|
|
1805
|
-
+ "then execute it directly or create
|
|
1805
|
+
+ "then execute it directly or create native subagents in the Leader Session.");
|
|
1806
1806
|
}
|
|
1807
1807
|
if (expanding && taskActor(options, task.id) !== "leader") {
|
|
1808
1808
|
throw usageError("Only the Task Leader may expand a running ExecutionGroup.");
|
|
@@ -26,7 +26,8 @@ import { enqueueWork } from "../coordination/workMailboxQueue.js";
|
|
|
26
26
|
import { RUNTIME_CLEANUP_REQUIRED_REASON, RUNTIME_LAUNCH_RESERVED_REASON, RUNTIME_LIFECYCLE_OWNER, hasRuntimeCleanupObligation, hasRuntimeLifecycleWork, isRuntimeLaunchReservation, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
|
|
27
27
|
import { nativeSessionIdForLaunch } from "../runtime/preallocatedNativeSession.js";
|
|
28
28
|
import { builtinAgentDriverRegistry } from "../runtime/builtinAgentDrivers.js";
|
|
29
|
-
import { RUNTIME_OBSERVATION_TASK_EVENT, createRuntimeObservation, runtimeObservationFenceMatches, runtimeObservationFromTaskEvent, runtimeObservationTaskEventPayload } from "../runtime/runtimeObservation.js";
|
|
29
|
+
import { RUNTIME_OBSERVATION_TASK_EVENT, createRuntimeObservation, runtimeObservationFenceMatches, runtimeObservationFromTaskEvent, runtimeObservationRunFenceMatches, runtimeObservationTaskEventPayload } from "../runtime/runtimeObservation.js";
|
|
30
|
+
import { projectRuntimeTaskEvents } from "../runtime/runtimeProjection.js";
|
|
30
31
|
/** Maps the authoritative FileTaskStore records to the scheduler's narrow port. */
|
|
31
32
|
export class FileSchedulerStoreAdapter {
|
|
32
33
|
store;
|
|
@@ -80,6 +81,13 @@ export class FileSchedulerStoreAdapter {
|
|
|
80
81
|
const classification = this.classifyRuntimeTurnCompleted(completed);
|
|
81
82
|
if (classification !== "apply")
|
|
82
83
|
return classification;
|
|
84
|
+
if (this.runtimeTurnHasActiveNativeSubagents(input)) {
|
|
85
|
+
// This is an intermediate provider Turn boundary, not the end of the
|
|
86
|
+
// durable Yui Run. The provider will deliver child completion
|
|
87
|
+
// notifications as later native Turns in the same Run generation.
|
|
88
|
+
outcome = this.validateCanonicalRunObservation(input, now);
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
83
91
|
const result = this.observeRuntimeTurnCompleted(completed, now);
|
|
84
92
|
outcome = result.disposition === "obsolete" ? "obsolete" : "applied";
|
|
85
93
|
break;
|
|
@@ -180,6 +188,14 @@ export class FileSchedulerStoreAdapter {
|
|
|
180
188
|
return "applied";
|
|
181
189
|
});
|
|
182
190
|
}
|
|
191
|
+
runtimeTurnHasActiveNativeSubagents(input) {
|
|
192
|
+
const taskId = input.fence.taskId;
|
|
193
|
+
const run = this.store.getAgentRun(taskId, input.fence.runId);
|
|
194
|
+
if (run === null)
|
|
195
|
+
return false;
|
|
196
|
+
const projection = projectRuntimeTaskEvents(input.fence, run.createdAt, this.store.listEvents(taskId));
|
|
197
|
+
return Object.values(projection.operations).some(({ kind }) => kind === "subagent");
|
|
198
|
+
}
|
|
183
199
|
validateCanonicalSessionObservation(input, now) {
|
|
184
200
|
return this.store.transaction((store) => {
|
|
185
201
|
const run = store.getAgentRun(input.fence.taskId, input.fence.runId);
|
|
@@ -1249,7 +1265,8 @@ export class FileSchedulerStoreAdapter {
|
|
|
1249
1265
|
});
|
|
1250
1266
|
}
|
|
1251
1267
|
/**
|
|
1252
|
-
* Fast hook path:
|
|
1268
|
+
* Fast hook path: validates the native Turn boundary before it is either
|
|
1269
|
+
* retained as an intermediate child wait or given a two-second workflow
|
|
1253
1270
|
* closure deadline. It never performs tmux, workspace, or Controller I/O.
|
|
1254
1271
|
*/
|
|
1255
1272
|
classifyRuntimeTurnCompleted(input) {
|
|
@@ -2808,8 +2825,13 @@ function runtimeObservationTelemetryEntry(input) {
|
|
|
2808
2825
|
function compactedRuntimeObservationIds(events, incoming) {
|
|
2809
2826
|
const existing = events.flatMap((event) => {
|
|
2810
2827
|
const observation = runtimeObservationFromTaskEvent(event);
|
|
2828
|
+
const matches = incoming.kind.startsWith("operation.")
|
|
2829
|
+
? observation !== null
|
|
2830
|
+
&& runtimeObservationRunFenceMatches(observation.fence, incoming.fence)
|
|
2831
|
+
: observation !== null
|
|
2832
|
+
&& runtimeObservationFenceMatches(observation.fence, incoming.fence);
|
|
2811
2833
|
return observation !== null
|
|
2812
|
-
&&
|
|
2834
|
+
&& matches
|
|
2813
2835
|
? [{ event, observation }]
|
|
2814
2836
|
: [];
|
|
2815
2837
|
});
|
|
@@ -2838,7 +2860,8 @@ function compactedRuntimeObservationIds(events, incoming) {
|
|
|
2838
2860
|
&& observation.payload.sourceId === incoming.payload.sourceId;
|
|
2839
2861
|
}
|
|
2840
2862
|
if (["turn.completed", "turn.failed", "turn.cancelled"].includes(incoming.kind)) {
|
|
2841
|
-
return observation.kind.startsWith("operation.")
|
|
2863
|
+
return (observation.kind.startsWith("operation.")
|
|
2864
|
+
&& observation.payload.operation !== "subagent")
|
|
2842
2865
|
|| observation.kind === "turn.waiting"
|
|
2843
2866
|
|| observation.kind === "turn.completed"
|
|
2844
2867
|
|| observation.kind === "turn.failed"
|
|
@@ -118,6 +118,28 @@ export function runtimeObservationFenceMatches(expected, actual) {
|
|
|
118
118
|
}
|
|
119
119
|
return true;
|
|
120
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Matches observations that belong to one durable Run/session generation.
|
|
123
|
+
* A provider may advance its native Turn while background subagents from an
|
|
124
|
+
* earlier Turn are still active, so nativeTurnId is intentionally excluded.
|
|
125
|
+
*/
|
|
126
|
+
export function runtimeObservationRunFenceMatches(expected, actual) {
|
|
127
|
+
for (const field of [
|
|
128
|
+
"taskId",
|
|
129
|
+
"roleName",
|
|
130
|
+
"runId",
|
|
131
|
+
"agentId",
|
|
132
|
+
"driverId",
|
|
133
|
+
"launchId",
|
|
134
|
+
"sessionGenerationId",
|
|
135
|
+
"nativeSessionId",
|
|
136
|
+
"receiptId"
|
|
137
|
+
]) {
|
|
138
|
+
if (expected[field] !== actual[field])
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
121
143
|
/** Compact TaskEvent payload used for durable state-boundary observations. */
|
|
122
144
|
export function runtimeObservationTaskEventPayload(input) {
|
|
123
145
|
const observation = createRuntimeObservation(input);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createRuntimeObservation,
|
|
1
|
+
import { createRuntimeObservation, runtimeObservationFromTaskEvent, runtimeObservationRunFenceMatches } from "./runtimeObservation.js";
|
|
2
2
|
export function createRuntimeProjection(fence, createdAt) {
|
|
3
3
|
const timestamp = requireTimestamp(createdAt);
|
|
4
4
|
return Object.freeze({
|
|
@@ -20,7 +20,7 @@ export function createRuntimeProjection(fence, createdAt) {
|
|
|
20
20
|
export function projectRuntimeTaskEvents(fence, createdAt, events) {
|
|
21
21
|
const observations = events
|
|
22
22
|
.map(runtimeObservationFromTaskEvent)
|
|
23
|
-
.filter((event) => (event !== null &&
|
|
23
|
+
.filter((event) => (event !== null && runtimeObservationRunFenceMatches(fence, event.fence)))
|
|
24
24
|
.sort((left, right) => (left.receivedAt.localeCompare(right.receivedAt)
|
|
25
25
|
|| (left.sequence ?? -1) - (right.sequence ?? -1)
|
|
26
26
|
|| (left.ordinal ?? -1) - (right.ordinal ?? -1)
|
|
@@ -29,7 +29,7 @@ export function projectRuntimeTaskEvents(fence, createdAt, events) {
|
|
|
29
29
|
}
|
|
30
30
|
export function projectRuntimeObservation(current, raw) {
|
|
31
31
|
const event = createRuntimeObservation(raw);
|
|
32
|
-
if (!
|
|
32
|
+
if (!runtimeObservationRunFenceMatches(current.fence, event.fence)) {
|
|
33
33
|
throw new Error("Runtime observation fence does not match the projection.");
|
|
34
34
|
}
|
|
35
35
|
const at = event.receivedAt;
|
|
@@ -79,7 +79,10 @@ export function projectRuntimeObservation(current, raw) {
|
|
|
79
79
|
turn: "completed",
|
|
80
80
|
waitingReason: undefined,
|
|
81
81
|
waitId: undefined,
|
|
82
|
-
operations
|
|
82
|
+
// Tool operations belong to the completed native Turn. Provider-owned
|
|
83
|
+
// background subagents may legitimately outlive it and wake later
|
|
84
|
+
// native Turns inside the same durable Yui Run.
|
|
85
|
+
operations: activeSubagentOperations(current.operations),
|
|
83
86
|
stateSince: at
|
|
84
87
|
}), "provider", at);
|
|
85
88
|
case "turn.failed":
|
|
@@ -261,6 +264,9 @@ function dominantOperation(operations) {
|
|
|
261
264
|
return "model";
|
|
262
265
|
return null;
|
|
263
266
|
}
|
|
267
|
+
function activeSubagentOperations(operations) {
|
|
268
|
+
return Object.freeze(Object.fromEntries(Object.entries(operations).filter(([, operation]) => operation.kind === "subagent")));
|
|
269
|
+
}
|
|
264
270
|
function terminalTurn(current, fallback = "cancelled") {
|
|
265
271
|
return current === "completed" || current === "failed" || current === "cancelled"
|
|
266
272
|
? current
|
|
@@ -410,7 +410,8 @@ function leaderWakeupInput(taskId, runId, reasons, projectBindings) {
|
|
|
410
410
|
: `Project Policy references: ${projectBindings.map((binding) => `${binding.directory} (${binding.projectId})`).join(", ")}. Read each with yui project show <project>, then yui project knowledge list <project> and yui project knowledge show <project> <knowledge>.`,
|
|
411
411
|
"Use narrower Task message, WorkItem, decision, milestone, and input commands only when a specific record needs closer inspection.",
|
|
412
412
|
`When the requested outcome is finished and there are no active Worker Runs or unresolved inputs, complete the Task with yui task complete ${taskId} --summary-file - and a quoted heredoc containing the final outcome and evidence.`,
|
|
413
|
-
|
|
413
|
+
"Provider-native subagents remain inside this Leader AgentRun. Yui observes their structured lifecycle and keeps the Run active across intermediate provider Turn boundaries while children remain active; let the provider deliver completion notifications and continue synthesis without polling or yielding merely for that native wait.",
|
|
414
|
+
`Before ending this turn, if the Task was not completed, no InputRequest terminalized this Run, and no provider-native child work remains active, release the active fence with yui task run yield ${runId} --summary-file - and a quoted heredoc containing the current result or waiting state. For managed Task Role or Reviewer results, checkpoint and yield before waiting so their durable mailbox can wake a later Leader Run. The yield command must be the final tool action: after it succeeds, stop immediately and do not inspect, poll, accept, or perform further work in the same native turn.`
|
|
414
415
|
];
|
|
415
416
|
return lines.join("\n");
|
|
416
417
|
}
|
package/dist/task/nextAction.js
CHANGED
|
@@ -244,7 +244,7 @@ export function projectNextAction(facts) {
|
|
|
244
244
|
},
|
|
245
245
|
{
|
|
246
246
|
kind: "native-subagent",
|
|
247
|
-
reason: "Use
|
|
247
|
+
reason: "Use native implementer subagents when bounded work benefits from specialist attention or parallel fan-out inside the Leader Session.",
|
|
248
248
|
refs
|
|
249
249
|
}
|
|
250
250
|
],
|
package/package.json
CHANGED
|
@@ -38,21 +38,24 @@ Choose the executor in this order:
|
|
|
38
38
|
|
|
39
39
|
1. **Leader directly** when the work is small and the current Leader context,
|
|
40
40
|
authority, and tools are sufficient.
|
|
41
|
-
2. **
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
Session and durable Run lifecycle.
|
|
41
|
+
2. **Native subagents** when bounded implementation or research benefits from
|
|
42
|
+
specialist attention or parallel fan-out inside the current Agent Session.
|
|
43
|
+
Give each child an explicit Profile, workspace access, and result contract.
|
|
44
|
+
3. **Task Role AgentRun** when the work genuinely needs independent durable
|
|
45
|
+
ownership, different credentials or authority, a provider/model capability
|
|
46
|
+
unavailable to the current Agent, or an independently managed Session and
|
|
47
|
+
Run lifecycle.
|
|
49
48
|
|
|
50
49
|
Do not dispatch a Task Role merely to obtain a fresh context, run a command,
|
|
51
50
|
perform a routine small edit, or add an intermediate review. Direct and native
|
|
52
51
|
execution add no Worker Role, Worker Yui Session, or Worker AgentRun. The exact
|
|
53
|
-
Leader Run fence stays active
|
|
52
|
+
Leader Run fence stays active while native child work is outstanding; the
|
|
54
53
|
WorkItem and its workspace remain the durable delivery boundary.
|
|
55
54
|
|
|
55
|
+
Provider-native foreground and background child lifecycle stays owned by the
|
|
56
|
+
current Agent Session. Structured child completion notifications may resume the
|
|
57
|
+
Leader in later provider Turns while the same Yui AgentRun remains active.
|
|
58
|
+
|
|
56
59
|
A Project-backed code result still uses one WorkItem-owned Develop workspace
|
|
57
60
|
and a clean committed Candidate. The fast path compresses orchestration, not
|
|
58
61
|
delivery evidence: Candidate, ChangeSet, committed Integration, acceptance,
|
|
@@ -84,11 +87,11 @@ WorkItem while its delivery scope remains open. If an immutable final-review
|
|
|
84
87
|
boundary makes that impossible, create only the smallest repair WorkItem and
|
|
85
88
|
retain the original Candidate, Review, and Integration evidence.
|
|
86
89
|
|
|
87
|
-
Native and managed waits use different fences. For
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
Do not poll, send a waiting
|
|
91
|
-
|
|
90
|
+
Native and managed waits use different fences. For native children, let the
|
|
91
|
+
provider deliver structured completion notifications and continue the parent
|
|
92
|
+
Agent. The exact Leader Run stays active across intermediate provider Turn
|
|
93
|
+
boundaries while native children remain active. Do not poll, send a waiting
|
|
94
|
+
Message, rewrite a checkpoint, or yield merely to preserve that native wait.
|
|
92
95
|
|
|
93
96
|
For a managed Task Role or Reviewer Run, persist a necessary changed checkpoint,
|
|
94
97
|
yield the active Leader Run, and stop the turn. Its durable mailbox result or an
|
|
@@ -344,12 +347,12 @@ hint only if this Agent's native child API supports that override; otherwise
|
|
|
344
347
|
inherit the actual runtime setting. Never claim a model that cannot be
|
|
345
348
|
confirmed.
|
|
346
349
|
|
|
347
|
-
Create and communicate with
|
|
348
|
-
not create, address, resume, or terminate
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
350
|
+
Create and communicate with children through the native Agent tools. Yui does
|
|
351
|
+
not create, address, resume, or terminate those children; it observes their
|
|
352
|
+
structured lifecycle so the parent AgentRun can span the provider Turns needed
|
|
353
|
+
to receive their results. Children must not mutate Yui lifecycle state. Let the
|
|
354
|
+
provider's native completion mechanism return results to the Leader, then
|
|
355
|
+
synthesize them before deciding the next Yui workflow outcome.
|
|
353
356
|
|
|
354
357
|
Review the returned work and run proportionate checks. Record each round in the
|
|
355
358
|
WorkItem summary; preserve earlier round facts when updating it:
|
|
@@ -617,9 +620,10 @@ evidence; truthfully surface the blocker through the supported provider failure
|
|
|
617
620
|
boundary. Do not add a fallback protocol.
|
|
618
621
|
|
|
619
622
|
Yield before waiting only for managed Task Role or Reviewer results whose
|
|
620
|
-
durable mailbox can wake the Task. Do not yield
|
|
621
|
-
|
|
622
|
-
|
|
623
|
+
durable mailbox can wake the Task. Do not yield merely because native child
|
|
624
|
+
results are outstanding: the provider owns their completion notifications, and
|
|
625
|
+
Yui keeps this AgentRun active across intermediate provider Turns. After native
|
|
626
|
+
work drains, continue to Task completion, InputRequest, or final yield.
|
|
623
627
|
|
|
624
628
|
Complete only after required WorkItems are accepted, Role work is terminal,
|
|
625
629
|
latest isolated results are integrated or deliberately abandoned, and user
|
|
@@ -162,11 +162,13 @@ Do not pre-split WorkItems or decide their dependsOn, execution path,
|
|
|
162
162
|
acceptance, or Integration; the Leader owns WorkItem creation, replacement,
|
|
163
163
|
parallel dispatch, dependency and conflict resolution inside the one Task.
|
|
164
164
|
|
|
165
|
-
The Leader chooses among direct execution,
|
|
166
|
-
AgentRun.
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
165
|
+
The Leader chooses among direct execution, native subagents, and a Task Role
|
|
166
|
+
AgentRun. Native subagents are created inside the Leader Session, inherit the
|
|
167
|
+
Leader Agent, ignore Task Role Agent bindings, and have no Yui launch command.
|
|
168
|
+
Their structured lifecycle and completion notifications may span provider
|
|
169
|
+
Turns inside the same Yui AgentRun. A Task Role is required when the user
|
|
170
|
+
requests a different provider, credentials, interactive Session, or durable
|
|
171
|
+
independent lifecycle.
|
|
170
172
|
|
|
171
173
|
When the user requires a specific Leader or Worker provider, inspect Roles
|
|
172
174
|
before routing:
|