@taifoon/jev 0.2.10 → 0.2.12
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/README.md +8 -2
- package/bin/jev.mjs +19 -6
- package/dist/addresses.d.ts +62 -0
- package/dist/addresses.js +24 -0
- package/dist/contracts.d.ts +1 -1
- package/dist/contracts.js +12 -10
- package/dist/decision.d.ts +35 -0
- package/dist/decision.js +40 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/docs/API.md +4 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -137,7 +137,7 @@ code checks reject is still graded). Exit codes: `0` done, `1` a step failed, `2
|
|
|
137
137
|
- **What the result is.** *Graded against a published rubric by a pinned decision model, with an appeal.* It is not
|
|
138
138
|
"independently verified". An on-chain record is an attestation by whoever sent it, not a proof the answer is right.
|
|
139
139
|
|
|
140
|
-
## The
|
|
140
|
+
## The six calls
|
|
141
141
|
|
|
142
142
|
| Call | What it does |
|
|
143
143
|
|---|---|
|
|
@@ -148,13 +148,18 @@ code checks reject is still graded). Exit codes: `0` done, `1` a step failed, `2
|
|
|
148
148
|
| `evaluatorCall(protocol, jobId, verdict, digest)` | Returns the one unsigned call that ends the job; `null` for needs_review |
|
|
149
149
|
| `verify(receiptOrDigest)` | Recomputes every digest and finds the records on chain |
|
|
150
150
|
|
|
151
|
+
`verifyDecision(record, { answers? })` does the same for a decision as the coordination layer serves it at
|
|
152
|
+
`/v1/judge/decisions/{id}`: the input fingerprint, the decision digest, the answers digest and the verdict RUBRIC_v1
|
|
153
|
+
composes, all offline. `npx @taifoon/jev verify decision-1790522234088-10691d5017 --network base` runs it on the
|
|
154
|
+
block-proof grade and then finds both entries on Base.
|
|
155
|
+
|
|
151
156
|
Evaluator seats: `assurance-hook`, `judge-adapter`, `virtuals-erc8183`, `virtuals-memo-acp` and `bitagent-erc8183`.
|
|
152
157
|
Each is tested against calldata a mined transaction carried.
|
|
153
158
|
|
|
154
159
|
## Documentation
|
|
155
160
|
|
|
156
161
|
- [How grading works: Jev, the pipeline, THRESHOLDS_v1, `npx @taifoon/jev run`](docs/PIPELINE.md)
|
|
157
|
-
- [API: the
|
|
162
|
+
- [API: the six calls, where Jev is reached, your own rubric](docs/API.md)
|
|
158
163
|
- [Evaluator seats, with the transactions they are tested against](docs/EVALUATORS.md)
|
|
159
164
|
- [On chain: contract addresses on Base and devnet, and one real grade end to end](docs/ON_CHAIN.md)
|
|
160
165
|
- [The records: receipt, decision.v2, jev.answer.v1](docs/RECORDS.md)
|
|
@@ -163,6 +168,7 @@ Each is tested against calldata a mined transaction carried.
|
|
|
163
168
|
|
|
164
169
|
```
|
|
165
170
|
npx @taifoon/jev verify <digest> --network base
|
|
171
|
+
npx @taifoon/jev verify <decision id> --network base # recompute the served record offline, then find it on chain
|
|
166
172
|
npx @taifoon/jev workflows export ./jev-workflows
|
|
167
173
|
```
|
|
168
174
|
|
package/bin/jev.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// jev workflows list [--version X] the bundled n8n workflows (and which nodes and credentials each needs)
|
|
4
4
|
// jev workflows export <dir> [--version X] write them, their schemas, manifest and README into <dir>
|
|
5
5
|
// jev run [--job 8453:81100] [--evidence pack.json] [--network …] [--yes] [--json] one job through the pipeline, step by step
|
|
6
|
-
// jev verify <answers-digest> [--network devnet|base|any] find the digest's JevAnswered / Decided rows (default any: devnet, then Base 8453)
|
|
6
|
+
// jev verify <answers-digest | decision id> [--network devnet|base|any] (a decision id: recompute its record offline, then) find the digest's JevAnswered / Decided rows (default any: devnet, then Base 8453)
|
|
7
7
|
import { cpSync, existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
8
8
|
import { dirname, join, resolve } from 'node:path';
|
|
9
9
|
import { fileURLToPath } from 'node:url';
|
|
@@ -34,12 +34,25 @@ if (cmd === 'workflows') {
|
|
|
34
34
|
} else if (cmd === 'run') {
|
|
35
35
|
await import('./run.mjs').then((m) => m.run(process.argv.slice(3)));
|
|
36
36
|
} else if (cmd === 'verify' && sub) {
|
|
37
|
-
const { verify } = await import('../dist/index.js');
|
|
37
|
+
const { verify, verifyDecision, LAYER } = await import('../dist/index.js');
|
|
38
38
|
if (!['devnet', 'base', 'any'].includes(network)) die('--network is devnet, base or any');
|
|
39
|
-
let
|
|
39
|
+
let digest = sub; let offline = null;
|
|
40
|
+
// a decision id (or its /v1/judge/decisions URL): read the served record and its answers, recompute both here, then
|
|
41
|
+
// find the answers digest on chain like any other
|
|
42
|
+
const m = /(decision-\d+-[0-9a-f]{10})/.exec(sub);
|
|
43
|
+
if (m) {
|
|
44
|
+
const get = async (p) => { const r = await fetch(`${LAYER}${p}`, { signal: AbortSignal.timeout(30_000) }); if (!r.ok) die(`${LAYER}${p} answered ${r.status}`); return r.json(); };
|
|
45
|
+
const rec = await get(`/v1/judge/decisions/${m[1]}`);
|
|
46
|
+
const ad = rec?.decision?.answers_digest;
|
|
47
|
+
const ans = ad ? await get(`/v1/judge/answers/${ad}`) : null;
|
|
48
|
+
offline = verifyDecision(rec, ans?.record ? { answers: ans.record } : {});
|
|
49
|
+
if (!ad) { console.log(JSON.stringify({ offline }, null, 1)); process.exit(offline.ok ? 0 : 2); }
|
|
50
|
+
digest = ad;
|
|
51
|
+
}
|
|
52
|
+
let v = await verify(digest, { network: network === 'base' ? 'base' : 'devnet' });
|
|
40
53
|
if (network === 'any' && !v.checks.answersOnChain) { const b = await verify(sub, { network: 'base' }); if (b.checks.answersOnChain || !v.ok) v = b; }
|
|
41
|
-
console.log(JSON.stringify(v, null, 1));
|
|
42
|
-
process.exit(v.ok ? 0 : 2);
|
|
54
|
+
console.log(JSON.stringify(offline ? { offline, ...v, ok: v.ok && offline.ok } : v, null, 1));
|
|
55
|
+
process.exit(v.ok && (!offline || offline.ok) ? 0 : 2);
|
|
43
56
|
} else {
|
|
44
|
-
console.log(`jev ${PKG.version}\n jev workflows list [--version X]\n jev workflows export <dir> [--version X]\n jev run [--job <chain>:<id>] [--evidence pack.json] [--network none|devnet|base|both] [--yes] [--json]\n jev verify <answers-digest> [--network devnet|base|any]`);
|
|
57
|
+
console.log(`jev ${PKG.version}\n jev workflows list [--version X]\n jev workflows export <dir> [--version X]\n jev run [--job <chain>:<id>] [--evidence pack.json] [--network none|devnet|base|both] [--yes] [--json]\n jev verify <answers-digest | decision id> [--network devnet|base|any]`);
|
|
45
58
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export declare const REGISTRY_ADDRESSES: {
|
|
2
|
+
/** jev · JevAnswerLog (devnet) (devnet-only) */
|
|
3
|
+
readonly devnetAnswerLog: {
|
|
4
|
+
readonly chainId: 36927;
|
|
5
|
+
readonly address: "0xD8c1d8188Fc8EE388792f6EB3B3dbd4f341Ba8e3";
|
|
6
|
+
readonly block: 1106754;
|
|
7
|
+
};
|
|
8
|
+
/** jev · JevDecisionLog (devnet) (devnet-only) */
|
|
9
|
+
readonly devnetDecisionLog: {
|
|
10
|
+
readonly chainId: 36927;
|
|
11
|
+
readonly address: "0x1D622511862DD7DEffB8fEeBc225E0FAdA1e9A05";
|
|
12
|
+
readonly block: 981099;
|
|
13
|
+
};
|
|
14
|
+
/** taifoon-assurance · JudgeAdapter (devnet) (devnet-only) */
|
|
15
|
+
readonly devnetJudgeAdapter: {
|
|
16
|
+
readonly chainId: 36927;
|
|
17
|
+
readonly address: "0xbd4c9e797a8DBbfdde9C77bc5A5b21ACf611Db8f";
|
|
18
|
+
readonly block: null;
|
|
19
|
+
};
|
|
20
|
+
/** taifoon-assurance · AssuranceHook (proxy, devnet) (devnet-only) */
|
|
21
|
+
readonly devnetAssuranceHook: {
|
|
22
|
+
readonly chainId: 36927;
|
|
23
|
+
readonly address: "0x7110C8951d17B69054395742e265bbd98259564A";
|
|
24
|
+
readonly block: 909786;
|
|
25
|
+
};
|
|
26
|
+
/** jev · JevAnswerLog (live) */
|
|
27
|
+
readonly baseAnswerLog: {
|
|
28
|
+
readonly chainId: 8453;
|
|
29
|
+
readonly address: "0x8e9B9cE86a2d55c10318607b0c815B7B8C66254d";
|
|
30
|
+
readonly block: 51863008;
|
|
31
|
+
};
|
|
32
|
+
/** jev · JevAnswerLog (first deploy) (superseded) */
|
|
33
|
+
readonly baseAnswerLogFirstDeploy: {
|
|
34
|
+
readonly chainId: 8453;
|
|
35
|
+
readonly address: "0x5bac70eb78224bbCBa83f5A36DF09D549d3f57fa";
|
|
36
|
+
readonly block: 51856203;
|
|
37
|
+
};
|
|
38
|
+
/** jev · JevDecisionLog (live) */
|
|
39
|
+
readonly baseDecisionLog: {
|
|
40
|
+
readonly chainId: 8453;
|
|
41
|
+
readonly address: "0x209490d6A0FFC5368A42b0c2208BDCda853f6a92";
|
|
42
|
+
readonly block: 51856200;
|
|
43
|
+
};
|
|
44
|
+
/** standards-erc8183 · Virtuals ACP v3 (ERC-8183) (live) */
|
|
45
|
+
readonly virtualsErc8183: {
|
|
46
|
+
readonly chainId: 8453;
|
|
47
|
+
readonly address: "0x238E541BfefD82238730D00a2208E5497F1832E0";
|
|
48
|
+
readonly block: 44427013;
|
|
49
|
+
};
|
|
50
|
+
/** standards-erc8183 · memo-ACP router (live) */
|
|
51
|
+
readonly virtualsMemoAcpRouter: {
|
|
52
|
+
readonly chainId: 8453;
|
|
53
|
+
readonly address: "0xa6C9BA866992cfD7fd6460ba912bfa405adA9df0";
|
|
54
|
+
readonly block: 36786061;
|
|
55
|
+
};
|
|
56
|
+
/** standards-erc8183 · BitAgent ERC-8183 escrow (live) */
|
|
57
|
+
readonly bitagentErc8183: {
|
|
58
|
+
readonly chainId: 8453;
|
|
59
|
+
readonly address: "0x5009ABB3A309115a4a682C66BAf3BC9E0329BaB7";
|
|
60
|
+
readonly block: 48395723;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// VENDORED from an address registry (addresses.json, public subset) by scripts/vendor-addresses.mjs - do not edit here.
|
|
2
|
+
// Only the contracts this SDK names; test/addresses-vendor.test.ts checks the copy against the registry.
|
|
3
|
+
export const REGISTRY_ADDRESSES = {
|
|
4
|
+
/** jev · JevAnswerLog (devnet) (devnet-only) */
|
|
5
|
+
devnetAnswerLog: { chainId: 36927, address: '0xD8c1d8188Fc8EE388792f6EB3B3dbd4f341Ba8e3', block: 1106754 },
|
|
6
|
+
/** jev · JevDecisionLog (devnet) (devnet-only) */
|
|
7
|
+
devnetDecisionLog: { chainId: 36927, address: '0x1D622511862DD7DEffB8fEeBc225E0FAdA1e9A05', block: 981099 },
|
|
8
|
+
/** taifoon-assurance · JudgeAdapter (devnet) (devnet-only) */
|
|
9
|
+
devnetJudgeAdapter: { chainId: 36927, address: '0xbd4c9e797a8DBbfdde9C77bc5A5b21ACf611Db8f', block: null },
|
|
10
|
+
/** taifoon-assurance · AssuranceHook (proxy, devnet) (devnet-only) */
|
|
11
|
+
devnetAssuranceHook: { chainId: 36927, address: '0x7110C8951d17B69054395742e265bbd98259564A', block: 909786 },
|
|
12
|
+
/** jev · JevAnswerLog (live) */
|
|
13
|
+
baseAnswerLog: { chainId: 8453, address: '0x8e9B9cE86a2d55c10318607b0c815B7B8C66254d', block: 51863008 },
|
|
14
|
+
/** jev · JevAnswerLog (first deploy) (superseded) */
|
|
15
|
+
baseAnswerLogFirstDeploy: { chainId: 8453, address: '0x5bac70eb78224bbCBa83f5A36DF09D549d3f57fa', block: 51856203 },
|
|
16
|
+
/** jev · JevDecisionLog (live) */
|
|
17
|
+
baseDecisionLog: { chainId: 8453, address: '0x209490d6A0FFC5368A42b0c2208BDCda853f6a92', block: 51856200 },
|
|
18
|
+
/** standards-erc8183 · Virtuals ACP v3 (ERC-8183) (live) */
|
|
19
|
+
virtualsErc8183: { chainId: 8453, address: '0x238E541BfefD82238730D00a2208E5497F1832E0', block: 44427013 },
|
|
20
|
+
/** standards-erc8183 · memo-ACP router (live) */
|
|
21
|
+
virtualsMemoAcpRouter: { chainId: 8453, address: '0xa6C9BA866992cfD7fd6460ba912bfa405adA9df0', block: 36786061 },
|
|
22
|
+
/** standards-erc8183 · BitAgent ERC-8183 escrow (live) */
|
|
23
|
+
bitagentErc8183: { chainId: 8453, address: '0x5009ABB3A309115a4a682C66BAf3BC9E0329BaB7', block: 48395723 },
|
|
24
|
+
};
|
package/dist/contracts.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export declare const CONTRACTS: Readonly<{
|
|
|
15
15
|
};
|
|
16
16
|
/** JudgeAdapter: posts a verdict (with the decision digest) to the devnet assurance hook */
|
|
17
17
|
readonly judgeAdapter: "0xbd4c9e797a8DBbfdde9C77bc5A5b21ACf611Db8f";
|
|
18
|
-
readonly assuranceHook:
|
|
18
|
+
readonly assuranceHook: string;
|
|
19
19
|
};
|
|
20
20
|
/** Base (8453): both logs are live. Only the KMS recorder 0xe9F0E71e7Fc66864126C0aE5588a7858b25dE51D is `trusted` on the answer log. */
|
|
21
21
|
readonly base: {
|
package/dist/contracts.js
CHANGED
|
@@ -1,31 +1,33 @@
|
|
|
1
1
|
// Where the records and the evaluator seats live. Every address here was read from the chain or from its deploy
|
|
2
|
-
// broadcast; nothing is deployed by this package.
|
|
2
|
+
// broadcast; nothing is deployed by this package. The contract addresses come from the address registry (src/addresses.ts,
|
|
3
|
+
// vendored by scripts/vendor-addresses.mjs); the recorder and the BitAgent evaluator are accounts, not registry contracts.
|
|
4
|
+
import { REGISTRY_ADDRESSES as R } from './addresses.js';
|
|
3
5
|
export const CONTRACTS = Object.freeze({
|
|
4
6
|
/** the public devnet the logs live on: chain 36927, free gas; anyone may send */
|
|
5
7
|
devnet: {
|
|
6
8
|
chainId: 36927,
|
|
7
9
|
rpc: 'https://rpc.taifoon.dev',
|
|
8
10
|
/** JevAnswerLog: append-only, no owner. Recorders fixed at deploy are `trusted`; any other sender is logged with trusted = false */
|
|
9
|
-
answerLog: { address:
|
|
11
|
+
answerLog: { address: R.devnetAnswerLog.address, fromBlock: R.devnetAnswerLog.block },
|
|
10
12
|
/** JevDecisionLog: append-only, no owner, no upgrade; the recorder is msg.sender */
|
|
11
|
-
decisionLog: { address:
|
|
13
|
+
decisionLog: { address: R.devnetDecisionLog.address, fromBlock: R.devnetDecisionLog.block },
|
|
12
14
|
/** JudgeAdapter: posts a verdict (with the decision digest) to the devnet assurance hook */
|
|
13
|
-
judgeAdapter:
|
|
14
|
-
assuranceHook:
|
|
15
|
+
judgeAdapter: R.devnetJudgeAdapter.address,
|
|
16
|
+
assuranceHook: R.devnetAssuranceHook.address.toLowerCase(),
|
|
15
17
|
},
|
|
16
18
|
/** Base (8453): both logs are live. Only the KMS recorder 0xe9F0E71e7Fc66864126C0aE5588a7858b25dE51D is `trusted` on the answer log. */
|
|
17
19
|
base: {
|
|
18
20
|
chainId: 8453,
|
|
19
21
|
rpc: 'https://mainnet.base.org',
|
|
20
22
|
/** JevAnswerLog, redeployed 2026-09-27 (tx 0xeb7d2dac…ae87); supersedes 0x5bac70eb78224bbCBa83f5A36DF09D549d3f57fa */
|
|
21
|
-
answerLog: { address:
|
|
23
|
+
answerLog: { address: R.baseAnswerLog.address, fromBlock: R.baseAnswerLog.block },
|
|
22
24
|
/** JevDecisionLog, deployed 2026-09-27 (tx 0x0e6300c0…4fc7); the recorder is msg.sender */
|
|
23
|
-
decisionLog: { address:
|
|
25
|
+
decisionLog: { address: R.baseDecisionLog.address, fromBlock: R.baseDecisionLog.block },
|
|
24
26
|
/** the one trusted recorder of the Base answer log */
|
|
25
27
|
answerRecorder: '0xe9F0E71e7Fc66864126C0aE5588a7858b25dE51D',
|
|
26
|
-
virtualsErc8183:
|
|
27
|
-
virtualsMemoAcpRouter:
|
|
28
|
-
bitagentErc8183:
|
|
28
|
+
virtualsErc8183: R.virtualsErc8183.address,
|
|
29
|
+
virtualsMemoAcpRouter: R.virtualsMemoAcpRouter.address,
|
|
30
|
+
bitagentErc8183: R.bitagentErc8183.address,
|
|
29
31
|
bitagentEvaluator: '0x4302e523D982f3b89Cfc43cE4530C012b495Ec11',
|
|
30
32
|
},
|
|
31
33
|
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type Hex } from './hash.js';
|
|
2
|
+
import { type AnswerRecord, type DecisionAnswer, type Subject } from './records.js';
|
|
3
|
+
import { type Composed, type Facts, type Rubric, type RubricInput } from './rubric.js';
|
|
4
|
+
/** The fields of a served decision.v2 record this check reads (the rest is carried as is). */
|
|
5
|
+
export type DecisionRecord = {
|
|
6
|
+
v?: string;
|
|
7
|
+
id?: string;
|
|
8
|
+
kind: string;
|
|
9
|
+
subject: Subject;
|
|
10
|
+
model: string | null;
|
|
11
|
+
answers: DecisionAnswer[];
|
|
12
|
+
input?: string | null;
|
|
13
|
+
input_digest: Hex;
|
|
14
|
+
digest: Hex;
|
|
15
|
+
subject_id?: Hex;
|
|
16
|
+
kind_id?: Hex;
|
|
17
|
+
confidence_bps?: number;
|
|
18
|
+
answers_digest?: Hex | null;
|
|
19
|
+
};
|
|
20
|
+
export type DecisionCheck = {
|
|
21
|
+
ok: boolean;
|
|
22
|
+
checks: Record<string, boolean>;
|
|
23
|
+
problems: string[];
|
|
24
|
+
composed: Composed | null;
|
|
25
|
+
digest: Hex;
|
|
26
|
+
};
|
|
27
|
+
export declare function verifyDecision(d: DecisionRecord | {
|
|
28
|
+
decision: DecisionRecord;
|
|
29
|
+
}, opts?: {
|
|
30
|
+
answers?: AnswerRecord | {
|
|
31
|
+
record: AnswerRecord;
|
|
32
|
+
};
|
|
33
|
+
facts?: Facts;
|
|
34
|
+
rubric?: Rubric | RubricInput;
|
|
35
|
+
}): DecisionCheck;
|
package/dist/decision.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// verifyDecision(): recompute a decision.v2 record exactly as the coordination layer serves it at
|
|
2
|
+
// /v1/judge/decisions/{id} (and its jev.answer.v1 record at /v1/judge/answers/{digest}), offline, with this package's
|
|
3
|
+
// own code: the input fingerprint (sha256 of the text Jev read), the subject and kind ids, the decision digest, the
|
|
4
|
+
// on-chain confidence, the answers digest and the verdict RUBRIC_v1 composes from the answers. `verify()` takes the
|
|
5
|
+
// receipt `grade()` returns; this takes the record a reader finds by its link.
|
|
6
|
+
import { sha256Hex } from './hash.js';
|
|
7
|
+
import { answersDigestOf, confidenceBpsOf, decisionDigest, kindIdOf, subjectIdOf } from './records.js';
|
|
8
|
+
import { defineRubric, RUBRIC_v1 } from './rubric.js';
|
|
9
|
+
const low = (h) => String(h ?? '').toLowerCase();
|
|
10
|
+
export function verifyDecision(d, opts = {}) {
|
|
11
|
+
const r = ('decision' in d ? d.decision : d);
|
|
12
|
+
const checks = {};
|
|
13
|
+
if (typeof r.input === 'string')
|
|
14
|
+
checks.inputDigest = low(sha256Hex(r.input)) === low(r.input_digest);
|
|
15
|
+
if (r.subject_id)
|
|
16
|
+
checks.subjectId = low(subjectIdOf(r.subject)) === low(r.subject_id);
|
|
17
|
+
if (r.kind_id)
|
|
18
|
+
checks.kindId = low(kindIdOf(r.kind)) === low(r.kind_id);
|
|
19
|
+
const digest = decisionDigest({ kind: r.kind, subject: r.subject, answers: r.answers, model: r.model, input_digest: r.input_digest });
|
|
20
|
+
checks.decisionDigest = low(digest) === low(r.digest);
|
|
21
|
+
if (typeof r.confidence_bps === 'number')
|
|
22
|
+
checks.confidenceBps = confidenceBpsOf(r.answers) === r.confidence_bps;
|
|
23
|
+
const ans = opts.answers ? ('record' in opts.answers ? opts.answers.record : opts.answers) : null;
|
|
24
|
+
if (ans) {
|
|
25
|
+
if (r.answers_digest)
|
|
26
|
+
checks.answersDigest = low(answersDigestOf(ans)) === low(r.answers_digest);
|
|
27
|
+
checks.answersBindInput = low(ans.input_digest) === low(r.input_digest);
|
|
28
|
+
checks.answersMatchDecision = r.answers.every((a) => { const b = ans.answers.find((x) => x.id === a.id); return !!b && b.value === a.value && JSON.stringify(b.distribution) === JSON.stringify(a.probabilities); });
|
|
29
|
+
}
|
|
30
|
+
let composed = null;
|
|
31
|
+
if (r.kind === 'grade') {
|
|
32
|
+
const rubric = opts.rubric ? defineRubric(opts.rubric) : RUBRIC_v1;
|
|
33
|
+
// A recorded decision exists only once the checks passed (a failed check rejects without asking Jev), so the
|
|
34
|
+
// default facts are "delivered, checks ok"; pass `facts` to compose under others (e.g. the job's price).
|
|
35
|
+
const facts = opts.facts ?? { delivered: true, checksOk: true, checks: {}, priceUsdc: null };
|
|
36
|
+
composed = rubric.compose(facts, Object.fromEntries(r.answers.map((a) => [a.id, { id: a.id, value: a.value, confidence: a.confidence, probabilities: a.probabilities }])));
|
|
37
|
+
}
|
|
38
|
+
const problems = Object.entries(checks).filter(([, v]) => !v).map(([k]) => `${k} does not recompute`);
|
|
39
|
+
return { ok: problems.length === 0, checks, problems, composed, digest };
|
|
40
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* record() the unsigned calls that put the receipt's digests on JevAnswerLog / JevDecisionLog
|
|
8
8
|
* evaluatorCall() the unsigned call that ends the job on the protocol whose evaluator seat you hold
|
|
9
9
|
* verify() recompute every digest and find the chain events that hold them
|
|
10
|
+
* verifyDecision() recompute a decision record as /v1/judge/decisions/{id} serves it, offline
|
|
10
11
|
* pipeline() one job through the coordination layer's steps: pick → evidence → facts → grade → record → evaluator → premium → verify
|
|
11
12
|
* RUBRIC_v1 the questions, thresholds and composition (pass your own rubric to grade)
|
|
12
13
|
* CONTRACTS where the logs and the evaluator seats are
|
|
@@ -19,6 +20,7 @@ export { prepareJob, runCheck } from './job.js';
|
|
|
19
20
|
export { record } from './record.js';
|
|
20
21
|
export { evaluatorCall } from './evaluator/index.js';
|
|
21
22
|
export { verify } from './verify.js';
|
|
23
|
+
export { verifyDecision } from './decision.js';
|
|
22
24
|
export { pipeline, STEPS, LAYER, factsFromEvidence, protocolFor, parseJob, localQuote } from './pipeline.js';
|
|
23
25
|
export { RUBRIC_v1 } from './rubric.js';
|
|
24
26
|
export { CONTRACTS } from './contracts.js';
|
|
@@ -27,6 +29,7 @@ export type { FactsInput, Check } from './facts.js';
|
|
|
27
29
|
export type { JobSpec, JobCheck, PreparedJob } from './job.js';
|
|
28
30
|
export type { Recorded, UnsignedCall, Network } from './record.js';
|
|
29
31
|
export type { Verification, AnswerEvent, DecisionEvent } from './verify.js';
|
|
32
|
+
export type { DecisionRecord, DecisionCheck } from './decision.js';
|
|
30
33
|
export type { Protocol, Adapter, AdapterOpts, EvaluatorCall } from './evaluator/index.js';
|
|
31
34
|
export type { JevError } from './ask.js';
|
|
32
35
|
export type { Rubric, RubricInput, Question, Answer, Facts, Verdict, Composed, Thresholds } from './rubric.js';
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* record() the unsigned calls that put the receipt's digests on JevAnswerLog / JevDecisionLog
|
|
8
8
|
* evaluatorCall() the unsigned call that ends the job on the protocol whose evaluator seat you hold
|
|
9
9
|
* verify() recompute every digest and find the chain events that hold them
|
|
10
|
+
* verifyDecision() recompute a decision record as /v1/judge/decisions/{id} serves it, offline
|
|
10
11
|
* pipeline() one job through the coordination layer's steps: pick → evidence → facts → grade → record → evaluator → premium → verify
|
|
11
12
|
* RUBRIC_v1 the questions, thresholds and composition (pass your own rubric to grade)
|
|
12
13
|
* CONTRACTS where the logs and the evaluator seats are
|
|
@@ -19,6 +20,7 @@ export { prepareJob, runCheck } from './job.js';
|
|
|
19
20
|
export { record } from './record.js';
|
|
20
21
|
export { evaluatorCall } from './evaluator/index.js';
|
|
21
22
|
export { verify } from './verify.js';
|
|
23
|
+
export { verifyDecision } from './decision.js';
|
|
22
24
|
export { pipeline, STEPS, LAYER, factsFromEvidence, protocolFor, parseJob, localQuote } from './pipeline.js';
|
|
23
25
|
export { RUBRIC_v1 } from './rubric.js';
|
|
24
26
|
export { CONTRACTS } from './contracts.js';
|
package/docs/API.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# API
|
|
2
2
|
|
|
3
|
-
## The
|
|
3
|
+
## The six calls
|
|
4
4
|
|
|
5
5
|
| Call | What it does | Network |
|
|
6
6
|
|---|---|---|
|
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
| `facts({ delivered, checks, priceUsdc? })` | Runs the checks your protocol supplies. A check is a value or an (async) function; one that throws counts as "could not be checked". | None |
|
|
9
9
|
| `record(receipt, { network, send? })` | Returns the unsigned `JevAnswerLog.record` and `JevDecisionLog.record` calls. `network` is a flag: `none`, `devnet` (default), `base` or `both`. Pass `send` (your wallet) to send every call that has an address. | None unless you pass `send` |
|
|
10
10
|
| `evaluatorCall(protocol, jobId, verdict, digest)` | Returns the one unsigned call that ends the job on that protocol. For `needs_review` it returns `null`. | None |
|
|
11
|
+
| `prepareJob({ id, task, criteria, delivered, source?, checks? })` | Turns a job as the buyer laid it out into the evidence Jev reads and the checks code runs. | None |
|
|
11
12
|
| `verify(receiptOrDigest)` | Recomputes every digest, re-composes the verdict and finds the `JevAnswered` and `Decided` events. | One devnet RPC read |
|
|
13
|
+
| `verifyDecision(record, { answers?, facts? })` | Recomputes a decision record as `/v1/judge/decisions/{id}` serves it: sha256 of the text Jev read, subject and kind ids, the decision digest, the on-chain confidence, the answers digest (with the `/v1/judge/answers/{digest}` record) and the verdict RUBRIC_v1 composes. | None |
|
|
12
14
|
|
|
13
|
-
It also exports `RUBRIC_v1` and `
|
|
15
|
+
It also exports `RUBRIC_v1`, `CONTRACTS`, `runCheck` and the `jev run` pipeline (`pipeline`, `STEPS`, `LAYER` and its helpers).
|
|
14
16
|
|
|
15
17
|
**Where Jev is reached.** Calls go to `https://api.typesafe.ai/v1/systemone` with your own TypeSafe key from
|
|
16
18
|
[console.typesafe.ai](https://console.typesafe.ai), and to no one else. The key is not stored and never appears in a receipt. If you already
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taifoon/jev",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.12",
|
|
4
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.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jev",
|