@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,130 @@
1
+ import { sha256Bytes } from "../canonical.js";
2
+ /**
3
+ * The deterministic DAG-CBOR encoder of ERC-8338 / ERC-8414 packages, ported from the reference
4
+ * `tools/skill-pack/som.py` (and the identical `tools/task-pack/tom.py`):
5
+ * - definite lengths only, shortest-form integer heads;
6
+ * - map keys MUST be strings and are sorted by the bytes of their ENCODED form;
7
+ * - links are CBOR tag 42 over a byte string of 0x00 (identity multibase) + the binary CID;
8
+ * - CIDs are CIDv1 with sha2-256 multihash: 0x01 <codec varint> 0x12 0x20 <32-byte digest>;
9
+ * - no floats, booleans, null or other tags.
10
+ */
11
+ export const RAW_CODEC = 0x55;
12
+ export const DAG_CBOR_CODEC = 0x71;
13
+ export class Link {
14
+ cid;
15
+ constructor(cid) {
16
+ this.cid = cid;
17
+ if (cid.length !== 36)
18
+ throw new Error("Link: binary CID must be 36 bytes");
19
+ }
20
+ }
21
+ function head(major, arg) {
22
+ if (arg < 24n)
23
+ return Uint8Array.of((major << 5) | Number(arg));
24
+ const sizes = [
25
+ [24, 1],
26
+ [25, 2],
27
+ [26, 4],
28
+ [27, 8],
29
+ ];
30
+ for (const [ai, size] of sizes) {
31
+ if (arg < 1n << BigInt(8 * size)) {
32
+ const out = new Uint8Array(1 + size);
33
+ out[0] = (major << 5) | ai;
34
+ let v = arg;
35
+ for (let i = size; i >= 1; i--) {
36
+ out[i] = Number(v & 0xffn);
37
+ v >>= 8n;
38
+ }
39
+ return out;
40
+ }
41
+ }
42
+ throw new Error("integer too large");
43
+ }
44
+ function concat(parts) {
45
+ const total = parts.reduce((n, p) => n + p.length, 0);
46
+ const out = new Uint8Array(total);
47
+ let o = 0;
48
+ for (const p of parts) {
49
+ out.set(p, o);
50
+ o += p.length;
51
+ }
52
+ return out;
53
+ }
54
+ function compareBytes(a, b) {
55
+ const n = Math.min(a.length, b.length);
56
+ for (let i = 0; i < n; i++)
57
+ if (a[i] !== b[i])
58
+ return a[i] - b[i];
59
+ return a.length - b.length;
60
+ }
61
+ export function encode(obj) {
62
+ if (obj instanceof Link) {
63
+ const body = new Uint8Array(1 + obj.cid.length);
64
+ body[0] = 0x00;
65
+ body.set(obj.cid, 1);
66
+ return concat([head(6, 42n), encode(body)]);
67
+ }
68
+ if (obj instanceof Uint8Array)
69
+ return concat([head(2, BigInt(obj.length)), obj]);
70
+ if (typeof obj === "string") {
71
+ const raw = Buffer.from(obj, "utf8");
72
+ return concat([head(3, BigInt(raw.length)), raw]);
73
+ }
74
+ if (typeof obj === "boolean")
75
+ throw new TypeError("booleans are not supported in SkillRoot");
76
+ if (typeof obj === "number" || typeof obj === "bigint") {
77
+ const v = BigInt(obj);
78
+ if (typeof obj === "number" && !Number.isInteger(obj))
79
+ throw new TypeError("floats are not supported in SkillRoot");
80
+ return v >= 0n ? head(0, v) : head(1, -1n - v);
81
+ }
82
+ if (Array.isArray(obj))
83
+ return concat([head(4, BigInt(obj.length)), ...obj.map(encode)]);
84
+ if (obj && typeof obj === "object") {
85
+ const items = Object.entries(obj).map(([k, v]) => [encode(k), encode(v)]);
86
+ items.sort((x, y) => compareBytes(x[0], y[0]));
87
+ return concat([head(5, BigInt(items.length)), ...items.flatMap(([k, v]) => [k, v])]);
88
+ }
89
+ throw new TypeError(`unsupported type in SkillRoot: ${typeof obj}`);
90
+ }
91
+ export function makeCid(digest, codec) {
92
+ if (digest.length !== 32)
93
+ throw new Error("makeCid: sha2-256 digest expected");
94
+ if (codec >= 128)
95
+ throw new Error("makeCid: codec varint > 1 byte not needed here");
96
+ const out = new Uint8Array(36);
97
+ out[0] = 0x01;
98
+ out[1] = codec;
99
+ out[2] = 0x12;
100
+ out[3] = 0x20;
101
+ out.set(digest, 4);
102
+ return out;
103
+ }
104
+ export function leafLink(content) {
105
+ return new Link(makeCid(sha256Bytes(content), RAW_CODEC));
106
+ }
107
+ export function dagLink(digest) {
108
+ return new Link(makeCid(digest, DAG_CBOR_CODEC));
109
+ }
110
+ /** "b" + lowercase base32 (RFC 4648, no padding) of the binary CID. */
111
+ export function cidString(cid) {
112
+ const alphabet = "abcdefghijklmnopqrstuvwxyz234567";
113
+ let bits = 0;
114
+ let value = 0;
115
+ let out = "b";
116
+ for (const byte of cid) {
117
+ value = (value << 8) | byte;
118
+ bits += 8;
119
+ while (bits >= 5) {
120
+ out += alphabet[(value >>> (bits - 5)) & 31];
121
+ bits -= 5;
122
+ }
123
+ }
124
+ if (bits > 0)
125
+ out += alphabet[(value << (5 - bits)) & 31];
126
+ return out;
127
+ }
128
+ export function cidDigestHex(cid) {
129
+ return "0x" + Buffer.from(cid.subarray(4)).toString("hex");
130
+ }
@@ -0,0 +1,142 @@
1
+ import { readdirSync, readFileSync, statSync } from "node:fs";
2
+ import { join, posix, relative } from "node:path";
3
+ import { sha256Hex, sha256Bytes } from "../canonical.js";
4
+ import { cidDigestHex, dagLink, encode, leafLink } from "./cbor.js";
5
+ const LICENSE_NAMES = new Set(["license", "LICENSE", "license.json"]);
6
+ function validatePath(p) {
7
+ if (!p || p.startsWith("/") || p.includes("\\"))
8
+ throw new Error(`invalid package path: ${p}`);
9
+ for (const seg of p.split("/"))
10
+ if (seg === "" || seg === "." || seg === "..")
11
+ throw new Error(`invalid package path: ${p}`);
12
+ }
13
+ /** Read a directory into a path -> bytes map (POSIX relative paths, sorted). */
14
+ export function readSkillDir(dir) {
15
+ const files = {};
16
+ const walk = (d) => {
17
+ for (const name of readdirSync(d).sort()) {
18
+ const p = join(d, name);
19
+ const st = statSync(p);
20
+ if (st.isDirectory())
21
+ walk(p);
22
+ else
23
+ files[posix.normalize(relative(dir, p).split("\\").join("/"))] = new Uint8Array(readFileSync(p));
24
+ }
25
+ };
26
+ walk(dir);
27
+ return files;
28
+ }
29
+ export function detectPrimary(files) {
30
+ if (files["SKILL.md"])
31
+ return "SKILL.md";
32
+ if (files["README.md"])
33
+ return "README.md";
34
+ const mds = Object.keys(files).filter((p) => !p.includes("/") && /\.(md|markdown)$/i.test(p));
35
+ if (mds.length === 1)
36
+ return mds[0];
37
+ throw new Error("cannot detect the primary document; pass primaryPath");
38
+ }
39
+ export function buildSkillPackage(files, opts = {}) {
40
+ for (const p of Object.keys(files))
41
+ validatePath(p);
42
+ const primaryPath = opts.primaryPath ?? detectPrimary(files);
43
+ const md = files[primaryPath];
44
+ if (!md)
45
+ throw new Error(`primary document ${primaryPath} missing`);
46
+ Buffer.from(md).toString("utf8"); // must be text
47
+ let manifestBytes = files["manifest.json"];
48
+ let manifest;
49
+ if (manifestBytes) {
50
+ manifest = JSON.parse(Buffer.from(manifestBytes).toString("utf8"));
51
+ }
52
+ else {
53
+ const firstLine = Buffer.from(md).toString("utf8").split("\n")[0].replace(/^#+\s*/, "").trim();
54
+ manifest = { schemaVersion: "1.0", name: "skill", skillVersion: "0.0.1", summary: firstLine, entrypoint: primaryPath };
55
+ manifestBytes = new Uint8Array(Buffer.from(JSON.stringify(manifest, null, 2)));
56
+ }
57
+ for (const k of ["schemaVersion", "name", "skillVersion", "summary", "entrypoint"]) {
58
+ if (!(k in manifest))
59
+ throw new Error(`manifest.json lacks required field ${k}`);
60
+ }
61
+ const entryPath = manifest.entrypoint;
62
+ validatePath(entryPath);
63
+ const profile = typeof manifest["x-entry-profile"] === "string" ? manifest["x-entry-profile"] : "llm-markdown-v1";
64
+ const leaves = {};
65
+ const objects = {};
66
+ const leafOf = (path, bytes) => {
67
+ const hex = sha256Hex(bytes);
68
+ leaves[path] = hex;
69
+ objects[hex.slice(2)] = bytes;
70
+ return leafLink(bytes);
71
+ };
72
+ const mdLink = leafOf(primaryPath, md);
73
+ const manifestLink = leafOf("manifest.json", manifestBytes);
74
+ let license;
75
+ const fileLinks = {};
76
+ for (const [path, bytes] of Object.entries(files)) {
77
+ if (path === primaryPath || path === "manifest.json")
78
+ continue;
79
+ if (!path.includes("/") && LICENSE_NAMES.has(path)) {
80
+ license = { path, cid: leafOf(path, bytes) };
81
+ continue;
82
+ }
83
+ fileLinks[path] = leafOf(path, bytes);
84
+ }
85
+ let entryCid;
86
+ if (entryPath === primaryPath)
87
+ entryCid = mdLink;
88
+ else if (fileLinks[entryPath])
89
+ entryCid = fileLinks[entryPath];
90
+ else
91
+ throw new Error(`entrypoint ${entryPath} is not a file of the package`);
92
+ const version = opts.version ?? 1;
93
+ const root = {
94
+ md: { path: primaryPath, cid: mdLink },
95
+ manifest: manifestLink,
96
+ entry: { path: entryPath, cid: entryCid, profile },
97
+ files: fileLinks,
98
+ };
99
+ if (license)
100
+ root.license = { path: license.path, cid: license.cid };
101
+ if (version > 1) {
102
+ if (!opts.previousPackageHash)
103
+ throw new Error("version > 1 needs previousPackageHash");
104
+ root.prev = dagLink(Uint8Array.from(Buffer.from(opts.previousPackageHash.replace(/^0x/, ""), "hex")));
105
+ }
106
+ else if (opts.previousPackageHash) {
107
+ throw new Error("version 1 must not carry prev");
108
+ }
109
+ const rootBytes = encode(root);
110
+ const packageHash = sha256Hex(rootBytes);
111
+ const rootCid = cidDigestHex(dagLink(sha256Bytes(rootBytes)).cid);
112
+ return { mdHash: sha256Hex(md), packageHash, rootCid, version, primaryPath, manifest, root, rootBytes, leaves, objects };
113
+ }
114
+ /** Verify a set of files against on-chain commitments; returns the problems found (empty = verified). */
115
+ export function verifySkillPackage(files, expected, primaryPath) {
116
+ const problems = [];
117
+ let pkg;
118
+ try {
119
+ pkg = buildSkillPackage(files, { primaryPath, version: expected.version ?? 1, previousPackageHash: expected.previousPackageHash });
120
+ }
121
+ catch (e) {
122
+ return [`package does not build: ${e.message}`];
123
+ }
124
+ if (pkg.mdHash.toLowerCase() !== expected.mdHash.toLowerCase())
125
+ problems.push(`mdHash ${pkg.mdHash} != ${expected.mdHash}`);
126
+ if (pkg.packageHash.toLowerCase() !== expected.packageHash.toLowerCase())
127
+ problems.push(`packageHash ${pkg.packageHash} != ${expected.packageHash}`);
128
+ return problems;
129
+ }
130
+ /** The vector.json shape the reference tooling writes next to skillroot.cbor. */
131
+ export function vectorOf(pkg) {
132
+ return {
133
+ standard: "KERNEL v4.3",
134
+ mdHash: pkg.mdHash,
135
+ packageHash: pkg.packageHash,
136
+ rootCID_digest: pkg.rootCid,
137
+ version: pkg.version,
138
+ confidential: false,
139
+ encryptedPaths: [],
140
+ leaves: pkg.leaves,
141
+ };
142
+ }
@@ -0,0 +1,40 @@
1
+ import { eventArgs } from "./world.js";
2
+ import { buildSkillPackage, readSkillDir } from "./pack/skillpack.js";
3
+ /** ERC-8338 Token-Bound Executable Skills: the cell's capabilities as hash-verified packages. */
4
+ export class Skills {
5
+ world;
6
+ address;
7
+ constructor(world) {
8
+ this.world = world;
9
+ this.address = world.address("8338/SkillToken");
10
+ }
11
+ contract(runner) {
12
+ return this.world.contract("8338/SkillToken", runner);
13
+ }
14
+ /** Build a package from a directory (pack.py semantics). */
15
+ packageFromDir(dir, opts) {
16
+ return buildSkillPackage(readSkillDir(dir), opts);
17
+ }
18
+ encodeMint(to, updateAuthority, pkg, packageURI) {
19
+ return this.contract().interface.encodeFunctionData("mintSkill", [to, updateAuthority, pkg.mdHash, pkg.packageHash, packageURI]);
20
+ }
21
+ encodeUpdate(tokenId, pkg) {
22
+ return this.contract().interface.encodeFunctionData("updateSkill", [tokenId, pkg.mdHash, pkg.packageHash]);
23
+ }
24
+ encodeSafeTransfer(from, to, tokenId) {
25
+ return this.contract().interface.encodeFunctionData("safeTransferFrom(address,address,uint256)", [from, to, tokenId]);
26
+ }
27
+ tokenIdFromReceipt(receipt) {
28
+ return BigInt(eventArgs(this.contract().interface, receipt, "SkillUpdated", this.address).tokenId);
29
+ }
30
+ async skillOf(tokenId) {
31
+ const b = await this.contract().skillOf(tokenId);
32
+ return { mdHash: b.mdHash, packageHash: b.packageHash, version: BigInt(b.version) };
33
+ }
34
+ async ownerOf(tokenId) {
35
+ return this.contract().ownerOf(tokenId);
36
+ }
37
+ async skillURI(tokenId) {
38
+ return this.contract().skillURI(tokenId);
39
+ }
40
+ }
@@ -0,0 +1,206 @@
1
+ import { keccak256, toUtf8Bytes } from "ethers";
2
+ import { abi, dataUriJcs, jcs, sha256Hex } from "./canonical.js";
3
+ import { eventArgs } from "./world.js";
4
+ /** ERC-8447 Swarm AI Framework: how cells organize into purpose-bound swarms. */
5
+ export const Boundary = { OPEN: 0, GATED: 1, CLOSED: 2 };
6
+ export const Admission = { RECRUIT: 0, INHERIT: 1, SELECT: 2 };
7
+ export const AuthorityKind = { NONE: 0, ADDRESS: 1, ROLE: 2, ANY_MEMBER: 3, INITIATOR: 4 };
8
+ export const DecisionPoint = { ACTIVATE: 0, ABORT: 1, SUSPEND: 2, RESUME: 3, ADVANCE: 4, CONCLUDE: 5, DISSOLVE: 6, SUCCEED: 7, ADMIT: 8, REMOVE: 9, REINSTATE: 10, AMEND: 11, ATTACH: 12, DETACH: 13, SET_ACCOUNT: 14, REPRESENT: 15 };
9
+ export const DECISION_POINTS = 16;
10
+ export const Activation = { ROSTER_MET: 0, AUTHORITY: 1, BOTH: 2 };
11
+ export const Contributions = { DISABLED: 0, MEMBERS: 1, OPEN: 2 };
12
+ export const ObjectiveKind = { TERMINAL: 0, MILESTONED: 1, EXPLORATORY: 2, STANDING: 3 };
13
+ export const LifecycleStateName = ["NONE", "FORMING", "ACTIVE", "SUSPENDED", "CONCLUDING", "DISSOLVED"];
14
+ export const OutcomeTypeName = ["NONE", "ACHIEVED", "PARTIAL", "FAILED", "ABANDONED", "SUPERSEDED"];
15
+ export const MemberStatusName = ["NONE", "CANDIDATE", "ACTIVE", "SUSPENDED", "EXITED"];
16
+ export const SuspensionCauseName = ["NONE", "AUTHORITY", "INELIGIBLE"];
17
+ export const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
18
+ export const ZERO32 = "0x" + "00".repeat(32);
19
+ export function saiId(name) {
20
+ return keccak256(toUtf8Bytes(name));
21
+ }
22
+ export const roleId = (name) => saiId(`sai.role.${name}`);
23
+ export const term = { payoff: (n) => saiId(`sai.payoff.${n}`), topology: (n) => saiId(`sai.topology.${n}`), coordination: (n) => saiId(`sai.coordination.${n}`) };
24
+ export const originKind = (name) => saiId(`sai.origin.${name}`);
25
+ export function bytes32Label(label) {
26
+ const b = toUtf8Bytes(label);
27
+ if (b.length > 32)
28
+ throw new Error(`label ${label} exceeds 32 bytes`);
29
+ return "0x" + Buffer.from(b).toString("hex").padEnd(64, "0");
30
+ }
31
+ export const none = { kind: AuthorityKind.NONE, target: ZERO_ADDRESS, roleId: ZERO32 };
32
+ export const initiator = { kind: AuthorityKind.INITIATOR, target: ZERO_ADDRESS, roleId: ZERO32 };
33
+ export const byAddress = (target) => ({ kind: AuthorityKind.ADDRESS, target, roleId: ZERO32 });
34
+ export const byRole = (role) => ({ kind: AuthorityKind.ROLE, target: ZERO_ADDRESS, roleId: role });
35
+ /** Sixteen authorities indexed by decision point, from a partial map; the rest are NONE. */
36
+ export function authorities(map) {
37
+ const out = new Array(DECISION_POINTS).fill(none);
38
+ for (const [name, a] of Object.entries(map))
39
+ out[DecisionPoint[name]] = a;
40
+ return out;
41
+ }
42
+ export const SWARM_SPEC_TUPLE = "tuple(tuple(bytes32 roleId,uint32 min,uint32 max,address eligibility,bool selfJoin)[] roles,tuple(bytes32 name,bytes32 payoff,bytes32 topology,bytes32 coordination,uint8 boundary,uint8 admission,uint64 inputWindow,bytes32 descriptorHash)[] phases,tuple(uint8 kind,address target,bytes32 roleId)[] authorities,uint8 activation,uint8 contributions,uint32 minMembers,uint32 maxMembers,uint64 recruitmentWindow,uint64 maxDuration,uint64 inactivityTimeout,uint64 ineligibleGrace,uint64 settlementWindow,uint64 amendmentExitWindow,bytes32 descriptorHash)";
43
+ /** specHash = keccak256(abi.encode(spec)). */
44
+ export function specHashOf(spec) {
45
+ return keccak256(abi.encode([SWARM_SPEC_TUPLE], [spec]));
46
+ }
47
+ /** Swarm Pack commitments: SHA-256 over the JCS form of the root, the objective and the commitment members. */
48
+ export function packHashes(pack) {
49
+ return {
50
+ charterHash: sha256Hex(jcs(pack)),
51
+ objectiveHash: sha256Hex(jcs(pack.objective)),
52
+ commitmentHash: pack.commitment ? sha256Hex(jcs(pack.commitment)) : ZERO32,
53
+ charterURI: dataUriJcs(pack),
54
+ };
55
+ }
56
+ export function taskOriginRef(chainId, taskToken, tokenId) {
57
+ return keccak256(abi.encode(["uint256", "address", "uint256"], [chainId, taskToken, tokenId]));
58
+ }
59
+ export class Swarms {
60
+ world;
61
+ address;
62
+ constructor(world) {
63
+ this.world = world;
64
+ this.address = world.address("8447/SwarmRegistry");
65
+ }
66
+ contract(runner) {
67
+ return this.world.contract("8447/SwarmRegistry", runner);
68
+ }
69
+ // ---- calldata builders
70
+ encodeForm(spec, charterHash, commitmentHash, charterURI, origin, objective) {
71
+ return this.contract().interface.encodeFunctionData("form", [spec, charterHash, commitmentHash, charterURI, ZERO32, origin, objective]);
72
+ }
73
+ encodeJoin(swarmId, role, charterHash) {
74
+ return this.contract().interface.encodeFunctionData("join", [swarmId, role, charterHash]);
75
+ }
76
+ encodeActivate(swarmId) {
77
+ return this.contract().interface.encodeFunctionData("activate", [swarmId]);
78
+ }
79
+ encodeSetAccount(swarmId, account) {
80
+ return this.contract().interface.encodeFunctionData("setAccount", [swarmId, account]);
81
+ }
82
+ encodeChallenge(swarmId, account) {
83
+ return this.contract().interface.encodeFunctionData("challenge", [swarmId, account]);
84
+ }
85
+ encodeAmend(swarmId, charterHash, charterURI, objectiveHash = ZERO32, commitmentHash = ZERO32, material = false) {
86
+ return this.contract().interface.encodeFunctionData("amend", [swarmId, charterHash, charterURI, objectiveHash, commitmentHash, material]);
87
+ }
88
+ encodeLeave(swarmId) {
89
+ return this.contract().interface.encodeFunctionData("leave", [swarmId]);
90
+ }
91
+ swarmIdFromReceipt(receipt) {
92
+ return eventArgs(this.contract().interface, receipt, "SwarmFormed", this.address).swarmId;
93
+ }
94
+ selector(fn) {
95
+ return this.contract().interface.getFunction(fn).selector;
96
+ }
97
+ // ---- views
98
+ async getSwarm(swarmId) {
99
+ const s = await this.contract().getSwarm(swarmId);
100
+ const state = Number(s.state);
101
+ const outcome = Number(s.outcome);
102
+ return {
103
+ specHash: s.specHash,
104
+ charterHash: s.charterHash,
105
+ commitmentHash: s.commitmentHash,
106
+ charterVersion: Number(s.charterVersion),
107
+ commitmentVersion: Number(s.commitmentVersion),
108
+ charterURI: s.charterURI,
109
+ initiator: s.initiator,
110
+ account: s.account,
111
+ state,
112
+ stateName: LifecycleStateName[state] ?? String(state),
113
+ outcome,
114
+ outcomeName: OutcomeTypeName[outcome] ?? String(outcome),
115
+ phase: Number(s.phase),
116
+ resultHash: s.resultHash,
117
+ activatedAt: BigInt(s.activatedAt),
118
+ };
119
+ }
120
+ async memberStatus(swarmId, account) {
121
+ const r = await this.contract().memberStatus(swarmId, account);
122
+ const status = Number(r[0]);
123
+ return { status, statusName: MemberStatusName[status] ?? String(status), exitReason: Number(r[1]) };
124
+ }
125
+ async member(swarmId, account) {
126
+ const m = await this.contract().getMember(swarmId, account);
127
+ const suspension = Number(m.suspension);
128
+ return { roleId: m.roleId, status: Number(m.status), suspension, suspensionName: SuspensionCauseName[suspension] ?? String(suspension), acceptedCharter: m.acceptedCharter, acceptedVersion: Number(m.acceptedVersion) };
129
+ }
130
+ async rosterMet(swarmId) {
131
+ return this.contract().rosterMet(swarmId);
132
+ }
133
+ async roleCount(swarmId, role) {
134
+ const r = await this.contract().roleCount(swarmId, role);
135
+ return { active: Number(r[0]), suspended: Number(r[1]) };
136
+ }
137
+ async isAuthorized(swarmId, dp, account) {
138
+ return this.contract().isAuthorized(swarmId, dp, account);
139
+ }
140
+ async seatEpoch(swarmId, account) {
141
+ return BigInt(await this.contract().seatEpochOf(swarmId, account));
142
+ }
143
+ async hardDeadline(swarmId) {
144
+ return BigInt(await this.contract().hardDeadlineOf(swarmId));
145
+ }
146
+ }
147
+ /** The swarm account the SwarmRegistry reference ships: `execute` by whoever holds REPRESENT. */
148
+ export class SwarmAccountRef {
149
+ world;
150
+ address;
151
+ iface;
152
+ constructor(world, address) {
153
+ this.world = world;
154
+ this.address = address;
155
+ this.iface = world.ifaceOf("swarm-ai-framework:SwarmAccount");
156
+ }
157
+ contract(runner) {
158
+ return this.world.at("swarm-ai-framework:SwarmAccount", this.address, runner);
159
+ }
160
+ encodeExecute(target, value, data) {
161
+ return this.iface.encodeFunctionData("execute", [target, value, data]);
162
+ }
163
+ get executeSelector() {
164
+ return this.iface.getFunction("execute").selector;
165
+ }
166
+ }
167
+ export class SwarmTenderAdapterRef {
168
+ world;
169
+ address;
170
+ constructor(world, address) {
171
+ this.world = world;
172
+ this.address = address;
173
+ }
174
+ contract(runner) {
175
+ return this.world.contract("8447/SwarmTenderAdapter", runner);
176
+ }
177
+ encodeSettle(swarmId, submissionId) {
178
+ return this.contract().interface.encodeFunctionData("settle", [swarmId, submissionId]);
179
+ }
180
+ }
181
+ export class SwarmDecisionAdapterRef {
182
+ world;
183
+ address;
184
+ iface;
185
+ constructor(world, address) {
186
+ this.world = world;
187
+ this.address = address;
188
+ this.iface = world.ifaceOf("swarm-ai-framework:SwarmDecisionAdapter");
189
+ }
190
+ contract(runner) {
191
+ return this.world.at("swarm-ai-framework:SwarmDecisionAdapter", this.address, runner);
192
+ }
193
+ encodeOpen(swarmId, dp, call, roster) {
194
+ return this.iface.encodeFunctionData("open", [swarmId, dp, call, roster]);
195
+ }
196
+ encodeExecute(issueId) {
197
+ return this.iface.encodeFunctionData("execute", [issueId]);
198
+ }
199
+ issueIdFromReceipt(receipt) {
200
+ return eventArgs(this.iface, receipt, "ProposalOpened", this.address).issueId;
201
+ }
202
+ async proposal(issueId) {
203
+ const p = await this.contract().proposalOf(issueId);
204
+ return { exists: Boolean(p.exists), enacted: Boolean(p.enacted), swarmId: p.swarmId, charterVersion: Number(p.charterVersion), phase: Number(p.phase) };
205
+ }
206
+ }
@@ -0,0 +1,121 @@
1
+ import { concat, getAddress, sha256, toUtf8Bytes } from "ethers";
2
+ import { eventArgs } from "./world.js";
3
+ export const SubmissionStatus = { Pending: 0, Accepted: 1, Rejected: 2 };
4
+ export function nativeTerms(reward, overrides = {}) {
5
+ return {
6
+ asset: "0x0000000000000000000000000000000000000000",
7
+ rewardPerCompletion: reward,
8
+ maxCompletions: 1n,
9
+ submitBy: 0n,
10
+ settleBy: 0n,
11
+ epochLength: 0n,
12
+ maxCompletionsPerEpoch: 0n,
13
+ judgmentWindow: 7n * 86400n,
14
+ ...overrides,
15
+ };
16
+ }
17
+ /** HashlockVerifier profile (x-hashlock-v1): the demander commits sha256(answer); the solver submits sha256(answer ‖ fulfiller). */
18
+ export const hashlock = {
19
+ answerHash(answer) {
20
+ return sha256(typeof answer === "string" ? toUtf8Bytes(answer) : answer);
21
+ },
22
+ resultHash(answer, fulfiller) {
23
+ const a = typeof answer === "string" ? toUtf8Bytes(answer) : answer;
24
+ return sha256(concat([a, getAddress(fulfiller)]));
25
+ },
26
+ };
27
+ export class Tenders {
28
+ world;
29
+ address;
30
+ constructor(world) {
31
+ this.world = world;
32
+ this.address = world.address("8414/TaskToken");
33
+ }
34
+ contract(runner) {
35
+ return this.world.contract("8414/TaskToken", runner);
36
+ }
37
+ hashlockVerifier(runner) {
38
+ return this.world.contract("8414/HashlockVerifier", runner);
39
+ }
40
+ get hashlockAddress() {
41
+ return this.world.address("8414/HashlockVerifier");
42
+ }
43
+ // ---- calldata builders (for calls routed through a cell account)
44
+ encodeMint(p) {
45
+ return this.contract().interface.encodeFunctionData("mintTask", [p.to, p.updateAuthority, p.acceptanceAuthority, p.tdHash, p.taskHash, p.taskURI, p.terms]);
46
+ }
47
+ /** fundTask for a native-asset tender: the call must carry `amount` as value. */
48
+ encodeFund(tokenId, amount) {
49
+ return this.contract().interface.encodeFunctionData("fundTask", [tokenId, amount]);
50
+ }
51
+ encodeCommitAnswer(tokenId, answerHash) {
52
+ return this.hashlockVerifier().interface.encodeFunctionData("commitAnswer", [this.address, tokenId, answerHash]);
53
+ }
54
+ tokenIdFromReceipt(receipt) {
55
+ return BigInt(eventArgs(this.contract().interface, receipt, "TaskUpdated", this.address).tokenId);
56
+ }
57
+ encodeSubmit(tokenId, resultHash, resultURI) {
58
+ return this.contract().interface.encodeFunctionData("submitFulfillment", [tokenId, resultHash, resultURI]);
59
+ }
60
+ encodeSettle(tokenId, submissionId, proof) {
61
+ return this.contract().interface.encodeFunctionData("settleFulfillment", [tokenId, submissionId, proof]);
62
+ }
63
+ encodeWithdrawCredit(tokenId, account) {
64
+ return this.contract().interface.encodeFunctionData("withdrawCredit", [tokenId, account]);
65
+ }
66
+ selectors() {
67
+ const i = this.contract().interface;
68
+ return {
69
+ submitFulfillment: i.getFunction("submitFulfillment").selector,
70
+ settleFulfillment: i.getFunction("settleFulfillment").selector,
71
+ withdrawCredit: i.getFunction("withdrawCredit").selector,
72
+ fundTask: i.getFunction("fundTask").selector,
73
+ };
74
+ }
75
+ // ---- demander side (EOA signer)
76
+ async mint(signer, p) {
77
+ const tx = await this.contract(signer).mintTask(p.to, p.updateAuthority, p.acceptanceAuthority, p.tdHash, p.taskHash, p.taskURI, p.terms);
78
+ const receipt = await tx.wait();
79
+ const tokenId = BigInt(eventArgs(this.contract().interface, receipt, "TaskUpdated", this.address).tokenId);
80
+ return { tokenId, receipt };
81
+ }
82
+ async fundNative(signer, tokenId, amount) {
83
+ const tx = await this.contract(signer).fundTask(tokenId, amount, { value: amount });
84
+ return tx.wait();
85
+ }
86
+ async commitAnswer(signer, tokenId, answerHash) {
87
+ const tx = await this.hashlockVerifier(signer).commitAnswer(this.address, tokenId, answerHash);
88
+ return tx.wait();
89
+ }
90
+ // ---- views
91
+ async submissionOf(tokenId, submissionId) {
92
+ const s = await this.contract().submissionOf(tokenId, submissionId);
93
+ return { fulfiller: s.fulfiller, resultHash: s.resultHash, taskVersion: BigInt(s.taskVersion), submittedAt: BigInt(s.submittedAt), machineSettled: Boolean(s.machineSettled), status: Number(s.status) };
94
+ }
95
+ async submissionCount(tokenId) {
96
+ return BigInt(await this.contract().submissionCountOf(tokenId));
97
+ }
98
+ async terms(tokenId) {
99
+ const t = await this.contract().tenderTermsOf(tokenId);
100
+ return { asset: t.asset, rewardPerCompletion: BigInt(t.rewardPerCompletion), maxCompletions: BigInt(t.maxCompletions), submitBy: BigInt(t.submitBy), settleBy: BigInt(t.settleBy), epochLength: BigInt(t.epochLength), maxCompletionsPerEpoch: BigInt(t.maxCompletionsPerEpoch), judgmentWindow: BigInt(t.judgmentWindow) };
101
+ }
102
+ async acceptanceAuthorityOf(tokenId) {
103
+ return this.contract().acceptanceAuthorityOf(tokenId);
104
+ }
105
+ async escrowBalanceOf(tokenId) {
106
+ return BigInt(await this.contract().escrowBalanceOf(tokenId));
107
+ }
108
+ async creditOf(tokenId, account) {
109
+ return BigInt(await this.contract().creditOf(tokenId, account));
110
+ }
111
+ async isCancelled(tokenId) {
112
+ return this.contract().isTenderCancelled(tokenId);
113
+ }
114
+ async nextId() {
115
+ return BigInt(await this.contract().nextId());
116
+ }
117
+ /** Submission id of a FulfillmentSubmitted event in a receipt (receipts of execute() calls carry the token's logs). */
118
+ submissionIdFromReceipt(receipt) {
119
+ return BigInt(eventArgs(this.contract().interface, receipt, "FulfillmentSubmitted", this.address).submissionId);
120
+ }
121
+ }