@hraness/sponge 0.2.5 → 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 (227) hide show
  1. package/README.md +23 -10
  2. package/bin/sponge.js +8 -7
  3. package/dist/contracts/index.js +24 -6
  4. package/dist/contracts/validate.js +106 -29
  5. package/dist/migrations/008.d.ts +3 -0
  6. package/dist/migrations/008.js +5 -0
  7. package/dist/migrations/009.d.ts +3 -0
  8. package/dist/migrations/009.js +8 -0
  9. package/dist/migrations/010.d.ts +3 -0
  10. package/dist/migrations/010.js +11 -0
  11. package/dist/migrations/index.d.ts +15 -0
  12. package/dist/migrations/index.js +39 -0
  13. package/dist/src/acquisition/capture.js +87 -28
  14. package/dist/src/acquisition/cleanup.d.ts +0 -4
  15. package/dist/src/acquisition/cleanup.js +3 -5
  16. package/dist/src/acquisition/errors.js +17 -2
  17. package/dist/src/acquisition/files.d.ts +0 -1
  18. package/dist/src/acquisition/files.js +14 -11
  19. package/dist/src/acquisition/index.d.ts +2 -3
  20. package/dist/src/acquisition/index.js +1 -2
  21. package/dist/src/acquisition/lock.js +1 -1
  22. package/dist/src/acquisition/normalize.js +57 -18
  23. package/dist/src/acquisition/pins.js +22 -4
  24. package/dist/src/acquisition/process.js +67 -29
  25. package/dist/src/acquisition/runtime.d.ts +5 -7
  26. package/dist/src/acquisition/runtime.js +129 -68
  27. package/dist/src/acquisition/verify-helper.js +10 -3
  28. package/dist/src/annotations/quote.js +40 -12
  29. package/dist/src/annotations/store.d.ts +0 -1
  30. package/dist/src/annotations/store.js +66 -14
  31. package/dist/src/app/article.d.ts +2 -1
  32. package/dist/src/app/article.js +66 -70
  33. package/dist/src/app/bundle-files.d.ts +0 -4
  34. package/dist/src/app/bundle-files.js +11 -14
  35. package/dist/src/app/markdown.js +18 -6
  36. package/dist/src/app/source.d.ts +0 -4
  37. package/dist/src/app/source.js +18 -14
  38. package/dist/src/cli/arguments.d.ts +2 -8
  39. package/dist/src/cli/arguments.js +67 -80
  40. package/dist/src/cli/commands.d.ts +42 -0
  41. package/dist/src/cli/commands.js +795 -0
  42. package/dist/src/cli/config.js +12 -9
  43. package/dist/src/cli/digest.d.ts +1 -1
  44. package/dist/src/cli/digest.js +39 -17
  45. package/dist/src/cli/import.js +25 -4
  46. package/dist/src/cli/indexing.d.ts +2 -0
  47. package/dist/src/cli/indexing.js +26 -9
  48. package/dist/src/cli/main.js +47 -386
  49. package/dist/src/cli/portability.d.ts +0 -2
  50. package/dist/src/cli/portability.js +13 -8
  51. package/dist/src/cli/queue.js +12 -8
  52. package/dist/src/cli/runtimes.d.ts +1 -3
  53. package/dist/src/cli/runtimes.js +13 -6
  54. package/dist/src/cli/terminal.js +1 -1
  55. package/dist/src/cli/worker.d.ts +7 -0
  56. package/dist/src/cli/worker.js +54 -15
  57. package/dist/src/derivations/kinds.d.ts +34 -0
  58. package/dist/src/derivations/kinds.js +14 -0
  59. package/dist/src/derivations/types.d.ts +48 -0
  60. package/dist/src/derivations/types.js +1 -0
  61. package/dist/src/digest/derivation.d.ts +25 -0
  62. package/dist/src/digest/derivation.js +124 -0
  63. package/dist/src/digest/editorial.d.ts +10 -46
  64. package/dist/src/digest/editorial.js +73 -59
  65. package/dist/src/digest/endpoint.js +63 -27
  66. package/dist/src/digest/pipeline.d.ts +53 -32
  67. package/dist/src/digest/pipeline.js +357 -125
  68. package/dist/src/digest/schema.d.ts +44 -17
  69. package/dist/src/digest/schema.js +132 -44
  70. package/dist/src/errors.d.ts +46 -0
  71. package/dist/src/errors.js +117 -0
  72. package/dist/src/identity/query-policy.d.ts +0 -5
  73. package/dist/src/identity/query-policy.js +61 -9
  74. package/dist/src/identity/uri.d.ts +6 -5
  75. package/dist/src/identity/uri.js +37 -18
  76. package/dist/src/identity/visibility.d.ts +11 -1
  77. package/dist/src/identity/visibility.js +81 -17
  78. package/dist/src/importers/apply.js +104 -36
  79. package/dist/src/importers/custody.js +27 -9
  80. package/dist/src/importers/inspect.js +125 -38
  81. package/dist/src/importers/jungle-digest.d.ts +31 -24
  82. package/dist/src/importers/jungle-digest.js +217 -115
  83. package/dist/src/importers/jungle-store.d.ts +20 -11
  84. package/dist/src/importers/jungle-store.js +136 -41
  85. package/dist/src/importers/jungle.d.ts +32 -16
  86. package/dist/src/importers/jungle.js +145 -32
  87. package/dist/src/importers/manifest.js +57 -21
  88. package/dist/src/importers/verify-helper.js +18 -4
  89. package/dist/src/indexing/image.js +29 -10
  90. package/dist/src/indexing/index.d.ts +37 -6
  91. package/dist/src/indexing/index.js +360 -124
  92. package/dist/src/indexing/jobs.js +156 -47
  93. package/dist/src/indexing/prune-run.d.ts +2 -0
  94. package/dist/src/indexing/prune-run.js +69 -26
  95. package/dist/src/indexing/prune.d.ts +12 -3
  96. package/dist/src/indexing/prune.js +85 -35
  97. package/dist/src/indexing/runner.d.ts +2 -1
  98. package/dist/src/indexing/runner.js +83 -40
  99. package/dist/src/indexing/upgrade.d.ts +9 -0
  100. package/dist/src/indexing/upgrade.js +79 -0
  101. package/dist/src/inference/asr/bridge.js +6 -2
  102. package/dist/src/inference/asr/protocol.js +109 -19
  103. package/dist/src/inference/asr/runtime.js +58 -21
  104. package/dist/src/inference/asr/windows.js +9 -5
  105. package/dist/src/inference/embedding/index.d.ts +2 -6
  106. package/dist/src/inference/embedding/index.js +89 -29
  107. package/dist/src/ingest/attempt.d.ts +29 -0
  108. package/dist/src/ingest/attempt.js +55 -0
  109. package/dist/src/media/capture-helper.js +7 -2
  110. package/dist/src/media/capture.d.ts +2 -1
  111. package/dist/src/media/capture.js +124 -43
  112. package/dist/src/media/handler.d.ts +4 -2
  113. package/dist/src/media/handler.js +88 -70
  114. package/dist/src/media/transcribe.js +63 -21
  115. package/dist/src/metadata/article.js +49 -17
  116. package/dist/src/models/artifacts.d.ts +10 -8
  117. package/dist/src/models/artifacts.js +237 -106
  118. package/dist/src/models/catalog.d.ts +2 -5
  119. package/dist/src/models/catalog.js +94 -25
  120. package/dist/src/models/download.js +55 -15
  121. package/dist/src/models/runtime.d.ts +0 -2
  122. package/dist/src/models/runtime.js +69 -33
  123. package/dist/src/pdf/capture-helper.js +3 -1
  124. package/dist/src/pdf/capture.d.ts +2 -1
  125. package/dist/src/pdf/capture.js +95 -27
  126. package/dist/src/pdf/errors.js +12 -1
  127. package/dist/src/pdf/handler.d.ts +2 -1
  128. package/dist/src/pdf/handler.js +70 -71
  129. package/dist/src/pdf/index.d.ts +1 -3
  130. package/dist/src/pdf/index.js +0 -2
  131. package/dist/src/pdf/input.js +12 -7
  132. package/dist/src/pdf/normalize.js +80 -21
  133. package/dist/src/pdf/source-identity.js +3 -2
  134. package/dist/src/pdf/source.js +12 -8
  135. package/dist/src/pdf/verify-helper.js +15 -3
  136. package/dist/src/portability/backup.d.ts +2 -9
  137. package/dist/src/portability/backup.js +108 -22
  138. package/dist/src/portability/database.js +51 -17
  139. package/dist/src/portability/export.js +35 -9
  140. package/dist/src/portability/files.d.ts +1 -4
  141. package/dist/src/portability/files.js +127 -67
  142. package/dist/src/portability/gc.js +65 -22
  143. package/dist/src/publish/component.js +1 -1
  144. package/dist/src/publish/feed.d.ts +2 -2
  145. package/dist/src/publish/feed.js +75 -29
  146. package/dist/src/publish/identity.d.ts +5 -1
  147. package/dist/src/publish/identity.js +31 -11
  148. package/dist/src/publish/index.d.ts +2 -3
  149. package/dist/src/publish/index.js +34 -21
  150. package/dist/src/publish/projection.d.ts +2 -9
  151. package/dist/src/publish/projection.js +21 -14
  152. package/dist/src/search/extension.js +3 -1
  153. package/dist/src/search/hybrid.d.ts +3 -8
  154. package/dist/src/search/hybrid.js +45 -11
  155. package/dist/src/search/lexical.js +18 -4
  156. package/dist/src/search/query.js +56 -25
  157. package/dist/src/search/vector.js +37 -10
  158. package/dist/src/service/launch-agent.d.ts +5 -3
  159. package/dist/src/service/launch-agent.js +272 -83
  160. package/dist/src/service/lifetime.js +47 -14
  161. package/dist/src/service/loop.d.ts +3 -3
  162. package/dist/src/service/loop.js +47 -19
  163. package/dist/src/store/annotations.d.ts +32 -0
  164. package/dist/src/store/annotations.js +44 -0
  165. package/dist/src/store/backups.d.ts +18 -0
  166. package/dist/src/store/backups.js +110 -0
  167. package/dist/src/store/blobs.d.ts +24 -0
  168. package/dist/src/store/blobs.js +139 -0
  169. package/dist/src/store/changes.d.ts +5 -12
  170. package/dist/src/store/changes.js +108 -56
  171. package/dist/src/store/context.d.ts +15 -0
  172. package/dist/src/store/context.js +19 -0
  173. package/dist/src/store/derivations.d.ts +42 -0
  174. package/dist/src/store/derivations.js +104 -0
  175. package/dist/src/store/files.d.ts +0 -2
  176. package/dist/src/store/files.js +19 -24
  177. package/dist/src/store/ids.d.ts +30 -0
  178. package/dist/src/store/ids.js +34 -0
  179. package/dist/src/store/imports.d.ts +26 -0
  180. package/dist/src/store/imports.js +57 -0
  181. package/dist/src/store/index.d.ts +3 -186
  182. package/dist/src/store/index.js +2 -809
  183. package/dist/src/store/lease.d.ts +20 -0
  184. package/dist/src/store/lease.js +62 -0
  185. package/dist/src/store/library.d.ts +75 -0
  186. package/dist/src/store/library.js +174 -0
  187. package/dist/src/store/open.d.ts +7 -0
  188. package/dist/src/store/open.js +183 -0
  189. package/dist/src/store/queue.d.ts +65 -0
  190. package/dist/src/store/queue.js +327 -0
  191. package/dist/src/store/revisions.d.ts +12 -0
  192. package/dist/src/store/revisions.js +189 -0
  193. package/dist/src/store/search-index.d.ts +63 -0
  194. package/dist/src/store/search-index.js +190 -0
  195. package/dist/src/store/sync-ready.d.ts +6 -0
  196. package/dist/src/store/sync-ready.js +121 -0
  197. package/dist/src/store/types.d.ts +1 -5
  198. package/dist/src/store/types.js +1 -8
  199. package/dist/src/util/canonical.d.ts +2 -0
  200. package/dist/src/util/canonical.js +17 -0
  201. package/dist/src/util/exact.d.ts +2 -0
  202. package/dist/src/util/exact.js +9 -0
  203. package/dist/src/util/fsync.d.ts +4 -0
  204. package/dist/src/util/fsync.js +22 -0
  205. package/dist/src/util/hash.d.ts +4 -0
  206. package/dist/src/util/hash.js +8 -0
  207. package/dist/src/worker/basic.d.ts +0 -1
  208. package/dist/src/worker/basic.js +23 -7
  209. package/dist/src/worker/custody.d.ts +4 -1
  210. package/dist/src/worker/custody.js +111 -38
  211. package/dist/src/worker/engine.d.ts +2 -1
  212. package/dist/src/worker/engine.js +42 -20
  213. package/dist/src/worker/identity.js +13 -5
  214. package/dist/src/worker/journal.d.ts +1 -1
  215. package/dist/src/worker/journal.js +54 -24
  216. package/dist/src/worker/lock.d.ts +18 -0
  217. package/dist/src/worker/lock.js +196 -0
  218. package/dist/src/worker/runtime-gc.d.ts +4 -3
  219. package/dist/src/worker/runtime-gc.js +68 -41
  220. package/dist/src/worker/supervisor-child.js +19 -6
  221. package/dist/src/worker/supervisor.js +41 -8
  222. package/native/runtime-manifest.json +4 -4
  223. package/package.json +10 -2
  224. package/dist/src/digest/store.d.ts +0 -57
  225. package/dist/src/digest/store.js +0 -77
  226. package/dist/src/store/foundation.d.ts +0 -7
  227. package/dist/src/store/foundation.js +0 -7
