@capxul/mcp 4.20.0-beta.27 → 4.20.0-beta.28

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.
Files changed (2) hide show
  1. package/dist/main.mjs +276 -124
  2. package/package.json +3 -3
package/dist/main.mjs CHANGED
@@ -1227,7 +1227,7 @@ const FIELD_RULES = {
1227
1227
  },
1228
1228
  anonymousId: {
1229
1229
  maxLength: 128,
1230
- pattern: /^anon_[A-Za-z0-9-]+$/u
1230
+ 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
1231
1231
  },
1232
1232
  traceparent: {
1233
1233
  maxLength: 55,
@@ -10312,6 +10312,28 @@ const probes = {
10312
10312
  })
10313
10313
  };
10314
10314
  //#endregion
10315
+ //#region ../../packages/protocol/src/evm/role-dsl.ts
10316
+ const ORG_BUDGET_ACTIONS = [
10317
+ "send",
10318
+ "sendBatch",
10319
+ "createCommitment"
10320
+ ];
10321
+ /**
10322
+ * How a capped Budget's allowance refills (#2298). `monthly` refills the
10323
+ * allowance to its cap every 30 days. Roles v2 counts fixed periods from the
10324
+ * moment the allowance is set; it has no calendar month. Absent means `none`:
10325
+ * the cap is one pot that spending drains for good.
10326
+ */
10327
+ const ORG_BUDGET_REFILLS = ["monthly", "none"];
10328
+ /** The fixed Roles v2 refill period of a `monthly` Budget: 30 days, in seconds. */
10329
+ const MONTHLY_REFILL_PERIOD_SECONDS = 30n * 24n * 60n * 60n;
10330
+ /** Derive the Zodiac substrate key for one durable Permission identity. */
10331
+ function roleKeyForPermission(permissionId) {
10332
+ const normalized = permissionId.trim();
10333
+ if (normalized.length === 0 || normalized !== permissionId) throw Errors.invalidInput("permissionId", "must be a non-empty canonical Permission ID");
10334
+ return keccak256$1(toBytes$1(`permission ${permissionId}`));
10335
+ }
10336
+ //#endregion
10315
10337
  //#region ../../packages/protocol/src/permissions/permission-schema.ts
10316
10338
  /**
10317
10339
  * One stored `setAllowance` call inside a command's expected authority set.
@@ -10350,12 +10372,19 @@ const BudgetScopeSchema = Schema.Struct({
10350
10372
  })]),
10351
10373
  actions: Schema.NonEmptyArray(Schema.Literals(["pay", "commitments"]))
10352
10374
  }).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" }));
10375
+ /**
10376
+ * How a capped Budget refills (#2298): `monthly` tops the cap back up every 30
10377
+ * days (a fixed Roles v2 period, not a calendar month); `none` never refills.
10378
+ * Absent means `none`, and writers store only `monthly`.
10379
+ */
10380
+ const BudgetRefillSchema = Schema.Literals(ORG_BUDGET_REFILLS);
10353
10381
  /** The accepted product policy stored with one Permission revision. */
10354
10382
  const CurrentPolicySchema = Schema.Union([Schema.Struct({
10355
10383
  type: Schema.Literal("budget"),
10356
10384
  asset: AssetIdSchema,
10357
10385
  limit: Schema.NullOr(Schema.String),
10358
- scope: BudgetScopeSchema
10386
+ scope: BudgetScopeSchema,
10387
+ refill: Schema.optional(BudgetRefillSchema)
10359
10388
  }), Schema.Struct({ type: Schema.Literal("managePeople") })]);
10360
10389
  const BudgetIntentSchema = CurrentPolicySchema.pipe(Schema.refine((value) => {
10361
10390
  if (value.type !== "budget") return false;
@@ -10420,7 +10449,8 @@ const ResolvedPermissionBudgetPolicySchema = Schema.Struct({
10420
10449
  "send",
10421
10450
  "sendBatch",
10422
10451
  "createCommitment"
10423
- ]))
10452
+ ])),
10453
+ refill: Schema.optional(BudgetRefillSchema)
10424
10454
  });
