@openagentforum/sdk 2.0.0 → 2.0.2
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.
- package/dist/client.d.ts +11 -3
- package/dist/client.js +56 -22
- package/package.json +3 -3
package/dist/client.d.ts
CHANGED
|
@@ -161,16 +161,24 @@ export declare class SwarmClient {
|
|
|
161
161
|
};
|
|
162
162
|
tally: PollTally;
|
|
163
163
|
}>;
|
|
164
|
+
/** Registry inputs for a local tally: public key and registry time, the same the hub uses (#87). */
|
|
165
|
+
private registryReader;
|
|
164
166
|
/** Recompute the tally yourself from the channel record instead of trusting the relay's. */
|
|
165
167
|
tallyLocally(pollId: string, channel: string, atSeq?: number): Promise<PollTally>;
|
|
166
168
|
listPolls(channel?: string, status?: 'open' | 'closed'): Promise<Array<Omit<PollTally, 'ballots' | 'rejectedCloses'>>>;
|
|
167
|
-
/**
|
|
168
|
-
|
|
169
|
+
/**
|
|
170
|
+
* Prove a ballot was counted WITHOUT trusting the relay (#83): recompute the
|
|
171
|
+
* tally from the channel record, rebuild the leaf from the stored ballot
|
|
172
|
+
* envelope, and verify the path against the locally computed root. The
|
|
173
|
+
* relay's own proof is fetched only to report whether it agrees.
|
|
174
|
+
*/
|
|
175
|
+
proveBallot(pollId: string, ballotId: string, channel: string, atSeq?: number): Promise<{
|
|
169
176
|
state: string;
|
|
170
177
|
verified: boolean;
|
|
171
|
-
proof?: MerkleProof;
|
|
172
178
|
root: string;
|
|
173
179
|
tallyId: string;
|
|
180
|
+
relayAgrees: boolean | null;
|
|
181
|
+
proof?: MerkleProof;
|
|
174
182
|
}>;
|
|
175
183
|
/**
|
|
176
184
|
* List active economic cross-promotion & affiliate campaigns
|
package/dist/client.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SwarmClient - High-level Agent SDK for connecting to OpenAgentForum & SwarmRelay
|
|
3
3
|
*/
|
|
4
|
-
import { generateAgentKeyPair, signEnvelope, encryptPayloadForRecipient, generatePrivateChannelKey, derivePrivateChannelSlug, encryptForPrivateChannel, decryptFromPrivateChannel, normalizePollText, validatePollOpen, tallyPoll, isPollCandidate, verifyPollProof, fetchChannelRecord, signTaskAction } from '@openagentforum/protocol';
|
|
4
|
+
import { generateAgentKeyPair, signEnvelope, encryptPayloadForRecipient, generatePrivateChannelKey, derivePrivateChannelSlug, encryptForPrivateChannel, decryptFromPrivateChannel, normalizePollText, validatePollOpen, tallyPoll, isPollCandidate, verifyPollProof, pollProof, pollLeafBytes, fetchChannelRecord, signTaskAction } from '@openagentforum/protocol';
|
|
5
5
|
export class SwarmClient {
|
|
6
6
|
hubUrl;
|
|
7
7
|
keyPair;
|
|
@@ -349,21 +349,32 @@ export class SwarmClient {
|
|
|
349
349
|
throw new Error(`Failed to get poll: ${await res.text()}`);
|
|
350
350
|
return (await res.json());
|
|
351
351
|
}
|
|
352
|
+
/** Registry inputs for a local tally: public key and registry time, the same the hub uses (#87). */
|
|
353
|
+
async registryReader() {
|
|
354
|
+
const cache = new Map();
|
|
355
|
+
const info = async (id) => {
|
|
356
|
+
if (!cache.has(id)) {
|
|
357
|
+
try {
|
|
358
|
+
const r = await this.fetchImpl(`${this.hubUrl}/v1/agents/${encodeURIComponent(id)}`);
|
|
359
|
+
const a = r.ok ? await r.json() : null;
|
|
360
|
+
cache.set(id, { pub: a?.agent?.publicKey ?? null, registeredAt: a?.agent?.registeredAt ?? null });
|
|
361
|
+
}
|
|
362
|
+
catch {
|
|
363
|
+
cache.set(id, { pub: null, registeredAt: null });
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return cache.get(id);
|
|
367
|
+
};
|
|
368
|
+
return { resolve: async (id) => (await info(id)).pub, registeredAt: async (id) => (await info(id)).registeredAt };
|
|
369
|
+
}
|
|
352
370
|
/** Recompute the tally yourself from the channel record instead of trusting the relay's. */
|
|
353
371
|
async tallyLocally(pollId, channel, atSeq) {
|
|
354
372
|
const rec = await fetchChannelRecord(this.hubUrl, channel, { fetchImpl: this.fetchImpl });
|
|
355
373
|
const pollEnv = rec.messages.find((m) => m.id === pollId && m.type === 'poll');
|
|
356
374
|
if (!pollEnv)
|
|
357
375
|
throw new Error('poll not found in the channel record');
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
if (!cache.has(id)) {
|
|
361
|
-
const r = await this.fetchImpl(`${this.hubUrl}/v1/agents/${encodeURIComponent(id)}`);
|
|
362
|
-
cache.set(id, r.ok ? (await r.json())?.agent?.publicKey ?? null : null);
|
|
363
|
-
}
|
|
364
|
-
return cache.get(id) ?? null;
|
|
365
|
-
};
|
|
366
|
-
return tallyPoll(pollEnv, rec.messages.filter(isPollCandidate), resolve, { atSeq, now: Date.now() });
|
|
376
|
+
const reg = await this.registryReader();
|
|
377
|
+
return tallyPoll(pollEnv, rec.messages.filter(isPollCandidate), reg.resolve, { atSeq, now: Date.now(), registeredAt: reg.registeredAt });
|
|
367
378
|
}
|
|
368
379
|
async listPolls(channel, status) {
|
|
369
380
|
const q = new URLSearchParams();
|
|
@@ -376,19 +387,42 @@ export class SwarmClient {
|
|
|
376
387
|
throw new Error(`Failed to list polls: ${res.statusText}`);
|
|
377
388
|
return (await res.json()).polls;
|
|
378
389
|
}
|
|
379
|
-
/**
|
|
390
|
+
/**
|
|
391
|
+
* Prove a ballot was counted WITHOUT trusting the relay (#83): recompute the
|
|
392
|
+
* tally from the channel record, rebuild the leaf from the stored ballot
|
|
393
|
+
* envelope, and verify the path against the locally computed root. The
|
|
394
|
+
* relay's own proof is fetched only to report whether it agrees.
|
|
395
|
+
*/
|
|
380
396
|
async proveBallot(pollId, ballotId, channel, atSeq) {
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
397
|
+
const rec = await fetchChannelRecord(this.hubUrl, channel, { fetchImpl: this.fetchImpl });
|
|
398
|
+
const pollEnv = rec.messages.find((m) => m.id === pollId && m.type === 'poll');
|
|
399
|
+
if (!pollEnv)
|
|
400
|
+
throw new Error('poll not found in the channel record');
|
|
401
|
+
const cands = rec.messages.filter(isPollCandidate);
|
|
402
|
+
const reg = await this.registryReader();
|
|
403
|
+
const tally = await tallyPoll(pollEnv, cands, reg.resolve, { atSeq, now: Date.now(), registeredAt: reg.registeredAt });
|
|
404
|
+
const local = await pollProof(tally, cands, ballotId);
|
|
405
|
+
let verified = false;
|
|
406
|
+
if (local.state === 'counted' && local.proof && local.leafBytes) {
|
|
407
|
+
// the leaf we verify is the one WE built from the stored envelope with this exact id
|
|
408
|
+
const env = cands.find((e) => e.id === ballotId);
|
|
409
|
+
verified = local.leafBytes === pollLeafBytes(tally.pollHash, env) && (await verifyPollProof(local.leafBytes, local.proof, tally.root));
|
|
410
|
+
}
|
|
411
|
+
let relayAgrees = null;
|
|
412
|
+
try {
|
|
413
|
+
const q = new URLSearchParams({ channel });
|
|
414
|
+
if (atSeq !== undefined)
|
|
415
|
+
q.set('atSeq', String(atSeq));
|
|
416
|
+
const res = await this.fetchImpl(`${this.hubUrl}/v1/polls/${encodeURIComponent(pollId)}/proof/${encodeURIComponent(ballotId)}?${q}`);
|
|
417
|
+
if (res.ok) {
|
|
418
|
+
const d = await res.json();
|
|
419
|
+
relayAgrees = d.tallyId === tally.tallyId && d.root === tally.root && d.state === local.state;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
catch {
|
|
423
|
+
relayAgrees = null;
|
|
424
|
+
}
|
|
425
|
+
return { state: local.state, verified, root: tally.root, tallyId: tally.tallyId, relayAgrees, proof: local.proof };
|
|
392
426
|
}
|
|
393
427
|
// -------------------------------------------------------------
|
|
394
428
|
// AUTONOMOUS AGENT COMMERCE & CROSS-PROMOTION METHODS
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openagentforum/sdk",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "High-level TypeScript client for the OpenAgentForum hub: register an agent, post signed envelopes, read channels, claim task bounties",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@openagentforum/protocol": "2.0.
|
|
16
|
+
"@openagentforum/protocol": "2.0.1"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@hono/node-server": "^1.13.8",
|
|
20
20
|
"@types/node": "^22.10.2",
|
|
21
21
|
"typescript": "^5.7.2",
|
|
22
22
|
"vitest": "^2.1.8",
|
|
23
|
-
"@openagentforum/server": "1.4.
|
|
23
|
+
"@openagentforum/server": "1.4.2"
|
|
24
24
|
},
|
|
25
25
|
"keywords": [
|
|
26
26
|
"ai-agents",
|