Repository navigation
Releases
v0.12.4
Corrige un error de cierre de compromisos. Un registro que resolvía un
compromiso abierto no lo cerraba cuando el clasificador de duplicados lo marcaba
antes como "complementa" a ese compromiso: remember.mjs trataba esa decisión
automática igual que un --complements escrito a mano y lo dejaba fuera del juicio
de cierre. El compromiso seguía en list-commitments.mjs aunque ya estuviera
resuelto. Un clasificador con confianza 1.0 sí lo habría cerrado; nunca llegó a
verlo.
remember.mjs: solo lo que pasas con--complementso--supersedes
queda fuera del juicio de cierre. Lo que decide el clasificador de duplicados
entra al juicio.remember-batch.mjs(el camino de la extracción automática y del hook
Stop): ahora también cierra compromisos resueltos, con el mismo clasificador y
umbral de confianza (0.85). Antes nunca lo hacía. El cierre es reversible con
unsupersede-record.mjs, y si el clasificador falla no se cierra nada.- Servidor MCP: el aviso de compromisos abiertos que devuelve
remember
ya no oculta el que el clasificador marcó como "complementa". El servidor sigue
sin cerrar compromisos por sí solo.
Para actualizar: redespliega la Edge Function mcp-server con el index.ts
nuevo. No hace falta apply-schema.mjs ni reiniciar el dashboard. Los
compromisos que ya quedaron abiertos por este error no se cierran solos: revisa
list-commitments.mjs y cierra a mano con supersede-record.mjs los que ya
estén resueltos.
v0.12.3
Corrige un error de v0.12.2: extract-page-records.mjs fallaba con
ReferenceError al ejecutarse sin --provider, porque la declaración de TASK
quedó dentro de un comentario. extract-records.mjs no estaba afectado.
Gemini elegible por tarea. Hay dos pasos para actualizar:
- Redespliega la Edge Function
mcp-servercon elindex.tsnuevo. - Reinicia el servidor del dashboard.
No hace falta apply-schema.mjs: el esquema no cambia, solo la semilla de
settings, que nunca pisa lo que ya tienes (on conflict do nothing). Una
instalación existente conserva su fila available_models sin la lista de Gemini:
los scripts y el dashboard la completan solos desde config/gemini-models.json,
pero set_task_model del servidor MCP rechazará gemini::... hasta que la
agregues. Solo si quieres elegir Gemini desde el MCP, corre en el editor SQL:
update settings
set value = value || '{"gemini": ["gemini-2.5-flash", "gemini-flash-latest", "gemini-3.7-flash", "gemini-3.6-flash", "gemini-3.5-flash"]}'::jsonb
where key = 'available_models';- Gemini en los cinco grupos de "Modelos por tarea". El dashboard ofrece
Gemini además de Ollama Cloud y OpenRouter paraclassifiers,extraction,
synthesisydeepSweep, con el mismo selectorproveedor::modelo. - Grupo nuevo
mentionSecondOpinion. La segunda opinión del clasificador de
menciones escalaba a Gemini escrito a mano en el código, sin que el usuario lo
viera. Ahora es un grupo editable, congemini::gemini-flash-latestpor
defecto, que es lo que hacía antes. lib/llm-call.mjs. Una sola función,getTaskLlm(grupo), llama al modelo
del grupo sea cual sea el proveedor. Los clasificadores (duplicados, memoria,
alias, compromisos, menciones, sugerencia de alias y de nombre de categoría)
dejan de repetir cada uno su propioif (openrouter) ... else fetch(ollama), que
era la razón por la queclassifiersno podía ofrecer Gemini: bastaba un
archivo olvidado para mandar un modelo de Gemini a Ollama.synthesize.mjsymemory-status.mjsrespetan el prefijoproveedor::
del modelo del gruposynthesis.- Los extractores usan el modelo de Gemini del grupo
extractioncomo primero
de su lista de respaldo cuando ese grupo apunta a Gemini. - Servidor MCP:
set_task_modelaceptaproveedor::modelo(ollama,
openrouter o gemini) y lo valida contra la lista de modelos disponibles de ese
proveedor;get_settingsmuestra el grupo nuevo. MyMCP solo guarda la elección:
quien llama a Gemini son los scripts locales, y los clasificadores del propio
MCP siguen corriendo en Ollama aunque el grupo apunte a otro proveedor, con un
aviso en su respuesta. El servidor MCP también pasa la marcais_meta
al clasificador de recuerdos, igual queremember.mjsyremember-batch.mjs
desde v0.12.0.
v0.12.2
Mejoras al extractor de registros (extract-records.mjs, el que corre el hook
Stop en segundo plano, y extract-page-records.mjs). No hay pasos
obligatorios: no cambia el esquema ni el servidor MCP.
- Las ventanas largas ya no pierden el final. Si la conversación supera el
límite del proveedor (20.000 caracteres en Ollama), antes se cortaba el final,
justo donde están las decisiones más recientes. Ahora se parte en trozos de
turnos completos, cada trozo se extrae por separado y los candidatos se unen,
sin repetir un mismo texto. El bloque "Ya registrado en esta ventana" va
completo en cada trozo. Un solo turno más largo que el límite sigue
recortándose. Con--dump-promptse ve solo el primer trozo. - El prompt del extractor excluye lo que ya queda escrito en otro lado. No se
registran los commits y su contenido, que un script se ejecutó o qué imprimió,
cambios de configuración que se explican al leer el archivo, resultados de
chequeos rutinarios sin novedad, citas de registros que ya existen,
explicaciones del asistente sobre cómo funciona el sistema de memoria,
advertencias que el usuario no adoptó, reuniones que ya están en el calendario
ni ecos mecánicos de la propia captura. Se mantienen las excepciones: una
decisión del usuario, un hallazgo no obvio, un compromiso con fecha o un
cambio de estado que el repositorio no conserva. Se quitó del ejemplo del
prompt una cita de calendario. - El prompt ahora le dice al modelo que no repita lo ya registrado. El código
del extractor ya agregaba el bloque "Ya registrado en esta ventana", pero el
prompt por defecto no tenía la regla que le indica al modelo qué hacer con él. - Proveedor por defecto. Sin
--provider, ambos extractores usan el
proveedor del grupo "extraction" del dashboard (Modelos por tarea) en vez de
Gemini fijo. El hookStopya pasaba--provider ollama, así que su
comportamiento no cambia. Gemini sigue disponible con--provider gemini. - Gemini:
gemini-2.5-flashpasa al inicio del orden de respaldo en
config/gemini-models.json.
v0.12.1
Retira de v0.12.0 todo lo relacionado con la jardinería de etiquetas. Esa
parte entró por error: sigue en pruebas y calibración y no estaba lista para
publicarse. v0.12.1 conserva solo la reescritura de remember.mjs, el ajuste de
fecha de remember-batch.mjs y la marca is_meta del clasificador.
No hay pasos obligatorios. Si ya actualizaste a v0.12.0:
apply-schema.mjsno se vuelve a correr: v0.12.1 no cambia el esquema. Las
tablasparent_proposalsyblind_name_sightingsy la columna
memories.descriptionque v0.12.0 creó siguen en tu base, vacías y sin uso;
no estorban y no hace falta borrarlas.- Los archivos retirados desaparecen al actualizar el repositorio:
propose-categories.mjs,propose-parents.mjs,set-memory-description.mjs,
lib/propose-parent.mjs,lib/judge-coverage.mjsylib/llm-call.mjs. garden.mjsvuelve a la versión de v0.11.0: sin--parent-proposalsni
--parent-proposal, y el contador de propuestas pendientes vuelve a contar
también las de registros ya reemplazados..claude/settings.jsonyCLAUDE.mdvuelven a lo de v0.11.0.
Se quedan de v0.12.0: remember.mjs reescrito (registro, recuerdos y cierre en
una sola transacción; avisa si falla memories_similar; fecha de hoy con
formatToParts), el mismo ajuste de fecha en remember-batch.mjs, y la marca
is_meta en el clasificador de recuerdos.
v0.12.0
Hay dos pasos para actualizar, en este orden:
node scripts/db/apply-schema.mjs(crea las tablasparent_proposalsy
blind_name_sightings, ambas con RLS activo, y la columna
memories.description; es idempotente).- Reinicia el servidor del dashboard.
No hay cambios incompatibles: remember.mjs conserva las mismas opciones. No
hace falta redesplegar la Edge Function mcp-server.
remember.mjsreescrito. Mismo comportamiento de cara al usuario, un
archivo mucho más corto (409 líneas contra 688). El registro, sus recuerdos y el
cierre de lo que reemplaza se guardan en una sola transacción, así que un
fallo a medias ya no deja un registro sin sus recuerdos. Si la consulta de
recuerdos parecidos (memories_similar) falla, el script avisa y sigue sin
sugerencias, en vez de caer sin tratar o afirmar que no hay embeddings. La fecha
de hoy se calcula conformatToParts, que funciona también con Node con ICU
reducido (en-CAdevolvía otro formato y la fecha de hoy nunca coincidía con
--date).remember-batch.mjsrecibe el mismo ajuste de fecha.- Propuestas de padre (nuevo). Un recuerdo sin lugar en la jerarquía
pertenece_a, o uno nuevo que nace de un cúmulo de registros, recibe tres
padres candidatos, del más al menos probable. Nada se aplica solo, igual que las
propuestas de etiqueta.propose-parents.mjspropone padre para recuerdos que existen y están fuera
de toda jerarquía. Por defecto solo muestra; con--queuedeja las propuestas
pendientes en la tabla nuevaparent_proposals. El recuerdousuarioqueda
excluido por defecto.propose-categories.mjspropone recuerdos nuevos: el modelo da un nombre
sin ver los recuerdos existentes (3 corridas, deben coincidir 2), un juez de
cobertura (lib/judge-coverage.mjs) descarta los que un recuerdo existente ya
cubre, y se exige un apoyo mínimo (--min-support, 7) y que converjan al menos 2 registros (--min-records). Con--queueguarda
enblind_name_sightingslos nombres de un solo registro, para que la
convergencia cuente también corridas de días anteriores (--memory-days).
Los umbrales no están calibrados contra un criterio externo: tómalos como
punto de partida.set-memory-description.mjsescribe la ficha de definición de un recuerdo
(memories.description), quepropose-parent.mjsusa para ubicarlo.- Dónde se deciden: con
garden.mjs --parent-proposals(lista) y
--parent-proposal <id> accept [1|2|3|nombre] | reject | undo. Aceptar crea el
enlacepertenece_ay, si el recuerdo es nuevo, lo crea y etiqueta los
registros que lo originaron; rechaza ciclos. El dashboard todavía no tiene
la pantalla "Propuestas de padre": por ahora se decide solo por consola.
lib/llm-call.mjs. Una sola función,getTaskLlm(grupo), para llamar al
modelo de un grupo de "Modelos por tarea" (Ollama Cloud, OpenRouter o Gemini).
Hoy la usan las propuestas de padre; los clasificadores existentes siguen con su
llamada propia. La rama de Gemini solo se activa cuando un grupo tenga Gemini
elegido; en esta versióntask-models.mjstodavía no lo ofrece.- Recuerdos marcados
is_metaen el clasificador. El clasificador de recuerdos
recibe la marca y, para un recuerdo sobre el propio sistema, no se deja llevar
por la mención literal ni por el parecido de ejemplos.remember-batch.mjsla
pasa igual queremember.mjs. - Revisión de etiquetas: el contador de propuestas pendientes de
garden.mjs
ignora las de registros ya reemplazados, que inflaban el total sin aparecer
nunca en la lista. - Claude Code:
.claude/settings.jsonsuma reglasaskpara
garden.mjs --parent-proposal, yCLAUDE.mdlo nombra junto a
--proposalen el protocolo del hook Stop.
v0.11.0
Cambio incompatible: remember.mjs ya no crea recuerdos ni acepta
--create-memory para crearlos, --aliases, --no-suggest-aliases ni
--memories-confirmed. Si tienes scripts propios o instrucciones que los usan,
crea antes el recuerdo con create-memory.mjs (ver la Fase 5 de CLAUDE.md).
Hay cuatro pasos, en este orden:
node scripts/db/apply-schema.mjs(crea la tablamemory_proposals, con RLS
activo, y cambia la función de búsquedarecords_search; es idempotente).- Redespliega la Edge Function
mcp-servercon elindex.tsnuevo:remember
cambia de comportamiento y hay dos herramientas nuevas, y sin redesplegar el
servidor MCP sigue aplicando etiquetas solo. - Reinicia el servidor del dashboard.
CLAUDE.mdcambia en dos lugares fuera de "Cómo trabajar con el usuario":
el "Protocolo del hook Stop" y la Fase 5 de la instalación. Aplícalos a mano.
- Ninguna etiqueta se aplica sin que la aceptes. Antes el clasificador de
recuerdos podía agregar solo un recuerdo adicional a un registro, o crear uno
nuevo. Una etiqueta equivocada puesta así se reforzaba sola: el clasificador usa
los registros de cada recuerdo como ejemplos, así que un recuerdo contaminado
atraía cada vez más registros ajenos. Ahora el registro se guarda con los
recuerdos que pediste y existen, y todo lo demás (un recuerdo que no existe, o
lo que sugiera el clasificador) queda como propuesta pendiente en la tabla
nuevamemory_proposals, con su número. - Dónde se deciden las propuestas: en el dashboard, Revisión de etiquetas >
Propuestas pendientes (Aceptar / Descartar, y Deshacer en "Decididas
recientemente"), o congarden.mjs --proposals,--proposal <id> accept|reject|undoy--decided. Aceptar una propuesta de recuerdo nuevo lo
crea. - Servidor MCP:
rememberya no crea recuerdos ni aplica etiquetas no
pedidas, y lista las propuestas en su respuesta. Herramientas nuevas:
list_memory_proposals(solo lee) ydecide_memory_proposal(acepta o
descarta). Dejadecide_memory_proposalen "Needs approval" en tu cliente
MCP: el servidor no puede saber si una decisión la tomaste tú o el modelo, así
que esa aprobación es la única que de verdad espera tu clic.rememberademás
corre sus clasificadores en paralelo y responde más rápido. - Claude Code:
.claude/settings.jsonnuevo con reglasaskpara que Claude
pida permiso antes de corrergarden.mjs --proposal,recategorize-record.mjs
yedit-record.mjs ... --memory. Son un respaldo, no una barrera: otra forma de
escribir el comando las evita, por eso la regla principal sigue enCLAUDE.md.
create-memory.mjsqueda fuera a propósito: la instalación lo corre muchas
veces seguidas. - El dashboard y la extracción de páginas siguen creando recuerdos cuando tú
lo marcas: como ahí decide el usuario, crean el recuerdo concreate-memory.mjs
antes de guardar (Guardar registro con "Crear recuerdo", Extraer con
createMemory, yextract-page-records.mjs --review). - Búsqueda más precisa:
records_searchya no premia los registros de un
recuerdo solo porque su nombre o alias comparta una palabra con la pregunta.
Bastaba una palabra genérica ("proyecto", o "iniciar" que coincide con
"iniciativa" por raíz) para que esos registros desplazaran al resultado
correcto. Los recuerdos nombrados en la pregunta los sigue trayendo aparte el
detector de recuerdos desearch.mjsy del servidor MCP, por alias exacto: si
sueles nombrar un recuerdo con una palabra corta (una sigla, por ejemplo),
agrégala como alias conset-memory-aliases.mjs. - Fix propio: la copia de
segundo-cerebro-captureen.claude/skills/(la
que Claude Code carga) se había quedado atrás de la deskills/; ahora son
iguales. - Cómo se verificó:
node --checksobre todos los.mjstocados y
deno checksobreindex.ts, sin errores. El sistema de propuestas y el cambio
de búsqueda se probaron primero en la instancia de desarrollo contra una base
real (cola desde la extracción y desde MCP, aceptar, descartar, deshacer, y
búsquedas con y sin palabras genéricas). Esta copia de 2oBrain no se probó
contra una base: el port se hizo fusionando los mismos cambios sobre los
archivos de 2oBrain.
v0.10.9
Hay tres pasos, en este orden:
node scripts/db/apply-schema.mjs(crea las tablas nuevassettingsy
settings_history, con RLS activo y sembradas con los valores actuales de
config/task-models.json; es idempotente y nunca pisa un valor ya editado).- Redespliega la Edge Function
mcp-servercon elindex.tsnuevo: las
herramientas nuevas y la lectura del modelo desde la base viven ahí, y sin
redesplegar el servidor MCP sigue con su modelo escrito a mano. - Reinicia el servidor del dashboard.
- El modelo de IA por tarea ahora vive en la base, no en cada máquina. Antes
la elección de modelo para cada grupo de tareas (classifiers, extraction,
synthesis, deepSweep) se guardaba enconfig/task-models.jsonde cada máquina,
y el servidor MCP tenía la suya escrita a mano: dos máquinas, o una máquina y
el MCP, podían usar modelos distintos para la misma tarea sin que nadie lo
notara. Ahora hay una sola fila en la base (settings.task_models) que leen por
igual los scripts y el servidor MCP. Los scripts la sincronizan al arrancar,
con un caché local de 10 minutos (state/task-models.cache.local.json, fuera de
git); si la base no responde en 3 segundos usan el caché anterior o, si nunca
hubo,config/task-models.json, que queda solo como respaldo. - El dashboard escribe en la base. Cambiar un modelo en la sección de modelos
por tarea ya no editaconfig/task-models.json: escribe en la base y deja el
cambio ensettings_history. Si la base no responde, el cambio falla visible en
vez de guardarse solo en esa máquina. - El servidor MCP expone dos herramientas nuevas:
get_settings(muestra el
modelo de cada grupo, los disponibles por proveedor y los últimos cambios) y
set_task_model(cambia el modelo de un grupo). - Aviso de seguridad:
set_task_modelcambia el modelo para TODAS las
máquinas y para el propio servidor MCP a la vez. Tiene salvaguardas (solo
modelos de la lista de disponibles, solo Ollama Cloud, motivo obligatorio,
historial ensettings_historyy bloqueo optimista contra cambios
simultáneos), pero en la app de Claude conviene dejar esa herramienta en
"Needs approval" para que nunca se ejecute sin que la confirmes. - Cómo se verificó:
node --checksobre los dos.mjstocados y
deno checksobreindex.ts, sin errores. Con una copia de prueba se comprobó
que, sin base alcanzable, los scripts caen aconfig/task-models.json(en modo
nube al instante; en local tras esperar los 3 segundos del límite) y que
setTaskModelrechaza un modelo que no está en la lista de disponibles. No se
probó contra una base real: niapply-schema.mjs, ni la sincronización
real, ni el guardado desde el dashboard, niget_settings/set_task_model
desplegados.
v0.10.8
Sin migración de schema.sql y sin pasos a mano, solo reinicia el servidor del
dashboard (el cambio está en el servidor, no en la página).
- Corrige las vistas Lista, Gráfico y Heatmap del Timeline y el Grafo, que
dejaban de cargar cuando la base crecía. El servidor del dashboard corre cada
script como subproceso yspawnSynclimita su salida a 1 MiB por defecto. El
JSON de/api/timeliney de/api/graphsupera ese límite hacia los 1.500 o
1.700 registros: el subproceso se mataba (ENOBUFS) y el dashboard respondía
422 sin explicar por qué, así que las vistas quedaban vacías sin ningún aviso.
Ahora el límite es de 256 MiB, y si un subproceso no termina por sí mismo el
error se devuelve enstderren vez de un 422 mudo. Si tu base es chica no lo
habías notado, pero habría aparecido con el uso. - Cómo se verificó: el fallo se reprodujo primero (los dos endpoints daban 422
con salidas de 1,10 MB y 1,05 MB,statusnulo yENOBUFS), y tras el cambio
los dos devolvieron 200 con los mismos tamaños; en el navegador cargaron las
tres vistas del Timeline sin errores nuevos de consola.
v0.10.7
Hay un paso de migración y un reinicio:
node scripts/db/apply-schema.mjs(crea la tabla nuevarecord_reviews, con
RLS activo; es idempotente).- Reinicia el servidor del dashboard: la página se recarga sola, pero los
endpoints nuevos (/api/garden/...) solo existen tras reiniciar.
- Revisión de etiquetas (nueva sección del dashboard, grupo Recuerdos). Para
revisar a mano si los registros recientes quedaron en el recuerdo correcto, sin
depender de que ningún modelo opine sobre el asunto:- Muestrear registros automáticos: trae al azar registros de la extracción
automática que nadie ha revisado. No detecta errores por sí mismo, mide qué
tan seguido el etiquetado se equivoca. - Señales de texto: registros con un indicio concreto (solo tiene recuerdos
paraguas, el texto nombra a otro recuerdo que no tiene, o alguno de sus
recuerdos tiene 2 registros o menos). - Una tabla por tarjeta: cada etiqueta actual se decide por separado
(Correcto, Mover a otro recuerdo o Quitar), se pueden añadir etiquetas
nuevas, y todo se aplica junto con Aplicar y un solo motivo. No se deja
el registro sin ninguna etiqueta. Todas correctas guarda el veredicto de
una vez. Cada revisión se guarda enrecord_reviewsy el registro no vuelve a
salir; arriba se muestra la tasa de error observada (no concluir con menos de
unas 30 revisiones). - También por consola:
node scripts/db/garden.mjs --sample,--stats,
--verdict <id> ok|corrected.
- Muestrear registros automáticos: trae al azar registros de la extracción
recategorize-record.mjscon dos modos nuevos:--remove(quita una
etiqueta sin reasignarla, nunca la única) y--add <recuerdo>(agrega una
etiqueta sin tocar las que ya tiene).--addno crea enlaces entre recuerdos:
si el registro queda con dos recuerdos sin enlace,doctor.mjs --fixlo
resuelve.- La señal "paraguas" arranca vacía. En
garden.mjs,UMBRELLAes la lista de
tus recuerdos demasiado amplios para decir de qué trata un registro; ponla ahí
si la quieres usar. - Cómo se verificó: se levantó el dashboard de 2oBrain en un puerto de prueba
contra una base real. Los tres endpoints de lectura (estadísticas, muestreo y
señales) respondieron bien y la sección se renderizó en el navegador sin
errores de consola; solo se hicieron lecturas desde esta copia. Las escrituras
(Aplicar con mover, quitar y añadir, y las guardas) se probaron en la copia de
trabajo de origen antes de portar.
v0.10.6
Sin migración de schema.sql, sin redespliegue del servidor MCP y sin pasos a
mano: recarga la página del dashboard (el servidor lee index.html en cada
carga, no hace falta reiniciarlo).
- Grafo: la búsqueda ahora avisa cuando no encuentra nada. Antes, buscar
algo que no estaba en el grafo no mostraba ningún mensaje: el botón "Ir" se
quedaba sin efecto visible. Ahora aparece un aviso bajo la barra de búsqueda:- Vista por recuerdos: si el recuerdo no existe, dice que puede haberse
fusionado, renombrado o borrado, y sugiere "Refrescar". La búsqueda ya no
distingue mayúsculas de minúsculas como último recurso. - Vista por registros, búsqueda por número (
#123,#94, #95): distingue
tres casos que antes se veían igual: el registro no existe en la base
(purgado o nunca existió), existe pero está retractado o reemplazado
(indica por cuál), o existe y el filtro "Ocultar registros
supersedidos/retractados" lo esconde del grafo. Un registro vigente no
muestra aviso. - Vista por registros, texto libre sin resultados: avisa y sugiere buscar
por número.
El aviso se limpia al cambiar de vista o de búsqueda.
- Vista por recuerdos: si el recuerdo no existe, dice que puede haberse
- Cómo se verificó: el dashboard se levantó en un puerto de prueba contra una
base real y se probaron en el navegador los siete casos (recuerdo existente e
inexistente, id purgado, retractado, reemplazado, vigente y mezcla de ids),
más el caso del filtro de ocultar, sin errores de consola. La rama de "texto
libre sin resultados" no se pudo provocar, porque la búsqueda híbrida casi
siempre devuelve algo.