@geminixiang/mama 0.2.0-beta.4 → 0.2.0-beta.6

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 (133) hide show
  1. package/README.md +151 -417
  2. package/dist/adapter.d.ts +9 -0
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/adapter.js.map +1 -1
  5. package/dist/adapters/discord/bot.d.ts +2 -1
  6. package/dist/adapters/discord/bot.d.ts.map +1 -1
  7. package/dist/adapters/discord/bot.js +71 -18
  8. package/dist/adapters/discord/bot.js.map +1 -1
  9. package/dist/adapters/discord/context.d.ts.map +1 -1
  10. package/dist/adapters/discord/context.js +9 -2
  11. package/dist/adapters/discord/context.js.map +1 -1
  12. package/dist/adapters/slack/bot.d.ts +5 -0
  13. package/dist/adapters/slack/bot.d.ts.map +1 -1
  14. package/dist/adapters/slack/bot.js +69 -11
  15. package/dist/adapters/slack/bot.js.map +1 -1
  16. package/dist/adapters/slack/context.d.ts.map +1 -1
  17. package/dist/adapters/slack/context.js +13 -3
  18. package/dist/adapters/slack/context.js.map +1 -1
  19. package/dist/adapters/telegram/bot.d.ts.map +1 -1
  20. package/dist/adapters/telegram/bot.js +1 -35
  21. package/dist/adapters/telegram/bot.js.map +1 -1
  22. package/dist/adapters/telegram/context.d.ts.map +1 -1
  23. package/dist/adapters/telegram/context.js +9 -2
  24. package/dist/adapters/telegram/context.js.map +1 -1
  25. package/dist/agent.d.ts.map +1 -1
  26. package/dist/agent.js +15 -7
  27. package/dist/agent.js.map +1 -1
  28. package/dist/bindings.d.ts +2 -1
  29. package/dist/bindings.d.ts.map +1 -1
  30. package/dist/bindings.js.map +1 -1
  31. package/dist/commands/index.d.ts +5 -0
  32. package/dist/commands/index.d.ts.map +1 -0
  33. package/dist/commands/index.js +15 -0
  34. package/dist/commands/index.js.map +1 -0
  35. package/dist/commands/login.d.ts +5 -0
  36. package/dist/commands/login.d.ts.map +1 -0
  37. package/dist/commands/login.js +37 -0
  38. package/dist/commands/login.js.map +1 -0
  39. package/dist/commands/model.d.ts +14 -0
  40. package/dist/commands/model.d.ts.map +1 -0
  41. package/dist/commands/model.js +94 -0
  42. package/dist/commands/model.js.map +1 -0
  43. package/dist/commands/new.d.ts +9 -0
  44. package/dist/commands/new.d.ts.map +1 -0
  45. package/dist/commands/new.js +28 -0
  46. package/dist/commands/new.js.map +1 -0
  47. package/dist/commands/registry.d.ts +7 -0
  48. package/dist/commands/registry.d.ts.map +1 -0
  49. package/dist/commands/registry.js +14 -0
  50. package/dist/commands/registry.js.map +1 -0
  51. package/dist/commands/session-view.d.ts +5 -0
  52. package/dist/commands/session-view.d.ts.map +1 -0
  53. package/dist/commands/session-view.js +38 -0
  54. package/dist/commands/session-view.js.map +1 -0
  55. package/dist/commands/types.d.ts +43 -0
  56. package/dist/commands/types.d.ts.map +1 -0
  57. package/dist/commands/types.js +2 -0
  58. package/dist/commands/types.js.map +1 -0
  59. package/dist/commands/utils.d.ts +5 -0
  60. package/dist/commands/utils.d.ts.map +1 -0
  61. package/dist/commands/utils.js +9 -0
  62. package/dist/commands/utils.js.map +1 -0
  63. package/dist/config.d.ts +15 -8
  64. package/dist/config.d.ts.map +1 -1
  65. package/dist/config.js +167 -56
  66. package/dist/config.js.map +1 -1
  67. package/dist/context.d.ts.map +1 -1
  68. package/dist/context.js +74 -68
  69. package/dist/context.js.map +1 -1
  70. package/dist/execution-resolver.d.ts +6 -3
  71. package/dist/execution-resolver.d.ts.map +1 -1
  72. package/dist/execution-resolver.js +47 -14
  73. package/dist/execution-resolver.js.map +1 -1
  74. package/dist/index.d.ts +7 -0
  75. package/dist/index.d.ts.map +1 -0
  76. package/dist/index.js +4 -0
  77. package/dist/index.js.map +1 -0
  78. package/dist/instrument.d.ts.map +1 -1
  79. package/dist/instrument.js +2 -3
  80. package/dist/instrument.js.map +1 -1
  81. package/dist/login/index.d.ts.map +1 -1
  82. package/dist/login/index.js +19 -8
  83. package/dist/login/index.js.map +1 -1
  84. package/dist/login/portal.d.ts.map +1 -1
  85. package/dist/login/portal.js +7 -7
  86. package/dist/login/portal.js.map +1 -1
  87. package/dist/login/session.d.ts +3 -2
  88. package/dist/login/session.d.ts.map +1 -1
  89. package/dist/login/session.js.map +1 -1
  90. package/dist/main.d.ts.map +1 -1
  91. package/dist/main.js +107 -388
  92. package/dist/main.js.map +1 -1
  93. package/dist/provisioner.d.ts +11 -9
  94. package/dist/provisioner.d.ts.map +1 -1
  95. package/dist/provisioner.js +125 -87
  96. package/dist/provisioner.js.map +1 -1
  97. package/dist/runtime/index.d.ts +2 -0
  98. package/dist/runtime/index.d.ts.map +1 -0
  99. package/dist/runtime/index.js +2 -0
  100. package/dist/runtime/index.js.map +1 -0
  101. package/dist/runtime/session-runtime.d.ts +27 -0
  102. package/dist/runtime/session-runtime.d.ts.map +1 -0
  103. package/dist/runtime/session-runtime.js +303 -0
  104. package/dist/runtime/session-runtime.js.map +1 -0
  105. package/dist/sandbox/cloudflare.d.ts +14 -0
  106. package/dist/sandbox/cloudflare.d.ts.map +1 -0
  107. package/dist/sandbox/cloudflare.js +131 -0
  108. package/dist/sandbox/cloudflare.js.map +1 -0
  109. package/dist/sandbox/index.d.ts +6 -4
  110. package/dist/sandbox/index.d.ts.map +1 -1
  111. package/dist/sandbox/index.js +6 -3
  112. package/dist/sandbox/index.js.map +1 -1
  113. package/dist/sandbox/types.d.ts +5 -1
  114. package/dist/sandbox/types.d.ts.map +1 -1
  115. package/dist/sandbox/types.js.map +1 -1
  116. package/dist/session-view/portal.d.ts.map +1 -1
  117. package/dist/session-view/portal.js +10 -1
  118. package/dist/session-view/portal.js.map +1 -1
  119. package/dist/session-view/service.d.ts.map +1 -1
  120. package/dist/session-view/service.js +36 -26
  121. package/dist/session-view/service.js.map +1 -1
  122. package/dist/session-view/store.d.ts +3 -2
  123. package/dist/session-view/store.d.ts.map +1 -1
  124. package/dist/session-view/store.js.map +1 -1
  125. package/dist/vault-routing.d.ts +3 -5
  126. package/dist/vault-routing.d.ts.map +1 -1
  127. package/dist/vault-routing.js +8 -20
  128. package/dist/vault-routing.js.map +1 -1
  129. package/dist/vault.d.ts +7 -5
  130. package/dist/vault.d.ts.map +1 -1
  131. package/dist/vault.js +101 -50
  132. package/dist/vault.js.map +1 -1
  133. package/package.json +1 -2
