feat(session): adapter HTTP OpenAI-compatible pour profils locaux/LAN (#14)

Ajoute un adapter de session HTTP OpenAI-compatible, purement additif,
permettant d'intégrer des modèles locaux/LAN comme profils IdeA canoniques
avec parité tool-calling/MCP.

- domain: extension du profil et des ports pour l'adapter OpenAI-compatible
- infrastructure: adapter openai_compat + routage factory
- app-tauri: mapping des outils OpenAI (openai_tools) + wiring state/lib
- application: catalogue d'agents + tests de use-cases profils

Validé QA (backend GO): round-trip byte-identique Claude/Codex, mapping
erreurs, dégradation tools, conversation_id None, conformance un seul Final,
routage factory. Suites vertes domain 467 / application 530 /
infrastructure 523 / app-tauri 247, 0 échec.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-07 22:09:16 +02:00
parent cb20fabdf4
commit aab4bcafb6
14 changed files with 1794 additions and 25 deletions

View File

@ -49,7 +49,7 @@ use domain::ports::{
EmbedderPromptStore, EventBus, FileSystem, GitPort, IdGenerator, IssueNumberAllocator,
IssueStore, MemoryRecall, MemoryStore, PermissionStore, ProcessSpawner, ProfileStore,
ProjectStore, PtyPort, ScheduledTask, Scheduler, SkillStore, SprintStore, TemplateStore,
WakeError, WakeReason,
ToolInvoker, WakeError, WakeReason,
};
use domain::profile::{
AgentProfile, ContextInjection, McpConfigStrategy, McpTransport, StructuredAdapter,
@ -81,6 +81,7 @@ use infrastructure::{
use crate::chat::ChatBridge;
use crate::mcp_endpoint::{mcp_endpoint, AppMcpRuntimeProvider, McpEndpoint};
use crate::openai_tools::{AppOpenAiToolInvoker, LateBoundOpenAiToolInvoker};
use crate::pty::PtyBridge;
use crate::tickets::AppTicketToolProvider;
@ -1084,9 +1085,12 @@ impl AppState {
// `profile.structured_adapter`. Injectés dans LaunchAgent (routage §17.4) et
// ChangeAgentProfile (shutdown polymorphe au hot-swap).
let structured_sessions = Arc::new(StructuredSessions::new());
let openai_tool_invoker = Arc::new(LateBoundOpenAiToolInvoker::new());
let openai_tool_invoker_port = Arc::clone(&openai_tool_invoker) as Arc<dyn ToolInvoker>;
let session_factory = Arc::new(
StructuredSessionFactory::new()
.with_sandbox_enforcer(infrastructure::default_enforcer()),
.with_sandbox_enforcer(infrastructure::default_enforcer())
.with_tool_invoker(openai_tool_invoker_port),
) as Arc<dyn AgentSessionFactory>;
let open_terminal = Arc::new(OpenTerminal::new(
@ -2146,6 +2150,10 @@ impl AppState {
// même use case que la commande Tauri, sans aller-retour frontend.
.with_spawn_background_command(Arc::clone(&spawn_background_command)),
);
openai_tool_invoker.bind(Arc::new(AppOpenAiToolInvoker::new(
Arc::clone(&orchestrator_service),
Arc::clone(&store_port),
)) as Arc<dyn ToolInvoker>);
let stop_live_agent = Arc::new(
StopLiveAgent::new(Arc::clone(&live_sessions), Arc::clone(&close_terminal))