@ostraagent/mcp 0.1.12 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -46,7 +46,7 @@ Keep the file and the passphrase off the machine. On a new machine, run `npx -y
46
46
  - **Ostra AI:** `ostra_ai` (a text, image or video task; Ostra chooses the model by its quality rating within `maxPrice`, paid in USDG from a channel of the vault, and what a task did not cost stays as the vault's credit), `ostra_ai_job` (a video job, saved as a file once completed), `ostra_ai_credit`
47
47
  - **Selling services:** `accept_payments`, `seller_balance`, `seller_withdraw`
48
48
  - **Recovery:** `recovery_status`, `set_guardian`, `set_recovery_policy`, `cancel_recovery`, `support_recovery`, `request_recovery`
49
- - **Withdrawal lock:** `vault_lock` (status; set a withdrawal address on another device, a delay of 36 hours to 14 days and a daily limit: anything else the vault sends counts against the limit, and every loosening waits the delay; off; cancel what waits), `vault_link` (the linked vault a Borro loan is paid to), `vault_approve_hash` (EIP-1271 approvals; under the lock they wait the delay), `guard_vault` (as a guardian of another locked vault: cancel its waiting changes, revoke an approval, sweep it to its withdrawal address, bring its Borro shares or collateral back)
49
+ - **Withdrawal lock:** `vault_lock` (status, with the hashes the vault approves for EIP-1271, also of any other Ostra vault for a guardian; set a withdrawal address on another device, a delay of 36 hours to 14 days and a daily limit: anything else the vault sends counts against the limit, and every loosening waits the delay; off; cancel what waits), `vault_link` (the linked vault a Borro loan is paid to), `vault_revoke_hash` (withdraws an EIP-1271 approval of the vault or of a Borro stock's vault; no tool approves a hash it is given, since USDG takes the vault's EIP-1271 answer as a permit or a transfer authorization of its funds: the vault approves only digests the server builds itself), `guard_vault` (as a guardian of another locked vault: cancel its waiting changes, revoke an approval, sweep it to its withdrawal address, bring its Borro shares or collateral back)
50
50
  - **Strategies:** `strategy_plan` (a conservative plan, USDG into a cash vault that passes every check, and, when the agent holds SPY, QQQ or NVDA, a balanced one: the stock as collateral on Borro, USDG borrowed into the cash vault; each with its steps, gas, 30-day earnings net of gas, risks and when it stops; every step earns over 3x its gas in 30 days or is left out), `strategy_apply` (runs a plan step by step, each checked and sized again right before it is signed), `strategy_check` (each run's check of the cash vaults and the carries; urgent exits and repayments run at once), `strategy_report` (what the strategies earn). Only for an agent registered in ERC-8004 (`publish_profile`); `strategy_check` and its exits run for every vault
51
51
  - **Trading capital:** `trading` (USDG set aside for the agent's own trades, with Ostra's limits, which an agent may only tighten: a position at most 25% of the capital and no larger than a buy that moves the price 1%; buys stop for the day after a 5% loss that day and for the week after 10% that week)
52
52
  - **OstraBreaker:** `breaker` (status, or arm: OstraBreaker as a guardian of the USDG vault, watching each cash vault; when a limit is crossed on chain, Ostra's breaker keeper sends the trip and the position comes back to the vault). `vault_lock set` arms it first by itself
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.12", 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.13", 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
 
@@ -15453,7 +15453,7 @@ var init_morpho = __esm({
15453
15453
  });
15454
15454
 
15455
15455
  // ../sdk/src/borro.ts
15456
- import { getAddress as getAddress5, parseAbi as parseAbi2, zeroAddress as zeroAddress4 } from "viem";
15456
+ import { getAbiItem, getAddress as getAddress5, parseAbi as parseAbi2, zeroAddress as zeroAddress4 } from "viem";
15457
15457
  function borroStocks() {
15458
15458
  return BORRO_STOCKS.map((s) => {
15459
15459
  const market2 = { loanToken: USDG, collateralToken: s.vault, oracle: s.oracle, irm: BORRO_IRM, lltv: s.lltv };
@@ -15474,6 +15474,26 @@ function halfLltvMaxDebt(collateral, price, lltv) {
15474
15474
  const value = collateral * price / 10n ** 36n;
15475
15475
  return value * lltv * VAULT_MAX_LTV_OF_LLTV_BPS / (10n ** 18n * 10000n);
15476
15476
  }
15477
+ async function readHashApprovals(client, logsClient, vault, fromBlock) {
15478
+ const version = Number(await client.readContract({ address: vault, abi: ostraVaultAbi, functionName: "VERSION" }));
15479
+ if (version < VAULT_V3) return [];
15480
+ const event = getAbiItem({ abi: ostraVaultModuleAbi, name: "HashApproved" });
15481
+ const logs = await getLogsChunked(logsClient, { address: vault, event, fromBlock, toBlock: await logsClient.getBlockNumber({ cacheTime: 0 }) });
15482
+ const head = await blockTime(client);
15483
+ const hashes = [];
15484
+ for (const l of logs) {
15485
+ const h = l.args.hash.toLowerCase();
15486
+ if (!hashes.includes(h)) hashes.push(h);
15487
+ }
15488
+ const out = [];
15489
+ for (const hash of hashes) {
15490
+ const validFrom = BigInt(
15491
+ await client.readContract({ address: vault, abi: ostraVaultAbi, functionName: "hashValidFrom", args: [hash], blockNumber: head.blockNumber })
15492
+ );
15493
+ if (validFrom !== 0n) out.push({ hash, validFrom, valid: head.timestamp >= validFrom });
15494
+ }
15495
+ return out;
15496
+ }
15477
15497
  async function readVaultGuard(client, vault) {
15478
15498
  const version = Number(await client.readContract({ address: vault, abi: ostraVaultAbi, functionName: "VERSION" }));
15479
15499
  const off = { withdrawal: zeroAddress4, delay: 0, dailyLimit: 0n };
@@ -19180,7 +19200,7 @@ var init_relayer = __esm({
19180
19200
  init_messages();
19181
19201
  init_external_abi();
19182
19202
  RELAYER_MARGIN_BPS = 1000n;
19183
- RELAYER_QUOTE_TOLERANCE_BPS = 200n;
19203
+ RELAYER_QUOTE_TOLERANCE_BPS = 1000n;
19184
19204
  ETH_USD_DEVIATION_BPS = 50n;
19185
19205
  ETH_USD_MAX_AGE_SECONDS = 90000n;
19186
19206
  MAX_RELAY_CALLS = 8;
@@ -22391,6 +22411,26 @@ var init_borro2 = __esm({
22391
22411
  const at = await this.#address(vault, what.toUpperCase());
22392
22412
  return { vault: at, ...await readVaultGuard(this.#p.client, at) };
22393
22413
  }
22414
+ /**
22415
+ * The lock's status (guard) with the hashes the vault approves for EIP-1271 now (sdk readHashApprovals): of the vault
22416
+ * of `what`, or, given `otherText`, of any Ostra vault (a guardian watching the vault it guards). Read only.
22417
+ */
22418
+ async lockStatus(what, otherText) {
22419
+ let at;
22420
+ if (otherText === void 0) {
22421
+ const { vault } = this.#vaultOf(what);
22422
+ at = await this.#address(vault, what.toUpperCase());
22423
+ } else {
22424
+ if (!isAddress8(otherText, { strict: false })) throw new RangeError(`${otherText} is not an address`);
22425
+ at = getAddress19(otherText);
22426
+ if (await readVault(this.#p.client, this.#p.config.deployment, at) === null) throw new Error(`${at} is not an Ostra vault`);
22427
+ }
22428
+ const [g, approvals] = await Promise.all([
22429
+ readVaultGuard(this.#p.client, at),
22430
+ readHashApprovals(this.#p.client, this.#p.logsClient ?? this.#p.client, at, BigInt(this.#p.config.deployment.fromBlock))
22431
+ ]);
22432
+ return { vault: at, ...g, approvals };
22433
+ }
22394
22434
  /**
22395
22435
  * Turns the withdrawal lock of the vault of `what` on (a withdrawal address, a delay, a daily limit in the vault's
22396
22436
  * token) or changes it; `withdrawal` "off" turns it off (that waits the delay). The withdrawal address must keep
@@ -22474,11 +22514,27 @@ var init_borro2 = __esm({
22474
22514
  const names = ["cancel", "revoke", "sweep", "redeem", "withdraw collateral"];
22475
22515
  return this.#spend(this.#p.buyer, VAULT_ACTION.GUARD, guardParams(target, call), `guard (${names[call.op] ?? call.op}) of ${target}`);
22476
22516
  }
22477
- /** Approves (or withdraws) `hash` for EIP-1271 on the buyer vault: under the lock an approval waits its delay. */
22517
+ /**
22518
+ * Approves (or withdraws) `hash` for EIP-1271 on the buyer vault: under the lock an approval waits its delay. For this
22519
+ * package's own digests only (Raeto's login message, OstraBreaker's limits), which it builds and checks itself: an
22520
+ * approved hash makes the vault answer isValidSignature for it, and USDG takes such an answer for a permit and for a
22521
+ * transfer authorization, so a hash from anywhere else could hand the vault's funds out. No tool approves a hash it is
22522
+ * given (withdrawApproval only withdraws).
22523
+ */
22478
22524
  async approveHash(hash, approved) {
22479
22525
  if (!/^0x[0-9a-fA-F]{64}$/.test(hash) || /^0x0{64}$/.test(hash)) throw new RangeError("the hash is 32 bytes of 0x hex, not zero");
22480
22526
  return this.#spend(this.#p.buyer, VAULT_ACTION.APPROVE_HASH, approveHashParams({ hash, approved }), approved ? "EIP-1271 approval" : "EIP-1271 approval withdrawal");
22481
22527
  }
22528
+ /**
22529
+ * Withdraws the EIP-1271 approval of `hash` by the vault of `what` ("USDG", the buyer vault, or a Borro stock's
22530
+ * vault): lands when there is none (nothing to do). Withdrawing is at once, under the lock too.
22531
+ */
22532
+ async withdrawApproval(what, hash) {
22533
+ if (!/^0x[0-9a-fA-F]{64}$/.test(hash) || /^0x0{64}$/.test(hash)) throw new RangeError("the hash is 32 bytes of 0x hex, not zero");
22534
+ const { vault } = this.#vaultOf(what);
22535
+ await this.#address(vault, what.toUpperCase());
22536
+ return this.#spend(vault, VAULT_ACTION.APPROVE_HASH, approveHashParams({ hash, approved: false }), `EIP-1271 approval withdrawal (${what.toUpperCase()} vault)`);
22537
+ }
22482
22538
  };
22483
22539
  }
22484
22540
  });
@@ -23749,7 +23805,7 @@ function simulatedGas(gasUsed) {
23749
23805
  const g = gasUsed * 5n / 4n + SPEND_GAS_HEADROOM2;
23750
23806
  return g > VAULT_SPEND_GAS ? g : VAULT_SPEND_GAS;
23751
23807
  }
23752
- 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;
23808
+ var LAUNCHPAD_DIR, CREATE_VAULT_GAS2, LAUNCH_GAS_HEADROOM, PAIRS_READ_CONCURRENCY, SPEND_GAS_HEADROOM2, DAY, LOGS_CATCH_UP_SECONDS, TOKEN_LIST_MAX, METADATA_MAX_BYTES, launchedEvent, POOL_SETTING_NAMES, WOTS_GAS_SPREAD, STAND_IN_URI, LaunchpadService;
23753
23809
  var init_launchpad = __esm({
23754
23810
  "../core/src/launchpad.ts"() {
23755
23811
  "use strict";
@@ -23770,6 +23826,7 @@ var init_launchpad = __esm({
23770
23826
  PAIRS_READ_CONCURRENCY = 6;
23771
23827
  SPEND_GAS_HEADROOM2 = 100000n;
23772
23828
  DAY = 86400;
23829
+ LOGS_CATCH_UP_SECONDS = 30;
23773
23830
  TOKEN_LIST_MAX = 50;
23774
23831
  METADATA_MAX_BYTES = 65536;
23775
23832
  launchedEvent = parseAbiItem5("event Launched(bytes32 indexed poolId, address indexed token, address indexed creatorVault, address quote)");
@@ -24601,13 +24658,20 @@ var init_launchpad = __esm({
24601
24658
  * is never registered twice. An identity someone else registered and sent to the creator vault (an ERC-721 transfer
24602
24659
  * the vault accepts) is no registration of this agent: with no Registered event of the vault, this is null. A
24603
24660
  * registration sent again just now is read from its receipt instead (#recordFromReceipt), not from logs that may lag.
24661
+ * The vault's identities are counted at the chain's head and the events read up to that same block, after the logs
24662
+ * RPC has reached it: a registration the count holds is never missed because the logs RPC was behind (which would
24663
+ * register the agent twice); when the logs RPC does not reach that block soon, this throws before anything is signed.
24604
24664
  */
24605
24665
  async #recoverAgentRecord() {
24606
24666
  const vault = getAddress24(await this.#creator.vaultAddress());
24607
- const owned = await this.#client.readContract({ address: ERC8004_IDENTITY_REGISTRY, abi: identityRegistryAbi, functionName: "balanceOf", args: [vault] });
24667
+ const head = await this.#client.getBlockNumber({ cacheTime: 0 });
24668
+ const owned = await this.#client.readContract({ address: ERC8004_IDENTITY_REGISTRY, abi: identityRegistryAbi, functionName: "balanceOf", args: [vault], blockNumber: head });
24608
24669
  if (owned === 0n) return null;
24609
- const to = await this.#logsClient.getBlockNumber({ cacheTime: 0 });
24610
- const logs = await getLogsChunked(this.#logsClient, { address: ERC8004_IDENTITY_REGISTRY, event: agentRegisteredEvent, args: { owner: vault }, fromBlock: BigInt(this.#config.deployment.fromBlock), toBlock: to });
24670
+ for (let waited = 0; await this.#logsClient.getBlockNumber({ cacheTime: 0 }) < head; waited++) {
24671
+ if (waited >= LOGS_CATCH_UP_SECONDS) throw new Error(`the logs RPC has not reached block ${head} of the chain RPC after ${LOGS_CATCH_UP_SECONDS} seconds, so the creator vault's ERC-8004 registration cannot be read yet: call again shortly; nothing was signed`);
24672
+ await new Promise((r) => setTimeout(r, 1e3));
24673
+ }
24674
+ const logs = await getLogsChunked(this.#logsClient, { address: ERC8004_IDENTITY_REGISTRY, event: agentRegisteredEvent, args: { owner: vault }, fromBlock: BigInt(this.#config.deployment.fromBlock), toBlock: head });
24611
24675
  for (const log2 of logs) {
24612
24676
  const { agentId, agentURI } = log2.args;
24613
24677
  const who = await readAgentIdentity(this.#client, agentId);
@@ -24620,7 +24684,9 @@ var init_launchpad = __esm({
24620
24684
  }
24621
24685
  /**
24622
24686
  * The registration transaction `txHash` of the creator vault, read from its receipt (the vault's AgentRegistered
24623
- * event) and written to agent.json; null when it registered nothing. `uri` is the file it registered with.
24687
+ * event) and written to agent.json; null when it registered nothing. The file it registered with is the Identity
24688
+ * Registry's Registered event's agentURI in that receipt (a registration signed by an earlier call carries that
24689
+ * call's file); `uri` only when the receipt has no such event.
24624
24690
  */
24625
24691
  async #recordFromReceipt(txHash, uri2) {
24626
24692
  const vault = getAddress24(await this.#creator.vaultAddress());
@@ -24635,7 +24701,17 @@ var init_launchpad = __esm({
24635
24701
  }
24636
24702
  }
24637
24703
  if (agentId === void 0) return null;
24638
- const record = { agentId, uri: uri2, txHash, at: Math.floor(Date.now() / 1e3) };
24704
+ let registered;
24705
+ for (const log2 of receipt.logs) {
24706
+ if (getAddress24(log2.address) !== getAddress24(ERC8004_IDENTITY_REGISTRY)) continue;
24707
+ try {
24708
+ const e = decodeEventLog3({ abi: [agentRegisteredEvent], data: log2.data, topics: log2.topics });
24709
+ const a = e.args;
24710
+ if (a.agentId.toString() === agentId && getAddress24(a.owner) === vault) registered = a.agentURI;
24711
+ } catch {
24712
+ }
24713
+ }
24714
+ const record = { agentId, uri: registered ?? uri2, txHash, at: Math.floor(Date.now() / 1e3) };
24639
24715
  await this.#folder.write("agent.json", JSON.stringify(record, null, 2));
24640
24716
  return record;
24641
24717
  }
@@ -24707,10 +24783,11 @@ var init_launchpad = __esm({
24707
24783
  const first = await this.#publishProfileFile(null);
24708
24784
  const unnumbered = { uri: first.uri, profile: first.profile };
24709
24785
  if (first.profile.status !== "sent") {
24710
- const resent = first.profile.status === "resent_pending" ? first.profile.resent : null;
24711
- const landed = resent !== null && resent.operation === "vault_spend" && resent.action === VAULT_ACTION.REGISTER_AGENT ? await this.#recordFromReceipt(resent.txHash, first.uri) : null;
24712
- if (landed === null) return { agentId: null, registration: null, unnumbered, ...first };
24713
- return { agentId: landed.agentId, registration: null, unnumbered, ...await this.#publishProfileFile(landed) };
24786
+ const resent = first.profile.resent;
24787
+ const landed = resent.operation === "vault_spend" && resent.action === VAULT_ACTION.REGISTER_AGENT ? await this.#recordFromReceipt(resent.txHash, first.uri) : null;
24788
+ if (landed !== null) return { agentId: landed.agentId, registration: null, unnumbered, ...await this.#publishProfileFile(landed) };
24789
+ const after = await this.#agentRecord() ?? await this.#recoverAgentRecord();
24790
+ if (after !== null) return { agentId: after.agentId, registration: null, unnumbered, ...await this.#publishProfileFile(after) };
24714
24791
  }
24715
24792
  const registration = await this.registerAgent(first.uri);
24716
24793
  const record = await this.#agentRecord();
@@ -26000,7 +26077,7 @@ var init_raeto = __esm({
26000
26077
  await this.#keepApprovals((await this.#keptApprovals()).filter((h) => h !== hash));
26001
26078
  return { done: true, note };
26002
26079
  }
26003
- return { done: false, note: `${note}; the approval may still read or land: call raeto login again to withdraw it, or vault_approve_hash ${hash} approved false` };
26080
+ return { done: false, note: `${note}; the approval may still read or land: call raeto login again to withdraw it, or vault_revoke_hash ${hash}` };
26004
26081
  }
26005
26082
  /**
26006
26083
  * Signs the vault in to Raeto as an agent (see the file comment): its own Raeto account and API key, kept here. With a
@@ -29590,7 +29667,7 @@ var init_orders = __esm({
29590
29667
  });
29591
29668
 
29592
29669
  // ../../../../../../srv/apps/ostra-swap/sdk/src/private.ts
29593
- import { encodeAbiParameters as encodeAbiParameters13, encodeFunctionData as encodeFunctionData15, getAbiItem, getAddress as getAddress32, getContractAddress as getContractAddress5, keccak256 as keccak25619, parseEventLogs, toHex as toHex8 } from "viem";
29670
+ import { encodeAbiParameters as encodeAbiParameters13, encodeFunctionData as encodeFunctionData15, getAbiItem as getAbiItem2, getAddress as getAddress32, getContractAddress as getContractAddress5, keccak256 as keccak25619, parseEventLogs, toHex as toHex8 } from "viem";
29594
29671
  function packDigest(elements) {
29595
29672
  if (elements.length !== 8) throw new Error("a digest has 8 field elements");
29596
29673
  let v = 0n;
@@ -29804,7 +29881,7 @@ var init_private = __esm({
29804
29881
  MAX_AMOUNT = 1n << 128n;
29805
29882
  MAX_UINT642 = (1n << 64n) - 1n;
29806
29883
  ZERO2 = "0x0000000000000000000000000000000000000000";
29807
- noteAddedEvent = getAbiItem({ abi: ostraPrivatePoolAbi, name: "NoteAdded" });
29884
+ noteAddedEvent = getAbiItem2({ abi: ostraPrivatePoolAbi, name: "NoteAdded" });
29808
29885
  PoolTrees = class {
29809
29886
  #data;
29810
29887
  /** The sync running now: syncs run one at a time (each one's rollback assumes no other changes the trees). */
@@ -32464,7 +32541,7 @@ var init_strategy2 = __esm({
32464
32541
  });
32465
32542
 
32466
32543
  // ../core/src/swap.ts
32467
- import { decodeErrorResult as decodeErrorResult2, decodeEventLog as decodeEventLog4, getAbiItem as getAbiItem2, getAddress as getAddress38 } from "viem";
32544
+ import { decodeErrorResult as decodeErrorResult2, decodeEventLog as decodeEventLog4, getAbiItem as getAbiItem3, getAddress as getAddress38 } from "viem";
32468
32545
  function refusalOf(reason, outDecimals) {
32469
32546
  if (reason === "0x") return "the swap reverted without a reason (out of gas, or a revert with no data)";
32470
32547
  try {
@@ -33081,7 +33158,7 @@ var init_swap = __esm({
33081
33158
  */
33082
33159
  async orderResult(order, fromBlock, escrow) {
33083
33160
  const toBlock = await this.#p.logsClient.getBlockNumber({ cacheTime: 0 });
33084
- const logs = await getLogsChunked(this.#p.logsClient, { address: this.#p.swap.router, event: getAbiItem2({ abi: ostraRouterAbi, name: "EscrowExecuted" }), args: { orderHash: order }, fromBlock, toBlock });
33161
+ const logs = await getLogsChunked(this.#p.logsClient, { address: this.#p.swap.router, event: getAbiItem3({ abi: ostraRouterAbi, name: "EscrowExecuted" }), args: { orderHash: order }, fromBlock, toBlock });
33085
33162
  let spent = 0n;
33086
33163
  let received = 0n;
33087
33164
  let moved = false;
@@ -33152,7 +33229,7 @@ var init_swap = __esm({
33152
33229
  let hash = tx;
33153
33230
  if (hash === null && r.fromBlock !== void 0) {
33154
33231
  const toBlock = await this.#p.logsClient.getBlockNumber({ cacheTime: 0 });
33155
- const logs = await getLogsChunked(this.#p.logsClient, { address: this.#p.swap.router, event: getAbiItem2({ abi: ostraRouterAbi, name: "EscrowExecuted" }), args: { orderHash: r.hash }, fromBlock: BigInt(r.fromBlock), toBlock });
33232
+ const logs = await getLogsChunked(this.#p.logsClient, { address: this.#p.swap.router, event: getAbiItem3({ abi: ostraRouterAbi, name: "EscrowExecuted" }), args: { orderHash: r.hash }, fromBlock: BigInt(r.fromBlock), toBlock });
33156
33233
  const moved = logs.find((l) => l.args.received !== 0n);
33157
33234
  hash = moved === void 0 ? null : moved.transactionHash;
33158
33235
  }
@@ -34310,7 +34387,15 @@ var init_agent = __esm({
34310
34387
  this.shield = new ShieldService(client, config, keys, this.buyer, this.sender);
34311
34388
  this.creatorShield = new ShieldService(client, config, keys, this.creator, creatorSender);
34312
34389
  this.unshield = new UnshieldService(client, config, keys, this.shield, (index) => this.privateVault(index));
34313
- this.borro = new BorroService({ client, config, buyer: this.buyer, tokenVault: (token) => this.vaultFor(token), sender: this.sender, ownVaults: () => this.ownVaults() });
34390
+ this.borro = new BorroService({
34391
+ client,
34392
+ config,
34393
+ buyer: this.buyer,
34394
+ tokenVault: (token) => this.vaultFor(token),
34395
+ sender: this.sender,
34396
+ ownVaults: () => this.ownVaults(),
34397
+ logsClient: createPublicClient2({ chain: robinhoodChain, transport: http2(config.logsRpcUrl, { retryCount: 6, retryDelay: 2e3 }) })
34398
+ });
34314
34399
  this.breaker = new BreakerService({ client, config, buyer: this.buyer, borro: this.borro, sender: this.sender, folder: keys.folder.folder("breaker") });
34315
34400
  this.raeto = config.raetoUrl === null ? null : new RaetoService({ client, buyer: this.buyer, borro: this.borro, folder: keys.folder.folder("raeto"), apiUrl: config.raetoUrl });
34316
34401
  if (config.swap === null) {
@@ -35021,10 +35106,11 @@ function registerTools(server, agent) {
35021
35106
  );
35022
35107
  tool(
35023
35108
  "vault_lock",
35024
- "The withdrawal lock of the vault (USDG) or of the agent's vault of a Borro stock. status: the lock, the daily limit left, the linked vault and the changes waiting. set: turns the lock on or changes it (withdrawal: a wallet whose key is not on this device, or another seed's Ostra vault in the same token that is locked already; never one of this agent's vaults; delaySeconds 129600 to 1209600; dailyLimit in the vault's token): turning it on and tightening are at once, loosening waits the delay. off: turns it off after the delay. cancel: drops the changes still waiting.",
35109
+ "The withdrawal lock of the vault (USDG) or of the agent's vault of a Borro stock. status: the lock, the daily limit left, the linked vault, the changes waiting, and the hashes the vault approves for EIP-1271 (approvals: each hash, from when it counts and whether that time has come; a hash can be a permit or a transfer authorization of the vault's funds; under the lock no flow of this server approves anything, so every hash listed then came from elsewhere and should go; without the lock the list also holds this server's own approvals, such as Ostrapad's, which look alike: vault_revoke_hash withdraws one, a guardian of a locked vault revokes it with guard_vault); with vault, the same for any Ostra vault (a guardian watching the vault it guards). set: turns the lock on or changes it (withdrawal: a wallet whose key is not on this device, or another seed's Ostra vault in the same token that is locked already; never one of this agent's vaults; delaySeconds 129600 to 1209600; dailyLimit in the vault's token): turning it on and tightening are at once, loosening waits the delay. off: turns it off after the delay. cancel: drops the changes still waiting.",
35025
35110
  {
35026
35111
  action: z.enum(["status", "set", "off", "cancel"]),
35027
35112
  asset: borroAsset.optional().describe("USDG (default) or a Borro stock's ticker"),
35113
+ vault: address5.optional().describe("status only: another Ostra vault to read"),
35028
35114
  withdrawal: address5.optional(),
35029
35115
  delaySeconds: z.number().int().min(129600).max(1209600).optional(),
35030
35116
  dailyLimit: units.optional(),
@@ -35032,7 +35118,8 @@ function registerTools(server, agent) {
35032
35118
  },
35033
35119
  async (a) => {
35034
35120
  const asset = a.asset ?? "USDG";
35035
- if (a.action === "status") return agent.borro.guard(asset);
35121
+ if (a.vault !== void 0 && a.action !== "status") throw new RangeError("vault is for status only: set, off and cancel act on this agent's own vaults");
35122
+ if (a.action === "status") return agent.borro.lockStatus(asset, a.vault);
35036
35123
  if (a.action === "cancel") return agent.borro.cancelPending(asset);
35037
35124
  if (a.action === "off") return agent.borro.setLock(asset, "off", 0, "0");
35038
35125
  if (a.withdrawal === void 0 || a.delaySeconds === void 0 || a.dailyLimit === void 0) throw new RangeError("set takes withdrawal, delaySeconds and dailyLimit");
@@ -35056,10 +35143,10 @@ function registerTools(server, agent) {
35056
35143
  (a) => agent.borro.setLink(a.asset ?? "USDG", a.vault)
35057
35144
  );
35058
35145
  tool(
35059
- "vault_approve_hash",
35060
- "Approves a 32-byte hash for EIP-1271 on the vault (isValidSignature answers for it), or withdraws an approval. Under the lock an approval waits the lock's delay; a new lock and every recovery end all approvals.",
35061
- { hash: bytes323, approved: z.boolean() },
35062
- (a) => agent.borro.approveHash(a.hash, a.approved)
35146
+ "vault_revoke_hash",
35147
+ "Withdraws the EIP-1271 approval of a 32-byte hash by the vault (USDG) or by the agent's vault of a Borro stock (vault_lock status lists them); at once, under the lock too; lands as done when there is none. No tool approves a hash it is given: USDG takes the vault's EIP-1271 answer as a permit or a transfer authorization of its funds, so the vault only approves digests this server builds and checks itself (Raeto's login message, OstraBreaker's limits, Ostrapad's actions).",
35148
+ { hash: bytes323, asset: borroAsset.optional().describe("USDG (default) or a Borro stock's ticker") },
35149
+ (a) => agent.borro.withdrawApproval(a.asset ?? "USDG", a.hash)
35063
35150
  );
35064
35151
  tool(
35065
35152
  "guard_vault",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ostraagent/mcp",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
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",