@@ -1,4 +1,4 @@
1
- import { DigestError } from './pipeline.js';
1
+ import { SpongeError } from '../errors.js';
2
2
  /**
3
3
  * A user-started local OpenAI-compatible server (llama.cpp `llama-server`, LM Studio, Ollama). Captured text must not
4
4
  * leave the machine, so only literal loopback HTTP origins are accepted, redirects are refused, and Sponge never starts a server.
@@ -9,17 +9,23 @@ export function loopbackEndpoint(value) {
9
9
  url = new URL(value);
10
10
  }
11
11
  catch {
12
- throw new DigestError('invalid-input');
12
+ throw new SpongeError('invalid-input');
13
13
  }
14
14
  // A hostname such as `localhost` resolves through configuration Sponge does not control; only literal addresses pass.
15
- if (url.protocol !== 'http:' || !['127.0.0.1', '[::1]'].includes(url.hostname) || url.username || url.password || url.search || url.hash || !['', '/', '/v1', '/v1/'].includes(url.pathname))
16
- throw new DigestError('invalid-input');
15
+ if (url.protocol !== 'http:' ||
16
+ !['127.0.0.1', '[::1]'].includes(url.hostname) ||
17
+ url.username ||
18
+ url.password ||
19
+ url.search ||
20
+ url.hash ||
21
+ !['', '/', '/v1', '/v1/'].includes(url.pathname))
22
+ throw new SpongeError('invalid-input');
17
23
  return new URL(url.origin);
18
24
  }
19
25
  async function readCapped(response, max) {
20
26
  const reader = response.body?.getReader(), chunks = [];
21
27
  if (!reader)
22
- throw new DigestError('unavailable');
28
+ throw new SpongeError('unavailable');
23
29
  let size = 0;
24
30
  for (;;) {
25
31
  const { done, value } = await reader.read();
@@ -28,7 +34,7 @@ async function readCapped(response, max) {
28
34
  size += value.byteLength;
29
35
  if (size > max) {
30
36
  await reader.cancel().catch(() => undefined);
31
- throw new DigestError('limit-exceeded');
37
+ throw new SpongeError('limit-exceeded');
32
38
  }
33
39
  chunks.push(value);
34
40
  }
@@ -37,7 +43,9 @@ async function readCapped(response, max) {
37
43
  const label = (v, max = 128) => typeof v === 'string' && v.trim() && v.length <= max && !/[\u0000-\u001f\u007f]/.test(v) ? v.trim() : undefined;
38
44
  /** GGUF quantization from a model filename, e.g. `gemma-4-12b-it-Q4_K_M.gguf` → `Q4_K_M`. */
