@geminixiang/mama 0.2.0-beta.1 → 0.2.0-beta.11

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 (271) hide show
  1. package/README.md +168 -371
  2. package/dist/adapter.d.ts +36 -12
  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 +12 -7
  6. package/dist/adapters/discord/bot.d.ts.map +1 -1
  7. package/dist/adapters/discord/bot.js +358 -135
  8. package/dist/adapters/discord/bot.js.map +1 -1
  9. package/dist/adapters/discord/context.d.ts +1 -1
  10. package/dist/adapters/discord/context.d.ts.map +1 -1
  11. package/dist/adapters/discord/context.js +100 -36
  12. package/dist/adapters/discord/context.js.map +1 -1
  13. package/dist/adapters/shared.d.ts +71 -0
  14. package/dist/adapters/shared.d.ts.map +1 -0
  15. package/dist/adapters/shared.js +168 -0
  16. package/dist/adapters/shared.js.map +1 -0
  17. package/dist/adapters/slack/bot.d.ts +30 -24
  18. package/dist/adapters/slack/bot.d.ts.map +1 -1
  19. package/dist/adapters/slack/bot.js +613 -224
  20. package/dist/adapters/slack/bot.js.map +1 -1
  21. package/dist/adapters/slack/branch-manager.d.ts +22 -0
  22. package/dist/adapters/slack/branch-manager.d.ts.map +1 -0
  23. package/dist/adapters/slack/branch-manager.js +97 -0
  24. package/dist/adapters/slack/branch-manager.js.map +1 -0
  25. package/dist/adapters/slack/context.d.ts +1 -1
  26. package/dist/adapters/slack/context.d.ts.map +1 -1
  27. package/dist/adapters/slack/context.js +127 -72
  28. package/dist/adapters/slack/context.js.map +1 -1
  29. package/dist/adapters/slack/session.d.ts +3 -0
  30. package/dist/adapters/slack/session.d.ts.map +1 -0
  31. package/dist/adapters/slack/session.js +16 -0
  32. package/dist/adapters/slack/session.js.map +1 -0
  33. package/dist/adapters/slack/tools/attach.d.ts +1 -1
  34. package/dist/adapters/slack/tools/attach.d.ts.map +1 -1
  35. package/dist/adapters/slack/tools/attach.js.map +1 -1
  36. package/dist/adapters/telegram/bot.d.ts +4 -2
  37. package/dist/adapters/telegram/bot.d.ts.map +1 -1
  38. package/dist/adapters/telegram/bot.js +193 -147
  39. package/dist/adapters/telegram/bot.js.map +1 -1
  40. package/dist/adapters/telegram/context.d.ts.map +1 -1
  41. package/dist/adapters/telegram/context.js +58 -111
  42. package/dist/adapters/telegram/context.js.map +1 -1
  43. package/dist/adapters/telegram/html.d.ts +3 -0
  44. package/dist/adapters/telegram/html.d.ts.map +1 -0
  45. package/dist/adapters/telegram/html.js +98 -0
  46. package/dist/adapters/telegram/html.js.map +1 -0
  47. package/dist/agent.d.ts +9 -13
  48. package/dist/agent.d.ts.map +1 -1
  49. package/dist/agent.js +601 -567
  50. package/dist/agent.js.map +1 -1
  51. package/dist/commands/auto-reply.d.ts +16 -0
  52. package/dist/commands/auto-reply.d.ts.map +1 -0
  53. package/dist/commands/auto-reply.js +69 -0
  54. package/dist/commands/auto-reply.js.map +1 -0
  55. package/dist/commands/index.d.ts +5 -0
  56. package/dist/commands/index.d.ts.map +1 -0
  57. package/dist/commands/index.js +19 -0
  58. package/dist/commands/index.js.map +1 -0
  59. package/dist/commands/login.d.ts +5 -0
  60. package/dist/commands/login.d.ts.map +1 -0
  61. package/dist/commands/login.js +76 -0
  62. package/dist/commands/login.js.map +1 -0
  63. package/dist/commands/model.d.ts +14 -0
  64. package/dist/commands/model.d.ts.map +1 -0
  65. package/dist/commands/model.js +112 -0
  66. package/dist/commands/model.js.map +1 -0
  67. package/dist/commands/new.d.ts +9 -0
  68. package/dist/commands/new.d.ts.map +1 -0
  69. package/dist/commands/new.js +28 -0
  70. package/dist/commands/new.js.map +1 -0
  71. package/dist/commands/registry.d.ts +7 -0
  72. package/dist/commands/registry.d.ts.map +1 -0
  73. package/dist/commands/registry.js +14 -0
  74. package/dist/commands/registry.js.map +1 -0
  75. package/dist/commands/sandbox.d.ts +10 -0
  76. package/dist/commands/sandbox.d.ts.map +1 -0
  77. package/dist/commands/sandbox.js +88 -0
  78. package/dist/commands/sandbox.js.map +1 -0
  79. package/dist/commands/session-view.d.ts +5 -0
  80. package/dist/commands/session-view.d.ts.map +1 -0
  81. package/dist/commands/session-view.js +62 -0
  82. package/dist/commands/session-view.js.map +1 -0
  83. package/dist/commands/types.d.ts +41 -0
  84. package/dist/commands/types.d.ts.map +1 -0
  85. package/dist/commands/types.js +2 -0
  86. package/dist/commands/types.js.map +1 -0
  87. package/dist/commands/utils.d.ts +8 -0
  88. package/dist/commands/utils.d.ts.map +1 -0
  89. package/dist/commands/utils.js +14 -0
  90. package/dist/commands/utils.js.map +1 -0
  91. package/dist/config.d.ts +49 -30
  92. package/dist/config.d.ts.map +1 -1
  93. package/dist/config.js +313 -75
  94. package/dist/config.js.map +1 -1
  95. package/dist/context.d.ts +10 -42
  96. package/dist/context.d.ts.map +1 -1
  97. package/dist/context.js +14 -127
  98. package/dist/context.js.map +1 -1
  99. package/dist/events.d.ts +13 -6
  100. package/dist/events.d.ts.map +1 -1
  101. package/dist/events.js +118 -64
  102. package/dist/events.js.map +1 -1
  103. package/dist/execution-resolver.d.ts +9 -5
  104. package/dist/execution-resolver.d.ts.map +1 -1
  105. package/dist/execution-resolver.js +82 -18
  106. package/dist/execution-resolver.js.map +1 -1
  107. package/dist/file-guards.d.ts +6 -0
  108. package/dist/file-guards.d.ts.map +1 -0
  109. package/dist/file-guards.js +48 -0
  110. package/dist/file-guards.js.map +1 -0
  111. package/dist/fs-atomic.d.ts +10 -0
  112. package/dist/fs-atomic.d.ts.map +1 -0
  113. package/dist/fs-atomic.js +45 -0
  114. package/dist/fs-atomic.js.map +1 -0
  115. package/dist/index.d.ts +7 -0
  116. package/dist/index.d.ts.map +1 -0
  117. package/dist/index.js +4 -0
  118. package/dist/index.js.map +1 -0
  119. package/dist/instrument.d.ts.map +1 -1
  120. package/dist/instrument.js +4 -11
  121. package/dist/instrument.js.map +1 -1
  122. package/dist/log.d.ts +1 -5
  123. package/dist/log.d.ts.map +1 -1
  124. package/dist/log.js +13 -38
  125. package/dist/log.js.map +1 -1
  126. package/dist/{login.d.ts → login/index.d.ts} +16 -4
  127. package/dist/login/index.d.ts.map +1 -0
  128. package/dist/{login.js → login/index.js} +55 -17
  129. package/dist/login/index.js.map +1 -0
  130. package/dist/{link-server.d.ts → login/portal.d.ts} +7 -4
  131. package/dist/login/portal.d.ts.map +1 -0
  132. package/dist/login/portal.js +1453 -0
  133. package/dist/login/portal.js.map +1 -0
  134. package/dist/{link-token.d.ts → login/session.d.ts} +4 -3
  135. package/dist/login/session.d.ts.map +1 -0
  136. package/dist/{link-token.js → login/session.js} +1 -1
  137. package/dist/login/session.js.map +1 -0
  138. package/dist/main.d.ts.map +1 -1
  139. package/dist/main.js +151 -373
  140. package/dist/main.js.map +1 -1
  141. package/dist/provisioner.d.ts +42 -52
  142. package/dist/provisioner.d.ts.map +1 -1
  143. package/dist/provisioner.js +256 -111
  144. package/dist/provisioner.js.map +1 -1
  145. package/dist/runtime/conversation-orchestrator.d.ts +42 -0
  146. package/dist/runtime/conversation-orchestrator.d.ts.map +1 -0
  147. package/dist/runtime/conversation-orchestrator.js +150 -0
  148. package/dist/runtime/conversation-orchestrator.js.map +1 -0
  149. package/dist/runtime/index.d.ts +2 -0
  150. package/dist/runtime/index.d.ts.map +1 -0
  151. package/dist/runtime/index.js +2 -0
  152. package/dist/runtime/index.js.map +1 -0
  153. package/dist/runtime/session-runtime.d.ts +27 -0
  154. package/dist/runtime/session-runtime.d.ts.map +1 -0
  155. package/dist/runtime/session-runtime.js +211 -0
  156. package/dist/runtime/session-runtime.js.map +1 -0
  157. package/dist/sandbox/cloudflare.d.ts +15 -0
  158. package/dist/sandbox/cloudflare.d.ts.map +1 -0
  159. package/dist/sandbox/cloudflare.js +137 -0
  160. package/dist/sandbox/cloudflare.js.map +1 -0
  161. package/dist/sandbox/container.d.ts +2 -1
  162. package/dist/sandbox/container.d.ts.map +1 -1
  163. package/dist/sandbox/container.js +5 -1
  164. package/dist/sandbox/container.js.map +1 -1
  165. package/dist/sandbox/firecracker.d.ts +2 -1
  166. package/dist/sandbox/firecracker.d.ts.map +1 -1
  167. package/dist/sandbox/firecracker.js +6 -0
  168. package/dist/sandbox/firecracker.js.map +1 -1
  169. package/dist/sandbox/host.d.ts +2 -3
  170. package/dist/sandbox/host.d.ts.map +1 -1
  171. package/dist/sandbox/host.js +5 -5
  172. package/dist/sandbox/host.js.map +1 -1
  173. package/dist/sandbox/index.d.ts +6 -4
  174. package/dist/sandbox/index.d.ts.map +1 -1
  175. package/dist/sandbox/index.js +9 -6
  176. package/dist/sandbox/index.js.map +1 -1
  177. package/dist/sandbox/path-context.d.ts +4 -0
  178. package/dist/sandbox/path-context.d.ts.map +1 -0
  179. package/dist/sandbox/path-context.js +20 -0
  180. package/dist/sandbox/path-context.js.map +1 -0
  181. package/dist/sandbox/types.d.ts +17 -1
  182. package/dist/sandbox/types.d.ts.map +1 -1
  183. package/dist/sandbox/types.js.map +1 -1
  184. package/dist/sentry.d.ts +1 -1
  185. package/dist/sentry.d.ts.map +1 -1
  186. package/dist/sentry.js +4 -2
  187. package/dist/sentry.js.map +1 -1
  188. package/dist/session-policy.d.ts +13 -0
  189. package/dist/session-policy.d.ts.map +1 -0
  190. package/dist/session-policy.js +23 -0
  191. package/dist/session-policy.js.map +1 -0
  192. package/dist/session-store.d.ts +34 -3
  193. package/dist/session-store.d.ts.map +1 -1
  194. package/dist/session-store.js +184 -22
  195. package/dist/session-store.js.map +1 -1
  196. package/dist/session-view/command.d.ts +5 -0
  197. package/dist/session-view/command.d.ts.map +1 -0
  198. package/dist/session-view/command.js +11 -0
  199. package/dist/session-view/command.js.map +1 -0
  200. package/dist/session-view/portal.d.ts +16 -0
  201. package/dist/session-view/portal.d.ts.map +1 -0
  202. package/dist/session-view/portal.js +1742 -0
  203. package/dist/session-view/portal.js.map +1 -0
  204. package/dist/session-view/service.d.ts +34 -0
  205. package/dist/session-view/service.d.ts.map +1 -0
  206. package/dist/session-view/service.js +427 -0
  207. package/dist/session-view/service.js.map +1 -0
  208. package/dist/session-view/store.d.ts +18 -0
  209. package/dist/session-view/store.d.ts.map +1 -0
  210. package/dist/session-view/store.js +39 -0
  211. package/dist/session-view/store.js.map +1 -0
  212. package/dist/store.d.ts +3 -6
  213. package/dist/store.d.ts.map +1 -1
  214. package/dist/store.js +22 -48
  215. package/dist/store.js.map +1 -1
  216. package/dist/tool-diagnostics.d.ts +2 -0
  217. package/dist/tool-diagnostics.d.ts.map +1 -0
  218. package/dist/tool-diagnostics.js +7 -0
  219. package/dist/tool-diagnostics.js.map +1 -0
  220. package/dist/tools/bash.d.ts +1 -1
  221. package/dist/tools/bash.d.ts.map +1 -1
  222. package/dist/tools/bash.js.map +1 -1
  223. package/dist/tools/edit.d.ts +1 -1
  224. package/dist/tools/edit.d.ts.map +1 -1
  225. package/dist/tools/edit.js.map +1 -1
  226. package/dist/tools/event.d.ts +43 -2
  227. package/dist/tools/event.d.ts.map +1 -1
  228. package/dist/tools/event.js +48 -13
  229. package/dist/tools/event.js.map +1 -1
  230. package/dist/tools/index.d.ts +2 -1
  231. package/dist/tools/index.d.ts.map +1 -1
  232. package/dist/tools/index.js +3 -3
  233. package/dist/tools/index.js.map +1 -1
  234. package/dist/tools/read.d.ts +1 -1
  235. package/dist/tools/read.d.ts.map +1 -1
  236. package/dist/tools/read.js.map +1 -1
  237. package/dist/tools/write.d.ts +1 -1
  238. package/dist/tools/write.d.ts.map +1 -1
  239. package/dist/tools/write.js.map +1 -1
  240. package/dist/trigger.d.ts +31 -0
  241. package/dist/trigger.d.ts.map +1 -0
  242. package/dist/trigger.js +98 -0
  243. package/dist/trigger.js.map +1 -0
  244. package/dist/ui-copy.d.ts +1 -0
  245. package/dist/ui-copy.d.ts.map +1 -1
  246. package/dist/ui-copy.js +3 -0
  247. package/dist/ui-copy.js.map +1 -1
  248. package/dist/vault-routing.d.ts +1 -7
  249. package/dist/vault-routing.d.ts.map +1 -1
  250. package/dist/vault-routing.js +6 -48
  251. package/dist/vault-routing.js.map +1 -1
  252. package/dist/vault.d.ts +21 -55
  253. package/dist/vault.d.ts.map +1 -1
  254. package/dist/vault.js +144 -263
  255. package/dist/vault.js.map +1 -1
  256. package/package.json +12 -10
  257. package/dist/bindings.d.ts +0 -63
  258. package/dist/bindings.d.ts.map +0 -1
  259. package/dist/bindings.js +0 -94
  260. package/dist/bindings.js.map +0 -1
  261. package/dist/link-server.d.ts.map +0 -1
  262. package/dist/link-server.js +0 -839
  263. package/dist/link-server.js.map +0 -1
  264. package/dist/link-token.d.ts.map +0 -1
  265. package/dist/link-token.js.map +0 -1
  266. package/dist/login.d.ts.map +0 -1
  267. package/dist/login.js.map +0 -1
  268. package/dist/vault.test.d.ts +0 -2
  269. package/dist/vault.test.d.ts.map +0 -1
  270. package/dist/vault.test.js +0 -67
  271. package/dist/vault.test.js.map +0 -1
