You need to sign in before continuing.

Commit 5d095609389de02ef65ea7f8a9d5c39e16a6f4ed

Authored by yibo
1 parent f0468e9b

fix(heartbeat): 两套心跳自环调用缺内部令牌,暗中 401(真实回归发现)

验证 46 号文档 Phase 3 v2(CEO 心跳发现并派发子任务)时,触发 CEO 心跳
直接撞上 401——company-heartbeat-service.js 的 _sendAgentChat 和
heartbeat-service.js 的 _triggerHeartbeat 都在自环调用
POST /api/agent/:agentId/chat(agent-chat.js 挂了 requireAuthOrInternal),
但两处都没有带 INTERNAL_TOKEN_HEADER——company.js/task-settle.js 的同类
自环调用早就在带,这两处漏了。

失败只落一行 console.warn,界面上什么都看不出来,影响的不只是 CEO 心跳:
两套心跳系统(per-agent 的 heartbeat-service 和公司级的
company-heartbeat-service,含它驱动的 pipeline/schedule 触发)都受影响。

按现成模式补上内部令牌头。全量单测 1189 passed(唯二失败与本次改动
无关),修完真实验证 CEO 心跳能正常跑完整段推理会话。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H85SS5hVC4u1PbVhjmrhH8
server/services/company-heartbeat-service.js
@@ -473,6 +473,11 @@ class CompanyHeartbeatService { @@ -473,6 +473,11 @@ class CompanyHeartbeatService {
473 * isHeartbeat 仍然发,但只对真心跳为真 —— 老的消费方(如果还有)行为不变。 473 * isHeartbeat 仍然发,但只对真心跳为真 —— 老的消费方(如果还有)行为不变。
474 */ 474 */
475 _sendAgentChat(companyId, agentId, message, conversationId, kind = 'heartbeat') { 475 _sendAgentChat(companyId, agentId, message, conversationId, kind = 'heartbeat') {
  476 + // agent-chat.js 挂了 requireAuthOrInternal(38 号文档 Phase 1)——这是一条服务端自环
  477 + // fetch,没有用户 cookie,不带内部令牌会被 401 挡住。company.js/task-settle.js 的自环
  478 + // 调用都带了这个头,这里漏了,是心跳(含 CEO 心跳、pipeline/schedule 触发)一直在暗中
  479 + // 401 的根因:失败只落一行 console.warn,界面上什么都看不出来。
  480 + const { INTERNAL_TOKEN, INTERNAL_TOKEN_HEADER } = require('../middleware/jwt-auth');
476 const postData = JSON.stringify({ 481 const postData = JSON.stringify({
477 message, 482 message,
478 conversationId, 483 conversationId,
@@ -488,6 +493,7 @@ class CompanyHeartbeatService { @@ -488,6 +493,7 @@ class CompanyHeartbeatService {
488 headers: { 493 headers: {
489 'Content-Type': 'application/json', 494 'Content-Type': 'application/json',
490 'Content-Length': Buffer.byteLength(postData), 495 'Content-Length': Buffer.byteLength(postData),
  496 + [INTERNAL_TOKEN_HEADER]: INTERNAL_TOKEN,
491 }, 497 },
492 }; 498 };
493 499
server/services/heartbeat-service.js
@@ -228,6 +228,11 @@ class HeartbeatService { @@ -228,6 +228,11 @@ class HeartbeatService {
228 `无需等待人类确认,请完全自主决策。` 228 `无需等待人类确认,请完全自主决策。`
229 ].join('\n'); 229 ].join('\n');
230 230
  231 + // agent-chat.js 挂了 requireAuthOrInternal(38 号文档 Phase 1)——这是一条服务端自环
  232 + // fetch,没有用户 cookie,不带内部令牌会被 401 挡住。company.js/task-settle.js 的自环
  233 + // 调用都带了这个头,这里漏了,是"心跳一直在暗中 401"的根因之一(另一处见
  234 + // company-heartbeat-service.js 的同款漏洞):失败只落一行 console.warn,界面上看不出来。
  235 + const { INTERNAL_TOKEN, INTERNAL_TOKEN_HEADER } = require('../middleware/jwt-auth');
231 const postData = JSON.stringify({ 236 const postData = JSON.stringify({
232 message: heartbeatMessage, 237 message: heartbeatMessage,
233 conversationId: convId, 238 conversationId: convId,
@@ -241,7 +246,8 @@ class HeartbeatService { @@ -241,7 +246,8 @@ class HeartbeatService {
241 method: 'POST', 246 method: 'POST',
242 headers: { 247 headers: {
243 'Content-Type': 'application/json', 248 'Content-Type': 'application/json',
244 - 'Content-Length': Buffer.byteLength(postData) 249 + 'Content-Length': Buffer.byteLength(postData),
  250 + [INTERNAL_TOKEN_HEADER]: INTERNAL_TOKEN,
245 } 251 }
246 }; 252 };
247 253