@agenticprimitives/contracts 0.1.0-alpha.2

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.
Files changed (84) hide show
  1. package/AUDIT.md +67 -0
  2. package/CLAUDE.md +40 -0
  3. package/LICENSE +21 -0
  4. package/README.md +45 -0
  5. package/deployments-anvil.json +1 -0
  6. package/deployments-base-sepolia.json +1 -0
  7. package/dist/abi/AgentNameAttributeResolver.json +798 -0
  8. package/dist/abi/AgentNamePredicates.json +1 -0
  9. package/dist/abi/AgentNameRegistry.json +826 -0
  10. package/dist/abi/AgentNameUniversalResolver.json +222 -0
  11. package/dist/abi/AgentProfilePredicates.json +1 -0
  12. package/dist/abi/AgentProfileResolver.json +1044 -0
  13. package/dist/abi/AgentRelationship.json +583 -0
  14. package/dist/abi/AgentRelationshipPredicates.json +1 -0
  15. package/dist/abi/AgenticGovernance.json +259 -0
  16. package/dist/abi/AllowedMethodsEnforcer.json +108 -0
  17. package/dist/abi/AllowedTargetsEnforcer.json +103 -0
  18. package/dist/abi/ApprovedHashRegistry.json +114 -0
  19. package/dist/abi/AttributeStorage.json +557 -0
  20. package/dist/abi/CaveatEnforcerBase.json +130 -0
  21. package/dist/abi/GovernanceManaged.json +43 -0
  22. package/dist/abi/IAttributeReader.json +98 -0
  23. package/dist/abi/ICaveatEnforcer.json +98 -0
  24. package/dist/abi/IDelegationManager.json +211 -0
  25. package/dist/abi/IERC7579Module.json +34 -0
  26. package/dist/abi/IERC7579ModuleLifecycle.json +60 -0
  27. package/dist/abi/IGovernanceView.json +34 -0
  28. package/dist/abi/MultiSendCallOnly.json +29 -0
  29. package/dist/abi/MultiSendCallOnlyHarness.json +42 -0
  30. package/dist/abi/OntologyTermRegistry.json +397 -0
  31. package/dist/abi/P256Verifier.json +1 -0
  32. package/dist/abi/PermissionlessSubregistry.json +207 -0
  33. package/dist/abi/RelationshipTypeRegistry.json +455 -0
  34. package/dist/abi/ShapeRegistry.json +627 -0
  35. package/dist/abi/SmartAgentModuleTypes.json +1 -0
  36. package/dist/abi/TimestampEnforcer.json +108 -0
  37. package/dist/abi/ValueEnforcer.json +103 -0
  38. package/dist/abi/WebAuthnLib.json +1 -0
  39. package/dist/abi/index.d.ts +35 -0
  40. package/dist/abi/index.js +35 -0
  41. package/package.json +48 -0
  42. package/spec.md +52 -0
  43. package/src/AgentAccount.sol +1374 -0
  44. package/src/AgentAccountFactory.sol +274 -0
  45. package/src/ApprovedHashRegistry.sol +57 -0
  46. package/src/IAgentAccount.sol +138 -0
  47. package/src/SmartAgentPaymaster.sol +281 -0
  48. package/src/UniversalSignatureValidator.sol +136 -0
  49. package/src/agency/DelegationManager.sol +374 -0
  50. package/src/agency/ICaveatEnforcer.sol +62 -0
  51. package/src/agency/IDelegationManager.sol +69 -0
  52. package/src/custody/CustodyPolicy.sol +892 -0
  53. package/src/custody/IERC7579Module.sol +60 -0
  54. package/src/enforcers/AllowedMethodsEnforcer.AUDIT.md +51 -0
  55. package/src/enforcers/AllowedMethodsEnforcer.sol +48 -0
  56. package/src/enforcers/AllowedTargetsEnforcer.AUDIT.md +49 -0
  57. package/src/enforcers/AllowedTargetsEnforcer.sol +44 -0
  58. package/src/enforcers/CaveatEnforcerBase.sol +19 -0
  59. package/src/enforcers/QuorumEnforcer.AUDIT.md +71 -0
  60. package/src/enforcers/QuorumEnforcer.sol +191 -0
  61. package/src/enforcers/TimestampEnforcer.AUDIT.md +50 -0
  62. package/src/enforcers/TimestampEnforcer.sol +43 -0
  63. package/src/enforcers/ValueEnforcer.AUDIT.md +51 -0
  64. package/src/enforcers/ValueEnforcer.sol +41 -0
  65. package/src/governance/AgenticGovernance.sol +140 -0
  66. package/src/governance/GovernanceManaged.sol +75 -0
  67. package/src/governance/IGovernance.sol +15 -0
  68. package/src/identity/AgentProfilePredicates.sol +40 -0
  69. package/src/identity/AgentProfileResolver.sol +194 -0
  70. package/src/libraries/MultiSendCallOnly.sol +95 -0
  71. package/src/libraries/P256Verifier.sol +47 -0
  72. package/src/libraries/SignatureSlotRecovery.sol +196 -0
  73. package/src/libraries/WebAuthnLib.sol +164 -0
  74. package/src/naming/AgentNameAttributeResolver.sol +95 -0
  75. package/src/naming/AgentNamePredicates.sol +74 -0
  76. package/src/naming/AgentNameRegistry.sol +362 -0
  77. package/src/naming/AgentNameUniversalResolver.sol +210 -0
  78. package/src/naming/PermissionlessSubregistry.sol +98 -0
  79. package/src/ontology/AttributeStorage.sol +289 -0
  80. package/src/ontology/OntologyTermRegistry.sol +146 -0
  81. package/src/ontology/ShapeRegistry.sol +240 -0
  82. package/src/relationships/AgentRelationship.sol +289 -0
  83. package/src/relationships/AgentRelationshipPredicates.sol +44 -0
  84. package/src/relationships/RelationshipTypeRegistry.sol +143 -0
