@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
@@ -42,9 +42,13 @@ import {
42
42
  } from "../core/findings-run-state";
43
43
  import { JobManager } from "../core/job-manager";
44
44
  import { recordContentMutation } from "../core/mutation-generations";
45
+ import {
46
+ shutdownDuration,
47
+ SHUTDOWN_DRAIN_MS,
48
+ SHUTDOWN_ABORT_MS,
49
+ } from "../core/shutdown-budget";
45
50
  import { defaultSyncService, withContentTypeRules } from "../ingestion";
46
- import { getModelManager } from "../llm/nodeLlamaCpp/lifecycle";
47
- import { getModelConfig } from "../llm/registry";
51
+ import { withOwnedInferenceScope } from "../llm/inference-scope";
48
52
  import { getActivePreset } from "../llm/registry";
49
53
  import { createToolContext, Mutex } from "../mcp/context";
50
54
  import { SqliteAdapter } from "../store/sqlite/adapter";
@@ -58,10 +62,13 @@ import { createEmbedScheduler } from "./embed-scheduler";
58
62
  import { FindingsScheduler, type FindingsPassResult } from "./findings-pass";
59
63
  import { AdmissionController, ReaderGate } from "./resident-admission";
60
64
  import { ResidentBackgroundWork } from "./resident-background-work";
61
- import { buildResidentStatusSnapshot } from "./resident-status";
65
+ import { disposeResidentResources } from "./resident-shutdown";
66
+ import {
67
+ createStandaloneResidentStatus,
68
+ buildResidentStatusSnapshot,
69
+ } from "./resident-status";
62
70
  import { CollectionWatchService as DefaultCollectionWatchService } from "./watch-service";
63
71
 
64
- const DEFAULT_SHUTDOWN_DEADLINE_MS = 5_000;
65
72
  const OWNER_LOCK_TIMEOUT_MS = 0;
66
73
 
67
74
  export type ResidentMode = "serve" | "daemon";
@@ -110,7 +117,10 @@ export interface ResidentRuntime {
110
117
  readonly capsuleReverificationScheduler: SavedCapsuleReverificationScheduler;
111
118
  /** Present only when daemon mode runs with `findings.enabled`. */
112
119
  readonly findingsScheduler: FindingsScheduler | null;
113
- readonly modelManager: ModelManager;
120
+ readonly modelManager: Pick<
121
+ ModelManager,
122
+ "acquireLease" | "getLifecycleStats" | "disposeAll"
123
+ >;
114
124
  readonly mcpContext: ToolContext;
115
125
  readonly generations: ResidentGeneration;
116
126
  readonly activeRequests: number;
@@ -136,7 +146,7 @@ export interface ResidentRuntime {
136
146
  runUpdateCmd?: boolean;
137
147
  triggerEmbed?: boolean;
138
148
  }): Promise<{ syncResult: SyncResult; embedResult: EmbedResult | null }>;
139
- dispose(): Promise<void>;
149
+ dispose(closeSurface?: () => Promise<unknown>): Promise<void>;
140
150
  }
141
151
 
142
152
  export type ResidentRuntimeResult =
