Dokumen desain teknis untuk fitur dasbor sesi, ditulis sebelum dan
selama implementasi. Dokumen ini merupakan sumber kebenaran untuk pengembangannya. Saat
fitur dirilis,
/web/dashboard menjadi halaman yang ditujukan bagi pengguna dan halaman ini tetap
menjadi referensi arsitektur.Visi
Bekerja dengan agen saat ini berupa aliran teks. Dasbor mengubahnya menjadi meja kerja: agen merender widget langsung dan interaktif; pengguna menyematkannya ke permukaan persisten; obrolan ditambatkan ke samping (atau disembunyikan) dan konten utama adalah papan. Anda beralih dari “berbicara dengan agen” menjadi “mengoperasikan panel kontrol yang dibuat agen untuk Anda” tanpa pernah meninggalkan sesi. Prinsip:- Papan adalah wajah suatu sesi, bukan objek baru. Setiap sesi (utas)
memiliki dua wajah: transkrip dan papan. Sesi tanpa widget yang disematkan
adalah obrolan biasa. Sematkan satu widget dan papan pun ada. Papan mewarisi
identitas sesi, kepemilikan agen, penamaan, penyematan, dan siklus hidup. Tidak ada
dashboard_create, tidak ada registri papan, tidak ada model ACL terpisah. - Kesetaraan agen. Segala sesuatu yang dapat dilakukan pengguna pada papan juga dapat dilakukan agen dengan alat: menambah/memperbarui/menghapus widget, menatanya, mengelola tab, mengganti tab yang terlihat, serta menambatkan atau menyembunyikan obrolan.
- Native, bukan tersemat. Papan merupakan komponen Lit dalam shell Control UI (sistem desain yang sama dengan bagian aplikasi lainnya). Hanya konten widget yang disandbox dalam iframe. Tidak ada bilah URL, tidak ada krom peramban.
- Permukaan agen yang kecil. Widget dirujuk dengan nama stabil dan diperbarui di tempat. Tata letaknya berupa grid mengalir yang memadat otomatis; agen menyebutkan ukuran dan jangkar, bukan piksel atau koordinat.
- Kapabilitas, bukan kepercayaan. Kode widget adalah HTML/JS arbitrer yang dibuat agen dalam sandbox ketat. Jangkauan (data Gateway, tindakan, jaringan) hanya tersedia melalui manifes kapabilitas yang dideklarasikan dan diberikan oleh operator.
Konsep
Alur UX
- Peningkatan: agen memanggil
show_widgetdalam obrolan mana pun → widget dirender sebaris dalam transkrip persis seperti saat ini → mengarahkan kursor menampilkan Sematkan ke dasbor → widget muncul di papan sesi. Agen dapat meneruskanpin: trueuntuk melakukan hal yang sama. - Tampilan papan: sesi yang memiliki papan mendapatkan tombol pengalih wajah (Obrolan / Dasbor). Tampilan papan = strip tab (hanya saat >1 tab) + grid mengalir + panel obrolan tertambat. Tambatan obrolan dapat diubah ukurannya, dipindahkan (kiri/kanan/bawah), dan diciutkan persis seperti bilah samping. Status tambatan per tab diingat.
- Seret: pengguna menyeret widget; grid memadat otomatis (widget mengapung ke atas, tetangga mengalir ulang). Mengubah ukuran dengan handel akan menjepret ke tahapan ukuran. Tidak ada penempatan piksel — bagi siapa pun.
- Peringatan pengaturan ulang:
/new//resetpada sesi yang memiliki papan meminta konfirmasi di UI web (“konteks diatur ulang, dasbor tetap ada”) dan mempertahankan papan. - Bilah samping: sesi tersemat merender wajah papannya jika memilikinya. Papan sesi Beranda adalah “dasbor agen” default.
- Interaksi (tiga tingkat, lihat di bawah): peristiwa status senyap, pengiriman prompt yang terlihat, dan pemicu otomatisasi.
Tingkat interaksi
- Peristiwa status (default). Interaksi UI widget yang perlu diketahui model,
tetapi tidak perlu ditanggapi.
bridge.emitState({...})menambahkan pemberitahuan sesi terstruktur (mekanisme yang sama dengan pemberitahuan aktivitas grup). Tidak ada giliran agen yang dimulai; model melihat akumulasi pemberitahuan pada eksekusi berikutnya. - Prompt (percakapan eksplisit).
bridge.sendPrompt(text)— memerlukan aktivasi pengguna; mengirim pesan pengguna yang terlihat ke dalam sesi (obrolan tertambat menampilkannya). Dibatasi lajunya; setiap pengiriman dikonfirmasi pengguna kecuali widget memiliki pemberian kapabilitasprompt. - Otomatisasi.
bridge.runAction(name, args)— menjalankan tindakan yang dideklarasikan dalam manifes. Kumpulan verba awal:cron.trigger(jalankan tugas Cron yang sudah ada sekarang) danbinding.refresh. Tugas Cron sudah berjalan dalam sesi eksekusi terisolasi yang terlihat dan dapat menggunakan model yang lebih murah: itulah jalur “model kecil menggerakkan widget”. Tidak ada sesi tersembunyi di mana pun.
Model dan hosting widget
HTML/JS widget dibuat oleh agen (biasanya melaluishow_widget), dibungkus
dalam shell dokumen standar (meta CSP, pelapor ukuran, bootstrap bridge), dan
dirender dalam <iframe sandbox="allow-scripts"> (tidak pernah allow-same-origin).
- Widget sebaris (transkrip) mempertahankan pipeline dokumen canvas saat ini: ditulis di bawah direktori status, disajikan oleh Gateway, dipangkas per cakupan, tanpa persetujuan (widget tersebut tidak memiliki kapabilitas secara bawaan — pengiriman prompt dikonfirmasi pengguna).
- Widget papan merupakan status sesi: byte berada dalam DB SQLite milik agen
pemiliknya (
board_widgets), disajikan oleh rute Gateway inti (/__openclaw__/board/<agentId>/<sessionKey>/<name>/) yang membaca DB. Menyematkan widget transkrip akan menyalin byte. Batas: 256 KB per widget, 48 widget per papan. - Pembaruan di tempat: memancarkan ulang widget dengan
nameyang sama akan mengganti byte, menaikkanrevision, menyiarkanboard.changed, dan tampilan langsung hanya memuat ulang iframe tersebut. - Pembekuan byte: kapabilitas yang diberikan terikat pada sha256 byte widget.
Mengubah byte mempertahankan pemberian
data/net/actionshanya jika revisi baru mendeklarasikan subset dari manifes yang diberikan; manifes yang diperluas akan meminta ulang persetujuan operator.
Widget meng-host konten; aplikasi MCP adalah salah satu jenis konten
Widget adalah primitif OpenClaw: sel papan bernama, tersemat, berukuran, dan dimiliki sesi dengan catatan pemberian. Yang dirender di dalamnya adalah jenis konten:html— dibuat oleh agen melaluishow_widget, byte dalam penyimpanan papan.mcp-app— tampilan aplikasi MCP pihak ketiga (sumber dayaui://dari server yang dikonfigurasi) yang di-host di dalam sel widget.
show_widget tetap seringkas saat ini dan tidak pernah
perlu mengetahui bahwa spesifikasi MCP Apps ada.
Infrastruktur bersama di bawahnya (di sinilah penyederhanaan diterapkan):
- Satu host sandbox. Widget
htmldirender melalui pipeline tangguh yang sama yang digunakan aplikasi MCP saat dirilis (iframe ganda pada origin sandbox khusus, CSP per widget yang dideklarasikan dan didekode secara gagal-tertutup), alih-alih host iframe khusus kedua. Proksi menerima HTML berdasarkan nilai, sehingga konten lokal menjadi kasus yang alami. - Satu model otorisasi. Jangkauan widget adalah daftar izin yang diberikan,
apa pun jenisnya: untuk widget
html, alat host; untuk widgetmcp-app, alat server yang terlihat oleh aplikasi (melalui mekanismeallowedAppToolNamesyang sudah ada, dibuat persisten per widget alih-alih per eksekusi pencetakan). - Alat host untuk widget
html(diekspos melalui bridge widget, diperiksa terhadap pemberian):openclaw.prompt.send— tingkat 2; dirutekan melalui komposer yang terlihat, dikonfirmasi pengguna kecuali telah diberikanopenclaw.state.emit— pemberitahuan sesi tingkat 1 (digabungkan, ukurannya dibatasi)openclaw.data.read— binding hanya-baca berparameter (kumpulan RPC baca yang diizinkan dan sudah ada), diselesaikan di sisi Gatewayopenclaw.cron.trigger— otomatisasi tingkat 3
net= CSP. Jangkauan jaringan menggunakan deklarasi CSP per widget yang sudah dirilis (originconnect-src) — widget cuaca yang memperbarui diri mengambil API-nya langsung dari sandbox, tanpa keterlibatan Gateway.- Pemberian. Widget yang tidak mendeklarasikan apa pun langsung dirender (tersandbox,
default-src 'none', pengiriman prompt dikonfirmasi satu per satu) — tingkat kepercayaan yang sama dengan widget obrolan sebaris saat ini. Alat/origin yang dideklarasikan menempatkan widget dalampendingpada papan: kartu placeholder mencantumkannya dalam bentuk yang mudah dibaca manusia dengan sekali ketuk Izinkan/Tolak. Pemberian berlaku per nama widget; untuk widgethtml, pemberian dibekukan berdasarkan byte (sha256), dan byte yang berubah hanya mempertahankan pemberian jika deklarasinya menyempit. - Shim pembuatan. Pembungkus dokumen menyuntikkan
window.openclaw.sendPrompt/emitState/read/callsebagai API pembuat yang stabil; apakah transportasi di bawahnya merupakan saluran milik kami atau AppBridge adalah detail internal yang tidak pernah dilihat pembuat widget. Pelaporan ukuran dan token tema melewati bridge yang sama.
Tampilan transkrip: satu kartu widget
Tampilan sebaris disatukan pada primitif widget. Saat hasil alat membawa UI — keluaranshow_widget atau hasil alat MCP dengan sumber daya aplikasi — sistem
mewujudkan widget sementara bernama otomatis (dicakup ke sesi, dipangkas) dan
transkrip merender satu kartu widget yang melakukan dispatch berdasarkan jenis konten.
Tampilan otomatis aplikasi MCP tetap persis seperti yang diharapkan spesifikasi (tanpa pekerjaan model tambahan);
di bawahnya, itu memang adalah widget. Hal ini menghapus kasus khusus paralel mcpApp
dalam perenderan obrolan (pembatasan permukaan, deduplikasi terpisah), memberi setiap
UI sebaris kemudahan penyematan yang sama, dan menjadikan registri widget sebagai jalur utama
untuk membuka kembali (rekonstruksi melalui pemindaian transkrip tetap menjadi fallback bagi riwayat yang tidak pernah
disematkan). Host mandiri bertiket dan hanya-baca bertumpang tindih dengan papan sebagai
permukaan persisten untuk membuka kembali — kandidat konsolidasi yang akan dievaluasi dalam T6, bukan
diasumsikan.
Komposisi: v1 menggunakan kedekatan grid (widget krom agen di sebelah widget aplikasi pada
satu tab). v2 menambahkan slot aplikasi yang dikelola host — HTML widget agen mendeklarasikan
wilayah slot dan host mengomposisikan tampilan aplikasi sebenarnya sebagai sandbox saudara.
Aplikasi tidak pernah dirender di dalam iframe agen: penyarangan akan merusak identitas
bridge dan memungkinkan penimpaan/clickjack pada UI aplikasi yang telah diberikan izin, sehingga slot merupakan
kontrak tata letak, bukan sematan.
Widget bersumber dari server (aplikasi MCP tersemat)
Dengan host terpadu, menyematkan aplikasi MCP pihak ketiga hanyalah sebuah widget yang kontennya diambil dari server alih-alih disimpan:board_widgets menyimpan
deskriptor (serverName, toolName, uiResourceUri, asal
toolCallId + sessionKey) alih-alih byte HTML, dan board membuat ulang
sewa tampilan setelah TTL 10 menit giliran chat terlampaui (mengambil ulang sumber daya
ui:// ketika kedaluwarsa). Tampilan aplikasi MCP sebaris dalam chat mendapatkan
fasilitas Sematkan ke dasbor yang sama seperti widget agen. Tampilan yang dibuka
kembali saat ini bersifat hanya-baca sesuai desain; aplikasi tersemat yang harus tetap
interaktif mendapatkan izin permanen atas alat server yang terlihat oleh aplikasi
(daftar izin eksplisit ditampilkan kepada operator saat penyematan), yang dipisahkan
dari proses penerbitan. Sematan tanpa izin tetap hanya-baca — masih berguna untuk dasbor
tampilan. v1 menyematkan ke board sesi asal; penyematan lintas sesi memerlukan broker sewa
dan harus menunggu. Koordinasikan dengan PR terbuka #109807 (ui/message
perutean composer, propagasi tema/ukuran).
Tata letak: grid fleksibel
12 kolom, tinggi baris tetap, pemadatan otomatis (gravitasi ke atas, bergeser ke samping saat diseret — semantik gridstack, diimplementasikan secara native; matematika grid tetap murni dan bebas DOM). Status tata letak widget per tab:{ name, w (1-12), h (rows) } ditambah
urutan. Kosakata agen:
size:sm(3×3) ·md(6×4) ·lg(8×6) ·xl(12×8) ·full(tab satu widget)after: <widgetName>jangkar pengurutan opsional; dihilangkan = tambahkan- Pengguna bebas menyeret/mengubah ukuran; model urutan+ukuran yang sama dapat dikirim dan dikembalikan tanpa perubahan.
Model data (DB per agen)
Tabel baru diagents/<agentId>/agent/openclaw-agent.sqlite
(memerlukan peningkatan versi skema DB agen — persetujuan operator diperlukan
sebelum perubahan ini diterapkan):
sessionKey. Menghapus sesi akan menghapus
baris board-nya. /new//reset tidak mengubahnya.
Permukaan protokol
RPC (tabel metode inti, skema typebox digateway-protocol):
board.get { sessionKey }→ tab + metadata widget (tanpa byte) —operator.readboard.update { sessionKey, ops[] }— CRUD/pengurutan ulang tab, pemindahan/perubahan ukuran/ penghapusan/pembatalan sematan widget, status dok, fokus-tab —operator.writeboard.widget.put { sessionKey, name, html, manifest, placement }—operator.write(jalur alat agen dan jalur penyematan)board.widget.grant { sessionKey, name, decision }—operator.approvalsboard.event { sessionKey, widget, payload }— penyerapan peristiwa status tingkat-1 —operator.write
EVENT_SCOPE_GUARDS, cakupan baca):
board.changed { sessionKey, revision, widget? }— status tersimpan berubah; UI mengambil ulang (dan memuat ulang satu iframe ketikawidgettersedia).board.command { sessionKey, command }— pengendalian UI sementara (agen mengalihkan tab yang terlihat, mengaktifkan/menonaktifkan dok chat) — polaui.command.
Alat agen
Total tiga alat (inti, selalu terdaftar; rendering dibatasi berdasarkan kapabilitas klieninline-widgets seperti saat ini):
show_widget { title, widget_code, name?, pin?, size?, tab?, after?, capabilities? }— buat/perbarui berdasarkan nama;pinmenempatkannya di papan. Tanpaname/pin, perilakunya sama persis seperti saat ini (sebaris, sementara).dashboard { action, ... }— verba pengelolaan papan:read,tab_create,tab_update,tab_delete,tabs_reorder,widget_move,widget_remove,unpin,focus_tab,set_chat_dock.- Alat
cronyang ada mencakup tingkat otomatisasi; tidak diperlukan alat baru.
[dashboard] user clicked "Refresh" on widget weather (tab main).
Yang digantikan oleh ini
extensions/workspacesdihapus. Eksperimental,enabledByDefault: false, tidak pernah ada dalam rilis stabil (pertama kali muncul dalam versi beta 2026.7.2). Tidak ada migrasi; aturan doctor menghapus<stateDir>/workspaces/yang usang jika ada. Gagasan yang diambil: matematika grid murni, model keamanan bridge (bootstrap port, pembatasan binding, batas laju), persetujuan dengan byte yang dibekukan.- Hosting widget dipindahkan dari
extensions/canvaske inti. Penyimpanan dokumen canvas, pembungkus dokumen, penyajian HTTP, dan alatshow_widgetmenjadi bagian inti (src/canvas/); plugin mempertahankan alat kontrol node-canvas (canvas) dan A2UI. PengumumanpluginSurfaceUrls["canvas"]dan jalur/__openclaw__/canvasmerupakan kontrak klien native yang telah dirilis dan tetap stabil. Sesi Discord mempertahankan varianshow_widgetyang dimiliki Discord. - WorkBoard tidak diubah (integrasinya merupakan program lanjutan).
Bukan sasaran (program ini)
- Berbagi papan multi-pengguna/ACL (mendatang; akan hadir melalui berbagi sesi).
- Rendering papan native macOS/iOS (mereka mendapatkannya di mana pun mereka menyematkan Control UI; jalur widget sebaris tidak berubah).
- Widget data bawaan (kartu sesi/penggunaan/cron) — bridge kapabilitas beserta widget buatan agen mencakup v1; registri jenis bawaan dapat ditambahkan nanti.
- WorkBoard di dasbor.
Rencana implementasi
Worktree independen, dibuat dengan Codex, direview+diintegrasikan secara berurutan. Integrasikan-lalu-perbaiki.
Validasi sesuai aturan repo: vitest terfokus secara lokal, pemeriksaan lengkap di
Crabbox/Testbox,
$autoreview sebelum setiap integrasi, bukti langsung untuk T6.