// src/graph-retrieval.ts
var DEFAULT_PPR_DAMPING = 0.85;
var DEFAULT_PPR_ITERATIONS = 20;
var DEFAULT_PPR_TOLERANCE = 1e-6;
function normalizeSeedWeights(input) {
  const out = /* @__PURE__ */ new Map();
  if (!input) return out;
  const entries = input instanceof Map ? input.entries() : Object.entries(input);
  for (const [k, v] of entries) {
    if (typeof k !== "string" || !k) continue;
    if (typeof v !== "number" || !Number.isFinite(v) || v < 0) continue;
    out.set(k, v);
  }
  return out;
}
function buildSeedVector(graph, seedIds, options) {
  const seed = /* @__PURE__ */ new Map();
  const explicitWeights = normalizeSeedWeights(options.seedWeights);
  if (explicitWeights.size > 0) {
    const allowedKeys = seedIds.length > 0 ? new Set(seedIds.filter((id) => typeof id === "string")) : null;
    let total = 0;
    for (const [id, w] of explicitWeights) {
      if (!graph.nodes.has(id)) continue;
      if (allowedKeys !== null && !allowedKeys.has(id)) continue;
      seed.set(id, (seed.get(id) ?? 0) + w);
      total += w;
    }
    if (total > 0) {
      for (const [id, v] of seed) seed.set(id, v / total);
      return seed;
    }
    seed.clear();
  }
  const validSeeds = /* @__PURE__ */ new Set();
  for (const id of seedIds) {
    if (typeof id === "string" && graph.nodes.has(id)) validSeeds.add(id);
  }
  if (validSeeds.size > 0) {
    const share = 1 / validSeeds.size;
    for (const id of validSeeds) seed.set(id, share);
    return seed;
  }
  if (graph.nodes.size > 0) {
    const share = 1 / graph.nodes.size;
    for (const id of graph.nodes.keys()) seed.set(id, share);
  }
  return seed;
}
function buildAdjacency(graph) {
  const outgoing = /* @__PURE__ */ new Map();
  const outSum = /* @__PURE__ */ new Map();
  for (const edge of graph.edges) {
    if (!edge || typeof edge.from !== "string" || typeof edge.to !== "string") continue;
    if (!graph.nodes.has(edge.from) || !graph.nodes.has(edge.to)) continue;
    const weight = typeof edge.weight === "number" && Number.isFinite(edge.weight) && edge.weight > 0 ? edge.weight : 1;
    let bucket = outgoing.get(edge.from);
    if (!bucket) {
      bucket = [];
      outgoing.set(edge.from, bucket);
    }
    bucket.push({ to: edge.to, weight });
    outSum.set(edge.from, (outSum.get(edge.from) ?? 0) + weight);
  }
  return { outgoing, outSum };
}
function queryGraph(graph, seedIds, options = {}) {
  const n = graph.nodes.size;
  if (n === 0) {
    return { rankedNodes: [], iterations: 0, converged: true };
  }
  let damping = typeof options.damping === "number" ? options.damping : DEFAULT_PPR_DAMPING;
  if (!Number.isFinite(damping) || damping < 0) damping = 0;
  if (damping >= 1) damping = 1 - 1e-9;
  let maxIter = typeof options.iterations === "number" ? Math.floor(options.iterations) : DEFAULT_PPR_ITERATIONS;
  if (!Number.isFinite(maxIter) || maxIter < 0) maxIter = 0;
  let tolerance = typeof options.tolerance === "number" ? options.tolerance : DEFAULT_PPR_TOLERANCE;
  if (!Number.isFinite(tolerance) || tolerance < 0) tolerance = 0;
  const seed = buildSeedVector(graph, seedIds, options);
  const hasExplicitWeights = options.seedWeights !== void 0 && options.seedWeights !== null;
  if (!hasExplicitWeights) {
    let priorTotal = 0;
    for (const [id, s] of seed) {
      const node = graph.nodes.get(id);
      const w = node !== void 0 && typeof node.weight === "number" && Number.isFinite(node.weight) && node.weight > 0 ? node.weight : 1;
      const biased = s * w;
      seed.set(id, biased);
      priorTotal += biased;
    }
    if (priorTotal > 0) {
      for (const [id, s] of seed) seed.set(id, s / priorTotal);
    }
  }
  const { outgoing, outSum } = buildAdjacency(graph);
  let rank = /* @__PURE__ */ new Map();
  for (const id of graph.nodes.keys()) rank.set(id, seed.get(id) ?? 0);
  let seedTotal = 0;
  for (const v of seed.values()) seedTotal += v;
  if (seedTotal === 0) {
    return { rankedNodes: [], iterations: 0, converged: true };
  }
  let converged = false;
  let iter = 0;
  for (; iter < maxIter; iter++) {
    const next = /* @__PURE__ */ new Map();
    let danglingMass = 0;
    for (const [id, r] of rank) {
      if ((outSum.get(id) ?? 0) === 0) danglingMass += r;
    }
    const teleportScale = 1 - damping + damping * danglingMass;
    for (const [id, s] of seed) {
      next.set(id, (next.get(id) ?? 0) + teleportScale * s);
    }
    for (const [from, edges] of outgoing) {
      const r = rank.get(from) ?? 0;
      if (r === 0) continue;
      const total = outSum.get(from) ?? 0;
      if (total === 0) continue;
      const share = damping * r / total;
      for (const { to, weight } of edges) {
        next.set(to, (next.get(to) ?? 0) + share * weight);
      }
    }
    let delta = 0;
    for (const id of graph.nodes.keys()) {
      delta += Math.abs((next.get(id) ?? 0) - (rank.get(id) ?? 0));
    }
    rank = next;
    if (delta < tolerance) {
      converged = true;
      iter += 1;
      break;
    }
  }
  const ranked = [];
  for (const [id, score] of rank) {
    if (score > 0) ranked.push({ id, score });
  }
  ranked.sort((a, b) => {
    if (b.score !== a.score) return b.score - a.score;
    return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
  });
  const topK = options.topK;
  let trimmed;
  if (typeof topK === "number") {
    if (topK <= 0) {
      trimmed = [];
    } else if (topK < ranked.length) {
      trimmed = ranked.slice(0, topK);
    } else {
      trimmed = ranked;
    }
  } else {
    trimmed = ranked;
  }
  return { rankedNodes: trimmed, iterations: iter, converged };
}
var NODE_TYPES = /* @__PURE__ */ new Set([
  "memory",
  "entity",
  "episode",
  "concept",
  "reflection"
]);
var EDGE_TYPES = /* @__PURE__ */ new Set([
  "references",
  "supersedes",
  "authored-by",
  "mentions",
  "derived-from",
  "temporal-next",
  "related-to"
]);
function isNodeType(value) {
  return typeof value === "string" && NODE_TYPES.has(value);
}
function isEdgeType(value) {
  return typeof value === "string" && EDGE_TYPES.has(value);
}
var CITATION_REGEX = /\[Source:([^\]\n[]+)\]/gi;
function parseCitationFields(body) {
  const out = {};
  for (const rawPart of body.split(",")) {
    const part = rawPart.trim();
    if (!part) continue;
    const eq = part.indexOf("=");
    if (eq <= 0) continue;
    const key = part.slice(0, eq).trim().toLowerCase();
    const value = part.slice(eq + 1).trim();
    if (key && value) out[key] = value;
  }
  return out;
}
function stripDerivedFromVersion(ref) {
  const colon = ref.lastIndexOf(":");
  if (colon < 0) return ref;
  const tail = ref.slice(colon + 1);
  if (tail.length === 0) return ref.slice(0, colon);
  if (/^\d+$/.test(tail)) return ref.slice(0, colon);
  return ref;
}
function extractGraphEdges(memories, options = {}) {
  const includeDangling = options.includeDanglingEdges === true;
  const nodes = /* @__PURE__ */ new Map();
  const edges = [];
  const seenEdgeKeys = /* @__PURE__ */ new Set();
  const addNode = (id, type) => {
    if (!nodes.has(id)) nodes.set(id, { id, type });
  };
  const addEdge = (from, to, type) => {
    if (!from || !to || from === to) return;
    const key = `${from}\0${to}\0${type}`;
    if (seenEdgeKeys.has(key)) return;
    seenEdgeKeys.add(key);
    edges.push({ from, to, type });
  };
  for (const memory of memories) {
    if (!memory?.id) continue;
    addNode(memory.id, "memory");
  }
  const entityClaimed = /* @__PURE__ */ new Set();
  for (const memory of memories) {
    if (!memory?.id) continue;
    if (typeof memory.entityRef === "string" && memory.entityRef) {
      if (memory.entityRef !== memory.id) entityClaimed.add(memory.entityRef);
    }
    if (Array.isArray(memory.entityRefs)) {
      for (const ref of memory.entityRefs) {
        if (typeof ref === "string" && ref && ref !== memory.id) {
          entityClaimed.add(ref);
        }
      }
    }
    if (typeof memory.content === "string" && memory.content.length > 0) {
      CITATION_REGEX.lastIndex = 0;
      let match;
      while ((match = CITATION_REGEX.exec(memory.content)) !== null) {
        const body = match[1];
        if (!body) continue;
        const agent = parseCitationFields(body).agent;
        if (!agent) continue;
        const agentId = `agent:${agent}`;
        if (agentId !== memory.id) entityClaimed.add(agentId);
      }
    }
  }
  for (const memory of memories) {
    if (memory?.id) entityClaimed.delete(memory.id);
  }
  for (const memory of memories) {
    if (!memory?.id) continue;
    const from = memory.id;
    const canTargetMemory = (id) => {
      const existing = nodes.get(id);
      if (existing !== void 0) return existing.type === "memory";
      if (entityClaimed.has(id)) return false;
      return includeDangling;
    };
    if (typeof memory.supersedes === "string" && memory.supersedes) {
      const to = memory.supersedes;
      if (canTargetMemory(to)) {
        if (!nodes.has(to)) addNode(to, "memory");
        addEdge(from, to, "supersedes");
      }
    }
    if (Array.isArray(memory.lineage)) {
      for (const parent of memory.lineage) {
        if (typeof parent !== "string" || !parent) continue;
        if (!canTargetMemory(parent)) continue;
        if (!nodes.has(parent)) addNode(parent, "memory");
        addEdge(from, parent, "derived-from");
      }
    }
    if (Array.isArray(memory.derived_from)) {
      for (const raw of memory.derived_from) {
        if (typeof raw !== "string" || !raw) continue;
        const to = stripDerivedFromVersion(raw);
        if (!to) continue;
        if (!canTargetMemory(to)) continue;
        if (!nodes.has(to)) addNode(to, "memory");
        addEdge(from, to, "derived-from");
      }
    }
    const entitySet = /* @__PURE__ */ new Set();
    if (typeof memory.entityRef === "string" && memory.entityRef) {
      entitySet.add(memory.entityRef);
    }
    if (Array.isArray(memory.entityRefs)) {
      for (const ref of memory.entityRefs) {
        if (typeof ref === "string" && ref) entitySet.add(ref);
      }
    }
    for (const ref of entitySet) {
      const existing = nodes.get(ref);
      if (existing !== void 0 && existing.type !== "entity") continue;
      if (!existing) addNode(ref, "entity");
      addEdge(from, ref, "mentions");
    }
    if (typeof memory.content === "string" && memory.content.length > 0) {
      CITATION_REGEX.lastIndex = 0;
      let match;
      while ((match = CITATION_REGEX.exec(memory.content)) !== null) {
        const body = match[1];
        if (!body) continue;
        const fields = parseCitationFields(body);
        const agent = fields.agent;
        if (!agent) continue;
        const agentId = `agent:${agent}`;
        const existing = nodes.get(agentId);
        if (existing !== void 0 && existing.type !== "entity") continue;
        if (!existing) addNode(agentId, "entity");
        addEdge(from, agentId, "authored-by");
      }
    }
  }
  return { nodes, edges };
}
function buildGraphFromMemories(memories, options = {}) {
  const { nodes, edges } = extractGraphEdges(memories, options);
  return { nodes, edges };
}

export {
  DEFAULT_PPR_DAMPING,
  DEFAULT_PPR_ITERATIONS,
  DEFAULT_PPR_TOLERANCE,
  queryGraph,
  isNodeType,
  isEdgeType,
  extractGraphEdges,
  buildGraphFromMemories
};
//# sourceMappingURL=chunk-64NJRYU2.js.map