From 495407d25b7557b901d972bd8087248d829f6573 Mon Sep 17 00:00:00 2001 From: hailin Date: Sun, 8 Mar 2026 14:20:58 -0700 Subject: [PATCH] fix(agent): pass sessionId to system prompt for text chat OAuth trigger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Text sessions were not passing sessionId to SystemPromptBuilder, causing Claude to use the `initiate_dingtalk_binding` custom tool (claude_api only). When the engine is claude_agent_sdk, this tool does not exist → 404. Fix: pass session.id as sessionId to systemPromptBuilder.build() in agent.controller.ts. Claude will now use the wget oauth-trigger endpoint for ALL session types (text and voice), which works with every engine. Also: store userId (staffId) as the DingTalk binding ID when resolvable, falling back to openId. Bot messages deliver senderStaffId which matches userId, not openId — this prevents the "binding not found" routing failure. Co-Authored-By: Claude Sonnet 4.6 --- .../dingtalk/dingtalk-router.service.ts | 14 +++++++++++--- .../rest/controllers/agent.controller.ts | 4 ++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/services/agent-service/src/infrastructure/dingtalk/dingtalk-router.service.ts b/packages/services/agent-service/src/infrastructure/dingtalk/dingtalk-router.service.ts index 50b7c06..7ce8a93 100644 --- a/packages/services/agent-service/src/infrastructure/dingtalk/dingtalk-router.service.ts +++ b/packages/services/agent-service/src/infrastructure/dingtalk/dingtalk-router.service.ts @@ -256,11 +256,19 @@ export class DingTalkRouterService implements OnModuleInit, OnModuleDestroy { const instance = await this.instanceRepo.findById(entry.instanceId); if (!instance) throw new Error('智能体实例不存在'); - // Store openId — this matches senderId in incoming bot messages (used for routing) - instance.dingTalkUserId = openId; + + // Store userId (staffId) as the binding identifier if resolved — this matches senderStaffId + // in incoming DingTalk bot messages, enabling correct routing. + // Fall back to openId if userId could not be resolved (routing may still work if + // DingTalk delivers openId in senderId for this app type). + const bindingId = userId ?? openId; + instance.dingTalkUserId = bindingId; await this.instanceRepo.save(instance); - this.logger.log(`OAuth binding saved: instance ${entry.instanceId} → dingTalkUserId(openId)=${openId}`); + this.logger.log( + `OAuth binding saved: instance ${entry.instanceId} → dingTalkUserId=${bindingId} ` + + `(${userId ? 'staffId/userId' : 'openId fallback — staffId not resolved'})`, + ); // Send proactive greeting using userId (staffId). Skip if not resolved. this.sendGreeting(userId, openId, instance.name).catch((e: Error) => diff --git a/packages/services/agent-service/src/interfaces/rest/controllers/agent.controller.ts b/packages/services/agent-service/src/interfaces/rest/controllers/agent.controller.ts index f0cf284..5810584 100644 --- a/packages/services/agent-service/src/interfaces/rest/controllers/agent.controller.ts +++ b/packages/services/agent-service/src/interfaces/rest/controllers/agent.controller.ts @@ -146,6 +146,10 @@ export class AgentController { tenantId, userId, userEmail, + // Pass session ID so Claude uses the wget oauth-trigger endpoint (works for ALL engine types). + // The oauth-trigger endpoint emits an oauth_prompt WS event to this session's stream, + // which Flutter's chat page handles the same way as voice sessions. + sessionId: session.id, }); // Fire-and-forget: run the task stream