@zq-silk/yui 0.6.8 → 0.6.10

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 (58) hide show
  1. package/README.md +10 -3
  2. package/dist/cli/commandCatalog.js +1 -1
  3. package/dist/commands/taskCommands.js +63 -28
  4. package/dist/commands/taskContextCommand.js +27 -1
  5. package/dist/commands/taskRoleRuntimeStatus.js +17 -6
  6. package/dist/controller/agentRuntimeObserver.js +6 -3
  7. package/dist/controller/controller.js +29 -47
  8. package/dist/controller/fileSchedulerStoreAdapter.js +572 -258
  9. package/dist/controller/runtime.js +8 -1
  10. package/dist/controller/runtimeEventInbox.js +16 -5
  11. package/dist/controller/runtimeHookRunFence.js +51 -5
  12. package/dist/controller/runtimeObservationHook.js +8 -2
  13. package/dist/coordination/workMailbox.js +408 -28
  14. package/dist/coordination/workMailboxQueue.js +12 -10
  15. package/dist/executor/agentExecutor.js +101 -94
  16. package/dist/executor/executorRegistry.js +47 -2
  17. package/dist/executor/fileRoleLaunchPlanner.js +4 -2
  18. package/dist/lifecycle/exactRunTerminalization.js +1 -7
  19. package/dist/repository/taskWorkspaceCoordinator.js +9 -4
  20. package/dist/runtime/agentDriver.js +83 -4
  21. package/dist/runtime/agentDriverObservation.js +25 -10
  22. package/dist/runtime/builtinAgentDrivers.js +168 -18
  23. package/dist/runtime/codexAppServerRuntime.js +355 -0
  24. package/dist/runtime/continuationManager.js +117 -0
  25. package/dist/runtime/index.js +2 -0
  26. package/dist/runtime/lifecycleReservation.js +4 -3
  27. package/dist/runtime/promptEnvelope.js +14 -3
  28. package/dist/runtime/providerContinuation.js +225 -0
  29. package/dist/runtime/providerContinuationReconciliationService.js +172 -0
  30. package/dist/runtime/providerRuntimeIdentity.js +232 -0
  31. package/dist/runtime/providerRuntimeReconciler.js +166 -0
  32. package/dist/runtime/runtimeContinuationProjection.js +34 -0
  33. package/dist/runtime/runtimeObservation.js +217 -6
  34. package/dist/runtime/runtimeProjection.js +172 -11
  35. package/dist/scheduler/activeRoleRunDelivery.js +314 -1
  36. package/dist/scheduler/leaderWakeupProcessor.js +2 -1
  37. package/dist/scheduler/operatorInputNotificationProcessor.js +3 -2
  38. package/dist/scheduler/roleRunLiveness.js +8 -7
  39. package/dist/scheduler/roleRunStall.js +4 -2
  40. package/dist/scheduler/taskExecutionProjection.js +2 -2
  41. package/dist/storage/migration/productionRegistry.js +474 -1
  42. package/dist/storage/sqliteSchema.js +102 -21
  43. package/dist/storage/sqliteStore.js +52 -110
  44. package/dist/storage/storageVersions.js +1 -1
  45. package/dist/storage/storeRpc.js +0 -1
  46. package/dist/storage/taskStore.js +40 -53
  47. package/dist/storage/upgrade/sqliteStateMigration.js +0 -21
  48. package/dist/task/nextAction.js +1 -1
  49. package/dist/web/assets/client/app.js +1 -1
  50. package/dist/web/assets/client/components.js +233 -4
  51. package/dist/web/assets/client/i18n.js +166 -2
  52. package/dist/web/assets/client/view.js +30 -13
  53. package/dist/web/assets/styles/cards.js +62 -0
  54. package/dist/web/assets/styles/widgets.js +1 -0
  55. package/dist/web/webSnapshot.js +11 -2
  56. package/package.json +1 -1
  57. package/skills/yui-leader/SKILL.md +33 -24
  58. package/skills/yui-operator/SKILL.md +7 -5
@@ -13,6 +13,7 @@ import { isProjectMaintenanceFenced } from "../repository/projectMaintenanceLock
13
13
  import { KeyedWorkQueue } from "../coordination/keyedWorkQueue.js";
14
14
  import { MailboxScheduler } from "../coordination/mailboxScheduler.js";
