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

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 +191 -30
  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":
@@ -263767,7 +263777,8 @@ function callReadout(terms, ctx) {
263767
263777
  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
263778
  const out = [
263769
263779
  `${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}`] : [],
263780
+ /* 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. */
263781
+ ...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
263782
  call3.title,
263772
263783
  call3.lede,
263773
263784
  ...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 +264285,7 @@ async function postNextLegNow(slug, ctx) {
264274
264285
  tag: tagOf(draft),
264275
264286
  ...draft.fields.cover?.url ? { cover: draft.fields.cover.url } : {},
264276
264287
  /* 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}`,
264288
+ url: draft.fields.kind === "review" ? `${ctx.site.replace(/\/+$/, "")}/me/pro-asks/${slug}` : `${ctx.site.replace(/\/+$/, "")}/opencall/${slug}`,
264278
264289
  ...dates?.closes ? { endsAt: new Date(new Date(dates.pick ?? dates.closes).getTime() + 30 * 24 * 3600 * 1e3) } : {}
264279
264290
  },
264280
264291
  { onMinted: remember2 }
@@ -265126,6 +265137,7 @@ __export(invites_exports, {
265126
265137
  inviteItemsOf: () => inviteItemsOf,
265127
265138
  linkedinStatus: () => linkedinStatus,
265128
265139
  markNoticesRead: () => markNoticesRead,
265140
+ myCalls: () => myCalls,
265129
265141
  myInvitations: () => myInvitations,
265130
265142
  noticeWords: () => noticeWords,
265131
265143
  pageWords: () => pageWords,
@@ -265268,6 +265280,23 @@ async function myInvitations(site, signer2, fetchImpl) {
265268
265280
  }
265269
265281
  return { invited };
265270
265282
  }
265283
+ async function myCalls(site, signer2, fetchImpl) {
265284
+ const calls = [];
265285
+ const person = signer2.principal && signer2.principal !== signer2.address ? signer2.principal : null;
265286
+ for (const onBehalfOf of [null, ...person ? [person] : []]) {
265287
+ const message = callsMessage(signer2.address);
265288
+ 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);
265289
+ const missed = (why) => ({ calls, missed: `the pro asks of ${onBehalfOf}, the account you act for, could not be read just now (${why})` });
265290
+ if (!answer.ok) {
265291
+ if (!onBehalfOf) return { refused: refusalOf(answer) };
265292
+ return missed(refusalOf(answer));
265293
+ }
265294
+ const rows2 = answer.body.calls ?? [];
265295
+ if (onBehalfOf && rows2.some((one) => one.owner && one.owner !== onBehalfOf)) return missed("the site does not read them for an agent yet");
265296
+ for (const one of rows2) if (!calls.some((seen2) => seen2.collectionId === one.collectionId)) calls.push(one);
265297
+ }
265298
+ return { calls };
265299
+ }
265271
265300
  async function readNotices(site, signer2, options = {}, fetchImpl) {
265272
265301
  const message = noticesMessage(signer2.address);
265273
265302
  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 +265313,7 @@ function noticeWords(notices, site) {
265284
265313
  return notices.map((one) => {
265285
265314
  const room = one.kind === "message" ? /\/with\/([^/?#]+)$/.exec(one.href)?.[1] : void 0;
265286
265315
  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;
265316
+ 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
265317
  return ` \xB7 ${one.headline}: ${one.line}${/["”.]$/.test(one.line) ? "" : "."} ${base6}${href}${next ? ` \xB7 ${next}` : ""}`;
265289
265318
  });
265290
265319
  }
@@ -267013,7 +267042,7 @@ var init_pairing = __esm({
267013
267042
  CODE = /^[a-f0-9]{16}$/;
267014
267043
  memoFor = (code10) => `openhands-connect:${code10}`;
267015
267044
  connectLinkWithCode = (site, agent, code10) => `${site}/connect?agent=${encodeURIComponent(agent)}&code=${code10}`;
267016
- pairingSite = (site, pairing) => pairing.reviews ? `${site.replace(/\/+$/, "")}/pro` : site;
267045
+ pairingSite = (site, _pairing) => site;
267017
267046
  }
267018
267047
  });
267019
267048
 
@@ -267088,6 +267117,8 @@ function roomRows(counts) {
267088
267117
  switch (room.key) {
267089
267118
  case "page":
267090
267119
  return { ...room, figure: null, after: null, ...counts.page === "listed" ? { chip: "Listed" } : counts.page === "private" ? { chip: "Private" } : counts.page === "verify" ? { verify: true } : {} };
267120
+ case "proAsks":
267121
+ return { ...room, ...counted(counts.proAsks ?? null, null) };
267091
267122
  case "work":
267092
267123
  return { ...room, ...counted(counts.work, "waiting") };
267093
267124
  case "messages":
@@ -267109,6 +267140,7 @@ var init_meRooms = __esm({
267109
267140
  "use strict";
267110
267141
  ROOMS = [
267111
267142
  { key: "page", word: "Your page", href: "/me/page" },
267143
+ { key: "proAsks", word: "Pro asks", href: "/me/pro-asks" },
267112
267144
  { key: "work", word: "Work", href: "/me/work" },
267113
267145
  { key: "messages", word: "Messages", href: "/me/messages" },
267114
267146
  { key: "inbox", word: "Inbox", href: "/me/check" },
@@ -267121,6 +267153,53 @@ var init_meRooms = __esm({
267121
267153
  }
267122
267154
  });
267123
267155
 
267156
+ // src/proAsks.ts
267157
+ var proAsks_exports = {};
267158
+ __export(proAsks_exports, {
267159
+ approvePreview: () => approvePreview,
267160
+ handOver: () => handOver,
267161
+ proAskLines: () => proAskLines,
267162
+ proAsksOf: () => proAsksOf,
267163
+ reportPage: () => reportPage,
267164
+ shortDate: () => shortDate
267165
+ });
267166
+ function proAskLines(site, calls, options = {}) {
267167
+ const now3 = options.now ?? Date.now();
267168
+ const base6 = site.replace(/\/+$/, "");
267169
+ return proAsksOf(calls).flatMap((one) => {
267170
+ const open = isOpen2(one, now3);
267171
+ const parts = [open ? "Open" : "Closed", `${one.count} of ${one.quota} reports`];
267172
+ const closes = open ? shortDate(one.end_date) : "";
267173
+ if (closes) parts.push(`closes ${closes}`);
267174
+ const held = options.held?.get(one.collectionId);
267175
+ if (held) parts.push(`holds ${held}`);
267176
+ const toApprove = options.waiting?.filter((item) => item.collectionId === one.collectionId).length ?? 0;
267177
+ if (toApprove) parts.push(`${toApprove} ${toApprove === 1 ? "report" : "reports"} to approve`);
267178
+ return [`#${one.collectionId} ${bare(one.title)} \xB7 ${parts.join(" \xB7 ")}`, ` ${base6}/me/pro-asks/${one.collectionId}`];
267179
+ });
267180
+ }
267181
+ function approvePreview(site, input) {
267182
+ const page = reportPage(site, input.collectionId, input.claimId);
267183
+ 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.`;
267184
+ 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");
267185
+ }
267186
+ var bare, shortDate, isOpen2, proAsksOf, reportPage, handOver;
267187
+ var init_proAsks = __esm({
267188
+ "src/proAsks.ts"() {
267189
+ "use strict";
267190
+ init_world();
267191
+ bare = (title) => (title ?? "").replace(/^Expert review:\s*/, "") || "A pro ask";
267192
+ shortDate = (iso) => {
267193
+ const at = iso ? new Date(iso) : null;
267194
+ return at && !Number.isNaN(at.getTime()) ? at.toLocaleDateString("en-GB", { day: "numeric", month: "short", timeZone: "UTC" }) : "";
267195
+ };
267196
+ isOpen2 = (one, now3) => one.state === 0 && (!one.end_date || Date.parse(one.end_date) > now3);
267197
+ proAsksOf = (calls) => calls.filter((one) => isReviewTitle(one.title)).sort((a, b) => Number(b.collectionId) - Number(a.collectionId));
267198
+ reportPage = (site, collectionId, claimId) => `${site.replace(/\/+$/, "")}/pro/reviews/${collectionId}/report/${encodeURIComponent(claimId)}`;
267199
+ 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.`;
267200
+ }
267201
+ });
267202
+
267124
267203
  // src/verdictSource.ts
267125
267204
  var verdictSource_exports = {};
267126
267205
  __export(verdictSource_exports, {
@@ -292281,7 +292360,7 @@ import { randomBytes as randomBytes7 } from "node:crypto";
292281
292360
  // package.json
292282
292361
  var package_default = {
292283
292362
  name: "@ixo/asks-mcp",
292284
- version: "2.0.0-dev.2",
292363
+ version: "2.0.0-dev.3",
292285
292364
  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
292365
  license: "Apache-2.0",
292287
292366
  repository: {
@@ -292627,6 +292706,20 @@ var TOOLS = {
292627
292706
  hosted: false,
292628
292707
  annotations: { ...reads, readOnlyHint: true }
292629
292708
  },
292709
+ my_pro_asks: {
292710
+ title: "List the pro asks the person posted",
292711
+ 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.`,
292712
+ spends: false,
292713
+ hosted: false,
292714
+ annotations: { ...reads, readOnlyHint: true }
292715
+ },
292716
+ approve_report: {
292717
+ title: "Approve a report on a pro ask (spends money)",
292718
+ 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.",
292719
+ spends: true,
292720
+ hosted: false,
292721
+ annotations: acts
292722
+ },
292630
292723
  accept_invitation: {
292631
292724
  title: "Accept an invitation to a pro ask",
292632
292725
  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 +293342,8 @@ function whyFromReceipt(receipt, rubric) {
293249
293342
  const push = (r) => {
293250
293343
  const clean6 = { ...r, label: scrub(r.label, 200), detail: scrub(r.detail) };
293251
293344
  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;
293345
+ const bare2 = clean6.detail === "Passed" || clean6.detail === "Did not pass";
293346
+ if (seen2.has(`${key}|${clean6.detail}`) || bare2 && seen2.has(key)) return;
293254
293347
  seen2.add(key).add(`${key}|${clean6.detail}`);
293255
293348
  reasons.push(clean6);
293256
293349
  };
@@ -294702,7 +294795,7 @@ Each is its own ask and pays on its own. Read one with read_ask before deciding.
294702
294795
  return [
294703
294796
  `${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
294797
  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}`}`
294798
+ `${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
294799
  ].filter(Boolean).join("\n");
294707
294800
  };
294708
294801
  const lines2 = board.rows.filter(within2).map((row) => {
@@ -295429,7 +295522,7 @@ ${SITE}/t/${earlier.id}`);
295429
295522
  const callPerCheck = async (checker = CHECKER) => (await Promise.resolve().then(() => (init_reviewBilling(), reviewBilling_exports))).deploymentBillsPerCheck(checker).catch(() => false);
295430
295523
  const callChecker2 = async (review) => (await Promise.resolve().then(() => (init_call2(), call_exports2))).callChecker(NETWORK2, review, CHECKER);
295431
295524
  const callSplit = async () => (await Promise.resolve().then(() => (init_query43(), query_exports43))).feeSplitOrNull();
295432
- async function sharedPays(leg, posted, bare = false) {
295525
+ async function sharedPays(leg, posted, bare2 = false) {
295433
295526
  if (leg?.is_oracle_payment !== true || !leg.amount?.[0]) return null;
295434
295527
  const { coinDisplay: coinDisplay2, faceOf: faceOf2 } = await Promise.resolve().then(() => (init_format(), format_exports));
295435
295528
  const { netOf: netOf2 } = await Promise.resolve().then(() => (init_share(), share_exports));
@@ -295439,7 +295532,7 @@ ${SITE}/t/${earlier.id}`);
295439
295532
  const split3 = posted ?? await callSplit();
295440
295533
  if (!split3) return `${said4(Number(amount3))} less the network's share (the fee split could not be read just now)`;
295441
295534
  const receives = netOf2(Number(amount3), split3);
295442
- if (bare) return said4(receives);
295535
+ if (bare2) return said4(receives);
295443
295536
  return `${said4(receives)} (the leg is ${said4(Number(amount3))}; the network keeps ${said4(Number(amount3) - receives)}: ${shareWords2(split3, posted ? "as posted" : "today")})`;
295444
295537
  }
295445
295538
  const callLocks = (rewardBase, feeBase, symbol2 = ASK_SYMBOL) => `${pays(rewardBase, symbol2)}${feeBase > 0 ? ` and ${feeWords(feeBase)} for reviews` : ""}`;
@@ -296356,10 +296449,11 @@ ${pending.link}`);
296356
296449
  })();
296357
296450
  const rooms = await (async () => {
296358
296451
  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))]);
296452
+ 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
296453
  const signer3 = await inviteSigner(await principal2());
296361
- const [page, invited, mineRooms, waiting, agents] = await Promise.all([
296454
+ const [page, calls, invited, mineRooms, waiting, agents] = await Promise.all([
296362
296455
  invitesModule.readMyPage(SITE, signer3).catch(() => void 0),
296456
+ invitesModule.myCalls(SITE, signer3).catch(() => null),
296363
296457
  invitesModule.myInvitations(SITE, signer3).catch(() => null),
296364
296458
  roomsModule.myRooms(SITE, signer3).catch(() => null),
296365
296459
  fetch2(`${SITE}/api/waiting?address=${encodeURIComponent(signer3.address)}`, { signal: AbortSignal.timeout(2e4) }).then((r) => r.ok ? r.json() : null).catch(() => null),
@@ -296370,6 +296464,7 @@ ${pending.link}`);
296370
296464
  "You, as the account area says it:",
