@ostraagent/mcp 0.1.10 → 0.1.11

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/ostra-mcp.js +171 -25
  2. package/package.json +1 -1
package/dist/ostra-mcp.js CHANGED
@@ -18,7 +18,7 @@ var __export = (target, all) => {
18
18
  var define_OSTRA_PACKAGED_default;
19
19
  var init_define_OSTRA_PACKAGED = __esm({
20
20
  "<define:OSTRA_PACKAGED>"() {
21
- define_OSTRA_PACKAGED_default = { version: "0.1.10", deployment: { version: 1, chainId: 4663, fromBlock: "91991509", token: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168", batchSettlement: "0x4020074e9dF2ce1deE5A9C1b5c3f541D02a10003", vaultImplementation: "0xec0ca4A1393D91c42cA809F7F9dd82Bd597E8E58", vaultBeacon: "0xE9F525B9EaeE48d13AEd28fE146F48f2832a18a0", vaultFactory: "0x043a15c514CFE2c797d33520bFF91a5DCdbf2979", poolImplementation: "0xC04d4a222dC1C966640B977608bE29949b0648ec", poolBeacon: "0x30669Ca534921038a531BBE27116d85bB7406fFF", pool: "0x6ea80F2462e5Fd783B0Ec3B2c7BaC96795980CD6", splitFactory: "0xAAf54A483B2c7c2c5ecFc1eDED5ed1619C1bA882", beaconOwner: "0x55533a51d9bd9b06F2D179d919BB31995EF96E5e", registry: "0xcE6Cc1f93744f4090DA1444Bb8590d9206157506", vaultModule: "0x55A89fF907f5F5756470e2ec5Dc5E4E1C54Da770", launchpad: { hook: "0x63A784afb8905452aDceC471a45E555F5caE68cC", fees: "0x8e79671DC9729476614C78aFcf2345D4c1963801", launchpad: "0xfC57162C0e3f9773F97fEa8d276e957d7c43A6C5", creatorSplit: "0x987994dE6A1d5303B9e3F50977cB0321BA4D033B", dispatcher: "0x44Bb65164b2855f8E80B142d5Fb9bDb72E869986", locker: "0xb593812363bAd70Ee381c7B54c4FC82b0BA3A4A7", devBuy: "0x0e1Fd799E136dcfEcad5695B07Ac5DAc5B2fF4bc", vesting: "0xadD9C1995910457312c156E7D8120D1b447E179a", airdrop: "0xBC798D956aF4c2B104FFC6106d5322294Bc103bb", owner: "0x6569E27A95a6A3E92A311B4Cc6600fed56aA50A2" }, breaker: "0x2E33244dE6A64007204f763e9ef81FC33F1a8e01" }, swapDeployment: { chainId: 4663, fromBlock: "94940585", router: "0x5b9686f384A5Fb768B2360cF0B893F0d1E960B01", stake: "0xaDA4FaB9c8aB9165bf4d6cD9d4Da6d6b32Bb4BC6", buyback: "0xd86083031b7Af66ecD5C9c5e90D4C6d8E661F3f5", spendVerifier: "0x0a0E28a81DE6289e5044950163FfE95b37180e3B", privatePool: "0xe4250895Da2499602Cee4e178Fb46A0cF39F5b76" }, prover: { wasm: "0xd5f3069334ab49a2f955e4b4be9493b80c3f7b2a85f4ff2073a327ef5fbc2747", glue: "0x4bc8bef7c755c83daaf833623f85473a20a7c40b390d8d639d9e4353618a0537" }, relayerUrl: "https://relayer.ostraagent.xyz", keeperUrl: "https://keeper.ostraagent.xyz", aggregatorUrl: "https://aggregator.ostraagent.xyz", aiUrl: "https://ai.ostraagent.xyz" };
21
+ define_OSTRA_PACKAGED_default = { version: "0.1.11", deployment: { version: 1, chainId: 4663, fromBlock: "91991509", token: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168", batchSettlement: "0x4020074e9dF2ce1deE5A9C1b5c3f541D02a10003", vaultImplementation: "0xec0ca4A1393D91c42cA809F7F9dd82Bd597E8E58", vaultBeacon: "0xE9F525B9EaeE48d13AEd28fE146F48f2832a18a0", vaultFactory: "0x043a15c514CFE2c797d33520bFF91a5DCdbf2979", poolImplementation: "0xC04d4a222dC1C966640B977608bE29949b0648ec", poolBeacon: "0x30669Ca534921038a531BBE27116d85bB7406fFF", pool: "0x6ea80F2462e5Fd783B0Ec3B2c7BaC96795980CD6", splitFactory: "0xAAf54A483B2c7c2c5ecFc1eDED5ed1619C1bA882", beaconOwner: "0x55533a51d9bd9b06F2D179d919BB31995EF96E5e", registry: "0xcE6Cc1f93744f4090DA1444Bb8590d9206157506", vaultModule: "0x55A89fF907f5F5756470e2ec5Dc5E4E1C54Da770", launchpad: { hook: "0x63A784afb8905452aDceC471a45E555F5caE68cC", fees: "0x8e79671DC9729476614C78aFcf2345D4c1963801", launchpad: "0xfC57162C0e3f9773F97fEa8d276e957d7c43A6C5", creatorSplit: "0x987994dE6A1d5303B9e3F50977cB0321BA4D033B", dispatcher: "0x44Bb65164b2855f8E80B142d5Fb9bDb72E869986", locker: "0xb593812363bAd70Ee381c7B54c4FC82b0BA3A4A7", devBuy: "0x0e1Fd799E136dcfEcad5695B07Ac5DAc5B2fF4bc", vesting: "0xadD9C1995910457312c156E7D8120D1b447E179a", airdrop: "0xBC798D956aF4c2B104FFC6106d5322294Bc103bb", owner: "0x6569E27A95a6A3E92A311B4Cc6600fed56aA50A2" }, breaker: "0x2E33244dE6A64007204f763e9ef81FC33F1a8e01" }, swapDeployment: { chainId: 4663, fromBlock: "94940585", router: "0x5b9686f384A5Fb768B2360cF0B893F0d1E960B01", stake: "0xaDA4FaB9c8aB9165bf4d6cD9d4Da6d6b32Bb4BC6", buyback: "0xd86083031b7Af66ecD5C9c5e90D4C6d8E661F3f5", spendVerifier: "0x0a0E28a81DE6289e5044950163FfE95b37180e3B", privatePool: "0xe4250895Da2499602Cee4e178Fb46A0cF39F5b76" }, prover: { wasm: "0xd5f3069334ab49a2f955e4b4be9493b80c3f7b2a85f4ff2073a327ef5fbc2747", glue: "0x4bc8bef7c755c83daaf833623f85473a20a7c40b390d8d639d9e4353618a0537" }, relayerUrl: "https://relayer.ostraagent.xyz", keeperUrl: "https://keeper.ostraagent.xyz", aggregatorUrl: "https://aggregator.ostraagent.xyz", aiUrl: "https://ai.ostraagent.xyz" };
22
22
  }
23
23
  });
24
24
 
@@ -15521,6 +15521,7 @@ async function readBorroPosition(client, stockVault, stock2) {
15521
15521
  listed,
15522
15522
  supply: e.totalSupplyAssets,
15523
15523
  borrowed: e.totalBorrowAssets,
15524
+ borrowedShares: e.totalBorrowShares,
15524
15525
  at: at.timestamp
15525
15526
  };
15526
15527
  }
@@ -20248,6 +20249,10 @@ import { decodeFunctionData as decodeFunctionData4, getAddress as getAddress17,
20248
20249
  import { x402Client, x402HTTPClient } from "@x402/core/client";
20249
20250
  import { toClientEvmSigner } from "@x402/evm";
20250
20251
  import { BatchSettlementEvmScheme } from "@x402/evm/batch-settlement/client";
20252
+ function notLandedError(attempts, lastReason) {
20253
+ const reason = lastReason.trim().slice(0, NOT_SENT_MESSAGE_MAX);
20254
+ return new Error(`the relayer could not land the request in ${attempts} attempts${reason === "" ? "" : `; it answered last: ${reason}`}`);
20255
+ }
20251
20256
  function address(value, what) {
20252
20257
  if (typeof value !== "string" || !isAddress6(value, { strict: false })) throw new Error(`${what} is not an address`);
20253
20258
  return getAddress17(value);
@@ -20296,6 +20301,25 @@ function relayerChannelDeposit(needed, vaultBalance, keep = 0n) {
20296
20301
  const want = needed > RELAYER_CHANNEL_DEPOSIT ? needed : RELAYER_CHANNEL_DEPOSIT;
20297
20302
  return want < available ? want : available;
20298
20303
  }
20304
+ function relayerSpendPlan(p) {
20305
+ const rooms = [...p.rooms];
20306
+ let balance = p.balance;
20307
+ let debt = p.debt;
20308
+ for (const a of p.amounts) {
20309
+ const i = debt > 0n ? -1 : rooms.findIndex((r) => r >= a);
20310
+ if (i >= 0) {
20311
+ rooms[i] = rooms[i] - a;
20312
+ continue;
20313
+ }
20314
+ const needed = debt + p.openCost + a;
20315
+ if (balance < needed) return false;
20316
+ const deposit = relayerChannelDeposit(needed, balance);
20317
+ balance -= deposit;
20318
+ debt = 0n;
20319
+ rooms.push(deposit - needed);
20320
+ }
20321
+ return true;
20322
+ }
20299
20323
  function relayerOfferProblem(r, want) {
20300
20324
  if (r.scheme !== "batch-settlement") return `the offer is ${r.scheme}, not batch-settlement`;
20301
20325
  if (r.network !== want.network) return `the offer is on ${r.network}, not ${want.network}`;
@@ -20330,7 +20354,7 @@ function openChannelCall(calls, vault, token) {
20330
20354
  }
20331
20355
  return null;
20332
20356
  }
20333
- var RELAYER_DIR, VAULT_SPEND_GAS, REQUEST_TIMEOUT_MS2, MAX_STEPS, MAX_RESENDS, LANDED_TIMEOUT_MS, OPEN_READ_TRIES, OPEN_READ_INTERVAL_MS, BPS_DENOMINATOR, now, LocalSender, RelayerUnavailableError, PendingSpendUnpaidError, RelayerError, RelayerStore, RelayerClient;
20357
+ var RELAYER_DIR, VAULT_SPEND_GAS, REQUEST_TIMEOUT_MS2, MAX_STEPS, MAX_RESENDS, NOT_SENT_MESSAGE_MAX, LANDED_TIMEOUT_MS, OPEN_READ_TRIES, OPEN_READ_INTERVAL_MS, BPS_DENOMINATOR, now, LocalSender, RelayerUnavailableError, PendingSpendUnpaidError, RelayerError, RelayerStore, RelayerClient;
20334
20358
  var init_relay = __esm({
20335
20359
  "../core/src/relay.ts"() {
20336
20360
  "use strict";
@@ -20344,6 +20368,7 @@ var init_relay = __esm({
20344
20368
  REQUEST_TIMEOUT_MS2 = 24e4;
20345
20369
  MAX_STEPS = 8;
20346
20370
  MAX_RESENDS = 3;
20371
+ NOT_SENT_MESSAGE_MAX = 500;
20347
20372
  LANDED_TIMEOUT_MS = 6e4;
20348
20373
  OPEN_READ_TRIES = 20;
20349
20374
  OPEN_READ_INTERVAL_MS = 500;
@@ -20572,6 +20597,55 @@ var init_relay = __esm({
20572
20597
  }
20573
20598
  return null;
20574
20599
  }
20600
+ /**
20601
+ * Read only (nothing is retired, opened or signed): why this vault cannot pay, now, vault spends that reserve `gases`
20602
+ * (each as VaultService.operate reserves it: reserve's amount for that gas), or null when it can. The spends are
20603
+ * played in order as reserve and #ensureChannel would pay them: each from a usable relayer channel with room for it,
20604
+ * or else from a new channel, whose open needs the vault's debt and the open's own spend (#spendBound) on top and
20605
+ * takes RELAYER_CHANNEL_DEPOSIT or what it needs, at most what the vault holds. The vault's credit at the relayer is
20606
+ * not counted: it lowers a price only after the reservation is made. An unpaid open the relayer holds for the vault
20607
+ * is refused here (it is paid before anything else). An operation that sends something first (an approval before the
20608
+ * call it allows) checks this before its first spend, so it does not leave the first done and the rest unpaid.
20609
+ */
20610
+ async shortfall(gases) {
20611
+ const facts = await this.facts();
20612
+ const vault = await this.#buyerVault().vaultAddress();
20613
+ if (await readVault(this.#client, this.#config.deployment, vault) === null) return `the vault ${vault} does not exist yet`;
20614
+ const state = await this.vaultState(vault);
20615
+ if (state.unpaidOpen !== null) return `the relayer holds an unpaid open of the vault's relayer channel ${state.unpaidOpen.channel}: the vault's next relayed operation pays it first`;
20616
+ const amounts = [];
20617
+ for (const g of gases) amounts.push(await this.#reserveAmount(g));
20618
+ const bound2 = await this.#spendBound();
20619
+ const payer = await this.#payer();
20620
+ const rooms = [];
20621
+ for (const c of await this.#records()) {
20622
+ if (c.retired || now() >= c.openedAt + SESSION_MAX_AGE_SECONDS) continue;
20623
+ const free = this.#free(c, null);
20624
+ if (free <= 0n) continue;
20625
+ const ch = await readChannel(this.#client, this.#config.deployment.batchSettlement, c.channel);
20626
+ const usable = payer !== null && getAddress17(c.config.payer) === payer && getAddress17(c.config.receiverAuthorizer) === facts.vault && getAddress17(c.config.receiver) === facts.split && ch.pendingWithdrawal === null;
20627
+ const onChain = ch.balance - BigInt(c.cumulative);
20628
+ if (usable) rooms.push(free < onChain ? free : onChain);
20629
+ }
20630
+ const balance = await tokenBalance(this.#client, this.#config.deployment.token, vault);
20631
+ const plan = (held) => relayerSpendPlan({ amounts, rooms, balance: held, debt: state.debt, openCost: bound2 });
20632
+ if (plan(balance)) return null;
20633
+ let lo = balance;
20634
+ let hi = balance > 0n ? balance * 2n : 1n;
20635
+ while (!plan(hi)) hi *= 2n;
20636
+ while (hi - lo > 1n) {
20637
+ const mid = (lo + hi) / 2n;
20638
+ if (plan(mid)) hi = mid;
20639
+ else lo = mid;
20640
+ }
20641
+ const n = gases.length;
20642
+ return `the vault ${vault} holds ${formatTokenAmount(balance)} USDG: ${n === 1 ? "its relayed transaction needs" : `its ${n} relayed transactions need`} it to hold ${formatTokenAmount(hi)} USDG now (with what its relayer channels have left, and a new channel's open where needed)`;
20643
+ }
20644
+ /** What reserve sets aside for a spend of `gas` (and `extraWei` of ETH it sends): the relayer's price for it, margin and tolerance included. */
20645
+ async #reserveAmount(gas, extraWei = 0n) {
20646
+ const wei = withMargin(gas * await this.#client.getGasPrice()) + extraWei;
20647
+ return maxAcceptedQuote(weiToTokenUnits(wei, await this.#ethUsd(), TOKEN_DECIMALS));
20648
+ }
20575
20649
  /** Base units a transaction of `wei` comes to at the Chainlink price, margin included (the relayer's rule). */
20576
20650
  async #amountFor(wei) {
20577
20651
  return weiToTokenUnits(withMargin(wei), await this.#ethUsd(), TOKEN_DECIMALS);
@@ -20610,8 +20684,7 @@ var init_relay = __esm({
20610
20684
  await (openChannel ?? ((t) => this.#buyerVault().openRelayerChannel(t)))(terms);
20611
20685
  }
20612
20686
  async reserve(gas, openChannel, keep = 0n, extraWei = 0n) {
20613
- const wei = withMargin(gas * await this.#client.getGasPrice()) + extraWei;
20614
- const amount = maxAcceptedQuote(weiToTokenUnits(wei, await this.#ethUsd(), TOKEN_DECIMALS));
20687
+ const amount = await this.#reserveAmount(gas, extraWei);
20615
20688
  for (let attempt = 0; attempt < 2; attempt++) {
20616
20689
  const c = await this.#serial(async () => {
20617
20690
  const found = await this.#channelFor(amount, null);
@@ -20826,6 +20899,7 @@ var init_relay = __esm({
20826
20899
  }
20827
20900
  async send(calls, options = {}) {
20828
20901
  await this.facts();
20902
+ let notSent = "";
20829
20903
  for (let resend = 0; resend < MAX_RESENDS; resend++) {
20830
20904
  let answer = await this.#post(calls, { prepay: options.prepay === true, payer: await this.#payer(), allowFailedSwaps: options.allowFailedSwaps === true });
20831
20905
  let again = false;
@@ -20848,6 +20922,7 @@ var init_relay = __esm({
20848
20922
  continue;
20849
20923
  }
20850
20924
  if (answer.status === 409 && error === "not_sent") {
20925
+ notSent = typeof b?.message === "string" ? b.message : "";
20851
20926
  again = true;
20852
20927
  continue;
20853
20928
  }
@@ -20866,7 +20941,7 @@ var init_relay = __esm({
20866
20941
  }
20867
20942
  if (!again) throw new Error(`the relayer did not relay the request after ${MAX_STEPS} answers`);
20868
20943
  }
20869
- throw new Error(`the relayer could not land the request in ${MAX_RESENDS} attempts`);
20944
+ throw notLandedError(MAX_RESENDS, notSent);
20870
20945
  }
20871
20946
  /**
20872
20947
  * Relayer channels past SESSION_MAX_AGE_SECONDS stop paying; returns those
@@ -22035,6 +22110,21 @@ var init_borro2 = __esm({
22035
22110
  }
22036
22111
  return { buyerVault: buyer, usdgLent: { shares: usdg2.shares.toString(), value: formatUnitsAmount(usdg2.value, TOKEN_DECIMALS) }, stocks };
22037
22112
  }
22113
+ /**
22114
+ * Per Borro stock whose vault of this agent exists: its market, the stock vault's borrow shares, and the market's
22115
+ * borrows and borrow shares accrued to this block, so that any number of borrow shares is valued now as
22116
+ * toAssetsUp(shares, borrowed, borrowedShares) (what fundFromYield takes off as the debt's interest).
22117
+ */
22118
+ async debtBook() {
22119
+ const out = [];
22120
+ for (const s of borroStocks()) {
22121
+ const v = await this.#p.tokenVault(s.token).vaultAddress();
22122
+ if (await readVault(this.#p.client, this.#p.config.deployment, v) === null) continue;
22123
+ const p = await readBorroPosition(this.#p.client, v, s);
22124
+ out.push({ symbol: s.symbol, marketId: s.marketId, borrowShares: p.borrowShares, borrowed: p.borrowed, borrowedShares: p.borrowedShares });
22125
+ }
22126
+ return out;
22127
+ }
22038
22128
  // -------------------------------------------------------------------------
22039
22129
  // Lend and redeem
22040
22130
  // -------------------------------------------------------------------------
@@ -23562,7 +23652,7 @@ function simulatedGas(gasUsed) {
23562
23652
  const g = gasUsed * 5n / 4n + SPEND_GAS_HEADROOM2;
23563
23653
  return g > VAULT_SPEND_GAS ? g : VAULT_SPEND_GAS;
23564
23654
  }
23565
- var LAUNCHPAD_DIR, CREATE_VAULT_GAS2, LAUNCH_GAS_HEADROOM, PAIRS_READ_CONCURRENCY, SPEND_GAS_HEADROOM2, DAY, TOKEN_LIST_MAX, METADATA_MAX_BYTES, launchedEvent, POOL_SETTING_NAMES, LaunchpadService;
23655
+ var LAUNCHPAD_DIR, CREATE_VAULT_GAS2, LAUNCH_GAS_HEADROOM, PAIRS_READ_CONCURRENCY, SPEND_GAS_HEADROOM2, DAY, TOKEN_LIST_MAX, METADATA_MAX_BYTES, launchedEvent, POOL_SETTING_NAMES, WOTS_GAS_SPREAD, STAND_IN_URI, LaunchpadService;
23566
23656
  var init_launchpad = __esm({
23567
23657
  "../core/src/launchpad.ts"() {
23568
23658
  "use strict";
@@ -23598,6 +23688,8 @@ var init_launchpad = __esm({
23598
23688
  volatility_fee: POOL_SETTING.VOLATILITY_FEE,
23599
23689
  max_tx_move: POOL_SETTING.MAX_TX_MOVE
23600
23690
  };
23691
+ WOTS_GAS_SPREAD = 100000n;
23692
+ STAND_IN_URI = `ipfs://bafkrei${"a".repeat(52)}`;
23601
23693
  LaunchpadService = class {
23602
23694
  #client;
23603
23695
  #transport;
@@ -24503,6 +24595,32 @@ var init_launchpad = __esm({
24503
24595
  if (profile.status !== "sent" || record === null) return { uri: uri2, profile, agentUri: null, pairCreators };
24504
24596
  return { uri: uri2, profile, agentUri: await this.setAgentUri(uri2), pairCreators };
24505
24597
  }
24598
+ /**
24599
+ * Read only: why the creator vault cannot pay for publishRegistrationFile now, or null when it can or does not pay
24600
+ * through the relayer. Its spends (the profile and, once the agent is registered, its agent URI) are simulated as
24601
+ * setProfile and setAgentUri send them, with the URI set now (or one of an ipfs:// URI's length) in place of the
24602
+ * file's, and each is counted at the gas it will be reserved (simulatedGas) with WOTS_GAS_SPREAD added (a simulation
24603
+ * signs with a throwaway key, and a WOTS signature's verification costs more or less with each message);
24604
+ * RelayerClient.shortfall replays their payment. A
24605
+ * spend the creator vault signed earlier and has not sent is refused here: its next operation would send that one
24606
+ * instead of the profile.
24607
+ */
24608
+ async publishShortfall() {
24609
+ if (!(this.#creatorSender instanceof RelayerClient)) return null;
24610
+ const s = await this.#creator.state();
24611
+ if (!s.exists) return `the creator vault ${s.vault} does not exist yet`;
24612
+ if ((await this.#creator.spendSlot())?.pending != null) {
24613
+ return `the creator vault ${s.vault} signed a spend earlier that has not landed: its next operation sends that one first (publish_profile with the same fields does), then publish again`;
24614
+ }
24615
+ const record = await this.#agentRecord();
24616
+ const uri2 = record?.uri ?? await this.#profileUri(s.vault) ?? STAND_IN_URI;
24617
+ const relayer = await this.#creatorSender.payer();
24618
+ const gasOf = async (action, params) => simulatedGas((await simulateVaultSpend(this.#client, this.#config.deployment, { relayer, vault: s.vault, action, params })).gasUsed + WOTS_GAS_SPREAD);
24619
+ const gases = [await gasOf(VAULT_ACTION.SET_PROFILE, setProfileParams(uri2))];
24620
+ if (record !== null) gases.push(await gasOf(VAULT_ACTION.SET_AGENT_URI, setAgentUriParams({ agentId: BigInt(record.agentId), uri: uri2 })));
24621
+ const short = await this.#creatorSender.shortfall(gases);
24622
+ return short === null ? null : `${short}; fund_creator_vault moves USDG into it`;
24623
+ }
24506
24624
  /**
24507
24625
  * Pins `file`, a registration file built from the current one (Ostrapad's listing, pad.ts), and makes it the
24508
24626
  * agent's profile in OstraRegistry and its ERC-8004 registration file when it is registered.
@@ -24818,7 +24936,11 @@ var init_pad2 = __esm({
24818
24936
  if (offers !== null) {
24819
24937
  const entry = padServiceEntry({ provider: vault, offers }, this.#pad, this.#p.config.chainId);
24820
24938
  const digest = padListingDigest(agentId, creatorVault, this.#pad, this.#p.config.chainId);
24821
- if (!await this.#approved(vault, digest)) approval = await this.#approveAlone(digest, "Ostrapad listing approval");
24939
+ if (!await this.#approved(vault, digest)) {
24940
+ const short = this.#p.publishShortfall === void 0 ? null : await this.#p.publishShortfall();
24941
+ if (short !== null) throw new Error(`the creator vault could not publish the listing now, so its approval is not given: ${short}; nothing was signed`);
24942
+ approval = await this.#approveAlone(digest, "Ostrapad listing approval");
24943
+ }
24822
24944
  services.push(entry);
24823
24945
  }
24824
24946
  const out = await this.#p.publishFile({ ...current, services });
@@ -25542,6 +25664,19 @@ var init_pay = __esm({
25542
25664
 
25543
25665
  // ../core/src/raeto.ts
25544
25666
  import { getAddress as getAddress27, hashMessage, isAddress as isAddress13, parseAbi as parseAbi6 } from "viem";
25667
+ function netYieldSince(mark, now3, book) {
25668
+ if (mark.debts === void 0) return null;
25669
+ const high = BigInt(mark.price);
25670
+ const earned = now3.price > high ? BigInt(mark.shares) * (now3.price - high) / now3.unit : 0n;
25671
+ let interest = 0n;
25672
+ for (const d of mark.debts) {
25673
+ const m = book.find((b) => b.marketId.toLowerCase() === d.marketId.toLowerCase());
25674
+ if (m === void 0) return null;
25675
+ const owesNow = toAssetsUp(BigInt(d.borrowShares), m.borrowed, m.borrowedShares);
25676
+ if (owesNow > BigInt(d.debt)) interest += owesNow - BigInt(d.debt);
25677
+ }
25678
+ return { earned, interest };
25679
+ }
25545
25680
  function raetoImages(answer) {
25546
25681
  const out = [];
25547
25682
  const choices = answer?.choices;
@@ -25788,15 +25923,20 @@ var init_raeto = __esm({
25788
25923
  return this.#scrub(await this.#call("POST", "/v1/chat/completions", request, await this.#key(), COMPLETION_TIMEOUT_MS));
25789
25924
  }
25790
25925
  /**
25791
- * What the vault's cash vault shares earn, as credits. Kept here: a mark (the shares, and the highest share price read:
25792
- * a high-water mark, so a price that falls and comes back earns nothing until it passes its high), what is owed (the
25793
- * shares of each mark times the rise of the price over its high, added on every call), and USDG redeemed for it and
25794
- * not sent yet (`ready`), with the vault's spend slot its transfer is signed at. A call adds what the shares earned
25795
- * since the mark, moves the mark to now, sends what was redeemed and not sent first, and else, when what is owed is at
25796
- * least `min` USDG, redeems that from the cash vault and sends what the redeem paid (at most what is owed) to Raeto's
25797
- * payTo. A transfer of `ready` that another operation of the vault sent first (it sends a signed spend before its
25798
- * own) is found at its slot and not paid again. Shares added or removed count from the read after; a full exit leaves
25799
- * what was owed until then. Raeto's terms are checked before anything is redeemed. Calls run one after the other.
25926
+ * What the vault's cash vault shares earn, net of the agent's Borro debts' interest, as credits. Kept here: a mark
25927
+ * (the shares, the highest share price read: a high-water mark, so a price that falls and comes back earns nothing
25928
+ * until it passes its high; and each Borro debt's borrow shares with what they owed), what is owed (netYieldSince,
25929
+ * added on every call: the shares' rise over the high less the interest the marked debts accrued; it can fall below
25930
+ * zero, and nothing is sent until it is back above `min`), and USDG redeemed for it and not sent yet (`ready`), with
25931
+ * the vault's spend slot its transfer is signed at. The interest comes off because the carry strategy borrows USDG
25932
+ * from Borro into this same cash vault: the shares' rise is not earned while the loan behind them owes interest.
25933
+ * A call adds what was earned since the mark, moves the mark to now, sends what was redeemed and not sent first, and
25934
+ * else, when what is owed is at least `min` USDG, redeems that from the cash vault and sends what the redeem paid (at
25935
+ * most what is owed) to Raeto's payTo. A transfer of `ready` that another operation of the vault sent first (it sends
25936
+ * a signed spend before its own) is found at its slot and not paid again. Shares and debts added or removed count
25937
+ * from the read after; a full exit leaves what was owed until then. A mark kept before debts were marked (MCP 0.1.10
25938
+ * and earlier) earns nothing for the time since it, as that time's interest cannot be read back. Raeto's terms are
25939
+ * checked before anything is redeemed. Calls run one after the other.
25800
25940
  */
25801
25941
  fundFromYield(min2 = "1") {
25802
25942
  return this.#serial(async () => {
@@ -25810,20 +25950,25 @@ var init_raeto = __esm({
25810
25950
  return { shares, unit, price };
25811
25951
  };
25812
25952
  const now3 = await read();
25953
+ const book = await this.#p.borro.debtBook();
25954
+ const debts = book.filter((b) => b.borrowShares > 0n).map((b) => ({ marketId: b.marketId, borrowShares: b.borrowShares.toString(), debt: toAssetsUp(b.borrowShares, b.borrowed, b.borrowedShares).toString() }));
25813
25955
  const text3 = await this.#p.folder.read(YIELD_FILE);
25814
25956
  const kept = text3 === void 0 ? null : JSON.parse(text3);
25815
- const usd4 = (x) => formatUnitsAmount(x, TOKEN_DECIMALS);
25957
+ const usd4 = (x) => formatUnitsAmount(x > 0n ? x : 0n, TOKEN_DECIMALS);
25816
25958
  const write = (m) => this.#p.folder.write(YIELD_FILE, JSON.stringify(m));
25817
25959
  if (kept === null || getAddress27(kept.vault) !== target) {
25818
- await write({ vault: target, shares: now3.shares.toString(), price: now3.price.toString(), owed: "0", ready: "0" });
25819
- return { vault: target, owed: "0", sent: "0", note: "the mark is set now: what the shares earn from here is sent as credits by later calls" };
25960
+ await write({ vault: target, shares: now3.shares.toString(), price: now3.price.toString(), owed: "0", ready: "0", debts });
25961
+ return { vault: target, owed: "0", sent: "0", note: "the mark is set now: what the shares earn from here, less the Borro debts' interest, is sent as credits by later calls" };
25820
25962
  }
25821
25963
  const high = BigInt(kept.price);
25822
- let owed = BigInt(kept.owed) + (now3.price > high ? BigInt(kept.shares) * (now3.price - high) / now3.unit : 0n);
25964
+ const net = netYieldSince(kept, now3, book);
25965
+ let owed = BigInt(kept.owed) + (net === null ? 0n : net.earned - net.interest);
25823
25966
  let ready = BigInt(kept.ready);
25824
- const mark = { ...kept, vault: target, shares: now3.shares.toString(), price: (now3.price > high ? now3.price : high).toString(), owed: owed.toString(), ready: ready.toString() };
25967
+ const mark = { ...kept, vault: target, shares: now3.shares.toString(), price: (now3.price > high ? now3.price : high).toString(), owed: owed.toString(), ready: ready.toString(), debts };
25825
25968
  await write(mark);
25826
- if (ready === 0n && (owed === 0n || owed < minimum)) return { vault: target, owed: usd4(owed), sent: "0", note: `under ${min2} USDG: kept for a later call` };
25969
+ const unmarked = net === null ? "the mark did not follow the Borro debts yet (kept by an earlier version): nothing is counted for the time since it, and the mark now follows them; " : "";
25970
+ if (ready === 0n && owed < 0n) return { vault: target, owed: "0", sent: "0", note: `${unmarked}the Borro debts' interest since the mark is more than the shares earned: ${formatUnitsAmount(-owed, TOKEN_DECIMALS)} USDG to make up before anything is sent` };
25971
+ if (ready === 0n && (owed === 0n || owed < minimum)) return { vault: target, owed: usd4(owed), sent: "0", note: `${unmarked}under ${min2} USDG: kept for a later call` };
25827
25972
  const { payTo } = await this.terms();
25828
25973
  if (ready > 0n && mark.readySlot !== void 0) {
25829
25974
  const slot2 = { nonce: BigInt(mark.readySlot.nonce), keyIndex: BigInt(mark.readySlot.keyIndex) };
@@ -25859,7 +26004,7 @@ var init_raeto = __esm({
25859
26004
  ready = paid <= 0n ? 0n : paid < owed ? paid : owed;
25860
26005
  owed -= ready;
25861
26006
  const afterHigh = BigInt(mark.price) > after.price ? BigInt(mark.price) : after.price;
25862
- await write({ vault: target, shares: after.shares.toString(), price: afterHigh.toString(), owed: owed.toString(), ready: ready.toString() });
26007
+ await write({ vault: target, shares: after.shares.toString(), price: afterHigh.toString(), owed: owed.toString(), ready: ready.toString(), debts });
25863
26008
  if (ready === 0n) return { vault: target, owed: usd4(owed), sent: "0", note: "the redeem paid nothing into the vault", redeem };
25864
26009
  }
25865
26010
  const slot = await this.#p.buyer.spendSlot();
@@ -33955,6 +34100,7 @@ var init_agent = __esm({
33955
34100
  return typeof p2.profile === "object" && p2.profile !== null && !Array.isArray(p2.profile) ? p2.profile : null;
33956
34101
  },
33957
34102
  publishFile: (file) => lp.publishRegistrationFile(file),
34103
+ publishShortfall: () => lp.publishShortfall(),
33958
34104
  upload: (file) => uploadJson(config.relayerUrl, file),
33959
34105
  indexerUrl: config.padIndexerUrl
33960
34106
  });
@@ -34624,7 +34770,7 @@ function registerTools(server, agent) {
34624
34770
  };
34625
34771
  tool(
34626
34772
  "raeto",
34627
- "Raeto (raeto.cc): 400+ AI models (text, and image models such as google/gemini-2.5-flash-image and openai/gpt-5-image-mini) through one OpenAI-compatible API, paid in Raeto credits of the vault's own Raeto account. login: signs the USDG vault in as an agent with EIP-1271 (the vault approves Raeto's login message, then withdraws the approval; refused under the withdrawal lock), its API key kept on this machine and never shown (name: the account's name; limitUsd: its spending limit). me: the account's credits. models: the models and prices per token (output: image for the image models). fund: sends `amount` USDG from the vault to Raeto, as credits. fund_yield: sends what the cash vault shares earned since the last call (at least `min`, default 1 USDG), redeemed then sent. complete: a model call, `request` in the OpenAI chat format with Raeto's model id (images it returns are saved as files in the agent's directory).",
34773
+ "Raeto (raeto.cc): 400+ AI models (text, and image models such as google/gemini-2.5-flash-image and openai/gpt-5-image-mini) through one OpenAI-compatible API, paid in Raeto credits of the vault's own Raeto account. login: signs the USDG vault in as an agent with EIP-1271 (the vault approves Raeto's login message, then withdraws the approval; refused under the withdrawal lock), its API key kept on this machine and never shown (name: the account's name; limitUsd: its spending limit). me: the account's credits. models: the models and prices per token (output: image for the image models). fund: sends `amount` USDG from the vault to Raeto, as credits. fund_yield: sends what the cash vault shares earned since the last call, less the interest the agent's Borro loans accrued meanwhile (at least `min`, default 1 USDG), redeemed then sent. complete: a model call, `request` in the OpenAI chat format with Raeto's model id (images it returns are saved as files in the agent's directory).",
34628
34774
  {
34629
34775
  action: z.enum(["login", "me", "models", "fund", "fund_yield", "complete"]),
34630
34776
  name: z.string().min(1).max(64).optional(),
@@ -34683,7 +34829,7 @@ function registerTools(server, agent) {
34683
34829
  const files = z.array(z.string().max(200).regex(/^ipfs:\/\/[A-Za-z0-9]+(\/[A-Za-z0-9._-]+)*$/)).max(10).optional().describe("ipfs:// files (upload_image makes one)");
34684
34830
  tool(
34685
34831
  "pad_publish",
34686
- "Ostrapad as a provider. publish: lists offers in the agent's ERC-8004 registration file, paid into its USDG vault (the vault approves the listing once, so a listing is its own); needs publish_profile and register_agent first. unpublish: removes the listing. status: the agent's own listing as clients read it. bond / withdraw_bond: a voluntary provider bond (raises the limit per job to the bond, up to 500 USDG; a rejected job costs it 10% of the budget plus the evaluator fee). return_bond_box: returns USDG left in the provider bond box (a deposit whose second step failed) to the USDG vault.",
34832
+ "Ostrapad as a provider. publish: lists offers in the agent's ERC-8004 registration file, paid into its USDG vault (the vault approves the listing once, so a listing is its own); needs publish_profile and register_agent first, and USDG in the creator vault for its relayed transactions (checked before the approval). unpublish: removes the listing. status: the agent's own listing as clients read it. bond / withdraw_bond: a voluntary provider bond (raises the limit per job to the bond, up to 500 USDG; a rejected job costs it 10% of the budget plus the evaluator fee). return_bond_box: returns USDG left in the provider bond box (a deposit whose second step failed) to the USDG vault.",
34687
34833
  { action: z.enum(["publish", "unpublish", "status", "bond", "withdraw_bond", "return_bond_box"]), offers: z.array(offer).min(1).max(20).optional(), amount: usdg.optional() },
34688
34834
  async (a) => {
34689
34835
  if (a.action === "publish") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ostraagent/mcp",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "Ostra Agent's MCP server (stdio): the post-quantum privacy vault for AI agents. One vault to pay, earn, work and hire on Ostrapad, and stay private on Robinhood Chain, with no ETH (the Ostra relayer sends its transactions, paid in USDG).",
5
5
  "keywords": [
6
6
  "mcp",