ollama-intern-mcp 1.0.0

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 (256) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +252 -0
  3. package/SECURITY.md +22 -0
  4. package/dist/corpus/chunker.d.ts +63 -0
  5. package/dist/corpus/chunker.d.ts.map +1 -0
  6. package/dist/corpus/chunker.js +230 -0
  7. package/dist/corpus/chunker.js.map +1 -0
  8. package/dist/corpus/fusion.d.ts +67 -0
  9. package/dist/corpus/fusion.d.ts.map +1 -0
  10. package/dist/corpus/fusion.js +76 -0
  11. package/dist/corpus/fusion.js.map +1 -0
  12. package/dist/corpus/indexer.d.ts +33 -0
  13. package/dist/corpus/indexer.d.ts.map +1 -0
  14. package/dist/corpus/indexer.js +201 -0
  15. package/dist/corpus/indexer.js.map +1 -0
  16. package/dist/corpus/lexical.d.ts +95 -0
  17. package/dist/corpus/lexical.d.ts.map +1 -0
  18. package/dist/corpus/lexical.js +227 -0
  19. package/dist/corpus/lexical.js.map +1 -0
  20. package/dist/corpus/manifest.d.ts +29 -0
  21. package/dist/corpus/manifest.d.ts.map +1 -0
  22. package/dist/corpus/manifest.js +45 -0
  23. package/dist/corpus/manifest.js.map +1 -0
  24. package/dist/corpus/refresh.d.ts +54 -0
  25. package/dist/corpus/refresh.d.ts.map +1 -0
  26. package/dist/corpus/refresh.js +155 -0
  27. package/dist/corpus/refresh.js.map +1 -0
  28. package/dist/corpus/searcher.d.ts +48 -0
  29. package/dist/corpus/searcher.d.ts.map +1 -0
  30. package/dist/corpus/searcher.js +131 -0
  31. package/dist/corpus/searcher.js.map +1 -0
  32. package/dist/corpus/storage.d.ts +56 -0
  33. package/dist/corpus/storage.d.ts.map +1 -0
  34. package/dist/corpus/storage.js +81 -0
  35. package/dist/corpus/storage.js.map +1 -0
  36. package/dist/coverage.d.ts +38 -0
  37. package/dist/coverage.d.ts.map +1 -0
  38. package/dist/coverage.js +117 -0
  39. package/dist/coverage.js.map +1 -0
  40. package/dist/embedMath.d.ts +22 -0
  41. package/dist/embedMath.d.ts.map +1 -0
  42. package/dist/embedMath.js +34 -0
  43. package/dist/embedMath.js.map +1 -0
  44. package/dist/envelope.d.ts +53 -0
  45. package/dist/envelope.d.ts.map +1 -0
  46. package/dist/envelope.js +32 -0
  47. package/dist/envelope.js.map +1 -0
  48. package/dist/errors.d.ts +20 -0
  49. package/dist/errors.d.ts.map +1 -0
  50. package/dist/errors.js +30 -0
  51. package/dist/errors.js.map +1 -0
  52. package/dist/evals/retrieval.d.ts +68 -0
  53. package/dist/evals/retrieval.d.ts.map +1 -0
  54. package/dist/evals/retrieval.js +173 -0
  55. package/dist/evals/retrieval.js.map +1 -0
  56. package/dist/guardrails/citations.d.ts +28 -0
  57. package/dist/guardrails/citations.d.ts.map +1 -0
  58. package/dist/guardrails/citations.js +45 -0
  59. package/dist/guardrails/citations.js.map +1 -0
  60. package/dist/guardrails/compileCheck.d.ts +20 -0
  61. package/dist/guardrails/compileCheck.d.ts.map +1 -0
  62. package/dist/guardrails/compileCheck.js +96 -0
  63. package/dist/guardrails/compileCheck.js.map +1 -0
  64. package/dist/guardrails/confidence.d.ts +21 -0
  65. package/dist/guardrails/confidence.d.ts.map +1 -0
  66. package/dist/guardrails/confidence.js +18 -0
  67. package/dist/guardrails/confidence.js.map +1 -0
  68. package/dist/guardrails/timeouts.d.ts +35 -0
  69. package/dist/guardrails/timeouts.d.ts.map +1 -0
  70. package/dist/guardrails/timeouts.js +58 -0
  71. package/dist/guardrails/timeouts.js.map +1 -0
  72. package/dist/guardrails/writeConfirm.d.ts +25 -0
  73. package/dist/guardrails/writeConfirm.d.ts.map +1 -0
  74. package/dist/guardrails/writeConfirm.js +35 -0
  75. package/dist/guardrails/writeConfirm.js.map +1 -0
  76. package/dist/index.d.ts +12 -0
  77. package/dist/index.d.ts.map +1 -0
  78. package/dist/index.js +153 -0
  79. package/dist/index.js.map +1 -0
  80. package/dist/observability.d.ts +66 -0
  81. package/dist/observability.d.ts.map +1 -0
  82. package/dist/observability.js +51 -0
  83. package/dist/observability.js.map +1 -0
  84. package/dist/ollama.d.ts +105 -0
  85. package/dist/ollama.d.ts.map +1 -0
  86. package/dist/ollama.js +109 -0
  87. package/dist/ollama.js.map +1 -0
  88. package/dist/prewarm.d.ts +28 -0
  89. package/dist/prewarm.d.ts.map +1 -0
  90. package/dist/prewarm.js +69 -0
  91. package/dist/prewarm.js.map +1 -0
  92. package/dist/profiles.d.ts +54 -0
  93. package/dist/profiles.d.ts.map +1 -0
  94. package/dist/profiles.js +95 -0
  95. package/dist/profiles.js.map +1 -0
  96. package/dist/protectedPaths.d.ts +33 -0
  97. package/dist/protectedPaths.d.ts.map +1 -0
  98. package/dist/protectedPaths.js +54 -0
  99. package/dist/protectedPaths.js.map +1 -0
  100. package/dist/runContext.d.ts +21 -0
  101. package/dist/runContext.d.ts.map +1 -0
  102. package/dist/runContext.js +9 -0
  103. package/dist/runContext.js.map +1 -0
  104. package/dist/semaphore.d.ts +16 -0
  105. package/dist/semaphore.d.ts.map +1 -0
  106. package/dist/semaphore.js +37 -0
  107. package/dist/semaphore.js.map +1 -0
  108. package/dist/sources.d.ts +19 -0
  109. package/dist/sources.d.ts.map +1 -0
  110. package/dist/sources.js +41 -0
  111. package/dist/sources.js.map +1 -0
  112. package/dist/tiers.d.ts +39 -0
  113. package/dist/tiers.d.ts.map +1 -0
  114. package/dist/tiers.js +44 -0
  115. package/dist/tiers.js.map +1 -0
  116. package/dist/tools/artifactDiff.d.ts +66 -0
  117. package/dist/tools/artifactDiff.d.ts.map +1 -0
  118. package/dist/tools/artifactDiff.js +66 -0
  119. package/dist/tools/artifactDiff.js.map +1 -0
  120. package/dist/tools/artifactExportToPath.d.ts +38 -0
  121. package/dist/tools/artifactExportToPath.d.ts.map +1 -0
  122. package/dist/tools/artifactExportToPath.js +51 -0
  123. package/dist/tools/artifactExportToPath.js.map +1 -0
  124. package/dist/tools/artifactList.d.ts +60 -0
  125. package/dist/tools/artifactList.d.ts.map +1 -0
  126. package/dist/tools/artifactList.js +107 -0
  127. package/dist/tools/artifactList.js.map +1 -0
  128. package/dist/tools/artifactRead.d.ts +43 -0
  129. package/dist/tools/artifactRead.d.ts.map +1 -0
  130. package/dist/tools/artifactRead.js +83 -0
  131. package/dist/tools/artifactRead.js.map +1 -0
  132. package/dist/tools/artifactSnippets.d.ts +56 -0
  133. package/dist/tools/artifactSnippets.d.ts.map +1 -0
  134. package/dist/tools/artifactSnippets.js +97 -0
  135. package/dist/tools/artifactSnippets.js.map +1 -0
  136. package/dist/tools/artifacts/diff.d.ts +161 -0
  137. package/dist/tools/artifacts/diff.d.ts.map +1 -0
  138. package/dist/tools/artifacts/diff.js +267 -0
  139. package/dist/tools/artifacts/diff.js.map +1 -0
  140. package/dist/tools/artifacts/export.d.ts +43 -0
  141. package/dist/tools/artifacts/export.d.ts.map +1 -0
  142. package/dist/tools/artifacts/export.js +113 -0
  143. package/dist/tools/artifacts/export.js.map +1 -0
  144. package/dist/tools/artifacts/scan.d.ts +86 -0
  145. package/dist/tools/artifacts/scan.d.ts.map +1 -0
  146. package/dist/tools/artifacts/scan.js +251 -0
  147. package/dist/tools/artifacts/scan.js.map +1 -0
  148. package/dist/tools/artifacts/snippets.d.ts +42 -0
  149. package/dist/tools/artifacts/snippets.d.ts.map +1 -0
  150. package/dist/tools/artifacts/snippets.js +165 -0
  151. package/dist/tools/artifacts/snippets.js.map +1 -0
  152. package/dist/tools/batch.d.ts +65 -0
  153. package/dist/tools/batch.d.ts.map +1 -0
  154. package/dist/tools/batch.js +117 -0
  155. package/dist/tools/batch.js.map +1 -0
  156. package/dist/tools/briefs/common.d.ts +65 -0
  157. package/dist/tools/briefs/common.d.ts.map +1 -0
  158. package/dist/tools/briefs/common.js +128 -0
  159. package/dist/tools/briefs/common.js.map +1 -0
  160. package/dist/tools/briefs/evidence.d.ts +40 -0
  161. package/dist/tools/briefs/evidence.d.ts.map +1 -0
  162. package/dist/tools/briefs/evidence.js +103 -0
  163. package/dist/tools/briefs/evidence.js.map +1 -0
  164. package/dist/tools/changeBrief.d.ts +85 -0
  165. package/dist/tools/changeBrief.d.ts.map +1 -0
  166. package/dist/tools/changeBrief.js +228 -0
  167. package/dist/tools/changeBrief.js.map +1 -0
  168. package/dist/tools/chat.d.ts +42 -0
  169. package/dist/tools/chat.d.ts.map +1 -0
  170. package/dist/tools/chat.js +49 -0
  171. package/dist/tools/chat.js.map +1 -0
  172. package/dist/tools/classify.d.ts +56 -0
  173. package/dist/tools/classify.d.ts.map +1 -0
  174. package/dist/tools/classify.js +103 -0
  175. package/dist/tools/classify.js.map +1 -0
  176. package/dist/tools/corpusAnswer.d.ts +79 -0
  177. package/dist/tools/corpusAnswer.d.ts.map +1 -0
  178. package/dist/tools/corpusAnswer.js +259 -0
  179. package/dist/tools/corpusAnswer.js.map +1 -0
  180. package/dist/tools/corpusIndex.d.ts +30 -0
  181. package/dist/tools/corpusIndex.d.ts.map +1 -0
  182. package/dist/tools/corpusIndex.js +67 -0
  183. package/dist/tools/corpusIndex.js.map +1 -0
  184. package/dist/tools/corpusList.d.ts +19 -0
  185. package/dist/tools/corpusList.d.ts.map +1 -0
  186. package/dist/tools/corpusList.js +34 -0
  187. package/dist/tools/corpusList.js.map +1 -0
  188. package/dist/tools/corpusRefresh.d.ts +29 -0
  189. package/dist/tools/corpusRefresh.d.ts.map +1 -0
  190. package/dist/tools/corpusRefresh.js +58 -0
  191. package/dist/tools/corpusRefresh.js.map +1 -0
  192. package/dist/tools/corpusSearch.d.ts +44 -0
  193. package/dist/tools/corpusSearch.d.ts.map +1 -0
  194. package/dist/tools/corpusSearch.js +80 -0
  195. package/dist/tools/corpusSearch.js.map +1 -0
  196. package/dist/tools/draft.d.ts +38 -0
  197. package/dist/tools/draft.d.ts.map +1 -0
  198. package/dist/tools/draft.js +72 -0
  199. package/dist/tools/draft.js.map +1 -0
  200. package/dist/tools/embed.d.ts +27 -0
  201. package/dist/tools/embed.d.ts.map +1 -0
  202. package/dist/tools/embed.js +41 -0
  203. package/dist/tools/embed.js.map +1 -0
  204. package/dist/tools/embedSearch.d.ts +58 -0
  205. package/dist/tools/embedSearch.d.ts.map +1 -0
  206. package/dist/tools/embedSearch.js +76 -0
  207. package/dist/tools/embedSearch.js.map +1 -0
  208. package/dist/tools/extract.d.ts +58 -0
  209. package/dist/tools/extract.d.ts.map +1 -0
  210. package/dist/tools/extract.js +93 -0
  211. package/dist/tools/extract.js.map +1 -0
  212. package/dist/tools/incidentBrief.d.ts +81 -0
  213. package/dist/tools/incidentBrief.d.ts.map +1 -0
  214. package/dist/tools/incidentBrief.js +252 -0
  215. package/dist/tools/incidentBrief.js.map +1 -0
  216. package/dist/tools/packs/changePack.d.ts +166 -0
  217. package/dist/tools/packs/changePack.d.ts.map +1 -0
  218. package/dist/tools/packs/changePack.js +470 -0
  219. package/dist/tools/packs/changePack.js.map +1 -0
  220. package/dist/tools/packs/incidentPack.d.ts +125 -0
  221. package/dist/tools/packs/incidentPack.d.ts.map +1 -0
  222. package/dist/tools/packs/incidentPack.js +354 -0
  223. package/dist/tools/packs/incidentPack.js.map +1 -0
  224. package/dist/tools/packs/repoPack.d.ts +207 -0
  225. package/dist/tools/packs/repoPack.d.ts.map +1 -0
  226. package/dist/tools/packs/repoPack.js +456 -0
  227. package/dist/tools/packs/repoPack.js.map +1 -0
  228. package/dist/tools/repoBrief.d.ts +81 -0
  229. package/dist/tools/repoBrief.d.ts.map +1 -0
  230. package/dist/tools/repoBrief.js +213 -0
  231. package/dist/tools/repoBrief.js.map +1 -0
  232. package/dist/tools/research.d.ts +45 -0
  233. package/dist/tools/research.d.ts.map +1 -0
  234. package/dist/tools/research.js +100 -0
  235. package/dist/tools/research.js.map +1 -0
  236. package/dist/tools/runner.d.ts +24 -0
  237. package/dist/tools/runner.d.ts.map +1 -0
  238. package/dist/tools/runner.js +47 -0
  239. package/dist/tools/runner.js.map +1 -0
  240. package/dist/tools/summarizeDeep.d.ts +49 -0
  241. package/dist/tools/summarizeDeep.d.ts.map +1 -0
  242. package/dist/tools/summarizeDeep.js +109 -0
  243. package/dist/tools/summarizeDeep.js.map +1 -0
  244. package/dist/tools/summarizeFast.d.ts +28 -0
  245. package/dist/tools/summarizeFast.d.ts.map +1 -0
  246. package/dist/tools/summarizeFast.js +43 -0
  247. package/dist/tools/summarizeFast.js.map +1 -0
  248. package/dist/tools/triageLogs.d.ts +51 -0
  249. package/dist/tools/triageLogs.d.ts.map +1 -0
  250. package/dist/tools/triageLogs.js +96 -0
  251. package/dist/tools/triageLogs.js.map +1 -0
  252. package/dist/version.d.ts +2 -0
  253. package/dist/version.d.ts.map +1 -0
  254. package/dist/version.js +2 -0
  255. package/dist/version.js.map +1 -0
  256. package/package.json +60 -0
