@gmickel/gno 1.46.0 → 2.1.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 (235) hide show
  1. package/README.md +17 -5
  2. package/THIRD_PARTY_NOTICES.md +46 -0
  3. package/assets/skill/SKILL.md +7 -6
  4. package/assets/spa-production.json.gz +0 -0
  5. package/browser-extension/artifacts/gno-browser-clipper-v2.1.0.zip +0 -0
  6. package/browser-extension/artifacts/gno-browser-clipper-v2.1.0.zip.sha256 +1 -0
  7. package/browser-extension/dist/chunk-4tc9v0ja.js +74 -0
  8. package/browser-extension/dist/manifest.json +1 -1
  9. package/browser-extension/dist/preview.html +1 -1
  10. package/browser-extension/dist/service-worker.js +32 -33
  11. package/bunfig.toml +2 -0
  12. package/package.json +40 -26
  13. package/spec/cli.md +29 -4
  14. package/spec/db/schema.sql +146 -1
  15. package/spec/mcp.md +26 -0
  16. package/src/app/context-runtime-types.ts +3 -0
  17. package/src/app/context-runtime.ts +2 -0
  18. package/src/cli/commands/ask.ts +6 -1
  19. package/src/cli/commands/daemon.ts +21 -8
  20. package/src/cli/commands/embed.ts +77 -41
  21. package/src/cli/detach.ts +3 -2
  22. package/src/config/types.ts +3 -3
  23. package/src/converters/adapters/markitdownTs/adapter.ts +1 -2
  24. package/src/converters/adapters/officeparser/adapter.ts +1 -2
  25. package/src/converters/versions.ts +6 -8
  26. package/src/core/context-evidence.ts +8 -4
  27. package/src/core/job-manager.ts +95 -13
  28. package/src/core/network-boundary-inventory.ts +10 -0
  29. package/src/core/shutdown-budget.ts +45 -0
  30. package/src/embed/backlog.ts +107 -4
  31. package/src/embed/batch.ts +42 -2
  32. package/src/embed/fingerprint.ts +16 -0
  33. package/src/embed/retry.ts +113 -5
  34. package/src/embed/variant-backlog.ts +105 -0
  35. package/src/embed/variant-plan.ts +62 -0
  36. package/src/embed/variant-retry.ts +113 -0
  37. package/src/ingestion/graph-reconciliation.ts +327 -0
  38. package/src/ingestion/sync.ts +9 -272
  39. package/src/llm/http-inference.ts +6 -0
  40. package/src/llm/httpEmbedding.ts +37 -6
  41. package/src/llm/httpGeneration.ts +18 -3
  42. package/src/llm/httpRerank.ts +23 -5
  43. package/src/llm/inference-cancellation.ts +168 -0
  44. package/src/llm/inference-scope.ts +202 -0
  45. package/src/llm/lazy-ports.ts +115 -0
  46. package/src/llm/native-worker/client.ts +541 -0
  47. package/src/llm/native-worker/dispatcher.ts +228 -0
  48. package/src/llm/native-worker/embedding-identity.ts +33 -0
  49. package/src/llm/native-worker/entry.ts +173 -0
  50. package/src/llm/native-worker/errors.ts +32 -0
  51. package/src/llm/native-worker/evaluation.ts +16 -0
  52. package/src/llm/native-worker/owned-exit.ts +108 -0
  53. package/src/llm/native-worker/owner.ts +141 -0
  54. package/src/llm/native-worker/ports.ts +317 -0
  55. package/src/llm/native-worker/protocol.ts +442 -0
  56. package/src/llm/native-worker/runtime-config.ts +92 -0
  57. package/src/llm/nodeLlamaCpp/adapter.ts +77 -20
  58. package/src/llm/nodeLlamaCpp/embedding.ts +130 -46
  59. package/src/llm/nodeLlamaCpp/generation.ts +34 -5
  60. package/src/llm/nodeLlamaCpp/lifecycle-options.ts +99 -0
  61. package/src/llm/nodeLlamaCpp/lifecycle.ts +209 -204
  62. package/src/llm/nodeLlamaCpp/rerank-capacity.ts +111 -0
  63. package/src/llm/nodeLlamaCpp/rerank.ts +118 -27
  64. package/src/llm/nodeLlamaCpp/simulator-handle.ts +73 -0
  65. package/src/llm/nodeLlamaCpp/simulator-install.ts +124 -0
  66. package/src/llm/nodeLlamaCpp/simulator-session.ts +240 -0
  67. package/src/llm/nodeLlamaCpp/simulator-types.ts +80 -0
  68. package/src/llm/types.ts +35 -5
  69. package/src/mcp/context.ts +27 -0
  70. package/src/mcp/http-transport.ts +12 -10
  71. package/src/mcp/server.ts +3 -0
  72. package/src/mcp/tool-profile.ts +30 -8
  73. package/src/mcp/tools/context.ts +8 -11
  74. package/src/mcp/tools/embed.ts +1 -1
  75. package/src/mcp/tools/index-cmd.ts +1 -1
  76. package/src/mcp/tools/index.ts +10 -8
  77. package/src/mcp/tools/query.ts +14 -30
  78. package/src/mcp/tools/vsearch.ts +1 -1
  79. package/src/pipeline/answer.ts +23 -3
  80. package/src/pipeline/claim-verifier.ts +6 -0
  81. package/src/pipeline/expansion.ts +43 -40
  82. package/src/pipeline/explain.ts +6 -2
  83. package/src/pipeline/filters.ts +63 -0
  84. package/src/pipeline/fusion.ts +29 -9
  85. package/src/pipeline/graph-retrieval.ts +29 -9
  86. package/src/pipeline/hybrid.ts +198 -55
  87. package/src/pipeline/hydration.ts +161 -0
  88. package/src/pipeline/owner-fusion.ts +87 -0
  89. package/src/pipeline/rerank.ts +35 -11
  90. package/src/pipeline/search.ts +13 -2
  91. package/src/pipeline/types.ts +5 -3
  92. package/src/pipeline/vsearch.ts +87 -7
  93. package/src/sdk/client.ts +47 -3
  94. package/src/sdk/embed.ts +63 -39
  95. package/src/serve/background-runtime.ts +1 -1
  96. package/src/serve/context.ts +41 -56
  97. package/src/serve/embed-scheduler.ts +58 -35
  98. package/src/serve/public/components/IndexingProgress.tsx +46 -60
  99. package/src/serve/public/components/PublishExportDialog.tsx +266 -0
  100. package/src/serve/public/globals.built.css +1 -1
  101. package/src/serve/public/globals.css +35 -0
  102. package/src/serve/public/lib/publish-export.ts +81 -1
  103. package/src/serve/public/lib/shiki-language-ids.ts +14 -0
  104. package/src/serve/public/pages/Collections.tsx +12 -46
  105. package/src/serve/public/pages/DocView.tsx +14 -52
  106. package/src/serve/resident-admission.ts +36 -36
  107. package/src/serve/resident-background-work.ts +20 -2
  108. package/src/serve/resident-request.ts +11 -5
  109. package/src/serve/resident-runtime.ts +97 -61
  110. package/src/serve/resident-shutdown.ts +153 -0
  111. package/src/serve/routes/api.ts +3 -1
  112. package/src/serve/server.ts +47 -26
  113. package/src/store/migrations/028-vector-variants.ts +54 -0
  114. package/src/store/migrations/029-graph-reference-state.ts +77 -0
  115. package/src/store/migrations/index.ts +4 -0
  116. package/src/store/sqlite/adapter.ts +251 -183
  117. package/src/store/sqlite/eligibility.ts +174 -0
  118. package/src/store/sqlite/graph-edge-application.ts +66 -0
  119. package/src/store/sqlite/graph-reference-state.ts +194 -0
  120. package/src/store/sqlite/legacy-vector-ownership.ts +79 -0
  121. package/src/store/types.ts +80 -12
  122. package/src/store/vector/eligibility.ts +36 -0
  123. package/src/store/vector/freshness.ts +33 -6
  124. package/src/store/vector/lazy.ts +81 -0
  125. package/src/store/vector/sqlite-vec.ts +106 -54
  126. package/src/store/vector/stats.ts +14 -3
  127. package/src/store/vector/types.ts +35 -2
  128. package/src/store/vector/variant-search.ts +192 -0
  129. package/src/store/vector/variants.ts +451 -0
  130. package/vendor/converters/markitdown-ts/LICENSE +21 -0
  131. package/vendor/converters/markitdown-ts/dist/index.cjs +1180 -0
  132. package/vendor/converters/markitdown-ts/dist/index.d.cts +46 -0
  133. package/vendor/converters/markitdown-ts/dist/index.d.mts +46 -0
  134. package/vendor/converters/markitdown-ts/dist/index.d.ts +46 -0
  135. package/vendor/converters/markitdown-ts/dist/index.mjs +1152 -0
  136. package/vendor/converters/markitdown-ts/package.json +77 -0
  137. package/vendor/converters/officeparser/LICENSE +21 -0
  138. package/vendor/converters/officeparser/dist/OfficeConverter.d.ts +47 -0
  139. package/vendor/converters/officeparser/dist/OfficeConverter.js +76 -0
  140. package/vendor/converters/officeparser/dist/OfficeGenerator.d.ts +23 -0
  141. package/vendor/converters/officeparser/dist/OfficeGenerator.js +73 -0
  142. package/vendor/converters/officeparser/dist/OfficeParser.d.ts +106 -0
  143. package/vendor/converters/officeparser/dist/OfficeParser.js +332 -0
  144. package/vendor/converters/officeparser/dist/cli.d.ts +28 -0
  145. package/vendor/converters/officeparser/dist/cli.js +381 -0
  146. package/vendor/converters/officeparser/dist/defaults.d.ts +41 -0
  147. package/vendor/converters/officeparser/dist/defaults.js +218 -0
  148. package/vendor/converters/officeparser/dist/generators/BaseGenerator.d.ts +107 -0
  149. package/vendor/converters/officeparser/dist/generators/BaseGenerator.js +248 -0
  150. package/vendor/converters/officeparser/dist/generators/ChunkingGenerator.d.ts +82 -0
  151. package/vendor/converters/officeparser/dist/generators/ChunkingGenerator.js +797 -0
  152. package/vendor/converters/officeparser/dist/generators/CsvGenerator.d.ts +38 -0
  153. package/vendor/converters/officeparser/dist/generators/CsvGenerator.js +245 -0
  154. package/vendor/converters/officeparser/dist/generators/EpubGenerator.d.ts +43 -0
  155. package/vendor/converters/officeparser/dist/generators/EpubGenerator.js +315 -0
  156. package/vendor/converters/officeparser/dist/generators/HtmlGenerator.d.ts +59 -0
  157. package/vendor/converters/officeparser/dist/generators/HtmlGenerator.js +1942 -0
  158. package/vendor/converters/officeparser/dist/generators/MarkdownGenerator.d.ts +96 -0
  159. package/vendor/converters/officeparser/dist/generators/MarkdownGenerator.js +1175 -0
  160. package/vendor/converters/officeparser/dist/generators/PdfGenerator.d.ts +22 -0
  161. package/vendor/converters/officeparser/dist/generators/PdfGenerator.js +194 -0
  162. package/vendor/converters/officeparser/dist/generators/RtfGenerator.d.ts +29 -0
  163. package/vendor/converters/officeparser/dist/generators/RtfGenerator.js +316 -0
  164. package/vendor/converters/officeparser/dist/generators/TextGenerator.d.ts +13 -0
  165. package/vendor/converters/officeparser/dist/generators/TextGenerator.js +201 -0
  166. package/vendor/converters/officeparser/dist/index.d.ts +60 -0
  167. package/vendor/converters/officeparser/dist/index.js +72 -0
  168. package/vendor/converters/officeparser/dist/index.mjs +18 -0
  169. package/vendor/converters/officeparser/dist/officeparser.browser.d.ts +2621 -0
  170. package/vendor/converters/officeparser/dist/officeparser.browser.iife.js +1336 -0
  171. package/vendor/converters/officeparser/dist/officeparser.browser.mjs +1335 -0
  172. package/vendor/converters/officeparser/dist/officeparser.browser.slim.d.ts +2621 -0
  173. package/vendor/converters/officeparser/dist/officeparser.browser.slim.iife.js +1336 -0
  174. package/vendor/converters/officeparser/dist/officeparser.browser.slim.mjs +1335 -0
  175. package/vendor/converters/officeparser/dist/parsers/CsvParser.d.ts +9 -0
  176. package/vendor/converters/officeparser/dist/parsers/CsvParser.js +115 -0
  177. package/vendor/converters/officeparser/dist/parsers/EpubParser.d.ts +8 -0
  178. package/vendor/converters/officeparser/dist/parsers/EpubParser.js +217 -0
  179. package/vendor/converters/officeparser/dist/parsers/ExcelParser.d.ts +32 -0
  180. package/vendor/converters/officeparser/dist/parsers/ExcelParser.js +736 -0
  181. package/vendor/converters/officeparser/dist/parsers/HtmlParser.d.ts +2 -0
  182. package/vendor/converters/officeparser/dist/parsers/HtmlParser.js +1287 -0
  183. package/vendor/converters/officeparser/dist/parsers/MarkdownParser.d.ts +2 -0
  184. package/vendor/converters/officeparser/dist/parsers/MarkdownParser.js +1272 -0
  185. package/vendor/converters/officeparser/dist/parsers/OpenOfficeParser.d.ts +31 -0
  186. package/vendor/converters/officeparser/dist/parsers/OpenOfficeParser.js +1819 -0
  187. package/vendor/converters/officeparser/dist/parsers/PdfParser.d.ts +67 -0
  188. package/vendor/converters/officeparser/dist/parsers/PdfParser.js +848 -0
  189. package/vendor/converters/officeparser/dist/parsers/PowerPointParser.d.ts +32 -0
  190. package/vendor/converters/officeparser/dist/parsers/PowerPointParser.js +950 -0
  191. package/vendor/converters/officeparser/dist/parsers/RtfParser.d.ts +187 -0
  192. package/vendor/converters/officeparser/dist/parsers/RtfParser.js +1801 -0
  193. package/vendor/converters/officeparser/dist/parsers/WordParser.d.ts +79 -0
  194. package/vendor/converters/officeparser/dist/parsers/WordParser.js +1177 -0
  195. package/vendor/converters/officeparser/dist/sbom.cdx.json +1763 -0
  196. package/vendor/converters/officeparser/dist/types.d.ts +2507 -0
  197. package/vendor/converters/officeparser/dist/types.js +107 -0
  198. package/vendor/converters/officeparser/dist/utils/astUtils.d.ts +16 -0
  199. package/vendor/converters/officeparser/dist/utils/astUtils.js +33 -0
  200. package/vendor/converters/officeparser/dist/utils/chartUtils.d.ts +6 -0
  201. package/vendor/converters/officeparser/dist/utils/chartUtils.js +257 -0
  202. package/vendor/converters/officeparser/dist/utils/configUtils.d.ts +44 -0
  203. package/vendor/converters/officeparser/dist/utils/configUtils.js +315 -0
  204. package/vendor/converters/officeparser/dist/utils/dateUtils.d.ts +17 -0
  205. package/vendor/converters/officeparser/dist/utils/dateUtils.js +69 -0
  206. package/vendor/converters/officeparser/dist/utils/envUtils.d.ts +29 -0
  207. package/vendor/converters/officeparser/dist/utils/envUtils.js +152 -0
  208. package/vendor/converters/officeparser/dist/utils/errorUtils.d.ts +72 -0
  209. package/vendor/converters/officeparser/dist/utils/errorUtils.js +245 -0
  210. package/vendor/converters/officeparser/dist/utils/imageUtils.d.ts +66 -0
  211. package/vendor/converters/officeparser/dist/utils/imageUtils.js +133 -0
  212. package/vendor/converters/officeparser/dist/utils/mathUtils.d.ts +42 -0
  213. package/vendor/converters/officeparser/dist/utils/mathUtils.js +385 -0
  214. package/vendor/converters/officeparser/dist/utils/moduleLoader.d.ts +18 -0
  215. package/vendor/converters/officeparser/dist/utils/moduleLoader.js +106 -0
  216. package/vendor/converters/officeparser/dist/utils/ocrUtils.d.ts +42 -0
  217. package/vendor/converters/officeparser/dist/utils/ocrUtils.js +428 -0
  218. package/vendor/converters/officeparser/dist/utils/sanitize.d.ts +148 -0
  219. package/vendor/converters/officeparser/dist/utils/sanitize.js +344 -0
  220. package/vendor/converters/officeparser/dist/utils/sheetUtils.d.ts +7 -0
  221. package/vendor/converters/officeparser/dist/utils/sheetUtils.js +35 -0
  222. package/vendor/converters/officeparser/dist/utils/styleMapper.d.ts +36 -0
  223. package/vendor/converters/officeparser/dist/utils/styleMapper.js +224 -0
  224. package/vendor/converters/officeparser/dist/utils/xmlUtils.d.ts +163 -0
  225. package/vendor/converters/officeparser/dist/utils/xmlUtils.js +461 -0
  226. package/vendor/converters/officeparser/dist/utils/zipUtils.d.ts +134 -0
  227. package/vendor/converters/officeparser/dist/utils/zipUtils.js +337 -0
  228. package/vendor/converters/officeparser/package.json +147 -0
  229. package/vendor/converters/upstream-manifest.json +124 -0
  230. package/vendor/dependency-fixes/README.md +77 -0
  231. package/vendor/dependency-fixes/vendor-converters.py +83 -0
  232. package/browser-extension/artifacts/gno-browser-clipper-v1.46.0.zip +0 -0
  233. package/browser-extension/artifacts/gno-browser-clipper-v1.46.0.zip.sha256 +0 -1
  234. package/browser-extension/dist/chunk-627emwpj.js +0 -75
  235. /package/browser-extension/dist/{chunk-ydfx5d7p.css → chunk-z74y8n8c.css} +0 -0
