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.
This commit is contained in:
Egor
2026-07-31 11:52:31 +03:00
parent c605cabf08
commit aefab44503
2 changed files with 25 additions and 1 deletions

View File

@@ -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;

View File

@@ -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}`);
}