lucidAGENTS
Buy

Schedule paid calls

Lease recurring Lucid-profile invocations without silently duplicating work or spend.

@lucid-agents/scheduler polls due jobs, claims a lease, discovers a remote Lucid card, and invokes one entrypoint with a stable idempotency key. Delivery is at least once: a worker can finish the remote paid call and die before it stores local success.

The package uses Lucid /entrypoints calls through @lucid-agents/a2a; this is not an official A2A v1 scheduling protocol.

Create the runtime and job

const buyer = await createAgent({ name: 'scheduled-buyer', version: '1.0.0' })
  .use(wallets({ config: walletsFromEnv() }))
  .use(a2a())
  .use(payments({ config: paymentsFromEnv() }))
  .use(
    scheduler({
      store: durableSchedulerStore,
      leaseMs: 120_000,
      defaultMaxRetries: 3,
      defaultConcurrency: 5,
    })
  )
  .build();

const { hire, job } = await buyer.scheduler.createHire({
  agentCardUrl: 'https://reports.example',
  entrypointKey: 'daily-report',
  schedule: { kind: 'interval', everyMs: 24 * 60 * 60_000 },
  jobInput: { accountId: 'acct-123' },
  idempotencyKey: 'daily-report-acct-123-generation-v1',
});

The interval is due immediately. After success, the next occurrence is scheduled from the tick time plus everyMs; there is no cron/time-zone/misfire or missed-interval catch-up behavior.

Run a long-lived worker

const worker = createSchedulerWorker(buyer.scheduler, 5_000);
await buyer.scheduler.recoverExpiredLeases();
worker.start();

async function shutdown() {
  worker.stop();
  await buyer.close();
}

The convenience loop can overlap tick() calls when work exceeds the polling interval and does not recover leases automatically after startup. A production orchestrator should call recovery periodically and rely on an atomic store.

Lease and duplicate boundary

There is no lease heartbeat in this release. Set leaseMs longer than card refresh, payment negotiation, remote execution, and final persistence. A recovery worker can otherwise claim an expired job while the first worker is still calling.

The same occurrence idempotency key is the final duplicate defense. The remote Lucid invoke service must retain that key durably longer than scheduler retry and lease-recovery windows, and the remote handler must pass it to downstream effects.

The package ships only createMemoryStore(). Implement SchedulerStore with atomic claimJob(), durable writes, expired-lease discovery, tenant isolation, and stale-worker protection. Do not assume SQLite/Postgres support exists without your adapter.

Payment and retry behavior

Budget is evaluated when the occurrence runs, not reserved when the hire is created. A future price/payee/network change can make a previously valid hire fail policy. Use the same recipient and total policy as an interactive buyer, backed by a shared payment store.

Any thrown remote invocation error is retried with exponential jittered backoff up to maxRetries; the scheduler does not classify a permanent 4xx policy or configuration failure. Alert and pause the hire when repeated errors are not transient.

Evidence and tests

Record hire, job, occurrence/idempotency key, lease owner/expiry, target card version/interface, entrypoint, amount/payee/network, external settlement, remote run ID, attempts, and terminal job state.

Test concurrent workers, overlapping ticks, lease expiry during a slow call, crash after remote success, price/payee drift, policy denial, card refresh/SSRF, store disconnect, retry exhaustion, pause/resume/cancel, restart recovery, and same-key target replay.

See the complete scheduler reference, durable storage, and retry contract.

On this page