dsh-llm-verifier 0.6.0 → 0.6.2

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 (61) hide show
  1. package/README.md +3 -0
  2. package/lib/{auto-DAMeKRkK.js → auto-caXDU2pw.js} +19 -1
  3. package/lib/{auto-DAMeKRkK.js.map → auto-caXDU2pw.js.map} +1 -1
  4. package/lib/client.js +493 -331
  5. package/lib/client.js.map +1 -1
  6. package/lib/index.js +201 -52
  7. package/lib/index.js.map +1 -1
  8. package/lib/replay.js +1 -1
  9. package/lib/types/client-i18n.d.ts +36 -11
  10. package/lib/types/client-i18n.d.ts.map +1 -1
  11. package/lib/types/client-i18n.js +50 -9
  12. package/lib/types/client-i18n.js.map +1 -1
  13. package/lib/types/client.d.ts +17 -7
  14. package/lib/types/client.d.ts.map +1 -1
  15. package/lib/types/client.js +165 -44
  16. package/lib/types/client.js.map +1 -1
  17. package/lib/types/decisions.d.ts +8 -0
  18. package/lib/types/decisions.d.ts.map +1 -1
  19. package/lib/types/decisions.js +13 -1
  20. package/lib/types/decisions.js.map +1 -1
  21. package/lib/types/index.d.ts.map +1 -1
  22. package/lib/types/index.js +47 -31
  23. package/lib/types/index.js.map +1 -1
  24. package/lib/types/process-selection.d.ts +27 -5
  25. package/lib/types/process-selection.d.ts.map +1 -1
  26. package/lib/types/process-selection.js +17 -8
  27. package/lib/types/process-selection.js.map +1 -1
  28. package/lib/types/router.d.ts +22 -0
  29. package/lib/types/router.d.ts.map +1 -1
  30. package/lib/types/router.js +20 -0
  31. package/lib/types/router.js.map +1 -1
  32. package/lib/types/statistics.d.ts +8 -0
  33. package/lib/types/statistics.d.ts.map +1 -1
  34. package/lib/types/statistics.js +1 -1
  35. package/lib/types/statistics.js.map +1 -1
  36. package/lib/types/verifier-activity.d.ts +189 -0
  37. package/lib/types/verifier-activity.d.ts.map +1 -0
  38. package/lib/types/verifier-activity.js +223 -0
  39. package/lib/types/verifier-activity.js.map +1 -0
  40. package/package.json +1 -1
  41. package/src/client-i18n.ts +55 -16
  42. package/src/client.test.ts +101 -16
  43. package/src/client.tsx +238 -103
  44. package/src/decisions.test.ts +16 -0
  45. package/src/decisions.ts +22 -1
  46. package/src/index.test.ts +32 -0
  47. package/src/index.ts +48 -33
  48. package/src/process-selection.test.ts +13 -0
  49. package/src/process-selection.ts +39 -14
  50. package/src/router.test.ts +46 -0
  51. package/src/router.ts +31 -0
  52. package/src/statistics.test.ts +16 -0
  53. package/src/statistics.ts +9 -1
  54. package/src/verifier-activity.test.ts +164 -0
  55. package/src/verifier-activity.ts +321 -0
  56. package/lib/types/process-activity.d.ts +0 -109
  57. package/lib/types/process-activity.d.ts.map +0 -1
  58. package/lib/types/process-activity.js +0 -135
  59. package/lib/types/process-activity.js.map +0 -1
  60. package/src/process-activity.test.ts +0 -91
  61. package/src/process-activity.ts +0 -177
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { CRITERIA_PRESETS, CRITERIA_PRESET_IDS, DEFAULT_CRITERIA, DEFAULT_GROUND_TRUTH_NOTE, EMPTY_WORK_BASELINE, GRANULARITY, LETTERS, MAX_DIAGNOSTICS, MAX_DIAGNOSTIC_ACTION_CHARS, MAX_DIAGNOSTIC_CHARS, PROCESS_CRITERIA, PROPOSAL_CRITERIA, SCALE_DESCRIPTION, UNTRUSTED_EVIDENCE_NOTE, accumulatePairs, bradleyTerry, buildFindingContract, buildGenerationPrompt, buildPairwisePrompt, buildProgressPrompt, dedupeCriterionId, diagnosticKey, evidenceNonce, extractProgressScore, extractScore, normalizeScoreLetter, parseCriteriaMarkdown, parseDiagnostics, pivotRoundPairs, rankScores, renderDelimitedBlock, renderDiagnostics, renderReferenceContext, ringCycle, seededRandom, slugCriterionId, swapDiagnosticEvidence, topPivots } from "./core.js";
2
2
  import { a as attachUsage, c as emptyUsage, f as partialUsage, g as resolveCapabilityFile, h as TopLogprobCapabilityCache, i as addUsage, m as requestAttempts, o as callVerifier, p as predictScoringChannel, r as RequestLimiter, s as callVerifierText, u as generateCandidate } from "./caller-BUK7OgUs.js";
