@capxul/sdk 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { _ as ClockPort, v as AuthClientPort } from "./signer-HY5Ju8xs.mjs";
1
+ import { _ as ClockPort, v as AuthClientPort } from "./signer-BpjdawiY.mjs";
2
2
  //#region src/adapters/auth-client/OAuthBearerAuthClient.d.ts
3
3
  interface OAuthBearerAuthClientInput {
4
4
  /** The exchange endpoint: `{authBaseUrl}/api/auth/agent/exchange`. */
@@ -1,4 +1,4 @@
1
- import { F as toEmail, I as toEpochMs, K as toSessionToken, L as toEpochSeconds, R as toJwtToken, Z as Errors, c as ClockError, k as toAuthUserId, l as ClockPortTag, t as readClockNow, u as authClientPortFromPromiseAdapter } from "./clock-0c5NM-TY.mjs";
1
+ import { F as toEmail, I as toEpochMs, K as toSessionToken, L as toEpochSeconds, R as toJwtToken, Z as Errors, c as ClockError, k as toAuthUserId, l as ClockPortTag, t as readClockNow, u as authClientPortFromPromiseAdapter } from "./clock-m_aHLaNm.mjs";
2
2
  import { Effect, Layer } from "effect";
3
3
  //#region src/adapters/clock/SystemClockAdapter.ts
4
4
  var SystemClockAdapter = class {
@@ -1,5 +1,41 @@
1
1
  import { concat, encodeAbiParameters, encodeFunctionData, encodePacked, getContractAddress, keccak256, padHex, stringToHex, toBytes, toEventSelector, toFunctionSelector, toFunctionSignature } from "viem";
2
2
  import { Context, Data, Effect, Layer, Result } from "effect";
3
+ //#region ../errors/src/secret-material.ts
4
+ const SENSITIVE_MATERIAL_PATTERNS = [
5
+ /(?:^|[^a-fA-F0-9])(?<!0[xX])[a-fA-F0-9]{64}(?:$|[^a-fA-F0-9])/u,
6
+ /eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/u,
7
+ /(?:(?:sk|pk|rk)_(?:live|test)|(?:phc|phx|ghp|gho|ghu|ghs|ghr|whsec)_|github_pat_|xox[baprs]-|AKIA[0-9A-Z]{16}|AIza)[A-Za-z0-9_-]*/iu,
8
+ /\bBearer\s+[^\s,;]+/iu,
9
+ /https?:\/\/[^\s"']+?\/v[0-9]\/[A-Za-z0-9_-]{16,}/iu,
10
+ /\b(?:api[ _-]?key|access[ _-]?token|token|secret|password|passphrase|private[ _-]?key|authorization|cookie|signature|request[ _-]?body|credential|otp|one[ _-]?time[ _-]?(?:password|code)|verification[ _-]?code)\b\s*[:=]\s*(?:"[^"]*"|'[^']*'|[^\s,;&]+)/iu
11
+ ];
12
+ const MASKS = [
13
+ [/\bBearer\s+[^\s,;]+/giu, "Bearer [REDACTED]"],
14
+ [/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/gu, "[REDACTED]"],
15
+ [/(^|[^a-fA-F0-9])(?<!0[xX])[a-fA-F0-9]{64}(?=$|[^a-fA-F0-9])/gu, "$1[REDACTED]"],
16
+ [/(?:(?:sk|pk|rk)_(?:live|test)|(?:phc|phx|ghp|gho|ghu|ghs|ghr|whsec)_|github_pat_|xox[baprs]-|AKIA[0-9A-Z]{16}|AIza)[A-Za-z0-9_-]*/giu, "[REDACTED]"],
17
+ [/(https?:\/\/[^\s"']+?\/v[0-9]\/)[A-Za-z0-9_-]{16,}/giu, "$1[REDACTED]"],
18
+ [/\b(api[ _-]?key|access[ _-]?token|token|secret|password|passphrase|private[ _-]?key|authorization|cookie|signature|request[ _-]?body|credential|otp|one[ _-]?time[ _-]?(?:password|code)|verification[ _-]?code)\b\s*[:=]\s*(?:"[^"]*"|'[^']*'|[^\s,;&]+)/giu, "$1=[REDACTED]"]
19
+ ];
20
+ /**
21
+ * Reject: does the value carry any known secret material? Best effort — callers
22
+ * drop the whole value on a match; a false negative is a leak, a false positive
23
+ * merely omits an observation field.
24
+ */
25
+ function containsSensitiveMaterial(value) {
26
+ return SENSITIVE_MATERIAL_PATTERNS.some((pattern) => pattern.test(value));
27
+ }
28
+ /**
29
+ * Mask: return the value with every known secret rewritten to `[REDACTED]`,
30
+ * preserving surrounding text. Used where a value must still be shown (log
31
+ * lines, provider messages, PostHog rows) but must not carry live credentials.
32
+ */
33
+ function redactSecrets(value) {
34
+ let text = value;
35
+ for (const [pattern, replacement] of MASKS) text = text.replace(pattern, replacement);
36
+ return text;
37
+ }
38
+ //#endregion
3
39
  //#region ../errors/src/chain-cause.ts
4
40
  /**
5
41
  * Why an operation failed — the one flat vocabulary. The first four are the
@@ -22,6 +58,9 @@ const FAILURE_MODES = [
22
58
  "insufficient-allowance",
23
59
  "rejected",
24
60
  "unauthorized",
61
+ "forbidden",
62
+ "bad-request",
63
+ "method-unsupported",
25
64
  "unknown"
26
65
  ];
27
66
  function isFailureMode(value) {
@@ -38,6 +77,33 @@ const CHAIN_UPSTREAMS = [
38
77
  function isChainUpstream(value) {
39
78
  return typeof value === "string" && CHAIN_UPSTREAMS.includes(value);
40
79
  }
80
+ /**
81
+ * The response headers that name a refusal or let provider support find the
82
+ * request. Nothing else crosses: a header can carry a session or a key.
83
+ */
84
+ const RESPONSE_HEADER_ALLOWLIST = [
85
+ "retry-after",
86
+ "www-authenticate",
87
+ "content-type",
88
+ "x-request-id",
89
+ "cf-ray",
90
+ "x-ratelimit-remaining",
91
+ "x-ratelimit-reset",
92
+ "server"
93
+ ];
94
+ const RESPONSE_HEADER_VALUE_MAX = 128;
95
+ /**
96
+ * The allow-listed headers of one answer, masked and bounded. `read` is
97
+ * `Headers.get` or a lookup into a decoded record. Empty when none is present.
98
+ */
99
+ function boundedResponseHeaders(read) {
100
+ const headers = {};
101
+ for (const name of RESPONSE_HEADER_ALLOWLIST) {
102
+ const value = read(name);
103
+ if (typeof value === "string" && value.length > 0) headers[name] = boundedEvidence(value, RESPONSE_HEADER_VALUE_MAX);
104
+ }
105
+ return headers;
106
+ }
41
107
  const PROVIDER_MESSAGE_MAX = 300;
42
108
  const METHOD_MAX = 64;
43
109
  const REVERT_NAME_MAX = 64;
@@ -45,19 +111,9 @@ const REVERT_ARG_KEY_MAX = 32;
45
111
  const REVERT_ARG_VALUE_MAX = 128;
46
112
  const REVERT_ARGS_MAX = 8;
47
113
  const SELECTOR_RE = /^0x(?:[0-9a-fA-F]{8})?$/u;
48
- const MASKS = [
49
- [/\bBearer\s+[^\s,;]+/giu, "Bearer [REDACTED]"],
50
- [/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/gu, "[REDACTED]"],
51
- [/\b0x[a-fA-F0-9]{40,}\b/gu, "[REDACTED]"],
52
- [/(^|[^a-fA-F0-9])[a-fA-F0-9]{64}(?=$|[^a-fA-F0-9])/gu, "$1[REDACTED]"],
53
- [/(?:(?:sk|pk|rk)_(?:live|test)|(?:phc|phx|ghp|gho|ghu|ghs|ghr|whsec)_|github_pat_|xox[baprs]-|AKIA[0-9A-Z]{16}|AIza)[A-Za-z0-9_-]*/giu, "[REDACTED]"],
54
- [/(https?:\/\/[^\s"']+?\/v[0-9]\/)[A-Za-z0-9_-]{16,}/giu, "$1[REDACTED]"],
55
- [/\b(api[ _-]?key|access[ _-]?token|token|secret|password|passphrase|private[ _-]?key|authorization|cookie|signature|credential|otp)\b\s*[:=]\s*(?:"[^"]*"|'[^']*'|[^\s,;&]+)/giu, "$1=[REDACTED]"]
56
- ];
57
114
  /** Mask credential-shaped content, then cap the length. Total on any string. */
58
115
  function boundedEvidence(value, max) {
59
- let text = value;
60
- for (const [pattern, replacement] of MASKS) text = text.replace(pattern, replacement);
116
+ const text = redactSecrets(value);
61
117
  return text.length <= max ? text : text.slice(0, max);
62
118
  }
63
119
  function isRecord(value) {
@@ -99,8 +155,36 @@ function decodeChainCause(value) {
99
155
  const revert = decodeRevert(value.revert);
100
156
  if (revert !== null) cause.revert = revert;
101
157
  if (typeof value.providerMessage === "string") cause.providerMessage = boundedEvidence(value.providerMessage, PROVIDER_MESSAGE_MAX);
158
+ const attempts = decodeAttempts(value.attempts);
159
+ if (attempts !== null) cause.attempts = attempts;
160
+ if (isRecord(value.responseHeaders)) {
161
+ const raw = value.responseHeaders;
162
+ const headers = boundedResponseHeaders((name) => {
163
+ const header = raw[name];
164
+ return typeof header === "string" ? header : null;
165
+ });
166
+ if (Object.keys(headers).length > 0) cause.responseHeaders = headers;
167
+ }
102
168
  return cause;
103
169
  }
170
+ /** The attempt sequence, bounded. A malformed entry drops the sequence, never the cause. */
171
+ function decodeAttempts(value) {
172
+ if (!Array.isArray(value)) return null;
173
+ const attempts = [];
174
+ for (const entry of value.slice(0, 5)) {
175
+ if (!isRecord(entry) || !isChainUpstream(entry.upstream) || !isFailureMode(entry.mode)) return null;
176
+ const duration = entry.durationMs;
177
+ if (typeof duration !== "number" || !Number.isFinite(duration) || duration < 0) return null;
178
+ attempts.push({
179
+ upstream: entry.upstream,
180
+ mode: entry.mode,
181
+ ...typeof entry.httpStatus === "number" && Number.isInteger(entry.httpStatus) ? { httpStatus: entry.httpStatus } : {},
182
+ ...typeof entry.rpcCode === "number" && Number.isInteger(entry.rpcCode) ? { rpcCode: entry.rpcCode } : {},
183
+ durationMs: Math.round(duration)
184
+ });
185
+ }
186
+ return attempts.length === 0 ? null : attempts;
187
+ }
104
188
  /**
105
189
  * The cause as flat PostHog properties. Same keys from every producer, so one
106
190
  * filter (`failure_mode = 'rate-limited'`) finds the gateway's, the backend's,
@@ -119,7 +203,15 @@ function chainCauseProperties(chain) {
119
203
  chain_revert_selector: chain.revert.selector,
120
204
  chain_revert_args: chain.revert.args
121
205
  },
122
- ...chain.providerMessage === void 0 ? {} : { chain_provider_message: chain.providerMessage }
206
+ ...chain.providerMessage === void 0 ? {} : { chain_provider_message: chain.providerMessage },
207
+ ...chain.attempts === void 0 ? {} : { chain_attempts: chain.attempts.map((attempt) => ({
208
+ upstream: attempt.upstream,
209
+ failure_mode: attempt.mode,
210
+ ...attempt.httpStatus === void 0 ? {} : { http_status: attempt.httpStatus },
211
+ ...attempt.rpcCode === void 0 ? {} : { rpc_code: attempt.rpcCode },
212
+ duration_ms: attempt.durationMs
213
+ })) },
214
+ ...chain.responseHeaders === void 0 ? {} : { chain_response_headers: chain.responseHeaders }
123
215
  };
124
216
  }
125
217
  /**
@@ -175,6 +267,18 @@ function revertSummaryText(revert) {
175
267
  return `${revert.name}(${args})`;
176
268
  }
177
269
  /**
270
+ * The evidence behind the one word, for an issue name: ` (HTTP 403)` or
271
+ * ` (JSON-RPC -32000)`. Empty when the cause carries neither. The status
272
+ * names the provider's front door and the code names the node's verdict;
273
+ * `forbidden` alone made an operator open the issue to learn it was a 403.
274
+ */
275
+ function chainEvidenceLabel(chain) {
276
+ if (chain === void 0) return "";
277
+ if (chain.httpStatus !== void 0 && chain.httpStatus !== 200) return ` (HTTP ${String(chain.httpStatus)})`;
278
+ if (chain.rpcCode !== void 0) return ` (JSON-RPC ${String(chain.rpcCode)})`;
279
+ return "";
280
+ }
281
+ /**
178
282
  * The error-tracking fingerprint for a failure: the code, the one-word cause,
179
283
  * and the operation. PostHog groups `$exception` rows by this value, so one
180
284
  * issue is one cause at one operation instead of every provider failure in
@@ -1753,4 +1857,4 @@ async function readClockNow(clock) {
1753
1857
  }
1754
1858
  }
1755
1859
  //#endregion
1756
- export { CHAIN_UPSTREAMS as $, toBudgetId as A, toOrgId as B, ZERO_BYTES32 as C, toAllowedOrigin as D, toAddress as E, toEmail as F, toRoleKey as G, toPayrollGroupId as H, toEpochMs as I, CAPXUL_ERROR_CODES as J, toSessionToken as K, toEpochSeconds as L, toCountryCode as M, toCurrencyCode as N, toAppId as O, toDurationMs as P, isCapxulError as Q, toJwtToken as R, WEI_RE as S, toAccountId as T, toPayrollRunId as U, toPartyId as V, toPublishableKey as W, EXPECTED_OPERATION_OUTCOMES as X, CapxulError as Y, Errors as Z, deriveCapxulSafeAddress as _, parseCachedJwt as a, revertSummaryText as at, EVM_ADDRESS_RE as b, ClockError as c, AuthClientError as d, FAILURE_MODES as et, AuthClientPortTag as f, BASE_SEPOLIA_CHAIN_ID as g, normalizeBindingEmail as h, parseAuthSession as i, isFailureMode as it, toChainId as j, toAuthUserId as k, ClockPortTag as l, CAPXUL_PAYMENTS_V2_ADDRESS as m, InMemoryAuthCacheAdapter as n, decodeChainCause as nt, AuthCacheError as o, orgRoleKeyForLabel as p, decodeConvexError as q, BrowserAuthCacheAdapter as r, failureFingerprint as rt, AuthCachePortTag as s, readClockNow as t, chainCauseProperties as tt, authClientPortFromPromiseAdapter as u, APP_ID_RE as v, currencySymbolFor as w, SUPPORTED_CURRENCY_CODES as x, BYTES32_RE as y, toKycTier as z };
1860
+ export { CHAIN_UPSTREAMS as $, toBudgetId as A, toOrgId as B, ZERO_BYTES32 as C, toAllowedOrigin as D, toAddress as E, toEmail as F, toRoleKey as G, toPayrollGroupId as H, toEpochMs as I, CAPXUL_ERROR_CODES as J, toSessionToken as K, toEpochSeconds as L, toCountryCode as M, toCurrencyCode as N, toAppId as O, toDurationMs as P, isCapxulError as Q, toJwtToken as R, WEI_RE as S, toAccountId as T, toPayrollRunId as U, toPartyId as V, toPublishableKey as W, EXPECTED_OPERATION_OUTCOMES as X, CapxulError as Y, Errors as Z, deriveCapxulSafeAddress as _, parseCachedJwt as a, failureFingerprint as at, EVM_ADDRESS_RE as b, ClockError as c, revertSummaryText as ct, AuthClientError as d, FAILURE_MODES as et, AuthClientPortTag as f, BASE_SEPOLIA_CHAIN_ID as g, normalizeBindingEmail as h, parseAuthSession as i, decodeChainCause as it, toChainId as j, toAuthUserId as k, ClockPortTag as l, containsSensitiveMaterial as lt, CAPXUL_PAYMENTS_V2_ADDRESS as m, InMemoryAuthCacheAdapter as n, chainCauseProperties as nt, AuthCacheError as o, isChainUpstream as ot, orgRoleKeyForLabel as p, decodeConvexError as q, BrowserAuthCacheAdapter as r, chainEvidenceLabel as rt, AuthCachePortTag as s, isFailureMode as st, readClockNow as t, boundedResponseHeaders as tt, authClientPortFromPromiseAdapter as u, redactSecrets as ut, APP_ID_RE as v, currencySymbolFor as w, SUPPORTED_CURRENCY_CODES as x, BYTES32_RE as y, toKycTier as z };
@@ -1,4 +1,4 @@
1
- import { $ as CHAIN_UPSTREAMS, A as toBudgetId, B as toOrgId, C as ZERO_BYTES32, E as toAddress, F as toEmail, G as toRoleKey, H as toPayrollGroupId, J as CAPXUL_ERROR_CODES, M as toCountryCode, N as toCurrencyCode, Q as isCapxulError, S as WEI_RE, T as toAccountId, U as toPayrollRunId, V as toPartyId, X as EXPECTED_OPERATION_OUTCOMES, Y as CapxulError, Z as Errors, _ as deriveCapxulSafeAddress, at as revertSummaryText, b as EVM_ADDRESS_RE$1, et as FAILURE_MODES$1, it as isFailureMode, j as toChainId, k as toAuthUserId, n as InMemoryAuthCacheAdapter, nt as decodeChainCause, p as orgRoleKeyForLabel, r as BrowserAuthCacheAdapter, rt as failureFingerprint, tt as chainCauseProperties, v as APP_ID_RE, x as SUPPORTED_CURRENCY_CODES, y as BYTES32_RE } from "./clock-0c5NM-TY.mjs";
1
+ import { $ as CHAIN_UPSTREAMS, A as toBudgetId, B as toOrgId, C as ZERO_BYTES32, E as toAddress, F as toEmail, G as toRoleKey, H as toPayrollGroupId, J as CAPXUL_ERROR_CODES, M as toCountryCode, N as toCurrencyCode, Q as isCapxulError, S as WEI_RE, T as toAccountId, U as toPayrollRunId, V as toPartyId, X as EXPECTED_OPERATION_OUTCOMES, Y as CapxulError, Z as Errors, _ as deriveCapxulSafeAddress, at as failureFingerprint, b as EVM_ADDRESS_RE$1, ct as revertSummaryText, et as FAILURE_MODES$1, it as decodeChainCause, j as toChainId, k as toAuthUserId, lt as containsSensitiveMaterial, n as InMemoryAuthCacheAdapter, nt as chainCauseProperties, ot as isChainUpstream, p as orgRoleKeyForLabel, r as BrowserAuthCacheAdapter, rt as chainEvidenceLabel, st as isFailureMode, tt as boundedResponseHeaders, ut as redactSecrets, v as APP_ID_RE, x as SUPPORTED_CURRENCY_CODES, y as BYTES32_RE } from "./clock-m_aHLaNm.mjs";
2
2
  import { formatUnits, keccak256, parseUnits, recoverAddress, toBytes } from "viem";
3
3
  import { Context, Data, Deferred, Duration, Effect, Exit, Fiber, Layer, Queue, Ref, Result, Schema, SchemaGetter, Scope } from "effect";
4
4
  import { makeFunctionReference } from "convex/server";
@@ -19,7 +19,10 @@ const DEFINITIVE_PROVIDER_MODES = /* @__PURE__ */ new Set([
19
19
  "insufficient-balance",
20
20
  "insufficient-allowance",
21
21
  "rejected",
22
- "unauthorized"
22
+ "unauthorized",
23
+ "forbidden",
24
+ "bad-request",
25
+ "method-unsupported"
23
26
  ]);
24
27
  const isRetryable = (failure) => {
25
28
  if (failure.code === "CANCELLED" || failure.code === "SIGNER_REJECTED") return true;
@@ -2018,33 +2021,6 @@ Schema.Struct({
2018
2021
  @media (max-width:540px){.capxul-doc{padding:1.75rem 1.25rem}.capxul-doc .doc-parties{grid-template-columns:1fr}}
2019
2022
  `.trim();
2020
2023
  //#endregion
2021
- //#region ../wire/src/secret-material.ts
2022
- const SENSITIVE_MATERIAL_PATTERNS = [
2023
- /0x[a-fA-F0-9]{40,}/u,
2024
- /(?:^|[^a-fA-F0-9])[a-fA-F0-9]{64}(?:$|[^a-fA-F0-9])/u,
2025
- /eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/u,
2026
- /(?:(?:sk|pk|rk)_(?:live|test)|(?:phc|phx|ghp|gho|ghu|ghs|ghr|whsec)_|github_pat_|xox[baprs]-|AKIA[0-9A-Z]{16}|AIza)[A-Za-z0-9_-]*/iu
2027
- ];
2028
- const RAW_PRIVATE_KEY_MASK = /(^|[^a-fA-F0-9])[a-fA-F0-9]{64}(?=$|[^a-fA-F0-9])/gu;
2029
- const KNOWN_CREDENTIAL_MASK = /(?:(?:sk|pk|rk)_(?:live|test)|(?:phc|phx|ghp|gho|ghu|ghs|ghr|whsec)_|github_pat_|xox[baprs]-|AKIA[0-9A-Z]{16}|AIza)[A-Za-z0-9_-]*/giu;
2030
- const LABELLED_SECRET_MASK = /\b(api[ _-]?key|access[ _-]?token|token|secret|password|passphrase|private[ _-]?key|authorization|cookie|signature|request[ _-]?body|credential|otp|one[ _-]?time[ _-]?(?:password|code)|verification[ _-]?code)\b\s*[:=]\s*(?:"[^"]*"|'[^']*'|[^\s,;]+)/giu;
2031
- /**
2032
- * Reject: does the value carry any known secret material? Best effort — callers
2033
- * drop the whole value on a match; a false negative is a leak, a false positive
2034
- * merely omits an observation field.
2035
- */
2036
- function containsSensitiveMaterial(value) {
2037
- return SENSITIVE_MATERIAL_PATTERNS.some((pattern) => pattern.test(value));
2038
- }
2039
- /**
2040
- * Mask: return the value with every known secret rewritten to `[REDACTED]`,
2041
- * preserving surrounding text. Used where a value must still be shown (log
2042
- * lines, issue bodies) but must not carry live credentials.
2043
- */
2044
- function redactSecrets(value) {
2045
- return value.replace(/\bBearer\s+[^\s,;]+/giu, "Bearer [REDACTED]").replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/gu, "[REDACTED]").replace(/\b0x[a-fA-F0-9]{40,}\b/gu, "[REDACTED]").replace(RAW_PRIVATE_KEY_MASK, "$1[REDACTED]").replace(KNOWN_CREDENTIAL_MASK, "[REDACTED]").replace(LABELLED_SECRET_MASK, (_match, label) => `${label}=[REDACTED]`);
2046
- }
2047
- //#endregion
2048
2024
  //#region ../wire/src/observation-context.ts
2049
2025
  /** Single bounded HTTP carrier used before a Convex action envelope exists. */
2050
2026
  const OBSERVATION_CONTEXT_HEADER = "x-capxul-observation-context";
@@ -3614,7 +3590,7 @@ const moneyExecutionContract = {
3614
3590
  };
3615
3591
  //#endregion
3616
3592
  //#region package.json
3617
- var version = "3.0.0";
3593
+ var version = "3.1.0";
3618
3594
  //#endregion
3619
3595
  //#region src/telemetry/exception-projection.ts
3620
3596
  /** Fixed fallback for failures that have no safe message. */
@@ -3626,6 +3602,10 @@ function projectSdkException(input) {
3626
3602
  const errorKind = normalizeExceptionErrorKind(input.errorKind);
3627
3603
  const mode = isFailureMode(input.failureMode) ? input.failureMode : void 0;
3628
3604
  const message = input.message ?? "Capxul SDK operation failed";
3605
+ const evidence = mode === void 0 ? "" : chainEvidenceLabel({
3606
+ httpStatus: input.chainHttpStatus,
3607
+ rpcCode: input.chainRpcCode
3608
+ });
3629
3609
  const where = input.chainUpstream === void 0 ? "" : ` on ${input.chainUpstream}${input.chainMethod === void 0 ? "" : ` during ${input.chainMethod}`}`;
3630
3610
  return {
3631
3611
  ...input.properties,
@@ -3637,13 +3617,19 @@ function projectSdkException(input) {
3637
3617
  $exception_message: message,
3638
3618
  $exception_level: "error",
3639
3619
  $exception_fingerprint: failureFingerprint(errorKind, mode, operation),
3640
- $issue_name: `${errorKind}${mode === void 0 ? "" : `: ${mode}`}${where} (${operation})`,
3620
+ $issue_name: `${errorKind}${mode === void 0 ? "" : `: ${mode}`}${evidence}${where} (${operation})`,
3641
3621
  $issue_description: input.issueDescription ?? message,
3642
3622
  $exception_list: input.exceptionList
3643
3623
  };
3644
3624
  }
3625
+ /**
3626
+ * `:` and `/` are allowed because this also accepts a `provider_operation`
3627
+ * fallback, and a Convex function names itself `dir/file:function`. Kept byte-
3628
+ * equal to the backend's own `normalizeOperation`: when the two disagree, one
3629
+ * side silently rewrites the operation to `unknown` and the issue loses its name.
3630
+ */
3645
3631
  function normalizeExceptionOperation(value) {
3646
- return typeof value === "string" && /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,255}$/u.test(value) ? value : "unknown";
3632
+ return typeof value === "string" && /^[A-Za-z0-9_][A-Za-z0-9_.:/-]{0,255}$/u.test(value) ? value : "unknown";
3647
3633
  }
3648
3634
  function normalizeExceptionErrorKind(value) {
3649
3635
  return safeExceptionLabel(value) ?? "UNKNOWN";
@@ -3908,6 +3894,8 @@ function projectFailureException(failure, properties) {
3908
3894
  issueDescription: issueDescription(failure, message),
3909
3895
  ...failure.detail?.chain_upstream === void 0 ? {} : { chainUpstream: failure.detail.chain_upstream },
3910
3896
  ...failure.detail?.chain_method === void 0 ? {} : { chainMethod: failure.detail.chain_method },
3897
+ ...failure.detail?.chain_http_status === void 0 ? {} : { chainHttpStatus: failure.detail.chain_http_status },
3898
+ ...failure.detail?.chain_rpc_code === void 0 ? {} : { chainRpcCode: failure.detail.chain_rpc_code },
3911
3899
  exceptionList: [{
3912
3900
  type: failure.errorKind,
3913
3901
  value: message,
@@ -3932,8 +3920,8 @@ function projectFailureException(failure, properties) {
3932
3920
  });
3933
3921
  }
3934
3922
  /**
3935
- * The decoded revert leads the description: the provider's own message masks
3936
- * the raw revert data (`reason: [REDACTED]`), and the name plus arguments
3923
+ * The decoded revert leads the description: the provider's own message carries
3924
+ * the raw revert data (`reason: 0x…`), and the name plus arguments
3937
3925
  * (`Error(message=GS205)`) is what tells one revert from another.
3938
3926
  */
3939
3927
  function issueDescription(failure, message) {
@@ -4093,13 +4081,13 @@ function observationError(cause) {
4093
4081
  function sanitizeChainDetail(source, detail) {
4094
4082
  for (const key of [
4095
4083
  "provider",
4096
- "provider_operation",
4097
4084
  "failure_mode",
4098
4085
  "reason"
4099
4086
  ]) {
4100
4087
  const value = source[key];
4101
4088
  if (typeof value === "string" && ENUMERATED_DETAIL_RE.test(value)) detail[key] = value;
4102
4089
  }
4090
+ sanitizeProviderOperation(source, detail);
4103
4091
  sanitizeProviderErrorDetail(source, detail);
4104
4092
  const status = source.http_status;
4105
4093
  if (typeof status === "number" && Number.isInteger(status) && status >= 100 && status <= 599) detail.http_status = status;
@@ -4128,6 +4116,43 @@ function sanitizeChainDetail(source, detail) {
4128
4116
  }
4129
4117
  const providerMessage = evidenceText(source.chain_provider_message);
4130
4118
  if (providerMessage !== void 0) detail.chain_provider_message = providerMessage;
4119
+ sanitizeChainAttempts(source, detail);
4120
+ }
4121
+ /** One flat attempt entry, or undefined when a field is not what the vocabulary allows. */
4122
+ function sanitizeChainAttempt(entry) {
4123
+ if (!isPlainObject(entry)) return void 0;
4124
+ const duration = entry.duration_ms;
4125
+ if (!isChainUpstream(entry.upstream) || !isFailureMode(entry.failure_mode) || typeof duration !== "number" || !Number.isFinite(duration) || duration < 0) return;
4126
+ return {
4127
+ upstream: entry.upstream,
4128
+ failure_mode: entry.failure_mode,
4129
+ ...typeof entry.http_status === "number" && Number.isInteger(entry.http_status) ? { http_status: entry.http_status } : {},
4130
+ ...typeof entry.rpc_code === "number" && Number.isInteger(entry.rpc_code) ? { rpc_code: entry.rpc_code } : {},
4131
+ duration_ms: Math.round(duration)
4132
+ };
4133
+ }
4134
+ /**
4135
+ * The attempt sequence and the refusing upstream's headers (#1818), same
4136
+ * bounds as the backend; a malformed entry is dropped, never invented.
4137
+ */
4138
+ function sanitizeChainAttempts(source, detail) {
4139
+ if (Array.isArray(source.chain_attempts)) {
4140
+ const attempts = source.chain_attempts.slice(0, 5).map(sanitizeChainAttempt);
4141
+ if (attempts.length > 0 && attempts.every((attempt) => attempt !== void 0)) detail.chain_attempts = attempts;
4142
+ }
4143
+ if (isPlainObject(source.chain_response_headers)) {
4144
+ const raw = source.chain_response_headers;
4145
+ const headers = boundedResponseHeaders((name) => {
4146
+ const value = raw[name];
4147
+ return typeof value === "string" ? value : null;
4148
+ });
4149
+ if (Object.keys(headers).length > 0) detail.chain_response_headers = headers;
4150
+ }
4151
+ }
4152
+ /** Its own check because a provider operation carries that provider's separators. */
4153
+ function sanitizeProviderOperation(source, detail) {
4154
+ const value = source.provider_operation;
4155
+ if (typeof value === "string" && PROVIDER_OPERATION_RE.test(value)) detail.provider_operation = value;
4131
4156
  }
4132
4157
  function sanitizeProviderErrorDetail(source, detail) {
4133
4158
  if (source.provider_error_source !== "openfort") return;
@@ -4173,14 +4198,25 @@ function sanitizeFailureDetail(input) {
4173
4198
  }
4174
4199
  /** Enumerated codes only (`convex`, `no-secure-context`, `bootstrap-encode`); free text fails. */
4175
4200
  const ENUMERATED_DETAIL_RE = /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/u;
4201
+ /**
4202
+ * A provider operation NAMES A FUNCTION on the provider, so it carries that
4203
+ * provider's separators. Convex writes its own as `dir/file:function`
4204
+ * (`media:setProfileImage`), and the enumerated shape above rejects both `:`
4205
+ * and `/` — so the operation was dropped and the PostHog issue lost the name of
4206
+ * what actually failed (staging, 2026-09-03). This widens the separators, not
4207
+ * the class: no spaces, same bound, and it matches the backend's own
4208
+ * `normalizeOperation`, which has always accepted them.
4209
+ */
4210
+ const PROVIDER_OPERATION_RE = /^[A-Za-z][A-Za-z0-9_.:/-]{0,63}$/u;
4176
4211
  const TARGET_HOST_RE = /^[A-Za-z0-9.[\]:-]{1,253}$/u;
4177
4212
  /** A four-byte revert selector, or the bare `0x` of an empty revert. */
4178
4213
  const REVERT_SELECTOR_RE = /^0x(?:[0-9a-fA-F]{8})?$/u;
4179
4214
  function normalizeSdkVersion(value) {
4180
4215
  return typeof value === "string" && /^[A-Za-z0-9][A-Za-z0-9.+_-]{0,127}$/u.test(value) ? value : "unknown";
4181
4216
  }
4217
+ /** Same shape as the backend's `normalizeOperation`: a Convex path (`dir/file:fn`) survives. */
4182
4218
  function normalizeOperation(value) {
4183
- return typeof value === "string" && /^[A-Za-z0-9_][A-Za-z0-9_.-]{0,255}$/u.test(value) ? value : "unknown";
4219
+ return typeof value === "string" && /^[A-Za-z0-9_][A-Za-z0-9_.:/-]{0,255}$/u.test(value) ? value : "unknown";
4184
4220
  }
4185
4221
  function normalizeErrorKind(value) {
4186
4222
  return typeof value === "string" && /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/u.test(value) ? value : "Error";
@@ -5462,6 +5498,29 @@ const TelemetryEnvelopeProps = {
5462
5498
  */
5463
5499
  const FailureModeSchema = Schema.Literals(FAILURE_MODES$1);
5464
5500
  const ChainUpstreamSchema = Schema.Literals(CHAIN_UPSTREAMS);
5501
+ /**
5502
+ * The refusing upstream's response headers (#1818): exactly the names in
5503
+ * `RESPONSE_HEADER_ALLOWLIST`, each optional, so the catalog itself refuses a
5504
+ * `set-cookie` or `authorization` that a producer let through.
5505
+ */
5506
+ const ResponseHeadersSchema = Schema.Struct({
5507
+ "retry-after": OptionalString,
5508
+ "www-authenticate": OptionalString,
5509
+ "content-type": OptionalString,
5510
+ "x-request-id": OptionalString,
5511
+ "cf-ray": OptionalString,
5512
+ "x-ratelimit-remaining": OptionalString,
5513
+ "x-ratelimit-reset": OptionalString,
5514
+ server: OptionalString
5515
+ });
5516
+ /** One upstream tried, flat (#1818): the `chain_attempts` entry on every producer's row. */
5517
+ const ChainAttemptSchema = Schema.Struct({
5518
+ upstream: ChainUpstreamSchema,
5519
+ failure_mode: FailureModeSchema,
5520
+ http_status: Schema.optional(Schema.Number),
5521
+ rpc_code: Schema.optional(Schema.Number),
5522
+ duration_ms: Schema.Number
5523
+ });
5465
5524
  const ChainCauseTelemetryProps = {
5466
5525
  failure_mode: Schema.optional(FailureModeSchema),
5467
5526
  chain_upstream: Schema.optional(ChainUpstreamSchema),
@@ -5472,7 +5531,9 @@ const ChainCauseTelemetryProps = {
5472
5531
  chain_revert_name: OptionalString,
5473
5532
  chain_revert_selector: OptionalString,
5474
5533
  chain_revert_args: Schema.optional(Schema.Record(Schema.String, Schema.String)),
5475
- chain_provider_message: OptionalString
5534
+ chain_provider_message: OptionalString,
5535
+ chain_attempts: Schema.optional(Schema.Array(ChainAttemptSchema)),
5536
+ chain_response_headers: Schema.optional(ResponseHeadersSchema)
5476
5537
  };
5477
5538
  /**
5478
5539
  * Stamp the required envelope onto an event at its emit boundary. Call-site
@@ -5590,7 +5651,8 @@ const ChainUpstreamFallbackProps = Schema.Struct({
5590
5651
  failure_mode: FailureModeSchema,
5591
5652
  exhausted: Schema.Boolean,
5592
5653
  http_status: Schema.optional(Schema.Number),
5593
- rpc_code: Schema.optional(Schema.Number)
5654
+ rpc_code: Schema.optional(Schema.Number),
5655
+ response_headers: Schema.optional(ResponseHeadersSchema)
5594
5656
  });
5595
5657
  const BundlerErrorProps = Schema.Struct({
5596
5658
  ...TelemetryEnvelopeProps,
@@ -8117,10 +8179,18 @@ async function recordSetupFailure(setup, orgId, error, retryable, observationSou
8117
8179
  if (!scopedRecorded.ok) return scopedRecorded;
8118
8180
  return scopedRecorded;
8119
8181
  }
8182
+ /**
8183
+ * ADR-0023 R7: the layer that saw the chain failure says whether the same
8184
+ * request can succeed later; its word wins over the mode table. The table is
8185
+ * the one `identity/model.ts` uses, so the two gates read one list
8186
+ * (`org.account.get` retried a `transient: false` 403 in 1.5 s, 2026-09-03).
8187
+ */
8120
8188
  function isRetryableProviderFailure(error) {
8121
- const mode = error.details?.failure_mode;
8122
- return mode !== "auth-origin-mismatch" && mode !== "app-env-allowlist" && mode !== "no-secure-context";
8189
+ if (error.chain !== void 0) return error.chain.retryable;
8190
+ const mode = error.mode ?? error.details?.failure_mode;
8191
+ return !DEFINITIVE_PROVIDER_MODES.has(isFailureMode(mode) ? mode : void 0);
8123
8192
  }
8193
+ /** @internal Exported for the retry-gate test; the surface calls it from the setup loop. */
8124
8194
  function isRetryableOrganizationSetupFailure(error) {
8125
8195
  if (error.code === "CANCELLED" || error.code === "SIGNER_REJECTED") return true;
8126
8196
  if (error.code === "PROVIDER_ERROR") return isRetryableProviderFailure(error);
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
- import { $ as RoleKey, D as Address, E as AccountId, F as BudgetId, G as PartyId, J as PayrollRunId, L as CountryCode, M as AuthSession, N as AuthUserId, T as Account, U as Money, W as OrgId, _t as FailureMode, a as SignerStatusStore, at as toCountryCode, c as AccountProviderSource, ct as CapxulErrorCode, d as eip1193AccountProvider, f as localPrivateKeyAccountProvider, ft as isCapxulError, g as SmartAccount, gt as FAILURE_MODES, h as Session, ht as ChainUpstream, i as SignerStatus, it as toAddress, l as AccountRequirement, lt as CapxulErrorDetails, m as Profile, mt as ChainCause, n as CapxulSigner, o as injectedWalletSigner, ot as CAPXUL_ERROR_CODES, p as CapxulResult, pt as CHAIN_UPSTREAMS, q as PayrollGroupId, r as Eip1193RequestProvider, s as AccountProvider, st as CapxulError, t as CapxulDigestSigner, u as Eip1193Provider, ut as Errors, vt as RevertSummary, w as CAPXUL_PAYMENTS_V2_ADDRESS } from "./signer-HY5Ju8xs.mjs";
2
- import { $ as OrganizationPaymentsMethods, $n as OrgLane, $t as MeMethods, A as OrgView, An as AccountSetupStep, At as ActivityFilter, B as PayrollGroupInput, Bn as SubmittedPermissionExecution, Bt as ActivitySummaryTotal, C as DetectPendingOrgInvitationsResult, Cn as RecipientResolutionKind, Ct as AddressBookMethods, D as OrgMethods, Dn as TargetsMethods, Dt as ActivityAnnotation, E as MemberView, En as TargetReference, Et as InboxMethods, F as RoleSpendCap, Fn as OrgLifecycle, Ft as ActivityPage, G as PayrollRun, Gn as RequestStatus, Gt as DestinationKind, H as PayrollGroupsMethods, Hn as PAYMENT_DIRECTIONS, Ht as DepositInstructions, I as RoleView, In as OrgSetupStep, It as ActivityRange, J as PayrollRuns, Jt as DestinationRail, K as PayrollRunItemInput, Kt as DestinationListInput, L as AuthorizeRunInput, Ln as ActorRef, Lt as ActivityReference, M as OrganizationAuditLogItem, Mn as IdentityMethods, Mt as ActivityKind, N as ResendInviteTokenInput, Nn as SmartAccountMethods, Nt as ActivityListParams, O as OrgScopedMethods, On as fingerprintPaymentIntent, Ot as ActivityAnnotationInput, P as RoleDefinition, Pn as AuthMethods, Pt as ActivityMethods, Q as OrganizationPaymentItemInput, Qn as IdentityState, Qt as HandlesMethods, R as PayrollEngagementTerms, Rn as CurrentHoldings, Rt as ActivitySummary, S as CreateOrgInput, Sn as RecipientResolution, St as AddressBookLabelInput, T as MemberStatus, Tn as ResolvedTarget, Tt as InboxItem, U as PayrollMethods, Un as PAYMENT_STATUSES, Ut as Destination, V as PayrollGroupMember, Vn as InboxStatus, Vt as ActorReference, W as PayrollOptions, Wn as PaymentStatus$1, Wt as DestinationAddInput, X as OrganizationPaymentBatchInput, Xn as Destination$1, Xt as DestinationsMethods, Y as PayrollTermsUnit, Yn as TelemetryPort, Yt as DestinationRemoveInput, Z as OrganizationPaymentInput, Zn as IdentityEvent, Zt as FinancialOpsMethods, _ as SystemMethods, _n as PaymentStatus, _t as ActorRequest, a as SdkFailureObservation, an as OfframpStatus, ar as IdentityTransition, at as PermissionReplaceInput, b as CurrentUserContext, bn as PaymentsMethods, bt as AddressBookAddInput, c as PostHogObservabilityOptions, cn as Payment, ct as Budget, d as CreateCapxulClientInput, dn as PaymentDocumentKind, dt as OrgMeOptions, en as MeProfile, er as Readiness, et as PermissionAssignInput, f as IdentityProfileDetails, fn as PaymentDocumentRef, ft as AccountsMethods, g as Holding, gn as PaymentMoney, gt as ActorRelationshipMethods, h as HoldingsMethods, hn as PaymentDocumentsMethods, ht as ActorProfileMethods, i as ObservationDelivery, in as OfframpQuoteInput, ir as isRestoring, it as PermissionOptions, j as OrganizationAccount, jn as isSettingUpLifecycle, jt as ActivityItem, k as OrgTemplate, kn as AccountLifecycle, kt as ActivityDetail, l as postHogObservability, ln as PaymentActivityEvidence, lt as OrgMe, m as IdentityRuntimeSendResult, mn as PaymentDocumentVerification, mt as ActorProfile, n as ObservationAdapter, nn as OfframpMethods, nr as destination, nt as PermissionCreateInput, o as HostObservability, on as Payee, or as InvocationControls, ot as PermissionRevokeInput, p as IdentityRuntime, pn as PaymentDocumentRender, pt as AccountMethods, q as PayrollRunStatus, qt as DestinationPayload, r as ObservationContext, rn as OfframpQuote, rr as isClaimed, rt as PermissionMethods, s as PostHogObservabilityClient, sn as PayeesMethods, st as PermissionReadResult, t as CAPXUL_SDK_EXPECTED_OUTCOME_EVENT, tn as MovementActivityEvidence, tr as StateLabel, tt as PermissionChangeInput, u as CapxulClient, un as PaymentDirection, ut as OrgMeMethod, v as SystemHealth, vn as PaymentTiming, vt as ActorRequestIssueInput, w as InviteMemberInput, wn as Ref, wt as InboxApproveInput, x as CurrentUserMethods, xn as PaymentsPayInput, xt as AddressBookEntry, y as MediaMethods, yn as PaymentType, yt as ActorRequestsMethods, z as PayrollGroup, zn as Permission, zt as ActivitySummaryParams } from "./observation-CR3_haBB.mjs";
3
- import "./OAuthBearerAuthClient-GkHnGqNi.mjs";
1
+ import { $ as RoleKey, D as Address, E as AccountId, F as BudgetId, G as PartyId, J as PayrollRunId, L as CountryCode, M as AuthSession, N as AuthUserId, T as Account, U as Money, W as OrgId, _t as FailureMode, a as SignerStatusStore, at as toCountryCode, c as AccountProviderSource, ct as CapxulErrorCode, d as eip1193AccountProvider, f as localPrivateKeyAccountProvider, ft as isCapxulError, g as SmartAccount, gt as FAILURE_MODES, h as Session, ht as ChainUpstream, i as SignerStatus, it as toAddress, l as AccountRequirement, lt as CapxulErrorDetails, m as Profile, mt as ChainCause, n as CapxulSigner, o as injectedWalletSigner, ot as CAPXUL_ERROR_CODES, p as CapxulResult, pt as CHAIN_UPSTREAMS, q as PayrollGroupId, r as Eip1193RequestProvider, s as AccountProvider, st as CapxulError, t as CapxulDigestSigner, u as Eip1193Provider, ut as Errors, vt as RevertSummary, w as CAPXUL_PAYMENTS_V2_ADDRESS } from "./signer-BpjdawiY.mjs";
2
+ import { $ as OrganizationPaymentsMethods, $n as OrgLane, $t as MeMethods, A as OrgView, An as AccountSetupStep, At as ActivityFilter, B as PayrollGroupInput, Bn as SubmittedPermissionExecution, Bt as ActivitySummaryTotal, C as DetectPendingOrgInvitationsResult, Cn as RecipientResolutionKind, Ct as AddressBookMethods, D as OrgMethods, Dn as TargetsMethods, Dt as ActivityAnnotation, E as MemberView, En as TargetReference, Et as InboxMethods, F as RoleSpendCap, Fn as OrgLifecycle, Ft as ActivityPage, G as PayrollRun, Gn as RequestStatus, Gt as DestinationKind, H as PayrollGroupsMethods, Hn as PAYMENT_DIRECTIONS, Ht as DepositInstructions, I as RoleView, In as OrgSetupStep, It as ActivityRange, J as PayrollRuns, Jt as DestinationRail, K as PayrollRunItemInput, Kt as DestinationListInput, L as AuthorizeRunInput, Ln as ActorRef, Lt as ActivityReference, M as OrganizationAuditLogItem, Mn as IdentityMethods, Mt as ActivityKind, N as ResendInviteTokenInput, Nn as SmartAccountMethods, Nt as ActivityListParams, O as OrgScopedMethods, On as fingerprintPaymentIntent, Ot as ActivityAnnotationInput, P as RoleDefinition, Pn as AuthMethods, Pt as ActivityMethods, Q as OrganizationPaymentItemInput, Qn as IdentityState, Qt as HandlesMethods, R as PayrollEngagementTerms, Rn as CurrentHoldings, Rt as ActivitySummary, S as CreateOrgInput, Sn as RecipientResolution, St as AddressBookLabelInput, T as MemberStatus, Tn as ResolvedTarget, Tt as InboxItem, U as PayrollMethods, Un as PAYMENT_STATUSES, Ut as Destination, V as PayrollGroupMember, Vn as InboxStatus, Vt as ActorReference, W as PayrollOptions, Wn as PaymentStatus$1, Wt as DestinationAddInput, X as OrganizationPaymentBatchInput, Xn as Destination$1, Xt as DestinationsMethods, Y as PayrollTermsUnit, Yn as TelemetryPort, Yt as DestinationRemoveInput, Z as OrganizationPaymentInput, Zn as IdentityEvent, Zt as FinancialOpsMethods, _ as SystemMethods, _n as PaymentStatus, _t as ActorRequest, a as SdkFailureObservation, an as OfframpStatus, ar as IdentityTransition, at as PermissionReplaceInput, b as CurrentUserContext, bn as PaymentsMethods, bt as AddressBookAddInput, c as PostHogObservabilityOptions, cn as Payment, ct as Budget, d as CreateCapxulClientInput, dn as PaymentDocumentKind, dt as OrgMeOptions, en as MeProfile, er as Readiness, et as PermissionAssignInput, f as IdentityProfileDetails, fn as PaymentDocumentRef, ft as AccountsMethods, g as Holding, gn as PaymentMoney, gt as ActorRelationshipMethods, h as HoldingsMethods, hn as PaymentDocumentsMethods, ht as ActorProfileMethods, i as ObservationDelivery, in as OfframpQuoteInput, ir as isRestoring, it as PermissionOptions, j as OrganizationAccount, jn as isSettingUpLifecycle, jt as ActivityItem, k as OrgTemplate, kn as AccountLifecycle, kt as ActivityDetail, l as postHogObservability, ln as PaymentActivityEvidence, lt as OrgMe, m as IdentityRuntimeSendResult, mn as PaymentDocumentVerification, mt as ActorProfile, n as ObservationAdapter, nn as OfframpMethods, nr as destination, nt as PermissionCreateInput, o as HostObservability, on as Payee, or as InvocationControls, ot as PermissionRevokeInput, p as IdentityRuntime, pn as PaymentDocumentRender, pt as AccountMethods, q as PayrollRunStatus, qt as DestinationPayload, r as ObservationContext, rn as OfframpQuote, rr as isClaimed, rt as PermissionMethods, s as PostHogObservabilityClient, sn as PayeesMethods, st as PermissionReadResult, t as CAPXUL_SDK_EXPECTED_OUTCOME_EVENT, tn as MovementActivityEvidence, tr as StateLabel, tt as PermissionChangeInput, u as CapxulClient, un as PaymentDirection, ut as OrgMeMethod, v as SystemHealth, vn as PaymentTiming, vt as ActorRequestIssueInput, w as InviteMemberInput, wn as Ref, wt as InboxApproveInput, x as CurrentUserMethods, xn as PaymentsPayInput, xt as AddressBookEntry, y as MediaMethods, yn as PaymentType, yt as ActorRequestsMethods, z as PayrollGroup, zn as Permission, zt as ActivitySummaryParams } from "./observation-B3PCugGC.mjs";
3
+ import "./OAuthBearerAuthClient-BxTiv8dg.mjs";
4
4
  import { Hex } from "viem";
5
5
  import { Context, Effect, Layer } from "effect";
6
6
  import { FunctionReference } from "convex/server";
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { $ as EngineeringTelemetryBootstrapPolicy, A as normalizeExceptionErrorKind, B as isSignerNotReadySignal, C as failureDetail, D as postHogFailureObservation, E as observeFailedResult, F as fromWei, G as readInvocationObservation, H as signerFailure, I as isSettingUpLifecycle, J as sanitizeObservationContext, K as OBSERVATION_CONTEXT_HEADER, L as formatTraceparent, M as projectSdkException, N as safeExceptionLabel, O as EXCEPTION_MESSAGE, P as version, Q as BootstrapEnvelope, R as causeChain, S as CAPXUL_SDK_EXPECTED_OUTCOME_EVENT, T as observationContextProps, U as resolveFailureMode, V as openfortSignerNotReadyCode, W as copyInvocationObservation, X as PAYMENT_STATUSES, Y as PAYMENT_DIRECTIONS, Z as CAPXUL_FUNCTIONS, _ as convexCallErrorFromCapxul, a as AccountReadPortTag, b as fingerprintPaymentIntent, d as retryIdempotentRead, et as destination, f as PRODUCT_INVOCATION, g as ConvexCallPortTag, h as identityErrorFromCapxul, i as smartAccountErrorFromCapxul, j as normalizeExceptionOperation, k as SDK_VERSION$1, l as redactTelemetryProps, m as IdentityPortTag, n as detectAuthCacheAdapter, nt as isRestoring, o as accountReadErrorFromCapxul, p as wireChainId, q as encodeObservationContextHeader, r as SmartAccountPortTag, s as TelemetryPortTag, t as assembleCapxulClient, tt as isClaimed, u as stampTelemetryEnvelope, v as BootstrapPortTag, w as failureEvidenceProps, x as toWei, y as bootstrapErrorFromCapxul, z as injectedWalletSigner } from "./create-capxul-client-DwINzpXZ.mjs";
2
- import { $ as CHAIN_UPSTREAMS, B as toOrgId, D as toAllowedOrigin, E as toAddress, F as toEmail, G as toRoleKey, I as toEpochMs, J as CAPXUL_ERROR_CODES, K as toSessionToken, L as toEpochSeconds, M as toCountryCode, N as toCurrencyCode, P as toDurationMs, Q as isCapxulError, R as toJwtToken, T as toAccountId, W as toPublishableKey, Y as CapxulError, Z as Errors, d as AuthClientError, et as FAILURE_MODES, f as AuthClientPortTag, g as BASE_SEPOLIA_CHAIN_ID, h as normalizeBindingEmail, j as toChainId, k as toAuthUserId, l as ClockPortTag, m as CAPXUL_PAYMENTS_V2_ADDRESS, q as decodeConvexError, s as AuthCachePortTag, u as authClientPortFromPromiseAdapter, w as currencySymbolFor, z as toKycTier } from "./clock-0c5NM-TY.mjs";
3
- import { n as SystemClockLayer } from "./OAuthBearerAuthClient-BZ6tXt4m.mjs";
1
+ import { $ as EngineeringTelemetryBootstrapPolicy, A as normalizeExceptionErrorKind, B as isSignerNotReadySignal, C as failureDetail, D as postHogFailureObservation, E as observeFailedResult, F as fromWei, G as readInvocationObservation, H as signerFailure, I as isSettingUpLifecycle, J as sanitizeObservationContext, K as OBSERVATION_CONTEXT_HEADER, L as formatTraceparent, M as projectSdkException, N as safeExceptionLabel, O as EXCEPTION_MESSAGE, P as version, Q as BootstrapEnvelope, R as causeChain, S as CAPXUL_SDK_EXPECTED_OUTCOME_EVENT, T as observationContextProps, U as resolveFailureMode, V as openfortSignerNotReadyCode, W as copyInvocationObservation, X as PAYMENT_STATUSES, Y as PAYMENT_DIRECTIONS, Z as CAPXUL_FUNCTIONS, _ as convexCallErrorFromCapxul, a as AccountReadPortTag, b as fingerprintPaymentIntent, d as retryIdempotentRead, et as destination, f as PRODUCT_INVOCATION, g as ConvexCallPortTag, h as identityErrorFromCapxul, i as smartAccountErrorFromCapxul, j as normalizeExceptionOperation, k as SDK_VERSION$1, l as redactTelemetryProps, m as IdentityPortTag, n as detectAuthCacheAdapter, nt as isRestoring, o as accountReadErrorFromCapxul, p as wireChainId, q as encodeObservationContextHeader, r as SmartAccountPortTag, s as TelemetryPortTag, t as assembleCapxulClient, tt as isClaimed, u as stampTelemetryEnvelope, v as BootstrapPortTag, w as failureEvidenceProps, x as toWei, y as bootstrapErrorFromCapxul, z as injectedWalletSigner } from "./create-capxul-client-dk67qjlK.mjs";
2
+ import { $ as CHAIN_UPSTREAMS, B as toOrgId, D as toAllowedOrigin, E as toAddress, F as toEmail, G as toRoleKey, I as toEpochMs, J as CAPXUL_ERROR_CODES, K as toSessionToken, L as toEpochSeconds, M as toCountryCode, N as toCurrencyCode, P as toDurationMs, Q as isCapxulError, R as toJwtToken, T as toAccountId, W as toPublishableKey, Y as CapxulError, Z as Errors, d as AuthClientError, et as FAILURE_MODES, f as AuthClientPortTag, g as BASE_SEPOLIA_CHAIN_ID, h as normalizeBindingEmail, j as toChainId, k as toAuthUserId, l as ClockPortTag, m as CAPXUL_PAYMENTS_V2_ADDRESS, q as decodeConvexError, s as AuthCachePortTag, u as authClientPortFromPromiseAdapter, w as currencySymbolFor, z as toKycTier } from "./clock-m_aHLaNm.mjs";
3
+ import { n as SystemClockLayer } from "./OAuthBearerAuthClient-DfS4QOg7.mjs";
4
4
  import { keccak256, recoverAddress, stringToHex } from "viem";
5
5
  import { Cause, Context, Data, Duration, Effect, Exit, Layer, Result, Schedule, SchemaIssue, SchemaParser, Scope, Tracer } from "effect";
6
6
  import { getFunctionName, makeFunctionReference } from "convex/server";
@@ -3775,6 +3775,8 @@ function captureException(telemetry, error, context) {
3775
3775
  issueDescription: detail.reason ?? failureMode,
3776
3776
  ...detail.chain_upstream === void 0 ? {} : { chainUpstream: detail.chain_upstream },
3777
3777
  ...detail.chain_method === void 0 ? {} : { chainMethod: detail.chain_method },
3778
+ ...detail.chain_http_status === void 0 ? {} : { chainHttpStatus: detail.chain_http_status },
3779
+ ...detail.chain_rpc_code === void 0 ? {} : { chainRpcCode: detail.chain_rpc_code },
3778
3780
  exceptionList: buildExceptionList({
3779
3781
  type: errorCode,
3780
3782
  value: EXCEPTION_MESSAGE,
@@ -1,5 +1,5 @@
1
- import { C as CachedJwt, M as AuthSession, S as AuthCachePortTag, b as AuthCacheError, n as CapxulSigner, x as AuthCachePort } from "../signer-HY5Ju8xs.mjs";
2
- import { n as oauthBearerAuthClient, t as OAuthBearerAuthClientInput } from "../OAuthBearerAuthClient-GkHnGqNi.mjs";
1
+ import { C as CachedJwt, M as AuthSession, S as AuthCachePortTag, b as AuthCacheError, n as CapxulSigner, x as AuthCachePort } from "../signer-BpjdawiY.mjs";
2
+ import { n as oauthBearerAuthClient, t as OAuthBearerAuthClientInput } from "../OAuthBearerAuthClient-BxTiv8dg.mjs";
3
3
  import { Hex } from "viem";
4
4
  import { Effect, FileSystem, Layer, Path } from "effect";
5
5
  //#region src/adapters/auth-cache/FileSystemAuthCacheAdapter.d.ts
@@ -1,5 +1,5 @@
1
- import { E as toAddress, a as parseCachedJwt, i as parseAuthSession, n as InMemoryAuthCacheAdapter, o as AuthCacheError, r as BrowserAuthCacheAdapter, s as AuthCachePortTag } from "../clock-0c5NM-TY.mjs";
2
- import { t as oauthBearerAuthClient } from "../OAuthBearerAuthClient-BZ6tXt4m.mjs";
1
+ import { E as toAddress, a as parseCachedJwt, i as parseAuthSession, n as InMemoryAuthCacheAdapter, o as AuthCacheError, r as BrowserAuthCacheAdapter, s as AuthCachePortTag } from "../clock-m_aHLaNm.mjs";
2
+ import { t as oauthBearerAuthClient } from "../OAuthBearerAuthClient-DfS4QOg7.mjs";
3
3
  import { Effect, FileSystem, Layer, Path } from "effect";
4
4
  import { privateKeyToAccount } from "viem/accounts";
5
5
  import * as os from "node:os";
@@ -1,4 +1,4 @@
1
- import { $ as RoleKey, A as AnonymousDistinctId, B as DurationMs, D as Address$1, F as BudgetId, G as PartyId, H as EpochMs, I as ChainId, J as PayrollRunId, K as PaymentCommandId, L as CountryCode, M as AuthSession, N as AuthUserId, O as AllowanceKey, P as BlockNumber, Q as PublishableKey, R as CurrencyCode, T as Account$1, U as Money, V as Email, W as OrgId, X as PermissionId, Y as PermissionAssignmentId, Z as Profile, _ as ClockPort, _t as FailureMode, a as SignerStatusStore, c as AccountProviderSource, ct as CapxulErrorCode, dt as Failure, et as SessionToken, g as SmartAccount$1, h as Session$1, j as AppId, k as AllowedOrigin, l as AccountRequirement, lt as CapxulErrorDetails, m as Profile$1, n as CapxulSigner, nt as TxHash, p as CapxulResult, q as PayrollGroupId, rt as WeiAmount, st as CapxulError, tt as SmartAccount, v as AuthClientPort, x as AuthCachePort, y as CanSendOtpStatus, z as DocumentHash } from "./signer-HY5Ju8xs.mjs";
1
+ import { $ as RoleKey, A as AnonymousDistinctId, B as DurationMs, D as Address$1, F as BudgetId, G as PartyId, H as EpochMs, I as ChainId, J as PayrollRunId, K as PaymentCommandId, L as CountryCode, M as AuthSession, N as AuthUserId, O as AllowanceKey, P as BlockNumber, Q as PublishableKey, R as CurrencyCode, T as Account$1, U as Money, V as Email, W as OrgId, X as PermissionId, Y as PermissionAssignmentId, Z as Profile, _ as ClockPort, _t as FailureMode, a as SignerStatusStore, c as AccountProviderSource, ct as CapxulErrorCode, dt as Failure, et as SessionToken, g as SmartAccount$1, h as Session$1, j as AppId, k as AllowedOrigin, l as AccountRequirement, lt as CapxulErrorDetails, m as Profile$1, n as CapxulSigner, nt as TxHash, p as CapxulResult, q as PayrollGroupId, rt as WeiAmount, st as CapxulError, tt as SmartAccount, v as AuthClientPort, x as AuthCachePort, y as CanSendOtpStatus, z as DocumentHash } from "./signer-BpjdawiY.mjs";
2
2
  import { Hex } from "viem";
3
3
  import { Context, Effect, Layer, Schema, Scope, Tracer } from "effect";
4
4
  import { FunctionReference } from "convex/server";
@@ -2763,6 +2763,17 @@ interface SdkFailureDetail {
2763
2763
  readonly chain_revert_selector?: string;
2764
2764
  readonly chain_revert_args?: Readonly<Record<string, string>>;
2765
2765
  readonly chain_provider_message?: string;
2766
+ /** #1818: the attempt sequence and the refusing upstream's headers, same bounds as the backend. */
2767
+ readonly chain_attempts?: readonly SdkChainAttempt[];
2768
+ readonly chain_response_headers?: Readonly<Record<string, string>>;
2769
+ }
2770
+ /** One upstream tried, flat: the `chain_attempts` entry shape on every producer's row. */
2771
+ interface SdkChainAttempt {
2772
+ readonly upstream: string;
2773
+ readonly failure_mode: string;
2774
+ readonly http_status?: number;
2775
+ readonly rpc_code?: number;
2776
+ readonly duration_ms: number;
2766
2777
  }
2767
2778
  type ObservationDelivery = void | PromiseLike<void>;
2768
2779
  /**
@@ -33,8 +33,14 @@ declare const FAILURE_MODES: readonly [
33
33
  "insufficient-allowance",
34
34
  /** The bundler's validation refused the operation (AA2x/AA3x); its text is carried. */
35
35
  "rejected",
36
- /** The gateway refused the token. */
36
+ /** The upstream refused the credential (HTTP 401): the gateway token or a hosted node key. */
37
37
  "unauthorized",
38
+ /** The upstream refused the key, origin, or method (HTTP 403). The next upstream's key may be fine. */
39
+ "forbidden",
40
+ /** The upstream refused the request shape (HTTP 400-family; JSON-RPC -32600, -32602, -32700). */
41
+ "bad-request",
42
+ /** The node does not serve the method (JSON-RPC -32601). The next upstream may. */
43
+ "method-unsupported",
38
44
  /** No cause could be determined. */
39
45
  "unknown"];
40
46
  type FailureMode = (typeof FAILURE_MODES)[number];
@@ -62,6 +68,22 @@ type ChainCause = {
62
68
  readonly rpcCode?: number;
63
69
  readonly revert?: RevertSummary;
64
70
  readonly providerMessage?: string;
71
+ /**
72
+ * Every upstream tried for this read, in order, ending with the one whose
73
+ * answer this cause is (#1818). One `$exception` row then tells the whole
74
+ * story of an exhausted chain; the recovered case stays an event.
75
+ */
76
+ readonly attempts?: readonly ChainAttempt[];
77
+ /** The refusing upstream's allow-listed response headers, masked and bounded. */
78
+ readonly responseHeaders?: Readonly<Record<string, string>>;
79
+ };
80
+ /** One upstream tried and the word for how it failed (#1818). */
81
+ type ChainAttempt = {
82
+ readonly upstream: ChainUpstream;
83
+ readonly mode: FailureMode;
84
+ readonly httpStatus?: number;
85
+ readonly rpcCode?: number;
86
+ readonly durationMs: number;
65
87
  };
66
88
  //#endregion
67
89
  //#region ../errors/src/errors.d.ts
@@ -1,4 +1,4 @@
1
- import { Jn as TelemetryIdentifyInput, Kn as TelemetryEvent, ar as IdentityTransition, n as ObservationAdapter, qn as TelemetryGroupInput, u as CapxulClient } from "../observation-CR3_haBB.mjs";
1
+ import { Jn as TelemetryIdentifyInput, Kn as TelemetryEvent, ar as IdentityTransition, n as ObservationAdapter, qn as TelemetryGroupInput, u as CapxulClient } from "../observation-B3PCugGC.mjs";
2
2
  import { Effect, Layer } from "effect";
3
3
  //#region src/testing/telemetry/RecordingTelemetryAdapter.d.ts
4
4
  type RecordingTelemetryOperation = {
@@ -1,5 +1,5 @@
1
- import { F as fromWei, _ as convexCallErrorFromCapxul, c as redactTelemetryEvent, h as identityErrorFromCapxul, i as smartAccountErrorFromCapxul, o as accountReadErrorFromCapxul, p as wireChainId, t as assembleCapxulClient, x as toWei, y as bootstrapErrorFromCapxul } from "../create-capxul-client-DwINzpXZ.mjs";
2
- import { D as toAllowedOrigin, E as toAddress, F as toEmail, I as toEpochMs, K as toSessionToken, L as toEpochSeconds, M as toCountryCode, O as toAppId, P as toDurationMs, R as toJwtToken, T as toAccountId, W as toPublishableKey, Y as CapxulError, Z as Errors, _ as deriveCapxulSafeAddress, j as toChainId, k as toAuthUserId, n as InMemoryAuthCacheAdapter, t as readClockNow, u as authClientPortFromPromiseAdapter, z as toKycTier } from "../clock-0c5NM-TY.mjs";
1
+ import { F as fromWei, _ as convexCallErrorFromCapxul, c as redactTelemetryEvent, h as identityErrorFromCapxul, i as smartAccountErrorFromCapxul, o as accountReadErrorFromCapxul, p as wireChainId, t as assembleCapxulClient, x as toWei, y as bootstrapErrorFromCapxul } from "../create-capxul-client-dk67qjlK.mjs";
2
+ import { D as toAllowedOrigin, E as toAddress, F as toEmail, I as toEpochMs, K as toSessionToken, L as toEpochSeconds, M as toCountryCode, O as toAppId, P as toDurationMs, R as toJwtToken, T as toAccountId, W as toPublishableKey, Y as CapxulError, Z as Errors, _ as deriveCapxulSafeAddress, j as toChainId, k as toAuthUserId, n as InMemoryAuthCacheAdapter, t as readClockNow, u as authClientPortFromPromiseAdapter, z as toKycTier } from "../clock-m_aHLaNm.mjs";
3
3
  import { keccak256 } from "viem";
4
4
  import { Effect, Result, Semaphore } from "effect";
5
5
  import { getFunctionName } from "convex/server";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capxul/sdk",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/Xelmar-tech/infrastructure.git",
@@ -47,12 +47,12 @@
47
47
  "vite": "npm:@voidzero-dev/vite-plus-core@0.3.0",
48
48
  "vite-plus": "0.3.0",
49
49
  "vitest": "4.1.11",
50
- "@capxul/errors": "0.0.2",
51
- "@capxul/types": "0.2.1",
50
+ "@capxul/config": "0.2.3",
51
+ "@capxul/errors": "0.2.0",
52
+ "@capxul/types": "0.2.3",
52
53
  "@capxul/typescript-config": "0.0.0",
53
- "@capxul/wire": "0.5.1",
54
- "@capxul/config": "0.2.1",
55
- "@capxul/observability": "3.0.0"
54
+ "@capxul/wire": "0.6.0",
55
+ "@capxul/observability": "3.1.0"
56
56
  },
57
57
  "_permissionlessPinReason": "permissionless.toSafeSmartAccount is pinned to 0.3.4 for live Safe deployment E2E. Counterfactual address fixtures captured 2026-05-17 in packages/backend/convex/_shared/__tests__/counterfactual.test.ts and packages/config/tests/safe.test.ts must be re-verified before upgrading.",
58
58
  "scripts": {