@sage-protocol/sdk 0.1.21 → 0.1.24

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.
@@ -14,7 +14,7 @@ var require_package = __commonJS({
14
14
  "package.json"(exports, module) {
15
15
  module.exports = {
16
16
  name: "@sage-protocol/sdk",
17
- version: "0.1.21",
17
+ version: "0.1.24",
18
18
  description: "Backend-agnostic SDK for interacting with the Sage Protocol (governance, SubDAOs, tokens).",
19
19
  main: "dist/index.cjs",
20
20
  module: "dist/index.mjs",
@@ -106,20 +106,26 @@ var require_abi = __commonJS({
106
106
  var SubDAO = [
107
107
  "function governor() view returns (address)",
108
108
  "function timelock() view returns (address)",
109
+ // SubDAO ops Safe / treasury (multisig) address. On newer deployments opsSafe() is an alias.
110
+ "function treasury() view returns (address)",
111
+ "function opsSafe() view returns (address)",
109
112
  "function getGovernanceMode() view returns (uint8)",
113
+ // New governance profile (3-axis model). May be unavailable on legacy SubDAOs.
114
+ "function profileInitialized() view returns (bool)",
115
+ "function getGovernanceProfile() view returns (uint8 kind, uint8 proposalAccess, uint8 executionAccess)",
110
116
  "function promptRegistry() view returns (address)",
111
117
  "function sageTreasury() view returns (address)",
112
- "function stakeToken() view returns (address)",
113
- "function minStakeAmount() view returns (uint256)",
114
118
  "function accessModel() view returns (uint8)",
115
119
  "function membershipPolicy() view returns (uint8)",
116
120
  "function profileCID() view returns (string)",
117
121
  "function factoryDeployer() view returns (address)",
118
- "function stakedAmount(address) view returns (uint256)",
119
122
  "function userForkCount(address) view returns (uint256)",
120
123
  "function forkCount(string) view returns (uint256)",
121
- "function stake(uint256)",
122
- "function unstake(uint256)"
124
+ // Fork functions
125
+ "function forkSubDAO(string,string)",
126
+ "function forkSubDAO(string,string,bool)",
127
+ "function forkSubDAOWithStable(string,string,(uint256,uint256,uint8,bytes32,bytes32))",
128
+ "function forkSubDAOWithStable(string,string,(uint256,uint256,uint8,bytes32,bytes32),bool)"
123
129
  ];
124
130
  var Factory = [
125
131
  "event SubDAOGovernanceDeployed(address indexed subDAO, address indexed governor, address timelock, address treasury)"
@@ -154,13 +160,13 @@ var require_abi = __commonJS({
154
160
  var FactoryWrite = [
155
161
  "function createSubDAO(string name, string description, uint8 accessModel, uint256 minStakeAmount, uint256 burnAmount) returns (address subDAO, address registry)",
156
162
  "function createSubDAOWithStable(string name, string description, uint8 accessModel, uint256 minStakeAmount, (uint256 value,uint256 deadline,uint8 v,bytes32 r,bytes32 s) permit) returns (address subDAO, address registry)",
157
- "function createSubDAOWithParams(string name, string description, uint8 accessModel, uint256 minStakeAmount, uint256 burnAmount, uint256 votingDelay, uint256 votingPeriod, uint256 proposalThreshold, uint256 quorumPercentage, uint8 initialForkPolicy, uint8 initialMembershipPolicy, string profileCID) returns (address subDAO, address registry)",
163
+ "function createSubDAOWithParams(string name, string description, uint8 accessModel, uint256 minStakeAmount, uint256 burnAmount, uint256 votingDelay, uint256 votingPeriod, uint256 proposalThreshold, uint256 quorumPercentage, uint8 initialForkPolicy, uint8 initialMembershipPolicy, string profileCID, string manifestCID, string manifestVersion) returns (address subDAO, address registry)",
158
164
  "function createSubDAOOperatorWithStable(string name, string description, uint8 accessModel, uint256 minStakeAmount, address operatorExecutor, address operatorAdmin, (uint256 value,uint256 deadline,uint8 v,bytes32 r,bytes32 s) permit) returns (address subDAO, address registry)",
159
165
  "function createSubDAOOperatorWithStableAdvanced(string name, string description, uint8 accessModel, uint256 minStakeAmount, address operatorExecutor, address operatorAdmin, bool anyoneExec, bool governorProposer, (uint256 value,uint256 deadline,uint8 v,bytes32 r,bytes32 s) permit) returns (address subDAO, address registry)",
160
166
  "function createSubDAOOperator(string name, string description, uint8 accessModel, uint256 minStakeAmount, uint256 burnAmount, address operatorExecutor, address operatorAdmin) returns (address subDAO, address registry)",
161
167
  "function createSubDAOOperatorAdvanced(string name, string description, uint8 accessModel, uint256 minStakeAmount, uint256 burnAmount, address operatorExecutor, address operatorAdmin, bool anyoneExec, bool governorProposer) returns (address subDAO, address registry)",
162
- "function createForkedSubDAO(string newName, string newDescription, string originalName, address originalSubDAO, address forker) returns (address subDAO, address registry)",
163
- "function createForkedSubDAOWithStable(string newName, string newDescription, string originalName, address originalSubDAO, address forker, uint64 authorizationNonce, (uint256 value,uint256 deadline,uint8 v,bytes32 r,bytes32 s) permit) returns (address subDAO, address registry)"
168
+ "function createForkedSubDAO(string newName, string newDescription, string originalName, address originalSubDAO, address forker, bool copyLibrary) returns (address subDAO, address registry)",
169
+ "function createForkedSubDAOWithStable(string newName, string newDescription, string originalName, address originalSubDAO, address forker, uint64 authorizationNonce, (uint256 value,uint256 deadline,uint8 v,bytes32 r,bytes32 s) permit, bool copyLibrary) returns (address subDAO, address registry)"
164
170
  ];
165
171
  var TemplateModule = [
166
172
  "function getActiveTemplates() view returns (uint256[])",
@@ -171,18 +177,21 @@ var require_abi = __commonJS({
171
177
  "function maxCreationBurn() view returns (uint256)"
172
178
  ];
173
179
  var LibraryRegistry = [
174
- "function libraryByDAO(address) view returns (string manifestCID, address lastUpdater, uint256 lastUpdated, string version, address forkedFromDAO, uint256 sxxxForkFee)",
180
+ "function libraryByDAO(address) view returns (string manifestCID, address lastUpdater, uint256 lastUpdated, string version, address forkedFromDAO, uint256 sxxxForkFee, uint256 nonce)",
175
181
  "function daoTimelock(address) view returns (address)",
176
- "function getLibrary(address) view returns (tuple(string manifestCID, address lastUpdater, uint256 lastUpdated, string version, address forkedFromDAO, uint256 sxxxForkFee))",
182
+ "function getLibrary(address) view returns (tuple(string manifestCID, address lastUpdater, uint256 lastUpdated, string version, address forkedFromDAO, uint256 sxxxForkFee, uint256 nonce))",
177
183
  "function getLibraryForkFee(address dao) view returns (uint256)",
178
184
  "function setLibraryForkFee(address dao, uint256 fee)",
179
185
  "function updateLibrary(address dao, string manifestCID, string version)",
186
+ "function updateLibraryCAS(address dao, string manifestCID, string version, uint256 expectedNonce)",
180
187
  "function registerDAO(address dao, address timelock)",
181
188
  "function registerForkedDAO(address childDAO, address childTimelock, address parentDAO, string manifestCID, string version)",
182
- "event LibraryUpdated(address indexed dao, string manifestCID, address indexed timelock, string version)",
189
+ "function initializeLibraryFromFork(address sourceDao, address forkedDao)",
190
+ "event LibraryUpdated(address indexed dao, string manifestCID, address indexed timelock, string version, uint256 nonce)",
183
191
  "event DAORegistered(address indexed dao, address indexed timelock)",
184
- "event LibraryForked(address indexed parentDAO, address indexed childDAO)",
185
- "event LibraryForkFeeUpdated(address indexed dao, uint256 fee)"
192
+ "event LibraryForked(address indexed sourceDao, address indexed forkedDao, string manifestCID)",
193
+ "event LibraryForkFeeUpdated(address indexed dao, uint256 fee)",
194
+ "error NonceMismatch(uint256 expected, uint256 actual)"
186
195
  ];
187
196
  var PromptRegistry = [
188
197
  "function prompts(string key) view returns (string cid, uint256 version, uint256 timestamp, address author, string forkedFromCID, address originalAuthor, bool isFork, uint256 forkDepth)",
@@ -209,14 +218,23 @@ var require_abi = __commonJS({
209
218
  ];
210
219
  var Governor = [
211
220
  "function name() view returns (string)",
221
+ // ERC6372 clock (Governor uses this for vote snapshots)
222
+ "function clock() view returns (uint48)",
212
223
  "function timelock() view returns (address)",
213
224
  "function _executor() view returns (address)",
214
225
  "function sxxxToken() view returns (address)",
215
226
  "function votingDelay() view returns (uint256)",
216
227
  "function votingPeriod() view returns (uint256)",
217
228
  "function proposalThreshold() view returns (uint256)",
229
+ "function minVotesToVote() view returns (uint256)",
230
+ // OZ Governor: voting power at a past timepoint
231
+ "function getVotes(address,uint256) view returns (uint256)",
218
232
  // OZ Governor: quorum at a past block
219
233
  "function quorum(uint256) view returns (uint256)",
234
+ // Fixed quorumVotes model (PromptGovernor v2+)
235
+ "function quorumVotes() view returns (uint256)",
236
+ "function setQuorumVotes(uint256)",
237
+ // Legacy OZ Governor: percentage-based quorum (deprecated)
220
238
  "function quorumNumerator() view returns (uint256)",
221
239
  "function proposalStakeAmount(uint256) view returns (uint256)",
222
240
  "function state(uint256) view returns (uint8)",
@@ -229,9 +247,24 @@ var require_abi = __commonJS({
229
247
  "function castVoteWithReason(uint256 proposalId, uint8 support, string reason) returns (uint256)",
230
248
  "function queue(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash)",
231
249
  "function execute(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash)",
250
+ "function cancel(address[] targets, uint256[] values, bytes[] calldatas, bytes32 descriptionHash) returns (uint256)",
232
251
  // Some Governors expose id-based queue/execute via TimelockController
233
252
  "function queue(uint256)",
234
- "function execute(uint256)"
253
+ "function execute(uint256)",
254
+ // Threshold model (PromptGovernor)
255
+ "function proposalProposer(uint256 proposalId) view returns (address)",
256
+ "function proposalThresholdSnapshot(uint256 proposalId) view returns (uint256)",
257
+ "function proposalCreator(uint256 proposalId) view returns (address)",
258
+ "function proposalCooldown() view returns (uint256)",
259
+ "function lastProposalTimestamp(address) view returns (uint256)",
260
+ "function getProposalCooldownInfo(address) view returns (bool canPropose, uint256 cooldownRemaining)",
261
+ "function canCreateProposal(address) view returns (bool)",
262
+ "function setMinVotesToVote(uint256)",
263
+ // Multiplier helpers
264
+ "function baseVotesToken() view returns (address)",
265
+ "function isMultiplierEnabled() view returns (bool)",
266
+ "function multiplierNFT() view returns (address)",
267
+ "function getVotingBreakdown(address) view returns (uint256 baseVotes, uint256 multiplier, uint256 effectiveVotes)"
235
268
  ];
236
269
  var Timelock = [
237
270
  "function getMinDelay() view returns (uint256)",
@@ -451,13 +484,11 @@ var require_abi = __commonJS({
451
484
  "function governanceConfig() view returns (address)",
452
485
  "function communityTreasury() view returns (address)",
453
486
  "function governanceContract() view returns (address)",
454
- "function libraryRegistry() view returns (address)",
455
487
  "function subdao() view returns (address)",
456
488
  "function soulboundBadge() view returns (address)",
457
489
  // Admin
458
490
  "function setGovernanceContract(address _gov)",
459
491
  "function setCommunityTreasury(address _treasury)",
460
- "function setLibraryRegistry(address _registry)",
461
492
  "function setSoulboundBadge(address _sbtAddress)",
462
493
  "function grantRole(bytes32 role, address account)",
463
494
  "function revokeRole(bytes32 role, address account)",
@@ -471,7 +502,7 @@ var require_abi = __commonJS({
471
502
  "event VotingStarted(uint256 indexed bountyId, uint256 votingEndTime)",
472
503
  "event BountyCompleted(uint256 indexed bountyId, address indexed winner, uint256 reward, string deliverable)",
473
504
  "event BountyAutoApproved(uint256 indexed bountyId, address indexed winner)",
474
- "event PromptAddedToLibrary(uint256 indexed bountyId, string libraryKey, string promptIPFS)",
505
+ "event BountyWinnerSelected(uint256 indexed bountyId, address indexed subdao, string libraryKey, string winnerCid, address indexed winner, uint8 libraryAction)",
475
506
  "event BountyCancelled(uint256 indexed bountyId, address indexed creator)",
476
507
  "event BountyExpired(uint256 indexed bountyId)",
477
508
  "event BountyRewardIncreased(uint256 indexed bountyId, uint256 additionalReward, uint256 newTotal)",
@@ -1107,19 +1138,37 @@ var require_subgraph = __commonJS({
1107
1138
  updatedAt: Number(p.updatedAt || 0)
1108
1139
  };
1109
1140
  },
1110
- async getProposalById({ url, id }) {
1141
+ async getProposalById({ url, id, governor }) {
1111
1142
  if (!url) throw new Error("subgraph url required");
1112
- const doc = `query($id: ID!){ proposal(id:$id){ id proposer description createdAt updatedAt state eta targets values calldatas } }`;
1113
- const data = await query(url, doc, { id: String(id) });
1114
- const p = data?.proposal;
1143
+ let p = null;
1144
+ if (governor) {
1145
+ const govLower = String(governor).toLowerCase();
1146
+ const compositeId = `${govLower}-${String(id)}`;
1147
+ const doc = `query($id: ID!){ proposal(id:$id){ id proposer description createdAt updatedAt state eta targets values calldatas } }`;
1148
+ const data = await query(url, doc, { id: compositeId });
1149
+ p = data?.proposal;
1150
+ }
1151
+ if (!p && governor) {
1152
+ const govLower = String(governor).toLowerCase();
1153
+ const doc = `query($gov: Bytes!, $first: Int!){ proposals(where:{governor:$gov}, first:$first, orderBy:createdAt, orderDirection:desc){ id proposer description createdAt updatedAt state eta targets values calldatas } }`;
1154
+ const data = await query(url, doc, { gov: govLower, first: 100 });
1155
+ const proposals = data?.proposals || [];
1156
+ p = proposals.find((prop) => {
1157
+ const parts = String(prop.id || "").split("-");
1158
+ const propId = parts.length > 1 ? parts[parts.length - 1] : prop.id;
1159
+ return String(propId) === String(id) || propId && String(BigInt(propId)) === String(BigInt(id));
1160
+ });
1161
+ }
1115
1162
  if (!p) return null;
1116
1163
  try {
1117
1164
  const proposer = safeGetAddress(p.proposer);
1118
1165
  if (!proposer) return null;
1119
1166
  const targets = (p.targets || []).map((t) => safeGetAddress(t)).filter(Boolean);
1120
1167
  if (!targets.length && (p.targets || []).length) return null;
1168
+ const idParts = String(p.id || "").split("-");
1169
+ const actualId = idParts.length > 1 ? idParts[idParts.length - 1] : p.id;
1121
1170
  return {
1122
- id: BigInt(p.id),
1171
+ id: BigInt(actualId),
1123
1172
  proposer,
1124
1173
  description: p.description || "",
1125
1174
  createdAt: Number(p.createdAt || 0),
@@ -1292,8 +1341,9 @@ var require_ipfs = __commonJS({
1292
1341
  baseUrl: removeTrailingSlash(options.workerBaseUrl || env.SAGE_IPFS_WORKER_URL || ""),
1293
1342
  uploadUrl: removeTrailingSlash(options.workerUploadUrl || env.SAGE_IPFS_UPLOAD_URL || ""),
1294
1343
  token: options.workerUploadToken || env.SAGE_IPFS_UPLOAD_TOKEN || "",
1295
- challengePath: options.workerChallengePath || env.SAGE_IPFS_WORKER_CHALLENGE_PATH || "/auth/challenge",
1344
+ challengePath: options.workerChallengePath || env.SAGE_IPFS_WORKER_CHALLENGE_PATH || "/ipfs/auth/challenge",
1296
1345
  uploadPath: options.workerUploadPath || env.SAGE_IPFS_WORKER_UPLOAD_PATH || "/ipfs/upload",
1346
+ uploadRawPath: options.workerUploadRawPath || env.SAGE_IPFS_WORKER_UPLOAD_RAW_PATH || "/ipfs/upload-raw",
1297
1347
  pinPath: options.workerPinPath || env.SAGE_IPFS_WORKER_PIN_PATH || "/ipfs/pin",
1298
1348
  warmPath: options.workerWarmPath || env.SAGE_IPFS_WORKER_WARM_PATH || "/ipfs/warm",
1299
1349
  pinUrl: removeTrailingSlash(options.workerPinUrl || env.SAGE_IPFS_WORKER_PIN_URL || ""),
@@ -1301,11 +1351,13 @@ var require_ipfs = __commonJS({
1301
1351
  signer: options.workerSigner || options.signer || null,
1302
1352
  getAuth: options.workerGetAuth || null,
1303
1353
  address: options.workerAddress || env.SAGE_SANDBOX_ADDRESS || "",
1304
- discoveryEventsPath: options.workerDiscoveryEventsPath || env.SAGE_IPFS_WORKER_DISCOVERY_EVENTS_PATH || "/discover/events",
1305
- discoveryMcpPath: options.workerDiscoveryMcpPath || env.SAGE_IPFS_WORKER_DISCOVERY_MCP_PATH || "/discover/mcp",
1306
- discoveryLaunchPath: options.workerDiscoveryLaunchPath || env.SAGE_IPFS_WORKER_DISCOVERY_LAUNCH_PATH || "/discover/launch",
1354
+ // All event types route to /trends/usage - worker tracks usage for trend scoring
1355
+ discoveryEventsPath: options.workerDiscoveryEventsPath || env.SAGE_IPFS_WORKER_DISCOVERY_EVENTS_PATH || "/trends/usage",
1356
+ discoveryMcpPath: options.workerDiscoveryMcpPath || env.SAGE_IPFS_WORKER_DISCOVERY_MCP_PATH || "/trends/usage",
1357
+ discoveryLaunchPath: options.workerDiscoveryLaunchPath || env.SAGE_IPFS_WORKER_DISCOVERY_LAUNCH_PATH || "/trends/usage",
1307
1358
  discoveryFeedPath: options.workerDiscoveryFeedPath || env.SAGE_IPFS_WORKER_DISCOVERY_FEED_PATH || "/discover",
1308
- discoveryMetricsPath: options.workerDiscoveryMetricsPath || env.SAGE_IPFS_WORKER_DISCOVERY_METRICS_PATH || "/discover/metrics",
1359
+ // Metrics uses /discover/stats which returns index statistics
1360
+ discoveryMetricsPath: options.workerDiscoveryMetricsPath || env.SAGE_IPFS_WORKER_DISCOVERY_METRICS_PATH || "/discover/stats",
1309
1361
  governanceReportPath: options.workerGovernanceReportPath || env.SAGE_IPFS_WORKER_GOVERNANCE_REPORT_PATH || "/governance/report",
1310
1362
  governanceReportsPath: options.workerGovernanceReportsPath || env.SAGE_IPFS_WORKER_GOVERNANCE_REPORTS_PATH || "/governance/reports",
1311
1363
  governanceReviewPath: options.workerGovernanceReviewPath || env.SAGE_IPFS_WORKER_GOVERNANCE_REVIEW_PATH || "/governance/report/review",
@@ -1339,8 +1391,9 @@ var require_ipfs = __commonJS({
1339
1391
  const base = workerBaseUrl();
1340
1392
  const ensure = (path) => path.startsWith("/") ? path : `/${path}`;
1341
1393
  if (base) {
1342
- if (kind === "challenge") return `${base}${ensure(config.worker.challengePath || "/auth/challenge")}`;
1394
+ if (kind === "challenge") return `${base}${ensure(config.worker.challengePath || "/ipfs/auth/challenge")}`;
1343
1395
  if (kind === "upload") return `${base}${ensure(config.worker.uploadPath || "/ipfs/upload")}`;
1396
+ if (kind === "upload-raw") return `${base}${ensure(config.worker.uploadRawPath || "/ipfs/upload-raw")}`;
1344
1397
  if (kind === "pin") return `${base}${ensure(config.worker.pinPath || "/ipfs/pin")}`;
1345
1398
  if (kind === "warm") return `${base}${ensure(config.worker.warmPath || "/ipfs/warm")}`;
1346
1399
  if (kind === "status") return `${base}${ensure(config.worker.pinPath || "/ipfs/pin")}/${cid}`;
@@ -1375,6 +1428,26 @@ var require_ipfs = __commonJS({
1375
1428
  const response = await axiosInstance.get(url, { headers, params, timeout: config.timeoutMs });
1376
1429
  return response?.data;
1377
1430
  }
1431
+ async function withRetry(fn, { retries = 2, baseMs = 500 } = {}) {
1432
+ let attempt = 0;
1433
+ let lastErr;
1434
+ while (attempt <= retries) {
1435
+ try {
1436
+ return await fn();
1437
+ } catch (e) {
1438
+ lastErr = e;
1439
+ const status = e?.response?.status;
1440
+ if (status && status >= 400 && status < 500 && status !== 429) {
1441
+ throw e;
1442
+ }
1443
+ if (attempt === retries) break;
1444
+ const delay = baseMs * Math.pow(2, attempt);
1445
+ await new Promise((r) => setTimeout(r, delay));
1446
+ attempt++;
1447
+ }
1448
+ }
1449
+ throw lastErr;
1450
+ }
1378
1451
  async function buildWorkerAuthHeaders(extraHeaders = {}) {
1379
1452
  if (config.worker.token) {
1380
1453
  return { headers: { ...extraHeaders, Authorization: `Bearer ${config.worker.token}` }, auth: null };
@@ -1447,6 +1520,51 @@ var require_ipfs = __commonJS({
1447
1520
  if (warm || config.shouldWarm) await warmGateways(cid, { gateways });
1448
1521
  return { cid, provider: "worker", response: response?.data || null };
1449
1522
  }
1523
+ async function uploadRawViaWorker(content, { name, warm, gateways } = {}) {
1524
+ const { headers, auth } = await buildWorkerAuthHeaders({ "Content-Type": "application/json" });
1525
+ const body = { content };
1526
+ if (auth) body.auth = auth;
1527
+ if (name) body.name = name;
1528
+ const response = await axiosInstance.post(workerUrl("upload-raw"), body, { headers, timeout: config.timeoutMs });
1529
+ const cid = response?.data?.cid || response?.data?.IpfsHash;
1530
+ if (!cid) {
1531
+ throw new Error("Worker response missing cid");
1532
+ }
1533
+ if (warm || config.shouldWarm) await warmGateways(cid, { gateways });
1534
+ return { cid, provider: "worker", response: response?.data || null };
1535
+ }
1536
+ async function uploadRawJson(content, options2 = {}) {
1537
+ if (!content || typeof content !== "object") {
1538
+ throw new Error("content object required");
1539
+ }
1540
+ const { providers, provider, warm, gateways, name = "manifest.json" } = options2;
1541
+ const order = resolveProviderOrder(providers || provider);
1542
+ if (!order.length) {
1543
+ throw new Error("No IPFS providers configured");
1544
+ }
1545
+ const errors = [];
1546
+ for (const candidate of order) {
1547
+ try {
1548
+ if (candidate === "worker") {
1549
+ return await uploadRawViaWorker(content, { name, warm, gateways });
1550
+ }
1551
+ if (candidate === "pinata") {
1552
+ return await uploadToPinata(content, { filename: name, warm, gateways });
1553
+ }
1554
+ if (candidate === "web3") {
1555
+ return await uploadToWeb3Storage(content, { warm, gateways });
1556
+ }
1557
+ if (candidate === "simulate") {
1558
+ return await uploadSimulated(content, { warm, gateways });
1559
+ }
1560
+ } catch (error2) {
1561
+ errors.push({ provider: candidate, error: error2 });
1562
+ }
1563
+ }
1564
+ const error = new Error("All IPFS providers failed");
1565
+ error.details = errors;
1566
+ throw error;
1567
+ }
1450
1568
  async function uploadToPinata(payload, { filename = "payload.json", metadata, warm, gateways } = {}) {
1451
1569
  if (!hasPinataCreds()) {
1452
1570
  throw new Error("Missing Pinata credentials");
@@ -1651,28 +1769,28 @@ var require_ipfs = __commonJS({
1651
1769
  }
1652
1770
  async function recordDiscoveryEvent(event = {}) {
1653
1771
  if (!event || typeof event !== "object") throw new Error("event required");
1654
- const promptId = typeof event.promptId === "string" ? event.promptId.trim() : "";
1655
- if (!promptId) throw new Error("promptId required");
1656
- const payload = { ...event, promptId };
1657
- return postWorkerJson("discoveryEventsPath", "/discover/events", payload);
1658
- }
1659
- async function recordMcpUsageEvent({ promptId, metadata, address } = {}) {
1660
- const id = typeof promptId === "string" ? promptId.trim() : "";
1661
- if (!id) throw new Error("promptId required");
1662
- const payload = { promptId: id };
1772
+ const cid = typeof event.promptId === "string" ? event.promptId.trim() : typeof event.cid === "string" ? event.cid.trim() : "";
1773
+ if (!cid) throw new Error("promptId or cid required");
1774
+ const payload = { ...event, cid };
1775
+ return withRetry(() => postWorkerJson("discoveryEventsPath", "/trends/usage", payload));
1776
+ }
1777
+ async function recordMcpUsageEvent({ promptId, cid, metadata, address } = {}) {
1778
+ const id = typeof promptId === "string" ? promptId.trim() : typeof cid === "string" ? cid.trim() : "";
1779
+ if (!id) throw new Error("promptId or cid required");
1780
+ const payload = { cid: id };
1663
1781
  if (metadata && typeof metadata === "object") payload.metadata = metadata;
1664
1782
  const actor = address || config.worker.address;
1665
1783
  if (actor) payload.address = actor;
1666
- return postWorkerJson("discoveryMcpPath", "/discover/mcp", payload);
1784
+ return withRetry(() => postWorkerJson("discoveryMcpPath", "/trends/usage", payload));
1667
1785
  }
1668
- async function recordLaunchEvent({ promptId, metadata, address } = {}) {
1669
- const id = typeof promptId === "string" ? promptId.trim() : "";
1670
- if (!id) throw new Error("promptId required");
1671
- const payload = { promptId: id };
1786
+ async function recordLaunchEvent({ promptId, cid, metadata, address } = {}) {
1787
+ const id = typeof promptId === "string" ? promptId.trim() : typeof cid === "string" ? cid.trim() : "";
1788
+ if (!id) throw new Error("promptId or cid required");
1789
+ const payload = { cid: id };
1672
1790
  if (metadata && typeof metadata === "object") payload.metadata = metadata;
1673
1791
  const actor = address || config.worker.address;
1674
1792
  if (actor) payload.address = actor;
1675
- return postWorkerJson("discoveryLaunchPath", "/discover/launch", payload);
1793
+ return withRetry(() => postWorkerJson("discoveryLaunchPath", "/trends/usage", payload));
1676
1794
  }
1677
1795
  async function submitGovernanceReport(payload = {}) {
1678
1796
  if (!payload || typeof payload !== "object") throw new Error("payload required");
@@ -1734,6 +1852,7 @@ var require_ipfs = __commonJS({
1734
1852
  uploadPrompt,
1735
1853
  uploadJson,
1736
1854
  uploadText,
1855
+ uploadRawJson,
1737
1856
  downloadJson,
1738
1857
  warmGateways,
1739
1858
  buildGatewayUrls: (cid, extra) => buildGatewayUrls(cid, config.gateway, extra),
@@ -1760,7 +1879,7 @@ var require_ipfs = __commonJS({
1760
1879
  const timeoutMs = Number(options.timeoutMs ?? env.SAGE_IPFS_TIMEOUT_MS ?? 15e3);
1761
1880
  const challengePath = ensureLeadingSlash(
1762
1881
  options.workerChallengePath || env.SAGE_IPFS_WORKER_CHALLENGE_PATH,
1763
- "/auth/challenge"
1882
+ "/ipfs/auth/challenge"
1764
1883
  );
1765
1884
  const warmPath = ensureLeadingSlash(
1766
1885
  options.workerWarmPath || env.SAGE_IPFS_WORKER_WARM_PATH,
@@ -1827,6 +1946,335 @@ var require_ipfs = __commonJS({
1827
1946
  }
1828
1947
  });
1829
1948
 
1949
+ // src/browser/reputation.js
1950
+ var require_reputation = __commonJS({
1951
+ "src/browser/reputation.js"(exports, module) {
1952
+ var { getAddress } = require_utils();
1953
+ var subgraph = require_subgraph();
1954
+ function safeGetAddress(value) {
1955
+ try {
1956
+ return getAddress(value);
1957
+ } catch {
1958
+ return null;
1959
+ }
1960
+ }
1961
+ function clampInt(value, { min = 1, max = 100, fallback = 100 } = {}) {
1962
+ const n = Number(value);
1963
+ if (!Number.isFinite(n)) return fallback;
1964
+ return Math.min(max, Math.max(min, Math.trunc(n)));
1965
+ }
1966
+ function toBigIntString(value, fallback = "0") {
1967
+ try {
1968
+ if (typeof value === "bigint") return value.toString();
1969
+ if (typeof value === "number") return String(Math.trunc(value));
1970
+ const str = String(value).trim();
1971
+ if (!str) return fallback;
1972
+ if (!/^-?\d+$/.test(str)) return fallback;
1973
+ return str;
1974
+ } catch {
1975
+ return fallback;
1976
+ }
1977
+ }
1978
+ function mapSafe(list, mapper) {
1979
+ const out = [];
1980
+ for (const item of list || []) {
1981
+ try {
1982
+ const v = mapper(item);
1983
+ if (v != null) out.push(v);
1984
+ } catch {
1985
+ }
1986
+ }
1987
+ return out;
1988
+ }
1989
+ async function getBadgesByRecipient({ url, recipient, first = 50 }) {
1990
+ if (!url) throw new Error("subgraph url required");
1991
+ if (!recipient) throw new Error("recipient required");
1992
+ const addr = safeGetAddress(recipient);
1993
+ if (!addr) throw new Error("invalid recipient address");
1994
+ const doc = `
1995
+ query($recipient: Bytes!, $first: Int!) {
1996
+ soulboundBadges(
1997
+ where: { recipient: $recipient }
1998
+ first: $first
1999
+ orderBy: blockTimestamp
2000
+ orderDirection: desc
2001
+ ) {
2002
+ id
2003
+ contract
2004
+ badgeId
2005
+ recipient
2006
+ evidenceURI
2007
+ blockNumber
2008
+ blockTimestamp
2009
+ transactionHash
2010
+ }
2011
+ }
2012
+ `;
2013
+ const data = await subgraph.query(url, doc, {
2014
+ recipient: addr.toLowerCase(),
2015
+ first: clampInt(first, { min: 1, max: 100, fallback: 50 })
2016
+ });
2017
+ return mapSafe(data?.soulboundBadges, (row) => {
2018
+ const contract = safeGetAddress(row.contract);
2019
+ const recipientAddr = safeGetAddress(row.recipient);
2020
+ if (!contract || !recipientAddr) return null;
2021
+ return {
2022
+ id: String(row.id),
2023
+ contract,
2024
+ badgeId: toBigIntString(row.badgeId, "0"),
2025
+ recipient: recipientAddr,
2026
+ evidenceURI: row.evidenceURI || null,
2027
+ blockNumber: Number(row.blockNumber || 0),
2028
+ blockTimestamp: Number(row.blockTimestamp || 0),
2029
+ transactionHash: row.transactionHash || null
2030
+ };
2031
+ });
2032
+ }
2033
+ async function listContributions({ url, contributor, subdao, first = 50, orderBy = "updatedAt", orderDirection = "desc" }) {
2034
+ if (!url) throw new Error("subgraph url required");
2035
+ const filters = [];
2036
+ if (contributor) {
2037
+ const addr = safeGetAddress(contributor);
2038
+ if (!addr) throw new Error("invalid contributor address");
2039
+ filters.push(`contributor: "${addr.toLowerCase()}"`);
2040
+ }
2041
+ if (subdao) {
2042
+ const addr = safeGetAddress(subdao);
2043
+ if (!addr) throw new Error("invalid subdao address");
2044
+ filters.push(`dao: "${addr.toLowerCase()}"`);
2045
+ }
2046
+ const where = filters.length ? `where: { ${filters.join(", ")} }` : "";
2047
+ const safeOrderBy = ["updatedAt", "createdAt"].includes(orderBy) ? orderBy : "updatedAt";
2048
+ const safeOrderDirection = orderDirection === "asc" ? "asc" : "desc";
2049
+ const doc = `
2050
+ query($first: Int!) {
2051
+ promptContributions(
2052
+ ${where}
2053
+ first: $first
2054
+ orderBy: ${safeOrderBy}
2055
+ orderDirection: ${safeOrderDirection}
2056
+ ) {
2057
+ id
2058
+ dao
2059
+ promptKey
2060
+ contributor
2061
+ cid
2062
+ proposalId
2063
+ forVotes
2064
+ againstVotes
2065
+ abstainVotes
2066
+ uniqueVoters
2067
+ quorum
2068
+ fromBounty
2069
+ bountyId
2070
+ badgeId
2071
+ badgeEvidenceURI
2072
+ createdAt
2073
+ updatedAt
2074
+ transactionHash
2075
+ }
2076
+ }
2077
+ `;
2078
+ const data = await subgraph.query(url, doc, {
2079
+ first: clampInt(first, { min: 1, max: 100, fallback: 50 })
2080
+ });
2081
+ return mapSafe(data?.promptContributions, (row) => {
2082
+ const dao = safeGetAddress(row.dao);
2083
+ const contributorAddr = safeGetAddress(row.contributor);
2084
+ if (!dao) return null;
2085
+ return {
2086
+ id: String(row.id),
2087
+ dao,
2088
+ promptKey: String(row.promptKey),
2089
+ contributor: contributorAddr || "0x0000000000000000000000000000000000000000",
2090
+ cid: String(row.cid),
2091
+ timestamp: Number(row.updatedAt || 0),
2092
+ createdAt: Number(row.createdAt || 0),
2093
+ updatedAt: Number(row.updatedAt || 0),
2094
+ transactionHash: row.transactionHash || null,
2095
+ proposalId: row.proposalId != null ? toBigIntString(row.proposalId) : null,
2096
+ forVotes: row.forVotes != null ? toBigIntString(row.forVotes) : null,
2097
+ againstVotes: row.againstVotes != null ? toBigIntString(row.againstVotes) : null,
2098
+ abstainVotes: row.abstainVotes != null ? toBigIntString(row.abstainVotes) : null,
2099
+ uniqueVoters: row.uniqueVoters != null ? Number(row.uniqueVoters) : null,
2100
+ quorum: row.quorum != null ? toBigIntString(row.quorum) : null,
2101
+ fromBounty: Boolean(row.fromBounty),
2102
+ bountyId: row.bountyId != null ? toBigIntString(row.bountyId) : null,
2103
+ badgeId: row.badgeId != null ? toBigIntString(row.badgeId) : null,
2104
+ badgeEvidenceURI: row.badgeEvidenceURI || null
2105
+ };
2106
+ });
2107
+ }
2108
+ function computeAggregates(contributions) {
2109
+ if (!contributions || contributions.length === 0) {
2110
+ return {
2111
+ totalContributions: 0,
2112
+ uniqueContributors: 0,
2113
+ bountyOriginated: 0,
2114
+ governanceOriginated: 0,
2115
+ totalForVotes: "0",
2116
+ totalAgainstVotes: "0",
2117
+ averageVoterCount: 0,
2118
+ badgeCount: 0
2119
+ };
2120
+ }
2121
+ const contributorSet = /* @__PURE__ */ new Set();
2122
+ let bountyOriginated = 0;
2123
+ let governanceOriginated = 0;
2124
+ let totalForVotes = 0n;
2125
+ let totalAgainstVotes = 0n;
2126
+ let voterCountSum = 0;
2127
+ let voterCountN = 0;
2128
+ let badgeCount = 0;
2129
+ for (const c of contributions) {
2130
+ if (c.contributor) contributorSet.add(c.contributor.toLowerCase());
2131
+ if (c.fromBounty) {
2132
+ bountyOriginated++;
2133
+ } else {
2134
+ governanceOriginated++;
2135
+ }
2136
+ if (c.forVotes != null) {
2137
+ try {
2138
+ totalForVotes += BigInt(c.forVotes);
2139
+ } catch {
2140
+ }
2141
+ }
2142
+ if (c.againstVotes != null) {
2143
+ try {
2144
+ totalAgainstVotes += BigInt(c.againstVotes);
2145
+ } catch {
2146
+ }
2147
+ }
2148
+ if (c.uniqueVoters != null) {
2149
+ voterCountSum += c.uniqueVoters;
2150
+ voterCountN++;
2151
+ }
2152
+ if (c.badgeId != null) badgeCount++;
2153
+ }
2154
+ return {
2155
+ totalContributions: contributions.length,
2156
+ uniqueContributors: contributorSet.size,
2157
+ bountyOriginated,
2158
+ governanceOriginated,
2159
+ totalForVotes: totalForVotes.toString(),
2160
+ totalAgainstVotes: totalAgainstVotes.toString(),
2161
+ averageVoterCount: voterCountN > 0 ? Math.round(voterCountSum / voterCountN) : 0,
2162
+ badgeCount
2163
+ };
2164
+ }
2165
+ async function getByAddress({
2166
+ url,
2167
+ address,
2168
+ subdao,
2169
+ includeBadges = true,
2170
+ includeContributions = true,
2171
+ first = 100
2172
+ } = {}) {
2173
+ if (!url) throw new Error("subgraph url required");
2174
+ if (!address) throw new Error("address required");
2175
+ const addr = safeGetAddress(address);
2176
+ if (!addr) throw new Error("invalid address");
2177
+ const subdaoAddr = subdao ? safeGetAddress(subdao) : null;
2178
+ if (subdao && !subdaoAddr) throw new Error("invalid subdao address");
2179
+ const limit = clampInt(first, { min: 1, max: 200, fallback: 100 });
2180
+ let badges = void 0;
2181
+ if (includeBadges) {
2182
+ badges = await getBadgesByRecipient({ url, recipient: addr, first: limit });
2183
+ }
2184
+ let contributionAggregates = void 0;
2185
+ let lastContributionAt = null;
2186
+ if (includeContributions) {
2187
+ const rows = await listContributions({
2188
+ url,
2189
+ contributor: addr,
2190
+ subdao: subdaoAddr || void 0,
2191
+ first: limit,
2192
+ orderBy: "updatedAt",
2193
+ orderDirection: "desc"
2194
+ });
2195
+ if (rows && rows.length) {
2196
+ const updatedAt = rows[0]?.updatedAt ?? rows[0]?.timestamp ?? null;
2197
+ lastContributionAt = updatedAt != null ? Number(updatedAt) : null;
2198
+ if (!Number.isFinite(lastContributionAt)) lastContributionAt = null;
2199
+ }
2200
+ contributionAggregates = computeAggregates(rows || []);
2201
+ }
2202
+ const signals = {
2203
+ badgeCount: includeBadges ? badges ? badges.length : 0 : null,
2204
+ totalContributions: includeContributions ? contributionAggregates ? contributionAggregates.totalContributions : 0 : null,
2205
+ governanceOriginated: includeContributions ? contributionAggregates ? contributionAggregates.governanceOriginated : 0 : null,
2206
+ bountyOriginated: includeContributions ? contributionAggregates ? contributionAggregates.bountyOriginated : 0 : null,
2207
+ lastContributionAt: includeContributions ? lastContributionAt : null
2208
+ };
2209
+ const out = {
2210
+ address: addr,
2211
+ subdao: subdaoAddr || null,
2212
+ signals
2213
+ };
2214
+ if (includeBadges) out.badges = badges;
2215
+ if (includeContributions) {
2216
+ out.contributionAggregates = contributionAggregates;
2217
+ out.lastContributionAt = lastContributionAt;
2218
+ }
2219
+ return out;
2220
+ }
2221
+ function normalizeGateInt(value, name) {
2222
+ if (value === void 0 || value === null) return null;
2223
+ const n = Number(value);
2224
+ if (!Number.isFinite(n) || n < 0) {
2225
+ throw new Error(`invalid gate: ${name}`);
2226
+ }
2227
+ return Math.trunc(n);
2228
+ }
2229
+ function normalizeRequiredBadgeIds(value) {
2230
+ const list = Array.isArray(value) ? value : value != null ? [value] : [];
2231
+ const out = [];
2232
+ for (const item of list) {
2233
+ const id = toBigIntString(item, "");
2234
+ if (id) out.push(id);
2235
+ }
2236
+ return out;
2237
+ }
2238
+ function evaluate(result, gates = {}) {
2239
+ const reasons = [];
2240
+ const minBadges = normalizeGateInt(gates.minBadges, "minBadges");
2241
+ const minContributions = normalizeGateInt(gates.minContributions, "minContributions");
2242
+ const requiredBadges = normalizeRequiredBadgeIds(gates.requireBadges ?? gates.requireBadge);
2243
+ const badgeCount = Number(result?.signals?.badgeCount ?? (result?.badges?.length ?? 0));
2244
+ const totalContributions = Number(result?.signals?.totalContributions ?? (result?.contributionAggregates?.totalContributions ?? 0));
2245
+ if (minBadges != null) {
2246
+ if (!Number.isFinite(badgeCount)) {
2247
+ reasons.push("badges_unavailable");
2248
+ } else if (badgeCount < minBadges) {
2249
+ reasons.push(`minBadges:${badgeCount}<${minBadges}`);
2250
+ }
2251
+ }
2252
+ if (minContributions != null) {
2253
+ if (!Number.isFinite(totalContributions)) {
2254
+ reasons.push("contributions_unavailable");
2255
+ } else if (totalContributions < minContributions) {
2256
+ reasons.push(`minContributions:${totalContributions}<${minContributions}`);
2257
+ }
2258
+ }
2259
+ if (requiredBadges.length) {
2260
+ const have = new Set((result?.badges || []).map((b) => toBigIntString(b?.badgeId, "")).filter(Boolean));
2261
+ for (const req of requiredBadges) {
2262
+ if (!have.has(req)) reasons.push(`missingBadge:${req}`);
2263
+ }
2264
+ }
2265
+ return { ok: reasons.length === 0, reasons };
2266
+ }
2267
+ module.exports = {
2268
+ getByAddress,
2269
+ evaluate,
2270
+ // Expose lower-level functions for advanced usage
2271
+ getBadgesByRecipient,
2272
+ listContributions,
2273
+ computeAggregates
2274
+ };
2275
+ }
2276
+ });
2277
+
1830
2278
  // src/errors/index.js
1831
2279
  var require_errors = __commonJS({
1832
2280
  "src/errors/index.js"(exports, module) {
@@ -2232,6 +2680,7 @@ var require_browser = __commonJS({
2232
2680
  var governance = require_governance();
2233
2681
  var subgraph = require_subgraph();
2234
2682
  var ipfs = require_ipfs();
2683
+ var reputation = require_reputation();
2235
2684
  var serviceErrors = require_errors();
2236
2685
  var { SimpleCache } = require_cache();
2237
2686
  var { retryWithBackoff } = require_retry();
@@ -2252,6 +2701,7 @@ var require_browser = __commonJS({
2252
2701
  // Browser-safe modules (no ethers dependencies)
2253
2702
  subgraph,
2254
2703
  ipfs,
2704
+ reputation,
2255
2705
  // Service utilities
2256
2706
  utils: {
2257
2707
  SimpleCache,