@orquentra/codebuddy 2.15.1 → 2.15.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/ARCHITECTURE.md +11 -6
  2. package/dist/{chunk-VBDM7HC5.js → chunk-3L5IR6OW.js} +2 -2
  3. package/dist/{chunk-VBDM7HC5.js.map → chunk-3L5IR6OW.js.map} +1 -1
  4. package/dist/{chunk-AFXWCSQ4.js → chunk-3TZNQJTH.js} +3 -3
  5. package/dist/{chunk-RKF4HBBQ.js → chunk-4LMBZWIF.js} +47 -12
  6. package/dist/chunk-4LMBZWIF.js.map +1 -0
  7. package/dist/{chunk-RGIVHKBH.js → chunk-4TKKLQLH.js} +2 -2
  8. package/dist/{chunk-OO7Q7XZF.js → chunk-5ZLNHYJH.js} +30 -14
  9. package/dist/chunk-5ZLNHYJH.js.map +1 -0
  10. package/dist/{chunk-VF7DKRIU.js → chunk-6KSSJJZO.js} +8 -7
  11. package/dist/chunk-6KSSJJZO.js.map +1 -0
  12. package/dist/{chunk-MNSMW4MZ.js → chunk-7IXPARDM.js} +3 -3
  13. package/dist/{chunk-K4XNTYV7.js → chunk-FEFOZ2ZW.js} +14 -3
  14. package/dist/chunk-FEFOZ2ZW.js.map +1 -0
  15. package/dist/{chunk-7JPUMXFE.js → chunk-FGJ2KPPN.js} +53 -26
  16. package/dist/chunk-FGJ2KPPN.js.map +1 -0
  17. package/dist/{chunk-GNV4X67F.js → chunk-KGDRGA2L.js} +10 -10
  18. package/dist/{chunk-4DTO2E4Y.js → chunk-LZTYPJ5I.js} +35 -17
  19. package/dist/chunk-LZTYPJ5I.js.map +1 -0
  20. package/dist/{chunk-BNZDEU5K.js → chunk-R5E2YGLE.js} +13 -7
  21. package/dist/chunk-R5E2YGLE.js.map +1 -0
  22. package/dist/{chunk-6OQKL35B.js → chunk-R5JRKMEP.js} +1 -1
  23. package/dist/chunk-R5JRKMEP.js.map +1 -0
  24. package/dist/{chunk-PZ4EBFIA.js → chunk-SPQU2422.js} +4 -4
  25. package/dist/{chunk-NNJSAYHY.js → chunk-SXVH74NE.js} +36 -6
  26. package/dist/chunk-SXVH74NE.js.map +1 -0
  27. package/dist/{chunk-IM5RFEW4.js → chunk-TIMS3PB7.js} +2 -2
  28. package/dist/cli/index.js +4 -4
  29. package/dist/{config-S2BDMT4B.js → config-32X53LWY.js} +3 -3
  30. package/dist/{expand-TONFVT5D.js → expand-VFYOWNZS.js} +4 -4
  31. package/dist/{file-vectors-VREL75MA.js → file-vectors-UBOKGYXX.js} +3 -2
  32. package/dist/{index-facts-MBJKBYA3.js → index-facts-ZTUANWBF.js} +6 -6
  33. package/dist/{index-pass-RP23G2CR.js → index-pass-S6NC25P5.js} +5 -5
  34. package/dist/index.js +8 -7
  35. package/dist/{install-MCJIJMFF.js → install-ZTE3H7QM.js} +7 -6
  36. package/dist/{lean-CBHVW6JM.js → lean-5N6BOONS.js} +4 -3
  37. package/dist/{local-TH5XCLVJ.js → local-5EYLEBEP.js} +4 -2
  38. package/dist/{local-llm-GZREB5DF.js → local-llm-ESMVP5LE.js} +3 -3
  39. package/dist/{main-QF6Z552J.js → main-B3HZCGVE.js} +45 -31
  40. package/dist/main-B3HZCGVE.js.map +1 -0
  41. package/dist/{memory-LSZKSF53.js → memory-CI7HIGHN.js} +4 -4
  42. package/dist/{operations-BPI633VN.js → operations-MW52HIQ3.js} +3 -3
  43. package/dist/{rerank-EPCTSEVP.js → rerank-O4S5BC45.js} +4 -4
  44. package/dist/{runtime-W5ACTDFS.js → runtime-SZAWIXEB.js} +5 -4
  45. package/dist/{summaries-W2J5FCLG.js → summaries-GCHZGIIA.js} +10 -8
  46. package/dist/{zvec-LSA2IRAU.js → zvec-V34P24WF.js} +10 -5
  47. package/package.json +1 -1
  48. package/dist/chunk-4DTO2E4Y.js.map +0 -1
  49. package/dist/chunk-6OQKL35B.js.map +0 -1
  50. package/dist/chunk-7JPUMXFE.js.map +0 -1
  51. package/dist/chunk-BNZDEU5K.js.map +0 -1
  52. package/dist/chunk-K4XNTYV7.js.map +0 -1
  53. package/dist/chunk-NNJSAYHY.js.map +0 -1
  54. package/dist/chunk-OO7Q7XZF.js.map +0 -1
  55. package/dist/chunk-RKF4HBBQ.js.map +0 -1
  56. package/dist/chunk-VF7DKRIU.js.map +0 -1
  57. package/dist/main-QF6Z552J.js.map +0 -1
  58. /package/dist/{chunk-AFXWCSQ4.js.map → chunk-3TZNQJTH.js.map} +0 -0
  59. /package/dist/{chunk-RGIVHKBH.js.map → chunk-4TKKLQLH.js.map} +0 -0
  60. /package/dist/{chunk-MNSMW4MZ.js.map → chunk-7IXPARDM.js.map} +0 -0
  61. /package/dist/{chunk-GNV4X67F.js.map → chunk-KGDRGA2L.js.map} +0 -0
  62. /package/dist/{chunk-PZ4EBFIA.js.map → chunk-SPQU2422.js.map} +0 -0
  63. /package/dist/{chunk-IM5RFEW4.js.map → chunk-TIMS3PB7.js.map} +0 -0
  64. /package/dist/{config-S2BDMT4B.js.map → config-32X53LWY.js.map} +0 -0
  65. /package/dist/{expand-TONFVT5D.js.map → expand-VFYOWNZS.js.map} +0 -0
  66. /package/dist/{file-vectors-VREL75MA.js.map → file-vectors-UBOKGYXX.js.map} +0 -0
  67. /package/dist/{index-facts-MBJKBYA3.js.map → index-facts-ZTUANWBF.js.map} +0 -0
  68. /package/dist/{index-pass-RP23G2CR.js.map → index-pass-S6NC25P5.js.map} +0 -0
  69. /package/dist/{install-MCJIJMFF.js.map → install-ZTE3H7QM.js.map} +0 -0
  70. /package/dist/{lean-CBHVW6JM.js.map → lean-5N6BOONS.js.map} +0 -0
  71. /package/dist/{local-TH5XCLVJ.js.map → local-5EYLEBEP.js.map} +0 -0
  72. /package/dist/{local-llm-GZREB5DF.js.map → local-llm-ESMVP5LE.js.map} +0 -0
  73. /package/dist/{memory-LSZKSF53.js.map → memory-CI7HIGHN.js.map} +0 -0
  74. /package/dist/{operations-BPI633VN.js.map → operations-MW52HIQ3.js.map} +0 -0
  75. /package/dist/{rerank-EPCTSEVP.js.map → rerank-O4S5BC45.js.map} +0 -0
  76. /package/dist/{runtime-W5ACTDFS.js.map → runtime-SZAWIXEB.js.map} +0 -0
  77. /package/dist/{summaries-W2J5FCLG.js.map → summaries-GCHZGIIA.js.map} +0 -0
  78. /package/dist/{zvec-LSA2IRAU.js.map → zvec-V34P24WF.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/llm/index-facts.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdir, readdir, readFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { parse as parseYaml } from \"yaml\";\nimport { z } from \"zod\";\nimport { resolveNamespaceFrom } from \"../core/config-file.js\";\nimport { generateEntityId } from \"../core/ids.js\";\nimport { writeAtomic } from \"../core/markdown-store-fs.js\";\nimport { MemoryFileStore } from \"../core/memory-file-store.js\";\nimport { termOverlap } from \"../core/memory-search.js\";\nimport { redactSensitive, scanSensitive } from \"../core/sensitive.js\";\nimport { chatJson } from \"./client.js\";\nimport { type EnabledLlmConfig, resolveLlmConfig } from \"./config.js\";\nimport { waitForForeground } from \"./local.js\";\n\n/**\n * Project facts drawn from the repository at index time, so a fresh project\n * does not start with empty memory: supported versions and the test/lint\n * commands from manifests (read deterministically), required CI checks from\n * workflows, and rules from README / CONTRIBUTING / AGENTS.md / CLAUDE.md and\n * deprecation or release policies (read by the small model, local or BYOK).\n *\n * Every fact keeps its source: `paths: [file]`, `verification: [\"file:Lline\"]`,\n * and a line the fact's words must actually appear near. They are ordinary\n * Markdown facts (the durable store `remember` writes too), marked\n * `createdByAgent: \"codebuddy-index\"`; re-extraction only ever replaces facts\n * with that origin whose source file changed, never a human or agent fact.\n * Sensitive text is dropped, not redacted. At most `MAX_FACTS` per repository.\n */\nexport const INDEX_FACT_AGENT = \"codebuddy-index\";\nexport const MAX_FACTS = 40;\nconst PER_SOURCE = 8;\nconst SECTION_CHARS = 1_600;\n/** A fact this close to an existing one (Jaccard of content words) is a duplicate. */\nconst DUPLICATE_OVERLAP = 0.8;\nconst TIMEOUT_MS = { local: 60_000, hosted: 20_000 } as const;\n\nexport type IndexFactsResult = {\n written: number;\n removed: number;\n skippedSources: number;\n stoppedBy: \"done\" | \"disabled\" | \"cap\" | \"aborted\";\n};\n\ntype Candidate = { path: string; line: number; text: string };\ntype Ledger = { version: 1; sources: Record<string, string> };\n\nconst PROSE_SOURCES = /^(README|CONTRIBUTING|AGENTS|CLAUDE)(\\.[a-z]+)?$/i;\nconst POLICY_DOC =\n /(deprecat|release[-_ ]?(policy|process)|versioning|support[-_ ]?policy|compatib)/i;\n\n/** Files facts are read from, relative to the root, in priority order. */\nexport async function factSources(root: string): Promise<string[]> {\n const top = await readdir(root).catch(() => [] as string[]);\n const docs = await readdir(join(root, \"docs\"), { recursive: true }).catch(() => [] as string[]);\n const workflows = await readdir(join(root, \".github\", \"workflows\")).catch(() => [] as string[]);\n return [\n ...[\"package.json\", \"pyproject.toml\"].filter((name) => top.includes(name)),\n ...top.filter((name) => PROSE_SOURCES.test(name)).sort(),\n ...workflows\n .filter((name) => /\\.ya?ml$/.test(name))\n .sort()\n .map((name) => `.github/workflows/${name}`),\n ...docs\n .map((name) => name.split(\"\\\\\").join(\"/\"))\n .filter((name) => /\\.(md|rst|txt)$/i.test(name) && POLICY_DOC.test(name))\n .sort()\n .slice(0, 4)\n .map((name) => `docs/${name}`),\n ];\n}\n\n/**\n * Extract (or refresh) index facts. Sources whose content hash is unchanged\n * are skipped; a changed or deleted source has its old index facts removed\n * first. Without an enabled model layer nothing happens (2.14 behaviour).\n */\nexport async function extractIndexFacts(\n root: string,\n options: { config?: EnabledLlmConfig; signal?: AbortSignal; namespace?: string } = {},\n): Promise<IndexFactsResult> {\n const resolved = options.config ?? (await resolveLlmConfig(root));\n if (!resolved.enabled || resolved.features.facts === false) {\n return { written: 0, removed: 0, skippedSources: 0, stoppedBy: \"disabled\" };\n }\n const config = resolved;\n const namespace = options.namespace ?? (await resolveNamespaceFrom(root));\n const store = new MemoryFileStore(root);\n const ledger = await readLedger(root);\n const sources = await factSources(root);\n let facts = await store.listFacts();\n let removed = 0;\n const removeFrom = async (path: string) => {\n for (const fact of facts) {\n if (fact.createdByAgent === INDEX_FACT_AGENT && fact.paths[0] === path) {\n await store.deleteFact(fact.id);\n removed++;\n }\n }\n facts = await store.listFacts();\n };\n for (const path of Object.keys(ledger.sources)) {\n if (!sources.includes(path)) {\n await removeFrom(path);\n delete ledger.sources[path];\n }\n }\n\n let written = 0;\n let skippedSources = 0;\n let stoppedBy: IndexFactsResult[\"stoppedBy\"] = \"done\";\n const indexFacts = () => facts.filter((fact) => fact.createdByAgent === INDEX_FACT_AGENT).length;\n for (const path of sources) {\n if (options.signal?.aborted) {\n stoppedBy = \"aborted\";\n break;\n }\n const text = await readFile(join(root, path), \"utf8\").catch(() => null);\n if (text === null) continue;\n const hash = sha(text);\n if (ledger.sources[path] === hash) {\n skippedSources++;\n continue;\n }\n await removeFrom(path);\n if (indexFacts() >= MAX_FACTS) {\n stoppedBy = \"cap\";\n break;\n }\n const candidates = await candidatesFor(root, path, text, config, options.signal);\n let fromSource = 0;\n for (const candidate of candidates) {\n if (fromSource >= PER_SOURCE || indexFacts() >= MAX_FACTS) break;\n if (scanSensitive(candidate.text).sensitive) continue;\n // Never a second copy of what a human, agent or earlier source already said.\n if (\n facts.some(\n (fact) =>\n Math.max(\n termOverlap(candidate.text, fact.object),\n termOverlap(candidate.text, fact.content),\n ) >= DUPLICATE_OVERLAP,\n )\n ) {\n continue;\n }\n const id = generateEntityId(\"fact\");\n const content = `${candidate.text} (source: ${path}:L${candidate.line})`;\n await store.writeFact({\n id,\n namespace,\n subject: path,\n predicate: \"project-fact\",\n object: candidate.text,\n confidence: 0.8,\n createdAt: new Date().toISOString(),\n createdByAgent: INDEX_FACT_AGENT,\n sourceInteractionId: null,\n sourceDeleted: false,\n content,\n paths: [path],\n verification: [`${path}:L${candidate.line}`],\n idempotencyKey: `index:${path}:${sha(candidate.text).slice(0, 16)}`.slice(0, 200),\n fingerprint: sha(`${namespace}\\n${content}`),\n });\n facts = await store.listFacts();\n written++;\n fromSource++;\n }\n ledger.sources[path] = hash;\n await writeLedger(root, ledger);\n }\n await writeLedger(root, ledger);\n return { written, removed, skippedSources, stoppedBy };\n}\n\n/** Index facts currently stored (for `memory list|prune --origin index`). */\nexport async function listIndexFacts(root: string) {\n return (await new MemoryFileStore(root).listFacts()).filter(\n (fact) => fact.createdByAgent === INDEX_FACT_AGENT,\n );\n}\n\n/** Delete every index fact and forget the source hashes, so the next pass re-extracts. */\nexport async function pruneIndexFacts(root: string): Promise<number> {\n const store = new MemoryFileStore(root);\n const facts = await listIndexFacts(root);\n for (const fact of facts) await store.deleteFact(fact.id);\n await writeLedger(root, { version: 1, sources: {} });\n return facts.length;\n}\n\nasync function candidatesFor(\n root: string,\n path: string,\n text: string,\n config: EnabledLlmConfig,\n signal: AbortSignal | undefined,\n): Promise<Candidate[]> {\n if (path === \"package.json\") return packageFacts(path, text);\n if (path === \"pyproject.toml\") return pyprojectFacts(path, text);\n if (path.startsWith(\".github/workflows/\")) return workflowFacts(path, text);\n return proseFacts(root, path, stripManagedBlocks(text), config, signal);\n}\n\n/** Supported Node version and the project's own check commands. */\nexport function packageFacts(path: string, text: string): Candidate[] {\n let manifest: { engines?: Record<string, string>; scripts?: Record<string, string> };\n try {\n manifest = JSON.parse(text);\n } catch {\n return [];\n }\n const out: Candidate[] = [];\n const node = manifest.engines?.node;\n if (node)\n out.push({ path, line: lineOf(text, '\"node\"'), text: `The project requires Node.js ${node}.` });\n for (const name of [\"test\", \"lint\", \"typecheck\", \"build\", \"format\"]) {\n const script = manifest.scripts?.[name];\n if (script) {\n out.push({\n path,\n line: lineOf(text, `\"${name}\"`),\n text: `Run \\`npm run ${name}\\` to ${VERB[name]} (it runs \\`${script.slice(0, 120)}\\`).`,\n });\n }\n }\n return out;\n}\nconst VERB: Record<string, string> = {\n test: \"run the tests\",\n lint: \"lint the code\",\n typecheck: \"type-check the code\",\n build: \"build the project\",\n format: \"format the code\",\n};\n\n/** Supported Python versions and the configured tools. */\nexport function pyprojectFacts(path: string, text: string): Candidate[] {\n const out: Candidate[] = [];\n const python = text.match(/^\\s*requires-python\\s*=\\s*[\"']([^\"']+)[\"']/m)?.[1];\n if (python) {\n out.push({\n path,\n line: lineOf(text, \"requires-python\"),\n text: `The project supports Python ${python}.`,\n });\n }\n for (const [tool, what] of [\n [\"ruff\", \"Ruff is the linter/formatter\"],\n [\"mypy\", \"mypy type-checks the code\"],\n [\"pyright\", \"Pyright type-checks the code\"],\n [\"pytest\", \"Tests run with pytest\"],\n ] as const) {\n const header = `[tool.${tool}`;\n if (text.includes(header)) {\n out.push({\n path,\n line: lineOf(text, header),\n text: `${what} (configured in pyproject.toml).`,\n });\n }\n }\n return out;\n}\n\nconst CHECK_COMMAND =\n /\\b(test|tests|pytest|lint|ruff|mypy|pyright|tsc|typecheck|vitest|jest|biome|eslint|go vet|cargo (test|clippy)|coverage)\\b/i;\n\n/** Steps that only report or branch (`echo \"test workflow exited with …\"`) are not checks. */\nconst SHELL_PLUMBING = /^(echo|printf|if|then|else|fi|exit|export|set|\\[|test\\s+-)\\b/i;\n\n/** CI's required checks: the run steps that test, lint or type-check. */\nexport function workflowFacts(path: string, text: string): Candidate[] {\n let workflow: {\n name?: string;\n on?: unknown;\n jobs?: Record<string, { steps?: Array<{ run?: string }> }>;\n };\n try {\n workflow = parseYaml(text) ?? {};\n } catch {\n return [];\n }\n const commands = new Set<string>();\n for (const job of Object.values(workflow.jobs ?? {})) {\n for (const step of job?.steps ?? []) {\n const run = typeof step?.run === \"string\" ? step.run.split(\"\\n\")[0]?.trim() : undefined;\n if (run && CHECK_COMMAND.test(run) && !SHELL_PLUMBING.test(run) && run.length <= 120)\n commands.add(run);\n }\n }\n // Only a workflow that runs on pull requests gates merging (a release job does not).\n // YAML 1.1 readers may parse the `on:` key as boolean true.\n const triggers = JSON.stringify(workflow.on ?? (workflow as Record<string, unknown>).true ?? \"\");\n const gates = triggers.includes(\"pull_request\");\n return [...commands].slice(0, 4).map((command) => ({\n path,\n line: lineOf(text, command),\n text: `CI (${workflow.name ?? path}) runs \\`${command}\\`${gates ? \" on pull requests; it must pass before merging\" : \"\"}.`,\n }));\n}\n\nconst proseSchema = z.object({\n facts: z.array(z.object({ line: z.number().int(), fact: z.string() })).default([]),\n});\n\n/** Grammar bounds for the local model (see summaries.ts CARD_GRAMMAR). */\nconst FACT_GRAMMAR = {\n type: \"object\",\n properties: {\n facts: {\n type: \"array\",\n maxItems: 6,\n items: {\n type: \"object\",\n properties: { line: { type: \"integer\" }, fact: { type: \"string\", maxLength: 240 } },\n },\n },\n },\n};\n\nconst SYSTEM = [\n \"You extract durable project facts from a repository document for a team memory.\",\n 'Answer with JSON only: {\"facts\":[{\"line\":12,\"fact\":\"...\"}]}.',\n \"Keep only lasting rules and facts a developer must know: conventions, required checks, supported versions, deprecation and release policy, how to run or test the project.\",\n \"Each fact is one short sentence in the document's own words; line is the number of the line it comes from.\",\n \"Skip marketing, installation steps for end users, links, badges and anything temporary. Give at most 6 facts; none is fine.\",\n].join(\" \");\n\nasync function proseFacts(\n root: string,\n path: string,\n text: string,\n config: EnabledLlmConfig,\n signal: AbortSignal | undefined,\n): Promise<Candidate[]> {\n const lines = text.split(/\\r?\\n/);\n const out: Candidate[] = [];\n for (const section of sections(lines)) {\n if (signal?.aborted || out.length >= PER_SOURCE) break;\n if (config.local) await waitForForeground(root, signal);\n const numbered = section\n .map(({ line, text }) => `${line}: ${redactSensitive(text)}`)\n .join(\"\\n\");\n const result = await chatJson(config, {\n system: SYSTEM,\n user: `File: ${path}\\n\\n${numbered}`,\n schema: proseSchema,\n jsonSchema: FACT_GRAMMAR,\n maxTokens: 500,\n deadlineMs: config.local ? TIMEOUT_MS.local : TIMEOUT_MS.hosted,\n purpose: \"background\",\n repositoryRoot: root,\n });\n if (!result.ok) continue;\n for (const fact of result.value.facts) {\n const sentence = fact.fact.trim().replace(/\\s+/g, \" \").slice(0, 240);\n if (isStatement(sentence) && hasLineEvidence(lines, fact.line, sentence)) {\n out.push({ path, line: fact.line, text: sentence });\n }\n }\n }\n return out;\n}\n\n/**\n * A fact is a statement: at least five words of prose, not a shell line, a\n * code fence, a code line, an installer option (\"Using pyenv\") or a bare\n * heading the small model copied out of a README.\n */\nexport function isStatement(text: string): boolean {\n if (/```|^\\$\\s|^(cd|npm|npx|pip|uv|git|make|curl|echo)\\s/i.test(text)) return false;\n // Code: an import line, an assignment or call (`app = Litestar(...)`), or\n // dense code punctuation.\n if (/^(from\\s+[\\w.]+\\s+import|import\\s+[\\w.]+)\\b/.test(text)) return false;\n if (/\\b\\w+\\s*=\\s*[\\w.]+\\(/.test(text) || (text.match(/[(){}[\\]=;<>]/g) ?? []).length >= 4)\n return false;\n // One option in a list of ways to install (\"Using asdf\", \"With Homebrew: …\").\n if (/^(using|via|with)\\s+[\\w.-]+\\s*(:|$|\\(|,)/i.test(text)) return false;\n return (text.match(/\\p{L}{2,}/gu) ?? []).length >= 5;\n}\n\n/**\n * The model's fact must be supported by its cited line (± 2): at least two of\n * its content words (≥ 4 letters) appear there, or all of them for a short fact.\n */\nexport function hasLineEvidence(lines: string[], line: number, fact: string): boolean {\n if (line < 1 || line > lines.length) return false;\n const nearby = new Set(\n lines\n .slice(Math.max(0, line - 3), line + 2)\n .join(\" \")\n .toLowerCase()\n .match(/[\\p{L}\\p{N}_]{4,}/gu) ?? [],\n );\n const words = [...new Set(fact.toLowerCase().match(/[\\p{L}\\p{N}_]{4,}/gu) ?? [])];\n const shared = words.filter((word) => nearby.has(word)).length;\n return shared >= Math.min(2, words.length) && shared > 0;\n}\n\n/** Heading-delimited chunks of at most `SECTION_CHARS`, with 1-based line numbers. */\nfunction sections(lines: string[]): Array<Array<{ line: number; text: string }>> {\n const out: Array<Array<{ line: number; text: string }>> = [];\n let current: Array<{ line: number; text: string }> = [];\n let size = 0;\n lines.forEach((text, index) => {\n const heading = /^#{1,3}\\s/.test(text);\n if (current.length > 0 && (heading || size + text.length > SECTION_CHARS)) {\n out.push(current);\n current = [];\n size = 0;\n }\n if (text.trim()) {\n current.push({ line: index + 1, text: text.slice(0, 400) });\n size += Math.min(text.length, 400) + 6;\n }\n });\n if (current.length > 0) out.push(current);\n // A long README: the first sections carry the rules that matter.\n return out.slice(0, 12);\n}\n\n/** CodeBuddy's own managed blocks in CLAUDE.md / AGENTS.md are not project facts. */\nfunction stripManagedBlocks(text: string): string {\n // Blank the block (keeping its line breaks) so cited line numbers stay true.\n return text.replace(\n /<!-- codebuddy:[\\w-]+:start -->[\\s\\S]*?<!-- codebuddy:[\\w-]+:end -->/g,\n (block) => block.replace(/[^\\n]/g, \"\"),\n );\n}\n\nfunction lineOf(text: string, needle: string): number {\n const index = text.indexOf(needle);\n return index < 0 ? 1 : text.slice(0, index).split(\"\\n\").length;\n}\n\nfunction sha(text: string): string {\n return createHash(\"sha256\").update(text).digest(\"hex\");\n}\n\nfunction ledgerPath(root: string): string {\n return join(root, \".codebuddy\", \"cache\", \"index-facts.json\");\n}\n\nasync function readLedger(root: string): Promise<Ledger> {\n try {\n const parsed = JSON.parse(await readFile(ledgerPath(root), \"utf8\")) as Ledger;\n if (parsed.version === 1 && parsed.sources) return parsed;\n } catch {\n // Missing: every source is new.\n }\n return { version: 1, sources: {} };\n}\n\nasync function writeLedger(root: string, ledger: Ledger): Promise<void> {\n await mkdir(dirname(ledgerPath(root)), { recursive: true });\n await writeAtomic(ledgerPath(root), JSON.stringify(ledger));\n}\n"],"mappings":"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","names":["text"]}
