@capxul/mcp 4.2.0-rc.9 → 4.20.0-beta.1

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
@@ -8,9 +8,9 @@ import { createHash } from "node:crypto";
8
8
  import { createServer } from "node:http";
9
9
  import { localhostHostValidation, localhostOriginValidation, toNodeHandler } from "@modelcontextprotocol/node";
10
10
  import { z } from "zod";
11
- import { Cause, Clock, Context, Data, Deferred, Duration, Effect, Exit, Fiber, FiberSet, Layer, Option, Queue, Ref, Result, Schedule, Schema, SchemaGetter, SchemaIssue, SchemaParser, Scope, Stream, Tracer } from "effect";
12
- import { FetchHttpClient, Headers, HttpClient } from "effect/unstable/http";
13
- import { OtlpExporter, OtlpLogger, OtlpSerialization, OtlpTracer } from "effect/unstable/observability";
11
+ import { Cause, Clock, Context, Data, Deferred, Duration, Effect, Exit, Fiber, FiberSet, Layer, Logger, Option, Queue, Ref, Result, Schedule, Schema, SchemaGetter, SchemaIssue, SchemaParser, Scope, Stream, Tracer } from "effect";
12
+ import { FetchHttpClient, Headers, HttpBody, HttpClient, HttpClientResponse, HttpTraceContext } from "effect/unstable/http";
13
+ import { OtlpExporter, OtlpLogger, OtlpMetrics, OtlpSerialization, OtlpTracer } from "effect/unstable/observability";
14
14
  import { resolve } from "path";
15
15
  import util from "util";
16
16
  import stream, { Readable } from "stream";
@@ -127,14 +127,19 @@ function redactUrlSecrets(value, source) {
127
127
  changed ||= safeName !== name || safeEntry !== entry;
128
128
  return [safeName, safeEntry];
129
129
  })).toString();
