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

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 (149) hide show
  1. package/README.md +133 -78
  2. package/dist/adapter.d.ts +22 -10
  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 +10 -7
  6. package/dist/adapters/discord/bot.d.ts.map +1 -1
  7. package/dist/adapters/discord/bot.js +228 -69
  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 +92 -34
  11. package/dist/adapters/discord/context.js.map +1 -1
  12. package/dist/adapters/shared.d.ts +23 -0
  13. package/dist/adapters/shared.d.ts.map +1 -0
  14. package/dist/adapters/shared.js +57 -0
  15. package/dist/adapters/shared.js.map +1 -0
  16. package/dist/adapters/slack/bot.d.ts +19 -11
  17. package/dist/adapters/slack/bot.d.ts.map +1 -1
  18. package/dist/adapters/slack/bot.js +356 -96
  19. package/dist/adapters/slack/bot.js.map +1 -1
  20. package/dist/adapters/slack/branch-manager.d.ts +21 -0
  21. package/dist/adapters/slack/branch-manager.d.ts.map +1 -0
  22. package/dist/adapters/slack/branch-manager.js +96 -0
  23. package/dist/adapters/slack/branch-manager.js.map +1 -0
  24. package/dist/adapters/slack/context.d.ts.map +1 -1
  25. package/dist/adapters/slack/context.js +100 -67
  26. package/dist/adapters/slack/context.js.map +1 -1
  27. package/dist/adapters/slack/session.d.ts +3 -0
  28. package/dist/adapters/slack/session.d.ts.map +1 -0
  29. package/dist/adapters/slack/session.js +16 -0
  30. package/dist/adapters/slack/session.js.map +1 -0
  31. package/dist/adapters/telegram/bot.d.ts +4 -2
  32. package/dist/adapters/telegram/bot.d.ts.map +1 -1
  33. package/dist/adapters/telegram/bot.js +141 -74
  34. package/dist/adapters/telegram/bot.js.map +1 -1
  35. package/dist/adapters/telegram/context.d.ts.map +1 -1
  36. package/dist/adapters/telegram/context.js +49 -109
  37. package/dist/adapters/telegram/context.js.map +1 -1
  38. package/dist/adapters/telegram/html.d.ts +3 -0
  39. package/dist/adapters/telegram/html.d.ts.map +1 -0
  40. package/dist/adapters/telegram/html.js +98 -0
  41. package/dist/adapters/telegram/html.js.map +1 -0
  42. package/dist/agent.d.ts +4 -11
  43. package/dist/agent.d.ts.map +1 -1
  44. package/dist/agent.js +116 -196
  45. package/dist/agent.js.map +1 -1
  46. package/dist/bindings.d.ts +1 -20
  47. package/dist/bindings.d.ts.map +1 -1
  48. package/dist/bindings.js +1 -21
  49. package/dist/bindings.js.map +1 -1
  50. package/dist/config.d.ts +9 -27
  51. package/dist/config.d.ts.map +1 -1
  52. package/dist/config.js +89 -63
  53. package/dist/config.js.map +1 -1
  54. package/dist/context.d.ts +13 -3
  55. package/dist/context.d.ts.map +1 -1
  56. package/dist/context.js +102 -18
  57. package/dist/context.js.map +1 -1
  58. package/dist/events.d.ts +18 -6
  59. package/dist/events.d.ts.map +1 -1
  60. package/dist/events.js +86 -35
  61. package/dist/events.js.map +1 -1
  62. package/dist/execution-resolver.d.ts.map +1 -1
  63. package/dist/execution-resolver.js +1 -3
  64. package/dist/execution-resolver.js.map +1 -1
  65. package/dist/instrument.d.ts.map +1 -1
  66. package/dist/instrument.js +5 -11
  67. package/dist/instrument.js.map +1 -1
  68. package/dist/{login.d.ts → login/index.d.ts} +2 -2
  69. package/dist/login/index.d.ts.map +1 -0
  70. package/dist/{login.js → login/index.js} +2 -2
  71. package/dist/login/index.js.map +1 -0
  72. package/dist/{link-server.d.ts → login/portal.d.ts} +6 -4
  73. package/dist/login/portal.d.ts.map +1 -0
  74. package/dist/login/portal.js +1453 -0
  75. package/dist/login/portal.js.map +1 -0
  76. package/dist/{link-token.d.ts → login/session.d.ts} +1 -1
  77. package/dist/login/session.d.ts.map +1 -0
  78. package/dist/{link-token.js → login/session.js} +1 -1
  79. package/dist/login/session.js.map +1 -0
  80. package/dist/main.d.ts.map +1 -1
  81. package/dist/main.js +175 -119
  82. package/dist/main.js.map +1 -1
  83. package/dist/provisioner.d.ts +17 -43
  84. package/dist/provisioner.d.ts.map +1 -1
  85. package/dist/provisioner.js +84 -50
  86. package/dist/provisioner.js.map +1 -1
  87. package/dist/sandbox/host.d.ts +0 -2
  88. package/dist/sandbox/host.d.ts.map +1 -1
  89. package/dist/sandbox/host.js +1 -5
  90. package/dist/sandbox/host.js.map +1 -1
  91. package/dist/sentry.d.ts.map +1 -1
  92. package/dist/sentry.js +2 -0
  93. package/dist/sentry.js.map +1 -1
  94. package/dist/session-policy.d.ts +13 -0
  95. package/dist/session-policy.d.ts.map +1 -0
  96. package/dist/session-policy.js +23 -0
  97. package/dist/session-policy.js.map +1 -0
  98. package/dist/session-store.d.ts +27 -1
  99. package/dist/session-store.d.ts.map +1 -1
  100. package/dist/session-store.js +162 -9
  101. package/dist/session-store.js.map +1 -1
  102. package/dist/session-view/command.d.ts +5 -0
  103. package/dist/session-view/command.d.ts.map +1 -0
  104. package/dist/session-view/command.js +11 -0
  105. package/dist/session-view/command.js.map +1 -0
  106. package/dist/session-view/portal.d.ts +9 -0
  107. package/dist/session-view/portal.d.ts.map +1 -0
  108. package/dist/session-view/portal.js +766 -0
  109. package/dist/session-view/portal.js.map +1 -0
  110. package/dist/session-view/service.d.ts +34 -0
  111. package/dist/session-view/service.d.ts.map +1 -0
  112. package/dist/session-view/service.js +380 -0
  113. package/dist/session-view/service.js.map +1 -0
  114. package/dist/session-view/store.d.ts +16 -0
  115. package/dist/session-view/store.d.ts.map +1 -0
  116. package/dist/session-view/store.js +38 -0
  117. package/dist/session-view/store.js.map +1 -0
  118. package/dist/store.d.ts +3 -6
  119. package/dist/store.d.ts.map +1 -1
  120. package/dist/store.js +15 -35
  121. package/dist/store.js.map +1 -1
  122. package/dist/tools/event.d.ts +3 -0
  123. package/dist/tools/event.d.ts.map +1 -1
  124. package/dist/tools/event.js +27 -8
  125. package/dist/tools/event.js.map +1 -1
  126. package/dist/tools/index.d.ts +3 -0
  127. package/dist/tools/index.d.ts.map +1 -1
  128. package/dist/tools/index.js +2 -2
  129. package/dist/tools/index.js.map +1 -1
  130. package/dist/ui-copy.d.ts +1 -0
  131. package/dist/ui-copy.d.ts.map +1 -1
  132. package/dist/ui-copy.js +3 -0
  133. package/dist/ui-copy.js.map +1 -1
  134. package/dist/vault-routing.d.ts +1 -2
  135. package/dist/vault-routing.d.ts.map +1 -1
  136. package/dist/vault-routing.js +1 -7
  137. package/dist/vault-routing.js.map +1 -1
  138. package/package.json +1 -1
  139. package/dist/link-server.d.ts.map +0 -1
  140. package/dist/link-server.js +0 -839
  141. package/dist/link-server.js.map +0 -1
  142. package/dist/link-token.d.ts.map +0 -1
  143. package/dist/link-token.js.map +0 -1
  144. package/dist/login.d.ts.map +0 -1
  145. package/dist/login.js.map +0 -1
  146. package/dist/vault.test.d.ts +0 -2
  147. package/dist/vault.test.d.ts.map +0 -1
  148. package/dist/vault.test.js +0 -67
  149. package/dist/vault.test.js.map +0 -1
