boten-gemma 0.1.0

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.
Files changed (334) hide show
  1. package/README.md +860 -0
  2. package/dist/automation/cron/index.d.ts +6 -0
  3. package/dist/automation/cron/index.d.ts.map +1 -0
  4. package/dist/automation/cron/index.js +4 -0
  5. package/dist/automation/cron/index.js.map +1 -0
  6. package/dist/automation/cron/parse-schedule.d.ts +11 -0
  7. package/dist/automation/cron/parse-schedule.d.ts.map +1 -0
  8. package/dist/automation/cron/parse-schedule.js +36 -0
  9. package/dist/automation/cron/parse-schedule.js.map +1 -0
  10. package/dist/automation/cron/schedule.d.ts +7 -0
  11. package/dist/automation/cron/schedule.d.ts.map +1 -0
  12. package/dist/automation/cron/schedule.js +28 -0
  13. package/dist/automation/cron/schedule.js.map +1 -0
  14. package/dist/automation/cron/service.d.ts +70 -0
  15. package/dist/automation/cron/service.d.ts.map +1 -0
  16. package/dist/automation/cron/service.js +207 -0
  17. package/dist/automation/cron/service.js.map +1 -0
  18. package/dist/automation/cron/store.d.ts +12 -0
  19. package/dist/automation/cron/store.d.ts.map +1 -0
  20. package/dist/automation/cron/store.js +27 -0
  21. package/dist/automation/cron/store.js.map +1 -0
  22. package/dist/automation/cron/types.d.ts +67 -0
  23. package/dist/automation/cron/types.d.ts.map +1 -0
  24. package/dist/automation/cron/types.js +5 -0
  25. package/dist/automation/cron/types.js.map +1 -0
  26. package/dist/automation/cron.d.ts +15 -0
  27. package/dist/automation/cron.d.ts.map +1 -0
  28. package/dist/automation/cron.js +10 -0
  29. package/dist/automation/cron.js.map +1 -0
  30. package/dist/automation/heartbeat.d.ts +14 -0
  31. package/dist/automation/heartbeat.d.ts.map +1 -0
  32. package/dist/automation/heartbeat.js +27 -0
  33. package/dist/automation/heartbeat.js.map +1 -0
  34. package/dist/channels/cli.d.ts +17 -0
  35. package/dist/channels/cli.d.ts.map +1 -0
  36. package/dist/channels/cli.js +125 -0
  37. package/dist/channels/cli.js.map +1 -0
  38. package/dist/channels/telegram.d.ts +27 -0
  39. package/dist/channels/telegram.d.ts.map +1 -0
  40. package/dist/channels/telegram.js +288 -0
  41. package/dist/channels/telegram.js.map +1 -0
  42. package/dist/channels/types.d.ts +50 -0
  43. package/dist/channels/types.d.ts.map +1 -0
  44. package/dist/channels/types.js +6 -0
  45. package/dist/channels/types.js.map +1 -0
  46. package/dist/channels/web.d.ts +149 -0
  47. package/dist/channels/web.d.ts.map +1 -0
  48. package/dist/channels/web.js +1113 -0
  49. package/dist/channels/web.js.map +1 -0
  50. package/dist/config/defaults.d.ts +11 -0
  51. package/dist/config/defaults.d.ts.map +1 -0
  52. package/dist/config/defaults.js +80 -0
  53. package/dist/config/defaults.js.map +1 -0
  54. package/dist/config/loader.d.ts +22 -0
  55. package/dist/config/loader.d.ts.map +1 -0
  56. package/dist/config/loader.js +158 -0
  57. package/dist/config/loader.js.map +1 -0
  58. package/dist/config/schema.d.ts +261 -0
  59. package/dist/config/schema.d.ts.map +1 -0
  60. package/dist/config/schema.js +119 -0
  61. package/dist/config/schema.js.map +1 -0
  62. package/dist/confirm/audit.d.ts +34 -0
  63. package/dist/confirm/audit.d.ts.map +1 -0
  64. package/dist/confirm/audit.js +49 -0
  65. package/dist/confirm/audit.js.map +1 -0
  66. package/dist/confirm/auto-approve.d.ts +20 -0
  67. package/dist/confirm/auto-approve.d.ts.map +1 -0
  68. package/dist/confirm/auto-approve.js +62 -0
  69. package/dist/confirm/auto-approve.js.map +1 -0
  70. package/dist/confirm/gate.d.ts +50 -0
  71. package/dist/confirm/gate.d.ts.map +1 -0
  72. package/dist/confirm/gate.js +138 -0
  73. package/dist/confirm/gate.js.map +1 -0
  74. package/dist/confirm/parser.d.ts +23 -0
  75. package/dist/confirm/parser.d.ts.map +1 -0
  76. package/dist/confirm/parser.js +76 -0
  77. package/dist/confirm/parser.js.map +1 -0
  78. package/dist/confirm/presenter.d.ts +23 -0
  79. package/dist/confirm/presenter.d.ts.map +1 -0
  80. package/dist/confirm/presenter.js +92 -0
  81. package/dist/confirm/presenter.js.map +1 -0
  82. package/dist/core/agent-loop.d.ts +44 -0
  83. package/dist/core/agent-loop.d.ts.map +1 -0
  84. package/dist/core/agent-loop.js +156 -0
  85. package/dist/core/agent-loop.js.map +1 -0
  86. package/dist/core/compaction.d.ts +17 -0
  87. package/dist/core/compaction.d.ts.map +1 -0
  88. package/dist/core/compaction.js +70 -0
  89. package/dist/core/compaction.js.map +1 -0
  90. package/dist/core/context-builder.d.ts +38 -0
  91. package/dist/core/context-builder.d.ts.map +1 -0
  92. package/dist/core/context-builder.js +137 -0
  93. package/dist/core/context-builder.js.map +1 -0
  94. package/dist/core/logger.d.ts +42 -0
  95. package/dist/core/logger.d.ts.map +1 -0
  96. package/dist/core/logger.js +95 -0
  97. package/dist/core/logger.js.map +1 -0
  98. package/dist/core/queue.d.ts +25 -0
  99. package/dist/core/queue.d.ts.map +1 -0
  100. package/dist/core/queue.js +33 -0
  101. package/dist/core/queue.js.map +1 -0
  102. package/dist/core/router.d.ts +21 -0
  103. package/dist/core/router.d.ts.map +1 -0
  104. package/dist/core/router.js +27 -0
  105. package/dist/core/router.js.map +1 -0
  106. package/dist/core/session-store.d.ts +53 -0
  107. package/dist/core/session-store.d.ts.map +1 -0
  108. package/dist/core/session-store.js +367 -0
  109. package/dist/core/session-store.js.map +1 -0
  110. package/dist/core/session.d.ts +89 -0
  111. package/dist/core/session.d.ts.map +1 -0
  112. package/dist/core/session.js +49 -0
  113. package/dist/core/session.js.map +1 -0
  114. package/dist/daemon/launchd.d.ts +8 -0
  115. package/dist/daemon/launchd.d.ts.map +1 -0
  116. package/dist/daemon/launchd.js +155 -0
  117. package/dist/daemon/launchd.js.map +1 -0
  118. package/dist/daemon/service.d.ts +12 -0
  119. package/dist/daemon/service.d.ts.map +1 -0
  120. package/dist/daemon/service.js +42 -0
  121. package/dist/daemon/service.js.map +1 -0
  122. package/dist/daemon/systemd.d.ts +11 -0
  123. package/dist/daemon/systemd.d.ts.map +1 -0
  124. package/dist/daemon/systemd.js +145 -0
  125. package/dist/daemon/systemd.js.map +1 -0
  126. package/dist/daemon/types.d.ts +14 -0
  127. package/dist/daemon/types.d.ts.map +1 -0
  128. package/dist/daemon/types.js +2 -0
  129. package/dist/daemon/types.js.map +1 -0
  130. package/dist/daemon/update.d.ts +41 -0
  131. package/dist/daemon/update.d.ts.map +1 -0
  132. package/dist/daemon/update.js +106 -0
  133. package/dist/daemon/update.js.map +1 -0
  134. package/dist/gateway.d.ts +103 -0
  135. package/dist/gateway.d.ts.map +1 -0
  136. package/dist/gateway.js +1063 -0
  137. package/dist/gateway.js.map +1 -0
  138. package/dist/index.d.ts +3 -0
  139. package/dist/index.d.ts.map +1 -0
  140. package/dist/index.js +572 -0
  141. package/dist/index.js.map +1 -0
  142. package/dist/llm/auth/oauth-callback.d.ts +20 -0
  143. package/dist/llm/auth/oauth-callback.d.ts.map +1 -0
  144. package/dist/llm/auth/oauth-callback.js +95 -0
  145. package/dist/llm/auth/oauth-callback.js.map +1 -0
  146. package/dist/llm/auth/oauth.d.ts +30 -0
  147. package/dist/llm/auth/oauth.d.ts.map +1 -0
  148. package/dist/llm/auth/oauth.js +331 -0
  149. package/dist/llm/auth/oauth.js.map +1 -0
  150. package/dist/llm/auth/resolver.d.ts +24 -0
  151. package/dist/llm/auth/resolver.d.ts.map +1 -0
  152. package/dist/llm/auth/resolver.js +97 -0
  153. package/dist/llm/auth/resolver.js.map +1 -0
  154. package/dist/llm/auth/token-store.d.ts +15 -0
  155. package/dist/llm/auth/token-store.d.ts.map +1 -0
  156. package/dist/llm/auth/token-store.js +94 -0
  157. package/dist/llm/auth/token-store.js.map +1 -0
  158. package/dist/llm/auth/types.d.ts +20 -0
  159. package/dist/llm/auth/types.d.ts.map +1 -0
  160. package/dist/llm/auth/types.js +3 -0
  161. package/dist/llm/auth/types.js.map +1 -0
  162. package/dist/llm/gemini.d.ts +28 -0
  163. package/dist/llm/gemini.d.ts.map +1 -0
  164. package/dist/llm/gemini.js +524 -0
  165. package/dist/llm/gemini.js.map +1 -0
  166. package/dist/llm/openai-compat.d.ts +14 -0
  167. package/dist/llm/openai-compat.d.ts.map +1 -0
  168. package/dist/llm/openai-compat.js +200 -0
  169. package/dist/llm/openai-compat.js.map +1 -0
  170. package/dist/llm/provider.d.ts +2 -0
  171. package/dist/llm/provider.d.ts.map +1 -0
  172. package/dist/llm/provider.js +4 -0
  173. package/dist/llm/provider.js.map +1 -0
  174. package/dist/llm/types.d.ts +75 -0
  175. package/dist/llm/types.d.ts.map +1 -0
  176. package/dist/llm/types.js +3 -0
  177. package/dist/llm/types.js.map +1 -0
  178. package/dist/mcp/adapter.d.ts +14 -0
  179. package/dist/mcp/adapter.d.ts.map +1 -0
  180. package/dist/mcp/adapter.js +58 -0
  181. package/dist/mcp/adapter.js.map +1 -0
  182. package/dist/mcp/index.d.ts +4 -0
  183. package/dist/mcp/index.d.ts.map +1 -0
  184. package/dist/mcp/index.js +3 -0
  185. package/dist/mcp/index.js.map +1 -0
  186. package/dist/mcp/manager.d.ts +48 -0
  187. package/dist/mcp/manager.d.ts.map +1 -0
  188. package/dist/mcp/manager.js +109 -0
  189. package/dist/mcp/manager.js.map +1 -0
  190. package/dist/memory/daily-log.d.ts +19 -0
  191. package/dist/memory/daily-log.d.ts.map +1 -0
  192. package/dist/memory/daily-log.js +47 -0
  193. package/dist/memory/daily-log.js.map +1 -0
  194. package/dist/memory/memory-manager.d.ts +41 -0
  195. package/dist/memory/memory-manager.d.ts.map +1 -0
  196. package/dist/memory/memory-manager.js +97 -0
  197. package/dist/memory/memory-manager.js.map +1 -0
  198. package/dist/memory/workspace.d.ts +21 -0
  199. package/dist/memory/workspace.d.ts.map +1 -0
  200. package/dist/memory/workspace.js +63 -0
  201. package/dist/memory/workspace.js.map +1 -0
  202. package/dist/panels/bridge.d.ts +19 -0
  203. package/dist/panels/bridge.d.ts.map +1 -0
  204. package/dist/panels/bridge.js +74 -0
  205. package/dist/panels/bridge.js.map +1 -0
  206. package/dist/panels/registry.d.ts +30 -0
  207. package/dist/panels/registry.d.ts.map +1 -0
  208. package/dist/panels/registry.js +102 -0
  209. package/dist/panels/registry.js.map +1 -0
  210. package/dist/panels/routes.d.ts +8 -0
  211. package/dist/panels/routes.d.ts.map +1 -0
  212. package/dist/panels/routes.js +252 -0
  213. package/dist/panels/routes.js.map +1 -0
  214. package/dist/panels/sdk/gemma-panel.js +331 -0
  215. package/dist/panels/service-context.d.ts +28 -0
  216. package/dist/panels/service-context.d.ts.map +1 -0
  217. package/dist/panels/service-context.js +84 -0
  218. package/dist/panels/service-context.js.map +1 -0
  219. package/dist/panels/service-loader.d.ts +19 -0
  220. package/dist/panels/service-loader.d.ts.map +1 -0
  221. package/dist/panels/service-loader.js +61 -0
  222. package/dist/panels/service-loader.js.map +1 -0
  223. package/dist/panels/service-manager.d.ts +40 -0
  224. package/dist/panels/service-manager.d.ts.map +1 -0
  225. package/dist/panels/service-manager.js +148 -0
  226. package/dist/panels/service-manager.js.map +1 -0
  227. package/dist/panels/storage.d.ts +14 -0
  228. package/dist/panels/storage.d.ts.map +1 -0
  229. package/dist/panels/storage.js +47 -0
  230. package/dist/panels/storage.js.map +1 -0
  231. package/dist/panels/types.d.ts +39 -0
  232. package/dist/panels/types.d.ts.map +1 -0
  233. package/dist/panels/types.js +8 -0
  234. package/dist/panels/types.js.map +1 -0
  235. package/dist/shared/ws-types.d.ts +332 -0
  236. package/dist/shared/ws-types.d.ts.map +1 -0
  237. package/dist/shared/ws-types.js +7 -0
  238. package/dist/shared/ws-types.js.map +1 -0
  239. package/dist/skills/gating.d.ts +15 -0
  240. package/dist/skills/gating.d.ts.map +1 -0
  241. package/dist/skills/gating.js +49 -0
  242. package/dist/skills/gating.js.map +1 -0
  243. package/dist/skills/loader.d.ts +32 -0
  244. package/dist/skills/loader.d.ts.map +1 -0
  245. package/dist/skills/loader.js +66 -0
  246. package/dist/skills/loader.js.map +1 -0
  247. package/dist/skills/parser.d.ts +22 -0
  248. package/dist/skills/parser.d.ts.map +1 -0
  249. package/dist/skills/parser.js +29 -0
  250. package/dist/skills/parser.js.map +1 -0
  251. package/dist/skills/prompt-injector.d.ts +10 -0
  252. package/dist/skills/prompt-injector.d.ts.map +1 -0
  253. package/dist/skills/prompt-injector.js +29 -0
  254. package/dist/skills/prompt-injector.js.map +1 -0
  255. package/dist/tools/builtin/cron.d.ts +3 -0
  256. package/dist/tools/builtin/cron.d.ts.map +1 -0
  257. package/dist/tools/builtin/cron.js +198 -0
  258. package/dist/tools/builtin/cron.js.map +1 -0
  259. package/dist/tools/builtin/edit.d.ts +3 -0
  260. package/dist/tools/builtin/edit.d.ts.map +1 -0
  261. package/dist/tools/builtin/edit.js +77 -0
  262. package/dist/tools/builtin/edit.js.map +1 -0
  263. package/dist/tools/builtin/exec.d.ts +3 -0
  264. package/dist/tools/builtin/exec.d.ts.map +1 -0
  265. package/dist/tools/builtin/exec.js +88 -0
  266. package/dist/tools/builtin/exec.js.map +1 -0
  267. package/dist/tools/builtin/panel.d.ts +14 -0
  268. package/dist/tools/builtin/panel.d.ts.map +1 -0
  269. package/dist/tools/builtin/panel.js +521 -0
  270. package/dist/tools/builtin/panel.js.map +1 -0
  271. package/dist/tools/builtin/read.d.ts +3 -0
  272. package/dist/tools/builtin/read.d.ts.map +1 -0
  273. package/dist/tools/builtin/read.js +61 -0
  274. package/dist/tools/builtin/read.js.map +1 -0
  275. package/dist/tools/builtin/web-fetch.d.ts +3 -0
  276. package/dist/tools/builtin/web-fetch.d.ts.map +1 -0
  277. package/dist/tools/builtin/web-fetch.js +68 -0
  278. package/dist/tools/builtin/web-fetch.js.map +1 -0
  279. package/dist/tools/builtin/web-search.d.ts +3 -0
  280. package/dist/tools/builtin/web-search.d.ts.map +1 -0
  281. package/dist/tools/builtin/web-search.js +67 -0
  282. package/dist/tools/builtin/web-search.js.map +1 -0
  283. package/dist/tools/builtin/write.d.ts +3 -0
  284. package/dist/tools/builtin/write.d.ts.map +1 -0
  285. package/dist/tools/builtin/write.js +40 -0
  286. package/dist/tools/builtin/write.js.map +1 -0
  287. package/dist/tools/executor.d.ts +16 -0
  288. package/dist/tools/executor.d.ts.map +1 -0
  289. package/dist/tools/executor.js +142 -0
  290. package/dist/tools/executor.js.map +1 -0
  291. package/dist/tools/registry.d.ts +23 -0
  292. package/dist/tools/registry.d.ts.map +1 -0
  293. package/dist/tools/registry.js +72 -0
  294. package/dist/tools/registry.js.map +1 -0
  295. package/dist/tools/types.d.ts +58 -0
  296. package/dist/tools/types.d.ts.map +1 -0
  297. package/dist/tools/types.js +8 -0
  298. package/dist/tools/types.js.map +1 -0
  299. package/dist/ui/assets/code-block-OCS4YCEC-D5mQabi7.js +2 -0
  300. package/dist/ui/assets/index-0RdIdnZu.js +456 -0
  301. package/dist/ui/assets/index-CjWH8YjU.css +1 -0
  302. package/dist/ui/index.html +16 -0
  303. package/package.json +89 -0
  304. package/skills/coding/SKILL.md +52 -0
  305. package/skills/file-ops/SKILL.md +50 -0
  306. package/skills/panel-dev/SKILL.md +742 -0
  307. package/skills/system-admin/SKILL.md +56 -0
  308. package/skills/web-research/SKILL.md +44 -0
  309. package/templates/BOOTSTRAP.md +19 -0
  310. package/templates/IDENTITY.md +9 -0
  311. package/templates/INSTRUCTIONS.md +19 -0
  312. package/templates/MEMORY.md +5 -0
  313. package/templates/SOUL.md +21 -0
  314. package/templates/TOOLS.md +13 -0
  315. package/templates/USER.md +12 -0
  316. package/templates/panel/gemma-panel-sdk.d.ts +27 -0
  317. package/templates/panel/gemma-panel.d.ts +46 -0
  318. package/templates/panel/index.html +59 -0
  319. package/templates/panel/package.json +15 -0
  320. package/templates/panel/panel.json +12 -0
  321. package/templates/panel/service.ts +69 -0
  322. package/templates/panel/tsconfig.json +17 -0
  323. package/templates/panel/vite.config.ts +13 -0
  324. package/templates/panel-react/index.html +12 -0
  325. package/templates/panel-react/package.json +22 -0
  326. package/templates/panel-react/panel.json +10 -0
  327. package/templates/panel-react/service.ts +69 -0
  328. package/templates/panel-react/src/App.tsx +108 -0
  329. package/templates/panel-react/src/gemma.d.ts +13 -0
  330. package/templates/panel-react/src/main.tsx +9 -0
  331. package/templates/panel-react/src/vite-env.d.ts +1 -0
  332. package/templates/panel-react/tsconfig.json +17 -0
  333. package/templates/panel-react/tsconfig.service.json +16 -0
  334. package/templates/panel-react/vite.config.ts +14 -0
