---
title: "Build Autonomous AI Agents in OpenClaw (with TypeScript Examples)"
description: "A practical guide to designing, scheduling, and running autonomous AI agents in OpenClaw-email triage + draft replies, hourly tech-update-to-tweet generation for X.com delivered to Telegram, code snippet generation, and Docker maintenance-plus a real TypeScript agent example."
date: "2026-02-13"
author: "Jayesh Jain"
category: "Artificial Intelligence"
tags: ["OpenClaw", "AI Agents", "Automation", "LLM"]
keywords: "openclaw, openclaw autonomous AI agents,openclaw ai agents, ai agents, AI automation,openclaw assistant"
featuredImage: "/blog/build-autonomous-ai-agents-openclaw.png"
cta: "Want to ship agent automations fast?"
ctaDescription: "I can help you design and deploy OpenClaw agents for support, ops, sales, and content workflows-securely and maintainably."
---

# Build Autonomous AI Agents in OpenClaw (with TypeScript Examples)

“Autonomous AI agents” sounds like a buzzword until you ship one that quietly saves you hours every week. The core loop is straightforward:

- **Watch** for something important (new email, a tech update, a failing container)
- **Think** (classify, plan, decide what to do next)
- **Act** (draft a reply, suggest a post, generate code, propose a fix)
- **Learn/remember** (store decisions, avoid repeat work, keep an audit trail)

OpenClaw is a strong fit for this because it’s built like a personal operations layer: it can schedule recurring tasks (**cron**), react to events (**hooks/webhooks**), and deliver output to real channels (**Telegram/WhatsApp/etc.**).

---

## What you’ll build in this post

1) A dedicated **Trends Agent** with its own workspace + **SOUL.md**
2) An hourly cron job that produces: **5 tech updates + sources + 3 tweet drafts + optional thread**
3) Telegram delivery (draft-only; no auto-posting)

---

## What “autonomous” should mean (in production)

Autonomy is a spectrum. In real systems, the best agents are usually:

1. **Semi-autonomous by default**: the agent drafts actions and asks for approval.
2. **Fully autonomous only for low-risk tasks**: housekeeping, summaries, formatting, routine checks.
3. **Auditable**: every action is logged and reversible when possible.
4. **Bounded**: strict scope, explicit tools, and clear stop conditions.

A useful mental model is: **agent = policy + tools + schedule + guardrails**.

---

## Agent set #1: Email agent (read → draft response)

### Goal
- Detect new emails (support inquiries, sales leads, vendor messages).
- Classify urgency + topic.
- Draft a reply in your voice with the right context.

### Guardrails (recommended)
- Start with **draft-only**. Don’t auto-send on day one.
- Don’t open attachments automatically.
- For password resets, billing changes, refunds, legal requests: always escalate.

---

## Agent set #2: Latest tech updates → tweet suggestions (hourly)

### Goal
- Track **latest tech updates** (AI, developer tooling, cloud, security, OSS releases).
- Turn them into **tweet drafts** for X.com.
- Deliver drafts to **Telegram** for quick review.

### Guardrails
- No confidential info.
- Avoid unverifiable claims; include sources.
- Draft-only: do not auto-post.

---

## Agent set #3: Code snippet agent (prompt → generate → validate)

### Goal
- Generate correct code snippets (TypeScript utilities, API handlers, Dockerfiles).
- Provide usage examples and edge cases.

---

## Agent set #4: Docker ops agent (observe → report → optionally fix)

### Goal
- Identify disk usage (images, volumes, logs).
- Detect unhealthy containers and repeated restarts.
- Propose safe cleanups; require approval for destructive actions.

---

## Step-by-step: Create a dedicated “Trends Agent” workspace (with its own SOUL.md)

If you want your hourly trend/tweet automation to have a *different personality and scope* than your main assistant, set up a separate agent workspace.

### Step 1 - Create a new agent workspace folder