package/README.md CHANGED
@@ -1,70 +1,35 @@
1
- # mama
1
+ # mama (Multi-Agent Mischief Assistant)
2
2
 
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 AI agent bot 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 shared containers, per-user containers, or Firecracker VMs (see [docs/sandbox.md](docs/sandbox.md))
51
- - **Persistent memory** — workspace-level and channel-level `MEMORY.md` files
52
- - **Skills** — drop custom CLI tools into `skills/` directories
53
- - **Event system** — schedule one-shot or recurring tasks via JSON files
54
- - **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`
55
21
 
56
22
  ## Platform Session Model
57
23
 
58
- | Platform | User Interaction Structure | `sessionKey` Rule | Default Session Model | Special Handling Needed | Notes |
59
- | -------- | ----------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ----------------------- | ------------------------------------------------------------------------------------------------ |
60
- | Slack | channel top-level + thread replies | top-level: `channelId`; thread: `channelId:threadTs` | channel keeps one persistent session; thread forks from channel into its own session | High | channel -> thread inherits context via fork; thread -> channel does not merge back automatically |
61
- | Discord | normal messages, replies, thread channels | `channelId:threadTsOrMsgId` | replies / thread channels naturally map to isolated sessions | Low | no aliasing layer needed; session identity is determined directly from the Discord event |
62
- | Telegram | private chats, group replies | private chat: `chatId`; group reply chain: `chatId:replyToIdOrMsgId` | private chats use one long session; groups split by reply chain | Medium | Telegram has no native thread model; group sessions are modeled 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 |
63
29
 
64
30
  ## Requirements
65
31
 
66
32
  - Node.js >= 20
67
- - One of the platform integrations below
68
33
 
69
34
  ## Installation
70
35
 
@@ -72,416 +37,248 @@ We actively track the upstream `pi-mom` and plan to:
72
37
  npm install -g @geminixiang/mama
73
38
  ```
