Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,43 @@

All notable changes to MemForge are documented here.

## [Unreleased] — Memory Sentiment Tagging + Adaptive Sleep Intelligence

### Added

- **Memory Sentiment Tagging (F6)** — pure keyword-heuristic inference of
`urgency` (`low | medium | high | critical`), `sentiment`
(`positive | negative | neutral`), and `session_type`
(`debug | plan | review | explore | build | unknown`) at write time.
Stored in the new `context_signals JSONB` column on both `hot_tier` and
`warm_tier`. During consolidation, signals from all contributing hot rows
are merged: urgency = maximum, sentiment = majority vote, session_type =
majority vote. The merged signals are returned in `query()` results as
`context_signals` on each `QueryResult`. No LLM call required; runs
synchronously inside `add()`. New types `UrgencyLevel`, `SentimentTag`,
`SessionType`, `ContextSignals` in `src/types.ts`.

- **Adaptive Sleep Intelligence (F5) — analytics infrastructure** — new
`sleep_phase_analytics` table (`agent_id`, `phase`, `duration_ms`,
`tokens_used`, `changes_made`, `created_at`) and two new public methods
on `SleepCycleEngine`: `recordPhaseAnalytics()` (writes one telemetry
row per phase call) and `shouldSkipPhase()` (returns `true` when the
last 3 recorded runs for that phase all had `changes_made = 0`).
Index `sleep_phase_analytics_agent_idx` on
`(agent_id, created_at DESC)`. RLS agent isolation policy mirrors the
rest of the schema. **Wiring of these helpers into the `run()` loop is
deferred to a follow-up** — this PR ships the schema, helpers, and
unit-level test coverage so the wiring change has a stable target.

### Migration

- `schema/migration-v3.8.sql` — applies `sleep_phase_analytics` table,
`hot_tier.context_signals JSONB NOT NULL DEFAULT '{}'`, and
`warm_tier.context_signals JSONB NOT NULL DEFAULT '{}'`. Idempotent
(`IF NOT EXISTS` / `IF NOT EXISTS` on the `ADD COLUMN` statements).

---

## [3.7.0] - 2026-05-21 — Claude Dreaming Layer 4: Anthropic Memory Store Bridge

This is the first npm release since `3.0.0-beta.4`. It rolls up four
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@
"test:dreams-compat": "node --import tsx/esm --test tests/dreams-compat.test.ts",
"test:dreams-anthropic": "node --import tsx/esm --test tests/dreams-anthropic.test.ts",
"test:dreams-bridge": "node --import tsx/esm --test tests/dreams-bridge.test.ts",
"test": "node --import tsx/esm --test --test-concurrency=1 tests/integration.test.ts tests/llm-paths.test.ts tests/http-api.test.ts tests/cache.test.ts tests/embedding-migration.test.ts tests/outcome-revision.test.ts tests/reflection-revision.test.ts tests/selective-forgetting.test.ts tests/multi-device.test.ts tests/dream-runs.test.ts tests/dreams-compat.test.ts tests/dreams-anthropic.test.ts tests/dreams-bridge.test.ts",
"test": "node --import tsx/esm --test --test-concurrency=1 tests/integration.test.ts tests/llm-paths.test.ts tests/http-api.test.ts tests/cache.test.ts tests/embedding-migration.test.ts tests/outcome-revision.test.ts tests/reflection-revision.test.ts tests/selective-forgetting.test.ts tests/multi-device.test.ts tests/dream-runs.test.ts tests/dreams-compat.test.ts tests/dreams-anthropic.test.ts tests/dreams-bridge.test.ts tests/sentiment-tagging.test.ts tests/adaptive-sleep.test.ts",
"test:multi-device": "node --import tsx/esm --test tests/multi-device.test.ts",
"test:sentiment-tagging": "node --import tsx/esm --test tests/sentiment-tagging.test.ts",
"test:adaptive-sleep": "node --import tsx/esm --test tests/adaptive-sleep.test.ts",
"benchmark:longmemeval": "node --import tsx/esm benchmarks/longmemeval/run.ts",
"benchmark:download": "node --import tsx/esm benchmarks/longmemeval/download.ts",
"benchmark:ingest": "node --import tsx/esm benchmarks/longmemeval/ingest.ts",
Expand Down
60 changes: 60 additions & 0 deletions schema/migration-v3.8.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
-- MemForge — Migration v3.8: Sentiment Tagging + Adaptive Sleep Intelligence
--
-- Features:
-- F5: Adaptive Sleep Intelligence (sleep_phase_analytics table)
-- F6: Memory Sentiment Tagging (context_signals on hot_tier and warm_tier)
--
-- Apply: psql "$DATABASE_URL" -f schema/migration-v3.8.sql

BEGIN;

-- ─── Feature 5: Adaptive Sleep Intelligence ─────────────────────────────────
--
-- Records per-phase telemetry for each sleep cycle run. The sleep cycle engine
-- reads the last 3 records per (agent, phase) to decide whether to skip a
-- phase that has produced zero changes across all recent runs — avoiding wasted
-- work on idle agents.