@@ -52,4 +52,4 @@ export {
52
52
  withCardLock,
53
53
  cardLockHolder
54
54
  };
55
- //# sourceMappingURL=chunk-6OQKL35B.js.map
55
+ //# sourceMappingURL=chunk-R5JRKMEP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/llm/card-lock.ts"],"sourcesContent":["import { link, mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\n/**\n * One model writer per repository: intent cards, index facts, file and reuse\n * vectors. Setup (`codebuddy`) starts a detached `index --cards`, every\n * `codebuddy serve` schedules its own pass and every capture a `hooks sync`:\n * without this each loads its own model copy, they oversubscribe the CPU until\n * every file times out, and each rewrites the shared cache from its own copy.\n * The lock file holds the writer's PID; a dead PID is a stale lock.\n */\nexport function cardLockPath(root: string): string {\n return join(root, \".codebuddy\", \"cache\", \"cards.lock\");\n}\n\nexport type CardLockResult<T> = { ok: true; value: T } | { ok: false; holder: number | null };\n\n/** Run `fn` holding the lock, or return the live holder's PID without running it. */\nexport async function withCardLock<T>(\n root: string,\n fn: () => Promise<T>,\n): Promise<CardLockResult<T>> {\n const path = cardLockPath(root);\n await mkdir(dirname(path), { recursive: true });\n // Written whole, then linked: a reader never sees an empty lock file.\n const temporary = `${path}.${process.pid}`;\n await writeFile(temporary, `${process.pid}\\n`, { mode: 0o600 });\n try {\n for (let attempt = 0; ; attempt++) {\n try {\n await link(temporary, path);\n break;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n const holder = await cardLockHolder(root);\n if (holder !== null || attempt > 0) return { ok: false, holder };\n // ponytail: two processes clearing the same stale lock at once can both\n // win; needs a rename-and-recheck if that window ever matters.\n await rm(path, { force: true });\n }\n }\n } finally {\n await rm(temporary, { force: true });\n }\n try {\n return { ok: true, value: await fn() };\n } finally {\n if ((await readPid(path)) === process.pid) await rm(path, { force: true });\n }\n}\n\n/** The live process writing cards, or null (no lock, or its process is gone). */\nexport async function cardLockHolder(root: string): Promise<number | null> {\n const pid = await readPid(cardLockPath(root));\n return pid !== null && isAlive(pid) ? pid : null;\n}\n\nfunction isAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return (error as NodeJS.ErrnoException).code === \"EPERM\";\n }\n}\n\nasync function readPid(path: string): Promise<number | null> {\n const pid = Number.parseInt(await readFile(path, \"utf8\").catch(() => \"\"), 10);\n return Number.isInteger(pid) && pid > 0 ? pid : null;\n}\n"],"mappings":";AAAA,SAAS,MAAM,OAAO,UAAU,IAAI,iBAAiB;AACrD,SAAS,SAAS,YAAY;AAUvB,SAAS,aAAa,MAAsB;AACjD,SAAO,KAAK,MAAM,cAAc,SAAS,YAAY;AACvD;AAKA,eAAsB,aACpB,MACA,IAC4B;AAC5B,QAAM,OAAO,aAAa,IAAI;AAC9B,QAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAE9C,QAAM,YAAY,GAAG,IAAI,IAAI,QAAQ,GAAG;AACxC,QAAM,UAAU,WAAW,GAAG,QAAQ,GAAG;AAAA,GAAM,EAAE,MAAM,IAAM,CAAC;AAC9D,MAAI;AACF,aAAS,UAAU,KAAK,WAAW;AACjC,UAAI;AACF,cAAM,KAAK,WAAW,IAAI;AAC1B;AAAA,MACF,SAAS,OAAO;AACd,YAAK,MAAgC,SAAS,SAAU,OAAM;AAC9D,cAAM,SAAS,MAAM,eAAe,IAAI;AACxC,YAAI,WAAW,QAAQ,UAAU,EAAG,QAAO,EAAE,IAAI,OAAO,OAAO;AAG/D,cAAM,GAAG,MAAM,EAAE,OAAO,KAAK,CAAC;AAAA,MAChC;AAAA,IACF;AAAA,EACF,UAAE;AACA,UAAM,GAAG,WAAW,EAAE,OAAO,KAAK,CAAC;AAAA,EACrC;AACA,MAAI;AACF,WAAO,EAAE,IAAI,MAAM,OAAO,MAAM,GAAG,EAAE;AAAA,EACvC,UAAE;AACA,QAAK,MAAM,QAAQ,IAAI,MAAO,QAAQ,IAAK,OAAM,GAAG,MAAM,EAAE,OAAO,KAAK,CAAC;AAAA,EAC3E;AACF;AAGA,eAAsB,eAAe,MAAsC;AACzE,QAAM,MAAM,MAAM,QAAQ,aAAa,IAAI,CAAC;AAC5C,SAAO,QAAQ,QAAQ,QAAQ,GAAG,IAAI,MAAM;AAC9C;AAEA,SAAS,QAAQ,KAAsB;AACrC,MAAI;AACF,YAAQ,KAAK,KAAK,CAAC;AACnB,WAAO;AAAA,EACT,SAAS,OAAO;AACd,WAAQ,MAAgC,SAAS;AAAA,EACnD;AACF;AAEA,eAAe,QAAQ,MAAsC;AAC3D,QAAM,MAAM,OAAO,SAAS,MAAM,SAAS,MAAM,MAAM,EAAE,MAAM,MAAM,EAAE,GAAG,EAAE;AAC5E,SAAO,OAAO,UAAU,GAAG,KAAK,MAAM,IAAI,MAAM;AAClD;","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runLeanHook
3
- } from "./chunk-AFXWCSQ4.js";
3
+ } from "./chunk-3TZNQJTH.js";
4
4
  import {
5
5
  CLIENT_ADAPTERS,
6
6
  CLIENT_IDS,
@@ -21,13 +21,13 @@ import {
21
21
  runResetHook,
22
22
  runResumeHook,
23
23
  runStageHook
24
- } from "./chunk-OO7Q7XZF.js";
24
+ } from "./chunk-5ZLNHYJH.js";
25
25
  import {
26
26
  hookRoot
27
27
  } from "./chunk-TGFTKWOR.js";
