vectr 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. agent/__init__.py +0 -0
  2. agent/cartographer.py +428 -0
  3. agent/chunk_quality.py +1119 -0
  4. agent/config.py +648 -0
  5. agent/config.yaml +1153 -0
  6. agent/eviction_advisor.py +447 -0
  7. agent/identifier_hint.py +68 -0
  8. agent/indexer/__init__.py +112 -0
  9. agent/indexer/_chunking.py +458 -0
  10. agent/indexer/_constants.py +105 -0
  11. agent/indexer/_core.py +878 -0
  12. agent/indexer/_types.py +157 -0
  13. agent/instance_registry.py +127 -0
  14. agent/llm_client.py +8 -0
  15. agent/model_cache.py +101 -0
  16. agent/prompt_templates.py +31 -0
  17. agent/searcher.py +848 -0
  18. agent/strategy_selector.py +299 -0
  19. agent/symbol_graph/__init__.py +131 -0
  20. agent/symbol_graph/_constants.py +366 -0
  21. agent/symbol_graph/_extraction.py +528 -0
  22. agent/symbol_graph/_graph.py +1878 -0
  23. agent/symbol_graph/_types.py +35 -0
  24. agent/templates/claude_md.md +65 -0
  25. agent/templates/claude_md_search_only.md +27 -0
  26. agent/templates/cursor_mcp.json.template +7 -0
  27. agent/templates/cursor_rules_header.txt +5 -0
  28. agent/templates/hook_no_double_recall.txt +1 -0
  29. agent/templates/mcp.json.template +8 -0
  30. agent/templates/session_start_guidance_default.txt +3 -0
  31. agent/templates/session_start_guidance_hooks_aware.txt +1 -0
  32. agent/templates/tool_loading_guidance_claude.txt +1 -0
  33. agent/templates/tool_loading_guidance_claude_search_only.txt +1 -0
  34. agent/templates/vscode_mcp.json.template +8 -0
  35. agent/tool_necessity_probe.py +177 -0
  36. agent/version_stamp.py +58 -0
  37. agent/watcher.py +515 -0
  38. agent/working_context_store/__init__.py +76 -0
  39. agent/working_context_store/_audit.py +46 -0
  40. agent/working_context_store/_encryption.py +75 -0
  41. agent/working_context_store/_store.py +1130 -0
  42. agent/working_context_store/_types.py +50 -0
  43. api.py +101 -0
  44. app/__init__.py +0 -0
  45. app/models.py +368 -0
  46. app/routes.py +451 -0
  47. app/service.py +1055 -0
  48. integrations/__init__.py +0 -0
  49. integrations/mcp_server/__init__.py +73 -0
  50. integrations/mcp_server/_dispatch.py +782 -0
  51. integrations/mcp_server/_schemas.py +484 -0
  52. integrations/mcp_server/_session.py +86 -0
  53. integrations/vscode_bridge.py +71 -0
  54. integrations/workspace_detect.py +259 -0
  55. main.py +2037 -0
  56. vectr-1.0.0.dist-info/METADATA +37 -0
  57. vectr-1.0.0.dist-info/RECORD +61 -0
  58. vectr-1.0.0.dist-info/WHEEL +5 -0
  59. vectr-1.0.0.dist-info/entry_points.txt +2 -0
  60. vectr-1.0.0.dist-info/licenses/LICENSE +21 -0
  61. vectr-1.0.0.dist-info/top_level.txt +5 -0
