@vibgrate/cli 2026.825.1 → 2026.826.1
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.
- package/dist/{agent-IHNUBFBM.js → agent-4RHH2FR2.js} +13 -13
- package/dist/{agent-IHNUBFBM.js.map → agent-4RHH2FR2.js.map} +1 -1
- package/dist/{approvals-HRIMDIX3.js → approvals-XDFM346D.js} +5 -5
- package/dist/{approvals-HRIMDIX3.js.map → approvals-XDFM346D.js.map} +1 -1
- package/dist/{attach-L43JUJ7A.js → attach-LGTCYNKC.js} +3 -3
- package/dist/{attach-L43JUJ7A.js.map → attach-LGTCYNKC.js.map} +1 -1
- package/dist/{baseline-EZQ7SCRT.js → baseline-PRY5HKL5.js} +3 -3
- package/dist/{baseline-EZQ7SCRT.js.map → baseline-PRY5HKL5.js.map} +1 -1
- package/dist/{chunk-BCK2NIBM.js → chunk-7KOEEEIZ.js} +6 -6
- package/dist/{chunk-BCK2NIBM.js.map → chunk-7KOEEEIZ.js.map} +1 -1
- package/dist/{chunk-5BFA532E.js → chunk-7S4ELKK6.js} +3 -3
- package/dist/{chunk-5BFA532E.js.map → chunk-7S4ELKK6.js.map} +1 -1
- package/dist/{chunk-UUVOA6WD.js → chunk-BHDP7EVJ.js} +8 -8
- package/dist/{chunk-UUVOA6WD.js.map → chunk-BHDP7EVJ.js.map} +1 -1
- package/dist/{chunk-GRMZU24V.js → chunk-E44I7635.js} +3 -3
- package/dist/{chunk-GRMZU24V.js.map → chunk-E44I7635.js.map} +1 -1
- package/dist/{chunk-RHYRMMN3.js → chunk-IEZCFMNL.js} +3 -3
- package/dist/{chunk-RHYRMMN3.js.map → chunk-IEZCFMNL.js.map} +1 -1
- package/dist/{chunk-TLYNLQL2.js → chunk-IM7HME2T.js} +9 -9
- package/dist/{chunk-TLYNLQL2.js.map → chunk-IM7HME2T.js.map} +1 -1
- package/dist/chunk-JQ7OL4XG.js +6 -0
- package/dist/{chunk-KFLNWYZG.js.map → chunk-JQ7OL4XG.js.map} +1 -1
- package/dist/{chunk-FVBN4E5C.js → chunk-K2IWOZEQ.js} +3 -3
- package/dist/{chunk-FVBN4E5C.js.map → chunk-K2IWOZEQ.js.map} +1 -1
- package/dist/{chunk-6VLOI2ZP.js → chunk-LAYDXJEQ.js} +3 -3
- package/dist/{chunk-6VLOI2ZP.js.map → chunk-LAYDXJEQ.js.map} +1 -1
- package/dist/{chunk-LUJ4H4RJ.js → chunk-NQU6E6OG.js} +3 -3
- package/dist/{chunk-LUJ4H4RJ.js.map → chunk-NQU6E6OG.js.map} +1 -1
- package/dist/{chunk-QTQRM7HQ.js → chunk-PEHMRPYM.js} +3 -3
- package/dist/{chunk-QTQRM7HQ.js.map → chunk-PEHMRPYM.js.map} +1 -1
- package/dist/{chunk-M7O6IVKN.js → chunk-PIB3YNJU.js} +3 -3
- package/dist/{chunk-M7O6IVKN.js.map → chunk-PIB3YNJU.js.map} +1 -1
- package/dist/{chunk-S3HMG6BC.js → chunk-QONHVC4H.js} +3 -3
- package/dist/{chunk-S3HMG6BC.js.map → chunk-QONHVC4H.js.map} +1 -1
- package/dist/{chunk-YP243ZKD.js → chunk-R5BGRJ6X.js} +3 -3
- package/dist/{chunk-YP243ZKD.js.map → chunk-R5BGRJ6X.js.map} +1 -1
- package/dist/{chunk-4PCFSRYH.js → chunk-TKUWEJZJ.js} +5 -5
- package/dist/{chunk-4PCFSRYH.js.map → chunk-TKUWEJZJ.js.map} +1 -1
- package/dist/{chunk-L3YEEJ6U.js → chunk-TOHNVUDD.js} +3 -3
- package/dist/{chunk-L3YEEJ6U.js.map → chunk-TOHNVUDD.js.map} +1 -1
- package/dist/{chunk-ECQFRZJI.js → chunk-TQ75FB52.js} +4 -4
- package/dist/{chunk-ECQFRZJI.js.map → chunk-TQ75FB52.js.map} +1 -1
- package/dist/{chunk-LKJIYGO5.js → chunk-TUVB6Q6F.js} +6 -6
- package/dist/{chunk-LKJIYGO5.js.map → chunk-TUVB6Q6F.js.map} +1 -1
- package/dist/{chunk-X34HVFZ6.js → chunk-WGA4KUGS.js} +8 -8
- package/dist/{chunk-X34HVFZ6.js.map → chunk-WGA4KUGS.js.map} +1 -1
- package/dist/{chunk-Y3X3ORQ3.js → chunk-WGSLN73L.js} +5 -5
- package/dist/{chunk-Y3X3ORQ3.js.map → chunk-WGSLN73L.js.map} +1 -1
- package/dist/{chunk-DLNNNESQ.js → chunk-WHNB3443.js} +3 -3
- package/dist/{chunk-DLNNNESQ.js.map → chunk-WHNB3443.js.map} +1 -1
- package/dist/{chunk-CIC3NY2D.js → chunk-WPQCJADP.js} +27 -17
- package/dist/chunk-WPQCJADP.js.map +1 -0
- package/dist/{chunk-B5X246UH.js → chunk-WU2DVESP.js} +3 -3
- package/dist/{chunk-B5X246UH.js.map → chunk-WU2DVESP.js.map} +1 -1
- package/dist/{chunk-JSZ46IDG.js → chunk-XW4FCBL4.js} +6 -6
- package/dist/{chunk-JSZ46IDG.js.map → chunk-XW4FCBL4.js.map} +1 -1
- package/dist/{chunk-YIRDV4YJ.js → chunk-ZRDTI67N.js} +3 -3
- package/dist/{chunk-YIRDV4YJ.js.map → chunk-ZRDTI67N.js.map} +1 -1
- package/dist/cli.js +52 -51
- package/dist/cli.js.map +1 -1
- package/dist/{daemon-WPX2SBVV.js → daemon-2YMCH7R2.js} +9 -9
- package/dist/{daemon-WPX2SBVV.js.map → daemon-2YMCH7R2.js.map} +1 -1
- package/dist/embed-worker-main.js +3 -3
- package/dist/{ensure-map-MLI4XCRV.js → ensure-map-S2GBW2UC.js} +14 -14
- package/dist/{ensure-map-MLI4XCRV.js.map → ensure-map-S2GBW2UC.js.map} +1 -1
- package/dist/{graph-backend-LK5EFQXN.js → graph-backend-QDEU5RIR.js} +7 -7
- package/dist/{graph-backend-LK5EFQXN.js.map → graph-backend-QDEU5RIR.js.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +15 -15
- package/dist/{interactive-K446F42I.js → interactive-GMGWUEQO.js} +30 -26
- package/dist/interactive-GMGWUEQO.js.map +1 -0
- package/dist/load-JXCTAAEI.js +11 -0
- package/dist/{load-2FRH37JY.js.map → load-JXCTAAEI.js.map} +1 -1
- package/dist/parse-worker.js +2 -2
- package/dist/{run-outcome-4ETNDPX6.js → run-outcome-5NA7PI52.js} +5 -5
- package/dist/{run-outcome-4ETNDPX6.js.map → run-outcome-5NA7PI52.js.map} +1 -1
- package/dist/{runtime-session-RMSKXFIJ.js → runtime-session-IEM6DZDP.js} +9 -9
- package/dist/{runtime-session-RMSKXFIJ.js.map → runtime-session-IEM6DZDP.js.map} +1 -1
- package/dist/{session-22BGDYWF.js → session-VYFLJ7KL.js} +8 -8
- package/dist/{session-22BGDYWF.js.map → session-VYFLJ7KL.js.map} +1 -1
- package/dist/{stream-json-MD7MSSCH.js → stream-json-2ISQLKPD.js} +13 -13
- package/dist/{stream-json-MD7MSSCH.js.map → stream-json-2ISQLKPD.js.map} +1 -1
- package/dist/version-B62ELJRY.js +4 -0
- package/dist/{version-FRUT6RI2.js.map → version-B62ELJRY.js.map} +1 -1
- package/dist/{vg-mcp-bridge-H6I3ALUW.js → vg-mcp-bridge-CJIK5U56.js} +8 -8
- package/dist/{vg-mcp-bridge-H6I3ALUW.js.map → vg-mcp-bridge-CJIK5U56.js.map} +1 -1
- package/dist/{vgd-TW7HU2PH.js → vgd-4EB6SKFB.js} +8 -8
- package/dist/vgd-4EB6SKFB.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-CIC3NY2D.js.map +0 -1
- package/dist/chunk-KFLNWYZG.js +0 -6
- package/dist/interactive-K446F42I.js.map +0 -1
- package/dist/load-2FRH37JY.js +0 -11
- package/dist/version-FRUT6RI2.js +0 -4
- package/dist/vgd-TW7HU2PH.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/engine/relations.ts","../src/engine/user-ask.ts","../src/engine/query.ts","../src/engine/impact.ts"],"names":["push","text","round"],"mappings":"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","file":"chunk-LUJ4H4RJ.js","sourcesContent":["import type { EdgeKind, GraphEdge, GraphNode, VgGraph } from '../schema.js';\n\n/** Indexes over a graph for O(1) neighbor lookups (built once per command). */\nexport class GraphIndex {\n readonly nodeById = new Map<string, GraphNode>();\n private outById = new Map<string, GraphEdge[]>();\n private inById = new Map<string, GraphEdge[]>();\n\n constructor(readonly graph: VgGraph) {\n for (const n of graph.nodes) this.nodeById.set(n.id, n);\n for (const e of graph.edges) {\n push(this.outById, e.src, e);\n push(this.inById, e.dst, e);\n }\n }\n\n out(id: string, kind?: EdgeKind): GraphEdge[] {\n const all = this.outById.get(id) ?? [];\n return kind ? all.filter((e) => e.kind === kind) : all;\n }\n\n in(id: string, kind?: EdgeKind): GraphEdge[] {\n const all = this.inById.get(id) ?? [];\n return kind ? all.filter((e) => e.kind === kind) : all;\n }\n\n node(id: string): GraphNode | undefined {\n return this.nodeById.get(id);\n }\n\n /**\n * Resolved nodes called by `id` — invocations (`call`) plus structural\n * dependency references (`references`, e.g. a constructor-injected field's\n * type), since both represent real usage a caller/impact question cares\n * about. `references` is otherwise only emitted by the precise SCIP/tsc\n * rungs and (for Java DI wiring) the heuristic rung — never a guess.\n */\n callees(id: string): { edge: GraphEdge; node: GraphNode }[] {\n return this.resolveTargets(this.out(id).filter(isUsageEdge), 'dst');\n }\n\n /** Resolved nodes that call or structurally reference `id`. */\n callers(id: string): { edge: GraphEdge; node: GraphNode }[] {\n return this.resolveTargets(this.in(id).filter(isUsageEdge), 'src');\n }\n\n private resolveTargets(edges: GraphEdge[], end: 'src' | 'dst') {\n const out: { edge: GraphEdge; node: GraphNode }[] = [];\n for (const e of edges) {\n const node = this.nodeById.get(e[end]);\n if (node) out.push({ edge: e, node });\n }\n return out;\n }\n}\n\nconst USAGE_KINDS: ReadonlySet<EdgeKind> = new Set(['call', 'references']);\nfunction isUsageEdge(e: GraphEdge): boolean {\n return USAGE_KINDS.has(e.kind);\n}\n\nfunction push(map: Map<string, GraphEdge[]>, key: string, value: GraphEdge): void {\n const list = map.get(key);\n if (list) list.push(value);\n else map.set(key, [value]);\n}\n\n/**\n * Memoized `GraphIndex`, keyed on the graph object's identity.\n *\n * The index (node-by-id + in/out adjacency) is a pure function of the graph's\n * content, and every read path rebuilds it from scratch — an O(nodes + edges)\n * pass that, on a large map, costs hundreds of milliseconds *per query*. Under\n * `vg serve` the same parsed graph object is reused across many tool calls\n * (`GraphSource` caches it by file mtime and only replaces the reference when\n * `graph.json` actually changes), so rebuilding the index on every call is pure\n * waste. Keying a `WeakMap` on that object identity builds the index once and\n * reuses it until the file is rebuilt — at which point `GraphSource` hands back a\n * *new* object, this map misses, and the stale index is collected with the old\n * graph. Correct and stale-safe by construction: a different graph is a\n * different object, so there is no cross-graph reuse and nothing to invalidate.\n *\n * `GraphIndex` is read-only (no method mutates its maps), so sharing one\n * instance across callers is safe.\n */\nconst INDEX_CACHE = new WeakMap<VgGraph, GraphIndex>();\n\nexport function indexFor(graph: VgGraph): GraphIndex {\n let idx = INDEX_CACHE.get(graph);\n if (!idx) {\n idx = new GraphIndex(graph);\n INDEX_CACHE.set(graph, idx);\n }\n return idx;\n}\n","/**\n * Split the user's ask from host-injected attachment metadata.\n *\n * The VS Code panel (`codeAttachments.ts`) and other hosts append a markdown\n * block after a `---` fence:\n *\n * ## User attachments (always include in your reasoning for this turn)\n * ### Attached image: `image.png`\n * … saved at: `.vibgrate/code-attachments/…`\n *\n * Those backticks are for the model. If the full instruction is fed into\n * {@link extractLiteralNeedles} / seed ranking, attachment basenames and paths\n * become **literal-locate hard constraints** (\"0 hits → finish, do not invent\n * an edit\") and the agent aborts a real coding task. Field report: version-badge\n * health UI with a screenshot — agent searched for `image.png` and stopped.\n *\n * Retrieval, needle pinning, locate-only detection, and relevance analysis must\n * use {@link userAskFromInstruction}. The full instruction (with attachments)\n * still belongs in the Task / user message so the model can see the appendix.\n */\n\n/** Heading written by hosts when packing panel attachments into the instruction. */\nexport const USER_ATTACHMENTS_HEADING_PREFIX = '## User attachments';\n\n/**\n * Return only the user-authored portion of an instruction, dropping a trailing\n * host \"User attachments\" appendix when present. Unchanged when no appendix.\n */\nexport function userAskFromInstruction(instruction: string): string {\n if (!instruction) return instruction;\n\n // Canonical host shape: blank line, --- fence, then the attachments heading.\n const fenced = /\\n---\\s*\\n## User attachments\\b[^\\n]*\\n?/;\n const m = fenced.exec(instruction);\n if (m && m.index != null) {\n return instruction.slice(0, m.index).trimEnd();\n }\n\n // Defensive: heading without the fence (older hosts / hand-edited prompts).\n const bare = instruction.search(/(?:^|\\n)## User attachments\\b/);\n if (bare > 0) {\n return instruction.slice(0, bare).trimEnd();\n }\n if (bare === 0) {\n // Instruction is only the appendix — no user ask left.\n return '';\n }\n\n return instruction;\n}\n","import { indexFor, type GraphIndex } from './relations.js';\nimport { cosine, type Embedder } from './embeddings.js';\nimport type { SanitizedRank } from './relevance-provider.js';\nimport type { GraphNode, VgGraph } from '../schema.js';\nimport { userAskFromInstruction } from './user-ask.js';\n\n/**\n * Retrieval front-end for `vg ask` / `vg code` capsule seeds (VG-CLI-SPEC\n * §3.2).\n *\n * Since the 2026-08 relevance relocation, the RANKING ENGINE lives in the\n * optional relevance module (`@vibgrate/relevance`, auto-provisioned): the\n * async callers run the ask through the module via\n * `relevance-provider.rankQuestion` and pass the SANITIZED result in as\n * `options.ranked`. This file keeps only what is mechanical:\n *\n * - literal string/URL needle handling (the locate path — exact-text\n * matching, not relevance);\n * - a deliberately dumb module-less fallback: exact identifier-name and\n * name-part matching, nothing that understands language (no lexicon, no\n * term roles, no IDF, no typo repair, no expansion) — enough that an ask\n * NAMING a symbol still pins its file when the module is unavailable;\n * - context-block rendering and the semantic Reciprocal Rank Fusion\n * plumbing (`--semantic`), which fuses ORDERINGS and carries no ranking\n * heuristics of its own.\n */\n\nexport interface QueryOptions {\n budget?: number; // approx token budget for the context block (default 2000)\n limit?: number; // max seed matches to expand (default 12)\n /** Sanitized module ranking (engine/relevance-provider.ts rankQuestion),\n * injected by async callers so this module stays pure and sync. When\n * absent — module not installed, predates the ranking API, or failed —\n * the mechanical fallback below answers. */\n ranked?: SanitizedRank | null;\n}\n\nexport interface QueryMatch {\n node: GraphNode;\n score: number;\n why: string;\n}\n\nexport interface QueryResult {\n question: string;\n matches: QueryMatch[];\n context: string;\n tokensEstimate: number;\n}\n\n/**\n * Function-word hygiene for the mechanical fallback tokenizer. This is NOT a\n * relevance stopword model (that moved into the module): nothing here scores,\n * ranks, weights, or reorders anything. It is a purely SUBTRACTIVE floor —\n * the words a caller uses to FRAME an ask rather than to name the code they\n * mean, which therefore cannot be identifier evidence.\n *\n * The floor exists because `mechanicalRank` treats every surviving token as\n * equal evidence, and English framing words collide with real identifier\n * parts constantly. Without it, `\"add a new feature\"` scores `AddBlogForm`\n * on the camel-part tier (`add`) and `FeatureFlagStore` (`feature`) and fills\n * the entire seed window — a grab-bag Task Capsule for an ask that named\n * nothing. That is the capsule-poisoning failure of the 2026-07 field report,\n * and an empty result is the correct answer.\n *\n * Note this is the floor ONLY. Deciding which of several genuine candidates\n * is most relevant remains the module's job, and no part of that logic —\n * term roles, weighting, expansion, morphology — belongs in this file.\n */\nconst FUNCTION_WORDS = new Set([\n // Closed-class English: could only ever match by coincidence.\n 'the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'to', 'of', 'in', 'on', 'for', 'and', 'or',\n 'where', 'what', 'which', 'how', 'do', 'does', 'did', 'i', 'we', 'it', 'this', 'that', 'with',\n 'when', 'who', 'why', 'can', 'should', 'my', 'our', 'you', 'your', 'from', 'by', 'at', 'as',\n // Process verbs. A caller says what they want DONE with these; they never\n // name the code it should be done to. Left in, bare `add` seeds every\n // Add*/*Add* symbol in the repo ahead of the terms that carry the subject.\n 'add', 'adds', 'added', 'adding',\n 'create', 'creates', 'created', 'creating',\n 'make', 'makes', 'making', 'made',\n 'remove', 'removes', 'removed', 'removing',\n 'delete', 'deletes', 'deleted', 'deleting',\n 'update', 'updates', 'updated', 'updating',\n 'change', 'changes', 'changed', 'changing',\n 'set', 'sets', 'setting', 'setup', 'get', 'gets', 'getting',\n 'use', 'uses', 'used', 'using', 'want', 'wants', 'wanted', 'doing', 'done',\n 'support', 'supports', 'supported', 'supporting',\n 'enable', 'enables', 'enabled', 'enabling',\n 'disable', 'disables', 'disabled', 'disabling',\n 'allow', 'allows', 'allowed', 'handle', 'handles', 'handling',\n 'implement', 'implements', 'implemented', 'implementing',\n 'build', 'builds', 'building', 'built', 'need', 'needs', 'needed',\n // Filler nouns and connectives: the generic scaffolding of a request.\n 'new', 'newly', 'via', 'through', 'into', 'onto',\n 'way', 'ways', 'thing', 'things', 'stuff',\n 'feature', 'features', 'functionality',\n 'method', 'methods', 'option', 'options', 'ability',\n 'form', 'forms', 'page', 'pages', 'screen', 'screens', 'button', 'buttons',\n 'help', 'start', 'begin', 'work', 'works', 'working',\n 'also', 'currently', 'properly', 'correctly', 'please',\n 'happens', 'happen', 'something', 'somewhere',\n 'step', 'steps', 'flow', 'process',\n // Discovery-question scaffolding: words that FRAME the search itself\n // (\"find the code responsible for X\", \"explain how X works\").\n 'find', 'code', 'responsible', 'modify', 'me',\n 'implementation', 'explain', 'codebase', 'contains', 'file',\n // Bug-report framing: says something is WRONG, never what is wrong.\n // `fix` alone lit up `export_fixed_width_records`, `out` lit up every\n // *Router*/*rollout* symbol (vg code prompt-relevance corpus).\n 'fix', 'fixes', 'fixing', 'bug', 'bugs', 'broken', 'busted', 'borked',\n 'wrong', 'incorrect', 'incorrectly', 'figure', 'out', 'sure',\n]);\n\n/**\n * Strip sentence punctuation from a URL match without breaking real query\n * strings (`?key=value`). A lone trailing `?` / `!` is English punctuation\n * (\"where is https://…/signup?\") — leaving it in the needle makes the literal\n * sweep miss the bare path in source.\n */\nfunction cleanUrlNeedle(raw: string): string {\n let s = raw.trim();\n // Trailing ) ] } > , ; : ! . from markdown links / sentence ends.\n s = s.replace(/[.,;:!)\\]}>]+$/g, '');\n // Lone trailing `?` → drop. Keep `?key=…` (content after `?`).\n if (s.endsWith('?')) {\n const qi = s.lastIndexOf('?');\n if (qi === s.length - 1) s = s.slice(0, -1);\n }\n return s;\n}\n\n/**\n * Extract string/URL needles a caller is asking to *locate as text*, not as\n * bag-of-words symbol terms. URLs and quoted phrases are exact-match material\n * for the literal sweep (`search_symbols` / `search_code`); feeding their path\n * segments into lexical symbol ranking produces confidently wrong seeds\n * (dash → dashboard package, exist → NonExisting, …).\n */\nexport function extractLiteralNeedles(question: string): string[] {\n const out: string[] = [];\n const seen = new Set<string>();\n const push = (raw: string, kind: 'url' | 'quote') => {\n let s = raw.trim();\n s = kind === 'url' ? cleanUrlNeedle(s) : s.replace(/[.,;:!)\\]}>?]+$/g, '');\n if (s.length < 2 || seen.has(s)) return;\n seen.add(s);\n out.push(s);\n };\n for (const m of question.matchAll(/https?:\\/\\/[^\\s\"'`<>]+/gi)) push(m[0]!, 'url');\n for (const m of question.matchAll(/([\"'`])((?:(?!\\1)[^\\\\]|\\\\.){2,})\\1/g)) {\n push(m[2]!.replace(/\\\\(.)/g, '$1'), 'quote');\n }\n return out;\n}\n\n/** Question text with URL/quoted needles removed so residual tokens can name symbols. */\nexport function stripLiteralNeedles(question: string): string {\n let s = question;\n // Strip raw URL spans first (may still carry a trailing `?` from the ask).\n for (const m of question.matchAll(/https?:\\/\\/[^\\s\"'`<>]+/gi)) {\n s = s.split(m[0]!).join(' ');\n }\n for (const lit of extractLiteralNeedles(question)) {\n s = s.split(lit).join(' ');\n }\n // Also strip bare quote pairs left behind.\n s = s.replace(/([\"'`])\\s*\\1/g, ' ');\n return s.replace(/\\s+/g, ' ').trim();\n}\n\n/**\n * Residual ask used for graph symbol ranking when the question embeds a\n * URL/quote. Empty when only framing words remain (\"where is …?\") so we do\n * not light up unrelated symbols like `applyPlan.where`.\n */\nexport function residualForSymbolSearch(question: string): string {\n const literals = extractLiteralNeedles(question);\n if (literals.length === 0) return question.trim();\n const stripped = stripLiteralNeedles(question);\n return tokenize(stripped).length === 0 ? '' : stripped;\n}\n\n/**\n * True when the instruction is a string/URL occurrence locate (not an edit).\n * Used to skip PatchIR grammar and answer from the literal sweep without\n * dumping constrained-decoding JSON into the VG Code panel.\n */\nexport function isLocateOnlyInstruction(instruction: string): boolean {\n // Drop host-injected \"User attachments\" appendix (backticked image paths)\n // before classify — same rule as buildCodeContext. Avoids treating a screenshot\n // filename as a locate-only occurrence search.\n const q = userAskFromInstruction(instruction).trim();\n if (!q) return false;\n const needles = extractLiteralNeedles(q);\n const hasLocateFrame =\n /\\b(where\\s+is|where\\s+are|find\\s+(all\\s+|every\\s+)?occurrence|find\\s+occurrences|does\\s+not\\s+exist|locate|search\\s+for)\\b/i.test(\n q,\n ) ||\n (needles.length > 0 && /^https?:\\/\\//i.test(q));\n // Strong edit verbs cancel locate-only (ignore \"does not exist\" as non-edit).\n const withoutExist = q.replace(/\\bdoes\\s+not\\s+exist\\b/gi, ' ');\n if (\n /\\b(fix|edit|change|replace|implement|refactor|rename|patch|create\\s+file|delete\\s+file|add\\s+a\\b|remove\\s+the\\b)\\b/i.test(\n withoutExist,\n )\n ) {\n return false;\n }\n if (needles.some((n) => /^https?:\\/\\//i.test(n))) return true;\n return hasLocateFrame && needles.length > 0;\n}\n\nexport function queryGraph(graph: VgGraph, question: string, options: QueryOptions = {}): QueryResult {\n const budget = options.budget ?? 2000;\n const limit = options.limit ?? 12;\n const literals = extractLiteralNeedles(question);\n const index = indexFor(graph);\n\n let seeds: QueryMatch[];\n if (options.ranked) {\n // Module ranking (already sanitized: every id names a real symbol).\n // An honest miss from the engine stays an honest miss here.\n const byId = new Map<string, GraphNode>();\n for (const node of graph.nodes) byId.set(node.id, node);\n seeds = options.ranked.hasContent\n ? options.ranked.seeds\n .map((s) => {\n const node = byId.get(s.id);\n return node ? { node, score: s.score, why: s.why } : null;\n })\n .filter((m): m is QueryMatch => m !== null)\n .slice(0, limit)\n : [];\n } else {\n seeds = mechanicalRank(graph, question, literals).slice(0, limit);\n }\n\n const { context, tokensEstimate } = buildContext(graph, index, question, seeds, budget, literals);\n return { question, matches: seeds, context, tokensEstimate };\n}\n\n/**\n * The module-less fallback: mechanical identifier matching, deliberately free\n * of language understanding. A token scores only when it IS a symbol's name,\n * one of its camel/snake parts, or (length ≥ 4) a substring of the qualified\n * name — so \"write tests for chargeCard\" or a pasted `src/…/File.ts` path\n * still pins the right file, while intent phrasings honestly miss until the\n * relevance module is available. No lexicon, no term roles, no IDF, no typo\n * repair, no expansions — that is the module's job now.\n */\nfunction mechanicalRank(graph: VgGraph, question: string, literals: string[]): QueryMatch[] {\n const forTokens = literals.length > 0 ? stripLiteralNeedles(question) : question;\n let tokens = tokenize(forTokens);\n // A literal ask's residual framing (\"does not exist\", \"find occurrences\")\n // must not light up DoesNot*/NonExisting*/commandExists lookalikes — the\n // honest-miss contract of the locate path holds module-less too.\n if (literals.length > 0) tokens = tokens.filter((t) => !LOCATE_SCAFFOLD.has(t));\n if (tokens.length === 0) return [];\n const scored: QueryMatch[] = [];\n for (const node of graph.nodes) {\n if (node.kind === 'file' || node.kind === 'external') continue;\n const name = node.name.toLowerCase();\n const qn = node.qualifiedName.toLowerCase();\n const parts = identifierParts(node.name);\n let score = 0;\n const hits: string[] = [];\n for (const t of tokens) {\n if (name === t) {\n score += 10;\n hits.push(t);\n } else if (parts.has(t)) {\n score += 6;\n hits.push(t);\n } else if (t.length >= 4 && qn.includes(t)) {\n score += 3;\n hits.push(t);\n }\n }\n if (score > 0) {\n scored.push({\n node,\n score: round(score * (1 + IMPORTANCE_WEIGHT * node.importance)),\n why: `matched: ${hits.join(', ')}`,\n });\n }\n }\n scored.sort((a, b) => b.score - a.score || a.node.qualifiedName.localeCompare(b.node.qualifiedName));\n return diversifyByFile(scored);\n}\n\nfunction tokenize(q: string): string[] {\n return [\n ...new Set(\n q\n .toLowerCase()\n .split(/[^\\p{L}\\p{N}]+/u)\n .filter((t) => t.length >= 1 && !FUNCTION_WORDS.has(t)),\n ),\n ];\n}\n\n/**\n * Importance is a mild tiebreaker, not a doubling: at 0.4 a top hub adds at\n * most 40%, enough to break genuine ties without overriding term evidence.\n */\nconst IMPORTANCE_WEIGHT = 0.4;\n\n/** Residual framing of a string/URL locate ask — never symbol evidence. */\nconst LOCATE_SCAFFOLD = new Set([\n 'not', 'no', 'exist', 'exists', 'existing', 'occurrence', 'occurrences',\n 'occurence', 'occurences', 'find', 'every', 'place', 'places', 'string',\n 'literal', 'text', 'search', 'locate', 'look', 'looking', 'show', 'list', 'all',\n]);\n\n/**\n * Stable file-diversity pass over the ranked list: each file's best node\n * keeps its rank; further nodes from an already-represented file are demoted\n * behind the first representative of every other matching file (never\n * dropped). Mechanical presentation hygiene — the seed window is a coverage\n * budget either way.\n */\nfunction diversifyByFile(matches: QueryMatch[]): QueryMatch[] {\n const seen = new Set<string>();\n const firstPerFile: QueryMatch[] = [];\n const rest: QueryMatch[] = [];\n for (const m of matches) {\n if (seen.has(m.node.file)) {\n rest.push(m);\n } else {\n seen.add(m.node.file);\n firstPerFile.push(m);\n }\n }\n return firstPerFile.concat(rest);\n}\n\nexport interface SemanticQueryOptions extends QueryOptions {\n /** Local embedder. Optional only when {@link semanticRanked} supplies the pass. */\n embedder?: Embedder;\n /** Precomputed node vectors (from getNodeEmbeddings); falls back to lexical for nodes without one. */\n nodeVectors?: Map<string, number[]>;\n /**\n * A semantic pass someone else already ran — vgd ranking the question\n * against its resident slot index. Supplied instead of `embedder` +\n * `nodeVectors` so the caller pays neither a model load nor a vector scan,\n * and no vectors cross the socket.\n *\n * Only the ORDER of this list is consumed (RRF fuses rankings, not scores),\n * so a truncated top-K is not an approximation: a candidate ranked past a\n * few hundred cannot reach a 12-row answer.\n */\n semanticRanked?: Array<{ id: string; score: number }>;\n}\n\n/**\n * Reciprocal Rank Fusion constant (the standard k=60). Fusing RANKINGS\n * instead of blending raw scores removes the need to calibrate two\n * incomparable scales: a node's fused score is Σ 1/(k + rankᵢ) over the\n * lists it appears in, so agreement between signals dominates and a single\n * signal's outlier magnitude cannot.\n */\nconst RRF_K = 60;\n\n/** 1-based ranks for a scored id list (desc score, qualifiedName tiebreak). */\nfunction ranksOf(scores: Map<string, number>, nameOf: (id: string) => string): Map<string, number> {\n const ids = [...scores.entries()]\n .filter(([, s]) => s > 0)\n .sort((a, b) => b[1] - a[1] || nameOf(a[0]).localeCompare(nameOf(b[0])))\n .map(([id]) => id);\n return new Map(ids.map((id, i) => [id, i + 1]));\n}\n\n/**\n * Hybrid retrieval, combined with Reciprocal Rank Fusion: the module ranking\n * (or the mechanical fallback) is one arm, the local-embedding pass the\n * other, so a question like \"where do we handle auth failures?\" can surface\n * `verify_token` even with no shared word. Deterministic given the same\n * model + cached vectors; embeddings live in a binary sidecar next to the\n * map, never in `graph.json`.\n */\nexport async function queryGraphSemantic(\n graph: VgGraph,\n question: string,\n options: SemanticQueryOptions,\n): Promise<QueryResult> {\n const budget = options.budget ?? 2000;\n const limit = options.limit ?? 12;\n const literals = extractLiteralNeedles(question);\n const forTokens = literals.length > 0 ? stripLiteralNeedles(question) : question;\n const residualTokens = tokenize(forTokens);\n // Embed the residual ask (without URL/quote noise) so semantic neighbours\n // aren't dragged toward package names that share host path segments. Skipped\n // entirely when the ranking arrives pre-computed (vgd already embedded it).\n const queryVec = options.semanticRanked\n ? null\n : await options.embedder?.embedQuery(forTokens || question);\n const index = indexFor(graph);\n\n // Lexical arm: module ranking order when available, mechanical otherwise.\n // RRF consumes only the ORDER. An honest miss contributes an empty arm.\n const lexRaw = new Map<string, number>();\n const whyById = new Map<string, string>();\n const lexSeeds = options.ranked\n ? options.ranked.hasContent\n ? options.ranked.seeds.map((s) => ({ id: s.id, score: s.score, why: s.why }))\n : []\n : mechanicalRank(graph, question, literals).map((m) => ({ id: m.node.id, score: m.score, why: m.why }));\n for (const s of lexSeeds) {\n lexRaw.set(s.id, s.score);\n whyById.set(s.id, s.why);\n }\n\n const scored: QueryMatch[] = [];\n // Literal-only asks: skip the semantic loop entirely — embeddings over\n // \"does not exist find occurrences\" still surface unrelated hubs.\n if (residualTokens.length > 0 || literals.length === 0) {\n const semRaw = new Map<string, number>();\n const nodeById = new Map<string, GraphNode>();\n for (const node of graph.nodes) {\n if (node.kind === 'file' || node.kind === 'external') continue;\n nodeById.set(node.id, node);\n if (options.semanticRanked || !queryVec) continue;\n const vec = options.nodeVectors?.get(node.id);\n const sem = vec && residualTokens.length > 0 ? Math.max(0, cosine(queryVec, vec)) : 0;\n if (sem > 0) semRaw.set(node.id, sem);\n }\n if (options.semanticRanked && residualTokens.length > 0) {\n // Ignore ids the ranking knows about but this graph no longer has: the\n // daemon's index can be one publish behind a locally refreshed map.\n for (const { id, score } of options.semanticRanked) {\n if (score > 0 && nodeById.has(id)) semRaw.set(id, score);\n }\n }\n const nameOf = (id: string) => nodeById.get(id)?.qualifiedName ?? id;\n const lexRanks = ranksOf(lexRaw, nameOf);\n const semRanks = ranksOf(semRaw, nameOf);\n for (const [id, node] of nodeById) {\n const lr = lexRanks.get(id);\n const sr = semRanks.get(id);\n if (lr === undefined && sr === undefined) continue;\n const fused = (lr !== undefined ? 1 / (RRF_K + lr) : 0) + (sr !== undefined ? 1 / (RRF_K + sr) : 0);\n const sem = semRaw.get(id) ?? 0;\n const lexWhy = whyById.get(id);\n const why = lexWhy || (sem > 0.3 ? `semantic match (${sem.toFixed(2)})` : 'weak match');\n scored.push({ node, score: round(fused * (1 + IMPORTANCE_WEIGHT * node.importance)), why });\n }\n scored.sort((a, b) => b.score - a.score || a.node.qualifiedName.localeCompare(b.node.qualifiedName));\n }\n\n const seeds = diversifyByFile(scored).slice(0, limit);\n const { context, tokensEstimate } = buildContext(graph, index, question, seeds, budget, literals);\n return { question, matches: seeds, context, tokensEstimate };\n}\n\n/**\n * camelCase / snake_case / kebab / letter↔digit split of an identifier →\n * lowercased parts. The separator alternative is Unicode-letter-aware\n * (`\\p{L}\\p{N}`, not ASCII-only) so non-Latin identifiers split on\n * punctuation without losing every character to it; the camelCase boundary\n * lookaround stays ASCII-only since casing is itself an ASCII-script concept.\n * Letter↔digit boundaries split too, so `Session2` keeps `session` reachable.\n */\nexport function identifierParts(name: string): Set<string> {\n return new Set(\n name\n .split(/[^\\p{L}\\p{N}]+|(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?<=[\\p{L}])(?=[0-9])|(?<=[0-9])(?=[\\p{L}])/u)\n .filter(Boolean)\n .map((s) => s.toLowerCase()),\n );\n}\n\nfunction buildContext(\n graph: VgGraph,\n index: GraphIndex,\n question: string,\n seeds: QueryMatch[],\n budget: number,\n literals: string[] = [],\n): { context: string; tokensEstimate: number } {\n const lines: string[] = [];\n lines.push(`# Context for: ${question}`);\n lines.push('');\n if (literals.length) {\n lines.push('## Literal string(s) to locate (exact text, not symbol names)');\n for (const lit of literals) lines.push(`- \\`${lit}\\``);\n lines.push(\n '_Use a quoted phrase / URL search (`search_symbols` or `search_code` with the exact needle) for occurrences. Graph symbols below (if any) are residual identifier hints only._',\n );\n lines.push('');\n }\n if (seeds.length === 0) {\n lines.push(\n literals.length\n ? '_No graph symbols matched residual terms — this is expected for a pure string/URL occurrence search. Run a literal sweep on the needle(s) above._'\n : '_No matching symbols found in the map. Try different terms, or `vg hubs` for the most important code._',\n );\n const text = lines.join('\\n');\n return { context: text, tokensEstimate: estimateTokens(text) };\n }\n\n for (const { node, why } of seeds) {\n const block: string[] = [];\n block.push(`## ${node.qualifiedName} (${node.kind}, ${node.file}:${node.span.start})`);\n if (node.signature) block.push('`' + node.signature + '`');\n const callees = index.callees(node.id).map((x) => x.node.qualifiedName);\n const callers = index.callers(node.id).map((x) => x.node.qualifiedName);\n if (callees.length) block.push(`calls: ${unique(callees).slice(0, 8).join(', ')}`);\n if (callers.length) block.push(`called by: ${unique(callers).slice(0, 8).join(', ')}`);\n block.push(`importance ${node.importance.toFixed(3)} · area #${node.area}${node.isHub ? ' · hub' : ''} · ${why}`);\n block.push('');\n\n const candidate = lines.concat(block).join('\\n');\n if (estimateTokens(candidate) > budget && lines.length > 2) break;\n lines.push(...block);\n }\n\n const text = lines.join('\\n');\n return { context: text, tokensEstimate: estimateTokens(text) };\n}\n\nfunction unique(xs: string[]): string[] {\n return [...new Set(xs)];\n}\n\nfunction estimateTokens(text: string): number {\n // ~4 chars per token, the standard rough estimate.\n return Math.ceil(text.length / 4);\n}\n\nfunction round(x: number): number {\n return Math.round(x * 1e6) / 1e6;\n}\n","import { indexFor } from './relations.js';\nimport type { EdgeKind, VgGraph } from '../schema.js';\n\n/**\n * Deterministic reverse-reachability impact analysis (VG-CLI-SPEC §3.5).\n *\n * \"What breaks if `<node>` changes\" = everything that (transitively) depends on\n * it: its callers, importers, and subtypes. We BFS *backwards* along dependency\n * edges, bounded by depth, with a confidence that decays with distance (direct =\n * high; transitive lower; ambiguous/dynamic edges already carry lower edge\n * confidence which we fold in). Test-impact selection (`--tests`) lands in\n * Phase 2; this is the structural blast radius.\n */\n\nconst DEPEND_EDGES = new Set<EdgeKind>(['call', 'import', 'extends', 'implements', 'references']);\n\nexport interface ImpactItem {\n id: string;\n name: string;\n kind: string;\n file: string;\n line: number;\n depth: number;\n confidence: number;\n}\n\nexport interface ImpactResult {\n root: { id: string; name: string };\n depth: number;\n affected: ImpactItem[];\n direct: number;\n transitive: number;\n /** Lowest edge confidence encountered (e.g. a dynamic-dispatch edge). */\n minEdgeConfidence: number;\n}\n\nexport function impactOf(graph: VgGraph, rootId: string, opts: { depth?: number } = {}): ImpactResult {\n const maxDepth = Math.max(1, opts.depth ?? 4);\n const index = indexFor(graph);\n const root = index.node(rootId);\n\n const affected = new Map<string, ImpactItem>();\n let minEdgeConfidence = 1;\n\n // BFS over incoming dependency edges (dependents of the frontier).\n let frontier: { id: string; conf: number }[] = [{ id: rootId, conf: 1 }];\n for (let depth = 1; depth <= maxDepth; depth++) {\n const nextFrontier: { id: string; conf: number }[] = [];\n for (const { id, conf } of frontier) {\n for (const e of index.in(id)) {\n if (!DEPEND_EDGES.has(e.kind)) continue;\n minEdgeConfidence = Math.min(minEdgeConfidence, e.confidence);\n const dependentId = e.src;\n if (dependentId === rootId || affected.has(dependentId)) continue;\n const node = index.node(dependentId);\n if (!node) continue;\n const conf2 = round(conf * e.confidence * 0.9); // decay with distance\n affected.set(dependentId, {\n id: node.id,\n name: node.qualifiedName,\n kind: node.kind,\n file: node.file,\n line: node.span.start,\n depth,\n confidence: conf2,\n });\n nextFrontier.push({ id: dependentId, conf: conf2 });\n }\n }\n frontier = nextFrontier;\n if (frontier.length === 0) break;\n }\n\n const items = [...affected.values()].sort(\n (a, b) => a.depth - b.depth || b.confidence - a.confidence || a.name.localeCompare(b.name),\n );\n\n return {\n root: { id: rootId, name: root?.qualifiedName ?? rootId },\n depth: maxDepth,\n affected: items,\n direct: items.filter((i) => i.depth === 1).length,\n transitive: items.filter((i) => i.depth > 1).length,\n minEdgeConfidence: round(minEdgeConfidence),\n };\n}\n\nfunction round(x: number): number {\n return Math.round(x * 1e6) / 1e6;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/engine/relations.ts","../src/engine/user-ask.ts","../src/engine/query.ts","../src/engine/impact.ts"],"names":["push","text","round"],"mappings":"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","file":"chunk-NQU6E6OG.js","sourcesContent":["import type { EdgeKind, GraphEdge, GraphNode, VgGraph } from '../schema.js';\n\n/** Indexes over a graph for O(1) neighbor lookups (built once per command). */\nexport class GraphIndex {\n readonly nodeById = new Map<string, GraphNode>();\n private outById = new Map<string, GraphEdge[]>();\n private inById = new Map<string, GraphEdge[]>();\n\n constructor(readonly graph: VgGraph) {\n for (const n of graph.nodes) this.nodeById.set(n.id, n);\n for (const e of graph.edges) {\n push(this.outById, e.src, e);\n push(this.inById, e.dst, e);\n }\n }\n\n out(id: string, kind?: EdgeKind): GraphEdge[] {\n const all = this.outById.get(id) ?? [];\n return kind ? all.filter((e) => e.kind === kind) : all;\n }\n\n in(id: string, kind?: EdgeKind): GraphEdge[] {\n const all = this.inById.get(id) ?? [];\n return kind ? all.filter((e) => e.kind === kind) : all;\n }\n\n node(id: string): GraphNode | undefined {\n return this.nodeById.get(id);\n }\n\n /**\n * Resolved nodes called by `id` — invocations (`call`) plus structural\n * dependency references (`references`, e.g. a constructor-injected field's\n * type), since both represent real usage a caller/impact question cares\n * about. `references` is otherwise only emitted by the precise SCIP/tsc\n * rungs and (for Java DI wiring) the heuristic rung — never a guess.\n */\n callees(id: string): { edge: GraphEdge; node: GraphNode }[] {\n return this.resolveTargets(this.out(id).filter(isUsageEdge), 'dst');\n }\n\n /** Resolved nodes that call or structurally reference `id`. */\n callers(id: string): { edge: GraphEdge; node: GraphNode }[] {\n return this.resolveTargets(this.in(id).filter(isUsageEdge), 'src');\n }\n\n private resolveTargets(edges: GraphEdge[], end: 'src' | 'dst') {\n const out: { edge: GraphEdge; node: GraphNode }[] = [];\n for (const e of edges) {\n const node = this.nodeById.get(e[end]);\n if (node) out.push({ edge: e, node });\n }\n return out;\n }\n}\n\nconst USAGE_KINDS: ReadonlySet<EdgeKind> = new Set(['call', 'references']);\nfunction isUsageEdge(e: GraphEdge): boolean {\n return USAGE_KINDS.has(e.kind);\n}\n\nfunction push(map: Map<string, GraphEdge[]>, key: string, value: GraphEdge): void {\n const list = map.get(key);\n if (list) list.push(value);\n else map.set(key, [value]);\n}\n\n/**\n * Memoized `GraphIndex`, keyed on the graph object's identity.\n *\n * The index (node-by-id + in/out adjacency) is a pure function of the graph's\n * content, and every read path rebuilds it from scratch — an O(nodes + edges)\n * pass that, on a large map, costs hundreds of milliseconds *per query*. Under\n * `vg serve` the same parsed graph object is reused across many tool calls\n * (`GraphSource` caches it by file mtime and only replaces the reference when\n * `graph.json` actually changes), so rebuilding the index on every call is pure\n * waste. Keying a `WeakMap` on that object identity builds the index once and\n * reuses it until the file is rebuilt — at which point `GraphSource` hands back a\n * *new* object, this map misses, and the stale index is collected with the old\n * graph. Correct and stale-safe by construction: a different graph is a\n * different object, so there is no cross-graph reuse and nothing to invalidate.\n *\n * `GraphIndex` is read-only (no method mutates its maps), so sharing one\n * instance across callers is safe.\n */\nconst INDEX_CACHE = new WeakMap<VgGraph, GraphIndex>();\n\nexport function indexFor(graph: VgGraph): GraphIndex {\n let idx = INDEX_CACHE.get(graph);\n if (!idx) {\n idx = new GraphIndex(graph);\n INDEX_CACHE.set(graph, idx);\n }\n return idx;\n}\n","/**\n * Split the user's ask from host-injected attachment metadata.\n *\n * The VS Code panel (`codeAttachments.ts`) and other hosts append a markdown\n * block after a `---` fence:\n *\n * ## User attachments (always include in your reasoning for this turn)\n * ### Attached image: `image.png`\n * … saved at: `.vibgrate/code-attachments/…`\n *\n * Those backticks are for the model. If the full instruction is fed into\n * {@link extractLiteralNeedles} / seed ranking, attachment basenames and paths\n * become **literal-locate hard constraints** (\"0 hits → finish, do not invent\n * an edit\") and the agent aborts a real coding task. Field report: version-badge\n * health UI with a screenshot — agent searched for `image.png` and stopped.\n *\n * Retrieval, needle pinning, locate-only detection, and relevance analysis must\n * use {@link userAskFromInstruction}. The full instruction (with attachments)\n * still belongs in the Task / user message so the model can see the appendix.\n */\n\n/** Heading written by hosts when packing panel attachments into the instruction. */\nexport const USER_ATTACHMENTS_HEADING_PREFIX = '## User attachments';\n\n/**\n * Return only the user-authored portion of an instruction, dropping a trailing\n * host \"User attachments\" appendix when present. Unchanged when no appendix.\n */\nexport function userAskFromInstruction(instruction: string): string {\n if (!instruction) return instruction;\n\n // Canonical host shape: blank line, --- fence, then the attachments heading.\n const fenced = /\\n---\\s*\\n## User attachments\\b[^\\n]*\\n?/;\n const m = fenced.exec(instruction);\n if (m && m.index != null) {\n return instruction.slice(0, m.index).trimEnd();\n }\n\n // Defensive: heading without the fence (older hosts / hand-edited prompts).\n const bare = instruction.search(/(?:^|\\n)## User attachments\\b/);\n if (bare > 0) {\n return instruction.slice(0, bare).trimEnd();\n }\n if (bare === 0) {\n // Instruction is only the appendix — no user ask left.\n return '';\n }\n\n return instruction;\n}\n","import { indexFor, type GraphIndex } from './relations.js';\nimport { cosine, type Embedder } from './embeddings.js';\nimport type { SanitizedRank } from './relevance-provider.js';\nimport type { GraphNode, VgGraph } from '../schema.js';\nimport { userAskFromInstruction } from './user-ask.js';\n\n/**\n * Retrieval front-end for `vg ask` / `vg code` capsule seeds (VG-CLI-SPEC\n * §3.2).\n *\n * Since the 2026-08 relevance relocation, the RANKING ENGINE lives in the\n * optional relevance module (`@vibgrate/relevance`, auto-provisioned): the\n * async callers run the ask through the module via\n * `relevance-provider.rankQuestion` and pass the SANITIZED result in as\n * `options.ranked`. This file keeps only what is mechanical:\n *\n * - literal string/URL needle handling (the locate path — exact-text\n * matching, not relevance);\n * - a deliberately dumb module-less fallback: exact identifier-name and\n * name-part matching, nothing that understands language (no lexicon, no\n * term roles, no IDF, no typo repair, no expansion) — enough that an ask\n * NAMING a symbol still pins its file when the module is unavailable;\n * - context-block rendering and the semantic Reciprocal Rank Fusion\n * plumbing (`--semantic`), which fuses ORDERINGS and carries no ranking\n * heuristics of its own.\n */\n\nexport interface QueryOptions {\n budget?: number; // approx token budget for the context block (default 2000)\n limit?: number; // max seed matches to expand (default 12)\n /** Sanitized module ranking (engine/relevance-provider.ts rankQuestion),\n * injected by async callers so this module stays pure and sync. When\n * absent — module not installed, predates the ranking API, or failed —\n * the mechanical fallback below answers. */\n ranked?: SanitizedRank | null;\n}\n\nexport interface QueryMatch {\n node: GraphNode;\n score: number;\n why: string;\n}\n\nexport interface QueryResult {\n question: string;\n matches: QueryMatch[];\n context: string;\n tokensEstimate: number;\n}\n\n/**\n * Function-word hygiene for the mechanical fallback tokenizer. This is NOT a\n * relevance stopword model (that moved into the module): nothing here scores,\n * ranks, weights, or reorders anything. It is a purely SUBTRACTIVE floor —\n * the words a caller uses to FRAME an ask rather than to name the code they\n * mean, which therefore cannot be identifier evidence.\n *\n * The floor exists because `mechanicalRank` treats every surviving token as\n * equal evidence, and English framing words collide with real identifier\n * parts constantly. Without it, `\"add a new feature\"` scores `AddBlogForm`\n * on the camel-part tier (`add`) and `FeatureFlagStore` (`feature`) and fills\n * the entire seed window — a grab-bag Task Capsule for an ask that named\n * nothing. That is the capsule-poisoning failure of the 2026-07 field report,\n * and an empty result is the correct answer.\n *\n * Note this is the floor ONLY. Deciding which of several genuine candidates\n * is most relevant remains the module's job, and no part of that logic —\n * term roles, weighting, expansion, morphology — belongs in this file.\n */\nconst FUNCTION_WORDS = new Set([\n // Closed-class English: could only ever match by coincidence.\n 'the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'to', 'of', 'in', 'on', 'for', 'and', 'or',\n 'where', 'what', 'which', 'how', 'do', 'does', 'did', 'i', 'we', 'it', 'this', 'that', 'with',\n 'when', 'who', 'why', 'can', 'should', 'my', 'our', 'you', 'your', 'from', 'by', 'at', 'as',\n // Process verbs. A caller says what they want DONE with these; they never\n // name the code it should be done to. Left in, bare `add` seeds every\n // Add*/*Add* symbol in the repo ahead of the terms that carry the subject.\n 'add', 'adds', 'added', 'adding',\n 'create', 'creates', 'created', 'creating',\n 'make', 'makes', 'making', 'made',\n 'remove', 'removes', 'removed', 'removing',\n 'delete', 'deletes', 'deleted', 'deleting',\n 'update', 'updates', 'updated', 'updating',\n 'change', 'changes', 'changed', 'changing',\n 'set', 'sets', 'setting', 'setup', 'get', 'gets', 'getting',\n 'use', 'uses', 'used', 'using', 'want', 'wants', 'wanted', 'doing', 'done',\n 'support', 'supports', 'supported', 'supporting',\n 'enable', 'enables', 'enabled', 'enabling',\n 'disable', 'disables', 'disabled', 'disabling',\n 'allow', 'allows', 'allowed', 'handle', 'handles', 'handling',\n 'implement', 'implements', 'implemented', 'implementing',\n 'build', 'builds', 'building', 'built', 'need', 'needs', 'needed',\n // Filler nouns and connectives: the generic scaffolding of a request.\n 'new', 'newly', 'via', 'through', 'into', 'onto',\n 'way', 'ways', 'thing', 'things', 'stuff',\n 'feature', 'features', 'functionality',\n 'method', 'methods', 'option', 'options', 'ability',\n 'form', 'forms', 'page', 'pages', 'screen', 'screens', 'button', 'buttons',\n 'help', 'start', 'begin', 'work', 'works', 'working',\n 'also', 'currently', 'properly', 'correctly', 'please',\n 'happens', 'happen', 'something', 'somewhere',\n 'step', 'steps', 'flow', 'process',\n // Discovery-question scaffolding: words that FRAME the search itself\n // (\"find the code responsible for X\", \"explain how X works\").\n 'find', 'code', 'responsible', 'modify', 'me',\n 'implementation', 'explain', 'codebase', 'contains', 'file',\n // Bug-report framing: says something is WRONG, never what is wrong.\n // `fix` alone lit up `export_fixed_width_records`, `out` lit up every\n // *Router*/*rollout* symbol (vg code prompt-relevance corpus).\n 'fix', 'fixes', 'fixing', 'bug', 'bugs', 'broken', 'busted', 'borked',\n 'wrong', 'incorrect', 'incorrectly', 'figure', 'out', 'sure',\n]);\n\n/**\n * Strip sentence punctuation from a URL match without breaking real query\n * strings (`?key=value`). A lone trailing `?` / `!` is English punctuation\n * (\"where is https://…/signup?\") — leaving it in the needle makes the literal\n * sweep miss the bare path in source.\n */\nfunction cleanUrlNeedle(raw: string): string {\n let s = raw.trim();\n // Trailing ) ] } > , ; : ! . from markdown links / sentence ends.\n s = s.replace(/[.,;:!)\\]}>]+$/g, '');\n // Lone trailing `?` → drop. Keep `?key=…` (content after `?`).\n if (s.endsWith('?')) {\n const qi = s.lastIndexOf('?');\n if (qi === s.length - 1) s = s.slice(0, -1);\n }\n return s;\n}\n\n/**\n * Extract string/URL needles a caller is asking to *locate as text*, not as\n * bag-of-words symbol terms. URLs and quoted phrases are exact-match material\n * for the literal sweep (`search_symbols` / `search_code`); feeding their path\n * segments into lexical symbol ranking produces confidently wrong seeds\n * (dash → dashboard package, exist → NonExisting, …).\n */\nexport function extractLiteralNeedles(question: string): string[] {\n const out: string[] = [];\n const seen = new Set<string>();\n const push = (raw: string, kind: 'url' | 'quote') => {\n let s = raw.trim();\n s = kind === 'url' ? cleanUrlNeedle(s) : s.replace(/[.,;:!)\\]}>?]+$/g, '');\n if (s.length < 2 || seen.has(s)) return;\n seen.add(s);\n out.push(s);\n };\n for (const m of question.matchAll(/https?:\\/\\/[^\\s\"'`<>]+/gi)) push(m[0]!, 'url');\n for (const m of question.matchAll(/([\"'`])((?:(?!\\1)[^\\\\]|\\\\.){2,})\\1/g)) {\n push(m[2]!.replace(/\\\\(.)/g, '$1'), 'quote');\n }\n return out;\n}\n\n/** Question text with URL/quoted needles removed so residual tokens can name symbols. */\nexport function stripLiteralNeedles(question: string): string {\n let s = question;\n // Strip raw URL spans first (may still carry a trailing `?` from the ask).\n for (const m of question.matchAll(/https?:\\/\\/[^\\s\"'`<>]+/gi)) {\n s = s.split(m[0]!).join(' ');\n }\n for (const lit of extractLiteralNeedles(question)) {\n s = s.split(lit).join(' ');\n }\n // Also strip bare quote pairs left behind.\n s = s.replace(/([\"'`])\\s*\\1/g, ' ');\n return s.replace(/\\s+/g, ' ').trim();\n}\n\n/**\n * Residual ask used for graph symbol ranking when the question embeds a\n * URL/quote. Empty when only framing words remain (\"where is …?\") so we do\n * not light up unrelated symbols like `applyPlan.where`.\n */\nexport function residualForSymbolSearch(question: string): string {\n const literals = extractLiteralNeedles(question);\n if (literals.length === 0) return question.trim();\n const stripped = stripLiteralNeedles(question);\n return tokenize(stripped).length === 0 ? '' : stripped;\n}\n\n/**\n * True when the instruction is a string/URL occurrence locate (not an edit).\n * Used to skip PatchIR grammar and answer from the literal sweep without\n * dumping constrained-decoding JSON into the VG Code panel.\n */\nexport function isLocateOnlyInstruction(instruction: string): boolean {\n // Drop host-injected \"User attachments\" appendix (backticked image paths)\n // before classify — same rule as buildCodeContext. Avoids treating a screenshot\n // filename as a locate-only occurrence search.\n const q = userAskFromInstruction(instruction).trim();\n if (!q) return false;\n const needles = extractLiteralNeedles(q);\n const hasLocateFrame =\n /\\b(where\\s+is|where\\s+are|find\\s+(all\\s+|every\\s+)?occurrence|find\\s+occurrences|does\\s+not\\s+exist|locate|search\\s+for)\\b/i.test(\n q,\n ) ||\n (needles.length > 0 && /^https?:\\/\\//i.test(q));\n // Strong edit verbs cancel locate-only (ignore \"does not exist\" as non-edit).\n const withoutExist = q.replace(/\\bdoes\\s+not\\s+exist\\b/gi, ' ');\n if (\n /\\b(fix|edit|change|replace|implement|refactor|rename|patch|create\\s+file|delete\\s+file|add\\s+a\\b|remove\\s+the\\b)\\b/i.test(\n withoutExist,\n )\n ) {\n return false;\n }\n if (needles.some((n) => /^https?:\\/\\//i.test(n))) return true;\n return hasLocateFrame && needles.length > 0;\n}\n\nexport function queryGraph(graph: VgGraph, question: string, options: QueryOptions = {}): QueryResult {\n const budget = options.budget ?? 2000;\n const limit = options.limit ?? 12;\n const literals = extractLiteralNeedles(question);\n const index = indexFor(graph);\n\n let seeds: QueryMatch[];\n if (options.ranked) {\n // Module ranking (already sanitized: every id names a real symbol).\n // An honest miss from the engine stays an honest miss here.\n const byId = new Map<string, GraphNode>();\n for (const node of graph.nodes) byId.set(node.id, node);\n seeds = options.ranked.hasContent\n ? options.ranked.seeds\n .map((s) => {\n const node = byId.get(s.id);\n return node ? { node, score: s.score, why: s.why } : null;\n })\n .filter((m): m is QueryMatch => m !== null)\n .slice(0, limit)\n : [];\n } else {\n seeds = mechanicalRank(graph, question, literals).slice(0, limit);\n }\n\n const { context, tokensEstimate } = buildContext(graph, index, question, seeds, budget, literals);\n return { question, matches: seeds, context, tokensEstimate };\n}\n\n/**\n * The module-less fallback: mechanical identifier matching, deliberately free\n * of language understanding. A token scores only when it IS a symbol's name,\n * one of its camel/snake parts, or (length ≥ 4) a substring of the qualified\n * name — so \"write tests for chargeCard\" or a pasted `src/…/File.ts` path\n * still pins the right file, while intent phrasings honestly miss until the\n * relevance module is available. No lexicon, no term roles, no IDF, no typo\n * repair, no expansions — that is the module's job now.\n */\nfunction mechanicalRank(graph: VgGraph, question: string, literals: string[]): QueryMatch[] {\n const forTokens = literals.length > 0 ? stripLiteralNeedles(question) : question;\n let tokens = tokenize(forTokens);\n // A literal ask's residual framing (\"does not exist\", \"find occurrences\")\n // must not light up DoesNot*/NonExisting*/commandExists lookalikes — the\n // honest-miss contract of the locate path holds module-less too.\n if (literals.length > 0) tokens = tokens.filter((t) => !LOCATE_SCAFFOLD.has(t));\n if (tokens.length === 0) return [];\n const scored: QueryMatch[] = [];\n for (const node of graph.nodes) {\n if (node.kind === 'file' || node.kind === 'external') continue;\n const name = node.name.toLowerCase();\n const qn = node.qualifiedName.toLowerCase();\n const parts = identifierParts(node.name);\n let score = 0;\n const hits: string[] = [];\n for (const t of tokens) {\n if (name === t) {\n score += 10;\n hits.push(t);\n } else if (parts.has(t)) {\n score += 6;\n hits.push(t);\n } else if (t.length >= 4 && qn.includes(t)) {\n score += 3;\n hits.push(t);\n }\n }\n if (score > 0) {\n scored.push({\n node,\n score: round(score * (1 + IMPORTANCE_WEIGHT * node.importance)),\n why: `matched: ${hits.join(', ')}`,\n });\n }\n }\n scored.sort((a, b) => b.score - a.score || a.node.qualifiedName.localeCompare(b.node.qualifiedName));\n return diversifyByFile(scored);\n}\n\nfunction tokenize(q: string): string[] {\n return [\n ...new Set(\n q\n .toLowerCase()\n .split(/[^\\p{L}\\p{N}]+/u)\n .filter((t) => t.length >= 1 && !FUNCTION_WORDS.has(t)),\n ),\n ];\n}\n\n/**\n * Importance is a mild tiebreaker, not a doubling: at 0.4 a top hub adds at\n * most 40%, enough to break genuine ties without overriding term evidence.\n */\nconst IMPORTANCE_WEIGHT = 0.4;\n\n/** Residual framing of a string/URL locate ask — never symbol evidence. */\nconst LOCATE_SCAFFOLD = new Set([\n 'not', 'no', 'exist', 'exists', 'existing', 'occurrence', 'occurrences',\n 'occurence', 'occurences', 'find', 'every', 'place', 'places', 'string',\n 'literal', 'text', 'search', 'locate', 'look', 'looking', 'show', 'list', 'all',\n]);\n\n/**\n * Stable file-diversity pass over the ranked list: each file's best node\n * keeps its rank; further nodes from an already-represented file are demoted\n * behind the first representative of every other matching file (never\n * dropped). Mechanical presentation hygiene — the seed window is a coverage\n * budget either way.\n */\nfunction diversifyByFile(matches: QueryMatch[]): QueryMatch[] {\n const seen = new Set<string>();\n const firstPerFile: QueryMatch[] = [];\n const rest: QueryMatch[] = [];\n for (const m of matches) {\n if (seen.has(m.node.file)) {\n rest.push(m);\n } else {\n seen.add(m.node.file);\n firstPerFile.push(m);\n }\n }\n return firstPerFile.concat(rest);\n}\n\nexport interface SemanticQueryOptions extends QueryOptions {\n /** Local embedder. Optional only when {@link semanticRanked} supplies the pass. */\n embedder?: Embedder;\n /** Precomputed node vectors (from getNodeEmbeddings); falls back to lexical for nodes without one. */\n nodeVectors?: Map<string, number[]>;\n /**\n * A semantic pass someone else already ran — vgd ranking the question\n * against its resident slot index. Supplied instead of `embedder` +\n * `nodeVectors` so the caller pays neither a model load nor a vector scan,\n * and no vectors cross the socket.\n *\n * Only the ORDER of this list is consumed (RRF fuses rankings, not scores),\n * so a truncated top-K is not an approximation: a candidate ranked past a\n * few hundred cannot reach a 12-row answer.\n */\n semanticRanked?: Array<{ id: string; score: number }>;\n}\n\n/**\n * Reciprocal Rank Fusion constant (the standard k=60). Fusing RANKINGS\n * instead of blending raw scores removes the need to calibrate two\n * incomparable scales: a node's fused score is Σ 1/(k + rankᵢ) over the\n * lists it appears in, so agreement between signals dominates and a single\n * signal's outlier magnitude cannot.\n */\nconst RRF_K = 60;\n\n/** 1-based ranks for a scored id list (desc score, qualifiedName tiebreak). */\nfunction ranksOf(scores: Map<string, number>, nameOf: (id: string) => string): Map<string, number> {\n const ids = [...scores.entries()]\n .filter(([, s]) => s > 0)\n .sort((a, b) => b[1] - a[1] || nameOf(a[0]).localeCompare(nameOf(b[0])))\n .map(([id]) => id);\n return new Map(ids.map((id, i) => [id, i + 1]));\n}\n\n/**\n * Hybrid retrieval, combined with Reciprocal Rank Fusion: the module ranking\n * (or the mechanical fallback) is one arm, the local-embedding pass the\n * other, so a question like \"where do we handle auth failures?\" can surface\n * `verify_token` even with no shared word. Deterministic given the same\n * model + cached vectors; embeddings live in a binary sidecar next to the\n * map, never in `graph.json`.\n */\nexport async function queryGraphSemantic(\n graph: VgGraph,\n question: string,\n options: SemanticQueryOptions,\n): Promise<QueryResult> {\n const budget = options.budget ?? 2000;\n const limit = options.limit ?? 12;\n const literals = extractLiteralNeedles(question);\n const forTokens = literals.length > 0 ? stripLiteralNeedles(question) : question;\n const residualTokens = tokenize(forTokens);\n // Embed the residual ask (without URL/quote noise) so semantic neighbours\n // aren't dragged toward package names that share host path segments. Skipped\n // entirely when the ranking arrives pre-computed (vgd already embedded it).\n const queryVec = options.semanticRanked\n ? null\n : await options.embedder?.embedQuery(forTokens || question);\n const index = indexFor(graph);\n\n // Lexical arm: module ranking order when available, mechanical otherwise.\n // RRF consumes only the ORDER. An honest miss contributes an empty arm.\n const lexRaw = new Map<string, number>();\n const whyById = new Map<string, string>();\n const lexSeeds = options.ranked\n ? options.ranked.hasContent\n ? options.ranked.seeds.map((s) => ({ id: s.id, score: s.score, why: s.why }))\n : []\n : mechanicalRank(graph, question, literals).map((m) => ({ id: m.node.id, score: m.score, why: m.why }));\n for (const s of lexSeeds) {\n lexRaw.set(s.id, s.score);\n whyById.set(s.id, s.why);\n }\n\n const scored: QueryMatch[] = [];\n // Literal-only asks: skip the semantic loop entirely — embeddings over\n // \"does not exist find occurrences\" still surface unrelated hubs.\n if (residualTokens.length > 0 || literals.length === 0) {\n const semRaw = new Map<string, number>();\n const nodeById = new Map<string, GraphNode>();\n for (const node of graph.nodes) {\n if (node.kind === 'file' || node.kind === 'external') continue;\n nodeById.set(node.id, node);\n if (options.semanticRanked || !queryVec) continue;\n const vec = options.nodeVectors?.get(node.id);\n const sem = vec && residualTokens.length > 0 ? Math.max(0, cosine(queryVec, vec)) : 0;\n if (sem > 0) semRaw.set(node.id, sem);\n }\n if (options.semanticRanked && residualTokens.length > 0) {\n // Ignore ids the ranking knows about but this graph no longer has: the\n // daemon's index can be one publish behind a locally refreshed map.\n for (const { id, score } of options.semanticRanked) {\n if (score > 0 && nodeById.has(id)) semRaw.set(id, score);\n }\n }\n const nameOf = (id: string) => nodeById.get(id)?.qualifiedName ?? id;\n const lexRanks = ranksOf(lexRaw, nameOf);\n const semRanks = ranksOf(semRaw, nameOf);\n for (const [id, node] of nodeById) {\n const lr = lexRanks.get(id);\n const sr = semRanks.get(id);\n if (lr === undefined && sr === undefined) continue;\n const fused = (lr !== undefined ? 1 / (RRF_K + lr) : 0) + (sr !== undefined ? 1 / (RRF_K + sr) : 0);\n const sem = semRaw.get(id) ?? 0;\n const lexWhy = whyById.get(id);\n const why = lexWhy || (sem > 0.3 ? `semantic match (${sem.toFixed(2)})` : 'weak match');\n scored.push({ node, score: round(fused * (1 + IMPORTANCE_WEIGHT * node.importance)), why });\n }\n scored.sort((a, b) => b.score - a.score || a.node.qualifiedName.localeCompare(b.node.qualifiedName));\n }\n\n const seeds = diversifyByFile(scored).slice(0, limit);\n const { context, tokensEstimate } = buildContext(graph, index, question, seeds, budget, literals);\n return { question, matches: seeds, context, tokensEstimate };\n}\n\n/**\n * camelCase / snake_case / kebab / letter↔digit split of an identifier →\n * lowercased parts. The separator alternative is Unicode-letter-aware\n * (`\\p{L}\\p{N}`, not ASCII-only) so non-Latin identifiers split on\n * punctuation without losing every character to it; the camelCase boundary\n * lookaround stays ASCII-only since casing is itself an ASCII-script concept.\n * Letter↔digit boundaries split too, so `Session2` keeps `session` reachable.\n */\nexport function identifierParts(name: string): Set<string> {\n return new Set(\n name\n .split(/[^\\p{L}\\p{N}]+|(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?<=[\\p{L}])(?=[0-9])|(?<=[0-9])(?=[\\p{L}])/u)\n .filter(Boolean)\n .map((s) => s.toLowerCase()),\n );\n}\n\nfunction buildContext(\n graph: VgGraph,\n index: GraphIndex,\n question: string,\n seeds: QueryMatch[],\n budget: number,\n literals: string[] = [],\n): { context: string; tokensEstimate: number } {\n const lines: string[] = [];\n lines.push(`# Context for: ${question}`);\n lines.push('');\n if (literals.length) {\n lines.push('## Literal string(s) to locate (exact text, not symbol names)');\n for (const lit of literals) lines.push(`- \\`${lit}\\``);\n lines.push(\n '_Use a quoted phrase / URL search (`search_symbols` or `search_code` with the exact needle) for occurrences. Graph symbols below (if any) are residual identifier hints only._',\n );\n lines.push('');\n }\n if (seeds.length === 0) {\n lines.push(\n literals.length\n ? '_No graph symbols matched residual terms — this is expected for a pure string/URL occurrence search. Run a literal sweep on the needle(s) above._'\n : '_No matching symbols found in the map. Try different terms, or `vg hubs` for the most important code._',\n );\n const text = lines.join('\\n');\n return { context: text, tokensEstimate: estimateTokens(text) };\n }\n\n for (const { node, why } of seeds) {\n const block: string[] = [];\n block.push(`## ${node.qualifiedName} (${node.kind}, ${node.file}:${node.span.start})`);\n if (node.signature) block.push('`' + node.signature + '`');\n const callees = index.callees(node.id).map((x) => x.node.qualifiedName);\n const callers = index.callers(node.id).map((x) => x.node.qualifiedName);\n if (callees.length) block.push(`calls: ${unique(callees).slice(0, 8).join(', ')}`);\n if (callers.length) block.push(`called by: ${unique(callers).slice(0, 8).join(', ')}`);\n block.push(`importance ${node.importance.toFixed(3)} · area #${node.area}${node.isHub ? ' · hub' : ''} · ${why}`);\n block.push('');\n\n const candidate = lines.concat(block).join('\\n');\n if (estimateTokens(candidate) > budget && lines.length > 2) break;\n lines.push(...block);\n }\n\n const text = lines.join('\\n');\n return { context: text, tokensEstimate: estimateTokens(text) };\n}\n\nfunction unique(xs: string[]): string[] {\n return [...new Set(xs)];\n}\n\nfunction estimateTokens(text: string): number {\n // ~4 chars per token, the standard rough estimate.\n return Math.ceil(text.length / 4);\n}\n\nfunction round(x: number): number {\n return Math.round(x * 1e6) / 1e6;\n}\n","import { indexFor } from './relations.js';\nimport type { EdgeKind, VgGraph } from '../schema.js';\n\n/**\n * Deterministic reverse-reachability impact analysis (VG-CLI-SPEC §3.5).\n *\n * \"What breaks if `<node>` changes\" = everything that (transitively) depends on\n * it: its callers, importers, and subtypes. We BFS *backwards* along dependency\n * edges, bounded by depth, with a confidence that decays with distance (direct =\n * high; transitive lower; ambiguous/dynamic edges already carry lower edge\n * confidence which we fold in). Test-impact selection (`--tests`) lands in\n * Phase 2; this is the structural blast radius.\n */\n\nconst DEPEND_EDGES = new Set<EdgeKind>(['call', 'import', 'extends', 'implements', 'references']);\n\nexport interface ImpactItem {\n id: string;\n name: string;\n kind: string;\n file: string;\n line: number;\n depth: number;\n confidence: number;\n}\n\nexport interface ImpactResult {\n root: { id: string; name: string };\n depth: number;\n affected: ImpactItem[];\n direct: number;\n transitive: number;\n /** Lowest edge confidence encountered (e.g. a dynamic-dispatch edge). */\n minEdgeConfidence: number;\n}\n\nexport function impactOf(graph: VgGraph, rootId: string, opts: { depth?: number } = {}): ImpactResult {\n const maxDepth = Math.max(1, opts.depth ?? 4);\n const index = indexFor(graph);\n const root = index.node(rootId);\n\n const affected = new Map<string, ImpactItem>();\n let minEdgeConfidence = 1;\n\n // BFS over incoming dependency edges (dependents of the frontier).\n let frontier: { id: string; conf: number }[] = [{ id: rootId, conf: 1 }];\n for (let depth = 1; depth <= maxDepth; depth++) {\n const nextFrontier: { id: string; conf: number }[] = [];\n for (const { id, conf } of frontier) {\n for (const e of index.in(id)) {\n if (!DEPEND_EDGES.has(e.kind)) continue;\n minEdgeConfidence = Math.min(minEdgeConfidence, e.confidence);\n const dependentId = e.src;\n if (dependentId === rootId || affected.has(dependentId)) continue;\n const node = index.node(dependentId);\n if (!node) continue;\n const conf2 = round(conf * e.confidence * 0.9); // decay with distance\n affected.set(dependentId, {\n id: node.id,\n name: node.qualifiedName,\n kind: node.kind,\n file: node.file,\n line: node.span.start,\n depth,\n confidence: conf2,\n });\n nextFrontier.push({ id: dependentId, conf: conf2 });\n }\n }\n frontier = nextFrontier;\n if (frontier.length === 0) break;\n }\n\n const items = [...affected.values()].sort(\n (a, b) => a.depth - b.depth || b.confidence - a.confidence || a.name.localeCompare(b.name),\n );\n\n return {\n root: { id: rootId, name: root?.qualifiedName ?? rootId },\n depth: maxDepth,\n affected: items,\n direct: items.filter((i) => i.depth === 1).length,\n transitive: items.filter((i) => i.depth > 1).length,\n minEdgeConfidence: round(minEdgeConfidence),\n };\n}\n\nfunction round(x: number): number {\n return Math.round(x * 1e6) / 1e6;\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ensureVibgrateGitignore } from './chunk-
|
|
1
|
+
import { ensureVibgrateGitignore } from './chunk-K2IWOZEQ.js';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
|
|
@@ -170,5 +170,5 @@ function findMatchingRule(action, rules) {
|
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
export { addApprovalRule, approvalsPath, findMatchingRule, globMatch, loadApprovalRules, normalizeCommandPrefix, removeApprovalRule, ruleForAction, ruleKey, ruleLabel, saveApprovalRules };
|
|
173
|
-
//# sourceMappingURL=chunk-
|
|
174
|
-
//# sourceMappingURL=chunk-
|
|
173
|
+
//# sourceMappingURL=chunk-PEHMRPYM.js.map
|
|
174
|
+
//# sourceMappingURL=chunk-PEHMRPYM.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/code/approvals.ts"],"names":[],"mappings":"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","file":"chunk-QTQRM7HQ.js","sourcesContent":["/**\n * Allow-always approval rules (P2 — permission parity).\n *\n * The approval gate exists to review state changes, but reviewing the *same*\n * change shape every time (`pnpm test`, edits to a scratch dir) is friction\n * without safety. A user can promote an approval to a standing rule\n * (\"Always allow\"); rules live in `.vibgrate/code-approvals.json` at the repo\n * root so the CLI and VS Code honour the same set, and are re-read on every\n * approval so a revocation (from the VG Code manage UI or by editing the\n * file) takes effect immediately — no session restart, no stale cache.\n *\n * Rules never widen safety: {@link dangerousCommand} still screens every rule\n * match, plan mode still denies everything engine-side before rules are\n * consulted, and a rule-approved action is still announced (notice event) and\n * checkpointed like any human-approved change.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { ensureVibgrateGitignore } from '../engine/artifacts.js';\nimport type { MutatingAction, PatchFileAction } from './tools.js';\n\n/** One standing approval. Discriminated by `kind`, mirroring MutatingAction. */\nexport type ApprovalRule =\n /** Matches `run` actions whose normalized command starts with these tokens. */\n | { kind: 'run'; prefix: string }\n /** Matches a single-file action of that op when the path matches the glob. */\n | { kind: 'edit' | 'create' | 'delete'; glob: string }\n /** Matches external `tool` calls by tool name. */\n | { kind: 'tool'; tool: string };\n\nexport function approvalsPath(root: string): string {\n return path.join(root, '.vibgrate', 'code-approvals.json');\n}\n\n/**\n * The stable identity of a rule — used for dedupe and for revocation from\n * hosts that only carry the display string.\n */\nexport function ruleKey(rule: ApprovalRule): string {\n if (rule.kind === 'run') return `run:${rule.prefix}`;\n if (rule.kind === 'tool') return `tool:${rule.tool}`;\n return `${rule.kind}:${rule.glob}`;\n}\n\n/** Human-readable label for notices and manage UIs. */\nexport function ruleLabel(rule: ApprovalRule): string {\n if (rule.kind === 'run') return `run \\`${rule.prefix} …\\``;\n if (rule.kind === 'tool') return `call ${rule.tool}`;\n return `${rule.kind} ${rule.glob}`;\n}\n\n/**\n * Load the rule set, tolerating absence and malformed JSON (an unreadable file\n * means \"no rules\", never a crash — same posture as `.vibgrate/code.json`).\n * Unknown shapes are dropped so an untrusted file cannot inject junk.\n */\nexport function loadApprovalRules(root: string): ApprovalRule[] {\n let raw: unknown;\n try {\n raw = JSON.parse(fs.readFileSync(approvalsPath(root), 'utf8'));\n } catch {\n return [];\n }\n const list = Array.isArray(raw)\n ? raw\n : raw && typeof raw === 'object' && Array.isArray((raw as { rules?: unknown }).rules)\n ? ((raw as { rules: unknown[] }).rules)\n : [];\n const out: ApprovalRule[] = [];\n for (const item of list) {\n const rule = sanitizeRule(item);\n if (rule) out.push(rule);\n }\n return dedupe(out);\n}\n\nfunction sanitizeRule(item: unknown): ApprovalRule | null {\n if (!item || typeof item !== 'object') return null;\n const o = item as Record<string, unknown>;\n if (o.kind === 'run' && typeof o.prefix === 'string' && o.prefix.trim()) {\n return { kind: 'run', prefix: normalizeCommandPrefix(o.prefix) };\n }\n if (o.kind === 'tool' && typeof o.tool === 'string' && o.tool.trim()) {\n return { kind: 'tool', tool: o.tool.trim() };\n }\n if ((o.kind === 'edit' || o.kind === 'create' || o.kind === 'delete') && typeof o.glob === 'string' && o.glob.trim()) {\n return { kind: o.kind, glob: o.glob.trim() };\n }\n return null;\n}\n\nfunction dedupe(rules: ApprovalRule[]): ApprovalRule[] {\n const seen = new Set<string>();\n const out: ApprovalRule[] = [];\n for (const r of rules) {\n const key = ruleKey(r);\n if (seen.has(key)) continue;\n seen.add(key);\n out.push(r);\n }\n return out;\n}\n\n/**\n * Persist the rule set, deterministically sorted so identical sets produce an\n * identical file. Best-effort like other `.vibgrate/` writers — a read-only\n * checkout means no standing rules, never a thrown error mid-approval.\n */\nexport function saveApprovalRules(root: string, rules: ApprovalRule[]): boolean {\n try {\n const dir = path.join(root, '.vibgrate');\n fs.mkdirSync(dir, { recursive: true });\n // First writer into .vibgrate/ creates its .gitignore (create-once): one\n // developer's standing consents must not ride into the team's repo.\n ensureVibgrateGitignore(root);\n const sorted = dedupe(rules).slice().sort((a, b) => ruleKey(a).localeCompare(ruleKey(b)));\n fs.writeFileSync(approvalsPath(root), `${JSON.stringify({ rules: sorted }, null, 2)}\\n`);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Add one rule (deduped). Returns the saved rule, or null when nothing was written. */\nexport function addApprovalRule(root: string, rule: ApprovalRule): ApprovalRule | null {\n const rules = loadApprovalRules(root);\n if (rules.some((r) => ruleKey(r) === ruleKey(rule))) return rule;\n rules.push(rule);\n return saveApprovalRules(root, rules) ? rule : null;\n}\n\n/** Remove the rule with this {@link ruleKey}. Returns true when one was removed and saved. */\nexport function removeApprovalRule(root: string, key: string): boolean {\n const rules = loadApprovalRules(root);\n const kept = rules.filter((r) => ruleKey(r) !== key);\n if (kept.length === rules.length) return false;\n return saveApprovalRules(root, kept);\n}\n\n/**\n * Normalize a shell command to its rule prefix: the first two tokens for the\n * `cmd subcommand [flags…]` shape (`git status`, `pnpm test --watch`), the\n * single token for a one-word command, and the *whole* command\n * (whitespace-normalized) in every other case. Collapsing `rm -f temp.log` to\n * bare `rm` — or `rm foo.log secrets.env` to `rm foo.log` — would turn one\n * blessed invocation into a standing grant covering paths the user never saw\n * (2026-08 audit, D2), so a command with a flag second-token or positional\n * arguments beyond the subcommand keeps its exact form.\n */\nexport function normalizeCommandPrefix(command: string): string {\n const tokens = command.trim().split(/\\s+/).filter(Boolean);\n const head = tokens[0] ?? '';\n const second = tokens[1];\n if (!second) return head;\n if (second.startsWith('-')) return tokens.join(' ');\n // Extra positionals after `cmd subcommand` make the rule exact; trailing\n // flags still collapse to the extensible two-token prefix.\n if (tokens.slice(2).some((t) => !t.startsWith('-'))) return tokens.join(' ');\n return `${head} ${second}`;\n}\n\n/** True when a rule prefix is the extensible `cmd [subcommand]` shape (no flags). */\nfunction extensiblePrefix(prefix: string): boolean {\n return !prefix.split(' ').some((t) => t.startsWith('-'));\n}\n\n/**\n * Shell constructs that can smuggle a second command into what looks like a\n * flag: command substitution, redirects, sequencing, backgrounding, subshells.\n * A `pnpm test` rule must not bless `pnpm test --reporter=$(curl … | sh)` —\n * the appended token is flag-shaped, but its *value* is another program the\n * user never saw. Same screen {@link readOnlyCommand} applies; kept local so\n * safety and approvals stay independent modules.\n */\nconst SHELL_METACHARS = /[><;&|`\\n(){}$]/;\n\n/**\n * Derive the standing rule an \"Always allow\" on this action should create.\n * Patch actions return null — an atomic multi-file transaction has no single\n * honest generalisation, so hosts do not offer Always on patch cards.\n */\nexport function ruleForAction(action: MutatingAction): ApprovalRule | null {\n switch (action.kind) {\n case 'run':\n return { kind: 'run', prefix: normalizeCommandPrefix(action.command) };\n case 'tool':\n return { kind: 'tool', tool: action.name };\n case 'edit':\n case 'create':\n case 'delete':\n return { kind: action.kind, glob: action.file };\n case 'patch':\n return null;\n }\n}\n\n/**\n * Minimal glob match for rule paths: `*` matches within a segment, `**`\n * across segments, `?` one character. Deterministic, no dependency, and\n * conservative — a malformed pattern simply never matches.\n */\nexport function globMatch(glob: string, file: string): boolean {\n let re = '';\n for (let i = 0; i < glob.length; i++) {\n const c = glob[i];\n if (c === '*') {\n if (glob[i + 1] === '*') {\n // `**/` or trailing `**` — match across segments.\n re += '.*';\n i += glob[i + 2] === '/' ? 2 : 1;\n } else {\n re += '[^/]*';\n }\n } else if (c === '?') {\n re += '[^/]';\n } else if ('\\\\^$.|+()[]{}'.includes(c)) {\n re += `\\\\${c}`;\n } else {\n re += c;\n }\n }\n try {\n return new RegExp(`^${re}$`).test(file);\n } catch {\n return false;\n }\n}\n\nfunction fileRuleMatches(rules: ApprovalRule[], op: 'edit' | 'create' | 'delete', file: string): ApprovalRule | null {\n for (const r of rules) {\n if (r.kind === op && globMatch(r.glob, file)) return r;\n }\n return null;\n}\n\n/**\n * The first rule matching this action, or null. A `patch` matches only when\n * *every* file in the transaction is covered by a matching file rule — an\n * atomic approval must never be half-earned.\n */\nexport function findMatchingRule(action: MutatingAction, rules: ApprovalRule[]): ApprovalRule | null {\n if (!rules.length) return null;\n switch (action.kind) {\n case 'run': {\n // Match the FULL whitespace-normalized command, never a truncated form:\n // collapsing `rm foo.log secrets.env` to its first two tokens before\n // comparing would silently discard the appended argument and let a rule\n // saved for `rm foo.log` bless it (2026-08 audit, D2).\n const full = action.command.trim().split(/\\s+/).filter(Boolean).join(' ');\n for (const r of rules) {\n if (r.kind !== 'run') continue;\n if (full === r.prefix) return r;\n // A `cmd [subcommand]` prefix extends to longer commands only when\n // every appended token is a flag (`git status --short`). Appended\n // positionals (extra paths, script arguments) are new state the user\n // never blessed — they re-prompt, and can be Always-allowed exactly.\n if (extensiblePrefix(r.prefix) && full.startsWith(`${r.prefix} `)) {\n const tail = full.slice(r.prefix.length + 1);\n // A flag whose value is a subshell is not an extension of the blessed\n // command, it is a different one — screen the appended text before\n // the flag-shape check, never after.\n if (SHELL_METACHARS.test(tail)) continue;\n const extension = tail.split(' ');\n if (extension.every((t) => t.startsWith('-'))) return r;\n }\n }\n return null;\n }\n case 'tool':\n return rules.find((r) => r.kind === 'tool' && r.tool === action.name) ?? null;\n case 'edit':\n case 'create':\n case 'delete':\n return fileRuleMatches(rules, action.kind, action.file);\n case 'patch': {\n const covered = action.files.every((f: PatchFileAction) => fileRuleMatches(rules, f.op, f.file));\n if (!covered) return null;\n return fileRuleMatches(rules, action.files[0]!.op, action.files[0]!.file);\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/code/approvals.ts"],"names":[],"mappings":"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","file":"chunk-PEHMRPYM.js","sourcesContent":["/**\n * Allow-always approval rules (P2 — permission parity).\n *\n * The approval gate exists to review state changes, but reviewing the *same*\n * change shape every time (`pnpm test`, edits to a scratch dir) is friction\n * without safety. A user can promote an approval to a standing rule\n * (\"Always allow\"); rules live in `.vibgrate/code-approvals.json` at the repo\n * root so the CLI and VS Code honour the same set, and are re-read on every\n * approval so a revocation (from the VG Code manage UI or by editing the\n * file) takes effect immediately — no session restart, no stale cache.\n *\n * Rules never widen safety: {@link dangerousCommand} still screens every rule\n * match, plan mode still denies everything engine-side before rules are\n * consulted, and a rule-approved action is still announced (notice event) and\n * checkpointed like any human-approved change.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { ensureVibgrateGitignore } from '../engine/artifacts.js';\nimport type { MutatingAction, PatchFileAction } from './tools.js';\n\n/** One standing approval. Discriminated by `kind`, mirroring MutatingAction. */\nexport type ApprovalRule =\n /** Matches `run` actions whose normalized command starts with these tokens. */\n | { kind: 'run'; prefix: string }\n /** Matches a single-file action of that op when the path matches the glob. */\n | { kind: 'edit' | 'create' | 'delete'; glob: string }\n /** Matches external `tool` calls by tool name. */\n | { kind: 'tool'; tool: string };\n\nexport function approvalsPath(root: string): string {\n return path.join(root, '.vibgrate', 'code-approvals.json');\n}\n\n/**\n * The stable identity of a rule — used for dedupe and for revocation from\n * hosts that only carry the display string.\n */\nexport function ruleKey(rule: ApprovalRule): string {\n if (rule.kind === 'run') return `run:${rule.prefix}`;\n if (rule.kind === 'tool') return `tool:${rule.tool}`;\n return `${rule.kind}:${rule.glob}`;\n}\n\n/** Human-readable label for notices and manage UIs. */\nexport function ruleLabel(rule: ApprovalRule): string {\n if (rule.kind === 'run') return `run \\`${rule.prefix} …\\``;\n if (rule.kind === 'tool') return `call ${rule.tool}`;\n return `${rule.kind} ${rule.glob}`;\n}\n\n/**\n * Load the rule set, tolerating absence and malformed JSON (an unreadable file\n * means \"no rules\", never a crash — same posture as `.vibgrate/code.json`).\n * Unknown shapes are dropped so an untrusted file cannot inject junk.\n */\nexport function loadApprovalRules(root: string): ApprovalRule[] {\n let raw: unknown;\n try {\n raw = JSON.parse(fs.readFileSync(approvalsPath(root), 'utf8'));\n } catch {\n return [];\n }\n const list = Array.isArray(raw)\n ? raw\n : raw && typeof raw === 'object' && Array.isArray((raw as { rules?: unknown }).rules)\n ? ((raw as { rules: unknown[] }).rules)\n : [];\n const out: ApprovalRule[] = [];\n for (const item of list) {\n const rule = sanitizeRule(item);\n if (rule) out.push(rule);\n }\n return dedupe(out);\n}\n\nfunction sanitizeRule(item: unknown): ApprovalRule | null {\n if (!item || typeof item !== 'object') return null;\n const o = item as Record<string, unknown>;\n if (o.kind === 'run' && typeof o.prefix === 'string' && o.prefix.trim()) {\n return { kind: 'run', prefix: normalizeCommandPrefix(o.prefix) };\n }\n if (o.kind === 'tool' && typeof o.tool === 'string' && o.tool.trim()) {\n return { kind: 'tool', tool: o.tool.trim() };\n }\n if ((o.kind === 'edit' || o.kind === 'create' || o.kind === 'delete') && typeof o.glob === 'string' && o.glob.trim()) {\n return { kind: o.kind, glob: o.glob.trim() };\n }\n return null;\n}\n\nfunction dedupe(rules: ApprovalRule[]): ApprovalRule[] {\n const seen = new Set<string>();\n const out: ApprovalRule[] = [];\n for (const r of rules) {\n const key = ruleKey(r);\n if (seen.has(key)) continue;\n seen.add(key);\n out.push(r);\n }\n return out;\n}\n\n/**\n * Persist the rule set, deterministically sorted so identical sets produce an\n * identical file. Best-effort like other `.vibgrate/` writers — a read-only\n * checkout means no standing rules, never a thrown error mid-approval.\n */\nexport function saveApprovalRules(root: string, rules: ApprovalRule[]): boolean {\n try {\n const dir = path.join(root, '.vibgrate');\n fs.mkdirSync(dir, { recursive: true });\n // First writer into .vibgrate/ creates its .gitignore (create-once): one\n // developer's standing consents must not ride into the team's repo.\n ensureVibgrateGitignore(root);\n const sorted = dedupe(rules).slice().sort((a, b) => ruleKey(a).localeCompare(ruleKey(b)));\n fs.writeFileSync(approvalsPath(root), `${JSON.stringify({ rules: sorted }, null, 2)}\\n`);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Add one rule (deduped). Returns the saved rule, or null when nothing was written. */\nexport function addApprovalRule(root: string, rule: ApprovalRule): ApprovalRule | null {\n const rules = loadApprovalRules(root);\n if (rules.some((r) => ruleKey(r) === ruleKey(rule))) return rule;\n rules.push(rule);\n return saveApprovalRules(root, rules) ? rule : null;\n}\n\n/** Remove the rule with this {@link ruleKey}. Returns true when one was removed and saved. */\nexport function removeApprovalRule(root: string, key: string): boolean {\n const rules = loadApprovalRules(root);\n const kept = rules.filter((r) => ruleKey(r) !== key);\n if (kept.length === rules.length) return false;\n return saveApprovalRules(root, kept);\n}\n\n/**\n * Normalize a shell command to its rule prefix: the first two tokens for the\n * `cmd subcommand [flags…]` shape (`git status`, `pnpm test --watch`), the\n * single token for a one-word command, and the *whole* command\n * (whitespace-normalized) in every other case. Collapsing `rm -f temp.log` to\n * bare `rm` — or `rm foo.log secrets.env` to `rm foo.log` — would turn one\n * blessed invocation into a standing grant covering paths the user never saw\n * (2026-08 audit, D2), so a command with a flag second-token or positional\n * arguments beyond the subcommand keeps its exact form.\n */\nexport function normalizeCommandPrefix(command: string): string {\n const tokens = command.trim().split(/\\s+/).filter(Boolean);\n const head = tokens[0] ?? '';\n const second = tokens[1];\n if (!second) return head;\n if (second.startsWith('-')) return tokens.join(' ');\n // Extra positionals after `cmd subcommand` make the rule exact; trailing\n // flags still collapse to the extensible two-token prefix.\n if (tokens.slice(2).some((t) => !t.startsWith('-'))) return tokens.join(' ');\n return `${head} ${second}`;\n}\n\n/** True when a rule prefix is the extensible `cmd [subcommand]` shape (no flags). */\nfunction extensiblePrefix(prefix: string): boolean {\n return !prefix.split(' ').some((t) => t.startsWith('-'));\n}\n\n/**\n * Shell constructs that can smuggle a second command into what looks like a\n * flag: command substitution, redirects, sequencing, backgrounding, subshells.\n * A `pnpm test` rule must not bless `pnpm test --reporter=$(curl … | sh)` —\n * the appended token is flag-shaped, but its *value* is another program the\n * user never saw. Same screen {@link readOnlyCommand} applies; kept local so\n * safety and approvals stay independent modules.\n */\nconst SHELL_METACHARS = /[><;&|`\\n(){}$]/;\n\n/**\n * Derive the standing rule an \"Always allow\" on this action should create.\n * Patch actions return null — an atomic multi-file transaction has no single\n * honest generalisation, so hosts do not offer Always on patch cards.\n */\nexport function ruleForAction(action: MutatingAction): ApprovalRule | null {\n switch (action.kind) {\n case 'run':\n return { kind: 'run', prefix: normalizeCommandPrefix(action.command) };\n case 'tool':\n return { kind: 'tool', tool: action.name };\n case 'edit':\n case 'create':\n case 'delete':\n return { kind: action.kind, glob: action.file };\n case 'patch':\n return null;\n }\n}\n\n/**\n * Minimal glob match for rule paths: `*` matches within a segment, `**`\n * across segments, `?` one character. Deterministic, no dependency, and\n * conservative — a malformed pattern simply never matches.\n */\nexport function globMatch(glob: string, file: string): boolean {\n let re = '';\n for (let i = 0; i < glob.length; i++) {\n const c = glob[i];\n if (c === '*') {\n if (glob[i + 1] === '*') {\n // `**/` or trailing `**` — match across segments.\n re += '.*';\n i += glob[i + 2] === '/' ? 2 : 1;\n } else {\n re += '[^/]*';\n }\n } else if (c === '?') {\n re += '[^/]';\n } else if ('\\\\^$.|+()[]{}'.includes(c)) {\n re += `\\\\${c}`;\n } else {\n re += c;\n }\n }\n try {\n return new RegExp(`^${re}$`).test(file);\n } catch {\n return false;\n }\n}\n\nfunction fileRuleMatches(rules: ApprovalRule[], op: 'edit' | 'create' | 'delete', file: string): ApprovalRule | null {\n for (const r of rules) {\n if (r.kind === op && globMatch(r.glob, file)) return r;\n }\n return null;\n}\n\n/**\n * The first rule matching this action, or null. A `patch` matches only when\n * *every* file in the transaction is covered by a matching file rule — an\n * atomic approval must never be half-earned.\n */\nexport function findMatchingRule(action: MutatingAction, rules: ApprovalRule[]): ApprovalRule | null {\n if (!rules.length) return null;\n switch (action.kind) {\n case 'run': {\n // Match the FULL whitespace-normalized command, never a truncated form:\n // collapsing `rm foo.log secrets.env` to its first two tokens before\n // comparing would silently discard the appended argument and let a rule\n // saved for `rm foo.log` bless it (2026-08 audit, D2).\n const full = action.command.trim().split(/\\s+/).filter(Boolean).join(' ');\n for (const r of rules) {\n if (r.kind !== 'run') continue;\n if (full === r.prefix) return r;\n // A `cmd [subcommand]` prefix extends to longer commands only when\n // every appended token is a flag (`git status --short`). Appended\n // positionals (extra paths, script arguments) are new state the user\n // never blessed — they re-prompt, and can be Always-allowed exactly.\n if (extensiblePrefix(r.prefix) && full.startsWith(`${r.prefix} `)) {\n const tail = full.slice(r.prefix.length + 1);\n // A flag whose value is a subshell is not an extension of the blessed\n // command, it is a different one — screen the appended text before\n // the flag-shape check, never after.\n if (SHELL_METACHARS.test(tail)) continue;\n const extension = tail.split(' ');\n if (extension.every((t) => t.startsWith('-'))) return r;\n }\n }\n return null;\n }\n case 'tool':\n return rules.find((r) => r.kind === 'tool' && r.tool === action.name) ?? null;\n case 'edit':\n case 'create':\n case 'delete':\n return fileRuleMatches(rules, action.kind, action.file);\n case 'patch': {\n const covered = action.files.every((f: PatchFileAction) => fileRuleMatches(rules, f.op, f.file));\n if (!covered) return null;\n return fileRuleMatches(rules, action.files[0]!.op, action.files[0]!.file);\n }\n }\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveIngestHost } from './chunk-HTLDXEHW.js';
|
|
2
2
|
import { cacheDir } from './chunk-MVPRPSXU.js';
|
|
3
|
-
import { VERSION } from './chunk-
|
|
3
|
+
import { VERSION } from './chunk-JQ7OL4XG.js';
|
|
4
4
|
import * as fs from 'fs';
|
|
5
5
|
import * as os from 'os';
|
|
6
6
|
import * as path from 'path';
|
|
@@ -212,5 +212,5 @@ var StatsSharer = class {
|
|
|
212
212
|
};
|
|
213
213
|
|
|
214
214
|
export { StatsSharer, installId, isCI, statsEndpoint, telemetryOptOut };
|
|
215
|
-
//# sourceMappingURL=chunk-
|
|
216
|
-
//# sourceMappingURL=chunk-
|
|
215
|
+
//# sourceMappingURL=chunk-PIB3YNJU.js.map
|
|
216
|
+
//# sourceMappingURL=chunk-PIB3YNJU.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/engine/stats-share.ts"],"names":[],"mappings":"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","file":"chunk-M7O6IVKN.js","sourcesContent":["import * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport { cacheDir } from './cache.js';\nimport { VERSION } from '../version.js';\nimport { resolveIngestHost } from '../reporting/regions.js';\nimport type { SavingEntry, Source, Outcome } from './savings.js';\n\n/**\n * Opt-in usage-stats sharing for `vg serve --share-stats`.\n *\n * This is the ONLY path in `vg serve` that sends anything off the machine, and\n * it is off unless the operator explicitly asks for it. What we send is the\n * same counts-only ledger `vg savings` reports locally — per (source, client,\n * tool, outcome): how many calls and the vg-vs-grep token figures — plus the vg\n * version, OS/arch, and a random per-install id. We never send code, file paths,\n * question text, repo identity, or any credential. See GUARDRAILS §3.4.\n *\n * Even when the flag is passed, the universal `DO_NOT_TRACK` opt-out (or\n * `VIBGRATE_TELEMETRY=0`) wins: nothing is uploaded and no install id is ever\n * minted or persisted (see `telemetryOptOut` / `installId`).\n *\n * Design:\n * - The local ledger (savings.jsonl) is the single source of truth; the flusher\n * reads only the tail past a persisted byte offset, so a batch is sent once.\n * - Fire-and-forget with a short timeout. A network problem never blocks a tool\n * call and never throws — the offset only advances on a confirmed 2xx, so a\n * failed batch is retried on the next flush.\n * - Fully offline-safe: under `--local` the sharer is never constructed.\n */\n\n/** Payload schema version — must match the server's `mcpUsageStatsSchema`. */\nconst SCHEMA_VERSION = '1';\n/** Where the offset (and cached install id) live, per repo, beside the ledger. */\nconst OFFSET_FILE = 'stats-share.json';\n/** The machine-global anonymous install id lives here (stable across repos). */\nconst INSTALL_ID_DIR = path.join(os.homedir(), '.vibgrate');\nconst INSTALL_ID_FILE = path.join(INSTALL_ID_DIR, 'install-id');\n/** Give a flush POST a hard ceiling so a hung endpoint never stalls shutdown. */\nconst SEND_TIMEOUT_MS = 4000;\n\n/** One aggregated row of the shared batch — counts only. */\nexport interface StatsRow {\n source: Source;\n client: string;\n tool: string;\n outcome: Outcome;\n calls: number;\n vgTokens: number;\n baselineTokens: number;\n}\n\n/** The counts-only batch POSTed to the ingest endpoint. */\nexport interface StatsBatch {\n schemaVersion: string;\n installId: string;\n vgVersion: string;\n os: string;\n arch: string;\n windowStart: string;\n windowEnd: string;\n totalCalls: number;\n rows: StatsRow[];\n}\n\n/**\n * The universal opt-out (https://consoledonottrack.com) plus a tool-specific\n * alias, honoured even when `--share-stats` is passed explicitly. Pure function\n * of the environment for testability. Returns the name of the variable that\n * opted out (for the disclosure message), or null when sharing may proceed.\n */\nexport function telemetryOptOut(env: NodeJS.ProcessEnv = process.env): string | null {\n const dnt = (env.DO_NOT_TRACK ?? '').trim().toLowerCase();\n if (dnt !== '' && dnt !== '0' && dnt !== 'false') return 'DO_NOT_TRACK';\n const vt = (env.VIBGRATE_TELEMETRY ?? '').trim().toLowerCase();\n if (vt === '0' || vt === 'false' || vt === 'off') return 'VIBGRATE_TELEMETRY';\n return null;\n}\n\n/** Common CI environment markers — a CI runner is never an interactive session. */\nconst CI_ENV_VARS = [\n 'CI',\n 'GITHUB_ACTIONS',\n 'GITLAB_CI',\n 'CIRCLECI',\n 'TRAVIS',\n 'BUILDKITE',\n 'JENKINS_URL',\n 'TF_BUILD',\n] as const;\n\n/** True when running under a recognised CI system. Pure function of the env. */\nexport function isCI(env: NodeJS.ProcessEnv = process.env): boolean {\n return CI_ENV_VARS.some((v) => {\n const val = (env[v] ?? '').trim().toLowerCase();\n return val !== '' && val !== '0' && val !== 'false';\n });\n}\n\n/**\n * Read (or lazily create) the anonymous per-install id — a random UUID stored in\n * `~/.vibgrate/install-id`. It is *not* tied to the user, the repo, or any\n * account; it only lets us de-duplicate batches from the same install when\n * aggregating. If the home dir isn't writable we fall back to an ephemeral id\n * so sharing still works (it just can't be de-duplicated across restarts).\n *\n * Under an env opt-out no id is ever read or persisted (the sharing path isn't\n * reached then either — this is defence in depth), and on CI runners the id is\n * ephemeral: a persisted id on a shared/throwaway runner would be meaningless\n * for de-duplication and would leave state behind on machines we don't own.\n */\nexport function installId(env: NodeJS.ProcessEnv = process.env): string {\n if (telemetryOptOut(env) !== null || isCI(env)) return randomUuid();\n try {\n const existing = fs.readFileSync(INSTALL_ID_FILE, 'utf8').trim();\n if (isUuid(existing)) return existing;\n } catch {\n /* not created yet */\n }\n const id = randomUuid();\n try {\n fs.mkdirSync(INSTALL_ID_DIR, { recursive: true });\n fs.writeFileSync(INSTALL_ID_FILE, id + '\\n', { mode: 0o600 });\n } catch {\n /* home dir read-only — use the ephemeral id for this process */\n }\n return id;\n}\n\nfunction isUuid(s: string): boolean {\n return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s);\n}\n\nfunction randomUuid(): string {\n return randomUUID();\n}\n\n/** Resolve the ingest endpoint: env override wins, else the default region host. */\nexport function statsEndpoint(): string {\n const override = process.env.VIBGRATE_STATS_ENDPOINT;\n if (override && /^https?:\\/\\//.test(override)) return override;\n return `https://${resolveIngestHost()}/v1/ingest/cli-mcp-usage`;\n}\n\nfunction offsetPath(root: string): string {\n return path.join(cacheDir(root), OFFSET_FILE);\n}\n\nfunction ledgerFilePath(root: string): string {\n return path.join(cacheDir(root), 'savings.jsonl');\n}\n\nfunction readOffset(root: string): number {\n try {\n const raw = JSON.parse(fs.readFileSync(offsetPath(root), 'utf8')) as { offset?: number };\n return typeof raw.offset === 'number' && raw.offset >= 0 ? raw.offset : 0;\n } catch {\n return 0;\n }\n}\n\nfunction writeOffset(root: string, offset: number): void {\n try {\n fs.mkdirSync(cacheDir(root), { recursive: true });\n fs.writeFileSync(offsetPath(root), JSON.stringify({ offset }) + '\\n');\n } catch {\n /* best-effort; a lost offset just means one batch may re-send */\n }\n}\n\n/**\n * Aggregate the ledger entries after `fromOffset` into a counts-only batch.\n * Returns the batch (or null if there's nothing new) and the new byte offset to\n * persist once the batch is confirmed sent.\n */\nexport function buildBatch(\n root: string,\n fromOffset: number,\n id = installId(),\n): { batch: StatsBatch | null; newOffset: number } {\n const file = ledgerFilePath(root);\n let size = 0;\n try {\n size = fs.statSync(file).size;\n } catch {\n return { batch: null, newOffset: 0 };\n }\n // The ledger was truncated/recreated under us — re-read from the top.\n const start = fromOffset > size ? 0 : fromOffset;\n if (start >= size) return { batch: null, newOffset: size };\n\n let text = '';\n try {\n const fd = fs.openSync(file, 'r');\n try {\n const buf = Buffer.alloc(size - start);\n fs.readSync(fd, buf, 0, buf.length, start);\n text = buf.toString('utf8');\n } finally {\n fs.closeSync(fd);\n }\n } catch {\n return { batch: null, newOffset: fromOffset };\n }\n\n // Only whole lines are complete; a trailing partial line (mid-append) is left\n // for the next flush by rewinding the offset to the last newline.\n const lastNl = text.lastIndexOf('\\n');\n if (lastNl < 0) return { batch: null, newOffset: start };\n const consumed = start + lastNl + 1;\n const rows = new Map<string, StatsRow>();\n let total = 0;\n let minTs = Infinity;\n let maxTs = -Infinity;\n for (const line of text.slice(0, lastNl).split('\\n')) {\n if (!line.trim()) continue;\n let e: SavingEntry;\n try {\n e = JSON.parse(line) as SavingEntry;\n } catch {\n continue;\n }\n const source: Source = e.source ?? 'mcp';\n const client = e.client ?? 'unknown';\n const outcome: Outcome = e.outcome ?? 'complete';\n const key = `${source}|${client}|${e.tool}|${outcome}`;\n const row = rows.get(key) ?? {\n source,\n client,\n tool: e.tool,\n outcome,\n calls: 0,\n vgTokens: 0,\n baselineTokens: 0,\n };\n row.calls++;\n row.vgTokens += e.vgTokens ?? 0;\n row.baselineTokens += e.baselineTokens ?? 0;\n rows.set(key, row);\n total++;\n if (typeof e.ts === 'number') {\n if (e.ts < minTs) minTs = e.ts;\n if (e.ts > maxTs) maxTs = e.ts;\n }\n }\n if (total === 0 || !Number.isFinite(minTs)) return { batch: null, newOffset: consumed };\n const batch: StatsBatch = {\n schemaVersion: SCHEMA_VERSION,\n installId: id,\n vgVersion: VERSION,\n os: process.platform,\n arch: process.arch,\n windowStart: new Date(minTs).toISOString(),\n windowEnd: new Date(maxTs).toISOString(),\n totalCalls: total,\n rows: [...rows.values()],\n };\n return { batch, newOffset: consumed };\n}\n\n/**\n * POST a batch to the endpoint. Never throws; returns whether it was accepted.\n * A short timeout keeps a hung endpoint from stalling the caller (e.g. shutdown).\n */\nasync function postBatch(batch: StatsBatch): Promise<boolean> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), SEND_TIMEOUT_MS);\n timer.unref?.();\n try {\n const res = await fetch(statsEndpoint(), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Connection: 'close' },\n body: JSON.stringify(batch),\n signal: controller.signal,\n });\n return res.ok;\n } catch {\n return false;\n } finally {\n clearTimeout(timer);\n }\n}\n\n/**\n * Periodic + on-shutdown flusher for one serve session. Holds the install id and\n * the persisted offset so each flush reads only new ledger lines and advances the\n * offset only when the batch is confirmed sent.\n */\nexport class StatsSharer {\n private readonly id = installId();\n private offset: number;\n private flushing = false;\n\n constructor(private readonly root: string) {\n this.offset = readOffset(root);\n }\n\n /**\n * Flush any new ledger entries. Single-flight (a slow POST won't overlap the\n * next tick). Best-effort: any failure is swallowed and retried next time.\n */\n async flush(): Promise<void> {\n if (this.flushing) return;\n this.flushing = true;\n try {\n const { batch, newOffset } = buildBatch(this.root, this.offset, this.id);\n if (!batch) {\n // Nothing to send, but still advance past consumed blank/partial space.\n if (newOffset > this.offset) {\n this.offset = newOffset;\n writeOffset(this.root, newOffset);\n }\n return;\n }\n const ok = await postBatch(batch);\n if (ok) {\n this.offset = newOffset;\n writeOffset(this.root, newOffset);\n }\n } catch {\n /* never let sharing break serving */\n } finally {\n this.flushing = false;\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/engine/stats-share.ts"],"names":[],"mappings":";;;;;;;;AAiCA,IAAM,cAAA,GAAiB,GAAA;AAEvB,IAAM,WAAA,GAAc,kBAAA;AAEpB,IAAM,cAAA,GAAsB,IAAA,CAAA,IAAA,CAAQ,EAAA,CAAA,OAAA,EAAQ,EAAG,WAAW,CAAA;AAC1D,IAAM,eAAA,GAAuB,IAAA,CAAA,IAAA,CAAK,cAAA,EAAgB,YAAY,CAAA;AAE9D,IAAM,eAAA,GAAkB,GAAA;AAgCjB,SAAS,eAAA,CAAgB,GAAA,GAAyB,OAAA,CAAQ,GAAA,EAAoB;AACnF,EAAA,MAAM,OAAO,GAAA,CAAI,YAAA,IAAgB,EAAA,EAAI,IAAA,GAAO,WAAA,EAAY;AACxD,EAAA,IAAI,QAAQ,EAAA,IAAM,GAAA,KAAQ,GAAA,IAAO,GAAA,KAAQ,SAAS,OAAO,cAAA;AACzD,EAAA,MAAM,MAAM,GAAA,CAAI,kBAAA,IAAsB,EAAA,EAAI,IAAA,GAAO,WAAA,EAAY;AAC7D,EAAA,IAAI,OAAO,GAAA,IAAO,EAAA,KAAO,OAAA,IAAW,EAAA,KAAO,OAAO,OAAO,oBAAA;AACzD,EAAA,OAAO,IAAA;AACT;AAGA,IAAM,WAAA,GAAc;AAAA,EAClB,IAAA;AAAA,EACA,gBAAA;AAAA,EACA,WAAA;AAAA,EACA,UAAA;AAAA,EACA,QAAA;AAAA,EACA,WAAA;AAAA,EACA,aAAA;AAAA,EACA;AACF,CAAA;AAGO,SAAS,IAAA,CAAK,GAAA,GAAyB,OAAA,CAAQ,GAAA,EAAc;AAClE,EAAA,OAAO,WAAA,CAAY,IAAA,CAAK,CAAC,CAAA,KAAM;AAC7B,IAAA,MAAM,OAAO,GAAA,CAAI,CAAC,KAAK,EAAA,EAAI,IAAA,GAAO,WAAA,EAAY;AAC9C,IAAA,OAAO,GAAA,KAAQ,EAAA,IAAM,GAAA,KAAQ,GAAA,IAAO,GAAA,KAAQ,OAAA;AAAA,EAC9C,CAAC,CAAA;AACH;AAcO,SAAS,SAAA,CAAU,GAAA,GAAyB,OAAA,CAAQ,GAAA,EAAa;AACtE,EAAA,IAAI,eAAA,CAAgB,GAAG,CAAA,KAAM,IAAA,IAAQ,KAAK,GAAG,CAAA,SAAU,UAAA,EAAW;AAClE,EAAA,IAAI;AACF,IAAA,MAAM,QAAA,GAAc,EAAA,CAAA,YAAA,CAAa,eAAA,EAAiB,MAAM,EAAE,IAAA,EAAK;AAC/D,IAAA,IAAI,MAAA,CAAO,QAAQ,CAAA,EAAG,OAAO,QAAA;AAAA,EAC/B,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,MAAM,KAAK,UAAA,EAAW;AACtB,EAAA,IAAI;AACF,IAAG,EAAA,CAAA,SAAA,CAAU,cAAA,EAAgB,EAAE,SAAA,EAAW,MAAM,CAAA;AAChD,IAAG,iBAAc,eAAA,EAAiB,EAAA,GAAK,MAAM,EAAE,IAAA,EAAM,KAAO,CAAA;AAAA,EAC9D,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,OAAO,EAAA;AACT;AAEA,SAAS,OAAO,CAAA,EAAoB;AAClC,EAAA,OAAO,iEAAA,CAAkE,KAAK,CAAC,CAAA;AACjF;AAEA,SAAS,UAAA,GAAqB;AAC5B,EAAA,OAAO,UAAA,EAAW;AACpB;AAGO,SAAS,aAAA,GAAwB;AACtC,EAAA,MAAM,QAAA,GAAW,QAAQ,GAAA,CAAI,uBAAA;AAC7B,EAAA,IAAI,QAAA,IAAY,cAAA,CAAe,IAAA,CAAK,QAAQ,GAAG,OAAO,QAAA;AACtD,EAAA,OAAO,CAAA,QAAA,EAAW,mBAAmB,CAAA,wBAAA,CAAA;AACvC;AAEA,SAAS,WAAW,IAAA,EAAsB;AACxC,EAAA,OAAY,IAAA,CAAA,IAAA,CAAK,QAAA,CAAS,IAAI,CAAA,EAAG,WAAW,CAAA;AAC9C;AAEA,SAAS,eAAe,IAAA,EAAsB;AAC5C,EAAA,OAAY,IAAA,CAAA,IAAA,CAAK,QAAA,CAAS,IAAI,CAAA,EAAG,eAAe,CAAA;AAClD;AAEA,SAAS,WAAW,IAAA,EAAsB;AACxC,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,KAAK,KAAA,CAAS,EAAA,CAAA,YAAA,CAAa,WAAW,IAAI,CAAA,EAAG,MAAM,CAAC,CAAA;AAChE,IAAA,OAAO,OAAO,IAAI,MAAA,KAAW,QAAA,IAAY,IAAI,MAAA,IAAU,CAAA,GAAI,IAAI,MAAA,GAAS,CAAA;AAAA,EAC1E,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,CAAA;AAAA,EACT;AACF;AAEA,SAAS,WAAA,CAAY,MAAc,MAAA,EAAsB;AACvD,EAAA,IAAI;AACF,IAAG,aAAU,QAAA,CAAS,IAAI,GAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AAChD,IAAG,EAAA,CAAA,aAAA,CAAc,UAAA,CAAW,IAAI,CAAA,EAAG,IAAA,CAAK,UAAU,EAAE,MAAA,EAAQ,CAAA,GAAI,IAAI,CAAA;AAAA,EACtE,CAAA,CAAA,MAAQ;AAAA,EAER;AACF;AAOO,SAAS,UAAA,CACd,IAAA,EACA,UAAA,EACA,EAAA,GAAK,WAAU,EACkC;AACjD,EAAA,MAAM,IAAA,GAAO,eAAe,IAAI,CAAA;AAChC,EAAA,IAAI,IAAA,GAAO,CAAA;AACX,EAAA,IAAI;AACF,IAAA,IAAA,GAAU,EAAA,CAAA,QAAA,CAAS,IAAI,CAAA,CAAE,IAAA;AAAA,EAC3B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAE,KAAA,EAAO,IAAA,EAAM,SAAA,EAAW,CAAA,EAAE;AAAA,EACrC;AAEA,EAAA,MAAM,KAAA,GAAQ,UAAA,GAAa,IAAA,GAAO,CAAA,GAAI,UAAA;AACtC,EAAA,IAAI,SAAS,IAAA,EAAM,OAAO,EAAE,KAAA,EAAO,IAAA,EAAM,WAAW,IAAA,EAAK;AAEzD,EAAA,IAAI,IAAA,GAAO,EAAA;AACX,EAAA,IAAI;AACF,IAAA,MAAM,EAAA,GAAQ,EAAA,CAAA,QAAA,CAAS,IAAA,EAAM,GAAG,CAAA;AAChC,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,MAAA,CAAO,KAAA,CAAM,IAAA,GAAO,KAAK,CAAA;AACrC,MAAG,YAAS,EAAA,EAAI,GAAA,EAAK,CAAA,EAAG,GAAA,CAAI,QAAQ,KAAK,CAAA;AACzC,MAAA,IAAA,GAAO,GAAA,CAAI,SAAS,MAAM,CAAA;AAAA,IAC5B,CAAA,SAAE;AACA,MAAG,aAAU,EAAE,CAAA;AAAA,IACjB;AAAA,EACF,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAE,KAAA,EAAO,IAAA,EAAM,SAAA,EAAW,UAAA,EAAW;AAAA,EAC9C;AAIA,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,WAAA,CAAY,IAAI,CAAA;AACpC,EAAA,IAAI,SAAS,CAAA,EAAG,OAAO,EAAE,KAAA,EAAO,IAAA,EAAM,WAAW,KAAA,EAAM;AACvD,EAAA,MAAM,QAAA,GAAW,QAAQ,MAAA,GAAS,CAAA;AAClC,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAsB;AACvC,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,IAAI,KAAA,GAAQ,QAAA;AACZ,EAAA,IAAI,KAAA,GAAQ,CAAA,QAAA;AACZ,EAAA,KAAA,MAAW,IAAA,IAAQ,KAAK,KAAA,CAAM,CAAA,EAAG,MAAM,CAAA,CAAE,KAAA,CAAM,IAAI,CAAA,EAAG;AACpD,IAAA,IAAI,CAAC,IAAA,CAAK,IAAA,EAAK,EAAG;AAClB,IAAA,IAAI,CAAA;AACJ,IAAA,IAAI;AACF,MAAA,CAAA,GAAI,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,IACrB,CAAA,CAAA,MAAQ;AACN,MAAA;AAAA,IACF;AACA,IAAA,MAAM,MAAA,GAAiB,EAAE,MAAA,IAAU,KAAA;AACnC,IAAA,MAAM,MAAA,GAAS,EAAE,MAAA,IAAU,SAAA;AAC3B,IAAA,MAAM,OAAA,GAAmB,EAAE,OAAA,IAAW,UAAA;AACtC,IAAA,MAAM,GAAA,GAAM,GAAG,MAAM,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA,EAAI,CAAA,CAAE,IAAI,CAAA,CAAA,EAAI,OAAO,CAAA,CAAA;AACpD,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA,IAAK;AAAA,MAC3B,MAAA;AAAA,MACA,MAAA;AAAA,MACA,MAAM,CAAA,CAAE,IAAA;AAAA,MACR,OAAA;AAAA,MACA,KAAA,EAAO,CAAA;AAAA,MACP,QAAA,EAAU,CAAA;AAAA,MACV,cAAA,EAAgB;AAAA,KAClB;AACA,IAAA,GAAA,CAAI,KAAA,EAAA;AACJ,IAAA,GAAA,CAAI,QAAA,IAAY,EAAE,QAAA,IAAY,CAAA;AAC9B,IAAA,GAAA,CAAI,cAAA,IAAkB,EAAE,cAAA,IAAkB,CAAA;AAC1C,IAAA,IAAA,CAAK,GAAA,CAAI,KAAK,GAAG,CAAA;AACjB,IAAA,KAAA,EAAA;AACA,IAAA,IAAI,OAAO,CAAA,CAAE,EAAA,KAAO,QAAA,EAAU;AAC5B,MAAA,IAAI,CAAA,CAAE,EAAA,GAAK,KAAA,EAAO,KAAA,GAAQ,CAAA,CAAE,EAAA;AAC5B,MAAA,IAAI,CAAA,CAAE,EAAA,GAAK,KAAA,EAAO,KAAA,GAAQ,CAAA,CAAE,EAAA;AAAA,IAC9B;AAAA,EACF;AACA,EAAA,IAAI,KAAA,KAAU,CAAA,IAAK,CAAC,MAAA,CAAO,QAAA,CAAS,KAAK,CAAA,EAAG,OAAO,EAAE,KAAA,EAAO,IAAA,EAAM,SAAA,EAAW,QAAA,EAAS;AACtF,EAAA,MAAM,KAAA,GAAoB;AAAA,IACxB,aAAA,EAAe,cAAA;AAAA,IACf,SAAA,EAAW,EAAA;AAAA,IACX,SAAA,EAAW,OAAA;AAAA,IACX,IAAI,OAAA,CAAQ,QAAA;AAAA,IACZ,MAAM,OAAA,CAAQ,IAAA;AAAA,IACd,WAAA,EAAa,IAAI,IAAA,CAAK,KAAK,EAAE,WAAA,EAAY;AAAA,IACzC,SAAA,EAAW,IAAI,IAAA,CAAK,KAAK,EAAE,WAAA,EAAY;AAAA,IACvC,UAAA,EAAY,KAAA;AAAA,IACZ,IAAA,EAAM,CAAC,GAAG,IAAA,CAAK,QAAQ;AAAA,GACzB;AACA,EAAA,OAAO,EAAE,KAAA,EAAO,SAAA,EAAW,QAAA,EAAS;AACtC;AAMA,eAAe,UAAU,KAAA,EAAqC;AAC5D,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,MAAM,QAAQ,UAAA,CAAW,MAAM,UAAA,CAAW,KAAA,IAAS,eAAe,CAAA;AAClE,EAAA,KAAA,CAAM,KAAA,IAAQ;AACd,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,aAAA,EAAc,EAAG;AAAA,MACvC,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAoB,YAAY,OAAA,EAAQ;AAAA,MACnE,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA;AAAA,MAC1B,QAAQ,UAAA,CAAW;AAAA,KACpB,CAAA;AACD,IAAA,OAAO,GAAA,CAAI,EAAA;AAAA,EACb,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT,CAAA,SAAE;AACA,IAAA,YAAA,CAAa,KAAK,CAAA;AAAA,EACpB;AACF;AAOO,IAAM,cAAN,MAAkB;AAAA,EAKvB,YAA6B,IAAA,EAAc;AAAd,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAC3B,IAAA,IAAA,CAAK,MAAA,GAAS,WAAW,IAAI,CAAA;AAAA,EAC/B;AAAA,EAF6B,IAAA;AAAA,EAJZ,KAAK,SAAA,EAAU;AAAA,EACxB,MAAA;AAAA,EACA,QAAA,GAAW,KAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUnB,MAAM,KAAA,GAAuB;AAC3B,IAAA,IAAI,KAAK,QAAA,EAAU;AACnB,IAAA,IAAA,CAAK,QAAA,GAAW,IAAA;AAChB,IAAA,IAAI;AACF,MAAA,MAAM,EAAE,KAAA,EAAO,SAAA,EAAU,GAAI,UAAA,CAAW,KAAK,IAAA,EAAM,IAAA,CAAK,MAAA,EAAQ,IAAA,CAAK,EAAE,CAAA;AACvE,MAAA,IAAI,CAAC,KAAA,EAAO;AAEV,QAAA,IAAI,SAAA,GAAY,KAAK,MAAA,EAAQ;AAC3B,UAAA,IAAA,CAAK,MAAA,GAAS,SAAA;AACd,UAAA,WAAA,CAAY,IAAA,CAAK,MAAM,SAAS,CAAA;AAAA,QAClC;AACA,QAAA;AAAA,MACF;AACA,MAAA,MAAM,EAAA,GAAK,MAAM,SAAA,CAAU,KAAK,CAAA;AAChC,MAAA,IAAI,EAAA,EAAI;AACN,QAAA,IAAA,CAAK,MAAA,GAAS,SAAA;AACd,QAAA,WAAA,CAAY,IAAA,CAAK,MAAM,SAAS,CAAA;AAAA,MAClC;AAAA,IACF,CAAA,CAAA,MAAQ;AAAA,IAER,CAAA,SAAE;AACA,MAAA,IAAA,CAAK,QAAA,GAAW,KAAA;AAAA,IAClB;AAAA,EACF;AACF","file":"chunk-PIB3YNJU.js","sourcesContent":["import * as fs from 'node:fs';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport { cacheDir } from './cache.js';\nimport { VERSION } from '../version.js';\nimport { resolveIngestHost } from '../reporting/regions.js';\nimport type { SavingEntry, Source, Outcome } from './savings.js';\n\n/**\n * Opt-in usage-stats sharing for `vg serve --share-stats`.\n *\n * This is the ONLY path in `vg serve` that sends anything off the machine, and\n * it is off unless the operator explicitly asks for it. What we send is the\n * same counts-only ledger `vg savings` reports locally — per (source, client,\n * tool, outcome): how many calls and the vg-vs-grep token figures — plus the vg\n * version, OS/arch, and a random per-install id. We never send code, file paths,\n * question text, repo identity, or any credential. See GUARDRAILS §3.4.\n *\n * Even when the flag is passed, the universal `DO_NOT_TRACK` opt-out (or\n * `VIBGRATE_TELEMETRY=0`) wins: nothing is uploaded and no install id is ever\n * minted or persisted (see `telemetryOptOut` / `installId`).\n *\n * Design:\n * - The local ledger (savings.jsonl) is the single source of truth; the flusher\n * reads only the tail past a persisted byte offset, so a batch is sent once.\n * - Fire-and-forget with a short timeout. A network problem never blocks a tool\n * call and never throws — the offset only advances on a confirmed 2xx, so a\n * failed batch is retried on the next flush.\n * - Fully offline-safe: under `--local` the sharer is never constructed.\n */\n\n/** Payload schema version — must match the server's `mcpUsageStatsSchema`. */\nconst SCHEMA_VERSION = '1';\n/** Where the offset (and cached install id) live, per repo, beside the ledger. */\nconst OFFSET_FILE = 'stats-share.json';\n/** The machine-global anonymous install id lives here (stable across repos). */\nconst INSTALL_ID_DIR = path.join(os.homedir(), '.vibgrate');\nconst INSTALL_ID_FILE = path.join(INSTALL_ID_DIR, 'install-id');\n/** Give a flush POST a hard ceiling so a hung endpoint never stalls shutdown. */\nconst SEND_TIMEOUT_MS = 4000;\n\n/** One aggregated row of the shared batch — counts only. */\nexport interface StatsRow {\n source: Source;\n client: string;\n tool: string;\n outcome: Outcome;\n calls: number;\n vgTokens: number;\n baselineTokens: number;\n}\n\n/** The counts-only batch POSTed to the ingest endpoint. */\nexport interface StatsBatch {\n schemaVersion: string;\n installId: string;\n vgVersion: string;\n os: string;\n arch: string;\n windowStart: string;\n windowEnd: string;\n totalCalls: number;\n rows: StatsRow[];\n}\n\n/**\n * The universal opt-out (https://consoledonottrack.com) plus a tool-specific\n * alias, honoured even when `--share-stats` is passed explicitly. Pure function\n * of the environment for testability. Returns the name of the variable that\n * opted out (for the disclosure message), or null when sharing may proceed.\n */\nexport function telemetryOptOut(env: NodeJS.ProcessEnv = process.env): string | null {\n const dnt = (env.DO_NOT_TRACK ?? '').trim().toLowerCase();\n if (dnt !== '' && dnt !== '0' && dnt !== 'false') return 'DO_NOT_TRACK';\n const vt = (env.VIBGRATE_TELEMETRY ?? '').trim().toLowerCase();\n if (vt === '0' || vt === 'false' || vt === 'off') return 'VIBGRATE_TELEMETRY';\n return null;\n}\n\n/** Common CI environment markers — a CI runner is never an interactive session. */\nconst CI_ENV_VARS = [\n 'CI',\n 'GITHUB_ACTIONS',\n 'GITLAB_CI',\n 'CIRCLECI',\n 'TRAVIS',\n 'BUILDKITE',\n 'JENKINS_URL',\n 'TF_BUILD',\n] as const;\n\n/** True when running under a recognised CI system. Pure function of the env. */\nexport function isCI(env: NodeJS.ProcessEnv = process.env): boolean {\n return CI_ENV_VARS.some((v) => {\n const val = (env[v] ?? '').trim().toLowerCase();\n return val !== '' && val !== '0' && val !== 'false';\n });\n}\n\n/**\n * Read (or lazily create) the anonymous per-install id — a random UUID stored in\n * `~/.vibgrate/install-id`. It is *not* tied to the user, the repo, or any\n * account; it only lets us de-duplicate batches from the same install when\n * aggregating. If the home dir isn't writable we fall back to an ephemeral id\n * so sharing still works (it just can't be de-duplicated across restarts).\n *\n * Under an env opt-out no id is ever read or persisted (the sharing path isn't\n * reached then either — this is defence in depth), and on CI runners the id is\n * ephemeral: a persisted id on a shared/throwaway runner would be meaningless\n * for de-duplication and would leave state behind on machines we don't own.\n */\nexport function installId(env: NodeJS.ProcessEnv = process.env): string {\n if (telemetryOptOut(env) !== null || isCI(env)) return randomUuid();\n try {\n const existing = fs.readFileSync(INSTALL_ID_FILE, 'utf8').trim();\n if (isUuid(existing)) return existing;\n } catch {\n /* not created yet */\n }\n const id = randomUuid();\n try {\n fs.mkdirSync(INSTALL_ID_DIR, { recursive: true });\n fs.writeFileSync(INSTALL_ID_FILE, id + '\\n', { mode: 0o600 });\n } catch {\n /* home dir read-only — use the ephemeral id for this process */\n }\n return id;\n}\n\nfunction isUuid(s: string): boolean {\n return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s);\n}\n\nfunction randomUuid(): string {\n return randomUUID();\n}\n\n/** Resolve the ingest endpoint: env override wins, else the default region host. */\nexport function statsEndpoint(): string {\n const override = process.env.VIBGRATE_STATS_ENDPOINT;\n if (override && /^https?:\\/\\//.test(override)) return override;\n return `https://${resolveIngestHost()}/v1/ingest/cli-mcp-usage`;\n}\n\nfunction offsetPath(root: string): string {\n return path.join(cacheDir(root), OFFSET_FILE);\n}\n\nfunction ledgerFilePath(root: string): string {\n return path.join(cacheDir(root), 'savings.jsonl');\n}\n\nfunction readOffset(root: string): number {\n try {\n const raw = JSON.parse(fs.readFileSync(offsetPath(root), 'utf8')) as { offset?: number };\n return typeof raw.offset === 'number' && raw.offset >= 0 ? raw.offset : 0;\n } catch {\n return 0;\n }\n}\n\nfunction writeOffset(root: string, offset: number): void {\n try {\n fs.mkdirSync(cacheDir(root), { recursive: true });\n fs.writeFileSync(offsetPath(root), JSON.stringify({ offset }) + '\\n');\n } catch {\n /* best-effort; a lost offset just means one batch may re-send */\n }\n}\n\n/**\n * Aggregate the ledger entries after `fromOffset` into a counts-only batch.\n * Returns the batch (or null if there's nothing new) and the new byte offset to\n * persist once the batch is confirmed sent.\n */\nexport function buildBatch(\n root: string,\n fromOffset: number,\n id = installId(),\n): { batch: StatsBatch | null; newOffset: number } {\n const file = ledgerFilePath(root);\n let size = 0;\n try {\n size = fs.statSync(file).size;\n } catch {\n return { batch: null, newOffset: 0 };\n }\n // The ledger was truncated/recreated under us — re-read from the top.\n const start = fromOffset > size ? 0 : fromOffset;\n if (start >= size) return { batch: null, newOffset: size };\n\n let text = '';\n try {\n const fd = fs.openSync(file, 'r');\n try {\n const buf = Buffer.alloc(size - start);\n fs.readSync(fd, buf, 0, buf.length, start);\n text = buf.toString('utf8');\n } finally {\n fs.closeSync(fd);\n }\n } catch {\n return { batch: null, newOffset: fromOffset };\n }\n\n // Only whole lines are complete; a trailing partial line (mid-append) is left\n // for the next flush by rewinding the offset to the last newline.\n const lastNl = text.lastIndexOf('\\n');\n if (lastNl < 0) return { batch: null, newOffset: start };\n const consumed = start + lastNl + 1;\n const rows = new Map<string, StatsRow>();\n let total = 0;\n let minTs = Infinity;\n let maxTs = -Infinity;\n for (const line of text.slice(0, lastNl).split('\\n')) {\n if (!line.trim()) continue;\n let e: SavingEntry;\n try {\n e = JSON.parse(line) as SavingEntry;\n } catch {\n continue;\n }\n const source: Source = e.source ?? 'mcp';\n const client = e.client ?? 'unknown';\n const outcome: Outcome = e.outcome ?? 'complete';\n const key = `${source}|${client}|${e.tool}|${outcome}`;\n const row = rows.get(key) ?? {\n source,\n client,\n tool: e.tool,\n outcome,\n calls: 0,\n vgTokens: 0,\n baselineTokens: 0,\n };\n row.calls++;\n row.vgTokens += e.vgTokens ?? 0;\n row.baselineTokens += e.baselineTokens ?? 0;\n rows.set(key, row);\n total++;\n if (typeof e.ts === 'number') {\n if (e.ts < minTs) minTs = e.ts;\n if (e.ts > maxTs) maxTs = e.ts;\n }\n }\n if (total === 0 || !Number.isFinite(minTs)) return { batch: null, newOffset: consumed };\n const batch: StatsBatch = {\n schemaVersion: SCHEMA_VERSION,\n installId: id,\n vgVersion: VERSION,\n os: process.platform,\n arch: process.arch,\n windowStart: new Date(minTs).toISOString(),\n windowEnd: new Date(maxTs).toISOString(),\n totalCalls: total,\n rows: [...rows.values()],\n };\n return { batch, newOffset: consumed };\n}\n\n/**\n * POST a batch to the endpoint. Never throws; returns whether it was accepted.\n * A short timeout keeps a hung endpoint from stalling the caller (e.g. shutdown).\n */\nasync function postBatch(batch: StatsBatch): Promise<boolean> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), SEND_TIMEOUT_MS);\n timer.unref?.();\n try {\n const res = await fetch(statsEndpoint(), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Connection: 'close' },\n body: JSON.stringify(batch),\n signal: controller.signal,\n });\n return res.ok;\n } catch {\n return false;\n } finally {\n clearTimeout(timer);\n }\n}\n\n/**\n * Periodic + on-shutdown flusher for one serve session. Holds the install id and\n * the persisted offset so each flush reads only new ledger lines and advances the\n * offset only when the batch is confirmed sent.\n */\nexport class StatsSharer {\n private readonly id = installId();\n private offset: number;\n private flushing = false;\n\n constructor(private readonly root: string) {\n this.offset = readOffset(root);\n }\n\n /**\n * Flush any new ledger entries. Single-flight (a slow POST won't overlap the\n * next tick). Best-effort: any failure is swallowed and retried next time.\n */\n async flush(): Promise<void> {\n if (this.flushing) return;\n this.flushing = true;\n try {\n const { batch, newOffset } = buildBatch(this.root, this.offset, this.id);\n if (!batch) {\n // Nothing to send, but still advance past consumed blank/partial space.\n if (newOffset > this.offset) {\n this.offset = newOffset;\n writeOffset(this.root, newOffset);\n }\n return;\n }\n const ok = await postBatch(batch);\n if (ok) {\n this.offset = newOffset;\n writeOffset(this.root, newOffset);\n }\n } catch {\n /* never let sharing break serving */\n } finally {\n this.flushing = false;\n }\n }\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { inventory, installedTreeVersion } from './chunk-6BEBJCK4.js';
|
|
2
2
|
import { findNodes } from './chunk-J7LKEB2R.js';
|
|
3
|
-
import { extractLiteralNeedles, residualForSymbolSearch } from './chunk-
|
|
3
|
+
import { extractLiteralNeedles, residualForSymbolSearch } from './chunk-NQU6E6OG.js';
|
|
4
4
|
import { stableStringify } from './chunk-O3LABX6I.js';
|
|
5
5
|
import { hashString } from './chunk-JFGYT6BI.js';
|
|
6
6
|
import * as fs2 from 'fs';
|
|
@@ -1103,5 +1103,5 @@ function parseRgFileList(stdout, sep3 = path2.sep) {
|
|
|
1103
1103
|
}
|
|
1104
1104
|
|
|
1105
1105
|
export { addLibrary, catalogPath, driftFor, libDir, libId, loadCatalog, localApiSurface, localPackageDocs, readDoc, resolveLib, resolveVersion, saveCatalog, searchSymbols };
|
|
1106
|
-
//# sourceMappingURL=chunk-
|
|
1107
|
-
//# sourceMappingURL=chunk-
|
|
1106
|
+
//# sourceMappingURL=chunk-QONHVC4H.js.map
|
|
1107
|
+
//# sourceMappingURL=chunk-QONHVC4H.js.map
|