@zq-silk/yui 0.11.3 → 0.12.0

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 (86) hide show
  1. package/ARCHITECTURE.md +23 -4
  2. package/README.md +36 -13
  3. package/dist/cli/commandCatalog.js +2 -4
  4. package/dist/cli.js +105 -43
  5. package/dist/commands/configCommands.js +12 -45
  6. package/dist/commands/executionAuditCommands.js +1 -1
  7. package/dist/commands/jobCommands.js +4 -10
  8. package/dist/commands/taskCommands.js +154 -171
  9. package/dist/commands/taskCompletionGate.js +29 -54
  10. package/dist/commands/taskContextCommand.js +55 -11
  11. package/dist/commands/taskInputCommands.js +8 -5
  12. package/dist/commands/taskNextActionCommand.js +56 -6
  13. package/dist/commands/taskOverviewCommand.js +17 -31
  14. package/dist/commands/taskRoleRuntimeStatus.js +24 -0
  15. package/dist/commands/taskWorkspaceCommands.js +5 -2
  16. package/dist/config/configCatalog.js +2 -2
  17. package/dist/config/yuiConfig.js +3 -7
  18. package/dist/context/wakeNotification.js +20 -5
  19. package/dist/controller/agentRuntimeObserver.js +247 -51
  20. package/dist/controller/clientRuntime.js +1 -1
  21. package/dist/controller/controller.js +8 -12
  22. package/dist/controller/fileSchedulerStoreAdapter.js +146 -198
  23. package/dist/controller/runtime.js +3 -3
  24. package/dist/controller/runtimeEventInbox.js +17 -7
  25. package/dist/controller/runtimeEventProcessor.js +12 -19
  26. package/dist/controller/runtimeObservationHook.js +45 -0
  27. package/dist/execution/resourceBroker.js +5 -4
  28. package/dist/executor/agentExecutor.js +4 -4
  29. package/dist/executor/effectiveLaunch.js +8 -48
  30. package/dist/executor/fileRoleLaunchPlanner.js +36 -16
  31. package/dist/executor/workspacePreflightClassification.js +23 -2
  32. package/dist/interaction/operatorPresentation.js +33 -89
  33. package/dist/lifecycle/exactRunTerminalization.js +1 -2
  34. package/dist/lifecycle/taskRoleSessionReset.js +5 -13
  35. package/dist/observability/orchestrationMetrics.js +8 -3
  36. package/dist/profile/agentProfile.js +1 -1
  37. package/dist/release/cliHomeReleaseFence.js +123 -0
  38. package/dist/repository/taskWorkspacePreparer.js +176 -60
  39. package/dist/resources/sqliteResourceRegistry.js +1 -1
  40. package/dist/review/deltaRecheck.js +12 -51
  41. package/dist/review/reviewAcceptance.js +26 -0
  42. package/dist/review/reviewConfig.js +0 -31
  43. package/dist/review/reviewDecision.js +113 -0
  44. package/dist/review/reviewOutcomeClassifier.js +1 -1
  45. package/dist/review/reviewRound.js +1 -1
  46. package/dist/review/reviewerAvailability.js +69 -0
  47. package/dist/runtime/agentDriverObservation.js +24 -10
  48. package/dist/runtime/builtinAgentDrivers.js +4 -3
  49. package/dist/runtime/builtinTranscriptObserver.js +301 -64
  50. package/dist/runtime/builtinTranscriptUsage.js +9 -7
  51. package/dist/runtime/index.js +1 -0
  52. package/dist/runtime/runtimeObservation.js +13 -0
  53. package/dist/runtime/runtimeProjection.js +16 -25
  54. package/dist/runtime/sessionTokenMetrics.js +181 -0
  55. package/dist/runtime/structuredProviderHost.js +7 -1
  56. package/dist/scheduler/activeRoleRunDelivery.js +4 -4
  57. package/dist/scheduler/activeTaskProgress.js +7 -6
  58. package/dist/scheduler/leaderWakeupProcessor.js +3 -5
  59. package/dist/scheduler/operatorEvent.js +34 -0
  60. package/dist/scheduler/operatorInputNotificationProcessor.js +54 -94
  61. package/dist/scheduler/roleRunStall.js +0 -14
  62. package/dist/scheduler/taskExecutionProjection.js +97 -76
  63. package/dist/scheduler/taskObservabilityProjection.js +10 -11
  64. package/dist/storage/migration/productionRegistry.js +379 -0
  65. package/dist/storage/sqliteSchema.js +18 -8
  66. package/dist/storage/sqliteStore.js +24 -20
  67. package/dist/storage/storeRpc.js +0 -1
  68. package/dist/storage/taskStore.js +15 -30
  69. package/dist/storage/upgrade/recordVersions.js +3 -2
  70. package/dist/storage/upgrade/sqliteMigrationTarget.js +30 -8
  71. package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +23 -11
  72. package/dist/storage/upgrade/sqliteStateMigration.js +66 -8
  73. package/dist/task/completionReadiness.js +10 -6
  74. package/dist/task/nextAction.js +82 -24
  75. package/dist/telemetry/sqliteTelemetryStore.js +1 -1
  76. package/dist/web/assets/client/components.js +13 -3
  77. package/dist/web/assets/client/i18n.js +6 -2
  78. package/dist/web/webSnapshot.js +4 -0
  79. package/i18n/README.zh-CN.md +2 -2
  80. package/package.json +1 -1
  81. package/skills/yui-leader/SKILL.md +53 -16
  82. package/skills/yui-operator/SKILL.md +9 -0
  83. package/skills/yui-reviewer/SKILL.md +28 -7
  84. package/dist/context/sessionContextBudget.js +0 -71
  85. package/dist/lifecycle/contextBudgetRollover.js +0 -81
  86. package/dist/scheduler/operatorNotification.js +0 -59