130
- const provider = source !== void 0 || /(?:^|\.)(?:alchemy\.com|alchemyapi\.io|infura\.io)$/iu.test(url.hostname);
131
- const segments = (absolute || protocolRelative ? url.pathname : value.split(/[?#]/u)[0] ?? "").split("/");
132
- const path = segments.map((segment, index) => {
130
+ const provider = source === "alchemy" || source === "infura" || /(?:^|\.)(?:alchemy\.com|alchemyapi\.io|infura\.io)$/iu.test(url.hostname);
131
+ const rawPath = absolute || protocolRelative ? url.pathname : value.split(/[?#]/u)[0] ?? "";
132
+ const segments = rawPath.split("/");
133
+ let path = segments.map((segment, index) => {
133
134
  const decoded = decodeUrlComponent(segment);
134
135
  const masked = provider && /^v[23]$/u.test(decodeUrlComponent(segments[index - 1] ?? "")) && decoded.length > 0 ? "[REDACTED]" : maskText(decoded);
135
136
  changed ||= masked !== decoded;
136
137
  return masked === decoded ? segment : encodeURIComponent(masked);
137
138
  }).join("/");
139
+ if (source === "gateway" && rawPath.length > 0) {
140
+ path = `${rawPath.startsWith("/") ? "/" : ""}${encodeURIComponent("[REDACTED]")}`;
141
+ changed = true;
142
+ }
138
143
  const query = maskParams(url.searchParams);
139
144
  const fragment = url.hash.slice(1);
140
145
  const decodedFragment = decodeUrlComponent(fragment);
@@ -149,6 +154,7 @@ function redactUrlSecrets(value, source) {
149
154
  if (!changed) return value;
150
155
  return `${origin}${path}${query ? `?${query}` : ""}${hash ? `#${hash}` : ""}`;
151
156
  } catch {
157
+ if (source === "gateway") return "[REDACTED]";
152
158
  return maskText(value.replace(/(https?:\/\/)[^/\s@]+@/giu, "$1[REDACTED]@").replace(/([?&#])([^=&#]+)=([^&#]*)/gu, (whole, separator, name) => isCredentialField(decodeUrlComponent(name)) || /^(?:code|draft|__posthog)$/iu.test(decodeUrlComponent(name)) ? `${separator}${name}=[REDACTED]` : whole));
153
159
  }
154
160
  }
@@ -931,6 +937,10 @@ function toChainId(raw) {
931
937
  if (typeof raw !== "number" || !Number.isSafeInteger(raw) || raw <= 0) throw Errors.invalidInput("chainId", invalidValueReason("must be a positive safe integer", raw));
932
938
  return raw;
933
939
  }
940
+ function toWeiAmount(raw) {
941
+ if (typeof raw !== "string" || !WEI_RE.test(raw)) throw Errors.invalidInput("weiAmount", invalidValueReason("must be a non-negative integer string", raw));
942
+ return raw;
943
+ }
934
944
  function toCountryCode(raw) {
935
945
  if (typeof raw !== "string") throw Errors.invalidInput("countryCode", "must be a string");
936
946
  const upper = raw.toUpperCase();
@@ -1087,8 +1097,18 @@ const SYNTHETIC_TEST_ASSETS = [
1087
1097
  }
1088
1098
  ];
1089
1099
  toChainId(84532);
1090
- [...CONFIGURED_MONEY_ASSETS, ...SYNTHETIC_TEST_ASSETS];
1100
+ /**
1101
+ * Every known asset in ONE identity space. The tiers above declare admission;
1102
+ * this is the only list an identity resolves against, so an asset is never
1103
+ * two different things depending on which list a caller happened to read.
1104
+ */
1105
+ const MONEY_ASSET_REGISTRY = [...CONFIGURED_MONEY_ASSETS, ...SYNTHETIC_TEST_ASSETS];
1091
1106
  CONFIGURED_MONEY_ASSETS.map((asset) => asset.tokenAddress.toLowerCase());
1107
+ /** The registry row for `assetId` across every tier, or `null`. */
1108
+ function configuredMoneyAssetById(assetId) {
1109
+ const canonical = toAssetId(assetId);
1110
+ return MONEY_ASSET_REGISTRY.find((asset) => asset.assetId === canonical) ?? null;
1111
+ }
1092
1112
  //#endregion
1093
1113
  //#region ../../node_modules/.pnpm/abitype@1.2.3_typescript@7.0.2_zod@4.4.3/node_modules/abitype/dist/esm/regex.js
1094
1114
  function execTyped(regex, string) {
@@ -6933,8 +6953,12 @@ const CAPXUL_OPERATIONS = {
6933
6953
  cancelRequest: "financialOps.cancelRequest",
6934
6954
  createPayee: "financialOps.createPayee",
6935
6955
  declineRequest: "financialOps.declineRequest",
6956
+ emitInvoiceIssued: "financialOps.emitInvoiceIssued",
6957
+ emitInvoiceRequestTransitioned: "financialOps.emitInvoiceRequestTransitioned",
6936
6958
  emitPaymentWriterTelemetry: "financialOps.emitPaymentWriterTelemetry",
6937
6959
  issueRequest: "financialOps.issueRequest",
6960
+ migrateEvidenceIdentity: "financialOps.migrateEvidenceIdentity",
6961
+ planEvidenceIdentity: "financialOps.planEvidenceIdentity",
6938
6962
  recoverSettlementVerification: "financialOps.recoverSettlementVerification",
6939
6963
  removeDestination: "financialOps.removeDestination",
6940
6964
  verifySettlement: "financialOps.verifySettlement"
@@ -6943,6 +6967,7 @@ const CAPXUL_OPERATIONS = {
6943
6967
  current: "holdings.current",
6944
6968
  primary: "holdings.primary"
6945
6969
  },
6970
+ indexer: { reconcile: "indexer.reconcile" },
6946
6971
  identity: {
6947
6972
  accountAt: "identity.AccountAt",
6948
6973
  accountClaimed: "identity.AccountClaimed",
@@ -7032,6 +7057,7 @@ const CAPXUL_OPERATIONS = {
7032
7057
  createOrg: "org.createOrg",
7033
7058
  create: "org.create",
7034
7059
  detectAndAcceptPendingInvitations: "org.detectAndAcceptPendingInvitations",
7060
+ emitInviteProgressed: "org.emitInviteProgressed",
7035
7061
  expireInvitations: "org.expireInvitations",
7036
7062
  getLifecycle: "org.getLifecycle",
7037
7063
  getProofReceipt: "org.getProofReceipt",
@@ -7135,6 +7161,8 @@ const CAPXUL_OPERATIONS = {
7135
7161
  recipient: { resolve: "recipient.resolve" },
7136
7162
  payroll: {
7137
7163
  authorizeRun: "payroll.authorizeRun",
7164
+ emitGroupChanged: "payroll.emitGroupChanged",
7165
+ emitRunTransitioned: "payroll.emitRunTransitioned",
7138
7166
  groups: {
7139
7167
  access: "payroll.groups",
7140
7168
  list: "payroll.groups.list",
@@ -7266,6 +7294,10 @@ const OptionalAccountId = Schema.optional(AccountIdSchema$1);
7266
7294
  const OptionalWeiAmount = Schema.optional(WeiAmountSchema$1);
7267
7295
  const OptionalCurrencyCode = Schema.optional(CurrencyCodeSchema$1);
7268
7296
  const OptionalBalanceBucket = Schema.optional(BalanceBucketSchema);
7297
+ /** Which Safe a money record is about. Declared here so the money-domain
7298
+ * props below can name it as well as the UI-step props further down. */
7299
+ const AccountKindSchema = Schema.Literals(["personalSafe", "orgTreasury"]);
7300
+ const OptionalAccountKind = Schema.optional(AccountKindSchema);
7269
7301
  const OptionalAddress = Schema.optional(AddressSchema$1);
7270
7302
  const OptionalAppId = Schema.optional(AppIdSchema$1);
7271
7303
  const OptionalBlockNumber = Schema.optional(BlockNumberSchema$1);
@@ -7418,9 +7450,22 @@ const BootstrapFailedProps = Schema.Struct({
7418
7450
  reason: OptionalString,
7419
7451
  target_host: OptionalString
7420
7452
  });
7453
+ /**
7454
+ * R08 (#1966): which Safe was read, on which chain, of which token. `currency`
7455
+ * is the DISPLAY code the surface shows ("USD"); it never says which ERC-20 the
7456
+ * `balanceOf` actually hit, so a reader could not tell a treasury read from a
7457
+ * personal one, nor USDX from any other dollar-labelled token. The producer
7458
+ * takes each of these from the authority that already decided it — the verified
7459
+ * Organization row's chain, the configured token address — never from a label.
7460
+ * The verified `organization_id` and its group arrive from the backend
7461
+ * attribution, so they are not repeated here.
7462
+ */
7421
7463
  const AccountBalanceReadProps = Schema.Struct({
7422
7464
  ...TelemetryEnvelopeProps,
7423
7465
  account_id: OptionalAccountId,
7466
+ account_kind: OptionalAccountKind,
7467
+ chain_id: OptionalChainId,
7468
+ token_address: OptionalAddress,
7424
7469
  currency: OptionalCurrencyCode,
7425
7470
  balance_bucket: OptionalBalanceBucket,
7426
7471
  durationMs: OptionalDurationMs
@@ -7788,7 +7833,27 @@ const PermissionMirrorVerificationProps = Schema.Struct({
7788
7833
  "conflicting_replay",
7789
7834
  "revision_mismatch"
7790
7835
  ])),
7791
- command_id: OptionalString
7836
+ command_id: OptionalString,
7837
+ /**
7838
+ * #1961 R03. The authorized stored row names the Permission it changed; the
7839
+ * verified receipt names the chain facts that prove it. Every field is
7840
+ * optional because a pending or provider-failed verification has neither an
7841
+ * application result nor a receipt to report.
7842
+ */
7843
+ permission_id: OptionalString,
7844
+ permission_assignment_id: OptionalString,
7845
+ permission_operation: Schema.optional(Schema.Literals([
7846
+ "create",
7847
+ "change",
7848
+ "assign",
7849
+ "revoke",
7850
+ "replace"
7851
+ ])),
7852
+ revision: Schema.optional(Schema.Number),
7853
+ user_op_hash: OptionalString,
7854
+ transaction_hash: OptionalString,
7855
+ block_number: Schema.optional(Schema.Number),
7856
+ log_index: Schema.optional(Schema.Number)
7792
7857
  });
7793
7858
  const MovementScanWindowProps = Schema.Struct({
7794
7859
  ...TelemetryEnvelopeProps,
@@ -7814,6 +7879,33 @@ const MovementScanIncidentProps = Schema.Struct({
7814
7879
  incident_kind: Schema.String,
7815
7880
  anchor_block_number: Schema.Number
7816
7881
  });
7882
+ /**
7883
+ * One durable row the reconciliation job corrected against Ponder (SPEC D9).
7884
+ *
7885
+ * Both block fields are optional and for the same reason: a correction INTO
7886
+ * drift (a Payment settled in Convex whose Ponder fact a reorg removed) has no
7887
+ * current block, and a correction that confirms a later fact has one.
7888
+ *
7889
+ * `projection_kind` is closed. A later ticket that reconciles a deploy, a
7890
+ * bootstrap or a grant outcome adds its kind here in the same change; an
7891
+ * unknown kind must not decode.
7892
+ */
7893
+ const IndexerDriftCorrectedProps = Schema.Struct({
7894
+ ...TelemetryEnvelopeProps,
7895
+ projection_kind: Schema.Literals([
7896
+ "member_authority",
7897
+ "payment_settlement",
7898
+ "permission_assignment"
7899
+ ]),
7900
+ previous_state: Schema.String,
7901
+ current_state: Schema.String,
7902
+ previous_block: Schema.optional(Schema.Number),
7903
+ current_block: Schema.optional(Schema.Number),
7904
+ logical_event_id: Schema.optional(Schema.String),
7905
+ scope_kind: Schema.optional(Schema.Literals(["account", "organization"])),
7906
+ account_id: OptionalAccountId,
7907
+ reference_id: OptionalString
7908
+ });
7817
7909
  Schema.Struct({
7818
7910
  name: Schema.Literal("auth_otp_requested"),
7819
7911
  props: AuthOtpRequestedProps
@@ -8018,9 +8110,12 @@ Schema.Struct({
8018
8110
  name: Schema.Literal("movement_scan_incident"),
8019
8111
  props: MovementScanIncidentProps
8020
8112
  });
8113
+ Schema.Struct({
8114
+ name: Schema.Literal("indexer_drift_corrected"),
8115
+ props: IndexerDriftCorrectedProps
8116
+ });
8021
8117
  /** The one spelling of "whose scope did this happen in" across every surface. */
8022
8118
  const ScopeKindSchema = Schema.Literals(["account", "organization"]);
8023
- const AccountKindSchema = Schema.Literals(["personalSafe", "orgTreasury"]);
8024
8119
  const OptionalReasonCode = Schema.optional(Schema.Literals(CAPXUL_ERROR_CODES));
8025
8120
  const ActivityReferenceProps = {
8026
8121
  reference_kind: Schema.Literals(["payment", "movement"]),
@@ -8085,6 +8180,20 @@ const ContactActionProps = {
8085
8180
  party_id: OptionalString,
8086
8181
  reason_code: OptionalReasonCode
8087
8182
  };
8183
+ Schema.Struct({
8184
+ name: Schema.Literal("cli_diagnostic_completed"),
8185
+ props: Schema.Struct({
8186
+ ...TelemetryEnvelopeProps,
8187
+ outcome: Schema.Literals([
8188
+ "succeeded",
8189
+ "refused",
8190
+ "failed",
8191
+ "cancelled"
8192
+ ]),
8193
+ invocation_id: Schema.String.check(Schema.isPattern(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u)),
8194
+ cli_version: Schema.String.check(Schema.isPattern(/^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/u))
8195
+ })
8196
+ });
8088
8197
  Schema.Struct({
8089
8198
  name: Schema.Literal("ui_contact_action_completed"),
8090
8199
  props: Schema.Union([
@@ -8118,6 +8227,16 @@ Schema.Struct({
8118
8227
  Schema.Struct({
8119
8228
  name: Schema.Literal("ui_activity_action_completed"),
8120
8229
  props: Schema.Union([
8230
+ Schema.Struct({
8231
+ ...ActivityActionProps,
8232
+ action: Schema.Literal("read"),
8233
+ outcome: Schema.Literals(["partial_displayed", "recovered"]),
8234
+ surface: Schema.Literals([
8235
+ "transactions",
8236
+ "recent",
8237
+ "summary"
8238
+ ])
8239
+ }),
8121
8240
  Schema.Struct({
8122
8241
  ...ActivityActionProps,
8123
8242
  ...ActivityFilterProps,
@@ -8334,6 +8453,12 @@ Schema.Struct({
8334
8453
  "settled"
8335
8454
  ]),
8336
8455
  run_id: Schema.String,
8456
+ /**
8457
+ * The `paymentCommands` row at `payrollRuns.batchRef` — the durable owner of
8458
+ * this run's Payment membership. `payment_count` counts THAT command's
8459
+ * `paymentIds`, so an unrelated Payment quoting the run id changes neither.
8460
+ */
8461
+ command_id: OptionalString,
8337
8462
  payment_count: Schema.optional(Schema.Number),
8338
8463
  occurred_at: Schema.Number
8339
8464
  })
@@ -8359,6 +8484,71 @@ function redactTelemetryProps(name, props, options = {}) {
8359
8484
  for (const key of PII_PROP_KEYS_BY_EVENT[name] ?? []) redactProperty(clone, key);
8360
8485
  return clone;
8361
8486
  }
8487
+ /** In unstructured bodies, preserve text around explicitly named credentials. */
8488
+ const NETWORK_FIELD = /((?:"?)([A-Za-z_][A-Za-z0-9_.-]*)(?:"?)\s*[:=]\s*)("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|[^\s,;&}]+)/gu;
8489
+ function maskNetworkText(text, context) {
8490
+ return redactSecrets(text).replace(NETWORK_FIELD, (whole, prefix, name, value) => {
8491
+ if (!isCredentialField(name, context)) return whole;
8492
+ const quote = value.startsWith("\"") ? "\"" : value.startsWith("'") ? "'" : "";
8493
+ return `${prefix}${quote}[REDACTED]${quote}`;
8494
+ });
8495
+ }
8496
+ function maskNetworkHeaders(headers) {
8497
+ if (headers === void 0) return void 0;
8498
+ const masked = {};
8499
+ for (const [name, value] of Object.entries(headers)) masked[name] = isCredentialField(name, "header") ? "[REDACTED]" : /^(?:location|content-location|referer)$/iu.test(name) ? redactUrlSecrets(value) : maskNetworkText(value);
8500
+ return masked;
8501
+ }
8502
+ function maskJsonValue(value, depth = 0, context) {
8503
+ if (typeof value === "function" || typeof value === "symbol") return "[UNREADABLE]";
8504
+ if (typeof value === "string") return maskNetworkText(value, context);
8505
+ if (typeof value !== "object" || value === null) return value;
8506
+ if (depth >= 16) return "[REDACTED]";
8507
+ if (Array.isArray(value)) return value.map((item) => maskJsonValue(item, depth + 1, context));
8508
+ const masked = Object.create(null);
8509
+ for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(value))) {
8510
+ if (!descriptor.enumerable) continue;
8511
+ masked[key] = isCredentialField(key, context) ? "[REDACTED]" : !("value" in descriptor) ? "[UNREADABLE]" : typeof descriptor.value === "string" && isPostHogUrlProperty(key) ? redactUrlSecrets(descriptor.value) : maskJsonValue(descriptor.value, depth + 1, context);
8512
+ }
8513
+ return masked;
8514
+ }
8515
+ function maskNetworkBody(body, context) {
8516
+ try {
8517
+ if (typeof body !== "string") return maskJsonValue(body, 0, context);
8518
+ let parsed;
8519
+ try {
8520
+ parsed = JSON.parse(body);
8521
+ } catch {
8522
+ if (/^[^=&\s]+=[\s\S]*$/u.test(body)) return new URLSearchParams(Array.from(new URLSearchParams(body), ([name, value]) => [name, isCredentialField(name, context) ? "[REDACTED]" : maskNetworkText(value)])).toString();
8523
+ return maskNetworkText(body, context);
8524
+ }
8525
+ return JSON.stringify(maskJsonValue(parsed, 0, context));
8526
+ } catch {
8527
+ return "[UNREADABLE]";
8528
+ }
8529
+ }
8530
+ /** Keep request evidence while masking credential values at the browser boundary. */
8531
+ function maskCapturedNetworkRequest(request) {
8532
+ const requestUrl = request.name ?? request.url ?? "";
8533
+ let context;
8534
+ try {
8535
+ const path = new URL(requestUrl, "https://capxul.invalid").pathname;
8536
+ if (/\/(?:auth|oauth|sign-in|signin|login|verify-otp|verify-email)(?:\/|$)/iu.test(path)) context = "authentication";
8537
+ } catch {}
8538
+ return {
8539
+ ...request,
8540
+ ...request.name === void 0 ? {} : { name: redactUrlSecrets(request.name) },
8541
+ ...request.url === void 0 ? {} : { url: redactUrlSecrets(request.url) },
8542
+ requestHeaders: maskNetworkHeaders(request.requestHeaders),
8543
+ responseHeaders: maskNetworkHeaders(request.responseHeaders),
8544
+ requestBody: maskNetworkBody(request.requestBody, context),
8545
+ responseBody: maskNetworkBody(request.responseBody)
8546
+ };
8547
+ }
8548
+ function isPostHogUrlProperty(key) {
8549
+ const normalized = key.toLowerCase();
8550
+ return normalized.includes("url") || normalized.includes("referrer") || normalized.includes("path") || normalized.includes("filename");
8551
+ }
8362
8552
  const PII_PROP_KEYS_BY_EVENT = {
8363
8553
  auth_otp_requested: ["email"],
8364
8554
  auth_otp_delivered: ["email"],
@@ -8713,6 +8903,7 @@ const CAPXUL_FUNCTIONS = {
8713
8903
  list: "movement/activity:list",
8714
8904
  summary: "movement/activity:summary"
8715
8905
  },
8906
+ "movement/annotations": { overlay: "movement/annotations:overlay" },
8716
8907
  "moneyExecution/actions": {
8717
8908
  abandonPaymentExecution: "moneyExecution/actions:abandonPaymentExecution",
8718
8909
  preparePermissionExecution: "moneyExecution/actions:preparePermissionExecution",
@@ -8790,7 +8981,10 @@ const CAPXUL_FUNCTIONS = {
8790
8981
  isUserOperationReceiptHeld: "e2eControls:isUserOperationReceiptHeld",
8791
8982
  releaseUserOperationReceipt: "e2eControls:releaseUserOperationReceipt"
8792
8983
  },
8793
- system: { health: "system:health" }
8984
+ system: {
8985
+ health: "system:health",
8986
+ healthObserved: "system:healthObserved"
8987
+ }
8794
8988
  };
8795
8989
  //#endregion
8796
8990
  //#region ../../packages/wire/src/status.ts
@@ -9311,7 +9505,19 @@ const StoredPermissionCommandSchema = Schema.Struct({
9311
9505
  roleKey: RoleKeySchema,
9312
9506
  allowanceKey: Schema.NullOr(AllowanceKeySchema),
9313
9507
  memberSafe: Schema.NullOr(SafeAddressSchema),
9314
- expectedAuthorityEvents: Schema.Array(Bytes32Schema)
9508
+ expectedAuthorityEvents: Schema.Array(Bytes32Schema),
9509
+ /**
9510
+ * #1961 R03. The authenticated user who created the command, and the browser
9511
+ * linkage captured with it. Delayed verification and bounded retries read
9512
+ * this snapshot instead of the current viewer, so one command keeps one
9513
+ * origin. Both are absent on commands created before this contract.
9514
+ *
9515
+ * The context stays `unknown` on purpose: `sanitizeObservationContext` owns
9516
+ * the field allowlist, and every reader passes the stored value through it
9517
+ * rather than repeating those rules here.
9518
+ */
9519
+ originatingAuthUserId: Schema.optional(Schema.String),
9520
+ originatingObservationContext: Schema.optional(Schema.Unknown)
9315
9521
  });
9316
9522
  Schema.Struct({
9317
9523
  command: StoredPermissionCommandSchema,
@@ -9471,7 +9677,9 @@ const ExpectedPaymentCommonSchema = Schema.Struct({
9471
9677
  chainId: BaseSepoliaChainIdSchema,
9472
9678
  contractAddress: AddressSchema,
9473
9679
  userOpHash: Bytes32Schema,
9474
- userOpSenderSafe: SafeAddressSchema
9680
+ userOpSenderSafe: SafeAddressSchema,
9681
+ /** Retained receipt location only; it cannot replace UserOperation or financial proof. */
9682
+ recordedTransactionHash: Schema.optional(Bytes32Schema)
9475
9683
  });
9476
9684
  Schema.Union([
9477
9685
  Schema.Struct({
@@ -9589,6 +9797,13 @@ Schema.Struct({
9589
9797
  transfers: Schema.Array(TransferEvidenceSchema),
9590
9798
  requestCount: nonNegativeInteger("requestCount")
9591
9799
  });
9800
+ /**
9801
+ * `chainId:txHash:transactionLogOrdinal` — the indexer's own row id. The
9802
+ * ordinal is the position in the transaction's receipt logs, never the
9803
+ * block-global index that `eventLogIndex` carries.
9804
+ */
9805
+ const LOGICAL_EVENT_ID_RE = /^[1-9][0-9]{0,9}:0x[0-9a-f]{64}:(0|[1-9][0-9]{0,5})$/u;
9806
+ const LogicalEventIdSchema = Schema.String.pipe(Schema.refine((value) => LOGICAL_EVENT_ID_RE.test(value), { message: "must be chainId:txHash:transactionLogOrdinal" }));
9592
9807
  const VerifiedMoneyEvidenceCommonSchema = Schema.Struct({
9593
9808
  chainId: BaseSepoliaChainIdSchema,
9594
9809
  contractAddress: AddressSchema,
@@ -9597,7 +9812,18 @@ const VerifiedMoneyEvidenceCommonSchema = Schema.Struct({
9597
9812
  blockNumber: BlockNumberSchema,
9598
9813
  blockHash: Bytes32Schema,
9599
9814
  blockTimestamp: Uint48Schema,
9600
- eventLogIndex: LogIndexSchema
9815
+ /**
9816
+ * The block-global log index of the settled event, as
9817
+ * `eth_getTransactionReceipt` reports it. Both readers key on this value, and
9818
+ * the ledger de-duplicates a settlement on `(txHash, eventLogIndex)`.
9819
+ */
9820
+ eventLogIndex: LogIndexSchema,
9821
+ /**
9822
+ * Stable chain event identity. Ponder supplies its row id; the receipt-backed
9823
+ * direct-send reader derives the same ordinal from the complete transaction
9824
+ * receipt. Legacy and remaining Commitment readers can omit this field.
9825
+ */
9826
+ logicalEventId: Schema.optional(LogicalEventIdSchema)
9601
9827
  });
9602
9828
  const VerifiedPaymentEvidenceSchema = Schema.Struct({
9603
9829
  ...VerifiedMoneyEvidenceCommonSchema.fields,
@@ -11145,7 +11371,9 @@ function verifiedOperationFields(attributes) {
11145
11371
  for (const [key, allowed] of [
11146
11372
  ["scope_kind", ["account", "organization"]],
11147
11373
  ["account_kind", ["personalSafe", "orgTreasury"]],
11148
- ["network", ["base-sepolia"]]
11374
+ ["network", ["base-sepolia"]],
11375
+ ["read_source", ["indexer", "convex"]],
11376
+ ["read_warning", ["indexer-unavailable"]]
11149
11377
  ]) {
11150
11378
  const value = attributes.get(key);
11151
11379
  if (typeof value === "string" && allowed.some((candidate) => candidate === value)) domain[key] = value;
@@ -11829,31 +12057,31 @@ function makeActorRelationshipMethods(deps) {
11829
12057
  reference: input.reference,
11830
12058
  ...input.memo === void 0 ? {} : { memo: input.memo },
11831
12059
  ...input.expiresAt === void 0 ? {} : { expiresAt: input.expiresAt }
11832
- }))), (request) => mapActorRequest(request, input.payer)), controls, CAPXUL_OPERATIONS.requests.issue, deps.runPromise);
12060
+ }))), (request) => mapActorRequest(request, input.payer)).pipe(Effect.tap(() => observeContactScope)), controls, CAPXUL_OPERATIONS.requests.issue, deps.runPromise);
11833
12061
  },
11834
12062
  list: (options) => {
11835
12063
  const controls = deps.invocationControls?.(options) ?? options;
11836
- return runPortEffect(mapReply(CAPXUL_OPERATIONS.requests.list, Effect.suspend(() => convexCall.query(fns.requestsList, copyInvocationObservation(controls, { actor }))), (requests) => requests.map((request) => mapActorRequest(request))), controls, CAPXUL_OPERATIONS.requests.list, deps.runPromise);
12064
+ return runPortEffect(mapReply(CAPXUL_OPERATIONS.requests.list, Effect.suspend(() => convexCall.query(fns.requestsList, copyInvocationObservation(controls, { actor }))), (requests) => requests.map((request) => mapActorRequest(request))).pipe(Effect.tap(() => observeContactScope)), controls, CAPXUL_OPERATIONS.requests.list, deps.runPromise);
11837
12065
  },
11838
12066
  get: (requestId, options) => {
11839
12067
  const controls = deps.invocationControls?.(options) ?? options;
11840
12068
  return runPortEffect(mapReply(CAPXUL_OPERATIONS.requests.get, Effect.suspend(() => convexCall.query(fns.requestsGet, copyInvocationObservation(controls, {
11841
12069
  actor,
11842
12070
  paymentRequestId: requestId
11843
- }))), (request) => request === null ? null : mapActorRequest(request)), controls, CAPXUL_OPERATIONS.requests.get, deps.runPromise);
12071
+ }))), (request) => request === null ? null : mapActorRequest(request)).pipe(Effect.tap(() => observeContactScope)), controls, CAPXUL_OPERATIONS.requests.get, deps.runPromise);
11844
12072
  },
11845
12073
  cancel: (requestId, options) => {
11846
12074
  const controls = deps.invocationControls?.(options) ?? options;
11847
12075
  return runPortEffect(mapReply(CAPXUL_OPERATIONS.requests.cancel, Effect.suspend(() => convexCall.mutation(fns.requestsCancel, copyInvocationObservation(controls, {
11848
12076
  actor,
11849
12077
  paymentRequestId: requestId
11850
- }))), (request) => mapActorRequest(request)), controls, CAPXUL_OPERATIONS.requests.cancel, deps.runPromise);
12078
+ }))), (request) => mapActorRequest(request)).pipe(Effect.tap(() => observeContactScope)), controls, CAPXUL_OPERATIONS.requests.cancel, deps.runPromise);
11851
12079
  }
11852
12080
  },
11853
12081
  inbox: {
11854
12082
  list: (options) => {
11855
12083
  const controls = deps.invocationControls?.(options) ?? options;
11856
- return runPortEffect(mapReply(CAPXUL_OPERATIONS.inbox.list, Effect.suspend(() => convexCall.query(fns.inboxList, copyInvocationObservation(controls, { actor }))), (items) => items.map(mapInboxItem)), controls, CAPXUL_OPERATIONS.inbox.list, deps.runPromise);
12084
+ return runPortEffect(mapReply(CAPXUL_OPERATIONS.inbox.list, Effect.suspend(() => convexCall.query(fns.inboxList, copyInvocationObservation(controls, { actor }))), (items) => items.map(mapInboxItem)).pipe(Effect.tap(() => observeContactScope)), controls, CAPXUL_OPERATIONS.inbox.list, deps.runPromise);
11857
12085
  },
11858
12086
  approve: (input, options) => {
11859
12087
  const controls = deps.invocationControls?.(options) ?? options;
@@ -11865,14 +12093,14 @@ function makeActorRelationshipMethods(deps) {
11865
12093
  }))).pipe(Effect.flatMap((prepared) => {
11866
12094
  const result = mapApprovedInboxPayment(prepared, input);
11867
12095
  return result.ok ? Effect.succeed(result.value) : Effect.fail({ publicError: result.error });
11868
- })), controls, CAPXUL_OPERATIONS.inbox.approve, deps.runPromise);
12096
+ }), Effect.tap(() => observeContactScope)), controls, CAPXUL_OPERATIONS.inbox.approve, deps.runPromise);
11869
12097
  },
11870
12098
  decline: (requestId, options) => {
11871
12099
  const controls = deps.invocationControls?.(options) ?? options;
11872
12100
  return runPortEffect(mapReply(CAPXUL_OPERATIONS.inbox.decline, Effect.suspend(() => convexCall.mutation(fns.inboxDecline, copyInvocationObservation(controls, {
11873
12101
  actor,
11874
12102
  paymentRequestId: requestId
11875
- }))), mapInboxItem), controls, CAPXUL_OPERATIONS.inbox.decline, deps.runPromise);
12103
+ }))), mapInboxItem).pipe(Effect.tap(() => observeContactScope)), controls, CAPXUL_OPERATIONS.inbox.decline, deps.runPromise);
11876
12104
  }
11877
12105
  },
11878
12106
  profile: {
@@ -12361,6 +12589,7 @@ const financialOpsContract = {
12361
12589
  activitySummary: makeFunctionReference(CAPXUL_FUNCTIONS["movement/activity"].summary),
12362
12590
  activityGet: makeFunctionReference(CAPXUL_FUNCTIONS["movement/activity"].get),
12363
12591
  activityAnnotate: makeFunctionReference(CAPXUL_FUNCTIONS["movement/activity"].annotate),
12592
+ activityOverlay: makeFunctionReference(CAPXUL_FUNCTIONS["movement/annotations"].overlay),
12364
12593
  verifyPaymentDocument: makeFunctionReference(CAPXUL_FUNCTIONS["financialOps/queries"].verifyPaymentDocument),
12365
12594
  renderStoredDocument: makeFunctionReference(CAPXUL_FUNCTIONS["financialOps/queries"].renderStoredDocument)
12366
12595
  };
@@ -12377,8 +12606,383 @@ const moneyExecutionContract = {
12377
12606
  submitPaymentCommandExecution: makeFunctionReference(CAPXUL_FUNCTIONS["moneyExecution/paymentCommandActions"].submitPaymentCommandExecution)
12378
12607
  };
12379
12608
  //#endregion
12609
+ //#region ../../packages/sdk/src/ports/indexer-read.ts
12610
+ /** The indexer's own page bound. Exceeding it is a caller defect, not a refusal. */
12611
+ const INDEXER_MAX_LIMIT = 1e3;
12612
+ const LOWER_CASE_ADDRESS = /^0x[0-9a-f]{40}$/;
12613
+ /**
12614
+ * Refuse a page the indexer would refuse, before any implementation spends a
12615
+ * round trip on it. Every implementation calls this, so a consumer test written
12616
+ * against the hermetic adapter cannot pass on input production rejects. Throws
12617
+ * a `CapxulError`; callers convert it to an `IndexerReadError`.
12618
+ */
12619
+ function assertIndexerPage(input) {
12620
+ const safe = String(input.safe).toLowerCase();
12621
+ if (!LOWER_CASE_ADDRESS.test(safe)) throw Errors.invalidInput("safe", "must be a lower-case EVM address");
12622
+ 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}`);
12623
+ const offset = input.offset ?? 0;
12624
+ if (!Number.isSafeInteger(offset) || offset < 0) throw Errors.invalidInput("offset", "must be a non-negative integer");
12625
+ return safe;
12626
+ }
12627
+ var IndexerReadError = class extends Data.TaggedError("IndexerReadError") {};
12628
+ function indexerReadErrorFromCapxul(operation, error, cause = error) {
12629
+ return new IndexerReadError({
12630
+ operation,
12631
+ publicCode: error.code,
12632
+ publicError: error,
12633
+ cause,
12634
+ ...error.details === void 0 ? {} : { details: error.details }
12635
+ });
12636
+ }
12637
+ Context.Service()("@capxul/sdk/ports/IndexerReadPort");
12638
+ //#endregion
12639
+ //#region ../../packages/sdk/src/surface/activity-stitch.ts
12640
+ /** Epoch milliseconds of the block that carried the row. */
12641
+ function blockMillis(row) {
12642
+ return Number(row.timestamp) * 1e3;
12643
+ }
12644
+ /** `0x1234…abcd` — the counterparty of an unclaimed transfer is an address. */
12645
+ function shortAddressLabel(address) {
12646
+ return address.length <= 12 ? address : `${address.slice(0, 6)}…${address.slice(-4)}`;
12647
+ }
12648
+ function directionFor(safe, transfer) {
12649
+ const from = transfer.from.toLowerCase();
12650
+ const to = transfer.to.toLowerCase();
12651
+ if (from === safe && to === safe) return "self";
12652
+ return to === safe ? "in" : "out";
12653
+ }
12654
+ /**
12655
+ * The Transfer a protocol Payment moved is the same money as its `payment`
12656
+ * row. One actor sees one item for it, so the Transfer is claimed and does not
12657
+ * become a second row. Another transfer in the same transaction that this
12658
+ * payment did not move stays unclaimed and keeps its own row.
12659
+ */
12660
+ function claimedTransferIds(transfers, payments) {
12661
+ const claimed = /* @__PURE__ */ new Set();
12662
+ for (const payment of payments) {
12663
+ 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());
12664
+ if (match !== void 0) claimed.add(match.id);
12665
+ }
12666
+ return claimed;
12667
+ }
12668
+ function withAnnotation(item, annotation) {
12669
+ return annotation?.counterpartyLabel === void 0 ? item : {
12670
+ ...item,
12671
+ counterpartyLabel: annotation.counterpartyLabel
12672
+ };
12673
+ }
12674
+ /**
12675
+ * An indexed settlement confirms the intent. A missing row can be outside
12676
+ * this page or still backfilling; only the correction owner can prove rollback.
12677
+ */
12678
+ function confirmPayment(intent, chain) {
12679
+ if (chain !== void 0) return {
12680
+ ...intent,
12681
+ status: "settled",
12682
+ receipt: {
12683
+ chainId: chain.chainId,
12684
+ transactionHash: chain.txHash
12685
+ },
12686
+ updatedAt: blockMillis(chain)
12687
+ };
12688
+ return intent;
12689
+ }
12690
+ /**
12691
+ * One unclaimed Transfer as an Activity row, or `null` when this SDK cannot
12692
+ * denominate its asset. The indexer serves only configured assets, so a row
12693
+ * the registry does not know means the SDK and that deployment disagree; it is
12694
+ * dropped rather than shown with a guessed currency.
12695
+ */
12696
+ function transferItem(safe, transfer, annotation) {
12697
+ const asset = configuredMoneyAssetById(transfer.assetId);
12698
+ if (asset === null) return null;
12699
+ const direction = directionFor(safe, transfer);
12700
+ const counterparty = direction === "in" ? transfer.from : transfer.to;
12701
+ const at = blockMillis(transfer);
12702
+ return withAnnotation({
12703
+ kind: "movement",
12704
+ id: transfer.id,
12705
+ classification: "movement_only_outflow",
12706
+ amount: {
12707
+ currency: asset.peg ?? asset.symbol,
12708
+ value: formatUnits(transfer.amount, asset.decimals),
12709
+ decimals: asset.decimals
12710
+ },
12711
+ direction,
12712
+ counterpartyLabel: shortAddressLabel(counterparty),
12713
+ receipt: {
12714
+ chainId: transfer.chainId,
12715
+ transactionHash: transfer.txHash
12716
+ },
12717
+ createdAt: at,
12718
+ updatedAt: at
12719
+ }, annotation);
12720
+ }
12721
+ /** Newest first. A Payment precedes a Movement at one instant, as the ledger reader orders them. */
12722
+ function compareItems(left, right) {
12723
+ if (right.createdAt !== left.createdAt) return right.createdAt - left.createdAt;
12724
+ if (left.kind !== right.kind) return left.kind === "payment" ? -1 : 1;
12725
+ if (left.id === right.id) return 0;
12726
+ return left.id < right.id ? 1 : -1;
12727
+ }
12728
+ function stitchActivity(input) {
12729
+ const safe = input.safe.toLowerCase();
12730
+ const chainBySettlement = /* @__PURE__ */ new Map();
12731
+ for (const row of input.payments) chainBySettlement.set(row.settlementId.toLowerCase(), row);
12732
+ const chainByPaymentId = /* @__PURE__ */ new Map();
12733
+ for (const link of input.links) {
12734
+ const row = chainBySettlement.get(link.settlementId.toLowerCase());
12735
+ if (row !== void 0) chainByPaymentId.set(link.paymentId, row);
12736
+ }
12737
+ const annotations = /* @__PURE__ */ new Map();
12738
+ for (const annotation of input.annotations) annotations.set(`${annotation.reference.kind}:${annotation.reference.id}`, annotation);
12739
+ const items = [];
12740
+ const claiming = [];
12741
+ for (const intent of input.intents) {
12742
+ if (intent.kind !== "payment") continue;
12743
+ const chain = chainByPaymentId.get(intent.id);
12744
+ if (chain !== void 0) claiming.push(chain);
12745
+ items.push(withAnnotation(confirmPayment(intent, chain), annotations.get(`payment:${intent.id}`)));
12746
+ }
12747
+ const claimed = claimedTransferIds(input.transfers, claiming);
12748
+ for (const transfer of input.transfers) {
12749
+ if (claimed.has(transfer.id)) continue;
12750
+ const item = transferItem(safe, transfer, annotations.get(`movement:${transfer.id}`));
12751
+ if (item !== null) items.push(item);
12752
+ }
12753
+ return items.sort(compareItems);
12754
+ }
12755
+ /** The reference ids one stitched page needs its Convex overlay for. */
12756
+ function overlayReferences(items) {
12757
+ return items.map((item) => ({
12758
+ kind: item.kind,
12759
+ id: item.id
12760
+ }));
12761
+ }
12762
+ /**
12763
+ * Money in, money out and the pending share, one bucket per currency and
12764
+ * precision. Two assets that peg to the same currency at different precision
12765
+ * stay distinct buckets: their raw units are not interchangeable.
12766
+ */
12767
+ function summarizeItems(items) {
12768
+ const buckets = /* @__PURE__ */ new Map();
12769
+ for (const item of items) {
12770
+ const key = `${item.amount.currency}:${String(item.amount.decimals)}`;
12771
+ const bucket = buckets.get(key) ?? {
12772
+ currency: item.amount.currency,
12773
+ decimals: item.amount.decimals,
12774
+ in: 0n,
12775
+ out: 0n,
12776
+ pendingIn: 0n
12777
+ };
12778
+ const raw = rawUnits(item.amount.value, item.amount.decimals);
12779
+ if (item.kind === "payment" && item.status !== "settled") {
12780
+ if (item.direction === "in" && item.status !== "cancelled" && item.status !== "failed") bucket.pendingIn += raw;
12781
+ } else if (item.direction === "in") bucket.in += raw;
12782
+ else if (item.direction === "out") bucket.out += raw;
12783
+ buckets.set(key, bucket);
12784
+ }
12785
+ return [...buckets.values()].map((bucket) => ({
12786
+ currency: bucket.currency,
12787
+ decimals: bucket.decimals,
12788
+ in: bucket.in.toString(),
12789
+ out: bucket.out.toString(),
12790
+ net: (bucket.in - bucket.out).toString(),
12791
+ pendingIn: bucket.pendingIn.toString()
12792
+ }));
12793
+ }
12794
+ /** The inverse of `formatUnits`, kept exact: `"1.25"` at 6 decimals is `1250000n`. */
12795
+ function rawUnits(value, decimals) {
12796
+ const negative = value.startsWith("-");
12797
+ const [whole = "0", fraction = ""] = (negative ? value.slice(1) : value).split(".");
12798
+ const raw = BigInt(`${whole}${fraction.padEnd(decimals, "0").slice(0, decimals)}`);
12799
+ return negative ? -raw : raw;
12800
+ }
12801
+ //#endregion
12802
+ //#region ../../packages/sdk/src/surface/activity-read.ts
12803
+ /** How many Convex intent rows one page reads for its labels and pending rows. */
12804
+ const INTENT_WINDOW = 100;
12805
+ /** movement/annotations:overlay bounds each identifier array to 200. */
12806
+ const MAX_OVERLAY_IDS = 200;
12807
+ /** The window a summary aggregates over. The indexer's own page bound. */
12808
+ const SUMMARY_WINDOW = INDEXER_MAX_LIMIT;
12809
+ function parseCursor(value) {
12810
+ if (value === void 0) return 0;
12811
+ try {
12812
+ const parsed = JSON.parse(value);
12813
+ return Number.isSafeInteger(parsed.offset) && parsed.offset >= 0 ? parsed.offset : 0;
12814
+ } catch {
12815
+ return 0;
12816
+ }
12817
+ }
12818
+ function inRange(item, range) {
12819
+ if (range?.from !== void 0 && item.createdAt < range.from) return false;
12820
+ return range?.to === void 0 || item.createdAt <= range.to;
12821
+ }
12822
+ /** The caller's filter applied to the merged page. The join owns the order. */
12823
+ function matchesFilter(item, filter) {
12824
+ if (filter === void 0) return true;
12825
+ if (filter.kind !== void 0 && item.kind !== filter.kind) return false;
12826
+ if (filter.direction !== void 0 && item.direction !== filter.direction) return false;
12827
+ if (filter.status === void 0) return true;
12828
+ return item.kind === "payment" && filter.status.includes(item.status);
12829
+ }
12830
+ function overlay(deps, actor, controls, input) {
12831
+ const count = Math.max(input.references?.length ?? 0, input.settlementIds?.length ?? 0);
12832
+ const offsets = Array.from({ length: Math.max(1, Math.ceil(count / MAX_OVERLAY_IDS)) }, (_, index) => index * MAX_OVERLAY_IDS);
12833
+ return Effect.forEach(offsets, (offset) => deps.convexCall.query(deps.functions.activityOverlay, copyInvocationObservation(controls, { input: {
12834
+ ...actor === void 0 ? {} : { actor },
12835
+ ...input.references === void 0 ? {} : { references: input.references.slice(offset, offset + MAX_OVERLAY_IDS) },
12836
+ ...input.settlementIds === void 0 ? {} : { settlementIds: input.settlementIds.slice(offset, offset + MAX_OVERLAY_IDS) }
12837
+ } })), { concurrency: 2 }).pipe(Effect.map((pages) => ({
12838
+ ...pages[0],
12839
+ links: pages.flatMap((page) => page.links),
12840
+ annotations: pages.flatMap((page) => page.annotations)
12841
+ })));
12842
+ }
12843
+ function convexPage(deps, actor, controls, input) {
12844
+ return deps.convexCall.query(deps.functions.activityList, copyInvocationObservation(controls, { input: {
12845
+ ...actor === void 0 ? {} : { actor },
12846
+ ...input.cursor === void 0 ? {} : { cursor: input.cursor },
12847
+ ...input.limit === void 0 ? {} : { limit: input.limit },
12848
+ ...input.range === void 0 ? {} : { range: input.range }
12849
+ } }));
12850
+ }
12851
+ /** Both indexed tables for one Safe, or `null` when the indexer cannot answer. */
12852
+ function chainRows(deps, safe, limit) {
12853
+ const page = {
12854
+ safe,
12855
+ limit: Math.min(limit, INDEXER_MAX_LIMIT)
12856
+ };
12857
+ return Effect.all([deps.indexer.read("transfer", page), deps.indexer.read("payment", page)], { concurrency: 2 }).pipe(Effect.map(([transfers, payments]) => ({
12858
+ transfers,
12859
+ payments
12860
+ })), Effect.catch(() => Effect.succeed(null)));
12861
+ }
12862
+ /** The stitched, filtered rows of one window, newest first. */
12863
+ function stitchedWindow(deps, actor, controls, window, params) {
12864
+ return Effect.gen(function* () {
12865
+ const scope = yield* overlay(deps, actor, controls, {});
12866
+ const convex = yield* convexPage(deps, actor, controls, {
12867
+ limit: INTENT_WINDOW,
12868
+ ...params.range === void 0 ? {} : { range: params.range }
12869
+ });
12870
+ const rows = scope.safe === null ? null : yield* chainRows(deps, scope.safe, window);
12871
+ if (rows === null || scope.safe === null) {
12872
+ if (scope.safe !== null) yield* Effect.annotateCurrentSpan({
12873
+ read_source: "convex",
12874
+ read_warning: "indexer-unavailable"
12875
+ });
12876
+ return {
12877
+ items: convex.items.filter((item) => matchesFilter(item, params.filter)),
12878
+ stitched: false,
12879
+ ...scope.safe === null ? {} : { readWarning: "indexer-unavailable" }
12880
+ };
12881
+ }
12882
+ yield* Effect.annotateCurrentSpan({ read_source: "indexer" });
12883
+ const decoration = yield* overlay(deps, actor, controls, {
12884
+ references: [...overlayReferences(convex.items), ...rows.transfers.map((row) => ({
12885
+ kind: "movement",
12886
+ id: row.id
12887
+ }))],
12888
+ settlementIds: rows.payments.map((row) => row.settlementId)
12889
+ });
12890
+ const intents = [...convex.items];
12891
+ const missing = new Set(decoration.links.map((link) => link.paymentId));
12892
+ for (const item of intents) if (item.kind === "payment") missing.delete(item.id);
12893
+ let cursor = convex.cursor;
12894
+ while (missing.size > 0 && cursor !== null) {
12895
+ const page = yield* convexPage(deps, actor, controls, {
12896
+ limit: INTENT_WINDOW,
12897
+ cursor,
12898
+ ...params.range === void 0 ? {} : { range: params.range }
12899
+ });
12900
+ for (const item of page.items) if (item.kind === "payment" && missing.delete(item.id)) intents.push(item);
12901
+ cursor = page.cursor;
12902
+ }
12903
+ const older = intents.slice(convex.items.length);
12904
+ const olderDecoration = older.length === 0 ? [] : (yield* overlay(deps, actor, controls, { references: overlayReferences(older) })).annotations;
12905
+ return {
12906
+ items: stitchActivity({
12907
+ safe: scope.safe,
12908
+ transfers: rows.transfers,
12909
+ payments: rows.payments,
12910
+ intents,
12911
+ links: decoration.links,
12912
+ annotations: [...decoration.annotations, ...olderDecoration]
12913
+ }).filter((item) => inRange(item, params.range) && matchesFilter(item, params.filter)),
12914
+ stitched: true
12915
+ };
12916
+ });
12917
+ }
12918
+ function readActivityPage(deps, actor, controls, params) {
12919
+ const limit = params?.limit ?? 25;
12920
+ const offset = parseCursor(params?.cursor);
12921
+ return stitchedWindow(deps, actor, controls, offset + limit + 1, {
12922
+ ...params?.range === void 0 ? {} : { range: params.range },
12923
+ ...params?.filter === void 0 ? {} : { filter: params.filter }
12924
+ }).pipe(Effect.map(({ items, stitched, readWarning }) => {
12925
+ const observedAt = Date.now();
12926
+ if (!stitched) return {
12927
+ items: items.slice(0, limit),
12928
+ cursor: null,
12929
+ checkpoint: observedAt,
12930
+ observedAt,
12931
+ ...readWarning === void 0 ? {} : { readWarning }
12932
+ };
12933
+ return {
12934
+ items: items.slice(offset, offset + limit),
12935
+ cursor: items.length > offset + limit ? JSON.stringify({ offset: offset + limit }) : null,
12936
+ checkpoint: observedAt,
12937
+ observedAt
12938
+ };
12939
+ }));
12940
+ }
12941
+ function readActivitySummary(deps, actor, controls, params) {
12942
+ return stitchedWindow(deps, actor, controls, SUMMARY_WINDOW, params?.window === void 0 ? {} : { range: params.window }).pipe(Effect.map(({ items, readWarning }) => ({
12943
+ window: {
12944
+ from: params?.window?.from ?? null,
12945
+ to: params?.window?.to ?? null
12946
+ },
12947
+ totals: summarizeItems(items),
12948
+ count: items.length,
12949
+ observedAt: Date.now(),
12950
+ ...readWarning === void 0 ? {} : { readWarning }
12951
+ })));
12952
+ }
12953
+ /**
12954
+ * Tell the caller when the indexed page for this Safe changed.
12955
+ *
12956
+ * The transport is the indexer's one SSE connection, wired in the Ponder
12957
+ * adapter under `activity.subscribe`. A reactive snapshot is not a product
12958
+ * event, so nothing here emits telemetry: the adapter reports a teardown, and
12959
+ * the caller re-reads.
12960
+ */
12961
+ function subscribeActivity(deps, actor, controls, onChange) {
12962
+ return Effect.gen(function* () {
12963
+ const scope = yield* overlay(deps, actor, controls, {});
12964
+ if (scope.safe === null) return () => {};
12965
+ const page = {
12966
+ safe: scope.safe,
12967
+ limit: INTENT_WINDOW
12968
+ };
12969
+ let delivered = false;
12970
+ const notify = () => {
12971
+ if (delivered) onChange();
12972
+ delivered = true;
12973
+ };
12974
+ return yield* deps.indexer.subscribe("transfer", page, (snapshot) => {
12975
+ if (snapshot.status === "ok") notify();
12976
+ if (snapshot.status === "error") {
12977
+ delivered = true;
12978
+ onChange();
12979
+ }
12980
+ });
12981
+ });
12982
+ }
12983
+ //#endregion
12380
12984
  //#region ../../packages/sdk/package.json
12381
- var version$1 = "4.2.0-rc.9";
12985
+ var version$1 = "4.20.0-beta.1";
12382
12986
  //#endregion
12383
12987
  //#region ../../packages/sdk/src/telemetry/exception-projection.ts
12384
12988
  /** Fixed fallback for failures that have no safe message. */
@@ -12480,10 +13084,15 @@ function observeFailedResult(result, adapter, operation, origin) {
12480
13084
  * A reactive stream is torn down after its call already returned, so nothing
12481
13085
  * downstream observes it. `captureException` is the right kind: the caller did
12482
13086
  * not ask for the end, so it is not an expected product outcome.
13087
+ *
13088
+ * A caller that DID ask for the end — the session it authenticated with
13089
+ * expired, and the service closed the stream on purpose — passes
13090
+ * `kind: "operation"` instead, so the refusal lands in the expected-outcome
13091
+ * stream its code already declares.
12483
13092
  */
12484
- function observeStreamFailure(adapter, operation, cause) {
13093
+ function observeStreamFailure(adapter, operation, cause, kind = "exception") {
12485
13094
  if (adapter === void 0) return;
12486
- report(adapter, "exception", operation, cause, resolveAdapterSnapshot(adapter));
13095
+ report(adapter, kind, operation, cause, resolveAdapterSnapshot(adapter));
12487
13096
  }
12488
13097
  /**
12489
13098
  * One failure, one event. The identity machine reports a failed transition
@@ -13475,6 +14084,11 @@ function paymentExecutionIntent(input) {
13475
14084
  }
13476
14085
  function makeFinancialOpsMethods(deps) {
13477
14086
  const fns = deps.functions ?? financialOpsContract;
14087
+ const indexerDeps = deps.indexerRead === void 0 ? void 0 : {
14088
+ indexer: deps.indexerRead,
14089
+ convexCall: deps.convexCall,
14090
+ functions: fns
14091
+ };
13478
14092
  const executionFns = deps.moneyExecutionFunctions ?? moneyExecutionContract;
13479
14093
  const requestKeyScope = deps.requestKeyScope ?? randomPaymentRequestKey();
13480
14094
  const inFlightPersonalPayments = /* @__PURE__ */ new Map();
@@ -13621,10 +14235,12 @@ function makeFinancialOpsMethods(deps) {
13621
14235
  return runPortEffect(Effect.suspend(() => deps.convexCall.query(fns.depositInstructions, copyInvocationObservation(controls, {}))).pipe(Effect.tap((instructions) => emitDepositInitiated(deps.telemetry, instructions, controls))), controls, CAPXUL_OPERATIONS.me.depositInstructions, deps.runPromise);
13622
14236
  }
13623
14237
  },
13624
- targets: { resolve: async (reference, options) => {
13625
- const recipient = normalizeTargetForBackend(reference, "reference");
13626
- if (!recipient.ok) return recipient;
13627
- return mapOk(await runIfActive(options?.signal, CAPXUL_OPERATIONS.recipient.resolve, () => deps.convexCall.query(fns.resolveRecipient, { recipient: recipient.value })), resolvedTargetFromResolution);
14238
+ targets: { resolve: (reference, options) => {
14239
+ const controls = deps.invocationControls?.(options) ?? options;
14240
+ return runPortEffect(projectReply(Effect.suspend(() => {
14241
+ const recipient = normalizeTargetForBackend(reference, "reference");
14242
+ return recipient.ok ? deps.convexCall.query(fns.resolveRecipient, copyInvocationObservation(controls, { recipient: recipient.value })) : Effect.fail({ publicError: recipient.error });
14243
+ }), resolvedTargetFromResolution), controls, CAPXUL_OPERATIONS.recipient.resolve, deps.runPromise);
13628
14244
  } },
13629
14245
  destinations: {
13630
14246
  add: (input, options) => {
@@ -13687,8 +14303,14 @@ function makeFinancialOpsMethods(deps) {
13687
14303
  kind: "claim",
13688
14304
  paymentId
13689
14305
  }, deps.invocationControls?.(options) ?? options, CAPXUL_OPERATIONS.payments.claim),
13690
- list: async (options) => mapOk(await runIfActive(options?.signal, CAPXUL_OPERATIONS.payments.list, () => deps.convexCall.query(fns.listPayments, {})), (payments) => payments.map(normalizePaymentTiming)),
13691
- get: async (paymentId, options) => mapOk(await runIfActive(options?.signal, CAPXUL_OPERATIONS.payments.get, () => deps.convexCall.query(fns.getPayment, { paymentId })), (payment) => payment === null ? null : normalizePaymentTiming(payment)),
14306
+ list: (options) => {
14307
+ const controls = deps.invocationControls?.(options) ?? options;
14308
+ return runPortEffect(projectReply(Effect.suspend(() => deps.convexCall.query(fns.listPayments, copyInvocationObservation(controls, {}))), (payments) => payments.map(normalizePaymentTiming)), controls, CAPXUL_OPERATIONS.payments.list, deps.runPromise);
14309
+ },
14310
+ get: (paymentId, options) => {
14311
+ const controls = deps.invocationControls?.(options) ?? options;
14312
+ return runPortEffect(projectReply(Effect.suspend(() => deps.convexCall.query(fns.getPayment, copyInvocationObservation(controls, { paymentId }))), (payment) => payment === null ? null : normalizePaymentTiming(payment)), controls, CAPXUL_OPERATIONS.payments.get, deps.runPromise);
14313
+ },
13692
14314
  cancel: (paymentId, options) => runLifecycle({
13693
14315
  kind: "cancel",
13694
14316
  paymentId
@@ -13705,22 +14327,26 @@ function makeFinancialOpsMethods(deps) {
13705
14327
  }
13706
14328
  },
13707
14329
  activity: {
14330
+ subscribe: (onChange, params, options) => {
14331
+ const controls = deps.invocationControls?.(options) ?? options;
14332
+ 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);
14333
+ },
13708
14334
  list: (params, options) => {
13709
14335
  const controls = deps.invocationControls?.(options) ?? options;
13710
- return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(params?.actor).actor)).pipe(Effect.flatMap((actor) => Effect.suspend(() => deps.convexCall.query(fns.activityList, copyInvocationObservation(controls, { input: {
14336
+ 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: {
13711
14337
  ...actor === void 0 ? {} : { actor },
13712
14338
  ...params?.cursor === void 0 ? {} : { cursor: params.cursor },
13713
14339
  ...params?.limit === void 0 ? {} : { limit: params.limit },
13714
14340
  ...params?.range === void 0 ? {} : { range: params.range },
13715
14341
  ...params?.filter === void 0 ? {} : { filter: params.filter }
13716
- } }))).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.list, deps.runPromise);
14342
+ } })) : readActivityPage(indexerDeps, actor, controls, params)).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.list, deps.runPromise);
13717
14343
  },
13718
14344
  summary: (params, options) => {
13719
14345
  const controls = deps.invocationControls?.(options) ?? options;
13720
- return runPortEffect(captureActivityInput(() => Effect.succeed(activityActorField(params?.actor).actor)).pipe(Effect.flatMap((actor) => Effect.suspend(() => deps.convexCall.query(fns.activitySummary, copyInvocationObservation(controls, { input: {
14346
+ 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: {
13721
14347
  ...actor === void 0 ? {} : { actor },
13722
14348
  ...params?.window === void 0 ? {} : { window: params.window }
13723
- } }))).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.summary, deps.runPromise);
14349
+ } })) : readActivitySummary(indexerDeps, actor, controls, params)).pipe(Effect.tap(() => observeActivityScope(actor))))), controls, CAPXUL_OPERATIONS.activity.summary, deps.runPromise);
13724
14350
  },
13725
14351
  get: (reference, options) => {
13726
14352
  const controls = deps.invocationControls?.(options) ?? options;
@@ -14052,6 +14678,22 @@ function normalizePaymentTiming(payment) {
14052
14678
  timing: payment.timing ?? payment.release ?? { kind: "instant" }
14053
14679
  };
14054
14680
  }
14681
+ /**
14682
+ * `mapOk`, moved inside the observed Effect (R09 #1967).
14683
+ *
14684
+ * The projection can refuse — `toAddress` and `toPartyId` throw on a malformed
14685
+ * reply — and every verb on this surface returns a `CapxulResult`, so that
14686
+ * throw stays a value here (ADR-0023). The error identity is `mapOk`'s: a
14687
+ * `CapxulError` passes through, anything else becomes `Errors.unknown(cause)`.
14688
+ * Running it inside the runner is what puts the decode failure on the same
14689
+ * operation record as the read that produced the reply.
14690
+ */
14691
+ function projectReply(program, project) {
14692
+ return program.pipe(Effect.flatMap((value) => Effect.try({
14693
+ try: () => project(value),
14694
+ catch: (cause) => ({ publicError: cause instanceof CapxulError ? cause : Errors.unknown(cause) })
14695
+ })));
14696
+ }
14055
14697
  function mapOk(result, f) {
14056
14698
  if (!result.ok) return result;
14057
14699
  try {
@@ -14423,13 +15065,18 @@ const postHogOtlpEndpoints = (host) => {
14423
15065
  const base = host.replace(/\/+$/, "");
14424
15066
  return {
14425
15067
  logs: `${base}/i/v1/logs`,
14426
- traces: `${base}/i/v1/traces`
15068
+ traces: `${base}/i/v1/traces`,
15069
+ metrics: `${base}/i/v1/metrics`
14427
15070
  };
14428
15071
  };
14429
15072
  const ENGINEERING_CAPXUL_ENV_SET = new Set(ENGINEERING_CAPXUL_ENVS);
14430
15073
  const ENGINEERING_PRODUCERS = /* @__PURE__ */ new Set(["browser", "server"]);
14431
15074
  const PUBLIC_POSTHOG_AUTHORIZATION = /^Bearer phc_[A-Za-z0-9_-]{1,191}$/u;
14432
15075
  const SAFE_RESOURCE_VALUE = /^[A-Za-z0-9][A-Za-z0-9._+@/-]{0,127}$/u;
15076
+ function validateEngineeringPrivacy(config) {
15077
+ if (config.privacy !== void 0 && config.privacy !== "minimal") throw new TypeError("engineering telemetry privacy must be minimal when specified");
15078
+ if (config.privacy === "minimal" && (!/^[A-Za-z0-9][A-Za-z0-9._+@-]{0,127}$/u.test(config.sdkVersion) || config.serviceName !== void 0 && !/^[A-Za-z0-9][A-Za-z0-9._+@-]{0,127}$/u.test(config.serviceName))) throw new TypeError("minimal engineering resource values must not contain paths");
15079
+ }
14433
15080
  const validateEngineeringTelemetryConfig = (config) => {
14434
15081
  let url;
14435
15082
  try {
@@ -14440,6 +15087,8 @@ const validateEngineeringTelemetryConfig = (config) => {
14440
15087
  if (url.protocol !== "https:" || url.username.length > 0 || url.password.length > 0 || url.pathname !== "/" || url.search.length > 0 || url.hash.length > 0 || !(url.hostname === "posthog.com" || url.hostname.endsWith(".posthog.com"))) throw new TypeError("engineering telemetry host must be a credential-free HTTPS origin");
14441
15088
  if (!ENGINEERING_CAPXUL_ENV_SET.has(config.capxulEnv)) throw new TypeError("engineering telemetry capxulEnv is not canonical");
14442
15089
  if (!ENGINEERING_PRODUCERS.has(config.producer)) throw new TypeError("engineering telemetry producer is not canonical");
15090
+ if (config.enabled !== void 0 && typeof config.enabled !== "boolean" && typeof config.enabled !== "function") throw new TypeError("engineering telemetry enabled must be a boolean or callback");
15091
+ validateEngineeringPrivacy(config);
14443
15092
  if (!SAFE_RESOURCE_VALUE.test(config.sdkVersion)) throw new TypeError("engineering telemetry sdkVersion must be a bounded safe value");
14444
15093
  if (config.serviceName !== void 0 && !SAFE_RESOURCE_VALUE.test(config.serviceName)) throw new TypeError("engineering telemetry serviceName must be a bounded safe value");
14445
15094
  const headerEntries = Object.entries(config.headers);
@@ -14490,25 +15139,34 @@ const spanUpstream = (span) => {
14490
15139
  let current = span;
14491
15140
  while (current?._tag === "Span") {
14492
15141
  const upstream = current.attributes.get("chain.upstream");
14493
- if (upstream === "alchemy" || upstream === "infura") return upstream;
15142
+ if (upstream === "alchemy" || upstream === "infura" || upstream === "gateway") return upstream;
14494
15143
  current = current.parent._tag === "Some" ? current.parent.value : void 0;
14495
15144
  }
14496
15145
  };
15146
+ function redactHttpPath(path, fullUrl, source) {
15147
+ let origin = "https://redaction.invalid";
15148
+ try {
15149
+ const url = new URL(String(fullUrl));
15150
+ if (url.protocol === "https:" || url.protocol === "http:") origin = url.origin;
15151
+ } catch {}
15152
+ const prefix = path.startsWith("/") ? origin : "";
15153
+ return redactUrlSecrets(`${prefix}${path}`, source).slice(prefix.length, prefix.length + 2048);
15154
+ }
14497
15155
  /** Preserve domain exits while preventing the OTLP serializer from seeing raw causes. */
14498
- const makeLeakSafeEngineeringTracer = (delegate) => Tracer.make({
15156
+ const makeLeakSafeEngineeringTracer = (delegate, enabled = () => true) => Tracer.make({
14499
15157
  span(options) {
14500
- const span = delegate.span(options);
15158
+ const span = enabled() ? delegate.span(options) : new Tracer.NativeSpan(options);
14501
15159
  const wrapped = Object.create(span);
14502
15160
  Object.defineProperty(wrapped, "attribute", { value: (name, value) => {
14503
15161
  const source = spanUpstream(span);
14504
15162
  const header = /^http\.(?:request|response)\.header\.(.+)$/u.exec(name)?.[1];
14505
15163
  if (header !== void 0 && isCredentialField(header, "header")) span.attribute(name, "[REDACTED]");
14506
15164
  else if (typeof value === "string" && name === "url.query") span.attribute(name, redactUrlSecrets(`?${value}`).slice(1, 2049));
14507
- else if (typeof value === "string" && name === "url.path") {
14508
- const fullUrl = span.attributes.get("url.full");
14509
- const path = typeof fullUrl === "string" ? /^https?:\/\/[^/]+([^?#]*)/u.exec(fullUrl)?.[1] ?? value : value;
14510
- span.attribute(name, redactUrlSecrets(path, source).slice(0, 2048));
14511
- } else if (typeof value === "string" && (name === "url.full" || header === "location" || header === "referer" || header === "referrer")) span.attribute(name, redactUrlSecrets(value, source).slice(0, 2048));
15165
+ else if (typeof value === "string" && (name === "url.path" || name === "http.target")) {
15166
+ const fullUrl = span.attributes.get("url.full") ?? span.attributes.get("http.url");
15167
+ const path = name === "url.path" && typeof fullUrl === "string" ? /^https?:\/\/[^/]+([^?#]*)/u.exec(fullUrl)?.[1] ?? value : value;
15168
+ span.attribute(name, redactHttpPath(path, fullUrl, source));
15169
+ } else if (typeof value === "string" && (name === "url.full" || name === "http.url" || header === "location" || header === "referer" || header === "referrer")) span.attribute(name, redactUrlSecrets(value, source).slice(0, 2048));
14512
15170
  else if (header !== void 0 && typeof value === "string") span.attribute(name, redactSecrets(value, source).slice(0, 2048));
14513
15171
  else span.attribute(name, value);
14514
15172
  } });
@@ -14516,6 +15174,15 @@ const makeLeakSafeEngineeringTracer = (delegate) => Tracer.make({
14516
15174
  configurable: false,
14517
15175
  enumerable: false,
14518
15176
  value: (endTime, exit) => {
15177
+ if (!enabled()) {
15178
+ Object.defineProperty(wrapped, "status", { value: {
15179
+ _tag: "Ended",
15180
+ startTime: span.status.startTime,
15181
+ endTime,
15182
+ exit
15183
+ } });
15184
+ return;
15185
+ }
14519
15186
  if (Exit.isFailure(exit) && !Cause.hasInterruptsOnly(exit.cause)) {
14520
15187
  const first = exit.cause.reasons.find((reason) => !Cause.isInterruptReason(reason));
14521
15188
  const code = failureText(first === void 0 ? void 0 : Cause.isFailReason(first) ? first.error : first.defect, "code", 128);
@@ -14529,53 +15196,360 @@ const makeLeakSafeEngineeringTracer = (delegate) => Tracer.make({
14529
15196
  },
14530
15197
  ...delegate.context === void 0 ? {} : { context: delegate.context.bind(delegate) }
14531
15198
  });
15199
+ const MINIMAL_LOG_NAMES = /* @__PURE__ */ new Set([
15200
+ "capxul_sdk_operation",
15201
+ "identity.transition",
15202
+ "convex.connection.changed",
15203
+ "convex.action.connection_lost",
15204
+ "cli.command.completed",
15205
+ "cli.sdk.operation.failed"
15206
+ ]);
15207
+ const MINIMAL_ERROR_CODES = /* @__PURE__ */ new Set([
15208
+ ...CAPXUL_ERROR_CODES,
15209
+ "CLI_USAGE",
15210
+ "CLI_CONFIG",
15211
+ "CLI_STORAGE",
15212
+ "CLI_DEFECT"
15213
+ ]);
15214
+ const MINIMAL_NUMBERS = /* @__PURE__ */ new Set([
15215
+ "schema_version",
15216
+ "duration_ms",
15217
+ "wait_duration_ms",
15218
+ "retry_count",
15219
+ "http.status_code",
15220
+ "http.response.status_code",
15221
+ "http_status",
15222
+ "rpc.code",
15223
+ "connection_count",
15224
+ "connection_retries"
15225
+ ]);
15226
+ const MINIMAL_CORRELATIONS = /* @__PURE__ */ new Set([
15227
+ "correlation_id",
15228
+ "journey_id",
15229
+ "invocation_id"
15230
+ ]);
15231
+ const MINIMAL_METRIC_LABELS = /* @__PURE__ */ new Set([
15232
+ "command",
15233
+ "outcome",
15234
+ "capxul_env",
15235
+ "unit"
15236
+ ]);
15237
+ const MINIMAL_METRIC_NAMES = /* @__PURE__ */ new Set(["capxul.cli.command.count", "capxul.cli.command.duration"]);
15238
+ const traceId = (value) => typeof value === "string" && /^(?!0+$)[0-9a-f]{32}$/u.test(value);
15239
+ const spanId = (value) => typeof value === "string" && /^(?!0+$)[0-9a-f]{16}$/u.test(value);
15240
+ const minimalName = (value) => typeof value === "string" && (MINIMAL_LOG_NAMES.has(value) || isCapxulOperation(value)) ? value : "capxul.engineering";
15241
+ const MINIMAL_ENUM_VALUES = /* @__PURE__ */ new Map([
15242
+ ["outcome", [
15243
+ "succeeded",
15244
+ "refused",
15245
+ "failed",
15246
+ "cancelled",
15247
+ "applied"
15248
+ ]],
15249
+ ["mode", FAILURE_MODES$1],
15250
+ ["failure_mode", FAILURE_MODES$1],
15251
+ ["capxul_env", ENGINEERING_CAPXUL_ENVS],
15252
+ ["producer", ["browser", "server"]],
15253
+ ["command", ["doctor"]],
15254
+ ["output_mode", ["human", "json"]],
15255
+ ["unit", [
15256
+ "1",
15257
+ "s",
15258
+ "ms"
15259
+ ]]
15260
+ ]);
15261
+ const MINIMAL_CODE_KEYS = /* @__PURE__ */ new Set([
15262
+ "error.code",
15263
+ "error_code",
15264
+ "failure_code",
15265
+ "refusal_code",
15266
+ "capxul_error_code"
15267
+ ]);
15268
+ const MINIMAL_VERSION_KEYS = /* @__PURE__ */ new Set([
15269
+ "sdk_version",
15270
+ "cli_version",
15271
+ "release"
15272
+ ]);
15273
+ function minimalStringValue(key, text) {
15274
+ if (key === "operation") return isCapxulOperation(text);
15275
+ if (MINIMAL_CODE_KEYS.has(key)) return MINIMAL_ERROR_CODES.has(text);
15276
+ if (MINIMAL_VERSION_KEYS.has(key)) return /^[A-Za-z0-9][A-Za-z0-9._+@-]{0,127}$/u.test(text);
15277
+ if (key === "trace_id") return traceId(text);
15278
+ if (key === "span_id" || key === "parent_span_id") return spanId(text);
15279
+ const values = MINIMAL_ENUM_VALUES.get(key);
15280
+ if (values !== void 0) return values.includes(text);
15281
+ return MINIMAL_CORRELATIONS.has(key) && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/u.test(text) && !/^0x[0-9a-f]+$/iu.test(text) && redactSecrets(text) === text;
15282
+ }
15283
+ /** A named privacy projection, separate from the default credential-only redaction policy. */
15284
+ function minimalAttributes(attributes, metric = false) {
15285
+ return attributes.flatMap(({ key, value }) => {
15286
+ if (metric && !MINIMAL_METRIC_LABELS.has(key)) return [];
15287
+ const text = value.stringValue;
15288
+ const number = value.doubleValue ?? value.intValue;
15289
+ if (MINIMAL_NUMBERS.has(key) && typeof number === "number" && Number.isFinite(number)) return [{
15290
+ key,
15291
+ value: { doubleValue: number }
15292
+ }];
15293
+ if (typeof text !== "string") return [];
15294
+ return minimalStringValue(key, text) ? [{
15295
+ key,
15296
+ value: { stringValue: text }
15297
+ }] : [];
15298
+ });
15299
+ }
15300
+ const finiteProtocolNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
15301
+ const protocolCount = (value, maximum = Number.MAX_SAFE_INTEGER) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0 && value <= maximum ? value : void 0;
15302
+ const protocolFlags = (value) => protocolCount(Object.getOwnPropertyDescriptor(value, "flags")?.value, 4294967295);
15303
+ function protocolTime(value) {
15304
+ if (typeof value === "string" && /^\d{1,20}$/u.test(value) && BigInt(value) <= 18446744073709551615n) return value;
15305
+ const number = protocolCount(value);
15306
+ if (number !== void 0) return String(number);
15307
+ if (value === null || typeof value !== "object") return void 0;
15308
+ const low = protocolCount(Object.getOwnPropertyDescriptor(value, "low")?.value, 4294967295);
15309
+ const high = protocolCount(Object.getOwnPropertyDescriptor(value, "high")?.value, 4294967295);
15310
+ return low === void 0 || high === void 0 ? void 0 : (BigInt(high) << 32n | BigInt(low)).toString();
15311
+ }
15312
+ function minimalNumberPoints(points) {
15313
+ return points.flatMap((point) => {
15314
+ const attributes = minimalAttributes(point.attributes, true);
15315
+ const asDouble = finiteProtocolNumber(point.asDouble);
15316
+ const asInt = typeof point.asInt === "number" && Number.isSafeInteger(point.asInt) ? point.asInt : void 0;
15317
+ if (attributes.length !== point.attributes.length || asDouble === void 0 && asInt === void 0) return [];
15318
+ return [{
15319
+ attributes,
15320
+ timeUnixNano: protocolTime(point.timeUnixNano),
15321
+ startTimeUnixNano: protocolTime(point.startTimeUnixNano),
15322
+ asDouble,
15323
+ asInt: asDouble === void 0 ? asInt : void 0,
15324
+ flags: protocolFlags(point)
15325
+ }];
15326
+ });
15327
+ }
15328
+ function minimalHistogramPoints(points) {
15329
+ return points.flatMap((point) => {
15330
+ const attributes = minimalAttributes(point.attributes ?? [], true);
15331
+ const count = protocolCount(point.count);
15332
+ const bucketCounts = point.bucketCounts;
15333
+ const explicitBounds = point.explicitBounds;
15334
+ if (attributes.length !== (point.attributes?.length ?? 0) || count === void 0 || !Array.isArray(bucketCounts) || !bucketCounts.every((value) => protocolCount(value) !== void 0) || !Array.isArray(explicitBounds) || !explicitBounds.every((value) => finiteProtocolNumber(value) !== void 0) || bucketCounts.length !== explicitBounds.length + 1) return [];
15335
+ return [{
15336
+ attributes,
15337
+ timeUnixNano: protocolTime(point.timeUnixNano),
15338
+ startTimeUnixNano: protocolTime(point.startTimeUnixNano),
15339
+ count,
15340
+ sum: finiteProtocolNumber(point.sum),
15341
+ min: finiteProtocolNumber(point.min),
15342
+ max: finiteProtocolNumber(point.max),
15343
+ bucketCounts: bucketCounts.map((value) => value),
15344
+ explicitBounds: explicitBounds.map((value) => value),
15345
+ flags: protocolFlags(point)
15346
+ }];
15347
+ });
15348
+ }
15349
+ function minimalSerialization(config) {
15350
+ const service = config.serviceName ?? "capxul-sdk";
15351
+ const resource = { attributes: Object.entries({
15352
+ "service.name": service,
15353
+ "service.version": config.sdkVersion,
15354
+ sdk_version: config.sdkVersion,
15355
+ capxul_env: config.capxulEnv,
15356
+ producer: config.producer
15357
+ }).map(([key, value]) => ({
15358
+ key,
15359
+ value: { stringValue: value }
15360
+ })) };
15361
+ return {
15362
+ logs: (data) => HttpBody.jsonUnsafe({ resourceLogs: data.resourceLogs.map((entry) => ({
15363
+ resource,
15364
+ scopeLogs: entry.scopeLogs.map((scope) => ({
15365
+ scope: { name: service },
15366
+ logRecords: (scope.logRecords ?? []).map((record) => ({
15367
+ timeUnixNano: protocolTime(record.timeUnixNano),
15368
+ observedTimeUnixNano: protocolTime(record.observedTimeUnixNano),
15369
+ severityNumber: protocolCount(record.severityNumber, 24),
15370
+ severityText: [
15371
+ "All",
15372
+ "Trace",
15373
+ "Debug",
15374
+ "Info",
15375
+ "Warn",
15376
+ "Error",
15377
+ "Fatal",
15378
+ "None"
15379
+ ].includes(record.severityText ?? "") ? record.severityText : void 0,
15380
+ body: { stringValue: minimalName(record.body?.stringValue) },
15381
+ attributes: minimalAttributes(record.attributes),
15382
+ droppedAttributesCount: protocolCount(record.droppedAttributesCount) ?? 0,
15383
+ traceId: traceId(record.traceId) ? record.traceId : void 0,
15384
+ spanId: spanId(record.spanId) ? record.spanId : void 0,
15385
+ flags: protocolFlags(record)
15386
+ }))
15387
+ }))
15388
+ })) }),
15389
+ traces: (data) => HttpBody.jsonUnsafe({ resourceSpans: data.resourceSpans.map((entry) => ({
15390
+ resource,
15391
+ scopeSpans: entry.scopeSpans.map((scope) => ({
15392
+ scope: { name: service },
15393
+ spans: scope.spans.filter((span) => traceId(span.traceId) && spanId(span.spanId)).map((span) => {
15394
+ const attributes = minimalAttributes(span.attributes);
15395
+ const code = attributes.find((attribute) => attribute.key === "error.code");
15396
+ return {
15397
+ traceId: span.traceId,
15398
+ spanId: span.spanId,
15399
+ name: minimalName(span.name),
15400
+ kind: protocolCount(span.kind, 5) ?? 0,
15401
+ startTimeUnixNano: protocolTime(span.startTimeUnixNano),
15402
+ endTimeUnixNano: protocolTime(span.endTimeUnixNano),
15403
+ attributes,
15404
+ droppedAttributesCount: protocolCount(span.droppedAttributesCount) ?? 0,
15405
+ parentSpanId: spanId(span.parentSpanId) ? span.parentSpanId : void 0,
15406
+ flags: protocolFlags(span),
15407
+ status: { code: protocolCount(span.status.code, 2) ?? 0 },
15408
+ events: span.events.slice(0, 8).map((event) => ({
15409
+ timeUnixNano: protocolTime(event.timeUnixNano),
15410
+ droppedAttributesCount: protocolCount(event.droppedAttributesCount) ?? 0,
15411
+ name: event.name === "exception" ? "exception" : minimalName(event.name),
15412
+ attributes: event.name === "exception" ? [{
15413
+ key: "exception.type",
15414
+ value: code?.value ?? { stringValue: "Error" }
15415
+ }] : minimalAttributes(event.attributes)
15416
+ })),
15417
+ droppedEventsCount: protocolCount(span.droppedEventsCount) ?? 0,
15418
+ links: span.links.filter((link) => traceId(link.traceId) && spanId(link.spanId)).slice(0, 8).map((link) => ({
15419
+ traceId: link.traceId,
15420
+ spanId: link.spanId,
15421
+ flags: protocolFlags(link),
15422
+ attributes: minimalAttributes(link.attributes),
15423
+ droppedAttributesCount: protocolCount(link.droppedAttributesCount) ?? 0
15424
+ })),
15425
+ droppedLinksCount: protocolCount(span.droppedLinksCount) ?? 0
15426
+ };
15427
+ })
15428
+ }))
15429
+ })) }),
15430
+ metrics: (data) => {
15431
+ const resourceMetrics = data.resourceMetrics.map((entry) => ({
15432
+ resource,
15433
+ scopeMetrics: entry.scopeMetrics.map((scope) => ({
15434
+ scope: { name: service },
15435
+ metrics: scope.metrics.filter((metric) => MINIMAL_METRIC_NAMES.has(metric.name)).flatMap((metric) => {
15436
+ const gauge = metric.gauge === void 0 ? void 0 : { dataPoints: minimalNumberPoints(metric.gauge.dataPoints) };
15437
+ const sum = metric.sum === void 0 ? void 0 : {
15438
+ aggregationTemporality: protocolCount(metric.sum.aggregationTemporality, 2) ?? 0,
15439
+ isMonotonic: metric.sum.isMonotonic === true,
15440
+ dataPoints: minimalNumberPoints(metric.sum.dataPoints)
15441
+ };
15442
+ const histogram = metric.histogram === void 0 ? void 0 : {
15443
+ aggregationTemporality: protocolCount(metric.histogram.aggregationTemporality, 2) ?? 0,
15444
+ dataPoints: minimalHistogramPoints(metric.histogram.dataPoints)
15445
+ };
15446
+ if (![
15447
+ gauge,
15448
+ sum,
15449
+ histogram
15450
+ ].some((series) => (series?.dataPoints.length ?? 0) > 0)) return [];
15451
+ return [{
15452
+ name: metric.name,
15453
+ unit: [
15454
+ "1",
15455
+ "s",
15456
+ "ms"
15457
+ ].includes(metric.unit ?? "") ? metric.unit : "1",
15458
+ ...gauge === void 0 ? {} : { gauge },
15459
+ ...sum === void 0 ? {} : { sum },
15460
+ ...histogram === void 0 ? {} : { histogram }
15461
+ }];
15462
+ })
15463
+ }))
15464
+ }));
15465
+ return resourceMetrics.some((entry) => entry.scopeMetrics.some((scope) => scope.metrics.length > 0)) ? HttpBody.jsonUnsafe({ resourceMetrics }) : HttpBody.empty;
15466
+ }
15467
+ };
15468
+ }
15469
+ Effect.flatMap(OtlpExporter.Flusher, (flusher) => flusher.flush);
14532
15470
  /** Shared browser/server OTLP layer. OtlpLogger merges with incumbent loggers once. */
14533
15471
  const makeEngineeringTelemetryLayer = (config) => {
14534
15472
  const validated = validateEngineeringTelemetryConfig(config);
14535
- const endpoints = postHogOtlpEndpoints(validated.host);
14536
- const resource = {
14537
- serviceName: validated.serviceName ?? "capxul-sdk",
14538
- serviceVersion: validated.sdkVersion,
14539
- attributes: {
14540
- capxul_env: validated.capxulEnv,
14541
- producer: validated.producer,
14542
- sdk_version: validated.sdkVersion
14543
- }
14544
- };
14545
- const tracing = Layer.effect(Tracer.Tracer, OtlpTracer.make({
14546
- url: endpoints.traces,
14547
- headers: validated.headers,
14548
- resource
14549
- }).pipe(Effect.map(makeLeakSafeEngineeringTracer))).pipe(Layer.provideMerge(OtlpExporter.layerFlusher));
14550
- const logging = OtlpLogger.layer({
14551
- url: endpoints.logs,
14552
- headers: validated.headers,
14553
- resource,
14554
- mergeWithExisting: true
14555
- });
14556
- const headerPolicy = Layer.merge(Layer.succeed(HttpClient.TracerHeaderFilter, traceHeaderFilter), Layer.succeed(Headers.CurrentRedactedNames, [credentialHeaderPattern]));
14557
- const browserLifecycle = Layer.effectDiscard(Effect.gen(function* () {
14558
- if (validated.producer !== "browser" || !hasBrowserDocument()) return;
14559
- const browserWindow = window;
14560
- const browserDocument = document;
14561
- const flusher = yield* OtlpExporter.Flusher;
14562
- const run = yield* FiberSet.makeRuntime();
14563
- const flush = () => {
14564
- run(flusher.flush);
14565
- };
14566
- const visibilityChanged = () => {
14567
- if (browserDocument.visibilityState === "hidden") flush();
14568
- };
14569
- yield* Effect.acquireRelease(Effect.sync(() => {
14570
- browserDocument.addEventListener("visibilitychange", visibilityChanged);
14571
- browserWindow.addEventListener("pagehide", flush);
14572
- }), () => Effect.sync(() => {
14573
- browserDocument.removeEventListener("visibilitychange", visibilityChanged);
14574
- browserWindow.removeEventListener("pagehide", flush);
15473
+ return Layer.suspend(() => {
15474
+ const endpoints = postHogOtlpEndpoints(validated.host);
15475
+ let disabled = false;
15476
+ const enabled = () => {
15477
+ if (disabled) return false;
15478
+ try {
15479
+ disabled = (typeof validated.enabled === "function" ? validated.enabled() : validated.enabled ?? true) !== true;
15480
+ } catch {
15481
+ disabled = true;
15482
+ }
15483
+ return !disabled;
15484
+ };
15485
+ const delivery = {
15486
+ ...validated.shutdownTimeout === void 0 ? {} : { shutdownTimeout: validated.shutdownTimeout },
15487
+ ...validated.exportInterval === void 0 ? {} : { exportInterval: validated.exportInterval }
15488
+ };
15489
+ const resource = {
15490
+ serviceName: validated.serviceName ?? "capxul-sdk",
15491
+ serviceVersion: validated.sdkVersion,
15492
+ attributes: {
15493
+ capxul_env: validated.capxulEnv,
15494
+ producer: validated.producer,
15495
+ sdk_version: validated.sdkVersion
15496
+ }
15497
+ };
15498
+ const tracing = Layer.effect(Tracer.Tracer, OtlpTracer.make({
15499
+ url: endpoints.traces,
15500
+ headers: validated.headers,
15501
+ resource,
15502
+ ...delivery
15503
+ }).pipe(Effect.map((tracer) => makeLeakSafeEngineeringTracer(tracer, enabled)))).pipe(Layer.provideMerge(OtlpExporter.layerFlusher));
15504
+ const logging = Logger.layer([OtlpLogger.make({
15505
+ url: endpoints.logs,
15506
+ headers: validated.headers,
15507
+ resource,
15508
+ ...delivery
15509
+ }).pipe(Effect.map((logger) => Logger.make((options) => {
15510
+ if (enabled()) logger.log(options);
15511
+ })))], { mergeWithExisting: true }).pipe(Layer.provideMerge(OtlpExporter.layerFlusher));
15512
+ const metrics = OtlpMetrics.layer({
15513
+ url: endpoints.metrics,
15514
+ headers: validated.headers,
15515
+ resource,
15516
+ temporality: "delta",
15517
+ ...delivery
15518
+ });
15519
+ const headerPolicy = Layer.merge(Layer.succeed(HttpClient.TracerHeaderFilter, traceHeaderFilter), Layer.succeed(Headers.CurrentRedactedNames, [credentialHeaderPattern]));
15520
+ const browserLifecycle = Layer.effectDiscard(Effect.gen(function* () {
15521
+ if (validated.producer !== "browser" || !hasBrowserDocument()) return;
15522
+ const browserWindow = window;
15523
+ const browserDocument = document;
15524
+ const flusher = yield* OtlpExporter.Flusher;
15525
+ const run = yield* FiberSet.makeRuntime();
15526
+ const flush = () => {
15527
+ run(flusher.flush);
15528
+ };
15529
+ const visibilityChanged = () => {
15530
+ if (browserDocument.visibilityState === "hidden") flush();
15531
+ };
15532
+ yield* Effect.acquireRelease(Effect.sync(() => {
15533
+ browserDocument.addEventListener("visibilitychange", visibilityChanged);
15534
+ browserWindow.addEventListener("pagehide", flush);
15535
+ }), () => Effect.sync(() => {
15536
+ browserDocument.removeEventListener("visibilitychange", visibilityChanged);
15537
+ browserWindow.removeEventListener("pagehide", flush);
15538
+ }));
14575
15539
  }));
14576
- }));
14577
- const transport = Layer.effect(HttpClient.HttpClient, Effect.map(HttpClient.HttpClient, (client) => validated.producer === "browser" && hasBrowserDocument() ? browserExportClient(client) : client)).pipe(Layer.provide(FetchHttpClient.layer));
14578
- return browserLifecycle.pipe(Layer.provideMerge(Layer.mergeAll(tracing, logging, headerPolicy)), Layer.provide(OtlpSerialization.layerJson), Layer.provide(transport));
15540
+ const transport = Layer.effect(HttpClient.HttpClient, Effect.map(HttpClient.HttpClient, (client) => {
15541
+ const selectedTransport = validated.producer === "browser" && hasBrowserDocument() ? browserExportClient(client) : client;
15542
+ return HttpClient.transform(selectedTransport, (response, request) => Effect.suspend(() => enabled() && request.body._tag !== "Empty" ? response : Effect.succeed(HttpClientResponse.fromWeb(request, new Response(null, { status: 200 })))));
15543
+ })).pipe(Layer.provide(FetchHttpClient.layer));
15544
+ const serialization = Layer.effect(OtlpSerialization.OtlpSerialization, Effect.map(OtlpSerialization.OtlpSerialization, (json) => {
15545
+ const projected = minimalSerialization(validated);
15546
+ return validated.privacy === "minimal" ? projected : {
15547
+ ...json,
15548
+ metrics: projected.metrics
15549
+ };
15550
+ })).pipe(Layer.provide(OtlpSerialization.layerJson));
15551
+ return browserLifecycle.pipe(Layer.provideMerge(Layer.mergeAll(tracing, logging, metrics, headerPolicy)), Layer.provide(serialization), Layer.provide(transport));
15552
+ });
14579
15553
  };
14580
15554
  //#endregion
14581
15555
  //#region ../../packages/sdk/src/ports/auth-client.ts
@@ -14900,9 +15874,16 @@ var BetterAuthBrowserAdapter = class {
14900
15874
  ok: false,
14901
15875
  error: Errors.notAuthenticated()
14902
15876
  };
15877
+ if (res.status === 429) return {
15878
+ ok: false,
15879
+ error: Errors.rateLimited({ resource: "auth.token" })
15880
+ };
14903
15881
  if (!res.ok) return {
14904
15882
  ok: false,
14905
- error: Errors.providerError("better-auth", "getConvexJwt", /* @__PURE__ */ new Error(`HTTP ${res.status}`))
15883
+ error: Errors.providerError("better-auth", "getConvexJwt", /* @__PURE__ */ new Error(`HTTP ${res.status}`), {
15884
+ httpStatus: res.status,
15885
+ ...res.status >= 500 ? { failure_mode: "upstream-down" } : {}
15886
+ })
14906
15887
  };
14907
15888
  const body = await safeJson$1(res);
14908
15889
  if (typeof body === "object" && body !== null) {
@@ -15331,9 +16312,16 @@ var BetterAuthNodeAdapter = class {
15331
16312
  ok: false,
15332
16313
  error: Errors.notAuthenticated()
15333
16314
  };
16315
+ if (res.status === 429) return {
16316
+ ok: false,
16317
+ error: Errors.rateLimited({ resource: "auth.token" })
16318
+ };
15334
16319
  if (!res.ok) return {
15335
16320
  ok: false,
15336
- error: Errors.providerError("better-auth", "getConvexJwt", /* @__PURE__ */ new Error(`HTTP ${res.status}`))
16321
+ error: Errors.providerError("better-auth", "getConvexJwt", /* @__PURE__ */ new Error(`HTTP ${res.status}`), {
16322
+ httpStatus: res.status,
16323
+ ...res.status >= 500 ? { failure_mode: "upstream-down" } : {}
16324
+ })
15337
16325
  };
15338
16326
  const body = await safeJson(res);
15339
16327
  if (typeof body === "object" && body !== null) {
@@ -18675,8 +19663,20 @@ function makeConnectionId() {
18675
19663
  * ever needs a different one.
18676
19664
  */
18677
19665
  const DEFAULT_CLOSE_TIMEOUT_MS = 5e3;
19666
+ const writeConvexDiagnostic = (...args) => {
19667
+ try {
19668
+ console.error(...maskCapturedNetworkRequest({ requestBody: args }).requestBody);
19669
+ } catch {}
19670
+ };
19671
+ const stderrConvexLogger = {
19672
+ log: writeConvexDiagnostic,
19673
+ warn: writeConvexDiagnostic,
19674
+ error: writeConvexDiagnostic,
19675
+ logVerbose: () => void 0
19676
+ };
18678
19677
  /** Exact floor-first allowlist; every additional handler must migrate its validator first. */
18679
19678
  const OBSERVED_CONVEX_ACTIONS = /* @__PURE__ */ new Set([
19679
+ "system:healthObserved",
18680
19680
  "payroll/actions:authorizeRun",
18681
19681
  "account/actions:readBalance",
18682
19682
  "holdings/actions:current",
@@ -18687,6 +19687,9 @@ const OBSERVED_CONVEX_ACTIONS = /* @__PURE__ */ new Set([
18687
19687
  "org/actions:resumeBootstrapSubmission",
18688
19688
  "org/actions:confirmBootstrap",
18689
19689
  "org/actions:readTreasury",
19690
+ "moneyExecution/actions:preparePermissionExecution",
19691
+ "moneyExecution/actions:submitPermissionExecution",
19692
+ "permission/actions:verify",
18690
19693
  "moneyExecution/actions:preparePaymentExecution",
18691
19694
  "moneyExecution/actions:abandonPaymentExecution",
18692
19695
  "moneyExecution/actions:submitPaymentExecution",
@@ -18699,6 +19702,8 @@ const OBSERVED_CONVEX_QUERIES = /* @__PURE__ */ new Set([
18699
19702
  "movement/activity:summary",
18700
19703
  "movement/activity:get",
18701
19704
  "financialOps/queries:depositInstructions",
19705
+ "financialOps/queries:listPayments",
19706
+ "financialOps/queries:getPayment",
18702
19707
  "financialOps/destinations:list",
18703
19708
  "financialOps/queries:verifyPaymentDocument",
18704
19709
  "financialOps/queries:renderStoredDocument",
@@ -18711,6 +19716,9 @@ const OBSERVED_CONVEX_QUERIES = /* @__PURE__ */ new Set([
18711
19716
  "payroll/queries:groups",
18712
19717
  "payroll/queries:terms",
18713
19718
  "financialOps/queries:resolveRecipient",
19719
+ "org/queries:me",
19720
+ "permission/queries:read",
19721
+ "permission/queries:authorize",
18714
19722
  "identity/queries:loadByAuthUserId",
18715
19723
  "smartAccount/queries:loadByAuthUserId",
18716
19724
  "org/lifecycle:load"
@@ -18729,6 +19737,7 @@ const OBSERVED_CONVEX_MUTATIONS = /* @__PURE__ */ new Set([
18729
19737
  "financialOps/requestsInbox:decline",
18730
19738
  "payroll/mutations:saveGroup",
18731
19739
  "payroll/mutations:removeGroup",
19740
+ "permission/mutations:command",
18732
19741
  "identity/mutations:create",
18733
19742
  "identity/mutations:update",
18734
19743
  "identity/mutations:completeOnboarding",
@@ -18748,7 +19757,10 @@ var ConvexCallAdapter = class {
18748
19757
  this.#closeTimeoutMs = deps.closeTimeoutMs ?? DEFAULT_CLOSE_TIMEOUT_MS;
18749
19758
  this.#connectionMonitor = new ConvexConnectionMonitor();
18750
19759
  const webSocketConstructor = this.#connectionMonitor.observedWebSocketConstructor();
18751
- this.#client = deps.client ?? new ConvexClient(deps.convexUrl, webSocketConstructor === void 0 ? {} : { webSocketConstructor });
19760
+ this.#client = deps.client ?? new ConvexClient(deps.convexUrl, {
19761
+ ...webSocketConstructor === void 0 ? {} : { webSocketConstructor },
19762
+ ...deps.logToStderr === true ? { logger: stderrConvexLogger } : {}
19763
+ });
18752
19764
  this.#tokenProvider = deps.tokenProvider;
18753
19765
  this.#applicationId = deps.applicationId;
18754
19766
  this.#observation = deps.observation;
@@ -18796,9 +19808,13 @@ var ConvexCallAdapter = class {
18796
19808
  }
18797
19809
  #observedArgs(allowlist, path, args, traceparent) {
18798
19810
  if (!allowlist.has(path)) return args;
19811
+ const invocationSnapshot = readInvocationObservation(args);
19812
+ if (path === "system:healthObserved" && invocationSnapshot?.active === false) {
19813
+ const { observationContext: _context, ...unobservedArgs } = args;
19814
+ return unobservedArgs;
19815
+ }
18799
19816
  const carriedContext = sanitizeObservationContext(args.observationContext);
18800
19817
  let hostContext;
18801
- const invocationSnapshot = readInvocationObservation(args);
18802
19818
  if (invocationSnapshot !== void 0) hostContext = invocationSnapshot.active ? invocationSnapshot.context : void 0;
18803
19819
  else {
18804
19820
  const resolveContext = this.#observation?.resolveContext;
@@ -19419,11 +20435,11 @@ var ConvexOrganizationAdapter = class {
19419
20435
  return this.listMembers(input).pipe(Effect.map((members) => members.filter((member) => member.status === "pending" || member.status === "pending_safe" || member.status === "pending_grant")));
19420
20436
  }
19421
20437
  inviteMember(input) {
19422
- return this.#convex.action(this.#fns.inviteMember, {
20438
+ return this.#convex.action(this.#fns.inviteMember, copyInvocationObservation(input, {
19423
20439
  orgId: input.orgId,
19424
20440
  email: input.input.email,
19425
20441
  role: input.input.role
19426
- }).pipe(Effect.mapError((error) => orgErrorFromCapxul("inviteMember", error.publicError, error)), Effect.map(brandOrgMember), Effect.catchDefect((cause) => Effect.fail(toOrgError("inviteMember", cause))));
20442
+ })).pipe(Effect.mapError((error) => orgErrorFromCapxul("inviteMember", error.publicError, error)), Effect.map(brandOrgMember), Effect.catchDefect((cause) => Effect.fail(toOrgError("inviteMember", cause))));
19427
20443
  }
19428
20444
  resendInviteToken(input) {
19429
20445
  return this.#convex.mutation(this.#fns.resendInvite, {
@@ -44703,11 +45719,18 @@ function makeMediaMethods(deps) {
44703
45719
  }
44704
45720
  //#endregion
44705
45721
  //#region ../../packages/sdk/src/contract/system.ts
44706
- const healthQuery = makeFunctionReference(CAPXUL_FUNCTIONS.system.health);
45722
+ const healthAction = makeFunctionReference(CAPXUL_FUNCTIONS.system.healthObserved);
44707
45723
  //#endregion
44708
45724
  //#region ../../packages/sdk/src/surface/system.ts
44709
45725
  function makeSystemMethods(deps) {
44710
- return { health: (nonce, options) => runIfActive(options?.signal, CAPXUL_OPERATIONS.system.health, () => deps.convexCall.query(healthQuery, { nonce })) };
45726
+ return { health: (nonce, options) => {
45727
+ let controls = deps.invocationControls?.(options) ?? options;
45728
+ const invocation = readInvocationObservation(controls);
45729
+ const traceparent = invocation?.active === false ? void 0 : sanitizeObservationContext(invocation?.context)?.traceparent;
45730
+ const parent = traceparent === void 0 ? void 0 : Option.getOrUndefined(HttpTraceContext.fromHeaders(Headers.fromRecordUnsafe({ traceparent })));
45731
+ if (parent !== void 0) controls = carryInvocationParentSpan(controls, parent);
45732
+ return runCapxulEffect(Effect.suspend(() => deps.convexCall.action(healthAction, copyInvocationObservation(controls, { nonce }))).pipe(Effect.mapError((error) => error.publicError)), controls, CAPXUL_OPERATIONS.system.health, deps.runPromise);
45733
+ } };
44711
45734
  }
44712
45735
  //#endregion
44713
45736
  //#region ../../packages/sdk/src/contract/org.ts
@@ -44732,8 +45755,33 @@ function toOrgMe(wire) {
44732
45755
  observedAt: wire.observedAt
44733
45756
  };
44734
45757
  }
45758
+ /**
45759
+ * The read runs through the canonical observed runner, so `org.me` owns one
45760
+ * `capxul_sdk_operation` record and one span like every other public method.
45761
+ *
45762
+ * The call-start invocation snapshot travels on the query arguments: the
45763
+ * Convex adapter reads it there and sends the browser linkage with the call,
45764
+ * so an account switch mid-flight cannot re-resolve the actor. A successful
45765
+ * read proves participation in this Organization, so the owning span carries
45766
+ * the verified Organization and the two authority booleans the backend
45767
+ * actually enforces. Labels and Budget caps stay off the record.
45768
+ */
44735
45769
  function makeOrgMeMethod(deps, orgId) {
44736
- return (options) => runIfActive(options?.signal, CAPXUL_OPERATIONS.org.me, () => Effect.map(deps.convexCall.query(orgReadContract.me, { orgId }), toOrgMe));
45770
+ return (options) => {
45771
+ const controls = deps.invocationControls?.(options) ?? options;
45772
+ return runCapxulEffect(Effect.gen(function* () {
45773
+ const wire = yield* deps.convexCall.query(orgReadContract.me, copyInvocationObservation(controls, { orgId })).pipe(Effect.mapError((failure) => failure.publicError));
45774
+ yield* Effect.annotateCurrentSpan({
45775
+ verified_organization_id: orgId,
45776
+ scope_kind: "organization",
45777
+ can_manage_people: wire.capabilities.canManagePeople,
45778
+ can_spend: wire.capabilities.canSpend,
45779
+ budget_count: wire.budgets.length,
45780
+ observed_at: wire.observedAt
45781
+ }).pipe(Effect.catchCause(() => Effect.void));
45782
+ return toOrgMe(wire);
45783
+ }), controls, CAPXUL_OPERATIONS.org.me, deps.runPromise);
45784
+ };
44737
45785
  }
44738
45786
  //#endregion
44739
45787
  //#region ../../packages/sdk/src/contract/permission.ts
@@ -44743,7 +45791,34 @@ const permissionContract = { read: makeFunctionReference(CAPXUL_FUNCTIONS["permi
44743
45791
  function isAborted(signal) {
44744
45792
  return signal?.aborted === true;
44745
45793
  }
44746
- async function execute(deps, orgId, command, signal) {
45794
+ /**
45795
+ * One owning operation per public Permission method. The compound command
45796
+ * (prepare, equality checks, sign, submit) keeps owning its own cancellation;
45797
+ * the observed runner around it is what gives the attempt its
45798
+ * `capxul_sdk_operation` record, its span and its failure capture, so a signer
45799
+ * refusal or an unexpected provider error joins to the operation like every
45800
+ * other public surface.
45801
+ */
45802
+ function observed(deps, operation, options, run) {
45803
+ const controls = deps.invocationControls?.(options) ?? options;
45804
+ let verifiedOrganizationId;
45805
+ return runCapxulEffect(Effect.gen(function* () {
45806
+ if (controls?.signal?.aborted === true) return yield* Effect.fail(Errors.cancelled({ operation }));
45807
+ const parent = yield* Effect.currentSpan.pipe(Effect.orDie);
45808
+ const captured = carryInvocationParentSpan(controls, parent);
45809
+ const result = yield* Effect.tryPromise({
45810
+ try: () => run(controls?.signal, captured, (orgId) => {
45811
+ verifiedOrganizationId = orgId;
45812
+ }),
45813
+ catch: (cause) => cause instanceof CapxulError ? cause : Errors.unknown(cause)
45814
+ });
45815
+ return yield* result.ok ? Effect.succeed(result.value) : Effect.fail(result.error);
45816
+ }).pipe(Effect.ensuring(Effect.suspend(() => verifiedOrganizationId === void 0 ? Effect.void : Effect.annotateCurrentSpan({
45817
+ verified_organization_id: verifiedOrganizationId,
45818
+ scope_kind: "organization"
45819
+ })).pipe(Effect.catchCause(() => Effect.void)))), observationOnlyControls(controls), operation, deps.runPromise);
45820
+ }
45821
+ async function execute(deps, orgId, command, signal, controls, verifiedOrganization) {
44747
45822
  if (isAborted(signal)) return {
44748
45823
  ok: false,
44749
45824
  error: Errors.cancelled({ operation: "permissions" })
@@ -44767,17 +45842,18 @@ async function execute(deps, orgId, command, signal) {
44767
45842
  };
44768
45843
  }
44769
45844
  const executionFns = deps.executionFunctions ?? moneyExecutionContract;
44770
- const prepared = await runIfActive(signal, CAPXUL_OPERATIONS.permissions.prepareExecution, () => deps.convexCall.action(executionFns.preparePermissionExecution, { input: {
45845
+ const prepared = await runIfActive(signal, CAPXUL_OPERATIONS.permissions.prepareExecution, () => withInvocationParentSpan(Effect.suspend(() => deps.convexCall.action(executionFns.preparePermissionExecution, copyInvocationObservation(controls, { input: {
44771
45846
  orgId,
44772
45847
  signerAddress,
44773
45848
  command
44774
- } }));
45849
+ } }))), controls), deps.runPromise);
44775
45850
  if (!prepared.ok) return prepared;
44776
45851
  const expectedSafe = deriveCapxulSafeAddress({ email: session.email });
44777
45852
  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 {
44778
45853
  ok: false,
44779
45854
  error: Errors.invalidInput("permission", "prepared authority mismatch")
44780
45855
  };
45856
+ verifiedOrganization(prepared.value.orgId);
44781
45857
  if (isAborted(signal)) return {
44782
45858
  ok: false,
44783
45859
  error: Errors.cancelled({ operation: "permissions" })
@@ -44795,43 +45871,46 @@ async function execute(deps, orgId, command, signal) {
44795
45871
  ok: false,
44796
45872
  error: Errors.cancelled({ operation: "permissions" })
44797
45873
  };
44798
- return runIfActive(void 0, CAPXUL_OPERATIONS.permissions.submitExecution, () => deps.convexCall.action(executionFns.submitPermissionExecution, { input: {
45874
+ return runIfActive(void 0, CAPXUL_OPERATIONS.permissions.submitExecution, () => withInvocationParentSpan(Effect.suspend(() => deps.convexCall.action(executionFns.submitPermissionExecution, copyInvocationObservation(controls, { input: {
44799
45875
  executionId: prepared.value.executionId,
44800
45876
  signature
44801
- } }));
45877
+ } }))), controls), deps.runPromise);
44802
45878
  }
44803
45879
  function makePermissionMethods(deps, orgId) {
44804
45880
  const reads = deps.permissionFunctions ?? permissionContract;
45881
+ const ops = CAPXUL_OPERATIONS.org.permissions;
45882
+ const read = (signal, operation, controls) => runIfActive(signal, operation, () => withInvocationParentSpan(Effect.suspend(() => deps.convexCall.query(reads.read, copyInvocationObservation(controls, { orgId }))), controls), deps.runPromise);
45883
+ const command = (operation, build, options) => observed(deps, operation, options, (signal, captured, verifiedOrganization) => execute(deps, orgId, build(), signal, captured, verifiedOrganization));
44805
45884
  return {
44806
- create: (input, options) => execute(deps, orgId, {
45885
+ create: (input, options) => command(ops.create, () => ({
44807
45886
  operation: "create",
44808
45887
  ...input
44809
- }, options?.signal),
44810
- change: (input, options) => execute(deps, orgId, {
45888
+ }), options),
45889
+ change: (input, options) => command(ops.change, () => ({
44811
45890
  operation: "change",
44812
45891
  ...input
44813
- }, options?.signal),
44814
- assign: (input, options) => execute(deps, orgId, {
45892
+ }), options),
45893
+ assign: (input, options) => command(ops.assign, () => ({
44815
45894
  operation: "assign",
44816
45895
  ...input
44817
- }, options?.signal),
44818
- revoke: (input, options) => execute(deps, orgId, {
45896
+ }), options),
45897
+ revoke: (input, options) => command(ops.revoke, () => ({
44819
45898
  operation: "revoke",
44820
45899
  ...input
44821
- }, options?.signal),
44822
- replace: (input, options) => execute(deps, orgId, {
45900
+ }), options),
45901
+ replace: (input, options) => command(ops.replace, () => ({
44823
45902
  operation: "replace",
44824
45903
  ...input
44825
- }, options?.signal),
44826
- list: (options) => runIfActive(options?.signal, CAPXUL_OPERATIONS.permissions.list, () => deps.convexCall.query(reads.read, { orgId })),
44827
- get: async (permissionId, options) => {
44828
- const listed = await runIfActive(options?.signal, CAPXUL_OPERATIONS.permissions.get, () => deps.convexCall.query(reads.read, { orgId }));
45904
+ }), options),
45905
+ list: (options) => observed(deps, ops.list, options, (signal, captured) => read(signal, CAPXUL_OPERATIONS.permissions.list, captured)),
45906
+ get: (permissionId, options) => observed(deps, ops.get, options, async (signal, captured) => {
45907
+ const listed = await read(signal, CAPXUL_OPERATIONS.permissions.get, captured);
44829
45908
  if (!listed.ok) return listed;
44830
45909
  return {
44831
45910
  ok: true,
44832
45911
  value: listed.value.permissions.find((row) => row.permissionId === permissionId) ?? null
44833
45912
  };
44834
- }
45913
+ })
44835
45914
  };
44836
45915
  }
44837
45916
  //#endregion
@@ -45326,18 +46405,6 @@ function emitOrgCreateFailed(telemetry, error) {
45326
46405
  }
45327
46406
  });
45328
46407
  }
45329
- function emitOrgInviteSent(telemetry, input) {
45330
- const domain = emailDomain$1(input.email);
45331
- return emitOrgTelemetry(telemetry, {
45332
- name: "org_invite_sent",
45333
- props: {
45334
- org_id: input.orgId,
45335
- ...domain === void 0 ? {} : { email_domain: domain },
45336
- role: input.role,
45337
- status: input.status
45338
- }
45339
- });
45340
- }
45341
46408
  function reportOrgTelemetryFailure(kind, name, cause) {
45342
46409
  if (!isOrgTelemetryDebugEnabled()) return;
45343
46410
  globalThis.console?.warn?.("[capxul] org telemetry dropped", {
@@ -45513,25 +46580,26 @@ function listMembersProgram(orgId) {
45513
46580
  return [];
45514
46581
  });
45515
46582
  }
46583
+ /**
46584
+ * R06 (#1964): the SDK no longer emits `org_invite_sent`. The backend action
46585
+ * that writes the invitation is its ONE producer, so a hermetic call — which
46586
+ * writes nothing — can no longer report an invitation that never happened, and
46587
+ * a live call is counted once instead of twice. The captured attempt rides the
46588
+ * port input, so the durable event carries the correlation this call ran under.
46589
+ */
45516
46590
  function inviteMemberProgram(orgId, input) {
45517
46591
  return Effect.gen(function* () {
45518
46592
  const deps = yield* OrgDepsTag;
45519
- const member = deps.orgPort !== void 0 ? yield* deps.orgPort.inviteMember({
45520
- orgId,
45521
- input
45522
- }).pipe(Effect.mapError((error) => error.publicError)) : hermeticMember({
46593
+ if (deps.orgPort === void 0) return hermeticMember({
45523
46594
  orgId,
45524
46595
  email: input.email,
45525
46596
  role: input.role,
45526
46597
  status: "pending"
45527
46598
  });
45528
- yield* Effect.promise(() => emitOrgInviteSent(deps.telemetry, {
46599
+ return yield* deps.orgPort.inviteMember(copyInvocationObservation(deps.invocationSource, {
45529
46600
  orgId,
45530
- email: input.email,
45531
- role: member.role,
45532
- status: member.status
45533
- }));
45534
- return member;
46601
+ input
46602
+ })).pipe(Effect.mapError((error) => error.publicError));
45535
46603
  });
45536
46604
  }
45537
46605
  function detectAndAcceptPendingInvitationsProgram() {
@@ -45639,6 +46707,13 @@ function isRetryableOrganizationSetupFailure(error) {
45639
46707
  * constructed for each `org(orgId)` call. It has no shared mutable active-Org
45640
46708
  * state. The closed-over `orgId` is its only entity reference.
45641
46709
  */
46710
+ /** The observed-runner pair a method bundle needs to own its operation records. */
46711
+ function observedRunnerDeps(deps) {
46712
+ return {
46713
+ ...deps.runPromise === void 0 ? {} : { runPromise: deps.runPromise },
46714
+ ...deps.invocationControls === void 0 ? {} : { invocationControls: deps.invocationControls }
46715
+ };
46716
+ }
45642
46717
  function makeOrgMethods(deps) {
45643
46718
  const orgDeps = {
45644
46719
  ...deps.orgPort === void 0 ? {} : { orgPort: deps.orgPort },
@@ -45689,7 +46764,10 @@ function makeOrgMethods(deps) {
45689
46764
  },
45690
46765
  ...convexCall === void 0 ? {} : { convexCall }
45691
46766
  }),
45692
- me: convexCall === void 0 ? orgMeUnavailable : makeOrgMeMethod({ convexCall }, String(orgId)),
46767
+ me: convexCall === void 0 ? orgMeUnavailable : makeOrgMeMethod({
46768
+ convexCall,
46769
+ ...observedRunnerDeps(deps)
46770
+ }, String(orgId)),
45693
46771
  async getLifecycle(options) {
45694
46772
  if (options?.signal?.aborted) return {
45695
46773
  ok: false,
@@ -45746,32 +46824,27 @@ function makeOrgMethods(deps) {
45746
46824
  });
45747
46825
  return treasury(orgId, deps.invocationControls?.(options) ?? options);
45748
46826
  },
45749
- account: { async get(options) {
45750
- if (options?.signal?.aborted) return {
45751
- ok: false,
45752
- error: Errors.cancelled({ operation: CAPXUL_OPERATIONS.org.account.get })
45753
- };
46827
+ account: { get(options) {
45754
46828
  const controls = deps.invocationControls?.(options) ?? options;
45755
- const treasuryResult = await treasury(orgId, controls);
45756
- if (!treasuryResult.ok) return {
45757
- ok: false,
45758
- error: treasuryResult.error
45759
- };
45760
- const orgList = await listOrganizations(controls);
45761
- if (!orgList.ok) return {
46829
+ if (controls?.signal?.aborted) return Promise.resolve({
45762
46830
  ok: false,
45763
- error: orgList.error
45764
- };
45765
- const org = orgList.value.find((candidate) => candidate.id === orgId);
45766
- return {
45767
- ok: true,
45768
- value: {
46831
+ error: Errors.cancelled({ operation: CAPXUL_OPERATIONS.org.account.get })
46832
+ });
46833
+ return runCapxulEffect(Effect.gen(function* () {
46834
+ const parent = yield* Effect.currentSpan.pipe(Effect.orDie);
46835
+ const captured = carryInvocationParentSpan(controls, parent);
46836
+ const treasuryResult = yield* Effect.promise(() => treasury(orgId, captured));
46837
+ if (!treasuryResult.ok) return yield* Effect.fail(treasuryResult.error);
46838
+ const orgList = yield* Effect.promise(() => listOrganizations(captured));
46839
+ if (!orgList.ok) return yield* Effect.fail(orgList.error);
46840
+ const org = orgList.value.find((candidate) => candidate.id === orgId);
46841
+ return {
45769
46842
  id: treasuryResult.value.id,
45770
46843
  address: org?.safeAddress ?? null,
45771
46844
  balance: treasuryResult.value.balance,
45772
46845
  available: treasuryResult.value.available
45773
- }
45774
- };
46846
+ };
46847
+ }), observationOnlyControls(controls), CAPXUL_OPERATIONS.org.account.get, deps.runPromise);
45775
46848
  } },
45776
46849
  members(_options) {
45777
46850
  if (_options?.signal?.aborted) return Promise.resolve({
@@ -45788,11 +46861,15 @@ function makeOrgMethods(deps) {
45788
46861
  return toCapxulResult(listRolesProgram(orgId), layer);
45789
46862
  },
45790
46863
  invite(_input, _options) {
45791
- if (_options?.signal?.aborted) return Promise.resolve({
46864
+ const controls = deps.invocationControls?.(_options) ?? _options;
46865
+ if (controls?.signal?.aborted) return Promise.resolve({
45792
46866
  ok: false,
45793
46867
  error: Errors.cancelled({ operation: CAPXUL_OPERATIONS.org.invite })
45794
46868
  });
45795
- return toCapxulResult(inviteMemberProgram(orgId, _input), layer);
46869
+ return runCapxulEffect(inviteMemberProgram(orgId, _input).pipe(Effect.provide(orgDepsLayer({
46870
+ ...orgDeps,
46871
+ ...controls === void 0 ? {} : { invocationSource: controls }
46872
+ }))), controls, CAPXUL_OPERATIONS.org.invite, deps.runPromise);
45796
46873
  },
45797
46874
  auditLog(_options) {
45798
46875
  if (_options?.signal?.aborted) return Promise.resolve({
@@ -45806,6 +46883,7 @@ function makeOrgMethods(deps) {
45806
46883
  },
45807
46884
  permissions: convexCall === void 0 ? makeNotImplementedPermissionMethods() : makePermissionMethods({
45808
46885
  convexCall,
46886
+ ...observedRunnerDeps(deps),
45809
46887
  ...deps.actor === void 0 ? {} : { actor: deps.actor },
45810
46888
  ...deps.chainId === void 0 ? {} : { chainId: Number(deps.chainId) },
45811
46889
  ...deps.signer === void 0 ? {} : { signer: deps.signer }
@@ -45925,14 +47003,65 @@ function compareDecimal(first, second) {
45925
47003
  }
45926
47004
  //#endregion
45927
47005
  //#region ../../packages/sdk/src/surface/holdings.ts
47006
+ /** SPEC Freshness: past this block age the read is labelled stale. Nothing refuses. */
47007
+ const FRESHNESS_LIMIT_SECONDS = 60;
47008
+ /**
47009
+ * The Ponder balance view for one Safe as `CurrentHoldings`.
47010
+ *
47011
+ * The view is the signed sum of every indexed transfer, so the balance IS what
47012
+ * the chain said — there is no snapshot to go stale behind it. `observedAt` is
47013
+ * the indexed block's own timestamp, which is what the last-good label reads,
47014
+ * and `stale` is that block's age. A stopped indexer therefore shows an old
47015
+ * "as of" and the stale flag; it never turns the read into a refusal.
47016
+ */
47017
+ function indexerHoldings(indexer, convexCall, overlay, actor, controls) {
47018
+ return Effect.gen(function* () {
47019
+ const scope = yield* convexCall.query(overlay, copyInvocationObservation(controls, actor?.kind === "organization" ? { input: { actor: {
47020
+ kind: "organization",
47021
+ orgId: actor.organizationId
47022
+ } } } : { input: {} }));
47023
+ if (scope.safe === null) return null;
47024
+ const balances = yield* indexer.read("balance", {
47025
+ safe: toAddress(scope.safe),
47026
+ limit: INDEXER_MAX_LIMIT
47027
+ });
47028
+ const status = yield* indexer.status.pipe(Effect.catch(() => Effect.succeed([])));
47029
+ const rows = [];
47030
+ for (const balance of balances) {
47031
+ const asset = configuredMoneyAssetById(balance.assetId);
47032
+ if (asset === null || balance.amount < 0n) continue;
47033
+ rows.push({
47034
+ assetKind: "erc20",
47035
+ tokenAddress: toAddress(asset.tokenAddress),
47036
+ symbol: asset.symbol,
47037
+ decimals: asset.decimals,
47038
+ rawBalance: toWeiAmount(balance.amount.toString())
47039
+ });
47040
+ }
47041
+ const chainId = balances[0]?.chainId ?? status[0]?.chainId;
47042
+ if (chainId === void 0) return null;
47043
+ const head = status.find((entry) => entry.chainId === chainId);
47044
+ const nowSeconds = Math.floor(Date.now() / 1e3);
47045
+ const headSeconds = head === void 0 ? void 0 : Number(head.blockTimestamp);
47046
+ return {
47047
+ actor: scope.actor,
47048
+ chainId: toChainId(chainId),
47049
+ observedAt: headSeconds === void 0 ? Date.now() : headSeconds * 1e3,
47050
+ stale: headSeconds === void 0 || nowSeconds - headSeconds > FRESHNESS_LIMIT_SECONDS,
47051
+ rows
47052
+ };
47053
+ }).pipe(Effect.catch(() => Effect.succeed(null)));
47054
+ }
45928
47055
  function makeHoldingsMethods(deps) {
45929
47056
  const functions = deps.functions ?? holdingsContract;
47057
+ const overlay = (deps.financialOpsFunctions ?? financialOpsContract).activityOverlay;
45930
47058
  const read = (operation, actor, options, pick) => {
45931
47059
  const controls = deps.invocationControls?.(options) ?? options;
45932
- return runCapxulEffect(retryIdempotentRead(deps.convexCall.action(functions.current, copyInvocationObservation(controls, actor?.kind === "organization" ? { actor: {
47060
+ const convexSnapshot = retryIdempotentRead(deps.convexCall.action(functions.current, copyInvocationObservation(controls, actor?.kind === "organization" ? { actor: {
45933
47061
  kind: "organization",
45934
47062
  orgId: actor.organizationId
45935
- } } : {})), operation, deps.telemetry, controls).pipe(Effect.map(pick), Effect.mapError((error) => error.publicError)), controls, operation, deps.runPromise);
47063
+ } } : {})), operation, deps.telemetry, controls).pipe(Effect.mapError((error) => error.publicError));
47064
+ 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);
45936
47065
  };
45937
47066
  return {
45938
47067
  current: (input, options) => read(CAPXUL_OPERATIONS.holdings.current, input?.actor, options, (snapshot) => snapshot),
@@ -46066,8 +47195,8 @@ const APPLIED_ACTION_BY_EVENT = {
46066
47195
  RestoreSession: "none",
46067
47196
  ResumeOtpEntry: "none",
46068
47197
  Reset: "none",
46069
- SessionRestored: "none",
46070
- SessionAbsent: "none",
47198
+ SessionRestored: "restoration",
47199
+ SessionAbsent: "restoration",
46071
47200
  EnsureAccount: "none",
46072
47201
  ClaimAccount: "none",
46073
47202
  RetryAccount: "none",
@@ -46079,8 +47208,8 @@ const APPLIED_ACTION_BY_EVENT = {
46079
47208
  OtpFailed: "failed",
46080
47209
  Verified: "verified",
46081
47210
  VerifyFailed: "failed",
46082
- SessionRead: "none",
46083
- SessionReadFailed: "none",
47211
+ SessionRead: "restoration",
47212
+ SessionReadFailed: "restoration",
46084
47213
  SignedOut: "signed-out",
46085
47214
  SignOutFailed: "failed",
46086
47215
  AccountAt: "none",
@@ -46106,6 +47235,49 @@ const authFailure = (record, reason, mode) => ({
46106
47235
  ...linkage(record)
46107
47236
  }
46108
47237
  });
47238
+ /**
47239
+ * The label of the restore window every client boots into. A settled session
47240
+ * fact that left this label is the restoration result (#1962 R04); the same
47241
+ * facts from a signed-out or authenticated state are the routine session probe
47242
+ * that `getSession()` runs, and a probe is not a restoration attempt.
47243
+ */
47244
+ const RESTORE_WINDOW = "restoring";
47245
+ /**
47246
+ * Which result the window settled on. `SessionRestored` and `SessionAbsent` are
47247
+ * the cache door and say it themselves. `ReadSession` is also valid inside the
47248
+ * window — `getSession()` drives it on a cache miss and its `SessionRead`
47249
+ * completion settles the window — so that one event reports both halves and the
47250
+ * state it produced is what separates them.
47251
+ */
47252
+ const restorationOutcome = (event, state) => {
47253
+ switch (event) {
47254
+ case "SessionRestored": return "restored";
47255
+ case "SessionAbsent": return "absent";
47256
+ case "SessionReadFailed": return "failed";
47257
+ case "SessionRead":
47258
+ if (state.phase === "authenticated") return "restored";
47259
+ return state.phase === "signed_out" ? "absent" : void 0;
47260
+ default: return;
47261
+ }
47262
+ };
47263
+ /**
47264
+ * The restore window's one result, or nothing when this settled session fact
47265
+ * belongs to a probe rather than to the window.
47266
+ */
47267
+ const restorationObservation = (record, state) => {
47268
+ if (record.slot !== "identity:session" || record.from !== RESTORE_WINDOW) return NONE;
47269
+ const outcome = restorationOutcome(record.event, state);
47270
+ if (outcome === void 0) return NONE;
47271
+ return {
47272
+ name: "access_recovery_completed",
47273
+ props: {
47274
+ recovery: "session_restoration",
47275
+ outcome,
47276
+ ...outcome === "failed" && state.phase === "faulted" ? { reason_code: state.failure.code } : {},
47277
+ ...linkage(record)
47278
+ }
47279
+ };
47280
+ };
46109
47281
  const REFUSAL_EVENTS = /* @__PURE__ */ new Set([
46110
47282
  "ClaimAccount",
46111
47283
  "RetryAccount",
@@ -46150,6 +47322,7 @@ function mapIdentityProductObservation(record, state) {
46150
47322
  name: "auth_signed_out",
46151
47323
  props: linkage(record)
46152
47324
  };
47325
+ case "restoration": return restorationObservation(record, state);
46153
47326
  }
46154
47327
  }
46155
47328
  const ignoreTransportFailure = (operation, operationName, eventName) => Effect.suspend(operation).pipe(Effect.catchCause(() => Effect.logWarning("identity.product.telemetry.dropped").pipe(Effect.annotateLogs({
@@ -46161,7 +47334,7 @@ function executeIdentityProductObservation(telemetry, record, state) {
46161
47334
  if (mapped === null) return Effect.void;
46162
47335
  copyInvocationObservation(record, mapped);
46163
47336
  const invocationTelemetry = bindProductTelemetryInvocation(telemetry, mapped);
46164
- const after = () => mapped.name === "auth_verified" && state.phase === "authenticated" ? invocationTelemetry.identify({
47337
+ const after = () => state.phase === "authenticated" && (mapped.name === "auth_verified" || mapped.name === "access_recovery_completed") ? invocationTelemetry.identify({
46165
47338
  distinctId: state.session.authUserId,
46166
47339
  traits: { email_domain: emailDomain(state.session.email) }
46167
47340
  }) : mapped.name === "auth_signed_out" ? invocationTelemetry.reset() : Effect.void;
@@ -46206,6 +47379,73 @@ function startSignerReadinessOnClaim(actor, signer) {
46206
47379
  browserSigner.ensureWalletReady?.().catch(() => {});
46207
47380
  });
46208
47381
  }
47382
+ /**
47383
+ * Report the result of each signer readiness cycle (#1962 R04).
47384
+ *
47385
+ * The readiness store already carries the cycle boundary this needs, so no
47386
+ * public signer hook, second identity subscriber, or render counter is added.
47387
+ * `ensureOpenfortWalletReady` writes `recovering` synchronously when it starts
47388
+ * a cycle, and only the cycle that is still the current one writes `ready` or
47389
+ * `unavailable` (`create-openfort-browser-signer.ts:501-521`). Callers that
47390
+ * join a live cycle share that single write, and a `resetSession` writes
47391
+ * `unknown` first, so a cycle that a reset or an account replacement outran
47392
+ * reports nothing.
47393
+ *
47394
+ * The host context is bound when the cycle starts, so a person who signs out
47395
+ * and back in while a cycle runs cannot relabel the result that began under
47396
+ * the previous person. A signer with no readiness cycle — a node or dev key,
47397
+ * and a headless client whose requirement never opens the account lane —
47398
+ * writes no status and reports nothing.
47399
+ */
47400
+ function observeSignerReadinessCycles(signer, telemetry, hostObservationSnapshot, runPromise) {
47401
+ const statusStore = signer?.statusStore;
47402
+ if (statusStore === void 0 || telemetry === void 0) return () => {};
47403
+ let running = false;
47404
+ let startContext;
47405
+ return statusStore.subscribe((status) => {
47406
+ if (status === "recovering") {
47407
+ running = true;
47408
+ startContext = hostObservationSnapshot?.();
47409
+ return;
47410
+ }
47411
+ const cycleContext = startContext;
47412
+ const completesCycle = running;
47413
+ running = false;
47414
+ startContext = void 0;
47415
+ if (!completesCycle || status !== "ready" && status !== "unavailable") return;
47416
+ const event = {
47417
+ name: "access_recovery_completed",
47418
+ props: {
47419
+ recovery: "signer_readiness",
47420
+ outcome: status
47421
+ }
47422
+ };
47423
+ if (cycleContext !== void 0) attachInvocationObservation(event, cycleContext);
47424
+ runPromise(Effect.suspend(() => bindProductTelemetryInvocation(telemetry, event).emit(event)).pipe(Effect.catchCause(() => Effect.void))).catch(() => {});
47425
+ });
47426
+ }
47427
+ /** Join the assembled actor lifetime to the adapter lifetime for internal client factories. */
47428
+ function withAdapterClose(client, closeAdapter) {
47429
+ const closeActor = client._internal.close;
47430
+ let closePromise;
47431
+ const close = () => {
47432
+ closePromise ??= Promise.allSettled([Promise.resolve().then(() => closeActor?.()), Promise.resolve().then(closeAdapter)]).then((results) => {
47433
+ const failure = results.find((result) => result.status === "rejected");
47434
+ if (failure?.status === "rejected") throw failure.reason;
47435
+ });
47436
+ return closePromise;
47437
+ };
47438
+ return {
47439
+ ...client,
47440
+ close: () => closeClientResources(close),
47441
+ _internal: {
47442
+ ...client._internal,
47443
+ close
47444
+ }
47445
+ };
47446
+ }
47447
+ /** Keep lifecycle failures on the same public result boundary as domain work. */
47448
+ const closeClientResources = (close) => toCapxulResult(Effect.promise(close), Layer.empty);
46209
47449
  function assembleCapxulClient(input) {
46210
47450
  const observation = input;
46211
47451
  const authCache = input.authCache ?? detectAuthCacheAdapter();
@@ -46235,12 +47475,14 @@ function assembleCapxulClient(input) {
46235
47475
  }, failureObservation, normalizeCapxulOperation(`identity.${record.event}`), "machine");
46236
47476
  });
46237
47477
  const unsubscribeSignerReadiness = startSignerReadinessOnClaim(actor, input.signer);
47478
+ const unsubscribeReadinessObservation = observeSignerReadinessCycles(input.signer, input.ports.telemetry, observation.hostObservationSnapshot, effectRunner.runPromise);
46238
47479
  let stopPromise = null;
46239
47480
  const stopActor = () => {
46240
47481
  if (stopPromise === null) {
46241
47482
  unsubscribeProductTelemetry();
46242
47483
  unsubscribeFailureObservation();
46243
47484
  unsubscribeSignerReadiness();
47485
+ unsubscribeReadinessObservation();
46244
47486
  stopPromise = effectRunner.runPromise(Scope.close(scope, Exit.void));
46245
47487
  }
46246
47488
  return stopPromise;
@@ -46257,7 +47499,8 @@ function assembleCapxulClient(input) {
46257
47499
  convexCall: input.ports.convexCall,
46258
47500
  ...input.signer === void 0 ? {} : { signer: input.signer },
46259
47501
  telemetry: input.ports.telemetry,
46260
- requestKeyScope: `${input.bootstrap.convexUrl}:${String(input.bootstrap.chainId)}`
47502
+ requestKeyScope: `${input.bootstrap.convexUrl}:${String(input.bootstrap.chainId)}`,
47503
+ ...input.ports.indexerRead === void 0 ? {} : { indexerRead: input.ports.indexerRead }
46261
47504
  });
46262
47505
  const accountBundle = makeAccountMethods({
46263
47506
  actor,
@@ -46322,13 +47565,18 @@ function assembleCapxulClient(input) {
46322
47565
  subscribeIdentity: actor.subscribe,
46323
47566
  sendIdentity: (event, options) => runCapxulEffect(askIdentity(actor, event, options), options, CAPXUL_OPERATIONS._internal.identity.send, effectRunner.runPromise)
46324
47567
  });
46325
- const system = makeSystemMethods({ convexCall: input.ports.convexCall });
47568
+ const system = makeSystemMethods({
47569
+ convexCall: input.ports.convexCall,
47570
+ runPromise: effectRunner.runPromise,
47571
+ invocationControls: (controls) => withHostObservationControls(observation.hostObservationSnapshot, controls)
47572
+ });
46326
47573
  const media = makeMediaMethods({ convexCall: input.ports.convexCall });
46327
47574
  const holdings = makeHoldingsMethods({
46328
47575
  convexCall: input.ports.convexCall,
46329
47576
  telemetry: input.ports.telemetry,
46330
47577
  runPromise: effectRunner.runPromise,
46331
- invocationControls: (controls) => withHostObservationControls(observation.hostObservationSnapshot, controls, actor.authSession()?.authUserId)
47578
+ invocationControls: (controls) => withHostObservationControls(observation.hostObservationSnapshot, controls, actor.authSession()?.authUserId),
47579
+ ...input.ports.indexerRead === void 0 ? {} : { indexerRead: input.ports.indexerRead }
46332
47580
  });
46333
47581
  const accounts = makeAccountsMethods({
46334
47582
  accountReadPort: input.ports.accountRead,
@@ -46471,7 +47719,8 @@ function assembleCapxulClient(input) {
46471
47719
  })).pipe(Effect.catchCause(() => Effect.void)));
46472
47720
  },
46473
47721
  close: stopActor
46474
- }
47722
+ },
47723
+ close: () => closeClientResources(stopActor)
46475
47724
  }, observation.failureObservation, observation.hostObservationSnapshot);
46476
47725
  }
46477
47726
  function transitionError(record) {
@@ -46521,6 +47770,7 @@ function withHostObservationControls(snapshot, input, actorId) {
46521
47770
  }
46522
47771
  //#endregion
46523
47772
  //#region ../../packages/sdk/src/host-observability.ts
47773
+ const HOST_ENGINEERING_POLICY = Symbol("capxul.host-engineering-policy");
46524
47774
  const HOST_INVOCATION = Symbol("capxul.host-observability-invocation");
46525
47775
  /** @internal Bind both projections to one call-start decision/context snapshot. */
46526
47776
  function bindHostObservabilityInvocation(observability) {
@@ -46534,36 +47784,14 @@ function snapshotHostObservability(observability) {
46534
47784
  ...snapshot.context === void 0 ? {} : { context: snapshot.context }
46535
47785
  };
46536
47786
  }
46537
- //#endregion
46538
- //#region ../../packages/sdk/src/ports/indexer-read.ts
46539
- /** The indexer's own page bound. Exceeding it is a caller defect, not a refusal. */
46540
- const INDEXER_MAX_LIMIT = 1e3;
46541
- const LOWER_CASE_ADDRESS = /^0x[0-9a-f]{40}$/;
46542
- /**
46543
- * Refuse a page the indexer would refuse, before any implementation spends a
46544
- * round trip on it. Every implementation calls this, so a consumer test written
46545
- * against the hermetic adapter cannot pass on input production rejects. Throws
46546
- * a `CapxulError`; callers convert it to an `IndexerReadError`.
46547
- */
46548
- function assertIndexerPage(input) {
46549
- const safe = String(input.safe).toLowerCase();
46550
- if (!LOWER_CASE_ADDRESS.test(safe)) throw Errors.invalidInput("safe", "must be a lower-case EVM address");
46551
- if (!Number.isSafeInteger(input.limit) || input.limit < 1 || input.limit > 1e3) throw Errors.invalidInput("limit", `must be an integer from 1 to ${INDEXER_MAX_LIMIT}`);
46552
- const offset = input.offset ?? 0;
46553
- if (!Number.isSafeInteger(offset) || offset < 0) throw Errors.invalidInput("offset", "must be a non-negative integer");
46554
- return safe;
46555
- }
46556
- var IndexerReadError = class extends Data.TaggedError("IndexerReadError") {};
46557
- function indexerReadErrorFromCapxul(operation, error, cause = error) {
46558
- return new IndexerReadError({
46559
- operation,
46560
- publicCode: error.code,
46561
- publicError: error,
46562
- cause,
46563
- ...error.details === void 0 ? {} : { details: error.details }
46564
- });
47787
+ /** @internal Carry the explicit host engineering policy into production composition. */
47788
+ function hostEngineeringPolicy(observability) {
47789
+ const policy = observability?.[HOST_ENGINEERING_POLICY];
47790
+ return {
47791
+ ...policy?.engineeringEnabled === void 0 ? {} : { enabled: policy.engineeringEnabled },
47792
+ ...policy?.engineeringPrivacy === void 0 ? {} : { privacy: policy.engineeringPrivacy }
47793
+ };
46565
47794
  }
46566
- Context.Service()("@capxul/sdk/ports/IndexerReadPort");
46567
47795
  //#endregion
46568
47796
  //#region ../../packages/sdk/src/adapters/ponder/decode.ts
46569
47797
  /**
@@ -46614,6 +47842,7 @@ function identity(row) {
46614
47842
  chainId: toChainId(integer(row, "chain_id")),
46615
47843
  txHash: toTxHash(hex(row, "tx_hash")),
46616
47844
  transactionLogOrdinal: integer(row, "transaction_log_ordinal"),
47845
+ logIndex: integer(row, "log_index"),
46617
47846
  blockNumber: big(row, "block_number"),
46618
47847
  blockHash: hex(row, "block_hash"),
46619
47848
  timestamp: big(row, "timestamp")
@@ -46696,8 +47925,8 @@ function decodeStatus(body) {
46696
47925
  * that allowlist changes, this file changes with it or every read fails 403.
46697
47926
  */
46698
47927
  const HEADS = {
46699
- transfer: "select \"id\", \"chain_id\", \"tx_hash\", \"transaction_log_ordinal\", \"block_number\", \"block_hash\", \"timestamp\", \"asset_id\", \"token\", \"from\", \"to\", \"amount\" from \"transfer\" where (\"transfer\".\"from\" = $1 or \"transfer\".\"to\" = $2) order by \"transfer\".\"block_number\" desc, \"transfer\".\"id\" desc",
46700
- payment: "select \"id\", \"chain_id\", \"tx_hash\", \"transaction_log_ordinal\", \"block_number\", \"block_hash\", \"timestamp\", \"contract\", \"settlement_id\", \"sender\", \"recipient\", \"asset_id\", \"token\", \"amount\", \"kind\", \"document_hash\" from \"payment\" where (\"payment\".\"sender\" = $1 or \"payment\".\"recipient\" = $2) order by \"payment\".\"block_number\" desc, \"payment\".\"id\" desc",
47928
+ transfer: "select \"id\", \"chain_id\", \"tx_hash\", \"transaction_log_ordinal\", \"log_index\", \"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",
47929
+ payment: "select \"id\", \"chain_id\", \"tx_hash\", \"transaction_log_ordinal\", \"log_index\", \"block_number\", \"block_hash\", \"timestamp\", \"contract\", \"settlement_id\", \"sender\", \"recipient\", \"asset_id\", \"token\", \"amount\", \"kind\", \"document_hash\" from \"payment\" where (\"payment\".\"sender\" = $1 or \"payment\".\"recipient\" = $2) order by \"payment\".\"block_number\" desc, \"payment\".\"id\" desc",
46701
47930
  balance: "select \"chain_id\", \"asset_id\", \"to\", \"amount\" from \"balance\" where \"balance\".\"to\" = $1"
46702
47931
  };
46703
47932
  /** How many times the statement binds the Safe address. */
@@ -46747,11 +47976,16 @@ function ponderQueryParam(query) {
46747
47976
  * policy allows exactly one request header (`Authorization`), so this reader
46748
47977
  * sends nothing else. Ponder writes one `data:` line per frame.
46749
47978
  */
46750
- async function readEventStream(body, onData, stopped) {
47979
+ async function readEventStream(body, onData, stopped, signal) {
46751
47980
  const reader = body.getReader();
46752
47981
  const decoder = new TextDecoder();
46753
47982
  let buffer = "";
47983
+ const cancel = () => {
47984
+ reader.cancel().catch(() => void 0);
47985
+ };
47986
+ signal?.addEventListener("abort", cancel, { once: true });
46754
47987
  try {
47988
+ if (signal?.aborted) return;
46755
47989
  for (;;) {
46756
47990
  const { done, value } = await reader.read();
46757
47991
  if (done || stopped()) return;
@@ -46767,6 +48001,7 @@ async function readEventStream(body, onData, stopped) {
46767
48001
  }
46768
48002
  }
46769
48003
  } finally {
48004
+ signal?.removeEventListener("abort", cancel);
46770
48005
  await reader.cancel().catch(() => void 0);
46771
48006
  }
46772
48007
  }
@@ -46803,12 +48038,10 @@ var PonderIndexerAdapter = class {
46803
48038
  subscribe(table, input, callback) {
46804
48039
  const operation = CAPXUL_OPERATIONS.activity.subscribe;
46805
48040
  return Effect.sync(() => {
46806
- let stopped = false;
46807
- const unsubscribe = () => {
46808
- stopped = true;
46809
- };
48041
+ const controller = new AbortController();
48042
+ const unsubscribe = () => controller.abort();
46810
48043
  callback({ status: "loading" });
46811
- this.#stream(table, input, operation, callback, () => stopped).catch(() => void 0);
48044
+ this.#stream(table, input, operation, callback, controller.signal).catch(() => void 0);
46812
48045
  return unsubscribe;
46813
48046
  });
46814
48047
  }
@@ -46821,9 +48054,13 @@ var PonderIndexerAdapter = class {
46821
48054
  catch: (cause) => toIndexerReadError("indexer.status", cause)
46822
48055
  });
46823
48056
  /** One authenticated `/sql/:method` call. Throws a `CapxulError` on refusal. */
46824
- async #request(method, table, input, operation) {
48057
+ async #request(method, table, input, operation, signal, forceRefreshToken = false) {
46825
48058
  const query = ponderQuery(table, input);
46826
- const token = await this.#token({ forceRefreshToken: false });
48059
+ const token = await this.#token({
48060
+ forceRefreshToken,
48061
+ ...signal === void 0 ? {} : { signal }
48062
+ });
48063
+ if (signal?.aborted) throw Errors.cancelled({ operation });
46827
48064
  if (token === null || token === "") throw Errors.notAuthenticated();
46828
48065
  const url = `${this.#baseUrl}/sql/${method}?sql=${encodeURIComponent(ponderQueryParam(query))}`;
46829
48066
  const headers = { authorization: `Bearer ${token}` };
@@ -46832,7 +48069,8 @@ var PonderIndexerAdapter = class {
46832
48069
  try {
46833
48070
  response = await this.#fetch(url, {
46834
48071
  method: "GET",
46835
- headers
48072
+ headers,
48073
+ ...signal === void 0 ? {} : { signal }
46836
48074
  });
46837
48075
  } catch (cause) {
46838
48076
  throw Errors.networkError(operation, cause, {
@@ -46844,39 +48082,72 @@ var PonderIndexerAdapter = class {
46844
48082
  return response;
46845
48083
  }
46846
48084
  /**
46847
- * Follow the live stream until the caller unsubscribes or the indexer closes
46848
- * it. The indexer closes the stream on token expiry or revoked authority
46849
- * with no frame, so any end the caller did not ask for is a teardown: it
46850
- * reaches `captureException` under `activity.subscribe` and the caller sees
46851
- * one `error` snapshot.
48085
+ * Keep the subscription alive across transport loss. Every new connection
48086
+ * rechecks authority. A refusal or invalid payload is terminal. Unsubscribe
48087
+ * aborts both the current request and any reconnect delay.
46852
48088
  */
46853
- async #stream(table, input, operation, callback, stopped) {
46854
- let failure;
46855
- try {
46856
- const response = await this.#request("live", table, input, operation);
46857
- if (response.body === null) throw Errors.providerError("indexer", operation, "empty stream");
46858
- await readEventStream(response.body, (data) => {
46859
- try {
46860
- callback({
46861
- status: "ok",
46862
- value: decodeRows(table, JSON.parse(data))
46863
- });
46864
- } catch (cause) {
46865
- failure = toPublicError$1(operation, cause);
46866
- }
46867
- }, () => stopped() || failure !== void 0);
46868
- } catch (cause) {
46869
- failure = toPublicError$1(operation, cause);
48089
+ async #stream(table, input, operation, callback, signal) {
48090
+ let refreshToken = false;
48091
+ let delay = 1e3;
48092
+ let reported = false;
48093
+ while (!signal.aborted) {
48094
+ let failure;
48095
+ let invalidFrame = false;
48096
+ let reading = false;
48097
+ const startedAt = Date.now();
48098
+ try {
48099
+ const response = await this.#request("live", table, input, operation, signal, refreshToken);
48100
+ if (response.body === null) throw Errors.providerError("indexer", operation, "empty stream");
48101
+ reading = true;
48102
+ await readEventStream(response.body, (data) => {
48103
+ try {
48104
+ callback({
48105
+ status: "ok",
48106
+ value: decodeRows(table, JSON.parse(data))
48107
+ });
48108
+ reported = false;
48109
+ } catch (cause) {
48110
+ invalidFrame = true;
48111
+ failure = toPublicError$1(operation, cause);
48112
+ }
48113
+ }, () => signal.aborted || failure !== void 0, signal);
48114
+ } catch (cause) {
48115
+ failure = reading && !(cause instanceof CapxulError) ? Errors.networkError(operation, cause, {
48116
+ provider: "indexer",
48117
+ failure_mode: "upstream-down"
48118
+ }) : toPublicError$1(operation, cause);
48119
+ }
48120
+ if (signal.aborted || failure?.code === "CANCELLED") return;
48121
+ const error = failure ?? Errors.capabilityUnavailable("indexer", operation);
48122
+ const retryable = !invalidFrame && (error.code === "NETWORK_ERROR" || error.code === "CAPABILITY_UNAVAILABLE" || error.code === "RATE_LIMITED" || error.mode === "upstream-down");
48123
+ if (!reported || !retryable) {
48124
+ observeStreamFailure(this.#observation, operation, error, error.code === "NOT_AUTHENTICATED" ? "operation" : "exception");
48125
+ callback({
48126
+ status: "error",
48127
+ error
48128
+ });
48129
+ reported = true;
48130
+ }
48131
+ if (!retryable) return;
48132
+ if (Date.now() - startedAt >= 3e4) delay = 1e3;
48133
+ await reconnectDelay(delay, signal);
48134
+ delay = Math.min(delay * 2, 3e4);
48135
+ refreshToken = true;
46870
48136
  }
46871
- if (stopped()) return;
46872
- const error = failure ?? Errors.capabilityUnavailable("indexer", operation);
46873
- observeStreamFailure(this.#observation, CAPXUL_OPERATIONS.activity.subscribe, error);
46874
- callback({
46875
- status: "error",
46876
- error
46877
- });
46878
48137
  }
46879
48138
  };
48139
+ function reconnectDelay(milliseconds, signal) {
48140
+ return new Promise((resolve) => {
48141
+ const finish = () => {
48142
+ clearTimeout(timer);
48143
+ signal.removeEventListener("abort", finish);
48144
+ resolve();
48145
+ };
48146
+ const timer = setTimeout(finish, milliseconds);
48147
+ signal.addEventListener("abort", finish, { once: true });
48148
+ if (signal.aborted) finish();
48149
+ });
48150
+ }
46880
48151
  /**
46881
48152
  * Map an indexer refusal into the shared error vocabulary.
46882
48153
  *
@@ -47244,6 +48515,7 @@ function productionAuthCacheLayer(input) {
47244
48515
  function productionConvexCallLayer(input, refreshConvexAuthRef) {
47245
48516
  return Layer.unwrap(Effect.map(AuthClientPortTag, (authClient) => ConvexCallLayer({
47246
48517
  convexUrl: input.runtimeUrls.convexUrl,
48518
+ logToStderr: input.runtime === "node",
47247
48519
  ...input.applicationId === void 0 ? {} : { applicationId: input.applicationId },
47248
48520
  ...input.observation === void 0 ? {} : { observation: input.observation },
47249
48521
  ...input.convexClient === void 0 ? {} : { client: input.convexClient },
@@ -47365,7 +48637,7 @@ async function createProductionAdapters(input) {
47365
48637
  ...input.signal === void 0 ? {} : { signal: input.signal },
47366
48638
  ...input.resetSignerSession === void 0 ? {} : { resetSignerSession: input.resetSignerSession }
47367
48639
  }));
47368
- const applicationLayer = Layer.merge(portsLayer, optionalEngineeringTelemetryLayer(bootstrapResult.value.engineeringTelemetry, resolvedInput.value.runtime));
48640
+ const applicationLayer = Layer.merge(portsLayer, optionalEngineeringTelemetryLayer(bootstrapResult.value.engineeringTelemetry, resolvedInput.value.runtime, hostEngineeringPolicy(input.observability))).pipe(Layer.provideMerge(resolvedInput.value.runtime === "node" ? Layer.succeed(Logger.LogToStderr, true) : Layer.empty));
47369
48641
  const context = await Effect.runPromise(Layer.buildWithScope(applicationLayer, scope));
47370
48642
  const collected = await Effect.runPromise(collectProductionFlowPorts.pipe(Effect.provide(context)));
47371
48643
  const indexerUrl = input.indexerUrl ?? bootstrapResult.value.indexerUrl;
@@ -47414,7 +48686,7 @@ function observeProductionConvexConnection(convexCall, context) {
47414
48686
  return () => {};
47415
48687
  }
47416
48688
  }
47417
- function optionalEngineeringTelemetryLayer(policy, runtime) {
48689
+ function optionalEngineeringTelemetryLayer(policy, runtime, hostPolicy) {
47418
48690
  if (policy === void 0) return Layer.empty;
47419
48691
  try {
47420
48692
  return makeEngineeringTelemetryLayer({
@@ -47422,7 +48694,8 @@ function optionalEngineeringTelemetryLayer(policy, runtime) {
47422
48694
  headers: { authorization: `Bearer ${policy.projectToken}` },
47423
48695
  capxulEnv: policy.capxulEnv,
47424
48696
  producer: runtime === "browser" ? "browser" : "server",
47425
- sdkVersion: SDK_VERSION
48697
+ sdkVersion: SDK_VERSION,
48698
+ ...hostPolicy
47426
48699
  });
47427
48700
  } catch {
47428
48701
  return Layer.empty;
@@ -47517,7 +48790,7 @@ async function createCapxulClientWithSignerControls(input, controls) {
47517
48790
  }
47518
48791
  };
47519
48792
  }
47520
- const client = assembleCapxulClient({
48793
+ const ownedClient = withAdapterClose(assembleCapxulClient({
47521
48794
  ports: adapters.value.ports,
47522
48795
  bootstrap: adapters.value.bootstrap,
47523
48796
  authCache: adapters.value.ports.authCache,
@@ -47538,8 +48811,7 @@ async function createCapxulClientWithSignerControls(input, controls) {
47538
48811
  runSync: Effect.runSyncWith(adapters.value.context),
47539
48812
  runPromise: Effect.runPromiseWith(adapters.value.context)
47540
48813
  }
47541
- });
47542
- const upstreamClose = adapters.value.close;
48814
+ }), adapters.value.close);
47543
48815
  const close = idempotentClose(async () => {
47544
48816
  try {
47545
48817
  if (signer !== void 0 && "resetSession" in signer) try {
@@ -47548,15 +48820,16 @@ async function createCapxulClientWithSignerControls(input, controls) {
47548
48820
  throw signerFailure(signer.source, "resetSession", cause);
47549
48821
  }
47550
48822
  } finally {
47551
- await Promise.all([client._internal.close?.(), upstreamClose()]);
48823
+ await ownedClient._internal.close?.();
47552
48824
  }
47553
48825
  });
47554
48826
  return {
47555
48827
  ok: true,
47556
48828
  value: {
47557
- ...client,
48829
+ ...ownedClient,
48830
+ close: () => closeClientResources(close),
47558
48831
  _internal: {
47559
- ...client._internal,
48832
+ ...ownedClient._internal,
47560
48833
  close
47561
48834
  }
47562
48835
  }
@@ -47715,15 +48988,17 @@ function idempotentClose(close) {
47715
48988
  * and Convex see one session with one scope.
47716
48989
  */
47717
48990
  function productionIndexerReadPort(input, ports, observation) {
48991
+ if (input.activityReadSource === "convex") return void 0;
47718
48992
  if (input.indexerUrl === void 0 || input.indexerUrl === "") return void 0;
47719
48993
  return new PonderIndexerAdapter({
47720
48994
  baseUrl: normalizeHttpUrl("indexerUrl", input.indexerUrl),
47721
- token: async ({ forceRefreshToken }) => {
48995
+ token: async ({ forceRefreshToken, signal }) => {
48996
+ const tokenSignal = input.signal === void 0 ? signal : signal === void 0 ? input.signal : AbortSignal.any([input.signal, signal]);
47722
48997
  const token = await Effect.runPromise(Effect.result(ports.authClient.getConvexJwt({
47723
48998
  forceRefresh: forceRefreshToken,
47724
- ...input.signal === void 0 ? {} : { signal: input.signal }
48999
+ ...tokenSignal === void 0 ? {} : { signal: tokenSignal }
47725
49000
  })));
47726
- if (Result.isFailure(token)) return null;
49001
+ if (Result.isFailure(token)) throw toPublicError(token.failure, "getConvexJwt");
47727
49002
  return String(token.success.token);
47728
49003
  },
47729
49004
  ...input.fetch === void 0 ? {} : { fetch: input.fetch },
@@ -48341,7 +49616,7 @@ function stdioIdentity(env = process.env, options = {}) {
48341
49616
  }
48342
49617
  //#endregion
48343
49618
  //#region package.json
48344
- var version = "4.2.0-rc.9";
49619
+ var version = "4.20.0-beta.1";
48345
49620
  //#endregion
48346
49621
  //#region src/deps.ts
48347
49622
  const NO_IDENTITY = async () => void 0;