@capxul/sdk 4.20.0-beta.27 → 4.20.0-beta.29

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 { Cause, Clock, Context, Data, Deferred, Effect, Exit, Fiber, Layer, Option, Predicate, Queue, Ref, Result, Schema, SchemaGetter, SchemaParser } from "effect";
1
+ import { Cause, Clock, Context, Data, Deferred, Effect, Exit, Fiber, Layer, Option, Predicate, Queue, Ref, Result, Schema, SchemaAST, SchemaGetter, SchemaIssue, SchemaParser } from "effect";
2
2
  import { makeFunctionReference } from "convex/server";
3
3
  import { bytesToHex, concat, decodeErrorResult, encodeAbiParameters, encodeFunctionData, encodePacked, formatUnits, getContractAddress, hashTypedData, isAddress, isHex, keccak256, pad, padHex, parseAbiParameters, recoverAddress, recoverTypedDataAddress, toBytes, toFunctionSignature, toHex } from "viem";
4
4
  import { getUserOperationHash } from "viem/account-abstraction";
@@ -858,7 +858,7 @@ const FIELD_RULES = {
858
858
  },
859
859
  anonymousId: {
860
860
  maxLength: 128,
861
- pattern: /^anon_[A-Za-z0-9-]+$/u
861
+ pattern: /^(?:anon_[A-Za-z0-9-]+|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/u
862
862
  },
863
863
  traceparent: {
864
864
  maxLength: 55,
@@ -936,6 +936,21 @@ function markFailureInvocationSnapshot(failure, snapshot) {
936
936
  function readFailureInvocationSnapshot(failure) {
937
937
  return isRecord(failure) ? failureInvocationSnapshots.get(failure) : void 0;
938
938
  }
939
+ /**
940
+ * The operation each failure came from, by the error it carries. A public
941
+ * method's path (`approvals.review`) is not always an operation name; the row
942
+ * it called (`accounts.approvals.review`) is. The innermost call marks first
943
+ * and keeps the mark, so the report names the row that actually failed.
944
+ */
945
+ const failureOperations = /* @__PURE__ */ new WeakMap();
946
+ /** @internal Record which operation a failure came from; the first mark is kept. */
947
+ function markFailureOperation(error, operation) {
948
+ if (isRecord(error) && !failureOperations.has(error)) failureOperations.set(error, operation);
949
+ }
950
+ /** @internal The operation a failure came from, when a runner marked it. */
951
+ function readFailureOperation(error) {
952
+ return isRecord(error) ? failureOperations.get(error) : void 0;
953
+ }
939
954
  //#endregion
940
955
  //#region src/observability/http-headers.ts
941
956
  /** Resolve one bounded pre-auth snapshot for an outbound SDK HTTP request. */
@@ -2079,7 +2094,9 @@ async function runIfActive(signal, operation, effect, runPromise, observe) {
2079
2094
  signal.addEventListener("abort", onAbort, { once: true });
2080
2095
  return Effect.sync(() => signal.removeEventListener("abort", onAbort));
2081
2096
  }));
2082
- return runProgram(observe === void 0 ? controlled : observe(controlled), runPromise);
2097
+ const result = await runProgram(observe === void 0 ? controlled : observe(controlled), runPromise);
2098
+ if (!result.ok) markFailureOperation(result.error, operation);
2099
+ return result;
2083
2100
  }
2084
2101
  //#endregion
2085
2102
  //#region src/platform/capxul-effect.ts
@@ -4688,7 +4705,9 @@ const OrganizationPaymentExecutionItemSchema = Schema.Struct({
4688
4705
  "invoice",
4689
4706
  "payroll",
4690
4707
  "reimbursement"
4691
- ]))
4708
+ ])),
4709
+ /** When the recipient gets it: instant (the default), or held until a date or over a period. */
4710
+ timing: Schema.optional(PaymentTiming)
4692
4711
  }).check(Schema.makeFilter((value) => Schema.is(PaymentDocumentOptionsSchema)(value)));