39
45
  export function quantizationOf(path) {
40
- return /(?:^|[-_.])((?:IQ|Q)\d(?:_[A-Z0-9]+)*|BF16|F16|F32)(?=\.gguf$|[-_.])/i.exec(path.split('/').pop() ?? '')?.[1]?.toUpperCase();
46
+ return /(?:^|[-_.])((?:IQ|Q)\d(?:_[A-Z0-9]+)*|BF16|F16|F32)(?=\.gguf$|[-_.])/i
47
+ .exec(path.split('/').pop() ?? '')?.[1]
48
+ ?.toUpperCase();
41
49
  }
42
50
  /**
43
51
  * Confirm the server lists `model`, read what identity it exposes (llama.cpp `/props`, Ollama `/api/version`),
@@ -46,58 +54,86 @@ export function quantizationOf(path) {
46
54
  export async function connectEndpoint(options) {
47
55
  const origin = loopbackEndpoint(options.endpoint), request = options.fetch ?? fetch, model = label(options.model, 256);
48
56
  if (!model)
49
- throw new DigestError('invalid-input');
57
+ throw new SpongeError('invalid-input');
50
58
  const get = async (path) => {
51
- const signal = AbortSignal.any([options.signal ?? new AbortController().signal, AbortSignal.timeout(options.probeTimeoutMs ?? 10000)]);
59
+ const signal = AbortSignal.any([
60
+ options.signal ?? new AbortController().signal,
61
+ AbortSignal.timeout(options.probeTimeoutMs ?? 10000),
62
+ ]);
52
63
  try {
53
- const r = await request(new URL(path, origin), { signal, redirect: 'error', headers: { accept: 'application/json' } });
64
+ const r = await request(new URL(path, origin), {
65
+ signal,
66
+ redirect: 'error',
67
+ headers: { accept: 'application/json' },
68
+ });
54
69
  return r.ok ? JSON.parse(await readCapped(r, 1 << 20)) : undefined;
55
70
  }
56
71
  catch (error) {
57
72
  if (options.signal?.aborted)
58
- throw new DigestError('cancelled');
59
- if (error instanceof DigestError)
73
+ throw new SpongeError('cancelled');
74
+ if (error instanceof SpongeError)
60
75
  throw error;
61
76
  return undefined;
62
77
  }
63
78
  };
64
- const models = await get('/v1/models');
79
+ const models = (await get('/v1/models'));
65
80
  if (!Array.isArray(models?.data))
66
- throw new DigestError('unavailable');
67
- if (!models.data.some(m => m?.id === model))
68
- throw new DigestError('invalid-input');
69
- const props = await get('/props'), build = label(props?.build_info);
81
+ throw new SpongeError('unavailable');
82
+ if (!models.data.some((m) => m?.id === model))
83
+ throw new SpongeError('invalid-input');
84
+ const props = (await get('/props')), build = label(props?.build_info);
70
85
  let runtime = build ? `llama.cpp ${build}` : undefined;
71
86
  if (!runtime) {
72
87
  const ollama = label((await get('/api/version'))?.version);
73
88
  runtime = ollama ? `ollama ${ollama}` : 'openai-compatible';
74
89
  }
75
- const identity = { model, quantization: (typeof props?.model_path === 'string' && quantizationOf(props.model_path)) || quantizationOf(model) || 'unknown', runtime };
76
- return { identity, generate: (messages, schema, generate) => complete(origin, request, model, messages, schema, generate) };
90
+ const identity = {
91
+ model,
92
+ quantization: (typeof props?.model_path === 'string' && quantizationOf(props.model_path)) || quantizationOf(model) || 'unknown',
93
+ runtime,
94
+ };
95
+ return {
96
+ identity,
97
+ generate: (messages, schema, generate) => complete(origin, request, model, messages, schema, generate),
98
+ };
77
99
  }
78
100
  async function complete(origin, request, model, messages, schema, o) {
79
- const body = JSON.stringify({ model, messages, stream: false, temperature: o.temperature, seed: o.seed, max_tokens: o.maxTokens, response_format: { type: 'json_schema', json_schema: { name: 'sponge_digest', strict: true, schema } } });
80
- const response = await request(new URL('/v1/chat/completions', origin), { method: 'POST', signal: o.signal, redirect: 'error', headers: { 'content-type': 'application/json', accept: 'application/json' }, body });
101
+ const body = JSON.stringify({
102
+ model,
103
+ messages,
104
+ stream: false,
105
+ temperature: o.temperature,
106
+ seed: o.seed,
107
+ max_tokens: o.maxTokens,
108
+ response_format: { type: 'json_schema', json_schema: { name: 'sponge_digest', strict: true, schema } },
109
+ });
110
+ const response = await request(new URL('/v1/chat/completions', origin), {
111
+ method: 'POST',
112
+ signal: o.signal,
113
+ redirect: 'error',
114
+ headers: { 'content-type': 'application/json', accept: 'application/json' },
115
+ body,
116
+ });
81
117
  if (!response.ok) {
82
118
  await response.body?.cancel().catch(() => undefined);
83
- throw new DigestError('unavailable');
119
+ throw new SpongeError('unavailable');
84
120
  }
85
121
  let parsed;
86
122
  try {
87
123
  parsed = JSON.parse(await readCapped(response, o.maxOutputBytes * 2 + 65536));
88
124
  }
89
125
  catch (error) {
90
- if (error instanceof DigestError)
126
+ if (error instanceof SpongeError)
91
127
  throw error;
92
- throw new DigestError('unavailable');
128
+ throw new SpongeError('unavailable');
93
129
  }
94
130
  const choice = parsed?.choices?.[0];
95
131
  if (choice?.finish_reason === 'length')
96
- throw new DigestError('limit-exceeded');
132
+ throw new SpongeError('limit-exceeded');
97
133
  const content = choice?.message?.content;
98
134
  if (typeof content !== 'string' || !content.trim())
99
- throw new DigestError('unavailable');
135
+ throw new SpongeError('unavailable');
100
136
  if (Buffer.byteLength(content) > o.maxOutputBytes)
101
- throw new DigestError('limit-exceeded');
137
+ throw new SpongeError('limit-exceeded');
102
138
  return content;
103
139
  }
@@ -1,11 +1,10 @@
1
- import { forms, type DigestIssue, type DigestOptions, type DigestV1 } from './schema.js';
2
- import type { LibraryStore } from '../store/index.js';
3
- import type { Derivation } from '../store/foundation.js';
1
+ import { forms, type DigestIssue, type DigestOptions, type DigestV2 } from './schema.js';
2
+ import type { Derivation, DigestCandidate } from './derivation.js';
4
3
  /**
5
- * Model-agnostic Digest v1 drafting. The model sees numbered passages and cites a passage id plus a short exact
6
- * span; code snaps, orders, prunes and checks. Nothing leaves this module except through the injected model.
4
+ * Model-agnostic Digest v2 drafting. The model sees numbered passages and cites a passage id plus a short exact
5
+ * span; code snaps, prunes and checks. Nothing leaves this module except through the injected model.
7
6
  */