74
39
 
75
- Or run directly after cloning:
40
+ Or from source:
76
41
 
77
42
  ```bash
78
- npm install
79
- npm run build
43
+ npm install && npm run build
80
44
  ```
81
45
 
82
- ---
83
-
84
46
  ## Quick Start
85
47
 
86
- ### Slack
87
-
88
- 1. Create a Slack app with **Socket Mode** enabled ([setup guide](docs/slack-bot-minimal-guide.md)).
89
- 2. Add the following **OAuth Bot Token Scopes**:
90
- - `app_mentions:read`, `channels:history`, `channels:read`, `chat:write`
91
- - `files:read`, `files:write`, `groups:history`, `groups:read`
92
- - `im:history`, `im:read`, `im:write`, `users:read`
93
- - `assistant:write` — required for native "Thinking" status indicator
94
- 3. Enable the **Home Tab** and **Agent mode**:
95
- - **App Home → Show Tabs** — toggle **Home Tab** on
96
- - **App Home → Agents & AI Apps** — toggle **Agent or Assistant** on
97
- 4. Subscribe to **Bot Events**:
98
- - `app_home_opened`, `app_mention`
99
- - `assistant_thread_context_changed`, `assistant_thread_started`
100
- - `message.channels`, `message.groups`, `message.im`
101
- 5. Enable **Interactivity** (Settings → Interactivity & Shortcuts → toggle on).
102
- 6. Copy the **App-Level Token** (`xapp-…`) and **Bot Token** (`xoxb-…`).
103
-
104
- Or import this **App Manifest** directly (Settings → App Manifest → paste JSON):
105
-
106
- <details>
107
- <summary>Example App Manifest</summary>
48
+ All platforms share the same CLI:
108
49
 
