@taifoon/jev 0.2.2 → 0.2.4
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 +26 -0
- package/bin/jev.mjs +3 -1
- package/bin/run.mjs +23 -5
- package/dist/pipeline.js +9 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -76,6 +76,32 @@ seat. The decision is anchored on the Taifoon devnet in
|
|
|
76
76
|
`npx @taifoon/jev run --demo` replays it from [`examples/jobs/base-bitagent-7287.recorded.json`](examples/jobs/base-bitagent-7287.recorded.json)
|
|
77
77
|
with no key and no network, and arrives at the same decision digest.
|
|
78
78
|
|
|
79
|
+
## In n8n
|
|
80
|
+
|
|
81
|
+
The same grader runs in n8n through [`@taifoon/n8n-nodes-typesafe`](https://www.npmjs.com/package/@taifoon/n8n-nodes-typesafe),
|
|
82
|
+
with eleven ready-to-import judge workflows: grade an agent's delivery, grade a Base job from its record, fact-check a
|
|
83
|
+
chatbot answer, settle a refund dispute and more. See [its README](https://github.com/taifoon-io/n8n-nodes-typesafe#judge-workflows-ready-to-import).
|
|
84
|
+
|
|
85
|
+
## `jev run` flags
|
|
86
|
+
|
|
87
|
+
Safe by default: your own TypeSafe key only, nothing recorded, nothing signed or sent. A typo is refused, never guessed.
|
|
88
|
+
|
|
89
|
+
| Flag | Default | What it does |
|
|
90
|
+
|---|---|---|
|
|
91
|
+
| `--demo` | off | Replays the real Base job above, offline, no key |
|
|
92
|
+
| `--job <chain>:<id>` | first ready job in the layer's queue | Grade one live job, e.g. `8453:bitagent:8453:7287` |
|
|
93
|
+
| `--job-file job.json` | | Grade a job you describe (see `examples/jobs/`) |
|
|
94
|
+
| `--evidence pack.json` | | Grade your own evidence pack, no layer |
|
|
95
|
+
| `--answers answers.json` | | Answers you already have (e.g. from the n8n node); Jev is not asked |
|
|
96
|
+
| `--record none\|devnet\|base\|both` | `none` | Opt in to the unsigned calls that record the grade on chain |
|
|
97
|
+
| `--layer <https url>` / `--no-layer` | `https://coord.taifoon.dev` | Where jobs and quotes are read |
|
|
98
|
+
| `--protocol <name>` | from the job id | Evaluator seat: `virtuals-erc8183`, `virtuals-memo-acp`, `bitagent-erc8183`, `assurance-hook`, `judge-adapter` |
|
|
99
|
+
| `--price-usdc <n>` | the job's budget | Price for the premium quote |
|
|
100
|
+
| `--yes` / `--json` | ask per step / text | Run every step without asking / print the whole trace as JSON |
|
|
101
|
+
|
|
102
|
+
Environment: `TYPESAFE_KEY` (your key; without it a job that needs Jev stops and says where to get one, while a job the
|
|
103
|
+
code checks reject is still graded). Exit codes: `0` done, `1` a step failed, `2` a flag was refused.
|
|
104
|
+
|
|
79
105
|
## What you must know
|
|
80
106
|
|
|
81
107
|
- **Code decides the facts first.** A failed check ends the job as reject, and Jev is never asked.
|
package/bin/jev.mjs
CHANGED
|
@@ -12,7 +12,9 @@ const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
|
12
12
|
const PKG = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8'));
|
|
13
13
|
const args = process.argv.slice(2);
|
|
14
14
|
const flag = (n) => { const i = args.indexOf(n); return i >= 0 ? args.splice(i, 2)[1] : undefined; };
|
|
15
|
-
|
|
15
|
+
// the newest bundle shipped (a bundle is versioned on its own and rebuilt only when the workflows change)
|
|
16
|
+
const BUNDLES = readdirSync(join(ROOT, 'workflows')).filter((d) => /^\d+\.\d+\.\d+$/.test(d)).sort((x, y) => x.localeCompare(y, undefined, { numeric: true }));
|
|
17
|
+
const version = flag('--version') ?? BUNDLES.at(-1) ?? PKG.version;
|
|
16
18
|
const network = flag('--network') ?? 'any';
|
|
17
19
|
const bundle = join(ROOT, 'workflows', version);
|
|
18
20
|
const die = (m) => { console.error(m); process.exit(1); };
|
package/bin/run.mjs
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// Keys come from the environment only: TYPESAFE_KEY (your own TypeSafe key, console.typesafe.ai) and
|
|
14
14
|
// TAIFOON_RELAYER_KEY (records the answers on the layer). Neither is printed or written anywhere.
|
|
15
15
|
// Output is @taifoon/term (vendored as ./term.mjs), the same rhythm as `taifoon up` and the STUDIO run log.
|
|
16
|
-
import { readFileSync } from 'node:fs';
|
|
16
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
17
17
|
import { createInterface } from 'node:readline/promises';
|
|
18
18
|
import { createTerm } from './term.mjs';
|
|
19
19
|
|
|
@@ -35,18 +35,36 @@ function factOf(r) {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
const USAGE = 'jev run [--demo | --job <chain>:<id> | --job-file job.json | --evidence pack.json] [--answers answers.json] [--record none|devnet|base|both] [--layer <https url> | --no-layer] [--protocol <name>] [--price-usdc <n>] [--yes] [--json]';
|
|
39
|
+
const PROTOCOLS = ['assurance-hook', 'judge-adapter', 'virtuals-erc8183', 'virtuals-memo-acp', 'bitagent-erc8183'];
|
|
40
|
+
/** A stranger's typo must never run something else: every flag is known, every value checked, exit 2 with the fix. */
|
|
41
|
+
function refuse(msg) { process.stderr.write(`jev run: ${msg}\n ${USAGE}\n`); process.exit(2); }
|
|
42
|
+
|
|
38
43
|
export async function run(argv) {
|
|
39
44
|
const a = [...argv];
|
|
40
45
|
const has = (n) => { const i = a.indexOf(n); if (i < 0) return false; a.splice(i, 1); return true; };
|
|
41
|
-
const val = (n) => {
|
|
46
|
+
const val = (n) => {
|
|
47
|
+
const i = a.indexOf(n); if (i < 0) return undefined;
|
|
48
|
+
const v = a[i + 1]; if (v === undefined || v.startsWith('--')) refuse(`${n} needs a value`);
|
|
49
|
+
a.splice(i, 2); return v;
|
|
50
|
+
};
|
|
51
|
+
const file = (n) => { const f = val(n); if (f !== undefined && !existsSync(f)) refuse(`${n}: no such file ${f}`); return f; };
|
|
42
52
|
const yes = has('--yes') || !process.stdin.isTTY; const json = has('--json'); const noLayer = has('--no-layer') || argv.includes('--demo') || argv.includes('--job-file');
|
|
43
53
|
const demo = has('--demo');
|
|
44
|
-
const job = val('--job'); const evFile =
|
|
45
|
-
const jobFile =
|
|
46
|
-
const ansFile =
|
|
54
|
+
const job = val('--job'); const evFile = file('--evidence');
|
|
55
|
+
const jobFile = file('--job-file');
|
|
56
|
+
const ansFile = file('--answers');
|
|
47
57
|
const rec = demo ? JSON.parse(readFileSync(new URL('../examples/jobs/base-bitagent-7287.recorded.json', import.meta.url), 'utf8')) : null;
|
|
48
58
|
const network = val('--record') ?? val('--network') ?? 'none'; const layer = noLayer ? false : val('--layer');
|
|
49
59
|
const protocol = val('--protocol'); const price = val('--price-usdc');
|
|
60
|
+
if (a.length) refuse(`unknown flag ${a[0]}`);
|
|
61
|
+
if (!['none', 'devnet', 'base', 'both'].includes(network)) refuse(`--record is none, devnet, base or both (got ${network})`);
|
|
62
|
+
if (demo && (job || evFile || jobFile || ansFile)) refuse('--demo replays one recorded job: drop --job, --job-file, --evidence and --answers');
|
|
63
|
+
if ([job, evFile, jobFile].filter(Boolean).length > 1) refuse('pick one source: --job, --job-file or --evidence');
|
|
64
|
+
if (job && !/^\d+:.+$/.test(job) && !/^\S+$/.test(job)) refuse(`--job is <chain>:<id>, e.g. 8453:bitagent:8453:7287 (got ${job})`);
|
|
65
|
+
if (layer && !/^https:\/\//.test(layer)) refuse('--layer must be an https:// URL');
|
|
66
|
+
if (protocol && !PROTOCOLS.includes(protocol)) refuse(`--protocol is one of ${PROTOCOLS.join(', ')}`);
|
|
67
|
+
if (price !== undefined && !(Number(price) > 0)) refuse(`--price-usdc must be a positive number (got ${price})`);
|
|
50
68
|
const { pipeline, STEPS, prepareJob } = await import('../dist/index.js');
|
|
51
69
|
|
|
52
70
|
const t = createTerm({ quiet: json });
|
package/dist/pipeline.js
CHANGED
|
@@ -142,9 +142,15 @@ export async function pipeline(o = {}) {
|
|
|
142
142
|
}
|
|
143
143
|
else if (o.key)
|
|
144
144
|
g.key = o.key;
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
145
|
+
// no key: a job the code checks reject is still graded (Jev is not asked); only asking Jev needs the key
|
|
146
|
+
try {
|
|
147
|
+
t.receipt = await grade(g);
|
|
148
|
+
}
|
|
149
|
+
catch (e) {
|
|
150
|
+
if (!o.key && !o.answers && /TypeSafe key/.test(e.message))
|
|
151
|
+
throw new Error('Jev needs your TypeSafe key: set TYPESAFE_KEY (console.typesafe.ai), or pass answers you already have');
|
|
152
|
+
throw e;
|
|
153
|
+
}
|
|
148
154
|
return { verdict: t.receipt.verdict, reasons: t.receipt.reasons, model: t.receipt.model, receiptHash: t.receipt.receiptHash };
|
|
149
155
|
});
|
|
150
156
|
if (failed() || !t.receipt)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taifoon/jev",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
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",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
61
61
|
"typecheck": "tsc --noEmit -p .",
|
|
62
62
|
"test": "vitest run",
|
|
63
|
-
"check": "npm run typecheck && npm
|
|
63
|
+
"check": "npm run typecheck && npm run build && npm test",
|
|
64
64
|
"prepublishOnly": "npm run check",
|
|
65
65
|
"bundle": "node scripts/bundle-workflows.mjs",
|
|
66
66
|
"zip": "node scripts/zip-workflows.mjs"
|