sigil
All patterns
advancedcookbook

Always-on Twitter agent

An autonomous AI account that posts, replies, and survives forever.

Persistent Agent (`0x0820`) spawns a Docker container in a TEE with its own DKMS-derived wallet, encrypted memory on DA, and heartbeat-driven revival. The agent posts to X via HTTP precompile, processes replies via LLM, and pays for its own gas via RitualWallet — fully autonomous, no off-chain keeper.

Architecture


   ┌────────────────────┐        heartbeat       ┌───────────────────┐
   │ AgentHeartbeat     │ ◀──────────────────── │ Persistent Agent  │
   │ 0xEF505E…3aCa      │  every 150 blocks      │ 0x0820 (Docker)   │
   └────────────────────┘                        │ in TEE            │
            │                                     │                   │
            │ if missed →                        │ ┌───────────────┐ │
            │ revive from DA                     │ │ DKMS-derived  │ │
            ▼                                     │ │ wallet (own)  │ │
   ┌────────────────────┐                        │ └───────────────┘ │
   │ Spawn from CID     │                        │                   │
   │ (encrypted memory) │                        │ HTTP → X API      │
   └────────────────────┘                        │ LLM → replies     │
                                                  └───────────────────┘

Code

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {PrecompileConsumer} from "./utils/PrecompileConsumer.sol";

contract AlwaysOnAgent is PrecompileConsumer {
    address constant PERSISTENT_AGENT = address(0x820);
    address constant ASYNC_DELIVERY = 0x5A16214fF555848411544b005f7Ac063742f39F6;
    address constant HEARTBEAT      = 0xEF505E801f1Db392B5289690E2ffc20e840A3aCa;

    bytes32 public agentCid;     // restoration point on DA
    address public agentWallet;  // DKMS-derived address

    function spawn(address executor, bytes32 reviveCid) external {
        // soulRef: identity + system prompt
        // memoryRef: append-only event log
        bytes memory encoded = abi.encode(
            executor,
            soulStorageRef(),
            memoryStorageRef(),
            reviveCid,                 // 0x0...0 = fresh spawn, else revive
            "ritual/sigil-agent:latest",
            uint256(150_000),          // ttl in blocks
            address(this),
            this.onPersistentAgentResult.selector,
            // ... 17 more fields per spec
        );
        (bool ok, bytes memory result) = PERSISTENT_AGENT.call(encoded);
        require(ok, "persistent agent failed");
    }

    function onPersistentAgentResult(bytes32 jobId, bytes calldata result) external {
        require(msg.sender == ASYNC_DELIVERY, "unauth");
        // Agent completed an action cycle — extract new memory CID
        (bytes32 newCid, address derivedWallet) =
            abi.decode(result, (bytes32, address));
        agentCid = newCid;        // store for next revival
        agentWallet = derivedWallet;
    }

    function soulStorageRef() internal pure returns (bytes memory) {
        // ('hf', 'taufik/sigil-soul/agent.jsonl', 'HF_TOKEN')
        return abi.encode("hf", "taufik/sigil-soul/agent.jsonl", "HF_TOKEN");
    }

    function memoryStorageRef() internal pure returns (bytes memory) {
        return abi.encode("gcs", "agents/sigil/memory.jsonl", "GCS_CREDS");
    }
}