@@ -38,10 +38,7 @@ import {
3838 COPILOT_VFS_ROUTING_KEYS ,
3939 isCopilotModelTextKey ,
4040} from '@/lib/copilot/model-visible-content'
41- import {
42- collectModelVisibleSchemaContent ,
43- getModelVisibleSchemaAction ,
44- } from '@/lib/copilot/model-visible-schema'
41+ import { getModelVisibleSchemaAction } from '@/lib/copilot/model-visible-schema'
4542import { getAutoAllowedTools } from '@/lib/copilot/persistence/tool-permission/auto-allow'
4643import { createStreamingContext } from '@/lib/copilot/request/context/request-context'
4744import { buildToolCallSummaries } from '@/lib/copilot/request/context/result'
@@ -332,9 +329,9 @@ function schemaContentAction(
332329}
333330
334331const toolContentSelector : SelectedContentSelector = ( path , key , value ) => {
335- if ( path . length === 1 && key === 'description' ) return 'project'
336- if ( path . length === 1 && key === 'name' ) return 'verify'
337- if ( path . length === 1 && TOOL_SCHEMA_KEYS . has ( key ) ) return 'traverse'
332+ if ( path . length === 0 && key === 'description' ) return 'project'
333+ if ( path . length === 0 && key === 'name' ) return 'verify'
334+ if ( path . length === 0 && TOOL_SCHEMA_KEYS . has ( key ) ) return 'traverse'
338335
339336 const schemaRootIndex = path . findIndex ( ( segment ) => TOOL_SCHEMA_KEYS . has ( segment ) )
340337 if ( schemaRootIndex >= 0 ) {
@@ -402,31 +399,27 @@ const desktopContentSelector: SelectedContentSelector = (_path, key, value) => {
402399 return value !== null && typeof value === 'object' ? 'traverse' : 'preserve'
403400}
404401
405- function isModelSafeToolPayload (
406- candidate : unknown ,
402+ function projectModelSafeToolPayloads (
403+ value : unknown ,
407404 registry : ResolvedSecretTraceRegistry
408- ) : boolean {
409- if ( ! isPlainRecord ( candidate ) || typeof candidate . name !== 'string' ) return false
410- if ( ! isResolvedSecretModelContentUnchanged ( candidate . name , registry ) ) return false
405+ ) : unknown [ ] {
406+ if ( ! Array . isArray ( value ) ) throw new CopilotModelContentProjectionError ( )
411407
412- try {
413- for ( const schemaKey of TOOL_SCHEMA_KEYS ) {
414- if ( ! Object . hasOwn ( candidate , schemaKey ) ) continue
415- const guardedValues = collectModelVisibleSchemaContent ( candidate [ schemaKey ] ) . guardedValues
416- if ( ! isResolvedSecretModelContentUnchanged ( guardedValues , registry ) ) return false
408+ const projected : unknown [ ] = [ ]
409+ for ( const candidate of value ) {
410+ if ( ! isPlainRecord ( candidate ) || typeof candidate . name !== 'string' ) continue
411+
412+ try {
413+ projected . push ( projectStructuredContent ( candidate , registry , toolContentSelector , 'record' ) )
414+ } catch {
415+ // Tool definitions are independent protocol entities. Reject an unsafe definition without
416+ // turning the entire catalog into one synthetic projection value or failing safe siblings.
417417 }
418- return true
419- } catch {
420- return false
421418 }
422- }
423419
424- function filterModelSafeToolPayloads (
425- value : unknown ,
426- registry : ResolvedSecretTraceRegistry
427- ) : unknown [ ] {
428- if ( ! Array . isArray ( value ) ) throw new CopilotModelContentProjectionError ( )
429- return value . filter ( ( candidate ) => isModelSafeToolPayload ( candidate , registry ) )
420+ // Projection completeness is a request-level invariant, even when every candidate was rejected.
421+ projectModelContent ( [ ] , registry )
422+ return projected
430423}
431424
432425function hasModelSafeRoutingFields (
@@ -645,12 +638,7 @@ function projectInitialCopilotPayload(
645638 }
646639 for ( const key of TOOL_PAYLOAD_KEYS ) {
647640 if ( Object . hasOwn ( payload , key ) ) {
648- projectedPayload [ key ] = projectStructuredContent (
649- filterModelSafeToolPayloads ( payload [ key ] , registry ) ,
650- registry ,
651- toolContentSelector ,
652- 'array'
653- )
641+ projectedPayload [ key ] = projectModelSafeToolPayloads ( payload [ key ] , registry )
654642 }
655643 }
656644 if ( Object . hasOwn ( payload , 'responseFormat' ) ) {
0 commit comments