···79798080No new channel-specific config is needed in V1.
81818282+Required platform permissions:
8383+8484+- Slack: add bot scope `files:read` when `slack` is enabled in `multimodal.image.sources`; Slack file URLs such as `url_private` and `url_private_download` require a bearer token with that scope. Keep existing message scopes and Socket Mode `connections:write`.
8585+- Lark/Feishu: grant the message resource permission used by the `/im/v1/messages/:message_id/resources/:file_key` API. In current consoles this is usually found by searching for `im:resource` or the label for getting/uploading image or file resources. Keep message send/reply permissions and `im.message.receive_v1` event subscription.
8686+- After changing permissions, publish/reinstall the app so the runtime token receives the new grants.
8787+8288## 5) Shared Data Model
83898490The smallest useful shape is still `[]string` of local image paths.
···154160- Enforce max image bytes before or during download.
155161- Only accept image MIME types.
156162- Use secure child directory creation, matching existing cache rules.
163163+- Document that Slack image download needs bot scope `files:read`.
157164158165### 7.3 Runtime Wiring
159166···223230- Enforce max image bytes.
224231- Only accept image MIME types.
225232- Keep the API method local to Lark runtime; do not create a broad Lark SDK.
233233+- Document that Lark image download needs the app permission for message resources, commonly surfaced as `im:resource`.
226234227235### 8.3 Runtime Wiring
228236···351359 Download only supported image MIME types into `file_cache_dir/lark/`, enforce size limits, and pass local paths into the current LLM message.
3523603533616. Update config support lists.
354354- Add `lark` where the UI or config template lists supported image sources.
362362+ Add `lark` where the UI or config template lists supported image sources, and document Slack/Lark platform permissions.
3553633563647. Add focused tests.
357365 Cover parsing, config flags, download validation, bus image path preservation, prompt image parts, disabled image fallback, and text-only model fallback.
+30-9
docs/lark.md
···11-# Lark (Feishu) Support Plan
11+# Lark (Feishu) Runtime
2233-This document defines the implemented `mistermorph lark` runtime shape for V1.
33+This document defines the implemented `mistermorph lark` runtime shape.
4455Status on 2026-03-06:
66- implemented for `private + group` text messaging
77- webhook ingress, token exchange, bus runtime, delivery adapter, contacts integration, and manual sender routing are in place
88-- V1 intentionally excludes cards, files, images, reactions, and extra identity namespaces
88+99+Status on 2026-04-30:
1010+- inbound image messages can be downloaded and sent to image-capable models when `lark` is enabled in `multimodal.image.sources`
1111+- V1 still excludes cards, generic file browsing, outbound rich media, reactions, and extra identity namespaces
9121013## 1. Verified Platform Facts
1114···1518- `tenant_access_token` is valid for 2 hours
1619- bot apps can subscribe to the message receive event and get messages from both private and group chats
1720- outbound delivery has both a general send API and a dedicated reply API
2121+- message resources, including images, are fetched through the message resource API with the app tenant token
1822- the same API family is exposed under both:
1923 - `open.feishu.cn`
2024 - `open.larksuite.com`
···3640## 2. Scope
37413842- Add `mistermorph lark` as a long-running webhook runtime.
3939-- Support `private + group` text conversations in V1.
4343+- Support `private + group` text conversations.
4444+- Support inbound image understanding for the current user message when `lark` is listed in `multimodal.image.sources`.
4045- Reuse the existing channel pipeline:
4146 - inbound event -> bus -> per-conversation worker -> `run*Task` -> outbound bus -> delivery adapter
4247- Reuse shared group-trigger logic: `strict | smart | talkative`.
···45504651## 3. Non-Goals (V1)
47524848-- No cards, rich post bodies, files, or image multimodal input in the first pass.
5353+- No cards, rich post bodies, generic file browsing, video, audio, or outbound rich media.
4954- No `message_react` parity in V1.
5055- No app-store or multi-tenant install flow in V1.
5156- No separate execution architecture just for Lark.
···114119- Start with `msg_type=text` only.
115120- Keep reply/send selection in the delivery adapter, not in agent logic.
116121117117-## 8. Proposed CLI and Config Surface
122122+## 8. CLI and Config Surface
118123119119-Planned command:
124124+Run command:
120125121126```bash
122127go run ./cmd/mistermorph lark \
···126131 --lark-webhook-path /lark/webhook
127132```
128133129129-Planned config:
134134+Config:
130135131136```yaml
132137lark:
···144149 addressing_interject_threshold: 0.6
145150 task_timeout: "0s"
146151 max_concurrency: 3
152152+153153+multimodal:
154154+ image:
155155+ sources: ["telegram", "line", "lark"]
147156```
148157149158Field notes:
···155164- typical deployment shape:
156165 - Feishu app -> one `mistermorph lark` instance with Feishu `base_url`
157166 - Lark app -> another `mistermorph lark` instance with Lark `base_url`
167167+- when `lark` is listed in `multimodal.image.sources`, inbound image messages are downloaded under `file_cache_dir/lark/` and passed to image-capable models as image parts
168168+- the current runtime accepts PNG, JPEG, and WebP images, keeps at most 3 images per message, and rejects images larger than 5 MiB each
158169159170Environment note:
160171···169180- a self-built app with bot capability enabled
170181- event subscription enabled for message receive events
171182- message send and reply permissions granted
183183+- message resource download permissions granted when image input is enabled
172184- the app has been added to the target group chats or users can reach it in private chat
173185174174-We should not hard-code permission names into runtime logic. Validate them through startup checks and actionable logs.
186186+Set this up in the Feishu/Lark developer console:
187187+188188+- Enable bot capability.
189189+- Subscribe to event `im.message.receive_v1`.
190190+- Grant message send/reply permission. In current Feishu/Lark consoles this is usually `im:message` or the equivalent send-as-bot message permission.
191191+- Grant receive-message permissions for the conversations you expect to handle. For group messages, use the console permission corresponding to `im:message.group_msg:readonly`; for private messages, use the corresponding P2P message permission when your tenant requires it.
192192+- Grant message resource permission for images. Search for `im:resource` or the current console label for "get/upload image or file resources".
193193+- Publish a new app version after changing permissions, then reinstall or refresh the app in the tenant.
194194+195195+If image download fails with a "lack of permissions" style error, check `im:resource`, group/P2P message read permission, app publication status, and whether the bot is in the chat.
175196176197## 10. Implementation Plan
177198
+19
docs/slack.md
···6969- `im:history`
7070- `mpim:history`
7171- `chat:write`
7272+- `files:read` (required for image attachments when `slack` is enabled in `multimodal.image.sources`)
7273- `users:read`
73747475Optional `Bot Token Scopes`:
···80818182- `connections:write` (on `xapp-...`)
82838484+Event subscriptions for Socket Mode:
8585+8686+- `app_mention`
8787+- `message.channels`
8888+- `message.groups`
8989+- `message.im`
9090+- `message.mpim`
9191+9292+Image attachments arrive through normal message events. The runtime reads Slack file objects from those events and downloads `url_private_download` or `url_private` with the bot token. Slack requires the token used for those URLs to have `files:read`.
9393+8394After adding or changing any scope:
849585961. Click `Reinstall to Workspace`.
···113124 addressing_interject_threshold: 0.6
114125 task_timeout: "0s"
115126 max_concurrency: 3
127127+128128+multimodal:
129129+ image:
130130+ sources: ["telegram", "line", "slack"]
116131```
132132+133133+When `slack` is listed in `multimodal.image.sources`, inbound Slack images are downloaded under `file_cache_dir/slack/` and passed to image-capable models as image parts. The current runtime accepts PNG, JPEG, and WebP images, keeps at most 3 images per message, and rejects images larger than 5 MiB each. If `slack` is not listed, image-only messages get a text fallback asking the user to describe the image.
117134118135## 7. Run Example
119136···131148 - `xoxb` is invalid/expired/mis-copied, or installed in the wrong workspace.
132149- `slack users.info failed: missing_scope`
133150 - Bot token is missing `users:read`, or scope changed without reinstall/token refresh.
151151+- `slack image download http 403` or image-only messages cannot be read
152152+ - Bot token is missing `files:read`, the app was not reinstalled after adding it, or the bot is not in the conversation where the file was shared.
134153- `slack_emoji_catalog_load_failed ... slack emoji.list failed: missing_scope`
135154 - Bot token is missing `emoji:read`; `message_react` will not be registered until emoji catalog can be loaded.
136155- `slack apps.connections.open failed: not_allowed_token_type`