28
28
  import {
29
29
  indexRepository
30
- } from "./chunk-4DTO2E4Y.js";
30
+ } from "./chunk-LZTYPJ5I.js";
31
31
  import {
32
32
  resolveNamespaceFrom
33
33
  } from "./chunk-Y4QQML24.js";
@@ -1662,4 +1662,4 @@ export {
1662
1662
  setHooks,
1663
1663
  detectClient
1664
1664
  };
1665
- //# sourceMappingURL=chunk-PZ4EBFIA.js.map
1665
+ //# sourceMappingURL=chunk-SPQU2422.js.map
@@ -3,13 +3,16 @@ import {
3
3
  } from "./chunk-3QGW53GD.js";
4
4
 
5
5
  // src/llm/local.ts
6
+ import { execFile } from "child_process";
6
7
  import { createHash } from "crypto";
7
8
  import { createWriteStream } from "fs";
8
9
  import { mkdir, rename, rm, stat, statfs, utimes, writeFile } from "fs/promises";
10
+ import { createRequire } from "module";
9
11
  import { availableParallelism, freemem, totalmem } from "os";
10
12
  import { dirname, join } from "path";
11
13
  import { Readable } from "stream";
12
14
  import { pipeline } from "stream/promises";
15
+ import { pathToFileURL } from "url";
13
16
  import { z } from "zod";
