@ixo/asks-mcp 2.0.0-dev.2 → 2.0.0-dev.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/asks-mcp.mjs +264 -99
  2. package/package.json +1 -1
package/dist/asks-mcp.mjs CHANGED
@@ -7508,7 +7508,7 @@ function reviewSlugOf(facts) {
7508
7508
  function askPage(site, collectionId, facts) {
7509
7509
  const base6 = site.replace(/\/+$/, "");
7510
7510
  const review = reviewSlugOf(facts);
7511
- if (review) return `${base6}/pro/reviews/${collectionId}`;
7511
+ if (review) return `${base6}/me/pro-asks/${collectionId}`;
7512
7512
  const slug = facts.slug ?? (facts.campaign ? slugOfTag(facts.campaign) : null);
7513
7513
  return slug ? `${base6}/opencall/${slug}` : `${base6}/t/${collectionId}`;
7514
7514
  }
@@ -7518,7 +7518,10 @@ function answerPage(site, collectionId, claimId, facts) {
7518
7518
  return review ? `${base6}/pro/reviews/${collectionId}/report/${encodeURIComponent(claimId)}` : `${base6}/r/${collectionId}/trace/${encodeURIComponent(claimId)}`;
7519
7519
  }
7520
7520
  function inviteLink(site, token, review) {
7521
- return review ? `${site.replace(/\/+$/, "")}/pro/i/${token}` : `${site.replace(/\/+$/, "")}/opencall/i/${token}`;
7521
+ return review ? `${site.replace(/\/+$/, "")}/i/${token}` : `${site.replace(/\/+$/, "")}/opencall/i/${token}`;
7522
+ }
7523
+ function sitePath(path) {
7524
+ return path.replace(/^\/pro\/reviews\/([^/?#]+)\/with\//, "/me/pro-asks/$1/with/").replace(/^\/pro\/reviews\/([^/?#]+)$/, "/me/pro-asks/$1").replace(/^\/pro\/work(?=\/|$)/, "/me/work").replace(/^\/pro\/i\//, "/i/").replace(/^\/pro\/pros(?=\/|$)/, "/pros");
7522
7525
  }
7523
7526
  var isReviewTitle, proPage, roomPage;
7524
7527
  var init_world = __esm({
@@ -7526,8 +7529,8 @@ var init_world = __esm({
7526
7529
  "use strict";
7527
7530
  init_callTag();
7528
7531
  isReviewTitle = (title) => /^Expert review:/.test(title ?? "");
7529
- proPage = (site, handle2) => `${site.replace(/\/+$/, "")}/pro/pros/${handle2}`;
7530
- roomPage = (site, collectionId, pro) => `${site.replace(/\/+$/, "")}/pro/reviews/${collectionId}/with/${encodeURIComponent(pro.replace(/^@/, ""))}`;
7532
+ proPage = (site, handle2) => `${site.replace(/\/+$/, "")}/pros/${handle2}`;
7533
+ roomPage = (site, collectionId, pro) => `${site.replace(/\/+$/, "")}/me/pro-asks/${collectionId}/with/${encodeURIComponent(pro.replace(/^@/, ""))}`;
7531
7534
  }
7532
7535
  });
7533
7536
 
@@ -9812,6 +9815,13 @@ var init_gateMet = __esm({
9812
9815
  });
9813
9816
 
9814
9817
  // ../../packages/openhands-sdk/src/data/engineUrl.ts
9818
+ var engineUrl_exports = {};
9819
+ __export(engineUrl_exports, {
9820
+ engineConfigured: () => engineConfigured,
9821
+ engineIssuerKeysUrl: () => engineIssuerKeysUrl,
9822
+ engineReceiptUrl: () => engineReceiptUrl,
9823
+ engineUrl: () => engineUrl
9824
+ });
9815
9825
  var engineUrl, engineConfigured, engineReceiptUrl, engineIssuerKeysUrl;
9816
9826
  var init_engineUrl = __esm({
9817
9827
  "../../packages/openhands-sdk/src/data/engineUrl.ts"() {
@@ -29605,8 +29615,8 @@ function fileAnswers(files, urlOf = ipfsDocUrl) {
29605
29615
  };
29606
29616
  }
29607
29617
  function extensionOf(type) {
29608
- const bare = (type ?? "").toLowerCase().split(";")[0]?.trim() ?? "";
29609
- switch (bare) {
29618
+ const bare2 = (type ?? "").toLowerCase().split(";")[0]?.trim() ?? "";
29619
+ switch (bare2) {
29610
29620
  case "image/jpeg":
29611
29621
  return ".jpg";
29612
29622
  case "image/png":
@@ -259526,10 +259536,10 @@ __export(connect_exports, {
259526
259536
  workOf: () => workOf
259527
259537
  });
259528
259538
  function noteIgnored(agent, pinned, others, log) {
259529
- const fresh2 = others.filter((one) => !told.has(`${agent}:${pinned}:${one}`));
259530
- if (!fresh2.length) return;
259531
- for (const one of fresh2) told.add(`${agent}:${pinned}:${one}`);
259532
- log(`asks-mcp: ignoring ${fresh2.length === 1 ? "a connection" : "connections"} to this key from ${fresh2.join(", ")}: it is pinned to ${pinned === SELF ? "itself" : pinned}, and only connect_account changes that.`);
259539
+ const fresh3 = others.filter((one) => !told.has(`${agent}:${pinned}:${one}`));
259540
+ if (!fresh3.length) return;
259541
+ for (const one of fresh3) told.add(`${agent}:${pinned}:${one}`);
259542
+ log(`asks-mcp: ignoring ${fresh3.length === 1 ? "a connection" : "connections"} to this key from ${fresh3.join(", ")}: it is pinned to ${pinned === SELF ? "itself" : pinned}, and only connect_account changes that.`);
259533
259543
  }
259534
259544
  async function actingFor(agent, reads2 = chainReads, pins2 = keyPins) {
259535
259545
  const pinned = pinOf(agent, pins2);
@@ -260179,17 +260189,17 @@ function laneStore(deps) {
260179
260189
  const bearer = (can) => laneInvocation({ agent: deps.agent, pass: deps.pass, entityDid: deps.entityDid, collectionId: deps.collectionId, can });
260180
260190
  async function put(bytes3, mediaType, name8) {
260181
260191
  const deadline = Date.now() + (deps.settleMs ?? 6e4);
260182
- let fresh2 = false;
260192
+ let fresh3 = false;
260183
260193
  for (let pause2 = 2e3; ; pause2 = Math.min(pause2 * 2, 8e3)) {
260184
260194
  try {
260185
- return await uploadToLane(bytes3, mediaType, { bearer: await bearer("fs/write"), entityDid: deps.entityDid, collectionId: deps.collectionId, name: laneName(deps.person, bytes3, name8, fresh2) });
260195
+ return await uploadToLane(bytes3, mediaType, { bearer: await bearer("fs/write"), entityDid: deps.entityDid, collectionId: deps.collectionId, name: laneName(deps.person, bytes3, name8, fresh3) });
260186
260196
  } catch (error54) {
260187
- if (error54 instanceof LaneRefused && error54.status === 409 && !fresh2) {
260197
+ if (error54 instanceof LaneRefused && error54.status === 409 && !fresh3) {
260188
260198
  if (bytes3.byteLength <= RAW_CID_MAX) {
260189
260199
  const cid = cidOfBytes(bytes3);
260190
260200
  return { cid, url: laneCidUrl(cid) };
260191
260201
  }
260192
- fresh2 = true;
260202
+ fresh3 = true;
260193
260203
  continue;
260194
260204
  }
260195
260205
  if (!passing(error54) || Date.now() + pause2 > deadline) return null;
@@ -260266,7 +260276,7 @@ ${shareLink(site, collectionId, approver)}` : "";
260266
260276
  BOX_TTL_MS = 10 * 6e4;
260267
260277
  newBoxId = () => randomBytes5(16).toString("hex");
260268
260278
  safeName = (name8) => name8.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^[.-]+/, "").slice(-80) || "file";
260269
- laneName = (person, bytes3, name8, fresh2 = false) => `${person}/${createHash5("sha256").update(bytes3).digest("hex").slice(0, 24)}${fresh2 ? `-${randomBytes5(3).toString("hex")}` : ""}-${safeName(name8)}`;
260279
+ laneName = (person, bytes3, name8, fresh3 = false) => `${person}/${createHash5("sha256").update(bytes3).digest("hex").slice(0, 24)}${fresh3 ? `-${randomBytes5(3).toString("hex")}` : ""}-${safeName(name8)}`;
260270
260280
  RAW_CID_MAX = 1024 * 1024;
260271
260281
  passing = (error54) => !(error54 instanceof LaneRefused) || error54.status === 403 || error54.status === 429 || error54.status >= 500;
260272
260282
  ENVELOPE_LIMIT = 200 * 1024;
@@ -261821,8 +261831,8 @@ function senderFor(deps) {
261821
261831
  let signerData = deps.signerData ? await deps.signerData() : void 0;
261822
261832
  let fee = "auto";
261823
261833
  try {
261824
- const simulated = await simulateWithChainSequence({ client: client2, address: address2, messages, memo, signerData, warn, onCorrected: (fresh2) => {
261825
- signerData = fresh2;
261834
+ const simulated = await simulateWithChainSequence({ client: client2, address: address2, messages, memo, signerData, warn, onCorrected: (fresh3) => {
261835
+ signerData = fresh3;
261826
261836
  } });
261827
261837
  const priced = (0, import_stargate7.calculateFee)(Math.round(simulated.gas * 1.8), GAS_PRICE2);
261828
261838
  const granter = await deps.granterFor(messages.map((one) => one.typeUrl), Number(priced.amount[0]?.amount ?? 0));
@@ -263676,7 +263686,9 @@ var init_callTerms = __esm({
263676
263686
  var feeLine_exports = {};
263677
263687
  __export(feeLine_exports, {
263678
263688
  MAINNET_SPLIT: () => MAINNET_SPLIT,
263679
- feeLine: () => feeLine
263689
+ feeLine: () => feeLine,
263690
+ feeSentence: () => feeSentence,
263691
+ feeWords: () => feeWords
263680
263692
  });
263681
263693
  function feeLine(args) {
263682
263694
  const { split: split3, symbol: symbol2 } = args;
@@ -263684,12 +263696,12 @@ function feeLine(args) {
263684
263696
  const receives = `Each pro receives ${amountWords(args.net, symbol2)}.`;
263685
263697
  const locked2 = `${amountWords(leg / 1e6, symbol2)} is locked per report`;
263686
263698
  if (split3.network + split3.node === 0) {
263687
- if (args.network === "mainnet") return `${receives} Nothing is taken on top: ${locked2}.`;
263688
- return `${receives} In the sandbox nothing is added: ${amountWords(leg / 1e6, symbol2)} is held per report; on the real network the fee is ${percentWords(MAINNET_SPLIT.network + MAINNET_SPLIT.node)} on top.`;
263699
+ if (args.network === "mainnet") return `${receives} Nothing is taken: ${locked2}.`;
263700
+ return `${receives} In the sandbox nothing is added: ${amountWords(leg / 1e6, symbol2)} is held per report. On the real network, ${feeWords(MAINNET_SPLIT)}`;
263689
263701
  }
263690
- return `${receives} ${locked2}: ${percentWords(split3.network + split3.node)} on top of what the pro receives.`;
263702
+ return `${receives} ${locked2}. ${feeWords(split3)}`;
263691
263703
  }
263692
- var MAINNET_SPLIT, percentWords, amountWords;
263704
+ var MAINNET_SPLIT, percentWords, feeWords, feeSentence, amountWords;
263693
263705
  var init_feeLine = __esm({
263694
263706
  "../../packages/openhands-sdk/src/rubric/feeLine.ts"() {
263695
263707
  "use strict";
@@ -263697,6 +263709,8 @@ var init_feeLine = __esm({
263697
263709
  init_share();
263698
263710
  MAINNET_SPLIT = { network: 10, node: 10 };
263699
263711
  percentWords = (value) => `${Number(value.toFixed(6))} %`;
263712
+ feeWords = (split3, who = "The pro") => `${percentWords(split3.network + split3.node)} of what you pay runs asks.world. ${who} gets the rest.`;
263713
+ feeSentence = (split3, network2, who = "The pro") => split3.network + split3.node === 0 && network2 !== "mainnet" ? `In the sandbox nothing is added. On the real network, ${feeWords(MAINNET_SPLIT, who)}` : feeWords(split3, who);
263700
263714
  amountWords = (value, symbol2) => ["USDC", "USD", "PAY", "$"].includes(symbol2) ? dollars(value) : `${Number(value.toFixed(6))} ${symbol2}`;
263701
263715
  }
263702
263716
  });
@@ -263763,11 +263777,12 @@ function callReadout(terms, ctx) {
263763
263777
  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.";
263764
263778
  const review = call3.kind === "review";
263765
263779
  const shared = call3.share ? sharedPay(m.entryEach, call3.share) : null;
263766
- const shareLine2 = shared && call3.share ? `each ${review ? "pro" : 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(call3.share, "as posted")})${hire ? "" : "; the pick's and the runners-up's legs carry the same share on top of theirs"}` : "";
263780
+ const shareLine2 = shared && call3.share ? `each ${review ? "pro" : 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(call3.share, "as posted")})${hire ? "" : "; the pick's and the runners-up's legs carry the same share"}` : "";
263767
263781
  const proMoney = review && call3.share && hire ? `Money: ${feeLine({ split: call3.share, net: m.entryEach, symbol: ctx.symbol, network: ctx.network ?? "mainnet" })} Up to ${m.entryUpTo} ${m.entryUpTo === 1 ? "report is" : "reports are"} paid, each one that passes all ${n} lines; there is no pick. Held on the ask's own account before anyone enters.` : null;
263768
263782
  const out = [
263769
263783
  `${review ? "Pro ask" : hire ? "Hiring call" : "Open call"} "${call3.slug}" \xB7 posted by ${call3.poster} \xB7 ${stageWords(call3, ctx.now)}`,
263770
- ...review && ctx.site ? [`Its page: ${ctx.site.replace(/\/+$/, "")}/pro/reviews/${ctx.id}`] : [],
263784
+ /* A pro ask's page is its poster's alone (2026-10-29); an invited pro reads the same ask as their job, under Work. */
263785
+ ...review && ctx.site ? [`Its pages: the poster's ${ctx.site.replace(/\/+$/, "")}/me/pro-asks/${ctx.id}; an invited pro's job ${ctx.site.replace(/\/+$/, "")}/me/work/${ctx.id}`] : [],
263771
263786
  call3.title,
263772
263787
  call3.lede,
263773
263788
  ...review && call3.subject ? [`Reviewing: ${call3.subject.name}${call3.subject.version ? ` ${call3.subject.version}` : ""}${call3.subject.where ? ` \xB7 ${call3.subject.where}` : ""}. Access details, if any, are in your invitation.`] : [],
@@ -264274,7 +264289,7 @@ async function postNextLegNow(slug, ctx) {
264274
264289
  tag: tagOf(draft),
264275
264290
  ...draft.fields.cover?.url ? { cover: draft.fields.cover.url } : {},
264276
264291
  /* A review's page is in its world (world.ts); every other call's on Open Call. */
264277
- url: draft.fields.kind === "review" ? `${ctx.site.replace(/\/+$/, "")}/pro/reviews/${slug}` : `${ctx.site.replace(/\/+$/, "")}/opencall/${slug}`,
264292
+ url: draft.fields.kind === "review" ? `${ctx.site.replace(/\/+$/, "")}/me/pro-asks/${slug}` : `${ctx.site.replace(/\/+$/, "")}/opencall/${slug}`,
264278
264293
  ...dates?.closes ? { endsAt: new Date(new Date(dates.pick ?? dates.closes).getTime() + 30 * 24 * 3600 * 1e3) } : {}
264279
264294
  },
264280
264295
  { onMinted: remember2 }
@@ -265126,6 +265141,7 @@ __export(invites_exports, {
265126
265141
  inviteItemsOf: () => inviteItemsOf,
265127
265142
  linkedinStatus: () => linkedinStatus,
265128
265143
  markNoticesRead: () => markNoticesRead,
265144
+ myCalls: () => myCalls,
265129
265145
  myInvitations: () => myInvitations,
265130
265146
  noticeWords: () => noticeWords,
265131
265147
  pageWords: () => pageWords,
@@ -265268,6 +265284,23 @@ async function myInvitations(site, signer2, fetchImpl) {
265268
265284
  }
265269
265285
  return { invited };
265270
265286
  }
265287
+ async function myCalls(site, signer2, fetchImpl) {
265288
+ const calls = [];
265289
+ const person = signer2.principal && signer2.principal !== signer2.address ? signer2.principal : null;
265290
+ for (const onBehalfOf of [null, ...person ? [person] : []]) {
265291
+ const message = callsMessage(signer2.address);
265292
+ const answer = await post(site, "/api/invites/calls", { address: signer2.address, ...onBehalfOf ? { onBehalfOf } : {}, closed: true, proof: { kind: "adr36", message, ...await signWords(signer2.mnemonic, message) } }, fetchImpl);
265293
+ const missed = (why) => ({ calls, missed: `the pro asks of ${onBehalfOf}, the account you act for, could not be read just now (${why})` });
265294
+ if (!answer.ok) {
265295
+ if (!onBehalfOf) return { refused: refusalOf(answer) };
265296
+ return missed(refusalOf(answer));
265297
+ }
265298
+ const rows2 = answer.body.calls ?? [];
265299
+ if (onBehalfOf && rows2.some((one) => one.owner && one.owner !== onBehalfOf)) return missed("the site does not read them for an agent yet");
265300
+ for (const one of rows2) if (!calls.some((seen2) => seen2.collectionId === one.collectionId)) calls.push(one);
265301
+ }
265302
+ return { calls };
265303
+ }
265271
265304
  async function readNotices(site, signer2, options = {}, fetchImpl) {
265272
265305
  const message = noticesMessage(signer2.address);
265273
265306
  const answer = await post(site, "/api/invites/notices", { address: signer2.address, ...signer2.principal ? { onBehalfOf: signer2.principal } : {}, ...options, proof: { kind: "adr36", message, ...await signWords(signer2.mnemonic, message) } }, fetchImpl);
@@ -265284,7 +265317,7 @@ function noticeWords(notices, site) {
265284
265317
  return notices.map((one) => {
265285
265318
  const room = one.kind === "message" ? /\/with\/([^/?#]+)$/.exec(one.href)?.[1] : void 0;
265286
265319
  const next = one.kind === "message" ? `read_messages collectionId "${one.collectionId}"${room ? ` with "${decodeURIComponent(room)}"` : ""}` : one.kind === "application" ? `decide_application "${one.collectionId}"` : one.kind === "link-opened" || one.kind === "invite-accepted" ? `call_invites "${one.collectionId}"` : one.kind === "invite-declined" ? `invite_to_call "${one.collectionId}" (someone else)` : one.kind === "invited" && isReviewTitle(one.title) ? `accept_invitation "${one.collectionId}" or decline_invitation` : one.kind === "declined" ? "" : `read_ask "${one.collectionId}"`;
265287
- const href = isReviewTitle(one.title) ? one.href.replace(/^\/opencall\/(?!i\/)([a-z][a-z0-9-]{2,39})$/, "/pro/reviews/$1") : one.href;
265320
+ const href = sitePath(isReviewTitle(one.title) ? one.href.replace(/^\/opencall\/(?!i\/)([a-z][a-z0-9-]{2,39})$/, one.kind === "invited" ? `/me/work/${one.collectionId}` : "/me/pro-asks/$1") : one.href);
265288
265321
  return ` \xB7 ${one.headline}: ${one.line}${/["”.]$/.test(one.line) ? "" : "."} ${base6}${href}${next ? ` \xB7 ${next}` : ""}`;
265289
265322
  });
265290
265323
  }
@@ -267013,7 +267046,7 @@ var init_pairing = __esm({
267013
267046
  CODE = /^[a-f0-9]{16}$/;
267014
267047
  memoFor = (code10) => `openhands-connect:${code10}`;
267015
267048
  connectLinkWithCode = (site, agent, code10) => `${site}/connect?agent=${encodeURIComponent(agent)}&code=${code10}`;
267016
- pairingSite = (site, pairing) => pairing.reviews ? `${site.replace(/\/+$/, "")}/pro` : site;
267049
+ pairingSite = (site, _pairing) => site;
267017
267050
  }
267018
267051
  });
267019
267052
 
@@ -267088,12 +267121,14 @@ function roomRows(counts) {
267088
267121
  switch (room.key) {
267089
267122
  case "page":
267090
267123
  return { ...room, figure: null, after: null, ...counts.page === "listed" ? { chip: "Listed" } : counts.page === "private" ? { chip: "Private" } : counts.page === "verify" ? { verify: true } : {} };
267124
+ case "proAsks":
267125
+ return { ...room, ...counted(counts.proAsks ?? null, null) };
267091
267126
  case "work":
267092
- return { ...room, ...counted(counts.work, "waiting") };
267127
+ return { ...room, ...fresh2(counts.work, "waiting") };
267093
267128
  case "messages":
267094
- return { ...room, ...counted(counts.unread, "unread") };
267129
+ return { ...room, ...fresh2(counts.unread, "unread") };
267095
267130
  case "inbox":
267096
- return { ...room, ...counted(counts.inbox, null) };
267131
+ return { ...room, ...fresh2(counts.inbox, "new") };
267097
267132
  case "money":
267098
267133
  return { ...room, figure: counts.money.available, money: true, after: counts.money.onAsks ? `${counts.money.onAsks} on asks` : null };
267099
267134
  case "agents":
@@ -267103,12 +267138,13 @@ function roomRows(counts) {
267103
267138
  }
267104
267139
  });
267105
267140
  }
267106
- var ROOMS, counted, roomLines;
267141
+ var ROOMS, counted, fresh2, roomLines;
267107
267142
  var init_meRooms = __esm({
267108
267143
  "../../app/lib/meRooms.ts"() {
267109
267144
  "use strict";
267110
267145
  ROOMS = [
267111
267146
  { key: "page", word: "Your page", href: "/me/page" },
267147
+ { key: "proAsks", word: "Pro asks", href: "/me/pro-asks" },
267112
267148
  { key: "work", word: "Work", href: "/me/work" },
267113
267149
  { key: "messages", word: "Messages", href: "/me/messages" },
267114
267150
  { key: "inbox", word: "Inbox", href: "/me/check" },
@@ -267117,10 +267153,58 @@ var init_meRooms = __esm({
267117
267153
  { key: "settings", word: "Settings", href: "/me/settings" }
267118
267154
  ];
267119
267155
  counted = (value, after) => value ? { figure: String(value), after } : { figure: null, after: null };
267156
+ fresh2 = (value, after) => ({ ...counted(value, after), ...value ? { fresh: true } : {} });
267120
267157
  roomLines = (counts) => roomRows(counts).map((row) => `${row.word}: ${row.chip ?? (row.verify ? "not verified yet" : [row.figure, row.after].filter(Boolean).join(row.money ? " \xB7 " : " ") || (row.key === "settings" ? "sign-in, recovery, currency, identity" : "nothing waiting"))}`);
267121
267158
  }
267122
267159
  });
267123
267160
 
267161
+ // src/proAsks.ts
267162
+ var proAsks_exports = {};
267163
+ __export(proAsks_exports, {
267164
+ approvePreview: () => approvePreview,
267165
+ handOver: () => handOver,
267166
+ proAskLines: () => proAskLines,
267167
+ proAsksOf: () => proAsksOf,
267168
+ reportPage: () => reportPage,
267169
+ shortDate: () => shortDate
267170
+ });
267171
+ function proAskLines(site, calls, options = {}) {
267172
+ const now3 = options.now ?? Date.now();
267173
+ const base6 = site.replace(/\/+$/, "");
267174
+ return proAsksOf(calls).flatMap((one) => {
267175
+ const open = isOpen2(one, now3);
267176
+ const parts = [open ? "Open" : "Closed", `${one.count} of ${one.quota} reports`];
267177
+ const closes = open ? shortDate(one.end_date) : "";
267178
+ if (closes) parts.push(`closes ${closes}`);
267179
+ const held = options.held?.get(one.collectionId);
267180
+ if (held) parts.push(`holds ${held}`);
267181
+ const toApprove = options.waiting?.filter((item) => item.collectionId === one.collectionId).length ?? 0;
267182
+ if (toApprove) parts.push(`${toApprove} ${toApprove === 1 ? "report" : "reports"} to approve`);
267183
+ return [`#${one.collectionId} ${bare(one.title)} \xB7 ${parts.join(" \xB7 ")}`, ` ${base6}/me/pro-asks/${one.collectionId}`];
267184
+ });
267185
+ }
267186
+ function approvePreview(site, input) {
267187
+ const page = reportPage(site, input.collectionId, input.claimId);
267188
+ const what = `Approving report ${input.claimId} on pro ask #${input.collectionId} pays ${input.pro}${input.pays ? ` ${input.pays}` : ""} from what the ask holds. Approval is final.`;
267189
+ return input.canSign ? [what, `The report: ${page}`, "", "To approve, call approve_report again with confirm: true."].join("\n") : [what, handOver(site, input), "Nothing was sent."].join("\n");
267190
+ }
267191
+ var bare, shortDate, isOpen2, proAsksOf, reportPage, handOver;
267192
+ var init_proAsks = __esm({
267193
+ "src/proAsks.ts"() {
267194
+ "use strict";
267195
+ init_world();
267196
+ bare = (title) => (title ?? "").replace(/^Expert review:\s*/, "") || "A pro ask";
267197
+ shortDate = (iso) => {
267198
+ const at = iso ? new Date(iso) : null;
267199
+ return at && !Number.isNaN(at.getTime()) ? at.toLocaleDateString("en-GB", { day: "numeric", month: "short", timeZone: "UTC" }) : "";
267200
+ };
267201
+ isOpen2 = (one, now3) => one.state === 0 && (!one.end_date || Date.parse(one.end_date) > now3);
267202
+ proAsksOf = (calls) => calls.filter((one) => isReviewTitle(one.title)).sort((a, b) => Number(b.collectionId) - Number(a.collectionId));
267203
+ reportPage = (site, collectionId, claimId) => `${site.replace(/\/+$/, "")}/pro/reviews/${collectionId}/report/${encodeURIComponent(claimId)}`;
267204
+ handOver = (site, input) => `Only ${input.approver} can give this OK, signed in as themselves: the Approve button on the report's page, ${reportPage(site, input.collectionId, input.claimId)}#decide. This key acts for them but cannot sign their OK at the verifier.`;
267205
+ }
267206
+ });
267207
+
267124
267208
  // src/verdictSource.ts
267125
267209
  var verdictSource_exports = {};
267126
267210
  __export(verdictSource_exports, {
@@ -292281,7 +292365,7 @@ import { randomBytes as randomBytes7 } from "node:crypto";
292281
292365
  // package.json
292282
292366
  var package_default = {
292283
292367
  name: "@ixo/asks-mcp",
292284
- version: "2.0.0-dev.2",
292368
+ version: "2.0.0-dev.4",
292285
292369
  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.",
292286
292370
  license: "Apache-2.0",
292287
292371
  repository: {
@@ -292571,7 +292655,7 @@ var TOOLS = {
292571
292655
  },
292572
292656
  draft_call: {
292573
292657
  title: "Set up an open call or a pro ask",
292574
- description: 'Draft an open call or a pro ask. An open call is a brief people answer with their own AI, checked against five requirements: a competition picks a winner; a hire pays every entry that passes, up to a number, and picks nobody; open to anyone, by application (you say yes to each), or by invitation (your list: addresses, @handles, single-use links). A software-review Ask (`kind: "review"`) is one specialised work type on Pro Asks: verified pros you invite try the scenarios and send reports; always a hire, by invitation only; Asky Pro Checker, the verifier, checks each report. Interviews in order: give what the person said, and the answer lists what is still missing, each with the question to ask them. Kept on disk between calls, so never re-send what was already taken. When nothing is missing it shows the whole call, what each leg holds (a pro ask\'s money in one sentence: what each pro receives, what is held per report, and the fee: 20 % on top of what the pro receives) and the clock, for the person to approve. Spends nothing.',
292658
+ description: 'Draft an open call or a pro ask. An open call is a brief people answer with their own AI, checked against five requirements: a competition picks a winner; a hire pays every entry that passes, up to a number, and picks nobody; open to anyone, by application (you say yes to each), or by invitation (your list: addresses, @handles, single-use links). A software-review Ask (`kind: "review"`) is one specialised work type on Pro Asks: verified pros you invite try the scenarios and send reports; always a hire, by invitation only; Asky Pro Checker, the verifier, checks each report. Interviews in order: give what the person said, and the answer lists what is still missing, each with the question to ask them. Kept on disk between calls, so never re-send what was already taken. When nothing is missing it shows the whole call, what each leg holds (a pro ask\'s money in one sentence: what each pro receives, what is held per report, and the fee: 20 % of what you pay runs asks.world, the pro gets the rest) and the clock, for the person to approve. Spends nothing.',
292575
292659
  spends: false,
292576
292660
  hosted: false,
292577
292661
  /* It writes the draft file, and nothing else anywhere. */
@@ -292627,6 +292711,20 @@ var TOOLS = {
292627
292711
  hosted: false,
292628
292712
  annotations: { ...reads, readOnlyHint: true }
292629
292713
  },
292714
+ my_pro_asks: {
292715
+ title: "List the pro asks the person posted",
292716
+ description: `The pro asks this key posted, and those of the person it acts for, newest first, one line each as the site's Pro asks room says them: Open or Closed, how many reports of how many, when it closes, what it holds, and what waits for them ("1 report to approve"), with its page, which only they can open. The next step on each is a tool: invite_to_call, collect_answer (reads its reports), approve_report, close_ask. Spends nothing.`,
292717
+ spends: false,
292718
+ hosted: false,
292719
+ annotations: { ...reads, readOnlyHint: true }
292720
+ },
292721
+ approve_report: {
292722
+ title: "Approve a report on a pro ask (spends money)",
292723
+ description: "Give the final OK on one report a pro sent to a pro ask: the verifier records it and the pro is paid from what the ask holds. Approval is final. Call without confirm first: it says the report, who sent it, what approving pays and whose OK it is; then again with confirm: true, which approves when this key is the one the ask names to give the OK. When the OK is the person's own (a pro ask posted for them), it hands them the report's Approve button on the site instead, and sends nothing.",
292724
+ spends: true,
292725
+ hosted: false,
292726
+ annotations: acts
292727
+ },
292630
292728
  accept_invitation: {
292631
292729
  title: "Accept an invitation to a pro ask",
292632
292730
  description: "Accept an invitation to a pro ask: a link or the ask's number. As the account you act for when connected, else as this key. The builder hears you accepted, and your room with them opens with their note first; a link becomes yours, so nobody else can use it. Someone who is not a verified pro is refused, and the link stays open. Previews; `confirm: true` accepts, once the person says yes. Spends nothing.",
@@ -293249,8 +293347,8 @@ function whyFromReceipt(receipt, rubric) {
293249
293347
  const push = (r) => {
293250
293348
  const clean6 = { ...r, label: scrub(r.label, 200), detail: scrub(r.detail) };
293251
293349
  const key = `${clean6.result}|${clean6.label}`;
293252
- const bare = clean6.detail === "Passed" || clean6.detail === "Did not pass";
293253
- if (seen2.has(`${key}|${clean6.detail}`) || bare && seen2.has(key)) return;
293350
+ const bare2 = clean6.detail === "Passed" || clean6.detail === "Did not pass";
293351
+ if (seen2.has(`${key}|${clean6.detail}`) || bare2 && seen2.has(key)) return;
293254
293352
  seen2.add(key).add(`${key}|${clean6.detail}`);
293255
293353
  reasons.push(clean6);
293256
293354
  };
@@ -294400,18 +294498,18 @@ async function build2(options, server2, parts) {
294400
294498
  const refreshDeployment = async () => {
294401
294499
  if (!local || options.deployment || Date.now() - siteReadAt < (CLAIM_FORM && !DEPLOYMENT.note ? SITE_REREAD_MS : 1e4)) return;
294402
294500
  siteReadAt = Date.now();
294403
- const fresh2 = await deployment().catch(() => null);
294404
- if (!fresh2 || fresh2.note) return;
294405
- DEPLOYMENT = fresh2;
294406
- adoptRelayer(fresh2);
294407
- adoptAskProtocol(fresh2);
294408
- adoptReviewBilling(fresh2);
294409
- adoptOracleUrl(fresh2);
294410
- adoptFeeSplit(fresh2);
294411
- adoptAskDenom(fresh2);
294412
- adoptClaimForm(fresh2);
294413
- CLAIM_FORM = fresh2.claimForm;
294414
- applyDeployment(fresh2);
294501
+ const fresh3 = await deployment().catch(() => null);
294502
+ if (!fresh3 || fresh3.note) return;
294503
+ DEPLOYMENT = fresh3;
294504
+ adoptRelayer(fresh3);
294505
+ adoptAskProtocol(fresh3);
294506
+ adoptReviewBilling(fresh3);
294507
+ adoptOracleUrl(fresh3);
294508
+ adoptFeeSplit(fresh3);
294509
+ adoptAskDenom(fresh3);
294510
+ adoptClaimForm(fresh3);
294511
+ CLAIM_FORM = fresh3.claimForm;
294512
+ applyDeployment(fresh3);
294415
294513
  };
294416
294514
  const unjudged = async () => {
294417
294515
  await refreshDeployment();
@@ -294442,7 +294540,7 @@ async function build2(options, server2, parts) {
294442
294540
  const cap = capCoinRefusal2(acting, coin);
294443
294541
  return cap ? { refused: cap } : coin;
294444
294542
  };
294445
- const feeWords = (base6, symbol2 = FEE_SYMBOL) => dollarsOf(base6, symbol2, NETWORK2);
294543
+ const feeWords2 = (base6, symbol2 = FEE_SYMBOL) => dollarsOf(base6, symbol2, NETWORK2);
294446
294544
  const FUNDER = ownEnv().OPENHANDS_FUNDER?.trim() || void 0;
294447
294545
  const FOLLOW_PAY_MAX = 10;
294448
294546
  const MAX_PER_ASK = CAPS.perAsk;
@@ -294698,11 +294796,11 @@ Each is its own ask and pays on its own. Read one with read_ask before deciding.
294698
294796
  if (title) taint2(title);
294699
294797
  const facts = row.callSummary;
294700
294798
  const shares = call3.shared;
294701
- const entryPays = shares && facts?.entryEach !== void 0 && call3.entries?.pays ? `${faceOf2({ value: facts.entryEach, symbol: call3.entries.pays.symbol }, SANDBOX)?.text} (the network's share is on top of it)` : call3.entries?.pays ? `${faceOf2(call3.entries.pays, SANDBOX)?.text}${shares ? ", less the network's share" : ""}` : void 0;
294799
+ const entryPays = shares && facts?.entryEach !== void 0 && call3.entries?.pays ? faceOf2({ value: facts.entryEach, symbol: call3.entries.pays.symbol }, SANDBOX)?.text : call3.entries?.pays ? `${faceOf2(call3.entries.pays, SANDBOX)?.text}${shares ? ", less the network's share" : ""}` : void 0;
294702
294800
  return [
294703
294801
  `${review ? "Expert review" : "Open call"} "${call3.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`,
294704
294802
  title ? asData("ask-title", title) : "",
294705
- `${review ? "A specialist's report on the review's scenarios, assessed against its requirements. A report that meets them is paid, after the builder's final approval when they give one." : "A written entry assessed against five requirements. Entries that meet all five are paid. For competitions, the poster also chooses a prize winner."} ${call3.entries ? `Read it with read_ask "${call3.entries.id}"; the page is` : "Read its legs on the page:"} ${review ? `${EXPERT_SITE}/pro/reviews/${call3.entries?.id ?? call3.slug}` : `${SITE}/opencall/${call3.slug}`}`
294803
+ `${review ? "A specialist's report on the review's scenarios, assessed against its requirements. A report that meets them is paid, after the builder's final approval when they give one." : "A written entry assessed against five requirements. Entries that meet all five are paid. For competitions, the poster also chooses a prize winner."} ${call3.entries ? `Read it with read_ask "${call3.entries.id}"; the page is` : "Read its legs on the page:"} ${review ? `${EXPERT_SITE}/me/pro-asks/${call3.entries?.id ?? call3.slug}` : `${SITE}/opencall/${call3.slug}`}`
294706
294804
  ].filter(Boolean).join("\n");
294707
294805
  };
294708
294806
  const lines2 = board.rows.filter(within2).map((row) => {
@@ -295305,15 +295403,15 @@ Never open it yourself.`);
295305
295403
  amount: offChain ? `${offChain.amount} ${offChain.currency}` : face(pay, symbol2),
295306
295404
  quota: spots,
295307
295405
  total: offChain ? `${(Number(offChain.amount) * spots).toFixed(2)} ${offChain.currency}` : rewardsFace,
295308
- fee: feeWords(perCheck ? perCheck.price : legBase),
295309
- feeTotal: feeWords(legBase * spots),
295406
+ fee: feeWords2(perCheck ? perCheck.price : legBase),
295407
+ feeTotal: feeWords2(legBase * spots),
295310
295408
  perReview: Boolean(perCheck),
295311
295409
  own: !perCheck && legBase === 0 && !orgAsk.oracle.url,
295312
295410
  free: !perCheck && legBase === 0 && Boolean(orgAsk.oracle.url)
295313
295411
  }).map((line2) => `- ${line2.label}: ${line2.text}`).join("\n") : "";
295314
295412
  const { total, what: preview, details: costDetails } = orgAsk ? { total: 0, what: `nothing from this key (${orgName} pays)`, details: `What this costs ${orgName}:
295315
- ${orgCosts}` } : { total: onChain.total, what: perCheck ? `${onChain.what}. Review fee: ${billing.creditsCopy(null, feeWords(perCheck.price))}` : onChain.what, details: "" };
295316
- const orgToken = orgAsk ? (await Promise.resolve().then(() => (init_hq(), hq_exports))).orgConfirmToken({ offChainTotal: offChain ? `${(Number(offChain.amount) * spots).toFixed(2)} ${offChain.currency}` : null, rewards: rewardsFace, feesSetAside: !perCheck && legBase > 0 ? feeWords(legBase * spots) : null }) : void 0;
295413
+ ${orgCosts}` } : { total: onChain.total, what: perCheck ? `${onChain.what}. Review fee: ${billing.creditsCopy(null, feeWords2(perCheck.price))}` : onChain.what, details: "" };
295414
+ const orgToken = orgAsk ? (await Promise.resolve().then(() => (init_hq(), hq_exports))).orgConfirmToken({ offChainTotal: offChain ? `${(Number(offChain.amount) * spots).toFixed(2)} ${offChain.currency}` : null, rewards: rewardsFace, feesSetAside: !perCheck && legBase > 0 ? feeWords2(legBase * spots) : null }) : void 0;
295317
295415
  const payDraft = card ? await Promise.resolve().then(() => (init_payDraft(), payDraft_exports)) : null;
295318
295416
  const quote = payDraft?.quoteOf(Number(baseFromPay2(pay)) * spots, legBase * spots);
295319
295417
  const cardWords = payDraft && quote ? payDraft.cardPreview(quote) : null;
@@ -295342,7 +295440,7 @@ ${orgCosts}` } : { total: onChain.total, what: perCheck ? `${onChain.what}. Revi
295342
295440
  card ? null : actingNote(acting),
295343
295441
  pick2,
295344
295442
  expert ? expertNote(expert) : checkingNote(terms, acting?.principal ?? null),
295345
- expert ? null : orgAsk ? `Reviewed by ${await oracleWordsFor({ did: orgAsk.oracle.did, address: orgAsk.oracle.address })}.` : reviewer && reviewer !== CHECKER ? `Reviewed by ${await oracleWordsFor({ address: reviewer })}.` : reviewer ? `Checked by asks.world verifier, ${feeWords(perCheck ? perCheck.price : legBase)} per check.` : null,
295443
+ expert ? null : orgAsk ? `Reviewed by ${await oracleWordsFor({ did: orgAsk.oracle.did, address: orgAsk.oracle.address })}.` : reviewer && reviewer !== CHECKER ? `Reviewed by ${await oracleWordsFor({ address: reviewer })}.` : reviewer ? `Checked by asks.world verifier, ${feeWords2(perCheck ? perCheck.price : legBase)} per check.` : null,
295346
295444
  doorWords(door2.access, door2.invite.length),
295347
295445
  inCampaign ? `In campaign ${inCampaign.protocolDid}, checked by its shared rules (${inCampaign.claimProtocolDid}).` : null,
295348
295446
  picture2 && !("refused" in picture2) ? `Picture: ${picture2.contentType.slice(6)}, ${Math.ceil(picture2.bytes.byteLength / 1024)} KB, uploaded through your account connection with the ask.` : null,
@@ -295429,7 +295527,7 @@ ${SITE}/t/${earlier.id}`);
295429
295527
  const callPerCheck = async (checker = CHECKER) => (await Promise.resolve().then(() => (init_reviewBilling(), reviewBilling_exports))).deploymentBillsPerCheck(checker).catch(() => false);
295430
295528
  const callChecker2 = async (review) => (await Promise.resolve().then(() => (init_call2(), call_exports2))).callChecker(NETWORK2, review, CHECKER);
295431
295529
  const callSplit = async () => (await Promise.resolve().then(() => (init_query43(), query_exports43))).feeSplitOrNull();
295432
- async function sharedPays(leg, posted, bare = false) {
295530
+ async function sharedPays(leg, posted, bare2 = false) {
295433
295531
  if (leg?.is_oracle_payment !== true || !leg.amount?.[0]) return null;
295434
295532
  const { coinDisplay: coinDisplay2, faceOf: faceOf2 } = await Promise.resolve().then(() => (init_format(), format_exports));
295435
295533
  const { netOf: netOf2 } = await Promise.resolve().then(() => (init_share(), share_exports));
@@ -295439,15 +295537,15 @@ ${SITE}/t/${earlier.id}`);
295439
295537
  const split3 = posted ?? await callSplit();
295440
295538
  if (!split3) return `${said4(Number(amount3))} less the network's share (the fee split could not be read just now)`;
295441
295539
  const receives = netOf2(Number(amount3), split3);
295442
- if (bare) return said4(receives);
295540
+ if (bare2) return said4(receives);
295443
295541
  return `${said4(receives)} (the leg is ${said4(Number(amount3))}; the network keeps ${said4(Number(amount3) - receives)}: ${shareWords2(split3, posted ? "as posted" : "today")})`;
295444
295542
  }
295445
- const callLocks = (rewardBase, feeBase, symbol2 = ASK_SYMBOL) => `${pays(rewardBase, symbol2)}${feeBase > 0 ? ` and ${feeWords(feeBase)} for reviews` : ""}`;
295543
+ const callLocks = (rewardBase, feeBase, symbol2 = ASK_SYMBOL) => `${pays(rewardBase, symbol2)}${feeBase > 0 ? ` and ${feeWords2(feeBase)} for reviews` : ""}`;
295446
295544
  function escrowTotal(pay, spots, legBase, symbol2 = ASK_SYMBOL, sameCoin = true, feeSymbol = FEE_SYMBOL) {
295447
295545
  const leg = legBase / MICRO6;
295448
295546
  if (!sameCoin && leg > 0) {
295449
295547
  const total2 = pay * spots;
295450
- return { total: total2, what: `${face(total2, symbol2)} and ${feeWords(legBase * spots, feeSymbol)}. A ${face(pay, symbol2)} reward for each of ${spots} ${spots === 1 ? "answer" : "answers"}, plus ${feeWords(legBase, feeSymbol)} for each review. Held now` };
295548
+ return { total: total2, what: `${face(total2, symbol2)} and ${feeWords2(legBase * spots, feeSymbol)}. A ${face(pay, symbol2)} reward for each of ${spots} ${spots === 1 ? "answer" : "answers"}, plus ${feeWords2(legBase, feeSymbol)} for each review. Held now` };
295451
295549
  }
295452
295550
  const total = (pay + leg) * spots;
295453
295551
  return {
@@ -295554,7 +295652,7 @@ ${SITE}/t/${earlier.id}`);
295554
295652
  const billing = await Promise.resolve().then(() => (init_reviewBilling(), reviewBilling_exports));
295555
295653
  const escrowed = escrowTotal(pay, spots, legBase, coinSymbol, coin === (expert?.feeDenom ?? FEE_DENOM), symbolOf(expert?.feeDenom ?? FEE_DENOM));
295556
295654
  const total = escrowed.total;
295557
- const what = perCheck ? `${escrowed.what}. Review fee: ${billing.creditsCopy(null, feeWords(perCheck.price))}` : escrowed.what;
295655
+ const what = perCheck ? `${escrowed.what}. Review fee: ${billing.creditsCopy(null, feeWords2(perCheck.price))}` : escrowed.what;
295558
295656
  const guard = spendGuard({
295559
295657
  bucket: "world",
295560
295658
  task: question,
@@ -296356,10 +296454,11 @@ ${pending.link}`);
296356
296454
  })();
296357
296455
  const rooms = await (async () => {
296358
296456
  try {
296359
- const [{ roomLines: roomLines2 }, invitesModule, roomsModule] = await Promise.all([Promise.resolve().then(() => (init_meRooms(), meRooms_exports)), Promise.resolve().then(() => (init_invites(), invites_exports)), Promise.resolve().then(() => (init_rooms(), rooms_exports))]);
296457
+ const [{ roomLines: roomLines2 }, invitesModule, roomsModule, { proAsksOf: proAsksOf2 }] = await Promise.all([Promise.resolve().then(() => (init_meRooms(), meRooms_exports)), Promise.resolve().then(() => (init_invites(), invites_exports)), Promise.resolve().then(() => (init_rooms(), rooms_exports)), Promise.resolve().then(() => (init_proAsks(), proAsks_exports))]);
296360
296458
  const signer3 = await inviteSigner(await principal2());
296361
- const [page, invited, mineRooms, waiting, agents] = await Promise.all([
296459
+ const [page, calls, invited, mineRooms, waiting, agents] = await Promise.all([
296362
296460
  invitesModule.readMyPage(SITE, signer3).catch(() => void 0),
296461
+ invitesModule.myCalls(SITE, signer3).catch(() => null),
296363
296462
  invitesModule.myInvitations(SITE, signer3).catch(() => null),
296364
296463
  roomsModule.myRooms(SITE, signer3).catch(() => null),
296365
296464
  fetch2(`${SITE}/api/waiting?address=${encodeURIComponent(signer3.address)}`, { signal: AbortSignal.timeout(2e4) }).then((r) => r.ok ? r.json() : null).catch(() => null),
@@ -296370,6 +296469,7 @@ ${pending.link}`);
296370
296469
  "You, as the account area says it:",
296371
296470
  ...roomLines2({
296372
296471
  page: page === void 0 ? null : page === null ? "none" : !verified ? "verify" : page.profile.listed === false ? "private" : "listed",
296472
+ proAsks: calls && "calls" in calls ? proAsksOf2(calls.calls).length : null,
296373
296473
  work: invited && "invited" in invited ? invited.invited.filter((one) => one.answer === "sent").length : null,
296374
296474
  unread: mineRooms && "rooms" in mineRooms ? mineRooms.rooms.reduce((sum2, one) => sum2 + one.unread, 0) : null,
296375
296475
  inbox: Array.isArray(waiting?.waiting) ? waiting.waiting.length : null,
@@ -296393,7 +296493,7 @@ ${pending.link}`);
296393
296493
  The review fee's coin, where it is not the reward's (mainnet: USDC rewards, PAY fees), is what covers the next
296394
296494
  ask's checks, so it is said when there is any. In dollars, as every amount. The network fee is the gate's to
296395
296495
  cover for an account that holds money, so it is not a line here. */
296396
- `Available: ${pays(Number(held[ASK_DENOM] ?? 0), ASK_DENOM)} \xB7 On your asks ${onAsks === null ? "(could not be read just now)" : pays(onAsks, "upay")}${ASK_DENOM !== FEE_DENOM && Number(held[FEE_DENOM] ?? 0) > 0 ? ` \xB7 ${feeWords(Number(held[FEE_DENOM]))} set aside for checks` : ""}`,
296496
+ `Available: ${pays(Number(held[ASK_DENOM] ?? 0), ASK_DENOM)} \xB7 On your asks ${onAsks === null ? "(could not be read just now)" : pays(onAsks, "upay")}${ASK_DENOM !== FEE_DENOM && Number(held[FEE_DENOM] ?? 0) > 0 ? ` \xB7 ${feeWords2(Number(held[FEE_DENOM]))} set aside for checks` : ""}`,
296397
296497
  done ? `Work: ${done.submitted ?? 0} answers \xB7 ${done.approved ?? 0} approved${done.earned ? ` \xB7 ${face(done.earned.value, done.earned.symbol)} earned` : ""}` : "",
296398
296498
  /* Whose money this would spend, and how much of it is left. An agent
296399
296499
  funded by somebody's allowance should be able to say so — and an
@@ -296559,7 +296659,7 @@ ${settings}`
296559
296659
  const unread = await unreadable(terms);
296560
296660
  if (unread) return text4(unread);
296561
296661
  const { total } = escrowTotal(pin_pay, pins2, legBase, coinSymbol, coin === FEE_DENOM);
296562
- const preview = `${face(total, coinSymbol)} to open "${name8}". ${pins2} ${pins2 === 1 ? "report" : "reports"} at ${face(pin_pay, coinSymbol)} each, plus ${feeWords(legBase)} to whoever checks each one. Mapping stays open ${open} days, and two pins closer than ${apart} m are the same mess`;
296662
+ const preview = `${face(total, coinSymbol)} to open "${name8}". ${pins2} ${pins2 === 1 ? "report" : "reports"} at ${face(pin_pay, coinSymbol)} each, plus ${feeWords2(legBase)} to whoever checks each one. Mapping stays open ${open} days, and two pins closer than ${apart} m are the same mess`;
296563
296663
  const guard = spendGuard({ bucket: "organise", task: `campaign ${tag}`, total, ...confirm ? { confirm } : {}, what: preview, symbol: coinSymbol });
296564
296664
  if (guard) {
296565
296665
  return text4(
@@ -296706,12 +296806,12 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
296706
296806
  const keyOnly = !acting && feeTotal > 0;
296707
296807
  const blocked = capped4.length > 0 || Boolean(otherCoin) || Boolean(twoCoins) && draft.fields.kind !== "review";
296708
296808
  const m = draft.fields.money;
296709
- const { shareWords: shareWords2 } = await Promise.resolve().then(() => (init_callRead(), callRead_exports));
296809
+ const { feeSentence: feeSentence2 } = await Promise.resolve().then(() => (init_feeLine(), feeLine_exports));
296710
296810
  const proMoney = proAsk && Boolean(plan.share);
296711
296811
  const who = draft.fields.kind === "review" ? "pro" : "hired person";
296712
- const onTop = (leg) => plan.share && !proMoney ? `; the network's share (${shareWords2(plan.share, "today")}) is added on top, so the leg pays ${pays(leg.legBase, coinSymbol)}` : "";
296812
+ const legPays = (leg) => plan.share && !proMoney ? `, so the leg pays ${pays(leg.legBase, coinSymbol)}` : "";
296713
296813
  const legRows = plan.legs.map(
296714
- (leg) => leg.leg === "prize" ? ` The pick ${pays(leg.payBase, coinSymbol)} to one entry${onTop(leg)}; its ask takes ${leg.quota} claims (the pick and two spare) \xB7 locks ${pays(leg.escrowBase, coinSymbol)}` : leg.leg === "runnersUp" ? ` Runners-up ${pays(leg.payBase, coinSymbol)} to each of ${m.runnersN}${onTop(leg)} \xB7 locks ${pays(leg.escrowBase, coinSymbol)}` : plan.share ? ` Entries each ${who} receives ${pays(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 ${pays(leg.checkBase, coinSymbol)} per check to ${checker.name}` : leg.checkBase > 0 ? `, plus the verifier's ${feeWords(leg.checkBase)} per ruling` : ", the verifier billed per check from credits"} \xB7 locks ${callLocks(leg.escrowBase, leg.feeBase, coinSymbol)}` : ` Entries ${pays(leg.payBase, coinSymbol)} to each entry that passes all ${draft.fields.kind === "review" ? "six" : "five"} lines, up to ${leg.quota}${checker.fee ? `, plus ${pays(leg.checkBase, coinSymbol)} per check to ${checker.name}` : leg.checkBase > 0 ? `, plus the verifier's ${feeWords(leg.checkBase)} per ruling` : ", the verifier billed per check from credits"} \xB7 locks ${callLocks(leg.escrowBase, leg.feeBase, coinSymbol)}`
296814
+ (leg) => leg.leg === "prize" ? ` The pick ${pays(leg.payBase, coinSymbol)} to one entry${legPays(leg)}; its ask takes ${leg.quota} claims (the pick and two spare) \xB7 locks ${pays(leg.escrowBase, coinSymbol)}` : leg.leg === "runnersUp" ? ` Runners-up ${pays(leg.payBase, coinSymbol)} to each of ${m.runnersN}${legPays(leg)} \xB7 locks ${pays(leg.escrowBase, coinSymbol)}` : plan.share ? ` Entries each ${who} receives ${pays(leg.payBase, coinSymbol)} for an entry that passes all ${draft.fields.kind === "review" ? "six" : "five"} lines${legPays(leg)} \xB7 up to ${leg.quota}${checker.fee ? `, plus ${pays(leg.checkBase, coinSymbol)} per check to ${checker.name}` : leg.checkBase > 0 ? `, plus the verifier's ${feeWords2(leg.checkBase)} per ruling` : ", the verifier billed per check from credits"} \xB7 locks ${callLocks(leg.escrowBase, leg.feeBase, coinSymbol)}` : ` Entries ${pays(leg.payBase, coinSymbol)} to each entry that passes all ${draft.fields.kind === "review" ? "six" : "five"} lines, up to ${leg.quota}${checker.fee ? `, plus ${pays(leg.checkBase, coinSymbol)} per check to ${checker.name}` : leg.checkBase > 0 ? `, plus the verifier's ${feeWords2(leg.checkBase)} per ruling` : ", the verifier billed per check from credits"} \xB7 locks ${callLocks(leg.escrowBase, leg.feeBase, coinSymbol)}`
296715
296815
  );
296716
296816
  const shareTotal = plan.legs.reduce((sum2, one) => sum2 + one.shareBase * one.escrowSpots, 0);
296717
296817
  const hire = draft.fields.mode === "hire";
@@ -296722,7 +296822,7 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
296722
296822
  !hire && m.prize < m.entryEach ? `The prize (${face(m.prize, coinSymbol)}) is less than one entry earns (${face(m.entryEach, coinSymbol)}).` : "",
296723
296823
  draft.fields.access === "invite" && !draft.fields.invite?.length ? proAsk ? "Nobody invited yet: find_pro suggests verified pros; add them with invite now, or invite_to_call after it posts." : 'By invitation, and nobody invited yet: add invite (addresses, @handles, "link") now, or invite_to_call after it posts.' : "",
296724
296824
  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." : "",
296725
- ...call3.balanceWarnings(held, { totalBase: plan.totalBase, feeTotal, symbol: coinSymbol, network: NETWORK2, feeWords }),
296825
+ ...call3.balanceWarnings(held, { totalBase: plan.totalBase, feeTotal, symbol: coinSymbol, network: NETWORK2, feeWords: feeWords2 }),
296726
296826
  acting?.cap && !otherCoin && Number(capIn2(acting, coin)?.remainingBase ?? 0) < plan.totalBase ? `Their cap has ${pays(Number(capIn2(acting, coin)?.remainingBase ?? 0), coinSymbol)} left; the call locks ${pays(plan.totalBase, coinSymbol)}. They raise it at ${SITE}/connect.` : "",
296727
296827
  keyOnly ? `Not connected, and a connection would not carry this call: ${call3.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.` : ""
296728
296828
  ].filter(Boolean);
@@ -296736,15 +296836,15 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
296736
296836
  ...legRows,
296737
296837
  ` Total ${callLocks(plan.totalBase, feeTotal, coinSymbol)}`,
296738
296838
  ...plan.share && !proMoney ? [
296739
- ` Network's share ${pays(shareTotal, coinSymbol)} of that, if every spot is paid (${shareWords2(plan.share, "today")}): the network splits it off each approval and pays the rest to the person. Asks and public calls carry no share.`
296839
+ ` Network's share ${pays(shareTotal, coinSymbol)} of that, if every spot is paid. ${feeSentence2(plan.share, NETWORK2, "Each hired person")} Asks and public calls carry no share.`
296740
296840
  ] : [],
296741
296841
  "",
296742
- `${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)}.`,
296842
+ `${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 ${feeWords2(held.fee)}` : ""}; this call locks ${callLocks(plan.totalBase, feeTotal, coinSymbol)}.`,
296743
296843
  hire ? `The clock: entries close ${new Date(plan.dates.closes).toUTCString()}; ${finalOk ? `every passing report is paid once ${finalOk} gives the final OK` : "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()}.`,
296744
296844
  `Who may enter: ${draft.fields.access === "invite" ? `by invitation only${draft.fields.invite?.length ? `. ${draft.fields.invite.length} people will be invited when the Ask is posted${draft.fields.inviteNote ? ", each with your private note" : ""}` : ""}` : draft.fields.access === "apply" ? "by application, listed on the board" : "anyone, listed on the board"}.`,
296745
296845
  ...draft.fields.kind === "review" ? [
296746
296846
  `A pro ask: 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`}.`,
296747
- draft.posted.entries ? `Its page: ${EXPERT_SITE}/pro/reviews/${draft.posted.entries.id}` : "Its page is named by the pro ask's number, given once it posts."
296847
+ draft.posted.entries ? `Its page: ${EXPERT_SITE}/me/pro-asks/${draft.posted.entries.id}` : "Its page is named by the pro ask's number, given once it posts."
296748
296848
  ] : [],
296749
296849
  ...warnings2.length ? ["", "Warnings:", ...warnings2.map((one) => ` - ${one}`)] : [],
296750
296850
  "",
@@ -296772,7 +296872,7 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
296772
296872
  if (!found) return text4(`No draft called "${slug}". Start one with draft_call.`);
296773
296873
  const review = found.fields.kind === "review";
296774
296874
  const checker = await callChecker2(review);
296775
- const linkOf = (posted2) => review ? `${EXPERT_SITE}/pro/reviews/${posted2.entries?.id ?? slug}` : `${SITE}/opencall/${slug}`;
296875
+ const linkOf = (posted2) => review ? `${EXPERT_SITE}/me/pro-asks/${posted2.entries?.id ?? slug}` : `${SITE}/opencall/${slug}`;
296776
296876
  const settled = found.inFlight ? await call3.settleInFlight(slug, call3.indexLookup) : null;
296777
296877
  if (settled?.state === "pending") return text4(settled.words);
296778
296878
  if (settled?.state === "landed") {
@@ -296838,20 +296938,19 @@ Then call post_call again. Nothing was posted.`);
296838
296938
  return text4(await payDraft.saveDraft(SITE, { kind: "call", terms, quote, network: NETWORK2 }, fetch2));
296839
296939
  }
296840
296940
  if (!confirm) {
296841
- const { shareWords: shareWords2 } = await Promise.resolve().then(() => (init_callRead(), callRead_exports));
296842
- const shareTotal = plan.legs.reduce((sum2, one) => sum2 + one.shareBase * one.escrowSpots, 0);
296941
+ const { feeSentence: feeSentence2 } = await Promise.resolve().then(() => (init_feeLine(), feeLine_exports));
296843
296942
  const held2 = await heldBy(acting, coin);
296844
296943
  return text4(
296845
296944
  [
296846
296945
  `Call "${slug}" locks ${callLocks(plan.totalBase, feeTotal, coinSymbol)} in ${order2.length} ${order2.length === 1 ? "Ask" : "Asks"}, posted one per call: ${order2.map((one) => LEG_WORDS[one]).join(", then ")}.${ids.length ? ` Posted already: ${ids.join(", ")}.` : ""}`,
296847
- /* A private call: the share is in that total, on top of what each person receives, and said before the yes; a pro ask says it in its one sentence. */
296848
- ...moneySentence ? [moneySentence] : plan.share ? [`Of that, the network's share is ${pays(shareTotal, coinSymbol)} if every spot is paid (${shareWords2(plan.share, "today")}), on top of what each person receives: ${pays(plan.legs.find((one) => one.leg === "entries").payBase, coinSymbol)} for each passing entry.`] : [],
296946
+ /* A private call: the fee is in that total, said before the yes in one figure of what the poster pays; a pro ask says it in its one sentence. */
296947
+ ...moneySentence ? [moneySentence] : plan.share ? [`Each hired person receives ${pays(plan.legs.find((one) => one.leg === "entries").payBase, coinSymbol)} for each passing entry. ${feeSentence2(plan.share, NETWORK2, "Each hired person")}`] : [],
296849
296948
  `Next: ${LEG_WORDS[next]}, locking ${callLocks(leg.escrowBase, leg.feeBase, coinSymbol)}, plus a small network fee.`,
296850
296949
  actingNote(acting) ?? (review ? "Posting from your own account (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}.`),
296851
296950
  ...pick2 ? [pick2] : [],
296852
296951
  `Spent today (call): ${spentWords("call", coinSymbol)}. Every tool together: ${spentAllWords()}.`,
296853
296952
  ...call3.capWords(plan.legs.filter((one) => !draft.posted[one.leg]), CAP_BASE, coinSymbol, NETWORK2),
296854
- ...call3.balanceWarnings(held2, { totalBase: plan.totalBase, feeTotal, symbol: coinSymbol, network: NETWORK2, feeWords }),
296953
+ ...call3.balanceWarnings(held2, { totalBase: plan.totalBase, feeTotal, symbol: coinSymbol, network: NETWORK2, feeWords: feeWords2 }),
296855
296954
  "",
296856
296955
  `To go ahead, call again with confirm: "${total}".`
296857
296956
  ].join("\n")
@@ -296871,7 +296970,7 @@ Then call post_call again. Nothing was posted.`);
296871
296970
  feeDenom: FEE_DENOM,
296872
296971
  balanceBase: async (who, denom) => Number((await balancesOfStrict2(who))[denom] ?? 0),
296873
296972
  face: (base6) => pays(base6, coinSymbol),
296874
- feeFace: feeWords,
296973
+ feeFace: feeWords2,
296875
296974
  coinFixed: Boolean(checker.fee),
296876
296975
  site: SITE
296877
296976
  });
@@ -297133,7 +297232,7 @@ ${allowLine(SITE, posted.id, approver, acting?.principal ?? null)}` : `Every rep
297133
297232
  const answer = await invitesModule.myInvitations(SITE, signer3);
297134
297233
  if ("refused" in answer) return text4(`Refused: ${answer.refused}`);
297135
297234
  const missed = answer.missed ? `Not shown: ${answer.missed}.` : "";
297136
- if (answer.invited.length === 0) return text4([`Nothing yet: no poster has put this key${signer3.principal && signer3.principal !== signer3.address ? ", or the person it acts for," : ""} on a list. A person's own invitations are also on the site, signed in: ${EXPERT_SITE}/pro/work for reviews, ${SITE}/opencall for other calls.`, missed].filter(Boolean).join("\n"));
297235
+ if (answer.invited.length === 0) return text4([`Nothing yet: no poster has put this key${signer3.principal && signer3.principal !== signer3.address ? ", or the person it acts for," : ""} on a list. A person's own invitations are also on the site, signed in: ${EXPERT_SITE}/me/work for pro asks, ${SITE}/opencall for other calls.`, missed].filter(Boolean).join("\n"));
297137
297236
  const rooms = await roomsModule.myRooms(SITE, signer3).catch(() => ({ refused: "unreachable" }));
297138
297237
  const roomOf = "refused" in rooms ? null : new Map(rooms.rooms.filter((one) => one.role === "pro").map((one) => [one.collectionId, one]));
297139
297238
  const me = roomsModule.actorOf(signer3);
@@ -297141,7 +297240,7 @@ ${allowLine(SITE, posted.id, approver, acting?.principal ?? null)}` : `Every rep
297141
297240
  [
297142
297241
  "Invited to:",
297143
297242
  ...answer.invited.flatMap((one) => [
297144
- ` #${one.collectionId}${one.title ? ` \xB7 ${one.title}` : ""}${one.answer ? ` \xB7 ${invitesModule.answerLine(one)}` : ""}${one.campaign?.startsWith("call-") ? ` \xB7 ${isReviewTitle(one.title) ? `${EXPERT_SITE}/pro/work/${one.collectionId}` : askPage(SITE, one.collectionId, { campaign: one.campaign, title: one.title })}` : ""} \xB7 ${one.answer === "sent" ? `accept_invitation "${one.collectionId}" or decline_invitation` : `read_ask "${one.collectionId}"`}`,
297243
+ ` #${one.collectionId}${one.title ? ` \xB7 ${one.title}` : ""}${one.answer ? ` \xB7 ${invitesModule.answerLine(one)}` : ""}${one.campaign?.startsWith("call-") ? ` \xB7 ${isReviewTitle(one.title) ? `${EXPERT_SITE}/me/work/${one.collectionId}` : askPage(SITE, one.collectionId, { campaign: one.campaign, title: one.title })}` : ""} \xB7 ${one.answer === "sent" ? `accept_invitation "${one.collectionId}" or decline_invitation` : `read_ask "${one.collectionId}"`}`,
297145
297244
  ...one.note ? [` The poster's note to you: ${asData("invitation-note", one.note)}`] : [],
297146
297245
  ...roomOf ? [roomsModule.invitationRoomLine(one, roomOf.get(one.collectionId) ?? null, { site: SITE, me })].filter((line2) => Boolean(line2)) : []
297147
297246
  ]),
@@ -297155,12 +297254,78 @@ ${allowLine(SITE, posted.id, approver, acting?.principal ?? null)}` : `Every rep
297155
297254
  const peeked = await invitesModule.peekInvitation(SITE, which.token).catch(() => null);
297156
297255
  return peeked ? `pro ask #${peeked.collectionId}${peeked.title ? ` (${peeked.title.replace(/^Expert review:\s*/, "")})` : ""}` : "the pro ask this link names";
297157
297256
  };
297257
+ if (offered("my_pro_asks")) server2.registerTool("my_pro_asks", { ...meta3("my_pro_asks"), inputSchema: {} }, async () => {
297258
+ const [invitesModule, proAsksModule] = await Promise.all([Promise.resolve().then(() => (init_invites(), invites_exports)), Promise.resolve().then(() => (init_proAsks(), proAsks_exports))]);
297259
+ const signer3 = await inviteSigner(await principal2());
297260
+ const answer = await invitesModule.myCalls(SITE, signer3);
297261
+ if ("refused" in answer) return text4(`Refused: ${answer.refused}`);
297262
+ const missed = answer.missed ? `Not shown: ${answer.missed}.` : "";
297263
+ const asks = proAsksModule.proAsksOf(answer.calls);
297264
+ if (asks.length === 0) return text4([`No pro asks yet${signer3.principal && signer3.principal !== signer3.address ? " from this key or the person it acts for" : ""}. draft_call with kind "review" sets one up.`, missed].filter(Boolean).join("\n"));
297265
+ const owners = [...new Set(asks.map((one) => one.owner ?? signer3.address))];
297266
+ const [held, waiting] = await Promise.all([
297267
+ (async () => {
297268
+ const { asksHolding: asksHolding2 } = await Promise.resolve().then(() => (init_onAsks(), onAsks_exports));
297269
+ const { graphqlUrl: graphqlUrl4, restUrl: restUrl2 } = await Promise.resolve().then(() => (init_env(), env_exports));
297270
+ const lists = await Promise.all(owners.map((who) => asksHolding2(who, { graphqlUrl: graphqlUrl4(), restUrl: restUrl2() })));
297271
+ return new Map(lists.flat().map((one) => [one.collectionId, pays(one.held, "upay")]));
297272
+ })().catch(() => null),
297273
+ Promise.all(owners.map((who) => fetch2(`${SITE}/api/waiting?address=${encodeURIComponent(who)}`, { signal: AbortSignal.timeout(2e4) }).then((r) => r.ok ? r.json() : null))).then((answers) => answers.flatMap((one) => one?.waiting ?? [])).catch(() => null)
297274
+ ]);
297275
+ return text4([`Pro asks (${asks.length}):`, ...proAsksModule.proAskLines(EXPERT_SITE, asks, { held, waiting }), missed].filter(Boolean).join("\n"));
297276
+ });
297277
+ if (offered("approve_report")) server2.registerTool(
297278
+ "approve_report",
297279
+ {
297280
+ ...meta3("approve_report"),
297281
+ inputSchema: {
297282
+ collectionId: external_exports.string().regex(/^\d+$/).describe("The pro ask's number (my_pro_asks shows it)."),
297283
+ claimId: external_exports.string().min(1).max(128).describe("The report's id: the claim my_pro_asks or the ask's page names."),
297284
+ confirm: external_exports.boolean().optional().describe("Leave out to see what approving pays and whose OK it is. true approves.")
297285
+ }
297286
+ },
297287
+ async ({ collectionId, claimId, confirm }) => {
297288
+ const proAsksModule = await Promise.resolve().then(() => (init_proAsks(), proAsks_exports));
297289
+ const mine = await address2();
297290
+ const acting = await principal2();
297291
+ const who = acting?.principal ?? mine;
297292
+ const queue = await fetch2(`${SITE}/api/waiting?address=${encodeURIComponent(who)}`, { signal: AbortSignal.timeout(2e4) }).then((r) => r.ok ? r.json() : null).catch(() => null);
297293
+ if (!queue || queue.unavailable) return text4("Refused: the reports waiting for an OK could not be read just now. Nothing was sent; try again in a minute.");
297294
+ const item = (queue.waiting ?? []).find((one) => one.collectionId === collectionId && one.claimId === claimId);
297295
+ if (!item) return text4(`Nothing to approve: report ${claimId} on pro ask #${collectionId} is not waiting for ${who === mine ? "this key's" : `${who}'s`} OK. It may be approved already, still being checked, or another account's to approve. The report: ${proAsksModule.reportPage(EXPERT_SITE, collectionId, claimId)}`);
297296
+ const approver = item.approver ?? who;
297297
+ const canSign = approver === mine;
297298
+ const paid = item.pays ? face(item.pays.value, item.pays.symbol) : null;
297299
+ const facts = { collectionId, claimId, pro: item.agentAddress, pays: paid, approver, canSign };
297300
+ if (!confirm || !canSign) return text4(proAsksModule.approvePreview(EXPERT_SITE, facts));
297301
+ const ask = await readJson3(`${SITE}/api/oracle/${encodeURIComponent(collectionId)}`).catch(() => null);
297302
+ if (!ask?.oracle?.url) return text4("Refused: the verifier for this pro ask could not be reached. Nothing was sent; try again in a minute.");
297303
+ const review = await Promise.resolve().then(() => (init_personReview(), personReview_exports));
297304
+ const key = { address: mine, mnemonic: (await loadKey2()).mnemonic };
297305
+ const held = await review.readHeldClaim(ask.oracle.url, key, claimId);
297306
+ if (!held?.decision || held.decision.status !== "pending_review") return text4(`Nothing to approve: the verifier has no decision waiting on report ${claimId}${held?.decision?.status ? ` (it is ${held.decision.status.replace(/_/g, " ")})` : ""}. Nothing was sent.`);
297307
+ const protocol = item.protocol ?? ask.protocol ?? null;
297308
+ const { engineUrl: engineUrl2 } = await Promise.resolve().then(() => (init_engineUrl(), engineUrl_exports));
297309
+ const questions = protocol && engineUrl2() ? await review.reviewQuestions(engineUrl2(), protocol, held.decision.pendingTasks).catch(() => null) : null;
297310
+ if (questions?.some((one) => one.answer.type !== "boolean")) return text4(`Not approved: this report has a question that is not a yes or no, so its OK is given on the report's page: ${proAsksModule.reportPage(EXPERT_SITE, collectionId, claimId)}#decide. Nothing was sent.`);
297311
+ const answers = Object.fromEntries(held.decision.pendingTasks.map((task) => [task, true]));
297312
+ const receipt = await askReceipt(collectionId);
297313
+ if (receipt?.owner === mine) {
297314
+ const ends = ask.endsAt ? Math.floor(Date.parse(ask.endsAt) / 1e3) : null;
297315
+ await review.ensureReviewing({ owner: key, oracleUrl: ask.oracle.url, collectionId, reviewer: mine, endsAt: Number.isFinite(ends) ? ends : null }).catch(() => null);
297316
+ }
297317
+ const sent = await review.sendReview({ reviewer: key, oracleUrl: ask.oracle.url, collectionId, claimId, answers });
297318
+ if (!sent.ok) return text4(`Not approved: ${sent.why}${sent.detail ? ` (${sent.detail})` : ""}. Nothing was paid.`);
297319
+ return text4(`Approved report ${claimId} on pro ask #${collectionId}. The verifier records the OK and pays ${item.agentAddress}${paid ? ` ${paid}` : ""} on the ledger in a minute or two.
297320
+ The report: ${proAsksModule.reportPage(EXPERT_SITE, collectionId, claimId)}`);
297321
+ }
297322
+ );
297158
297323
  if (offered("accept_invitation")) server2.registerTool(
297159
297324
  "accept_invitation",
297160
297325
  {
297161
297326
  ...meta3("accept_invitation"),
297162
297327
  inputSchema: {
297163
- invitation: external_exports.string().min(1).max(512).describe("The invitation: its link (\u2026/pro/i/\u2026), or the pro ask's number (my_invitations shows it)."),
297328
+ invitation: external_exports.string().min(1).max(512).describe("The invitation: its link (\u2026/i/\u2026), or the pro ask's number (my_invitations shows it)."),
297164
297329
  confirm: external_exports.boolean().optional().describe("false (the default) previews; true accepts, once the person has said yes.")
297165
297330
  }
297166
297331
  },
@@ -297184,7 +297349,7 @@ ${allowLine(SITE, posted.id, approver, acting?.principal ?? null)}` : `Every rep
297184
297349
  return text4(
297185
297350
  [
297186
297351
  done.state === "already" ? `Already accepted #${id}.` : `Accepted #${id}${acting ? ` as ${acting.principal}` : ""}. The builder knows.`,
297187
- `Your job page: ${EXPERT_SITE}/pro/work/${id}`,
297352
+ `Your job page: ${EXPERT_SITE}/me/work/${id}`,
297188
297353
  `Next: read_ask "${id}" for the brief, read_messages collectionId "${id}" for the builder's note, then submit_work when the person says so.`
297189
297354
  ].join("\n")
297190
297355
  );
@@ -297195,7 +297360,7 @@ ${allowLine(SITE, posted.id, approver, acting?.principal ?? null)}` : `Every rep
297195
297360
  {
297196
297361
  ...meta3("decline_invitation"),
297197
297362
  inputSchema: {
297198
- invitation: external_exports.string().min(1).max(512).describe("The invitation: its link (\u2026/pro/i/\u2026), or the pro ask's number (my_invitations shows it)."),
297363
+ invitation: external_exports.string().min(1).max(512).describe("The invitation: its link (\u2026/i/\u2026), or the pro ask's number (my_invitations shows it)."),
297199
297364
  note: external_exports.string().max(200).optional().describe("A line for the builder, in the person's words: only they read it. Optional."),
297200
297365
  confirm: external_exports.boolean().optional().describe("false (the default) previews; true declines, once the person has said yes.")
297201
297366
  }
@@ -297287,7 +297452,7 @@ The room's page: ${roomPage(SITE, collectionId, page?.profile.handle ?? sent.pro
297287
297452
  const pageInput = external_exports.object({
297288
297453
  verificationServices: external_exports.array(verificationServiceSchema).max(6).optional(),
297289
297454
  verificationCredentials: external_exports.array(credentialReferenceSchema).max(12).optional(),
297290
- handle: external_exports.string().max(30).describe("Lowercase letters, digits and dashes, 3 to 30, starting with a letter. The page's address: /pro/pros/<handle>."),
297455
+ handle: external_exports.string().max(30).describe("Lowercase letters, digits and dashes, 3 to 30, starting with a letter. The page's address: /pros/<handle>."),
297291
297456
  name: external_exports.string().max(80),
297292
297457
  headline: external_exports.string().max(160).optional().describe("One line: what they do, in their words."),
297293
297458
  about: external_exports.string().max(2e3).optional().describe("The summary, a few sentences in their words."),
@@ -297391,12 +297556,12 @@ No private link was made: ${link5.refused}.`;
297391
297556
  const page = await invitesModule.readMyPage(SITE, await inviteSigner(acting));
297392
297557
  if (!page) return text4("No pro page yet. Next: connect_linkedin (about a minute).");
297393
297558
  return text4(`${invitesModule.pageWords(page, SITE)}
297394
- ${page.profile.listed === false ? `Your page is private. Preview it at ${EXPERT_SITE}/pro/profile/preview (only you can see it), or ask me for your private link.
297559
+ ${page.profile.listed === false ? `Your page is private. Preview it at ${EXPERT_SITE}/me/page/preview (only you can see it), or ask me for your private link.
297395
297560
  ` : ""}${page.shared ? 'Private link: there is one (it is shown only when made; set_my_page with share_link: "replace" gives a new one and switches the old one off).' : 'Private link: none (set_my_page with share_link: "create" makes one).'}
297396
297561
 
297397
297562
  ${page.linkedin?.credentialUrl || page.linkedin?.credential ? `${credentialWords(page.linkedin, page.profile.listed !== false)}
297398
297563
 
297399
- ` : ""}Edit the profile at ${EXPERT_SITE}/pro/profile.${page.linkedin ? "" : " Next: connect_linkedin verifies it, so you can be invited to pro asks."}`);
297564
+ ` : ""}Edit the profile at ${EXPERT_SITE}/me/page/edit.${page.linkedin ? "" : " Next: connect_linkedin verifies it, so you can be invited to pro asks."}`);
297400
297565
  });
297401
297566
  if (offered("find_pro")) server2.registerTool(
297402
297567
  "find_pro",
@@ -297426,7 +297591,7 @@ ${page.linkedin?.credentialUrl || page.linkedin?.credential ? `${credentialWords
297426
297591
  return text4(pros.prosWords(found, { site: SITE, q, available }));
297427
297592
  }
297428
297593
  );
297429
- const credentialWords = (linkedin, listed) => linkedin?.credentialUrl && listed ? `You also have a LinkedIn credential: it shows you logged in to this LinkedIn account, and any app can check it. Its public link: ${linkedin.credentialUrl}. Download it at ${EXPERT_SITE}/pro/profile.` : `You also have a LinkedIn credential: it shows you logged in to this LinkedIn account, and any app can check it. ${listed && !linkedin?.credential && !linkedin?.credentialUrl ? "It appears on your page once the page is published." : "Only you can see it while your page is private; it goes public when the page does."} Download it at ${EXPERT_SITE}/pro/profile.`;
297594
+ const credentialWords = (linkedin, listed) => linkedin?.credentialUrl && listed ? `You also have a LinkedIn credential: it shows you logged in to this LinkedIn account, and any app can check it. Its public link: ${linkedin.credentialUrl}. Download it at ${EXPERT_SITE}/me/page/edit.` : `You also have a LinkedIn credential: it shows you logged in to this LinkedIn account, and any app can check it. ${listed && !linkedin?.credential && !linkedin?.credentialUrl ? "It appears on your page once the page is published." : "Only you can see it while your page is private; it goes public when the page does."} Download it at ${EXPERT_SITE}/me/page/edit.`;
297430
297595
  if (offered("connect_linkedin")) server2.registerTool(
297431
297596
  "connect_linkedin",
297432
297597
  {
@@ -297475,7 +297640,7 @@ ${asData("linkedin", JSON.stringify(prefill, null, 2))}`
297475
297640
  const owner = acting?.principal ?? await address2();
297476
297641
  const page = await invitesModule.readMyPage(SITE, await inviteSigner(acting));
297477
297642
  if (!page) return text4(`${owner} has no page. Nothing to remove.`);
297478
- const where2 = `${SITE.replace(/\/+$/, "")}/pro/pros/${page.profile.handle}`;
297643
+ const where2 = `${SITE.replace(/\/+$/, "")}/pros/${page.profile.handle}`;
297479
297644
  if (!confirm)
297480
297645
  return text4(
297481
297646
  [
@@ -297516,15 +297681,15 @@ ${asData("linkedin", JSON.stringify(prefill, null, 2))}`
297516
297681
  }
297517
297682
  };
297518
297683
  let PLANS = DEPLOYMENT.plans;
297519
- function applyDeployment(fresh2) {
297520
- ASK_DENOM = fresh2.askDenom;
297684
+ function applyDeployment(fresh3) {
297685
+ ASK_DENOM = fresh3.askDenom;
297521
297686
  ASK_SYMBOL = symbolOf(ASK_DENOM);
297522
- CHECKER = fresh2.checker;
297523
- GATE = fresh2.gate;
297524
- CHECK_REWARD = fresh2.checkRewardBase;
297525
- ENGINE_ORACLE = fresh2.engine.oracle;
297526
- ENGINE_MODE = fresh2.engine.live;
297527
- PLANS = fresh2.plans;
297687
+ CHECKER = fresh3.checker;
297688
+ GATE = fresh3.gate;
297689
+ CHECK_REWARD = fresh3.checkRewardBase;
297690
+ ENGINE_ORACLE = fresh3.engine.oracle;
297691
+ ENGINE_MODE = fresh3.engine.live;
297692
+ PLANS = fresh3.plans;
297528
297693
  }
297529
297694
  const MAX_PER_PLAN = CAPS.perPlan;
297530
297695
  const PLAN_CAPS = { perAsk: MAX_PER_ASK, perPlan: MAX_PER_PLAN, perDayTotal: MAX_PER_DAY_TOTAL };
@@ -297535,8 +297700,8 @@ ${asData("linkedin", JSON.stringify(prefill, null, 2))}`
297535
297700
  return { cost: planCost2(plan, { reviewUsd: sameCoin ? world.leg / MICRO6 : 0, feeRate: 0 }), reviewsBase: sameCoin ? 0 : world.leg * planAnswerCount2(plan) };
297536
297701
  }
297537
297702
  async function reviewWords(world, reviewsBase) {
297538
- if (world.perCheck) return `Review fee: ${(await Promise.resolve().then(() => (init_reviewBilling(), reviewBilling_exports))).creditsCopy(null, feeWords(world.perCheck.price))}`;
297539
- return reviewsBase > 0 ? `Reviews: ${feeWords(reviewsBase)} on top, ${feeWords(world.leg)} for each answer, escrowed beside the rewards.` : "";
297703
+ if (world.perCheck) return `Review fee: ${(await Promise.resolve().then(() => (init_reviewBilling(), reviewBilling_exports))).creditsCopy(null, feeWords2(world.perCheck.price))}`;
297704
+ return reviewsBase > 0 ? `Reviews: ${feeWords2(reviewsBase)} on top, ${feeWords2(world.leg)} for each answer, escrowed beside the rewards.` : "";
297540
297705
  }
297541
297706
  const UNPRICED = "The verifier could not be priced just now, so no plan can be bought. Try again shortly. Nothing was posted.";
297542
297707
  async function registerPlan(cid, poster) {
@@ -297734,7 +297899,7 @@ ${asData("linkedin", JSON.stringify(prefill, null, 2))}`
297734
297899
  }
297735
297900
  if (confirm.trim() !== money3(cost.totalUsd)) return text4(`Refused: confirm said "${confirm}" but the total is ${money3(cost.totalUsd)}. Preview it again.`);
297736
297901
  if (!resuming) {
297737
- if (acting && reviewsBase > 0) return text4(`Refused: this plan's reviews escrow ${feeWords(reviewsBase)} beside its ${coinSymbol} rewards, and ${acting.principal}'s allowance covers ${coinSymbol} only. Use a verifier that bills per check (Pay for reviews), or post from this key. Nothing was posted.`);
297902
+ if (acting && reviewsBase > 0) return text4(`Refused: this plan's reviews escrow ${feeWords2(reviewsBase)} beside its ${coinSymbol} rewards, and ${acting.principal}'s allowance covers ${coinSymbol} only. Use a verifier that bills per check (Pay for reviews), or post from this key. Nothing was posted.`);
297738
297903
  const otherCoin = (await Promise.resolve().then(() => (init_hq(), hq_exports))).capCoinRefusal(acting, coin);
297739
297904
  if (otherCoin) return text4(`Refused: ${otherCoin}. Draft the plan again with draft_plan in that coin. Nothing was posted.`);
297740
297905
  if (acting) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ixo/asks-mcp",
3
- "version": "2.0.0-dev.2",
3
+ "version": "2.0.0-dev.4",
4
4
  "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.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {