nebula-ai-plugin-onchain 0.1.0 → 0.2.0

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nebula-ai-plugin-onchain",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "The Mantle on-chain tools for nebula: transfers, Agni + Merchant Moe swaps, Aave V3 lending, DeFiLlama discovery, and ERC-8004 identity — every write policy + simulation gated",
6
6
  "license": "MIT",
@@ -45,7 +45,7 @@
45
45
  "test": "bun test"
46
46
  },
47
47
  "dependencies": {
48
- "nebula-ai-core": "0.1.0",
48
+ "nebula-ai-core": "0.2.0",
49
49
  "viem": "^2.21.55",
50
50
  "zod": "^3.23.8"
51
51
  }
package/src/index.ts CHANGED
@@ -68,6 +68,13 @@ import { makeSwapExecute, makeSwapQuote } from './tools/swap'
68
68
  import { makeSwapBest, makeSwapCompare } from './tools/swap-best'
69
69
  import { makeTokensInfo } from './tools/tokens-info'
70
70
  import { makeChainSend } from './tools/transfer'
71
+ import {
72
+ makeReputationGive,
73
+ makeReputationShow,
74
+ makeValidationRequest,
75
+ makeValidationRespond,
76
+ makeValidationShow,
77
+ } from './tools/trust'
71
78
  import { makeChainUnwrap, makeChainWrap } from './tools/wrap'
72
79
  import type { OnchainRuntimeContext } from './types'
73
80
 
