@taifoon/jev 0.3.0 → 0.4.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/CHANGELOG.md ADDED
@@ -0,0 +1,32 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@taifoon/jev`. Versions follow the GitHub releases of this repository; each is published to npm
4
+ with provenance by `.github/workflows/publish.yml`.
5
+
6
+ ## 0.4.0
7
+
8
+ - `verify` reads a trusted record past an endpoint that serves only recent logs. `JevAnswerLog.recordedAt` names the block
9
+ of a trusted record; when an endpoint answers `[]` for that block, the next endpoint of the rotation is asked. A record
10
+ reads `not_recorded` only when every endpoint that answered held no row; no answer at all is still `rpc_unavailable`.
11
+ (Seen on Base for a record 1.35M blocks old.) Two tests cover both outcomes.
12
+ - `src/attest.ts`: the defaults of an unknown chain are their own typed value, so the copy compiles under stricter
13
+ TypeScript settings (TS2783). Types only; the calls it returns are unchanged.
14
+ - Taifoon mainnet (3692781) is Jev's record home: `record()` builds the `JevAnswerLog.record` and `JevDecisionLog.record`
15
+ calls for it by default (JevDecisionLog `0xaEbF9084Bb32eB2E0F58537526A8877dd1436b23`, JevAnswerLogV2
16
+ `0xC08dfc61d702AEA45d1EF2515d8BDA36d548C75c`), and `verify --chain taifoon` reads them. It has no GradeStampRegistry
17
+ yet; the stamp is listed under `missing`.
18
+ - Base: answers are written to JevAnswerLogV2 (`0xC08dfc61d702AEA45d1EF2515d8BDA36d548C75c`, from block 52639649).
19
+ `verify` reads V2 first, then the earlier JevAnswerLog (`0x8e9B9cE86a2d55c10318607b0c815B7B8C66254d`), so every
20
+ earlier record still verifies. Two tests cover both logs.
21
+ - README: what you get, how it works, and where Jev's records live.
22
+ - package: CHANGELOG.md ships in the tarball; search keywords.
23
+
24
+ ## 0.3.0 (2026-10-14)
25
+
26
+ - Recording is on by default: every grade returns the unsigned `JevAnswerLog.record`, `JevDecisionLog.record` and
27
+ `GradeStampRegistry.stamp` calls for the Taifoon devnet 36927 and the Taifoon mainnet 3692781 (`--record none` turns
28
+ them off; a contract a network does not have is listed in `missing`). One shared attestation helper for the mark, the
29
+ attestation and the stamp.
30
+ - Optional fields of the attestation helper accept `undefined` (`exactOptionalPropertyTypes`).
31
+
32
+ Earlier versions: see the [releases](https://github.com/taifoon-io/jev/releases).
package/README.md CHANGED
@@ -1,24 +1,83 @@
1
- # @taifoon/jev
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/taifoon-io/jev/main/assets/banner.svg" alt="Jev for agent jobs" width="100%">
3
+ </p>
4
+
5
+ <p align="center">
6
+ <a href="https://www.npmjs.com/package/@taifoon/jev"><img src="https://img.shields.io/npm/v/@taifoon/jev?style=flat-square&labelColor=000000&color=3DA5FF&label=npm" alt="npm"></a>
7
+ <a href="https://www.npmjs.com/package/@taifoon/jev#provenance"><img src="https://img.shields.io/badge/provenance-signed-00B5B8?style=flat-square&labelColor=000000" alt="provenance"></a>
8
+ <a href="LICENSE"><img src="https://img.shields.io/npm/l/@taifoon/jev?style=flat-square&labelColor=000000&color=E6F0F7" alt="licence"></a>
9
+ <a href="https://www.npmjs.com/package/@taifoon/jev"><img src="https://img.shields.io/npm/dm/@taifoon/jev?style=flat-square&labelColor=000000&color=3DA5FF" alt="downloads"></a>
10
+ </p>
2
11
 
3
12
  **Did the AI agent do the work it was paid for? Ask Jev, and get an answer you can check.**
4
13
 
5
- - **What:** a grader for agent jobs. Code checks the facts, TypeSafe's Jev answers four closed questions about the
6
- delivery, and code turns the answers into complete, reject or needs review, with a receipt anyone can recompute.
7
- - **Why:** agents are paid on chain for work nobody reads. A grade with its full probabilities, a published rubric and
8
- an appeal path is something a buyer, a seller and a contract can all rely on.
9
- - **How:** replay a Base job, graded by Jev, offline and with no key:
14
+ Code checks the facts of the delivery, TypeSafe's Jev answers four closed questions about it, and code turns the answers
15
+ into **complete**, **reject** or **needs review**, with a receipt anyone can recompute, the unsigned calls that record it on
16
+ chain and the one call that ends the job on its protocol (ERC-8183, Virtuals ACP, BitAgent). No key inside, and it signs
17
+ nothing.
10
18
 
11
- ```
19
+ ## 30 seconds
20
+
21
+ ```sh
12
22
  npx @taifoon/jev run --demo
13
23
  ```
