Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Heuristic gate for session bundles produced by `buildSessions`.
*
* `runHeuristicGate(session)` returns a binary decision (`KEEP` or `SKIP`)
* so the orchestrator can drop sessions that are structurally guaranteed
* to have no extractable content.
*
* This gate intentionally only rejects sessions where structure alone
* proves there is nothing to extract (no queries, no usable titles, no
* meaningful chat). Anything with even a single query or a meaningful
* title is handed to the downstream LLM step, which is far better at
* judging whether the content is worth remembering.
*
* Cross-modal policy is permissive: for a session that has both browse
* activity and chat messages, the gate keeps the session if either
* modality individually passes its checks. The intuition is that
* cross-modal sessions are by construction more contextual than
* single-modal, so a substantive chat carries an otherwise-trivial browse
* session and vice versa.
*/
import {
GATE_KEEP,
GATE_SKIP,
GENERIC_TITLES,
NAV_TITLE_PATTERNS,
SKIP_ONLY_DOMAINS,
SEARCH_ENGINE_DOMAINS,
TRIVIAL_MESSAGES,
MIN_TITLE_LENGTH,
MIN_MESSAGE_LENGTH,
} from "./MemoriesConstants.sys.mjs";
/**
* Decide whether a session bundle should be processed by the memory
* pipeline or dropped as trivial.
*
* @param {object} session A session bundle produced by `buildSessions`.
* @returns {{decision: "KEEP"|"SKIP", reason: string}}
* `KEEP` means the session is worth running through downstream LLM
* steps. `SKIP` means drop it. `reason` is a short string suitable for
* logging; empty string when `KEEP`.
*/
export function runHeuristicGate(session) {
const hasBrowse = session.visit_count + session.search_count > 0;
const hasChat = session.chat_count > 0;
if (!hasBrowse && !hasChat) {
return { decision: GATE_SKIP, reason: "empty session" };
}
const browseReason = hasBrowse ? checkBrowse(session) : null;
const chatReason = hasChat ? checkChat(session) : null;
// Permissive policy: only skip when every present modality would skip.
if (hasBrowse && hasChat) {
if (browseReason !== null && chatReason !== null) {
return {
decision: GATE_SKIP,
reason: `browse: ${browseReason}; chat: ${chatReason}`,
};
}
} else if (hasBrowse && browseReason !== null) {
return { decision: GATE_SKIP, reason: browseReason };
} else if (hasChat && chatReason !== null) {
return { decision: GATE_SKIP, reason: chatReason };
}
return { decision: GATE_KEEP, reason: "" };
}
function checkBrowse(session) {
const queryCount = session.search_queries.length;
const domains = session.domains;
const hasDomains = !!domains.length;
// Pure SERP bounce: only search-engine hosts, no queries surfaced.
if (
queryCount === 0 &&
hasDomains &&
domains.every(d => SEARCH_ENGINE_DOMAINS.has(d))
) {
return "search-engine pages with no queries";
}
// Nav-only hosts (auth, shorteners, click-trackers) with no query
// signal to redeem them.
if (
queryCount === 0 &&
hasDomains &&
domains.every(d => SKIP_ONLY_DOMAINS.has(d))
) {
return "only navigation/auth domains";
}
// Structural zero-content: no queries and no usable titles. Anything
// with even one query or one meaningful title is handed to the LLM.
if (queryCount === 0) {
const meaningfulTitles = session.titles.filter(
t =>
t.length >= MIN_TITLE_LENGTH &&
!GENERIC_TITLES.has(t) &&
!NAV_TITLE_PATTERNS.some(re => re.test(t))
);
if (meaningfulTitles.length === 0) {
return "no meaningful titles or queries";
}
}
return null;
}
function checkChat(session) {
const hasMeaningful = session.chats.some(msg => {
const body =
typeof msg.content === "string" ? msg.content.trim().toLowerCase() : "";
return body.length >= MIN_MESSAGE_LENGTH && !TRIVIAL_MESSAGES.has(body);
});
if (!hasMeaningful) {
return "trivial messages only";
}
return null;
}