@@ -22,6 +22,7 @@ import {
22
22
  type WriteLeaseContention,
23
23
  withCliWriteLease,
24
24
  } from "../../core/write-lease";
25
+ import { embedBacklog, prepareEmbeddingBacklog } from "../../embed/backlog";
25
26
  import { getEmbeddingFingerprint } from "../../embed/fingerprint";
26
27
  import {
27
28
  addUniqueSamples,
@@ -38,6 +39,7 @@ import {
38
39
  markIndexStageRunning,
39
40
  readIndexStageState,
40
41
  } from "../../embed/stage-state";
42
+ import { countVariantBacklog } from "../../embed/variant-plan";
41
43
  import { LlmAdapter } from "../../llm/nodeLlamaCpp/adapter";
42
44
  import { resolveDownloadPolicy } from "../../llm/policy";
43
45
  import { resolveModelUri } from "../../llm/registry";
@@ -139,18 +141,6 @@ function isDisposedBatchError(message: string): boolean {
139
141
  return message.toLowerCase().includes("object is disposed");
140
142
  }
141
143
 
142
- async function checkVecAvailable(
143
- db: import("bun:sqlite").Database
144
- ): Promise<boolean> {
145
- try {
146
- const sqliteVec = await import("sqlite-vec");
147
- sqliteVec.load(db);
148
- return true;
149
- } catch {
150
- return false;
151
- }
152
- }
153
-
154
144
  interface BatchContext {
155
145
  db: import("bun:sqlite").Database;
156
146
  stats: VectorStatsPort;
@@ -506,28 +496,6 @@ export async function embed(options: EmbedOptions = {}): Promise<EmbedResult> {
506
496
  const stats: VectorStatsPort = createVectorStatsPort(db);
507
497
 
508
498
  let totalToEmbed = 0;
509
- if (force) {
510
- const forceCount = await getActiveChunkCount(db, options.collection);
511
- if (!forceCount.ok) {
512
- return { success: false, error: forceCount.error.message };
513
- }
514
- totalToEmbed = forceCount.value;
515
-
516
- if (totalToEmbed === 0 || dryRun) {
517
- const vecAvailable = await checkVecAvailable(db);
518
- return {
519
- success: true,
520
- embedded: totalToEmbed,
521
- errors: 0,
522
- contentionErrors: 0,
523
- duration: 0,
524
- model: modelUri,
525
- searchAvailable: vecAvailable,
526
- errorSamples: [],
527
- };
528
- }
529
- }
530
-
531
499
  // Create LLM adapter and embedding port with auto-download
532
500
  let offline = options.offline;
533
501
  if (offline === undefined) {
@@ -585,12 +553,16 @@ export async function embed(options: EmbedOptions = {}): Promise<EmbedResult> {
585
553
  process.stderr.write("\n");
586
554
  }
587
555
 
588
- // Discover dimensions via probe embedding
589
- const probeResult = await embedPort.embed("dimension probe");
590
- if (!probeResult.ok) {
591
- return { success: false, error: probeResult.error.message };
556
+ const initializedPort = await embedPort.init();
557
+ if (!initializedPort.ok)
558
+ return { success: false, error: initializedPort.error.message };
559
+ let dimensions = embedPort.dimensions();
560
+ if (!embedPort.getIdentity?.()) {
561
+ const probeResult = await embedPort.embed("dimension probe");
562
+ if (!probeResult.ok)
563
+ return { success: false, error: probeResult.error.message };
564
+ dimensions = probeResult.value.length;
592
565
  }
593
- const dimensions = probeResult.value.length;
594
566
 
595
567
  // Create vector index port
596
568
  const vectorResult = await createVectorIndexPort(db, {
@@ -602,6 +574,70 @@ export async function embed(options: EmbedOptions = {}): Promise<EmbedResult> {
602
574
  }
603
575
  vectorIndex = vectorResult.value;
604
576
 
577
+ const prepared = await prepareEmbeddingBacklog({
578
+ statsPort: stats,
579
+ embedPort,
580
+ vectorIndex,
581
+ modelUri,
582
+ collection: options.collection,
583
+ batchSize,
584
+ force,
585
+ });
586
+ if (!prepared.ok)
587
+ return { success: false, error: prepared.error.message };
588
+ if (prepared.value.variantStore) {
589
+ totalToEmbed = countVariantBacklog(prepared.value);
590
+ const startedAt = Date.now();
591
+ if (dryRun)
592
+ return {
593
+ success: true,
594
+ embedded: totalToEmbed,
595
+ errors: 0,
596
+ contentionErrors: 0,
597
+ duration: 0,
598
+ model: modelUri,
599
+ searchAvailable: prepared.value.variantStore.searchAvailable,
600
+ errorSamples: [],
601
+ };
602
+ const processed = await embedBacklog({
603
+ ...prepared.value,
604
+ onProgress: !options.json
605
+ ? (embedded, errors) =>
606
+ process.stderr.write(
607
+ `\rEmbedded ${embedded}/${totalToEmbed} owners; ${errors} errors`
608
+ )
609
+ : undefined,
610
+ });
611
+ if (!options.json) process.stderr.write("\n");
612
+ if (!processed.ok)
613
+ return { success: false, error: processed.error.message };
614
+ return {
615
+ success: true,
616
+ ...processed.value,
617
+ contentionErrors: processed.value.contentionErrors ?? 0,
618
+ duration: (Date.now() - startedAt) / 1000,
619
+ model: modelUri,
620
+ searchAvailable: prepared.value.variantStore.searchAvailable,
621
+ errorSamples: [],
622
+ };
623
+ }
624
+ if (force) {
625
+ const count = await getActiveChunkCount(db, options.collection);
626
+ if (!count.ok) return { success: false, error: count.error.message };
627
+ totalToEmbed = count.value;
628
+ if (dryRun || !totalToEmbed)
629
+ return {
630
+ success: true,
631
+ embedded: totalToEmbed,
632
+ errors: 0,
633
+ contentionErrors: 0,
634
+ duration: 0,
635
+ model: modelUri,
636
+ searchAvailable: vectorIndex.searchAvailable,
637
+ errorSamples: [],
638
+ };
639
+ }
640
+
605
641
  if (!force) {
606
642
  const embedFingerprint = getEmbeddingFingerprint({
607
643
  modelUri,
@@ -773,7 +809,7 @@ function getActiveChunks(
773
809
  const sql = after
774
810
  ? `
775
811
  SELECT c.mirror_hash as mirrorHash, c.seq, c.text,
776
- (SELECT d.title FROM documents d WHERE d.mirror_hash = c.mirror_hash AND d.active = 1 LIMIT 1) as title,
812
+ (SELECT d.title FROM documents d WHERE d.mirror_hash = c.mirror_hash AND d.active = 1 ORDER BY d.id LIMIT 1) as title,
777
813
  'force' as reason
778
814
  FROM content_chunks c
779
815
  WHERE EXISTS (
@@ -786,7 +822,7 @@ function getActiveChunks(
786
822
  `
787
823
  : `
788
824
  SELECT c.mirror_hash as mirrorHash, c.seq, c.text,
789
- (SELECT d.title FROM documents d WHERE d.mirror_hash = c.mirror_hash AND d.active = 1 LIMIT 1) as title,
825
+ (SELECT d.title FROM documents d WHERE d.mirror_hash = c.mirror_hash AND d.active = 1 ORDER BY d.id LIMIT 1) as title,
790
826
  'force' as reason
791
827
  FROM content_chunks c
792
828
  WHERE EXISTS (
package/src/cli/detach.ts CHANGED
@@ -25,6 +25,7 @@ import type { ResidentStatus } from "../serve/status-model";
25
25
  import { VERSION, resolveDirs } from "../app/constants";
26
26
  import { toAbsolutePath } from "../config/paths";
27
27
  import { atomicWrite } from "../core/file-ops";
28
+ import { RESIDENT_STOP_GRACE_MS } from "../core/shutdown-budget";
28
29
  import { isResidentStatus } from "../serve/resident-status";
29
30
  import { CliError } from "./errors";
30
31
 
@@ -877,7 +878,7 @@ export async function inspectForeignLive(options: {
877
878
  export interface StopOptions {
878
879
  kind: DetachKind;
879
880
  pidFile: string;
880
- /** Grace period for SIGTERM before we escalate to SIGKILL. Default 10s. */
881
+ /** Grace period for SIGTERM before we escalate to SIGKILL. Default 12s. */
881
882
  timeoutMs?: number;
882
883
  /** Poll interval while waiting for the process to exit. Default 100ms. */
883
884
  pollIntervalMs?: number;
@@ -921,7 +922,7 @@ async function waitForExit(
921
922
  * unlinks stale pid-files it discovers on entry.
922
923
  */
923
924
  export async function stopProcess(options: StopOptions): Promise<StopOutcome> {
924
- const timeoutMs = options.timeoutMs ?? 10_000;
925
+ const timeoutMs = options.timeoutMs ?? RESIDENT_STOP_GRACE_MS;
925
926
  const pollIntervalMs = options.pollIntervalMs ?? 100;
926
927
  const killTimeoutMs = options.killTimeoutMs ?? 2_000;
927
928
  const sleep = options.sleep ?? defaultSleep;
@@ -425,9 +425,9 @@ export const ModelConfigSchema = z.object({
425
425
  /** Model presets */
426
426
  presets: z.array(ModelPresetSchema).default(DEFAULT_MODEL_PRESETS),
427
427
  /** Model load timeout in ms */
428
- loadTimeout: z.number().default(60_000),
429
- /** Inference timeout in ms */
430
- inferenceTimeout: z.number().default(30_000),
428
+ loadTimeout: z.number().int().min(1).max(2_147_483_647).default(60_000),
429
+ /** Inference timeout in ms, measured from native evaluation start. */
430
+ inferenceTimeout: z.number().int().min(1).max(2_147_483_647).default(30_000),
431
431
  /** Context size used for query expansion generation */
432
432
  expandContextSize: z.number().int().min(256).default(2_048),
433
433
  /** Keep warm model TTL in ms (5 min) */
@@ -3,8 +3,6 @@
3
3
  * Uses convertBuffer() with bytes for determinism.
4
4
  */
5
5
 
6
- import { MarkItDown } from "markitdown-ts";
7
-
8
6
  import type {
9
7
  Converter,
10
8
  ConvertInput,
@@ -12,6 +10,7 @@ import type {
12
10
  ConvertWarning,
13
11
  } from "../../types";
14
12
 
13
+ import { MarkItDown } from "../../../../vendor/converters/markitdown-ts";
15
14
  import {
16
15
  adapterError,
17
16
  corruptError,
@@ -3,8 +3,6 @@
3
3
  * Uses parseOffice() v7 API with Buffer for in-memory extraction.
4
4
  */
5
5
 
6
- import { parseOffice } from "officeparser";
7
-
8
6
  import type {
9
7
  Converter,
10
8
  ConvertInput,
@@ -12,6 +10,7 @@ import type {
12
10
  ConvertWarning,
13
11
  } from "../../types";
14
12
 
13
+ import { parseOffice } from "../../../../vendor/converters/officeparser";
15
14
  import { adapterError, corruptError, tooLargeError } from "../../errors";
16
15
  import { basenameWithoutExt } from "../../path";
17
16
  import { ADAPTER_VERSIONS } from "../../versions";
@@ -2,10 +2,9 @@
2
2
  * Centralized converter version tracking.
3
3
  *
4
4
  * Native converters use our own versioning.
5
- * Adapter versions MUST match the wrapped npm package version.
6
- *
7
- * When updating npm dependencies, update these versions too.
8
- * Run `bun pm ls markitdown-ts officeparser` to check current versions.
5
+ * Adapter versions identify upstream code and security-sensitive parser versions.
6
+ * Update them when changing the vendored distributions or parser dependencies.
7
+ * Upstream identities live in vendor/converters/upstream-manifest.json.
9
8
  */
10
9
 
11
10
  /** Native converter versions (our own) */
@@ -15,10 +14,9 @@ export const NATIVE_VERSIONS = {
15
14
  } as const;
16
15
 
17
16
  /**
18
- * Adapter versions - MUST match npm package versions.
19
- * Update these when running `bun update`.
17
+ * Include repaired parser versions so existing converted content is invalidated.
20
18
  */
21
19
  export const ADAPTER_VERSIONS = {
22
- "markitdown-ts": "0.0.8",
23
- officeparser: "6.0.4",
20
+ "markitdown-ts": "0.0.10+xlsx.0.20.3",
21
+ officeparser: "7.8.0+pdfjs.6.3.289",
24
22
  } as const;
@@ -1,6 +1,7 @@
1
1
  /** Strict indexed-evidence loading for Context Capsule compilation. */
2
2
 
3
3
  import type { EgressPolicy } from "../config/types";
4
+ import type { RequestHydration } from "../pipeline/hydration";
4
5
  import type { SearchResults } from "../pipeline/types";
5
6
  import type {
6
7
  ActivationIndexSnapshot,
@@ -127,6 +128,7 @@ export interface ContextEvidenceProjectionContext {
127
128
 
128
129
  export interface ContextEvidenceCompilerDeps<P> {
129
130
  store: ContextEvidenceStore;
131
+ hydration?: Pick<RequestHydration, "getContentBatch" | "getChunksBatch">;
130
132
  retrieve: (request: ContextRetrievalRequest) => Promise<SearchResults>;
131
133
  projectCanonical: (
132
134
  draft: ContextCanonicalPlanDraft<ContextEvidenceValue>,
@@ -329,7 +331,8 @@ const referenceDocument = (
329
331
  export const materializeContextEvidenceCandidates = async (
330
332
  store: ContextEvidenceStore,
331
333
  candidates: ContextRetrievalCandidate[],
332
- indexNameInput: string
334
+ indexNameInput: string,
335
+ hydration?: ContextEvidenceCompilerDeps<unknown>["hydration"]
333
336
  ): Promise<ContextMaterialization<ContextEvidenceValue>[]> => {
334
337
  if (candidates.length === 0) return [];
335
338
  const indexName = canonicalizeIndexName(indexNameInput);
@@ -363,8 +366,8 @@ export const materializeContextEvidenceCandidates = async (
363
366
  ),
364
367
  ];
365
368
  const [contentResult, chunksResult] = await Promise.all([
366
- store.getContentBatch(mirrorHashes),
367
- store.getChunksBatch(mirrorHashes),
369
+ (hydration ?? store).getContentBatch(mirrorHashes),
370
+ (hydration ?? store).getChunksBatch(mirrorHashes),
368
371
  ]);
369
372
  const contentByHash = unwrapStore(
370
373
  contentResult,
@@ -517,7 +520,8 @@ export const compileContextEvidence = async <P>(
517
520
  materializeContextEvidenceCandidates(
518
521
  deps.store,
519
522
  candidates,
520
- input.indexName
523
+ input.indexName,
524
+ deps.hydration
521
525
  ),
522
526
  projectCanonical: (draft) => deps.projectCanonical(draft, fingerprints),
523
527
  }
@@ -1,11 +1,15 @@
1
+ import type { SyncResult } from "../ingestion";
1
2
  /**
2
3
  * Background job manager for MCP write operations.
3
4
  *
4
5
  * @module src/core/job-manager
5
6
  */
6
7
 
7
- import type { SyncResult } from "../ingestion";
8
-
8
+ import {
9
+ assertInferenceActive,
10
+ withOwnedInferenceScope,
11
+ withBackgroundInference,
12
+ } from "../llm/inference-scope";
9
13
  import { MCP_ERRORS } from "./errors";
10
14
  import { acquireWriteLock, type WriteLockHandle } from "./file-lock";
11
15
 
@@ -78,7 +82,10 @@ export class JobManager {
78
82
  #jobs = new Map<string, JobRecord>();
79
83
  #jobAuthorizationEpochs = new Map<string, string>();
80
84
  #activeJobs = new Set<Promise<void>>();
85
+ #jobControllers = new Map<string, AbortController>();
81
86
  #authorizationEpoch = "egress-epoch-uninitialized";
87
+ #accepting = true;
88
+ #releaseJobLocks = new Map<string, () => Promise<void>>();
82
89
 
83
90
  constructor(options: JobManagerOptions) {
84
91
  this.#lockPath = options.lockPath;
@@ -89,8 +96,10 @@ export class JobManager {
89
96
 
90
97
  async startJob(
91
98
  type: JobType,
92
- fn: () => Promise<SyncResult>
99
+ fn: (signal: AbortSignal) => Promise<SyncResult>
93
100
  ): Promise<string> {
101
+ assertInferenceActive();
102
+ this.#assertAccepting();
94
103
  this.#cleanupExpiredJobs();
95
104
 
96
105
  if (this.#activeJobId) {
@@ -105,14 +114,21 @@ export class JobManager {
105
114
  throw new JobError("LOCKED", MCP_ERRORS.LOCKED.message);
106
115
  }
107
116
 
117
+ if (!this.#accepting) {
118
+ await lock.release();
119
+ this.#assertAccepting();
120
+ }
121
+
108
122
  return this.#startJobWithLock(type, fn, lock);
109
123
  }
110
124
 
111
125
  async startJobWithLock(
112
126
  type: JobType,
113
127
  lock: WriteLockHandle,
114
- fn: () => Promise<SyncResult>
128
+ fn: (signal: AbortSignal) => Promise<SyncResult>
115
129
  ): Promise<string> {
130
+ assertInferenceActive();
131
+ this.#assertAccepting();
116
132
  this.#cleanupExpiredJobs();
117
133
 
118
134
  if (this.#activeJobId) {
@@ -132,8 +148,10 @@ export class JobManager {
132
148
  async startTypedJobWithLock(
133
149
  type: JobType,
134
150
  lock: WriteLockHandle,
135
- fn: () => Promise<JobResult>
151
+ fn: (signal: AbortSignal) => Promise<JobResult>
136
152
  ): Promise<string> {
153
+ assertInferenceActive();
154
+ this.#assertAccepting();
137
155
  this.#cleanupExpiredJobs();
138
156
 
139
157
  if (this.#activeJobId) {
@@ -205,10 +223,56 @@ export class JobManager {
205
223
  return { active, recent };
206
224
  }
207
225
 
226
+ /** Internal owner control; cancellation retains the existing failed job schema. */
227
+ cancelJob(jobId: string): boolean {
228
+ const controller = this.#jobControllers.get(jobId);
229
+ if (!controller) return false;
230
+ controller.abort();
231
+ return true;
232
+ }
233
+
208
234
  async shutdown(): Promise<void> {
235
+ this.stop();
209
236
  await Promise.allSettled(this.#activeJobs);
210
237
  }
211
238
 
239
+ stop(): void {
240
+ this.#accepting = false;
241
+ }
242
+
243
+ cancel(): void {
244
+ this.stop();
245
+ for (const controller of this.#jobControllers.values()) controller.abort();
246
+ }
247
+
248
+ /** Call only after the shared shutdown budget; late callbacks stay canceled. */
249
+ async failUnfinished(): Promise<void> {
250
+ this.cancel();
251
+ for (const job of this.#jobs.values()) {
252
+ if (job.status !== "running") continue;
253
+ job.status = "failed";
254
+ job.error = "Resident shutdown deadline exceeded";
255
+ job.completedAt = Date.now();
256
+ }
257
+ await Promise.allSettled(
258
+ Array.from(this.#releaseJobLocks.values(), (release) => release())
259
+ );
260
+ }
261
+
262
+ #assertAccepting(): void {
263
+ if (!this.#accepting) throw new Error("Resident runtime is shutting down");
264
+ }
265
+
266
+ #ownLock(jobId: string, lock: WriteLockHandle): WriteLockHandle {
267
+ let releasing: Promise<void> | undefined;
268
+ const owned = {
269
+ release: () =>
270
+ (releasing ??= Promise.resolve().then(() => lock.release())),
271
+ };
272
+ this.#releaseJobLocks.set(jobId, owned.release);
273
+ return owned;
274
+ }
275
+
212
276
  #track(jobPromise: Promise<void>): void {
213
277
  const tracked = jobPromise.catch(() => undefined);
214
278
  this.#activeJobs.add(tracked);
@@ -219,14 +283,16 @@ export class JobManager {
219
283
 
220
284
  async #runJob(
221
285
  job: JobRecord,
222
- fn: () => Promise<SyncResult>,
286
+ fn: (signal: AbortSignal) => Promise<SyncResult>,
223
287
  lock: { release: () => Promise<void> }
224
288
  ): Promise<void> {
225
289
  try {
226
290
  const release = await this.#toolMutex.acquire();
227
291
  try {
228
292
  this.#assertAuthorizationEpoch(job);
229
- const result = await fn();
293
+ assertInferenceActive();
294
+ const result = await fn(this.#jobControllers.get(job.id)!.signal);
295
+ assertInferenceActive();
230
296
  job.status = "completed";
231
297
  job.result = result;
232
298
  } catch (e) {
@@ -239,6 +305,8 @@ export class JobManager {
239
305
  job.status = "failed";
240
306
  job.error = e instanceof Error ? e.message : String(e);
241
307
  } finally {
308
+ this.#releaseJobLocks.delete(job.id);
309
+ this.#jobControllers.delete(job.id);
242
310
  job.completedAt = Date.now();
243
311
  this.#activeJobId = null;
244
312
  await lock.release().catch(() => undefined);
@@ -248,10 +316,11 @@ export class JobManager {
248
316
 
249
317
  #startJobWithLock(
250
318
  type: JobType,
251
- fn: () => Promise<SyncResult>,
319
+ fn: (signal: AbortSignal) => Promise<SyncResult>,
252
320
  lock: WriteLockHandle
253
321
  ): string {
254
322
  const jobId = crypto.randomUUID();
323
+ const ownedLock = this.#ownLock(jobId, lock);
255
324
  const job: JobRecord = {
256
325
  id: jobId,
257
326
  type,
@@ -260,11 +329,15 @@ export class JobManager {
260
329
  serverInstanceId: this.#serverInstanceId,
261
330
  };
262
331
 
332
+ this.#jobControllers.set(jobId, new AbortController());
263
333
  this.#jobs.set(jobId, job);
264
334
  this.#jobAuthorizationEpochs.set(jobId, this.#authorizationEpoch);
265
335
  this.#activeJobId = jobId;
266
336
 
267
- const jobPromise = this.#runJob(job, fn, lock);
337
+ const jobPromise = withOwnedInferenceScope(
338
+ { signal: this.#jobControllers.get(jobId)!.signal },
339
+ () => withBackgroundInference(() => this.#runJob(job, fn, ownedLock))
340
+ );
268
341
  this.#track(jobPromise);
269
342
 
270
343
  return jobId;
@@ -272,10 +345,11 @@ export class JobManager {
272
345
 
273
346
  #startTypedJobWithLock(
274
347
  type: JobType,
275
- fn: () => Promise<JobResult>,
348
+ fn: (signal: AbortSignal) => Promise<JobResult>,
276
349
  lock: WriteLockHandle
277
350
  ): string {
278
351
  const jobId = crypto.randomUUID();
352
+ const ownedLock = this.#ownLock(jobId, lock);
279
353
  const job: JobRecord = {
280
354
  id: jobId,
281
355
  type,
@@ -284,11 +358,15 @@ export class JobManager {
284
358
  serverInstanceId: this.#serverInstanceId,
285
359
  };
286
360
 
361
+ this.#jobControllers.set(jobId, new AbortController());
287
362
  this.#jobs.set(jobId, job);
288
363
  this.#jobAuthorizationEpochs.set(jobId, this.#authorizationEpoch);
289
364
  this.#activeJobId = jobId;
290
365
 
291
- const jobPromise = this.#runTypedJob(job, fn, lock);
366
+ const jobPromise = withOwnedInferenceScope(
367
+ { signal: this.#jobControllers.get(jobId)!.signal },
368
+ () => withBackgroundInference(() => this.#runTypedJob(job, fn, ownedLock))
369
+ );
292
370
  this.#track(jobPromise);
293
371
 
294
372
  return jobId;
@@ -296,14 +374,16 @@ export class JobManager {
296
374
 
297
375
  async #runTypedJob(
298
376
  job: JobRecord,
299
- fn: () => Promise<JobResult>,
377
+ fn: (signal: AbortSignal) => Promise<JobResult>,
300
378
  lock: { release: () => Promise<void> }
301
379
  ): Promise<void> {
302
380
  try {
303
381
  const release = await this.#toolMutex.acquire();
304
382
  try {
305
383
  this.#assertAuthorizationEpoch(job);
306
- const result = await fn();
384
+ assertInferenceActive();
385
+ const result = await fn(this.#jobControllers.get(job.id)!.signal);
386
+ assertInferenceActive();
307
387
  job.status = "completed";
308
388
  job.typedResult = result;
309
389
  } catch (e) {
@@ -316,6 +396,8 @@ export class JobManager {
316
396
  job.status = "failed";
317
397
  job.error = e instanceof Error ? e.message : String(e);
318
398
  } finally {
399
+ this.#releaseJobLocks.delete(job.id);
400
+ this.#jobControllers.delete(job.id);
319
401
  job.completedAt = Date.now();
320
402
  this.#activeJobId = null;
321
403
  await lock.release().catch(() => undefined);
@@ -149,6 +149,16 @@ export const NETWORK_BOUNDARY_INVENTORY = [
149
149
  action: "remote_inference",
150
150
  enforcement: "collection_policy",
151
151
  },
152
+ {
153
+ // Collection text crosses owned local IPC only. The child receives approved
154
+ // model paths and a restricted environment with downloads/builds disabled.
155
+ id: "native-inference-worker",
156
+ key: "src/llm/native-worker/client.ts::child_process#1",
157
+ path: "src/llm/native-worker/client.ts",
158
+ primitive: "child_process",
159
+ action: null,
160
+ enforcement: "local_process_only",
161
+ },
152
162
  {
153
163
  id: "pinned-http-fetch",
154
164
  key: "src/llm/pinned-http-connection.ts::fetch#1",
@@ -0,0 +1,45 @@
1
+ /** Internal resident shutdown clock. Every participant shares these deadlines. */
2
+ export const SHUTDOWN_DRAIN_MS = 5_000;
3
+ export const SHUTDOWN_ABORT_MS = 5_000;
4
+ export const SHUTDOWN_EXIT_MS = 1_000;
5
+ // The detached parent must not be killed before it can reap its native child.
6
+ export const RESIDENT_STOP_GRACE_MS = 12_000;
7
+
8
+ /** Observe settlement without abandoning rejection handling or retaining a timer. */
9
+ export async function settlesBy(
10
+ work: Promise<unknown>,
11
+ deadline: number,
12
+ interrupt?: AbortSignal
13
+ ): Promise<boolean> {
14
+ let timer: ReturnType<typeof setTimeout> | undefined;
15
+ let interrupted: (() => void) | undefined;
16
+ try {
17
+ return await Promise.race([
18
+ work.then(() => true),
19
+ new Promise<false>((resolve) => {
20
+ interrupted = () => resolve(false);
21
+ interrupt?.addEventListener("abort", interrupted, { once: true });
22
+ if (interrupt?.aborted) resolve(false);
23
+ timer = setTimeout(
24
+ () => resolve(false),
25
+ Math.max(0, deadline - performance.now())
26
+ );
27
+ }),
28
+ ]);
29
+ } finally {
30
+ clearTimeout(timer);
31
+ if (interrupted) interrupt?.removeEventListener("abort", interrupted);
32
+ }
33
+ }
34
+
35
+ export function shutdownDuration(
36
+ value: number | undefined,
37
+ fallback: number
38
+ ): number {
39
+ const duration = value ?? fallback;
40
+ if (!Number.isSafeInteger(duration) || duration < 0)
41
+ throw new RangeError(
42
+ "Shutdown duration must be a nonnegative finite integer"
43
+ );
44
+ return duration;
45
+ }