296371
296465
  ...roomLines2({
296372
296466
  page: page === void 0 ? null : page === null ? "none" : !verified ? "verify" : page.profile.listed === false ? "private" : "listed",
296467
+ proAsks: calls && "calls" in calls ? proAsksOf2(calls.calls).length : null,
296373
296468
  work: invited && "invited" in invited ? invited.invited.filter((one) => one.answer === "sent").length : null,
296374
296469
  unread: mineRooms && "rooms" in mineRooms ? mineRooms.rooms.reduce((sum2, one) => sum2 + one.unread, 0) : null,
296375
296470
  inbox: Array.isArray(waiting?.waiting) ? waiting.waiting.length : null,
@@ -296744,7 +296839,7 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
296744
296839
  `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
296840
  ...draft.fields.kind === "review" ? [
296746
296841
  `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."
296842
+ 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
296843
  ] : [],
296749
296844
  ...warnings2.length ? ["", "Warnings:", ...warnings2.map((one) => ` - ${one}`)] : [],
296750
296845
  "",
@@ -296772,7 +296867,7 @@ Mapping pays for the report. What happens to a mapped pin afterwards is a separa
296772
296867
  if (!found) return text4(`No draft called "${slug}". Start one with draft_call.`);
296773
296868
  const review = found.fields.kind === "review";
296774
296869
  const checker = await callChecker2(review);
296775
- const linkOf = (posted2) => review ? `${EXPERT_SITE}/pro/reviews/${posted2.entries?.id ?? slug}` : `${SITE}/opencall/${slug}`;
296870
+ const linkOf = (posted2) => review ? `${EXPERT_SITE}/me/pro-asks/${posted2.entries?.id ?? slug}` : `${SITE}/opencall/${slug}`;
296776
296871
  const settled = found.inFlight ? await call3.settleInFlight(slug, call3.indexLookup) : null;
296777
296872
  if (settled?.state === "pending") return text4(settled.words);
296778
296873
  if (settled?.state === "landed") {
@@ -297133,7 +297228,7 @@ ${allowLine(SITE, posted.id, approver, acting?.principal ?? null)}` : `Every rep
297133
297228
  const answer = await invitesModule.myInvitations(SITE, signer3);
297134
297229
  if ("refused" in answer) return text4(`Refused: ${answer.refused}`);
297135
297230
  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"));
297231
+ 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
297232
  const rooms = await roomsModule.myRooms(SITE, signer3).catch(() => ({ refused: "unreachable" }));
297138
297233
  const roomOf = "refused" in rooms ? null : new Map(rooms.rooms.filter((one) => one.role === "pro").map((one) => [one.collectionId, one]));
297139
297234
  const me = roomsModule.actorOf(signer3);
@@ -297141,7 +297236,7 @@ ${allowLine(SITE, posted.id, approver, acting?.principal ?? null)}` : `Every rep
297141
297236
  [
297142
297237
  "Invited to:",
297143
297238
  ...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}"`}`,
297239
+ ` #${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
297240
  ...one.note ? [` The poster's note to you: ${asData("invitation-note", one.note)}`] : [],
297146
297241
  ...roomOf ? [roomsModule.invitationRoomLine(one, roomOf.get(one.collectionId) ?? null, { site: SITE, me })].filter((line2) => Boolean(line2)) : []
297147
297242
  ]),
