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

8.9 KiB
Raw Permalink Blame History

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:

./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:

./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:
STEM_AGENT=codex
STEM_MCP=1
STEM_INSTANCE=hazard3-sim-inf-bss-t4
  1. rozwiązać bieżący container ID i sockety z katalogu instancji,
  2. uruchomić bridge MCP dla tmux i nvim,
  3. 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:

$XDG_RUNTIME_DIR/stem/<thread-key>/<instance-key>/<cid12>/

W nim mogą znajdować się:

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:

$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.:

/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.