@finchip.ai/foga 0.0.0-stage → 0.6.0-alpha.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.
Files changed (131) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +21 -0
  3. package/README.md +216 -2
  4. package/artifacts/bundle.json +74250 -0
  5. package/artifacts/bundle.sha256 +1 -0
  6. package/bin/foga.js +14 -0
  7. package/build/known-interface-ids.json +18 -0
  8. package/build/profiles.json +120 -0
  9. package/dist/build/bundle.js +417 -0
  10. package/dist/build/vendor.js +74 -0
  11. package/dist/bundle/load.js +24 -0
  12. package/dist/bundle/types.js +1 -0
  13. package/dist/cli-cell.js +395 -0
  14. package/dist/cli.js +505 -0
  15. package/dist/core/acdf.js +162 -0
  16. package/dist/core/adrf.js +273 -0
  17. package/dist/core/aid.js +83 -0
  18. package/dist/core/ard.js +146 -0
  19. package/dist/core/asf.js +157 -0
  20. package/dist/core/canonical.js +79 -0
  21. package/dist/core/erc8004.js +52 -0
  22. package/dist/core/kya.js +225 -0
  23. package/dist/core/measure.js +96 -0
  24. package/dist/core/pack/cbor.js +130 -0
  25. package/dist/core/pack/skillpack.js +142 -0
  26. package/dist/core/skills.js +40 -0
  27. package/dist/core/swarm.js +206 -0
  28. package/dist/core/tenders.js +121 -0
  29. package/dist/core/units.js +122 -0
  30. package/dist/core/world.js +104 -0
  31. package/dist/demo/demander.js +76 -0
  32. package/dist/demo/evidence.js +1 -0
  33. package/dist/demo/loop-d.js +449 -0
  34. package/dist/doctor/checks.js +51 -0
  35. package/dist/doctor/doctor.js +385 -0
  36. package/dist/doctor/loop-d.js +103 -0
  37. package/dist/doctor/probe.js +97 -0
  38. package/dist/doctor/semantic.js +83 -0
  39. package/dist/genesis/extract.js +64 -0
  40. package/dist/genesis/funding.js +126 -0
  41. package/dist/genesis/operator-scope.js +90 -0
  42. package/dist/genesis/preflight.js +54 -0
  43. package/dist/genesis/probe.js +164 -0
  44. package/dist/genesis/roles.js +96 -0
  45. package/dist/genesis/run.js +71 -0
  46. package/dist/genesis/scenario.js +765 -0
  47. package/dist/genesis/skill-runner.js +103 -0
  48. package/dist/genesis/skill-worker.js +41 -0
  49. package/dist/genesis/t1-zk.js +62 -0
  50. package/dist/genesis/t3-zk.js +105 -0
  51. package/dist/genesis/t4.js +259 -0
  52. package/dist/genesis/t5.js +168 -0
  53. package/dist/genesis/t6.js +222 -0
  54. package/dist/genesis/t7.js +198 -0
  55. package/dist/genesis/t8.js +189 -0
  56. package/dist/genesis/t9.js +188 -0
  57. package/dist/genesis/zk.js +128 -0
  58. package/dist/http/server.js +276 -0
  59. package/dist/kernel/cell.js +104 -0
  60. package/dist/kernel/errors.js +78 -0
  61. package/dist/kernel/kernel.js +251 -0
  62. package/dist/kernel/provider.js +125 -0
  63. package/dist/kernel/store.js +470 -0
  64. package/dist/kernel/tx.js +385 -0
  65. package/dist/mcp/server.js +170 -0
  66. package/dist/node/config.js +195 -0
  67. package/dist/node/init.js +200 -0
  68. package/dist/node/keys.js +95 -0
  69. package/dist/node/migrate.js +173 -0
  70. package/dist/node/model/anthropic.js +97 -0
  71. package/dist/node/model/stub.js +129 -0
  72. package/dist/node/model/types.js +9 -0
  73. package/dist/node/node.js +198 -0
  74. package/dist/node/policy.js +95 -0
  75. package/dist/node/runner.js +256 -0
  76. package/dist/node/watchers.js +88 -0
  77. package/dist/tools/admin.js +184 -0
  78. package/dist/tools/catalog.js +261 -0
  79. package/dist/tools/common.js +143 -0
  80. package/dist/tools/identity.js +107 -0
  81. package/dist/tools/index.js +14 -0
  82. package/dist/tools/read.js +471 -0
  83. package/dist/tools/tenders.js +400 -0
  84. package/dist/tools/types.js +4 -0
  85. package/dist/util/bytecode.js +123 -0
  86. package/dist/util/fs.js +58 -0
  87. package/dist/util/hash.js +4 -0
  88. package/dist/util/log.js +11 -0
  89. package/dist/util/version.js +12 -0
  90. package/dist/world/deploy.js +277 -0
  91. package/dist/world/plan.js +93 -0
  92. package/dist/world/types.js +1 -0
  93. package/docs/build.md +45 -0
  94. package/docs/cell-account.md +58 -0
  95. package/docs/compatibility-matrix.md +38 -0
  96. package/docs/design-memo.md +113 -0
  97. package/docs/doctor.md +53 -0
  98. package/docs/examples/README.md +11 -0
  99. package/docs/examples/claude-code-mcp.json +13 -0
  100. package/docs/examples/claude_desktop_config.json +12 -0
  101. package/docs/examples/cursor-mcp.json +11 -0
  102. package/docs/genesis.md +312 -0
  103. package/docs/kernel.md +101 -0
  104. package/docs/loop-d.md +90 -0
  105. package/docs/mcp.md +105 -0
  106. package/docs/node.md +66 -0
  107. package/docs/version-lock.md +67 -0
  108. package/docs/world.md +50 -0
  109. package/docs/zk.md +48 -0
  110. package/package.json +82 -4
  111. package/prover/README.md +26 -0
  112. package/prover/build-predicate.mjs +77 -0
  113. package/prover/package-lock.json +1837 -0
  114. package/prover/package.json +15 -0
  115. package/prover/run.cjs +199 -0
  116. package/scenarios/fixtures/sample-filing.txt +16 -0
  117. package/scenarios/skills/finch-extract-verify/LICENSE +2 -0
  118. package/scenarios/skills/finch-extract-verify/SKILL.md +34 -0
  119. package/scenarios/skills/finch-extract-verify/fields.json +56 -0
  120. package/scenarios/skills/finch-extract-verify/manifest.json +16 -0
  121. package/scenarios/skills/foga-probe/LICENSE +2 -0
  122. package/scenarios/skills/foga-probe/SKILL.md +35 -0
  123. package/scenarios/skills/foga-probe/manifest.json +16 -0
  124. package/scenarios/skills/foga-probe/probe.schema.json +40 -0
  125. package/server.json +79 -0
  126. package/vendor/LOCK.json +107 -0
  127. package/vendor/README.md +13 -0
  128. package/worlds/local.json +34 -0
  129. package/worlds/sepolia.json +314 -0
  130. package/zk/build/verification_key.json +174 -0
  131. package/zk/circuits/kya_predicate_count_v1.circom +231 -0
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "foga-prover",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "description": "Proving-side dependencies of the ERC-8419 credential-pattern ZK path (kya-public-v1), kept apart from the kernel's own dependencies",
6
+ "license": "Apache-2.0",
7
+ "dependencies": {
8
+ "circomlibjs": "0.1.7",
9
+ "snarkjs": "0.7.6"
10
+ },
11
+ "devDependencies": {
12
+ "circom2": "^0.2.23",
13
+ "circomlib": "^2.0.5"
14
+ }
15
+ }
package/prover/run.cjs ADDED
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env node
2
+ // Bridge between the kernel and the upstream prover of kya-standard (companions/zk-kya-groth16/prover.js):
3
+ // reads one JSON job on stdin, writes one JSON result on stdout. The upstream module and its dependencies
4
+ // (snarkjs, circomlibjs, ethers) are resolved from this directory's node_modules, which `npm run zk:install`
5
+ // populates; the kernel's own dependency tree stays free of them.
6
+ //
7
+ // Jobs:
8
+ // { "op": "root", "attestors": [<32-byte hex private key>, ...] }
9
+ // -> { "root": bytes32, "attestors": [{ "pub": [hex, hex], "leaf": hex }] }
10
+ // { "op": "commit", "secret": <hex> }
11
+ // -> { "commitment": hex } Poseidon(secret): what the attestor signs
12
+ // { "op": "attest", "attestor": <hex prv>, "claim": {...}, "commitment": <hex> }
13
+ // -> { "sig": { "R8x", "R8y", "S" } } the attestor never sees the secret
14
+ // { "op": "prove", "issuerPubs": [[hex, hex], ...], "attestorPub": [hex, hex], "claim": {...}, "secret": <hex>,
15
+ // "sig": {...}, "epoch": <decimal string>, "admissionDomain": bytes32 }
16
+ // -> { "publicInputs": hex, "proof": hex, "nullifier": bytes32, "publicSignals": [...] }
17
+ // the prover holds the secret and public material only; no attestor private key is involved
18
+ // claim = { subjectKey, schemeId, level, claimDigest, expiresAt }.
19
+ //
20
+ // Predicate-pattern jobs (FOGA's own circuit zk/circuits/kya_predicate_count_v1.circom, built by
21
+ // prover/build-predicate.mjs into zk/build; FOGA_ZK_PREDICATE_DIR overrides the build directory):
22
+ // { "op": "record.sign", "source": <hex prv>, "record": { "subjectKey": bytes32, "taskRef": <hex, 128-bit>,
23
+ // "status": <number>, "timestamp": <number> } }
24
+ // -> { "sig": { "R8x", "R8y", "S" }, "pub": [hex, hex], "message": hex }
25
+ // a source signs one task-completion record about a subject; the same `root` job gives the source set
26
+ // { "op": "predicate.prove", "sourcePubs": [[hex, hex], ...], "sourcePub": [hex, hex], "subjectKey": bytes32,
27
+ // "records": [{ "taskRef", "status", "timestamp", "sig" }], "challenge": bytes32,
28
+ // "t0", "t1", "k": <number>, "schemeId": bytes32, "epoch": <decimal string>,
29
+ // "admissionDomain": bytes32, "secret": <hex> }
30
+ // -> { "publicInputs": hex, "proof": hex, "nullifier": bytes32, "result": 0|1, "count": n,
31
+ // "publicSignals": [...], "verified": bool }
32
+ // records are sorted by taskRef (which must be distinct) and padded to the circuit's N slots
33
+ "use strict";
34
+ const path = require("node:path");
35
+ const Module = require("node:module");
36
+
37
+ const root = __dirname;
38
+ process.env.NODE_PATH = path.join(root, "node_modules") + (process.env.NODE_PATH ? path.delimiter + process.env.NODE_PATH : "");
39
+ Module._initPaths();
40
+
41
+ const upstream = process.env.FOGA_ZK_PROVER || path.join(root, "..", "vendor", "kya-standard", "companions", "zk-kya-groth16", "prover.js");
42
+ const predicateDir = process.env.FOGA_ZK_PREDICATE_DIR || path.join(root, "..", "zk", "build");
43
+ const PREDICATE = { name: "kya_predicate_count_v1", slots: 8, depth: 16 };
44
+ const MASK128 = (1n << 128n) - 1n;
45
+ const split128 = (hex) => {
46
+ const v = BigInt(hex);
47
+ return [v >> 128n, v & MASK128];
48
+ };
49
+
50
+ function toBig(hex) {
51
+ return BigInt(hex);
52
+ }
53
+
54
+ function attestorFromPrv(zk, prvHex) {
55
+ const prv = Buffer.from(prvHex.replace(/^0x/, ""), "hex");
56
+ if (prv.length !== 32) throw new Error("attestor private key must be 32 bytes");
57
+ const pubP = zk.eddsa.prv2pub(prv);
58
+ const pub = [zk.F.toObject(pubP[0]), zk.F.toObject(pubP[1])];
59
+ return { prv, pub, leaf: zk.H(pub[0], pub[1]) };
60
+ }
61
+
62
+ /** The signed message, as the circuit recomputes it, from the secret's Poseidon commitment. */
63
+ function messageFromCommitment(zk, claim, commitment) {
64
+ const split = (hex) => {
65
+ const v = BigInt(hex);
66
+ return [v >> 128n, v & ((1n << 128n) - 1n)];
67
+ };
68
+ const [skHi, skLo] = split(claim.subjectKey);
69
+ const [siHi, siLo] = split(claim.schemeId);
70
+ const [cdHi, cdLo] = split(claim.claimDigest);
71
+ return zk.H(skHi, skLo, siHi, siLo, BigInt(claim.level), cdHi, cdLo, BigInt(claim.expiresAt), toBig(commitment));
72
+ }
73
+
74
+ /** Poseidon(5) over a task-completion record, as the predicate circuit recomputes it. */
75
+ function recordMessage(zk, subjectKey, record) {
76
+ const [skHi, skLo] = split128(subjectKey);
77
+ const taskRef = BigInt(record.taskRef);
78
+ if (taskRef < 0n || taskRef > MASK128) throw new Error("taskRef must be a 128-bit value");
79
+ return zk.H(skHi, skLo, taskRef, BigInt(record.status), BigInt(record.timestamp));
80
+ }
81
+
82
+ async function provePredicate(zk, job) {
83
+ // ethers v6 from the repository root (this sub-package's own tree holds the v5 that circomlibjs depends on)
84
+ const { ethers } = require(require.resolve("ethers", { paths: [path.join(root, "..")] }));
85
+ const snarkjs = require("snarkjs");
86
+ const coder = ethers.AbiCoder.defaultAbiCoder();
87
+ const wasm = path.join(predicateDir, `${PREDICATE.name}_js`, `${PREDICATE.name}.wasm`);
88
+ const zkey = path.join(predicateDir, `${PREDICATE.name}_final.zkey`);
89
+ const vkeyPath = path.join(predicateDir, "verification_key.json");
90
+ for (const f of [wasm, zkey]) if (!require("node:fs").existsSync(f)) throw new Error(`predicate proving material missing: ${f} (run: node prover/build-predicate.mjs)`);
91
+
92
+ const pubs = job.sourcePubs.map(([x, y]) => [toBig(x), toBig(y)]);
93
+ const set = zk.issuerSet(pubs); // the source set: the same Poseidon Merkle tree (depth 16) as the issuer set
94
+ const sourcePub = [toBig(job.sourcePub[0]), toBig(job.sourcePub[1])];
95
+ const leaf = zk.H(sourcePub[0], sourcePub[1]);
96
+ const idx = set.leaves.indexOf(leaf);
97
+ if (idx < 0) throw new Error("source not in the source set");
98
+ const { pathElements, pathIndices } = set.proofFor(idx);
99
+
100
+ const records = job.records.map((r) => ({ taskRef: BigInt(r.taskRef), status: BigInt(r.status), timestamp: BigInt(r.timestamp), sig: r.sig }));
101
+ records.sort((a, b) => (a.taskRef < b.taskRef ? -1 : a.taskRef > b.taskRef ? 1 : 0));
102
+ for (let i = 1; i < records.length; i++) if (records[i].taskRef === records[i - 1].taskRef) throw new Error("records must have distinct taskRefs");
103
+ if (records.length > PREDICATE.slots) throw new Error(`at most ${PREDICATE.slots} records per proof`);
104
+ const n = PREDICATE.slots;
105
+ const slot = (f, pad) => Array.from({ length: n }, (_, i) => (i < records.length ? f(records[i]) : pad));
106
+
107
+ const [skHi, skLo] = split128(job.subjectKey);
108
+ const [chHi, chLo] = split128(job.challenge);
109
+ const [rtHi, rtLo] = split128(set.rootHex);
110
+ const [siHi, siLo] = split128(job.schemeId);
111
+ const [adHi, adLo] = split128(job.admissionDomain);
112
+ const t0 = BigInt(job.t0);
113
+ const t1 = BigInt(job.t1);
114
+ const k = BigInt(job.k);
115
+ const epoch = BigInt(job.epoch);
116
+ const input = {
117
+ subjectKeyHi: skHi, subjectKeyLo: skLo, challengeHi: chHi, challengeLo: chLo, t0, t1, k,
118
+ sourceSetRootHi: rtHi, sourceSetRootLo: rtLo, schemeIdHi: siHi, schemeIdLo: siLo, epoch,
119
+ admissionDomainHi: adHi, admissionDomainLo: adLo,
120
+ secret: toBig(job.secret), Ax: sourcePub[0], Ay: sourcePub[1], pathElements, pathIndices,
121
+ present: slot(() => 1n, 0n),
122
+ taskRef: slot((r) => r.taskRef, 0n),
123
+ status: slot((r) => r.status, 0n),
124
+ timestamp: slot((r) => r.timestamp, 0n),
125
+ S: slot((r) => BigInt(r.sig.S), 0n),
126
+ R8x: slot((r) => toBig(r.sig.R8x), 0n),
127
+ R8y: slot((r) => toBig(r.sig.R8y), 0n),
128
+ };
129
+ const { proof, publicSignals } = await snarkjs.groth16.fullProve(input, wasm, zkey);
130
+ const nullifier = ethers.toBeHex((BigInt(publicSignals[0]) << 128n) | BigInt(publicSignals[1]), 32);
131
+ const result = Number(publicSignals[2]);
132
+ const count = records.filter((r) => r.status === 1n && r.timestamp >= t0 && r.timestamp <= t1).length;
133
+ const publicInputs = coder.encode(
134
+ ["bytes32", "bytes32", "bytes32", "uint64", "uint64", "uint16", "bytes32", "uint64", "uint8"],
135
+ [job.subjectKey, nullifier, job.challenge, t0, t1, k, set.rootHex, epoch, result],
136
+ );
137
+ const proofBytes = coder.encode(
138
+ ["uint256[2]", "uint256[2][2]", "uint256[2]"],
139
+ [[proof.pi_a[0], proof.pi_a[1]], [[proof.pi_b[0][1], proof.pi_b[0][0]], [proof.pi_b[1][1], proof.pi_b[1][0]]], [proof.pi_c[0], proof.pi_c[1]]],
140
+ );
141
+ let verified = null;
142
+ if (require("node:fs").existsSync(vkeyPath)) verified = await snarkjs.groth16.verify(JSON.parse(require("node:fs").readFileSync(vkeyPath, "utf8")), publicSignals, proof);
143
+ return { publicInputs, proof: proofBytes, nullifier, result, count, publicSignals: publicSignals.map(String), verified };
144
+ }
145
+
146
+ async function main() {
147
+ const chunks = [];
148
+ for await (const c of process.stdin) chunks.push(Buffer.from(c));
149
+ const job = JSON.parse(Buffer.concat(chunks).toString("utf8"));
150
+ const { ZK } = require(upstream);
151
+ const zk = await ZK.create();
152
+ const hex32 = (v) => "0x" + BigInt(v).toString(16).padStart(64, "0");
153
+
154
+ let out;
155
+ if (job.op === "root") {
156
+ const atts = job.attestors.map((p) => attestorFromPrv(zk, p));
157
+ const set = zk.issuerSet(atts.map((a) => a.pub));
158
+ out = { root: set.rootHex, attestors: atts.map((a) => ({ pub: [hex32(a.pub[0]), hex32(a.pub[1])], leaf: hex32(a.leaf) })) };
159
+ } else if (job.op === "commit") {
160
+ out = { commitment: hex32(zk.H(toBig(job.secret))) };
161
+ } else if (job.op === "attest") {
162
+ const attestor = attestorFromPrv(zk, job.attestor);
163
+ const m = zk.F.e(messageFromCommitment(zk, job.claim, job.commitment));
164
+ const s = zk.eddsa.signPoseidon(attestor.prv, m);
165
+ out = { sig: { R8x: hex32(zk.F.toObject(s.R8[0])), R8y: hex32(zk.F.toObject(s.R8[1])), S: s.S.toString() } };
166
+ } else if (job.op === "prove") {
167
+ const pubs = job.issuerPubs.map(([x, y]) => [toBig(x), toBig(y)]);
168
+ const set = zk.issuerSet(pubs);
169
+ const pub = [toBig(job.attestorPub[0]), toBig(job.attestorPub[1])];
170
+ const attestor = { pub, leaf: zk.H(pub[0], pub[1]) };
171
+ const sig = { R8x: toBig(job.sig.R8x), R8y: toBig(job.sig.R8y), S: BigInt(job.sig.S) };
172
+ const r = await zk.prove({
173
+ ...job.claim,
174
+ epoch: BigInt(job.epoch),
175
+ admissionDomain: job.admissionDomain,
176
+ secret: toBig(job.secret),
177
+ attestor,
178
+ sig,
179
+ set,
180
+ });
181
+ out = { publicInputs: r.publicInputs, proof: r.proof, nullifier: r.nullifier, publicSignals: r.publicSignals.map(String) };
182
+ } else if (job.op === "record.sign") {
183
+ const source = attestorFromPrv(zk, job.source);
184
+ const m = recordMessage(zk, job.record.subjectKey, job.record);
185
+ const sg = zk.eddsa.signPoseidon(source.prv, zk.F.e(m));
186
+ out = { sig: { R8x: hex32(zk.F.toObject(sg.R8[0])), R8y: hex32(zk.F.toObject(sg.R8[1])), S: sg.S.toString() }, pub: [hex32(source.pub[0]), hex32(source.pub[1])], message: hex32(m) };
187
+ } else if (job.op === "predicate.prove") {
188
+ out = await provePredicate(zk, job);
189
+ } else {
190
+ throw new Error(`unknown op ${job.op}`);
191
+ }
192
+ process.stdout.write(JSON.stringify(out));
193
+ process.exit(0);
194
+ }
195
+
196
+ main().catch((e) => {
197
+ process.stderr.write(String(e && e.stack ? e.stack : e));
198
+ process.exit(1);
199
+ });
@@ -0,0 +1,16 @@
1
+ EXAMPLE HOLDINGS LTD
2
+ Condensed interim financial statement (synthetic fixture for the FOGA genesis; no real company)
3
+
4
+ Document date: 2026-08-15
5
+ Issuer: Example Holdings Ltd
6
+ Period end: 2026-06-30
7
+ Reporting currency: USD thousands
8
+
9
+ BALANCE SHEET
10
+ Total assets: 1,250
11
+ Total liabilities: 800
12
+ Total equity: 450
13
+
14
+ NOTES
15
+ 1. The figures above are invented for a software test and carry no financial meaning.
16
+ 2. Total equity equals total assets less total liabilities by construction.
@@ -0,0 +1,2 @@
1
+ Apache License 2.0. This skill package is part of the Finch OG Agent repository; the full license text is the
2
+ repository's LICENSE file.
@@ -0,0 +1,34 @@
1
+ # finch-extract-verify
2
+
3
+ Extract named fields from a fixed-version public document and verify their mutual consistency.
4
+
5
+ ## What the skill does
6
+
7
+ The input is one document pinned by content hash (a filing, a published report, a specification) and a field
8
+ list (`fields.json`). The skill extracts each field as it appears in the document, records where it was found,
9
+ checks the consistency rules the field list declares (totals that must add up, dates that must be ordered,
10
+ identifiers that must match across sections) and produces a deliverable with the extracted values, the
11
+ evidence spans and the verdict of every rule.
12
+
13
+ This is the first business task of a FOGA cell: structured extraction plus consistency checking over a fixed
14
+ version of public material, with the output judged by a reader or by a council (ERC-8436), not by a hash.
15
+
16
+ ## Inputs
17
+
18
+ | name | type | meaning |
19
+ |-------------|--------|----------------------------------------------------------------------------|
20
+ | document | bytes | the pinned document; its sha256 must equal `documentDigest` |
21
+ | documentDigest | string | `sha256:<hex>` of the document |
22
+ | fields | object | the field list and rules (`fields.json` by default) |
23
+
24
+ ## Output
25
+
26
+ A JSON deliverable: `{ "schema": "finch.extract-verify/1", "documentDigest", "fields": { name: { "value",
27
+ "evidence": [{ "page", "quote" }] } }, "rules": [{ "id", "verdict": "pass" | "fail" | "unknown", "note" }] }`.
28
+
29
+ ## Rules
30
+
31
+ 1. Values are copied, never computed, unless a rule says a derived value is expected.
32
+ 2. Every value carries at least one evidence span.
33
+ 3. A field that cannot be found yields `null` and the rules that depend on it yield `unknown`.
34
+ 4. The deliverable states the document digest it was produced from.
@@ -0,0 +1,56 @@
1
+ {
2
+ "schema": "finch.extract-verify.fields/1",
3
+ "fields": [
4
+ {
5
+ "name": "issuer",
6
+ "kind": "text",
7
+ "label": "Issuer",
8
+ "where": "cover page"
9
+ },
10
+ {
11
+ "name": "documentDate",
12
+ "kind": "date",
13
+ "label": "Document date",
14
+ "where": "cover page"
15
+ },
16
+ {
17
+ "name": "periodEnd",
18
+ "kind": "date",
19
+ "label": "Period end",
20
+ "where": "cover page"
21
+ },
22
+ {
23
+ "name": "totalAssets",
24
+ "kind": "amount",
25
+ "label": "Total assets",
26
+ "where": "balance sheet"
27
+ },
28
+ {
29
+ "name": "totalLiabilities",
30
+ "kind": "amount",
31
+ "label": "Total liabilities",
32
+ "where": "balance sheet"
33
+ },
34
+ {
35
+ "name": "totalEquity",
36
+ "kind": "amount",
37
+ "label": "Total equity",
38
+ "where": "balance sheet"
39
+ }
40
+ ],
41
+ "rules": [
42
+ {
43
+ "id": "balance",
44
+ "check": "totalAssets == totalLiabilities + totalEquity",
45
+ "tolerance": "1"
46
+ },
47
+ {
48
+ "id": "period",
49
+ "check": "periodEnd is a calendar date not after documentDate"
50
+ }
51
+ ],
52
+ "extraction": {
53
+ "method": "line-label",
54
+ "description": "A field's value is the text after `<label>:` on the first line that starts with the label; amounts drop thousands separators; dates are ISO 8601."
55
+ }
56
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "schemaVersion": "1.0",
3
+ "name": "finch-extract-verify",
4
+ "skillVersion": "1.0.0",
5
+ "summary": "Extract named fields from a fixed-version public document and verify their mutual consistency",
6
+ "entrypoint": "SKILL.md",
7
+ "skillType": "business",
8
+ "scopes": ["read:document"],
9
+ "standard": "ERC-8338",
10
+ "x-entry-profile": "llm-markdown-v1",
11
+ "x-foga": {
12
+ "runner": "foga.kernel.llm/1",
13
+ "deterministic": false,
14
+ "fields": "fields.json"
15
+ }
16
+ }
@@ -0,0 +1,2 @@
1
+ Apache License 2.0. This skill package is part of the Finch OG Agent repository; the full license text is the
2
+ repository's LICENSE file.
@@ -0,0 +1,35 @@
1
+ # foga-probe
2
+
3
+ Probe one address against the ten-ERC agent stack at one block and produce a canonical report.
4
+
5
+ ## What the skill does
6
+
7
+ Given a subject address, a block number and the world's address book, read — at that block and only through
8
+ view calls — what each of the ten registries says about the subject, and emit one JSON document in RFC 8785
9
+ canonical form. The report is deterministic: two independent runs against the same chain, block and inputs
10
+ produce byte-identical output. That is what makes it usable as the known answer of a hashlock tender
11
+ (ERC-8414, profile x-hashlock-v1): the demander runs it first and commits `sha256(report)`; a fulfiller runs it
12
+ again and settles by revealing the same bytes.
13
+
14
+ ## Inputs
15
+
16
+ | name | type | meaning |
17
+ |-----------|----------------------|---------------------------------------------------------------------|
18
+ | subject | address | the account to probe |
19
+ | block | integer | the block at which every read is made |
20
+ | lookback | integer (optional) | how many blocks before `block` the log-based sections scan (from `block - lookback`, at least 0); absent, from the chain's first block. Public endpoints cap the range of a log query, so a task on such a chain names one |
21
+ | kya | object | `{ "schemeId": bytes32, "issuers": [address] }` — the standing scheme and the issuers the report trusts |
22
+ | world | string | the world id (address book) the probe reads |
23
+
24
+ ## Output
25
+
26
+ `foga.probe/1` — see `probe.schema.json`. Sections, one per standard: erc8004, erc8434, erc8419, erc8468,
27
+ erc8444, erc8454, erc8338, erc8414, erc8447, erc8436, erc8471. Every number is a decimal string, every address
28
+ is lower-case, the block hash is included so a report is bound to one chain history.
29
+
30
+ ## Rules
31
+
32
+ 1. Read at `block`; never at `latest`. Logs are read from `block - lookback` to `block`.
33
+ 2. Never write. The skill has no transaction.
34
+ 3. No clocks, no randomness, no environment-dependent values in the report.
35
+ 4. A registry that reverts or is absent for the subject yields `null` for that section, never an omission.
@@ -0,0 +1,16 @@
1
+ {
2
+ "schemaVersion": "1.0",
3
+ "name": "foga-probe",
4
+ "skillVersion": "1.1.0",
5
+ "summary": "Probe one address against the ten-ERC agent stack at one block and produce a canonical report",
6
+ "entrypoint": "SKILL.md",
7
+ "skillType": "protocol",
8
+ "scopes": ["read:chain"],
9
+ "standard": "ERC-8338",
10
+ "x-entry-profile": "llm-markdown-v1",
11
+ "x-foga": {
12
+ "runner": "foga.kernel.probe/1",
13
+ "deterministic": true,
14
+ "outputSchema": "probe.schema.json"
15
+ }
16
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://foga.local/schemas/probe.schema.json",
4
+ "title": "foga.probe/1 report",
5
+ "type": "object",
6
+ "required": ["schema", "skill", "world", "subject", "checks"],
7
+ "properties": {
8
+ "schema": { "const": "foga.probe/1" },
9
+ "skill": { "const": "foga-probe" },
10
+ "world": {
11
+ "type": "object",
12
+ "required": ["worldId", "chainId", "block", "blockHash"],
13
+ "properties": {
14
+ "worldId": { "type": "string" },
15
+ "chainId": { "type": "integer" },
16
+ "block": { "type": "integer" },
17
+ "blockHash": { "type": "string" }
18
+ }
19
+ },
20
+ "subject": { "type": "string", "description": "CAIP-10 account reference, lower-case" },
21
+ "checks": {
22
+ "type": "object",
23
+ "required": ["erc8004", "erc8434", "erc8419", "erc8468", "erc8444", "erc8454", "erc8338", "erc8414", "erc8447", "erc8436", "erc8471"],
24
+ "additionalProperties": false,
25
+ "properties": {
26
+ "erc8004": { "type": ["object", "null"] },
27
+ "erc8434": { "type": ["object", "null"] },
28
+ "erc8419": { "type": ["object", "null"] },
29
+ "erc8468": { "type": ["object", "null"] },
30
+ "erc8444": { "type": ["object", "null"] },
31
+ "erc8454": { "type": ["object", "null"] },
32
+ "erc8338": { "type": ["object", "null"] },
33
+ "erc8414": { "type": ["object", "null"] },
34
+ "erc8447": { "type": ["object", "null"] },
35
+ "erc8436": { "type": ["object", "null"] },
36
+ "erc8471": { "type": ["object", "null"] }
37
+ }
38
+ }
39
+ }
40
+ }
package/server.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.garyyang-finchip/foga",
4
+ "title": "FOGA node",
5
+ "description": "A Finch OG Agent cell as an MCP server: read the world, dry-run and take ERC-8414 tenders by policy.",
6
+ "repository": {
7
+ "url": "https://github.com/garyyang-finchip/foga",
8
+ "source": "github"
9
+ },
10
+ "websiteUrl": "https://foga.finchip.ai",
11
+ "version": "0.6.0-alpha.4",
12
+ "packages": [
13
+ {
14
+ "registryType": "npm",
15
+ "registryBaseUrl": "https://registry.npmjs.org",
16
+ "identifier": "@finchip.ai/foga",
17
+ "version": "0.6.0-alpha.4",
18
+ "runtimeHint": "npx",
19
+ "transport": {
20
+ "type": "stdio"
21
+ },
22
+ "packageArguments": [
23
+ {
24
+ "type": "positional",
25
+ "value": "node",
26
+ "description": "the node command group"
27
+ },
28
+ {
29
+ "type": "positional",
30
+ "value": "mcp",
31
+ "description": "MCP over stdio"
32
+ },
33
+ {
34
+ "type": "named",
35
+ "name": "--config",
36
+ "valueHint": "cell.config.json",
37
+ "format": "filepath",
38
+ "isRequired": true,
39
+ "description": "the cell's configuration file (written by foga cell new and foga cell init)"
40
+ },
41
+ {
42
+ "type": "named",
43
+ "name": "--rpc",
44
+ "valueHint": "https://...",
45
+ "isRequired": false,
46
+ "description": "the JSON-RPC endpoint of the world's chain, when the configuration names none"
47
+ }
48
+ ],
49
+ "environmentVariables": [
50
+ {
51
+ "name": "FOGA_OPERATOR_KEY_FILE",
52
+ "format": "filepath",
53
+ "isRequired": false,
54
+ "isSecret": false,
55
+ "description": "a file (mode 0600) holding the operator key: with it the server acts for the cell; without it, reads, dry runs and builds only"
56
+ },
57
+ {
58
+ "name": "FOGA_RPC_URL",
59
+ "isRequired": false,
60
+ "description": "the JSON-RPC endpoint when neither the configuration nor --rpc names one"
61
+ }
62
+ ]
63
+ }
64
+ ],
65
+ "remotes": [
66
+ {
67
+ "type": "streamable-http",
68
+ "url": "https://foga.finchip.ai/mcp",
69
+ "headers": [
70
+ {
71
+ "name": "Authorization",
72
+ "isRequired": false,
73
+ "isSecret": true,
74
+ "description": "Bearer <api key> for a caller the node's operator granted; without it the public reads and builds"
75
+ }
76
+ ]
77
+ }
78
+ ]
79
+ }
@@ -0,0 +1,107 @@
1
+ {
2
+ "schema": "foga.vendor-lock/1",
3
+ "lockedAt": "2026-10-30",
4
+ "note": "Reference implementations of the ten ERCs, pinned by commit. `foga vendor sync` materializes each source tree under vendor/<repo>/ at exactly this commit; the committed artifacts/bundle.json is built from these commits and CI rebuilds it to prove the hash. Spec references are recorded separately from implementation commits: a Sepolia deployment may predate the implementation commit (see docs/version-lock.md).",
5
+ "sources": [
6
+ {
7
+ "erc": 8338,
8
+ "title": "Token-Bound Executable Skills",
9
+ "repo": "skill-token-standard",
10
+ "url": "https://github.com/garyyang-finchip/skill-token-standard.git",
11
+ "commit": "359ad64aa698c7c206bee9329a9a5ec05f12a685",
12
+ "contractsRoot": "contracts",
13
+ "upstreamToolchain": "foundry (solc 0.8.24, optimizer on, default runs)",
14
+ "spec": { "status": "Draft", "where": "ethereum/ERCs PR #1879 (not in this repository)", "discussion": "https://ethereum-magicians.org/t/29005" }
15
+ },
16
+ {
17
+ "erc": 8414,
18
+ "title": "Token-Bound Task Tenders",
19
+ "repo": "task-token-standard",
20
+ "url": "https://github.com/garyyang-finchip/task-token-standard.git",
21
+ "commit": "b3da62e761b00ef0921860f353d7eed24c6c85ae",
22
+ "contractsRoot": "contracts",
23
+ "upstreamToolchain": "foundry (solc 0.8.24, optimizer runs 1)",
24
+ "spec": { "status": "Draft", "where": "ERCS/erc-9999.md in this repository (placeholder file name); ethereum/ERCs PR #2005", "discussion": "https://ethereum-magicians.org/t/29597" }
25
+ },
26
+ {
27
+ "erc": 8419,
28
+ "title": "Know-Your-Agent (KYA) Framework",
29
+ "repo": "kya-standard",
30
+ "url": "https://github.com/garyyang-finchip/kya-standard.git",
31
+ "commit": "f204f2ec2115bd8d1a96de60e0e844dee31cfe1b",
32
+ "contractsRoot": "assets/erc-kya/contracts",
33
+ "upstreamToolchain": "solc-js via tools/compile.js (solc 0.8.28, optimizer runs 200, evm cancun)",
34
+ "spec": { "status": "Draft", "where": "ERCS/erc-kya.md in this repository; ethereum/ERCs PR #2012", "discussion": "https://ethereum-magicians.org/t/29735" }
35
+ },
36
+ {
37
+ "erc": 8434,
38
+ "title": "Agent Identity (AID)",
39
+ "repo": "aid-standard",
40
+ "url": "https://github.com/garyyang-finchip/aid-standard.git",
41
+ "commit": "1ff7215505af0f4699d437744402bd1e1b4dd043",
42
+ "contractsRoot": "assets/erc-aid/contracts",
43
+ "upstreamToolchain": "solc-js via scripts/compile.js (solc 0.8.28, optimizer runs 200, evm cancun)",
44
+ "spec": { "status": "Draft", "where": "ERCS/erc-aid.md in this repository; ethereum/ERCs PR #2044", "discussion": "https://ethereum-magicians.org/t/29805" }
45
+ },
46
+ {
47
+ "erc": 8436,
48
+ "title": "Agent Collective Decision Framework (ACDF)",
49
+ "repo": "acd-framework",
50
+ "url": "https://github.com/garyyang-finchip/acd-framework.git",
51
+ "commit": "f8204fb9c38d47d619b9d8bc81441dfe9e8e7f91",
52
+ "contractsRoot": "assets/erc-acdf/contracts",
53
+ "upstreamToolchain": "foundry (solc 0.8.24, optimizer runs 1, via-IR)",
54
+ "spec": { "status": "Draft (merged into ethereum/ERCs 2026-10-14)", "where": "https://eips.ethereum.org/EIPS/eip-8436", "discussion": "https://ethereum-magicians.org/t/29850" }
55
+ },
56
+ {
57
+ "erc": 8444,
58
+ "title": "Agentic Resource Discovery (ARD) Onchain",
59
+ "repo": "ard-onchain-framework",
60
+ "url": "https://github.com/garyyang-finchip/ard-onchain-framework.git",
61
+ "commit": "26416854e489564fc32c4a69cf9c176b703dc089",
62
+ "contractsRoot": "assets/erc-8444/contracts",
63
+ "upstreamToolchain": "foundry (solc 0.8.24, optimizer runs 200)",
64
+ "spec": { "status": "Draft (merged into ethereum/ERCs 2026-10-15)", "where": "https://eips.ethereum.org/ERCS/erc-8444", "discussion": "https://ethereum-magicians.org/t/29963" }
65
+ },
66
+ {
67
+ "erc": 8447,
68
+ "title": "Swarm AI Framework (SAI)",
69
+ "repo": "swarm-ai-framework",
70
+ "url": "https://github.com/garyyang-finchip/swarm-ai-framework.git",
71
+ "commit": "de5bc291ab93bca0163bec47caa66136d1cf3f6a",
72
+ "contractsRoot": "assets/erc-8447/contracts",
73
+ "upstreamToolchain": "foundry (solc 0.8.24, optimizer runs 1, via-IR, external libraries)",
74
+ "spec": { "status": "Draft", "where": "ERCS/erc-8447.md in this repository; ethereum/ERCs PR #2081", "discussion": "https://ethereum-magicians.org/t/29998" }
75
+ },
76
+ {
77
+ "erc": 8454,
78
+ "title": "AIFi Unit Framework (AUF)",
79
+ "repo": "aifi-unit-framework",
80
+ "url": "https://github.com/garyyang-finchip/aifi-unit-framework.git",
81
+ "commit": "1be1da69811875bae0a6fd7b10152b4831e5538f",
82
+ "contractsRoot": "assets/erc-auf/contracts",
83
+ "upstreamToolchain": "foundry (solc 0.8.30, optimizer runs 200, evm cancun, bytecode_hash none)",
84
+ "spec": { "status": "Draft", "where": "ERCS/erc-auf.md in this repository; ethereum/ERCs PR #2095", "discussion": "https://ethereum-magicians.org/t/30040" }
85
+ },
86
+ {
87
+ "erc": 8468,
88
+ "title": "Agent Safety Framework (ASF)",
89
+ "repo": "agent-safety-framework",
90
+ "url": "https://github.com/garyyang-finchip/agent-safety-framework.git",
91
+ "commit": "a76071c0e0ab76c296a0eb878dee1365ce5a78bc",
92
+ "contractsRoot": "assets/erc-asf/contracts",
93
+ "upstreamToolchain": "foundry (solc 0.8.30, optimizer runs 200, via-IR, evm cancun, bytecode_hash none, external libraries)",
94
+ "spec": { "status": "Draft", "where": "ERCS/erc-asf.md in this repository; ethereum/ERCs PR #2113", "discussion": "https://ethereum-magicians.org/t/30073" }
95
+ },
96
+ {
97
+ "erc": 8471,
98
+ "title": "AI Dispute Resolution Framework (ADRF)",
99
+ "repo": "ai-dispute-resolution",
100
+ "url": "https://github.com/garyyang-finchip/ai-dispute-resolution.git",
101
+ "commit": "cd5a3e076d648ff5c29d87ab023b22398e0ab17a",
102
+ "contractsRoot": "assets/erc-adrf/contracts",
103
+ "upstreamToolchain": "foundry (solc 0.8.30, optimizer runs 200, via-IR, evm cancun, bytecode_hash none)",
104
+ "spec": { "status": "Draft", "where": "ERCS/erc-adrf.md in this repository; ethereum/ERCs PR #2123", "discussion": "https://ethereum-magicians.org/t/30121" }
105
+ }
106
+ ]
107
+ }
@@ -0,0 +1,13 @@
1
+ # vendor/
2
+
3
+ This directory holds the pinned upstream sources of the ten reference implementations.
4
+ Only `LOCK.json` is committed. Run `npm run vendor:sync` (or `foga vendor sync`) to
5
+ materialize `vendor/<repo>/` at exactly the locked commits; the directories are
6
+ gitignored so the FOGA repository itself stays small and uploadable by hand.
7
+
8
+ The sources are needed only to (re)build `artifacts/bundle.json`. Deploying a world,
9
+ running the doctor and (later) running cells all work from the committed bundle.
10
+
11
+ The upstream repositories are CC0-1.0 (with Apache-2.0 vendored ARD material inside
12
+ ard-onchain-framework). FOGA does not modify them; changes it needs go back upstream as
13
+ separate pull requests.