99 createEvent , upsertFact , upsertStatus , listEvents , listFacts , listStatuses , isStoreAvailable ,
1010 isEntityStoreAvailable , createEntity , findEntity , linkEntityToMemory ,
1111} from '../services/stores/interface.js' ;
12- import { scrubCredentials } from '../services/scrub.js' ;
12+ import { scrubCredentials , scrubObject } from '../services/scrub.js' ;
1313import { extractEntities , linkExtractedEntities } from '../services/entities.js' ;
14+ import { validateMemoryInput , MAX_OBSERVED_BY } from '../middleware/validate.js' ;
1415
1516export const memoryRouter = Router ( ) ;
1617
@@ -19,16 +20,10 @@ memoryRouter.post('/', async (req, res) => {
1920 try {
2021 const { type, content, source_agent, client_id, category, importance, metadata } = req . body ;
2122
22- // Validate required fields
23- if ( ! type || ! content || ! source_agent ) {
24- return res . status ( 400 ) . json ( {
25- error : 'Missing required fields: type, content, source_agent' ,
26- valid_types : [ 'event' , 'fact' , 'decision' , 'status' ] ,
27- } ) ;
28- }
29-
30- if ( ! [ 'event' , 'fact' , 'decision' , 'status' ] . includes ( type ) ) {
31- return res . status ( 400 ) . json ( { error : `Invalid type: ${ type } . Must be event, fact, decision, or status` } ) ;
23+ // Validate all input fields
24+ const validationError = validateMemoryInput ( req . body ) ;
25+ if ( validationError ) {
26+ return res . status ( 400 ) . json ( { error : validationError } ) ;
3227 }
3328
3429 // Scrub credentials
@@ -58,6 +53,18 @@ memoryRouter.post('/', async (req, res) => {
5853 }
5954
6055 // Different agent → corroborate: record that another agent observed the same thing
56+ if ( existingObservedBy . length >= MAX_OBSERVED_BY ) {
57+ return res . status ( 200 ) . json ( {
58+ id : existing . id ,
59+ type : existing . payload . type ,
60+ content_hash : contentHash ,
61+ deduplicated : true ,
62+ observed_by : existingObservedBy ,
63+ observation_count : existingObservedBy . length ,
64+ message : `Observer cap reached (${ MAX_OBSERVED_BY } ) — corroboration noted but not recorded` ,
65+ stored_in : { qdrant : true , structured_db : true } ,
66+ } ) ;
67+ }
6168 const updatedObservedBy = [ ...existingObservedBy , source_agent ] ;
6269 const now = new Date ( ) . toISOString ( ) ;
6370 await updatePointPayload ( existing . id , {
@@ -132,7 +139,7 @@ memoryRouter.post('/', async (req, res) => {
132139 superseded_by : null ,
133140 ...( type === 'fact' && req . body . key ? { key : req . body . key } : { } ) ,
134141 ...( type === 'status' && req . body . subject ? { subject : req . body . subject , status_value : req . body . status_value } : { } ) ,
135- ...( metadata ? { metadata } : { } ) ,
142+ ...( metadata ? { metadata : scrubObject ( metadata ) } : { } ) ,
136143 } ;
137144
138145 // Extract entities (fast path — regex + alias cache, no LLM)
@@ -242,7 +249,7 @@ memoryRouter.get('/search', async (req, res) => {
242249 nestedFilters . push ( { arrayField : 'entities' , key : 'name' , value : entityName } ) ;
243250 }
244251
245- const rawResults = await searchPoints ( vector , filter , parseInt ( limit ) || 10 , nestedFilters ) ;
252+ const rawResults = await searchPoints ( vector , filter , Math . min ( parseInt ( limit ) || 10 , 100 ) , nestedFilters ) ;
246253
247254 // Apply confidence decay and re-rank
248255 const results = rawResults . map ( r => {
0 commit comments