import {
  isSafeRouteNamespace
} from "./chunk-2LGMW3DJ.js";
import {
  sanitizeMemoryContent
} from "./chunk-FVQJYWH7.js";

// src/explicit-capture.ts
import { randomUUID } from "crypto";
var INLINE_NOTE_RE = /<memory_note>\s*([\s\S]*?)\s*<\/memory_note>/gi;
var INLINE_NOTE_MARKUP_RE = /<memory_note>\s*[\s\S]*?\s*<\/memory_note>/i;
var INLINE_ALLOWED_CATEGORIES = /* @__PURE__ */ new Set([
  "fact",
  "preference",
  "correction",
  "entity",
  "decision",
  "relationship",
  "principle",
  "commitment",
  "moment",
  "skill",
  "rule",
  "procedure",
  "reasoning_trace"
]);
var SECRET_PATTERNS = [
  /\bsk-[A-Za-z0-9]{16,}\b/,
  /\bAKIA[0-9A-Z]{16}\b/,
  /\bBearer\s+[A-Za-z0-9._-]{16,}\b/i,
  /\b(?:api[_-]?key|secret|token|password|passwd)\s*[:=]\s*[^\s]{8,}\b/i,
  /\b(?:authorization)\s*:\s*[^\s]{8,}\b/i
];
var SECRET_REDACTION_PATTERNS = [
  { pattern: /\bsk-[A-Za-z0-9]{16,}\b/g, replacement: "[redacted openai key]" },
  { pattern: /\bAKIA[0-9A-Z]{16}\b/g, replacement: "[redacted aws key]" },
  { pattern: /\bBearer\s+[A-Za-z0-9._-]{16,}\b/gi, replacement: "Bearer [redacted token]" },
  {
    pattern: /\b(?:api[_-]?key|secret|token|password|passwd)\s*[:=]\s*[^\s]{8,}\b/gi,
    replacement: "[redacted credential]"
  },
  {
    pattern: /\b(?:authorization)\s*:\s*[^\s]{8,}\b/gi,
    replacement: "authorization: [redacted credential]"
  }
];
var EXPLICIT_CAPTURE_REVIEW_TAGS = ["explicit-capture", "queued-review"];
function explicitCaptureActor(source) {
  switch (source) {
    case "inline":
      return "inline.memory_note";
    case "memory_store":
      return "tool.memory_store";
    case "suggestion_submit":
      return "tool.suggestion_submit";
    default:
      return "tool.memory_capture";
  }
}
function asTrimmed(value) {
  const trimmed = value?.trim();
  return trimmed && trimmed.length > 0 ? trimmed : void 0;
}
function normalizeCaptureContent(value) {
  return value.toLowerCase().replace(/\s+/g, " ").trim();
}
function redactSecrets(value) {
  let redacted = value;
  for (const { pattern, replacement } of SECRET_REDACTION_PATTERNS) {
    redacted = redacted.replace(pattern, replacement);
  }
  return redacted;
}
function normalizeExplicitCaptureError(error) {
  if (error instanceof Error && error.message.trim().length > 0) return error.message.trim();
  const rendered = String(error).trim();
  return rendered.length > 0 ? rendered : "explicit capture failed";
}
function resolveExplicitCaptureReviewNamespace(orchestrator, namespace) {
  const normalized = asTrimmed(namespace);
  if (!normalized) return void 0;
  return resolveExplicitCaptureNamespace(orchestrator, normalized);
}
function resolveExplicitCaptureNamespace(orchestrator, namespace) {
  const normalized = asTrimmed(namespace);
  if (!normalized) return void 0;
  if (!orchestrator.config.namespacesEnabled) {
    if (normalized !== orchestrator.config.defaultNamespace) {
      throw new Error(`unsupported namespace: ${normalized}`);
    }
    return normalized;
  }
  const allowed = new Set([
    orchestrator.config.defaultNamespace,
    orchestrator.config.sharedNamespace,
    ...orchestrator.config.namespacePolicies.map((policy) => policy.name)
  ].map((value) => value.trim()).filter(Boolean));
  if (!allowed.has(normalized)) {
    throw new Error(`unsupported namespace: ${normalized}`);
  }
  return normalized;
}
function parseExplicitCaptureTtl(ttl) {
  const raw = asTrimmed(ttl);
  if (!raw) return void 0;
  const absoluteMs = Date.parse(raw);
  if (Number.isFinite(absoluteMs)) {
    return new Date(absoluteMs).toISOString();
  }
  const relative = raw.match(/^(\d+)\s*([mhdw])$/i);
  if (!relative) {
    throw new Error("ttl must be an ISO-8601 timestamp or relative duration like 30m, 12h, 7d, or 2w");
  }
  const amount = Number.parseInt(relative[1] ?? "", 10);
  const unit = (relative[2] ?? "").toLowerCase();
  if (!Number.isFinite(amount) || amount <= 0) {
    throw new Error("ttl duration must be a positive integer");
  }
  const multiplier = unit === "m" ? 6e4 : unit === "h" ? 60 * 6e4 : unit === "d" ? 24 * 60 * 6e4 : 7 * 24 * 60 * 6e4;
  return new Date(Date.now() + amount * multiplier).toISOString();
}
function parseInlineNote(block) {
  const lines = block.replace(/\r/g, "").split("\n");
  const note = {};
  let idx = 0;
  while (idx < lines.length) {
    const rawLine = lines[idx] ?? "";
    const line = rawLine.trim();
    idx += 1;
    if (line.length === 0) continue;
    const colonIdx = line.indexOf(":");
    if (colonIdx < 0) continue;
    const key = line.slice(0, colonIdx).trim();
    const value = line.slice(colonIdx + 1).trim();
    if (key === "content" && value === "|") {
      const contentLines = [];
      while (idx < lines.length) {
        const next = lines[idx] ?? "";
        if (next.startsWith("  ") || next.startsWith("	")) {
          contentLines.push(next.replace(/^(  |\t)/, ""));
          idx += 1;
          continue;
        }
        if (next.trim().length === 0) {
          contentLines.push("");
          idx += 1;
          continue;
        }
        break;
      }
      note.content = contentLines.join("\n").trim();
      continue;
    }
    switch (key) {
      case "content":
        note.content = value;
        break;
      case "category":
        note.category = value;
        break;
      case "confidence":
        note.confidence = Number.parseFloat(value);
        break;
      case "namespace":
        note.namespace = value;
        break;
      case "tags":
        note.tags = value.split(",").map((entry) => entry.trim()).filter(Boolean);
        break;
      case "entityRef":
        note.entityRef = value;
        break;
      case "ttl":
        note.ttl = value;
        break;
      case "sourceReason":
        note.sourceReason = value;
        break;
      default:
        break;
    }
  }
  return asTrimmed(note.content) ? note : null;
}
function parseInlineExplicitCaptureNotes(text) {
  const notes = [];
  for (const match of text.matchAll(INLINE_NOTE_RE)) {
    const parsed = parseInlineNote(match[1] ?? "");
    if (parsed) notes.push(parsed);
  }
  return notes;
}
function hasInlineExplicitCaptureMarkup(text) {
  return INLINE_NOTE_MARKUP_RE.test(text);
}
function stripInlineExplicitCaptureNotes(text) {
  return text.replace(INLINE_NOTE_RE, "").trim();
}
function validateExplicitCaptureInput(input, mode = "strict_explicit") {
  const content = asTrimmed(input.content);
  if (!content) throw new Error("content is required");
  if (mode === "strict_explicit") {
    if (content.length < 10) throw new Error("content must be at least 10 characters");
    if (content.length > 4e3) throw new Error("content must be 4000 characters or fewer");
  }
  if (/<memory_note>/i.test(content) || /<\/memory_note>/i.test(content)) {
    throw new Error("nested memory_note blocks are not allowed");
  }
  const category = asTrimmed(input.category) ?? "fact";
  if (!INLINE_ALLOWED_CATEGORIES.has(category)) {
    throw new Error(`unsupported category: ${input.category ?? category}`);
  }
  const sanitized = sanitizeMemoryContent(content);
  if (!sanitized.clean) {
    throw new Error("content failed memory sanitization");
  }
  for (const pattern of SECRET_PATTERNS) {
    if (pattern.test(content)) {
      throw new Error("content appears to contain a secret or credential");
    }
  }
  const confidence = Number.isFinite(input.confidence) ? Number(input.confidence) : 0.95;
  if (confidence < 0 || confidence > 1) {
    throw new Error("confidence must be between 0 and 1");
  }
  const requestedNamespace = asTrimmed(input.namespace);
  if (requestedNamespace && !isSafeRouteNamespace(requestedNamespace)) {
    throw new Error(`unsafe namespace: ${requestedNamespace}`);
  }
  const expiresAt = parseExplicitCaptureTtl(input.ttl);
  return {
    content,
    category,
    confidence,
    namespace: asTrimmed(input.namespace),
    tags: Array.from(new Set((input.tags ?? []).map((tag) => tag.trim()).filter(Boolean))),
    entityRef: asTrimmed(input.entityRef),
    expiresAt,
    sourceReason: asTrimmed(input.sourceReason)
  };
}
async function findDuplicateExplicitCapture(orchestrator, resolvedNamespace, candidate) {
  const storage = await orchestrator.getStorage(resolvedNamespace);
  if (candidate.category === "fact" && typeof storage.hasFactContentHash === "function") {
    try {
      const hasHash = await storage.hasFactContentHash(
        candidate.content
      );
      if (!hasHash) {
        const authoritative = typeof storage.isFactContentHashAuthoritative === "function" ? await storage.isFactContentHashAuthoritative() : false;
        if (authoritative) return null;
      }
    } catch (err) {
      void err;
    }
  }
  const existing = await storage.readAllMemories();
  const normalizedCandidate = normalizeCaptureContent(candidate.content);
  const match = existing.find((memory) => {
    const status = memory.frontmatter.status ?? "active";
    if (status !== "active") return false;
    if (memory.frontmatter.category !== candidate.category) return false;
    return normalizeCaptureContent(memory.content) === normalizedCandidate;
  });
  return match?.frontmatter.id ?? null;
}
async function persistExplicitCapture(orchestrator, candidate, source) {
  const resolvedNamespace = resolveExplicitCaptureNamespace(orchestrator, candidate.namespace);
  const duplicateOf = await findDuplicateExplicitCapture(orchestrator, resolvedNamespace, candidate);
  if (duplicateOf) {
    return { id: duplicateOf, duplicateOf };
  }
  const storage = await orchestrator.getStorage(resolvedNamespace);
  const id = await storage.writeMemory(candidate.category, candidate.content, {
    confidence: candidate.confidence,
    tags: candidate.tags,
    entityRef: candidate.entityRef,
    expiresAt: candidate.expiresAt,
    source: source === "inline" ? "explicit-inline" : "explicit"
  });
  const created = (/* @__PURE__ */ new Date()).toISOString();
  const event = {
    eventId: `mle-${randomUUID()}`,
    memoryId: id,
    eventType: "explicit_capture_accepted",
    timestamp: created,
    actor: explicitCaptureActor(source),
    reasonCode: candidate.sourceReason,
    ruleVersion: "explicit-capture.v1"
  };
  await storage.appendMemoryLifecycleEvents([event]);
  return { id };
}
function buildExplicitCaptureReviewContent(input, reason) {
  const requestedContent = asTrimmed(input.content);
  const sanitized = sanitizeMemoryContent(redactSecrets(requestedContent ?? "[empty explicit capture]"));
  const safeContent = sanitized.text.trim().length > 0 ? sanitized.text.trim() : "[empty explicit capture]";
  const lines = [
    "Explicit capture queued for review.",
    "",
    `Reason: ${reason}`,
    "",
    "Submitted content:",
    safeContent
  ];
  const metadata = [
    input.category ? `Requested category: ${input.category}` : void 0,
    input.namespace ? `Requested namespace: ${input.namespace}` : void 0,
    input.entityRef ? `Requested entityRef: ${input.entityRef}` : void 0,
    input.ttl ? `Requested ttl: ${input.ttl}` : void 0,
    input.sourceReason ? `Requested sourceReason: ${input.sourceReason}` : void 0,
    input.tags && input.tags.length > 0 ? `Requested tags: ${input.tags.join(", ")}` : void 0
  ].filter((entry) => typeof entry === "string" && entry.length > 0);
  if (metadata.length > 0) {
    lines.push("", ...metadata);
  }
  return lines.join("\n");
}
async function findQueuedExplicitCaptureDuplicate(orchestrator, namespace, content) {
  const storage = await orchestrator.getStorage(namespace);
  const existing = await storage.readAllMemories();
  const normalized = normalizeCaptureContent(content);
  const match = existing.find((memory) => {
    const status = memory.frontmatter.status ?? "active";
    if (status !== "pending_review") return false;
    if (!(memory.frontmatter.tags ?? []).includes("queued-review")) return false;
    return normalizeCaptureContent(memory.content) === normalized;
  });
  return match?.frontmatter.id ?? null;
}
async function queueExplicitCaptureForReview(orchestrator, input, source, error) {
  const reason = normalizeExplicitCaptureError(error);
  const requestedNamespace = asTrimmed(input.namespace);
  const queueNamespace = resolveExplicitCaptureReviewNamespace(orchestrator, requestedNamespace);
  const content = buildExplicitCaptureReviewContent(input, reason);
  const duplicateOf = await findQueuedExplicitCaptureDuplicate(orchestrator, queueNamespace, content);
  if (duplicateOf) {
    return { id: duplicateOf, duplicateOf };
  }
  const requestedCategory = asTrimmed(input.category);
  const reviewCategory = requestedCategory && INLINE_ALLOWED_CATEGORIES.has(requestedCategory) ? requestedCategory : "fact";
  const requestedTags = Array.isArray(input.tags) ? input.tags.map((tag) => tag.trim()).filter(Boolean) : [];
  const storage = await orchestrator.getStorage(queueNamespace);
  const id = await storage.writeMemory(reviewCategory, content, {
    confidence: 0.2,
    tags: Array.from(/* @__PURE__ */ new Set([...EXPLICIT_CAPTURE_REVIEW_TAGS, ...requestedTags])),
    entityRef: asTrimmed(input.entityRef),
    source: source === "inline" ? "explicit-inline-review" : "explicit-review"
  });
  const created = await storage.getMemoryById(id);
  if (created) {
    await storage.writeMemoryFrontmatter(created, {
      status: "pending_review",
      updated: (/* @__PURE__ */ new Date()).toISOString()
    }, {
      actor: explicitCaptureActor(source),
      reasonCode: reason,
      ruleVersion: "explicit-capture.v1"
    });
  }
  const event = {
    eventId: `mle-${randomUUID()}`,
    memoryId: id,
    eventType: "explicit_capture_queued",
    timestamp: (/* @__PURE__ */ new Date()).toISOString(),
    actor: explicitCaptureActor(source),
    reasonCode: reason,
    ruleVersion: "explicit-capture.v1"
  };
  await storage.appendMemoryLifecycleEvents([event]);
  return { id };
}
function shouldSkipImplicitExtraction(cfg) {
  return cfg.captureMode === "explicit";
}
function shouldProcessInlineExplicitCapture(cfg) {
  return cfg.captureMode !== "implicit";
}

export {
  parseInlineExplicitCaptureNotes,
  hasInlineExplicitCaptureMarkup,
  stripInlineExplicitCaptureNotes,
  validateExplicitCaptureInput,
  persistExplicitCapture,
  queueExplicitCaptureForReview,
  shouldSkipImplicitExtraction,
  shouldProcessInlineExplicitCapture
};
//# sourceMappingURL=chunk-GVPWB7EY.js.map