diff --git a/extensions/index.ts b/extensions/index.ts index 52349d4..634c901 100644 --- a/extensions/index.ts +++ b/extensions/index.ts @@ -1,9 +1,12 @@ /** * pi-qmem — memoria centralizzata e condivisa per agenti AI (estensione pi). * - * Espone due tool: - * - qmem_store → salva un record di memoria (nessun LLM in scrittura) - * - qmem_search → ricerca semantica con filtri + * Espone quattro tool: + * - qmem_store → salva un record di memoria (nessun LLM in scrittura) + * - qmem_search → ricerca semantica con filtri + * - qmem_get → recupero deterministico di un record per UUID + * - qmem_correct → supersede di una memoria falsa o superata + * - qmem_meta → panoramica scope/kind/progetti/agenti * * Il gateway (FastAPI su brain.vpn:8082) usa una chiave condivisa con accesso * COMPLETO in lettura e scrittura all'intera conoscenza: qualsiasi agente può @@ -562,6 +565,72 @@ export default function qmemExtension(pi: ExtensionAPI) { }, }); + // ========================================================================= + // TOOL: qmem_get — recupero deterministico di un record per UUID + // ========================================================================= + pi.registerTool({ + name: "qmem_get", + label: "Qmem memory get by ID", + description: + "Recupera un record di memoria per UUID (recupero deterministico, non semantico). " + + "Usalo quando conosci gia' l'ID di un record (es. citato da un puntatore, dal playbook o da un altro record): " + + "qmem_search non puo' garantire di trovare il record giusto, qmem_get lo restituisce esattamente. " + + "Restituisce anche i record superseduti (utile per lineage/audit). " + + "Per trovare record senza conoscerne l'ID usa qmem_search.", + promptGuidelines: [ + "qmem_get: se un record cita un ID (es. 'record e526b65a'), usa qmem_get con quell'ID per recuperarlo esattamente — non tentare di indovinarlo con qmem_search.", + ], + parameters: Type.Object({ + memory_id: Type.String({ description: "UUID del record da recuperare (es. dalla risposta di qmem_search o da un puntatore)." }), + }), + async execute(toolCallId, params, signal, onUpdate, ctx) { + const cfg = loadConfig(); + if (!cfg.apiKey) { + return { + content: [{ type: "text", text: "Config mancante: esegui /qmem:config per impostare url e apiKey." }], + details: { error: "missing_config" }, + }; + } + const p = params as any; + onUpdate?.({ content: [{ type: "text", text: `qmem: recupero record ${p.memory_id}...` }] }); + const { ok, status, data } = await gatewayRequest(cfg, "GET", `/v1/memories/${p.memory_id}`, undefined, signal); + if (!ok) { + const notFound = status === 404 || data?.detail === "Memoria non trovata"; + return { + content: [ + { + type: "text", + text: notFound + ? `Record ${p.memory_id} non trovato (404). Verifica l'ID (qmem_search include_superseded=true per la lineage) oppure l'URL gateway.` + : `Errore ${status}: ${JSON.stringify(data)}`, + }, + ], + details: { error: notFound ? "not_found" : "gateway_error", status, memory_id: p.memory_id }, + }; + } + const r = data ?? {}; + const meta = [ + `[${r.kind ?? "?"}/${r.scope ?? "?"}${r.confidence ? ` conf=${r.confidence}` : ""}]`, + `project: ${r.project_id ?? "?"}`, + `agente: ${r.agent_id ?? "?"}, creato: ${r.created_at ?? "?"}${r.source ? `, fonte: ${r.source}` : ""}`, + ]; + if (r.supersedes_id) meta.push(`supersede ${r.supersedes_id}`); + if (r.superseded_by) meta.push(`⚠️ SUPERSEDUTO da ${r.superseded_by}`); + if (r.expires_at) meta.push(`scade: ${r.expires_at}`); + const lines = [`memory_id: ${r.memory_id ?? p.memory_id}`, meta.join(" | "), "", r.text ?? "(nessun testo)"]; + return { + content: [{ type: "text", text: lines.join("\n") }], + details: { + memory_id: r.memory_id ?? p.memory_id, + kind: r.kind, + scope: r.scope, + project_id: r.project_id, + superseded_by: r.superseded_by ?? null, + }, + }; + }, + }); + // ========================================================================= // COMANDO: /qmem:config — menu interattivo + modalità CLI rapida // ========================================================================= @@ -673,7 +742,7 @@ QUANDO un tool fallisce o un'operazione si blocca: pi.on("before_agent_start", async (event) => { const tools = event.systemPromptOptions?.selectedTools ?? []; - const hasQmem = ["qmem_store", "qmem_search", "qmem_correct", "qmem_meta"].some((t) => tools.includes(t)); + const hasQmem = ["qmem_store", "qmem_search", "qmem_get", "qmem_correct", "qmem_meta"].some((t) => tools.includes(t)); if (!hasQmem) return {}; return { systemPrompt: event.systemPrompt + QMEM_RULES }; });