# Connector

Connector 在一个外部渠道（聊天平台、直播间）和 Cowork 的 Gateway 之间收发消息。用 `api.registerConnector(definition)` 注册它。

## ConnectorDefinition

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `id` | `string` | 0.1.41 中必填；<Badge type="warning" text="尚未发布" /> 中可选 | 小写字母、数字和连字符。`dozin.connectors` 列出了 id 时，必须是其中之一；没有这个列表时任何有效 id 都可以，最多 16 个 Connector。0.1.41 中必填且必须列出。从下一个版本起默认为包名 |
| `systemPrompt` <Badge type="warning" text="尚未发布" /> | `string` | 否 | 这个 Connector 的每个对话都遵守的规则，最多 4000 个字符；用户不能修改 |
| `chat` <Badge type="warning" text="尚未发布" /> | `{ title, roles?, maxLength?, maxPerMinute? }` | 否 | 消息进入同一个对话，由它用 `gateway_send` 回复。`title` 1–80 个字符；`roles` 最多 16 项，id 小写（`host`、`viewer`、`self`）→ 1–24 个字符的标签；`maxLength` 1–4000（默认 500）；`maxPerMinute` 1–30（默认 6）。`chat` 无效时整个被拒绝 |
| `start(ctx)` | function | 是 | 见下文 |
| `stop()` | function | 是 | 见下文 |
| `send(reply)` | function | 是 | 见下文 |

## start(ctx)

Connector 启动时调用。在这里打开连接、定时器和页面监听。

| 名称 | 类型 | 说明 |
|---|---|---|
| `ctx` | `ConnectorContext` | `emit`、`log`，有相应能力时还有 `browser` |

返回 `void` 或 promise。限时 5 秒。

## stop()

Connector 停止时调用。关闭 `start` 打开的一切。调用多次也必须安全。限时 5 秒。

## send(reply)

把 Cowork 的一条回复送到渠道。

| 名称 | 类型 | 说明 |
|---|---|---|
| `reply.text` | `string` | 回复内容 |
| `reply.channelKey` | `string` | 发往哪里，即 `emit` 给出的值 |
| `reply.idempotencyKey` | `string` | 同一个 key 最多发送一次 |
| `reply.replyTo` <Badge type="warning" text="尚未发布" /> | `{ id, senderId, senderName, role }` | 有 `chat` 时：这条回复所回答的消息 |

返回 `Promise<{ ok: boolean, error?: string, externalKey?: string, verified?: boolean }>`。`externalKey` 是平台自己给这个对话的 key。`verified` <Badge type="warning" text="尚未发布" /> 表示在对方那边看到了这条回复（默认等于 `ok`）。

| 结果 `error` | 何时 |
|---|---|
| `send_timeout` | `send` 用时超过 30 秒 |
| `unknown_connector` | 没有注册这个 id 的 Connector |
| `invalid_reply` | 无法读取 Cowork 传入的回复 |
| `invalid_send_result` | `send` 的返回值里没有布尔值 `ok` |
| 抛出的错误信息 | `send` 抛出了异常 |

## ctx.emit(message)

把一条收到的消息交给 Cowork。

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `text` | `string` | 是 | 对话里显示的内容 |
| `channelKey` | `string` | 否 | `<connectorId>/<kind>/<channelId>`；默认 `<connectorId>/group/default` |
| `senderId` | `string` | 否 | 平台上发送者的固定 id |
| `senderName` | `string` | 否 | 显示名 |
| `msgId` | `string` | 否 | 用于去除重复消息 |
| `at` | `number` | 否 | 平台时间，单位毫秒 |
| `trigger` | `boolean` | 否 | `false`：只作为上下文，随下一条需要回复的消息一起发送 |
| `contextText` | `string` | 否 | 模型读取的内容，代替 `text` |
| `role` <Badge type="warning" text="尚未发布" /> | `string` | 否 | 有 `chat` 时：谁在说话（`host`、`viewer`、`self`）；`self` 只作为上下文 |
| `modality` <Badge type="warning" text="尚未发布" /> | `'text' \| 'voice'` | 否 | `voice`：由语音转写而来 |

无返回值。

## ctx.log(text)

向 Extension 的日志写一行。无返回值。

```js
ctx.log('connected to room 42');
```

## 示例

```js
export function activate(api) {
  let timer;
  api.registerConnector({
    id: 'demo',
    start(ctx) { timer = setInterval(() => ctx.emit({ text: 'ping', senderId: 'bot' }), 60000); },
    stop() { clearInterval(timer); },
    async send(reply) { return { ok: true }; }
  });
}
```

## 相关

- [入口与注册](https://docs.micromeet.ai/zh/extension-sdk/entry.md): `registerConnector`
- [浏览器](https://docs.micromeet.ai/zh/extension-sdk/browser.md): `ctx.browser`，供读取网页的 Connector 使用
- [Gateway 外部渠道消息](https://docs.micromeet.ai/zh/connections/gateway.md): 消息去往哪里
