@@ -278,16 +278,33 @@ const PROJECT_NAMESPACE_INPUT_SCHEMA = Object.freeze({
278278 pattern : PROJECT_NAMESPACE_SCHEMA_PATTERN
279279} )
280280
281+ const CONTEXT_READ_BINDING_REQUEST_FIELDS = Object . freeze ( [
282+ 'schemaVersion' ,
283+ 'contextEpoch' ,
284+ 'planId' ,
285+ 'planContentId' ,
286+ 'activeRoot' ,
287+ 'project'
288+ ] )
289+ const CONTEXT_READ_BINDING_ROUNDTRIP_FIELDS = Object . freeze ( [
290+ ...CONTEXT_READ_BINDING_REQUEST_FIELDS ,
291+ 'bindingStatus' ,
292+ 'verificationMode'
293+ ] )
294+ const CONTEXT_READ_BINDING_ROUNDTRIP_FIELD_SET = new Set ( CONTEXT_READ_BINDING_ROUNDTRIP_FIELDS )
295+
281296const CONTEXT_READ_BINDING_SCHEMA = {
282297 type : 'object' ,
283- required : [ 'schemaVersion' , 'contextEpoch' , 'planId' , 'planContentId' , 'activeRoot' , 'project' ] ,
298+ required : CONTEXT_READ_BINDING_REQUEST_FIELDS ,
284299 properties : {
285300 schemaVersion : { const : 'ContextReadBindingV1' } ,
286301 contextEpoch : { type : 'string' , minLength : 1 } ,
287302 planId : { type : 'string' , minLength : 1 } ,
288303 planContentId : { type : 'string' , minLength : 1 } ,
289304 activeRoot : { type : 'string' , minLength : 1 } ,
290- project : { type : 'string' }
305+ project : { type : 'string' } ,
306+ bindingStatus : { type : 'string' , enum : [ 'verified' ] } ,
307+ verificationMode : { type : 'string' , enum : [ 'request-bound' ] }
291308 } ,
292309 additionalProperties : false
293310}
@@ -640,7 +657,7 @@ const TOOLS = [
640657 limit : { type : 'integer' , minimum : 1 , maximum : 20 , description : '最多返回会话数,默认 1' } ,
641658 handoffOnly : { type : 'boolean' , description : '仅返回 ContextHandoffCard' } ,
642659 maxChars : { type : 'integer' , minimum : 1 , maximum : 50000 , description : '正文总字符预算,默认 12000' } ,
643- cursor : { type : 'string' , minLength : 1 , maxLength : 8192 , description : '上一页返回的 opaque MemoryCursorV1;必须与同一 tool/target/context/query/source 完全匹配' } ,
660+ cursor : { type : 'string' , minLength : 1 , maxLength : 8192 } ,
644661 contextBinding : CONTEXT_READ_BINDING_SCHEMA
645662 }
646663 }
@@ -659,7 +676,7 @@ const TOOLS = [
659676 status : { type : 'string' , enum : [ 'active' , 'completed' , 'blocked' , 'unresolved' , 'all' ] , description : '默认 active' } ,
660677 limit : { type : 'integer' , minimum : 1 , maximum : 50 , description : '最多返回行数,默认 5,最大 50' } ,
661678 since : { type : 'string' , pattern : '^\\d{4}-\\d{2}-\\d{2}$' , description : '只返回该日期及之后的行' } ,
662- cursor : { type : 'string' , minLength : 1 , maxLength : 8192 , description : '上一页返回的 opaque MemoryCursorV1;必须与同一 tool/target/context/query/source 完全匹配' } ,
679+ cursor : { type : 'string' , minLength : 1 , maxLength : 8192 } ,
663680 contextBinding : CONTEXT_READ_BINDING_SCHEMA
664681 }
665682 }
@@ -2158,15 +2175,16 @@ function resolveContextReadBinding(binding, target, sourceId) {
21582175 'CONTEXT_BINDING_INVALID'
21592176 )
21602177 }
2161- const allowed = new Set ( [ 'schemaVersion' , 'contextEpoch' , 'planId' , 'planContentId' , 'activeRoot' , 'project' ] )
2162- const unknown = Object . keys ( binding ) . filter ( key => ! allowed . has ( key ) )
2178+ const unknown = Object . keys ( binding ) . filter ( key => ! CONTEXT_READ_BINDING_ROUNDTRIP_FIELD_SET . has ( key ) )
21632179 const requiredStrings = [ 'contextEpoch' , 'planId' , 'planContentId' , 'activeRoot' ]
21642180 if ( unknown . length || binding . schemaVersion !== 'ContextReadBindingV1' ||
21652181 requiredStrings . some ( field => typeof binding [ field ] !== 'string' || ! binding [ field ] . trim ( ) ) ||
2166- typeof binding . project !== 'string' ) {
2182+ typeof binding . project !== 'string' ||
2183+ ( binding . bindingStatus !== undefined && binding . bindingStatus !== 'verified' ) ||
2184+ ( binding . verificationMode !== undefined && binding . verificationMode !== 'request-bound' ) ) {
21672185 throw memoryQueryError (
21682186 'contextBinding does not match the published ContextReadBindingV1 request schema.' ,
2169- 'Pass only schemaVersion/contextEpoch/planId/planContentId/activeRoot/project from the current plan .' ,
2187+ 'Pass the current ContextReadBindingV1. Verified round-trip metadata is accepted; arbitrary extra fields are not .' ,
21702188 'CONTEXT_BINDING_INVALID'
21712189 )
21722190 }
0 commit comments