8
- export declare const PROMPT_VERSION = "sponge-digest-prompt-v1";
7
+ export declare const PROMPT_VERSION = "sponge-digest-prompt-v4";
9
8
  export type ChatMessage = {
10
9
  role: 'system' | 'user' | 'assistant';
11
10
  content: string;
@@ -29,7 +28,7 @@ export interface DigestModel {
29
28
  generate(messages: ChatMessage[], schema: JsonSchema, options: GenerateOptions): Promise<string>;
30
29
  }
31
30
  /** Output-shaping parameters are hashed; time and byte bounds are not, because they only decide whether a run finishes. */
32
- export interface DigestParams {
31
+ interface DigestParams {
33
32
  temperature: number;
34
33
  seed: number;
35
34
  maxTokens: number;
@@ -39,17 +38,7 @@ export interface DigestParams {
39
38
  deadlineMs: number;
40
39
  maxOutputBytes: number;
41
40
  }
42
- /** 24k characters is roughly 6k tokens of passages, leaving room for instructions and output in an 8k context on an 8 GB Mac. */
43
- export declare const defaultDigestParams: DigestParams;
44
- export type DigestErrorCode = 'invalid-input' | 'unavailable' | 'cancelled' | 'limit-exceeded';
45
- export declare class DigestError extends Error {
46
- readonly code: DigestErrorCode;
47
- readonly issues?: DigestIssue[] | undefined;
48
- constructor(code: DigestErrorCode, issues?: DigestIssue[] | undefined);
49
- }
50
- export type Form = typeof forms[number];
51
- /** Quotes under this many words carry no evidence; the checker is gaining the same floor. */
52
- export declare const minQuoteWords = 4;
41
+ export type Form = (typeof forms)[number];
53
42
  export interface Passage {
54
43
  id: number;
55
44
  start: number;
@@ -57,30 +46,29 @@ export interface Passage {
57
46
  heading: string;
58
47
  text: string;
59
48
  }
60
- export interface Highlight {
49
+ interface Highlight {
61
50
  text: string;
62
51
  start?: number;
63
52
  }
64
- /** Sorted-key JSON; the only serialization that enters input hashes. */
53
+ /**
54
+ * Sorted-key JSON; the only serialization that enters input hashes. Deliberately not `util/canonical.ts`: this one
55
+ * accepts finite non-integer numbers (temperature) and writes `undefined` array entries as null, so merging would
56
+ * change digest input hashes.
57
+ */
65
58
  export declare function canonicalJson(value: unknown): string;
66
59
  /** Paragraph-level passages with raw offsets. Headings label passages; code fences and image-only blocks are not quotable. */
67
60
  export declare function passages(markdown: string, maxChars?: number): Passage[];
68
61
  /** Pack passages into sections of at most `budget` display characters, preferring heading boundaries past half a budget. */
69
62
  export declare function sectionsOf(list: Passage[], budget: number): Passage[][];
70
- export declare const quoteBudget: (markdown: string) => number;
71
63
  /** JSON Schema for the model's draft. Tags become an enum when a vocabulary is given. */
72
64
  export declare function draftSchema(passageCount: number, vocabulary?: readonly string[]): JsonSchema;
73
- export declare function notesSchema(first: number, last: number): JsonSchema;
74
- interface Draft {
75
- digest: DigestV1;
76
- cites: (number | undefined)[][];
77
- }
78
65
  export interface QuoteStats {
79
66
  exact: number;
80
67
  folded: number;
81
68
  snapped: number;
82
69
  missing: number;
83
70
  short: number;
71
+ trimmed?: number;
84
72
  }
85
73
  export type QuoteMatch = 'exact' | 'folded' | 'snapped' | 'missing';
86
74
  /**
@@ -94,8 +82,11 @@ export declare function snapQuote(source: string, text: string, within?: {
94
82
  text: string;
95
83
  match: QuoteMatch;
96
84
  };
97
- /** Snap every quote in place and drop quotes under `minQuoteWords`. */
98
- export declare function snapQuotes(draft: Draft, source: string, list: Passage[]): QuoteStats;
85
+ /**
86
+ * A located quote over the word limit keeps its leading words: a prefix of a source span is still verbatim. Trailing
87
+ * clause punctuation goes; unlocated quotes are left for the checker.
88
+ */
89
+ export declare function trimQuote(text: string, max?: number): string;
99
90
  export interface DraftInput {
100
91
  revisionId: string;
101
92
  markdown: string;
@@ -126,7 +117,7 @@ export interface DraftTrace {
126
117
  };
127
118
  }
128
119
  export interface DraftResult {
129
- output: DigestV1;
120
+ output: DigestV2;
130
121
  inputHash: string;
131
122
  producer: Record<string, unknown>;
132
123
  options: DigestOptions;
@@ -136,11 +127,41 @@ export interface DraftResult {
136
127
  export declare function digestInputHash(input: Pick<DraftInput, 'revisionId' | 'markdown' | 'form' | 'highlights' | 'vocabulary' | 'params' | 'supportThreshold' | 'embed'> & {
137
128
  model: ModelIdentity;
138
129
  }): string;
139
- /** Unqualified on real claims: a deliberately low bar that only catches quotes unrelated to their claim. */
140
- export declare const defaultSupportThreshold = 0.35;
141
130
  export declare function draftDigest(input: DraftInput): Promise<DraftResult>;
131
+ /**
132
+ * What `createDigest` needs from a library (a `LibraryStore` satisfies it). Declared here, not imported, so drafting
133
+ * never depends on the store.
134
+ */
135
+ export interface DigestStore {
136
+ read(itemId: string): {
137
+ revisionId: string;
138
+ markdown: string;
139
+ title: string;
140
+ kind: string;
141
+ } | null;
142
+ annotations: {
143
+ list(itemId: string): {
144
+ kind: string;
145
+ revisionId: string | null;
146
+ anchored?: boolean;
147
+ anchor: unknown;
148
+ }[];
149
+ };
150
+ derivations: {
151
+ find(kind: 'digest', revisionId: string, inputHash: string): Derivation | undefined;
152
+ put(kind: 'digest', input: {
153
+ itemId: string;
154
+ revisionId: string;
155
+ producer: Record<string, unknown>;
156
+ } & DigestCandidate): {
157
+ id: string;
158
+ duplicate: boolean;
159
+ };
160
+ read(kind: 'digest', id: string): Derivation | undefined;
161
+ };
162
+ }
142
163
  /** Draft and store a digest for the item's current revision. A matching input hash returns the stored draft without calling the model. */
143
- export declare function createDigest(store: LibraryStore, itemId: string, model: DigestModel, options?: Omit<DraftInput, 'revisionId' | 'markdown' | 'title' | 'form' | 'model' | 'highlights'>): Promise<{
164
+ export declare function createDigest(store: DigestStore, itemId: string, model: DigestModel, options?: Omit<DraftInput, 'revisionId' | 'markdown' | 'title' | 'form' | 'model' | 'highlights'>): Promise<{
144
165
  derivation: Derivation;
145
166
  duplicate: boolean;
146
167
  trace?: DraftTrace;