@smeltjs/core 0.2.0 → 0.3.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 (319) hide show
  1. package/README.md +3 -3
  2. package/dist/apply.d.ts.map +1 -1
  3. package/dist/apply.js.map +1 -1
  4. package/dist/cache/prefix.d.ts.map +1 -1
  5. package/dist/cache/prefix.js.map +1 -1
  6. package/dist/cli/args.d.ts +16 -70
  7. package/dist/cli/args.d.ts.map +1 -1
  8. package/dist/cli/args.js +18 -338
  9. package/dist/cli/args.js.map +1 -1
  10. package/dist/cli/bin.d.ts.map +1 -1
  11. package/dist/cli/bin.js.map +1 -1
  12. package/dist/cli/config.d.ts +40 -0
  13. package/dist/cli/config.d.ts.map +1 -1
  14. package/dist/cli/config.js +55 -1
  15. package/dist/cli/config.js.map +1 -1
  16. package/dist/cli/hooks.d.ts +33 -38
  17. package/dist/cli/hooks.d.ts.map +1 -1
  18. package/dist/cli/hooks.js +140 -485
  19. package/dist/cli/hooks.js.map +1 -1
  20. package/dist/cli/init.d.ts +30 -15
  21. package/dist/cli/init.d.ts.map +1 -1
  22. package/dist/cli/init.js +167 -38
  23. package/dist/cli/init.js.map +1 -1
  24. package/dist/cli/report.d.ts.map +1 -1
  25. package/dist/cli/report.js +2 -2
  26. package/dist/cli/report.js.map +1 -1
  27. package/dist/cli/run.d.ts +25 -88
  28. package/dist/cli/run.d.ts.map +1 -1
  29. package/dist/cli/run.js +32 -341
  30. package/dist/cli/run.js.map +1 -1
  31. package/dist/cli/shell.d.ts +106 -0
  32. package/dist/cli/shell.d.ts.map +1 -0
  33. package/dist/cli/shell.js +100 -0
  34. package/dist/cli/shell.js.map +1 -0
  35. package/dist/cli/subcommands/flags.d.ts +141 -0
  36. package/dist/cli/subcommands/flags.d.ts.map +1 -0
  37. package/dist/cli/subcommands/flags.js +208 -0
  38. package/dist/cli/subcommands/flags.js.map +1 -0
  39. package/dist/cli/subcommands/hooks.d.ts +18 -0
  40. package/dist/cli/subcommands/hooks.d.ts.map +1 -0
  41. package/dist/cli/subcommands/hooks.js +64 -0
  42. package/dist/cli/subcommands/hooks.js.map +1 -0
  43. package/dist/cli/subcommands/init.d.ts +19 -0
  44. package/dist/cli/subcommands/init.d.ts.map +1 -0
  45. package/dist/cli/subcommands/init.js +44 -0
  46. package/dist/cli/subcommands/init.js.map +1 -0
  47. package/dist/cli/subcommands/map.d.ts +75 -0
  48. package/dist/cli/subcommands/map.d.ts.map +1 -0
  49. package/dist/cli/subcommands/map.js +132 -0
  50. package/dist/cli/subcommands/map.js.map +1 -0
  51. package/dist/cli/subcommands/registry.d.ts +80 -0
  52. package/dist/cli/subcommands/registry.d.ts.map +1 -0
  53. package/dist/cli/subcommands/registry.js +138 -0
  54. package/dist/cli/subcommands/registry.js.map +1 -0
  55. package/dist/cli/subcommands/retrieve.d.ts +74 -0
  56. package/dist/cli/subcommands/retrieve.d.ts.map +1 -0
  57. package/dist/cli/subcommands/retrieve.js +84 -0
  58. package/dist/cli/subcommands/retrieve.js.map +1 -0
  59. package/dist/cli/subcommands/smelt.d.ts +107 -0
  60. package/dist/cli/subcommands/smelt.d.ts.map +1 -0
  61. package/dist/cli/subcommands/smelt.js +304 -0
  62. package/dist/cli/subcommands/smelt.js.map +1 -0
  63. package/dist/cli/subcommands/stats.d.ts +43 -0
  64. package/dist/cli/subcommands/stats.d.ts.map +1 -0
  65. package/dist/cli/subcommands/stats.js +62 -0
  66. package/dist/cli/subcommands/stats.js.map +1 -0
  67. package/dist/cli/subcommands/subcommand.d.ts +113 -0
  68. package/dist/cli/subcommands/subcommand.d.ts.map +1 -0
  69. package/dist/cli/subcommands/subcommand.js +3 -0
  70. package/dist/cli/subcommands/subcommand.js.map +1 -0
  71. package/dist/cli/usage.d.ts +3 -0
  72. package/dist/cli/usage.d.ts.map +1 -0
  73. package/dist/cli/usage.js +95 -0
  74. package/dist/cli/usage.js.map +1 -0
  75. package/dist/detect.d.ts.map +1 -1
  76. package/dist/detect.js.map +1 -1
  77. package/dist/errors.d.ts +21 -1
  78. package/dist/errors.d.ts.map +1 -1
  79. package/dist/errors.js +33 -1
  80. package/dist/errors.js.map +1 -1
  81. package/dist/harness/aider.d.ts +10 -0
  82. package/dist/harness/aider.d.ts.map +1 -0
  83. package/dist/harness/aider.js +21 -0
  84. package/dist/harness/aider.js.map +1 -0
  85. package/dist/harness/claude-code.d.ts +3 -0
  86. package/dist/harness/claude-code.d.ts.map +1 -0
  87. package/dist/harness/claude-code.js +68 -0
  88. package/dist/harness/claude-code.js.map +1 -0
  89. package/dist/harness/cline.d.ts +3 -0
  90. package/dist/harness/cline.d.ts.map +1 -0
  91. package/dist/harness/cline.js +54 -0
  92. package/dist/harness/cline.js.map +1 -0
  93. package/dist/harness/codex.d.ts +3 -0
  94. package/dist/harness/codex.d.ts.map +1 -0
  95. package/dist/harness/codex.js +91 -0
  96. package/dist/harness/codex.js.map +1 -0
  97. package/dist/harness/cursor.d.ts +3 -0
  98. package/dist/harness/cursor.d.ts.map +1 -0
  99. package/dist/harness/cursor.js +54 -0
  100. package/dist/harness/cursor.js.map +1 -0
  101. package/dist/harness/gemini.d.ts +3 -0
  102. package/dist/harness/gemini.d.ts.map +1 -0
  103. package/dist/harness/gemini.js +59 -0
  104. package/dist/harness/gemini.js.map +1 -0
  105. package/dist/harness/grok.d.ts +3 -0
  106. package/dist/harness/grok.d.ts.map +1 -0
  107. package/dist/harness/grok.js +46 -0
  108. package/dist/harness/grok.js.map +1 -0
  109. package/dist/harness/hermes.d.ts +3 -0
  110. package/dist/harness/hermes.d.ts.map +1 -0
  111. package/dist/harness/hermes.js +71 -0
  112. package/dist/harness/hermes.js.map +1 -0
  113. package/dist/harness/kilocode.d.ts +3 -0
  114. package/dist/harness/kilocode.d.ts.map +1 -0
  115. package/dist/harness/kilocode.js +35 -0
  116. package/dist/harness/kilocode.js.map +1 -0
  117. package/dist/harness/opencode.d.ts +3 -0
  118. package/dist/harness/opencode.d.ts.map +1 -0
  119. package/dist/harness/opencode.js +97 -0
  120. package/dist/harness/opencode.js.map +1 -0
  121. package/dist/harness/paths.d.ts +17 -0
  122. package/dist/harness/paths.d.ts.map +1 -0
  123. package/dist/harness/paths.js +45 -0
  124. package/dist/harness/paths.js.map +1 -0
  125. package/dist/harness/profile.d.ts +184 -0
  126. package/dist/harness/profile.d.ts.map +1 -0
  127. package/dist/harness/profile.js +26 -0
  128. package/dist/harness/profile.js.map +1 -0
  129. package/dist/harness/registry.d.ts +51 -0
  130. package/dist/harness/registry.d.ts.map +1 -0
  131. package/dist/harness/registry.js +87 -0
  132. package/dist/harness/registry.js.map +1 -0
  133. package/dist/harness/snippet.d.ts +19 -0
  134. package/dist/harness/snippet.d.ts.map +1 -0
  135. package/dist/harness/snippet.js +41 -0
  136. package/dist/harness/snippet.js.map +1 -0
  137. package/dist/hash.d.ts.map +1 -1
  138. package/dist/hash.js.map +1 -1
  139. package/dist/hooks/guard-core.d.ts +14 -14
  140. package/dist/hooks/guard-core.d.ts.map +1 -1
  141. package/dist/hooks/guard-core.js +15 -37
  142. package/dist/hooks/guard-core.js.map +1 -1
  143. package/dist/hooks/shim.d.ts +95 -4
  144. package/dist/hooks/shim.d.ts.map +1 -1
  145. package/dist/hooks/shim.js +87 -2
  146. package/dist/hooks/shim.js.map +1 -1
  147. package/dist/hooks/shims/claude-code.d.ts +5 -18
  148. package/dist/hooks/shims/claude-code.d.ts.map +1 -1
  149. package/dist/hooks/shims/claude-code.js +9 -56
  150. package/dist/hooks/shims/claude-code.js.map +1 -1
  151. package/dist/hooks/shims/cline.d.ts +5 -12
  152. package/dist/hooks/shims/cline.d.ts.map +1 -1
  153. package/dist/hooks/shims/cline.js +9 -34
  154. package/dist/hooks/shims/cline.js.map +1 -1
  155. package/dist/hooks/shims/codex.d.ts +5 -18
  156. package/dist/hooks/shims/codex.d.ts.map +1 -1
  157. package/dist/hooks/shims/codex.js +9 -51
  158. package/dist/hooks/shims/codex.js.map +1 -1
  159. package/dist/hooks/shims/cursor.d.ts +5 -14
  160. package/dist/hooks/shims/cursor.d.ts.map +1 -1
  161. package/dist/hooks/shims/cursor.js +9 -42
  162. package/dist/hooks/shims/cursor.js.map +1 -1
  163. package/dist/hooks/shims/gemini.d.ts +5 -18
  164. package/dist/hooks/shims/gemini.d.ts.map +1 -1
  165. package/dist/hooks/shims/gemini.js +9 -48
  166. package/dist/hooks/shims/gemini.js.map +1 -1
  167. package/dist/hooks/shims/grok.d.ts +5 -13
  168. package/dist/hooks/shims/grok.d.ts.map +1 -1
  169. package/dist/hooks/shims/grok.js +9 -32
  170. package/dist/hooks/shims/grok.js.map +1 -1
  171. package/dist/hooks/shims/hermes.d.ts +5 -17
  172. package/dist/hooks/shims/hermes.d.ts.map +1 -1
  173. package/dist/hooks/shims/hermes.js +9 -45
  174. package/dist/hooks/shims/hermes.js.map +1 -1
  175. package/dist/index.d.ts +33 -76
  176. package/dist/index.d.ts.map +1 -1
  177. package/dist/index.js +13 -67
  178. package/dist/index.js.map +1 -1
  179. package/dist/lang/bash.d.ts.map +1 -1
  180. package/dist/lang/bash.js.map +1 -1
  181. package/dist/lang/c.d.ts.map +1 -1
  182. package/dist/lang/c.js.map +1 -1
  183. package/dist/lang/c_sharp.d.ts.map +1 -1
  184. package/dist/lang/c_sharp.js.map +1 -1
  185. package/dist/lang/cpp.d.ts.map +1 -1
  186. package/dist/lang/cpp.js.map +1 -1
  187. package/dist/lang/go.d.ts.map +1 -1
  188. package/dist/lang/go.js.map +1 -1
  189. package/dist/lang/java.d.ts.map +1 -1
  190. package/dist/lang/java.js.map +1 -1
  191. package/dist/lang/javascript.d.ts.map +1 -1
  192. package/dist/lang/javascript.js.map +1 -1
  193. package/dist/lang/kotlin.d.ts.map +1 -1
  194. package/dist/lang/kotlin.js.map +1 -1
  195. package/dist/lang/php.d.ts.map +1 -1
  196. package/dist/lang/php.js.map +1 -1
  197. package/dist/lang/profile.d.ts.map +1 -1
  198. package/dist/lang/profile.js.map +1 -1
  199. package/dist/lang/python.d.ts.map +1 -1
  200. package/dist/lang/python.js.map +1 -1
  201. package/dist/lang/registry.d.ts.map +1 -1
  202. package/dist/lang/registry.js.map +1 -1
  203. package/dist/lang/ruby.d.ts.map +1 -1
  204. package/dist/lang/ruby.js.map +1 -1
  205. package/dist/lang/rust.d.ts.map +1 -1
  206. package/dist/lang/rust.js.map +1 -1
  207. package/dist/lang/swift.d.ts.map +1 -1
  208. package/dist/lang/swift.js.map +1 -1
  209. package/dist/lang/tsx.d.ts.map +1 -1
  210. package/dist/lang/tsx.js.map +1 -1
  211. package/dist/lang/typescript.d.ts.map +1 -1
  212. package/dist/lang/typescript.js.map +1 -1
  213. package/dist/net/policy.d.ts +18 -1
  214. package/dist/net/policy.d.ts.map +1 -1
  215. package/dist/net/policy.js +6 -4
  216. package/dist/net/policy.js.map +1 -1
  217. package/dist/ops/index.d.ts +39 -0
  218. package/dist/ops/index.d.ts.map +1 -0
  219. package/dist/ops/index.js +37 -0
  220. package/dist/ops/index.js.map +1 -0
  221. package/dist/ops/inputs.d.ts +170 -0
  222. package/dist/ops/inputs.d.ts.map +1 -0
  223. package/dist/ops/inputs.js +142 -0
  224. package/dist/ops/inputs.js.map +1 -0
  225. package/dist/ops/verbs.d.ts +136 -0
  226. package/dist/ops/verbs.d.ts.map +1 -0
  227. package/dist/ops/verbs.js +76 -0
  228. package/dist/ops/verbs.js.map +1 -0
  229. package/dist/plan/auto.d.ts +69 -0
  230. package/dist/plan/auto.d.ts.map +1 -0
  231. package/dist/plan/auto.js +70 -0
  232. package/dist/plan/auto.js.map +1 -0
  233. package/dist/plan/budget.d.ts +21 -0
  234. package/dist/plan/budget.d.ts.map +1 -0
  235. package/dist/plan/budget.js +26 -0
  236. package/dist/plan/budget.js.map +1 -0
  237. package/dist/plan/grammar.d.ts +14 -0
  238. package/dist/plan/grammar.d.ts.map +1 -1
  239. package/dist/plan/grammar.js +47 -6
  240. package/dist/plan/grammar.js.map +1 -1
  241. package/dist/plan/lexical.d.ts.map +1 -1
  242. package/dist/plan/lexical.js +1 -7
  243. package/dist/plan/lexical.js.map +1 -1
  244. package/dist/plan/planners.d.ts +35 -2
  245. package/dist/plan/planners.d.ts.map +1 -1
  246. package/dist/plan/planners.js +35 -2
  247. package/dist/plan/planners.js.map +1 -1
  248. package/dist/plan/structural.d.ts +19 -5
  249. package/dist/plan/structural.d.ts.map +1 -1
  250. package/dist/plan/structural.js +180 -33
  251. package/dist/plan/structural.js.map +1 -1
  252. package/dist/repomap/cache.d.ts +48 -0
  253. package/dist/repomap/cache.d.ts.map +1 -1
  254. package/dist/repomap/cache.js +112 -13
  255. package/dist/repomap/cache.js.map +1 -1
  256. package/dist/repomap/io.d.ts +24 -0
  257. package/dist/repomap/io.d.ts.map +1 -0
  258. package/dist/repomap/io.js +34 -0
  259. package/dist/repomap/io.js.map +1 -0
  260. package/dist/repomap/map.d.ts +69 -6
  261. package/dist/repomap/map.d.ts.map +1 -1
  262. package/dist/repomap/map.js +104 -22
  263. package/dist/repomap/map.js.map +1 -1
  264. package/dist/repomap/rank.d.ts +26 -2
  265. package/dist/repomap/rank.d.ts.map +1 -1
  266. package/dist/repomap/rank.js +20 -0
  267. package/dist/repomap/rank.js.map +1 -1
  268. package/dist/repomap/reader.d.ts +73 -0
  269. package/dist/repomap/reader.d.ts.map +1 -0
  270. package/dist/repomap/reader.js +30 -0
  271. package/dist/repomap/reader.js.map +1 -0
  272. package/dist/repomap/tags.d.ts.map +1 -1
  273. package/dist/repomap/tags.js.map +1 -1
  274. package/dist/retrieve.d.ts +8 -0
  275. package/dist/retrieve.d.ts.map +1 -1
  276. package/dist/retrieve.js +12 -1
  277. package/dist/retrieve.js.map +1 -1
  278. package/dist/smelter.d.ts +79 -0
  279. package/dist/smelter.d.ts.map +1 -0
  280. package/dist/smelter.js +65 -0
  281. package/dist/smelter.js.map +1 -0
  282. package/dist/stages.d.ts.map +1 -1
  283. package/dist/stages.js +2 -2
  284. package/dist/stages.js.map +1 -1
  285. package/dist/stats.d.ts +2 -1
  286. package/dist/stats.d.ts.map +1 -1
  287. package/dist/stats.js +2 -1
  288. package/dist/stats.js.map +1 -1
  289. package/dist/store-dir.d.ts +43 -4
  290. package/dist/store-dir.d.ts.map +1 -1
  291. package/dist/store-dir.js +48 -5
  292. package/dist/store-dir.js.map +1 -1
  293. package/dist/store.d.ts +6 -0
  294. package/dist/store.d.ts.map +1 -1
  295. package/dist/store.js +6 -0
  296. package/dist/store.js.map +1 -1
  297. package/dist/types.d.ts +35 -3
  298. package/dist/types.d.ts.map +1 -1
  299. package/dist/types.js.map +1 -1
  300. package/grammars/tree-sitter-bash.wasm +0 -0
  301. package/grammars/tree-sitter-c.wasm +0 -0
  302. package/grammars/tree-sitter-c_sharp.wasm +0 -0
  303. package/grammars/tree-sitter-cpp.wasm +0 -0
  304. package/grammars/tree-sitter-go.wasm +0 -0
  305. package/grammars/tree-sitter-java.wasm +0 -0
  306. package/grammars/tree-sitter-javascript.wasm +0 -0
  307. package/grammars/tree-sitter-kotlin.wasm +0 -0
  308. package/grammars/tree-sitter-php.wasm +0 -0
  309. package/grammars/tree-sitter-python.wasm +0 -0
  310. package/grammars/tree-sitter-ruby.wasm +0 -0
  311. package/grammars/tree-sitter-rust.wasm +0 -0
  312. package/grammars/tree-sitter-swift.wasm +0 -0
  313. package/grammars/tree-sitter-tsx.wasm +0 -0
  314. package/grammars/tree-sitter-typescript.wasm +0 -0
  315. package/package.json +11 -11
  316. package/dist/cli/resolve.d.ts +0 -103
  317. package/dist/cli/resolve.d.ts.map +0 -1
  318. package/dist/cli/resolve.js +0 -106
  319. package/dist/cli/resolve.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"store-dir.js","sourceRoot":"","sources":["../src/store-dir.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store-dir.js","sourceRoot":"","sources":["../src/store-dir.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n closeSync,\n fsyncSync,\n linkSync,\n mkdirSync,\n openSync,\n readdirSync,\n readFileSync,\n statSync,\n unlinkSync,\n writeSync,\n} from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport { randomBytes } from 'node:crypto';\nimport process from 'node:process';\n\nimport {\n HashCollisionError,\n SmeltError,\n StoreCorruptionError,\n StoreFormatError,\n UnknownHashError,\n} from './errors.ts';\nimport { contentHash } from './hash.ts';\nimport { retrieveStats } from './stats.ts';\nimport type { RawRetrieveCounters } from './stats.ts';\nimport type { ElisionStore, RetrieveStats } from './types.ts';\n\n/**\n * The format marker every store directory carries, and the one version this code\n * understands. A future layout is a new version, refused loudly by old code — never a\n * quiet reinterpretation of someone's stored bytes.\n */\nexport const DIRECTORY_STORE_FORMAT = 'smelt-elision-store';\nexport const DIRECTORY_STORE_VERSION = 1;\n\n/**\n * What a storage key may look like. `contentHash` produces 16 lowercase hex characters;\n * the pattern is wider so an injected test hash still works, and strict enough that a\n * key can never traverse out of `blobs/` or collide with `format.json`.\n */\nconst KEY_PATTERN = /^[0-9a-f]{4,128}$/;\n\n/** One journal line: a kind, a space, and the hash as a JSON string literal. */\nconst LOG_LINE = /^(hit|miss|corrupt) (\"(?:[^\"\\\\]|\\\\.)*\")$/;\n\n/** See {@link MemoryElisionStoreOptions} in `store.ts` — same escape hatch, same reason. */\nexport interface DirectoryElisionStoreOptions {\n /**\n * Override the hash function, so the collision branch — unreachable with sha256 —\n * can be tested. Production has no reason to pass this.\n */\n readonly hash?: (content: string) => string;\n}\n\n/**\n * A persistent {@link ElisionStore} over a content-addressed directory. `node:fs` only —\n * no SQLite, no new dependency, nothing that phones home. Elisions put here outlive the\n * process, so a long-lived agent session can `retrieve()` across restarts.\n *\n * ## Storage layout\n *\n * ```text\n * <root>/\n * format.json { \"format\": \"smelt-elision-store\", \"version\": 1 } — refused if unknown\n * blobs/<hash> one file per elision: the exact UTF-8 bytes, named by their content hash\n * tmp/ staging for atomic writes; never read, safe to sweep\n * retrievals.log append-only journal: `hit \"<hash>\"` | `miss \"<hash>\"` | `corrupt \"<hash>\"`\n * ```\n *\n * **Nothing lives in memory.** Every read — `stats()` included — comes off the disk, so\n * two instances over the same directory (two processes, or one process before and after\n * a restart) always agree. `stats()` is a scan; elision counts per session are small and\n * retrieval is the model asking for material back, which is rare by design.\n *\n * ## Durability\n *\n * - **Writes are crash-safe.** A blob is written to `tmp/`, `fsync`ed, then `link(2)`ed\n * into `blobs/` — an atomic, no-clobber publish. A torn write dies in `tmp/`, where\n * nothing looks; a name in `blobs/` always refers to a fully written file.\n * - **`fsync` is only as strong as the platform makes it.** Every flush here is Node's\n * `fsyncSync`, which is libuv's `uv_fs_fsync`, and what that reaches the hardware with\n * differs by platform. On Apple it is strong: libuv knows macOS's own `fsync(2)` only\n * hands the write to the drive, so its `__APPLE__` branch issues\n * `fcntl(fd, F_FULLFSYNC)` — a real drive-cache flush — before falling back to\n * `F_BARRIERFSYNC` and then plain `fsync(2)`. Measured here (Node 26, libuv 1.52,\n * internal APFS SSD): an 11-byte append costs 0.01 ms unflushed, 5.5 ms through\n * `fsyncSync`, and 0.10 ms through a raw `fsync(2)` against 4.8 ms through a raw\n * `F_FULLFSYNC` — the cost says which syscall is being made. Everywhere else libuv\n * calls plain `fsync(2)`, which is as durable as the drive's honesty about its own\n * write cache. So a **power loss** can lose a blob or a journal line this code has\n * already `fsync`ed and reported as written on any non-Apple platform whose drive\n * lies, and on Apple only where `F_FULLFSYNC` itself fails and libuv degrades\n * silently — some non-APFS and network mounts. A **process** crash cannot lose one\n * anywhere: the bytes are in the page cache and the publish is still atomic. Nothing\n * is ever handed back unverified either way, so the worst a lost blob can produce is\n * {@link UnknownHashError} — never wrong bytes presented as right ones. \"Crash-safe\"\n * is the claim, deliberately not \"power-loss-proof\": the durability is real, it is\n * just not unconditional.\n * - **Reads verify.** `retrieve()`, `peek()` and `has()` re-hash the bytes and refuse a\n * mismatch with {@link StoreCorruptionError} — a damaged blob is never handed back as\n * a retrieval nor reported as present, and \"we hold damaged bytes\" is distinct from\n * {@link UnknownHashError}'s \"never existed\". The guard in\n * `test/guards/persistent-store.test.ts` watches this.\n * - **Counters survive a restart.** Every `retrieve()` appends one `fsync`ed line to\n * `retrievals.log`, and `stats()` is a fold over it — so `expansionRate` stays\n * meaningful across a whole session, not just one process. A crash in the middle of\n * an append can tear at most that one line; a torn tail is skipped, costing at most\n * the single count that was being written when the process died.\n * - **Concurrent writers are safe.** `link(2)` refuses to clobber, so two processes\n * putting at once race to publish and the loser verifies byte-for-byte agreement with\n * the winner — identical content dedupes, different content under one hash is a\n * {@link HashCollisionError}. Journal appends use `O_APPEND`. Tested with two real\n * processes in `test/store-dir.test.ts`.\n *\n * ## No eviction\n *\n * Same rule as {@link MemoryElisionStore}: no cap, no LRU, no `clear()`. A store that\n * can forget turns Law 3 into \"reversible, usually\". Elided text is smaller than the\n * session that produced it; if disk pressure ever forces a cap, retrieval of an evicted\n * hash must throw a distinct \"evicted\" error — never {@link UnknownHashError} — so the\n * model can tell \"we lost it\" from \"never existed\". Today there is no such error because\n * there is no such cap.\n *\n * ## Two deliberate choices around the edges\n *\n * - **The root is resolved to an absolute path at construction.** Every later path is\n * joined from that, so a `process.chdir()` after construction cannot silently\n * re-target the store — the bytes a relative-rooted store put before a chdir would\n * otherwise be unreachable after it, which reads exactly like data loss.\n * - **A failed journal append never withholds intact bytes.** `retrieve()`'s order of\n * business is: read, verify, count, return. When the *count* cannot be written (a\n * read-only journal, a full disk), the bytes are still returned — they are verified\n * and the caller asked for them; refusing would turn a bookkeeping failure into\n * Law 3 breaking. The failure is surfaced distinctly instead: a\n * `process.emitWarning` with name `SmeltCounterWriteFailure`, so \"your retrieval\n * worked\" and \"your counters just went quiet\" stay two separate facts. The same\n * applies to the `miss`/`corrupt` journal lines: the store's own error for the\n * lookup still wins over the journal's I/O error.\n */\nexport class DirectoryElisionStore implements ElisionStore {\n readonly #blobsDir: string;\n readonly #tmpDir: string;\n readonly #logPath: string;\n readonly #hash: (content: string) => string;\n\n constructor(root: string, options: DirectoryElisionStoreOptions = {}) {\n this.#hash = options.hash ?? contentHash;\n // Resolve NOW, against the working directory the caller constructed with — a\n // later chdir must never re-point an already-constructed store. See the class doc.\n const absoluteRoot = resolve(root);\n this.#blobsDir = join(absoluteRoot, 'blobs');\n this.#tmpDir = join(absoluteRoot, 'tmp');\n this.#logPath = join(absoluteRoot, 'retrievals.log');\n const markerPath = join(absoluteRoot, 'format.json');\n // Validate before mutating: a directory carrying a marker this code does not\n // understand is refused with the directory exactly as it was found — no blobs/,\n // no tmp/, no staged temp file created inside someone else's layout.\n const existing = this.#readMarker(markerPath);\n if (existing !== undefined) this.#verifyMarker(markerPath, existing);\n mkdirSync(this.#blobsDir, { recursive: true });\n mkdirSync(this.#tmpDir, { recursive: true });\n this.#claimFormat(markerPath);\n }\n\n put(content: string): string {\n const hash = this.#hash(content);\n if (!KEY_PATTERN.test(hash)) {\n throw new SmeltError(\n `smelt: hash \"${hash}\" is not usable as a storage key — it must match ` +\n `${String(KEY_PATTERN)} so it can name a file inside blobs/ and nothing else.`,\n );\n }\n const existing = this.#readBlob(hash);\n if (existing !== undefined) {\n // Verify the stored bytes before comparing: a damaged blob is corruption, not a\n // collision. Only intact bytes that still differ earn HashCollisionError.\n if (this.#hash(existing) !== hash) throw new StoreCorruptionError(hash);\n if (existing !== content) throw new HashCollisionError(hash);\n return hash;\n }\n const tmpPath = this.#writeTemp(content);\n try {\n // link(2) is the atomic, no-clobber publish: it fails with EEXIST rather than\n // overwrite, so a concurrent writer can never silently replace someone's bytes.\n linkSync(tmpPath, join(this.#blobsDir, hash));\n } catch (error) {\n if ((error as { code?: string }).code !== 'EEXIST') throw error;\n // Another writer published this hash between our existence check and our link.\n // Same bytes: idempotent put, done. Damaged or vanished bytes: corruption — the\n // store was torn or edited outside smelt. Intact different bytes: a collision.\n const winner = this.#readBlob(hash);\n if (winner === undefined || this.#hash(winner) !== hash) {\n throw new StoreCorruptionError(hash);\n }\n if (winner !== content) throw new HashCollisionError(hash);\n } finally {\n unlinkSync(tmpPath);\n }\n fsyncDirBestEffort(this.#blobsDir);\n return hash;\n }\n\n peek(hash: string): string | undefined {\n const content = this.#readBlob(hash);\n if (content === undefined) return undefined;\n if (this.#hash(content) !== hash) throw new StoreCorruptionError(hash);\n return content;\n }\n\n retrieve(hash: string): string {\n const content = this.#readBlob(hash);\n if (content === undefined) {\n this.#appendLogCounting('miss', hash);\n throw new UnknownHashError(hash);\n }\n if (this.#hash(content) !== hash) {\n this.#appendLogCounting('corrupt', hash);\n throw new StoreCorruptionError(hash);\n }\n this.#appendLogCounting('hit', hash);\n return content;\n }\n\n /**\n * Whether this hash can be **retrieved** — verified, exactly as {@link peek} and\n * {@link retrieve} verify, because it is `peek()`.\n *\n * `has()` used to be the one read that skipped verification: a damaged blob answered\n * `true` and then threw {@link StoreCorruptionError} on the very next line, so a\n * consumer that checked before retrieving was told a lie by the cheaper call. The two\n * answers now come from one place and cannot drift: `true` means the bytes are there\n * and hash to their name, `false` means this store never held them, and damage is\n * raised rather than hidden behind a boolean — the same distinction `peek()` draws\n * between \"we hold damaged bytes\" and \"never existed\".\n *\n * It stays uncounted: a check is not the model asking for material back, and counting\n * one would inflate `retrieveCalls` and with it the expansion rate, which is the one\n * number this library exists to keep honest. So no journal line is written here, not\n * even for the corrupt case — `retrieve()` journals that when the model asks.\n *\n * @throws {StoreCorruptionError} when the stored bytes do not hash to their name.\n */\n has(hash: string): boolean {\n return this.peek(hash) !== undefined;\n }\n\n /**\n * The five directly-observed counts, every one read off the disk — a scan of\n * `blobs/` plus a fold over `retrievals.log`. See {@link RawRetrieveCounters}; the\n * derived half of the stats comes from the shared `retrieveStats()`, never here.\n */\n rawCounters(): RawRetrieveCounters {\n let elisionsStored = 0;\n let bytesStored = 0;\n for (const entry of readdirSync(this.#blobsDir)) {\n if (!KEY_PATTERN.test(entry)) continue; // `.DS_Store` and friends are not blobs\n elisionsStored += 1;\n bytesStored += statSync(join(this.#blobsDir, entry)).size;\n }\n\n let retrieveCalls = 0;\n let misses = 0;\n const hits = new Set<string>();\n for (const line of this.#readLog().split('\\n')) {\n const match = LOG_LINE.exec(line);\n if (match === null) continue; // a torn tail from a crash mid-append, or blank\n retrieveCalls += 1;\n if (match[1] === 'miss') misses += 1;\n else if (match[1] === 'hit') hits.add(JSON.parse(match[2]!) as string);\n }\n\n return { elisionsStored, bytesStored, retrieveCalls, uniqueRetrieved: hits.size, misses };\n }\n\n stats(): RetrieveStats {\n return retrieveStats(this.rawCounters());\n }\n\n /** The blob's exact content, or `undefined` when no such blob is stored. */\n #readBlob(hash: string): string | undefined {\n if (!KEY_PATTERN.test(hash)) return undefined; // never a path component\n try {\n return readFileSync(join(this.#blobsDir, hash), 'utf8');\n } catch (error) {\n if ((error as { code?: string }).code === 'ENOENT') return undefined;\n throw error;\n }\n }\n\n /** Write content to a unique file in `tmp/`, fsynced, and return its path. */\n #writeTemp(content: string): string {\n const tmpPath = join(this.#tmpDir, `${String(process.pid)}-${randomBytes(8).toString('hex')}`);\n const fd = openSync(tmpPath, 'wx');\n try {\n // writeSync may write fewer bytes than asked; loop, or a short write would be\n // fsynced and published under the full content's hash as a torn blob.\n const bytes = Buffer.from(content, 'utf8');\n let written = 0;\n while (written < bytes.length) {\n written += writeSync(fd, bytes, written);\n }\n fsyncSync(fd);\n } finally {\n closeSync(fd);\n }\n return tmpPath;\n }\n\n /**\n * A journal append on the `retrieve()` path — counting, not custody. A failure here\n * must never decide whether the caller gets its verified bytes (or its true error),\n * so it is caught and surfaced as a distinct `process.emitWarning` — see the class\n * doc, and the read-only-journal case in `test/store-dir.test.ts`.\n */\n #appendLogCounting(kind: 'hit' | 'miss' | 'corrupt', hash: string): void {\n try {\n this.#appendLog(kind, hash);\n } catch (error) {\n process.emitWarning(\n `smelt: could not journal a \"${kind}\" for hash \"${hash}\" in ${this.#logPath} ` +\n `(${error instanceof Error ? error.message : String(error)}). The retrieval ` +\n `itself is unaffected, but this count is lost — retrieveCalls and ` +\n `expansionRate now UNDER-report until the journal is writable again.`,\n 'SmeltCounterWriteFailure',\n );\n }\n }\n\n /**\n * One durable journal line. The hash is JSON-encoded because `retrieve()` takes it\n * from the model verbatim — a hash containing a newline must not forge a second line.\n * The record starts with its own newline so a torn tail from an earlier crash — a\n * partial record with no trailing newline — can never bleed into this one: the tear\n * stays on its own line and is skipped by `stats()`, as blank lines are.\n */\n #appendLog(kind: 'hit' | 'miss' | 'corrupt', hash: string): void {\n const fd = openSync(this.#logPath, 'a');\n try {\n const record = Buffer.from(`\\n${kind} ${JSON.stringify(hash)}\\n`, 'utf8');\n let written = 0;\n while (written < record.length) {\n written += writeSync(fd, record, written);\n }\n fsyncSync(fd);\n } finally {\n closeSync(fd);\n }\n }\n\n #readLog(): string {\n try {\n return readFileSync(this.#logPath, 'utf8');\n } catch (error) {\n if ((error as { code?: string }).code === 'ENOENT') return '';\n throw error;\n }\n }\n\n /**\n * Write the format marker if this directory has none, or verify the one it has.\n * Creation is atomic (write to `tmp/`, then `link`), so a concurrent creator never\n * observes a half-written marker. The constructor pre-verified any pre-existing\n * marker; the verify here catches only a concurrent creator's claim.\n */\n #claimFormat(markerPath: string): void {\n const claim = (): string | undefined => {\n const body = `${JSON.stringify({\n format: DIRECTORY_STORE_FORMAT,\n version: DIRECTORY_STORE_VERSION,\n })}\\n`;\n const tmpPath = this.#writeTemp(body);\n try {\n linkSync(tmpPath, markerPath);\n return undefined; // claimed by us; nothing to verify\n } catch (error) {\n if ((error as { code?: string }).code !== 'EEXIST') throw error;\n return readFileSync(markerPath, 'utf8');\n } finally {\n unlinkSync(tmpPath);\n }\n };\n\n const existing = claim();\n if (existing === undefined) return;\n this.#verifyMarker(markerPath, existing);\n }\n\n /** The marker's body, or `undefined` when the directory carries none. */\n #readMarker(markerPath: string): string | undefined {\n try {\n return readFileSync(markerPath, 'utf8');\n } catch (error) {\n if ((error as { code?: string }).code === 'ENOENT') return undefined;\n throw error;\n }\n }\n\n /** Refuse a marker this version of smelt does not understand. */\n #verifyMarker(markerPath: string, existing: string): void {\n let parsed: { format?: unknown; version?: unknown };\n try {\n parsed = JSON.parse(existing) as { format?: unknown; version?: unknown };\n } catch {\n throw new StoreFormatError(\n `smelt: \"${markerPath}\" is not parseable JSON, so this directory cannot be ` +\n `trusted as an elision store. Refusing to read or write it.`,\n );\n }\n if (parsed.format !== DIRECTORY_STORE_FORMAT || parsed.version !== DIRECTORY_STORE_VERSION) {\n throw new StoreFormatError(\n `smelt: \"${markerPath}\" declares format ${JSON.stringify(parsed.format)} ` +\n `version ${JSON.stringify(parsed.version)}; this code understands ` +\n `\"${DIRECTORY_STORE_FORMAT}\" version ${String(DIRECTORY_STORE_VERSION)}. ` +\n `Refusing to reinterpret someone else's layout.`,\n );\n }\n }\n}\n\n/**\n * Flush the directory entry after a publish, so the *name* survives a crash as well as\n * the bytes. Where the platform refuses to fsync a directory (Windows does), the publish\n * is still atomic — only the durability of the directory entry falls back to the OS's\n * own schedule. Only that refusal is swallowed: a real I/O failure (`EIO`) propagates,\n * because \"the disk could not flush\" must never be reported as a successful put.\n *\n * Where it does work it is the same `fsyncSync` the blob's own flush uses, and so is\n * exactly as strong as that — see the durability note on {@link DirectoryElisionStore}\n * for what that means per platform.\n */\nfunction fsyncDirBestEffort(path: string): void {\n let fd: number;\n try {\n fd = openSync(path, 'r');\n } catch {\n return; // the platform refuses to even open a directory for reading (Windows)\n }\n try {\n fsyncSync(fd);\n } catch (error) {\n const code = (error as { code?: string }).code;\n // EINVAL/ENOTSUP/EPERM/EBADF: the platform refuses to fsync a directory — see the\n // doc comment. Anything else (EIO above all) is a genuine write failure.\n if (code !== 'EINVAL' && code !== 'ENOTSUP' && code !== 'EPERM' && code !== 'EBADF') {\n throw error;\n }\n } finally {\n closeSync(fd);\n }\n}\n"]}
