@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,162 @@
1
+ import { keccak256, toUtf8Bytes } from "ethers";
2
+ import { keccakJcs } from "./canonical.js";
3
+ import { eventArgs } from "./world.js";
4
+ /** ERC-8436 Agent Collective Decision Framework: how cells decide together. */
5
+ export const BodyKind = { ROSTER_KOFN: 0, AUTHORIZED_SUBMITTER: 1 };
6
+ export const Acceptance = { ON_CHAIN_TALLY: 0, SIGNED_BALLOTS: 1, AUTHORIZED_SUBMITTER: 2 };
7
+ export const Combinator = { BODY: 0, ALL: 1, ANY: 2, KOFM: 3, VETO: 4 };
8
+ export const VetoSilence = { PASS_THROUGH: 0, REQUIRE_CLEARANCE: 1 };
9
+ export const AppealStanding = { ANYONE: 0, CONSUMER_OR_FILER: 1 };
10
+ export const AppealMode = { PRESERVE_UNLESS_OVERTURNED: 0, REQUIRE_FRESH_DECISION: 1 };
11
+ export const ProcedureStateName = ["None", "Filed", "Deciding", "Provisional", "Final", "Withdrawn"];
12
+ export const OutcomeTypeName = ["None", "Decided", "NoDecision"];
13
+ export const ReasonName = ["NONE", "QUORUM_NOT_MET", "SUBMITTER_SILENT", "VETOED", "NO_CLEARANCE", "NOT_REACHED", "TOTAL_TIMEOUT", "NO_ACCEPTANCE"];
14
+ export const EnactmentStatusName = ["NotEnacted", "Enacted", "Failed"];
15
+ const ZERO32 = "0x" + "00".repeat(32);
16
+ export function familyId(name) {
17
+ return keccak256(toUtf8Bytes(name));
18
+ }
19
+ /**
20
+ * The FOGA-Council policy: one roster body of judges deciding K-of-N by on-chain tally within `window`, one
21
+ * appeal allowed for a Decided result (an appeal round that ends without a decision preserves the first one),
22
+ * no appeal of a NoDecision, hard cap covering every round and the appeal window.
23
+ */
24
+ export function councilPolicySpec(p) {
25
+ const maxAppeals = 1;
26
+ return {
27
+ family: familyId("foga.council"),
28
+ version: p.version ?? 1,
29
+ previous: p.previous ?? ZERO32,
30
+ updateAuthority: p.updateAuthority,
31
+ bodies: [{ kind: BodyKind.ROSTER_KOFN, acceptance: Acceptance.ON_CHAIN_TALLY, members: p.judges, k: p.k, window: p.window }],
32
+ nodes: [{ op: Combinator.BODY, body: 0, k: 0, target: 0, vetoBody: 0, silence: VetoSilence.PASS_THROUGH, children: [] }],
33
+ maxAppeals,
34
+ appealWindow: p.appealWindow,
35
+ appealable: 1, // bit0: Decided results may be appealed; a NoDecision is final at once
36
+ appealStanding: AppealStanding.ANYONE,
37
+ appealMode: AppealMode.PRESERVE_UNLESS_OVERTURNED,
38
+ maxTotalDuration: p.window * (maxAppeals + 1) + p.appealWindow * maxAppeals,
39
+ ackWindow: 0,
40
+ allowAdvisory: false,
41
+ descriptorHash: keccakJcs(p.descriptor),
42
+ };
43
+ }
44
+ /** The off-chain descriptor the policy's descriptorHash commits to. */
45
+ export function councilDescriptor(p) {
46
+ return {
47
+ acdf: "1.0",
48
+ kind: "policy-descriptor",
49
+ name: "FOGA-Council",
50
+ summary: "The judging body of the Finch OG Agent genesis: three judges, two approvals decide, one appeal of a decision, silence is no decision.",
51
+ bodies: [{ role: "judge", members: p.judges.map((j) => j.toLowerCase()), k: p.k, acceptance: "on-chain-tally", window: p.window }],
52
+ appeals: { max: 1, window: p.appealWindow, appealable: ["Decided"], mode: "preserve-unless-overturned" },
53
+ consumers: ["ACDFTaskTenderAdapter (ERC-8414 acceptance)", "ADRFACDFBridge (ERC-8471 judgment tier, phase 2b)"],
54
+ independence: "A judge is never a party: not a demander, fulfiller, swarm member or world operator of the matter judged; checked by the genesis before each case.",
55
+ };
56
+ }
57
+ export class ACDF {
58
+ world;
59
+ constructor(world) {
60
+ this.world = world;
61
+ }
62
+ policies(runner) {
63
+ return this.world.contract("8436/ACDFPolicyRegistry", runner);
64
+ }
65
+ registry(runner) {
66
+ return this.world.contract("8436/ACDFRegistry", runner);
67
+ }
68
+ get policyRegistryAddress() {
69
+ return this.world.address("8436/ACDFPolicyRegistry");
70
+ }
71
+ get registryAddress() {
72
+ return this.world.address("8436/ACDFRegistry");
73
+ }
74
+ encodeRegisterPolicy(spec) {
75
+ return this.policies().interface.encodeFunctionData("registerPolicy", [spec]);
76
+ }
77
+ async policyIdOf(spec) {
78
+ return this.policies().policyIdOf(spec);
79
+ }
80
+ policyIdFromReceipt(receipt) {
81
+ return eventArgs(this.policies().interface, receipt, "PolicyRegistered", this.policyRegistryAddress).policyId;
82
+ }
83
+ encodeCastBallot(issueId, round, body, approve) {
84
+ return this.registry().interface.encodeFunctionData("castBallot", [issueId, round, body, approve]);
85
+ }
86
+ encodeSettleRound(issueId) {
87
+ return this.registry().interface.encodeFunctionData("settleRound", [issueId]);
88
+ }
89
+ encodeFinalize(issueId) {
90
+ return this.registry().interface.encodeFunctionData("finalize", [issueId]);
91
+ }
92
+ async getResult(issueId) {
93
+ const r = await this.registry().getResult(issueId);
94
+ const state = Number(r.state);
95
+ const outcomeType = Number(r.outcomeType);
96
+ const reason = Number(r.reason);
97
+ return {
98
+ state,
99
+ stateName: ProcedureStateName[state] ?? String(state),
100
+ outcomeType,
101
+ outcomeName: OutcomeTypeName[outcomeType] ?? String(outcomeType),
102
+ outcomeYes: Boolean(r.outcomeYes),
103
+ reason,
104
+ reasonName: ReasonName[reason] ?? String(reason),
105
+ roundCount: Number(r.roundCount),
106
+ sourceRound: Number(r.sourceRound),
107
+ adoptedFromEarlierRound: Boolean(r.adoptedFromEarlierRound),
108
+ decidedAt: BigInt(r.decidedAt),
109
+ finalAt: BigInt(r.finalAt),
110
+ };
111
+ }
112
+ async roundOf(issueId) {
113
+ return Number((await this.registry().getIssue(issueId)).roundCount);
114
+ }
115
+ async roundState(issueId, round) {
116
+ const r = await this.registry().getRound(issueId, round);
117
+ return { startedAt: BigInt(r.startedAt), deadline: BigInt(r.deadline), settled: Boolean(r.settled), appealOpenUntil: BigInt(r.appealOpenUntil) };
118
+ }
119
+ async hasVoted(issueId, round, body, voter) {
120
+ return this.registry().hasVoted(issueId, round, body, voter);
121
+ }
122
+ async enactment(issueId, consumer, effectId) {
123
+ const e = await this.registry().getEnactment(issueId, consumer, effectId);
124
+ const status = Number(e.status);
125
+ return { status, statusName: EnactmentStatusName[status] ?? String(status), reporter: e.reporter, ref: e.ref };
126
+ }
127
+ /** Decode the first IssueFiled event of a receipt (filed by an adapter). */
128
+ issueIdFromReceipt(receipt) {
129
+ return eventArgs(this.registry().interface, receipt, "IssueFiled", this.registryAddress).issueId;
130
+ }
131
+ }
132
+ /** An ACDFTaskTenderAdapter instance (the upstream adapter, deployed by FOGA for its policy). */
133
+ export class TenderAdapterRef {
134
+ world;
135
+ address;
136
+ iface;
137
+ constructor(world, address) {
138
+ this.world = world;
139
+ this.address = address;
140
+ this.iface = world.ifaceOf("acd-framework:ACDFTaskTenderAdapter");
141
+ }
142
+ contract(runner) {
143
+ return this.world.at("acd-framework:ACDFTaskTenderAdapter", this.address, runner);
144
+ }
145
+ encodeOpen(tokenId, submissionId) {
146
+ return this.iface.encodeFunctionData("open", [tokenId, submissionId]);
147
+ }
148
+ encodeExecute(issueId) {
149
+ return this.iface.encodeFunctionData("execute", [issueId]);
150
+ }
151
+ issueIdFromReceipt(receipt) {
152
+ return eventArgs(this.iface, receipt, "CaseOpened", this.address).issueId;
153
+ }
154
+ async caseOf(issueId) {
155
+ const c = await this.contract().caseOf(issueId);
156
+ return { exists: Boolean(c.exists), enacted: Boolean(c.enacted), tokenId: BigInt(c.tokenId), submissionId: BigInt(c.submissionId), consumerDeadline: BigInt(c.consumerDeadline) };
157
+ }
158
+ async effects() {
159
+ const c = this.contract();
160
+ return { accept: await c.EFFECT_ACCEPT(), reject: await c.EFFECT_REJECT() };
161
+ }
162
+ }
@@ -0,0 +1,273 @@
1
+ import { keccak256, toUtf8Bytes } from "ethers";
2
+ import { abi, keccakJcs } from "./canonical.js";
3
+ import { eventArgs } from "./world.js";
4
+ /**
5
+ * ERC-8471 AI Dispute Resolution Framework: disputes about deliveries and declarations, decided by methods a
6
+ * policy binds (a deterministic verifier, an evidence rule, or an ERC-8436 collective decision through the
7
+ * bridge), with rulings enacted by the bound business contract within its own authority.
8
+ */
9
+ export const QuestionType = { FORMAL: 0, EVIDENCED: 1, JUDGED: 2 };
10
+ export const Value = { UNDETERMINED: 0, TRUE: 1, FALSE: 2 };
11
+ export const ValueName = ["UNDETERMINED", "TRUE", "FALSE"];
12
+ export const AcceptanceMode = { PRE_COMMIT: 0, POST_DISPUTE: 1 };
13
+ export const EffectClass = { Advisory: 0, Binding: 1 };
14
+ export const EffectCategory = { SUBSTANTIVE: 0, PROCEDURAL_ECONOMIC: 1 };
15
+ export const OnUndetermined = { NOT_SUSTAINED: 0, NO_DECISION: 1 };
16
+ export const StandingMode = { PARTIES: 0, BENEFICIARIES: 1, OPEN: 2 };
17
+ export const SilenceRule = { NONE: 0, SUSTAIN: 1, NOT_SUSTAIN: 2 };
18
+ export const ReviewMode = { PRESERVE_UNLESS_OVERTURNED: 0, REQUIRE_FRESH_DECISION: 1 };
19
+ export const ProcedureStateName = ["None", "Filed", "Admitted", "Provisional", "Final", "NotAdmitted", "Withdrawn", "Expired"];
20
+ export const SubstantiveResultName = ["None", "Sustained", "NotSustained", "NoDecision"];
21
+ export const ReasonName = ["NONE", "MISSING_ELEMENT", "NO_BINDING_AUTHORITY", "CLOCK_INCOMPATIBLE", "DUPLICATE", "PREMISE_NOT_ESTABLISHED", "METHOD_UNDETERMINED", "BURDEN_NOT_MET", "TIMEOUT", "REVIEW_LAPSED", "ACCEPTANCE_LAPSED", "SILENCE", "PROCEDURE_FAILURE"];
22
+ export const EnactmentStatusName = ["NotRequested", "Pending", "Enacted", "Failed", "Unknown"];
23
+ export const SettlementStateName = ["Held", "Released", "Refunded"];
24
+ export const REVIEW_FACT = 1;
25
+ export const REVIEW_EVIDENCE = 2;
26
+ export const REVIEW_RULE_APPLICATION = 4;
27
+ export const RESULT_PROFILE_V1 = keccak256(toUtf8Bytes("adrf:result/v1"));
28
+ /** Obligation kinds as the worked-example escrow declares them. */
29
+ export const KIND_RESULT = 1;
30
+ export const KIND_LIMIT = 3;
31
+ /** Identifiers of the worked-example escrow (ADRFServiceEscrow). */
32
+ export const EFFECT_REFUND = keccak256(toUtf8Bytes("adrf:effect/refund"));
33
+ export const DISPOSITION_STATUS_QUO = keccak256(toUtf8Bytes("adrf:disposition/status-quo"));
34
+ export const EVENT_DELIVERY = keccak256(toUtf8Bytes("delivery"));
35
+ export const STATEMENT_ACTUAL_VERSION = keccak256(toUtf8Bytes("the version actually used for the call matches the committed version"));
36
+ const DECLARED_VERSION_STATEMENT = keccak256(toUtf8Bytes("adrf:prop/declared-version-differs/v1"));
37
+ const ZERO32 = "0x" + "00".repeat(32);
38
+ export function familyId(name) {
39
+ return keccak256(toUtf8Bytes(name));
40
+ }
41
+ /**
42
+ * FOGA-Service-v1: the dispute policy for service calls between cells. A (result, FORMAL) question goes to the
43
+ * upstream DeclaredVersionVerifier over the escrow; a (result, JUDGED) question goes to FOGA-Council through
44
+ * the ADRF/ACDF bridge, with NO_DECISION as the burden rule (an undecided council leaves the status quo). One
45
+ * substantive effect: the escrow's refund. Standing: the parties of the call. One review round.
46
+ */
47
+ export function servicePolicySpec(p) {
48
+ const t = p.timing;
49
+ const maxReviews = 1;
50
+ return {
51
+ family: familyId("foga.service"),
52
+ version: p.version ?? 1,
53
+ previous: p.previous ?? ZERO32,
54
+ updateAuthority: p.updateAuthority,
55
+ objectKind: keccak256(toUtf8Bytes("service-call")),
56
+ methods: [
57
+ { obligationKind: KIND_RESULT, qType: QuestionType.FORMAL, method: p.versionVerifier, params: addressParam(p.escrow), onUndetermined: OnUndetermined.NOT_SUSTAINED },
58
+ { obligationKind: KIND_RESULT, qType: QuestionType.JUDGED, method: p.bridge, params: p.councilPolicyId, onUndetermined: OnUndetermined.NO_DECISION },
59
+ ],
60
+ effects: [{ id: EFFECT_REFUND, category: EffectCategory.SUBSTANTIVE }],
61
+ track: {
62
+ filingWindow: t.filingWindow,
63
+ acceptanceWindow: 0,
64
+ evidenceWindow: t.evidenceWindow,
65
+ decisionWindow: t.decisionWindow,
66
+ reviewWindow: t.reviewWindow,
67
+ maxReviews,
68
+ reviewTypes: REVIEW_FACT | REVIEW_EVIDENCE | REVIEW_RULE_APPLICATION,
69
+ reviewMode: ReviewMode.PRESERVE_UNLESS_OVERTURNED,
70
+ maxTotalDuration: t.decisionWindow + maxReviews * (t.reviewWindow + t.decisionWindow),
71
+ enactmentMargin: t.enactmentMargin,
72
+ optimistic: false,
73
+ answerWindow: 0,
74
+ silenceRule: SilenceRule.NONE,
75
+ },
76
+ standing: StandingMode.PARTIES,
77
+ resultProfile: RESULT_PROFILE_V1,
78
+ economicDeclaration: keccakJcs({ kind: "economic-declaration", name: "FOGA-Service-v1", bonds: "none", fees: "none", rewards: "none" }),
79
+ descriptorHash: keccakJcs(p.descriptor),
80
+ };
81
+ }
82
+ /** The off-chain descriptor the policy's descriptorHash commits to. */
83
+ export function serviceDescriptor(p) {
84
+ return {
85
+ adrf: "0.2",
86
+ kind: "policy-descriptor",
87
+ name: "FOGA-Service-v1",
88
+ summary: "Disputes about service calls between Finch OG Agent cells settled through the worked-example escrow: a formal question on the declared version, a judged question on the version actually used, decided by FOGA-Council; one effect, the refund of the held settlement.",
89
+ object: { kind: "service-call", escrow: p.escrow.toLowerCase() },
90
+ methods: [
91
+ { obligation: "result", question: "FORMAL", method: p.versionVerifier.toLowerCase(), params: "the escrow", burden: "NOT_SUSTAINED" },
92
+ { obligation: "result", question: "JUDGED", method: p.bridge.toLowerCase(), params: p.councilPolicyId, burden: "NO_DECISION" },
93
+ ],
94
+ effects: [{ id: "adrf:effect/refund", category: "substantive", executor: "the escrow, within the settlement it holds" }],
95
+ windows: { ...p.timing },
96
+ standing: "parties",
97
+ reviews: { max: 1, mode: "preserve-unless-overturned", types: ["fact", "evidence", "rule-application"] },
98
+ noDecision: "status quo: balances stay where the call left them",
99
+ };
100
+ }
101
+ export function addressParam(a) {
102
+ return "0x" + a.toLowerCase().replace(/^0x/, "").padStart(64, "0");
103
+ }
104
+ /** The declared-version verifier's canonical proposition for a call of a service. */
105
+ export function declaredVersionProposition(service, callId) {
106
+ return keccak256(abi.encode(["bytes32", "address", "uint256"], [DECLARED_VERSION_STATEMENT, service, callId]));
107
+ }
108
+ export function declaredVersionInputs(callId) {
109
+ return abi.encode(["uint256"], [callId]);
110
+ }
111
+ export function declaredVersionInputsHash(callId) {
112
+ return keccak256(declaredVersionInputs(callId));
113
+ }
114
+ export class ADRF {
115
+ world;
116
+ constructor(world) {
117
+ this.world = world;
118
+ }
119
+ policies(runner) {
120
+ return this.world.contract("8471/ADRFPolicyRegistry", runner);
121
+ }
122
+ /** The dispute registry through its full interface: the deployed contract is a router whose own ABI only names its modules. */
123
+ registry(runner) {
124
+ return this.world.at("ai-dispute-resolution:IADRFDisputeRegistry", this.registryAddress, runner);
125
+ }
126
+ bridge(runner) {
127
+ return this.world.contract("8471/ADRFACDFBridge", runner);
128
+ }
129
+ escrow(runner) {
130
+ return this.world.contract("8471/ADRFServiceEscrow", runner);
131
+ }
132
+ get policyRegistryAddress() {
133
+ return this.world.address("8471/ADRFPolicyRegistry");
134
+ }
135
+ get registryAddress() {
136
+ return this.world.address("8471/ADRFDisputeRegistry");
137
+ }
138
+ get bridgeAddress() {
139
+ return this.world.address("8471/ADRFACDFBridge");
140
+ }
141
+ get escrowAddress() {
142
+ return this.world.address("8471/ADRFServiceEscrow");
143
+ }
144
+ get versionVerifierAddress() {
145
+ return this.world.address("8471/DeclaredVersionVerifier");
146
+ }
147
+ policyIdFromReceipt(receipt) {
148
+ return eventArgs(this.policies().interface, receipt, "PolicyRegistered", this.policyRegistryAddress).policyId;
149
+ }
150
+ bindingIdFromReceipt(receipt) {
151
+ return eventArgs(this.registry().interface, receipt, "BindingRegistered", this.registryAddress).bindingId;
152
+ }
153
+ caseIdFromReceipt(receipt) {
154
+ return eventArgs(this.registry().interface, receipt, "CaseFiled", this.registryAddress).caseId;
155
+ }
156
+ callIdFromReceipt(receipt) {
157
+ return BigInt(eventArgs(this.escrow().interface, receipt, "CallOpened", this.escrowAddress).callId);
158
+ }
159
+ // ---- escrow (business) ----
160
+ encodeDeposit() {
161
+ return this.escrow().interface.encodeFunctionData("deposit", []);
162
+ }
163
+ encodeOpenCall(provider, amount, termsHash, committedVersion, disputePeriod) {
164
+ return this.escrow().interface.encodeFunctionData("openCall", [provider, amount, termsHash, committedVersion, disputePeriod]);
165
+ }
166
+ encodeDeliver(callId, declaredVersion) {
167
+ return this.escrow().interface.encodeFunctionData("deliver", [callId, declaredVersion]);
168
+ }
169
+ encodeSettle(callId) {
170
+ return this.escrow().interface.encodeFunctionData("settle", [callId]);
171
+ }
172
+ encodeRelease(callId) {
173
+ return this.escrow().interface.encodeFunctionData("release", [callId]);
174
+ }
175
+ encodeWithdrawBalance(amount) {
176
+ return this.escrow().interface.encodeFunctionData("withdrawBalance", [amount]);
177
+ }
178
+ encodeEnact(caseId, effectId) {
179
+ return this.escrow().interface.encodeFunctionData("enact", [caseId, effectId]);
180
+ }
181
+ async objectOf(callId) {
182
+ const o = await this.escrow().objectOf(callId);
183
+ return { chainId: BigInt(o.chainId), target: o.target, id: BigInt(o.id), versionHash: o.versionHash };
184
+ }
185
+ async call(callId) {
186
+ const c = await this.escrow().calls(callId);
187
+ return { client: c.client, provider: c.provider, amount: BigInt(c.amount), termsHash: c.termsHash, committedVersion: c.committedVersion, declaredVersion: c.declaredVersion, createdAt: Number(c.createdAt), deliveredAt: Number(c.deliveredAt), disputePeriod: Number(c.disputePeriod) };
188
+ }
189
+ async settlementOf(callId, ordinal) {
190
+ const s = await this.escrow().settlementOf(callId, ordinal);
191
+ // positional: the output is named `at`, which on an ethers Result is shadowed by Array.prototype.at
192
+ const state = Number(s[1]);
193
+ return { at: Number(s[0]), state, stateName: SettlementStateName[state] ?? String(state) };
194
+ }
195
+ async escrowBalanceOf(account) {
196
+ return BigInt(await this.escrow().balanceOf(account));
197
+ }
198
+ async heldOf(callId) {
199
+ return BigInt(await this.escrow().heldOf(callId));
200
+ }
201
+ // ---- registry (procedure) ----
202
+ encodeFile(bindingId, claim) {
203
+ return this.registry().interface.encodeFunctionData("file", [bindingId, claim]);
204
+ }
205
+ async getCase(caseId) {
206
+ const c = await this.registry().getCase(caseId);
207
+ const state = Number(c.state);
208
+ return { state, stateName: ProcedureStateName[state] ?? String(state), effectClass: Number(c.effectClass), claimant: c.claimant, respondent: c.respondent, source: c.source, bindingId: c.bindingId, exclusivityKey: c.exclusivityKey, decisive: Number(c.decisive) };
209
+ }
210
+ async getClock(caseId) {
211
+ const c = await this.registry().getClock(caseId);
212
+ return { eventAt: Number(c.eventAt), filedAt: Number(c.filedAt), admittedAt: Number(c.admittedAt), decisionDeadline: Number(c.decisionDeadline), hardDeadline: Number(c.hardDeadline), reviewOpenUntil: Number(c.reviewOpenUntil), roundCount: Number(c.roundCount) };
213
+ }
214
+ async getFinding(caseId, subQuestion) {
215
+ const f = await this.registry().getFinding(caseId, subQuestion);
216
+ const value = Number(f.value);
217
+ const reason = Number(f.reason);
218
+ return { resolved: Boolean(f.resolved), value, valueName: ValueName[value] ?? String(value), supportKinds: Number(f.supportKinds), acceptedBy: f.acceptedBy, reason, reasonName: ReasonName[reason] ?? String(reason), issueId: f.source.issueId };
219
+ }
220
+ async getRuling(caseId) {
221
+ const u = await this.registry().getRuling(caseId);
222
+ const state = Number(u.state);
223
+ const result = Number(u.result);
224
+ const reason = Number(u.reason);
225
+ const closure = Number(u.closure);
226
+ return { state, stateName: ProcedureStateName[state] ?? String(state), result, resultName: SubstantiveResultName[result] ?? String(result), reason, reasonName: ReasonName[reason] ?? String(reason), closure, closureName: ReasonName[closure] ?? String(closure), allowedEffect: u.allowedEffect, fallbackRef: u.fallbackRef, supportKinds: Number(u.supportKinds), judgmentIssueId: u.judgment.issueId };
227
+ }
228
+ async getEnactment(caseId, effectId) {
229
+ const e = await this.registry().getEnactment(caseId, effectId);
230
+ const status = Number(e.status);
231
+ return { status, statusName: EnactmentStatusName[status] ?? String(status), reporter: e.reporter, ref: e.ref };
232
+ }
233
+ async issueOf(caseId, subQuestion) {
234
+ return this.bridge().issueOf(keccak256(abi.encode(["bytes32", "uint32"], [caseId, subQuestion])));
235
+ }
236
+ }
237
+ /** Builders for the dispute of T6 (the delivered version is not the committed one). */
238
+ export const claims = {
239
+ /**
240
+ * The claim of a client against a provider on the delivery of a call: "the service did not run the committed
241
+ * version". Sub-question 0 (FORMAL, informative): the declared version differs from the committed one, decided by
242
+ * the verifier over the escrow's own records. Sub-question 1 (JUDGED, decisive): "the version actually used
243
+ * matches the committed version", put to FOGA-Council; the claim is sustained when the council finds it FALSE.
244
+ */
245
+ versionCommitment(p) {
246
+ return {
247
+ object: p.object,
248
+ eventRef: EVENT_DELIVERY,
249
+ proposition: keccak256(toUtf8Bytes("claim: the service did not run the committed version")),
250
+ normRef: p.termsHash,
251
+ clause: keccak256(toUtf8Bytes("terms: the service runs the committed version")),
252
+ obligationKind: KIND_RESULT,
253
+ respondent: p.provider,
254
+ actorRef: ZERO32,
255
+ requestedEffects: [EFFECT_REFUND],
256
+ effectOnSustained: EFFECT_REFUND,
257
+ effectOnNotSustained: ZERO32,
258
+ fallbackRef: DISPOSITION_STATUS_QUO,
259
+ evidenceScopeHash: p.evidenceScopeHash,
260
+ documentHash: p.documentHash,
261
+ decisive: 1,
262
+ supportsWhen: Value.FALSE,
263
+ subQuestions: [
264
+ { proposition: declaredVersionProposition(p.escrow, p.callId), qType: QuestionType.FORMAL, obligationKind: KIND_RESULT, inputsHash: declaredVersionInputsHash(p.callId), dependsOn: [], requiredValues: [] },
265
+ { proposition: STATEMENT_ACTUAL_VERSION, qType: QuestionType.JUDGED, obligationKind: KIND_RESULT, inputsHash: ZERO32, dependsOn: [], requiredValues: [] },
266
+ ],
267
+ };
268
+ },
269
+ };
270
+ /** The escrow's binding to FOGA-Service-v1: all its calls, pre-committed, the refund authorized, the escrow as executor. */
271
+ export function escrowBindingInput(policyId, termsHash) {
272
+ return { policyId, scopeKey: ZERO32, mode: AcceptanceMode.PRE_COMMIT, authorizedEffects: [EFFECT_REFUND], executors: [], beneficiaries: [], openChallenge: false, downgradeOnClock: false, validUntil: 0, termsHash };
273
+ }
@@ -0,0 +1,83 @@
1
+ import { keccak256, toUtf8Bytes } from "ethers";
2
+ import { accountRef, dataUriJcs, jcs, keccakUtf8 } from "./canonical.js";
3
+ /** ERC-8434 Agent Identity: the address-anchored identity and liveness of a cell. */
4
+ export const AIDState = { DORMANT: 0, ACTIVE: 1, STALE: 2, RETIRED: 3 };
5
+ export const AIDStateName = ["DORMANT", "ACTIVE", "STALE", "RETIRED"];
6
+ export function facetTypeKey(facetType) {
7
+ return keccak256(toUtf8Bytes(facetType));
8
+ }
9
+ /**
10
+ * A second ERC-8434 instance another standard's adapter reads. On Sepolia the ERC-8444 agent kind is anchored to
11
+ * ARD's own fixture AIDRegistry, not to aid-standard's reference instance (docs/version-lock.md, seam 1); a cell
12
+ * whose records must resolve as backed keeps its liveness in both. The world file names it under this id.
13
+ */
14
+ export const BACKING_REGISTRY_ID = "8434/AIDRegistry(ard-fixture)";
15
+ export class AID {
16
+ world;
17
+ address;
18
+ /** The backing registry, when the world has one at another address than the primary registry. */
19
+ backing;
20
+ constructor(world) {
21
+ this.world = world;
22
+ this.address = world.address("8434/AIDRegistry");
23
+ const backing = world.has(BACKING_REGISTRY_ID) ? world.address(BACKING_REGISTRY_ID) : null;
24
+ this.backing = backing && backing.toLowerCase() !== this.address.toLowerCase() ? { id: BACKING_REGISTRY_ID, address: backing } : null;
25
+ }
26
+ contract(runner) {
27
+ return this.world.contract("8434/AIDRegistry", runner);
28
+ }
29
+ /** The same ABI at another registry address (the backing registry). */
30
+ at(address, runner) {
31
+ return this.world.at("aid-standard:AIDRegistry", address, runner);
32
+ }
33
+ encodeBind(identityRegistry, agentId) {
34
+ return this.contract().interface.encodeFunctionData("bind", [identityRegistry, agentId]);
35
+ }
36
+ encodeHeartbeat() {
37
+ return this.contract().interface.encodeFunctionData("heartbeat", []);
38
+ }
39
+ encodeSetDocumentURI(uri, digest) {
40
+ return this.contract().interface.encodeFunctionData("setDocumentURI", [uri, digest]);
41
+ }
42
+ encodeSetFacet(facetType, digest, validFrom, validUntil, access, uri) {
43
+ return this.contract().interface.encodeFunctionData("setFacet", [facetTypeKey(facetType), digest, validFrom, validUntil, access, uri]);
44
+ }
45
+ encodeRetire(successor) {
46
+ return this.contract().interface.encodeFunctionData("retire", [successor]);
47
+ }
48
+ /** `window` in seconds, at most the registry's maximum; 0 restores the registry's default. */
49
+ encodeSetLivenessWindow(window) {
50
+ return this.contract().interface.encodeFunctionData("setLivenessWindow", [window]);
51
+ }
52
+ async state(anchor, registry = this.address) {
53
+ return Number(await this.at(registry).state(anchor));
54
+ }
55
+ async stateName(anchor, registry = this.address) {
56
+ return AIDStateName[await this.state(anchor, registry)];
57
+ }
58
+ async livenessWindow(anchor) {
59
+ return Number(await this.contract().livenessWindow(anchor));
60
+ }
61
+ async defaultLivenessWindow() {
62
+ return Number(await this.contract().defaultLivenessWindow());
63
+ }
64
+ async bindingOf(anchor) {
65
+ const b = await this.contract().bindingOf(anchor);
66
+ return { registry: b.registry, agentId: BigInt(b.agentId), boundAt: BigInt(b.boundAt) };
67
+ }
68
+ async documentURI(anchor) {
69
+ const r = await this.contract().documentURI(anchor);
70
+ return { uri: r.uri ?? r[0], digest: r.digest ?? r[1] };
71
+ }
72
+ async lastSeen(anchor) {
73
+ return BigInt(await this.contract().lastSeen(anchor));
74
+ }
75
+ /** The AID document and its on-chain commitment: keccak256(UTF-8(JCS(document))). */
76
+ static documentCommitment(doc) {
77
+ const canonical = jcs(doc);
78
+ return { jcs: canonical, digest: keccakUtf8(canonical), uri: dataUriJcs(doc) };
79
+ }
80
+ static aidOf(chainId, anchor) {
81
+ return accountRef(chainId, anchor);
82
+ }
83
+ }
@@ -0,0 +1,146 @@
1
+ import { keccak256, toUtf8Bytes } from "ethers";
2
+ import { abi, accountRef, dataUri, jcs, sha256Hex } from "./canonical.js";
3
+ import { eventArgs } from "./world.js";
4
+ /** ERC-8444 ARD Onchain: how cells, skills and tasks are found. */
5
+ export const ARDStatus = { None: 0, Active: 1, Suspended: 2, Retired: 3 };
6
+ export function kindId(name) {
7
+ return keccak256(toUtf8Bytes(`ard-onchain.kind.${name}`));
8
+ }
9
+ const PUBLICATION_MEMBERS = ["@context", "@id", "identifier", "trustManifest"];
10
+ const RESOLVED_MEMBERS = ["value:anchor", "value:adopted", "value:binding", "value:level", "value:status", "value:aliases", "value:canonical", "value:registry", "value:descriptorHash", "value:stale", "value:claimedUrn"];
11
+ /** The authored (content) members of a descriptor: publication and resolver-computed members removed. */
12
+ export function authoredDescriptor(entry) {
13
+ const out = {};
14
+ for (const [k, v] of Object.entries(entry)) {
15
+ if (PUBLICATION_MEMBERS.includes(k) || RESOLVED_MEMBERS.includes(k))
16
+ continue;
17
+ out[k] = v;
18
+ }
19
+ return out;
20
+ }
21
+ /** descriptorHash = SHA-256(JCS(authored descriptor)). */
22
+ export function descriptorHash(entry) {
23
+ const canonical = jcs(authoredDescriptor(entry));
24
+ const hex = sha256Hex(canonical);
25
+ return { canonical, hex, digest: `sha256:${hex.slice(2)}` };
26
+ }
27
+ export function recordIdOf(chainId, target, id) {
28
+ return keccak256(abi.encode(["uint256", "address", "uint256"], [chainId, target, id]));
29
+ }
30
+ const CONTEXT = ["https://agenticresourcediscovery.org/context/v1", { value: "https://eips.ethereum.org/assets/eip-8444/ns#" }];
31
+ export function agentDescriptor(p) {
32
+ return {
33
+ "@context": CONTEXT,
34
+ identifier: `urn:air:${p.domain ?? "foga.local"}:agents:${p.slug}`,
35
+ type: "application/a2a-agent-card+json",
36
+ displayName: p.name,
37
+ description: p.description,
38
+ url: p.url ?? "",
39
+ tags: p.tags ?? ["foga", "cell"],
40
+ capabilities: p.capabilities ?? [],
41
+ version: "1",
42
+ trustManifest: { identity: p.domain ?? "foga.local" },
43
+ "value:kind": "agent",
44
+ "value:controller": `did:pkh:eip155:${p.chainId}:${p.anchor.toLowerCase()}`,
45
+ ...(p.registration ? { "value:registration": p.registration } : {}),
46
+ ...(p.settlement ? { "value:settlement": p.settlement } : {}),
47
+ ...(p.credit ? { "value:credit": p.credit } : {}),
48
+ };
49
+ }
50
+ export function skillDescriptor(p) {
51
+ return {
52
+ "@context": CONTEXT,
53
+ identifier: `urn:air:${p.domain ?? "foga.local"}:skills:${p.slug}`,
54
+ type: "application/vnd.erc8338.skill+json",
55
+ displayName: p.name,
56
+ description: p.description,
57
+ url: p.packageURI,
58
+ tags: p.tags ?? ["foga", "skill"],
59
+ version: "1",
60
+ trustManifest: { identity: p.domain ?? "foga.local" },
61
+ "value:kind": "skill",
62
+ "value:controller": `did:pkh:eip155:${p.chainId}:${p.controller.toLowerCase()}`,
63
+ "value:terms": { unit: "call", price: p.price ?? { amount: "0", currency: "ETH", asset: `eip155:${p.chainId}/slip44:60` } },
64
+ "value:settlement": [{ rail: "onchain", payee: accountRef(p.chainId, p.payee ?? p.controller), asset: `eip155:${p.chainId}/slip44:60` }],
65
+ "value:provenance": { packageDigest: `sha256:${p.packageHash.replace(/^0x/, "")}` },
66
+ };
67
+ }
68
+ export function taskDescriptor(p) {
69
+ return {
70
+ "@context": CONTEXT,
71
+ identifier: `urn:air:${p.domain ?? "foga.local"}:tasks:${p.slug}`,
72
+ type: "application/ai-task+json",
73
+ displayName: p.name,
74
+ description: p.wants,
75
+ url: p.taskURI,
76
+ tags: ["foga", "task"],
77
+ version: "1",
78
+ trustManifest: { identity: p.domain ?? "foga.local" },
79
+ data: {
80
+ wants: p.wants,
81
+ specDigest: `sha256:${p.taskHash.replace(/^0x/, "")}`,
82
+ specURI: p.taskURI,
83
+ bounty: p.bounty,
84
+ cardinality: "1-of-1",
85
+ copies: 1,
86
+ acceptance: p.acceptance,
87
+ deadlines: { ...(p.settleBy ? { settleBy: new Date(p.settleBy * 1000).toISOString() } : {}), judgmentWindow: p.judgmentWindow },
88
+ },
89
+ "value:kind": "task",
90
+ "value:controller": `did:pkh:eip155:${p.chainId}:${p.demander.toLowerCase()}`,
91
+ "value:provenance": { packageDigest: `sha256:${p.taskHash.replace(/^0x/, "")}` },
92
+ };
93
+ }
94
+ export class ARD {
95
+ world;
96
+ address;
97
+ constructor(world) {
98
+ this.world = world;
99
+ this.address = world.address("8444/ARDOnchainRegistry");
100
+ }
101
+ contract(runner) {
102
+ return this.world.contract("8444/ARDOnchainRegistry", runner);
103
+ }
104
+ /** Publication form: descriptorURI as a data: URI, descriptorHash over the authored members, urn = identifier. */
105
+ publication(descriptor) {
106
+ return { descriptorHash: descriptorHash(descriptor).hex, descriptorURI: dataUri(descriptor), urn: String(descriptor.identifier ?? "") };
107
+ }
108
+ encodeRegister(kind, target, id, descriptor) {
109
+ const p = this.publication(descriptor);
110
+ return this.contract().interface.encodeFunctionData("register", [kind, target, id, p.descriptorHash, p.descriptorURI, p.urn]);
111
+ }
112
+ /** Recommit a record's current descriptor unchanged: the adoption of a record by its new controller. */
113
+ encodeRecommit(recordId, rec) {
114
+ return this.contract().interface.encodeFunctionData("updateDescriptor", [recordId, rec.descriptorHash, rec.descriptorURI, rec.urn]);
115
+ }
116
+ encodeUpdateDescriptor(recordId, descriptor) {
117
+ const p = this.publication(descriptor);
118
+ return this.contract().interface.encodeFunctionData("updateDescriptor", [recordId, p.descriptorHash, p.descriptorURI, p.urn]);
119
+ }
120
+ async register(signer, kind, target, id, descriptor) {
121
+ const p = this.publication(descriptor);
122
+ const tx = await this.contract(signer).register(kind, target, id, p.descriptorHash, p.descriptorURI, p.urn);
123
+ const receipt = await tx.wait();
124
+ return { recordId: eventArgs(this.contract().interface, receipt, "Registered", this.address).recordId, receipt };
125
+ }
126
+ recordIdFromReceipt(receipt) {
127
+ return eventArgs(this.contract().interface, receipt, "Registered", this.address).recordId;
128
+ }
129
+ recordIdOf(target, id) {
130
+ return recordIdOf(this.world.chainId, target, id);
131
+ }
132
+ async recordOf(recordId) {
133
+ const r = await this.contract().recordOf(recordId);
134
+ return { kind: r.kind, target: r.target, id: BigInt(r.id), descriptorHash: r.descriptorHash, descriptorURI: r.descriptorURI, urn: r.urn, status: Number(r.status), attestor: r.attestor, updatedAt: BigInt(r.updatedAt) };
135
+ }
136
+ async controllerOf(recordId) {
137
+ return this.contract().controllerOf(recordId);
138
+ }
139
+ async backed(recordId) {
140
+ return this.contract().backed(recordId);
141
+ }
142
+ async isAdopted(recordId) {
143
+ const [rec, controller] = await Promise.all([this.recordOf(recordId), this.controllerOf(recordId)]);
144
+ return controller !== "0x0000000000000000000000000000000000000000" && rec.attestor.toLowerCase() === controller.toLowerCase();
145
+ }
146
+ }