Le runner de tâches de fond détectait la fin d'un process via l'EOF du drain de
sortie PTY, couplant le cycle de vie du process au flux d'output. Un output
encore ouvert (ou drainé ailleurs) pouvait masquer ou retarder la détection de
fin.
Ajoute wait/try_wait au port figé PtyPort :
- wait : bloquant, rend un ExitStatus idempotent ;
- try_wait : non bloquant, rend Option<ExitStatus> ;
- exit status mémorisé pour garder wait/try_wait/kill cohérents.
Implémentation dans PortablePtyAdapter (état d'exit mémorisé). Le
CommandBackgroundRunner détecte désormais la fin via pty.wait (select sur
cancel/deadline) au lieu de l'EOF du drain. Tous les fakes PtyPort du workspace
sont complétés en conséquence.
Le tee live UI reste hors périmètre (traité en #58). Aucun breaking IPC/front.
Tests : infrastructure/tests/background_task_runner.rs (nouveau) + pty_adapter.rs
verts, non-régression application/app-tauri OK (hors échecs réseau du sandbox).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Le first-run wizard restait figé : `CliAgentRuntime::detect` construisait un
runtime tokio courant-thread via `futures_block_on` pour piloter un
`ProcessSpawner` async. Appelé depuis le runtime async de Tauri, ce
`block_on` imbriqué panique ; la commande IPC ne répond alors jamais, la
promesse `detectProfiles` reste pendante et `busy` ne redescend plus.
Ce commit s'attaque à la cause côté backend :
- `AgentRuntime::detect` devient `async fn` (`#[async_trait]`) : le port cesse
de mentir sur sa nature. Il pilote un spawner async, il est async. Le
`futures_block_on` disparaît, et avec lui le runtime imbriqué.
- La sonde CLI est bornée par `tokio::time::timeout(DETECTION_TIMEOUT)` :
un binaire qui ne rend jamais la main dégrade la détection en `Err`, il
ne gèle plus l'appelant.
- Les profils `StructuredAdapter::OpenAiCompatible` n'ont pas de CLI à
spawner : les sonder revenait à tester un binaire inexistant. Ils sont
désormais sondés par un GET HTTP sur l'endpoint `/models` dérivé de
`chat_http.endpoint`, borné par le même timeout.
`DetectProfiles` séquence les `await` sur les candidats. Les fakes
`AgentRuntime` des crates application/infrastructure/app-tauri suivent la
nouvelle signature.
Tests: cargo test -p domain (241), -p application (81), -p infrastructure
(269), -p app-tauri (63+15) — verts. `rg futures_block_on crates` sans match.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adapters de persistance des BackgroundTask (store), sink de récupération
de complétion post-tour et boîte de réception (mailbox) pour les messages
concurrents, câblés sur l'entrée. Couvert par background_task_store,
background_completion_sink, agent_inbox et orchestrator_watcher (verts).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduit le modèle AgentManifest { version, entries, orchestrator } et la
garde d'écriture directe may_write_directly(..., &OrchestratorDesignation) :
seul l'orchestrateur désigné peut écrire directement, les autres passent par
le rendez-vous médié. Câble la désignation à travers domain → application →
infrastructure → app-tauri (context_guard, service, lifecycle, ports).
Ajoute crates/application/src/diag.rs : sink de diagnostic best-effort, sans
dépendance, qui miroite les traces du rendez-vous inter-agents de
l'orchestrateur vers un fichier de log persistant (utile au lancement via
AppImage où stderr est jeté), avec la même discipline « zéro dépendance,
ne casse jamais le rendez-vous ».
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fondations pures (zéro I/O, zéro dépendance landlock, aucun câblage
runtime — SpawnSpec.sandbox posé mais jamais lu ⇒ zéro régression) de la
voie « airtight » des permissions, complément de la voie projection LP3.
- domain/sandbox.rs : SandboxPlan/PathGrant/PathAccess (RO|RW|EXEC),
SandboxContext, SandboxKind/Status/Error, port SandboxEnforcer, et la
fonction pure compile_sandbox_plan(EffectivePermissions → plan OS).
- domain/permission.rs : render_permission_summary (bloc Markdown injecté
plus tard ; mentionne explicitement fichiers OS-enforced vs commandes
advisory).
- domain/ports.rs : SpawnSpec.sandbox: Option<SandboxPlan> (None ⇒ natif),
propagé à tous les sites de construction.
Sémantique de compile_sandbox_plan :
- Invariant produit : eff == None ⇒ None (rien posé ⇒ CLI 100 % native).
- Borne Landlock : seules les capabilities fichier produisent des grants
(Read→RO, Write/Delete→RW) ; ExecuteBash reste advisory (non verrouillable
par chemin).
- Deny-wins PAR CLASSE D'ACCÈS (RO/RW), fail-closed intra-classe : un Deny
ne ferme que les grants de sa propre classe (un Deny Write n'ampute pas un
Allow Read). Choix retenu pour maximiser l'autonomie des agents : on
respecte exactement la politique pré-renseignée sans sur-restreindre, donc
moins de blocages qui forceraient l'agent à redemander l'utilisateur.
- Globs réduits à leur préfixe statique ; grant abandonné si une barrière de
même classe chevauche (égal/ancêtre/descendant) — sous-approximation
conservatrice (un sandbox additif ne peut pas carver un deny sous-arbre).
Tests : 16 tests purs sur sandbox + 3 sur render_permission_summary,
cargo test --workspace 100 % vert, 0 ignored.
Reste LP4 : LP4-1 adapter LandlockSandbox + pre_exec PTY (fail-open+warning
sauf posture Deny), LP4-2 câblage application, LP4-3 composition root.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sauvegarde de l'arbre de travail en cours (persistance P8, conversations
C-series, write-portal frontend, médiation d'entrée) avant d'attaquer le
support de la délégation inter-agents pour les profils Codex.
Le round-trip inter-agent question/réponse est couvert sans tokens par
les tests loopback existants (state::mcp_e2e_loopback_tests).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Expose l'orchestration IdeA comme serveur MCP par-dessus le même
OrchestratorService::dispatch, avec repli fichier .ideai/requests pour les
CLI sans MCP. v3 réduite à la surface MCP : la messagerie inter-agents et la
corrélation requête↔réponse étaient déjà résolues par §17 (send_blocking).
- M0 capacité MCP sur le profil (McpCapability/McpConfigStrategy/McpTransport)
- M1 injection conf MCP au LaunchAgent + prose adaptée selon la surface
- M2 serveur/adapter MCP (JSON-RPC 2.0 maison ; outils idea_*) + ListAgents
- M3 câblage par projet (registre mcp_servers jumeau du watcher)
- M4 observabilité UI : OrchestratorRequestProcessed.source = file|mcp + badge
Trois portes d'entrée (fichier, MCP, UI) → un seul dispatch ; aucun nouveau
port applicatif ; MCP confiné à l'adapter infra. Tous lots verts (cycle §3).
Cadrage : .ideai/briefs/orchestration-v3-cadrage.md ; ARCHITECTURE.md §14.3.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ferme le canal fichier de la messagerie inter-agents : un agent qui délègue
via le protocole .ideai/requests reçoit désormais le CONTENU de la réponse,
plus seulement l'ACK de cycle de vie.
- OrchestratorResponse gagne reply: Option<String> (skip_serializing_if
None, camelCase). success() porte le reply ; failure() => None.
- dispatch_file passe out.reply (de l'OrchestratorOutcome D6) à success.
Champ additif : un .response.json legacy sans reply reste valide ; une
commande non-ask (agent.run, spawn_agent...) => clé reply absente du JSON.
Tests (QA) : +4 verts — ask => reply présent ET detail coexistent ;
non-ask => clé absente (garde anti-always-green sur l'absence littérale) ;
round-trip legacy ; failure => absent. cargo test --workspace : 824 passed.
Orchestration structurée bouclée : in-process (D6) + protocole fichier (D6b).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- LOT C2 (§14.5.3) : use cases de configuration des embedders déclaratifs
(List/Save/Delete + DescribeEmbedderEngines : modèles ONNX recommandés,
environnement local détecté, stratégies compilées). UI EmbedderSettings.
- LOT C3 (§14.5.5) : suggestion contextuelle best-effort à l'activation quand la
mémoire dépasse le budget de recall sans embedder configuré (event
EmbedderSuggested, anti-spam 1×/session, « ne plus demander »).
- Contexte projet partagé .ideai/CONTEXT.md (model-agnostic) injecté à tous les
agents/profils au lancement, avant la persona. UI ProjectContextPanel.
Tests : backend workspace vert (0 échec) ; frontend 306/306.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
À l'activation d'un agent, LaunchAgent compose désormais une section
« # Mémoire projet » dans le convention file généré (CLAUDE.md/AGENTS.md…),
au même titre que les skills assignés (§14.2). Les agents lisent ainsi la
mémoire projet sans aucun flag ni mécanisme propre à une CLI.
- LaunchAgent reçoit le port MemoryRecall (Arc<dyn MemoryRecall>), résout le
rappel (budget AGENT_MEMORY_RECALL_BUDGET=2048, requête = persona de l'agent)
en best-effort : mémoire vide/absente ou erreur ⇒ section omise, le launch
n'est jamais bloqué (comme un SkillRef dangling).
- compose_convention_file gagne un argument `memory: &[MemoryIndexEntry]`
(reste pure) ; section injectée seulement pour la stratégie conventionFile.
- state.rs : une seule instance NaiveMemoryRecall partagée (recall + LaunchAgent).
Tests: 57 binaires verts. compose_convention_file (vide ⇒ inchangé, ordre,
cohabitation skills) + intégration LaunchAgent (section présente / absente /
best-effort sur erreur / pas d'injection en stratégie env).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Permet de recharger la conversation CLI précédente de chaque cellule à la
réouverture du projet, de façon universelle (indépendant du modèle/CLI).
- profil AgentRuntime: bloc déclaratif optionnel `session { assignFlag, resumeFlag }`
- LeafCell: `conversationId` (persistant, distinct du SessionId PTY) + `agentWasRunning`
- runtime: SessionPlan (None/Assign/Resume) + composition pure des args
- LaunchAgent: décide Assign vs Resume, génère l'UUID, remonte l'id assigné
(persistance par l'appelant via setCellConversation — découplage SRP)
- close: SnapshotRunningAgents fige `agentWasRunning` avant le kill-all
(statut clot/en cours universel, sans parsing CLI)
- SessionInspector: port optionnel best-effort + adapter ClaudeTranscriptInspector
- popup de reprise par cellule (statut + sujet/tokens si dispo), intercalée
avant le Resume auto, jamais sur le chemin reattach
fix(terminals): sérialise les écritures PTY (file FIFO par handle) — corrige
les caractères mélangés/accents dus au réordonnancement des invoke Tauri concurrents
fix(layout): l'opération `move` préservait mal les champs du leaf (perdait `agent`)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- domain: OrchestratorCommand::CreateSkill with optional scope field
(defaults to Project, case-insensitive, UnknownScope on bad value)
- application: dispatch CreateSkill through the CreateSkill use case
- app-tauri: build OrchestratorService and start/stop per-project request
watchers on open/create/close_project (ensure/stop_orchestrator_watch)
- tests: domain validation, service dispatch, infra watcher e2e,
app-tauri wiring lifecycle (idempotent, isolated, stop)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The orchestrator branch predated the skills feature; its LaunchAgent test
construction lagged the new 8-arg signature. Add an empty FakeSkills to both
the service and watcher tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>