14
17
  var GB = 1024 ** 3;
15
18
  var LOCAL_MODELS = {
@@ -130,8 +133,30 @@ var loaded = null;
130
133
  var queue = Promise.resolve();
131
134
  var grammars = /* @__PURE__ */ new Map();
132
135
  async function importRuntime() {
133
- const specifier = process.env.CODEBUDDY_LLAMA_RUNTIME ?? "node-llama-cpp";
134
- return await import(specifier);
136
+ const override = process.env.CODEBUDDY_LLAMA_RUNTIME;
137
+ if (override) return await import(override);
138
+ try {
139
+ return await import("node-llama-cpp");
140
+ } catch (error) {
141
+ const global = await globalRuntimeUrl();
142
+ if (!global) throw error;
143
+ return await import(global);
144
+ }
145
+ }
146
+ var globalRuntime = null;
147
+ function globalRuntimeUrl() {
148
+ globalRuntime ??= new Promise((resolveRoot) => {
149
+ execFile("npm", ["root", "-g"], { timeout: 5e3 }, (error, stdout) => {
150
+ if (error) return resolveRoot(null);
151
+ try {
152
+ const entry = createRequire(join(stdout.trim(), "noop.js")).resolve("node-llama-cpp");
153
+ resolveRoot(pathToFileURL(entry).href);
154
+ } catch {
155
+ resolveRoot(null);
156
+ }
157
+ });
158
+ });
159
+ return globalRuntime;
135
160
  }
136
161
  var CONTEXT_TOKENS = 4096;
137
162
  function generationThreads() {
@@ -182,11 +207,15 @@ function isLocalModelWarm() {
182
207
  return loaded !== null;
183
208
  }
184
209
  async function warmLocalModel(spec) {
210
+ return await loadLocalModel(spec) === null;
211
+ }
212
+ async function loadLocalModel(spec) {
185
213
  try {
186
214
  await load(spec);
187
- return true;
188
- } catch {
189
- return false;
215
+ return null;
216
+ } catch (error) {
217
+ const message = error?.message ?? String(error);
218
+ return /Cannot find (package|module)[^\n]*node-llama-cpp/i.test(message) ? `node-llama-cpp runtime not found (npm install -g ${LLAMA_RUNTIME})` : `local model failed to load: ${message.split("\n")[0]?.slice(0, 160)}`;
190
219
  }
191
220
  }
192
221
  async function localChatJson(spec, request) {
@@ -362,6 +391,7 @@ export {
362
391
  unloadLocalModel,
363
392
  isLocalModelWarm,
364
393
  warmLocalModel,
394
+ loadLocalModel,
365
395
  localChatJson,
366
396
  markForeground,
367
397
  waitForForeground,
@@ -369,4 +399,4 @@ export {
369
399
  formatBytes,
370
400
  freeMemory
371
401
  };
372
- //# sourceMappingURL=chunk-NNJSAYHY.js.map
402
+ //# sourceMappingURL=chunk-SXVH74NE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/llm/local.ts"],"sourcesContent":["import { execFile } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport { createWriteStream } from \"node:fs\";\nimport { mkdir, rename, rm, stat, statfs, utimes, writeFile } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { availableParallelism, freemem, totalmem } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport { pathToFileURL } from \"node:url\";\nimport { z } from \"zod\";\nimport { modelCacheDir } from \"../providers/local.js\";\nimport type { ChatJsonRequest, LlmResult } from \"./client.js\";\n\n/**\n * Local text generation (Qwen3 only: 0.6B default, 1.7B \"better\") for the small-model features (intent cards, index\n * facts, query expansion, and optionally rerank): a GGUF model run by\n * node-llama-cpp (llama.cpp bindings; prebuilt for macOS arm64/x64, Linux\n * x64/arm64 and Windows x64; Metal/CUDA/Vulkan when present, CPU otherwise).\n * See ARCHITECTURE.md for why node-llama-cpp and not Cactus.\n *\n * Installed by setup (`codebuddy`, skipped with `--no-local-llm` or\n * `CODEBUDDY_LOCAL_LLM=0`) or `codebuddy models add local-llm`: the runtime with\n * npm, the weights checksum-verified into the shared model cache. Without\n * either, every caller gets `disabled` and keeps 2.14.0 behaviour. Nothing\n * here makes a network call except `downloadLocalModel`.\n *\n * Generation runs on llama.cpp's own native threads (capped at cores − 1),\n * so the MCP event loop keeps answering while a card is written; background\n * passes also yield to foreground work through `waitForForeground`.\n */\n\nexport type LocalModelSize = \"default\" | \"better\";\n\nexport type LocalModelSpec = {\n size: LocalModelSize;\n id: string;\n repo: string;\n revision: string;\n file: string;\n bytes: number;\n sha256: string;\n /** Rough resident memory while loaded, for the free-RAM check. */\n ramBytes: number;\n /** Qwen3 thinks unless told not to; the JSON grammar alone does not stop it. */\n noThink: boolean;\n};\n\nconst GB = 1024 ** 3;\n\n/** Pinned revisions and checksums (Hugging Face LFS sha256). */\nexport const LOCAL_MODELS: Record<LocalModelSize, LocalModelSpec> = {\n default: {\n size: \"default\",\n id: \"qwen3-0.6b-q4_k_m\",\n repo: \"unsloth/Qwen3-0.6B-GGUF\",\n revision: \"50968a4468ef4233ed78cd7c3de230dd1d61a56b\",\n file: \"Qwen3-0.6B-Q4_K_M.gguf\",\n bytes: 396_705_472,\n sha256: \"ac2d97712095a558e31573f62f466a3f9d93990898b0ec79d7c974c1780d524a\",\n ramBytes: 1 * GB,\n noThink: true,\n },\n better: {\n size: \"better\",\n id: \"qwen3-1.7b-q4_k_m\",\n repo: \"unsloth/Qwen3-1.7B-GGUF\",\n revision: \"d7f544eead698dbd1f15126ef60b45a1e1933222\",\n file: \"Qwen3-1.7B-Q4_K_M.gguf\",\n bytes: 1_107_409_472,\n sha256: \"b139949c5bd74937ad8ed8c8cf3d9ffb1e99c866c823204dc42c0d91fa181897\",\n ramBytes: 2.2 * GB,\n noThink: true,\n },\n};\n\n/** Exact runtime version the adapter was tested against. */\nexport const LLAMA_RUNTIME = \"node-llama-cpp@3.22.3\";\n\nexport function localModelPath(spec: LocalModelSpec): string {\n return join(modelCacheDir(), \"gguf\", spec.file);\n}\n\n/** The installed model of `preferred` size, else any installed one (default first). */\nexport async function installedLocalModel(\n preferred?: LocalModelSize,\n): Promise<LocalModelSpec | null> {\n if (process.env.CODEBUDDY_LOCAL_LLM === \"0\") return null;\n const order: LocalModelSize[] = [...(preferred ? [preferred] : []), \"default\", \"better\"];\n for (const size of new Set(order)) {\n const spec = LOCAL_MODELS[size];\n const info = await stat(localModelPath(spec)).catch(() => null);\n if (info?.isFile() && info.size === spec.bytes) return spec;\n }\n return null;\n}\n\n/** Whether the node-llama-cpp runtime can be imported from this install. */\nexport async function localRuntimeAvailable(): Promise<boolean> {\n try {\n await importRuntime();\n return true;\n } catch {\n return false;\n }\n}\n\nexport type DownloadCheck = { ok: true } | { ok: false; reason: string };\n\n/**\n * Whether setup should install the model on its own. A low-end machine (under\n * 4 GB of RAM or a single core) would spend most of a session carding at a few\n * tokens per second; it keeps keyword + vector discovery, and\n * `codebuddy models add local-llm` still works there by hand.\n */\nexport function machineCanCard(): DownloadCheck {\n if (totalmem() < 4 * GB) {\n return { ok: false, reason: `${formatBytes(totalmem())} RAM (setup installs it from 4 GB)` };\n }\n if (availableParallelism() < 2) return { ok: false, reason: \"a single CPU core\" };\n return { ok: true };\n}\n\n/** Free disk (model cache volume) and RAM before a download. */\nexport async function checkLocalModelResources(spec: LocalModelSpec): Promise<DownloadCheck> {\n const directory = modelCacheDir();\n await mkdir(directory, { recursive: true });\n const fs = await statfs(directory).catch(() => null);\n if (fs && fs.bavail * fs.bsize < spec.bytes * 1.1) {\n return {\n ok: false,\n reason: `needs ${formatBytes(spec.bytes * 1.1)} free in ${directory}, ${formatBytes(fs.bavail * fs.bsize)} available`,\n };\n }\n if (totalmem() < spec.ramBytes * 2) {\n return {\n ok: false,\n reason: `needs about ${formatBytes(spec.ramBytes)} of RAM while loaded; this machine has ${formatBytes(totalmem())} in total`,\n };\n }\n return { ok: true };\n}\n\n/**\n * Download the pinned GGUF into the model cache, verifying size and sha256\n * before it is moved into place (a partial or corrupt file is never used).\n */\nexport async function downloadLocalModel(\n spec: LocalModelSpec,\n onProgress?: (received: number, total: number) => void,\n fetchImpl: typeof fetch = fetch,\n): Promise<string> {\n const target = localModelPath(spec);\n await mkdir(dirname(target), { recursive: true });\n const partial = `${target}.partial`;\n const url = `https://huggingface.co/${spec.repo}/resolve/${spec.revision}/${spec.file}`;\n const response = await fetchImpl(url);\n if (!response.ok || !response.body) {\n throw new Error(`download failed: HTTP ${response.status} for ${url}`);\n }\n const hash = createHash(\"sha256\");\n let received = 0;\n const source = Readable.fromWeb(response.body as import(\"node:stream/web\").ReadableStream);\n source.on(\"data\", (chunk: Buffer) => {\n hash.update(chunk);\n received += chunk.length;\n onProgress?.(received, spec.bytes);\n });\n try {\n await pipeline(source, createWriteStream(partial));\n const digest = hash.digest(\"hex\");\n if (received !== spec.bytes || digest !== spec.sha256) {\n throw new Error(\n `checksum mismatch for ${spec.file} (got ${received} bytes, sha256 ${digest.slice(0, 12)}…); not installed`,\n );\n }\n await rename(partial, target);\n } finally {\n await rm(partial, { force: true });\n }\n return target;\n}\n\nexport async function removeLocalModels(): Promise<string[]> {\n const removed: string[] = [];\n for (const spec of Object.values(LOCAL_MODELS)) {\n const path = localModelPath(spec);\n if (await stat(path).catch(() => null)) {\n await rm(path, { force: true });\n removed.push(spec.id);\n }\n }\n return removed;\n}\n\n// ── Generation ──────────────────────────────────────────────────────────\n\n/** Minimal structural types for the dynamically imported runtime. */\ntype Llama = {\n loadModel(options: { modelPath: string }): Promise<LlamaModel>;\n createGrammarForJsonSchema(schema: unknown): Promise<unknown>;\n getGrammarFor(type: \"json\"): Promise<unknown>;\n};\ntype LlamaModel = {\n createContext(options: { contextSize: number; threads: number }): Promise<LlamaContext>;\n dispose(): Promise<void>;\n};\ntype LlamaContext = { getSequence(): unknown; dispose(): Promise<void> };\ntype ChatSession = {\n prompt(\n text: string,\n options: {\n grammar?: unknown;\n maxTokens: number;\n temperature: number;\n budgets?: { thoughtTokens: number };\n signal?: AbortSignal;\n stopOnAbortSignal?: boolean;\n },\n ): Promise<string>;\n resetChatHistory(): void;\n setChatHistory?(history: unknown): void;\n dispose(options?: { disposeSequence?: boolean }): void;\n};\ntype Runtime = {\n getLlama(options?: { gpu?: \"auto\" | false }): Promise<Llama>;\n LlamaChatSession: new (options: {\n contextSequence: unknown;\n autoDisposeSequence?: boolean;\n systemPrompt?: string;\n }) => ChatSession;\n};\n\ntype Loaded = {\n spec: LocalModelSpec;\n llama: Llama;\n model: LlamaModel;\n context: LlamaContext;\n sequence: unknown;\n runtime: Runtime;\n};\n\nlet loading: Promise<Loaded> | null = null;\nlet loaded: Loaded | null = null;\n/** One generation at a time: a single context sequence is shared. */\nlet queue: Promise<unknown> = Promise.resolve();\nconst grammars = new Map<unknown, Promise<unknown>>();\n\nasync function importRuntime(): Promise<Runtime> {\n // A variable specifier: not a dependency, never bundled, resolved at run time.\n const override = process.env.CODEBUDDY_LLAMA_RUNTIME;\n if (override) return (await import(override)) as Runtime;\n try {\n return (await import(\"node-llama-cpp\" as string)) as Runtime;\n } catch (error) {\n // Setup installs the runtime with `npm install -g`, which this install can\n // only see when CodeBuddy itself is global; npx, `npm i --prefix`, a local\n // dependency or a dev build look in the global root too.\n const global = await globalRuntimeUrl();\n if (!global) throw error;\n return (await import(global)) as Runtime;\n }\n}\n\nlet globalRuntime: Promise<string | null> | null = null;\n/** node-llama-cpp's entry under `npm root -g`, or null (looked up once per process). */\nfunction globalRuntimeUrl(): Promise<string | null> {\n globalRuntime ??= new Promise((resolveRoot) => {\n execFile(\"npm\", [\"root\", \"-g\"], { timeout: 5_000 }, (error, stdout) => {\n if (error) return resolveRoot(null);\n try {\n const entry = createRequire(join(stdout.trim(), \"noop.js\")).resolve(\"node-llama-cpp\");\n resolveRoot(pathToFileURL(entry).href);\n } catch {\n resolveRoot(null);\n }\n });\n });\n return globalRuntime;\n}\n\n/** Context large enough for the bounded card/fact prompts (~1k tokens in, ≤ 400 out). */\nconst CONTEXT_TOKENS = 4096;\n\n/** Physical cores minus one (logical on SMT machines: a ceiling, not an exact count). */\nexport function generationThreads(): number {\n const configured = Number(process.env.CODEBUDDY_LLM_THREADS);\n if (Number.isInteger(configured) && configured > 0) return configured;\n return Math.max(1, availableParallelism() - 1);\n}\n\nasync function load(spec: LocalModelSpec): Promise<Loaded> {\n if (loaded?.spec.id === spec.id) return loaded;\n if (!loading) {\n loading = (async () => {\n const runtime = await importRuntime();\n const llama = await runtime.getLlama({ gpu: \"auto\" });\n const model = await llama.loadModel({ modelPath: localModelPath(spec) });\n const context = await model.createContext({\n contextSize: CONTEXT_TOKENS,\n threads: generationThreads(),\n });\n loaded = { spec, llama, model, context, sequence: context.getSequence(), runtime };\n return loaded;\n })().finally(() => {\n loading = null;\n });\n }\n return loading;\n}\n\n/**\n * The model leaves memory this long after its last generation, so a finished\n * card pass does not keep ~1 GB resident in `codebuddy serve` for the rest of\n * the session. The next pass (or a warm-up) loads it again in under a second.\n */\nexport const IDLE_UNLOAD_MS = 60_000;\nlet idleTimer: NodeJS.Timeout | null = null;\nlet running = 0;\n\nfunction scheduleUnload(): void {\n if (idleTimer) clearTimeout(idleTimer);\n idleTimer = setTimeout(() => void unloadLocalModel(), IDLE_UNLOAD_MS);\n // Never keeps the process alive on its own.\n idleTimer.unref();\n}\n\n/** Free the model and its context now (idle timer, `serve` shutdown). Never throws. */\nexport async function unloadLocalModel(): Promise<void> {\n if (idleTimer) clearTimeout(idleTimer);\n idleTimer = null;\n const current = loaded;\n if (!current || running > 0) return;\n loaded = null;\n grammars.clear();\n await current.context.dispose().catch(() => {});\n await current.model.dispose().catch(() => {});\n}\n\n/** True once the model is resident in this process (query expansion only runs then). */\nexport function isLocalModelWarm(): boolean {\n return loaded !== null;\n}\n\n/** Load the model now (background warm-up); resolves false when it cannot load. */\nexport async function warmLocalModel(spec: LocalModelSpec): Promise<boolean> {\n return (await loadLocalModel(spec)) === null;\n}\n\n/** Load the model now; null on success, else why it could not load. Never throws. */\nexport async function loadLocalModel(spec: LocalModelSpec): Promise<string | null> {\n try {\n await load(spec);\n return null;\n } catch (error) {\n const message = (error as Error)?.message ?? String(error);\n return /Cannot find (package|module)[^\\n]*node-llama-cpp/i.test(message)\n ? `node-llama-cpp runtime not found (npm install -g ${LLAMA_RUNTIME})`\n : `local model failed to load: ${message.split(\"\\n\")[0]?.slice(0, 160)}`;\n }\n}\n\n/**\n * `chatJson` for the local backend: JSON constrained by a grammar built from\n * the request's zod schema (plain JSON grammar if the schema cannot be\n * converted), then validated with zod. Never throws.\n */\nexport async function localChatJson<T>(\n spec: LocalModelSpec,\n request: ChatJsonRequest<T>,\n): Promise<LlmResult<T>> {\n const started = Date.now();\n const fail = (reason: \"timeout\" | \"invalid\" | \"disabled\", detail: string): LlmResult<T> => ({\n ok: false,\n reason,\n detail: detail.slice(0, 300),\n latencyMs: Date.now() - started,\n });\n let model: Loaded;\n try {\n model = await withDeadline(load(spec), request.deadlineMs);\n } catch (error) {\n return (error as Error).name === \"TimeoutError\"\n ? fail(\"timeout\", \"model still loading\")\n : fail(\"disabled\", `local model unavailable: ${(error as Error).message}`);\n }\n const run = async (): Promise<LlmResult<T>> => {\n running++;\n try {\n return await generate();\n } finally {\n running--;\n scheduleUnload();\n }\n };\n const generate = async (): Promise<LlmResult<T>> => {\n // The idle unload may have run while this call waited in the queue.\n if (!loaded) model = await load(spec);\n const remaining = request.deadlineMs - (Date.now() - started);\n if (remaining <= 0) return fail(\"timeout\", `no answer within ${request.deadlineMs} ms`);\n const grammar = await grammarFor(model.llama, request.jsonSchema ?? request.schema);\n const session = new model.runtime.LlamaChatSession({\n contextSequence: model.sequence,\n autoDisposeSequence: false,\n systemPrompt: request.system,\n });\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), remaining);\n try {\n const text = await session.prompt(`${spec.noThink ? \"/no_think\\n\" : \"\"}${request.user}`, {\n grammar,\n maxTokens: request.maxTokens,\n temperature: 0,\n ...(spec.noThink ? { budgets: { thoughtTokens: 0 } } : {}),\n signal: controller.signal,\n stopOnAbortSignal: true,\n });\n if (controller.signal.aborted) {\n return fail(\"timeout\", `no answer within ${request.deadlineMs} ms`);\n }\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch {\n return fail(\"invalid\", `output is not JSON: ${text.slice(0, 120)}`);\n }\n const parsed = request.schema.safeParse(value);\n if (!parsed.success)\n return fail(\"invalid\", `output failed validation: ${text.slice(0, 120)}`);\n return {\n ok: true,\n value: parsed.data,\n model: spec.id,\n latencyMs: Date.now() - started,\n tokens: Math.ceil((request.system.length + request.user.length + text.length) / 4),\n cached: false,\n };\n } catch (error) {\n return controller.signal.aborted\n ? fail(\"timeout\", `no answer within ${request.deadlineMs} ms`)\n : fail(\"invalid\", String((error as Error)?.message ?? error));\n } finally {\n clearTimeout(timer);\n session.dispose({ disposeSequence: false });\n }\n };\n const result = queue.then(run, run);\n queue = result.catch(() => undefined);\n return result;\n}\n\nasync function grammarFor(llama: Llama, schema: z.ZodType | object): Promise<unknown> {\n let grammar = grammars.get(schema);\n if (!grammar) {\n grammar = (async () => {\n try {\n const json =\n schema instanceof z.ZodType\n ? z.toJSONSchema(schema, { io: \"input\", unrepresentable: \"any\" })\n : schema;\n return await llama.createGrammarForJsonSchema(stripSchemaMeta(json));\n } catch {\n return llama.getGrammarFor(\"json\");\n }\n })();\n grammars.set(schema, grammar);\n }\n return grammar;\n}\n\n/** llama.cpp's schema grammar wants plain structure: no `$schema`, no defaults. */\nfunction stripSchemaMeta(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(stripSchemaMeta);\n if (!value || typeof value !== \"object\") return value;\n const out: Record<string, unknown> = {};\n for (const [key, child] of Object.entries(value)) {\n if (key === \"$schema\" || key === \"default\" || key === \"additionalProperties\") continue;\n out[key] = stripSchemaMeta(child);\n }\n return out;\n}\n\nfunction withDeadline<T>(promise: Promise<T>, ms: number): Promise<T> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(\n () => reject(Object.assign(new Error(\"deadline\"), { name: \"TimeoutError\" })),\n ms,\n );\n promise.then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (error) => {\n clearTimeout(timer);\n reject(error);\n },\n );\n });\n}\n\n// ── Foreground yield ────────────────────────────────────────────────────\n\n/** A prompt hook or MCP tool call touched this within the window: background work waits. */\nconst FOREGROUND_WINDOW_MS = 2_000;\nconst FOREGROUND_MAX_WAIT_MS = 15_000;\n\nfunction foregroundPath(root: string): string {\n return join(root, \".codebuddy\", \"cache\", \"foreground\");\n}\n\n/** Record that a hook or tool call is running (cheap: one utimes). Never throws. */\nexport async function markForeground(root: string): Promise<void> {\n const path = foregroundPath(root);\n const now = new Date();\n try {\n await utimes(path, now, now);\n } catch {\n await mkdir(dirname(path), { recursive: true }).catch(() => {});\n await writeFile(path, \"\").catch(() => {});\n }\n}\n\n/** Wait (bounded) until no foreground work was marked within the window. */\nexport async function waitForForeground(root: string, signal?: AbortSignal): Promise<void> {\n const until = Date.now() + FOREGROUND_MAX_WAIT_MS;\n while (Date.now() < until && !signal?.aborted) {\n const info = await stat(foregroundPath(root)).catch(() => null);\n if (!info || Date.now() - info.mtimeMs > FOREGROUND_WINDOW_MS) return;\n await new Promise((resolve) => setTimeout(resolve, 250));\n }\n}\n\n// ── Status ──────────────────────────────────────────────────────────────\n\nexport type LocalModelStatus = {\n runtime: boolean;\n installed: LocalModelSpec | null;\n path: string | null;\n};\n\nexport async function localModelStatus(preferred?: LocalModelSize): Promise<LocalModelStatus> {\n const installed = await installedLocalModel(preferred);\n return {\n runtime: await localRuntimeAvailable(),\n installed,\n path: installed ? localModelPath(installed) : null,\n };\n}\n\nexport function formatBytes(bytes: number): string {\n return bytes >= GB ? `${(bytes / GB).toFixed(1)} GB` : `${Math.round(bytes / 1024 ** 2)} MB`;\n}\n\n/** Free RAM now (for the `models add` warning; the hard check uses total RAM). */\nexport function freeMemory(): number {\n return freemem();\n}\n"],"mappings":"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","names":[]}
@@ -19,7 +19,7 @@ import {
19
19
  sessionSummaries,
20
20
  shares,
21
21
  validSearchVector
22
- } from "./chunk-VBDM7HC5.js";
22
+ } from "./chunk-3L5IR6OW.js";
23
23
  import {
24
24
  computeContentHash,
25
25
  computeFactHash,
@@ -3388,4 +3388,4 @@ export {
3388
3388
  inspectNamespace,
3389
3389
  runDoctor
3390
3390
  };
3391
- //# sourceMappingURL=chunk-IM5RFEW4.js.map
3391
+ //# sourceMappingURL=chunk-TIMS3PB7.js.map
package/dist/cli/index.js CHANGED
@@ -11,10 +11,10 @@ if (command === "hooks" && (subcommand === "context" || subcommand === "lean"))
11
11
  const input = await readHookInput();
12
12
  if (skipPromptRetrieval(input.prompt)) return;
13
13
  if (subcommand === "lean") {
14
- const { runLeanHook } = await import("../lean-CBHVW6JM.js");
14
+ const { runLeanHook } = await import("../lean-5N6BOONS.js");
15
15
  await runLeanHook(input, writeStdout);
16
16
  } else {
17
- const { runContextHook } = await import("../runtime-W5ACTDFS.js");
17
+ const { runContextHook } = await import("../runtime-SZAWIXEB.js");
18
18
  await runContextHook(input, writeStdout);
19
19
  }
20
20
  });
@@ -24,7 +24,7 @@ if (command === "hooks" && (subcommand === "context" || subcommand === "lean"))
24
24
  } else if (command === "hooks" && (subcommand === "stage" || subcommand === "reset" || subcommand === "checkpoint" || subcommand === "resume")) {
25
25
  const [{ failSoft, readHookInput, writeStdout }, runtime] = await Promise.all([
26
26
  import("../stdio-HOV4P3DQ.js"),
27
- import("../runtime-W5ACTDFS.js")
27
+ import("../runtime-SZAWIXEB.js")
28
28
  ]);
29
29
  await failSoft(async () => {
30
30
  const input = await readHookInput();
@@ -34,7 +34,7 @@ if (command === "hooks" && (subcommand === "context" || subcommand === "lean"))
34
34
  else await runtime.runResetHook(input, writeStdout);
35
35
  });
36
36
  } else {
37
- const { createCli } = await import("../main-QF6Z552J.js");
37
+ const { createCli } = await import("../main-B3HZCGVE.js");
38
38
  await createCli().parseAsync();
39
39
  }
40
40
  //# sourceMappingURL=index.js.map
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  redactLlmKeys,
3
3
  resolveLlmConfig
4
- } from "./chunk-RGIVHKBH.js";
4
+ } from "./chunk-4TKKLQLH.js";
5
5
  import "./chunk-Y4QQML24.js";