package/README.md CHANGED
@@ -47,7 +47,9 @@ We actively track the upstream `pi-mom` and plan to:
47
47
  - **Multi-platform** — Slack, Telegram, and Discord adapters out of the box
48
48
  - **Persistent sessions** — session behavior is adapted per platform instead of forcing one thread model everywhere
49
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))
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`
51
53
  - **Persistent memory** — workspace-level and channel-level `MEMORY.md` files
52
54
  - **Skills** — drop custom CLI tools into `skills/` directories
53
55
  - **Event system** — schedule one-shot or recurring tasks via JSON files
@@ -55,11 +57,11 @@ We actively track the upstream `pi-mom` and plan to:
55
57
 
56
58
  ## Platform Session Model
57
59
 
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 |
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 |
63
65
 
64
66
  ## Requirements
65
67
 
@@ -99,7 +101,8 @@ npm run build
99
101
  - `assistant_thread_context_changed`, `assistant_thread_started`
100
102
  - `message.channels`, `message.groups`, `message.im`
101
103
  5. Enable **Interactivity** (Settings → Interactivity & Shortcuts → toggle on).
102
- 6. Copy the **App-Level Token** (`xapp-…`) and **Bot Token** (`xoxb-…`).
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-…`).
103
106
 
104
107
  Or import this **App Manifest** directly (Settings → App Manifest → paste JSON):
105
108
 