@@ -0,0 +1,289 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.28;
3
+
4
+ /**
5
+ * @title AgentRelationship
6
+ * @notice Trust-fabric edge store between Smart Agents.
7
+ *
8
+ * One edge per `(subject, object, relationshipType)` triple.
9
+ * Each edge carries a set of role labels (subject-side + object-side
10
+ * implicit; same bag for now) and a lifecycle status.
11
+ *
12
+ * Lifecycle: `PROPOSED → CONFIRMED → ACTIVE → REVOKED` (matches
13
+ * the TypeScript `EdgeStatus` enum in
14
+ * agenticprimitives/agent-relationships).
15
+ *
16
+ * Adapted from smart-agent
17
+ * (`packages/contracts/src/AgentRelationship.sol`, 392 LOC) with the
18
+ * following simplifications per spec 216 § Phase 3:
19
+ *
20
+ * - **No baked-in relationship-type / role constants.** The
21
+ * vocabulary lives in `AgentRelationshipPredicates.sol` (mirrored
22
+ * by the TS SDK in `agent-relationships/src/constants.ts`), and
23
+ * type semantics live in `RelationshipTypeRegistry.sol`. Keeps
24
+ * this contract focused on edge mechanics.
25
+ *
26
+ * - **No `isOwner(address)` fallback in auth.** Our AgentAccount is
27
+ * ERC-7579 modular; quorum belongs in the CustodyPolicy module.
28
+ * Authorization is `msg.sender == subject` (or `object` / the
29
+ * `createdBy` for read-back), trusting the AgentAccount's execute
30
+ * path to gate upstream.
31
+ *
32
+ * - **Add `metadataHash`** field to the edge for off-chain
33
+ * content-hash anchoring (matches the AgentName resolver pattern).
34
+ * Off-chain JSON published at `metadataURI` MUST hash to
35
+ * `metadataHash`; consumers reject mismatched fetches.
36
+ */
37
+ contract AgentRelationship {
38
+ enum EdgeStatus {
39
+ NONE, // 0 — never set (sentinel)
40
+ PROPOSED, // 1
41
+ CONFIRMED, // 2
42
+ ACTIVE, // 3
43
+ REVOKED // 4
44
+ }
45
+
46
+ struct Edge {
47
+ bytes32 edgeId;
48
+ address subject;
49
+ address object_;
50
+ bytes32 relationshipType;
51
+ EdgeStatus status;
52
+ address createdBy;
53
+ uint64 createdAt;
54
+ uint64 updatedAt;
55
+ string metadataURI;
56
+ bytes32 metadataHash;
57
+ }
58
+
59
+ // ─── Storage ────────────────────────────────────────────────────
60
+
61
+ mapping(bytes32 => Edge) private _edges;
62
+ mapping(bytes32 => bytes32[]) private _roles; // edgeId → role[]
63
+ mapping(bytes32 => mapping(bytes32 => bool)) private _hasRole; // edgeId → role → exists
64
+ mapping(address => bytes32[]) private _edgesBySubject;
65
+ mapping(address => bytes32[]) private _edgesByObject;
66
+ mapping(address => mapping(address => mapping(bytes32 => bytes32))) private _byTriple;
67
+
68
+ // ─── Events ─────────────────────────────────────────────────────
69
+
70
+ event EdgeProposed(
71
+ bytes32 indexed edgeId,
72
+ address indexed subject,
73
+ address indexed object_,
74
+ bytes32 relationshipType,
75
+ address createdBy
76
+ );
77
+ event EdgeConfirmed(bytes32 indexed edgeId, address indexed confirmedBy);
78
+ event EdgeActivated(bytes32 indexed edgeId, address indexed activatedBy);
79
+ event EdgeRevoked(bytes32 indexed edgeId, address indexed revokedBy);
80
+ event RoleAdded(bytes32 indexed edgeId, bytes32 indexed role, address indexed updater);
81
+ event RoleRemoved(bytes32 indexed edgeId, bytes32 indexed role, address indexed updater);
82
+ event EdgeMetadataUpdated(bytes32 indexed edgeId, string metadataURI, bytes32 metadataHash, address indexed updater);
83
+
84
+ // ─── Errors ─────────────────────────────────────────────────────
85
+
86
+ error InvalidEdge();
87
+ error EdgeAlreadyExists();
88
+ error EdgeNotFound();
89
+ error RoleAlreadyExists();
90
+ error RoleNotFound();
91
+ error NotAuthorized();
92
+ error InvalidTransition();
93
+
94
+ // ─── Edge ID ────────────────────────────────────────────────────
95
+
96
+ /**
97
+ * @notice Deterministic edge id =
98
+ * `keccak256(abi.encodePacked(subject, object, relationshipType))`.
99
+ * Matches the TS SDK's `computeEdgeId` (lowercased addresses
100
+ * packed in the same order).
101
+ */
102
+ function computeEdgeId(
103
+ address subject,
104
+ address object_,
105
+ bytes32 relationshipType
106
+ ) public pure returns (bytes32) {
107
+ return keccak256(abi.encodePacked(subject, object_, relationshipType));
108
+ }
109
+
110
+ // ─── Propose ────────────────────────────────────────────────────
111
+
112
+ /**
113
+ * @notice Propose a new edge. The caller must be the subject's
114
+ * Smart Agent OR (for relayed flows) any party authorized
115
+ * to act on its behalf upstream.
116
+ *
117
+ * Phase 3 auth model: `msg.sender` is the proposer (typically the
118
+ * subject's AgentAccount execute path). The contract trusts the
119
+ * caller to be gated upstream; cross-side confirmation is required
120
+ * before the edge becomes ACTIVE.
121
+ */
122
+ function proposeEdge(
123
+ address subject,
124
+ address object_,
125
+ bytes32 relationshipType,
126
+ bytes32[] calldata initialRoles,
127
+ string calldata metadataURI,
128
+ bytes32 metadataHash
129
+ ) external returns (bytes32 edgeId) {
130
+ if (subject == address(0) || object_ == address(0)) revert InvalidEdge();
131
+ if (subject == object_) revert InvalidEdge();
132
+ if (msg.sender != subject) revert NotAuthorized();
133
+ edgeId = computeEdgeId(subject, object_, relationshipType);
134
+ if (_edges[edgeId].createdAt != 0) revert EdgeAlreadyExists();
135
+
136
+ _edges[edgeId] = Edge({
137
+ edgeId: edgeId,
138
+ subject: subject,
139
+ object_: object_,
140
+ relationshipType: relationshipType,
141
+ status: EdgeStatus.PROPOSED,
142
+ createdBy: msg.sender,
143
+ createdAt: uint64(block.timestamp),
144
+ updatedAt: uint64(block.timestamp),
145
+ metadataURI: metadataURI,
146
+ metadataHash: metadataHash
147
+ });
148
+
149
+ _edgesBySubject[subject].push(edgeId);
150
+ _edgesByObject[object_].push(edgeId);
151
+ _byTriple[subject][object_][relationshipType] = edgeId;
152
+
153
+ for (uint256 i = 0; i < initialRoles.length; i++) {
154
+ _addRole(edgeId, initialRoles[i]);
155
+ emit RoleAdded(edgeId, initialRoles[i], msg.sender);
156
+ }
157
+
158
+ emit EdgeProposed(edgeId, subject, object_, relationshipType, msg.sender);
159
+ }
160
+
161
+ // ─── Confirm ────────────────────────────────────────────────────
162
+
163
+ /**
164
+ * @notice The object side confirms a PROPOSED edge.
165
+ * PROPOSED → CONFIRMED. Caller MUST be the object.
166
+ */
167
+ function confirmEdge(bytes32 edgeId) external {
168
+ Edge storage e = _edges[edgeId];
169
+ if (e.createdAt == 0) revert EdgeNotFound();
170
+ if (e.status != EdgeStatus.PROPOSED) revert InvalidTransition();
171
+ if (msg.sender != e.object_) revert NotAuthorized();
172
+ e.status = EdgeStatus.CONFIRMED;
173
+ e.updatedAt = uint64(block.timestamp);
174
+ emit EdgeConfirmed(edgeId, msg.sender);
175
+ }
176
+
177
+ /**
178
+ * @notice Activate a CONFIRMED edge. CONFIRMED → ACTIVE. Either
179
+ * side may activate; off-chain resolvers may sequence the
180
+ * activation when ancillary checks pass (e.g. timelock).
181
+ */
182
+ function activateEdge(bytes32 edgeId) external {
183
+ Edge storage e = _edges[edgeId];
184
+ if (e.createdAt == 0) revert EdgeNotFound();
185
+ if (e.status != EdgeStatus.CONFIRMED) revert InvalidTransition();
186
+ if (msg.sender != e.subject && msg.sender != e.object_) revert NotAuthorized();
187
+ e.status = EdgeStatus.ACTIVE;
188
+ e.updatedAt = uint64(block.timestamp);
189
+ emit EdgeActivated(edgeId, msg.sender);
190
+ }
191
+
192
+ /**
193
+ * @notice Permissionless revocation from either side. Spec 216
194
+ * security invariant: either party may revoke
195
+ * unilaterally. Once REVOKED, the edge is terminal.
196
+ */
197
+ function revokeEdge(bytes32 edgeId) external {
198
+ Edge storage e = _edges[edgeId];
199
+ if (e.createdAt == 0) revert EdgeNotFound();
200
+ if (e.status == EdgeStatus.REVOKED) revert InvalidTransition();
201
+ if (msg.sender != e.subject && msg.sender != e.object_) revert NotAuthorized();
202
+ e.status = EdgeStatus.REVOKED;
203
+ e.updatedAt = uint64(block.timestamp);
204
+ emit EdgeRevoked(edgeId, msg.sender);
205
+ }
206
+
207
+ // ─── Roles ──────────────────────────────────────────────────────
208
+
209
+ /**
210
+ * @notice Add a role to an existing edge. Either side may add
211
+ * roles to the edge's role bag.
212
+ */
213
+ function addRole(bytes32 edgeId, bytes32 role) external {
214
+ Edge storage e = _edges[edgeId];
215
+ if (e.createdAt == 0) revert EdgeNotFound();
216
+ if (msg.sender != e.subject && msg.sender != e.object_) revert NotAuthorized();
217
+ if (_hasRole[edgeId][role]) revert RoleAlreadyExists();
218
+ _addRole(edgeId, role);
219
+ e.updatedAt = uint64(block.timestamp);
220
+ emit RoleAdded(edgeId, role, msg.sender);
221
+ }
222
+
223
+ function removeRole(bytes32 edgeId, bytes32 role) external {
224
+ Edge storage e = _edges[edgeId];
225
+ if (e.createdAt == 0) revert EdgeNotFound();
226
+ if (msg.sender != e.subject && msg.sender != e.object_) revert NotAuthorized();
227
+ if (!_hasRole[edgeId][role]) revert RoleNotFound();
228
+ _removeRole(edgeId, role);
229
+ e.updatedAt = uint64(block.timestamp);
230
+ emit RoleRemoved(edgeId, role, msg.sender);
231
+ }
232
+
233
+ // ─── Metadata ───────────────────────────────────────────────────
234
+
235
+ function setMetadata(bytes32 edgeId, string calldata metadataURI, bytes32 metadataHash) external {
236
+ Edge storage e = _edges[edgeId];
237
+ if (e.createdAt == 0) revert EdgeNotFound();
238
+ if (msg.sender != e.subject && msg.sender != e.object_) revert NotAuthorized();
239
+ e.metadataURI = metadataURI;
240
+ e.metadataHash = metadataHash;
241
+ e.updatedAt = uint64(block.timestamp);
242
+ emit EdgeMetadataUpdated(edgeId, metadataURI, metadataHash, msg.sender);
243
+ }
244
+
245
+ // ─── Queries ────────────────────────────────────────────────────
246
+
247
+ function getEdge(bytes32 edgeId) external view returns (Edge memory) {
248
+ Edge memory e = _edges[edgeId];
249
+ if (e.createdAt == 0) revert EdgeNotFound();
250
+ return e;
251
+ }
252
+ function getRoles(bytes32 edgeId) external view returns (bytes32[] memory) {
253
+ return _roles[edgeId];
254
+ }
255
+ function hasRole(bytes32 edgeId, bytes32 role) external view returns (bool) {
256
+ return _hasRole[edgeId][role];
257
+ }
258
+ function getEdgesBySubject(address subject) external view returns (bytes32[] memory) {
259
+ return _edgesBySubject[subject];
260
+ }
261
+ function getEdgesByObject(address object_) external view returns (bytes32[] memory) {
262
+ return _edgesByObject[object_];
263
+ }
264
+ function getEdgeByTriple(address subject, address object_, bytes32 relationshipType) external view returns (bytes32) {
265
+ return _byTriple[subject][object_][relationshipType];
266
+ }
267
+ function edgeExists(bytes32 edgeId) external view returns (bool) {
268
+ return _edges[edgeId].createdAt != 0;
269
+ }
270
+
271
+ // ─── Internal ───────────────────────────────────────────────────
272
+
273
+ function _addRole(bytes32 edgeId, bytes32 role) internal {
274
+ _roles[edgeId].push(role);
275
+ _hasRole[edgeId][role] = true;
276
+ }
277
+
278
+ function _removeRole(bytes32 edgeId, bytes32 role) internal {
279
+ _hasRole[edgeId][role] = false;
280
+ bytes32[] storage roles = _roles[edgeId];
281
+ for (uint256 i = 0; i < roles.length; i++) {
282
+ if (roles[i] == role) {
283
+ roles[i] = roles[roles.length - 1];
284
+ roles.pop();
285
+ break;
286
+ }
287
+ }
288
+ }
289
+ }
@@ -0,0 +1,44 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.28;
3
+
4
+ /**
5
+ * @title AgentRelationshipPredicates
6
+ * @notice Canonical bytes32 ids for the agent-relationships trust
7
+ * fabric (relationship types + role labels). Single source of
8
+ * truth shared by:
9
+ * - AgentRelationship.sol (uses the IDs as bytes32 keys)
10
+ * - Deploy.s.sol (bootstraps the type set in
11
+ * RelationshipTypeRegistry)
12
+ * - agenticprimitives/agent-relationships SDK (mirror
13
+ * constants in src/constants.ts)
14
+ *
15
+ * The six well-known relationship types match spec 216 § 5; matching
16
+ * `keccak256(name)` IDs are derived here. The role set is deliberately
17
+ * narrower than the smart-agent original (start small, add via PR + a
18
+ * golden-vector test when concrete consumers need a role).
19
+ */
20
+ library AgentRelationshipPredicates {
21
+ // ─── Well-known relationship types ──────────────────────────────
22
+
23
+ /// Membership: subject is a member of object.
24
+ bytes32 internal constant HAS_MEMBER = keccak256("HAS_MEMBER");
25
+ /// Governance: subject has governance authority over object.
26
+ bytes32 internal constant HAS_GOVERNANCE_OVER = keccak256("HAS_GOVERNANCE_OVER");
27
+ /// Validation: subject trusts object as a validator / verifier.
28
+ bytes32 internal constant VALIDATION_TRUST = keccak256("VALIDATION_TRUST");
29
+ /// Bilateral partnership / cross-recognition (symmetric).
30
+ bytes32 internal constant PARTNERSHIP = keccak256("PARTNERSHIP");
31
+ /// Operational delegation marker: subject operates on behalf of object.
32
+ bytes32 internal constant OPERATES_ON_BEHALF_OF = keccak256("OPERATES_ON_BEHALF_OF");
33
+ /// Endorsement: subject recommends object.
34
+ bytes32 internal constant RECOMMENDS = keccak256("RECOMMENDS");
35
+
36
+ // ─── Well-known roles ───────────────────────────────────────────
37
+
38
+ bytes32 internal constant ROLE_MEMBER = keccak256("MEMBER");
39
+ bytes32 internal constant ROLE_BOARD_MEMBER = keccak256("BOARD_MEMBER");
40
+ bytes32 internal constant ROLE_OPERATOR = keccak256("OPERATOR");
41
+ bytes32 internal constant ROLE_VALIDATOR = keccak256("VALIDATOR");
42
+ bytes32 internal constant ROLE_TREASURER = keccak256("TREASURER");
43
+ bytes32 internal constant ROLE_RECOVERY_CONTACT = keccak256("RECOVERY_CONTACT");
44
+ }
@@ -0,0 +1,143 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.28;
3
+
4
+ /**
5
+ * @title RelationshipTypeRegistry
6
+ * @notice Governed registry of relationship-type semantic metadata
7
+ * used by `AgentRelationship` to interpret edges.
8
+ *
9
+ * Each relationship type can be annotated with:
10
+ * - `isHierarchical` — edges induce parent → child semantics
11
+ * - `isTransitive` — supports ancestor / descendant inference
12
+ * - `isSymmetric` — direction-independent (`PARTNERSHIP`)
13
+ *
14
+ * Governance: only `governor` may register / update / deactivate
15
+ * types. Same pattern as `OntologyTermRegistry` (NS Phase 3 stack);
16
+ * deployer is the bootstrap governor; rotation via
17
+ * `transferGovernor`.
18
+ *
19
+ * Adapted from smart-agent
20
+ * (`packages/contracts/src/RelationshipTypeRegistry.sol`, 138 LOC) —
21
+ * structurally identical with constructor zero-governor guard.
22
+ */
23
+ contract RelationshipTypeRegistry {
24
+ struct TypeSemantics {
25
+ bytes32 relationshipType;
26
+ string label;
27
+ bool isHierarchical;
28
+ bool isTransitive;
29
+ bool isSymmetric;
30
+ bool active;
31
+ uint256 registeredAt;
32
+ }
33
+
34
+ address public governor;
35
+ mapping(bytes32 => TypeSemantics) private _types;
36
+ bytes32[] private _typeIds;
37
+
38
+ event TypeRegistered(bytes32 indexed relationshipType, string label, bool isHierarchical, bool isTransitive, bool isSymmetric);
39
+ event TypeDeactivated(bytes32 indexed relationshipType);
40
+ event TypeActivated(bytes32 indexed relationshipType);
41
+ event TypeUpdated(bytes32 indexed relationshipType, bool isHierarchical, bool isTransitive, bool isSymmetric);
42
+ event GovernorTransferred(address indexed oldGovernor, address indexed newGovernor);
43
+
44
+ error NotGovernor();
45
+ error TypeExists();
46
+ error TypeNotFound();
47
+ error ZeroGovernor();
48
+
49
+ modifier onlyGovernor() {
50
+ if (msg.sender != governor) revert NotGovernor();
51
+ _;
52
+ }
53
+
54
+ constructor(address governor_) {
55
+ if (governor_ == address(0)) revert ZeroGovernor();
56
+ governor = governor_;
57
+ }
58
+
59
+ function transferGovernor(address newGovernor) external onlyGovernor {
60
+ if (newGovernor == address(0)) revert ZeroGovernor();
61
+ emit GovernorTransferred(governor, newGovernor);
62
+ governor = newGovernor;
63
+ }
64
+
65
+ // ─── Registration ───────────────────────────────────────────────
66
+
67
+ function registerType(
68
+ bytes32 relationshipType,
69
+ string calldata label,
70
+ bool hierarchical,
71
+ bool transitive,
72
+ bool symmetric
73
+ ) external onlyGovernor {
74
+ if (_types[relationshipType].registeredAt != 0) revert TypeExists();
75
+ _types[relationshipType] = TypeSemantics({
76
+ relationshipType: relationshipType,
77
+ label: label,
78
+ isHierarchical: hierarchical,
79
+ isTransitive: transitive,
80
+ isSymmetric: symmetric,
81
+ active: true,
82
+ registeredAt: block.timestamp
83
+ });
84
+ _typeIds.push(relationshipType);
85
+ emit TypeRegistered(relationshipType, label, hierarchical, transitive, symmetric);
86
+ }
87
+
88
+ function updateSemantics(
89
+ bytes32 relationshipType,
90
+ bool hierarchical,
91
+ bool transitive,
92
+ bool symmetric
93
+ ) external onlyGovernor {
94
+ if (_types[relationshipType].registeredAt == 0) revert TypeNotFound();
95
+ _types[relationshipType].isHierarchical = hierarchical;
96
+ _types[relationshipType].isTransitive = transitive;
97
+ _types[relationshipType].isSymmetric = symmetric;
98
+ emit TypeUpdated(relationshipType, hierarchical, transitive, symmetric);
99
+ }
100
+
101
+ function deactivateType(bytes32 relationshipType) external onlyGovernor {
102
+ if (_types[relationshipType].registeredAt == 0) revert TypeNotFound();
103
+ _types[relationshipType].active = false;
104
+ emit TypeDeactivated(relationshipType);
105
+ }
106
+
107
+ function activateType(bytes32 relationshipType) external onlyGovernor {
108
+ if (_types[relationshipType].registeredAt == 0) revert TypeNotFound();
109
+ _types[relationshipType].active = true;
110
+ emit TypeActivated(relationshipType);
111
+ }
112
+
113
+ // ─── Queries ────────────────────────────────────────────────────
114
+
115
+ function getTypeSemantics(bytes32 relationshipType) external view returns (TypeSemantics memory) {
116
+ return _types[relationshipType];
117
+ }
118
+
119
+ function isRegistered(bytes32 relationshipType) external view returns (bool) {
120
+ return _types[relationshipType].registeredAt != 0;
121
+ }
122
+ function isActive(bytes32 relationshipType) external view returns (bool) {
123
+ return _types[relationshipType].active;
124
+ }
125
+ function isHierarchical(bytes32 relationshipType) external view returns (bool) {
126
+ return _types[relationshipType].isHierarchical;
127
+ }
128
+ function isTransitive(bytes32 relationshipType) external view returns (bool) {
129
+ return _types[relationshipType].isTransitive;
130
+ }
131
+ function isSymmetric(bytes32 relationshipType) external view returns (bool) {
132
+ return _types[relationshipType].isSymmetric;
133
+ }
134
+ function typeCount() external view returns (uint256) {
135
+ return _typeIds.length;
136
+ }
137
+ function getTypeAt(uint256 index) external view returns (bytes32) {
138
+ return _typeIds[index];
139
+ }
140
+ function getAllTypeIds() external view returns (bytes32[] memory) {
141
+ return _typeIds;
142
+ }
143
+ }