makecindy / makecindy/cindy

[Bug] Slack Robot 缺少最终消息投递回执,发送失败时无法定位真实原因

Open
#80 2 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
TypeScript
Stars
2.7k
Forks
395
Avg merge
21h 48m
Merged PRs (30d)
776

Description

## 问题现象

用户反馈 Slack Robot 接到任务后,最终没有把消息成功发送给用户。

目前无法从现有证据确定 Slack 最初拒绝发送的具体原因。可能涉及 token、scope、channel、限流、消息格式或服务端状态,但在拿到结构化 Slack API 错误前都只能是假设,不应先把某个猜测写成根因。

需要先修复当前消息处理链路的确定性缺口:

desktop 完成 agent turn 后,只能把 `turn.end` 写入 WebSocket,或在连接不可用时进行本地缓存;协议没有 server 侧“已经成功投递到 Slack”或“Slack 投递失败”的语义回执。

因此 desktop 无法区分:

- turn 已正常完成,最终 Slack 消息也已投递;
- `turn.end` 只到达 hook server,后续 Slack `chat.postMessage` / `chat.update` 失败;
- 服务端尚未处理、处理超时或重连后重复处理;
- Slack 返回了可诊断错误,但错误没有沿协议回传。

## 当前链路与源码证据

当前 Slack Robot 主链路是:

```text
slack-hook-server
→ task.dispatch
→ desktop hook-control/session-runner
→ turn.progress / turn.end
→ slack-hook-server
→ Slack 最终消息投递
```

本仓源码证据:

- `apps/desktop/src/main/hook-control/session-runner.ts:1-25`:desktop 运行 headless turn,通过 `turn.progress` 和最终结果回传;
- `apps/desktop/src/main/hook-control/dispatcher.ts:324-331`:`turn.end` 只按连接发送;连接不可用时在本地缓存;
- `apps/desktop/src/main/hook-control/dispatcher.ts:353-413`:agent turn 收口后发送 `turn.end`,没有等待 Slack 投递结果;
- `cindy-protocol/packages/slack-hook-protocol/src/types.ts:6-20`、`cindy-protocol/packages/slack-hook-protocol/src/types.ts:36-40`、`cindy-protocol/packages/slack-hook-protocol/src/types.ts:112-138`:协议包含 `task.ack`、`turn.progress` 和 `turn.end`,但没有最终 delivery ack / receipt;
- 对照 `tool.request/tool.response`(`cindy-protocol/packages/slack-hook-protocol/src/types.ts:65-74`),Slack 工具链已经有结构化应答,Robot 最终消息投递没有同等的结果闭环。

迁仓前旧 `SlackIM → /api/slack/proxy` 路径中的 streaming Promise/finalize 缺陷已不属于当前 Robot 主链路,本 Issue 不应照搬旧实现结论。

## 期望行为

为最终消息投递建立可诊断、幂等且有界的结果闭环:

1. hook server 处理 `turn.end` 后,向 desktop 回传结构化投递结果;
2. 成功回执应关联原始 `requestId`,明确最终回复已投递;
3. 失败回执至少包含稳定、机器可读的错误分类和经过脱敏的安全信息,保留 Slack 原始错误类别所需的诊断价值;
4. desktop 在收到成功回执前,不能把“帧写入 socket”误当作“用户已经收到消息”;
5. 超时、断线和重连需要有界收口,并基于 `requestId` 保证幂等,避免重复发送最终回复;
6. 可重试错误只能进行有界且幂等安全的重试;不可重试错误应立即保留诊断结果;
7. 日志能够串联 `connectionId / requestId / externalKey / Slack method / delivery status`,但不得记录消息正文、token 或其他凭证;
8. 用户侧应获得合理的失败反馈。如果 Slack 本身已无法发送,至少应在 desktop 状态或日志中明确显示,而不是静默丢失。

具体协议形态,例如新增 delivery receipt 帧或扩展现有确认机制,可在实现前确定;但不能继续依赖日志文案或 prompt 猜测成功与否。

## 实现范围

### 当前仓库

- 在协议权威来源中定义结构化投递回执及解析、构造逻辑;
- desktop hook-control 维护待确认状态、超时和重连收口;
- 增加结构化日志和失败状态消费;
- 补齐协议与 desktop 单测。

### 服务端配合

`slack-hook-server` 不在当前仓库。服务端需要同步实现:

- 仅在 Slack 最终投递成功后返回成功回执;
- 将 Slack SDK/API 错误归一化为稳定错误分类并安全回传;
- 使用 `requestId` 做幂等,避免断线重投造成重复消息。

本仓的 `cindy-protocol` 是协议权威来源。升级 submodule 指针前必须确认服务端同步升级,避免 wire protocol 漂移。

## 验收标准

- [ ] `turn.end` 已到 server 且 Slack 最终发送成功时,desktop 收到与 `requestId` 匹配的成功回执;
- [ ] Slack `chat.postMessage` 失败时,desktop 收到结构化失败结果,不再只表现为“没有回复”;
- [ ] Slack `chat.update` 或最终收口失败时同样可以定位;
- [ ] 失败结果保留稳定错误分类,但不泄露 token、消息正文或其他敏感数据;
- [ ] 回执超时有明确且有界的失败状态;
- [ ] WebSocket 断线、重连、重复帧和迟到回执不会造成重复最终回复;
- [ ] 可重试错误的重试次数有上限,不可重试错误不会被盲目重试;
- [ ] 已覆盖成功、Slack API 拒绝、回执超时、断线重连、重复 `requestId`、迟到回执的协议与 desktop 测试;
- [ ] 已在对应服务端补齐 Slack API 失败与幂等投递测试;
- [ ] macOS / Windows desktop 行为一致;
- [ ] 远程连接和手机版能够看到一致的 session / 任务结果;如果当前通道无法同步投递失败状态,需要明确记录后续适配范围。

## 非目标

- 本 Issue 不猜测现场一定是 `missing_scope`、`invalid_auth`、`channel_not_found`、限流或账号切换导致;取得结构化回执后再根据证据定位上游故障。
- 本 Issue 不处理 `App not approved for Slack MCP server access`;该问题由独立的 Slack App 管理员 Issue 跟踪。
- 本 Issue 不复活或修补迁仓前已经退出主链路的 `/api/slack/proxy` / SlackIM streaming 实现。

Contributor guide

Open the contributing guide

Research direction

Start with cindy-protocol/packages/slack-hook-protocol/src/types.ts and then read apps/desktop/src/main/hook-control/dispatcher.ts and session-runner.ts to trace turn.end handling. Run the existing protocol and desktop test suites before changing behavior. Done means matched, structured delivery results cover success, failure, timeout, reconnect, duplicates, and late receipts without exposing sensitive data.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
backend-api-design, desktop, distributed-systems, testing
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.