6
6
  import "./chunk-CG5B3LYG.js";
7
- import "./chunk-NNJSAYHY.js";
7
+ import "./chunk-SXVH74NE.js";
8
8
  import "./chunk-3QGW53GD.js";
9
9
  import "./chunk-HPOR6YXA.js";
10
10
  import "./chunk-PZ5AY32C.js";
@@ -12,4 +12,4 @@ export {
12
12
  redactLlmKeys,
13
13
  resolveLlmConfig
14
14
  };
15
- //# sourceMappingURL=config-S2BDMT4B.js.map
15
+ //# sourceMappingURL=config-32X53LWY.js.map
@@ -1,19 +1,19 @@
1
1
  import {
2
2
  chatJson
3
- } from "./chunk-MNSMW4MZ.js";
3
+ } from "./chunk-7IXPARDM.js";
4
4
  import {
5
5
  redactSensitive
6
6
  } from "./chunk-AL6VBTNA.js";
7
7
  import {
8
8
  resolveLlmConfig
9
- } from "./chunk-RGIVHKBH.js";
9
+ } from "./chunk-4TKKLQLH.js";
10
10
  import "./chunk-Y4QQML24.js";
11
11
  import {
12
12
  writeAtomic
13
13
  } from "./chunk-CG5B3LYG.js";
