fix(toolintent): exempt list_dir from reference-exists + rejection-loop breaker

Greenfield discovery/analyst stages thrashed on plane-2 REFERENCE_EXISTS
rejections: the model listed a not-yet-created dir (e.g. frontend/), got
hard-BLOCKED every round, and burned MAX_TOOL_ROUNDS without progress.

- New ToolCapability.DIRECTORY_LIST; ListDirTool declares it alongside
  FILE_READ. ReferenceExistsRule now exempts directory enumeration (a
  listing of an absent dir truthfully reports empty; only hallucinated
  file *reads* still fail loud). Dispatch stays on capability, not name.
- Stage-agnostic rejection-loop breaker in the tool loop: after 3 rounds
  where every tool call is rejected (BLOCKED/ERROR) with no success,
  force the model to produce its output. Covers JSON-emitting stages the
  read-loop breaker (file_written-only) never protected.
This commit is contained in:
2026-07-07 14:09:03 +04:00
parent efb6eb0334
commit a8c496e909
5 changed files with 56 additions and 2 deletions
@@ -14,6 +14,14 @@ import kotlinx.serialization.Serializable
@Serializable
enum class ToolCapability {
FILE_READ,
/**
* Enumerates a directory rather than reading a file's contents. Carried alongside [FILE_READ]
* (a listing is a read-only operation) but tagged distinctly so anti-hallucination read gates
* can exempt it: listing a not-yet-created directory is a legitimate survey move (it truthfully
* reports "absent/empty"), not a hallucinated read that must be blocked.
*/
DIRECTORY_LIST,
FILE_WRITE,
NETWORK_ACCESS,
SHELL_EXEC,