Example (inside your existing OpenClaw workspace mount):

```bash
mkdir -p /mnt/openclaw-workspace/trends-agent/memory
cp /mnt/openclaw-workspace/AGENTS.md /mnt/openclaw-workspace/trends-agent/AGENTS.md
cp /mnt/openclaw-workspace/USER.md /mnt/openclaw-workspace/trends-agent/USER.md
cp /mnt/openclaw-workspace/TOOLS.md /mnt/openclaw-workspace/trends-agent/TOOLS.md
```

### Step 2 - Add a Trends-focused SOUL.md

Create:

```bash
nano /mnt/openclaw-workspace/trends-agent/SOUL.md
```

Keep it focused. Example rules:
- mission: find latest tech updates + draft X.com posts
- must include sources
- draft-only (never auto-post)

---

## What these agent files are for (SOUL.md, AGENTS.md, USER.md, etc.)

OpenClaw agents don’t “remember” anything unless it’s written down. These files are how you give an agent a stable identity, rules, and continuity.

### **SOUL.md** - persona + behavior rules

The agent’s *operating system*: tone, boundaries, priorities, what to do/avoid.

### **AGENTS.md** - how the workspace should be run

A workspace playbook: what to read first, safety conventions, and how to operate day-to-day.

### **TOOLS.md** - environment-specific notes

Local facts needed to operate reliably (paths, ports, hostnames). Keep secrets out unless you explicitly want them stored.

### **USER.md** - the human profile (how to help you)

Your preferences (tone, timezone, style) and current goals.

### **IDENTITY.md** - name/emoji/avatar metadata

Optional, but helpful when you run multiple agents so it’s obvious who is who.

### **MEMORY.md** - curated long-term memory

High-signal, stable context (decisions, preferences, long-lived facts).

### **memory/YYYY-MM-DD.md** - daily logs / journal

Raw daily notes. Later, promote important items into **MEMORY.md**.

---

## Copy/paste templates for a Trends (X.com) agent workspace

If you want a working example you can paste straight into files, here are minimal templates aligned to the **hourly tech updates → X.com drafts → Telegram delivery** workflow.

### **SOUL.md** (Trends Agent)

```md
# SOUL.md - Trends Agent

Mission:
- Every hour, find credible tech updates and draft X.com tweets.

Rules (non-negotiable):
- Always include sources/links.
- No unverifiable claims. If unsure, say "unclear" or skip.
- Draft-only: never auto-post to X.com.
- Keep tweets <= 280 characters.

Style:
- Practical, builder-focused, slightly opinionated.
- Short sentences. No hype.

Output format:
1) 5 updates + links
2) best pick + why
3) Tweet A/B/C
4) optional 4-tweet thread
```

### **AGENTS.md** (workspace playbook)

```md
# AGENTS.md (Trends workspace)

## Every session
1) Read SOUL.md, USER.md, latest daily memory.
2) Stay in scope: tech updates + X.com drafts only.

## Safety
- Never run destructive commands.
- Never post automatically.
- Prefer reversible actions.
```

### **USER.md** (preferences)

```md
# USER.md

- Name: Jayesh Jain
- Timezone: UTC (or set your local TZ)
- Posting style: crisp, practical, no fluff
- CTA preference: only when relevant ("Read more", "Try it", etc.)
```

### **TOOLS.md** (where/how to deliver)

```md
# TOOLS.md

### Telegram
- Primary destination: telegram:<your_chat_id>

### X.com
- Posting: manual only (drafts delivered to Telegram)
```

### **IDENTITY.md** (optional)

```md
# IDENTITY.md

- Name: Trends Agent
- Creature: Scout
- Vibe: concise, evidence-driven
- Emoji: 🛰️
```

### **MEMORY.md** (curated long-term rules)

```md
# MEMORY.md

## Writing rules
- Avoid hashtags unless essential.
- Prefer one strong insight per tweet.

## Topics to prioritize
- AI agents, developer tooling, cloud infra, security advisories, notable OSS releases.
```

