hanoman 0.9.19 → 0.9.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-info.json +3 -3
- package/dist/cli.js +2966 -775
- package/dist/server.js +10106 -8155
- package/docs/agent-integration.md +208 -97
- package/package.json +1 -1
- package/prisma/migrations/20261010070000_remove_memory_changelog/migration.sql +40 -0
- package/prisma/migrations/20261012100000_spec_reviewed_at/migration.sql +13 -0
- package/prisma/schema.prisma +7 -80
- package/web/assets/DalangHanomanScreen-Us_5qG25.js +13 -0
- package/web/assets/DalangHanomanScreen-gk7fa9dl.css +1 -0
- package/web/assets/{DocPreviewModal-CHrsP0pL.js → DocPreviewModal-CJP6d0Hx.js} +1 -1
- package/web/assets/DocsWorkspace-CSak7xFX.css +1 -0
- package/web/assets/DocsWorkspace-zGpPd7u3.js +3 -0
- package/web/assets/IdeScreen-CppUucs0.css +10 -0
- package/web/assets/IdeScreen-DTvfuR2n.js +2 -0
- package/web/assets/LeadScreen-CN53yR_e.css +1 -0
- package/web/assets/LeadScreen-DWbunLkQ.js +1 -0
- package/web/assets/ReviewScreen-Ch9UiKlx.js +1 -0
- package/web/assets/SchedulerScreen-zIUh-Vbe.js +1 -0
- package/web/assets/SettingsScreen-CLMuCJ6R.js +62 -0
- package/web/assets/SettingsScreen-DQZ3K43g.css +1 -0
- package/web/assets/TeamScreen-CV1R5Ekk.js +1 -0
- package/web/assets/TeamScreen-CtkpFpCF.css +1 -0
- package/web/assets/TerminalScreen-DrgZKlw2.css +1 -0
- package/web/assets/TerminalScreen-QVjhjrxx.js +2 -0
- package/web/assets/TriageScreen-B6_KrS6l.css +1 -0
- package/web/assets/TriageScreen-Dpj4YrBQ.js +1 -0
- package/web/assets/VpsScreen-BzXpc3le.css +1 -0
- package/web/assets/VpsScreen-DX_HEquM.js +2 -0
- package/web/assets/index-CL0_zz87.css +32 -0
- package/web/assets/index-DmnwLncG.js +1012 -0
- package/web/assets/live-B193WtZm.js +1 -0
- package/web/assets/sched-lead-shared-BjMO8Q_k.js +1 -0
- package/web/assets/scheduler-CQt-VP9-.css +1 -0
- package/web/assets/settings-rebase-BI7-Bk98.js +1 -0
- package/web/index.html +10 -3
- package/web/assets/ChangelogScreen-DL5v_FZf.js +0 -1
- package/web/assets/DalangHanomanScreen-aLBtUGE4.js +0 -1
- package/web/assets/DocsWorkspace-D262CVxH.js +0 -3
- package/web/assets/IdeScreen-BfC0goYb.css +0 -10
- package/web/assets/IdeScreen-D2GI3KYh.js +0 -2
- package/web/assets/LeadScreen-eb_DK1j6.js +0 -1
- package/web/assets/ReviewScreen-DarOFH_a.js +0 -1
- package/web/assets/SchedulerScreen-DKoMf2LT.js +0 -1
- package/web/assets/SettingsScreen-BAUhwd5K.js +0 -62
- package/web/assets/TeamScreen-C2fSsQ8C.js +0 -1
- package/web/assets/TerminalScreen-GsfOMtu5.js +0 -2
- package/web/assets/TriageScreen-DdKMAroz.js +0 -1
- package/web/assets/VpsScreen-CDAjvyT1.js +0 -2
- package/web/assets/hnm-blencong-v01-CBBfchr0.webp +0 -0
- package/web/assets/hnm-dalang-six-arms-v01-DeBacuq8.webp +0 -0
- package/web/assets/hnm-hero-rig-arm-ml-v01-Kts3pjTf.webp +0 -0
- package/web/assets/hnm-hero-rig-arm-mr-v01-DQgnE6bD.webp +0 -0
- package/web/assets/hnm-hero-rig-arm-ul-v01-CIAWAxFe.webp +0 -0
- package/web/assets/hnm-hero-rig-arm-ur-v01-C0R0CZnp.webp +0 -0
- package/web/assets/hnm-hero-rig-body-v01-DmbKJJHB.webp +0 -0
- package/web/assets/hnm-wayang-panakawan-v01-1LGv-V8N.webp +0 -0
- package/web/assets/hnm-wayang-project-v01-BG9EZqzn.webp +0 -0
- package/web/assets/hnm-wayang-putri-v01-F6p69kHq.webp +0 -0
- package/web/assets/hnm-wayang-satria-alus-v01-CpfoXKTy.webp +0 -0
- package/web/assets/hnm-wayang-satria-gagah-v01-bLfIYAsv.webp +0 -0
- package/web/assets/index-DI8I0VA5.css +0 -32
- package/web/assets/index-gNO91_qw.js +0 -1063
- package/web/assets/live-Cz6dWAau.js +0 -1
|
@@ -91,62 +91,57 @@ host-mu benar dan masalahnya ada pada token atau master switch.
|
|
|
91
91
|
## 3. Capability
|
|
92
92
|
|
|
93
93
|
Capability berformat `"<domain>:<access>"`, `access ∈ {read, write}`, dan **write meng-implikasikan
|
|
94
|
-
read** pada domain yang sama. Ada **
|
|
95
|
-
(ADR-0155) yang **tak diimplikasikan `:write`**, sehingga totalnya **
|
|
94
|
+
read** pada domain yang sama. Ada **17 domain**; empat di antaranya punya akses KETIGA `danger`
|
|
95
|
+
(ADR-0155) yang **tak diimplikasikan `:write`**, sehingga totalnya **38 capability**. Katalog resmi (dengan label &
|
|
96
|
+
read** pada domain yang sama. Ada **18 domain**; lima di antaranya punya akses KETIGA `danger`
|
|
97
|
+
(ADR-0155, ADR-0193) yang **tak diimplikasikan `:write`**, sehingga totalnya **40 capability**
|
|
98
|
+
(`portal` hanya punya `read` — lihat tabel). Katalog resmi (dengan label &
|
|
96
99
|
deskripsi) tampil di panel **Settings → Akses AI Agent** saat manusia membuat token; endpoint
|
|
97
100
|
katalognya (`GET /api/agent-tokens/capabilities`) bersifat **cookie-only** (lihat §5) — kamu tak
|
|
98
101
|
perlu mengambilnya, cukup rujuk tabel di bawah:
|
|
102
|
+
read** pada domain yang sama. Ada **23 domain** dan **48 capability**; domain yang punya akses KETIGA
|
|
103
|
+
`danger` (`sessions:spawn`, `ide:git`, `backlog:lifecycle`, `vps:exec` — ADR-0155 — dan, sejak
|
|
104
|
+
ADR-0196..0198, `access:admin`, `remote:control`, `relay:exec`) **tak diimplikasikan `:write`**.
|
|
105
|
+
Katalog resmi (dengan label & deskripsi) tampil di panel **Settings → Akses AI Agent** saat manusia
|
|
106
|
+
membuat token; endpoint katalognya (`GET /api/agent-tokens/capabilities`) kini terbaca dengan
|
|
107
|
+
`tokens:read` — selain itu cukup rujuk tabel di bawah:
|
|
99
108
|
|
|
100
109
|
| Domain | Cakupan endpoint | Catatan |
|
|
101
110
|
|---|---|---|
|
|
102
111
|
| `projects` | `/api/projects*` | project, branch, binding, Help Center |
|
|
103
112
|
| `backlog` | `/api/specs*` | spec/backlog, dokumen, review diff, integrate |
|
|
104
|
-
| `sessions` | `/api/terminal*` (+ WS terminal) | jalankan sesi agen/shell, kirim input
|
|
113
|
+
| `sessions` | `/api/terminal*` (+ WS terminal) | jalankan sesi agen/shell, kirim input — **high-risk (RCE)**. Sub-pohon `/dialog` **bukan** domain ini sejak ADR-0190: lihat `decisions`. `steer` ke sesi yang sedang menampilkan dialog juga digerbangi `sessions:decide` |
|
|
105
114
|
| `docs` | `/api/prds*`, `/api/projects/:id/{docs,prds}*` | dokumen SoT project & PRD |
|
|
106
115
|
| `ide` | `/api/projects/:id/{tree,file,file-diff,working-status,graph,commit,git,status,stashes,remotes,compare,archive,pr-url}*` | tree/file working tree, operasi git |
|
|
107
116
|
| `vps` | `/api/vps*` | kelola VPS, audit, harden, konsol — **high-risk (remote exec)** |
|
|
108
|
-
| `settings` | `/api/settings*`, `/api/config*`, `/api/scheduler*` | setelan instance & config runtime |
|
|
117
|
+
| `settings` | `/api/settings*`, `/api/config*`, `/api/scheduler*` kecuali `/api/scheduler/crons*` | setelan instance & config runtime |
|
|
109
118
|
| `support` | `/api/tickets*`, `/api/github-issues*`, `/api/projects/:id/github*` | tiket Help Center & issue GitHub (triase) |
|
|
110
119
|
| `notifications` | `/api/notifications*` | notifikasi |
|
|
111
120
|
| `lead` | `/api/lead*` | minta putusan ke hanoman-lead & baca jejaknya — **`lead:write` bisa menggerakkan sesi** (ADR-0091) |
|
|
112
121
|
| `agents` | `/api/custom-agents*` | katalog custom agent global & per project — **`agents:write` mengubah apa yang dilihat SETIAP sesi baru** (ADR-0094) |
|
|
113
122
|
| `skills` | `/api/skills*` | skill global hanoman, user, plugin (baca-saja) & per project, termasuk isi berkasnya — **`skills:write` pada skill global hanoman mengubah instruksi SETIAP sesi baru di semua project** |
|
|
114
123
|
| `telegram` | `/api/telegram*` kecuali sub-path kredensial | context/memory/reply/audit kanal operator Telegram (ADR-0096) |
|
|
115
|
-
| `
|
|
124
|
+
| `qa` | `/api/projects/:id/qa*`, `/api/qa*` | laporan QA per project: test case, temuan, lampiran |
|
|
125
|
+
| `activity` | `/api/session-results*` | log aktivitas lintas device (stage, commit, PR); **`activity:write` = purge yang menyeberang ke SEMUA mesin lewat sync** (ADR-0188) |
|
|
126
|
+
| `portal-chat` | `/api/portal-chat*` | chat portal klien sisi OPERATOR: sesi, PRD draft, ekspor transkrip — berisi **email & percakapan klien**; `portal-chat:write` menulis `docs/prd/<slug>.md` di checkout (ADR-0189). Beda dari `/api/portal*` yang tetap cookie-only |
|
|
127
|
+
| `telegram` | `/api/telegram*` kecuali sub-path kredensial | context/memory/reply/audit kanal operator Telegram (ADR-0096). Sub-path **kredensial** (`/api/telegram/settings`, `/api/telegram/test`, `/api/telegram/credentials`) menuntut capability `danger` **`telegram:admin`** (ADR-0193) — tak diimplikasikan `telegram:write` maupun `settings:write`, sehingga token gateway Telegram sendiri tetap tak menjangkaunya. **Anti-eskalasi (403):** token gateway efektif tak boleh memegang capability yang tak dimiliki token pemanggil |
|
|
128
|
+
| `webhooks` | `/api/webhooks*` | webhook keluar: endpoint, putar secret, ping, riwayat & ulang pengiriman (ADR-0194). **`webhooks:write` menentukan ke mana data workspace mengalir keluar**; secret penandatanganan tampil **sekali** saat dibuat/diputar. `allowPrivate` (SSRF ke jaringan internal) tetap keputusan manusia — agent token ditolak 403 bila menyalakannya |
|
|
129
|
+
| `clients` | `/api/client-accounts*` | akun login portal klien: daftar, buat, ubah akses project/nonaktifkan, reset password, hapus (ADR-0194). `clients:write` **membuka login baru ke data project** |
|
|
130
|
+
| `portal` | `/api/portal*` (**baca** saja) | lihat portal **atas nama satu akun klien** yang disebut eksplisit lewat `?asClient=<id akun klien>` (ADR-0195); hanya `portal:read`, tak ada `portal:write`. Chat portal memuat percakapan klien |
|
|
116
131
|
| `team` | `/api/tasks*`, `/api/members*` | papan **Tim**: kartu kerja MANUSIA & direktori anggota (ADR-0157). `status` kartu milik manusia — ia **bukan** `stage` backlog. `POST /api/tasks/:id/escalate` melahirkan backlog item dan tetap `team:write` (cermin `POST /api/tickets/:id/accept`) |
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
-
|
|
130
|
-
- **Dari dalam sesi hanoman** (ADR-0179): CLI MCP meneruskan `HANOMAN_SESSION_ID` + `HANOMAN_EVENT_TOKEN`
|
|
131
|
-
sesi induknya sebagai `x-hanoman-session` / `x-hanoman-session-token` (hanya ke host loopback). Project
|
|
132
|
-
diambil dari sesi — allowlist `projectIds` tak dibutuhkan, capability `memory:*` tetap. Kredensial sesi
|
|
133
|
-
salah → `401`; sesi tak hidup → `404`. Memori dari sesi yang menyentuh input eksternal (Help Center,
|
|
134
|
-
tiket, issue GitHub) selalu masuk review manusia.
|
|
135
|
-
- Sesi backlog lahir dengan memori `active` yang jangkarnya cocok HEAD worktree-nya (claude:
|
|
136
|
-
`--append-system-prompt-file`, codex: `developer_instructions`).
|
|
137
|
-
|
|
138
|
-
Aturan pemetaan **deterministik** (`server/src/services/agent-capabilities.ts`): `GET`/`HEAD` →
|
|
139
|
-
`:read`, metode lain → `:write`. Itu berlaku untuk domain `lead` juga — **`POST /api/lead/decisions`
|
|
140
|
-
menuntut `lead:write`**, dan `lead:read` tak pernah cukup: meminta putusan melahirkan baris jejak
|
|
141
|
-
permanen dan keputusannya bisa menggerakkan sesi. Sub-path `/api/projects/:id/{docs,prds}` dihitung
|
|
142
|
-
domain **`docs`**; sub-path IDE/git di atas dihitung domain **`ide`**; WebSocket terminal butuh
|
|
143
|
-
**`sessions:write`**.
|
|
144
|
-
|
|
145
|
-
Empat endpoint STATUS tak menuntut capability sama sekali — token sah mana pun boleh membacanya
|
|
146
|
-
(`GLOBAL_READ`, ADR-0157): `GET /api/limits`, `GET /api/limits/codex`, `GET /api/update`, dan
|
|
147
|
-
`GET /api/fs/browse` (menelusuri folder mesin untuk mengisi `repoDir`). Hanya method BACA:
|
|
148
|
-
`POST /api/update/apply` me-restart instance dan tetap **403** untuk agent token, apa pun
|
|
149
|
-
capability-nya (SPEC-405/ADR-0088) — prefix yang sama tak menurunkan gerbangnya.
|
|
132
|
+
| `qa` | `/api/projects/:id/qa/**`, `/api/qa/**` | laporan QA, test case, temuan, lampiran, ekspor/impor. Method `DELETE` menuntut `qa:delete` (akses `danger`, ADR-0185), bukan `qa:write` |
|
|
133
|
+
| `decisions` | `/api/terminal/sessions/:id/dialog*` (capability `sessions:decide`, akses `danger`) | **gerbang manusia-yang-memutuskan** (ADR-0190, ADR-0192): baca, jawab, dan rebut dialog `AskUserQuestion` sebuah sesi. **Tak diimplikasikan `sessions:write`.** Beri HANYA ke token yang tidak dipakai agen di dalam sesi hanoman — lihat §13 |
|
|
134
|
+
| `scheduler` | `/api/scheduler/crons*` | cronjob per project — **cron = sesi agen berizin penuh yang ditunda** (ADR-0191). Membuat, mengubah prompt/agen/model/effort/jadwal, mengaktifkan, dan menjalankan cron menuntut **`scheduler:write` DAN `sessions:spawn`**; server menjawab 403 `need: sessions:spawn` bila yang kedua tak dicentang |
|
|
135
|
+
| `tokens` | `/api/agent-tokens*` | cetak/ubah/cabut **agent token**. Dulu cookie-only (ADR-0065); ADR-0196 membukanya dengan **pagar tak-eskalasi-diri** (§5b). `tokens:write` **bukan** izin menaikkan hak sendiri |
|
|
136
|
+
| `devices` | `/api/device-tokens*` | **device token** (identitas mesin untuk sync & relay) atas nama akun admin yang menerbitkan agent token ini (ADR-0197). Plaintext sekali-tampil |
|
|
137
|
+
| `access` | `/api/auth/users*`, `POST /api/auth/change-password` | daftar akun (`access:read`); **`access:admin`** (danger) membuat akun admin, menghapus akun, dan menyetel ulang password akun lain — **setara akses penuh instance** |
|
|
138
|
+
| `remote` | `/api/remote-control` | grant kendali jarak jauh mesin ini. `remote:read` membaca; **`remote:control`** (danger) mengubahnya — grant yang menyala membuat hub menjalankan agen di mesin ini (ADR-0165, ADR-0197) |
|
|
139
|
+
| `relay` | `/api/devices/:id/relay/*` | **`relay:exec`** (danger): meneruskan aksi sesi ke mesin LAIN lewat relay hub, atas nama operator — eksekusi lintas mesin (ADR-0165 §4, ADR-0197) |
|
|
140
|
+
| `logs` | `/api/logs*` | log terpusat lintas device: cari, transkrip, retensi (`logs:read`); `logs:write` mengubah retensi & lajur pengiriman log (memendekkan retensi menghapus jejak audit) |
|
|
141
|
+
| `presence` | `/api/presence` | peta sesi hidup di seluruh device operator (`presence:read`, ADR-0147/0197) |
|
|
142
|
+
| `metrics` | `/api/custom-agents/{metrics,invocations}*` | metrik custom agent (`metrics:read`) dan disposisi per invokasi (`metrics:write` — vonis MANUSIA atas mutu agen; ADR-0198) |
|
|
143
|
+
| `sync` | `GET /api/sync/{conflicts,pending}`, `POST /api/sync/now`, `POST /api/sync/conflicts/:entity/:recordId/resolve` | **hanya tiga permukaan UI sync** (ADR-0199): antrean konflik & hapus-tertunda (`sync:read`), pemicu sync manual (`sync:write`), memutuskan konflik (`sync:resolve`, akses `danger` — **membuang data satu sisi tanpa jalan pulang**, tak diimplikasikan `sync:write`). Kanal mesin-ke-mesin (`/api/sync/{pull,bootstrap,push,logs,attachments,qa-attachments,ws,relay}`) tetap bukan milik agen — lihat §5 |
|
|
144
|
+
| `instance` | `POST /api/update/apply`, `POST /api/restart` | satu capability `instance:admin` (akses `danger`, ADR-0200): memasang versi hanoman terbaru dari registry npm dan me-**restart** proses server. Hanya sah bila instance punya supervisor (`canApply` di `GET /api/update`); wajib `{ "confirm": true }` dan balasan 202 dikirim **sebelum** proses keluar |
|
|
150
145
|
|
|
151
146
|
## 4. Aturan gate & kode status
|
|
152
147
|
|
|
@@ -171,6 +166,26 @@ token selalu **403**, apa pun capability-nya, dan tak ada capability yang bisa m
|
|
|
171
166
|
- `/api/auth/*` — kelola user & password
|
|
172
167
|
- `/api/agent-tokens*` — agen tak boleh mencetak/menaikkan token sendiri
|
|
173
168
|
- `/api/device-tokens*`, `/api/sync*` — identitas mesin & sync hub
|
|
169
|
+
- **Tulis** ke `/api/portal*` (`POST …/tickets`, `POST …/chat/sessions`, `POST …/messages`) — itu
|
|
170
|
+
bertindak **sebagai klien** (tiket multipart; chat menghabiskan kuota klien dan tertulis sebagai
|
|
171
|
+
ucapannya): peniruan identitas, bukan capability. Membaca portal sebagai klien tersedia lewat
|
|
172
|
+
capability `portal:read` (ADR-0195)
|
|
173
|
+
|
|
174
|
+
> Dulu cookie-only, kini capability **opt-in** (manusia yang mencentangnya di Settings → Akses AI
|
|
175
|
+
> Agent): `/api/webhooks*` → `webhooks:*` (ADR-0194), `/api/client-accounts*` → `clients:*`
|
|
176
|
+
> (ADR-0194), `/api/telegram/{settings,test,credentials}` → `telegram:admin` (ADR-0193), dan
|
|
177
|
+
> **baca** `/api/portal*` → `portal:read` (ADR-0195). Lihat tabel §3.
|
|
178
|
+
- `/api/auth/{status,setup,login,logout}` — cookie sesi & bootstrap akun pertama (setup token di
|
|
179
|
+
`HANOMAN_HOME`). Agen tak punya sesi cookie, jadi tak ada yang bisa dibungkus bermakna. (Pengelolaan
|
|
180
|
+
akun — `/api/auth/users*` dan `change-password` — **dibuka** lewat `access:*`; lihat §5b.)
|
|
181
|
+
- `/api/sync*` — sync mesin-ke-mesin; kredensialnya Bearer **device token**, bukan agent token
|
|
182
|
+
- `/api/device-tokens*`, `/api/sync*` — identitas mesin & sync hub. **Pengecualian sempit (ADR-0199):**
|
|
183
|
+
tiga permukaan UI sync (`GET /api/sync/conflicts`, `GET /api/sync/pending`, `POST /api/sync/now`) dan
|
|
184
|
+
`POST /api/sync/conflicts/:entity/:recordId/resolve` terbuka lewat domain `sync` (§3). SEMUA sisanya
|
|
185
|
+
— `/api/sync/{pull,bootstrap,push,logs,attachments,qa-attachments,ws,relay/ws}` — adalah kanal
|
|
186
|
+
mesin-ke-mesin ber-**Bearer device-token** yang ditegakkan route-nya sendiri; agent token bukan
|
|
187
|
+
device token, dan membungkusnya berarti menyerahkan kredensial mesin ke agen. `POST /api/ws-tickets`
|
|
188
|
+
(tiket WebSocket sekali-pakai untuk sesi cookie) juga cookie-only
|
|
174
189
|
- `/api/webhooks*` — memegang secret penandatanganan **dan** menentukan ke mana data workspace
|
|
175
190
|
mengalir keluar (ADR-0100)
|
|
176
191
|
- `/api/telegram/settings`, `/api/telegram/test`, `/api/telegram/credentials` — permukaan
|
|
@@ -184,6 +199,15 @@ token selalu **403**, apa pun capability-nya, dan tak ada capability yang bisa m
|
|
|
184
199
|
bisa memalsukan "sesi X bertanya Y" bisa menggerakkan lead atas nama sesi mana pun — itu peniruan
|
|
185
200
|
identitas, bukan capability. Agent token di sini menerima **401** (token sesi tak cocok), bukan
|
|
186
201
|
403: gate cookie mem-bypass path-nya dan route menegakkan tokennya sendiri
|
|
202
|
+
- `/api/terminal/workspace` — tata letak tab/pane **browser** satu akun manusia (revisi optimistik per
|
|
203
|
+
`req.user.id`); agent token tak punya akun, dan menulisnya hanya merusak layout operator
|
|
204
|
+
- Frame siar `presence` di `/api/events/ws` — grup itu **tidak** dikirim ke principal non-cookie,
|
|
205
|
+
walau kanalnya sendiri `GLOBAL_READ`. Agen membaca peta yang sama lewat `GET /api/presence`
|
|
206
|
+
(`presence:read`, §5b). `PUT /api/settings` tetap tak bisa mengubah kunci `remoteControl`/
|
|
207
|
+
`logShipping`/`logRetention`; jalurnya hanya `PUT /api/remote-control` dan `PUT /api/logs/retention`
|
|
208
|
+
- `POST /api/update/apply` dan tulis lain di bawah prefix status (`/api/limits`, `/api/update`,
|
|
209
|
+
`/api/events`, `/api/fs`, `/api/health`, serta — sejak ADR-0187 — `/api/models`, `/api/methods`,
|
|
210
|
+
`/api/codex`) — **baca**-nya terbuka untuk token mana pun, **tulis**-nya cookie-only
|
|
187
211
|
- `/api/presence` — peta pekerjaan yang sedang berjalan di **seluruh mesin** operator: sesi apa
|
|
188
212
|
hidup di device mana (SPEC-919/ADR-0147). Tak ada capability yang bisa berarti sesuatu untuk
|
|
189
213
|
agregat lintas-mesin, jadi ia cookie-only apa pun method-nya. Frame siar `presence` di
|
|
@@ -198,13 +222,58 @@ token selalu **403**, apa pun capability-nya, dan tak ada capability yang bisa m
|
|
|
198
222
|
mesin mana", dan agen yang bisa membacanya atau memendekkan retensinya bisa menyembunyikan atau
|
|
199
223
|
memalsukan seluruh jejak sesi. Cookie-only apa pun method-nya — beda dari `POST /api/sync/logs`
|
|
200
224
|
(pengiriman batch **dari** klien), yang tetap device-token seperti jalur sync lain
|
|
201
|
-
-
|
|
202
|
-
`/api/
|
|
203
|
-
|
|
225
|
+
- Tulis lain di bawah prefix status (`/api/limits`, `/api/update`, `/api/events`, `/api/fs`,
|
|
226
|
+
`/api/health`) — **baca**-nya terbuka untuk token mana pun, **tulis**-nya cookie-only. Satu-satunya
|
|
227
|
+
pengecualian (ADR-0200): `POST /api/update/apply` dan `POST /api/restart` lewat capability danger
|
|
228
|
+
`instance:admin` (§3), tidak lewat capability lain
|
|
204
229
|
|
|
205
230
|
Route yang tak dikenal peta juga **default cookie-only** (aman): endpoint baru tak pernah terbuka
|
|
206
231
|
karena kelalaian. Endpoint `/api/help*` (Help Center publik) punya otorisasi sendiri (kunci tiket)
|
|
207
|
-
dan tak memakai agent token
|
|
232
|
+
dan tak memakai agent token: ia anonim by design (honeypot + rate-limit dirancang untuk manusia
|
|
233
|
+
pelapor), jadi **tak ada tool MCP** untuknya (ADR-0201) — operator membaca tiket lewat
|
|
234
|
+
`hanoman_tickets_list`/`hanoman_ticket_get` dan URL publik lewat `hanoman_help_center_get`.
|
|
235
|
+
|
|
236
|
+
## 5b. Permukaan admin yang dibuka opt-in (ADR-0196..0198)
|
|
237
|
+
|
|
238
|
+
Sampai ADR-0196..0198, `/api/agent-tokens*`, `/api/device-tokens*`, `/api/auth/users*`,
|
|
239
|
+
`/api/auth/change-password`, `/api/remote-control`, `/api/devices/:id/relay/*`, `/api/logs*`,
|
|
240
|
+
`/api/presence`, dan metrik custom agent cookie-only. Pemilik repo meminta mereka dibungkus, jadi
|
|
241
|
+
keputusan lama itu dibalik **dengan capability baru yang opt-in** (tak ada capability lama yang
|
|
242
|
+
membukanya) dan **pagar yang ditegakkan di server**, bukan hanya di katalog tool:
|
|
243
|
+
|
|
244
|
+
| Hal | Aturan |
|
|
245
|
+
|---|---|
|
|
246
|
+
| Cetak agent token (`POST /api/agent-tokens`) | `capabilities` token baru HARUS himpunan-bagian milik pemanggil (`write ⊇ read`; danger hanya bila dimiliki persis). Selebihnya **403** `{ error, exceeds: [...] }` |
|
|
247
|
+
| Ubah agent token (`PATCH`) | `capabilities` baru ⊆ milik pemanggil, **dan** token target tak boleh lebih berkuasa dari pemanggil (agen lemah tak bisa menonaktifkan/menurunkan token yang manusia beri hak lebih) |
|
|
248
|
+
| Cabut agent token (`DELETE`) | aturan target yang sama |
|
|
249
|
+
| Token sendiri | **tak bisa** diubah atau dicabut oleh dirinya sendiri (403 `{ self: true }`) — minta manusia |
|
|
250
|
+
| Plaintext token | hanya di balasan `POST` (agent maupun device token); tak pernah di daftar atau di log audit; runtime MCP melepas topeng token HANYA untuk hasil sukses dua tool pencetak |
|
|
251
|
+
| Device token | dicetak atas nama akun admin yang menerbitkan agent token pemanggil (rantai `agent:<id>` ditelusuri); tanpa penerbit manusia → **409**. Agen hanya melihat/mencabut device token milik akun itu |
|
|
252
|
+
| Akun | membuat akun admin, menghapus akun, menyetel ulang password akun lain = `access:admin` (danger). Reset password agen: `{ userId, newPassword }` tanpa password lama, mencabut semua sesi akun itu, dan **tidak** menerbitkan cookie. Admin terakhir tetap tak bisa dihapus. `access:admin` setara menyerahkan seluruh instance |
|
|
253
|
+
| Grant jarak jauh (`PUT /api/remote-control`) | `control.capabilities` ⊆ milik pemanggil; mengubah lajur log (data keluar ke hub) menuntut juga `logs:write` |
|
|
254
|
+
| Relay perangkat | `relay:exec` (danger) untuk semua method; aktor ditandai `agent:<id>`; allowlist route ditegakkan mesin tujuan; WebSocket relay tidak tersedia untuk agen |
|
|
255
|
+
| Jejak | setiap aksi admin oleh agent token tercatat di log event (`kind` `admin.*`) tanpa rahasia; perubahan retensi log dicatat **sebelum** nilainya berubah |
|
|
256
|
+
|
|
257
|
+
Tool MCP-nya (semua tulis bermode `danger`, hanya tampil dengan `--danger`):
|
|
258
|
+
|
|
259
|
+
| Tool | Mode | Capability |
|
|
260
|
+
|---|---|---|
|
|
261
|
+
| `hanoman_agent_tokens_list`, `hanoman_agent_token_capabilities` | baca | `tokens:read` |
|
|
262
|
+
| `hanoman_agent_token_create`, `hanoman_agent_token_update`, `hanoman_agent_token_revoke` | danger | `tokens:write` |
|
|
263
|
+
| `hanoman_device_tokens_list` | baca | `devices:read` |
|
|
264
|
+
| `hanoman_device_token_create`, `hanoman_device_token_revoke` | danger | `devices:write` |
|
|
265
|
+
| `hanoman_users_list` | baca | `access:read` |
|
|
266
|
+
| `hanoman_user_create`, `hanoman_user_delete`, `hanoman_user_password_reset` | danger | `access:admin` |
|
|
267
|
+
| `hanoman_remote_control_get` | baca | `remote:read` |
|
|
268
|
+
| `hanoman_remote_control_set` | danger | `remote:control` |
|
|
269
|
+
| `hanoman_device_relay` | danger | `relay:exec` |
|
|
270
|
+
| `hanoman_device_access_request` | danger | `relay:exec` |
|
|
271
|
+
| `hanoman_device_access_requests` | danger | `relay:exec` |
|
|
272
|
+
| `hanoman_logs_search`, `hanoman_log_transcript`, `hanoman_logs_retention_get` | baca | `logs:read` |
|
|
273
|
+
| `hanoman_logs_retention_set` | danger | `logs:write` |
|
|
274
|
+
| `hanoman_presence_get` | baca | `presence:read` |
|
|
275
|
+
| `hanoman_agent_metrics` | baca | `metrics:read` |
|
|
276
|
+
| `hanoman_agent_invocation_disposition` | danger | `metrics:write` |
|
|
208
277
|
|
|
209
278
|
## 6. Endpoint yang paling sering dipakai
|
|
210
279
|
|
|
@@ -218,61 +287,25 @@ dan tak memakai agent token.
|
|
|
218
287
|
| `POST /api/specs` | `backlog:write` | buat backlog item — bentuk payload di §7. |
|
|
219
288
|
| `PATCH /api/specs/:id` | `backlog:write` | ubah item; konten hanya selagi belum dimulai. |
|
|
220
289
|
| `POST /api/specs/:id/done` | `backlog:write` | tandai item **selesai** tanpa menjalankan sesi — untuk pekerjaan yang beres di luar hanoman. Body `{ reason?: string (≤280), confirm?: boolean }`, keduanya opsional; balasannya `Spec` yang sudah `stage:"done"`. Tak menjalankan maupun menghentikan sesi apa pun. |
|
|
290
|
+
| `POST /api/specs/:id/reviewed` | `backlog:write` | tandai atau batalkan **sudah ditinjau** pada item yang sudah **selesai** (tool `hanoman_backlog_mark_reviewed`, ADR-0218). Body `{ reviewed?: boolean }`, default `true`; `false` membatalkan. Idempoten: menandai ulang tak mengubah stempelnya. Balasannya `Spec` dengan `reviewedAt`. 409 bila item belum selesai. Tak mengubah stage, diff, atau berkas. |
|
|
221
291
|
| `GET /api/specs/:id/attachments` | `backlog:read` | lampiran backlog item — gambar & dokumen yang dilampirkan manusia sebagai konteks kerja. |
|
|
222
292
|
| `POST /api/specs/:id/attachments` | `backlog:write` | unggah lampiran (`multipart/form-data`, beberapa berkas per request). Berkas yang ditolak **tak** menggagalkan yang lain — periksa `rejected[]`. |
|
|
223
293
|
| `GET /api/specs/:id/attachments/:attId` | `backlog:read` | byte satu lampiran. |
|
|
224
294
|
| `DELETE /api/specs/:id/attachments/:attId` | `backlog:write` | hapus satu lampiran. |
|
|
225
295
|
| `GET /api/specs/:id/docs` | `backlog:read` | dokumen yang ditulis sesi item itu. |
|
|
226
|
-
| `GET /api/specs/:id/review` | `backlog:read` | diff hasil kerja sesi. |
|
|
296
|
+
| `GET /api/specs/:id/review` | `backlog:read` | diff hasil kerja sesi. Bila belum ada worktree maupun commit: **200** dengan `empty: true` dan `reason` — `no-worktree` (belum pernah dimulai) atau `no-commit` (worktree sudah lenyap tanpa commit); `base`, `files`, dan `changed` kosong. Itu keadaan, bukan galat (ADR-0213). Sesi memakai pola yang sama (ADR-0215). |
|
|
297
|
+
| `DELETE /api/specs/:id/worktree` | `ide:git` (danger) | hapus worktree satu item (tool `hanoman_backlog_worktree_delete`, ADR-0213). Perubahan yang belum di-commit di dalamnya **hilang** dan sesi yang berjalan di sana ditutup; cabang dan commit tetap. Balasan **200** `{ name, ok, cleanup, closedSession?, error? }` — `ok: false` bila worktree tak ditemukan atau tak bisa dihapus. Capability-nya sama dengan `POST /api/projects/:id/worktrees/delete`, bukan `backlog:write`. |
|
|
227
298
|
| `GET /api/projects/:id/docs` | `docs:read` | index Source of Truth project. |
|
|
228
299
|
| `GET /api/projects/:id/docs/<path>` | `docs:read` | isi satu dokumen. |
|
|
229
|
-
| `GET /api/projects/:id/changelog` | `docs:read` | changelog yang sudah dibangkitkan (paginated). |
|
|
230
|
-
| `POST /api/projects/:id/changelog` | `docs:write` | bangkitkan changelog baru — bentuknya di **§6a**. |
|
|
231
300
|
| `GET /api/terminal/sessions` | `sessions:read` | sesi yang sedang hidup. |
|
|
301
|
+
| `GET /api/terminal/sessions/:id/review` | `sessions:read` | diff worktree sesi project-level (PRD). Bentuknya sama dengan review backlog item. Bila sesi tak punya worktree (`no-worktree`) atau worktree-nya sudah lenyap tanpa commit (`no-commit`): **200** dengan `empty: true` dan `reason`; `base`, `files`, dan `changed` kosong. Itu keadaan, bukan galat (ADR-0215, amandemen ADR-0213). Tool `hanoman_session_review`. |
|
|
302
|
+
| `GET /api/terminal/sessions/:id/review/<path>` | `sessions:read` | isi diff satu berkas. **409** bila tak ada sumber sama sekali (tak ada berkas untuk dibaca); 404 bila path di luar daftar. |
|
|
232
303
|
| `GET /api/notifications` | `notifications:read` | notifikasi. **Tanpa `limit` → 50 teratas**, bukan seluruhnya (lihat jebakan di §10). |
|
|
233
304
|
| `GET /api/tickets` | `support:read` | tiket Help Center. |
|
|
234
305
|
| `GET /api/lead/decisions` | `lead:read` | jejak keputusan hanoman-lead. Menerima `page`/`limit`; `take`/`skip` lama tetap jalan. |
|
|
235
306
|
| `GET /api/scheduler/queue` | `settings:read` | antrean scheduler (`?status=queued\|launched\|done\|failed`). `GET /api/scheduler/state` **tak lagi** memuat `queue` — ia memuat `queueCounts`. |
|
|
236
307
|
| `POST /api/lead/decisions` | `lead:write` | minta putusan — baca **§8** dan **§11** dulu. |
|
|
237
308
|
|
|
238
|
-
## 6a. Changelog per project
|
|
239
|
-
|
|
240
|
-
Ringkasan perubahan **berorientasi pemakai** — bukan daftar commit. Tiga mode, satu endpoint;
|
|
241
|
-
`mode` menentukan field lainnya:
|
|
242
|
-
|
|
243
|
-
```bash
|
|
244
|
-
# 1) backlog yang selesai di rentang tanggal (dua field opsional → 30 hari terakhir)
|
|
245
|
-
curl -sS -X POST "$HANOMAN_HOST/api/projects/<id>/changelog" \
|
|
246
|
-
-H "Authorization: Bearer $HANOMAN_AGENT_TOKEN" -H 'Content-Type: application/json' \
|
|
247
|
-
-d '{"mode":"backlog","from":"2026-07-01","to":"2026-07-31"}'
|
|
248
|
-
|
|
249
|
-
# 2) rentang commit di repo project
|
|
250
|
-
-d '{"mode":"commit","fromSha":"4f2a1c9","toSha":"HEAD"}'
|
|
251
|
-
|
|
252
|
-
# 3) versi/tag rilis (fromTag opsional → sejak versi sebelumnya)
|
|
253
|
-
-d '{"mode":"version","toTag":"v1.2.0"}'
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
Jawaban **201** berisi `body` (markdown siap pakai), `title`, `itemCount`, `generator`, dan
|
|
257
|
-
`warning`. Ambil ulang atau unduh kapan saja:
|
|
258
|
-
|
|
259
|
-
```bash
|
|
260
|
-
curl -sS "$HANOMAN_HOST/api/projects/<id>/changelog/<cid>?download=md" \
|
|
261
|
-
-H "Authorization: Bearer $HANOMAN_AGENT_TOKEN"
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
Tiga hal yang perlu kamu tahu sebelum memanggilnya:
|
|
265
|
-
|
|
266
|
-
- **Panggil `GET /api/projects/:id/changelog/sources` dulu.** Ia memberi tag yang tersedia, HEAD
|
|
267
|
-
singkat, rentang default, dan — bila repo belum ditautkan di mesin itu atau belum punya tag — satu
|
|
268
|
-
`reason` yang menjelaskan sebabnya. Ia menjawab **200**, bukan galat, jadi jangan perlakukan
|
|
269
|
-
`reason` sebagai kegagalan.
|
|
270
|
-
- **422 berarti permintaanmu sah tapi tak ada isinya** (rentang kosong, repo tanpa tag, revisi tak
|
|
271
|
-
dikenal) — pesannya bisa langsung diteruskan ke manusia. **400** berarti bentuknya salah
|
|
272
|
-
(mis. `from` lebih baru dari `to`).
|
|
273
|
-
- **`generator:"fallback"` bukan kegagalan.** Artinya narasi otomatis tak tersedia dan yang kamu
|
|
274
|
-
terima adalah draf ringkas deterministik; alasannya ada di `warning`.
|
|
275
|
-
|
|
276
309
|
## 7. `POST /api/specs` — bentuk payload per `source`
|
|
277
310
|
|
|
278
311
|
`source` dan bentuk `payload` **saling mengikat**. Salah pasang → **400**
|
|
@@ -324,9 +357,11 @@ capability-nya. Capability menjawab "boleh?", bukan "sebaiknya?".
|
|
|
324
357
|
| `POST /api/terminal/sessions` | melahirkan proses agen `--dangerously-skip-permissions` di sebuah worktree — **RCE efektif**. Batas satu-satunya adalah isolasi git worktree (ADR-0037). |
|
|
325
358
|
| `POST`/`PUT`/`DELETE` di bawah `/api/vps` | **remote exec** di server produksi. |
|
|
326
359
|
| `POST /api/lead/decisions` | putusannya bisa **menggerakkan sesi** (integrate ke main, menghentikan sesi) dan selalu melahirkan baris jejak permanen (ADR-0091/0098). |
|
|
360
|
+
| `POST /api/update/apply`, `POST /api/restart` | memasang paket npm dan **me-restart server** (ADR-0200): dashboard & koneksi MCP putus beberapa detik; pane tmux selamat. Selalu `confirm: false` dulu untuk membaca jumlah sesi hidup. |
|
|
361
|
+
| `POST /api/sync/conflicts/:entity/:recordId/resolve` | **membuang data satu sisi** (lokal atau hub) tanpa jalan pulang (ADR-0199). Baca `differs` dari `hanoman_sync_conflicts` dulu. |
|
|
327
362
|
|
|
328
|
-
Perlakukan `POST /api/specs/:id/integrate`, `DELETE /api/specs/:id`,
|
|
329
|
-
disiplin yang sama:
|
|
363
|
+
Perlakukan `POST /api/specs/:id/integrate`, `DELETE /api/specs/:id`, `DELETE /api/specs/:id/worktree`,
|
|
364
|
+
dan perubahan `stage` dengan disiplin yang sama: keempatnya mengubah sejarah git atau membuang pekerjaan. `POST /api/specs/:id/done`
|
|
330
365
|
tak menyentuh git, tetapi ia **menyatakan pekerjaan orang lain selesai** — pakai hanya bila kamu punya
|
|
331
366
|
buktinya, dan tulis buktinya di `reason`.
|
|
332
367
|
|
|
@@ -506,7 +541,20 @@ Panduan siap salin untuk keempat klien, berikut tabel tool → capability, ada d
|
|
|
506
541
|
| `hanoman_lead_decisions_list` | baca | `lead:read` |
|
|
507
542
|
| `hanoman_backlog_create`, `hanoman_backlog_update` | tulis | `backlog:write` |
|
|
508
543
|
| `hanoman_notifications_mark_read` | tulis | `notifications:write` |
|
|
544
|
+
| `hanoman_notification_mark_read` | tulis | `notifications:write` — satu notifikasi berdasarkan `id` (ADR-0204), idempoten |
|
|
545
|
+
| `hanoman_scheduler_queue_cancel`, `hanoman_scheduler_queue_requeue` | tulis | `settings:write`; requeue baris `failed` juga menuntut `sessions:spawn` (ADR-0204, 403 bila tak ada) |
|
|
509
546
|
| `hanoman_lead_ask` | tulis | `lead:write` |
|
|
547
|
+
| `hanoman_health`, `hanoman_models`, `hanoman_codex_version`, `hanoman_methods_status` | baca | — (`GLOBAL_READ`, ADR-0187) |
|
|
548
|
+
| `hanoman_activity_list` | baca | `activity:read` |
|
|
549
|
+
| `hanoman_activity_purge` | **berbahaya** | `activity:write` |
|
|
550
|
+
| `hanoman_portal_admin_chat_sessions`, `hanoman_portal_admin_chat_session_get` | baca | `portal-chat:read` |
|
|
551
|
+
| `hanoman_portal_admin_chat_export` | **berbahaya** | `portal-chat:read` |
|
|
552
|
+
| `hanoman_portal_admin_chat_prd_write` | **berbahaya** | `portal-chat:write` |
|
|
553
|
+
| `hanoman_sync_conflicts`, `hanoman_sync_pending` | baca | `sync:read` |
|
|
554
|
+
| `hanoman_sync_now` | tulis | `sync:write` |
|
|
555
|
+
| `hanoman_sync_conflict_resolve` | **danger** | `sync:resolve` |
|
|
556
|
+
| `hanoman_update_apply`, `hanoman_instance_restart` | **danger** | `instance:admin` |
|
|
557
|
+
| `hanoman_agent_guide` | baca | — (membaca halaman ini lewat `GET /api/agent-integration.md`, publik) |
|
|
510
558
|
|
|
511
559
|
### Yang sengaja TIDAK tersedia lewat MCP
|
|
512
560
|
|
|
@@ -516,18 +564,70 @@ penghapusan backlog, dan perubahan `stage`. Batasan ini ada di katalog toolnya,
|
|
|
516
564
|
token yang punya `sessions:write` sekalipun tak akan menemukan tool untuk memakainya. Lihat §8.
|
|
517
565
|
|
|
518
566
|
**Menjawab dialog sesi** (`GET /api/terminal/sessions/:id/dialog`,
|
|
519
|
-
`POST /api/terminal/sessions/:id/dialog/answer`,
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
567
|
+
`POST /api/terminal/sessions/:id/dialog/answer`, `POST /api/terminal/sessions/:id/dialog/takeover`)
|
|
568
|
+
dulu sengaja tak punya tool (SPEC-899 · ADR-0142: agen yang bisa menjawab `AskUserQuestion` bisa
|
|
569
|
+
menjawab pertanyaannya sendiri, dan gerbang "manusia terakhir yang memutuskan" runtuh). Sejak
|
|
570
|
+
**ADR-0190/ADR-0192** pemilik membungkusnya sebagai tool `hanoman_session_dialog_get|answer|takeover`
|
|
571
|
+
(semuanya `danger`, hanya muncul dengan `--danger`) — dengan gerbang di **server**, bukan di
|
|
572
|
+
deskripsi:
|
|
573
|
+
|
|
574
|
+
- capability **`sessions:decide`** (domain `decisions`), terpisah dari `sessions:write`; `steer` ke
|
|
575
|
+
pane yang sedang menampilkan dialog pun menuntutnya;
|
|
576
|
+
- **403** bila token ITU yang meluncurkan sesinya (`reason: "launcher"`), atau bila panggilan datang
|
|
577
|
+
dari dalam sesi hanoman — proses MCP yang diwarisi `HANOMAN_SESSION_ID` mengirim header
|
|
578
|
+
`x-hanoman-session` (`reason: "self"` / `"in-session"`);
|
|
579
|
+
- **409** `deciding` (hanoman-lead sedang menyusun keputusan; rebut dulu lewat `takeover`) dan
|
|
580
|
+
`answering` tetap berlaku. 403 mendahului 409 supaya penolakan tak membocorkan keadaan lead.
|
|
581
|
+
|
|
582
|
+
**Batas yang jujur:** server tak bisa membuktikan siapa di balik sebuah request Bearer — sesi tidak
|
|
583
|
+
menerima agent token dari hanoman, jadi tak ada ikatan token↔sesi. Header `x-hanoman-session`
|
|
584
|
+
bisa dilepas oleh klien yang sengaja memakai token yang sama lewat `curl`. Yang menutupnya adalah
|
|
585
|
+
praktik: **jangan pasang token ber-`sessions:decide` di konfigurasi yang dibaca sesi hanoman**
|
|
586
|
+
(mis. `~/.claude.json` tingkat-user diwarisi setiap sesi). Pakai token terpisah, di klien MCP yang
|
|
587
|
+
dijalankan manusia di luar sesi.
|
|
588
|
+
|
|
589
|
+
**Cronjob** (`/api/scheduler/crons*`, ADR-0191 membalik ADR-0112) kini punya tool
|
|
590
|
+
`hanoman_scheduler_crons_list|cron_runs` (baca, `scheduler:read`) dan
|
|
591
|
+
`hanoman_scheduler_cron_create|update|delete|run` (`danger`, `scheduler:write`). Karena sebuah cron
|
|
592
|
+
adalah `POST /terminal/sessions` yang ditunda, **create, update yang menyentuh prompt/agen/model/
|
|
593
|
+
effort/jadwal atau mengaktifkan, dan run** juga menuntut `sessions:spawn` — ditegakkan di handler
|
|
594
|
+
server (403 `need: sessions:spawn`), bukan hanya disebut di deskripsi. Mengganti nama, menonaktifkan,
|
|
595
|
+
dan menghapus cron cukup dengan `scheduler:write`.
|
|
596
|
+
|
|
597
|
+
**Lampiran, ekspor/impor QA, unggah IDE, dan arsip git** punya tool sejak ADR-0183 — lihat
|
|
598
|
+
[Transfer berkas](#transfer-berkas-adr-0183) di bawah. Yang tetap tak punya tool hanya WebSocket
|
|
599
|
+
(`/terminal/sessions/:id/ws`, `/events/ws`) dan tiga route `dialog` di atas.
|
|
600
|
+
|
|
601
|
+
### Transfer berkas (ADR-0183)
|
|
602
|
+
|
|
603
|
+
Tool MCP berbicara JSON teks, jadi **byte tak pernah lewat model**. Satu mekanisme seragam:
|
|
604
|
+
|
|
605
|
+
- **Unggah** — argumen `filePath` / `filePaths` / `files[].filePath` adalah path **lokal di mesin
|
|
606
|
+
tempat `hanoman mcp` berjalan**. Klien membacanya dan mengirimnya sebagai multipart.
|
|
607
|
+
- **Unduh** — byte disimpan ke `savePath` lokal (default: direktori sementara aman); jawabannya
|
|
608
|
+
`{savedTo, filename, bytes, mime, sha256}`. `overwrite:true` wajib untuk menimpa berkas yang ada.
|
|
609
|
+
Teks kecil (template Markdown, ekspor `md`/`csv`) dikembalikan **inline** oleh
|
|
610
|
+
`hanoman_qa_template_md` dan `hanoman_qa_report_export_text`.
|
|
611
|
+
- **Batas (ADR-0184)** — path hanya boleh di dalam direktori kerja MCP, direktori sementara MCP,
|
|
612
|
+
atau `HANOMAN_MCP_TRANSFER_DIRS`; `..`, symlink yang keluar, dan berkas rahasia (`.env`,
|
|
613
|
+
kunci SSH, `.git`, …) ditolak di kedua arah; ukuran dibatasi (default 100 MiB).
|
|
614
|
+
- **Mode (ADR-0186)** — tool yang menyentuh disk klien bermode `write` walau capability-nya `:read`,
|
|
615
|
+
sehingga `--read-only` tak pernah menyentuh disk.
|
|
616
|
+
|
|
617
|
+
| Tool | Mode | Capability |
|
|
618
|
+
|---|---|---|
|
|
619
|
+
| `hanoman_backlog_attachment_upload`, `hanoman_backlog_attachment_download` | tulis | `backlog:write` / `backlog:read` |
|
|
620
|
+
| `hanoman_ticket_attachment_download` | tulis | `support:read` |
|
|
621
|
+
| `hanoman_qa_attachment_upload`, `hanoman_qa_import`, `hanoman_qa_cases_import` | tulis | `qa:write` |
|
|
622
|
+
| `hanoman_qa_attachment_download`, `hanoman_qa_template_xlsx`, `hanoman_qa_report_export_file` | tulis | `qa:read` |
|
|
623
|
+
| `hanoman_qa_template_md`, `hanoman_qa_report_export_text` (inline) | baca | `qa:read` |
|
|
624
|
+
| `hanoman_ide_upload` | tulis | `ide:write` |
|
|
625
|
+
| `hanoman_ide_archive_download` | tulis | `ide:read` |
|
|
626
|
+
| `hanoman_session_attachment_upload` (PNG/JPEG/WebP ≤ 5 MB; jawabannya path di SERVER) | tulis | `sessions:write` |
|
|
627
|
+
| `hanoman_qa_report_delete`, `hanoman_qa_case_delete`, `hanoman_qa_finding_delete`, `hanoman_qa_attachment_delete` | **berbahaya** | `qa:delete` (ADR-0185) |
|
|
628
|
+
|
|
629
|
+
`qa:delete` adalah akses `danger` yang **tak diimplikasikan `qa:write`**: token lama ber-`qa:write`
|
|
630
|
+
tak lagi bisa menghapus data QA sampai manusia mencentang `qa:delete` di Settings → Akses AI Agent.
|
|
531
631
|
|
|
532
632
|
### Opsi
|
|
533
633
|
|
|
@@ -536,6 +636,8 @@ ber-`backlog:read`/`backlog:write` — yang tak dipajang adalah **tool**-nya.
|
|
|
536
636
|
| `HANOMAN_HOST` / `--host <url>` | **Wajib.** Instance yang dituju. Agent token diterbitkan per-instance — token dari instance lain selalu 401 di sini, dan MCP server menjelaskannya, bukan meneruskan 401 telanjang. |
|
|
537
637
|
| `HANOMAN_AGENT_TOKEN` | **Wajib.** Hanya dari env atau `~/.hanoman/agent-token` — **tak pernah** dari argumen baris perintah (ARGV terbaca proses lain di mesin yang sama). |
|
|
538
638
|
| `HANOMAN_MCP_READ_ONLY=1` / `--read-only` | Menyembunyikan seluruh tool tulis dari `tools/list`. |
|
|
639
|
+
| `HANOMAN_MCP_TRANSFER_DIRS` / `--transfer-dir <d>` | Direktori tambahan (dipisah `:`) yang boleh dibaca/ditulis tool transfer berkas, di luar direktori kerja MCP dan direktori sementara MCP. Akar filesystem ditolak. |
|
|
640
|
+
| `HANOMAN_MCP_TRANSFER_MAX_BYTES` / `--transfer-max-bytes <n>` | Plafon ukuran per berkas dan total per panggilan transfer. Default 100 MiB, maks 2 GiB. |
|
|
539
641
|
| `HANOMAN_MCP_MAX_BYTES` / `--max-bytes <n>` | Plafon ukuran balasan tool. Default 24576. Balasan yang dipotong ditandai `truncated: true` + `shown`/`total`. |
|
|
540
642
|
|
|
541
643
|
Skema tool berversi (`MCP_TOOL_SCHEMA_VERSION`, saat ini **1**) dan aditif dalam satu versi:
|
|
@@ -547,3 +649,12 @@ menambah tool tak mematahkan klien lama.
|
|
|
547
649
|
untuk permukaan MCP, [ADR-0099](../internal/docs/adr/0099-mcp-server-hanoman.md). Kontrak API penuh:
|
|
548
650
|
[`internal/docs/architecture/api-contract.md`](../internal/docs/architecture/api-contract.md) —
|
|
549
651
|
permukaan REST-nya identik dengan yang dipakai dashboard.*
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
## Fitur yang dihapus — skema tool MCP versi 2
|
|
655
|
+
|
|
656
|
+
Memori project dan changelog dihapus (ADR-0182). Tool `hanoman_memory_*` dan
|
|
657
|
+
`hanoman_changelog_*` tidak tersedia; endpoint `/api/memories*` dan
|
|
658
|
+
`/api/projects/:id/changelog*` tidak terdaftar. Token tidak lagi memiliki
|
|
659
|
+
capability `memory:*` atau allowlist project khusus memori. Fungsi context/memory
|
|
660
|
+
kanal Telegram tetap tersedia melalui domain `telegram`.
|
package/package.json
CHANGED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
-- ADR-0182: user requested removal of these features AND their stored data.
|
|
2
|
+
-- Purge sync payloads/outboxes first so retired records cannot be replayed.
|
|
3
|
+
DELETE FROM "SyncLog" WHERE "entity" IN ('projectMemory', 'memoryEvent');
|
|
4
|
+
DELETE FROM "SyncOutbox" WHERE "entity" IN ('projectMemory', 'memoryEvent');
|
|
5
|
+
DELETE FROM "SyncConflict" WHERE "entity" IN ('projectMemory', 'memoryEvent');
|
|
6
|
+
DELETE FROM "SyncTombstone" WHERE "entity" IN ('projectMemory', 'memoryEvent');
|
|
7
|
+
UPDATE "SyncState" SET "entities" = trim(replace(replace(',' || "entities" || ',', ',projectMemory,', ','), ',memoryEvent,', ','), ',');
|
|
8
|
+
DELETE FROM "Notification" WHERE "type" = 'memory';
|
|
9
|
+
UPDATE "Setting" SET "data" = json_remove("data", '$.changelog') WHERE json_type("data", '$.changelog') IS NOT NULL;
|
|
10
|
+
UPDATE "AgentToken" SET "capabilities" = (
|
|
11
|
+
SELECT json_group_array(value) FROM json_each("AgentToken"."capabilities")
|
|
12
|
+
WHERE value NOT IN ('memory:read', 'memory:write')
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
DROP TABLE "MemoryEvent";
|
|
16
|
+
DROP TABLE "MemoryLocalState";
|
|
17
|
+
DROP TABLE "ProjectMemory";
|
|
18
|
+
DROP TABLE "Changelog";
|
|
19
|
+
|
|
20
|
+
PRAGMA defer_foreign_keys=ON;
|
|
21
|
+
PRAGMA foreign_keys=OFF;
|
|
22
|
+
CREATE TABLE "new_AgentToken" (
|
|
23
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
24
|
+
"name" TEXT NOT NULL,
|
|
25
|
+
"tokenHash" TEXT NOT NULL,
|
|
26
|
+
"tokenPrefix" TEXT NOT NULL,
|
|
27
|
+
"capabilities" JSONB NOT NULL,
|
|
28
|
+
"enabled" BOOLEAN NOT NULL DEFAULT true,
|
|
29
|
+
"createdBy" TEXT,
|
|
30
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
31
|
+
"lastUsedAt" DATETIME,
|
|
32
|
+
"revokedAt" DATETIME
|
|
33
|
+
);
|
|
34
|
+
INSERT INTO "new_AgentToken" ("capabilities", "createdAt", "createdBy", "enabled", "id", "lastUsedAt", "name", "revokedAt", "tokenHash", "tokenPrefix") SELECT "capabilities", "createdAt", "createdBy", "enabled", "id", "lastUsedAt", "name", "revokedAt", "tokenHash", "tokenPrefix" FROM "AgentToken";
|
|
35
|
+
DROP TABLE "AgentToken";
|
|
36
|
+
ALTER TABLE "new_AgentToken" RENAME TO "AgentToken";
|
|
37
|
+
CREATE UNIQUE INDEX "AgentToken_tokenHash_key" ON "AgentToken"("tokenHash");
|
|
38
|
+
PRAGMA foreign_keys=ON;
|
|
39
|
+
PRAGMA defer_foreign_keys=OFF;
|
|
40
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-- ADR-0218 · penanda "sudah ditinjau" pada item selesai (filter Backlog "Perlu ditinjau").
|
|
2
|
+
--
|
|
3
|
+
-- Ditulis tangan (bukan `migrate dev`), pola SPEC-516/SPEC-804: worktree tetangga membuat
|
|
4
|
+
-- `migrate dev` me-reset DB saat ada drift. ADITIF murni — satu kolom NULLABLE tanpa default.
|
|
5
|
+
ALTER TABLE "Spec" ADD COLUMN "reviewedAt" DATETIME;
|
|
6
|
+
|
|
7
|
+
-- Backfill sekali-jalan: item yang SUDAH selesai dianggap sudah ditinjau pada `updatedAt`-nya.
|
|
8
|
+
-- Tanpa ini setiap item selesai yang pernah dikerjakan sesi (baseSha terisi) langsung masuk
|
|
9
|
+
-- "Perlu ditinjau" begitu migrasi jalan, dan filter yang sejak hari pertama berisi ratusan baris
|
|
10
|
+
-- tak lagi bisa dipercaya. Harganya diterima dan ditulis di ADR-0218: penanda hanya berlaku untuk
|
|
11
|
+
-- penyelesaian SETELAH migrasi ini. Item lama yang belum pernah ditinjau tetap bisa dibuka lewat
|
|
12
|
+
-- Review perubahan, hanya saja tidak lagi disorot oleh filter.
|
|
13
|
+
UPDATE "Spec" SET "reviewedAt" = "updatedAt" WHERE "stage" = 'done' AND "reviewedAt" IS NULL;
|
package/prisma/schema.prisma
CHANGED
|
@@ -32,12 +32,10 @@ model Project {
|
|
|
32
32
|
tickets Ticket[]
|
|
33
33
|
customAgents CustomAgent[] // SPEC-450 · ADR-0094 · custom agent milik project ini
|
|
34
34
|
githubIssues GithubIssue[] // SPEC-471 · ADR-0095 · cermin issue repo GitHub project ini
|
|
35
|
-
changelogs Changelog[] // SPEC-516 · ADR-0105 · changelog tersimpan project ini
|
|
36
35
|
clientAccess ClientProjectAccess[] // SPEC-617 · ADR-0110 · akun klien yang boleh melihat project ini
|
|
37
36
|
portalChats PortalChatSession[] // SPEC-854 · ADR-0129 · percakapan portal klien project ini
|
|
38
37
|
tasks Task[] // SPEC-945 · ADR-0150 · kartu kerja manusia di project ini
|
|
39
38
|
qaReports QaReport[] // Workspace QA · laporan QA project ini
|
|
40
|
-
memories ProjectMemory[] // ADR-0178 · memori project bersama lintas runtime
|
|
41
39
|
}
|
|
42
40
|
|
|
43
41
|
model Spec {
|
|
@@ -77,6 +75,12 @@ model Spec {
|
|
|
77
75
|
// ditandai manual. Ditimpa tiap penandaan berikutnya: ia menjelaskan keadaan yang BERLAKU;
|
|
78
76
|
// riwayat transisi stage tinggal di SessionResult (ADR-0047). Ikut FIELDS.spec sync.
|
|
79
77
|
manualDone Json?
|
|
78
|
+
// ADR-0218 · kapan operator menandai item SELESAI ini sudah ditinjau ("Tandai ditinjau"). null =
|
|
79
|
+
// belum ditinjau, atau item belum selesai. Ditulis HANYA oleh `POST /specs/:id/reviewed`, dan
|
|
80
|
+
// di-reset ke null saat stage dikembalikan mundur dari `done` (PATCH stage, POST source). SENGAJA
|
|
81
|
+
// tak di-reset oleh recordCompletion: reconcile memanggilnya ulang untuk item yang sudah selesai,
|
|
82
|
+
// dan itu akan menghapus tanda yang baru dipasang. Ikut FIELDS.spec sync (ADR-0218).
|
|
83
|
+
reviewedAt DateTime?
|
|
80
84
|
// SPEC-447 · ADR-0093 · id spec lain yang harus SELESAI & commit-nya sudah ada di branch basis
|
|
81
85
|
// sebelum item ini boleh diluncurkan. Array JSON of string; null/[] = tak bergantung apa pun.
|
|
82
86
|
// Sengaja kolom, bukan tabel join: SQLite melarang scalar list, `Json` sudah dipakai `payload`,
|
|
@@ -106,7 +110,7 @@ model Spec {
|
|
|
106
110
|
// `storageKey` opaque, `filename` nama asli tersanitasi untuk tampilan saja.
|
|
107
111
|
//
|
|
108
112
|
// LOCAL-only: TANPA `version`, jadi ia tak pernah masuk changefeed sync (cermin LeadFlow,
|
|
109
|
-
// WebhookEndpoint
|
|
113
|
+
// WebhookEndpoint). Byte memang tak menyeberang (ADR-0062), dan metadata yang menyeberang
|
|
110
114
|
// TANPA byte-nya hanya menghasilkan lampiran yang tak bisa dibuka di mesin lain — keadaan terburuk
|
|
111
115
|
// dari ketiga pilihan. Sengaja BUKAN TicketAttachment: tiket dan backlog dua domain dengan aturan
|
|
112
116
|
// sync dan tingkat kepercayaan yang berbeda (ADR-0124 §1/§4).
|
|
@@ -124,29 +128,6 @@ model SpecAttachment {
|
|
|
124
128
|
@@index([specId])
|
|
125
129
|
}
|
|
126
130
|
|
|
127
|
-
// SPEC-516 · ADR-0105 · changelog naratif per project yang sudah dibangkitkan.
|
|
128
|
-
//
|
|
129
|
-
// LOCAL-only: TANPA kolom `version`, jadi ia tak pernah masuk changefeed sync (cermin LeadFlow,
|
|
130
|
-
// WebhookEndpoint, dan Project.autoMerge). Dua dari tiga modenya diturunkan dari checkout git di
|
|
131
|
-
// MESIN INI, jadi barisnya fakta lokal; yang portabel adalah keluarannya, dan jalannya sudah ada
|
|
132
|
-
// — unduh .md.
|
|
133
|
-
model Changelog {
|
|
134
|
-
id String @id @default(cuid())
|
|
135
|
-
projectId String
|
|
136
|
-
mode String // "backlog" | "commit" | "version" (zChangelogMode di @hanoman/shared)
|
|
137
|
-
title String // judul di daftar: rentang tanggal / nama tag / rentang tag
|
|
138
|
-
params Json // parameter pembangkitan apa adanya (zChangelogRequest)
|
|
139
|
-
body String // markdown hasil akhir, sudah lewat scrubOutput
|
|
140
|
-
generator String // "agent" | "fallback"
|
|
141
|
-
warning String? // alasan fallback + catatan cakupan; null = mulus
|
|
142
|
-
itemCount Int @default(0)
|
|
143
|
-
createdAt DateTime @default(now())
|
|
144
|
-
updatedAt DateTime @updatedAt
|
|
145
|
-
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
|
146
|
-
|
|
147
|
-
@@index([projectId, createdAt])
|
|
148
|
-
}
|
|
149
|
-
|
|
150
131
|
// SPEC-450 · ADR-0094 · custom agent: persona yang bisa dipilih ulang & dibagi.
|
|
151
132
|
// `projectId` null = GLOBAL (semua project); terisi = milik satu project, dan agen project
|
|
152
133
|
// MENIMPA agen global bernama sama.
|
|
@@ -439,8 +420,6 @@ model AgentToken {
|
|
|
439
420
|
tokenHash String @unique
|
|
440
421
|
tokenPrefix String
|
|
441
422
|
capabilities Json
|
|
442
|
-
// ADR-0178 · allowlist project. null/[] = token tak boleh menyentuh route memori sama sekali.
|
|
443
|
-
projectIds Json?
|
|
444
423
|
enabled Boolean @default(true)
|
|
445
424
|
createdBy String?
|
|
446
425
|
createdAt DateTime @default(now())
|
|
@@ -1159,55 +1138,3 @@ model QaAttachment {
|
|
|
1159
1138
|
@@index([ownerType, ownerId])
|
|
1160
1139
|
@@index([syncState])
|
|
1161
1140
|
}
|
|
1162
|
-
|
|
1163
|
-
// ADR-0178 · satu fakta per baris. content/anchors/scopePaths/kind TAK PERNAH diedit — koreksi =
|
|
1164
|
-
// baris baru dengan `supersedesId`. Hanya `status` yang bergerak, dan hanya naik (lattice).
|
|
1165
|
-
model ProjectMemory {
|
|
1166
|
-
id String @id @default(cuid())
|
|
1167
|
-
projectId String
|
|
1168
|
-
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
|
1169
|
-
kind String // convention | gotcha | decision | fact
|
|
1170
|
-
content String
|
|
1171
|
-
scopePaths Json // string[] glob relatif root repo; [] = seluruh project
|
|
1172
|
-
anchors Json // [{path, blobSha, lines?}]
|
|
1173
|
-
status String // proposed | active | invalidated | rejected
|
|
1174
|
-
supersedesId String?
|
|
1175
|
-
reviewReason String? // decision | no-anchor | anchor-unverified | untrusted-source
|
|
1176
|
-
sourceRuntime String // claude | codex | external | human
|
|
1177
|
-
sourceSessionId String?
|
|
1178
|
-
sourceTokenId String?
|
|
1179
|
-
sourceDeviceId String? // diisi tahap sync
|
|
1180
|
-
commitSha String?
|
|
1181
|
-
trusted Boolean @default(true)
|
|
1182
|
-
version Int @default(0)
|
|
1183
|
-
createdAt DateTime @default(now())
|
|
1184
|
-
updatedAt DateTime @updatedAt
|
|
1185
|
-
events MemoryEvent[]
|
|
1186
|
-
|
|
1187
|
-
@@index([projectId, status])
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
// ADR-0178 · jejak audit append-only. Tak pernah di-update atau dihapus kecuali ikut cascade.
|
|
1191
|
-
model MemoryEvent {
|
|
1192
|
-
id String @id @default(cuid())
|
|
1193
|
-
memoryId String
|
|
1194
|
-
memory ProjectMemory @relation(fields: [memoryId], references: [id], onDelete: Cascade)
|
|
1195
|
-
op String // propose | activate | reject | invalidate | supersede | reverify
|
|
1196
|
-
actorKind String // user | token | session | system
|
|
1197
|
-
actorId String?
|
|
1198
|
-
reason String?
|
|
1199
|
-
createdAt DateTime @default(now())
|
|
1200
|
-
version Int @default(0) // ADR-0180 · wajib bagi entitas sync
|
|
1201
|
-
updatedAt DateTime @updatedAt
|
|
1202
|
-
|
|
1203
|
-
@@index([memoryId, createdAt])
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
// ADR-0178 · LOCAL-ONLY (tak pernah disync): hasil verifikasi jangkar bergantung HEAD mesin ini.
|
|
1207
|
-
model MemoryLocalState {
|
|
1208
|
-
memoryId String @id
|
|
1209
|
-
verdict String // valid | stale | unverifiable
|
|
1210
|
-
verifiedHead String?
|
|
1211
|
-
lastVerifiedAt DateTime?
|
|
1212
|
-
lastUsedAt DateTime?
|
|
1213
|
-
}
|