@agentistics/mcp 2.125.0 → 2.126.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.
package/README.md CHANGED
@@ -38,6 +38,7 @@ The `AGENTISTICS_API` env var must point to a running agentistics server.
38
38
  |------|---------|
39
39
  | `agentistics_summary` | All-time totals: tokens, cost, sessions, streak — unified or per-harness |
40
40
  | `agentistics_harnesses` | Side-by-side comparison of every tracked harness |
41
+ | `agentistics_plan_limits` | Subscription windows, pacing, and the recommended harness/model before opening sessions |
41
42
  | `agentistics_projects` | Per-project token and cost breakdown (optionally per-harness) |
42
43
  | `agentistics_sessions` | Recent sessions with harness, duration, model, cost |
43
44
  | `agentistics_costs` | Model pricing breakdown and cache savings (optionally per-harness) |
@@ -1009,6 +1009,7 @@ var AGENT_TOOL_POLICY = {
1009
1009
  },
1010
1010
  agentistics_summary: { ...DATA, capability: "localTranscripts", host: true, risk: "R", routes: ["GET /api/data", "GET /api/runtime/metrics"], reason: "reads computed metrics" },
1011
1011
  agentistics_harnesses: { ...DATA, capability: "localTranscripts", host: true, risk: "R", routes: ["GET /api/data", "GET /api/runtime/metrics"], reason: "reads computed metrics per harness" },
1012
+ agentistics_plan_limits: { ...DATA, capability: "localTranscripts", host: true, risk: "R", routes: ["GET /api/plan-limits"], reason: "reads subscription windows and routing guidance; does not start work" },
1012
1013
  agentistics_projects: { ...DATA, capability: "localTranscripts", host: true, risk: "R", routes: ["GET /api/data", "GET /api/runtime/metrics"], reason: "reads computed metrics per project" },
1013
1014
  agentistics_sessions: { ...DATA, capability: "localShell", host: true, risk: "R", routes: ["GET /api/data", "GET /api/fleet"], reason: "reads session metadata (first_prompt is untrusted text, §4.8)" },
1014
1015
  agentistics_costs: { ...DATA, capability: "localTranscripts", host: true, risk: "R", routes: ["GET /api/data", "GET /api/runtime/metrics"], reason: "reads computed costs" },
@@ -1038,6 +1039,46 @@ var PLAN_WINDOW_MS = {
1038
1039
  week: 7 * 24 * 60 * 60000
1039
1040
  };
1040
1041
  var PLAN_LIMITS_STALE_MS = 10 * 60000;
1042
+ var ROUTE_PREFERENCES = {
1043
+ lead: [["claude", "opus"], ["codex"]],
1044
+ "hard-bug": [["claude", "opus"], ["codex"]],
1045
+ design: [["claude", "opus"], ["codex"]],
1046
+ "spec-impl": [["codex"], ["claude", "sonnet"], ["gemini"], ["copilot"]],
1047
+ mechanical: [["codex"], ["claude", "sonnet"], ["gemini"], ["copilot"]],
1048
+ qa: [["gemini"], ["copilot"], ["codex"], ["claude", "sonnet"]],
1049
+ probe: [["claude", "haiku"], ["gemini"]]
1050
+ };
1051
+ function routeRecommendation(budgets, kind, authorHarness) {
1052
+ const allowTight = kind === "lead";
1053
+ const eligible = budgets.filter((b) => b.plan !== "unregistered" && b.status !== "hold" && (allowTight || b.status !== "tight") && b.harness !== (kind === "qa" ? authorHarness : undefined));
1054
+ const ordered = [...eligible].sort((a, b) => {
1055
+ const ap = a.paceStatus === "under" ? 0 : 1;
1056
+ const bp = b.paceStatus === "under" ? 0 : 1;
1057
+ if (ap !== bp)
1058
+ return ap - bp;
1059
+ const ai = ROUTE_PREFERENCES[kind].findIndex(([h]) => h === a.harness);
1060
+ const bi = ROUTE_PREFERENCES[kind].findIndex(([h]) => h === b.harness);
1061
+ return (ai < 0 ? 999 : ai) - (bi < 0 ? 999 : bi);
1062
+ });
1063
+ const preference = ROUTE_PREFERENCES[kind];
1064
+ const chosen = ordered.find((b) => preference.some(([h]) => h === b.harness));
1065
+ const pick = chosen ?? ordered[0];
1066
+ if (!pick) {
1067
+ const earliest = budgets.filter((b) => b.status === "hold").sort((a, b) => a.weekResetsAt - b.weekResetsAt)[0];
1068
+ const reset = earliest ? new Date(earliest.weekResetsAt).toISOString() : "the next reset";
1069
+ return { harness: null, reason: `all plans on hold until ${reset}`, alternatives: [], cadence: { parallel: 0, note: "Wait for a plan to renew." } };
1070
+ }
1071
+ const selected = preference.find(([h]) => h === pick.harness);
1072
+ const alternatives = ordered.filter((b) => b.harness !== pick.harness).map((b) => b.harness);
1073
+ const paceText = pick.note ? ` ${pick.note}` : "";
1074
+ return {
1075
+ harness: pick.harness,
1076
+ ...selected?.[1] ? { model: selected[1] } : {},
1077
+ reason: `${pick.harness} is the preferred eligible plan for ${kind}.${paceText}`,
1078
+ alternatives,
1079
+ cadence: { parallel: pick.suggestedParallel ?? 1, note: pick.note ?? "Keep one worker running." }
1080
+ };
1081
+ }
1041
1082
  var NEW_PERIOD_MS = 5 * 60000;
