@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.
- package/ARCHITECTURE.md +23 -4
- package/README.md +36 -13
- package/dist/cli/commandCatalog.js +2 -4
- package/dist/cli.js +105 -43
- package/dist/commands/configCommands.js +12 -45
- package/dist/commands/executionAuditCommands.js +1 -1
- package/dist/commands/jobCommands.js +4 -10
- package/dist/commands/taskCommands.js +154 -171
- package/dist/commands/taskCompletionGate.js +29 -54
- package/dist/commands/taskContextCommand.js +55 -11
- package/dist/commands/taskInputCommands.js +8 -5
- package/dist/commands/taskNextActionCommand.js +56 -6
- package/dist/commands/taskOverviewCommand.js +17 -31
- package/dist/commands/taskRoleRuntimeStatus.js +24 -0
- package/dist/commands/taskWorkspaceCommands.js +5 -2
- package/dist/config/configCatalog.js +2 -2
- package/dist/config/yuiConfig.js +3 -7
- package/dist/context/wakeNotification.js +20 -5
- package/dist/controller/agentRuntimeObserver.js +247 -51
- package/dist/controller/clientRuntime.js +1 -1
- package/dist/controller/controller.js +8 -12
- package/dist/controller/fileSchedulerStoreAdapter.js +146 -198
- package/dist/controller/runtime.js +3 -3
- package/dist/controller/runtimeEventInbox.js +17 -7
- package/dist/controller/runtimeEventProcessor.js +12 -19
- package/dist/controller/runtimeObservationHook.js +45 -0
- package/dist/execution/resourceBroker.js +5 -4
- package/dist/executor/agentExecutor.js +4 -4
- package/dist/executor/effectiveLaunch.js +8 -48
- package/dist/executor/fileRoleLaunchPlanner.js +36 -16
- package/dist/executor/workspacePreflightClassification.js +23 -2
- package/dist/interaction/operatorPresentation.js +33 -89
- package/dist/lifecycle/exactRunTerminalization.js +1 -2
- package/dist/lifecycle/taskRoleSessionReset.js +5 -13
- package/dist/observability/orchestrationMetrics.js +8 -3
- package/dist/profile/agentProfile.js +1 -1
- package/dist/release/cliHomeReleaseFence.js +123 -0
- package/dist/repository/taskWorkspacePreparer.js +176 -60
- package/dist/resources/sqliteResourceRegistry.js +1 -1
- package/dist/review/deltaRecheck.js +12 -51
- package/dist/review/reviewAcceptance.js +26 -0
- package/dist/review/reviewConfig.js +0 -31
- package/dist/review/reviewDecision.js +113 -0
- package/dist/review/reviewOutcomeClassifier.js +1 -1
- package/dist/review/reviewRound.js +1 -1
- package/dist/review/reviewerAvailability.js +69 -0
- package/dist/runtime/agentDriverObservation.js +24 -10
- package/dist/runtime/builtinAgentDrivers.js +4 -3
- package/dist/runtime/builtinTranscriptObserver.js +301 -64
- package/dist/runtime/builtinTranscriptUsage.js +9 -7
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/runtimeObservation.js +13 -0
- package/dist/runtime/runtimeProjection.js +16 -25
- package/dist/runtime/sessionTokenMetrics.js +181 -0
- package/dist/runtime/structuredProviderHost.js +7 -1
- package/dist/scheduler/activeRoleRunDelivery.js +4 -4
- package/dist/scheduler/activeTaskProgress.js +7 -6
- package/dist/scheduler/leaderWakeupProcessor.js +3 -5
- package/dist/scheduler/operatorEvent.js +34 -0
- package/dist/scheduler/operatorInputNotificationProcessor.js +54 -94
- package/dist/scheduler/roleRunStall.js +0 -14
- package/dist/scheduler/taskExecutionProjection.js +97 -76
- package/dist/scheduler/taskObservabilityProjection.js +10 -11
- package/dist/storage/migration/productionRegistry.js +379 -0
- package/dist/storage/sqliteSchema.js +18 -8
- package/dist/storage/sqliteStore.js +24 -20
- package/dist/storage/storeRpc.js +0 -1
- package/dist/storage/taskStore.js +15 -30
- package/dist/storage/upgrade/recordVersions.js +3 -2
- package/dist/storage/upgrade/sqliteMigrationTarget.js +30 -8
- package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +23 -11
- package/dist/storage/upgrade/sqliteStateMigration.js +66 -8
- package/dist/task/completionReadiness.js +10 -6
- package/dist/task/nextAction.js +82 -24
- package/dist/telemetry/sqliteTelemetryStore.js +1 -1
- package/dist/web/assets/client/components.js +13 -3
- package/dist/web/assets/client/i18n.js +6 -2
- package/dist/web/webSnapshot.js +4 -0
- package/i18n/README.zh-CN.md +2 -2
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +53 -16
- package/skills/yui-operator/SKILL.md +9 -0
- package/skills/yui-reviewer/SKILL.md +28 -7
- package/dist/context/sessionContextBudget.js +0 -71
- package/dist/lifecycle/contextBudgetRollover.js +0 -81
- 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
|
|
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,
|
|
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
|
|
160
|
-
const
|
|
161
|
-
const
|
|
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
|
|
174
|
+
const pendingDeliveryRuns = activeRuns.filter((run) => run.deliveredAt === undefined);
|
|
165
175
|
const hasPendingLeaderWork = pendingWakeup !== null
|
|
166
|
-
|| leaderMailbox
|
|
167
|
-
|
|
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
|
-
&&
|
|
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
|
-
? `${
|
|
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
|
-
|
|
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: "
|
|
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 (
|
|
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: "
|
|
299
|
-
owner:
|
|
300
|
-
action: "
|
|
301
|
-
summary:
|
|
302
|
-
|
|
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 (
|
|
329
|
+
if (hasPendingLeaderWork) {
|
|
313
330
|
return render({
|
|
314
331
|
task,
|
|
315
|
-
status:
|
|
332
|
+
status: "needs-leader-action",
|
|
316
333
|
owner: "leader",
|
|
317
|
-
action:
|
|
318
|
-
summary:
|
|
319
|
-
|
|
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 (
|
|
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(
|
|
368
|
+
owner: roleOwner(activeDelegatedExecutions[0].roleName),
|
|
336
369
|
action: "wait-for-agents",
|
|
337
|
-
summary: `${
|
|
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 (
|
|
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
|
-
:
|
|
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
|
-
:
|
|
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
|
|
460
|
-
if (leaderFailure !== null
|
|
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
|
|
466
|
-
|
|
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
|
|
643
|
-
if (failure !== null
|
|
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.
|
|
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.
|
|
57
|
-
return Object.freeze({
|
|
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,
|
|
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"
|