---
title: "Membangun Laras-Bot: Dari Takopi, Pattern Utah, ke Impian ngoding dari Hape"
description: "Cerita di balik pembuatan laras-bot: menggabungkan konsep Takopi, pattern Utah, dan Temporal untuk membangun coding agent yang observable dan manageable di homelab."
publishedAt: 2026-04-25
locale: id
urlSlug: membangun-laras-bot-temporal-mattermost
isDraft: false
defaultLocale: en
---
> Note: EN translation is unavailable, showing ID source.

[TOC]

Beberapa minggu lalu, aku nyobain [Takopi](https://takopi.dev) — sebuah CLI tool yang bisa jalanin *coding agent* dan nyambungin ke Telegram[^fn:1]. Idenya simpel tapi *powerful* banget: ada layer otomasi di atas *coding agent*, dengan interface *chat* sebagai *gateway*-nya. Pas nyoba, rasanya langsung **"klik"**.

Tapi pas udah dipake beberapa lama, aku mulai ngerasa ada yang kurang—terutama di sisi *observability* pas *agent*-nya lagi kerja. Terus aku nemu sebuah tulisan yang nunjukin *pattern* yang pas buat nutup celah itu. Akhirnya, berujung jadi proyek sendiri yang sekarang udah jalan hampir seminggu di *homelab*.

Nah, ini ceritanya gimana **laras-bot** akhirnya lahir.

## Takopi: Pertama kali semuanya terasa "klik"

[Takopi](https://takopi.dev) itu intinya layer otomasi di atas *coding agent* berbasis CLI. Dia *support* beberapa *engine* kayak Pi dan Codex, terus punya *gateway* ke Telegram.

Yang bikin aku semangat bukan cuma fitur-fiturnya, tapi **gimana cara dia ngelola project**:

1. **Project registration**: Tinggal `takopi init` di sebuah repo dan bikin alias. Dari mana aja (termasuk lewat *chat*), tinggal kirim perintah kayak `/happy-gadgets do something`.
2. **Topic binding**: Tiap topik di Telegram bisa di-*bind* ke satu *project*. Jadi percakapan tetep rapi, satu topik = satu konteks *project*.

Ini mirip banget sama UX di Slack atau Discord: tiap *channel* ngomongin hal yang beda, konteks kepisah, gak campur aduk. Dari sini aku mikir: *"Wah, ini cara kerja yang mau aku pake terus nih."* Tapi ya itu tadi, ada sesuatu yang masih ganjel.

## Celah yang mau aku tutup: Resilience & Error Monitoring

Sebenernya Takopi udah oke banget, dia pun udah punya *streaming output* buat liat apa yang lagi dikerjain *agent*-nya. Tapi masalah utamaku lebih ke arah **resilience** dan **monitoring** pas ada yang *error*.

Belajar dari pengalaman pas pake OpenClaw dulu, banyak *cron job* atau sesi *chat* yang sering banget kena *silently error*—tiba-tiba berenti gitu aja tanpa kabar, atau *failed* tapi kita gak tau di *step* mana dan kenapa.

Buat tugas kecil sih gak masalah ya. Tapi kalau *agent*-nya mesti jalan 10-30 menit—misalnya pas lagi *refactor* besar atau *debug* yang kompleks—aku butuh visibilitas yang lebih dalam:

- Kalau *error*, dia nyangkut di *activity* mana secara spesifik?
- Bisa gak tugasnya di-*retry* otomatis tanpa harus ngulang dari nol?
- Status tiap langkahnya tercatat gak secara historis?

Aku butuh sistem yang lebih "keras kepala" dan bisa kasih tau dengan jelas kalau ada yang gak beres. Di situlah aku ngerasa butuh sesuatu yang lebih dari sekadar *streaming logs*.

## Pattern Utah: Agent loop sebagai Durable Workflow

Terus aku baca *blog post* dari JoelClaw tentang Utah[^fn:2], dan dia ngasih link ke proyek *open-source* namanya [Utah](https://github.com/inngest/utah) dari Inngest[^fn:3].

Utah ini ngasih contoh implementasi yang bikin aku beneran paham polanya. Idenya begini:

> Setiap interaksi sama *agent* di-*orchestrate* sebagai **durable workflow**. Tiap langkah (think, act, observe) adalah *step* yang bisa dimonitor, di-*retry*, dan di-*trace*.

Kira-kira alurnya kayak gini:

```text
Message masuk → trigger workflow
  ├── step: acknowledge (tampilin "typing...")
  ├── step: think (LLM call)
  ├── step: tool-read
  ├── step: tool-exec
  ├── step: think lagi (loop)
  └── step: send-reply
```

Plus ada beberapa tambahan keren:
- **Heartbeat**: *Cron* berkala buat ngerangkum *daily logs* jadi *long-term memory*.
- **Failure handler**: *Global catch* yang bakal ngabarin kita kalau ada yang *error*.
- **Singleton per chat**: Satu sesi per *thread*, otomatis *cancel* kalau ada pesan baru masuk.

Yang bikin Utah menarik bukan cuma kodenya, tapi **pattern-nya**. Ini pola yang muncul terus di berbagai proyek: Pi punya *session management*, Takopi punya *project management*, Utah punya *workflow durability*—semuanya kayak "konvergen" ke satu titik kebutuhan yang sama.

## Inspirasi di Atas Bermuara Jadi Laras-Bot

Tulisan JoelClaw itu nunjukin sesuatu yang sekarang makin aku yakini: **ada pola arsitektur yang ditemuin secara independen oleh banyak orang, dan semuanya bermuara ke bentuk yang mirip**.

Dari tiap proyek yang aku pelajarin, ada beberapa komponen yang selalu ada:

| Komponen | Takopi | Utah | Pi |
|----------|--------|------|----|
| Transport/Gateway | Telegram | Inngest Cloud | Terminal |
| Orchestration | Internal | Inngest (durable) | Session management |
| Project/Context | Project + branch | Workspace files | Session dir |
| Memory | — | File-based + distillation | Session file |
| Observability | Minimal | Inngest dashboard | Internal |

Masing-masing punya kelebihan sendiri. Tapi masalahnya: **gak ada yang punya semuanya dalam satu paket**.

## Keputusan: Bikin sendiri aja wqwq

Dari situ, keputusannya udah jelas: aku mau gabungin hal-hal terbaik dari tiap *pattern* tadi, tapi pake infrastruktur yang emang udah ada di *homelab*-ku sendiri.

**Tech stack pilihanku:**

- **Node.js + TypeScript + Fastify**: *Backend* yang emang udah jadi makanan sehari-hari.
- **Temporal**: *Durable workflow engine* yang jalan di *homelab* pake **SQLite-backed** (jadi gak perlu ribet setup DB terpisah).
- **SQLite**: Database utama, simpel, enteng, dan ngebut.
- **Mattermost**: *Self-hosted chat* yang mirip Slack, setup-nya gampang banget.

**Kenapa Mattermost, bukan Telegram?**

Takopi milih Telegram karena gampang diakses siapa aja. Tapi buat kebutuhanku, Mattermost lebih masuk akal:

1. **Self-hosted**: Data gak lewat server orang lain.
2. **Mirip Slack**: UX-nya udah biasa dipake pas kerja, jadi lebih familiar.
3. **Channel = Project**: Tiap *channel* bisa di-*bind* ke satu *project*, persis kayak konsep Takopi tapi lebih rapi.
4. **Full control**: Bisa di-custom dan dipantau sesuka hati.

Dan yang paling penting: **Temporal udah jalan di homelab-ku**. Jadi kalau mau liat *workflow dashboard*-nya, tinggal buka *web UI*-nya aja, beres!

## Laras-Bot: Arsitekturnya

Proyek ini aku kasih nama **laras-bot**. Kodenya ada di repo sendiri dan sekarang udah jalan anteng sebagai *systemd service*.

### Flow Utama

![Interaksi laras-bot di Mattermost](/images/laras-bot/mattermost-interaction.png)
*Contoh interaksi dengan laras-bot di Mattermost.*

```text
Mattermost (WebSocket)
  → Message masuk
    → Project Router (nentuin ke project mana)
      → Dispatcher → Temporal Workflow (chatThreadWorkflow)
        → Activities:
            ├── ensureSession (bikin/lanjutin session)
            ├── createRun (catat run di DB)
            ├── executeEngineTurn (jalanin engine CLI)
            └── finalizeRun (simpen hasilnya)
              → Engine Adapter (Pi / Gemini)
                → Events via NATS → Progress Consumer → Mattermost
```

### Temporal Workflow: chatThreadWorkflow

Ini jantung dari sistemnya. *Workflow* ini aku desain pake pola **drain-and-exit loop** yang terinspirasi dari konsep **singleton per chat** milik Utah. Idenya sederhana: cuma boleh ada satu *workflow* aktif per *thread* chat, jadi gak bakal ada dua *agent* yang rebutan konteks di waktu yang sama.

Alurnya kira-kira begini:

1. Tunggu pesan masuk (lewat *signal*).
2. Proses pesannya: *ensure session* → *create run* → *execute engine* → *finalize*.
3. Cek apa masih ada pesan di antrean (*queue*).
4. Kalau kosong, tunggu *timeout* 10 detik terus selesaikan *workflow*.
5. *Workflow* baru bakal otomatis jalan lagi kalau ada pesan baru masuk.

Semua proses ini kelihatan jelas di **Temporal Web Dashboard**. Aku bisa *trace* tiap langkahnya, liat riwayat *event*-nya, dan gampang banget kalau mau *debug* pas ada yang aneh-aneh. Gak ada lagi ceritanya *error* diem-diem tanpa jejak wqwq.

### Engine Adapter: Pi dan Gemini

Sejauh ini aku implementasi dua *engine*:

**Pi**: Karena dia punya *package* `pi-ai` yang bisa nyambung ke berbagai *provider* AI. Enaknya, aku bisa *switch provider* dengan gampang dan pake fitur `--append-system-prompt` buat nyuntik kepribadian (*persona*) si *agent*.

**Gemini CLI**: Aku suka banget sama model Gemini, dan CLI-nya makin ke sini makin mantap. Plus, langganan Google One AI Pro itu *value*-nya gede banget: dapet Gemini CLI, NotebookLM, AI Studio, dll. Harganya mirip sama langganan AI lain tapi dapet fiturnya bejibun.

Kedua *engine* ini jalan sebagai *child process*. Si *adapter* bakal nangkep *output* JSONL, ngerapiin *event*-nya, terus dikirim lewat NATS biar kita bisa liat *progress*-nya secara *real-time* di Mattermost.

### Scheduled Tasks via Temporal

![Temporal Dashboard - Scheduled Tasks](/images/laras-bot/temporal-scheduled-tasks.png)
*Tampilan daftar scheduled tasks di Temporal dashboard.*

Ini fitur yang bikin kerjaan harian jadi otomatis banget. *Cron*-nya dikelola langsung sama Temporal, jadi lebih enak karena:

- Bisa dipantau di *dashboard* Temporal.
- Bisa dipicu manual kalau lagi butuh.
- Bisa di-*pause* atau *resume*.
- Udah ada *retry logic* bawaan kalau gagal.

Beberapa *task* yang udah jalan otomatis:

| Task | Jadwal |
|------|----------|
| Brain auto commit | setiap 4 jam |
| Brain [qmd](https://github.com/tobi/qmd) update | setiap 6 jam |
| Daily Brain Memory | setiap hari |
| Morning Journal & Weather | harian pagi |
| Daily Journal Retro Check | harian sore |
| Monday Blog Writer | senin |
| Sunday Weekly Digest | minggu |

![Temporal Dashboard - Workflow Trace](/images/laras-bot/temporal-workflow-trace.png)
*Trace detail untuk satu workflow yang sedang berjalan.*

## Review setelah seminggu pake

Sekarang **laras-bot** udah jalan hampir seminggu. Ini beberapa statistiknya:

- **254 sessions** (250 aktif).
- **547 runs** (tingkat keberhasilan 94%).
- **7 projects** aktif.
- **7 scheduled tasks** yang jalan terus.

Perubahan paling kerasa dibanding sebelumnya:
1. **Bisa monitor dari mana aja**: Cukup buka *dashboard* Temporal, bisa liat semua *workflow* yang lagi jalan dan *trace* langkah demi langkah.
2. **Restart tanpa takut ilang state**: Kalau ada masalah koneksi, tinggal *restart* server-nya. *Workflow* yang sempet kegantung bakal dilanjutin otomatis karena *state*-nya aman di Temporal.
3. **Punya kontrol penuh**: Semuanya jalan di *homelab*, gak tergantung sama layanan pihak ketiga buat urusan *orchestration*-nya.

## Bukan cuma soal Tool, tapi soal Pattern

Kalau ditarik garis besarnya, cerita ini tuh sambungan dari [tulisan sebelumnya soal evolusi workflow coding agent](https://wayanjim.my.id/posts/evolusi-workflow-coding-agent)[^fn:4]. Dari Cody ke Amp terus ke Pi, fokusnya selalu sama: **konteks harus dijaga, sesi harus dipisah, dan agent harus punya peran yang jelas**.

Laras-bot nambahin satu lapis lagi: **orchestration harus observable**. Bukan cuma asal *agent*-nya jalan, tapi kita kudu bisa liat *gimana* dia jalan, di mana, dan berapa lama.

Pola yang muncul dari Takopi, Utah, Pi, sampe laras-bot itu sebenernya sama:

1. **Pisahin konteks per project/thread**: Takopi pake *topic*, laras-bot pake *channel binding*.
2. **Durable execution**: Utah pake Inngest, laras-bot pake Temporal.
3. **Engine abstraction**: Semuanya bisa gonta-ganti *engine* (Pi, Gemini, dll).

Masing-masing emang beda cara ngerjainnya, tapi polanya mirip-mirip. Dan ini bikin aku makin yakin kalau ini bukan sekadar tren doang, tapi emang **cara yang "bener"** buat ngatur *coding agent* buat skala personal.

## Selanjutnya?

Masih ada beberapa hal yang mau aku perbaikin lagi:
- **Dashboard monitoring khusus**: Aku pengen bikin *dashboard* ringan buat mantau *session* lebih detail dan *real-time* dengan cara *consume events* dari NATS. Jadi bisa liat langsung tiap *step*-nya (kayak *tool-read*, *tool-exec*, *think loop*) pas lagi jalan.
- **Abstraksi Konsep Agent**: Alih-alih cuma gonta-ganti file `SOUL.md`, aku mau nerapin hierarki **Project → Agent → Engine**. Konsep ini terinspirasi saat baca tweet Bang Anvie[^fn:5] tentang bagaimana memisahkan identitas agent dari infrastrukturnya.
    - **Project** bakal nentuin *working directory* (konteks kode).
    - **Agent** bakal jadi "personanya" yang punya identitas unik lewat `SOUL.md` yang bisa di-kustomisasi.
    - **Engine** adalah mesin penggeraknya (Pi, Gemini, dsb).
  Jadi tiap *project* bisa punya *default agent*, dan kita bisa ganti "siapa" yang ngerjain *project* itu dengan mudah tanpa kehilangan konteks direktorinya.
- **Logika fallback**: Kalau satu engine/model lagi *down* atau limit, otomatis nyoba *fallback* ke engine lain yang tersedia di agent tersebut.

Tapi buat sekarang, yang paling penting udah dapet: *coding agent* yang bisa dipantau, gampang dikelola, dan standby 24/7 di *homelab*. Mantap!

---

*Proyek laras-bot ini adalah tool personal yang aku bikin buat kebutuhan sendiri. Kodenya belum open-source, tapi konsep dan polanya bisa dipelajari dari tulisan ini.*

[^fn:1]: [Takopi - CLI tool for coding agents](https://takopi.dev)
[^fn:2]: [JoelClaw - Utah: Convergent Architecture](https://joelclaw.com/utah-joelclaw-convergent-architecture)
[^fn:3]: [Utah GitHub Repository](https://github.com/inngest/utah)
[^fn:4]: [Evolusi Workflow Coding Agent](https://blog.wayanjim.my.id/id/posts/evolusi-workflow-coding-agent)
[^fn:5]: [Inspirasi Abstraksi Agent - Anvie on X](https://x.com/anvie/status/2048667816614887813)
)