package/dist/index.js ADDED
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Ollama Intern MCP — entrypoint.
4
+ *
5
+ * Registers the 8-tool labor surface. Tool descriptions encode *when* to reach
6
+ * for them — Claude picks the tier by picking the tool, and the tool implies
7
+ * the model. ollama_chat is visibly last-resort.
8
+ */
9
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
11
+ import { fileURLToPath } from "node:url";
12
+ import { realpathSync } from "node:fs";
13
+ import { VERSION } from "./version.js";
14
+ import { loadProfile } from "./profiles.js";
15
+ import { HttpOllamaClient } from "./ollama.js";
16
+ import { NdjsonLogger } from "./observability.js";
17
+ import { toErrorShape } from "./errors.js";
18
+ import { runPrewarm } from "./prewarm.js";
19
+ import { classifySchema, handleClassify } from "./tools/classify.js";
20
+ import { triageLogsSchema, handleTriageLogs } from "./tools/triageLogs.js";
21
+ import { summarizeFastSchema, handleSummarizeFast } from "./tools/summarizeFast.js";
22
+ import { summarizeDeepSchema, handleSummarizeDeep } from "./tools/summarizeDeep.js";
23
+ import { draftSchema, handleDraft } from "./tools/draft.js";
24
+ import { extractSchema, handleExtract } from "./tools/extract.js";
25
+ import { researchSchema, handleResearch } from "./tools/research.js";
26
+ import { embedSchema, handleEmbed } from "./tools/embed.js";
27
+ import { embedSearchSchema, handleEmbedSearch } from "./tools/embedSearch.js";
28
+ import { corpusIndexSchema, handleCorpusIndex } from "./tools/corpusIndex.js";
29
+ import { corpusSearchSchema, handleCorpusSearch } from "./tools/corpusSearch.js";
30
+ import { corpusAnswerSchema, handleCorpusAnswer } from "./tools/corpusAnswer.js";
31
+ import { corpusRefreshSchema, handleCorpusRefresh } from "./tools/corpusRefresh.js";
32
+ import { corpusListSchema, handleCorpusList } from "./tools/corpusList.js";
33
+ import { incidentBriefSchema, handleIncidentBrief } from "./tools/incidentBrief.js";
34
+ import { repoBriefSchema, handleRepoBrief } from "./tools/repoBrief.js";
35
+ import { changeBriefSchema, handleChangeBrief } from "./tools/changeBrief.js";
36
+ import { incidentPackSchema, handleIncidentPack } from "./tools/packs/incidentPack.js";
37
+ import { repoPackSchema, handleRepoPack } from "./tools/packs/repoPack.js";
38
+ import { changePackSchema, handleChangePack } from "./tools/packs/changePack.js";
39
+ import { artifactListSchema, handleArtifactList } from "./tools/artifactList.js";
40
+ import { artifactReadSchema, handleArtifactRead } from "./tools/artifactRead.js";
41
+ import { artifactDiffSchema, handleArtifactDiff } from "./tools/artifactDiff.js";
42
+ import { artifactExportToPathSchema, handleArtifactExportToPath } from "./tools/artifactExportToPath.js";
43
+ import { artifactIncidentNoteSnippetSchema, handleArtifactIncidentNoteSnippet, artifactOnboardingSectionSnippetSchema, handleArtifactOnboardingSectionSnippet, artifactReleaseNoteSnippetSchema, handleArtifactReleaseNoteSnippet, } from "./tools/artifactSnippets.js";
44
+ import { chatSchema, handleChat } from "./tools/chat.js";
45
+ export function createServer(ctx) {
46
+ const server = new McpServer({ name: "ollama-intern-mcp", version: VERSION });
47
+ const wrap = (p) => p.then((value) => ({ content: [{ type: "text", text: JSON.stringify(value, null, 2) }] }), (err) => ({
48
+ content: [{ type: "text", text: JSON.stringify(toErrorShape(err), null, 2) }],
49
+ isError: true,
50
+ }));
51
+ // FLAGSHIP — ollama_research
52
+ server.tool("ollama_research", "FLAGSHIP. Answer a question grounded in specific files. Takes FILE PATHS (not raw text) — reads and chunks locally, returns a digest with validated citations. Use this to understand a repo/doc without burning Claude context on the full content. Citations outside source_paths are stripped server-side.", researchSchema.shape, (args) => wrap(handleResearch(args, ctx)));
53
+ // FLAGSHIP — ollama_corpus_search (persistent concept search over named corpora)
54
+ server.tool("ollama_corpus_search", "FLAGSHIP. Concept search over a persistent named corpus (e.g. 'memory', 'canon', 'handbook'). Pass `corpus` + `query`; returns ranked `[{id, path, score, chunk_index, preview?}]` drawn from the indexed corpus. Use this as your default for semantic recall — the corpus is persistent across sessions so you don't re-embed every call. Build or refresh a corpus with ollama_corpus_index first; see what's available with ollama_corpus_list.", corpusSearchSchema.shape, (args) => wrap(handleCorpusSearch(args, ctx)));
55
+ // FLAGSHIP — ollama_corpus_answer (grounded synthesis over a named corpus)
56
+ server.tool("ollama_corpus_answer", "FLAGSHIP. Answer a question from a NAMED CORPUS with chunk-grounded citations. Retrieves via corpus_search, synthesizes with the Deep tier from the retrieved chunks ONLY, and returns `{answer, citations:[{path, chunk_index, heading_path, title}], covered_sources, omitted_sources, coverage_notes, retrieval:{retrieved, top_score, weak}}`. Distinct from ollama_research: research takes source paths you explicitly hand in; corpus_answer pulls from an already-indexed corpus. Weak retrieval degrades honestly — 0 hits short-circuits without invoking the model; thin retrieval flags `weak: true`.", corpusAnswerSchema.shape, (args) => wrap(handleCorpusAnswer(args, ctx)));
57
+ // FLAGSHIP — ollama_incident_brief (structured operator brief)
58
+ server.tool("ollama_incident_brief", "FLAGSHIP compound job. Produces a STRUCTURED OPERATOR BRIEF from log_text and/or source_paths, optionally blended with a named corpus for background context. Returns `{root_cause_hypotheses, affected_surfaces, timeline_clues, next_checks, evidence, weak, coverage_notes, corpus_used}`. Every hypothesis/surface/clue carries evidence_refs into the evidence array — refs to unknown ids are stripped server-side. Distinct from ollama_triage_logs (symptoms in one blob) and ollama_research (answer a specific question). Thin evidence degrades to weak=true with coverage_notes — never a smooth fake narrative. next_checks are INVESTIGATIVE, not remediations.", incidentBriefSchema.shape, (args) => wrap(handleIncidentBrief(args, ctx)));
59
+ // FLAGSHIP — ollama_repo_brief (operator map of a repo)
60
+ server.tool("ollama_repo_brief", "FLAGSHIP compound job. Produces an OPERATOR MAP of a repo: `{repo_thesis, key_surfaces, architecture_shape, risk_areas, read_next, evidence, weak, coverage_notes, corpus_used}`. Takes source_paths (typically README + key src entries + manifests + docs) and optionally a corpus for cross-cutting context. Not a research clone — research answers a specific question; repo_brief synthesizes orientation. Every key_surface and risk_area cites evidence. read_next is INVESTIGATIVE (files or sections to look at), never prescriptive fixes or refactors. Thin evidence → weak=true with coverage notes.", repoBriefSchema.shape, (args) => wrap(handleRepoBrief(args, ctx)));
61
+ // FLAGSHIP — ollama_change_brief (structured impact brief for a change)
62
+ server.tool("ollama_change_brief", "FLAGSHIP compound job. Produces a CHANGE IMPACT BRIEF: `{change_summary, affected_surfaces, why_it_matters, likely_breakpoints, validation_checks, release_note_draft, evidence, weak, coverage_notes, corpus_used}`. Accepts diff_text (split per file on `diff --git` markers) and/or source_paths (changed files), with an optional corpus for architecture context. Not a git chat bot — structured and reviewable. likely_breakpoints are INVESTIGATIVE reasoning about what could break; validation_checks are what to verify after the change. Never remedial (no 'apply this fix'). release_note_draft is a draft the operator reviews.", changeBriefSchema.shape, (args) => wrap(handleChangeBrief(args, ctx)));
63
+ // PACK — ollama_incident_pack (deterministic orchestration, durable artifact)
64
+ server.tool("ollama_incident_pack", "PACK. Runs the full incident job end-to-end: triage_logs → corpus_search → incident_brief → deterministic markdown+JSON artifact on disk. Single call, single completed job, single pair of files the operator can keep and diff. Response is compact ({artifact:{markdown_path,json_path}, summary, steps}) — the full brief lives in the artifact, not the MCP payload. Fixed pipeline, fixed markdown layout, no prose drift. Use this instead of calling triage_logs + incident_brief manually when you want one deliverable.", incidentPackSchema.shape, (args) => wrap(handleIncidentPack(args, ctx)));
65
+ // PACK — ollama_repo_pack (onboarding job, corpus-first, durable artifact)
66
+ server.tool("ollama_repo_pack", "PACK. Runs the full repo ONBOARDING job: corpus_search (if corpus given) → repo_brief → targeted ollama_extract (narrow onboarding schema: packages, entrypoints, scripts, config_files, exposed_surfaces, runtime_hints) → deterministic markdown+JSON artifact on disk. Corpus-first posture: when a corpus is given, it's the main working surface alongside source_paths. Not repo Q&A — this is `get me onboarded fast with a stable operator artifact`. Response is compact; full brief + extracted facts live in the artifact.", repoPackSchema.shape, (args) => wrap(handleRepoPack(args, ctx)));
67
+ // PACK — ollama_change_pack (change-centered review job, durable artifact)
68
+ server.tool("ollama_change_pack", "PACK. Runs the full change REVIEW job: assemble evidence (diff + paths + corpus if given) → triage_logs ONLY when log_text is provided → change_brief → targeted ollama_extract (narrow review schema: scripts_touched, config_surfaces, runtime_hints) → deterministic markdown+JSON artifact on disk. Change-first, not repo-first — this is about the DELTA, not a tour. Release note draft is a blockquote-wrapped DRAFT (not marketing copy). No VCS integration — caller hands in diff_text / source_paths / optional log_text. Response is compact; full brief + extracted facts live in the artifact.", changePackSchema.shape, (args) => wrap(handleChangePack(args, ctx)));
69
+ // ARTIFACT — ollama_artifact_list (metadata-only index over pack artifacts)
70
+ server.tool("ollama_artifact_list", "ARTIFACT. Metadata-only index of pack artifacts on disk. Returns one compact record per artifact: `{pack, slug, title, created_at, weak, corpus_used, evidence_count, section_counts, md_path, json_path}`. Filter by pack / date_after / date_before / weak_only / strong_only; sort is newest first. Scans ~/.ollama-intern/artifacts/{incident,repo,change} by default; pass extra_artifact_dirs for additional read-only search surfaces. Full payloads belong to ollama_artifact_read — listing stays cheap.", artifactListSchema.shape, (args) => wrap(handleArtifactList(args, ctx)));
71
+ // ARTIFACT — ollama_artifact_read (typed read by identity or path)
72
+ server.tool("ollama_artifact_read", "ARTIFACT. Read a single pack artifact, typed by pack. Primary: `{pack, slug}` — identity-based, collisions fail loud. Secondary: `{json_path}` — absolute path, must live under a recognized artifact dir (canonical roots + extra_artifact_dirs), must end in .json, path-traversal rejected. Returns `{metadata, artifact}` where artifact is a discriminated union on `pack` (incident_pack / repo_pack / change_pack — payloads stay distinct, never flattened).", artifactReadSchema.shape, (args) => wrap(handleArtifactRead(args, ctx)));
73
+ // ARTIFACT — ollama_artifact_diff (structured same-pack comparison)
74
+ server.tool("ollama_artifact_diff", "ARTIFACT. Structured diff of two same-pack artifacts. Input: `{a: {pack, slug}, b: {pack, slug}}` — must share pack; cross-pack diffs refused loudly. Returns `{pack, a, b, weak, diff}` with weak flip surfaced at top level (strong→weak or weak→strong). Lists diff as {added, removed, unchanged} matched on primary key per item kind; narrative fields as {before, after}; release_note_draft also carries a compact LCS line_diff. Evidence is SUMMARIZED (counts + referenced_paths + path_delta), never exploded chunk-by-chunk. Deterministic ordering on every list.", artifactDiffSchema.shape, (args) => wrap(handleArtifactDiff(args, ctx)));
75
+ // ARTIFACT — ollama_artifact_export_to_path (handoff move, narrow writer)
76
+ server.tool("ollama_artifact_export_to_path", "ARTIFACT. Writes the artifact's EXISTING markdown to a caller-specified path with a provenance header prepended. No re-render, no model call. Path safety is strict: target_path must be absolute, must end in .md, must live under one of `allowed_roots` (REQUIRED — caller declares intent). Overwrite is opt-in: existing files refuse by default so re-runs never clobber hand-edits. Not a generic file writer — export is the single handoff move.", artifactExportToPathSchema.shape, (args) => wrap(handleArtifactExportToPath(args, ctx)));
77
+ // ARTIFACT — ollama_artifact_incident_note_snippet (operator note fragment)
78
+ server.tool("ollama_artifact_incident_note_snippet", "ARTIFACT. Renders a compact incident-note markdown fragment from an incident_pack artifact — top hypotheses, affected surfaces, next checks, with an evidence-aware operator tone. No model call, no re-render; pure derivation from stored JSON. Returns `{rendered, metadata}`. For the full artifact use artifact_read; for the whole markdown as a reviewable file use artifact_export_to_path.", artifactIncidentNoteSnippetSchema.shape, (args) => wrap(handleArtifactIncidentNoteSnippet(args, ctx)));
79
+ // ARTIFACT — ollama_artifact_onboarding_section_snippet (handbook fragment)
80
+ server.tool("ollama_artifact_onboarding_section_snippet", "ARTIFACT. Renders a handbook-ready `## What this repo is` section from a repo_pack artifact — thesis, key surfaces, read-next, runtime hints. Investigative tone preserved (read-next is LOOK AT, not prescriptive). No model call. Returns `{rendered, metadata}`.", artifactOnboardingSectionSnippetSchema.shape, (args) => wrap(handleArtifactOnboardingSectionSnippet(args, ctx)));
81
+ // ARTIFACT — ollama_artifact_release_note_snippet (change pack DRAFT fragment)
82
+ server.tool("ollama_artifact_release_note_snippet", "ARTIFACT. Renders the release-note draft from a change_pack artifact as a blockquote-wrapped DRAFT fragment with the caveat preserved. No model call, no polishing, no marketing lift. Returns `{rendered, metadata}` — operator reviews before publishing.", artifactReleaseNoteSnippetSchema.shape, (args) => wrap(handleArtifactReleaseNoteSnippet(args, ctx)));
83
+ // FLAGSHIP — ollama_embed_search (ephemeral concept search on ad-hoc candidates)
84
+ server.tool("ollama_embed_search", "FLAGSHIP. Rank AD-HOC candidates by concept similarity to a query. Pass `query` + `candidates: [{id, text}]`; server embeds everything, computes cosine, returns ranked `[{id, score, preview?}]`. Use this when you have in-memory candidates to compare; for persistent recall over memory/canon/doctrine use ollama_corpus_search instead. Does NOT return raw vectors to you.", embedSearchSchema.shape, (args) => wrap(handleEmbedSearch(args, ctx)));
85
+ // Corpus builder
86
+ server.tool("ollama_corpus_index", "Build a persistent named corpus. Pass `name` + `paths: string[]`; the server chunks, embeds, stores the corpus at ~/.ollama-intern/corpora/<name>.json AND writes a manifest at <name>.manifest.json that captures the declared paths + chunk params + embed model. Idempotent — unchanged files are reused by sha256; changed files are re-embedded; paths not in the input are dropped. For day-to-day upkeep once a manifest exists, prefer `ollama_corpus_refresh` — it reconciles corpus vs manifest and reports drift.", corpusIndexSchema.shape, (args) => wrap(handleCorpusIndex(args, ctx)));
87
+ // Corpus refresh — living-corpus workflow: reconcile against manifest
88
+ server.tool("ollama_corpus_refresh", "Reconcile a named corpus against its manifest (intent vs reality). Single arg: `name`. The manifest's declared paths, chunk params, and embed model are the source of truth — refresh doesn't accept them. Returns a drift report: added / changed / unchanged / deleted / missing (per-path lists) plus reused / reembedded / dropped (chunk-level counts) plus no_op. Idempotent: a no-change refresh is fast, boring, and makes zero embed calls.", corpusRefreshSchema.shape, (args) => wrap(handleCorpusRefresh(args, ctx)));
89
+ // Corpus list
90
+ server.tool("ollama_corpus_list", "List named corpora on disk with stats. No Ollama call. Use to discover what's been indexed, check freshness (indexed_at), or verify a corpus exists before searching.", corpusListSchema.shape, (args) => wrap(handleCorpusList(args, ctx)));
91
+ // LOW-LEVEL — ollama_embed (raw vectors for external index builds)
92
+ server.tool("ollama_embed", "LOW-LEVEL primitive. Returns raw 768-dim vectors for one text or a batch. Use `ollama_embed_search` instead for concept-search — this tool is for building external indexes (sqlite-vss, pgvector) where you need the raw geometry. Output can be large.", embedSchema.shape, (args) => wrap(handleEmbed(args, ctx)));
93
+ // Core — classify (batch-capable)
94
+ server.tool("ollama_classify", "Single-label classification with confidence. Single: pass `text`. BATCH: pass `items:[{id,text}]` — returns ONE envelope with `result.items[]` of `{id, ok, result|error}` plus `batch_count/ok_count/error_count`. Use the batch shape to chew through bulk labeling (commit types, PR titles, log severities) in one handoff instead of N round-trips. Set allow_none=true when weak guesses are worse than 'unsure' — label returns null below threshold (default 0.7).", classifySchema.shape, (args) => wrap(handleClassify(args, ctx)));
95
+ // Core — triage_logs (batch-capable)
96
+ server.tool("ollama_triage_logs", "Stable-shape log digest: {errors, warnings, suspected_root_cause}. Single: pass `log_text`. BATCH: pass `items:[{id,log_text}]` for triaging many log blobs at once (multiple CI runs, matrix legs, per-service logs) — returns one envelope with per-item entries. Use before grep-storms on long CI/test output.", triageLogsSchema.shape, (args) => wrap(handleTriageLogs(args, ctx)));
97
+ // Core — summarize_fast
98
+ server.tool("ollama_summarize_fast", "Gist of short input (best under ~4k tokens). Use as a decision gate: 'is this file worth reading in full?' Summary carries source_preview so you can spot-check fabrication.", summarizeFastSchema.shape, (args) => wrap(handleSummarizeFast(args, ctx)));
99
+ // Core — summarize_deep
100
+ server.tool("ollama_summarize_deep", "Digest of long input with optional focus. Pass EITHER `text` (when you already have the content) OR `source_paths[]` (server reads + chunks locally — use this to save Claude context). Exactly one of the two. Carries source_preview for fabrication spot-checks.", summarizeDeepSchema.shape, (args) => wrap(handleSummarizeDeep(args, ctx)));
101
+ // Core — draft
102
+ server.tool("ollama_draft", "DRAFT code or prose stubs (never autonomous — Claude reviews). Pass language for a server-side compile check: envelope returns {compiles, checker, stderr_tail}. target_path pointing into memory/, .claude/, docs/canon/, games/ requires confirm_write: true.", draftSchema.shape, (args) => wrap(handleDraft(args, ctx)));
103
+ // Core — extract (batch-capable)
104
+ server.tool("ollama_extract", "Schema-constrained JSON extraction using Ollama's JSON mode. Single: pass `text`, returns `{ok: true, data}` or `{ok: false, error: 'unparseable'}` — never partial. BATCH: pass `items:[{id,text}]` with a shared schema — returns one envelope with per-item `{id, ok, result|error}`. Use the batch shape for any 10+-similar-inputs workload (frontmatter, package.json, release metadata) so you hand over the whole job, not N calls.", extractSchema.shape, (args) => wrap(handleExtract(args, ctx)));
105
+ // Last resort — chat
106
+ server.tool("ollama_chat", "LAST RESORT catch-all. Prefer a specialty tool above when one fits. If you reach for this often, a specialty tool is missing and should be added.", chatSchema.shape, (args) => wrap(handleChat(args, ctx)));
107
+ return server;
108
+ }
109
+ async function main() {
110
+ const profile = loadProfile();
111
+ const ctx = {
112
+ client: new HttpOllamaClient(),
113
+ tiers: profile.tiers,
114
+ timeouts: profile.timeouts,
115
+ hardwareProfile: profile.name,
116
+ logger: new NdjsonLogger(),
117
+ };
118
+ // Profile-policy prewarm: pulls Instant tier into VRAM on dev profiles
119
+ // before connecting transport, so the first real Claude call doesn't eat
120
+ // cold-load latency. Failures are logged but never throw — server startup
121
+ // must not depend on Ollama being reachable.
122
+ if (profile.prewarm.length > 0) {
123
+ await runPrewarm(ctx, profile.prewarm);
124
+ }
125
+ const server = createServer(ctx);
126
+ const transport = new StdioServerTransport();
127
+ await server.connect(transport);
128
+ }
129
+ /**
130
+ * Run main() only when invoked as a script, not when imported by tests.
131
+ *
132
+ * Robust-on-Windows check: normalize both sides through realpathSync +
133
+ * fileURLToPath so forward/backslash and symlink differences don't cause
134
+ * the script to no-op silently (which is what Claude Code would see as
135
+ * "connected then immediately disconnected").
136
+ */
137
+ function isInvokedAsScript() {
138
+ try {
139
+ if (!process.argv[1])
140
+ return false;
141
+ return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1]);
142
+ }
143
+ catch {
144
+ return false;
145
+ }
146
+ }
147
+ if (isInvokedAsScript()) {
148
+ main().catch((err) => {
149
+ console.error(JSON.stringify(toErrorShape(err), null, 2));
150
+ process.exit(1);
151
+ });
152
+ }
153
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * NDJSON observability — one line per call to ~/.ollama-intern/log.ndjson.
3
+ *
4
+ * This is what lets Claude tune delegation instead of guessing:
5
+ * "that call used 5k tokens on Deep when fast would have sufficed."
6
+ *
7
+ * Also logs timeout events and fallback decisions so later we can prove
8
+ * the system degraded correctly under pressure, not just that a call was slow.
9
+ */
10
+ import type { Envelope, Residency } from "./envelope.js";
11
+ import type { Tier } from "./tiers.js";
12
+ export type LogEvent = {
13
+ kind: "call";
14
+ ts: string;
15
+ tool: string;
16
+ envelope: Envelope<unknown>;
17
+ } | {
18
+ kind: "timeout";
19
+ ts: string;
20
+ tool: string;
21
+ tier: Tier;
22
+ timeout_ms: number;
23
+ } | {
24
+ kind: "fallback";
25
+ ts: string;
26
+ tool: string;
27
+ from: Tier;
28
+ to: Tier;
29
+ reason: string;
30
+ } | {
31
+ kind: "guardrail";
32
+ ts: string;
33
+ tool: string;
34
+ rule: string;
35
+ action: string;
36
+ detail?: unknown;
37
+ } | {
38
+ kind: "prewarm";
39
+ ts: string;
40
+ tier: Tier;
41
+ model: string;
42
+ hardware_profile: string;
43
+ success: boolean;
44
+ elapsed_ms: number;
45
+ residency: Residency | null;
46
+ error?: string;
47
+ };
48
+ export interface Logger {
49
+ log(event: LogEvent): Promise<void>;
50
+ }
51
+ export declare class NdjsonLogger implements Logger {
52
+ private path;
53
+ private readyPromise;
54
+ constructor(path?: string);
55
+ private ready;
56
+ log(event: LogEvent): Promise<void>;
57
+ }
58
+ /** No-op logger for tests. */
59
+ export declare class NullLogger implements Logger {
60
+ events: LogEvent[];
61
+ log(event: LogEvent): Promise<void>;
62
+ }
63
+ export declare function timestamp(): string;
64
+ /** Build a LogEvent for a completed tool call from its envelope. */
65
+ export declare function callEvent(tool: string, envelope: Envelope<unknown>): LogEvent;
66
+ //# sourceMappingURL=observability.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observability.d.ts","sourceRoot":"","sources":["../src/observability.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAKvC,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;CAAE,GACvE;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC7E;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACpF;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/F;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEN,MAAM,WAAW,MAAM;IACrB,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,qBAAa,YAAa,YAAW,MAAM;IAG7B,OAAO,CAAC,IAAI;IAFxB,OAAO,CAAC,YAAY,CAA8B;gBAE9B,IAAI,GAAE,MAAyB;IAEnD,OAAO,CAAC,KAAK;IAOP,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;CAQ1C;AAED,8BAA8B;AAC9B,qBAAa,UAAW,YAAW,MAAM;IAChC,MAAM,EAAE,QAAQ,EAAE,CAAM;IACzB,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;CAG1C;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,oEAAoE;AACpE,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,QAAQ,CAE7E"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * NDJSON observability — one line per call to ~/.ollama-intern/log.ndjson.
3
+ *
4
+ * This is what lets Claude tune delegation instead of guessing:
5
+ * "that call used 5k tokens on Deep when fast would have sufficed."
6
+ *
7
+ * Also logs timeout events and fallback decisions so later we can prove
8
+ * the system degraded correctly under pressure, not just that a call was slow.
9
+ */
10
+ import { appendFile, mkdir } from "node:fs/promises";
11
+ import { homedir } from "node:os";
12
+ import { dirname, join } from "node:path";
13
+ const DEFAULT_LOG_DIR = join(homedir(), ".ollama-intern");
14
+ const DEFAULT_LOG_PATH = process.env.INTERN_LOG_PATH || join(DEFAULT_LOG_DIR, "log.ndjson");
15
+ export class NdjsonLogger {
16
+ path;
17
+ readyPromise = null;
18
+ constructor(path = DEFAULT_LOG_PATH) {
19
+ this.path = path;
20
+ }
21
+ ready() {
22
+ if (!this.readyPromise) {
23
+ this.readyPromise = mkdir(dirname(this.path), { recursive: true }).then(() => undefined);
24
+ }
25
+ return this.readyPromise;
26
+ }
27
+ async log(event) {
28
+ try {
29
+ await this.ready();
30
+ await appendFile(this.path, JSON.stringify(event) + "\n", "utf8");
31
+ }
32
+ catch {
33
+ // observability failures must never break tool calls
34
+ }
35
+ }
36
+ }
37
+ /** No-op logger for tests. */
38
+ export class NullLogger {
39
+ events = [];
40
+ async log(event) {
41
+ this.events.push(event);
42
+ }
43
+ }
44
+ export function timestamp() {
45
+ return new Date().toISOString();
46
+ }
47
+ /** Build a LogEvent for a completed tool call from its envelope. */
48
+ export function callEvent(tool, envelope) {
49
+ return { kind: "call", ts: timestamp(), tool, envelope };
50
+ }
51
+ //# sourceMappingURL=observability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observability.js","sourceRoot":"","sources":["../src/observability.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAI1C,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,gBAAgB,CAAC,CAAC;AAC1D,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC;AAuB5F,MAAM,OAAO,YAAY;IAGH;IAFZ,YAAY,GAAyB,IAAI,CAAC;IAElD,YAAoB,OAAe,gBAAgB;QAA/B,SAAI,GAAJ,IAAI,CAA2B;IAAG,CAAC;IAE/C,KAAK;QACX,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC3F,CAAC;QACD,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,KAAe;QACvB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;YACnB,MAAM,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;QACpE,CAAC;QAAC,MAAM,CAAC;YACP,qDAAqD;QACvD,CAAC;IACH,CAAC;CACF;AAED,8BAA8B;AAC9B,MAAM,OAAO,UAAU;IACd,MAAM,GAAe,EAAE,CAAC;IAC/B,KAAK,CAAC,GAAG,CAAC,KAAe;QACvB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;CACF;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,QAA2B;IACjE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Ollama HTTP client — generate, embed, residency probe (/api/ps).
3
+ *
4
+ * Every call is guarded by the global semaphore. Timeouts via AbortController.
5
+ * Residency probe runs on every call so the envelope can surface eviction
6
+ * without the caller asking. This is what prevents silent 5–10× slowdowns
7
+ * from hiding behind polished output.
8
+ */
9
+ import type { Residency } from "./envelope.js";
10
+ export interface GenerateRequest {
11
+ model: string;
12
+ prompt: string;
13
+ system?: string;
14
+ format?: "json";
15
+ stream?: boolean;
16
+ options?: {
17
+ temperature?: number;
18
+ num_predict?: number;
19
+ top_p?: number;
20
+ };
21
+ /** Keep model resident for this long. "-1" = forever. */
22
+ keep_alive?: string | number;
23
+ }
24
+ export interface GenerateResponse {
25
+ model: string;
26
+ response: string;
27
+ done: boolean;
28
+ prompt_eval_count?: number;
29
+ eval_count?: number;
30
+ total_duration?: number;
31
+ load_duration?: number;
32
+ eval_duration?: number;
33
+ }
34
+ export interface ChatMessage {
35
+ role: "system" | "user" | "assistant";
36
+ content: string;
37
+ }
38
+ export interface ChatRequest {
39
+ model: string;
40
+ messages: ChatMessage[];
41
+ format?: "json";
42
+ stream?: boolean;
43
+ options?: GenerateRequest["options"];
44
+ keep_alive?: string | number;
45
+ }
46
+ export interface ChatResponse {
47
+ model: string;
48
+ message: ChatMessage;
49
+ done: boolean;
50
+ prompt_eval_count?: number;
51
+ eval_count?: number;
52
+ }
53
+ export interface EmbedRequest {
54
+ model: string;
55
+ input: string | string[];
56
+ keep_alive?: string | number;
57
+ }
58
+ export interface EmbedResponse {
59
+ model: string;
60
+ embeddings: number[][];
61
+ }
62
+ export interface PsModel {
63
+ name: string;
64
+ model: string;
65
+ size: number;
66
+ size_vram: number;
67
+ digest: string;
68
+ expires_at: string;
69
+ }
70
+ export interface PsResponse {
71
+ models: PsModel[];
72
+ }
73
+ export interface OllamaClient {
74
+ generate(req: GenerateRequest, signal?: AbortSignal): Promise<GenerateResponse>;
75
+ chat(req: ChatRequest, signal?: AbortSignal): Promise<ChatResponse>;
76
+ embed(req: EmbedRequest, signal?: AbortSignal): Promise<EmbedResponse>;
77
+ residency(model: string): Promise<Residency | null>;
78
+ }
79
+ /**
80
+ * Normalize an OLLAMA_HOST value.
81
+ *
82
+ * Ollama's CLI accepts `127.0.0.1:11434` (no scheme) and routes Windows users
83
+ * often set it that way. fetch() requires a full URL, so we coerce anything
84
+ * that looks like host[:port] into a proper http:// URL, and strip trailing
85
+ * slashes so path concatenation stays clean.
86
+ */
87
+ export declare function normalizeOllamaHost(raw: string | undefined): string;
88
+ export declare class HttpOllamaClient implements OllamaClient {
89
+ private baseUrl;
90
+ constructor(baseUrl?: string);
91
+ generate(req: GenerateRequest, signal?: AbortSignal): Promise<GenerateResponse>;
92
+ chat(req: ChatRequest, signal?: AbortSignal): Promise<ChatResponse>;
93
+ embed(req: EmbedRequest, signal?: AbortSignal): Promise<EmbedResponse>;
94
+ residency(model: string): Promise<Residency | null>;
95
+ private post;
96
+ private get;
97
+ }
98
+ export declare function countTokens(resp: {
99
+ prompt_eval_count?: number;
100
+ eval_count?: number;
101
+ }): {
102
+ in: number;
103
+ out: number;
104
+ };
105
+ //# sourceMappingURL=ollama.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ollama.d.ts","sourceRoot":"","sources":["../src/ollama.ts"],"names":[],"mappings":"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"}
package/dist/ollama.js ADDED
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Ollama HTTP client — generate, embed, residency probe (/api/ps).
3
+ *
4
+ * Every call is guarded by the global semaphore. Timeouts via AbortController.
5
+ * Residency probe runs on every call so the envelope can surface eviction
6
+ * without the caller asking. This is what prevents silent 5–10× slowdowns
7
+ * from hiding behind polished output.
8
+ */
9
+ import { ollamaSemaphore } from "./semaphore.js";
10
+ import { InternError } from "./errors.js";
11
+ const API_TIMEOUT_MS = 10_000;
12
+ /**
13
+ * Normalize an OLLAMA_HOST value.
14
+ *
15
+ * Ollama's CLI accepts `127.0.0.1:11434` (no scheme) and routes Windows users
16
+ * often set it that way. fetch() requires a full URL, so we coerce anything
17
+ * that looks like host[:port] into a proper http:// URL, and strip trailing
18
+ * slashes so path concatenation stays clean.
19
+ */
20
+ export function normalizeOllamaHost(raw) {
21
+ const fallback = "http://127.0.0.1:11434";
22
+ const value = (raw ?? "").trim();
23
+ if (!value)
24
+ return fallback;
25
+ const withScheme = /^https?:\/\//i.test(value) ? value : `http://${value}`;
26
+ return withScheme.replace(/\/+$/, "");
27
+ }
28
+ export class HttpOllamaClient {
29
+ baseUrl;
30
+ constructor(baseUrl) {
31
+ this.baseUrl = normalizeOllamaHost(baseUrl ?? process.env.OLLAMA_HOST);
32
+ }
33
+ async generate(req, signal) {
34
+ return this.post("/api/generate", { ...req, stream: false }, signal);
35
+ }
36
+ async chat(req, signal) {
37
+ return this.post("/api/chat", { ...req, stream: false }, signal);
38
+ }
39
+ async embed(req, signal) {
40
+ return this.post("/api/embed", req, signal);
41
+ }
42
+ async residency(model) {
43
+ try {
44
+ const ps = await this.get("/api/ps");
45
+ const hit = ps.models.find((m) => m.name === model || m.model === model);
46
+ if (!hit)
47
+ return null;
48
+ const evicted = hit.size_vram < hit.size;
49
+ return {
50
+ in_vram: !evicted,
51
+ size_bytes: hit.size,
52
+ size_vram_bytes: hit.size_vram,
53
+ evicted,
54
+ expires_at: hit.expires_at ?? null,
55
+ };
56
+ }
57
+ catch {
58
+ return null;
59
+ }
60
+ }
61
+ async post(path, body, signal) {
62
+ const release = await ollamaSemaphore.acquire();
63
+ try {
64
+ const res = await fetch(`${this.baseUrl}${path}`, {
65
+ method: "POST",
66
+ headers: { "Content-Type": "application/json" },
67
+ body: JSON.stringify(body),
68
+ signal,
69
+ });
70
+ if (!res.ok) {
71
+ const text = await res.text().catch(() => "");
72
+ if (res.status === 404) {
73
+ throw new InternError("OLLAMA_MODEL_MISSING", `Model not found (404): ${text}`, "Run `ollama pull <model>` — see README for the full tier list.", false);
74
+ }
75
+ throw new InternError("OLLAMA_UNREACHABLE", `Ollama returned ${res.status}: ${text}`, "Check that Ollama is running (`ollama serve`) and reachable at OLLAMA_HOST.", true);
76
+ }
77
+ return (await res.json());
78
+ }
79
+ catch (err) {
80
+ if (err instanceof InternError)
81
+ throw err;
82
+ if (err.name === "AbortError") {
83
+ throw new InternError("OLLAMA_TIMEOUT", "Ollama request aborted", "Tier timeout fired — see fallback in envelope.", true);
84
+ }
85
+ throw new InternError("OLLAMA_UNREACHABLE", `Failed to reach Ollama: ${err.message}`, "Check that Ollama is running at OLLAMA_HOST (default http://127.0.0.1:11434).", true);
86
+ }
87
+ finally {
88
+ release();
89
+ }
90
+ }
91
+ async get(path) {
92
+ const controller = new AbortController();
93
+ const timeout = setTimeout(() => controller.abort(), API_TIMEOUT_MS);
94
+ try {
95
+ const res = await fetch(`${this.baseUrl}${path}`, { signal: controller.signal });
96
+ if (!res.ok) {
97
+ throw new InternError("OLLAMA_UNREACHABLE", `Ollama GET ${path} → ${res.status}`, "Check Ollama is running.", true);
98
+ }
99
+ return (await res.json());
100
+ }
101
+ finally {
102
+ clearTimeout(timeout);
103
+ }
104
+ }
105
+ }
106
+ export function countTokens(resp) {
107
+ return { in: resp.prompt_eval_count ?? 0, out: resp.eval_count ?? 0 };
108
+ }
109
+ //# sourceMappingURL=ollama.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ollama.js","sourceRoot":"","sources":["../src/ollama.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Targeted prewarm — pulls profile.prewarm tiers into VRAM at server startup
3
+ * so the first real Claude call doesn't eat cold-load latency.
4
+ *
5
+ * Behavior is profile policy, not magic:
6
+ * - dev-rtx5080: prewarm = ["instant"]
7
+ * - dev-rtx5080-llama: prewarm = ["instant"]
8
+ * - m5-max: prewarm = [] (cold-load on unified memory is ~free)
9
+ *
10
+ * Each prewarm attempt logs a {kind: "prewarm", ...} NDJSON event with
11
+ * model, success/failure, elapsed_ms, and residency. That keeps benchmarks
12
+ * able to distinguish cold from warm and keeps adoption data clean.
13
+ *
14
+ * Failures are logged but never thrown — server startup must not depend on
15
+ * Ollama being reachable. If the model is missing or Ollama is down, the
16
+ * server still comes up; the user sees the prewarm failure in the log.
17
+ */
18
+ import { type Tier } from "./tiers.js";
19
+ import type { RunContext } from "./runContext.js";
20
+ /**
21
+ * Run prewarm for the given tiers. Each tier issues a minimal generate
22
+ * (`prompt: "ok", num_predict: 1`) with `keep_alive: -1` so the model
23
+ * stays resident afterwards.
24
+ *
25
+ * Returns the count of successful prewarms; never throws.
26
+ */
27
+ export declare function runPrewarm(ctx: RunContext, tiers: Tier[]): Promise<number>;
28
+ //# sourceMappingURL=prewarm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prewarm.d.ts","sourceRoot":"","sources":["../src/prewarm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAe,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAMlD;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA0ChF"}