# DingTalk EBA Adapter Migration Record

Status: migrated with partial plugin E2E evidence.

Adapter directory: `src/langbot/pkg/platform/adapters/dingtalk/`

## What Changed

The DingTalk adapter now has an Event-Based Agents adapter package with:

- `manifest.yaml` for adapter metadata, configuration, events, common APIs, and platform-specific APIs.
- `adapter.py` for DingTalk client startup, native callback handling, legacy compatibility, and EBA dispatch.
- `event_converter.py` for native DingTalk events to common EBA events.
- `message_converter.py` for DingTalk message payloads to/from common `MessageChain` components.
- `api_impl.py` for common EBA API implementations.
- `platform_api.py` for DingTalk-specific `call_platform_api` actions.

The legacy DingTalk HTTP client now returns successful JSON response bodies from proactive send methods and raises with response details on non-200 responses.

## Configuration

| Field | Required | Notes |
|-------|----------|-------|
| `client-id` | yes | DingTalk robot/client identifier. |
| `client-secret` | yes | DingTalk client secret. |
| `robot-code` | yes | Robot code used for send APIs. |
| `robot-name` | no | Used for bot mention/self filtering and display. |
| `encrypt-key` | no | DingTalk callback encryption key when configured. |
| `verification-token` | no | DingTalk callback verification token when configured. |

## Supported Events

| Event | Support | Evidence |
|-------|---------|----------|
| `message.received` | implemented | `plugin-e2e-ui` private text and emoji-as-text. |
| `platform.specific` | implemented | Not reproduced in the latest UI run. |

## Receive Components

| Component | Support | Evidence |
|-----------|---------|----------|
| `Source` | supported | `plugin-e2e-ui` private message. |
| `Plain` | supported | `plugin-e2e-ui` private text. DingTalk emoji currently arrives as plain text such as `[smile]`. |
| `At` | converter path | Group trigger was not completed in the latest run. |
| `AtAll` | fallback/send-side only | Not completed inbound. |
| `Image` | supported | Real DingTalk Mac private-chat image upload reached the plugin as common `Image`. |
| `Voice` | converter path | Real UI inbound voice was not completed. |
| `File` | supported | Real DingTalk Mac private-chat file upload reached the plugin as common `File`. |
| `Quote` | converter path | Real UI inbound quote was not completed. |
| `Face` | not native common mapping | DingTalk emoji was observed as `Plain`, not `Face`. |
| `Forward` | not-supported inbound | DingTalk does not expose a portable structured forward event in this adapter. |

## Send Components

| Component | Support | Evidence |
|-----------|---------|----------|
| `Plain` | supported | `plugin-e2e-outbound`. |
| `At` | supported or text fallback | `plugin-e2e-outbound`. |
| `AtAll` | fallback | `plugin-e2e-outbound`. |
| `Image` | supported | `plugin-e2e-outbound`. |
| `File` | supported | `plugin-e2e-outbound`. |
| `Quote` | fallback | `plugin-e2e-outbound`. |
| `Face` | fallback | `plugin-e2e-outbound` as text fallback. |
| `Forward` | flattened fallback | `plugin-e2e-outbound`. |
| `Voice` | fallback/endpoint-dependent | Not separately verified as a native DingTalk voice send. |

## Common APIs

| API | Support | Notes |
|-----|---------|-------|
| `send_message` | supported | Verified through `EBAEventProbe`. |
| `reply_message` | supported | Verified through quoted/fallback send path. |
| `get_message` | cache-backed | Requires the message to have been observed by this adapter process. |
| `get_group_info` | cache-backed/API-backed where available | Group path not completed in latest UI run. |
| `get_group_list` | supported where DingTalk API allows | Limited live coverage. |
| `get_group_member_info` | supported where DingTalk API allows | Limited live coverage. |
| `get_user_info` | supported | Private sender path verified. |
| `get_friend_list` | limited | DingTalk does not expose a portable friend-list equivalent. |
| `get_file_url` | supported with media/file identifiers | Real inbound file yielded a platform file URL in the converted `File` component. |
| `call_platform_api` | supported | Safe action `check_access_token` verified. |

## Platform-Specific APIs

| Action | Support | Evidence |
|--------|---------|----------|
| `check_access_token` | supported | `plugin-e2e`. |
| `refresh_access_token` | supported | Implemented; not separately reproduced in the latest plugin run. |
| `get_file_url` | supported | Real inbound file yielded a platform file URL in the converted `File` component. |
| `get_audio_base64` | supported | Needs real inbound audio/media ID. |
| `download_image_base64` | supported | Real inbound image reached the plugin as `Image`; separate image-download API replay was not completed. |

## End-to-End Evidence

Evidence files:

- Text/API/component JSONL: `data/temp/dingtalk-plugin-e2e-20260510-rerun.jsonl`
- Real UI inbound media JSONL: `data/temp/dingtalk-plugin-e2e-media-ui.jsonl`

Verified:

- DingTalk Mac private chat in the `LangBot Team` organization produced `MessageReceived` through LangBot standalone runtime and `EBAEventProbe`.
- The common chain was `Source + Plain` for normal text.
- DingTalk emoji was received as `Source + Plain`, not common `Face`.
- Real DingTalk Mac private-chat image upload was received as `Source + Image`.
- Real DingTalk Mac private-chat file upload was received as `Source + File`.
- The plugin sent outbound text, mention/fallback, image, quote/fallback, file, and forward/fallback messages visible in DingTalk.
- The plugin called safe SDK and DingTalk platform APIs.

Not completed:

- Real UI inbound voice.
- Real UI inbound quote.
- Group trigger with a real robot mention.
- Destructive or organization-mutating APIs.
