@agentistics/mcp 2.136.1 → 2.137.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.
@@ -1060,6 +1060,7 @@ var PLAN_WINDOW_MS = {
1060
1060
  week: 7 * 24 * 60 * 60000
1061
1061
  };
1062
1062
  var PLAN_LIMITS_STALE_MS = 10 * 60000;
1063
+ var PROJECTED_MAX_PCT_PER_HOUR = 100 / 0.5 * 0.99;
1063
1064
  var ROUTE_PREFERENCES = {
1064
1065
  lead: [["claude", "opus"], ["codex"]],
1065
1066
  "hard-bug": [["claude", "opus"], ["codex"]],
@@ -1069,10 +1070,34 @@ var ROUTE_PREFERENCES = {
1069
1070
  qa: [["gemini"], ["copilot"], ["codex"], ["claude", "sonnet"]],
1070
1071
  probe: [["claude", "haiku"], ["gemini"]]
1071
1072
  };
1073
+ var STATUS_RANK = { ok: 0, tight: 1, hold: 2 };
1074
+ var PACE_RANK = { under: 0, on: 1, over: 2 };
1075
+ function harnessAggregate(rows) {
1076
+ const by = new Map;
1077
+ for (const r of rows)
1078
+ by.set(r.harness, [...by.get(r.harness) ?? [], r]);
1079
+ return [...by].map(([harness, list]) => {
1080
+ const live = list.filter((r) => r.status !== "hold");
1081
+ const status = live.length ? live.reduce((best, r) => STATUS_RANK[r.status] < STATUS_RANK[best] ? r.status : best, "hold") : "hold";
1082
+ const paces = live.flatMap((r) => r.paceStatus ? [r.paceStatus] : []);
1083
+ const paceStatus = paces.length ? paces.reduce((best, p) => PACE_RANK[p] < PACE_RANK[best] ? p : best) : undefined;
1084
+ return {
1085
+ harness,
1086
+ status,
1087
+ accounts: list.length,
1088
+ freeSlots: live.reduce((sum, r) => sum + (r.freeSlots ?? 0), 0),
1089
+ ...paceStatus ? { paceStatus } : {}
1090
+ };
1091
+ });
1092
+ }
1093
+ var roomOf = (b) => Math.max(b.fiveHourPct, b.weekPct);
1072
1094
  function routeRecommendation(budgets, kind, authorHarness) {
1073
1095
  const allowTight = kind === "lead";
1074
- const eligible = budgets.filter((b) => b.spawnable !== false && b.plan !== "unregistered" && b.status !== "hold" && (allowTight || b.status !== "tight") && b.harness !== (kind === "qa" ? authorHarness : undefined));
1075
- const ordered = [...eligible].sort((a, b) => {
1096
+ const aggregates = new Map(harnessAggregate(budgets.filter((b) => b.plan !== "unregistered")).map((a) => [a.harness, a]));
1097
+ const eligibleHarness = (agg) => eligibleAccounts(agg.harness).length > 0 && agg.status !== "hold" && (allowTight || agg.status !== "tight") && agg.harness !== (kind === "qa" ? authorHarness : undefined);
1098
+ const eligibleAccounts = (h) => budgets.filter((b) => b.harness === h && b.spawnable !== false && b.plan !== "unregistered" && b.status !== "hold" && (allowTight || b.status !== "tight"));
1099
+ const harnesses = [...aggregates.values()].filter(eligibleHarness);
1100
+ const ordered = [...harnesses].sort((a, b) => {
1076
1101
  const ap = a.paceStatus === "under" ? 0 : 1;
1077
1102
  const bp = b.paceStatus === "under" ? 0 : 1;
1078
1103
  if (ap !== bp)
@@ -1082,28 +1107,42 @@ function routeRecommendation(budgets, kind, authorHarness) {
1082
1107
  return (ai < 0 ? 999 : ai) - (bi < 0 ? 999 : bi);
1083
1108
  });
1084
1109
  const preference = ROUTE_PREFERENCES[kind];
1085
- const chosen = ordered.find((b) => preference.some(([h]) => h === b.harness));
1086
- const pick = chosen ?? ordered[0];
1087
- if (!pick) {
1110
+ const chosen = ordered.find((a) => preference.some(([h]) => h === a.harness));
1111
+ const pickAgg = chosen ?? ordered[0];
1112
+ const noRoute = () => {
1088
1113
  if (budgets.length && budgets.every((b) => b.spawnable === false)) {
1089
- return { harness: null, reason: "No harness can start a new session.", alternatives: [], cadence: { parallel: 0, note: "Install or repair a harness before opening workers." } };
1114
+ return { harness: null, reason: "No harness can start a new session.", alternatives: [], accountAlternatives: [], cadence: { parallel: 0, note: "Install or repair a harness before opening workers." } };
1090
1115
  }
1091
1116
  const earliest = budgets.filter((b) => b.status === "hold").sort((a, b) => a.weekResetsAt - b.weekResetsAt)[0];
1092
1117
  const reset = earliest ? new Date(earliest.weekResetsAt).toISOString() : "the next reset";
1093
- return { harness: null, reason: `all plans on hold until ${reset}`, alternatives: [], cadence: { parallel: 0, note: "Wait for a plan to renew." } };
1094
- }
1118
+ return { harness: null, reason: `all plans on hold until ${reset}`, alternatives: [], accountAlternatives: [], cadence: { parallel: 0, note: "Wait for a plan to renew." } };
1119
+ };
1120
+ if (!pickAgg)
1121
+ return noRoute();
1122
+ const candidates = eligibleAccounts(pickAgg.harness);
1123
+ if (!candidates.length)
1124
+ return noRoute();
1125
+ const indexed = candidates.map((b, i) => ({ b, i }));
1126
+ indexed.sort((x, y) => roomOf(x.b) - roomOf(y.b) || (y.b.updatedAt ?? 0) - (x.b.updatedAt ?? 0) || y.i - x.i);
1127
+ const pick = indexed[0].b;
1095
1128
  const selected = preference.find(([h]) => h === pick.harness);
1096
- const alternatives = ordered.filter((b) => b.harness !== pick.harness).map((b) => b.harness);
1129
+ const alternatives = ordered.filter((a) => a.harness !== pick.harness).map((a) => a.harness);
1130
+ const accountAlternatives = ordered.flatMap((a) => eligibleAccounts(a.harness).filter((b) => !(b.harness === pick.harness && b.account === pick.account)).map((b) => ({ harness: b.harness, account: b.account ?? "default" })));
1097
1131
  const paceText = pick.note ? ` ${pick.note}` : "";
1132
+ const measured = candidates.some((b) => b.freeSlotsMeasured);
1133
+ const parallel = measured ? pickAgg.freeSlots : candidates.length > 1 ? candidates.reduce((sum, b) => sum + (b.suggestedParallel ?? 1), 0) : pick.suggestedParallel ?? 1;
1098
1134
  return {
1099
1135
  harness: pick.harness,
1136
+ ...pick.account ? { account: pick.account } : {},
1100
1137
  ...selected?.[1] ? { model: selected[1] } : {},
1101
1138
  reason: `${pick.harness} is the preferred eligible plan for ${kind}.${paceText}`,
1102
1139
  alternatives,
1103
- cadence: { parallel: pick.freeSlotsMeasured ? pick.freeSlots ?? 0 : pick.suggestedParallel ?? 1, note: pick.note ?? "Keep one worker running." }
1140
+ accountAlternatives,
1141
+ cadence: { parallel, note: pick.note ?? "Keep one worker running." }
1104
1142
  };
1105
1143
  }
1106
1144
  var NEW_PERIOD_MS = 5 * 60000;
1145
+ var DISCONTINUITY_RESET_MS = 60 * 60000;
1107
1146
  // session-tokens.ts
1108
1147
  function sessionHarness(s) {
1109
1148
  return s.harness ?? "claude";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentistics/mcp",
3
- "version": "2.136.1",
3
+ "version": "2.137.0",
4
4
  "description": "Agentistics MCP server — Claude Code analytics for Claude Desktop and Claude Code",
5
5
  "type": "module",
6
6
  "main": "./dist/agentistics-mcp.js",