Commit Graph

118 Commits

Author SHA1 Message Date
785e9935fd feat(memory): config embedders (LOT C2) + suggestion contextuelle (LOT C3) + contexte projet partagé
- 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>
2026-06-09 09:24:51 +02:00
32398827fb feat(memory): embedders vectoriels réels HTTP + ONNX derrière features (LOT C1)
Remplace les StubEmbedder pour les stratégies localServer/api/localOnnx par de
vrais moteurs, chacun derrière une feature cargo off-by-default — la posture
fondatrice « rien d'imposé, zéro dépendance » (défaut none → rappel naïf) reste
byte-for-byte inchangée.

C1a (feature vector-http, reqwest rustls optional):
- HttpEmbedder couvrant localServer (Ollama/llama.cpp) et api (OpenAI/Voyage…),
  payload OpenAI-compatible /v1/embeddings, ordre restauré par index, bearer
  token lu via env var (jamais en clair), timeout client 30s.
- detect_ollama() pour la détection de l'existant (C3).

C1b (feature vector-onnx, fastembed v5 optional):
- OnnxEmbedder en-process (e5-small, dim 384), init paresseuse + spawn_blocking,
  cache modèle sous <app_data>/embedders/onnx — aucun download au build ni au
  first-run, uniquement à la demande au 1er embed.
- Catalogue RECOMMENDED_ONNX_MODELS + ONNX_CACHE_SUBDIR + onnx_model_is_cached
  exposés (sans feature) pour la config (C2) et la popup (C3).

embedder_from_profile(profile, onnx_cache_dir) dispatche feature-gated ; sans la
feature, retombe sur StubEmbedder (Unsupported) → fallback naïf via
AdaptiveMemoryRecall. Composition root (build_memory_recall) propage le cache dir.