1042
1083
  // session-tokens.ts
1043
1084
  function sessionHarness(s) {
@@ -1424,6 +1465,13 @@ function sessionProof(env, readKey = (p) => readFileSync(p, "utf8"), readProc =
1424
1465
  }
1425
1466
  }
1426
1467
 
1468
+ // plan-limits-tool.ts
1469
+ function planLimitsTool(args, body) {
1470
+ const budgets = body.budgets ?? [];
1471
+ const recommendation = args?.kind ? routeRecommendation(budgets, args.kind, args.authorHarness) : undefined;
1472
+ return { budgets, ...recommendation ? { recommendation } : {}, readAt: body.now ?? Date.now() };
1473
+ }
1474
+
1427
1475
  // legacy-analytics.ts
1428
1476
  function legacySummary(data, harness) {
1429
1477
  const unified = !harness || harness === "all";
@@ -2230,6 +2278,18 @@ var TOOLS = [
2230
2278
  description: "Compare the tracked AI coding harnesses side by side. Lists every harness present in the data with its sessions, messages, token usage, estimated cost, and last-active date. Use this to answer 'which harness do I use most / costs most' questions. Rows tagged source \"projections\" (read from the journal) carry personTurns (the person's turns); there `messages` is a deprecated alias of personTurns, and subagent spend is reported apart (subagentTokens, subagentCostUSD).",
2231
2279
  inputSchema: { type: "object", properties: {}, required: [] }
2232
2280
  },
2281
+ {
2282
+ name: "agentistics_plan_limits",
2283
+ description: "Read this BEFORE opening sessions; follow the recommendation; a plan on hold gets no new worker. Returns subscription budgets, pacing, and a routing recommendation.",
2284
+ inputSchema: {
2285
+ type: "object",
2286
+ properties: {
2287
+ kind: { type: "string", enum: ["lead", "hard-bug", "design", "spec-impl", "mechanical", "qa", "probe"] },
2288
+ authorHarness: { type: "string" }
2289
+ },
2290
+ required: []
2291
+ }
2292
+ },
2233
2293
  {
2234
2294
  name: "agentistics_projects",
2235
2295
  description: 'List projects with session counts, message counts, token usage, estimated cost, and last active date. Optionally scope to a single harness. Rows tagged source "projections" (read from the journal) carry personTurns (the person\'s turns); there `messages` is a deprecated alias of personTurns, and subagent spend is reported apart (subagentTokens, subagentCostUSD).',
@@ -2662,6 +2722,9 @@ async function callTool(req) {
2662
2722
  metrics: (q) => fetch(`${API}/api/runtime/metrics?${q}`)
2663
2723
  }));
2664
2724
  }
2725
+ case "agentistics_plan_limits": {
2726
+ return text(planLimitsTool(args ?? {}, await apiGet("/api/plan-limits")));
2727
+ }
2665
2728
  case "agentistics_projects": {
2666
2729
  const harness = args?.harness;
2667
2730
  return text(await analytics("agentistics_projects", harness, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentistics/mcp",
3
- "version": "2.125.0",
3
+ "version": "2.126.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",