package/README.md CHANGED
@@ -3,70 +3,33 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@geminixiang/mama.svg)](https://www.npmjs.com/package/@geminixiang/mama)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- A multi-platform, multi-agent AI assistant for Slack, Telegram, and Discord — based on [pi-mom](https://github.com/badlogic/pi-mono), with the goal of merging improvements back upstream.
6
+ A multi-platform AI assistant for Slack, Telegram, and Discord.
7
7
 
8
- ## 📜 Attribution & Origins
9
-
10
- This project is a **forked and extended version** of the `mom` package from [`badlogic/pi-mono`](https://github.com/badlogic/pi-mono) by Mario Zechner, licensed under MIT.
11
-
12
- - **Original project**: [pi-mom](https://github.com/badlogic/pi-mono/tree/main/packages/mom) (22K+ stars)
13
- - **Base version**: forked from pi-mom v0.57.1 (synchronized with `@mariozechner/*` packages)
14
- - **Primary motivation**: Internal services urgently needed a multi-platform bot — this fork enables rapid iteration while preparing changes to contribute back upstream
15
-
16
- ## 🎯 Positioning & Roadmap
17
-
18
- | Aspect | Description |
19
- | ------------------ | ------------------------------------------------------------------------------ |
20
- | **Current Status** | Temporary standalone fork for urgent internal deployment |
21
- | **Ultimate Goal** | Merge all improvements back into pi-mono monorepo |
22
- | **Unique Value** | Multi-platform support (Slack + Telegram + Discord) to be contributed upstream |
23
-
24
- ### Why a temporary fork?
25
-
26
- Our internal services urgently needed a multi-platform bot, and we couldn't wait for upstream release cycles. This fork allows us to:
27
-
28
- 1. **Ship fast**: Deploy to production immediately while internal demand is high
29
- 2. **Iterate freely**: Test multi-platform adapters (Slack, Telegram, Discord) without monorepo constraints
30
- 3. **Contribute back**: All work here is intended to be merged into pi-mono — `mama` is not a replacement for `mom`
31
-
32
- ### Contribution Philosophy 🔄
33
-
34
- > "This is not a separate product — it's a **temporary fork** for urgent internal needs, and all improvements will be contributed back to pi-mono."
35
-
36
- We actively track the upstream `pi-mom` and plan to:
37
-
38
- - ✅ Submit PRs for platform adapters (Telegram, Discord)
39
- - ✅ Contribute cross-platform abstractions
40
- - ✅ Keep dependencies synchronized with pi-mono releases
41
- - ✅ Document what we learn from production use
42
-
43
- ---
8
+ Forked from [`badlogic/pi-mono`](https://github.com/badlogic/pi-mono)'s `mom` package (MIT, by Mario Zechner) at v0.57.1. This fork adds Telegram and Discord adapters and exists to ship internally while we prepare changes to upstream.
44
9
 
45
10
  ## Features
46
11
 
47
- - **Multi-platform** — Slack, Telegram, and Discord adapters out of the box
48
- - **Persistent sessions** — session behavior is adapted per platform instead of forcing one thread model everywhere
49
- - **Concurrent conversations** — Slack threads, Discord replies/threads, and Telegram reply chains can run independently
50
- - **Sandbox execution** — run agent commands on host, in a shared container, in a managed per-user container, or experimentally in a Firecracker VM
51
- - **Credential vaults** — `/login` stores credentials under `--state-dir` and injects env only into container/image/Firecracker runs
52
- - **Web session viewer** — users can open a read-only web view of the current session via `session` / `/session`
53
- - **Persistent memory** — workspace-level and channel-level `MEMORY.md` files
54
- - **Skills** — drop custom CLI tools into `skills/` directories
55
- - **Event system** — schedule one-shot or recurring tasks via JSON files
56
- - **Multi-provider** — configure any provider/model supported by `pi-ai`
12
+ - **Multi-platform** — Slack, Telegram, Discord adapters
13
+ - **Concurrent conversations** — Slack threads, Discord replies/threads, and Telegram reply chains run as independent sessions
14
+ - **Sandbox execution** — host, shared container, per-user managed container, Firecracker (alpha), or Cloudflare bridge (experimental)
15
+ - **Credential vaults** — `/login` stores credentials under `--state-dir` and injects env into sandbox runs
16
+ - **Web session viewer** — read-only web view of the current session via `session` / `/session`
17
+ - **Persistent memory** — workspace-level and channel-level `MEMORY.md`
18
+ - **Skills** — drop CLI tools into `skills/`
19
+ - **Events** — schedule one-shot or recurring tasks via JSON files
20
+ - **Multi-provider** — any provider/model supported by `pi-ai`
57
21
 
58
22
  ## Platform Session Model
59
23
 
60
- | Platform | User Interaction Structure | `sessionKey` Rule | Default Session Model | Special Handling Needed | Notes |
61
- | -------- | ------------------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------ |
62
- | Slack | channel top-level + thread replies | top-level / DM: `conversationId`; thread: `conversationId:threadTs` | top-level channel and DM sessions persist; each Slack thread forks into its own session | High | thread inherits parent context at fork time only; branch changes do not merge back automatically |
63
- | Discord | server mentions, replies, thread channels, DMs | DM: `channelId`; shared top-level: `channelId:messageId`; reply/thread: rooted id | DMs are one persistent session; shared spaces are scoped per top-level mention or thread | Medium | replies in shared channels continue the root message session; DM replies do not fork |
64
- | Telegram | private chats, group mentions, group reply chains | private: `chatId`; shared top-level: `chatId:messageId`; reply chain: root reply | private chats are one persistent session; shared groups are scoped per mentioned reply root | Medium | Telegram has no native thread model; shared sessions are inferred from reply chains |
24
+ | Platform | `sessionKey` Rule | Notes |
25
+ | -------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
26
+ | Slack | top-level / DM: `conversationId`; thread: `conversationId:threadTs` | thread inherits parent context at fork time only; branch changes do not merge back |
27
+ | Discord | DM: `channelId`; shared top-level: `channelId:messageId`; reply/thread: rooted id | replies in shared channels continue the root message session; DM replies do not fork |
28
+ | Telegram | private: `chatId`; shared top-level: `chatId:messageId`; reply chain: root reply | no native thread model; shared sessions are inferred from reply chains |
65
29
 
66
30
  ## Requirements
67
31
 
68
32
  - Node.js >= 20
69
- - One of the platform integrations below
70
33
 
71
34
  ## Installation
72
35
 
@@ -74,469 +37,240 @@ We actively track the upstream `pi-mom` and plan to:
74
37
  npm install -g @geminixiang/mama
75
38
  ```
76
39
 
77
- Or run directly after cloning:
40
+ Or from source:
78
41
 
79
42
  ```bash
80
- npm install
81
- npm run build
43
+ npm install && npm run build
82
44
  ```
83
45
 
84
- ---
85
-
86
46
  ## Quick Start
87
47
 
88
- ### Slack
48
+ All platforms share the same CLI:
89
49
 
90
- 1. Create a Slack app with **Socket Mode** enabled ([setup guide](docs/slack-bot-minimal-guide.md)).
91
- 2. Add the following **OAuth Bot Token Scopes**:
92
- - `app_mentions:read`, `channels:history`, `channels:read`, `chat:write`
93
- - `files:read`, `files:write`, `groups:history`, `groups:read`
94
- - `im:history`, `im:read`, `im:write`, `users:read`
95
- - `assistant:write` — required for native "Thinking" status indicator
96
- 3. Enable the **Home Tab** and **Agent mode**:
97
- - **App Home → Show Tabs** — toggle **Home Tab** on
98
- - **App Home → Agents & AI Apps** — toggle **Agent or Assistant** on
99
- 4. Subscribe to **Bot Events**:
100
- - `app_home_opened`, `app_mention`
101
- - `assistant_thread_context_changed`, `assistant_thread_started`
102
- - `message.channels`, `message.groups`, `message.im`
103
- 5. Enable **Interactivity** (Settings → Interactivity & Shortcuts → toggle on).
104
- 6. (Optional) Add **Slash Commands** such as `/pi-login` and `/pi-new` in the Slack app settings if you want dedicated commands with less naming conflict. `/pi-new` is intended for DM use only.
105
- 7. Copy the **App-Level Token** (`xapp-…`) and **Bot Token** (`xoxb-…`).
106
-
107
- Or import this **App Manifest** directly (Settings → App Manifest → paste JSON):
108
-
109
- <details>
110
- <summary>Example App Manifest</summary>
111
-
112
- ```json
113
- {
114
- "display_information": {
115
- "name": "mama"
116
- },
117
- "features": {
118
- "app_home": {
119
- "home_tab_enabled": true,
120
- "messages_tab_enabled": false,
121
- "messages_tab_read_only_enabled": false
122
- },
123
- "bot_user": {
124
- "display_name": "mama",
125
- "always_online": false
126
- }
127
- },
128
- "oauth_config": {
129
- "scopes": {
130
- "bot": [
131
- "app_mentions:read",
132
- "assistant:write",
133
- "channels:history",
134
- "channels:read",
135
- "chat:write",
136
- "files:read",
137
- "files:write",
138
- "groups:history",
139
- "groups:read",
140
- "im:history",
141
- "im:read",
142
- "im:write",
143
- "users:read"
144
- ]
145
- }
146
- },
147
- "settings": {
148
- "event_subscriptions": {
149
- "bot_events": [
150
- "app_home_opened",
151
- "app_mention",
152
- "assistant_thread_context_changed",
153
- "assistant_thread_started",
154
- "message.channels",
155
- "message.groups",
156
- "message.im"
157
- ]
158
- },
159
- "interactivity": {
160
- "is_enabled": true
161
- },
162
- "org_deploy_enabled": false,
163
- "socket_mode_enabled": true,
164
- "token_rotation_enabled": false
165
- }
166
- }
50
+ ```bash
51
+ mama [--state-dir=~/.mama] [--sandbox=<mode>] <working-directory>
167
52
  ```
168
53
 
169
- </details>
54
+ Set the platform tokens you need (you can run multiple platforms at once):
170
55
 
171
56
  ```bash
172
- export MOM_SLACK_APP_TOKEN=xapp-...
173
- export MOM_SLACK_BOT_TOKEN=xoxb-...
174
-
175
- mama [--state-dir=~/.mama] [--sandbox=host|container:<container>|image:<image>|firecracker:<vm-id>:<path>] <working-directory>
57
+ export MAMA_SLACK_APP_TOKEN=xapp-...
58
+ export MAMA_SLACK_BOT_TOKEN=xoxb-...
59
+ export MAMA_TELEGRAM_BOT_TOKEN=123456:ABC-...
60
+ export MAMA_DISCORD_BOT_TOKEN=MTI...
176
61
  ```
177
62
 
178
- The bot responds when `@mentioned` in any channel or via DM.
179
-
180
- - **Top-level channel messages** — share one persistent channel session.
181
- - **Thread replies** — fork from the channel session into an isolated thread session.
182
- - **DM top-level messages** — share one persistent DM session.
183
- - **DM thread replies** — fork from the DM session into an isolated thread session.
184
- - **Thread memory** — inherited at fork time only; thread changes do not merge back into the parent session automatically.
63
+ ### Slack
185
64
 
186
- ---
65
+ Create a Socket Mode app with the scopes and event subscriptions listed in [docs/slack-bot-minimal-guide.md](docs/slack-bot-minimal-guide.md). The bot responds when `@mentioned` in channels and to all DMs.
187
66
 
188
67
  ### Telegram
189
68
 
190
- 1. Message [@BotFather](https://t.me/BotFather) `/newbot` to create a bot and get the **Bot Token**.
191
- 2. Optionally disable privacy mode (`/setprivacy → Disable`) so the bot can read group messages without being `@mentioned`.
192
-
193
- ```bash
194
- export MOM_TELEGRAM_BOT_TOKEN=123456:ABC-...
195
-
196
- mama [--state-dir=~/.mama] [--sandbox=host|container:<container>|image:<image>|firecracker:<vm-id>:<path>] <working-directory>
197
- ```
198
-
199
- - **Private chats** — every message is forwarded to the bot automatically.
200
- - **Group chats** — the bot only responds when `@mentioned` by username.
201
- - **Private chat session** — one persistent session per DM.
202
- - **Group top-level mentions** — each mentioned message starts its own scoped session.
203
- - **Reply chains** — replying to a previous message continues that reply-root session.
204
- - Say `stop` or `/stop` to cancel a running task.
205
-
206
- ---
69
+ Create a bot via [@BotFather](https://t.me/BotFather) and copy the token. The bot responds to all private messages, and to `@mention` or reply chains in groups. Use `/login`, `/session`, `/new`, and `/stop` for controls.
207
70
 
208
71
  ### Discord
209
72
 
210
- 1. Go to the [Discord Developer Portal](https://discord.com/developers/applications) **New Application**.
211
- 2. Under **Bot**, enable **Message Content Intent** (required to read message text).
212
- 3. Under **OAuth2 → URL Generator**, select scopes `bot` + permissions `Send Messages`, `Read Message History`, `Attach Files`. Invite the bot to your server with the generated URL.
213
- 4. Copy the **Bot Token**.
73
+ Create an application in the [Discord Developer Portal](https://discord.com/developers/applications), enable **Message Content Intent**, and invite the bot with `Send Messages`, `Read Message History`, `Attach Files`. The bot responds to `@mentions` in servers and to all DMs.
214
74
 
215
- ```bash
216
- export MOM_DISCORD_BOT_TOKEN=MTI...
75
+ ## Sandbox Modes
217
76
 
218
- mama [--state-dir=~/.mama] [--sandbox=host|container:<container>|image:<image>|firecracker:<vm-id>:<path>] <working-directory>
219
- ```
220
-
221
- - **Server channels** the bot responds when `@mentioned`.
222
- - **DMs** every message is forwarded automatically.
223
- - **DM session** one persistent session per DM channel.
224
- - **Top-level mentions in shared channels** each mentioned message starts its own scoped session.
225
- - **Threads and reply chains** — messages inside the same thread or reply root share a session.
226
- - Say `stop` or `/stop` to cancel a running task.
227
-
228
- ---
77
+ | Mode | Description |
78
+ | ---------------------------- | ---------------------------------------------------------------------- |
79
+ | `host` (default) | Run on host; no vault env injection |
80
+ | `container:<name>` | Run in an existing shared container; uses vault key `container-<name>` |
81
+ | `image:<image>` | Auto-provision one Docker container per resolved vault/user |
82
+ | `firecracker:<vm-id>:<path>` | Firecracker microVM (alpha; not recommended) |
83
+ | `cloudflare:<sandbox-id>` | Cloudflare Worker bridge (experimental; no auto workspace sync) |
229
84
 
230
- ## Options
85
+ Vault routing: `image`, `firecracker`, and `cloudflare` look up `bindings.json` first, then fall back to the userId vault. See [docs/sandbox.md](docs/sandbox.md) for the full matrix.
231
86
 
232
- | Option | Default | Description |
233
- | -------------------------------------- | --------- | ------------------------------------------------------------------ |
234
- | `--state-dir=<dir>` | `~/.mama` | Store settings, credential vaults, and bindings outside workspace |
235
- | `--sandbox=host` | ✓ | Run commands directly on host; vault env is not injected |
236
- | `--sandbox=container:<name>` | | Run commands in an existing shared container |
237
- | `--sandbox=image:<image>` | | Auto-provision one Docker container per platform user |
238
- | `--sandbox=firecracker:<vm-id>:<path>` | | Experimental Firecracker microVM mode (alpha; not recommended yet) |
239
- | `--download <channel-id>` | | Download channel history to stdout and exit (Slack only) |
240
-
241
- ### Sandbox and Vault Semantics
242
-
243
- - `host`: no vault env injection.
244
- - `container:<name>`: one container maps to one shared vault key: `container-<name>`.
245
- - `image:<image>`: mama creates one container per resolved vault/user and injects that vault's env and file mounts.
246
- - `firecracker:*`: per-user vault routing via `bindings.json` first, then direct userId vault. This mode is still alpha and not recommended for normal deployments yet.
247
- - `docker:*` is not supported; use `container:*` or `image:*`.
248
-
249
- See [docs/sandbox.md](docs/sandbox.md) for the full sandbox/vault behavior matrix.
250
-
251
- ### Download channel history (Slack)
87
+ ### Managed per-user containers (`image:*`)
252
88
 
253
89
  ```bash
254
- mama --download C0123456789
90
+ docker pull ghcr.io/geminixiang/mama-sandbox:tools
91
+ mama --sandbox=image:ghcr.io/geminixiang/mama-sandbox:tools /path/to/workspace
255
92
  ```
256
93
 
257
- ## `/login` Credential Onboarding
258
-
259
- For normal deployments, set `MOM_LINK_URL` to the externally reachable base URL of the web credential onboarding flow:
94
+ Or build locally:
260
95
 
261
96
  ```bash
262
- export MOM_LINK_URL="https://mama.example.com"
263
- # optional; defaults to 8181 when MOM_LINK_URL is set
264
- export MOM_LINK_PORT=8181
97
+ docker build -f docker/mama-sandbox.Dockerfile -t mama-sandbox:tools .
265
98
  ```
266
99
 
267
- For local-only testing, you can set `MOM_LINK_PORT` without `MOM_LINK_URL`; mama will use `http://localhost:<port>` for the onboarding link.
268
-
269
- Users can then run `/login` in a private conversation with the bot. mama returns a 15-minute link for storing API keys or using built-in OAuth providers. `/login` is rejected in shared channels to avoid leaking onboarding links.
270
-
271
- On Slack, you can also register native slash commands such as `/pi-login` and `/pi-new`.
272
-
273
- - `/pi-login` in a shared channel opens a DM and continues the credential flow there.
274
- - `/pi-new` only works in a Slack DM and resets that DM session context.
100
+ mama creates one container per vault, attaches each to its own bridge network, mounts the workspace at `/workspace`, injects vault env, mounts declared credential files, and stops idle containers.
275
101
 
276
- ## Web Session Viewer
102
+ ### Firecracker / Cloudflare
277
103
 
278
- The same web portal used for `/login` can also render a read-only view of the current session.
104
+ See [docs/firecracker-setup.md](docs/firecracker-setup.md) and [examples/cloudflare-sandbox-bridge/README.md](examples/cloudflare-sandbox-bridge/README.md).
279
105
 
280
- - Users can send `session`, `/session`, or `/pi-session` in a private conversation / DM.
281
- - mama returns an expiring read-only link to `/session?token=...`.
282
- - The page shows the current branch timeline, including user messages, assistant replies, tool results, and compaction / branch summary events.
283
- - For now, session links are only issued from private conversations to avoid leaking shared-channel history.
106
+ ## `/login` and Web Session Viewer
284
107
 
285
- This feature uses the same `MOM_LINK_URL` / `MOM_LINK_PORT` configuration as `/login`.
108
+ ```bash
109
+ export MAMA_LINK_URL="https://mama.example.com" # public base URL
110
+ export MAMA_LINK_PORT=8181 # optional, defaults to 8181
111
+ ```
286
112
 
287
- Built-in OAuth guides:
113
+ For local testing you can set just `MAMA_LINK_PORT`; mama will use `http://localhost:<port>`.
288
114
 
289
- - [GitHub OAuth](docs/oauth/github.md)
290
- - [Google Workspace CLI OAuth](docs/oauth/google-workspace.md)
115
+ - `/login` (DM only) returns a 15-minute link to store API keys or run built-in OAuth flows ([GitHub](docs/oauth/github.md), [Google Workspace](docs/oauth/google-workspace.md)).
116
+ - `session` / `/session` (DM only) returns a read-only link showing the current session timeline.
117
+ - `new` / `/new` (DM only) resets the current session and starts fresh.
118
+ - `model` / `/model` / `/pi-model provider/model[:thinking]` switches the LLM for the current conversation, e.g. `/pi-model anthropic/claude-sonnet-4-5:off`.
119
+ - `stop` / `/stop` stops the current run. On Slack, use text commands so thread-local stop routing remains accurate.
120
+ - On Slack you can also register native commands like `/pi-login`, `/pi-session`, `/pi-model`, and `/pi-new`.
291
121
 
292
- Credentials are stored under `<state-dir>/vaults` (default `~/.mama/vaults`). Runtime env injection only happens in `container`, `image`, and `firecracker` modes.
122
+ Credentials are stored under `<state-dir>/vaults` (default `~/.mama/vaults`). Vault env is only injected in `container`, `image`, `firecracker`, and `cloudflare` modes.
293
123
 
294
124
  ## Configuration
295
125
 
296
- mama loads settings from `<state-dir>/settings.json` first, then falls back to `<working-directory>/settings.json` if the state-dir file is absent. For shared bot deployments, prefer the state-dir copy:
126
+ mama reads global settings from `<state-dir>/settings.json` (default `~/.mama/settings.json`, override via `--state-dir` or `MAMA_STATE_DIR`). This file is required and is created explicitly with `mama --onboard`. Per-conversation settings live at `<workingDir>/<conversationId>/settings.json` and override global settings for that conversation.
297
127
 
298
128
  ```json
299
129
  {
300
- "provider": "anthropic",
301
- "model": "claude-sonnet-4-5",
302
- "thinkingLevel": "off",
303
- "sessionScope": "thread",
304
- "logFormat": "console",
305
- "logLevel": "info",
306
- "sentryDsn": "https://examplePublicKey@o0.ingest.sentry.io/0"
130
+ "llm": {
131
+ "provider": "anthropic",
132
+ "model": "claude-sonnet-4-5",
133
+ "thinkingLevel": "off"
134
+ },
135
+ "log": {
136
+ "format": "console",
137
+ "level": "info"
138
+ },
139
+ "sentry": {
140
+ "dsn": "https://examplePublicKey@o0.ingest.sentry.io/0"
141
+ },
142
+ "sandbox": {
143
+ "cpus": "0.5",
144
+ "memory": "512m"
145
+ }
307
146
  }
308
147
  ```
309
148
 
310
- | Field | Default | Description |
311
- | --------------- | ------------------- | -------------------------------------------------------------------------------------------------------- |
312
- | `provider` | `anthropic` | AI provider (env: `MOM_AI_PROVIDER`) |
313
- | `model` | `claude-sonnet-4-5` | Model name (env: `MOM_AI_MODEL`) |
314
- | `thinkingLevel` | `off` | `off` / `low` / `medium` / `high` |
315
- | `sessionScope` | `thread` | Legacy compatibility setting. Current session boundaries are platform-defined by adapter/session policy. |
316
- | `logFormat` | `console` | `console` (colored stdout) or `json` (GCP Cloud Logging) |
317
- | `logLevel` | `info` | `trace` / `debug` / `info` / `warn` / `error` |
318
- | `sentryDsn` | unset | Sentry DSN (preferred over env `SENTRY_DSN`) |
319
-
320
- When `sentryDsn` is set, mama sends Sentry events with sensitive prompt/tool content redacted before upload.
321
-
322
- ### GCP Cloud Logging (Compute Engine)
323
-
324
- Set `logFormat: "json"` to send structured logs directly to Cloud Logging via API — no Ops Agent or log file configuration needed.
325
-
326
- **Requirements:**
327
-
328
- 1. VM service account has `roles/logging.logWriter`
329
- 2. `GOOGLE_CLOUD_PROJECT` env var is set
330
-
331
- ```bash
332
- GOOGLE_CLOUD_PROJECT=<your-project-id> mama <working-directory>
333
- ```
149
+ | Field | Default | Description |
150
+ | ------------------- | ------------------- | -------------------------------------------------------- |
151
+ | `llm.provider` | `anthropic` | AI provider |
152
+ | `llm.model` | `claude-sonnet-4-5` | Model name |
153
+ | `llm.thinkingLevel` | `off` | `off` / `low` / `medium` / `high` |
154
+ | `log.format` | `console` | `console` (colored stdout) or `json` (GCP Cloud Logging) |
155
+ | `log.level` | `info` | `trace` / `debug` / `info` / `warn` / `error` |
156
+ | `sentry.dsn` | unset | Sentry DSN; sensitive prompt/tool content is redacted |
157
+ | `sandbox.cpus` | unset | CPU limit for managed containers |
158
+ | `sandbox.memory` | unset | Memory limit for managed containers |
334
159
 
335
- In `<state-dir>/settings.json` (or `<working-directory>/settings.json` as a fallback):
160
+ Conversation-local settings written by `/pi-model` use the same shape and usually only include the override:
336
161
 
337
162
  ```json
338
163
  {
339
- "logFormat": "json",
340
- "logLevel": "info"
164
+ "llm": {
165
+ "provider": "anthropic",
166
+ "model": "claude-sonnet-4-5",
167
+ "thinkingLevel": "off"
168
+ }
341
169
  }
342
170
  ```
343
171
 
344
- Logs appear in Cloud Logging under **Log name: `mama`**. Console output (stdout) is unaffected and continues to work alongside Cloud Logging.
172
+ For GCP Cloud Logging, set `log.format: "json"`, give the VM service account `roles/logging.logWriter`, and export `GOOGLE_CLOUD_PROJECT`. Logs land under log name `mama`.
345
173
 
346
- ## State Directory Layout
174
+ ## Layout
347
175
 
348
176
  ```
349
177
  <state-dir>/
350
- ├── settings.json # Preferred provider/model/logging/Sentry config
178
+ ├── settings.json
351
179
  └── vaults/
352
- ├── bindings.json # Platform user -> vault mapping
353
- ├── vault.json # Vault metadata
180
+ ├── bindings.json # platform user -> vault mapping
181
+ ├── vault.json
354
182
  └── <vault-id>/
355
- ├── env # Injected env vars
356
- └── ... # Credential files (e.g. gws.json, .ssh/)
357
- ```
358
-
359
- ## Working Directory Layout
183
+ ├── env
184
+ └── ... # credential files
360
185
 
361
- ```
362
186
  <working-directory>/
363
- ├── settings.json # Optional fallback config if <state-dir>/settings.json is absent
364
- ├── MEMORY.md # Global memory (all channels)
365
- ├── SYSTEM.md # Installed packages / env changes log
366
- ├── skills/ # Global skills (CLI tools)
367
- ├── events/ # Scheduled event files
187
+ ├── MEMORY.md # global memory
188
+ ├── SYSTEM.md # installed packages / env log
189
+ ├── skills/ # global skills
190
+ ├── events/ # scheduled events
368
191
  └── <conversation-id>/
369
- ├── MEMORY.md # Conversation-specific memory
370
- ├── log.jsonl # Full message history
371
- ├── attachments/ # Downloaded user files
372
- ├── scratch/ # Agent working directory
373
- ├── skills/ # Conversation-specific skills
192
+ ├── MEMORY.md
193
+ ├── log.jsonl
194
+ ├── attachments/
195
+ ├── scratch/
196
+ ├── skills/
374
197
  └── sessions/
375
- ├── current # Pointer for the persistent top-level / direct session
376
- ├── 2026-04-05T18-04-31-010Z_1d92b3ad.jsonl
377
- └── <session-suffix>.jsonl # Fixed-path scoped session (thread / reply root)
378
198
  ```
379
199
 
380
- ## Container Sandbox
381
-
382
- ```bash
383
- # Create a container (mount your working directory to /workspace)
384
- docker run -d --name mama-tools \
385
- -v /path/to/workspace:/workspace \
386
- alpine:latest sleep infinity
387
-
388
- # Start mama with container sandbox
389
- mama --sandbox=container:mama-tools /path/to/workspace
390
- ```
200
+ ## Events
391
201
 
392
- `container:mama-tools` uses vault key `container-mama-tools`. If multiple users share the same container, they share that container vault.
202
+ Drop JSON files into `<working-directory>/events/`:
393
203
 
394
- ## Managed Per-User Container Sandbox
204
+ ```json
205
+ // Immediate
206
+ {"type": "immediate", "platform": "slack", "conversationId": "C0123456789", "conversationKind": "shared", "text": "Deploy finished"}
395
207
 
396
- ```bash
397
- # Pull the prebuilt image from GHCR
398
- # Release builds publish :tools, :<version>, and :latest / :beta
399
- # Pushes to main also publish :edge
400
- docker pull ghcr.io/geminixiang/mama-sandbox:tools
208
+ // One-shot
209
+ {"type": "one-shot", "platform": "telegram", "conversationId": "574247312", "conversationKind": "direct", "text": "Standup", "at": "2025-12-15T09:00:00+08:00"}
401
210
 
402
- # Start mama with managed image sandboxes
403
- mama --sandbox=image:ghcr.io/geminixiang/mama-sandbox:tools /path/to/workspace
211
+ // Periodic (cron)
212
+ {"type": "periodic", "platform": "discord", "conversationId": "1498975469343739948", "conversationKind": "shared", "text": "Check inbox", "schedule": "0 9 * * 1-5", "timezone": "Asia/Taipei"}
404
213
  ```
405
214
 
406
- Or build the bundled image locally:
215
+ ## Skills
407
216
 
408
- ```bash
409
- docker build -f docker/mama-sandbox.Dockerfile -t mama-sandbox:tools .
410
- mama --sandbox=image:mama-sandbox:tools /path/to/workspace
217
+ ```
218
+ skills/my-tool/
219
+ ├── SKILL.md # name + description frontmatter, usage docs
220
+ └── run.sh
411
221
  ```
412
222
 
413
- In this mode mama creates one Docker container per resolved vault/user, attaches each container to its own Docker bridge network for per-user network isolation, mounts the workspace at `/workspace`, injects vault env on execution, mounts any credential files declared in the vault, and stops idle containers automatically.
414
-
415
- ## Firecracker Sandbox
416
-
417
- Warning: Firecracker support is still in very early alpha. It is useful for experimentation, but it is not yet the recommended sandbox mode for normal development or production use. Prefer `image:<image>` unless you are actively validating Firecracker behavior.
418
-
419
- Firecracker provides lightweight VM isolation with the security benefits of a hypervisor. Unlike Docker containers, Firecracker runs a full Linux kernel, providing stronger isolation.
420
-
421
- ### Requirements
422
-
423
- - SSH access to the Firecracker VM
424
- - SSH key-based authentication configured
425
- - Host workspace must be mounted at `/workspace` inside the VM
426
-
427
- ### Format
223
+ ```yaml
224
+ ---
225
+ name: my-tool
226
+ description: Does something useful
227
+ ---
428
228
 
429
- ```
430
- --sandbox=firecracker:<vm-id>:<host-path>[:<ssh-user>[:<ssh-port>]]
229
+ Usage: {baseDir}/run.sh <args>
431
230
  ```
432
231
 
433
- | Parameter | Default | Description |
434
- | ----------- | ------- | ------------------------------ |
435
- | `vm-id` | - | VM identifier (hostname or IP) |
436
- | `host-path` | - | Working directory on the host |
437
- | `ssh-user` | `root` | SSH username |
438
- | `ssh-port` | `22` | SSH port |
439
-
440
- ### Examples
232
+ ## Slack: Download channel history
441
233
 
442
234
  ```bash
443
- # Basic usage (VM at 192.168.1.100, default ssh user root:22)
444
- mama --sandbox=firecracker:192.168.1.100:/home/user/workspace /home/user/workspace
445
-
446
- # Custom SSH user
447
- mama --sandbox=firecracker:192.168.1.100:/home/user/workspace:ubuntu /home/user/workspace
448
-
449
- # Custom SSH port
450
- mama --sandbox=firecracker:192.168.1.100:/home/user/workspace:root:2222 /home/user/workspace
235
+ mama --download C0123456789
451
236
  ```
452
237
 
453
- ### Setup
454
-
455
- 1. **Start a Firecracker VM** with your preferred method (fc-agent, firecracker-ctl, or manual)
456
-
457
- 2. **Configure SSH access** inside the VM:
458
-
459
- ```bash
460
- # Inside the VM - allow password-less SSH for mama
461
- sudo systemctl enable ssh
462
- sudo sed -i 's/^#*PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config
463
- sudo sed -i 's/^#*PubkeyAuthentication.*/PubkeyAuthentication yes/' /etc/ssh/sshd_config
464
- sudo systemctl restart ssh
465
- ```
466
-
467
- 3. **Mount your workspace** at `/workspace` inside the VM:
468
-
469
- ```bash
470
- # Option A: 9pfs (recommended, from host)
471
- sudo mount -t 9p -o trans=virtio,version=9p2000.L host0 /workspace
238
+ ## Production deployment (PM2)
472
239
 
473
- # Option B: NFS
474
- sudo mount -t nfs <host-ip>:/path/to/workspace /workspace
475
- ```
240
+ For long-running deployments, use [PM2](https://pm2.keymetrics.io/) as a process supervisor. It daemonizes mama, restarts on crash, and survives reboots.
476
241
 
477
- 4. **Test SSH connectivity** from host:
478
- ```bash
479
- ssh root@192.168.1.100 "echo works"
480
- ```
481
-
482
- The host path is mounted as `/workspace` inside the Firecracker VM. All bash commands will execute inside the VM.
483
-
484
- ## Events
485
-
486
- Drop JSON files into `<working-directory>/events/` to trigger the agent:
487
-
488
- ```json
489
- // Immediate — triggers as soon as mama sees the file
490
- {"type": "immediate", "platform": "slack", "conversationId": "C0123456789", "conversationKind": "shared", "text": "New deployment finished"}
242
+ ```bash
243
+ # 1. Install mama and pm2
244
+ npm i -g @geminixiang/mama pm2
491
245
 
492
- // One-shot triggers once at a specific time
493
- {"type": "one-shot", "platform": "telegram", "conversationId": "574247312", "conversationKind": "direct", "text": "Daily standup reminder", "at": "2025-12-15T09:00:00+08:00"}
246
+ # 2. Start the sandbox container (long-lived; mama execs into it)
247
+ docker pull ghcr.io/geminixiang/mama-sandbox:latest
494
248
 
495
- // Periodic triggers on a cron schedule
496
- {"type": "periodic", "platform": "discord", "conversationId": "1498975469343739948", "conversationKind": "shared", "text": "Check inbox", "schedule": "0 9 * * 1-5", "timezone": "Asia/Taipei"}
249
+ # 3. Grab the ecosystem file, edit args + env tokens, then start
250
+ curl -O https://raw.githubusercontent.com/geminixiang/mama/main/deploy/pm2/ecosystem.config.cjs
251
+ pm2 start ecosystem.config.cjs
252
+ pm2 save
253
+ pm2 startup # run the printed command to enable boot autostart
497
254
  ```
498
255
 
499
- ## Skills
500
-
501
- Create reusable CLI tools by adding a directory with a `SKILL.md`:
256
+ Upgrade flow:
502
257
 
503
- ```
504
- skills/
505
- └── my-tool/
506
- ├── SKILL.md # name + description frontmatter, usage docs
507
- └── run.sh # the actual script
258
+ ```bash
259
+ npm i -g @geminixiang/mama && pm2 reload mama
508
260
  ```
509
261
 
510
- `SKILL.md` frontmatter:
511
-
512
- ```yaml
513
- ---
514
- name: my-tool
515
- description: Does something useful
516
- ---
262
+ `pm2 reload` sends SIGTERM and waits up to `kill_timeout` (60s in the shipped config) before SIGKILL. mama's internal graceful shutdown drains in-flight LLM turns within that window, so reloads do not interrupt active conversations.
517
263
 
518
- Usage: {baseDir}/run.sh <args>
519
- ```
264
+ See [`deploy/pm2/ecosystem.config.cjs`](deploy/pm2/ecosystem.config.cjs) for all tunables.
520
265
 
521
266
  ## Development
522
267
 
523
268
  ```bash
524
269
  npm run dev # watch mode
525
- npm test # run tests
526
- npm run build # production build
270
+ npm test
271
+ npm run build
527
272
  ```
528
273
 
529
- ## 📦 Dependencies & Versions
530
-
531
- | Package | mama Version | pi-mom Synced Version |
532
- | ------------------------------- | ------------ | -------------------------------- |
533
- | `@mariozechner/pi-agent-core` | `^0.69.0` | ✅ Synchronized |
534
- | `@mariozechner/pi-ai` | `^0.69.0` | ✅ Synchronized |
535
- | `@mariozechner/pi-coding-agent` | `^0.69.0` | ✅ Synchronized |
536
- | `@anthropic-ai/sandbox-runtime` | `^0.0.49` | ⚠️ Newer than original fork base |
537
-
538
274
  ## License
539
275
 
540
- MIT — see [LICENSE](LICENSE).
541
-
542
- **Note**: This project inherits the MIT license from pi-mom and aims to keep its contributions compatible with the upstream ecosystem.
276
+ MIT — see [LICENSE](LICENSE). Inherits from pi-mom.