@lcv-ideas-software/cross-review 4.2.4 → 4.2.5
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/CHANGELOG.md +30 -0
- package/README.md +8 -1
- package/dist/scripts/smoke.js +162 -0
- package/dist/scripts/smoke.js.map +1 -1
- package/dist/src/core/config.d.ts +1 -1
- package/dist/src/core/config.js +1 -1
- package/dist/src/core/orchestrator.js +12 -2
- package/dist/src/core/orchestrator.js.map +1 -1
- package/dist/src/core/reports.d.ts +6 -0
- package/dist/src/core/reports.js +74 -4
- package/dist/src/core/reports.js.map +1 -1
- package/dist/src/core/session-store.d.ts +1 -0
- package/dist/src/core/session-store.js +154 -16
- package/dist/src/core/session-store.js.map +1 -1
- package/dist/src/core/types.d.ts +14 -0
- package/docs/apresentacao-cross-review.md +28 -27
- package/docs/apresentacao.md +18 -17
- package/docs/architecture.md +17 -1
- package/docs/costs.md +6 -0
- package/docs/evidence-preflight.md +3 -2
- package/package.json +1 -1
package/dist/src/core/reports.js
CHANGED
|
@@ -3,9 +3,78 @@ function valueOrDash(value) {
|
|
|
3
3
|
return "-";
|
|
4
4
|
return String(value);
|
|
5
5
|
}
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
function moneyText(value, currency = "USD") {
|
|
7
|
+
return value == null ? "unknown" : `$${value.toFixed(6)} ${currency}`;
|
|
8
|
+
}
|
|
9
|
+
function moneyAmountText(value) {
|
|
10
|
+
return `$${value.toFixed(6)}`;
|
|
11
|
+
}
|
|
12
|
+
export function sessionCostBreakdown(session) {
|
|
13
|
+
const currency = session.totals.cost.currency ?? "USD";
|
|
14
|
+
let peerTotal = 0;
|
|
15
|
+
let peerSeen = false;
|
|
16
|
+
for (const round of session.rounds) {
|
|
17
|
+
for (const peer of round.peers) {
|
|
18
|
+
const value = peer.cost?.total_cost;
|
|
19
|
+
if (value == null || !Number.isFinite(value))
|
|
20
|
+
continue;
|
|
21
|
+
peerSeen = true;
|
|
22
|
+
peerTotal += value;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
let generationTotal = 0;
|
|
26
|
+
let generationSeen = false;
|
|
27
|
+
for (const generation of session.generation_files ?? []) {
|
|
28
|
+
const value = generation.cost?.total_cost;
|
|
29
|
+
if (value == null || !Number.isFinite(value))
|
|
30
|
+
continue;
|
|
31
|
+
generationSeen = true;
|
|
32
|
+
generationTotal += value;
|
|
33
|
+
}
|
|
34
|
+
const total = session.totals.cost.total_cost ?? null;
|
|
35
|
+
return {
|
|
36
|
+
currency,
|
|
37
|
+
total,
|
|
38
|
+
peer_total: peerSeen ? peerTotal : null,
|
|
39
|
+
generation_total: generationSeen ? generationTotal : null,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function costSummaryLines(session) {
|
|
43
|
+
const breakdown = sessionCostBreakdown(session);
|
|
44
|
+
const lines = [
|
|
45
|
+
`- Cost: ${moneyText(breakdown.total, breakdown.currency)}`,
|
|
46
|
+
`- Peer call cost: ${moneyText(breakdown.peer_total, breakdown.currency)}`,
|
|
47
|
+
`- Generation cost: ${moneyText(breakdown.generation_total, breakdown.currency)}`,
|
|
48
|
+
];
|
|
49
|
+
if (breakdown.total != null &&
|
|
50
|
+
breakdown.peer_total != null &&
|
|
51
|
+
breakdown.generation_total != null) {
|
|
52
|
+
lines.push(`- Cost reconciliation: ${moneyText(breakdown.total, breakdown.currency)} = ${moneyAmountText(breakdown.peer_total)} peer + ${moneyAmountText(breakdown.generation_total)} generation`);
|
|
53
|
+
}
|
|
54
|
+
return lines;
|
|
55
|
+
}
|
|
56
|
+
function evidenceChecklistLines(session) {
|
|
57
|
+
const checklist = session.evidence_checklist ?? [];
|
|
58
|
+
if (!checklist.length)
|
|
59
|
+
return [];
|
|
60
|
+
const counts = new Map();
|
|
61
|
+
for (const item of checklist) {
|
|
62
|
+
const status = item.status ?? "open";
|
|
63
|
+
counts.set(status, (counts.get(status) ?? 0) + 1);
|
|
64
|
+
}
|
|
65
|
+
const lines = ["## Evidence Checklist", ""];
|
|
66
|
+
for (const [status, count] of [...counts.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
67
|
+
lines.push(`- ${status}: ${count}`);
|
|
68
|
+
}
|
|
69
|
+
const notResurfaced = checklist.filter((item) => item.status === "not_resurfaced");
|
|
70
|
+
if (notResurfaced.length) {
|
|
71
|
+
lines.push("- not_resurfaced means the ask was not repeated; it is not proof that evidence was satisfied.");
|
|
72
|
+
for (const item of notResurfaced.slice(0, 10)) {
|
|
73
|
+
lines.push(` - ${item.peer}/${item.id}: ${item.ask}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
lines.push("");
|
|
77
|
+
return lines;
|
|
9
78
|
}
|
|
10
79
|
export function sessionReportMarkdown(session, events = []) {
|
|
11
80
|
const latestRound = session.rounds.at(-1);
|
|
@@ -22,7 +91,7 @@ export function sessionReportMarkdown(session, events = []) {
|
|
|
22
91
|
`- Outcome reason: ${valueOrDash(session.outcome_reason)}`,
|
|
23
92
|
`- Health: ${valueOrDash(session.convergence_health?.state)} - ${valueOrDash(session.convergence_health?.detail)}`,
|
|
24
93
|
`- Rounds: ${session.rounds.length}`,
|
|
25
|
-
|
|
94
|
+
...costSummaryLines(session),
|
|
26
95
|
`- Total tokens: ${valueOrDash(session.totals.usage.total_tokens)}`,
|
|
27
96
|
"",
|
|
28
97
|
"## Task",
|
|
@@ -46,6 +115,7 @@ export function sessionReportMarkdown(session, events = []) {
|
|
|
46
115
|
"## Peer Decisions",
|
|
47
116
|
"",
|
|
48
117
|
];
|
|
118
|
+
lines.push(...evidenceChecklistLines(session));
|
|
49
119
|
if (session.generation_files?.length) {
|
|
50
120
|
lines.push("## Generations", "");
|
|
51
121
|
for (const generation of session.generation_files) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reports.js","sourceRoot":"","sources":["../../../src/core/reports.ts"],"names":[],"mappings":"AAEA,SAAS,WAAW,CAAC,KAAc;IACjC,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,GAAG,CAAC;IAC9C,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"reports.js","sourceRoot":"","sources":["../../../src/core/reports.ts"],"names":[],"mappings":"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"}
|
|
@@ -25,6 +25,7 @@ export declare class SessionStore {
|
|
|
25
25
|
readTextArtifact(sessionId: string, relativePath: string, maxChars: number): string;
|
|
26
26
|
private peekNextSeq;
|
|
27
27
|
private commitSeq;
|
|
28
|
+
private appendEventRecord;
|
|
28
29
|
appendEvent(event: RuntimeEvent): Promise<void>;
|
|
29
30
|
flushPendingEvents(): Promise<void>;
|
|
30
31
|
readEvents(sessionId: string, sinceSeq?: number): SessionEvent[];
|
|
@@ -9,6 +9,45 @@ export const SWEEP_MIN_IDLE_MS = 24 * 60 * 60 * 1000;
|
|
|
9
9
|
function now() {
|
|
10
10
|
return new Date().toISOString();
|
|
11
11
|
}
|
|
12
|
+
function isStubSession(session) {
|
|
13
|
+
const peerCosts = session.rounds.flatMap((round) => round.peers.map((peer) => peer.cost));
|
|
14
|
+
const generationCosts = (session.generation_files ?? []).map((generation) => generation.cost);
|
|
15
|
+
const costs = [...peerCosts, ...generationCosts].filter(Boolean);
|
|
16
|
+
if (costs.length > 0)
|
|
17
|
+
return costs.every((cost) => cost?.source === "stub");
|
|
18
|
+
return session.capability_snapshot.some((probe) => probe.provider.startsWith("stub-") || probe.model.startsWith("stub-"));
|
|
19
|
+
}
|
|
20
|
+
function sessionPeerCostTotal(session) {
|
|
21
|
+
let total = 0;
|
|
22
|
+
let seen = false;
|
|
23
|
+
for (const round of session.rounds) {
|
|
24
|
+
for (const peer of round.peers) {
|
|
25
|
+
const value = peer.cost?.total_cost;
|
|
26
|
+
if (value == null || !Number.isFinite(value))
|
|
27
|
+
continue;
|
|
28
|
+
seen = true;
|
|
29
|
+
total += value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return seen ? total : null;
|
|
33
|
+
}
|
|
34
|
+
function sessionGenerationCostTotal(session) {
|
|
35
|
+
let total = 0;
|
|
36
|
+
let seen = false;
|
|
37
|
+
for (const generation of session.generation_files ?? []) {
|
|
38
|
+
const value = generation.cost?.total_cost;
|
|
39
|
+
if (value == null || !Number.isFinite(value))
|
|
40
|
+
continue;
|
|
41
|
+
seen = true;
|
|
42
|
+
total += value;
|
|
43
|
+
}
|
|
44
|
+
return seen ? total : null;
|
|
45
|
+
}
|
|
46
|
+
function addNullableCost(a, b) {
|
|
47
|
+
if (a == null && b == null)
|
|
48
|
+
return null;
|
|
49
|
+
return (a ?? 0) + (b ?? 0);
|
|
50
|
+
}
|
|
12
51
|
// v2.4.0 / audit closure (P1.3): atomicWriteFile retry on Windows.
|
|
13
52
|
// `fs.renameSync` in Win32 fails with EPERM/EACCES/EBUSY when the
|
|
14
53
|
// destination is briefly held by another handle (AV scan, indexing,
|
|
@@ -392,6 +431,16 @@ export class SessionStore {
|
|
|
392
431
|
commitSeq(sessionId, committed) {
|
|
393
432
|
this.seqCache.set(sessionId, committed);
|
|
394
433
|
}
|
|
434
|
+
appendEventRecord(event) {
|
|
435
|
+
const sessionId = event.session_id;
|
|
436
|
+
if (!sessionId)
|
|
437
|
+
return;
|
|
438
|
+
const file = this.eventsPath(sessionId);
|
|
439
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
440
|
+
const seq = this.peekNextSeq(sessionId, file);
|
|
441
|
+
fs.appendFileSync(file, `${JSON.stringify({ ...event, seq, ts: event.ts ?? now() })}\n`, "utf8");
|
|
442
|
+
this.commitSeq(sessionId, seq);
|
|
443
|
+
}
|
|
395
444
|
// v4.1.0: durable event persistence. withSessionLock became async
|
|
396
445
|
// with the proper-lockfile refactor; appendEvent awaits the lock so
|
|
397
446
|
// callers that read events after persisting get the expected
|
|
@@ -405,13 +454,11 @@ export class SessionStore {
|
|
|
405
454
|
const write = (async () => {
|
|
406
455
|
try {
|
|
407
456
|
await this.withSessionLock(sessionId, () => {
|
|
408
|
-
const file = this.eventsPath(sessionId);
|
|
409
|
-
const seq = this.peekNextSeq(sessionId, file);
|
|
410
|
-
fs.appendFileSync(file, `${JSON.stringify({ ...event, seq, ts: event.ts ?? now() })}\n`, "utf8");
|
|
411
457
|
// Only commit the cache AFTER the durable append succeeded.
|
|
412
|
-
// If appendFileSync threw
|
|
413
|
-
// last persisted seq and the next call
|
|
414
|
-
this
|
|
458
|
+
// If appendFileSync threw inside appendEventRecord, the cache
|
|
459
|
+
// still reflects the last persisted seq and the next call
|
|
460
|
+
// reuses this seq number.
|
|
461
|
+
this.appendEventRecord(event);
|
|
415
462
|
});
|
|
416
463
|
}
|
|
417
464
|
catch {
|
|
@@ -730,13 +777,26 @@ export class SessionStore {
|
|
|
730
777
|
if (reason)
|
|
731
778
|
meta.outcome_reason = reason;
|
|
732
779
|
delete meta.in_flight;
|
|
780
|
+
const ts = now();
|
|
733
781
|
meta.convergence_health = {
|
|
734
782
|
state: outcome === "converged" ? "converged" : outcome === "max-rounds" ? "blocked" : "stale",
|
|
735
|
-
last_event_at:
|
|
783
|
+
last_event_at: ts,
|
|
736
784
|
detail: reason ?? outcome,
|
|
737
785
|
};
|
|
738
|
-
meta.updated_at =
|
|
786
|
+
meta.updated_at = ts;
|
|
739
787
|
await writeJson(this.metaPath(sessionId), meta);
|
|
788
|
+
try {
|
|
789
|
+
this.appendEventRecord({
|
|
790
|
+
type: "session.finalized",
|
|
791
|
+
session_id: sessionId,
|
|
792
|
+
ts,
|
|
793
|
+
message: `Session finalized as ${outcome}${reason ? `: ${reason}` : ""}`,
|
|
794
|
+
data: { outcome, reason: reason ?? null },
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
catch {
|
|
798
|
+
/* event persistence is best-effort; session_doctor will flag gaps */
|
|
799
|
+
}
|
|
740
800
|
return meta;
|
|
741
801
|
});
|
|
742
802
|
}
|
|
@@ -763,6 +823,7 @@ export class SessionStore {
|
|
|
763
823
|
async markCancelled(sessionId, reason = "cancelled") {
|
|
764
824
|
return this.withSessionLock(sessionId, async () => {
|
|
765
825
|
const meta = this.read(sessionId);
|
|
826
|
+
const ts = now();
|
|
766
827
|
meta.outcome = "aborted";
|
|
767
828
|
meta.outcome_reason = reason;
|
|
768
829
|
delete meta.in_flight;
|
|
@@ -771,15 +832,27 @@ export class SessionStore {
|
|
|
771
832
|
reason,
|
|
772
833
|
job_id: meta.control?.job_id,
|
|
773
834
|
requested_at: meta.control?.requested_at,
|
|
774
|
-
updated_at:
|
|
835
|
+
updated_at: ts,
|
|
775
836
|
};
|
|
776
837
|
meta.convergence_health = {
|
|
777
838
|
state: "stale",
|
|
778
|
-
last_event_at:
|
|
839
|
+
last_event_at: ts,
|
|
779
840
|
detail: reason,
|
|
780
841
|
};
|
|
781
|
-
meta.updated_at =
|
|
842
|
+
meta.updated_at = ts;
|
|
782
843
|
await writeJson(this.metaPath(sessionId), meta);
|
|
844
|
+
try {
|
|
845
|
+
this.appendEventRecord({
|
|
846
|
+
type: "session.cancelled",
|
|
847
|
+
session_id: sessionId,
|
|
848
|
+
ts,
|
|
849
|
+
message: `Session cancelled: ${reason}`,
|
|
850
|
+
data: { outcome: "aborted", reason },
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
catch {
|
|
854
|
+
/* event persistence is best-effort; session_doctor will flag gaps */
|
|
855
|
+
}
|
|
783
856
|
return meta;
|
|
784
857
|
});
|
|
785
858
|
}
|
|
@@ -1349,11 +1422,19 @@ export class SessionStore {
|
|
|
1349
1422
|
const maxRoundsSessions = [];
|
|
1350
1423
|
const selfLeadMetadata = [];
|
|
1351
1424
|
const openEvidenceSessions = [];
|
|
1425
|
+
const notResurfacedEvidenceSessions = [];
|
|
1352
1426
|
const grokProviderErrorSessions = [];
|
|
1353
1427
|
const eventReadErrorSessions = [];
|
|
1428
|
+
const terminalEventMissingSessions = [];
|
|
1354
1429
|
let eventsTotal = 0;
|
|
1355
1430
|
let tokenDeltaEvents = 0;
|
|
1356
1431
|
let tokenCompletedEvents = 0;
|
|
1432
|
+
let realSessions = 0;
|
|
1433
|
+
let stubSessions = 0;
|
|
1434
|
+
let peerCallCostUsd = null;
|
|
1435
|
+
let generationCostUsd = null;
|
|
1436
|
+
let totalCostUsd = null;
|
|
1437
|
+
let terminalEventMissingCount = 0;
|
|
1357
1438
|
const pushLimited = (target, entry) => {
|
|
1358
1439
|
if (target.length < cappedLimit)
|
|
1359
1440
|
target.push(entry);
|
|
@@ -1365,7 +1446,18 @@ export class SessionStore {
|
|
|
1365
1446
|
const evidenceList = session.evidence_checklist ?? [];
|
|
1366
1447
|
const openEvidenceItemsList = evidenceList.filter((item) => (item.status ?? "open") === "open");
|
|
1367
1448
|
const openEvidenceItems = openEvidenceItemsList.length;
|
|
1449
|
+
const notResurfacedEvidenceItems = evidenceList.filter((item) => item.status === "not_resurfaced").length;
|
|
1368
1450
|
const grokProviderErrors = (session.failed_attempts ?? []).filter((failure) => failure.peer === "grok" && failure.failure_class === "provider_error").length;
|
|
1451
|
+
if (isStubSession(session))
|
|
1452
|
+
stubSessions += 1;
|
|
1453
|
+
else
|
|
1454
|
+
realSessions += 1;
|
|
1455
|
+
peerCallCostUsd = addNullableCost(peerCallCostUsd, sessionPeerCostTotal(session));
|
|
1456
|
+
generationCostUsd = addNullableCost(generationCostUsd, sessionGenerationCostTotal(session));
|
|
1457
|
+
const sessionTotalCost = session.totals.cost.total_cost;
|
|
1458
|
+
if (sessionTotalCost != null && Number.isFinite(sessionTotalCost)) {
|
|
1459
|
+
totalCostUsd = addNullableCost(totalCostUsd, sessionTotalCost);
|
|
1460
|
+
}
|
|
1369
1461
|
const entry = {
|
|
1370
1462
|
session_id: session.session_id,
|
|
1371
1463
|
version: session.version,
|
|
@@ -1379,6 +1471,9 @@ export class SessionStore {
|
|
|
1379
1471
|
rounds: session.rounds.length,
|
|
1380
1472
|
updated_at: session.updated_at,
|
|
1381
1473
|
...(openEvidenceItems > 0 ? { open_evidence_items: openEvidenceItems } : {}),
|
|
1474
|
+
...(notResurfacedEvidenceItems > 0
|
|
1475
|
+
? { not_resurfaced_evidence_items: notResurfacedEvidenceItems }
|
|
1476
|
+
: {}),
|
|
1382
1477
|
...(grokProviderErrors > 0 ? { grok_provider_errors: grokProviderErrors } : {}),
|
|
1383
1478
|
};
|
|
1384
1479
|
// v2.22.0 (B.P2): drill-down for open-evidence entries. Aggregate
|
|
@@ -1419,6 +1514,8 @@ export class SessionStore {
|
|
|
1419
1514
|
pushLimited(selfLeadMetadata, entry);
|
|
1420
1515
|
if (openEvidenceItems > 0)
|
|
1421
1516
|
pushLimited(openEvidenceSessions, entry);
|
|
1517
|
+
if (notResurfacedEvidenceItems > 0)
|
|
1518
|
+
pushLimited(notResurfacedEvidenceSessions, entry);
|
|
1422
1519
|
if (grokProviderErrors > 0)
|
|
1423
1520
|
pushLimited(grokProviderErrorSessions, entry);
|
|
1424
1521
|
let sessionEvents = [];
|
|
@@ -1429,6 +1526,18 @@ export class SessionStore {
|
|
|
1429
1526
|
entry.event_read_error = redact(error instanceof Error ? error.message : String(error));
|
|
1430
1527
|
pushLimited(eventReadErrorSessions, entry);
|
|
1431
1528
|
}
|
|
1529
|
+
if (session.outcome) {
|
|
1530
|
+
const expectedTerminalEvent = session.control?.status === "cancelled" || session.outcome_reason === "session_cancelled"
|
|
1531
|
+
? "session.cancelled"
|
|
1532
|
+
: "session.finalized";
|
|
1533
|
+
const hasExpectedTerminalEvent = sessionEvents.some((event) => event.type === expectedTerminalEvent);
|
|
1534
|
+
if (!hasExpectedTerminalEvent) {
|
|
1535
|
+
terminalEventMissingCount += 1;
|
|
1536
|
+
entry.terminal_event_missing = true;
|
|
1537
|
+
entry.terminal_event_expected = expectedTerminalEvent;
|
|
1538
|
+
pushLimited(terminalEventMissingSessions, entry);
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1432
1541
|
for (const event of sessionEvents) {
|
|
1433
1542
|
eventsTotal += 1;
|
|
1434
1543
|
if (event.type === "peer.token.delta")
|
|
@@ -1464,6 +1573,9 @@ export class SessionStore {
|
|
|
1464
1573
|
if (openEvidenceSessions.length > 0) {
|
|
1465
1574
|
recommendations.push("Address or explicitly terminal-mark open evidence checklist items before expecting convergence.");
|
|
1466
1575
|
}
|
|
1576
|
+
if (notResurfacedEvidenceSessions.length > 0) {
|
|
1577
|
+
recommendations.push("`not_resurfaced` evidence items are inference-only; review them separately from satisfied/deferred/rejected items.");
|
|
1578
|
+
}
|
|
1467
1579
|
if (grokProviderErrorSessions.length > 0) {
|
|
1468
1580
|
recommendations.push("Run a Grok-specific smoke/probe for sessions with grok provider errors before relying on Grok in release gates.");
|
|
1469
1581
|
}
|
|
@@ -1473,21 +1585,32 @@ export class SessionStore {
|
|
|
1473
1585
|
if (eventsTotal > 0 && tokenDeltaEvents / eventsTotal > 0.5) {
|
|
1474
1586
|
recommendations.push("Token delta events dominate this corpus; increase CROSS_REVIEW_TOKEN_DELTA_CHARS_THRESHOLD or disable token streaming for low-noise audits.");
|
|
1475
1587
|
}
|
|
1588
|
+
if (terminalEventMissingCount > 0) {
|
|
1589
|
+
recommendations.push("Terminal outcome metadata exists without matching terminal events; treat as legacy/event-gap evidence and inspect before relying on event-only analytics.");
|
|
1590
|
+
}
|
|
1476
1591
|
return {
|
|
1477
1592
|
generated_at: now(),
|
|
1478
1593
|
scope: "all",
|
|
1479
1594
|
limit: cappedLimit,
|
|
1480
1595
|
totals: {
|
|
1481
1596
|
sessions: sessions.length,
|
|
1597
|
+
real_sessions: realSessions,
|
|
1598
|
+
stub_sessions: stubSessions,
|
|
1482
1599
|
open: sessions.filter((session) => !session.outcome).length,
|
|
1483
|
-
stale: sessions.filter((session) => session.convergence_health?.state === "stale").length,
|
|
1484
|
-
blocked: sessions.filter((session) => session.convergence_health?.state === "blocked")
|
|
1485
|
-
.length,
|
|
1600
|
+
stale: sessions.filter((session) => !session.outcome && session.convergence_health?.state === "stale").length,
|
|
1601
|
+
blocked: sessions.filter((session) => !session.outcome && session.convergence_health?.state === "blocked").length,
|
|
1486
1602
|
max_rounds: sessions.filter((session) => session.outcome === "max-rounds").length,
|
|
1487
1603
|
self_lead_metadata: selfLeadCount,
|
|
1488
1604
|
open_evidence_sessions: sessions.filter((session) => (session.evidence_checklist ?? []).some((item) => (item.status ?? "open") === "open")).length,
|
|
1605
|
+
not_resurfaced_evidence_sessions: sessions.filter((session) => (session.evidence_checklist ?? []).some((item) => item.status === "not_resurfaced")).length,
|
|
1489
1606
|
grok_provider_error_sessions: sessions.filter((session) => (session.failed_attempts ?? []).some((failure) => failure.peer === "grok" && failure.failure_class === "provider_error")).length,
|
|
1490
1607
|
event_read_error_sessions: eventReadErrorSessions.length,
|
|
1608
|
+
terminal_event_missing_sessions: terminalEventMissingCount,
|
|
1609
|
+
},
|
|
1610
|
+
cost_breakdown: {
|
|
1611
|
+
total_cost_usd: totalCostUsd,
|
|
1612
|
+
peer_call_cost_usd: peerCallCostUsd,
|
|
1613
|
+
generation_cost_usd: generationCostUsd,
|
|
1491
1614
|
},
|
|
1492
1615
|
findings: {
|
|
1493
1616
|
open_sessions: openSessions,
|
|
@@ -1499,8 +1622,10 @@ export class SessionStore {
|
|
|
1499
1622
|
// in `totals.self_lead_metadata`.
|
|
1500
1623
|
self_lead_metadata: includeLegacy ? selfLeadMetadata : [],
|
|
1501
1624
|
open_evidence_sessions: openEvidenceSessions,
|
|
1625
|
+
not_resurfaced_evidence_sessions: notResurfacedEvidenceSessions,
|
|
1502
1626
|
grok_provider_error_sessions: grokProviderErrorSessions,
|
|
1503
1627
|
event_read_error_sessions: eventReadErrorSessions,
|
|
1628
|
+
terminal_event_missing_sessions: terminalEventMissingSessions,
|
|
1504
1629
|
},
|
|
1505
1630
|
event_noise: {
|
|
1506
1631
|
events_total: eventsTotal,
|
|
@@ -1793,17 +1918,30 @@ export class SessionStore {
|
|
|
1793
1918
|
continue;
|
|
1794
1919
|
const finalized = await this.withSessionLock(session.session_id, async () => {
|
|
1795
1920
|
const current = this.read(session.session_id);
|
|
1921
|
+
const ts = now();
|
|
1796
1922
|
current.outcome = outcome;
|
|
1797
1923
|
current.outcome_reason = reason;
|
|
1798
1924
|
delete current.in_flight;
|
|
1799
1925
|
current.convergence_health = {
|
|
1800
1926
|
state: "stale",
|
|
1801
|
-
last_event_at:
|
|
1927
|
+
last_event_at: ts,
|
|
1802
1928
|
detail: reason,
|
|
1803
1929
|
idle_ms: idleFor,
|
|
1804
1930
|
};
|
|
1805
|
-
current.updated_at =
|
|
1931
|
+
current.updated_at = ts;
|
|
1806
1932
|
await writeJson(this.metaPath(session.session_id), current);
|
|
1933
|
+
try {
|
|
1934
|
+
this.appendEventRecord({
|
|
1935
|
+
type: "session.finalized",
|
|
1936
|
+
session_id: session.session_id,
|
|
1937
|
+
ts,
|
|
1938
|
+
message: `Session finalized as ${outcome}${reason ? `: ${reason}` : ""}`,
|
|
1939
|
+
data: { outcome, reason, idle_ms: idleFor },
|
|
1940
|
+
});
|
|
1941
|
+
}
|
|
1942
|
+
catch {
|
|
1943
|
+
/* event persistence is best-effort; session_doctor will flag gaps */
|
|
1944
|
+
}
|
|
1807
1945
|
return current;
|
|
1808
1946
|
});
|
|
1809
1947
|
swept.push(finalized);
|