@@ -169,14 +172,16 @@ Or import this **App Manifest** directly (Settings → App Manifest → paste JS
169
172
  export MOM_SLACK_APP_TOKEN=xapp-...
170
173
  export MOM_SLACK_BOT_TOKEN=xoxb-...
171
174
 
172
- mama [--sandbox=host|container:<container>] <working-directory>
175
+ mama [--state-dir=~/.mama] [--sandbox=host|container:<container>|image:<image>|firecracker:<vm-id>:<path>] <working-directory>
173
176
  ```
174
177
 
175
178
  The bot responds when `@mentioned` in any channel or via DM.
176
179
 
177
180
  - **Top-level channel messages** — share one persistent channel session.
178
181
  - **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.
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.
180
185
 
181
186
  ---
182
187
 
@@ -184,17 +189,18 @@ The bot responds when `@mentioned` in any channel or via DM.
184
189
 
185
190
  1. Message [@BotFather](https://t.me/BotFather) → `/newbot` to create a bot and get the **Bot Token**.
186
191
  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).
188
192
 
189
193
  ```bash
190
194
  export MOM_TELEGRAM_BOT_TOKEN=123456:ABC-...
191
195
 
192
- mama [--sandbox=host|container:<container>] <working-directory>
196
+ mama [--state-dir=~/.mama] [--sandbox=host|container:<container>|image:<image>|firecracker:<vm-id>:<path>] <working-directory>
193
197
  ```
194
198
 
195
199
  - **Private chats** — every message is forwarded to the bot automatically.
196
200
  - **Group chats** — the bot only responds when `@mentioned` by username.
197
- - **Reply chains** — replying to a previous message continues the same session.
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.
198
204
  - Say `stop` or `/stop` to cancel a running task.
199
205
 
200
206
  ---
@@ -209,33 +215,38 @@ mama [--sandbox=host|container:<container>] <working-directory>
209
215
  ```bash
210
216
  export MOM_DISCORD_BOT_TOKEN=MTI...
211
217
 
212
- mama [--sandbox=host|container:<container>] <working-directory>
218
+ mama [--state-dir=~/.mama] [--sandbox=host|container:<container>|image:<image>|firecracker:<vm-id>:<path>] <working-directory>
213
219
  ```
214
220
 
215
221
  - **Server channels** — the bot responds when `@mentioned`.
216
222
  - **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.
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.
219
226
  - Say `stop` or `/stop` to cancel a running task.
220
227
 
221
228
  ---
222
229
 
223
230
  ## Options
224
231
 
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) |
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) |
233
240
 
234
- ### Container Mode Semantics
241
+ ### Sandbox and Vault Semantics
235
242
 
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.
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.
239
250
 
240
251
  ### Download channel history (Slack)
241
252
 
@@ -243,14 +254,51 @@ mama [--sandbox=host|container:<container>] <working-directory>
243
254
  mama --download C0123456789
244
255
  ```
245
256
 
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:
260
+
261
+ ```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
265
+ ```
266
+
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.
275
+
276
+ ## Web Session Viewer
277
+
278
+ The same web portal used for `/login` can also render a read-only view of the current session.
279
+
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.
284
+
285
+ This feature uses the same `MOM_LINK_URL` / `MOM_LINK_PORT` configuration as `/login`.
286
+
287
+ Built-in OAuth guides:
288
+
289
+ - [GitHub OAuth](docs/oauth/github.md)
290
+ - [Google Workspace CLI OAuth](docs/oauth/google-workspace.md)
291
+
292
+ Credentials are stored under `<state-dir>/vaults` (default `~/.mama/vaults`). Runtime env injection only happens in `container`, `image`, and `firecracker` modes.
293
+
246
294
  ## Configuration
247
295
 
248
- mama stores operator-managed configuration in `~/.mama` by default. Use `--state-dir <path>` to choose another location. Create or edit `settings.json` there:
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:
249
297
 
250
298
  ```json
251
299
  {
252
300
  "provider": "anthropic",
253
- "model": "claude-sonnet-4-6",
301
+ "model": "claude-sonnet-4-5",
254
302
  "thinkingLevel": "off",
255
303
  "sessionScope": "thread",
256
304
  "logFormat": "console",
@@ -259,15 +307,15 @@ mama stores operator-managed configuration in `~/.mama` by default. Use `--state
259
307
  }
260
308
  ```
261
309
 
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`) |
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`) |
271
319
 
272
320
  When `sentryDsn` is set, mama sends Sentry events with sensitive prompt/tool content redacted before upload.
273
321
 
@@ -284,7 +332,7 @@ Set `logFormat: "json"` to send structured logs directly to Cloud Logging via AP
284
332
  GOOGLE_CLOUD_PROJECT=<your-project-id> mama <working-directory>
285
333
  ```
286
334
 
287
- `settings.json`:
335
+ In `<state-dir>/settings.json` (or `<working-directory>/settings.json` as a fallback):
288
336
 
289
337
  ```json
290
338
  {
@@ -295,72 +343,79 @@ GOOGLE_CLOUD_PROJECT=<your-project-id> mama <working-directory>
295
343
 
296
344
  Logs appear in Cloud Logging under **Log name: `mama`**. Console output (stdout) is unaffected and continues to work alongside Cloud Logging.
297
345
 
346
+ ## State Directory Layout
347
+
348
+ ```
349
+ <state-dir>/
350
+ ├── settings.json # Preferred provider/model/logging/Sentry config
351
+ └── vaults/
352
+ ├── bindings.json # Platform user -> vault mapping
353
+ ├── vault.json # Vault metadata
354
+ └── <vault-id>/
355
+ ├── env # Injected env vars
356
+ └── ... # Credential files (e.g. gws.json, .ssh/)
357
+ ```
358
+
298
359
  ## Working Directory Layout
299
360
 
300
361
  ```
301
362
  <working-directory>/