### **memory/YYYY-MM-DD.md** (daily log example)

```md
# 2026-02-13

- Picked: <best update>
- Tweet chosen: Option B
- Notes: audience liked practical benchmark angle
```

---

## Step-by-step: Register the Trends Agent in OpenClaw

Edit **~/.openclaw/openclaw.json** and add a second agent entry:

```json5
{
  agents: {
    list: [
      { id: "main", default: true, workspace: "/mnt/openclaw-workspace" },
      { id: "trends", name: "Trends Agent", workspace: "/mnt/openclaw-workspace/trends-agent" }
    ]
  }
}
```

Restart the gateway after config changes:

```bash
openclaw gateway restart
```

---

## Step-by-step: Add an hourly “Latest Tech → X.com Tweet Suggestions” cron job (deliver to Telegram)

This section shows the exact cron job setup. It assumes:
- Telegram bot is configured and you’ve paired/allowed your chat ID
- your OpenClaw instance can retrieve web pages (via sandbox browser if you’re in Docker)

### Step 1 - Create the cron job (every 1 hour)

```bash
openclaw cron add \
  --name "Latest tech → X.com tweet suggestions (hourly)" \
  --every-ms 3600000 \
  --session isolated \
  --message "You are my tech news + X.com copy assistant.\n\nRun hourly:\n1) Find 5 latest notable tech updates from the last 24-48 hours (AI + dev tools + cloud + security + OSS).\n2) Include a source link for each.\n3) Pick the best ONE update to tweet about (explain why it’s most interesting/valuable).\n4) Draft 3 tweet options (each <= 280 chars) with different angles: (a) practical takeaway, (b) opinionated take, (c) mini-summary + what to do next.\n5) Provide an optional 4-tweet thread version.\n\nRules:\n- Draft-only. Do NOT post automatically.\n- No confidential info.\n- No unverifiable claims; if unsure, frame as opinion or ask for confirmation.\n- Output in plain text with clear separators." \
  --announce \
  --channel telegram \
  --to "telegram:<telegram_chat_id>"
```

### Step 2 - Verify and test

```bash
openclaw cron list
openclaw cron run <job-id>
openclaw cron runs --id <job-id>
```

### Step 3 - Bind the cron job to the Trends agent

Bind the job to **agentId: "trends"** so it runs with the Trends Agent’s **SOUL.md** and workspace.

---

## TypeScript example: Turn a trend into an X.com post (draft + approval)

Below is a practical TypeScript pattern you can use inside your codebase to generate a tweet (or thread) from a selected trend, **without auto-posting**.

It’s intentionally split into two steps:
1) **Generate** a draft tweet from a trend + source URL
2) **Human approval** before anything is posted to X

### 1) Define the input + output

```ts
type TrendItem = {
  title: string;
  url: string;
  bullets: string[]; // 2-5 key facts
};

type TweetDraft = {
  bestPickWhy: string;
  tweetOptions: {
    style: "practical" | "opinionated" | "summary";
    text: string; // <= 280 chars
  }[];
  thread?: string[]; // each tweet <= 280 chars
};
```

### 2) Generate tweet drafts from a trend (LLM call)

```ts
interface LlmClient {
  complete(opts: {
    system: string;
    user: string;
    temperature?: number;
  }): Promise<string>;
}

const SYSTEM = `
You write X.com drafts for a tech audience.
Rules:
- Output JSON only.
- Every claim must be supported by the provided bullets.
- Each tweet must be <= 280 characters.
- No hashtags unless they add real value.
- Draft-only: do not instruct posting automation.
`.trim();

export async function draftTweetsFromTrend(llm: LlmClient, trend: TrendItem): Promise<TweetDraft> {
  const user = `
Trend:
- Title: ${trend.title}
- Source: ${trend.url}
- Facts:\n${trend.bullets.map((b) => `- ${b}`).join("\n")}

