fix: 统一 Shell 审批开关语义

- 关闭 Shell 审批时移除命令级审批策略与强制审批元数据

- 补充适配器与运行时回归测试
This commit is contained in:
2026-08-20 11:23:55 +08:00
parent c8be163124
commit 870c2cc583
3 changed files with 28 additions and 10 deletions

View File

@@ -119,7 +119,10 @@ public class AgentOperateToolAdapter {
toolkit.registerAgentTool(shellTool); toolkit.registerAgentTool(shellTool);
AgentToolSpec shellToolSpec = toolSpec( AgentToolSpec shellToolSpec = toolSpec(
spec, EXECUTE_SHELL_COMMAND_TOOL, "Execute shell command.", true); spec, EXECUTE_SHELL_COMMAND_TOOL, "Execute shell command.", true);
if (shellToolSpec.isApprovalRequired()) {
// 命令级审批策略服从 Agent 的 Shell 审批开关;关闭后仅保留安全校验。
shellToolSpec.setApprovalPolicy(shellTool::approvalEvaluation); shellToolSpec.setApprovalPolicy(shellTool::approvalEvaluation);
}
toolSpecs.add(shellToolSpec); toolSpecs.add(shellToolSpec);
} }
default -> throw new AgentRuntimeException("Unsupported agent operate tool type: " + type); default -> throw new AgentRuntimeException("Unsupported agent operate tool type: " + type);
@@ -157,7 +160,7 @@ public class AgentOperateToolAdapter {
toolSpec.setVisibility(AgentToolVisibility.VISIBLE); toolSpec.setVisibility(AgentToolVisibility.VISIBLE);
toolSpec.setApprovalRequired(approvalRequired); toolSpec.setApprovalRequired(approvalRequired);
toolSpec.setApprovalRequest(approvalRequest(operateSpec, approvalRequired)); toolSpec.setApprovalRequest(approvalRequest(operateSpec, approvalRequired));
toolSpec.setMetadata(metadata(operateSpec)); toolSpec.setMetadata(metadata(operateSpec, approvalRequired));
return toolSpec; return toolSpec;
} }
@@ -173,11 +176,11 @@ public class AgentOperateToolAdapter {
return defaultRequest; return defaultRequest;
} }
private Map<String, Object> metadata(AgentOperateToolSpec spec) { private Map<String, Object> metadata(AgentOperateToolSpec spec, boolean approvalRequired) {
Map<String, Object> metadata = new LinkedHashMap<>(); Map<String, Object> metadata = new LinkedHashMap<>();
metadata.put("operateTool", true); metadata.put("operateTool", true);
metadata.put("operateToolType", spec.getType().name()); metadata.put("operateToolType", spec.getType().name());
if (spec.getType() == AgentOperateToolType.SHELL) { if (spec.getType() == AgentOperateToolType.SHELL && approvalRequired) {
metadata.put("forceApprovalCommands", List.of("rm")); metadata.put("forceApprovalCommands", List.of("rm"));
metadata.put("forceApprovalCommandArgument", "command"); metadata.put("forceApprovalCommandArgument", "command");
} }

View File

@@ -282,13 +282,13 @@ public class AgentScopeStatefulRuntimeTest {
} }
@Test @Test
public void shouldForceApprovalForRemoveWhenShellApprovalIsDisabled() { public void shouldBypassRemoveApprovalWhenShellApprovalIsDisabled() {
AgentInitRequest request = initRequest(); AgentInitRequest request = initRequest();
AgentOperateToolSpec shell = operateToolSpec(AgentOperateToolType.SHELL); AgentOperateToolSpec shell = operateToolSpec(AgentOperateToolType.SHELL);
shell.setApprovalRequired(false); shell.setApprovalRequired(false);
request.getAgentDefinition().setOperateToolSpecs(List.of(shell)); request.getAgentDefinition().setOperateToolSpecs(List.of(shell));
AgentScopeReActRuntime runtime = runtimeWithModel(List.of(ChatResponse.builder() AgentScopeReActRuntime runtime = runtimeWithModel(List.of(ChatResponse.builder()
.id("forced-remove-message") .id("remove-message")
.content(List.of(ToolUseBlock.builder() .content(List.of(ToolUseBlock.builder()
.id("call-remove") .id("call-remove")
.name(AgentOperateToolAdapter.EXECUTE_SHELL_COMMAND_TOOL) .name(AgentOperateToolAdapter.EXECUTE_SHELL_COMMAND_TOOL)
@@ -303,11 +303,11 @@ public class AgentScopeStatefulRuntimeTest {
.block(Duration.ofSeconds(5)); .block(Duration.ofSeconds(5));
Assert.assertNotNull(events); Assert.assertNotNull(events);
Assert.assertTrue(events.stream()
.anyMatch(event -> event.getEventType() == AgentRuntimeEventType.TOOL_APPROVAL_REQUIRED));
Assert.assertTrue(events.stream()
.anyMatch(event -> event.getEventType() == AgentRuntimeEventType.SUSPENDED));
Assert.assertFalse(events.stream() Assert.assertFalse(events.stream()
.anyMatch(event -> event.getEventType() == AgentRuntimeEventType.TOOL_APPROVAL_REQUIRED));
Assert.assertFalse(events.stream()
.anyMatch(event -> event.getEventType() == AgentRuntimeEventType.SUSPENDED));
Assert.assertTrue(events.stream()
.anyMatch(event -> event.getEventType() == AgentRuntimeEventType.TOOL_RESULT)); .anyMatch(event -> event.getEventType() == AgentRuntimeEventType.TOOL_RESULT));
} }

View File

@@ -68,6 +68,21 @@ public class AgentOperateToolAdapterTest {
Assert.assertFalse(toolSpecs.get(0).getMetadata().containsKey("baseDir")); Assert.assertFalse(toolSpecs.get(0).getMetadata().containsKey("baseDir"));
} }
@Test
public void shouldDisableAllShellApprovalPoliciesWithAgentSwitch() {
Toolkit toolkit = new Toolkit();
AgentOperateToolSpec spec = spec(AgentOperateToolType.SHELL);
spec.setApprovalRequired(false);
List<AgentToolSpec> toolSpecs = adapter.register(List.of(spec), toolkit);
Assert.assertEquals(1, toolSpecs.size());
Assert.assertFalse(toolSpecs.get(0).isApprovalRequired());
Assert.assertNull(toolSpecs.get(0).getApprovalPolicy());
Assert.assertFalse(toolSpecs.get(0).getMetadata().containsKey("forceApprovalCommands"));
Assert.assertFalse(toolSpecs.get(0).getMetadata().containsKey("forceApprovalCommandArgument"));
}
@Test @Test
public void shouldRegisterPatchWithDefaultHitlDisabled() { public void shouldRegisterPatchWithDefaultHitlDisabled() {
Toolkit toolkit = new Toolkit(); Toolkit toolkit = new Toolkit();