When a user places a voice call from an instance chat page (AgentInstanceChatPage),
the call is now routed through the OpenClaw bridge instead of iAgent's
VoiceSessionManager SDK loop. iAgent voice behavior is completely unchanged.
Changes:
- Flutter: ChatPage accepts agentInstanceId, passes to AgentCallPage
- Flutter: AgentCallPage sends instance_id in livekit/token request body
- Flutter: AgentInstanceChatPage passes instance.id as agentInstanceId
- voice-service: livekit_token.py includes instance_id in dispatch metadata
- voice-agent: agent.py reads instance_id from metadata, passes to LLM
- voice-agent: agent_llm.py stores _instance_id, sends instanceId in voice/start
- agent-service voice/start: stores instanceId in session metadata; skips
VoiceSessionManager for instance mode (iAgent path unchanged)
- agent-service voice/inject: detects instanceId in session metadata and
fire-and-forgets to OpenClaw bridge; callback via openclaw-app-callback
emits text WS events that voice-agent collects for TTS playback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
chatRepositoryProvider and sendMessageUseCaseProvider were not overridden
in AgentInstanceChatPage's ProviderScope, causing ChatNotifier.sendMessage
to use the parent scope's ChatRemoteDatasource (iAgent endpoint
POST /api/v1/agent/tasks) instead of AgentInstanceChatDatasource
(OpenClaw endpoint POST /api/v1/agent/instances/:id/tasks).
Fix: override both providers in the child scope so the full call chain
(sendMessage → SendMessage → ChatRepositoryImpl → createTask) routes
to the correct instance-specific endpoint.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fix 2 — Callback timeout wiring:
- Store callbackTimer in pendingCallbackTimers Map after creation
- handleOpenClawAppCallback clears the timer immediately on arrival,
preventing spurious "timeout" errors when the bridge replies in time
Fix 3 — Provider scope isolation:
- Override agentStatusProvider and robotStateProvider in child ProviderScope
so the robot avatar/FAB reflects the instance chat state, not iAgent's
Fix 4 — Voice routing to OpenClaw:
- AgentInstanceChatDatasource.sendVoiceMessage() now calls transcribeAudio()
then routes the transcript through instance-specific createTask() endpoint,
ensuring voice messages reach the user's OpenClaw agent, not iAgent
Fix 5 — Attachment UI in instance mode:
- Attachment button shown as disabled (onPressed: null) with explanatory
tooltip ("附件功能暂不支持智能体对话") when agentName != null
- Prevents misleading UX where attachments appear to work but are silently
dropped by the OpenClaw bridge
Fix 6 — DB schema template:
- Add agent_instance_id UUID NULL to agent_sessions table in migration 002
(tenant schema template) so new tenants get the column from creation
- Add covering index idx_agent_sessions_instance for efficient instance queries
All TypeScript and Flutter analyze checks pass clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>