14
24
 
25
+ Offline, no key: replays BitAgent job 7287 on Base as Jev graded it, composes **reject**, and arrives at the decision
26
+ digest that is anchored on chain. Then read a recorded grade back from Base:
27
+
28
+ ```sh
29
+ npx @taifoon/jev verify decision-1790522234088-10691d5017 --network base
30
+ ```
31
+
32
+ It recomputes the record's digests offline and finds both rows on Base (`"status": "recorded"`).
33
+
15
34
  ![npx @taifoon/jev run --demo: a BitAgent job on Base, graded by Jev, checked against the chain](https://raw.githubusercontent.com/taifoon-io/jev/main/docs/demo.gif)
16
35
 
17
- Then grade your own: with no key, on your free grades on the Taifoon coordination layer (3 a day per caller), or with
18
- your TypeSafe key from [console.typesafe.ai](https://console.typesafe.ai). Installing it also
19
- installs Taifoon's other packages (@taifoon/jev-wilson, @taifoon/n8n-nodes-typesafe). No key inside, and it signs nothing. Every grade comes with the unsigned calls that record it on chain (the Taifoon devnet and mainnet by default, any network you name; `--record none` turns them off).
36
+ ## What you get
37
+
38
+ | | |
39
+ |---|---|
40
+ | **A verdict with its reasons** | complete, reject or needs review, with every answer's full probability distribution |
41
+ | **A receipt anyone can recompute** | digests of the evidence, the answers and the decision; `verify()` recomputes them offline |
42
+ | **The on-chain record, unsigned** | `JevAnswerLog.record`, `JevDecisionLog.record` and `GradeStampRegistry.stamp` calls for your wallet |
43
+ | **The call that ends the job** | the evaluator call for `assurance-hook`, `judge-adapter`, `virtuals-erc8183`, `virtuals-memo-acp`, `bitagent-erc8183` |
44
+ | **A cover price for the next job** | the seller's premium from its record, through [`@taifoon/jev-wilson`](https://www.npmjs.com/package/@taifoon/jev-wilson) |
45
+ | **No key needed to start** | 3 free grades a day per caller on the Taifoon coordination layer, or your own TypeSafe key |
46
+
47
+ ## How it works
48
+
49
+ ```
50
+ the job as the buyer wrote it (task, criteria, source, delivered)
51
+ │ prepareJob()
52
+ ▼
53
+ code checks the facts ─── a check fails ──▶ reject (Jev is not asked)
54
+ │ all pass
55
+ ▼
56
+ Jev answers RUBRIC_v1: spec_met · unsupported_claim · ending · cheat_shaped
57
+ │ grade()
58
+ ▼
59
+ code composes THRESHOLDS_v1 ──▶ complete | reject | needs_review + receipt
60
+ │ record() · evaluatorCall()
61
+ ▼
62
+ unsigned calls: the answer log, the decision log, the evaluator seat (you sign)
63
+ │ verify()
64
+ ▼
65
+ digests recomputed, rows found on chain
66
+ ```
67
+
68
+ ## Install
69
+
70
+ ```sh
71
+ npm i @taifoon/jev
72
+ ```
73
+
74
+ Node.js 20 or later. Installing it also installs Taifoon's other packages (@taifoon/jev-wilson,
75
+ @taifoon/n8n-nodes-typesafe); nothing third-party. Grade with no key on your free grades on the Taifoon coordination layer
76
+ (3 a day per caller), or with your TypeSafe key from [console.typesafe.ai](https://console.typesafe.ai). Every grade comes
77
+ with the unsigned calls that record it on chain (the Taifoon devnet and mainnet by default, any network you name;
78
+ `--record none` turns them off).
20
79
 
21
- ## Quick start
80
+ ## Grade your own job
22
81
 
23
82
  An agent was hired for a job. The grader answers one question: did it do the work as the task laid it out? You give
24
83
  it the job the way the buyer wrote it, and it does the rest:
@@ -182,8 +241,30 @@ npx @taifoon/jev workflows export ./jev-workflows
182
241
  ## Chains
183
242
 
184
243
  `verify` reads the Jev logs on every chain they are deployed to: the Taifoon devnet (36927), Base (8453), Arbitrum One
185
- (42161), Arc (5042) and Monad (143). Robinhood Chain (4663), Taifoon mainnet (3692781) and Ethereum (1) are known
186
- chains with no Jev log yet; `verify` says so instead of reading them.
244
+ (42161), Arc (5042) and Monad (143). Robinhood Chain (4663) and Ethereum (1) are known chains with no Jev log; `verify`
245
+ says so instead of reading them.
246
+
247
+ ### Where Jev's records live
248
+
249
+ | Chain | JevDecisionLog | JevAnswerLog |
250
+ |---|---|---|
251
+ | Taifoon mainnet (3692781), the record home | `0xaEbF9084Bb32eB2E0F58537526A8877dd1436b23` | `0xC08dfc61d702AEA45d1EF2515d8BDA36d548C75c` (JevAnswerLogV2) |
252
+ | Base (8453) | [`0x209490d6A0FFC5368A42b0c2208BDCda853f6a92`](https://basescan.org/address/0x209490d6A0FFC5368A42b0c2208BDCda853f6a92) | [`0xC08dfc61d702AEA45d1EF2515d8BDA36d548C75c`](https://basescan.org/address/0xC08dfc61d702AEA45d1EF2515d8BDA36d548C75c) (JevAnswerLogV2); earlier records in [`0x8e9B9cE86a2d55c10318607b0c815B7B8C66254d`](https://basescan.org/address/0x8e9B9cE86a2d55c10318607b0c815B7B8C66254d) |
253
+ | Taifoon devnet (36927) | `0x1D622511862DD7DEffB8fEeBc225E0FAdA1e9A05` | `0xD8c1d8188Fc8EE388792f6EB3B3dbd4f341Ba8e3` |
254
+
255
+ The two Taifoon mainnet logs were deployed on 2026-11-08 at their CREATE2 addresses (JevAnswerLogV2 has the same
256
+ address on Base). Taifoon mainnet has no public explorer yet; read them from its public RPC:
257
+
258
+ ```sh
259
+ curl -s https://mainnet.taifoon.dev -H 'content-type: application/json' \
260
+ -d '{"jsonrpc":"2.0","id":1,"method":"eth_getCode","params":["0xaEbF9084Bb32eB2E0F58537526A8877dd1436b23","latest"]}'
261
+ ```
262
+
263
+ `record()` builds the calls for both logs on Taifoon mainnet by default (next to the devnet), and `verify --chain taifoon`
264
+ reads them. Taifoon mainnet has no GradeStampRegistry yet, so the stamp is listed under `missing` there, said rather
265
+ than faked. On Base, answers are written to JevAnswerLogV2 and `verify` reads it first, then the earlier JevAnswerLog,
266
+ so a record from before V2 still verifies. Base keeps its published JevDecisionLog. Every address above is in
267
+ `src/addresses.ts` and `src/attest.ts`.
187
268
 
188
269
  | Flag | What it does |
189
270
  |---|---|
@@ -213,6 +294,12 @@ it ships (`src/rpc-fallback.ts`, generated from the RPC registry).
213
294
  In code: `verify(digest, { network: 'arbitrum' })` (a name or a chain id), `{ rpc: 'https://…' }`, and `JevChainError`
214
295
  (`code`: `rpc_unavailable`, `not_deployed`, `reverted`, `bad_input`) from the lower-level calls.
215
296
 
297
+ ## Links
298
+
299
+ - npm: [@taifoon/jev](https://www.npmjs.com/package/@taifoon/jev) · pricing: [@taifoon/jev-wilson](https://www.npmjs.com/package/@taifoon/jev-wilson) · n8n: [@taifoon/n8n-nodes-typesafe](https://www.npmjs.com/package/@taifoon/n8n-nodes-typesafe)
300
+ - The coordination layer: [coord.taifoon.dev/v1](https://coord.taifoon.dev/v1) ([OpenAPI](https://coord.taifoon.dev/v1/openapi.json)) · [taifoon.io](https://www.taifoon.io)
301
+ - Changes: [CHANGELOG.md](CHANGELOG.md) · issues: [github.com/taifoon-io/jev/issues](https://github.com/taifoon-io/jev/issues) · contact: info@taifoon.io
302
+
216
303
  ## Licence
217
304
 
218
305
  Independent project. Jev and TypeSafe are products of TypeSafe AI, Inc., which does not endorse this package.
@@ -60,7 +60,8 @@ export declare const REGISTRY_ADDRESSES: {
60
60
  readonly block: 48395723;
61
61
  };
62
62
  };
63
- /** The Jev logs on every chain the registry deploys them to (JevAnswerLog + JevDecisionLog, from their deploy block). */
63
+ /** The Jev logs on every chain the registry deploys them to (from their deploy block): the answer log records go to (JevAnswerLogV2
64
+ * where it is live, else JevAnswerLog), the decision log, and the earlier answer logs still read (Base: the first JevAnswerLog). */
64
65
  export declare const JEV_LOGS: Readonly<Record<number, {
65
66
  answerLog: {
66
67
  address: string;
@@ -70,4 +71,8 @@ export declare const JEV_LOGS: Readonly<Record<number, {
70
71
  address: string;
71
72
  fromBlock: number;
72
73
  };
74
+ earlierAnswerLogs?: readonly {
75
+ address: string;
76
+ fromBlock: number;
77
+ }[];
73
78
  }>>;
package/dist/addresses.js CHANGED
@@ -22,11 +22,13 @@ export const REGISTRY_ADDRESSES = {
22
22
  /** standards-erc8183 · BitAgent ERC-8183 escrow (live) */
23
23
  bitagentErc8183: { chainId: 8453, address: '0x5009ABB3A309115a4a682C66BAf3BC9E0329BaB7', block: 48395723 },
24
24
  };
25
- /** The Jev logs on every chain the registry deploys them to (JevAnswerLog + JevDecisionLog, from their deploy block). */
25
+ /** The Jev logs on every chain the registry deploys them to (from their deploy block): the answer log records go to (JevAnswerLogV2
26
+ * where it is live, else JevAnswerLog), the decision log, and the earlier answer logs still read (Base: the first JevAnswerLog). */
26
27
  export const JEV_LOGS = {
27
28
  143: { answerLog: { address: '0x29fEEc116e0CAbbaf38e9EBF2BBf65d82D672104', fromBlock: 110216088 }, decisionLog: { address: '0x1aC6266b9926cB3E206D5dc0c9d1B7c873aa150a', fromBlock: 110216093 } },
28
29
  5042: { answerLog: { address: '0x45214361A8cADb8aEe6302cd5653E119E5a89839', fromBlock: 24071071 }, decisionLog: { address: '0x863db7dAc69CC614a98822082cbe884F1f3b55cA', fromBlock: 24071074 } },
29
- 8453: { answerLog: { address: '0x8e9B9cE86a2d55c10318607b0c815B7B8C66254d', fromBlock: 51863008 }, decisionLog: { address: '0x209490d6A0FFC5368A42b0c2208BDCda853f6a92', fromBlock: 51856200 } },
30
+ 8453: { answerLog: { address: '0xC08dfc61d702AEA45d1EF2515d8BDA36d548C75c', fromBlock: 52639649 }, decisionLog: { address: '0x209490d6A0FFC5368A42b0c2208BDCda853f6a92', fromBlock: 51856200 }, earlierAnswerLogs: [{ address: '0x8e9B9cE86a2d55c10318607b0c815B7B8C66254d', fromBlock: 51863008 }] },
30
31
  36927: { answerLog: { address: '0xD8c1d8188Fc8EE388792f6EB3B3dbd4f341Ba8e3', fromBlock: 1106754 }, decisionLog: { address: '0x1D622511862DD7DEffB8fEeBc225E0FAdA1e9A05', fromBlock: 981099 } },
31
32
  42161: { answerLog: { address: '0x29fEEc116e0CAbbaf38e9EBF2BBf65d82D672104', fromBlock: 511340178 }, decisionLog: { address: '0x1aC6266b9926cB3E206D5dc0c9d1B7c873aa150a', fromBlock: 511340188 } },
33
+ 3692781: { answerLog: { address: '0xC08dfc61d702AEA45d1EF2515d8BDA36d548C75c', fromBlock: 2598386 }, decisionLog: { address: '0xaEbF9084Bb32eB2E0F58537526A8877dd1436b23', fromBlock: 2598383 } },
32
34
  };
package/dist/attest.js CHANGED
@@ -22,10 +22,10 @@
22
22
  export const ATTEST_TARGETS = {
23
23
  143: { name: 'Monad', decisionLog: '0x1aC6266b9926cB3E206D5dc0c9d1B7c873aa150a', answerLog: '0x29fEEc116e0CAbbaf38e9EBF2BBf65d82D672104', stampRegistry: null },
24
24
  5042: { name: 'Arc', decisionLog: '0x863db7dAc69CC614a98822082cbe884F1f3b55cA', answerLog: '0x45214361A8cADb8aEe6302cd5653E119E5a89839', stampRegistry: null },
25
- 8453: { name: 'Base', decisionLog: '0x209490d6A0FFC5368A42b0c2208BDCda853f6a92', answerLog: '0x8e9B9cE86a2d55c10318607b0c815B7B8C66254d', stampRegistry: null },
25
+ 8453: { name: 'Base', decisionLog: '0x209490d6A0FFC5368A42b0c2208BDCda853f6a92', answerLog: '0xC08dfc61d702AEA45d1EF2515d8BDA36d548C75c', stampRegistry: null },
26
26
  36927: { name: 'Taifoon devnet', decisionLog: '0x1D622511862DD7DEffB8fEeBc225E0FAdA1e9A05', answerLog: '0xD8c1d8188Fc8EE388792f6EB3B3dbd4f341Ba8e3', stampRegistry: '0xff5B4852AC7066D04FEd8de97bdA52401d03ad6E' },
27
27
  42161: { name: 'Arbitrum One', decisionLog: '0x1aC6266b9926cB3E206D5dc0c9d1B7c873aa150a', answerLog: '0x29fEEc116e0CAbbaf38e9EBF2BBf65d82D672104', stampRegistry: null },
28
- 3692781: { name: 'Taifoon mainnet', decisionLog: null, answerLog: null, stampRegistry: null },
28
+ 3692781: { name: 'Taifoon mainnet', decisionLog: '0xaEbF9084Bb32eB2E0F58537526A8877dd1436b23', answerLog: '0xC08dfc61d702AEA45d1EF2515d8BDA36d548C75c', stampRegistry: null },
29
29
  };
30
30
  // ── END GENERATED ──
31
31
  /** The default networks per profile. Taifoon: its devnet and its mainnet (GRID). Moonbeam: Base only. */
@@ -198,7 +198,9 @@ export function attest(input, opts = {}) {
198
198
  const calls = [];
199
199
  const missing = [];
200
200
  for (const chainId of networks) {
201
- const t = { name: `chain ${chainId}`, decisionLog: null, answerLog: null, stampRegistry: null, ...ATTEST_TARGETS[chainId], ...opts.targets?.[chainId] };
201
+ // the defaults as their own value: written inline before a spread of a fully typed target, tsc refuses them (TS2783)
202
+ const unknownChain = { name: `chain ${chainId}`, decisionLog: null, answerLog: null, stampRegistry: null };
203
+ const t = { ...unknownChain, ...ATTEST_TARGETS[chainId], ...opts.targets?.[chainId] };
202
204
  const gap = (contract) => missing.push({ chainId, network: t.name, contract, why: `no ${contract} on ${t.name} (${chainId}) in the address registry` });
203
205
  // the answers first, then the decision that cites them (the order the layer's recorder sends)
204
206
  if (input.answers) {
package/dist/chains.d.ts CHANGED
@@ -13,6 +13,10 @@ export declare const jevLogsOn: (chainId: number) => {
13
13
  address: string;
14
14
  fromBlock: number;
15
15
  };
16
+ earlierAnswerLogs?: readonly {
17
+ address: string;
18
+ fromBlock: number;
19
+ }[];
16
20
  } | null;
17
21
  /** Every chain that carries Jev logs, the devnet first, then Base, then the rest by id. */
18
22
  export declare const JEV_CHAINS: readonly number[];
package/dist/verify.js CHANGED
@@ -15,6 +15,33 @@ const hexN = (n) => '0x' + n.toString(16);
15
15
  async function logs(rpc, f, address, topics, fromBlock, toBlock, chainId) {
16
16
  return (await rpcCall(rpc, f, 'eth_getLogs', [{ address, topics, fromBlock: hexN(fromBlock), toBlock: toBlock == null ? 'latest' : hexN(toBlock) }], { chainId })) ?? [];
17
17
  }
18
+ /**
19
+ * _LOGS_AT_TRUSTED_v1_: the rows of the one block JevAnswerLog.recordedAt named. That block holds a trusted record, so an
20
+ * empty answer is the endpoint's (one that serves only recent logs answers [] for an older block instead of an error:
21
+ * base.rpc.blxrbdn.com, first in Base's rotation, did for a 1.35M-block-old record on 2026-10-29) and the next endpoint
22
+ * is asked. [] only when every endpoint that answered said so; no answer at all is rpc_unavailable, as before.
23
+ */
24
+ async function logsAtRecorded(rpc, f, address, topics, block, chainId) {
25
+ const urls = typeof rpc === 'string' ? [rpc] : rpc;
26
+ let answered = false;
27
+ let last = null;
28
+ for (const u of urls) {
29
+ try {
30
+ const l = await logs(u, f, address, topics, block, block, chainId);
31
+ answered = true;
32
+ if (l.length)
33
+ return l;
34
+ }
35
+ catch (e) {
36
+ if (e instanceof JevChainError && e.code !== 'rpc_unavailable')
37
+ throw e;
38
+ last = e;
39
+ }
40
+ }
41
+ if (!answered && last)
42
+ throw last;
43
+ return [];
44
+ }
18
45
  const SEL_RECORDED_AT = keccakHex('recordedAt(bytes32)').slice(0, 10);
19
46
  /** JevAnswerLog.recordedAt(digest): the block of the first trusted record, 0 when none. */
20
47
  async function recordedAt(rpc, f, address, digest, chainId) {
@@ -125,9 +152,14 @@ export async function verify(x, opts = {}) {
125
152
  const given = typeof opts.rpc === 'string' ? opts.rpc.split(',').map((u) => u.trim()).filter(Boolean) : opts.rpc;
126
153
  const rpc = given?.length ? given : await rpcRotation(chainId, f);
127
154
  if (chainId !== DEVNET) {
128
- const at = await recordedAt(rpc, f, dev.answerLog.address, answersDigest, chainId);
129
- if (at > 0)
130
- onChain.answers = (await logs(rpc, f, dev.answerLog.address, [TOPIC_ANSWERED, null, null, answersDigest], at, at, chainId)).map(answerEvent);
155
+ // _JEV_SEATS_v1_: the answer log records go to (JevAnswerLogV2 where live), then the earlier ones still read (Base: the first log)
156
+ for (const log of [dev.answerLog, ...(dev.earlierAnswerLogs ?? [])]) {
157
+ const at = await recordedAt(rpc, f, log.address, answersDigest, chainId);
158
+ if (at > 0) {
159
+ onChain.answers = (await logsAtRecorded(rpc, f, log.address, [TOPIC_ANSWERED, null, null, answersDigest], at, chainId)).map(answerEvent);
160
+ break;
161
+ }
162
+ }
131
163
  }
132
164
  else
133
165
  onChain.answers = (await logs(rpc, f, dev.answerLog.address, [TOPIC_ANSWERED, null, null, answersDigest], dev.answerLog.fromBlock, undefined, chainId)).map(answerEvent);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@taifoon/jev",
3
- "version": "0.3.0",
4
- "description": "Grade AI agent jobs with TypeSafe's Jev and record the verdict on chain: code checks the facts, Jev answers a published rubric, and you get a receipt, the on-chain record and the evaluator call for ERC-8183 and Virtuals ACP. No runtime dependencies, no key inside.",
3
+ "version": "0.4.0",
4
+ "description": "Grade AI agent jobs with TypeSafe's Jev and record the verdict on chain: code checks the facts, Jev answers a published rubric, and you get a receipt, the on-chain record and the evaluator call for ERC-8183 and Virtuals ACP. No third-party runtime dependencies, no key inside.",
5
5
  "keywords": [
6
6
  "jev",
7
7
  "typesafe",
@@ -18,7 +18,16 @@
18
18
  "ai-agents",
19
19
  "on-chain",
20
20
  "attestation",
21
- "base"
21
+ "base",
22
+ "taifoon",
23
+ "agent-evaluation",
24
+ "ai-evaluation",
25
+ "llm-evaluation",
26
+ "verifiable-ai",
27
+ "receipt",
28
+ "x402",
29
+ "arbitrum",
30
+ "monad"
22
31
  ],
23
32
  "license": "MIT",
24
33
  "author": {
@@ -51,6 +60,7 @@
51
60
  "examples",
52
61
  "README.md",
53
62
  "LICENSE",
63
+ "CHANGELOG.md",
54
64
  "!docs/demo.gif"
55
65
  ],
56
66
  "sideEffects": false,