agentfootprint-lens 0.32.0 → 0.33.1

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.
@@ -1552,8 +1552,159 @@ import {
1552
1552
  renderCommentary,
1553
1553
  selectCommentaryKey
1554
1554
  } from "agentfootprint";
1555
+
1556
+ // src/core/humanizeRouting.ts
1557
+ function fmtScore(n) {
1558
+ return String(Number(n.toFixed(2)));
1559
+ }
1560
+ function fmtShare(n) {
1561
+ return `${Math.round(n * 100)}%`;
1562
+ }
1563
+ function quoteList(ids) {
1564
+ return ids.join(", ");
1565
+ }
1566
+ function q(id) {
1567
+ return id === void 0 ? void 0 : `"${id}"`;
1568
+ }
1569
+ function movement(p) {
1570
+ const { from, to } = p;
1571
+ if (to !== void 0 && from !== void 0) {
1572
+ return from === to ? `Stayed in ${q(to)}` : `Moved from ${q(from)} to ${q(to)}`;
1573
+ }
1574
+ if (to !== void 0) return `Turn started in ${q(to)}`;
1575
+ if (from !== void 0) return `Stayed in ${q(from)}`;
1576
+ return "The turn was routed";
1577
+ }
1578
+ function droppedResumeNote(p) {
1579
+ const d = p.droppedResume;
1580
+ if (!d) return "";
1581
+ return ` The conversation's saved place (${q(d.id)}) no longer exists in this deployment.`;
1582
+ }
1583
+ function humanizeTurnRouted(p) {
1584
+ switch (p.by) {
1585
+ case "entry":
1586
+ case "rule":
1587
+ return `${movement(p)} \u2014 a rule matched (tier 1).${droppedResumeNote(p)}`;
1588
+ case "intent": {
1589
+ const winner = p.scores?.[0];
1590
+ const ruRow = p.runnerUp && p.scores ? p.scores.find((s) => s.id === p.runnerUp.id) : void 0;
1591
+ let clause = " \u2014 intent";
1592
+ if (winner) {
1593
+ clause += ` scored ${fmtScore(winner.score)}`;
1594
+ if (ruRow) clause += ` vs ${fmtScore(ruRow.score)} for runner-up ${q(ruRow.id)}`;
1595
+ else if (p.runnerUp)
1596
+ clause += `, ${fmtScore(p.runnerUp.gap)} ahead of ${q(p.runnerUp.id)}`;
1597
+ } else {
1598
+ clause += " matched";
1599
+ }
1600
+ const scorer = p.scorer ? ` (${p.scorer} scorer)` : "";
1601
+ return `${movement(p)}${clause}${scorer}.${droppedResumeNote(p)}`;
1602
+ }
1603
+ case "continuity": {
1604
+ const nearTie = p.decisive === false && p.scores && p.scores.length >= 2;
1605
+ if (nearTie) {
1606
+ const [a, b] = p.scores;
1607
+ const where2 = p.to ?? p.from;
1608
+ const tail = where2 !== void 0 ? `; continuing in ${q(where2)}` : "";
1609
+ return `Stayed \u2014 near-tie between ${q(a.id)} (${fmtShare(a.relevance)}) and ${q(b.id)} (${fmtShare(b.relevance)})${tail}.${droppedResumeNote(p)}`;
1610
+ }
1611
+ const where = p.to ?? p.from;
1612
+ const head = where !== void 0 ? `Stayed in ${q(where)}` : "Stayed";
1613
+ return `${head} \u2014 treated as a continuation of the conversation.${droppedResumeNote(p)}`;
1614
+ }
1615
+ case "menu":
1616
+ case "model-pick": {
1617
+ let line = "No single skill won \u2014 the model was offered a menu";
1618
+ if (p.offered && p.offered.length > 0)
1619
+ line += ` of ${p.offered.length}: ${quoteList(p.offered)}`;
1620
+ if (p.stayOffered) line += `; 'stay' was on the menu`;
1621
+ return `${line}.${droppedResumeNote(p)}`;
1622
+ }
1623
+ case "none": {
1624
+ if (p.droppedResume) {
1625
+ let line = `The conversation's saved place (${q(p.droppedResume.id)}) no longer exists in this deployment \u2014 the turn started fresh.`;
1626
+ if (p.offered && p.offered.length > 0)
1627
+ line += ` Options on the record: ${quoteList(p.offered)}.`;
1628
+ return line;
1629
+ }
1630
+ if (p.offered && p.offered.length > 0) {
1631
+ const top = p.scores?.[0]?.score;
1632
+ const floor = p.policy?.floor;
1633
+ const cleared = top !== void 0 && Number.isFinite(top) && (floor === void 0 || top > floor);
1634
+ if (p.decisive === false && (p.scores?.length ?? 0) >= 2 && cleared) {
1635
+ return `This message matched ${quoteList(p.offered)} too closely to call, and rails mode does not let the model break the tie \u2014 the turn ran on the base prompt.`;
1636
+ }
1637
+ if (top !== void 0 && !cleared) {
1638
+ return `No rule or intent matched this message strongly enough, and rails mode does not let the model route \u2014 the turn ran on the base prompt. The full menu stayed on the record: ${quoteList(p.offered)}.`;
1639
+ }
1640
+ return `Routing ended in a menu, but rails mode does not let the model route \u2014 the turn ran on the base prompt. On the record: ${quoteList(p.offered)}.`;
1641
+ }
1642
+ return "Routing decided nothing this turn \u2014 the turn ran on the base prompt.";
1643
+ }
1644
+ default:
1645
+ return `Turn routing: ${p.by}${p.to !== void 0 ? ` \u2192 ${q(p.to)}` : ""}.`;
1646
+ }
1647
+ }
1648
+ var COUNT_WORDS = ["Zero", "One", "Two", "Three", "Four", "Five"];
1649
+ function humanizeRouteConflict(p) {
1650
+ const winner = p.winner;
1651
+ const losers = p.losers ?? [];
1652
+ const winnerClause = winner ? `${winner.toolName ?? "a tool"} \u2192 ${q(winner.target) ?? "its target"} won (first in the batch)` : "the first match in the batch won";
1653
+ if (losers.length === 0) {
1654
+ return `Tool results conflicted on the next skill \u2014 ${winnerClause}.`;
1655
+ }
1656
+ const count = losers.length + 1;
1657
+ const countWord = count < COUNT_WORDS.length ? COUNT_WORDS[count] : String(count);
1658
+ const loserClause = losers.map((l) => `${l.toolName ?? "a tool"} \u2192 ${q(l.target) ?? "another skill"}`).join(", ");
1659
+ const verb = losers.length === 1 ? "was" : "were";
1660
+ return `${countWord} tool results wanted different next skills \u2014 ${winnerClause}; ${loserClause} ${verb} suppressed and ${losers.length === 1 ? "is" : "are"} on the record.`;
1661
+ }
1662
+ function humanizeSkillRejected(p) {
1663
+ const pick = `The model tried to switch to ${q(p.requestedId)}`;
1664
+ if (p.posture === "guard") {
1665
+ let line2 = `${pick}, but that pick was off the menu it was given \u2014 refused (guard).`;
1666
+ if (p.allowed && p.allowed.length > 0) line2 += ` Reachable: ${quoteList(p.allowed)}.`;
1667
+ return line2;
1668
+ }
1669
+ if (p.posture === "rails") {
1670
+ return `${pick}, but rails mode does not let the model route \u2014 refused.`;
1671
+ }
1672
+ const at = p.currentSkillId !== void 0 ? q(p.currentSkillId) : "the start";
1673
+ let line = `${pick}, but it is not reachable from ${at} \u2014 refused.`;
1674
+ if (p.allowed && p.allowed.length > 0) line += ` Reachable: ${quoteList(p.allowed)}.`;
1675
+ return line;
1676
+ }
1677
+ function humanizeCursorMovePick(move) {
1678
+ if (!move || move.by !== "model-pick") return null;
1679
+ if (move.offered === void 0 && move.declinedOffer === void 0) return null;
1680
+ let line = move.to !== void 0 ? `The model chose ${q(move.to)}` : "The model made a pick";
1681
+ if (move.offered !== void 0 && move.declinedOffer !== true) {
1682
+ line += ` from a ${move.offered.length}-item menu`;
1683
+ const rest = move.to !== void 0 ? move.offered.filter((id) => id !== move.to) : move.offered;
1684
+ if (rest.length > 0) line += ` (${quoteList(rest)} were offered too)`;
1685
+ }
1686
+ if (move.from !== void 0 && move.from !== move.to) {
1687
+ line += `, moving from ${q(move.from)}`;
1688
+ }
1689
+ line += ".";
1690
+ if (move.declinedOffer === true) {
1691
+ line += " Its pick was not on the offered menu \u2014 the divergence is on the record.";
1692
+ }
1693
+ return line;
1694
+ }
1695
+
1696
+ // src/core/humanizer.ts
1555
1697
  var defaultHumanizer = (event) => {
1556
- if (event.type === "agentfootprint.context.evaluated") return null;
1698
+ if (event.type === "agentfootprint.context.evaluated") {
1699
+ const cursorMove = event.payload.cursorMove;
1700
+ return humanizeCursorMovePick(cursorMove);
1701
+ }
1702
+ if (event.type === "agentfootprint.skill.turn_routed") {
1703
+ return humanizeTurnRouted(event.payload);
1704
+ }
1705
+ if (event.type === "agentfootprint.skill.route_conflict") {
1706
+ return humanizeRouteConflict(event.payload);
1707
+ }
1557
1708
  switch (event.type) {
1558
1709
  // Composition
1559
1710
  case "agentfootprint.composition.enter":
@@ -1637,6 +1788,8 @@ var defaultHumanizer = (event) => {
1637
1788
  return `Skill "${event.payload.skillId}" activated \u2014 ${event.payload.reason}.`;
1638
1789
  case "agentfootprint.skill.deactivated":
1639
1790
  return `Skill "${event.payload.skillId}" deactivated \u2014 ${event.payload.reason}.`;
1791
+ case "agentfootprint.skill.rejected":
1792
+ return humanizeSkillRejected(event.payload);
1640
1793
  // Error
1641
1794
  case "agentfootprint.error.fatal":
1642
1795
  return `\u26D4 The run failed \u2014 ${event.payload.error}`;
@@ -2525,20 +2678,20 @@ function expandSubflowInternals(subflows) {
2525
2678
  replayWalkInto(subRec, walkSubflowSpec(spec, path, { recurse: false }));
2526
2679
  const sub = subTrace.getGraph();
2527
2680
  const prefix = path.endsWith("/") ? path : `${path}/`;
2528
- const q = (id) => id.startsWith(prefix) ? id : `${prefix}${id}`;
2681
+ const q2 = (id) => id.startsWith(prefix) ? id : `${prefix}${id}`;
2529
2682
  for (const n of sub.nodes) {
2530
2683
  nodes.push({
2531
2684
  ...n,
2532
- id: q(n.id),
2685
+ id: q2(n.id),
2533
2686
  data: { ...n.data, subflowOf: subflowId }
2534
2687
  });
2535
2688
  }
2536
2689
  for (const e of sub.edges) {
2537
2690
  edges.push({
2538
2691
  ...e,
2539
- id: `${q(e.source)}->${q(e.target)}`,
2540
- source: q(e.source),
2541
- target: q(e.target)
2692
+ id: `${q2(e.source)}->${q2(e.target)}`,
2693
+ source: q2(e.source),
2694
+ target: q2(e.target)
2542
2695
  });
2543
2696
  }
2544
2697
  }
@@ -3055,4 +3208,4 @@ export {
3055
3208
  buildNewIssueUrl,
3056
3209
  encodeBase64
3057
3210
  };
3058
- //# sourceMappingURL=chunk-QBSH233H.js.map
3211
+ //# sourceMappingURL=chunk-6EHPI5F6.js.map