Commit d48457acbe2d7e66651417d523e3705a11f00663

Authored by yibo
1 parent 9e586563

fix(auth): selfChat 自环唤醒漏带内部令牌,7 处调用全部静默 401

router.use(requireAuthOrInternal) 之后,agent-chat.js 自己的 selfChat() helper
(TaskDispatch/TaskCallback/TaskTransition ×2/TaskReject/HumanRequest 共 7 处
调用)一直在用裸 http.request 自己打自己的 /api/agent/:id/chat,没带
x-internal-token,全部被 401 挡住——而且 chatRes 回调之前不看 statusCode,
401 也照样打印"✅ 已唤醒",症状是静默的(跟 claude-cli-session-leak.md 一个模式)。
实测复现:真实 dispatch 一个任务给 org-innovator,目标 agent 从未收到消息,
任务卡在 dispatched 状态没人执行。

修法:selfChat 加 x-internal-token header(跟 company.js/task-settle.js 的
自环调用同一套机制),并且真的检查 chatRes.statusCode,非 2xx 打日志说明失败,
不再假装成功。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Showing 1 changed file with 19 additions and 4 deletions
server/routes/agent-chat.js
... ... @@ -30,7 +30,7 @@ const companyService = new CompanyService();
30 30 // 交付物归宿只有一份推导(28 §4.4),公司环境上下文与执行链路的运行时坐标共用它
31 31 const { companyWorkspaceDir } = require('../src/core/prompt-builder');
32 32 const systemConfig = require('../config');
33   -const { requireAuthOrInternal } = require('../middleware/jwt-auth');
  33 +const { requireAuthOrInternal, INTERNAL_TOKEN, INTERNAL_TOKEN_HEADER } = require('../middleware/jwt-auth');
34 34 const router = express.Router();
35 35  
36 36 // 38 号文档 Phase 1:全站要求登录,但这个文件同时承接三处服务端自环调用
... ... @@ -80,10 +80,25 @@ function selfChat(req, agentId, message, conversationId, label = &#39;SelfChat&#39;) {
80 80 port: req?.socket?.localPort || systemConfig.port,
81 81 path: `/api/agent/${agentId}/chat`,
82 82 method: 'POST',
83   - headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData) },
  83 + // 本文件顶部 router.use(requireAuthOrInternal) 之后,这条自环 fetch 也要带内部令牌,
  84 + // 否则 401——之前漏了这一处,TaskDispatch/TaskCallback/TaskTransition/TaskReject/
  85 + // HumanRequest 全部 7 个调用点静默失败(下面 chatRes 分支之前不看 statusCode,
  86 + // 401 也照样打印"✅ 已唤醒",症状跟 claude-cli-session-leak.md 那次一样是静默的)。
  87 + headers: {
  88 + 'Content-Type': 'application/json',
  89 + 'Content-Length': Buffer.byteLength(postData),
  90 + [INTERNAL_TOKEN_HEADER]: INTERNAL_TOKEN,
  91 + },
84 92 }, (chatRes) => {
85   - chatRes.on('data', () => {});
86   - chatRes.on('end', () => console.log(`[${label}] ✅ 已唤醒 ${agentId} (conv: ${conversationId})`));
  93 + let body = '';
  94 + chatRes.on('data', (c) => { body += c; });
  95 + chatRes.on('end', () => {
  96 + if (chatRes.statusCode >= 400) {
  97 + console.error(`[${label}] ❌ 唤醒 ${agentId} 失败: HTTP ${chatRes.statusCode} ${body.slice(0, 200)} (conv: ${conversationId})`);
  98 + } else {
  99 + console.log(`[${label}] ✅ 已唤醒 ${agentId} (conv: ${conversationId})`);
  100 + }
  101 + });
87 102 });
88 103 chatReq.on('error', (err) => console.error(`[${label}] ❌ 回调失败: ${err.message}`));
89 104 chatReq.write(postData);
... ...