@@ -297155,12 +297250,78 @@ ${allowLine(SITE, posted.id, approver, acting?.principal ?? null)}` : `Every rep
297155
297250
  const peeked = await invitesModule.peekInvitation(SITE, which.token).catch(() => null);
297156
297251
  return peeked ? `pro ask #${peeked.collectionId}${peeked.title ? ` (${peeked.title.replace(/^Expert review:\s*/, "")})` : ""}` : "the pro ask this link names";
297157
297252
  };
297253
+ if (offered("my_pro_asks")) server2.registerTool("my_pro_asks", { ...meta3("my_pro_asks"), inputSchema: {} }, async () => {
297254
+ const [invitesModule, proAsksModule] = await Promise.all([Promise.resolve().then(() => (init_invites(), invites_exports)), Promise.resolve().then(() => (init_proAsks(), proAsks_exports))]);
297255
+ const signer3 = await inviteSigner(await principal2());
297256
+ const answer = await invitesModule.myCalls(SITE, signer3);
297257
+ if ("refused" in answer) return text4(`Refused: ${answer.refused}`);
297258
+ const missed = answer.missed ? `Not shown: ${answer.missed}.` : "";
297259
+ const asks = proAsksModule.proAsksOf(answer.calls);
297260
+ 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"));
297261
+ const owners = [...new Set(asks.map((one) => one.owner ?? signer3.address))];
297262
+ const [held, waiting] = await Promise.all([
297263
+ (async () => {
297264
+ const { asksHolding: asksHolding2 } = await Promise.resolve().then(() => (init_onAsks(), onAsks_exports));
297265
+ const { graphqlUrl: graphqlUrl4, restUrl: restUrl2 } = await Promise.resolve().then(() => (init_env(), env_exports));
297266
+ const lists = await Promise.all(owners.map((who) => asksHolding2(who, { graphqlUrl: graphqlUrl4(), restUrl: restUrl2() })));
297267
+ return new Map(lists.flat().map((one) => [one.collectionId, pays(one.held, "upay")]));
297268
+ })().catch(() => null),
297269
+ 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)
297270
+ ]);
297271
+ return text4([`Pro asks (${asks.length}):`, ...proAsksModule.proAskLines(EXPERT_SITE, asks, { held, waiting }), missed].filter(Boolean).join("\n"));
297272
+ });
297273
+ if (offered("approve_report")) server2.registerTool(
297274
+ "approve_report",
297275
+ {
297276
+ ...meta3("approve_report"),
297277
+ inputSchema: {
297278
+ collectionId: external_exports.string().regex(/^\d+$/).describe("The pro ask's number (my_pro_asks shows it)."),
297279
+ claimId: external_exports.string().min(1).max(128).describe("The report's id: the claim my_pro_asks or the ask's page names."),
297280
+ confirm: external_exports.boolean().optional().describe("Leave out to see what approving pays and whose OK it is. true approves.")
297281
+ }
297282
+ },
297283
+ async ({ collectionId, claimId, confirm }) => {
297284
+ const proAsksModule = await Promise.resolve().then(() => (init_proAsks(), proAsks_exports));
297285
+ const mine = await address2();
297286
+ const acting = await principal2();
297287
+ const who = acting?.principal ?? mine;
297288
+ const queue = await fetch2(`${SITE}/api/waiting?address=${encodeURIComponent(who)}`, { signal: AbortSignal.timeout(2e4) }).then((r) => r.ok ? r.json() : null).catch(() => null);
297289
+ 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.");
297290
+ const item = (queue.waiting ?? []).find((one) => one.collectionId === collectionId && one.claimId === claimId);
297291
+ 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)}`);
297292
+ const approver = item.approver ?? who;
297293
+ const canSign = approver === mine;
297294
+ const paid = item.pays ? face(item.pays.value, item.pays.symbol) : null;
297295
+ const facts = { collectionId, claimId, pro: item.agentAddress, pays: paid, approver, canSign };
297296
+ if (!confirm || !canSign) return text4(proAsksModule.approvePreview(EXPERT_SITE, facts));
297297
+ const ask = await readJson3(`${SITE}/api/oracle/${encodeURIComponent(collectionId)}`).catch(() => null);
297298
+ 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.");
297299
+ const review = await Promise.resolve().then(() => (init_personReview(), personReview_exports));
297300
+ const key = { address: mine, mnemonic: (await loadKey2()).mnemonic };
297301
+ const held = await review.readHeldClaim(ask.oracle.url, key, claimId);
297302
+ 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.`);
297303
+ const protocol = item.protocol ?? ask.protocol ?? null;
297304
+ const { engineUrl: engineUrl2 } = await Promise.resolve().then(() => (init_engineUrl(), engineUrl_exports));
297305
+ const questions = protocol && engineUrl2() ? await review.reviewQuestions(engineUrl2(), protocol, held.decision.pendingTasks).catch(() => null) : null;
297306
+ 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.`);
297307
+ const answers = Object.fromEntries(held.decision.pendingTasks.map((task) => [task, true]));
297308
+ const receipt = await askReceipt(collectionId);
297309
+ if (receipt?.owner === mine) {
297310
+ const ends = ask.endsAt ? Math.floor(Date.parse(ask.endsAt) / 1e3) : null;
297311
+ await review.ensureReviewing({ owner: key, oracleUrl: ask.oracle.url, collectionId, reviewer: mine, endsAt: Number.isFinite(ends) ? ends : null }).catch(() => null);
297312
+ }
297313
+ const sent = await review.sendReview({ reviewer: key, oracleUrl: ask.oracle.url, collectionId, claimId, answers });
297314
+ if (!sent.ok) return text4(`Not approved: ${sent.why}${sent.detail ? ` (${sent.detail})` : ""}. Nothing was paid.`);
297315
+ 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.
297316
+ The report: ${proAsksModule.reportPage(EXPERT_SITE, collectionId, claimId)}`);
297317
+ }
297318
+ );
297158
297319
  if (offered("accept_invitation")) server2.registerTool(
297159
297320
  "accept_invitation",
297160
297321
  {
297161
297322
  ...meta3("accept_invitation"),
297162
297323
  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)."),
297324
+ 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
297325
  confirm: external_exports.boolean().optional().describe("false (the default) previews; true accepts, once the person has said yes.")
297165
297326
  }