3
- import { A as verificationFailed, C as latestDirectUserSeq, D as semanticDecision, E as routedRepeats, F as ScoreCache, I as SingleFlight, L as resolveCacheFile, M as extractSession, N as sanitizeVerifierText, O as semanticReferencesVisible, P as sessionEvents, R as stableHash, S as itemBudget, T as parseSemanticRoute, _ as buildSemanticRoutePrompt, a as compareRouteFeedbackDetail, b as inspectDeliveryPhase, c as selectRouteFeedbackDetail, d as AutoVerifierRouter, f as MAX_ROUTED_CHECKPOINTS, g as buildEvidenceIndex, h as boundDecision, i as automaticFeedback, j as verificationVerdict, k as semanticRouteHint, l as sessionAccepted, m as analyzeStructuredRoute, n as PROPOSAL_FEEDBACK_NOTE, o as failedAcceptanceCriteria, p as RECOVERY_FAILURE_CONTEXT_CHARS, r as analyzeAutoTask, s as isSubagentSession, t as MAX_ROUTE_FEEDBACK_CHARS, u as topScoreIndices, v as buildSemanticRouteView, w as nextDiagnosticCycleId, x as inspectRecoverySignal, y as estimateRoutedCalls } from "./auto-DAMeKRkK.js";
3
+ import { A as verificationFailed, C as latestDirectUserSeq, D as semanticDecision, E as routedRepeats, F as ScoreCache, I as SingleFlight, L as resolveCacheFile, M as extractSession, N as sanitizeVerifierText, O as semanticReferencesVisible, P as sessionEvents, R as stableHash, S as itemBudget, T as parseSemanticRoute, _ as buildSemanticRoutePrompt, a as compareRouteFeedbackDetail, b as inspectDeliveryPhase, c as selectRouteFeedbackDetail, d as AutoVerifierRouter, f as MAX_ROUTED_CHECKPOINTS, g as buildEvidenceIndex, h as boundDecision, i as automaticFeedback, j as verificationVerdict, k as semanticRouteHint, l as sessionAccepted, m as analyzeStructuredRoute, n as PROPOSAL_FEEDBACK_NOTE, o as failedAcceptanceCriteria, p as RECOVERY_FAILURE_CONTEXT_CHARS, r as analyzeAutoTask, s as isSubagentSession, t as MAX_ROUTE_FEEDBACK_CHARS, u as topScoreIndices, v as buildSemanticRouteView, w as nextDiagnosticCycleId, x as inspectRecoverySignal, y as estimateRoutedCalls } from "./auto-caXDU2pw.js";
4
4
  import { defineTool } from "@deepseek-ai/dsh-tools";
5
5
  import { createUserMessage, isAgentLoopRequest } from "@deepseek-ai/dsh-llm";
6
6
  import z from "schemastery";
@@ -1381,7 +1381,7 @@ var CriteriaResolver = class {
1381
1381
  }
1382
1382
  };