10425
10455
  const PermissionRecipientBindingSchema = Schema.Struct({
10426
10456
  accountId: AccountIdSchema$1,
@@ -10432,16 +10462,22 @@ const StoredPermissionIntentSchema = Schema.Union([Schema.Struct({
10432
10462
  type: Schema.Literal("budget"),
10433
10463
  asset: AssetIdSchema,
10434
10464
  limit: Schema.NullOr(AssetAmountSchema),
10435
- scope: Schema.optional(BudgetScopeSchema)
10465
+ scope: Schema.optional(BudgetScopeSchema),
10466
+ refill: Schema.optional(BudgetRefillSchema)
10436
10467
  }).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") })]);
10437
10468
  /** Writes state every granted right and use a finite positive AssetAmount cap. */
10438
10469
  const PermissionIntentSchema = Schema.Union([Schema.Struct({
10439
10470
  type: Schema.Literal("budget"),
10440
10471
  asset: AssetIdSchema,
10441
10472
  limit: AssetAmountSchema,
10442
- scope: BudgetScopeSchema
10473
+ scope: BudgetScopeSchema,
10474
+ refill: Schema.optional(BudgetRefillSchema)
10443
10475
  }).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") })]);
10444
- /** Decode once and give set-like scope fields one stable command representation. */
10476
+ /**
10477
+ * Decode once and give set-like scope fields one stable command representation.
10478
+ * An explicit `refill: "none"` is kept: on a change it turns a refill off,
10479
+ * where a missing `refill` keeps the Budget's current one (`keepRefillOnChange`).
10480
+ */
10445
10481
  function decodePermissionIntent$1(input) {
10446
10482
  const intent = Schema.decodeUnknownSync(PermissionIntentSchema, { onExcessProperty: "error" })(input);
10447
10483
  if (intent.type === "managePeople") return intent;
@@ -10456,6 +10492,19 @@ function decodePermissionIntent$1(input) {
10456
10492
  }
10457
10493
  });
10458
10494
  }
10495
+ /**
10496
+ * A Budget change that leaves `refill` out keeps the Budget's current refill:
10497
+ * a change never turns a refill off by omission. An explicit `"none"` turns it
10498
+ * off. The backend plans, the SDK verifies and the CLI confirms the same
10499
+ * result. A create has no current refill, so a missing one means `none`.
10500
+ */
10501
+ function keepRefillOnChange(intent, before) {
10502
+ if (intent.type !== "budget" || intent.refill !== void 0 || before?.type !== "budget" || before.refill !== "monthly") return intent;
10503
+ return {
10504
+ ...intent,
10505
+ refill: before.refill
10506
+ };
10507
+ }
10459
10508
  //#endregion
10460
10509
  //#region ../../packages/protocol/src/permissions/replacement-schema.ts
10461
10510
  const InvitationPhaseSchema = Schema.Literals([
@@ -10660,7 +10709,7 @@ const InviteGrantsSchema = Schema.Array(Schema.Union([
10660
10709
  Schema.Struct({
10661
10710
  type: Schema.Literal("new_budget"),
10662
10711
  label: Schema.String.pipe(Schema.refine((value) => value.trim().length >= 1 && value.trim().length <= 128)),
10663
- intent: BudgetIntentSchema
10712
+ intent: BudgetIntentSchema.pipe(Schema.refine((intent) => intent.refill !== "monthly", { message: "An invitation's new Budget cannot refill; change it once granted" }))
10664
10713
  })
10665
10714
  ])).pipe(Schema.refine((grants) => {
10666
10715
  if (grants.length < 1 || grants.length > 16) return false;
@@ -13534,7 +13583,9 @@ const PEOPLE_OPERATIONS = {
13534
13583
  emitSafeConfirmed: "accounts.safes.emitSafeConfirmed",
13535
13584
  readSafe: "accounts.safes.readSafe",
13536
13585
  recordDeployment: "accounts.safes.recordDeployment"
13537
- }
13586
+ },
13587
+ /** The home's requests to pay and Payments to retry, read in one query. */
13588
+ summaries: { loadNeedsYou: "accounts.summaries.loadNeedsYou" }
13538
13589
  },
13539
13590
  /** The host observer's names for `client.approvals.*`. */
13540
13591
  approvals: {
@@ -13793,7 +13844,9 @@ emitSaved: "activity.annotations.emitSaved" },
13793
13844
  pay: "organizationPayments.pay",
13794
13845
  payBatch: "organizationPayments.payBatch",
13795
13846
  prepareExecution: "organizationPayments.prepareExecution",
13796
- retry: "organizationPayments.retry"
13847
+ retry: "organizationPayments.retry",
13848
+ cancel: "organizationPayments.cancel",
13849
+ redirect: "organizationPayments.redirect"
13797
13850
  },
13798
13851
  /**
13799
13852
  * Payments' internal functions and the client's own method names; its public
@@ -13815,7 +13868,9 @@ emitSaved: "activity.annotations.emitSaved" },
13815
13868
  loadContext: "payments.commands.loadContext",
13816
13869
  loadExecutionContext: "payments.commands.loadExecutionContext",
13817
13870
  /** The capped Budget a prepared Organization command spends from, before its allowance is read on chain. */
13818
- loadBudgetAllowance: "payments.commands.loadBudgetAllowance"
13871
+ loadBudgetAllowance: "payments.commands.loadBudgetAllowance",
13872
+ /** What a new Payment command would spend and from which Safe, read before it is born. */
13873
+ loadBirthFunding: "payments.commands.loadBirthFunding"
13819
13874
  },
13820
13875
  lifecycles: { prepareIntent: "payments.lifecycles.prepareIntent" },
13821
13876
  /** The stored UserOperation of every money command: Payment, Permission and configuration. */
@@ -14252,6 +14307,42 @@ const UiStepProps = {
14252
14307
  organization_id: OptionalOrgId,
14253
14308
  reason_code: OptionalReasonCode
14254
14309
  };
14310
+ /**
14311
+ * A journey's variants (`JOURNEYS`): the way a person came into a journey, or
14312
+ * the way they took through it. A variant is a property of the journey's step
14313
+ * events, never a journey of its own, so each journey funnel breaks down by
14314
+ * it. A step event takes only the variants that a journey contract names on
14315
+ * it, picked one by one; the analytics plan test checks both directions.
14316
+ * `pay_path` and `funding_source` have no carrier until their Phase 2 events.
14317
+ * `payment_*` events already carry `recipient_is_capxul_account`, and
14318
+ * `invoice_issued` carries `issuer_scope_kind`; neither is repeated here.
14319
+ */
14320
+ const JourneyVariantProps = {
14321
+ /** How a person came to Get started: by themselves, an invite, money that waited, or an invoice. */
14322
+ entry_path: Schema.optional(Schema.Literals([
14323
+ "signup",
14324
+ "invite",
14325
+ "claim",
14326
+ "invoice"
14327
+ ])),
14328
+ /** Where the person acted. `agent` is an AI agent driving the CLI. */
14329
+ surface: Schema.optional(Schema.Literals([
14330
+ "web",
14331
+ "cli",
14332
+ "agent"
14333
+ ])),
14334
+ /** Who pays: a person from their own account, or an Organization from a Budget. */
14335
+ payer_kind: Schema.optional(Schema.Literals(["person", "organization"])),
14336
+ /** How a request was paid: from the payer's inbox, or by its link at checkout. */
14337
+ pay_path: Schema.optional(Schema.Literals(["inbox", "link"])),
14338
+ /** Where an account's first money came from. `bank` is the planned on-ramp. */
14339
+ funding_source: Schema.optional(Schema.Literals([
14340
+ "deposit",
14341
+ "payment",
14342
+ "claim",
14343
+ "bank"
14344
+ ]))
14345
+ };
14255
14346
  //#endregion
14256
14347
  //#region ../../packages/observability/src/events/money.ts
14257
14348
  /**
@@ -14341,6 +14432,8 @@ const PaymentInitiatedProps = Schema.Struct({
14341
14432
  execution_id: OptionalString,
14342
14433
  payment_lifecycle_id: OptionalString,
14343
14434
  ...TelemetryEnvelopeProps,
14435
+ payer_kind: JourneyVariantProps.payer_kind,
14436
+ surface: JourneyVariantProps.surface,
14344
14437
  ...RecipientTelemetryProps,
14345
14438
  kind: Schema.String,
14346
14439
  payment_id: Schema.String
@@ -14356,6 +14449,8 @@ const PaymentLifecycleProps = {
14356
14449
  const PaymentSettledL2Props = Schema.Struct({
14357
14450
  ...PaymentLifecycleProps,
14358
14451
  ...TelemetryEnvelopeProps,
14452
+ payer_kind: JourneyVariantProps.payer_kind,
14453
+ surface: JourneyVariantProps.surface,
14359
14454
  ...RecipientTelemetryProps,
14360
14455
  kind: Schema.String,
14361
14456
  chain_id: Schema.Finite,
@@ -14563,6 +14658,7 @@ const OrganizationCreationStartedProps = Schema.Struct({
14563
14658
  });
14564
14659
  const OrganizationCreationReadyProps = Schema.Struct({
14565
14660
  ...TelemetryEnvelopeProps,
14661
+ surface: JourneyVariantProps.surface,
14566
14662
  $insert_id: Schema.String,
14567
14663
  organization_id: OrgIdTelemetrySchema,
14568
14664
  attempt_number: AttemptNumberSchema,
@@ -14587,45 +14683,6 @@ const OrganizationCreationFailedProps = Schema.Struct({
14587
14683
  ...ORGANIZATION_SETUP_REASONS
14588
14684
  ]))
14589
14685
  });
14590
- const OrgCreateStartedProps = Schema.Struct({
14591
- ...TelemetryEnvelopeProps,
14592
- org_id: OptionalOrgId,
14593
- email_domain: OptionalString,
14594
- template: OptionalString
14595
- });
14596
- const OrgSafeCreatedProps = Schema.Struct({
14597
- ...TelemetryEnvelopeProps,
14598
- org_id: OptionalOrgId,
14599
- chainId: OptionalChainId,
14600
- safe_address: OptionalAddress
14601
- });
14602
- const OrgSafeConfirmedProps = Schema.Struct({
14603
- ...TelemetryEnvelopeProps,
14604
- org_id: OptionalOrgId,
14605
- chainId: OptionalChainId,
14606
- safe_address: OptionalAddress,
14607
- durationMs: OptionalDurationMs
14608
- });
14609
- const OrgRolesSeededProps = Schema.Struct({
14610
- ...TelemetryEnvelopeProps,
14611
- org_id: OptionalOrgId,
14612
- chainId: OptionalChainId,
14613
- role: OptionalString,
14614
- txHash: OptionalTxHash
14615
- });
14616
- const OrgCreatedProps = Schema.Struct({
14617
- ...TelemetryEnvelopeProps,
14618
- org_id: OptionalOrgId,
14619
- chainId: OptionalChainId,
14620
- safe_address: OptionalAddress,
14621
- durationMs: OptionalDurationMs
14622
- });
14623
- const OrgCreateFailedProps = Schema.Struct({
14624
- ...TelemetryEnvelopeProps,
14625
- ...ChainCauseTelemetryProps,
14626
- org_id: OptionalOrgId,
14627
- reason: OptionalString
14628
- });
14629
14686
  const OrgInviteSentProps = Schema.Struct({
14630
14687
  ...TelemetryEnvelopeProps,
14631
14688
  org_id: OptionalOrgId,
@@ -14670,30 +14727,6 @@ Schema.Struct({
14670
14727
  name: Schema.Literal("organization_creation_failed"),
14671
14728
  props: OrganizationCreationFailedProps
14672
14729
  });
14673
- Schema.Struct({
14674
- name: Schema.Literal("org_create_started"),
14675
- props: OrgCreateStartedProps
14676
- });
14677
- Schema.Struct({
14678
- name: Schema.Literal("org_safe_created"),
14679
- props: OrgSafeCreatedProps
14680
- });
14681
- Schema.Struct({
14682
- name: Schema.Literal("org_safe_confirmed"),
14683
- props: OrgSafeConfirmedProps
14684
- });
14685
- Schema.Struct({
14686
- name: Schema.Literal("org_roles_seeded"),
14687
- props: OrgRolesSeededProps
14688
- });
14689
- Schema.Struct({
14690
- name: Schema.Literal("org_created"),
14691
- props: OrgCreatedProps
14692
- });
14693
- Schema.Struct({
14694
- name: Schema.Literal("org_create_failed"),
14695
- props: OrgCreateFailedProps
14696
- });
14697
14730
  Schema.Struct({
14698
14731
  name: Schema.Literal("org_invite_sent"),
14699
14732
  props: OrgInviteSentProps
@@ -15567,10 +15600,14 @@ const BootstrapFailedProps = Schema.Struct({
15567
15600
  });
15568
15601
  const MemberActivationStartedProps = Schema.Struct({
15569
15602
  ...TelemetryEnvelopeProps,
15603
+ entry_path: JourneyVariantProps.entry_path,
15604
+ surface: JourneyVariantProps.surface,
15570
15605
  $insert_id: Schema.String
15571
15606
  });
15572
15607
  const MemberActivationReadyProps = Schema.Struct({
15573
15608
  ...TelemetryEnvelopeProps,
15609
+ entry_path: JourneyVariantProps.entry_path,
15610
+ surface: JourneyVariantProps.surface,
15574
15611
  $insert_id: Schema.String,
15575
15612
  duration_ms: DurationMsSchema
15576
15613
  });
@@ -17116,7 +17153,7 @@ function mapAccountLifecycle(input) {
17116
17153
  }
17117
17154
  //#endregion
17118
17155
  //#region ../../packages/sdk/package.json
17119
- var version$2 = "4.20.0-beta.27";
17156
+ var version$2 = "4.20.0-beta.28";
17120
17157
  //#endregion
17121
17158
  //#region ../../packages/sdk/src/observability/telemetry/exception-projection.ts
17122
17159
  /** Fixed fallback for failures that have no safe message. */
@@ -20374,6 +20411,12 @@ function paymentsV2Signature(name) {
20374
20411
  const SEND_SIGNATURE = paymentsV2Signature("send");
20375
20412
  const SEND_BATCH_SIGNATURE = paymentsV2Signature("sendBatch");
20376
20413
  const CREATE_COMMITMENT_SIGNATURE = paymentsV2Signature("createCommitment");
20414
+ /**
20415
+ * #2299 ruling C: only an Owner cancels or redirects an Organization-funded
20416
+ * commitment, so these two sit in the Owner role's scope, never a Budget's.
20417
+ * The contract admits only the commitment's creator, the Organization Safe.
20418
+ */
20419
+ const OWNER_COMMITMENT_SIGNATURES = [paymentsV2Signature("cancel"), paymentsV2Signature("redirect")];
20377
20420
  //#endregion
20378
20421
  //#region ../../packages/protocol/src/evm/organization-payments.ts
20379
20422
  /** Zodiac ModuleProxyFactory on Base Sepolia. */
@@ -20610,24 +20653,18 @@ function encodePreparedPaymentCallData(command) {
20610
20653
  }));
20611
20654
  }
20612
20655
  const [item] = command.items;
20613
- if (command.executionRoute.kind !== "direct" || command.items.length !== 1 || item === void 0 || isBirth(item)) throw Errors.invalidInput("items", "Commitment lifecycle requires one direct execution");
20614
- return safe4337ExecuteUserOpCallData({
20656
+ const route = command.executionRoute;
20657
+ 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");
20658
+ const call = {
20615
20659
  to: command.contractAddress,
20660
+ value: 0n,
20616
20661
  data: lifecycleData(item)
20617
- });
20618
- }
20619
- //#endregion
20620
- //#region ../../packages/protocol/src/evm/role-dsl.ts
20621
- const ORG_BUDGET_ACTIONS = [
20622
- "send",
20623
- "sendBatch",
20624
- "createCommitment"
20625
- ];
20626
- /** Derive the Zodiac substrate key for one durable Permission identity. */
20627
- function roleKeyForPermission(permissionId) {
20628
- const normalized = permissionId.trim();
20629
- if (normalized.length === 0 || normalized !== permissionId) throw Errors.invalidInput("permissionId", "must be a non-empty canonical Permission ID");
20630
- return keccak256$1(toBytes$1(`permission ${permissionId}`));
20662
+ };
20663
+ return route.kind === "direct" ? safe4337ExecuteUserOpCallData(call) : buildOrgRoleSpendPersonalCallData(buildOrgRoleMultiCall({
20664
+ rolesModuleAddress: route.rolesModule,
20665
+ roleKey: route.roleKey,
20666
+ calls: [call]
20667
+ }));
20631
20668
  }
20632
20669
  //#endregion
20633
20670
  //#region ../../packages/sdk/src/payments/operation-verification.ts
@@ -20656,6 +20693,16 @@ function moneySafeOpDigest(operation, chainId) {
20656
20693
  userOp: safe4337UserOperation(operation)
20657
20694
  });
20658
20695
  }