297166
297327
  },
@@ -297184,7 +297345,7 @@ ${allowLine(SITE, posted.id, approver, acting?.principal ?? null)}` : `Every rep
297184
297345
  return text4(
297185
297346
  [
297186
297347
  done.state === "already" ? `Already accepted #${id}.` : `Accepted #${id}${acting ? ` as ${acting.principal}` : ""}. The builder knows.`,
297187
- `Your job page: ${EXPERT_SITE}/pro/work/${id}`,
297348
+ `Your job page: ${EXPERT_SITE}/me/work/${id}`,
297188
297349
  `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
297350
  ].join("\n")
297190
297351
  );
@@ -297195,7 +297356,7 @@ ${allowLine(SITE, posted.id, approver, acting?.principal ?? null)}` : `Every rep
297195
297356
  {
297196
297357
  ...meta3("decline_invitation"),
297197
297358
  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)."),
297359
+ 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
297360
  note: external_exports.string().max(200).optional().describe("A line for the builder, in the person's words: only they read it. Optional."),
297200
297361
  confirm: external_exports.boolean().optional().describe("false (the default) previews; true declines, once the person has said yes.")
297201
297362
  }
@@ -297287,7 +297448,7 @@ The room's page: ${roomPage(SITE, collectionId, page?.profile.handle ?? sent.pro
297287
297448
  const pageInput = external_exports.object({
297288
297449
  verificationServices: external_exports.array(verificationServiceSchema).max(6).optional(),
297289
297450
  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>."),
297451
+ 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
297452
  name: external_exports.string().max(80),
297292
297453
  headline: external_exports.string().max(160).optional().describe("One line: what they do, in their words."),
297293
297454
  about: external_exports.string().max(2e3).optional().describe("The summary, a few sentences in their words."),
@@ -297391,12 +297552,12 @@ No private link was made: ${link5.refused}.`;
297391
297552
  const page = await invitesModule.readMyPage(SITE, await inviteSigner(acting));
297392
297553
  if (!page) return text4("No pro page yet. Next: connect_linkedin (about a minute).");
297393
297554
  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.
297555
+ ${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
297556
  ` : ""}${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
297557
 
297397
297558
  ${page.linkedin?.credentialUrl || page.linkedin?.credential ? `${credentialWords(page.linkedin, page.profile.listed !== false)}
297398
297559
 
297399
- ` : ""}Edit the profile at ${EXPERT_SITE}/pro/profile.${page.linkedin ? "" : " Next: connect_linkedin verifies it, so you can be invited to pro asks."}`);
297560
+ ` : ""}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
297561
  });
297401
297562
  if (offered("find_pro")) server2.registerTool(
297402
297563
  "find_pro",
@@ -297426,7 +297587,7 @@ ${page.linkedin?.credentialUrl || page.linkedin?.credential ? `${credentialWords
297426
297587
  return text4(pros.prosWords(found, { site: SITE, q, available }));
297427
297588
  }
297428
297589
  );
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.`;
297590
+ 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
297591
  if (offered("connect_linkedin")) server2.registerTool(
297431
297592
  "connect_linkedin",
297432
297593
  {
@@ -297475,7 +297636,7 @@ ${asData("linkedin", JSON.stringify(prefill, null, 2))}`
297475
297636
  const owner = acting?.principal ?? await address2();
297476
297637
  const page = await invitesModule.readMyPage(SITE, await inviteSigner(acting));
297477
297638
  if (!page) return text4(`${owner} has no page. Nothing to remove.`);
297478
- const where2 = `${SITE.replace(/\/+$/, "")}/pro/pros/${page.profile.handle}`;
297639
+ const where2 = `${SITE.replace(/\/+$/, "")}/pros/${page.profile.handle}`;
297479
297640
  if (!confirm)
297480
297641
  return text4(
297481
297642
  [
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.3",
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": {