Task:
1) Explain why this is the best trend to tweet today.
2) Draft 3 tweet options (practical/opinionated/summary).
3) Provide an optional 4-tweet thread.

Return JSON matching TweetDraft.
`.trim();

  const raw = await llm.complete({ system: SYSTEM, user, temperature: 0.4 });
  return JSON.parse(raw) as TweetDraft;
}
```

### 3) Approval loop (example)

Your cron job can deliver the drafts to Telegram. You reply with:
- “Use option #2”
- “Rewrite option #1 more technical”

Only after that should you post to X (manual is safest to start).

---

## Common failure mode: no web retrieval (especially in Docker)

If the agent can’t browse/search, it *should* refuse to invent sources.

When running OpenClaw in Docker, the recommended fix is the **sandbox browser**:

- Build the browser sandbox image:

```bash
scripts/sandbox-browser-setup.sh
```

- Enable it:

```json5
{
  agents: {
    defaults: {
      sandbox: { browser: { enabled: true, autoStart: true } }
    }
  }
}
```

- Restart:

```bash
openclaw gateway restart
```

---

## A practical TypeScript example: “Autonomous Email Draft Agent”

Below is a practical TypeScript agent skeleton that demonstrates the pattern:

- fetch work items (new emails),
- run an LLM step to classify + draft,
- output structured results you can route to a chat surface.

> Note: the exact OpenClaw SDK/API surface depends on your deployment. Treat this as an application-level pattern you can run inside an OpenClaw isolated job.

### Types + prompt discipline (structured output)

```ts
// email-agent.ts
type EmailMessage = {
  id: string;
  from: string;
  subject: string;
  receivedAt: string; // ISO
  text: string;
};

type DraftResult = {
  emailId: string;
  urgency: "low" | "medium" | "high";
  category: "sales" | "support" | "billing" | "partnership" | "unknown";
  summaryBullets: string[];
  draftSubject: string;
  draftBody: string;
  questionsForHuman?: string[];
  safeToAutoSend: boolean;
};

const SYSTEM_POLICY = `
You are an assistant that drafts email responses.
Rules:
- Never claim you performed actions you didn't do.
- If the email requests credentials, payment changes, refunds, or legal statements: set safeToAutoSend=false and add questionsForHuman.
- Keep tone professional, concise, helpful.
- Output MUST be valid JSON matching the DraftResult type.
`.trim();

function buildUserPrompt(email: EmailMessage) {
  return `
Email:
From: ${email.from}
Subject: ${email.subject}
ReceivedAt: ${email.receivedAt}

Body:
${email.text}

Task:
1) Classify urgency + category
2) Summarize in 2-5 bullets
3) Produce a reply draft (subject + body)
4) Ask clarifying questions if needed
5) Set safeToAutoSend=false unless it's a trivial acknowledgement.
`.trim();
}
```

### LLM call (provider-agnostic pattern)

```ts
interface LlmClient {
  complete(opts: {
    system: string;
    user: string;
    temperature?: number;
  }): Promise<string>;
}

function safeJsonParse<T>(raw: string): T {
  const first = raw.indexOf("{");
  const last = raw.lastIndexOf("}");
  if (first === -1 || last === -1) throw new Error("No JSON object found.");
  return JSON.parse(raw.slice(first, last + 1));
}

export async function draftRepliesForEmails(opts: {
  llm: LlmClient;
  emails: EmailMessage[];
}): Promise<DraftResult[]> {
  const results: DraftResult[] = [];

  for (const email of opts.emails) {
    const raw = await opts.llm.complete({
      system: SYSTEM_POLICY,
      user: buildUserPrompt(email),
      temperature: 0.3,
    });

    const parsed = safeJsonParse<DraftResult>(raw);

    // Hard guardrail: never allow auto-send initially.
    parsed.safeToAutoSend = false;

    results.push(parsed);
  }

  return results;
}
```

---