@@ -30,20 +30,6 @@ const ACTIVITY_EVENT_TYPES = new Set([
30
30
  "integration.completed",
31
31
  "integration.failed"
32
32
  ]);
33
- /**
34
- * Clear only the Operator projection raised for this exact Leader Run. A
35
- * later Run (or a recovery notification of another kind) must remain intact;
36
- * callers use this helper from every supported recovery/terminalization path
37
- * so attention cleanup cannot drift between CLI and runtime code.
38
- */
39
- export function clearMatchingLeaderStallAttention(store, taskId, runId) {
40
- const notification = store.getOperatorNotification(taskId);
41
- if (notification?.type !== "leader-stalled" || notification.runId !== runId) {
42
- return false;
43
- }
44
- store.clearOperatorNotification(taskId);
45
- return true;
46
- }
47
33
  /**
48
34
  * One pure projection used by every Role. Resource activity is retained only
49
35
  * as exact-generation diagnostic evidence; it never changes the durable
@@ -5,6 +5,7 @@ import { actionableExecutionLaneRecoveries, summarizeExecutionGroupHealth } from
5
5
  import { buildTaskObservabilityProjection } from "./taskObservabilityProjection.js";
6
6
  import { isRoleRunStalled, latestStallProgressAt } from "./roleRunStall.js";
7
7
  import { resolveRuntimeHealth } from "../config/yuiConfig.js";
8
+ import { projectSessionTokenMetrics, resolveSessionTokenIdentity } from "../runtime/sessionTokenMetrics.js";
8
9
  /**
9
10
  * Build one consistent Task-first projection from the existing durable
10
11
  * aggregate. This function only reads; it never starts a Controller, queues a
@@ -23,7 +24,7 @@ export function buildTaskExecutionProjection(store, taskId, taskOverride, now =
23
24
  roleName: "leader"
24
25
  }) ?? null;
25
26
  const roleSessions = roles.flatMap((role) => {
26
- const session = store.getRoleSession?.(taskId, role.name, role.activeAgentId);
27
+ const session = store.getRoleSession?.(taskId, role.name);
27
28
  return session === null || session === undefined
28
29
  ? []
29
30
  : [{ roleName: role.name, ...session }];
@@ -54,7 +55,6 @@ export function buildTaskExecutionProjection(store, taskId, taskOverride, now =
54
55
  pendingWakeup: store.getPendingWakeup?.(taskId) ?? null,
55
56
  leaderMailbox,
56
57
  leaderFailure: store.getLeaderFailure?.(taskId) ?? null,
57
- operatorNotification: store.getOperatorNotification?.(taskId) ?? null,
58
58
  roleSessions,
59
59
  now,
60
60
  runtimeHealthPolicy: resolveRuntimeHealth(store.getConfig?.().runtimeHealth)
@@ -78,7 +78,7 @@ export function projectTaskExecutionFromFacts(facts) {
78
78
  /** Alias kept intentionally small for scheduler callers and external read models. */