CREATE TABLE IF NOT EXISTS sleep_phase_analytics (
id BIGSERIAL PRIMARY KEY,
agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
phase TEXT NOT NULL,
duration_ms INTEGER NOT NULL,
tokens_used INTEGER NOT NULL DEFAULT 0,
changes_made INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS sleep_phase_analytics_agent_idx
ON sleep_phase_analytics (agent_id, created_at DESC);

ALTER TABLE sleep_phase_analytics ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS sleep_phase_analytics_agent_isolation ON sleep_phase_analytics;
CREATE POLICY sleep_phase_analytics_agent_isolation ON sleep_phase_analytics
FOR ALL
USING (agent_id = current_setting('app.current_agent_id', true))
WITH CHECK (agent_id = current_setting('app.current_agent_id', true));

-- ─── Feature 6: Memory Sentiment Tagging ────────────────────────────────────
--
-- JSONB column storing urgency, sentiment, and session_type signals inferred
-- from content at write time. On hot_tier the signals are per-event; during
-- consolidation the signals from all contributing hot rows are merged into the
-- warm_tier row (urgency = max, sentiment = majority, session_type = majority).

ALTER TABLE hot_tier
ADD COLUMN IF NOT EXISTS context_signals JSONB NOT NULL DEFAULT '{}';

ALTER TABLE warm_tier
ADD COLUMN IF NOT EXISTS context_signals JSONB NOT NULL DEFAULT '{}';

-- ─── Grants for memforge_app role (if exists) ───────────────────────────────

DO $$ BEGIN
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'memforge_app') THEN
EXECUTE 'GRANT ALL ON sleep_phase_analytics TO memforge_app';
EXECUTE 'GRANT USAGE, SELECT ON SEQUENCE sleep_phase_analytics_id_seq TO memforge_app';
END IF;
END $$;

COMMIT;
48 changes: 39 additions & 9 deletions schema/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,18 @@ CREATE TABLE IF NOT EXISTS agents (
-- Added in v2.4: content_hash
-- Added in v3.1: namespace
-- Added in v3.5: session_id (per-device isolation; default 'default')
-- Added in v3.8: context_signals (urgency/sentiment/session_type via keyword heuristics)
-- ─────────────────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS hot_tier (
id BIGSERIAL PRIMARY KEY,
agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
content TEXT NOT NULL,
metadata JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
content_hash TEXT,
namespace TEXT NOT NULL DEFAULT 'default',
session_id TEXT NOT NULL DEFAULT 'default'
id BIGSERIAL PRIMARY KEY,
agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
content TEXT NOT NULL,
metadata JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
content_hash TEXT,
namespace TEXT NOT NULL DEFAULT 'default',
session_id TEXT NOT NULL DEFAULT 'default',
context_signals JSONB NOT NULL DEFAULT '{}'
);

CREATE INDEX IF NOT EXISTS hot_tier_agent_id_idx ON hot_tier (agent_id);
Expand All @@ -60,6 +62,7 @@ CREATE INDEX IF NOT EXISTS hot_tier_session_idx ON hot_tier (agent_id, name
-- content_code_tsv, summary (v2.5)
-- v2.6: surprise_score, staleness_score, last_corroborated
-- v3.1: namespace
-- v3.8: context_signals (merged from contributing hot rows at consolidation time)
-- ─────────────────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS warm_tier (
id BIGSERIAL PRIMARY KEY,
Expand Down Expand Up @@ -105,7 +108,9 @@ CREATE TABLE IF NOT EXISTS warm_tier (
embedding_model TEXT,
-- Originating hot-tier session for provenance (v3.5) — NULL = consolidated before
-- per-session tracking, distinct from the literal 'default' session.
session_id TEXT
session_id TEXT,
-- Sentiment tagging (v3.8) — merged from contributing hot rows: urgency=max, others=majority
context_signals JSONB NOT NULL DEFAULT '{}'
);

CREATE INDEX IF NOT EXISTS warm_tier_agent_id_idx ON warm_tier (agent_id);
Expand Down Expand Up @@ -667,6 +672,24 @@ CREATE TABLE IF NOT EXISTS anthropic_memory_stores (
CREATE INDEX IF NOT EXISTS anthropic_memory_stores_agent_ns_idx
ON anthropic_memory_stores (agent_id, namespace);

-- ─────────────────────────────────────────────────────────────────────────────
-- sleep_phase_analytics (v3.8+) — per-phase telemetry for each sleep cycle run.
-- The sleep cycle reads the last 3 records per (agent_id, phase) to decide
-- whether to skip a phase that produced zero changes in every recent run.
-- ─────────────────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS sleep_phase_analytics (
id BIGSERIAL PRIMARY KEY,
agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
phase TEXT NOT NULL,
duration_ms INTEGER NOT NULL,
tokens_used INTEGER NOT NULL DEFAULT 0,
changes_made INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS sleep_phase_analytics_agent_idx
ON sleep_phase_analytics (agent_id, created_at DESC);

-- ─────────────────────────────────────────────────────────────────────────────
-- Row-Level Security (v3.0+ fresh installs — backported from migration-v2.3)
-- FORCE ROW LEVEL SECURITY is intentionally omitted on all tables.
Expand Down Expand Up @@ -836,6 +859,13 @@ CREATE POLICY anthropic_memory_stores_agent_isolation ON anthropic_memory_stores
USING (agent_id = current_setting('app.current_agent_id', true))
WITH CHECK (agent_id = current_setting('app.current_agent_id', true));

ALTER TABLE sleep_phase_analytics ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS sleep_phase_analytics_agent_isolation ON sleep_phase_analytics;
CREATE POLICY sleep_phase_analytics_agent_isolation ON sleep_phase_analytics
FOR ALL
USING (agent_id = current_setting('app.current_agent_id', true))
WITH CHECK (agent_id = current_setting('app.current_agent_id', true));

-- ─────────────────────────────────────────────────────────────────────────────
-- Service role that bypasses RLS
-- ─────────────────────────────────────────────────────────────────────────────
Expand Down
Loading