@@ -129,12 +129,16 @@ export class ObjectQLPlugin implements Plugin {
129129 // object registered by plugins (e.g., sys_user from plugin-auth).
130130 await this . syncRegisteredSchemas ( ctx ) ;
131131
132+ // Bridge all SchemaRegistry objects to metadata service
133+ // This ensures AI tools and other IMetadataService consumers can see all objects
134+ await this . bridgeObjectsToMetadataService ( ctx ) ;
135+
132136 // Register built-in audit hooks
133137 this . registerAuditHooks ( ctx ) ;
134138
135139 // Register tenant isolation middleware
136140 this . registerTenantMiddleware ( ctx ) ;
137-
141+
138142 ctx . logger . info ( 'ObjectQL engine started' , {
139143 driversRegistered : this . ql ?. [ 'drivers' ] ?. size || 0 ,
140144 objectsRegistered : this . ql ?. registry ?. getAllObjects ?.( ) ?. length || 0
@@ -365,9 +369,6 @@ export class ObjectQLPlugin implements Plugin {
365369 * This closes the persistence loop so that user-created schemas survive
366370 * kernel cold starts and redeployments.
367371 *
368- * Also registers loaded objects with the metadata service so they are
369- * visible to tools like AI chat that query the metadata service.
370- *
371372 * Gracefully degrades when:
372373 * - The protocol service is unavailable (e.g., in-memory-only mode).
373374 * - `loadMetaFromDb` is not implemented by the protocol shim.
@@ -398,47 +399,67 @@ export class ObjectQLPlugin implements Plugin {
398399 ctx . logger . info ( 'Metadata restored from database to SchemaRegistry' , { loaded, errors } ) ;
399400 } else {
400401 ctx . logger . debug ( 'No persisted metadata found in database' ) ;
402+ }
403+ } catch ( e : unknown ) {
404+ // Non-fatal: first-run or in-memory driver may not have sys_metadata yet
405+ ctx . logger . debug ( 'DB metadata restore failed (non-fatal)' , {
406+ error : e instanceof Error ? e . message : String ( e ) ,
407+ } ) ;
408+ }
409+ }
410+
411+ /**
412+ * Bridge all SchemaRegistry objects to the metadata service.
413+ *
414+ * This ensures objects registered by plugins and loaded from sys_metadata
415+ * are visible to AI tools and other consumers that query IMetadataService.
416+ *
417+ * Runs after both restoreMetadataFromDb() and syncRegisteredSchemas() to
418+ * catch all objects in the SchemaRegistry regardless of their source.
419+ */
420+ private async bridgeObjectsToMetadataService ( ctx : PluginContext ) : Promise < void > {
421+ try {
422+ const metadataService = ctx . getService < any > ( 'metadata' ) ;
423+ if ( ! metadataService || typeof metadataService . register !== 'function' ) {
424+ ctx . logger . debug ( 'Metadata service unavailable for bridging, skipping' ) ;
425+ return ;
426+ }
427+
428+ if ( ! this . ql ?. registry ) {
429+ ctx . logger . debug ( 'SchemaRegistry unavailable for bridging, skipping' ) ;
401430 return ;
402431 }
403432
404- // Phase 3: Bridge SchemaRegistry objects to metadata service
405- // This ensures objects loaded from sys_metadata are visible to AI tools and other
406- // consumers that query via IMetadataService.listObjects()
407- if ( loaded > 0 ) {
433+ const objects = this . ql . registry . getAllObjects ( ) ;
434+ let bridged = 0 ;
435+
436+ for ( const obj of objects ) {
408437 try {
409- const metadataService = ctx . getService < any > ( 'metadata' ) ;
410- if ( metadataService && typeof metadataService . register === 'function' && this . ql ?. registry ) {
411- const objects = this . ql . registry . getAllObjects ( ) ;
412- let bridged = 0 ;
413- for ( const obj of objects ) {
414- try {
415- // Check if object is already in metadata service to avoid duplicates
416- const existing = await metadataService . getObject ( obj . name ) ;
417- if ( ! existing ) {
418- // Register object that exists in SchemaRegistry but not in metadata service
419- await metadataService . register ( 'object' , obj . name , obj ) ;
420- bridged ++ ;
421- }
422- } catch ( e : unknown ) {
423- ctx . logger . debug ( 'Failed to bridge object to metadata service' , {
424- object : obj . name ,
425- error : e instanceof Error ? e . message : String ( e ) ,
426- } ) ;
427- }
428- }
429- if ( bridged > 0 ) {
430- ctx . logger . info ( 'Bridged objects from SchemaRegistry to metadata service' , { count : bridged } ) ;
431- }
438+ // Check if object is already in metadata service to avoid duplicates
439+ const existing = await metadataService . getObject ( obj . name ) ;
440+ if ( ! existing ) {
441+ // Register object that exists in SchemaRegistry but not in metadata service
442+ await metadataService . register ( 'object' , obj . name , obj ) ;
443+ bridged ++ ;
432444 }
433445 } catch ( e : unknown ) {
434- ctx . logger . debug ( 'Metadata service unavailable for bridging, skipping' , {
446+ ctx . logger . debug ( 'Failed to bridge object to metadata service' , {
447+ object : obj . name ,
435448 error : e instanceof Error ? e . message : String ( e ) ,
436449 } ) ;
437450 }
438451 }
452+
453+ if ( bridged > 0 ) {
454+ ctx . logger . info ( 'Bridged objects from SchemaRegistry to metadata service' , {
455+ count : bridged ,
456+ total : objects . length
457+ } ) ;
458+ } else {
459+ ctx . logger . debug ( 'No objects needed bridging (all already in metadata service)' ) ;
460+ }
439461 } catch ( e : unknown ) {
440- // Non-fatal: first-run or in-memory driver may not have sys_metadata yet
441- ctx . logger . debug ( 'DB metadata restore failed (non-fatal)' , {
462+ ctx . logger . debug ( 'Failed to bridge objects to metadata service' , {
442463 error : e instanceof Error ? e . message : String ( e ) ,
443464 } ) ;
444465 }
0 commit comments