@pulseai/sdk 0.1.2 → 0.1.3

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.ts CHANGED
@@ -321,6 +321,32 @@ declare function createRequirements(params: RequirementsParams): {
321
321
  hash: Hex;
322
322
  };
323
323
 
324
+ type JsonSchema = {
325
+ type: 'object';
326
+ properties: Record<string, {
327
+ type: 'string' | 'number' | 'boolean' | 'object' | 'array';
328
+ description?: string;
329
+ enum?: unknown[];
330
+ items?: {
331
+ type: string;
332
+ };
333
+ }>;
334
+ required?: string[];
335
+ };
336
+ /** Pulse offering schema document (JSON Schema-based). */
337
+ type OfferingSchema = {
338
+ version: number;
339
+ serviceRequirements: JsonSchema;
340
+ deliverableRequirements: JsonSchema;
341
+ };
342
+ /** Default schemas by service type (Custom/5 intentionally omitted). */
343
+ declare const DEFAULT_SCHEMAS: Record<number, OfferingSchema>;
344
+ /** Validate runtime data against a compact JSON Schema object definition. */
345
+ declare function validateAgainstSchema(data: Record<string, unknown> | undefined, schema: JsonSchema): {
346
+ valid: boolean;
347
+ reason?: string;
348
+ };
349
+
324
350
  type JobContext = {
325
351
  jobId: bigint;
326
352
  offeringId: bigint;
@@ -336,6 +362,11 @@ type ExecuteJobResult = {
336
362
  content?: string;
337
363
  url?: string;
338
364
  mimeType?: string;
365
+ usage?: {
366
+ provider: string;
367
+ inputTokens: number;
368
+ outputTokens: number;
369
+ };
339
370
  };
340
371
  type ValidationResult = {
341
372
  valid: boolean;
@@ -344,6 +375,7 @@ type ValidationResult = {
344
375
  interface OfferingHandler {
345
376
  offeringId: number;
346
377
  autoAccept?: boolean;
378
+ schema?: OfferingSchema;
347
379
  validateRequirements?(context: JobContext): Promise<ValidationResult>;
348
380
  executeJob(context: JobContext): Promise<ExecuteJobResult>;
349
381
  }
@@ -360,6 +392,7 @@ type SiteModifierConfig = {
360
392
  declare class SiteModifierHandler implements OfferingHandler {
361
393
  readonly offeringId: number;
362
394
  readonly autoAccept?: boolean;
395
+ readonly schema: OfferingSchema;
363
396
  private readonly config;
364
397
  constructor(offeringId: number, config: SiteModifierConfig, options?: {
365
398
  autoAccept?: boolean;
@@ -542,10 +575,15 @@ type CallAIParams = {
542
575
  maxTokens?: number;
543
576
  signal?: AbortSignal;
544
577
  };
578
+ type AIUsage = {
579
+ inputTokens: number;
580
+ outputTokens: number;
581
+ };
545
582
  type CallAIResult = {
546
583
  content: string;
547
584
  truncated: boolean;
548
585
  raw: unknown;
586
+ usage?: AIUsage;
549
587
  };
550
588
  type AICallOptions = CallAIParams;
551
589
  type AICallResult = CallAIResult;
@@ -588,6 +626,7 @@ interface IndexerOffering {
588
626
  priceUsdm: string;
589
627
  slaMinutes: number | null;
590
628
  description: string | null;
629
+ requirementsSchemaUri?: string;
591
630
  active: boolean;
592
631
  blockNumber: number | null;
593
632
  txHash: string | null;
@@ -720,6 +759,7 @@ declare class HandlerProviderRuntime {
720
759
  private running;
721
760
  private processedJobs;
722
761
  private failedJobs;
762
+ private schemaCache;
723
763
  private readonly maxRetries;
724
764
  private onError?;
725
765
  constructor(client: PulseClient, agentId: bigint, options: HandlerProviderRuntimeOptions);
@@ -734,6 +774,11 @@ declare class HandlerProviderRuntime {
734
774
  private canProcess;
735
775
  private markProcessed;
736
776
  private markFailed;
777
+ private validateSchemaRequirements;
778
+ private resolveOfferingSchemaFromUri;
779
+ private parseDataUriJson;
780
+ private isOfferingSchema;
781
+ private isJsonSchema;
737
782
  private getRemainingSlaMs;
738
783
  }
739
784
 
@@ -3629,4 +3674,4 @@ declare const masterAbi: readonly [{
3629
3674
  readonly anonymous: false;
3630
3675
  }];
3631
3676
 
3632
- export { ACCEPT_TIMEOUT, type AICallOptions, type AICallResult, type AIMessage, type AIProvider, type AgentCardParams, BPS_DENOMINATOR, type BuyerCallbacks, BuyerRuntime, type BuyerRuntimeOptions, type CreateJobParams, type CreatePresetPulseClientOptions, type CreatePulseClientOptions, DEFAULT_INDEXER_URLS, type DeliverableData, type DeliverableParams, EVALUATION_TIMEOUT, type ExecuteJobResult, FEE_BPS, HandlerProviderRuntime, type HandlerProviderRuntimeOptions, type IndexerAgent, IndexerClient, IndexerClientError, type IndexerClientOptions, type IndexerJob, type IndexerJobsFilter, type IndexerOffering, type IndexerOfferingsFilter, type IndexerWarrenLink, type IndexerWarrenLinks, type Job, type JobContext, type JobOffering, JobStatus, type JobTermsParams, type ListOfferingParams, MAINNET_ADDRESSES, MEMO_TYPES, type OfferingHandler, PLATFORM_BUYER_AGENT_ID, PULSE_DOMAIN, type PresetPulseClient, type ProviderCallbacks, ProviderRuntime, type ProviderRuntimeOptions, type PulseAddresses, type PulseAgentData, type PulseClient, RISK_POOL_BPS, type RequirementsData, type RequirementsParams, ServiceType, type SignMemoParams, type SiteModifierConfig, SiteModifierHandler, TESTNET_ADDRESSES, TREASURY_BPS, USDM_MAINNET, type ValidationResult, acceptJob, activateOffering, buyerRelayAbi, callAI, cancelJob, createAgentCard, createDeliverable, createJob, createJobTerms, createMainnetClient, createPulseClient, createRequirements, createTestnetClient, deactivateOffering, deployAgentCard, deployDeliverable, deployJobTerms, deployRequirements, deployWarrenMaster, deployWarrenPage, erc20Abi, erc8004IdentityAbi, erc8004ReputationAbi, evaluate, feeDistributorAbi, formatUsdm, getAgent, getJob, getJobCount, getOffering, getOfferingCount, initAgent, jobEngineAbi, listOffering, mainnetERC8004, masterAbi, megaethMainnet, megaethTestnet, pageAbi, parseUsdm, pulseExtensionAbi, readDeliverable, readRequirements, readWarrenMaster, readWarrenPage, registerAgent, rejectJob, resolveDispute, serviceMarketplaceAbi, setOperator, setWarrenContract, settle, signMemo, submitDeliverable, testnetERC8004, updateOffering, verifyContentHash };
3677
+ export { ACCEPT_TIMEOUT, type AICallOptions, type AICallResult, type AIMessage, type AIProvider, type AIUsage, type AgentCardParams, BPS_DENOMINATOR, type BuyerCallbacks, BuyerRuntime, type BuyerRuntimeOptions, type CreateJobParams, type CreatePresetPulseClientOptions, type CreatePulseClientOptions, DEFAULT_INDEXER_URLS, DEFAULT_SCHEMAS, type DeliverableData, type DeliverableParams, EVALUATION_TIMEOUT, type ExecuteJobResult, FEE_BPS, HandlerProviderRuntime, type HandlerProviderRuntimeOptions, type IndexerAgent, IndexerClient, IndexerClientError, type IndexerClientOptions, type IndexerJob, type IndexerJobsFilter, type IndexerOffering, type IndexerOfferingsFilter, type IndexerWarrenLink, type IndexerWarrenLinks, type Job, type JobContext, type JobOffering, JobStatus, type JobTermsParams, type JsonSchema, type ListOfferingParams, MAINNET_ADDRESSES, MEMO_TYPES, type OfferingHandler, type OfferingSchema, PLATFORM_BUYER_AGENT_ID, PULSE_DOMAIN, type PresetPulseClient, type ProviderCallbacks, ProviderRuntime, type ProviderRuntimeOptions, type PulseAddresses, type PulseAgentData, type PulseClient, RISK_POOL_BPS, type RequirementsData, type RequirementsParams, ServiceType, type SignMemoParams, type SiteModifierConfig, SiteModifierHandler, TESTNET_ADDRESSES, TREASURY_BPS, USDM_MAINNET, type ValidationResult, acceptJob, activateOffering, buyerRelayAbi, callAI, cancelJob, createAgentCard, createDeliverable, createJob, createJobTerms, createMainnetClient, createPulseClient, createRequirements, createTestnetClient, deactivateOffering, deployAgentCard, deployDeliverable, deployJobTerms, deployRequirements, deployWarrenMaster, deployWarrenPage, erc20Abi, erc8004IdentityAbi, erc8004ReputationAbi, evaluate, feeDistributorAbi, formatUsdm, getAgent, getJob, getJobCount, getOffering, getOfferingCount, initAgent, jobEngineAbi, listOffering, mainnetERC8004, masterAbi, megaethMainnet, megaethTestnet, pageAbi, parseUsdm, pulseExtensionAbi, readDeliverable, readRequirements, readWarrenMaster, readWarrenPage, registerAgent, rejectJob, resolveDispute, serviceMarketplaceAbi, setOperator, setWarrenContract, settle, signMemo, submitDeliverable, testnetERC8004, updateOffering, validateAgainstSchema, verifyContentHash };
package/dist/index.js CHANGED
@@ -2896,7 +2896,8 @@ async function callOpenAI(params) {
2896
2896
  throw new Error("OpenAI returned an empty response");
2897
2897
  }
2898
2898
  const truncated = payload?.choices?.[0]?.finish_reason === "length";
2899
- return { content, truncated, raw: payload };
2899
+ const usage = payload?.usage ? { inputTokens: payload.usage.prompt_tokens ?? 0, outputTokens: payload.usage.completion_tokens ?? 0 } : void 0;
2900
+ return { content, truncated, raw: payload, usage };
2900
2901
  }
2901
2902
  async function callAnthropic(params) {
2902
2903
  const systemPrompt = params.messages.filter((message) => message.role === "system").map((message) => message.content).join("\n\n").trim();
@@ -2930,7 +2931,8 @@ async function callAnthropic(params) {
2930
2931
  throw new Error("Anthropic returned an empty response");
2931
2932
  }
2932
2933
  const truncated = payload?.stop_reason === "max_tokens";
2933
- return { content, truncated, raw: payload };
2934
+ const usage = payload?.usage ? { inputTokens: payload.usage.input_tokens ?? 0, outputTokens: payload.usage.output_tokens ?? 0 } : void 0;
2935
+ return { content, truncated, raw: payload, usage };
2934
2936
  }
2935
2937
  async function callGoogle(params) {
2936
2938
  const systemPrompt = params.messages.filter((message) => message.role === "system").map((message) => message.content).join("\n\n").trim();
@@ -2965,7 +2967,8 @@ async function callGoogle(params) {
2965
2967
  throw new Error("Google returned an empty response");
2966
2968
  }
2967
2969
  const truncated = payload?.candidates?.[0]?.finishReason === "MAX_TOKENS";
2968
- return { content, truncated, raw: payload };
2970
+ const usage = payload?.usageMetadata ? { inputTokens: payload.usageMetadata.promptTokenCount ?? 0, outputTokens: payload.usageMetadata.candidatesTokenCount ?? 0 } : void 0;
2971
+ return { content, truncated, raw: payload, usage };
2969
2972
  }
2970
2973
  async function callAI(params) {
2971
2974
  if (params.provider === "openai") {
@@ -2989,8 +2992,8 @@ var RETRY_DELAY_MS = 2e3;
2989
2992
  var GOOGLE_FALLBACK_MODEL = "gemini-2.5-pro";
2990
2993
  var SUPPORTED_PROVIDERS = ["anthropic", "google", "openai"];
2991
2994
  var DEFAULT_MODELS = {
2992
- openai: "gpt-4o-mini",
2993
- anthropic: "claude-3-5-sonnet-latest",
2995
+ openai: "gpt-5.2",
2996
+ anthropic: "claude-sonnet-4-6",
2994
2997
  google: "gemini-3.1-pro-preview"
2995
2998
  };
2996
2999
  var DEFAULT_SYSTEM_PROMPT = [
@@ -3110,6 +3113,52 @@ function getByteLength(content) {
3110
3113
  var SiteModifierHandler = class {
3111
3114
  offeringId;
3112
3115
  autoAccept;
3116
+ schema = {
3117
+ version: 1,
3118
+ serviceRequirements: {
3119
+ type: "object",
3120
+ properties: {
3121
+ siteUrl: {
3122
+ type: "string",
3123
+ description: "URL of the site to modify (http/https)"
3124
+ },
3125
+ modificationRequest: {
3126
+ type: "string",
3127
+ description: "What changes to make"
3128
+ },
3129
+ provider: {
3130
+ type: "string",
3131
+ description: "AI provider",
3132
+ enum: ["google", "anthropic", "openai"]
3133
+ },
3134
+ model: {
3135
+ type: "string",
3136
+ description: "Specific model override"
3137
+ }
3138
+ },
3139
+ required: ["siteUrl", "modificationRequest"]
3140
+ },
3141
+ deliverableRequirements: {
3142
+ type: "object",
3143
+ properties: {
3144
+ type: {
3145
+ type: "string",
3146
+ description: 'Delivery type, always "inline"',
3147
+ enum: ["inline"]
3148
+ },
3149
+ content: {
3150
+ type: "string",
3151
+ description: "Modified HTML content"
3152
+ },
3153
+ mimeType: {
3154
+ type: "string",
3155
+ description: 'Content type, always "text/html"',
3156
+ enum: ["text/html"]
3157
+ }
3158
+ },
3159
+ required: ["type", "content", "mimeType"]
3160
+ }
3161
+ };
3113
3162
  config;
3114
3163
  constructor(offeringId, config, options) {
3115
3164
  this.offeringId = offeringId;
@@ -3132,7 +3181,8 @@ var SiteModifierHandler = class {
3132
3181
  const targetUrl = parseAndValidateUrl(requirements.siteUrl);
3133
3182
  ensureAllowedUrl(targetUrl, this.config.allowedDomains);
3134
3183
  const provider = requirements.provider ?? this.config.defaultProvider ?? "openai";
3135
- const model = requirements.model?.trim() || this.config.defaultModel?.trim() || DEFAULT_MODELS[provider];
3184
+ const useConfigModel = provider === (this.config.defaultProvider ?? "openai");
3185
+ const model = requirements.model?.trim() || (useConfigModel ? this.config.defaultModel?.trim() : void 0) || DEFAULT_MODELS[provider];
3136
3186
  const maxTokens = this.config.maxTokens && this.config.maxTokens > 0 ? this.config.maxTokens : DEFAULT_MAX_TOKENS;
3137
3187
  if (!model) {
3138
3188
  throw new Error(
@@ -3235,7 +3285,8 @@ var SiteModifierHandler = class {
3235
3285
  return {
3236
3286
  type: "inline",
3237
3287
  content: modifiedHtml,
3238
- mimeType: "text/html"
3288
+ mimeType: "text/html",
3289
+ usage: aiResponse.usage ? { provider, inputTokens: aiResponse.usage.inputTokens, outputTokens: aiResponse.usage.outputTokens } : void 0
3239
3290
  };
3240
3291
  }
3241
3292
  parseRequirements(requirements) {
@@ -3351,6 +3402,189 @@ var SiteModifierHandler = class {
3351
3402
  }
3352
3403
  };
3353
3404
 
3405
+ // src/types.ts
3406
+ var JobStatus = /* @__PURE__ */ ((JobStatus2) => {
3407
+ JobStatus2[JobStatus2["Created"] = 0] = "Created";
3408
+ JobStatus2[JobStatus2["Accepted"] = 1] = "Accepted";
3409
+ JobStatus2[JobStatus2["InProgress"] = 2] = "InProgress";
3410
+ JobStatus2[JobStatus2["Delivered"] = 3] = "Delivered";
3411
+ JobStatus2[JobStatus2["Evaluated"] = 4] = "Evaluated";
3412
+ JobStatus2[JobStatus2["Completed"] = 5] = "Completed";
3413
+ JobStatus2[JobStatus2["Disputed"] = 6] = "Disputed";
3414
+ JobStatus2[JobStatus2["Cancelled"] = 7] = "Cancelled";
3415
+ return JobStatus2;
3416
+ })(JobStatus || {});
3417
+ var ServiceType = /* @__PURE__ */ ((ServiceType2) => {
3418
+ ServiceType2[ServiceType2["TextGeneration"] = 0] = "TextGeneration";
3419
+ ServiceType2[ServiceType2["ImageGeneration"] = 1] = "ImageGeneration";
3420
+ ServiceType2[ServiceType2["DataAnalysis"] = 2] = "DataAnalysis";
3421
+ ServiceType2[ServiceType2["CodeGeneration"] = 3] = "CodeGeneration";
3422
+ ServiceType2[ServiceType2["Translation"] = 4] = "Translation";
3423
+ ServiceType2[ServiceType2["Custom"] = 5] = "Custom";
3424
+ return ServiceType2;
3425
+ })(ServiceType || {});
3426
+
3427
+ // src/handler/schema.ts
3428
+ var DEFAULT_SCHEMAS = {
3429
+ [0 /* TextGeneration */]: {
3430
+ version: 1,
3431
+ serviceRequirements: {
3432
+ type: "object",
3433
+ properties: {
3434
+ prompt: { type: "string", description: "Text prompt to generate from" },
3435
+ maxTokens: { type: "number", description: "Maximum output token count" }
3436
+ },
3437
+ required: ["prompt"]
3438
+ },
3439
+ deliverableRequirements: {
3440
+ type: "object",
3441
+ properties: {
3442
+ text: { type: "string", description: "Generated text output" },
3443
+ tokenCount: { type: "number", description: "Tokens used to produce output" }
3444
+ },
3445
+ required: ["text"]
3446
+ }
3447
+ },
3448
+ [1 /* ImageGeneration */]: {
3449
+ version: 1,
3450
+ serviceRequirements: {
3451
+ type: "object",
3452
+ properties: {
3453
+ prompt: { type: "string", description: "Image prompt to generate from" },
3454
+ size: { type: "string", description: "Output size (for example: 1024x1024)" },
3455
+ style: { type: "string", description: "Desired visual style" }
3456
+ },
3457
+ required: ["prompt"]
3458
+ },
3459
+ deliverableRequirements: {
3460
+ type: "object",
3461
+ properties: {
3462
+ imageUrl: { type: "string", description: "URL of the generated image" },
3463
+ mimeType: { type: "string", description: "Image MIME type" }
3464
+ },
3465
+ required: ["imageUrl"]
3466
+ }
3467
+ },
3468
+ [2 /* DataAnalysis */]: {
3469
+ version: 1,
3470
+ serviceRequirements: {
3471
+ type: "object",
3472
+ properties: {
3473
+ data: { type: "string", description: "Input data for analysis" },
3474
+ analysisRequest: { type: "string", description: "Question or analysis task" }
3475
+ },
3476
+ required: ["data", "analysisRequest"]
3477
+ },
3478
+ deliverableRequirements: {
3479
+ type: "object",
3480
+ properties: {
3481
+ summary: { type: "string", description: "Analysis summary" },
3482
+ findings: {
3483
+ type: "array",
3484
+ description: "Structured key findings",
3485
+ items: { type: "string" }
3486
+ }
3487
+ },
3488
+ required: ["summary"]
3489
+ }
3490
+ },
3491
+ [3 /* CodeGeneration */]: {
3492
+ version: 1,
3493
+ serviceRequirements: {
3494
+ type: "object",
3495
+ properties: {
3496
+ prompt: { type: "string", description: "Code generation request" },
3497
+ language: { type: "string", description: "Target programming language" }
3498
+ },
3499
+ required: ["prompt"]
3500
+ },
3501
+ deliverableRequirements: {
3502
+ type: "object",
3503
+ properties: {
3504
+ code: { type: "string", description: "Generated source code" },
3505
+ language: { type: "string", description: "Detected or requested language" }
3506
+ },
3507
+ required: ["code"]
3508
+ }
3509
+ },
3510
+ [4 /* Translation */]: {
3511
+ version: 1,
3512
+ serviceRequirements: {
3513
+ type: "object",
3514
+ properties: {
3515
+ text: { type: "string", description: "Text to translate" },
3516
+ targetLanguage: { type: "string", description: "Target language code or name" },
3517
+ sourceLanguage: { type: "string", description: "Optional source language code or name" }
3518
+ },
3519
+ required: ["text", "targetLanguage"]
3520
+ },
3521
+ deliverableRequirements: {
3522
+ type: "object",
3523
+ properties: {
3524
+ translatedText: { type: "string", description: "Translated text output" },
3525
+ sourceLanguage: { type: "string", description: "Detected or provided source language" }
3526
+ },
3527
+ required: ["translatedText"]
3528
+ }
3529
+ }
3530
+ };
3531
+ function isObjectRecord(value) {
3532
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3533
+ }
3534
+ function isTypeMatch(value, type) {
3535
+ switch (type) {
3536
+ case "string":
3537
+ return typeof value === "string";
3538
+ case "number":
3539
+ return typeof value === "number" && Number.isFinite(value);
3540
+ case "boolean":
3541
+ return typeof value === "boolean";
3542
+ case "object":
3543
+ return isObjectRecord(value);
3544
+ case "array":
3545
+ return Array.isArray(value);
3546
+ default:
3547
+ return false;
3548
+ }
3549
+ }
3550
+ function validateAgainstSchema(data, schema) {
3551
+ if (!data || !isObjectRecord(data)) {
3552
+ return { valid: false, reason: "Requirements must be a JSON object" };
3553
+ }
3554
+ for (const requiredField of schema.required ?? []) {
3555
+ if (!(requiredField in data)) {
3556
+ return { valid: false, reason: `Missing required field: ${requiredField}` };
3557
+ }
3558
+ }
3559
+ for (const [field, fieldSchema] of Object.entries(schema.properties)) {
3560
+ const value = data[field];
3561
+ if (value === void 0) continue;
3562
+ if (!isTypeMatch(value, fieldSchema.type)) {
3563
+ return {
3564
+ valid: false,
3565
+ reason: `Field "${field}" must be of type ${fieldSchema.type}`
3566
+ };
3567
+ }
3568
+ if (fieldSchema.enum && !fieldSchema.enum.some((enumValue) => Object.is(enumValue, value))) {
3569
+ return {
3570
+ valid: false,
3571
+ reason: `Field "${field}" must be one of: ${fieldSchema.enum.map((v) => JSON.stringify(v)).join(", ")}`
3572
+ };
3573
+ }
3574
+ if (fieldSchema.type === "array" && fieldSchema.items && Array.isArray(value)) {
3575
+ for (let i = 0; i < value.length; i += 1) {
3576
+ if (!isTypeMatch(value[i], fieldSchema.items.type)) {
3577
+ return {
3578
+ valid: false,
3579
+ reason: `Field "${field}[${i}]" must be of type ${fieldSchema.items.type}`
3580
+ };
3581
+ }
3582
+ }
3583
+ }
3584
+ }
3585
+ return { valid: true };
3586
+ }
3587
+
3354
3588
  // src/utils.ts
3355
3589
  var USDM_DECIMALS = 18;
3356
3590
  var USDM_SCALE = 10n ** BigInt(USDM_DECIMALS);
@@ -3387,28 +3621,6 @@ function formatUsdm(amount) {
3387
3621
  return `${whole}.${trimmedFraction.padEnd(2, "0")}`;
3388
3622
  }
3389
3623
 
3390
- // src/types.ts
3391
- var JobStatus = /* @__PURE__ */ ((JobStatus2) => {
3392
- JobStatus2[JobStatus2["Created"] = 0] = "Created";
3393
- JobStatus2[JobStatus2["Accepted"] = 1] = "Accepted";
3394
- JobStatus2[JobStatus2["InProgress"] = 2] = "InProgress";
3395
- JobStatus2[JobStatus2["Delivered"] = 3] = "Delivered";
3396
- JobStatus2[JobStatus2["Evaluated"] = 4] = "Evaluated";
3397
- JobStatus2[JobStatus2["Completed"] = 5] = "Completed";
3398
- JobStatus2[JobStatus2["Disputed"] = 6] = "Disputed";
3399
- JobStatus2[JobStatus2["Cancelled"] = 7] = "Cancelled";
3400
- return JobStatus2;
3401
- })(JobStatus || {});
3402
- var ServiceType = /* @__PURE__ */ ((ServiceType2) => {
3403
- ServiceType2[ServiceType2["TextGeneration"] = 0] = "TextGeneration";
3404
- ServiceType2[ServiceType2["ImageGeneration"] = 1] = "ImageGeneration";
3405
- ServiceType2[ServiceType2["DataAnalysis"] = 2] = "DataAnalysis";
3406
- ServiceType2[ServiceType2["CodeGeneration"] = 3] = "CodeGeneration";
3407
- ServiceType2[ServiceType2["Translation"] = 4] = "Translation";
3408
- ServiceType2[ServiceType2["Custom"] = 5] = "Custom";
3409
- return ServiceType2;
3410
- })(ServiceType || {});
3411
-
3412
3624
  // src/indexer/client.ts
3413
3625
  var IndexerClientError = class extends Error {
3414
3626
  status;
@@ -3446,6 +3658,7 @@ function toIndexerOffering(raw) {
3446
3658
  priceUsdm: raw.price_usdm,
3447
3659
  slaMinutes: raw.sla_minutes,
3448
3660
  description: raw.description,
3661
+ requirementsSchemaUri: raw.requirements_schema_uri ?? void 0,
3449
3662
  active: raw.active === 1,
3450
3663
  blockNumber: raw.block_number,
3451
3664
  txHash: raw.tx_hash
@@ -3878,6 +4091,7 @@ var HandlerProviderRuntime = class {
3878
4091
  running = false;
3879
4092
  processedJobs = /* @__PURE__ */ new Set();
3880
4093
  failedJobs = /* @__PURE__ */ new Map();
4094
+ schemaCache = /* @__PURE__ */ new Map();
3881
4095
  maxRetries = 3;
3882
4096
  onError;
3883
4097
  constructor(client, agentId, options) {
@@ -3922,8 +4136,8 @@ var HandlerProviderRuntime = class {
3922
4136
  for (const job of jobs) {
3923
4137
  const newKey = `new:${job.jobId}`;
3924
4138
  if (!this.canProcess(newKey)) continue;
3925
- const matchesOffering = offerings.some((o) => o.offeringId === job.offeringId);
3926
- if (!matchesOffering) continue;
4139
+ const offering = offerings.find((o) => o.offeringId === job.offeringId);
4140
+ if (!offering) continue;
3927
4141
  const handler = this.handlers.get(job.offeringId);
3928
4142
  if (!handler) continue;
3929
4143
  try {
@@ -3946,6 +4160,10 @@ var HandlerProviderRuntime = class {
3946
4160
  if (reqData) {
3947
4161
  context.requirements = reqData.requirements;
3948
4162
  }
4163
+ const schemaValidation = await this.validateSchemaRequirements(context, handler, offering);
4164
+ if (!schemaValidation.valid) {
4165
+ throw new Error(`Schema validation failed for job ${job.jobId}: ${schemaValidation.reason}`);
4166
+ }
3949
4167
  if (handler.validateRequirements) {
3950
4168
  const validation = await handler.validateRequirements(context);
3951
4169
  if (!validation.valid) {
@@ -3966,6 +4184,10 @@ var HandlerProviderRuntime = class {
3966
4184
  }
3967
4185
  }
3968
4186
  async checkInProgressJobs() {
4187
+ const offerings = await this.indexer.getOfferings({ agentId: Number(this.agentId) });
4188
+ const offeringsById = new Map(
4189
+ offerings.map((offering) => [offering.offeringId, offering])
4190
+ );
3969
4191
  const inProgressJobs = await this.indexer.getJobs({
3970
4192
  status: 2,
3971
4193
  agentId: Number(this.agentId)
@@ -3975,6 +4197,7 @@ var HandlerProviderRuntime = class {
3975
4197
  if (!this.canProcess(deliverKey)) continue;
3976
4198
  const handler = this.handlers.get(job.offeringId);
3977
4199
  if (!handler) continue;
4200
+ const offering = offeringsById.get(job.offeringId);
3978
4201
  try {
3979
4202
  if (job.slaMinutes === null) {
3980
4203
  throw new Error(`Job ${job.jobId} is missing slaMinutes`);
@@ -3995,6 +4218,14 @@ var HandlerProviderRuntime = class {
3995
4218
  if (reqData) {
3996
4219
  context.requirements = reqData.requirements;
3997
4220
  }
4221
+ const schemaValidation = await this.validateSchemaRequirements(context, handler, offering);
4222
+ if (!schemaValidation.valid) {
4223
+ this.markFailed(
4224
+ deliverKey,
4225
+ new Error(`Schema validation failed for job ${job.jobId}: ${schemaValidation.reason}`)
4226
+ );
4227
+ continue;
4228
+ }
3998
4229
  if (handler.validateRequirements) {
3999
4230
  const validation = await handler.validateRequirements(context);
4000
4231
  if (!validation.valid) {
@@ -4088,6 +4319,75 @@ var HandlerProviderRuntime = class {
4088
4319
  this.onError?.(new Error(`Giving up on ${key} after ${retries} failed attempts`));
4089
4320
  }
4090
4321
  }
4322
+ async validateSchemaRequirements(context, handler, offering) {
4323
+ if (offering?.requirementsSchemaUri) {
4324
+ try {
4325
+ const offeringSchema = await this.resolveOfferingSchemaFromUri(offering.requirementsSchemaUri);
4326
+ if (!offeringSchema) {
4327
+ return {
4328
+ valid: false,
4329
+ reason: `Unsupported requirementsSchemaUri format: ${offering.requirementsSchemaUri}`
4330
+ };
4331
+ }
4332
+ return validateAgainstSchema(context.requirements, offeringSchema.serviceRequirements);
4333
+ } catch (error) {
4334
+ return {
4335
+ valid: false,
4336
+ reason: error instanceof Error ? error.message : String(error)
4337
+ };
4338
+ }
4339
+ }
4340
+ const defaultSchema = offering ? DEFAULT_SCHEMAS[offering.serviceType] : void 0;
4341
+ const schema = handler.schema ?? defaultSchema;
4342
+ if (!schema) return { valid: true };
4343
+ return validateAgainstSchema(context.requirements, schema.serviceRequirements);
4344
+ }
4345
+ async resolveOfferingSchemaFromUri(schemaUri) {
4346
+ const cached = this.schemaCache.get(schemaUri);
4347
+ if (cached !== void 0) {
4348
+ return cached;
4349
+ }
4350
+ let schemaPayload;
4351
+ if (schemaUri.startsWith("data:")) {
4352
+ schemaPayload = this.parseDataUriJson(schemaUri);
4353
+ } else if (schemaUri.startsWith("http://") || schemaUri.startsWith("https://")) {
4354
+ const response = await fetch(schemaUri);
4355
+ if (!response.ok) {
4356
+ throw new Error(`Failed to fetch requirements schema URI (${response.status} ${response.statusText})`);
4357
+ }
4358
+ schemaPayload = await response.json();
4359
+ } else if (schemaUri.trim().startsWith("{")) {
4360
+ schemaPayload = JSON.parse(schemaUri);
4361
+ } else {
4362
+ this.schemaCache.set(schemaUri, null);
4363
+ return null;
4364
+ }
4365
+ if (!this.isOfferingSchema(schemaPayload)) {
4366
+ throw new Error("requirementsSchemaUri did not resolve to a valid OfferingSchema document");
4367
+ }
4368
+ this.schemaCache.set(schemaUri, schemaPayload);
4369
+ return schemaPayload;
4370
+ }
4371
+ parseDataUriJson(dataUri) {
4372
+ const commaIndex = dataUri.indexOf(",");
4373
+ if (commaIndex < 0) {
4374
+ throw new Error("Invalid data URI schema format");
4375
+ }
4376
+ const metadata = dataUri.slice(5, commaIndex);
4377
+ const payload = dataUri.slice(commaIndex + 1);
4378
+ const decoded = metadata.includes(";base64") ? new TextDecoder().decode(Uint8Array.from(atob(payload), (char) => char.charCodeAt(0))) : decodeURIComponent(payload);
4379
+ return JSON.parse(decoded);
4380
+ }
4381
+ isOfferingSchema(value) {
4382
+ if (typeof value !== "object" || value === null) return false;
4383
+ const candidate = value;
4384
+ return typeof candidate.version === "number" && this.isJsonSchema(candidate.serviceRequirements) && this.isJsonSchema(candidate.deliverableRequirements);
4385
+ }
4386
+ isJsonSchema(value) {
4387
+ if (typeof value !== "object" || value === null) return false;
4388
+ const candidate = value;
4389
+ return candidate.type === "object" && typeof candidate.properties === "object" && candidate.properties !== null;
4390
+ }
4091
4391
  getRemainingSlaMs(createdAt, acceptedAt, slaMinutes) {
4092
4392
  const start = acceptedAt ?? createdAt;
4093
4393
  if (start === null) return null;
@@ -5470,6 +5770,7 @@ export {
5470
5770
  BPS_DENOMINATOR,
5471
5771
  BuyerRuntime,
5472
5772
  DEFAULT_INDEXER_URLS,
5773
+ DEFAULT_SCHEMAS,
5473
5774
  EVALUATION_TIMEOUT,
5474
5775
  FEE_BPS,
5475
5776
  HandlerProviderRuntime,
@@ -5543,6 +5844,7 @@ export {
5543
5844
  submitDeliverable,
5544
5845
  testnetERC8004,
5545
5846
  updateOffering,
5847
+ validateAgainstSchema,
5546
5848
  verifyContentHash
5547
5849
  };
5548
5850
  //# sourceMappingURL=index.js.map