Tests: 10 HTTP + 6 ONNX (dont 2 #[ignore] download réel) + 26 vectoriels, verts
en défaut, --features vector-http et --features vector-onnx.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 19:00:40 +02:00
b39c11a64d fix(agents): enforcer l'invariant « 1 session vivante par agent » (singleton)
Un agent ne peut tourner que dans une seule cellule à la fois. La garde dans
LaunchAgent refuse le spawn si l'agent est déjà vivant dans un autre node
(AGENT_ALREADY_RUNNING) ; idempotent sur le même node ; le chemin resume
(agent mort) reste inchangé. Le node_id est désormais plombé jusqu'au use case.

Corrige le reset asymétrique d'une cellule au changement d'onglet : deux leaves
partageant le même agent id rendaient session_for_agent/is_agent_live/stop_agent
ambigus (cible arbitraire). Le churn reset/reattach déclenchait aussi les accents
mélangés (FIFO intact, non touché).

- snapshot agentWasRunning calculé par node (is_node_live) et non par agent
- commande list_live_agents + live_agents()/node_for_agent()/is_node_live()
- UI : dropdown grise les agents déjà placés ailleurs ; 2e cellule en doublon
  affiche « disponible » au lieu d'une relance fantôme

Tests : cargo test (application + app-tauri) vert ; tsc + vitest vert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 14:43:48 +02:00
f3bc3f20d8 feat(memory): clôture du sujet mémoire — bascule adaptative live + panneau UI (§14.5.5)
Pièce 1 (backend) : state.rs câble AdaptiveMemoryRecall via build_memory_recall,
piloté par le profil embedder chargé depuis embedder.json (fallback none). Défaut
none ⇒ NaiveMemoryRecall nu (comportement inchangé, StubEmbedder jamais touché,
zéro dépendance lourde). Instance de recall partagée (RecallMemory + LaunchAgent).
Chargement du profil isolé sur un runtime dédié (évite le block_on imbriqué).

Pièce 2 (frontend) : feature mémoire complète en miroir de skills —
MemoryGateway (port) + TauriMemoryGateway + MockMemoryGateway, types domaine
Memory/MemoryIndexEntry/MemoryType, MemoryPanel/MemoryEditor/useMemory, onglet
sidebar « Memory » dans ProjectsView. CRUD par slug, liens [[slug]] résolus.

Le sujet mémoire est clos : CRUD .md + index + rappel adaptatif + injection à
l'activation des agents + UI de gestion. Embedder concret ONNX/HTTP reste un
follow-up (défaut none = pleinement fonctionnel sans dépendance).

Tests: backend 57 binaires verts, frontend 285 tests verts, typecheck OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 13:28:27 +02:00
2435857cbf feat(memory): injecter le rappel mémoire dans le convention file à l'activation (§14.5.4)
À 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>
2026-06-08 09:01:51 +02:00
98a8b7292a feat(memory): système de mémoire projet model-agnostic (L14, LOT A+B+C)
Base de connaissance persistante par projet, indépendante de tout modèle/CLU
et de git. Cadrage archi en §14.5 (ARCHITECTURE.md), cycle Archi→Dev→Test.

LOT A — étage 1 (.md, source de vérité)
  - domaine: entité Memory (+ MemorySlug, MemoryType, MemoryFrontmatter,
    MemoryLink, MemoryIndexEntry), liens [[slug]], index MEMORY.md dérivé
  - port MemoryStore + MemoryError, adapter FsMemoryStore (.ideai/memory/)
  - application: 7 use cases (Create/Update/List/Get/Delete/ReadIndex/
    ResolveLinks), From<MemoryError> for AppError
  - app-tauri: commandes + DTO, events MemorySaved/MemoryDeleted
  - suppression de la variante morte DomainError::MalformedFrontmatter

LOT B — rappel adaptatif (étage 1)
  - port MemoryRecall + MemoryQuery, adapter NaiveMemoryRecall (troncature
    au budget de tokens, court-circuit budget-0), use case RecallMemory

LOT C — étage 2 vectoriel (structure complète, zéro dépendance lourde)
  - port Embedder + EmbedderError, profils déclaratifs EmbedderProfile/
    EmbedderStrategy (embedder.json)
  - VectorMemoryRecall (cosinus, cache .ideai/memory/.index/ gitignoré)
  - AdaptiveMemoryRecall (bascule pure should_use_vector), défaut none
  - HashEmbedder (déterministe, tests), StubEmbedder (onnx/server/api)

Tests: 57 binaires verts, build + clippy --workspace sans warning.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 08:47:23 +02:00
3ed0f6b45f feat(terminals): reprise de conversation par cellule + fix ordre d'écriture
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>
2026-06-07 22:27:08 +02:00
d11eaaa8c0 feat(orchestrator): create_skill action + wire per-project watchers (§14.3)
- 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>
2026-06-07 13:06:41 +02:00
b9fd2fb925 fix(terminals): stop PTY output duplication on re-attach
Each open/reattach spawned a pump thread feeding pty_bridge[session]. The
broadcast hub added a subscriber per attach without dropping the previous one,
and the stale pump thread kept delivering (its send_output still succeeded
against the new channel) — so every byte was delivered twice (more after
further re-attaches). Accumulated on each tab/layout/agent switch; window
resize made it glaring.

- Broadcast::subscribe() is now single-consumer: a new subscription supersedes
  the previous one, ending the old pump thread's stream.
- PtyBridge gains a per-session generation; register() returns it and
  unregister_if(session, gen) only removes when still current, so a dying
  superseded thread can't tear down the channel that replaced it.
- All three attach sites (open/reattach/agent launch) use unregister_if.

Tests: 6 added (generation guard + single-consumer broadcast). Workspace green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 11:52:24 +02:00
fbcf7bd436 test(orchestrator): inject SkillStore into LaunchAgent after skills merge
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>
2026-06-07 11:17:31 +02:00
e0c7e1403d Merge branch 'worktree-agent-a2650e91d2bd39ca2' into develop 2026-06-07 11:14:02 +02:00
480e7c7bbe feat(orchestrator): file-based orchestrator request watcher (§14.3)
- domain: OrchestratorRequest/Command parse-don't-validate + OrchestratorRequestProcessed event
- application: OrchestratorService dispatching spawn/stop/update_agent_context
- infrastructure: request watcher over .ideai/requests/, writes .response.json
- app-tauri: relay OrchestratorRequestProcessed to the frontend DTO

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 11:12:04 +02:00
3be55795a6 fix: fix some displays and features 2026-06-06 17:06:45 +02:00
2332b7f815 fix: fix some ui displays and features miss implemented 2026-06-06 16:15:19 +02:00
9736c42424 merge: isolate agent cwd in .ideai/run/<id> (convention-file collision fix, §14.1) 2026-06-06 12:26:02 +02:00
0660f52e2b fix(terminals): decouple PTY lifecycle from view lifecycle (no kill on navigation)
Navigating (layout/tab switch) tore the xterm view down and called
handle.close(), killing the backend PTY and cutting off running AIs. Now
the view's cleanup only detaches; only an explicit user action kills a PTY.

Backend:
- PortablePtyAdapter: per-session scrollback ring buffer (~100KB, most
  recent) + re-subscribable fan-out broadcast replacing the single-take
  output_rx. Reader thread feeds both the ring buffer and current
  subscribers; on EOF it closes subscribers (streams end) while keeping
  scrollback for late re-attach.
- PtyPort: new scrollback() method; subscribe_output is now re-subscribable
  (all impls + test fakes updated).
- reattach_terminal IPC command: returns scrollback and re-wires a fresh
  output channel on the live session without re-spawning.
- CloseRequested hook kills all live PTYs cleanly on app shutdown.
- TerminalSessions::handles() to enumerate live sessions at shutdown.

Frontend:
- TerminalHandle.detach(); TerminalGateway/AgentGateway.reattach() + mocks.
- TerminalView cleanup detaches (never close); on mount it re-attaches to a
  persisted session (repainting scrollback) instead of opening a new PTY.
- LayoutGrid persists the cell's session id via setSession; AgentsPanel
  tracks per-agent session ids — both drive reattach-vs-open.

Tests: ring buffer bounds to 100KB keeping newest bytes; scrollback retained;
re-subscription delivers post-reattach output; TerminalView detaches (not
closes) on unmount and reattaches with a known session; mock detach/reattach.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 12:24:48 +02:00
33edbad713 feat(agent): isolate agent cwd in .ideai/run/<id> to kill convention-file collisions
ARCHITECTURE §14.1: an agent's PTY cwd is now its own
`<project_root>/.ideai/run/<agent-id>/` directory, never the project root, so
N agents of the same profile no longer collide on a single conventional file
(CLAUDE.md/AGENTS.md/...).

- profile: cwd_template is now "{agentRunDir}" (built-in catalogue + docs).
- runtime: resolve_cwd substitutes {agentRunDir} (legacy {projectRoot} kept).
- LaunchAgent: computes + creates the run dir via FileSystem::create_dir_all,
  passes it as the cwd base to the pure prepare_invocation. Contract chosen:
  pass run_dir as the `cwd` argument (no PreparedContext change) — keeps
  prepare_invocation pure, I/O stays in the use case.
- convention file is generated by IdeA inside the run dir via a pure
  compose_convention_file(project_root, agent_md): absolute project-root header
  + agent persona (extensible for skills, §14.2).
- .gitignore: ignore .ideai/run/.
- run-dir cleanup left as a TODO (FileSystem port exposes no delete).

Tests: anti-collision (2 agents -> 2 distinct cwd, 2 distinct convention files,
none at root), run-dir creation order, composed convention file; pure unit
tests for agent_run_dir + compose_convention_file; runtime {agentRunDir}
substitution. cargo test --workspace + clippy -D warnings green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 12:18:14 +02:00
307ae71857 feat: add main features
Agents for developpement added + frontend add + backend added. Git viewer created + agent and template creator + layout and project creator
2026-06-06 01:27:01 +02:00