4693
4712
  const PrepareOrganizationPaymentExecutionInputSchema = Schema.Struct({
4694
4713
  orgId: nonEmpty("orgId"),
@@ -4798,11 +4817,20 @@ const PreparedPaymentCommandExecutionSchema = Schema.Struct({
4798
4817
  //#region ../protocol/src/accounts/approval-schema.ts
4799
4818
  const APPROVAL_ID_RE = /^approval_[0-9A-HJKMNP-TV-Z]{26}$/u;
4800
4819
  const ApprovalIdSchema = Schema.String.pipe(Schema.refine((value) => APPROVAL_ID_RE.test(value), { message: "must be approval_ plus a ULID" }));
4801
- /** What is being approved. */
4820
+ /**
4821
+ * What is being approved. A `permission_change` creates, changes, assigns,
4822
+ * revokes or replaces an Organization's Permissions; an `invitation` sets up
4823
+ * the rights an invitation offers. Neither moves money. A `payment_cancel`
4824
+ * returns a held Payment to whoever funded it; a `payment_redirect` sends it
4825
+ * to someone else.
4826
+ */
4802
4827
  const APPROVAL_KINDS = [
4803
4828
  "payment",
4804
4829
  "payroll_run",
4830
+ "payment_cancel",
4831
+ "payment_redirect",
4805
4832
  "permission_change",
4833
+ "invitation",
4806
4834
  "bank_payout"
4807
4835
  ];
4808
4836
  /**
@@ -5026,8 +5054,7 @@ const approvals = {
5026
5054
  errors: [
5027
5055
  ...CALL$10,
5028
5056
  ...DEFECTS,
5029
- "WRONG_STATE",
5030
- "NOT_IMPLEMENTED"
5057
+ "WRONG_STATE"
5031
5058
  ]
5032
5059
  }),
5033
5060
  /** The approval as the person reads it, with its state judged against its expiry. */
@@ -5525,6 +5552,28 @@ const probes = {
5525
5552
  })
5526
5553
  };
5527
5554
  //#endregion
5555
+ //#region ../protocol/src/evm/role-dsl.ts
5556
+ const ORG_BUDGET_ACTIONS = [
5557
+ "send",
5558
+ "sendBatch",
5559
+ "createCommitment"
5560
+ ];
5561
+ /**
5562
+ * How a capped Budget's allowance refills (#2298). `monthly` refills the
5563
+ * allowance to its cap every 30 days. Roles v2 counts fixed periods from the
5564
+ * moment the allowance is set; it has no calendar month. Absent means `none`:
5565
+ * the cap is one pot that spending drains for good.
5566
+ */
5567
+ const ORG_BUDGET_REFILLS = ["monthly", "none"];
5568
+ /** The fixed Roles v2 refill period of a `monthly` Budget: 30 days, in seconds. */
5569
+ const MONTHLY_REFILL_PERIOD_SECONDS = 30n * 24n * 60n * 60n;
5570
+ /** Derive the Zodiac substrate key for one durable Permission identity. */
5571
+ function roleKeyForPermission(permissionId) {
5572
+ const normalized = permissionId.trim();
5573
+ if (normalized.length === 0 || normalized !== permissionId) throw Errors.invalidInput("permissionId", "must be a non-empty canonical Permission ID");
5574
+ return keccak256(toBytes(`permission ${permissionId}`));
5575
+ }
5576
+ //#endregion
5528
5577
  //#region ../protocol/src/permissions/permission-schema.ts
5529
5578
  /**
5530
5579
  * One stored `setAllowance` call inside a command's expected authority set.
@@ -5563,12 +5612,19 @@ const BudgetScopeSchema = Schema.Struct({
5563
5612
  })]),
5564
5613
  actions: Schema.NonEmptyArray(Schema.Literals(["pay", "commitments"]))
5565
5614
  }).pipe(Schema.refine((scope) => new Set(scope.actions).size === scope.actions.length && (scope.recipients.type === "anyone" || scope.recipients.accounts.length >= 1 && scope.recipients.accounts.length <= 32 && new Set(scope.recipients.accounts).size === scope.recipients.accounts.length), { message: "Budget scope requires unique actions and one to 32 unique allowed Accounts" }));
5615
+ /**
5616
+ * How a capped Budget refills (#2298): `monthly` tops the cap back up every 30
5617
+ * days (a fixed Roles v2 period, not a calendar month); `none` never refills.
5618
+ * Absent means `none`, and writers store only `monthly`.
5619
+ */
5620
+ const BudgetRefillSchema = Schema.Literals(ORG_BUDGET_REFILLS);
5566
5621
  /** The accepted product policy stored with one Permission revision. */
5567
5622
  const CurrentPolicySchema = Schema.Union([Schema.Struct({
5568
5623
  type: Schema.Literal("budget"),
5569
5624
  asset: AssetIdSchema,
5570
5625
  limit: Schema.NullOr(Schema.String),
5571
- scope: BudgetScopeSchema
5626
+ scope: BudgetScopeSchema,
5627
+ refill: Schema.optional(BudgetRefillSchema)
5572
5628
  }), Schema.Struct({ type: Schema.Literal("managePeople") })]);
5573
5629
  const BudgetIntentSchema = CurrentPolicySchema.pipe(Schema.refine((value) => {
5574
5630
  if (value.type !== "budget") return false;
@@ -5590,6 +5646,15 @@ const PermissionSchema = Schema.Struct({
5590
5646
  limitRaw: Schema.optional(WeiAmountSchema$1),
5591
5647
  allowanceKey: Schema.optional(AllowanceKeySchema)
5592
5648
  });
5649
+ /**
5650
+ * A refilling Budget's refill (#2439): how much each one adds, every how many
5651
+ * days, and when the next one is due.
5652
+ */
5653
+ const BudgetNextRefillSchema = Schema.Struct({
5654
+ amount: AssetAmountSchema,
5655
+ everyDays: Schema.Finite,
5656
+ nextAt: EpochMsSchema$1
5657
+ });
5593
5658
  const PermissionAssignmentSchema = Schema.Struct({
5594
5659
  permissionAssignmentId: PermissionAssignmentIdSchema,
5595
5660
  permissionId: PermissionIdSchema,
@@ -5633,7 +5698,8 @@ const ResolvedPermissionBudgetPolicySchema = Schema.Struct({
5633
5698
  "send",
5634
5699
  "sendBatch",
5635
5700
  "createCommitment"
5636
- ]))
5701
+ ])),
5702
+ refill: Schema.optional(BudgetRefillSchema)
5637
5703
  });
5638
5704
  const PermissionRecipientBindingSchema = Schema.Struct({
5639
5705
  accountId: AccountIdSchema$1,
@@ -5645,16 +5711,22 @@ const StoredPermissionIntentSchema = Schema.Union([Schema.Struct({
5645
5711
  type: Schema.Literal("budget"),
5646
5712
  asset: AssetIdSchema,
5647
5713
  limit: Schema.NullOr(AssetAmountSchema),
5648
- scope: Schema.optional(BudgetScopeSchema)
5714
+ scope: Schema.optional(BudgetScopeSchema),
5715
+ refill: Schema.optional(BudgetRefillSchema)
5649
5716
  }).pipe(Schema.refine((intent) => intent.limit === null || intent.limit.asset === intent.asset, { message: "Budget limit must use the Budget asset" })), Schema.Struct({ type: Schema.Literal("managePeople") })]);
5650
5717
  /** Writes state every granted right and use a finite positive AssetAmount cap. */
5651
5718
  const PermissionIntentSchema = Schema.Union([Schema.Struct({
5652
5719
  type: Schema.Literal("budget"),
5653
5720
  asset: AssetIdSchema,
5654
5721
  limit: AssetAmountSchema,
5655
- scope: BudgetScopeSchema
5722
+ scope: BudgetScopeSchema,
5723
+ refill: Schema.optional(BudgetRefillSchema)
5656
5724
  }).pipe(Schema.refine((intent) => intent.limit.asset === intent.asset && /[1-9]/u.test(intent.limit.value), { message: "Budget cap must be positive and use the Budget asset" })), Schema.Struct({ type: Schema.Literal("managePeople") })]);
5657
- /** Decode once and give set-like scope fields one stable command representation. */
5725
+ /**
5726
+ * Decode once and give set-like scope fields one stable command representation.
5727
+ * An explicit `refill: "none"` is kept: on a change it turns a refill off,
5728
+ * where a missing `refill` keeps the Budget's current one (`keepRefillOnChange`).
5729
+ */
5658
5730
  function decodePermissionIntent(input) {
5659
5731
  const intent = Schema.decodeUnknownSync(PermissionIntentSchema, { onExcessProperty: "error" })(input);
5660
5732
  if (intent.type === "managePeople") return intent;
@@ -5669,6 +5741,19 @@ function decodePermissionIntent(input) {
5669
5741
  }
5670
5742
  });
5671
5743
  }
5744
+ /**
5745
+ * A Budget change that leaves `refill` out keeps the Budget's current refill:
5746
+ * a change never turns a refill off by omission. An explicit `"none"` turns it
5747
+ * off. The backend plans, the SDK verifies and the CLI confirms the same
5748
+ * result. A create has no current refill, so a missing one means `none`.
5749
+ */
5750
+ function keepRefillOnChange(intent, before) {
5751
+ if (intent.type !== "budget" || intent.refill !== void 0 || before?.type !== "budget" || before.refill !== "monthly") return intent;
5752
+ return {
5753
+ ...intent,
5754
+ refill: before.refill
5755
+ };
5756
+ }
5672
5757
  //#endregion
5673
5758
  //#region ../protocol/src/permissions/replacement-schema.ts
5674
5759
  const InvitationPhaseSchema = Schema.Literals([
@@ -5873,7 +5958,7 @@ const InviteGrantsSchema = Schema.Array(Schema.Union([
5873
5958
  Schema.Struct({
5874
5959
  type: Schema.Literal("new_budget"),
5875
5960
  label: Schema.String.pipe(Schema.refine((value) => value.trim().length >= 1 && value.trim().length <= 128)),
5876
- intent: BudgetIntentSchema
5961
+ intent: BudgetIntentSchema.pipe(Schema.refine((intent) => intent.refill !== "monthly", { message: "An invitation's new Budget cannot refill; change it once granted" }))
5877
5962
  })
5878
5963
  ])).pipe(Schema.refine((grants) => {
5879
5964
  if (grants.length < 1 || grants.length > 16) return false;
@@ -8190,6 +8275,30 @@ const authorities = {
8190
8275
  "ROLE_PERMISSION_DENIED"
8191
8276
  ]
8192
8277
  }),
8278
+ /**
8279
+ * What one active Budget can still spend now: its Roles allowance, read on
8280
+ * chain at the latest block as the payment gate reads it. `left` is null for
8281
+ * a Budget with no allowance (no cap). `refill` names a refilling Budget's
8282
+ * refill and when the next one is due; null for a one-off or uncapped Budget
8283
+ * (#2439). For a participant of the Organization.
8284
+ */
8285
+ budgetLeft: Operation.action("permissions.authorities.budgetLeft", {
8286
+ input: Schema.Struct({
8287
+ orgId: OrgId,
8288
+ budgetId: PermissionIdSchema
8289
+ }),
8290
+ output: Schema.Struct({
8291
+ budgetId: PermissionIdSchema,
8292
+ left: Schema.NullOr(AssetAmountSchema),
8293
+ refill: Schema.optionalKey(Schema.NullOr(BudgetNextRefillSchema))
8294
+ }),
8295
+ errors: [
8296
+ ...CALL$2,
8297
+ ...DEFECTS,
8298
+ ...CHAIN$1,
8299
+ "ROLE_PERMISSION_DENIED"
8300
+ ]
8301
+ }),
8193
8302
  /** Whether the caller's Account may spend this amount from this Budget right now. */
8194
8303
  authorize: Operation.query("permissions.authorities.authorize", {
8195
8304
  input: MoneyAuthorizationRequestSchema,
@@ -8964,7 +9073,9 @@ const PEOPLE_OPERATIONS = {
8964
9073
  emitSafeConfirmed: "accounts.safes.emitSafeConfirmed",
8965
9074
  readSafe: "accounts.safes.readSafe",
8966
9075
  recordDeployment: "accounts.safes.recordDeployment"
8967
- }
9076
+ },
9077
+ /** The home's requests to pay and Payments to retry, read in one query. */
9078
+ summaries: { loadNeedsYou: "accounts.summaries.loadNeedsYou" }
8968
9079
  },
8969
9080
  /** The host observer's names for `client.approvals.*`. */
8970
9081
  approvals: {
@@ -9078,6 +9189,7 @@ const ORGANIZATION_OPERATIONS = {
9078
9189
  payment: "org.payment",
9079
9190
  permissions: {
9080
9191
  assign: "org.permissions.assign",
9192
+ budgetLeft: "org.permissions.budgetLeft",
9081
9193
  change: "org.permissions.change",
9082
9194
  create: "org.permissions.create",
9083
9195
  get: "org.permissions.get",
@@ -9223,7 +9335,9 @@ emitSaved: "activity.annotations.emitSaved" },
9223
9335
  pay: "organizationPayments.pay",
9224
9336
  payBatch: "organizationPayments.payBatch",
9225
9337
  prepareExecution: "organizationPayments.prepareExecution",
9226
- retry: "organizationPayments.retry"
9338
+ retry: "organizationPayments.retry",
9339
+ cancel: "organizationPayments.cancel",
9340
+ redirect: "organizationPayments.redirect"
9227
9341
  },
9228
9342
  /**
9229
9343
  * Payments' internal functions and the client's own method names; its public
@@ -9245,7 +9359,9 @@ emitSaved: "activity.annotations.emitSaved" },
9245
9359
  loadContext: "payments.commands.loadContext",
9246
9360
  loadExecutionContext: "payments.commands.loadExecutionContext",
9247
9361
  /** The capped Budget a prepared Organization command spends from, before its allowance is read on chain. */
9248
- loadBudgetAllowance: "payments.commands.loadBudgetAllowance"
9362
+ loadBudgetAllowance: "payments.commands.loadBudgetAllowance",
9363
+ /** What a new Payment command would spend and from which Safe, read before it is born. */
9364
+ loadBirthFunding: "payments.commands.loadBirthFunding"
9249
9365
  },
9250
9366
  lifecycles: { prepareIntent: "payments.lifecycles.prepareIntent" },
9251
9367
  /** The stored UserOperation of every money command: Payment, Permission and configuration. */
@@ -9320,6 +9436,9 @@ emitChanged: "payroll.groups.emitChanged" }
9320
9436
  },
9321
9437
  /** Permissions' internal functions; its public rows join from `OPERATIONS`. */
9322
9438
  permissions: {
9439
+ authorities: {
9440
+ /** The Budget-left read's participant check and the Budget's stored allowance. */
9441
+ loadBudgetLeft: "permissions.authorities.loadBudgetLeft" },
9323
9442
  commands: {
9324
9443
  /** The prepare action's transaction: authorize, plan and save the command. */
9325
9444
  prepareIntent: "permissions.commands.prepareIntent",
@@ -9604,6 +9723,13 @@ const OptionalReasonCode = Schema.optional(Schema.Literals(CAPXUL_ERROR_CODES));
9604
9723
  * no catalog schema validates it.
9605
9724
  */
9606
9725
  const EXCEPTION_EVENT_NAME = "$exception";
9726
+ /**
9727
+ * The SDK's twin of `capxul_backend_expected_outcome`: an expected outcome the
9728
+ * SDK's failure projection captures through the host's PostHog client. Like
9729
+ * the backend's, its properties are the error evidence, so no catalog schema
9730
+ * validates it.
9731
+ */
9732
+ const SDK_EXPECTED_OUTCOME_EVENT_NAME = "capxul_sdk_expected_outcome";
9607
9733
  const PII = Schema.String.pipe(Schema.brand("PII"));
9608
9734
  /**
9609
9735
  * The canonical envelope (ADR-0020 A4): snake_case ONLY. `capxul_env` and
@@ -9703,6 +9829,42 @@ const UiStepProps = {
9703
9829
  organization_id: OptionalOrgId,
9704
9830
  reason_code: OptionalReasonCode
9705
9831
  };
9832
+ /**
9833
+ * A journey's variants (`JOURNEYS`): the way a person came into a journey, or
9834
+ * the way they took through it. A variant is a property of the journey's step
9835
+ * events, never a journey of its own, so each journey funnel breaks down by
9836
+ * it. A step event takes only the variants that a journey contract names on
9837
+ * it, picked one by one; the analytics plan test checks both directions.
9838
+ * `pay_path` and `funding_source` have no carrier until their Phase 2 events.
9839
+ * `payment_*` events already carry `recipient_is_capxul_account`, and
9840
+ * `invoice_issued` carries `issuer_scope_kind`; neither is repeated here.
9841
+ */
9842
+ const JourneyVariantProps = {
9843
+ /** How a person came to Get started: by themselves, an invite, money that waited, or an invoice. */
9844
+ entry_path: Schema.optional(Schema.Literals([
9845
+ "signup",
9846
+ "invite",
9847
+ "claim",
9848
+ "invoice"
9849
+ ])),
9850
+ /** Where the person acted. `agent` is an AI agent driving the CLI. */
9851
+ surface: Schema.optional(Schema.Literals([
9852
+ "web",
9853
+ "cli",
9854
+ "agent"
9855
+ ])),
9856
+ /** Who pays: a person from their own account, or an Organization from a Budget. */
9857
+ payer_kind: Schema.optional(Schema.Literals(["person", "organization"])),
9858
+ /** How a request was paid: from the payer's inbox, or by its link at checkout. */
9859
+ pay_path: Schema.optional(Schema.Literals(["inbox", "link"])),
9860
+ /** Where an account's first money came from. `bank` is the planned on-ramp. */
9861
+ funding_source: Schema.optional(Schema.Literals([
9862
+ "deposit",
9863
+ "payment",
9864
+ "claim",
9865
+ "bank"
9866
+ ]))
9867
+ };
9706
9868
  //#endregion
9707
9869
  //#region ../observability/src/events/money.ts
9708
9870
  /**
@@ -9792,6 +9954,8 @@ const PaymentInitiatedProps = Schema.Struct({
9792
9954
  execution_id: OptionalString,
9793
9955
  payment_lifecycle_id: OptionalString,
9794
9956
  ...TelemetryEnvelopeProps,
9957
+ payer_kind: JourneyVariantProps.payer_kind,
9958
+ surface: JourneyVariantProps.surface,
9795
9959
  ...RecipientTelemetryProps,
9796
9960
  kind: Schema.String,
9797
9961
  payment_id: Schema.String
@@ -9807,6 +9971,8 @@ const PaymentLifecycleProps = {
9807
9971
  const PaymentSettledL2Props = Schema.Struct({
9808
9972
  ...PaymentLifecycleProps,
9809
9973
  ...TelemetryEnvelopeProps,
9974
+ payer_kind: JourneyVariantProps.payer_kind,
9975
+ surface: JourneyVariantProps.surface,
9810
9976
  ...RecipientTelemetryProps,
9811
9977
  kind: Schema.String,
9812
9978
  chain_id: Schema.Finite,
@@ -10014,6 +10180,7 @@ const OrganizationCreationStartedProps = Schema.Struct({
10014
10180
  });
10015
10181
  const OrganizationCreationReadyProps = Schema.Struct({
10016
10182
  ...TelemetryEnvelopeProps,
10183
+ surface: JourneyVariantProps.surface,
10017
10184
  $insert_id: Schema.String,
10018
10185
  organization_id: OrgIdTelemetrySchema,
10019
10186
  attempt_number: AttemptNumberSchema,
@@ -10038,45 +10205,6 @@ const OrganizationCreationFailedProps = Schema.Struct({
10038
10205
  ...ORGANIZATION_SETUP_REASONS
10039
10206
  ]))
10040
10207
  });
10041
- const OrgCreateStartedProps = Schema.Struct({
10042
- ...TelemetryEnvelopeProps,
10043
- org_id: OptionalOrgId,
10044
- email_domain: OptionalString,
10045
- template: OptionalString
10046
- });
10047
- const OrgSafeCreatedProps = Schema.Struct({
10048
- ...TelemetryEnvelopeProps,
10049
- org_id: OptionalOrgId,
10050
- chainId: OptionalChainId,
10051
- safe_address: OptionalAddress
10052
- });
10053
- const OrgSafeConfirmedProps = Schema.Struct({
10054
- ...TelemetryEnvelopeProps,
10055
- org_id: OptionalOrgId,
10056
- chainId: OptionalChainId,
10057
- safe_address: OptionalAddress,
10058
- durationMs: OptionalDurationMs
10059
- });
10060
- const OrgRolesSeededProps = Schema.Struct({
10061
- ...TelemetryEnvelopeProps,
10062
- org_id: OptionalOrgId,
10063
- chainId: OptionalChainId,
10064
- role: OptionalString,
10065
- txHash: OptionalTxHash
10066
- });
10067
- const OrgCreatedProps = Schema.Struct({
10068
- ...TelemetryEnvelopeProps,
10069
- org_id: OptionalOrgId,
10070
- chainId: OptionalChainId,
10071
- safe_address: OptionalAddress,
10072
- durationMs: OptionalDurationMs
10073
- });
10074
- const OrgCreateFailedProps = Schema.Struct({
10075
- ...TelemetryEnvelopeProps,
10076
- ...ChainCauseTelemetryProps,
10077
- org_id: OptionalOrgId,
10078
- reason: OptionalString
10079
- });
10080
10208
  const OrgInviteSentProps = Schema.Struct({
10081
10209
  ...TelemetryEnvelopeProps,
10082
10210
  org_id: OptionalOrgId,
@@ -10121,30 +10249,6 @@ Schema.Struct({
10121
10249
  name: Schema.Literal("organization_creation_failed"),
10122
10250
  props: OrganizationCreationFailedProps
10123
10251
  });
10124
- Schema.Struct({
10125
- name: Schema.Literal("org_create_started"),
10126
- props: OrgCreateStartedProps
10127
- });
10128
- Schema.Struct({
10129
- name: Schema.Literal("org_safe_created"),
10130
- props: OrgSafeCreatedProps
10131
- });
10132
- Schema.Struct({
10133
- name: Schema.Literal("org_safe_confirmed"),
10134
- props: OrgSafeConfirmedProps
10135
- });
10136
- Schema.Struct({
10137
- name: Schema.Literal("org_roles_seeded"),
10138
- props: OrgRolesSeededProps
10139
- });
10140
- Schema.Struct({
10141
- name: Schema.Literal("org_created"),
10142
- props: OrgCreatedProps
10143
- });
10144
- Schema.Struct({
10145
- name: Schema.Literal("org_create_failed"),
10146
- props: OrgCreateFailedProps
10147
- });
10148
10252
  Schema.Struct({
10149
10253
  name: Schema.Literal("org_invite_sent"),
10150
10254
  props: OrgInviteSentProps
@@ -10530,6 +10634,14 @@ Schema.Struct({
10530
10634
  account_kind: Schema.optional(AccountKindSchema)
10531
10635
  })
10532
10636
  });
10637
+ Schema.Struct({
10638
+ name: Schema.Literal("ui_approval_sign_in_required"),
10639
+ props: Schema.Struct({
10640
+ ...TelemetryEnvelopeProps,
10641
+ approval_id: Schema.String,
10642
+ reason: Schema.Literals(["signed_out", "wrong_account"])
10643
+ })
10644
+ });
10533
10645
  Schema.Struct({
10534
10646
  name: Schema.Literal("payroll_group_changed"),
10535
10647
  props: Schema.Struct({
@@ -10785,6 +10897,8 @@ const CLI_COMMAND_NAMES = [
10785
10897
  "payment.get",
10786
10898
  "payment.wait",
10787
10899
  "payment.retry",
10900
+ "payment.cancel",
10901
+ "payment.redirect",
10788
10902
  "inbox",
10789
10903
  CAPXUL_OPERATIONS.inbox.list,
10790
10904
  "inbox.get",
@@ -11018,10 +11132,14 @@ const BootstrapFailedProps = Schema.Struct({
11018
11132
  });
11019
11133
  const MemberActivationStartedProps = Schema.Struct({
11020
11134
  ...TelemetryEnvelopeProps,
11135
+ entry_path: JourneyVariantProps.entry_path,
11136
+ surface: JourneyVariantProps.surface,
11021
11137
  $insert_id: Schema.String
11022
11138
  });
11023
11139
  const MemberActivationReadyProps = Schema.Struct({
11024
11140
  ...TelemetryEnvelopeProps,
11141
+ entry_path: JourneyVariantProps.entry_path,
11142
+ surface: JourneyVariantProps.surface,
11025
11143
  $insert_id: Schema.String,
11026
11144
  duration_ms: DurationMsSchema
11027
11145
  });
@@ -12567,7 +12685,7 @@ function mapAccountLifecycle(input) {
12567
12685
  }
12568
12686
  //#endregion
12569
12687
  //#region package.json
12570
- var version = "4.20.0-beta.27";
12688
+ var version = "4.20.0-beta.29";
12571
12689
  //#endregion
12572
12690
  //#region src/observability/telemetry/exception-projection.ts
12573
12691
  /** Fixed fallback for failures that have no safe message. */
@@ -12619,7 +12737,7 @@ function report(adapter, kind, operation, cause, invocation, origin, authoritati
12619
12737
  link = link.cause;
12620
12738
  }
12621
12739
  } else if (reportedByMachine(cause)) return;
12622
- const operationName = normalizeCapxulOperation(operation);
12740
+ const operationName = isCapxulOperation(operation) ? operation : normalizeCapxulOperation(readFailureOperation(cause) ?? operation);
12623
12741
  const kindName = normalizeErrorKind(errorKind(cause));
12624
12742
  const context = sanitizeObservationContext({
12625
12743
  ...invocation.context,
@@ -13005,7 +13123,7 @@ const REVERT_SELECTOR_RE = /^0x(?:[0-9a-fA-F]{8})?$/u;
13005
13123
  //#region src/observability/posthog-failure.ts
13006
13124
  /** The expected-outcome codes, read as plain strings so a free-form kind can be looked up. */
13007
13125
  /** Stable PostHog event used for typed failures that are expected product outcomes. */
13008
- const CAPXUL_SDK_EXPECTED_OUTCOME_EVENT = "capxul_sdk_expected_outcome";
13126
+ const CAPXUL_SDK_EXPECTED_OUTCOME_EVENT = SDK_EXPECTED_OUTCOME_EVENT_NAME;
13009
13127
  /**
13010
13128
  * Adapt the host's already-initialized PostHog-like client. This function does
13011
13129
  * not import, initialize, configure, or own PostHog.
@@ -15688,6 +15806,12 @@ function paymentsV2Signature(name) {
15688
15806
  const SEND_SIGNATURE = paymentsV2Signature("send");
15689
15807
  const SEND_BATCH_SIGNATURE = paymentsV2Signature("sendBatch");
15690
15808
  const CREATE_COMMITMENT_SIGNATURE = paymentsV2Signature("createCommitment");
15809
+ /**
15810
+ * #2299 ruling C: only an Owner cancels or redirects an Organization-funded
15811
+ * commitment, so these two sit in the Owner role's scope, never a Budget's.
15812
+ * The contract admits only the commitment's creator, the Organization Safe.
15813
+ */
15814
+ const OWNER_COMMITMENT_SIGNATURES = [paymentsV2Signature("cancel"), paymentsV2Signature("redirect")];
15691
15815
  //#endregion
15692
15816
  //#region ../protocol/src/evm/organization-payments.ts
15693
15817
  /** Zodiac ModuleProxyFactory on Base Sepolia. */
@@ -15924,24 +16048,18 @@ function encodePreparedPaymentCallData(command) {
15924
16048
  }));
15925
16049
  }
15926
16050
  const [item] = command.items;
15927
- if (command.executionRoute.kind !== "direct" || command.items.length !== 1 || item === void 0 || isBirth(item)) throw Errors.invalidInput("items", "Commitment lifecycle requires one direct execution");
15928
- return safe4337ExecuteUserOpCallData({
16051
+ const route = command.executionRoute;
16052
+ if (command.items.length !== 1 || item === void 0 || isBirth(item) || route.kind !== "direct" && item.operation === "claim_commitment") throw Errors.invalidInput("items", "Commitment lifecycle requires one lone execution");
16053
+ const call = {
15929
16054
  to: command.contractAddress,
16055
+ value: 0n,
15930
16056
  data: lifecycleData(item)
15931
- });
15932
- }
15933
- //#endregion
15934
- //#region ../protocol/src/evm/role-dsl.ts
15935
- const ORG_BUDGET_ACTIONS = [
15936
- "send",
15937
- "sendBatch",
15938
- "createCommitment"
15939
- ];
15940
- /** Derive the Zodiac substrate key for one durable Permission identity. */
15941
- function roleKeyForPermission(permissionId) {
15942
- const normalized = permissionId.trim();
15943
- if (normalized.length === 0 || normalized !== permissionId) throw Errors.invalidInput("permissionId", "must be a non-empty canonical Permission ID");
15944
- return keccak256(toBytes(`permission ${permissionId}`));
16057
+ };
16058
+ return route.kind === "direct" ? safe4337ExecuteUserOpCallData(call) : buildOrgRoleSpendPersonalCallData(buildOrgRoleMultiCall({
16059
+ rolesModuleAddress: route.rolesModule,
16060
+ roleKey: route.roleKey,
16061
+ calls: [call]
16062
+ }));
15945
16063
  }
15946
16064
  //#endregion
15947
16065
  //#region src/payments/operation-verification.ts
@@ -15970,6 +16088,16 @@ function moneySafeOpDigest(operation, chainId) {
15970
16088
  userOp: safe4337UserOperation(operation)
15971
16089
  });
