@taifoon/jev 0.1.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.
Files changed (56) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +222 -0
  3. package/bin/jev.mjs +37 -0
  4. package/dist/abi.d.ts +11 -0
  5. package/dist/abi.js +81 -0
  6. package/dist/ask.d.ts +27 -0
  7. package/dist/ask.js +47 -0
  8. package/dist/contracts.d.ts +37 -0
  9. package/dist/contracts.js +27 -0
  10. package/dist/evaluator/assurance-hook.d.ts +6 -0
  11. package/dist/evaluator/assurance-hook.js +15 -0
  12. package/dist/evaluator/bitagent-erc8183.d.ts +7 -0
  13. package/dist/evaluator/bitagent-erc8183.js +18 -0
  14. package/dist/evaluator/index.d.ts +12 -0
  15. package/dist/evaluator/index.js +20 -0
  16. package/dist/evaluator/judge-adapter.d.ts +5 -0
  17. package/dist/evaluator/judge-adapter.js +16 -0
  18. package/dist/evaluator/types.d.ts +27 -0
  19. package/dist/evaluator/types.js +6 -0
  20. package/dist/evaluator/virtuals-erc8183.d.ts +6 -0
  21. package/dist/evaluator/virtuals-erc8183.js +15 -0
  22. package/dist/evaluator/virtuals-memo-acp.d.ts +5 -0
  23. package/dist/evaluator/virtuals-memo-acp.js +15 -0
  24. package/dist/facts.d.ts +16 -0
  25. package/dist/facts.js +15 -0
  26. package/dist/grade.d.ts +32 -0
  27. package/dist/grade.js +33 -0
  28. package/dist/hash.d.ts +10 -0
  29. package/dist/hash.js +132 -0
  30. package/dist/index.d.ts +29 -0
  31. package/dist/index.js +20 -0
  32. package/dist/receipt.d.ts +68 -0
  33. package/dist/receipt.js +42 -0
  34. package/dist/record.d.ts +32 -0
  35. package/dist/record.js +41 -0
  36. package/dist/records.d.ts +78 -0
  37. package/dist/records.js +55 -0
  38. package/dist/rubric.d.ts +109 -0
  39. package/dist/rubric.js +91 -0
  40. package/dist/verify.d.ts +40 -0
  41. package/dist/verify.js +80 -0
  42. package/package.json +73 -0
  43. package/workflows/0.1.0/README.md +104 -0
  44. package/workflows/0.1.0/batch-judge.json +341 -0
  45. package/workflows/0.1.0/hire-judge-settle.json +767 -0
  46. package/workflows/0.1.0/jev-grader.json +433 -0
  47. package/workflows/0.1.0/manifest.json +151 -0
  48. package/workflows/0.1.0/schemas/answers.json +248 -0
  49. package/workflows/0.1.0/schemas/common.json +394 -0
  50. package/workflows/0.1.0/schemas/delivery.json +233 -0
  51. package/workflows/0.1.0/schemas/facts.json +225 -0
  52. package/workflows/0.1.0/schemas/hire.json +337 -0
  53. package/workflows/0.1.0/schemas/job.json +96 -0
  54. package/workflows/0.1.0/schemas/settlement.json +225 -0
  55. package/workflows/0.1.0/schemas/verdict.json +314 -0
  56. package/workflows/0.1.0/stamp-grade.json +175 -0