1383
1383
  /**
1384
- * Map one finished cycle onto the outcome the chip distinguishes.
1384
+ * Map one finished P06 cycle onto the outcome the chip distinguishes.
1385
1385
  *
1386
1386
  * `replayed` is the host-facing fact ("which stream did the host actually receive") and wins over
1387
1387
  * the outcome label: a cycle recorded as `candidate-selected` whose winner was withheld before the
@@ -1398,17 +1398,120 @@ function classifyProcessOutcome(outcome, replayed) {
1398
1398
  return "failed";
1399
1399
  }
1400
1400
  /**
1401
- * Per-session activity table. One active cycle per session, because the selector owns one phase
1402
- * controller per session; a settled cycle replaces the previous settled one (the chip is a status
1403
- * line, not a history — the statistics sidecar is the history).
1401
+ * Map one granted reservation onto the chip's active record.
1402
+ *
1403
+ * `process` is deliberately NOT mapped: P06 publishes its own richer record (candidate count,
1404
+ * generation vs comparison), and two writers for one cycle id would race. `plan_review` and
1405
+ * `team_task` are not announced either — the plan gate already shows a pending tool row in the
1406
+ * chat, so a chip line would only repeat it.
1407
+ * @param reservation - the reservation the router just granted.
1408
+ * @param startedAt - wall clock the cycle began at.
1409
+ * @returns The active record, or undefined for a phase the chip stays silent about.
1410
+ */
1411
+ function reservationActivity(reservation, startedAt) {
1412
+ switch (reservation.phase) {
1413
+ case "semantic": return {
1414
+ cycleId: reservation.id,
1415
+ stage: "route",
1416
+ phase: "classifying",
1417
+ expectedCalls: reservation.expectedCalls,
1418
+ startedAt
1419
+ };
1420
+ case "compare":
1421
+ case "select":
1422
+ case "track": return {
1423
+ cycleId: reservation.id,
1424
+ stage: "route",
1425
+ phase: "reviewing",
1426
+ destination: reservation.phase,
1427
+ expectedCalls: reservation.expectedCalls,
1428
+ startedAt
1429
+ };
1430
+ case "final": return {
1431
+ cycleId: reservation.id,
1432
+ stage: "final",
1433
+ phase: "accepting",
1434
+ expectedCalls: reservation.expectedCalls,
1435
+ startedAt
1436
+ };
1437
+ default: return;
1438
+ }
1439
+ }
1440
+ /**
1441
+ * Map a promotion onto the update the chip needs.
1442
+ *
1443
+ * A classification cycle that resolved to a decision stays the SAME reservation (that is what
1444
+ * promotion is for), so the chip must be moved from "classifying" to "reviewing" instead of leaving
1445
+ * a second cycle behind.
1446
+ * @param reservation - the promoted reservation, already carrying its execution phase.
1447
+ * @returns The patch, or undefined when the phase is not one the chip announces.
1448
+ */
1449
+ function reservationPromotion(reservation) {
1450
+ switch (reservation.phase) {
1451
+ case "compare":
1452
+ case "select":
1453
+ case "track": return {
1454
+ phase: "reviewing",
1455
+ destination: reservation.phase,
1456
+ expectedCalls: reservation.expectedCalls
1457
+ };
1458
+ default: return;
1459
+ }
1460
+ }
1461
+ /**
1462
+ * Map a router settlement onto the chip's settled row.
1463
+ *
1464
+ * A routed verdict is steered into the chat by the caller right after the cycle settles, so a second
1465
+ * transient line would only repeat it; the final gate is the opposite (a PASSING acceptance steers
1466
+ * nothing), and that is exactly the case worth showing. `undefined` means "clear the active row and
1467
+ * leave nothing behind".
1468
+ * @param reservation - the reservation being settled.
1469
+ * @param outcome - whether the router committed or failed it.
1470
+ * @returns The stage and outcome to keep, or undefined to clear silently.
1471
+ */
1472
+ function reservationSettled(reservation, outcome) {
1473
+ return reservation.phase === "final" ? {
1474
+ stage: "final",
1475
+ outcome: outcome === "committed" ? "accepted" : "rejected"
1476
+ } : void 0;
1477
+ }
1478
+ /**
1479
+ * Build the router's advisory observer over one activity table.
1480
+ *
1481
+ * The router supplies the reservation (it is the only component that knows a cycle was granted and
1482
+ * when it settled); this module owns what the chip says about it.
1483
+ * @param activities - the table the plugin's RPC reads.
1484
+ * @param now - wall clock used for the records and their TTLs.
1485
+ * @returns The observer to hand to `new AutoVerifierRouter(...)`.
1404
1486
  */
