Analyzer::AI::Unified
Inherits Analyzer < FileHelper < Reference < Object
Unified AI analyzer that uses a provider-agnostic LLM adapter. Supports both OpenAI-compatible APIs and Ollama.
Constants
Every adapter maps a call it could not complete — an HTTP error the retries did not clear, a provider error body, a dead ACP agent, a reply that would not parse — to an empty string, and each one already prints its own WARNING naming the cause. The empty string is what reaches here, so this is the reason the analyzer can attach to the lost coverage.
Ceiling on simultaneous bundle requests, independent of a high
--concurrency: past a handful of in-flight calls a metered provider
answers with 429s rather than faster.
Bare URL tokens the LLM emits when it has nothing real to report
(schema echoes, "no endpoint" stand-ins). Compared case-folded
against the whole path with a single leading slash stripped, so a
legitimate nested route like /example/users is never rejected —
only a URL that IS one of these placeholders is dropped.
Constructors
Class methods
Instance methods
Containment gate for every agent file tool. The agent picks these paths from LLM output, and the LLM is steered by the source tree being scanned, so "stay inside the scan base" is a boundary an untrusted repo gets to push on.
File.expand_path is purely lexical — it folds .. and makes the path
absolute but never follows a link. A checked-out repo containing
notes -> /home/user/.ssh/id_rsa therefore passed as an in-base path,
and read_file shipped the target's contents to the LLM provider.
(The tree walk, the grep and the bundling path all skip symlinks
already; these two entry points were the gap.)
So resolve both sides and require containment of the real path. A
base that is itself reached through a link (/tmp -> /private/tmp on
macOS) still matches, because @real_base_paths went through the same
resolution. The lexical check stays as the first gate: it rejects the
plain ../../etc/passwd case without touching the filesystem.
Public rather than private so the rule can be asserted directly: reaching it through a live LLM round trip is not a test.
Instance-side view of the same declaration. The per-file rescues live on
this base class, which has no way to name the analyzer that is running
inside them, so a skipped file could not be attributed to a tech.
Deriving it from analyzer_for keeps the name written exactly once.