@capxul/mcp 4.2.0-rc.7 → 4.2.0-rc.9

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.
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "source": {
3
- "commit": "8686db25e55ef5d7e7b3b6a4314e077c5e00d54a",
4
- "tree": "dbde050fc2c894e1eba477c371d19d6999690ee8",
3
+ "commit": "1c96a26356e8f36402e47eb4ac60bb89d6aeed7f",
4
+ "tree": "375f49f76377eeec331fd44ce17238d4a6f02ad4",
5
5
  "branch": "codex/programme-rc",
6
6
  "repository": "https://github.com/Xelmar-tech/infrastructure",
7
7
  "lockfileSha256": "5870f53b72706d103220c1bcf733cb4fb71196aea32a9cfbdc277d73cc959264"
8
8
  },
9
9
  "name": "@capxul/mcp",
10
- "version": "4.2.0-rc.7"
10
+ "version": "4.2.0-rc.9"
11
11
  }
package/dist/main.mjs CHANGED
@@ -852,6 +852,7 @@ const COUNTRY_CODE_RE = /^[A-Z]{2}$/;
852
852
  const ACCOUNT_ID_RE = /^account_[0-9A-Za-z]+$/;
853
853
  const PARTY_ID_RE$1 = /^party_[0-9A-Za-z]+$/;
854
854
  const APP_ID_RE = /^app_[0-7][0-9A-HJKMNP-TV-Z]{25}$/;
855
+ const TX_HASH_RE = /^0x[0-9a-f]{64}$/i;
855
856
  const WEI_RE = /^[0-9]+$/;
856
857
  Math.floor(Number.MAX_SAFE_INTEGER / 1e3);
