feat(0144c): launcher wiring + adapter al tool-use loop LLM
Schema DeviceMeshConfig en AgentConfig. Adapter ToolsForLLM convierte ToolSpec → tools.Tool transparente al LLM existente. URL via env var override. tools_allowed filter. agent-wsl-lucas blank import en launcher. LLM ve los tools como cualquier otra herramienta. Effects runner ya soporta ActionKindDeviceMesh como fallback. Build + tests verdes.
This commit is contained in:
@@ -171,3 +171,147 @@ func assertToolNotRegistered(t *testing.T, reg interface{ Names() []string }, na
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildToolRegistry_DeviceMeshDisabled(t *testing.T) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
|
||||
cfg := &config.AgentConfig{
|
||||
Agent: config.AgentMeta{ID: "test-agent"},
|
||||
DeviceMesh: nil,
|
||||
}
|
||||
roomCtx := &toolmemory.RoomContext{}
|
||||
|
||||
reg := buildToolRegistry(cfg, nil, nil, nil, nil, nil, nil, nil, nil, roomCtx, logger)
|
||||
|
||||
// None of the device_mesh tool names should appear when the block is nil.
|
||||
assertToolNotRegistered(t, reg, "exec")
|
||||
assertToolNotRegistered(t, reg, "shell.eval")
|
||||
assertToolNotRegistered(t, reg, "fs.read")
|
||||
}
|
||||
|
||||
func TestBuildDeviceMeshRegistry_NoURLReturnsNil(t *testing.T) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
|
||||
cfg := &config.AgentConfig{
|
||||
Agent: config.AgentMeta{ID: "agent-x"},
|
||||
DeviceMesh: &config.DeviceMeshConfig{
|
||||
Enabled: true,
|
||||
Mode: "user",
|
||||
// no URL, no URLEnv
|
||||
},
|
||||
}
|
||||
if got := buildDeviceMeshRegistry(cfg, logger); got != nil {
|
||||
t.Errorf("expected nil registry when no URL is set, got %d tools", got.Len())
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDeviceMeshRegistry_URLEnvOverride(t *testing.T) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
|
||||
t.Setenv("TEST_DM_URL", "http://10.42.0.99:7474")
|
||||
|
||||
cfg := &config.AgentConfig{
|
||||
Agent: config.AgentMeta{ID: "agent-x"},
|
||||
DeviceMesh: &config.DeviceMeshConfig{
|
||||
Enabled: true,
|
||||
Mode: "user",
|
||||
DeviceAgentURL: "http://stale-url",
|
||||
URLEnv: "TEST_DM_URL",
|
||||
},
|
||||
}
|
||||
reg := buildDeviceMeshRegistry(cfg, logger)
|
||||
if reg == nil {
|
||||
t.Fatalf("expected non-nil registry")
|
||||
}
|
||||
if reg.Client().BaseURL != "http://10.42.0.99:7474" {
|
||||
t.Errorf("URLEnv override failed: got %q", reg.Client().BaseURL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDeviceMeshRegistry_UserModeFiltersApproval(t *testing.T) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
|
||||
cfg := &config.AgentConfig{
|
||||
Agent: config.AgentMeta{ID: "agent-x"},
|
||||
DeviceMesh: &config.DeviceMeshConfig{
|
||||
Enabled: true,
|
||||
Mode: "user",
|
||||
DeviceAgentURL: "http://dummy:7474",
|
||||
},
|
||||
}
|
||||
reg := buildDeviceMeshRegistry(cfg, logger)
|
||||
if reg == nil {
|
||||
t.Fatalf("expected non-nil registry")
|
||||
}
|
||||
for _, n := range reg.Names() {
|
||||
// User mode: pkg.install (requires approval) must not be present.
|
||||
if n == "pkg.install" {
|
||||
t.Errorf("user mode leaked approval-only tool: %s", n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDeviceMeshRegistry_SudoModeKeepsOnlyApproval(t *testing.T) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
|
||||
cfg := &config.AgentConfig{
|
||||
Agent: config.AgentMeta{ID: "agent-x-sudo"},
|
||||
DeviceMesh: &config.DeviceMeshConfig{
|
||||
Enabled: true,
|
||||
Mode: "sudo",
|
||||
DeviceAgentURL: "http://dummy:7474",
|
||||
},
|
||||
}
|
||||
reg := buildDeviceMeshRegistry(cfg, logger)
|
||||
if reg == nil {
|
||||
t.Fatalf("expected non-nil registry")
|
||||
}
|
||||
// pkg.install MUST be there in sudo mode.
|
||||
assertToolRegistered(t, reg, "pkg.install")
|
||||
// shell.eval is always registered (special-cased) and promoted to approval.
|
||||
spec, ok := reg.Get("shell.eval")
|
||||
if !ok {
|
||||
t.Fatalf("shell.eval should be registered in sudo mode too")
|
||||
}
|
||||
if !spec.RequiresApproval {
|
||||
t.Errorf("shell.eval in sudo mode should have RequiresApproval=true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDeviceMeshRegistry_ToolsAllowedNarrows(t *testing.T) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
|
||||
cfg := &config.AgentConfig{
|
||||
Agent: config.AgentMeta{ID: "agent-x"},
|
||||
DeviceMesh: &config.DeviceMeshConfig{
|
||||
Enabled: true,
|
||||
Mode: "user",
|
||||
DeviceAgentURL: "http://dummy:7474",
|
||||
ToolsAllowed: []string{"exec", "fs.read", "zzz.unknown"},
|
||||
},
|
||||
}
|
||||
reg := buildDeviceMeshRegistry(cfg, logger)
|
||||
if reg == nil {
|
||||
t.Fatalf("expected non-nil registry")
|
||||
}
|
||||
if reg.Len() != 2 {
|
||||
t.Errorf("expected 2 tools after filter, got %d: %v", reg.Len(), reg.Names())
|
||||
}
|
||||
assertToolRegistered(t, reg, "exec")
|
||||
assertToolRegistered(t, reg, "fs.read")
|
||||
}
|
||||
|
||||
func TestBuildToolRegistry_DeviceMeshAdaptedIntoMainRegistry(t *testing.T) {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
|
||||
cfg := &config.AgentConfig{
|
||||
Agent: config.AgentMeta{ID: "agent-x"},
|
||||
DeviceMesh: &config.DeviceMeshConfig{
|
||||
Enabled: true,
|
||||
Mode: "user",
|
||||
DeviceAgentURL: "http://dummy:7474",
|
||||
ToolsAllowed: []string{"exec"},
|
||||
},
|
||||
}
|
||||
roomCtx := &toolmemory.RoomContext{}
|
||||
|
||||
reg := buildToolRegistry(cfg, nil, nil, nil, nil, nil, nil, nil, nil, roomCtx, logger)
|
||||
|
||||
// The "exec" tool should appear in the main agent tool registry, alongside
|
||||
// the always-on tools, ready for the LLM tool-use loop to invoke.
|
||||
assertToolRegistered(t, reg, "exec")
|
||||
assertToolRegistered(t, reg, "current_time")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user