@agalmic/contextengine 0.1.0 → 0.2.0
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/README.md +77 -16
- package/dist/contextengine.mjs +720 -358
- package/package.json +1 -1
package/dist/contextengine.mjs
CHANGED
|
@@ -7618,8 +7618,8 @@ var require_bytes32 = __commonJS({
|
|
|
7618
7618
|
var constants_1 = require_lib8();
|
|
7619
7619
|
var bytes_1 = require_lib2();
|
|
7620
7620
|
var utf8_1 = require_utf8();
|
|
7621
|
-
function formatBytes32String(
|
|
7622
|
-
var bytes = (0, utf8_1.toUtf8Bytes)(
|
|
7621
|
+
function formatBytes32String(text5) {
|
|
7622
|
+
var bytes = (0, utf8_1.toUtf8Bytes)(text5);
|
|
7623
7623
|
if (bytes.length > 31) {
|
|
7624
7624
|
throw new Error("bytes32 string must be less than 32 bytes");
|
|
7625
7625
|
}
|
|
@@ -8137,8 +8137,8 @@ var require_id = __commonJS({
|
|
|
8137
8137
|
exports.id = void 0;
|
|
8138
8138
|
var keccak256_1 = require_lib5();
|
|
8139
8139
|
var strings_1 = require_lib9();
|
|
8140
|
-
function id(
|
|
8141
|
-
return (0, keccak256_1.keccak256)((0, strings_1.toUtf8Bytes)(
|
|
8140
|
+
function id(text5) {
|
|
8141
|
+
return (0, keccak256_1.keccak256)((0, strings_1.toUtf8Bytes)(text5));
|
|
8142
8142
|
}
|
|
8143
8143
|
exports.id = id;
|
|
8144
8144
|
}
|
|
@@ -21923,13 +21923,13 @@ var require_aes_js = __commonJS({
|
|
|
21923
21923
|
targetArray.set(sourceArray, targetStart);
|
|
21924
21924
|
}
|
|
21925
21925
|
var convertUtf8 = /* @__PURE__ */ (function() {
|
|
21926
|
-
function toBytes(
|
|
21926
|
+
function toBytes(text5) {
|
|
21927
21927
|
var result = [], i = 0;
|
|
21928
|
-
|
|
21929
|
-
while (i <
|
|
21930
|
-
var c =
|
|
21928
|
+
text5 = encodeURI(text5);
|
|
21929
|
+
while (i < text5.length) {
|
|
21930
|
+
var c = text5.charCodeAt(i++);
|
|
21931
21931
|
if (c === 37) {
|
|
21932
|
-
result.push(parseInt(
|
|
21932
|
+
result.push(parseInt(text5.substr(i, 2), 16));
|
|
21933
21933
|
i += 2;
|
|
21934
21934
|
} else {
|
|
21935
21935
|
result.push(c);
|
|
@@ -21960,10 +21960,10 @@ var require_aes_js = __commonJS({
|
|
|
21960
21960
|
};
|
|
21961
21961
|
})();
|
|
21962
21962
|
var convertHex = /* @__PURE__ */ (function() {
|
|
21963
|
-
function toBytes(
|
|
21963
|
+
function toBytes(text5) {
|
|
21964
21964
|
var result = [];
|
|
21965
|
-
for (var i = 0; i <
|
|
21966
|
-
result.push(parseInt(
|
|
21965
|
+
for (var i = 0; i < text5.length; i += 2) {
|
|
21966
|
+
result.push(parseInt(text5.substr(i, 2), 16));
|
|
21967
21967
|
}
|
|
21968
21968
|
return result;
|
|
21969
21969
|
}
|
|
@@ -31004,7 +31004,7 @@ var require_websocket = __commonJS({
|
|
|
31004
31004
|
var http = __require("http");
|
|
31005
31005
|
var net = __require("net");
|
|
31006
31006
|
var tls = __require("tls");
|
|
31007
|
-
var { randomBytes, createHash } = __require("crypto");
|
|
31007
|
+
var { randomBytes, createHash: createHash2 } = __require("crypto");
|
|
31008
31008
|
var { Readable } = __require("stream");
|
|
31009
31009
|
var { URL: URL2 } = __require("url");
|
|
31010
31010
|
var PerMessageDeflate = require_permessage_deflate();
|
|
@@ -31586,7 +31586,7 @@ var require_websocket = __commonJS({
|
|
|
31586
31586
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
31587
31587
|
return;
|
|
31588
31588
|
}
|
|
31589
|
-
const digest =
|
|
31589
|
+
const digest = createHash2("sha1").update(key + GUID).digest("base64");
|
|
31590
31590
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
31591
31591
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
31592
31592
|
return;
|
|
@@ -31900,7 +31900,7 @@ var require_websocket_server = __commonJS({
|
|
|
31900
31900
|
var https = __require("https");
|
|
31901
31901
|
var net = __require("net");
|
|
31902
31902
|
var tls = __require("tls");
|
|
31903
|
-
var { createHash } = __require("crypto");
|
|
31903
|
+
var { createHash: createHash2 } = __require("crypto");
|
|
31904
31904
|
var PerMessageDeflate = require_permessage_deflate();
|
|
31905
31905
|
var WebSocket = require_websocket();
|
|
31906
31906
|
var { format, parse } = require_extension();
|
|
@@ -32127,7 +32127,7 @@ var require_websocket_server = __commonJS({
|
|
|
32127
32127
|
);
|
|
32128
32128
|
}
|
|
32129
32129
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
32130
|
-
const digest =
|
|
32130
|
+
const digest = createHash2("sha1").update(key + GUID).digest("base64");
|
|
32131
32131
|
const headers = [
|
|
32132
32132
|
"HTTP/1.1 101 Switching Protocols",
|
|
32133
32133
|
"Upgrade: websocket",
|
|
@@ -32137,7 +32137,7 @@ var require_websocket_server = __commonJS({
|
|
|
32137
32137
|
const ws = new WebSocket(null);
|
|
32138
32138
|
let protocol = req.headers["sec-websocket-protocol"];
|
|
32139
32139
|
if (protocol) {
|
|
32140
|
-
protocol = protocol.split(",").map(
|
|
32140
|
+
protocol = protocol.split(",").map(trim9);
|
|
32141
32141
|
if (this.options.handleProtocols) {
|
|
32142
32142
|
protocol = this.options.handleProtocols(protocol, req);
|
|
32143
32143
|
} else {
|
|
@@ -32206,7 +32206,7 @@ var require_websocket_server = __commonJS({
|
|
|
32206
32206
|
socket.removeListener("error", socketOnError);
|
|
32207
32207
|
socket.destroy();
|
|
32208
32208
|
}
|
|
32209
|
-
function
|
|
32209
|
+
function trim9(str) {
|
|
32210
32210
|
return str.trim();
|
|
32211
32211
|
}
|
|
32212
32212
|
}
|
|
@@ -36317,10 +36317,9 @@ var require_rpcDefaults = __commonJS({
|
|
|
36317
36317
|
}
|
|
36318
36318
|
});
|
|
36319
36319
|
|
|
36320
|
-
//
|
|
36320
|
+
// shared/crypto/groupPasswordDerivation.cjs
|
|
36321
36321
|
var require_groupPasswordDerivation = __commonJS({
|
|
36322
|
-
"
|
|
36323
|
-
"use strict";
|
|
36322
|
+
"shared/crypto/groupPasswordDerivation.cjs"(exports, module) {
|
|
36324
36323
|
var GROUP_PASSWORD_DERIVATION = "sbt-group-password-v4";
|
|
36325
36324
|
var GROUP_PASSWORD_PBKDF2_ITERATIONS = 6e5;
|
|
36326
36325
|
var createGroupPasswordDerivation2 = (ethers7) => {
|
|
@@ -36407,15 +36406,15 @@ var SETTINGS = Object.freeze({
|
|
|
36407
36406
|
var MAX_LABEL_LENGTH = 80;
|
|
36408
36407
|
var trim = (value) => String(value ?? "").trim();
|
|
36409
36408
|
var normalizeLabel = (value, field) => {
|
|
36410
|
-
const
|
|
36411
|
-
if (
|
|
36412
|
-
return
|
|
36409
|
+
const text5 = trim(value).replace(/\s+/g, " ");
|
|
36410
|
+
if (text5.length > MAX_LABEL_LENGTH) throw new Error(`${field} must be ${MAX_LABEL_LENGTH} characters or fewer.`);
|
|
36411
|
+
return text5;
|
|
36413
36412
|
};
|
|
36414
36413
|
var normalizeHttpOrigin = (value, field) => {
|
|
36415
|
-
const
|
|
36414
|
+
const text5 = trim(value);
|
|
36416
36415
|
let parsed;
|
|
36417
36416
|
try {
|
|
36418
|
-
parsed = new URL(
|
|
36417
|
+
parsed = new URL(text5);
|
|
36419
36418
|
} catch {
|
|
36420
36419
|
throw new Error(`${field} must be an http(s) origin.`);
|
|
36421
36420
|
}
|
|
@@ -36426,11 +36425,11 @@ var normalizeHttpOrigin = (value, field) => {
|
|
|
36426
36425
|
};
|
|
36427
36426
|
var isLocalUrl = (url) => ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
|
|
36428
36427
|
var normalizeRpcUrl = (value, allowLocal) => {
|
|
36429
|
-
const
|
|
36430
|
-
if (!
|
|
36428
|
+
const text5 = trim(value);
|
|
36429
|
+
if (!text5) return "";
|
|
36431
36430
|
let parsed;
|
|
36432
36431
|
try {
|
|
36433
|
-
parsed = new URL(
|
|
36432
|
+
parsed = new URL(text5);
|
|
36434
36433
|
} catch {
|
|
36435
36434
|
throw new Error("CE_MCP_RPC_URL must be an https URL.");
|
|
36436
36435
|
}
|
|
@@ -36495,13 +36494,22 @@ var ensureStateDir = async (stateDir) => {
|
|
|
36495
36494
|
var writeOwnerOnlyJson = async (filePath, value, { exclusive = false } = {}) => {
|
|
36496
36495
|
const body = `${JSON.stringify(value, null, 2)}
|
|
36497
36496
|
`;
|
|
36498
|
-
if (exclusive) {
|
|
36499
|
-
await fs.writeFile(filePath, body, { mode: OWNER_ONLY_FILE, flag: "wx" });
|
|
36500
|
-
return;
|
|
36501
|
-
}
|
|
36502
36497
|
const temporary = `${filePath}.${process.pid}.${crypto.randomBytes(6).toString("hex")}.tmp`;
|
|
36503
36498
|
await fs.writeFile(temporary, body, { mode: OWNER_ONLY_FILE, flag: "wx" });
|
|
36504
|
-
|
|
36499
|
+
try {
|
|
36500
|
+
if (exclusive) {
|
|
36501
|
+
try {
|
|
36502
|
+
await fs.link(temporary, filePath);
|
|
36503
|
+
} catch (error) {
|
|
36504
|
+
if (!["EPERM", "ENOTSUP", "EXDEV"].includes(error?.code)) throw error;
|
|
36505
|
+
await fs.writeFile(filePath, body, { mode: OWNER_ONLY_FILE, flag: "wx" });
|
|
36506
|
+
}
|
|
36507
|
+
} else {
|
|
36508
|
+
await fs.rename(temporary, filePath);
|
|
36509
|
+
}
|
|
36510
|
+
} finally {
|
|
36511
|
+
await fs.rm(temporary, { force: true });
|
|
36512
|
+
}
|
|
36505
36513
|
};
|
|
36506
36514
|
var readKeyFile = async (keyPath) => {
|
|
36507
36515
|
const info = await fs.lstat(keyPath);
|
|
@@ -36521,8 +36529,7 @@ var readKeyFile = async (keyPath) => {
|
|
|
36521
36529
|
}
|
|
36522
36530
|
return wallet;
|
|
36523
36531
|
};
|
|
36524
|
-
var
|
|
36525
|
-
await ensureStateDir(stateDir);
|
|
36532
|
+
var loadOrCreateKey = async (stateDir) => {
|
|
36526
36533
|
const keyPath = path2.join(stateDir, KEY_FILE);
|
|
36527
36534
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
36528
36535
|
try {
|
|
@@ -36530,6 +36537,12 @@ var loadOrCreateWallet = async (stateDir) => {
|
|
|
36530
36537
|
} catch (error) {
|
|
36531
36538
|
if (error?.code !== "ENOENT") throw error;
|
|
36532
36539
|
}
|
|
36540
|
+
const saved = await readAgentState(stateDir);
|
|
36541
|
+
if (saved.address || Object.keys(saved.sessions).length) {
|
|
36542
|
+
throw new Error(
|
|
36543
|
+
`${keyPath} is missing, but ${STATE_FILE} belongs to ${saved.address ? `agent ${saved.address}` : "an earlier agent"}. Restore the key from a backup, or move ${STATE_FILE} aside to start as a new agent.`
|
|
36544
|
+
);
|
|
36545
|
+
}
|
|
36533
36546
|
const wallet = import_ethers.ethers.Wallet.createRandom();
|
|
36534
36547
|
try {
|
|
36535
36548
|
await writeOwnerOnlyJson(
|
|
@@ -36544,7 +36557,21 @@ var loadOrCreateWallet = async (stateDir) => {
|
|
|
36544
36557
|
}
|
|
36545
36558
|
throw new Error(`${keyPath} could not be read or created.`);
|
|
36546
36559
|
};
|
|
36547
|
-
var
|
|
36560
|
+
var loadOrCreateWallet = async (stateDir) => {
|
|
36561
|
+
await ensureStateDir(stateDir);
|
|
36562
|
+
const loaded = await loadOrCreateKey(stateDir);
|
|
36563
|
+
await withStateWriter(stateDir, async () => {
|
|
36564
|
+
const state = await readAgentState(stateDir);
|
|
36565
|
+
if (state.address && state.address.toLowerCase() !== loaded.wallet.address.toLowerCase()) {
|
|
36566
|
+
throw new Error(
|
|
36567
|
+
`${path2.join(stateDir, STATE_FILE)} belongs to agent ${state.address}, but ${KEY_FILE} holds ${loaded.wallet.address}. Restore the matching files, or move ${STATE_FILE} aside to start fresh with this key.`
|
|
36568
|
+
);
|
|
36569
|
+
}
|
|
36570
|
+
if (!state.address) await writeOwnerOnlyJson(path2.join(stateDir, STATE_FILE), { ...state, address: loaded.wallet.address });
|
|
36571
|
+
});
|
|
36572
|
+
return loaded;
|
|
36573
|
+
};
|
|
36574
|
+
var emptyState = () => ({ version: 1, address: "", profile: {}, sessions: {}, current: "" });
|
|
36548
36575
|
var readAgentState = async (stateDir) => {
|
|
36549
36576
|
let parsed;
|
|
36550
36577
|
try {
|
|
@@ -36556,6 +36583,8 @@ var readAgentState = async (stateDir) => {
|
|
|
36556
36583
|
if (!isRecord(parsed)) return emptyState();
|
|
36557
36584
|
return {
|
|
36558
36585
|
version: 1,
|
|
36586
|
+
// The agent this state belongs to; its remembered answers are that agent's.
|
|
36587
|
+
address: typeof parsed.address === "string" ? parsed.address : "",
|
|
36559
36588
|
profile: isRecord(parsed.profile) ? parsed.profile : {},
|
|
36560
36589
|
sessions: isRecord(parsed.sessions) ? parsed.sessions : {},
|
|
36561
36590
|
current: typeof parsed.current === "string" ? parsed.current : ""
|
|
@@ -36606,7 +36635,15 @@ var hasId = (message) => Object.prototype.hasOwnProperty.call(message, "id");
|
|
|
36606
36635
|
var isValidId = (id) => typeof id === "string" || typeof id === "number" && Number.isFinite(id);
|
|
36607
36636
|
var errorResponse = (id, code, message) => ({ jsonrpc: "2.0", id, error: { code, message } });
|
|
36608
36637
|
var resultResponse = (id, result) => ({ jsonrpc: "2.0", id, result });
|
|
36609
|
-
var
|
|
36638
|
+
var MAX_ERROR_LENGTH = 1e3;
|
|
36639
|
+
var DIAGNOSTICS = /\[ See: |\((?=\w+=)|\b(?:requestBody|body|url|transaction|data|error)=/i;
|
|
36640
|
+
var URLS = /\b[a-z][\w+.-]*:\/\/[^\s"'`]+/gi;
|
|
36641
|
+
var hostOnly = (url) => /<\w+>/.test(url) ? url : url.replace(/^([^:]+:\/\/)(?:[^/?#]*@)?([^/?#]*)[\s\S]*$/, "$1$2");
|
|
36642
|
+
var describeError = (error) => {
|
|
36643
|
+
const text5 = String(error?.message || error || "").slice(0, MAX_ERROR_LENGTH);
|
|
36644
|
+
const cut = text5.search(DIAGNOSTICS);
|
|
36645
|
+
return (cut < 0 ? text5 : text5.slice(0, cut)).trimEnd().replace(URLS, hostOnly) || "Unknown error.";
|
|
36646
|
+
};
|
|
36610
36647
|
var DEFAULT_TOOL_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
36611
36648
|
var createMcpServer = ({
|
|
36612
36649
|
serverInfo,
|
|
@@ -36628,8 +36665,8 @@ var createMcpServer = ({
|
|
|
36628
36665
|
...annotations ? { annotations } : {}
|
|
36629
36666
|
}));
|
|
36630
36667
|
const toolResult = (value, isError = false) => {
|
|
36631
|
-
const
|
|
36632
|
-
const result = { content: [{ type: "text", text:
|
|
36668
|
+
const text5 = typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
36669
|
+
const result = { content: [{ type: "text", text: text5 }] };
|
|
36633
36670
|
if (!isError && isRecord2(value) && protocolVersion >= STRUCTURED_CONTENT_SINCE) {
|
|
36634
36671
|
result.structuredContent = value;
|
|
36635
36672
|
}
|
|
@@ -36724,11 +36761,11 @@ var createMcpServer = ({
|
|
|
36724
36761
|
}
|
|
36725
36762
|
};
|
|
36726
36763
|
const handleLine = async (line) => {
|
|
36727
|
-
const
|
|
36728
|
-
if (!
|
|
36764
|
+
const text5 = String(line || "").trim();
|
|
36765
|
+
if (!text5) return "";
|
|
36729
36766
|
let decoded;
|
|
36730
36767
|
try {
|
|
36731
|
-
decoded = JSON.parse(
|
|
36768
|
+
decoded = JSON.parse(text5);
|
|
36732
36769
|
} catch {
|
|
36733
36770
|
return JSON.stringify(errorResponse(null, PARSE_ERROR, "Parse error."));
|
|
36734
36771
|
}
|
|
@@ -36801,13 +36838,45 @@ var describeTestEthOffer = ({ config, resourcePresence } = {}) => {
|
|
|
36801
36838
|
return { ...result, reason: "Session config is unavailable or unsupported." };
|
|
36802
36839
|
}
|
|
36803
36840
|
if (!network) return { ...result, reason: "No supported test network is configured." };
|
|
36804
|
-
if (hosted && !(Array.isArray(config.workerAuthority?.participantScopes) && config.workerAuthority.participantScopes.
|
|
36841
|
+
if (hosted && !(Array.isArray(config.workerAuthority?.participantScopes) && config.workerAuthority.participantScopes.some((scope) => scope === "faucet_self" || scope === "faucet"))) {
|
|
36805
36842
|
return { ...result, reason: "Participants are not offered test ETH." };
|
|
36806
36843
|
}
|
|
36807
36844
|
if (resourcePresence?.txGas !== true) return { ...result, reason: "A configured faucet has not been confirmed." };
|
|
36808
36845
|
return { ...result, offered: true };
|
|
36809
36846
|
};
|
|
36810
36847
|
|
|
36848
|
+
// mcp/lib/chainErrors.mjs
|
|
36849
|
+
var SAFE_CODE = /^[A-Z_]{3,40}$/;
|
|
36850
|
+
var SAFE_REASON = /^[A-Za-z][\w .,'()-]{0,119}$/;
|
|
36851
|
+
var TX_HASH = /^0x[0-9a-f]{64}$/i;
|
|
36852
|
+
var safeErrorCode = (error) => typeof error?.code === "string" && SAFE_CODE.test(error.code) ? error.code : "";
|
|
36853
|
+
var safeErrorReason = (error) => [error?.reason, error?.errorName, error?.error?.reason].map((value) => typeof value === "string" ? value.replace(/^execution reverted:?\s*/i, "").trim() : "").find((value) => SAFE_REASON.test(value)) || "";
|
|
36854
|
+
var transactionHashOf = (error) => [error?.transactionHash, error?.transaction?.hash, error?.receipt?.transactionHash].find((value) => typeof value === "string" && TX_HASH.test(value));
|
|
36855
|
+
var safeChainError = (error, { submission = false } = {}) => {
|
|
36856
|
+
const code = safeErrorCode(error);
|
|
36857
|
+
const detail = [code, safeErrorReason(error)].filter(Boolean).join(": ");
|
|
36858
|
+
const note = detail ? ` (${detail})` : "";
|
|
36859
|
+
if (submission) {
|
|
36860
|
+
const txHash = transactionHashOf(error);
|
|
36861
|
+
return Object.assign(
|
|
36862
|
+
new Error(`${txHash ? `Transaction ${txHash}` : "The transaction"} may have been submitted but its outcome is unknown${note}. Check the chain before retrying.`),
|
|
36863
|
+
{ ...code ? { code } : {}, reconciliationRequired: true, ...txHash ? { txHash } : {} }
|
|
36864
|
+
);
|
|
36865
|
+
}
|
|
36866
|
+
const retryable = code !== "CALL_EXCEPTION";
|
|
36867
|
+
return Object.assign(
|
|
36868
|
+
new Error(retryable ? `The chain request failed${note}. Check the RPC connection and retry.` : `The contract rejected the request${note}.`),
|
|
36869
|
+
{ ...code ? { code } : {}, retryable }
|
|
36870
|
+
);
|
|
36871
|
+
};
|
|
36872
|
+
var chainRequest = async (request, options) => {
|
|
36873
|
+
try {
|
|
36874
|
+
return await request();
|
|
36875
|
+
} catch (error) {
|
|
36876
|
+
throw safeChainError(error, options);
|
|
36877
|
+
}
|
|
36878
|
+
};
|
|
36879
|
+
|
|
36811
36880
|
// mcp/lib/testEth.mjs
|
|
36812
36881
|
var amount = (value) => typeof value === "string" && /^\d+(?:\.\d{1,18})?$/.test(value) ? value : null;
|
|
36813
36882
|
var pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -36836,15 +36905,16 @@ var createTestEthRequester = ({
|
|
|
36836
36905
|
await ctx.client.ensureToken();
|
|
36837
36906
|
if (ctx.client.tokenClaims?.scopes?.faucet !== true) throw new Error("This session does not offer test ETH to you.");
|
|
36838
36907
|
const provider = suppliedProvider || createProvider(chainId);
|
|
36839
|
-
const reported = Number(await provider.send("eth_chainId", []));
|
|
36908
|
+
const reported = Number(await chainRequest(() => provider.send("eth_chainId", [])));
|
|
36840
36909
|
if (reported !== chainId) throw new Error(`The RPC for chain ${chainId} reports chain ${reported}; refusing the faucet request.`);
|
|
36841
|
-
const before = await provider.getBalance(wallet.address);
|
|
36910
|
+
const before = await chainRequest(() => provider.getBalance(wallet.address));
|
|
36842
36911
|
checkpoint();
|
|
36843
36912
|
let paid;
|
|
36844
36913
|
try {
|
|
36845
36914
|
paid = await ctx.client.request("/", {
|
|
36846
36915
|
method: "POST",
|
|
36847
36916
|
auth: true,
|
|
36917
|
+
signal,
|
|
36848
36918
|
headers: { "X-Session-Slug": ctx.info.slug },
|
|
36849
36919
|
body: { action: "request_test_eth", to: wallet.address }
|
|
36850
36920
|
});
|
|
@@ -36872,7 +36942,7 @@ var createTestEthRequester = ({
|
|
|
36872
36942
|
let observed;
|
|
36873
36943
|
try {
|
|
36874
36944
|
observed = await Promise.race([
|
|
36875
|
-
provider.getBalance(wallet.address).catch(() => null),
|
|
36945
|
+
chainRequest(() => provider.getBalance(wallet.address)).catch(() => null),
|
|
36876
36946
|
new Promise((resolve) => {
|
|
36877
36947
|
timer = setTimeout(() => resolve(null), Math.max(0, deadline - now()));
|
|
36878
36948
|
})
|
|
@@ -36895,9 +36965,23 @@ var createTestEthRequester = ({
|
|
|
36895
36965
|
};
|
|
36896
36966
|
};
|
|
36897
36967
|
|
|
36968
|
+
// mcp/lib/participant.mjs
|
|
36969
|
+
import { createHash, randomUUID as randomUUID2 } from "node:crypto";
|
|
36970
|
+
|
|
36898
36971
|
// mcp/lib/registrySession.mjs
|
|
36899
36972
|
var import_ethers5 = __toESM(require_lib32(), 1);
|
|
36900
36973
|
|
|
36974
|
+
// shared/responseReadWindow.mjs
|
|
36975
|
+
var responsePairKey = (responder, id) => `${String(responder || "").toLowerCase()}|${String(id || "").toLowerCase()}`;
|
|
36976
|
+
var responseReadWindowOptions = (window2, responder, id) => window2 ? {
|
|
36977
|
+
_responseReadWindow: window2,
|
|
36978
|
+
blockTag: window2.laterPairs && !window2.laterPairs.has(responsePairKey(responder, id)) ? window2.unchangedBlockTag : window2.blockTag
|
|
36979
|
+
} : {};
|
|
36980
|
+
var historicalResponseReadError = (error, blockTag) => Object.assign(
|
|
36981
|
+
new Error(`Response state at block ${String(blockTag)} is unavailable: ${error instanceof Error ? error.message : String(error)}`),
|
|
36982
|
+
{ code: "RESPONSE_HISTORY_UNAVAILABLE", retryable: true, blockTag, cause: error }
|
|
36983
|
+
);
|
|
36984
|
+
|
|
36901
36985
|
// client/src/variables/contracts.json
|
|
36902
36986
|
var contracts_default = {
|
|
36903
36987
|
sessionRegistryAddresses: {
|
|
@@ -38193,50 +38277,41 @@ var normalizeRatingScale = (question = {}) => {
|
|
|
38193
38277
|
)
|
|
38194
38278
|
};
|
|
38195
38279
|
};
|
|
38280
|
+
var DEFAULT_RATING_SCALE = Object.freeze({ min: 0, max: 10, minLabel: "0", maxLabel: "10" });
|
|
38281
|
+
var ratingStepFor = (question = {}) => {
|
|
38282
|
+
const source = [question.scale, question.ratingScale].find((record) => isObj(record) && recordHasRatingScaleMetadata(record)) || question;
|
|
38283
|
+
const step = Number(source.step);
|
|
38284
|
+
return Number.isFinite(step) && step > 0 ? step : 1;
|
|
38285
|
+
};
|
|
38286
|
+
var decimalPlaces = (value) => {
|
|
38287
|
+
const [coefficient, exponent = "0"] = String(value).split("e");
|
|
38288
|
+
return Math.max(0, (coefficient.split(".")[1]?.length || 0) - Number(exponent));
|
|
38289
|
+
};
|
|
38290
|
+
var normalizeRatingAnswer = (value, question = {}) => {
|
|
38291
|
+
if (typeof value !== "number" && typeof value !== "string") return null;
|
|
38292
|
+
if (typeof value === "string" && value.trim() === "") return null;
|
|
38293
|
+
const number = Number(value);
|
|
38294
|
+
const { min, max } = normalizeRatingScale(question) || DEFAULT_RATING_SCALE;
|
|
38295
|
+
const step = ratingStepFor(question);
|
|
38296
|
+
const index = (number - min) / step;
|
|
38297
|
+
if (!Number.isFinite(number) || number < min || number > max || !Number.isFinite(index) || Math.abs(index - Math.round(index)) > 1e-9) return null;
|
|
38298
|
+
const precision = Math.min(100, Math.max(decimalPlaces(step), decimalPlaces(min)));
|
|
38299
|
+
return Number((min + Math.round(index) * step).toFixed(precision));
|
|
38300
|
+
};
|
|
38196
38301
|
|
|
38197
|
-
//
|
|
38302
|
+
// shared/questions/answerValue.mjs
|
|
38198
38303
|
var QUESTION_TYPES = Object.freeze(["binary", "rating", "multichoice", "quadratic", "freeform"]);
|
|
38199
38304
|
var BINARY_ANSWERS = Object.freeze(["Agree", "Unsure", "Disagree"]);
|
|
38200
|
-
var MAX_PROMPT_LENGTH = 1e3;
|
|
38201
|
-
var MAX_OPTION_LENGTH = 200;
|
|
38202
|
-
var MAX_OPTIONS = 12;
|
|
38203
|
-
var MAX_TAGS = 10;
|
|
38204
|
-
var MAX_TAG_LENGTH = 40;
|
|
38205
38305
|
var MAX_FREEFORM_LENGTH = 8e3;
|
|
38206
|
-
var MAX_COMMENT_LENGTH = 4e3;
|
|
38207
38306
|
var trim3 = (value) => typeof value === "string" ? value.trim() : typeof value === "number" ? String(value) : "";
|
|
38208
|
-
var isRecord3 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
38209
38307
|
var fail = (error) => ({ ok: false, error });
|
|
38210
|
-
var
|
|
38211
|
-
let dataToHash = `${type}:${(prompt || "").trim().toLowerCase()}`;
|
|
38212
|
-
const validOptions = Array.isArray(options) ? options.filter((option) => typeof option === "string" && option.trim() !== "") : [];
|
|
38213
|
-
if (type === "multichoice") {
|
|
38214
|
-
if (validOptions.length > 0) dataToHash += `:${validOptions.map((option) => option.trim()).join(",").toLowerCase()}`;
|
|
38215
|
-
if (singleSelect) dataToHash += ":single";
|
|
38216
|
-
}
|
|
38217
|
-
if (type === "quadratic") {
|
|
38218
|
-
dataToHash += `:${JSON.stringify(validOptions.map((option) => option.trim().toLowerCase()))}:${voiceCredits}`;
|
|
38219
|
-
}
|
|
38220
|
-
return import_ethers3.ethers.utils.id(dataToHash);
|
|
38221
|
-
};
|
|
38222
|
-
var ratingScaleFor = (question = {}) => normalizeRatingScale(question) || { min: 0, max: 10, minLabel: "0", maxLabel: "10" };
|
|
38308
|
+
var ratingScaleFor = (question = {}) => ({ ...normalizeRatingScale(question) || DEFAULT_RATING_SCALE, step: ratingStepFor(question) });
|
|
38223
38309
|
var optionsOf = (question) => Array.isArray(question.options) ? question.options.filter((option) => typeof option === "string") : [];
|
|
38224
38310
|
var isSingleSelect = (question) => question.singleSelect === true || question.oneSelectionOnly === true || question.singleChoice === true;
|
|
38225
|
-
var isLockedQuestion = (question) => question?.locked === true || !!question?.promptEncrypted || !!question?.encryption && typeof question.encryption === "object" || typeof question?.prompt === "string" && question.prompt.trim() === "[encrypted]";
|
|
38226
|
-
var normalizeRecruitmentSource = (value) => {
|
|
38227
|
-
const normalized2 = (typeof value === "string" ? value : "").trim().replace(/\s+/g, "-").slice(0, 128);
|
|
38228
|
-
return /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/.test(normalized2) ? normalized2 : "";
|
|
38229
|
-
};
|
|
38230
38311
|
var matchOption = (options, value) => {
|
|
38231
38312
|
const wanted = trim3(value).toLowerCase();
|
|
38232
38313
|
return options.find((option) => option.trim().toLowerCase() === wanted);
|
|
38233
38314
|
};
|
|
38234
|
-
var readScore = (value, field) => {
|
|
38235
|
-
if (value === void 0 || value === null || value === "") return { ok: true, value: null };
|
|
38236
|
-
const number = Number(value);
|
|
38237
|
-
if (!Number.isInteger(number) || number < 0 || number > 10) return fail(`${field} must be a whole number from 0 to 10.`);
|
|
38238
|
-
return { ok: true, value: number };
|
|
38239
|
-
};
|
|
38240
38315
|
var normalizeAnswerValue = (question = {}, value) => {
|
|
38241
38316
|
switch (question.type) {
|
|
38242
38317
|
case "binary": {
|
|
@@ -38245,9 +38320,9 @@ var normalizeAnswerValue = (question = {}, value) => {
|
|
|
38245
38320
|
}
|
|
38246
38321
|
case "rating": {
|
|
38247
38322
|
const scale = ratingScaleFor(question);
|
|
38248
|
-
const number =
|
|
38249
|
-
if (
|
|
38250
|
-
return fail(`Rating answers must be
|
|
38323
|
+
const number = normalizeRatingAnswer(value, question);
|
|
38324
|
+
if (number === null) {
|
|
38325
|
+
return fail(`Rating answers must be from ${scale.min} to ${scale.max} in increments of ${scale.step} from ${scale.min}.`);
|
|
38251
38326
|
}
|
|
38252
38327
|
return { ok: true, value: number };
|
|
38253
38328
|
}
|
|
@@ -38275,15 +38350,72 @@ var normalizeAnswerValue = (question = {}, value) => {
|
|
|
38275
38350
|
return { ok: true, value: votes };
|
|
38276
38351
|
}
|
|
38277
38352
|
case "freeform": {
|
|
38278
|
-
const
|
|
38279
|
-
if (!
|
|
38280
|
-
if (
|
|
38281
|
-
return { ok: true, value:
|
|
38353
|
+
const text5 = trim3(value);
|
|
38354
|
+
if (!text5) return fail("Freeform answers must be non-empty text.");
|
|
38355
|
+
if (text5.length > MAX_FREEFORM_LENGTH) return fail(`Freeform answers must be ${MAX_FREEFORM_LENGTH} characters or fewer.`);
|
|
38356
|
+
return { ok: true, value: text5 };
|
|
38282
38357
|
}
|
|
38283
38358
|
default:
|
|
38284
38359
|
return fail(`Unsupported question type "${trim3(question.type)}".`);
|
|
38285
38360
|
}
|
|
38286
38361
|
};
|
|
38362
|
+
|
|
38363
|
+
// shared/questions/questionId.mjs
|
|
38364
|
+
var validOptions = (options) => Array.isArray(options) ? options.filter((o) => o && o.trim() !== "") : [];
|
|
38365
|
+
var legacyPreimage = (type, prompt, options, singleSelect, voiceCredits) => {
|
|
38366
|
+
let preimage = `${type}:${(prompt || "").trim().toLowerCase()}`;
|
|
38367
|
+
const validOpts = validOptions(options);
|
|
38368
|
+
if (type === "multichoice") {
|
|
38369
|
+
if (validOpts.length > 0)
|
|
38370
|
+
preimage += `:${validOpts.map((o) => o.trim()).join(",").toLowerCase()}`;
|
|
38371
|
+
if (singleSelect) preimage += ":single";
|
|
38372
|
+
}
|
|
38373
|
+
if (type === "quadratic") {
|
|
38374
|
+
preimage += `:${JSON.stringify(validOpts.map((option) => option.trim().toLowerCase()))}:${voiceCredits}`;
|
|
38375
|
+
}
|
|
38376
|
+
return preimage;
|
|
38377
|
+
};
|
|
38378
|
+
var legacyIsAmbiguous = (type, options, singleSelect) => {
|
|
38379
|
+
if (type !== "multichoice") return false;
|
|
38380
|
+
const labels = validOptions(options).map((o) => o.trim().toLowerCase());
|
|
38381
|
+
return labels.length === 0 || labels.some((label) => label.includes(":") || label.includes(",")) || !singleSelect && labels.length === 1 && labels[0] === "single";
|
|
38382
|
+
};
|
|
38383
|
+
var v2Text = (text5) => (text5 || "").trim().toLowerCase().normalize("NFC");
|
|
38384
|
+
var v2Preimage = (type, prompt, options, singleSelect) => JSON.stringify(["ce-question-id", 2, type, v2Text(prompt), validOptions(options).map(v2Text), !!singleSelect]);
|
|
38385
|
+
var createQuestionIdDerivation = (ethers7) => {
|
|
38386
|
+
const { id: hash } = ethers7.utils;
|
|
38387
|
+
const generateQuestionId2 = (type, prompt, options = [], singleSelect = false, voiceCredits = 99) => hash(
|
|
38388
|
+
legacyIsAmbiguous(type, options, singleSelect) ? v2Preimage(type, prompt, options, singleSelect) : legacyPreimage(type, prompt, options, singleSelect, voiceCredits)
|
|
38389
|
+
);
|
|
38390
|
+
const questionIdMatchesContent2 = (id, { type, prompt, options = [], singleSelect = false, voiceCredits = 99 } = {}) => {
|
|
38391
|
+
const wanted = String(id).toLowerCase();
|
|
38392
|
+
return hash(legacyPreimage(type, prompt, options, singleSelect, voiceCredits)) === wanted || type === "multichoice" && hash(v2Preimage(type, prompt, options, singleSelect)) === wanted;
|
|
38393
|
+
};
|
|
38394
|
+
return Object.freeze({ generateQuestionId: generateQuestionId2, questionIdMatchesContent: questionIdMatchesContent2 });
|
|
38395
|
+
};
|
|
38396
|
+
|
|
38397
|
+
// mcp/lib/records.mjs
|
|
38398
|
+
var MAX_PROMPT_LENGTH = 1e3;
|
|
38399
|
+
var MAX_OPTION_LENGTH = 200;
|
|
38400
|
+
var MAX_OPTIONS = 12;
|
|
38401
|
+
var MAX_TAGS = 10;
|
|
38402
|
+
var MAX_TAG_LENGTH = 40;
|
|
38403
|
+
var MAX_COMMENT_LENGTH = 4e3;
|
|
38404
|
+
var trim4 = (value) => typeof value === "string" ? value.trim() : typeof value === "number" ? String(value) : "";
|
|
38405
|
+
var isRecord3 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
38406
|
+
var fail2 = (error) => ({ ok: false, error });
|
|
38407
|
+
var { generateQuestionId, questionIdMatchesContent } = createQuestionIdDerivation(import_ethers3.ethers);
|
|
38408
|
+
var isLockedQuestion = (question) => question?.locked === true || !!question?.promptEncrypted || !!question?.encryption && typeof question.encryption === "object" || typeof question?.prompt === "string" && question.prompt.trim() === "[encrypted]";
|
|
38409
|
+
var normalizeRecruitmentSource = (value) => {
|
|
38410
|
+
const normalized2 = (typeof value === "string" ? value : "").trim().replace(/\s+/g, "-").slice(0, 128);
|
|
38411
|
+
return /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/.test(normalized2) ? normalized2 : "";
|
|
38412
|
+
};
|
|
38413
|
+
var readScore = (value, field) => {
|
|
38414
|
+
if (value === void 0 || value === null || value === "") return { ok: true, value: null };
|
|
38415
|
+
const number = Number(value);
|
|
38416
|
+
if (!Number.isInteger(number) || number < 0 || number > 10) return fail2(`${field} must be a whole number from 0 to 10.`);
|
|
38417
|
+
return { ok: true, value: number };
|
|
38418
|
+
};
|
|
38287
38419
|
var plaintextField = (value, hash) => ({
|
|
38288
38420
|
value,
|
|
38289
38421
|
encrypted: false,
|
|
@@ -38296,8 +38428,8 @@ var plaintextField = (value, hash) => ({
|
|
|
38296
38428
|
var textHash = (value) => typeof value === "string" && value ? import_ethers3.ethers.utils.id(value) : "";
|
|
38297
38429
|
var buildParticipantMarker = ({ modelId = "", platform = "" } = {}) => ({
|
|
38298
38430
|
kind: "agent",
|
|
38299
|
-
...
|
|
38300
|
-
...
|
|
38431
|
+
...trim4(modelId) ? { modelId: trim4(modelId) } : {},
|
|
38432
|
+
...trim4(platform) ? { platform: trim4(platform) } : {},
|
|
38301
38433
|
verification: "self_reported"
|
|
38302
38434
|
});
|
|
38303
38435
|
var buildResponseRecord = ({
|
|
@@ -38311,21 +38443,21 @@ var buildResponseRecord = ({
|
|
|
38311
38443
|
profile = {},
|
|
38312
38444
|
recruitmentSource = ""
|
|
38313
38445
|
}) => {
|
|
38314
|
-
if (!isRecord3(question) || !
|
|
38315
|
-
if (isLockedQuestion(question)) return
|
|
38446
|
+
if (!isRecord3(question) || !trim4(question.id)) return fail2("Unknown question.");
|
|
38447
|
+
if (isLockedQuestion(question)) return fail2("This question is locked to certain members; agents cannot answer locked questions yet.");
|
|
38316
38448
|
const answer = normalizeAnswerValue(question, value);
|
|
38317
38449
|
if (!answer.ok) return answer;
|
|
38318
|
-
const note =
|
|
38319
|
-
if (note.length > MAX_COMMENT_LENGTH) return
|
|
38450
|
+
const note = trim4(comment);
|
|
38451
|
+
if (note.length > MAX_COMMENT_LENGTH) return fail2(`Comments must be ${MAX_COMMENT_LENGTH} characters or fewer.`);
|
|
38320
38452
|
const convictionScore = readScore(conviction, "Conviction");
|
|
38321
38453
|
if (!convictionScore.ok) return convictionScore;
|
|
38322
38454
|
const importanceScore = readScore(importance, "Importance");
|
|
38323
38455
|
if (!importanceScore.ok) return importanceScore;
|
|
38324
|
-
const displayName =
|
|
38456
|
+
const displayName = trim4(profile.displayName);
|
|
38325
38457
|
return {
|
|
38326
38458
|
ok: true,
|
|
38327
38459
|
record: {
|
|
38328
|
-
questionID:
|
|
38460
|
+
questionID: trim4(question.id).toLowerCase(),
|
|
38329
38461
|
responder: import_ethers3.ethers.utils.getAddress(responder),
|
|
38330
38462
|
type: question.type,
|
|
38331
38463
|
prompt: typeof question.prompt === "string" ? question.prompt : "",
|
|
@@ -38343,19 +38475,19 @@ var buildResponseRecord = ({
|
|
|
38343
38475
|
};
|
|
38344
38476
|
};
|
|
38345
38477
|
var normalizeOptionList = (options) => {
|
|
38346
|
-
const list = (Array.isArray(options) ? options : []).map(
|
|
38347
|
-
if (list.length < 2) return
|
|
38348
|
-
if (list.length > MAX_OPTIONS) return
|
|
38349
|
-
if (list.some((option) => option.length > MAX_OPTION_LENGTH)) return
|
|
38350
|
-
if (new Set(list.map((option) => option.toLowerCase())).size !== list.length) return
|
|
38478
|
+
const list = (Array.isArray(options) ? options : []).map(trim4).filter(Boolean);
|
|
38479
|
+
if (list.length < 2) return fail2("Provide at least two options.");
|
|
38480
|
+
if (list.length > MAX_OPTIONS) return fail2(`Provide at most ${MAX_OPTIONS} options.`);
|
|
38481
|
+
if (list.some((option) => option.length > MAX_OPTION_LENGTH)) return fail2(`Options must be ${MAX_OPTION_LENGTH} characters or fewer.`);
|
|
38482
|
+
if (new Set(list.map((option) => option.toLowerCase())).size !== list.length) return fail2("Options must be unique.");
|
|
38351
38483
|
return { ok: true, value: list };
|
|
38352
38484
|
};
|
|
38353
38485
|
var buildQuestionRecord = ({ type = "binary", prompt, options, singleSelect = false, voiceCredits, tags = [], creator, session, profile = {} }) => {
|
|
38354
|
-
if (!QUESTION_TYPES.includes(type)) return
|
|
38355
|
-
const
|
|
38356
|
-
if (!
|
|
38357
|
-
if (
|
|
38358
|
-
const record = { id: "", type, prompt:
|
|
38486
|
+
if (!QUESTION_TYPES.includes(type)) return fail2(`Question type must be one of: ${QUESTION_TYPES.join(", ")}.`);
|
|
38487
|
+
const text5 = trim4(prompt);
|
|
38488
|
+
if (!text5) return fail2("The statement text is empty.");
|
|
38489
|
+
if (text5.length > MAX_PROMPT_LENGTH) return fail2(`Statements must be ${MAX_PROMPT_LENGTH} characters or fewer.`);
|
|
38490
|
+
const record = { id: "", type, prompt: text5 };
|
|
38359
38491
|
let optionList = [];
|
|
38360
38492
|
if (type === "multichoice" || type === "quadratic") {
|
|
38361
38493
|
const normalized2 = normalizeOptionList(options);
|
|
@@ -38369,20 +38501,20 @@ var buildQuestionRecord = ({ type = "binary", prompt, options, singleSelect = fa
|
|
|
38369
38501
|
credits = voiceCredits === void 0 || voiceCredits === null ? 99 : Number(voiceCredits);
|
|
38370
38502
|
record.voiceCredits = credits;
|
|
38371
38503
|
const error = validateQuadraticQuestion(record);
|
|
38372
|
-
if (error) return
|
|
38504
|
+
if (error) return fail2(error);
|
|
38373
38505
|
}
|
|
38374
|
-
const tagList = (Array.isArray(tags) ? tags : []).map(
|
|
38506
|
+
const tagList = (Array.isArray(tags) ? tags : []).map(trim4).filter(Boolean);
|
|
38375
38507
|
if (tagList.length > MAX_TAGS || tagList.some((tag) => tag.length > MAX_TAG_LENGTH)) {
|
|
38376
|
-
return
|
|
38508
|
+
return fail2(`Use at most ${MAX_TAGS} tags of ${MAX_TAG_LENGTH} characters or fewer.`);
|
|
38377
38509
|
}
|
|
38378
38510
|
record.tags = tagList;
|
|
38379
38511
|
record.creator = import_ethers3.ethers.utils.getAddress(creator);
|
|
38380
38512
|
record.associatedSurveyId = import_ethers3.ethers.constants.HashZero;
|
|
38381
38513
|
record.participant = buildParticipantMarker(profile);
|
|
38382
|
-
record.sessionName =
|
|
38514
|
+
record.sessionName = trim4(session.sessionName) || session.sessionSlug;
|
|
38383
38515
|
record.sessionSlug = session.sessionSlug;
|
|
38384
38516
|
record.sessionId = session.sessionId;
|
|
38385
|
-
record.id = generateQuestionId(type,
|
|
38517
|
+
record.id = generateQuestionId(type, text5, optionList, record.singleSelect === true, credits);
|
|
38386
38518
|
return { ok: true, id: record.id, record };
|
|
38387
38519
|
};
|
|
38388
38520
|
|
|
@@ -38390,9 +38522,9 @@ var buildQuestionRecord = ({ type = "binary", prompt, options, singleSelect = fa
|
|
|
38390
38522
|
var SLUG_RE = /^[a-z0-9_-]{1,128}$/;
|
|
38391
38523
|
var ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
38392
38524
|
var GROUP_ID_RE = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
38393
|
-
var
|
|
38525
|
+
var trim5 = (value) => String(value ?? "").trim();
|
|
38394
38526
|
var normalizeSessionSlug = (value) => {
|
|
38395
|
-
const slug =
|
|
38527
|
+
const slug = trim5(value).toLowerCase();
|
|
38396
38528
|
if (!SLUG_RE.test(slug)) throw new Error("Session slug must be 1-128 lowercase letters, digits, dashes, or underscores.");
|
|
38397
38529
|
return slug;
|
|
38398
38530
|
};
|
|
@@ -38415,9 +38547,9 @@ var readClaimPairs = (params) => {
|
|
|
38415
38547
|
const pairs = [];
|
|
38416
38548
|
const seen = /* @__PURE__ */ new Set();
|
|
38417
38549
|
const add = (sbt, gp, inv, auto) => {
|
|
38418
|
-
const address =
|
|
38550
|
+
const address = trim5(sbt);
|
|
38419
38551
|
if (!ADDRESS_RE.test(address)) return;
|
|
38420
|
-
const pair = { sbt: address, groupPassword:
|
|
38552
|
+
const pair = { sbt: address, groupPassword: trim5(gp), inviteCode: trim5(inv), auto };
|
|
38421
38553
|
const key = `${address.toLowerCase()}|${pair.groupPassword}|${pair.inviteCode}`;
|
|
38422
38554
|
if (seen.has(key)) return;
|
|
38423
38555
|
seen.add(key);
|
|
@@ -38435,7 +38567,7 @@ var readClaimPairs = (params) => {
|
|
|
38435
38567
|
var parseSessionLink = (link, { allowLocalWorkers = false } = {}) => {
|
|
38436
38568
|
let url;
|
|
38437
38569
|
try {
|
|
38438
|
-
url = new URL(
|
|
38570
|
+
url = new URL(trim5(link));
|
|
38439
38571
|
} catch {
|
|
38440
38572
|
throw new Error("Session link must be a full URL, such as https://contextengine.sh/session/<slug>?worker=<origin>.");
|
|
38441
38573
|
}
|
|
@@ -38448,7 +38580,7 @@ var parseSessionLink = (link, { allowLocalWorkers = false } = {}) => {
|
|
|
38448
38580
|
if (workers.length > 1) {
|
|
38449
38581
|
throw new Error("Session link must name exactly one hosted Worker when using ?worker=.");
|
|
38450
38582
|
}
|
|
38451
|
-
const joinGroup =
|
|
38583
|
+
const joinGroup = trim5(params.get("joinGroup"));
|
|
38452
38584
|
if (joinGroup && !GROUP_ID_RE.test(joinGroup)) throw new Error("Session link has an invalid group id.");
|
|
38453
38585
|
return {
|
|
38454
38586
|
appOrigin: normalizeHttpOrigin(url.origin, "Session link"),
|
|
@@ -38462,6 +38594,51 @@ var parseSessionLink = (link, { allowLocalWorkers = false } = {}) => {
|
|
|
38462
38594
|
};
|
|
38463
38595
|
var buildSessionWebUrl = ({ appOrigin, slug, workerOrigin, kind }) => `${appOrigin}/session/${encodeURIComponent(slug)}${kind === "registry" ? "" : `?worker=${encodeURIComponent(workerOrigin)}`}`;
|
|
38464
38596
|
|
|
38597
|
+
// shared/auth/siweMessage.mjs
|
|
38598
|
+
var normalizeSessionIdHex = (value) => {
|
|
38599
|
+
if (typeof value !== "string" || value !== value.trim()) return "";
|
|
38600
|
+
const lower = value.toLowerCase();
|
|
38601
|
+
const hex = lower.startsWith("0x") ? lower.slice(2) : lower;
|
|
38602
|
+
if (/^[0-9a-f]{32}$/.test(hex) && !/^0+$/.test(hex)) return `0x${hex}`;
|
|
38603
|
+
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(lower)) {
|
|
38604
|
+
const uuidHex = lower.replace(/-/g, "");
|
|
38605
|
+
return /^0+$/.test(uuidHex) ? "" : `0x${uuidHex}`;
|
|
38606
|
+
}
|
|
38607
|
+
return "";
|
|
38608
|
+
};
|
|
38609
|
+
var buildSiweMessage = ({
|
|
38610
|
+
address,
|
|
38611
|
+
nonce,
|
|
38612
|
+
chainId,
|
|
38613
|
+
domain,
|
|
38614
|
+
uri,
|
|
38615
|
+
statement,
|
|
38616
|
+
issuedAt,
|
|
38617
|
+
expirationTime,
|
|
38618
|
+
workerUrl,
|
|
38619
|
+
sessionId,
|
|
38620
|
+
sessionSlug
|
|
38621
|
+
}) => {
|
|
38622
|
+
const id = String(sessionId || "").trim().toLowerCase();
|
|
38623
|
+
const slug = sessionSlug === "general" ? "" : String(sessionSlug || "");
|
|
38624
|
+
const sessionResource = id ? `urn:context-engine:session:id:${encodeURIComponent(id)}` : `urn:context-engine:session:slug:${encodeURIComponent(slug)}`;
|
|
38625
|
+
const resources = workerUrl ? `
|
|
38626
|
+
Resources:
|
|
38627
|
+
- ${new URL(String(workerUrl)).origin}
|
|
38628
|
+
- ${sessionResource}` : "";
|
|
38629
|
+
return `${domain} wants you to sign in with your Ethereum account:
|
|
38630
|
+
${address}
|
|
38631
|
+
|
|
38632
|
+
${statement}
|
|
38633
|
+
|
|
38634
|
+
URI: ${uri}
|
|
38635
|
+
Version: 1
|
|
38636
|
+
Chain ID: ${chainId}
|
|
38637
|
+
Nonce: ${nonce}
|
|
38638
|
+
Issued At: ${issuedAt}
|
|
38639
|
+
Expiration Time: ${expirationTime}${resources}`;
|
|
38640
|
+
};
|
|
38641
|
+
|
|
38465
38642
|
// mcp/lib/workerClient.mjs
|
|
38466
38643
|
var REQUEST_TIMEOUT_MS = 2e4;
|
|
38467
38644
|
var MAX_RESPONSE_BYTES = 8 * 1024 * 1024;
|
|
@@ -38521,17 +38698,6 @@ var readLimitedText = async (response, maxBytes = MAX_RESPONSE_BYTES) => {
|
|
|
38521
38698
|
}
|
|
38522
38699
|
return Buffer.concat(chunks.map((chunk) => Buffer.from(chunk))).toString("utf8");
|
|
38523
38700
|
};
|
|
38524
|
-
var normalizeSessionIdHex = (value) => {
|
|
38525
|
-
if (typeof value !== "string" || value !== value.trim()) return "";
|
|
38526
|
-
const lower = value.toLowerCase();
|
|
38527
|
-
const hex = lower.startsWith("0x") ? lower.slice(2) : lower;
|
|
38528
|
-
if (/^[0-9a-f]{32}$/.test(hex) && !/^0+$/.test(hex)) return `0x${hex}`;
|
|
38529
|
-
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(lower)) {
|
|
38530
|
-
const uuidHex = lower.replace(/-/g, "");
|
|
38531
|
-
return /^0+$/.test(uuidHex) ? "" : `0x${uuidHex}`;
|
|
38532
|
-
}
|
|
38533
|
-
return "";
|
|
38534
|
-
};
|
|
38535
38701
|
var resolveSessionIdHex = (config) => {
|
|
38536
38702
|
const present = (value) => value !== void 0 && value !== null && value !== "";
|
|
38537
38703
|
const sessionId = normalizeSessionIdHex(config?.sessionId);
|
|
@@ -38541,24 +38707,19 @@ var resolveSessionIdHex = (config) => {
|
|
|
38541
38707
|
}
|
|
38542
38708
|
return sessionId || sessionIdHex;
|
|
38543
38709
|
};
|
|
38544
|
-
var
|
|
38545
|
-
|
|
38546
|
-
|
|
38547
|
-
|
|
38548
|
-
|
|
38549
|
-
|
|
38550
|
-
|
|
38551
|
-
|
|
38552
|
-
|
|
38553
|
-
|
|
38554
|
-
|
|
38555
|
-
|
|
38556
|
-
|
|
38557
|
-
Expiration Time: ${expirationTime}
|
|
38558
|
-
Resources:
|
|
38559
|
-
- ${workerOrigin}
|
|
38560
|
-
- ${resource}`;
|
|
38561
|
-
};
|
|
38710
|
+
var buildSiweMessage2 = ({ address, nonce, appOrigin, workerOrigin, sessionId, sessionSlug = "", issuedAt, expirationTime, chainId = 1 }) => buildSiweMessage({
|
|
38711
|
+
address,
|
|
38712
|
+
nonce,
|
|
38713
|
+
domain: new URL(appOrigin).host,
|
|
38714
|
+
uri: appOrigin,
|
|
38715
|
+
workerUrl: workerOrigin,
|
|
38716
|
+
sessionId,
|
|
38717
|
+
sessionSlug,
|
|
38718
|
+
issuedAt,
|
|
38719
|
+
expirationTime,
|
|
38720
|
+
chainId,
|
|
38721
|
+
statement: "Sign in to Context Engine."
|
|
38722
|
+
});
|
|
38562
38723
|
var decodeTokenClaims = (token) => {
|
|
38563
38724
|
try {
|
|
38564
38725
|
const [payload] = text(token).split(".");
|
|
@@ -38595,8 +38756,13 @@ var createWorkerClient = ({ workerOrigin, slug, trustedOrigins, wallet, fetchImp
|
|
|
38595
38756
|
let pendingLogin = null;
|
|
38596
38757
|
let presence = null;
|
|
38597
38758
|
let presenceLoadedAt = -Infinity;
|
|
38598
|
-
const request = async (path3, { method = "GET", headers = {}, body, auth = false, retried = false, maxBytes = MAX_RESPONSE_BYTES } = {}) => {
|
|
38759
|
+
const request = async (path3, { method = "GET", headers = {}, body, auth = false, retried = false, maxBytes = MAX_RESPONSE_BYTES, signal } = {}) => {
|
|
38760
|
+
const checkpoint = () => {
|
|
38761
|
+
if (signal?.aborted) throw new Error("Stopped before the Worker request was sent.");
|
|
38762
|
+
};
|
|
38763
|
+
checkpoint();
|
|
38599
38764
|
if (auth) await ensureToken();
|
|
38765
|
+
checkpoint();
|
|
38600
38766
|
let response;
|
|
38601
38767
|
try {
|
|
38602
38768
|
response = await fetchImpl(`${workerOrigin}${path3}`, {
|
|
@@ -38626,7 +38792,7 @@ var createWorkerClient = ({ workerOrigin, slug, trustedOrigins, wallet, fetchImp
|
|
|
38626
38792
|
const error = isRecord4(data) ? text(data.error) || text(data.message) : "";
|
|
38627
38793
|
if (response.status === 401 && auth && !retried && (!error || TOKEN_ERROR_RE.test(error))) {
|
|
38628
38794
|
token = "";
|
|
38629
|
-
return request(path3, { method, headers, body, auth, retried: true, maxBytes });
|
|
38795
|
+
return request(path3, { method, headers, body, auth, retried: true, maxBytes, signal });
|
|
38630
38796
|
}
|
|
38631
38797
|
if (response.status === 401 && error && !TOKEN_ERROR_RE.test(error)) {
|
|
38632
38798
|
throw new WorkerRequestError(error.slice(0, MAX_ERROR_DETAIL), { status: 401, code: error });
|
|
@@ -38679,7 +38845,7 @@ var createWorkerClient = ({ workerOrigin, slug, trustedOrigins, wallet, fetchImp
|
|
|
38679
38845
|
const nonce = text(nonceData?.nonce);
|
|
38680
38846
|
if (!/^[A-Za-z0-9]{8,200}$/.test(nonce)) throw new WorkerRequestError("The Worker did not issue a usable sign-in nonce.");
|
|
38681
38847
|
const issued = new Date(now());
|
|
38682
|
-
const message =
|
|
38848
|
+
const message = buildSiweMessage2({
|
|
38683
38849
|
address,
|
|
38684
38850
|
nonce,
|
|
38685
38851
|
appOrigin: loginOrigin,
|
|
@@ -38744,15 +38910,16 @@ var createWorkerClient = ({ workerOrigin, slug, trustedOrigins, wallet, fetchImp
|
|
|
38744
38910
|
}
|
|
38745
38911
|
return data;
|
|
38746
38912
|
};
|
|
38747
|
-
const upload = async ({ resource, data, tags = [] }) => {
|
|
38913
|
+
const upload = async ({ resource, data, tags = [], requestId = "", signal }) => {
|
|
38748
38914
|
const result = await request("/storage/upload", {
|
|
38915
|
+
signal,
|
|
38749
38916
|
method: "POST",
|
|
38750
38917
|
auth: true,
|
|
38751
|
-
body: { backend: "cloudflare", resource, contentType: "application/json", payloadEncrypted: false, tags, data }
|
|
38918
|
+
body: { backend: "cloudflare", resource, contentType: "application/json", payloadEncrypted: false, tags, data, ...requestId ? { requestId } : {} }
|
|
38752
38919
|
});
|
|
38753
38920
|
return isRecord4(result?.storageRef) ? result.storageRef : { id: text(result?.id) };
|
|
38754
38921
|
};
|
|
38755
|
-
const joinGroup = (groupId) => request("/groups/join", { method: "POST", auth: true, body: { sessionId, groupId } });
|
|
38922
|
+
const joinGroup = (groupId, { signal } = {}) => request("/groups/join", { method: "POST", auth: true, signal, body: { sessionId, groupId } });
|
|
38756
38923
|
const describeTestEth = async () => {
|
|
38757
38924
|
await refreshConfig();
|
|
38758
38925
|
if (now() - presenceLoadedAt >= CONFIG_MAX_AGE_MS) {
|
|
@@ -38793,7 +38960,7 @@ var createWorkerClient = ({ workerOrigin, slug, trustedOrigins, wallet, fetchImp
|
|
|
38793
38960
|
// mcp/lib/registryChain.mjs
|
|
38794
38961
|
var compareChainOrder = (a, b) => Number(a.blockNumber || 0) - Number(b.blockNumber || 0) || Number(a.transactionIndex || 0) - Number(b.transactionIndex || 0) || Number(a.logIndex || 0) - Number(b.logIndex || 0);
|
|
38795
38962
|
var checkRegistryChain = async (provider, chainId) => {
|
|
38796
|
-
const actual = Number(await provider.send("eth_chainId", []));
|
|
38963
|
+
const actual = Number(await chainRequest(() => provider.send("eth_chainId", [])));
|
|
38797
38964
|
if (actual !== chainId) throw new Error(`The RPC is on a different chain (${actual}); expected ${chainId}.`);
|
|
38798
38965
|
};
|
|
38799
38966
|
var findStartBlock = async (provider, createdAt, head) => {
|
|
@@ -38801,7 +38968,7 @@ var findStartBlock = async (provider, createdAt, head) => {
|
|
|
38801
38968
|
let high = head;
|
|
38802
38969
|
while (low < high) {
|
|
38803
38970
|
const mid = Math.floor((low + high) / 2);
|
|
38804
|
-
const block = await provider.getBlock(mid);
|
|
38971
|
+
const block = await chainRequest(() => provider.getBlock(mid));
|
|
38805
38972
|
if (!block || !Number.isFinite(Number(block.timestamp))) throw new Error("Could not locate the session creation block.");
|
|
38806
38973
|
if (Number(block.timestamp) < createdAt) low = mid + 1;
|
|
38807
38974
|
else high = mid;
|
|
@@ -38839,7 +39006,7 @@ var scanRegistryLogs = async ({
|
|
|
38839
39006
|
range = Math.max(1, Math.floor((end - begin + 1) / 2));
|
|
38840
39007
|
continue;
|
|
38841
39008
|
}
|
|
38842
|
-
throw error;
|
|
39009
|
+
throw safeChainError(error);
|
|
38843
39010
|
}
|
|
38844
39011
|
batch = batch.sort((a, b) => descending ? compareChainOrder(b, a) : compareChainOrder(a, b));
|
|
38845
39012
|
let skipped = 0;
|
|
@@ -38971,13 +39138,14 @@ var stopped = (signal) => {
|
|
|
38971
39138
|
if (signal?.aborted) throw new Error("Stopped before the chain transaction was sent.");
|
|
38972
39139
|
};
|
|
38973
39140
|
var createRegistryWriter = ({ provider, chainId, wallet, surveys, slug, remember }) => {
|
|
39141
|
+
const checkChain = () => checkRegistryChain(provider, chainId);
|
|
38974
39142
|
const prepare = async ({ client, gasLimit, fund, signal }) => {
|
|
38975
39143
|
stopped(signal);
|
|
38976
|
-
await
|
|
38977
|
-
const gasPrice = await provider.getGasPrice();
|
|
39144
|
+
await checkChain();
|
|
39145
|
+
const gasPrice = await chainRequest(() => provider.getGasPrice());
|
|
38978
39146
|
const cost = gasPrice.mul(gasLimit);
|
|
38979
39147
|
if (cost.gt(MAX_COST)) throw new Error("This transaction would exceed the 0.005 ETH gas cap; retry when gas is cheaper.");
|
|
38980
|
-
let balance = await provider.getBalance(wallet.address);
|
|
39148
|
+
let balance = await chainRequest(() => provider.getBalance(wallet.address));
|
|
38981
39149
|
const shortfall = `Fund ${wallet.address} with test ETH on chain ${chainId}; this transaction needs up to ${import_ethers4.ethers.utils.formatEther(cost)} ETH.`;
|
|
38982
39150
|
let fundingNote = "";
|
|
38983
39151
|
if (balance.lt(cost) && fund) {
|
|
@@ -38985,13 +39153,14 @@ var createRegistryWriter = ({ provider, chainId, wallet, surveys, slug, remember
|
|
|
38985
39153
|
if (offer.offered && offer.tokenHasFaucetScope !== false) {
|
|
38986
39154
|
let funding;
|
|
38987
39155
|
try {
|
|
39156
|
+
stopped(signal);
|
|
38988
39157
|
funding = await fund();
|
|
38989
39158
|
} catch (error) {
|
|
38990
39159
|
throw new Error(`${shortfall} ${String(error?.message || "The faucet request failed.").slice(0, 250)}`);
|
|
38991
39160
|
}
|
|
38992
39161
|
if (funding?.sent) fundingNote = ` Faucet transaction ${funding.txHash} was sent; check it before requesting another payout.`;
|
|
38993
39162
|
else if (funding?.reason === "balance_high") fundingNote = " The faucet refused because the balance is above its threshold.";
|
|
38994
|
-
balance = await provider.getBalance(wallet.address).catch(() => null);
|
|
39163
|
+
balance = await chainRequest(() => provider.getBalance(wallet.address)).catch(() => null);
|
|
38995
39164
|
if (!balance) throw new Error(`${shortfall}${fundingNote} The current balance could not be checked.`);
|
|
38996
39165
|
}
|
|
38997
39166
|
}
|
|
@@ -39001,7 +39170,7 @@ var createRegistryWriter = ({ provider, chainId, wallet, surveys, slug, remember
|
|
|
39001
39170
|
};
|
|
39002
39171
|
const upload = async (client, resource, id, record, signal) => {
|
|
39003
39172
|
stopped(signal);
|
|
39004
|
-
const result = await client.request("/arweave/upload", { method: "POST", auth: true, body: {
|
|
39173
|
+
const result = await client.request("/arweave/upload", { method: "POST", auth: true, signal, body: {
|
|
39005
39174
|
data: JSON.stringify(record),
|
|
39006
39175
|
contentType: "application/json",
|
|
39007
39176
|
requestId: randomUUID(),
|
|
@@ -39021,7 +39190,7 @@ var createRegistryWriter = ({ provider, chainId, wallet, surveys, slug, remember
|
|
|
39021
39190
|
})
|
|
39022
39191
|
]);
|
|
39023
39192
|
} catch {
|
|
39024
|
-
throw new Error(`Transaction ${tx.hash} was submitted but could not be confirmed. Check it before retrying.`);
|
|
39193
|
+
throw Object.assign(new Error(`Transaction ${tx.hash} was submitted but could not be confirmed. Check it before retrying.`), { txHash: tx.hash, reconciliationRequired: true });
|
|
39025
39194
|
} finally {
|
|
39026
39195
|
clearTimeout(timer);
|
|
39027
39196
|
}
|
|
@@ -39046,22 +39215,24 @@ var createRegistryWriter = ({ provider, chainId, wallet, surveys, slug, remember
|
|
|
39046
39215
|
const pointers = [];
|
|
39047
39216
|
for (const record of records) pointers.push(await upload(client, "responses", record.questionID, record, signal));
|
|
39048
39217
|
stopped(signal);
|
|
39049
|
-
await
|
|
39050
|
-
|
|
39218
|
+
await checkChain();
|
|
39219
|
+
stopped(signal);
|
|
39220
|
+
const tx = await chainRequest(() => surveys.connect(wallet.connect(provider)).submitResponses(ids, pointers, import_ethers4.ethers.constants.HashZero, import_ethers4.ethers.constants.HashZero, fees), { submission: true });
|
|
39051
39221
|
const receipt = await confirm(tx, "ResponsesSubmitted", ids);
|
|
39052
39222
|
records.forEach((record, index) => remember("responses", record.questionID, record, pointers[index], receipt));
|
|
39053
39223
|
return { txHash: tx.hash, receipt };
|
|
39054
39224
|
};
|
|
39055
39225
|
const addStatement = async ({ client, record, fund, signal }) => {
|
|
39056
|
-
await
|
|
39057
|
-
if (await surveys.questionHashes(record.id) !== import_ethers4.ethers.constants.HashZero) {
|
|
39226
|
+
await checkChain();
|
|
39227
|
+
if (await chainRequest(() => surveys.questionHashes(record.id)) !== import_ethers4.ethers.constants.HashZero) {
|
|
39058
39228
|
throw new Error("This statement already exists on chain, possibly in another session; reword it to add a new statement.");
|
|
39059
39229
|
}
|
|
39060
39230
|
const fees = await prepare({ client, gasLimit: 4e5, fund, signal });
|
|
39061
39231
|
const pointer = await upload(client, "questions", record.id, record, signal);
|
|
39062
39232
|
stopped(signal);
|
|
39063
|
-
await
|
|
39064
|
-
|
|
39233
|
+
await checkChain();
|
|
39234
|
+
stopped(signal);
|
|
39235
|
+
const tx = await chainRequest(() => surveys.connect(wallet.connect(provider)).addQuestions([record.id], [pointer], [import_ethers4.ethers.constants.HashZero], fees), { submission: true });
|
|
39065
39236
|
const receipt = await confirm(tx, "QuestionsAdded", [record.id]);
|
|
39066
39237
|
remember("questions", record.id, record, pointer, receipt);
|
|
39067
39238
|
return { txHash: tx.hash };
|
|
@@ -39124,6 +39295,11 @@ var createRegistrySession = async ({ info, config, wallet, fetchImpl = globalThi
|
|
|
39124
39295
|
let workerOrigin;
|
|
39125
39296
|
let surveys;
|
|
39126
39297
|
let startBlock;
|
|
39298
|
+
const creationBlocks = /* @__PURE__ */ new Map();
|
|
39299
|
+
let endBlock = null;
|
|
39300
|
+
let readEnd;
|
|
39301
|
+
let windowKey;
|
|
39302
|
+
let questionMark = null;
|
|
39127
39303
|
let questionCursor;
|
|
39128
39304
|
const pending = /* @__PURE__ */ new Map();
|
|
39129
39305
|
const questions = /* @__PURE__ */ new Map();
|
|
@@ -39131,16 +39307,30 @@ var createRegistrySession = async ({ info, config, wallet, fetchImpl = globalThi
|
|
|
39131
39307
|
const localAnswers = /* @__PURE__ */ new Map();
|
|
39132
39308
|
const answerScans = /* @__PURE__ */ new Map();
|
|
39133
39309
|
let loadingQuestions;
|
|
39310
|
+
const containsBlock = (block) => Number.isSafeInteger(block) && block >= startBlock && (endBlock === null || block <= readEnd);
|
|
39311
|
+
const readWindowEnd = async () => {
|
|
39312
|
+
const head = await chainRequest(() => provider.getBlockNumber());
|
|
39313
|
+
readEnd = endBlock === null ? head : Math.min(endBlock, head);
|
|
39314
|
+
return readEnd;
|
|
39315
|
+
};
|
|
39316
|
+
const configuredBlock = (value, name) => {
|
|
39317
|
+
if (value == null) return null;
|
|
39318
|
+
const block = Number(value);
|
|
39319
|
+
if (typeof value !== "number" && typeof value !== "string" || String(value).trim() === "" || !Number.isSafeInteger(block) || block < 0) {
|
|
39320
|
+
throw new Error(`Invalid session block limit: ${name} must be a nonnegative integer.`);
|
|
39321
|
+
}
|
|
39322
|
+
return block;
|
|
39323
|
+
};
|
|
39134
39324
|
const loadConfig = async () => {
|
|
39135
39325
|
await checkRegistryChain(provider, chainId);
|
|
39136
|
-
const slug = info.slug
|
|
39137
|
-
const row = await registry.getSessionBySlug(slug);
|
|
39326
|
+
const slug = info.slug || "general";
|
|
39327
|
+
const row = await chainRequest(() => registry.getSessionBySlug(slug));
|
|
39138
39328
|
if (normalized(row[0]) !== slug) throw new Error("The registry returned a different session.");
|
|
39139
39329
|
if (text(row[3])) throw new Error("Encrypted registry metadata is unsupported; use an unencrypted session.");
|
|
39140
39330
|
if (Number(row[1]) !== chainId) throw new Error("The session uses a different chain from this registry.");
|
|
39141
39331
|
const metadata = await readRegistryMetadata(row[2], { fetchImpl });
|
|
39142
39332
|
checkMetadata(metadata);
|
|
39143
|
-
const fields = await registry.getSessionFields(slug, ["corsWorkerUrl"]);
|
|
39333
|
+
const fields = await chainRequest(() => registry.getSessionFields(slug, ["corsWorkerUrl"]));
|
|
39144
39334
|
const workerUrl = text(fields[0]) || text(metadata.corsWorkerUrl);
|
|
39145
39335
|
if (/^\s*(?:\{|enc:|lit:)/i.test(workerUrl)) throw new Error("Encrypted Worker URLs are unsupported; use an unencrypted registry session.");
|
|
39146
39336
|
const nextOrigin = normalizeWorkerOrigin(workerUrl, { allowLocalWorkers: config.allowLocalWorkers });
|
|
@@ -39161,11 +39351,32 @@ var createRegistrySession = async ({ info, config, wallet, fetchImpl = globalThi
|
|
|
39161
39351
|
corsWorkerUrl: workerOrigin,
|
|
39162
39352
|
contracts: { ...metadata.contracts, surveys: { address, chainId } }
|
|
39163
39353
|
};
|
|
39164
|
-
|
|
39165
|
-
|
|
39166
|
-
|
|
39167
|
-
|
|
39354
|
+
const head = await chainRequest(() => provider.getBlockNumber());
|
|
39355
|
+
let nextStart = configuredBlock(metadata.blockLimits?.start, "start");
|
|
39356
|
+
if (nextStart === null) {
|
|
39357
|
+
const createdAt = Number(row[5]);
|
|
39358
|
+
if (!creationBlocks.has(createdAt)) {
|
|
39359
|
+
const lookup = findStartBlock(provider, createdAt, head).catch((error) => {
|
|
39360
|
+
creationBlocks.delete(createdAt);
|
|
39361
|
+
throw error;
|
|
39362
|
+
});
|
|
39363
|
+
creationBlocks.set(createdAt, lookup);
|
|
39364
|
+
}
|
|
39365
|
+
nextStart = await creationBlocks.get(createdAt);
|
|
39366
|
+
}
|
|
39367
|
+
const nextEnd = configuredBlock(metadata.blockLimits?.end, "end");
|
|
39368
|
+
if (nextEnd !== null && nextEnd < nextStart) throw new Error("Invalid session block window: end must not precede start.");
|
|
39369
|
+
const nextKey = `${nextStart}:${nextEnd ?? "head"}`;
|
|
39370
|
+
startBlock = nextStart;
|
|
39371
|
+
endBlock = nextEnd;
|
|
39372
|
+
readEnd = endBlock === null ? head : Math.min(endBlock, head);
|
|
39373
|
+
if (windowKey !== nextKey) {
|
|
39374
|
+
windowKey = nextKey;
|
|
39168
39375
|
questionCursor = { block: startBlock, offset: 0 };
|
|
39376
|
+
questionMark = null;
|
|
39377
|
+
pending.clear();
|
|
39378
|
+
for (const [id, entry] of questions) if (!entry.local || !containsBlock(entry.blockNumber)) questions.delete(id);
|
|
39379
|
+
answerScans.clear();
|
|
39169
39380
|
}
|
|
39170
39381
|
return sessionConfig;
|
|
39171
39382
|
};
|
|
@@ -39181,28 +39392,66 @@ var createRegistrySession = async ({ info, config, wallet, fetchImpl = globalThi
|
|
|
39181
39392
|
return payload;
|
|
39182
39393
|
};
|
|
39183
39394
|
const filter = (eventName) => ({ address: surveys.address, topics: [surveys.interface.getEventTopic(eventName)] });
|
|
39395
|
+
const readBlock = (number, blocks) => {
|
|
39396
|
+
if (!blocks.has(number)) blocks.set(number, chainRequest(() => provider.getBlock(number)).catch(() => null));
|
|
39397
|
+
return blocks.get(number);
|
|
39398
|
+
};
|
|
39399
|
+
const readHeadMark = async (number, blocks) => {
|
|
39400
|
+
const block = await readBlock(number, blocks);
|
|
39401
|
+
return block?.hash ? { number, hash: block.hash } : null;
|
|
39402
|
+
};
|
|
39403
|
+
const historyReplaced = async (mark, blocks) => {
|
|
39404
|
+
if (!mark?.hash) return false;
|
|
39405
|
+
const block = await readBlock(mark.number, blocks);
|
|
39406
|
+
return block?.hash ? block.hash !== mark.hash : null;
|
|
39407
|
+
};
|
|
39408
|
+
const replacedWrite = (entry, blocks = /* @__PURE__ */ new Map()) => historyReplaced(entry?.blockHash ? { number: entry.blockNumber, hash: entry.blockHash } : null, blocks);
|
|
39409
|
+
const visibleQuestions = () => {
|
|
39410
|
+
const blocked = new Set((Array.isArray(sessionConfig.BLOCKED_QUESTION_IDS) ? sessionConfig.BLOCKED_QUESTION_IDS : []).map(normalized));
|
|
39411
|
+
return [...questions.values()].filter((entry) => !blocked.has(entry.id) && containsBlock(entry.blockNumber));
|
|
39412
|
+
};
|
|
39184
39413
|
const readQuestions = async ({ signal } = {}) => {
|
|
39185
39414
|
const deadline = now() + 9e4;
|
|
39415
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
39416
|
+
const end = await readWindowEnd();
|
|
39186
39417
|
let complete = true;
|
|
39418
|
+
const replaced = await historyReplaced(questionMark, blocks);
|
|
39419
|
+
if (replaced === null) return { items: visibleQuestions(), complete: false };
|
|
39420
|
+
if (replaced) {
|
|
39421
|
+
questions.clear();
|
|
39422
|
+
pending.clear();
|
|
39423
|
+
questionCursor = { block: startBlock, offset: 0 };
|
|
39424
|
+
questionMark = null;
|
|
39425
|
+
}
|
|
39426
|
+
for (const [id, entry] of questions) {
|
|
39427
|
+
if (!entry.local) continue;
|
|
39428
|
+
const replaced2 = await replacedWrite(entry, blocks);
|
|
39429
|
+
if (replaced2 === null) complete = false;
|
|
39430
|
+
if (replaced2) questions.delete(id);
|
|
39431
|
+
}
|
|
39187
39432
|
if (pending.size < MAX_CACHE && questions.size < MAX_QUESTIONS) {
|
|
39188
|
-
const
|
|
39189
|
-
|
|
39190
|
-
|
|
39191
|
-
|
|
39192
|
-
|
|
39193
|
-
|
|
39194
|
-
|
|
39195
|
-
|
|
39196
|
-
|
|
39197
|
-
|
|
39198
|
-
|
|
39199
|
-
complete = scanned.complete;
|
|
39200
|
-
for (const log2 of scanned.logs) {
|
|
39201
|
-
const event = surveys.interface.parseLog(log2);
|
|
39202
|
-
event.args.questionIds.forEach((id, index) => {
|
|
39203
|
-
if (event.args.surveyIds[index] === import_ethers5.ethers.constants.HashZero && !questions.has(normalized(id))) pending.set(normalized(id), log2);
|
|
39433
|
+
const head = await readHeadMark(end, blocks);
|
|
39434
|
+
if (head) {
|
|
39435
|
+
const scanned = await scanRegistryLogs({
|
|
39436
|
+
provider,
|
|
39437
|
+
filter: filter("QuestionsAdded"),
|
|
39438
|
+
cursor: questionCursor,
|
|
39439
|
+
toBlock: head.number,
|
|
39440
|
+
maxLogs: 1e3,
|
|
39441
|
+
deadline,
|
|
39442
|
+
now,
|
|
39443
|
+
signal
|
|
39204
39444
|
});
|
|
39205
|
-
|
|
39445
|
+
questionCursor = scanned.cursor;
|
|
39446
|
+
complete = complete && scanned.complete;
|
|
39447
|
+
if (replaced !== null && (!questionMark || head.number >= questionMark.number)) questionMark = head;
|
|
39448
|
+
for (const log2 of scanned.logs) {
|
|
39449
|
+
const event = surveys.interface.parseLog(log2);
|
|
39450
|
+
event.args.questionIds.forEach((id, index) => {
|
|
39451
|
+
if (event.args.surveyIds[index] === import_ethers5.ethers.constants.HashZero && !questions.has(normalized(id))) pending.set(normalized(id), log2);
|
|
39452
|
+
});
|
|
39453
|
+
}
|
|
39454
|
+
} else complete = false;
|
|
39206
39455
|
} else complete = false;
|
|
39207
39456
|
const work = [...pending.entries()];
|
|
39208
39457
|
let next = 0;
|
|
@@ -39210,7 +39459,7 @@ var createRegistrySession = async ({ info, config, wallet, fetchImpl = globalThi
|
|
|
39210
39459
|
while (next < work.length && now() < deadline && !signal?.aborted && questions.size < MAX_QUESTIONS) {
|
|
39211
39460
|
const [id, log2] = work[next++];
|
|
39212
39461
|
try {
|
|
39213
|
-
const pointer = await surveys.getQuestionHash(id);
|
|
39462
|
+
const pointer = await chainRequest(() => surveys.getQuestionHash(id));
|
|
39214
39463
|
const payload = await readPayload(pointer);
|
|
39215
39464
|
const matches = registryQuestionMatchesSession(payload, sessionConfig);
|
|
39216
39465
|
if (matches) questions.set(id, { id, payload: { ...payload, id }, createdAt: String(log2.blockNumber).padStart(16, "0"), ...log2 });
|
|
@@ -39220,8 +39469,7 @@ var createRegistrySession = async ({ info, config, wallet, fetchImpl = globalThi
|
|
|
39220
39469
|
}
|
|
39221
39470
|
}
|
|
39222
39471
|
}));
|
|
39223
|
-
|
|
39224
|
-
return { items: [...questions.values()].filter((entry) => !blocked.has(entry.id)), complete: complete && !pending.size && questions.size < MAX_QUESTIONS };
|
|
39472
|
+
return { items: visibleQuestions(), complete: complete && !pending.size && questions.size < MAX_QUESTIONS };
|
|
39225
39473
|
};
|
|
39226
39474
|
const loadQuestions = (args) => {
|
|
39227
39475
|
if (!loadingQuestions) loadingQuestions = readQuestions(args).finally(() => {
|
|
@@ -39232,17 +39480,42 @@ var createRegistrySession = async ({ info, config, wallet, fetchImpl = globalThi
|
|
|
39232
39480
|
const readAnswers = async ({ questionIds, maxRecords = 1e4, mine = false, signal } = {}) => {
|
|
39233
39481
|
const known = new Set(questionIds);
|
|
39234
39482
|
const deadline = now() + 9e4;
|
|
39483
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
39484
|
+
let verified = true;
|
|
39235
39485
|
const responseFilter = filter("ResponsesSubmitted");
|
|
39236
39486
|
if (mine) responseFilter.topics.push(import_ethers5.ethers.utils.hexZeroPad(wallet.address, 32));
|
|
39237
|
-
const head = await
|
|
39487
|
+
const head = await readWindowEnd();
|
|
39238
39488
|
const scope = [...known].sort().join(",");
|
|
39239
39489
|
let state = answerScans.get(mine);
|
|
39240
|
-
|
|
39241
|
-
|
|
39490
|
+
const replaced = !!state && state.scope === scope && head >= state.head && await historyReplaced(state.mark, blocks);
|
|
39491
|
+
if (replaced === null) {
|
|
39492
|
+
const held = new Map((state.result?.entries || []).map((entry) => [`${entry.questionId}:${normalized(entry.responder)}`, entry]));
|
|
39493
|
+
const chainOrders2 = new Map(state.result?.chainOrders || []);
|
|
39494
|
+
for (const entry of localAnswers.values()) {
|
|
39495
|
+
if (!known.has(entry.questionId) || !containsBlock(entry.blockNumber)) continue;
|
|
39496
|
+
const key = `${entry.questionId}:${normalized(entry.responder)}`;
|
|
39497
|
+
if ([state.latest.get(key), held.get(key)].some((previous) => previous && compareChainOrder(entry, previous) < 0)) continue;
|
|
39498
|
+
chainOrders2.set(entry.questionId, entry);
|
|
39499
|
+
held.delete(key);
|
|
39500
|
+
if (registryResponseMatchesSession(entry.payload, sessionConfig) && entry.payload.answer?.encrypted !== true && normalized(entry.payload.questionID || entry.payload.questionId) === entry.questionId) held.set(key, entry);
|
|
39501
|
+
}
|
|
39502
|
+
return {
|
|
39503
|
+
entries: [...held.values()].sort((a, b) => compareChainOrder(b, a)).slice(0, maxRecords),
|
|
39504
|
+
complete: false,
|
|
39505
|
+
chainOrders: chainOrders2,
|
|
39506
|
+
unreadAnswers: state.result?.unreadAnswers || 0
|
|
39507
|
+
};
|
|
39508
|
+
}
|
|
39509
|
+
if (!state || state.scope !== scope || head < state.head || replaced) {
|
|
39510
|
+
state = { scope, head: startBlock - 1, mark: null, ranges: [], latest: /* @__PURE__ */ new Map(), capped: false };
|
|
39242
39511
|
} else state = { ...state, ranges: state.ranges.map((range) => ({ ...range })), latest: new Map(state.latest) };
|
|
39243
39512
|
if (head > state.head) {
|
|
39244
|
-
|
|
39245
|
-
|
|
39513
|
+
const mark = await readHeadMark(head, blocks);
|
|
39514
|
+
if (mark) {
|
|
39515
|
+
state.ranges.unshift({ from: Math.max(startBlock, state.head + 1), cursor: { block: head, offset: 0 } });
|
|
39516
|
+
state.head = head;
|
|
39517
|
+
if (replaced !== null) state.mark = mark;
|
|
39518
|
+
} else verified = false;
|
|
39246
39519
|
}
|
|
39247
39520
|
if (state.ranges.length > 64) {
|
|
39248
39521
|
const oldest = state.ranges.pop();
|
|
@@ -39295,8 +39568,14 @@ var createRegistrySession = async ({ info, config, wallet, fetchImpl = globalThi
|
|
|
39295
39568
|
answerScans.set(mine, state);
|
|
39296
39569
|
const scannedAll = !state.ranges.length && !state.capped;
|
|
39297
39570
|
const latest = new Map(state.latest);
|
|
39298
|
-
for (const entry of localAnswers
|
|
39299
|
-
if (!known.has(entry.questionId)) continue;
|
|
39571
|
+
for (const [id, entry] of localAnswers) {
|
|
39572
|
+
if (!known.has(entry.questionId) || !containsBlock(entry.blockNumber)) continue;
|
|
39573
|
+
const replaced2 = await replacedWrite(entry, blocks);
|
|
39574
|
+
if (replaced2 === null) verified = false;
|
|
39575
|
+
if (replaced2) {
|
|
39576
|
+
localAnswers.delete(id);
|
|
39577
|
+
continue;
|
|
39578
|
+
}
|
|
39300
39579
|
if (!scannedAll && entry.blockNumber <= head) continue;
|
|
39301
39580
|
const key = `${entry.questionId}:${normalized(entry.responder)}`;
|
|
39302
39581
|
if (!latest.has(key) || compareChainOrder(entry, latest.get(key)) > 0) latest.set(key, { ...entry, local: true });
|
|
@@ -39304,8 +39583,8 @@ var createRegistrySession = async ({ info, config, wallet, fetchImpl = globalThi
|
|
|
39304
39583
|
const candidates = [...latest.values()].sort((a, b) => compareChainOrder(b, a));
|
|
39305
39584
|
const chainOrders = new Map(candidates.filter((entry) => normalized(entry.responder) === normalized(wallet.address)).map((entry) => [entry.questionId, entry]));
|
|
39306
39585
|
const entries = [];
|
|
39307
|
-
|
|
39308
|
-
let
|
|
39586
|
+
let complete = verified && scannedAll && candidates.length <= maxRecords;
|
|
39587
|
+
let unreadAnswers = 0;
|
|
39309
39588
|
let next = 0;
|
|
39310
39589
|
const selected = candidates.slice(0, maxRecords);
|
|
39311
39590
|
await Promise.all(Array.from({ length: Math.min(6, selected.length) }, async () => {
|
|
@@ -39316,20 +39595,32 @@ var createRegistrySession = async ({ info, config, wallet, fetchImpl = globalThi
|
|
|
39316
39595
|
}
|
|
39317
39596
|
const entry = selected[next++];
|
|
39318
39597
|
try {
|
|
39319
|
-
const
|
|
39598
|
+
const { blockTag } = responseReadWindowOptions(endBlock === null ? null : { blockTag: head }, entry.responder, entry.questionId);
|
|
39599
|
+
let pointer;
|
|
39600
|
+
if (entry.local) pointer = entry.storageId;
|
|
39601
|
+
else {
|
|
39602
|
+
try {
|
|
39603
|
+
pointer = await chainRequest(() => blockTag == null ? surveys.getResponse(entry.responder, entry.questionId) : surveys.getResponse(entry.responder, entry.questionId, { blockTag }));
|
|
39604
|
+
} catch (error) {
|
|
39605
|
+
throw blockTag == null ? error : historicalResponseReadError(error, blockTag);
|
|
39606
|
+
}
|
|
39607
|
+
}
|
|
39320
39608
|
const payload = await readPayload(pointer);
|
|
39321
39609
|
if (!registryResponseMatchesSession(payload, sessionConfig) || payload.answer?.encrypted === true) continue;
|
|
39322
39610
|
if (normalized(payload.questionID || payload.questionId) !== entry.questionId) continue;
|
|
39323
|
-
|
|
39324
|
-
|
|
39325
|
-
const createdAt = block?.timestamp ? new Date(Number(block.timestamp) * 1e3).toISOString() : "";
|
|
39611
|
+
const block = await readBlock(entry.blockNumber, blocks);
|
|
39612
|
+
if (!block) complete = false;
|
|
39613
|
+
const createdAt = block?.timestamp ? new Date(Number(block.timestamp) * 1e3).toISOString() : entry.createdAt || "";
|
|
39326
39614
|
entries.push({ ...entry, storageId: pointer, createdAt, payload });
|
|
39327
39615
|
} catch {
|
|
39328
39616
|
complete = false;
|
|
39617
|
+
unreadAnswers++;
|
|
39329
39618
|
}
|
|
39330
39619
|
}
|
|
39331
39620
|
}));
|
|
39332
|
-
|
|
39621
|
+
unreadAnswers += selected.length - next;
|
|
39622
|
+
state.result = { entries, complete, chainOrders, unreadAnswers };
|
|
39623
|
+
return state.result;
|
|
39333
39624
|
};
|
|
39334
39625
|
const writer = createRegistryWriter({ provider, chainId, wallet, surveys, slug: info.slug, remember: (resource, id, payload, pointer, receipt) => {
|
|
39335
39626
|
cache(pointer, payload);
|
|
@@ -39340,20 +39631,75 @@ var createRegistrySession = async ({ info, config, wallet, fetchImpl = globalThi
|
|
|
39340
39631
|
storageId: pointer,
|
|
39341
39632
|
responder: wallet.address,
|
|
39342
39633
|
blockNumber: receipt.blockNumber,
|
|
39634
|
+
blockHash: receipt.blockHash,
|
|
39343
39635
|
transactionIndex: receipt.transactionIndex,
|
|
39344
39636
|
logIndex: receipt.logs.at(-1)?.logIndex || 0,
|
|
39345
39637
|
createdAt: new Date(now()).toISOString()
|
|
39346
39638
|
};
|
|
39347
|
-
if (resource === "questions") questions.set(id, entry);
|
|
39639
|
+
if (resource === "questions") questions.set(id, { ...entry, local: true });
|
|
39348
39640
|
else localAnswers.set(id, entry);
|
|
39349
39641
|
} });
|
|
39350
|
-
return { loadConfig, loadQuestions, readAnswers,
|
|
39642
|
+
return { loadConfig, loadQuestions, readAnswers, replacedWrite, containsBlock, get windowKey() {
|
|
39643
|
+
return windowKey;
|
|
39644
|
+
}, ...writer, get config() {
|
|
39351
39645
|
return sessionConfig;
|
|
39352
39646
|
}, workerOrigin };
|
|
39353
39647
|
};
|
|
39354
39648
|
|
|
39355
|
-
//
|
|
39649
|
+
// shared/storage/canonicalStorageVersions.mjs
|
|
39650
|
+
var trim6 = (value) => (typeof value === "string" ? value : value == null ? "" : String(value)).trim();
|
|
39651
|
+
var compareStorageIds = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
39652
|
+
var compareVersionAge = (left, right) => (Number(left.createdAtMs) || 0) - (Number(right.createdAtMs) || 0) || compareStorageIds(trim6(left.storageRefId), trim6(right.storageRefId));
|
|
39653
|
+
var isAdminStorageVersion = (candidate) => candidate.uploadAuthority === "admin";
|
|
39654
|
+
var isPreferredStorageVersion = (candidate, current) => {
|
|
39655
|
+
if (!current) return true;
|
|
39656
|
+
const candidateAdmin = isAdminStorageVersion(candidate);
|
|
39657
|
+
const currentAdmin = isAdminStorageVersion(current);
|
|
39658
|
+
if (candidateAdmin !== currentAdmin) return candidateAdmin;
|
|
39659
|
+
const age = compareVersionAge(candidate, current);
|
|
39660
|
+
return candidateAdmin ? age > 0 : age < 0;
|
|
39661
|
+
};
|
|
39662
|
+
|
|
39663
|
+
// shared/sessionContext.mjs
|
|
39664
|
+
var SESSION_CONTEXT_LIMITS = Object.freeze({
|
|
39665
|
+
titleChars: 80,
|
|
39666
|
+
paragraphs: 6,
|
|
39667
|
+
paragraphChars: 2e3,
|
|
39668
|
+
links: 8,
|
|
39669
|
+
labelChars: 120,
|
|
39670
|
+
urlChars: 2e3,
|
|
39671
|
+
htmlChars: 2e4
|
|
39672
|
+
});
|
|
39673
|
+
var L = SESSION_CONTEXT_LIMITS;
|
|
39674
|
+
var isRecord5 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
39356
39675
|
var text2 = (value) => typeof value === "string" ? value.trim() : "";
|
|
39676
|
+
var NAMED_ENTITIES = { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'", nbsp: " " };
|
|
39677
|
+
var decodeEntity = (match, entity) => {
|
|
39678
|
+
const code = /^#x/i.test(entity) ? parseInt(entity.slice(2), 16) : /^#/.test(entity) ? parseInt(entity.slice(1), 10) : NaN;
|
|
39679
|
+
if (Number.isInteger(code)) return code > 0 && code <= 1114111 ? String.fromCodePoint(code) : " ";
|
|
39680
|
+
return NAMED_ENTITIES[entity.toLowerCase()] ?? match;
|
|
39681
|
+
};
|
|
39682
|
+
var htmlToPlainText = (html) => String(html ?? "").replace(/<(script|style|template)\b[\s\S]*?<\/\1\s*>/gi, " ").replace(/<!--[\s\S]*?-->/g, " ").replace(/<br\s*\/?>|<\/(?:p|div|li|h[1-6]|tr|summary|details|blockquote|pre|table|ul|ol|dt|dd)>/gi, "\n").replace(/<[^>]*>/g, " ").replace(/&(#x[0-9a-f]+|#\d+|[a-z]+);/gi, decodeEntity).split("\n").map((line) => line.replace(/\s+/g, " ").trim()).filter(Boolean).join("\n");
|
|
39683
|
+
var isHttpsUrl = (value) => {
|
|
39684
|
+
if (typeof value !== "string" || !value || value.length > L.urlChars) return false;
|
|
39685
|
+
try {
|
|
39686
|
+
return new URL(value).protocol === "https:";
|
|
39687
|
+
} catch {
|
|
39688
|
+
return false;
|
|
39689
|
+
}
|
|
39690
|
+
};
|
|
39691
|
+
var normalizeSessionContext = (value) => {
|
|
39692
|
+
const context = isRecord5(value) ? value : {};
|
|
39693
|
+
const title = text2(context.title).slice(0, L.titleChars);
|
|
39694
|
+
const html = text2(context.html).slice(0, L.htmlChars);
|
|
39695
|
+
if (html) return { title, html, paragraphs: [], links: [] };
|
|
39696
|
+
const paragraphs = (Array.isArray(context.paragraphs) ? context.paragraphs : [context.body]).map(text2).filter(Boolean).slice(0, L.paragraphs).map((paragraph) => paragraph.slice(0, L.paragraphChars));
|
|
39697
|
+
const links = (Array.isArray(context.links) ? context.links : []).filter(isRecord5).map((link) => ({ label: text2(link.label).slice(0, L.labelChars), url: text2(link.url) })).filter((link) => isHttpsUrl(link.url)).slice(0, L.links).map((link) => ({ label: link.label || link.url, url: link.url }));
|
|
39698
|
+
return { title, paragraphs, links };
|
|
39699
|
+
};
|
|
39700
|
+
|
|
39701
|
+
// mcp/lib/results.mjs
|
|
39702
|
+
var text3 = (value) => typeof value === "string" ? value.trim() : "";
|
|
39357
39703
|
var MAX_FREEFORM_SAMPLES = 5;
|
|
39358
39704
|
var MAX_SAMPLE_LENGTH = 280;
|
|
39359
39705
|
var isNewer = (candidate, current) => !current || candidate.createdAt > current.createdAt || candidate.createdAt === current.createdAt && candidate.storageId > current.storageId;
|
|
@@ -39362,11 +39708,11 @@ var latestAnswersByQuestion = (entries, { sessionSlug, sessionId, registryConfig
|
|
|
39362
39708
|
for (const entry of entries) {
|
|
39363
39709
|
const payload = entry?.payload;
|
|
39364
39710
|
if (!payload || typeof payload !== "object" || Array.isArray(payload)) continue;
|
|
39365
|
-
const questionId = (
|
|
39711
|
+
const questionId = (text3(payload.questionID) || text3(payload.questionId)).toLowerCase();
|
|
39366
39712
|
if (registryConfig) {
|
|
39367
39713
|
if (!questionIds?.has(questionId) || !registryResponseMatchesSession(payload, registryConfig) || payload.answer?.encrypted === true) continue;
|
|
39368
|
-
} else if (
|
|
39369
|
-
const responder =
|
|
39714
|
+
} else if (text3(payload.sessionSlug) !== sessionSlug || normalizeSessionIdHex(text3(payload.sessionId)) !== sessionId) continue;
|
|
39715
|
+
const responder = text3(entry.responder).toLowerCase();
|
|
39370
39716
|
if (!questionId || !responder) continue;
|
|
39371
39717
|
const answers = byQuestion.get(questionId) || /* @__PURE__ */ new Map();
|
|
39372
39718
|
const current = answers.get(responder);
|
|
@@ -39398,25 +39744,21 @@ var BINARY_VOTES = Object.freeze({
|
|
|
39398
39744
|
0: "Unsure"
|
|
39399
39745
|
});
|
|
39400
39746
|
var validValue = (question, entry) => {
|
|
39401
|
-
const payloadType =
|
|
39747
|
+
const payloadType = text3(entry?.payload?.type);
|
|
39402
39748
|
if (payloadType && payloadType !== question.type) return void 0;
|
|
39403
39749
|
const value = rawValue(entry);
|
|
39404
39750
|
if (value === void 0 || value === null || value === "*") return void 0;
|
|
39405
39751
|
switch (question.type) {
|
|
39406
39752
|
case "binary": {
|
|
39407
39753
|
const scalar = Array.isArray(value) && value.every((item) => ["string", "number"].includes(typeof item)) ? value.join(",") : value;
|
|
39408
|
-
const key = typeof scalar === "boolean" || typeof scalar === "number" ? String(scalar) :
|
|
39754
|
+
const key = typeof scalar === "boolean" || typeof scalar === "number" ? String(scalar) : text3(scalar).toLowerCase();
|
|
39409
39755
|
return Object.hasOwn(BINARY_VOTES, key) ? BINARY_VOTES[key] : void 0;
|
|
39410
39756
|
}
|
|
39411
|
-
case "rating":
|
|
39412
|
-
|
|
39413
|
-
const number = Number(value);
|
|
39414
|
-
const scale = ratingScaleFor(question);
|
|
39415
|
-
return Number.isFinite(number) && number >= scale.min && number <= scale.max ? number : void 0;
|
|
39416
|
-
}
|
|
39757
|
+
case "rating":
|
|
39758
|
+
return normalizeRatingAnswer(value, question) ?? void 0;
|
|
39417
39759
|
case "multichoice": {
|
|
39418
|
-
const options = (Array.isArray(question.options) ? question.options : []).map(
|
|
39419
|
-
const picks = (Array.isArray(value) ? value : [value]).map(
|
|
39760
|
+
const options = (Array.isArray(question.options) ? question.options : []).map(text3).filter(Boolean);
|
|
39761
|
+
const picks = (Array.isArray(value) ? value : [value]).map(text3);
|
|
39420
39762
|
if (!picks.length || picks.some((pick) => !pick)) return void 0;
|
|
39421
39763
|
const canonical = options.length ? picks.map((pick) => options.find((option) => option.toLowerCase() === pick.toLowerCase()) || "") : picks;
|
|
39422
39764
|
if (canonical.some((pick) => !pick)) return void 0;
|
|
@@ -39467,7 +39809,7 @@ var summarizeQuestion = (question, answersByResponder = /* @__PURE__ */ new Map(
|
|
|
39467
39809
|
return { ...summary, scale: { min: scale.min, max: scale.max }, mean, histogram };
|
|
39468
39810
|
}
|
|
39469
39811
|
case "multichoice": {
|
|
39470
|
-
const counts = new Map((Array.isArray(question.options) ? question.options : []).map(
|
|
39812
|
+
const counts = new Map((Array.isArray(question.options) ? question.options : []).map(text3).filter(Boolean).map((option) => [option, 0]));
|
|
39471
39813
|
answered.forEach((entry) => validValue(question, entry).forEach((option) => counts.set(option, (counts.get(option) || 0) + 1)));
|
|
39472
39814
|
return { ...summary, counts: Object.fromEntries(counts) };
|
|
39473
39815
|
}
|
|
@@ -39517,12 +39859,12 @@ var REVEAL_WAIT_MS = 9e4;
|
|
|
39517
39859
|
var POLL_MS = 2e3;
|
|
39518
39860
|
var GAS_HEADROOM_PERCENT = 130;
|
|
39519
39861
|
var PASSWORD_CLAIM_GAS_BUDGET = 4e5;
|
|
39520
|
-
var
|
|
39521
|
-
var
|
|
39862
|
+
var trim7 = (value) => typeof value === "string" ? value.trim() : typeof value === "number" ? String(value) : "";
|
|
39863
|
+
var isRecord6 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
39522
39864
|
var defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
39523
39865
|
var { deriveGroupPasswordWallet } = createGroupPasswordDerivation(import_ethers6.ethers);
|
|
39524
39866
|
var normalizeGroupPasswordInput = (raw) => {
|
|
39525
|
-
const compact =
|
|
39867
|
+
const compact = trim7(raw).replace(/\s+/g, "");
|
|
39526
39868
|
if (!compact) return "";
|
|
39527
39869
|
if (import_ethers6.ethers.utils.isHexString(compact)) {
|
|
39528
39870
|
try {
|
|
@@ -39534,14 +39876,14 @@ var normalizeGroupPasswordInput = (raw) => {
|
|
|
39534
39876
|
return compact;
|
|
39535
39877
|
};
|
|
39536
39878
|
var decodeInviteCode = (raw) => {
|
|
39537
|
-
let code =
|
|
39879
|
+
let code = trim7(raw);
|
|
39538
39880
|
const lower = code.toLowerCase();
|
|
39539
39881
|
if (lower.startsWith("inv:")) code = code.slice(4).trim();
|
|
39540
39882
|
else if (lower.startsWith("invite:")) code = code.slice(7).trim();
|
|
39541
39883
|
if (!code || code.length > 4096) return null;
|
|
39542
39884
|
try {
|
|
39543
39885
|
const parsed = JSON.parse(Buffer.from(code.replace(/-/g, "+").replace(/_/g, "/"), "base64").toString("utf8"));
|
|
39544
|
-
if (!
|
|
39886
|
+
if (!isRecord6(parsed) || Object.keys(parsed).sort().join(",") !== "a,c,n,s") return null;
|
|
39545
39887
|
const chainId = import_ethers6.ethers.BigNumber.from(String(parsed.c));
|
|
39546
39888
|
const slot = import_ethers6.ethers.BigNumber.from(String(parsed.n));
|
|
39547
39889
|
const sbtAddress = import_ethers6.ethers.utils.getAddress(String(parsed.a));
|
|
@@ -39562,13 +39904,13 @@ var accessConditionsOf = (config) => [
|
|
|
39562
39904
|
config?.sessionModeProfile?.encryption?.accessConditions,
|
|
39563
39905
|
config?.storageProfile?.payloadAccessControl?.accessConditions
|
|
39564
39906
|
].flatMap((document) => Array.isArray(document?.conditions) ? document.conditions : []);
|
|
39565
|
-
var conditionAddresses = (condition) => [condition?.contract, condition?.address, ...Array.isArray(condition?.sbtAddresses) ? condition.sbtAddresses : []].map((value) =>
|
|
39907
|
+
var conditionAddresses = (condition) => [condition?.contract, condition?.address, ...Array.isArray(condition?.sbtAddresses) ? condition.sbtAddresses : []].map((value) => trim7(value).toLowerCase()).filter(Boolean);
|
|
39566
39908
|
var positiveChainId = (value) => {
|
|
39567
39909
|
const number = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : NaN;
|
|
39568
39910
|
return Number.isSafeInteger(number) && number > 0 ? number : 0;
|
|
39569
39911
|
};
|
|
39570
39912
|
var resolveSbtChainId = (config, sbtAddress) => {
|
|
39571
|
-
const target =
|
|
39913
|
+
const target = trim7(sbtAddress).toLowerCase();
|
|
39572
39914
|
const condition = accessConditionsOf(config).find(
|
|
39573
39915
|
(entry) => entry?.kind === "sbt_onchain" && conditionAddresses(entry).includes(target) && positiveChainId(entry.chainId)
|
|
39574
39916
|
);
|
|
@@ -39586,19 +39928,13 @@ var listSessionSbts = (config) => {
|
|
|
39586
39928
|
return [...seen.values()];
|
|
39587
39929
|
};
|
|
39588
39930
|
var defaultCreateProvider = (rpcOverride) => (chainId) => {
|
|
39589
|
-
const url =
|
|
39931
|
+
const url = trim7(rpcOverride) || import_rpcDefaults3.default.getPublicRpcUrls(chainId)[0] || import_rpcDefaults3.default.getPathRpcUrl(chainId);
|
|
39590
39932
|
if (!url) throw new Error(`No public RPC is known for chain ${chainId}; set CE_MCP_RPC_URL.`);
|
|
39591
39933
|
return new import_ethers6.ethers.providers.StaticJsonRpcProvider(url, chainId);
|
|
39592
39934
|
};
|
|
39593
39935
|
var ClaimError = class extends Error {
|
|
39594
39936
|
};
|
|
39595
|
-
var
|
|
39596
|
-
var safeMessage = (error, fallback) => {
|
|
39597
|
-
if (error instanceof ClaimError) return error.message;
|
|
39598
|
-
const reason = [error?.reason, error?.errorName, error?.error?.reason].map((value) => typeof value === "string" ? value.replace(/^execution reverted:?\s*/i, "").trim() : "").find((value) => SAFE_REASON_RE.test(value));
|
|
39599
|
-
const code = typeof error?.code === "string" && /^[A-Z_]{3,40}$/.test(error.code) ? error.code : "";
|
|
39600
|
-
return [fallback, reason || code].filter(Boolean).join(": ");
|
|
39601
|
-
};
|
|
39937
|
+
var safeMessage = (error, fallback) => error instanceof ClaimError ? error.message : [fallback, safeErrorReason(error) || safeErrorCode(error)].filter(Boolean).join(": ");
|
|
39602
39938
|
var createSbtClaimer = ({
|
|
39603
39939
|
config = {},
|
|
39604
39940
|
wallet,
|
|
@@ -39711,7 +40047,7 @@ var createSbtClaimer = ({
|
|
|
39711
40047
|
const claimWithGroupPassword = async ({ ctx, provider, chainId, contract, sbtAddress, password }) => {
|
|
39712
40048
|
const normalized2 = normalizeGroupPasswordInput(password);
|
|
39713
40049
|
if (!normalized2) throw new ClaimError("This SBT needs a group password.");
|
|
39714
|
-
const expectedHash =
|
|
40050
|
+
const expectedHash = trim7(await contract.groupPasswordHash()).toLowerCase();
|
|
39715
40051
|
if (!expectedHash || expectedHash === import_ethers6.ethers.constants.HashZero) throw new ClaimError("This SBT has no group password set.");
|
|
39716
40052
|
const groupWallet = await deriveGroupPasswordWallet({ password: normalized2, sbtAddress });
|
|
39717
40053
|
if (import_ethers6.ethers.utils.solidityKeccak256(["address"], [groupWallet.address]) !== expectedHash) {
|
|
@@ -39745,7 +40081,7 @@ var createSbtClaimer = ({
|
|
|
39745
40081
|
signal = callSignal;
|
|
39746
40082
|
let address;
|
|
39747
40083
|
try {
|
|
39748
|
-
address = import_ethers6.ethers.utils.getAddress(
|
|
40084
|
+
address = import_ethers6.ethers.utils.getAddress(trim7(sbtAddress));
|
|
39749
40085
|
} catch {
|
|
39750
40086
|
throw new ClaimError("Pass the SBT contract address (0x\u2026).");
|
|
39751
40087
|
}
|
|
@@ -39778,7 +40114,7 @@ var createSbtClaimer = ({
|
|
|
39778
40114
|
const args = { ctx, provider, chainId, contract, sbtAddress: address };
|
|
39779
40115
|
let transactions;
|
|
39780
40116
|
if (mode === "public") transactions = [(await send({ ...args, method: "claim", args: [] })).transactionHash];
|
|
39781
|
-
else if (mode === "password") transactions = await claimWithCommitReveal({ ...args, password:
|
|
40117
|
+
else if (mode === "password") transactions = await claimWithCommitReveal({ ...args, password: trim7(claimPassword) || trim7(groupPassword) });
|
|
39782
40118
|
else if (mode === "group_password") transactions = await claimWithGroupPassword({ ...args, password: groupPassword });
|
|
39783
40119
|
else transactions = await claimWithInviteCode({ ...args, code: inviteCode || groupPassword });
|
|
39784
40120
|
return { ...base, mode, status: "claimed", transactions };
|
|
@@ -39804,11 +40140,9 @@ var MAX_CACHED_RECORDS = 2e4;
|
|
|
39804
40140
|
var MISSING_RECORD_RETRY_MS = 6e4;
|
|
39805
40141
|
var MAX_OWN_ANSWER_ITEMS = 5e3;
|
|
39806
40142
|
var MAX_TITLE_LENGTH = 120;
|
|
39807
|
-
var
|
|
39808
|
-
var
|
|
39809
|
-
var
|
|
39810
|
-
var trim6 = (value) => typeof value === "string" ? value.trim() : typeof value === "number" ? String(value) : "";
|
|
39811
|
-
var isRecord6 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
40143
|
+
var MAX_DESCRIPTION_LENGTH = 2e3;
|
|
40144
|
+
var trim8 = (value) => typeof value === "string" ? value.trim() : typeof value === "number" ? String(value) : "";
|
|
40145
|
+
var isRecord7 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
39812
40146
|
var sessionKey = (info) => info.kind === "registry" ? `registry ${info.chainId || 11155420} ${info.slug}` : `${info.workerOrigin} ${info.slug}`;
|
|
39813
40147
|
var mapLimit = async (items, limit, task) => {
|
|
39814
40148
|
const results = new Array(items.length);
|
|
@@ -39824,10 +40158,10 @@ var mapLimit = async (items, limit, task) => {
|
|
|
39824
40158
|
return results;
|
|
39825
40159
|
};
|
|
39826
40160
|
var contextOf = (config) => {
|
|
39827
|
-
const context =
|
|
39828
|
-
|
|
39829
|
-
const
|
|
39830
|
-
return {
|
|
40161
|
+
const { html, ...context } = normalizeSessionContext(config?.sessionContext);
|
|
40162
|
+
if (!html) return context;
|
|
40163
|
+
const words = htmlToPlainText(html);
|
|
40164
|
+
return { ...context, paragraphs: words ? [words] : [] };
|
|
39831
40165
|
};
|
|
39832
40166
|
var idList = (value) => new Set((Array.isArray(value) ? value : []).map((id) => text(id).toLowerCase()).filter(Boolean));
|
|
39833
40167
|
var stringOptions = (payload) => Array.isArray(payload.options) ? payload.options.filter((option) => typeof option === "string") : [];
|
|
@@ -39836,9 +40170,9 @@ var finiteNumber = (value) => {
|
|
|
39836
40170
|
if (typeof value === "string" && value.trim() && Number.isFinite(Number(value))) return Number(value);
|
|
39837
40171
|
return void 0;
|
|
39838
40172
|
};
|
|
39839
|
-
var SCALE_KEYS = ["min", "minimum", "max", "maximum", "minLabel", "lowLabel", "maxLabel", "highLabel"];
|
|
40173
|
+
var SCALE_KEYS = ["step", "min", "minimum", "max", "maximum", "minLabel", "lowLabel", "maxLabel", "highLabel"];
|
|
39840
40174
|
var cleanScale = (record) => {
|
|
39841
|
-
if (!
|
|
40175
|
+
if (!isRecord7(record)) return {};
|
|
39842
40176
|
const out = {};
|
|
39843
40177
|
SCALE_KEYS.forEach((key) => {
|
|
39844
40178
|
if (typeof record[key] === "string" || typeof record[key] === "number" && Number.isFinite(record[key])) out[key] = record[key];
|
|
@@ -39868,18 +40202,10 @@ var sanitizeQuestion = (payload, id) => {
|
|
|
39868
40202
|
ratingScaleFor(question);
|
|
39869
40203
|
return question;
|
|
39870
40204
|
};
|
|
39871
|
-
var
|
|
39872
|
-
|
|
39873
|
-
|
|
39874
|
-
|
|
39875
|
-
const ranked = [...versions].sort((left, right) => {
|
|
39876
|
-
const leftMatches = left.contentMatches === true ? 0 : 1;
|
|
39877
|
-
const rightMatches = right.contentMatches === true ? 0 : 1;
|
|
39878
|
-
if (leftMatches !== rightMatches) return leftMatches - rightMatches;
|
|
39879
|
-
return left.createdAt < right.createdAt ? -1 : left.createdAt > right.createdAt ? 1 : 0;
|
|
39880
|
-
});
|
|
39881
|
-
return ranked[0];
|
|
39882
|
-
};
|
|
40205
|
+
var chooseQuestionVersion = (versions) => versions.reduce(
|
|
40206
|
+
(chosen, candidate) => isPreferredStorageVersion(candidate, chosen) ? candidate : chosen,
|
|
40207
|
+
null
|
|
40208
|
+
);
|
|
39883
40209
|
var describeQuestion = (question, myAnswer) => {
|
|
39884
40210
|
const options = stringOptions(question);
|
|
39885
40211
|
const entry = {
|
|
@@ -39915,7 +40241,9 @@ var sameAuthoringInputs = (stored, record) => {
|
|
|
39915
40241
|
var progressNotes = (questions, mine) => ({
|
|
39916
40242
|
...questions.locked ? { lockedQuestions: questions.locked } : {},
|
|
39917
40243
|
...questions.complete === false ? { questionsComplete: false } : {},
|
|
39918
|
-
...
|
|
40244
|
+
...questions.unread ? { unreadQuestions: questions.unread } : {},
|
|
40245
|
+
...mine.unread ? { unreadMyAnswers: mine.unread } : {},
|
|
40246
|
+
...mine.complete === false ? { myAnswersComplete: false, note: "Some answers could not be read or the scan is unfinished; the answers shown may be incomplete." } : {}
|
|
39919
40247
|
});
|
|
39920
40248
|
var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = () => Date.now(), sbt = null, createProvider, sleep, registry = {} }) => {
|
|
39921
40249
|
const requestTestEth = createTestEthRequester({ config, wallet, createProvider, now, sleep });
|
|
@@ -39941,9 +40269,9 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
39941
40269
|
state = await updateAgentState(config.stateDir, mutate);
|
|
39942
40270
|
};
|
|
39943
40271
|
const profile = () => ({
|
|
39944
|
-
displayName:
|
|
39945
|
-
modelId:
|
|
39946
|
-
platform:
|
|
40272
|
+
displayName: trim8(state?.profile?.displayName ?? config.profile.displayName),
|
|
40273
|
+
modelId: trim8(state?.profile?.modelId ?? config.profile.modelId),
|
|
40274
|
+
platform: trim8(state?.profile?.platform ?? config.profile.platform)
|
|
39947
40275
|
});
|
|
39948
40276
|
const trustedOriginsFor = (preferred) => config.trustedAppOrigins.includes(preferred) ? [preferred, ...config.trustedAppOrigins.filter((origin) => origin !== preferred)] : config.trustedAppOrigins;
|
|
39949
40277
|
const openSession = async (info) => {
|
|
@@ -39971,7 +40299,7 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
39971
40299
|
};
|
|
39972
40300
|
const remember = (ctx, title, recruitmentSource) => saveState((fresh) => {
|
|
39973
40301
|
const key = sessionKey(ctx.info);
|
|
39974
|
-
const stored =
|
|
40302
|
+
const stored = isRecord7(fresh.sessions[key]) ? fresh.sessions[key] : {};
|
|
39975
40303
|
const previous = stored.sessionId && stored.sessionId !== ctx.client.sessionId ? {} : stored;
|
|
39976
40304
|
fresh.sessions[key] = {
|
|
39977
40305
|
...previous,
|
|
@@ -39988,24 +40316,25 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
39988
40316
|
});
|
|
39989
40317
|
const sessionState = (ctx) => {
|
|
39990
40318
|
const entry = state?.sessions?.[sessionKey(ctx.info)];
|
|
39991
|
-
return
|
|
40319
|
+
return isRecord7(entry) ? entry : {};
|
|
39992
40320
|
};
|
|
39993
40321
|
const rememberAnswers = (ctx, answered) => saveState((fresh) => {
|
|
39994
40322
|
const entry = fresh.sessions[sessionKey(ctx.info)];
|
|
39995
|
-
if (!
|
|
39996
|
-
entry.answers =
|
|
39997
|
-
answered.forEach(({ questionId, record, answeredAt, chainOrder }) => {
|
|
40323
|
+
if (!isRecord7(entry) || entry.sessionId !== ctx.client.sessionId) return;
|
|
40324
|
+
entry.answers = isRecord7(entry.answers) ? entry.answers : {};
|
|
40325
|
+
answered.forEach(({ questionId, record, answeredAt, chainOrder, requestId }) => {
|
|
39998
40326
|
entry.answers[questionId] = {
|
|
39999
40327
|
value: record.answer.value,
|
|
40000
40328
|
comment: record.additional.value,
|
|
40001
40329
|
answeredAt,
|
|
40002
40330
|
...ctx.registry && chainOrder ? chainOrder : {}
|
|
40003
40331
|
};
|
|
40332
|
+
if (requestId && entry.pendingUploads?.[questionId]?.requestId === requestId) delete entry.pendingUploads[questionId];
|
|
40004
40333
|
});
|
|
40005
40334
|
});
|
|
40006
40335
|
const resolveSession = async (session) => {
|
|
40007
40336
|
await loadState();
|
|
40008
|
-
const ref =
|
|
40337
|
+
const ref = trim8(session);
|
|
40009
40338
|
if (/^https?:\/\//i.test(ref)) {
|
|
40010
40339
|
const parsed = parseSessionLink(ref, { allowLocalWorkers: config.allowLocalWorkers });
|
|
40011
40340
|
const key2 = sessionKey(parsed);
|
|
@@ -40021,7 +40350,7 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40021
40350
|
if (!key) throw new Error(`No joined session named "${slug}". Join it first with join_session.`);
|
|
40022
40351
|
}
|
|
40023
40352
|
const entry = state.sessions[key];
|
|
40024
|
-
if (!key || !
|
|
40353
|
+
if (!key || !isRecord7(entry)) throw new Error("No session joined yet. Call join_session with a session link.");
|
|
40025
40354
|
return openSession({
|
|
40026
40355
|
workerOrigin: normalizeWorkerOrigin(entry.workerOrigin, { allowLocalWorkers: config.allowLocalWorkers }),
|
|
40027
40356
|
slug: normalizeSessionSlug(entry.slug),
|
|
@@ -40041,33 +40370,27 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40041
40370
|
};
|
|
40042
40371
|
const readCached = async (ctx, storageId) => {
|
|
40043
40372
|
if (ctx.records.has(storageId)) return ctx.records.get(storageId);
|
|
40044
|
-
if (now() - (ctx.missing.get(storageId) ?? -Infinity) < MISSING_RECORD_RETRY_MS) return
|
|
40045
|
-
let payload = null;
|
|
40373
|
+
if (now() - (ctx.missing.get(storageId) ?? -Infinity) < MISSING_RECORD_RETRY_MS) return void 0;
|
|
40046
40374
|
try {
|
|
40047
|
-
payload = await ctx.client.read(storageId);
|
|
40375
|
+
const payload = await ctx.client.read(storageId);
|
|
40376
|
+
ctx.missing.delete(storageId);
|
|
40377
|
+
cacheRecord(ctx, storageId, payload);
|
|
40378
|
+
return payload;
|
|
40048
40379
|
} catch (error) {
|
|
40049
40380
|
if (!(error instanceof WorkerRequestError) || error.status !== 404) throw error;
|
|
40050
|
-
}
|
|
40051
|
-
if (payload === null) {
|
|
40052
40381
|
ctx.missing.set(storageId, now());
|
|
40053
40382
|
if (ctx.missing.size > MAX_CACHED_RECORDS) ctx.missing.delete(ctx.missing.keys().next().value);
|
|
40054
|
-
|
|
40055
|
-
ctx.missing.delete(storageId);
|
|
40056
|
-
cacheRecord(ctx, storageId, payload);
|
|
40383
|
+
return void 0;
|
|
40057
40384
|
}
|
|
40058
|
-
return payload;
|
|
40059
40385
|
};
|
|
40060
40386
|
const readMany = async (ctx, storageIds) => {
|
|
40061
40387
|
const deadline = now() + READ_DEADLINE_MS;
|
|
40062
|
-
let complete = true;
|
|
40063
40388
|
const payloads = await mapLimit(storageIds, READ_CONCURRENCY, async (storageId) => {
|
|
40064
|
-
if (!ctx.records.has(storageId) && (now() >= deadline || signal?.aborted))
|
|
40065
|
-
|
|
40066
|
-
return void 0;
|
|
40067
|
-
}
|
|
40068
|
-
return readCached(ctx, storageId);
|
|
40389
|
+
if (!ctx.records.has(storageId) && (now() >= deadline || signal?.aborted)) return void 0;
|
|
40390
|
+
return readCached(ctx, storageId).catch(() => void 0);
|
|
40069
40391
|
});
|
|
40070
|
-
|
|
40392
|
+
const unread = payloads.filter((payload) => payload === void 0).length;
|
|
40393
|
+
return { payloads, complete: unread === 0, unread };
|
|
40071
40394
|
};
|
|
40072
40395
|
const loadQuestions = async (ctx) => {
|
|
40073
40396
|
const sessionConfig = await ensureReady(ctx);
|
|
@@ -40077,7 +40400,7 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40077
40400
|
const all2 = [];
|
|
40078
40401
|
for (const row of result.items) {
|
|
40079
40402
|
try {
|
|
40080
|
-
if (!
|
|
40403
|
+
if (!isRecord7(row.payload) || typeof row.payload.type !== "string") continue;
|
|
40081
40404
|
const payload = isLockedQuestion(row.payload) ? { ...row.payload, prompt: "[encrypted]", locked: true } : row.payload;
|
|
40082
40405
|
if (typeof payload.prompt !== "string") continue;
|
|
40083
40406
|
all2.push({
|
|
@@ -40098,15 +40421,15 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40098
40421
|
}
|
|
40099
40422
|
const { items, complete } = await ctx.client.listAll("questions", { maxItems: MAX_QUESTIONS2, signal });
|
|
40100
40423
|
const hidden = new Set(items.filter((item) => item.hidden === true).map((item) => text(item.metadata?.resourceId).toLowerCase()));
|
|
40101
|
-
const rows = items.filter((item) => item.hidden !== true && text(item.storageRef?.id));
|
|
40102
|
-
const { payloads, complete: readComplete } = await readMany(ctx, rows.map((item) => text(item.storageRef.id)));
|
|
40103
40424
|
const blocked = idList(sessionConfig.BLOCKED_QUESTION_IDS);
|
|
40425
|
+
const rows = items.filter((item) => item.hidden !== true && text(item.storageRef?.id) && !hidden.has(text(item.metadata?.resourceId).toLowerCase()) && !blocked.has(text(item.metadata?.resourceId).toLowerCase()));
|
|
40426
|
+
const { payloads, complete: readComplete, unread } = await readMany(ctx, rows.map((item) => text(item.storageRef.id)));
|
|
40104
40427
|
const highlighted = idList(sessionConfig.HIGHLIGHTED_QUESTION_IDS);
|
|
40105
40428
|
const versions = /* @__PURE__ */ new Map();
|
|
40106
40429
|
rows.forEach((item, index) => {
|
|
40107
40430
|
try {
|
|
40108
40431
|
const payload = payloads[index];
|
|
40109
|
-
if (!
|
|
40432
|
+
if (!isRecord7(payload) || typeof payload.prompt !== "string" || typeof payload.type !== "string") return;
|
|
40110
40433
|
if (text(payload.sessionSlug) !== ctx.info.slug) return;
|
|
40111
40434
|
if (text(payload.sessionId) && normalizeSessionIdHex(text(payload.sessionId)) !== ctx.client.sessionId) return;
|
|
40112
40435
|
const id = (text(payload.id) || text(item.metadata?.resourceId)).toLowerCase();
|
|
@@ -40116,8 +40439,9 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40116
40439
|
list.push({
|
|
40117
40440
|
payload: question,
|
|
40118
40441
|
createdAt: text(item.metadata?.createdAt),
|
|
40119
|
-
|
|
40120
|
-
|
|
40442
|
+
createdAtMs: Date.parse(text(item.metadata?.createdAt)) || 0,
|
|
40443
|
+
storageRefId: text(item.storageRef.id),
|
|
40444
|
+
uploadAuthority: item.metadata?.uploadAuthority
|
|
40121
40445
|
});
|
|
40122
40446
|
versions.set(id, list);
|
|
40123
40447
|
} catch {
|
|
@@ -40142,11 +40466,12 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40142
40466
|
questions.locked = all.length - questions.length;
|
|
40143
40467
|
questions.lockedIds = new Set(all.filter((question) => isLockedQuestion(question)).map((question) => question.id));
|
|
40144
40468
|
questions.complete = complete && readComplete;
|
|
40469
|
+
questions.unread = unread;
|
|
40145
40470
|
return questions;
|
|
40146
40471
|
};
|
|
40147
40472
|
const toEntries = async (ctx, items) => {
|
|
40148
|
-
const rows = items.filter((item) => text(item.storageRef?.id));
|
|
40149
|
-
const { payloads, complete } = await readMany(ctx, rows.map((item) => text(item.storageRef.id)));
|
|
40473
|
+
const rows = items.filter((item) => item.hidden !== true && text(item.storageRef?.id));
|
|
40474
|
+
const { payloads, complete, unread } = await readMany(ctx, rows.map((item) => text(item.storageRef.id)));
|
|
40150
40475
|
const entries = rows.map((item, index) => ({
|
|
40151
40476
|
storageId: text(item.storageRef.id),
|
|
40152
40477
|
createdAt: text(item.metadata?.createdAt) || text(item.storageRef?.createdAt),
|
|
@@ -40154,22 +40479,23 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40154
40479
|
payload: payloads[index]
|
|
40155
40480
|
}));
|
|
40156
40481
|
entries.complete = complete;
|
|
40482
|
+
entries.unread = unread;
|
|
40157
40483
|
return entries;
|
|
40158
40484
|
};
|
|
40159
40485
|
const scanMyAnswers = async (ctx) => {
|
|
40160
|
-
if (ctx.ownScan &&
|
|
40486
|
+
if (ctx.ownScan && ctx.ownScan.complete && now() - ctx.ownScan.at < OWN_ANSWER_SCAN_MAX_AGE_MS && ctx.ownScan.sessionId === ctx.client.sessionId && ctx.ownScan.windowKey === ctx.registry?.windowKey) {
|
|
40161
40487
|
return ctx.ownScan;
|
|
40162
40488
|
}
|
|
40163
40489
|
if (ctx.registry) {
|
|
40164
40490
|
const questions = await loadQuestions(ctx);
|
|
40165
40491
|
const questionIds = new Set(questions.map((question) => question.id));
|
|
40166
|
-
const { entries: entries2, complete: complete2, chainOrders } = await ctx.registry.readAnswers({ questionIds, maxRecords: MAX_OWN_ANSWER_ITEMS, mine: true, signal });
|
|
40492
|
+
const { entries: entries2, complete: complete2, chainOrders, unreadAnswers } = await ctx.registry.readAnswers({ questionIds, maxRecords: MAX_OWN_ANSWER_ITEMS, mine: true, signal });
|
|
40167
40493
|
const latest2 = latestAnswersByQuestion(entries2, { registryConfig: ctx.client.config, questionIds });
|
|
40168
40494
|
const answers2 = /* @__PURE__ */ new Map();
|
|
40169
40495
|
latest2.forEach((byResponder, id) => {
|
|
40170
40496
|
if (byResponder.has(wallet.address.toLowerCase())) answers2.set(id, byResponder.get(wallet.address.toLowerCase()));
|
|
40171
40497
|
});
|
|
40172
|
-
ctx.ownScan = { at: now(), sessionId: ctx.client.sessionId, answers: answers2, complete: complete2, chainOrders };
|
|
40498
|
+
ctx.ownScan = { at: now(), sessionId: ctx.client.sessionId, windowKey: ctx.registry.windowKey, answers: answers2, complete: complete2, chainOrders, unread: unreadAnswers };
|
|
40173
40499
|
return ctx.ownScan;
|
|
40174
40500
|
}
|
|
40175
40501
|
const { items, complete: listed } = await ctx.client.listAll("responses", {
|
|
@@ -40188,36 +40514,41 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40188
40514
|
const entry = byResponder.get(self2);
|
|
40189
40515
|
if (entry) answers.set(questionId, entry);
|
|
40190
40516
|
});
|
|
40191
|
-
ctx.ownScan = { at: now(), sessionId: ctx.client.sessionId, answers, complete };
|
|
40517
|
+
ctx.ownScan = { at: now(), sessionId: ctx.client.sessionId, answers, complete, unread: entries.unread };
|
|
40192
40518
|
return ctx.ownScan;
|
|
40193
40519
|
};
|
|
40194
40520
|
const loadMyAnswers = async (ctx) => {
|
|
40195
40521
|
await ensureReady(ctx);
|
|
40196
40522
|
const scan = await scanMyAnswers(ctx);
|
|
40197
40523
|
const mine = new Map(scan.answers);
|
|
40198
|
-
|
|
40524
|
+
let complete = scan.complete;
|
|
40525
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
40199
40526
|
await loadState();
|
|
40200
|
-
const local = sessionState(ctx).sessionId === ctx.client.sessionId &&
|
|
40201
|
-
|
|
40202
|
-
if (!
|
|
40203
|
-
if (ctx.registry && !complete)
|
|
40527
|
+
const local = sessionState(ctx).sessionId === ctx.client.sessionId && isRecord7(sessionState(ctx).answers) ? sessionState(ctx).answers : {};
|
|
40528
|
+
for (const [questionId, answer] of Object.entries(local)) {
|
|
40529
|
+
if (!isRecord7(answer)) continue;
|
|
40530
|
+
if (ctx.registry && (!scan.complete || !ctx.registry.containsBlock(answer.blockNumber))) continue;
|
|
40204
40531
|
const known = mine.get(questionId);
|
|
40205
40532
|
const chainOrder = scan.chainOrders?.get(questionId);
|
|
40206
|
-
if (ctx.registry && chainOrder && compareChainOrder(answer, chainOrder) < 0)
|
|
40533
|
+
if (ctx.registry && chainOrder && compareChainOrder(answer, chainOrder) < 0) continue;
|
|
40207
40534
|
const newer = ctx.registry ? !known || compareChainOrder(answer, known) > 0 : !known || text(answer.answeredAt) > known.createdAt;
|
|
40208
|
-
if (newer)
|
|
40535
|
+
if (!newer) continue;
|
|
40536
|
+
const replaced = ctx.registry ? await ctx.registry.replacedWrite(answer, blocks) : false;
|
|
40537
|
+
if (replaced === null) complete = false;
|
|
40538
|
+
if (!replaced) {
|
|
40209
40539
|
mine.set(questionId, {
|
|
40210
40540
|
createdAt: text(answer.answeredAt),
|
|
40211
40541
|
...ctx.registry ? { blockNumber: answer.blockNumber, transactionIndex: answer.transactionIndex, logIndex: answer.logIndex } : {},
|
|
40212
40542
|
payload: { answer: { value: answer.value }, additional: { value: answer.comment } }
|
|
40213
40543
|
});
|
|
40214
40544
|
}
|
|
40215
|
-
}
|
|
40545
|
+
}
|
|
40216
40546
|
[...mine.entries()].forEach(([questionId, entry]) => {
|
|
40217
40547
|
const value = entry?.payload?.answer?.value;
|
|
40218
40548
|
if (value === "" || value === null || value === void 0 || Array.isArray(value) && !value.length) mine.delete(questionId);
|
|
40219
40549
|
});
|
|
40220
40550
|
mine.complete = complete;
|
|
40551
|
+
mine.unread = scan.unread;
|
|
40221
40552
|
return mine;
|
|
40222
40553
|
};
|
|
40223
40554
|
const sessionSummary = (ctx, sessionConfig) => ({
|
|
@@ -40233,7 +40564,7 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40233
40564
|
address: wallet.address,
|
|
40234
40565
|
profile: profile(),
|
|
40235
40566
|
currentSession: state.sessions[state.current]?.slug || null,
|
|
40236
|
-
sessions: await mapLimit(Object.values(state.sessions).filter(
|
|
40567
|
+
sessions: await mapLimit(Object.values(state.sessions).filter(isRecord7), READ_CONCURRENCY, async (entry) => ({
|
|
40237
40568
|
session: entry.slug,
|
|
40238
40569
|
title: entry.title || entry.slug,
|
|
40239
40570
|
webUrl: buildSessionWebUrl(entry),
|
|
@@ -40254,9 +40585,9 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40254
40585
|
},
|
|
40255
40586
|
async joinSession({ link, worker, session, inviteCode, groupPassword, claimPassword } = {}) {
|
|
40256
40587
|
let parsed;
|
|
40257
|
-
if (
|
|
40588
|
+
if (trim8(link)) {
|
|
40258
40589
|
parsed = parseSessionLink(link, { allowLocalWorkers: config.allowLocalWorkers });
|
|
40259
|
-
} else if (
|
|
40590
|
+
} else if (trim8(worker) && trim8(session)) {
|
|
40260
40591
|
parsed = {
|
|
40261
40592
|
appOrigin: config.appOrigin,
|
|
40262
40593
|
slug: normalizeSessionSlug(session),
|
|
@@ -40277,9 +40608,9 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40277
40608
|
const offered = parsed.sbtClaims.filter((claim) => !claim.auto).map((claim) => claim.sbt);
|
|
40278
40609
|
if (offered.length) outcome.sbtsToClaimWithClaimSbt = offered;
|
|
40279
40610
|
const separateCodes = {
|
|
40280
|
-
inviteCode:
|
|
40281
|
-
groupPassword:
|
|
40282
|
-
claimPassword:
|
|
40611
|
+
inviteCode: trim8(inviteCode),
|
|
40612
|
+
groupPassword: trim8(groupPassword),
|
|
40613
|
+
claimPassword: trim8(claimPassword)
|
|
40283
40614
|
};
|
|
40284
40615
|
const hasSeparateCodes = Object.values(separateCodes).some(Boolean);
|
|
40285
40616
|
if (hasSeparateCodes && claims.length !== 1) {
|
|
@@ -40309,7 +40640,7 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40309
40640
|
})
|
|
40310
40641
|
);
|
|
40311
40642
|
} catch (error) {
|
|
40312
|
-
outcome.sbtClaims.push({ sbt: claim.sbt, status: "failed", error: error
|
|
40643
|
+
outcome.sbtClaims.push({ sbt: claim.sbt, status: "failed", error: describeError(error) });
|
|
40313
40644
|
}
|
|
40314
40645
|
}
|
|
40315
40646
|
tokenClaims = await ctx.client.login().then((claims2) => {
|
|
@@ -40320,22 +40651,22 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40320
40651
|
outcome.testEth = await ctx.client.describeTestEth();
|
|
40321
40652
|
outcome.signedIn = !loginError;
|
|
40322
40653
|
if (loginError) {
|
|
40323
|
-
outcome.readOnlyReason = loginError
|
|
40654
|
+
outcome.readOnlyReason = describeError(loginError);
|
|
40324
40655
|
outcome.note = "Joined read-only: public questions and results are available, but uploads, answers, statements, and faucet requests need Worker access.";
|
|
40325
40656
|
}
|
|
40326
|
-
const scopes =
|
|
40657
|
+
const scopes = isRecord7(tokenClaims?.scopes) ? tokenClaims.scopes : {};
|
|
40327
40658
|
outcome.scopes = Object.keys(scopes).filter((scope) => scopes[scope] === true);
|
|
40328
40659
|
if (parsed.joinGroupId) {
|
|
40329
40660
|
checkpoint();
|
|
40330
40661
|
try {
|
|
40331
40662
|
if (ctx.registry) throw new Error("Worker groups are not available in on-chain sessions.");
|
|
40332
|
-
await ctx.client.joinGroup(parsed.joinGroupId);
|
|
40663
|
+
await ctx.client.joinGroup(parsed.joinGroupId, { signal });
|
|
40333
40664
|
outcome.joinedGroup = parsed.joinGroupId;
|
|
40334
40665
|
} catch (error) {
|
|
40335
|
-
outcome.groupJoinError = error
|
|
40666
|
+
outcome.groupJoinError = describeError(error);
|
|
40336
40667
|
}
|
|
40337
40668
|
}
|
|
40338
|
-
outcome.description = text(sessionConfig.sessionInfo).slice(0,
|
|
40669
|
+
outcome.description = text(sessionConfig.sessionInfo).slice(0, MAX_DESCRIPTION_LENGTH);
|
|
40339
40670
|
outcome.context = contextOf(sessionConfig);
|
|
40340
40671
|
const sessionSbts = listSessionSbts(sessionConfig);
|
|
40341
40672
|
if (sessionSbts.length) outcome.sessionSbts = sessionSbts;
|
|
@@ -40355,7 +40686,7 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40355
40686
|
return {
|
|
40356
40687
|
...sessionSummary(ctx, sessionConfig),
|
|
40357
40688
|
testEth: await ctx.client.describeTestEth(),
|
|
40358
|
-
description: text(sessionConfig.sessionInfo).slice(0,
|
|
40689
|
+
description: text(sessionConfig.sessionInfo).slice(0, MAX_DESCRIPTION_LENGTH),
|
|
40359
40690
|
context: contextOf(sessionConfig),
|
|
40360
40691
|
...endsAt ? { endsAt } : {},
|
|
40361
40692
|
...sessionSbts.length ? { sessionSbts } : {},
|
|
@@ -40393,10 +40724,12 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40393
40724
|
const results = [];
|
|
40394
40725
|
const answered = [];
|
|
40395
40726
|
const pendingAnswers = [];
|
|
40727
|
+
const operationId = randomUUID2();
|
|
40396
40728
|
let txHash;
|
|
40729
|
+
let reconciliationRequired = false;
|
|
40397
40730
|
const seen = /* @__PURE__ */ new Set();
|
|
40398
40731
|
for (const item of answers) {
|
|
40399
|
-
const questionId =
|
|
40732
|
+
const questionId = trim8(item?.questionId).toLowerCase();
|
|
40400
40733
|
if (seen.has(questionId)) {
|
|
40401
40734
|
results.push({ questionId, ok: false, error: "This question appears more than once in the call; send one answer per question." });
|
|
40402
40735
|
continue;
|
|
@@ -40436,12 +40769,24 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40436
40769
|
continue;
|
|
40437
40770
|
}
|
|
40438
40771
|
try {
|
|
40439
|
-
const
|
|
40772
|
+
const data = JSON.stringify(built.record);
|
|
40773
|
+
const digest = createHash("sha256").update(data).digest("hex");
|
|
40774
|
+
let requestId;
|
|
40775
|
+
await saveState((fresh) => {
|
|
40776
|
+
const entry = fresh.sessions[sessionKey(ctx.info)];
|
|
40777
|
+
if (!isRecord7(entry) || entry.sessionId !== ctx.client.sessionId) throw new Error("Session identity changed; join again before answering.");
|
|
40778
|
+
entry.pendingUploads = isRecord7(entry.pendingUploads) ? entry.pendingUploads : {};
|
|
40779
|
+
const previous = entry.pendingUploads[questionId];
|
|
40780
|
+
requestId = previous?.payloadDigest === digest && trim8(previous.requestId) ? previous.requestId : `answer:v1:${operationId}:${questionId}`;
|
|
40781
|
+
entry.pendingUploads[questionId] = { payloadDigest: digest, requestId };
|
|
40782
|
+
});
|
|
40783
|
+
checkpoint();
|
|
40784
|
+
const storageRef = await ctx.client.upload({ resource: "responses", data, requestId, signal });
|
|
40440
40785
|
if (text(storageRef?.id)) cacheRecord(ctx, text(storageRef.id), built.record);
|
|
40441
|
-
answered.push({ questionId, record: built.record, answeredAt: text(storageRef?.createdAt) || new Date(now()).toISOString() });
|
|
40786
|
+
answered.push({ questionId, requestId, record: built.record, answeredAt: text(storageRef?.createdAt) || new Date(now()).toISOString() });
|
|
40442
40787
|
results.push({ questionId, ok: true, answer: built.record.answer.value });
|
|
40443
40788
|
} catch (error) {
|
|
40444
|
-
results.push({ questionId, ok: false, error: error
|
|
40789
|
+
results.push({ questionId, ok: false, error: describeError(error) });
|
|
40445
40790
|
}
|
|
40446
40791
|
}
|
|
40447
40792
|
if (pendingAnswers.length) {
|
|
@@ -40456,19 +40801,32 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40456
40801
|
pendingAnswers.forEach(({ questionId, record }) => {
|
|
40457
40802
|
answered.push({ questionId, record, answeredAt: new Date(now()).toISOString(), chainOrder: {
|
|
40458
40803
|
blockNumber: submitted.receipt.blockNumber,
|
|
40804
|
+
blockHash: submitted.receipt.blockHash,
|
|
40459
40805
|
transactionIndex: submitted.receipt.transactionIndex,
|
|
40460
40806
|
logIndex: submitted.receipt.logs.at(-1)?.logIndex || 0
|
|
40461
40807
|
} });
|
|
40462
40808
|
results.push({ questionId, ok: true, answer: record.answer.value });
|
|
40463
40809
|
});
|
|
40464
40810
|
} catch (error) {
|
|
40465
|
-
|
|
40811
|
+
if (typeof error.txHash === "string" && /^0x[0-9a-f]{64}$/i.test(error.txHash)) txHash = error.txHash;
|
|
40812
|
+
reconciliationRequired = error.reconciliationRequired === true;
|
|
40813
|
+
pendingAnswers.forEach(({ questionId }) => results.push({ questionId, ok: false, error: describeError(error) }));
|
|
40814
|
+
}
|
|
40815
|
+
}
|
|
40816
|
+
let warning;
|
|
40817
|
+
if (answered.length) {
|
|
40818
|
+
try {
|
|
40819
|
+
await rememberAnswers(ctx, answered);
|
|
40820
|
+
} catch {
|
|
40821
|
+
ctx.ownScan = null;
|
|
40822
|
+
warning = "Answers were submitted, but the local answer history could not be saved. Do not resubmit them to fix this warning.";
|
|
40466
40823
|
}
|
|
40467
40824
|
}
|
|
40468
|
-
if (answered.length) await rememberAnswers(ctx, answered);
|
|
40469
40825
|
return {
|
|
40470
40826
|
session: ctx.info.slug,
|
|
40471
40827
|
...txHash ? { txHash } : {},
|
|
40828
|
+
...reconciliationRequired ? { reconciliationRequired: true } : {},
|
|
40829
|
+
...warning ? { warning } : {},
|
|
40472
40830
|
submitted: results.filter((result) => result.ok).length,
|
|
40473
40831
|
failed: results.filter((result) => !result.ok).length,
|
|
40474
40832
|
results
|
|
@@ -40510,7 +40868,7 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40510
40868
|
];
|
|
40511
40869
|
checkpoint();
|
|
40512
40870
|
try {
|
|
40513
|
-
const storageRef = await ctx.client.upload({ resource: "questions", data: built.record, tags: tagList });
|
|
40871
|
+
const storageRef = await ctx.client.upload({ resource: "questions", data: built.record, tags: tagList, signal });
|
|
40514
40872
|
if (text(storageRef?.id)) cacheRecord(ctx, text(storageRef.id), built.record);
|
|
40515
40873
|
return { session: ctx.info.slug, questionId: built.id, created: true, prompt: built.record.prompt };
|
|
40516
40874
|
} catch (error) {
|
|
@@ -40554,18 +40912,21 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40554
40912
|
let entries;
|
|
40555
40913
|
let complete;
|
|
40556
40914
|
let listedCount;
|
|
40915
|
+
let unreadAnswers;
|
|
40557
40916
|
const knownIds = new Set(questions.map((question) => question.id));
|
|
40558
40917
|
if (ctx.registry) {
|
|
40559
40918
|
const result = await ctx.registry.readAnswers({ questionIds: knownIds, maxRecords: cap, signal });
|
|
40560
40919
|
entries = result.entries;
|
|
40561
40920
|
complete = result.complete;
|
|
40562
40921
|
listedCount = entries.length;
|
|
40922
|
+
unreadAnswers = result.unreadAnswers;
|
|
40563
40923
|
} else {
|
|
40564
40924
|
const result = await ctx.client.listAll("responses", { maxItems: MAX_RESULT_RECORDS, signal });
|
|
40565
40925
|
const listed = result.items.filter((item) => text(item.storageRef?.id)).sort((left, right) => text(left.metadata?.createdAt) < text(right.metadata?.createdAt) ? 1 : -1);
|
|
40566
40926
|
entries = await toEntries(ctx, listed.slice(0, cap));
|
|
40567
40927
|
complete = result.complete;
|
|
40568
40928
|
listedCount = listed.length;
|
|
40929
|
+
unreadAnswers = entries.unread;
|
|
40569
40930
|
}
|
|
40570
40931
|
const latest = latestAnswersByQuestion(entries, {
|
|
40571
40932
|
sessionSlug: ctx.info.slug,
|
|
@@ -40578,6 +40939,7 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40578
40939
|
resultsPublic: true,
|
|
40579
40940
|
webUrl: buildSessionWebUrl(ctx.info),
|
|
40580
40941
|
participants: participants.size,
|
|
40942
|
+
unreadAnswers,
|
|
40581
40943
|
partial: !complete || listedCount > cap || questions.complete === false || entries.complete === false,
|
|
40582
40944
|
questions: questions.filter((question) => !wanted || wanted.has(question.id)).map((question) => summarizeQuestion(question, latest.get(question.id)))
|
|
40583
40945
|
};
|
|
@@ -40586,10 +40948,10 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40586
40948
|
const ctx = await resolveSession(session);
|
|
40587
40949
|
if (ctx.registry) throw new Error("Worker groups are not available in on-chain sessions.");
|
|
40588
40950
|
await ensureReady(ctx);
|
|
40589
|
-
const id =
|
|
40951
|
+
const id = trim8(groupId);
|
|
40590
40952
|
if (!/^[A-Za-z0-9._:-]{1,128}$/.test(id)) throw new Error("Pass a valid group id.");
|
|
40591
40953
|
checkpoint();
|
|
40592
|
-
await ctx.client.joinGroup(id);
|
|
40954
|
+
await ctx.client.joinGroup(id, { signal });
|
|
40593
40955
|
return { session: ctx.info.slug, joinedGroup: id };
|
|
40594
40956
|
},
|
|
40595
40957
|
async claimSbt({ session, sbt: sbtAddress, inviteCode, groupPassword, claimPassword } = {}) {
|
|
@@ -40601,10 +40963,10 @@ var createParticipant = ({ config, wallet, fetchImpl = globalThis.fetch, now = (
|
|
|
40601
40963
|
});
|
|
40602
40964
|
const result = await sbt.claim({
|
|
40603
40965
|
ctx,
|
|
40604
|
-
sbtAddress:
|
|
40605
|
-
inviteCode:
|
|
40606
|
-
groupPassword:
|
|
40607
|
-
claimPassword:
|
|
40966
|
+
sbtAddress: trim8(sbtAddress),
|
|
40967
|
+
inviteCode: trim8(inviteCode),
|
|
40968
|
+
groupPassword: trim8(groupPassword),
|
|
40969
|
+
claimPassword: trim8(claimPassword),
|
|
40608
40970
|
signal
|
|
40609
40971
|
});
|
|
40610
40972
|
await ctx.client.login().catch(() => {
|
|
@@ -40628,7 +40990,7 @@ var sessionParam = {
|
|
|
40628
40990
|
type: "string",
|
|
40629
40991
|
description: "Session slug or link. Defaults to the session joined most recently."
|
|
40630
40992
|
};
|
|
40631
|
-
var
|
|
40993
|
+
var text4 = (description, extra = {}) => ({ type: "string", description, ...extra });
|
|
40632
40994
|
var READ = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true };
|
|
40633
40995
|
var WRITE = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true };
|
|
40634
40996
|
var SPENDS = { ...WRITE, destructiveHint: true };
|
|
@@ -40648,9 +41010,9 @@ var buildTools = (participant) => [
|
|
|
40648
41010
|
inputSchema: {
|
|
40649
41011
|
type: "object",
|
|
40650
41012
|
properties: {
|
|
40651
|
-
displayName:
|
|
40652
|
-
modelId:
|
|
40653
|
-
platform:
|
|
41013
|
+
displayName: text4("Name shown in reports; up to 80 characters. Empty string clears it."),
|
|
41014
|
+
modelId: text4("Model identifier, for example the model you run on."),
|
|
41015
|
+
platform: text4("Agent platform or harness.")
|
|
40654
41016
|
},
|
|
40655
41017
|
additionalProperties: false
|
|
40656
41018
|
},
|
|
@@ -40664,12 +41026,12 @@ var buildTools = (participant) => [
|
|
|
40664
41026
|
inputSchema: {
|
|
40665
41027
|
type: "object",
|
|
40666
41028
|
properties: {
|
|
40667
|
-
link:
|
|
40668
|
-
worker:
|
|
40669
|
-
session:
|
|
40670
|
-
inviteCode:
|
|
40671
|
-
groupPassword:
|
|
40672
|
-
claimPassword:
|
|
41029
|
+
link: text4("Session link, including any invite parameters."),
|
|
41030
|
+
worker: text4("Worker origin, when you have no link."),
|
|
41031
|
+
session: text4("Session slug, when you have no link."),
|
|
41032
|
+
inviteCode: text4("SBT invite code, if one was given separately from the link."),
|
|
41033
|
+
groupPassword: text4("SBT group password, if one was given separately."),
|
|
41034
|
+
claimPassword: text4("SBT claim password, if one was given separately.")
|
|
40673
41035
|
},
|
|
40674
41036
|
additionalProperties: false
|
|
40675
41037
|
},
|
|
@@ -40695,7 +41057,7 @@ var buildTools = (participant) => [
|
|
|
40695
41057
|
{
|
|
40696
41058
|
name: "list_questions",
|
|
40697
41059
|
title: "List questions",
|
|
40698
|
-
description: "List the session\u2019s statements and questions with their type, options or scale, and your latest answer. Unanswered ones come first by default.",
|
|
41060
|
+
description: "List the session\u2019s statements and questions with their type, options or scale, and your latest answer. Unanswered ones come first by default. questionsComplete or myAnswersComplete is false when listing or payload reads are unfinished; unreadQuestions and unreadMyAnswers count listed payloads that could not be read.",
|
|
40699
41061
|
inputSchema: {
|
|
40700
41062
|
type: "object",
|
|
40701
41063
|
properties: {
|
|
@@ -40711,7 +41073,7 @@ var buildTools = (participant) => [
|
|
|
40711
41073
|
{
|
|
40712
41074
|
name: "answer",
|
|
40713
41075
|
title: "Answer questions",
|
|
40714
|
-
description: 'Submit answers. Values by type: binary "Agree" | "Unsure" | "Disagree"; rating a
|
|
41076
|
+
description: 'Submit answers. Values by type: binary "Agree" | "Unsure" | "Disagree"; rating a number on the question\u2019s scale and step grid (default step 1); multichoice the option text (an array when several are allowed); quadratic one signed whole-number vote per option whose squares sum within the credit budget; freeform text. Answering again replaces your previous answer.',
|
|
40715
41077
|
inputSchema: {
|
|
40716
41078
|
type: "object",
|
|
40717
41079
|
properties: {
|
|
@@ -40723,9 +41085,9 @@ var buildTools = (participant) => [
|
|
|
40723
41085
|
items: {
|
|
40724
41086
|
type: "object",
|
|
40725
41087
|
properties: {
|
|
40726
|
-
questionId:
|
|
41088
|
+
questionId: text4("Question id from list_questions."),
|
|
40727
41089
|
value: { description: "The answer value; see the tool description for each type." },
|
|
40728
|
-
comment:
|
|
41090
|
+
comment: text4("Optional reasoning shown with your answer (up to 4000 characters)."),
|
|
40729
41091
|
importance: { type: "integer", minimum: 0, maximum: 10, description: "How much this question matters to you." },
|
|
40730
41092
|
conviction: { type: "integer", minimum: 0, maximum: 10, description: "How sure you are of your answer." }
|
|
40731
41093
|
},
|
|
@@ -40748,7 +41110,7 @@ var buildTools = (participant) => [
|
|
|
40748
41110
|
type: "object",
|
|
40749
41111
|
properties: {
|
|
40750
41112
|
session: sessionParam,
|
|
40751
|
-
prompt:
|
|
41113
|
+
prompt: text4("The statement or question text."),
|
|
40752
41114
|
type: { type: "string", enum: ["binary", "rating", "multichoice", "quadratic", "freeform"] },
|
|
40753
41115
|
options: { type: "array", items: { type: "string" }, description: "Options for multichoice or quadratic." },
|
|
40754
41116
|
singleSelect: { type: "boolean", description: "Multichoice: allow only one option." },
|
|
@@ -40764,7 +41126,7 @@ var buildTools = (participant) => [
|
|
|
40764
41126
|
{
|
|
40765
41127
|
name: "get_results",
|
|
40766
41128
|
title: "Get results",
|
|
40767
|
-
description: "Summarize everyone\u2019s latest answers per question when the session publishes results: agree/unsure/disagree counts, rating means, option counts, and how many answers came from self-declared agents.",
|
|
41129
|
+
description: "Summarize everyone\u2019s latest answers per question when the session publishes results: agree/unsure/disagree counts, rating means, option counts, and how many answers came from self-declared agents. partial is true when a scan is unfinished, capped, or any listed question or answer payload could not be read. unreadAnswers counts the selected listed answer records that could not be read; readable older answers may still appear until those records recover.",
|
|
40768
41130
|
inputSchema: {
|
|
40769
41131
|
type: "object",
|
|
40770
41132
|
properties: {
|
|
@@ -40783,7 +41145,7 @@ var buildTools = (participant) => [
|
|
|
40783
41145
|
description: "Join an open group in the session by its id (from a ?joinGroup= link).",
|
|
40784
41146
|
inputSchema: {
|
|
40785
41147
|
type: "object",
|
|
40786
|
-
properties: { session: sessionParam, groupId:
|
|
41148
|
+
properties: { session: sessionParam, groupId: text4("Group id.") },
|
|
40787
41149
|
required: ["groupId"],
|
|
40788
41150
|
additionalProperties: false
|
|
40789
41151
|
},
|
|
@@ -40798,10 +41160,10 @@ var buildTools = (participant) => [
|
|
|
40798
41160
|
type: "object",
|
|
40799
41161
|
properties: {
|
|
40800
41162
|
session: sessionParam,
|
|
40801
|
-
sbt:
|
|
40802
|
-
inviteCode:
|
|
40803
|
-
groupPassword:
|
|
40804
|
-
claimPassword:
|
|
41163
|
+
sbt: text4("SBT contract address."),
|
|
41164
|
+
inviteCode: text4("Invite code, when the SBT uses invites."),
|
|
41165
|
+
groupPassword: text4("Group password, when the SBT uses one."),
|
|
41166
|
+
claimPassword: text4("Claim password, when the SBT uses per-claim passwords.")
|
|
40805
41167
|
},
|
|
40806
41168
|
required: ["sbt"],
|
|
40807
41169
|
additionalProperties: false
|