15
15
  import { nearestDeadlineBatch } from "../coordination/deadlineScheduler.js";
16
+ import { mailboxHasWork, nextPendingBatch } from "../coordination/workMailbox.js";
16
17
  import { hasRuntimeCleanupObligation, isRuntimeLaunchReservation, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
17
18
  import { formatTaskRecordReference } from "../task/taskRecordReference.js";
18
19
  import { parseDurableJobAcknowledgeParams, parseDurableJobCancelParams, parseDurableJobRefParams, parseDurableJobStartParams } from "./jobControl.js";
@@ -43,7 +44,7 @@ const ZERO_DRAIN_METRICS = Object.freeze({
43
44
  * Runs one lean scheduler pass. Due native Turn completions are folded before
44
45
  * liveness, so a valid Hook boundary fences destructive process reconciliation.
45
46
  */
46
- export async function runControllerSchedulerPass(store, delivery, now, workspacePreparer, scope = { kind: "full" }, includeOperator = true, runtimeCleanupOutcomes = [], lifecycleHost, stallWindowMs = DEFAULT_STALL_WINDOW_MS, maintenanceFence, onMaintenanceFenceDefer, blockedTaskIds = new Set()) {
47
+ export async function runControllerSchedulerPass(store, delivery, now, workspacePreparer, scope = { kind: "full" }, includeOperator = true, runtimeCleanupOutcomes = [], lifecycleHost, stallWindowMs = DEFAULT_STALL_WINDOW_MS, maintenanceFence, onMaintenanceFenceDefer, blockedTaskIds = new Set(), inputDeliveryRecoveryCutoff) {
47
48
  const compiledSelection = compileReconcileSelection(scope);
48
49
  const selection = includeOperator
49
50
  ? { ...compiledSelection, blockedTaskIds }
@@ -75,12 +76,11 @@ export async function runControllerSchedulerPass(store, delivery, now, workspace
75
76
  // Sessions before delivery, so the existing delivery path re-pushes the
76
77
  // exact same input in this same pass.
77
78
  resolveDueProviderRetries(store, roleSelection, now);
78
- const activeRunDeliveries = await processActiveRoleRunDeliveries(store, delivery, now, roleSelection);
79
+ const activeRunDeliveries = await processActiveRoleRunDeliveries(store, delivery, now, roleSelection, inputDeliveryRecoveryCutoff);
79
80
  await controlEventLoopTurn();
80
81
  const unsettledRunRefs = new Set(activeRunDeliveries.flatMap((result) => (result.reason === "delivery-uncertain" || result.terminalFailure !== undefined
81
82
  ? [formatTaskRecordReference(result.taskId, result.runId, "agentRun")]
82
83
  : [])));
83
- resolveDueRuntimeTurnCompletions(store, delivery, selection, now);
84
84
  // Every successful Task has completed the targeted phases owned by its
85
85
  // mailbox at this boundary. Settle that exact claim before advisory
86
86
  // cross-Task reconciliation so an unrelated failure cannot retain and
@@ -412,29 +412,6 @@ function runtimeOwnerIdentity(owner) {
412
412
  ? `task\0${owner.taskId}\0${owner.roleName}`
413
413
  : `global\0${owner.roleName}`;
414
414
  }
415
- function resolveDueRuntimeTurnCompletions(store, delivery, selection, now) {
416
- if (typeof store.resolveDueRuntimeTurnCompletions !== "function")
417
- return;
418
- const selectedTaskIds = selectedTaskIdsForBoundedPass(store, selection);
419
- if (selectedTaskIds?.size === 0)
420
- return;
421
- const candidateTaskIds = selectedTaskIds === undefined
422
- ? undefined
423
- : [...selectedTaskIds].sort((left, right) => (left.localeCompare(right, undefined, { numeric: true })));
424
- const candidates = store.listPendingRuntimeTurnCompletions(candidateTaskIds);
425
- const finalized = new Set(store.resolveDueRuntimeTurnCompletions(now, selectedTaskIds));
426
- if (finalized.size === 0)
427
- return;
428
- for (const completion of candidates) {
429
- if (!finalized.has(formatTaskRecordReference(completion.taskId, completion.runId, "agentRun")))
430
- continue;
431
- delivery.forgetPrepared?.({
432
- taskId: completion.taskId,
433
- roleName: completion.roleName,
434
- runId: completion.runId
435
- });
436
- }
437
- }
438
415
  /**
439
416
  * Issue 04: reopens due in-place Provider retries on their original Native
440
417
  * Sessions before the active-run delivery pass, so the existing delivery
@@ -540,7 +517,7 @@ function runtimeCleanupMailboxKey(target) {
540
517
  function runtimeLifecycleBatchIdentity(target, mailbox) {
541
518
  if (mailbox.processing !== null)
542
519
  return mailbox.processing.batchId;
543
- const pending = mailbox.pending;
520
+ const pending = nextPendingBatch(mailbox);
544
521
  return pending === null
545
522
  ? runtimeTargetIdentity(target)
546
523
  : `${runtimeTargetIdentity(target)}:${pending.fromSequence}-${pending.toSequence}`;
@@ -607,10 +584,11 @@ function claimSelectedTaskMailboxes(store, selection, now) {
607
584
  const claims = [];
608
585
  for (const target of targets) {
609
586
  const mailbox = store.getWorkMailbox(target);
610
- if (mailbox === null || (mailbox.processing === null && mailbox.pending === null))
587
+ if (mailbox === null || !mailboxHasWork(mailbox))
611
588
  continue;
589
+ const pending = nextPendingBatch(mailbox);
612
590
  const batchId = mailbox.processing?.batchId
613
- ?? `task:${encodeURIComponent(target.taskId)}:${mailbox.pending.fromSequence}-${mailbox.pending.toSequence}`;
591
+ ?? `task:${encodeURIComponent(target.taskId)}:${pending.fromSequence}-${pending.toSequence}`;
614
592
  const claim = store.claimWorkMailbox({
615
593
  target,
616
594
  batchId,
@@ -809,6 +787,7 @@ function mergeControllerSchedulerResults(results) {
809
787
  export class FileTaskController {
810
788
  store;
811
789
  delivery;
790
+ #startedAt;
812
791
  #intervalMs;
813
792
  #now;
814
793
  #onError;
@@ -841,6 +820,7 @@ export class FileTaskController {
841
820
  #maintenanceFence;
842
821
  #onMaintenanceFenceDefer;
843
822
  #jobSupervisor;
823
+ #continuationReconciler;
844
824
  #current;
845
825
  #operatorCurrent;
846
826
  #runtimeObserverCurrent;
@@ -860,6 +840,7 @@ export class FileTaskController {
860
840
  this.delivery = delivery;
861
841
  this.#intervalMs = positiveInteger(options.intervalMs, DEFAULT_RECONCILIATION_INTERVAL_MS, "Controller reconciliation interval");
862
842
  this.#now = options.now ?? (() => new Date());
843
+ this.#startedAt = this.#now();
863
844
  this.#onError = options.onError ?? (() => { });
864
845
  this.#workspacePreparer = options.workspacePreparer;
865
846
  this.#deliveryRetryMs = positiveInteger(options.deliveryRetryMs, DEFAULT_DELIVERY_RETRY_MS, "Controller delivery retry delay");
@@ -878,6 +859,7 @@ export class FileTaskController {
878
859
  this.#maintenanceFence = options.maintenanceFence;
879
860
  this.#onMaintenanceFenceDefer = options.onMaintenanceFenceDefer;
880
861
  this.#jobSupervisor = options.jobSupervisor;
862
+ this.#continuationReconciler = options.continuationReconciler;
881
863
  this.#signalScheduler = new MailboxScheduler(async (keys) => { await this.#requestPass({ kind: "dirty", keys }); }, {
882
864
  windowMs: options.signalWindowMs ?? DEFAULT_SIGNAL_WINDOW_MS,
883
865
  onError: this.#onError,
@@ -1045,7 +1027,7 @@ export class FileTaskController {
1045
1027
  return;
1046
1028
  const mailbox = this.store.getWorkMailbox({ kind: "operator" });
1047
1029
  const shouldArm = mailbox !== null
1048
- && (mailbox.pending !== null || mailbox.processing !== null);
1030
+ && mailboxHasWork(mailbox);
1049
1031
  if (shouldArm)
1050
1032
  this.#clearDeliveryRetry("operator");
1051
1033
  this.#operatorStartupRetryArmed = shouldArm;
@@ -1116,12 +1098,18 @@ export class FileTaskController {
1116
1098
  for (const taskId of runtimeTaskFailureIds(firstRuntimeDrain)) {
1117
1099
  runtimeFailedTaskIds.add(taskId);
1118
1100
  }
1101
+ // Detached reconciliation is deliberately confined to the low-rate
1102
+ // full pass. It queries only identities already present in Task
1103
+ // facts and cannot start a model Turn or scan unknown children.
1104
+ if (scope.kind === "full" && this.#continuationReconciler !== undefined) {
1105
+ await this.#continuationReconciler.reconcile(this.#now());
1106
+ }
1119
1107
  // DurableJob reconciliation runs before the scheduler pass so a
1120
1108
  // terminal job's Leader wakeup is enqueued in the same pass that
1121
1109
  // processes Leader wakeups.
1122
1110
  this.#jobSupervisor?.reconcile(this.#now());
1123
1111
  if (scope.kind === "full") {
1124
- result = await runControllerSchedulerPass(this.store, this.delivery, this.#now(), this.#workspacePreparer, scope, false, runtimeCleanupOutcomes, this.#lifecycleHost, this.#stallWindowMs, this.#maintenanceFence, this.#onMaintenanceFenceDefer, runtimeFailedTaskIds);
1112
+ result = await runControllerSchedulerPass(this.store, this.delivery, this.#now(), this.#workspacePreparer, scope, false, runtimeCleanupOutcomes, this.#lifecycleHost, this.#stallWindowMs, this.#maintenanceFence, this.#onMaintenanceFenceDefer, runtimeFailedTaskIds, this.#startedAt);
1125
1113
  }
1126
1114
  else {
1127
1115
  const dirtyPass = await this.#runDirtySchedulerPass(scope, runtimeCleanupOutcomes, runtimeFailedTaskIds);
@@ -1219,7 +1207,7 @@ export class FileTaskController {
1219
1207
  const taskScopes = partition.taskScopes.filter((taskScope) => (!blockedTaskIds.has(taskScope.taskId)));
1220
1208
  const orderedResults = [];
1221
1209
  if (partition.globalKeys.length > 0) {
1222
- orderedResults.push(await runControllerSchedulerPass(this.store, this.delivery, this.#now(), this.#workspacePreparer, { kind: "dirty", keys: partition.globalKeys }, false, runtimeCleanupOutcomes, this.#lifecycleHost, this.#stallWindowMs, this.#maintenanceFence, this.#onMaintenanceFenceDefer, blockedTaskIds));
1210
+ orderedResults.push(await runControllerSchedulerPass(this.store, this.delivery, this.#now(), this.#workspacePreparer, { kind: "dirty", keys: partition.globalKeys }, false, runtimeCleanupOutcomes, this.#lifecycleHost, this.#stallWindowMs, this.#maintenanceFence, this.#onMaintenanceFenceDefer, blockedTaskIds, this.#startedAt));
1223
1211
  }
1224
1212
  if (taskScopes.length === 0
1225
1213
  || this.#stopped
@@ -1252,7 +1240,7 @@ export class FileTaskController {
1252
1240
  if (this.#stopped || this.#pendingFull)
1253
1241
  continue;
1254
1242
  try {
1255
- taskResults[selected.index] = await runControllerSchedulerPass(this.store, this.delivery, this.#now(), this.#workspacePreparer, { kind: "dirty", keys: selected.taskScope.keys }, false, taskCleanupOutcomes[selected.index], this.#lifecycleHost, this.#stallWindowMs, this.#maintenanceFence, this.#onMaintenanceFenceDefer, blockedTaskIds);
1243
+ taskResults[selected.index] = await runControllerSchedulerPass(this.store, this.delivery, this.#now(), this.#workspacePreparer, { kind: "dirty", keys: selected.taskScope.keys }, false, taskCleanupOutcomes[selected.index], this.#lifecycleHost, this.#stallWindowMs, this.#maintenanceFence, this.#onMaintenanceFenceDefer, blockedTaskIds, this.#startedAt);
1256
1244
  this.#clearTaskPassRetry(selected.taskScope.taskId);
1257
1245
  }
1258
1246
  catch (error) {
@@ -1300,7 +1288,7 @@ export class FileTaskController {
1300
1288
  if (this.#operatorStartupRetryArmed && result.inputNotifications.length === 0) {
1301
1289
  const mailbox = this.store.getWorkMailbox({ kind: "operator" });
1302
1290
  if (mailbox === null
1303
- || (mailbox.pending === null && mailbox.processing === null)) {
1291
+ || !mailboxHasWork(mailbox)) {
1304
1292
  this.#operatorStartupRetryArmed = false;
1305
1293
  }
1306
1294
  }
@@ -1443,12 +1431,6 @@ export class FileTaskController {
1443
1431
  at: Date.parse(request.policy.timeoutAt)
1444
1432
  }]
1445
1433
  : []),
1446
- ...(typeof this.store.listPendingRuntimeTurnCompletions === "function"
1447
- ? this.store.listPendingRuntimeTurnCompletions()
1448
- : []).map((completion) => ({
1449
- key: `role:${encodeURIComponent(completion.taskId)}/${encodeURIComponent(completion.roleName)}`,
1450
- at: Date.parse(completion.dueAt)
1451
- })),
1452
1434
  // Issue 04 durable in-place retry timer: arm the Controller wake at the
1453
1435
  // earliest `nextAttemptAt` so a due retry re-pushes on its original
1454
1436
  // Session. The projection is durable, so a restart resumes the lineage.
@@ -1491,8 +1473,7 @@ export class FileTaskController {
1491
1473
  else if (delivery.reason === "writer-attached")
1492
1474
  writerBlocked.add(key);
1493
1475
  else if (delivery.reason === "not-ready"
1494
- || delivery.reason === "runtime-unavailable"
1495
- || delivery.reason === "delivery-uncertain") {
1476
+ || delivery.reason === "runtime-unavailable") {
1496
1477
  retry.set(key, {
1497
1478
  identity: delivery.runId,
1498
1479
  ...(delivery.terminalFailure === undefined
@@ -1508,8 +1489,7 @@ export class FileTaskController {
1508
1489
  if (wakeup.reason === "writer-attached") {
1509
1490
  writerBlocked.add(key);
1510
1491
  }
1511
- else if (wakeup.reason === "not-ready"
1512
- || wakeup.reason === "delivery-uncertain") {
1492
+ else if (wakeup.reason === "not-ready") {
1513
1493
  retry.set(key, { identity: wakeup.runId ?? key });
1514
1494
  }
1515
1495
  else if (wakeup.status === "dispatched")
@@ -1569,7 +1549,8 @@ export class FileTaskController {
1569
1549
  const mailbox = this.store.getWorkMailbox(target);
1570
1550
  // A newly queued pending batch must not reset the retry identity/bound
1571
1551
  // while this Controller still owns an older processing batch.
1572
- const batch = mailbox?.processing?.batch ?? mailbox?.pending;
1552
+ const batch = mailbox?.processing?.batch
1553
+ ?? (mailbox === null || mailbox === undefined ? null : nextPendingBatch(mailbox));
1573
1554
  if (batch === undefined || batch === null) {
1574
1555
  this.#clearDeliveryRetry(key);
1575
1556
  continue;
@@ -1657,7 +1638,7 @@ export class FileTaskController {
1657
1638
  return;
1658
1639
  const mailbox = this.store.getWorkMailbox({ kind: "operator" });
1659
1640
  if (mailbox === null
1660
- || (mailbox.pending === null && mailbox.processing === null)) {
1641
+ || !mailboxHasWork(mailbox)) {
1661
1642
  this.#operatorStartupRetryArmed = false;
1662
1643
  this.#clearDeliveryRetry("operator");
1663
1644
  }
@@ -1886,7 +1867,8 @@ function boundedPositiveInteger(value, fallback, maximum, label) {
1886
1867
  return resolved;
1887
1868
  }
1888
1869
  function operatorMailboxBatchIdentity(mailbox) {
1889
- const batch = mailbox?.pending ?? mailbox?.processing?.batch;
1870
+ const batch = mailbox?.processing?.batch
1871
+ ?? (mailbox === null || mailbox === undefined ? null : nextPendingBatch(mailbox));
1890
1872
  if (batch === null || batch === undefined)
1891
1873
  return null;
1892
1874
  return [