@capxul/mcp 4.2.0-rc.1 → 4.2.0-rc.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.
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;
@@ -926,6 +931,10 @@ function toChainId(raw) {
926
931
  if (typeof raw !== "number" || !Number.isSafeInteger(raw) || raw <= 0) throw Errors.invalidInput("chainId", invalidValueReason("must be a positive safe integer", raw));
927
932
  return raw;
928
933
  }
934
+ function toWeiAmount(raw) {
935
+ if (typeof raw !== "string" || !WEI_RE.test(raw)) throw Errors.invalidInput("weiAmount", invalidValueReason("must be a non-negative integer string", raw));
936
+ return raw;
937
+ }
929
938
  function toCountryCode(raw) {
930
939
  if (typeof raw !== "string") throw Errors.invalidInput("countryCode", "must be a string");
931
940
  const upper = raw.toUpperCase();
@@ -982,19 +991,118 @@ function invalidValueReason(prefix, raw) {
982
991
  //#region ../../packages/config/src/tokens.ts
983
992
  /** `TestUSDC` ("USDX") — Base Sepolia, 6 decimals, open `mint`. (Canon §1.) */
984
993
  const USDX_ADDRESS_BASE_SEPOLIA = "0xf09fcbb6df9f8f918e58f9c8ab15a76ade862b77";
994
+ const USDX_DEPLOYMENT_START_BLOCK = 39860173;
995
+ /**
996
+ * The ERC-20 `symbol()` the deployed `TestUSDC` contract returns
997
+ * (`packages/contracts/src/TestUSDC.sol:10`). It travels with the address and
998
+ * the decimals so a holdings row can never carry one token's address with
999
+ * another token's display symbol. This is the display symbol, not the
1000
+ * currency: see `USDX_CURRENCY` below.
1001
+ */
1002
+ const USDX_SYMBOL = "USDX";
1003
+ const SYNTHETIC_TEST_ASSET_SOURCE = "synthetic-test-liquidity";
985
1004
  const USDX_ASSET = {
986
1005
  assetId: assetIdFor(84532, USDX_ADDRESS_BASE_SEPOLIA),
987
1006
  chainId: toChainId(84532),
988
- symbol: "USDX",
1007
+ symbol: USDX_SYMBOL,
989
1008
  peg: toCurrencyCode("USD"),
990
1009
  tokenAddress: USDX_ADDRESS_BASE_SEPOLIA,
991
- deploymentStartBlock: 39860173,
992
- decimals: 6
1010
+ deploymentStartBlock: USDX_DEPLOYMENT_START_BLOCK,
1011
+ decimals: 6,
1012
+ source: "settlement"
993
1013
  };
994
- [{
1014
+ /**
1015
+ * The settlement tier: rows the exact-transfer path and the Movement scanner
1016
+ * admit, each carrying the legacy `currency` projection those readers expect.
1017
+ * Adding an owned test asset here would put candidate liquidity on the
1018
+ * money-write path, so M03's rows go to the candidate tier below instead.
1019
+ */
1020
+ const CONFIGURED_MONEY_ASSETS = [{
995
1021
  ...USDX_ASSET,
996
1022
  currency: USDX_ASSET.peg
997
- }].map((asset) => asset.tokenAddress.toLowerCase());
1023
+ }];
1024
+ /**
1025
+ * The candidate tier: five owned test assets, every field read back from Base
1026
+ * Sepolia after the deployment that created it (chain 84532, blocks
1027
+ * 46,465,978–46,465,982). Real contracts, authored liquidity, no settlement
1028
+ * admission — so rollback is removing consumers, never relabelling a
1029
+ * deployed address or deleting its history.
1030
+ *
1031
+ * `symbol` is the display string the owned contract returns. A
1032
+ * provider-supported USDC would be a different contract at a different
1033
+ * address returning the same symbol, which is why the resolvers below key on
1034
+ * `assetId`, or on chain plus address, and never on a symbol.
1035
+ */
1036
+ const SYNTHETIC_TEST_ASSETS = [
1037
+ {
1038
+ assetId: assetIdFor(84532, "0xb3d8566fb90f7df939f4bc08a09b19047117f549"),
1039
+ chainId: toChainId(84532),
1040
+ tokenAddress: "0xb3d8566fb90f7df939f4bc08a09b19047117f549",
1041
+ symbol: "USDC",
1042
+ decimals: 6,
1043
+ peg: toCurrencyCode("USD"),
1044
+ deploymentStartBlock: 46465978,
1045
+ deploymentTransaction: "0xa7fb8bb9d1fd1cc5fe2fc42fa4cdccace7742efae8ea99f091a283e5709343de",
1046
+ source: SYNTHETIC_TEST_ASSET_SOURCE
1047
+ },
1048
+ {
1049
+ assetId: assetIdFor(84532, "0x7ddcfb6aaffb9908c4c2db308b784c269eec8a01"),
1050
+ chainId: toChainId(84532),
1051
+ tokenAddress: "0x7ddcfb6aaffb9908c4c2db308b784c269eec8a01",
1052
+ symbol: "USDT",
1053
+ decimals: 6,
1054
+ peg: toCurrencyCode("USD"),
1055
+ deploymentStartBlock: 46465979,
1056
+ deploymentTransaction: "0xa17ad76fe8b24cdf1fbcff245c3eb022444710cf9fa7b5ee59818be654f4ae10",
1057
+ source: SYNTHETIC_TEST_ASSET_SOURCE
1058
+ },
1059
+ {
1060
+ assetId: assetIdFor(84532, "0x1d93d525f73453fe18eebf044e8a3954bfe3721e"),
1061
+ chainId: toChainId(84532),
1062
+ tokenAddress: "0x1d93d525f73453fe18eebf044e8a3954bfe3721e",
1063
+ symbol: "WETH",
1064
+ decimals: 18,
1065
+ peg: null,
1066
+ deploymentStartBlock: 46465980,
1067
+ deploymentTransaction: "0xcb9b2d024686fac93ec1243ccf02d3567fd531da46176115660cba2ba644b09d",
1068
+ source: SYNTHETIC_TEST_ASSET_SOURCE
1069
+ },
1070
+ {
1071
+ assetId: assetIdFor(84532, "0x8fe52c70aa9f7b6d74d8bb26eca33d87413cb624"),
1072
+ chainId: toChainId(84532),
1073
+ tokenAddress: "0x8fe52c70aa9f7b6d74d8bb26eca33d87413cb624",
1074
+ symbol: "WBTC",
1075
+ decimals: 8,
1076
+ peg: null,
1077
+ deploymentStartBlock: 46465981,
1078
+ deploymentTransaction: "0x728ba7b430ffd141f83d0ed5ce429d050049d7b097c936fd11e9c572d1243854",
1079
+ source: SYNTHETIC_TEST_ASSET_SOURCE
1080
+ },
1081
+ {
1082
+ assetId: assetIdFor(84532, "0x5865fe9787ac214feb5facaebaec06969ac0a9a3"),
1083
+ chainId: toChainId(84532),
1084
+ tokenAddress: "0x5865fe9787ac214feb5facaebaec06969ac0a9a3",
1085
+ symbol: "cNGN",
1086
+ decimals: 6,
1087
+ peg: toCurrencyCode("NGN"),
1088
+ deploymentStartBlock: 46465982,
1089
+ deploymentTransaction: "0xcedf254bcef57fc64189261a5ab1161bc4fde370e470933f627c4d8ddb5a2e26",
1090
+ source: SYNTHETIC_TEST_ASSET_SOURCE
1091
+ }
1092
+ ];
1093
+ toChainId(84532);
1094
+ /**
1095
+ * Every known asset in ONE identity space. The tiers above declare admission;
1096
+ * this is the only list an identity resolves against, so an asset is never
1097
+ * two different things depending on which list a caller happened to read.
1098
+ */
1099
+ const MONEY_ASSET_REGISTRY = [...CONFIGURED_MONEY_ASSETS, ...SYNTHETIC_TEST_ASSETS];
1100
+ CONFIGURED_MONEY_ASSETS.map((asset) => asset.tokenAddress.toLowerCase());
1101
+ /** The registry row for `assetId` across every tier, or `null`. */
1102
+ function configuredMoneyAssetById(assetId) {
1103
+ const canonical = toAssetId(assetId);
1104
+ return MONEY_ASSET_REGISTRY.find((asset) => asset.assetId === canonical) ?? null;
1105
+ }
998
1106
  //#endregion
999
1107
  //#region ../../node_modules/.pnpm/abitype@1.2.3_typescript@7.0.2_zod@4.4.3/node_modules/abitype/dist/esm/regex.js
1000
1108
  function execTyped(regex, string) {
@@ -3738,6 +3846,12 @@ const USDX_BASE_SEPOLIA_TOKEN = {
3738
3846
  USDX_BASE_SEPOLIA_TOKEN.chainId;
3739
3847
  USDX_BASE_SEPOLIA_TOKEN.chainId;
3740
3848
  10n ** BigInt(6);
3849
+ /**
3850
+ * The bootstrap manage role. It administers people and roles, cannot spend,
3851
+ * and is not a signer of the Organization Safe; "Owner" implied the latter,
3852
+ * so the label is Admin (ruled 2026-09-06).
3853
+ */
3854
+ const ADMIN_ROLE_LABEL = "Admin";
3741
3855
  function normalizeOrgRoleLabel(label) {
3742
3856
  const normalized = label.trim().replace(/\s+/g, " ");
3743
3857
  if (normalized.length === 0) throw Errors.invalidInput("role.label", "must be a non-empty role label");
@@ -6788,6 +6902,8 @@ const CAPXUL_OPERATIONS = {
6788
6902
  emitAnnotationSaved: "activity.emitAnnotationSaved",
6789
6903
  get: "activity.get",
6790
6904
  list: "activity.list",
6905
+ /** The SDK's live Activity read over the indexer's SSE stream. */
6906
+ subscribe: "activity.subscribe",
6791
6907
  summary: "activity.summary"
6792
6908
  },
6793
6909
  addressBook: {
@@ -7271,6 +7387,17 @@ const AuthFailedProps = Schema.Struct({
7271
7387
  auth_type: OptionalString,
7272
7388
  reason: OptionalString
7273
7389
  });
7390
+ const IdentityRefusedProps = Schema.Struct({
7391
+ ...TelemetryEnvelopeProps,
7392
+ /** The refused identity event tag, for example CreateOrganization. */
7393
+ event: OptionalString,
7394
+ /** The machine's refusal code: WRONG_STATE, SUPERSEDED or STALE_EPOCH. */
7395
+ refusal_code: OptionalString,
7396
+ /** The machine slot the request targeted, for example identity:org. */
7397
+ slot: OptionalString,
7398
+ /** The machine state at refusal, for example authenticated:claiming. */
7399
+ state: OptionalString
7400
+ });
7274
7401
  const AuthSignedOutProps = Schema.Struct(TelemetryEnvelopeProps);
7275
7402
  const ProvisioningSafeCreatedProps = Schema.Struct({
7276
7403
  ...TelemetryEnvelopeProps,
@@ -7658,10 +7785,44 @@ const DepositSettledProps = Schema.Struct({
7658
7785
  });
7659
7786
  const PermissionMirrorVerificationProps = Schema.Struct({
7660
7787
  ...TelemetryEnvelopeProps,
7788
+ /** The RECEIPT verification outcome. What the mirror then did is separate. */
7661
7789
  outcome: Schema.String,
7662
7790
  reason: Schema.String,
7663
7791
  chain_id: Schema.Number,
7664
- retry_count: Schema.Number
7792
+ retry_count: Schema.Number,
7793
+ /**
7794
+ * #1960 R02, consumed by R03. A verified receipt is not an applied Permission:
7795
+ * these two optional fields let the producer say what the applying mutation
7796
+ * actually returned, and which command it was, without either fact rewriting
7797
+ * the receipt outcome above. Absent when application has not happened yet.
7798
+ */
7799
+ application_result: Schema.optional(Schema.Literals([
7800
+ "applied",
7801
+ "replayed",
7802
+ "conflicting_replay",
7803
+ "revision_mismatch"
7804
+ ])),
7805
+ command_id: OptionalString,
7806
+ /**
7807
+ * #1961 R03. The authorized stored row names the Permission it changed; the
7808
+ * verified receipt names the chain facts that prove it. Every field is
7809
+ * optional because a pending or provider-failed verification has neither an
7810
+ * application result nor a receipt to report.
7811
+ */
7812
+ permission_id: OptionalString,
7813
+ permission_assignment_id: OptionalString,
7814
+ permission_operation: Schema.optional(Schema.Literals([
7815
+ "create",
7816
+ "change",
7817
+ "assign",
7818
+ "revoke",
7819
+ "replace"
7820
+ ])),
7821
+ revision: Schema.optional(Schema.Number),
7822
+ user_op_hash: OptionalString,
7823
+ transaction_hash: OptionalString,
7824
+ block_number: Schema.optional(Schema.Number),
7825
+ log_index: Schema.optional(Schema.Number)
7665
7826
  });
7666
7827
  const MovementScanWindowProps = Schema.Struct({
7667
7828
  ...TelemetryEnvelopeProps,
@@ -7707,6 +7868,10 @@ Schema.Struct({
7707
7868
  name: Schema.Literal("auth_failed"),
7708
7869
  props: AuthFailedProps
7709
7870
  });
7871
+ Schema.Struct({
7872
+ name: Schema.Literal("identity_refused"),
7873
+ props: IdentityRefusedProps
7874
+ });
7710
7875
  Schema.Struct({
7711
7876
  name: Schema.Literal("auth_signed_out"),
7712
7877
  props: AuthSignedOutProps
@@ -7887,6 +8052,10 @@ Schema.Struct({
7887
8052
  name: Schema.Literal("movement_scan_incident"),
7888
8053
  props: MovementScanIncidentProps
7889
8054
  });
8055
+ /** The one spelling of "whose scope did this happen in" across every surface. */
8056
+ const ScopeKindSchema = Schema.Literals(["account", "organization"]);
8057
+ const AccountKindSchema = Schema.Literals(["personalSafe", "orgTreasury"]);
8058
+ const OptionalReasonCode = Schema.optional(Schema.Literals(CAPXUL_ERROR_CODES));
7890
8059
  const ActivityReferenceProps = {
7891
8060
  reference_kind: Schema.Literals(["payment", "movement"]),
7892
8061
  reference_id: Schema.String
@@ -7917,8 +8086,8 @@ const ActivityFilterProps = {
7917
8086
  };
7918
8087
  const ActivityActionProps = {
7919
8088
  ...TelemetryEnvelopeProps,
7920
- scope_kind: Schema.Literals(["account", "organization"]),
7921
- reason_code: Schema.optional(Schema.Literals(CAPXUL_ERROR_CODES))
8089
+ scope_kind: ScopeKindSchema,
8090
+ reason_code: OptionalReasonCode
7922
8091
  };
7923
8092
  Schema.Struct({
7924
8093
  name: Schema.Literal("contact_relationship_changed"),
@@ -7930,7 +8099,7 @@ Schema.Struct({
7930
8099
  "hidden",
7931
8100
  "restored"
7932
8101
  ]),
7933
- scope_kind: Schema.Literals(["account", "organization"]),
8102
+ scope_kind: ScopeKindSchema,
7934
8103
  scope_id: Schema.String,
7935
8104
  owner_party_id: Schema.String,
7936
8105
  party_id: Schema.String,
@@ -7946,9 +8115,9 @@ Schema.Struct({
7946
8115
  });
7947
8116
  const ContactActionProps = {
7948
8117
  ...TelemetryEnvelopeProps,
7949
- scope_kind: Schema.Literals(["account", "organization"]),
8118
+ scope_kind: ScopeKindSchema,
7950
8119
  party_id: OptionalString,
7951
- reason_code: Schema.optional(Schema.Literals(CAPXUL_ERROR_CODES))
8120
+ reason_code: OptionalReasonCode
7952
8121
  };
7953
8122
  Schema.Struct({
7954
8123
  name: Schema.Literal("ui_contact_action_completed"),
@@ -7975,7 +8144,7 @@ Schema.Struct({
7975
8144
  props: Schema.Struct({
7976
8145
  ...TelemetryEnvelopeProps,
7977
8146
  ...ActivityReferenceProps,
7978
- scope_kind: Schema.Literals(["account", "organization"]),
8147
+ scope_kind: ScopeKindSchema,
7979
8148
  scope_id: Schema.String,
7980
8149
  occurred_at: Schema.Number
7981
8150
  })
@@ -8030,6 +8199,193 @@ Schema.Struct({
8030
8199
  })
8031
8200
  ])
8032
8201
  });
8202
+ /**
8203
+ * Reliability closeout (#1960 R02) — the shape every explicit UI observation
8204
+ * below shares. `scope_kind` says whose surface the person was looking at,
8205
+ * `organization_id` is the verified Organization the producer resolved (absent
8206
+ * on a personal action), and `reason_code` is a catalog error code, never copy.
8207
+ */
8208
+ const UiStepProps = {
8209
+ ...TelemetryEnvelopeProps,
8210
+ scope_kind: ScopeKindSchema,
8211
+ organization_id: OptionalOrgId,
8212
+ reason_code: OptionalReasonCode
8213
+ };
8214
+ const AccessRecoveryCompletedProps = Schema.Union([Schema.Struct({
8215
+ ...TelemetryEnvelopeProps,
8216
+ recovery: Schema.Literal("session_restoration"),
8217
+ outcome: Schema.Literals([
8218
+ "restored",
8219
+ "absent",
8220
+ "failed"
8221
+ ]),
8222
+ reason_code: OptionalReasonCode,
8223
+ organization_id: OptionalOrgId
8224
+ }), Schema.Struct({
8225
+ ...TelemetryEnvelopeProps,
8226
+ recovery: Schema.Literal("signer_readiness"),
8227
+ outcome: Schema.Literals(["ready", "unavailable"]),
8228
+ reason_code: OptionalReasonCode,
8229
+ organization_id: OptionalOrgId
8230
+ })]);
8231
+ Schema.Struct({
8232
+ name: Schema.Literal("access_recovery_completed"),
8233
+ props: AccessRecoveryCompletedProps
8234
+ });
8235
+ Schema.Struct({
8236
+ name: Schema.Literal("ui_access_step_completed"),
8237
+ props: Schema.Struct({
8238
+ ...UiStepProps,
8239
+ step: Schema.Literals([
8240
+ "dashboard_granted",
8241
+ "redirect_completed",
8242
+ "gate_error_displayed",
8243
+ "retry_selected",
8244
+ "capability_refusal_displayed",
8245
+ "signer_refusal_displayed"
8246
+ ]),
8247
+ capability: Schema.optional(Schema.Literals(["spend", "managePeople"])),
8248
+ probe: Schema.optional(Schema.Literals([
8249
+ "session",
8250
+ "membership",
8251
+ "lifecycle"
8252
+ ])),
8253
+ reason: Schema.optional(Schema.Literals([
8254
+ "loading",
8255
+ "no-permission",
8256
+ "org-unavailable"
8257
+ ]))
8258
+ })
8259
+ });
8260
+ Schema.Struct({
8261
+ name: Schema.Literal("org_invite_progressed"),
8262
+ props: Schema.Struct({
8263
+ ...TelemetryEnvelopeProps,
8264
+ organization_id: OrgIdTelemetrySchema,
8265
+ progress: Schema.Literals([
8266
+ "account_ready",
8267
+ "permission_applied",
8268
+ "membership_activated",
8269
+ "revoked"
8270
+ ]),
8271
+ /** The invited Party. The initiating actor stays on the envelope's identity. */
8272
+ member_party_id: Schema.String,
8273
+ occurred_at: Schema.Number
8274
+ })
8275
+ });
8276
+ Schema.Struct({
8277
+ name: Schema.Literal("invoice_issued"),
8278
+ props: Schema.Struct({
8279
+ ...TelemetryEnvelopeProps,
8280
+ request_id: Schema.String,
8281
+ issuer_scope_kind: ScopeKindSchema,
8282
+ issuer_scope_id: Schema.String,
8283
+ payer_party_id: OptionalString,
8284
+ occurred_at: Schema.Number
8285
+ })
8286
+ });
8287
+ Schema.Struct({
8288
+ name: Schema.Literal("invoice_request_transitioned"),
8289
+ props: Schema.Struct({
8290
+ ...TelemetryEnvelopeProps,
8291
+ request_id: Schema.String,
8292
+ transition: Schema.Literals([
8293
+ "approval_prepared",
8294
+ "declined",
8295
+ "cancelled"
8296
+ ]),
8297
+ actor_scope_kind: ScopeKindSchema,
8298
+ actor_scope_id: Schema.String,
8299
+ occurred_at: Schema.Number
8300
+ })
8301
+ });
8302
+ Schema.Struct({
8303
+ name: Schema.Literal("ui_invoice_step_completed"),
8304
+ props: Schema.Struct({
8305
+ ...UiStepProps,
8306
+ step: Schema.Literals([
8307
+ "opened",
8308
+ "previewed",
8309
+ "dismissed",
8310
+ "submitted",
8311
+ "result_displayed",
8312
+ "refusal_displayed",
8313
+ "retry_selected"
8314
+ ]),
8315
+ request_id: OptionalString
8316
+ })
8317
+ });
8318
+ Schema.Struct({
8319
+ name: Schema.Literal("ui_treasury_step_completed"),
8320
+ props: Schema.Struct({
8321
+ ...UiStepProps,
8322
+ step: Schema.Literals([
8323
+ "balance_displayed",
8324
+ "read_refusal_displayed",
8325
+ "retry_selected",
8326
+ "recovery_completed"
8327
+ ]),
8328
+ account_kind: Schema.optional(AccountKindSchema)
8329
+ })
8330
+ });
8331
+ Schema.Struct({
8332
+ name: Schema.Literal("ui_receive_step_completed"),
8333
+ props: Schema.Struct({
8334
+ ...UiStepProps,
8335
+ step: Schema.Literals([
8336
+ "instructions_displayed",
8337
+ "address_copied",
8338
+ "copy_failed",
8339
+ "refusal_displayed",
8340
+ "retry_selected"
8341
+ ]),
8342
+ account_kind: Schema.optional(AccountKindSchema)
8343
+ })
8344
+ });
8345
+ Schema.Struct({
8346
+ name: Schema.Literal("payroll_group_changed"),
8347
+ props: Schema.Struct({
8348
+ ...TelemetryEnvelopeProps,
8349
+ organization_id: OrgIdTelemetrySchema,
8350
+ change: Schema.Literals([
8351
+ "created",
8352
+ "updated",
8353
+ "deleted"
8354
+ ]),
8355
+ group_id: Schema.String,
8356
+ member_count: Schema.Number,
8357
+ occurred_at: Schema.Number
8358
+ })
8359
+ });
8360
+ Schema.Struct({
8361
+ name: Schema.Literal("payroll_run_transitioned"),
8362
+ props: Schema.Struct({
8363
+ ...TelemetryEnvelopeProps,
8364
+ organization_id: OrgIdTelemetrySchema,
8365
+ transition: Schema.Literals([
8366
+ "prepared",
8367
+ "authorized",
8368
+ "settled"
8369
+ ]),
8370
+ run_id: Schema.String,
8371
+ payment_count: Schema.optional(Schema.Number),
8372
+ occurred_at: Schema.Number
8373
+ })
8374
+ });
8375
+ Schema.Struct({
8376
+ name: Schema.Literal("ui_payroll_action_completed"),
8377
+ props: Schema.Struct({
8378
+ ...UiStepProps,
8379
+ action: Schema.Literals([
8380
+ "opened",
8381
+ "refusal_displayed",
8382
+ "recovery_completed",
8383
+ "cancelled",
8384
+ "submitted_displayed"
8385
+ ]),
8386
+ run_id: OptionalString
8387
+ })
8388
+ });
8033
8389
  function redactTelemetryProps(name, props, options = {}) {
8034
8390
  if (props === void 0) return void 0;
8035
8391
  const clone = cloneProps$1(props);
@@ -8325,6 +8681,10 @@ const BootstrapEnvelope = Schema.Struct({
8325
8681
  siteBaseUrl: Schema.String,
8326
8682
  openfortPublishableKey: Schema.String,
8327
8683
  shieldPublishableKey: Schema.String,
8684
+ /** Which Openfort third-party auth provider the signer presents; absent means better-auth. */
8685
+ openfortAuthProvider: Schema.optional(Schema.Literals(["better-auth", "oidc"])),
8686
+ /** Origin of the Ponder indexer the SDK reads directly; absent means no indexer reads. */
8687
+ indexerUrl: Schema.optional(Schema.String),
8328
8688
  engineeringTelemetry: Schema.optional(EngineeringTelemetryBootstrapPolicy)
8329
8689
  })
8330
8690
  });
@@ -8387,6 +8747,7 @@ const CAPXUL_FUNCTIONS = {
8387
8747
  list: "movement/activity:list",
8388
8748
  summary: "movement/activity:summary"
8389
8749
  },
8750
+ "movement/annotations": { overlay: "movement/annotations:overlay" },
8390
8751
  "moneyExecution/actions": {
8391
8752
  abandonPaymentExecution: "moneyExecution/actions:abandonPaymentExecution",
8392
8753
  preparePermissionExecution: "moneyExecution/actions:preparePermissionExecution",
@@ -8399,6 +8760,7 @@ const CAPXUL_FUNCTIONS = {
8399
8760
  prepareOrganizationPaymentExecution: "moneyExecution/paymentCommandActions:prepareOrganizationPaymentExecution",
8400
8761
  submitPaymentCommandExecution: "moneyExecution/paymentCommandActions:submitPaymentCommandExecution"
8401
8762
  },
8763
+ "moneyExecution/providerSendWitness": { read: "moneyExecution/providerSendWitness:read" },
8402
8764
  media: {
8403
8765
  generateUploadUrl: "media:generateUploadUrl",
8404
8766
  setOrgLogo: "media:setOrgLogo",
@@ -8458,6 +8820,11 @@ const CAPXUL_FUNCTIONS = {
8458
8820
  loadByAuthUserId: "smartAccount/queries:loadByAuthUserId",
8459
8821
  loadBySmartAccountAddress: "smartAccount/queries:loadBySmartAccountAddress"
8460
8822
  },
8823
+ e2eControls: {
8824
+ holdUserOperationReceipt: "e2eControls:holdUserOperationReceipt",
8825
+ isUserOperationReceiptHeld: "e2eControls:isUserOperationReceiptHeld",
8826
+ releaseUserOperationReceipt: "e2eControls:releaseUserOperationReceipt"
8827
+ },
8461
8828
  system: { health: "system:health" }
8462
8829
  };
8463
8830
  //#endregion
@@ -8979,7 +9346,19 @@ const StoredPermissionCommandSchema = Schema.Struct({
8979
9346
  roleKey: RoleKeySchema,
8980
9347
  allowanceKey: Schema.NullOr(AllowanceKeySchema),
8981
9348
  memberSafe: Schema.NullOr(SafeAddressSchema),
8982
- expectedAuthorityEvents: Schema.Array(Bytes32Schema)
9349
+ expectedAuthorityEvents: Schema.Array(Bytes32Schema),
9350
+ /**
9351
+ * #1961 R03. The authenticated user who created the command, and the browser
9352
+ * linkage captured with it. Delayed verification and bounded retries read
9353
+ * this snapshot instead of the current viewer, so one command keeps one
9354
+ * origin. Both are absent on commands created before this contract.
9355
+ *
9356
+ * The context stays `unknown` on purpose: `sanitizeObservationContext` owns
9357
+ * the field allowlist, and every reader passes the stored value through it
9358
+ * rather than repeating those rules here.
9359
+ */
9360
+ originatingAuthUserId: Schema.optional(Schema.String),
9361
+ originatingObservationContext: Schema.optional(Schema.Unknown)
8983
9362
  });
8984
9363
  Schema.Struct({
8985
9364
  command: StoredPermissionCommandSchema,
@@ -12029,6 +12408,7 @@ const financialOpsContract = {
12029
12408
  activitySummary: makeFunctionReference(CAPXUL_FUNCTIONS["movement/activity"].summary),
12030
12409
  activityGet: makeFunctionReference(CAPXUL_FUNCTIONS["movement/activity"].get),
12031
12410
  activityAnnotate: makeFunctionReference(CAPXUL_FUNCTIONS["movement/activity"].annotate),
12411
+ activityOverlay: makeFunctionReference(CAPXUL_FUNCTIONS["movement/annotations"].overlay),
12032
12412
  verifyPaymentDocument: makeFunctionReference(CAPXUL_FUNCTIONS["financialOps/queries"].verifyPaymentDocument),
12033
12413
  renderStoredDocument: makeFunctionReference(CAPXUL_FUNCTIONS["financialOps/queries"].renderStoredDocument)
12034
12414
  };
@@ -12045,8 +12425,357 @@ const moneyExecutionContract = {
12045
12425
  submitPaymentCommandExecution: makeFunctionReference(CAPXUL_FUNCTIONS["moneyExecution/paymentCommandActions"].submitPaymentCommandExecution)
12046
12426
  };
12047
12427
  //#endregion
12428
+ //#region ../../packages/sdk/src/ports/indexer-read.ts
12429
+ /** The indexer's own page bound. Exceeding it is a caller defect, not a refusal. */
12430
+ const INDEXER_MAX_LIMIT = 1e3;
12431
+ const LOWER_CASE_ADDRESS = /^0x[0-9a-f]{40}$/;
12432
+ /**
12433
+ * Refuse a page the indexer would refuse, before any implementation spends a
12434
+ * round trip on it. Every implementation calls this, so a consumer test written
12435
+ * against the hermetic adapter cannot pass on input production rejects. Throws
12436
+ * a `CapxulError`; callers convert it to an `IndexerReadError`.
12437
+ */
12438
+ function assertIndexerPage(input) {
12439
+ const safe = String(input.safe).toLowerCase();
12440
+ if (!LOWER_CASE_ADDRESS.test(safe)) throw Errors.invalidInput("safe", "must be a lower-case EVM address");
12441
+ 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}`);
12442
+ const offset = input.offset ?? 0;
12443
+ if (!Number.isSafeInteger(offset) || offset < 0) throw Errors.invalidInput("offset", "must be a non-negative integer");
12444
+ return safe;
12445
+ }
12446
+ var IndexerReadError = class extends Data.TaggedError("IndexerReadError") {};
12447
+ function indexerReadErrorFromCapxul(operation, error, cause = error) {
12448
+ return new IndexerReadError({
12449
+ operation,
12450
+ publicCode: error.code,
12451
+ publicError: error,
12452
+ cause,
12453
+ ...error.details === void 0 ? {} : { details: error.details }
12454
+ });
12455
+ }
12456
+ Context.Service()("@capxul/sdk/ports/IndexerReadPort");
12457
+ //#endregion
12458
+ //#region ../../packages/sdk/src/surface/activity-stitch.ts
12459
+ /** Epoch milliseconds of the block that carried the row. */
12460
+ function blockMillis(row) {
12461
+ return Number(row.timestamp) * 1e3;
12462
+ }
12463
+ /** `0x1234…abcd` — the counterparty of an unclaimed transfer is an address. */
12464
+ function shortAddressLabel(address) {
12465
+ return address.length <= 12 ? address : `${address.slice(0, 6)}…${address.slice(-4)}`;
12466
+ }
12467
+ function directionFor(safe, transfer) {
12468
+ const from = transfer.from.toLowerCase();
12469
+ const to = transfer.to.toLowerCase();
12470
+ if (from === safe && to === safe) return "self";
12471
+ return to === safe ? "in" : "out";
12472
+ }
12473
+ /**
12474
+ * The Transfer a protocol Payment moved is the same money as its `payment`
12475
+ * row. One actor sees one item for it, so the Transfer is claimed and does not
12476
+ * become a second row. Another transfer in the same transaction that this
12477
+ * payment did not move stays unclaimed and keeps its own row.
12478
+ */
12479
+ function claimedTransferIds(transfers, payments) {
12480
+ const claimed = /* @__PURE__ */ new Set();
12481
+ for (const payment of payments) {
12482
+ const match = transfers.find((transfer) => !claimed.has(transfer.id) && transfer.txHash === payment.txHash && transfer.token.toLowerCase() === payment.token.toLowerCase() && transfer.amount === payment.amount && transfer.to.toLowerCase() === payment.recipient.toLowerCase());
12483
+ if (match !== void 0) claimed.add(match.id);
12484
+ }
12485
+ return claimed;
12486
+ }
12487
+ function withAnnotation(item, annotation) {
12488
+ return annotation?.counterpartyLabel === void 0 ? item : {
12489
+ ...item,
12490
+ counterpartyLabel: annotation.counterpartyLabel
12491
+ };
12492
+ }
12493
+ /**
12494
+ * Confirmation comes from the chain, never from Convex.
12495
+ *
12496
+ * A Ponder row for this Payment's settlement means settled, and its receipt is
12497
+ * the indexed transaction. No Ponder row and a Convex row that still says
12498
+ * settled means the block that carried it was dropped: the row goes back to
12499
+ * `settling` and its annotation, which keys on the Payment id, is untouched.
12500
+ */
12501
+ function confirmPayment(intent, chain, hasSettlement) {
12502
+ if (chain !== void 0) return {
12503
+ ...intent,
12504
+ status: "settled",
12505
+ receipt: {
12506
+ chainId: chain.chainId,
12507
+ transactionHash: chain.txHash
12508
+ },
12509
+ updatedAt: blockMillis(chain)
12510
+ };
12511
+ if (hasSettlement && intent.status === "settled") return {
12512
+ ...intent,
12513
+ status: "settling",
12514
+ receipt: null
12515
+ };
12516
+ return intent;
12517
+ }
12518
+ /**
12519
+ * One unclaimed Transfer as an Activity row, or `null` when this SDK cannot
12520
+ * denominate its asset. The indexer serves only configured assets, so a row
12521
+ * the registry does not know means the SDK and that deployment disagree; it is
12522
+ * dropped rather than shown with a guessed currency.
12523
+ */
12524
+ function transferItem(safe, transfer, annotation) {
12525
+ const asset = configuredMoneyAssetById(transfer.assetId);
12526
+ if (asset === null) return null;
12527
+ const direction = directionFor(safe, transfer);
12528
+ const counterparty = direction === "in" ? transfer.from : transfer.to;
12529
+ const at = blockMillis(transfer);
12530
+ return withAnnotation({
12531
+ kind: "movement",
12532
+ id: transfer.id,
12533
+ classification: "movement_only_outflow",
12534
+ amount: {
12535
+ currency: asset.peg ?? asset.symbol,
12536
+ value: formatUnits(transfer.amount, asset.decimals),
12537
+ decimals: asset.decimals
12538
+ },
12539
+ direction,
12540
+ counterpartyLabel: shortAddressLabel(counterparty),
12541
+ receipt: {
12542
+ chainId: transfer.chainId,
12543
+ transactionHash: transfer.txHash
12544
+ },
12545
+ createdAt: at,
12546
+ updatedAt: at
12547
+ }, annotation);
12548
+ }
12549
+ /** Newest first. A Payment precedes a Movement at one instant, as the ledger reader orders them. */
12550
+ function compareItems(left, right) {
12551
+ if (right.createdAt !== left.createdAt) return right.createdAt - left.createdAt;
12552
+ if (left.kind !== right.kind) return left.kind === "payment" ? -1 : 1;
12553
+ if (left.id === right.id) return 0;
12554
+ return left.id < right.id ? 1 : -1;
12555
+ }
12556
+ function stitchActivity(input) {
12557
+ const safe = input.safe.toLowerCase();
12558
+ const chainBySettlement = /* @__PURE__ */ new Map();
12559
+ for (const row of input.payments) chainBySettlement.set(row.settlementId.toLowerCase(), row);
12560
+ const chainByPaymentId = /* @__PURE__ */ new Map();
12561
+ const settledPaymentIds = /* @__PURE__ */ new Set();
12562
+ for (const link of input.links) {
12563
+ settledPaymentIds.add(link.paymentId);
12564
+ const row = chainBySettlement.get(link.settlementId.toLowerCase());
12565
+ if (row !== void 0) chainByPaymentId.set(link.paymentId, row);
12566
+ }
12567
+ const annotations = /* @__PURE__ */ new Map();
12568
+ for (const annotation of input.annotations) annotations.set(`${annotation.reference.kind}:${annotation.reference.id}`, annotation);
12569
+ const items = [];
12570
+ const claiming = [];
12571
+ for (const intent of input.intents) {
12572
+ if (intent.kind !== "payment") continue;
12573
+ const chain = chainByPaymentId.get(intent.id);
12574
+ if (chain !== void 0) claiming.push(chain);
12575
+ items.push(withAnnotation(confirmPayment(intent, chain, settledPaymentIds.has(intent.id)), annotations.get(`payment:${intent.id}`)));
12576
+ }
12577
+ const claimed = claimedTransferIds(input.transfers, claiming);
12578
+ for (const transfer of input.transfers) {
12579
+ if (claimed.has(transfer.id)) continue;
12580
+ const item = transferItem(safe, transfer, annotations.get(`movement:${transfer.id}`));
12581
+ if (item !== null) items.push(item);
12582
+ }
12583
+ return items.sort(compareItems);
12584
+ }
12585
+ /** The reference ids one stitched page needs its Convex overlay for. */
12586
+ function overlayReferences(items) {
12587
+ return items.map((item) => ({
12588
+ kind: item.kind,
12589
+ id: item.id
12590
+ }));
12591
+ }
12592
+ /**
12593
+ * Money in, money out and the pending share, one bucket per currency and
12594
+ * precision. Two assets that peg to the same currency at different precision
12595
+ * stay distinct buckets: their raw units are not interchangeable.
12596
+ */
12597
+ function summarizeItems(items) {
12598
+ const buckets = /* @__PURE__ */ new Map();
12599
+ for (const item of items) {
12600
+ const key = `${item.amount.currency}:${String(item.amount.decimals)}`;
12601
+ const bucket = buckets.get(key) ?? {
12602
+ currency: item.amount.currency,
12603
+ decimals: item.amount.decimals,
12604
+ in: 0n,
12605
+ out: 0n,
12606
+ pendingIn: 0n
12607
+ };
12608
+ const raw = rawUnits(item.amount.value, item.amount.decimals);
12609
+ if (item.direction === "in") {
12610
+ bucket.in += raw;
12611
+ if (item.kind === "payment" && item.status !== "settled") bucket.pendingIn += raw;
12612
+ } else if (item.direction === "out") bucket.out += raw;
12613
+ buckets.set(key, bucket);
12614
+ }
12615
+ return [...buckets.values()].map((bucket) => ({
12616
+ currency: bucket.currency,
12617
+ decimals: bucket.decimals,
12618
+ in: bucket.in.toString(),
12619
+ out: bucket.out.toString(),
12620
+ net: (bucket.in - bucket.out).toString(),
12621
+ pendingIn: bucket.pendingIn.toString()
12622
+ }));
12623
+ }
12624
+ /** The inverse of `formatUnits`, kept exact: `"1.25"` at 6 decimals is `1250000n`. */
12625
+ function rawUnits(value, decimals) {
12626
+ const negative = value.startsWith("-");
12627
+ const [whole = "0", fraction = ""] = (negative ? value.slice(1) : value).split(".");
12628
+ const raw = BigInt(`${whole}${fraction.padEnd(decimals, "0").slice(0, decimals)}`);
12629
+ return negative ? -raw : raw;
12630
+ }
12631
+ //#endregion
12632
+ //#region ../../packages/sdk/src/surface/activity-read.ts
12633
+ /** How many Convex intent rows one page reads for its labels and pending rows. */
12634
+ const INTENT_WINDOW = 100;
12635
+ /** The window a summary aggregates over. The indexer's own page bound. */
12636
+ const SUMMARY_WINDOW = INDEXER_MAX_LIMIT;
12637
+ function parseCursor(value) {
12638
+ if (value === void 0) return 0;
12639
+ try {
12640
+ const parsed = JSON.parse(value);
12641
+ return Number.isSafeInteger(parsed.offset) && parsed.offset >= 0 ? parsed.offset : 0;
12642
+ } catch {
12643
+ return 0;
12644
+ }
12645
+ }
12646
+ function inRange(item, range) {
12647
+ if (range?.from !== void 0 && item.createdAt < range.from) return false;
12648
+ return range?.to === void 0 || item.createdAt <= range.to;
12649
+ }
12650
+ /** The caller's filter applied to the merged page. The join owns the order. */
12651
+ function matchesFilter(item, filter) {
12652
+ if (filter === void 0) return true;
12653
+ if (filter.kind !== void 0 && item.kind !== filter.kind) return false;
12654
+ if (filter.direction !== void 0 && item.direction !== filter.direction) return false;
12655
+ if (filter.status === void 0) return true;
12656
+ return item.kind === "payment" && filter.status.includes(item.status);
12657
+ }
12658
+ function overlay(deps, actor, controls, input) {
12659
+ return deps.convexCall.query(deps.functions.activityOverlay, copyInvocationObservation(controls, { input: {
12660
+ ...actor === void 0 ? {} : { actor },
12661
+ ...input.references === void 0 ? {} : { references: input.references },
12662
+ ...input.settlementIds === void 0 ? {} : { settlementIds: input.settlementIds }
12663
+ } }));
12664
+ }
12665
+ function convexPage(deps, actor, controls, input) {
12666
+ return deps.convexCall.query(deps.functions.activityList, copyInvocationObservation(controls, { input: {
12667
+ ...actor === void 0 ? {} : { actor },
12668
+ ...input.cursor === void 0 ? {} : { cursor: input.cursor },
12669
+ ...input.limit === void 0 ? {} : { limit: input.limit },
12670
+ ...input.range === void 0 ? {} : { range: input.range }
12671
+ } }));
12672
+ }
12673
+ /** Both indexed tables for one Safe, or `null` when the indexer cannot answer. */
12674
+ function chainRows(deps, safe, limit) {
12675
+ const page = {
12676
+ safe,
12677
+ limit: Math.min(limit, INDEXER_MAX_LIMIT)
12678
+ };
12679
+ return Effect.all([deps.indexer.read("transfer", page), deps.indexer.read("payment", page)], { concurrency: 2 }).pipe(Effect.map(([transfers, payments]) => ({
12680
+ transfers,
12681
+ payments
12682
+ })), Effect.catch(() => Effect.succeed(null)));
12683
+ }
12684
+ /** The stitched, filtered rows of one window, newest first. */
12685
+ function stitchedWindow(deps, actor, controls, window, params) {
12686
+ return Effect.gen(function* () {
12687
+ const scope = yield* overlay(deps, actor, controls, {});
12688
+ const convex = yield* convexPage(deps, actor, controls, {
12689
+ limit: INTENT_WINDOW,
12690
+ ...params.range === void 0 ? {} : { range: params.range }
12691
+ });
12692
+ const rows = scope.safe === null ? null : yield* chainRows(deps, scope.safe, window);
12693
+ if (rows === null || scope.safe === null) return {
12694
+ items: convex.items.filter((item) => matchesFilter(item, params.filter)),
12695
+ stitched: false
12696
+ };
12697
+ const decoration = yield* overlay(deps, actor, controls, {
12698
+ references: [...overlayReferences(convex.items), ...rows.transfers.map((row) => ({
12699
+ kind: "movement",
12700
+ id: row.id
12701
+ }))],
12702
+ settlementIds: rows.payments.map((row) => row.settlementId)
12703
+ });
12704
+ return {
12705
+ items: stitchActivity({
12706
+ safe: scope.safe,
12707
+ transfers: rows.transfers,
12708
+ payments: rows.payments,
12709
+ intents: convex.items,
12710
+ links: decoration.links,
12711
+ annotations: decoration.annotations
12712
+ }).filter((item) => inRange(item, params.range) && matchesFilter(item, params.filter)),
12713
+ stitched: true
12714
+ };
12715
+ });
12716
+ }
12717
+ function readActivityPage(deps, actor, controls, params) {
12718
+ const limit = params?.limit ?? 25;
12719
+ const offset = parseCursor(params?.cursor);
12720
+ return stitchedWindow(deps, actor, controls, offset + limit + 1, {
12721
+ ...params?.range === void 0 ? {} : { range: params.range },
12722
+ ...params?.filter === void 0 ? {} : { filter: params.filter }
12723
+ }).pipe(Effect.map(({ items, stitched }) => {
12724
+ const observedAt = Date.now();
12725
+ if (!stitched) return {
12726
+ items: items.slice(0, limit),
12727
+ cursor: null,
12728
+ checkpoint: observedAt,
12729
+ observedAt
12730
+ };
12731
+ return {
12732
+ items: items.slice(offset, offset + limit),
12733
+ cursor: items.length > offset + limit ? JSON.stringify({ offset: offset + limit }) : null,
12734
+ checkpoint: observedAt,
12735
+ observedAt
12736
+ };
12737
+ }));
12738
+ }
12739
+ function readActivitySummary(deps, actor, controls, params) {
12740
+ return stitchedWindow(deps, actor, controls, SUMMARY_WINDOW, params?.window === void 0 ? {} : { range: params.window }).pipe(Effect.map(({ items }) => ({
12741
+ window: {
12742
+ from: params?.window?.from ?? null,
12743
+ to: params?.window?.to ?? null
12744
+ },
12745
+ totals: summarizeItems(items),
12746
+ count: items.length,
12747
+ observedAt: Date.now()
12748
+ })));
12749
+ }
12750
+ /**
12751
+ * Tell the caller when the indexed page for this Safe changed.
12752
+ *
12753
+ * The transport is the indexer's one SSE connection, wired in the Ponder
12754
+ * adapter under `activity.subscribe`. A reactive snapshot is not a product
12755
+ * event, so nothing here emits telemetry: the adapter reports a teardown, and
12756
+ * the caller re-reads.
12757
+ */
12758
+ function subscribeActivity(deps, actor, controls, onChange) {
12759
+ return Effect.gen(function* () {
12760
+ const scope = yield* overlay(deps, actor, controls, {});
12761
+ if (scope.safe === null) return () => {};
12762
+ const page = {
12763
+ safe: scope.safe,
12764
+ limit: INTENT_WINDOW
12765
+ };
12766
+ let delivered = false;
12767
+ const notify = () => {
12768
+ if (delivered) onChange();
12769
+ delivered = true;
12770
+ };
12771
+ return yield* deps.indexer.subscribe("transfer", page, (snapshot) => {
12772
+ if (snapshot.status === "ok") notify();
12773
+ });
12774
+ });
12775
+ }
12776
+ //#endregion
12048
12777
  //#region ../../packages/sdk/package.json
12049
- var version$1 = "4.2.0-rc.1";
12778
+ var version$1 = "4.2.0-rc.11";
12050
12779
  //#endregion
12051
12780
  //#region ../../packages/sdk/src/telemetry/exception-projection.ts
12052
12781
  /** Fixed fallback for failures that have no safe message. */
@@ -12143,6 +12872,22 @@ function observeFailedResult(result, adapter, operation, origin) {
12143
12872
  return result;
12144
12873
  }
12145
12874
  /**
12875
+ * @internal Reports a failure that can never become a method result.
12876
+ *
12877
+ * A reactive stream is torn down after its call already returned, so nothing
12878
+ * downstream observes it. `captureException` is the right kind: the caller did
12879
+ * not ask for the end, so it is not an expected product outcome.
12880
+ *
12881
+ * A caller that DID ask for the end — the session it authenticated with
12882
+ * expired, and the service closed the stream on purpose — passes
12883
+ * `kind: "operation"` instead, so the refusal lands in the expected-outcome
12884
+ * stream its code already declares.
12885
+ */
12886
+ function observeStreamFailure(adapter, operation, cause, kind = "exception") {
12887
+ if (adapter === void 0) return;
12888
+ report(adapter, kind, operation, cause, resolveAdapterSnapshot(adapter));
12889
+ }
12890
+ /**
12146
12891
  * One failure, one event. The identity machine reports a failed transition
12147
12892
  * first; when the same `CapxulError` then surfaces as a public method result
12148
12893
  * (directly or down its `cause` chain), the boundary drops that second report.
@@ -12219,6 +12964,34 @@ function ignoreDeliveryFailure(delivery) {
12219
12964
  Promise.resolve(delivery).catch(() => void 0);
12220
12965
  } catch {}
12221
12966
  }
12967
+ /**
12968
+ * Fold a caller-captured attempt identity into the host's resolved context.
12969
+ *
12970
+ * The host's `resolveContext` runs at the moment an SDK method is entered, so a
12971
+ * host that mints a correlation per resolve gives every operation of one user
12972
+ * attempt a different identity — and a user or Organization switch between the
12973
+ * click and the reply relabels an action that already started. When the caller
12974
+ * captured `correlation_id`/`journey_id` BEFORE the action started, that is the
12975
+ * attempt, and it replaces what the resolver returned for this one call. The
12976
+ * result is what the SDK span/Log, the failure envelope and the backend carrier
12977
+ * all read, so the three agree by construction rather than by convention.
12978
+ *
12979
+ * A caller that captured nothing gets the resolver's context unchanged, so
12980
+ * automatic reads keep their own invocation context.
12981
+ */
12982
+ function withCapturedAttemptContext(context, controls) {
12983
+ const correlationId = controls?.correlation_id;
12984
+ const journeyId = controls?.journey_id;
12985
+ if (correlationId === void 0 && journeyId === void 0) return context;
12986
+ const captured = sanitizeObservationContext({
12987
+ ...correlationId === void 0 ? {} : { correlationId },
12988
+ ...journeyId === void 0 ? {} : { journeyId }
12989
+ });
12990
+ return captured === void 0 ? context : {
12991
+ ...context,
12992
+ ...captured
12993
+ };
12994
+ }
12222
12995
  const MAX_EVIDENCE_TEXT = 4e3;
12223
12996
  const MAX_CAUSE_DEPTH = 8;
12224
12997
  function evidenceText(value) {
@@ -13104,6 +13877,11 @@ function paymentExecutionIntent(input) {
13104
13877
  }
13105
13878
  function makeFinancialOpsMethods(deps) {
13106
13879
  const fns = deps.functions ?? financialOpsContract;
13880
+ const indexerDeps = deps.indexerRead === void 0 ? void 0 : {
13881
+ indexer: deps.indexerRead,
13882
+ convexCall: deps.convexCall,
13883
+ functions: fns
13884
+ };
13107
13885
  const executionFns = deps.moneyExecutionFunctions ?? moneyExecutionContract;
13108
13886
  const requestKeyScope = deps.requestKeyScope ?? randomPaymentRequestKey();
13109
13887
  const inFlightPersonalPayments = /* @__PURE__ */ new Map();
@@ -13334,22 +14112,26 @@ function makeFinancialOpsMethods(deps) {
13334
14112
  }
13335
14113
  },
13336
14114
  activity: {
14115
+ subscribe: (onChange, params, options) => {
14116
+ const controls = deps.invocationControls?.(options) ?? options;
14117
+ return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(params?.actor).actor)).pipe(Effect.flatMap((actor) => indexerDeps === void 0 ? Effect.succeed(() => {}) : Effect.suspend(() => subscribeActivity(indexerDeps, actor, controls, onChange)).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.subscribe, deps.runPromise);
14118
+ },
13337
14119
  list: (params, options) => {
13338
14120
  const controls = deps.invocationControls?.(options) ?? options;
13339
- return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(params?.actor).actor)).pipe(Effect.flatMap((actor) => Effect.suspend(() => deps.convexCall.query(fns.activityList, copyInvocationObservation(controls, { input: {
14121
+ return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(params?.actor).actor)).pipe(Effect.flatMap((actor) => Effect.suspend(() => indexerDeps === void 0 ? deps.convexCall.query(fns.activityList, copyInvocationObservation(controls, { input: {
13340
14122
  ...actor === void 0 ? {} : { actor },
13341
14123
  ...params?.cursor === void 0 ? {} : { cursor: params.cursor },
13342
14124
  ...params?.limit === void 0 ? {} : { limit: params.limit },
13343
14125
  ...params?.range === void 0 ? {} : { range: params.range },
13344
14126
  ...params?.filter === void 0 ? {} : { filter: params.filter }
13345
- } }))).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.list, deps.runPromise);
14127
+ } })) : readActivityPage(indexerDeps, actor, controls, params)).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.list, deps.runPromise);
13346
14128
  },
13347
14129
  summary: (params, options) => {
13348
14130
  const controls = deps.invocationControls?.(options) ?? options;
13349
- return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(params?.actor).actor)).pipe(Effect.flatMap((actor) => Effect.suspend(() => deps.convexCall.query(fns.activitySummary, copyInvocationObservation(controls, { input: {
14131
+ return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(params?.actor).actor)).pipe(Effect.flatMap((actor) => Effect.suspend(() => indexerDeps === void 0 ? deps.convexCall.query(fns.activitySummary, copyInvocationObservation(controls, { input: {
13350
14132
  ...actor === void 0 ? {} : { actor },
13351
14133
  ...params?.window === void 0 ? {} : { window: params.window }
13352
- } }))).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.summary, deps.runPromise);
14134
+ } })) : readActivitySummary(indexerDeps, actor, controls, params)).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.summary, deps.runPromise);
13353
14135
  },
13354
14136
  get: (reference, options) => {
13355
14137
  const controls = deps.invocationControls?.(options) ?? options;
@@ -14445,7 +15227,8 @@ var BetterAuthBrowserAdapter = class {
14445
15227
  try {
14446
15228
  const res = await this.fetchImpl(this.url("/api/auth/get-session"), withSignal$1({
14447
15229
  method: "GET",
14448
- credentials: "include"
15230
+ credentials: "include",
15231
+ cache: "no-store"
14449
15232
  }, options?.signal));
14450
15233
  if (options?.signal?.aborted) return {
14451
15234
  ok: false,
@@ -14517,7 +15300,8 @@ var BetterAuthBrowserAdapter = class {
14517
15300
  try {
14518
15301
  const res = await this.fetchImpl(this.url("/api/auth/convex/token"), withSignal$1({
14519
15302
  method: "GET",
14520
- credentials: "include"
15303
+ credentials: "include",
15304
+ cache: "no-store"
14521
15305
  }, options?.signal));
14522
15306
  if (options?.signal?.aborted) return {
14523
15307
  ok: false,
@@ -15030,7 +15814,7 @@ function isTransientBootstrapFailure(error) {
15030
15814
  const status = error.details?.httpStatus;
15031
15815
  return typeof status === "number" && isTransientHttpStatus(status);
15032
15816
  }
15033
- async function safeText(res) {
15817
+ async function safeText$1(res) {
15034
15818
  try {
15035
15819
  return await res.text();
15036
15820
  } catch {
@@ -15103,6 +15887,8 @@ var HttpBootstrapAdapter = class {
15103
15887
  siteBaseUrl: normalizeRuntimeUrl("siteBaseUrl", decodedState.siteBaseUrl),
15104
15888
  openfortPublishableKey: decodedState.openfortPublishableKey,
15105
15889
  shieldPublishableKey: decodedState.shieldPublishableKey,
15890
+ ...decodedState.openfortAuthProvider === void 0 ? {} : { openfortAuthProvider: decodedState.openfortAuthProvider },
15891
+ ...decodedState.indexerUrl === void 0 ? {} : { indexerUrl: decodedState.indexerUrl },
15106
15892
  ...decodedEngineeringTelemetry !== void 0 && Result.isSuccess(decodedEngineeringTelemetry) ? { engineeringTelemetry: decodedEngineeringTelemetry.success } : {}
15107
15893
  };
15108
15894
  },
@@ -15111,7 +15897,7 @@ var HttpBootstrapAdapter = class {
15111
15897
  return bootstrapErrorFromCapxul("malformedBody", Errors.providerError("convex", "bootstrap", cause instanceof Error ? cause : new Error(String(cause))));
15112
15898
  }
15113
15899
  });
15114
- return Effect.promise(() => safeText(res)).pipe(Effect.flatMap((body) => {
15900
+ return Effect.promise(() => safeText$1(res)).pipe(Effect.flatMap((body) => {
15115
15901
  if (res.status === 401 || body.startsWith("NOT_AUTHENTICATED")) return Effect.fail(bootstrapErrorFromCapxul("notAuthenticated", Errors.notAuthenticated()));
15116
15902
  if (res.status === 400 || body.startsWith("INVALID_INPUT")) return Effect.fail(bootstrapErrorFromCapxul("invalidInput", Errors.invalidInput("publishableKey", "rejected by bootstrap")));
15117
15903
  const responseBody = body.slice(0, 300);
@@ -18294,6 +19080,12 @@ function makeConnectionId() {
18294
19080
  }
18295
19081
  //#endregion
18296
19082
  //#region ../../packages/sdk/src/adapters/convex-call/ConvexCallAdapter.ts
19083
+ /**
19084
+ * A socket that has not emitted `close` within this window is not going to.
19085
+ * ponytail: one fixed bound; `closeTimeoutMs` is the knob if a slow runtime
19086
+ * ever needs a different one.
19087
+ */
19088
+ const DEFAULT_CLOSE_TIMEOUT_MS = 5e3;
18297
19089
  /** Exact floor-first allowlist; every additional handler must migrate its validator first. */
18298
19090
  const OBSERVED_CONVEX_ACTIONS = /* @__PURE__ */ new Set([
18299
19091
  "payroll/actions:authorizeRun",
@@ -18306,6 +19098,9 @@ const OBSERVED_CONVEX_ACTIONS = /* @__PURE__ */ new Set([
18306
19098
  "org/actions:resumeBootstrapSubmission",
18307
19099
  "org/actions:confirmBootstrap",
18308
19100
  "org/actions:readTreasury",
19101
+ "moneyExecution/actions:preparePermissionExecution",
19102
+ "moneyExecution/actions:submitPermissionExecution",
19103
+ "permission/actions:verify",
18309
19104
  "moneyExecution/actions:preparePaymentExecution",
18310
19105
  "moneyExecution/actions:abandonPaymentExecution",
18311
19106
  "moneyExecution/actions:submitPaymentExecution",
@@ -18330,6 +19125,9 @@ const OBSERVED_CONVEX_QUERIES = /* @__PURE__ */ new Set([
18330
19125
  "payroll/queries:groups",
18331
19126
  "payroll/queries:terms",
18332
19127
  "financialOps/queries:resolveRecipient",
19128
+ "org/queries:me",
19129
+ "permission/queries:read",
19130
+ "permission/queries:authorize",
18333
19131
  "identity/queries:loadByAuthUserId",
18334
19132
  "smartAccount/queries:loadByAuthUserId",
18335
19133
  "org/lifecycle:load"
@@ -18348,6 +19146,7 @@ const OBSERVED_CONVEX_MUTATIONS = /* @__PURE__ */ new Set([
18348
19146
  "financialOps/requestsInbox:decline",
18349
19147
  "payroll/mutations:saveGroup",
18350
19148
  "payroll/mutations:removeGroup",
19149
+ "permission/mutations:command",
18351
19150
  "identity/mutations:create",
18352
19151
  "identity/mutations:update",
18353
19152
  "identity/mutations:completeOnboarding",
@@ -18362,7 +19161,9 @@ var ConvexCallAdapter = class {
18362
19161
  #applicationId;
18363
19162
  #observation;
18364
19163
  #connectionMonitor;
19164
+ #closeTimeoutMs;
18365
19165
  constructor(deps) {
19166
+ this.#closeTimeoutMs = deps.closeTimeoutMs ?? DEFAULT_CLOSE_TIMEOUT_MS;
18366
19167
  this.#connectionMonitor = new ConvexConnectionMonitor();
18367
19168
  const webSocketConstructor = this.#connectionMonitor.observedWebSocketConstructor();
18368
19169
  this.#client = deps.client ?? new ConvexClient(deps.convexUrl, webSocketConstructor === void 0 ? {} : { webSocketConstructor });
@@ -18461,11 +19262,33 @@ var ConvexCallAdapter = class {
18461
19262
  callback({ status: "loading" });
18462
19263
  })));
18463
19264
  }
19265
+ /**
19266
+ * Teardown always completes. `ConvexClient.close()` resolves only when the
19267
+ * socket emits `close`, and `WebSocketManager.close()` in the `connecting`
19268
+ * state arms `ws.onopen = () => ws.close()` without ever closing the socket
19269
+ * itself — so a handshake that stalls right after a server-initiated close
19270
+ * (code 1012, which is what a Convex deploy sends) leaves that promise
19271
+ * pending for as long as the page lives. Convex marks the client closed and
19272
+ * terminates the socket manager synchronously before returning it, so
19273
+ * abandoning it drops nothing. A rejection is dropped for the same reason —
19274
+ * the layer release turns it into a defect that fails the whole scope close,
19275
+ * and teardown has no caller who can act on it.
19276
+ */
18464
19277
  async close() {
18465
19278
  this.#connectionMonitor.close();
18466
- await this.#client.close();
19279
+ await settledWithin(this.#client.close(), this.#closeTimeoutMs);
18467
19280
  }
18468
19281
  };
19282
+ function settledWithin(promise, timeoutMs) {
19283
+ return new Promise((resolve) => {
19284
+ const timer = setTimeout(resolve, timeoutMs);
19285
+ const finish = () => {
19286
+ clearTimeout(timer);
19287
+ resolve();
19288
+ };
19289
+ promise.then(finish, finish);
19290
+ });
19291
+ }
18469
19292
  function ConvexCallLayer(deps) {
18470
19293
  return Layer.effect(ConvexCallPortTag, Effect.acquireRelease(Effect.sync(() => new ConvexCallAdapter(deps)), (adapter) => Effect.promise(() => adapter.close()).pipe(Effect.orDie)));
18471
19294
  }
@@ -19418,6 +20241,32 @@ var ConsoleDiagnosticAdapter = class {
19418
20241
  }
19419
20242
  };
19420
20243
  //#endregion
20244
+ //#region ../../packages/sdk/src/adapters/diagnostic/EffectLogDiagnosticAdapter.ts
20245
+ /**
20246
+ * Diagnostic breadcrumbs as SDK log records. The signer's readiness cycle
20247
+ * traces (`openfort.*`) used to reach only the browser console; through this
20248
+ * adapter they run in the client's own Effect context, so the engineering
20249
+ * telemetry layer ships them to PostHog Logs beside `identity.transition`.
20250
+ * Logging never throws back into the cycle that emitted it.
20251
+ */
20252
+ var EffectLogDiagnosticAdapter = class {
20253
+ #runPromise;
20254
+ #console;
20255
+ constructor(context, consoleFallback) {
20256
+ this.#runPromise = Effect.runPromiseWith(context);
20257
+ this.#console = consoleFallback;
20258
+ }
20259
+ trace(scope, detail) {
20260
+ this.#console?.trace(scope, detail);
20261
+ try {
20262
+ this.#runPromise(Effect.logInfo(scope).pipe(Effect.annotateLogs({
20263
+ ...detail,
20264
+ diagnostic_scope: scope
20265
+ }), Effect.catchCause(() => Effect.void))).catch(() => void 0);
20266
+ } catch {}
20267
+ }
20268
+ };
20269
+ //#endregion
19421
20270
  //#region ../../node_modules/.pnpm/axios@1.17.0/node_modules/axios/lib/helpers/bind.js
19422
20271
  /**
19423
20272
  * Create a bound version of a function with a specified `this` context
@@ -42496,15 +43345,35 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
42496
43345
  });
42497
43346
  throw failOpenfort("configure", "no-secure-context", /* @__PURE__ */ new Error("Web Crypto unavailable: browser is not a secure context"));
42498
43347
  }
42499
- function betterAuthSessionUrl() {
42500
- return `${authBaseUrl}/get-session`;
43348
+ const authProvider = bootstrap.openfortAuthProvider ?? "better-auth";
43349
+ function accessTokenUrl() {
43350
+ return authProvider === "oidc" ? `${authBaseUrl}/convex/token` : `${authBaseUrl}/get-session`;
42501
43351
  }
42502
43352
  function encryptionSessionUrl() {
42503
43353
  return `${authBaseUrl}/encryption-session`;
42504
43354
  }
43355
+ let cycleStartedAt = null;
43356
+ let stepStartedAt = 0;
43357
+ let tokenCalls = 0;
43358
+ const sinceCycleStart = () => cycleStartedAt === null ? void 0 : Date.now() - cycleStartedAt;
43359
+ const step = (name, extra = {}) => {
43360
+ const now = Date.now();
43361
+ diagnostic?.trace("openfort.step", {
43362
+ step: name,
43363
+ duration_ms: now - stepStartedAt,
43364
+ elapsed_ms: sinceCycleStart(),
43365
+ ...extra
43366
+ });
43367
+ stepStartedAt = now;
43368
+ };
42505
43369
  async function fetchBetterAuthAccessToken() {
43370
+ tokenCalls += 1;
43371
+ const tokenStartedAt = Date.now();
42506
43372
  try {
42507
- const response = await fetch(betterAuthSessionUrl(), { credentials: "include" });
43373
+ const response = await fetch(accessTokenUrl(), {
43374
+ credentials: "include",
43375
+ cache: "no-store"
43376
+ });
42508
43377
  if (!response.ok) {
42509
43378
  diagnostic?.trace("openfort.token", {
42510
43379
  ok: false,
@@ -42514,7 +43383,8 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
42514
43383
  });
42515
43384
  return null;
42516
43385
  }
42517
- const token = (await response.json()).session?.token?.trim();
43386
+ const body = await response.json();
43387
+ const token = (authProvider === "oidc" ? body.token : body.session?.token)?.trim();
42518
43388
  if (token === void 0 || token.length === 0) {
42519
43389
  diagnostic?.trace("openfort.token", {
42520
43390
  ok: false,
@@ -42525,7 +43395,11 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
42525
43395
  }
42526
43396
  diagnostic?.trace("openfort.token", {
42527
43397
  ok: true,
42528
- tokenPresent: true
43398
+ tokenPresent: true,
43399
+ provider: authProvider,
43400
+ call: tokenCalls,
43401
+ duration_ms: Date.now() - tokenStartedAt,
43402
+ elapsed_ms: sinceCycleStart()
42529
43403
  });
42530
43404
  return token;
42531
43405
  } catch (cause) {
@@ -42541,7 +43415,7 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
42541
43415
  baseConfiguration: { publishableKey: bootstrap.openfortPublishableKey },
42542
43416
  shieldConfiguration: { shieldPublishableKey: bootstrap.shieldPublishableKey },
42543
43417
  thirdPartyAuth: {
42544
- provider: o$13.BETTER_AUTH,
43418
+ provider: authProvider === "oidc" ? o$13.OIDC : o$13.BETTER_AUTH,
42545
43419
  getAccessToken: fetchBetterAuthAccessToken
42546
43420
  }
42547
43421
  });
@@ -42581,10 +43455,16 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
42581
43455
  let walletReadyPromise = null;
42582
43456
  function startWalletReady() {
42583
43457
  return (async () => {
43458
+ const startedAt = Date.now();
43459
+ cycleStartedAt = startedAt;
43460
+ stepStartedAt = startedAt;
43461
+ tokenCalls = 0;
42584
43462
  if (isInsecureBrowserContext()) failNoSecureContext();
42585
43463
  await openfort.waitForInitialization();
43464
+ step("initialize");
42586
43465
  const accessToken = await fetchBetterAuthAccessToken();
42587
43466
  if (accessToken === null) throw openfortProviderError("token", /* @__PURE__ */ new Error("Better Auth access token unavailable for Openfort"));
43467
+ step("token");
42588
43468
  let encryptionResponse;
42589
43469
  try {
42590
43470
  encryptionResponse = await fetch(encryptionSessionUrl(), {
@@ -42627,10 +43507,12 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
42627
43507
  ok: true,
42628
43508
  httpStatus: encryptionResponse.status
42629
43509
  });
43510
+ step("encryptionSession", { httpStatus: encryptionResponse.status });
42630
43511
  let embeddedState;
42631
43512
  try {
42632
43513
  embeddedState = await openfort.embeddedWallet.getEmbeddedState();
42633
43514
  diagnostic?.trace("openfort.embeddedState", { state: embeddedState });
43515
+ step("embeddedState", { state: embeddedState });
42634
43516
  } catch (cause) {
42635
43517
  diagnostic?.trace("openfort.embeddedState", {
42636
43518
  ok: false,
@@ -42644,6 +43526,7 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
42644
43526
  try {
42645
43527
  await configureEmbeddedWallet(encryptionBody.sessionId);
42646
43528
  diagnostic?.trace("openfort.configure", { ok: true });
43529
+ step("configure");
42647
43530
  } catch (cause) {
42648
43531
  const error = openfortProviderError("configure", cause);
42649
43532
  diagnostic?.trace("openfort.configure", {
@@ -42656,6 +43539,7 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
42656
43539
  try {
42657
43540
  await openfort.embeddedWallet.get();
42658
43541
  diagnostic?.trace("openfort.get", { ok: true });
43542
+ step("get");
42659
43543
  } catch (cause) {
42660
43544
  if (!isStaleUserSignal(cause)) {
42661
43545
  diagnostic?.trace("openfort.get", {
@@ -42669,7 +43553,13 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
42669
43553
  failure_mode: "stale-openfort-cache"
42670
43554
  });
42671
43555
  await healStaleOpenfortCache(encryptionBody.sessionId);
43556
+ step("healStaleCache");
42672
43557
  }
43558
+ step("walletReady", {
43559
+ ok: true,
43560
+ total_ms: Date.now() - startedAt,
43561
+ token_calls: tokenCalls
43562
+ });
42673
43563
  })();
42674
43564
  }
42675
43565
  async function ensureOpenfortWalletReady() {
@@ -42752,9 +43642,13 @@ function createOpenfortBrowserSignerFromBootstrap(bootstrap, options = {}) {
42752
43642
  statusStore,
42753
43643
  ensureWalletReady: ensureOpenfortWalletReady,
42754
43644
  getAddress: async () => {
43645
+ const addressStartedAt = Date.now();
42755
43646
  try {
42756
43647
  const address = await signer.getAddress();
42757
- diagnostic?.trace("openfort.address", { ok: true });
43648
+ diagnostic?.trace("openfort.address", {
43649
+ ok: true,
43650
+ duration_ms: Date.now() - addressStartedAt
43651
+ });
42758
43652
  return address;
42759
43653
  } catch (cause) {
42760
43654
  diagnostic?.trace("openfort.address", {
@@ -43856,14 +44750,66 @@ function organizationWrongState(currentState, validStates) {
43856
44750
  function requestCancelled(options) {
43857
44751
  return options?.signal?.aborted === true;
43858
44752
  }
44753
+ function accountInFlight(state) {
44754
+ return state.phase === "authenticated" && (state.account.at === "deriving" || state.account.at === "claiming");
44755
+ }
44756
+ function settledAccount(deps, options) {
44757
+ return new Promise((resolve) => {
44758
+ let done = false;
44759
+ let unsubscribe = null;
44760
+ const onAbort = () => finish({
44761
+ ok: false,
44762
+ error: Errors.cancelled({ operation: CAPXUL_OPERATIONS.onboarding.completeOrganization })
44763
+ });
44764
+ const finish = (result) => {
44765
+ if (done) return;
44766
+ done = true;
44767
+ unsubscribe?.();
44768
+ options?.signal?.removeEventListener("abort", onAbort);
44769
+ resolve(result);
44770
+ };
44771
+ if (options?.signal?.aborted) {
44772
+ onAbort();
44773
+ return;
44774
+ }
44775
+ options?.signal?.addEventListener("abort", onAbort, { once: true });
44776
+ const stop = deps.subscribeIdentity((state) => {
44777
+ if (!accountInFlight(state)) finish({
44778
+ ok: true,
44779
+ value: state
44780
+ });
44781
+ });
44782
+ if (done) {
44783
+ stop();
44784
+ return;
44785
+ }
44786
+ unsubscribe = stop;
44787
+ const current = deps.snapshotIdentity();
44788
+ if (!accountInFlight(current)) finish({
44789
+ ok: true,
44790
+ value: current
44791
+ });
44792
+ });
44793
+ }
43859
44794
  async function reachClaimedAccount(deps, options) {
43860
44795
  let state = deps.snapshotIdentity();
44796
+ if (accountInFlight(state)) {
44797
+ const settled = await settledAccount(deps, options);
44798
+ if (!settled.ok) return settled;
44799
+ state = settled.value;
44800
+ }
43861
44801
  if (state.phase !== "authenticated" || state.account.at === "unknown") {
43862
44802
  const ensured = await deps.sendIdentity({ _tag: "EnsureAccount" }, options);
43863
44803
  if (!ensured.ok) return ensured;
43864
44804
  state = ensured.value;
43865
44805
  }
43866
44806
  if (state.phase !== "authenticated") return organizationWrongState(state.phase, ["authenticated:claimed"]);
44807
+ if (accountInFlight(state)) {
44808
+ const settled = await settledAccount(deps, options);
44809
+ if (!settled.ok) return settled;
44810
+ state = settled.value;
44811
+ if (state.phase !== "authenticated") return organizationWrongState(state.phase, ["authenticated:claimed"]);
44812
+ }
43867
44813
  if (state.account.at !== "claimed") {
43868
44814
  const event = state.account.at === "failed" ? { _tag: "RetryAccount" } : state.account.at === "counterfactual" ? { _tag: "ClaimAccount" } : { _tag: "EnsureAccount" };
43869
44815
  const claimed = await deps.sendIdentity(event, options);
@@ -44204,8 +45150,33 @@ function toOrgMe(wire) {
44204
45150
  observedAt: wire.observedAt
44205
45151
  };
44206
45152
  }
45153
+ /**
45154
+ * The read runs through the canonical observed runner, so `org.me` owns one
45155
+ * `capxul_sdk_operation` record and one span like every other public method.
45156
+ *
45157
+ * The call-start invocation snapshot travels on the query arguments: the
45158
+ * Convex adapter reads it there and sends the browser linkage with the call,
45159
+ * so an account switch mid-flight cannot re-resolve the actor. A successful
45160
+ * read proves participation in this Organization, so the owning span carries
45161
+ * the verified Organization and the two authority booleans the backend
45162
+ * actually enforces. Labels and Budget caps stay off the record.
45163
+ */
44207
45164
  function makeOrgMeMethod(deps, orgId) {
44208
- return (options) => runIfActive(options?.signal, CAPXUL_OPERATIONS.org.me, () => Effect.map(deps.convexCall.query(orgReadContract.me, { orgId }), toOrgMe));
45165
+ return (options) => {
45166
+ const controls = deps.invocationControls?.(options) ?? options;
45167
+ return runCapxulEffect(Effect.gen(function* () {
45168
+ const wire = yield* deps.convexCall.query(orgReadContract.me, copyInvocationObservation(controls, { orgId })).pipe(Effect.mapError((failure) => failure.publicError));
45169
+ yield* Effect.annotateCurrentSpan({
45170
+ verified_organization_id: orgId,
45171
+ scope_kind: "organization",
45172
+ can_manage_people: wire.capabilities.canManagePeople,
45173
+ can_spend: wire.capabilities.canSpend,
45174
+ budget_count: wire.budgets.length,
45175
+ observed_at: wire.observedAt
45176
+ }).pipe(Effect.catchCause(() => Effect.void));
45177
+ return toOrgMe(wire);
45178
+ }), controls, CAPXUL_OPERATIONS.org.me, deps.runPromise);
45179
+ };
44209
45180
  }
44210
45181
  //#endregion
44211
45182
  //#region ../../packages/sdk/src/contract/permission.ts
@@ -44215,7 +45186,34 @@ const permissionContract = { read: makeFunctionReference(CAPXUL_FUNCTIONS["permi
44215
45186
  function isAborted(signal) {
44216
45187
  return signal?.aborted === true;
44217
45188
  }
44218
- async function execute(deps, orgId, command, signal) {
45189
+ /**
45190
+ * One owning operation per public Permission method. The compound command
45191
+ * (prepare, equality checks, sign, submit) keeps owning its own cancellation;
45192
+ * the observed runner around it is what gives the attempt its
45193
+ * `capxul_sdk_operation` record, its span and its failure capture, so a signer
45194
+ * refusal or an unexpected provider error joins to the operation like every
45195
+ * other public surface.
45196
+ */
45197
+ function observed(deps, operation, options, run) {
45198
+ const controls = deps.invocationControls?.(options) ?? options;
45199
+ let verifiedOrganizationId;
45200
+ return runCapxulEffect(Effect.gen(function* () {
45201
+ if (controls?.signal?.aborted === true) return yield* Effect.fail(Errors.cancelled({ operation }));
45202
+ const parent = yield* Effect.currentSpan.pipe(Effect.orDie);
45203
+ const captured = carryInvocationParentSpan(controls, parent);
45204
+ const result = yield* Effect.tryPromise({
45205
+ try: () => run(controls?.signal, captured, (orgId) => {
45206
+ verifiedOrganizationId = orgId;
45207
+ }),
45208
+ catch: (cause) => cause instanceof CapxulError ? cause : Errors.unknown(cause)
45209
+ });
45210
+ return yield* result.ok ? Effect.succeed(result.value) : Effect.fail(result.error);
45211
+ }).pipe(Effect.ensuring(Effect.suspend(() => verifiedOrganizationId === void 0 ? Effect.void : Effect.annotateCurrentSpan({
45212
+ verified_organization_id: verifiedOrganizationId,
45213
+ scope_kind: "organization"
45214
+ })).pipe(Effect.catchCause(() => Effect.void)))), observationOnlyControls(controls), operation, deps.runPromise);
45215
+ }
45216
+ async function execute(deps, orgId, command, signal, controls, verifiedOrganization) {
44219
45217
  if (isAborted(signal)) return {
44220
45218
  ok: false,
44221
45219
  error: Errors.cancelled({ operation: "permissions" })
@@ -44239,17 +45237,18 @@ async function execute(deps, orgId, command, signal) {
44239
45237
  };
44240
45238
  }
44241
45239
  const executionFns = deps.executionFunctions ?? moneyExecutionContract;
44242
- const prepared = await runIfActive(signal, CAPXUL_OPERATIONS.permissions.prepareExecution, () => deps.convexCall.action(executionFns.preparePermissionExecution, { input: {
45240
+ const prepared = await runIfActive(signal, CAPXUL_OPERATIONS.permissions.prepareExecution, () => withInvocationParentSpan(Effect.suspend(() => deps.convexCall.action(executionFns.preparePermissionExecution, copyInvocationObservation(controls, { input: {
44243
45241
  orgId,
44244
45242
  signerAddress,
44245
45243
  command
44246
- } }));
45244
+ } }))), controls), deps.runPromise);
44247
45245
  if (!prepared.ok) return prepared;
44248
45246
  const expectedSafe = deriveCapxulSafeAddress({ email: session.email });
44249
45247
  if (prepared.value.chainId !== deps.chainId || prepared.value.signerAddress.toLowerCase() !== signerAddress.toLowerCase() || prepared.value.userOpSenderSafe.toLowerCase() !== expectedSafe.toLowerCase() || prepared.value.operation !== command.operation || prepared.value.orgId !== orgId || canonicalJson(prepared.value.command) !== canonicalJson(command)) return {
44250
45248
  ok: false,
44251
45249
  error: Errors.invalidInput("permission", "prepared authority mismatch")
44252
45250
  };
45251
+ verifiedOrganization(prepared.value.orgId);
44253
45252
  if (isAborted(signal)) return {
44254
45253
  ok: false,
44255
45254
  error: Errors.cancelled({ operation: "permissions" })
@@ -44267,43 +45266,46 @@ async function execute(deps, orgId, command, signal) {
44267
45266
  ok: false,
44268
45267
  error: Errors.cancelled({ operation: "permissions" })
44269
45268
  };
44270
- return runIfActive(void 0, CAPXUL_OPERATIONS.permissions.submitExecution, () => deps.convexCall.action(executionFns.submitPermissionExecution, { input: {
45269
+ return runIfActive(void 0, CAPXUL_OPERATIONS.permissions.submitExecution, () => withInvocationParentSpan(Effect.suspend(() => deps.convexCall.action(executionFns.submitPermissionExecution, copyInvocationObservation(controls, { input: {
44271
45270
  executionId: prepared.value.executionId,
44272
45271
  signature
44273
- } }));
45272
+ } }))), controls), deps.runPromise);
44274
45273
  }
44275
45274
  function makePermissionMethods(deps, orgId) {
44276
45275
  const reads = deps.permissionFunctions ?? permissionContract;
45276
+ const ops = CAPXUL_OPERATIONS.org.permissions;
45277
+ const read = (signal, operation, controls) => runIfActive(signal, operation, () => withInvocationParentSpan(Effect.suspend(() => deps.convexCall.query(reads.read, copyInvocationObservation(controls, { orgId }))), controls), deps.runPromise);
45278
+ const command = (operation, build, options) => observed(deps, operation, options, (signal, captured, verifiedOrganization) => execute(deps, orgId, build(), signal, captured, verifiedOrganization));
44277
45279
  return {
44278
- create: (input, options) => execute(deps, orgId, {
45280
+ create: (input, options) => command(ops.create, () => ({
44279
45281
  operation: "create",
44280
45282
  ...input
44281
- }, options?.signal),
44282
- change: (input, options) => execute(deps, orgId, {
45283
+ }), options),
45284
+ change: (input, options) => command(ops.change, () => ({
44283
45285
  operation: "change",
44284
45286
  ...input
44285
- }, options?.signal),
44286
- assign: (input, options) => execute(deps, orgId, {
45287
+ }), options),
45288
+ assign: (input, options) => command(ops.assign, () => ({
44287
45289
  operation: "assign",
44288
45290
  ...input
44289
- }, options?.signal),
44290
- revoke: (input, options) => execute(deps, orgId, {
45291
+ }), options),
45292
+ revoke: (input, options) => command(ops.revoke, () => ({
44291
45293
  operation: "revoke",
44292
45294
  ...input
44293
- }, options?.signal),
44294
- replace: (input, options) => execute(deps, orgId, {
45295
+ }), options),
45296
+ replace: (input, options) => command(ops.replace, () => ({
44295
45297
  operation: "replace",
44296
45298
  ...input
44297
- }, options?.signal),
44298
- list: (options) => runIfActive(options?.signal, CAPXUL_OPERATIONS.permissions.list, () => deps.convexCall.query(reads.read, { orgId })),
44299
- get: async (permissionId, options) => {
44300
- const listed = await runIfActive(options?.signal, CAPXUL_OPERATIONS.permissions.get, () => deps.convexCall.query(reads.read, { orgId }));
45299
+ }), options),
45300
+ list: (options) => observed(deps, ops.list, options, (signal, captured) => read(signal, CAPXUL_OPERATIONS.permissions.list, captured)),
45301
+ get: (permissionId, options) => observed(deps, ops.get, options, async (signal, captured) => {
45302
+ const listed = await read(signal, CAPXUL_OPERATIONS.permissions.get, captured);
44301
45303
  if (!listed.ok) return listed;
44302
45304
  return {
44303
45305
  ok: true,
44304
45306
  value: listed.value.permissions.find((row) => row.permissionId === permissionId) ?? null
44305
45307
  };
44306
- }
45308
+ })
44307
45309
  };
44308
45310
  }
44309
45311
  //#endregion
@@ -44865,7 +45867,7 @@ function hermeticOrgView(input) {
44865
45867
  name: input.name,
44866
45868
  handle: input.handle,
44867
45869
  safeAddress: toAddress(placeholder),
44868
- role: "Owner",
45870
+ role: ADMIN_ROLE_LABEL,
44869
45871
  treasury: zeroTreasury(orgId),
44870
45872
  bio: null,
44871
45873
  size: null,
@@ -45111,6 +46113,13 @@ function isRetryableOrganizationSetupFailure(error) {
45111
46113
  * constructed for each `org(orgId)` call. It has no shared mutable active-Org
45112
46114
  * state. The closed-over `orgId` is its only entity reference.
45113
46115
  */
46116
+ /** The observed-runner pair a method bundle needs to own its operation records. */
46117
+ function observedRunnerDeps(deps) {
46118
+ return {
46119
+ ...deps.runPromise === void 0 ? {} : { runPromise: deps.runPromise },
46120
+ ...deps.invocationControls === void 0 ? {} : { invocationControls: deps.invocationControls }
46121
+ };
46122
+ }
45114
46123
  function makeOrgMethods(deps) {
45115
46124
  const orgDeps = {
45116
46125
  ...deps.orgPort === void 0 ? {} : { orgPort: deps.orgPort },
@@ -45161,7 +46170,10 @@ function makeOrgMethods(deps) {
45161
46170
  },
45162
46171
  ...convexCall === void 0 ? {} : { convexCall }
45163
46172
  }),
45164
- me: convexCall === void 0 ? orgMeUnavailable : makeOrgMeMethod({ convexCall }, String(orgId)),
46173
+ me: convexCall === void 0 ? orgMeUnavailable : makeOrgMeMethod({
46174
+ convexCall,
46175
+ ...observedRunnerDeps(deps)
46176
+ }, String(orgId)),
45165
46177
  async getLifecycle(options) {
45166
46178
  if (options?.signal?.aborted) return {
45167
46179
  ok: false,
@@ -45278,6 +46290,7 @@ function makeOrgMethods(deps) {
45278
46290
  },
45279
46291
  permissions: convexCall === void 0 ? makeNotImplementedPermissionMethods() : makePermissionMethods({
45280
46292
  convexCall,
46293
+ ...observedRunnerDeps(deps),
45281
46294
  ...deps.actor === void 0 ? {} : { actor: deps.actor },
45282
46295
  ...deps.chainId === void 0 ? {} : { chainId: Number(deps.chainId) },
45283
46296
  ...deps.signer === void 0 ? {} : { signer: deps.signer }
@@ -45397,14 +46410,65 @@ function compareDecimal(first, second) {
45397
46410
  }
45398
46411
  //#endregion
45399
46412
  //#region ../../packages/sdk/src/surface/holdings.ts
46413
+ /** SPEC Freshness: past this block age the read is labelled stale. Nothing refuses. */
46414
+ const FRESHNESS_LIMIT_SECONDS = 60;
46415
+ /**
46416
+ * The Ponder balance view for one Safe as `CurrentHoldings`.
46417
+ *
46418
+ * The view is the signed sum of every indexed transfer, so the balance IS what
46419
+ * the chain said — there is no snapshot to go stale behind it. `observedAt` is
46420
+ * the indexed block's own timestamp, which is what the last-good label reads,
46421
+ * and `stale` is that block's age. A stopped indexer therefore shows an old
46422
+ * "as of" and the stale flag; it never turns the read into a refusal.
46423
+ */
46424
+ function indexerHoldings(indexer, convexCall, overlay, actor, controls) {
46425
+ return Effect.gen(function* () {
46426
+ const scope = yield* convexCall.query(overlay, copyInvocationObservation(controls, actor?.kind === "organization" ? { input: { actor: {
46427
+ kind: "organization",
46428
+ orgId: actor.organizationId
46429
+ } } } : { input: {} }));
46430
+ if (scope.safe === null) return null;
46431
+ const balances = yield* indexer.read("balance", {
46432
+ safe: toAddress(scope.safe),
46433
+ limit: INDEXER_MAX_LIMIT
46434
+ });
46435
+ const status = yield* indexer.status.pipe(Effect.catch(() => Effect.succeed([])));
46436
+ const rows = [];
46437
+ for (const balance of balances) {
46438
+ const asset = configuredMoneyAssetById(balance.assetId);
46439
+ if (asset === null || balance.amount < 0n) continue;
46440
+ rows.push({
46441
+ assetKind: "erc20",
46442
+ tokenAddress: toAddress(asset.tokenAddress),
46443
+ symbol: asset.symbol,
46444
+ decimals: asset.decimals,
46445
+ rawBalance: toWeiAmount(balance.amount.toString())
46446
+ });
46447
+ }
46448
+ const chainId = balances[0]?.chainId ?? status[0]?.chainId;
46449
+ if (chainId === void 0) return null;
46450
+ const head = status.find((entry) => entry.chainId === chainId);
46451
+ const nowSeconds = Math.floor(Date.now() / 1e3);
46452
+ const headSeconds = head === void 0 ? void 0 : Number(head.blockTimestamp);
46453
+ return {
46454
+ actor: scope.actor,
46455
+ chainId: toChainId(chainId),
46456
+ observedAt: headSeconds === void 0 ? Date.now() : headSeconds * 1e3,
46457
+ stale: headSeconds === void 0 || nowSeconds - headSeconds > FRESHNESS_LIMIT_SECONDS,
46458
+ rows
46459
+ };
46460
+ }).pipe(Effect.catch(() => Effect.succeed(null)));
46461
+ }
45400
46462
  function makeHoldingsMethods(deps) {
45401
46463
  const functions = deps.functions ?? holdingsContract;
46464
+ const overlay = (deps.financialOpsFunctions ?? financialOpsContract).activityOverlay;
45402
46465
  const read = (operation, actor, options, pick) => {
45403
46466
  const controls = deps.invocationControls?.(options) ?? options;
45404
- return runCapxulEffect(retryIdempotentRead(deps.convexCall.action(functions.current, copyInvocationObservation(controls, actor?.kind === "organization" ? { actor: {
46467
+ const convexSnapshot = retryIdempotentRead(deps.convexCall.action(functions.current, copyInvocationObservation(controls, actor?.kind === "organization" ? { actor: {
45405
46468
  kind: "organization",
45406
46469
  orgId: actor.organizationId
45407
- } } : {})), operation, deps.telemetry, controls).pipe(Effect.map(pick), Effect.mapError((error) => error.publicError)), controls, operation, deps.runPromise);
46470
+ } } : {})), operation, deps.telemetry, controls).pipe(Effect.mapError((error) => error.publicError));
46471
+ return runCapxulEffect((deps.indexerRead === void 0 ? convexSnapshot : indexerHoldings(deps.indexerRead, deps.convexCall, overlay, actor, controls).pipe(Effect.flatMap((current) => current === null ? convexSnapshot : Effect.succeed(current)))).pipe(Effect.map(pick)), controls, operation, deps.runPromise);
45408
46472
  };
45409
46473
  return {
45410
46474
  current: (input, options) => read(CAPXUL_OPERATIONS.holdings.current, input?.actor, options, (snapshot) => snapshot),
@@ -45538,8 +46602,8 @@ const APPLIED_ACTION_BY_EVENT = {
45538
46602
  RestoreSession: "none",
45539
46603
  ResumeOtpEntry: "none",
45540
46604
  Reset: "none",
45541
- SessionRestored: "none",
45542
- SessionAbsent: "none",
46605
+ SessionRestored: "restoration",
46606
+ SessionAbsent: "restoration",
45543
46607
  EnsureAccount: "none",
45544
46608
  ClaimAccount: "none",
45545
46609
  RetryAccount: "none",
@@ -45551,8 +46615,8 @@ const APPLIED_ACTION_BY_EVENT = {
45551
46615
  OtpFailed: "failed",
45552
46616
  Verified: "verified",
45553
46617
  VerifyFailed: "failed",
45554
- SessionRead: "none",
45555
- SessionReadFailed: "none",
46618
+ SessionRead: "restoration",
46619
+ SessionReadFailed: "restoration",
45556
46620
  SignedOut: "signed-out",
45557
46621
  SignOutFailed: "failed",
45558
46622
  AccountAt: "none",
@@ -45578,11 +46642,71 @@ const authFailure = (record, reason, mode) => ({
45578
46642
  ...linkage(record)
45579
46643
  }
45580
46644
  });
46645
+ /**
46646
+ * The label of the restore window every client boots into. A settled session
46647
+ * fact that left this label is the restoration result (#1962 R04); the same
46648
+ * facts from a signed-out or authenticated state are the routine session probe
46649
+ * that `getSession()` runs, and a probe is not a restoration attempt.
46650
+ */
46651
+ const RESTORE_WINDOW = "restoring";
46652
+ /**
46653
+ * Which result the window settled on. `SessionRestored` and `SessionAbsent` are
46654
+ * the cache door and say it themselves. `ReadSession` is also valid inside the
46655
+ * window — `getSession()` drives it on a cache miss and its `SessionRead`
46656
+ * completion settles the window — so that one event reports both halves and the
46657
+ * state it produced is what separates them.
46658
+ */
46659
+ const restorationOutcome = (event, state) => {
46660
+ switch (event) {
46661
+ case "SessionRestored": return "restored";
46662
+ case "SessionAbsent": return "absent";
46663
+ case "SessionReadFailed": return "failed";
46664
+ case "SessionRead":
46665
+ if (state.phase === "authenticated") return "restored";
46666
+ return state.phase === "signed_out" ? "absent" : void 0;
46667
+ default: return;
46668
+ }
46669
+ };
46670
+ /**
46671
+ * The restore window's one result, or nothing when this settled session fact
46672
+ * belongs to a probe rather than to the window.
46673
+ */
46674
+ const restorationObservation = (record, state) => {
46675
+ if (record.slot !== "identity:session" || record.from !== RESTORE_WINDOW) return NONE;
46676
+ const outcome = restorationOutcome(record.event, state);
46677
+ if (outcome === void 0) return NONE;
46678
+ return {
46679
+ name: "access_recovery_completed",
46680
+ props: {
46681
+ recovery: "session_restoration",
46682
+ outcome,
46683
+ ...outcome === "failed" && state.phase === "faulted" ? { reason_code: state.failure.code } : {},
46684
+ ...linkage(record)
46685
+ }
46686
+ };
46687
+ };
46688
+ const REFUSAL_EVENTS = /* @__PURE__ */ new Set([
46689
+ "ClaimAccount",
46690
+ "RetryAccount",
46691
+ "CreateOrganization",
46692
+ "AttachOrganization",
46693
+ "RetryOrganization"
46694
+ ]);
45581
46695
  function mapIdentityProductObservation(record, state) {
45582
46696
  if (record.machine !== "identity" || !IDENTITY_EVENT_TAG_SET.has(record.event)) return NONE;
45583
46697
  if (record.outcome === "cancelled") return NONE;
45584
46698
  if (record.slot === "identity:auth" && record.outcome === "refused") return authFailure(record, record.refusal_code, void 0);
45585
46699
  if (record.slot === "identity:auth" && record.outcome === "failed") return authFailure(record, record.error_code, record.failure?.mode);
46700
+ if (record.outcome === "refused" && REFUSAL_EVENTS.has(record.event)) return {
46701
+ name: "identity_refused",
46702
+ props: {
46703
+ event: record.event,
46704
+ refusal_code: record.refusal_code,
46705
+ slot: record.slot,
46706
+ state: record.state,
46707
+ ...linkage(record)
46708
+ }
46709
+ };
45586
46710
  if (record.outcome !== "applied") return NONE;
45587
46711
  switch (APPLIED_ACTION_BY_EVENT[record.event]) {
45588
46712
  case "none": return NONE;
@@ -45605,6 +46729,7 @@ function mapIdentityProductObservation(record, state) {
45605
46729
  name: "auth_signed_out",
45606
46730
  props: linkage(record)
45607
46731
  };
46732
+ case "restoration": return restorationObservation(record, state);
45608
46733
  }
45609
46734
  }
45610
46735
  const ignoreTransportFailure = (operation, operationName, eventName) => Effect.suspend(operation).pipe(Effect.catchCause(() => Effect.logWarning("identity.product.telemetry.dropped").pipe(Effect.annotateLogs({
@@ -45616,7 +46741,7 @@ function executeIdentityProductObservation(telemetry, record, state) {
45616
46741
  if (mapped === null) return Effect.void;
45617
46742
  copyInvocationObservation(record, mapped);
45618
46743
  const invocationTelemetry = bindProductTelemetryInvocation(telemetry, mapped);
45619
- const after = () => mapped.name === "auth_verified" && state.phase === "authenticated" ? invocationTelemetry.identify({
46744
+ const after = () => state.phase === "authenticated" && (mapped.name === "auth_verified" || mapped.name === "access_recovery_completed") ? invocationTelemetry.identify({
45620
46745
  distinctId: state.session.authUserId,
45621
46746
  traits: { email_domain: emailDomain(state.session.email) }
45622
46747
  }) : mapped.name === "auth_signed_out" ? invocationTelemetry.reset() : Effect.void;
@@ -45661,6 +46786,51 @@ function startSignerReadinessOnClaim(actor, signer) {
45661
46786
  browserSigner.ensureWalletReady?.().catch(() => {});
45662
46787
  });
45663
46788
  }
46789
+ /**
46790
+ * Report the result of each signer readiness cycle (#1962 R04).
46791
+ *
46792
+ * The readiness store already carries the cycle boundary this needs, so no
46793
+ * public signer hook, second identity subscriber, or render counter is added.
46794
+ * `ensureOpenfortWalletReady` writes `recovering` synchronously when it starts
46795
+ * a cycle, and only the cycle that is still the current one writes `ready` or
46796
+ * `unavailable` (`create-openfort-browser-signer.ts:501-521`). Callers that
46797
+ * join a live cycle share that single write, and a `resetSession` writes
46798
+ * `unknown` first, so a cycle that a reset or an account replacement outran
46799
+ * reports nothing.
46800
+ *
46801
+ * The host context is bound when the cycle starts, so a person who signs out
46802
+ * and back in while a cycle runs cannot relabel the result that began under
46803
+ * the previous person. A signer with no readiness cycle — a node or dev key,
46804
+ * and a headless client whose requirement never opens the account lane —
46805
+ * writes no status and reports nothing.
46806
+ */
46807
+ function observeSignerReadinessCycles(signer, telemetry, hostObservationSnapshot, runPromise) {
46808
+ const statusStore = signer?.statusStore;
46809
+ if (statusStore === void 0 || telemetry === void 0) return () => {};
46810
+ let running = false;
46811
+ let startContext;
46812
+ return statusStore.subscribe((status) => {
46813
+ if (status === "recovering") {
46814
+ running = true;
46815
+ startContext = hostObservationSnapshot?.();
46816
+ return;
46817
+ }
46818
+ const cycleContext = startContext;
46819
+ const completesCycle = running;
46820
+ running = false;
46821
+ startContext = void 0;
46822
+ if (!completesCycle || status !== "ready" && status !== "unavailable") return;
46823
+ const event = {
46824
+ name: "access_recovery_completed",
46825
+ props: {
46826
+ recovery: "signer_readiness",
46827
+ outcome: status
46828
+ }
46829
+ };
46830
+ if (cycleContext !== void 0) attachInvocationObservation(event, cycleContext);
46831
+ runPromise(Effect.suspend(() => bindProductTelemetryInvocation(telemetry, event).emit(event)).pipe(Effect.catchCause(() => Effect.void))).catch(() => {});
46832
+ });
46833
+ }
45664
46834
  function assembleCapxulClient(input) {
45665
46835
  const observation = input;
45666
46836
  const authCache = input.authCache ?? detectAuthCacheAdapter();
@@ -45690,12 +46860,14 @@ function assembleCapxulClient(input) {
45690
46860
  }, failureObservation, normalizeCapxulOperation(`identity.${record.event}`), "machine");
45691
46861
  });
45692
46862
  const unsubscribeSignerReadiness = startSignerReadinessOnClaim(actor, input.signer);
46863
+ const unsubscribeReadinessObservation = observeSignerReadinessCycles(input.signer, input.ports.telemetry, observation.hostObservationSnapshot, effectRunner.runPromise);
45693
46864
  let stopPromise = null;
45694
46865
  const stopActor = () => {
45695
46866
  if (stopPromise === null) {
45696
46867
  unsubscribeProductTelemetry();
45697
46868
  unsubscribeFailureObservation();
45698
46869
  unsubscribeSignerReadiness();
46870
+ unsubscribeReadinessObservation();
45699
46871
  stopPromise = effectRunner.runPromise(Scope.close(scope, Exit.void));
45700
46872
  }
45701
46873
  return stopPromise;
@@ -45712,7 +46884,8 @@ function assembleCapxulClient(input) {
45712
46884
  convexCall: input.ports.convexCall,
45713
46885
  ...input.signer === void 0 ? {} : { signer: input.signer },
45714
46886
  telemetry: input.ports.telemetry,
45715
- requestKeyScope: `${input.bootstrap.convexUrl}:${String(input.bootstrap.chainId)}`
46887
+ requestKeyScope: `${input.bootstrap.convexUrl}:${String(input.bootstrap.chainId)}`,
46888
+ ...input.ports.indexerRead === void 0 ? {} : { indexerRead: input.ports.indexerRead }
45716
46889
  });
45717
46890
  const accountBundle = makeAccountMethods({
45718
46891
  actor,
@@ -45741,10 +46914,13 @@ function assembleCapxulClient(input) {
45741
46914
  ...input.invokeTimeoutMs === void 0 ? {} : { invokeTimeoutMs: input.invokeTimeoutMs },
45742
46915
  afterVerifyOtp: async () => {
45743
46916
  const browserSigner = input.signer;
45744
- if (browserSigner !== void 0 && "resetSession" in browserSigner && typeof browserSigner.resetSession === "function") try {
45745
- browserSigner.resetSession();
45746
- } catch (cause) {
45747
- throw signerFailure(browserSigner.source, "resetSession", cause);
46917
+ if (browserSigner !== void 0 && "resetSession" in browserSigner && typeof browserSigner.resetSession === "function") {
46918
+ try {
46919
+ browserSigner.resetSession();
46920
+ } catch (cause) {
46921
+ throw signerFailure(browserSigner.source, "resetSession", cause);
46922
+ }
46923
+ if (browserSigner.statusStore?.status() === "unknown" && typeof browserSigner.ensureWalletReady === "function") browserSigner.ensureWalletReady().catch(() => {});
45748
46924
  }
45749
46925
  await detectPendingOrgInvitations?.();
45750
46926
  kickProvisioning?.();
@@ -45771,6 +46947,7 @@ function assembleCapxulClient(input) {
45771
46947
  ensureAccount: account._internal.ensureReady,
45772
46948
  getLifecycle: account.getLifecycle,
45773
46949
  snapshotIdentity: actor.snapshot,
46950
+ subscribeIdentity: actor.subscribe,
45774
46951
  sendIdentity: (event, options) => runCapxulEffect(askIdentity(actor, event, options), options, CAPXUL_OPERATIONS._internal.identity.send, effectRunner.runPromise)
45775
46952
  });
45776
46953
  const system = makeSystemMethods({ convexCall: input.ports.convexCall });
@@ -45779,7 +46956,8 @@ function assembleCapxulClient(input) {
45779
46956
  convexCall: input.ports.convexCall,
45780
46957
  telemetry: input.ports.telemetry,
45781
46958
  runPromise: effectRunner.runPromise,
45782
- invocationControls: (controls) => withHostObservationControls(observation.hostObservationSnapshot, controls, actor.authSession()?.authUserId)
46959
+ invocationControls: (controls) => withHostObservationControls(observation.hostObservationSnapshot, controls, actor.authSession()?.authUserId),
46960
+ ...input.ports.indexerRead === void 0 ? {} : { indexerRead: input.ports.indexerRead }
45783
46961
  });
45784
46962
  const accounts = makeAccountsMethods({
45785
46963
  accountReadPort: input.ports.accountRead,
@@ -45904,6 +47082,7 @@ function assembleCapxulClient(input) {
45904
47082
  identity: identityRuntime,
45905
47083
  bootstrap: input.bootstrap,
45906
47084
  accounts: { fund: accounts.fund },
47085
+ ...input.ports.indexerRead === void 0 ? {} : { indexer: input.ports.indexerRead },
45907
47086
  organizationSetup: { getProofReceipt: (orgId) => runPortEffect(input.ports.convexCall.query(organizationSetupProofReceiptQuery, { orgId })) },
45908
47087
  telemetry: input.ports.telemetry,
45909
47088
  captureExternalAddressAcknowledged: (address, organizationId) => {
@@ -45962,8 +47141,10 @@ function withHostObservationControls(snapshot, input, actorId) {
45962
47141
  const previous = readInvocationObservation(input);
45963
47142
  if (previous !== void 0) return attachInvocationObservation({ ...input }, previous);
45964
47143
  const captured = snapshot?.() ?? { active: true };
47144
+ const context = captured.active ? withCapturedAttemptContext(captured.context, input) : captured.context;
45965
47145
  return attachInvocationObservation({ ...input }, {
45966
47146
  ...captured,
47147
+ ...context === void 0 ? {} : { context },
45967
47148
  ...actorId === void 0 ? {} : { actorId }
45968
47149
  });
45969
47150
  }
@@ -45983,6 +47164,352 @@ function snapshotHostObservability(observability) {
45983
47164
  };
45984
47165
  }
45985
47166
  //#endregion
47167
+ //#region ../../packages/sdk/src/adapters/ponder/decode.ts
47168
+ /**
47169
+ * Decode the indexer's rows at the trust boundary.
47170
+ *
47171
+ * Ponder returns the raw PostgreSQL result: `integer` arrives as a number,
47172
+ * `numeric(78)` and `text` arrive as strings. Amounts, block numbers and
47173
+ * timestamps are `numeric(78)`, so they become `bigint` here and never pass
47174
+ * through `Number`. Every decoder throws a `CapxulError`; the adapter turns
47175
+ * that into a typed `IndexerReadError`.
47176
+ */
47177
+ const HEX = /^0x[0-9a-fA-F]+$/;
47178
+ function field(row, key) {
47179
+ if (!(key in row)) throw Errors.invalidInput(key, "missing from the indexer row");
47180
+ return row[key];
47181
+ }
47182
+ function text(row, key) {
47183
+ const value = field(row, key);
47184
+ if (typeof value !== "string") throw Errors.invalidInput(key, "must be a string");
47185
+ return value;
47186
+ }
47187
+ function hex(row, key) {
47188
+ const value = text(row, key);
47189
+ if (!HEX.test(value)) throw Errors.invalidInput(key, "must be a hex string");
47190
+ return value.toLowerCase();
47191
+ }
47192
+ function integer(row, key) {
47193
+ const value = field(row, key);
47194
+ const parsed = typeof value === "string" ? Number(value) : value;
47195
+ if (typeof parsed !== "number" || !Number.isSafeInteger(parsed)) throw Errors.invalidInput(key, "must be an integer");
47196
+ return parsed;
47197
+ }
47198
+ /** `numeric(78)` crosses the wire as a decimal string; keep every digit. */
47199
+ function big(row, key) {
47200
+ const value = field(row, key);
47201
+ if (typeof value === "bigint") return value;
47202
+ const raw = typeof value === "number" ? String(value) : value;
47203
+ if (typeof raw !== "string" || !/^-?[0-9]+$/.test(raw)) throw Errors.invalidInput(key, "must be an exact integer amount");
47204
+ return BigInt(raw);
47205
+ }
47206
+ function asRow(value) {
47207
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw Errors.invalidInput("row", "must be an object");
47208
+ return value;
47209
+ }
47210
+ function identity(row) {
47211
+ return {
47212
+ id: text(row, "id"),
47213
+ chainId: toChainId(integer(row, "chain_id")),
47214
+ txHash: toTxHash(hex(row, "tx_hash")),
47215
+ transactionLogOrdinal: integer(row, "transaction_log_ordinal"),
47216
+ blockNumber: big(row, "block_number"),
47217
+ blockHash: hex(row, "block_hash"),
47218
+ timestamp: big(row, "timestamp")
47219
+ };
47220
+ }
47221
+ function decodeTransfer(value) {
47222
+ const row = asRow(value);
47223
+ return {
47224
+ ...identity(row),
47225
+ assetId: toAssetId(text(row, "asset_id")),
47226
+ token: toAddress(hex(row, "token")),
47227
+ from: toAddress(hex(row, "from")),
47228
+ to: toAddress(hex(row, "to")),
47229
+ amount: big(row, "amount")
47230
+ };
47231
+ }
47232
+ function decodePayment(value) {
47233
+ const row = asRow(value);
47234
+ return {
47235
+ ...identity(row),
47236
+ contract: toAddress(hex(row, "contract")),
47237
+ settlementId: hex(row, "settlement_id"),
47238
+ sender: toAddress(hex(row, "sender")),
47239
+ recipient: toAddress(hex(row, "recipient")),
47240
+ assetId: toAssetId(text(row, "asset_id")),
47241
+ token: toAddress(hex(row, "token")),
47242
+ amount: big(row, "amount"),
47243
+ kind: integer(row, "kind"),
47244
+ documentHash: hex(row, "document_hash")
47245
+ };
47246
+ }
47247
+ /** The `balance` view exposes the signed sum under the underlying column name `to`. */
47248
+ function decodeBalance(value) {
47249
+ const row = asRow(value);
47250
+ return {
47251
+ chainId: toChainId(integer(row, "chain_id")),
47252
+ assetId: toAssetId(text(row, "asset_id")),
47253
+ address: toAddress(hex(row, "to")),
47254
+ amount: big(row, "amount")
47255
+ };
47256
+ }
47257
+ const DECODERS = {
47258
+ transfer: decodeTransfer,
47259
+ payment: decodePayment,
47260
+ balance: decodeBalance
47261
+ };
47262
+ /** Decode one `/sql/db` or `/sql/live` payload. Ponder returns `{ rows: [...] }`. */
47263
+ function decodeRows(table, body) {
47264
+ const rows = asRow(body).rows;
47265
+ if (!Array.isArray(rows)) throw Errors.invalidInput("rows", "must be an array");
47266
+ const decode = DECODERS[table];
47267
+ return rows.map(decode);
47268
+ }
47269
+ /** Decode Ponder's native `/status`: `{ [chainName]: { id, block: { number, timestamp } } }`. */
47270
+ function decodeStatus(body) {
47271
+ const chains = asRow(body);
47272
+ return Object.entries(chains).map(([chain, value]) => {
47273
+ const entry = asRow(value);
47274
+ const block = asRow(field(entry, "block"));
47275
+ return {
47276
+ chain,
47277
+ chainId: toChainId(integer(entry, "id")),
47278
+ blockNumber: big(block, "number"),
47279
+ blockTimestamp: big(block, "timestamp")
47280
+ };
47281
+ });
47282
+ }
47283
+ //#endregion
47284
+ //#region ../../packages/sdk/src/adapters/ponder/statements.ts
47285
+ /**
47286
+ * The exact statements the indexer accepts.
47287
+ *
47288
+ * `apps/indexer/src/queries.ts` compiles its allowlist with `@ponder/client`
47289
+ * and compares `sql`, `params` and `typings` byte for byte against the caller's
47290
+ * statement. Anything else gets 403. The SDK therefore reproduces the compiled
47291
+ * text here rather than pulling Ponder and Drizzle into a published browser
47292
+ * bundle to regenerate three fixed statements.
47293
+ *
47294
+ * `statements.test.ts` pins the text. When the indexer schema or a query in
47295
+ * that allowlist changes, this file changes with it or every read fails 403.
47296
+ */
47297
+ const HEADS = {
47298
+ 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",
47299
+ 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",
47300
+ balance: "select \"chain_id\", \"asset_id\", \"to\", \"amount\" from \"balance\" where \"balance\".\"to\" = $1"
47301
+ };
47302
+ /** How many times the statement binds the Safe address. */
47303
+ const ACTOR_PARAMS = {
47304
+ transfer: 2,
47305
+ payment: 2,
47306
+ balance: 1
47307
+ };
47308
+ /**
47309
+ * Build the compiled query for one table and page. Throws a `CapxulError` for
47310
+ * a page or address the indexer would refuse, so the caller never spends a
47311
+ * round trip to learn it built the request wrong.
47312
+ */
47313
+ function ponderQuery(table, input) {
47314
+ const safe = assertIndexerPage(input);
47315
+ const offset = input.offset ?? 0;
47316
+ const actors = ACTOR_PARAMS[table];
47317
+ const params = [];
47318
+ for (let i = 0; i < actors; i++) params.push(safe);
47319
+ params.push(input.limit);
47320
+ let sql = `${HEADS[table]} limit $${actors + 1}`;
47321
+ if (offset > 0) {
47322
+ params.push(offset);
47323
+ sql = `${sql} offset $${actors + 2}`;
47324
+ }
47325
+ return {
47326
+ sql,
47327
+ params,
47328
+ typings: params.map(() => "none")
47329
+ };
47330
+ }
47331
+ /**
47332
+ * The `?sql=` value. `@ponder/client` sends `superjson.stringify(query)`; for a
47333
+ * payload of strings and numbers superjson emits no `meta`, so the wire form is
47334
+ * exactly this envelope.
47335
+ */
47336
+ function ponderQueryParam(query) {
47337
+ return JSON.stringify({ json: query });
47338
+ }
47339
+ //#endregion
47340
+ //#region ../../packages/sdk/src/adapters/ponder/sse.ts
47341
+ /**
47342
+ * The smallest SSE reader that can carry an `Authorization` header.
47343
+ *
47344
+ * `@ponder/client` opens its live query with `EventSource`, which accepts no
47345
+ * headers, so it cannot reach the guarded indexer at all. The indexer's CORS
47346
+ * policy allows exactly one request header (`Authorization`), so this reader
47347
+ * sends nothing else. Ponder writes one `data:` line per frame.
47348
+ */
47349
+ async function readEventStream(body, onData, stopped) {
47350
+ const reader = body.getReader();
47351
+ const decoder = new TextDecoder();
47352
+ let buffer = "";
47353
+ try {
47354
+ for (;;) {
47355
+ const { done, value } = await reader.read();
47356
+ if (done || stopped()) return;
47357
+ buffer += decoder.decode(value, { stream: true });
47358
+ let split = buffer.indexOf("\n\n");
47359
+ while (split !== -1) {
47360
+ const frame = buffer.slice(0, split);
47361
+ buffer = buffer.slice(split + 2);
47362
+ const data = frameData(frame);
47363
+ if (data !== void 0) onData(data);
47364
+ if (stopped()) return;
47365
+ split = buffer.indexOf("\n\n");
47366
+ }
47367
+ }
47368
+ } finally {
47369
+ await reader.cancel().catch(() => void 0);
47370
+ }
47371
+ }
47372
+ function frameData(frame) {
47373
+ const lines = [];
47374
+ for (const line of frame.split("\n")) {
47375
+ if (!line.startsWith("data:")) continue;
47376
+ lines.push(line.slice(line.startsWith("data: ") ? 6 : 5));
47377
+ }
47378
+ return lines.length === 0 ? void 0 : lines.join("\n");
47379
+ }
47380
+ //#endregion
47381
+ //#region ../../packages/sdk/src/adapters/ponder/PonderIndexerAdapter.ts
47382
+ var PonderIndexerAdapter = class {
47383
+ #baseUrl;
47384
+ #token;
47385
+ #fetch;
47386
+ #observation;
47387
+ constructor(deps) {
47388
+ this.#baseUrl = deps.baseUrl.replace(/\/$/, "");
47389
+ this.#token = deps.token;
47390
+ this.#observation = deps.observation;
47391
+ this.#fetch = deps.fetch ?? ((input, init) => globalThis.fetch(input, init));
47392
+ }
47393
+ read(table, input) {
47394
+ const operation = `indexer.read.${table}`;
47395
+ return Effect.tryPromise({
47396
+ try: async () => {
47397
+ return decodeRows(table, await (await this.#request("db", table, input, operation)).json());
47398
+ },
47399
+ catch: (cause) => toIndexerReadError(operation, cause)
47400
+ });
47401
+ }
47402
+ subscribe(table, input, callback) {
47403
+ const operation = CAPXUL_OPERATIONS.activity.subscribe;
47404
+ return Effect.sync(() => {
47405
+ let stopped = false;
47406
+ const unsubscribe = () => {
47407
+ stopped = true;
47408
+ };
47409
+ callback({ status: "loading" });
47410
+ this.#stream(table, input, operation, callback, () => stopped).catch(() => void 0);
47411
+ return unsubscribe;
47412
+ });
47413
+ }
47414
+ status = Effect.tryPromise({
47415
+ try: async () => {
47416
+ const response = await this.#fetch(`${this.#baseUrl}/status`, { method: "GET" });
47417
+ if (!response.ok) throw httpError("indexer.status", response.status, await safeText(response));
47418
+ return decodeStatus(await response.json());
47419
+ },
47420
+ catch: (cause) => toIndexerReadError("indexer.status", cause)
47421
+ });
47422
+ /** One authenticated `/sql/:method` call. Throws a `CapxulError` on refusal. */
47423
+ async #request(method, table, input, operation) {
47424
+ const query = ponderQuery(table, input);
47425
+ const token = await this.#token({ forceRefreshToken: false });
47426
+ if (token === null || token === "") throw Errors.notAuthenticated();
47427
+ const url = `${this.#baseUrl}/sql/${method}?sql=${encodeURIComponent(ponderQueryParam(query))}`;
47428
+ const headers = { authorization: `Bearer ${token}` };
47429
+ if (method === "live") headers.accept = "text/event-stream";
47430
+ let response;
47431
+ try {
47432
+ response = await this.#fetch(url, {
47433
+ method: "GET",
47434
+ headers
47435
+ });
47436
+ } catch (cause) {
47437
+ throw Errors.networkError(operation, cause, {
47438
+ provider: "indexer",
47439
+ failure_mode: "upstream-down"
47440
+ });
47441
+ }
47442
+ if (!response.ok) throw httpError(operation, response.status, await safeText(response));
47443
+ return response;
47444
+ }
47445
+ /**
47446
+ * Follow the live stream until the caller unsubscribes or the indexer closes
47447
+ * it. The indexer closes the stream on token expiry or revoked authority
47448
+ * with no frame, so any end the caller did not ask for is a teardown: it
47449
+ * reaches `captureException` under `activity.subscribe` and the caller sees
47450
+ * one `error` snapshot.
47451
+ */
47452
+ async #stream(table, input, operation, callback, stopped) {
47453
+ let failure;
47454
+ try {
47455
+ const response = await this.#request("live", table, input, operation);
47456
+ if (response.body === null) throw Errors.providerError("indexer", operation, "empty stream");
47457
+ await readEventStream(response.body, (data) => {
47458
+ try {
47459
+ callback({
47460
+ status: "ok",
47461
+ value: decodeRows(table, JSON.parse(data))
47462
+ });
47463
+ } catch (cause) {
47464
+ failure = toPublicError$1(operation, cause);
47465
+ }
47466
+ }, () => stopped() || failure !== void 0);
47467
+ } catch (cause) {
47468
+ failure = toPublicError$1(operation, cause);
47469
+ }
47470
+ if (stopped()) return;
47471
+ const error = failure ?? Errors.capabilityUnavailable("indexer", operation);
47472
+ observeStreamFailure(this.#observation, CAPXUL_OPERATIONS.activity.subscribe, error, error.code === "NOT_AUTHENTICATED" ? "operation" : "exception");
47473
+ callback({
47474
+ status: "error",
47475
+ error
47476
+ });
47477
+ }
47478
+ };
47479
+ /**
47480
+ * Map an indexer refusal into the shared error vocabulary.
47481
+ *
47482
+ * 401 is the expected outcome of an expired or revoked session. 403 is not:
47483
+ * the indexer accepts only its own compiled statements for a Safe in the
47484
+ * claim, so a refusal means this SDK and that deployment disagree, or the
47485
+ * session gained a Safe and still holds the older token. Both must be loud.
47486
+ */
47487
+ function httpError(operation, status, body) {
47488
+ if (status === 401) return Errors.notAuthenticated();
47489
+ if (status === 429) return Errors.rateLimited({ resource: "indexer" });
47490
+ if (status === 503) return Errors.capabilityUnavailable("indexer", operation);
47491
+ return Errors.providerError("indexer", operation, body, {
47492
+ httpStatus: status,
47493
+ ...status >= 500 ? { failure_mode: "upstream-down" } : {},
47494
+ details: { reason: status === 403 ? "scope-refused" : "http-status" }
47495
+ });
47496
+ }
47497
+ function toPublicError$1(operation, cause) {
47498
+ if (cause instanceof CapxulError) return cause;
47499
+ return Errors.providerError("indexer", operation, cause);
47500
+ }
47501
+ function toIndexerReadError(operation, cause) {
47502
+ if (cause instanceof IndexerReadError) return cause;
47503
+ return indexerReadErrorFromCapxul(operation, toPublicError$1(operation, cause), cause);
47504
+ }
47505
+ async function safeText(response) {
47506
+ try {
47507
+ return await response.text();
47508
+ } catch {
47509
+ return "";
47510
+ }
47511
+ }
47512
+ //#endregion
45986
47513
  //#region ../../packages/sdk/src/adapters/_shared/clock.ts
45987
47514
  async function readClockNow(clock) {
45988
47515
  try {
@@ -46439,7 +47966,16 @@ async function createProductionAdapters(input) {
46439
47966
  }));
46440
47967
  const applicationLayer = Layer.merge(portsLayer, optionalEngineeringTelemetryLayer(bootstrapResult.value.engineeringTelemetry, resolvedInput.value.runtime));
46441
47968
  const context = await Effect.runPromise(Layer.buildWithScope(applicationLayer, scope));
46442
- const ports = await Effect.runPromise(collectProductionFlowPorts.pipe(Effect.provide(context)));
47969
+ const collected = await Effect.runPromise(collectProductionFlowPorts.pipe(Effect.provide(context)));
47970
+ const indexerUrl = input.indexerUrl ?? bootstrapResult.value.indexerUrl;
47971
+ const indexerRead = productionIndexerReadPort(indexerUrl === void 0 ? input : {
47972
+ ...input,
47973
+ indexerUrl
47974
+ }, collected, observation);
47975
+ const ports = indexerRead === void 0 ? collected : {
47976
+ ...collected,
47977
+ indexerRead
47978
+ };
46443
47979
  const stopConnectionObservation = observeProductionConvexConnection(ports.convexCall, context);
46444
47980
  const close = idempotentClose(async () => {
46445
47981
  try {
@@ -46566,7 +48102,7 @@ async function createCapxulClientWithSignerControls(input, controls) {
46566
48102
  ...adapters.value.bootstrap,
46567
48103
  authBaseUrl: resolvedAuthBaseUrl
46568
48104
  }, {
46569
- diagnostic: new ConsoleDiagnosticAdapter(),
48105
+ diagnostic: new EffectLogDiagnosticAdapter(adapters.value.context, new ConsoleDiagnosticAdapter()),
46570
48106
  signUserOpHash: controls.signEmbeddedUserOpHash
46571
48107
  });
46572
48108
  signer = withSignerControl(signer, controls.signUserOpHash);
@@ -46771,6 +48307,29 @@ function idempotentClose(close) {
46771
48307
  await close();
46772
48308
  };
46773
48309
  }
48310
+ /**
48311
+ * Build the Ponder read port for this client, or `undefined` when no indexer
48312
+ * origin was supplied. The token provider is the same
48313
+ * `AuthClientPort.getConvexJwt` closure `ConvexCallPort` uses, so the indexer
48314
+ * and Convex see one session with one scope.
48315
+ */
48316
+ function productionIndexerReadPort(input, ports, observation) {
48317
+ if (input.activityReadSource === "convex") return void 0;
48318
+ if (input.indexerUrl === void 0 || input.indexerUrl === "") return void 0;
48319
+ return new PonderIndexerAdapter({
48320
+ baseUrl: normalizeHttpUrl("indexerUrl", input.indexerUrl),
48321
+ token: async ({ forceRefreshToken }) => {
48322
+ const token = await Effect.runPromise(Effect.result(ports.authClient.getConvexJwt({
48323
+ forceRefresh: forceRefreshToken,
48324
+ ...input.signal === void 0 ? {} : { signal: input.signal }
48325
+ })));
48326
+ if (Result.isFailure(token)) return null;
48327
+ return String(token.success.token);
48328
+ },
48329
+ ...input.fetch === void 0 ? {} : { fetch: input.fetch },
48330
+ ...observation === void 0 ? {} : { observation }
48331
+ });
48332
+ }
46774
48333
  //#endregion
46775
48334
  //#region src/tools/account.ts
46776
48335
  const READ = "account:read";
@@ -47382,7 +48941,7 @@ function stdioIdentity(env = process.env, options = {}) {
47382
48941
  }
47383
48942
  //#endregion
47384
48943
  //#region package.json
47385
- var version = "4.2.0-rc.1";
48944
+ var version = "4.2.0-rc.11";
47386
48945
  //#endregion
47387
48946
  //#region src/deps.ts
47388
48947
  const NO_IDENTITY = async () => void 0;