package/README.md ADDED
@@ -0,0 +1,860 @@
1
+ # Gemma
2
+
3
+ A confirmation-first personal AI assistant. Gemma is a single-process Node.js gateway that connects messaging channels (Web, Telegram, CLI) to an LLM with tool access. The core invariant: **no tool executes without user approval**.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Quick Start](#quick-start)
8
+ - [Architecture](#architecture)
9
+ - [Configuration](#configuration)
10
+ - [Channels](#channels)
11
+ - [LLM Providers](#llm-providers)
12
+ - [Authentication](#authentication)
13
+ - [Tools](#tools)
14
+ - [Confirmation Gate](#confirmation-gate)
15
+ - [Skills](#skills)
16
+ - [Panels](#panels)
17
+ - [Automation](#automation)
18
+ - [Web UI](#web-ui)
19
+ - [WebSocket Protocol](#websocket-protocol)
20
+ - [Deployment](#deployment)
21
+ - [Project Structure](#project-structure)
22
+
23
+ ---
24
+
25
+ ## Quick Start
26
+
27
+ ```bash
28
+ # Install dependencies
29
+ npm install
30
+
31
+ # Interactive first-run setup (auth, channels, directories)
32
+ npx gemma setup
33
+
34
+ # Or configure manually and start
35
+ npx gemma start
36
+
37
+ # Development
38
+ npm run dev # Server with tsx (auto-restart)
39
+ npm run dev:ui # Vite dev server on :5173 with HMR
40
+ npm run build # Full production build (server + UI)
41
+ ```
42
+
43
+ ### Prerequisites
44
+
45
+ - **Node.js 22+** (uses built-in `.env` loading, ES2023 features)
46
+ - A Gemini API key or Google OAuth credentials
47
+ - Optional: Brave Search API key for web search
48
+
49
+ ### First Run
50
+
51
+ On first launch, Gemma runs an interactive setup wizard:
52
+
53
+ 1. **Auth** -- Choose OAuth (browser flow), API key, or skip
54
+ 2. **Channels** -- Enable Telegram (with bot token), Web UI (with optional password)
55
+ 3. **Setup** -- Creates `~/.gemma/` directory structure, writes `gemma.json`, copies workspace templates
56
+
57
+ After setup, Gemma starts on `http://localhost:18789`.
58
+
59
+ ---
60
+
61
+ ## Architecture
62
+
63
+ Gemma is a **single-process gateway** that orchestrates messaging channels, an LLM, and tool execution through a mandatory confirmation gate.
64
+
65
+ ### Message Flow
66
+
67
+ ```
68
+ Channel (Web / Telegram / CLI)
69
+ -> Gateway.handleMessage()
70
+ -> Session resolution (per channel+sender)
71
+ -> Agent loop (up to 25 iterations):
72
+ 1. Auto-compact if context > 200k chars
73
+ 2. Hard trim if context > 320k chars
74
+ 3. Build system prompt (workspace files + skills + runtime)
75
+ 4. LLM.generate() -- streaming async generator
76
+ 5. Collect text + tool calls from stream
77
+ 6. If text only -> send response, break
78
+ 7. If tool calls -> ConfirmationGate.process()
79
+ -> Auto-approve check (internal reads only)
80
+ -> Present to user via channel
81
+ -> Execute approved tools
82
+ 8. Feed tool results back to LLM
83
+ 9. Continue loop
84
+ -> Send final response to channel
85
+ ```
86
+
87
+ ### Key Design Decisions
88
+
89
+ - **Single import boundary**: `ConfirmationGate` is the _only_ module that imports the tool executor. The executor is never re-exported or accessible from any other path.
90
+ - **Streaming-first**: Both LLM providers are async generators. No buffered mode.
91
+ - **Error-as-text**: When all LLM retries are exhausted, errors are yielded as text chunks rather than thrown, keeping the conversation alive.
92
+ - **Session serialization**: Each session allows only one concurrent agent loop run via a `running` flag.
93
+
94
+ ---
95
+
96
+ ## Configuration
97
+
98
+ Gemma loads config from `gemma.json`, searched in order: current directory, then `~/.gemma/`.
99
+
100
+ - **JSON5** comments and trailing commas supported
101
+ - **`${ENV_VAR}`** expansion in string values
102
+ - **`~`** expands to home directory
103
+ - Full **Zod validation** with sensible defaults
104
+
105
+ ### Complete Reference
106
+
107
+ #### Model
108
+
109
+ | Key | Type | Default | Description |
110
+ |-----|------|---------|-------------|
111
+ | `model.provider` | `'gemini' \| 'openai-compat'` | `'gemini'` | LLM provider |
112
+ | `model.model` | `string` | `'gemini-2.5-flash'` | Default model name |
113
+ | `model.fallbackModel` | `string?` | -- | Fallback model when primary is at capacity |
114
+ | `model.auth` | `'oauth' \| 'api-key' \| 'adc'` | auto-detected | Force a specific auth mode |
115
+ | `model.apiKey` | `string?` | -- | Gemini API key (or use `GEMINI_API_KEY` env) |
116
+ | `model.oauth.clientId` | `string?` | -- | Custom OAuth client ID |
117
+ | `model.oauth.clientSecret` | `string?` | -- | Custom OAuth client secret |
118
+ | `model.openaiCompat.baseUrl` | `string` | `http://localhost:11434/v1` | OpenAI-compatible server URL |
119
+ | `model.openaiCompat.model` | `string` | `llama3.2` | Model name for OpenAI-compat |
120
+
121
+ #### Gateway
122
+
123
+ | Key | Type | Default | Description |
124
+ |-----|------|---------|-------------|
125
+ | `gateway.port` | `number` | `18789` | HTTP/WS port (env: `GEMMA_PORT`) |
126
+ | `gateway.bind` | `string` | `'127.0.0.1'` | Bind address (env: `GEMMA_BIND`) |
127
+ | `gateway.auth.token` | `string?` | -- | Optional password for WebSocket auth |
128
+
129
+ #### Channels
130
+
131
+ | Key | Type | Default | Description |
132
+ |-----|------|---------|-------------|
133
+ | `channels.web.enabled` | `boolean` | `true` | Enable Web UI + WebSocket |
134
+ | `channels.telegram.enabled` | `boolean` | `false` | Enable Telegram bot |
135
+ | `channels.telegram.token` | `string?` | -- | Bot token (or `TELEGRAM_BOT_TOKEN` env) |
136
+ | `channels.telegram.webhookUrl` | `string?` | -- | Webhook URL (omit for polling mode) |
137
+ | `channels.telegram.allowFrom` | `number[]` | `[]` | Allowed Telegram user IDs (empty = all) |
138
+ | `channels.cli.enabled` | `boolean` | `false` | Enable CLI channel |
139
+
140
+ #### Confirmation
141
+
142
+ | Key | Type | Default | Description |
143
+ |-----|------|---------|-------------|
144
+ | `confirm.timeout` | `number` | `300` | Confirmation timeout in seconds |
145
+ | `confirm.autoApprove.internalReads` | `boolean` | `true` | Auto-approve reads inside `~/.gemma/` |
146
+ | `confirm.autoApprove.memoryWrites` | `boolean` | `false` | Auto-approve writes to MEMORY.md |
147
+
148
+ #### Agent Loop
149
+
150
+ | Key | Type | Default | Description |
151
+ |-----|------|---------|-------------|
152
+ | `agent.maxIterations` | `number` | `25` | Max tool-call rounds per request (1-100) |
153
+ | `agent.compactionThreshold` | `number` | `200000` | Auto-compact above this char count |
154
+ | `agent.maxContextChars` | `number` | `320000` | Hard trim above this char count |
155
+ | `agent.maxToolResultChars` | `number` | `30000` | Truncate individual tool results |
156
+
157
+ #### Tools
158
+
159
+ | Key | Type | Default | Description |
160
+ |-----|------|---------|-------------|
161
+ | `tools.exec.timeout` | `number` | `30` | Shell command timeout (seconds) |
162
+ | `tools.exec.shell` | `string` | `'/bin/bash'` | Shell for exec tool |
163
+ | `tools.webSearch.provider` | `string` | `'brave'` | Web search provider |
164
+ | `tools.webSearch.apiKey` | `string?` | -- | Brave Search API key (or `BRAVE_SEARCH_API_KEY` env) |
165
+
166
+ #### Panels
167
+
168
+ | Key | Type | Default | Description |
169
+ |-----|------|---------|-------------|
170
+ | `panels.enabled` | `boolean` | `true` | Enable panel system |
171
+ | `panels.dir` | `string` | `'~/.gemma/panels'` | Panels directory |
172
+ | `panels.maxDataSize` | `number` | `5242880` | Max data storage per panel (bytes) |
173
+ | `panels.disabled` | `string[]` | `[]` | Panel names to disable |
174
+ | `panels.devMode` | `boolean` | `false` | Proxy to panel dev servers |
175
+ | `panels.serviceTimeout` | `number` | `30000` | Service start/stop/RPC timeout (ms) |
176
+
177
+ #### Automation
178
+
179
+ | Key | Type | Default | Description |
180
+ |-----|------|---------|-------------|
181
+ | `heartbeat.enabled` | `boolean` | `true` | Enable periodic heartbeat |
182
+ | `heartbeat.intervalMinutes` | `number` | `30` | Heartbeat interval |
183
+ | `heartbeat.prompt` | `string` | `'Read HEARTBEAT.md...'` | Prompt sent to agent |
184
+ | `cron.enabled` | `boolean` | `true` | Enable cron system |
185
+ | `cron.store` | `string` | `'~/.gemma/cron/jobs.json'` | Job persistence path |
186
+
187
+ #### Skills
188
+
189
+ | Key | Type | Default | Description |
190
+ |-----|------|---------|-------------|
191
+ | `skills.dirs` | `string[]` | `[]` | Extra skill directories |
192
+ | `skills.entries` | `Record` | `{}` | Per-skill configuration |
193
+
194
+ #### MCP
195
+
196
+ | Key | Type | Default | Description |
197
+ |-----|------|---------|-------------|
198
+ | `mcp.servers` | `Record<string, MCPServer>` | `{}` | Named MCP server configs |
199
+
200
+ Each MCP server: `{ command, args?, env?, enabled? }`
201
+
202
+ ### Environment Variables
203
+
204
+ | Variable | Purpose |
205
+ |----------|---------|
206
+ | `GEMMA_HOME` | Override home directory (default: `~/.gemma`) |
207
+ | `GEMMA_PORT` | Override gateway port |
208
+ | `GEMMA_BIND` | Override bind address |
209
+ | `GEMINI_API_KEY` | Gemini API key |
210
+ | `BRAVE_SEARCH_API_KEY` | Brave Search API key |
211
+ | `TELEGRAM_BOT_TOKEN` | Telegram bot token |
212
+ | `GEMMA_OAUTH_CLIENT_ID` | Custom OAuth client ID |
213
+ | `GEMMA_OAUTH_CLIENT_SECRET` | Custom OAuth client secret |
214
+
215
+ ### Example `gemma.json`
216
+
217
+ ```json5
218
+ {
219
+ // Use Gemini 3 Flash with 2.5 Flash as fallback
220
+ "model": {
221
+ "model": "gemini-3-flash-preview",
222
+ "fallbackModel": "gemini-2.5-flash"
223
+ },
224
+ "gateway": {
225
+ "port": 18789,
226
+ "bind": "0.0.0.0",
227
+ "auth": { "token": "my-secret" }
228
+ },
229
+ "channels": {
230
+ "web": { "enabled": true },
231
+ "telegram": {
232
+ "enabled": true,
233
+ "token": "${TELEGRAM_BOT_TOKEN}",
234
+ "allowFrom": [123456789]
235
+ }
236
+ },
237
+ "confirm": {
238
+ "autoApprove": {
239
+ "internalReads": true,
240
+ "memoryWrites": true
241
+ }
242
+ },
243
+ "mcp": {
244
+ "servers": {
245
+ "filesystem": {
246
+ "command": "npx",
247
+ "args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user"]
248
+ }
249
+ }
250
+ }
251
+ }
252
+ ```
253
+
254
+ ---
255
+
256
+ ## Channels
257
+
258
+ ### Web (default)
259
+
260
+ HTTP server + WebSocket on the configured port. Serves:
261
+ - React SPA at `/`
262
+ - Panel files at `/panels/:name/*`
263
+ - Panel SDK at `/panels/_sdk/gemma-panel.js`
264
+ - Panel API at `/api/panels`
265
+ - Health check at `/health`
266
+ - Telegram webhook at `/webhook/telegram`
267
+
268
+ WebSocket at `/ws` handles all real-time communication (chat, streaming, confirmations, config, sessions, cron, logs, panels). Optional token auth via `gateway.auth.token`.
269
+
270
+ ### Telegram
271
+
272
+ Uses [grammY](https://grammy.dev/) framework. Supports:
273
+ - **Polling mode** (default): Standalone long-polling
274
+ - **Webhook mode**: Routes through the web channel's HTTP server
275
+ - **Inline keyboard confirmations**: Approve All / Select Individual / Reject All
276
+ - **Streaming**: Edits message in-place every second with accumulated text
277
+ - **Access control**: `allowFrom` whitelist of Telegram user IDs
278
+
279
+ ### CLI
280
+
281
+ Readline-based interface for local development. Color-coded output via chalk. Confirmations via text input (`y`/`n`/comma-separated indices).
282
+
283
+ ---
284
+
285
+ ## LLM Providers
286
+
287
+ ### Gemini (default)
288
+
289
+ Two API endpoints depending on auth mode:
290
+
291
+ | Auth Mode | Endpoint | URL |
292
+ |-----------|----------|-----|
293
+ | API key | Standard Gemini API | `generativelanguage.googleapis.com/v1beta` |
294
+ | OAuth | Code Assist proxy | `cloudcode-pa.googleapis.com/v1internal` |
295
+ | ADC | Standard Gemini API | Same as API key |
296
+
297
+ **Features:**
298
+ - SSE streaming with async generator interface
299
+ - Thinking/reasoning mode (`thinkingBudget: 8192` tokens)
300
+ - `thoughtSignature` propagation on tool calls
301
+ - Retry logic: 5 retries, exponential backoff (1s base, 5s for capacity exhaustion), 60s max delay
302
+ - Fallback model: automatically tries a configured fallback on `MODEL_CAPACITY_EXHAUSTED`
303
+ - Respects `Retry-After` headers
304
+
305
+ **Available models:** `gemini-3-pro-preview`, `gemini-3-flash-preview`, `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-2.0-flash`, `gemini-2.0-flash-lite`
306
+
307
+ ### OpenAI-Compatible
308
+
309
+ For local/self-hosted LLMs: **Ollama**, **LM Studio**, **vLLM**, etc.
310
+
311
+ ```json5
312
+ {
313
+ "model": {
314
+ "provider": "openai-compat",
315
+ "openaiCompat": {
316
+ "baseUrl": "http://localhost:11434/v1",
317
+ "model": "llama3.2"
318
+ }
319
+ }
320
+ }
321
+ ```
322
+
323
+ Uses standard `/chat/completions` with streaming. No retry logic or fallback model support.
324
+
325
+ ---
326
+
327
+ ## Authentication
328
+
329
+ Three modes, auto-detected in priority order:
330
+
331
+ ### 1. OAuth (recommended)
332
+
333
+ ```bash
334
+ gemma auth --google
335
+ ```
336
+
337
+ Opens a browser for Google OAuth consent. Tokens cached at `~/.gemma/auth/tokens.json` (chmod 600) with automatic refresh 5 minutes before expiry.
338
+
339
+ OAuth credentials are borrowed from the installed [Gemini CLI](https://github.com/anthropics/gemini-cli) (`@google/gemini-cli-core`). Falls back to `GEMMA_OAUTH_CLIENT_ID`/`GEMMA_OAUTH_CLIENT_SECRET` env vars or config.
340
+
341
+ Supports headless environments (SSH, CI) -- prints URL for user to open on any device.
342
+
343
+ ### 2. API Key
344
+
345
+ ```bash
346
+ export GEMINI_API_KEY="your-key-here"
347
+ # or in gemma.json:
348
+ # "model": { "apiKey": "your-key" }
349
+ ```
350
+
351
+ ### 3. Application Default Credentials (ADC)
352
+
353
+ For GCP environments. Uses `google-auth-library` with `cloud-platform` scope. Auto-detected when no OAuth tokens or API key are available.
354
+
355
+ ### Auth Commands
356
+
357
+ ```bash
358
+ gemma auth --status # Show current auth mode and email
359
+ gemma auth --google # Run OAuth flow
360
+ gemma auth --api-key # Set API key
361
+ gemma auth --logout # Revoke tokens and delete
362
+ ```
363
+
364
+ ---
365
+
366
+ ## Tools
367
+
368
+ 8 built-in tools, all going through the confirmation gate:
369
+
370
+ ### `read`
371
+ Read file contents with optional line range.
372
+ - **Params**: `path` (required), `startLine?`, `endLine?` (1-based)
373
+ - Returns numbered lines with metadata
374
+
375
+ ### `write`
376
+ Write content to a file, creating parent directories as needed.
377
+ - **Params**: `path` (required), `content` (required)
378
+
379
+ ### `edit`
380
+ Find-and-replace a unique string in a file.
381
+ - **Params**: `path` (required), `old_string` (required), `new_string` (required)
382
+ - Rejects if old_string appears 0 or 2+ times
383
+
384
+ ### `exec`
385
+ Execute a shell command.
386
+ - **Params**: `command` (required), `timeout?` (default 30s)
387
+ - Returns stdout, stderr, exit code. SIGTERM then SIGKILL on timeout.
388
+
389
+ ### `web_search`
390
+ Search the web via Brave Search API.
391
+ - **Params**: `query` (required), `count?` (max 20, default 5)
392
+ - Requires `BRAVE_SEARCH_API_KEY` or config
393
+
394
+ ### `web_fetch`
395
+ Fetch a URL and convert HTML to markdown.
396
+ - **Params**: `url` (required), `maxLength?` (default 50000)
397
+
398
+ ### `cron`
399
+ Manage scheduled jobs (list, add, update, remove, enable, disable, run).
400
+ - Schedule formats: cron expressions (`0 9 * * 1-5`), intervals (`every:30m`), one-shots (`at:2024-03-15T09:00:00Z`)
401
+
402
+ ### `create_panel`
403
+ Scaffold, update, remove, list, or rebuild UI panels.
404
+ - Templates: `html` (custom HTML) or `react` (React 18 + Vite + TypeScript)
405
+ - Optional backend service with RPC and event support
406
+
407
+ ### MCP Tools
408
+
409
+ External tools from MCP servers are discovered at startup and registered as `mcp__<server>__<tool>`. They go through the same confirmation gate as built-in tools. MCP servers can be hot-reloaded via the Settings UI.
410
+
411
+ ---
412
+
413
+ ## Confirmation Gate
414
+
415
+ The security boundary. Every tool call passes through the gate before execution.
416
+
417
+ ### Auto-Approve Rules
418
+
419
+ | Condition | Auto-approved? | Default |
420
+ |-----------|---------------|---------|
421
+ | `read` tool targeting paths inside `~/.gemma/` | Yes | `internalReads: true` |
422
+ | `read` tool targeting `HEARTBEAT.md` | Yes | Always |
423
+ | `write`/`edit` to `MEMORY.md` or `memory/*.md` | Configurable | `memoryWrites: false` |
424
+ | Everything else | No | Requires confirmation |
425
+
426
+ ### Cowboy Mode
427
+
428
+ Per-session toggle that bypasses ALL confirmations. Enabled via the shield toggle in the Web UI. Use with caution.
429
+
430
+ ### Audit Log
431
+
432
+ Every confirmation cycle is logged to `~/.gemma/logs/audit.jsonl`:
433
+ ```json
434
+ {
435
+ "timestamp": "2024-03-15T09:00:00.000Z",
436
+ "session": "s-abc123-xyz",
437
+ "proposed": [{"tool": "exec", "args": {"command": "ls"}}],
438
+ "approved": [0],
439
+ "rejected": [],
440
+ "auto_approved": [],
441
+ "user_response_time_ms": 2100
442
+ }
443
+ ```
444
+
445
+ ---
446
+
447
+ ## Skills
448
+
449
+ Skills are `SKILL.md` files with YAML frontmatter that provide domain-specific instructions to the agent.
450
+
451
+ ### Format
452
+
453
+ ```markdown
454
+ ---
455
+ name: my-skill
456
+ description: What this skill does
457
+ metadata:
458
+ requires:
459
+ bins: [docker] # Required binaries on PATH
460
+ env: [DOCKER_HOST] # Required environment variables
461
+ ---
462
+ # Instructions for the agent
463
+ Detailed instructions here...
464
+ ```
465
+
466
+ ### Loading Order (later overrides earlier)
467
+
468
+ 1. `./skills/` -- Bundled with Gemma
469
+ 2. `~/.gemma/workspace/skills/` -- User workspace
470
+ 3. Config `skills.dirs` entries -- Custom directories
471
+
472
+ ### Bundled Skills (5)
473
+
474
+ | Skill | Description |
475
+ |-------|-------------|
476
+ | `web-research` | Search the web and extract content from URLs |
477
+ | `file-ops` | Read, write, and edit files on the local filesystem |
478
+ | `coding` | Writing, debugging, reviewing, and explaining code |
479
+ | `system-admin` | Execute system commands and manage servers |
480
+ | `panel-development` | Guide for creating Gemma panels with backend services |
481
+
482
+ Skills are gated by their `requires` field -- unavailable skills (missing binaries/env vars) are excluded from the system prompt.
483
+
484
+ ---
485
+
486
+ ## Panels
487
+
488
+ Panels are mini web apps that extend Gemma's UI. They run in sandboxed iframes and communicate via a client SDK and optional backend services.
489
+
490
+ ### Display Modes
491
+
492
+ - **Tab** -- Full-width content area, shown as a nav item in the sidebar
493
+ - **Sidebar** -- Compact widget at the bottom of the sidebar (max 300px height)
494
+
495
+ ### Creating a Panel
496
+
497
+ The LLM can scaffold panels via the `create_panel` tool, or you can create one manually:
498
+
499
+ ```
500
+ ~/.gemma/panels/my-panel/
501
+ panel.json # Manifest (required)
502
+ index.html # Entry point
503
+ dist/ # Build output (for React panels)
504
+ data/ # Scoped data directory (runtime)
505
+ service.ts # Backend service (optional)
506
+ ```
507
+
508
+ ### Manifest (`panel.json`)
509
+
510
+ ```json
511
+ {
512
+ "name": "my-panel",
513
+ "title": "My Panel",
514
+ "version": "1.0.0",
515
+ "entry": "index.html",
516
+ "display": "tab",
517
+ "icon": "puzzle",
518
+ "permissions": ["send:chat", "read:data", "write:data"],
519
+ "service": "service.js"
520
+ }
521
+ ```
522
+
523
+ **Permissions**: `send:chat`, `read:data`, `write:data`, `read:memory`, `listen:chat`
524
+
525
+ ### Client SDK
526
+
527
+ Auto-injected into panel HTML. Provides:
528
+
529
+ ```javascript
530
+ const panel = new GemmaPanel();
531
+
532
+ // Data storage (scoped per panel)
533
+ await panel.readData('key');
534
+ await panel.writeData('key', value);
535
+
536
+ // Chat integration
537
+ panel.sendChat('Hello from my panel');
538
+ panel.onMessage(msg => console.log(msg));
539
+
540
+ // Backend RPC
541
+ const result = await panel.callBackend('myMethod', { param: 'value' });
542
+ panel.onEvent('update', data => console.log(data));
543
+
544
+ // Theme
545
+ const theme = await panel.getTheme(); // { isDark, variables }
546
+
547
+ // Notifications
548
+ panel.notify('Something happened', 'info');
549
+ ```
550
+
551
+ ### Backend Services
552
+
553
+ Optional server-side logic for panels. Export `start`, `onRpc`, and `stop`:
554
+
555
+ ```typescript
556
+ import type { PanelServiceContext } from './gemma-panel.js';
557
+
558
+ export async function start(ctx: PanelServiceContext) {
559
+ ctx.emit('ready', { status: 'initialized' });
560
+ ctx.schedule('refresh', 60000, () => { /* periodic work */ });
561
+ ctx.registerTool({ name: 'my_tool', description: '...', parameters: {...},
562
+ execute: async (args) => ({ content: 'result' }) });
563
+ }
564
+
565
+ export async function onRpc(method: string, params: unknown, ctx: PanelServiceContext) {
566
+ if (method === 'getData') return { items: [] };
567
+ throw new Error(`Unknown method: ${method}`);
568
+ }
569
+
570
+ export async function stop() { /* cleanup */ }
571
+ ```
572
+
573
+ ---
574
+
575
+ ## Automation
576
+
577
+ ### Heartbeat
578
+
579
+ Periodic wake-up that prompts the agent on a timer (default: every 30 minutes).
580
+
581
+ The agent reads `HEARTBEAT.md` from the workspace and acts on standing instructions. If nothing needs attention, it replies `HEARTBEAT_OK`. Runs in the most recently active web session.
582
+
583
+ ### Cron
584
+
585
+ Full-featured scheduled job system.
586
+
587
+ **Schedule formats:**
588
+ - Cron expression: `0 9 * * 1-5` (9 AM weekdays)
589
+ - Interval: `every:30m`, `every:2h`, `every:1d`
590
+ - One-shot: `at:2024-03-15T09:00:00Z`
591
+
592
+ **Key behaviors:**
593
+ - Jobs persist to `~/.gemma/cron/jobs.json`
594
+ - Each job runs in an isolated session (or shared `main` session)
595
+ - Cron jobs **bypass the confirmation gate** -- they are pre-authorized
596
+ - Consecutive errors are tracked per job
597
+ - Jobs can be managed via the `cron` tool, Web UI, or WebSocket API
598
+
599
+ ---
600
+
601
+ ## Web UI
602
+
603
+ React 18 single-page application with Tailwind CSS 3 and shadcn/ui.
604
+
605
+ ### Views
606
+
607
+ | View | Description |
608
+ |------|-------------|
609
+ | **Chat** | Conversation interface with streaming markdown, thinking display, inline tool confirmations, cowboy mode toggle |
610
+ | **Settings** | MCP server management, model/fallback selection, agent loop tuning, API keys, auth status |
611
+ | **Cron** | Create/edit/delete scheduled jobs with friendly schedule picker |
612
+ | **Logs** | Real-time log viewer with level/source filtering and auto-scroll |
613
+ | **Panel Manager** | List installed panels, enable/disable, view errors |
614
+ | **Panel tabs** | Full-width iframe hosting for tab-display panels |
615
+
616
+ ### Chat Features
617
+
618
+ - **Streaming markdown** with syntax-highlighted code blocks (Shiki)
619
+ - **Thinking/reasoning display** -- collapsible with duration counter
620
+ - **Inline confirmations** -- approve/reject individual or all tool calls with risk indicators
621
+ - **Tool result cards** -- collapsible with status and preview
622
+ - **Per-session model override** and thinking toggle
623
+ - **Cowboy mode** -- shield toggle with confirmation dialog
624
+ - **Session management** -- sidebar with create, switch, rename, delete
625
+
626
+ ### Development
627
+
628
+ ```bash
629
+ npm run dev:ui # Vite on :5173, proxies /ws, /api, /panels to gateway on :18789
630
+ ```
631
+
632
+ ---
633
+
634
+ ## WebSocket Protocol
635
+
636
+ 38 message types over a single WebSocket connection at `/ws`.
637
+
638
+ ### Core Chat
639
+
640
+ | Type | Direction | Description |
641
+ |------|-----------|-------------|
642
+ | `chat` | Both | User message or assistant reply |
643
+ | `stream` | S->C | Streaming response chunk (`done: true` signals end) |
644
+ | `thinking` | S->C | LLM reasoning chunk |
645
+ | `confirmation` | S->C | Present tool calls with risk assessment |
646
+ | `confirmation_response` | C->S | User approval/rejection by call IDs |
647
+ | `tool_results` | S->C | Tool execution results |
648
+
649
+ ### Session Management
650
+
651
+ | Type | Direction | Description |
652
+ |------|-----------|-------------|
653
+ | `session:resume` | C->S | Resume session on connect |
654
+ | `session:list` / `session:list:response` | Both | List all sessions |
655
+ | `session:new` / `session:switch` / `session:delete` / `session:rename` | C->S | CRUD operations |
656
+ | `session:restored` | S->C | Full session data with history |
657
+ | `session:event` | S->C | Session CRUD notifications |
658
+
659
+ ### Config, Cron, Logs, Panels
660
+
661
+ Full CRUD over WebSocket for config management, MCP servers, cron jobs, log streaming, and panel bridge (data read/write, RPC, events).
662
+
663
+ See `src/shared/ws-types.ts` for the complete type definitions.
664
+
665
+ ---
666
+
667
+ ## Deployment
668
+
669
+ ### Docker
670
+
671
+ ```bash
672
+ # Build
673
+ docker build -f docker/Dockerfile -t gemma .
674
+
675
+ # Run with persistent state
676
+ docker run -d \
677
+ -p 18789:18789 \
678
+ -v gemma-data:/home/node/.gemma \
679
+ -e GEMINI_API_KEY=your-key \
680
+ -e GEMMA_BIND=0.0.0.0 \
681
+ gemma
682
+ ```
683
+
684
+ The Docker image:
685
+ - Two-stage build (Node 22 -> Node 22 Alpine)
686
+ - Installs Gemini CLI globally for OAuth credential extraction
687
+ - Runs as non-root `node` user
688
+ - Exposes port 18789
689
+ - Persists all state in `/home/node/.gemma` volume
690
+
691
+ ### Docker Compose
692
+
693
+ A `docker-compose.yml` with Caddy reverse proxy is included:
694
+
695
+ ```bash
696
+ cd docker
697
+ cp .env.example .env # Edit with your API keys
698
+ docker compose up -d
699
+ ```
700
+
701
+ This sets up Gemma behind Caddy for automatic HTTPS. Edit the `Caddyfile` for your domain.
702
+
703
+ ### VPS / Bare Metal
704
+
705
+ ```bash
706
+ npm install
707
+ npm run build
708
+ npx gemma start
709
+ ```
710
+
711
+ Use a process manager (systemd, PM2) for production. Set `GEMMA_BIND=0.0.0.0` to listen on all interfaces. Use a reverse proxy (nginx, Caddy) for TLS.
712
+
713
+ ---
714
+
715
+ ## Project Structure
716
+
717
+ ```
718
+ gemma/
719
+ src/
720
+ index.ts # CLI entry point, auth commands, setup wizard
721
+ gateway.ts # Main Gateway class, agent loop, lifecycle
722
+ config/
723
+ schema.ts # Zod config schema (all options)
724
+ loader.ts # JSON5 loading, env expansion, tilde expansion
725
+ defaults.ts # Default values
726
+ core/
727
+ session.ts # Session interface and creation
728
+ session-store.ts # File-based session persistence (JSONL)
729
+ compaction.ts # LLM-powered context summarization
730
+ logger.ts # Structured logger with ring buffer
731
+ llm/
732
+ types.ts # LLMProvider interface, Message, ToolCall types
733
+ gemini.ts # Gemini provider (dual endpoint, retry, fallback)
734
+ openai-compat.ts # OpenAI-compatible provider
735
+ auth/
736
+ resolver.ts # Auth mode detection and initialization
737
+ oauth.ts # OAuth flow (desktop + headless)
738
+ oauth-callback.ts # Local callback server for OAuth
739
+ token-store.ts # Token persistence and auto-refresh
740
+ types.ts # Auth types
741
+ channels/
742
+ types.ts # Channel interface
743
+ web.ts # HTTP server + WebSocket (700+ lines)
744
+ telegram.ts # grammY bot with inline keyboards
745
+ cli.ts # Readline interface
746
+ confirm/
747
+ gate.ts # Confirmation gate (security boundary)
748
+ auto-approve.ts # Auto-approve rule engine
749
+ audit.ts # JSONL audit logging
750
+ tools/
751
+ types.ts # Tool/ToolHandler interfaces, ToolContext
752
+ registry.ts # Tool registry with registerBuiltins()
753
+ executor.ts # Tool executor (only imported by gate.ts)
754
+ builtin/
755
+ read.ts, write.ts, edit.ts, exec.ts
756
+ web-search.ts, web-fetch.ts
757
+ cron.ts, panel.ts
758
+ mcp/
759
+ manager.ts # MCP server lifecycle management
760
+ adapter.ts # MCP tool -> Gemma tool adapter
761
+ skills/
762
+ loader.ts # SKILL.md discovery and parsing
763
+ gating.ts # Binary/env requirement checking
764
+ prompt-injector.ts # Skill list -> system prompt XML
765
+ panels/
766
+ registry.ts # Panel discovery and validation
767
+ routes.ts # HTTP routes for panel serving
768
+ types.ts # Panel manifest and service types
769
+ service-manager.ts # Panel service lifecycle
770
+ service-loader.ts # Dynamic ESM import with hot-reload
771
+ service-context.ts # Scoped context builder for services
772
+ sdk/
773
+ gemma-panel.js # Client-side panel SDK
774
+ automation/
775
+ heartbeat.ts # Periodic agent prompt timer
776
+ cron/
777
+ service.ts # CronService orchestrator
778
+ types.ts # Job, schedule, event types
779
+ schedule.ts # Next-run computation
780
+ parse-schedule.ts # Human-friendly schedule parsing
781
+ store.ts # JSON persistence
782
+ memory/
783
+ memory-manager.ts # Workspace file loading by scope
784
+ daily-log.ts # Daily log file management
785
+ shared/
786
+ ws-types.ts # WebSocket protocol types (38 message types)
787
+ ui/ # React SPA (separate build via Vite)
788
+ main.tsx # React entry
789
+ App.tsx # Root component
790
+ components/
791
+ AppShell.tsx # Layout shell (sidebar + content + statusbar)
792
+ Sidebar.tsx # Navigation + session list + panel widgets
793
+ StatusBar.tsx # Connection, model, cowboy mode indicators
794
+ views/ # ChatView, SettingsView, CronView, LogsView, PanelManagerView
795
+ ai/ # Conversation, MessageResponse, Reasoning, Shimmer, CodeBlock
796
+ chat/ # ConfirmationCard, ToolCallCard, ToolResultBadge
797
+ panels/ # PanelHost (tab), PanelSidebarSlot (sidebar)
798
+ settings/ # MCPServerCard, MCPServerForm
799
+ cron/ # CronJobCard, CronJobForm
800
+ ui/ # 19 shadcn/ui primitives
801
+ hooks/
802
+ useGateway.ts # Central WS hub (chat, streaming, sessions, model)
803
+ useConfig.ts # Config CRUD over WS
804
+ useSessions.ts # Session list management
805
+ usePanels.ts # Panel manifest fetching
806
+ useCron.ts # Cron job management
807
+ useLogs.ts # Log streaming
808
+ usePanelBridge.ts # iframe <-> WS bridge for panels
809
+ usePanelThemeBridge.ts # Theme sync to panel iframes
810
+ skills/ # Bundled skill definitions (SKILL.md files)
811
+ templates/
812
+ panel/ # HTML panel template
813
+ panel-react/ # React panel template (React 18 + Vite + TS)
814
+ *.md # Workspace templates (SOUL, INSTRUCTIONS, IDENTITY, etc.)
815
+ docker/
816
+ Dockerfile # Two-stage production build
817
+ ```
818
+
819
+ ### NPM Scripts
820
+
821
+ | Script | Description |
822
+ |--------|-------------|
823
+ | `npm run build` | Full build: TypeScript server + Vite React UI |
824
+ | `npm run build:server` | Server only: `tsc` -> `dist/` |
825
+ | `npm run build:ui` | UI only: `vite build` -> `dist/ui/` |
826
+ | `npm run dev` | Dev server: `tsx src/index.ts` |
827
+ | `npm run dev:ui` | UI dev server: Vite on :5173 with HMR |
828
+
829
+ ### TypeScript Conventions
830
+
831
+ - **ESM only** (`"type": "module"`)
832
+ - All imports use `.js` extensions: `import { X } from './file.js'`
833
+ - Node imports use `node:` prefix: `import { readFile } from 'node:fs/promises'`
834
+ - Type-only imports: `import type { X } from './types.js'`
835
+ - Target: ES2023, Module: Node16, Strict mode
836
+ - Path alias: `@gemma/*` -> `./src/*` (server), `@/*` -> `./src/ui/*` (UI)
837
+
838
+ ---
839
+
840
+ ## Workspace Files
841
+
842
+ On first run, these template files are copied to `~/.gemma/workspace/`:
843
+
844
+ | File | Purpose |
845
+ |------|---------|
846
+ | `SOUL.md` | Personality and behavioral guidelines |
847
+ | `INSTRUCTIONS.md` | Operating rules and constraints |
848
+ | `IDENTITY.md` | Name, role, tone configuration |
849
+ | `USER.md` | User profile and preferences |
850
+ | `TOOLS.md` | Tool usage notes |
851
+ | `MEMORY.md` | Curated long-term memory |
852
+ | `BOOTSTRAP.md` | First-run welcome (deleted after first session) |
853
+
854
+ These files are loaded into the system prompt on every LLM call. Edit them to customize Gemma's behavior.
855
+
856
+ ---
857
+
858
+ ## License
859
+
860
+ [Add license here]