feat(vibe): adapt sessions to context and exploration
Typecheck / typecheck (backend) (push) Has been cancelled
Typecheck / typecheck (workers) (push) Has been cancelled
Typecheck / typecheck (backend) (pull_request) Has been cancelled
Typecheck / typecheck (workers) (pull_request) Has been cancelled

This commit is contained in:
kami
2026-08-02 02:08:35 +04:00
parent fe13798c99
commit 61a1373ca9
14 changed files with 782 additions and 16 deletions
+16
View File
@@ -43,3 +43,19 @@ describe('Vibe durable session migration', () => {
expect(migration!.sql).toContain('idx_vibe_events_user_occurred');
});
});
describe('Vibe context memory migration', () => {
it('adds bounded session exploration state and exactly-once projection storage', () => {
const migration = MIGRATIONS.find(({ id }) => id === '20260802_vibe_context_memory_exploration');
expect(migration?.sql).toContain('CREATE TABLE IF NOT EXISTS vibe_session_profiles');
expect(migration?.sql).toContain('exploration_coefficient');
expect(migration?.sql).toContain('vibe_session_feedback_projections');
});
it('backfills profiles for durable sessions created before context memory', () => {
const migration = MIGRATIONS.find(({ id }) => id === '20260802_vibe_session_profile_backfill');
expect(migration?.sql).toContain('INSERT INTO vibe_session_profiles');
expect(migration?.sql).toContain('SELECT id, user_id FROM vibe_sessions');
expect(migration?.sql).toContain('ON CONFLICT (session_id) DO NOTHING');
});
});
+39
View File
@@ -720,4 +720,43 @@ export const MIGRATIONS: Migration[] = [
);
`,
},
{
// Session-specific exploration, goals, and deliberately lossy session
// fingerprints are derived from the immutable Vibe ledger. Keeping them
// separate from listener_beliefs prevents a transient session from
// rewriting permanent taste.
id: '20260802_vibe_context_memory_exploration',
sql: `
CREATE TABLE IF NOT EXISTS vibe_session_profiles (
session_id UUID PRIMARY KEY REFERENCES vibe_sessions(id) ON DELETE CASCADE,
user_id UUID NOT NULL,
fingerprint JSONB NOT NULL DEFAULT '{}'::jsonb,
goals JSONB NOT NULL DEFAULT '{"type":"discovery","target":1,"progress":0}'::jsonb,
exploration_coefficient REAL NOT NULL DEFAULT 0.30
CHECK (exploration_coefficient >= 0 AND exploration_coefficient <= 1),
discovery_radius REAL NOT NULL DEFAULT 0.38
CHECK (discovery_radius >= 0 AND discovery_radius <= 1),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_vibe_session_profiles_user_updated
ON vibe_session_profiles (user_id, updated_at DESC);
CREATE TABLE IF NOT EXISTS vibe_session_feedback_projections (
event_id UUID PRIMARY KEY REFERENCES vibe_events(id) ON DELETE CASCADE,
projected_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
`,
},
{
// The profile table was introduced after durable sessions. Backfill every
// pre-existing session before feedback can claim its exactly-once marker;
// newly created sessions receive their context-derived initial goals in
// createVibeSession's transaction.
id: '20260802_vibe_session_profile_backfill',
sql: `
INSERT INTO vibe_session_profiles (session_id, user_id)
SELECT id, user_id FROM vibe_sessions
ON CONFLICT (session_id) DO NOTHING;
`,
},
];
+25
View File
@@ -543,6 +543,23 @@ CREATE TABLE IF NOT EXISTS vibe_sessions (
CREATE INDEX IF NOT EXISTS idx_vibe_sessions_user_last_event
ON vibe_sessions (user_id, last_event_at DESC);
-- Compact, derived memory for the session director. Fingerprints are a
-- deliberately lossy description of session shape (not a track list) and are
-- used only as a soft freshness signal against recent sessions.
CREATE TABLE IF NOT EXISTS vibe_session_profiles (
session_id UUID PRIMARY KEY REFERENCES vibe_sessions(id) ON DELETE CASCADE,
user_id UUID NOT NULL,
fingerprint JSONB NOT NULL DEFAULT '{}'::jsonb,
goals JSONB NOT NULL DEFAULT '{"type":"discovery","target":1,"progress":0}'::jsonb,
exploration_coefficient REAL NOT NULL DEFAULT 0.30
CHECK (exploration_coefficient >= 0 AND exploration_coefficient <= 1),
discovery_radius REAL NOT NULL DEFAULT 0.38
CHECK (discovery_radius >= 0 AND discovery_radius <= 1),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_vibe_session_profiles_user_updated
ON vibe_session_profiles (user_id, updated_at DESC);
CREATE TABLE IF NOT EXISTS vibe_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
client_event_id UUID,
@@ -570,6 +587,14 @@ CREATE TABLE IF NOT EXISTS vibe_event_projections (
projected_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Separate from listener-belief projection because exploration is session
-- state. It lets a failed post-event replan safely retry the exact same
-- adaptation without counting the feedback twice.
CREATE TABLE IF NOT EXISTS vibe_session_feedback_projections (
event_id UUID PRIMARY KEY REFERENCES vibe_events(id) ON DELETE CASCADE,
projected_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS vibe_plan_versions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
session_id UUID NOT NULL REFERENCES vibe_sessions(id) ON DELETE CASCADE,
+13
View File
@@ -258,3 +258,16 @@ export interface VibePlanItem {
export interface VibePlan extends VibePlanVersion {
items: VibePlanItem[];
}
/** Derived session-director memory. The ledger remains authoritative; this
* compact row makes fingerprints, bounded goals, and exploration state cheap
* to read while planning. */
export interface VibeSessionProfile {
session_id: string;
user_id: string;
fingerprint: Record<string, unknown>;
goals: Record<string, unknown>;
exploration_coefficient: number;
discovery_radius: number;
updated_at: Date;
}