Files
2026-07-17 08:25:07 +02:00

241 lines
8.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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/<instance>/`.
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/<instance>/`, 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/<thread-key>/<instance-key>/<cid12>/
```
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/<uid-ucznia>/stem/<thread>/<instance>/<container-id>/
```
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 panea 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.