14
14
  import {
15
15
  isLocalModelWarm
16
- } from "./chunk-NNJSAYHY.js";
16
+ } from "./chunk-SXVH74NE.js";
17
17
  import "./chunk-3QGW53GD.js";
18
18
  import "./chunk-HPOR6YXA.js";
19
19
  import "./chunk-PZ5AY32C.js";
@@ -95,4 +95,4 @@ export {
95
95
  expandTask,
96
96
  normaliseTask
97
97
  };
98
- //# sourceMappingURL=expand-TONFVT5D.js.map
98
+ //# sourceMappingURL=expand-VFYOWNZS.js.map
@@ -3,8 +3,9 @@ import {
3
3
  rankFilesByVector,
4
4
  refreshFileVectors,
5
5
  syncFileVectors
6
- } from "./chunk-4DTO2E4Y.js";
6
+ } from "./chunk-LZTYPJ5I.js";
7
7
  import "./chunk-Y7D7PUBE.js";
8
+ import "./chunk-R5JRKMEP.js";
8
9
  import "./chunk-AL6VBTNA.js";
9
10
  import "./chunk-Y4QQML24.js";
10
11
  import "./chunk-CG5B3LYG.js";
@@ -16,4 +17,4 @@ export {
16
17
  refreshFileVectors,
17
18
  syncFileVectors
18
19
  };
