docs(channels): document attachments and block streaming features

- Add Attachments interface docs with handling examples
- Document block streaming configuration and behavior
- Update architecture diagrams to show attachment resolution
- Add Attachment type to exported types reference
- Update plugin-example README

Covers new structured attachment support and block streaming
that delivers responses as multiple progressive messages.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
tanzhenxin 2026-03-27 14:57:17 +00:00
parent 3e0f213ea3
commit 39103eea5f
4 changed files with 161 additions and 41 deletions

View file

@ -68,23 +68,61 @@ export class MyChannel extends ChannelBase {
The normalized message object you build from platform data. The boolean flags drive gate logic, so they must be accurate.
| Field | Type | Required | Notes |
| ---------------- | ------- | -------- | -------------------------------------------------------------------------- |
| `channelName` | string | Yes | Use `this.name` |
| `senderId` | string | Yes | Must be stable across messages (used for session routing + access control) |
| `senderName` | string | Yes | Display name |
| `chatId` | string | Yes | Must distinguish DMs from groups |
| `text` | string | Yes | Strip bot @mentions |
| `threadId` | string | No | For `sessionScope: "thread"` |
| `messageId` | string | No | Platform message ID — useful for response correlation |
| `isGroup` | boolean | Yes | GroupGate relies on this |
| `isMentioned` | boolean | Yes | GroupGate relies on this |
| `isReplyToBot` | boolean | Yes | GroupGate relies on this |
| `referencedText` | string | No | Quoted message — prepended as context |
| `imageBase64` | string | No | Base64-encoded image for multimodal models |
| `imageMimeType` | string | No | e.g., `image/jpeg` |
| Field | Type | Required | Notes |
| ---------------- | ------------ | -------- | -------------------------------------------------------------------------- |
| `channelName` | string | Yes | Use `this.name` |
| `senderId` | string | Yes | Must be stable across messages (used for session routing + access control) |
| `senderName` | string | Yes | Display name |
| `chatId` | string | Yes | Must distinguish DMs from groups |
| `text` | string | Yes | Strip bot @mentions |
| `threadId` | string | No | For `sessionScope: "thread"` |
| `messageId` | string | No | Platform message ID — useful for response correlation |
| `isGroup` | boolean | Yes | GroupGate relies on this |
| `isMentioned` | boolean | Yes | GroupGate relies on this |
| `isReplyToBot` | boolean | Yes | GroupGate relies on this |
| `referencedText` | string | No | Quoted message — prepended as context |
| `imageBase64` | string | No | Base64-encoded image (legacy — prefer `attachments`) |
| `imageMimeType` | string | No | e.g., `image/jpeg` (legacy — prefer `attachments`) |
| `attachments` | Attachment[] | No | Structured media attachments (see below) |
For **files**: download from your platform, save to a temp directory, include the file path in `text`.
### Attachments
Use the `attachments` array for images, files, audio, and video. `handleInbound()` resolves them automatically: images with base64 `data` are sent to the model as vision input, files with a `filePath` get their path appended to the prompt so the agent can read them.
```typescript
interface Attachment {
type: 'image' | 'file' | 'audio' | 'video';
data?: string; // base64-encoded data (images, small files)
filePath?: string; // absolute path to local file (large files saved to disk)
mimeType: string; // e.g. 'application/pdf', 'image/jpeg'
fileName?: string; // original file name from the platform
}
```
Example — handling a file upload in your adapter:
```typescript
import { writeFileSync, mkdirSync, existsSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
const buf = await downloadFromPlatform(fileId);
const dir = join(tmpdir(), 'channel-files');
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
const filePath = join(dir, fileName);
writeFileSync(filePath, buf);
envelope.attachments = [
{
type: 'file',
filePath,
mimeType: 'application/pdf',
fileName,
},
];
```
The legacy `imageBase64`/`imageMimeType` fields still work for backwards compatibility but `attachments` is preferred for new code.
## Extension Manifest
@ -126,7 +164,11 @@ override async handleInbound(envelope: Envelope): Promise<void> {
**Tool call hooks** — override `onToolCall()` to display agent activity (e.g., "Running shell command...").
**Media** — download from your platform, set `imageBase64`/`imageMimeType` on the Envelope before calling `handleInbound()`.
**Streaming hooks** — override `onResponseChunk(chatId, chunk, sessionId)` for per-chunk progressive display (e.g., editing a message in-place). Override `onResponseComplete(chatId, fullText, sessionId)` to customize final delivery.
**Block streaming** — set `blockStreaming: "on"` in the channel config. The base class automatically splits responses into multiple messages at paragraph boundaries. No plugin code needed — it works alongside `onResponseChunk`.
**Media** — populate `envelope.attachments` with images/files. See [Attachments](#attachments) above.
## Reference Implementations