1405
- var ProcessActivities = class {
1487
+ function createActivityObserver(activities, now = Date.now) {
1488
+ const key = (agent) => String(agent.id);
1489
+ return {
1490
+ begin: (agent, reservation) => {
1491
+ const activity = reservationActivity(reservation, now());
1492
+ if (activity !== void 0) activities.begin(key(agent), activity);
1493
+ },
1494
+ promoted: (agent, reservation) => {
1495
+ const patch = reservationPromotion(reservation);
1496
+ if (patch !== void 0) activities.update(key(agent), reservation.id, patch);
1497
+ },
1498
+ settled: (agent, reservation, outcome) => {
1499
+ const settled = reservationSettled(reservation, outcome);
1500
+ activities.finish(key(agent), reservation.id, settled?.outcome, now());
1501
+ }
1502
+ };
1503
+ }
1504
+ /**
1505
+ * Per-session activity table.
1506
+ *
1507
+ * One active cycle per session, because the router owns one in-flight reservation per agent at a
1508
+ * time; a settled cycle replaces the previous settled one (the chip is a status line, not a history
1509
+ * — the statistics sidecar is the history).
1510
+ */
1511
+ var VerifierActivities = class {
1406
1512
  active = /* @__PURE__ */ new Map();
1407
1513
  settled = /* @__PURE__ */ new Map();
1408
- /**
1409
- * Publish one cycle as in flight. Called once per bought cycle, after the reservation and the
1410
- * cycle log are in place and immediately before the alternative is dispatched.
1411
- */
1514
+ /** Publish one cycle as in flight. */
1412
1515
  begin(sessionId, activity) {
1413
1516
  this.active.set(sessionId, activity);
1414
1517
  }
@@ -1419,50 +1522,62 @@ var ProcessActivities = class {
1419
1522
  * moved back into "in flight" by a late async step.
1420
1523
  * @returns whether the exact cycle was the active one.
1421
1524
  */
1422
- phase(sessionId, cycleId, phase) {
1525
+ update(sessionId, cycleId, patch) {
1423
1526
  const current = this.active.get(sessionId);
1424
1527
  if (current === void 0 || current.cycleId !== cycleId) return false;
1425
1528
  this.active.set(sessionId, {
1426
1529
  ...current,
1427
- phase
1530
+ ...patch
1428
1531
  });
1429
1532
  return true;
1430
1533
  }
1431
1534
  /**
1432
- * Retire one cycle and report how it resolved.
1535
+ * Retire one cycle.
1433
1536
  *
1434
1537
  * A cycle nobody observed (the purchase itself failed, so `begin` never ran) leaves nothing
1435
1538
  * behind: the chip must not claim a cycle that never started. A cycle that WAS observed stays
1436
- * correctable while its own settled record is the current one, which is what the selector's
1437
- * late "the winner was not delivered" correction needs.
1438
- * @returns whether the cycle was known and the outcome recorded.
1539
+ * correctable while its own settled record is the current one, which is what P06's late
1540
+ * "the winner was not delivered" correction needs.
1541
+ * @param sessionId - the session the cycle belongs to.
1542
+ * @param cycleId - the cycle to retire.
1543
+ * @param outcome - the settled row to keep, or undefined to clear without leaving one.
1544
+ * @param now - wall clock used for the settled record.
1545
+ * @returns whether the cycle was known and retired.
1439
1546
  */
1440
- finish(sessionId, cycleId, outcome, replayed, now) {
1441
- const current = this.active.get(sessionId);
1442
- const known = current?.cycleId === cycleId ? current : this.settled.get(sessionId)?.cycleId === cycleId ? this.settled.get(sessionId) : void 0;
1547
+ finish(sessionId, cycleId, outcome, now) {
1548
+ const active = this.active.get(sessionId);
1549
+ const settled = this.settled.get(sessionId);
1550
+ const known = active?.cycleId === cycleId ? active : settled?.cycleId === cycleId ? settled : void 0;
1443
1551
  if (known === void 0) return false;
1444
- if (current?.cycleId === cycleId) this.active.delete(sessionId);
1552
+ if (active?.cycleId === cycleId) this.active.delete(sessionId);
1553
+ if (outcome === void 0) {
1554
+ if (settled?.cycleId === cycleId) this.settled.delete(sessionId);
1555
+ return true;
1556
+ }
1445
1557
  this.settled.set(sessionId, {
1446
1558
  cycleId,
1447
- outcome: classifyProcessOutcome(outcome, replayed),
1448
- candidates: known.candidates,
1559
+ stage: known.stage,
1560
+ outcome,
1561
+ ...known.candidates === void 0 ? {} : { candidates: known.candidates },
1449
1562
  at: now
1450
1563
  });
1451
1564
  return true;
1452
1565
  }
1453
1566
  /**
1454
- * What one session has to show right now, with expired settled records pruned in place.
1567
+ * What one session has to show right now, with expired records pruned in place.
1455
1568
  * @param sessionId - the session to read.
1456
- * @param now - wall clock used for the settled TTL.
1569
+ * @param now - wall clock used for both TTLs.
1457
1570
  */
1458
1571
  read(sessionId, now) {
1572
+ const active = this.active.get(sessionId);
1573
+ if (active !== void 0 && now - active.startedAt >= 6e5) this.active.delete(sessionId);
1459
1574
  const settled = this.settled.get(sessionId);
1460
1575
  if (settled !== void 0 && now - settled.at >= 2e4) this.settled.delete(sessionId);
1461
- const active = this.active.get(sessionId);
1462
- const current = this.settled.get(sessionId);
1576
+ const currentActive = this.active.get(sessionId);
1577
+ const currentSettled = this.settled.get(sessionId);
1463
1578
  return {
1464
- ...active === void 0 ? {} : { active },
1465
- ...current === void 0 ? {} : { settled: current }
1579
+ ...currentActive === void 0 ? {} : { active: currentActive },
1580
+ ...currentSettled === void 0 ? {} : { settled: currentSettled }
1466
1581
  };
1467
1582
  }
1468
1583
  /** Forget one session entirely (settings changed, task replaced, agent disposed). */
@@ -1996,14 +2111,18 @@ var ProcessSelector = class {
1996
2111
  *
1997
2112
  * Host-only and in memory: a session event would carry the same information, but the persistence
1998
2113
  * read path refuses unknown event types for an out-of-repo plugin, and `Session.append` cannot
1999
- * set the `ignorable` marker that would make one loadable (see `process-activity.ts`).
2114
+ * set the `ignorable` marker that would make one loadable (see `verifier-activity.ts`).
2000
2115
  */
2001
- activities = new ProcessActivities();
2116
+ activities;
2002
2117
  constructor(deps) {
2003
2118
  this.deps = deps;
2119
+ this.activities = deps.activities ?? new VerifierActivities();
2004
2120
  }
2005
2121
  /**
2006
2122
  * The cycle one session has to show right now, for the UI chip (in flight, or just settled).
2123
+ *
2124
+ * Reads the SAME table the router publishes into, so a routed review and a process cycle are never
2125
+ * two different answers to "what is this session doing".
2007
2126
  * @param sessionId - the session to read.
2008
2127
  */
2009
2128
  activity(sessionId) {
@@ -2169,6 +2288,7 @@ var ProcessSelector = class {
2169
2288
  this.cycles.set(intent.sessionId, phase);
2170
2289
  this.activities.begin(intent.sessionId, {
2171
2290
  cycleId: reservation.id,
2291
+ stage: "process",
2172
2292
  phase: "generating",
2173
2293
  candidates: count,
2174
2294
  ...observation.alternativeModel === void 0 ? {} : { alternativeModel: observation.alternativeModel },
@@ -2511,7 +2631,7 @@ var ProcessSelector = class {
2511
2631
  });
2512
2632
  return;
2513
2633
  }
2514
- this.activities.phase(intent.sessionId, reservation.id, "comparing");
2634
+ this.activities.update(intent.sessionId, reservation.id, { phase: "comparing" });
2515
2635
  let judged;
2516
2636
  try {
2517
2637
  if ("candidateA" in view) {
@@ -2530,7 +2650,8 @@ var ProcessSelector = class {
2530
2650
  winner: compared.winner === "B" ? alternatives[0] : void 0,
2531
2651
  tie: compared.winner === "tie",
2532
2652
  stats: compared.stats,
2533
- compare: compared
2653
+ compare: compared,
2654
+ ...compared.decisionId === void 0 ? {} : { decisionId: compared.decisionId }
2534
2655
  };
2535
2656
  } else {
2536
2657
  const selected = await this.deps.select({
@@ -2547,7 +2668,8 @@ var ProcessSelector = class {
2547
2668
  winner: selected.index > 0 ? alternatives[selected.index - 1] : void 0,
2548
2669
  tie: false,
2549
2670
  stats: selected.stats,
2550
- select: selected
2671
+ select: selected,
2672
+ ...selected.decisionId === void 0 ? {} : { decisionId: selected.decisionId }
2551
2673
  };
2552
2674
  }
2553
2675
  } catch (error) {
@@ -2622,7 +2744,7 @@ var ProcessSelector = class {
2622
2744
  const outcome = "candidate-not-delivered (" + staleBeforeReplay + ")";
2623
2745
  observation.replayed = "original";
2624
2746
  await this.deps.store(intent.agent).finish(reservation.id, outcome, "original");
2625
- this.activities.finish(intent.sessionId, reservation.id, outcome, "original", this.deps.now());
2747
+ this.activities.finish(intent.sessionId, reservation.id, classifyProcessOutcome(outcome, "original"), this.deps.now());
2626
2748
  try {
2627
2749
  await this.deps.correctDelivery({
2628
2750
  agent: intent.agent,
@@ -2690,13 +2812,15 @@ var ProcessSelector = class {
2690
2812
  }
2691
2813
  /** Record a purchased cycle and stamp its durable outcome. */
2692
2814
  async report(input) {
2693
- this.activities.finish(input.intent.sessionId, input.reservation.id, input.outcome, input.replayed, this.deps.now());
2815
+ this.activities.finish(input.intent.sessionId, input.reservation.id, classifyProcessOutcome(input.outcome, input.replayed), this.deps.now());
2694
2816
  await this.deps.store(input.intent.agent).finish(input.reservation.id, input.outcome, input.replayed);
2817
+ const decisionId = input.compare?.decisionId ?? input.select?.decisionId;
2695
2818
  await this.deps.record({
2696
2819
  agent: input.intent.agent,
2697
2820
  cycleId: input.reservation.id,
2698
2821
  purchased: true,
2699
2822
  startedAt: input.startedAt,
2823
+ ...decisionId === void 0 ? {} : { decisionId },
2700
2824
  outcome: input.outcome,
2701
2825
  replayed: input.replayed,
2702
2826
  generatedCalls: input.generatedCalls,
@@ -3302,7 +3426,7 @@ var StatisticsStore = class {
3302
3426
  const verdict = cleanVerdict(input.verdict);
3303
3427
  const route = cleanRoute(input.route);
3304
3428
  const record = {
3305
- id: randomUUID(),
3429
+ id: typeof input.id === "string" && input.id.length > 0 ? input.id : randomUUID(),
3306
3430
  toolName: input.toolName,
3307
3431
  ...input.sessionId ? { sessionId: input.sessionId } : {},
3308
3432
  startedAt: input.startedAt,
@@ -3638,7 +3762,7 @@ var DecisionStore = class {
3638
3762
  const calls = boundDecisionCalls(input.calls);
3639
3763
  if (calls.length === 0) return void 0;
3640
3764
  const record = {
3641
- id: randomUUID(),
3765
+ id: idOf(input.id),
3642
3766
  toolName: input.toolName,
3643
3767
  phase: input.phase,
3644
3768
  startedAt: input.startedAt,
@@ -3698,6 +3822,18 @@ var DecisionStore = class {
3698
3822
  }
3699
3823
  };
3700
3824
  /**
3825
+ * The id a snapshot is filed under.
3826
+ *
3827
+ * A caller-supplied id is the statistics row's own id ({@link DecisionInput.id}), which is what
3828
+ * makes the dashboard's "one snapshot per row" lookup resolve; anything unusable falls back to a
3829
+ * fresh uuid so a malformed argument can never merge two invocations into one record.
3830
+ * @param candidate - id supplied by the caller, if any.
3831
+ * @returns The id to store the record under.
3832
+ */
3833
+ function idOf(candidate) {
3834
+ return typeof candidate === "string" && candidate.length > 0 ? candidate : randomUUID();
3835
+ }
3836
+ /**
3701
3837
  * Loose validation, so a record written by a newer/older plugin still loads.
3702
3838
  * @param value - parsed JSON entry.
3703
3839
  * @returns True when the entry carries the fields the dashboard needs.
@@ -4095,7 +4231,8 @@ function apply(ctx, config = {}) {
4095
4231
  limiter = new RequestLimiter(current().maxConcurrency);
4096
4232
  clearProcessIntents();
4097
4233
  });
4098
- const autoRouter = new AutoVerifierRouter();
4234
+ const activities = new VerifierActivities();
4235
+ const autoRouter = new AutoVerifierRouter(createActivityObserver(activities));
4099
4236
  const topics = /* @__PURE__ */ new Map();
4100
4237
  const topic = (header) => {
4101
4238
  const selected = current();
@@ -4258,7 +4395,7 @@ function apply(ctx, config = {}) {
4258
4395
  ...completed.result,
4259
4396
  ...route(selected)
4260
4397
  };
4261
- await statistics.record({
4398
+ const invocation = await statistics.record({
4262
4399
  toolName,
4263
4400
  sessionId: String(agent.id),
4264
4401
  startedAt,
@@ -4268,8 +4405,9 @@ function apply(ctx, config = {}) {
4268
4405
  stats: statsFrom(value),
4269
4406
  verdict: verdictFrom(toolName, value, phase),
4270
4407
  ...observation ? { route: observation } : {}
4271
- }).catch(() => {});
4408
+ }).catch(() => void 0);
4272
4409
  if (calls.length > 0) await topicEntry.decisions.record({
4410
+ ...invocation === void 0 ? {} : { id: invocation.id },
4273
4411
  toolName,
4274
4412
  phase,
4275
4413
  startedAt,
@@ -4877,6 +5015,7 @@ function apply(ctx, config = {}) {
4877
5015
  sanitize: (text, maxChars) => sanitizeVerifierText(text, maxChars),
4878
5016
  policy: processPolicy,
4879
5017
  router: () => autoRouter,
5018
+ activities,
4880
5019
  store: (agent) => topic(agent.session.header).process,
4881
5020
  taskStatement: async (agent, fromSeq, signal) => {
4882
5021
  const target = agent;
@@ -4908,15 +5047,18 @@ function apply(ctx, config = {}) {
4908
5047
  reviewStage: "proposal",
4909
5048
  ...trace ? { trace } : {}
4910
5049
  }, request.signal);
4911
- if (calls.length > 0) await topic(agent.session.header).decisions.record({
5050
+ const snapshot = calls.length === 0 ? void 0 : await topic(agent.session.header).decisions.record({
4912
5051
  toolName: "verifier_compare",
4913
5052
  phase: "process",
4914
5053
  startedAt,
4915
5054
  provider: selected.provider,
4916
5055
  model: selected.model,
4917
5056
  calls: boundDecisionCalls([...calls].sort((a, b) => a.label < b.label ? -1 : a.label > b.label ? 1 : 0))
4918
- }).catch(() => {});
4919
- return result;
5057
+ }).catch(() => void 0);
5058
+ return snapshot === void 0 ? result : {
5059
+ ...result,
5060
+ decisionId: snapshot.id
5061
+ };
4920
5062
  },
4921
5063
  select: async (request) => {
4922
5064
  const agent = request.agent;
@@ -4935,15 +5077,18 @@ function apply(ctx, config = {}) {
4935
5077
  reviewStage: "proposal",
4936
5078
  ...trace ? { trace } : {}
4937
5079
  }, request.signal);
4938
- if (calls.length > 0) await topic(agent.session.header).decisions.record({
5080
+ const snapshot = calls.length === 0 ? void 0 : await topic(agent.session.header).decisions.record({
4939
5081
  toolName: "verifier_select",
4940
5082
  phase: "process",
4941
5083
  startedAt,
4942
5084
  provider: selected.provider,
4943
5085
  model: selected.model,
4944
5086
  calls: boundDecisionCalls([...calls].sort((a, b) => a.label < b.label ? -1 : a.label > b.label ? 1 : 0))
4945
- }).catch(() => {});
4946
- return result;
5087
+ }).catch(() => void 0);
5088
+ return snapshot === void 0 ? result : {
5089
+ ...result,
5090
+ decisionId: snapshot.id
5091
+ };
4947
5092
  },
4948
5093
  record: async (report) => {
4949
5094
  const agent = report.agent;
@@ -4970,6 +5115,7 @@ function apply(ctx, config = {}) {
4970
5115
  };
4971
5116
  const failedCall = report.outcome === "generation-failed" || report.outcome === "comparison-failed";
4972
5117
  await topic(agent.session.header).statistics.record({
5118
+ ...report.decisionId === void 0 ? {} : { id: report.decisionId },
4973
5119
  toolName: report.select === void 0 ? "verifier_compare" : "verifier_select",
4974
5120
  sessionId: String(agent.id),
4975
5121
  startedAt: report.startedAt,
@@ -5080,15 +5226,18 @@ function apply(ctx, config = {}) {
5080
5226
  /** Whether a payload asks for a decision snapshot instead of a statistics overview. */
5081
5227
  const isDecisionQuery = (payload) => typeof payload === "object" && payload !== null && payload.kind === "decision";
5082
5228
  /**
5083
- * Whether a payload asks what the process-selection (P06) cycle of one session is doing.
5229
+ * Whether a payload asks what this plugin is doing for one session.
5084
5230
  *
5085
- * This is what the chat chip polls while a turn runs. The answer is in-memory state only: no
5086
- * sidecar read, no model call, and nothing durable, so polling it cannot cost anything or change
5087
- * a verdict. A session that never bought a cycle answers with an empty object.
5231
+ * This is what the chat chip polls while a turn runs: a P06 cycle (which buffers the reply), a
5232
+ * routed review, or the final acceptance. The wire name is historical — it covers every stage now,
5233
+ * and keeping it means a page holding the older client bundle keeps working.
5088
5234
  */
5089
5235
  const isProcessQuery = (payload) => typeof payload === "object" && payload !== null && payload.kind === "process";
5090
5236
  /**
5091
- * Read the in-flight / just-settled process cycle of one session.
5237
+ * Read the in-flight / just-settled cycle of one session.
5238
+ *
5239
+ * In-memory state only: no sidecar read, no model call, nothing durable, so polling it cannot cost
5240
+ * anything or change a verdict. A session with nothing to show answers with an empty object.
5092
5241
  * @param payload - the RPC payload; `sessionId` is required.
5093
5242
  * @returns The activity view, or a bad-request failure.
5094
5243
  */
@@ -5097,7 +5246,7 @@ function apply(ctx, config = {}) {
5097
5246
  if (typeof sessionId !== "string" || sessionId === "") return rpcFailure("a sessionId is required");
5098
5247
  return rpcSuccess({
5099
5248
  sessionId,
5100
- ...processSelector.activity(sessionId)
5249
+ ...activities.read(sessionId, Date.now())
5101
5250
  });
5102
5251
  };
5103
5252
  /**