19
- //# sourceMappingURL=file-vectors-VREL75MA.js.map
20
+ //# sourceMappingURL=file-vectors-UBOKGYXX.js.map
@@ -10,15 +10,15 @@ import {
10
10
  pruneIndexFacts,
11
11
  pyprojectFacts,
12
12
  workflowFacts
13
- } from "./chunk-BNZDEU5K.js";
14
- import "./chunk-MNSMW4MZ.js";
15
- import "./chunk-VBDM7HC5.js";
13
+ } from "./chunk-R5E2YGLE.js";
14
+ import "./chunk-7IXPARDM.js";
15
+ import "./chunk-3L5IR6OW.js";
16
16
  import "./chunk-EMWGLKPR.js";
17
17
  import "./chunk-AL6VBTNA.js";
18
- import "./chunk-RGIVHKBH.js";
18
+ import "./chunk-4TKKLQLH.js";
19
19
  import "./chunk-Y4QQML24.js";
20
20
  import "./chunk-CG5B3LYG.js";
21
- import "./chunk-NNJSAYHY.js";
21
+ import "./chunk-SXVH74NE.js";
22
22
  import "./chunk-3QGW53GD.js";
23
23
  import "./chunk-HPOR6YXA.js";
24
24
  import "./chunk-PZ5AY32C.js";
