AgentOS 2026.8.13
A crypto-skill release. AgentOS can now launch a token on pools.fun and manage the creator fees afterwards, from a skill that keeps reading and signing in separate scripts and hashes the plan it quotes so the transaction that broadcasts is provably the one you approved.
Added
-
A bundled
poolsdotfun-token-launcherskill. Launches a token through the pools.funPartyFactoryon Robinhood Chain (4663) and manages creator fees on thePartyLockerafterwards. There is no RPC to configure — the chain is fixed and the endpoint is built in, so the only thing to set isPOOLSFUN_PRIVATE_KEY.-
A launch is one irreversible transaction, and the skill says so before sending it. It CREATE2-deploys a fixed-supply ERC20 — 1,000,000,000 tokens, no owner, no mint function, no transfer hooks — opens a SushiSwap V3 pool at the 1% fee tier, and mints the entire supply as a single-sided full-range position whose LP NFT goes to the locker permanently. The launcher never holds the LP.
-
Reading and signing are separate scripts, not separate flags.
pools_read.pyquotes cost and opening price, inspects pool state, simulates a launch and mines a launch salt using only a--fromaddress — it cannot sign even when the key is set.pools_write.pyis the only script that can. A capability boundary that is a different file cannot be crossed by a mistyped argument. -
The launch plan is hashed. What you were quoted and what gets broadcast are checked against each other, so a plan that renders one price and sends another does not exist as a state.
-
PINATA_JWTis optional and needed only to attach a token image. It is deliberately not declared inrequires.env, because that field is a hard eligibility gate — listing it would have made the whole skill unavailable on any machine where a logo was never configured, including for the launches that do not want one.
-
-
The launcher finds a logo the user attached in chat. This took two passes to get right, and the first one was worse than nothing. Chat attachments exist in two shapes — inline base64 in the transcript for anything under ~2 MB, which is most logos, or staged content-addressed on disk under a sha256 name with no extension — and the skill originally scanned only the media directory and returned the newest file by mtime. In a live session that meant offering a three-day-old blob from an unrelated session as the token's logo: mtime cannot distinguish "the image the user just sent" from "some image", and a token's identity is immutable once the transaction lands.
-
The transcript is now the authority. Message rows carry their own timestamp and their session, so
find-imagereads both attachment shapes newest-message-first, materializes the bytes to a real file — the step that was missing, since the image was always available, just not anywhere--imagecould reach — and prints the exactlaunch --image …line. It flags any candidate older than 15 minutes or belonging to another session, and tells the agent to open the file and confirm before pinning it. The media-directory scan survives only as a hint when the transcript yields nothing, labelled as probably-not-it. -
An empty result is an answer, not a failed search. When there is no image, the command says so and lists the alternatives in preference order — ask the user for a real path, use
--metadata-urifor an already-hosted logo, or launch without one — noting that a logo cannot be added afterwards. The previous documentation claimed an attached image "lands on disk", which is what sent the agent hunting for a file that had never been written.
-
Security
Found and closed in review before the skill shipped:
-
A private key with one mistyped character was printed in full to stderr. Validation checked length and prefix and then let
int(key, 16)raise — andValueErrorembeds the offending string, which the top-level handler prints straight into the agent transcript. The alphabet is now validated first, and the error carries no key material. -
The "To execute" replay line was command-injectable. It quoted values with double quotes, which do not neuter
$(…)or backticks, so a token name — attacker-influenced text — of the formX$(cmd)produced a suggested command that rancmd. Quoting now goes throughshlex.quote. -
A failed RPC read of the factory's paused switch was read as "not paused".
read_factory_statemaps a failed call toNone, andif state["paused"]is false forNone, so a hiccup could launch into a paused factory and burn the full ~6.1M gas. The guard now fails closed on an unknown switch and preflight reports "unknown" rather than "no". -
Value-taking flags silently accepted their own absence. A bare
--slippage-bpsbecameint(True) == 1, so asking for looser slippage gave the tightest possible setting;--imagewith no value became aTruesentinel that anisinstancecheck discarded, silently launching an image-less token against a contract the docs state three times.opt_str/opt_int/opt_floatare now the single place that sentinel is rejected.--slippage-bpsis also clamped to 0…9999, since a negative value setdevBuyMinOutabove the exact simulated fill and produced a plan that hashed normally and then reverted on-chain. -
Three error-message selectors were wrong, and one of them reported
ZeroAmount()as "your dev buy is too large". The table is now keyed by signature with selectors computed at import, which makes the class of bug impossible, and the selftest iterates the table rather than a hand-copied subset.
The skill ships with 200 offline self-test checks, pinning launch calldata byte-for-byte against a reference transaction, every custom error selector, plan-hash sensitivity, the Pinata-optional matrix, the capability separation, and the attachment-resolution behavior against a miniature sessions.db.
Full changelog: v2026.8.12...v2026.8.13