feat(memory): implement stability stage
Some checks failed
CI / validate (push) Has been cancelled
CI / validate (pull_request) Has been cancelled

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
2026-09-02 15:04:18 +05:00
parent 4cd0b7b390
commit 031d094867
24 changed files with 1661 additions and 79 deletions

View File

@@ -1,6 +1,6 @@
import { describe, expect, it } from 'vitest'
import type { Character, PlayerIntent, RoundPlan } from '@dng/shared'
import { buildActionSequence, buildRoundContext, sanitizeRoundMemory, shouldUpdateStorySummary, validateAndOrderRoundPlan } from './orchestration'
import { buildActionSequence, buildRetrievalText, buildRoundContext, sanitizeRoundMemory, shouldUpdateStorySummary, validateAndOrderRoundPlan } from './orchestration'
const character = (id: string, controller: Character['controller']): Character => ({
id, controller, name: id, concept: 'A campaign hero', userId: controller === 'human' ? 'user' : null,
@@ -61,11 +61,44 @@ describe('round orchestration', () => {
})
it('drops model-authored memory labels that PostgreSQL cannot cast to uuid[]', () => {
const knownId = '9ea0ef06-34bf-4cf3-8f2f-af1a65180d0e'
expect(sanitizeRoundMemory({
summary: 'The party learned who controls the gate.',
importance: 4,
tags: ['gate'],
entityIds: ['the-gatekeeper', '9ea0ef06-34bf-4cf3-8f2f-af1a65180d0e', '9ea0ef06-34bf-4cf3-8f2f-af1a65180d0e'],
})?.entityIds).toEqual(['9ea0ef06-34bf-4cf3-8f2f-af1a65180d0e'])
entityIds: ['the-gatekeeper', knownId, knownId, '8ea0ef06-34bf-4cf3-8f2f-af1a65180d0e'],
}, new Set([knownId]))?.entityIds).toEqual([knownId])
})
it('builds a bounded retrieval query from only current-round context', () => {
const text = buildRetrievalText({
scene: 'The sealed moon gate is humming.',
intents: [intent('ready', 'hero', '2026-01-01T00:00:00.000Z'), { ...intent('draft', 'hero', '2026-01-01T00:00:01.000Z'), ready: false }],
characters: [character('hero', 'human')],
})
expect(text).toContain('sealed moon gate')
expect(text).toContain('Action ready')
expect(text).not.toContain('Action draft')
})
it('accepts typed relationship, quest, and scene projections only for known entities', () => {
const questId = '11111111-1111-4111-8111-111111111111'
const locationId = '22222222-2222-4222-8222-222222222222'
const context = buildRoundContext({
scene: 'Scene', recentRounds: [], intents: [], characters: [character('hero', 'human')], memories: [],
entities: [
{ id: questId, kind: 'quest', name: 'Open the gate', summary: 'Find the key.', tags: ['gate'] },
{ id: locationId, kind: 'location', name: 'Moon Gate', summary: 'A sealed arch.', tags: ['gate'] },
],
})
const validated = validateAndOrderRoundPlan({
checks: [], aiActions: [], relevantMemoryQueries: [],
proposedEvents: [
{ type: 'relationship', actorId: 'hero', targetId: questId, value: 3, item: null, damageType: null, description: 'The hero becomes invested.' },
{ type: 'quest', actorId: 'hero', targetId: questId, value: null, item: 'active', damageType: null, description: 'The gate must be opened.' },
{ type: 'narrative', actorId: null, targetId: locationId, value: null, item: 'scene', damageType: null, description: 'The party reaches the Moon Gate.' },
],
}, context)
expect(validated.proposedEvents).toHaveLength(3)
})
})