@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.
Files changed (73) hide show
  1. package/ARCHITECTURE.md +27 -28
  2. package/README.md +79 -71
  3. package/dist/cli/commandCatalog.js +283 -136
  4. package/dist/cli/completion.js +3 -3
  5. package/dist/cli/helpRenderer.js +3 -0
  6. package/dist/cli/interactionPolicy.js +48 -33
  7. package/dist/cli/interactiveSelection.js +1 -1
  8. package/dist/cli/invocationRouter.js +3 -2
  9. package/dist/cli/roleWizard.js +8 -8
  10. package/dist/cli.js +189 -93
  11. package/dist/commands/agentCommands.js +5 -5
  12. package/dist/commands/configCommands.js +351 -104
  13. package/dist/commands/configOverview.js +60 -0
  14. package/dist/commands/deliveryGuardPreflight.js +2 -2
  15. package/dist/commands/globalRoleCommands.js +9 -9
  16. package/dist/commands/profileCommands.js +8 -8
  17. package/dist/commands/resourcesCommands.js +6 -5
  18. package/dist/commands/taskCommands.js +111 -59
  19. package/dist/commands/taskRoleRuntimeStatus.js +3 -1
  20. package/dist/commands/telemetryCommands.js +11 -6
  21. package/dist/config/configCatalog.js +42 -0
  22. package/dist/config/yuiConfig.js +80 -35
  23. package/dist/context/sessionBootstrapManifest.js +1 -1
  24. package/dist/controller/clientRuntime.js +0 -2
  25. package/dist/controller/controller.js +21 -9
  26. package/dist/controller/fileSchedulerStoreAdapter.js +409 -79
  27. package/dist/controller/resourceInventory.js +9 -5
  28. package/dist/controller/runtime.js +112 -25
  29. package/dist/controller/runtimeLaunchCoordinator.js +18 -78
  30. package/dist/controller/structuredProviderObservation.js +273 -0
  31. package/dist/doctor/doctor.js +2 -2
  32. package/dist/executor/agentAdapter.js +40 -0
  33. package/dist/executor/agentExecutor.js +31 -7
  34. package/dist/executor/executorRegistry.js +11 -49
  35. package/dist/executor/fileRoleLaunchPlanner.js +115 -37
  36. package/dist/lifecycle/canonicalLifecycleEvent.js +5 -2
  37. package/dist/resources/autoResourceGc.js +3 -1
  38. package/dist/review/reviewConfig.js +0 -2
  39. package/dist/run/agentRun.js +2 -2
  40. package/dist/run/providerRetry.js +29 -16
  41. package/dist/run/providerRetryConfig.js +5 -3
  42. package/dist/runtime/agentHost.js +767 -158
  43. package/dist/runtime/builtinAgentDrivers.js +1 -5
  44. package/dist/runtime/codexAppServerRuntime.js +67 -60
  45. package/dist/runtime/exactControlPlane.js +7 -2
  46. package/dist/runtime/index.js +6 -2
  47. package/dist/runtime/launchBroker.js +30 -8
  48. package/dist/runtime/launchDiagnostics.js +1 -1
  49. package/dist/runtime/providerAuthorityFence.js +24 -0
  50. package/dist/runtime/providerControl.js +63 -0
  51. package/dist/runtime/providerRecoveryDecision.js +55 -0
  52. package/dist/runtime/providerRuntimeIdentity.js +269 -19
  53. package/dist/runtime/runtimeBinding.js +20 -11
  54. package/dist/runtime/structuredProviderHost.js +476 -0
  55. package/dist/runtime/tmuxAdapters.js +143 -42
  56. package/dist/scheduler/activeRoleRunDelivery.js +206 -120
  57. package/dist/scheduler/leaderWakeupProcessor.js +141 -16
  58. package/dist/scheduler/roleRunStall.js +12 -9
  59. package/dist/setup/setupCommand.js +153 -492
  60. package/dist/storage/compatibleTaskStore.js +9 -5
  61. package/dist/storage/migration/productionRegistry.js +169 -0
  62. package/dist/storage/taskStore.js +22 -3
  63. package/dist/telemetry/sqliteTelemetryStore.js +9 -1
  64. package/dist/telemetry/telemetryConfig.js +1 -18
  65. package/dist/telemetry/telemetryStore.js +2 -2
  66. package/dist/telemetry/telemetryWiring.js +6 -5
  67. package/dist/tmux/tmuxManager.js +1 -1
  68. package/dist/web/webSnapshot.js +5 -3
  69. package/i18n/README.zh-CN.md +48 -40
  70. package/package.json +1 -1
  71. package/skills/yui-leader/SKILL.md +12 -5
  72. package/skills/yui-operator/SKILL.md +44 -6
  73. 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 host may already have submitted the exact Run prompt as
187
- // part of process launch. Once preparation returns that transport
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.inputSubmittedAtLaunch === true;
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.inputSubmittedAtLaunch === true;
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.inputSubmittedAtLaunch === true) {
240
- // A fresh Codex command may carry the exact first prompt in its launch
241
- // argv. That is transport evidence only: the matching Provider Hook
242
- // still owns Run acceptance. Persist the push fence without writing a
243
- // second terminal prompt, then leave the reservation for the async
244
- // SessionStart/UserPromptSubmit fold.
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: formatAgentRunReceiptId(task.id, run.id),
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 finite managed provider from the preceding Run may still be
318
- // exiting after its durable yield. Keep this newly-claimed Run as the
319
- // sole owner of the Role mailbox; active-Run delivery will retry it
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 >= DEFAULT_WORKFLOW_STALL_CANDIDATE_AGE_MS);
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 >= DEFAULT_WORKFLOW_STALL_CANDIDATE_AGE_MS;
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, DEFAULT_WORKFLOW_STALL_CANDIDATE_AGE_MS);
1034
+ && now.getTime() - observedAt < Math.max(windowMs, diagnosticAfterMs);
1032
1035
  }