15972
16090
  }
16091
+ /**
16092
+ * An Organization-funded cancel or redirect runs through the Owner role
16093
+ * (#2299), which no Budget names. Its role key cannot widen what the signer
16094
+ * may do: the Roles module admits only a role the signer's Safe holds, and the
16095
+ * contract admits only the commitment's creator.
16096
+ */
16097
+ function isOwnerLifecycle(command) {
16098
+ const [item] = command.items;
16099
+ return command.items.length === 1 && (item?.operation === "cancel_commitment" || item?.operation === "redirect_commitment");
16100
+ }
15973
16101
  /** Verify every actual Money signing target against configured trust anchors. */
15974
16102
  function verifiesMoneyOperation(evidence, expectedSafe, chainId, permissionId) {
15975
16103
  try {
@@ -15980,7 +16108,7 @@ function verifiesMoneyOperation(evidence, expectedSafe, chainId, permissionId) {
15980
16108
  const route = command.executionRoute;
15981
16109
  if (route.kind === "direct") {
15982
16110
  if (permissionId !== void 0 || command.senderSafe.toLowerCase() !== expectedSafe.toLowerCase()) return false;
15983
- } else if (permissionId === void 0 || route.roleKey.toLowerCase() !== roleKeyForPermission(permissionId).toLowerCase() || command.senderSafe.toLowerCase() !== route.organizationSafe.toLowerCase()) return false;
16111
+ } else if ((permissionId === void 0 ? !isOwnerLifecycle(command) : route.roleKey.toLowerCase() !== roleKeyForPermission(permissionId).toLowerCase()) || command.senderSafe.toLowerCase() !== route.organizationSafe.toLowerCase()) return false;
15984
16112
  if (operation.callData.toLowerCase() !== encodePreparedPaymentCallData(command).toLowerCase()) return false;
15985
16113
  const digest = moneySafeOpDigest(operation, chainId);
15986
16114
  return evidence.digest.toLowerCase() === digest.toLowerCase() && evidence.executionId === `money_execution_${digest.slice(2).toLowerCase()}`;
@@ -16770,9 +16898,11 @@ function subscribeFreshQuery(deps, args, listener, operation, projection, option
16770
16898
  //#region src/payments/lifecycle-execution.ts
16771
16899
  /**
16772
16900
  * The prepared command is this lifecycle intent; a retry also names the
16773
- * original birth command of the scope it was asked for.
16901
+ * original birth command of the scope it was asked for. A claim, and an
16902
+ * Account's own cancel or redirect, runs directly from the signer's Safe; an
16903
+ * Organization's cancel or redirect runs through its Owner role (#2299).
16774
16904
  */
16775
- const lifecycleCommandCheck = (intent, expectedOrganizationId) => async (request, evidence) => await matchesIntent(intent)(request, evidence) && (intent.kind !== "retry" || (expectedOrganizationId === void 0 ? evidence.sourceCommand?.kind === "birth" && evidence.sourceCommand.actor.kind === "account" : evidence.sourceCommand?.kind === "birth" && evidence.sourceCommand.actor.kind === "organization" && evidence.sourceCommand.actor.orgId === expectedOrganizationId));
16905
+ const lifecycleCommandCheck = (intent, expectedOrganizationId) => async (request, evidence) => await matchesIntent(intent)(request, evidence) && (intent.kind === "retry" ? expectedOrganizationId === void 0 ? evidence.sourceCommand?.kind === "birth" && evidence.sourceCommand.actor.kind === "account" : evidence.sourceCommand?.kind === "birth" && evidence.sourceCommand.actor.kind === "organization" && evidence.sourceCommand.actor.orgId === expectedOrganizationId : evidence.preparedCommand?.executionRoute.kind === (intent.kind === "claim" || expectedOrganizationId === void 0 ? "direct" : "zodiac_role"));
16776
16906
  /** The prepared command pays exactly the request the payer confirmed. */
16777
16907
  const requestPaymentCheck = (confirmation) => async (request) => "kind" in request && request.kind === "payment_request" && canonicalJson(request.confirmation) === canonicalJson(confirmation);
16778
16908
  function executePaymentLifecycle(deps, intent, signal, controls, expectedOrganizationId, onPrepared) {
@@ -17693,17 +17823,21 @@ function paymentExecutionIntent(input) {
17693
17823
  const to = normalizeTargetForBackend(input.to, "to");
17694
17824
  if (!to.ok) return to;
17695
17825
  try {
17696
- return {
17826
+ const decoded = Schema.decodeResult(PaymentExecutionIntentSchema, { onExcessProperty: "error" })({
17827
+ to: to.value,
17828
+ amount: input.amount,
17829
+ ...input.externalAddressAcknowledged === void 0 ? {} : { externalAddressAcknowledged: input.externalAddressAcknowledged },
17830
+ ...input.paymentType === void 0 ? {} : { paymentType: input.paymentType },
17831
+ ...input.document === void 0 ? {} : { document: input.document },
17832
+ ...input.supportingReceipts === void 0 ? {} : { supportingReceipts: input.supportingReceipts },
17833
+ ...input.timing === void 0 ? {} : { timing: input.timing }
17834
+ });
17835
+ return Result.isSuccess(decoded) ? {
17697
17836
  ok: true,
17698
- value: Schema.decodeSync(PaymentExecutionIntentSchema, { onExcessProperty: "error" })({
17699
- to: to.value,
17700
- amount: input.amount,
17701
- ...input.externalAddressAcknowledged === void 0 ? {} : { externalAddressAcknowledged: input.externalAddressAcknowledged },
17702
- ...input.paymentType === void 0 ? {} : { paymentType: input.paymentType },
17703
- ...input.document === void 0 ? {} : { document: input.document },
17704
- ...input.supportingReceipts === void 0 ? {} : { supportingReceipts: input.supportingReceipts },
17705
- ...input.timing === void 0 ? {} : { timing: input.timing }
17706
- })
17837
+ value: decoded.success
17838
+ } : {
17839
+ ok: false,
17840
+ error: invalidPaymentField(decoded.failure.issue, "invalid personal payment input")
17707
17841
  };
17708
17842
  } catch {
17709
17843
  return {
@@ -17712,6 +17846,30 @@ function paymentExecutionIntent(input) {
17712
17846
  };
17713
17847
  }
17714
17848
  }
17849
+ const formatIssue = SchemaIssue.makeFormatterStandardSchemaV1();
17850
+ /** A key the schema allows only as absent (`currency?: never`). Effect reports it as an empty `AnyOf`. */
17851
+ const forbiddenKey = (ast) => SchemaAST.isUnion(ast) && ast.types.every((type) => SchemaAST.isNever(type) || SchemaAST.isUndefined(type));
17852
+ /** Whether the issue that the formatter reports first is a key the caller must not pass. */
17853
+ function unknownKey(issue) {
17854
+ switch (issue._tag) {
17855
+ case "Encoding":
17856
+ case "Filter":
17857
+ case "Pointer": return unknownKey(issue.issue);
17858
+ case "Composite": return unknownKey(issue.issues[0]);
17859
+ case "AnyOf": return issue.issues[0] === void 0 ? forbiddenKey(issue.ast) : unknownKey(issue.issues[0]);
17860
+ default: return issue._tag === "UnexpectedKey";
17861
+ }
17862
+ }
17863
+ /**
17864
+ * The first failing field and why, e.g. `amount.value`, as the backend's row decode names it (#2381).
17865
+ * `callerPath` maps the decoded path to the path the caller passed (#2400).
17866
+ */
17867
+ function invalidPaymentField(issue, fallbackReason, callerPath = (path) => path) {
17868
+ const first = formatIssue(issue).issues[0];
17869
+ const path = callerPath(first?.path?.map((segment) => String(typeof segment === "object" ? segment.key : segment)) ?? []);
17870
+ const reason = path.length > 0 && unknownKey(issue) ? `unknown field \`${path.at(-1)}\`` : first?.message;
17871
+ return Errors.invalidInput(path.length === 0 ? "payment" : path.join("."), reason ?? fallbackReason);
17872
+ }
17715
17873
  function normalizePaymentTiming(payment) {
17716
17874
  const { release: _release, ...rest } = payment;
17717
17875
  return {
@@ -18280,7 +18438,13 @@ const organizationPaymentIntentSchema = Schema.Struct({
18280
18438
  items: PrepareOrganizationPaymentExecutionInputSchema.fields.items,
18281
18439
  lineage: PrepareOrganizationPaymentExecutionInputSchema.fields.lineage
18282
18440
  });
18283
- function organizationPaymentIntent(orgId, input) {
18441
+ /** One Payment's decoded `items.0.amount.value` is the caller's `amount.value`. */
18442
+ const singleItemPath = (path) => path[0] === "items" && path[1] === "0" ? path.slice(2) : path;
18443
+ /**
18444
+ * Decode a batch. `single` says the caller passed one Payment that its call
18445
+ * site wrapped as `items: [item]`, so a refusal names the caller's own field.
18446
+ */
18447
+ function organizationPaymentIntent(orgId, input, single = false) {
18284
18448
  if (input.requestKey !== void 0) {
18285
18449
  if (Schema.decodeResult(PrepareOrganizationPaymentExecutionInputSchema.fields.requestKey)(input.requestKey)._tag === "Failure") return {
18286
18450
  ok: false,
@@ -18293,13 +18457,14 @@ function organizationPaymentIntent(orgId, input) {
18293
18457
  items: input.items,
18294
18458
  ...input.lineage === void 0 ? {} : { lineage: input.lineage }
18295
18459
  });
18296
- return decoded._tag === "Failure" ? {
18297
- ok: false,
18298
- error: Errors.invalidInput("payment", "invalid Organization Payment input")
18299
- } : {
18460
+ if (decoded._tag === "Success") return {
18300
18461
  ok: true,
18301
18462
  value: decoded.success
18302
18463
  };
18464
+ return {
18465
+ ok: false,
18466
+ error: invalidPaymentField(decoded.failure.issue, "invalid Organization Payment input", single ? singleItemPath : void 0)
18467
+ };
18303
18468
  }
18304
18469
  async function organizationPaymentRequest(scope, actor, intent, providedKey) {
18305
18470
  let requestKey;
@@ -18405,12 +18570,12 @@ function executionDependencies$1(input) {
18405
18570
  * Submit one Organization payment batch. Callers that repeat a request key join
18406
18571
  * the flight already in progress instead of preparing a second execution.
18407
18572
  */
18408
- async function executeOrganizationBatch(scope, input, signal, operation, controls, onPrepared) {
18573
+ async function executeOrganizationBatch(scope, input, signal, operation, controls, onPrepared, single = false) {
18409
18574
  if (signal?.aborted === true) return {
18410
18575
  ok: false,
18411
18576
  error: Errors.cancelled({ operation })
18412
18577
  };
18413
- const intent = organizationPaymentIntent(scope.orgId, input);
18578
+ const intent = organizationPaymentIntent(scope.orgId, input, single);
18414
18579
  if (!intent.ok) return intent;
18415
18580
  const execution = executionDependencies$1(scope.deps);
18416
18581
  if (execution === null) return {
@@ -18527,7 +18692,28 @@ function makeOrganizationPaymentsMethods(deps, orgId) {
18527
18692
  requestKeyScope: deps.requestKeyScope ?? `client-${crypto.randomUUID()}`,
18528
18693
  flights: /* @__PURE__ */ new Map()
18529
18694
  };
18530
- const execute = (input, signal, operation = "organization-pay-batch", controls, onPrepared) => executeOrganizationBatch(scope, input, signal, operation, controls, onPrepared);
18695
+ const execute = (input, signal, operation = "organization-pay-batch", controls, onPrepared, single = false) => executeOrganizationBatch(scope, input, signal, operation, controls, onPrepared, single);
18696
+ /** One Owner-signed cancel or redirect, answering the one Payment it acted on (#2299). */
18697
+ const ownerLifecycle = (intent, controls, operation) => runCapxulEffect(Effect.gen(function* () {
18698
+ const captured = carryInvocationParentSpan(controls, yield* Effect.currentSpan.pipe(Effect.orDie));
18699
+ return yield* awaitCapxulResult(async () => {
18700
+ const execution = executionDependencies$1(deps);
18701
+ if (execution === null) return {
18702
+ ok: false,
18703
+ error: Errors.notImplemented("organizationPayments", "executionComposition")
18704
+ };
18705
+ const result = await executePaymentLifecycle(execution, intent, controls?.signal, captured, orgId);
18706
+ if (!result.ok) return result;
18707
+ const [payment, ...rest] = result.value.payments;
18708
+ return payment === void 0 || rest.length !== 0 ? {
18709
+ ok: false,
18710
+ error: Errors.unknown()
18711
+ } : {
18712
+ ok: true,
18713
+ value: normalizePaymentTiming(payment)
18714
+ };
18715
+ });
18716
+ }), observationOnlyControls(controls), operation, deps.runPromise);
18531
18717
  return {
18532
18718
  ...makeOrganizationPaymentReadMethods(deps, orgId),
18533
18719
  retry: (paymentId, options) => {
@@ -18573,7 +18759,7 @@ function makeOrganizationPaymentsMethods(deps, orgId) {
18573
18759
  ...providedRequestKey === void 0 ? {} : { requestKey: providedRequestKey },
18574
18760
  items: [item],
18575
18761
  ...lineage === void 0 ? {} : { lineage }
18576
- }, controls?.signal, "organization-pay", captured, options?.onPrepared);
18762
+ }, controls?.signal, "organization-pay", captured, options?.onPrepared, true);
18577
18763
  if (!result.ok) return result;
18578
18764
  const payment = result.value[0];
18579
18765
  return payment === void 0 ? {
@@ -18586,6 +18772,19 @@ function makeOrganizationPaymentsMethods(deps, orgId) {
18586
18772
  });
18587
18773
  }), observationOnlyControls(controls), CAPXUL_OPERATIONS.organizationPayments.pay, deps.runPromise);
18588
18774
  },
18775
+ cancel: (paymentId, options) => ownerLifecycle({
18776
+ kind: "cancel",
18777
+ paymentId
18778
+ }, deps.invocationControls?.(options) ?? options, CAPXUL_OPERATIONS.organizationPayments.cancel),
18779
+ redirect: (input, options) => {
18780
+ const controls = deps.invocationControls?.(options) ?? options;
18781
+ const recipient = normalizeTargetForBackend(input.to, "to");
18782
+ return recipient.ok ? ownerLifecycle({
18783
+ kind: "redirect",
18784
+ paymentId: input.paymentId,
18785
+ recipient: recipient.value
18786
+ }, controls, CAPXUL_OPERATIONS.organizationPayments.redirect) : runCapxulEffect(Effect.fail(recipient.error), observationOnlyControls(controls), CAPXUL_OPERATIONS.organizationPayments.redirect, deps.runPromise);
18787
+ },
18589
18788
  payBatch: (input, options) => {
18590
18789
  const controls = deps.invocationControls?.(options) ?? options;
18591
18790
  return runCapxulEffect(Effect.gen(function* () {
@@ -19745,20 +19944,45 @@ async function checked(deps, signerAddress, prepared, expected, close) {
19745
19944
  };
19746
19945
  return close ? abandonPrepared(deps, evidence.executionId, "prepared_mismatch", refused) : refused;
19747
19946
  }
19748
- /** A retry, a request payment or a payroll run, rather than a new Payment. */
19749
- const isCommandApproval = (input) => "retry" in input || "request" in input || "payrollRun" in input;
19750
- /** Prepare a retry, a request payment or a payroll run for `signerAddress`, checked. */
19947
+ /** A retry, cancel or redirect, a request payment or a payroll run, rather than a new Payment. */
19948
+ const isCommandApproval = (input) => "retry" in input || "cancel" in input || "redirect" in input || "request" in input || "payrollRun" in input;
19949
+ /** The next step of an existing Payment the input names; a recipient that cannot be one is refused. */
19950
+ function lifecycleIntent(input) {
19951
+ if ("retry" in input) return {
19952
+ ok: true,
19953
+ value: {
19954
+ kind: "retry",
19955
+ paymentId: input.retry
19956
+ }
19957
+ };
19958
+ if ("cancel" in input) return {
19959
+ ok: true,
19960
+ value: {
19961
+ kind: "cancel",
19962
+ paymentId: input.cancel
19963
+ }
19964
+ };
19965
+ const recipient = normalizeTargetForBackend(input.to, "to");
19966
+ return recipient.ok ? {
19967
+ ok: true,
19968
+ value: {
19969
+ kind: "redirect",
19970
+ paymentId: input.redirect,
19971
+ recipient: recipient.value
19972
+ }
19973
+ } : recipient;
19974
+ }
19975
+ /** Prepare a retry, cancel, redirect, request payment or payroll run for `signerAddress`, checked. */
19751
19976
  function prepareVerifiedCommand(deps, input, signerAddress, signal) {
19752
- if ("retry" in input) return prepareVerifiedRetry(deps, input, signerAddress, signal);
19753
19977
  if ("request" in input) return prepareVerifiedRequestPayment(deps, input, signerAddress, signal);
19754
- return prepareVerifiedPayrollRun(deps, input, signerAddress, signal);
19755
- }
19756
- /** The retry of one Payment, prepared in the scope that paid it. */
19757
- async function prepareVerifiedRetry(deps, input, signerAddress, signal) {
19758
- const intent = {
19759
- kind: "retry",
19760
- paymentId: input.retry
19761
- };
19978
+ if ("payrollRun" in input) return prepareVerifiedPayrollRun(deps, input, signerAddress, signal);
19979
+ return prepareVerifiedLifecycle(deps, input, signerAddress, signal);
19980
+ }
19981
+ /** The next step of one Payment, prepared in the scope that paid it. */
19982
+ async function prepareVerifiedLifecycle(deps, input, signerAddress, signal) {
19983
+ const named = lifecycleIntent(input);
19984
+ if (!named.ok) return named;
19985
+ const intent = named.value;
19762
19986
  return checked(deps, signerAddress, await runIfActive(signal, CAPXUL_OPERATIONS.payments.prepareLifecycleExecution, () => callOperation(deps.convexCall, lifecycles.prepareScoped, {
19763
19987
  signerAddress,
19764
19988
  intent,
@@ -19827,7 +20051,7 @@ async function prepareVerifiedOrganizationPayment(deps, input, signerAddress, si
19827
20051
  items: [item],
19828
20052
  ...lineage === void 0 ? {} : { lineage },
19829
20053
  ...requestKey === void 0 ? {} : { requestKey }
19830
- });
20054
+ }, true);
19831
20055
  if (!intent.ok) return intent;
19832
20056
  const session = deps.actor?.authSession() ?? null;
19833
20057
  if (session === null || deps.chainId === void 0) return {
@@ -19885,6 +20109,16 @@ async function readApprovalPayments(deps, paymentIds, organizationId, signal) {
19885
20109
  }
19886
20110
  //#endregion
19887
20111
  //#region src/accounts/approvals.ts
20112
+ /** A Permission write or an invitation, rather than a Payment. */
20113
+ const isPermissionChange = (input) => "permission" in input || "invite" in input;
20114
+ /** Only a Permission change or an invitation moves no money: its own owner sends it. */
20115
+ const movesMoney = (approval) => approval.kind !== "permission_change" && approval.kind !== "invitation";
20116
+ /**
20117
+ * A wait ends once it is sent: a Permission change or an invitation, and a
20118
+ * cancel or redirect of a held Payment, whose Payment was sent long before
20119
+ * and so cannot say whether this step was (#2439).
20120
+ */
20121
+ const endsOnceSent = (approval) => !movesMoney(approval) || approval.kind === "payment_cancel" || approval.kind === "payment_redirect";
19888
20122
  function makeApprovalsMethods(deps) {
19889
20123
  const call = (name, row, args, signal) => runIfActive(signal, name, () => callOperation(deps.convexCall, row, args, { fresh: true }).pipe(Effect.mapError((publicError) => ({ publicError }))), deps.runPromise);
19890
20124
  const get = (approvalId) => call(approvals.get.name, approvals.get, { approvalId });
@@ -19925,6 +20159,10 @@ function makeApprovalsMethods(deps) {
19925
20159
  const prepare = async (input, options) => {
19926
20160
  const signal = options?.signal;
19927
20161
  if (isCommandApproval(input)) return openApproval(signal, (signerAddress) => prepareVerifiedCommand(deps, input, signerAddress, signal));
20162
+ if (isPermissionChange(input)) return openApproval(signal, async (signerAddress) => {
20163
+ const { prepareVerifiedPermissionChange } = await import("./permission-approvals-B4iv9bXz.mjs");
20164
+ return prepareVerifiedPermissionChange(deps, input, signerAddress, signal);
20165
+ });
19928
20166
  if ("organizationId" in input) return openApproval(signal, (signerAddress) => prepareVerifiedOrganizationPayment(deps, input, signerAddress, signal));
19929
20167
  const intent = paymentExecutionIntent(input);
19930
20168
  if (!intent.ok) return intent;
@@ -19991,10 +20229,23 @@ function makeApprovalsMethods(deps) {
19991
20229
  const submit = async (approvalId) => {
19992
20230
  const consumed = await call(approvals.consume.name, approvals.consume, { approvalId });
19993
20231
  if (!consumed.ok) return consumed;
19994
- const sent = await call(executions.submitCommand.name, executions.submitCommand, {
19995
- executionId: consumed.value.approval.operation.executionId,
19996
- signature: consumed.value.signature
19997
- });
20232
+ const { approval, signature } = consumed.value;
20233
+ const signed = {
20234
+ executionId: approval.operation.executionId,
20235
+ signature
20236
+ };
20237
+ if (!movesMoney(approval)) {
20238
+ const row = approval.kind === "invitation" ? configurations : commands;
20239
+ const sent = await call(row.submitExecution.name, row.submitExecution, signed);
20240
+ return sent.ok ? {
20241
+ ok: true,
20242
+ value: {
20243
+ approval,
20244
+ payments: []
20245
+ }
20246
+ } : sent;
20247
+ }
20248
+ const sent = await call(executions.submitCommand.name, executions.submitCommand, signed);
19998
20249
  if (!sent.ok) return sent;
19999
20250
  return {
20000
20251
  ok: true,
@@ -20017,6 +20268,20 @@ function makeApprovalsMethods(deps) {
20017
20268
  ok: true,
20018
20269
  value: null
20019
20270
  };
20271
+ if (endsOnceSent(approval)) {
20272
+ if (approval.state !== "approved" && approval.state !== "consumed") return {
20273
+ ok: true,
20274
+ value: {
20275
+ approval,
20276
+ payments: []
20277
+ }
20278
+ };
20279
+ const sent = await submit(approvalId);
20280
+ return sent.ok || !anotherSendInFlight(sent.error) ? sent : {
20281
+ ok: true,
20282
+ value: null
20283
+ };
20284
+ }
20020
20285
  const payments = await readApprovalPayments(deps, approval.operation.paymentIds, organizationId ?? (approval.actor.kind === "organization" ? approval.actor.orgId : void 0), signal);
20021
20286
  if (!payments.ok) return payments;
20022
20287
  if (unsent(approval, payments.value)) {
@@ -22946,4 +23211,4 @@ function executeIdentityProductObservation(telemetry, record, state) {
22946
23211
  return ignoreTransportFailure(() => invocationTelemetry.emit(mapped), "emit", mapped.name).pipe(Effect.andThen(ignoreTransportFailure(after, mapped.name === "auth_signed_out" ? "reset" : "identify", mapped.name)));
22947
23212
  }
22948
23213
  //#endregion
22949
- export { parseAssetAmount as $, InvitationViewSchema as $n, SystemClockLayer as $r, version as $t, registerProductTelemetryInvocation as A, toJwtToken as Ai, details as An, toWeiAmount as Ar, safe4337ExecuteUserOpCallData as At, makeOrganizationPaymentsMethods as B, AuthCachePortTag as Bi, FixedOfferDraftSchema as Bn, runCapxulEffect as Br, failureEvidenceProps as Bt, AccountReadPortTag as C, parseAuthSession as Ci, CLI_INVOCATION_KINDS as Cn, toAssetId as Cr, SEND_BATCH_SIGNATURE as Ct, convexCallErrorFromCapxul as D, toEpochSeconds as Di, isCapxulOperation as Dn, isEvmAddress as Dr, TOKENS as Dt, ConvexCallPortTag as E, toEpochMs as Ei, CAPXUL_OPERATIONS as En, EVM_ADDRESS_RE$1 as Er, CONFIGURED_MONEY_ASSETS as Et, decodePayrollRunInput as F, FAILURE_MODES$1 as Fi, replacements as Fn, publicIdentityFailure as Fr, SAFE_SENTINEL_OWNERS as Ft, makeActorRelationshipMethods as G, treasuries as Gn, toCapxulResult as Gr, observationContextProps as Gt, makePaymentsMethods as H, members as Hn, awaitCapxulResult as Hr, postHogFailureObservation as Ht, toPayrollGroupId as I, isFailureMode as Ii, PreparedPermissionExecutionSchema as In, runIdentityFacade as Ir, callOperation as It, makeTargetsMethods as J, ORGANIZATION_SETUP_REASONS as Jn, EXPECTED_OPERATION_OUTCOMES as Jr, SDK_VERSION as Jt, makeCheckoutMethods as K, workspaces as Kn, formatTraceparent as Kr, withCapturedAttemptContext as Kt, makeAccountMethods as L, isRecord as Li, PermissionCommandViewSchema as Ln, runPortEffect as Lr, asCapxulError as Lt, makeApprovalsMethods as M, Errors as Mi, authorities as Mn, reference as Mr, buildSafeMultiSendExecTransactionCall as Mt, makePayrollMethods as N, CapxulError as Ni, commands as Nn, makeAuthMethods as Nr, BASE_SEPOLIA_CHAIN_ID as Nt, retryIdempotentRead as O, toAuthUserId as Oi, normalizeCapxulOperation as On, toAddress as Or, deriveCapxulSafeAddress as Ot, makeUnavailablePayrollMethods as P, isCapxulError as Pi, configurations as Pn, askIdentity as Pr, MULTI_SEND_CALL_ONLY as Pt, formatAssetAmount as Q, toOrgId as Qn, oauthBearerAuthClient as Qr, safeExceptionLabel as Qt, emitDepositInitiated as R, readField as Ri, PayrollGroupIdSchema as Rn, sessionFromActor as Rr, toPublicError$1 as Rt, valueIn as S, BrowserAuthCacheAdapter as Si, CLI_COMMAND_NAMES as Sn, currencySymbolFor as Sr, SAFE_ENABLE_MODULE_ABI as St, TelemetryPortTag as T, toDurationMs as Ti, stampTelemetryEnvelope as Tn, toExactAssetAmount as Tr, CAPXUL_PAYMENTS_V2_ADDRESS as Tt, waitForPaymentSubmission as U, roles as Un, defectAsCapxulError as Ur, observeFailedResult as Ut, makeOfframpMethods as V, invitations as Vn, withVerifiedOrganizationScope as Vr, CAPXUL_SDK_EXPECTED_OUTCOME_EVENT as Vt, makeDestinationsMethods as W, setups as Wn, runIfActive as Wr, observeSdkClient as Wt, assetAmountFromRaw as X, CHAIN_UPSTREAMS as Xn, carryInvocationParentSpan as Xr, normalizeExceptionOperation as Xt, subscribeFreshQuery as Y, ORGANIZATION_SETUP_STATUSES as Yn, ActorFailure as Yr, normalizeExceptionErrorKind as Yt, assetSymbolFor as Z, decodeChainCause as Zn, withInvocationParentSpan as Zr, projectSdkException as Zt, accountFromAccountPositions as _, AuthClientError as _i, toPartyId as _n, toBudgetId as _r, ROLES_EXEC_TRANSACTION_WITH_ROLE_ABI as _t, paymentPhase as a, observationRequestHeaders as ai, isSignerNotReadySignal as an, images as ar, fingerprintPaymentIntent as at, MONEY_READ_RATES as b, globalLocalStorage as bi, redactTelemetryProps as bn, toRoleKey as br, ROLES_SET_UP_ABI as bt, makePaymentDocumentsMethods as c, readInvocationObservation as ci, signerFailure as cn, isBytes32 as cr, roleKeyForPermission as ct, SmartAccountPortTag as d, redactUrlSecrets as di, makeClientRequestMethods as dn, toLowerBytes32 as dr, CREATE_COMMITMENT_SIGNATURE as dt, ClockError as ei, isSettingUpLifecycle as en, hashPermissionReplacementPreview as er, requireAsset as et, smartAccountErrorFromCapxul as f, isCredentialField as fi, toCountryCode as fn, toLowerHex as fr, MODULE_PROXY_FACTORY_ABI as ft, ConvexAccountLayer as g, authClientPortFromPromiseAdapter as gi, toHandle as gn, EpochMsSchema$1 as gr, ROLES_ASSIGN_ROLES_ABI as gt, makeAccountsMethods as h, toPublishableKey as hi, HANDLE_RE as hn, DurationMsSchema$1 as hr, PARAMETER_TYPE as ht, inboxPhase as i, BetterAuthBrowserLayer as ii, isEcdsaSignatureHex as in, AppIdSchema$1 as ir, canonicalJson as it, wireChainId as j, toSessionToken as ji, onboardings as jn, absurd as jr, safe4337MultiSendUserOpCallData as jt, bindProductTelemetryInvocation as k, toEmail as ki, OPERATIONS as kn, toChainId as kr, normalizeSafeSaltEmail as kt, makeSmartAccountMethods as l, sanitizeObservationContext as li, resolveFailureMode as ln, toExactHex as lr, ZODIAC_MODULE_PROXY_FACTORY as lt, PonderIndexerAdapter as m, toAppId as mi, toTesterKind as mn, PAYMENT_STATUSES as mr, OPERATOR as mt, bootIdentityFlow as n, readClockNow as ni, causeChain as nn, CurrentPolicySchema as nr, isMoneyParseError as nt, requestPhase as o, attachInvocationObservation as oi, openfortSignerNotReadyCode as on, toAccountId as or, moneySafeOpDigest as ot, makeHoldingsMethods as p, toAllowedOrigin as pi, toKycTier as pn, PAYMENT_DIRECTIONS as pr, MULTI_SEND_SELECTOR as pt, isCheckoutSettlementConsumed as q, OrganizationSetupSchema as qn, CAPXUL_ERROR_CODES as qr, EXCEPTION_MESSAGE as qt, makeMeMethods as r, BetterAuthNodeLayer as ri, injectedWalletSigner as rn, diagnostics as rr, parseMoney as rt, makeActivityMethods as s, copyInvocationObservation as si, recoverRawDigestSigner as sn, PaymentDocumentOptionsSchema as sr, ORG_BUDGET_ACTIONS as st, executeIdentityProductObservation as t, ClockPortTag as ti, UNOBSERVABLE_SIGNER_STATUS as tn, decodePermissionIntent as tr, toRaw as tt, ConvexSmartAccountLayer as u, redactSecrets as ui, makeCurrentUserMethods as un, toLowerAddress as ur, resolveOrgPermissionIdentity as ut, accountPositionsFromWire as v, AuthClientPortTag as vi, validateHandle as vn, toPermissionAssignmentId as vr, ROLES_SET_ALLOWANCE_ABI as vt, accountReadErrorFromCapxul as w, parseCachedJwt as wi, EXCEPTION_EVENT_NAME as wn, toCurrencyCode as wr, SEND_SIGNATURE as wt, valueActivitySummary as x, InMemoryAuthCacheAdapter as xi, CLI_CAPABILITIES as xn, ChainIdSchema$1 as xr, ROLE_MANAGEMENT_SIGNATURES as xt, positionPeg as y, detectAuthCacheAdapter as yi, redactTelemetryEvent as yn, toPermissionId as yr, ROLES_SET_TRANSACTION_UNWRAPPER_ABI as yt, logDroppedEvent as z, AuthCacheError as zi, PayrollGroupInputSchema as zn, observationOnlyControls as zr, failureDetail as zt };
23214
+ export { formatAssetAmount as $, InvitationViewSchema as $n, ActorFailure as $r, version as $t, registerProductTelemetryInvocation as A, toEpochMs as Ai, details as An, EVM_ADDRESS_RE$1 as Ar, safe4337ExecuteUserOpCallData as At, makeOrganizationPaymentsMethods as B, isFailureMode as Bi, FixedOfferDraftSchema as Bn, runIdentityFacade as Br, failureEvidenceProps as Bt, AccountReadPortTag as C, detectAuthCacheAdapter as Ci, CLI_INVOCATION_KINDS as Cn, toPermissionId as Cr, SEND_BATCH_SIGNATURE as Ct, convexCallErrorFromCapxul as D, parseAuthSession as Di, isCapxulOperation as Dn, toAssetId as Dr, TOKENS as Dt, ConvexCallPortTag as E, BrowserAuthCacheAdapter as Ei, CAPXUL_OPERATIONS as En, currencySymbolFor as Er, CONFIGURED_MONEY_ASSETS as Et, decodePayrollRunInput as F, toSessionToken as Fi, replacements as Fn, absurd as Fr, SAFE_SENTINEL_OWNERS as Ft, makeActorRelationshipMethods as G, treasuries as Gn, withVerifiedOrganizationScope as Gr, observationContextProps as Gt, makePaymentsMethods as H, readField as Hi, members as Hn, sessionFromActor as Hr, postHogFailureObservation as Ht, toPayrollGroupId as I, Errors as Ii, PreparedPermissionExecutionSchema as In, reference as Ir, callOperation as It, makeTargetsMethods as J, ORGANIZATION_SETUP_REASONS as Jn, runIfActive as Jr, SDK_VERSION as Jt, makeCheckoutMethods as K, workspaces as Kn, awaitCapxulResult as Kr, withCapturedAttemptContext as Kt, makeAccountMethods as L, CapxulError as Li, PermissionCommandViewSchema as Ln, makeAuthMethods as Lr, asCapxulError as Lt, makeApprovalsMethods as M, toAuthUserId as Mi, authorities as Mn, toAddress as Mr, buildSafeMultiSendExecTransactionCall as Mt, makePayrollMethods as N, toEmail as Ni, commands as Nn, toChainId as Nr, BASE_SEPOLIA_CHAIN_ID as Nt, retryIdempotentRead as O, parseCachedJwt as Oi, normalizeCapxulOperation as On, toCurrencyCode as Or, deriveCapxulSafeAddress as Ot, makeUnavailablePayrollMethods as P, toJwtToken as Pi, configurations as Pn, toWeiAmount as Pr, MULTI_SEND_CALL_ONLY as Pt, assetSymbolFor as Q, toOrgId as Qn, EXPECTED_OPERATION_OUTCOMES as Qr, safeExceptionLabel as Qt, emitDepositInitiated as R, isCapxulError as Ri, PayrollGroupIdSchema as Rn, askIdentity as Rr, toPublicError$1 as Rt, valueIn as S, AuthClientPortTag as Si, CLI_COMMAND_NAMES as Sn, toPermissionAssignmentId as Sr, SAFE_ENABLE_MODULE_ABI as St, TelemetryPortTag as T, InMemoryAuthCacheAdapter as Ti, stampTelemetryEnvelope as Tn, ChainIdSchema$1 as Tr, CAPXUL_PAYMENTS_V2_ADDRESS as Tt, waitForPaymentSubmission as U, AuthCacheError as Ui, roles as Un, observationOnlyControls as Ur, observeFailedResult as Ut, makeOfframpMethods as V, isRecord as Vi, invitations as Vn, runPortEffect as Vr, CAPXUL_SDK_EXPECTED_OUTCOME_EVENT as Vt, makeDestinationsMethods as W, AuthCachePortTag as Wi, setups as Wn, runCapxulEffect as Wr, observeSdkClient as Wt, abandonPrepared as X, CHAIN_UPSTREAMS as Xn, formatTraceparent as Xr, normalizeExceptionOperation as Xt, subscribeFreshQuery as Y, ORGANIZATION_SETUP_STATUSES as Yn, toCapxulResult as Yr, normalizeExceptionErrorKind as Yt, assetAmountFromRaw as Z, decodeChainCause as Zn, CAPXUL_ERROR_CODES as Zr, projectSdkException as Zt, accountFromAccountPositions as _, toAllowedOrigin as _i, toPartyId as _n, PAYMENT_DIRECTIONS as _r, ROLES_EXEC_TRANSACTION_WITH_ROLE_ABI as _t, paymentPhase as a, ClockPortTag as ai, isSignerNotReadySignal as an, ORG_BUDGET_ACTIONS as ar, canonicalJson as at, MONEY_READ_RATES as b, authClientPortFromPromiseAdapter as bi, redactTelemetryProps as bn, EpochMsSchema$1 as br, ROLES_SET_UP_ABI as bt, makePaymentDocumentsMethods as c, BetterAuthBrowserLayer as ci, signerFailure as cn, AppIdSchema$1 as cr, ZODIAC_MODULE_PROXY_FACTORY as ct, SmartAccountPortTag as d, copyInvocationObservation as di, makeClientRequestMethods as dn, PaymentDocumentOptionsSchema as dr, MODULE_PROXY_FACTORY_ABI as dt, carryInvocationParentSpan as ei, isSettingUpLifecycle as en, hashPermissionReplacementPreview as er, parseAssetAmount as et, smartAccountErrorFromCapxul as f, readInvocationObservation as fi, toCountryCode as fn, isBytes32 as fr, MULTI_SEND_SELECTOR as ft, ConvexAccountLayer as g, isCredentialField as gi, toHandle as gn, toLowerHex as gr, ROLES_ASSIGN_ROLES_ABI as gt, makeAccountsMethods as h, redactUrlSecrets as hi, HANDLE_RE as hn, toLowerBytes32 as hr, PARAMETER_TYPE as ht, inboxPhase as i, ClockError as ii, isEcdsaSignatureHex as in, MONTHLY_REFILL_PERIOD_SECONDS as ir, parseMoney as it, wireChainId as j, toEpochSeconds as ji, onboardings as jn, isEvmAddress as jr, safe4337MultiSendUserOpCallData as jt, bindProductTelemetryInvocation as k, toDurationMs as ki, OPERATIONS as kn, toExactAssetAmount as kr, normalizeSafeSaltEmail as kt, makeSmartAccountMethods as l, observationRequestHeaders as li, resolveFailureMode as ln, images as lr, resolveOrgPermissionIdentity as lt, PonderIndexerAdapter as m, redactSecrets as mi, toTesterKind as mn, toLowerAddress as mr, OWNER_COMMITMENT_SIGNATURES as mt, bootIdentityFlow as n, oauthBearerAuthClient as ni, causeChain as nn, keepRefillOnChange as nr, toRaw as nt, requestPhase as o, readClockNow as oi, openfortSignerNotReadyCode as on, roleKeyForPermission as or, fingerprintPaymentIntent as ot, makeHoldingsMethods as p, sanitizeObservationContext as pi, toKycTier as pn, toExactHex as pr, OPERATOR as pt, isCheckoutSettlementConsumed as q, OrganizationSetupSchema as qn, defectAsCapxulError as qr, EXCEPTION_MESSAGE as qt, makeMeMethods as r, SystemClockLayer as ri, injectedWalletSigner as rn, CurrentPolicySchema as rr, isMoneyParseError as rt, makeActivityMethods as s, BetterAuthNodeLayer as si, recoverRawDigestSigner as sn, diagnostics as sr, moneySafeOpDigest as st, executeIdentityProductObservation as t, withInvocationParentSpan as ti, UNOBSERVABLE_SIGNER_STATUS as tn, decodePermissionIntent as tr, requireAsset as tt, ConvexSmartAccountLayer as u, attachInvocationObservation as ui, makeCurrentUserMethods as un, toAccountId as ur, CREATE_COMMITMENT_SIGNATURE as ut, accountPositionsFromWire as v, toAppId as vi, validateHandle as vn, PAYMENT_STATUSES as vr, ROLES_SET_ALLOWANCE_ABI as vt, accountReadErrorFromCapxul as w, globalLocalStorage as wi, EXCEPTION_EVENT_NAME as wn, toRoleKey as wr, SEND_SIGNATURE as wt, valueActivitySummary as x, AuthClientError as xi, CLI_CAPABILITIES as xn, toBudgetId as xr, ROLE_MANAGEMENT_SIGNATURES as xt, positionPeg as y, toPublishableKey as yi, redactTelemetryEvent as yn, DurationMsSchema$1 as yr, ROLES_SET_TRANSACTION_UNWRAPPER_ABI as yt, logDroppedEvent as z, FAILURE_MODES$1 as zi, PayrollGroupInputSchema as zn, publicIdentityFailure as zr, failureDetail as zt };