@kage-core/kage-graph-mcp 3.1.0 → 3.3.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.
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ // Pure metric and statistics helpers used by the benchmark/metrics code.
3
+ // This is a dependency-free leaf module: it imports nothing from the kernel,
4
+ // so pulling it out carries no circular-dependency risk. Leaf-first is the
5
+ // safe way to decompose the kernel — extract the bottom of the dependency
6
+ // graph first, where nothing depends back inward.
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.codingRecallAt = codingRecallAt;
9
+ exports.codingPrecisionAt = codingPrecisionAt;
10
+ exports.codingNdcgAt = codingNdcgAt;
11
+ exports.codingMrr = codingMrr;
12
+ exports.averageNumber = averageNumber;
13
+ exports.percentileNumber = percentileNumber;
14
+ exports.roundDecimal = roundDecimal;
15
+ exports.countByKey = countByKey;
16
+ exports.titleCase = titleCase;
17
+ /** Recall@k: fraction of the relevant set retrieved within the top k. */
18
+ function codingRecallAt(retrieved, relevant, k) {
19
+ if (!relevant.size)
20
+ return 0;
21
+ return retrieved.slice(0, k).filter((item) => relevant.has(item.packet_id)).length / relevant.size;
22
+ }
23
+ /** Precision@k: fraction of the top k that is relevant. */
24
+ function codingPrecisionAt(retrieved, relevant, k) {
25
+ const rows = retrieved.slice(0, k);
26
+ return rows.length ? rows.filter((item) => relevant.has(item.packet_id)).length / rows.length : 0;
27
+ }
28
+ /** Normalized discounted cumulative gain at k (binary relevance). */
29
+ function codingNdcgAt(retrieved, relevant, k) {
30
+ const dcg = retrieved.slice(0, k).reduce((sum, item, index) => sum + (relevant.has(item.packet_id) ? 1 / Math.log2(index + 2) : 0), 0);
31
+ const idealHits = Math.min(relevant.size, k);
32
+ let ideal = 0;
33
+ for (let index = 0; index < idealHits; index += 1)
34
+ ideal += 1 / Math.log2(index + 2);
35
+ return ideal ? dcg / ideal : 0;
36
+ }
37
+ /** Mean reciprocal rank: 1 / rank of the first relevant hit, else 0. */
38
+ function codingMrr(retrieved, relevant) {
39
+ const index = retrieved.findIndex((item) => relevant.has(item.packet_id));
40
+ return index >= 0 ? 1 / (index + 1) : 0;
41
+ }
42
+ /** Arithmetic mean, 0 for an empty list. */
43
+ function averageNumber(values) {
44
+ return values.length ? values.reduce((sum, value) => sum + value, 0) / values.length : 0;
45
+ }
46
+ /** p-th percentile (0..1) via nearest-rank, 0 for an empty list. */
47
+ function percentileNumber(values, p) {
48
+ if (!values.length)
49
+ return 0;
50
+ const sorted = values.slice().sort((a, b) => a - b);
51
+ const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil(sorted.length * p) - 1));
52
+ return sorted[index];
53
+ }
54
+ /** Round to a fixed number of decimal places. */
55
+ function roundDecimal(value, digits = 2) {
56
+ const factor = 10 ** digits;
57
+ return Math.round(value * factor) / factor;
58
+ }
59
+ /** Tally rows by a string key. */
60
+ function countByKey(rows, fn) {
61
+ const counts = {};
62
+ for (const row of rows) {
63
+ const key = fn(row);
64
+ counts[key] = (counts[key] ?? 0) + 1;
65
+ }
66
+ return counts;
67
+ }
68
+ /** Upper-case the first letter of each word. */
69
+ function titleCase(value) {
70
+ return value.replace(/\b[a-z]/g, (match) => match.toUpperCase());
71
+ }
package/dist/okf.js CHANGED
@@ -132,8 +132,10 @@ function okfVerifiedStatus(packet) {
132
132
  return "superseded";
133
133
  if (packet.status === "deprecated")
134
134
  return "deprecated";
135
- const freshness = packet.freshness;
136
- return freshness && freshness.last_verified_at ? "verified" : "unverified";
135
+ // "verified" is earned, not born: capture provenance (repo_local_agent_capture)
136
+ // is not a check of the claim. Only an actual recheck (evidence-backed
137
+ // reverification, validation pass) may carry the label.
138
+ return (0, kernel_js_1.packetVerificationLabel)(packet);
137
139
  }
