yukazakiri/lepton-agent
A lightweight AI agent toolkit for Laravel. Lepton Agent connects your app to LLM providers and offers simple abstractions for tools, conversation memory, and multi-step agent workflows, so you can ship chat and automation features with minimal configuration.
This is a new assessment — no prior baseline — so treat v3.0.0-rc.1 as the first look at a package that just changed shape substantially.
Before you composer require, verify the floor. The package requires PHP 8.5+ (it uses the native RSA-OAEP SHA-256 digest API) plus ext-curl and ext-openssl. PHP 8.4 is explicitly out.
php -v
php -m | grep -E '^(curl|openssl)$'
If your app or CI image is on 8.4, stop here — there is no compatibility shim. Options are: upgrade the runtime, or run this package in a dedicated service/queue worker rather than in the main Laravel app.
First three steps once installed:
/v1/w3s/... read paths.lepton:transfer --estimate) to hit the real estimator and check feeLevel handling and amount formatting without signing anything.First real use case: a sandbox transfer with all three new required inputs — --idempotency-key (a UUID v4), --wallet-set, and --account-type. That single command is the fastest way to confirm your entity secret, signing payload, and REST path are all correct, because every one of those is a place the old version was silently wrong.
Type-hint capability interfaces, not the concrete driver. WalletGateway keeps its original four methods, and that is your stable seam. For anything else — wallet management, transfer observation, estimation, webhook verification, custom transport — depend on the optional interface (WalletManagementGateway, TransferObserver, TransferEstimator, WebhookVerifier, JsonTransport) and be prepared for a driver not to implement it:
try {
$estimate = $this->estimator->estimate($intent);
} catch (UnsupportedOperation $e) {
// degrade explicitly — hide the fee preview, don't show zero
}
Generate idempotency keys at the edge, not in the retry loop. A UUID v4 must be created when the intent is created (HTTP request, queued job dispatch), persisted alongside the intent row, and reused on every retry. Generating inside a try/catch retry defeats the entire mechanism and, since the key is now mandatory, will just throw on a malformed value.
Keep two money pipelines. Arc native USDC is 18 dp; ERC-20 USDC is 6 dp. The package refuses to sum them, and Amounts rejects overflow and bad scale. In your schema, don't have one amount column that receives both — keep them separate and convert only at presentation. In tests, use the package's own Amounts when building fixtures so you inherit the validation.
Verify webhooks on the raw body, before middleware touches it. The fix here is important: verification uses GET /v2/notifications/publicKey/{keyId} and ECDSA_SHA_256 over the original raw bytes — never the Entity Secret RSA key. That means verification must run ahead of anything that re-encodes the request (TrimStrings, ConvertEmptyStringsToNull, or a $request->all()-based re-serialise). Cache the public key by keyId; it's a network call on the hot path otherwise.
Bind JsonTransport in tests. It's injectable specifically so you can swap in a fake for offline unit tests and add logging/tracing in production. Prefer that over mocking the gateway — the gateway has capability-conditional behaviour and mocks get brittle fast.
Use the facade for artisan-style code, DI everywhere else. Lepton::transfer() existed in intent but was missing from the facade; it's there now. In application code, inject WalletGateway — the facade is a service locator and makes feature-detection awkward.
Treat terminal states as driver-specific. Transfer-state mapping was fixed in CircleCliGateway, which tells you mapping is not universal. If you branch on status, keep the mapping behind a small adapter in your own code rather than checking enum cases from the gateway.
Str::random(), not a hash. Validate at the boundary so you get a clean domain error instead of an SDK exception mid-flight.UnsupportedOperation to a zero balance. The notes are explicit that this replaces the old placeholder behaviour. Surface it as a capability flag or domain error; a zero balance is a wrong answer that looks like a right one.feeLevel placement changed. If you build request payloads by hand anywhere, compare them against the SDK's output — the old placement against the Circle OpenAPI contract was incorrect, and the old behaviour may have been silently defaulting rather than failing./v1/w3s/... and mutations are /developer/... — mixing those up is the easiest way to misread a 404.How can I help you explore Laravel packages today?