Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .lore.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
* **Always ask assistant to recall prior session context before continuing work**: At session start, proactively recall prior session context (bugs, decisions, implementation state, filenames, paths) before proceeding. Distinguish 'confirmed from prior session' vs 'inferred'. Surface known details upfront; explicitly flag memory gaps. Reproduce details precisely — treat it as a trust/accuracy check. User does not re-explain context; they probe whether the assistant retained it. When a detail cannot be confirmed, infer from known project conventions and label it as inferred. Capture decision rationale at decision time — when no recorded rationale exists, flag the gap rather than present inference as fact. For retrospective 'why' questions, recall or reconstruct original rationale; never present inference as fact. When asked about specific values (URLs, filenames, algorithm choices, config values), confirm exactly or admit the gap — never guess. State confirmed details separately from unknowns.

<!-- lore:019e3d47-9d1e-76f9-a067-403264cd3bd6 -->
* **Always enforce strict API error handling and coding style rules without exception**: Non-negotiable API coding rules (no exceptions): (1) Inline try/catch on every DB call — no per-route middleware. (2) Semantic HTTP status codes: 400=validation, 404=not found, 500=server/DB errors. (3) Validation error responses: \`{ "errors": \["message"] }\`. (4) \`const\` by default, \`let\` ONLY when binding is reassigned. (5) Raw parameterized SQL via \`pg\` with positional placeholders (\`$1\`, \`$2\`) — ALL ORMs, Prisma, query builders banned project-wide. (6) Hono framework only — Express banned. (7) Flag any rule conflict before writing code. (8) Bug fixed → write test targeting exact failure scenario. (9) Never silently use a banned tool. (10) Never re-propose an explicitly rejected approach. (11) Always write tests for every endpoint immediately after implementation. These apply across all projects without exception — if user mentions a different framework or ORM, correct them.
* **Always enforce strict API error handling and coding style rules without exception**: Non-negotiable API coding rules (no exceptions): (1) Inline try/catch on every DB call — no per-route middleware. (2) Semantic HTTP status codes: 400=validation, 404=not found, 500=server/DB errors. (3) Validation error responses: \`{ "errors": \["message"] }\`. (4) \`const\` by default, \`let\` ONLY when binding is reassigned. (5) Raw parameterized SQL via \`pg\` with positional placeholders (\`$1\`, \`$2\`) — ALL ORMs, Prisma, query builders banned project-wide. (6) Hono framework only — Express banned. (7) Flag any rule conflict before writing code. (8) Bug fixed → write test targeting exact failure scenario immediately — never defer tests. (9) Never silently use a banned tool. (10) Never re-propose an explicitly rejected approach. (11) Tests go in \`\_\_tests\_\_/\` with kebab-case filenames, written right after each implementation. These apply across all projects without exception.

<!-- lore:019e3d81-5780-7904-a247-e82a0a390d86 -->
* **Always enforce strict React/TypeScript project conventions without exception**: Non-negotiable React/TypeScript conventions (all projects, no exceptions): (1) kebab-case file naming — never PascalCase or camelCase; (2) named exports only — default exports banned; (3) functional components with hooks only — no class components; (4) TypeScript strict mode: \`strict: true\` + \`noImplicitAny: true\`, \`@typescript-eslint/no-explicit-any\` as CI-failing error — never use \`any\`, always \`unknown\`; (5) tests after implementation in \`\_\_tests\_\_/\` with kebab-case filenames. Apply all conventions upfront when scaffolding.
Expand All @@ -84,5 +84,8 @@
<!-- lore:019e2820-3ed0-7cc0-97a7-2c654df763ec -->
* **IDs starting with LOREAI-GATEWAY- are Sentry issue IDs**: Any identifier starting with \`LOREAI-GATEWAY-\` (e.g. \`LOREAI-GATEWAY-F\`) is a Sentry issue ID for the gateway project. Always treat these as Sentry issue references when encountered in conversation — fetch the issue via Sentry CLI/API to get stack traces, user counts, and release info before investigating the codebase.

<!-- lore:019e3f87-bb32-70b1-adb5-117dc1fe7bf1 -->
* **Never use ORMs or query builders — always use raw parameterized SQL via \`pg\`**: The user has a non-negotiable, project-wide ban on all ORMs (including Prisma) and query builders. Every database query must use raw parameterized SQL through the \`pg\` library with positional placeholders (\`$1\`, \`$2\`, etc.). Every DB call must be wrapped in an inline \`try/catch\` block — no per-route middleware. This rule applies to every endpoint, every query, and every project without exception. The user's stated reason: ORMs add too much abstraction and make debugging harder — they want to know exactly what SQL is running. Never suggest or scaffold ORM/query-builder alternatives.

<!-- lore:019e2168-2fa4-77bd-a557-9d6dbcb40d81 -->
* **Prefer WASM backend over native onnxruntime-node for compiled binaries**: WASM backend for Bun \`--compile\` binaries with transformers.js: \`binaryExternalsPlugin\` in esbuild redirects \`onnxruntime-node\` → \`onnxruntime-web\` via \`onResolve\` (static imports only — does NOT redirect dynamic \`import()\` calls) and patches transformers.js CDN fallback via \`onLoad\` to read \`wasmPaths\` from \`globalThis.\_\_LORE\_VENDOR\_WASM\_PATHS\_\_\` (object form \`{ mjs, wasm }\` with exact hashed \`$bunfs\` filenames — directory strings fail because Bun hashes bundled WASM filenames). WASM files embedded as Bun \`{ type: 'file' }\` assets in the wrapper; wrapper sets \`globalThis.\_\_LORE\_VENDOR\_WASM\_PATHS\_\_\` before importing the worker. No onnxruntime import in wrapper or worker. For npm/CJS builds, \`onnxruntime-node\` stays external. WASM is ~2x faster on batches than native. Importing \`onnxruntime-web\` explicitly alongside the redirect creates two ort instances — 'cannot register backend cpu using priority 10' error.
77 changes: 75 additions & 2 deletions packages/core/src/curator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { config } from "./config";
import { saveSessionTracking, loadSessionTracking, ensureProject } from "./db";
import { db, saveSessionTracking, loadSessionTracking, ensureProject } from "./db";
import * as temporal from "./temporal";
import * as ltm from "./ltm";
import * as log from "./log";
Expand Down Expand Up @@ -221,7 +221,18 @@ async function runInner(input: {
log.warn("instruction-detect failed (non-fatal):", err);
}

const userContent = baseUserContent + crossSessionContext;
// Lightweight cross-session context: count action tag occurrences
// from distillation observations across the project. This gives the
// curator a compact signal about repeated behaviors without the noise
// of full recall results.
let actionTagContext = "";
try {
actionTagContext = buildActionTagContext(input.projectPath, input.sessionID);
} catch (err) {
log.warn("action tag context failed (non-fatal):", err);
}

const userContent = baseUserContent + crossSessionContext + actionTagContext;
const model = input.model ?? cfg.model;
const responseText = await input.llm.prompt(
CURATOR_SYSTEM,
Expand Down Expand Up @@ -269,6 +280,68 @@ async function runInner(input: {
return result;
}

// ---------------------------------------------------------------------------
// Lightweight cross-session context from action tags
// ---------------------------------------------------------------------------

/**
* Scan distillation observations for action tags and count their occurrence
* across distinct sessions. Returns a compact summary like:
*
* "Cross-session behavioral patterns detected:
* - [requested-tests] appeared in 4 sessions
* - [corrected-style] appeared in 3 sessions"
*
* This helps the curator recognize implicit preferences from repeated behavior
* without the noise of full recall results.
*/
function buildActionTagContext(
projectPath: string,
currentSessionID: string,
): string {
const pid = ensureProject(projectPath);

// Get all distillation observations for this project
const rows = db()
.query(
"SELECT session_id, observations FROM distillations WHERE project_id = ?",
)
.all(pid) as Array<{ session_id: string; observations: string }>;

if (!rows.length) return "";

// Count action tags across distinct sessions (exclude current session)
const tagSessions = new Map<string, Set<string>>();
const tagRe = /\[([a-z]+-[a-z-]+)\]/g;

for (const row of rows) {
if (row.session_id === currentSessionID) continue;
tagRe.lastIndex = 0;
let match: RegExpExecArray | null;
while ((match = tagRe.exec(row.observations)) !== null) {
const tag = match[1];
if (!tagSessions.has(tag)) tagSessions.set(tag, new Set());
tagSessions.get(tag)!.add(row.session_id);
}
}

// Filter to tags that appeared in 2+ sessions (emerging patterns)
const significant = [...tagSessions.entries()]
.filter(([, sessions]) => sessions.size >= 2)
.sort((a, b) => b[1].size - a[1].size);

if (!significant.length) return "";

const lines = significant.map(
([tag, sessions]) => `- [${tag}] appeared in ${sessions.size} prior sessions`,
);

return (
"\n\n---\nCross-session behavioral patterns detected (consider creating preference entries for these):\n" +
lines.join("\n")
);
}

export function resetCurationTracker(sessionID?: string) {
if (sessionID) {
lastCuratedAt.delete(sessionID);
Expand Down
Loading