@taifoon/jev 0.1.0 → 0.2.1

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.
Files changed (49) hide show
  1. package/README.md +97 -191
  2. package/bin/jev.mjs +9 -3
  3. package/bin/run.mjs +86 -0
  4. package/bin/term.mjs +154 -0
  5. package/dist/ask.d.ts +5 -8
  6. package/dist/ask.js +16 -28
  7. package/dist/contracts.d.ts +5 -1
  8. package/dist/contracts.js +7 -3
  9. package/dist/grade.d.ts +0 -4
  10. package/dist/grade.js +5 -6
  11. package/dist/index.d.ts +7 -1
  12. package/dist/index.js +5 -1
  13. package/dist/job.d.ts +65 -0
  14. package/dist/job.js +50 -0
  15. package/dist/pipeline.d.ts +156 -0
  16. package/dist/pipeline.js +193 -0
  17. package/dist/rubric.js +1 -1
  18. package/dist/verify.d.ts +6 -0
  19. package/dist/verify.js +48 -9
  20. package/docs/API.md +20 -0
  21. package/docs/DEVELOP.md +7 -0
  22. package/docs/EVALUATORS.md +20 -0
  23. package/docs/ON_CHAIN.md +44 -0
  24. package/docs/PIPELINE.md +76 -0
  25. package/docs/RECORDS.md +10 -0
  26. package/docs/WORKFLOWS.md +38 -0
  27. package/examples/grade-job.mjs +17 -0
  28. package/examples/jobs/base-bitagent-7287.recorded.json +80 -0
  29. package/examples/jobs/invoice-extraction.json +24 -0
  30. package/examples/jobs/research-report-no-sources.json +29 -0
  31. package/examples/jobs/research-report.json +34 -0
  32. package/examples/jobs/signal-unsupported.json +27 -0
  33. package/examples/quickstart.mjs +22 -0
  34. package/package.json +19 -11
  35. package/workflows/{0.1.0 → 0.2.1}/README.md +3 -3
  36. package/workflows/{0.1.0 → 0.2.1}/jev-grader.json +2 -2
  37. package/workflows/{0.1.0 → 0.2.1}/manifest.json +12 -12
  38. package/workflows/{0.1.0 → 0.2.1}/schemas/answers.json +17 -35
  39. package/workflows/{0.1.0 → 0.2.1}/schemas/common.json +1 -1
  40. package/workflows/{0.1.0 → 0.2.1}/schemas/delivery.json +15 -31
  41. package/workflows/{0.1.0 → 0.2.1}/schemas/facts.json +15 -31
  42. package/workflows/{0.1.0 → 0.2.1}/schemas/hire.json +22 -45
  43. package/workflows/{0.1.0 → 0.2.1}/schemas/job.json +7 -15
  44. package/workflows/{0.1.0 → 0.2.1}/schemas/settlement.json +17 -33
  45. package/workflows/{0.1.0 → 0.2.1}/schemas/verdict.json +17 -35
  46. package/workflows/0.2.1/stamp-grade.json +175 -0
  47. package/workflows/0.1.0/stamp-grade.json +0 -175
  48. /package/workflows/{0.1.0 → 0.2.1}/batch-judge.json +0 -0
  49. /package/workflows/{0.1.0 → 0.2.1}/hire-judge-settle.json +0 -0
