@@ -53,7 +53,7 @@ function estimateTokens(text: string): number {
5353 return Math . ceil ( text . length / 4 ) ;
5454}
5555
56- /** Compute SHA-256 hash of text content. */
56+ /** Compute SHA-256 hash of text content (UTF-8 encoded; must match agent/src/memory.py) . */
5757function hashContent ( text : string ) : string {
5858 return createHash ( 'sha256' ) . update ( text ) . digest ( 'hex' ) ;
5959}
@@ -68,7 +68,18 @@ function verifyContentIntegrity(
6868 metadata ?: Record < string , { stringValue ?: string } > ,
6969) : boolean {
7070 const expected = metadata ?. content_sha256 ?. stringValue ;
71- if ( ! expected ) return true ; // No hash stored — skip verification
71+ if ( ! expected ) {
72+ // Schema v3 records should always have a hash — log if missing
73+ const schemaVersion = metadata ?. schema_version ?. stringValue ;
74+ if ( schemaVersion && parseInt ( schemaVersion , 10 ) >= 3 ) {
75+ logger . warn ( 'Schema v3 record missing content_sha256 — possible corrupted write' , {
76+ schema_version : schemaVersion ,
77+ source_type : metadata ?. source_type ?. stringValue ?? '(unknown)' ,
78+ metric_type : 'memory_integrity_missing_hash' ,
79+ } ) ;
80+ }
81+ return true ;
82+ }
7283 return hashContent ( text ) === expected ;
7384}
7485
@@ -96,7 +107,8 @@ function getClient(): BedrockAgentCoreClient {
96107 * - Semantic: `/{actorId}/knowledge/` (actorId = repo)
97108 * - Episodic: `/{actorId}/episodes/` (prefix matches all sessions)
98109 *
99- * Results are trimmed to a 2000-token budget (oldest entries dropped first).
110+ * Results are trimmed to a 2000-token budget (knowledge is prioritized before episodes;
111+ * entries beyond the budget are dropped).
100112 * Returns `undefined` on any error (fail-open).
101113 *
102114 * @param memoryId - the AgentCore Memory resource ID.
@@ -160,7 +172,16 @@ export async function loadMemoryContext(
160172 const text = record . content ?. text ;
161173 if ( text ) {
162174 if ( ! verifyContentIntegrity ( text , record . metadata ) ) {
163- logger . warn ( 'Memory record content integrity check failed' , { repo, namespace : semanticNamespace } ) ;
175+ logger . warn ( 'Memory record content integrity check failed — using content anyway (fail-open)' , {
176+ repo,
177+ namespace : semanticNamespace ,
178+ record_type : 'repo_knowledge' ,
179+ expected_hash : record . metadata ?. content_sha256 ?. stringValue ?? '(none)' ,
180+ actual_hash : hashContent ( text ) ,
181+ source_type : record . metadata ?. source_type ?. stringValue ?? '(unknown)' ,
182+ content_length : text . length ,
183+ metric_type : 'memory_integrity_mismatch' ,
184+ } ) ;
164185 }
165186 repoKnowledge . push ( sanitizeExternalContent ( text ) ) ;
166187 }
@@ -172,7 +193,16 @@ export async function loadMemoryContext(
172193 const text = record . content ?. text ;
173194 if ( text ) {
174195 if ( ! verifyContentIntegrity ( text , record . metadata ) ) {
175- logger . warn ( 'Memory record content integrity check failed' , { repo, namespace : episodicNamespace } ) ;
196+ logger . warn ( 'Memory record content integrity check failed — using content anyway (fail-open)' , {
197+ repo,
198+ namespace : episodicNamespace ,
199+ record_type : 'past_episode' ,
200+ expected_hash : record . metadata ?. content_sha256 ?. stringValue ?? '(none)' ,
201+ actual_hash : hashContent ( text ) ,
202+ source_type : record . metadata ?. source_type ?. stringValue ?? '(unknown)' ,
203+ content_length : text . length ,
204+ metric_type : 'memory_integrity_mismatch' ,
205+ } ) ;
176206 }
177207 pastEpisodes . push ( sanitizeExternalContent ( text ) ) ;
178208 }
0 commit comments