@kage-core/kage-graph-mcp 2.5.5 → 2.5.7

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/dist/cli.js CHANGED
@@ -15,7 +15,6 @@ const CORE_USAGE = `Kage — code-grounded memory for coding agents
15
15
  Core commands:
16
16
  kage install [--project <dir>] one-shot: init + index + auto-wire detected agents
17
17
  kage scan --project <dir> 60-second truth report on any repo (zero setup)
18
- kage demo watch the reject/withhold loop run in a sandbox
19
18
  kage init --project <dir> create repo memory (.agent_memory only)
20
19
  kage index --project <dir> [--full] build/refresh code graph + indexes
21
20
  kage recall "<query>" --project <dir> grounded recall from repo memory
@@ -1406,6 +1405,10 @@ async function main() {
1406
1405
  `~${(0, kernel_js_1.formatTokenCount)(summary.all_time.replay_tokens)} all time ` +
1407
1406
  `(discovery cost of served memories vs their compressed read cost)`);
1408
1407
  }
1408
+ const usdOverridden = Number.isFinite(Number(process.env.KAGE_USD_PER_MTOK)) && Number(process.env.KAGE_USD_PER_MTOK) > 0;
1409
+ console.log(`\nDollars estimated at $${kernel_js_1.VALUE_DOLLARS_PER_MILLION_TOKENS}/1M input tokens ` +
1410
+ `(${usdOverridden ? "via KAGE_USD_PER_MTOK" : "Sonnet-class default — set KAGE_USD_PER_MTOK for your model"}). ` +
1411
+ `Ledger: .agent_memory/reports/value.json`);
1409
1412
  return;
1410
1413
  }
1411
1414
  if (command === "file-context") {
package/dist/kernel.js CHANGED
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.PACKET_MERGE_DRIVER_CONFIG = exports.PACKET_MERGE_ATTRIBUTE_LINE = exports.AUTO_DISTILL_SIGNAL_THRESHOLD = exports.AUTO_DISTILL_TAG = exports.SETUP_AGENTS = exports.MEMORY_TYPES = exports.PACKET_SCHEMA_VERSION = void 0;
36
+ exports.PACKET_MERGE_DRIVER_CONFIG = exports.PACKET_MERGE_ATTRIBUTE_LINE = exports.AUTO_DISTILL_SIGNAL_THRESHOLD = exports.AUTO_DISTILL_TAG = exports.VALUE_DOLLARS_PER_MILLION_TOKENS = exports.SETUP_AGENTS = exports.MEMORY_TYPES = exports.PACKET_SCHEMA_VERSION = void 0;
37
37
  exports.memoryRoot = memoryRoot;
38
38
  exports.packetsDir = packetsDir;
39
39
  exports.pendingDir = pendingDir;
@@ -1017,8 +1017,14 @@ function recordRecallAccess(projectDir, results) {
1017
1017
  // ---------------------------------------------------------------------------
1018
1018
  const VALUE_LEDGER_SCHEMA_VERSION = 1;
1019
1019
  const VALUE_LEDGER_EVENT_CAP = 5000;
1020
- // Rough Sonnet-class input price used for the dollar estimate: $15 per 1M tokens.
1021
- const VALUE_DOLLARS_PER_MILLION_TOKENS = 15;
1020
+ // Input price used for the dollar estimate. Default: Sonnet-class ~$3 per 1M input tokens (the
1021
+ // typical coding-agent tier) — deliberately conservative so the savings figure never overstates.
1022
+ // Override with KAGE_USD_PER_MTOK to match your model (e.g. 15 for Opus, 2.5 for GPT-4o, 0.8 for
1023
+ // Haiku). The old default ($15, Opus pricing) overstated savings ~5x for most users.
1024
+ exports.VALUE_DOLLARS_PER_MILLION_TOKENS = (() => {
1025
+ const raw = Number(process.env.KAGE_USD_PER_MTOK);
1026
+ return Number.isFinite(raw) && raw > 0 ? raw : 3;
1027
+ })();
1022
1028
  function valueLedgerPath(projectDir) {
1023
1029
  return (0, node_path_1.join)(reportsDir(projectDir), "value.json");
1024
1030
  }
@@ -1112,7 +1118,7 @@ function recordValueEvent(projectDir, event) {
1112
1118
  recordValueEvents(projectDir, [event]);
1113
1119
  }
1114
1120
  function estimatedTokenDollars(tokensSaved) {
1115
- return Number(((tokensSaved / 1_000_000) * VALUE_DOLLARS_PER_MILLION_TOKENS).toFixed(2));
1121
+ return Number(((tokensSaved / 1_000_000) * exports.VALUE_DOLLARS_PER_MILLION_TOKENS).toFixed(2));
1116
1122
  }
1117
1123
  function summarizeValueWindow(events, cutoff) {
1118
1124
  const window = { tokens_saved: 0, replay_tokens: 0, stale_withheld: 0, stale_caught: 0, recalls: 0, caller_answers: 0 };
@@ -7015,10 +7021,11 @@ function gcProject(projectDir, options = {}) {
7015
7021
  skipped.push({ id: packet.id, title: packet.title, reason: "already deprecated" });
7016
7022
  continue;
7017
7023
  }
7018
- // Serialized transcript / tool-output / file-content dumps carry no durable knowledge
7019
- // and bloat recall + the graph. Always delete them (deprecating would leave the blob on
7020
- // disk) this also reclaims legacy dumps written before the capture-time guard existed.
7021
- if (isSerializedDumpTitle(packet.title) || isSerializedDumpBody(packet.body)) {
7024
+ // Serialized transcript / tool-output / file-content dumps and ungrounded conversational
7025
+ // chatter (a path-less rant at the assistant) carry no durable knowledge and bloat recall +
7026
+ // the digest. Always delete them (deprecating would leave the blob on disk) — this also
7027
+ // reclaims legacy junk written before the capture-time guards existed.
7028
+ if (isSerializedDumpTitle(packet.title) || isSerializedDumpBody(packet.body) || isUngroundedConversationalCapture(packet)) {
7022
7029
  if (!options.dryRun)
7023
7030
  (0, node_fs_1.unlinkSync)(path);
7024
7031
  deleted.push({ id: packet.id, title: packet.title });
@@ -8012,6 +8019,14 @@ function hasRepoGroundingSignal(text) {
8012
8019
  // punctuation, or one of a curated set of rhetorical / second-person-at-the-assistant
8013
8020
  // phrases — so it stays clear of normal declarative learnings (which read as statements, not
8014
8021
  // outbursts addressed to "you").
8022
+ // Phrases that rant AT the assistant ("why are you...", "it's your job", "don't stop"). These
8023
+ // never appear in a curated learning, so they mark chatter even when the outburst name-drops a
8024
+ // platform word (github, pr, x, linkedin) that the loose grounding matcher would otherwise
8025
+ // treat as a repo reference. This is the override that closes the leak where a frustrated
8026
+ // message peppered with nouns slipped through and was captured as approved memory.
8027
+ function looksFrustratedAtAssistant(text) {
8028
+ return /\b(why are you|why did you|why would you|why aren'?t you|are you (kidding|serious|even|really)|it'?s your job|that'?s your job|do your job|don'?t stop|stop asking|stop before you|keep going|hurry up|just do it|figure it out yourself|i (already )?told you)\b/i.test(text ?? "");
8029
+ }
8015
8030
  function looksLikeRawUserUtterance(text) {
8016
8031
  const t = (text ?? "").trim();
8017
8032
  if (!t)
@@ -8021,18 +8036,21 @@ function looksLikeRawUserUtterance(text) {
8021
8036
  if (/[!?]{2,}/.test(t))
8022
8037
  return true;
8023
8038
  // Rhetorical questions / second-person-imperative frustration directed at the assistant.
8024
- return /\b(why are you|why did you|why would you|why aren'?t you|are you (kidding|serious|even|really)|it'?s your job|that'?s your job|do your job|don'?t stop|stop asking|stop before you|keep going|hurry up|just do it|figure it out yourself|i (already )?told you)\b/i.test(t);
8025
- }
8026
- // Capture-noise guard for ungrounded chat. A packet trips it only when ALL hold: it cites zero
8027
- // repo paths, its text carries no repo grounding signal (no path/file/identifier/command), and
8028
- // it reads as a raw conversational user utterance. The conjunction is what keeps it safe — a
8029
- // real ungrounded decision or convention names a symbol, file, command, or rule and so is never
8030
- // caught. Such packets are routed to pending (not auto-approved) at capture time and withheld
8031
- // from recall, mirroring how serialized dumps are gated.
8039
+ return looksFrustratedAtAssistant(t);
8040
+ }
8041
+ // Capture-noise guard for ungrounded chat. A packet trips it when it cites zero repo paths and
8042
+ // either (a) it rants at the assistant which overrides incidental repo-ish words, since the
8043
+ // grounding matcher false-positives on bare platform names or (b) it reads as a raw outburst
8044
+ // and carries no repo grounding signal at all. The override + conjunction keep it safe: a real
8045
+ // ungrounded decision or convention is declarative (no "why are you.../don't stop") and usually
8046
+ // names a symbol, file, command, or rule, so it is never caught. Such packets route to pending
8047
+ // (not auto-approved) at capture time and are withheld from recall, like serialized dumps.
8032
8048
  function isUngroundedConversationalCapture(packet) {
8033
8049
  if (packet.paths && packet.paths.length > 0)
8034
8050
  return false;
8035
8051
  const text = `${packet.title ?? ""}\n${packet.body ?? ""}`;
8052
+ if (looksFrustratedAtAssistant(text))
8053
+ return true;
8036
8054
  if (hasRepoGroundingSignal(text))
8037
8055
  return false;
8038
8056
  return looksLikeRawUserUtterance(text);
@@ -15615,20 +15633,17 @@ function kageResume(projectDir) {
15615
15633
  updated_at: packetRecency(packet),
15616
15634
  age: humanPacketAge(packetRecency(packet)),
15617
15635
  }));
15618
- const hasSessionContent = Boolean(lastSession || lastChangeMemory || pendingAutoDistilled || reconciliation.unresolved_count);
15619
- const hasContent = hasSessionContent || recentMemory.length > 0;
15636
+ // A new session starts on a fresh task, so SessionStart does NOT replay last session's files,
15637
+ // commands, or a recency-ranked memory list — that was pre-task noise (it fires before the
15638
+ // first prompt, so it can't be task-targeted) and the surface that leaked raw command dumps and
15639
+ // junk packets. Task-relevant memory is pulled on the first prompt via prompt-context; recall
15640
+ // at the moment a file is read via file-context. SessionStart carries only always-on curated
15641
+ // repo facts (the pinned block, below) plus a few actionable open-thread pointers.
15642
+ const hasSessionContent = Boolean(lastChangeMemory || pendingAutoDistilled || reconciliation.unresolved_count);
15643
+ const hasContent = hasSessionContent;
15620
15644
  const lines = [];
15621
- if (hasContent) {
15622
- lines.push("# Previously (Kage)");
15623
- if (lastSession) {
15624
- lines.push(`Last session ${lastSession.session_id} (${lastSession.observations} observation${lastSession.observations === 1 ? "" : "s"}, ended ${lastSession.last_at}).`);
15625
- if (lastSession.paths.length)
15626
- lines.push(`Worked on: ${lastSession.paths.join(", ")}`);
15627
- if (lastSession.commands.length)
15628
- lines.push(`Commands: ${lastSession.commands.join("; ")}`);
15629
- if (lastSession.distilled_titles.length)
15630
- lines.push(`Learned: ${lastSession.distilled_titles.join("; ")}`);
15631
- }
15645
+ if (hasSessionContent) {
15646
+ lines.push("# Open threads (Kage)");
15632
15647
  if (lastChangeMemory) {
15633
15648
  lines.push(`Change memory: ${lastChangeMemory.title} — ${lastChangeMemory.summary}`);
15634
15649
  }
@@ -15641,22 +15656,7 @@ function kageResume(projectDir) {
15641
15656
  lines.push(` - ${item.packet_id}: ${item.title}`);
15642
15657
  }
15643
15658
  }
15644
- // Compact timeline index: one line per recent packet, full detail (summary)
15645
- // only for the newest few, hard-capped to the resume token budget.
15646
- const block = lines.slice(0, 15);
15647
- if (recentPackets.length) {
15648
- block.push("", "## Recent memory");
15649
- recentPackets.forEach((packet, position) => {
15650
- const entry = `[${packet.id.slice(0, 12)}] ${packet.type} ${packet.title} (${humanPacketAge(packetRecency(packet))})`;
15651
- const candidate = [entry];
15652
- if (position < RESUME_TIMELINE_DETAILED && packet.summary) {
15653
- const summary = packet.summary.replace(/\s+/g, " ").trim();
15654
- candidate.push(` ${summary.length > 160 ? `${summary.slice(0, 157)}...` : summary}`);
15655
- }
15656
- if (estimateTokens([...block, ...candidate].join("\n")) <= RESUME_CONTEXT_TOKEN_BUDGET)
15657
- block.push(...candidate);
15658
- });
15659
- }
15659
+ const block = lines.slice(0, 12);
15660
15660
  // Lead the SessionStart injection with the team's pinned, always-on repo memory (the
15661
15661
  // curated high-signal facts), not just the recent-timeline digest — parity with recall's
15662
15662
  // context block, so a new session starts already holding the key knowledge, not only a
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kage-core/kage-graph-mcp",
3
- "version": "2.5.5",
4
- "description": "Team memory for coding agents: captures the decisions, runbooks, and bug fixes that get lost, verified against your code and shared via git. MCP server, zero deps, no account.",
3
+ "version": "2.5.7",
4
+ "description": "Team memory for coding agents: captures the decisions, runbooks, and bug fixes that get lost, verified against your code and shared via git. MCP server, no account, no API key.",
5
5
  "main": "dist/index.js",
6
6
  "files": [
7
7
  "dist/**/*.js",