@elisym/sdk 0.25.5 → 0.26.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/skills.d.cts CHANGED
@@ -65,6 +65,12 @@ interface SkillContext {
65
65
  * `llmOverride` (or undefined for the agent default).
66
66
  */
67
67
  getLlm?: (override?: SkillLlmOverride) => LlmClient | undefined;
68
+ /**
69
+ * x402 protocol driver for `mode: 'x402'` skills. Injected by the elisym
70
+ * CLI runtime; absent in SDK-only hosts (which also refuse to LOAD x402
71
+ * skills - see `LoadSkillsOptions.allowX402Skills`).
72
+ */
73
+ x402?: X402Invoker;
68
74
  agentName: string;
69
75
  agentDescription: string;
70
76
  signal?: AbortSignal;
@@ -75,10 +81,66 @@ interface SkillContext {
75
81
  * - `static-file`: return the contents of a fixed file. No input required.
76
82
  * - `static-script`: spawn a script with no stdin. No input required.
77
83
  * - `dynamic-script`: spawn a script and pipe the user's input to stdin.
84
+ * - `x402`: proxy the job to an x402-paid HTTP upstream; the host runtime pays
85
+ * the upstream from the agent's wallet. Requires an x402-capable host (the
86
+ * elisym CLI) that injects `SkillContext.x402` - gated at load time via
87
+ * `LoadSkillsOptions.allowX402Skills`.
78
88
  *
79
- * Static modes set `card.static = true` so the webapp hides its input box.
89
+ * Static modes (and an x402 GET skill without a query param) set
90
+ * `card.static = true` so the webapp hides its input box.
91
+ */
92
+ type SkillMode = 'llm' | 'static-file' | 'static-script' | 'dynamic-script' | 'x402';
93
+ /**
94
+ * Static configuration of an x402 bridge skill, parsed from SKILL.md
95
+ * frontmatter (`x402_*` fields). All money values are integer subunits.
80
96
  */
81
- type SkillMode = 'llm' | 'static-file' | 'static-script' | 'dynamic-script';
97
+ interface X402SkillParams {
98
+ /** Upstream resource URL (https only). */
99
+ url: string;
100
+ /** HTTP method for the upstream call. Buyer input maps to the POST body or a GET query param. */
101
+ method: 'GET' | 'POST';
102
+ /** Query parameter carrying the buyer input (GET only). Absent on GET => the skill takes no input. */
103
+ queryParam?: string;
104
+ /**
105
+ * Ceiling on the upstream quote in subunits of the upstream asset. The
106
+ * host's payment layer must refuse to sign anything above it - this is the
107
+ * operator's wallet protection against upstream repricing.
108
+ */
109
+ maxUpstreamSubunits: bigint;
110
+ /**
111
+ * Ceiling on the buyer input size in bytes (inline UTF-8 length, or an
112
+ * attachment's declared size). Enforced by the host BEFORE the customer
113
+ * pays - upstream request-body limits are opaque, so an oversized input
114
+ * would otherwise fail deterministically after payment.
115
+ */
116
+ maxInputBytes: number;
117
+ }
118
+ /** Upstream response mapped by Content-Type: text inline, anything else as a file. */
119
+ interface X402ProxyResult {
120
+ data: string;
121
+ outputMime?: string;
122
+ /**
123
+ * Set for a non-text upstream body. The file is owned by the host's
124
+ * idempotency cache (delivery source for crash recovery) - the skill must
125
+ * NOT attach a cleanup callback for it; the cache's TTL sweep deletes it.
126
+ */
127
+ filePath?: string;
128
+ }
129
+ /**
130
+ * Host-provided x402 protocol driver. Implemented by the elisym CLI runtime
131
+ * (payment signing, requirement policies, idempotency, error classification
132
+ * all live host-side); the SDK's `X402ProxySkill` only delegates to it.
133
+ */
134
+ interface X402Invoker {
135
+ /**
136
+ * Pre-payment gate: verify the wallet invariant, probe the live 402 quote
137
+ * against `maxUpstreamSubunits`, check balance/margin and the input-size
138
+ * rules. Throws to refuse the job before the customer pays.
139
+ */
140
+ preflight(params: X402SkillParams, input: SkillInput): Promise<void>;
141
+ /** Perform the paid upstream call (or return a cached result for this jobId). */
142
+ execute(params: X402SkillParams, input: SkillInput, signal?: AbortSignal): Promise<X402ProxyResult>;
143
+ }
82
144
  interface ToolDef {
83
145
  name: string;
84
146
  description: string;
@@ -140,6 +202,14 @@ interface Skill {
140
202
  llmOverride?: SkillLlmOverride;
141
203
  image?: string;
142
204
  imageFile?: string;
205
+ /**
206
+ * Optional pre-payment gate. The runtime calls it BEFORE the customer pays
207
+ * (and before the job enters the ledger); throwing refuses the job with an
208
+ * error feedback and no payment. `input` is the pre-payment view: inline
209
+ * text, tags and jobId only - a file input is fetched after payment, so
210
+ * `filePath` is never set here.
211
+ */
212
+ preflight?(input: SkillInput, ctx: SkillContext): Promise<void>;
143
213
  execute(input: SkillInput, ctx: SkillContext): Promise<SkillOutput>;
144
214
  }
145
215
 
@@ -400,6 +470,44 @@ declare class DynamicScriptSkill implements Skill {
400
470
  execute(input: SkillInput, ctx: SkillContext): Promise<SkillOutput>;
401
471
  }
402
472
 
473
+ interface X402ProxySkillParams {
474
+ name: string;
475
+ description: string;
476
+ capabilities: string[];
477
+ priceSubunits: bigint;
478
+ asset: Asset;
479
+ x402: X402SkillParams;
480
+ image?: string;
481
+ imageFile?: string;
482
+ }
483
+ /**
484
+ * Bridges a job to an x402-paid HTTP upstream. The skill itself is a thin
485
+ * adapter: the whole protocol (payment signing, requirement policies,
486
+ * idempotency cache, error classification) lives in the host-injected
487
+ * `SkillContext.x402` driver, mirroring how `mode: 'llm'` delegates to
488
+ * `ctx.getLlm`. Constructing this skill is gated at load time
489
+ * (`LoadSkillsOptions.allowX402Skills`), so an SDK-only host never
490
+ * advertises a capability it cannot execute.
491
+ */
492
+ declare class X402ProxySkill implements Skill {
493
+ name: string;
494
+ description: string;
495
+ capabilities: string[];
496
+ priceSubunits: bigint;
497
+ asset: Asset;
498
+ mode: SkillMode;
499
+ image?: string;
500
+ imageFile?: string;
501
+ llmOverride?: SkillLlmOverride;
502
+ readonly x402: X402SkillParams;
503
+ constructor(params: X402ProxySkillParams);
504
+ /** GET upstream without a query param consumes no buyer input (card should be static). */
505
+ get noInput(): boolean;
506
+ private requireInvoker;
507
+ preflight(input: SkillInput, ctx: SkillContext): Promise<void>;
508
+ execute(input: SkillInput, ctx: SkillContext): Promise<SkillOutput>;
509
+ }
510
+
403
511
  /**
404
512
  * Resolve `value` relative to `rootDir` and reject anything that escapes
405
513
  * the root (`..` segments, absolute paths outside it, or the root itself).
@@ -410,6 +518,14 @@ declare class DynamicScriptSkill implements Skill {
410
518
  declare function resolveInsidePath(rootDir: string, value: string): string | null;
411
519
 
412
520
  declare const DEFAULT_MAX_TOOL_ROUNDS = 10;
521
+ /**
522
+ * Default ceiling on buyer input size for x402 skills (bytes). Conservative:
523
+ * upstream request-body limits are opaque (express.json defaults to 100KB,
524
+ * nginx to 1MiB) and an over-limit POST fails only AFTER the customer paid.
525
+ * Operators raise it per skill via `x402_max_input_bytes` when the upstream
526
+ * is known to accept more; hard cap is `LIMITS.MAX_REINLINE_TEXT_BYTES`.
527
+ */
528
+ declare const DEFAULT_X402_MAX_INPUT_BYTES = 100000;
413
529
  interface SkillFrontmatter {
414
530
  name?: unknown;
415
531
  description?: unknown;
@@ -478,6 +594,23 @@ interface SkillFrontmatter {
478
594
  * `execution_timeout_secs`, then to unlimited.
479
595
  */
480
596
  max_execution_secs?: unknown;
597
+ /** Required when mode === 'x402'. Upstream resource URL (https only). */
598
+ x402_url?: unknown;
599
+ /** HTTP method for the upstream call ('GET' | 'POST'). Default 'POST'. x402 mode only. */
600
+ x402_method?: unknown;
601
+ /** Query parameter carrying the buyer input. GET upstreams only; absent => skill takes no input. */
602
+ x402_query_param?: unknown;
603
+ /**
604
+ * Required when mode === 'x402'. Ceiling on the upstream quote in integer
605
+ * subunits of the upstream asset (recorded at `elisym x402 add` time). The
606
+ * host's payment layer refuses to sign anything above it.
607
+ */
608
+ x402_max_upstream?: unknown;
609
+ /**
610
+ * Ceiling on buyer input size in bytes (x402 mode only). Default
611
+ * `DEFAULT_X402_MAX_INPUT_BYTES`; hard cap `LIMITS.MAX_REINLINE_TEXT_BYTES`.
612
+ */
613
+ x402_max_input_bytes?: unknown;
481
614
  }
482
615
  interface ParsedSkill {
483
616
  name: string;
@@ -533,6 +666,14 @@ interface ParsedSkill {
533
666
  * then to unlimited.
534
667
  */
535
668
  executionTimeoutSecs?: number;
669
+ /** Set when mode === 'x402': the parsed `x402_*` frontmatter block. */
670
+ x402?: X402SkillParams;
671
+ /**
672
+ * True when the skill consumes no buyer input (x402 GET without a query
673
+ * param). Hosts mark the discovery card `static` so clients hide the input
674
+ * box instead of silently dropping what the buyer typed.
675
+ */
676
+ noInput?: boolean;
536
677
  }
537
678
  interface LoaderLogger {
538
679
  debug?(obj: Record<string, unknown>, msg?: string): void;
@@ -545,6 +686,14 @@ interface LoadSkillsOptions {
545
686
  * false: paid-only (plugin's historical behaviour).
546
687
  */
547
688
  allowFreeSkills?: boolean;
689
+ /**
690
+ * When true, `mode: 'x402'` skills load. Default false: an x402 skill
691
+ * needs a host runtime that injects `SkillContext.x402` (the elisym CLI);
692
+ * an SDK-only host (e.g. the ElizaOS plugin) must fail at load time -
693
+ * NOT after a customer has paid for a job the skill cannot execute.
694
+ * `loadSkillsFromDir` then skips the skill with a warning.
695
+ */
696
+ allowX402Skills?: boolean;
548
697
  logger?: LoaderLogger;
549
698
  }
550
699
  declare function parseSkillMd(content: string): {
@@ -559,4 +708,4 @@ declare function validateSkillFrontmatter(frontmatter: SkillFrontmatter, systemP
559
708
  */
560
709
  declare function loadSkillsFromDir(skillsDir: string, options?: LoadSkillsOptions): Skill[];
561
710
 
562
- export { type CompletionResult, DEFAULT_MAX_TOOL_ROUNDS, DEFAULT_SCRIPT_TIMEOUT_MS, DynamicScriptSkill, type DynamicScriptSkillParams, type LlmClient, type LlmClientConfig, type LlmProvider, type LoadSkillsOptions, type LoaderLogger, MAX_SCRIPT_OUTPUT, MAX_STATIC_FILE_SIZE, type ParsedSkill, type RunScriptOptions, type RunScriptResult, ScriptSkill, type ScriptSkillLogger, type ScriptSkillParams, type Skill, type SkillContext, type SkillFrontmatter, type SkillInput, type SkillLlmOverride, type SkillMode, type SkillOutput, SkillRateLimit, type SkillToolDef, StaticFileSkill, type StaticFileSkillParams, StaticScriptSkill, type StaticScriptSkillParams, type ToolCall, type ToolDef, type ToolResult, loadSkillsFromDir, parseSkillMd, resolveInsidePath, runScript, validateSkillFrontmatter };
711
+ export { type CompletionResult, DEFAULT_MAX_TOOL_ROUNDS, DEFAULT_SCRIPT_TIMEOUT_MS, DEFAULT_X402_MAX_INPUT_BYTES, DynamicScriptSkill, type DynamicScriptSkillParams, type LlmClient, type LlmClientConfig, type LlmProvider, type LoadSkillsOptions, type LoaderLogger, MAX_SCRIPT_OUTPUT, MAX_STATIC_FILE_SIZE, type ParsedSkill, type RunScriptOptions, type RunScriptResult, ScriptSkill, type ScriptSkillLogger, type ScriptSkillParams, type Skill, type SkillContext, type SkillFrontmatter, type SkillInput, type SkillLlmOverride, type SkillMode, type SkillOutput, SkillRateLimit, type SkillToolDef, StaticFileSkill, type StaticFileSkillParams, StaticScriptSkill, type StaticScriptSkillParams, type ToolCall, type ToolDef, type ToolResult, type X402Invoker, type X402ProxyResult, X402ProxySkill, type X402ProxySkillParams, type X402SkillParams, loadSkillsFromDir, parseSkillMd, resolveInsidePath, runScript, validateSkillFrontmatter };
package/dist/skills.d.ts CHANGED
@@ -65,6 +65,12 @@ interface SkillContext {
65
65
  * `llmOverride` (or undefined for the agent default).
66
66
  */
67
67
  getLlm?: (override?: SkillLlmOverride) => LlmClient | undefined;
68
+ /**
69
+ * x402 protocol driver for `mode: 'x402'` skills. Injected by the elisym
70
+ * CLI runtime; absent in SDK-only hosts (which also refuse to LOAD x402
71
+ * skills - see `LoadSkillsOptions.allowX402Skills`).
72
+ */
73
+ x402?: X402Invoker;
68
74
  agentName: string;
69
75
  agentDescription: string;
70
76
  signal?: AbortSignal;
@@ -75,10 +81,66 @@ interface SkillContext {
75
81
  * - `static-file`: return the contents of a fixed file. No input required.
76
82
  * - `static-script`: spawn a script with no stdin. No input required.
77
83
  * - `dynamic-script`: spawn a script and pipe the user's input to stdin.
84
+ * - `x402`: proxy the job to an x402-paid HTTP upstream; the host runtime pays
85
+ * the upstream from the agent's wallet. Requires an x402-capable host (the
86
+ * elisym CLI) that injects `SkillContext.x402` - gated at load time via
87
+ * `LoadSkillsOptions.allowX402Skills`.
78
88
  *
79
- * Static modes set `card.static = true` so the webapp hides its input box.
89
+ * Static modes (and an x402 GET skill without a query param) set
90
+ * `card.static = true` so the webapp hides its input box.
91
+ */
92
+ type SkillMode = 'llm' | 'static-file' | 'static-script' | 'dynamic-script' | 'x402';
93
+ /**
94
+ * Static configuration of an x402 bridge skill, parsed from SKILL.md
95
+ * frontmatter (`x402_*` fields). All money values are integer subunits.
80
96
  */
81
- type SkillMode = 'llm' | 'static-file' | 'static-script' | 'dynamic-script';
97
+ interface X402SkillParams {
98
+ /** Upstream resource URL (https only). */
99
+ url: string;
100
+ /** HTTP method for the upstream call. Buyer input maps to the POST body or a GET query param. */
101
+ method: 'GET' | 'POST';
102
+ /** Query parameter carrying the buyer input (GET only). Absent on GET => the skill takes no input. */
103
+ queryParam?: string;
104
+ /**
105
+ * Ceiling on the upstream quote in subunits of the upstream asset. The
106
+ * host's payment layer must refuse to sign anything above it - this is the
107
+ * operator's wallet protection against upstream repricing.
108
+ */
109
+ maxUpstreamSubunits: bigint;
110
+ /**
111
+ * Ceiling on the buyer input size in bytes (inline UTF-8 length, or an
112
+ * attachment's declared size). Enforced by the host BEFORE the customer
113
+ * pays - upstream request-body limits are opaque, so an oversized input
114
+ * would otherwise fail deterministically after payment.
115
+ */
116
+ maxInputBytes: number;
117
+ }
118
+ /** Upstream response mapped by Content-Type: text inline, anything else as a file. */
119
+ interface X402ProxyResult {
120
+ data: string;
121
+ outputMime?: string;
122
+ /**
123
+ * Set for a non-text upstream body. The file is owned by the host's
124
+ * idempotency cache (delivery source for crash recovery) - the skill must
125
+ * NOT attach a cleanup callback for it; the cache's TTL sweep deletes it.
126
+ */
127
+ filePath?: string;
128
+ }
129
+ /**
130
+ * Host-provided x402 protocol driver. Implemented by the elisym CLI runtime
131
+ * (payment signing, requirement policies, idempotency, error classification
132
+ * all live host-side); the SDK's `X402ProxySkill` only delegates to it.
133
+ */
134
+ interface X402Invoker {
135
+ /**
136
+ * Pre-payment gate: verify the wallet invariant, probe the live 402 quote
137
+ * against `maxUpstreamSubunits`, check balance/margin and the input-size
138
+ * rules. Throws to refuse the job before the customer pays.
139
+ */
140
+ preflight(params: X402SkillParams, input: SkillInput): Promise<void>;
141
+ /** Perform the paid upstream call (or return a cached result for this jobId). */
142
+ execute(params: X402SkillParams, input: SkillInput, signal?: AbortSignal): Promise<X402ProxyResult>;
143
+ }
82
144
  interface ToolDef {
83
145
  name: string;
84
146
  description: string;
@@ -140,6 +202,14 @@ interface Skill {
140
202
  llmOverride?: SkillLlmOverride;
141
203
  image?: string;
142
204
  imageFile?: string;
205
+ /**
206
+ * Optional pre-payment gate. The runtime calls it BEFORE the customer pays
207
+ * (and before the job enters the ledger); throwing refuses the job with an
208
+ * error feedback and no payment. `input` is the pre-payment view: inline
209
+ * text, tags and jobId only - a file input is fetched after payment, so
210
+ * `filePath` is never set here.
211
+ */
212
+ preflight?(input: SkillInput, ctx: SkillContext): Promise<void>;
143
213
  execute(input: SkillInput, ctx: SkillContext): Promise<SkillOutput>;
144
214
  }
145
215
 
@@ -400,6 +470,44 @@ declare class DynamicScriptSkill implements Skill {
400
470
  execute(input: SkillInput, ctx: SkillContext): Promise<SkillOutput>;
401
471
  }
402
472
 
473
+ interface X402ProxySkillParams {
474
+ name: string;
475
+ description: string;
476
+ capabilities: string[];
477
+ priceSubunits: bigint;
478
+ asset: Asset;
479
+ x402: X402SkillParams;
480
+ image?: string;
481
+ imageFile?: string;
482
+ }
483
+ /**
484
+ * Bridges a job to an x402-paid HTTP upstream. The skill itself is a thin
485
+ * adapter: the whole protocol (payment signing, requirement policies,
486
+ * idempotency cache, error classification) lives in the host-injected
487
+ * `SkillContext.x402` driver, mirroring how `mode: 'llm'` delegates to
488
+ * `ctx.getLlm`. Constructing this skill is gated at load time
489
+ * (`LoadSkillsOptions.allowX402Skills`), so an SDK-only host never
490
+ * advertises a capability it cannot execute.
491
+ */
492
+ declare class X402ProxySkill implements Skill {
493
+ name: string;
494
+ description: string;
495
+ capabilities: string[];
496
+ priceSubunits: bigint;
497
+ asset: Asset;
498
+ mode: SkillMode;
499
+ image?: string;
500
+ imageFile?: string;
501
+ llmOverride?: SkillLlmOverride;
502
+ readonly x402: X402SkillParams;
503
+ constructor(params: X402ProxySkillParams);
504
+ /** GET upstream without a query param consumes no buyer input (card should be static). */
505
+ get noInput(): boolean;
506
+ private requireInvoker;
507
+ preflight(input: SkillInput, ctx: SkillContext): Promise<void>;
508
+ execute(input: SkillInput, ctx: SkillContext): Promise<SkillOutput>;
509
+ }
510
+
403
511
  /**
404
512
  * Resolve `value` relative to `rootDir` and reject anything that escapes
405
513
  * the root (`..` segments, absolute paths outside it, or the root itself).
@@ -410,6 +518,14 @@ declare class DynamicScriptSkill implements Skill {
410
518
  declare function resolveInsidePath(rootDir: string, value: string): string | null;
411
519
 
412
520
  declare const DEFAULT_MAX_TOOL_ROUNDS = 10;
521
+ /**
522
+ * Default ceiling on buyer input size for x402 skills (bytes). Conservative:
523
+ * upstream request-body limits are opaque (express.json defaults to 100KB,
524
+ * nginx to 1MiB) and an over-limit POST fails only AFTER the customer paid.
525
+ * Operators raise it per skill via `x402_max_input_bytes` when the upstream
526
+ * is known to accept more; hard cap is `LIMITS.MAX_REINLINE_TEXT_BYTES`.
527
+ */
528
+ declare const DEFAULT_X402_MAX_INPUT_BYTES = 100000;
413
529
  interface SkillFrontmatter {
414
530
  name?: unknown;
415
531
  description?: unknown;
@@ -478,6 +594,23 @@ interface SkillFrontmatter {
478
594
  * `execution_timeout_secs`, then to unlimited.
479
595
  */
480
596
  max_execution_secs?: unknown;
597
+ /** Required when mode === 'x402'. Upstream resource URL (https only). */
598
+ x402_url?: unknown;
599
+ /** HTTP method for the upstream call ('GET' | 'POST'). Default 'POST'. x402 mode only. */
600
+ x402_method?: unknown;
601
+ /** Query parameter carrying the buyer input. GET upstreams only; absent => skill takes no input. */
602
+ x402_query_param?: unknown;
603
+ /**
604
+ * Required when mode === 'x402'. Ceiling on the upstream quote in integer
605
+ * subunits of the upstream asset (recorded at `elisym x402 add` time). The
606
+ * host's payment layer refuses to sign anything above it.
607
+ */
608
+ x402_max_upstream?: unknown;
609
+ /**
610
+ * Ceiling on buyer input size in bytes (x402 mode only). Default
611
+ * `DEFAULT_X402_MAX_INPUT_BYTES`; hard cap `LIMITS.MAX_REINLINE_TEXT_BYTES`.
612
+ */
613
+ x402_max_input_bytes?: unknown;
481
614
  }
482
615
  interface ParsedSkill {
483
616
  name: string;
@@ -533,6 +666,14 @@ interface ParsedSkill {
533
666
  * then to unlimited.
534
667
  */
535
668
  executionTimeoutSecs?: number;
669
+ /** Set when mode === 'x402': the parsed `x402_*` frontmatter block. */
670
+ x402?: X402SkillParams;
671
+ /**
672
+ * True when the skill consumes no buyer input (x402 GET without a query
673
+ * param). Hosts mark the discovery card `static` so clients hide the input
674
+ * box instead of silently dropping what the buyer typed.
675
+ */
676
+ noInput?: boolean;
536
677
  }
537
678
  interface LoaderLogger {
538
679
  debug?(obj: Record<string, unknown>, msg?: string): void;
@@ -545,6 +686,14 @@ interface LoadSkillsOptions {
545
686
  * false: paid-only (plugin's historical behaviour).
546
687
  */
547
688
  allowFreeSkills?: boolean;
689
+ /**
690
+ * When true, `mode: 'x402'` skills load. Default false: an x402 skill
691
+ * needs a host runtime that injects `SkillContext.x402` (the elisym CLI);
692
+ * an SDK-only host (e.g. the ElizaOS plugin) must fail at load time -
693
+ * NOT after a customer has paid for a job the skill cannot execute.
694
+ * `loadSkillsFromDir` then skips the skill with a warning.
695
+ */
696
+ allowX402Skills?: boolean;
548
697
  logger?: LoaderLogger;
549
698
  }
550
699
  declare function parseSkillMd(content: string): {
@@ -559,4 +708,4 @@ declare function validateSkillFrontmatter(frontmatter: SkillFrontmatter, systemP
559
708
  */
560
709
  declare function loadSkillsFromDir(skillsDir: string, options?: LoadSkillsOptions): Skill[];
561
710
 
562
- export { type CompletionResult, DEFAULT_MAX_TOOL_ROUNDS, DEFAULT_SCRIPT_TIMEOUT_MS, DynamicScriptSkill, type DynamicScriptSkillParams, type LlmClient, type LlmClientConfig, type LlmProvider, type LoadSkillsOptions, type LoaderLogger, MAX_SCRIPT_OUTPUT, MAX_STATIC_FILE_SIZE, type ParsedSkill, type RunScriptOptions, type RunScriptResult, ScriptSkill, type ScriptSkillLogger, type ScriptSkillParams, type Skill, type SkillContext, type SkillFrontmatter, type SkillInput, type SkillLlmOverride, type SkillMode, type SkillOutput, SkillRateLimit, type SkillToolDef, StaticFileSkill, type StaticFileSkillParams, StaticScriptSkill, type StaticScriptSkillParams, type ToolCall, type ToolDef, type ToolResult, loadSkillsFromDir, parseSkillMd, resolveInsidePath, runScript, validateSkillFrontmatter };
711
+ export { type CompletionResult, DEFAULT_MAX_TOOL_ROUNDS, DEFAULT_SCRIPT_TIMEOUT_MS, DEFAULT_X402_MAX_INPUT_BYTES, DynamicScriptSkill, type DynamicScriptSkillParams, type LlmClient, type LlmClientConfig, type LlmProvider, type LoadSkillsOptions, type LoaderLogger, MAX_SCRIPT_OUTPUT, MAX_STATIC_FILE_SIZE, type ParsedSkill, type RunScriptOptions, type RunScriptResult, ScriptSkill, type ScriptSkillLogger, type ScriptSkillParams, type Skill, type SkillContext, type SkillFrontmatter, type SkillInput, type SkillLlmOverride, type SkillMode, type SkillOutput, SkillRateLimit, type SkillToolDef, StaticFileSkill, type StaticFileSkillParams, StaticScriptSkill, type StaticScriptSkillParams, type ToolCall, type ToolDef, type ToolResult, type X402Invoker, type X402ProxyResult, X402ProxySkill, type X402ProxySkillParams, type X402SkillParams, loadSkillsFromDir, parseSkillMd, resolveInsidePath, runScript, validateSkillFrontmatter };
package/dist/skills.js CHANGED
@@ -447,6 +447,53 @@ var DynamicScriptSkill = class {
447
447
  }
448
448
  }
449
449
  };
450
+
451
+ // src/skills/x402ProxySkill.ts
452
+ var X402ProxySkill = class {
453
+ name;
454
+ description;
455
+ capabilities;
456
+ priceSubunits;
457
+ asset;
458
+ mode = "x402";
459
+ image;
460
+ imageFile;
461
+ llmOverride;
462
+ x402;
463
+ constructor(params) {
464
+ this.name = params.name;
465
+ this.description = params.description;
466
+ this.capabilities = params.capabilities;
467
+ this.priceSubunits = params.priceSubunits;
468
+ this.asset = params.asset;
469
+ this.x402 = params.x402;
470
+ this.image = params.image;
471
+ this.imageFile = params.imageFile;
472
+ }
473
+ /** GET upstream without a query param consumes no buyer input (card should be static). */
474
+ get noInput() {
475
+ return this.x402.method === "GET" && this.x402.queryParam === void 0;
476
+ }
477
+ requireInvoker(ctx) {
478
+ if (ctx.x402 === void 0) {
479
+ throw new Error(
480
+ `Skill "${this.name}": x402 skills require the elisym CLI runtime (no x402 driver in SkillContext)`
481
+ );
482
+ }
483
+ return ctx.x402;
484
+ }
485
+ async preflight(input, ctx) {
486
+ await this.requireInvoker(ctx).preflight(this.x402, input);
487
+ }
488
+ async execute(input, ctx) {
489
+ const result = await this.requireInvoker(ctx).execute(this.x402, input, ctx.signal);
490
+ return {
491
+ data: result.data,
492
+ outputMime: result.outputMime,
493
+ filePath: result.filePath
494
+ };
495
+ }
496
+ };
450
497
  function resolveInsidePath(rootDir, value) {
451
498
  const root = resolve(rootDir);
452
499
  const candidate = resolve(root, value);
@@ -457,6 +504,10 @@ function resolveInsidePath(rootDir, value) {
457
504
  return candidate;
458
505
  }
459
506
  var LIMITS = {
507
+ // Ceiling above which a text/* attachment is NOT materialized back into a string
508
+ // (re-inlined into SkillInput.data) - the consumer gets a filePath / explicit
509
+ // fetch instead. Also bounds the in-memory git-diff buffer (memory-DoS guard).
510
+ MAX_REINLINE_TEXT_BYTES: 4194304,
460
511
  // Upper bound for execution budgets (`max_execution_secs` / `execution_timeout_secs`).
461
512
  // Distinct from MAX_TIMEOUT_SECS (the result-wait cap): execution budgets may be
462
513
  // hours, so this exists only to keep `secs * 1000` within Node's setTimeout limit
@@ -536,8 +587,10 @@ var VALID_MODES = [
536
587
  "llm",
537
588
  "static-file",
538
589
  "static-script",
539
- "dynamic-script"
590
+ "dynamic-script",
591
+ "x402"
540
592
  ];
593
+ var DEFAULT_X402_MAX_INPUT_BYTES = 1e5;
541
594
  function solToLamports(sol) {
542
595
  const asString = (typeof sol === "string" ? sol : String(sol)).trim();
543
596
  if (/^0+(?:\.0+)?$/.test(asString)) {
@@ -693,6 +746,11 @@ function validateLlmOverride(skillName, frontmatter, mode) {
693
746
  if (!hasProvider && !hasModel && !hasMaxTokens) {
694
747
  return void 0;
695
748
  }
749
+ if (mode === "x402") {
750
+ throw new Error(
751
+ `SKILL.md "${skillName}": "provider"/"model"/"max_tokens" are not valid in mode 'x402'`
752
+ );
753
+ }
696
754
  if (hasMaxTokens && mode !== "llm") {
697
755
  throw new Error(
698
756
  `SKILL.md "${skillName}": "max_tokens" is only valid in mode 'llm' (got '${mode}'). For script modes, control token limits inside the script.`
@@ -816,6 +874,114 @@ function validateMaxExecutionSecs(skillName, raw) {
816
874
  }
817
875
  return raw;
818
876
  }
877
+ var X402_METHODS = ["GET", "POST"];
878
+ function validateX402Config(skillName, frontmatter, mode, options) {
879
+ const fieldNames = [
880
+ "x402_url",
881
+ "x402_method",
882
+ "x402_query_param",
883
+ "x402_max_upstream",
884
+ "x402_max_input_bytes"
885
+ ];
886
+ const presentFields = fieldNames.filter(
887
+ (field) => frontmatter[field] !== void 0 && frontmatter[field] !== null
888
+ );
889
+ if (mode !== "x402") {
890
+ if (presentFields.length > 0) {
891
+ throw new Error(
892
+ `SKILL.md "${skillName}": "${presentFields[0]}" is only valid in mode 'x402'`
893
+ );
894
+ }
895
+ return void 0;
896
+ }
897
+ if (!options.allowX402Skills) {
898
+ throw new Error(
899
+ `SKILL.md "${skillName}": x402 skills require the elisym CLI runtime; this host cannot execute mode 'x402'`
900
+ );
901
+ }
902
+ if (typeof frontmatter.x402_url !== "string" || frontmatter.x402_url.length === 0) {
903
+ throw new Error(`SKILL.md "${skillName}": mode 'x402' requires "x402_url" (string)`);
904
+ }
905
+ let url;
906
+ try {
907
+ url = new URL(frontmatter.x402_url);
908
+ } catch {
909
+ throw new Error(`SKILL.md "${skillName}": "x402_url" is not a valid URL`);
910
+ }
911
+ const isLoopback = ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
912
+ if (url.protocol !== "https:" && !(url.protocol === "http:" && isLoopback)) {
913
+ throw new Error(
914
+ `SKILL.md "${skillName}": "x402_url" must be an https:// URL (plain http is allowed for localhost only)`
915
+ );
916
+ }
917
+ let method = "POST";
918
+ if (frontmatter.x402_method !== void 0 && frontmatter.x402_method !== null) {
919
+ if (typeof frontmatter.x402_method !== "string" || !X402_METHODS.includes(frontmatter.x402_method)) {
920
+ throw new Error(
921
+ `SKILL.md "${skillName}": "x402_method" must be one of ${X402_METHODS.join(", ")}`
922
+ );
923
+ }
924
+ method = frontmatter.x402_method;
925
+ }
926
+ let queryParam;
927
+ if (frontmatter.x402_query_param !== void 0 && frontmatter.x402_query_param !== null) {
928
+ if (method !== "GET") {
929
+ throw new Error(
930
+ `SKILL.md "${skillName}": "x402_query_param" is only valid with x402_method 'GET' (POST maps the input to the request body)`
931
+ );
932
+ }
933
+ if (typeof frontmatter.x402_query_param !== "string" || frontmatter.x402_query_param.length === 0) {
934
+ throw new Error(`SKILL.md "${skillName}": "x402_query_param" must be a non-empty string`);
935
+ }
936
+ if (url.searchParams.has(frontmatter.x402_query_param)) {
937
+ throw new Error(
938
+ `SKILL.md "${skillName}": "x402_query_param" ("${frontmatter.x402_query_param}") collides with a parameter already present in "x402_url"`
939
+ );
940
+ }
941
+ queryParam = frontmatter.x402_query_param;
942
+ }
943
+ const rawMaxUpstream = frontmatter.x402_max_upstream;
944
+ if (rawMaxUpstream === void 0 || rawMaxUpstream === null) {
945
+ throw new Error(
946
+ `SKILL.md "${skillName}": mode 'x402' requires "x402_max_upstream" (integer subunits - the ceiling on the upstream quote)`
947
+ );
948
+ }
949
+ let maxUpstreamSubunits;
950
+ if (typeof rawMaxUpstream === "number" && Number.isSafeInteger(rawMaxUpstream)) {
951
+ maxUpstreamSubunits = BigInt(rawMaxUpstream);
952
+ } else if (typeof rawMaxUpstream === "string" && /^[0-9]+$/.test(rawMaxUpstream)) {
953
+ maxUpstreamSubunits = BigInt(rawMaxUpstream);
954
+ } else {
955
+ throw new Error(
956
+ `SKILL.md "${skillName}": "x402_max_upstream" must be a non-negative integer (subunits)`
957
+ );
958
+ }
959
+ if (maxUpstreamSubunits <= 0n) {
960
+ throw new Error(`SKILL.md "${skillName}": "x402_max_upstream" must be > 0`);
961
+ }
962
+ let maxInputBytes = DEFAULT_X402_MAX_INPUT_BYTES;
963
+ if (frontmatter.x402_max_input_bytes !== void 0 && frontmatter.x402_max_input_bytes !== null) {
964
+ const raw = frontmatter.x402_max_input_bytes;
965
+ if (typeof raw !== "number" || !Number.isInteger(raw) || raw <= 0) {
966
+ throw new Error(
967
+ `SKILL.md "${skillName}": "x402_max_input_bytes" must be a positive integer (bytes)`
968
+ );
969
+ }
970
+ if (raw > LIMITS.MAX_REINLINE_TEXT_BYTES) {
971
+ throw new Error(
972
+ `SKILL.md "${skillName}": "x402_max_input_bytes" must be <= ${LIMITS.MAX_REINLINE_TEXT_BYTES} (inputs above it never reach the skill as text)`
973
+ );
974
+ }
975
+ maxInputBytes = raw;
976
+ }
977
+ return {
978
+ url: frontmatter.x402_url,
979
+ method,
980
+ queryParam,
981
+ maxUpstreamSubunits,
982
+ maxInputBytes
983
+ };
984
+ }
819
985
  function validateSkillFrontmatter(frontmatter, systemPrompt, options = {}) {
820
986
  if (typeof frontmatter.name !== "string" || frontmatter.name.length === 0) {
821
987
  throw new Error('SKILL.md: missing or invalid "name" field');
@@ -1008,6 +1174,7 @@ function validateSkillFrontmatter(frontmatter, systemPrompt, options = {}) {
1008
1174
  frontmatter.name,
1009
1175
  frontmatter.max_execution_secs
1010
1176
  );
1177
+ const x402 = validateX402Config(frontmatter.name, frontmatter, mode, options);
1011
1178
  return {
1012
1179
  name: frontmatter.name,
1013
1180
  description: frontmatter.description,
@@ -1029,7 +1196,9 @@ function validateSkillFrontmatter(frontmatter, systemPrompt, options = {}) {
1029
1196
  inputMime,
1030
1197
  inputText,
1031
1198
  rateLimit,
1032
- executionTimeoutSecs
1199
+ executionTimeoutSecs,
1200
+ x402,
1201
+ noInput: x402 === void 0 ? void 0 : x402.method === "GET" && x402.queryParam === void 0
1033
1202
  };
1034
1203
  }
1035
1204
  function buildSkillFromParsed(parsed, skillDir, logger) {
@@ -1108,6 +1277,23 @@ function buildSkillFromParsed(parsed, skillDir, logger) {
1108
1277
  };
1109
1278
  return parsed.mode === "dynamic-script" ? new DynamicScriptSkill({ ...scriptParams, outputMime: parsed.outputMime }) : new StaticScriptSkill(scriptParams);
1110
1279
  }
1280
+ case "x402": {
1281
+ if (parsed.x402 === void 0) {
1282
+ throw new Error(
1283
+ `SKILL.md "${parsed.name}": internal error - x402 config missing for mode 'x402'`
1284
+ );
1285
+ }
1286
+ return new X402ProxySkill({
1287
+ name: parsed.name,
1288
+ description: parsed.description,
1289
+ capabilities: parsed.capabilities,
1290
+ priceSubunits: parsed.priceSubunits,
1291
+ asset: parsed.asset,
1292
+ x402: parsed.x402,
1293
+ image: parsed.image,
1294
+ imageFile
1295
+ });
1296
+ }
1111
1297
  }
1112
1298
  }
1113
1299
  function loadSkillsFromDir(skillsDir, options = {}) {
@@ -1143,6 +1329,6 @@ function loadSkillsFromDir(skillsDir, options = {}) {
1143
1329
  return skills;
1144
1330
  }
1145
1331
 
1146
- export { DEFAULT_MAX_TOOL_ROUNDS, DEFAULT_SCRIPT_TIMEOUT_MS, DynamicScriptSkill, MAX_SCRIPT_OUTPUT, MAX_STATIC_FILE_SIZE, ScriptSkill, StaticFileSkill, StaticScriptSkill, loadSkillsFromDir, parseSkillMd, resolveInsidePath, runScript, validateSkillFrontmatter };
1332
+ export { DEFAULT_MAX_TOOL_ROUNDS, DEFAULT_SCRIPT_TIMEOUT_MS, DEFAULT_X402_MAX_INPUT_BYTES, DynamicScriptSkill, MAX_SCRIPT_OUTPUT, MAX_STATIC_FILE_SIZE, ScriptSkill, StaticFileSkill, StaticScriptSkill, X402ProxySkill, loadSkillsFromDir, parseSkillMd, resolveInsidePath, runScript, validateSkillFrontmatter };
1147
1333
  //# sourceMappingURL=skills.js.map
1148
1334
  //# sourceMappingURL=skills.js.map