@datasynx/agentic-ai-cartography 2.6.0 → 2.7.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/dist/index.cjs CHANGED
@@ -45,6 +45,7 @@ __export(src_exports, {
45
45
  CredentialConfigSchema: () => CredentialConfigSchema,
46
46
  CsvCostSource: () => CsvCostSource,
47
47
  DEFAULT_ANOMALY_THRESHOLDS: () => DEFAULT_ANOMALY_THRESHOLDS,
48
+ DEFAULT_INGEST_QUOTA: () => DEFAULT_INGEST_QUOTA,
48
49
  DEFAULT_SERVER_NAME: () => DEFAULT_SERVER_NAME,
49
50
  DEFAULT_TENANT: () => DEFAULT_TENANT,
50
51
  DriftConfigSchema: () => DriftConfigSchema,
@@ -66,10 +67,12 @@ __export(src_exports, {
66
67
  ProviderRegistry: () => ProviderRegistry,
67
68
  RELATION_TO_DIRECTION: () => RELATION_TO_DIRECTION,
68
69
  ROLES: () => ROLES,
70
+ RateLimiter: () => RateLimiter,
69
71
  RoleSchema: () => RoleSchema,
70
72
  RuleCheckSchema: () => RuleCheckSchema,
71
73
  RulesetSchema: () => RulesetSchema,
72
74
  SCAN_ARG_PATTERNS: () => SCAN_ARG_PATTERNS,
75
+ SCHEMA_VERSION: () => SCHEMA_VERSION,
73
76
  SDL: () => SDL,
74
77
  SEVERITY_WEIGHT: () => SEVERITY_WEIGHT,
75
78
  SHARING_LEVELS: () => SHARING_LEVELS,
@@ -2850,6 +2853,7 @@ function newAnomalies(base, current) {
2850
2853
 
2851
2854
  // src/db.ts
2852
2855
  var DEFAULT_TENANT = "local";
2856
+ var SCHEMA_VERSION = 15;
2853
2857
  function normalizeTenant(raw) {
2854
2858
  if (raw == null) return DEFAULT_TENANT;
2855
2859
  const cleaned = sanitizeUntrusted(String(raw)).trim().slice(0, 128);
@@ -4254,6 +4258,14 @@ var CartographyDB = class {
4254
4258
  }
4255
4259
  return rows.length;
4256
4260
  }
4261
+ /**
4262
+ * Retention/compaction (4.7): delete audit events older than `olderThan` (ISO 8601).
4263
+ * The audit trail grows unbounded on a busy collector; this bounds it without touching
4264
+ * sessions/nodes/edges. Returns the number of events removed.
4265
+ */
4266
+ pruneEventsOlderThan(olderThan) {
4267
+ return this.db.prepare("DELETE FROM activity_events WHERE timestamp < ?").run(olderThan).changes;
4268
+ }
4257
4269
  // ── Graph queries (read-only context layer) ─────────────────────────────────
4258
4270
  /** Fetch a single node by id within a session. */
4259
4271
  getNode(sessionId, nodeId) {
@@ -4945,7 +4957,7 @@ var ContributorSchema = import_zod5.z.object({
4945
4957
  });
4946
4958
  var IngestEnvelopeSchema = import_zod5.z.object({
4947
4959
  schemaVersion: import_zod5.z.literal(INGEST_SCHEMA_VERSION),
4948
- org: import_zod5.z.string().min(1).optional(),
4960
+ org: import_zod5.z.string().min(1).max(128).optional(),
4949
4961
  items: import_zod5.z.array(import_zod5.z.object({
4950
4962
  contentHash: import_zod5.z.string(),
4951
4963
  kind: import_zod5.z.enum(["node", "edge"]),
@@ -5033,6 +5045,7 @@ function ingestEnvelope(store, envelope, opts = {}) {
5033
5045
 
5034
5046
  // src/central/server.ts
5035
5047
  function createIngestHandler(store, opts = {}) {
5048
+ const quota = opts.quota;
5036
5049
  return (body) => {
5037
5050
  const parsed = IngestEnvelopeSchema.safeParse(body);
5038
5051
  if (!parsed.success) {
@@ -5040,6 +5053,14 @@ function createIngestHandler(store, opts = {}) {
5040
5053
  logWarn("ingest: rejected invalid envelope", { issues });
5041
5054
  return { status: 400, body: { error: "invalid envelope", issues } };
5042
5055
  }
5056
+ if (quota) {
5057
+ const org = normalizeTenant(parsed.data.org ?? opts.defaultOrg);
5058
+ const decision = quota.take(org);
5059
+ if (!decision.allowed) {
5060
+ logWarn("ingest: rate limited", { org, retryAfterSec: decision.retryAfterSec });
5061
+ return { status: 429, body: { error: "too many requests" }, headers: { "retry-after": String(decision.retryAfterSec) } };
5062
+ }
5063
+ }
5043
5064
  try {
5044
5065
  const result = ingestEnvelope(store, parsed.data, opts);
5045
5066
  return { status: 200, body: result };
@@ -5050,6 +5071,39 @@ function createIngestHandler(store, opts = {}) {
5050
5071
  };
5051
5072
  }
5052
5073
 
5074
+ // src/central/quota.ts
5075
+ var DEFAULT_INGEST_QUOTA = { capacity: 120, refillMs: 6e4 };
5076
+ var MAX_KEYS = 1e4;
5077
+ var RateLimiter = class {
5078
+ constructor(cfg = DEFAULT_INGEST_QUOTA, now = () => Date.now()) {
5079
+ this.cfg = cfg;
5080
+ this.now = now;
5081
+ }
5082
+ buckets = /* @__PURE__ */ new Map();
5083
+ /** Consume one token for `key`. Returns whether the request is allowed (+ Retry-After when not). */
5084
+ take(key) {
5085
+ const t = this.now();
5086
+ const ratePerMs = this.cfg.capacity / this.cfg.refillMs;
5087
+ let b = this.buckets.get(key);
5088
+ if (!b) {
5089
+ if (this.buckets.size >= MAX_KEYS) {
5090
+ const oldest = this.buckets.keys().next().value;
5091
+ if (oldest !== void 0) this.buckets.delete(oldest);
5092
+ }
5093
+ b = { tokens: this.cfg.capacity, last: t };
5094
+ this.buckets.set(key, b);
5095
+ }
5096
+ b.tokens = Math.min(this.cfg.capacity, b.tokens + Math.max(0, t - b.last) * ratePerMs);
5097
+ b.last = t;
5098
+ if (b.tokens >= 1) {
5099
+ b.tokens -= 1;
5100
+ return { allowed: true, retryAfterSec: 0 };
5101
+ }
5102
+ const waitMs = (1 - b.tokens) / ratePerMs;
5103
+ return { allowed: false, retryAfterSec: Math.max(1, Math.ceil(waitMs / 1e3)) };
5104
+ }
5105
+ };
5106
+
5053
5107
  // src/scanners/bookmarks.ts
5054
5108
  var PERSONAL = [
5055
5109
  "facebook.",
@@ -5937,7 +5991,7 @@ async function resolveNlQuery(db, sessionId, search, raw, opts) {
5937
5991
 
5938
5992
  // src/mcp/server.ts
5939
5993
  var SERVER_NAME = "cartography";
5940
- var SERVER_VERSION = "2.6.0";
5994
+ var SERVER_VERSION = "2.7.0";
5941
5995
  var SERVICE_TYPES = NODE_TYPE_GROUPS.web;
5942
5996
  var DATA_TYPES = NODE_TYPE_GROUPS.data;
5943
5997
  var lexicalSearch = async (db, sessionId, query, opts) => db.searchNodes(sessionId, query, { types: opts.types, limit: opts.limit }).map((node) => ({ node }));
@@ -6580,6 +6634,16 @@ async function runHttp(factory, opts = {}) {
6580
6634
  const httpServer = import_node_http.default.createServer(async (req, res) => {
6581
6635
  try {
6582
6636
  const url = req.url ?? "";
6637
+ const probePath = new URL(url || "/", "http://probe").pathname;
6638
+ if (probePath === "/healthz") {
6639
+ res.writeHead(200, { "content-type": "application/json" }).end('{"status":"ok"}');
6640
+ return;
6641
+ }
6642
+ if (probePath === "/readyz") {
6643
+ const r = opts.readiness ? opts.readiness() : { ready: true };
6644
+ res.writeHead(r.ready ? 200 : 503, { "content-type": "application/json" }).end(JSON.stringify({ status: r.ready ? "ready" : "unready" }));
6645
+ return;
6646
+ }
6583
6647
  const isIngest = url.startsWith("/ingest") && opts.onIngest !== void 0;
6584
6648
  if (!url.startsWith("/mcp") && !isIngest) {
6585
6649
  res.writeHead(404, { "content-type": "application/json" }).end('{"error":"not found"}');
@@ -6607,7 +6671,7 @@ async function runHttp(factory, opts = {}) {
6607
6671
  return;
6608
6672
  }
6609
6673
  const out = onIngest(value);
6610
- res.writeHead(out.status, { "content-type": "application/json" }).end(JSON.stringify(out.body));
6674
+ res.writeHead(out.status, { "content-type": "application/json", ...out.headers ?? {} }).end(JSON.stringify(out.body));
6611
6675
  return;
6612
6676
  }
6613
6677
  const sessionId = req.headers["mcp-session-id"];
@@ -11859,6 +11923,7 @@ function checkClaudePrerequisites() {
11859
11923
  CredentialConfigSchema,
11860
11924
  CsvCostSource,
11861
11925
  DEFAULT_ANOMALY_THRESHOLDS,
11926
+ DEFAULT_INGEST_QUOTA,
11862
11927
  DEFAULT_SERVER_NAME,
11863
11928
  DEFAULT_TENANT,
11864
11929
  DriftConfigSchema,
@@ -11880,10 +11945,12 @@ function checkClaudePrerequisites() {
11880
11945
  ProviderRegistry,
11881
11946
  RELATION_TO_DIRECTION,
11882
11947
  ROLES,
11948
+ RateLimiter,
11883
11949
  RoleSchema,
11884
11950
  RuleCheckSchema,
11885
11951
  RulesetSchema,
11886
11952
  SCAN_ARG_PATTERNS,
11953
+ SCHEMA_VERSION,
11887
11954
  SDL,
11888
11955
  SEVERITY_WEIGHT,
11889
11956
  SHARING_LEVELS,