79
79
  export const deriveTaskExecutionProjection = projectTaskExecution;
80
80
  export function projectTaskExecution(facts) {
81
- const { task, roles, runs, workItems = [], inputRequests = [], reviewRounds = [], integrations = [], events = [], pendingWakeup = null, leaderMailbox = null, leaderFailure = null, operatorNotification = null, roleSessions = [], executionGroups = [] } = facts;
81
+ const { task, roles, runs, workItems = [], inputRequests = [], reviewRounds = [], integrations = [], events = [], pendingWakeup = null, leaderMailbox = null, leaderFailure = null, roleSessions = [], executionGroups = [] } = facts;
82
82
  const now = facts.now ?? new Date();
83
83
  const groupSummaries = executionGroups.map((group) => {
84
84
  const stageGroups = workItems.find((item) => (item.executionGroups.some(({ id }) => id === group.id)))?.executionGroups;
@@ -96,6 +96,14 @@ export function projectTaskExecution(facts) {
96
96
  ...executionGroups,
97
97
  ...workItems.flatMap((item) => item.executionGroups)
98
98
  ]);
99
+ const sessionTokens = roleSessions.map((session) => {
100
+ const identity = resolveSessionTokenIdentity({ taskId: task.id, ...session });
101
+ return Object.freeze({
102
+ roleName: session.roleName,
103
+ agentId: session.agentId,
104
+ metrics: projectSessionTokenMetrics(events, identity)
105
+ });
106
+ });
99
107
  const observability = buildTaskObservabilityProjection({
100
108
  workItems,
101
109
  executionGroups: observabilityGroups,
@@ -103,6 +111,7 @@ export function projectTaskExecution(facts) {
103
111
  runs,
104
112
  events,
105
113
  contextSnapshots: facts.contextSnapshots,
114
+ sessionTokens,
106
115
  now
107
116
  });
108
117
  const laneRecovery = actionableExecutionLaneRecoveries(groupSummaries)[0];
@@ -118,6 +127,8 @@ export function projectTaskExecution(facts) {
118
127
  purpose: run.purpose,
119
128
  delivered: run.deliveredAt !== undefined,
120
129
  status: run.status,
130
+ ...(run.workItemId === undefined ? {} : { workItemId: run.workItemId }),
131
+ ...(run.reviewRoundId === undefined ? {} : { reviewRoundId: run.reviewRoundId }),
121
132
  ...(run.executionGroupId === undefined ? {} : { executionGroupId: run.executionGroupId }),
122
133
  ...(run.executionLaneId === undefined ? {} : { executionLaneId: run.executionLaneId })
123
134
  }));
@@ -138,7 +149,6 @@ export function projectTaskExecution(facts) {
138
149
  monitoring,
139
150
  failClosed: false,
140
151
  activeRuns: activeRunViews,
141
- activeExecutorCount: 0,
142
152
  attention: [],
143
153
  blockers: [],
144
154
  pendingWakeup
@@ -150,21 +160,22 @@ export function projectTaskExecution(facts) {
150
160
  activeRuns,
151
161
  events,
152
162
  leaderFailure,
153
- operatorNotification,
154
163
  roleSessions,
155
164
  inputRequests
156
165
  });
157
166
  const openInputs = inputRequests.filter((request) => request.status === "open");
158
167
  const blockers = collectBlockers(workItems, reviewRounds, integrations, openInputs, task);
159
- const activeExecutors = activeRuns.filter((run) => isExecutionCarrier(run));
160
- const activeWorkers = activeExecutors.filter((run) => run.roleName !== "leader");
161
- const healthyExecutionCarriers = activeWorkers.filter((run) => (run.deliveredAt !== undefined
168
+ const activeExecutionRuns = activeRuns.filter((run) => run.purpose === "execution");
169
+ const activeReviewRuns = activeRuns.filter((run) => run.purpose === "review");
170
+ const activeDelegatedExecutions = activeExecutionRuns.filter((run) => (run.roleName !== "leader"));
171
+ const healthyActiveRuns = activeRuns.filter((run) => (run.deliveredAt !== undefined
162
172
  && !attention.some((item) => item.runId === run.id)));
163
173
  const activeLeader = activeRuns.find((run) => run.roleName === "leader");
164
- const pendingDelivery = activeRuns.some((run) => run.deliveredAt === undefined);
174
+ const pendingDeliveryRuns = activeRuns.filter((run) => run.deliveredAt === undefined);
165
175
  const hasPendingLeaderWork = pendingWakeup !== null
166
- || leaderMailbox?.pending !== null && leaderMailbox?.pending !== undefined;
167
- const recoveryPending = isRecoveryPending(pendingWakeup, leaderMailbox, leaderFailure, operatorNotification);
176
+ || leaderMailbox !== null && (leaderMailbox.pending.normal !== null
177
+ || leaderMailbox.pending.userCorrection !== null);
178
+ const recoveryPending = isRecoveryPending(pendingWakeup, leaderMailbox, leaderFailure);
168
179
  const failedWork = workItems.some((item) => item.status === "failed");
169
180
  const candidateReady = workItems.some((item) => item.status === "awaiting_acceptance");
170
181
  const blockedIntegration = integrations.some((attempt) => attempt.status === "blocked");
@@ -177,20 +188,19 @@ export function projectTaskExecution(facts) {
177
188
  if (first === undefined)
178
189
  throw new Error("Task execution attention disappeared.");
179
190
  const progressingWithAttention = first.kind === "checkpoint-overdue"
180
- && healthyExecutionCarriers.length > 0;
191
+ && healthyActiveRuns.length > 0;
181
192
  return render({
182
193
  task,
183
194
  status: progressingWithAttention ? "progressing-with-attention" : "attention",
184
195
  owner: first.owner,
185
196
  action: "inspect-attention",
186
197
  summary: progressingWithAttention
187
- ? `${healthyExecutionCarriers.length} healthy execution carrier(s) remain while ${first.summary}`
198
+ ? `${healthyActiveRuns.length} healthy active Run(s) remain while ${first.summary}`
188
199
  : first.summary,
189
200
  reason: progressingWithAttention ? "progressing-with-attention" : first.kind,
190
201
  monitoring,
191
202
  failClosed: hasLeaderMismatch || attention.some((item) => item.failClosed === true),
192
203
  activeRuns: activeRunViews,
193
- activeExecutorCount: activeExecutors.length,
194
204
  attention,
195
205
  blockers,
196
206
  pendingWakeup
@@ -210,7 +220,6 @@ export function projectTaskExecution(facts) {
210
220
  monitoring,
211
221
  failClosed: false,
212
222
  activeRuns: activeRunViews,
213
- activeExecutorCount: activeExecutors.length,
214
223
  attention,
215
224
  blockers,
216
225
  pendingWakeup
@@ -231,7 +240,6 @@ export function projectTaskExecution(facts) {
231
240
  monitoring,
232
241
  failClosed: false,
233
242
  activeRuns: activeRunViews,
234
- activeExecutorCount: activeExecutors.length,
235
243
  attention,
236
244
  blockers,
237
245
  pendingWakeup
@@ -251,7 +259,6 @@ export function projectTaskExecution(facts) {
251
259
  monitoring,
252
260
  failClosed: hasLeaderMismatch,
253
261
  activeRuns: activeRunViews,
254
- activeExecutorCount: activeExecutors.length,
255
262
  attention,
256
263
  blockers,
257
264
  pendingWakeup
@@ -269,83 +276,124 @@ export function projectTaskExecution(facts) {
269
276
  monitoring,
270
277
  failClosed: leaderFailure !== null,
271
278
  activeRuns: activeRunViews,
272
- activeExecutorCount: activeExecutors.length,
273
279
  attention,
274
280
  blockers,
275
281
  pendingWakeup
276
282
  });
277
283
  }
278
- if (pendingDelivery) {
284
+ const leaderDeliveryPending = pendingDeliveryRuns.some((run) => run.roleName === "leader");
285
+ if (leaderDeliveryPending) {
279
286
  return render({
280
287
  task,
281
288
  status: "recovering",
282
289
  owner: "leader",
283
290
  action: "recover-leader",
284
- summary: "An active Run is awaiting provider acceptance; delivery remains fail-closed.",
291
+ summary: "The active Leader Run is awaiting provider acceptance; delivery remains fail-closed.",
285
292
  reason: "delivery-pending",
286
293
  monitoring,
287
294
  failClosed: false,
288
295
  activeRuns: activeRunViews,
289
- activeExecutorCount: activeExecutors.length,
290
296
  attention,
291
297
  blockers,
292
298
  pendingWakeup
293
299
  });
294
300
  }
295
- if (activeWorkers.length > 0 && activeLeader === undefined) {
301
+ if (activeLeader !== undefined) {
302
+ const concurrentExecutionCount = activeDelegatedExecutions.length;
303
+ const concurrentReviewCount = activeReviewRuns.length;
304
+ const concurrentSummary = [
305
+ concurrentExecutionCount === 0
306
+ ? null
307
+ : `${concurrentExecutionCount} delegated execution Run(s)`,
308
+ concurrentReviewCount === 0
309
+ ? null
310
+ : `${concurrentReviewCount} Review Run(s)`
311
+ ].filter((value) => value !== null).join(" and ");
296
312
  return render({
297
313
  task,
298
- status: "waiting-on-agents",
299
- owner: roleOwner(activeWorkers[0].roleName),
300
- action: "wait-for-agents",
301
- summary: `${activeWorkers.length} delegated execution Run(s) are active; Leader is waiting for their result.`,
302
- reason: "delegated-work-active",
314
+ status: "working",
315
+ owner: "leader",
316
+ action: "advance-task",
317
+ summary: concurrentSummary.length === 0
318
+ ? "Leader Run is actively advancing the Task."
319
+ : `Leader Run is actively advancing the Task alongside ${concurrentSummary}.`,
320
+ reason: concurrentSummary.length === 0 ? "leader-run-active" : "leader-and-agents-active",
303
321
  monitoring,
304
322
  failClosed: false,
305
323
  activeRuns: activeRunViews,
306
- activeExecutorCount: activeExecutors.length,
307
324
  attention,
308
325
  blockers,
309
326
  pendingWakeup
310
327
  });
311
328
  }
312
- if (activeLeader !== undefined) {
329
+ if (hasPendingLeaderWork) {
313
330
  return render({
314
331
  task,
315
- status: activeWorkers.length > 0 ? "waiting-on-agents" : "working",
332
+ status: "needs-leader-action",
316
333
  owner: "leader",
317
- action: activeWorkers.length > 0 ? "wait-for-agents" : "advance-task",
318
- summary: activeWorkers.length > 0
319
- ? `Leader Run is active while ${activeWorkers.length} delegated Run(s) continue.`
320
- : "Leader Run is actively advancing the Task.",
321
- reason: activeWorkers.length > 0 ? "leader-and-delegates-active" : "leader-run-active",
334
+ action: "advance-task",
335
+ summary: "A durable Leader wake is pending; concurrent AgentRuns remain visible but do not suppress it.",
336
+ reason: "leader-wake-pending",
322
337
  monitoring,
323
338
  failClosed: false,
324
339
  activeRuns: activeRunViews,
325
- activeExecutorCount: activeExecutors.length,
326
340
  attention,
327
341
  blockers,
328
342
  pendingWakeup
329
343
  });
330
344
  }
331
- if (activeExecutors.length > 0) {
345
+ if (pendingDeliveryRuns.length > 0) {
346
+ return render({
347
+ task,
348
+ status: "recovering",
349
+ owner: "leader",
350
+ action: "recover-execution",
351
+ summary: `${pendingDeliveryRuns.length} active delegated AgentRun(s) are awaiting provider acceptance; delivery remains fail-closed.`,
352
+ reason: "delivery-pending",
353
+ monitoring,
354
+ failClosed: false,
355
+ activeRuns: activeRunViews,
356
+ attention,
357
+ blockers,
358
+ pendingWakeup
359
+ });
360
+ }
361
+ if (activeDelegatedExecutions.length > 0) {
362
+ const reviewSuffix = activeReviewRuns.length === 0
363
+ ? ""
364
+ : `; ${activeReviewRuns.length} Review Run(s) are also active`;
332
365
  return render({
333
366
  task,
334
367
  status: "waiting-on-agents",
335
- owner: roleOwner(activeExecutors[0].roleName),
368
+ owner: roleOwner(activeDelegatedExecutions[0].roleName),
336
369
  action: "wait-for-agents",
337
- summary: `${activeExecutors.length} delegated execution Run(s) are active.`,
370
+ summary: `${activeDelegatedExecutions.length} delegated execution Run(s) are active${reviewSuffix}.`,
338
371
  reason: "delegated-work-active",
339
372
  monitoring,
340
373
  failClosed: false,
341
374
  activeRuns: activeRunViews,
342
- activeExecutorCount: activeExecutors.length,
343
375
  attention,
344
376
  blockers,
345
377
  pendingWakeup
346
378
  });
347
379
  }
348
- if (candidateReady || unresolvedIntegration || hasPendingLeaderWork) {
380
+ if (activeReviewRuns.length > 0) {
381
+ return render({
382
+ task,
383
+ status: "waiting-on-agents",
384
+ owner: "reviewer",
385
+ action: "wait-for-agents",
386
+ summary: `${activeReviewRuns.length} Review Run(s) are evaluating frozen candidates; newer facts can still wake the Leader.`,
387
+ reason: "review-active",
388
+ monitoring,
389
+ failClosed: false,
390
+ activeRuns: activeRunViews,
391
+ attention,
392
+ blockers,
393
+ pendingWakeup
394
+ });
395
+ }
396
+ if (candidateReady || unresolvedIntegration) {
349
397
  return render({
350
398
  task,
351
399
  status: "needs-leader-action",
@@ -353,16 +401,13 @@ export function projectTaskExecution(facts) {
353
401
  action: "advance-task",
354
402
  summary: candidateReady
355
403
  ? "A WorkItem Candidate is awaiting Leader action."
356
- : unresolvedIntegration
357
- ? "An Integration or validation record needs Leader action."
358
- : "A durable Leader wake is pending.",
404
+ : "An Integration or validation record needs Leader action.",
359
405
  reason: candidateReady
360
406
  ? "candidate-ready"
361
- : unresolvedIntegration ? "integration-pending" : "leader-wake-pending",
407
+ : "integration-pending",
362
408
  monitoring,
363
409
  failClosed: false,
364
410
  activeRuns: activeRunViews,
365
- activeExecutorCount: activeExecutors.length,
366
411
  attention,
367
412
  blockers,
368
413
  pendingWakeup
@@ -378,7 +423,6 @@ export function projectTaskExecution(facts) {
378
423
  monitoring,
379
424
  failClosed: false,
380
425
  activeRuns: activeRunViews,
381
- activeExecutorCount: activeExecutors.length,
382
426
  attention,
383
427
  blockers,
384
428
  pendingWakeup
@@ -395,7 +439,6 @@ function projection(input) {
395
439
  reason: input.reason,
396
440
  monitoring: input.monitoring,
397
441
  failClosed: input.failClosed,
398
- activeExecutorCount: input.activeExecutorCount,
399
442
  activeRuns: input.activeRuns,
400
443
  executionGroups: input.executionGroups ?? [],
401
444
  observability: input.observability,
@@ -456,27 +499,14 @@ function collectExecutionGroups(workItems, reviewRounds) {
456
499
  }
457
500
  function collectAttention(input) {
458
501
  const result = [];
459
- const { task, activeRuns, events, leaderFailure, operatorNotification } = input;
460
- if (leaderFailure !== null || operatorNotification?.type === "leader-recovery-failed") {
502
+ const { task, activeRuns, events, leaderFailure } = input;
503
+ if (leaderFailure !== null) {
461
504
  result.push({
462
505
  kind: "leader-recovery",
463
506
  id: `leader-recovery:${task.id}`,
464
507
  owner: "operator",
465
- summary: leaderFailure?.message
466
- ?? (operatorNotification?.type === "leader-recovery-failed"
467
- ? operatorNotification.message
468
- : "Leader recovery failed."),
469
- ...(leaderFailure === null ? {} : { failClosed: true })
470
- });
471
- }
472
- if (operatorNotification?.type === "leader-stalled") {
473
- result.push({
474
- kind: "leader-stalled",
475
- id: `leader-stall:${operatorNotification.runId}:${operatorNotification.progressAt}`,
476
- owner: "operator",
477
- summary: operatorNotification.message,
478
- runId: operatorNotification.runId,
479
- roleName: "leader"
508
+ summary: leaderFailure.message,
509
+ failClosed: true
480
510
  });
481
511
  }
482
512
  for (const run of activeRuns) {
@@ -620,15 +650,6 @@ function collectBlockers(workItems, reviewRounds, integrations, openInputs, task
620
650
  }
621
651
  return blockers;
622
652
  }
623
- function isExecutionCarrier(run) {
624
- return run.purpose === "review"
625
- || run.roleName === "leader"
626
- // A scheduler-only projection may not have the WorkItem fold available
627
- // (for example during a recovery read). An active non-Leader Role is still
628
- // an execution carrier; omitting it would falsely wake the Leader.
629
- || run.workItemId !== undefined
630
- || run.roleName !== "leader";
631
- }
632
653
  function roleOwner(roleName) {
633
654
  const normalized = roleName.toLowerCase();
634
655
  if (normalized === "leader")
@@ -639,8 +660,8 @@ function roleOwner(roleName) {
639
660
  return "tester";
640
661
  return "worker";
641
662
  }
642
- function isRecoveryPending(wakeup, mailbox, failure, notification) {
643
- if (failure !== null || notification?.type === "leader-recovery-failed")
663
+ function isRecoveryPending(wakeup, mailbox, failure) {
664
+ if (failure !== null)
644
665
  return false;
645
666
  const reasons = [
646
667
  ...(wakeup?.reasons ?? []),
@@ -1,6 +1,5 @@
1
1
  import { isDeepStrictEqual } from "node:util";
2
2
  import { observedExecutionResourceUsage } from "../execution/resourceBroker.js";
3
- import { runtimeObservationFromTaskEvent } from "../runtime/runtimeObservation.js";
4
3
  /**
5
4
  * Build the read-only DAG, execution, cost, and context view consumed by CLI
6
5
  * and Web. It deliberately derives every value from existing Task records and
@@ -34,7 +33,7 @@ export function buildTaskObservabilityProjection(input) {
34
33
  });
35
34
  });
36
35
  const itemCost = projectCost(groups, input.runs, input.events, now);
37
- const itemContext = projectContext(groups, input.runs, input.events, input.contextSnapshots);
36
+ const itemContext = projectContext(groups, input.runs, input.contextSnapshots);
38
37
  const evidence = groups.flatMap((group) => group.lanes.flatMap((lane) => (lane.result?.evidence ?? [])));
39
38
  const openFindingCount = groups.reduce((count, group) => count + group.lanes.reduce((laneCount, lane) => laneCount + (lane.result?.findings ?? [])
40
39
  .filter(({ status }) => status === "open").length, 0), 0);
@@ -53,8 +52,14 @@ export function buildTaskObservabilityProjection(input) {
53
52
  });
54
53
  });
55
54
  const cost = projectCost(input.executionGroups, input.runs, input.events, now);
56
- const context = projectContext(input.executionGroups, input.runs, input.events, input.contextSnapshots);
57
- return Object.freeze({ dag, workItems, cost, context });
55
+ const context = projectContext(input.executionGroups, input.runs, input.contextSnapshots);
56
+ return Object.freeze({
57
+ dag,
58
+ workItems,
59
+ cost,
60
+ context,
61
+ sessionTokens: Object.freeze([...(input.sessionTokens ?? [])])
62
+ });
58
63
  }
59
64
  function projectDag(workItems) {
60
65
  const byId = new Map(workItems.map((item) => [item.id, item]));
@@ -187,7 +192,7 @@ function projectCost(groups, runs, events, now) {
187
192
  marginalValueStatus: "unavailable"
188
193
  });
189
194
  }
190
- function projectContext(groups, runs, events, snapshots) {
195
+ function projectContext(groups, runs, snapshots) {
191
196
  const refs = new Map();
192
197
  for (const group of groups) {
193
198
  const ref = group.stage?.contextSnapshotRef;
@@ -216,17 +221,11 @@ function projectContext(groups, runs, events, snapshots) {
216
221
  });
217
222
  });
218
223
  const sizes = metrics.flatMap(({ byteSize }) => byteSize === null ? [] : [byteSize]);
219
- const observedInputPeakTokens = events.flatMap((event) => {
220
- const observation = runtimeObservationFromTaskEvent(event);
221
- const inputTokens = observation?.payload.usage?.inputTokens;
222
- return inputTokens === undefined ? [] : [inputTokens];
223
- }).reduce((peak, value) => Math.max(peak, value), 0);
224
224
  return Object.freeze({
225
225
  snapshotCount: metrics.length,
226
226
  snapshots: Object.freeze(metrics),
227
227
  totalBytes: sizes.length === metrics.length ? sizes.reduce((sum, size) => sum + size, 0) : null,
228
228
  largestBytes: sizes.length === 0 ? null : Math.max(...sizes),
229
- observedInputPeakTokens,
230
229
  compressionEvents: null,
231
230
  compressionRatio: null,
232
231
  compressionStatus: "unavailable"