@@ -254,11 +254,17 @@ export function createIdentityApi(env: Env) {
254254 const network = getNetwork ( c . req . query ( "network" ) ) ;
255255 const nameFilter = c . req . query ( "name" ) ?. toLowerCase ( ) ;
256256 const ownerFilter = c . req . query ( "owner" ) ;
257+ const endpointTypesParam = c . req . query ( "endpointTypes" ) ;
257258 const limitParam = Number . parseInt ( c . req . query ( "limit" ) ?? "20" , 10 ) ;
259+ const offsetParam = Number . parseInt ( c . req . query ( "offset" ) ?? "0" , 10 ) ;
258260 const limit = Math . min ( Math . max ( limitParam , 1 ) , 50 ) ;
261+ const offset = Math . max ( offsetParam , 0 ) ;
262+
263+ const endpointTypes = endpointTypesParam ?. split ( "," ) . filter ( Boolean ) ?? [ ] ;
259264
260265 try {
261266 const sati = createSatiClient ( network , env ) ;
267+ const stats = await sati . getRegistryStats ( ) ;
262268
263269 let agents : AgentIdentity [ ] ;
264270 if ( ownerFilter ) {
@@ -267,11 +273,11 @@ export function createIdentityApi(env: Env) {
267273 }
268274 agents = await sati . listAgentsByOwner ( ownerFilter as Address ) ;
269275 } else {
270- const result = await sati . listAllAgents ( { limit } ) ;
271- agents = result . agents ;
276+ const result = await sati . listAllAgents ( { limit : limit + offset , offset : 0 } ) ;
277+ agents = result . agents . slice ( offset ) ;
272278 }
273279
274- // Fetch registration files and apply name filter
280+ // Fetch registration files and apply filters
275281 const results = [ ] ;
276282 for ( const agent of agents ) {
277283 if ( results . length >= limit ) break ;
@@ -283,6 +289,11 @@ export function createIdentityApi(env: Env) {
283289 if ( ! agentName . includes ( nameFilter ) ) continue ;
284290 }
285291
292+ if ( endpointTypes . length > 0 ) {
293+ const serviceNames = ( regFile ?. services ?? [ ] ) . map ( ( s ) => s . name ) ;
294+ if ( ! endpointTypes . some ( ( t ) => serviceNames . includes ( t ) ) ) continue ;
295+ }
296+
286297 results . push ( {
287298 mint : agent . mint ,
288299 agentId : `${ CAIP2_CHAINS [ network ] } :${ agent . mint } ` ,
@@ -292,14 +303,15 @@ export function createIdentityApi(env: Env) {
292303 image : regFile ?. image ?? "" ,
293304 uri : agent . uri ,
294305 memberNumber : Number ( agent . memberNumber ) ,
306+ nonTransferable : agent . nonTransferable ,
295307 active : regFile ?. active ?? true ,
296308 services : regFile ?. services ?? [ ] ,
297309 supportedTrust : regFile ?. supportedTrust ?? [ ] ,
298310 x402Support : regFile ?. x402Support ?? false ,
299311 } ) ;
300312 }
301313
302- return c . json ( { agents : results , count : results . length } ) ;
314+ return c . json ( { agents : results , count : results . length , totalAgents : Number ( stats . totalAgents ) } ) ;
303315 } catch ( error ) {
304316 console . error ( "[agents] ERROR:" , error ) ;
305317 return c . json ( { error : error instanceof Error ? error . message : "Failed to list agents" } , 500 ) ;
@@ -511,57 +523,111 @@ export function createIdentityApi(env: Env) {
511523 // GET /api/feedback/:mint - List feedback for agent
512524 // ---------------------------------------------------------------------------
513525
526+ // Shared feedback query logic for both per-agent and global endpoints
527+ async function queryFeedback (
528+ sati : Sati ,
529+ network : "devnet" | "mainnet" ,
530+ agentMint ?: Address ,
531+ filters ?: { clientAddress ?: string ; tag1 ?: string ; tag2 ?: string ; outcome ?: string } ,
532+ ) {
533+ const networkConfig = getNetworkConfig ( sati ) ;
534+ const feedbackSchemas = [ networkConfig . feedbackSchema , networkConfig . feedbackPublicSchema ] . filter ( Boolean ) ;
535+
536+ const { rpc : rpcUrl } = env . RPC_URLS [ network ] ;
537+ const slotResp = await fetch ( rpcUrl , {
538+ method : "POST" ,
539+ headers : { "Content-Type" : "application/json" } ,
540+ body : JSON . stringify ( { jsonrpc : "2.0" , id : 1 , method : "getSlot" , params : [ { commitment : "confirmed" } ] } ) ,
541+ } ) ;
542+ const slotJson = ( await slotResp . json ( ) ) as { result : number } ;
543+ const currentSlot = slotJson . result ;
544+ const nowSec = Math . floor ( Date . now ( ) / 1000 ) ;
545+
546+ const feedbackItems : Array < Record < string , unknown > > = [ ] ;
547+ let feedbackIndex = 0 ;
548+
549+ for ( const schema of feedbackSchemas ) {
550+ const filter : Record < string , unknown > = { sasSchema : schema as Address } ;
551+ if ( agentMint ) filter . agentMint = agentMint ;
552+
553+ const feedbacks = await sati . listFeedbacks ( filter as { sasSchema : Address ; agentMint ?: Address } ) ;
554+
555+ for ( const fb of feedbacks . items ) {
556+ const parsed = parseFeedbackContent ( fb . data . content , fb . data . contentType ) ;
557+
558+ if ( filters ?. clientAddress && fb . data . counterparty !== filters . clientAddress ) continue ;
559+ if ( filters ?. tag1 && parsed ?. tag1 !== filters . tag1 ) continue ;
560+ if ( filters ?. tag2 && parsed ?. tag2 !== filters . tag2 ) continue ;
561+ if ( filters ?. outcome !== undefined && String ( fb . data . outcome ) !== filters . outcome ) continue ;
562+
563+ const slotDiff = Number ( BigInt ( currentSlot ) - fb . raw . slotCreated ) ;
564+ const createdAt = nowSec - Math . floor ( slotDiff * 0.4 ) ;
565+
566+ feedbackItems . push ( {
567+ compressedAddress : fb . address ,
568+ clientAddress : fb . data . counterparty ,
569+ agentMint : fb . data . agentMint ,
570+ feedbackIndex : feedbackIndex ++ ,
571+ value : parsed ?. value ?? 0 ,
572+ valueDecimals : parsed ?. valueDecimals ?? 0 ,
573+ tag1 : parsed ?. tag1 ?? "" ,
574+ tag2 : parsed ?. tag2 ?? "" ,
575+ message : parsed ?. m ?? "" ,
576+ endpoint : parsed ?. endpoint ?? "" ,
577+ outcome : fb . data . outcome ,
578+ createdAt,
579+ schema : schema === networkConfig . feedbackSchema ? "FeedbackV1" : "FeedbackPublicV1" ,
580+ isRevoked : false ,
581+ } ) ;
582+ }
583+ }
584+
585+ return feedbackItems ;
586+ }
587+
514588 app . get ( "/api/feedback/:mint" , async ( c ) => {
515589 const mint = c . req . param ( "mint" ) ;
516590 const network = getNetwork ( c . req . query ( "network" ) ) ;
517- const clientAddressFilter = c . req . query ( "clientAddress" ) ;
518- const tag1Filter = c . req . query ( "tag1" ) ;
519- const tag2Filter = c . req . query ( "tag2" ) ;
520591
521592 if ( ! isAddress ( mint ) ) {
522593 return c . json ( { error : "Invalid mint address" } , 400 ) ;
523594 }
524595
525596 try {
526597 const sati = createSatiClient ( network , env ) ;
527- const networkConfig = getNetworkConfig ( sati ) ;
528- const feedbackSchemas = [ networkConfig . feedbackSchema , networkConfig . feedbackPublicSchema ] . filter ( Boolean ) ;
529-
530- const feedbackItems : Array < Record < string , unknown > > = [ ] ;
531- let feedbackIndex = 0 ;
598+ const feedbackItems = await queryFeedback ( sati , network , mint as Address , {
599+ clientAddress : c . req . query ( "clientAddress" ) ,
600+ tag1 : c . req . query ( "tag1" ) ,
601+ tag2 : c . req . query ( "tag2" ) ,
602+ outcome : c . req . query ( "outcome" ) ,
603+ } ) ;
532604
533- for ( const schema of feedbackSchemas ) {
534- const feedbacks = await sati . listFeedbacks ( {
535- sasSchema : schema as Address ,
536- agentMint : mint as Address ,
537- } ) ;
605+ return c . json ( { feedbacks : feedbackItems , count : feedbackItems . length } ) ;
606+ } catch ( error ) {
607+ console . error ( "[feedback list] ERROR:" , error ) ;
608+ return c . json ( { error : error instanceof Error ? error . message : "Failed to list feedback" } , 500 ) ;
609+ }
610+ } ) ;
538611
539- for ( const fb of feedbacks . items ) {
540- const parsed = parseFeedbackContent ( fb . data . content , fb . data . contentType ) ;
612+ // ---------------------------------------------------------------------------
613+ // GET /api/feedback - List ALL feedback across all agents
614+ // ---------------------------------------------------------------------------
541615
542- // Apply filters
543- if ( clientAddressFilter && fb . data . counterparty !== clientAddressFilter ) continue ;
544- if ( tag1Filter && parsed ?. tag1 !== tag1Filter ) continue ;
545- if ( tag2Filter && parsed ?. tag2 !== tag2Filter ) continue ;
616+ app . get ( "/api/feedback" , async ( c ) => {
617+ const network = getNetwork ( c . req . query ( "network" ) ) ;
546618
547- feedbackItems . push ( {
548- clientAddress : fb . data . counterparty ,
549- feedbackIndex : feedbackIndex ++ ,
550- value : parsed ?. value ?? 0 ,
551- valueDecimals : parsed ?. valueDecimals ?? 0 ,
552- tag1 : parsed ?. tag1 ?? "" ,
553- tag2 : parsed ?. tag2 ?? "" ,
554- endpoint : parsed ?. endpoint ?? "" ,
555- reviewer : parsed ?. reviewer ?? "" ,
556- outcome : fb . data . outcome ,
557- isRevoked : false ,
558- } ) ;
559- }
560- }
619+ try {
620+ const sati = createSatiClient ( network , env ) ;
621+ const feedbackItems = await queryFeedback ( sati , network , undefined , {
622+ clientAddress : c . req . query ( "clientAddress" ) ,
623+ tag1 : c . req . query ( "tag1" ) ,
624+ tag2 : c . req . query ( "tag2" ) ,
625+ outcome : c . req . query ( "outcome" ) ,
626+ } ) ;
561627
562628 return c . json ( { feedbacks : feedbackItems , count : feedbackItems . length } ) ;
563629 } catch ( error ) {
564- console . error ( "[feedback list ] ERROR:" , error ) ;
630+ console . error ( "[feedback global ] ERROR:" , error ) ;
565631 return c . json ( { error : error instanceof Error ? error . message : "Failed to list feedback" } , 500 ) ;
566632 }
567633 } ) ;
0 commit comments