857
858
  function toAddress(raw) {
@@ -903,6 +904,10 @@ function toPublishableKey(raw) {
903
904
  if (typeof raw !== "string" || !PUBLISHABLE_KEY_PATTERN.test(raw)) throw Errors.invalidInput("publishableKey", invalidValueReason("must match cap_pk_(test|live) plus 32 Crockford base32 chars", raw));
904
905
  return raw;
905
906
  }
907
+ function toTxHash(raw) {
908
+ if (typeof raw !== "string" || !TX_HASH_RE.test(raw)) throw Errors.invalidInput("txHash", invalidValueReason("must be 0x + 64 hex chars", raw));
909
+ return raw.toLowerCase();
910
+ }
906
911
  function toEpochMs(raw) {
907
912
  assertSafeNonNegativeInteger(raw, "epochMs");
908
913
  return raw;
@@ -982,19 +987,108 @@ function invalidValueReason(prefix, raw) {
982
987
  //#region ../../packages/config/src/tokens.ts
983
988
  /** `TestUSDC` ("USDX") — Base Sepolia, 6 decimals, open `mint`. (Canon §1.) */
984
989
  const USDX_ADDRESS_BASE_SEPOLIA = "0xf09fcbb6df9f8f918e58f9c8ab15a76ade862b77";
990
+ const USDX_DEPLOYMENT_START_BLOCK = 39860173;
991
+ /**
992
+ * The ERC-20 `symbol()` the deployed `TestUSDC` contract returns
993
+ * (`packages/contracts/src/TestUSDC.sol:10`). It travels with the address and
994
+ * the decimals so a holdings row can never carry one token's address with
995
+ * another token's display symbol. This is the display symbol, not the
996
+ * currency: see `USDX_CURRENCY` below.
997
+ */
998
+ const USDX_SYMBOL = "USDX";
999
+ const SYNTHETIC_TEST_ASSET_SOURCE = "synthetic-test-liquidity";
985
1000
  const USDX_ASSET = {
986
1001
  assetId: assetIdFor(84532, USDX_ADDRESS_BASE_SEPOLIA),
987
1002
  chainId: toChainId(84532),
988
- symbol: "USDX",
1003
+ symbol: USDX_SYMBOL,
989
1004
  peg: toCurrencyCode("USD"),
990
1005
  tokenAddress: USDX_ADDRESS_BASE_SEPOLIA,
991
- deploymentStartBlock: 39860173,
992
- decimals: 6
1006
+ deploymentStartBlock: USDX_DEPLOYMENT_START_BLOCK,
1007
+ decimals: 6,
1008
+ source: "settlement"
993
1009
  };
994
- [{
1010
+ /**
1011
+ * The settlement tier: rows the exact-transfer path and the Movement scanner
1012
+ * admit, each carrying the legacy `currency` projection those readers expect.
1013
+ * Adding an owned test asset here would put candidate liquidity on the
1014
+ * money-write path, so M03's rows go to the candidate tier below instead.
1015
+ */
1016
+ const CONFIGURED_MONEY_ASSETS = [{
995
1017
  ...USDX_ASSET,
996
1018
  currency: USDX_ASSET.peg
997
- }].map((asset) => asset.tokenAddress.toLowerCase());
1019
+ }];
1020
+ /**
1021
+ * The candidate tier: five owned test assets, every field read back from Base
1022
+ * Sepolia after the deployment that created it (chain 84532, blocks
1023
+ * 46,465,978–46,465,982). Real contracts, authored liquidity, no settlement
1024
+ * admission — so rollback is removing consumers, never relabelling a
1025
+ * deployed address or deleting its history.
1026
+ *
1027
+ * `symbol` is the display string the owned contract returns. A
1028
+ * provider-supported USDC would be a different contract at a different
1029
+ * address returning the same symbol, which is why the resolvers below key on
1030
+ * `assetId`, or on chain plus address, and never on a symbol.
1031
+ */
1032
+ const SYNTHETIC_TEST_ASSETS = [
1033
+ {
1034
+ assetId: assetIdFor(84532, "0xb3d8566fb90f7df939f4bc08a09b19047117f549"),
1035
+ chainId: toChainId(84532),
1036
+ tokenAddress: "0xb3d8566fb90f7df939f4bc08a09b19047117f549",
1037
+ symbol: "USDC",
1038
+ decimals: 6,
1039
+ peg: toCurrencyCode("USD"),
1040
+ deploymentStartBlock: 46465978,
1041
+ deploymentTransaction: "0xa7fb8bb9d1fd1cc5fe2fc42fa4cdccace7742efae8ea99f091a283e5709343de",
1042
+ source: SYNTHETIC_TEST_ASSET_SOURCE
1043
+ },
1044
+ {
1045
+ assetId: assetIdFor(84532, "0x7ddcfb6aaffb9908c4c2db308b784c269eec8a01"),
1046
+ chainId: toChainId(84532),
1047
+ tokenAddress: "0x7ddcfb6aaffb9908c4c2db308b784c269eec8a01",
1048
+ symbol: "USDT",
1049
+ decimals: 6,
1050
+ peg: toCurrencyCode("USD"),
1051
+ deploymentStartBlock: 46465979,
1052
+ deploymentTransaction: "0xa17ad76fe8b24cdf1fbcff245c3eb022444710cf9fa7b5ee59818be654f4ae10",
1053
+ source: SYNTHETIC_TEST_ASSET_SOURCE
1054
+ },
1055
+ {
1056
+ assetId: assetIdFor(84532, "0x1d93d525f73453fe18eebf044e8a3954bfe3721e"),
1057
+ chainId: toChainId(84532),
1058
+ tokenAddress: "0x1d93d525f73453fe18eebf044e8a3954bfe3721e",
1059
+ symbol: "WETH",
1060
+ decimals: 18,
1061
+ peg: null,
1062
+ deploymentStartBlock: 46465980,
1063
+ deploymentTransaction: "0xcb9b2d024686fac93ec1243ccf02d3567fd531da46176115660cba2ba644b09d",
1064
+ source: SYNTHETIC_TEST_ASSET_SOURCE
1065
+ },
1066
+ {
1067
+ assetId: assetIdFor(84532, "0x8fe52c70aa9f7b6d74d8bb26eca33d87413cb624"),
1068
+ chainId: toChainId(84532),
1069
+ tokenAddress: "0x8fe52c70aa9f7b6d74d8bb26eca33d87413cb624",
1070
+ symbol: "WBTC",
1071
+ decimals: 8,
1072
+ peg: null,
1073
+ deploymentStartBlock: 46465981,
1074
+ deploymentTransaction: "0x728ba7b430ffd141f83d0ed5ce429d050049d7b097c936fd11e9c572d1243854",
1075
+ source: SYNTHETIC_TEST_ASSET_SOURCE
1076
+ },
1077
+ {
1078
+ assetId: assetIdFor(84532, "0x5865fe9787ac214feb5facaebaec06969ac0a9a3"),
1079
+ chainId: toChainId(84532),
1080
+ tokenAddress: "0x5865fe9787ac214feb5facaebaec06969ac0a9a3",
1081
+ symbol: "cNGN",
1082
+ decimals: 6,
1083
+ peg: toCurrencyCode("NGN"),
1084
+ deploymentStartBlock: 46465982,
1085
+ deploymentTransaction: "0xcedf254bcef57fc64189261a5ab1161bc4fde370e470933f627c4d8ddb5a2e26",
1086
+ source: SYNTHETIC_TEST_ASSET_SOURCE
1087
+ }
1088
+ ];
1089
+ toChainId(84532);
1090
+ [...CONFIGURED_MONEY_ASSETS, ...SYNTHETIC_TEST_ASSETS];
1091
+ CONFIGURED_MONEY_ASSETS.map((asset) => asset.tokenAddress.toLowerCase());
998
1092
  //#endregion
999
1093
  //#region ../../node_modules/.pnpm/abitype@1.2.3_typescript@7.0.2_zod@4.4.3/node_modules/abitype/dist/esm/regex.js
1000
1094
  function execTyped(regex, string) {
@@ -6794,6 +6888,8 @@ const CAPXUL_OPERATIONS = {
6794
6888
  emitAnnotationSaved: "activity.emitAnnotationSaved",
6795
6889
  get: "activity.get",
6796
6890
  list: "activity.list",
6891
+ /** The SDK's live Activity read over the indexer's SSE stream. */
6892
+ subscribe: "activity.subscribe",
6797
6893
  summary: "activity.summary"
6798
6894
  },
6799
6895
  addressBook: {
@@ -7277,6 +7373,17 @@ const AuthFailedProps = Schema.Struct({
7277
7373
  auth_type: OptionalString,
7278
7374
  reason: OptionalString
7279
7375
  });
7376
+ const IdentityRefusedProps = Schema.Struct({
7377
+ ...TelemetryEnvelopeProps,
7378
+ /** The refused identity event tag, for example CreateOrganization. */
7379
+ event: OptionalString,
7380
+ /** The machine's refusal code: WRONG_STATE, SUPERSEDED or STALE_EPOCH. */
7381
+ refusal_code: OptionalString,
7382
+ /** The machine slot the request targeted, for example identity:org. */
7383
+ slot: OptionalString,
7384
+ /** The machine state at refusal, for example authenticated:claiming. */
7385
+ state: OptionalString
7386
+ });
7280
7387
  const AuthSignedOutProps = Schema.Struct(TelemetryEnvelopeProps);
7281
7388
  const ProvisioningSafeCreatedProps = Schema.Struct({
7282
7389
  ...TelemetryEnvelopeProps,
@@ -7727,6 +7834,10 @@ Schema.Struct({
7727
7834
  name: Schema.Literal("auth_failed"),
7728
7835
  props: AuthFailedProps
7729
7836
  });
7837
+ Schema.Struct({
7838
+ name: Schema.Literal("identity_refused"),
7839
+ props: IdentityRefusedProps
7840
+ });
7730
7841
  Schema.Struct({
7731
7842
  name: Schema.Literal("auth_signed_out"),
7732
7843
  props: AuthSignedOutProps
@@ -8536,6 +8647,10 @@ const BootstrapEnvelope = Schema.Struct({
8536
8647
  siteBaseUrl: Schema.String,
8537
8648
  openfortPublishableKey: Schema.String,
8538
8649
  shieldPublishableKey: Schema.String,
8650
+ /** Which Openfort third-party auth provider the signer presents; absent means better-auth. */
8651
+ openfortAuthProvider: Schema.optional(Schema.Literals(["better-auth", "oidc"])),
8652
+ /** Origin of the Ponder indexer the SDK reads directly; absent means no indexer reads. */
8653
+ indexerUrl: Schema.optional(Schema.String),
8539
8654
  engineeringTelemetry: Schema.optional(EngineeringTelemetryBootstrapPolicy)
8540
8655
  })
8541
8656
  });
@@ -8610,6 +8725,7 @@ const CAPXUL_FUNCTIONS = {
8610
8725
  prepareOrganizationPaymentExecution: "moneyExecution/paymentCommandActions:prepareOrganizationPaymentExecution",
8611
8726
  submitPaymentCommandExecution: "moneyExecution/paymentCommandActions:submitPaymentCommandExecution"
8612
8727
  },
8728
+ "moneyExecution/providerSendWitness": { read: "moneyExecution/providerSendWitness:read" },
8613
8729
  media: {
8614
8730
  generateUploadUrl: "media:generateUploadUrl",
8615
8731
  setOrgLogo: "media:setOrgLogo",
@@ -8658,8 +8774,7 @@ const CAPXUL_FUNCTIONS = {
8658
8774
  listMine: "org/queries:listMine",
8659
8775
  listRolesByOrgId: "org/queries:listRolesByOrgId",
8660
8776
  loadByOrgId: "org/queries:loadByOrgId",
8661
- me: "org/queries:me",
8662
- pendingSetup: "org/queries:pendingSetup"
8777
+ me: "org/queries:me"
8663
8778
  },
8664
8779
  "smartAccount/actions": {
8665
8780
  claim: "smartAccount/actions:claim",
@@ -8670,6 +8785,11 @@ const CAPXUL_FUNCTIONS = {
8670
8785
  loadByAuthUserId: "smartAccount/queries:loadByAuthUserId",
8671
8786
  loadBySmartAccountAddress: "smartAccount/queries:loadBySmartAccountAddress"
8672
8787
  },
8788
+ e2eControls: {
8789
+ holdUserOperationReceipt: "e2eControls:holdUserOperationReceipt",
8790
+ isUserOperationReceiptHeld: "e2eControls:isUserOperationReceiptHeld",
8791
+ releaseUserOperationReceipt: "e2eControls:releaseUserOperationReceipt"
8792
+ },
8673
8793
  system: { health: "system:health" }
8674
8794
  };
8675
8795
  //#endregion
@@ -12258,7 +12378,7 @@ const moneyExecutionContract = {
12258
12378
  };
12259
12379
  //#endregion
12260
12380
  //#region ../../packages/sdk/package.json
12261
- var version$1 = "4.2.0-rc.7";
12381
+ var version$1 = "4.2.0-rc.9";
12262
12382
  //#endregion
12263
12383
  //#region ../../packages/sdk/src/telemetry/exception-projection.ts
12264
12384
  /** Fixed fallback for failures that have no safe message. */
@@ -12355,6 +12475,17 @@ function observeFailedResult(result, adapter, operation, origin) {
12355
12475
  return result;
12356
12476
  }
12357
12477
  /**
12478
+ * @internal Reports a failure that can never become a method result.
12479
+ *
12480
+ * A reactive stream is torn down after its call already returned, so nothing
12481
+ * downstream observes it. `captureException` is the right kind: the caller did
12482
+ * not ask for the end, so it is not an expected product outcome.
12483
+ */
12484
+ function observeStreamFailure(adapter, operation, cause) {
12485
+ if (adapter === void 0) return;
12486
+ report(adapter, "exception", operation, cause, resolveAdapterSnapshot(adapter));
12487
+ }
12488
+ /**
12358
12489
  * One failure, one event. The identity machine reports a failed transition
12359
12490
  * first; when the same `CapxulError` then surfaces as a public method result
12360
12491
  * (directly or down its `cause` chain), the boundary drops that second report.
@@ -15272,7 +15403,7 @@ function isTransientBootstrapFailure(error) {
15272
15403
  const status = error.details?.httpStatus;
15273
15404
  return typeof status === "number" && isTransientHttpStatus(status);
15274
15405
  }
15275
- async function safeText(res) {
15406
+ async function safeText$1(res) {
15276
15407
  try {
15277
15408
  return await res.text();
15278
15409
  } catch {
@@ -15345,6 +15476,8 @@ var HttpBootstrapAdapter = class {
15345
15476
  siteBaseUrl: normalizeRuntimeUrl("siteBaseUrl", decodedState.siteBaseUrl),
15346
15477
  openfortPublishableKey: decodedState.openfortPublishableKey,
15347
15478
  shieldPublishableKey: decodedState.shieldPublishableKey,
15479
+ ...decodedState.openfortAuthProvider === void 0 ? {} : { openfortAuthProvider: decodedState.openfortAuthProvider },
15480
+ ...decodedState.indexerUrl === void 0 ? {} : { indexerUrl: decodedState.indexerUrl },
15348
15481
  ...decodedEngineeringTelemetry !== void 0 && Result.isSuccess(decodedEngineeringTelemetry) ? { engineeringTelemetry: decodedEngineeringTelemetry.success } : {}
15349
15482
  };
15350
15483
  },
@@ -15353,7 +15486,7 @@ var HttpBootstrapAdapter = class {
15353
15486
  return bootstrapErrorFromCapxul("malformedBody", Errors.providerError("convex", "bootstrap", cause instanceof Error ? cause : new Error(String(cause))));
15354
15487
  }
15355
15488
  });
15356
- return Effect.promise(() => safeText(res)).pipe(Effect.flatMap((body) => {
15489
+ return Effect.promise(() => safeText$1(res)).pipe(Effect.flatMap((body) => {
15357
15490
  if (res.status === 401 || body.startsWith("NOT_AUTHENTICATED")) return Effect.fail(bootstrapErrorFromCapxul("notAuthenticated", Errors.notAuthenticated()));
15358
15491
  if (res.status === 400 || body.startsWith("INVALID_INPUT")) return Effect.fail(bootstrapErrorFromCapxul("invalidInput", Errors.invalidInput("publishableKey", "rejected by bootstrap")));
15359
15492
  const responseBody = body.slice(0, 300);
@@ -18536,6 +18669,12 @@ function makeConnectionId() {
18536
18669
  }
18537
18670
  //#endregion
18538
18671
  //#region ../../packages/sdk/src/adapters/convex-call/ConvexCallAdapter.ts
18672
+ /**
18673
+ * A socket that has not emitted `close` within this window is not going to.
18674
+ * ponytail: one fixed bound; `closeTimeoutMs` is the knob if a slow runtime
18675
+ * ever needs a different one.
18676
+ */
18677
+ const DEFAULT_CLOSE_TIMEOUT_MS = 5e3;
18539
18678
  /** Exact floor-first allowlist; every additional handler must migrate its validator first. */
18540
18679
  const OBSERVED_CONVEX_ACTIONS = /* @__PURE__ */ new Set([
18541
18680
  "payroll/actions:authorizeRun",
@@ -18604,7 +18743,9 @@ var ConvexCallAdapter = class {
18604
18743
  #applicationId;
18605
18744
  #observation;
18606
18745
  #connectionMonitor;
18746
+ #closeTimeoutMs;
18607
18747
  constructor(deps) {
18748
+ this.#closeTimeoutMs = deps.closeTimeoutMs ?? DEFAULT_CLOSE_TIMEOUT_MS;
18608
18749
  this.#connectionMonitor = new ConvexConnectionMonitor();
18609
18750
  const webSocketConstructor = this.#connectionMonitor.observedWebSocketConstructor();
18610
18751
  this.#client = deps.client ?? new ConvexClient(deps.convexUrl, webSocketConstructor === void 0 ? {} : { webSocketConstructor });
@@ -18703,11 +18844,33 @@ var ConvexCallAdapter = class {
18703
18844
  callback({ status: "loading" });
18704
18845
  })));
18705
18846
  }
18847
+ /**
18848
+ * Teardown always completes. `ConvexClient.close()` resolves only when the
18849
+ * socket emits `close`, and `WebSocketManager.close()` in the `connecting`
18850
+ * state arms `ws.onopen = () => ws.close()` without ever closing the socket
18851
+ * itself — so a handshake that stalls right after a server-initiated close
18852
+ * (code 1012, which is what a Convex deploy sends) leaves that promise
18853
+ * pending for as long as the page lives. Convex marks the client closed and
18854
+ * terminates the socket manager synchronously before returning it, so
18855
+ * abandoning it drops nothing. A rejection is dropped for the same reason —
18856
+ * the layer release turns it into a defect that fails the whole scope close,
18857
+ * and teardown has no caller who can act on it.
18858
+ */
18706
18859
  async close() {
18707
18860
  this.#connectionMonitor.close();
18708
- await this.#client.close();
18861
+ await settledWithin(this.#client.close(), this.#closeTimeoutMs);
18709
18862
  }
18710
18863
  };
18864
+ function settledWithin(promise, timeoutMs) {
18865
+ return new Promise((resolve) => {
18866
+ const timer = setTimeout(resolve, timeoutMs);
18867
+ const finish = () => {
18868
+ clearTimeout(timer);
18869
+ resolve();
18870
+ };
18871
+ promise.then(finish, finish);
18872
+ });
18873
+ }
18711
18874
  function ConvexCallLayer(deps) {
18712
18875
  return Layer.effect(ConvexCallPortTag, Effect.acquireRelease(Effect.sync(() => new ConvexCallAdapter(deps)), (adapter) => Effect.promise(() => adapter.close()).pipe(Effect.orDie)));
18713
18876
  }
@@ -42764,8 +42927,9 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
42764
42927
  });
42765
42928
  throw failOpenfort("configure", "no-secure-context", /* @__PURE__ */ new Error("Web Crypto unavailable: browser is not a secure context"));
42766
42929
  }
42767
- function betterAuthSessionUrl() {
42768
- return `${authBaseUrl}/get-session`;
42930
+ const authProvider = bootstrap.openfortAuthProvider ?? "better-auth";
42931
+ function accessTokenUrl() {
42932
+ return authProvider === "oidc" ? `${authBaseUrl}/convex/token` : `${authBaseUrl}/get-session`;
42769
42933
  }
42770
42934
  function encryptionSessionUrl() {
42771
42935
  return `${authBaseUrl}/encryption-session`;
@@ -42788,7 +42952,7 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
42788
42952
  tokenCalls += 1;
42789
42953
  const tokenStartedAt = Date.now();
42790
42954
  try {
42791
- const response = await fetch(betterAuthSessionUrl(), {
42955
+ const response = await fetch(accessTokenUrl(), {
42792
42956
  credentials: "include",
42793
42957
  cache: "no-store"
42794
42958
  });
@@ -42801,7 +42965,8 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
42801
42965
  });
42802
42966
  return null;
42803
42967
  }
42804
- const token = (await response.json()).session?.token?.trim();
42968
+ const body = await response.json();
42969
+ const token = (authProvider === "oidc" ? body.token : body.session?.token)?.trim();
42805
42970
  if (token === void 0 || token.length === 0) {
42806
42971
  diagnostic?.trace("openfort.token", {
42807
42972
  ok: false,
@@ -42813,6 +42978,7 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
42813
42978
  diagnostic?.trace("openfort.token", {
42814
42979
  ok: true,
42815
42980
  tokenPresent: true,
42981
+ provider: authProvider,
42816
42982
  call: tokenCalls,
42817
42983
  duration_ms: Date.now() - tokenStartedAt,
42818
42984
  elapsed_ms: sinceCycleStart()
@@ -42831,7 +42997,7 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
42831
42997
  baseConfiguration: { publishableKey: bootstrap.openfortPublishableKey },
42832
42998
  shieldConfiguration: { shieldPublishableKey: bootstrap.shieldPublishableKey },
42833
42999
  thirdPartyAuth: {
42834
- provider: o$13.BETTER_AUTH,
43000
+ provider: authProvider === "oidc" ? o$13.OIDC : o$13.BETTER_AUTH,
42835
43001
  getAccessToken: fetchBetterAuthAccessToken
42836
43002
  }
42837
43003
  });
@@ -45940,11 +46106,28 @@ const authFailure = (record, reason, mode) => ({
45940
46106
  ...linkage(record)
45941
46107
  }
45942
46108
  });
46109
+ const REFUSAL_EVENTS = /* @__PURE__ */ new Set([
46110
+ "ClaimAccount",
46111
+ "RetryAccount",
46112
+ "CreateOrganization",
46113
+ "AttachOrganization",
46114
+ "RetryOrganization"
46115
+ ]);
45943
46116
  function mapIdentityProductObservation(record, state) {
45944
46117
  if (record.machine !== "identity" || !IDENTITY_EVENT_TAG_SET.has(record.event)) return NONE;
45945
46118
  if (record.outcome === "cancelled") return NONE;
45946
46119
  if (record.slot === "identity:auth" && record.outcome === "refused") return authFailure(record, record.refusal_code, void 0);
45947
46120
  if (record.slot === "identity:auth" && record.outcome === "failed") return authFailure(record, record.error_code, record.failure?.mode);
46121
+ if (record.outcome === "refused" && REFUSAL_EVENTS.has(record.event)) return {
46122
+ name: "identity_refused",
46123
+ props: {
46124
+ event: record.event,
46125
+ refusal_code: record.refusal_code,
46126
+ slot: record.slot,
46127
+ state: record.state,
46128
+ ...linkage(record)
46129
+ }
46130
+ };
45948
46131
  if (record.outcome !== "applied") return NONE;
45949
46132
  switch (APPLIED_ACTION_BY_EVENT[record.event]) {
45950
46133
  case "none": return NONE;
@@ -46270,6 +46453,7 @@ function assembleCapxulClient(input) {
46270
46453
  identity: identityRuntime,
46271
46454
  bootstrap: input.bootstrap,
46272
46455
  accounts: { fund: accounts.fund },
46456
+ ...input.ports.indexerRead === void 0 ? {} : { indexer: input.ports.indexerRead },
46273
46457
  organizationSetup: { getProofReceipt: (orgId) => runPortEffect(input.ports.convexCall.query(organizationSetupProofReceiptQuery, { orgId })) },
46274
46458
  telemetry: input.ports.telemetry,
46275
46459
  captureExternalAddressAcknowledged: (address, organizationId) => {
@@ -46351,6 +46535,382 @@ function snapshotHostObservability(observability) {
46351
46535
  };
46352
46536
  }
46353
46537
  //#endregion
46538
+ //#region ../../packages/sdk/src/ports/indexer-read.ts
46539
+ /** The indexer's own page bound. Exceeding it is a caller defect, not a refusal. */
46540
+ const INDEXER_MAX_LIMIT = 1e3;
46541
+ const LOWER_CASE_ADDRESS = /^0x[0-9a-f]{40}$/;
46542
+ /**
46543
+ * Refuse a page the indexer would refuse, before any implementation spends a
46544
+ * round trip on it. Every implementation calls this, so a consumer test written
46545
+ * against the hermetic adapter cannot pass on input production rejects. Throws
46546
+ * a `CapxulError`; callers convert it to an `IndexerReadError`.
46547
+ */
46548
+ function assertIndexerPage(input) {
46549
+ const safe = String(input.safe).toLowerCase();
46550
+ if (!LOWER_CASE_ADDRESS.test(safe)) throw Errors.invalidInput("safe", "must be a lower-case EVM address");
46551
+ if (!Number.isSafeInteger(input.limit) || input.limit < 1 || input.limit > 1e3) throw Errors.invalidInput("limit", `must be an integer from 1 to ${INDEXER_MAX_LIMIT}`);
46552
+ const offset = input.offset ?? 0;
46553
+ if (!Number.isSafeInteger(offset) || offset < 0) throw Errors.invalidInput("offset", "must be a non-negative integer");
46554
+ return safe;
46555
+ }
46556
+ var IndexerReadError = class extends Data.TaggedError("IndexerReadError") {};
46557
+ function indexerReadErrorFromCapxul(operation, error, cause = error) {
46558
+ return new IndexerReadError({
46559
+ operation,
46560
+ publicCode: error.code,
46561
+ publicError: error,
46562
+ cause,
46563
+ ...error.details === void 0 ? {} : { details: error.details }
46564
+ });
46565
+ }
46566
+ Context.Service()("@capxul/sdk/ports/IndexerReadPort");
46567
+ //#endregion
46568
+ //#region ../../packages/sdk/src/adapters/ponder/decode.ts
46569
+ /**
46570
+ * Decode the indexer's rows at the trust boundary.
46571
+ *
46572
+ * Ponder returns the raw PostgreSQL result: `integer` arrives as a number,
46573
+ * `numeric(78)` and `text` arrive as strings. Amounts, block numbers and
46574
+ * timestamps are `numeric(78)`, so they become `bigint` here and never pass
46575
+ * through `Number`. Every decoder throws a `CapxulError`; the adapter turns
46576
+ * that into a typed `IndexerReadError`.
46577
+ */
46578
+ const HEX = /^0x[0-9a-fA-F]+$/;
46579
+ function field(row, key) {
46580
+ if (!(key in row)) throw Errors.invalidInput(key, "missing from the indexer row");
46581
+ return row[key];
46582
+ }
46583
+ function text(row, key) {
46584
+ const value = field(row, key);
46585
+ if (typeof value !== "string") throw Errors.invalidInput(key, "must be a string");
46586
+ return value;
46587
+ }
46588
+ function hex(row, key) {
46589
+ const value = text(row, key);
46590
+ if (!HEX.test(value)) throw Errors.invalidInput(key, "must be a hex string");
46591
+ return value.toLowerCase();
46592
+ }
46593
+ function integer(row, key) {
46594
+ const value = field(row, key);
46595
+ const parsed = typeof value === "string" ? Number(value) : value;
46596
+ if (typeof parsed !== "number" || !Number.isSafeInteger(parsed)) throw Errors.invalidInput(key, "must be an integer");
46597
+ return parsed;
46598
+ }
46599
+ /** `numeric(78)` crosses the wire as a decimal string; keep every digit. */
46600
+ function big(row, key) {
46601
+ const value = field(row, key);
46602
+ if (typeof value === "bigint") return value;
46603
+ const raw = typeof value === "number" ? String(value) : value;
46604
+ if (typeof raw !== "string" || !/^-?[0-9]+$/.test(raw)) throw Errors.invalidInput(key, "must be an exact integer amount");
46605
+ return BigInt(raw);
46606
+ }
46607
+ function asRow(value) {
46608
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw Errors.invalidInput("row", "must be an object");
46609
+ return value;
46610
+ }
46611
+ function identity(row) {
46612
+ return {
46613
+ id: text(row, "id"),
46614
+ chainId: toChainId(integer(row, "chain_id")),
46615
+ txHash: toTxHash(hex(row, "tx_hash")),
46616
+ transactionLogOrdinal: integer(row, "transaction_log_ordinal"),
46617
+ blockNumber: big(row, "block_number"),
46618
+ blockHash: hex(row, "block_hash"),
46619
+ timestamp: big(row, "timestamp")
46620
+ };
46621
+ }
46622
+ function decodeTransfer(value) {
46623
+ const row = asRow(value);
46624
+ return {
46625
+ ...identity(row),
46626
+ assetId: toAssetId(text(row, "asset_id")),
46627
+ token: toAddress(hex(row, "token")),
46628
+ from: toAddress(hex(row, "from")),
46629
+ to: toAddress(hex(row, "to")),
46630
+ amount: big(row, "amount")
46631
+ };
46632
+ }
46633
+ function decodePayment(value) {
46634
+ const row = asRow(value);
46635
+ return {
46636
+ ...identity(row),
46637
+ contract: toAddress(hex(row, "contract")),
46638
+ settlementId: hex(row, "settlement_id"),
46639
+ sender: toAddress(hex(row, "sender")),
46640
+ recipient: toAddress(hex(row, "recipient")),
46641
+ assetId: toAssetId(text(row, "asset_id")),
46642
+ token: toAddress(hex(row, "token")),
46643
+ amount: big(row, "amount"),
46644
+ kind: integer(row, "kind"),
46645
+ documentHash: hex(row, "document_hash")
46646
+ };
46647
+ }
46648
+ /** The `balance` view exposes the signed sum under the underlying column name `to`. */
46649
+ function decodeBalance(value) {
46650
+ const row = asRow(value);
46651
+ return {
46652
+ chainId: toChainId(integer(row, "chain_id")),
46653
+ assetId: toAssetId(text(row, "asset_id")),
46654
+ address: toAddress(hex(row, "to")),
46655
+ amount: big(row, "amount")
46656
+ };
46657
+ }
46658
+ const DECODERS = {
46659
+ transfer: decodeTransfer,
46660
+ payment: decodePayment,
46661
+ balance: decodeBalance
46662
+ };
46663
+ /** Decode one `/sql/db` or `/sql/live` payload. Ponder returns `{ rows: [...] }`. */
46664
+ function decodeRows(table, body) {
46665
+ const rows = asRow(body).rows;
46666
+ if (!Array.isArray(rows)) throw Errors.invalidInput("rows", "must be an array");
46667
+ const decode = DECODERS[table];
46668
+ return rows.map(decode);
46669
+ }
46670
+ /** Decode Ponder's native `/status`: `{ [chainName]: { id, block: { number, timestamp } } }`. */
46671
+ function decodeStatus(body) {
46672
+ const chains = asRow(body);
46673
+ return Object.entries(chains).map(([chain, value]) => {
46674
+ const entry = asRow(value);
46675
+ const block = asRow(field(entry, "block"));
46676
+ return {
46677
+ chain,
46678
+ chainId: toChainId(integer(entry, "id")),
46679
+ blockNumber: big(block, "number"),
46680
+ blockTimestamp: big(block, "timestamp")
46681
+ };
46682
+ });
46683
+ }
46684
+ //#endregion
46685
+ //#region ../../packages/sdk/src/adapters/ponder/statements.ts
46686
+ /**
46687
+ * The exact statements the indexer accepts.
46688
+ *
46689
+ * `apps/indexer/src/queries.ts` compiles its allowlist with `@ponder/client`
46690
+ * and compares `sql`, `params` and `typings` byte for byte against the caller's
46691
+ * statement. Anything else gets 403. The SDK therefore reproduces the compiled
46692
+ * text here rather than pulling Ponder and Drizzle into a published browser
46693
+ * bundle to regenerate three fixed statements.
46694
+ *
46695
+ * `statements.test.ts` pins the text. When the indexer schema or a query in
46696
+ * that allowlist changes, this file changes with it or every read fails 403.
46697
+ */
46698
+ const HEADS = {
46699
+ transfer: "select \"id\", \"chain_id\", \"tx_hash\", \"transaction_log_ordinal\", \"block_number\", \"block_hash\", \"timestamp\", \"asset_id\", \"token\", \"from\", \"to\", \"amount\" from \"transfer\" where (\"transfer\".\"from\" = $1 or \"transfer\".\"to\" = $2) order by \"transfer\".\"block_number\" desc, \"transfer\".\"id\" desc",
46700
+ payment: "select \"id\", \"chain_id\", \"tx_hash\", \"transaction_log_ordinal\", \"block_number\", \"block_hash\", \"timestamp\", \"contract\", \"settlement_id\", \"sender\", \"recipient\", \"asset_id\", \"token\", \"amount\", \"kind\", \"document_hash\" from \"payment\" where (\"payment\".\"sender\" = $1 or \"payment\".\"recipient\" = $2) order by \"payment\".\"block_number\" desc, \"payment\".\"id\" desc",
46701
+ balance: "select \"chain_id\", \"asset_id\", \"to\", \"amount\" from \"balance\" where \"balance\".\"to\" = $1"
46702
+ };
46703
+ /** How many times the statement binds the Safe address. */
46704
+ const ACTOR_PARAMS = {
46705
+ transfer: 2,
46706
+ payment: 2,
46707
+ balance: 1
46708
+ };
46709
+ /**
46710
+ * Build the compiled query for one table and page. Throws a `CapxulError` for
46711
+ * a page or address the indexer would refuse, so the caller never spends a
46712
+ * round trip to learn it built the request wrong.
46713
+ */
46714
+ function ponderQuery(table, input) {
46715
+ const safe = assertIndexerPage(input);
46716
+ const offset = input.offset ?? 0;
46717
+ const actors = ACTOR_PARAMS[table];
46718
+ const params = [];
46719
+ for (let i = 0; i < actors; i++) params.push(safe);
46720
+ params.push(input.limit);
46721
+ let sql = `${HEADS[table]} limit $${actors + 1}`;
46722
+ if (offset > 0) {
46723
+ params.push(offset);
46724
+ sql = `${sql} offset $${actors + 2}`;
46725
+ }
46726
+ return {
46727
+ sql,
46728
+ params,
46729
+ typings: params.map(() => "none")
46730
+ };
46731
+ }
46732
+ /**
46733
+ * The `?sql=` value. `@ponder/client` sends `superjson.stringify(query)`; for a
46734
+ * payload of strings and numbers superjson emits no `meta`, so the wire form is
46735
+ * exactly this envelope.
46736
+ */
46737
+ function ponderQueryParam(query) {
46738
+ return JSON.stringify({ json: query });
46739
+ }
46740
+ //#endregion
46741
+ //#region ../../packages/sdk/src/adapters/ponder/sse.ts
46742
+ /**
46743
+ * The smallest SSE reader that can carry an `Authorization` header.
46744
+ *
46745
+ * `@ponder/client` opens its live query with `EventSource`, which accepts no
46746
+ * headers, so it cannot reach the guarded indexer at all. The indexer's CORS
46747
+ * policy allows exactly one request header (`Authorization`), so this reader
46748
+ * sends nothing else. Ponder writes one `data:` line per frame.
46749
+ */
46750
+ async function readEventStream(body, onData, stopped) {
46751
+ const reader = body.getReader();
46752
+ const decoder = new TextDecoder();
46753
+ let buffer = "";
46754
+ try {
46755
+ for (;;) {
46756
+ const { done, value } = await reader.read();
46757
+ if (done || stopped()) return;
46758
+ buffer += decoder.decode(value, { stream: true });
46759
+ let split = buffer.indexOf("\n\n");
46760
+ while (split !== -1) {
46761
+ const frame = buffer.slice(0, split);
46762
+ buffer = buffer.slice(split + 2);
46763
+ const data = frameData(frame);
46764
+ if (data !== void 0) onData(data);
46765
+ if (stopped()) return;
46766
+ split = buffer.indexOf("\n\n");
46767
+ }
46768
+ }
46769
+ } finally {
46770
+ await reader.cancel().catch(() => void 0);
46771
+ }
46772
+ }
46773
+ function frameData(frame) {
46774
+ const lines = [];
46775
+ for (const line of frame.split("\n")) {
46776
+ if (!line.startsWith("data:")) continue;
46777
+ lines.push(line.slice(line.startsWith("data: ") ? 6 : 5));
46778
+ }
46779
+ return lines.length === 0 ? void 0 : lines.join("\n");
46780
+ }
46781
+ //#endregion
46782
+ //#region ../../packages/sdk/src/adapters/ponder/PonderIndexerAdapter.ts
46783
+ var PonderIndexerAdapter = class {
46784
+ #baseUrl;
46785
+ #token;
46786
+ #fetch;
46787
+ #observation;
46788
+ constructor(deps) {
46789
+ this.#baseUrl = deps.baseUrl.replace(/\/$/, "");
46790
+ this.#token = deps.token;
46791
+ this.#observation = deps.observation;
46792
+ this.#fetch = deps.fetch ?? ((input, init) => globalThis.fetch(input, init));
46793
+ }
46794
+ read(table, input) {
46795
+ const operation = `indexer.read.${table}`;
46796
+ return Effect.tryPromise({
46797
+ try: async () => {
46798
+ return decodeRows(table, await (await this.#request("db", table, input, operation)).json());
46799
+ },
46800
+ catch: (cause) => toIndexerReadError(operation, cause)
46801
+ });
46802
+ }
46803
+ subscribe(table, input, callback) {
46804
+ const operation = CAPXUL_OPERATIONS.activity.subscribe;
46805
+ return Effect.sync(() => {
46806
+ let stopped = false;
46807
+ const unsubscribe = () => {
46808
+ stopped = true;
46809
+ };
46810
+ callback({ status: "loading" });
46811
+ this.#stream(table, input, operation, callback, () => stopped).catch(() => void 0);
46812
+ return unsubscribe;
46813
+ });
46814
+ }
46815
+ status = Effect.tryPromise({
46816
+ try: async () => {
46817
+ const response = await this.#fetch(`${this.#baseUrl}/status`, { method: "GET" });
46818
+ if (!response.ok) throw httpError("indexer.status", response.status, await safeText(response));
46819
+ return decodeStatus(await response.json());
46820
+ },
46821
+ catch: (cause) => toIndexerReadError("indexer.status", cause)
46822
+ });
46823
+ /** One authenticated `/sql/:method` call. Throws a `CapxulError` on refusal. */
46824
+ async #request(method, table, input, operation) {
46825
+ const query = ponderQuery(table, input);
46826
+ const token = await this.#token({ forceRefreshToken: false });
46827
+ if (token === null || token === "") throw Errors.notAuthenticated();
46828
+ const url = `${this.#baseUrl}/sql/${method}?sql=${encodeURIComponent(ponderQueryParam(query))}`;
46829
+ const headers = { authorization: `Bearer ${token}` };
46830
+ if (method === "live") headers.accept = "text/event-stream";
46831
+ let response;
46832
+ try {
46833
+ response = await this.#fetch(url, {
46834
+ method: "GET",
46835
+ headers
46836
+ });
46837
+ } catch (cause) {
46838
+ throw Errors.networkError(operation, cause, {
46839
+ provider: "indexer",
46840
+ failure_mode: "upstream-down"
46841
+ });
46842
+ }
46843
+ if (!response.ok) throw httpError(operation, response.status, await safeText(response));
46844
+ return response;
46845
+ }
46846
+ /**
46847
+ * Follow the live stream until the caller unsubscribes or the indexer closes
46848
+ * it. The indexer closes the stream on token expiry or revoked authority
46849
+ * with no frame, so any end the caller did not ask for is a teardown: it
46850
+ * reaches `captureException` under `activity.subscribe` and the caller sees
46851
+ * one `error` snapshot.
46852
+ */
46853
+ async #stream(table, input, operation, callback, stopped) {
46854
+ let failure;
46855
+ try {
46856
+ const response = await this.#request("live", table, input, operation);
46857
+ if (response.body === null) throw Errors.providerError("indexer", operation, "empty stream");
46858
+ await readEventStream(response.body, (data) => {
46859
+ try {
46860
+ callback({
46861
+ status: "ok",
46862
+ value: decodeRows(table, JSON.parse(data))
46863
+ });
46864
+ } catch (cause) {
46865
+ failure = toPublicError$1(operation, cause);
46866
+ }
46867
+ }, () => stopped() || failure !== void 0);
46868
+ } catch (cause) {
46869
+ failure = toPublicError$1(operation, cause);
46870
+ }
46871
+ if (stopped()) return;
46872
+ const error = failure ?? Errors.capabilityUnavailable("indexer", operation);
46873
+ observeStreamFailure(this.#observation, CAPXUL_OPERATIONS.activity.subscribe, error);
46874
+ callback({
46875
+ status: "error",
46876
+ error
46877
+ });
46878
+ }
46879
+ };
46880
+ /**
46881
+ * Map an indexer refusal into the shared error vocabulary.
46882
+ *
46883
+ * 401 is the expected outcome of an expired or revoked session. 403 is not:
46884
+ * the indexer accepts only its own compiled statements for a Safe in the
46885
+ * claim, so a refusal means this SDK and that deployment disagree, or the
46886
+ * session gained a Safe and still holds the older token. Both must be loud.
46887
+ */
46888
+ function httpError(operation, status, body) {
46889
+ if (status === 401) return Errors.notAuthenticated();
46890
+ if (status === 429) return Errors.rateLimited({ resource: "indexer" });
46891
+ if (status === 503) return Errors.capabilityUnavailable("indexer", operation);
46892
+ return Errors.providerError("indexer", operation, body, {
46893
+ httpStatus: status,
46894
+ ...status >= 500 ? { failure_mode: "upstream-down" } : {},
46895
+ details: { reason: status === 403 ? "scope-refused" : "http-status" }
46896
+ });
46897
+ }
46898
+ function toPublicError$1(operation, cause) {
46899
+ if (cause instanceof CapxulError) return cause;
46900
+ return Errors.providerError("indexer", operation, cause);
46901
+ }
46902
+ function toIndexerReadError(operation, cause) {
46903
+ if (cause instanceof IndexerReadError) return cause;
46904
+ return indexerReadErrorFromCapxul(operation, toPublicError$1(operation, cause), cause);
46905
+ }
46906
+ async function safeText(response) {
46907
+ try {
46908
+ return await response.text();
46909
+ } catch {
46910
+ return "";
46911
+ }
46912
+ }
46913
+ //#endregion
46354
46914
  //#region ../../packages/sdk/src/adapters/_shared/clock.ts
46355
46915
  async function readClockNow(clock) {
46356
46916
  try {
@@ -46807,7 +47367,16 @@ async function createProductionAdapters(input) {
46807
47367
  }));
46808
47368
  const applicationLayer = Layer.merge(portsLayer, optionalEngineeringTelemetryLayer(bootstrapResult.value.engineeringTelemetry, resolvedInput.value.runtime));
46809
47369
  const context = await Effect.runPromise(Layer.buildWithScope(applicationLayer, scope));
46810
- const ports = await Effect.runPromise(collectProductionFlowPorts.pipe(Effect.provide(context)));
47370
+ const collected = await Effect.runPromise(collectProductionFlowPorts.pipe(Effect.provide(context)));
47371
+ const indexerUrl = input.indexerUrl ?? bootstrapResult.value.indexerUrl;
47372
+ const indexerRead = productionIndexerReadPort(indexerUrl === void 0 ? input : {
47373
+ ...input,
47374
+ indexerUrl
47375
+ }, collected, observation);
47376
+ const ports = indexerRead === void 0 ? collected : {
47377
+ ...collected,
47378
+ indexerRead
47379
+ };
46811
47380
  const stopConnectionObservation = observeProductionConvexConnection(ports.convexCall, context);
46812
47381
  const close = idempotentClose(async () => {
46813
47382
  try {
@@ -47139,6 +47708,28 @@ function idempotentClose(close) {
47139
47708
  await close();
47140
47709
  };
47141
47710
  }
47711
+ /**
47712
+ * Build the Ponder read port for this client, or `undefined` when no indexer
47713
+ * origin was supplied. The token provider is the same
47714
+ * `AuthClientPort.getConvexJwt` closure `ConvexCallPort` uses, so the indexer
47715
+ * and Convex see one session with one scope.
47716
+ */
47717
+ function productionIndexerReadPort(input, ports, observation) {
47718
+ if (input.indexerUrl === void 0 || input.indexerUrl === "") return void 0;
47719
+ return new PonderIndexerAdapter({
47720
+ baseUrl: normalizeHttpUrl("indexerUrl", input.indexerUrl),
47721
+ token: async ({ forceRefreshToken }) => {
47722
+ const token = await Effect.runPromise(Effect.result(ports.authClient.getConvexJwt({
47723
+ forceRefresh: forceRefreshToken,
47724
+ ...input.signal === void 0 ? {} : { signal: input.signal }
47725
+ })));
47726
+ if (Result.isFailure(token)) return null;
47727
+ return String(token.success.token);
47728
+ },
47729
+ ...input.fetch === void 0 ? {} : { fetch: input.fetch },
47730
+ ...observation === void 0 ? {} : { observation }
47731
+ });
47732
+ }
47142
47733
  //#endregion
47143
47734
  //#region src/tools/account.ts
47144
47735
  const READ = "account:read";
@@ -47750,7 +48341,7 @@ function stdioIdentity(env = process.env, options = {}) {
47750
48341
  }
47751
48342
  //#endregion
47752
48343
  //#region package.json
47753
- var version = "4.2.0-rc.7";
48344
+ var version = "4.2.0-rc.9";
47754
48345
  //#endregion
47755
48346
  //#region src/deps.ts
47756
48347
  const NO_IDENTITY = async () => void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capxul/mcp",
3
- "version": "4.2.0-rc.7",
3
+ "version": "4.2.0-rc.9",
4
4
  "private": false,
5
5
  "description": "Capxul MCP server for auth, Account readiness, identity, Organizations, relationships, requests, destinations, and testnet funding over Streamable HTTP.",
6
6
  "keywords": [
@@ -51,7 +51,7 @@
51
51
  "vite": "npm:@voidzero-dev/vite-plus-core@0.3.0",
52
52
  "vite-plus": "0.3.0",
53
53
  "@capxul/config": "0.3.0",
54
- "@capxul/sdk": "4.2.0-rc.7",
54
+ "@capxul/sdk": "4.2.0-rc.9",
55
55
  "@capxul/typescript-config": "0.0.0"
56
56
  },
57
57
  "scripts": {