363
+ ├── settings.json # Optional fallback config if <state-dir>/settings.json is absent
302
364
  ├── MEMORY.md # Global memory (all channels)
303
365
  ├── SYSTEM.md # Installed packages / env changes log
304
366
  ├── skills/ # Global skills (CLI tools)
305
367
  ├── events/ # Scheduled event files
306
- └── <channel-id>/
307
- ├── MEMORY.md # Channel-specific memory
368
+ └── <conversation-id>/
369
+ ├── MEMORY.md # Conversation-specific memory
308
370
  ├── log.jsonl # Full message history
309
371
  ├── attachments/ # Downloaded user files
310
372
  ├── scratch/ # Agent working directory
311
- ├── skills/ # Channel-specific skills
373
+ ├── skills/ # Conversation-specific skills
312
374
  └── sessions/
313
- ├── current # Pointer for the current channel session file
375
+ ├── current # Pointer for the persistent top-level / direct session
314
376
  ├── 2026-04-05T18-04-31-010Z_1d92b3ad.jsonl
315
- └── <thread-ts>.jsonl # Fixed-path thread session file
316
- ```
317
-
318
- Operator-managed state lives outside the workspace:
319
-
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
377
+ └── <session-suffix>.jsonl # Fixed-path scoped session (thread / reply root)
326
378
  ```
327
379
 
328
380
  ## Container Sandbox
329
381
 
330
382
  ```bash
331
383
  # Create a container (mount your working directory to /workspace)
332
- docker run -d --name mama-sandbox \
384
+ docker run -d --name mama-tools \
333
385
  -v /path/to/workspace:/workspace \
334
386
  alpine:latest sleep infinity
335
387
 
336
388
  # Start mama with container sandbox
337
- mama --sandbox=container:mama-sandbox /path/to/workspace
389
+ mama --sandbox=container:mama-tools /path/to/workspace
338
390
  ```
339
391
 
392
+ `container:mama-tools` uses vault key `container-mama-tools`. If multiple users share the same container, they share that container vault.
393
+
340
394
  ## Managed Per-User Container Sandbox
341
395
 
342
396
  ```bash
343
- # Build the bundled sandbox image once
344
- docker build -f docker/mama-sandbox.Dockerfile -t mama-sandbox:tools .
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
345
401
 
346
- # Then use it for per-user managed containers
347
- mama --sandbox=image:mama-sandbox:tools /path/to/workspace
402
+ # Start mama with managed image sandboxes
403
+ mama --sandbox=image:ghcr.io/geminixiang/mama-sandbox:tools /path/to/workspace
348
404
  ```
349
405
 
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).
406
+ Or build the bundled image locally:
351
407
 
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`
408
+ ```bash
409
+ docker build -f docker/mama-sandbox.Dockerfile -t mama-sandbox:tools .
410
+ mama --sandbox=image:mama-sandbox:tools /path/to/workspace
411
+ ```
359
412
 
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.
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.
361
414
 
362
415
  ## Firecracker Sandbox
363
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
+
364
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.
365
420
 
366
421
  ### Requirements
@@ -432,13 +487,13 @@ Drop JSON files into `<working-directory>/events/` to trigger the agent:
432
487
 
433
488
  ```json
434
489
  // Immediate — triggers as soon as mama sees the file
435
- {"type": "immediate", "channelId": "C0123456789", "text": "New deployment finished"}
490
+ {"type": "immediate", "platform": "slack", "conversationId": "C0123456789", "conversationKind": "shared", "text": "New deployment finished"}
436
491
 
437
492
  // 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"}
493
+ {"type": "one-shot", "platform": "telegram", "conversationId": "574247312", "conversationKind": "direct", "text": "Daily standup reminder", "at": "2025-12-15T09:00:00+08:00"}
439
494
 
440
495
  // Periodic — triggers on a cron schedule