@@ -0,0 +1,767 @@
1
+ {
2
+ "name": "Taifoon — HIRE → JUDGE → SETTLE (devnet 36927)",
3
+ "nodes": [
4
+ {
5
+ "parameters": {},
6
+ "id": "n0",
7
+ "name": "Start",
8
+ "type": "n8n-nodes-base.manualTrigger",
9
+ "typeVersion": 1,
10
+ "position": [
11
+ 0,
12
+ 300
13
+ ],
14
+ "notes": "in: — → out: — · start a run (swap for a Webhook or Schedule trigger)",
15
+ "notesInFlow": true
16
+ },
17
+ {
18
+ "parameters": {
19
+ "assignments": {
20
+ "assignments": [
21
+ {
22
+ "id": "i1",
23
+ "name": "task",
24
+ "value": "Verify Base block 51828000 under the SuperRoot and return the portable V5 proof (class proof.verify.v5).",
25
+ "type": "string"
26
+ },
27
+ {
28
+ "id": "i2",
29
+ "name": "candidate",
30
+ "value": "0x3574999dd4c96eb73bd6e11d4177010c83e14f5b",
31
+ "type": "string"
32
+ },
33
+ {
34
+ "id": "i3",
35
+ "name": "endpoint",
36
+ "value": "https://www.taifoon.io/api/mcp",
37
+ "type": "string"
38
+ },
39
+ {
40
+ "id": "i4",
41
+ "name": "tool",
42
+ "value": "taifoon_proof",
43
+ "type": "string"
44
+ },
45
+ {
46
+ "id": "i5",
47
+ "name": "block_number",
48
+ "value": 51828000,
49
+ "type": "number"
50
+ },
51
+ {
52
+ "id": "i6",
53
+ "name": "buyer",
54
+ "value": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
55
+ "type": "string"
56
+ },
57
+ {
58
+ "id": "i7",
59
+ "name": "seller",
60
+ "value": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
61
+ "type": "string"
62
+ },
63
+ {
64
+ "id": "i8",
65
+ "name": "price",
66
+ "value": "1000000",
67
+ "type": "string"
68
+ },
69
+ {
70
+ "id": "i9",
71
+ "name": "chainId",
72
+ "value": 36927,
73
+ "type": "number"
74
+ }
75
+ ]
76
+ },
77
+ "options": {}
78
+ },
79
+ "id": "n1",
80
+ "name": "Inputs",
81
+ "type": "n8n-nodes-base.set",
82
+ "typeVersion": 3.4,
83
+ "position": [
84
+ 220,
85
+ 300
86
+ ],
87
+ "notes": "in: — → out: hire.json · the hire intent: task, candidate seller, endpoint/tool, buyer, price, chain (a HIRE in state Open)",
88
+ "notesInFlow": true
89
+ },
90
+ {
91
+ "parameters": {
92
+ "resource": "handshake",
93
+ "operation": "open",
94
+ "candidateAddress": "={{ $json.candidate }}",
95
+ "candidateKind": "mcp",
96
+ "task": "={{ $json.task }}",
97
+ "openOptions": {
98
+ "args": "={{ JSON.stringify({ chain_id: 8453, block_number: $json.block_number }) }}",
99
+ "chain": "={{ String($json.chainId) }}",
100
+ "dispatch": true,
101
+ "endpoint": "={{ $json.endpoint }}",
102
+ "hirer": "={{ $json.buyer }}",
103
+ "required_skills": "proof.verify.v5",
104
+ "tool": "={{ $json.tool }}"
105
+ }
106
+ },
107
+ "id": "n2",
108
+ "name": "Offer + deliver",
109
+ "type": "CUSTOM.taifoon",
110
+ "typeVersion": 1,
111
+ "position": [
112
+ 440,
113
+ 300
114
+ ],
115
+ "notes": "in: hire.json → out: job.json · POST /v1/handshake {dispatch:true}: the offer goes to the seller’s own endpoint; the reply is held with its keccak digest",
116
+ "notesInFlow": true,
117
+ "credentials": {
118
+ "taifoonRelayerApi": {
119
+ "id": "",
120
+ "name": "REPLACE: taifoonRelayerApi"
121
+ }
122
+ }
123
+ },
124
+ {
125
+ "parameters": {
126
+ "jsCode": "// Delivery: what came back over the wire. No digest = nothing to settle; say why and stop.\nconst r = $input.first().json;\nconst d = r.delivery || {};\nconst digest = d.reply_digest || d.digest || null;\nif (!r.handshake_id) throw new Error('no handshake id in ' + JSON.stringify(r).slice(0, 300));\nif (!digest) throw new Error('no delivery digest: dispatch status ' + (d.status || 'none') + (d.reason ? ' — ' + d.reason : ''));\nreturn [{ json: { handshake_id: r.handshake_id, delivery_status: d.status || null, protocol: d.protocol || null, latency_ms: d.latency_ms ?? null, reply_head: d.reply_head || null, evidence: digest, target: r.target || null } }];"
127
+ },
128
+ "id": "n3",
129
+ "name": "Delivery",
130
+ "type": "n8n-nodes-base.code",
131
+ "typeVersion": 2,
132
+ "position": [
133
+ 660,
134
+ 300
135
+ ],
136
+ "notes": "in: job.json → out: delivery.json · stop unless a reply digest is held (no digest = nothing to settle)",
137
+ "notesInFlow": true
138
+ },
139
+ {
140
+ "name": "Judge · prepare",
141
+ "type": "CUSTOM.taifoon",
142
+ "typeVersion": 1,
143
+ "position": [
144
+ 880,
145
+ 300
146
+ ],
147
+ "parameters": {
148
+ "resource": "judge",
149
+ "operation": "compose",
150
+ "composeSubject": "handshake",
151
+ "composeHandshakeId": "={{ $json.handshake_id }}",
152
+ "composePriceUsdc": 1,
153
+ "composeMode": "prepare"
154
+ },
155
+ "credentials": {
156
+ "taifoonRelayerApi": {
157
+ "id": "",
158
+ "name": "REPLACE: taifoonRelayerApi"
159
+ }
160
+ },
161
+ "id": "c2af3b0e-7e53-4102-88b8-4d9cea1be0d8",
162
+ "notes": "in: delivery.json → out: facts.json · POST /v1/judge/compose (prepare): the facts decide first; hard_fail ends here; else the exact text Jev reads + prepare_digest",
163
+ "notesInFlow": true
164
+ },
165
+ {
166
+ "name": "Hard fail?",
167
+ "type": "n8n-nodes-base.if",
168
+ "typeVersion": 2.2,
169
+ "position": [
170
+ 1100,
171
+ 300
172
+ ],
173
+ "parameters": {
174
+ "conditions": {
175
+ "options": {
176
+ "caseSensitive": true,
177
+ "leftValue": "",
178
+ "typeValidation": "strict",
179
+ "version": 2
180
+ },
181
+ "conditions": [
182
+ {
183
+ "id": "h1",
184
+ "leftValue": "={{ $json.hard_fail === true }}",
185
+ "rightValue": "",
186
+ "operator": {
187
+ "type": "boolean",
188
+ "operation": "true",
189
+ "singleValue": true
190
+ }
191
+ }
192
+ ],
193
+ "combinator": "and"
194
+ },
195
+ "options": {}
196
+ },
197
+ "id": "039a7992-9c11-4902-a64e-47518a3451c4",
198
+ "notes": "in: facts.json → out: facts.json · branch: a hard fail goes straight to the verdict without asking Jev",
199
+ "notesInFlow": true
200
+ },
201
+ {
202
+ "name": "Jev · TypeSafe (our credential)",
203
+ "type": "@taifoon/n8n-nodes-typesafe.taifoonTypeSafe",
204
+ "typeVersion": 1,
205
+ "position": [
206
+ 1320,
207
+ 420
208
+ ],
209
+ "parameters": {
210
+ "operation": "ask",
211
+ "connection": "direct",
212
+ "state": "={{ JSON.stringify($json.jev.state) }}",
213
+ "questions": {},
214
+ "questionsJson": "={{ JSON.stringify($json.jev.questions) }}",
215
+ "routing": "{}",
216
+ "replyLanguage": "off",
217
+ "failClosed": true
218
+ },
219
+ "credentials": {
220
+ "taifoonTypeSafeApi": {
221
+ "id": "",
222
+ "name": "REPLACE: taifoonTypeSafeApi"
223
+ }
224
+ },
225
+ "notes": "in: facts.json → out: answers.json · TypeSafe ask on the prepared text: every RUBRIC_ASKED answer with its whole distribution",
226
+ "id": "ba778a32-16e0-4076-b815-ffd923ee73f0",
227
+ "notesInFlow": true
228
+ },
229
+ {
230
+ "name": "Judge · answers",
231
+ "type": "CUSTOM.taifoon",
232
+ "typeVersion": 1,
233
+ "position": [
234
+ 1540,
235
+ 420
236
+ ],
237
+ "parameters": {
238
+ "resource": "judge",
239
+ "operation": "compose",
240
+ "composeSubject": "handshake",
241
+ "composeHandshakeId": "={{ $('Delivery').first().json.handshake_id }}",
242
+ "composePriceUsdc": 1,
243
+ "composeMode": "answers",
244
+ "composeAnswers": "={{ JSON.stringify($json.answers) }}",
245
+ "composeModel": "={{ $json.model }}",
246
+ "composePrepareDigest": "={{ $('Judge · prepare').first().json.prepare_digest }}",
247
+ "composeAnsweredBy": "n8n-typesafe",
248
+ "composeExecutionId": "={{ $execution.id }}",
249
+ "composeLatencyMs": "={{ $json.latency_ms }}"
250
+ },
251
+ "credentials": {
252
+ "taifoonRelayerApi": {
253
+ "id": "",
254
+ "name": "REPLACE: taifoonRelayerApi"
255
+ }
256
+ },
257
+ "id": "7b18e907-2293-4bb6-8199-c2a090bb7839",
258
+ "notes": "in: answers.json → out: verdict.json · POST /v1/judge/compose (answers): recompose under THRESHOLDS_v1, record decision.v2, anchor on JevDecisionLog",
259
+ "notesInFlow": true
260
+ },
261
+ {
262
+ "parameters": {
263
+ "jsCode": "// The verdict, with what the settlement needs carried forward. Two roads reach here: a hard fail straight from\n// Judge · prepare (the facts decided; Jev was not asked), or Judge · answers (our TypeSafe credential answered, the layer composed).\n// The receipt is the composed verdict: verdict (complete | reject | needs_review), auto, reasons[], receiptHash.\nconst j = $input.first().json;\nconst del = $('Delivery').first().json;\nconst inp = $('Inputs').first().json;\nif (j.ok === false) throw new Error('judge refused: ' + (j.code ? j.code + ': ' : '') + (j.error || JSON.stringify(j).slice(0, 300)));\nconst decision = j.decision || {};\nconst rc = j.receipt || {};\nreturn [{ json: { ...inp, handshake_id: del.handshake_id, evidence: del.evidence, reply_head: del.reply_head, decision_id: decision.id || null, decision_digest: decision.digest || null, decision_anchor: decision.anchor || null, verdict: rc.verdict || null, verdict_auto: rc.auto ?? null, forced: rc.forced || null, reasons: rc.reasons || [], receipt_hash: rc.receiptHash || null, next: j.next || null, judge_called: j.judge_called ?? null, model: j.model || null, judge_latency_ms: j.latency_ms ?? null, answers_source: j.answers_source || null, execution: j.execution || null, prepare_digest: j.prepare_digest || null, quota: j.quota || null } }];"
264
+ },
265
+ "id": "n5",
266
+ "name": "Verdict",
267
+ "type": "n8n-nodes-base.code",
268
+ "typeVersion": 2,
269
+ "position": [
270
+ 1760,
271
+ 300
272
+ ],
273
+ "notes": "in: verdict.json | facts.json → out: verdict.json · the composed verdict (complete | reject | needs_review) with what settlement needs carried forward",
274
+ "notesInFlow": true
275
+ },
276
+ {
277
+ "parameters": {
278
+ "resource": "settlement",
279
+ "operation": "settle",
280
+ "kind": "agent",
281
+ "chainId": 36927,
282
+ "buyer": "={{ $json.buyer }}",
283
+ "seller": "={{ $json.seller }}",
284
+ "price": "={{ $json.price }}",
285
+ "evidence": "={{ $json.evidence }}",
286
+ "refId": "={{ $json.handshake_id }}",
287
+ "settleOptions": {
288
+ "endpoint": "={{ $json.endpoint }}",
289
+ "note": "={{ 'agent ' + $json.candidate + ' · decision ' + ($json.decision_id || 'none') + ' · verdict ' + ($json.verdict || 'n/a') }}"
290
+ }
291
+ },
292
+ "credentials": {
293
+ "taifoonRelayerApi": {
294
+ "id": "",
295
+ "name": "REPLACE: taifoonRelayerApi"
296
+ }
297
+ },
298
+ "id": "n6",
299
+ "name": "Settle",
300
+ "type": "CUSTOM.taifoon",
301
+ "typeVersion": 1,
302
+ "position": [
303
+ 1980,
304
+ 300
305
+ ],
306
+ "notes": "in: verdict.json → out: settlement.json · POST /v1/settle: the plan (jobId, price, premium, deposit, calls[] in order, who signs each)",
307
+ "notesInFlow": true
308
+ },
309
+ {
310
+ "parameters": {
311
+ "callsField": "calls",
312
+ "onlySigner": "seller",
313
+ "onlyActions": "approve-deposit",
314
+ "sendRequires": false,
315
+ "wait": true
316
+ },
317
+ "credentials": {
318
+ "taifoonDevnetKeyApi": {
319
+ "id": "",
320
+ "name": "REPLACE: taifoonDevnetKeyApi"
321
+ }
322
+ },
323
+ "id": "n7",
324
+ "name": "Seller · approve deposit",
325
+ "type": "CUSTOM.taifoonDevnetSigner",
326
+ "typeVersion": 1,
327
+ "position": [
328
+ 2200,
329
+ 300
330
+ ],
331
+ "notes": "in: settlement.json → out: settlement.json · the seller approves D (> P); the tx joins settlement.txs",
332
+ "notesInFlow": true
333
+ },
334
+ {
335
+ "parameters": {
336
+ "callsField": "calls",
337
+ "onlySigner": "buyer",
338
+ "onlyActions": "fund-job",
339
+ "sendRequires": false,
340
+ "wait": true
341
+ },
342
+ "credentials": {
343
+ "taifoonDevnetKeyApi": {
344
+ "id": "",
345
+ "name": "REPLACE: taifoonDevnetKeyApi"
346
+ }
347
+ },
348
+ "id": "n8",
349
+ "name": "Buyer · approve + fund",
350
+ "type": "CUSTOM.taifoonDevnetSigner",
351
+ "typeVersion": 1,
352
+ "position": [
353
+ 2420,
354
+ 300
355
+ ],
356
+ "notes": "in: settlement.json → out: settlement.json · the buyer approves P + π and calls fundJob: machine Delivered → Funded",
357
+ "notesInFlow": true
358
+ },
359
+ {
360
+ "parameters": {
361
+ "callsField": "calls",
362
+ "onlySigner": "seller",
363
+ "onlyActions": "submit",
364
+ "sendRequires": false,
365
+ "wait": true
366
+ },
367
+ "credentials": {
368
+ "taifoonDevnetKeyApi": {
369
+ "id": "",
370
+ "name": "REPLACE: taifoonDevnetKeyApi"
371
+ }
372
+ },
373
+ "id": "n9",
374
+ "name": "Seller · submit",
375
+ "type": "CUSTOM.taifoonDevnetSigner",
376
+ "typeVersion": 1,
377
+ "position": [
378
+ 2640,
379
+ 300
380
+ ],
381
+ "notes": "in: settlement.json → out: settlement.json · submit(jobId, evidenceDigest = delivery.digest): machine Funded → Submitted",
382
+ "notesInFlow": true
383
+ },
384
+ {
385
+ "parameters": {
386
+ "callsField": "calls",
387
+ "onlySigner": "",
388
+ "onlyActions": "complete,capture",
389
+ "sendRequires": false,
390
+ "wait": true
391
+ },
392
+ "credentials": {
393
+ "taifoonDevnetKeyApi": {
394
+ "id": "",
395
+ "name": "REPLACE: taifoonDevnetKeyApi"
396
+ }
397
+ },
398
+ "id": "n10",
399
+ "name": "Buyer · complete + capture",
400
+ "type": "CUSTOM.taifoonDevnetSigner",
401
+ "typeVersion": 1,
402
+ "position": [
403
+ 3080,
404
+ 160
405
+ ],
406
+ "notes": "in: settlement.json → out: settlement.json · complete(jobId) (+ capture): machine → Settled",
407
+ "notesInFlow": true
408
+ },
409
+ {
410
+ "parameters": {
411
+ "jsCode": "// The record: every transaction with its proof and explorer link, the decision, the job, and whether money moved.\nconst legs = ['Seller · approve deposit', 'Buyer · approve + fund', 'Seller · submit', 'Buyer · complete + capture'];\nconst txs = [];\nfor (const n of legs) { let sent = []; try { sent = $(n).first().json.sent || []; } catch (e) { /* a leg the verdict skipped */ }\n for (const s of sent) txs.push({ leg: n, action: s.action, tx: s.tx, status: s.status, block: s.block, proof: 'https://www.taifoon.io/v1/proof/tx/36927/' + s.tx, scan: 'https://www.taifoon.io/scan/36927/tx/' + s.tx }); }\nlet held = null; try { held = $('Held for review').first().json; } catch (e) { held = null; }\nconst s = $('Settle').first().json;\nconst v = $('Verdict').first().json;\nconst complete = txs.find((t) => t.action === 'complete') || null;\nreturn [{ json: { handshake_id: v.handshake_id, decision_id: v.decision_id, decision_digest: v.decision_digest, decision_anchor_tx: v.decision_anchor ? v.decision_anchor.tx : null, verdict: v.verdict, verdict_auto: v.verdict_auto, reasons: v.reasons, receipt_hash: v.receipt_hash, judge: { called: v.judge_called, model: v.model, latency_ms: v.judge_latency_ms, answers_source: v.answers_source, execution: v.execution, prepare_digest: v.prepare_digest, quota: v.quota }, ending: complete ? 'completed: seller paid, deposit returned, payment captured' : held ? 'held: ' + held.next : 'incomplete', jobId: s.jobId, correlator: s.correlator, chainId: 36927, hook: s.hook, token: s.token, price: s.price, premium: s.premium, deposit: s.deposit, evidence: v.evidence, settlement_tx: complete ? complete.tx : null, settlement_proof: complete ? complete.proof : null, transactions: txs, all_mined: txs.every((t) => t.status === 'success') && txs.length === (complete ? 6 : 4) } }];"
412
+ },
413
+ "id": "n11",
414
+ "name": "Receipt",
415
+ "type": "n8n-nodes-base.code",
416
+ "typeVersion": 2,
417
+ "position": [
418
+ 3300,
419
+ 300
420
+ ],
421
+ "notes": "in: settlement.json → out: job.json · the whole JOB: hire, delivery, facts, verdict, settlement — every transaction with its proof link",
422
+ "notesInFlow": true
423
+ },
424
+ {
425
+ "parameters": {
426
+ "conditions": {
427
+ "options": {
428
+ "caseSensitive": true,
429
+ "leftValue": "",
430
+ "typeValidation": "strict",
431
+ "version": 2
432
+ },
433
+ "conditions": [
434
+ {
435
+ "id": "g1",
436
+ "leftValue": "={{ $('Verdict').first().json.verdict }}",
437
+ "rightValue": "complete",
438
+ "operator": {
439
+ "type": "string",
440
+ "operation": "equals"
441
+ }
442
+ }
443
+ ],
444
+ "combinator": "and"
445
+ },
446
+ "options": {}
447
+ },
448
+ "id": "n12",
449
+ "name": "Verdict says complete?",
450
+ "type": "n8n-nodes-base.if",
451
+ "typeVersion": 2.2,
452
+ "position": [
453
+ 2860,
454
+ 300
455
+ ],
456
+ "notes": "in: settlement.json → out: settlement.json · branch on the composed verdict: complete → complete; otherwise hold (Submitted → Held)",
457
+ "notesInFlow": true
458
+ },
459
+ {
460
+ "parameters": {
461
+ "jsCode": "// Held: the job is funded and the evidence is sealed; nothing is paid. Say what decides it next.\nconst v = $('Verdict').first().json;\nconst s = $('Settle').first().json;\nreturn [{ json: { held: true, verdict: v.verdict, reasons: v.reasons, next: v.next || (v.verdict === 'reject' ? 'reject: the buyer may reject on chain; the pool pays nothing on a rejection' : 'needs_review: open the evaluator path (bonded judge / challenge); do not auto-settle'), jobId: s.jobId, hook: s.hook, chainId: s.chainId, deadline: s.deadline, what_is_on_chain: 'fundJob (price + premium in escrow, seller deposit locked) and submit (evidence digest sealed); complete/reject not sent' } }];"
462
+ },
463
+ "id": "n13",
464
+ "name": "Held for review",
465
+ "type": "n8n-nodes-base.code",
466
+ "typeVersion": 2,
467
+ "position": [
468
+ 3080,
469
+ 480
470
+ ],
471
+ "notes": "in: settlement.json → out: settlement.json · needs_review / reject: funded and sealed, nothing paid; the evaluator leg or the deadline decides",
472
+ "notesInFlow": true
473
+ }
474
+ ],
475
+ "connections": {
476
+ "Start": {
477
+ "main": [
478
+ [
479
+ {
480
+ "node": "Inputs",
481
+ "type": "main",
482
+ "index": 0
483
+ }
484
+ ]
485
+ ]
486
+ },
487
+ "Inputs": {
488
+ "main": [
489
+ [
490
+ {
491
+ "node": "Offer + deliver",
492
+ "type": "main",
493
+ "index": 0
494
+ }
495
+ ]
496
+ ]
497
+ },
498
+ "Offer + deliver": {
499
+ "main": [
500
+ [
501
+ {
502
+ "node": "Delivery",
503
+ "type": "main",
504
+ "index": 0
505
+ }
506
+ ]
507
+ ]
508
+ },
509
+ "Delivery": {
510
+ "main": [
511
+ [
512
+ {
513
+ "node": "Judge · prepare",
514
+ "type": "main",
515
+ "index": 0
516
+ }
517
+ ]
518
+ ]
519
+ },
520
+ "Verdict": {
521
+ "main": [
522
+ [
523
+ {
524
+ "node": "Settle",
525
+ "type": "main",
526
+ "index": 0
527
+ }
528
+ ]
529
+ ]
530
+ },
531
+ "Settle": {
532
+ "main": [
533
+ [
534
+ {
535
+ "node": "Seller · approve deposit",
536
+ "type": "main",
537
+ "index": 0
538
+ }
539
+ ]
540
+ ]
541
+ },
542
+ "Seller · approve deposit": {
543
+ "main": [
544
+ [
545
+ {
546
+ "node": "Buyer · approve + fund",
547
+ "type": "main",
548
+ "index": 0
549
+ }
550
+ ]
551
+ ]
552
+ },
553
+ "Buyer · approve + fund": {
554
+ "main": [
555
+ [
556
+ {
557
+ "node": "Seller · submit",
558
+ "type": "main",
559
+ "index": 0
560
+ }
561
+ ]
562
+ ]
563
+ },
564
+ "Seller · submit": {
565
+ "main": [
566
+ [
567
+ {
568
+ "node": "Verdict says complete?",
569
+ "type": "main",
570
+ "index": 0
571
+ }
572
+ ]
573
+ ]
574
+ },
575
+ "Buyer · complete + capture": {
576
+ "main": [
577
+ [
578
+ {
579
+ "node": "Receipt",
580
+ "type": "main",
581
+ "index": 0
582
+ }
583
+ ]
584
+ ]
585
+ },
586
+ "Verdict says complete?": {
587
+ "main": [
588
+ [
589
+ {
590
+ "node": "Buyer · complete + capture",
591
+ "type": "main",
592
+ "index": 0
593
+ }
594
+ ],
595
+ [
596
+ {
597
+ "node": "Held for review",
598
+ "type": "main",
599
+ "index": 0
600
+ }
601
+ ]
602
+ ]
603
+ },
604
+ "Held for review": {
605
+ "main": [
606
+ [
607
+ {
608
+ "node": "Receipt",
609
+ "type": "main",
610
+ "index": 0
611
+ }
612
+ ]
613
+ ]
614
+ },
615
+ "Judge · prepare": {
616
+ "main": [
617
+ [
618
+ {
619
+ "node": "Hard fail?",
620
+ "type": "main",
621
+ "index": 0
622
+ }
623
+ ]
624
+ ]
625
+ },
626
+ "Hard fail?": {
627
+ "main": [
628
+ [
629
+ {
630
+ "node": "Verdict",
631
+ "type": "main",
632
+ "index": 0
633
+ }
634
+ ],
635
+ [
636
+ {
637
+ "node": "Jev · TypeSafe (our credential)",
638
+ "type": "main",
639
+ "index": 0
640
+ }
641
+ ]
642
+ ]
643
+ },
644
+ "Jev · TypeSafe (our credential)": {
645
+ "main": [
646
+ [
647
+ {
648
+ "node": "Judge · answers",
649
+ "type": "main",
650
+ "index": 0
651
+ }
652
+ ]
653
+ ]
654
+ },
655
+ "Judge · answers": {
656
+ "main": [
657
+ [
658
+ {
659
+ "node": "Verdict",
660
+ "type": "main",
661
+ "index": 0
662
+ }
663
+ ]
664
+ ]
665
+ }
666
+ },
667
+ "settings": {
668
+ "executionOrder": "v1"
669
+ },
670
+ "active": false,
671
+ "meta": {
672
+ "taifoon": {
673
+ "contract": "https://www.taifoon.io/schemas/coordination/v1/",
674
+ "steps": {
675
+ "Start": {
676
+ "in": null,
677
+ "out": null,
678
+ "does": "start a run (swap for a Webhook or Schedule trigger)"
679
+ },
680
+ "Inputs": {
681
+ "in": null,
682
+ "out": "https://www.taifoon.io/schemas/coordination/v1/hire.json",
683
+ "does": "the hire intent: task, candidate seller, endpoint/tool, buyer, price, chain (a HIRE in state Open)"
684
+ },
685
+ "Offer + deliver": {
686
+ "in": "https://www.taifoon.io/schemas/coordination/v1/hire.json",
687
+ "out": "https://www.taifoon.io/schemas/coordination/v1/job.json",
688
+ "does": "POST /v1/handshake {dispatch:true}: the offer goes to the seller’s own endpoint; the reply is held with its keccak digest"
689
+ },
690
+ "Delivery": {
691
+ "in": "https://www.taifoon.io/schemas/coordination/v1/job.json",
692
+ "out": "https://www.taifoon.io/schemas/coordination/v1/delivery.json",
693
+ "does": "stop unless a reply digest is held (no digest = nothing to settle)"
694
+ },
695
+ "Judge · prepare": {
696
+ "in": "https://www.taifoon.io/schemas/coordination/v1/delivery.json",
697
+ "out": "https://www.taifoon.io/schemas/coordination/v1/facts.json",
698
+ "does": "POST /v1/judge/compose (prepare): the facts decide first; hard_fail ends here; else the exact text Jev reads + prepare_digest"
699
+ },
700
+ "Hard fail?": {
701
+ "in": "https://www.taifoon.io/schemas/coordination/v1/facts.json",
702
+ "out": "https://www.taifoon.io/schemas/coordination/v1/facts.json",
703
+ "does": "branch: a hard fail goes straight to the verdict without asking Jev"
704
+ },
705
+ "Jev · TypeSafe (our credential)": {
706
+ "in": "https://www.taifoon.io/schemas/coordination/v1/facts.json",
707
+ "out": "https://www.taifoon.io/schemas/coordination/v1/answers.json",
708
+ "does": "TypeSafe ask on the prepared text: every RUBRIC_ASKED answer with its whole distribution"
709
+ },
710
+ "Judge · answers": {
711
+ "in": "https://www.taifoon.io/schemas/coordination/v1/answers.json",
712
+ "out": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
713
+ "does": "POST /v1/judge/compose (answers): recompose under THRESHOLDS_v1, record decision.v2, anchor on JevDecisionLog"
714
+ },
715
+ "Verdict": {
716
+ "in": [
717
+ "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
718
+ "https://www.taifoon.io/schemas/coordination/v1/facts.json"
719
+ ],
720
+ "out": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
721
+ "does": "the composed verdict (complete | reject | needs_review) with what settlement needs carried forward"
722
+ },
723
+ "Settle": {
724
+ "in": "https://www.taifoon.io/schemas/coordination/v1/verdict.json",
725
+ "out": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
726
+ "does": "POST /v1/settle: the plan (jobId, price, premium, deposit, calls[] in order, who signs each)"
727
+ },
728
+ "Seller · approve deposit": {
729
+ "in": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
730
+ "out": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
731
+ "does": "the seller approves D (> P); the tx joins settlement.txs"
732
+ },
733
+ "Buyer · approve + fund": {
734
+ "in": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
735
+ "out": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
736
+ "does": "the buyer approves P + π and calls fundJob: machine Delivered → Funded"
737
+ },
738
+ "Seller · submit": {
739
+ "in": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
740
+ "out": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
741
+ "does": "submit(jobId, evidenceDigest = delivery.digest): machine Funded → Submitted"
742
+ },
743
+ "Verdict says complete?": {
744
+ "in": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
745
+ "out": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
746
+ "does": "branch on the composed verdict: complete → complete; otherwise hold (Submitted → Held)"
747
+ },
748
+ "Buyer · complete + capture": {
749
+ "in": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
750
+ "out": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
751
+ "does": "complete(jobId) (+ capture): machine → Settled"
752
+ },
753
+ "Held for review": {
754
+ "in": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
755
+ "out": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
756
+ "does": "needs_review / reject: funded and sealed, nothing paid; the evaluator leg or the deadline decides"
757
+ },
758
+ "Receipt": {
759
+ "in": "https://www.taifoon.io/schemas/coordination/v1/settlement.json",
760
+ "out": "https://www.taifoon.io/schemas/coordination/v1/job.json",
761
+ "does": "the whole JOB: hire, delivery, facts, verdict, settlement — every transaction with its proof link"
762
+ }
763
+ }
764
+ }
765
+ },
766
+ "tags": []
767
+ }