You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Scope.backend/agents/qa_agent.py + route dans main.py :
WS /api/v1/agent/chat
Stateful per connection : maintient messages: list côté serveur
Tools : tools = await mcp_client.get_tools_schema() + UI_TOOLS + [ASK_INVESTIGATOR_TOOL]
(M5.2 est le fallback impl des M5.4 Managed Agents — même toolset)
Sur message client {type: "user", content: str} :
Append au history
Lance agent loop avec tools + Anthropic streaming
Pour chaque event reçu de l'API Anthropic :
Si content_block_delta text → envoie {type: "text_delta", content: ...} au client
Si tool_use block avec render_* → broadcast ui_render + envoie {type: "ui_render", component, props} au client
Si tool_use block avec ask_investigator → broadcast agent_handoff + spawn mini-session Investigator
envoie {type: "agent_handoff", from: "qa", to: "investigator", reason} au client
Si tool_use autre → envoie {type: "tool_call", name, args} + appelle mcp_client.call_tool()
envoie {type: "tool_result", name, ...}
À end_turn → envoie {type: "done"}
System prompt. "Tu es ARIA, assistant maintenance. Réponds aux questions de
l'opérateur sur ses équipements en utilisant les tools. Cite toujours les sources
(KB, logbook, signaux, RCA passés)."
Note
Decided — frontend gère le multi-tour.
Le ChatPanel (M6.5) affiche les
blocs dans l'ordre reçu : text_delta accumule dans une bulle assistant en
cours, tool_call crée une carte collapsable inline, tool_result enrichit la
carte, puis nouveau text_delta crée une nouvelle bulle. Événement done libre
l'input. Pattern éprouvé (Claude.ai, Cursor) — pas de risque.
Note
Decided — auth via cookie + decode manuel.
Au handshake :
Helper réutilisable core/security/ws_auth.py (même fonction utilisée par WS /api/v1/events en M4.1).
Acceptance.
wscat -c ws://localhost:8000/api/v1/agent/chat (avec cookie auth) → question "OEE de la pompe P-02 ce mois ?" → stream texte cohérent + tool_call get_oee visible — unit path fully covered by test_mcp_tool_call_streams_tool_call_and_result_summary (drives a fake messages.stream through the get_oee tool and asserts the client receives tool_call → tool_result → text_delta in order). Live E2E requires docker compose up + Sonnet key.
(added) Auth handshake: missing/invalid cookie closes with code 4401 — test_unauth_handshake_closes_without_calling_run_turn confirms the agent loop never runs unauthenticated.
(added) Per-connection messages: list state grows across user turns — verified in router test by passing the same list to two consecutive turns.
(added) ChatMap.tool_result.summary is a short string — _summarise_tool_result + test_summarise_tool_result_handles_list_dict_scalar_error pin the contract; raw tool JSON never reaches the chat channel.
(added) Dual-channel broadcast for ui_render and agent_handoff — events-bus uses from_agent/to_agent, chat channel uses from/to per ws.types.ts. test_render_tool_dual_channel_broadcast + test_ask_investigator_dual_channel_handoff assert both shapes.
(added) ask_investigator handoff does NOT spawn a 120s run_investigator — it calls the lightweight answer_investigator_question (Sonnet one-shot over recent RCAs + failure_history). Mirrors answer_kb_question (M3.5 — KB Builder en mode "agent appelé" (ask_kb_builder handler) #21) contract: pure, never raises, returns fallback dict on any error. Per audit §5 (M5.4).
(added) Turn isolation: each user frame sets current_turn_id ContextVar then resets on exit — every nested ws_manager.broadcast auto-tags with the correct turn_id for the Activity Feed / Agent Inspector.
Note
Milestone: M5 — Work Order Generator + Q&A
Planning doc:
docs/planning/M5-workorder-qa/issues.mdScope.
backend/agents/qa_agent.py+ route dansmain.py:WS /api/v1/agent/chatmessages: listcôté serveurtools = await mcp_client.get_tools_schema() + UI_TOOLS + [ASK_INVESTIGATOR_TOOL](M5.2 est le fallback impl des M5.4 Managed Agents — même toolset)
{type: "user", content: str}:content_block_deltatext → envoie{type: "text_delta", content: ...}au clienttool_useblock avecrender_*→ broadcastui_render+ envoie{type: "ui_render", component, props}au clienttool_useblock avecask_investigator→ broadcastagent_handoff+ spawn mini-session Investigator{type: "agent_handoff", from: "qa", to: "investigator", reason}au clienttool_useautre → envoie{type: "tool_call", name, args}+ appellemcp_client.call_tool(){type: "tool_result", name, ...}{type: "done"}System prompt. "Tu es ARIA, assistant maintenance. Réponds aux questions de
l'opérateur sur ses équipements en utilisant les tools. Cite toujours les sources
(KB, logbook, signaux, RCA passés)."
Note
Decided — frontend gère le multi-tour.
Le
ChatPanel(M6.5) affiche lesblocs dans l'ordre reçu :
text_deltaaccumule dans une bulle assistant encours,
tool_callcrée une carte collapsable inline,tool_resultenrichit lacarte, puis nouveau
text_deltacrée une nouvelle bulle. Événementdonelibrel'input. Pattern éprouvé (Claude.ai, Cursor) — pas de risque.
Note
Decided — auth via cookie + decode manuel.
Au handshake :
Helper réutilisable
core/security/ws_auth.py(même fonction utilisée parWS /api/v1/eventsen M4.1).Acceptance.
wscat -c ws://localhost:8000/api/v1/agent/chat(avec cookie auth) → question "OEE de la pompe P-02 ce mois ?" → stream texte cohérent + tool_callget_oeevisible — unit path fully covered bytest_mcp_tool_call_streams_tool_call_and_result_summary(drives a fakemessages.streamthrough theget_oeetool and asserts the client receivestool_call→tool_result→text_deltain order). Live E2E requiresdocker compose up+ Sonnet key.test_unauth_handshake_closes_without_calling_run_turnconfirms the agent loop never runs unauthenticated.messages: liststate grows across user turns — verified in router test by passing the same list to two consecutive turns.ChatMap.tool_result.summaryis a short string —_summarise_tool_result+test_summarise_tool_result_handles_list_dict_scalar_errorpin the contract; raw tool JSON never reaches the chat channel.ui_renderandagent_handoff— events-bus usesfrom_agent/to_agent, chat channel usesfrom/toperws.types.ts.test_render_tool_dual_channel_broadcast+test_ask_investigator_dual_channel_handoffassert both shapes.ask_investigatorhandoff does NOT spawn a 120srun_investigator— it calls the lightweightanswer_investigator_question(Sonnet one-shot over recent RCAs + failure_history). Mirrorsanswer_kb_question(M3.5 — KB Builder en mode "agent appelé" (ask_kb_builderhandler) #21) contract: pure, never raises, returns fallback dict on any error. Per audit §5 (M5.4).ASK_INVESTIGATOR_TOOLschema declared here (not M5.4 — Q&A migration vers Claude Managed Agents #33) so Managed Agents migration in M5.4 — Q&A migration vers Claude Managed Agents #33 just imports it.current_turn_idContextVar then resets on exit — every nestedws_manager.broadcastauto-tags with the correct turn_id for the Activity Feed / Agent Inspector.