package/dist/store.d.ts CHANGED
@@ -28,6 +28,12 @@ export declare class MemoryElisionStore implements ElisionStore {
28
28
  put(content: string): string;
29
29
  peek(hash: string): string | undefined;
30
30
  retrieve(hash: string): string;
31
+ /**
32
+ * True exactly when `retrieve(hash)` would return bytes. Nothing to verify: `put`
33
+ * hashed the content itself and no one else can reach the map, so a key here cannot
34
+ * name bytes that stopped matching it — unlike a directory on someone's disk, where
35
+ * {@link DirectoryElisionStore.has} re-hashes for this same promise.
36
+ */
31
37
  has(hash: string): boolean;
32
38
  /** The five directly-observed counts. See {@link RawRetrieveCounters} — no derivation here. */
33
39
  rawCounters(): RawRetrieveCounters;
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE9D;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,yBAAyB;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;CAC7C;AAED,qBAAa,kBAAmB,YAAW,YAAY;;IAQrD,YAAY,OAAO,GAAE,yBAA8B,EAElD;IAED,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAU3B;IAED,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAErC;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAS7B;IAED,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzB;IAED,+FAA+F;IAC/F,WAAW,IAAI,mBAAmB,CAQjC;IAED,KAAK,IAAI,aAAa,CAErB;CACF"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE9D;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,yBAAyB;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;CAC7C;AAED,qBAAa,kBAAmB,YAAW,YAAY;;IAQrD,YAAY,OAAO,GAAE,yBAA8B,EAElD;IAED,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAU3B;IAED,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAErC;IAED,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAS7B;IAED;;;;;OAKG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzB;IAED,+FAA+F;IAC/F,WAAW,IAAI,mBAAmB,CAQjC;IAED,KAAK,IAAI,aAAa,CAErB;CACF","sourcesContent":["import { HashCollisionError, UnknownHashError } from './errors.ts';\nimport { contentHash } from './hash.ts';\nimport { retrieveStats } from './stats.ts';\nimport type { RawRetrieveCounters } from './stats.ts';\nimport type { ElisionStore, RetrieveStats } from './types.ts';\n\n/**\n * The default store: in-process, content-addressed, no eviction.\n *\n * In-process is the right default because it is the only one with no failure modes to\n * explain. A consumer that needs elisions to survive a process restart (a long-lived\n * agent session, say) hands `createSmelter()` a {@link DirectoryElisionStore} — or\n * implements {@link ElisionStore} over storage of its own; the interface is five\n * methods wide for exactly that reason.\n *\n * There is no `clear()` and no LRU. A store that can forget turns Law 3 into\n * \"reversible, usually\", and a `retrieve()` that fails after an eviction is\n * indistinguishable to the model from a hallucinated hash.\n */\nexport interface MemoryElisionStoreOptions {\n /**\n * Override the hash function. The only reason this exists: the collision branch in\n * {@link MemoryElisionStore.put} is unreachable with sha256, and an untestable branch\n * is a branch nobody knows works. A test injects a colliding hash and watches it\n * throw. Production has no reason to pass this.\n */\n readonly hash?: (content: string) => string;\n}\n\nexport class MemoryElisionStore implements ElisionStore {\n readonly #blobs = new Map<string, string>();\n readonly #hash: (content: string) => string;\n #bytesStored = 0;\n #retrieveCalls = 0;\n #misses = 0;\n readonly #retrievedHashes = new Set<string>();\n\n constructor(options: MemoryElisionStoreOptions = {}) {\n this.#hash = options.hash ?? contentHash;\n }\n\n put(content: string): string {\n const hash = this.#hash(content);\n const existing = this.#blobs.get(hash);\n if (existing !== undefined) {\n if (existing !== content) throw new HashCollisionError(hash);\n return hash;\n }\n this.#blobs.set(hash, content);\n this.#bytesStored += Buffer.byteLength(content, 'utf8');\n return hash;\n }\n\n peek(hash: string): string | undefined {\n return this.#blobs.get(hash);\n }\n\n retrieve(hash: string): string {\n this.#retrieveCalls += 1;\n const content = this.#blobs.get(hash);\n if (content === undefined) {\n this.#misses += 1;\n throw new UnknownHashError(hash);\n }\n this.#retrievedHashes.add(hash);\n return content;\n }\n\n /**\n * True exactly when `retrieve(hash)` would return bytes. Nothing to verify: `put`\n * hashed the content itself and no one else can reach the map, so a key here cannot\n * name bytes that stopped matching it — unlike a directory on someone's disk, where\n * {@link DirectoryElisionStore.has} re-hashes for this same promise.\n */\n has(hash: string): boolean {\n return this.#blobs.has(hash);\n }\n\n /** The five directly-observed counts. See {@link RawRetrieveCounters} — no derivation here. */\n rawCounters(): RawRetrieveCounters {\n return {\n elisionsStored: this.#blobs.size,\n bytesStored: this.#bytesStored,\n retrieveCalls: this.#retrieveCalls,\n uniqueRetrieved: this.#retrievedHashes.size,\n misses: this.#misses,\n };\n }\n\n stats(): RetrieveStats {\n return retrieveStats(this.rawCounters());\n }\n}\n"]}
package/dist/store.js CHANGED
@@ -36,6 +36,12 @@ export class MemoryElisionStore {
36
36
  this.#retrievedHashes.add(hash);
37
37
  return content;
38
38
  }