109
- ```json
110
- {
111
- "display_information": {
112
- "name": "mama"
113
- },
114
- "features": {
115
- "app_home": {
116
- "home_tab_enabled": true,
117
- "messages_tab_enabled": false,
118
- "messages_tab_read_only_enabled": false
119
- },
120
- "bot_user": {
121
- "display_name": "mama",
122
- "always_online": false
123
- }
124
- },
125
- "oauth_config": {
126
- "scopes": {
127
- "bot": [
128
- "app_mentions:read",
129
- "assistant:write",
130
- "channels:history",
131
- "channels:read",
132
- "chat:write",
133
- "files:read",
134
- "files:write",
135
- "groups:history",
136
- "groups:read",
137
- "im:history",
138
- "im:read",
139
- "im:write",
140
- "users:read"
141
- ]
142
- }
143
- },
144
- "settings": {
145
- "event_subscriptions": {
146
- "bot_events": [
147
- "app_home_opened",
148
- "app_mention",
149
- "assistant_thread_context_changed",
150
- "assistant_thread_started",
151
- "message.channels",
152
- "message.groups",
153
- "message.im"
154
- ]
155
- },
156
- "interactivity": {
157
- "is_enabled": true
158
- },
159
- "org_deploy_enabled": false,
160
- "socket_mode_enabled": true,
161
- "token_rotation_enabled": false
162
- }
163
- }
50
+ ```bash
51
+ mama [--state-dir=~/.mama] [--sandbox=<mode>] <working-directory>
164
52
  ```
165
53
 
166
- </details>
54
+ Set the platform tokens you need (you can run multiple platforms at once):
167
55
 
168
56
  ```bash
