# Agenci w środowisku kontenerowym Ten dokument zapisuje wnioski do integracji agentów, takich jak Codex, Claude, Gemini i inne narzędzia asystujące. Docelowo integracja obejmuje trzy profile: `native-amd64`, `hazard3-sim` i `rp2350`. ## Założenie Agent nie powinien pracować wyłącznie przez komendy uruchamiane z hosta. Ma mieć wgląd w tę samą sesję, w której pracuje uczeń: - `tmux` - terminale, panele, `gdb`, wynik programu i logi uruchomienia, - `nvim` - edycja plików i nawigacja po kodzie, - `gdb` lub `gdb-multiarch` - stan debuggera, - katalog karty pracy zamontowany w kontenerze, - stan sesji zapisany pod `.stem/instances//`. Dzięki temu agent widzi środowisko debugowania, a nie tylko statyczne pliki. Punkt wejścia `stemctl` jest także projektem hostowym: pierwszy klon znajduje się w `~/dev/workspace/stem/tools/stem-launcher`. Agent ani użytkownik nie klonują launchera, kart lub repozytoriów odpowiedzi do kontenera. ## Aktualny fundament `rv32i-hazard3-student-env` dostarcza źródła wspólnego modelu: - profile `native-amd64`, `hazard3-sim` i `rp2350` jako trzy usługi Compose; - jeden wieloetapowy Dockerfile budowany lokalnie, bez dystrybucji obrazów; - `tmux` jako warstwa sesji terminalowej, - `nvim` uruchamiany ze stabilnym socketem, - `gdb-multiarch` dla profilu `rv32i`, - `gdb` i opcjonalnie `lldb` dla profilu `host`, - katalog stanu `.stem/instances//`, z fallbackiem `.rv`; - skrypty MCP dla `tmux` i `nvim`: - `scripts/mcp-tmux.sh`, - `scripts/mcp-nvim.sh`, - `scripts/nvim-in-container.sh`. Serwery MCP są instalowane w kontenerze, a hostowe wrappery weryfikują label i bieżący ID Podmana przed `podman exec`. `stemctl` oraz wspólny kontrakt trzech profili są wdrożone; osobne komendy wyższego poziomu `agent start/attach` pozostają rozszerzeniem późniejszym. ## Docelowy model komend Docelowo `stemctl` powinien ukrywać szczegóły socketów, kontenerów i providerów. Przykładowy kierunek: ```bash ./stemctl agent start codex native-amd64 inf bss 4 ./stemctl agent start codex hazard3-sim inf bss 4 ./stemctl agent start codex rp2350 inf bss 4 --target rp2350-rv ``` Skróty mogą powstać później, ale podstawowy model powinien zostać jawny: agent, profil środowiska, seria, karta i zadanie. Możliwy wariant dla już uruchomionej sesji: ```bash ./stemctl agent attach codex --instance hazard3-sim-inf-bss-t4 ``` ## Co powinien robić `stemctl` Przy `agent start` narzędzie powinno: 1. rozwiązać serię, kartę i zadanie tak samo jak `debug`, 2. wybrać jeden z trzech profili i właściwy target, 3. nadać stabilną nazwę instancji, na przykład `hazard3-sim-inf-bss-t4`, 4. uruchomić kontener i sesję `tmux`, 5. włączyć tryb agentowy przez zmienne środowiskowe, na przykład: ```text STEM_AGENT=codex STEM_MCP=1 STEM_INSTANCE=hazard3-sim-inf-bss-t4 ``` 6. rozwiązać bieżący container ID i sockety z katalogu instancji, 7. uruchomić bridge MCP dla `tmux` i `nvim`, 8. przekazać agentowi minimalny kontekst: - ścieżka repo karty, - profil środowiska, - nazwa zadania, - komendy build/debug/run, - ścieżki socketów, - ograniczenia profilu. ## Sockety i stan sesji Dla każdej instancji używamy dwóch poziomów tożsamości: ```text $XDG_RUNTIME_DIR/stem//// ``` W nim mogą znajdować się: ```text t.sock n.sock gdb-sync.json container.json ``` Pełna nazwa instancji pozostaje w label i registry. Krótkie klucze oraz 12-znakowy prefiks container ID utrzymują ścieżkę AF_UNIX poniżej 100 bajtów. Aktualny ID uniemożliwia użycie socketu pozostałego po odtworzeniu kontenera. `stemctl` traktuje te pliki jako szczegóły implementacyjne. Użytkownik i agent dostają komendy wyższego poziomu. ### Kto tworzy sockety Hostowy launcher `stem` najpierw tworzy katalog runtime i przekazuje go Podmanowi jako bind-mount pod **tą samą bezwzględną ścieżką**. Po uruchomieniu interfejsu debuggera procesy wewnątrz kontenera tworzą sockety: - `tmux` tworzy `t.sock`, - `nvim` tworzy `n.sock`. Nie są one kopiowane ani przekazywane przez sieć: host i kontener widzą ten sam plik Unix socket w zamontowanym katalogu. ID kontenera jest częścią ścieżki, więc nowy kontener po `rm/start` dostaje nowy katalog i nie może przypadkiem obsłużyć socketu poprzednika. ### Dynamiczny wybór kontenera Stały provider MCP na hoście używa dwóch krótkich ścieżek: ```text $XDG_RUNTIME_DIR/stem/mcp-selected/current/n.sock $XDG_RUNTIME_DIR/stem/mcp-selected/current/t.sock ``` Polecenie `stemctl mcp select INSTANCE_OR_CONTAINER_ID` weryfikuje registry, pełny ID i label kontenera oraz oba żywe serwery. Następnie atomowo przełącza symlink `current` dla obu socketów. Dzięki temu kolejna operacja MCP trafia do wybranej sesji tmuxa i Neovima bez restartowania Codexa. `stemctl mcp list` pokazuje tylko rekordy registry, a `stemctl mcp status` potwierdza bieżący wybór. Wybór pozostaje jawny: samo stworzenie kontenera tworzy jego katalog socketów, ale nie przejmuje automatycznie aktywnego MCP innej sesji. ### Wiele kont użytkowników i pomoc nauczyciela Sockety muszą pozostać prywatne dla unixowego konta, które uruchamia kontener, np.: ```text /run/user//stem//// ``` Nie montujemy ich do wspólnego `/tmp`, nie zmieniamy grup socketów tmuxa i nie udostępniamy ich przez port sieciowy. Codex nauczyciela łączy się przez SSH na konkretne konto ucznia; po drugiej stronie ograniczony wrapper MCP łączy się lokalnie z jego `n.sock` i `t.sock`. Stdio SSH jest transportem MCP — socket Unix nie opuszcza komputera ucznia. Każdy uczeń ma własny tmux i Neovim. Można uruchomić wiele serwerów MCP dla tej samej sesji (np. ucznia i nauczyciela), ale oba mogą równocześnie edytować bufor lub wysyłać klawisze, więc nie ma gwarancji arbitrażu zmian. Dostęp nauczyciela rozdzielamy na dwa klucze SSH: - zwykły klucz nauczycielski z pełną powłoką, używany wyłącznie do świadomej, ręcznej interwencji na koncie ucznia; - osobny klucz automatyzacji Codexa z forced command, bez TTY, forwardingu i powłoki, ograniczony do `nvim`, `tmux`, `status` i dozwolonych kontenerów. Pełny klucz nauczyciela nie trafia do kontenera ani do konfiguracji MCP. ### Poziomy uprawnień MCP Każdy wpis MCP otrzymuje jawny poziom dostępu. Poziom jest własnością klucza SSH i wrappera po stronie konta ucznia, a nie ustawieniem przekazywanym przez model lub klienta MCP: | Poziom | Przeznaczenie | Dozwolone działania | | --- | --- | --- | | `observe` | podgląd postępów | stan nvim, lista i capture pane’ów tmuxa, logi i metadane; bez edycji i wysyłania klawiszy | | `assist` | wspólne rozwiązywanie problemu | działania debuggera i jawnie dozwolona edycja/panele; bez ogólnego terminala oraz bez poleceń powłoki | | `full` | interwencja nauczyciela | pełne sterowanie nvimem i tmuxem, w tym terminalem w wybranym kontenerze, jako konto ucznia | `full` jest równoważny interaktywnej pracy na koncie ucznia w granicach wybranego kontenera. W szczególności arbitralne `nvim-remote-expr`, `nvim-ex` lub wysyłanie poleceń do pane’a tmuxa mogą uruchomić kod. Taki wpis tworzymy wyłącznie dla nauczyciela i zapisujemy w audycie konto, instancję, container ID, czas oraz użyty poziom. Nie wystarczy przekazać `MCP_ACCESS_LEVEL=observe` do tego samego pełnego serwera: niższe poziomy wymagają osobnych providerów z allowlistą narzędzi. W przeciwnym razie użytkownik nadal mógłby użyć ogólnego Ex/Vimscriptu albo terminala do obejścia ograniczenia. Klucz `full` może korzystać z obecnego providera STEM, ponieważ jego możliwości są celowo pełne. ## Role profili Profil `hazard3-sim`: - debugowanie kodu dla RISC-V/Hazard3, - `gdb-multiarch`, - symulator, - przykłady asemblerowe i mieszane C/ASM. Profil `native-amd64`: - natywne uruchomienie i debugowanie kodu C, - szybkie testowanie algorytmów, - `clang` albo `gcc`, - `gdb`, opcjonalnie `lldb` i `valgrind`. Profil `rp2350`: - debugowanie fizycznego Pico 2/Pico 2 W; - targety RISC-V Hazard3 i ARM Cortex-M33; - OpenOCD, probe, flash, serial i FreeRTOS; - dostęp tylko do jawnie wybranego urządzenia USB. Taski czysto asemblerowe RISC-V pozostają w `hazard3-sim` albo `rp2350-rv`. ## Kolejność wdrożenia 1. Domknąć trzy profile i komendy `build`, `test`, `run`, `debug`, `deploy`. 2. Ustabilizować labels instancji i katalogi socketów z container ID. 3. Zaimplementować resolver socketów dla `tmux` i `nvim`. 4. Dodać `stemctl agent list`. 5. Dodać `stemctl agent start`. 6. Dodać `stemctl agent attach`. 7. Dopiero potem podpinać konkretne providery: Codex, Gemini i kolejne. ## Zasada projektowa Integracja agentów ma być dodatkiem do kontenerowego środowiska pracy, a nie osobną ścieżką wykonywania zadań. Agent ma pomagać w tej samej sesji, w której działa uczeń: z tym samym repo, tym samym `tmux`, tym samym `nvim` i tym samym debuggerem.