@elisym/cli 0.28.0 → 0.29.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.js +84 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -5318,7 +5318,15 @@ var AgentRuntime = class {
|
|
|
5318
5318
|
return;
|
|
5319
5319
|
}
|
|
5320
5320
|
this.ledger.recordDeliveredContent(job.jobId, deliveredContent);
|
|
5321
|
+
delegatedPull.chargedSubunits = this.resolveMeteredCharge(
|
|
5322
|
+
job,
|
|
5323
|
+
matched,
|
|
5324
|
+
delegatedPull.priceSubunits,
|
|
5325
|
+
output.chargeSubunits,
|
|
5326
|
+
log
|
|
5327
|
+
);
|
|
5321
5328
|
const outcome = await this.executeDelegatedPull(job, delegatedPull, log);
|
|
5329
|
+
netAmount = Number(delegatedPull.chargedSubunits ?? delegatedPull.priceSubunits);
|
|
5322
5330
|
if (outcome === "dead") {
|
|
5323
5331
|
this.ledger.markFailed(job.jobId);
|
|
5324
5332
|
await this.transport.sendFeedback(job, {
|
|
@@ -5353,6 +5361,59 @@ var AgentRuntime = class {
|
|
|
5353
5361
|
}
|
|
5354
5362
|
}
|
|
5355
5363
|
}
|
|
5364
|
+
/**
|
|
5365
|
+
* Decide what a delegated pull should move, for a skill that opted into
|
|
5366
|
+
* metered pricing.
|
|
5367
|
+
*
|
|
5368
|
+
* The skill reports a figure through `ELISYM_CHARGE_FILE`; we clamp it into
|
|
5369
|
+
* the range the CARD published, so the buyer can never be billed above the
|
|
5370
|
+
* `price` they approved nor below the `min` they were quoted. A skill that is
|
|
5371
|
+
* not metered, or one that reported nothing usable, charges the ceiling -
|
|
5372
|
+
* that is exactly today's behaviour and exactly what the buyer consented to,
|
|
5373
|
+
* so a provider-side bug costs the provider nothing and surprises no one.
|
|
5374
|
+
* The "reported nothing" case is logged loudly because on a metered skill it
|
|
5375
|
+
* means the script is broken, not that the job was cheap.
|
|
5376
|
+
*
|
|
5377
|
+
* Returns `undefined` to mean "charge the ceiling", which is what every
|
|
5378
|
+
* caller already falls back to.
|
|
5379
|
+
*/
|
|
5380
|
+
resolveMeteredCharge(job, skill, ceiling, reported, log) {
|
|
5381
|
+
const floor = skill?.meteredMinSubunits;
|
|
5382
|
+
if (floor === void 0) {
|
|
5383
|
+
return void 0;
|
|
5384
|
+
}
|
|
5385
|
+
if (typeof floor !== "bigint" || floor <= 0n) {
|
|
5386
|
+
log(
|
|
5387
|
+
`[${job.jobId.slice(0, 8)}] Metered skill has an unusable floor (${String(floor)}) - billing the ${ceiling} ceiling.`
|
|
5388
|
+
);
|
|
5389
|
+
return void 0;
|
|
5390
|
+
}
|
|
5391
|
+
if (reported !== void 0 && typeof reported !== "bigint") {
|
|
5392
|
+
log(
|
|
5393
|
+
`[${job.jobId.slice(0, 8)}] Metered skill reported a non-bigint charge - billing the ${ceiling} ceiling.`
|
|
5394
|
+
);
|
|
5395
|
+
return void 0;
|
|
5396
|
+
}
|
|
5397
|
+
if (reported === void 0) {
|
|
5398
|
+
log(
|
|
5399
|
+
`[${job.jobId.slice(0, 8)}] Metered skill reported no charge - billing the ${ceiling} ceiling. The script should write its cost in subunits to ELISYM_CHARGE_FILE.`
|
|
5400
|
+
);
|
|
5401
|
+
return void 0;
|
|
5402
|
+
}
|
|
5403
|
+
let charged = reported;
|
|
5404
|
+
if (charged < floor) {
|
|
5405
|
+
charged = floor;
|
|
5406
|
+
}
|
|
5407
|
+
if (charged > ceiling) {
|
|
5408
|
+
charged = ceiling;
|
|
5409
|
+
}
|
|
5410
|
+
if (charged !== reported) {
|
|
5411
|
+
log(
|
|
5412
|
+
`[${job.jobId.slice(0, 8)}] Metered charge ${reported} clamped to ${charged} (published range ${floor}..${ceiling}).`
|
|
5413
|
+
);
|
|
5414
|
+
}
|
|
5415
|
+
return charged;
|
|
5416
|
+
}
|
|
5356
5417
|
reserveDelegation(owner, amount) {
|
|
5357
5418
|
this.delegationInFlight.set(owner, (this.delegationInFlight.get(owner) ?? 0n) + amount);
|
|
5358
5419
|
}
|
|
@@ -5484,13 +5545,13 @@ var AgentRuntime = class {
|
|
|
5484
5545
|
"the delegation account balance is below this skill price."
|
|
5485
5546
|
);
|
|
5486
5547
|
}
|
|
5487
|
-
this.reserveDelegation(request.owner, priceSubunits);
|
|
5488
5548
|
log(
|
|
5489
5549
|
`[${job.jobId.slice(0, 8)}] Delegated pre-check ok: ${formatAssetAmount(
|
|
5490
5550
|
delegationAsset,
|
|
5491
5551
|
priceSubunits
|
|
5492
5552
|
)} from ${request.owner.slice(0, 8)}... (pull after work)`
|
|
5493
5553
|
);
|
|
5554
|
+
this.reserveDelegation(request.owner, priceSubunits);
|
|
5494
5555
|
return { request, delegateSigner, ownerAta, priceSubunits, rpc, network };
|
|
5495
5556
|
}
|
|
5496
5557
|
/**
|
|
@@ -5504,11 +5565,12 @@ var AgentRuntime = class {
|
|
|
5504
5565
|
throw new Error("Solana address not configured");
|
|
5505
5566
|
}
|
|
5506
5567
|
const destinationAta = await deriveOwnerDelegationAta(this.config.solanaAddress, ctx.network);
|
|
5568
|
+
const amount = ctx.chargedSubunits ?? ctx.priceSubunits;
|
|
5507
5569
|
const instructions = await buildDelegatedTransfer({
|
|
5508
5570
|
delegate: ctx.delegateSigner,
|
|
5509
5571
|
source: ctx.ownerAta,
|
|
5510
5572
|
destination: destinationAta,
|
|
5511
|
-
amount
|
|
5573
|
+
amount,
|
|
5512
5574
|
network: ctx.network,
|
|
5513
5575
|
// Idempotent create (delegate-paid rent) so a fresh provider wallet's
|
|
5514
5576
|
// first delegated job does not dead-letter on a missing ATA.
|
|
@@ -5521,11 +5583,13 @@ var AgentRuntime = class {
|
|
|
5521
5583
|
job.jobId,
|
|
5522
5584
|
pull.signature,
|
|
5523
5585
|
Number(pull.lastValidBlockHeight),
|
|
5524
|
-
Number(
|
|
5586
|
+
Number(amount)
|
|
5525
5587
|
);
|
|
5526
5588
|
ctx.pullSignature = pull.signature;
|
|
5527
5589
|
const outcome = await sendConfirmToTerminal(ctx.rpc, pull, {});
|
|
5528
|
-
log(
|
|
5590
|
+
log(
|
|
5591
|
+
`[${job.jobId.slice(0, 8)}] Delegated pull ${outcome}: ${amount} subunits, ${pull.signature.slice(0, 16)}...`
|
|
5592
|
+
);
|
|
5529
5593
|
return outcome;
|
|
5530
5594
|
}
|
|
5531
5595
|
/**
|
|
@@ -7025,6 +7089,9 @@ function buildCliSkill(parsed, entryPath, scriptEnv, network) {
|
|
|
7025
7089
|
if (parsed.executionTimeoutSecs !== void 0) {
|
|
7026
7090
|
skill.executionTimeoutSecs = parsed.executionTimeoutSecs;
|
|
7027
7091
|
}
|
|
7092
|
+
if (parsed.meteredMinSubunits !== void 0) {
|
|
7093
|
+
skill.meteredMinSubunits = parsed.meteredMinSubunits;
|
|
7094
|
+
}
|
|
7028
7095
|
if (parsed.delegation) {
|
|
7029
7096
|
skill.delegation = parsed.delegation;
|
|
7030
7097
|
}
|
|
@@ -8926,6 +8993,19 @@ function buildCapabilityCard(skill, inputs) {
|
|
|
8926
8993
|
// exists (delegatePubkey resolved). The pubkey is injected here, never
|
|
8927
8994
|
// taken from the SKILL.md - the operator declares only the cap/mechanism.
|
|
8928
8995
|
...skill.delegation && delegatePubkey ? { delegation: { ...skill.delegation, delegate_pubkey: delegatePubkey } } : {},
|
|
8996
|
+
// Metered descriptor. The gate is COMPOUND on purpose and both halves
|
|
8997
|
+
// matter: `delegatePubkey` is agent-wide (hoisted when ANY skill declares
|
|
8998
|
+
// delegation), while `skill.delegation` is per-skill. Stamping on the agent
|
|
8999
|
+
// half alone would advertise pay-per-use on a card that ships no delegation
|
|
9000
|
+
// block - the buyer would be told "you pay for what you use" while the only
|
|
9001
|
+
// rail they can reach charges the ceiling. The loader already refuses
|
|
9002
|
+
// `metered` without `delegation`, so this is defence in depth.
|
|
9003
|
+
// `solanaAddress` is in the gate too: without it the `payment` block below
|
|
9004
|
+
// is omitted, and a metered descriptor with no price to clamp against is
|
|
9005
|
+
// incoherent by construction - the write-side mirror would then reject the
|
|
9006
|
+
// card with a misleading "malformed metered descriptor" instead of the real
|
|
9007
|
+
// reason, which is that the agent has no payment address.
|
|
9008
|
+
...skill.meteredMinSubunits !== void 0 && skill.delegation && delegatePubkey && solanaAddress ? { metered: { min_subunits: skill.meteredMinSubunits.toString() } } : {},
|
|
8929
9009
|
// On-chain promise (`mode: onchain`): the network is stamped from the
|
|
8930
9010
|
// agent's wallet here, never from SKILL.md, so a capability copied between
|
|
8931
9011
|
// a devnet and a mainnet agent cannot advertise the wrong chain.
|