169
- export MOM_SLACK_APP_TOKEN=xapp-...
170
- export MOM_SLACK_BOT_TOKEN=xoxb-...
171
-
172
- mama [--sandbox=host|container:<container>] <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...
173
61
  ```
174
62
 
175
- The bot responds when `@mentioned` in any channel or via DM.
63
+ ### Slack
176
64
 
177
- - **Top-level channel messages** share one persistent channel session.
178
- - **Thread replies** — fork from the channel session into an isolated thread session.
179
- - **Thread memory** — inherited at fork time only; thread changes do not merge back into the channel automatically.
180
-
181
- ---
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.
182
66
 
183
67
  ### Telegram
184
68
 
185
- 1. Message [@BotFather](https://t.me/BotFather) `/newbot` to create a bot and get the **Bot Token**.
186
- 2. Optionally disable privacy mode (`/setprivacy → Disable`) so the bot can read group messages without being `@mentioned`.
187
- 3. For OAuth login setup, see [docs/oauth/github.md](docs/oauth/github.md) and [docs/oauth/google-workspace.md](docs/oauth/google-workspace.md).
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.
188
70
 
189
- ```bash
190
- export MOM_TELEGRAM_BOT_TOKEN=123456:ABC-...
71
+ ### Discord
191
72
 
192
- mama [--sandbox=host|container:<container>] <working-directory>
193
- ```
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.
194
74
 
195
- - **Private chats** — every message is forwarded to the bot automatically.
196
- - **Group chats** — the bot only responds when `@mentioned` by username.
197
- - **Reply chains** — replying to a previous message continues the same session.
198
- - Say `stop` or `/stop` to cancel a running task.
75
+ ## Sandbox Modes
199
76
 
200
- ---
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) |
201
84
 
202
- ### Discord
85
+ Vault routing: `image`, `firecracker`, and `cloudflare` resolve a vault per platform userId. See [docs/sandbox.md](docs/sandbox.md) for the full matrix.
203
86
 
204
- 1. Go to the [Discord Developer Portal](https://discord.com/developers/applications) → **New Application**.
205
- 2. Under **Bot**, enable **Message Content Intent** (required to read message text).
206
- 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.
207
- 4. Copy the **Bot Token**.
87
+ ### Managed per-user containers (`image:*`)
208
88
 
209
89
  ```bash
210
- export MOM_DISCORD_BOT_TOKEN=MTI...
211
-
212
- mama [--sandbox=host|container:<container>] <working-directory>
90
+ docker pull ghcr.io/geminixiang/mama-sandbox:latest
91
+ mama --sandbox=image:ghcr.io/geminixiang/mama-sandbox:latest /path/to/workspace
213
92
  ```
214
93
 
215
- - **Server channels** — the bot responds when `@mentioned`.
216
- - **DMs** — every message is forwarded automatically.
217
- - **Threads** — messages inside a Discord thread share a single session.
218
- - **Reply chains** — replying to a message continues that session.
219
- - Say `stop` or `/stop` to cancel a running task.
94
+ Or build locally:
220
95
 
221
- ---
222
-
223
- ## Options
96
+ ```bash
97
+ docker build -f docker/mama-sandbox.Dockerfile -t mama-sandbox:tools .
98
+ ```
224
99
 
225
- | Option | Default | Description |
226
- | -------------------------------------- | --------- | ----------------------------------------------------------------------- |
227
- | `--sandbox=host` | ✓ | Run commands directly on host |
228
- | `--sandbox=container:<name>` | | Run commands in a shared container (mama does not manage lifecycle) |
229
- | `--sandbox=image:<image>` | | Auto-provision one Docker container per platform user from an image |
230
- | `--sandbox=firecracker:<vm-id>:<path>` | | Run commands inside a Firecracker microVM |
231
- | `--state-dir <path>` | `~/.mama` | Store operator-managed settings, vaults, and bindings outside workspace |
232
- | `--download <channel-id>` | | Download channel history to stdout and exit (Slack only) |
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.
233
101
 
234
- ### Container Mode Semantics
102
+ ### Firecracker / Cloudflare
235
103
 
236
- - `container:*` uses one shared container for all sessions/users. mama does not create/start/stop/delete this container.
237
- - `image:*` creates and restarts per-user containers named from the platform/user id. mama manages this container lifecycle.
238
- - `docker:*` is not supported; use `container:*` for a shared existing container or `image:*` for mama-managed per-user containers.
104
+ See [docs/firecracker-setup.md](docs/firecracker-setup.md) and [examples/cloudflare-sandbox-bridge/README.md](examples/cloudflare-sandbox-bridge/README.md).
239
105
 
240
- ### Download channel history (Slack)
106
+ ## `/login` and Web Session Viewer
241
107
 
242
108
  ```bash
243
- mama --download C0123456789
109
+ export MAMA_LINK_URL="https://mama.example.com" # public base URL
110
+ export MAMA_LINK_PORT=8181 # optional, defaults to 8181
244
111
  ```
245
112
 
113
+ For local testing you can set just `MAMA_LINK_PORT`; mama will use `http://localhost:<port>`.
114
+
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-6:off`.
119
+ - `auto-reply` / `/pi-auto-reply on|off|status` controls group/channel auto-reply for the current conversation. Rules live in the conversation's `auto-reply` marker file.
120
+ - `stop` / `/stop` stops the current run. On Slack, use text commands so thread-local stop routing remains accurate.
121
+ - On Slack you can also register native commands like `/pi-login`, `/pi-session`, `/pi-model`, `/pi-auto-reply`, and `/pi-new`.
122
+
123
+ Credentials are stored under `<state-dir>/vaults` (default `~/.mama/vaults`). Vault env is only injected in `container`, `image`, `firecracker`, and `cloudflare` modes.
124
+
246
125
  ## Configuration
247
126
 
248
- mama stores operator-managed configuration in `~/.mama` by default. Use `--state-dir <path>` to choose another location. Create or edit `settings.json` there:
127
+ 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.
249
128
 
250
129
  ```json
