@elisym/sdk 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -624,6 +624,48 @@ type ParseResult = {
624
624
  */
625
625
  declare function parsePaymentRequest(input: string, options?: ParseOptions): ParseResult;
626
626
 
627
+ /**
628
+ * Multi-asset / multi-chain payment model.
629
+ *
630
+ * `Asset` describes a currency a customer can spend: native coins (SOL, ETH, BTC)
631
+ * or tokens (SPL, ERC-20). `assetKey` produces a stable string id for Map lookups.
632
+ *
633
+ * Today only `NATIVE_SOL` is in `KNOWN_ASSETS`. SPL (USDC) and other chains are
634
+ * extended by adding entries to `KNOWN_ASSETS` and, where relevant, to the
635
+ * MCP `DEFAULT_SESSION_LIMITS` catalogue.
636
+ */
637
+ type Chain = 'solana';
638
+ interface Asset {
639
+ chain: Chain;
640
+ /** Lowercase token id: 'sol', 'usdc', 'btc', 'eth'. */
641
+ token: string;
642
+ /** SPL mint / ERC-20 contract. Undefined for a native coin. */
643
+ mint?: string;
644
+ /** Subunits per whole (9 SOL, 6 USDC, 8 BTC, 18 ETH). */
645
+ decimals: number;
646
+ /** Display symbol: 'SOL', 'USDC'. */
647
+ symbol: string;
648
+ }
649
+ declare const NATIVE_SOL: Asset;
650
+ declare const KNOWN_ASSETS: readonly Asset[];
651
+ /** Stable Map key for `Asset`. Same shape regardless of Asset identity. */
652
+ declare function assetKey(a: Pick<Asset, 'chain' | 'token' | 'mint'>): string;
653
+ /** Find a known asset by (chain, token, mint). Returns undefined if unknown. */
654
+ declare function resolveKnownAsset(chain: string, token: string, mint?: string): Asset | undefined;
655
+ /** Reverse lookup: given an assetKey string, return the known asset or undefined. */
656
+ declare function assetByKey(key: string): Asset | undefined;
657
+ /**
658
+ * Parse a human amount string ("0.5", "1", "0.000001") into raw subunits (BigInt).
659
+ * Uses integer math to avoid float precision issues.
660
+ *
661
+ * Throws on: empty, negative, zero, malformed, too many fractional digits, or
662
+ * a value exceeding `Number.MAX_SAFE_INTEGER` (to keep downstream `Number(...)`
663
+ * call-sites safe).
664
+ */
665
+ declare function parseAssetAmount(asset: Asset, human: string): bigint;
666
+ /** Format raw subunits back to `"<whole>.<frac> <SYMBOL>"`. Keeps all `decimals` digits. */
667
+ declare function formatAssetAmount(asset: Asset, raw: bigint): string;
668
+
627
669
  /**
628
670
  * Snapshot of the on-chain elisym-config program state.
629
671
  *
@@ -658,6 +700,73 @@ interface GetProtocolConfigOptions {
658
700
  */
659
701
  declare function getProtocolConfig(rpc: Rpc<SolanaRpcApi>, programId: Address, options?: GetProtocolConfigOptions): Promise<ProtocolConfig>;
660
702
 
703
+ /**
704
+ * Global (not per-agent) config stored at `~/.elisym/config.yaml`.
705
+ *
706
+ * Currently holds only session-spend-limit overrides; other top-level fields
707
+ * may be added later. The loader tolerates a missing file (returns `{}`), but
708
+ * fails fast on malformed YAML or schema violations.
709
+ */
710
+
711
+ declare const SessionSpendLimitEntrySchema: z.ZodObject<{
712
+ chain: z.ZodEnum<["solana"]>;
713
+ token: z.ZodString;
714
+ mint: z.ZodOptional<z.ZodString>;
715
+ amount: z.ZodNumber;
716
+ }, "strict", z.ZodTypeAny, {
717
+ amount: number;
718
+ chain: "solana";
719
+ token: string;
720
+ mint?: string | undefined;
721
+ }, {
722
+ amount: number;
723
+ chain: "solana";
724
+ token: string;
725
+ mint?: string | undefined;
726
+ }>;
727
+ declare const GlobalConfigSchema: z.ZodObject<{
728
+ session_spend_limits: z.ZodOptional<z.ZodArray<z.ZodObject<{
729
+ chain: z.ZodEnum<["solana"]>;
730
+ token: z.ZodString;
731
+ mint: z.ZodOptional<z.ZodString>;
732
+ amount: z.ZodNumber;
733
+ }, "strict", z.ZodTypeAny, {
734
+ amount: number;
735
+ chain: "solana";
736
+ token: string;
737
+ mint?: string | undefined;
738
+ }, {
739
+ amount: number;
740
+ chain: "solana";
741
+ token: string;
742
+ mint?: string | undefined;
743
+ }>, "many">>;
744
+ }, "strict", z.ZodTypeAny, {
745
+ session_spend_limits?: {
746
+ amount: number;
747
+ chain: "solana";
748
+ token: string;
749
+ mint?: string | undefined;
750
+ }[] | undefined;
751
+ }, {
752
+ session_spend_limits?: {
753
+ amount: number;
754
+ chain: "solana";
755
+ token: string;
756
+ mint?: string | undefined;
757
+ }[] | undefined;
758
+ }>;
759
+ type SessionSpendLimitEntry = z.infer<typeof SessionSpendLimitEntrySchema>;
760
+ type GlobalConfig = z.infer<typeof GlobalConfigSchema>;
761
+ /**
762
+ * Read and validate `~/.elisym/config.yaml`. Returns `{}` if missing. Throws
763
+ * on malformed YAML or schema violations — the MCP server treats these as fatal
764
+ * at startup rather than silently ignoring bad overrides.
765
+ */
766
+ declare function loadGlobalConfig(path: string): Promise<GlobalConfig>;
767
+ /** Write the config YAML atomically. Validates via Zod before writing. */
768
+ declare function writeGlobalConfig(path: string, config: GlobalConfig): Promise<void>;
769
+
661
770
  /** Encrypt plaintext using NIP-44 v2 (sender secret key + recipient public key). */
662
771
  declare function nip44Encrypt(plaintext: string, senderSk: Uint8Array, recipientPubkey: string): string;
663
772
  /** Decrypt ciphertext using NIP-44 v2 (receiver secret key + sender public key). */
@@ -836,4 +945,4 @@ declare const LIMITS: {
836
945
  readonly MAX_CAPABILITY_LENGTH: 64;
837
946
  };
838
947
 
839
- export { type Agent, BoundedSet, type BuildTransactionOptions, type CapabilityCard, DEFAULTS, DEFAULT_KIND_OFFSET, DEFAULT_REDACT_PATHS, DiscoveryService, ElisymClient, type ElisymClientConfig, type ElisymClientFullConfig, ElisymIdentity, type EstimatePriorityFeeOptions, type GetProtocolConfigOptions, INPUT_REDACT_PATHS, type Job, type JobStatus, type JobSubscriptionOptions, type JobUpdateCallbacks, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, type Network, type NetworkStats, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, type ParseOptions, type ParseResult, type ParsedPaymentRequest, type PaymentInfo, type PaymentRequestData, PaymentRequestSchema, type PaymentStrategy, type PaymentValidationCode, type PaymentValidationError, type PingResult, PingService, type ProtocolCluster, type ProtocolConfig, type ProtocolConfigInput, RELAYS, type RateLimitDecision, SECRET_REDACT_PATHS, type Signer, type SlidingWindowLimiter, type SlidingWindowLimiterOptions, SolanaPaymentStrategy, type SubCloser, type SubmitJobOptions, type VerifyOptions, type VerifyResult, assertExpiry, assertLamports, buildPaymentInstructions, calculateProtocolFee, clearPriorityFeeCache, clearProtocolConfigCache, createPaymentRequestWithOnchainConfig, createSlidingWindowLimiter, estimatePriorityFeeMicroLamports, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, makeCensor, nip44Decrypt, nip44Encrypt, parsePaymentRequest, pickPercentileFee, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry };
948
+ export { type Agent, type Asset, BoundedSet, type BuildTransactionOptions, type CapabilityCard, type Chain, DEFAULTS, DEFAULT_KIND_OFFSET, DEFAULT_REDACT_PATHS, DiscoveryService, ElisymClient, type ElisymClientConfig, type ElisymClientFullConfig, ElisymIdentity, type EstimatePriorityFeeOptions, type GetProtocolConfigOptions, type GlobalConfig, GlobalConfigSchema, INPUT_REDACT_PATHS, type Job, type JobStatus, type JobSubscriptionOptions, type JobUpdateCallbacks, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, KNOWN_ASSETS, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, NATIVE_SOL, type Network, type NetworkStats, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, type ParseOptions, type ParseResult, type ParsedPaymentRequest, type PaymentInfo, type PaymentRequestData, PaymentRequestSchema, type PaymentStrategy, type PaymentValidationCode, type PaymentValidationError, type PingResult, PingService, type ProtocolCluster, type ProtocolConfig, type ProtocolConfigInput, RELAYS, type RateLimitDecision, SECRET_REDACT_PATHS, type SessionSpendLimitEntry, SessionSpendLimitEntrySchema, type Signer, type SlidingWindowLimiter, type SlidingWindowLimiterOptions, SolanaPaymentStrategy, type SubCloser, type SubmitJobOptions, type VerifyOptions, type VerifyResult, assertExpiry, assertLamports, assetByKey, assetKey, buildPaymentInstructions, calculateProtocolFee, clearPriorityFeeCache, clearProtocolConfigCache, createPaymentRequestWithOnchainConfig, createSlidingWindowLimiter, estimatePriorityFeeMicroLamports, formatAssetAmount, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, loadGlobalConfig, makeCensor, nip44Decrypt, nip44Encrypt, parseAssetAmount, parsePaymentRequest, pickPercentileFee, resolveKnownAsset, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry, writeGlobalConfig };
package/dist/index.d.ts CHANGED
@@ -624,6 +624,48 @@ type ParseResult = {
624
624
  */
625
625
  declare function parsePaymentRequest(input: string, options?: ParseOptions): ParseResult;
626
626
 
627
+ /**
628
+ * Multi-asset / multi-chain payment model.
629
+ *
630
+ * `Asset` describes a currency a customer can spend: native coins (SOL, ETH, BTC)
631
+ * or tokens (SPL, ERC-20). `assetKey` produces a stable string id for Map lookups.
632
+ *
633
+ * Today only `NATIVE_SOL` is in `KNOWN_ASSETS`. SPL (USDC) and other chains are
634
+ * extended by adding entries to `KNOWN_ASSETS` and, where relevant, to the
635
+ * MCP `DEFAULT_SESSION_LIMITS` catalogue.
636
+ */
637
+ type Chain = 'solana';
638
+ interface Asset {
639
+ chain: Chain;
640
+ /** Lowercase token id: 'sol', 'usdc', 'btc', 'eth'. */
641
+ token: string;
642
+ /** SPL mint / ERC-20 contract. Undefined for a native coin. */
643
+ mint?: string;
644
+ /** Subunits per whole (9 SOL, 6 USDC, 8 BTC, 18 ETH). */
645
+ decimals: number;
646
+ /** Display symbol: 'SOL', 'USDC'. */
647
+ symbol: string;
648
+ }
649
+ declare const NATIVE_SOL: Asset;
650
+ declare const KNOWN_ASSETS: readonly Asset[];
651
+ /** Stable Map key for `Asset`. Same shape regardless of Asset identity. */
652
+ declare function assetKey(a: Pick<Asset, 'chain' | 'token' | 'mint'>): string;
653
+ /** Find a known asset by (chain, token, mint). Returns undefined if unknown. */
654
+ declare function resolveKnownAsset(chain: string, token: string, mint?: string): Asset | undefined;
655
+ /** Reverse lookup: given an assetKey string, return the known asset or undefined. */
656
+ declare function assetByKey(key: string): Asset | undefined;
657
+ /**
658
+ * Parse a human amount string ("0.5", "1", "0.000001") into raw subunits (BigInt).
659
+ * Uses integer math to avoid float precision issues.
660
+ *
661
+ * Throws on: empty, negative, zero, malformed, too many fractional digits, or
662
+ * a value exceeding `Number.MAX_SAFE_INTEGER` (to keep downstream `Number(...)`
663
+ * call-sites safe).
664
+ */
665
+ declare function parseAssetAmount(asset: Asset, human: string): bigint;
666
+ /** Format raw subunits back to `"<whole>.<frac> <SYMBOL>"`. Keeps all `decimals` digits. */
667
+ declare function formatAssetAmount(asset: Asset, raw: bigint): string;
668
+
627
669
  /**
628
670
  * Snapshot of the on-chain elisym-config program state.
629
671
  *
@@ -658,6 +700,73 @@ interface GetProtocolConfigOptions {
658
700
  */
659
701
  declare function getProtocolConfig(rpc: Rpc<SolanaRpcApi>, programId: Address, options?: GetProtocolConfigOptions): Promise<ProtocolConfig>;
660
702
 
703
+ /**
704
+ * Global (not per-agent) config stored at `~/.elisym/config.yaml`.
705
+ *
706
+ * Currently holds only session-spend-limit overrides; other top-level fields
707
+ * may be added later. The loader tolerates a missing file (returns `{}`), but
708
+ * fails fast on malformed YAML or schema violations.
709
+ */
710
+
711
+ declare const SessionSpendLimitEntrySchema: z.ZodObject<{
712
+ chain: z.ZodEnum<["solana"]>;
713
+ token: z.ZodString;
714
+ mint: z.ZodOptional<z.ZodString>;
715
+ amount: z.ZodNumber;
716
+ }, "strict", z.ZodTypeAny, {
717
+ amount: number;
718
+ chain: "solana";
719
+ token: string;
720
+ mint?: string | undefined;
721
+ }, {
722
+ amount: number;
723
+ chain: "solana";
724
+ token: string;
725
+ mint?: string | undefined;
726
+ }>;
727
+ declare const GlobalConfigSchema: z.ZodObject<{
728
+ session_spend_limits: z.ZodOptional<z.ZodArray<z.ZodObject<{
729
+ chain: z.ZodEnum<["solana"]>;
730
+ token: z.ZodString;
731
+ mint: z.ZodOptional<z.ZodString>;
732
+ amount: z.ZodNumber;
733
+ }, "strict", z.ZodTypeAny, {
734
+ amount: number;
735
+ chain: "solana";
736
+ token: string;
737
+ mint?: string | undefined;
738
+ }, {
739
+ amount: number;
740
+ chain: "solana";
741
+ token: string;
742
+ mint?: string | undefined;
743
+ }>, "many">>;
744
+ }, "strict", z.ZodTypeAny, {
745
+ session_spend_limits?: {
746
+ amount: number;
747
+ chain: "solana";
748
+ token: string;
749
+ mint?: string | undefined;
750
+ }[] | undefined;
751
+ }, {
752
+ session_spend_limits?: {
753
+ amount: number;
754
+ chain: "solana";
755
+ token: string;
756
+ mint?: string | undefined;
757
+ }[] | undefined;
758
+ }>;
759
+ type SessionSpendLimitEntry = z.infer<typeof SessionSpendLimitEntrySchema>;
760
+ type GlobalConfig = z.infer<typeof GlobalConfigSchema>;
761
+ /**
762
+ * Read and validate `~/.elisym/config.yaml`. Returns `{}` if missing. Throws
763
+ * on malformed YAML or schema violations — the MCP server treats these as fatal
764
+ * at startup rather than silently ignoring bad overrides.
765
+ */
766
+ declare function loadGlobalConfig(path: string): Promise<GlobalConfig>;
767
+ /** Write the config YAML atomically. Validates via Zod before writing. */
768
+ declare function writeGlobalConfig(path: string, config: GlobalConfig): Promise<void>;
769
+
661
770
  /** Encrypt plaintext using NIP-44 v2 (sender secret key + recipient public key). */
662
771
  declare function nip44Encrypt(plaintext: string, senderSk: Uint8Array, recipientPubkey: string): string;
663
772
  /** Decrypt ciphertext using NIP-44 v2 (receiver secret key + sender public key). */
@@ -836,4 +945,4 @@ declare const LIMITS: {
836
945
  readonly MAX_CAPABILITY_LENGTH: 64;
837
946
  };
838
947
 
839
- export { type Agent, BoundedSet, type BuildTransactionOptions, type CapabilityCard, DEFAULTS, DEFAULT_KIND_OFFSET, DEFAULT_REDACT_PATHS, DiscoveryService, ElisymClient, type ElisymClientConfig, type ElisymClientFullConfig, ElisymIdentity, type EstimatePriorityFeeOptions, type GetProtocolConfigOptions, INPUT_REDACT_PATHS, type Job, type JobStatus, type JobSubscriptionOptions, type JobUpdateCallbacks, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, type Network, type NetworkStats, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, type ParseOptions, type ParseResult, type ParsedPaymentRequest, type PaymentInfo, type PaymentRequestData, PaymentRequestSchema, type PaymentStrategy, type PaymentValidationCode, type PaymentValidationError, type PingResult, PingService, type ProtocolCluster, type ProtocolConfig, type ProtocolConfigInput, RELAYS, type RateLimitDecision, SECRET_REDACT_PATHS, type Signer, type SlidingWindowLimiter, type SlidingWindowLimiterOptions, SolanaPaymentStrategy, type SubCloser, type SubmitJobOptions, type VerifyOptions, type VerifyResult, assertExpiry, assertLamports, buildPaymentInstructions, calculateProtocolFee, clearPriorityFeeCache, clearProtocolConfigCache, createPaymentRequestWithOnchainConfig, createSlidingWindowLimiter, estimatePriorityFeeMicroLamports, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, makeCensor, nip44Decrypt, nip44Encrypt, parsePaymentRequest, pickPercentileFee, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry };
948
+ export { type Agent, type Asset, BoundedSet, type BuildTransactionOptions, type CapabilityCard, type Chain, DEFAULTS, DEFAULT_KIND_OFFSET, DEFAULT_REDACT_PATHS, DiscoveryService, ElisymClient, type ElisymClientConfig, type ElisymClientFullConfig, ElisymIdentity, type EstimatePriorityFeeOptions, type GetProtocolConfigOptions, type GlobalConfig, GlobalConfigSchema, INPUT_REDACT_PATHS, type Job, type JobStatus, type JobSubscriptionOptions, type JobUpdateCallbacks, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, KNOWN_ASSETS, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, NATIVE_SOL, type Network, type NetworkStats, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, type ParseOptions, type ParseResult, type ParsedPaymentRequest, type PaymentInfo, type PaymentRequestData, PaymentRequestSchema, type PaymentStrategy, type PaymentValidationCode, type PaymentValidationError, type PingResult, PingService, type ProtocolCluster, type ProtocolConfig, type ProtocolConfigInput, RELAYS, type RateLimitDecision, SECRET_REDACT_PATHS, type SessionSpendLimitEntry, SessionSpendLimitEntrySchema, type Signer, type SlidingWindowLimiter, type SlidingWindowLimiterOptions, SolanaPaymentStrategy, type SubCloser, type SubmitJobOptions, type VerifyOptions, type VerifyResult, assertExpiry, assertLamports, assetByKey, assetKey, buildPaymentInstructions, calculateProtocolFee, clearPriorityFeeCache, clearProtocolConfigCache, createPaymentRequestWithOnchainConfig, createSlidingWindowLimiter, estimatePriorityFeeMicroLamports, formatAssetAmount, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, loadGlobalConfig, makeCensor, nip44Decrypt, nip44Encrypt, parseAssetAmount, parsePaymentRequest, pickPercentileFee, resolveKnownAsset, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry, writeGlobalConfig };
package/dist/index.js CHANGED
@@ -4,6 +4,10 @@ import Decimal2 from 'decimal.js-light';
4
4
  import { z } from 'zod';
5
5
  import { verifyEvent, finalizeEvent, getPublicKey, nip19, generateSecretKey, SimplePool } from 'nostr-tools';
6
6
  import * as nip44 from 'nostr-tools/nip44';
7
+ import { readFile, mkdir, writeFile, rename } from 'node:fs/promises';
8
+ import YAML2 from 'yaml';
9
+ import { randomBytes } from 'node:crypto';
10
+ import { dirname } from 'node:path';
7
11
 
8
12
  // src/constants.ts
9
13
  var RELAYS = [
@@ -2331,6 +2335,129 @@ var ElisymClient = class {
2331
2335
  this.pool.close();
2332
2336
  }
2333
2337
  };
2338
+
2339
+ // src/payment/assets.ts
2340
+ var NATIVE_SOL = {
2341
+ chain: "solana",
2342
+ token: "sol",
2343
+ decimals: 9,
2344
+ symbol: "SOL"
2345
+ };
2346
+ var KNOWN_ASSETS = [NATIVE_SOL];
2347
+ function assetKey(a) {
2348
+ return a.mint ? `${a.chain}:${a.token}:${a.mint}` : `${a.chain}:${a.token}`;
2349
+ }
2350
+ function resolveKnownAsset(chain, token, mint) {
2351
+ const key = mint ? `${chain}:${token}:${mint}` : `${chain}:${token}`;
2352
+ return KNOWN_ASSETS.find((asset) => assetKey(asset) === key);
2353
+ }
2354
+ function assetByKey(key) {
2355
+ return KNOWN_ASSETS.find((asset) => assetKey(asset) === key);
2356
+ }
2357
+ var DECIMAL_RE = /^(\d+\.\d*|\d*\.\d+|\d+)$/;
2358
+ function parseAssetAmount(asset, human) {
2359
+ const trimmed = human.trim();
2360
+ if (!trimmed) {
2361
+ throw new Error(`${asset.symbol} amount is empty`);
2362
+ }
2363
+ if (trimmed.startsWith("-")) {
2364
+ throw new Error(`${asset.symbol} amount cannot be negative`);
2365
+ }
2366
+ if (!DECIMAL_RE.test(trimmed)) {
2367
+ throw new Error(
2368
+ `${asset.symbol} amount must be a non-negative decimal (e.g. "0.5", "1"); got "${human}"`
2369
+ );
2370
+ }
2371
+ const dotPos = trimmed.indexOf(".");
2372
+ let wholePart;
2373
+ if (dotPos === -1) {
2374
+ wholePart = trimmed;
2375
+ } else if (dotPos === 0) {
2376
+ wholePart = "0";
2377
+ } else {
2378
+ wholePart = trimmed.slice(0, dotPos);
2379
+ }
2380
+ const fracPart = dotPos === -1 ? "" : trimmed.slice(dotPos + 1);
2381
+ if (fracPart.length > asset.decimals) {
2382
+ throw new Error(
2383
+ `${asset.symbol} amount has too many decimals (max ${asset.decimals}); got "${human}"`
2384
+ );
2385
+ }
2386
+ const unit = 10n ** BigInt(asset.decimals);
2387
+ const whole = BigInt(wholePart);
2388
+ const frac = fracPart ? BigInt(fracPart.padEnd(asset.decimals, "0")) : 0n;
2389
+ const raw = whole * unit + frac;
2390
+ if (raw === 0n) {
2391
+ throw new Error(`${asset.symbol} amount must be positive; got "${human}"`);
2392
+ }
2393
+ if (raw > BigInt(Number.MAX_SAFE_INTEGER)) {
2394
+ throw new Error(
2395
+ `${asset.symbol} amount exceeds safe range (max ${Number.MAX_SAFE_INTEGER} subunits)`
2396
+ );
2397
+ }
2398
+ return raw;
2399
+ }
2400
+ function formatAssetAmount(asset, raw) {
2401
+ const sign = raw < 0n ? "-" : "";
2402
+ const abs = raw < 0n ? -raw : raw;
2403
+ const unit = 10n ** BigInt(asset.decimals);
2404
+ const whole = abs / unit;
2405
+ const frac = abs % unit;
2406
+ if (asset.decimals === 0) {
2407
+ return `${sign}${whole} ${asset.symbol}`;
2408
+ }
2409
+ return `${sign}${whole}.${frac.toString().padStart(asset.decimals, "0")} ${asset.symbol}`;
2410
+ }
2411
+ async function writeFileAtomic(path, data, mode) {
2412
+ await mkdir(dirname(path), { recursive: true });
2413
+ const tmpPath = `${path}.tmp.${randomBytes(6).toString("hex")}`;
2414
+ await writeFile(tmpPath, data, { mode });
2415
+ try {
2416
+ await rename(tmpPath, path);
2417
+ } catch (e) {
2418
+ try {
2419
+ const { unlink } = await import('node:fs/promises');
2420
+ await unlink(tmpPath);
2421
+ } catch {
2422
+ }
2423
+ throw e;
2424
+ }
2425
+ }
2426
+
2427
+ // src/config/global.ts
2428
+ var SessionSpendLimitEntrySchema = z.object({
2429
+ chain: z.enum(["solana"]),
2430
+ token: z.string().min(1).max(16).regex(/^[a-z0-9]+$/, "token must be lowercase alphanumeric"),
2431
+ mint: z.string().min(1).max(64).optional(),
2432
+ amount: z.number().positive().finite()
2433
+ }).strict();
2434
+ var GlobalConfigSchema = z.object({
2435
+ session_spend_limits: z.array(SessionSpendLimitEntrySchema).max(16).optional()
2436
+ }).strict();
2437
+ function isEnoent(e) {
2438
+ return typeof e === "object" && e !== null && "code" in e && e.code === "ENOENT";
2439
+ }
2440
+ async function loadGlobalConfig(path) {
2441
+ let raw;
2442
+ try {
2443
+ raw = await readFile(path, "utf-8");
2444
+ } catch (e) {
2445
+ if (isEnoent(e)) {
2446
+ return {};
2447
+ }
2448
+ throw e;
2449
+ }
2450
+ if (raw.trim() === "") {
2451
+ return {};
2452
+ }
2453
+ const parsed = YAML2.parse(raw);
2454
+ return GlobalConfigSchema.parse(parsed ?? {});
2455
+ }
2456
+ async function writeGlobalConfig(path, config) {
2457
+ const validated = GlobalConfigSchema.parse(config);
2458
+ const body = YAML2.stringify(validated);
2459
+ await writeFileAtomic(path, body, 420);
2460
+ }
2334
2461
  function formatSol(lamports) {
2335
2462
  const sol = new Decimal2(lamports).div(LAMPORTS_PER_SOL);
2336
2463
  if (sol.gte(1e6)) {
@@ -2522,6 +2649,6 @@ function makeCensor() {
2522
2649
  };
2523
2650
  }
2524
2651
 
2525
- export { BoundedSet, DEFAULTS, DEFAULT_KIND_OFFSET, DEFAULT_REDACT_PATHS, DiscoveryService, ElisymClient, ElisymIdentity, INPUT_REDACT_PATHS, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, PaymentRequestSchema, PingService, RELAYS, SECRET_REDACT_PATHS, SolanaPaymentStrategy, assertExpiry, assertLamports, buildPaymentInstructions, calculateProtocolFee, clearPriorityFeeCache, clearProtocolConfigCache, createPaymentRequestWithOnchainConfig, createSlidingWindowLimiter, estimatePriorityFeeMicroLamports, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, makeCensor, nip44Decrypt, nip44Encrypt, parsePaymentRequest, pickPercentileFee, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry };
2652
+ export { BoundedSet, DEFAULTS, DEFAULT_KIND_OFFSET, DEFAULT_REDACT_PATHS, DiscoveryService, ElisymClient, ElisymIdentity, GlobalConfigSchema, INPUT_REDACT_PATHS, KIND_APP_HANDLER, KIND_JOB_FEEDBACK, KIND_JOB_REQUEST, KIND_JOB_REQUEST_BASE, KIND_JOB_RESULT, KIND_JOB_RESULT_BASE, KIND_PING, KIND_PONG, KNOWN_ASSETS, LAMPORTS_PER_SOL, LIMITS, MarketplaceService, MediaService, NATIVE_SOL, NostrPool, PROTOCOL_FEE_BPS, PROTOCOL_PROGRAM_ID_DEVNET, PROTOCOL_TREASURY, PaymentRequestSchema, PingService, RELAYS, SECRET_REDACT_PATHS, SessionSpendLimitEntrySchema, SolanaPaymentStrategy, assertExpiry, assertLamports, assetByKey, assetKey, buildPaymentInstructions, calculateProtocolFee, clearPriorityFeeCache, clearProtocolConfigCache, createPaymentRequestWithOnchainConfig, createSlidingWindowLimiter, estimatePriorityFeeMicroLamports, formatAssetAmount, formatSol, getProtocolConfig, getProtocolProgramId, jobRequestKind, jobResultKind, loadGlobalConfig, makeCensor, nip44Decrypt, nip44Encrypt, parseAssetAmount, parsePaymentRequest, pickPercentileFee, resolveKnownAsset, timeAgo, toDTag, truncateKey, validateAgentName, validateExpiry, writeGlobalConfig };
2526
2653
  //# sourceMappingURL=index.js.map
2527
2654
  //# sourceMappingURL=index.js.map