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,7 +30,7 @@ const companyService = new CompanyService();
30 // 交付物归宿只有一份推导(28 §4.4),公司环境上下文与执行链路的运行时坐标共用它 30 // 交付物归宿只有一份推导(28 §4.4),公司环境上下文与执行链路的运行时坐标共用它
31 const { companyWorkspaceDir } = require('../src/core/prompt-builder'); 31 const { companyWorkspaceDir } = require('../src/core/prompt-builder');
32 const systemConfig = require('../config'); 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 const router = express.Router(); 34 const router = express.Router();
35 35
36 // 38 号文档 Phase 1:全站要求登录,但这个文件同时承接三处服务端自环调用 36 // 38 号文档 Phase 1:全站要求登录,但这个文件同时承接三处服务端自环调用
@@ -80,10 +80,25 @@ function selfChat(req, agentId, message, conversationId, label = &#39;SelfChat&#39;) { @@ -80,10 +80,25 @@ function selfChat(req, agentId, message, conversationId, label = &#39;SelfChat&#39;) {
80 port: req?.socket?.localPort || systemConfig.port, 80 port: req?.socket?.localPort || systemConfig.port,
81 path: `/api/agent/${agentId}/chat`, 81 path: `/api/agent/${agentId}/chat`,
82 method: 'POST', 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 }, (chatRes) => { 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 chatReq.on('error', (err) => console.error(`[${label}] ❌ 回调失败: ${err.message}`)); 103 chatReq.on('error', (err) => console.error(`[${label}] ❌ 回调失败: ${err.message}`));
89 chatReq.write(postData); 104 chatReq.write(postData);