mirror of
https://github.com/R0m1k3/Loki.git
synced 2026-10-16 11:49:43 +02:00
Quand la façade tient la conversation (LOKI_CHAT=rust), read, grep, glob, write, edit, bash et ask s'exécutent dans rust/loki-rs/src/tools au lieu de repartir au Go par /internal/v1/tool/exec : mêmes garde-fous dans le même ordre (commande catastrophique, « & » final en mode Code, bac à sable du mode Code, dossier mémoire interdit, lu avant d'écrire), même diff pour l'UI, même nettoyage de la sortie du shell. Le jeton de l'API interne est retiré de l'environnement du shell de l'agent. L'état partagé reste au Go, en un seul exemplaire (sous-agents et tâches le lisent aussi) : POST /internal/v1/tool/context (dossier de travail, dossier mémoire, cible distante), /tool/tracker (lecture notée, contrôle avant écriture), /tool/wrote (note après écriture, diagnostics LSP). Repli sur le Go pour une cible distante ou un contexte indisponible. Parité : internal/loki/parity_tools_test.go passe 58 appels à execTool sur une arborescence de test et range le journal dans tests/parity_tools ; tests/tools.rs le retrouve à l'identique avec exec_local. Écarts assumés consignés dans docs/superpowers/specs/2026-10-15-rust-migration-lot7-outils-design.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
75 lines
2.4 KiB
Go
75 lines
2.4 KiB
Go
package loki
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// Le contexte des outils dit à la façade où travaille l'agent, ce que le shell
|
|
// n'a pas le droit de toucher, et s'il vise un poste distant.
|
|
func TestOutilsInterneContexte(t *testing.T) {
|
|
testHome(t)
|
|
t.Setenv(internalTokenEnv, jetonTest)
|
|
w := appelOAIInterne(t, "POST", "/internal/v1/tool/context", `{}`)
|
|
if w.Code != 200 {
|
|
t.Fatalf("contexte : %d %s", w.Code, w.Body.String())
|
|
}
|
|
got := leaseJSON(t, w.Body.String())
|
|
if got["cwd"] != agentCwd() || got["memory_dir"] != memoryDir() || got["target"] != "" {
|
|
t.Fatalf("contexte : %v", got)
|
|
}
|
|
if w := appelOAIInterne(t, "GET", "/internal/v1/tool/context", ``); w.Code != 405 {
|
|
t.Fatalf("GET : %d", w.Code)
|
|
}
|
|
}
|
|
|
|
// Une lecture notée par la façade autorise l'écriture, exactement comme une
|
|
// lecture faite ici ; une écriture notée aussi.
|
|
func TestOutilsInterneTracker(t *testing.T) {
|
|
testHome(t)
|
|
t.Setenv(internalTokenEnv, jetonTest)
|
|
f := filepath.Join(t.TempDir(), "a.txt")
|
|
if err := os.WriteFile(f, []byte("x\n"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
check := func() string {
|
|
t.Helper()
|
|
w := appelOAIInterne(t, "POST", "/internal/v1/tool/tracker", `{"op":"check","path":`+jsonQuote(f)+`,"for_write":true}`)
|
|
if w.Code != 200 {
|
|
t.Fatalf("check : %d %s", w.Code, w.Body.String())
|
|
}
|
|
r, _ := leaseJSON(t, w.Body.String())["refusal"].(string)
|
|
return r
|
|
}
|
|
if r := check(); !strings.Contains(r, "pas lu") {
|
|
t.Fatalf("fichier jamais lu accepté : %q", r)
|
|
}
|
|
if w := appelOAIInterne(t, "POST", "/internal/v1/tool/tracker", `{"op":"read","path":`+jsonQuote(f)+`}`); w.Code != 200 {
|
|
t.Fatalf("read : %d", w.Code)
|
|
}
|
|
if r := check(); r != "" {
|
|
t.Fatalf("lecture notée mais refus : %q", r)
|
|
}
|
|
g := filepath.Join(filepath.Dir(f), "b.txt")
|
|
if err := os.WriteFile(g, []byte("y\n"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
w := appelOAIInterne(t, "POST", "/internal/v1/tool/wrote", `{"path":`+jsonQuote(g)+`,"caps":{"Code":true}}`)
|
|
if w.Code != 200 {
|
|
t.Fatalf("wrote : %d %s", w.Code, w.Body.String())
|
|
}
|
|
if trackerCheck(g, false) != "" {
|
|
t.Fatal("écriture notée mais edit refusé")
|
|
}
|
|
for _, body := range []string{"", "{", `{"op":"read"}`, `{"op":"x","path":"/a"}`} {
|
|
if w := appelOAIInterne(t, "POST", "/internal/v1/tool/tracker", body); w.Code != 400 {
|
|
t.Fatalf("%q : %d", body, w.Code)
|
|
}
|
|
}
|
|
if w := appelOAIInterne(t, "POST", "/internal/v1/tool/wrote", `{}`); w.Code != 400 {
|
|
t.Fatalf("wrote sans chemin : %d", w.Code)
|
|
}
|
|
}
|