# GRILLED_PLAN - `cc-agent-proxy` + programme Lua `opencc` > Compagnon de `.plans/opencc-agent-proxy-plan.md`. Chaque question a une > recommandation par defaut et un bloc `TODO: answer` que tu remplis. Un > prochain `/grill-me` lira ce fichier et le condensera en plan v2 pret a > executer. > > Convention : > - Remplace `TODO: answer` par ta reponse (1-3 lignes suffisent). > - Si tu choisis une option listee, ecris `Option N` + raison breve. > - Si tu inventes une 5e option, ecris-la in extenso. > - Si une question devient sans objet apres une reponse majeure, ecris > `N/A - voir Q`. > - Laisse `TODO: answer (optionnel)` tel quel si pas d'avis. ## Regles de base du PoC - Une turtle, un OpenCode, un proxy, une reponse utilisable. Rien de plus. - Contraintes explicites > design generique. - Pas de packaging tant que la boucle n'a pas tourne in-game. - Aucun secret commit. - La forme des reponses OpenCode est suspecte tant qu'on ne l'a pas vue en vrai. --- ## A. Les 8 questions determinantes A repondre en premier. Le reste devient souvent trivial une fois ces choix trances. ### A1. Critere d'acceptation du PoC Quand dit-on "c'est fini" ? - **Option 1 - curl roundtrip** : `curl /ask` retourne une vraie reponse OpenCode. Pas d'in-game. - **Option 2 - CraftOS-PC roundtrip** : `opencc "salut"` repond dans l'emulateur, en pointant sur le proxy local. - **Option 3 - in-game ATM10** : `opencc` marche sur une vraie turtle apres config `http.rules`. - **Option 4 - conversation in-game** : Option 3 + verifier que la session persistante garde le contexte sur plusieurs prompts. Recommandation : **Option 4**. C'est ce qui prouve que le pipeline tient bout en bout. 1-3 sont des jalons intermediaires. Premier prompt anodin (`dis bonjour`) avant prompts libres. Pourquoi ca compte : decide si async, multi-turtle, logs riches, quotas, packaging et UX terminale sont hors scope. Option 4. Le PoC est fini seulement quand `opencc` marche in-game sur une vraie turtle et que deux prompts successifs prouvent la session persistante. Les curls OpenCode faits sur `4242` restent des jalons de contrat API. ### A2. Autorite et workspace d'OpenCode Le proxy parle a un `opencode serve` qui a, potentiellement, acces complet a des fichiers et a un shell. Une turtle qui prompt = un acteur exterieur qui peut indirectement declencher des actions cote hote. - **Option 1 - workspace dedie jetable** : `opencode serve` lance dans un dir temporaire (`/tmp/opencc-poc`) sans secrets, sans repo perso. - **Option 2 - workspace projet dedie** : un dossier `~/opencc-workspace/` vide ou minimal, persistant entre runs. - **Option 3 - repo perso** : non, trop risque. - **Option 4 - sandbox container** : OpenCode dans Docker avec mount limite. Plus de cout pour le PoC. Recommandation : **Option 1**. Workspace jetable, recree a chaque session de test. Documenter le dir exact dans `.env`. Pourquoi ca compte : c'est la vraie frontiere de confiance, plus que le token Lua. Option 1. `opencode serve` doit tourner dans un workspace jetable dedie au PoC, pas dans ce repo : les endpoints serveur peuvent piloter le TUI et declencher le LLM, donc la frontiere de confiance est le workspace OpenCode. ### A3. Cycle de vie de la session - **Option 1 - session unique partagee** : le proxy cree une session au boot, toutes les turtles tapent dedans. Conflits de memoire conversationnelle. - **Option 2 - session par turtle, persistee cote turtle** : la turtle stocke `sessionId` dans `settings` ; le proxy se contente de relayer. - **Option 3 - session ephemere par prompt** : chaque `/ask` cree+detruit. Pas de continuite. Plus simple, perd le contexte. - **Option 4 - session par turtle, mappee cote proxy** : table `turtleId -> sessionId` cote proxy. Necessite d'identifier la turtle. Recommandation : **Option 2**. Le proxy reste sans etat ; la turtle envoie `sessionId` optionnel, le proxy en cree une si absent et le renvoie dans la reponse. Option 2. La turtle garde `opencc.session_id` dans `settings`; le proxy cree une session si absent et renvoie toujours le `sessionId`. Cela evite un etat proxy et colle au contrat reel `POST /session` puis `/session/:id/message`. ### A4. Sync vs async pour `/ask` `POST /session/:id/message` peut bloquer longtemps. CC:Tweaked a des timeouts HTTP (~30s typiques, depend du serveur). - **Option 1 - sync stricte** : le proxy attend la fin et renvoie. Risque de timeout cote turtle. - **Option 2 - async + polling** : `POST /ask` -> `jobId`, `GET /ask/:jobId` pour poll. Plus robuste, plus complexe. - **Option 3 - keepalive / chunked** : inutile en HTTP CC standard. - **Option 4 - sync now, async later** : on commence en sync (prompts courts pour PoC), on documente la limite, on bascule async si on l'atteint. API `/ask` reste compatible. Recommandation : **Option 4**. Option 4. On demarre en sync avec prompts courts, car `POST /session/:id/message` attend bien la reponse LLM quand `noReply` est absent. Si les timeouts CC apparaissent, on ajoute async/polling sans changer le happy path `/ask`. ### A5. Topologie de deploiement Le plan source dit "deploiement HTTPS, RP, firewall, exposition publique geres separement par Guillaume". - **Option 1 - tout sur ta machine perso** : `opencode serve` + `cc-agent-proxy` en localhost, expose via tunnel (Cloudflare, ngrok). - **Option 2 - VPS dedie** : meme hote distant, HTTPS via Caddy/nginx. - **Option 3 - homelab** : Option 2 mais chez toi, derriere ton RP existant. - **Option 4 - deux hotes separes** : OpenCode sur GPU dedie, proxy ailleurs. Conditionne : auth (A6), `OPENCODE_BASE_URL`, qui termine TLS. Recommandation : **Option 3** si tu as un homelab, sinon Option 1. Proxy + opencode sur le meme hote, loopback entre les deux, proxy expose en HTTPS. Option 3 pour moi : homelab/RP si expose a une turtle ATM10 reelle. Proxy et OpenCode restent sur le meme hote, OpenCode en loopback, seul le proxy sort en HTTPS. Pour dev local, Option 1 avec tunnel reste acceptable. ### A6. Modele d'authentification turtle <-> proxy - **Option 1 - token unique partage** : un seul `PROXY_TOKEN`, meme valeur sur toutes les turtles via `settings`. Pas de revocation granulaire. - **Option 2 - token par turtle** : table de tokens cote proxy. Permet revocation par turtle. - **Option 3 - HMAC signe** : turtle envoie `os.getComputerID()` + HMAC. Plus complexe, peu de gain sans rotation. - **Option 4 - allowlist IP** : impossible, la turtle sort via le proxy HTTP du serveur Minecraft. Recommandation : **Option 1**. Helper `isTokenValid(token)` pour permettre une table plus tard sans refactor. Option 1 pour le PoC. Token unique partage, header seulement, helper `isTokenValid` pour permettre token par turtle plus tard sans changer le programme Lua. ### A7. Forme de la reponse renvoyee a la turtle OpenCode retourne `parts`, tool calls, code blocks. La turtle a peu de RAM. - **Option 1 - texte plat** : `{ reply }`. Tout ce qui n'est pas texte est jete. - **Option 2 - texte + meta** : `{ reply, model, tokens, sessionId }`. - **Option 3 - parts brutes** : la turtle parse. Trop couteux. - **Option 4 - texte plat + troncature** : `{ reply, sessionId, truncated }` avec `MAX_REPLY_CHARS` (ex. 4 KiB). Logs proxy gardent le complet. Recommandation : **Option 4**. Option 4. Le proxy renvoie `{ reply, sessionId, truncated }`; il concatene les parts `text` uniquement et tronque pour la turtle. Les parts tools/reasoning observees dans OpenCode doivent rester cote proxy/logs, pas cote turtle. ### A8. Scope minimal v1 (in et out) - **Option 1 - hello world** : turtle envoie "ping", recoit "pong". Une seule route, sync, pas de session persistante. - **Option 2 - conversation 1 turtle / 1 session** : echange sur plusieurs prompts, sessionId persistante. - **Option 3 - + outils OpenCode** : agent peut lire/ecrire un fichier dans le workspace. - **Option 4 - + multi-turtle** : deux turtles en parallele sans collision. Recommandation : **Option 2**. Hors-scope explicite du PoC : async, multi-turtles, package ccpm, deploiement auto, Docker, quotas, UI terminale riche, streaming OpenCode (`/event`). Option 2. Scope v1 = une turtle, une session persistante, un prompt par invocation. Les endpoints TUI sont utiles pour piloter un client humain, mais le proxy turtle doit utiliser `/session/:id/message` directement. --- ## B. Scope, contraintes de temps, cleanup ### B1. Limite de temps et plan de bascule Si la forme de l'API OpenCode bloque, qu'est-ce qu'on fait ? Recommandation : **une seule passe d'implementation**. Si l'API OpenCode bloque, on arrete d'abstraire et on capture le contrat reel via curl avant de coder. Confirme. Une seule passe : avant de coder le client OpenCode, capturer le contrat reel par curl. On a deja valide `GET /global/health`, `POST /session`, `POST /session/:id/message`, `GET /session/:id/message` et `/tui/*`. ### B2. Rollback / cleanup post-PoC Comment on nettoie une fois la demo faite ? Recommandation : stop proxy, stop `opencode serve`, supprimer sessions de test, rotation du `PROXY_TOKEN` et du `OPENCODE_PASSWORD` utilises. Confirme. Stopper proxy + `opencode serve`, supprimer les sessions PoC si besoin, et rotation du `PROXY_TOKEN`/`OPENCODE_SERVER_PASSWORD`. Ne jamais garder les sorties `/provider` dans les logs partages. --- ## C. OpenCode upstream : contrat et comportement ### C1. Version OpenCode et Basic Auth verifies au premier run OpenCode evolue. Le plan suppose Basic Auth active. A valider. Recommandation : enregistrer `opencode --version` utilise lors de la validation ; pinger `/global/health` avec Basic Auth via curl avant de coder le client. Valide sur OpenCode `1.16.2`. `/global/health` retourne `{"healthy":true,"version":"1.16.2"}`. Basic Auth reste a retester avec `OPENCODE_SERVER_PASSWORD`; sans password, le serveur avertit qu'il est unsecured. ### C2. Spec exacte de `POST /session/:id/message` Plan : `{ parts: [{ type: "text", text }], model? }`. Marque comme inconnu. Recommandation : `curl http://localhost:4096/doc` une fois OpenCode lance, capturer un sample reel de reponse, le commiter sanitize dans `cc-agent-proxy/test/fixtures/`. Contrat valide : `POST /session/:id/message` body minimal `{"parts":[{"type":"text","text":"..."}]}`. `noReply:true` cree seulement le message user; sans `noReply`, OpenCode declenche le LLM et attend la reponse. ### C3. Provider et modele LLM `OPENCODE_MODEL` optionnel. Quel provider Guillaume utilise (anthropic, openai, ollama local) ? Recommandation : laisser defaut OpenCode, ne passer `model` que si `OPENCODE_MODEL` est defini dans `.env`. Confirme. Laisser le modele par defaut OpenCode pour le PoC. Ne passer `model:{providerID,modelID}` que si `OPENCODE_MODEL` est defini, car l'appel sans model marche deja avec la session courante. ### C4. Timeout amont vers OpenCode Recommandation : `REQUEST_TIMEOUT_MS` configurable, defaut 60s. Au dela -> 504 cote proxy. Confirme. `REQUEST_TIMEOUT_MS=60000` par defaut cote proxy. Le timeout doit englober l'appel bloquant `/session/:id/message`; 504 public si depassement. ### C5. Exposition des erreurs OpenCode au turtle Recommandation : pas de stack trace ni secret. Renvoyer `{ error: "...", code: "..." }` avec message public court ; detail dans les logs proxy uniquement. Confirme. Reponse publique courte, code stable, aucun detail provider/config. Attention particuliere : des endpoints comme `/provider` peuvent exposer du materiel d'auth, donc logs sanitize obligatoires. ### C6. SessionId auto-creation et recovery si stale Deux comportements distincts : - `sessionId` absent dans la requete -> proxy en cree un (recommande **oui**). - `sessionId` fourni mais 404 amont -> ? - **Option A** : recreer silencieusement. - **Option B** : renvoyer `{ error: "session_expired" }`, laisser la turtle decider (`opencc --new`). Recommandation : **Option B**. Auto-recreation cacherait une perte de contexte deroutante. Option B. Si `sessionId` absent, creer via `POST /session`. Si `sessionId` fourni renvoie 404/stale, repondre `session_expired`; la turtle pourra faire `opencc --new` plutot que perdre le contexte silencieusement. ### C7. Comportement quand OpenCode est down - 502/503 brutal ? - Retry interne avec backoff ? - `/health` reflete-t-il l'etat upstream ? Recommandation : pas de retry auto, **502** + JSON `{ error: "upstream_down" }`. `/health` inclut un champ `opencode: "up"/"down"`. Confirme. Pas de retry auto. `/health` proxy ping `/global/health`; si down, `{ proxy: "ok", opencode: "down" }`. `/ask` renvoie 502 `{ error: "upstream_down" }`. ### C8. Gestion des parts non-textuelles (tools, code blocks) Recommandation : concatener les parts texte, ignorer les non-textuelles dans la `reply`, mais logger un compteur cote proxy (`parts_text`, `parts_tool`, `parts_other`). Confirme. Les reponses reelles contiennent `text`, `reasoning`, `tool`, `step-start`, `step-finish`. La `reply` turtle concatene seulement les parts `text`; les compteurs de parts restent dans les logs proxy. --- ## D. Proxy API shape ### D1. Routes minimales du proxy Plan : `/health`, `/ask`, `/session`. - `/session` obligatoire ou implicite via `/ask` ? - `/health` inclut upstream ? Recommandation : - `GET /health` -> `{ proxy: "ok", opencode: "up"/"down" }`. - `POST /ask` -> obligatoire, body `{ prompt, sessionId? }`, response `{ sessionId, reply, truncated }`. - `POST /session` -> garder, retourne `{ sessionId }`. Pratique pour initialiser ou nommer une session ; ne casse pas le PoC si on ne l'expose pas a la turtle. Confirme. `/session` peut rester une route debug/initialisation, mais `/ask` doit creer implicitement si `sessionId` absent. `/health` inclut l'etat OpenCode via `/global/health`. ### D2. Codes HTTP stables pour la turtle Recommandation : - `200` succes - `400` requete invalide (prompt vide, body malforme) - `401` token absent/invalide - `413` prompt trop long - `502` OpenCode down - `504` OpenCode timeout - `500` erreur proxy inattendue Confirme ces codes. Ajouter `409` uniquement si un jour on gere des jobs async concurrents par session; hors PoC, la liste suffit. ### D3. Limites taille de requete - Max prompt length ? - Max body size HTTP ? Recommandation : prompt max **8 KiB** (`MAX_PROMPT_CHARS` env), body max **32 KiB** cote Fastify. Pas de rate-limit dans le PoC. Confirme. `MAX_PROMPT_CHARS=8192`, body JSON max 32 KiB, pas de rate-limit dans l'app. Ces limites protegent surtout CC/Turtle RAM et les logs. ### D4. Request IDs / correlation Recommandation : `requestId` auto-genere cote proxy, present dans tous les logs, non expose dans la reponse JSON (juste dans header `x-request-id` pour debug optionnel). Confirme. `requestId` genere cote proxy, logge partout, expose seulement via header `x-request-id` pour debug. Ne pas le mettre dans le JSON turtle par defaut. --- ## E. Stack Node / TypeScript ### E1. Runtime et gestionnaire de paquets Recommandation : **Node 20+**, **pnpm**. Sinon **npm** si tu prefers vanilla. Confirme : Node 20+ et pnpm. Si le repo n'a pas deja pnpm, garder npm possible, mais le proxy doit rester isole dans son sous-dossier. ### E2. Strictness TypeScript Recommandation : `"strict": true` + `noUncheckedIndexedAccess`. `exactOptionalPropertyTypes` evite (bruit pour peu de valeur en PoC). Confirme : `strict` + `noUncheckedIndexedAccess`, sans `exactOptionalPropertyTypes` pour limiter le bruit PoC. ### E3. Framework HTTP et validation Plan : Fastify + Zod. Recommandation : **Fastify** + **Zod** + `fastify-type-provider-zod` pour validation automatique des routes. Confirme ou propose autre. Confirme : Fastify + Zod. Le contrat proxy est petit et beneficie de schemas explicites pour `prompt`, `sessionId`, `reply`, `truncated`. ### E4. Logging Recommandation : **Pino** (default Fastify). `LOG_LEVEL=info` par defaut, `debug` en dev. Flag `LOG_BODIES=true` pour inclure prompt/reply ; **off par defaut**. Jamais logger token ni `OPENCODE_PASSWORD`. Logs : status, duree, `sessionId` (12 premiers chars), `requestId`, longueurs prompt/reply. Confirme. Pino/Fastify, `LOG_BODIES=false` par defaut. Ne pas logger les sorties brutes OpenCode sensibles (`/provider`, config, headers auth) ; loguer seulement longueurs, status, duree, requestId, prefix sessionId. ### E5. Process / lancement / deploiement - `tsx` en dev, `node dist/index.js` apres `tsc` en prod ? - Dockerfile ? systemd ? pm2 ? - Bind `0.0.0.0` ? Shutdown gracieux ? Recommandation : tsx en dev, node dist en prod, **pas de Docker**, pas de systemd dans le repo (tu deploies a la main). `0.0.0.0` car expose derriere RP. Shutdown : `app.close()` sur SIGTERM, rien de plus. Confirme. Dev avec `tsx`, prod `node dist/index.js`, pas de Docker/systemd dans le repo. Bind proxy `0.0.0.0` derriere RP, OpenCode en `127.0.0.1`. ### E6. Tests Node Plan : vitest, fetch mocke, `app.inject()`. Pas d'objectif de couverture %. Recommandation : couvrir `/ask` happy path, `401`, OpenCode 5xx mock, prompt vide, prompt trop long, sessionId 404 amont. Fixture OpenCode reelle (cf C2) utilisee dans `opencode.test.ts`. Confirme. Tests vitest avec fetch mocke + `app.inject()`. Ajouter fixture reelle sanitized de `/session/:id/message` incluant text/reasoning/tool/step parts. ### E7. Variables d'env (en plus du tableau du plan) A ajouter : - `LOG_LEVEL` (info/debug) - `LOG_BODIES` (true/false) - `MAX_REPLY_CHARS` (defaut 4096) - `MAX_PROMPT_CHARS` (defaut 8192) - `REQUEST_TIMEOUT_MS` (defaut 60000) - `OPENCODE_WORKSPACE` (info uniquement, documente le dir A2) Recommandation : oui, ajouter les six. `.env.example` les liste, jamais de secret reel. Confirme les six variables. Ajouter aussi un commentaire `.env.example` pour `OPENCODE_BASE_URL=http://127.0.0.1:4242` et credentials Basic Auth si actives. ### E8. CORS, body parsing, etat en memoire - CORS : non, ce n'est pas un navigateur. - Body : JSON only. - Etat : stateless (cf A3 Option 2). Recommandation : **garder tel quel**. Confirme ou contredit. Confirme. Pas de CORS, JSON only, proxy stateless. Les endpoints `/tui/*` ne sont pas utilises par le proxy turtle ; ils restent une piste d'integration TUI. --- ## F. Auth, secrets, exposition publique ### F1. Generation du `PROXY_TOKEN` Recommandation : genere a la main (`openssl rand -hex 32`), ecrit dans `.env`. Si absent au boot, le proxy refuse de demarrer (fail-fast). Confirme. Token genere manuellement, fail-fast si absent. Ne jamais le passer en query string ni l'afficher dans les logs. ### F2. HTTPS vs HTTP cote turtle ATM10 accepte les deux si dans `http.rules`. Token nu en HTTP = leak. Recommandation : **HTTPS uniquement**, meme en homelab, via le RP existant. Confirme : HTTPS uniquement entre turtle et proxy des que ca sort de localhost. OpenCode peut rester en HTTP loopback derriere le proxy. ### F3. Saisie du token sur la turtle Le secret ne doit jamais etre dans un fichier Lua commit. Recommandation : `settings set opencc.proxy_token ` + `settings.save()` manuellement dans l'environnement de chaque turtle. Documenter dans le quickstart. Confirme. Configuration manuelle via `settings`; rien de secret dans les fichiers Lua commit. Documenter commandes fish/bash separement cote host si besoin. ### F4. Token en query string ? Recommandation : **non**, header uniquement. Les query strings se retrouvent dans les logs RP/proxy. Confirme : non. Token en header seulement, jamais query string. ### F5. Headers acceptes : Bearer et X-Proxy-Token Plan : les deux pour simplicite CC. Recommandation : accepter `Authorization: Bearer ` ET `X-Proxy-Token`, documenter Bearer comme prefere. `X-Proxy-Token` reste seulement si CC pose souci avec `Authorization` (a tester). Confirme. Bearer prefere ; `X-Proxy-Token` accepte pour compat CC si `Authorization` pose souci, a valider en CraftOS-PC/ATM10. ### F6. Comparaison constant-time Recommandation : `crypto.timingSafeEqual` apres normalisation (longueur egalisee) pour eviter le throw sur taille differente. Confirme. `timingSafeEqual` avec buffers normalises pour eviter throw et timing trivial. ### F7. Rotation du token Recommandation : **hors scope PoC**. Redemarrer proxy + reconfigurer turtles = acceptable. Confirme. Rotation hors scope PoC : redemarrage proxy + reconfiguration turtle. ### F8. Rate-limit et allowlist IP Recommandation : ni l'un ni l'autre dans l'app. Si exposition publique le demande, c'est au RP / firewall de gerer (donc en dehors du repo). Confirme. Rate-limit/allowlist hors app, gere par RP/firewall si necessaire. ### F9. Basic Auth OpenCode Recommandation : credentials uniquement via env (`OPENCODE_USERNAME`, `OPENCODE_PASSWORD`), valides au boot, sinon fail-fast. Renommer cote proxy en `OPENCODE_SERVER_USERNAME`/`OPENCODE_SERVER_PASSWORD` ou documenter clairement le mapping, car OpenCode utilise ces noms pour proteger `serve`. Valider au boot par `/global/health` avec Basic Auth. --- ## G. Programme Lua `opencc` ### G1. Emplacement et packaging Plan : `apis/libopencc.lua`, `programs/opencc.lua`, `tests/opencc.lua`. Recommandation : pas de `packages/tos-agent/ccpm.json` avant validation in-game (cf I3). Fichiers nus dans `apis/` et `programs/` pendant le PoC. Confirme. Pas de packaging tant que l'in-game n'a pas tourne. Fichiers nus `apis/libopencc.lua`, `programs/opencc.lua`, `tests/opencc.lua` pour le PoC. ### G2. Source de config (settings vs fichier vs CLI) Clefs `settings` : - `opencc.proxy_url` (URL complete HTTPS recommandee) - `opencc.proxy_token` - `opencc.session_id` (auto-remplie) - `opencc.timeout` (optionnel) Recommandation : **`settings` uniquement** + flags CLI (`--url`, `--token`) qui overrident pour le debug. Si `proxy_url` ou `proxy_token` manque, message clair + exit non-zero. Pas de host/port separes. Confirme : `settings` uniquement + overrides CLI debug. `opencc.proxy_url`, `opencc.proxy_token`, `opencc.session_id`, `opencc.timeout` suffisent. ### G3. Format CLI - `opencc --help`, `--version` - `opencc "prompt"` (argv) ou interactif via `read()` si pas d'arg - REPL multi-tour ? Recommandation : pas de REPL. Un prompt par invocation. Le contexte vient de la `session_id` persistante. Confirme : pas de REPL. Un prompt par invocation ; le contexte vient du `session_id` persistant. ### G4. Persistance et reset de la session - Persistance : `settings.set('opencc.session_id', id); settings.save()` ou fichier dedie ? - Reset : `opencc --new` ? `--reset-session` ? Recommandation : **`settings`** (coherence G2) ; flag **`opencc --new`** qui efface `opencc.session_id` et cree une nouvelle session via le prochain `/ask`. Confirme. `opencc --new` efface `opencc.session_id`; le prochain `/ask` cree une session. Pas besoin d'appeler `/session` explicitement cote turtle. ### G5. Affichage de la reponse - Print brut ? - Word-wrap manuel ? - Pagination ? - Print metadata (sessionId, model) ? Recommandation : print + word-wrap simple sur largeur terminal. Pas de pagination. Pas de metadata sauf en mode `--verbose` (a ajouter plus tard). Confirme. Print avec word-wrap simple, pas de pagination. Metadata uniquement si `--verbose` est ajoute plus tard. ### G6. Erreurs reseau et `http.rules` `http.post` peut renvoyer `nil` (handshake ko, host bloque par `http.rules`) ou un response status != 200. Recommandation : - `nil` -> "proxy injoignable. Verifie `opencc.proxy_url` et que l'hote est autorise dans `http.rules`". - `401` -> "token invalide". - `413` -> "prompt trop long". - `502/504` -> "agent indisponible / trop lent". - Autre -> message generique + status code. Confirme ces messages. Ajouter un cas "session expiree" pour `session_expired` : "session perdue, relance avec `opencc --new`". ### G7. Timeout HTTP CC `http.post` n'a pas de parametre timeout standard. CC:Tweaked >= 1.97 a `http.request` avec timeout ; a verifier sur ATM10. Recommandation : exposer `opencc.timeout`. Si CC l'accepte, on l'utilise ; sinon documenter la limite et compter sur A4 Option 4 pour la suite. Confirme. Tester si ATM10/CC:Tweaked accepte un timeout via `http.request`; si non, documenter et garder sync court pour le PoC. ### G8. Pattern factory et JSON Repo : `apis/libccpm.lua` est une factory avec `opts.http` injectable. Meme chose pour `libopencc` ? Encodage JSON via `textutils.serialiseJSON` ? Recommandation : **factory** : `local createOpencc = require('/apis/libopencc'); local opencc = createOpencc({ http = http, settings = settings, computerCraftTimeout = ... })`. JSON via `textutils.serialiseJSON` / `unserialiseJSON`. Confirme. Factory injectable comme `libccpm.lua`; JSON via `textutils.serialiseJSON` et `textutils.unserialiseJSON`. ### G9. Trim / multiline / vide Recommandation : trim final, refus si prompt vide apres trim. Multiline preserve. Confirme. Trim final, refuser prompt vide apres trim, preserver multiline. ### G10. Tests Lua Plan : injection d'un faux `http` via `libtest`. Recommandation : `tests/opencc.lua` avec `libtest`, faux `http` qui verifie URL, headers (Bearer + Content-Type), body JSON, parsing succes, parsing erreur. Hors-scope : E2E reel avec vrai proxy. Confirme. Tests `libtest` avec faux `http`, headers, body JSON, success, erreurs proxy et sauvegarde `session_id`. --- ## H. Tests et validation ### H1. Fixture OpenCode capturee Recommandation : oui (cf C2). Sanitize, store dans `cc-agent-proxy/test/fixtures/session-message.json`. Sert de reference pour `opencode.test.ts`. Oui. Capturer une fixture sanitized de la reponse reelle OpenCode. Important : ne pas y inclure sortie `/provider` ni headers/auth. ### H2. E2E manuel vs automatise Recommandation : **manuel** pour le PoC. Documenter les commandes (cf H7). Confirme : E2E manuel pour le PoC. Les commandes de validation doivent etre documentees, avec versions fish si utilisees par Guillaume. ### H3. CraftOS-PC headless avant l'in-game Recommandation : oui si `just craftos --headless` peut faire `http.post`. Premier roundtrip en local avant ATM10. Oui. CraftOS-PC headless avant ATM10 si possible, mais sans ajouter de harness standalone Lua. Utiliser les recettes repo existantes. ### H4. Pre-requis ATM10 a documenter A documenter : - `http` enable cote serveur. - Hote du proxy autorise dans `http.rules` (allow `*.tondomaine.tld:443` ou IP). - DNS / IP joignable depuis le serveur Minecraft. - Token + URL definis dans les settings de la turtle. - Provider LLM authentifie cote OpenCode. Recommandation : checklist dans `docs/opencc-quickstart.md` ecrite **apres** la premiere validation reelle. Confirme. Ecrire la checklist apres premiere validation reelle, pas avant de figer des details faux. ### H5. Hooks `just check` et `just test` `pre-commit` = `just check test` (Lua + luacheck). Faut-il y mettre les tests Node ? Recommandation : **non**. Recipe separee `just node-check` (eslint+prettier) et `just node-test` (vitest) dans un sous-Justfile de `cc-agent-proxy/`. Eviter une dependance implicite Node pour les commits Lua. Confirme. Ne pas coupler Node aux hooks Lua existants pour le PoC ; recipes Node separees dans le sous-dossier proxy. ### H6. CI Repo : pas de CI publique declaree, juste hooks locaux. Recommandation : pas de CI pour le PoC. Plus tard, eventuellement un workflow GH Actions `node-test` + `lua-check`. Confirme. Pas de CI PoC. ### H7. Transcript de validation manuelle Recommandation : apres premier roundtrip reussi, enregistrer les commandes exactes et outputs sanitize dans `docs/opencc-quickstart.md`, incluant un deuxieme `/ask` avec le meme `sessionId` pour prouver la persistance. Confirme. Transcript manuel sanitize dans `docs/opencc-quickstart.md` apres roundtrip : `/health`, premier `/ask`, deuxieme `/ask` meme `sessionId`. --- ## I. Architecture / coherence repo ### I1. Reuse de `apis/net.lua` Recommandation : **non**. `net.lua` est modem/rednet. `http.post` direct cote Lua est le bon choix. Confirme. `apis/net.lua` est hors sujet pour HTTP externe. ### I2. Reuse de `apis/eventloop.lua` Recommandation : **non**. Le PoC est request/response synchrone. Re-evaluer si A4 passe en async (G1 post-PoC). Confirme. Pas d'eventloop Lua pour le PoC sync. ### I3. Packaging ccpm Recommandation : **post-PoC**. Apres validation in-game, creer `packages/tos-agent/ccpm.json` : - `files` : `apis/libopencc.lua`, `programs/opencc.lua` - `dependencies` : `tos-core` - `autostart` : aucun - Ajouter dans `packages/index.json`. Declencheur : PoC reussi + au moins une raison de reinstaller/partager. Confirme. Packaging seulement apres PoC in-game reussi et besoin de diffusion. ### I4. ADR Recommandation : ADR-0012 "external agent bridge via HTTPS proxy" ecrit apres validation. Justifie la presence d'un sous-dossier Node dans un repo Lua. Confirme. ADR apres validation, pour justifier proxy Node + pont agent externe. ### I5. Conformite CLAUDE.md - Bump `_VERSION` sur chaque module Lua modifie. - `--version` / `--help` sur `opencc`. - Lua : 2-space indent, `;`, `local function`. - Node : suit Prettier default. Recommandation : appliquer tel quel. Confirme. Respecter `_VERSION`, `--help`/`--version`, indent 2 spaces, semicolons Lua, `local function`. --- ## J. Post-PoC (evolutions, hors scope mais a ne pas peindre dans un coin) Reponds seulement si avis fort. ### J1. Mode async Trigger : reponses LLM frequemment > timeout CC/proxy. Recommandation : ajouter `POST /ask?async=1` + `GET /ask/:jobId/status`. Backward-compatible. `opencc` apprend a poll. Avis : garder ce design. Le test direct a confirme que sync fonctionne, mais `noReply:true` donne deja un equivalent "enqueue sans reponse" cote OpenCode si on doit construire un mode async plus tard. ### J2. SSE / WebSocket pour streaming Pas de SSE en CC:Tweaked, mais `http.websocket` existe. Recommandation : proxy WS dedie si streaming devient utile. Hors-scope PoC. Avis : ne pas confondre avec `/event` OpenCode. Le SSE existe et marche pour observer le serveur, mais CC ne le consomme pas bien. Pour piloter un TUI, les endpoints `/tui/append-prompt`, `/tui/submit-prompt`, `/tui/select-session` sont utiles, mais hors proxy turtle. ### J3. Multi-turtle isolation et identite Trigger : > 1 turtle/utilisateur sur le meme proxy. Recommandation : avec A3 Option 2 c'est deja gratuit niveau sessions. Pour quotas, ajouter `turtleId` (= `os.getComputerID()`) au body et a la table de tokens. Avis : confirmer. Ajouter `turtleId` plus tard seulement pour quotas/audit ; la vraie isolation conversationnelle reste le `sessionId` garde cote turtle. ### J4. Outils OpenCode (lecture/ecriture fichiers) Recommandation : sandbox stricte (mount lecture-seule + un dir scratch). Hors-scope PoC. Avis fort : sandbox obligatoire avant d'activer des outils capables d'ecrire. Le test `/tui/*` prouve qu'un client externe peut declencher le LLM ; le workspace OpenCode ne doit donc jamais etre un repo sensible pour le PoC. ### J5. Replacement de `http.post` par `apis/net.lua` Recommandation : probablement jamais. `net.lua` est routeur/rednet, pas sortie HTTP. Confirme : probablement jamais. HTTP direct est le bon chemin pour sortir vers le proxy ; `net.lua` reste pour modem/rednet. ### J6. Observabilite (metrics, traces) Recommandation : ignorer en PoC. Pino logs suffisent. Prometheus exporter plus tard si quotas. Confirme : Pino logs suffisent. Ajouter metrics seulement si plusieurs turtles ou quotas reels. --- ## K. Inconnues a valider au premier run Pas de reponse texte ; coche au fur et a mesure. - [x] Version exacte d'OpenCode utilisee (`opencode --version`) : `1.16.2`. - [x] Format reel des `parts` retournees par `POST /session/:id/message` : `text`, `reasoning`, `tool`, `step-start`, `step-finish` observes. - [x] Comportement OpenCode si `model` est passe vs absent : absent fonctionne avec le modele/session par defaut ; `noReply:true` cree sans LLM. - [ ] Basic Auth vraiment necessaire dans la config de Guillaume. - [ ] Timeout effectif `http.post` cote CC sur ATM10 (et si CC:Tweaked version supporte un parametre timeout). - [ ] Reaction OpenCode si on hammer une session morte (404 ? autre ?). - [ ] Limite de longueur d'un message accepte par OpenCode. - [ ] Cas ou le provider LLM est down : OpenCode renvoie quoi ? Ne pas tester via `/provider` en logs partages, cet endpoint peut exposer l'auth. - [ ] CraftOS-PC headless peut-il faire `http.post` HTTPS vers le proxy en local ? --- ## L. Quoi repondre en priorite Si tu veux aller vite, reponds dans cet ordre. Le reste decoule. 1. A1 - critere d'acceptation 2. A2 - workspace OpenCode (frontiere de confiance) 3. A3 - cycle session 4. A4 - sync vs async 5. A5 - topologie deploiement 6. A6 - auth turtle <-> proxy 7. A7 - forme de la reponse 8. A8 - scope v1 Une fois ces 8 trances, on peut souvent ignorer les sections B-J pour le "go/no-go" et juste valider les recommandations par defaut. Les detailler n'apporte de la valeur que si tu veux devier d'une recommandation precise.