@zq-silk/yui 0.7.1 → 0.8.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 +27 -28
- package/README.md +79 -71
- package/dist/cli/commandCatalog.js +283 -136
- package/dist/cli/completion.js +3 -3
- package/dist/cli/helpRenderer.js +3 -0
- package/dist/cli/interactionPolicy.js +48 -33
- package/dist/cli/interactiveSelection.js +1 -1
- package/dist/cli/invocationRouter.js +3 -2
- package/dist/cli/roleWizard.js +8 -8
- package/dist/cli.js +189 -93
- package/dist/commands/agentCommands.js +5 -5
- package/dist/commands/configCommands.js +351 -104
- package/dist/commands/configOverview.js +60 -0
- package/dist/commands/deliveryGuardPreflight.js +2 -2
- package/dist/commands/globalRoleCommands.js +9 -9
- package/dist/commands/profileCommands.js +8 -8
- package/dist/commands/resourcesCommands.js +6 -5
- package/dist/commands/taskCommands.js +111 -59
- package/dist/commands/taskRoleRuntimeStatus.js +3 -1
- package/dist/commands/telemetryCommands.js +11 -6
- package/dist/config/configCatalog.js +42 -0
- package/dist/config/yuiConfig.js +80 -35
- package/dist/context/sessionBootstrapManifest.js +1 -1
- package/dist/controller/clientRuntime.js +0 -2
- package/dist/controller/controller.js +21 -9
- package/dist/controller/fileSchedulerStoreAdapter.js +409 -79
- package/dist/controller/resourceInventory.js +9 -5
- package/dist/controller/runtime.js +112 -25
- package/dist/controller/runtimeLaunchCoordinator.js +18 -78
- package/dist/controller/structuredProviderObservation.js +273 -0
- package/dist/doctor/doctor.js +2 -2
- package/dist/executor/agentAdapter.js +40 -0
- package/dist/executor/agentExecutor.js +31 -7
- package/dist/executor/executorRegistry.js +11 -49
- package/dist/executor/fileRoleLaunchPlanner.js +115 -37
- package/dist/lifecycle/canonicalLifecycleEvent.js +5 -2
- package/dist/resources/autoResourceGc.js +3 -1
- package/dist/review/reviewConfig.js +0 -2
- package/dist/run/agentRun.js +2 -2
- package/dist/run/providerRetry.js +29 -16
- package/dist/run/providerRetryConfig.js +5 -3
- package/dist/runtime/agentHost.js +767 -158
- package/dist/runtime/builtinAgentDrivers.js +1 -5
- package/dist/runtime/codexAppServerRuntime.js +67 -60
- package/dist/runtime/exactControlPlane.js +7 -2
- package/dist/runtime/index.js +6 -2
- package/dist/runtime/launchBroker.js +30 -8
- package/dist/runtime/launchDiagnostics.js +1 -1
- package/dist/runtime/providerAuthorityFence.js +24 -0
- package/dist/runtime/providerControl.js +63 -0
- package/dist/runtime/providerRecoveryDecision.js +55 -0
- package/dist/runtime/providerRuntimeIdentity.js +269 -19
- package/dist/runtime/runtimeBinding.js +20 -11
- package/dist/runtime/structuredProviderHost.js +476 -0
- package/dist/runtime/tmuxAdapters.js +143 -42
- package/dist/scheduler/activeRoleRunDelivery.js +206 -120
- package/dist/scheduler/leaderWakeupProcessor.js +141 -16
- package/dist/scheduler/roleRunStall.js +12 -9
- package/dist/setup/setupCommand.js +153 -492
- package/dist/storage/compatibleTaskStore.js +9 -5
- package/dist/storage/migration/productionRegistry.js +169 -0
- package/dist/storage/taskStore.js +22 -3
- package/dist/telemetry/sqliteTelemetryStore.js +9 -1
- package/dist/telemetry/telemetryConfig.js +1 -18
- package/dist/telemetry/telemetryStore.js +2 -2
- package/dist/telemetry/telemetryWiring.js +6 -5
- package/dist/tmux/tmuxManager.js +1 -1
- package/dist/web/webSnapshot.js +5 -3
- package/i18n/README.zh-CN.md +48 -40
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +12 -5
- package/skills/yui-operator/SKILL.md +44 -6
- package/skills/yui-runtime/SKILL.md +1 -1
|
@@ -67,6 +67,7 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
|
|
|
67
67
|
let effectiveSession = existingSession;
|
|
68
68
|
let claimed = false;
|
|
69
69
|
let deliveryAttempted = false;
|
|
70
|
+
let providerSubmissionBegun = false;
|
|
70
71
|
let run = null;
|
|
71
72
|
let prepared;
|
|
72
73
|
let preStartFencePersisted = false;
|
|
@@ -183,13 +184,14 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
|
|
|
183
184
|
preStartFencePersisted = true;
|
|
184
185
|
}
|
|
185
186
|
});
|
|
186
|
-
// A managed
|
|
187
|
-
//
|
|
187
|
+
// A managed Host may have submitted the exact Run Turn after its
|
|
188
|
+
// two-phase launch handshake. Once preparation returns that transport
|
|
188
189
|
// fact, any
|
|
189
190
|
// later readiness or aggregate-write failure is delivery uncertainty,
|
|
190
191
|
// not a launch failure: preserve the Run and its reservation for the
|
|
191
192
|
// matching provider Hook instead of terminalizing it.
|
|
192
|
-
deliveryAttempted = prepared.
|
|
193
|
+
deliveryAttempted = prepared.turnAcceptedDuringLaunch === true
|
|
194
|
+
|| prepared.turnDeliveryUnknownDuringLaunch === true;
|
|
193
195
|
// Persist the exact preparation fence before waiting on provider
|
|
194
196
|
// readiness. A pre-input lifecycle Hook may fire during that wait; it
|
|
195
197
|
// must be able to resolve this Run/Session/launch generation from durable
|
|
@@ -211,7 +213,8 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
|
|
|
211
213
|
}
|
|
212
214
|
const ready = await delivery.waitUntilReady(prepared);
|
|
213
215
|
deliveryAttempted = deliveryAttempted
|
|
214
|
-
|| ready.prepared.
|
|
216
|
+
|| ready.prepared.turnAcceptedDuringLaunch === true
|
|
217
|
+
|| ready.prepared.turnDeliveryUnknownDuringLaunch === true;
|
|
215
218
|
const latestTask = store.getTask(task.id);
|
|
216
219
|
if (latestTask === null || latestTask.status !== "active") {
|
|
217
220
|
delivery.forgetPrepared?.({
|
|
@@ -236,12 +239,54 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
|
|
|
236
239
|
: { launchId: ready.prepared.launchId }),
|
|
237
240
|
now
|
|
238
241
|
});
|
|
239
|
-
if (ready.prepared.
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
242
|
+
if (ready.prepared.turnRejectedDuringLaunch === true) {
|
|
243
|
+
delivery.forgetPrepared?.({
|
|
244
|
+
taskId: task.id,
|
|
245
|
+
roleName: role.name,
|
|
246
|
+
runId: run.id,
|
|
247
|
+
...(ready.prepared.launchId === undefined
|
|
248
|
+
? {}
|
|
249
|
+
: { launchId: ready.prepared.launchId })
|
|
250
|
+
});
|
|
251
|
+
results.push({
|
|
252
|
+
taskId: task.id,
|
|
253
|
+
runId: run.id,
|
|
254
|
+
status: "skipped",
|
|
255
|
+
reason: "not-ready"
|
|
256
|
+
});
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
if (ready.prepared.turnDeliveryUnknownDuringLaunch === true) {
|
|
260
|
+
store.saveRoleRunDelivery({
|
|
261
|
+
task,
|
|
262
|
+
role,
|
|
263
|
+
run,
|
|
264
|
+
session: effectiveSession,
|
|
265
|
+
...(ready.prepared.launchId === undefined
|
|
266
|
+
? {}
|
|
267
|
+
: { launchId: ready.prepared.launchId }),
|
|
268
|
+
now
|
|
269
|
+
});
|
|
270
|
+
delivery.forgetPrepared?.({
|
|
271
|
+
taskId: task.id,
|
|
272
|
+
roleName: role.name,
|
|
273
|
+
runId: run.id,
|
|
274
|
+
...(ready.prepared.launchId === undefined
|
|
275
|
+
? {}
|
|
276
|
+
: { launchId: ready.prepared.launchId })
|
|
277
|
+
});
|
|
278
|
+
results.push({
|
|
279
|
+
taskId: task.id,
|
|
280
|
+
runId: run.id,
|
|
281
|
+
status: "skipped",
|
|
282
|
+
reason: "delivery-uncertain"
|
|
283
|
+
});
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
if (ready.prepared.turnAcceptedDuringLaunch === true) {
|
|
287
|
+
// The Agent Host submitted the exact first Turn through structured
|
|
288
|
+
// Provider control. Persist the transport fence without a second write;
|
|
289
|
+
// the matching structured acknowledgement owns Run acceptance.
|
|
245
290
|
store.saveRoleRunDelivery({
|
|
246
291
|
task,
|
|
247
292
|
role,
|
|
@@ -263,13 +308,41 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
|
|
|
263
308
|
results.push({ taskId: task.id, runId: run.id, status: "dispatched" });
|
|
264
309
|
continue;
|
|
265
310
|
}
|
|
311
|
+
const receiptId = formatAgentRunReceiptId(task.id, run.id);
|
|
312
|
+
if (effectiveSession === null || effectiveSession.launchId === undefined
|
|
313
|
+
|| !hasNativeSession(effectiveSession)
|
|
314
|
+
|| store.beginRoleRunProviderTurn?.({
|
|
315
|
+
taskId: task.id,
|
|
316
|
+
roleName: role.name,
|
|
317
|
+
runId: run.id,
|
|
318
|
+
agentId: run.effective.agentId,
|
|
319
|
+
launchId: effectiveSession.launchId,
|
|
320
|
+
nativeSessionId: effectiveSession.nativeSessionId,
|
|
321
|
+
attemptId: receiptId,
|
|
322
|
+
now
|
|
323
|
+
}) !== true) {
|
|
324
|
+
throw new Error("Provider Turn intent could not be durably fenced before delivery.");
|
|
325
|
+
}
|
|
326
|
+
providerSubmissionBegun = true;
|
|
266
327
|
deliveryAttempted = true;
|
|
267
328
|
const outcome = await delivery.sendOnce({
|
|
268
329
|
delivery: ready,
|
|
269
|
-
receiptId
|
|
330
|
+
receiptId,
|
|
270
331
|
text: serializeRunBootstrapEnvelope(run.bootstrapEnvelope)
|
|
271
332
|
});
|
|
272
333
|
if (outcome === "busy" || outcome === "unavailable") {
|
|
334
|
+
store.resolveRoleRunProviderSubmission?.({
|
|
335
|
+
taskId: task.id,
|
|
336
|
+
roleName: role.name,
|
|
337
|
+
runId: run.id,
|
|
338
|
+
attemptId: receiptId,
|
|
339
|
+
status: "rejected",
|
|
340
|
+
reason: outcome === "busy"
|
|
341
|
+
? "Agent Host was busy before Provider mutation."
|
|
342
|
+
: "Agent Host was unavailable before Provider mutation.",
|
|
343
|
+
now
|
|
344
|
+
});
|
|
345
|
+
providerSubmissionBegun = false;
|
|
273
346
|
results.push({
|
|
274
347
|
taskId: task.id,
|
|
275
348
|
runId: run.id,
|
|
@@ -278,6 +351,49 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
|
|
|
278
351
|
});
|
|
279
352
|
continue;
|
|
280
353
|
}
|
|
354
|
+
if (outcome === "rejected") {
|
|
355
|
+
store.resolveRoleRunProviderSubmission?.({
|
|
356
|
+
taskId: task.id,
|
|
357
|
+
roleName: role.name,
|
|
358
|
+
runId: run.id,
|
|
359
|
+
attemptId: receiptId,
|
|
360
|
+
status: "rejected",
|
|
361
|
+
reason: "Provider returned an exact negative acknowledgement.",
|
|
362
|
+
now
|
|
363
|
+
});
|
|
364
|
+
providerSubmissionBegun = false;
|
|
365
|
+
results.push({ taskId: task.id, runId: run.id, status: "skipped", reason: "not-ready" });
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
if (outcome === "delivery-unknown") {
|
|
369
|
+
store.resolveRoleRunProviderSubmission?.({
|
|
370
|
+
taskId: task.id,
|
|
371
|
+
roleName: role.name,
|
|
372
|
+
runId: run.id,
|
|
373
|
+
attemptId: receiptId,
|
|
374
|
+
status: "delivery-unknown",
|
|
375
|
+
reason: "Provider Turn delivery is ambiguous; automatic retry is fenced.",
|
|
376
|
+
now
|
|
377
|
+
});
|
|
378
|
+
providerSubmissionBegun = false;
|
|
379
|
+
store.saveRoleRunDelivery({
|
|
380
|
+
task,
|
|
381
|
+
role,
|
|
382
|
+
run,
|
|
383
|
+
session: effectiveSession,
|
|
384
|
+
...(ready.prepared.launchId === undefined
|
|
385
|
+
? {}
|
|
386
|
+
: { launchId: ready.prepared.launchId }),
|
|
387
|
+
now
|
|
388
|
+
});
|
|
389
|
+
results.push({
|
|
390
|
+
taskId: task.id,
|
|
391
|
+
runId: run.id,
|
|
392
|
+
status: "skipped",
|
|
393
|
+
reason: "delivery-uncertain"
|
|
394
|
+
});
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
281
397
|
store.saveRoleRunDelivery({
|
|
282
398
|
task,
|
|
283
399
|
role,
|
|
@@ -293,6 +409,17 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
|
|
|
293
409
|
catch (error) {
|
|
294
410
|
const detail = error instanceof Error ? error.message : String(error);
|
|
295
411
|
const message = `Leader dispatch failed: ${detail}`;
|
|
412
|
+
if (providerSubmissionBegun && run !== null) {
|
|
413
|
+
store.resolveRoleRunProviderSubmission?.({
|
|
414
|
+
taskId: task.id,
|
|
415
|
+
roleName: role.name,
|
|
416
|
+
runId: run.id,
|
|
417
|
+
attemptId: formatAgentRunReceiptId(task.id, run.id),
|
|
418
|
+
status: "delivery-unknown",
|
|
419
|
+
reason: detail,
|
|
420
|
+
now
|
|
421
|
+
});
|
|
422
|
+
}
|
|
296
423
|
if (claimed && run !== null) {
|
|
297
424
|
// Scheduler single-flight backpressure: the Role runtime lifecycle
|
|
298
425
|
// lane was busy when the launch was reserved. The claimed Run stays
|
|
@@ -314,11 +441,9 @@ export async function processLeaderWakeups(store, delivery, now, selection) {
|
|
|
314
441
|
});
|
|
315
442
|
continue;
|
|
316
443
|
}
|
|
317
|
-
// A
|
|
318
|
-
//
|
|
319
|
-
//
|
|
320
|
-
// after the old host disappears. This is runtime backpressure, not a
|
|
321
|
-
// Leader failure and not grounds for allocating another Run.
|
|
444
|
+
// A managed Provider lifecycle operation may still be settling. Keep
|
|
445
|
+
// this newly-claimed Run as the sole mailbox owner; active-Run delivery
|
|
446
|
+
// retries after the lifecycle lane clears.
|
|
322
447
|
if (error instanceof RuntimeLaunchError && error.retryable) {
|
|
323
448
|
results.push({
|
|
324
449
|
taskId: task.id,
|
|
@@ -51,6 +51,8 @@ export function clearMatchingLeaderStallAttention(store, taskId, runId) {
|
|
|
51
51
|
*/
|
|
52
52
|
export function projectRoleRunHealth(input) {
|
|
53
53
|
const windowMs = input.windowMs ?? DEFAULT_STALL_WINDOW_MS;
|
|
54
|
+
const diagnosticAfterMs = input.diagnosticAfterMs
|
|
55
|
+
?? DEFAULT_WORKFLOW_STALL_CANDIDATE_AGE_MS;
|
|
54
56
|
if (!Number.isFinite(windowMs) || windowMs <= 0) {
|
|
55
57
|
throw new Error("Role run stall window must be a positive number of milliseconds.");
|
|
56
58
|
}
|
|
@@ -65,7 +67,7 @@ export function projectRoleRunHealth(input) {
|
|
|
65
67
|
const candidate = Number.isFinite(candidateAge)
|
|
66
68
|
&& (input.deliveredAt === undefined
|
|
67
69
|
? candidateAge >= windowMs
|
|
68
|
-
: candidateAge >=
|
|
70
|
+
: candidateAge >= diagnosticAfterMs);
|
|
69
71
|
const providerAcceptance = input.providerAcceptance
|
|
70
72
|
?? (input.deliveredAt === undefined ? "ambiguous" : "accepted");
|
|
71
73
|
const hostLiveness = input.hostLiveness;
|
|
@@ -84,7 +86,7 @@ export function projectRoleRunHealth(input) {
|
|
|
84
86
|
: "matching";
|
|
85
87
|
const resourceActivity = hostLiveness === "present"
|
|
86
88
|
&& nativeSession === "matching"
|
|
87
|
-
&& resourceEvidenceIsFresh(input.resource, input.now, windowMs)
|
|
89
|
+
&& resourceEvidenceIsFresh(input.resource, input.now, windowMs, diagnosticAfterMs)
|
|
88
90
|
// Residency/RSS and an unchanged cumulative counter are not progress.
|
|
89
91
|
&& input.resource?.active === true
|
|
90
92
|
&& input.resource?.changed === true;
|
|
@@ -494,7 +496,7 @@ export function isRoleRunStalled(events, runId) {
|
|
|
494
496
|
* structured waiting/working facts. No branch sends terminal bytes, retries,
|
|
495
497
|
* replaces a Session, or changes Run status.
|
|
496
498
|
*/
|
|
497
|
-
export async function reconcileStalledRoleRuns(store, delivery, now, selection, windowMs = DEFAULT_STALL_WINDOW_MS, liveStatuses, resourceEvidence) {
|
|
499
|
+
export async function reconcileStalledRoleRuns(store, delivery, now, selection, windowMs = DEFAULT_STALL_WINDOW_MS, liveStatuses, resourceEvidence, diagnosticAfterMs = DEFAULT_WORKFLOW_STALL_CANDIDATE_AGE_MS) {
|
|
498
500
|
// Dirty mailbox passes are intentionally not a second scheduler. Full
|
|
499
501
|
// reconcile owns the all-active-Run scan; dirty passes may still route the
|
|
500
502
|
// existing mailbox work without manufacturing another episode.
|
|
@@ -519,7 +521,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
|
|
|
519
521
|
session: store.getRoleSession(task.id, role.name, run.effective.agentId)
|
|
520
522
|
}];
|
|
521
523
|
})));
|
|
522
|
-
const stallCandidates = candidates.filter(({ run }) => isStallCandidate(run, now, windowMs));
|
|
524
|
+
const stallCandidates = candidates.filter(({ run }) => isStallCandidate(run, now, windowMs, diagnosticAfterMs));
|
|
523
525
|
if (stallCandidates.length === 0)
|
|
524
526
|
return [];
|
|
525
527
|
const stallCandidateKeys = new Set(stallCandidates.map(({ task, role }) => (`${task.id}\0${role.name}`)));
|
|
@@ -689,7 +691,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
|
|
|
689
691
|
&& sessionUsable
|
|
690
692
|
&& expectedResourceIdentity !== undefined
|
|
691
693
|
&& resourceEvidenceMatchesCurrentRun(resourceSnapshot, expectedResourceIdentity, progressAt)
|
|
692
|
-
&& resourceEvidenceIsFresh(resourceSnapshot, now, windowMs);
|
|
694
|
+
&& resourceEvidenceIsFresh(resourceSnapshot, now, windowMs, diagnosticAfterMs);
|
|
693
695
|
const resource = resourceIsCurrent ? resourceSnapshot : undefined;
|
|
694
696
|
const health = projectRoleRunHealth({
|
|
695
697
|
progressAt,
|
|
@@ -699,6 +701,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
|
|
|
699
701
|
: { deliveredAt: candidate.run.deliveredAt }),
|
|
700
702
|
now,
|
|
701
703
|
windowMs,
|
|
704
|
+
diagnosticAfterMs,
|
|
702
705
|
hostLiveness: live,
|
|
703
706
|
nativeSession: candidate.session,
|
|
704
707
|
providerAcceptance: candidate.run.deliveredAt === undefined
|
|
@@ -825,7 +828,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
|
|
|
825
828
|
}
|
|
826
829
|
return raised;
|
|
827
830
|
}
|
|
828
|
-
function isStallCandidate(run, now, windowMs) {
|
|
831
|
+
function isStallCandidate(run, now, windowMs, diagnosticAfterMs) {
|
|
829
832
|
const startAt = run.deliveredAt ?? run.createdAt;
|
|
830
833
|
const ageMs = now.getTime() - Date.parse(startAt);
|
|
831
834
|
if (!Number.isFinite(ageMs))
|
|
@@ -834,7 +837,7 @@ function isStallCandidate(run, now, windowMs) {
|
|
|
834
837
|
// window, but they never enter workflow-stall candidate filtering.
|
|
835
838
|
return run.deliveredAt === undefined
|
|
836
839
|
? ageMs >= windowMs
|
|
837
|
-
: ageMs >=
|
|
840
|
+
: ageMs >= diagnosticAfterMs;
|
|
838
841
|
}
|
|
839
842
|
function classifyLeaderStall(store, taskId, observed, now, windowMs) {
|
|
840
843
|
if (store.hasOpenInputRequest(taskId))
|
|
@@ -1020,7 +1023,7 @@ function resourceEvidenceMatchesCurrentRun(resource, expected, progressAt) {
|
|
|
1020
1023
|
function hasResourceIdentityText(value) {
|
|
1021
1024
|
return typeof value === "string" && value.trim().length > 0;
|
|
1022
1025
|
}
|
|
1023
|
-
function resourceEvidenceIsFresh(evidence, now, windowMs) {
|
|
1026
|
+
function resourceEvidenceIsFresh(evidence, now, windowMs, diagnosticAfterMs = DEFAULT_WORKFLOW_STALL_CANDIDATE_AGE_MS) {
|
|
1024
1027
|
if (evidence === undefined)
|
|
1025
1028
|
return false;
|
|
1026
1029
|
const observedAt = Date.parse(evidence.observedAt);
|
|
@@ -1028,5 +1031,5 @@ function resourceEvidenceIsFresh(evidence, now, windowMs) {
|
|
|
1028
1031
|
return false;
|
|
1029
1032
|
// A sample from an earlier scheduler window is not a current health signal.
|
|
1030
1033
|
return observedAt <= now.getTime()
|
|
1031
|
-
&& now.getTime() - observedAt < Math.max(windowMs,
|
|
1034
|
+
&& now.getTime() - observedAt < Math.max(windowMs, diagnosticAfterMs);
|
|
1032
1035
|
}
|