138
140
  // ---- packet -> OKF concept document ----
139
141
  function packetToOkfConcept(packet) {
@@ -156,7 +158,8 @@ function packetToOkfConcept(packet) {
156
158
  fm.push(`x-kage-status: ${yamlScalar(packet.status)}`);
157
159
  fm.push(`x-kage-scope: ${yamlScalar(packet.scope)}`);
158
160
  fm.push(`x-kage-visibility: ${yamlScalar(packet.visibility)}`);
159
- fm.push(`x-kage-confidence: ${packet.confidence}`);
161
+ // No x-kage-confidence: the field was a hardcoded 0.7 nobody computed or
162
+ // consumed — a number that means nothing must not ship as trust metadata.
160
163
  fm.push(`x-kage-verified: ${yamlScalar(okfVerifiedStatus(packet))}`);
161
164
  if (packet.paths?.length)
162
165
  fm.push(`x-kage-paths: ${yamlList(packet.paths)}`);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kage-core/kage-graph-mcp",
3
- "version": "3.1.0",
4
- "description": "Verified memory for coding agents, stored as a Google Open Knowledge Format (OKF) bundle and checked against your code. The verification layer OKF leaves out. MCP server, no account, no API key.",
3
+ "version": "3.3.0",
4
+ "description": "Agent memory on Google's Open Knowledge Format (OKF). Kage maintains your coding agents' OKF memory bundle in git and verifies every concept against your code, deterministically. The verification and freshness layer OKF leaves out. MCP server, no account, no API key.",
5
5
  "main": "dist/index.js",
6
6
  "files": [
7
7
  "dist/**/*.js",
package/viewer/console.js CHANGED
@@ -67,7 +67,7 @@
67
67
  // ---- nav ----
68
68
  var META = {
69
69
  dashboard: ["kage://dashboard", "Dashboard", "Your team's captured knowledge — decisions, runbooks, and fixes — at a glance."],
70
- gains: ["kage://gains", "What Kage saved you", "Tokens, dollars, and bad memories caught — receipts, not vibes."],
70
+ gains: ["kage://gains", "What Kage saved you", "Tokens saved and bad memories caught — receipts, not vibes."],
71
71
  overview: ["kage://trust", "Memory trust", "Whether this repo's agent memory can be trusted — at a glance."],
72
72
  graph: ["kage://memory-map", "Memory ↔ code map", "Each packet anchored to the files it's grounded in. Hover a node to inspect."],
73
73
  memory: ["kage://memory", "Memory", "Every packet Kage has stored, with health and grounding."],
@@ -137,12 +137,6 @@
137
137
  // value.json is the raw ledger written by recall: { totals, events[] }. Windows are
138
138
  // recomputed here with the same rules as `kage gains` (today = local midnight,
139
139
  // 7d = rolling, all-time = totals so trimmed events never lose history).
140
- var DOLLARS_PER_MILLION_TOKENS = 15;
141
- function dollars(tokens) { return (tokens / 1e6) * DOLLARS_PER_MILLION_TOKENS; }
142
- function fmtDollars(tokens) {
143
- var d = dollars(tokens);
144
- return "$" + (d >= 100 ? Math.round(d) : d.toFixed(2));
145
- }
146
140
  function summarizeWindow(events, cutoff) {
147
141
  var w = { tokens_saved: 0, stale_withheld: 0, recalls: 0, caller_answers: 0 };
148
142
  events.forEach(function (e) {
@@ -175,7 +169,6 @@
175
169
  var big = el("div", "big");
176
170
  big.appendChild(el("b", null, all.tokens_saved ? "0" : "—"));
177
171
  big.appendChild(el("span", "unit", "tokens saved, all time"));
178
- if (all.tokens_saved) big.appendChild(el("span", "dollars", "≈ " + fmtDollars(all.tokens_saved)));
179
172
  hh.appendChild(big);
180
173
  hh.appendChild(el("p", "sub", all.tokens_saved
181
174
  ? "Context your agents did not have to re-read from source, because Kage served grounded memory instead."
@@ -186,7 +179,6 @@
186
179
  var w = el("div", "r-win");
187
180
  w.appendChild(el("div", "k", p[0]));
188
181
  w.appendChild(el("div", "v", fmt(p[1].tokens_saved) + " tok"));
189
- w.appendChild(el("div", "d", "≈ " + fmtDollars(p[1].tokens_saved)));
190
182
  wins.appendChild(w);
191
183
  });
192
184
  hero.appendChild(wins);
@@ -203,13 +195,13 @@
203
195
  lines.appendChild(row);
204
196
  });
205
197
  hero.appendChild(lines);
206
- hero.appendChild(el("div", "r-foot", "estimated at $" + DOLLARS_PER_MILLION_TOKENS + " per 1M input tokens · ledger: .agent_memory/reports/value.json · verify: kage gains"));
198
+ hero.appendChild(el("div", "r-foot", "ledger: .agent_memory/reports/value.json · verify: kage gains"));
207
199
  if (all.tokens_saved) countUp(big.querySelector("b"), all.tokens_saved, 900);
208
200
 
209
201
  if (tiles) {
210
202
  tiles.textContent = "";
211
203
  [
212
- { k: "Saved (7 days)", v: fmt(week.tokens_saved), s: " " + fmtDollars(week.tokens_saved) + " of context not re-read", cls: "green" },
204
+ { k: "Saved (7 days)", v: fmt(week.tokens_saved), s: "tokens of context not re-read", cls: "green" },
213
205
  { k: "Recalls served (7d)", v: fmt(week.recalls), s: fmt(all.recalls) + " all-time", cls: "green" },
214
206
  { k: "Stale caught", v: fmt(all.stale_withheld), s: all.stale_withheld ? "withheld before they misled an agent" : "nothing withheld yet", cls: all.stale_withheld ? "warn" : "" },
215
207
  { k: "Graph answers", v: fmt(all.caller_answers), s: "caller questions answered from the code graph", cls: "code" },
package/viewer/index.html CHANGED
@@ -69,14 +69,12 @@
69
69
  .receipt .r-hero .big { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
70
70
  .receipt .r-hero .big b { font: 600 64px/0.95 var(--serif); letter-spacing: -0.02em; color: var(--gain); font-variant-numeric: tabular-nums; }
71
71
  .receipt .r-hero .big .unit { color: var(--muted); font: 500 16px/1 var(--sans); }
72
- .receipt .r-hero .big .dollars { color: var(--ink); font: 600 22px/1 var(--serif); }
73
72
  .receipt .r-hero .sub { margin-top: 10px; color: var(--muted); font: 400 13.5px/1.5 var(--sans); max-width: 560px; }
74
73
  .receipt .r-windows { display: grid; grid-template-columns: repeat(3, 1fr); border-top: 1px dashed var(--line-strong); }
75
74
  .receipt .r-win { padding: 16px 24px; border-left: 1px dashed var(--line-strong); }
76
75
  .receipt .r-win:first-child { border-left: 0; }
77
76
  .receipt .r-win .k { color: var(--faint); font: 600 10px/1 var(--mono); text-transform: uppercase; letter-spacing: 0.1em; }
78
77
  .receipt .r-win .v { margin-top: 8px; font: 600 24px/1 var(--serif); color: var(--ink); font-variant-numeric: tabular-nums; }
79
- .receipt .r-win .d { margin-top: 5px; color: var(--gain); font: 600 12px/1 var(--mono); }
80
78
  .receipt .r-lines { border-top: 1px dashed var(--line-strong); padding: 14px 24px 16px; }
81
79
  .receipt .r-line { display: flex; align-items: baseline; gap: 10px; padding: 5px 0; font: 400 13.5px/1.5 var(--sans); color: var(--ink); }
82
80
  .receipt .r-line .dots { flex: 1; border-bottom: 1px dotted var(--line-strong); transform: translateY(-4px); }
@@ -290,7 +288,7 @@
290
288
  <header class="head">
291
289
  <span class="eyebrow" id="eyebrow">kage://gains</span>
292
290
  <h1 id="title">What Kage saved you</h1>
293
- <p id="subtitle">Tokens, dollars, and bad memories caught — receipts, not vibes.</p>
291
+ <p id="subtitle">Tokens saved and bad memories caught — receipts, not vibes.</p>
294
292
  </header>
295
293
  <div class="wrap">
296
294
  <section class="section active" id="section-dashboard">