Prima qmem_store falliva se il gateway non era raggiungibile: la conoscenza andava persa. Ora il record entra in una coda locale persistente e viene inviato automaticamente quando la connessione torna. Core (extensions/local-db.ts): - tabella `pending` (local_id, payload JSON, attempts, last_error, status, remote_id) + colonna `records.pending` (migrazione automatica dei DB esistenti) - queueStore(): accoda e crea subito il placeholder locale ricercabile (⏳) - flushQueue(): POST /v1/memories con Idempotency-Key = local_id (retry senza duplicati), FIFO, pacing sotto il rate limit, timeout 12s per richiesta - esiti: synced (il record locale adotta l'ID remoto, niente duplicati) · duplicate (409: registra l'ID del match e NON sovrascrive il testo locale autorevole) · failed (4xx di validazione, non ritentato) · 0/429/5xx: resta in coda e il flush si ferma - supersede offline: supersedes_id che punta a un local_id viene rimappato al remote_id al flush (se il genitore non è sincronizzato → failed esplicito) - submitOrQueue(): online → gateway + indicizzazione locale; offline → coda - maybeBackgroundFlush() (single-flight) e flushQueueIfPending() per session_start - stato/report: queued/synced/duplicate/failed, più vecchio, ultimo errore, last_flush, record pendenti in indice Estensione: - qmem_store: gateway giù → accoda e risponde con id locale, dimensione coda e spiegazione (details.queued/local_id/queue_size) - fallback offline di session_start: flush in background (non blocca l'avvio) - /qmem:local queue|flush; status con la coda; marker "⏳ in coda" nei risultati locali di qmem_search/qmem_get - regole e skill: un record in coda NON è ancora nella memoria condivisa CLI: store [--queue-only], queue, flush (+ status con la coda). Test: scripts/test-local.mjs ora copre anche outbox → 24 controlli (flush con 2 sync + 1 duplicato 409 + 1 fallito 422, Idempotency-Key, rimappatura del supersede, ricerca del record con l'ID remoto dopo il sync). Verifiche: 24/24 test superati; demo reale su DB temporaneo: store accodato, queue con local_id, flush con gateway giù → "fermato: HTTP 0" e voce che resta in coda con l'errore registrato.
52 lines
1.9 KiB
TypeScript
52 lines
1.9 KiB
TypeScript
/**
|
|
* pi-qmem — entrypoint estensione.
|
|
*
|
|
* La logica è divisa per responsabilità: shared/client, sei tool, comando
|
|
* di configurazione e regole operative.
|
|
*/
|
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
import { registerQmemConfig } from "./config-command";
|
|
import { registerQmemCorrect } from "./tools/correct";
|
|
import { registerQmemGet } from "./tools/get";
|
|
import { registerQmemMeta } from "./tools/meta";
|
|
import { registerQmemSearch } from "./tools/search";
|
|
import { registerQmemStore } from "./tools/store";
|
|
import { registerQmemTree } from "./tools/tree";
|
|
import { registerQmemRules } from "./rules";
|
|
import { registerQmemLocal } from "./local-command";
|
|
import { flushQueueIfPending, localDbPath } from "./local-db.ts";
|
|
import { loadConfig } from "./shared.ts";
|
|
|
|
export default function qmemExtension(pi: ExtensionAPI) {
|
|
registerQmemStore(pi);
|
|
registerQmemSearch(pi);
|
|
registerQmemCorrect(pi);
|
|
registerQmemMeta(pi);
|
|
registerQmemGet(pi);
|
|
registerQmemTree(pi);
|
|
registerQmemConfig(pi);
|
|
registerQmemLocal(pi);
|
|
registerQmemRules(pi);
|
|
|
|
// Outbox: al ritorno della connessione (nuova sessione/reload) i record
|
|
// accodati offline vengono inviati al gateway. In background: l'avvio della
|
|
// sessione non deve mai attendere la rete.
|
|
pi.on("session_start", async (_event, ctx) => {
|
|
const cfg = loadConfig();
|
|
if (cfg.offlineQueue === false) return;
|
|
void (async () => {
|
|
try {
|
|
const res = await flushQueueIfPending(cfg, localDbPath(cfg));
|
|
if (res && (res.synced || res.duplicates || res.failed)) {
|
|
ctx.ui.notify(
|
|
`Outbox qmem: ${res.synced} sincronizzati, ${res.duplicates} duplicati, ${res.failed} falliti${res.remaining ? `, ${res.remaining} in coda` : ""}${res.stopped ? ` (fermato: ${res.stopped})` : ""}`,
|
|
res.synced ? "info" : "warning",
|
|
);
|
|
}
|
|
} catch {
|
|
/* best effort: la coda resta e verrà ritentata */
|
|
}
|
|
})();
|
|
});
|
|
}
|