agent/config.py ADDED
@@ -0,0 +1,648 @@
1
+ """vectr bundled-config loader.
2
+
3
+ Reads ``agent/config.yaml`` at import time and caches the results. All paths
4
+ are resolved via ``importlib.resources`` so this works both when running from
5
+ the repository root *and* when vectr is installed as the global binary
6
+ (``/opt/homebrew/bin/vectr`` or similar pip install), where a ``cwd``-relative
7
+ ``open()`` would fail.
8
+
9
+ Public API
10
+ ----------
11
+ QUALITY_TRIVIAL : float
12
+ Quality prior for bare stub chunks (UPG-12.1).
13
+
14
+ QUALITY_NAVIGATIONAL : float
15
+ Quality prior for re-export / import-only navigational chunks (UPG-12.1).
16
+
17
+ QUALITY_NAV_DECLARATION_RESCUE : float
18
+ Softened quality prior for a bare-constructor-manifest navigational chunk
19
+ when the query lexically names one of the identifiers it declares
20
+ (UPG-NAV-OVERDEMOTE-DECL / F59). Applied instead of QUALITY_NAVIGATIONAL
21
+ only for that lexically-gated case; every other query still gets the full
22
+ navigational demotion.
23
+
24
+ QUALITY_HEADING_ONLY : float
25
+ Quality prior for markdown heading-only chunks (UPG-12.1).
26
+
27
+ QUALITY_GENERATED : float
28
+ Quality prior for machine-generated files (UPG-12.1).
29
+
30
+ QUALITY_VECTR_CONFIG : float
31
+ Quality prior for vectr's own config files (UPG-12.1).
32
+
33
+ QUALITY_TEST_DEPRIORITISED : float
34
+ Quality prior for test files (UPG-12.1).
35
+
36
+ TEST_FRAMEWORK_FAN_IN_THRESHOLD : int
37
+ Minimum unambiguous corpus-wide caller-file count (symbol_graph.file_fan_in())
38
+ above which a test-path-classified file is exempted from
39
+ QUALITY_TEST_DEPRIORITISED (UPG-TESTPATH-FRAMEWORK-MISCLASS / F58) — it is a
40
+ shipped testing-framework subpackage, not disposable test code.
41
+
42
+ QUALITY_DOC_PROSE : float
43
+ Quality prior for documentation prose chunks (UPG-12.1).
44
+
45
+ QUALITY_SHORT_PENALTY : float
46
+ Quality prior for chunks with very few meaningful lines (UPG-12.1).
47
+
48
+ QUALITY_PRIVATE_SYMBOL : float
49
+ Quality prior for a private/internal symbol (single leading underscore,
50
+ not a dunder) — a language-general naming convention, not corpus- or
51
+ query-specific (UPG-16.1 / F30).
52
+
53
+ TRIVIAL_DOC_MAX_LINES : int
54
+ Maximum non-blank lines for an HTML/markup or plain-text chunk to be
55
+ classified as trivial by is_trivial_chunk() (UPG-15.5). 1–2-line test
56
+ fixture templates and egg-info TXT files are trivial; multi-line .rst/.txt
57
+ docs are not affected.
58
+
59
+ RERANK_TOP_K : int
60
+ Number of hybrid candidates to rerank before trimming to n_results (UPG-12.1).
61
+
62
+ RERANK_TOP_K_UNFILTERED : int
63
+ Deeper candidate pool for unfiltered (no language filter) queries (UPG-12.1).
64
+
65
+ RERANK_PRE_FILTER_FETCH_K : int
66
+ Over-fetch depth for the pool-entry trivial filter (UPG-15.7). The hybrid
67
+ retrieval fetches this many raw candidates, drops trivial (non-forced) chunks
68
+ via is_trivial_chunk(), then trims to top_k_unfiltered before the cross-encoder
69
+ runs. Ensures the rerank pool is filled with real code on fixture-heavy corpora.
70
+
71
+ INDEXING_MAX_CHUNK_LINES : int
72
+ Hard cap on lines per chunk — prevents single huge chunks diluting embeddings (UPG-12.1).
73
+
74
+ INDEXING_CLASS_HEADER_LINES : int
75
+ Lines kept for class-level chunk (sig + docstring + attrs) (UPG-12.1).
76
+
77
+ OUTPUT_SNIPPET_LINES : int
78
+ Lines returned as a snippet with each symbol location (UPG-12.1).
79
+
80
+ BEHAVIOR_REMEMBER_NUDGE_THRESHOLD : int
81
+ Tool calls without vectr_remember before a nudge fires (UPG-12.1).
82
+
83
+ BEHAVIOR_REMEMBER_NUDGE_COOLDOWN : int
84
+ Calls between repeated nudges after the threshold first fires (UPG-12.1).
85
+
86
+ EVICTION_RETRIEVED_TOKEN_GATE : int
87
+ Minimum accumulated retrieved-token estimate since the last auto-eviction hint
88
+ before auto_eviction_hint() will emit. Suppresses the hint on bursts of tiny
89
+ searches that contribute negligible context pressure (UPG-11.15).
90
+
91
+ EVICTION_HINT_MAX_IDS : int
92
+ Maximum chunk ids listed as re-fetch keys in eviction_hint()'s render
93
+ (UPG-EVICT-SESSION-SCOPE).
94
+
95
+ EVICTION_MAX_TRACKED_SESSIONS : int
96
+ Maximum concurrent per-session EvictionAdvisor instances tracked by a
97
+ daemon before the oldest is dropped (LRU) (UPG-EVICT-SESSION-SCOPE).
98
+
99
+ EVICTION_REMEMBER_ESCALATION_CHUNKS : int
100
+ Chunks retrieved since the caller's last vectr_remember before
101
+ auto_eviction_hint()'s escalated ACTION REQUIRED directive fires again
102
+ (UPG-REMEMBER-BANNER-FATIGUE).
103
+
104
+ EVICTION_REMEMBER_ESCALATION_TOKENS : int
105
+ Tokens retrieved since the caller's last vectr_remember required IN ADDITION
106
+ to EVICTION_REMEMBER_ESCALATION_CHUNKS before the escalated directive fires
107
+ again — a companion gate so one large single search cannot trip both the
108
+ chunk-count and token gates in a single burst (UPG-EVICT-ESCALATION-GATE-TOO-LOW).
109
+
110
+ BOOT_MAX_DIRECTIVE_NOTES : int
111
+ Maximum directive notes returned by boot_recall() (UPG-9.2). Directives
112
+ are ordered oldest-first (standing rules stay in a stable order).
113
+
114
+ BOOT_MAX_TASK_NOTES : int
115
+ Maximum high-priority task notes returned by boot_recall() (UPG-9.2),
116
+ ordered newest-first (UPG-TASK-NOTE-INJECTION-RECENCY): task notes are
117
+ current-work state, so the boot set surfaces only the freshest
118
+ checkpoints rather than the full task history.
119
+
120
+ INGEST_TRACES_MAX_UNRESOLVED_EXAMPLES : int
121
+ Maximum unresolved caller/callee example strings surfaced in the
122
+ vectr_ingest_traces response (UPG-7.3). Edges are ingested regardless;
123
+ this only bounds the warning text length.
124
+
125
+ SYMBOL_NAME_PARAM_ALIASES : tuple[str, ...]
126
+ Alternate argument keys accepted in place of "name" on vectr_locate and
127
+ vectr_trace (F40-class ergonomics). A tool-description example that reads
128
+ positional trains callers to guess a wrong key; the dispatch layer accepts
129
+ these as drop-in aliases so the call succeeds instead of erroring.
130
+
131
+ LOCATE_LARGE_SPAN_THRESHOLD : int
132
+ Line-span (end_line - start_line) at or above which a located symbol is
133
+ considered "large" — typically a canonical library class or function (UPG-15.10).
134
+ Symbols with span >= this value get the best (lowest) span bucket in locate
135
+ ranking, so canonical 1000+ line base classes rank before tiny test stubs.
136
+
137
+ LOCATE_SMALL_SPAN_THRESHOLD : int
138
+ Line-span below which a located symbol is considered "tiny" — a stub or inner
139
+ test class (UPG-15.10). Symbols with span < this value get the worst (highest)
140
+ span bucket in locate ranking, penalising 2–5-line test-inner stub classes.
141
+
142
+ IMPORTANCE_PRIOR_LAMBDA : float
143
+ Blend weight for the file-level PageRank importance prior in the final search
144
+ sort (ARCH-1b): final_score = base_rerank_score * quality_score * (1 + lambda *
145
+ importance). 0 disables (pre-ARCH-1b behaviour). Relevance-gated by the multiply
146
+ against base_rerank_score.
147
+
148
+ CLASS_IMPORTANCE_PRIOR_LAMBDA : float
149
+ Blend weight for the class-level reference-frequency importance prior in the
150
+ final search sort (ARCH-2), composed with IMPORTANCE_PRIOR_LAMBDA:
151
+ final_score = base_rerank_score * quality_score * (1 + lambda_file * file_imp)
152
+ * (1 + lambda_class * class_imp). 0 disables (pre-ARCH-2 behaviour). The lever
153
+ that discriminates same-leaf method collisions file-level importance cannot.
154
+
155
+ PURPOSE_RANK_PRIOR_LAMBDA : float
156
+ Blend weight for the ARCH-4 dual-vector purpose-similarity prior in the final
157
+ search sort (ARCH-4b), composed with the two priors above: final_score =
158
+ base_rerank_score * quality_score * (1 + lambda_file * file_imp) * (1 +
159
+ lambda_class * class_imp) * (1 + lambda_purpose * purpose_sim). purpose_sim is
160
+ the chunk's own body-vs-purpose cosine similarity (0 when no purpose vector
161
+ exists for the chunk). 0 disables (pre-ARCH-4b behaviour). Carries the ARCH-4
162
+ pool-entry signal into the final rank, which the body-only cross-encoder
163
+ rerank cannot see.
164
+
165
+ TYPE_DEF_PRIOR_LAMBDA : float
166
+ Blend weight for the type-definition node_type prior in the final search
167
+ sort (UPG-RUST-DEF-EVICTION / DEF-B), composed with the three priors
168
+ above: final_score = ... * (1 + lambda_purpose * purpose_sim *
169
+ quality_score) * (1 + lambda_def * is_type_definition). is_type_definition
170
+ is 1 when the chunk's own node_type is a struct/enum/trait/class/interface
171
+ definition (chunk_quality.is_type_definition_chunk), else 0. 0 disables
172
+ (pre-DEF-B behaviour). The lever that keeps a canonical type definition
173
+ from losing to a same-name test/usage site at comparable rerank score.
174
+
175
+ DOCSTRING_DEDUP_LINES : int
176
+ Leading docstring/comment lines compared when computing the near-duplicate
177
+ docstring dedup key (UPG-RUST-DEF-EVICTION / DEF-C).
178
+
179
+ DOCSTRING_DEDUP_MIN_CHARS : int
180
+ Minimum normalized leading-docstring length (chars) below which a chunk is
181
+ never folded by the docstring dedup key — chunks with a trivial or absent
182
+ leading header keep every occurrence (UPG-RUST-DEF-EVICTION / DEF-C).
183
+
184
+ INDEXING_FLOW_SCAN_HEAD_BYTES : int
185
+ Bytes scanned from the start of a `.js` file when detecting Flow type syntax
186
+ (UPG-JSFLOW-SYMBOLS). A header scan, not a full-file walk.
187
+
188
+ INDEXING_FLOW_PRAGMA : str
189
+ Primary Flow-detection signal — the `@flow` pragma (UPG-JSFLOW-SYMBOLS).
190
+
191
+ INDEXING_FLOW_SECONDARY_MARKERS : tuple[str, ...]
192
+ Secondary Flow-detection signals — Flow-only import syntax (UPG-JSFLOW-SYMBOLS).
193
+
194
+ SYMBOL_GRAPH_RESERVED_KEYWORDS : dict[str, frozenset[str]]
195
+ Per-language keyword sets that must never be minted as a symbol name or
196
+ call-edge target — guards against a desynced/ERROR-node parse misattributing
197
+ a keyword token as an identifier (UPG-JSFLOW-SYMBOLS).
198
+
199
+ SYMBOL_GRAPH_ERROR_RECOVERY_MIN_SPAN_LINES : int
200
+ Minimum line-span an opaque errored (non-symbol-type) node must cover before
201
+ an isolated reparse-recovery attempt is made on its byte range
202
+ (UPG-REACT-TSX-FUNCTION-DECL-DROP).
203
+
204
+ SYMBOL_GRAPH_ERROR_RECOVERY_MAX_REPARSE_ATTEMPTS : int
205
+ Reparse-recovery attempts budget per file, bounding worst-case parse cost
206
+ (UPG-REACT-TSX-FUNCTION-DECL-DROP).
207
+
208
+ SYMBOL_GRAPH_ERROR_RECOVERY_MAX_EXTEND_STEPS_PER_ATTEMPT : int
209
+ Per-attempt cap on sibling-absorption steps while growing a reparse past
210
+ a mid-declaration cut, so one badly-cut region can't burn the whole
211
+ per-file reparse budget (UPG-REACT-TSX-FUNCTION-DECL-DROP).
212
+
213
+ WORKSPACE_DEFAULT_VECTRIGNORE_DIRS : tuple[str, ...]
214
+ Directory names seeded into a fresh .vectrignore on first `vectr start`/`vectr
215
+ init` when the workspace has none yet (UPG-13.2). Never overwrites an existing
216
+ .vectrignore.
217
+
218
+ WATCHER_TOP_LEVEL_RESCAN_INTERVAL_S : float
219
+ Seconds between CodeWatcher's shallow top-level-only rescans, used to pick up
220
+ new top-level directories/files since the watcher never watches the workspace
221
+ root itself (UPG-13.1/13.3).
222
+
223
+ WATCHER_BURST_FILES_THRESHOLD : int
224
+ Distinct paths pending per-file debounce simultaneously above which
225
+ CodeWatcher cancels every per-file timer and collapses into one deferred
226
+ batch re-index (UPG-WATCHER-PRESSURE-GOVERNOR) — bounds embed-pipeline
227
+ load under a sustained multi-file edit stream.
228
+
229
+ WATCHER_BURST_QUIET_SECONDS : float
230
+ Seconds of repo-wide silence required, once burst coalescing has started,
231
+ before the collapsed batch actually runs (UPG-WATCHER-PRESSURE-GOVERNOR).
232
+
233
+ WATCHER_MAX_RSS_MB : float
234
+ Self-limit (MB) on this process's own peak resident set size above which
235
+ a watcher-triggered batch re-index is deferred to the next quiet window
236
+ instead of run (UPG-WATCHER-PRESSURE-GOVERNOR). 0 disables the check.
237
+
238
+ HOOKS_LOG_INJECTIONS : bool
239
+ When true, every hook-driven recall that actually injects notes appends
240
+ one line to ~/.vectr/logs/<workspace-hash>.hooks.log (UPG-HOOK-INJECT-
241
+ OBSERVABILITY). Off by default — the per-hook-kind counters in `vectr
242
+ status` cover the common case without writing to disk.
243
+
244
+ HOOKS_LOG_CHARS_PER_TOKEN : int
245
+ Divisor for the approximate token count written to the optional hook
246
+ injection log above (UPG-HOOK-INJECT-OBSERVABILITY).
247
+
248
+ STRATEGY_DEFAULT_SEMANTIC_WEIGHT : float
249
+ STRATEGY_DEFAULT_BM25_WEIGHT : float
250
+ Fallback hybrid-search weights used before the first index-time codebase
251
+ fingerprint has run (UPG-8.2). Keeps `search` and `status` deterministic
252
+ from the first call instead of the weight fields being silently absent.
253
+ DUAL_VECTOR_ENABLED : bool
254
+ Master switch for the ARCH-4 per-symbol purpose-vector pool-entry mechanism.
255
+ True stores + queries a second body-stripped "purpose" embedding (qualified
256
+ signature + docstring) per symbol chunk. False reduces to pre-ARCH-4
257
+ body-only behaviour.
258
+
259
+ DUAL_VECTOR_BLEND_MODE : str
260
+ How a chunk's body and purpose similarity scores combine into one dense
261
+ score: "max" (default, non-averaging pool-entry rescue) or "weighted".
262
+
263
+ DUAL_VECTOR_BLEND_WEIGHT : float
264
+ Purpose-vector weight when DUAL_VECTOR_BLEND_MODE == "weighted".
265
+
266
+ DUAL_VECTOR_MAX_SIGNATURE_LINES : int
267
+ Maximum declaration lines captured when distilling a chunk's purpose text.
268
+
269
+ DUAL_VECTOR_MAX_DOCSTRING_LINES : int
270
+ Maximum docstring/leading-comment lines captured when distilling a chunk's
271
+ purpose text.
272
+
273
+ DUAL_VECTOR_MAX_DOCSTRING_CHARS : int
274
+ Maximum characters kept from the captured docstring/leading-comment text.
275
+
276
+ NOTFOUND_FLOOR_ENABLED : bool
277
+ Master switch for the UPG-NOTFOUND-FLOOR low-confidence signal (F46).
278
+ False is an exact no-op: low_confidence is always False, restoring
279
+ pre-UPG-NOTFOUND-FLOOR behaviour.
280
+
281
+ NOTFOUND_FLOOR_MIN_TOKEN_LEN : int
282
+ Minimum length (chars) for a query token to be considered a content word
283
+ in the zero-document-frequency check (UPG-NOTFOUND-FLOOR-2).
284
+
285
+ NOTFOUND_FLOOR_STOPWORDS : frozenset[str]
286
+ Generic English/query-scaffolding words excluded from the zero-document-
287
+ frequency check.
288
+
289
+ NOTFOUND_FLOOR_MIN_ZERO_DF_TOKENS : int
290
+ Minimum number of query content tokens with zero corpus-wide document
291
+ frequency (i.e. the token never appears in ANY indexed chunk, not just
292
+ the query's fetched candidate pool) required to flag a search's result
293
+ set low_confidence. Replaces the absolute-cosine floor (dense_score_floor)
294
+ of the first UPG-NOTFOUND-FLOOR iteration, which measurement showed
295
+ cannot separate absent-topic from on-topic queries against the
296
+ production embedder — see config.yaml for the evidence.
297
+
298
+ NOTFOUND_FLOOR_MIN_TOP_RELEVANCE : float
299
+ Absolute floor (UPG-SCORE-DISPLAY-FLAT) on the top result's cross-encoder
300
+ relevance score (ce_relevance): below this, low_confidence is flagged
301
+ even if the zero-document-frequency check above doesn't trip. Never
302
+ applied to the raw dense-cosine fallback score — see config.yaml for why.
303
+
304
+ NOTFOUND_FLOOR_BANNER : str
305
+ Low-confidence banner text prepended to the MCP vectr_search response
306
+ when the floor fires.
307
+
308
+ NOTFOUND_FLOOR_BANNER_CLI : str
309
+ Low-confidence banner text printed by `vectr search` (CLI surface) when
310
+ the floor fires (UPG-CLI-SEARCH-FLOOR). Separate from the MCP banner
311
+ above because that text names `vectr_locate`, meaningless at a shell
312
+ prompt — no `vectr locate` subcommand exists.
313
+
314
+ EMBEDDING_DEFAULT_MODEL : str
315
+ Default local (sentence-transformers) embedding model for the L3 content
316
+ index (UPG-EMBEDDER-SWAP-GRANITE). A workspace's ChromaDB collection is
317
+ stamped with whichever model built it (CodeIndexer's embed-model stamp);
318
+ changing this value forces a full vector index rebuild on next index
319
+ rather than mixing vectors from two models in one collection.
320
+
321
+ FETCH_MAX_IDS_PER_CALL : int
322
+ Maximum chunk ids accepted per vectr_fetch / POST /v1/fetch / `vectr
323
+ fetch` call (UPG-CTX-EVICT). Bounds the deterministic re-fetch-by-id
324
+ surface so it can't be used as an unbounded bulk export of the index.
325
+ """
326
+ from __future__ import annotations
327
+
328
+ import importlib.resources as _ilr
329
+ from typing import Any
330
+
331
+ import yaml as _yaml
332
+
333
+
334
+ # ---------------------------------------------------------------------------
335
+ # Internal helpers
336
+ # ---------------------------------------------------------------------------
337
+
338
+ def _read_bundled_text(resource_path: str) -> str:
339
+ """Read a text file bundled inside the ``agent`` package.
340
+
341
+ ``resource_path`` is relative to the ``agent`` package root, using forward
342
+ slashes (e.g. ``"config.yaml"``). We use
343
+ ``importlib.resources.files()`` (Python 3.9+) so the file is found whether
344
+ the package is on-disk or inside a zip/wheel.
345
+ """
346
+ parts = resource_path.split("/")
347
+ # Start from the agent package anchor.
348
+ pkg = _ilr.files(__name__.rsplit(".", 1)[0] if "." in __name__ else __name__)
349
+ resource = pkg
350
+ for part in parts:
351
+ resource = resource.joinpath(part) # type: ignore[arg-type]
352
+ return resource.read_text(encoding="utf-8")
353
+
354
+
355
+ def _load_config() -> dict[str, Any]:
356
+ """Load and return the parsed agent/config.yaml as a dict."""
357
+ raw = _read_bundled_text("config.yaml")
358
+ return _yaml.safe_load(raw) # type: ignore[no-any-return]
359
+
360
+
361
+ # ---------------------------------------------------------------------------
362
+ # Eagerly load once at import; cache in module-level constants.
363
+ # ---------------------------------------------------------------------------
364
+
365
+ _cfg = _load_config()
366
+
367
+ # ---------------------------------------------------------------------------
368
+ # Quality priors — ranking multipliers (UPG-12.1)
369
+ # ---------------------------------------------------------------------------
370
+
371
+ _qp_cfg: dict[str, Any] = _cfg["ranking"]["quality_priors"]
372
+
373
+ QUALITY_TRIVIAL: float = float(_qp_cfg["trivial"])
374
+ QUALITY_NAVIGATIONAL: float = float(_qp_cfg["navigational"])
375
+ QUALITY_NAV_DECLARATION_RESCUE: float = float(_qp_cfg["navigational_declaration_rescue"])
376
+ QUALITY_HEADING_ONLY: float = float(_qp_cfg["heading_only"])
377
+ QUALITY_GENERATED: float = float(_qp_cfg["generated"])
378
+ QUALITY_VECTR_CONFIG: float = float(_qp_cfg["vectr_config"])
379
+ QUALITY_TEST_DEPRIORITISED: float = float(_qp_cfg["test_deprioritised"])
380
+ TEST_FRAMEWORK_FAN_IN_THRESHOLD: int = int(_qp_cfg["test_framework_fan_in_threshold"])
381
+ QUALITY_DOC_PROSE: float = float(_qp_cfg["doc_prose"])
382
+ QUALITY_SHORT_PENALTY: float = float(_qp_cfg["short_penalty"])
383
+ QUALITY_PRIVATE_SYMBOL: float = float(_qp_cfg["private_symbol_deprioritised"])
384
+ TRIVIAL_DOC_MAX_LINES: int = int(_qp_cfg["trivial_doc_max_lines"])
385
+ TRIVIAL_ATTR_CLASS_MAX_ATTRS: int = int(_qp_cfg["trivial_attr_class_max_attrs"])
386
+
387
+ # ---------------------------------------------------------------------------
388
+ # Locate ranking tunables (UPG-15.10)
389
+ # ---------------------------------------------------------------------------
390
+
391
+ _lr_cfg: dict[str, Any] = _cfg["ranking"]["locate_ranking"]
392
+
393
+ LOCATE_LARGE_SPAN_THRESHOLD: int = int(_lr_cfg["large_span_threshold"])
394
+ LOCATE_SMALL_SPAN_THRESHOLD: int = int(_lr_cfg["small_span_threshold"])
395
+
396
+ # ---------------------------------------------------------------------------
397
+ # Importance prior — file-level PageRank blend (ARCH-1b)
398
+ # ---------------------------------------------------------------------------
399
+
400
+ _imp_cfg: dict[str, Any] = _cfg["ranking"]["importance_prior"]
401
+
402
+ IMPORTANCE_PRIOR_LAMBDA: float = float(_imp_cfg["lambda"])
403
+
404
+ # ---------------------------------------------------------------------------
405
+ # Class importance prior — class-level reference-frequency blend (ARCH-2)
406
+ # ---------------------------------------------------------------------------
407
+
408
+ _cimp_cfg: dict[str, Any] = _cfg["ranking"]["class_importance"]
409
+
410
+ CLASS_IMPORTANCE_PRIOR_LAMBDA: float = float(_cimp_cfg["lambda"])
411
+
412
+ # ---------------------------------------------------------------------------
413
+ # Purpose-rank prior — dual-vector purpose-similarity blend into final sort (ARCH-4b)
414
+ # ---------------------------------------------------------------------------
415
+
416
+ _prp_cfg: dict[str, Any] = _cfg["ranking"]["purpose_rank"]
417
+
418
+ PURPOSE_RANK_PRIOR_LAMBDA: float = float(_prp_cfg["lambda"])
419
+
420
+ # ---------------------------------------------------------------------------
421
+ # Type-definition node_type prior (UPG-RUST-DEF-EVICTION / DEF-B)
422
+ # ---------------------------------------------------------------------------
423
+
424
+ _tdp_cfg: dict[str, Any] = _cfg["ranking"]["type_def_prior"]
425
+
426
+ TYPE_DEF_PRIOR_LAMBDA: float = float(_tdp_cfg["lambda"])
427
+
428
+ # ---------------------------------------------------------------------------
429
+ # Docstring near-duplicate dedup (UPG-RUST-DEF-EVICTION / DEF-C)
430
+ # ---------------------------------------------------------------------------
431
+
432
+ _ddd_cfg: dict[str, Any] = _cfg["ranking"]["docstring_dedup"]
433
+
434
+ DOCSTRING_DEDUP_LINES: int = int(_ddd_cfg["lines"])
435
+ DOCSTRING_DEDUP_MIN_CHARS: int = int(_ddd_cfg["min_chars"])
436
+
437
+ # ---------------------------------------------------------------------------
438
+ # Rerank pool sizes (UPG-12.1) + reranker model
439
+ # ---------------------------------------------------------------------------
440
+
441
+ _rr_cfg: dict[str, Any] = _cfg["ranking"]["rerank"]
442
+
443
+ # Cross-encoder reranker (HuggingFace id). VECTR_RERANKER_MODEL overrides at
444
+ # the searcher; empty disables reranking. Query-time only — no reindex on swap.
445
+ RERANK_MODEL: str = str(_rr_cfg["model"])
446
+
447
+ RERANK_TOP_K: int = int(_rr_cfg["top_k"])
448
+ RERANK_TOP_K_UNFILTERED: int = int(_rr_cfg["top_k_unfiltered"])
449
+ RERANK_PRE_FILTER_FETCH_K: int = int(_rr_cfg["pre_filter_fetch_k"])
450
+
451
+ # ---------------------------------------------------------------------------
452
+ # Indexing tunables (UPG-12.1)
453
+ # ---------------------------------------------------------------------------
454
+
455
+ _idx_cfg: dict[str, Any] = _cfg["indexing"]
456
+
457
+ INDEXING_MAX_CHUNK_LINES: int = int(_idx_cfg["max_chunk_lines"])
458
+ INDEXING_CLASS_HEADER_LINES: int = int(_idx_cfg["class_header_lines"])
459
+ INDEXING_BUILD_ARTIFACT_DIR_SUFFIXES: tuple[str, ...] = tuple(
460
+ str(s).lower() for s in _idx_cfg["build_artifact_dir_suffixes"]
461
+ )
462
+
463
+ # UPG-JSFLOW-SYMBOLS: Flow-typed .js detection (routes to the typescript/tsx grammar).
464
+ _flow_cfg: dict[str, Any] = _idx_cfg["flow_detection"]
465
+
466
+ INDEXING_FLOW_SCAN_HEAD_BYTES: int = int(_flow_cfg["scan_head_bytes"])
467
+ INDEXING_FLOW_PRAGMA: str = str(_flow_cfg["pragma"])
468
+ INDEXING_FLOW_SECONDARY_MARKERS: tuple[str, ...] = tuple(
469
+ str(m) for m in _flow_cfg["secondary_markers"]
470
+ )
471
+
472
+ # ---------------------------------------------------------------------------
473
+ # Output tunables (UPG-12.1)
474
+ # ---------------------------------------------------------------------------
475
+
476
+ _out_cfg: dict[str, Any] = _cfg["output"]
477
+
478
+ OUTPUT_SNIPPET_LINES: int = int(_out_cfg["snippet_lines"])
479
+
480
+ # ---------------------------------------------------------------------------
481
+ # Behaviour tunables (UPG-12.1)
482
+ # ---------------------------------------------------------------------------
483
+
484
+ _beh_cfg: dict[str, Any] = _cfg["behavior"]["remember_nudge"]
485
+
486
+ BEHAVIOR_REMEMBER_NUDGE_THRESHOLD: int = int(_beh_cfg["threshold"])
487
+ BEHAVIOR_REMEMBER_NUDGE_COOLDOWN: int = int(_beh_cfg["cooldown"])
488
+
489
+ # ---------------------------------------------------------------------------
490
+ # Eviction auto-hint token gate (UPG-11.15)
491
+ # ---------------------------------------------------------------------------
492
+
493
+ _evict_cfg: dict[str, Any] = _cfg["behavior"]["eviction"]
494
+
495
+ EVICTION_RETRIEVED_TOKEN_GATE: int = int(_evict_cfg["retrieved_token_gate"])
496
+ EVICTION_HINT_MAX_IDS: int = int(_evict_cfg["hint_max_ids"])
497
+ EVICTION_MAX_TRACKED_SESSIONS: int = int(_evict_cfg["max_tracked_sessions"])
498
+ EVICTION_REMEMBER_ESCALATION_CHUNKS: int = int(_evict_cfg["remember_escalation_chunks"])
499
+ EVICTION_REMEMBER_ESCALATION_TOKENS: int = int(_evict_cfg["remember_escalation_tokens"])
500
+
501
+ # ---------------------------------------------------------------------------
502
+ # Boot recall bounds (UPG-9.2 / UPG-TASK-NOTE-INJECTION-RECENCY)
503
+ # ---------------------------------------------------------------------------
504
+
505
+ _boot_cfg: dict[str, Any] = _cfg["behavior"]["boot"]
506
+
507
+ BOOT_MAX_DIRECTIVE_NOTES: int = int(_boot_cfg["max_directive_notes"])
508
+ BOOT_MAX_TASK_NOTES: int = int(_boot_cfg["max_task_notes"])
509
+
510
+ # ---------------------------------------------------------------------------
511
+ # Symbol graph — reserved keywords (UPG-JSFLOW-SYMBOLS)
512
+ # ---------------------------------------------------------------------------
513
+
514
+ _sg_cfg: dict[str, Any] = _cfg["symbol_graph"]
515
+
516
+ SYMBOL_GRAPH_RESERVED_KEYWORDS: dict[str, frozenset[str]] = {
517
+ str(lang): frozenset(str(kw) for kw in kws)
518
+ for lang, kws in _sg_cfg["reserved_keywords"].items()
519
+ }
520
+
521
+ _sg_error_recovery_cfg: dict[str, Any] = _sg_cfg["error_recovery"]
522
+
523
+ SYMBOL_GRAPH_ERROR_RECOVERY_MIN_SPAN_LINES: int = int(_sg_error_recovery_cfg["min_span_lines"])
524
+ SYMBOL_GRAPH_ERROR_RECOVERY_MAX_REPARSE_ATTEMPTS: int = int(
525
+ _sg_error_recovery_cfg["max_reparse_attempts_per_file"]
526
+ )
527
+ SYMBOL_GRAPH_ERROR_RECOVERY_MAX_EXTEND_STEPS_PER_ATTEMPT: int = int(
528
+ _sg_error_recovery_cfg["max_extend_steps_per_attempt"]
529
+ )
530
+
531
+ # ---------------------------------------------------------------------------
532
+ # CLI daemon-readiness poll (UPG-CLI-START-READY-RACE)
533
+ # ---------------------------------------------------------------------------
534
+
535
+ _cli_cfg: dict[str, Any] = _cfg["cli"]
536
+
537
+ CLI_START_READY_POLL_TIMEOUT_S: float = float(_cli_cfg["start_ready_poll_timeout_s"])
538
+ CLI_START_READY_POLL_INTERVAL_S: float = float(_cli_cfg["start_ready_poll_interval_s"])
539
+ CLI_START_READY_PROBE_TIMEOUT_S: float = float(_cli_cfg["start_ready_probe_timeout_s"])
540
+ CLI_VERSION_SKEW_PROBE_TIMEOUT_S: float = float(_cli_cfg["version_skew_probe_timeout_s"])
541
+
542
+ # ---------------------------------------------------------------------------
543
+ # Workspace / watcher tunables (UPG-13.1/13.2/13.3)
544
+ # ---------------------------------------------------------------------------
545
+
546
+ _ws_cfg: dict[str, Any] = _cfg["workspace"]
547
+
548
+ WORKSPACE_DEFAULT_VECTRIGNORE_DIRS: tuple[str, ...] = tuple(
549
+ str(d) for d in _ws_cfg["default_vectrignore_dirs"]
550
+ )
551
+
552
+ _watcher_cfg: dict[str, Any] = _cfg["watcher"]
553
+
554
+ WATCHER_TOP_LEVEL_RESCAN_INTERVAL_S: float = float(_watcher_cfg["top_level_rescan_interval_s"])
555
+
556
+ # UPG-WATCHER-PRESSURE-GOVERNOR: burst coalescing + self-limit tunables.
557
+ WATCHER_BURST_FILES_THRESHOLD: int = int(_watcher_cfg["burst_files_threshold"])
558
+ WATCHER_BURST_QUIET_SECONDS: float = float(_watcher_cfg["burst_quiet_seconds"])
559
+ WATCHER_MAX_RSS_MB: float = float(_watcher_cfg["max_rss_mb"])
560
+
561
+ # ---------------------------------------------------------------------------
562
+ # Hook injection observability (UPG-HOOK-INJECT-OBSERVABILITY)
563
+ # ---------------------------------------------------------------------------
564
+
565
+ _hooks_cfg: dict[str, Any] = _cfg["hooks"]
566
+
567
+ HOOKS_LOG_INJECTIONS: bool = bool(_hooks_cfg["log_injections"])
568
+ HOOKS_LOG_CHARS_PER_TOKEN: int = int(_hooks_cfg["log_chars_per_token"])
569
+
570
+ # ---------------------------------------------------------------------------
571
+ # ingest_traces unresolved-caller/callee warning cap (UPG-7.3)
572
+ # ---------------------------------------------------------------------------
573
+
574
+ _ingest_cfg: dict[str, Any] = _cfg["behavior"]["ingest_traces"]
575
+
576
+ INGEST_TRACES_MAX_UNRESOLVED_EXAMPLES: int = int(_ingest_cfg["max_unresolved_examples"])
577
+
578
+ # ---------------------------------------------------------------------------
579
+ # MCP param-name aliases (F40-class ergonomics, UPG-TRACE-GRAPH-INCOMPLETE)
580
+ # ---------------------------------------------------------------------------
581
+
582
+ SYMBOL_NAME_PARAM_ALIASES: tuple[str, ...] = tuple(
583
+ str(a) for a in _cfg["behavior"]["symbol_name_param_aliases"]
584
+ )
585
+
586
+ # ---------------------------------------------------------------------------
587
+ # Retrieval strategy fallback (UPG-8.2)
588
+ # ---------------------------------------------------------------------------
589
+
590
+ _strat_cfg: dict[str, Any] = _cfg["strategy"]
591
+
592
+ STRATEGY_DEFAULT_SEMANTIC_WEIGHT: float = float(_strat_cfg["default_semantic_weight"])
593
+ STRATEGY_DEFAULT_BM25_WEIGHT: float = float(_strat_cfg["default_bm25_weight"])
594
+ # Dual-vector pool entry — purpose (signature+docstring) vector (ARCH-4)
595
+ # ---------------------------------------------------------------------------
596
+
597
+ _dv_cfg: dict[str, Any] = _cfg["retrieval"]["dual_vector"]
598
+
599
+ DUAL_VECTOR_ENABLED: bool = bool(_dv_cfg["enabled"])
600
+ DUAL_VECTOR_BLEND_MODE: str = str(_dv_cfg["blend_mode"])
601
+ DUAL_VECTOR_BLEND_WEIGHT: float = float(_dv_cfg["blend_weight"])
602
+ DUAL_VECTOR_MAX_SIGNATURE_LINES: int = int(_dv_cfg["max_signature_lines"])
603
+ DUAL_VECTOR_MAX_DOCSTRING_LINES: int = int(_dv_cfg["max_docstring_lines"])
604
+ DUAL_VECTOR_MAX_DOCSTRING_CHARS: int = int(_dv_cfg["max_docstring_chars"])
605
+
606
+ # ---------------------------------------------------------------------------
607
+ # Not-found floor — lexical-vocabulary-anchor low-confidence signal
608
+ # (UPG-NOTFOUND-FLOOR, UPG-NOTFOUND-FLOOR-2)
609
+ # ---------------------------------------------------------------------------
610
+
611
+ _nff_cfg: dict[str, Any] = _cfg["ranking"]["notfound_floor"]
612
+
613
+ NOTFOUND_FLOOR_ENABLED: bool = bool(_nff_cfg["enabled"])
614
+ NOTFOUND_FLOOR_MIN_TOKEN_LEN: int = int(_nff_cfg["min_content_token_length"])
615
+ NOTFOUND_FLOOR_STOPWORDS: frozenset[str] = frozenset(_nff_cfg["stopwords"])
616
+ NOTFOUND_FLOOR_MIN_ZERO_DF_TOKENS: int = int(_nff_cfg["min_zero_df_tokens"])
617
+ NOTFOUND_FLOOR_MIN_TOP_RELEVANCE: float = float(_nff_cfg["min_top_relevance"])
618
+ NOTFOUND_FLOOR_BANNER: str = str(_nff_cfg["banner"])
619
+ NOTFOUND_FLOOR_BANNER_CLI: str = str(_nff_cfg["banner_cli"])
620
+
621
+ # ---------------------------------------------------------------------------
622
+ # Search — additive identifier-shape symbol-graph hint (UPG-QUERYTYPE-REROUTE)
623
+ # ---------------------------------------------------------------------------
624
+
625
+ _id_hint_cfg: dict[str, Any] = _cfg["search"]["identifier_hint"]
626
+
627
+ SEARCH_IDENTIFIER_HINT_ENABLED: bool = bool(_id_hint_cfg["enabled"])
628
+ SEARCH_IDENTIFIER_HINT_MAX_IDENTIFIERS: int = int(_id_hint_cfg["max_identifiers"])
629
+ SEARCH_IDENTIFIER_HINT_MAX_LOCATIONS: int = int(_id_hint_cfg["max_locations"])
630
+
631
+ # UPG-NEARMISS-SYMBOL-NAMES: additive, honestly-labeled near-miss names for an
632
+ # identifier-shaped token that fails EXACT symbol-graph resolution.
633
+ SEARCH_IDENTIFIER_HINT_NEARMISS_ENABLED: bool = bool(_id_hint_cfg["nearmiss_enabled"])
634
+ SEARCH_IDENTIFIER_HINT_NEARMISS_MAX: int = int(_id_hint_cfg["nearmiss_max"])
635
+ SEARCH_IDENTIFIER_HINT_NEARMISS_MIN_PREFIX_LEN: int = int(_id_hint_cfg["nearmiss_min_prefix_len"])
636
+
637
+ # ---------------------------------------------------------------------------
638
+ # Embedding — default local model (UPG-EMBEDDER-SWAP-GRANITE)
639
+ # ---------------------------------------------------------------------------
640
+
641
+ EMBEDDING_DEFAULT_MODEL: str = str(_cfg["embedding"]["default_model"])
642
+ EMBEDDING_MAX_SEQ_LENGTH: int = int(_cfg["embedding"]["max_seq_length"])
643
+
644
+ # ---------------------------------------------------------------------------
645
+ # Deterministic re-fetch-by-chunk-id contract (UPG-CTX-EVICT)
646
+ # ---------------------------------------------------------------------------
647
+
648
+ FETCH_MAX_IDS_PER_CALL: int = int(_cfg["fetch"]["max_ids_per_call"])