@@ -176,6 +186,8 @@ export async function startResidentRuntime(
176
186
  options: ResidentRuntimeOptions = {},
177
187
  deps: ResidentRuntimeDeps = {}
178
188
  ): Promise<ResidentRuntimeResult> {
189
+ shutdownDuration(options.shutdownDeadlineMs, SHUTDOWN_DRAIN_MS);
190
+ shutdownDuration(options.shutdownAbortSettleMs, SHUTDOWN_ABORT_MS);
179
191
  if (options.index !== undefined && !isValidIndexName(options.index)) {
180
192
  return {
181
193
  success: false,
@@ -274,9 +286,16 @@ export async function startResidentRuntime(
274
286
  eventBus: options.eventBus ?? null,
275
287
  watchService: null,
276
288
  };
277
- const modelManager =
278
- deps.modelManagerFactory?.(initialConfig) ??
279
- getModelManager(getModelConfig(initialConfig));
289
+ const modelManager = deps.modelManagerFactory?.(initialConfig) ?? {
290
+ acquireLease: () =>
291
+ ctxHolder.current.llm?.acquireModelLease() ?? { release() {} },
292
+ getLifecycleStats: () =>
293
+ ctxHolder.current.llm?.getLifecycleStats() ??
294
+ createStandaloneResidentStatus("stdio").models,
295
+ disposeAll: async () => {
296
+ await ctxHolder.current.llm?.dispose();
297
+ },
298
+ };
280
299
  const generations: ResidentGeneration = { content: 0, index: 0 };
281
300
  ctxHolder.markContentMutation = () => {
282
301
  generations.content += 1;
@@ -289,7 +308,6 @@ export async function startResidentRuntime(
289
308
  getEmbedPort: () => ctxHolder.current.embedPort,
290
309
  getVectorIndex: () => ctxHolder.current.vectorIndex,
291
310
  getModelUri: () => getActivePreset(ctxHolder.config).embed,
292
- acquireModelLease: () => modelManager.acquireLease(),
293
311
  onEmbedded: () => {
294
312
  generations.index += 1;
295
313
  },
@@ -350,6 +368,7 @@ export async function startResidentRuntime(
350
368
  let shutdownState: ResidentStatus["shutdown"]["state"] = "none";
351
369
  let admissionState: ResidentStatus["admission"]["state"] = "accepting";
352
370
  let disposed = false;
371
+ let disposal: Promise<void> | undefined;
353
372
  const backgroundWork = new ResidentBackgroundWork(
354
373
  () => !disposed && admission.accepting
355
374
  );
@@ -405,7 +424,8 @@ export async function startResidentRuntime(
405
424
  writeLockPath,
406
425
  enableWrite: false,
407
426
  isShuttingDown: () => disposed || !admission.accepting,
408
- acquireModelLease: () => modelManager.acquireLease(),
427
+ getModelAdapter: (config) =>
428
+ config === ctxHolder.current.config ? ctxHolder.current.llm : undefined,
409
429
  markContentMutation: () => {
410
430
  generations.content += 1;
411
431
  },
@@ -545,60 +565,76 @@ export async function startResidentRuntime(
545
565
  policySessionInvalidator = invalidator;
546
566
  },
547
567
  async syncAll(syncOptions = {}) {
548
- const config = ctxHolder.config;
549
- const syncAllService = deps.syncAllService
550
- ? (...args: Parameters<typeof defaultSyncService.syncAll>) =>
551
- deps.syncAllService!(...args)
552
- : defaultSyncService.syncAll.bind(defaultSyncService);
553
- const syncResult = await syncAllService(
554
- config.collections,
555
- store,
556
- withContentTypeRules(
557
- {
558
- gitPull: syncOptions.gitPull,
559
- runUpdateCmd: syncOptions.runUpdateCmd,
560
- },
561
- config
562
- )
563
- );
564
- recordContentMutation(syncResult, () => {
565
- generations.content += 1;
566
- });
567
- const embedResult =
568
- syncOptions.triggerEmbed === false
569
- ? null
570
- : await scheduler.triggerNow();
571
- capsuleReverificationScheduler.notifySyncSettled();
572
- return { syncResult, embedResult };
568
+ const request = admission.admit();
569
+ if (!request) throw new Error("Resident runtime is shutting down");
570
+ try {
571
+ return await withOwnedInferenceScope(
572
+ { signal: request.signal },
573
+ async () => {
574
+ const config = ctxHolder.config;
575
+ const syncAllService = deps.syncAllService
576
+ ? (...args: Parameters<typeof defaultSyncService.syncAll>) =>
577
+ deps.syncAllService!(...args)
578
+ : defaultSyncService.syncAll.bind(defaultSyncService);
579
+ const syncResult = await syncAllService(
580
+ config.collections,
581
+ store,
582
+ withContentTypeRules(
583
+ {
584
+ gitPull: syncOptions.gitPull,
585
+ runUpdateCmd: syncOptions.runUpdateCmd,
586
+ },
587
+ config
588
+ )
589
+ );
590
+ recordContentMutation(syncResult, () => {
591
+ generations.content += 1;
592
+ });
593
+ const embedResult =
594
+ syncOptions.triggerEmbed === false
595
+ ? null
596
+ : await scheduler.triggerNow();
597
+ capsuleReverificationScheduler.notifySyncSettled();
598
+ return { syncResult, embedResult };
599
+ }
600
+ );
601
+ } finally {
602
+ request.finish();
603
+ }
573
604
  },
574
- async dispose() {
575
- if (disposed) return;
605
+ dispose(closeSurface) {
606
+ if (disposal) return disposal;
576
607
  disposed = true;
608
+ admission.stop();
609
+ jobManager.stop();
577
610
  admissionState = "draining";
578
611
  shutdownState = "graceful";
579
- const deadlineReached = await admission.closeAndDrain(
580
- options.shutdownDeadlineMs ?? DEFAULT_SHUTDOWN_DEADLINE_MS,
581
- options.shutdownAbortSettleMs ?? DEFAULT_SHUTDOWN_DEADLINE_MS
582
- );
583
- if (deadlineReached) shutdownState = "deadline";
584
- findingsScheduler?.dispose();
585
- await backgroundWork.cancelAndDrain();
586
- await capsuleReverificationScheduler.dispose();
587
- await jobManager.shutdown().catch(() => undefined);
588
- await Promise.allSettled([
589
- Promise.resolve().then(() => watchService.dispose()),
590
- Promise.resolve().then(() => options.eventBus?.close()),
591
- ]);
592
- await Promise.allSettled([scheduler.dispose()]);
593
- await Promise.allSettled([
594
- (deps.disposeServerContext ?? disposeServerContext)(ctxHolder.current),
595
- ]);
596
- await Promise.allSettled([modelManager.disposeAll()]);
597
- await Promise.allSettled([store.close()]);
598
- await Promise.allSettled([ownerLock.release()]);
599
- admissionState = "closed";
600
- transportStatusProvider = null;
601
- listenerPort = null;
612
+ disposal = disposeResidentResources({
613
+ options,
614
+ admission,
615
+ backgroundWork,
616
+ jobManager,
617
+ scheduler,
618
+ store,
619
+ watchService,
620
+ context: ctxHolder.current,
621
+ mcpContext,
622
+ modelManager,
623
+ ownerLock,
624
+ stopFindings: () => findingsScheduler?.dispose(),
625
+ stopCapsules: () => capsuleReverificationScheduler.dispose(),
626
+ closeEvents: () => options.eventBus?.close(),
627
+ disposeContext: deps.disposeServerContext ?? disposeServerContext,
628
+ closeSurface,
629
+ onDeadline: () => {
630
+ shutdownState = "deadline";
631
+ },
632
+ }).then(() => {
633
+ admissionState = "closed";
634
+ transportStatusProvider = null;
635
+ listenerPort = null;
636
+ });
637
+ return disposal;
602
638
  },
603
639
  };
604
640
  ctxHolder.startBackgroundWork = (operation) =>
@@ -0,0 +1,153 @@
1
+ import type { WriteLockHandle } from "../core/file-lock";
2
+ /** One finite shutdown clock for the resources owned by a resident runtime. */
3
+ import type { JobManager } from "../core/job-manager";
4
+ import type { ToolContext } from "../mcp/context";
5
+ import type { SqliteAdapter } from "../store/sqlite/adapter";
6
+ import type { ServerContext } from "./context";
7
+ import type { EmbedScheduler } from "./embed-scheduler";
8
+ import type { AdmissionController } from "./resident-admission";
9
+ import type { ResidentBackgroundWork } from "./resident-background-work";
10
+ import type { ResidentRuntimeOptions } from "./resident-runtime";
11
+
12
+ import {
13
+ settlesBy,
14
+ shutdownDuration,
15
+ SHUTDOWN_DRAIN_MS,
16
+ SHUTDOWN_ABORT_MS,
17
+ SHUTDOWN_EXIT_MS,
18
+ } from "../core/shutdown-budget";
19
+
20
+ interface ResidentShutdownResources {
21
+ options: ResidentRuntimeOptions;
22
+ admission: AdmissionController;
23
+ backgroundWork: ResidentBackgroundWork;
24
+ jobManager: JobManager;
25
+ scheduler: EmbedScheduler;
26
+ store: SqliteAdapter;
27
+ watchService: { dispose(): void | Promise<void> };
28
+ context: ServerContext;
29
+ mcpContext: ToolContext;
30
+ modelManager: { disposeAll(): Promise<void> };
31
+ ownerLock: WriteLockHandle;
32
+ stopFindings: () => void;
33
+ stopCapsules: () => Promise<void>;
34
+ closeEvents: () => void | Promise<void>;
35
+ disposeContext: (context: ServerContext) => Promise<void>;
36
+ closeSurface?: () => Promise<unknown>;
37
+ onDeadline: () => void;
38
+ }
39
+
40
+ export async function disposeResidentResources(
41
+ resources: ResidentShutdownResources
42
+ ): Promise<void> {
43
+ const {
44
+ options,
45
+ admission,
46
+ backgroundWork,
47
+ jobManager,
48
+ scheduler,
49
+ store,
50
+ watchService,
51
+ context,
52
+ mcpContext,
53
+ modelManager,
54
+ ownerLock,
55
+ stopFindings,
56
+ stopCapsules,
57
+ closeEvents,
58
+ disposeContext,
59
+ closeSurface,
60
+ onDeadline,
61
+ } = resources;
62
+ let deadlineReached = false;
63
+ const drainDeadline =
64
+ performance.now() +
65
+ shutdownDuration(options.shutdownDeadlineMs, SHUTDOWN_DRAIN_MS);
66
+ const abortDeadline =
67
+ drainDeadline +
68
+ shutdownDuration(options.shutdownAbortSettleMs, SHUTDOWN_ABORT_MS);
69
+ const exitDeadline = abortDeadline + SHUTDOWN_EXIT_MS;
70
+ let unfinishedJobs: Promise<void> = Promise.resolve();
71
+ let writeFailure: unknown;
72
+ store.beginShutdown?.(abortDeadline);
73
+ const observe = (operation: () => unknown): Promise<void> =>
74
+ Promise.resolve()
75
+ .then(operation)
76
+ .then(
77
+ () => {},
78
+ (error: unknown) => console.error("Resident cleanup failed:", error)
79
+ );
80
+ stopFindings();
81
+ const schedulerDrain = observe(() =>
82
+ scheduler.stop ? scheduler.stop() : scheduler.dispose()
83
+ );
84
+ const work = Promise.all([
85
+ observe(() => closeSurface?.()),
86
+ admission.drain(),
87
+ backgroundWork.drain(),
88
+ jobManager.shutdown(),
89
+ schedulerDrain,
90
+ observe(() => stopCapsules()),
91
+ observe(() => watchService.dispose()),
92
+ observe(() => closeEvents()),
93
+ ]);
94
+ if (!(await settlesBy(work, drainDeadline))) {
95
+ deadlineReached = true;
96
+ onDeadline();
97
+ admission.cancel();
98
+ backgroundWork.cancel();
99
+ jobManager.cancel();
100
+ const canceledScheduler = observe(() => scheduler.dispose());
101
+ if (
102
+ !(await settlesBy(Promise.all([work, canceledScheduler]), abortDeadline))
103
+ ) {
104
+ // Revoke/rollback suspended parent writers before closing their DB.
105
+ // The ordinary store API also fences any late noncooperative callback.
106
+ try {
107
+ store.fenceForShutdown?.();
108
+ // Calling close synchronously closes cached raw handles before
109
+ // any late callback can resume in the native-exit wait.
110
+ const closing = store.close();
111
+ unfinishedJobs = jobManager.failUnfinished();
112
+ if (!(await settlesBy(closing, exitDeadline)))
113
+ throw new Error("Store close exceeded shutdown deadline");
114
+ } catch (error) {
115
+ writeFailure = error;
116
+ }
117
+ }
118
+ }
119
+ // Reach the single native owner directly; port disposal may itself wait
120
+ // for that owner. Never let such a wait hide the forced-exit deadline.
121
+ const nativeOptions = {
122
+ deadline: exitDeadline,
123
+ force: deadlineReached,
124
+ };
125
+ const native = Promise.all([
126
+ context.llm?.dispose(nativeOptions),
127
+ mcpContext.disposeModels?.(nativeOptions),
128
+ ]);
129
+ let nativeFailure: unknown;
130
+ try {
131
+ if (!(await settlesBy(native, exitDeadline)))
132
+ throw new Error(
133
+ "Native shutdown did not settle before owned-child exit deadline"
134
+ );
135
+ } catch (error) {
136
+ nativeFailure = error;
137
+ console.error("Resident native shutdown failed:", error);
138
+ }
139
+ const cleanup = Promise.all([
140
+ unfinishedJobs,
141
+ observe(() => disposeContext(context)),
142
+ observe(() => modelManager.disposeAll()),
143
+ ]);
144
+ if (!(await settlesBy(cleanup, exitDeadline)))
145
+ console.error("Resident resource cleanup exceeded shutdown deadline");
146
+ // close performs synchronous rollback/revocation before disconnecting.
147
+ if (writeFailure) throw writeFailure;
148
+ if (!(await settlesBy(store.close(), exitDeadline)))
149
+ throw new Error("Store close exceeded shutdown deadline");
150
+ if (!(await settlesBy(ownerLock.release(), exitDeadline)))
151
+ throw new Error("Resident owner lock release exceeded shutdown deadline");
152
+ if (nativeFailure) throw nativeFailure;
153
+ }
@@ -4244,7 +4244,9 @@ export async function handleSearch(
4244
4244
  req,
4245
4245
  trace.session,
4246
4246
  "failed",
4247
- errorResponse("RUNTIME", result.error.message, 500)
4247
+ result.error.code === "INVALID_INPUT"
4248
+ ? errorResponse("VALIDATION", result.error.message, 400)
4249
+ : errorResponse("RUNTIME", result.error.message, 500)
4248
4250
  );
4249
4251
  }
4250
4252
 
@@ -472,8 +472,8 @@ export async function startServer(
472
472
  }
473
473
  } catch (error) {
474
474
  removeShutdownHandlers();
475
- await Promise.allSettled([gateway.close()]);
476
- await Promise.allSettled([runtime.dispose()]);
475
+ const surfaceClose = Promise.resolve().then(() => gateway.close());
476
+ await runtime.dispose(() => surfaceClose);
477
477
  return {
478
478
  success: false,
479
479
  error: error instanceof Error ? error.message : String(error),
@@ -1134,8 +1134,11 @@ export async function startServer(
1134
1134
  return withSecurityHeaders(forbiddenResponse(), isDev);
1135
1135
  }
1136
1136
  return withSecurityHeaders(
1137
- await handleResidentRead(runtime as ResidentRuntime, req, () =>
1138
- handleQuery(ctxHolder.current, req)
1137
+ await handleResidentRead(
1138
+ runtime as ResidentRuntime,
1139
+ req,
1140
+ (signal) =>
1141
+ handleQuery(ctxHolder.current, new Request(req, { signal }))
1139
1142
  ),
1140
1143
  isDev
1141
1144
  );
@@ -1147,8 +1150,14 @@ export async function startServer(
1147
1150
  return withSecurityHeaders(forbiddenResponse(), isDev);
1148
1151
  }
1149
1152
  return withSecurityHeaders(
1150
- await handleResidentRead(runtime as ResidentRuntime, req, () =>
1151
- handleContextBuild(ctxHolder.current, req)
1153
+ await handleResidentRead(
1154
+ runtime as ResidentRuntime,
1155
+ req,
1156
+ (signal) =>
1157
+ handleContextBuild(
1158
+ ctxHolder.current,
1159
+ new Request(req, { signal })
1160
+ )
1152
1161
  ),
1153
1162
  isDev
1154
1163
  );
@@ -1160,8 +1169,14 @@ export async function startServer(
1160
1169
  return withSecurityHeaders(forbiddenResponse(), isDev);
1161
1170
  }
1162
1171
  return withSecurityHeaders(
1163
- await handleResidentRead(runtime as ResidentRuntime, req, () =>
1164
- handleContextVerify(ctxHolder.current, req)
1172
+ await handleResidentRead(
1173
+ runtime as ResidentRuntime,
1174
+ req,
1175
+ (signal) =>
1176
+ handleContextVerify(
1177
+ ctxHolder.current,
1178
+ new Request(req, { signal })
1179
+ )
1165
1180
  ),
1166
1181
  isDev
1167
1182
  );
@@ -1173,8 +1188,14 @@ export async function startServer(
1173
1188
  return withSecurityHeaders(forbiddenResponse(), isDev);
1174
1189
  }
1175
1190
  return withSecurityHeaders(
1176
- await handleResidentRead(runtime as ResidentRuntime, req, () =>
1177
- handleQueryDiagnose(ctxHolder.current, req)
1191
+ await handleResidentRead(
1192
+ runtime as ResidentRuntime,
1193
+ req,
1194
+ (signal) =>
1195
+ handleQueryDiagnose(
1196
+ ctxHolder.current,
1197
+ new Request(req, { signal })
1198
+ )
1178
1199
  ),
1179
1200
  isDev
1180
1201
  );
@@ -1186,8 +1207,11 @@ export async function startServer(
1186
1207
  return withSecurityHeaders(forbiddenResponse(), isDev);
1187
1208
  }
1188
1209
  return withSecurityHeaders(
1189
- await handleResidentRead(runtime as ResidentRuntime, req, () =>
1190
- handleAsk(ctxHolder.current, req)
1210
+ await handleResidentRead(
1211
+ runtime as ResidentRuntime,
1212
+ req,
1213
+ (signal) =>
1214
+ handleAsk(ctxHolder.current, new Request(req, { signal }))
1191
1215
  ),
1192
1216
  isDev
1193
1217
  );
@@ -1481,11 +1505,11 @@ export async function startServer(
1481
1505
  });
1482
1506
  } catch (e) {
1483
1507
  removeShutdownHandlers();
1484
- if (spaBundleSource) {
1485
- await Promise.allSettled([spaBundleSource.close()]);
1486
- }
1487
- await Promise.allSettled([gateway.close()]);
1488
- await Promise.allSettled([runtime.dispose()]);
1508
+ const surfaceClose = Promise.all([
1509
+ Promise.resolve().then(() => spaBundleSource?.close()),
1510
+ Promise.resolve().then(() => gateway.close()),
1511
+ ]);
1512
+ await runtime.dispose(() => surfaceClose);
1489
1513
  return {
1490
1514
  success: false,
1491
1515
  error: e instanceof Error ? e.message : String(e),
@@ -1510,14 +1534,11 @@ export async function startServer(
1510
1534
  }
1511
1535
 
1512
1536
  removeShutdownHandlers();
1513
- try {
1514
- await server.stop(true);
1515
- } finally {
1516
- if (spaBundleSource) {
1517
- await Promise.allSettled([spaBundleSource.close()]);
1518
- }
1519
- await Promise.allSettled([gateway.close()]);
1520
- await Promise.allSettled([runtime.dispose()]);
1521
- }
1537
+ const surfaceClose = Promise.all([
1538
+ Promise.resolve().then(() => server.stop(true)),
1539
+ Promise.resolve().then(() => spaBundleSource?.close()),
1540
+ Promise.resolve().then(() => gateway.close()),
1541
+ ]);
1542
+ await runtime.dispose(() => surfaceClose);
1522
1543
  return { success: true };
1523
1544
  }
@@ -0,0 +1,54 @@
1
+ /** Additive exact-input variants; existing vectors have no historical proof. */
2
+ import type { Migration } from "./runner";
3
+
4
+ export const migration: Migration = {
5
+ version: 28,
6
+ name: "vector_variants",
7
+ up(db): void {
8
+ db.exec(`
9
+ CREATE TABLE vector_variant_epoch (
10
+ id INTEGER PRIMARY KEY CHECK (id = 1), epoch INTEGER NOT NULL DEFAULT 0
11
+ );
12
+ INSERT INTO vector_variant_epoch(id) VALUES (1);
13
+ CREATE TABLE vector_partitions (
14
+ partition_id TEXT PRIMARY KEY,
15
+ version INTEGER NOT NULL CHECK (version = 1),
16
+ model TEXT NOT NULL, fingerprint TEXT NOT NULL,
17
+ dimensions INTEGER NOT NULL CHECK (dimensions > 0),
18
+ state TEXT NOT NULL DEFAULT 'shadow' CHECK (state IN ('shadow', 'active')),
19
+ activated_epoch INTEGER,
20
+ UNIQUE(model, fingerprint, dimensions)
21
+ );
22
+ CREATE TABLE vector_variants (
23
+ variant_id INTEGER PRIMARY KEY,
24
+ partition_id TEXT NOT NULL REFERENCES vector_partitions(partition_id),
25
+ input_hash TEXT NOT NULL CHECK (length(input_hash) = 64),
26
+ embedding BLOB NOT NULL,
27
+ UNIQUE(partition_id, input_hash), UNIQUE(partition_id, variant_id)
28
+ );
29
+ CREATE TABLE vector_owners (
30
+ document_id INTEGER NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
31
+ mirror_hash TEXT NOT NULL, seq INTEGER NOT NULL,
32
+ partition_id TEXT NOT NULL, variant_id INTEGER NOT NULL,
33
+ PRIMARY KEY(document_id, seq, partition_id),
34
+ FOREIGN KEY(partition_id, variant_id)
35
+ REFERENCES vector_variants(partition_id, variant_id)
36
+ );
37
+ CREATE INDEX idx_vector_owners_variant ON vector_owners(variant_id);
38
+ `);
39
+ // All writers, including old consumers and raw SQL, participate in the fence.
40
+ for (const table of [
41
+ "documents",
42
+ "content_chunks",
43
+ "vector_owners",
44
+ "vector_variants",
45
+ ]) {
46
+ for (const event of ["INSERT", "UPDATE", "DELETE"]) {
47
+ db.exec(`CREATE TRIGGER variant_epoch_${table}_${event}
48
+ AFTER ${event} ON ${table} BEGIN
49
+ UPDATE vector_variant_epoch SET epoch = epoch + 1 WHERE id = 1;
50
+ END`);
51
+ }
52
+ }
53
+ },
54
+ };
@@ -0,0 +1,77 @@
1
+ /** Durable unresolved references and conservative projection recovery. */
2
+ import type { Migration } from "./runner";
3
+
4
+ export const migration: Migration = {
5
+ version: 29,
6
+ name: "graph_reference_state",
7
+ up(db): void {
8
+ db.exec(`
9
+ -- Graph reference inventory (migration 029). Parsed links remain in doc_links.
10
+ -- Snapshots deliberately survive document deletion: closure needs old identities.
11
+ -- Missing inventory, changed version/config, or dirty state requires full recovery.
12
+ -- Begin persists dirty before projection. Complete only after successful edge writes,
13
+ -- unchanged input epoch, and coverage of every active source; compose in a transaction.
14
+ CREATE TABLE graph_projection_state (
15
+ id INTEGER PRIMARY KEY CHECK (id = 1),
16
+ epoch INTEGER NOT NULL DEFAULT 0,
17
+ version INTEGER,
18
+ config_fingerprint TEXT,
19
+ in_progress INTEGER NOT NULL DEFAULT 0 CHECK (in_progress IN (0, 1)),
20
+ dirty INTEGER NOT NULL DEFAULT 1 CHECK (dirty IN (0, 1))
21
+ );
22
+ INSERT INTO graph_projection_state(id) VALUES (1);
23
+ CREATE TABLE graph_reference_documents (
24
+ document_id INTEGER PRIMARY KEY,
25
+ collection TEXT NOT NULL, rel_path TEXT NOT NULL, docid TEXT NOT NULL,
26
+ uri TEXT NOT NULL, title TEXT, mirror_hash TEXT, source_hash TEXT NOT NULL,
27
+ content_type TEXT
28
+ );
29
+ CREATE INDEX idx_graph_reference_uri ON graph_reference_documents(uri);
30
+ CREATE INDEX idx_graph_reference_path ON graph_reference_documents(collection, rel_path);
31
+ CREATE INDEX idx_graph_reference_title ON graph_reference_documents(title);
32
+ CREATE TABLE graph_frontmatter_references (
33
+ source_doc_id INTEGER NOT NULL REFERENCES graph_reference_documents(document_id) ON DELETE CASCADE,
34
+ ordinal INTEGER NOT NULL,
35
+ edge_type TEXT NOT NULL,
36
+ target TEXT NOT NULL,
37
+ PRIMARY KEY(source_doc_id, ordinal)
38
+ );
39
+ CREATE INDEX idx_graph_reference_target ON graph_frontmatter_references(target);
40
+ `);
41
+ for (const table of ["documents", "doc_links"]) {
42
+ for (const operation of ["INSERT", "UPDATE", "DELETE"]) {
43
+ const changed =
44
+ table === "documents" && operation === "UPDATE"
45
+ ? " WHEN " +
46
+ [
47
+ "collection",
48
+ "rel_path",
49
+ "docid",
50
+ "uri",
51
+ "title",
52
+ "mirror_hash",
53
+ "source_hash",
54
+ "content_type",
55
+ "active",
56
+ ]
57
+ .map((column) => `OLD.${column} IS NOT NEW.${column}`)
58
+ .join(" OR ")
59
+ : "";
60
+ db.exec(`CREATE TRIGGER graph_input_${table}_${operation.toLowerCase()}
61
+ AFTER ${operation} ON ${table}${changed} BEGIN
62
+ UPDATE graph_projection_state SET epoch = epoch + 1, dirty = 1 WHERE id = 1;
63
+ END`);
64
+ }
65
+ }
66
+ for (const table of [
67
+ "graph_reference_documents",
68
+ "graph_frontmatter_references",
69
+ ]) {
70
+ for (const operation of ["INSERT", "UPDATE", "DELETE"]) {
71
+ db.exec(`CREATE TRIGGER graph_inventory_${table}_${operation.toLowerCase()}
72
+ AFTER ${operation} ON ${table} BEGIN
73
+ UPDATE graph_projection_state SET dirty = 1, in_progress = 1 WHERE id = 1; END`);
74
+ }
75
+ }
76
+ },
77
+ };
@@ -41,6 +41,8 @@ import { migration as m024 } from "./024-egress-derived-lineage";
41
41
  import { migration as m025 } from "./025-collection-egress-policy-revision";
42
42
  import { migration as m026 } from "./026-file-refactor-recovery-journal";
43
43
  import { migration as m027 } from "./027-memory-scopes";
44
+ import { migration as m028 } from "./028-vector-variants";
45
+ import { migration as m029 } from "./029-graph-reference-state";
44
46
 
45
47
  /** All migrations in order */
46
48
  export const migrations = [
@@ -71,4 +73,6 @@ export const migrations = [
71
73
  m025,
72
74
  m026,
73
75
  m027,
76
+ m028,
77
+ m029,
74
78
  ];