251
130
  {
252
- "provider": "anthropic",
253
- "model": "claude-sonnet-4-6",
254
- "thinkingLevel": "off",
255
- "sessionScope": "thread",
256
- "logFormat": "console",
257
- "logLevel": "info",
258
- "sentryDsn": "https://examplePublicKey@o0.ingest.sentry.io/0"
131
+ "llm": {
132
+ "provider": "anthropic",
133
+ "model": "claude-sonnet-4-6",
134
+ "thinkingLevel": "off"
135
+ },
136
+ "log": {
137
+ "format": "console",
138
+ "level": "info"
139
+ },
140
+ "sentry": {
141
+ "dsn": "https://examplePublicKey@o0.ingest.sentry.io/0"
142
+ },
143
+ "sandbox": {
144
+ "cpus": "0.5",
145
+ "memory": "512m",
146
+ "boost": {
147
+ "cpus": "2",
148
+ "memory": "4g"
149
+ }
150
+ }
259
151
  }
260
152
  ```
261
153
 
262
- | Field | Default | Description |
263
- | --------------- | ------------------- | -------------------------------------------------------- |
264
- | `provider` | `anthropic` | AI provider (env: `MOM_AI_PROVIDER`) |
265
- | `model` | `claude-sonnet-4-6` | Model name (env: `MOM_AI_MODEL`) |
266
- | `thinkingLevel` | `off` | `off` / `low` / `medium` / `high` |
267
- | `sessionScope` | `thread` | `thread` (per thread/reply chain) or `channel` |
268
- | `logFormat` | `console` | `console` (colored stdout) or `json` (GCP Cloud Logging) |
269
- | `logLevel` | `info` | `trace` / `debug` / `info` / `warn` / `error` |
270
- | `sentryDsn` | unset | Sentry DSN (preferred over env `SENTRY_DSN`) |
271
-
272
- When `sentryDsn` is set, mama sends Sentry events with sensitive prompt/tool content redacted before upload.
273
-
274
- ### GCP Cloud Logging (Compute Engine)
154
+ | Field | Default | Description |
155
+ | ---------------------- | ------------------- | -------------------------------------------------------- |
156
+ | `llm.provider` | `anthropic` | AI provider |
157
+ | `llm.model` | `claude-sonnet-4-6` | Model name |
158
+ | `llm.thinkingLevel` | `off` | `off` / `low` / `medium` / `high` |
159
+ | `log.format` | `console` | `console` (colored stdout) or `json` (GCP Cloud Logging) |
160
+ | `log.level` | `info` | `trace` / `debug` / `info` / `warn` / `error` |
161
+ | `sentry.dsn` | unset | Sentry DSN; sensitive prompt/tool content is redacted |
162
+ | `sandbox.cpus` | unset | CPU limit for managed containers |
163
+ | `sandbox.memory` | unset | Memory limit for managed containers |
164
+ | `sandbox.boost.cpus` | unset | Temporary CPU limit used by `/pi-sandbox boost` |
165
+ | `sandbox.boost.memory` | unset | Temporary memory limit used by `/pi-sandbox boost` |
275
166
 
276
- Set `logFormat: "json"` to send structured logs directly to Cloud Logging via API no Ops Agent or log file configuration needed.
167
+ `/pi-sandbox` shows the current managed-container CPU/memory limits. `/pi-sandbox boost` temporarily applies `sandbox.boost` to the current conversation; the boost ends when that sandbox container is stopped.
277
168
 
278
- **Requirements:**
279
-
280
- 1. VM service account has `roles/logging.logWriter`
281
- 2. `GOOGLE_CLOUD_PROJECT` env var is set
282
-
283
- ```bash
284
- GOOGLE_CLOUD_PROJECT=<your-project-id> mama <working-directory>
285
- ```
286
-
287
- `settings.json`:
169
+ Conversation-local settings written by `/pi-model` use the same shape and usually only include the override:
288
170
 
289
171
  ```json
290
172
  {
291
- "logFormat": "json",
292
- "logLevel": "info"
173
+ "llm": {
174
+ "provider": "anthropic",
175
+ "model": "claude-sonnet-4-6",
176
+ "thinkingLevel": "off"
177
+ }
293
178
  }
294
179
  ```
295
180
 
296
- Logs appear in Cloud Logging under **Log name: `mama`**. Console output (stdout) is unaffected and continues to work alongside Cloud Logging.
181
+ 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`.
297
182
 
298
- ## Working Directory Layout
183
+ ## Layout
299
184
 