20696
+ /**
20697
+ * An Organization-funded cancel or redirect runs through the Owner role
20698
+ * (#2299), which no Budget names. Its role key cannot widen what the signer
20699
+ * may do: the Roles module admits only a role the signer's Safe holds, and the
20700
+ * contract admits only the commitment's creator.
20701
+ */
20702
+ function isOwnerLifecycle(command) {
20703
+ const [item] = command.items;
20704
+ return command.items.length === 1 && (item?.operation === "cancel_commitment" || item?.operation === "redirect_commitment");
20705
+ }
20659
20706
  /** Verify every actual Money signing target against configured trust anchors. */
20660
20707
  function verifiesMoneyOperation(evidence, expectedSafe, chainId, permissionId) {
20661
20708
  try {
@@ -20666,7 +20713,7 @@ function verifiesMoneyOperation(evidence, expectedSafe, chainId, permissionId) {
20666
20713
  const route = command.executionRoute;
20667
20714
  if (route.kind === "direct") {
20668
20715
  if (permissionId !== void 0 || command.senderSafe.toLowerCase() !== expectedSafe.toLowerCase()) return false;
20669
- } else if (permissionId === void 0 || route.roleKey.toLowerCase() !== roleKeyForPermission(permissionId).toLowerCase() || command.senderSafe.toLowerCase() !== route.organizationSafe.toLowerCase()) return false;
20716
+ } else if ((permissionId === void 0 ? !isOwnerLifecycle(command) : route.roleKey.toLowerCase() !== roleKeyForPermission(permissionId).toLowerCase()) || command.senderSafe.toLowerCase() !== route.organizationSafe.toLowerCase()) return false;
20670
20717
  if (operation.callData.toLowerCase() !== encodePreparedPaymentCallData(command).toLowerCase()) return false;
20671
20718
  const digest = moneySafeOpDigest(operation, chainId);
20672
20719
  return evidence.digest.toLowerCase() === digest.toLowerCase() && evidence.executionId === `money_execution_${digest.slice(2).toLowerCase()}`;
@@ -21385,9 +21432,11 @@ function subscribeFreshQuery(deps, args, listener, operation, projection, option
21385
21432
  //#region ../../packages/sdk/src/payments/lifecycle-execution.ts
21386
21433
  /**
21387
21434
  * The prepared command is this lifecycle intent; a retry also names the
21388
- * original birth command of the scope it was asked for.
21435
+ * original birth command of the scope it was asked for. A claim, and an
21436
+ * Account's own cancel or redirect, runs directly from the signer's Safe; an
21437
+ * Organization's cancel or redirect runs through its Owner role (#2299).
21389
21438
  */
21390
- 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));
21439
+ 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"));
21391
21440
  /** The prepared command pays exactly the request the payer confirmed. */
21392
21441
  const requestPaymentCheck = (confirmation) => async (request) => "kind" in request && request.kind === "payment_request" && canonicalJson(request.confirmation) === canonicalJson(confirmation);
21393
21442
  function executePaymentLifecycle(deps, intent, signal, controls, expectedOrganizationId, onPrepared) {
@@ -22277,17 +22326,21 @@ function paymentExecutionIntent(input) {
22277
22326
  const to = normalizeTargetForBackend(input.to, "to");
22278
22327
  if (!to.ok) return to;
22279
22328
  try {
22280
- return {
22329
+ const decoded = Schema.decodeResult(PaymentExecutionIntentSchema, { onExcessProperty: "error" })({
22330
+ to: to.value,
22331
+ amount: input.amount,
22332
+ ...input.externalAddressAcknowledged === void 0 ? {} : { externalAddressAcknowledged: input.externalAddressAcknowledged },
22333
+ ...input.paymentType === void 0 ? {} : { paymentType: input.paymentType },
22334
+ ...input.document === void 0 ? {} : { document: input.document },
22335
+ ...input.supportingReceipts === void 0 ? {} : { supportingReceipts: input.supportingReceipts },
22336
+ ...input.timing === void 0 ? {} : { timing: input.timing }
22337
+ });
22338
+ return Result.isSuccess(decoded) ? {
22281
22339
  ok: true,
22282
- value: Schema.decodeSync(PaymentExecutionIntentSchema, { onExcessProperty: "error" })({
22283
- to: to.value,
22284
- amount: input.amount,
22285
- ...input.externalAddressAcknowledged === void 0 ? {} : { externalAddressAcknowledged: input.externalAddressAcknowledged },
22286
- ...input.paymentType === void 0 ? {} : { paymentType: input.paymentType },
22287
- ...input.document === void 0 ? {} : { document: input.document },
22288
- ...input.supportingReceipts === void 0 ? {} : { supportingReceipts: input.supportingReceipts },
22289
- ...input.timing === void 0 ? {} : { timing: input.timing }
22290
- })
22340
+ value: decoded.success
22341
+ } : {
22342
+ ok: false,
22343
+ error: invalidPaymentField(decoded.failure.issue)
22291
22344
  };
22292
22345
  } catch {
22293
22346
  return {
@@ -22296,6 +22349,13 @@ function paymentExecutionIntent(input) {
22296
22349
  };
22297
22350
  }
22298
22351
  }
22352
+ const formatIssue = SchemaIssue.makeFormatterStandardSchemaV1();
22353
+ /** The first failing field and why, e.g. `amount.value`, as the backend's row decode names it (#2381). */
22354
+ function invalidPaymentField(issue) {
22355
+ const first = formatIssue(issue).issues[0];
22356
+ const field = first?.path?.map((segment) => String(typeof segment === "object" ? segment.key : segment)).join(".");
22357
+ return Errors.invalidInput(field === void 0 || field.length === 0 ? "payment" : field, first?.message ?? "invalid personal payment input");
22358
+ }
22299
22359
  function normalizePaymentTiming(payment) {
22300
22360
  const { release: _release, ...rest } = payment;
22301
22361
  return {
@@ -23112,6 +23172,27 @@ function makeOrganizationPaymentsMethods(deps, orgId) {
23112
23172
  flights: /* @__PURE__ */ new Map()
23113
23173
  };
23114
23174
  const execute = (input, signal, operation = "organization-pay-batch", controls, onPrepared) => executeOrganizationBatch(scope, input, signal, operation, controls, onPrepared);
23175
+ /** One Owner-signed cancel or redirect, answering the one Payment it acted on (#2299). */
23176
+ const ownerLifecycle = (intent, controls, operation) => runCapxulEffect(Effect.gen(function* () {
23177
+ const captured = carryInvocationParentSpan(controls, yield* Effect.currentSpan.pipe(Effect.orDie));
23178
+ return yield* awaitCapxulResult(async () => {
23179
+ const execution = executionDependencies$1(deps);
23180
+ if (execution === null) return {
23181
+ ok: false,
23182
+ error: Errors.notImplemented("organizationPayments", "executionComposition")
23183
+ };
23184
+ const result = await executePaymentLifecycle(execution, intent, controls?.signal, captured, orgId);
23185
+ if (!result.ok) return result;
23186
+ const [payment, ...rest] = result.value.payments;
23187
+ return payment === void 0 || rest.length !== 0 ? {
23188
+ ok: false,
23189
+ error: Errors.unknown()
23190
+ } : {
23191
+ ok: true,
23192
+ value: normalizePaymentTiming(payment)
23193
+ };
23194
+ });
23195
+ }), observationOnlyControls(controls), operation, deps.runPromise);
23115
23196
  return {
23116
23197
  ...makeOrganizationPaymentReadMethods(deps, orgId),
23117
23198
  retry: (paymentId, options) => {
@@ -23170,6 +23251,19 @@ function makeOrganizationPaymentsMethods(deps, orgId) {
23170
23251
  });
23171
23252
  }), observationOnlyControls(controls), CAPXUL_OPERATIONS.organizationPayments.pay, deps.runPromise);
23172
23253
  },
23254
+ cancel: (paymentId, options) => ownerLifecycle({
23255
+ kind: "cancel",
23256
+ paymentId
23257
+ }, deps.invocationControls?.(options) ?? options, CAPXUL_OPERATIONS.organizationPayments.cancel),
23258
+ redirect: (input, options) => {
23259
+ const controls = deps.invocationControls?.(options) ?? options;
23260
+ const recipient = normalizeTargetForBackend(input.to, "to");
23261
+ return recipient.ok ? ownerLifecycle({
23262
+ kind: "redirect",
23263
+ paymentId: input.paymentId,
23264
+ recipient: recipient.value
23265
+ }, controls, CAPXUL_OPERATIONS.organizationPayments.redirect) : runCapxulEffect(Effect.fail(recipient.error), observationOnlyControls(controls), CAPXUL_OPERATIONS.organizationPayments.redirect, deps.runPromise);
23266
+ },
23173
23267
  payBatch: (input, options) => {
23174
23268
  const controls = deps.invocationControls?.(options) ?? options;
23175
23269
  return runCapxulEffect(Effect.gen(function* () {
@@ -37099,13 +37193,15 @@ function normalizeOrgBudgetPolicy(policy) {
37099
37193
  if (actionSet.has(action)) throw Errors.invalidInput("policy.actions", `duplicate Budget action: ${action}`);
37100
37194
  actionSet.add(action);
37101
37195
  }
37196
+ if (policy.refill === "monthly" && limit.kind === "unlimited") throw Errors.invalidInput("policy.refill", "only a capped Budget can refill");
37102
37197
  return {
37103
37198
  assetId,
37104
37199
  tokenAddress,
37105
37200
  decimals: policy.asset.decimals,
37106
37201
  limit,
37107
37202
  recipients,
37108
- actions: ORG_BUDGET_ACTIONS.filter((action) => actionSet.has(action))
37203
+ actions: ORG_BUDGET_ACTIONS.filter((action) => actionSet.has(action)),
37204
+ refillPeriod: policy.refill === "monthly" ? MONTHLY_REFILL_PERIOD_SECONDS : 0n
37109
37205
  };
37110
37206
  }
37111
37207
  function resolvedBudgetApprovalPermission(tokenAddress) {
@@ -37180,8 +37276,8 @@ function buildOrgBudgetCallsWithEncoder(input, encodeRoles) {
37180
37276
  allowanceKey,
37181
37277
  BigInt(policy.limit.rawUnits),
37182
37278
  BigInt(policy.limit.rawUnits),
37183
- 0n,
37184
- 0n,
37279
+ policy.refillPeriod === 0n ? 0n : BigInt(policy.limit.rawUnits),
37280
+ policy.refillPeriod,
37185
37281
  0n
37186
37282
  ]
37187
37283
  }),
@@ -37375,13 +37471,20 @@ function buildExplicitBootstrapRoleCalls(input, encodeRoles) {
37375
37471
  roles: [{
37376
37472
  key: managementRoleKey,
37377
37473
  members: [input.ownerAddress],
37378
- permissions: [{
37379
- targetAddress: input.rolesModuleAddress,
37380
- signature: "assignRoles(address,bytes32[],bool[])"
37381
- }, ...ROLE_MANAGEMENT_SIGNATURES.map((signature) => ({
37382
- targetAddress: input.rolesModuleAddress,
37383
- signature
37384
- }))]
37474
+ permissions: [
37475
+ {
37476
+ targetAddress: input.rolesModuleAddress,
37477
+ signature: "assignRoles(address,bytes32[],bool[])"
37478
+ },
37479
+ ...ROLE_MANAGEMENT_SIGNATURES.map((signature) => ({
37480
+ targetAddress: input.rolesModuleAddress,
37481
+ signature
37482
+ })),
37483
+ ...OWNER_COMMITMENT_SIGNATURES.map((signature) => ({
37484
+ targetAddress: CAPXUL_PAYMENTS_V2_ADDRESS,
37485
+ signature
37486
+ }))
37487
+ ]
37385
37488
  }]
37386
37489
  }).map((call) => ({
37387
37490
  to: toLowerHex(call.to, "setUpRoles.call.to"),
@@ -37846,7 +37949,7 @@ function matchesCurrentPolicy(intent, policy) {
37846
37949
  asset: policy.asset,
37847
37950
  value: policy.limit
37848
37951
  }, CONFIGURED_MONEY_ASSETS).minorUnits;
37849
- return policy.asset === intent.asset && raw === recorded && (intent.scope === void 0 || canonicalJson(intent.scope) === canonicalJson(policy.scope));
37952
+ return policy.asset === intent.asset && raw === recorded && intent.refill === "monthly" === (policy.refill === "monthly") && (intent.scope === void 0 || canonicalJson(intent.scope) === canonicalJson(policy.scope));
37850
37953
  }
37851
37954
  function previousPolicyForChange(effect, command) {
37852
37955
  if (effect.previousIntent.type === "budget" && (effect.previousIntent.scope === void 0 || effect.previousRecipientBindings === void 0)) throw new EvidenceMismatch("Prior complete Budget proof is unavailable");
@@ -37864,8 +37967,9 @@ function policyCalls(evidence, context, command) {
37864
37967
  const roleKey = effect.operation === "change" ? effect.roleKey : roleKeyForPermission(permissionId);
37865
37968
  const revision = effect.operation === "change" ? effect.revision : 0;
37866
37969
  if (effect.operation !== command.operation || effect.operation === "change" && effect.permissionId !== permissionId || evidence.expectedRevision !== revision) throw new EvidenceMismatch("Permission policy binding mismatch");
37970
+ const intent = effect.operation === "change" ? keepRefillOnChange(command.intent, effect.previousCurrentPolicy) : command.intent;
37867
37971
  const value = evidence.value;
37868
- if (Array.isArray(value) || !("type" in value) || !matchesCurrentPolicy(command.intent, value.currentPolicy) || !permissionMatches(value, context.orgId, permissionId, command.operation === "create" ? 0 : revision + 1, command.intent, roleKey)) throw new EvidenceMismatch("Permission value mismatch");
37972
+ if (Array.isArray(value) || !("type" in value) || !matchesCurrentPolicy(intent, value.currentPolicy) || !permissionMatches(value, context.orgId, permissionId, command.operation === "create" ? 0 : revision + 1, intent, roleKey)) throw new EvidenceMismatch("Permission value mismatch");
37869
37973
  const planned = compilePermissionPolicyCallsWithEncoder({
37870
37974
  permissionId,
37871
37975
  roleKey,
@@ -37873,7 +37977,7 @@ function policyCalls(evidence, context, command) {
37873
37977
  actorSafe: evidence.userOpSenderSafe,
37874
37978
  rolesModule: context.rolesModule,
37875
37979
  managerRoleKey: context.managerRoleKey,
37876
- nextIntent: encodingIntent(command.intent, effect.recipientBindings),
37980
+ nextIntent: encodingIntent(intent, effect.recipientBindings),
37877
37981
  ...previous === void 0 ? {} : { previousIntent: previous },
37878
37982
  ...effect.operation !== "change" || effect.previousAllowanceKey === null ? {} : { previousAllowanceKey: effect.previousAllowanceKey }
37879
37983
  }, encodeRoles);
@@ -37925,7 +38029,8 @@ function encodingIntent(intent, bindings) {
37925
38029
  "send",
37926
38030
  "sendBatch",
37927
38031
  "createCommitment"
37928
- ].filter((action) => action === "createCommitment" ? intent.scope.actions.includes("commitments") : intent.scope.actions.includes("pay"))
38032
+ ].filter((action) => action === "createCommitment" ? intent.scope.actions.includes("commitments") : intent.scope.actions.includes("pay")),
38033
+ ...intent.refill === "monthly" ? { refill: intent.refill } : {}
37929
38034
  }
37930
38035
  };
37931
38036
  }
@@ -62352,6 +62457,22 @@ function normalizeRuntimeUrl(field, raw) {
62352
62457
  }
62353
62458
  //#endregion
62354
62459
  //#region ../../packages/sdk/src/applications/bootstrap-adapter.ts
62460
+ /** A cached envelope this close to its expiry is fetched again, so it cannot lapse mid-command. */
62461
+ const CACHE_EXPIRY_MARGIN_MS = 3e4;
62462
+ /**
62463
+ * The cached envelope, when it is still inside its own lifetime. It is decoded
62464
+ * as a fresh body is; anything that does not decode, or a store that fails,
62465
+ * is a miss.
62466
+ */
62467
+ async function cachedEnvelope(cache, now) {
62468
+ try {
62469
+ const cached = decodeBootstrapBody(await cache.load());
62470
+ const expiresAt = cached.issuedAt + cached.expiresIn - CACHE_EXPIRY_MARGIN_MS;
62471
+ return cached.issuedAt <= now && now < expiresAt ? cached : null;
62472
+ } catch {
62473
+ return null;
62474
+ }
62475
+ }
62355
62476
  const DEFAULT_RETRY = {
62356
62477
  attempts: 2,
62357
62478
  baseDelayMs: 400
@@ -62392,19 +62513,24 @@ var HttpBootstrapAdapter = class {
62392
62513
  fetchImpl;
62393
62514
  observation;
62394
62515
  retry;
62516
+ cache;
62395
62517
  constructor(deps) {
62396
62518
  this.bootstrapBaseUrl = deps.bootstrapBaseUrl.replace(/\/$/, "");
62397
62519
  this.bootstrapHost = hostOf(this.bootstrapBaseUrl);
62398
62520
  this.observation = deps.observation;
62399
62521
  this.retry = deps.retry ?? DEFAULT_RETRY;
62522
+ this.cache = deps.cache;
62400
62523
  this.fetchImpl = deps.fetch ?? ((input, init) => globalThis.fetch(input, init));
62401
62524
  }
62402
62525
  resolve(input) {
62403
- return this.attempt(input).pipe(Effect.retry({
62526
+ const fetched = this.attempt(input).pipe(Effect.retry({
62404
62527
  times: this.retry.attempts,
62405
62528
  while: isTransientBootstrapFailure,
62406
62529
  schedule: Schedule.exponential(Duration.millis(this.retry.baseDelayMs))
62407
62530
  }));
62531
+ const cache = this.cache;
62532
+ if (cache === void 0) return fetched;
62533
+ return Effect.promise(() => cachedEnvelope(cache, Date.now())).pipe(Effect.filterOrElse((cached) => cached !== null, () => fetched));
62408
62534
  }
62409
62535
  attempt(input) {
62410
62536
  return Effect.tryPromise({
@@ -62429,7 +62555,13 @@ var HttpBootstrapAdapter = class {
62429
62555
  }
62430
62556
  mapResponse(res, input) {
62431
62557
  if (res.ok) return Effect.tryPromise({
62432
- try: async () => decodeBootstrapBody(await res.json()),
62558
+ try: async () => {
62559
+ const body = await res.json();
62560
+ const resolution = decodeBootstrapBody(body);
62561
+ const cache = this.cache;
62562
+ if (cache !== void 0) await Promise.resolve().then(() => cache.save(body)).catch(() => void 0);
62563
+ return resolution;
62564
+ },
62433
62565
  catch: (cause) => {
62434
62566
  if (cause instanceof CapxulError && cause.code === "INVALID_INPUT") return bootstrapErrorFromCapxul("invalidInput", cause);
62435
62567
  return bootstrapErrorFromCapxul("malformedBody", Errors.providerError("convex", "bootstrap", cause instanceof Error ? cause : new Error(String(cause))));
@@ -64096,7 +64228,9 @@ function makeNotImplementedOrganizationPaymentsMethods() {
64096
64228
  subscribe: organizationPaymentExecutionUnavailable,
64097
64229
  pay: organizationPaymentExecutionUnavailable,
64098
64230
  payBatch: organizationPaymentExecutionUnavailable,
64099
- retry: organizationPaymentExecutionUnavailable
64231
+ retry: organizationPaymentExecutionUnavailable,
64232
+ cancel: organizationPaymentExecutionUnavailable,
64233
+ redirect: organizationPaymentExecutionUnavailable
64100
64234
  };
64101
64235
  }
64102
64236
  const orgMeUnavailable = () => Promise.resolve({
@@ -68289,6 +68423,23 @@ function subscribeConvexQuery(client, fn, args, callback, options) {
68289
68423
  * ever needs a different one.
68290
68424
  */
68291
68425
  const DEFAULT_CLOSE_TIMEOUT_MS = 5e3;
68426
+ /**
68427
+ * The fetcher the Convex client's auth handshake gets. Convex first asks for
68428
+ * "a possibly cached token" (`forceRefreshToken: false`) and, as soon as the
68429
+ * server accepts it, for a fresh one (`true`): the token whose `exp` it
68430
+ * schedules the next refresh from. The Better Auth clients keep no token: each
68431
+ * ask mints one over the network, so the cached ask was a second token fetch
68432
+ * and a second server verification (a JWKS read) on every session restore. It
68433
+ * answers "none" instead, and Convex makes its one fetch forced, as a fresh
68434
+ * token. Every forced ask still reaches the auth client: that first fetch, the
68435
+ * refresh before `exp`, and the retry after the server rejects a token.
68436
+ * ponytail: the OAuth bearer client can hold a cached exchange; it now pays one
68437
+ * exchange for the first authentication instead of reusing it (never more
68438
+ * fetches than before). Pass the cache through if that start ever matters.
68439
+ */
68440
+ function freshTokenHandshake(fetchToken) {
68441
+ return async (args) => args.forceRefreshToken ? fetchToken(args) : null;
68442
+ }
68292
68443
  /** Run a Convex call under the current trace parent, when there is one. */
68293
68444
  const withTraceparent = (run) => Effect.flatMap(Effect.serviceOption(Tracer.ParentSpan), (parent) => run(parent._tag === "Some" ? formatTraceparent(parent.value) : void 0));
68294
68445
  var ConvexCallAdapter = class {
@@ -68313,10 +68464,10 @@ var ConvexCallAdapter = class {
68313
68464
  this.#tokenProvider = deps.tokenProvider;
68314
68465
  this.#applicationId = deps.applicationId;
68315
68466
  this.#observation = deps.observation;
68316
- if (this.#tokenProvider) this.#client.setAuth(this.#tokenProvider);
68467
+ this.refreshAuth();
68317
68468
  }
68318
68469
  refreshAuth() {
68319
- if (this.#tokenProvider) this.#client.setAuth(this.#tokenProvider);
68470
+ if (this.#tokenProvider) this.#client.setAuth(freshTokenHandshake(this.#tokenProvider));
68320
68471
  }
68321
68472
  observeConnection(observer) {
68322
68473
  return this.#connectionMonitor.observe(this.#client, observer);
@@ -69754,7 +69905,8 @@ async function createProductionAdapters(input) {
69754
69905
  const bootstrapLayer = HttpBootstrapLayer({
69755
69906
  bootstrapBaseUrl: resolvedInput.value.bootstrapBaseUrl,
69756
69907
  ...input.fetch === void 0 ? {} : { fetch: input.fetch },
69757
- ...invocationObservation === void 0 ? {} : { observation: invocationObservation }
69908
+ ...invocationObservation === void 0 ? {} : { observation: invocationObservation },
69909
+ ...input.bootstrapCache === void 0 ? {} : { cache: input.bootstrapCache }
69758
69910
  });
69759
69911
  try {
69760
69912
  const bootstrapContext = await Effect.runPromise(Layer.buildWithScope(bootstrapLayer, scope));
@@ -71199,7 +71351,7 @@ function stdioIdentity(env = process.env, options = {}) {
71199
71351
  }
71200
71352
  //#endregion
71201
71353
  //#region package.json
71202
- var version = "4.20.0-beta.27";
71354
+ var version = "4.20.0-beta.28";
71203
71355
  //#endregion
71204
71356
  //#region src/deps.ts
71205
71357
  const NO_IDENTITY = async () => void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capxul/mcp",
3
- "version": "4.20.0-beta.27",
3
+ "version": "4.20.0-beta.28",
4
4
  "private": false,
5
5
  "description": "Capxul MCP server for auth, Account readiness, identity, Organizations, relationships, requests, destinations, and testnet funding over Streamable HTTP.",
6
6
  "keywords": [
@@ -54,8 +54,8 @@
54
54
  "typescript": "npm:@typescript/typescript6@6.0.2",
55
55
  "vite": "npm:@voidzero-dev/vite-plus-core@1.0.0",
56
56
  "vite-plus": "1.0.0",
57
- "@capxul/protocol": "0.2.0-beta.1",
58
- "@capxul/sdk": "4.20.0-beta.27",
57
+ "@capxul/protocol": "0.2.0-beta.2",
58
+ "@capxul/sdk": "4.20.0-beta.28",
59
59
  "@capxul/typescript-config": "0.0.0"
60
60
  },
61
61
  "scripts": {