39
+ /**
40
+ * True exactly when `retrieve(hash)` would return bytes. Nothing to verify: `put`
41
+ * hashed the content itself and no one else can reach the map, so a key here cannot
42
+ * name bytes that stopped matching it — unlike a directory on someone's disk, where
43
+ * {@link DirectoryElisionStore.has} re-hashes for this same promise.
44
+ */
39
45
  has(hash) {
40
46
  return this.#blobs.has(hash);
41
47
  }
package/dist/store.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA2B3C,MAAM,OAAO,kBAAkB;IACpB,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACnC,KAAK,CAA8B;IAC5C,YAAY,GAAG,CAAC,CAAC;IACjB,cAAc,GAAG,CAAC,CAAC;IACnB,OAAO,GAAG,CAAC,CAAC;IACH,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;IAE9C,YAAY,OAAO,GAA8B,EAAE;QACjD,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC;IAC3C,CAAC;IAED,GAAG,CAAC,OAAe;QACjB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,IAAI,QAAQ,KAAK,OAAO;gBAAE,MAAM,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC;YAC7D,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC/B,IAAI,CAAC,YAAY,IAAI,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACxD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC,IAAY;QACf,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,QAAQ,CAAC,IAAY;QACnB,IAAI,CAAC,cAAc,IAAI,CAAC,CAAC;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;YAClB,MAAM,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC;QACD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,+FAA+F;IAC/F,WAAW;QACT,OAAO;YACL,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;YAChC,WAAW,EAAE,IAAI,CAAC,YAAY;YAC9B,aAAa,EAAE,IAAI,CAAC,cAAc;YAClC,eAAe,EAAE,IAAI,CAAC,gBAAgB,CAAC,IAAI;YAC3C,MAAM,EAAE,IAAI,CAAC,OAAO;SACrB,CAAC;IACJ,CAAC;IAED,KAAK;QACH,OAAO,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IAC3C,CAAC;CACF"}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA2B3C,MAAM,OAAO,kBAAkB;IACpB,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACnC,KAAK,CAA8B;IAC5C,YAAY,GAAG,CAAC,CAAC;IACjB,cAAc,GAAG,CAAC,CAAC;IACnB,OAAO,GAAG,CAAC,CAAC;IACH,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;IAE9C,YAAY,OAAO,GAA8B,EAAE;QACjD,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC;IAC3C,CAAC;IAED,GAAG,CAAC,OAAe;QACjB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,IAAI,QAAQ,KAAK,OAAO;gBAAE,MAAM,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC;YAC7D,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC/B,IAAI,CAAC,YAAY,IAAI,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACxD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC,IAAY;QACf,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,QAAQ,CAAC,IAAY;QACnB,IAAI,CAAC,cAAc,IAAI,CAAC,CAAC;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;YAClB,MAAM,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC;QACD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;;;OAKG;IACH,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,+FAA+F;IAC/F,WAAW;QACT,OAAO;YACL,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI;YAChC,WAAW,EAAE,IAAI,CAAC,YAAY;YAC9B,aAAa,EAAE,IAAI,CAAC,cAAc;YAClC,eAAe,EAAE,IAAI,CAAC,gBAAgB,CAAC,IAAI;YAC3C,MAAM,EAAE,IAAI,CAAC,OAAO;SACrB,CAAC;IACJ,CAAC;IAED,KAAK;QACH,OAAO,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IAC3C,CAAC;CACF","sourcesContent":["import { HashCollisionError, UnknownHashError } from './errors.ts';\nimport { contentHash } from './hash.ts';\nimport { retrieveStats } from './stats.ts';\nimport type { RawRetrieveCounters } from './stats.ts';\nimport type { ElisionStore, RetrieveStats } from './types.ts';\n\n/**\n * The default store: in-process, content-addressed, no eviction.\n *\n * In-process is the right default because it is the only one with no failure modes to\n * explain. A consumer that needs elisions to survive a process restart (a long-lived\n * agent session, say) hands `createSmelter()` a {@link DirectoryElisionStore} — or\n * implements {@link ElisionStore} over storage of its own; the interface is five\n * methods wide for exactly that reason.\n *\n * There is no `clear()` and no LRU. A store that can forget turns Law 3 into\n * \"reversible, usually\", and a `retrieve()` that fails after an eviction is\n * indistinguishable to the model from a hallucinated hash.\n */\nexport interface MemoryElisionStoreOptions {\n /**\n * Override the hash function. The only reason this exists: the collision branch in\n * {@link MemoryElisionStore.put} is unreachable with sha256, and an untestable branch\n * is a branch nobody knows works. A test injects a colliding hash and watches it\n * throw. Production has no reason to pass this.\n */\n readonly hash?: (content: string) => string;\n}\n\nexport class MemoryElisionStore implements ElisionStore {\n readonly #blobs = new Map<string, string>();\n readonly #hash: (content: string) => string;\n #bytesStored = 0;\n #retrieveCalls = 0;\n #misses = 0;\n readonly #retrievedHashes = new Set<string>();\n\n constructor(options: MemoryElisionStoreOptions = {}) {\n this.#hash = options.hash ?? contentHash;\n }\n\n put(content: string): string {\n const hash = this.#hash(content);\n const existing = this.#blobs.get(hash);\n if (existing !== undefined) {\n if (existing !== content) throw new HashCollisionError(hash);\n return hash;\n }\n this.#blobs.set(hash, content);\n this.#bytesStored += Buffer.byteLength(content, 'utf8');\n return hash;\n }\n\n peek(hash: string): string | undefined {\n return this.#blobs.get(hash);\n }\n\n retrieve(hash: string): string {\n this.#retrieveCalls += 1;\n const content = this.#blobs.get(hash);\n if (content === undefined) {\n this.#misses += 1;\n throw new UnknownHashError(hash);\n }\n this.#retrievedHashes.add(hash);\n return content;\n }\n\n /**\n * True exactly when `retrieve(hash)` would return bytes. Nothing to verify: `put`\n * hashed the content itself and no one else can reach the map, so a key here cannot\n * name bytes that stopped matching it — unlike a directory on someone's disk, where\n * {@link DirectoryElisionStore.has} re-hashes for this same promise.\n */\n has(hash: string): boolean {\n return this.#blobs.has(hash);\n }\n\n /** The five directly-observed counts. See {@link RawRetrieveCounters} — no derivation here. */\n rawCounters(): RawRetrieveCounters {\n return {\n elisionsStored: this.#blobs.size,\n bytesStored: this.#bytesStored,\n retrieveCalls: this.#retrieveCalls,\n uniqueRetrieved: this.#retrievedHashes.size,\n misses: this.#misses,\n };\n }\n\n stats(): RetrieveStats {\n return retrieveStats(this.rawCounters());\n }\n}\n"]}
package/dist/types.d.ts CHANGED
@@ -111,7 +111,7 @@ export interface AppliedElision {
111
111
  *
112
112
  * Budgets in smelt's core are UTF-8 bytes, permanently — bytes are the only unit that
113
113
  * is computable locally for every model, and they mean the same thing in five years.
114
- * See `docs/HANDOFF.md` § "Decision 1". This hook does not change that: it adds a
114
+ * See `docs/ARCHITECTURE.md` § "Decision 1". This hook does not change that: it adds a
115
115
  * second, labelled number to the result. The plan is still made in bytes.
116
116
  *
117
117
  * Both `id` and `unit` are required, and that is a Law 4 decision rather than
@@ -227,9 +227,28 @@ export interface ElisionStore {
227
227
  peek(hash: string): string | undefined;
228
228
  /**
229
229
  * The stored content, *counted* as a retrieval. This is what the model's tool calls.
230
+ *
230
231
  * @throws {UnknownHashError} when the hash is unknown.
232
+ * @throws {StoreCorruptionError} — {@link DirectoryElisionStore} only — when the
233
+ * bytes on disk no longer hash to their own name. Distinct from
234
+ * `UnknownHashError` on purpose: "we hold damaged bytes" and "it never existed"
235
+ * are different answers, and a caller that conflates them will report the wrong
236
+ * one to its user.
237
+ *
238
+ * A {@link DirectoryElisionStore} whose journal cannot be written (a read-only
239
+ * store directory, a full disk) still returns the bytes — verified bytes are never
240
+ * withheld over a bookkeeping failure — and surfaces the lost count as a
241
+ * `process.emitWarning` named `SmeltCounterWriteFailure` instead of throwing. Its
242
+ * stats go quiet from that point; the retrieval itself succeeded.
231
243
  */
232
244
  retrieve(hash: string): string;
245
+ /**
246
+ * Whether this hash can be retrieved — **not** merely whether a slot bearing it
247
+ * exists. `true` must mean the very next `retrieve(hash)` returns bytes, so a store
248
+ * that verifies content on read verifies here too and raises its corruption error
249
+ * rather than answering `true` for bytes it would then refuse. Uncounted: a check is
250
+ * not the model asking for material back.
251
+ */
233
252
  has(hash: string): boolean;
234
253
  /** A snapshot of the counters. See {@link RetrieveStats}. */
235
254
  stats(): RetrieveStats;
@@ -244,17 +263,30 @@ export interface RetrieveTool {
244
263
  readonly name: string;
245
264
  /** Prose the consumer can put straight into a tool description. */
246
265
  readonly description: string;
247
- /** JSON-Schema-shaped parameter description, for consumers that want one. */
266
+ /**
267
+ * JSON-Schema-shaped parameter description, for consumers that want one.
268
+ *
269
+ * Strict-mode shaped: `additionalProperties: false` and a `required` naming every
270
+ * property, so a consumer registering this under OpenAI's structured-outputs strict
271
+ * mode is not refused at registration. See {@link createRetrieveTool}.
272
+ */
248
273
  readonly inputSchema: {
249
274
  readonly type: 'object';
250
275
  readonly properties: {
251
276
  readonly hash: {
252
277
  readonly type: 'string';
278
+ readonly description: string;
253
279
  };
254
280
  };
255
281
  readonly required: readonly ['hash'];
282
+ readonly additionalProperties: false;
256
283
  };
257
- /** @throws {UnknownHashError} when the hash is unknown. */
284
+ /**
285
+ * @throws {UnknownHashError} when the hash is unknown.
286
+ * @throws {StoreCorruptionError} when the backing store holds damaged bytes for
287
+ * that hash — see {@link ElisionStore.retrieve}, whose contract this forwards
288
+ * verbatim. Surface either to the model as a tool error, never as empty text.
289
+ */
258
290
  invoke(input: {
259
291
  readonly hash: string;
260
292
  }): string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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","sourcesContent":["/** Languages smelt can parse structurally. Everything else falls back to the lexical planner. */\nexport type LanguageId =\n | 'typescript'\n | 'tsx'\n | 'javascript'\n | 'rust'\n | 'python'\n | 'go'\n | 'java'\n | 'c'\n | 'cpp'\n | 'c_sharp'\n | 'ruby'\n | 'php'\n | 'kotlin'\n | 'swift'\n | 'bash';\n\n/** `'unknown'` is a first-class outcome, not a failure: it selects the lexical planner. */\nexport type DetectedLanguage = LanguageId | 'unknown';\n\n/** A half-open byte range `[start, end)` into the UTF-8 bytes of the input. */\nexport interface ByteRange {\n readonly start: number;\n readonly end: number;\n}\n\n/**\n * Why a range was elided, in two registers: a stable id for counters, and a sentence\n * a human can read in a diff. Law 2 — every elision is explainable — lives here:\n * if you cannot write the sentence, the rule does not ship.\n */\nexport interface ElisionReason {\n /** Stable machine id, e.g. `'sibling-collapse'`, `'line-window'`. */\n readonly rule: string;\n /** e.g. `'collapsed 3 sibling functions'`. Present tense, no trailing period. */\n readonly explanation: string;\n}\n\n/** One range a planner proposes to remove. Plans are pure data — inspectable and testable. */\nexport interface PlannedElision {\n readonly range: ByteRange;\n readonly reason: ElisionReason;\n}\n\n/**\n * The complete output of a planner: the whole decision, before anything is mutated.\n * A plan can be logged, diffed, snapshot-tested, and rejected without touching the text.\n */\nexport interface ElisionPlan {\n readonly planner: string;\n readonly language: DetectedLanguage;\n readonly elisions: readonly PlannedElision[];\n}\n\n/**\n * The seam through which a planner asks what a marker will cost, in UTF-8 bytes.\n *\n * Marker cost is `apply.ts`'s fact — the applier renders the marker, so only the\n * applier knows its price. Planners need that price for two decisions (profitability:\n * a marker that costs more than it removes grows the output; and budget prediction:\n * which ladder rung actually fits), and before this seam each planner rebuilt the\n * marker machinery privately to measure it — correct, but an inversion. Now\n * `markerPricing()` in `apply.ts` builds the one adapter from the exact builder\n * `applyPlan` will use, and planners ask it. They never guess, and they never render\n * a marker of their own.\n *\n * The price is exact, not an estimate: the cost of the marker this elision would\n * earn, comment leader and all, with a stand-in hash of the real hash's length —\n * marker cost depends on the hash's *length*, never its value.\n */\nexport interface MarkerPricing {\n /** The exact UTF-8 byte cost of the marker an elision with this reason and size would earn. */\n costBytes(reason: ElisionReason, elidedBytes: number): number;\n}\n\n/**\n * What the caller hands a planner.\n *\n * Constructed centrally: `createSmelter` (and through it, the CLI) builds the one\n * `PlanInput` per call, including its {@link MarkerPricing} — a caller invoking\n * `planLexical`/`planStructural` directly builds `pricing` with `markerPricing()`\n * from `apply.ts`. A JS caller who omits it gets {@link MissingMarkerPricingError}\n * at plan time, not a guessed cost.\n */\nexport interface PlanInput {\n readonly text: string;\n readonly language: DetectedLanguage;\n /** Soft ceiling for the emitted output, in UTF-8 bytes. Planners aim under it. */\n readonly budgetBytes: number;\n /**\n * What the caller was actually looking for — grep pattern, symbol name, error string.\n * Planners keep matching regions and collapse around them.\n */\n readonly focus?: readonly string[];\n /**\n * What a marker costs. Required: a planner that guesses marker cost can plan an\n * elision that grows the output. See {@link MarkerPricing}; built by\n * `markerPricing()` in `apply.ts` from the exact builder `applyPlan` will use.\n */\n readonly pricing: MarkerPricing;\n}\n\n/**\n * A planner decides *what* to remove. It never removes anything itself; `applyPlan`\n * does that. Keeping the decision and the mutation apart is what makes the decision\n * testable in isolation.\n */\nexport interface Planner {\n readonly id: string;\n plan(input: PlanInput): Promise<ElisionPlan>;\n}\n\n/** One elision that actually happened, with the receipt needed to undo it. */\nexport interface AppliedElision {\n /** Content hash of the removed bytes — the key `retrieve()` takes. */\n readonly hash: string;\n /** Where the removed bytes were in the *input*. */\n readonly range: ByteRange;\n /**\n * Where the marker sits in the *output*. Law 3 — every elision is reversible — needs\n * this: {@link Reconstructor} splices stored bytes back over these ranges. Without it,\n * \"reversible\" would mean parsing markers back out of the text, which is a guess.\n * This is a fact recorded at the moment of the cut.\n */\n readonly outputRange: ByteRange;\n /** Size of the removed content, in UTF-8 bytes. */\n readonly bytes: number;\n readonly reason: ElisionReason;\n /** The exact marker text substituted into the output. */\n readonly marker: string;\n}\n\n/**\n * A consumer-supplied counter, so a caller who bills in tokens can *see* tokens.\n *\n * Budgets in smelt's core are UTF-8 bytes, permanently — bytes are the only unit that\n * is computable locally for every model, and they mean the same thing in five years.\n * See `docs/ARCHITECTURE.md` § \"Decision 1\". This hook does not change that: it adds a\n * second, labelled number to the result. The plan is still made in bytes.\n *\n * Both `id` and `unit` are required, and that is a Law 4 decision rather than\n * bookkeeping: a token count is meaningless without naming the tokenizer that produced\n * it. Anthropic's own docs record that Claude 4.7 and later use a newer tokenizer where\n * the same text yields roughly 30% more tokens than on earlier models — so `1,204\n * tokens` is not a fact, and `1,204 tokens (claude-4.7/count_tokens)` is.\n *\n * **This hook does not relax Law 1.** smelt imports no transport and the guard proves\n * that about smelt's own modules; it cannot prove it about a function you hand in. A\n * `count()` that calls an API makes *your* process call an API, from a line in *your*\n * source — exactly the arrangement {@link RerankStage} already describes. `count` is\n * synchronous on purpose: local tokenizers are synchronous, and network clients are not.\n */\nexport interface Measure {\n /** Names the counter, e.g. `'tiktoken/o200k_base'` or `'claude-4.7/count_tokens'`. */\n readonly id: string;\n /** The unit `count()` returns, e.g. `'tokens'`. Printed next to the number. */\n readonly unit: string;\n /** Local, synchronous count over the whole string. */\n count(text: string): number;\n}\n\n/** A second size for a result, in someone else's unit, with the counter named. */\nexport interface MeasuredSize {\n /** {@link Measure.id} of the counter that produced these numbers. */\n readonly measure: string;\n /** {@link Measure.unit}. */\n readonly unit: string;\n readonly input: number;\n readonly output: number;\n}\n\n/** The result of smelting one blob of text. */\nexport interface SmeltResult {\n readonly text: string;\n readonly inputBytes: number;\n readonly outputBytes: number;\n readonly planner: string;\n readonly language: DetectedLanguage;\n readonly elisions: readonly AppliedElision[];\n /** Present only when the caller supplied a {@link Measure}. Never invented. */\n readonly measured?: MeasuredSize;\n}\n\n/** Options for a single `smelt()` call. */\nexport interface SmeltOptions {\n /** Soft ceiling for the emitted output, in UTF-8 bytes. */\n readonly budgetBytes: number;\n /** Filename or path, used to detect the language. Optional; detection falls back to `'unknown'`. */\n readonly path?: string;\n /** Override language detection entirely. */\n readonly language?: DetectedLanguage;\n /** What the caller was looking for. See {@link PlanInput.focus}. */\n readonly focus?: readonly string[];\n}\n\n/**\n * Reversibility, as a callable. Takes a {@link SmeltResult} and the store that holds its\n * elided bytes, and returns the original text — byte for byte.\n */\nexport type Reconstructor = (result: SmeltResult, store: ElisionStore) => string;\n\n// ---------------------------------------------------------------------------\n// The store, and the counters that make over-pruning visible\n// ---------------------------------------------------------------------------\n\n/**\n * The numbers that keep smelt honest about itself.\n *\n * Law 3 says elisions are reversible. That is cheap to satisfy and easy to abuse: a\n * compressor that cuts everything is \"reversible\" and useless. The *retrieve rate* is\n * the tell. If the model keeps calling `smelt_retrieve`, smelt cut material the task\n * needed, and the round trip cost more tokens than the elision saved.\n *\n * So: `expansionRate` is not telemetry. It is the metric a caller tunes budgets\n * against, and the only number smelt is willing to have an opinion about — because it\n * measures it locally, per session, on the caller's own traffic.\n */\nexport interface RetrieveStats {\n /** Distinct blobs put into the store. */\n readonly elisionsStored: number;\n /** Total bytes held by the store. */\n readonly bytesStored: number;\n /** Every `retrieve()` call, including repeats and misses. */\n readonly retrieveCalls: number;\n /** Distinct hashes successfully retrieved at least once. */\n readonly uniqueRetrieved: number;\n /** Calls for a hash the store does not hold. Non-zero means a bug, not over-pruning. */\n readonly misses: number;\n /**\n * `uniqueRetrieved / elisionsStored`, or `0` when nothing has been stored.\n *\n * Read it as: *what fraction of what smelt hid did the model have to ask for back?*\n * There is no universally right value, and smelt does not ship a threshold it has\n * not measured. Rising across a workload is the signal.\n */\n readonly expansionRate: number;\n /**\n * The one degenerate outcome smelt is willing to name: **every distinct blob it hid\n * was asked for again.**\n *\n * smelt ships no expansion-rate threshold, because a threshold is a policy claim it\n * has no basis for and the right rate depends on how aggressive a budget the caller\n * chose — and a library printing warnings into someone else's process is bad manners.\n * This is not a threshold. At `uniqueRetrieved === elisionsStored` the elision\n * achieved nothing and cost a round trip: an arithmetic fact, not a preference. What\n * to do about it is the caller's call.\n *\n * `false` for an empty store — nothing was hidden, so nothing was defeated.\n */\n readonly allElisionsRetrieved: boolean;\n}\n\n/**\n * Local, content-addressed storage for elided bytes. No network, no eviction in v1 —\n * evicting is how \"reversible\" quietly becomes \"reversible for a while\".\n */\nexport interface ElisionStore {\n /** Store content, returning its hash. Idempotent for identical content. */\n put(content: string): string;\n /** The stored content, or `undefined` if this store never held that hash. */\n peek(hash: string): string | undefined;\n /**\n * The stored content, *counted* as a retrieval. This is what the model's tool calls.\n *\n * @throws {UnknownHashError} when the hash is unknown.\n * @throws {StoreCorruptionError} — {@link DirectoryElisionStore} only — when the\n * bytes on disk no longer hash to their own name. Distinct from\n * `UnknownHashError` on purpose: \"we hold damaged bytes\" and \"it never existed\"\n * are different answers, and a caller that conflates them will report the wrong\n * one to its user.\n *\n * A {@link DirectoryElisionStore} whose journal cannot be written (a read-only\n * store directory, a full disk) still returns the bytes — verified bytes are never\n * withheld over a bookkeeping failure — and surfaces the lost count as a\n * `process.emitWarning` named `SmeltCounterWriteFailure` instead of throwing. Its\n * stats go quiet from that point; the retrieval itself succeeded.\n */\n retrieve(hash: string): string;\n /**\n * Whether this hash can be retrieved — **not** merely whether a slot bearing it\n * exists. `true` must mean the very next `retrieve(hash)` returns bytes, so a store\n * that verifies content on read verifies here too and raises its corruption error\n * rather than answering `true` for bytes it would then refuse. Uncounted: a check is\n * not the model asking for material back.\n */\n has(hash: string): boolean;\n /** A snapshot of the counters. See {@link RetrieveStats}. */\n stats(): RetrieveStats;\n}\n\n/**\n * The retrieval tool a consumer exposes to its model. Deliberately not an MCP or\n * provider-specific shape — smelt does not know which SDK you use. The consumer adapts\n * this into its own tool schema; the contract is `hash in, exact bytes out`.\n */\nexport interface RetrieveTool {\n /** `'smelt_retrieve'`. Stable — consumers hard-code it in prompts. */\n readonly name: string;\n /** Prose the consumer can put straight into a tool description. */\n readonly description: string;\n /**\n * JSON-Schema-shaped parameter description, for consumers that want one.\n *\n * Strict-mode shaped: `additionalProperties: false` and a `required` naming every\n * property, so a consumer registering this under OpenAI's structured-outputs strict\n * mode is not refused at registration. See {@link createRetrieveTool}.\n */\n readonly inputSchema: {\n readonly type: 'object';\n readonly properties: {\n readonly hash: { readonly type: 'string'; readonly description: string };\n };\n readonly required: readonly ['hash'];\n readonly additionalProperties: false;\n };\n /**\n * @throws {UnknownHashError} when the hash is unknown.\n * @throws {StoreCorruptionError} when the backing store holds damaged bytes for\n * that hash — see {@link ElisionStore.retrieve}, whose contract this forwards\n * verbatim. Surface either to the model as a tool error, never as empty text.\n */\n invoke(input: { readonly hash: string }): string;\n}\n\n// ---------------------------------------------------------------------------\n// Pluggable stages — interfaces in v1, nothing more\n// ---------------------------------------------------------------------------\n\n/** A candidate handed to a {@link RerankStage}: an opaque id plus the text to judge. */\nexport interface RerankCandidate {\n readonly id: string;\n readonly text: string;\n}\n\n/** A reranked candidate, most relevant first. `score` is the stage's own scale. */\nexport interface RerankedCandidate extends RerankCandidate {\n readonly score: number;\n}\n\n/**\n * Relevance reranking — a *seam*, not a feature.\n *\n * Hosted rerankers are good and smelt will never bundle one, because bundling would\n * break Law 1: the moment smelt ships a default reranker, `smelt()` can make a network\n * call that the caller did not ask for and cannot see. A consumer that wants one\n * implements this interface, wires its own key, and owns the fact that its context now\n * leaves the machine. That decision must be legible in the consumer's own source.\n */\nexport interface RerankStage {\n readonly id: string;\n /** May make network calls — that is the consumer's choice, made in the consumer's code. */\n rerank(\n candidates: readonly RerankCandidate[],\n query: string,\n ): Promise<readonly RerankedCandidate[]>;\n}\n\n/**\n * Learned distillation — rewriting content with a model instead of cutting it.\n *\n * Out of v1 for a reason beyond the network: a distilled paragraph cannot satisfy\n * Law 2. \"The model summarised this\" is not an explanation of what was removed, and\n * the removed material is no longer recoverable from the output. If this ever ships,\n * it ships as a stage that stores the original and explains itself in the same terms\n * every other rule does.\n */\nexport interface DistillStage {\n readonly id: string;\n distill(text: string, budgetBytes: number): Promise<string>;\n}\n"]}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["/** Languages smelt can parse structurally. Everything else falls back to the lexical planner. */\nexport type LanguageId =\n | 'typescript'\n | 'tsx'\n | 'javascript'\n | 'rust'\n | 'python'\n | 'go'\n | 'java'\n | 'c'\n | 'cpp'\n | 'c_sharp'\n | 'ruby'\n | 'php'\n | 'kotlin'\n | 'swift'\n | 'bash';\n\n/** `'unknown'` is a first-class outcome, not a failure: it selects the lexical planner. */\nexport type DetectedLanguage = LanguageId | 'unknown';\n\n/** A half-open byte range `[start, end)` into the UTF-8 bytes of the input. */\nexport interface ByteRange {\n readonly start: number;\n readonly end: number;\n}\n\n/**\n * Why a range was elided, in two registers: a stable id for counters, and a sentence\n * a human can read in a diff. Law 2 — every elision is explainable — lives here:\n * if you cannot write the sentence, the rule does not ship.\n */\nexport interface ElisionReason {\n /** Stable machine id, e.g. `'sibling-collapse'`, `'line-window'`. */\n readonly rule: string;\n /** e.g. `'collapsed 3 sibling functions'`. Present tense, no trailing period. */\n readonly explanation: string;\n}\n\n/** One range a planner proposes to remove. Plans are pure data — inspectable and testable. */\nexport interface PlannedElision {\n readonly range: ByteRange;\n readonly reason: ElisionReason;\n}\n\n/**\n * The complete output of a planner: the whole decision, before anything is mutated.\n * A plan can be logged, diffed, snapshot-tested, and rejected without touching the text.\n */\nexport interface ElisionPlan {\n readonly planner: string;\n readonly language: DetectedLanguage;\n readonly elisions: readonly PlannedElision[];\n}\n\n/**\n * The seam through which a planner asks what a marker will cost, in UTF-8 bytes.\n *\n * Marker cost is `apply.ts`'s fact — the applier renders the marker, so only the\n * applier knows its price. Planners need that price for two decisions (profitability:\n * a marker that costs more than it removes grows the output; and budget prediction:\n * which ladder rung actually fits), and before this seam each planner rebuilt the\n * marker machinery privately to measure it — correct, but an inversion. Now\n * `markerPricing()` in `apply.ts` builds the one adapter from the exact builder\n * `applyPlan` will use, and planners ask it. They never guess, and they never render\n * a marker of their own.\n *\n * The price is exact, not an estimate: the cost of the marker this elision would\n * earn, comment leader and all, with a stand-in hash of the real hash's length —\n * marker cost depends on the hash's *length*, never its value.\n */\nexport interface MarkerPricing {\n /** The exact UTF-8 byte cost of the marker an elision with this reason and size would earn. */\n costBytes(reason: ElisionReason, elidedBytes: number): number;\n}\n\n/**\n * What the caller hands a planner.\n *\n * Constructed centrally: `createSmelter` (and through it, the CLI) builds the one\n * `PlanInput` per call, including its {@link MarkerPricing} — a caller invoking\n * `planLexical`/`planStructural` directly builds `pricing` with `markerPricing()`\n * from `apply.ts`. A JS caller who omits it gets {@link MissingMarkerPricingError}\n * at plan time, not a guessed cost.\n */\nexport interface PlanInput {\n readonly text: string;\n readonly language: DetectedLanguage;\n /** Soft ceiling for the emitted output, in UTF-8 bytes. Planners aim under it. */\n readonly budgetBytes: number;\n /**\n * What the caller was actually looking for — grep pattern, symbol name, error string.\n * Planners keep matching regions and collapse around them.\n */\n readonly focus?: readonly string[];\n /**\n * What a marker costs. Required: a planner that guesses marker cost can plan an\n * elision that grows the output. See {@link MarkerPricing}; built by\n * `markerPricing()` in `apply.ts` from the exact builder `applyPlan` will use.\n */\n readonly pricing: MarkerPricing;\n}\n\n/**\n * A planner decides *what* to remove. It never removes anything itself; `applyPlan`\n * does that. Keeping the decision and the mutation apart is what makes the decision\n * testable in isolation.\n */\nexport interface Planner {\n readonly id: string;\n plan(input: PlanInput): Promise<ElisionPlan>;\n}\n\n/** One elision that actually happened, with the receipt needed to undo it. */\nexport interface AppliedElision {\n /** Content hash of the removed bytes — the key `retrieve()` takes. */\n readonly hash: string;\n /** Where the removed bytes were in the *input*. */\n readonly range: ByteRange;\n /**\n * Where the marker sits in the *output*. Law 3 — every elision is reversible — needs\n * this: {@link Reconstructor} splices stored bytes back over these ranges. Without it,\n * \"reversible\" would mean parsing markers back out of the text, which is a guess.\n * This is a fact recorded at the moment of the cut.\n */\n readonly outputRange: ByteRange;\n /** Size of the removed content, in UTF-8 bytes. */\n readonly bytes: number;\n readonly reason: ElisionReason;\n /** The exact marker text substituted into the output. */\n readonly marker: string;\n}\n\n/**\n * A consumer-supplied counter, so a caller who bills in tokens can *see* tokens.\n *\n * Budgets in smelt's core are UTF-8 bytes, permanently — bytes are the only unit that\n * is computable locally for every model, and they mean the same thing in five years.\n * See `docs/ARCHITECTURE.md` § \"Decision 1\". This hook does not change that: it adds a\n * second, labelled number to the result. The plan is still made in bytes.\n *\n * Both `id` and `unit` are required, and that is a Law 4 decision rather than\n * bookkeeping: a token count is meaningless without naming the tokenizer that produced\n * it. Anthropic's own docs record that Claude 4.7 and later use a newer tokenizer where\n * the same text yields roughly 30% more tokens than on earlier models — so `1,204\n * tokens` is not a fact, and `1,204 tokens (claude-4.7/count_tokens)` is.\n *\n * **This hook does not relax Law 1.** smelt imports no transport and the guard proves\n * that about smelt's own modules; it cannot prove it about a function you hand in. A\n * `count()` that calls an API makes *your* process call an API, from a line in *your*\n * source — exactly the arrangement {@link RerankStage} already describes. `count` is\n * synchronous on purpose: local tokenizers are synchronous, and network clients are not.\n */\nexport interface Measure {\n /** Names the counter, e.g. `'tiktoken/o200k_base'` or `'claude-4.7/count_tokens'`. */\n readonly id: string;\n /** The unit `count()` returns, e.g. `'tokens'`. Printed next to the number. */\n readonly unit: string;\n /** Local, synchronous count over the whole string. */\n count(text: string): number;\n}\n\n/** A second size for a result, in someone else's unit, with the counter named. */\nexport interface MeasuredSize {\n /** {@link Measure.id} of the counter that produced these numbers. */\n readonly measure: string;\n /** {@link Measure.unit}. */\n readonly unit: string;\n readonly input: number;\n readonly output: number;\n}\n\n/** The result of smelting one blob of text. */\nexport interface SmeltResult {\n readonly text: string;\n readonly inputBytes: number;\n readonly outputBytes: number;\n readonly planner: string;\n readonly language: DetectedLanguage;\n readonly elisions: readonly AppliedElision[];\n /** Present only when the caller supplied a {@link Measure}. Never invented. */\n readonly measured?: MeasuredSize;\n}\n\n/** Options for a single `smelt()` call. */\nexport interface SmeltOptions {\n /** Soft ceiling for the emitted output, in UTF-8 bytes. */\n readonly budgetBytes: number;\n /** Filename or path, used to detect the language. Optional; detection falls back to `'unknown'`. */\n readonly path?: string;\n /** Override language detection entirely. */\n readonly language?: DetectedLanguage;\n /** What the caller was looking for. See {@link PlanInput.focus}. */\n readonly focus?: readonly string[];\n}\n\n/**\n * Reversibility, as a callable. Takes a {@link SmeltResult} and the store that holds its\n * elided bytes, and returns the original text — byte for byte.\n */\nexport type Reconstructor = (result: SmeltResult, store: ElisionStore) => string;\n\n// ---------------------------------------------------------------------------\n// The store, and the counters that make over-pruning visible\n// ---------------------------------------------------------------------------\n\n/**\n * The numbers that keep smelt honest about itself.\n *\n * Law 3 says elisions are reversible. That is cheap to satisfy and easy to abuse: a\n * compressor that cuts everything is \"reversible\" and useless. The *retrieve rate* is\n * the tell. If the model keeps calling `smelt_retrieve`, smelt cut material the task\n * needed, and the round trip cost more tokens than the elision saved.\n *\n * So: `expansionRate` is not telemetry. It is the metric a caller tunes budgets\n * against, and the only number smelt is willing to have an opinion about — because it\n * measures it locally, per session, on the caller's own traffic.\n */\nexport interface RetrieveStats {\n /** Distinct blobs put into the store. */\n readonly elisionsStored: number;\n /** Total bytes held by the store. */\n readonly bytesStored: number;\n /** Every `retrieve()` call, including repeats and misses. */\n readonly retrieveCalls: number;\n /** Distinct hashes successfully retrieved at least once. */\n readonly uniqueRetrieved: number;\n /** Calls for a hash the store does not hold. Non-zero means a bug, not over-pruning. */\n readonly misses: number;\n /**\n * `uniqueRetrieved / elisionsStored`, or `0` when nothing has been stored.\n *\n * Read it as: *what fraction of what smelt hid did the model have to ask for back?*\n * There is no universally right value, and smelt does not ship a threshold it has\n * not measured. Rising across a workload is the signal.\n */\n readonly expansionRate: number;\n /**\n * The one degenerate outcome smelt is willing to name: **every distinct blob it hid\n * was asked for again.**\n *\n * smelt ships no expansion-rate threshold, because a threshold is a policy claim it\n * has no basis for and the right rate depends on how aggressive a budget the caller\n * chose — and a library printing warnings into someone else's process is bad manners.\n * This is not a threshold. At `uniqueRetrieved === elisionsStored` the elision\n * achieved nothing and cost a round trip: an arithmetic fact, not a preference. What\n * to do about it is the caller's call.\n *\n * `false` for an empty store — nothing was hidden, so nothing was defeated.\n */\n readonly allElisionsRetrieved: boolean;\n}\n\n/**\n * Local, content-addressed storage for elided bytes. No network, no eviction in v1 —\n * evicting is how \"reversible\" quietly becomes \"reversible for a while\".\n */\nexport interface ElisionStore {\n /** Store content, returning its hash. Idempotent for identical content. */\n put(content: string): string;\n /** The stored content, or `undefined` if this store never held that hash. */\n peek(hash: string): string | undefined;\n /**\n * The stored content, *counted* as a retrieval. This is what the model's tool calls.\n *\n * @throws {UnknownHashError} when the hash is unknown.\n * @throws {StoreCorruptionError} — {@link DirectoryElisionStore} only — when the\n * bytes on disk no longer hash to their own name. Distinct from\n * `UnknownHashError` on purpose: \"we hold damaged bytes\" and \"it never existed\"\n * are different answers, and a caller that conflates them will report the wrong\n * one to its user.\n *\n * A {@link DirectoryElisionStore} whose journal cannot be written (a read-only\n * store directory, a full disk) still returns the bytes — verified bytes are never\n * withheld over a bookkeeping failure — and surfaces the lost count as a\n * `process.emitWarning` named `SmeltCounterWriteFailure` instead of throwing. Its\n * stats go quiet from that point; the retrieval itself succeeded.\n */\n retrieve(hash: string): string;\n /**\n * Whether this hash can be retrieved — **not** merely whether a slot bearing it\n * exists. `true` must mean the very next `retrieve(hash)` returns bytes, so a store\n * that verifies content on read verifies here too and raises its corruption error\n * rather than answering `true` for bytes it would then refuse. Uncounted: a check is\n * not the model asking for material back.\n */\n has(hash: string): boolean;\n /** A snapshot of the counters. See {@link RetrieveStats}. */\n stats(): RetrieveStats;\n}\n\n/**\n * The retrieval tool a consumer exposes to its model. Deliberately not an MCP or\n * provider-specific shape — smelt does not know which SDK you use. The consumer adapts\n * this into its own tool schema; the contract is `hash in, exact bytes out`.\n */\nexport interface RetrieveTool {\n /** `'smelt_retrieve'`. Stable — consumers hard-code it in prompts. */\n readonly name: string;\n /** Prose the consumer can put straight into a tool description. */\n readonly description: string;\n /**\n * JSON-Schema-shaped parameter description, for consumers that want one.\n *\n * Strict-mode shaped: `additionalProperties: false` and a `required` naming every\n * property, so a consumer registering this under OpenAI's structured-outputs strict\n * mode is not refused at registration. See {@link createRetrieveTool}.\n */\n readonly inputSchema: {\n readonly type: 'object';\n readonly properties: {\n readonly hash: { readonly type: 'string'; readonly description: string };\n };\n readonly required: readonly ['hash'];\n readonly additionalProperties: false;\n };\n /**\n * @throws {UnknownHashError} when the hash is unknown.\n * @throws {StoreCorruptionError} when the backing store holds damaged bytes for\n * that hash — see {@link ElisionStore.retrieve}, whose contract this forwards\n * verbatim. Surface either to the model as a tool error, never as empty text.\n */\n invoke(input: { readonly hash: string }): string;\n}\n\n// ---------------------------------------------------------------------------\n// Pluggable stages — interfaces in v1, nothing more\n// ---------------------------------------------------------------------------\n\n/** A candidate handed to a {@link RerankStage}: an opaque id plus the text to judge. */\nexport interface RerankCandidate {\n readonly id: string;\n readonly text: string;\n}\n\n/** A reranked candidate, most relevant first. `score` is the stage's own scale. */\nexport interface RerankedCandidate extends RerankCandidate {\n readonly score: number;\n}\n\n/**\n * Relevance reranking — a *seam*, not a feature.\n *\n * Hosted rerankers are good and smelt will never bundle one, because bundling would\n * break Law 1: the moment smelt ships a default reranker, `smelt()` can make a network\n * call that the caller did not ask for and cannot see. A consumer that wants one\n * implements this interface, wires its own key, and owns the fact that its context now\n * leaves the machine. That decision must be legible in the consumer's own source.\n */\nexport interface RerankStage {\n readonly id: string;\n /** May make network calls — that is the consumer's choice, made in the consumer's code. */\n rerank(\n candidates: readonly RerankCandidate[],\n query: string,\n ): Promise<readonly RerankedCandidate[]>;\n}\n\n/**\n * Learned distillation — rewriting content with a model instead of cutting it.\n *\n * Out of v1 for a reason beyond the network: a distilled paragraph cannot satisfy\n * Law 2. \"The model summarised this\" is not an explanation of what was removed, and\n * the removed material is no longer recoverable from the output. If this ever ships,\n * it ships as a stage that stores the original and explains itself in the same terms\n * every other rule does.\n */\nexport interface DistillStage {\n readonly id: string;\n distill(text: string, budgetBytes: number): Promise<string>;\n}\n"]}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smeltjs/core",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Structure-aware, reversible, offline context optimization for AI coding agents.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "mong-x",
@@ -56,14 +56,6 @@
56
56
  "publishConfig": {
57
57
  "access": "public"
58
58
  },
59
- "scripts": {
60
- "build": "tsc -p tsconfig.build.json && node ../../scripts/bundle-grammars.mjs",
61
- "typecheck": "tsc -p tsconfig.json --noEmit",
62
- "test": "vitest run",
63
- "test:watch": "vitest",
64
- "prepack": "pnpm run build && node ../../scripts/generate-third-party.mjs",
65
- "bench": "node bench/run.mjs"
66
- },
67
59
  "dependencies": {
68
60
  "web-tree-sitter": "~0.25.10"
69
61
  },
@@ -71,6 +63,14 @@
71
63
  "@types/node": "^26.4.0",
72
64
  "tree-sitter-wasms": "^0.1.13",
73
65
  "typescript": "^7.0.2",
74
- "vitest": "^4.1.11"
66
+ "vitest": "^4.1.11",
67
+ "@smelt/guard-kit": "0.0.0"
68
+ },
69
+ "scripts": {
70
+ "build": "node ../../scripts/clean-dist.mjs && tsc -p tsconfig.build.json && node ../../scripts/inline-declaration-map-sources.mjs && node ../../scripts/bundle-grammars.mjs",
71
+ "typecheck": "tsc -p tsconfig.json --noEmit",
72
+ "test": "vitest run",
73
+ "test:watch": "vitest",
74
+ "bench": "node bench/run.mjs"
75
75
  }
76
- }
76
+ }
@@ -1,103 +0,0 @@
1
- import type { Strategy } from '../plan/planners.ts';
2
- import type { DetectedLanguage } from '../types.ts';
3
- import type { MapInvocation, SmeltInvocation } from './args.ts';
4
- import type { LoadedConfig } from './config.ts';
5
- /**
6
- * Everything one smelt run needs, fully merged — with a receipt for where each
7
- * merged value came from.
8
- *
9
- * This is the CLI's single merge of flags + config + built-ins. Precedence lives
10
- * here and nowhere else: `resolveRun` is the only code that may look at a flag and a
11
- * config default side by side, so a precedence question is always answered by one
12
- * function instead of by reading two files. `runSmelt` executes this object
13
- * straight-line, without a `??` of its own.
14
- */
15
- export interface ResolvedRun {
16
- readonly budgetBytes: number;
17
- /** Where the budget came from. A missing budget never gets here — it throws. */
18
- readonly budgetSource: 'flag' | 'config';
19
- readonly strategy: Strategy;
20
- readonly strategySource: 'flag' | 'config' | 'builtin';
21
- /**
22
- * The store decision, with `path` already resolved against the config file's
23
- * directory. `'memory'` is the built-in default — a fresh in-memory store per run.
24
- */
25
- readonly store: {
26
- readonly kind: 'memory';
27
- } | {
28
- readonly kind: 'directory';
29
- readonly path: string;
30
- };
31
- /** Path to read. `undefined` means stdin. Flags only; the config has no say. */
32
- readonly file?: string;
33
- readonly focus: readonly string[];
34
- readonly language?: DetectedLanguage;
35
- readonly json: boolean;
36
- }
37
- /**
38
- * Merge one `'smelt'`-mode invocation with the loaded config (or `undefined` when no
39
- * `smelt.config.json` exists) and the built-in defaults.
40
- *
41
- * The precedence is strict and one-directional: an explicit flag always wins over the
42
- * config, and the config only fills what the flags left unsaid. Built-ins fill last,
43
- * and only where a built-in exists at all — the budget deliberately has none, so a run
44
- * with no budget from either source is refused here, in the one module that owns that
45
- * error.
46
- *
47
- * @throws {CliUsageError} when neither `--budget` nor the config names a budget.
48
- */
49
- export declare function resolveRun(invocation: SmeltInvocation, config: LoadedConfig | undefined): ResolvedRun;
50
- /**
51
- * Everything one `smelt map` run needs, fully merged — {@link ResolvedRun}'s sibling,
52
- * not a contortion of it. The two commands share exactly one merged value (the
53
- * budget), so they share the *module* that owns precedence and the budget-required
54
- * refusal, not a struct whose fields would mostly be lies for one of them: a map has
55
- * no store, no strategy, no stdin, and its ignore/cache legs mean nothing to a
56
- * single-blob run.
57
- */
58
- export interface ResolvedMapRun {
59
- readonly budgetBytes: number;
60
- /** Where the budget came from. A missing budget never gets here — it throws. */
61
- readonly budgetSource: 'flag' | 'config';
62
- readonly dir: string;
63
- readonly focus: readonly string[];
64
- /** `undefined` means "use the library's default ignore list". Flags only. */
65
- readonly ignore?: readonly string[];
66
- /** Only when present does the map write to disk. Flags only; the config has no say. */
67
- readonly cacheDir?: string;
68
- readonly json: boolean;
69
- }
70
- /**
71
- * Merge one `'map'`-mode invocation with the loaded config and the built-ins. The
72
- * config contributes exactly what it contributes to a smelt run — `defaultBudgetBytes`,
73
- * a default the user chose explicitly — and nothing else: the store and strategy legs
74
- * are single-blob concerns, and the map ignores them rather than reinterpreting them.
75
- *
76
- * @throws {CliUsageError} when neither `--budget` nor the config names a budget.
77
- */
78
- export declare function resolveMapRun(invocation: MapInvocation, config: LoadedConfig | undefined): ResolvedMapRun;
79
- /**
80
- * Everything `smelt retrieve` and `smelt stats` need, fully merged: the persistent
81
- * store's directory, already resolved against the config file. A third sibling in
82
- * this module for the same reason {@link ResolvedMapRun} is one — these commands
83
- * share the *module* that owns precedence, not a struct: they have no budget, no
84
- * strategy, no file leg at all, only the store leg, which is config-only.
85
- */
86
- export interface ResolvedStoreRun {
87
- /** Absolute path of the directory store, resolved against the config file. */
88
- readonly storePath: string;
89
- }
90
- /**
91
- * The store leg alone, for the two commands whose entire job is the store between
92
- * runs. The refusal is the point: `retrieve` exists so the marker's
93
- * `retrieve("hash")` works from a later shell — cross-run retrieval — and a memory
94
- * store dies with the process that filled it, so with a memory store (or no config
95
- * at all) there is nothing those commands could honestly read. Answering with
96
- * `UnknownHashError` or all-zero stats instead would be the quiet wrong answer this
97
- * project refuses everywhere: the hash *was* elided, the counters *did* move — in a
98
- * store that no longer exists.
99
- *
100
- * @throws {CliUsageError} when no config exists, or the configured store is memory.
101
- */
102
- export declare function resolveStoreRun(command: 'retrieve' | 'stats', config: LoadedConfig | undefined): ResolvedStoreRun;
103
- //# sourceMappingURL=resolve.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../src/cli/resolve.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGpD,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAEhE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;;;;;;GASG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,gFAAgF;IAChF,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,QAAQ,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IACvD;;;OAGG;IACH,QAAQ,CAAC,KAAK,EACZ;QAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;KAAE,GAAG;QAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACtF,gFAAgF;IAChF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IACrC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CACxB,UAAU,EAAE,eAAe,EAC3B,MAAM,EAAE,YAAY,GAAG,SAAS,GAC/B,WAAW,CA8Bb;AAcD;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,gFAAgF;IAChF,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,QAAQ,CAAC;IACzC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,uFAAuF;IACvF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,aAAa,EACzB,MAAM,EAAE,YAAY,GAAG,SAAS,GAC/B,cAAc,CAqBhB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,8EAA8E;IAC9E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,UAAU,GAAG,OAAO,EAC7B,MAAM,EAAE,YAAY,GAAG,SAAS,GAC/B,gBAAgB,CAgBlB"}