vericify 1.0.2 → 1.1.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.
@@ -0,0 +1,627 @@
1
+ import { firstDefined } from "../core/util.js";
2
+
3
+ const MINUTE_MS = 60 * 1000;
4
+ const HOUR_MS = 60 * MINUTE_MS;
5
+ const DAY_MS = 24 * HOUR_MS;
6
+
7
+ const BASE_SIGNAL_SCORE = {
8
+ primary: 100,
9
+ secondary: 60,
10
+ continuity: 20,
11
+ background: 0,
12
+ };
13
+
14
+ const FRESHNESS_SCORE = {
15
+ fresh: 24,
16
+ aging: 8,
17
+ stale: -18,
18
+ stale_acknowledged: -18,
19
+ archival: -40,
20
+ };
21
+
22
+ const PROVENANCE_SCORE = {
23
+ native: 12,
24
+ partner: 4,
25
+ derived: -20,
26
+ };
27
+
28
+ const NARRATIVE_WINDOWS = {
29
+ blocker: 5 * MINUTE_MS,
30
+ handoff_note: 10 * MINUTE_MS,
31
+ completion: 10 * MINUTE_MS,
32
+ stale_ack: 5 * MINUTE_MS,
33
+ intervention: 10 * MINUTE_MS,
34
+ operator_note: 10 * MINUTE_MS,
35
+ };
36
+
37
+ const RUN_FRESHNESS = {
38
+ running: { fresh: 15 * MINUTE_MS, stale: 120 * MINUTE_MS, label: "running work" },
39
+ blocked: { fresh: 10 * MINUTE_MS, stale: 45 * MINUTE_MS, label: "blocked work" },
40
+ handoff_open: { fresh: 30 * MINUTE_MS, stale: 180 * MINUTE_MS, label: "open handoff" },
41
+ handoff_accepted: { fresh: 20 * MINUTE_MS, stale: 90 * MINUTE_MS, label: "accepted handoff" },
42
+ default: { fresh: 15 * MINUTE_MS, stale: 120 * MINUTE_MS, label: "active work" },
43
+ };
44
+
45
+ function clamp(value, min, max) {
46
+ return Math.max(min, Math.min(max, value));
47
+ }
48
+
49
+ function parseTimestamp(value) {
50
+ if (!value) return Number.NaN;
51
+ const ms = Date.parse(value);
52
+ return Number.isNaN(ms) ? Number.NaN : ms;
53
+ }
54
+
55
+ function sortByTimestamp(items, accessor) {
56
+ return [...items].sort((left, right) => String(accessor(left)).localeCompare(String(accessor(right))));
57
+ }
58
+
59
+ function addDuration(timestamp, deltaMs) {
60
+ const ms = parseTimestamp(timestamp);
61
+ if (Number.isNaN(ms)) return undefined;
62
+ return new Date(ms + deltaMs).toISOString();
63
+ }
64
+
65
+ function durationLabel(deltaMs) {
66
+ const abs = Math.abs(deltaMs);
67
+ if (abs < MINUTE_MS) return "now";
68
+ if (abs < HOUR_MS) return `${Math.round(abs / MINUTE_MS)}m`;
69
+ if (abs < DAY_MS) return `${Math.round(abs / HOUR_MS)}h`;
70
+ return `${Math.round(abs / DAY_MS)}d`;
71
+ }
72
+
73
+ function humanize(value) {
74
+ return String(value ?? "")
75
+ .replace(/[-_]+/g, " ")
76
+ .trim();
77
+ }
78
+
79
+ function hasRecordSource(record, source) {
80
+ if (record?.record_source === source) return true;
81
+ return Array.isArray(record?.record_sources) && record.record_sources.some((entry) => entry?.source === source);
82
+ }
83
+
84
+ function primaryRecordSource(record) {
85
+ if (record?.record_source) return record.record_source;
86
+ const first = Array.isArray(record?.record_sources) ? record.record_sources[0]?.source : undefined;
87
+ return first ?? "derived";
88
+ }
89
+
90
+ function collapsePolicyForItem(item) {
91
+ if (item.source_kind === "lane_heartbeat") {
92
+ return {
93
+ collapse_key: `${item.run_id}:heartbeat:${item.lane_id ?? item.actor_id ?? "-"}`,
94
+ collapse_window_ms: 0,
95
+ };
96
+ }
97
+ if (item.signal_class === "background" || item.visible_by_default === false) {
98
+ return {
99
+ collapse_key: `${item.run_id}:observer:${item.source_kind}:${item.actor_id}:${item.label}:${item.status}`,
100
+ collapse_window_ms: 10 * MINUTE_MS,
101
+ };
102
+ }
103
+ if ((item.source_kind === "status_event" || item.source_kind === "run_ledger") && item.signal_class !== "primary") {
104
+ return {
105
+ collapse_key: `${item.run_id}:maintenance:${item.source_kind}:${item.actor_id}:${item.label}:${item.status}`,
106
+ collapse_window_ms: 5 * MINUTE_MS,
107
+ };
108
+ }
109
+ return {
110
+ collapse_key: `${item.run_id}:${item.activity_id}`,
111
+ collapse_window_ms: 0,
112
+ };
113
+ }
114
+
115
+ function collapseSummaryForGroup(items, representative) {
116
+ if (items.length <= 1) return undefined;
117
+ if (representative.signal_class === "background" || representative.visible_by_default === false) {
118
+ return `${items.length} observer updates hidden by default`;
119
+ }
120
+ const label = humanize(representative.label || representative.source_kind || "event");
121
+ return `${items.length} ${label} updates; latest ${representative.summary}`;
122
+ }
123
+
124
+ function scoreActivityItem(item, nowMs) {
125
+ const reasons = [];
126
+ let score = BASE_SIGNAL_SCORE[item.signal_class] ?? 0;
127
+ reasons.push(`signal_class=${item.signal_class}(${BASE_SIGNAL_SCORE[item.signal_class] ?? 0})`);
128
+
129
+ const source = primaryRecordSource(item);
130
+ const provenance = PROVENANCE_SCORE[source] ?? 0;
131
+ score += provenance;
132
+ reasons.push(`provenance=${source}(${provenance})`);
133
+
134
+ const itemMs = parseTimestamp(item.timestamp);
135
+ const ageMs = Number.isNaN(itemMs) ? DAY_MS + 1 : nowMs - itemMs;
136
+ let freshnessClass = "archival";
137
+ if (ageMs <= 10 * MINUTE_MS) freshnessClass = "fresh";
138
+ else if (ageMs <= 60 * MINUTE_MS) freshnessClass = "aging";
139
+ else if (ageMs <= DAY_MS) freshnessClass = "stale";
140
+ const freshness = FRESHNESS_SCORE[freshnessClass] ?? 0;
141
+ score += freshness;
142
+ reasons.push(`freshness=${freshnessClass}(${freshness})`);
143
+
144
+ const novelty = item.collapsed_event_count > 1 ? -12 : 16;
145
+ score += novelty;
146
+ reasons.push(`novelty=${item.collapsed_event_count > 1 ? "repeated" : "new"}(${novelty})`);
147
+
148
+ const lifecycle = item.status === "blocked" || item.status === "failed" ? 40 : item.status === "completed" ? 10 : 0;
149
+ score += lifecycle;
150
+ if (lifecycle) reasons.push(`lifecycle=${item.status}(${lifecycle})`);
151
+
152
+ const narrative = item.source_kind === "process_post" ? 10 : 0;
153
+ score += narrative;
154
+ if (narrative) reasons.push(`narrative=present(${narrative})`);
155
+
156
+ return {
157
+ ...item,
158
+ score: clamp(score, 0, 200),
159
+ score_reasons: reasons,
160
+ display_summary: item.collapse_summary ?? item.summary,
161
+ };
162
+ }
163
+
164
+ export function isMeaningfulActivity(item) {
165
+ return item?.signal_class === "primary" || item?.signal_class === "secondary";
166
+ }
167
+
168
+ export function collapseAndScoreActivityItems(items, { now = Date.now() } = {}) {
169
+ const nowMs = typeof now === "string" ? parseTimestamp(now) : now;
170
+ const sorted = sortByTimestamp(items ?? [], (item) => item.timestamp ?? "");
171
+ const groups = [];
172
+
173
+ for (const item of sorted) {
174
+ const policy = collapsePolicyForItem(item);
175
+ const itemMs = parseTimestamp(item.timestamp);
176
+ const previous = groups.at(-1);
177
+ if (
178
+ previous &&
179
+ previous.collapse_key === policy.collapse_key &&
180
+ policy.collapse_window_ms > 0 &&
181
+ !Number.isNaN(itemMs) &&
182
+ !Number.isNaN(previous.last_ms) &&
183
+ itemMs - previous.last_ms <= policy.collapse_window_ms
184
+ ) {
185
+ previous.items.push(item);
186
+ previous.last_ms = itemMs;
187
+ continue;
188
+ }
189
+ groups.push({
190
+ collapse_key: policy.collapse_key,
191
+ collapse_window_ms: policy.collapse_window_ms,
192
+ items: [item],
193
+ first_ms: itemMs,
194
+ last_ms: itemMs,
195
+ });
196
+ }
197
+
198
+ return groups.map((group) => {
199
+ const representative = { ...group.items[group.items.length - 1] };
200
+ representative.collapse_key = group.collapse_key;
201
+ representative.collapse_window_ms = group.collapse_window_ms;
202
+ representative.first_timestamp = group.items[0]?.timestamp;
203
+ representative.collapsed_event_count = group.items.length;
204
+ representative.suppressed_event_ids = group.items.slice(0, -1).map((item) => item.activity_id);
205
+ representative.collapse_summary = collapseSummaryForGroup(group.items, representative);
206
+ return scoreActivityItem(representative, nowMs);
207
+ });
208
+ }
209
+
210
+ function latest(items, accessor) {
211
+ return sortByTimestamp(items ?? [], accessor).at(-1);
212
+ }
213
+
214
+ function latestMatching(items, accessor, predicate) {
215
+ return sortByTimestamp((items ?? []).filter(predicate), accessor).at(-1);
216
+ }
217
+
218
+ function latestNativePost(posts, kind, triggerTimestamp) {
219
+ const triggerMs = parseTimestamp(triggerTimestamp);
220
+ return latestMatching(posts, (post) => post.timestamp ?? "", (post) =>
221
+ hasRecordSource(post, "native") &&
222
+ post.kind === kind &&
223
+ (Number.isNaN(triggerMs) || parseTimestamp(post.timestamp) >= triggerMs)
224
+ );
225
+ }
226
+
227
+ function inferPresence(runDetail, meaningfulItems, processPosts, activeHandoff) {
228
+ if (activeHandoff) {
229
+ return {
230
+ current_actor_id: activeHandoff.to_agent ?? activeHandoff.from_agent,
231
+ waiting_on_actor_id: activeHandoff.to_agent ?? activeHandoff.from_agent,
232
+ acknowledgement_state: activeHandoff.status,
233
+ last_response_at: activeHandoff.updated_at ?? activeHandoff.created_at,
234
+ presence_source: "handoff",
235
+ };
236
+ }
237
+ const latestPost = latest(processPosts, (post) => post.timestamp ?? "");
238
+ if (latestPost) {
239
+ return {
240
+ current_actor_id: latestPost.agent_id ?? "unknown",
241
+ waiting_on_actor_id: undefined,
242
+ acknowledgement_state: "not_required",
243
+ last_response_at: latestPost.timestamp,
244
+ presence_source: hasRecordSource(latestPost, "native") ? "native_post" : "partner_post",
245
+ };
246
+ }
247
+ const latestItem = latest(meaningfulItems, (item) => item.timestamp ?? "");
248
+ if (latestItem) {
249
+ return {
250
+ current_actor_id: latestItem.actor_id ?? "unknown",
251
+ waiting_on_actor_id: undefined,
252
+ acknowledgement_state: "not_required",
253
+ last_response_at: latestItem.timestamp,
254
+ presence_source: latestItem.source_kind,
255
+ };
256
+ }
257
+ const latestLane = latest(runDetail.lanes ?? [], (lane) => lane.updated_at ?? "");
258
+ return {
259
+ current_actor_id: latestLane?.agent_id,
260
+ waiting_on_actor_id: undefined,
261
+ acknowledgement_state: "unknown",
262
+ last_response_at: latestLane?.updated_at,
263
+ presence_source: latestLane ? "lane" : "none",
264
+ };
265
+ }
266
+
267
+ function freshnessProfile(runDetail, activeHandoff) {
268
+ if (activeHandoff?.status === "accepted") return RUN_FRESHNESS.handoff_accepted;
269
+ if (activeHandoff?.status === "open") return RUN_FRESHNESS.handoff_open;
270
+ if (runDetail.run.status === "blocked") return RUN_FRESHNESS.blocked;
271
+ if (runDetail.run.status === "running") return RUN_FRESHNESS.running;
272
+ return RUN_FRESHNESS.default;
273
+ }
274
+
275
+ function classifyFreshness(runDetail, lastMeaningfulUpdateAt, lastStaleAckAt, activeHandoff, nowMs) {
276
+ const profile = freshnessProfile(runDetail, activeHandoff);
277
+ const lastMeaningfulMs = parseTimestamp(lastMeaningfulUpdateAt);
278
+ if (Number.isNaN(lastMeaningfulMs)) {
279
+ return {
280
+ freshness_class: "archival",
281
+ freshness_reason: "No meaningful activity available.",
282
+ stale_since: undefined,
283
+ overdue_seconds: 0,
284
+ needs_stale_ack: false,
285
+ freshness_profile_label: profile.label,
286
+ };
287
+ }
288
+ const ageMs = nowMs - lastMeaningfulMs;
289
+ const staleSince = new Date(lastMeaningfulMs + profile.stale).toISOString();
290
+ const staleAckMs = parseTimestamp(lastStaleAckAt);
291
+ let freshnessClass = "fresh";
292
+ if (ageMs > DAY_MS) freshnessClass = "archival";
293
+ else if (ageMs > profile.stale) freshnessClass = !Number.isNaN(staleAckMs) && staleAckMs >= lastMeaningfulMs ? "stale_acknowledged" : "stale";
294
+ else if (ageMs > profile.fresh) freshnessClass = "aging";
295
+ const active = runDetail.run.status === "running" || runDetail.run.status === "blocked" || activeHandoff;
296
+ return {
297
+ freshness_class: freshnessClass,
298
+ freshness_reason: `${profile.label} silent for ${durationLabel(ageMs)}.`,
299
+ stale_since: staleSince,
300
+ overdue_seconds: ageMs > profile.stale ? Math.round((ageMs - profile.stale) / 1000) : 0,
301
+ needs_stale_ack: active && freshnessClass === "stale",
302
+ freshness_profile_label: profile.label,
303
+ };
304
+ }
305
+
306
+ function latestBlockedSignal(runDetail, meaningfulItems) {
307
+ const item = latestMatching(meaningfulItems, (entry) => entry.timestamp ?? "", (entry) => entry.status === "blocked" || entry.status === "failed");
308
+ if (item) return { timestamp: item.timestamp, source: "activity", summary: item.display_summary ?? item.summary };
309
+ const handoff = latestMatching(runDetail.handoff_timeline?.items ?? [], (entry) => entry.updated_at ?? entry.created_at ?? "", (entry) => entry.status === "blocked");
310
+ if (handoff) return { timestamp: handoff.updated_at ?? handoff.created_at, source: "handoff", summary: handoff.node_ref ?? handoff.handoff_id };
311
+ return undefined;
312
+ }
313
+
314
+ function latestCompletionSignal(runDetail, meaningfulItems) {
315
+ const item = latestMatching(meaningfulItems, (entry) => entry.timestamp ?? "", (entry) => entry.status === "completed");
316
+ if (item) return { timestamp: item.timestamp, source: "activity", summary: item.display_summary ?? item.summary };
317
+ const handoff = latestMatching(runDetail.handoff_timeline?.items ?? [], (entry) => entry.updated_at ?? entry.created_at ?? "", (entry) => entry.status === "completed");
318
+ if (handoff) return { timestamp: handoff.updated_at ?? handoff.created_at, source: "handoff", summary: handoff.node_ref ?? handoff.handoff_id };
319
+ return undefined;
320
+ }
321
+
322
+ function summarizeTemplate(kind, owner, nextTimestamp) {
323
+ if (kind === "blocker") return `Blocked on ${owner ?? "<owner>"}. Next check at ${nextTimestamp ?? "<time>"}.`;
324
+ if (kind === "handoff_note") return `Accepted handoff. Next action: ${owner ?? "<next step>"}.`;
325
+ if (kind === "completion") return `Completed <change>. New truth: <outcome>.`;
326
+ if (kind === "stale_ack") return `Silence is expected because ${owner ?? "<reason>"}. Re-check at ${nextTimestamp ?? "<time>"}.`;
327
+ if (kind === "operator_note") return `Changed <thing>. Next expected event: ${owner ?? "<event>"}.`;
328
+ return `Update ${owner ?? "<state>"}.`;
329
+ }
330
+
331
+ function deriveNarrativeState({
332
+ runDetail,
333
+ meaningfulItems,
334
+ processPosts,
335
+ activeHandoff,
336
+ freshness,
337
+ currentActorId,
338
+ lastMeaningfulUpdateAt,
339
+ }) {
340
+ const nativeMeaningful = meaningfulItems.filter((item) => hasRecordSource(item, "native"));
341
+ const totalMeaningful = meaningfulItems.length;
342
+ const nativeSignalRatio = totalMeaningful ? nativeMeaningful.length / totalMeaningful : 0;
343
+ const partnerDependencyClass = nativeSignalRatio >= 0.6 ? "native-led" : nativeSignalRatio >= 0.25 ? "mixed" : "partner-led";
344
+ const nativeSignalGap = totalMeaningful > 0 && nativeSignalRatio < 0.35;
345
+
346
+ const blockedSignal = latestBlockedSignal(runDetail, meaningfulItems);
347
+ const acceptedHandoff = latestMatching(runDetail.handoff_timeline?.items ?? [], (entry) => entry.updated_at ?? entry.created_at ?? "", (entry) => entry.status === "accepted");
348
+ const completionSignal = latestCompletionSignal(runDetail, meaningfulItems);
349
+ const lastStaleAck = latestMatching(processPosts, (post) => post.timestamp ?? "", (post) => post.kind === "stale_ack");
350
+
351
+ const obligations = [];
352
+ if (blockedSignal && !latestNativePost(processPosts, "blocker", blockedSignal.timestamp)) {
353
+ obligations.push({
354
+ kind: "blocker",
355
+ due_at: addDuration(blockedSignal.timestamp, NARRATIVE_WINDOWS.blocker),
356
+ reason: "Blocked work lacks a native blocker explanation.",
357
+ });
358
+ }
359
+ if (acceptedHandoff && !latestNativePost(processPosts, "handoff_note", acceptedHandoff.updated_at ?? acceptedHandoff.created_at)) {
360
+ obligations.push({
361
+ kind: "handoff_note",
362
+ due_at: addDuration(acceptedHandoff.updated_at ?? acceptedHandoff.created_at, NARRATIVE_WINDOWS.handoff_note),
363
+ reason: "Accepted handoff lacks a native acknowledgement note.",
364
+ });
365
+ }
366
+ if (completionSignal && !latestNativePost(processPosts, "completion", completionSignal.timestamp)) {
367
+ obligations.push({
368
+ kind: "completion",
369
+ due_at: addDuration(completionSignal.timestamp, NARRATIVE_WINDOWS.completion),
370
+ reason: "Completed work lacks a native completion summary.",
371
+ });
372
+ }
373
+ if (freshness.needs_stale_ack && !latestNativePost(processPosts, "stale_ack", freshness.stale_since)) {
374
+ obligations.push({
375
+ kind: "stale_ack",
376
+ due_at: freshness.stale_since,
377
+ reason: "Stale work lacks a native silence acknowledgement.",
378
+ });
379
+ }
380
+ if (nativeSignalGap && !latestNativePost(processPosts, "operator_note", lastMeaningfulUpdateAt)) {
381
+ obligations.push({
382
+ kind: "operator_note",
383
+ due_at: addDuration(lastMeaningfulUpdateAt, NARRATIVE_WINDOWS.operator_note),
384
+ reason: "Meaningful movement is mostly partner-derived and lacks a native Vericify note.",
385
+ });
386
+ }
387
+
388
+ const obligation = obligations[0];
389
+ return {
390
+ needs_narrative: Boolean(obligation),
391
+ recommended_post_kind: obligation?.kind,
392
+ recommended_summary_template: obligation ? summarizeTemplate(obligation.kind, currentActorId, obligation.due_at) : undefined,
393
+ native_signal_gap: nativeSignalGap,
394
+ native_signal_gap_reason: nativeSignalGap ? `Native meaningful coverage is ${(nativeSignalRatio * 100).toFixed(0)}%.` : "Native meaningful coverage is healthy.",
395
+ native_signal_ratio: Number(nativeSignalRatio.toFixed(3)),
396
+ native_narrative_gap: Boolean(obligation),
397
+ partner_dependency_class: partnerDependencyClass,
398
+ narrative_reason: obligation?.reason,
399
+ narrative_due_at: obligation?.due_at,
400
+ };
401
+ }
402
+
403
+ function isHumanNote(post) {
404
+ if (post.kind === "operator_note") return true;
405
+ const agent = String(post.agent_id ?? "").toLowerCase();
406
+ return agent === "operator" || agent === "human" || agent === "vericify-hub";
407
+ }
408
+
409
+ function determineExpectedNextEvent(runDetail, activeHandoff, narrativeState, freshness, lastMeaningfulUpdateAt) {
410
+ if (activeHandoff?.status === "open") {
411
+ return {
412
+ expected_next_event: "accept_handoff",
413
+ overdue_at: addDuration(activeHandoff.updated_at ?? activeHandoff.created_at, RUN_FRESHNESS.handoff_open.fresh),
414
+ recommended_owner_action: "accept_handoff",
415
+ };
416
+ }
417
+ if (narrativeState.recommended_post_kind) {
418
+ return {
419
+ expected_next_event: narrativeState.recommended_post_kind,
420
+ overdue_at: narrativeState.narrative_due_at,
421
+ recommended_owner_action: narrativeState.recommended_post_kind === "stale_ack" ? "ack_stale" : "write_note",
422
+ };
423
+ }
424
+ if (runDetail.run.status === "blocked") {
425
+ return {
426
+ expected_next_event: "resolve_blocker",
427
+ overdue_at: freshness.stale_since,
428
+ recommended_owner_action: "resolve_blocker",
429
+ };
430
+ }
431
+ if (runDetail.run.status === "running") {
432
+ return {
433
+ expected_next_event: "state_update",
434
+ overdue_at: freshness.stale_since ?? addDuration(lastMeaningfulUpdateAt, RUN_FRESHNESS.running.stale),
435
+ recommended_owner_action: "update_state",
436
+ };
437
+ }
438
+ if (runDetail.run.status === "planned") {
439
+ return {
440
+ expected_next_event: "clarify_owner",
441
+ overdue_at: addDuration(lastMeaningfulUpdateAt, RUN_FRESHNESS.default.stale),
442
+ recommended_owner_action: "clarify_owner",
443
+ };
444
+ }
445
+ return {
446
+ expected_next_event: null,
447
+ overdue_at: null,
448
+ recommended_owner_action: "observe_only",
449
+ };
450
+ }
451
+
452
+ function attentionClassForRun(runDetail, freshness, expected, narrativeState, overdueSeconds, activeHandoff) {
453
+ if (activeHandoff && overdueSeconds > 0) return "overdue_handoff";
454
+ if (runDetail.run.status === "blocked") return "blocked";
455
+ if (runDetail.run.status === "running" && (freshness.freshness_class === "stale" || freshness.freshness_class === "stale_acknowledged" || freshness.freshness_class === "archival")) {
456
+ return "stale_running";
457
+ }
458
+ if (narrativeState.needs_narrative) return "missing_narrative";
459
+ if (narrativeState.native_signal_gap) return "native_signal_gap";
460
+ if (expected.expected_next_event) return "active";
461
+ return "observe_only";
462
+ }
463
+
464
+ function baseSignalClassForAttention(attentionClass) {
465
+ if (["blocked", "overdue_handoff", "stale_running", "missing_narrative"].includes(attentionClass)) return "primary";
466
+ if (["active", "native_signal_gap"].includes(attentionClass)) return "secondary";
467
+ return "continuity";
468
+ }
469
+
470
+ function buildAttentionReason(runDetail, attentionClass, narrativeState, freshness, expected, overdueSeconds, currentActorId) {
471
+ if (attentionClass === "blocked") return narrativeState.narrative_reason ?? "Blocked work needs movement.";
472
+ if (attentionClass === "overdue_handoff") return `Handoff is overdue for ${currentActorId ?? "the recipient"}.`;
473
+ if (attentionClass === "stale_running") return `Running work has gone ${freshness.freshness_class.replace(/_/g, " ")} without meaningful updates.`;
474
+ if (attentionClass === "missing_narrative") return narrativeState.narrative_reason ?? "Native narrative is missing.";
475
+ if (attentionClass === "native_signal_gap") return narrativeState.native_signal_gap_reason;
476
+ if (expected.expected_next_event) return `Awaiting ${humanize(expected.expected_next_event)}.`;
477
+ if (runDetail.run.status === "completed") return "Completed; observe for follow-up only.";
478
+ return "No immediate action required.";
479
+ }
480
+
481
+ function buildWhyNow(attentionReason, overdueSeconds, latestMeaningful) {
482
+ if (overdueSeconds > 0) return `${attentionReason} Late by ${durationLabel(overdueSeconds * 1000)}.`;
483
+ if (latestMeaningful) return latestMeaningful.display_summary ?? latestMeaningful.summary ?? attentionReason;
484
+ return attentionReason;
485
+ }
486
+
487
+ function signalConfidence({ latestMeaningful, currentActorId, expectedNextEvent, narrativeState, source }) {
488
+ let confidence = 0.5;
489
+ if (latestMeaningful) confidence += 0.15;
490
+ if (currentActorId) confidence += 0.1;
491
+ if (expectedNextEvent) confidence += 0.1;
492
+ if (!narrativeState.needs_narrative) confidence += 0.05;
493
+ if (source === "native") confidence += 0.05;
494
+ if (source === "derived") confidence -= 0.2;
495
+ if (narrativeState.native_signal_gap) confidence -= 0.1;
496
+ return clamp(Number(confidence.toFixed(2)), 0.1, 0.99);
497
+ }
498
+
499
+ export function deriveRunLiveSignal(runDetail, { now = Date.now() } = {}) {
500
+ const nowMs = typeof now === "string" ? parseTimestamp(now) : now;
501
+ const activityItems = sortByTimestamp(runDetail.activity_items ?? [], (item) => item.timestamp ?? "");
502
+ const meaningfulItems = activityItems.filter(isMeaningfulActivity);
503
+ const processPosts = sortByTimestamp(runDetail.process_posts ?? [], (post) => post.timestamp ?? "");
504
+ const activeHandoff = latestMatching(
505
+ runDetail.handoff_timeline?.items ?? [],
506
+ (item) => item.updated_at ?? item.created_at ?? "",
507
+ (item) => item.status === "open" || item.status === "accepted" || item.status === "blocked"
508
+ );
509
+ const latestMeaningful = meaningfulItems.at(-1);
510
+ const latestNativeMeaningful = meaningfulItems.filter((item) => hasRecordSource(item, "native")).at(-1);
511
+ const latestPartnerMeaningful = meaningfulItems.filter((item) => hasRecordSource(item, "partner")).at(-1);
512
+ const latestNativePost = latestMatching(processPosts, (post) => post.timestamp ?? "", (post) => hasRecordSource(post, "native"));
513
+ const lastStaleAck = latestMatching(processPosts, (post) => post.timestamp ?? "", (post) => post.kind === "stale_ack");
514
+ const lastMeaningfulUpdateAt = latestMeaningful?.timestamp ?? runDetail.run.updated_at;
515
+ const presence = inferPresence(runDetail, meaningfulItems, processPosts, activeHandoff);
516
+ const freshness = classifyFreshness(runDetail, lastMeaningfulUpdateAt, lastStaleAck?.timestamp, activeHandoff, nowMs);
517
+ const narrativeState = deriveNarrativeState({
518
+ runDetail,
519
+ meaningfulItems,
520
+ processPosts,
521
+ activeHandoff,
522
+ freshness,
523
+ currentActorId: presence.current_actor_id,
524
+ lastMeaningfulUpdateAt,
525
+ });
526
+ const expected = determineExpectedNextEvent(runDetail, activeHandoff, narrativeState, freshness, lastMeaningfulUpdateAt);
527
+ const overdueAtMs = parseTimestamp(expected.overdue_at);
528
+ const overdueSeconds = !Number.isNaN(overdueAtMs) && nowMs > overdueAtMs ? Math.round((nowMs - overdueAtMs) / 1000) : 0;
529
+ const attentionClass = attentionClassForRun(runDetail, freshness, expected, narrativeState, overdueSeconds, activeHandoff);
530
+ const baseSignalClass = baseSignalClassForAttention(attentionClass);
531
+ const source = primaryRecordSource(latestMeaningful ?? latestNativePost ?? { record_source: "derived" });
532
+ const reasons = [];
533
+ let attentionScore = BASE_SIGNAL_SCORE[baseSignalClass] ?? 0;
534
+ reasons.push(`signal_class=${baseSignalClass}(${BASE_SIGNAL_SCORE[baseSignalClass] ?? 0})`);
535
+
536
+ const severity = attentionClass === "blocked" || attentionClass === "overdue_handoff" ? 40 : 0;
537
+ attentionScore += severity;
538
+ if (severity) reasons.push(`severity=${attentionClass}(${severity})`);
539
+
540
+ const freshnessModifier = FRESHNESS_SCORE[freshness.freshness_class] ?? 0;
541
+ attentionScore += freshnessModifier;
542
+ reasons.push(`freshness=${freshness.freshness_class}(${freshnessModifier})`);
543
+
544
+ const provenanceModifier = PROVENANCE_SCORE[source] ?? 0;
545
+ attentionScore += provenanceModifier;
546
+ reasons.push(`provenance=${source}(${provenanceModifier})`);
547
+
548
+ const noveltyModifier = latestMeaningful?.collapsed_event_count > 1 ? -12 : 16;
549
+ attentionScore += noveltyModifier;
550
+ reasons.push(`novelty=${latestMeaningful?.collapsed_event_count > 1 ? "repeated" : "new"}(${noveltyModifier})`);
551
+
552
+ const narrativeModifier = narrativeState.needs_narrative ? -14 : latestNativePost ? 10 : 0;
553
+ attentionScore += narrativeModifier;
554
+ reasons.push(`narrative=${narrativeState.needs_narrative ? "missing" : latestNativePost ? "present" : "none"}(${narrativeModifier})`);
555
+
556
+ const ownershipModifier = presence.current_actor_id && expected.expected_next_event ? 8 : -10;
557
+ attentionScore += ownershipModifier;
558
+ reasons.push(`ownership=${presence.current_actor_id && expected.expected_next_event ? "clear" : "missing"}(${ownershipModifier})`);
559
+
560
+ const attentionReason = buildAttentionReason(runDetail, attentionClass, narrativeState, freshness, expected, overdueSeconds, presence.current_actor_id);
561
+ const whyNow = buildWhyNow(attentionReason, overdueSeconds, latestMeaningful);
562
+ const lastHumanNote = latestMatching(processPosts, (post) => post.timestamp ?? "", (post) => isHumanNote(post));
563
+ const lastAgentNote = latestMatching(processPosts, (post) => post.timestamp ?? "", (post) => !isHumanNote(post));
564
+
565
+ return {
566
+ attention_class: attentionClass,
567
+ attention_reason: attentionReason,
568
+ attention_score: clamp(attentionScore, 0, 200),
569
+ attention_rank: 0,
570
+ why_now: whyNow,
571
+ expected_next_event: expected.expected_next_event,
572
+ overdue_at: expected.overdue_at,
573
+ overdue_seconds: overdueSeconds,
574
+ escalation_state: overdueSeconds > 0 ? "late" : freshness.freshness_class === "aging" ? "watch" : "clear",
575
+ highlight_reason: attentionReason,
576
+ recommended_owner_action: expected.recommended_owner_action,
577
+ recommended_post_kind: narrativeState.recommended_post_kind,
578
+ recommended_summary_template: narrativeState.recommended_summary_template,
579
+ next_best_update_kind: firstDefined(narrativeState.recommended_post_kind, expected.expected_next_event, "observe_only"),
580
+ needs_narrative: narrativeState.needs_narrative,
581
+ native_signal_gap: narrativeState.native_signal_gap,
582
+ native_signal_gap_reason: narrativeState.native_signal_gap_reason,
583
+ native_signal_ratio: narrativeState.native_signal_ratio,
584
+ native_narrative_gap: narrativeState.native_narrative_gap,
585
+ partner_dependency_class: narrativeState.partner_dependency_class,
586
+ freshness_class: freshness.freshness_class,
587
+ freshness_reason: freshness.freshness_reason,
588
+ stale_since: freshness.stale_since,
589
+ needs_stale_ack: freshness.needs_stale_ack,
590
+ silence_owner: presence.waiting_on_actor_id ?? presence.current_actor_id,
591
+ silence_explainer: overdueSeconds > 0 ? `Expected ${humanize(expected.expected_next_event)} is late.` : freshness.freshness_reason,
592
+ current_actor_id: presence.current_actor_id,
593
+ waiting_on_actor_id: presence.waiting_on_actor_id,
594
+ acknowledgement_state: freshness.freshness_class === "stale_acknowledged" ? "stale_acknowledged" : presence.acknowledgement_state,
595
+ last_response_at: presence.last_response_at,
596
+ presence_source: presence.presence_source,
597
+ last_meaningful_update_at: lastMeaningfulUpdateAt,
598
+ last_native_update_at: firstDefined(latestNativeMeaningful?.timestamp, latestNativePost?.timestamp),
599
+ last_partner_update_at: latestPartnerMeaningful?.timestamp,
600
+ last_human_note_at: lastHumanNote?.timestamp,
601
+ last_agent_note_at: lastAgentNote?.timestamp,
602
+ signal_confidence: signalConfidence({
603
+ latestMeaningful,
604
+ currentActorId: presence.current_actor_id,
605
+ expectedNextEvent: expected.expected_next_event,
606
+ narrativeState,
607
+ source,
608
+ }),
609
+ score_reasons: reasons,
610
+ should_interrupt_agent: clamp(attentionScore, 0, 200) >= 140 || attentionClass === "overdue_handoff",
611
+ };
612
+ }
613
+
614
+ export function compareRunsByAttention(left, right) {
615
+ const leftSignal = left.live_signal ?? {};
616
+ const rightSignal = right.live_signal ?? {};
617
+ const leftScore = leftSignal.attention_score ?? 0;
618
+ const rightScore = rightSignal.attention_score ?? 0;
619
+ if (leftScore !== rightScore) return rightScore - leftScore;
620
+ const leftMeaningful = parseTimestamp(leftSignal.last_meaningful_update_at ?? left.run.updated_at);
621
+ const rightMeaningful = parseTimestamp(rightSignal.last_meaningful_update_at ?? right.run.updated_at);
622
+ if (leftMeaningful !== rightMeaningful) return rightMeaningful - leftMeaningful;
623
+ const provenanceDelta = (PROVENANCE_SCORE[primaryRecordSource({ record_source: rightSignal.last_native_update_at ? "native" : rightSignal.last_partner_update_at ? "partner" : "derived" })] ?? 0) -
624
+ (PROVENANCE_SCORE[primaryRecordSource({ record_source: leftSignal.last_native_update_at ? "native" : leftSignal.last_partner_update_at ? "partner" : "derived" })] ?? 0);
625
+ if (provenanceDelta !== 0) return provenanceDelta;
626
+ return String(left.run.run_id).localeCompare(String(right.run.run_id));
627
+ }