From aefab445030afcc8b97938ba5b44d2d27f7cb53e Mon Sep 17 00:00:00 2001 From: Egor Date: Fri, 31 Jul 2026 11:52:31 +0300 Subject: [PATCH] fix(skills+memory): builtin handler fallback in executor, skip vector upsert for deleted memories - skills: Next.js compiles SkillExecutor into multiple chunks (own singleton each); route chunk lacked builtin handlers registered at startup via instrumentation. execute() now falls back to builtinSkills registry, so POST /api/skills/executions works for file_read/web_fetch/etc. - memory: scheduleVectorUpsert is fire-and-forget and embeddings are slow; health-check verify (create->delete test memory) left queued upserts failing with 'memory not found' every 30s. Check existence before embedding and skip quietly. --- src/lib/memory/store.ts | 11 +++++++++++ src/lib/skills/executor.ts | 15 ++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/lib/memory/store.ts b/src/lib/memory/store.ts index 549f61a800..ad1c53edb1 100644 --- a/src/lib/memory/store.ts +++ b/src/lib/memory/store.ts @@ -122,6 +122,17 @@ function safeMarkNeedsReindex(id: string, needs: boolean): void { function scheduleVectorUpsert(id: string, content: string): void { setImmediate(async () => { try { + // The upsert is fire-and-forget and embeddings are slow (potion loads + // lazily). Health-check verification (and user deletes) can remove the + // memory before this callback runs — skip quietly instead of spamming + // "memory not found" warnings every sweep interval. + const db = getDbInstance(); + const exists = db.prepare("SELECT rowid FROM memories WHERE id = ?").get(id); + if (!exists) { + log.debug("memory.vec.upsert.skipped_deleted", { id }); + return; + } + const settings = await getMemorySettings(); const resolution = resolveEmbeddingSource(settings); if (!resolution.source) return; diff --git a/src/lib/skills/executor.ts b/src/lib/skills/executor.ts index 8d7db16810..692716d485 100644 --- a/src/lib/skills/executor.ts +++ b/src/lib/skills/executor.ts @@ -1,5 +1,6 @@ import { skillRegistry } from "./registry"; import { SkillExecution, SkillStatus, SkillHandler } from "./types"; +import { builtinSkills } from "./builtins"; import { getDbInstance } from "../db/core"; import { getSettings } from "../db/settings"; import { randomUUID } from "crypto"; @@ -73,7 +74,19 @@ class SkillExecutor { new Date().toISOString() ); - const handler = this.handlers.get(skill.handler); + let handler = this.handlers.get(skill.handler); + if (!handler) { + // Builtin handlers are registered by instrumentation-node at startup, + // but Next.js may compile this module into multiple chunks (each with + // its own SkillExecutor singleton). Fall back to the builtin registry + // so `POST /api/skills/executions` works regardless of which chunk the + // route is served from. + const builtin = builtinSkills[skill.handler]; + if (builtin) { + this.handlers.set(skill.handler, builtin); + handler = builtin; + } + } if (!handler) { throw new Error(`Handler not found: ${skill.handler}`); }