@@ -122,6 +129,11 @@ const plugin: NativePlugin = {
122
129
 
123
130
  ctx.registerTool(makeIdentityResolve(onchain) as ToolDef)
124
131
  ctx.registerTool(makeIdentityRegister(onchain) as ToolDef)
132
+ ctx.registerTool(makeReputationGive(onchain) as ToolDef)
133
+ ctx.registerTool(makeReputationShow(onchain) as ToolDef)
134
+ ctx.registerTool(makeValidationRequest(onchain) as ToolDef)
135
+ ctx.registerTool(makeValidationRespond(onchain) as ToolDef)
136
+ ctx.registerTool(makeValidationShow(onchain) as ToolDef)
125
137
 
126
138
  ctx.registerTool(makeChainBlock(onchain) as ToolDef)
127
139
  ctx.registerTool(makeChainGas(onchain) as ToolDef)
@@ -0,0 +1,255 @@
1
+ /**
2
+ * ERC-8004 Reputation + Validation brain tools — `reputation.*` and
3
+ * `validation.*`. Lets the agent rate other agents, read reputation, and
4
+ * request/respond/read on-chain validations of agent output.
5
+ */
6
+ import {
7
+ type ToolDef,
8
+ agentIdByAddress,
9
+ getReputation,
10
+ getValidation,
11
+ giveFeedback,
12
+ requestValidation,
13
+ resolveRegistryAddress,
14
+ resolveReputationRegistry,
15
+ resolveValidationRegistry,
16
+ respondValidation,
17
+ } from 'nebula-ai-core'
18
+ import { type Hex, keccak256, toHex } from 'viem'
19
+ import { z } from 'zod'
20
+ import type { OnchainRuntimeContext } from '../types'
21
+
22
+ const noRep = (network: string) =>
23
+ ({
24
+ ok: false as const,
25
+ error: `No ERC-8004 Reputation Registry for ${network}. Set NEBULA_REPUTATION_REGISTRY.`,
26
+ }) as const
27
+ const noVal = (network: string) =>
28
+ ({
29
+ ok: false as const,
30
+ error: `No ERC-8004 Validation Registry for ${network}. Set NEBULA_VALIDATION_REGISTRY.`,
31
+ }) as const
32
+
33
+ // ─── reputation.give ──
34
+ const GiveSchema = z.object({
35
+ agentId: z.string().describe('The ERC-8004 agent id to rate.'),
36
+ score: z.number().int().min(0).max(100).describe('Score 0–100.'),
37
+ tag: z.string().optional().describe('Short category, e.g. "trade-exec", "accuracy".'),
38
+ uri: z.string().optional().describe('Optional URI to detailed feedback.'),
39
+ })
40
+ export function makeReputationGive(
41
+ ctx: OnchainRuntimeContext,
42
+ ): ToolDef<z.infer<typeof GiveSchema>> {
43
+ return {
44
+ name: 'reputation.give',
45
+ description:
46
+ 'Record on-chain ERC-8004 reputation feedback about another agent (score 0–100 + tag + optional URI). You cannot rate an agent you own. Writes a tx.',
47
+ searchHint: 'erc-8004 reputation feedback rate review score agent trust give rating',
48
+ schema: GiveSchema,
49
+ handler: async args => {
50
+ const registry = resolveReputationRegistry(ctx.network)
51
+ if (!registry) return noRep(ctx.network)
52
+ if (!ctx.walletClient?.account) return { ok: false as const, error: 'no signer available' }
53
+ try {
54
+ const { txHash } = await giveFeedback({
55
+ walletClient: ctx.walletClient,
56
+ publicClient: ctx.publicClient,
57
+ registry,
58
+ agentId: BigInt(args.agentId),
59
+ score: args.score,
60
+ tag: args.tag ?? '',
61
+ uri: args.uri ?? '',
62
+ })
63
+ return { ok: true as const, data: { txHash, agentId: args.agentId, score: args.score } }
64
+ } catch (e) {
65
+ return { ok: false as const, error: (e as Error).message }
66
+ }
67
+ },
68
+ }
69
+ }
70
+
71
+ // ─── reputation.show ──
72
+ const ShowRepSchema = z.object({
73
+ agentId: z.string().optional().describe('Agent id. Or pass `address` to reverse-resolve.'),
74
+ address: z
75
+ .string()
76
+ .optional()
77
+ .describe('Resolve the agent at this EOA, then read its reputation.'),
78
+ })
79
+ export function makeReputationShow(
80
+ ctx: OnchainRuntimeContext,
81
+ ): ToolDef<z.infer<typeof ShowRepSchema>> {
82
+ return {
83
+ name: 'reputation.show',
84
+ description:
85
+ "Read an agent's on-chain ERC-8004 reputation: number of ratings + average score (0–100). By agentId or by EOA. Read-only.",
86
+ searchHint: 'erc-8004 reputation read score rating average agent trust reputation lookup',
87
+ schema: ShowRepSchema,
88
+ handler: async args => {
89
+ const registry = resolveReputationRegistry(ctx.network)
90
+ if (!registry) return noRep(ctx.network)
91
+ try {
92
+ let agentId: bigint
93
+ if (args.agentId) agentId = BigInt(args.agentId)
94
+ else {
95
+ const idReg = resolveRegistryAddress(ctx.network)
96
+ if (!idReg)
97
+ return { ok: false as const, error: 'no identity registry to reverse-resolve' }
98
+ agentId = await agentIdByAddress({
99
+ publicClient: ctx.publicClient,
100
+ registry: idReg,
101
+ agentAddress: (args.address ?? ctx.agentEoa) as `0x${string}`,
102
+ })
103
+ if (agentId === 0n) return { ok: true as const, data: { registered: false } }
104
+ }
105
+ const { count, averageScore } = await getReputation({
106
+ publicClient: ctx.publicClient,
107
+ registry,
108
+ agentId,
109
+ })
110
+ return {
111
+ ok: true as const,
112
+ data: {
113
+ agentId: agentId.toString(),
114
+ ratings: count.toString(),
115
+ averageScore: averageScore.toString(),
116
+ },
117
+ }
118
+ } catch (e) {
119
+ return { ok: false as const, error: (e as Error).message }
120
+ }
121
+ },
122
+ }
123
+ }
124
+
125
+ // ─── validation.request ──
126
+ const ReqSchema = z.object({
127
+ agentId: z.string().describe('Agent id whose output is being validated.'),
128
+ data: z
129
+ .string()
130
+ .describe('The output/work to validate (hashed on-chain as keccak256), or a 0x32-byte hash.'),
131
+ uri: z.string().optional().describe('Optional URI to the work/context.'),
132
+ })
133
+ export function makeValidationRequest(
134
+ ctx: OnchainRuntimeContext,
135
+ ): ToolDef<z.infer<typeof ReqSchema>> {
136
+ return {
137
+ name: 'validation.request',
138
+ description:
139
+ "Open an ERC-8004 validation request for an agent's output. `data` is hashed (keccak256) on-chain as the anchor, or pass a 0x 32-byte hash directly. Returns a requestId. Writes a tx.",
140
+ searchHint: 'erc-8004 validation request verify agent output proof attest validate',
141
+ schema: ReqSchema,
142
+ handler: async args => {
143
+ const registry = resolveValidationRegistry(ctx.network)
144
+ if (!registry) return noVal(ctx.network)
145
+ if (!ctx.walletClient?.account) return { ok: false as const, error: 'no signer available' }
146
+ try {
147
+ const dataHash: Hex = /^0x[0-9a-fA-F]{64}$/.test(args.data)
148
+ ? (args.data as Hex)
149
+ : keccak256(toHex(args.data))
150
+ const { requestId, txHash } = await requestValidation({
151
+ walletClient: ctx.walletClient,
152
+ publicClient: ctx.publicClient,
153
+ registry,
154
+ agentId: BigInt(args.agentId),
155
+ dataHash,
156
+ uri: args.uri ?? '',
157
+ })
158
+ return { ok: true as const, data: { requestId: requestId.toString(), dataHash, txHash } }
159
+ } catch (e) {
160
+ return { ok: false as const, error: (e as Error).message }
161
+ }
162
+ },
163
+ }
164
+ }
165
+
166
+ // ─── validation.respond ──
167
+ const RespSchema = z.object({
168
+ requestId: z.string().describe('The validation request id to respond to.'),
169
+ passed: z.boolean().describe('Did the output pass validation?'),
170
+ score: z
171
+ .number()
172
+ .int()
173
+ .min(0)
174
+ .max(100)
175
+ .optional()
176
+ .describe('Optional 0–100 confidence/quality score.'),
177
+ uri: z.string().optional().describe('Optional URI to the verification artifact.'),
178
+ })
179
+ export function makeValidationRespond(
180
+ ctx: OnchainRuntimeContext,
181
+ ): ToolDef<z.infer<typeof RespSchema>> {
182
+ return {
183
+ name: 'validation.respond',
184
+ description:
185
+ 'Respond to an ERC-8004 validation request as a validator: pass/fail + optional score + URI. You cannot validate your own request. Writes a tx.',
186
+ searchHint: 'erc-8004 validation respond validator verdict pass fail attest result',
187
+ schema: RespSchema,
188
+ handler: async args => {
189
+ const registry = resolveValidationRegistry(ctx.network)
190
+ if (!registry) return noVal(ctx.network)
191
+ if (!ctx.walletClient?.account) return { ok: false as const, error: 'no signer available' }
192
+ try {
193
+ const { txHash } = await respondValidation({
194
+ walletClient: ctx.walletClient,
195
+ publicClient: ctx.publicClient,
196
+ registry,
197
+ requestId: BigInt(args.requestId),
198
+ passed: args.passed,
199
+ score: args.score ?? 0,
200
+ uri: args.uri ?? '',
201
+ })
202
+ return {
203
+ ok: true as const,
204
+ data: { requestId: args.requestId, passed: args.passed, txHash },
205
+ }
206
+ } catch (e) {
207
+ return { ok: false as const, error: (e as Error).message }
208
+ }
209
+ },
210
+ }
211
+ }
212
+
213
+ // ─── validation.show ──
214
+ const ShowValSchema = z.object({
215
+ requestId: z.string().describe('The validation request id to read.'),
216
+ })
217
+ export function makeValidationShow(
218
+ ctx: OnchainRuntimeContext,
219
+ ): ToolDef<z.infer<typeof ShowValSchema>> {
220
+ return {
221
+ name: 'validation.show',
222
+ description:
223
+ 'Read an ERC-8004 validation request + its response (requester, validator, pass/fail, score, dataHash). Read-only.',
224
+ searchHint: 'erc-8004 validation read result status verdict request lookup',
225
+ schema: ShowValSchema,
226
+ handler: async args => {
227
+ const registry = resolveValidationRegistry(ctx.network)
228
+ if (!registry) return noVal(ctx.network)
229
+ try {
230
+ const v = await getValidation({
231
+ publicClient: ctx.publicClient,
232
+ registry,
233
+ requestId: BigInt(args.requestId),
234
+ })
235
+ return {
236
+ ok: true as const,
237
+ data: {
238
+ requestId: args.requestId,
239
+ agentId: v.agentId.toString(),
240
+ requester: v.requester,
241
+ validator: v.validator,
242
+ responded: v.responded,
243
+ passed: v.passed,
244
+ score: v.score,
245
+ dataHash: v.dataHash,
246
+ requestUri: v.requestUri,
247
+ responseUri: v.responseUri,
248
+ },
249
+ }
250
+ } catch (e) {
251
+ return { ok: false as const, error: (e as Error).message }
252
+ }
253
+ },
254
+ }
255
+ }