agent0-sdk 1.2.0__py3-none-any.whl → 1.4.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -159,7 +159,7 @@ IDENTITY_REGISTRY_ABI = [
159
159
  {"internalType": "uint256", "name": "agentId", "type": "uint256"}
160
160
  ],
161
161
  "name": "getAgentWallet",
162
- "outputs": [{"internalType": "bytes", "name": "", "type": "bytes"}],
162
+ "outputs": [{"internalType": "address", "name": "", "type": "address"}],
163
163
  "stateMutability": "view",
164
164
  "type": "function"
165
165
  },
@@ -231,7 +231,7 @@ REPUTATION_REGISTRY_ABI = [
231
231
  {
232
232
  "inputs": [
233
233
  {"internalType": "uint256", "name": "agentId", "type": "uint256"},
234
- {"internalType": "int256", "name": "value", "type": "int256"},
234
+ {"internalType": "int128", "name": "value", "type": "int128"},
235
235
  {"internalType": "uint8", "name": "valueDecimals", "type": "uint8"},
236
236
  {"internalType": "string", "name": "tag1", "type": "string"},
237
237
  {"internalType": "string", "name": "tag2", "type": "string"},
@@ -285,7 +285,7 @@ REPUTATION_REGISTRY_ABI = [
285
285
  ],
286
286
  "name": "readFeedback",
287
287
  "outputs": [
288
- {"internalType": "int256", "name": "value", "type": "int256"},
288
+ {"internalType": "int128", "name": "value", "type": "int128"},
289
289
  {"internalType": "uint8", "name": "valueDecimals", "type": "uint8"},
290
290
  {"internalType": "string", "name": "tag1", "type": "string"},
291
291
  {"internalType": "string", "name": "tag2", "type": "string"},
@@ -304,7 +304,7 @@ REPUTATION_REGISTRY_ABI = [
304
304
  "name": "getSummary",
305
305
  "outputs": [
306
306
  {"internalType": "uint64", "name": "count", "type": "uint64"},
307
- {"internalType": "int256", "name": "summaryValue", "type": "int256"},
307
+ {"internalType": "int128", "name": "summaryValue", "type": "int128"},
308
308
  {"internalType": "uint8", "name": "summaryValueDecimals", "type": "uint8"}
309
309
  ],
310
310
  "stateMutability": "view",
@@ -322,7 +322,7 @@ REPUTATION_REGISTRY_ABI = [
322
322
  "outputs": [
323
323
  {"internalType": "address[]", "name": "clients", "type": "address[]"},
324
324
  {"internalType": "uint64[]", "name": "feedbackIndexes", "type": "uint64[]"},
325
- {"internalType": "int256[]", "name": "values", "type": "int256[]"},
325
+ {"internalType": "int128[]", "name": "values", "type": "int128[]"},
326
326
  {"internalType": "uint8[]", "name": "valueDecimals", "type": "uint8[]"},
327
327
  {"internalType": "string[]", "name": "tag1s", "type": "string[]"},
328
328
  {"internalType": "string[]", "name": "tag2s", "type": "string[]"},
@@ -358,7 +358,7 @@ REPUTATION_REGISTRY_ABI = [
358
358
  {"indexed": True, "internalType": "uint256", "name": "agentId", "type": "uint256"},
359
359
  {"indexed": True, "internalType": "address", "name": "clientAddress", "type": "address"},
360
360
  {"indexed": False, "internalType": "uint64", "name": "feedbackIndex", "type": "uint64"},
361
- {"indexed": False, "internalType": "int256", "name": "value", "type": "int256"},
361
+ {"indexed": False, "internalType": "int128", "name": "value", "type": "int128"},
362
362
  {"indexed": False, "internalType": "uint8", "name": "valueDecimals", "type": "uint8"},
363
363
  {"indexed": True, "internalType": "string", "name": "indexedTag1", "type": "string"},
364
364
  {"indexed": False, "internalType": "string", "name": "tag1", "type": "string"},
@@ -514,6 +514,11 @@ VALIDATION_REGISTRY_ABI = [
514
514
  # Contract registry for different chains
515
515
  # Updated addresses from: https://github.com/erc-8004/erc-8004-contracts
516
516
  DEFAULT_REGISTRIES: Dict[int, Dict[str, str]] = {
517
+ 1: { # Ethereum Mainnet
518
+ "IDENTITY": "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
519
+ "REPUTATION": "0x8004BAa17C55a88189AE136b182e5fdA19dE9b63",
520
+ # "VALIDATION": "0x...", # Set when deployed/enabled
521
+ },
517
522
  11155111: { # Ethereum Sepolia
518
523
  "IDENTITY": "0x8004A818BFB912233c491871b3d84c89A494BD9e",
519
524
  "REPUTATION": "0x8004B663056A597Dffe9eCcC1965A193B7388713",
@@ -540,8 +545,9 @@ DEFAULT_REGISTRIES: Dict[int, Dict[str, str]] = {
540
545
  # Default subgraph URLs for different chains
541
546
  # Note: Subgraph URLs may need to be updated when new contracts are deployed
542
547
  DEFAULT_SUBGRAPH_URLS: Dict[int, str] = {
548
+ 1: "https://gateway.thegraph.com/api/7fd2e7d89ce3ef24cd0d4590298f0b2c/subgraphs/id/FX78UzofJFr5h2Udznv7pZ2uLG1JBbYsPm7eecRSYnty", # Ethereum Mainnet
543
549
  11155111: "https://gateway.thegraph.com/api/00a452ad3cd1900273ea62c1bf283f93/subgraphs/id/6wQRC7geo9XYAhckfmfo8kbMRLeWU8KQd3XsJqFKmZLT", # Ethereum Sepolia
544
550
  # Other chains temporarily disabled - subgraphs to be updated
545
551
  # 84532: "https://gateway.thegraph.com/api/...", # Base Sepolia - To be updated
546
552
  # 80002: "https://gateway.thegraph.com/api/...", # Polygon Amoy - To be updated
547
- }
553
+ }
@@ -17,6 +17,7 @@ from .models import (
17
17
  from .web3_client import Web3Client
18
18
  from .ipfs_client import IPFSClient
19
19
  from .value_encoding import encode_feedback_value, decode_feedback_value
20
+ from .transaction_handle import TransactionHandle
20
21
 
21
22
  logger = logging.getLogger(__name__)
22
23
 
@@ -73,7 +74,7 @@ class FeedbackManager:
73
74
  tag2: Optional[str] = None,
74
75
  endpoint: Optional[str] = None,
75
76
  feedbackFile: Optional[Dict[str, Any]] = None,
76
- ) -> Feedback:
77
+ ) -> TransactionHandle[Feedback]:
77
78
  """Give feedback (maps 8004 endpoint)."""
78
79
  # Parse agentId into (chainId, tokenId)
79
80
  agent_chain_id: Optional[int] = None
@@ -185,7 +186,7 @@ class FeedbackManager:
185
186
  "clientAddress": f"eip155:{agent_chain_id}:{clientAddress}",
186
187
  "createdAt": created_at,
187
188
  # On-chain fields (store raw+decimals for precision)
188
- "value": str(value_raw),
189
+ "value": int(value_raw),
189
190
  "valueDecimals": int(value_decimals),
190
191
 
191
192
  # OPTIONAL fields that mirror on-chain
@@ -220,35 +221,34 @@ class FeedbackManager:
220
221
  feedbackUri,
221
222
  feedbackHash
222
223
  )
223
-
224
- # Wait for transaction confirmation
225
- receipt = self.web3_client.wait_for_transaction(txHash)
226
-
227
224
  except Exception as e:
228
225
  raise ValueError(f"Failed to submit feedback to blockchain: {e}")
229
-
230
- # Create feedback object (address normalization happens in Feedback.create_id)
226
+
227
+ # Create a tx handle; build the Feedback object on confirmation.
231
228
  feedbackId = Feedback.create_id(agentId, clientAddress, feedbackIndex)
232
-
233
229
  ff: Dict[str, Any] = feedback_file or {}
234
- return Feedback(
235
- id=feedbackId,
236
- agentId=agentId,
237
- reviewer=clientAddress, # create_id normalizes the ID; reviewer field can remain as-is
238
- value=decode_feedback_value(value_raw, value_decimals),
239
- tags=[tag1, tag2] if tag1 or tag2 else [],
240
- text=ff.get("text"),
241
- context=ff.get("context"),
242
- proofOfPayment=ff.get("proofOfPayment"),
243
- fileURI=feedbackUri if feedbackUri else None,
244
- endpoint=endpoint_onchain if endpoint_onchain else None,
245
- createdAt=int(time.time()),
246
- isRevoked=False,
247
- # Off-chain only fields
248
- capability=ff.get("capability"),
249
- name=ff.get("name"),
250
- skill=ff.get("skill"),
251
- task=ff.get("task")
230
+
231
+ return TransactionHandle(
232
+ web3_client=self.web3_client,
233
+ tx_hash=txHash,
234
+ compute_result=lambda _receipt: Feedback(
235
+ id=feedbackId,
236
+ agentId=agentId,
237
+ reviewer=clientAddress,
238
+ value=decode_feedback_value(value_raw, value_decimals),
239
+ tags=[tag1, tag2] if tag1 or tag2 else [],
240
+ text=ff.get("text"),
241
+ context=ff.get("context"),
242
+ proofOfPayment=ff.get("proofOfPayment"),
243
+ fileURI=feedbackUri if feedbackUri else None,
244
+ endpoint=endpoint_onchain if endpoint_onchain else None,
245
+ createdAt=int(time.time()),
246
+ isRevoked=False,
247
+ capability=ff.get("capability"),
248
+ name=ff.get("name"),
249
+ skill=ff.get("skill"),
250
+ task=ff.get("task"),
251
+ ),
252
252
  )
253
253
 
254
254
  def getFeedback(
@@ -412,7 +412,7 @@ class FeedbackManager:
412
412
 
413
413
  def searchFeedback(
414
414
  self,
415
- agentId: AgentId,
415
+ agentId: Optional[AgentId] = None,
416
416
  clientAddresses: Optional[List[Address]] = None,
417
417
  tags: Optional[List[str]] = None,
418
418
  capabilities: Optional[List[str]] = None,
@@ -424,25 +424,68 @@ class FeedbackManager:
424
424
  include_revoked: bool = False,
425
425
  first: int = 100,
426
426
  skip: int = 0,
427
+ agents: Optional[List[AgentId]] = None,
427
428
  ) -> List[Feedback]:
428
- """Search feedback for an agent - uses subgraph if available."""
429
+ """Search feedback.
430
+
431
+ Backwards compatible:
432
+ - `agentId` was previously required; it is now optional.
433
+
434
+ New:
435
+ - `agents` supports searching across multiple agents.
436
+ - If neither `agentId` nor `agents` are provided, the query can still run via subgraph
437
+ using other filters like `clientAddresses` (reviewers), tags, etc.
438
+ """
429
439
  # Use indexer for subgraph queries (unified search interface)
430
440
  if self.indexer and self.subgraph_client:
431
441
  # Indexer handles subgraph queries for unified search architecture
432
442
  # This enables future semantic search capabilities
433
443
  return self.indexer.search_feedback(
434
- agentId, clientAddresses, tags, capabilities, skills, tasks, names,
435
- minValue, maxValue, include_revoked, first, skip
444
+ agentId,
445
+ clientAddresses,
446
+ tags,
447
+ capabilities,
448
+ skills,
449
+ tasks,
450
+ names,
451
+ minValue,
452
+ maxValue,
453
+ include_revoked,
454
+ first,
455
+ skip,
456
+ agents=agents,
436
457
  )
437
458
 
438
459
  # Fallback: direct subgraph access (if indexer not available)
439
460
  if self.subgraph_client:
440
461
  return self._search_feedback_subgraph(
441
- agentId, clientAddresses, tags, capabilities, skills, tasks, names,
442
- minValue, maxValue, include_revoked, first, skip
462
+ agentId,
463
+ clientAddresses,
464
+ tags,
465
+ capabilities,
466
+ skills,
467
+ tasks,
468
+ names,
469
+ minValue,
470
+ maxValue,
471
+ include_revoked,
472
+ first,
473
+ skip,
474
+ agents=agents,
443
475
  )
444
476
 
445
- # Fallback to blockchain
477
+ # Fallback to blockchain (requires a specific agent)
478
+ if not agentId and not agents:
479
+ raise ValueError(
480
+ "searchFeedback requires a subgraph when searching without agentId/agents."
481
+ )
482
+ if not agentId and agents and len(agents) == 1:
483
+ agentId = agents[0]
484
+ if not agentId:
485
+ raise ValueError(
486
+ "Blockchain fallback only supports searching a single agent; provide agentId or a single-item agents=[...]."
487
+ )
488
+
446
489
  # Parse agent ID
447
490
  if ":" in agentId:
448
491
  tokenId = int(agentId.split(":")[-1])
@@ -505,7 +548,7 @@ class FeedbackManager:
505
548
 
506
549
  def _search_feedback_subgraph(
507
550
  self,
508
- agentId: AgentId,
551
+ agentId: Optional[AgentId],
509
552
  clientAddresses: Optional[List[Address]],
510
553
  tags: Optional[List[str]],
511
554
  capabilities: Optional[List[str]],
@@ -517,11 +560,18 @@ class FeedbackManager:
517
560
  include_revoked: bool,
518
561
  first: int,
519
562
  skip: int,
563
+ agents: Optional[List[AgentId]] = None,
520
564
  ) -> List[Feedback]:
521
565
  """Search feedback using subgraph."""
566
+ merged_agents: Optional[List[AgentId]] = None
567
+ if agents:
568
+ merged_agents = list(agents)
569
+ if agentId:
570
+ merged_agents = (merged_agents or []) + [agentId]
571
+
522
572
  # Create SearchFeedbackParams
523
573
  params = SearchFeedbackParams(
524
- agents=[agentId],
574
+ agents=merged_agents,
525
575
  reviewers=clientAddresses,
526
576
  tags=tags,
527
577
  capabilities=capabilities,
@@ -613,7 +663,7 @@ class FeedbackManager:
613
663
  self,
614
664
  agentId: AgentId,
615
665
  feedbackIndex: int,
616
- ) -> Dict[str, Any]:
666
+ ) -> TransactionHandle[Feedback]:
617
667
  """Revoke feedback."""
618
668
  # Parse agent ID
619
669
  if ":" in agentId:
@@ -630,17 +680,11 @@ class FeedbackManager:
630
680
  tokenId,
631
681
  feedbackIndex
632
682
  )
633
-
634
- receipt = self.web3_client.wait_for_transaction(txHash)
635
-
636
- return {
637
- "txHash": txHash,
638
- "agentId": agentId,
639
- "clientAddress": clientAddress,
640
- "feedbackIndex": feedbackIndex,
641
- "status": "revoked"
642
- }
643
-
683
+ return TransactionHandle(
684
+ web3_client=self.web3_client,
685
+ tx_hash=txHash,
686
+ compute_result=lambda _receipt: self.getFeedback(agentId, clientAddress, feedbackIndex),
687
+ )
644
688
  except Exception as e:
645
689
  raise ValueError(f"Failed to revoke feedback: {e}")
646
690
 
@@ -650,7 +694,7 @@ class FeedbackManager:
650
694
  clientAddress: Address,
651
695
  feedbackIndex: int,
652
696
  response: Dict[str, Any],
653
- ) -> Feedback:
697
+ ) -> TransactionHandle[Feedback]:
654
698
  """Append a response/follow-up to existing feedback."""
655
699
  # Parse agent ID
656
700
  if ":" in agentId:
@@ -681,12 +725,11 @@ class FeedbackManager:
681
725
  responseUri, # Note: contract uses responseURI but variable name kept for compatibility
682
726
  responseHash
683
727
  )
684
-
685
- receipt = self.web3_client.wait_for_transaction(txHash)
686
-
687
- # Read updated feedback
688
- return self.getFeedback(agentId, clientAddress, feedbackIndex)
689
-
728
+ return TransactionHandle(
729
+ web3_client=self.web3_client,
730
+ tx_hash=txHash,
731
+ compute_result=lambda _receipt: self.getFeedback(agentId, clientAddress, feedbackIndex),
732
+ )
690
733
  except Exception as e:
691
734
  raise ValueError(f"Failed to append response: {e}")
692
735
 
@@ -433,7 +433,7 @@ class AgentIndexer:
433
433
  mcpPrompts=reg_file.get('mcpPrompts', []),
434
434
  mcpResources=reg_file.get('mcpResources', []),
435
435
  active=reg_file.get('active', True),
436
- x402support=reg_file.get('x402support', False),
436
+ x402support=reg_file.get('x402Support', reg_file.get('x402support', False)),
437
437
  extras={}
438
438
  )
439
439
 
@@ -724,7 +724,7 @@ class AgentIndexer:
724
724
  "mcpPrompts": reg_file.get('mcpPrompts', []),
725
725
  "mcpResources": reg_file.get('mcpResources', []),
726
726
  "active": reg_file.get('active', True),
727
- "x402support": reg_file.get('x402support', False),
727
+ "x402support": reg_file.get('x402Support', reg_file.get('x402support', False)),
728
728
  "totalFeedback": agent_data.get('totalFeedback', 0),
729
729
  "lastActivity": agent_data.get('lastActivity'),
730
730
  "updatedAt": agent_data.get('updatedAt'),
@@ -871,7 +871,7 @@ class AgentIndexer:
871
871
  "mcpPrompts": reg_file.get('mcpPrompts', []),
872
872
  "mcpResources": reg_file.get('mcpResources', []),
873
873
  "active": reg_file.get('active', True),
874
- "x402support": reg_file.get('x402support', False),
874
+ "x402support": reg_file.get('x402Support', reg_file.get('x402support', False)),
875
875
  "totalFeedback": agent.get('totalFeedback', 0),
876
876
  "lastActivity": agent.get('lastActivity'),
877
877
  "updatedAt": agent.get('updatedAt'),
@@ -1106,7 +1106,7 @@ class AgentIndexer:
1106
1106
 
1107
1107
  def search_feedback(
1108
1108
  self,
1109
- agentId: AgentId,
1109
+ agentId: Optional[AgentId] = None,
1110
1110
  clientAddresses: Optional[List[Address]] = None,
1111
1111
  tags: Optional[List[str]] = None,
1112
1112
  capabilities: Optional[List[str]] = None,
@@ -1118,29 +1118,77 @@ class AgentIndexer:
1118
1118
  include_revoked: bool = False,
1119
1119
  first: int = 100,
1120
1120
  skip: int = 0,
1121
+ agents: Optional[List[AgentId]] = None,
1121
1122
  ) -> List[Feedback]:
1122
- """Search feedback for an agent - uses subgraph if available."""
1123
- # Parse chainId from agentId
1124
- chain_id, token_id = self._parse_agent_id(agentId)
1123
+ """Search feedback via subgraph.
1124
+
1125
+ Backwards compatible:
1126
+ - Previously required `agentId`; it is now optional.
1127
+
1128
+ New:
1129
+ - `agents` supports searching across multiple agents.
1130
+ - If neither `agentId` nor `agents` is provided, subgraph search can still run using
1131
+ other filters (e.g., reviewers / tags).
1132
+ """
1133
+
1134
+ merged_agents: Optional[List[AgentId]] = None
1135
+ if agents:
1136
+ merged_agents = list(agents)
1137
+ if agentId:
1138
+ merged_agents = (merged_agents or []) + [agentId]
1139
+
1140
+ # Determine chain/subgraph client based on first specified agent (if any)
1141
+ chain_id = None
1142
+ if merged_agents and len(merged_agents) > 0:
1143
+ first_agent = merged_agents[0]
1144
+ chain_id, token_id = self._parse_agent_id(first_agent)
1125
1145
 
1126
1146
  # Get subgraph client for the chain
1127
1147
  subgraph_client = None
1128
- full_agent_id = agentId
1129
-
1148
+
1130
1149
  if chain_id is not None:
1131
1150
  subgraph_client = self._get_subgraph_client_for_chain(chain_id)
1132
1151
  else:
1133
- # No chainId in agentId, use SDK's default
1134
- # Construct full agentId format for subgraph query
1135
- default_chain_id = self.web3_client.chain_id
1136
- full_agent_id = f"{default_chain_id}:{token_id}"
1152
+ # If no explicit chainId, use SDK's default subgraph client (if configured).
1137
1153
  subgraph_client = self.subgraph_client
1154
+
1155
+ # If we have agent ids but they weren't chain-prefixed, prefix them with default chain id for the subgraph.
1156
+ if merged_agents and chain_id is None:
1157
+ default_chain_id = self.web3_client.chain_id
1158
+ normalized: List[AgentId] = []
1159
+ for aid in merged_agents:
1160
+ if isinstance(aid, str) and ":" in aid:
1161
+ normalized.append(aid)
1162
+ else:
1163
+ normalized.append(f"{default_chain_id}:{int(aid)}")
1164
+ merged_agents = normalized
1165
+ elif merged_agents and chain_id is not None:
1166
+ # Ensure all agent ids are chain-prefixed for the chosen chain
1167
+ normalized = []
1168
+ for aid in merged_agents:
1169
+ if isinstance(aid, str) and ":" in aid:
1170
+ normalized.append(aid)
1171
+ else:
1172
+ normalized.append(f"{chain_id}:{int(aid)}")
1173
+ merged_agents = normalized
1138
1174
 
1139
1175
  # Use subgraph if available (preferred)
1140
1176
  if subgraph_client:
1141
1177
  return self._search_feedback_subgraph(
1142
- full_agent_id, clientAddresses, tags, capabilities, skills, tasks, names,
1143
- minValue, maxValue, include_revoked, first, skip, subgraph_client
1178
+ agentId=None,
1179
+ agents=merged_agents,
1180
+ clientAddresses=clientAddresses,
1181
+ tags=tags,
1182
+ capabilities=capabilities,
1183
+ skills=skills,
1184
+ tasks=tasks,
1185
+ names=names,
1186
+ minValue=minValue,
1187
+ maxValue=maxValue,
1188
+ include_revoked=include_revoked,
1189
+ first=first,
1190
+ skip=skip,
1191
+ subgraph_client=subgraph_client,
1144
1192
  )
1145
1193
 
1146
1194
  # Fallback not implemented (would require blockchain queries)
@@ -1149,7 +1197,8 @@ class AgentIndexer:
1149
1197
 
1150
1198
  def _search_feedback_subgraph(
1151
1199
  self,
1152
- agentId: AgentId,
1200
+ agentId: Optional[AgentId],
1201
+ agents: Optional[List[AgentId]],
1153
1202
  clientAddresses: Optional[List[Address]],
1154
1203
  tags: Optional[List[str]],
1155
1204
  capabilities: Optional[List[str]],
@@ -1169,9 +1218,15 @@ class AgentIndexer:
1169
1218
  if not client:
1170
1219
  return []
1171
1220
 
1221
+ merged_agents: Optional[List[AgentId]] = None
1222
+ if agents:
1223
+ merged_agents = list(agents)
1224
+ if agentId:
1225
+ merged_agents = (merged_agents or []) + [agentId]
1226
+
1172
1227
  # Create SearchFeedbackParams
1173
1228
  params = SearchFeedbackParams(
1174
- agents=[agentId],
1229
+ agents=merged_agents,
1175
1230
  reviewers=clientAddresses,
1176
1231
  tags=tags,
1177
1232
  capabilities=capabilities,
@@ -1305,7 +1360,7 @@ class AgentIndexer:
1305
1360
  token_id
1306
1361
  )
1307
1362
 
1308
- # Get agentWallet using new dedicated function
1363
+ # Get on-chain verified wallet (IdentityRegistry.getAgentWallet)
1309
1364
  wallet_address = None
1310
1365
  try:
1311
1366
  wallet_address = self.web3_client.call_contract(
@@ -1316,7 +1371,6 @@ class AgentIndexer:
1316
1371
  if wallet_address == "0x0000000000000000000000000000000000000000":
1317
1372
  wallet_address = None
1318
1373
  except Exception:
1319
- # Fallback to registration file if getAgentWallet not available
1320
1374
  pass
1321
1375
 
1322
1376
  # Create agent ID
agent0_sdk/core/models.py CHANGED
@@ -89,7 +89,7 @@ class RegistrationFile:
89
89
  endpoints.append(endpoint_dict)
90
90
 
91
91
  # Note: agentWallet is no longer included in endpoints array.
92
- # It's now a reserved on-chain metadata key managed via setAgentWallet().
92
+ # It's now a reserved on-chain metadata key managed via Agent.setWallet().
93
93
 
94
94
  # Build registrations array
95
95
  registrations = []
@@ -106,7 +106,7 @@ class RegistrationFile:
106
106
  "name": self.name,
107
107
  "description": self.description,
108
108
  "image": self.image,
109
- "endpoints": endpoints,
109
+ "services": endpoints,
110
110
  "registrations": registrations,
111
111
  "supportedTrust": [tm.value if isinstance(tm, TrustModel) else tm for tm in self.trustModels],
112
112
  "active": self.active,
@@ -118,7 +118,8 @@ class RegistrationFile:
118
118
  def from_dict(cls, data: Dict[str, Any]) -> RegistrationFile:
119
119
  """Create from dictionary."""
120
120
  endpoints = []
121
- for ep_data in data.get("endpoints", []):
121
+ raw_services = data.get("services", data.get("endpoints", []))
122
+ for ep_data in raw_services:
122
123
  name = ep_data["name"]
123
124
  # Special handling for agentWallet - it's not a standard endpoint type
124
125
  if name == "agentWallet":
agent0_sdk/core/sdk.py CHANGED
@@ -27,6 +27,7 @@ from .agent import Agent
27
27
  from .indexer import AgentIndexer
28
28
  from .ipfs_client import IPFSClient
29
29
  from .feedback_manager import FeedbackManager
30
+ from .transaction_handle import TransactionHandle
30
31
  from .subgraph_client import SubgraphClient
31
32
 
32
33
 
@@ -273,6 +274,8 @@ class SDK:
273
274
  name=name,
274
275
  description=description,
275
276
  image=image,
277
+ # Default trust model: reputation (if caller doesn't set one explicitly).
278
+ trustModels=[TrustModel.REPUTATION],
276
279
  updatedAt=int(time.time())
277
280
  )
278
281
  return Agent(sdk=self, registration_file=registration_file)
@@ -590,7 +593,7 @@ class SDK:
590
593
  mcpPrompts=reg_file.get('mcpPrompts', []),
591
594
  mcpResources=reg_file.get('mcpResources', []),
592
595
  active=reg_file.get('active', True),
593
- x402support=reg_file.get('x402support', False),
596
+ x402support=reg_file.get('x402Support', reg_file.get('x402support', False)),
594
597
  extras={'averageValue': agent_data.get('averageValue')}
595
598
  )
596
599
  results.append(agent_summary)
@@ -747,7 +750,7 @@ class SDK:
747
750
  mcpPrompts=reg_file.get('mcpPrompts', []),
748
751
  mcpResources=reg_file.get('mcpResources', []),
749
752
  active=reg_file.get('active', True),
750
- x402support=reg_file.get('x402support', False),
753
+ x402support=reg_file.get('x402Support', reg_file.get('x402support', False)),
751
754
  extras={'averageValue': agent_data.get('averageValue')}
752
755
  )
753
756
  results.append(agent_summary)
@@ -797,7 +800,7 @@ class SDK:
797
800
  tag2: Optional[str] = None,
798
801
  endpoint: Optional[str] = None,
799
802
  feedbackFile: Optional[Dict[str, Any]] = None,
800
- ) -> "Feedback":
803
+ ) -> "TransactionHandle[Feedback]":
801
804
  """Give feedback (on-chain first; optional off-chain file upload).
802
805
 
803
806
  - If feedbackFile is None: submit on-chain only (no upload even if IPFS is configured).
@@ -825,7 +828,7 @@ class SDK:
825
828
 
826
829
  def searchFeedback(
827
830
  self,
828
- agentId: "AgentId",
831
+ agentId: Optional["AgentId"] = None,
829
832
  reviewers: Optional[List["Address"]] = None,
830
833
  tags: Optional[List[str]] = None,
831
834
  capabilities: Optional[List[str]] = None,
@@ -837,10 +840,38 @@ class SDK:
837
840
  include_revoked: bool = False,
838
841
  first: int = 100,
839
842
  skip: int = 0,
843
+ agents: Optional[List["AgentId"]] = None,
840
844
  ) -> List["Feedback"]:
841
- """Search feedback for an agent."""
845
+ """Search feedback.
846
+
847
+ Backwards compatible:
848
+ - Previously required `agentId`; it is now optional.
849
+
850
+ New:
851
+ - `agents` can be used to search feedback across multiple agents in one call.
852
+ - `reviewers` can now be used without specifying any agent, enabling "all feedback given by a wallet".
853
+ """
854
+ has_any_filter = any([
855
+ bool(agentId),
856
+ bool(agents),
857
+ bool(reviewers),
858
+ bool(tags),
859
+ bool(capabilities),
860
+ bool(skills),
861
+ bool(tasks),
862
+ bool(names),
863
+ minValue is not None,
864
+ maxValue is not None,
865
+ ])
866
+ if not has_any_filter:
867
+ raise ValueError(
868
+ "searchFeedback requires at least one filter "
869
+ "(agentId/agents/reviewers/tags/capabilities/skills/tasks/names/minValue/maxValue)."
870
+ )
871
+
842
872
  return self.feedback_manager.searchFeedback(
843
873
  agentId=agentId,
874
+ agents=agents,
844
875
  clientAddresses=reviewers,
845
876
  tags=tags,
846
877
  capabilities=capabilities,
@@ -857,13 +888,10 @@ class SDK:
857
888
  def revokeFeedback(
858
889
  self,
859
890
  agentId: "AgentId",
860
- clientAddress: "Address",
861
891
  feedbackIndex: int,
862
- ) -> "Feedback":
863
- """Revoke feedback."""
864
- return self.feedback_manager.revokeFeedback(
865
- agentId, clientAddress, feedbackIndex
866
- )
892
+ ) -> "TransactionHandle[Feedback]":
893
+ """Revoke feedback (submitted-by-default)."""
894
+ return self.feedback_manager.revokeFeedback(agentId, feedbackIndex)
867
895
 
868
896
  def appendResponse(
869
897
  self,
@@ -871,8 +899,8 @@ class SDK:
871
899
  clientAddress: "Address",
872
900
  feedbackIndex: int,
873
901
  response: Dict[str, Any],
874
- ) -> "Feedback":
875
- """Append a response/follow-up to existing feedback."""
902
+ ) -> "TransactionHandle[Feedback]":
903
+ """Append a response/follow-up to existing feedback (submitted-by-default)."""
876
904
  return self.feedback_manager.appendResponse(
877
905
  agentId, clientAddress, feedbackIndex, response
878
906
  )
@@ -890,7 +918,7 @@ class SDK:
890
918
  self,
891
919
  agentId: "AgentId",
892
920
  newOwnerAddress: str,
893
- ) -> Dict[str, Any]:
921
+ ) -> "TransactionHandle[Dict[str, Any]]":
894
922
  """Transfer agent ownership to a new address.
895
923
 
896
924
  Convenience method that loads the agent and calls transfer().