@ixo/asks-mcp 1.5.0-dev.0 → 1.5.0-dev.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/asks-mcp.mjs +653 -148
  2. package/package.json +1 -1
package/dist/asks-mcp.mjs CHANGED
@@ -4026,12 +4026,12 @@ var require_fast_uri = __commonJS({
4026
4026
  }
4027
4027
  function hasMalformedPercentEncoding(component) {
4028
4028
  if (component === void 0) return false;
4029
- let percent = component.indexOf("%");
4030
- while (percent !== -1) {
4031
- if (percent + 2 >= component.length || !/^[\da-f]{2}$/iu.test(component.slice(percent + 1, percent + 3))) {
4029
+ let percent2 = component.indexOf("%");
4030
+ while (percent2 !== -1) {
4031
+ if (percent2 + 2 >= component.length || !/^[\da-f]{2}$/iu.test(component.slice(percent2 + 1, percent2 + 3))) {
4032
4032
  return true;
4033
4033
  }
4034
- percent = component.indexOf("%", percent + 3);
4034
+ percent2 = component.indexOf("%", percent2 + 3);
4035
4035
  }
4036
4036
  return false;
4037
4037
  }
@@ -7447,19 +7447,28 @@ var init_callTag = __esm({
7447
7447
  }
7448
7448
  });
7449
7449
 
7450
+ // ../../app/lib/networkName.ts
7451
+ var NETWORK;
7452
+ var init_networkName = __esm({
7453
+ "../../app/lib/networkName.ts"() {
7454
+ "use strict";
7455
+ NETWORK = process.env.NEXT_PUBLIC_CHAIN_NETWORK || "testnet";
7456
+ }
7457
+ });
7458
+
7450
7459
  // ../../app/lib/networks.ts
7451
- var NETWORK, ORIGIN, originOf, EXPERT, expertOrigin;
7460
+ var ORIGIN, originOf, expertOrigin, PRO;
7452
7461
  var init_networks = __esm({
7453
7462
  "../../app/lib/networks.ts"() {
7454
7463
  "use strict";
7455
- NETWORK = process.env.NEXT_PUBLIC_CHAIN_NETWORK || "testnet";
7464
+ init_networkName();
7456
7465
  ORIGIN = {
7457
- mainnet: (process.env.NEXT_PUBLIC_MAINNET_APP_URL || "https://openhands.network").replace(/\/+$/, ""),
7458
- testnet: (process.env.NEXT_PUBLIC_TESTNET_APP_URL || "https://test.openhands.network").replace(/\/+$/, "")
7466
+ mainnet: (process.env.NEXT_PUBLIC_MAINNET_APP_URL || "https://asks.world").replace(/\/+$/, ""),
7467
+ testnet: (process.env.NEXT_PUBLIC_TESTNET_APP_URL || "https://test.asks.world").replace(/\/+$/, "")
7459
7468
  };
7460
7469
  originOf = (network2) => ORIGIN[network2 === "mainnet" ? "mainnet" : "testnet"];
7461
- EXPERT = { [ORIGIN.mainnet]: "https://asks.world", [ORIGIN.testnet]: "https://test.asks.world" };
7462
- expertOrigin = (origin2) => EXPERT[origin2.replace(/\/+$/, "")] ?? origin2.replace(/\/+$/, "");
7470
+ expertOrigin = (origin2) => origin2.replace(/\/+$/, "");
7471
+ PRO = { [ORIGIN.mainnet]: "https://pro.asks.world", [ORIGIN.testnet]: "https://pro.test.asks.world" };
7463
7472
  }
7464
7473
  });
7465
7474
 
@@ -7472,14 +7481,14 @@ function reviewSlugOf(facts) {
7472
7481
  function askPage(site, collectionId, facts) {
7473
7482
  const base5 = site.replace(/\/+$/, "");
7474
7483
  const review = reviewSlugOf(facts);
7475
- if (review) return `${expertOrigin(base5)}/expert/reviews/${review}`;
7484
+ if (review) return `${expertOrigin(base5)}/expert/reviews/${collectionId}`;
7476
7485
  const slug = facts.slug ?? (facts.campaign ? slugOfTag(facts.campaign) : null);
7477
7486
  return slug ? `${base5}/opencall/${slug}` : `${base5}/t/${collectionId}`;
7478
7487
  }
7479
7488
  function answerPage(site, collectionId, claimId, facts) {
7480
7489
  const base5 = site.replace(/\/+$/, "");
7481
7490
  const review = reviewSlugOf(facts);
7482
- return review ? `${expertOrigin(base5)}/expert/reviews/${review}/report/${encodeURIComponent(claimId)}` : `${base5}/r/${collectionId}/trace/${encodeURIComponent(claimId)}`;
7491
+ return review ? `${expertOrigin(base5)}/expert/reviews/${collectionId}/report/${encodeURIComponent(claimId)}` : `${base5}/r/${collectionId}/trace/${encodeURIComponent(claimId)}`;
7483
7492
  }
7484
7493
  function inviteLink(site, token, review) {
7485
7494
  return review ? `${expertOrigin(site)}/expert/i/${token}` : `${site.replace(/\/+$/, "")}/opencall/i/${token}`;
@@ -7766,6 +7775,12 @@ function requiredSections(raw) {
7766
7775
  return [...new Set(names)];
7767
7776
  }
7768
7777
  function reportCheckText(requirement) {
7778
+ if (requirement.v === 2) {
7779
+ const extra2 = requirement.sections.length ? ` and has the sections ${requirement.sections.map((one) => `"${one}"`).join(", ")}` : "";
7780
+ const line2 = `The answer is a review report (written out, or attached as a PDF) of what this ask asked to be reviewed${extra2}. Fail this only if there is no report, or it plainly reviews something else.`;
7781
+ if (line2.length <= 300) return line2;
7782
+ return "The answer is a review report (written out, or attached as a PDF) of what this ask asked to be reviewed, with the sections the ask names. Fail this only if there is no report, or it plainly reviews something else.";
7783
+ }
7769
7784
  const extra = requirement.sections.length ? ` It also has these sections: ${requirement.sections.map((one) => `"${one}"`).join(", ")}.` : "";
7770
7785
  return `The answer is a review report, either written out or attached as a PDF. It says what was checked, when, where and what was in scope, and the outcome; lists the steps taken, each with what was expected and what was seen, passed, failed, blocked, not tested or left out; lists the findings, each with its impact and a severity of blocker, major or minor with why, and every major finding says how to reproduce it (a blocker says how, or what is missing to reproduce it); gives recommendations, each with a priority and what would show it is done; and gives the review's limitations.${extra}`;
7771
7786
  }
@@ -7782,6 +7797,7 @@ var init_reportLine = __esm({
7782
7797
  // ../../packages/openhands-sdk/src/rubric/report.ts
7783
7798
  function reportProblems(raw, requirement, fileIds = []) {
7784
7799
  const problems = [];
7800
+ const review = requirement?.v === 2;
7785
7801
  const report = obj(raw);
7786
7802
  if (!report) return ["The report must be an object with summary, steps, findings, recommendations and limitations."];
7787
7803
  let size2 = 0;
@@ -7873,9 +7889,13 @@ function reportProblems(raw, requirement, fileIds = []) {
7873
7889
  if (finding.severity === "blocker" && finding.how_to_reproduce === void 0 && finding.cannot_reproduce === void 0)
7874
7890
  problems.push(`${where2} is a blocker: say how to reproduce it, or under cannot_reproduce what is missing to reproduce it.`);
7875
7891
  if (finding.cannot_reproduce !== void 0 && finding.severity !== "blocker") problems.push(`${where2}: only a blocker may say it cannot be reproduced; say how to reproduce it.`);
7892
+ if (review && finding.severity === "minor" && finding.how_to_reproduce === void 0) problems.push(`${where2}: say how to reproduce it.`);
7893
+ if (review && (!Array.isArray(finding.evidence) || finding.evidence.length === 0)) problems.push(`${where2}: attach its evidence (a screenshot, recording or trace) and name the file under evidence.`);
7876
7894
  evidence(where2, finding.evidence);
7877
7895
  });
7878
7896
  }
7897
+ const cited = [report.steps, report.findings].some((entries2) => Array.isArray(entries2) && entries2.some((one) => Array.isArray(obj(one)?.evidence) && obj(one).evidence.length > 0));
7898
+ if (review && !cited) problems.push("Attach your evidence: at least one screenshot, recording or trace, named under a step's or a finding's evidence.");
7879
7899
  if (!Array.isArray(report.recommendations)) problems.push("Add recommendations: a list, empty when there are none.");
7880
7900
  else if (report.recommendations.length > REPORT_LIMITS.lines) problems.push(`Keep it to ${REPORT_LIMITS.lines} recommendations.`);
7881
7901
  else
@@ -7890,7 +7910,8 @@ function reportProblems(raw, requirement, fileIds = []) {
7890
7910
  text4(where2, recommendation.done_when, "say what you would see once it is done");
7891
7911
  text4(where2, recommendation.advice, "advice is optional text", REPORT_LIMITS.text, false);
7892
7912
  });
7893
- if (!Array.isArray(report.limitations)) problems.push("Add limitations: a list of short lines, empty when there are none.");
7913
+ if (review && Array.isArray(report.limitations) && report.limitations.length === 0) problems.push("Add limitations: what you could not test, or why the result may not hold.");
7914
+ else if (!Array.isArray(report.limitations)) problems.push("Add limitations: a list of short lines, empty when there are none.");
7894
7915
  else if (report.limitations.length > REPORT_LIMITS.lines || report.limitations.some((one) => !isText(one, REPORT_LIMITS.text))) problems.push(`Limitations: at most ${REPORT_LIMITS.lines} lines of text.`);
7895
7916
  const sections = report.sections === void 0 ? {} : obj(report.sections);
7896
7917
  if (!sections) problems.push("Sections: an object of heading \u2192 text.");
@@ -8039,6 +8060,38 @@ var init_survey = __esm({
8039
8060
  }
8040
8061
  });
8041
8062
 
8063
+ // ../../packages/openhands-sdk/src/rubric/share.ts
8064
+ var share_exports = {};
8065
+ __export(share_exports, {
8066
+ grossFor: () => grossFor,
8067
+ isPrivateTerms: () => isPrivateTerms,
8068
+ netOf: () => netOf,
8069
+ sharedCall: () => sharedCall
8070
+ });
8071
+ function isPrivateTerms(terms) {
8072
+ const doc = terms;
8073
+ return doc?.access === "invite" || doc?.access === "apply" || doc?.call?.kind === "review";
8074
+ }
8075
+ var sharedCall, grossFor, netOf;
8076
+ var init_share = __esm({
8077
+ "../../packages/openhands-sdk/src/rubric/share.ts"() {
8078
+ "use strict";
8079
+ sharedCall = (terms) => Boolean(terms?.call) && isPrivateTerms(terms);
8080
+ grossFor = (net, split3) => {
8081
+ const PPM = 1000000n;
8082
+ const rate = BigInt(Math.ceil((split3.network + split3.node) * 1e4));
8083
+ if (rate <= 0n) return net;
8084
+ if (rate >= PPM) throw new Error("the split leaves nothing for the person");
8085
+ const keep = PPM - rate;
8086
+ return Number((BigInt(net) * PPM + keep - 1n) / keep);
8087
+ };
8088
+ netOf = (leg, split3) => {
8089
+ const cut = (percent2) => BigInt(leg) * BigInt(Math.round(percent2 * 1e4)) / 1000000n;
8090
+ return Number(BigInt(leg) - cut(split3.network) - cut(split3.node));
8091
+ };
8092
+ }
8093
+ });
8094
+
8042
8095
  // ../../packages/openhands-sdk/src/rubric/terms.ts
8043
8096
  var terms_exports = {};
8044
8097
  __export(terms_exports, {
@@ -8062,15 +8115,19 @@ __export(terms_exports, {
8062
8115
  doneMeansSentence: () => doneMeansSentence,
8063
8116
  firstUrl: () => firstUrl,
8064
8117
  gatePhrase: () => gatePhrase,
8118
+ grossFor: () => grossFor,
8065
8119
  hostOf: () => hostOf,
8066
8120
  inferDeliverable: () => inferDeliverable,
8121
+ isPrivateTerms: () => isPrivateTerms,
8067
8122
  matchHuman: () => matchHuman,
8068
8123
  matchSkill: () => matchSkill,
8124
+ netOf: () => netOf,
8069
8125
  normalizeTerms: () => normalizeTerms,
8070
8126
  parseTerms: () => parseTerms,
8071
8127
  requiredTerms: () => requiredTerms,
8072
8128
  requiresPoster: () => requiresPoster,
8073
8129
  servableByOracle: () => servableByOracle,
8130
+ sharedCall: () => sharedCall,
8074
8131
  withReport: () => withReport
8075
8132
  });
8076
8133
  function firstUrl(text4) {
@@ -8227,6 +8284,8 @@ function callOf(raw) {
8227
8284
  const money = call.money;
8228
8285
  if (!money || typeof money !== "object") return null;
8229
8286
  if (!amount(money.prize) || !whole(money.runnersN, 0, 5) || !amount(money.runnersEach) || !amount(money.entryEach) || !whole(money.entryUpTo, 1, 1e4)) return null;
8287
+ const share = call.share;
8288
+ if (share !== void 0 && (!share || typeof share !== "object" || !nonNegative(share.network) || !nonNegative(share.node) || share.network + share.node >= 100)) return null;
8230
8289
  if (!date5(call.closes) || !date5(call.drop) || !date5(call.pick)) return null;
8231
8290
  let legs;
8232
8291
  if (call.legs !== void 0) {
@@ -8271,6 +8330,7 @@ function callOf(raw) {
8271
8330
  poster: call.poster,
8272
8331
  points: [...call.points],
8273
8332
  money: { prize: money.prize, runnersN: money.runnersN, runnersEach: money.runnersEach, entryEach: money.entryEach, entryUpTo: money.entryUpTo },
8333
+ ...share ? { share: { network: share.network, node: share.node } } : {},
8274
8334
  closes: call.closes,
8275
8335
  drop: call.drop,
8276
8336
  pick: call.pick,
@@ -8291,10 +8351,10 @@ function deliverableOf(deliverable) {
8291
8351
  function reportOf(raw) {
8292
8352
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
8293
8353
  const sections = requiredSections(raw.sections);
8294
- return sections ? { report: { sections } } : null;
8354
+ return sections ? { report: { sections, ...raw.v === 2 ? { v: 2 } : {} } } : null;
8295
8355
  }
8296
- function withReport(terms, sections) {
8297
- const requirement = { sections: requiredSections([...sections]) ?? [] };
8356
+ function withReport(terms, sections, rules) {
8357
+ const requirement = { sections: requiredSections([...sections]) ?? [], ...rules ? { v: rules.v } : {} };
8298
8358
  const { via, ...rest } = terms;
8299
8359
  const kept = terms.criteria.filter((criterion) => criterion.id !== REPORT_CHECK_ID && !(criterion.kind === "measure" && PAGE_CHECK_GATES.has(criterion.gate?.id ?? "")));
8300
8360
  return {
@@ -8397,6 +8457,7 @@ var init_terms = __esm({
8397
8457
  "use strict";
8398
8458
  init_report();
8399
8459
  init_survey();
8460
+ init_share();
8400
8461
  PHYSICAL = /\b(photo|photograph|picture|snap|image of|screenshot|screen shot|selfie|video|film|scan|handwritten|sticky note|poster|in person|visit|go to|walk|drive|deliver|pick up|drop off|knock|doorstep|on ?site|meet|queue|shelf|receipt)\b/i;
8401
8462
  URL_PATTERN = /\bhttps?:\/\/[^\s"'<>)]+|\b(?:[a-z0-9-]+\.)+(?:com|org|net|io|dev|app|earth|xyz|co|ai|me|so|sh|eu|uk|de)(?:\/[^\s"'<>)]*)?/i;
8402
8463
  CHECK_WORDS = /\b(check|verify|confirm|test|load|loads|loading|status|up|down|reachable|online|responds?|working|works|broken|404|uptime|ssl|redirect)\b/i;
@@ -8606,6 +8667,12 @@ function realPhotoRule(shape) {
8606
8667
  const refusal = `${shape.pair ? "A photo is" : "This is"} not a direct photograph of the real ${shape.outdoors ? "outdoor place" : "scene"} asked for (a screen, a print, a generated picture${shape.outdoors ? " or an indoor shot" : ""}). Take the photo yourself, there.`;
8607
8668
  return { rule, text: `${rule} ${fails}`, refusal };
8608
8669
  }
8670
+ function reviewBrief(terms) {
8671
+ const subject = terms.call?.subject;
8672
+ const what = subject ? ` It reviews ${subject.name}${subject.version ? ` version ${subject.version}` : ""}.` : "";
8673
+ const scenarios = terms.call?.scenarios?.length ? ` The scenarios to try: ${terms.call.scenarios.map((one, index) => `${index + 1}. ${one}`).join(" ")}` : "";
8674
+ return `${terms.task.replace(/[.\s]+$/, "")}.${what}${scenarios}`;
8675
+ }
8609
8676
  function nativeGate(gate, index) {
8610
8677
  switch (gate.id) {
8611
8678
  /* `present` is false for null, "" and [] — exactly our "there is a
@@ -8774,8 +8841,9 @@ function toEngineRubric(terms, binding, options = {}) {
8774
8841
  const context = { radiusMeters: radius && radius.id === "within-radius" ? radius.meters : null, pinFromAsk: options.pinFromAsk === true };
8775
8842
  const pair = normalized.deliverable.type === "photo-pair" || measure("before-after") !== null;
8776
8843
  const expectsExtra = normalized.site === true || measure("with-file") !== null || measure("at-registered-point") !== null;
8844
+ const reviewRules = normalized.report?.v === 2 && normalized.call?.kind === "review" && Boolean(normalized.approver);
8777
8845
  const judgeSend = {
8778
- task: literal2(options.task?.trim() || normalized.task),
8846
+ task: literal2(options.task?.trim() || (reviewRules ? reviewBrief(normalized) : normalized.task)),
8779
8847
  text: TEXT,
8780
8848
  ...withPhoto ? { photo: PHOTO } : {},
8781
8849
  ...pair ? { before: BEFORE_PHOTO, after: AFTER_PHOTO } : {},
@@ -8784,6 +8852,7 @@ function toEngineRubric(terms, binding, options = {}) {
8784
8852
  };
8785
8853
  const guidance = guidanceFor(normalized.deliverable.type);
8786
8854
  const gates = [];
8855
+ const clearFailures = /* @__PURE__ */ new Set();
8787
8856
  let review = null;
8788
8857
  const personTasks = [];
8789
8858
  const shown2 = withPhoto ? [PHOTO, TEXT] : [TEXT];
@@ -8837,7 +8906,7 @@ function toEngineRubric(terms, binding, options = {}) {
8837
8906
  expect: "boolean",
8838
8907
  onError: ON_ERROR
8839
8908
  });
8840
- gates.push({
8909
+ const gate = {
8841
8910
  "@type": "Gate",
8842
8911
  code: codeFor(index, "judged"),
8843
8912
  reason: criterion.text,
@@ -8845,7 +8914,9 @@ function toEngineRubric(terms, binding, options = {}) {
8845
8914
  when: field(`ai.${name8}.valid`, "isTrue"),
8846
8915
  onFail: "reject",
8847
8916
  class: "refuted"
8848
- });
8917
+ };
8918
+ gates.push(gate);
8919
+ if (reviewRules && criterion.id === REPORT_CHECK_ID) clearFailures.add(gate);
8849
8920
  return;
8850
8921
  }
8851
8922
  if (!normalized.approver) throw new Error(`criterion ${criterion.id} is attested by the poster; an oracle cannot serve it`);
@@ -8962,6 +9033,26 @@ function toEngineRubric(terms, binding, options = {}) {
8962
9033
  onFail: "reject",
8963
9034
  class: "insufficient_evidence"
8964
9035
  });
9036
+ if (reviewRules) {
9037
+ extra += 1;
9038
+ gates.push({
9039
+ "@type": "Gate",
9040
+ code: codeFor(extra, "report-evidence"),
9041
+ reason: "A review report needs its evidence: attach at least one screenshot, recording or trace and name it under a step's or a finding's evidence",
9042
+ when: { "@type": "Condition", any: [field(TEXT, "matches", escapeRegex2('"evidence":["')), field(PHOTO, "mediaTypeIn", ["application/pdf"])] },
9043
+ onFail: "reject",
9044
+ class: "insufficient_evidence"
9045
+ });
9046
+ extra += 1;
9047
+ gates.push({
9048
+ "@type": "Gate",
9049
+ code: codeFor(extra, "report-limits"),
9050
+ reason: "The report states no limitations: say what could not be tested, or why the result may not hold",
9051
+ when: { "@type": "Condition", any: [field(TEXT, "matches", escapeRegex2(',"limitations":["')), field(PHOTO, "mediaTypeIn", ["application/pdf"])] },
9052
+ onFail: "review",
9053
+ class: "insufficient_evidence"
9054
+ });
9055
+ }
8965
9056
  }
8966
9057
  if (normalized.approver) {
8967
9058
  extra += 1;
@@ -8989,7 +9080,7 @@ function toEngineRubric(terms, binding, options = {}) {
8989
9080
  });
8990
9081
  if (normalized.approver) {
8991
9082
  const readsFacts = (gate) => /"field":"(ext|ai)\./.test(JSON.stringify(gate.when));
8992
- for (const gate of gates) if (gate.onFail === "reject" && readsFacts(gate)) gate.onFail = "review";
9083
+ for (const gate of gates) if (gate.onFail === "reject" && readsFacts(gate) && !clearFailures.has(gate)) gate.onFail = "review";
8993
9084
  for (const source of sources) source.onError = "review";
8994
9085
  for (const check2 of aiChecks) check2.onError = "review";
8995
9086
  }
@@ -9536,7 +9627,9 @@ var init_gateMet = __esm({
9536
9627
  "not-elsewhere": "Taken at the place reported",
9537
9628
  "camera-or-live": "The photo carries camera data or a live capture",
9538
9629
  person: "A person approved the answer",
9539
- report: "A review report was sent, as structured text or a PDF"
9630
+ report: "A review report was sent, as structured text or a PDF",
9631
+ "report-evidence": "The report carries its evidence",
9632
+ "report-limits": "The report states its limitations"
9540
9633
  };
9541
9634
  RULE_IS_REASON = /* @__PURE__ */ new Set(["judged", "real-photo"]);
9542
9635
  gateLabel = (code10) => /^OH-\d+-([A-Z0-9-]+)$/.exec(code10)?.[1]?.toLowerCase() ?? null;
@@ -9565,7 +9658,9 @@ var init_gateMet = __esm({
9565
9658
  "not-elsewhere": "Taken there",
9566
9659
  "camera-or-live": "Camera or live capture",
9567
9660
  person: "Approved by a person",
9568
- report: "Report received"
9661
+ report: "Report received",
9662
+ "report-evidence": "Evidence attached",
9663
+ "report-limits": "Limitations stated"
9569
9664
  };
9570
9665
  PERSON_WORDS = {
9571
9666
  approved: "A person approved the answer",
@@ -27287,7 +27382,9 @@ function firstStore(candidates, init, timeoutMs, fetchImpl, outer) {
27287
27382
  }
27288
27383
  await response.body?.cancel().catch(() => void 0);
27289
27384
  if (!response.ok && (!last || response.status >= 500)) last = { response, url: url4, store: store2 };
27290
- }).catch(() => void 0).finally(() => {
27385
+ }).catch(() => {
27386
+ if (!done) last = { response: unanswered(), url: url4, store: store2 };
27387
+ }).finally(() => {
27291
27388
  if (--pending === 0) settle2(last);
27292
27389
  });
27293
27390
  });
@@ -27339,7 +27436,7 @@ async function resolveDocUrl(cid, init = {}) {
27339
27436
  rememberDocStore(cid, found.store);
27340
27437
  return found.url;
27341
27438
  }
27342
- var WHERE_MAX, where, forgetDocStores, ANCHOR_MAX_BYTES, missing;
27439
+ var WHERE_MAX, where, forgetDocStores, ANCHOR_MAX_BYTES, missing, unanswered;
27343
27440
  var init_docs = __esm({
27344
27441
  "../../packages/openhands-sdk/src/data/docs.ts"() {
27345
27442
  "use strict";
@@ -27352,6 +27449,7 @@ var init_docs = __esm({
27352
27449
  forgetDocStores = () => void where.clear();
27353
27450
  ANCHOR_MAX_BYTES = 2e6;
27354
27451
  missing = () => new Response(null, { status: 404, statusText: "not found in any store" });
27452
+ unanswered = () => new Response(null, { status: 504, statusText: "a store did not answer" });
27355
27453
  }
27356
27454
  });
27357
27455
 
@@ -29626,7 +29724,13 @@ var init_checkersRead = __esm({
29626
29724
  // ../../packages/openhands-sdk/src/data/checkers.ts
29627
29725
  var checkers_exports = {};
29628
29726
  __export(checkers_exports, {
29727
+ ASKY_CHECKER_ADDRESSES: () => ASKY_CHECKER_ADDRESSES,
29728
+ ASKY_CHECKER_FEES: () => ASKY_CHECKER_FEES,
29729
+ ASKY_CHECKER_NAME: () => ASKY_CHECKER_NAME,
29730
+ ASKY_CHECKER_URLS: () => ASKY_CHECKER_URLS,
29629
29731
  ASK_THE_WORLD_CHECKERS: () => ASK_THE_WORLD_CHECKERS,
29732
+ askyChecker: () => askyChecker,
29733
+ askyCheckerUrl: () => askyCheckerUrl,
29630
29734
  checkEnvelope: () => checkEnvelope,
29631
29735
  checkOfProtocol: () => checkOfProtocol,
29632
29736
  checkerByAddress: () => checkerByAddress,
@@ -29823,11 +29927,17 @@ function checkEnvelope(input) {
29823
29927
  ...answers
29824
29928
  };
29825
29929
  }
29826
- var ASK_THE_WORLD_CHECKERS, isOpenhandsChecker, openhandsPath, plain, list4, str5, words2, choiceValue, NOT_ANSWERS, isEmpty2, filesOf, isFile, numberOf;
29930
+ var ASK_THE_WORLD_CHECKERS, ASKY_CHECKER_URLS, ASKY_CHECKER_ADDRESSES, ASKY_CHECKER_FEES, ASKY_CHECKER_NAME, askyCheckerUrl, askyChecker, isOpenhandsChecker, openhandsPath, plain, list4, str5, words2, choiceValue, NOT_ANSWERS, isEmpty2, filesOf, isFile, numberOf;
29827
29931
  var init_checkers = __esm({
29828
29932
  "../../packages/openhands-sdk/src/data/checkers.ts"() {
29829
29933
  "use strict";
29830
29934
  ASK_THE_WORLD_CHECKERS = /* @__PURE__ */ new Set(["ixo1qrk9dp4r2u8q5l9cs42yh4dne4pj3wynp4enwq", "ixo12mvzayzrzsvaud86jf9shy6q2nvkea3tygxhsh"]);
29935
+ ASKY_CHECKER_URLS = { devnet: "", testnet: "https://asky-checker.testnet.ixo.earth", mainnet: "https://asky-checker.ixo.earth" };
29936
+ ASKY_CHECKER_ADDRESSES = { devnet: "", testnet: "ixo149t76xlqg5u5dmpz7sjhlz7y0qktafv4z6qg9u", mainnet: "ixo1gmt3rxksd0ukzd228u67p3a34ew7sfl2shff9f" };
29937
+ ASKY_CHECKER_FEES = { testnet: { base: 1e6, denom: "upay" }, mainnet: { base: 1e6, denom: "upay" } };
29938
+ ASKY_CHECKER_NAME = "Asky Checker";
29939
+ askyCheckerUrl = (network2) => ASKY_CHECKER_URLS[network2] || null;
29940
+ askyChecker = (network2) => ASKY_CHECKER_URLS[network2] && ASKY_CHECKER_ADDRESSES[network2] && ASKY_CHECKER_FEES[network2] ? { url: ASKY_CHECKER_URLS[network2], address: ASKY_CHECKER_ADDRESSES[network2], fee: ASKY_CHECKER_FEES[network2] } : null;
29831
29941
  isOpenhandsChecker = (info, own2 = false) => own2 || typeof info?.openhands?.hire === "string";
29832
29942
  openhandsPath = (routes, event, collectionId) => (routes?.[event] ?? `/openhands/${event}/:collectionId`).replace(":collectionId", encodeURIComponent(collectionId));
29833
29943
  plain = (text4) => text4.trim().toLowerCase();
@@ -29865,7 +29975,9 @@ __export(format_exports, {
29865
29975
  learnDenomRoutes: () => learnDenomRoutes,
29866
29976
  paysFromPool: () => paysFromPool,
29867
29977
  readsAsMoney: () => readsAsMoney,
29978
+ receivedAmount: () => receivedAmount,
29868
29979
  resolveDenom: () => resolveDenom,
29980
+ sharedApproval: () => sharedApproval,
29869
29981
  shortAddress: () => shortAddress,
29870
29982
  shortStr: () => shortStr,
29871
29983
  timeAgo: () => timeAgo,
@@ -29911,6 +30023,14 @@ function approvalAmount(payments) {
29911
30023
  const display = coinDisplay(payments?.["approval"]?.amount);
29912
30024
  return display && display.value > 0 ? display : null;
29913
30025
  }
30026
+ function receivedAmount(payments, split3, amount4 = payments?.["approval"]?.amount) {
30027
+ const leg = coinDisplay(amount4);
30028
+ if (!leg || leg.value <= 0) return null;
30029
+ if (!sharedApproval(payments)) return leg;
30030
+ if (!split3) return { ...leg, less: true };
30031
+ const coin = amount4[0];
30032
+ return coinDisplay([{ denom: coin.denom, amount: String(netOf(Number(coin.amount), split3)) }]);
30033
+ }
29914
30034
  function evaluationAmount(payments) {
29915
30035
  const display = coinDisplay(payments?.["evaluation"]?.amount);
29916
30036
  return display && display.value > 0 ? display : null;
@@ -29943,10 +30063,11 @@ function timeShort(iso) {
29943
30063
  const date6 = chainTime(iso);
29944
30064
  return date6 ? date6.toLocaleTimeString("en-GB", { hour: "2-digit", minute: "2-digit" }) : "";
29945
30065
  }
29946
- var shortAddress, PEGGED, isPegged2, ODD, ROUTES, resolveDenom, fractionFor, readsAsMoney, POOL_LEG_BASE, paysFromPool, NAME_MAX3, ESTATE_DEFAULTS, RECORD_SUFFIX, cleanName;
30066
+ var shortAddress, PEGGED, isPegged2, ODD, ROUTES, resolveDenom, fractionFor, readsAsMoney, POOL_LEG_BASE, paysFromPool, sharedApproval, NAME_MAX3, ESTATE_DEFAULTS, RECORD_SUFFIX, cleanName;
29947
30067
  var init_format = __esm({
29948
30068
  "../../packages/openhands-sdk/src/data/format.ts"() {
29949
30069
  "use strict";
30070
+ init_share();
29950
30071
  shortAddress = (address2) => shortStr(address2, 12, 6);
29951
30072
  PEGGED = /* @__PURE__ */ new Set(["PAY", "USDC", "USDT", "USD"]);
29952
30073
  isPegged2 = (symbol2) => PEGGED.has(symbol2);
@@ -29961,6 +30082,7 @@ var init_format = __esm({
29961
30082
  readsAsMoney = (amount4) => Boolean(amount4 && amount4.value >= 0.01);
29962
30083
  POOL_LEG_BASE = "1";
29963
30084
  paysFromPool = (payments) => payments?.["approval"]?.amount?.[0]?.amount === POOL_LEG_BASE;
30085
+ sharedApproval = (payments) => payments?.["approval"]?.is_oracle_payment === true;
29964
30086
  NAME_MAX3 = 40;
29965
30087
  ESTATE_DEFAULTS = /* @__PURE__ */ new Set([
29966
30088
  "wallet send ui user",
@@ -30020,7 +30142,7 @@ var init_policy = __esm({
30020
30142
  // ../../app/lib/screenWords.ts
30021
30143
  function phraseVerdict(input) {
30022
30144
  const phrase = checkAsk(input);
30023
- return phrase.ok ? null : { verdict: "refuse", reason: `Openhands Network doesn't carry ${phrase.reason}.` };
30145
+ return phrase.ok ? null : { verdict: "refuse", reason: `asks.world doesn't carry ${phrase.reason}.` };
30024
30146
  }
30025
30147
  var screenInput;
30026
30148
  var init_screenWords = __esm({
@@ -258263,12 +258385,68 @@ var init_session = __esm({
258263
258385
  });
258264
258386
 
258265
258387
  // ../../packages/openhands-sdk/src/chain/query.ts
258388
+ var query_exports43 = {};
258389
+ __export(query_exports43, {
258390
+ FeeAllowanceShort: () => FeeAllowanceShort,
258391
+ affords: () => affords,
258392
+ canPay: () => canPay,
258393
+ ensureGas: () => ensureGas,
258394
+ expired: () => expired,
258395
+ feePayerFor: () => feePayerFor,
258396
+ feeSplit: () => feeSplit,
258397
+ feeSplitOrNull: () => feeSplitOrNull,
258398
+ getFeegrantGranter: () => getFeegrantGranter,
258399
+ getQueryClient: () => getQueryClient,
258400
+ getUixoBalance: () => getUixoBalance,
258401
+ legacyPercent: () => legacyPercent,
258402
+ pickFeePayer: () => pickFeePayer,
258403
+ pickGranter: () => pickGranter,
258404
+ txEvents: () => txEvents,
258405
+ valueFromTx: () => valueFromTx,
258406
+ valuesFromEvents: () => valuesFromEvents,
258407
+ valuesFromTx: () => valuesFromTx
258408
+ });
258266
258409
  function getQueryClient() {
258267
258410
  const url4 = rpcUrl();
258268
258411
  let client2 = clients.get(url4);
258269
- if (!client2) clients.set(url4, client2 = createQueryClient(url4));
258412
+ if (!client2) {
258413
+ const value = createQueryClient(url4);
258414
+ clients.set(url4, client2 = value);
258415
+ value.catch(() => {
258416
+ if (clients.get(url4) === value) clients.delete(url4);
258417
+ });
258418
+ }
258270
258419
  return client2;
258271
258420
  }
258421
+ function legacyPercent(raw) {
258422
+ const value = (raw ?? "").trim();
258423
+ if (!value) return 0;
258424
+ if (/^\d+$/.test(value)) return Number(BigInt(value) * 1000000n / 10n ** 18n) / 1e6;
258425
+ const read9 = Number(value);
258426
+ if (!Number.isFinite(read9)) throw new Error(`not a percentage: ${JSON.stringify(raw)}`);
258427
+ return read9;
258428
+ }
258429
+ async function feeSplit() {
258430
+ const given2 = givenConfig().feeSplit;
258431
+ const chain2 = chainSplit();
258432
+ return given2 ? chain2.catch(() => given2) : chain2;
258433
+ }
258434
+ function chainSplit() {
258435
+ const rpc = rpcUrl();
258436
+ const kept = splitReads.get(rpc);
258437
+ if (kept && Date.now() - kept.at < SPLIT_TTL_MS) return kept.value;
258438
+ const value = (async () => {
258439
+ const client2 = await getQueryClient();
258440
+ const { params } = await client2.ixo.claims.v1beta1.params({});
258441
+ if (!params) throw new Error("the chain answered no claims params");
258442
+ return { network: legacyPercent(params.networkFeePercentage), node: legacyPercent(params.nodeFeePercentage), ixoAccount: params.ixoAccount };
258443
+ })();
258444
+ splitReads.set(rpc, { at: Date.now(), value });
258445
+ value.catch(() => {
258446
+ if (splitReads.get(rpc)?.value === value) splitReads.delete(rpc);
258447
+ });
258448
+ return value;
258449
+ }
258272
258450
  async function getUixoBalance(address2) {
258273
258451
  const client2 = await getQueryClient();
258274
258452
  const { balance } = await client2.cosmos.bank.v1beta1.balance({ address: address2, denom: "uixo" });
@@ -258369,6 +258547,19 @@ async function txEvents(transactionHash) {
258369
258547
  if (json2.error) throw new Error(`The chain node could not find transaction ${transactionHash.slice(0, 12)}\u2026: ${json2.error.data ?? json2.error.message ?? "unknown error"}`);
258370
258548
  return json2.result?.tx_result?.events ?? [];
258371
258549
  }
258550
+ async function valueFromTx(transactionHash, type, key) {
258551
+ for (const event of await txEvents(transactionHash)) {
258552
+ if (event.type !== type) continue;
258553
+ for (const attribute of event.attributes) {
258554
+ const name8 = /^[A-Za-z0-9+/=]+$/.test(attribute.key) && attribute.key !== key ? Buffer.from(attribute.key, "base64").toString("utf8") : attribute.key;
258555
+ if (name8 !== key) continue;
258556
+ const raw = attribute.value ?? "";
258557
+ const decoded = /^[A-Za-z0-9+/=]+$/.test(raw) ? Buffer.from(raw, "base64").toString("utf8") : raw;
258558
+ return (decoded || raw).replace(/^"|"$/g, "");
258559
+ }
258560
+ }
258561
+ return "";
258562
+ }
258372
258563
  function valuesFromEvents(events, type, key) {
258373
258564
  const out = [];
258374
258565
  for (const event of events) {
@@ -258386,13 +258577,23 @@ function valuesFromEvents(events, type, key) {
258386
258577
  async function valuesFromTx(transactionHash, type, key) {
258387
258578
  return valuesFromEvents(await txEvents(transactionHash), type, key);
258388
258579
  }
258389
- var clients, FeeAllowanceShort;
258580
+ var clients, SPLIT_TTL_MS, splitReads, feeSplitOrNull, FeeAllowanceShort;
258390
258581
  var init_query43 = __esm({
258391
258582
  "../../packages/openhands-sdk/src/chain/query.ts"() {
258392
258583
  "use strict";
258393
258584
  init_module23();
258394
258585
  init_env();
258586
+ init_given();
258395
258587
  clients = /* @__PURE__ */ new Map();
258588
+ SPLIT_TTL_MS = 10 * 6e4;
258589
+ splitReads = /* @__PURE__ */ new Map();
258590
+ feeSplitOrNull = (timeoutMs = 8e3) => Promise.race([
258591
+ feeSplit().catch(() => null),
258592
+ new Promise((resolve2) => {
258593
+ const timer = setTimeout(() => resolve2(givenConfig().feeSplit ?? null), timeoutMs);
258594
+ timer.unref?.();
258595
+ })
258596
+ ]);
258396
258597
  FeeAllowanceShort = class extends Error {
258397
258598
  };
258398
258599
  }
@@ -258952,6 +259153,7 @@ function delegationsAmong(rows2, now3 = Date.now()) {
258952
259153
  agent,
258953
259154
  expiresAt: expiries.length ? expiries.sort()[0] : null,
258954
259155
  cap: limit && Number(limit.amount) > 0 ? { denom: limit.denom, remainingBase: limit.amount } : null,
259156
+ caps: (send?.authorization?.spend_limit ?? []).filter((coin) => Number(coin.amount) > 0).map((coin) => ({ denom: coin.denom, remainingBase: coin.amount })),
258955
259157
  iidEdits: IID_EDIT_MSGS.every((msg) => typed.has(msg)),
258956
259158
  iidDeletes: IID_DELETE_MSGS.every((msg) => typed.has(msg))
258957
259159
  });
@@ -259121,7 +259323,9 @@ __export(connect_exports, {
259121
259323
  gasRenewWords: () => gasRenewWords,
259122
259324
  gasUsedUp: () => gasUsedUp,
259123
259325
  keyPins: () => keyPins,
259124
- markSigned: () => markSigned
259326
+ markSigned: () => markSigned,
259327
+ ownAnswer: () => ownAnswer,
259328
+ receiptWithOurs: () => receiptWithOurs
259125
259329
  });
259126
259330
  function noteIgnored(agent, pinned, others, log) {
259127
259331
  const fresh2 = others.filter((one) => !told.has(`${agent}:${pinned}:${one}`));
@@ -259202,6 +259406,17 @@ function actingWords(acting, face) {
259202
259406
  const cap = acting.cap ? `, ${face(Number(acting.cap.remainingBase) / 1e6, symbolOf(acting.cap.denom))} remaining in its spending allowance` : ", no spending allowance remains";
259203
259407
  return `Connected to account ${acting.principal}${until}${cap}. This key has permission to post asks and send answers linked to that account. Rewards go to that account.`;
259204
259408
  }
259409
+ function ownAnswer(claims, key, filedFor) {
259410
+ return claims.find((claim3) => claim3.agentAddress === key) ?? (filedFor ? claims.find((claim3) => claim3.agentAddress === filedFor) : void 0);
259411
+ }
259412
+ async function receiptWithOurs(read9, key, filedFor, fromIndex) {
259413
+ const first = await read9();
259414
+ const found = first ? ownAnswer(first.claims ?? [], key, filedFor) : void 0;
259415
+ if (!first || found) return { receipt: first, ours: found };
259416
+ const indexed = await fromIndex().catch(() => null);
259417
+ const ours = indexed ? ownAnswer(indexed, key, filedFor) : void 0;
259418
+ return { receipt: ours ? { ...first, claims: [ours, ...first.claims ?? []] } : first, ours };
259419
+ }
259205
259420
  function gasUsedUp(found, principalUixo, now3 = Date.now()) {
259206
259421
  if (!found) return principalUixo >= GAS_LEND_MIN_UIXO;
259207
259422
  const basic = found["@type"]?.endsWith("AllowedMsgAllowance") ? found.allowance ?? {} : found;
@@ -259244,7 +259459,8 @@ var init_connect = __esm({
259244
259459
  /* `did:ixo:<address>` is how this chain names an account's DID (`didOf`), and it is what the site served. */
259245
259460
  did: `did:ixo:${found.principal}`,
259246
259461
  expiresAt: found.expiresAt,
259247
- cap: found.cap
259462
+ cap: found.cap,
259463
+ caps: found.caps
259248
259464
  });
259249
259465
  list5 = (principals) => principals.join(", ");
259250
259466
  SELF = "self";
@@ -259406,6 +259622,7 @@ var init_accountProof = __esm({
259406
259622
  var hq_exports = {};
259407
259623
  __export(hq_exports, {
259408
259624
  capCoinRefusal: () => capCoinRefusal,
259625
+ capIn: () => capIn,
259409
259626
  evaluationBase: () => evaluationBase,
259410
259627
  evaluationPrice: () => evaluationPrice,
259411
259628
  hqPlan: () => hqPlan,
@@ -259438,9 +259655,10 @@ function pickCoin(currency, network2, fallback) {
259438
259655
  const named = currency === "uixo" ? "IXO" : currency === USDC_MAINNET ? "USDC" : currency;
259439
259656
  return named?.trim() ? rewardCoin(named, network2) : fallback;
259440
259657
  }
259441
- function capCoinRefusal(acting, coin) {
259442
- if (!acting?.cap || acting.cap.denom === coin) return null;
259658
+ function capCoinRefusal(acting, coin, grantAt) {
259659
+ if (!acting?.cap || capIn(acting, coin)) return null;
259443
259660
  const has = symbolOf(acting.cap.denom);
259661
+ if (grantAt) return `${acting.principal}'s allowance for this key is in ${has}, not ${symbolOf(coin)}, so it cannot pay this. They grant a ${symbolOf(coin)} allowance at ${grantAt}`;
259444
259662
  return `${acting.principal}'s allowance for this key is in ${has}, not ${symbolOf(coin)}, so it cannot pay this. Pay in ${has} (currency: "${has}") instead`;
259445
259663
  }
259446
259664
  function hqPlan(args, network2) {
@@ -259493,7 +259711,7 @@ async function oracleWords(oracle, io) {
259493
259711
  if (!id) return null;
259494
259712
  return named?.name ? `${named.name} (${id})` : id;
259495
259713
  }
259496
- var refuse3, ENTITY2, orgDefaultOracle, offChainPayWords, orgConfirmToken;
259714
+ var refuse3, ENTITY2, orgDefaultOracle, capIn, offChainPayWords, orgConfirmToken;
259497
259715
  var init_hq = __esm({
259498
259716
  "src/hq.ts"() {
259499
259717
  "use strict";
@@ -259504,6 +259722,7 @@ var init_hq = __esm({
259504
259722
  refuse3 = (refused2) => ({ refused: refused2 });
259505
259723
  ENTITY2 = /^did:ixo:entity:[0-9a-f]{32}$/;
259506
259724
  orgDefaultOracle = (allowed, askTheWorld, ordinary) => (ordinary && askTheWorld && allowed.includes(askTheWorld) ? askTheWorld : allowed[0]) ?? null;
259725
+ capIn = (acting, coin) => acting?.caps?.find((one) => one.denom === coin) ?? (acting?.cap?.denom === coin ? acting.cap : null);
259507
259726
  offChainPayWords = (payout, orgName) => `${payout.amount} ${payout.currency}`.concat(` per approved answer, off chain via ${orgName ? `${orgName}'s` : "the company's"} payouts (${payout.rails[0] === "stripe" ? "Stripe" : "USDC on Base"})`);
259508
259727
  orgConfirmToken = (args) => [args.offChainTotal ?? args.rewards, args.feesSetAside].filter(Boolean).join(" and ");
259509
259728
  }
@@ -259531,10 +259750,6 @@ __export(lane_exports, {
259531
259750
  signShare: () => signShare,
259532
259751
  uploadToLane: () => uploadToLane
259533
259752
  });
259534
- function isPrivateTerms(terms) {
259535
- const doc = terms;
259536
- return doc?.access === "invite" || doc?.access === "apply" || doc?.call?.kind === "review";
259537
- }
259538
259753
  async function agentDidKey(mnemonic) {
259539
259754
  const { signerFromMnemonic: signerFromMnemonic2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
259540
259755
  const { signer: signer2 } = await signerFromMnemonic2(mnemonic);
@@ -259652,6 +259867,7 @@ var init_lane = __esm({
259652
259867
  "../../packages/openhands-sdk/src/chain/lane.ts"() {
259653
259868
  "use strict";
259654
259869
  init_docUrls();
259870
+ init_share();
259655
259871
  networkVfsDid = () => `did:web:${new URL(laneVfsUrl()).host.replace(/:/g, "%3A")}`;
259656
259872
  WRITE_PASS_SECONDS = 3600;
259657
259873
  READ_PASS_SECONDS = 86400;
@@ -260869,6 +261085,7 @@ function prepareAsk(poster, entityDid, adminAccount, terms, now3 = Date.now(), s
260869
261085
  if (orgFunds && !orgAdmin) throw new Error("an organisation's ask is funded by the organisation: its admin account is needed");
260870
261086
  const posterCoins = orgFunds ? [] : coins;
260871
261087
  const recorded = offChain ? feeDenom : denom;
261088
+ const shared = sharedCall(terms.rules) && !offChain;
260872
261089
  const collection = {
260873
261090
  typeUrl: "/ixo.claims.v1beta1.MsgCreateCollection",
260874
261091
  value: ixo.claims.v1beta1.MsgCreateCollection.fromPartial({
@@ -260883,8 +261100,8 @@ function prepareAsk(poster, entityDid, adminAccount, terms, now3 = Date.now(), s
260883
261100
  payments: {
260884
261101
  submission: paymentLeg(adminAccount),
260885
261102
  evaluation: paymentLeg(adminAccount, checkReward > 0n ? [{ denom: feeDenom, amount: String(checkReward) }] : void 0),
260886
- /* Off chain: a leg with no coin. A "0" coin fails (code 18). */
260887
- approval: paymentLeg(adminAccount, offChain ? void 0 : [{ denom, amount: terms.amountUixo }]),
261103
+ /* Off chain: a leg with no coin. A "0" coin fails (code 18). The chain allows the flag on this leg alone. */
261104
+ approval: paymentLeg(adminAccount, offChain ? void 0 : [{ denom, amount: terms.amountUixo }], shared),
260888
261105
  rejection: paymentLeg(adminAccount)
260889
261106
  }
260890
261107
  })
@@ -261107,10 +261324,11 @@ var init_ask = __esm({
261107
261324
  init_env();
261108
261325
  init_entityShape();
261109
261326
  init_docUrls();
261327
+ init_share();
261110
261328
  init_given();
261111
261329
  init_envRead();
261112
261330
  claimFormRef = (url4, proof) => url4?.trim() && proof?.trim() ? { url: url4.trim(), proof: proof.trim() } : null;
261113
- paymentLeg = (account, amount4) => ixo.claims.v1beta1.Payment.fromPartial({ account, amount: amount4 ?? [] });
261331
+ paymentLeg = (account, amount4, oraclePayment = false) => ixo.claims.v1beta1.Payment.fromPartial({ account, amount: amount4 ?? [], isOraclePayment: oraclePayment });
261114
261332
  ESCROW_MEMO = "Open the ask and escrow the money";
261115
261333
  AskPending = class extends Error {
261116
261334
  marker;
@@ -261608,7 +261826,9 @@ function metaGrantMsg2(actor, entityDid, admin, grantee, endDate, authType, maxQ
261608
261826
  }
261609
261827
  function assertAskShape(ask) {
261610
261828
  const campaignAsk = typeof ask.terms?.campaign === "string";
261611
- if (!ask.payoutTerms && !campaignAsk && (ask.denom ?? askDenom()) === "upay") throw new Error(PAY_NOT_A_REWARD);
261829
+ const asky = askyChecker(network());
261830
+ const askyReview = ask.terms?.call?.kind === "review" && asky !== null && asky.fee.denom === "upay" && ask.checker === asky.address;
261831
+ if (!ask.payoutTerms && !campaignAsk && !askyReview && (ask.denom ?? askDenom()) === "upay") throw new Error(PAY_NOT_A_REWARD);
261612
261832
  if (ask.payoutTerms) {
261613
261833
  if (!ask.org) throw new Error("payout terms need the organisation (org) whose facilitator pays them");
261614
261834
  if (ask.amountUixo !== 0) throw new Error("an ask paid off chain carries nothing on chain: amountUixo must be 0, or the worker is paid twice");
@@ -261782,7 +262002,7 @@ function openAskMessages(actorAddress, owner, ask, at) {
261782
262002
  const endsAt = at.now + days * 24 * 3600 * 1e3;
261783
262003
  const endDate = utils3.proto.toTimestamp(new Date(endsAt));
261784
262004
  const judgeUntil = utils3.proto.toTimestamp(new Date(judgeUntilMs(endsAt)));
261785
- const leg = (amount4) => ixo.claims.v1beta1.Payment.fromPartial({ account: admin, amount: amount4 ?? [] });
262005
+ const leg = (amount4, oraclePayment = false) => ixo.claims.v1beta1.Payment.fromPartial({ account: admin, amount: amount4 ?? [], isOraclePayment: oraclePayment });
261786
262006
  const checkReward = ask.checker && ask.checkRewardUixo ? Math.max(0, Math.floor(ask.checkRewardUixo)) : 0;
261787
262007
  const approvalLeg = ask.poolCeilingUixo ? 1 : ask.amountUixo;
261788
262008
  const offChain = Boolean(ask.payoutTerms);
@@ -261796,6 +262016,7 @@ function openAskMessages(actorAddress, owner, ask, at) {
261796
262016
  if (orgFunds && !at.orgAdmin) throw new Error("an organisation's ask is funded by the organisation: its admin account is needed");
261797
262017
  const recorded = offChain ? feeDenom : denom;
261798
262018
  const escrow = coins.find((coin) => coin.denom === recorded)?.amount ?? "0";
262019
+ const shared = sharedCall(ask.terms) && !offChain && !ask.poolCeilingUixo;
261799
262020
  const fee = feeEscrow > 0n && feeDenom !== recorded ? { denom: feeDenom, amount: String(feeEscrow), from: orgFunds ? "org" : "poster" } : null;
261800
262021
  const messages = [
261801
262022
  {
@@ -261812,7 +262033,7 @@ function openAskMessages(actorAddress, owner, ask, at) {
261812
262033
  submission: leg(),
261813
262034
  evaluation: leg(checkReward ? [{ denom: feeDenom, amount: String(checkReward) }] : void 0),
261814
262035
  /* Off chain: a leg with no coin (NO_PAYMENT). A "0" coin fails (code 18), and so would a MsgSend of 0 (code 10). */
261815
- approval: leg(offChain ? void 0 : [{ denom, amount: String(approvalLeg) }]),
262036
+ approval: leg(offChain ? void 0 : [{ denom, amount: String(approvalLeg) }], shared),
261816
262037
  rejection: leg()
261817
262038
  }
261818
262039
  })
@@ -262300,6 +262521,7 @@ var init_chain = __esm({
262300
262521
  init_delegate();
262301
262522
  init_domainIndexer();
262302
262523
  init_publish();
262524
+ init_share();
262303
262525
  init_proof3();
262304
262526
  init_sequenceStore();
262305
262527
  init_config3();
@@ -262310,6 +262532,7 @@ var init_chain = __esm({
262310
262532
  init_entityShape();
262311
262533
  init_docUrls();
262312
262534
  init_askImage();
262535
+ init_checkers();
262313
262536
  init_store2();
262314
262537
  init_pendingTx();
262315
262538
  GAS_PRICE2 = import_stargate7.GasPrice.fromString("0.025uixo");
@@ -262961,7 +263184,7 @@ function callEntriesTerms(input) {
262961
263184
  judge: input.judge,
262962
263185
  ...input.approver ? { approver: input.approver } : {}
262963
263186
  };
262964
- return input.report ? withReport(terms, input.report) : terms;
263187
+ return input.report ? withReport(terms, input.report, input.approver ? { v: 2 } : void 0) : terms;
262965
263188
  }
262966
263189
  function callPickTerms(input) {
262967
263190
  const call = legCall(input.call, input.leg);
@@ -262999,13 +263222,13 @@ var init_call = __esm({
262999
263222
  init_callTag();
263000
263223
  ENTRY_MIN_CHARS = 200;
263001
263224
  REVIEW_LINES = [
263002
- { text: "Every scenario in the brief has a step with what was done, what was expected, what was observed, and a result", fail: "a scenario in the brief has no step" },
263003
- { text: "Every finding names the step it came from, what was observed against what was expected, and its impact on the user", fail: "a finding has no step, no observation or no impact" },
263004
- { text: "Every blocker says how to reproduce it, or what is missing to reproduce it", fail: "a blocker says neither" },
263005
- { text: "Every severity says why it is that severe, in terms of what the user could and could not do", fail: "a severity gives no reason" },
263006
- { text: "The summary names the version tested, the devices or environment, and the dates", fail: "the version tested is missing" }
263225
+ { text: "The report covers the scenarios this ask lists, each with what was done and whether it passed, failed, was blocked or was not tested", fail: "a scenario the ask lists is missing from the report altogether" },
263226
+ { text: "Each finding points at its evidence: a screenshot, recording or trace", fail: "a finding has no evidence at all" },
263227
+ { text: "Each problem found says how to reproduce it, or for a blocker what is missing to reproduce it", fail: "a problem gives no way to reproduce it" },
263228
+ { text: "Each problem found has a severity: blocker, major or minor", fail: "a problem has no severity" },
263229
+ { text: "Each recommendation says what to change", fail: "a recommendation does not say what should change" }
263007
263230
  ];
263008
- REVIEW_DELIVERABLE_PROMPT = "A review report: the steps you took per scenario, your findings with severity and how to reproduce them, your recommendations, with screenshots";
263231
+ REVIEW_DELIVERABLE_PROMPT = "A review report: what you tested, your steps per scenario, each finding with its evidence, how to reproduce it and its severity, what to change, and the limitations";
263009
263232
  DAY_MS2 = 24 * 3600 * 1e3;
263010
263233
  legsOf = (call) => call.mode === "hire" ? ["entries"] : ["prize", ...call.money.runnersN > 0 ? ["runnersUp"] : [], "entries"];
263011
263234
  pickQuota = (winners) => winners + 2;
@@ -263013,6 +263236,15 @@ var init_call = __esm({
263013
263236
  });
263014
263237
 
263015
263238
  // src/callRead.ts
263239
+ var callRead_exports = {};
263240
+ __export(callRead_exports, {
263241
+ blockText: () => blockText,
263242
+ callReadout: () => callReadout,
263243
+ isPickLeg: () => isPickLeg,
263244
+ shareWords: () => shareWords,
263245
+ sharedPay: () => sharedPay,
263246
+ stageWords: () => stageWords
263247
+ });
263016
263248
  function stageWords(call, now3 = Date.now()) {
263017
263249
  if (now3 < Date.parse(call.closes)) return `open until ${day(call.closes)}`;
263018
263250
  if (call.mode === "hire") return `closed ${day(call.closes)}; every entry that passed is paid as it is judged`;
@@ -263020,6 +263252,11 @@ function stageWords(call, now3 = Date.now()) {
263020
263252
  if (now3 < Date.parse(call.pick)) return `closed; the shortlist is out; the pick by ${day(call.pick)}`;
263021
263253
  return `closed; the pick was due ${day(call.pick)}`;
263022
263254
  }
263255
+ function sharedPay(quote, share) {
263256
+ const leg = grossFor(Math.round(quote * 1e6), share);
263257
+ const receives = netOf(leg, share);
263258
+ return { receives: receives / 1e6, leg: leg / 1e6, keeps: (leg - receives) / 1e6 };
263259
+ }
263023
263260
  function blockText(block2) {
263024
263261
  switch (block2.kind) {
263025
263262
  case "story":
@@ -263058,14 +263295,16 @@ function callReadout(terms, ctx) {
263058
263295
  ].filter(Boolean);
263059
263296
  const who = terms.access === "invite" ? "By invitation: only the people the poster put on the list can enter. If you were sent a link, pass it as submit_work's `invite`; if your address or page was named, nothing more is needed." : terms.access === "apply" ? "By application: apply_to_call first, with a short note; when the poster says yes, submit_work is admitted." : "Open to anyone.";
263060
263297
  const review = call.kind === "review";
263298
+ const shared = call.share ? sharedPay(m.entryEach, call.share) : null;
263299
+ const shareLine2 = shared && call.share ? `each ${review ? "specialist" : hire ? "hired person" : "entry paid"} receives ${amount3(shared.receives, ctx.symbol)} (the leg is ${amount3(shared.leg, ctx.symbol)}; the network keeps ${amount3(shared.keeps, ctx.symbol)}: ${shareWords(call.share, "as posted")})${hire ? "" : "; the pick's and the runners-up's legs carry the same share on top of theirs"}` : "";
263061
263300
  const out = [
263062
263301
  `${review ? "Expert review" : hire ? "Hiring call" : "Open call"} "${call.slug}" \xB7 posted by ${call.poster} \xB7 ${stageWords(call, ctx.now)}`,
263063
- ...review && ctx.site ? [`Its page: ${expertOrigin(ctx.site)}/expert/reviews/${call.slug}`] : [],
263302
+ ...review && ctx.site ? [`Its page: ${expertOrigin(ctx.site)}/expert/reviews/${ctx.id}`] : [],
263064
263303
  call.title,
263065
263304
  call.lede,
263066
263305
  ...review && call.subject ? [`Reviewing: ${call.subject.name}${call.subject.version ? ` ${call.subject.version}` : ""}${call.subject.where ? ` \xB7 ${call.subject.where}` : ""}. Access details, if any, are in your invitation.`] : [],
263067
263306
  ...review && call.scenarios?.length ? ["Scenarios, each one a step in your report:", ...call.scenarios.map((one, index) => ` S${index + 1}. ${one}`)] : [],
263068
- `Money: ${money.join("; ")}. Locked on chain on ${hire ? "the ask's own account" : "each leg's own account"} before anyone enters.`,
263307
+ `Money: ${[...money, shareLine2].filter(Boolean).join("; ")}. Locked on chain on ${hire ? "the ask's own account" : "each leg's own account"} before anyone enters.`,
263069
263308
  who,
263070
263309
  call.leg === "entries" ? review ? `Enter: a review report with screenshots, as submit_work's report and files, collectionId "${ctx.id}"${terms.approver ? `; the AI checks it against the requirements, then ${terms.approver} gives final approval to release payment` : "; the AI checks it against the requirements and a passing report is paid"}. One report per account.` : `Enter: one page of text, in your own words: submit_work collectionId "${ctx.id}". One entry per account.` : `The pick's leg: only the entry the poster picked may claim here, with one line naming it. ${entriesId ? `The entries are ask ${entriesId}.` : "The entries are the call's own ask."}`,
263071
263310
  hire ? `Judged: ${n} requirements that every entry must meet. Pass all ${n} and the entry is paid on approval; nobody is picked.` : `Judged: ${n} requirements that every entry must meet. Pass all ${n} and the entry is paid; how clearly it meets each requirement ranks the entries for the pick.`,
@@ -263076,13 +263315,16 @@ function callReadout(terms, ctx) {
263076
263315
  ];
263077
263316
  return out.join("\n");
263078
263317
  }
263079
- var day, amount3, countWords, isPickLeg;
263318
+ var day, amount3, percent, shareWords, countWords, isPickLeg;
263080
263319
  var init_callRead = __esm({
263081
263320
  "src/callRead.ts"() {
263082
263321
  "use strict";
263322
+ init_share();
263083
263323
  init_networks();
263084
263324
  day = (iso) => new Date(iso).toUTCString().replace(/:\d\d GMT$/, " UTC");
263085
263325
  amount3 = (value, symbol2) => `${Number(value.toFixed(6))} ${symbol2}`;
263326
+ percent = (value) => `${Number(value.toFixed(6))} %`;
263327
+ shareWords = (share, when2) => `${percent(share.network + share.node)} ${when2}: ${percent(share.network)} to the network, ${percent(share.node)} to the relayer node`;
263086
263328
  countWords = (count2) => ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"][count2] ?? String(count2);
263087
263329
  isPickLeg = (terms) => {
263088
263330
  const leg = terms?.call?.leg;
@@ -263099,21 +263341,27 @@ __export(call_exports2, {
263099
263341
  InFlightError: () => InFlightError,
263100
263342
  PostRefused: () => PostRefused,
263101
263343
  SETTLE_MS: () => SETTLE_MS,
263344
+ SHARE_UNREAD: () => SHARE_UNREAD,
263102
263345
  TESTNET_CALL_DEFAULTS: () => TESTNET_CALL_DEFAULTS,
263103
263346
  accessOf: () => accessOf,
263104
263347
  askInvitesFile: () => askInvitesFile,
263105
263348
  blockText: () => blockText,
263106
263349
  blocksOf: () => blocksOf,
263350
+ callChecker: () => callChecker,
263107
263351
  callCoin: () => callCoin,
263108
263352
  callOfDraft: () => callOfDraft,
263109
263353
  callPlan: () => callPlan,
263110
263354
  callReadout: () => callReadout,
263111
263355
  callServerEnv: () => callServerEnv,
263356
+ callShare: () => callShare,
263357
+ callShares: () => callShares,
263112
263358
  callTwoCoins: () => callTwoCoins,
263113
263359
  callsDir: () => callsDir,
263360
+ capWords: () => capWords,
263114
263361
  chainCheck: () => chainCheck,
263115
263362
  confirms: () => confirms,
263116
263363
  doorOf: () => doorOf,
263364
+ draftOfEntries: () => draftOfEntries,
263117
263365
  freshDraft: () => freshDraft,
263118
263366
  indexLookup: () => indexLookup,
263119
263367
  indexLookupWith: () => indexLookupWith,
@@ -263131,7 +263379,9 @@ __export(call_exports2, {
263131
263379
  missingOf: () => missingOf,
263132
263380
  nextLeg: () => nextLeg,
263133
263381
  nextSlug: () => nextSlug,
263382
+ nudgeAtFor: () => nudgeAtFor,
263134
263383
  optionalOf: () => optionalOf,
263384
+ overCap: () => overCap,
263135
263385
  payWords: () => payWords,
263136
263386
  postNextLeg: () => postNextLeg,
263137
263387
  preCheck: () => preCheck,
@@ -263166,6 +263416,15 @@ function writeDraft(draft) {
263166
263416
  writeFileSync3(`${path}.tmp`, JSON.stringify(draft, null, 2), { mode: 384 });
263167
263417
  renameSync3(`${path}.tmp`, path);
263168
263418
  }
263419
+ function draftOfEntries(collectionId) {
263420
+ if (!existsSync2(callsDir())) return null;
263421
+ for (const name8 of readdirSync(callsDir())) {
263422
+ const slug = /^(.+)\.json$/.exec(name8)?.[1];
263423
+ const draft = slug ? readDraft(slug) : null;
263424
+ if (draft?.posted.entries?.id === collectionId) return draft;
263425
+ }
263426
+ return null;
263427
+ }
263169
263428
  function readAskInvites(askId) {
263170
263429
  try {
263171
263430
  const kept = JSON.parse(readFileSync3(askInvitesFile(askId), "utf8"));
@@ -263182,8 +263441,7 @@ function keepAskInvites(askId, written) {
263182
263441
  renameSync3(`${path}.tmp`, path);
263183
263442
  }
263184
263443
  function nextSlug() {
263185
- const taken = existsSync2(callsDir()) ? readdirSync(callsDir()).map((name8) => /^call-(\d+)\.json$/.exec(name8)?.[1]).filter((one) => Boolean(one)).map(Number) : [];
263186
- return `call-${taken.length ? Math.max(...taken) + 1 : 1}`;
263444
+ return `call-${crypto.randomUUID().replaceAll("-", "")}`;
263187
263445
  }
263188
263446
  function doorOf(access2, invite) {
263189
263447
  const items = (invite ?? []).map((one) => one.trim()).filter(Boolean);
@@ -263377,6 +263635,10 @@ function mergeDraft(draft, input, now3 = /* @__PURE__ */ new Date()) {
263377
263635
  }
263378
263636
  return { draft: { ...draft, fields, updatedAt: now3.toISOString() }, refused: refused2 };
263379
263637
  }
263638
+ function callChecker(network2, review, deployment2) {
263639
+ const asky = review ? askyChecker(network2) : null;
263640
+ return asky ? { address: asky.address, at: { url: asky.url }, name: ASKY_CHECKER_NAME, fee: asky.fee } : { address: deployment2, at: null, name: null, fee: null };
263641
+ }
263380
263642
  function missingOf(fields, ctx) {
263381
263643
  const out = [];
263382
263644
  const review = fields.kind === "review";
@@ -263461,7 +263723,7 @@ function blocksOf(fields) {
263461
263723
  else if (fields.kind === "review") blocks.push({ kind: "start", prompt: reviewStart(fields.title) });
263462
263724
  return blocks;
263463
263725
  }
263464
- function callOfDraft(draft, poster, dates, legs) {
263726
+ function callOfDraft(draft, poster, dates, legs, share) {
263465
263727
  const f = draft.fields;
263466
263728
  if (!isReady(f) || !f.money) throw new Error("the draft is not ready to post");
263467
263729
  return {
@@ -263473,20 +263735,30 @@ function callOfDraft(draft, poster, dates, legs) {
263473
263735
  poster: f.poster ?? poster,
263474
263736
  points: linesOf(f).map(() => 100 / L.lines),
263475
263737
  money: f.mode === "hire" ? { ...f.money, prize: 0, runnersN: 0, runnersEach: 0 } : f.money,
263738
+ ...share ? { share: { network: share.network, node: share.node } } : {},
263476
263739
  ...dates,
263477
263740
  ...legs && Object.keys(legs).length ? { legs } : {},
263478
263741
  blocks: blocksOf(f)
263479
263742
  };
263480
263743
  }
263481
- function legPlan(fields, leg, checkBase) {
263744
+ function legPlan(fields, leg, checkBase, share, feeInReward = false) {
263482
263745
  const m = fields.money;
263746
+ const priced = (payBase) => {
263747
+ const legBase = share ? grossFor(payBase, share) : payBase;
263748
+ return { payBase, legBase, shareBase: legBase - payBase };
263749
+ };
263483
263750
  if (leg === "entries") {
263484
- const payBase2 = toBase2(m.entryEach);
263485
- return { leg, payBase: payBase2, quota: m.entryUpTo, escrowSpots: m.entryUpTo, checkBase, escrowBase: payBase2 * m.entryUpTo, feeBase: checkBase * m.entryUpTo };
263751
+ const pay2 = priced(toBase2(m.entryEach));
263752
+ if (feeInReward) return { leg, ...pay2, quota: m.entryUpTo, escrowSpots: m.entryUpTo, checkBase, escrowBase: (pay2.legBase + checkBase) * m.entryUpTo, feeBase: 0 };
263753
+ return { leg, ...pay2, quota: m.entryUpTo, escrowSpots: m.entryUpTo, checkBase, escrowBase: pay2.legBase * m.entryUpTo, feeBase: checkBase * m.entryUpTo };
263486
263754
  }
263487
263755
  const winners = leg === "prize" ? 1 : m.runnersN;
263488
- const payBase = toBase2(leg === "prize" ? m.prize : m.runnersEach);
263489
- return { leg, payBase, quota: pickQuota(winners), escrowSpots: winners, checkBase: 0, escrowBase: payBase * winners, feeBase: 0 };
263756
+ const pay = priced(toBase2(leg === "prize" ? m.prize : m.runnersEach));
263757
+ return { leg, ...pay, quota: pickQuota(winners), escrowSpots: winners, checkBase: 0, escrowBase: pay.legBase * winners, feeBase: 0 };
263758
+ }
263759
+ function callShare(fields, split3) {
263760
+ if (!callShares(fields)) return { share: null };
263761
+ return split3 ? { share: { network: split3.network, node: split3.node } } : { unread: true };
263490
263762
  }
263491
263763
  function legTerms(draft, leg, ctx) {
263492
263764
  const access2 = accessOf(draft.fields);
@@ -263495,7 +263767,7 @@ function legTerms(draft, leg, ctx) {
263495
263767
  const review = draft.fields.kind === "review";
263496
263768
  return {
263497
263769
  ...callEntriesTerms({
263498
- call: callOfDraft(draft, ctx.poster, ctx.dates, legs),
263770
+ call: callOfDraft(draft, ctx.poster, ctx.dates, legs, ctx.share),
263499
263771
  lines: linesOf(draft.fields),
263500
263772
  judge: ctx.oracle,
263501
263773
  access: access2,
@@ -263505,17 +263777,38 @@ function legTerms(draft, leg, ctx) {
263505
263777
  via: "mcp"
263506
263778
  };
263507
263779
  }
263508
- return { ...callPickTerms({ call: callOfDraft(draft, ctx.poster, ctx.dates), leg, access: access2 }), via: "mcp" };
263780
+ return { ...callPickTerms({ call: callOfDraft(draft, ctx.poster, ctx.dates, void 0, ctx.share), leg, access: access2 }), via: "mcp" };
263509
263781
  }
263510
263782
  function callPlan(draft, ctx, now3 = Date.now()) {
263783
+ if (callShares(draft.fields) !== Boolean(ctx.share)) throw new Error(ctx.share ? "only a private call carries the network's share" : "a private call is priced with the network's share: read the split first (callShare)");
263511
263784
  const dates = draft.dates ?? callDates(draft.fields.openDays, now3, draft.fields.mode === "hire" ? "hire" : void 0);
263512
- const entries2 = legTerms(draft, "entries", { poster: ctx.poster, dates, oracle: { kind: "oracle", address: ctx.oracle } });
263785
+ const entries2 = legTerms(draft, "entries", { poster: ctx.poster, dates, oracle: { kind: "oracle", address: ctx.oracle }, share: ctx.share });
263513
263786
  const checkBase = ctx.checkerLegBase(entries2);
263514
- const legs = legOrder(draft.fields).map((leg) => legPlan(draft.fields, leg, checkBase));
263515
- return { legs, totalBase: legs.reduce((sum, one) => sum + one.escrowBase, 0), dates };
263787
+ const legs = legOrder(draft.fields).map((leg) => legPlan(draft.fields, leg, checkBase, ctx.share, ctx.feeInReward));
263788
+ return { legs, totalBase: legs.reduce((sum, one) => sum + one.escrowBase, 0), dates, share: ctx.share };
263789
+ }
263790
+ function capWords(legs, capBase, symbol2) {
263791
+ const at = (base5) => payWords(base5, symbol2);
263792
+ const fitting = (base5) => Math.floor(base5 / 1e4) * 1e4;
263793
+ const raise = "or raise OPENHANDS_MAX_PER_ASK_USD where this server is configured, and restart it";
263794
+ return legs.filter((leg) => overCap(leg, capBase)).map((leg) => {
263795
+ const over = `${leg.leg === "entries" ? "The entries" : leg.leg === "prize" ? "The pick's" : "The runners-up"} leg locks ${at(leg.escrowBase)}, over this server's ${at(capBase)} per-ask cap (OPENHANDS_MAX_PER_ASK_USD): post_call will refuse it.`;
263796
+ if (leg.leg === "prize") return `${over} Fits: a prize of at most ${at(fitting(capBase))}; ${raise}.`;
263797
+ const each = fitting(Math.floor(capBase / leg.escrowSpots * leg.payBase / leg.legBase));
263798
+ const most = Math.floor(capBase / leg.legBase);
263799
+ const fits = [most > 0 ? `${most} at ${at(leg.payBase)}` : "", each > 0 ? `${leg.escrowSpots} at ${at(each)}` : ""].filter(Boolean);
263800
+ return `${over} Fits: ${fits.length ? fits.join(", or ") : "nothing at this server's cap"}; ${raise}.`;
263801
+ });
263516
263802
  }
263517
263803
  function callCoin(draft, currency, network2, fallback) {
263518
263804
  const started = Object.values(draft.posted).some(Boolean) || Boolean(draft.inFlight);
263805
+ const asky = draft.fields.kind === "review" ? askyChecker(network2) : null;
263806
+ if (asky) {
263807
+ const symbol2 = symbolOf(asky.fee.denom);
263808
+ if (currency?.trim() && currency.trim().toUpperCase() !== symbol2 && currency.trim() !== asky.fee.denom) return { refused: `an Expert Review pays in ${symbol2} on ${network2}: leave currency out, or pass "${symbol2}"` };
263809
+ if (started && draft.coin && draft.coin !== asky.fee.denom) return { refused: `this review already pays in ${symbolOf(draft.coin)}; start a new one to pay in ${symbol2}` };
263810
+ return asky.fee.denom;
263811
+ }
263519
263812
  const was = draft.coin ?? (started ? fallback : void 0);
263520
263813
  const coin = pickCoin(currency, network2, was ?? fallback);
263521
263814
  if (typeof coin !== "string") return coin;
@@ -263543,9 +263836,9 @@ async function realSteps() {
263543
263836
  async function preCheck(plan, ctx) {
263544
263837
  const who = ctx.acting?.principal ?? ctx.poster;
263545
263838
  if (ctx.acting) {
263546
- const otherCoin = capCoinRefusal(ctx.acting, ctx.askDenom);
263839
+ const otherCoin = capCoinRefusal(ctx.acting, ctx.askDenom, ctx.coinFixed ? `${ctx.site}/connect` : void 0);
263547
263840
  if (otherCoin) return `Refused: ${otherCoin}. Nothing was posted.`;
263548
- const left = ctx.acting.cap && ctx.acting.cap.denom === ctx.askDenom ? Number(ctx.acting.cap.remainingBase) : 0;
263841
+ const left = Number(capIn(ctx.acting, ctx.askDenom)?.remainingBase ?? 0);
263549
263842
  if (left < plan.escrowBase) return `Refused: your cap has ${ctx.face(left)} left; this leg locks ${ctx.face(plan.escrowBase)}. Raise it on ${ctx.site}/connect. Nothing was posted.`;
263550
263843
  const held = await ctx.balanceBase(who, ctx.askDenom).catch(() => -1);
263551
263844
  if (held >= 0 && held < plan.escrowBase) return `Refused: the account holds ${ctx.face(held)}; this leg locks ${ctx.face(plan.escrowBase)}. Nothing was posted.`;
@@ -263650,7 +263943,10 @@ async function postNextLegNow(slug, ctx) {
263650
263943
  const shortPoster = `${poster.slice(0, 10)}\u2026${poster.slice(-4)}`;
263651
263944
  const { checkerFeeDenom: checkerFeeDenom2 } = await Promise.resolve().then(() => (init_money(), money_exports));
263652
263945
  const { network: network2 } = await Promise.resolve().then(() => (init_network(), network_exports));
263653
- const { legs, dates } = callPlan(draft, { poster: shortPoster, checkerLegBase: (terms2) => ctx.perCheck ? 0 : checkerLegBase2(checkerFeeDenom2(network2()), terms2, ctx.checkReward), oracle: ctx.checker }, ctx.now);
263946
+ const split3 = !callShares(draft.fields) ? null : ctx.feeSplit !== void 0 ? ctx.feeSplit : await (await Promise.resolve().then(() => (init_query43(), query_exports43))).feeSplitOrNull();
263947
+ const priced = callShare(draft.fields, split3);
263948
+ if ("unread" in priced) throw new PostRefused(SHARE_UNREAD);
263949
+ const { legs, dates } = callPlan(draft, { poster: shortPoster, checkerLegBase: (terms2) => ctx.fee ? ctx.fee.base : ctx.perCheck ? 0 : checkerLegBase2(checkerFeeDenom2(network2()), terms2, ctx.checkReward), oracle: ctx.checker, share: priced.share, feeInReward: Boolean(ctx.fee) }, ctx.now);
263654
263950
  if (settled?.state === "landed") {
263655
263951
  ctx.nudge?.("hire", settled.id);
263656
263952
  return { leg: settled.leg, id: settled.id, tx: null, plan: legs.find((one) => one.leg === settled.leg), terms: null, draft, resumed: true };
@@ -263683,7 +263979,7 @@ async function postNextLegNow(slug, ctx) {
263683
263979
  if (ctx.acting && legs.some((one) => one.feeBase > 0)) throw new Error(callTwoCoins);
263684
263980
  const plan = legs.find((one) => one.leg === leg);
263685
263981
  const oracle = { kind: "oracle", address: ctx.checker, rewardUixo: plan.checkBase || legs.find((one) => one.leg === "entries").checkBase };
263686
- const terms = legTerms(draft, leg, { poster: shortPoster, dates, oracle, approver: ctx.approver ?? null });
263982
+ const terms = legTerms(draft, leg, { poster: shortPoster, dates, oracle, approver: ctx.approver ?? null, share: priced.share });
263687
263983
  const screen = await steps.screen(ctx.site, terms.task, terms);
263688
263984
  if (screen?.verdict === "refuse") throw new Error(refusedWords(screen.reason));
263689
263985
  await steps.fund(actor.address, ctx.site);
@@ -263722,13 +264018,14 @@ async function postNextLegNow(slug, ctx) {
263722
264018
  title: terms.task,
263723
264019
  terms,
263724
264020
  doneMeans: published,
263725
- amountUixo: plan.payBase,
264021
+ /* The leg, not the quote: on a private call it is grossed up, and the approval leg carries the chain's split (openAskMessages). */
264022
+ amountUixo: plan.legBase,
263726
264023
  quota: plan.quota,
263727
264024
  days: legDays(dates, leg, ctx.now),
263728
264025
  denom: ctx.askDenom,
263729
264026
  gateAddress: ctx.gate || void 0,
263730
264027
  /* The entries leg is the oracle's to judge, so it posts with its rules anchored or not at all. The picks are the poster's. */
263731
- ...leg === "entries" ? { checker: ctx.checker, checkRewardUixo: plan.checkBase, requireRubric: true } : { escrowSpots: plan.escrowSpots },
264028
+ ...leg === "entries" ? { checker: ctx.checker, checkRewardUixo: plan.checkBase, requireRubric: true, ...ctx.fee ? { feeDenom: ctx.fee.denom } : {} } : { escrowSpots: plan.escrowSpots },
263732
264029
  ...campaign ? { campaign } : {},
263733
264030
  ...ctx.acting ? { as: { address: ctx.acting.principal, did: ctx.acting.did, pubkey: await steps.pubkey(ctx.acting.principal) } } : {}
263734
264031
  },
@@ -263772,7 +264069,7 @@ function confirms(said3, totalBase, symbol2) {
263772
264069
  const figure = said3.replace(/[$,\s]|USDC|PAY|IXO/gi, "");
263773
264070
  return /^\d+(\.\d+)?$/.test(figure) && Math.round(Number(figure) * MICRO3) === totalBase;
263774
264071
  }
263775
- var MICRO3, callsDir, fileOf, askInvitesFile, freshDraft, L, ENTRIES_MAX, INVITES_MAX, INVITE_NOTE_MAX, ADDRESS2, HANDLE_ITEM, isInviteItem, BREAKS, KEEPS_LINES, oneLine, manyLines, within, picture, linesOf, reviewStart, isReady, legOrder, accessOf, nextLeg, toBase2, callTwoCoins, SETTLE_MS, chainCheck, InFlightError, PostRefused, said2, indexLookupWith, indexLookup, posting, isAskPending, isTxFailed, tagOf, TESTNET_CALL_DEFAULTS, payWords;
264072
+ var MICRO3, callsDir, fileOf, askInvitesFile, freshDraft, L, ENTRIES_MAX, INVITES_MAX, INVITE_NOTE_MAX, ADDRESS2, HANDLE_ITEM, isInviteItem, BREAKS, KEEPS_LINES, oneLine, manyLines, within, picture, linesOf, nudgeAtFor, reviewStart, isReady, legOrder, accessOf, nextLeg, toBase2, callShares, SHARE_UNREAD, overCap, callTwoCoins, SETTLE_MS, chainCheck, InFlightError, PostRefused, said2, indexLookupWith, indexLookup, posting, isAskPending, isTxFailed, tagOf, TESTNET_CALL_DEFAULTS, payWords;
263776
264073
  var init_call2 = __esm({
263777
264074
  "src/call.ts"() {
263778
264075
  "use strict";
@@ -263783,6 +264080,7 @@ var init_call2 = __esm({
263783
264080
  init_hq();
263784
264081
  init_money();
263785
264082
  init_networks();
264083
+ init_checkers();
263786
264084
  init_callRead();
263787
264085
  MICRO3 = 1e6;
263788
264086
  callsDir = () => join6(process.env.OPENHANDS_HOME || join6(homedir3(), ".openhands"), currentNetwork(), "calls");
@@ -263816,6 +264114,10 @@ var init_call2 = __esm({
263816
264114
  };
263817
264115
  picture = (one, what) => !isHttps2(one.url) ? `${what}: "${one.url.slice(0, 80)}" is not an https address` : within(oneLine(one.alt), L.alt, `${what}'s words`) ?? (one.caption ? within(oneLine(one.caption), L.caption, `${what}'s caption`) : null);
263818
264116
  linesOf = (fields) => fields.lines ?? (fields.kind === "review" ? [...REVIEW_LINES] : void 0);
264117
+ nudgeAtFor = (network2, judge) => {
264118
+ const asky = askyChecker(network2);
264119
+ return asky && judge === asky.address ? { url: asky.url } : null;
264120
+ };
263819
264121
  reviewStart = (title) => (
263820
264122
  /* The person copies this into their agent, so it is in their words: no tool names (expert prompts review, 2.6). */
263821
264123
  `I'm taking part in the IXO Expert Review "${title ?? "\u2026"}". Show me what the review asks for, then help me try each scenario and keep notes and screenshots as I go: what I did, what I expected and what I saw. Write up my findings with me, each with how serious it is and how to reproduce it, and my recommendations. Show me all of it, and hand it in as my report only when I say so.`
@@ -263825,6 +264127,9 @@ var init_call2 = __esm({
263825
264127
  accessOf = (fields) => fields.access === "apply" || fields.access === "invite" ? fields.access : void 0;
263826
264128
  nextLeg = (draft) => legOrder(draft.fields).find((leg) => !draft.posted[leg]) ?? null;
263827
264129
  toBase2 = (display) => Math.round(display * MICRO3);
264130
+ callShares = (fields) => sharedCall({ access: accessOf(fields), call: { kind: fields.kind } });
264131
+ SHARE_UNREAD = "Refused: this call is a hire by invitation or application, which carries the network's share, and the chain's split could not be read just now, so its leg cannot be priced. Nothing was posted. Try again in a minute.";
264132
+ overCap = (leg, capBase) => leg.escrowBase > capBase;
263828
264133
  callTwoCoins = "this call's reviews escrow PAY beside its IXO or USDC rewards, and the connection's allowance covers the reward coin only. Use a reviewer that bills per check (Pay for reviews)";
263829
264134
  SETTLE_MS = 10 * 60 * 1e3;
263830
264135
  chainCheck = {
@@ -264007,7 +264312,7 @@ var init_inviteMessages = __esm({
264007
264312
  callsMessage = (address2, now3 = Date.now()) => `calls:${address2}:${now3}`;
264008
264313
  noticesMessage = (address2, now3 = Date.now()) => `notices:${address2}:${now3}`;
264009
264314
  tellMessage = (collectionId, address2, now3 = Date.now()) => `tell:${collectionId}:${address2}:${now3}`;
264010
- NOTICE_KINDS = ["application", "link-opened", "admitted", "declined", "invited"];
264315
+ NOTICE_KINDS = ["application", "link-opened", "admitted", "declined", "invited", "ok-waiting", "ok-reminder"];
264011
264316
  handsMessage = (address2, prefAt, digest2) => `hands:${address2}:${prefAt}:${digest2}`;
264012
264317
  vettingMineMessage = (address2, now3 = Date.now()) => `vetting-mine:${address2}:${now3}`;
264013
264318
  vettingQueueMessage = (by, now3 = Date.now()) => `vetting-queue:${by}:${now3}`;
@@ -264355,6 +264660,7 @@ __export(invites_exports, {
264355
264660
  boardHas: () => boardHas,
264356
264661
  checkAct: () => checkAct,
264357
264662
  decideApplication: () => decideApplication,
264663
+ doorLine: () => doorLine,
264358
264664
  inviteItemsOf: () => inviteItemsOf,
264359
264665
  markNoticesRead: () => markNoticesRead,
264360
264666
  myInvitations: () => myInvitations,
@@ -264495,7 +264801,10 @@ async function readStanding(site, signer2, collectionId, fetchImpl) {
264495
264801
  }
264496
264802
  function standingWords(standing, context) {
264497
264803
  const label = `Admission${context.actingFor ? ` (for ${context.actingFor}, the account you act for)` : ""}: `;
264498
- if ("poster" in standing) return `${label}you posted this ask. call_invites shows who applied; decide_application says yes or no.`;
264804
+ if ("poster" in standing) {
264805
+ if (context.access === "invite") return `${label}you posted this ask, by invitation. call_invites shows the list and which links were opened; invite_to_call adds people (ixo1 addresses, @handles, or "link" for a single-use link).`;
264806
+ return context.access === "apply" ? `${label}you posted this ask, by application. call_invites shows who applied; decide_application says yes or no.` : "";
264807
+ }
264499
264808
  if ("refused" in standing) return `${label}could not check just now (${standing.refused}). submit_work says whether you are admitted.`;
264500
264809
  const access2 = standing.access ?? context.access;
264501
264810
  if (!access2) return "";
@@ -264513,6 +264822,12 @@ function standingWords(standing, context) {
264513
264822
  if (application.decision === "no") return `${label}the poster said no to your application. submit_work is refused.`;
264514
264823
  return `${label}the poster said yes, then took you off the list. submit_work is refused.`;
264515
264824
  }
264825
+ function doorLine(collectionId, sources) {
264826
+ const door = sources.worker ?? sources.draft ?? sources.terms ?? null;
264827
+ if (!door) return `#${collectionId} \xB7 who may enter is not known yet: the board has not read this ask's terms (it does within a minute of posting). The list below already gates; call again shortly.`;
264828
+ const words4 = door === "invite" ? "by invitation" : door === "apply" ? "by application" : "open to anyone";
264829
+ return `#${collectionId} \xB7 ${words4}${!sources.worker && door !== "public" ? " (the board reads the terms within a minute; the list already gates)" : ""}`;
264830
+ }
264516
264831
  async function setPage(site, signer2, profile, fetchImpl) {
264517
264832
  const owner = signer2.principal ?? signer2.address;
264518
264833
  const prefAt = Date.now();
@@ -264948,7 +265263,7 @@ var init_receipt2 = __esm({
264948
265263
  // ../../app/lib/reviewerPick.ts
264949
265264
  var reviewerPick_exports = {};
264950
265265
  __export(reviewerPick_exports, {
264951
- ASK_THE_WORLD_CHECKERS: () => ASK_THE_WORLD_CHECKERS,
265266
+ ASK_THE_WORLD_CHECKERS: () => ASK_THE_WORLD_CHECKERS2,
264952
265267
  NETWORK_CHECKER_ROLE: () => NETWORK_CHECKER_ROLE,
264953
265268
  defaultReviewer: () => defaultReviewer,
264954
265269
  goodFor: () => goodFor,
@@ -264968,18 +265283,18 @@ function pickReviewers(oracles, args, cap = 6) {
264968
265283
  return kept.map((oracle, at) => ({ oracle, at })).sort((a, b) => Number(decided(b.oracle) > 0) - Number(decided(a.oracle) > 0) || a.at - b.at).slice(0, cap).map((one) => one.oracle);
264969
265284
  }
264970
265285
  function defaultReviewer(oracles, fallback, off) {
264971
- return oracles.find((oracle) => ASK_THE_WORLD_CHECKERS.has(oracle.address) && !oracle.unavailable && !off?.has(oracle.address)) ?? fallback;
265286
+ return oracles.find((oracle) => ASK_THE_WORLD_CHECKERS2.has(oracle.address) && !oracle.unavailable && !off?.has(oracle.address)) ?? fallback;
264972
265287
  }
264973
265288
  function goodFor(address2, named) {
264974
- if (ASK_THE_WORLD_CHECKERS.has(address2)) return "Good for photos, a quick look somewhere and short written answers. The default for an ordinary ask";
265289
+ if (ASK_THE_WORLD_CHECKERS2.has(address2)) return "Good for photos, a quick look somewhere and short written answers. The default for an ordinary ask";
264975
265290
  if (named) return "Good for cleanups, before-and-after photos and site work. The default for those";
264976
265291
  return null;
264977
265292
  }
264978
- var NETWORK_CHECKER_ROLE, plainReviewerName, isWorkAsk;
265293
+ var ASK_THE_WORLD_CHECKERS2, NETWORK_CHECKER_ROLE, plainReviewerName, isWorkAsk;
264979
265294
  var init_reviewerPick = __esm({
264980
265295
  "../../app/lib/reviewerPick.ts"() {
264981
265296
  "use strict";
264982
- init_checkers();
265297
+ ASK_THE_WORLD_CHECKERS2 = /* @__PURE__ */ new Set(["ixo1qrk9dp4r2u8q5l9cs42yh4dne4pj3wynp4enwq", "ixo12mvzayzrzsvaud86jf9shy6q2nvkea3tygxhsh"]);
264983
265298
  NETWORK_CHECKER_ROLE = "The default reviewer for cleanups and site work. An ordinary ask defaults to the Ask the World reviewer where this network carries it";
264984
265299
  plainReviewerName = (name8) => name8.replace(/\boracle\b/gi, (word) => word[0] === "O" ? "Reviewer" : "reviewer");
264985
265300
  isWorkAsk = (terms) => terms.deliverable.type === "photo-pair" || terms.deliverable.type === "mess-report" || Boolean(terms.site || terms.campaign);
@@ -265309,9 +265624,9 @@ var init_orgAsk = __esm({
265309
265624
  currencyFor = (rail, chainCoin) => rail === "usdc_base" ? "USDC" : rail === "stripe" ? "USD" : chainCoin === "USDC" ? "USDC" : void 0;
265310
265625
  chainCoinOf = (settings) => settings?.currency === "USDC" ? "USDC" : "IXO";
265311
265626
  setupPrompt = (args) => [
265312
- `Set up the payouts service for my company ${args.name} on OpenHands (${args.did}), to pay people ${args.rail === "stripe" ? "by card through Stripe" : "in USD to a wallet through the IXO payment gateway"}.`,
265627
+ `Set up the payouts service for my company ${args.name} on asks.world (${args.did}), to pay people ${args.rail === "stripe" ? "by card through Stripe" : "in USD to a wallet through the IXO payment gateway"}.`,
265313
265628
  "Use the asks plugin. If it isn't installed, install it first: /plugin marketplace add ixoworld/ixo-plugins, then /plugin install asks@ixo-plugins.",
265314
- "When it is running, tell me the payouts service address to paste into OpenHands."
265629
+ "When it is running, tell me the payouts service address to paste into asks.world."
265315
265630
  ].join("\n");
265316
265631
  needsPayoutsService = (rails) => rails.some((rail) => rail !== "chain");
265317
265632
  facilitatorDown = (health) => `Your payouts service is not answering${health.why ? ` (${health.why})` : ""}, so USD and card payouts are paused. Paying on chain still works.`;
@@ -290953,7 +291268,7 @@ import { randomBytes as randomBytes7 } from "node:crypto";
290953
291268
  // package.json
290954
291269
  var package_default = {
290955
291270
  name: "@ixo/asks-mcp",
290956
- version: "1.5.0-dev.0",
291271
+ version: "1.5.0-dev.19",
290957
291272
  description: "The Openhands Network MCP server: read the board, answer asks, post work and get paid on the ixo chain, with a key of its own on your machine.",
290958
291273
  license: "Apache-2.0",
290959
291274
  repository: {
@@ -291452,6 +291767,7 @@ init_world();
291452
291767
  init_networks();
291453
291768
  init_env();
291454
291769
  init_net();
291770
+ init_given();
291455
291771
  init_money();
291456
291772
 
291457
291773
  // ../../packages/openhands-sdk/src/data/plans.ts
@@ -291469,6 +291785,17 @@ function claimFormIn(env) {
291469
291785
  }
291470
291786
  var askProtocolChosenHere = () => Boolean(ownEnv().NEXT_PUBLIC_ASK_PROTOCOL_DID?.trim());
291471
291787
  var relayerChosenHere = () => Boolean(ownEnv().NEXT_PUBLIC_RELAYER?.trim() || ownEnv().NEXT_PUBLIC_RELAYER_NODE_DID?.trim());
291788
+ var FEE_SPLIT_ENV = "OPENHANDS_FEE_SPLIT";
291789
+ function feeSplitIn(raw) {
291790
+ const parts = (raw ?? "").split(",").map((one) => one.trim());
291791
+ if (parts.length !== 2 || parts.some((one) => !/^\d+(\.\d+)?$/.test(one))) return null;
291792
+ const [network2, node] = parts.map(Number);
291793
+ return network2 + node < 100 ? { network: network2, node } : null;
291794
+ }
291795
+ var servedSplit = (served) => {
291796
+ const { network: network2, node } = served ?? {};
291797
+ return typeof network2 === "number" && typeof node === "number" && network2 >= 0 && node >= 0 && network2 + node < 100 ? { network: network2, node } : null;
291798
+ };
291472
291799
  function fromEnv() {
291473
291800
  const env = ownEnv();
291474
291801
  return {
@@ -291486,6 +291813,7 @@ function fromEnv() {
291486
291813
  relayerNodeDid: relayerChosenHere() ? relayerNodeIfConfigured() ?? "" : "",
291487
291814
  claimForm: claimFormIn(env).form,
291488
291815
  askProtocolDid: askProtocolChosenHere() ? askProtocolDidIfConfigured() ?? "" : "",
291816
+ feeSplit: feeSplitIn(env[FEE_SPLIT_ENV]),
291489
291817
  from: "env"
291490
291818
  };
291491
291819
  }
@@ -291536,6 +291864,8 @@ async function deployment() {
291536
291864
  relayerNodeDid: relayerChosenHere() ? env.relayerNodeDid : answer.relayer?.did?.trim() || "",
291537
291865
  claimForm: mergedClaimForm(own2, answer.claimForm),
291538
291866
  askProtocolDid: env.askProtocolDid || (/^did:ixo:entity:[0-9a-f]{32}$/.test(answer.askProtocolDid?.trim() ?? "") ? answer.askProtocolDid.trim() : ""),
291867
+ /* The chain's split, as the site read it: the fallback when this process cannot read the chain itself (the chain wins). A pin here wins over the site's. */
291868
+ feeSplit: env.feeSplit ?? servedSplit(answer.feeSplit),
291539
291869
  from: "site"
291540
291870
  };
291541
291871
  }
@@ -291571,6 +291901,14 @@ function adoptAskProtocol(facts) {
291571
291901
  if (askProtocolChosenHere() || !facts.askProtocolDid) return;
291572
291902
  adopt({ askProtocolDid: facts.askProtocolDid });
291573
291903
  }
291904
+ function adoptFeeSplit(facts) {
291905
+ const split3 = facts.feeSplit ? { network: facts.feeSplit.network, node: facts.feeSplit.node } : void 0;
291906
+ const scope2 = activeScope();
291907
+ const next = scope2 ? scope2.given : { ...givenConfig() };
291908
+ if (split3) next.feeSplit = split3;
291909
+ else delete next.feeSplit;
291910
+ if (!scope2) setGivenConfig(next);
291911
+ }
291574
291912
  function adoptRelayer(facts) {
291575
291913
  if (relayerChosenHere() || !facts.relayerNodeDid) return;
291576
291914
  adopt({ relayerNodeDid: facts.relayerNodeDid });
@@ -292099,6 +292437,14 @@ function engineGateLines(decision, indent3 = " ") {
292099
292437
  return `${indent3}${MARK[gate.outcome]} ${gate.code}. ${why}`;
292100
292438
  });
292101
292439
  }
292440
+ function juryLines(criteria) {
292441
+ const judged = (criteria ?? []).filter((line2) => line2.id.endsWith("-JUDGED"));
292442
+ if (judged.length === 0) return [];
292443
+ const passed = judged.filter((line2) => line2.outcome === "pass").length;
292444
+ const word = (outcome) => outcome === "pass" ? "pass" : outcome === "fail" ? "FAIL" : "not reached";
292445
+ const must = (line2) => (line2.outcome === "fail" ? line2.text : line2.text.replace(/\s+Fail this only if[\s\S]*$/, "")).trim();
292446
+ return [`The jury, line by line: ${passed} of ${judged.length} passed`, ...judged.map((line2) => ` ${word(line2.outcome)} \xB7 ${must(line2)}`)];
292447
+ }
292102
292448
  function engineLines(decision, options = {}) {
292103
292449
  const indent3 = options.indent ?? "";
292104
292450
  const cid = decision.cid ?? options.cid ?? null;
@@ -292397,7 +292743,7 @@ var MAX_FILES = 12;
292397
292743
  var MAX_FILE_BYTES = 1e7;
292398
292744
  var reportInput = {
292399
292745
  report: external_exports.record(external_exports.string(), external_exports.unknown()).optional().describe(
292400
- "For an ask that wants a review report: { summary: { product, build?, date, environment?, target?, scope, outcome }, steps: [{ id, what, expected, observed, result: passed|failed|blocked|not_tested|excluded, notes?, evidence?: [file ids] }], findings: [{ id, title, steps?: [step ids], observed, expected, impact, severity: blocker|major|minor, severity_why, how_to_reproduce (major; a blocker may give cannot_reproduce instead), uncertainty?, advice?, evidence? }], recommendations: [{ text, priority: high|medium|low, findings?: [finding ids], target?, done_when, advice? }], limitations: [lines], sections?: { heading: text } } with every section the ask names. The person approves it in a dialog before anything is sent."
292746
+ "For an ask that wants a review report: { summary: { product, build?, date, environment?, target?, scope, outcome }, steps: [{ id, what, expected, observed, result: passed|failed|blocked|not_tested|excluded, notes?, evidence?: [file ids] }], findings: [{ id, title, steps?: [step ids], observed, expected, impact, severity: blocker|major|minor, severity_why, how_to_reproduce (major, and every severity on an Expert Review; a blocker may give cannot_reproduce instead), uncertainty?, advice?, evidence? (required on an Expert Review) }], recommendations: [{ text, priority: high|medium|low, findings?: [finding ids], target?, done_when, advice? }], limitations: [lines] (at least one on an Expert Review), sections?: { heading: text } } with every section the ask names. The person approves it in a dialog before anything is sent."
292401
292747
  ),
292402
292748
  uses_memory: external_exports.boolean().optional().describe("true when any part of the report came from what you remember about the person or from earlier chats. Ask them first; they are asked again in a dialog before it is used."),
292403
292749
  files: external_exports.array(external_exports.string().min(1).max(500)).max(MAX_FILES).optional().describe(`With report: screenshots or a PDF on this machine (png, jpg, webp or pdf, up to ${MAX_FILE_BYTES / 1e6} MB each, at most ${MAX_FILES}). A file's id, for evidence, is its name without the extension.`)
@@ -292465,7 +292811,7 @@ function summaryOf2(report, files, digest2) {
292465
292811
  `Digest: ${digest2}`
292466
292812
  ];
292467
292813
  }
292468
- var receiptLine = (site, collectionId, claimId, reviewSlug) => `Receipt: ${reviewSlug ? `${expertOrigin(site)}/expert/reviews/${reviewSlug}/report/${encodeURIComponent(claimId)}` : `${site}/r/${collectionId}/trace/${encodeURIComponent(claimId)}`}`;
292814
+ var receiptLine = (site, collectionId, claimId, reviewSlug) => `Receipt: ${reviewSlug ? `${expertOrigin(site)}/expert/reviews/${collectionId}/report/${encodeURIComponent(claimId)}` : `${site}/r/${collectionId}/trace/${encodeURIComponent(claimId)}`}`;
292469
292815
  async function sendReport(send, world) {
292470
292816
  for (const path of send.paths) {
292471
292817
  if (!isAbsolute(path) || CONTROL.test(path)) return `Refused: give each file as a full path (starting with /), with no line breaks in it. Nothing was sent.`;
@@ -292824,6 +293170,70 @@ async function sendFields(send, world) {
292824
293170
 
292825
293171
  // src/server.ts
292826
293172
  init_report();
293173
+
293174
+ // ../../packages/openhands-sdk/src/rubric/reportTemplate.ts
293175
+ var REVIEW_CHECKS = [
293176
+ "It covers the ask's scenarios (or steps)",
293177
+ "Every finding has evidence: a screenshot, recording or trace",
293178
+ "Every problem says how to reproduce it",
293179
+ "Every problem has a severity: blocker, major or minor",
293180
+ "Every recommendation says what to change",
293181
+ "The limitations are stated"
293182
+ ];
293183
+ var REVIEW_REFUSED = "A report is turned down by the checker only when it is clearly not one: no report, a report of something other than this ask, or no evidence at all. Anything else goes to the ask's owner, who gives the final OK before you are paid.";
293184
+ var REVIEW_REPORT_SECTIONS = [
293185
+ {
293186
+ title: "What was tested",
293187
+ field: "summary",
293188
+ items: [
293189
+ { say: "The ask and what you reviewed", field: "summary.product" },
293190
+ { say: "The version or build", field: "summary.build" },
293191
+ { say: "Where: environment, URL, device, build", field: "summary.environment" },
293192
+ { say: "The scenarios you covered", field: "summary.scope" },
293193
+ { say: "The date", field: "summary.date" },
293194
+ { say: "The outcome, in one sentence", field: "summary.outcome" }
293195
+ ]
293196
+ },
293197
+ {
293198
+ title: "Steps",
293199
+ field: "steps",
293200
+ items: [{ say: "One per scenario or step: what you did, what you expected, what you observed", field: "what, expected, observed" }, { say: "Its result: passed, failed, blocked or not tested", field: "result: passed, failed, blocked, not_tested" }]
293201
+ },
293202
+ {
293203
+ title: "Findings",
293204
+ field: "findings",
293205
+ items: [
293206
+ { say: "Each problem: what happened", field: "title, observed, expected" },
293207
+ { say: "Its evidence: a screenshot, recording or trace, attached and named", field: "evidence" },
293208
+ { say: "How to reproduce it (a blocker you cannot reproduce says what is missing)", field: "how_to_reproduce, cannot_reproduce" },
293209
+ { say: "Its severity, blocker, major or minor, and why", field: "severity, severity_why" },
293210
+ { say: "Its impact on the person using it", field: "impact" }
293211
+ ]
293212
+ },
293213
+ {
293214
+ title: "Recommendations",
293215
+ field: "recommendations",
293216
+ items: [{ say: "Each: what to change", field: "text" }, { say: "Its priority: high, medium or low", field: "priority" }, { say: "How to tell it is done", field: "done_when" }]
293217
+ },
293218
+ {
293219
+ title: "Limitations",
293220
+ field: "limitations",
293221
+ items: [{ say: "What you could not test, and why the result may not hold", field: "limitations" }]
293222
+ }
293223
+ ];
293224
+ function reviewReportTemplate(options = {}) {
293225
+ return [
293226
+ "Review report",
293227
+ "",
293228
+ ...REVIEW_REPORT_SECTIONS.flatMap((section) => [`## ${section.title}`, ...section.items.map((item) => `- ${item.say}${options.fields && item.field ? ` (${item.field})` : ""}`), ""]),
293229
+ "What the checker looks at:",
293230
+ ...REVIEW_CHECKS.map((check2, index) => `${index + 1}. ${check2}`),
293231
+ "",
293232
+ REVIEW_REFUSED
293233
+ ].join("\n");
293234
+ }
293235
+
293236
+ // src/server.ts
292827
293237
  init_site();
292828
293238
  var VERSION6 = package_default.version;
292829
293239
  var onSite = (url4) => {
@@ -292962,6 +293372,7 @@ async function build2(options, server2, parts) {
292962
293372
  adoptAskProtocol(DEPLOYMENT);
292963
293373
  adoptReviewBilling(DEPLOYMENT);
292964
293374
  adoptOracleUrl(DEPLOYMENT);
293375
+ if (local) adoptFeeSplit(DEPLOYMENT);
292965
293376
  const OWN_FORM_ENV = Object.fromEntries(CLAIM_FORM_ENV.map((name8) => [name8, ownEnv()[name8]]));
292966
293377
  if (local) adoptClaimForm(DEPLOYMENT);
292967
293378
  let CLAIM_FORM = DEPLOYMENT.claimForm;
@@ -292979,6 +293390,7 @@ async function build2(options, server2, parts) {
292979
293390
  adoptAskProtocol(fresh2);
292980
293391
  adoptReviewBilling(fresh2);
292981
293392
  adoptOracleUrl(fresh2);
293393
+ adoptFeeSplit(fresh2);
292982
293394
  adoptClaimForm(fresh2);
292983
293395
  CLAIM_FORM = fresh2.claimForm;
292984
293396
  applyDeployment(fresh2);
@@ -293017,6 +293429,7 @@ async function build2(options, server2, parts) {
293017
293429
  const FUNDER = ownEnv().OPENHANDS_FUNDER?.trim() || void 0;
293018
293430
  const FOLLOW_PAY_MAX = 10;
293019
293431
  const MAX_PER_ASK = CAPS.perAsk;
293432
+ const CAP_BASE = Math.round(MAX_PER_ASK * MICRO4);
293020
293433
  const MAX_PER_DAY = CAPS.perDay;
293021
293434
  const MAX_PER_DAY_TOTAL = CAPS.total;
293022
293435
  const ALL_TOOLS = "all";
@@ -293251,7 +293664,9 @@ Each is its own ask and pays on its own. Read one with read_ask before deciding.
293251
293664
  const slug = tag ? slugOfTag2(tag) : null;
293252
293665
  if (!tag || !slug) return;
293253
293666
  const group = await read9(`/api/board/campaign/${tag}`);
293254
- calls.set(tag, { slug, entries: group?.rows?.find((one) => /^(Open call|Expert review): /.test(one.title)) ?? null });
293667
+ const entries2 = group?.rows?.find((one) => /^(Open call|Expert review): /.test(one.title)) ?? null;
293668
+ const shared = entries2 ? (await askReceipt(entries2.id))?.payments?.["approval"]?.is_oracle_payment === true : false;
293669
+ calls.set(tag, { slug, entries: entries2, shared });
293255
293670
  })
293256
293671
  );
293257
293672
  const callLine = (row, call) => {
@@ -293259,11 +293674,13 @@ Each is its own ask and pays on its own. Read one with read_ask before deciding.
293259
293674
  const review = isReviewTitle(call.entries?.title);
293260
293675
  const title = call.entries?.title.replace(/^(Open call|Expert review): /, "") ?? null;
293261
293676
  if (title) taint2(title);
293262
- const entryPays = call.entries?.pays ? faceOf2(call.entries.pays)?.text : void 0;
293677
+ const facts = row.callSummary;
293678
+ const shares = call.shared;
293679
+ const entryPays = shares && facts?.entryEach !== void 0 && call.entries?.pays ? `${faceOf2({ value: facts.entryEach, symbol: call.entries.pays.symbol })?.text} (the network's share is on top of it)` : call.entries?.pays ? `${faceOf2(call.entries.pays)?.text}${shares ? ", less the network's share" : ""}` : void 0;
293263
293680
  return [
293264
293681
  `${review ? "Expert review" : "Open call"} "${call.slug}" \xB7 ${summary.open} of ${summary.places} legs open${entryPays ? ` \xB7 every ${review ? "report" : "entry"} that meets the bar is paid ${entryPays}` : ""} \xB7 needs-a-human`,
293265
293682
  title ? asData("ask-title", title) : "",
293266
- `${review ? "A specialist's report on the review's scenarios, assessed against its requirements. A report that meets them qualifies for payment, after the builder's final approval when they give one." : "A written entry assessed against five requirements. Entries that meet all five qualify for payment. For competitions, the poster also chooses a prize winner."} ${call.entries ? `Read it with read_ask "${call.entries.id}"; the page is` : "Read its legs on the page:"} ${review ? `${EXPERT_SITE}/expert/reviews/${call.slug}` : `${SITE}/opencall/${call.slug}`}`
293683
+ `${review ? "A specialist's report on the review's scenarios, assessed against its requirements. A report that meets them qualifies for payment, after the builder's final approval when they give one." : "A written entry assessed against five requirements. Entries that meet all five qualify for payment. For competitions, the poster also chooses a prize winner."} ${call.entries ? `Read it with read_ask "${call.entries.id}"; the page is` : "Read its legs on the page:"} ${review ? `${EXPERT_SITE}/expert/reviews/${call.entries?.id ?? call.slug}` : `${SITE}/opencall/${call.slug}`}`
293267
293684
  ].filter(Boolean).join("\n");
293268
293685
  };
293269
293686
  const lines2 = board.rows.filter(within2).map((row) => {
@@ -293321,7 +293738,8 @@ ${[...folded, ...hidden].join("\n")}${folded.length || hidden.length ? "\n\n" :
293321
293738
  if (!receipt) return text4(`Could not read ask ${collectionId}.`);
293322
293739
  const { coinDisplay: coinDisplay2, faceOf: faceOf2 } = await Promise.resolve().then(() => (init_format(), format_exports));
293323
293740
  const { fetchDoc: fetchDoc2 } = await Promise.resolve().then(() => (init_docs(), docs_exports));
293324
- const pays = faceOf2(coinDisplay2(receipt.payments?.["approval"]?.amount));
293741
+ const shown2 = coinDisplay2(receipt.payments?.["approval"]?.amount);
293742
+ const pays = shown2?.symbol === "PAY" ? { text: `${shown2.value} PAY` } : faceOf2(shown2);
293325
293743
  const already = receipt.claims?.some((claim3) => ours.includes(claim3.agentAddress));
293326
293744
  let offChain = null;
293327
293745
  if (!pays && receipt.payout) {
@@ -293332,6 +293750,7 @@ ${[...folded, ...hidden].join("\n")}${folded.length || hidden.length ? "\n\n" :
293332
293750
  }
293333
293751
  let terms = "(this ask published no terms. The title is all there is)";
293334
293752
  let parsed = null;
293753
+ let postedShare = null;
293335
293754
  if (receipt.doneMeansCid) {
293336
293755
  const doc = await fetchDoc2(receipt.doneMeansCid, { timeoutMs: 15e3, fetchImpl: fetch2, at: receipt.doneMeansUrl }).then((r) => r.ok ? r.text() : null).catch(() => null);
293337
293756
  if (doc) {
@@ -293344,7 +293763,8 @@ ${[...folded, ...hidden].join("\n")}${folded.length || hidden.length ? "\n\n" :
293344
293763
  const read9 = parsed ? parseTerms2(parsed) : null;
293345
293764
  if (read9?.call) {
293346
293765
  const { callReadout: callReadout2 } = await Promise.resolve().then(() => (init_call2(), call_exports2));
293347
- terms = callReadout2(read9, { id: collectionId, symbol: ASK_SYMBOL, site: SITE });
293766
+ terms = callReadout2(read9, { id: collectionId, symbol: shown2?.symbol ?? ASK_SYMBOL, site: SITE });
293767
+ postedShare = read9.call.share ?? null;
293348
293768
  }
293349
293769
  }
293350
293770
  }
@@ -293353,14 +293773,16 @@ ${[...folded, ...hidden].join("\n")}${folded.length || hidden.length ? "\n\n" :
293353
293773
  taint2(terms);
293354
293774
  const fit = fitFromTerms(parsed);
293355
293775
  const declared = parsed?.access;
293356
- const access2 = receipt.access ?? (declared === "apply" || declared === "invite" ? declared : null);
293776
+ const drafted = local ? (await Promise.resolve().then(() => (init_call2(), call_exports2))).draftOfEntries(collectionId)?.fields.access : void 0;
293777
+ const access2 = receipt.access ?? (declared === "apply" || declared === "invite" ? declared : drafted === "apply" || drafted === "invite" ? drafted : null);
293357
293778
  const admitted = access2 && admission ? await admission(receipt, access2) : "";
293358
293779
  const judge = parsed?.judge;
293359
293780
  const reviewer = await oracleWordsFor({ did: receipt.payout?.oracle ?? null, address: judge?.kind === "oracle" && typeof judge.address === "string" ? judge.address : null });
293360
293781
  taint2(reviewer);
293782
+ const shared = pays ? await sharedPays(receipt.payments?.["approval"], postedShare) : null;
293361
293783
  return text4(
293362
293784
  [
293363
- offChain ? `Ask #${collectionId} \xB7 pays ${offChain}` : `Ask #${collectionId} \xB7 pays ${pays ? `${pays.text}` : "nothing"} per approved answer`,
293785
+ offChain ? `Ask #${collectionId} \xB7 pays ${offChain}` : `Ask #${collectionId} \xB7 pays ${shared ?? (pays ? `${pays.text}` : "nothing")} per approved answer`,
293364
293786
  `Standing: ${receipt.standing ?? (receipt.openness?.open ? "open" : "not accepting answers")}`,
293365
293787
  /* A call's readout says its own door (callRead.ts); this line is for every other ask whose terms carry one. */
293366
293788
  !parsed?.call && receipt.access === "invite" ? "By invitation: only the addresses the poster listed are admitted (a link from the poster goes in submit_work's invite)." : !parsed?.call && receipt.access === "apply" ? "By application: apply_to_call first; the poster's yes admits you." : "",
@@ -293372,7 +293794,10 @@ ${[...folded, ...hidden].join("\n")}${folded.length || hidden.length ? "\n\n" :
293372
293794
 
293373
293795
  ${terms}`),
293374
293796
  "",
293375
- `Fit: ${fit.fit}. ${fit.because}`
293797
+ `Fit: ${fit.fit}. ${fit.because}`,
293798
+ /* An Expert Review: the same template and checks the submit page shows, for the report submit_work sends. */
293799
+ ...parsed?.call?.kind === "review" ? ["", `Write the report to this template (submit_work's report fields in brackets):
293800
+ ${reviewReportTemplate({ fields: true })}`] : []
293376
293801
  ].filter(Boolean).join("\n")
293377
293802
  );
293378
293803
  }
@@ -293448,6 +293873,7 @@ ${terms}`),
293448
293873
  if (expertAsk && (!fields || deliverable || report)) return text4(`Refused: ${expertAsk.checker.name} checks this ask by its own form. Pass fields (and no deliverable or report): ${questionsLine(expertAsk.check.fields)}. Nothing was sent.`);
293449
293874
  let requirement = null;
293450
293875
  let reviewSlug = null;
293876
+ let judgeAddress = null;
293451
293877
  const rubricTerms = await Promise.resolve().then(() => (init_terms(), terms_exports));
293452
293878
  if (receipt.doneMeansCid && !expertAsk) {
293453
293879
  const { readDocJson: readDocJson2 } = await Promise.resolve().then(() => (init_src5(), src_exports4));
@@ -293458,9 +293884,13 @@ ${terms}`),
293458
293884
  if (photo) return text4(photo);
293459
293885
  const siteReport = terms?.report;
293460
293886
  if (!raw && !terms && report) return text4("Refused: this ask's terms could not be read just now, so the report could not be checked against them. Nothing was sent; try again in a minute.");
293887
+ const judge = (raw ?? terms)?.judge;
293888
+ judgeAddress = judge?.kind === "oracle" && typeof judge.address === "string" ? judge.address : null;
293461
293889
  reviewSlug = reviewSlugOf(raw ? rubricTerms.parseTerms(raw)?.call ?? {} : terms?.call ?? {});
293462
- requirement = raw ? rubricTerms.parseTerms(raw)?.report ?? null : siteReport ? { sections: Array.isArray(siteReport.sections) ? siteReport.sections.filter((one) => typeof one === "string") : [] } : null;
293463
- if (requirement && !report) return text4(`Refused: this ask wants a review report. Pass report (and files for screenshots or a PDF), or answer on the website: ${SITE}/submit/${collectionId}. Nothing was sent.`);
293890
+ requirement = raw ? rubricTerms.parseTerms(raw)?.report ?? null : siteReport ? { sections: Array.isArray(siteReport.sections) ? siteReport.sections.filter((one) => typeof one === "string") : [], ...siteReport.v === 2 ? { v: 2 } : {} } : null;
293891
+ if (requirement && !report) return text4(`Refused: this ask wants a review report. Pass report (and files for screenshots or a PDF), or answer on the website: ${SITE}/submit/${collectionId}. Nothing was sent.${requirement.v === 2 ? `
293892
+
293893
+ ${reviewReportTemplate({ fields: true })}` : ""}`);
293464
293894
  if (report) {
293465
293895
  const problems = reportProblems(report, requirement, (files ?? []).map(fileId));
293466
293896
  if (problems.length) return text4(`Refused: the report is not ready. Nothing was sent.
@@ -293551,7 +293981,7 @@ You cannot complete it, and trying would spend the gate's gas to be rejected. Wh
293551
293981
  };
293552
293982
  const who = { submitter, ...acting ? { delegate: actor.address } : {}, ...alias ? { alias } : {} };
293553
293983
  const said3 = report ? await sendReport({ collectionId, report, paths: files ?? [], requirement, ...uses_memory ? { usesMemory: true } : {}, ...who }, world) : await sendFields({ fields: expertAsk.check.fields, values: fields, collectionId, ...who }, world);
293554
- if (said3.startsWith("Answered")) nudge("scan", collectionId, expertAsk?.checker);
293984
+ if (said3.startsWith("Answered")) nudge("scan", collectionId, expertAsk?.checker ?? (await Promise.resolve().then(() => (init_call2(), call_exports2))).nudgeAtFor(NETWORK2, judgeAddress));
293555
293985
  return text4(expertAsk && said3.startsWith("Answered") ? said3.replace(/^(Answered #\d+ as \S+)\./, `$1, for ${expertAsk.checker.name} to check.`) : said3);
293556
293986
  }
293557
293987
  const refusedToPrepare = await prepare2();
@@ -293615,8 +294045,8 @@ Receipt: ${answerPage(SITE, collectionId, stored.cid, { kind: reviewSlug ? "revi
293615
294045
  if (!who.url) return { refused: `${who.name} has no address to be hired at` };
293616
294046
  const billing = await Promise.resolve().then(() => (init_reviewBilling(), reviewBilling_exports));
293617
294047
  if (who.perReview && billing.reviewsBilledPerCheck() && what.priceBase !== null) return { ...picked, leg: 0, feeDenom: FEE_DENOM, perCheck: { price: what.priceBase, oracleUrl: who.url } };
293618
- const { ASK_THE_WORLD_CHECKERS: ASK_THE_WORLD_CHECKERS2, feeCoinOf: feeCoinOf2 } = await Promise.resolve().then(() => (init_checkers(), checkers_exports));
293619
- const feeCoin = feeCoinOf2(what.priceDenom, ASK_THE_WORLD_CHECKERS2.has(who.address) || CHECKER !== "" && who.address === CHECKER);
294048
+ const { ASK_THE_WORLD_CHECKERS: ASK_THE_WORLD_CHECKERS3, feeCoinOf: feeCoinOf2 } = await Promise.resolve().then(() => (init_checkers(), checkers_exports));
294049
+ const feeCoin = feeCoinOf2(what.priceDenom, ASK_THE_WORLD_CHECKERS3.has(who.address) || CHECKER !== "" && who.address === CHECKER);
293620
294050
  if ("refused" in feeCoin) return feeCoin;
293621
294051
  if (what.priceBase === null) return { refused: `${who.name} does not say what "${what.name}" costs` };
293622
294052
  return { ...picked, leg: what.priceBase, feeDenom: feeCoin.denom, perCheck: null };
@@ -293931,8 +294361,22 @@ ${SITE}/t/${earlier.id}`);
293931
294361
  let ENGINE_ORACLE = DEPLOYMENT.engine.oracle;
293932
294362
  let ENGINE_MODE = DEPLOYMENT.engine.live;
293933
294363
  const engineLive = () => ENGINE_MODE && Boolean(ENGINE_ORACLE);
293934
- const callPerCheck = async () => (await Promise.resolve().then(() => (init_reviewBilling(), reviewBilling_exports))).deploymentBillsPerCheck(CHECKER).catch(() => false);
293935
- const callLocks = (rewardBase, feeBase) => `${face(rewardBase / MICRO4, ASK_SYMBOL)}${feeBase > 0 ? ` and ${feeWords(feeBase)} for reviews` : ""}`;
294364
+ const callPerCheck = async (checker = CHECKER) => (await Promise.resolve().then(() => (init_reviewBilling(), reviewBilling_exports))).deploymentBillsPerCheck(checker).catch(() => false);
294365
+ const callChecker2 = async (review) => (await Promise.resolve().then(() => (init_call2(), call_exports2))).callChecker(NETWORK2, review, CHECKER);
294366
+ const callSplit = async () => (await Promise.resolve().then(() => (init_query43(), query_exports43))).feeSplitOrNull();
294367
+ async function sharedPays(leg, posted) {
294368
+ if (leg?.is_oracle_payment !== true || !leg.amount?.[0]) return null;
294369
+ const { coinDisplay: coinDisplay2, faceOf: faceOf2 } = await Promise.resolve().then(() => (init_format(), format_exports));
294370
+ const { netOf: netOf2 } = await Promise.resolve().then(() => (init_share(), share_exports));
294371
+ const { shareWords: shareWords2 } = await Promise.resolve().then(() => (init_callRead(), callRead_exports));
294372
+ const { denom, amount: amount4 } = leg.amount[0];
294373
+ const said3 = (base5) => faceOf2(coinDisplay2([{ denom, amount: String(base5) }]))?.text ?? `${base5} ${denom}`;
294374
+ const split3 = posted ?? await callSplit();
294375
+ if (!split3) return `${said3(Number(amount4))} less the network's share (the chain's split could not be read just now)`;
294376
+ const receives = netOf2(Number(amount4), split3);
294377
+ return `${said3(receives)} (the leg is ${said3(Number(amount4))}; the network keeps ${said3(Number(amount4) - receives)}: ${shareWords2(split3, posted ? "as posted" : "today")})`;
294378
+ }
294379
+ const callLocks = (rewardBase, feeBase, symbol2 = ASK_SYMBOL) => `${symbol2 === "PAY" ? feeWords(rewardBase, "PAY") : face(rewardBase / MICRO4, symbol2)}${feeBase > 0 ? ` and ${feeWords(feeBase)} for reviews` : ""}`;
293936
294380
  function escrowTotal(pay, spots, legBase, symbol2 = ASK_SYMBOL, sameCoin = true, feeSymbol = FEE_SYMBOL) {
293937
294381
  const leg = legBase / MICRO4;
293938
294382
  if (!sameCoin && leg > 0) {
@@ -294277,6 +294721,8 @@ ${SITE}/t/${earlier.id}`);
294277
294721
  "",
294278
294722
  ...evidenceLines2(evidence),
294279
294723
  "",
294724
+ /* How each judged line went, in the ask's own words (#44101). */
294725
+ ...proof.cid ? juryLines((await readJson3(`${SITE}/api/check/${encodeURIComponent(proof.cid)}`))?.criteria) : [],
294280
294726
  gates.length ? `Machine: ${gates.join(" \xB7 ")}` : "",
294281
294727
  `Answered by ${approved.agentAddress}`,
294282
294728
  /* The answer's own page: a link that unfurls as the work-done card, wherever an agent posts it. */
@@ -294825,10 +295271,24 @@ They can disconnect at ${SITE}/connect.`);
294825
295271
  }
294826
295272
  );
294827
295273
  async function checkVerdict(collectionId, mine, filedFor, mint) {
294828
- const receipt = await askReceipt(collectionId, [mine, ...filedFor ? [filedFor] : []]);
295274
+ const agents = [mine, ...filedFor ? [filedFor] : []];
295275
+ const { receiptWithOurs: receiptWithOurs2 } = await Promise.resolve().then(() => (init_connect(), connect_exports));
295276
+ const read9 = await receiptWithOurs2(
295277
+ () => askReceipt(collectionId, agents),
295278
+ mine,
295279
+ filedFor,
295280
+ async () => {
295281
+ const { receiptQuery: receiptQuery2 } = await Promise.resolve().then(() => (init_receipt2(), receipt_exports2));
295282
+ const { gqlAt: gqlAt2 } = await Promise.resolve().then(() => (init_gql(), gql_exports));
295283
+ const { graphqlUrl: graphqlUrl4 } = await Promise.resolve().then(() => (init_env(), env_exports));
295284
+ const data = await gqlAt2(graphqlUrl4(), receiptQuery2(collectionId, { size: 1, waiting: false, agents }), { revalidate: null, fetchImpl: fetch2 });
295285
+ return data.claimCollection?.named?.nodes ?? [];
295286
+ }
295287
+ );
295288
+ const receipt = read9.receipt;
294829
295289
  if (!receipt) return text4(`Could not read ask ${collectionId}.`);
294830
295290
  const claims = receipt.claims ?? [];
294831
- const ours = claims.find((claim3) => claim3.agentAddress === mine) ?? (filedFor ? claims.find((claim3) => claim3.agentAddress === filedFor) : void 0);
295291
+ const ours = read9.ours;
294832
295292
  if (!ours) {
294833
295293
  return text4(
294834
295294
  claims.length > 0 ? `Nothing of yours on #${collectionId}. ${claims.length} other answer(s) are there.` : `Nothing on #${collectionId} yet. If you have just sent an answer, the public index trails the chain by a few seconds. Check again shortly.`
@@ -294841,6 +295301,7 @@ They can disconnect at ${SITE}/connect.`);
294841
295301
  const facts = verdictFacts2(ours.evaluationByClaimId, onChain);
294842
295302
  const said3 = verdictSays2(facts.status);
294843
295303
  const paid = faceOf2(coinDisplay2(facts.amount ?? void 0));
295304
+ const arrived = paid && facts.amount?.length ? await sharedPays({ amount: facts.amount, is_oracle_payment: receipt.payments?.["approval"]?.is_oracle_payment }, null) : null;
294844
295305
  const proofRaw = facts.verificationProof;
294845
295306
  const { proofRef: proofRef2 } = await Promise.resolve().then(() => (init_proof(), proof_exports));
294846
295307
  const { engineDecisionFor: engineDecisionFor2 } = await Promise.resolve().then(() => (init_engine(), engine_exports));
@@ -294849,7 +295310,7 @@ They can disconnect at ${SITE}/connect.`);
294849
295310
  const tail = engine ? ["", ...engineLines(engine, { cid: signed.cid ?? null })] : signed.kind === "udid-url" && signed.cid ? [`signed ${SITE}/v1/receipts/${signed.cid}?format=json. Check it rather than taking our word`] : [];
294850
295311
  const steps = await traceBlock(collectionId, ours, receipt, engine);
294851
295312
  const why = await whyBlock(ours.claimId, proofRaw, ours.agentAddress === mine ? mint : void 0);
294852
- return text4([`#${collectionId}: ${said3}.${paid ? ` Paid ${paid.text}.` : ""}`, `answer ${ours.claimId}`, `${SITE}/r/${collectionId}/trace/${encodeURIComponent(ours.claimId)}`, ...why, ...steps, ...tail].join("\n"));
295313
+ return text4([`#${collectionId}: ${said3}.${paid ? ` Paid ${arrived ?? paid.text}.` : ""}`, `answer ${ours.claimId}`, `${SITE}/r/${collectionId}/trace/${encodeURIComponent(ours.claimId)}`, ...why, ...steps, ...tail].join("\n"));
294853
295314
  }
294854
295315
  if (local) {
294855
295316
  server2.registerTool(
@@ -295009,14 +295470,14 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
295009
295470
  {
295010
295471
  ...meta3("draft_call"),
295011
295472
  inputSchema: {
295012
- slug: external_exports.string().max(40).optional().describe("The call's name, for its page: lowercase letters, digits and dashes, starting with a letter. Omit for call-<n>; then use the name the answer gives."),
295473
+ slug: external_exports.string().max(40).optional().describe("The call's name, for its page: lowercase letters, digits and dashes, starting with a letter. Omit for a random call-<hex> name; then use the name the answer gives."),
295013
295474
  title: external_exports.string().max(160).optional().describe("The call, in one sentence."),
295014
295475
  lede: external_exports.string().max(500).optional().describe("Two or three sentences under the title."),
295015
295476
  poster: external_exports.string().max(80).optional().describe("How the poster wants to be named on the call."),
295016
295477
  brief: external_exports.array(external_exports.string().max(1500)).max(8).optional().describe("The story, up to eight short paragraphs, in the person's words."),
295017
295478
  lines: external_exports.array(external_exports.object({ text: external_exports.string().max(300), fail: external_exports.string().max(300).optional() })).max(5).optional().describe('The five lines the jury rules on: each a must a good entry passes (text) and what fails it (fail). The fail is the end of the sentence "Fail this only if \u2026" \u2014 e.g. "it names no page" \u2014 not a sentence of its own. All five, together.'),
295018
295479
  mode: external_exports.enum(["competition", "hire"]).optional().describe("competition: a pick and runners-up beside the paid entries. hire: every entry that passes is paid the same, up to a number, and nobody is picked."),
295019
- kind: external_exports.literal("review").optional().describe("review: an expert review of an app or an agent (IXO Expert Reviews). Specialists try the scenarios and hand in review reports; every report that passes the lines is paid the same (a hire); by invitation or by application, never open to anyone; the five lines default to the review's own unless given."),
295480
+ kind: external_exports.literal("review").optional().describe("review: an expert review of an app or an agent (IXO Expert Reviews). Specialists try the scenarios and hand in review reports; every report that passes the lines is paid the same (a hire); by invitation or by application, never open to anyone; the five lines default to the review's own unless given. Where Asky Checker judges reviews (testnet), a review pays in PAY: the reward and Asky's 1 PAY a check."),
295020
295481
  subject: external_exports.object({ name: external_exports.string().max(120), version: external_exports.string().max(40).optional(), where: external_exports.string().max(2048).optional() }).optional().describe("A review: what is reviewed, the version to test, and where a specialist finds it (a public address). Private access goes in the invitation note, never here."),
295021
295482
  scenarios: external_exports.array(external_exports.string().max(300)).max(8).optional().describe("A review: the scenarios to try, one line each; every report must cover each with a step."),
295022
295483
  report: external_exports.array(external_exports.string().max(60)).max(5).optional().describe("A review: extra section headings every report must have, besides the standard summary, steps, findings, recommendations and limitations. Empty is fine."),
@@ -295052,11 +295513,11 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
295052
295513
  const changing = clear || Object.values(input).some((value) => value !== void 0);
295053
295514
  if (posted && changing) return text4(`Call "${slug}" is already on chain: its terms were frozen with the money. Start a new call with another slug.`);
295054
295515
  const start = clear || !existing ? call.freshDraft(slug) : existing;
295055
- const coin = call.callCoin(start, currency, NETWORK2, ASK_DENOM);
295056
- if (typeof coin !== "string") return text4(`Refused: ${coin.refused}. Nothing was saved.`);
295057
295516
  const merged = call.mergeDraft(start, input);
295517
+ const coin = call.callCoin(merged.draft, currency, NETWORK2, ASK_DENOM);
295518
+ if (typeof coin !== "string") return text4(`Refused: ${coin.refused}. Nothing was saved.`);
295058
295519
  const { refused: refused2 } = merged;
295059
- const draft = currency ? { ...merged.draft, coin } : merged.draft;
295520
+ const draft = currency || coin !== ASK_DENOM ? { ...merged.draft, coin } : merged.draft;
295060
295521
  call.writeDraft(draft);
295061
295522
  const coinSymbol = symbolOf(coin);
295062
295523
  const acting = await principal2();
@@ -295078,18 +295539,34 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
295078
295539
  const { checkerLegBase: checkerLegBase2 } = await Promise.resolve().then(() => (init_leg(), leg_exports));
295079
295540
  const poster = acting?.principal ?? await address2();
295080
295541
  const shortPoster = `${poster.slice(0, 10)}\u2026${poster.slice(-4)}`;
295081
- const perCheck = await callPerCheck();
295082
- const plan = call.callPlan(draft, { poster: shortPoster, checkerLegBase: (terms) => perCheck ? 0 : checkerLegBase2(FEE_DENOM, terms, CHECK_REWARD), oracle: CHECKER || "the oracle" });
295542
+ const checker = await callChecker2(draft.fields.kind === "review");
295543
+ if (checker.fee && checker.fee.denom !== coin) return text4(`Refused: ${checker.name} is paid in ${symbolOf(checker.fee.denom)}, so a review pays in it too: pass currency "${symbolOf(checker.fee.denom)}".`);
295544
+ const perCheck = checker.fee ? false : await callPerCheck(checker.address);
295545
+ const priced = call.callShare(draft.fields, call.callShares(draft.fields) ? await callSplit() : null);
295546
+ if ("unread" in priced) {
295547
+ out.push("", "Not priced yet: this call is a hire by invitation or application, which carries the network's share, and the chain's split could not be read just now. The draft is saved; call draft_call again in a minute.");
295548
+ return text4(out.join("\n"));
295549
+ }
295550
+ const plan = call.callPlan(draft, { poster: shortPoster, checkerLegBase: (terms) => checker.fee ? checker.fee.base : perCheck ? 0 : checkerLegBase2(FEE_DENOM, terms, CHECK_REWARD), oracle: checker.address || "the oracle", share: priced.share, feeInReward: Boolean(checker.fee) });
295083
295551
  const feeTotal = plan.legs.reduce((sum, one) => sum + one.feeBase, 0);
295084
- const preview = call.legTerms(draft, "entries", { poster: shortPoster, dates: plan.dates, oracle: { kind: "oracle", address: CHECKER || "the oracle" } });
295552
+ const preview = call.legTerms(draft, "entries", { poster: shortPoster, dates: plan.dates, oracle: { kind: "oracle", address: checker.address || "the oracle" }, share: plan.share });
295085
295553
  const twoCoins = acting && feeTotal > 0 ? `Can't post as ${acting.principal}: ${call.callTwoCoins}.` : null;
295086
- const otherCoin = (await Promise.resolve().then(() => (init_hq(), hq_exports))).capCoinRefusal(acting, coin);
295554
+ const { capCoinRefusal: capCoinRefusal2, capIn: capIn2 } = await Promise.resolve().then(() => (init_hq(), hq_exports));
295555
+ const otherCoin = capCoinRefusal2(acting, coin, checker.fee ? `${SITE}/connect` : void 0);
295556
+ const capped3 = call.capWords(plan.legs, CAP_BASE, coinSymbol);
295557
+ const keyOnly = !acting && feeTotal > 0;
295558
+ const blocked = capped3.length > 0 || Boolean(otherCoin) || Boolean(twoCoins) && draft.fields.kind !== "review";
295087
295559
  const m = draft.fields.money;
295560
+ const { shareWords: shareWords2 } = await Promise.resolve().then(() => (init_callRead(), callRead_exports));
295561
+ const who = draft.fields.kind === "review" ? "specialist" : "hired person";
295562
+ const onTop = (leg) => plan.share ? `; the network's share (${shareWords2(plan.share, "today")}) is added on top, so the leg pays ${call.payWords(leg.legBase, coinSymbol)}` : "";
295088
295563
  const legRows = plan.legs.map(
295089
- (leg) => leg.leg === "prize" ? ` The pick ${call.payWords(leg.payBase, coinSymbol)} to one entry; its ask takes ${leg.quota} claims (the pick and two spare) \xB7 locks ${call.payWords(leg.escrowBase, coinSymbol)}` : leg.leg === "runnersUp" ? ` Runners-up ${call.payWords(leg.payBase, coinSymbol)} to each of ${m.runnersN} \xB7 locks ${call.payWords(leg.escrowBase, coinSymbol)}` : ` Entries ${call.payWords(leg.payBase, coinSymbol)} to each entry that passes all ${draft.fields.kind === "review" ? "six" : "five"} lines, up to ${leg.quota}${leg.checkBase > 0 ? `, plus the oracle's ${feeWords(leg.checkBase)} per ruling` : ", the oracle billed per check from credits"} \xB7 locks ${callLocks(leg.escrowBase, leg.feeBase)}`
295564
+ (leg) => leg.leg === "prize" ? ` The pick ${call.payWords(leg.payBase, coinSymbol)} to one entry${onTop(leg)}; its ask takes ${leg.quota} claims (the pick and two spare) \xB7 locks ${call.payWords(leg.escrowBase, coinSymbol)}` : leg.leg === "runnersUp" ? ` Runners-up ${call.payWords(leg.payBase, coinSymbol)} to each of ${m.runnersN}${onTop(leg)} \xB7 locks ${call.payWords(leg.escrowBase, coinSymbol)}` : plan.share ? ` Entries each ${who} receives ${call.payWords(leg.payBase, coinSymbol)} for an entry that passes all ${draft.fields.kind === "review" ? "six" : "five"} lines${onTop(leg)} \xB7 up to ${leg.quota}${checker.fee ? `, plus ${call.payWords(leg.checkBase, coinSymbol)} per check to ${checker.name}` : leg.checkBase > 0 ? `, plus the oracle's ${feeWords(leg.checkBase)} per ruling` : ", the oracle billed per check from credits"} \xB7 locks ${callLocks(leg.escrowBase, leg.feeBase, coinSymbol)}` : ` Entries ${call.payWords(leg.payBase, coinSymbol)} to each entry that passes all ${draft.fields.kind === "review" ? "six" : "five"} lines, up to ${leg.quota}${checker.fee ? `, plus ${call.payWords(leg.checkBase, coinSymbol)} per check to ${checker.name}` : leg.checkBase > 0 ? `, plus the oracle's ${feeWords(leg.checkBase)} per ruling` : ", the oracle billed per check from credits"} \xB7 locks ${callLocks(leg.escrowBase, leg.feeBase, coinSymbol)}`
295090
295565
  );
295566
+ const shareTotal = plan.legs.reduce((sum, one) => sum + one.shareBase * one.escrowSpots, 0);
295091
295567
  const hire = draft.fields.mode === "hire";
295092
295568
  const warnings2 = [
295569
+ ...capped3,
295093
295570
  twoCoins ?? "",
295094
295571
  otherCoin ? `Can't post as it stands: ${otherCoin}.` : "",
295095
295572
  !hire && m.prize < m.entryEach ? `The prize (${m.prize} ${coinSymbol}) is less than one entry earns (${m.entryEach} ${coinSymbol}).` : "",
@@ -295097,29 +295574,32 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
295097
295574
  draft.fields.access === "apply" ? "By application: people apply on the call's page or with apply_to_call; you say yes with decide_application before they can enter." : "",
295098
295575
  held !== null && held.reward < plan.totalBase ? `The account holds ${holds}; the call locks ${call.payWords(plan.totalBase, coinSymbol)}. It will be refused before anything moves.` : "",
295099
295576
  held !== null && feeTotal > 0 && held.fee < feeTotal ? `The account holds ${feeWords(held.fee)}; the reviews need ${feeWords(feeTotal)}. It will be refused before anything moves; top_up buys PAY.` : "",
295100
- acting?.cap && !otherCoin && Number(acting.cap.remainingBase) < plan.totalBase ? `Their cap has ${call.payWords(Number(acting.cap.remainingBase), coinSymbol)} left; the call locks ${call.payWords(plan.totalBase, coinSymbol)}. They raise it at ${SITE}/connect.` : "",
295101
- !acting ? `Not connected: post_call refuses until the person connects this key (connect_account gives them the link). For a rehearsal, plain_key: true posts as this key itself, ${await address2()}, from its own money.` : ""
295577
+ acting?.cap && !otherCoin && Number(capIn2(acting, coin)?.remainingBase ?? 0) < plan.totalBase ? `Their cap has ${call.payWords(Number(capIn2(acting, coin)?.remainingBase ?? 0), coinSymbol)} left; the call locks ${call.payWords(plan.totalBase, coinSymbol)}. They raise it at ${SITE}/connect.` : "",
295578
+ keyOnly ? `Not connected, and a connection would not carry this call: ${call.callTwoCoins}. With plain_key: true it posts as this key itself, ${await address2()}, from its own money.` : !acting ? `Not connected: post_call refuses until the person connects this key (connect_account gives them the link). For a rehearsal, plain_key: true posts as this key itself, ${await address2()}, from its own money.` : ""
295102
295579
  ].filter(Boolean);
295103
295580
  out.push(
295104
295581
  "",
295105
- "Ready. This is the call as an entrant's AI will read it:",
295582
+ blocked ? "Drafted, but post_call would refuse it as it stands (the warnings say why). This is the call as an entrant's AI will read it:" : "Ready. This is the call as an entrant's AI will read it:",
295106
295583
  "",
295107
295584
  call.callReadout(preview, { id: "(the entries ask, once posted)", symbol: coinSymbol }),
295108
295585
  "",
295109
295586
  "What each leg locks on chain:",
295110
295587
  ...legRows,
295111
- ` Total ${callLocks(plan.totalBase, feeTotal)}`,
295588
+ ` Total ${callLocks(plan.totalBase, feeTotal, coinSymbol)}`,
295589
+ ...plan.share ? [
295590
+ ` Network's share ${call.payWords(shareTotal, coinSymbol)} of that, if every spot is paid (${shareWords2(plan.share, "today")}): the chain splits it off each approval and pays the rest to the person. A call by invitation or application is a hire; the network keeps its share of what each hired person earns. Asks and public calls carry no share.`
295591
+ ] : [],
295112
295592
  "",
295113
- `${actingNote(acting) ?? `Not connected; with plain_key this key posts as itself, ${await address2()}.`} The account holds ${holds ?? "an amount nobody could read"}${held !== null && feeTotal > 0 ? ` and ${feeWords(held.fee)}` : ""}; this call locks ${callLocks(plan.totalBase, feeTotal)}.`,
295593
+ `${actingNote(acting) ?? `Not connected; with plain_key this key posts as itself, ${await address2()}.`} The account holds ${holds ?? "an amount nobody could read"}${held !== null && feeTotal > 0 ? ` and ${feeWords(held.fee)}` : ""}; this call locks ${callLocks(plan.totalBase, feeTotal, coinSymbol)}.`,
295114
295594
  hire ? `The clock: entries close ${new Date(plan.dates.closes).toUTCString()}; every passing entry is paid as it is judged, and nothing waits for a pick.` : `The clock: entries close ${new Date(plan.dates.closes).toUTCString()}; the shortlist shows ${new Date(plan.dates.drop).toUTCString()}; the pick by ${new Date(plan.dates.pick).toUTCString()}.`,
295115
295595
  `Who may enter: ${draft.fields.access === "invite" ? `by invitation only${draft.fields.invite?.length ? ` \u2014 ${draft.fields.invite.length} to invite when the entries post${draft.fields.inviteNote ? ", each with your private note" : ""}` : ""}` : draft.fields.access === "apply" ? "by application, listed on the board" : "anyone, listed on the board"}.`,
295116
295596
  ...draft.fields.kind === "review" ? [
295117
- `A review: every entry is a review report with screenshots (submit_work's report and files), checked by the AI on the lines${acting ? `, then ${acting.principal} gives final approval before anyone is paid` : "; posted from this key itself, the AI decides alone and passing reports are paid as they are judged"}.`,
295118
- `Its page: ${EXPERT_SITE}/expert/reviews/${slug}`
295597
+ `A review: every entry is a review report with screenshots (submit_work's report and files), checked by ${checker.name ?? "the AI"} on the lines${acting ? `, then ${acting.principal} gives final approval before anyone is paid` : `; posted from this key itself, ${checker.name ?? "the AI"} decides alone and passing reports are paid as they are judged`}.`,
295598
+ draft.posted.entries ? `Its page: ${EXPERT_SITE}/expert/reviews/${draft.posted.entries.id}` : "Its page is named by the review's ask number, given once it posts."
295119
295599
  ] : [],
295120
295600
  ...warnings2.length ? ["", "Warnings:", ...warnings2.map((one) => ` - ${one}`)] : [],
295121
295601
  "",
295122
- `Show this to the person and get a yes. Then: post_call slug: "${slug}", confirm: "${call.payWords(plan.totalBase, coinSymbol)}"${acting ? "" : ", plain_key: true \u2014 or connect_account first and leave plain_key out, to post as the person"}. One leg per call; call again until it says everything is posted.`
295602
+ blocked ? "Do not ask the person for a yes on these terms: post_call would refuse them. Fix what the warnings name (through draft_call), then preview again." : `Show this to the person and get a yes. Then: post_call slug: "${slug}", confirm: "${call.payWords(plan.totalBase, coinSymbol)}"${acting ? "" : keyOnly ? ", plain_key: true (a connected person cannot post this call; the warnings say why)" : ", plain_key: true \u2014 or connect_account first and leave plain_key out, to post as the person"}. One leg per call; call again until it says everything is posted.`
295123
295603
  );
295124
295604
  return text4(out.join("\n"));
295125
295605
  }
@@ -295141,16 +295621,17 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
295141
295621
  const found = call.readDraft(slug);
295142
295622
  if (!found) return text4(`No draft called "${slug}". Start one with draft_call.`);
295143
295623
  const review = found.fields.kind === "review";
295144
- const link5 = review ? `${EXPERT_SITE}/expert/reviews/${slug}` : `${SITE}/opencall/${slug}`;
295624
+ const checker = await callChecker2(review);
295625
+ const linkOf = (posted2) => review ? `${EXPERT_SITE}/expert/reviews/${posted2.entries?.id ?? slug}` : `${SITE}/opencall/${slug}`;
295145
295626
  const settled = found.inFlight ? await call.settleInFlight(slug, call.indexLookup) : null;
295146
295627
  if (settled?.state === "pending") return text4(settled.words);
295147
295628
  if (settled?.state === "landed") {
295148
- nudge("hire", settled.id);
295629
+ nudge("hire", settled.id, checker.at);
295149
295630
  const after2 = call.nextLeg(settled.draft);
295150
295631
  return text4(
295151
295632
  [
295152
295633
  `The earlier post landed: ${LEG_WORDS[settled.leg]} is #${settled.id}. It is recorded, and nothing was posted twice.`,
295153
- after2 ? `Call post_call again for ${LEG_WORDS[after2]}.` : await liveLine(settled.id, link5)
295634
+ after2 ? `Call post_call again for ${LEG_WORDS[after2]}.` : await liveLine(settled.id, linkOf(settled.draft.posted))
295154
295635
  ].join("\n")
295155
295636
  );
295156
295637
  }
@@ -295162,11 +295643,11 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
295162
295643
  const next = call.nextLeg(draft);
295163
295644
  const ids = ["prize", "runnersUp", "entries"].flatMap((leg2) => draft.posted[leg2] ? [`${LEG_WORDS[leg2]} #${draft.posted[leg2].id}`] : []);
295164
295645
  if (!next) return text4(`Everything is posted: ${ids.join(", ")}.
295165
- The call: ${link5}`);
295646
+ The call: ${linkOf(draft.posted)}`);
295166
295647
  if (!call.isReady(draft.fields)) return text4(`"${slug}" is not ready: ${call.missingOf(draft.fields, { symbol: coinSymbol, holds: null }).map((one) => one.field).join(", ")} still missing. Finish it with draft_call.`);
295167
295648
  const cannot = await unjudged();
295168
295649
  if (cannot) return text4(cannot);
295169
- if (!CHECKER) return text4("No oracle is configured on this deployment, so nothing could judge the entries. Refusing rather than posting a call nobody can settle.");
295650
+ if (!checker.address) return text4("No oracle is configured on this deployment, so nothing could judge the entries. Refusing rather than posting a call nobody can settle.");
295170
295651
  const acting = plain_key && review ? null : await principal2();
295171
295652
  if (!acting && !plain_key) {
295172
295653
  return text4(`Not connected. A call uses the connected account as its poster and funding source. Ask them to connect this key first:
@@ -295175,8 +295656,12 @@ Then call post_call again. Nothing was posted.`);
295175
295656
  }
295176
295657
  const { checkerLegBase: checkerLegBase2 } = await Promise.resolve().then(() => (init_leg(), leg_exports));
295177
295658
  const poster = acting?.principal ?? await address2();
295178
- const perCheck = await callPerCheck();
295179
- const plan = call.callPlan(draft, { poster: `${poster.slice(0, 10)}\u2026${poster.slice(-4)}`, checkerLegBase: (terms) => perCheck ? 0 : checkerLegBase2(FEE_DENOM, terms, CHECK_REWARD), oracle: CHECKER });
295659
+ if (checker.fee && checker.fee.denom !== coin) return text4(`Refused: ${checker.name} is paid in ${symbolOf(checker.fee.denom)}, so a review pays in it too: pass currency "${symbolOf(checker.fee.denom)}". Nothing was posted.`);
295660
+ const perCheck = checker.fee ? false : await callPerCheck(checker.address);
295661
+ const split3 = call.callShares(draft.fields) ? await callSplit() : null;
295662
+ const priced = call.callShare(draft.fields, split3);
295663
+ if ("unread" in priced) return text4(call.SHARE_UNREAD);
295664
+ const plan = call.callPlan(draft, { poster: `${poster.slice(0, 10)}\u2026${poster.slice(-4)}`, checkerLegBase: (terms) => checker.fee ? checker.fee.base : perCheck ? 0 : checkerLegBase2(FEE_DENOM, terms, CHECK_REWARD), oracle: checker.address, share: priced.share, feeInReward: Boolean(checker.fee) });
295180
295665
  const feeTotal = plan.legs.reduce((sum, one) => sum + one.feeBase, 0);
295181
295666
  const total = call.payWords(plan.totalBase, coinSymbol);
295182
295667
  if (acting && feeTotal > 0) return text4(`Refused: ${call.callTwoCoins}.${review ? ` ${PLAIN_REVIEW}` : ""} Nothing was posted.`);
@@ -295188,19 +295673,24 @@ Then call post_call again. Nothing was posted.`);
295188
295673
  const leg = plan.legs.find((one) => one.leg === next);
295189
295674
  const order2 = plan.legs.map((one) => one.leg);
295190
295675
  if (!confirm) {
295676
+ const { shareWords: shareWords2 } = await Promise.resolve().then(() => (init_callRead(), callRead_exports));
295677
+ const shareTotal = plan.legs.reduce((sum, one) => sum + one.shareBase * one.escrowSpots, 0);
295191
295678
  return text4(
295192
295679
  [
295193
- `Call "${slug}" locks ${callLocks(plan.totalBase, feeTotal)} in ${order2.length} asks, posted one per call: ${order2.map((one) => LEG_WORDS[one]).join(", then ")}.${ids.length ? ` Posted already: ${ids.join(", ")}.` : ""}`,
295194
- `Next: ${LEG_WORDS[next]}, locking ${callLocks(leg.escrowBase, leg.feeBase)}, plus a little IXO for gas.`,
295680
+ `Call "${slug}" locks ${callLocks(plan.totalBase, feeTotal, coinSymbol)} in ${order2.length} asks, posted one per call: ${order2.map((one) => LEG_WORDS[one]).join(", then ")}.${ids.length ? ` Posted already: ${ids.join(", ")}.` : ""}`,
295681
+ /* A private call: the share is in that total, on top of what each person receives, and said before the yes. */
295682
+ ...plan.share ? [`Of that, the network's share is ${call.payWords(shareTotal, coinSymbol)} if every spot is paid (${shareWords2(plan.share, "today")}), on top of what each person receives: ${call.payWords(plan.legs.find((one) => one.leg === "entries").payBase, coinSymbol)} for each passing entry.`] : [],
295683
+ `Next: ${LEG_WORDS[next]}, locking ${callLocks(leg.escrowBase, leg.feeBase, coinSymbol)}, plus a little IXO for gas.`,
295195
295684
  actingNote(acting) ?? (review ? "Posting from your own wallet (this key), not the person's account; the AI decides each report alone, with no final OK from the person." : `Posting as this key, ${poster}.`),
295196
295685
  `Spent today (call): ${spentWords("call")}. Every tool together: ${spentAllWords()}.`,
295686
+ ...call.capWords(plan.legs.filter((one) => !draft.posted[one.leg]), CAP_BASE, coinSymbol),
295197
295687
  "",
295198
295688
  `To go ahead, call again with confirm: "${total}".`
295199
295689
  ].join("\n")
295200
295690
  );
295201
295691
  }
295202
295692
  if (!call.confirms(confirm, plan.totalBase, coinSymbol)) return text4(`Refused: confirm said "${confirm}" but the call locks ${total}. Preview it again.`);
295203
- if (leg.escrowBase / MICRO4 > MAX_PER_ASK) return text4(`Refused: this leg locks ${call.payWords(leg.escrowBase, coinSymbol)}, over this server's ${face(MAX_PER_ASK, coinSymbol)} per-ask cap (OPENHANDS_MAX_PER_ASK_USD).`);
295693
+ if (call.overCap(leg, CAP_BASE)) return text4(`Refused: this leg locks ${call.payWords(leg.escrowBase, coinSymbol)}, over this server's ${face(MAX_PER_ASK, coinSymbol)} per-ask cap (OPENHANDS_MAX_PER_ASK_USD).`);
295204
295694
  if (ids.length === 0) {
295205
295695
  const taken = await fetch2(`${SITE}/api/board?campaign=${encodeURIComponent(call.tagOf(draft))}&size=5`, { signal: AbortSignal.timeout(15e3) }).then((response) => response.ok ? response.json() : null).catch(() => null);
295206
295696
  if (taken?.rows?.length) return text4(`Refused: "${slug}" already names a call with open asks on this board. Draft it under another slug with draft_call. Nothing was posted.`);
@@ -295214,6 +295704,7 @@ Then call post_call again. Nothing was posted.`);
295214
295704
  balanceBase: async (who, denom) => Number((await balancesOfStrict2(who))[denom] ?? 0),
295215
295705
  face: (base5) => call.payWords(base5, coinSymbol),
295216
295706
  feeFace: feeWords,
295707
+ coinFixed: Boolean(checker.fee),
295217
295708
  site: SITE
295218
295709
  });
295219
295710
  if (refusedBefore) return text4(refusedBefore);
@@ -295225,15 +295716,17 @@ Then call post_call again. Nothing was posted.`);
295225
295716
  posted = await call.postNextLeg(slug, {
295226
295717
  site: SITE,
295227
295718
  askDenom: coin,
295228
- checker: CHECKER,
295719
+ checker: checker.address,
295229
295720
  gate: GATE,
295230
295721
  checkReward: CHECK_REWARD,
295231
295722
  perCheck,
295723
+ fee: checker.fee,
295232
295724
  acting,
295233
295725
  approver,
295234
295726
  mnemonic: key.mnemonic,
295235
- nudge,
295236
- charge: (escrowBase) => chargeDay("call", escrowBase / MICRO4)
295727
+ nudge: (event, id) => nudge(event, id, checker.at),
295728
+ charge: (escrowBase) => chargeDay("call", escrowBase / MICRO4),
295729
+ feeSplit: split3
295237
295730
  });
295238
295731
  } catch (error54) {
295239
295732
  if (error54 instanceof call.InFlightError || error54 instanceof call.PostRefused) return text4(error54.message);
@@ -295246,7 +295739,7 @@ Then call post_call again. Nothing was posted.`);
295246
295739
  );
295247
295740
  }
295248
295741
  if ("done" in posted) return text4(`Everything is posted.
295249
- The call: ${link5}`);
295742
+ The call: ${linkOf(posted.draft.posted)}`);
295250
295743
  const done = order2.filter((one) => posted.draft.posted[one]).length;
295251
295744
  const after = call.nextLeg(posted.draft);
295252
295745
  const hire = posted.draft.fields.mode === "hire";
@@ -295257,10 +295750,10 @@ The call: ${link5}`);
295257
295750
  `${done} of ${order2.length} posted: ${LEG_WORDS[posted.leg]}, #${posted.id}${acting ? `, as ${acting.principal}` : ""}, locking ${call.payWords(posted.plan.escrowBase, coinSymbol)}.`,
295258
295751
  held ?? "",
295259
295752
  after ? `Call post_call again for ${LEG_WORDS[after]}.` : [
295260
- held ? `The call is posted but not yet open: ${link5} says so until a person clears the entries. Once it is open:` : await liveLine(posted.id, link5),
295753
+ held ? `The call is posted but not yet open: ${linkOf(posted.draft.posted)} says so until a person clears the entries. Once it is open:` : await liveLine(posted.id, linkOf(posted.draft.posted)),
295261
295754
  posted.draft.fields.access === "invite" ? `By invitation: only the people on its list can enter. Entrants' AI read it with read_ask "${posted.id}" and enter with submit_work (a link goes in submit_work's invite).` : posted.draft.fields.access === "apply" ? `By application: people apply on the page or with apply_to_call; say yes with decide_application. Entrants' AI read it with read_ask "${posted.id}" and enter with submit_work once admitted.` : `Entrants' AI read it with read_ask "${posted.id}" and enter with submit_work.`,
295262
- review ? approver ? `Every report is checked by the AI on the lines, then waits for your OK, which pays it. Switch your checks on now:
295263
- ${allowLine(SITE, posted.id, approver, acting?.principal ?? null)}` : "Every report that passes the lines is paid as the AI rules; no final OK is needed." : hire ? "Every entry that passes is paid as the jury rules; there is no pick." : `The pick is yours: approve the picked entry's claim on ${originOf(NETWORK2)}/t/${posted.draft.posted.prize?.id ?? ""}.`
295755
+ review ? approver ? `Every report is checked by ${checker.name ?? "the AI"} on the lines, then waits for your OK, which pays it. Switch your checks on now:
295756
+ ${allowLine(SITE, posted.id, approver, acting?.principal ?? null)}` : `Every report that passes the lines is paid as ${checker.name ?? "the AI"} rules; no final OK is needed.` : hire ? "Every entry that passes is paid as the jury rules; there is no pick." : `The pick is yours: approve the picked entry's claim on ${originOf(NETWORK2)}/t/${posted.draft.posted.prize?.id ?? ""}.`
295264
295757
  ].join("\n"),
295265
295758
  ...invited ? ["", ...invited] : []
295266
295759
  ].filter(Boolean).join("\n")
@@ -295328,6 +295821,15 @@ ${allowLine(SITE, posted.id, approver, acting?.principal ?? null)}` : "Every rep
295328
295821
  ].filter(Boolean);
295329
295822
  }
295330
295823
  const isReview = async (draft, entriesId) => draft ? draft.fields.kind === "review" : isReviewTitle((await askReceipt(entriesId))?.entityName);
295824
+ const termsDoor = async (collectionId) => {
295825
+ const receipt = await askReceipt(collectionId);
295826
+ if (receipt?.access) return receipt.access;
295827
+ if (!receipt?.doneMeansCid) return null;
295828
+ const { readDocJson: readDocJson2 } = await Promise.resolve().then(() => (init_docs(), docs_exports));
295829
+ const terms = await readDocJson2(receipt.doneMeansCid, { timeoutMs: 15e3, at: receipt.doneMeansUrl ?? void 0 });
295830
+ if (!terms) return null;
295831
+ return terms.access === "apply" || terms.access === "invite" ? terms.access : "public";
295832
+ };
295331
295833
  if (offered("invite_to_call")) server2.registerTool(
295332
295834
  "invite_to_call",
295333
295835
  {
@@ -295389,22 +295891,25 @@ ${allowLine(SITE, posted.id, approver, acting?.principal ?? null)}` : "Every rep
295389
295891
  const acting = await principal2();
295390
295892
  const answer = await invitesModule.readInvites(SITE, await inviteSigner(acting), entriesId);
295391
295893
  if ("refused" in answer) return text4(`Refused: ${answer.refused}`);
295392
- const review = await isReview(draft, entriesId);
295393
- const links = [...draft?.invites ?? [], ...callModule.readAskInvites(entriesId)].filter((one) => one.via === "link" && one.collectionId === entriesId && one.token);
295894
+ const fromHere = draft ?? callModule.draftOfEntries(entriesId);
295895
+ const review = await isReview(fromHere, entriesId);
295896
+ const links = [...fromHere?.invites ?? [], ...callModule.readAskInvites(entriesId)].filter((one) => one.via === "link" && one.collectionId === entriesId && one.token);
295394
295897
  const opened = new Set(answer.invites.filter((one) => one.via === "link" && one.bound_at).map((one) => one.id));
295395
295898
  const rows2 = answer.invites.map(
295396
295899
  (one) => ` ${one.revoked_at ? "(revoked) " : ""}${one.kind === "exclude" ? "turned away" : one.via}${one.handle ? ` @${one.handle}` : ""}${one.address ? ` ${one.address}` : one.via === "link" ? ` link #${one.id}, ${one.bound_at ? `opened ${one.bound_at}` : "not opened yet"}` : ""}${one.note ? ` \u2014 ${one.note}` : ""}`
295397
295900
  );
295398
295901
  const applications = answer.applications.map((one) => ` ${one.address}${one.note ? ` \u2014 ${one.note}` : ""} \xB7 ${one.decision ? `${one.decision} (${one.decided_at})` : "waiting for your decision"}`);
295399
- const door = answer.access ?? (draft?.fields.access && draft.fields.access !== "public" ? draft.fields.access : null);
295902
+ const drafted = fromHere?.fields.access ?? null;
295903
+ const terms = answer.access || drafted ? null : await termsDoor(entriesId);
295904
+ const door = answer.access ?? drafted ?? terms;
295400
295905
  return text4(
295401
295906
  [
295402
- `#${entriesId} \xB7 ${door === "invite" ? "by invitation" : door === "apply" ? "by application" : "open to anyone"}${!answer.access && door ? " (the board reads the terms within a minute; the list already gates)" : ""}`,
295907
+ invitesModule.doorLine(entriesId, { worker: answer.access, draft: drafted, terms }),
295403
295908
  rows2.length ? "The list:" : "The list is empty.",
295404
295909
  ...rows2,
295405
295910
  links.length ? "Links kept here (each for one person):" : "",
295406
295911
  ...links.map((one) => ` ${inviteLink(SITE, one.token, review)}${one.id !== void 0 && opened.has(one.id) ? " (opened)" : ""}`),
295407
- applications.length ? "Applications:" : answer.access === "apply" ? "No applications yet." : "",
295912
+ applications.length ? "Applications:" : door === "apply" ? "No applications yet." : "",
295408
295913
  ...applications
295409
295914
  ].filter(Boolean).join("\n")
295410
295915
  );
@@ -295878,7 +296383,7 @@ Open the profile and the evidence. Decline with decide_check. Approving happens
295878
296383
  if (screen?.verdict === "hold") held.push(`${check2.id}: ${heldWords(screen.reason)}`);
295879
296384
  }
295880
296385
  const { cost, reviewsBase } = await priceOf2(plan, world, coin);
295881
- const overCap = plans.planCapRefusal(plan, cost, PLAN_CAPS, money2);
296386
+ const overCap2 = plans.planCapRefusal(plan, cost, PLAN_CAPS, money2);
295882
296387
  const published = await participant.publishJson(plan, "market-plan");
295883
296388
  if (!published) return text4("The store would not take the plan, and a plan nobody can read back is not worth funding. Try again shortly.");
295884
296389
  const longest = Math.max(...plan.checks.map((check2) => check2.hours));
@@ -295895,7 +296400,7 @@ Open the profile and the evidence. Decline with decide_check. Approving happens
295895
296400
  ...held.length ? ["", ...held] : [],
295896
296401
  "",
295897
296402
  `Stored, frozen: ${published.cid}`,
295898
- overCap ? `This cannot be funded here: ${overCap} Lower the pay, fewer answers per place or fewer places, or a higher cap set by the person who runs this server.` : PLANS ? `To post it once they approve: fund_plan plan: "${published.cid}". It previews first; nothing is posted without confirm.` : "This deployment runs no plans, so this plan can be read but not bought.",
296403
+ overCap2 ? `This cannot be funded here: ${overCap2} Lower the pay, fewer answers per place or fewer places, or a higher cap set by the person who runs this server.` : PLANS ? `To post it once they approve: fund_plan plan: "${published.cid}". It previews first; nothing is posted without confirm.` : "This deployment runs no plans, so this plan can be read but not bought.",
295899
296404
  "",
295900
296405
  plans.APPROVAL_STEP
295901
296406
  ].join("\n")
@@ -295929,8 +296434,8 @@ Open the profile and the evidence. Decline with decide_check. Approving happens
295929
296434
  const world = await askTheWorldPriced();
295930
296435
  if (!world) return text4(UNPRICED);
295931
296436
  const { cost, reviewsBase } = await priceOf2(doc, world, coin);
295932
- const overCap = plans.planCapRefusal(doc, cost, PLAN_CAPS, money2);
295933
- if (overCap) return text4(`Refused: ${overCap} Nothing was posted.`);
296437
+ const overCap2 = plans.planCapRefusal(doc, cost, PLAN_CAPS, money2);
296438
+ if (overCap2) return text4(`Refused: ${overCap2} Nothing was posted.`);
295934
296439
  const acting = await principal2();
295935
296440
  const poster = acting?.principal ?? await address2();
295936
296441
  const started = planPost.readProgress(doc.slug);