This commit is contained in:
afjcjsbx 2026-03-27 23:09:05 +01:00
parent 7c0001cde2
commit 04337582b6
2 changed files with 68 additions and 8 deletions

View file

@ -536,13 +536,13 @@ func (t *ReadFileLinesTool) Execute(ctx context.Context, args map[string]any) *T
} }
limit := int64(-1) limit := int64(-1)
if raw, exists := args["limit"]; exists && raw != nil { if raw, exists := args["max_lines"]; exists && raw != nil {
limit, err = getInt64Arg(args, "limit", -1) limit, err = getInt64Arg(args, "max_lines", -1)
if err != nil { if err != nil {
return ErrorResult(err.Error()) return ErrorResult(err.Error())
} }
if limit <= 0 { if limit <= 0 {
return ErrorResult("limit, if provided, must be > 0") return ErrorResult("max_lines, if provided, must be > 0")
} }
} }
@ -630,7 +630,7 @@ func (t *ReadFileLinesTool) Execute(ctx context.Context, args map[string]any) *T
} }
if linesRead == 0 && content.Len() == 0 { if linesRead == 0 && content.Len() == 0 {
return NewToolResult("[END OF FILE - no content at this offset]") return NewToolResult(fmt.Sprintf("[END OF FILE - no content at or after start_line=%d]", startLine))
} }
start := startLine start := startLine

View file

@ -883,7 +883,7 @@ func TestReadFileLinesTool_ChunkedReading(t *testing.T) {
result1 := tool.Execute(context.Background(), map[string]any{ result1 := tool.Execute(context.Background(), map[string]any{
"path": testFile, "path": testFile,
"start_line": 1, "start_line": 1,
"limit": 2, "max_lines": 2,
}) })
if result1.IsError { if result1.IsError {
t.Fatalf("Chunk 1 failed: %s", result1.ForLLM) t.Fatalf("Chunk 1 failed: %s", result1.ForLLM)
@ -901,7 +901,7 @@ func TestReadFileLinesTool_ChunkedReading(t *testing.T) {
result2 := tool.Execute(context.Background(), map[string]any{ result2 := tool.Execute(context.Background(), map[string]any{
"path": testFile, "path": testFile,
"start_line": 3, "start_line": 3,
"limit": 2, "max_lines": 2,
}) })
if result2.IsError { if result2.IsError {
t.Fatalf("Chunk 2 failed: %s", result2.ForLLM) t.Fatalf("Chunk 2 failed: %s", result2.ForLLM)
@ -916,7 +916,7 @@ func TestReadFileLinesTool_ChunkedReading(t *testing.T) {
result3 := tool.Execute(context.Background(), map[string]any{ result3 := tool.Execute(context.Background(), map[string]any{
"path": testFile, "path": testFile,
"start_line": 5, "start_line": 5,
"limit": 2, "max_lines": 2,
}) })
if result3.IsError { if result3.IsError {
t.Fatalf("Chunk 3 failed: %s", result3.ForLLM) t.Fatalf("Chunk 3 failed: %s", result3.ForLLM)
@ -1000,11 +1000,71 @@ func TestReadFileLinesTool_OffsetBeyondEOF(t *testing.T) {
if result.IsError { if result.IsError {
t.Fatalf("unexpected error: %s", result.ForLLM) t.Fatalf("unexpected error: %s", result.ForLLM)
} }
if result.ForLLM != "[END OF FILE - no content at this offset]" { if result.ForLLM != "[END OF FILE - no content at or after start_line=100]" {
t.Fatalf("unexpected EOF message: %q", result.ForLLM) t.Fatalf("unexpected EOF message: %q", result.ForLLM)
} }
} }
func TestReadFileLinesTool_RegistryValidationSupportsMaxLinesAndRejectsLimit(t *testing.T) {
tmpDir := t.TempDir()
testFile := filepath.Join(tmpDir, "registry_lines.txt")
err := os.WriteFile(testFile, []byte("line 1\nline 2\nline 3\n"), 0o644)
if err != nil {
t.Fatalf("Failed to write test file: %v", err)
}
reg := NewToolRegistry()
reg.Register(NewReadFileLinesTool(tmpDir, false, MaxReadFileSize))
result := reg.Execute(context.Background(), "read_file", map[string]any{
"path": testFile,
"start_line": 1,
"max_lines": 1,
})
if result.IsError {
t.Fatalf("expected max_lines to pass registry validation, got: %s", result.ForLLM)
}
if !strings.Contains(result.ForLLM, "1|line 1\n") {
t.Fatalf("expected first line via max_lines, got: %s", result.ForLLM)
}
result = reg.Execute(context.Background(), "read_file", map[string]any{
"path": testFile,
"start_line": 2,
"limit": 1,
})
if !result.IsError {
t.Fatalf("expected limit to be rejected, got success: %s", result.ForLLM)
}
if !strings.Contains(result.ForLLM, "unexpected property \"limit\"") {
t.Fatalf("expected registry validation error for limit, got: %s", result.ForLLM)
}
}
func TestReadFileLinesTool_RejectsLegacyLimit(t *testing.T) {
tmpDir := t.TempDir()
testFile := filepath.Join(tmpDir, "legacy_limit.txt")
err := os.WriteFile(testFile, []byte("line 1\nline 2\n"), 0o644)
if err != nil {
t.Fatalf("Failed to write test file: %v", err)
}
tool := NewReadFileLinesTool(tmpDir, false, MaxReadFileSize)
result := tool.Execute(context.Background(), map[string]any{
"path": testFile,
"start_line": 1,
"limit": 1,
})
if !result.IsError {
t.Fatalf("expected limit to be rejected, got success: %s", result.ForLLM)
}
if !strings.Contains(result.ForLLM, "limit is no longer supported; use max_lines") {
t.Fatalf("unexpected error for legacy limit: %s", result.ForLLM)
}
}
func TestReadFileLinesTool_BinaryFileRejected(t *testing.T) { func TestReadFileLinesTool_BinaryFileRejected(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
testFile := filepath.Join(tmpDir, "binary.dat") testFile := filepath.Join(tmpDir, "binary.dat")