Lot 47 : outil subagent du mode Code exécuté par la façade

Le builder délègue à un rôle en lecture seule (explorer, code-reviewer,
planner) sans passer par /tool/exec : src/chat/subagent.rs porte
toolSubagent. Mêmes refus avant tout appel au moteur (rôle inconnu,
verifier compris, tâche vide, récursion), contexte isolé servi par le Go
(POST /internal/v1/turn/subagent : prompt du rôle + briefing machine,
outils, capacités ; subagentView et subagentFraming partagés), tour
imbriqué run_turn avec l'endpoint, le raisonnement, l'écho et l'arrêt du
tour qui délègue, température 0,6, travail annexe (second slot, effacé à
la fin), télémétrie à part. Seul le texte écrit après le dernier outil
revient, rogné, préfixé du rôle, tronqué à 6000 runes.

Le tour du rôle est porté par SubHost, qui délègue tout à son GoTurnHost
sauf un nouvel appel à subagent ; les outils de la façade passent dans
GoTurnHost::exec_tool_base, que seul le tour principal complète de
subagent (le type du tour imbriqué ne se contient pas lui-même).

Parité : journal tiré du Go contre un faux moteur scripté
(parity_subagent_test.go → tests/parity_subagent/subagent.json : refus,
réponse directe, rapport après deux outils, texte vide relancé, rapport
tronqué, cadrage du planner, moteur qui refuse ; requêtes du rôle),
rejoué par un tour complet de la façade (tests/lot6.rs) ;
TestSubagentVueInterne côté Go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016tcGfJWefW5kUGoC2wL8cv
This commit is contained in:
Claude committed 2026-10-16 07:12:29 +00:00
1 parent 92e9adee7d
commit ded1c97c17
16 files changed
+1846 -73

No files matched your search

+29
View File
@@ -2,6 +2,7 @@ package loki
import (
"context"
"encoding/json"
"strings"
"testing"
)
@@ -54,3 +55,31 @@ func TestSubagentRolesAreReadOnly(t *testing.T) {
}
}
}
// Lot 47 : la façade demande au Go le contexte isolé d'un rôle délégué —
// le même prompt système, les mêmes outils et capacités que toolSubagent.
func TestSubagentVueInterne(t *testing.T) {
testHome(t)
t.Setenv(internalTokenEnv, jetonTest)
w := appelOAIInterne(t, "POST", "/internal/v1/turn/subagent", `{"role":"planner"}`)
if w.Code != 200 {
t.Fatalf("vue : %d %s", w.Code, w.Body.String())
}
var v struct {
System string `json:"system"`
Tools []Tool `json:"tools"`
Caps Caps `json:"caps"`
}
if err := json.Unmarshal(w.Body.Bytes(), &v); err != nil {
t.Fatal(err)
}
caps, system := subagentView("planner")
if v.System != system || v.Caps != caps || len(v.Tools) != len(EnabledTools(caps)) {
t.Fatalf("vue : %+v", v.Caps)
}
for _, body := range []string{`{"role":"verifier"}`, `{"role":""}`, `{`} {
if w := appelOAIInterne(t, "POST", "/internal/v1/turn/subagent", body); w.Code != 400 {
t.Fatalf("%s : %d", body, w.Code)
}
}
}