@@ -35,4 +35,4 @@ export {
35
35
  pyprojectFacts,
36
36
  workflowFacts
37
37
  };
38
- //# sourceMappingURL=index-facts-MBJKBYA3.js.map
38
+ //# sourceMappingURL=index-facts-ZTUANWBF.js.map
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  runIndexModelPass
3
- } from "./chunk-VF7DKRIU.js";
4
- import "./chunk-6OQKL35B.js";
5
- import "./chunk-RGIVHKBH.js";
3
+ } from "./chunk-6KSSJJZO.js";
4
+ import "./chunk-R5JRKMEP.js";
5
+ import "./chunk-4TKKLQLH.js";
6
6
  import "./chunk-Y4QQML24.js";
7
7
  import "./chunk-CG5B3LYG.js";
8
- import "./chunk-NNJSAYHY.js";
8
+ import "./chunk-SXVH74NE.js";
9
9
  import "./chunk-3QGW53GD.js";
10
10
  import "./chunk-HPOR6YXA.js";
11
11
  import "./chunk-PZ5AY32C.js";
12
12
  export {
13
13
  runIndexModelPass
14
14
  };
15
- //# sourceMappingURL=index-pass-RP23G2CR.js.map
15
+ //# sourceMappingURL=index-pass-S6NC25P5.js.map
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  migrateMemoryToFiles,
4
4
  registerCodeBuddyTools,
5
5
  startMcpServer
6
- } from "./chunk-GNV4X67F.js";
6
+ } from "./chunk-KGDRGA2L.js";
7
7
  import {
8
8
  ACTIVE_PLAN_STATUSES,
9
9
  ActivePlanExistsError,
@@ -21,19 +21,20 @@ import {
21
21
  createPolicySignal,
22
22
  readPolicyFile,
23
23
  resolveRiskPaths
24
- } from "./chunk-OO7Q7XZF.js";
24
+ } from "./chunk-5ZLNHYJH.js";
25
25
  import "./chunk-YN7TEIUH.js";
26
26
  import "./chunk-7IPSDDO5.js";
27
27
  import "./chunk-TGFTKWOR.js";
28
28
  import "./chunk-GQKWUHMA.js";
29
29
  import "./chunk-DFV2SK6C.js";
30
- import "./chunk-RKF4HBBQ.js";
30
+ import "./chunk-4LMBZWIF.js";
31
31
  import {
32
32
  buildArchitectureMap,
33
33
  neighbours,
34
34
  queryMap
35
- } from "./chunk-4DTO2E4Y.js";
35
+ } from "./chunk-LZTYPJ5I.js";
36
36
  import "./chunk-Y7D7PUBE.js";
37
+ import "./chunk-R5JRKMEP.js";
37
38
  import {
38
39
  CodeBuddy,
39
40
  DefaultPolicy,
@@ -48,7 +49,7 @@ import {
48
49
  isLangSmithTracingEnabled,
49
50
  listFactsPage,
50
51
  runDoctor
51
- } from "./chunk-IM5RFEW4.js";
52
+ } from "./chunk-TIMS3PB7.js";
52
53
  import {
53
54
  clampBudgetForCallerModel,
54
55
  countTokens
@@ -57,7 +58,7 @@ import {
57
58
  FACT_CATEGORIES,
58
59
  INCIDENT_SEVERITIES,
59
60
  MemoryFileStore
60
- } from "./chunk-VBDM7HC5.js";
61
+ } from "./chunk-3L5IR6OW.js";
61
62
  import "./chunk-EMWGLKPR.js";
62
63
  import "./chunk-AL6VBTNA.js";
63
64
  import "./chunk-6JCKMIQY.js";
@@ -73,7 +74,7 @@ import {
73
74
  setPlanPolicy
74
75
  } from "./chunk-Y4QQML24.js";
75
76
  import "./chunk-CG5B3LYG.js";
76
- import "./chunk-NNJSAYHY.js";
77
+ import "./chunk-SXVH74NE.js";
77
78
  import {
78
79
  LocalModelProvider
79
80
  } from "./chunk-3QGW53GD.js";
@@ -3,19 +3,20 @@ import {
3
3
  installClient,
4
4
  setHooks,
5
5
  uninstallClient
6
- } from "./chunk-PZ4EBFIA.js";
7
- import "./chunk-AFXWCSQ4.js";
6
+ } from "./chunk-SPQU2422.js";
7
+ import "./chunk-3TZNQJTH.js";
8
8
  import "./chunk-V7PT6ER5.js";
9
- import "./chunk-OO7Q7XZF.js";
9
+ import "./chunk-5ZLNHYJH.js";
10
10
  import "./chunk-YN7TEIUH.js";
11
11
  import "./chunk-7IPSDDO5.js";
12
12
  import "./chunk-TGFTKWOR.js";
13
13
  import "./chunk-GQKWUHMA.js";
14
14
  import "./chunk-DFV2SK6C.js";
15
- import "./chunk-4DTO2E4Y.js";
15
+ import "./chunk-LZTYPJ5I.js";
16
16
  import "./chunk-Y7D7PUBE.js";
17
+ import "./chunk-R5JRKMEP.js";
17
18
  import "./chunk-57642NGU.js";
18
- import "./chunk-VBDM7HC5.js";
19
+ import "./chunk-3L5IR6OW.js";
19
20
  import "./chunk-EMWGLKPR.js";
20
21
  import "./chunk-AL6VBTNA.js";
21
22
  import "./chunk-Y4QQML24.js";
@@ -28,4 +29,4 @@ export {
28
29
  setHooks,
29
30
  uninstallClient
30
31
  };
31
- //# sourceMappingURL=install-MCJIJMFF.js.map
32
+ //# sourceMappingURL=install-ZTE3H7QM.js.map
@@ -5,12 +5,13 @@ import {
5
5
  isReadOnlyPrompt,
6
6
  renderLeanBlock,
7
7
  runLeanHook
8
- } from "./chunk-AFXWCSQ4.js";
8
+ } from "./chunk-3TZNQJTH.js";
9
9
  import "./chunk-7IPSDDO5.js";
10
10
  import "./chunk-TGFTKWOR.js";
11
11
  import "./chunk-GQKWUHMA.js";
12
- import "./chunk-4DTO2E4Y.js";
12
+ import "./chunk-LZTYPJ5I.js";
13
13
  import "./chunk-Y7D7PUBE.js";
14
+ import "./chunk-R5JRKMEP.js";
14
15
  import "./chunk-57642NGU.js";
15
16
  import "./chunk-EMWGLKPR.js";
16
17
  import "./chunk-AL6VBTNA.js";
@@ -26,4 +27,4 @@ export {
26
27
  renderLeanBlock,
27
28
  runLeanHook
28
29
  };
29
- //# sourceMappingURL=lean-CBHVW6JM.js.map
30
+ //# sourceMappingURL=lean-5N6BOONS.js.map
@@ -9,6 +9,7 @@ import {
9
9
  generationThreads,
10
10
  installedLocalModel,
11
11
  isLocalModelWarm,
12
+ loadLocalModel,
12
13
  localChatJson,
13
14
  localModelPath,
14
15
  localModelStatus,
@@ -19,7 +20,7 @@ import {
19
20
  unloadLocalModel,
20
21
  waitForForeground,
21
22
  warmLocalModel
22
- } from "./chunk-NNJSAYHY.js";
23
+ } from "./chunk-SXVH74NE.js";
23
24
  import "./chunk-3QGW53GD.js";
24
25
  import "./chunk-HPOR6YXA.js";
25
26
  import "./chunk-PZ5AY32C.js";
@@ -34,6 +35,7 @@ export {
34
35
  generationThreads,
35
36
  installedLocalModel,
36
37
  isLocalModelWarm,
38
+ loadLocalModel,
37
39
  localChatJson,
38
40
  localModelPath,
39
41
  localModelStatus,
@@ -45,4 +47,4 @@ export {
45
47
  waitForForeground,
46
48
  warmLocalModel
47
49
  };
48
- //# sourceMappingURL=local-TH5XCLVJ.js.map
50
+ //# sourceMappingURL=local-5EYLEBEP.js.map
@@ -3,10 +3,10 @@ import {
3
3
  addLocalLlm,
4
4
  printLocalLlmStatus,
5
5
  removeLocalLlm
6
- } from "./chunk-K4XNTYV7.js";
6
+ } from "./chunk-FEFOZ2ZW.js";
7
7
  import "./chunk-Y4QQML24.js";
8
8
  import "./chunk-CG5B3LYG.js";
9
- import "./chunk-NNJSAYHY.js";
9
+ import "./chunk-SXVH74NE.js";
10
10
  import "./chunk-3QGW53GD.js";
11
11
  import "./chunk-HPOR6YXA.js";
12
12
  import "./chunk-PZ5AY32C.js";
@@ -16,4 +16,4 @@ export {
16
16
  printLocalLlmStatus,
17
17
  removeLocalLlm
18
18
  };
19
- //# sourceMappingURL=local-llm-GZREB5DF.js.map
19
+ //# sourceMappingURL=local-llm-ESMVP5LE.js.map