441
- {"type": "periodic", "channelId": "C0123456789", "text": "Check inbox", "schedule": "0 9 * * 1-5", "timezone": "Asia/Taipei"}
496
+ {"type": "periodic", "platform": "discord", "conversationId": "1498975469343739948", "conversationKind": "shared", "text": "Check inbox", "schedule": "0 9 * * 1-5", "timezone": "Asia/Taipei"}
442
497
  ```
443
498
 
444
499
  ## Skills
@@ -473,12 +528,12 @@ npm run build # production build
473
528
 
474
529
  ## 📦 Dependencies & Versions
475
530
 
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) |
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 |
482
537
 
483
538
  ## License
484
539
 
package/dist/adapter.d.ts CHANGED
@@ -1,6 +1,8 @@
1
+ export type ConversationKind = "direct" | "shared";
1
2
  export interface ChatMessage {
2
3
  id: string;
3
4
  sessionKey: string;
5
+ conversationKind: ConversationKind;
4
6
  userId: string;
5
7
  userName?: string;
6
8
  text: string;
@@ -10,12 +12,21 @@ export interface ChatMessage {
10
12
  }[];
11
13
  threadTs?: string;
12
14
  }
15
+ export interface ChatToolResult {
16
+ toolName: string;
17
+ label?: string;
18
+ args?: Record<string, unknown>;
19
+ result: string;
20
+ isError: boolean;
21
+ durationMs: number;
22
+ }
13
23
  export interface ChatResponseContext {
14
24
  respond(text: string): Promise<void>;
15
25
  replaceResponse(text: string): Promise<void>;
16
- respondInThread(text: string, options?: {
17
- style?: "muted";
26
+ respondDiagnostic(text: string, options?: {
27
+ style?: "muted" | "error";
18
28
  }): Promise<void>;
29
+ respondToolResult(result: ChatToolResult): Promise<void>;
19
30
  setTyping(isTyping: boolean): Promise<void>;
20
31
  setWorking(working: boolean): Promise<void>;
21
32
  uploadFile(filePath: string, title?: string): Promise<void>;
@@ -33,6 +44,9 @@ export interface PlatformInfo {
33
44
  userName: string;
34
45
  displayName: string;
35
46
  }[];
47
+ diagnostics?: {
48
+ showUsageSummary?: boolean;
49
+ };
36
50
  }
37
51
  export interface ChatAdapter {
38
52
  start(): Promise<void>;
@@ -44,8 +58,10 @@ export interface ChatAdapter {
44
58
  */
45
59
  export interface BotEvent {
46
60
  type: string;
47
- /** Internal conversation identifier used by the shared runtime */
61
+ /** Platform-specific raw conversation/channel/chat identifier */
48
62
  conversationId: string;
63
+ /** Cross-platform conversation shape: direct message vs shared space */
64
+ conversationKind: ConversationKind;
49
65
  /** Message timestamp or ID as string */
50
66
  ts: string;
51
67
  /** Parent message ID for threaded replies (optional) */
@@ -59,7 +75,7 @@ export interface BotEvent {
59
75
  name: string;
60
76
  localPath: string;
61
77
  }[];
62
- /** Platform-computed session key; overrides default conversation:thread_ts computation */
78
+ /** Platform-computed session key; overrides default conversationId:thread_ts computation */
63
79
  sessionKey?: string;
64
80
  }
65
81
  /**
@@ -68,8 +84,8 @@ export interface BotEvent {
68
84
  */
69
85
  export interface Bot {
70
86
  start(): Promise<void>;
71
- postMessage(conversationId: string, text: string): Promise<string>;
72
- updateMessage(conversationId: string, ts: string, text: string): Promise<void>;
87
+ postMessage(channel: string, text: string): Promise<string>;
88
+ updateMessage(channel: string, ts: string, text: string): Promise<void>;
73
89
  enqueueEvent(event: BotEvent): boolean;
74
90
  getPlatformInfo(): PlatformInfo;
75
91
  }
@@ -100,9 +116,5 @@ export interface BotHandler {
100
116
  forceStop(sessionKey: string): void;
101
117
  /** Reset a session: abort if running, delete history, remove from cache */
102
118
  handleNew(sessionKey: string, conversationId: string, bot: Bot): Promise<void>;
103
- /** Handle credential onboarding for a user login command. */
104
- handleLogin(platform: string, platformUserId: string, conversationId: string, bot: Bot, commandText: string, isPrivateConversation: boolean): Promise<void>;
105
119
  }
106
- /** @deprecated Use BotHandler */
107
- export type MomHandler = BotHandler;
108
120
  //# sourceMappingURL=adapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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","sourcesContent":["export interface ChatMessage {\n id: string;\n sessionKey: string;\n userId: string;\n userName?: string;\n text: string;\n attachments?: { name: string; localPath: string }[];\n threadTs?: string;\n}\n\nexport interface ChatResponseContext {\n respond(text: string): Promise<void>;\n replaceResponse(text: string): Promise<void>;\n respondInThread(text: string, options?: { style?: \"muted\" }): Promise<void>;\n setTyping(isTyping: boolean): Promise<void>;\n setWorking(working: boolean): Promise<void>;\n uploadFile(filePath: string, title?: string): Promise<void>;\n deleteResponse(): Promise<void>;\n}\n\nexport interface PlatformInfo {\n name: string;\n formattingGuide: string;\n channels: { id: string; name: string }[];\n users: { id: string; userName: string; displayName: string }[];\n}\n\nexport interface ChatAdapter {\n start(): Promise<void>;\n stop(): Promise<void>;\n getPlatformInfo(): PlatformInfo;\n}\n\n// ============================================================================\n// Generic cross-platform event and bot interfaces\n// ============================================================================\n\n/**\n * A platform-agnostic event (message/mention) that triggers the agent.\n */\nexport interface BotEvent {\n type: string;\n /** Internal conversation identifier used by the shared runtime */\n conversationId: string;\n /** Message timestamp or ID as string */\n ts: string;\n /** Parent message ID for threaded replies (optional) */\n thread_ts?: string;\n /** User ID */\n user: string;\n /** Message text (already stripped of bot mentions) */\n text: string;\n /** Downloaded attachments */\n attachments?: { name: string; localPath: string }[];\n /** Platform-computed session key; overrides default conversation:thread_ts computation */\n sessionKey?: string;\n}\n\n/**\n * Minimum interface that every platform bot must implement,\n * used by the central handler in main.ts and by EventsWatcher.\n */\nexport interface Bot {\n start(): Promise<void>;\n postMessage(conversationId: string, text: string): Promise<string>;\n updateMessage(conversationId: string, ts: string, text: string): Promise<void>;\n enqueueEvent(event: BotEvent): boolean;\n getPlatformInfo(): PlatformInfo;\n}\n\n/** Pre-created platform adapters passed to the handler */\nexport interface BotAdapters {\n message: ChatMessage;\n responseCtx: ChatResponseContext;\n platform: PlatformInfo;\n}\n\n/**\n * Handler callbacks invoked by each platform bot.\n * Each bot creates platform-specific adapters before calling handleEvent.\n */\nexport interface RunningSession {\n sessionKey: string;\n startedAt: number; // Date.now() when run started\n /** Last activity timestamp (for detecting hung tasks) */\n lastActivityAt?: number;\n /** Current tool/step being executed (if any) */\n currentTool?: string;\n}\n\nexport interface BotHandler {\n isRunning(sessionKey: string): boolean;\n getRunningSessions(): RunningSession[];\n handleEvent(event: BotEvent, bot: Bot, adapters: BotAdapters, isEvent?: boolean): Promise<void>;\n handleStop(sessionKey: string, conversationId: string, bot: Bot): Promise<void>;\n /** Force stop a running session (bypass normal stop mechanism) */\n forceStop(sessionKey: string): void;\n /** Reset a session: abort if running, delete history, remove from cache */\n handleNew(sessionKey: string, conversationId: string, bot: Bot): Promise<void>;\n /** Handle credential onboarding for a user login command. */\n handleLogin(\n platform: string,\n platformUserId: string,\n conversationId: string,\n bot: Bot,\n commandText: string,\n isPrivateConversation: boolean,\n ): Promise<void>;\n}\n\n/** @deprecated Use BotHandler */\nexport type MomHandler = BotHandler;\n"]}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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","sourcesContent":["export type ConversationKind = \"direct\" | \"shared\";\n\nexport interface ChatMessage {\n id: string;\n sessionKey: string;\n conversationKind: ConversationKind;\n userId: string;\n userName?: string;\n text: string;\n attachments?: { name: string; localPath: string }[];\n threadTs?: string;\n}\n\nexport interface ChatToolResult {\n toolName: string;\n label?: string;\n args?: Record<string, unknown>;\n result: string;\n isError: boolean;\n durationMs: number;\n}\n\nexport interface ChatResponseContext {\n respond(text: string): Promise<void>;\n replaceResponse(text: string): Promise<void>;\n respondDiagnostic(text: string, options?: { style?: \"muted\" | \"error\" }): Promise<void>;\n respondToolResult(result: ChatToolResult): Promise<void>;\n setTyping(isTyping: boolean): Promise<void>;\n setWorking(working: boolean): Promise<void>;\n uploadFile(filePath: string, title?: string): Promise<void>;\n deleteResponse(): Promise<void>;\n}\n\nexport interface PlatformInfo {\n name: string;\n formattingGuide: string;\n channels: { id: string; name: string }[];\n users: { id: string; userName: string; displayName: string }[];\n diagnostics?: {\n showUsageSummary?: boolean;\n };\n}\n\nexport interface ChatAdapter {\n start(): Promise<void>;\n stop(): Promise<void>;\n getPlatformInfo(): PlatformInfo;\n}\n\n// ============================================================================\n// Generic cross-platform event and bot interfaces\n// ============================================================================\n\n/**\n * A platform-agnostic event (message/mention) that triggers the agent.\n */\nexport interface BotEvent {\n type: string;\n /** Platform-specific raw conversation/channel/chat identifier */\n conversationId: string;\n /** Cross-platform conversation shape: direct message vs shared space */\n conversationKind: ConversationKind;\n /** Message timestamp or ID as string */\n ts: string;\n /** Parent message ID for threaded replies (optional) */\n thread_ts?: string;\n /** User ID */\n user: string;\n /** Message text (already stripped of bot mentions) */\n text: string;\n /** Downloaded attachments */\n attachments?: { name: string; localPath: string }[];\n /** Platform-computed session key; overrides default conversationId:thread_ts computation */\n sessionKey?: string;\n}\n\n/**\n * Minimum interface that every platform bot must implement,\n * used by the central handler in main.ts and by EventsWatcher.\n */\nexport interface Bot {\n start(): Promise<void>;\n postMessage(channel: string, text: string): Promise<string>;\n updateMessage(channel: string, ts: string, text: string): Promise<void>;\n enqueueEvent(event: BotEvent): boolean;\n getPlatformInfo(): PlatformInfo;\n}\n\n/** Pre-created platform adapters passed to the handler */\nexport interface BotAdapters {\n message: ChatMessage;\n responseCtx: ChatResponseContext;\n platform: PlatformInfo;\n}\n\n/**\n * Handler callbacks invoked by each platform bot.\n * Each bot creates platform-specific adapters before calling handleEvent.\n */\nexport interface RunningSession {\n sessionKey: string;\n startedAt: number; // Date.now() when run started\n /** Last activity timestamp (for detecting hung tasks) */\n lastActivityAt?: number;\n /** Current tool/step being executed (if any) */\n currentTool?: string;\n}\n\nexport interface BotHandler {\n isRunning(sessionKey: string): boolean;\n getRunningSessions(): RunningSession[];\n handleEvent(event: BotEvent, bot: Bot, adapters: BotAdapters, isEvent?: boolean): Promise<void>;\n handleStop(sessionKey: string, conversationId: string, bot: Bot): Promise<void>;\n /** Force stop a running session (bypass normal stop mechanism) */\n forceStop(sessionKey: string): void;\n /** Reset a session: abort if running, delete history, remove from cache */\n handleNew(sessionKey: string, conversationId: string, bot: Bot): Promise<void>;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"","sourcesContent":["export interface ChatMessage {\n id: string;\n sessionKey: string;\n userId: string;\n userName?: string;\n text: string;\n attachments?: { name: string; localPath: string }[];\n threadTs?: string;\n}\n\nexport interface ChatResponseContext {\n respond(text: string): Promise<void>;\n replaceResponse(text: string): Promise<void>;\n respondInThread(text: string, options?: { style?: \"muted\" }): Promise<void>;\n setTyping(isTyping: boolean): Promise<void>;\n setWorking(working: boolean): Promise<void>;\n uploadFile(filePath: string, title?: string): Promise<void>;\n deleteResponse(): Promise<void>;\n}\n\nexport interface PlatformInfo {\n name: string;\n formattingGuide: string;\n channels: { id: string; name: string }[];\n users: { id: string; userName: string; displayName: string }[];\n}\n\nexport interface ChatAdapter {\n start(): Promise<void>;\n stop(): Promise<void>;\n getPlatformInfo(): PlatformInfo;\n}\n\n// ============================================================================\n// Generic cross-platform event and bot interfaces\n// ============================================================================\n\n/**\n * A platform-agnostic event (message/mention) that triggers the agent.\n */\nexport interface BotEvent {\n type: string;\n /** Internal conversation identifier used by the shared runtime */\n conversationId: string;\n /** Message timestamp or ID as string */\n ts: string;\n /** Parent message ID for threaded replies (optional) */\n thread_ts?: string;\n /** User ID */\n user: string;\n /** Message text (already stripped of bot mentions) */\n text: string;\n /** Downloaded attachments */\n attachments?: { name: string; localPath: string }[];\n /** Platform-computed session key; overrides default conversation:thread_ts computation */\n sessionKey?: string;\n}\n\n/**\n * Minimum interface that every platform bot must implement,\n * used by the central handler in main.ts and by EventsWatcher.\n */\nexport interface Bot {\n start(): Promise<void>;\n postMessage(conversationId: string, text: string): Promise<string>;\n updateMessage(conversationId: string, ts: string, text: string): Promise<void>;\n enqueueEvent(event: BotEvent): boolean;\n getPlatformInfo(): PlatformInfo;\n}\n\n/** Pre-created platform adapters passed to the handler */\nexport interface BotAdapters {\n message: ChatMessage;\n responseCtx: ChatResponseContext;\n platform: PlatformInfo;\n}\n\n/**\n * Handler callbacks invoked by each platform bot.\n * Each bot creates platform-specific adapters before calling handleEvent.\n */\nexport interface RunningSession {\n sessionKey: string;\n startedAt: number; // Date.now() when run started\n /** Last activity timestamp (for detecting hung tasks) */\n lastActivityAt?: number;\n /** Current tool/step being executed (if any) */\n currentTool?: string;\n}\n\nexport interface BotHandler {\n isRunning(sessionKey: string): boolean;\n getRunningSessions(): RunningSession[];\n handleEvent(event: BotEvent, bot: Bot, adapters: BotAdapters, isEvent?: boolean): Promise<void>;\n handleStop(sessionKey: string, conversationId: string, bot: Bot): Promise<void>;\n /** Force stop a running session (bypass normal stop mechanism) */\n forceStop(sessionKey: string): void;\n /** Reset a session: abort if running, delete history, remove from cache */\n handleNew(sessionKey: string, conversationId: string, bot: Bot): Promise<void>;\n /** Handle credential onboarding for a user login command. */\n handleLogin(\n platform: string,\n platformUserId: string,\n conversationId: string,\n bot: Bot,\n commandText: string,\n isPrivateConversation: boolean,\n ): Promise<void>;\n}\n\n/** @deprecated Use BotHandler */\nexport type MomHandler = BotHandler;\n"]}
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"","sourcesContent":["export type ConversationKind = \"direct\" | \"shared\";\n\nexport interface ChatMessage {\n id: string;\n sessionKey: string;\n conversationKind: ConversationKind;\n userId: string;\n userName?: string;\n text: string;\n attachments?: { name: string; localPath: string }[];\n threadTs?: string;\n}\n\nexport interface ChatToolResult {\n toolName: string;\n label?: string;\n args?: Record<string, unknown>;\n result: string;\n isError: boolean;\n durationMs: number;\n}\n\nexport interface ChatResponseContext {\n respond(text: string): Promise<void>;\n replaceResponse(text: string): Promise<void>;\n respondDiagnostic(text: string, options?: { style?: \"muted\" | \"error\" }): Promise<void>;\n respondToolResult(result: ChatToolResult): Promise<void>;\n setTyping(isTyping: boolean): Promise<void>;\n setWorking(working: boolean): Promise<void>;\n uploadFile(filePath: string, title?: string): Promise<void>;\n deleteResponse(): Promise<void>;\n}\n\nexport interface PlatformInfo {\n name: string;\n formattingGuide: string;\n channels: { id: string; name: string }[];\n users: { id: string; userName: string; displayName: string }[];\n diagnostics?: {\n showUsageSummary?: boolean;\n };\n}\n\nexport interface ChatAdapter {\n start(): Promise<void>;\n stop(): Promise<void>;\n getPlatformInfo(): PlatformInfo;\n}\n\n// ============================================================================\n// Generic cross-platform event and bot interfaces\n// ============================================================================\n\n/**\n * A platform-agnostic event (message/mention) that triggers the agent.\n */\nexport interface BotEvent {\n type: string;\n /** Platform-specific raw conversation/channel/chat identifier */\n conversationId: string;\n /** Cross-platform conversation shape: direct message vs shared space */\n conversationKind: ConversationKind;\n /** Message timestamp or ID as string */\n ts: string;\n /** Parent message ID for threaded replies (optional) */\n thread_ts?: string;\n /** User ID */\n user: string;\n /** Message text (already stripped of bot mentions) */\n text: string;\n /** Downloaded attachments */\n attachments?: { name: string; localPath: string }[];\n /** Platform-computed session key; overrides default conversationId:thread_ts computation */\n sessionKey?: string;\n}\n\n/**\n * Minimum interface that every platform bot must implement,\n * used by the central handler in main.ts and by EventsWatcher.\n */\nexport interface Bot {\n start(): Promise<void>;\n postMessage(channel: string, text: string): Promise<string>;\n updateMessage(channel: string, ts: string, text: string): Promise<void>;\n enqueueEvent(event: BotEvent): boolean;\n getPlatformInfo(): PlatformInfo;\n}\n\n/** Pre-created platform adapters passed to the handler */\nexport interface BotAdapters {\n message: ChatMessage;\n responseCtx: ChatResponseContext;\n platform: PlatformInfo;\n}\n\n/**\n * Handler callbacks invoked by each platform bot.\n * Each bot creates platform-specific adapters before calling handleEvent.\n */\nexport interface RunningSession {\n sessionKey: string;\n startedAt: number; // Date.now() when run started\n /** Last activity timestamp (for detecting hung tasks) */\n lastActivityAt?: number;\n /** Current tool/step being executed (if any) */\n currentTool?: string;\n}\n\nexport interface BotHandler {\n isRunning(sessionKey: string): boolean;\n getRunningSessions(): RunningSession[];\n handleEvent(event: BotEvent, bot: Bot, adapters: BotAdapters, isEvent?: boolean): Promise<void>;\n handleStop(sessionKey: string, conversationId: string, bot: Bot): Promise<void>;\n /** Force stop a running session (bypass normal stop mechanism) */\n forceStop(sessionKey: string): void;\n /** Reset a session: abort if running, delete history, remove from cache */\n handleNew(sessionKey: string, conversationId: string, bot: Bot): Promise<void>;\n}\n"]}
@@ -18,8 +18,8 @@ export declare class DiscordBot implements Bot {
18
18
  workingDir: string;
19
19
  });
20
20
  start(): Promise<void>;
21
- postMessage(conversationId: string, text: string): Promise<string>;
22
- updateMessage(conversationId: string, ts: string, text: string): Promise<void>;
21
+ postMessage(channel: string, text: string): Promise<string>;
22
+ updateMessage(channel: string, ts: string, text: string): Promise<void>;
23
23
  enqueueEvent(event: BotEvent): boolean;
24
24
  getPlatformInfo(): PlatformInfo;
25
25
  updateMessageRaw(channelId: string, messageId: string, text: string): Promise<void>;
@@ -28,6 +28,7 @@ export declare class DiscordBot implements Bot {
28
28
  deleteMessageRaw(channelId: string, messageId: string): Promise<void>;
29
29
  sendTyping(channelId: string): Promise<void>;
30
30
  uploadFile(channelId: string, filePath: string, title?: string): Promise<void>;
31
+ sendDirectMessage(userId: string, text: string): Promise<string>;
31
32
  getAllChannels(): {
32
33
  id: string;
33
34
  name: string;
@@ -40,18 +41,20 @@ export declare class DiscordBot implements Bot {
40
41
  logToFile(channelId: string, entry: object): void;
41
42
  logBotResponse(channelId: string, text: string, ts: string): void;
42
43
  /**
43
- * Process attachments from a Discord message
44
- * Downloads files in background and returns metadata
45
- * Returns format compatible with ChatMessage: { name: string, localPath: string }[]
44
+ * Process attachments from a Discord message.
45
+ * Downloads files before returning so the agent can read them immediately.
46
46
  */
47
- processAttachments(channelId: string, attachments: Collection<string, Attachment>, _messageId: string): {
47
+ processAttachments(channelId: string, attachments: Collection<string, Attachment>, _messageId: string): Promise<{
48
48
  name: string;
49
49
  localPath: string;
50
- }[];
50
+ }[]>;
51
51
  private downloadAttachment;
52
52
  private getQueue;
53
+ private resolveStopTarget;
53
54
  private loadCachedGuildData;
54
55
  private stripBotMention;
56
+ private resolveConversationContext;
57
+ private createSessionSlashAdapters;
55
58
  private setupEventHandlers;
56
59
  private fetchTextChannel;
57
60
  }