@@ -1,175 +0,0 @@
1
- {
2
- "name": "Taifoon — Stamp Grade (a recorded verdict → GradeStampRegistry on the devnet)",
3
- "nodes": [
4
- {
5
- "parameters": {},
6
- "id": "a1",
7
- "name": "Start",
8
- "type": "n8n-nodes-base.manualTrigger",
9
- "typeVersion": 1,
10
- "position": [
11
- 0,
12
- 0
13
- ],
14
- "notes": "in: — → out: — · start (pass { decision_id } to stamp another verdict)",
15
- "notesInFlow": true
16
- },
17
- {
18
- "parameters": {
19
- "jsCode": "// ── canonical VERDICT (taifoon.verdict.v1) from a decision record — the same mapping as schemas/src/from/judge.ts\n// fromDecision, inlined because an n8n code node cannot import the schemas package. workflows/test.mjs runs this node\n// and compares its output with fromDecision field by field.\nconst SITE = 'https://www.taifoon.io';\nconst isHash = (s) => typeof s === 'string' && /^0x[0-9a-fA-F]{64}$/.test(s);\nconst chainAnchor = (chain_id, tx, what, x = {}) => ({ kind: 'chain', chain_id, tx: tx.toLowerCase(), block: x.block ?? null, ts: x.ts ?? null, log_index: x.log_index ?? null, contract: x.contract ? x.contract.toLowerCase() : null, what });\nfunction jobKeyOf(subject) {\n const ref = String(subject && subject.ref || ''), chain = Number(subject && subject.chainId) || 8453;\n if (/^hs_[0-9a-f]{24}$/.test(ref)) return 'hs:' + ref;\n if (/^([a-z][a-z0-9-]{0,31}):([1-9][0-9]{0,9}):([0-9]{1,78})$/.test(ref)) return ref;\n if (/^0x[0-9a-fA-F]{64}$/.test(ref)) return 'hook:' + chain + ':' + ref.toLowerCase();\n if (/^[0-9]{1,78}$/.test(ref)) return /^[0-9]{10,12}$/.test(ref) && BigInt(ref) >= 1000000001n ? 'memo-acp:8453:' + ref : 'erc8183:' + chain + ':' + ref;\n return null;\n}\nfunction verdictFromDecision(d, composed) {\n const rubric = /RUBRIC_v1 compose · rubric (0x[0-9a-f]{64})/.exec(String(d.note ?? ''));\n const anchors = [];\n if (d.anchor && isHash(d.anchor.tx)) anchors.push(chainAnchor(Number(d.anchor.chain), d.anchor.tx, `JevDecisionLog.record${d.anchor.index != null ? ` (decision index ${d.anchor.index})` : ''} · status ${d.anchor.status}`, { block: d.anchor.block ?? null, contract: d.anchor.contract ?? null }));\n anchors.push({ kind: 'offchain', store: 'coordd:decisions', key: String(d.id), digest: isHash(d.digest) ? d.digest.toLowerCase() : null, anchored_by: anchors[0] && anchors[0].kind === 'chain' ? anchors[0] : null, what: 'the decision record; digest = sha256(canonicalDecision)' });\n return {\n schema: 'taifoon.verdict.v1', decision_id: String(d.id), version: d.v, kind: d.kind,\n subject: { chain_id: Number(d.subject && d.subject.chainId), at: d.subject && d.subject.at ? String(d.subject.at).toLowerCase() : null, ref: String(d.subject && d.subject.ref), label: (d.subject && d.subject.label) ?? null },\n subject_id: String(d.subject_id).toLowerCase(), subject_job_key: jobKeyOf(d.subject), digest: String(d.digest).toLowerCase(),\n input_digest: isHash(d.input_digest) ? d.input_digest.toLowerCase() : null, answers_digest: isHash(d.answers_digest) ? d.answers_digest.toLowerCase() : null,\n answers: (d.answers || []).map((a) => ({ id: String(a.id), question: a.question ?? null, options: a.options ?? null, value: String(a.value), probabilities: a.probabilities ?? {}, confidence: Number(a.confidence) })),\n confidence_bps: d.confidence_bps, model: d.model ?? null, answers_source: d.answers_source ?? 'layer',\n rubric: rubric ? { version: 'RUBRIC_v1', hash: rubric[1] } : null,\n composed: composed ? { verdict: composed.verdict, auto: Boolean(composed.auto), forced: composed.forced ?? null, reasons: (composed.reasons || []).map(String), receipt_hash: isHash(composed.receiptHash) ? composed.receiptHash.toLowerCase() : null } : null,\n anchors, source: { kind: 'decision', url: d.uri ?? `${SITE}/v1/judge/decisions/${d.id}`, read_at: Math.floor(Date.now() / 1000) },\n };\n}\n// the GradeStampRegistry verdict code (1 delivered_as_specified · 2 delivered_with_defects · 3 not_delivered · 4 cannot_determine)\nconst STAMP_CODE = { delivered_as_specified: 1, delivered_with_defects: 2, not_delivered: 3, cannot_determine: 4 };\nfunction stampCodeOf(v) {\n if (v.composed) return v.composed.verdict === 'complete' ? 1 : v.composed.verdict === 'reject' ? 2 : 4;\n const ref = (v.answers || []).find((a) => STAMP_CODE[a.value]);\n return ref ? STAMP_CODE[ref.value] : 4;\n}\n// the stamp transaction(s) the devnet signer mined → appended to the verdict's anchors\nconst stampAnchors = (sent, chainId) => (sent || []).filter((s) => isHash(s.tx) && s.status === 'success').map((s) => chainAnchor(chainId, s.tx, 'GradeStampRegistry.stamp (' + (s.action || 'stamp') + ')', { block: s.block ?? null, contract: s.to ?? null }));\n\n// the verdict to stamp: a recorded decision (pass { decision_id } on the trigger; the default is job 81067's REF decision)\nconst t = $input.first().json || {};\nconst id = t.decision_id || 'decision-1790239759511-7fc88c5799';\nconst d = (await this.helpers.httpRequest({ method: 'GET', url: SITE + '/v1/judge/decisions/' + id, json: true })).decision;\nconst v = verdictFromDecision(d, null);\nreturn [{ json: { ...v, stamp: { code: stampCodeOf(v), mode: 2 } } }];"
20
- },
21
- "id": "e00116ba-ac40-4cbe-9c4c-bddf65b3fc6d",
22
- "name": "Verdict",
23
- "type": "n8n-nodes-base.code",
24
- "typeVersion": 2,
25
- "position": [
26
- 420,
27
- 300
28
- ],
29
- "notes": "in: — → out: verdict.json · GET /v1/judge/decisions/:id → the canonical VERDICT (fromDecision) and its stamp code",
30
- "notesInFlow": true
31
- },
32
- {
33
- "parameters": {
34
- "resource": "assurance",
35
- "operation": "stampGrade",
36
- "chainId": 36927,
37
- "stampSubject": "={{ $json.subject_id }}",
38
- "stampDigest": "={{ $json.digest }}",
39
- "stampVerdict": "={{ String($json.stamp.code) }}",
40
- "stampMode": "={{ String($json.stamp.mode) }}",
41
- "stampModel": "={{ $json.model || 'jev' }}",
42
- "stampUri": "={{ $json.source.url }}"
43
- },
44
- "id": "b2",
45
- "name": "Stamp Grade",
46
- "type": "CUSTOM.taifoon",
47
- "typeVersion": 1,
48
- "position": [
49
- 640,
50
- 300
51
- ],
52
- "notes": "in: verdict.json → out: verdict.json · POST /v1/assurance/call stampGrade: the unsigned GradeStampRegistry.stamp(subject_id, digest, verdict code, mode)",
53
- "notesInFlow": true
54
- },
55
- {
56
- "parameters": {
57
- "callsField": "calls",
58
- "sendRequires": false,
59
- "wait": true
60
- },
61
- "id": "2900d540-a3cd-488b-bc44-175b18575dcf",
62
- "name": "Taifoon Devnet Signer",
63
- "type": "CUSTOM.taifoonDevnetSigner",
64
- "typeVersion": 1,
65
- "position": [
66
- 860,
67
- 300
68
- ],
69
- "credentials": {
70
- "taifoonDevnetKeyApi": {
71
- "id": "",
72
- "name": "REPLACE: taifoonDevnetKeyApi"
73
- }
74
- },
75
- "notes": "in: verdict.json → out: verdict.json · sign and send the stamp on devnet 36927 (anvil #0)",
76
- "notesInFlow": true
77
- },
78
- {
79
- "parameters": {
80
- "jsCode": "// ── canonical VERDICT (taifoon.verdict.v1) from a decision record — the same mapping as schemas/src/from/judge.ts\n// fromDecision, inlined because an n8n code node cannot import the schemas package. workflows/test.mjs runs this node\n// and compares its output with fromDecision field by field.\nconst SITE = 'https://www.taifoon.io';\nconst isHash = (s) => typeof s === 'string' && /^0x[0-9a-fA-F]{64}$/.test(s);\nconst chainAnchor = (chain_id, tx, what, x = {}) => ({ kind: 'chain', chain_id, tx: tx.toLowerCase(), block: x.block ?? null, ts: x.ts ?? null, log_index: x.log_index ?? null, contract: x.contract ? x.contract.toLowerCase() : null, what });\nfunction jobKeyOf(subject) {\n const ref = String(subject && subject.ref || ''), chain = Number(subject && subject.chainId) || 8453;\n if (/^hs_[0-9a-f]{24}$/.test(ref)) return 'hs:' + ref;\n if (/^([a-z][a-z0-9-]{0,31}):([1-9][0-9]{0,9}):([0-9]{1,78})$/.test(ref)) return ref;\n if (/^0x[0-9a-fA-F]{64}$/.test(ref)) return 'hook:' + chain + ':' + ref.toLowerCase();\n if (/^[0-9]{1,78}$/.test(ref)) return /^[0-9]{10,12}$/.test(ref) && BigInt(ref) >= 1000000001n ? 'memo-acp:8453:' + ref : 'erc8183:' + chain + ':' + ref;\n return null;\n}\nfunction verdictFromDecision(d, composed) {\n const rubric = /RUBRIC_v1 compose · rubric (0x[0-9a-f]{64})/.exec(String(d.note ?? ''));\n const anchors = [];\n if (d.anchor && isHash(d.anchor.tx)) anchors.push(chainAnchor(Number(d.anchor.chain), d.anchor.tx, `JevDecisionLog.record${d.anchor.index != null ? ` (decision index ${d.anchor.index})` : ''} · status ${d.anchor.status}`, { block: d.anchor.block ?? null, contract: d.anchor.contract ?? null }));\n anchors.push({ kind: 'offchain', store: 'coordd:decisions', key: String(d.id), digest: isHash(d.digest) ? d.digest.toLowerCase() : null, anchored_by: anchors[0] && anchors[0].kind === 'chain' ? anchors[0] : null, what: 'the decision record; digest = sha256(canonicalDecision)' });\n return {\n schema: 'taifoon.verdict.v1', decision_id: String(d.id), version: d.v, kind: d.kind,\n subject: { chain_id: Number(d.subject && d.subject.chainId), at: d.subject && d.subject.at ? String(d.subject.at).toLowerCase() : null, ref: String(d.subject && d.subject.ref), label: (d.subject && d.subject.label) ?? null },\n subject_id: String(d.subject_id).toLowerCase(), subject_job_key: jobKeyOf(d.subject), digest: String(d.digest).toLowerCase(),\n input_digest: isHash(d.input_digest) ? d.input_digest.toLowerCase() : null, answers_digest: isHash(d.answers_digest) ? d.answers_digest.toLowerCase() : null,\n answers: (d.answers || []).map((a) => ({ id: String(a.id), question: a.question ?? null, options: a.options ?? null, value: String(a.value), probabilities: a.probabilities ?? {}, confidence: Number(a.confidence) })),\n confidence_bps: d.confidence_bps, model: d.model ?? null, answers_source: d.answers_source ?? 'layer',\n rubric: rubric ? { version: 'RUBRIC_v1', hash: rubric[1] } : null,\n composed: composed ? { verdict: composed.verdict, auto: Boolean(composed.auto), forced: composed.forced ?? null, reasons: (composed.reasons || []).map(String), receipt_hash: isHash(composed.receiptHash) ? composed.receiptHash.toLowerCase() : null } : null,\n anchors, source: { kind: 'decision', url: d.uri ?? `${SITE}/v1/judge/decisions/${d.id}`, read_at: Math.floor(Date.now() / 1000) },\n };\n}\n// the GradeStampRegistry verdict code (1 delivered_as_specified · 2 delivered_with_defects · 3 not_delivered · 4 cannot_determine)\nconst STAMP_CODE = { delivered_as_specified: 1, delivered_with_defects: 2, not_delivered: 3, cannot_determine: 4 };\nfunction stampCodeOf(v) {\n if (v.composed) return v.composed.verdict === 'complete' ? 1 : v.composed.verdict === 'reject' ? 2 : 4;\n const ref = (v.answers || []).find((a) => STAMP_CODE[a.value]);\n return ref ? STAMP_CODE[ref.value] : 4;\n}\n// the stamp transaction(s) the devnet signer mined → appended to the verdict's anchors\nconst stampAnchors = (sent, chainId) => (sent || []).filter((s) => isHash(s.tx) && s.status === 'success').map((s) => chainAnchor(chainId, s.tx, 'GradeStampRegistry.stamp (' + (s.action || 'stamp') + ')', { block: s.block ?? null, contract: s.to ?? null }));\n\n// the same VERDICT with the GradeStampRegistry stamp transaction appended to its anchors\nconst v = { ...$('Verdict').first().json };\ndelete v.stamp;\nreturn [{ json: { ...v, anchors: [...v.anchors, ...stampAnchors($json.sent, 36927)] } }];"
81
- },
82
- "id": "49dcb63d-9ae5-46e8-b7c3-abdc60598643",
83
- "name": "Verdict + stamp",
84
- "type": "n8n-nodes-base.code",
85
- "typeVersion": 2,
86
- "position": [
87
- 1080,
88
- 300
89
- ],
90
- "notes": "in: verdict.json → out: verdict.json · the same VERDICT with the stamp transaction appended to anchors",
91
- "notesInFlow": true
92
- }
93
- ],
94
- "connections": {
95
- "Start": {
96
- "main": [
97
- [
98
- {
99
- "node": "Verdict",
100
- "type": "main",
101
- "index": 0
102
- }
103
- ]
104
- ]
105
- },
106
- "Verdict": {
107
- "main": [
108
- [
109
- {
110
- "node": "Stamp Grade",
111
- "type": "main",
112
- "index": 0
113
- }
114
- ]
115
- ]
116
- },
117
- "Stamp Grade": {
118
- "main": [
119
- [
120
- {
121
- "node": "Taifoon Devnet Signer",
122
- "type": "main",
123
- "index": 0
124
- }
125
- ]
126
- ]
127
- },
128
- "Taifoon Devnet Signer": {
129
- "main": [
130
- [
131
- {
132
- "node": "Verdict + stamp",
133
- "type": "main",
134
- "index": 0
135
- }
136
- ]
137
- ]
138
- }
139
- },
140
- "settings": {},
141
- "active": false,
142
- "meta": {
143
- "taifoon": {
144
- "contract": "https://www.taifoon.io/schemas/coordination/v1/",
145
- "steps": {
146
- "Start": {
147
- "in": null,
148
- "out": null,
149
- "does": "start (pass { decision_id } to stamp another verdict)"
150
- },
151
- "Verdict": {
152
- "in": null,
153
- "out": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
154
- "does": "GET /v1/judge/decisions/:id → the canonical VERDICT (fromDecision) and its stamp code"
155
- },
156
- "Stamp Grade": {
157
- "in": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
158
- "out": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
159
- "does": "POST /v1/assurance/call stampGrade: the unsigned GradeStampRegistry.stamp(subject_id, digest, verdict code, mode)"
160
- },
161
- "Taifoon Devnet Signer": {
162
- "in": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
163
- "out": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
164
- "does": "sign and send the stamp on devnet 36927 (anvil #0)"
165
- },
166
- "Verdict + stamp": {
167
- "in": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
168
- "out": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
169
- "does": "the same VERDICT with the stamp transaction appended to anchors"
170
- }
171
- }
172
- }
173
- },
174
- "tags": []
175
- }
File without changes