@taifoon/jev 0.2.11 → 0.2.13
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 +9 -3
- package/bin/jev.mjs +19 -6
- 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/dist/wilson.d.ts +2 -49
- package/dist/wilson.js +3 -66
- package/docs/API.md +4 -2
- package/package.json +4 -2
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
|
|
|
@@ -170,4 +176,4 @@ npx @taifoon/jev workflows export ./jev-workflows
|
|
|
170
176
|
|
|
171
177
|
Independent project. Jev and TypeSafe are products of TypeSafe AI, Inc., which does not endorse this package.
|
|
172
178
|
|
|
173
|
-
Built by Taifoon. MIT. The package
|
|
179
|
+
Built by Taifoon. MIT. The package's only runtime dependency is Taifoon's own @taifoon/jev-wilson, the pricing; nothing third-party. It contains no key. It calls Jev only with your own TypeSafe key.
|
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,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/dist/wilson.d.ts
CHANGED
|
@@ -1,49 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
/** The layer's pool rule: no pool covers above this premium ratio. */
|
|
4
|
-
export declare const MAX_PREMIUM_RATIO = 0.3;
|
|
5
|
-
/** The premium ratio is rounded to millionths before it multiplies the price. */
|
|
6
|
-
export declare const RATIO_SCALE = 1000000;
|
|
7
|
-
/** The Wilson score interval for `successes` of `n`. [0, 1] when n = 0. Same operations, same order as the layer. */
|
|
8
|
-
export declare function wilson(successes: number, n: number, z?: number): [number, number];
|
|
9
|
-
/** u_F: the upper Wilson bound on the failure rate, n - k failed of n. This is the layer's π ratio. */
|
|
10
|
-
export declare function wilsonUpperFailure(k: number, n: number, z?: number): number;
|
|
11
|
-
/** floor(price × round(ratio × 1e6) / 1e6), exactly as the layer's pool-quote computes it. */
|
|
12
|
-
export declare function premiumAmount(price: bigint, ratio: number): bigint;
|
|
13
|
-
export type SellerRecord = {
|
|
14
|
-
k: number;
|
|
15
|
-
n: number;
|
|
16
|
-
};
|
|
17
|
-
export type PremiumOpts = {
|
|
18
|
-
/** floor on the ratio; default 0 (the layer has none) */
|
|
19
|
-
min?: number;
|
|
20
|
-
/** cover only when ratio <= max; default 0.30 (the layer's pool rule) */
|
|
21
|
-
max?: number;
|
|
22
|
-
/** price in the token's smallest unit; when given, `amount` is computed */
|
|
23
|
-
price?: bigint | number | string;
|
|
24
|
-
/** z for the record form; default Z */
|
|
25
|
-
z?: number;
|
|
26
|
-
};
|
|
27
|
-
export type Premium = {
|
|
28
|
-
insurable: false;
|
|
29
|
-
reason: 'UNKNOWN';
|
|
30
|
-
ratio: null;
|
|
31
|
-
amount: null;
|
|
32
|
-
covered: false;
|
|
33
|
-
basis: 'record';
|
|
34
|
-
} | {
|
|
35
|
-
insurable: true;
|
|
36
|
-
ratio: number;
|
|
37
|
-
amount: bigint | null;
|
|
38
|
-
covered: boolean;
|
|
39
|
-
basis: 'record' | 'p_L';
|
|
40
|
-
};
|
|
41
|
-
/**
|
|
42
|
-
* The premium for the next job.
|
|
43
|
-
*
|
|
44
|
-
* premium({ k, n }, opts) prices from the record: ratio = u_F, bit-for-bit with the layer.
|
|
45
|
-
* premium(pL, opts) prices from a lower bound you already hold: ratio = 1 - p_L. Equal in real
|
|
46
|
-
* arithmetic, but 1 - p_L can differ from u_F in the last bits of a double.
|
|
47
|
-
* Use the record form when you need the layer's number to the last digit.
|
|
48
|
-
*/
|
|
49
|
-
export declare function premium(input: number | SellerRecord, opts?: PremiumOpts): Premium;
|
|
1
|
+
export { Z, MAX_PREMIUM_RATIO, RATIO_SCALE, wilson, wilsonUpperFailure, premiumAmount, premium } from '@taifoon/jev-wilson';
|
|
2
|
+
export type { SellerRecord, PremiumOpts, Premium } from '@taifoon/jev-wilson';
|
package/dist/wilson.js
CHANGED
|
@@ -1,66 +1,3 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
export const Z = 1.959963984540054;
|
|
5
|
-
/** The layer's pool rule: no pool covers above this premium ratio. */
|
|
6
|
-
export const MAX_PREMIUM_RATIO = 0.3;
|
|
7
|
-
/** The premium ratio is rounded to millionths before it multiplies the price. */
|
|
8
|
-
export const RATIO_SCALE = 1_000_000;
|
|
9
|
-
function checkRecord(k, n) {
|
|
10
|
-
if (!Number.isInteger(k) || !Number.isInteger(n) || k < 0 || n < 0 || k > n) {
|
|
11
|
-
throw new RangeError(`need integers 0 <= k <= n, got k=${k}, n=${n}`);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
/** The Wilson score interval for `successes` of `n`. [0, 1] when n = 0. Same operations, same order as the layer. */
|
|
15
|
-
export function wilson(successes, n, z = Z) {
|
|
16
|
-
if (n <= 0)
|
|
17
|
-
return [0, 1];
|
|
18
|
-
const p = successes / n, z2 = z * z;
|
|
19
|
-
const d = 1 + z2 / n;
|
|
20
|
-
const c = p + z2 / (2 * n);
|
|
21
|
-
const s = z * Math.sqrt((p * (1 - p) + z2 / (4 * n)) / n);
|
|
22
|
-
return [Math.max(0, (c - s) / d), Math.min(1, (c + s) / d)];
|
|
23
|
-
}
|
|
24
|
-
/** u_F: the upper Wilson bound on the failure rate, n - k failed of n. This is the layer's π ratio. */
|
|
25
|
-
export function wilsonUpperFailure(k, n, z = Z) {
|
|
26
|
-
checkRecord(k, n);
|
|
27
|
-
return wilson(n - k, n, z)[1];
|
|
28
|
-
}
|
|
29
|
-
/** JavaScript Math.round, kept explicit so the Python twin can match it. */
|
|
30
|
-
const roundHalfUp = (x) => Math.round(x);
|
|
31
|
-
/** floor(price × round(ratio × 1e6) / 1e6), exactly as the layer's pool-quote computes it. */
|
|
32
|
-
export function premiumAmount(price, ratio) {
|
|
33
|
-
if (price <= 0n)
|
|
34
|
-
throw new RangeError('price must be a positive integer in the token’s smallest unit');
|
|
35
|
-
return (price * BigInt(roundHalfUp(ratio * RATIO_SCALE))) / BigInt(RATIO_SCALE);
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* The premium for the next job.
|
|
39
|
-
*
|
|
40
|
-
* premium({ k, n }, opts) prices from the record: ratio = u_F, bit-for-bit with the layer.
|
|
41
|
-
* premium(pL, opts) prices from a lower bound you already hold: ratio = 1 - p_L. Equal in real
|
|
42
|
-
* arithmetic, but 1 - p_L can differ from u_F in the last bits of a double.
|
|
43
|
-
* Use the record form when you need the layer's number to the last digit.
|
|
44
|
-
*/
|
|
45
|
-
export function premium(input, opts = {}) {
|
|
46
|
-
const min = opts.min ?? 0;
|
|
47
|
-
const max = opts.max ?? MAX_PREMIUM_RATIO;
|
|
48
|
-
let raw;
|
|
49
|
-
let basis;
|
|
50
|
-
if (typeof input === 'number') {
|
|
51
|
-
if (!(input >= 0 && input <= 1))
|
|
52
|
-
throw new RangeError(`p_L must be in [0, 1], got ${input}`);
|
|
53
|
-
raw = 1 - input;
|
|
54
|
-
basis = 'p_L';
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
checkRecord(input.k, input.n);
|
|
58
|
-
if (input.k <= 0)
|
|
59
|
-
return { insurable: false, reason: 'UNKNOWN', ratio: null, amount: null, covered: false, basis: 'record' };
|
|
60
|
-
raw = wilsonUpperFailure(input.k, input.n, opts.z ?? Z);
|
|
61
|
-
basis = 'record';
|
|
62
|
-
}
|
|
63
|
-
const ratio = raw < min ? min : raw;
|
|
64
|
-
const amount = opts.price === undefined ? null : premiumAmount(BigInt(opts.price), ratio);
|
|
65
|
-
return { insurable: true, ratio, amount, covered: ratio <= max, basis };
|
|
66
|
-
}
|
|
1
|
+
// The pricing half is @taifoon/jev-wilson, the layer's own package, and this module is its re-export: one
|
|
2
|
+
// implementation, the same numbers as the layer's /v1/pools/quote. Installing @taifoon/jev installs it.
|
|
3
|
+
export { Z, MAX_PREMIUM_RATIO, RATIO_SCALE, wilson, wilsonUpperFailure, premiumAmount, premium } from '@taifoon/jev-wilson';
|
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.13",
|
|
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",
|
|
@@ -69,8 +69,10 @@
|
|
|
69
69
|
"publishConfig": {
|
|
70
70
|
"access": "public"
|
|
71
71
|
},
|
|
72
|
+
"dependencies": {
|
|
73
|
+
"@taifoon/jev-wilson": "^0.2.0"
|
|
74
|
+
},
|
|
72
75
|
"devDependencies": {
|
|
73
|
-
"@taifoon/jev-wilson": "^0.1.0",
|
|
74
76
|
"@types/node": "^25.9.2",
|
|
75
77
|
"typescript": "5.9.2",
|
|
76
78
|
"viem": "2.52.2",
|