300
185
  ```
186
+ <state-dir>/
187
+ ├── settings.json
188
+ └── vaults/
189
+ └── <vault-id>/
190
+ ├── env
191
+ └── ... # credential files
192
+
301
193
  <working-directory>/
302
- ├── MEMORY.md # Global memory (all channels)
303
- ├── SYSTEM.md # Installed packages / env changes log
304
- ├── skills/ # Global skills (CLI tools)
305
- ├── events/ # Scheduled event files
306
- └── <channel-id>/
307
- ├── MEMORY.md # Channel-specific memory
308
- ├── log.jsonl # Full message history
309
- ├── attachments/ # Downloaded user files
310
- ├── scratch/ # Agent working directory
311
- ├── skills/ # Channel-specific skills
194
+ ├── MEMORY.md # global memory
195
+ ├── SYSTEM.md # installed packages / env log
196
+ ├── skills/ # global skills
197
+ ├── events/ # scheduled events
198
+ └── <conversation-id>/
199
+ ├── MEMORY.md
200
+ ├── auto-reply[.disabled] # optional channel auto-reply rules
201
+ ├── log.jsonl
202
+ ├── attachments/
203
+ ├── scratch/
204
+ ├── skills/
312
205
  └── sessions/
313
- ├── current # Pointer for the current channel session file
314
- ├── 2026-04-05T18-04-31-010Z_1d92b3ad.jsonl
315
- └── <thread-ts>.jsonl # Fixed-path thread session file
316
206
  ```
317
207
 
318
- Operator-managed state lives outside the workspace:
208
+ ## Events
319
209
 
320
- ```
321
- <state-dir>/
322
- ├── settings.json # AI provider/model/Sentry config
323
- └── vaults/
324
- ├── vault.json # Per-user vault routing
325
- └── bindings.json # Optional platform-user to vault mapping
326
- ```
210
+ Drop JSON files into `<working-directory>/events/`:
327
211
 
328
- ## Container Sandbox
212
+ ```json
213
+ // Immediate
214
+ {"type": "immediate", "platform": "slack", "conversationId": "C0123456789", "conversationKind": "shared", "text": "Deploy finished"}
329
215
 
330
- ```bash
331
- # Create a container (mount your working directory to /workspace)
332
- docker run -d --name mama-sandbox \
333
- -v /path/to/workspace:/workspace \
334
- alpine:latest sleep infinity
216
+ // One-shot
217
+ {"type": "one-shot", "platform": "telegram", "conversationId": "574247312", "conversationKind": "direct", "text": "Standup", "at": "2025-12-15T09:00:00+08:00"}
335
218
 
336
- # Start mama with container sandbox
337
- mama --sandbox=container:mama-sandbox /path/to/workspace
219
+ // Periodic (cron)
220
+ {"type": "periodic", "platform": "discord", "conversationId": "1498975469343739948", "conversationKind": "shared", "text": "Check inbox", "schedule": "0 9 * * 1-5", "timezone": "Asia/Taipei"}
338
221
  ```
339
222
 
340
- ## Managed Per-User Container Sandbox
341
-
342
- ```bash
343
- # Build the bundled sandbox image once
344
- docker build -f docker/mama-sandbox.Dockerfile -t mama-sandbox:tools .
223
+ ## Skills
345
224
 
346
- # Then use it for per-user managed containers
347
- mama --sandbox=image:mama-sandbox:tools /path/to/workspace
225
+ ```
226
+ skills/my-tool/
227
+ ├── SKILL.md # name + description frontmatter, usage docs
228
+ └── run.sh
348
229
  ```
349
230
 
350
- In this mode mama creates one container per platform user, mounts the workspace at `/workspace`, injects that user's vault environment variables into tool execution, and stops idle containers after the configured idle window. Containers are labeled for management (`mama.managed=true`, `mama.sandbox=image`, `mama.vault-id=<id>`), and mama reconciles managed containers on startup/restart (including legacy `mama-sandbox-*` containers).
351
-
352
- The bundled image at `docker/mama-sandbox.Dockerfile` starts from `ubuntu:24.04` and installs:
353
-
354
- - `gh`
355
- - `nvm` with Node.js and npm
356
- - `uv`
357
- - `gcloud`, `gsutil`, `bq`
358
- - `gws` via `npm install -g @googleworkspace/cli`
359
-
360
- The image symlinks those tools into `/usr/local/bin` so they remain available when mama executes commands with `sh -c` inside the sandbox container.
361
-
362
- ## Firecracker Sandbox
363
-
364
- Firecracker provides lightweight VM isolation with the security benefits of a hypervisor. Unlike Docker containers, Firecracker runs a full Linux kernel, providing stronger isolation.
365
-
366
- ### Requirements
367
-
368
- - SSH access to the Firecracker VM
369
- - SSH key-based authentication configured
370
- - Host workspace must be mounted at `/workspace` inside the VM
371
-
372
- ### Format
231
+ ```yaml
232
+ ---
233
+ name: my-tool
234
+ description: Does something useful
235
+ ---
373
236
 
237
+ Usage: {baseDir}/run.sh <args>
374
238
  ```
375
- --sandbox=firecracker:<vm-id>:<host-path>[:<ssh-user>[:<ssh-port>]]
376
- ```
377
-
378
- | Parameter | Default | Description |
379
- | ----------- | ------- | ------------------------------ |
380
- | `vm-id` | - | VM identifier (hostname or IP) |
381
- | `host-path` | - | Working directory on the host |
382
- | `ssh-user` | `root` | SSH username |
383
- | `ssh-port` | `22` | SSH port |
384
239
 
385
- ### Examples
240
+ ## Slack: Download channel history
386
241
 
387
242
  ```bash
388
- # Basic usage (VM at 192.168.1.100, default ssh user root:22)
389
- mama --sandbox=firecracker:192.168.1.100:/home/user/workspace /home/user/workspace
390
-
391
- # Custom SSH user
392
- mama --sandbox=firecracker:192.168.1.100:/home/user/workspace:ubuntu /home/user/workspace
393
-
394
- # Custom SSH port
395
- mama --sandbox=firecracker:192.168.1.100:/home/user/workspace:root:2222 /home/user/workspace
243
+ mama --download C0123456789
396
244
  ```
397
245
 
398
- ### Setup
399
-
400
- 1. **Start a Firecracker VM** with your preferred method (fc-agent, firecracker-ctl, or manual)
401
-
402
- 2. **Configure SSH access** inside the VM:
403
-
404
- ```bash
405
- # Inside the VM - allow password-less SSH for mama
406
- sudo systemctl enable ssh
407
- sudo sed -i 's/^#*PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config
408
- sudo sed -i 's/^#*PubkeyAuthentication.*/PubkeyAuthentication yes/' /etc/ssh/sshd_config
409
- sudo systemctl restart ssh
410
- ```
411
-
412
- 3. **Mount your workspace** at `/workspace` inside the VM:
413
-
414
- ```bash
415
- # Option A: 9pfs (recommended, from host)
416
- sudo mount -t 9p -o trans=virtio,version=9p2000.L host0 /workspace
417
-
418
- # Option B: NFS
419
- sudo mount -t nfs <host-ip>:/path/to/workspace /workspace
420
- ```
246
+ ## Production deployment (PM2)
421
247
 
422
- 4. **Test SSH connectivity** from host:
423
- ```bash
424
- ssh root@192.168.1.100 "echo works"
425
- ```
248
+ For long-running deployments, use [PM2](https://pm2.keymetrics.io/) as a process supervisor. It daemonizes mama, restarts on crash, and survives reboots.
426
249
 
427
- The host path is mounted as `/workspace` inside the Firecracker VM. All bash commands will execute inside the VM.
428
-
429
- ## Events
430
-
431
- Drop JSON files into `<working-directory>/events/` to trigger the agent:
432
-
433
- ```json
434
- // Immediate — triggers as soon as mama sees the file
435
- {"type": "immediate", "channelId": "C0123456789", "text": "New deployment finished"}
250
+ ```bash
251
+ # 1. Install mama and pm2
252
+ npm i -g @geminixiang/mama pm2
436
253
 
437
- // One-shot triggers once at a specific time
438
- {"type": "one-shot", "channelId": "C0123456789", "text": "Daily standup reminder", "at": "2025-12-15T09:00:00+08:00"}
254
+ # 2. Start the sandbox container (long-lived; mama execs into it)
255
+ docker pull ghcr.io/geminixiang/mama-sandbox:latest
439
256
 
440
- // Periodic triggers on a cron schedule
441
- {"type": "periodic", "channelId": "C0123456789", "text": "Check inbox", "schedule": "0 9 * * 1-5", "timezone": "Asia/Taipei"}
257
+ # 3. Grab the ecosystem file, edit args + env tokens, then start
258
+ curl -O https://raw.githubusercontent.com/geminixiang/mama/main/deploy/pm2/ecosystem.config.cjs
259
+ pm2 start ecosystem.config.cjs
260
+ pm2 save
261
+ pm2 startup # run the printed command to enable boot autostart
442
262
  ```
443
263
 
444
- ## Skills
445
-
446
- Create reusable CLI tools by adding a directory with a `SKILL.md`:
264
+ Upgrade flow:
447
265
 
448
- ```
449
- skills/
450
- └── my-tool/
451
- ├── SKILL.md # name + description frontmatter, usage docs
452
- └── run.sh # the actual script
266
+ ```bash
267
+ npm i -g @geminixiang/mama && pm2 reload mama
453
268
  ```
454
269
 
455
- `SKILL.md` frontmatter:
270
+ `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.
456
271
 
457
- ```yaml
458
- ---
459
- name: my-tool
460
- description: Does something useful
461
- ---
462
-
463
- Usage: {baseDir}/run.sh <args>
464
- ```
272
+ See [`deploy/pm2/ecosystem.config.cjs`](deploy/pm2/ecosystem.config.cjs) for all tunables.
465
273
 
466
274
  ## Development
467
275
 
468
276
  ```bash
469
277
  npm run dev # watch mode
470
- npm test # run tests
471
- npm run build # production build
278
+ npm test
279
+ npm run build
472
280
  ```
473
281
 
474
- ## 📦 Dependencies & Versions
475
-
476
- | Package | mama Version | pi-mom Synced Version |
477
- | ------------------------------- | ------------ | ----------------------------- |
478
- | `@mariozechner/pi-agent-core` | `^0.57.1` | ✅ Synchronized |
479
- | `@mariozechner/pi-ai` | `^0.57.1` | ✅ Synchronized |
480
- | `@mariozechner/pi-coding-agent` | `^0.57.1` | ✅ Synchronized |
481
- | `@anthropic-ai/sandbox-runtime` | `^0.0.40` | ⚠️ Newer (pi-mom uses 0.0.16) |
482
-
483
282
  ## License
484
283
 
485
- MIT — see [LICENSE](LICENSE).
486
-
487
- **Note**: This project inherits the MIT license from pi-mom and aims to keep its contributions compatible with the upstream ecosystem.
284
+ MIT — see [LICENSE](LICENSE). Inherits from pi-mom.